@oh-my-pi/pi-coding-agent 17.2.12 → 17.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/dist/{CHANGELOG-k9ghy5sn.md → CHANGELOG-d8xh7keh.md} +57 -0
  3. package/dist/cli.js +3069 -3047
  4. package/dist/types/advisor/delta-split.d.ts +24 -0
  5. package/dist/types/advisor/runtime.d.ts +2 -2
  6. package/dist/types/async/job-manager.d.ts +8 -1
  7. package/dist/types/cli/update-cli.d.ts +53 -1
  8. package/dist/types/config/keybindings.d.ts +10 -0
  9. package/dist/types/config/model-resolver.d.ts +15 -2
  10. package/dist/types/config/settings-schema.d.ts +4 -0
  11. package/dist/types/discovery/agents-md.d.ts +10 -1
  12. package/dist/types/eval/runner-cache.d.ts +12 -0
  13. package/dist/types/extensibility/extensions/runner.d.ts +12 -3
  14. package/dist/types/extensibility/extensions/types.d.ts +15 -4
  15. package/dist/types/extensibility/plugins/marketplace/manager.d.ts +4 -1
  16. package/dist/types/lib/xai-http.d.ts +0 -1
  17. package/dist/types/mcp/tool-bridge.d.ts +8 -5
  18. package/dist/types/modes/components/agent-hub-renderer.d.ts +6 -1
  19. package/dist/types/modes/components/status-line/types.d.ts +4 -0
  20. package/dist/types/modes/controllers/extension-ui-controller.d.ts +2 -4
  21. package/dist/types/modes/interactive-mode.d.ts +17 -8
  22. package/dist/types/modes/types.d.ts +7 -10
  23. package/dist/types/modes/utils/hotkeys-markdown.d.ts +1 -1
  24. package/dist/types/session/agent-session-types.d.ts +2 -0
  25. package/dist/types/session/agent-session.d.ts +22 -3
  26. package/dist/types/session/messages.d.ts +20 -0
  27. package/dist/types/session/retry-fallback-chains.d.ts +13 -0
  28. package/dist/types/session/session-advisors.d.ts +1 -1
  29. package/dist/types/session/session-history-format.d.ts +10 -0
  30. package/dist/types/session/session-maintenance.d.ts +1 -1
  31. package/dist/types/session/session-tools.d.ts +24 -5
  32. package/dist/types/session/turn-recovery.d.ts +34 -5
  33. package/dist/types/slash-commands/types.d.ts +5 -1
  34. package/dist/types/task/executor.d.ts +1 -1
  35. package/dist/types/tools/approval.d.ts +7 -0
  36. package/dist/types/tools/todo.d.ts +14 -15
  37. package/dist/types/tools/write.d.ts +2 -2
  38. package/dist/types/utils/local-date.d.ts +2 -0
  39. package/dist/types/vibe/runtime.d.ts +1 -1
  40. package/dist/types/web/parallel.d.ts +1 -0
  41. package/dist/types/web/search/providers/brave.d.ts +8 -3
  42. package/dist/types/web/search/providers/codex.d.ts +6 -0
  43. package/dist/types/web/search/providers/firecrawl.d.ts +3 -2
  44. package/dist/types/web/search/providers/jina.d.ts +3 -3
  45. package/dist/types/web/search/providers/parallel.d.ts +1 -0
  46. package/dist/types/web/search/providers/perplexity.d.ts +4 -0
  47. package/dist/types/web/search/providers/tinyfish.d.ts +2 -0
  48. package/package.json +13 -13
  49. package/src/advisor/delta-split.ts +98 -0
  50. package/src/advisor/runtime.ts +321 -69
  51. package/src/async/job-manager.ts +14 -3
  52. package/src/cli/plugin-cli.ts +30 -2
  53. package/src/cli/update-cli.ts +259 -24
  54. package/src/config/keybindings.ts +52 -9
  55. package/src/config/model-resolver.ts +19 -3
  56. package/src/config/settings-schema.ts +5 -0
  57. package/src/cursor.ts +10 -5
  58. package/src/discovery/agents-md.ts +61 -23
  59. package/src/eval/jl/kernel.ts +2 -20
  60. package/src/eval/py/kernel.ts +2 -20
  61. package/src/eval/rb/kernel.ts +2 -20
  62. package/src/eval/runner-cache.ts +41 -0
  63. package/src/exec/non-interactive-env.ts +14 -3
  64. package/src/extensibility/extensions/loader.ts +5 -2
  65. package/src/extensibility/extensions/runner.ts +184 -66
  66. package/src/extensibility/extensions/types.ts +26 -2
  67. package/src/extensibility/extensions/wrapper.ts +13 -7
  68. package/src/extensibility/plugins/marketplace/manager.ts +6 -2
  69. package/src/hindsight/client.ts +1 -1
  70. package/src/lib/xai-http.ts +0 -4
  71. package/src/lsp/client.ts +2 -0
  72. package/src/lsp/servers.ts +1 -1
  73. package/src/mcp/tool-bridge.ts +15 -6
  74. package/src/modes/components/agent-hub-renderer.ts +9 -3
  75. package/src/modes/components/agent-hub.ts +2 -1
  76. package/src/modes/components/status-line/component.ts +58 -6
  77. package/src/modes/components/status-line/segments.ts +12 -1
  78. package/src/modes/components/status-line/types.ts +1 -0
  79. package/src/modes/components/user-message.ts +20 -5
  80. package/src/modes/controllers/event-controller.ts +48 -0
  81. package/src/modes/controllers/extension-ui-controller.ts +14 -7
  82. package/src/modes/controllers/input-controller.ts +25 -6
  83. package/src/modes/interactive-mode.ts +315 -129
  84. package/src/modes/rpc/rpc-frame.ts +13 -5
  85. package/src/modes/theme/tui-adapters.ts +4 -5
  86. package/src/modes/types.ts +13 -7
  87. package/src/modes/utils/hotkeys-markdown.ts +10 -6
  88. package/src/prompts/system/system-prompt.md +1 -1
  89. package/src/registry/persisted-agents.ts +43 -8
  90. package/src/sdk.ts +139 -8
  91. package/src/session/agent-session-types.ts +2 -0
  92. package/src/session/agent-session.ts +66 -10
  93. package/src/session/messages.ts +98 -28
  94. package/src/session/retry-fallback-chains.ts +14 -0
  95. package/src/session/session-advisors.ts +32 -15
  96. package/src/session/session-history-format.ts +15 -1
  97. package/src/session/session-maintenance.ts +8 -8
  98. package/src/session/session-manager.ts +6 -2
  99. package/src/session/session-tools.ts +321 -184
  100. package/src/session/turn-recovery.ts +225 -47
  101. package/src/slash-commands/builtin-modes.ts +41 -12
  102. package/src/slash-commands/types.ts +5 -1
  103. package/src/task/executor.ts +92 -45
  104. package/src/task/structured-subagent.ts +5 -5
  105. package/src/tools/approval.ts +44 -10
  106. package/src/tools/fetch.ts +21 -2
  107. package/src/tools/image-gen.ts +6 -8
  108. package/src/tools/todo.ts +70 -26
  109. package/src/tools/tts.ts +3 -2
  110. package/src/tools/write.ts +7 -3
  111. package/src/utils/local-date.ts +13 -0
  112. package/src/utils/tools-manager.ts +2 -2
  113. package/src/vibe/runtime.ts +22 -14
  114. package/src/web/kagi.ts +91 -34
  115. package/src/web/parallel.ts +11 -2
  116. package/src/web/scrapers/crates-io.ts +2 -2
  117. package/src/web/scrapers/discogs.ts +2 -2
  118. package/src/web/scrapers/docs-rs.ts +2 -2
  119. package/src/web/scrapers/github.ts +2 -2
  120. package/src/web/scrapers/musicbrainz.ts +1 -2
  121. package/src/web/scrapers/pubmed.ts +2 -2
  122. package/src/web/scrapers/sec-edgar.ts +2 -2
  123. package/src/web/search/providers/brave.ts +121 -46
  124. package/src/web/search/providers/codex.ts +88 -12
  125. package/src/web/search/providers/exa.ts +45 -10
  126. package/src/web/search/providers/firecrawl.ts +53 -11
  127. package/src/web/search/providers/gemini.ts +139 -27
  128. package/src/web/search/providers/jina.ts +48 -25
  129. package/src/web/search/providers/parallel.ts +23 -9
  130. package/src/web/search/providers/perplexity.ts +24 -7
  131. package/src/web/search/providers/searxng.ts +77 -1
  132. package/src/web/search/providers/tavily.ts +23 -22
  133. package/src/web/search/providers/tinyfish.ts +44 -10
  134. package/src/web/search/providers/xai.ts +85 -14
