Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/fork/upstream-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ Removal set today: empty. The one future removal candidate (permissions legacy d

| Date | Upstream range | Conflicts | Notes |
| --- | --- | --- | --- |
| 2026-08-27 | e86823096..4e4949299 (3 commits) | coding-agent CHANGELOG only | Additive sync: `fullscreenCopyOnSelect` + Ctrl+X selection copy (#7720/#8731), `ui_prompt_start`/`ui_prompt_end` extension events (#5329/#8355), Google Vertex https-proxy-agent named-export fix (#8610/#8723). No telemetry or managed-install subsystem re-additions to re-drop. CHANGELOG conflict resolved by keeping the fork's [Unreleased] entries and adding upstream's three new ones marked inherited; upstream's re-listing of the already-released transcript-notices entry skipped. Fork surfaces verified intact post-merge (permissions settings, request-observer, ToolInfo capability + timeoutMs, telemetry removals). |
| 2026-08-26 | 8fa7eebd2..e86823096 (11 commits) | compaction.ts, settings-manager.ts, 3 tests, CHANGELOG | Upstream removed `toolChoice: "none"` from summarizer calls (#8649/#8638); adopted in both `cacheOptOut` branches (persona instruction + rejects-tool-call handling already cover it). e2e cache test classification reworked to the persona marker. Upstream's session-tail newline repair (0b5ee5d8b) is complementary to the fork's read-only validator. Settings test needed both sides' imports. |
| 2026-08-24 | 460191cfc..a470b121b (4 commits) | `agent-session.ts` (import trim vs fork's `join` usage; resolved by taking upstream imports + re-adding `join`) | README untouched upstream, no port needed; ledger assumptions re-verified; fork test set green |
| 2026-08-24 | a470b121b..dcd461925 (8 commits, v0.84.3) | `tools/bash.ts` (upstream extracted a shared `createShellToolDefinition`; fork's `capability: "process.execute"` moved onto the shared factory, covering powershell too), `tools/index.ts` (fork permissions exports + upstream powershell exports, both kept), `package-manager-cli.ts` + `test/package-command-paths.test.ts` (managed-install subsystem dropped per the decision above; fork self-update flow kept), `main.ts` (`cleanupManagedInstall` wiring unwound) | Upstream's installer-managed update subsystem not ported (see Decisions); `docs/packages.md` managed sentence reverted to pre-upstream text; `minimal` profile deny+hide list gains `powershell` (registered by default via `createAllToolDefinitions`); powershell ships with `process.execute` capability; golden system-prompt and prompt-prefix tests green without updates; fresh worktrees must run `npm run generate-models` (in `packages/ai`) before `npm run check`/tests — `src/providers/data/.manifest.json` is a git-ignored artifact whose absence cascades `unknown`/`never` type errors |
Expand Down
9 changes: 8 additions & 1 deletion packages/coding-agent/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
- Added opt-in permission policies (`tools.permissions`): `legacy` mode (default) is byte-identical to upstream; `policy` mode evaluates rules matching tool name, capability, path prefix, or command prefix with deny > ask > allow precedence, user rules overriding profile presets, and `hide: true` removing a tool from the model's tool list; profiles `code` (default), `review` (read-only), and `minimal` are plain rule presets ([#11](https://github.com/R-Dson/pi/issues/11)).
- Added fork-aware self-update channels: the running install's package name picks the update source — the fork's standalone tarball, the fork's GitHub-Packages registry, or upstream npmjs with a channel notice — so no update path replaces the fork with upstream ([#29](https://github.com/R-Dson/pi/issues/29)).
- Added a live preview of the newest thinking run while thinking is hidden: first ~120 display columns while streaming, collapsing to a one-line duration marker when the message finishes ([#30](https://github.com/R-Dson/pi/issues/30)).
- Added inherited `ui_prompt_start` and `ui_prompt_end` extension events so host integrations can distinguish active agent work from waiting on user-facing `ctx.ui` prompts ([#5329](https://github.com/earendil-works/pi/issues/5329)).
- Added inherited `fullscreenCopyOnSelect` to disable automatic fullscreen selection copy; when disabled, `Ctrl+X` copies the active text selection before falling back to the last assistant message, while `/tree` still copies the selected message ([#7720](https://github.com/earendil-works/pi/issues/7720)).

### Fixed

Expand All @@ -30,6 +32,7 @@
- Fixed inherited Windows shell aborts crashing Pi when `taskkill.exe` is unavailable on `PATH` ([#6596](https://github.com/earendil-works/pi/issues/6596)).
- Fixed inherited resumed sessions corrupting the next appended entry when their JSONL file lacks a trailing newline ([#8345](https://github.com/earendil-works/pi/issues/8345)).
- Fixed inherited compaction and branch summaries forcing `toolChoice: "none"` ([#8649](https://github.com/earendil-works/pi/issues/8649), [#8638](https://github.com/earendil-works/pi/issues/8638)).
- Fixed inherited Google Vertex requests failing with `HttpsProxyAgent is not a constructor` when the bundled Node.js runtime uses an HTTP(S) proxy ([#8610](https://github.com/earendil-works/pi/issues/8610)).

### Removed

Expand All @@ -52,7 +55,11 @@
- Added an optional `powershell` tool for Windows, configurable through `defaultTools` and the SDK. See [PowerShell Tool](docs/windows.md#powershell-tool).
- Added a `/thinking` selector and searchable default choices to the model and thinking selectors; Ctrl+S saves the selected model as the global default. See [Models and Thinking](docs/keybindings.md#models-and-thinking).
- Added optional routing session IDs to exported compaction summary helpers so callers can preserve provider routing without enabling prompt cache writes.
- Added transcript usage notices for compaction and branch summaries when cache miss notices are enabled.
- Added



usage notices for compaction and branch summaries when cache miss notices are enabled.
- Added `session_compact_failed` extension events so compaction failures and aborts expose their reason, retry state, source, and error message to handlers ([#8175](https://github.com/earendil-works/pi/issues/8175)).
- Added inherited provider-neutral `toolChoice` support to simple stream requests.
- Added inherited automatic Anthropic server-side refusal fallback for supported first-party models, including returned-model usage pricing ([#8017](https://github.com/earendil-works/pi/issues/8017)).
Expand Down
2 changes: 1 addition & 1 deletion packages/coding-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ See `/hotkeys` for the full list. Customize via `~/.pi/agent/keybindings.json`.
| Shift+Tab | Cycle thinking level |
| Ctrl+O | Collapse/expand tool output |
| Ctrl+T | Collapse/expand thinking blocks |
| Ctrl+X | Copy the last assistant message |
| Ctrl+X | Copy the last assistant message; with fullscreen copy-on-select disabled, copy the active text selection |

### Message Queue

Expand Down
18 changes: 18 additions & 0 deletions packages/coding-agent/docs/extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,24 @@ pi.on("agent_settled", async (_event, ctx) => {
});
```

#### ui_prompt_start / ui_prompt_end

Notification-only lifecycle events for blocking user-facing extension UI prompts. They fire around `ctx.ui.select()`, `ctx.ui.confirm()`, `ctx.ui.input()`, `ctx.ui.editor()`, and `ctx.ui.custom()` so host/status integrations can report "waiting for user" instead of just "running".

Nested or overlapping prompts are coalesced into one outer waiting span. Handlers are invoked best-effort and are not awaited before showing or closing the prompt.

```typescript
pi.on("ui_prompt_start", async (event, ctx) => {
// event.reason === "ui_prompt"
// event.kind: "select" | "confirm" | "input" | "editor" | "custom"
// event.title: prompt title when available
});

pi.on("ui_prompt_end", async (event, ctx) => {
// Pi is no longer waiting on that UI prompt span.
});
```

#### turn_start / turn_end

Fired for each turn (one LLM response + tool calls).
Expand Down
2 changes: 1 addition & 1 deletion packages/coding-agent/docs/keybindings.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ This routing remains configurable through the ordinary action bindings. For exam
| Keybinding id | Default | Description |
|--------|---------|-------------|
| `app.tools.expand` | `ctrl+o` | Collapse or expand tool output |
| `app.message.copy` | `ctrl+x` | Copy the last assistant message, or the selected message in `/tree` |
| `app.message.copy` | `ctrl+x` | Copy the selected message in `/tree`; otherwise copy the last assistant message, or the active fullscreen text selection when `fullscreenCopyOnSelect` is disabled |
| `app.message.followUp` | `alt+enter` (`ctrl+q` on Windows and WSL) | Queue follow-up message |
| `app.message.dequeue` | `alt+up` (`alt+q` on Windows and WSL) | Restore queued messages to editor |

Expand Down
1 change: 1 addition & 0 deletions packages/coding-agent/docs/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ Use `/trust` in interactive mode to save a project trust decision for future ses
| `tuiMode` | string | `"regular"` | Interactive TUI mode: `"regular"` or experimental `"fullscreen"`. Changes from `/settings` apply immediately; `--tui-mode` overrides this setting at startup |
| `fullscreenExitOutput` | string | `"transcript"` | Fullscreen exit output: `"transcript"` prints the final transcript and resume hint, while `"resume-hint"` restores the previous screen and prints only the resume hint. Has no effect in regular TUI mode |
| `fullscreenScrollbar` | string | `"auto"` | Fullscreen transcript scrollbar: `"auto"` shows it temporarily while scrolling, `"always"` reserves the rightmost column and keeps it visible, and `"hidden"` hides it. Has no effect in regular TUI mode |
| `fullscreenCopyOnSelect` | boolean | `true` | Automatically copy selected text in fullscreen mode. When disabled, selections stay highlighted and `Ctrl+X` copies the active selection |

For VS Code, include `--wait` so pi resumes after the editor exits:

Expand Down
2 changes: 1 addition & 1 deletion packages/coding-agent/docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ The editor can be replaced temporarily by built-in UI such as `/settings` or by
| File reference | Type `@` to fuzzy-search project files |
| Path completion | Press Tab to complete paths |
| Multi-line input | Shift+Enter, or Ctrl+Enter on Windows Terminal |
| Copy response | Ctrl+X copies the last assistant message; in `/tree`, it copies the selected message |
| Copy response | Ctrl+X copies the selected message in `/tree`; otherwise it copies the last assistant message, or the active fullscreen text selection when `fullscreenCopyOnSelect` is disabled |
| Images | Paste with Ctrl+V, Alt+V on Windows, or drag into the terminal |
| Shell command | `!command` runs and sends output to the model |
| Hidden shell command | `!!command` runs without sending output to the model |
Expand Down
3 changes: 3 additions & 0 deletions packages/coding-agent/src/core/extensions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ export type {
TreePreparation,
TurnEndEvent,
TurnStartEvent,
UIPromptEndEvent,
UIPromptKind,
UIPromptStartEvent,
// Events - User Bash
UserBashEvent,
UserBashEventResult,
Expand Down
52 changes: 51 additions & 1 deletion packages/coding-agent/src/core/extensions/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import type {
ToolCallEventResult,
ToolResultEvent,
ToolResultEventResult,
UIPromptKind,
UserBashEvent,
UserBashEventResult,
} from "./types.ts";
Expand Down Expand Up @@ -295,6 +296,8 @@ export class ExtensionRunner {
private shortcutDiagnostics: ResourceDiagnostic[] = [];
private commandDiagnostics: ResourceDiagnostic[] = [];
private staleMessage: string | undefined;
private uiPromptDepth = 0;
private activeUIPrompt: { kind: UIPromptKind; title?: string } | undefined;

constructor(
extensions: Extension[],
Expand Down Expand Up @@ -431,10 +434,57 @@ export class ExtensionRunner {
}

setUIContext(uiContext?: ExtensionUIContext, mode: ExtensionMode = "print"): void {
this.uiContext = uiContext ?? noOpUIContext;
this.uiContext = uiContext ? this.wrapUIPromptContext(uiContext) : noOpUIContext;
this.mode = mode;
}

private wrapUIPromptContext(ui: ExtensionUIContext): ExtensionUIContext {
return {
...ui,
select: (title, options, opts) => this.withUIPrompt("select", title, () => ui.select(title, options, opts)),
confirm: (title, message, opts) => this.withUIPrompt("confirm", title, () => ui.confirm(title, message, opts)),
input: (title, placeholder, opts) =>
this.withUIPrompt("input", title, () => ui.input(title, placeholder, opts)),
editor: (title, prefill) => this.withUIPrompt("editor", title, () => ui.editor(title, prefill)),
custom: (factory, options) => this.withUIPrompt("custom", undefined, () => ui.custom(factory, options)),
};
}

private withUIPrompt<T>(kind: UIPromptKind, title: string | undefined, run: () => Promise<T>): Promise<T> {
const outerPrompt = this.uiPromptDepth++ === 0;
if (outerPrompt) {
this.activeUIPrompt = { kind, title };
this.emitUIPromptEvent({ type: "ui_prompt_start", reason: "ui_prompt", kind, ...(title ? { title } : {}) });
}

const finish = () => {
if (--this.uiPromptDepth > 0) return;
this.uiPromptDepth = 0;

const prompt = this.activeUIPrompt ?? { kind, title };
this.activeUIPrompt = undefined;
this.emitUIPromptEvent({
type: "ui_prompt_end",
reason: "ui_prompt",
kind: prompt.kind,
...(prompt.title ? { title: prompt.title } : {}),
});
};

try {
return run().finally(finish);
} catch (err) {
finish();
throw err;
}
}

private emitUIPromptEvent(event: Extract<RunnerEmitEvent, { type: "ui_prompt_start" | "ui_prompt_end" }>): void {
queueMicrotask(() => {
void this.emit(event);
});
}

getUIContext(): ExtensionUIContext {
return this.uiContext;
}
Expand Down
22 changes: 22 additions & 0 deletions packages/coding-agent/src/core/extensions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,24 @@ export interface AgentSettledEvent {
type: "agent_settled";
}

export type UIPromptKind = "select" | "confirm" | "input" | "editor" | "custom";

/** Fired when Pi starts waiting on a blocking user-facing extension UI prompt. */
export interface UIPromptStartEvent {
type: "ui_prompt_start";
reason: "ui_prompt";
kind: UIPromptKind;
title?: string;
}

/** Fired when Pi is no longer waiting on a blocking user-facing extension UI prompt. */
export interface UIPromptEndEvent {
type: "ui_prompt_end";
reason: "ui_prompt";
kind: UIPromptKind;
title?: string;
}

/** Fired at the start of each turn */
export interface TurnStartEvent {
type: "turn_start";
Expand Down Expand Up @@ -1094,6 +1112,8 @@ export type ExtensionEvent =
| AgentStartEvent
| AgentEndEvent
| AgentSettledEvent
| UIPromptStartEvent
| UIPromptEndEvent
| TurnStartEvent
| TurnEndEvent
| MessageStartEvent
Expand Down Expand Up @@ -1280,6 +1300,8 @@ export interface ExtensionAPI {
on(event: "agent_start", handler: ExtensionHandler<AgentStartEvent>): void;
on(event: "agent_end", handler: ExtensionHandler<AgentEndEvent>): void;
on(event: "agent_settled", handler: ExtensionHandler<AgentSettledEvent>): void;
on(event: "ui_prompt_start", handler: ExtensionHandler<UIPromptStartEvent>): void;
on(event: "ui_prompt_end", handler: ExtensionHandler<UIPromptEndEvent>): void;
on(event: "turn_start", handler: ExtensionHandler<TurnStartEvent>): void;
on(event: "turn_end", handler: ExtensionHandler<TurnEndEvent>): void;
on(event: "message_start", handler: ExtensionHandler<MessageStartEvent>): void;
Expand Down
11 changes: 11 additions & 0 deletions packages/coding-agent/src/core/settings-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ export interface Settings {
tuiMode?: TuiMode; // default: "regular"
fullscreenExitOutput?: FullscreenExitOutput; // default: "transcript"; no effect in regular TUI mode
fullscreenScrollbar?: ScrollViewScrollbar; // default: "auto"; no effect in regular TUI mode
fullscreenCopyOnSelect?: boolean; // default: true; no effect in regular TUI mode
}

function isMergeableObject(value: unknown): value is Record<string, unknown> {
Expand Down Expand Up @@ -1233,6 +1234,16 @@ export class SettingsManager {
this.save();
}

getFullscreenCopyOnSelect(): boolean {
return this.settings.fullscreenCopyOnSelect ?? true;
}

setFullscreenCopyOnSelect(enabled: boolean): void {
this.globalSettings.fullscreenCopyOnSelect = enabled;
this.markModified("fullscreenCopyOnSelect");
this.save();
}

getImageAutoResize(): boolean {
return this.settings.images?.autoResize ?? true;
}
Expand Down
3 changes: 3 additions & 0 deletions packages/coding-agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ export type {
ToolResultEvent,
TurnEndEvent,
TurnStartEvent,
UIPromptEndEvent,
UIPromptKind,
UIPromptStartEvent,
UserBashEvent,
UserBashEventResult,
WidgetPlacement,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export interface SettingsConfig {
tuiMode: TuiMode;
fullscreenExitOutput: FullscreenExitOutput;
fullscreenScrollbar: ScrollViewScrollbar;
fullscreenCopyOnSelect: boolean;
warnings: WarningSettings;
}

Expand Down Expand Up @@ -118,6 +119,7 @@ export interface SettingsCallbacks {
onTuiModeChange: (mode: TuiMode) => void;
onFullscreenExitOutputChange: (output: FullscreenExitOutput) => void;
onFullscreenScrollbarChange: (mode: ScrollViewScrollbar) => void;
onFullscreenCopyOnSelectChange: (enabled: boolean) => void;
onWarningsChange: (warnings: WarningSettings) => void;
onCancel: () => void;
}
Expand Down Expand Up @@ -680,6 +682,13 @@ export class SettingsSelectorComponent extends Container {
currentValue: config.fullscreenScrollbar,
values: ["auto", "always", "hidden"],
},
{
id: "fullscreen-copy-on-select",
label: "Fullscreen copy on select",
description: "Automatically copy selected text in fullscreen mode; disable to copy selections with Ctrl+X",
currentValue: config.fullscreenCopyOnSelect ? "true" : "false",
values: ["true", "false"],
},
{
id: "theme",
label: "Theme",
Expand Down Expand Up @@ -898,6 +907,9 @@ export class SettingsSelectorComponent extends Container {
case "fullscreen-scrollbar":
callbacks.onFullscreenScrollbarChange(newValue as ScrollViewScrollbar);
break;
case "fullscreen-copy-on-select":
callbacks.onFullscreenCopyOnSelectChange(newValue === "true");
break;
case "theme":
callbacks.onThemeChange(newValue);
break;
Expand Down
Loading