@oh-my-pi/pi-coding-agent 17.0.7 → 17.0.8

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 (82) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/dist/cli.js +4967 -4948
  3. package/dist/types/config/model-registry.d.ts +1 -1
  4. package/dist/types/config/model-resolver.d.ts +5 -1
  5. package/dist/types/config/settings-schema.d.ts +16 -0
  6. package/dist/types/dap/client.d.ts +19 -0
  7. package/dist/types/extensibility/extensions/runner.d.ts +4 -2
  8. package/dist/types/extensibility/extensions/types.d.ts +3 -0
  9. package/dist/types/extensibility/typebox.d.ts +4 -0
  10. package/dist/types/hindsight/client.d.ts +10 -0
  11. package/dist/types/hindsight/config.d.ts +8 -0
  12. package/dist/types/mcp/tool-bridge.d.ts +9 -0
  13. package/dist/types/mnemopi/state.d.ts +2 -0
  14. package/dist/types/modes/components/user-message.d.ts +25 -1
  15. package/dist/types/modes/controllers/extension-ui-controller.d.ts +8 -0
  16. package/dist/types/modes/interactive-mode.d.ts +7 -1
  17. package/dist/types/modes/types.d.ts +14 -1
  18. package/dist/types/modes/utils/ui-helpers.d.ts +12 -8
  19. package/dist/types/sdk.d.ts +1 -0
  20. package/dist/types/session/agent-session-error-log.test.d.ts +1 -0
  21. package/dist/types/session/agent-session.d.ts +78 -2
  22. package/dist/types/task/executor.d.ts +10 -0
  23. package/dist/types/task/index.d.ts +1 -0
  24. package/dist/types/task/prewalk.d.ts +3 -0
  25. package/dist/types/tools/ask.d.ts +10 -0
  26. package/dist/types/tools/tool-timeouts.d.ts +8 -2
  27. package/dist/types/utils/changelog.d.ts +4 -6
  28. package/package.json +12 -13
  29. package/src/cli/models-cli.ts +37 -17
  30. package/src/cli/update-cli.ts +14 -1
  31. package/src/config/model-registry.ts +47 -24
  32. package/src/config/model-resolver.ts +56 -31
  33. package/src/config/settings-schema.ts +5 -0
  34. package/src/config/settings.ts +68 -5
  35. package/src/dap/client.ts +86 -7
  36. package/src/edit/diff.ts +4 -4
  37. package/src/eval/py/prelude.py +5 -5
  38. package/src/export/html/template.js +23 -9
  39. package/src/extensibility/extensions/runner.ts +9 -4
  40. package/src/extensibility/extensions/types.ts +3 -0
  41. package/src/extensibility/typebox.ts +22 -9
  42. package/src/hindsight/client.test.ts +42 -0
  43. package/src/hindsight/client.ts +40 -3
  44. package/src/hindsight/config.ts +18 -0
  45. package/src/launch/broker.ts +31 -5
  46. package/src/lsp/index.ts +1 -1
  47. package/src/main.ts +15 -5
  48. package/src/mcp/tool-bridge.ts +9 -0
  49. package/src/mnemopi/state.ts +70 -3
  50. package/src/modes/components/agent-dashboard.ts +6 -2
  51. package/src/modes/components/chat-transcript-builder.ts +13 -2
  52. package/src/modes/components/diff.ts +2 -2
  53. package/src/modes/components/plan-review-overlay.ts +20 -4
  54. package/src/modes/components/user-message.ts +100 -1
  55. package/src/modes/controllers/command-controller.ts +22 -5
  56. package/src/modes/controllers/event-controller.ts +4 -1
  57. package/src/modes/controllers/extension-ui-controller.ts +18 -0
  58. package/src/modes/controllers/input-controller.ts +47 -12
  59. package/src/modes/controllers/selector-controller.ts +82 -6
  60. package/src/modes/interactive-mode.ts +36 -4
  61. package/src/modes/types.ts +16 -3
  62. package/src/modes/utils/ui-helpers.ts +40 -20
  63. package/src/prompts/system/workflow-notice.md +1 -1
  64. package/src/sdk.ts +134 -48
  65. package/src/session/agent-session-error-log.test.ts +59 -0
  66. package/src/session/agent-session.ts +300 -26
  67. package/src/system-prompt.ts +1 -2
  68. package/src/task/executor.ts +41 -27
  69. package/src/task/index.ts +11 -0
  70. package/src/task/prewalk.ts +6 -0
  71. package/src/task/worktree.ts +25 -11
  72. package/src/tools/ask.ts +15 -0
  73. package/src/tools/bash.ts +14 -6
  74. package/src/tools/browser.ts +1 -1
  75. package/src/tools/debug.ts +1 -1
  76. package/src/tools/eval.ts +4 -5
  77. package/src/tools/fetch.ts +1 -1
  78. package/src/tools/hub/launch.ts +7 -0
  79. package/src/tools/tool-timeouts.ts +10 -3
  80. package/src/tools/write.ts +31 -0
  81. package/src/utils/changelog.ts +54 -58
  82. package/src/utils/git.ts +57 -49