@@ -0,0 +1,24 @@
1
+ import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
2
+ /**
3
+ * Obfuscation surface the split renderer needs: a single text redaction pass.
4
+ * Narrowed from the full SecretObfuscator class to the method actually
5
+ * consumed, so tests can satisfy the contract with a typed helper instead of
6
+ * an `as any` escape. A SecretObfuscator instance is structurally assignable.
7
+ */
8
+ export interface AdvisorObfuscator {
9
+ obfuscate(text: string, sharedRegexSecretValues?: ReadonlySet<string>): string;
10
+ }
11
+ /** Render options shared by the advisor single-block and multi-message paths. */
12
+ export declare const ADVISOR_RENDER_OPTIONS: {
13
+ readonly includeToolIntent: true;
14
+ readonly watchedRoles: true;
15
+ readonly expandPrimaryContext: true;
16
+ readonly expandEditDiffs: true;
17
+ };
18
+ export interface RenderAdvisorDeltaChunksOptions {
19
+ wip: boolean;
20
+ includeThinking: boolean;
21
+ obfuscator?: AdvisorObfuscator;
22
+ advisorRegexSecretValues: ReadonlySet<string>;
23
+ }
24
+ export declare function renderAdvisorDeltaChunks(delta: AgentMessage[], opts: RenderAdvisorDeltaChunksOptions): AgentMessage[] | null;
@@ -8,7 +8,7 @@ import type { SecretObfuscator } from "../secrets/obfuscator.js";
8
8
  * this field after every prompt to detect a failed turn.
9
9
  */