@@ -44,7 +44,7 @@ interface ProviderOverride {
44
44
  * See `xiaomi-tp-discovery-merge.test.ts` and the `refresh()` baseUrl-override
45
45
  * regression in `model-registry.test.ts`.
46
46
  */
47
- export declare function mergeDiscoveredModel<TApi extends Api>(model: Model<TApi>, existing: Model<Api> | undefined, providerOverride?: Pick<ProviderOverride, "baseUrl" | "headers" | "remoteCompaction" | "transport">): Model<TApi>;
47
+ export declare function mergeDiscoveredModel<TApi extends Api>(model: Model<TApi>, existing: Model<Api> | undefined, providerOverride?: Pick<ProviderOverride, "baseUrl" | "compat" | "headers" | "remoteCompaction" | "transport">): Model<TApi>;
48
48
  export type ProviderDiscoveryStatus = "idle" | "ok" | "empty" | "cached" | "unavailable" | "unauthenticated";
49
49
  export interface ProviderDiscoveryState {
50
50
  provider: string;
@@ -265,8 +265,10 @@ export declare function resolveAllowedModels(modelRegistry: Pick<ModelRegistry,
265
265
  export declare function filterAvailableModelsByEnabledPatterns(available: Model<Api>[], patterns: readonly string[], settings?: Settings): Model<Api>[];
266
266
  export interface ResolveCliModelResult {
267
267
  model: Model<Api> | undefined;
268
- /** configuredPatterns is the full configured fallback chain when the selector resolves through a role. */
268
+ /** configuredPatterns contains the role's ordered primary candidates. */
269
269
  configuredPatterns?: string[];
270
+ /** configuredRole identifies the role expanded into configuredPatterns. */
271
+ configuredRole?: string;
270
272
  /** configuredPatternIndex identifies the configured role pattern that matched an available model. */
271
273
  configuredPatternIndex?: number;
272
274
  selector?: string;
@@ -283,6 +285,8 @@ export declare function resolveCliModel(options: {
283
285
  cliProvider?: string;
284
286
  cliModel?: string;
285
287
  modelRegistry: CliModelRegistry;
288
+ /** Authenticated models to prefer for unqualified selectors; omit to preserve catalog-order behavior. */
289
+ availableModels?: Model<Api>[];
286
290
  settings?: Settings;
287
291
  preferences?: ModelMatchPreferences;
288
292
  }): ResolveCliModelResult;
@@ -2968,6 +2968,22 @@ export declare const SETTINGS_SCHEMA: {
2968
2968
  readonly type: "boolean";
2969
2969
  readonly default: false;
2970
2970
  };
2971
+ readonly "hindsight.requestTimeoutMs": {
2972
+ readonly type: "number";
2973
+ readonly default: 30000;
2974
+ };
2975
+ readonly "hindsight.reflectTimeoutMs": {
2976
+ readonly type: "number";
2977
+ readonly default: 120000;
2978
+ };
2979
+ readonly "hindsight.recallTimeoutMs": {
2980
+ readonly type: "number";
2981
+ readonly default: 30000;
2982
+ };
2983
+ readonly "hindsight.retainTimeoutMs": {
2984
+ readonly type: "number";
2985
+ readonly default: 60000;
2986
+ };
2971
2987
  readonly "hindsight.mentalModelsEnabled": {
2972
2988
  readonly type: "boolean";
2973
2989
  readonly default: true;
@@ -53,4 +53,23 @@ export declare class DapClient {
53
53
  sendResponse(request: DapRequestMessage, success: boolean, body?: unknown, message?: string): Promise<void>;
54
54
  dispose(): Promise<void>;
55
55
  }
56
+ /**
57
+ * Give the adapter a chance to announce it is listening on `port` before the
58
+ * first connect. vscode-js-debug prints `Debug server listening at HOST:PORT`
59
+ * to stdout from inside its `listen()` callback; waiting for the port to appear
60
+ * there means we only connect once the child genuinely owns the reserved port,
61
+ * which closes the WSL2-mirrored ghost-accept window (issue #6055) at its root.
62
+ *
63
+ * Best-effort: resolves on the banner, on process exit, or on timeout — the
64
+ * subsequent connect loop and `proc.exitCode` checks surface real failures, so
65
+ * an adapter that never prints a banner still proceeds (just without the gate).
66
+ * Also drains stdout for the wait's duration: in tcp mode the DAP protocol
67
+ * flows over the socket, so nothing else consumes the adapter's stdout.
68
+ *
69
+ * Exported so tests can drive the gate deterministically with a synthetic stdout.
70
+ */
71
+ export declare function waitForTcpServerListening(proc: {
72
+ stdout: ReadableStream<Uint8Array>;
73
+ exitCode: number | null;
74
+ }, port: number, timeoutMs: number): Promise<void>;
56
75
  export {};
@@ -54,8 +54,10 @@ export type SwitchSessionHandler = (sessionPath: string) => Promise<{
54
54
  }>;
55
55
  export type ShutdownHandler = () => void;
56
56
  /**
57
- * Helper function to emit session_shutdown event to extensions.
58
- * Returns true if the event was emitted, false if there were no handlers.
57
+ * Emit `session_shutdown` and clear timers owned by an extension runner.
58
+ *
59
+ * Returns whether any shutdown handlers were present. Timer cleanup runs even
60
+ * when a handler fails so extension background work cannot outlive its host.
59
61
  */
60
62
  export declare function emitSessionShutdownEvent(extensionRunner: ExtensionRunner | undefined): Promise<boolean>;
61
63
  export declare class ExtensionRunner {
@@ -398,6 +398,9 @@ export interface ToolDefinition<TParams extends TSchema = TSchema, TDetails = un
398
398
  /** Tool approval tier. Defaults to `"exec"` when omitted.
399
399
  * `"read"`: read-only operations. `"write"`: mutations. `"exec"`: code execution. */
400
400
  approval?: ToolApproval;
401
+ /** Structured-output strict grammar opt-in/out. `false` is meaningful: OpenAI-family
402
+ * serializers preserve an explicit `strict: false` on the wire (#4336/#4340). */
403
+ strict?: boolean;
401
404
  /** MCP server name for discovery/search metadata when this tool fronts an MCP server. */
402
405
  mcpServerName?: string;
403
406
  /** Original MCP tool name for discovery/search metadata. */
@@ -33,6 +33,8 @@ export type TOptional<_E extends ArkSchema> = ArkSchema;
33
33
  export type TUnion<_T extends readonly ArkSchema[] = readonly ArkSchema[]> = ArkSchema;
34
34
  export type TEnum<_T extends readonly (string | number)[] = readonly (string | number)[]> = ArkSchema;
35
35
  export type TRecord<_K extends ArkSchema, _V extends ArkSchema> = ArkSchema;
36
+ /** TypeBox-compatible wrapper for raw JSON Schema documents. */
37
+ export type TUnsafe<_T = unknown> = ArkSchema;
36
38
  declare const VALIDATION_FAILURE: unique symbol;
37
39
  interface ValidationFailure {
38
40
  message: string;
@@ -126,6 +128,7 @@ export declare const Type: {
126
128
  readonly Null: typeof tNull;
127
129
  readonly Any: typeof tAny;
128
130
  readonly Unknown: typeof tUnknown;
131
+ readonly Unsafe: <T = unknown>(jsonSchema?: Record<string, unknown>) => TUnsafe<T>;
129
132
  readonly Never: typeof tNever;
130
133
  readonly Literal: typeof tLiteral;
131
134
  readonly Union: typeof tUnion;
@@ -155,6 +158,7 @@ declare const _default: {
155
158
  readonly Null: typeof tNull;
156
159
  readonly Any: typeof tAny;
157
160
  readonly Unknown: typeof tUnknown;
161
+ readonly Unsafe: <T = unknown>(jsonSchema?: Record<string, unknown>) => TUnsafe<T>;
158
162
  readonly Never: typeof tNever;
159
163
  readonly Literal: typeof tLiteral;
160
164
  readonly Union: typeof tUnion;
@@ -12,10 +12,20 @@ export type Budget = "low" | "mid" | "high" | string;
12
12
  export type TagsMatch = "any" | "all" | "any_strict" | "all_strict";
13
13
  export type UpdateMode = "replace" | "append";
14
14
  export type ConsolidationState = "failed" | "pending" | "done";
15
+ /** Per-operation request deadlines (ms). Each falls back to a built-in default. */
16
+ export interface HindsightTimeouts {
17
+ /** Default deadline for ops without a specific override. */
18
+ request?: number;
19
+ reflect?: number;
20
+ recall?: number;
21
+ retain?: number;
22
+ }
15
23
  export interface HindsightApiOptions {
16
24
  baseUrl: string;
17
25
  apiKey?: string;
18
26
  userAgent?: string;
27
+ /** Per-op deadlines; unset entries fall back to built-in defaults. */
28
+ timeouts?: HindsightTimeouts;
19
29
  }
20
30
  /** Caller cancellation shared by Hindsight request option bags. */
21
31
  export interface HindsightRequestOptions {
@@ -32,6 +32,14 @@ export interface HindsightConfig {
32
32
  recallMaxQueryChars: number;
33
33
  recallPromptPreamble: string;
34
34
  debug: boolean;
35
+ /** Default per-request client deadline (ms) for ops without a specific override. */
36
+ requestTimeoutMs: number;
37
+ /** Client deadline (ms) for reflect (agentic synthesis; costlier than a metadata fetch). */
38
+ reflectTimeoutMs: number;
39
+ /** Client deadline (ms) for recall. */
40
+ recallTimeoutMs: number;
41
+ /** Client deadline (ms) for retain / retainBatch. */
42
+ retainTimeoutMs: number;
35
43
  mentalModelsEnabled: boolean;
36
44
  mentalModelAutoSeed: boolean;
37
45
  mentalModelRefreshIntervalMs: number;
@@ -59,6 +59,13 @@ export declare class MCPTool implements CustomTool<TSchema, MCPToolDetails> {
59
59
  readonly approval: "write";
60
60
  /** Render completed MCP calls with the result header replacing the pending call header. */
61
61
  readonly mergeCallAndResult = true;
62
+ /**
63
+ * MCP-backed tools opt out of strict structured-output grammar. The server
64
+ * owns validation, and strict mode makes OpenAI-family models over-fill
65
+ * mutually exclusive optional fields (#4336/#4340). Serializers preserve an
66
+ * explicit `false`; an omitted flag would leave nothing to preserve.
67
+ */
68
+ readonly strict: false;
62
69
  /** Create MCPTool instances for all tools from an MCP server connection */
63
70
  static fromTools(connection: MCPServerConnection, tools: MCPToolDefinition[], reconnect?: MCPReconnect): MCPTool[];
64
71
  constructor(connection: MCPServerConnection, tool: MCPToolDefinition, reconnect?: MCPReconnect | undefined);
@@ -86,6 +93,8 @@ export declare class DeferredMCPTool implements CustomTool<TSchema, MCPToolDetai
86
93
  readonly approval: "write";
87
94
  /** Render completed MCP calls with the result header replacing the pending call header. */
88
95
  readonly mergeCallAndResult = true;
96
+ /** See {@link MCPTool.strict}: MCP servers own validation, so stay non-strict. */
97
+ readonly strict: false;
89
98
  /** Create DeferredMCPTool instances for all tools from an MCP server */
90
99
  static fromTools(serverName: string, tools: MCPToolDefinition[], getConnection: () => Promise<MCPServerConnection>, source?: SourceMeta, reconnect?: MCPReconnect): DeferredMCPTool[];
91
100
  constructor(serverName: string, tool: MCPToolDefinition, getConnection: () => Promise<MCPServerConnection>, source?: SourceMeta, reconnect?: MCPReconnect | undefined);
@@ -74,6 +74,7 @@ export interface MnemopiSessionStateOptions {
74
74
  hasRecalledForFirstTurn?: boolean;
75
75
  }
76
76
  export declare class MnemopiSessionState {
77
+ #private;
77
78
  sessionId: string;
78
79
  readonly config: MnemopiBackendConfig;
79
80
  readonly session: AgentSession;
@@ -124,6 +125,7 @@ export declare class MnemopiSessionState {
124
125
  content: string;
125
126
  }>, sourceId: string, options?: {
126
127
  extract?: boolean;
128
+ retainedThroughUserTurn?: number;
127
129
  }): Promise<void>;
128
130
  attachSessionListeners(): void;
129
131
  maybeRecallOnAgentStart(): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { Container } from "@oh-my-pi/pi-tui";
1
+ import { type Component, Container } from "@oh-my-pi/pi-tui";
2
2
  /**
3
3
  * Component that renders a user message
4
4
  */
@@ -7,3 +7,27 @@ export declare class UserMessageComponent extends Container {
7
7
  constructor(text: string, synthetic?: boolean, imageLinks?: readonly (string | undefined)[]);
8
8
  render(width: number): readonly string[];
9
9
  }
10
+ /**
11
+ * Collapsed placeholder for a synthetic (agent-attributed) user input in the
12
+ * file/remote-backed transcript viewer — chiefly the advisor's `Session update`
13
+ * replay dumps, which can each be hundreds of KiB of Markdown and, on cold open,
14
+ * blocked the TUI for tens of seconds while every historical body was laid out
15
+ * before the viewport clip (issue #6308).
16
+ *
17
+ * Collapsed by default: renders one dim summary row (label · size · line count ·
18
+ * expand hint) and builds NO Markdown. The heavy {@link UserMessageComponent} is
19
+ * constructed lazily only when expanded via `ctrl+o`, so blocks above the
20
+ * viewport never pay layout cost until the reader asks to see them. The raw
21
+ * observability data stays intact in `__advisor.jsonl`.
22
+ */
23
+ export declare class CollapsedSyntheticMessageComponent implements Component {
24
+ #private;
25
+ private readonly text;
26
+ private readonly imageLinks?;
27
+ constructor(text: string, imageLinks?: readonly (string | undefined)[] | undefined);
28
+ /** ctrl+o toggle: reveal/hide the full Markdown body. */
29
+ setExpanded(expanded: boolean): void;
30
+ invalidate(): void;
31
+ dispose(): void;
32
+ render(width: number): readonly string[];
33
+ }
@@ -12,6 +12,14 @@ export declare class ExtensionUiController {
12
12
  * Initialize the hook system with TUI-based UI context.
13
13
  */
14
14
  initHooksAndCustomTools(): Promise<void>;
15
+ /**
16
+ * The `ExtensionUIContext` built in `initHooksAndCustomTools()` — the same
17
+ * picker/dialog primitives passed as `context.ui` for every live tool
18
+ * call. `/tree` `ask` re-answer (issue #5642) reuses this to drive a
19
+ * standalone `AskTool.execute()` call outside a normal agent turn.
20
+ * `undefined` before hooks have initialized.
21
+ */
22
+ getToolUIContext(): ExtensionUIContext | undefined;
15
23
  setHookWidget(key: string, content: ExtensionWidgetContent, options?: ExtensionWidgetOptions): void;
16
24
  initializeHookRunner(uiContext: ExtensionUIContext, _hasUI: boolean): void;
17
25
  /**
@@ -128,6 +128,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
128
128
  proseOnlyThinking: boolean;
129
129
  compactionQueuedMessages: CompactionQueuedMessage[];
130
130
  pendingTools: Map<string, ToolExecutionHandle>;
131
+ transcriptMessageComponents: WeakMap<AgentMessage, Component>;
131
132
  pendingBashComponents: BashExecutionComponent[];
132
133
  bashComponent: BashExecutionComponent | undefined;
133
134
  pendingPythonComponents: EvalExecutionComponent[];
@@ -228,7 +229,9 @@ export declare class InteractiveMode implements InteractiveModeContext {
228
229
  syncRunningSubagentBadge(options?: {
229
230
  requestRender?: boolean;
230
231
  }): void;
231
- rebuildChatFromMessages(): void;
232
+ rebuildChatFromMessages(options?: {
233
+ reuseSettledComponents?: boolean;
234
+ }): void;
232
235
  /**
233
236
  * Render the ctrl+p model-role cycle chip track into its own anchored
234
237
  * container (just above the editor), mirroring the todo HUD: the container is
@@ -301,10 +304,12 @@ export declare class InteractiveMode implements InteractiveModeContext {
301
304
  addMessageToChat(message: AgentMessage, options?: {
302
305
  populateHistory?: boolean;
303
306
  imageLinks?: readonly (string | undefined)[];
307
+ reuseSettledComponent?: boolean;
304
308
  }): Component[];
305
309
  renderSessionContext(sessionContext: SessionContext, options?: {
306
310
  updateFooter?: boolean;
307
311
  populateHistory?: boolean;
312
+ reuseSettledComponents?: boolean;
308
313
  }): void;
309
314
  renderInitialMessages(options?: {
310
315
  preserveExistingChat?: boolean;
@@ -399,6 +404,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
399
404
  openExternalEditor(): void;
400
405
  registerExtensionShortcuts(): void;
401
406
  initHooksAndCustomTools(): Promise<void>;
407
+ getToolUIContext(): ExtensionUIContext | undefined;
402
408
  emitCustomToolSessionEvent(reason: "start" | "switch" | "branch" | "tree" | "shutdown", previousSessionFile?: string): Promise<void>;
403
409
  setHookWidget(key: string, content: ExtensionWidgetContent, options?: ExtensionWidgetOptions): void;
404
410
  setHookStatus(key: string, text: string | undefined): void;
@@ -156,6 +156,8 @@ export interface InteractiveModeContext {
156
156
  noteDisplayableThinkingContent(message: AgentMessage): boolean;
157
157
  proseOnlyThinking: boolean;
158
158
  compactionQueuedMessages: CompactionQueuedMessage[];
159
+ /** Settled user/assistant components reusable across post-compaction transcript rebuilds. */
160
+ transcriptMessageComponents: WeakMap<AgentMessage, Component>;
159
161
  pendingTools: Map<string, ToolExecutionHandle>;
160
162
  pendingBashComponents: BashExecutionComponent[];
161
163
  bashComponent: BashExecutionComponent | undefined;
@@ -280,10 +282,12 @@ export interface InteractiveModeContext {
280
282
  addMessageToChat(message: AgentMessage, options?: {
281
283
  populateHistory?: boolean;
282
284
  imageLinks?: readonly (string | undefined)[];
285
+ reuseSettledComponent?: boolean;
283
286
  }): Component[];
284
287
  renderSessionContext(sessionContext: SessionContext, options?: {
285
288
  updateFooter?: boolean;
286
289
  populateHistory?: boolean;
290
+ reuseSettledComponents?: boolean;
287
291
  }): void;
288
292
  renderInitialMessages(options?: {
289
293
  preserveExistingChat?: boolean;
@@ -295,7 +299,9 @@ export interface InteractiveModeContext {
295
299
  /** Refresh the running-subagents status badge from the active local or collab registry. */
296
300
  syncRunningSubagentBadge(): void;
297
301
  updateEditorBorderColor(): void;
298
- rebuildChatFromMessages(): void;
302
+ rebuildChatFromMessages(options?: {
303
+ reuseSettledComponents?: boolean;
304
+ }): void;
299
305
  setTodos(todos: TodoItem[] | TodoPhase[]): void;
300
306
  reloadTodos(): Promise<void>;
301
307
  toggleTodoExpansion(): void;
@@ -396,6 +402,13 @@ export interface InteractiveModeContext {
396
402
  handlePlanApproval(details: PlanApprovalDetails): Promise<void>;
397
403
  openPlanReview(): Promise<void>;
398
404
  initHooksAndCustomTools(): Promise<void>;
405
+ /**
406
+ * The live `ExtensionUIContext` (picker/dialog primitives) used for tool
407
+ * execution, `undefined` before hooks have initialized. `/tree` `ask`
408
+ * re-answer (issue #5642) reuses it to drive a standalone
409
+ * `AskTool.execute()` call.
410
+ */
411
+ getToolUIContext(): ExtensionUIContext | undefined;
399
412
  emitCustomToolSessionEvent(reason: "start" | "switch" | "branch" | "tree" | "shutdown", previousSessionFile?: string): Promise<void>;
400
413
  setHookWidget(key: string, content: ExtensionWidgetContent, options?: ExtensionWidgetOptions): void;
401
414
  setHookStatus(key: string, text: string | undefined): void;
@@ -7,6 +7,16 @@ interface RenderInitialMessagesOptions {
7
7
  preserveExistingChat?: boolean;
8
8
  clearTerminalHistory?: boolean;
9
9
  }
10
+ type AddMessageOptions = {
11
+ populateHistory?: boolean;
12
+ imageLinks?: readonly (string | undefined)[];
13
+ reuseSettledComponent?: boolean;
14
+ };
15
+ type RenderSessionContextOptions = {
16
+ updateFooter?: boolean;
17
+ populateHistory?: boolean;
18
+ reuseSettledComponents?: boolean;
19
+ };
10
20
  export declare class UiHelpers {
11
21
  #private;
12
22
  private ctx;
@@ -22,20 +32,14 @@ export declare class UiHelpers {
22
32
  showStatus(message: string, options?: {
23
33
  dim?: boolean;
24
34
  }): void;
25
- addMessageToChat(message: AgentMessage, options?: {
26
- populateHistory?: boolean;
27
- imageLinks?: readonly (string | undefined)[];
28
- }): Component[];
35
+ addMessageToChat(message: AgentMessage, options?: AddMessageOptions): Component[];
29
36
  /**
30
37
  * Render session context to chat. Used for initial load and rebuild after compaction.
31
38
  * @param sessionContext Session context to render
32
39
  * @param options.updateFooter Update footer state
33
40
  * @param options.populateHistory Add user messages to editor history
34
41
  */
35
- renderSessionContext(sessionContext: SessionContext, options?: {
36
- updateFooter?: boolean;
37
- populateHistory?: boolean;
38
- }): void;
42
+ renderSessionContext(sessionContext: SessionContext, options?: RenderSessionContextOptions): void;
39
43
  renderInitialMessages(options?: RenderInitialMessagesOptions): void;
40
44
  clearEditor(): void;
41
45
  showError(errorMessage: string): void;
@@ -359,6 +359,7 @@ export interface BuildSystemPromptOptions {
359
359
  * as separate entries so providers can cache prompt prefixes without concatenating blocks.
360
360
  */
361
361
  export declare function buildSystemPrompt(options?: BuildSystemPromptOptions): Promise<BuildSystemPromptResult>;
362
+ export declare function customToolToDefinition(tool: CustomTool): ToolDefinition;
362
363
  /** Dependencies used to construct an isolated auto-learn capture agent. */
363
364
  export interface AutoLearnCaptureRunnerOptions {
364
365
  sourceAgent: Agent;
@@ -13,7 +13,7 @@
13
13
  * Modes use this class and add their own I/O layer on top.
14
14
  */
15
15
  import type { InMemorySnapshotStore } from "@oh-my-pi/hashline";
16
- import { Agent, type AgentEvent, type AgentMessage, type AgentState, type AgentTool, type AgentToolResult, type StreamFn, ThinkingLevel, type ToolChoiceDirective } from "@oh-my-pi/pi-agent-core";
16
+ import { Agent, type AgentEvent, type AgentMessage, type AgentState, type AgentTool, type AgentToolContext, type AgentToolResult, type StreamFn, ThinkingLevel, type ToolChoiceDirective } from "@oh-my-pi/pi-agent-core";
17
17
  import { type CompactionResult, type ShakeConfig } from "@oh-my-pi/pi-agent-core/compaction";
18
18
  import type { AssistantMessage, Context, ImageContent, Message, MessageAttribution, Model, ProviderSessionState, ResetCreditAccountStatus, ResetCreditRedeemOutcome, ResetCreditTarget, ServiceTier, ServiceTierByFamily, ServiceTierFamily, SimpleStreamOptions, TextContent, ToolChoice, UsageReport } from "@oh-my-pi/pi-ai";
19
19
  import { Effort } from "@oh-my-pi/pi-ai";
@@ -31,7 +31,7 @@ import { type BashResult } from "../exec/bash-executor.js";
31
31
  import type { TtsrManager } from "../export/ttsr.js";
32
32
  import type { LoadedCustomCommand } from "../extensibility/custom-commands/index.js";
33
33
  import type { CustomTool } from "../extensibility/custom-tools/types.js";
34
- import type { ExtensionRunner } from "../extensibility/extensions/index.js";
34
+ import type { ExtensionRunner, ExtensionUIContext } from "../extensibility/extensions/index.js";
35
35
  import type { CompactOptions, ContextUsage } from "../extensibility/extensions/types.js";
36
36
  import type { RecoveredRetryError } from "../extensibility/shared-events.js";
37
37
  import { type Skill, type SkillWarning } from "../extensibility/skills.js";
@@ -45,6 +45,7 @@ import { type PlanApprovalDetails } from "../plan-mode/approved-plan.js";
45
45
  import type { PlanModeState } from "../plan-mode/state.js";
46
46
  import { type SecretObfuscator } from "../secrets/obfuscator.js";
47
47
  import { type ConfiguredThinkingLevel } from "../thinking.js";
48
+ import { type AskToolDetails, type AskToolInput } from "../tools/ask.js";
48
49
  import type { CheckpointState, CompletedRewindState } from "../tools/checkpoint.js";
49
50
  import { type PlanProposalHandler } from "../tools/resolve.js";
50
51
  import { type TodoItem, type TodoPhase } from "../tools/todo.js";
@@ -180,6 +181,15 @@ export interface PlanYolo {
180
181
  target: Model;
181
182
  thinkingLevel?: ConfiguredThinkingLevel;
182
183
  }
184
+ /** Identifies a retry fallback chain already entered during startup model resolution. */
185
+ export interface InitialRetryFallbackState {
186
+ /** Role whose configured primary was unavailable. */
187
+ role: string;
188
+ /** Configured primary selector retained for restoration when it becomes available. */
189
+ originalSelector: string;
190
+ /** Thinking selector configured for the unavailable primary. */
191
+ originalThinkingLevel: ConfiguredThinkingLevel | undefined;
192
+ }
183
193
  export interface AgentSessionConfig {
184
194
  agent: Agent;
185
195
  sessionManager: SessionManager;
@@ -193,6 +203,8 @@ export interface AgentSessionConfig {
193
203
  }>;
194
204
  /** Initial session thinking selector. */
195
205
  thinkingLevel?: ConfiguredThinkingLevel;
206
+ /** Retry chain ownership when startup selected one of its fallback entries. */
207
+ initialRetryFallback?: InitialRetryFallbackState;
196
208
  /** Prewalk from the starting model to a fast/cheap target at the first edit/write once the todo list exists. */
197
209
  prewalk?: Prewalk;
198
210
  /** Force read-only plan mode at start, auto-approve on the model's first
@@ -520,6 +532,15 @@ export type RestoredQueuedMessage = {
520
532
  text: string;
521
533
  images?: ImageContent[];
522
534
  };
535
+ /**
536
+ * Emit a warn-level log for a turn that ended in a provider error so recurring
537
+ * stream failures are diagnosable from the main log alone. The `agent_end`
538
+ * routing trace is debug-only and omits the error fields; without this a
539
+ * session dying on provider errors leaves only `stopReason:"error"` debug lines
540
+ * and the real cause lives solely in the session transcript (issue #6177).
541
+ * No-op for any non-error stop reason.
542
+ */
543
+ export declare function logProviderTurnError(msg: AssistantMessage): void;
523
544
  type SessionNameTrigger = "replan";
524
545
  export declare class AgentSession {
525
546
  #private;
@@ -1215,6 +1236,16 @@ export declare class AgentSession {
1215
1236
  /**
1216
1237
  * Manually retry the last failed assistant turn.
1217
1238
  * Removes the error message from agent state and re-attempts with a fresh retry budget.
1239
+ *
1240
+ * A stream that stalls or aborts mid-tool-call ends the turn with
1241
+ * `stopReason: "error" | "aborted"` and then appends one synthetic
1242
+ * {@link isSyntheticToolResultMessage tool_result} per emitted tool call to
1243
+ * preserve the provider's tool_use/tool_result pairing (see
1244
+ * `createAbortedToolResult` in `agent-loop.ts`). Those placeholders trail the
1245
+ * failed assistant turn, so the retry lookback walks back over them before
1246
+ * checking the assistant message; it strips both the placeholders and the
1247
+ * failed turn before re-attempting.
1248
+ *
1218
1249
  * @returns true if retry was initiated, false if no failed turn to retry or agent is busy
1219
1250
  */
1220
1251
  retry(): Promise<boolean>;
@@ -1380,6 +1411,26 @@ export declare class AgentSession {
1380
1411
  navigateTree(targetId: string, options?: {
1381
1412
  summarize?: boolean;
1382
1413
  customInstructions?: string;
1414
+ /**
1415
+ * Opts into the two-phase `ask` toolResult re-answer protocol
1416
+ * (issue #5642): set only by the interactive `/tree` selector, which
1417
+ * knows how to re-open the picker on `reopenAsk` and complete the
1418
+ * navigation with `reanswerAskResult`. Every other public caller
1419
+ * (extensions, hooks, ACP, session-extension actions) leaves this
1420
+ * unset and gets the pre-#5642 plain leaf move onto `ask`
1421
+ * toolResults instead — they have no picker to re-open and would
1422
+ * otherwise report a successful no-op navigation (roboomp review on
1423
+ * #5895).
1424
+ */
1425
+ allowAskReopen?: boolean;
1426
+ /**
1427
+ * Completes an in-progress `ask` re-answer (issue #5642): the caller
1428
+ * already received `reopenAsk` from a prior call on the same
1429
+ * `targetId`, re-opened the picker, and is handing back the fresh
1430
+ * answer. Branches a new toolResult sibling instead of landing on
1431
+ * the original one.
1432
+ */
1433
+ reanswerAskResult?: AgentToolResult<AskToolDetails>;
1383
1434
  }): Promise<{
1384
1435
  editorText?: string;
1385
1436
  cancelled: boolean;
@@ -1387,7 +1438,32 @@ export declare class AgentSession {
1387
1438
  summaryEntry?: BranchSummaryEntry;
1388
1439
  /** Raw session context built during navigation — pass to renderInitialMessages to skip a second O(N) walk. */
1389
1440
  sessionContext?: SessionContext;
1441
+ /**
1442
+ * Set when `targetId` is an `ask` toolResult, `options.allowAskReopen`
1443
+ * was set, and `options.reanswerAskResult` was not supplied: nothing was
1444
+ * mutated. The caller must re-open the ask picker with these
1445
+ * `questions`, then call `navigateTree(targetId, { ...options,
1446
+ * reanswerAskResult })` with the produced result to actually branch
1447
+ * (issue #5642).
1448
+ */
1449
+ reopenAsk?: {
1450
+ toolCallId: string;
1451
+ questions: AskToolInput["questions"];
1452
+ };
1390
1453
  }>;
1454
+ /**
1455
+ * Build a standalone `AgentToolContext` for running `AskTool.execute()`
1456
+ * outside a normal agent turn, for `/tree` `ask` re-answer (issue #5642).
1457
+ * `SelectorController` has no reachable `ToolContextStore` (that store is
1458
+ * built inside `sdk.ts` and never threaded through to mode controllers),
1459
+ * so this mirrors `refreshMCPTools()`'s `getCustomToolContext` factory
1460
+ * with real session state instead of a `{ ... } as unknown as
1461
+ * AgentToolContext` cast that could silently compile with an incomplete
1462
+ * context (roboomp review on #5895) — every `CustomToolContext` field is
1463
+ * backed by live session state, so a future required field fails to
1464
+ * compile here instead of surfacing as `undefined` at runtime.
1465
+ */
1466
+ buildAskReanswerContext(uiContext: ExtensionUIContext): AgentToolContext;
1391
1467
  /**
1392
1468
  * Get all user messages from session for branch selector.
1393
1469
  */
@@ -219,6 +219,16 @@ export declare const SUBAGENT_WARNING_MISSING_YIELD = "SYSTEM WARNING: Subagent
219
219
  export declare function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): FinalizeSubprocessOutputResult;
220
220
  /**
221
221
  * Create proxy tools that reuse the parent's MCP connections.
222
+ *
223
+ * Each proxy delegates to the current source `MCPTool`/`DeferredMCPTool` rather
224
+ * than rebuilding a raw `tools/call` request, so the Task/subagent path shares
225
+ * the source tool's authoritative outbound boundary: harness-intent (`i`)
226
+ * stripping, optional-placeholder pruning, local-URL resolution, reconnect
227
+ * retry, abort handling, and result/provider metadata. The source tool is
228
+ * re-resolved on every call by raw MCP server/tool metadata (not the normalized
229
+ * display name), so a reconnect that swaps the instance in `getTools()` is
230
+ * always honored. The proxy adds only the Task-specific 60s call timeout,
231
+ * combining its abort signal with the caller's around source execution.
222
232
  */
223
233
  export declare function createMCPProxyTools(mcpManager: MCPManager): CustomTool[];
224
234
  export declare function createSubagentSettings(baseSettings: Settings, overrides?: Partial<Record<SettingPath, unknown>>, inheritedServiceTier?: ServiceTierByFamily | null): Settings;
@@ -66,6 +66,7 @@ export declare class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskT
66
66
  readonly summary = "Spawn subagents to complete delegated tasks";
67
67
  readonly strict = false;
68
68
  readonly loadMode = "essential";
69
+ readonly lenientArgValidation = true;
69
70
  readonly renderResult: typeof renderResult;
70
71
  readonly mergeCallAndResult = true;
71
72
  get parameters(): TaskToolSchemaInstance;
@@ -0,0 +1,3 @@
1
+ import type { AgentDefinition } from "./types.js";
2
+ /** Resolve an agent's prewalk default, including the bundled task opt-in. */
3
+ export declare function resolveAgentPrewalkDefault(agent: AgentDefinition, taskPrewalk: boolean): boolean | string | undefined;
@@ -35,6 +35,16 @@ declare const askSchema: import("arktype/internal/variants/object.ts").ObjectTyp
35
35
  }[];
36
36
  }, {}>;
37
37
  export type AskToolInput = typeof askSchema.infer;
38
+ /**
39
+ * Recover a validated `questions` payload from a persisted `ask` toolCall's
40
+ * `arguments`. Used by `/tree` re-answer (issue #5642): selecting a past
41
+ * `ask` toolResult re-opens the picker with the *original* questions, so the
42
+ * new answer branches as a sibling instead of mutating the old one. Runs the
43
+ * same schema the live tool call validated against — legacy/corrupted
44
+ * persisted args fail closed (`undefined`) rather than feeding malformed
45
+ * data back into the picker.
46
+ */
47
+ export declare function recoverAskQuestions(toolCallArguments: unknown): AskToolInput["questions"] | undefined;
38
48
  /** Result for a single question */
39
49
  export interface QuestionResult {
40
50
  id: string;
@@ -46,6 +46,12 @@ export declare const TOOL_TIMEOUTS: {
46
46
  export type ToolWithTimeout = keyof typeof TOOL_TIMEOUTS;
47
47
  /**
48
48
  * Clamp a raw timeout to the allowed range for a tool.
49
- * If rawTimeout is undefined, returns the tool's default.
49
+ *
50
+ * When `rawTimeout` is undefined the tool's `default` is used. A positive
51
+ * `maxTimeout` (the `tools.maxTimeout` global ceiling) caps the *resolved*
52
+ * value — including the default-fallback path — before the per-tool `min`/`max`
53
+ * floor and ceiling apply, so a configured global cap governs calls where the
54
+ * agent omits `timeout`, not only explicitly-passed values. `maxTimeout <= 0`
55
+ * means no global cap.
50
56
  */
51
- export declare function clampTimeout(tool: ToolWithTimeout, rawTimeout?: number): number;
57
+ export declare function clampTimeout(tool: ToolWithTimeout, rawTimeout?: number, maxTimeout?: number): number;
@@ -23,13 +23,11 @@ export interface StartupChangelogSelection {
23
23
  selectedEntries: number;
24
24
  }
25
25
  /**
26
- * Parse changelog entries from the file at `changelogPath`. Scans for `## [x.y.z]`
27
- * headings and collects each block until the next heading or EOF.
26
+ * Parse changelog entries from omp's package asset when available, falling back
27
+ * to the copy embedded in compiled binaries.
28
28
  *
29
- * Returns `[]` when `changelogPath` is `undefined` (package directory not
30
- * resolvable see `getChangelogPath`) or the file is missing. Callers MUST NOT
31
- * synthesize a fallback path from the host project's cwd; doing so caused issue
32
- * #1423 (the host project's `CHANGELOG.md` was rendered as omp's).
29
+ * The embedded fallback keeps standalone binaries self-contained without
30
+ * resolving relative to the host project's cwd, which caused issue #1423.
33
31
  */
34
32
  export declare function parseChangelog(changelogPath: string | undefined): Promise<ChangelogEntry[]>;
35
33
  /**