10
10
  export interface AdvisorAgent {
11
- prompt(input: string): Promise<void>;
11
+ prompt(input: string | AgentMessage[]): Promise<void>;
12
12
  abort(reason?: unknown): void;
13
13
  reset(): void;
14
14
  /**
@@ -129,7 +129,7 @@ export declare class AdvisorRuntime {
129
129
  * post-compaction — transcript, giving the advisor fresh context instead of
130
130
  * leaving it blind to everything before the rewrite.
131
131
  */
132
- reset(): void;
132
+ reset(reason?: string): void;
133
133
  /**
134
134
  * Seed the cursor to the current transcript length when the advisor is enabled
135
135
  * mid-session. Prevents the next turn from replaying the entire history to the
@@ -1,3 +1,5 @@
1
+ /** Abort reason used only when the owning session shuts down the entire manager. */
2
+ export declare const ASYNC_JOB_MANAGER_SHUTDOWN_REASON: unique symbol;
1
3
  export interface AsyncJob {
2
4
  id: string;
3
5
  type: "bash" | "task";
@@ -132,8 +134,13 @@ export declare class AsyncJobManager {
132
134
  * Cancel running jobs. With `filter.ownerId` set, cancels only jobs the
133
135
  * matching agent registered; with no filter, cancels every running job
134
136
  * (used by `dispose()` to nuke the manager's state).
137
+ *
138
+ * `reason` is forwarded to each job's `AbortController.abort`, so a session
139
+ * teardown can tag its owned jobs with {@link ASYNC_JOB_MANAGER_SHUTDOWN_REASON}
140
+ * before `dispose()` runs — the task executor reads it to park (not
141
+ * tombstone) a subagent interrupted purely by process shutdown.
135
142
  */
136
- cancelAll(filter?: AsyncJobFilter): void;
143
+ cancelAll(filter?: AsyncJobFilter, reason?: unknown): void;
137
144
  /**
138
145
  * Immediately evict completed and failed jobs matching the filter instead of
139
146
  * waiting for retention expiry, dropping every queued delivery so a prior
@@ -1,9 +1,37 @@
1
+ /** Distribution channel advertised by a release's published npm manifest. */
2
+ export type ReleaseDist = "npm" | "binary";
1
3
  export interface ReleaseBinaryAsset {
2
4
  url: string;
3
5
  size: number;
4
6
  digest: string;
5
7
  }
6
8
  type Fetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
9
+ /**
10
+ * Parse the `omp.dist` field from a published package manifest.
11
+ *
12
+ * Forward-compatibility contract with future releases: a release that is not
13
+ * installable as an npm package (e.g. a native rewrite) publishes
14
+ * `"omp": { "dist": "binary" }` in its package.json. Any value other than
15
+ * "npm" — including values this updater does not know yet — maps to "binary"
16
+ * so already-deployed updaters never run a package-manager install against a
17
+ * release that no longer supports it.
18
+ */
19
+ export declare function resolveReleaseDist(manifest: unknown): ReleaseDist | undefined;
20
+ /**
21
+ * Whether the update must bypass bun/npm and install the release binary.
22
+ *
23
+ * An explicit `omp.dist` wins in both directions. Without one, a release with
24
+ * a higher major than the running build is assumed not npm-installable: the
25
+ * runtime may have changed out from under the package layout, and the pinned
26
+ * `@oh-my-pi/pi-natives*` companions ({@link buildBunInstallArgs}) may not
27
+ * exist at that version, which would strand bun/npm-managed installs behind a
28
+ * hard install failure. Homebrew and mise installs are unaffected — both
29
+ * already pull GitHub release binaries.
30
+ */
31
+ export declare function shouldForceBinaryUpdate(release: {
32
+ version: string;
33
+ dist?: ReleaseDist;
34
+ }, currentVersion?: string): boolean;
7
35
  /**
8
36
  * Select and validate the binary asset from GitHub release metadata.
9
37
  */
@@ -86,7 +114,11 @@ interface MuslDetectionOptions {
86
114
  /** Test seam for libc detection. */
87
115
  export declare function isMuslLinuxForTest(options: Required<MuslDetectionOptions>): boolean;
88
116
  /**
89
- * Run the resolved omp binary and check if it reports the expected version.
117
+ * Run a specific binary and check if it reports the expected version.
118
+ */
119
+ declare function verifyBinaryAtPath(binaryPath: string, expectedVersion: string): Promise<InstalledVersionVerification>;
120
+ /**
121
+ * Run the PATH-resolved omp binary and check if it reports the expected version.
90
122
  */
91
123
  declare function verifyInstalledVersion(expectedVersion: string): Promise<InstalledVersionVerification>;
92
124
  /**
@@ -149,6 +181,26 @@ export declare function updateViaBinaryAt(targetPath: string, expectedVersion: s
149
181
  githubToken?: string;
150
182
  verifyInstalledVersion?: typeof verifyInstalledVersion;
151
183
  }): Promise<void>;
184
+ /**
185
+ * Take over a Windows script-launcher install for a binary-only release.
186
+ *
187
+ * npm-managed Windows installs are launched through script shims
188
+ * (`omp`/`omp.cmd`/`omp.ps1`) that cannot be overwritten with a native
189
+ * executable. The release binary is installed as `omp.exe` beside them and
190
+ * the shims are then renamed aside: cmd.exe would already prefer `.exe` via
191
+ * PATHEXT, but PowerShell resolves `.ps1` first, so the takeover only sticks
192
+ * once the shims are out of the way. A working launcher exists at every
193
+ * step — the exe lands before any shim moves, a shim that refuses to move
194
+ * (a running `.cmd` can be renamed but may be held open some other way) is
195
+ * rewritten in place as a forwarder to the exe, and a failed version
196
+ * verification moves everything back.
197
+ */
198
+ export declare function updateViaShimTakeover(shimPath: string, expectedVersion: string, options?: {
199
+ binaryName?: string;
200
+ fetchImpl?: Fetch;
201
+ githubToken?: string;
202
+ verifyBinary?: typeof verifyBinaryAtPath;
203
+ }): Promise<void>;
152
204
  /**
153
205
  * Run the update command.
154
206
  */
@@ -371,6 +371,16 @@ export declare class KeybindingsManager extends TuiKeybindingsManager {
371
371
  */
372
372
  getDisplayString(keybinding: Keybinding): string;
373
373
  }
374
+ /** Pin the platform used to render modifier labels (test seam). */
375
+ export declare function setKeyHintPlatform(platform: NodeJS.Platform | undefined): void;
376
+ /** Platform currently used for key-hint rendering. */
377
+ export declare function keyHintPlatform(): NodeJS.Platform;
378
+ type Modifier = "ctrl" | "shift" | "alt" | "super";
379
+ /**
380
+ * Human label for a modifier, using each platform's own key names. `ctrl` and
381
+ * `shift` are the same everywhere; `alt`/`super` become `Option`/`Cmd` on macOS.
382
+ */
383
+ export declare function modifierLabel(mod: Modifier, platform?: NodeJS.Platform): string;
374
384
  export declare function formatKeyHint(key: KeyId): string;
375
385
  export declare function formatKeyHints(keys: KeyId | KeyId[]): string;
376
386
  export type { Keybinding, KeybindingsConfig, KeyId };
@@ -120,8 +120,21 @@ export interface AgentModelPatternResolutionOptions {
120
120
  activeModelPattern?: string;
121
121
  fallbackModelPattern?: string;
122
122
  }
123
- /** Return the raw selector source that supplies the effective agent patterns. */
124
- export declare function resolveAgentModelSource(options: AgentModelPatternResolutionOptions): string | string[] | undefined;
123
+ /** Effective agent model patterns paired with the pre-expansion role alias behind them. */
124
+ export interface AgentModelSelection {
125
+ /** Expanded model patterns to spawn with. */
126
+ patterns: string[];
127
+ /** Role alias the patterns came from (`@task` -> `task`), when the source named one. */
128
+ role: string | undefined;
129
+ }
130
+ /**
131
+ * Resolve an agent's model patterns together with the role identity they were
132
+ * expanded from. Spawn paths MUST take both from this single call: the child's
133
+ * inherited retry-fallback chain is keyed off the role, which the expansion
134
+ * discards, and deriving the two halves separately is how they drift apart.
135
+ */
136
+ export declare function resolveAgentModelSelection(options: AgentModelPatternResolutionOptions): AgentModelSelection;
137
+ /** Effective agent model patterns alone, for callers with no interest in role identity. */
125
138
  export declare function resolveAgentModelPatterns(options: AgentModelPatternResolutionOptions): string[];
126
139
  /** Default prewalk hand-off target when no explicit target is configured. */
127
140
  export declare const DEFAULT_PREWALK_TARGET = "@smol";
@@ -6030,6 +6030,10 @@ export declare const SETTINGS_SCHEMA: {
6030
6030
  readonly type: "string";
6031
6031
  readonly default: undefined;
6032
6032
  };
6033
+ readonly "searxng.safesearch": {
6034
+ readonly type: "number";
6035
+ readonly default: undefined;
6036
+ };
6033
6037
  readonly "commit.mapReduceEnabled": {
6034
6038
  readonly type: "boolean";
6035
6039
  readonly default: true;
@@ -1 +1,10 @@
1
- export {};
1
+ import { type ContextFile } from "../capability/context-file.js";
2
+ import type { LoadContext, LoadResult } from "../capability/types.js";
3
+ /**
4
+ * Load standalone AGENTS.md files.
5
+ *
6
+ * When a repository is nested below the user's home directory, continue past
7
+ * the Git root to discover workspace-level AGENTS.md files, but stop before
8
+ * loading the home directory's own AGENTS.md as project context.
9
+ */
10
+ export declare function loadAgentsMd(ctx: LoadContext): Promise<LoadResult<ContextFile>>;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Stage `script` under `os.tmpdir()/<dirName>` and return the runner path.
3
+ *
4
+ * The staged path is memoized per `dirName` but re-checked with `fs.existsSync`
5
+ * before reuse, so a runner deleted mid-session is re-written on the next call
6
+ * instead of handing back a path to a missing file (issue #8140).
7
+ *
8
+ * @param dirName Cache subdirectory under the OS temp dir (unique per language).
9
+ * @param ext Runner file extension without the dot (e.g. `py`, `jl`, `rb`).
10
+ * @param script Runner source, hashed to key the cached file per version.
11
+ */
12
+ export declare function stageRunnerScript(dirName: string, ext: string, script: string): Promise<string>;
@@ -1,6 +1,3 @@
1
- /**
2
- * Extension runner - executes extensions and manages their lifecycle.
3
- */
4
1
  import type { AgentMessage, AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
5
2
  import type { CredentialDisabledEvent, ImageContent, Model, ProviderResponseMetadata } from "@oh-my-pi/pi-ai";
6
3
  import type { KeyId } from "@oh-my-pi/pi-tui";
@@ -152,11 +149,23 @@ export declare class ExtensionRunner {
152
149
  emitMcpNotification(event: Omit<McpNotificationEvent, "type">): Promise<void>;
153
150
  /** Emits a session stop pass that can be cancelled with the active settle signal. */
154
151
  emitSessionStop(event: Omit<SessionStopEvent, "type">): Promise<SessionStopEventResult | undefined>;
152
+ /** Registers the interactive transcript gate that must settle before a tool approval is presented. */
153
+ setToolApprovalPreviewWaiter(waiter: (toolCallId: string) => Promise<void>): () => void;
154
+ /** Waits until the interactive transcript can show the tool call being approved. */
155
+ waitForToolApprovalPreview(toolCallId: string): Promise<void>;
155
156
  getUIContext(): ExtensionUIContext;
156
157
  hasUI(): boolean;
157
158
  getExtensionPaths(): string[];
158
159
  /** Get all registered tools from all extensions. */
159
160
  getAllRegisteredTools(): RegisteredTool[];
161
+ /** Get the effective registered tool for a name using normal last-extension-wins precedence. */
162
+ getRegisteredTool(name: string): RegisteredTool | undefined;
163
+ /**
164
+ * Observe tools registered after extension factories have loaded. Listener
165
+ * promises are drained before the lifecycle handler that registered them
166
+ * completes, keeping the model tool snapshot and system prompt coherent.
167
+ */
168
+ onToolRegistered(listener: (tool: RegisteredTool, signal?: AbortSignal) => void | Promise<void>): () => void;
160
169
  /**
161
170
  * Aggregate the registered CLI flags across a set of extensions (last write
162
171
  * wins on name collision). Static so callers that need the flag set before a
@@ -14,7 +14,7 @@ import type { AgentMessage, AgentToolResult, AgentToolUpdateCallback, ThinkingLe
14
14
  import type { CompactionResult } from "@oh-my-pi/pi-agent-core/compaction";
15
15
  import type { Api, AssistantMessageEvent, AssistantMessageEventStream, Context, ImageContent, Model, ModelSpec, ProviderResponseMetadata, ServiceTier, ServiceTierByFamily, ServiceTierFamily, SimpleStreamOptions, Static, TextContent, TSchema } from "@oh-my-pi/pi-ai";
16
16
  import type { OAuthCredentials, OAuthLoginCallbacks } from "@oh-my-pi/pi-ai/oauth/types";
17
- import type { AutocompleteItem, AutocompleteProvider, Component, EditorTheme, KeyId, TUI } from "@oh-my-pi/pi-tui";
17
+ import type { AutocompleteItem, AutocompleteProvider, Component, EditorTheme, KeyId, OverlayHandle, OverlayOptions, TUI } from "@oh-my-pi/pi-tui";
18
18
  import type { logger as PiLogger } from "@oh-my-pi/pi-utils";
19
19
  import type { KeybindingsManager } from "../../config/keybindings.js";
20
20
  import type { ModelRegistry } from "../../config/model-registry.js";
@@ -36,6 +36,7 @@ import type { ApprovalMode } from "../../tools/approval.js";
36
36
  import type { EventBus } from "../../utils/event-bus.js";
37
37
  import type { AgentEndEvent, AgentStartEvent, AutoCompactionEndEvent, AutoCompactionStartEvent, AutoRetryEndEvent, AutoRetryStartEvent, ContextEvent, GoalUpdatedEvent, SessionBeforeBranchEvent, SessionBeforeBranchResult, SessionBeforeCompactEvent, SessionBeforeCompactResult, SessionBeforeSwitchEvent, SessionBeforeSwitchResult, SessionBeforeTreeEvent, SessionBeforeTreeResult, SessionBranchEvent, SessionCompactEvent, SessionCompactingEvent, SessionCompactingResult, SessionEvent, SessionShutdownEvent, SessionStartEvent, SessionStopEvent, SessionStopEventResult, SessionSwitchEvent, SessionTreeEvent, TodoReminderEvent, ToolCallEventResult, ToolResultEventResult, TtsrTriggeredEvent, TurnEndEvent, TurnStartEvent } from "../shared-events.js";
38
38
  import type { SlashCommandInfo } from "../slash-commands.js";
39
+ export type { OverlayHandle, OverlayOptions } from "@oh-my-pi/pi-tui";
39
40
  export type { AppKeybinding, KeybindingsManager } from "../../config/keybindings.js";
40
41
  export type { ExecOptions, ExecResult } from "../../exec/exec.js";
41
42
  export type { AgentToolResult, AgentToolUpdateCallback };
@@ -129,6 +130,15 @@ export type ExtensionUiComponent = Component & {
129
130
  };
130
131
  export type ExtensionUiComponentFactory = (tui: TUI, theme: Theme) => ExtensionUiComponent;
131
132
  export type ExtensionWidgetContent = string[] | ExtensionUiComponentFactory | undefined;
133
+ /** Options for `ExtensionUIContext.custom()` (overlay rendering of a custom component). */
134
+ export interface ExtensionCustomOptions {
135
+ /** Render the component as an overlay over the transcript instead of replacing the editor area. */
136
+ overlay?: boolean;
137
+ /** Static or lazily resolved overlay positioning/sizing options forwarded to `showOverlay`. */
138
+ overlayOptions?: OverlayOptions | (() => OverlayOptions);
139
+ /** Invoked with the overlay handle once the overlay is created (overlay mode only). */
140
+ onHandle?: (handle: OverlayHandle) => void;
141
+ }
132
142
  /** Wrap the current autocomplete provider with additional behavior (pi-compatible). */
133
143
  export type AutocompleteProviderFactory = (current: AutocompleteProvider) => AutocompleteProvider;
134
144
  /**
@@ -163,9 +173,7 @@ export interface ExtensionUIContext {
163
173
  /** Set the terminal window/tab title. */
164
174
  setTitle(title: string): void;
165
175
  /** Show a custom component with keyboard focus. */
166
- custom<T>(factory: (tui: TUI, theme: Theme, keybindings: KeybindingsManager, done: (result: T) => void) => ExtensionUiComponent | Promise<ExtensionUiComponent>, options?: {
167
- overlay?: boolean;
168
- }): Promise<T>;
176
+ custom<T>(factory: (tui: TUI, theme: Theme, keybindings: KeybindingsManager, done: (result: T) => void) => ExtensionUiComponent | Promise<ExtensionUiComponent>, options?: ExtensionCustomOptions): Promise<T>;
169
177
  /** Set the text in the core input editor. */
170
178
  setEditorText(text: string): void;
171
179
  /**
@@ -1003,6 +1011,8 @@ export interface RegisteredTool<TParams extends TSchema = TSchema, TDetails = un
1003
1011
  definition: ToolDefinition<TParams, TDetails>;
1004
1012
  extensionPath: string;
1005
1013
  }
1014
+ /** Internal observer invoked when an already-loaded extension registers or replaces a tool. */
1015
+ export type ToolRegistrationListener = (toolName: string) => void;
1006
1016
  export interface ExtensionFlag {
1007
1017
  name: string;
1008
1018
  description?: string;
@@ -1119,6 +1129,7 @@ export interface Extension {
1119
1129
  label?: string;
1120
1130
  handlers: Map<string, HandlerFn[]>;
1121
1131
  tools: Map<string, RegisteredTool<any, any>>;
1132
+ toolRegistrationListeners?: Set<ToolRegistrationListener>;
1122
1133
  assistantThinkingRenderers: AssistantThinkingRenderer[];
1123
1134
  messageRenderers: Map<string, MessageRenderer>;
1124
1135
  commands: Map<string, RegisteredCommand>;
@@ -36,7 +36,10 @@ export declare class MarketplaceManager {
36
36
  force?: boolean;
37
37
  scope?: "user" | "project";
38
38
  }): Promise<InstalledPluginEntry>;
39
- uninstallPlugin(pluginId: string, scope?: "user" | "project"): Promise<void>;
39
+ /** Validates and removes a marketplace plugin, or only validates when `dryRun` is set. */
40
+ uninstallPlugin(pluginId: string, scope?: "user" | "project", options?: {
41
+ dryRun?: boolean;
42
+ }): Promise<void>;
40
43
  listInstalledPlugins(): Promise<InstalledPluginSummary[]>;
41
44
  setPluginEnabled(pluginId: string, enabled: boolean, scope?: "user" | "project"): Promise<void>;
42
45
  refreshStaleMarketplaces(): Promise<void>;
@@ -4,7 +4,6 @@ interface XAICredentials {
4
4
  apiKey: string;
5
5
  baseURL: string;
6
6
  }
7
- export declare function ohMyPiXAIUserAgent(): string;
8
7
  /** xAI provider ids supported by shared HTTP tool transport resolution. */
9
8
  export type XAIHttpProvider = "xai-oauth" | "xai";
10
9
  /** Resolved endpoint and configured headers for an xAI HTTP tool request. */
@@ -35,6 +35,13 @@ export interface MCPToolDetails {
35
35
  meta?: OutputMeta;
36
36
  }
37
37
  export declare function createMCPToolName(serverName: string, toolName: string): string;
38
+ export interface MCPToolOriginSource {
39
+ readonly name: string;
40
+ readonly mcpServerName?: unknown;
41
+ readonly mcpToolName?: unknown;
42
+ }
43
+ /** Stable identity for a tool's original MCP route, before its public name was normalized. */
44
+ export declare function getMCPToolOriginKey(tool: MCPToolOriginSource): string | undefined;
38
45
  /**
39
46
  * Keeps one MCP tool per minted name and logs collisions between distinct MCP
40
47
  * origins. The winner is chosen by a stable origin key (server name + original
@@ -43,11 +50,7 @@ export declare function createMCPToolName(serverName: string, toolName: string):
43
50
  * silently flip ownership of the minted name. Non-MCP tools pass through
44
51
  * unchanged.
45
52
  */
46
- export declare function deduplicateMCPToolsByName<T extends {
47
- name: string;
48
- mcpServerName?: unknown;
49
- mcpToolName?: unknown;
50
- }>(tools: readonly T[]): T[];
53
+ export declare function deduplicateMCPToolsByName<T extends MCPToolOriginSource>(tools: readonly T[]): T[];
51
54
  /**
52
55
  * Parse an MCP tool name back to server and tool components.
53
56
  *
@@ -21,7 +21,12 @@ export declare function formatRoleBadge(role: string, settings: Settings): strin
21
21
  /**
22
22
  * Resolved model + reasoning level for a hub row. Exact executor progress is
23
23
  * authoritative (and survives completion); direct live sessions are the
24
- * fallback for agents without an observer snapshot.
24
+ * fallback for agents without an observer snapshot — the main session has no
25
+ * snapshot at all, so its row is read straight off the live session.
26
+ *
27
+ * Every source reports the model that produced the row's work, never the one
28
+ * the session merely points at: an armed fallback that has not served yet stays
29
+ * attributed to whichever model last actually spoke.
25
30
  */
26
31
  export declare function modelBadge(ref: AgentRef, observed: ObservableSession | undefined): string | undefined;
27
32
  export declare function formatMetricDuration(metrics: AgentMetrics): string | undefined;
@@ -137,6 +137,10 @@ export interface SegmentContext {
137
137
  percent: number;
138
138
  resetHours?: number;
139
139
  };
140
+ monthly?: {
141
+ percent: number;
142
+ resetHours?: number;
143
+ };
140
144
  } | null;
141
145
  }
142
146
  export interface RenderedSegment {
@@ -1,6 +1,6 @@
1
1
  import type { Component, TUI } from "@oh-my-pi/pi-tui";
2
2
  import { KeybindingsManager } from "../../config/keybindings.js";
3
- import type { ExtensionAskDialogQuestion, ExtensionAskDialogResult, ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectItem, ExtensionWidgetContent, ExtensionWidgetOptions, TerminalInputHandler } from "../../extensibility/extensions/index.js";
3
+ import type { ExtensionAskDialogQuestion, ExtensionAskDialogResult, ExtensionCustomOptions, ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectItem, ExtensionWidgetContent, ExtensionWidgetOptions, TerminalInputHandler } from "../../extensibility/extensions/index.js";
4
4
  import { type HookSelectorSlider } from "../../modes/components/hook-selector.js";
5
5
  import { type Theme } from "../../modes/theme/theme.js";
6
6
  import type { InteractiveModeContext, InteractiveSelectorDialogOptions } from "../../modes/types.js";
@@ -84,9 +84,7 @@ export declare class ExtensionUiController {
84
84
  dispose?(): void;
85
85
  }) | Promise<Component & {
86
86
  dispose?(): void;
87
- }>, options?: {
88
- overlay?: boolean;
89
- }): Promise<T>;
87
+ }>, options?: ExtensionCustomOptions): Promise<T>;
90
88
  /**
91
89
  * Show an extension error in the UI.
92
90
  */
@@ -7,7 +7,7 @@ import type { CollabGuestLink } from "../collab/guest.js";
7
7
  import type { CollabHost } from "../collab/host.js";
8
8
  import { KeybindingsManager } from "../config/keybindings.js";
9
9
  import { Settings } from "../config/settings.js";
10
- import type { AutocompleteProviderFactory, ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectItem, ExtensionWidgetContent, ExtensionWidgetOptions } from "../extensibility/extensions/index.js";
10
+ import type { AutocompleteProviderFactory, ExtensionCustomOptions, ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectItem, ExtensionWidgetContent, ExtensionWidgetOptions } from "../extensibility/extensions/index.js";
11
11
  import type { CompactOptions } from "../extensibility/extensions/types.js";
12
12
  import type { Skill } from "../extensibility/skills.js";
13
13
  import type { MCPManager } from "../mcp/index.js";
@@ -94,6 +94,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
94
94
  omfgContainer: Container;
95
95
  errorBannerContainer: Container;
96
96
  modelCycleContainer: Container;
97
+ deferredCommandContainer: Container;
97
98
  editor: CustomEditor;
98
99
  editorContainer: Container;
99
100
  hookWidgetContainerAbove: Container;
@@ -224,6 +225,8 @@ export declare class InteractiveMode implements InteractiveModeContext {
224
225
  customType?: string;
225
226
  display?: boolean;
226
227
  streamingBehavior?: "steer" | "followUp";
228
+ }, options?: {
229
+ preserveDraft?: boolean;
227
230
  }): SubmittedUserInput;
228
231
  cancelPendingSubmission(): boolean;
229
232
  markPendingSubmissionStarted(input: SubmittedUserInput): boolean;
@@ -257,7 +260,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
257
260
  }, extra?: {
258
261
  slider?: HookSelectorSlider;
259
262
  }): Promise<string | undefined>;
260
- handlePlanModeCommand(initialPrompt?: string): Promise<void>;
263
+ handlePlanModeCommand(initialPrompt?: string, input?: Pick<SubmittedUserInput, "images" | "imageLinks">): Promise<boolean>;
261
264
  /**
262
265
  * `/vibe` toggle. Entering installs the ephemeral vibe tools, strips the
263
266
  * active toolset down to `read`, optional parent-owned `todo`, plus those
@@ -265,9 +268,9 @@ export declare class InteractiveMode implements InteractiveModeContext {
265
268
  * the previous toolset, and kills every worker session so workers cannot
266
269
  * outlive the mode that directs them.
267
270
  */
268
- handleVibeModeCommand(initialPrompt?: string): Promise<void>;
269
- handleGoalModeCommand(rest?: string): Promise<void>;
270
- handleGuidedGoalCommand(rest?: string): Promise<void>;
271
+ handleVibeModeCommand(initialPrompt?: string, input?: Pick<SubmittedUserInput, "images" | "imageLinks">): Promise<boolean>;
272
+ handleGoalModeCommand(rest?: string, input?: Pick<SubmittedUserInput, "images" | "imageLinks">): Promise<boolean>;
273
+ handleGuidedGoalCommand(rest?: string, input?: Pick<SubmittedUserInput, "images" | "imageLinks">): Promise<boolean>;
271
274
  /** Manually (re-)open the plan-review overlay — bound to `/plan-review`. Lets
272
275
  * the operator pull the review back up after dismissing it, or review a plan
273
276
  * the agent wrote without dispatching approval. There is no fixed plan filename:
@@ -291,6 +294,14 @@ export declare class InteractiveMode implements InteractiveModeContext {
291
294
  * flushed there. That is preferred over leaving it queued behind a command
292
295
  * the user runs during the pause, which mounts immediately and would put the
293
296
  * older panel out of order.
297
+ *
298
+ * The deferral is acknowledged in {@link deferredCommandContainer}, an
299
+ * anchored container above the editor. Nothing is mounted into the
300
+ * transcript: a mid-turn transcript mount re-renders rows below the growing
301
+ * live block and duplicates them in native scrollback (issues #4806/#6767),
302
+ * which is why the earlier `showStatus` acknowledgment was reverted. An
303
+ * anchored container is cleared and rebuilt in place, so it costs no
304
+ * scrollback rows — the same reason the ctrl+p role-cycle track lives there.
294
305
  */
295
306
  presentCommandOutput(content: Component | readonly Component[]): void;
296
307
  /** Mount every command panel queued for the current session while the agent was streaming. */
@@ -440,9 +451,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
440
451
  dispose?(): void;
441
452
  }) | Promise<Component & {
442
453
  dispose?(): void;
443
- }>, options?: {
444
- overlay?: boolean;
445
- }): Promise<T>;
454
+ }>, options?: ExtensionCustomOptions): Promise<T>;
446
455
  showExtensionError(extensionPath: string, error: string): void;
447
456
  showToolError(toolName: string, error: string): void;
448
457
  }
@@ -6,7 +6,7 @@ import type { CollabGuestLink } from "../collab/guest.js";
6
6
  import type { CollabHost } from "../collab/host.js";
7
7
  import type { KeybindingsManager } from "../config/keybindings.js";
8
8
  import type { Settings } from "../config/settings.js";
9
- import type { AutocompleteProviderFactory, ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectItem, ExtensionWidgetContent, ExtensionWidgetOptions } from "../extensibility/extensions/index.js";
9
+ import type { AutocompleteProviderFactory, ExtensionCustomOptions, ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectItem, ExtensionWidgetContent, ExtensionWidgetOptions } from "../extensibility/extensions/index.js";
10
10
  import type { CompactOptions } from "../extensibility/extensions/types.js";
11
11
  import type { Skill } from "../extensibility/skills.js";
12
12
  import type { MCPManager } from "../mcp/index.js";
@@ -96,6 +96,7 @@ export interface InteractiveModeContext {
96
96
  omfgContainer: Container;
97
97
  errorBannerContainer: Container;
98
98
  modelCycleContainer: Container;
99
+ deferredCommandContainer: Container;
99
100
  editor: CustomEditor;
100
101
  editorContainer: Container;
101
102
  hookWidgetContainerAbove: Container;
@@ -401,12 +402,10 @@ export interface InteractiveModeContext {
401
402
  toggleToolOutputExpansion(): void;
402
403
  setToolsExpanded(expanded: boolean): void;
403
404
  toggleThinkingBlockVisibility(): void;
404
- openExternalEditor(): void;
405
- registerExtensionShortcuts(): void;
406
- handlePlanModeCommand(initialPrompt?: string): Promise<void>;
407
- handleVibeModeCommand(initialPrompt?: string): Promise<void>;
408
- handleGoalModeCommand(rest?: string): Promise<void>;
409
- handleGuidedGoalCommand(rest?: string): Promise<void>;
405
+ handlePlanModeCommand(initialPrompt?: string, input?: Pick<SubmittedUserInput, "images" | "imageLinks">): Promise<boolean>;
406
+ handleVibeModeCommand(initialPrompt?: string, input?: Pick<SubmittedUserInput, "images" | "imageLinks">): Promise<boolean>;
407
+ handleGoalModeCommand(rest?: string, input?: Pick<SubmittedUserInput, "images" | "imageLinks">): Promise<boolean>;
408
+ handleGuidedGoalCommand(rest?: string, input?: Pick<SubmittedUserInput, "images" | "imageLinks">): Promise<boolean>;
410
409
  handleLoopCommand(args?: string): Promise<string | undefined>;
411
410
  setLoopPrompt(prompt: string): void;
412
411
  disableLoopMode(): void;
@@ -437,9 +436,7 @@ export interface InteractiveModeContext {
437
436
  dispose?(): void;
438
437
  }) | Promise<Component & {
439
438
  dispose?(): void;
440
- }>, options?: {
441
- overlay?: boolean;
442
- }): Promise<T>;
439
+ }>, options?: ExtensionCustomOptions): Promise<T>;
443
440
  showExtensionError(extensionPath: string, error: string): void;
444
441
  showToolError(toolName: string, error: string): void;
445
442
  }
@@ -1,4 +1,4 @@
1
- import type { KeybindingsManager } from "../../config/keybindings.js";
1
+ import { type KeybindingsManager } from "../../config/keybindings.js";
2
2
  export interface HotkeysMarkdownBindings {
3
3
  keybindings: Pick<KeybindingsManager, "getDisplayString">;
4
4
  }
@@ -148,6 +148,8 @@ export interface AgentSessionConfig {
148
148
  createVibeTools?: () => AgentTool[];
149
149
  /** Names whose current registry entry is the built-in implementation. */
150
150
  builtInToolNames?: Iterable<string>;
151
+ /** MCP names whose initial registry entries came from the manager snapshot. */
152
+ mcpManagerToolNames?: Iterable<string>;
151
153
  /** Updates tool-session predicates from the live active tool set. */
152
154
  setActiveToolNames?: (names: Iterable<string>) => void;
153
155
  /** Registers the write transport when runtime xdev mounts first need it. */
@@ -53,6 +53,7 @@ import type { AgentSessionEventListener } from "./agent-session-events.js";
53
53
  import type { AgentSessionConfig, AgentSessionDisposeOptions, AsyncJobSnapshot, CommandMetadataChangedListener, ContextUsageBreakdown, FollowUpOptions, FreshSessionResult, HandoffResult, ModelCycleResult, Prewalk, PromptOptions, ResetSessionContextResult, ResolvedRoleModel, RestoredQueuedMessage, RoleModelCycle, RoleModelCycleResult, SessionHandoffOptions, SessionOAuthAccountList, SessionStats, UsageFallbackConfirmer } from "./agent-session-types.js";
54
54
  import type { ClientBridge } from "./client-bridge.js";
55
55
  import { type CustomMessage, type CustomMessagePayload } from "./messages.js";
56
+ import type { ServingModel } from "./retry-fallback-chains.js";
56
57
  import { type AdvisorStats } from "./session-advisors.js";
57
58
  import type { BuildSessionContextOptions, SessionContext } from "./session-context.js";
58
59
  import type { BranchSummaryEntry, NewSessionOptions } from "./session-entries.js";
@@ -225,8 +226,12 @@ export declare class AgentSession {
225
226
  get state(): AgentState;
226
227
  /** Current model (may be undefined if not yet selected) */
227
228
  get model(): Model | undefined;
228
- /** Resolved selector while retry routing is using a fallback model. */
229
- get retryFallbackModel(): string | undefined;
229
+ /**
230
+ * Model this session's produced work is attributed to. Holds the last model
231
+ * that actually served while a fallback is armed but unproven, so observers
232
+ * never credit a run to a candidate that produced nothing.
233
+ */
234
+ get servingModel(): ServingModel | undefined;
230
235
  /** Install the interactive decision surface for reserve-triggered model changes. */
231
236
  setUsageFallbackConfirmer(confirmer: UsageFallbackConfirmer | undefined): void;
232
237
  /** Effective thinking level applied to the agent (the resolved level when `auto`). */
@@ -283,6 +288,20 @@ export declare class AgentSession {
283
288
  getToolByName(name: string): AgentTool | undefined;
284
289
  /** Whether a registry entry came from a built-in factory. */
285
290
  hasBuiltInTool(name: string): boolean;
291
+ /** Updates source provenance when a live registry entry is replaced or restored. */
292
+ setToolBuiltIn(name: string, builtIn: boolean): void;
293
+ /** Whether the live registry entry is owned by the RPC host. */
294
+ hasRpcHostTool(name: string): boolean;
295
+ /** Whether the current MCP entry came from the manager snapshot. */
296
+ hasMCPManagerTool(name: string): boolean;
297
+ /** Restores manager ownership after a lifecycle registration rollback. */
298
+ setMCPManagerTool(name: string, managerOwned: boolean): void;
299
+ /** Current extension-owned MCP entry retained across manager refreshes. */
300
+ getExtensionMCPTool(name: string): AgentTool | undefined;
301
+ /** Updates extension MCP ownership after a lifecycle registration commit or rollback. */
302
+ setExtensionMCPTool(name: string, tool: AgentTool | undefined): void;
303
+ /** Runs a registry/presentation mutation in this session's shared queue. */
304
+ runToolRegistryMutation<T>(mutation: () => Promise<T>, signal?: AbortSignal): Promise<T>;
286
305
  /** Names of every registered tool. */
287
306
  getAllToolNames(): string[];
288
307
  /** Full metadata for every registered tool, including source provenance (backs `getAllTools()`). */
@@ -300,7 +319,7 @@ export declare class AgentSession {
300
319
  /** Selects enabled tools, ignoring names absent from the registry. */
301
320
  setActiveToolsByName(toolNames: string[]): Promise<void>;
302
321
  /** Restores an exact top-level versus `xd://` tool partition. */
303
- setActiveToolPresentation(toolNames: string[], mountedToolNames: string[]): Promise<void>;
322
+ setActiveToolPresentation(toolNames: string[], mountedToolNames: string[], forcePromptRefresh?: boolean, signal?: AbortSignal): Promise<void>;
304
323
  /**
305
324
  * Session-scoped enable/disable for the settings-gated `computer` tool.
306
325
  *
@@ -120,6 +120,26 @@ export declare function shouldRenderAbortReason(message: Pick<AssistantMessage,
120
120
  * the error is surfaced live (pinned) instead. A turn that streamed partial text,
121
121
  * reasoning, or tool calls is NOT empty and stays in history. */
122
122
  export declare function isEmptyErrorTurn(message: Pick<AssistantMessage, "stopReason" | "content">): boolean;
123
+ /** A `stop`/`toolUse` turn that produced nothing actionable. Any other stop
124
+ * reason is not an "empty stop": an `error`/`aborted` turn is a failure rather
125
+ * than an empty completion, and a `length` stop was cut off mid-output. */
126
+ export declare function isEmptyAssistantStop(message: Pick<AssistantMessage, "stopReason" | "content">): boolean;
127
+ /**
128
+ * True when this assistant turn actually produced output, making its model the
129
+ * one that served the run.
130
+ *
131
+ * Attribution asks this from two places that MUST reach the same verdict: the
132
+ * live session, which flips a fallback to "served", and the offline walk
133
+ * replaying a transcript. `error` and `aborted` are both failures — a stalled or
134
+ * dropped stream is finalized as `aborted` with its partial block still
135
+ * attached, so a stop reason alone is not proof.
136
+ *
137
+ * Actionable content is required on top of the empty-stop rule, which only
138
+ * inspects `stop`/`toolUse`. A `length` stop burns the whole output budget
139
+ * without necessarily emitting anything usable, and every other stop reason
140
+ * bypasses that rule entirely.
141
+ */
142
+ export declare function assistantTurnProducedOutput(message: Pick<AssistantMessage, "stopReason" | "content">): boolean;
123
143
  /** Sentinel `errorMessage` the agent stamps on any abort that carried no custom
124
144
  * reason (bare `abort()`). Renderers treat it as "no specific reason given". */
125
145
  export declare const GENERIC_ABORT_SENTINEL = "Request was aborted";