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

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 (153) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/dist/cli.js +5025 -4930
  3. package/dist/types/config/__tests__/model-registry.test.d.ts +1 -0
  4. package/dist/types/config/model-registry.d.ts +28 -1
  5. package/dist/types/config/model-resolver.d.ts +5 -1
  6. package/dist/types/config/settings-schema.d.ts +50 -3
  7. package/dist/types/dap/client.d.ts +19 -0
  8. package/dist/types/extensibility/extensions/runner.d.ts +4 -2
  9. package/dist/types/extensibility/extensions/types.d.ts +3 -0
  10. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -0
  11. package/dist/types/extensibility/legacy-pi-tui-shim.d.ts +10 -0
  12. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +1 -1
  13. package/dist/types/extensibility/typebox.d.ts +4 -0
  14. package/dist/types/hindsight/client.d.ts +10 -0
  15. package/dist/types/hindsight/config.d.ts +8 -0
  16. package/dist/types/internal-urls/registry-helpers.d.ts +11 -0
  17. package/dist/types/lsp/client.d.ts +2 -0
  18. package/dist/types/mcp/manager.d.ts +6 -2
  19. package/dist/types/mcp/render.d.ts +2 -2
  20. package/dist/types/mcp/tool-bridge.d.ts +16 -3
  21. package/dist/types/mcp/types.d.ts +6 -0
  22. package/dist/types/mnemopi/state.d.ts +2 -0
  23. package/dist/types/modes/components/oauth-selector.d.ts +8 -0
  24. package/dist/types/modes/components/settings-defs.d.ts +1 -0
  25. package/dist/types/modes/components/user-message.d.ts +25 -1
  26. package/dist/types/modes/controllers/extension-ui-controller.d.ts +8 -0
  27. package/dist/types/modes/controllers/mcp-command-controller.d.ts +3 -0
  28. package/dist/types/modes/interactive-mode.d.ts +7 -1
  29. package/dist/types/modes/rpc/rpc-client.d.ts +10 -1
  30. package/dist/types/modes/rpc/rpc-frame.d.ts +16 -0
  31. package/dist/types/modes/rpc/rpc-messages.d.ts +26 -0
  32. package/dist/types/modes/rpc/rpc-types.d.ts +40 -0
  33. package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
  34. package/dist/types/modes/setup-wizard/scenes/types.d.ts +9 -1
  35. package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
  36. package/dist/types/modes/types.d.ts +14 -1
  37. package/dist/types/modes/utils/ui-helpers.d.ts +12 -8
  38. package/dist/types/sdk.d.ts +1 -0
  39. package/dist/types/session/agent-session-error-log.test.d.ts +1 -0
  40. package/dist/types/session/agent-session.d.ts +86 -3
  41. package/dist/types/session/session-loader.d.ts +6 -4
  42. package/dist/types/task/executor.d.ts +10 -0
  43. package/dist/types/task/index.d.ts +1 -0
  44. package/dist/types/task/prewalk.d.ts +3 -0
  45. package/dist/types/task/types.d.ts +14 -0
  46. package/dist/types/tools/ask.d.ts +10 -0
  47. package/dist/types/tools/hub/jobs.d.ts +1 -1
  48. package/dist/types/tools/index.d.ts +3 -0
  49. package/dist/types/tools/report-tool-issue.d.ts +24 -9
  50. package/dist/types/tools/tool-timeouts.d.ts +8 -2
  51. package/dist/types/utils/changelog.d.ts +4 -6
  52. package/dist/types/web/search/providers/firecrawl.d.ts +9 -0
  53. package/dist/types/web/search/types.d.ts +1 -1
  54. package/package.json +13 -13
  55. package/scripts/legacy-pi-virtual-module.ts +1 -1
  56. package/src/cli/grievances-cli.ts +2 -2
  57. package/src/cli/models-cli.ts +37 -17
  58. package/src/cli/update-cli.ts +14 -1
  59. package/src/cli/usage-cli.ts +1 -1
  60. package/src/config/__tests__/model-registry.test.ts +147 -0
  61. package/src/config/model-registry.ts +87 -24
  62. package/src/config/model-resolver.ts +56 -32
  63. package/src/config/settings-schema.ts +46 -3
  64. package/src/config/settings.ts +68 -5
  65. package/src/dap/client.ts +86 -7
  66. package/src/edit/diff.ts +4 -4
  67. package/src/eval/py/prelude.py +5 -5
  68. package/src/export/html/template.js +23 -9
  69. package/src/extensibility/extensions/runner.ts +9 -4
  70. package/src/extensibility/extensions/types.ts +3 -0
  71. package/src/extensibility/legacy-pi-coding-agent-shim.ts +1 -0
  72. package/src/extensibility/legacy-pi-tui-shim.ts +10 -0
  73. package/src/extensibility/plugins/legacy-pi-compat.ts +851 -310
  74. package/src/extensibility/typebox.ts +22 -9
  75. package/src/hindsight/client.test.ts +42 -0
  76. package/src/hindsight/client.ts +40 -3
  77. package/src/hindsight/config.ts +18 -0
  78. package/src/internal-urls/registry-helpers.ts +40 -0
  79. package/src/launch/broker.ts +31 -5
  80. package/src/lsp/client.ts +23 -0
  81. package/src/lsp/clients/biome-client.ts +3 -4
  82. package/src/lsp/index.ts +26 -8
  83. package/src/main.ts +16 -5
  84. package/src/mcp/manager.ts +39 -8
  85. package/src/mcp/render.ts +94 -35
  86. package/src/mcp/tool-bridge.ts +116 -11
  87. package/src/mcp/types.ts +7 -0
  88. package/src/mnemopi/state.ts +70 -3
  89. package/src/modes/components/agent-dashboard.ts +6 -2
  90. package/src/modes/components/agent-hub.ts +26 -9
  91. package/src/modes/components/chat-transcript-builder.ts +13 -2
  92. package/src/modes/components/diff.ts +2 -2
  93. package/src/modes/components/oauth-selector.ts +24 -7
  94. package/src/modes/components/plan-review-overlay.ts +20 -4
  95. package/src/modes/components/settings-defs.ts +5 -2
  96. package/src/modes/components/settings-selector.ts +9 -5
  97. package/src/modes/components/tool-execution.test.ts +63 -2
  98. package/src/modes/components/user-message.ts +100 -1
  99. package/src/modes/controllers/command-controller.ts +36 -12
  100. package/src/modes/controllers/event-controller.ts +4 -1
  101. package/src/modes/controllers/extension-ui-controller.ts +18 -0
  102. package/src/modes/controllers/input-controller.ts +47 -12
  103. package/src/modes/controllers/mcp-command-controller.ts +70 -40
  104. package/src/modes/controllers/selector-controller.ts +82 -6
  105. package/src/modes/interactive-mode.ts +39 -4
  106. package/src/modes/rpc/rpc-client.ts +94 -3
  107. package/src/modes/rpc/rpc-frame.ts +164 -4
  108. package/src/modes/rpc/rpc-messages.ts +127 -0
  109. package/src/modes/rpc/rpc-mode.ts +79 -7
  110. package/src/modes/rpc/rpc-types.ts +34 -2
  111. package/src/modes/setup-wizard/scenes/model.ts +5 -7
  112. package/src/modes/setup-wizard/scenes/providers.ts +3 -2
  113. package/src/modes/setup-wizard/scenes/sign-in.ts +8 -2
  114. package/src/modes/setup-wizard/scenes/theme.ts +13 -3
  115. package/src/modes/setup-wizard/scenes/types.ts +9 -1
  116. package/src/modes/setup-wizard/scenes/web-search.ts +6 -1
  117. package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
  118. package/src/modes/types.ts +16 -3
  119. package/src/modes/utils/ui-helpers.ts +40 -20
  120. package/src/prompts/goals/guided-goal-system.md +24 -3
  121. package/src/prompts/system/workflow-notice.md +1 -1
  122. package/src/prompts/tools/task.md +12 -2
  123. package/src/sdk.ts +179 -51
  124. package/src/session/agent-session-error-log.test.ts +59 -0
  125. package/src/session/agent-session.ts +351 -46
  126. package/src/session/session-context.test.ts +224 -1
  127. package/src/session/session-context.ts +41 -2
  128. package/src/session/session-loader.ts +10 -5
  129. package/src/slash-commands/helpers/usage-report.ts +3 -1
  130. package/src/system-prompt.ts +1 -2
  131. package/src/task/executor.ts +44 -27
  132. package/src/task/index.ts +63 -8
  133. package/src/task/prewalk.ts +6 -0
  134. package/src/task/structured-subagent.ts +3 -1
  135. package/src/task/types.ts +16 -0
  136. package/src/task/worktree.ts +25 -11
  137. package/src/tools/ask.ts +15 -0
  138. package/src/tools/bash.ts +14 -6
  139. package/src/tools/browser.ts +1 -1
  140. package/src/tools/debug.ts +1 -1
  141. package/src/tools/eval.ts +4 -5
  142. package/src/tools/fetch.ts +1 -1
  143. package/src/tools/hub/index.ts +1 -1
  144. package/src/tools/hub/jobs.ts +67 -8
  145. package/src/tools/hub/launch.ts +7 -0
  146. package/src/tools/index.ts +3 -0
  147. package/src/tools/report-tool-issue.ts +79 -28
  148. package/src/tools/tool-timeouts.ts +10 -3
  149. package/src/tools/write.ts +31 -0
  150. package/src/utils/changelog.ts +54 -58
  151. package/src/utils/git.ts +57 -49
  152. package/src/web/search/providers/firecrawl.ts +46 -13
  153. package/src/web/search/types.ts +5 -1
@@ -8,6 +8,7 @@ import type { CompactionResult } from "@oh-my-pi/pi-agent-core/compaction";
8
8
  import type { ImageContent, Model } from "@oh-my-pi/pi-ai";
9
9
  import type { BashResult } from "../../exec/bash-executor.js";
10
10
  import type { AgentSessionEvent, SessionStats } from "../../session/agent-session.js";
11
+ import { type RpcMessagesPage, type RpcMessagesPageOptions } from "./rpc-messages.js";
11
12
  import type { RpcAvailableSlashCommand, RpcHandoffResult, RpcHostToolDefinition, RpcSessionState, RpcSubagentEventFrame, RpcSubagentLifecycleFrame, RpcSubagentMessagesResult, RpcSubagentProgressFrame, RpcSubagentSnapshot, RpcSubagentSubscriptionLevel } from "./rpc-types.js";
12
13
  export interface RpcClientOptions {
13
14
  /** Path to the CLI entry point (default: searches for dist/cli.js) */
@@ -45,6 +46,12 @@ export interface RpcClientCustomTool<TParams extends Record<string, unknown> = R
45
46
  execute(params: TParams, context: RpcClientToolContext<TDetails>): Promise<RpcClientToolResult<TDetails>> | RpcClientToolResult<TDetails>;
46
47
  }
47
48
  export declare function defineRpcClientTool<TParams extends Record<string, unknown> = Record<string, unknown>, TDetails = unknown>(tool: RpcClientCustomTool<TParams, TDetails>): RpcClientCustomTool<TParams, TDetails>;
49
+ /** Failed RPC command; `code` mirrors the server's machine-readable error code when present. */
50
+ export declare class RpcCommandError extends Error {
51
+ readonly command: string;
52
+ readonly code?: string | undefined;
53
+ constructor(message: string, command: string, code?: string | undefined);
54
+ }
48
55
  export declare class RpcClient {
49
56
  #private;
50
57
  private options;
@@ -254,8 +261,10 @@ export declare class RpcClient {
254
261
  */
255
262
  getLastAssistantText(): Promise<string | null>;
256
263
  /**
257
- * Get all messages in the session.
264
+ * Get one stable, byte-bounded message page.
258
265
  */
266
+ getMessagesPage(options?: RpcMessagesPageOptions): Promise<RpcMessagesPage>;
267
+ /** Get all messages, draining stable pages when protocol v2 is available. */
259
268
  getMessages(): Promise<AgentMessage[]>;
260
269
  /**
261
270
  * Get list of OAuth providers available for login, with their current authentication status.
@@ -1,9 +1,25 @@
1
1
  /** Maximum UTF-8 size of one newline-delimited RPC frame, including the newline. */
2
2
  export declare const MAX_RPC_FRAME_BYTES: number;
3
+ /** Maximum UTF-8 size of one logical frame reassembled by protocol v2. */
4
+ export declare const MAX_RPC_REASSEMBLED_BYTES: number;
5
+ export type RpcProtocolVersion = 1 | 2;
6
+ /** Reassemble protocol v2 chunk frames after each JSONL line has been parsed. */
7
+ export declare class RpcFrameDecoder {
8
+ #private;
9
+ push(value: unknown): object | undefined;
10
+ }
3
11
  /** Serialize a complete JSONL frame while enforcing the transport byte ceiling. */
4
12
  export declare function encodeRpcFrame(frame: object, streamedMessageCount?: number, streamedMessages?: readonly unknown[]): string;
5
13
  /** Stateful encoder that tracks which messages a client has already received. */
6
14
  export declare class RpcFrameEncoder {
7
15
  #private;
16
+ setProtocolVersion(version: number): void;
17
+ /**
18
+ * Encode one logical frame into physical JSONL lines. Encoder bookkeeping runs
19
+ * eagerly; only chunk emission is lazy, so a chunked result can be streamed to
20
+ * stdout with backpressure without holding the whole transport in memory. The
21
+ * returned iterable MUST be fully consumed exactly once.
22
+ */
23
+ encodeFrames(frame: object): Iterable<string>;
8
24
  encode(frame: object): string;
9
25
  }
@@ -0,0 +1,26 @@
1
+ import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
2
+ export declare const RPC_MESSAGES_PAGE_BUSY_ERROR = "Cannot page messages while the session is changing";
3
+ export declare const RPC_MESSAGES_PAGE_STALE_ERROR = "RPC message cursor is stale";
4
+ /** Machine-readable reasons a `get_messages_page` request can fail; carried as `code` on the error response. */
5
+ export type RpcMessagesPageErrorCode = "session_busy" | "stale_cursor";
6
+ /** Paging failure that maps to a structured wire `code`, so clients can react without matching message text. */
7
+ export declare class RpcMessagesPageError extends Error {
8
+ readonly code: RpcMessagesPageErrorCode;
9
+ constructor(message: string, code: RpcMessagesPageErrorCode);
10
+ }
11
+ export interface RpcMessageSnapshot {
12
+ sessionId: string;
13
+ leafId: string | null;
14
+ messageCount: number;
15
+ }
16
+ export interface RpcMessagesPage {
17
+ messages: AgentMessage[];
18
+ nextCursor?: string;
19
+ totalMessages: number;
20
+ }
21
+ export interface RpcMessagesPageOptions {
22
+ cursor?: string;
23
+ limit?: number;
24
+ }
25
+ /** Page one stable in-memory message snapshot without crossing the v1 frame budget. */
26
+ export declare function pageRpcMessages(messages: readonly AgentMessage[], snapshot: RpcMessageSnapshot, options?: RpcMessagesPageOptions): RpcMessagesPage;
@@ -14,7 +14,12 @@ import type { FileEntry } from "../../session/session-entries.js";
14
14
  import type { AvailableSlashCommandSource } from "../../slash-commands/available-commands.js";
15
15
  import type { AgentProgress, SubagentEventPayload, SubagentLifecyclePayload, SubagentProgressPayload } from "../../task/index.js";
16
16
  import type { TodoPhase } from "../../tools/todo.js";
17
+ import type { RpcMessagesPage } from "./rpc-messages.js";
17
18
  export type RpcCommand = {
19
+ id?: string;
20
+ type: "negotiate_protocol";
21
+ protocolVersion: number;
22
+ } | {
18
23
  id?: string;
19
24
  type: "prompt";
20
25
  message: string;
@@ -157,6 +162,11 @@ export type RpcCommand = {
157
162
  } | {
158
163
  id?: string;
159
164
  type: "get_messages";
165
+ } | {
166
+ id?: string;
167
+ type: "get_messages_page";
168
+ cursor?: string;
169
+ limit?: number;
160
170
  } | {
161
171
  id?: string;
162
172
  type: "get_login_providers";
@@ -214,6 +224,21 @@ export interface RpcPromptResultFrame {
214
224
  id?: string;
215
225
  agentInvoked: boolean;
216
226
  }
227
+ export interface RpcReadyFrame {
228
+ type: "ready";
229
+ protocolVersion: 1;
230
+ supportedProtocolVersions: [1, 2];
231
+ maxFrameBytes: number;
232
+ maxReassembledFrameBytes: number;
233
+ }
234
+ export interface RpcChunkFrame {
235
+ type: "rpc_chunk";
236
+ chunkId: string;
237
+ index: number;
238
+ count: number;
239
+ byteLength: number;
240
+ data: string;
241
+ }
217
242
  export interface RpcHandoffResult {
218
243
  savedPath?: string;
219
244
  }
@@ -241,6 +266,14 @@ export interface RpcSubagentMessagesResult {
241
266
  messages: AgentMessage[];
242
267
  }
243
268
  export type RpcResponse = {
269
+ id?: string;
270
+ type: "response";
271
+ command: "negotiate_protocol";
272
+ success: true;
273
+ data: {
274
+ protocolVersion: 2;
275
+ };
276
+ } | {
244
277
  id?: string;
245
278
  type: "response";
246
279
  command: "prompt";
@@ -489,6 +522,12 @@ export type RpcResponse = {
489
522
  data: {
490
523
  messages: AgentMessage[];
491
524
  };
525
+ } | {
526
+ id?: string;
527
+ type: "response";
528
+ command: "get_messages_page";
529
+ success: true;
530
+ data: RpcMessagesPage;
492
531
  } | {
493
532
  id?: string;
494
533
  type: "response";
@@ -516,6 +555,7 @@ export type RpcResponse = {
516
555
  command: string;
517
556
  success: false;
518
557
  error: string;
558
+ code?: string;
519
559
  };
520
560
  export interface RpcSubagentLifecycleFrame {
521
561
  type: "subagent_lifecycle";
@@ -18,5 +18,5 @@ export declare class SignInTab implements SetupTab {
18
18
  handleInput(data: string): void;
19
19
  /** Forward mouse to the provider selector; pointer is inert during an active login or code prompt. */
20
20
  routeMouse(event: SgrMouseEvent, line: number, col: number): void;
21
- render(width: number): readonly string[];
21
+ render(width: number, maxLines?: number): readonly string[];
22
22
  }
@@ -14,6 +14,13 @@ export interface SetupSceneController extends Component {
14
14
  onMount?(): void | Promise<void>;
15
15
  onUnmount?(): void;
16
16
  dispose?(): void;
17
+ /**
18
+ * Render the scene body. `maxLines` is the number of body rows the wizard
19
+ * will actually display (header and footer already subtracted); scenes
20
+ * shrink list windows and drop decorative chrome so the selected row stays
21
+ * inside the budget. Overflow beyond `maxLines` is clipped by the wizard.
22
+ */
23
+ render(width: number, maxLines?: number): readonly string[];
17
24
  /**
18
25
  * Route an SGR mouse report (tracking is on while the wizard holds the
19
26
  * alternate screen). `line`/`col` are 0-based within this controller's
@@ -34,7 +41,8 @@ export interface SetupTab {
34
41
  * login). The parent scene MUST NOT switch tabs or finish while modal.
35
42
  */
36
43
  readonly modal: boolean;
37
- render(width: number): readonly string[];
44
+ /** See {@link SetupSceneController.render}: `maxLines` is the tab-local row budget. */
45
+ render(width: number, maxLines?: number): readonly string[];
38
46
  handleInput(data: string): void;
39
47
  invalidate(): void;
40
48
  /** Called when the tab becomes active (including initial mount). */
@@ -19,5 +19,5 @@ export declare class WebSearchTab implements SetupTab {
19
19
  routeMouse(event: SgrMouseEvent, line: number, _col: number): void;
20
20
  invalidate(): void;
21
21
  dispose(): void;
22
- render(width: number): readonly string[];
22
+ render(width: number, maxLines?: number): readonly string[];
23
23
  }
@@ -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;
@@ -645,6 +666,8 @@ export declare class AgentSession {
645
666
  get state(): AgentState;
646
667
  /** Current model (may be undefined if not yet selected) */
647
668
  get model(): Model | undefined;
669
+ /** Resolved selector while retry routing is using a fallback model. */
670
+ get retryFallbackModel(): string | undefined;
648
671
  /** Effective thinking level applied to the agent (the resolved level when `auto`). */
649
672
  get thinkingLevel(): ThinkingLevel | undefined;
650
673
  /** The selector the user configured: `auto` when auto mode is active, else the effective level. */
@@ -1214,7 +1237,22 @@ export declare class AgentSession {
1214
1237
  setAutoRetryEnabled(enabled: boolean): void;
1215
1238
  /**
1216
1239
  * Manually retry the last failed assistant turn.
1217
- * Removes the error message from agent state and re-attempts with a fresh retry budget.
1240
+ * Removes the error message from active agent state when present and
1241
+ * re-attempts with a fresh retry budget.
1242
+ *
1243
+ * A stream that stalls or aborts mid-tool-call ends the turn with
1244
+ * `stopReason: "error" | "aborted"` and then appends one synthetic
1245
+ * {@link isSyntheticToolResultMessage tool_result} per emitted tool call to
1246
+ * preserve the provider's tool_use/tool_result pairing (see
1247
+ * `createAbortedToolResult` in `agent-loop.ts`). Those placeholders trail the
1248
+ * failed assistant turn, so the retry lookback walks back over them before
1249
+ * checking the assistant message; it strips both the placeholders and the
1250
+ * failed turn before re-attempting.
1251
+ *
1252
+ * A restored session deliberately omits failed assistant turns from provider
1253
+ * context. In that case, the persisted display transcript remains the source
1254
+ * of truth for whether the current branch has a retryable failed tail.
1255
+ *
1218
1256
  * @returns true if retry was initiated, false if no failed turn to retry or agent is busy
1219
1257
  */
1220
1258
  retry(): Promise<boolean>;
@@ -1380,6 +1418,26 @@ export declare class AgentSession {
1380
1418
  navigateTree(targetId: string, options?: {
1381
1419
  summarize?: boolean;
1382
1420
  customInstructions?: string;
1421
+ /**
1422
+ * Opts into the two-phase `ask` toolResult re-answer protocol
1423
+ * (issue #5642): set only by the interactive `/tree` selector, which
1424
+ * knows how to re-open the picker on `reopenAsk` and complete the
1425
+ * navigation with `reanswerAskResult`. Every other public caller
1426
+ * (extensions, hooks, ACP, session-extension actions) leaves this
1427
+ * unset and gets the pre-#5642 plain leaf move onto `ask`
1428
+ * toolResults instead — they have no picker to re-open and would
1429
+ * otherwise report a successful no-op navigation (roboomp review on
1430
+ * #5895).
1431
+ */
1432
+ allowAskReopen?: boolean;
1433
+ /**
1434
+ * Completes an in-progress `ask` re-answer (issue #5642): the caller
1435
+ * already received `reopenAsk` from a prior call on the same
1436
+ * `targetId`, re-opened the picker, and is handing back the fresh
1437
+ * answer. Branches a new toolResult sibling instead of landing on
1438
+ * the original one.
1439
+ */
1440
+ reanswerAskResult?: AgentToolResult<AskToolDetails>;
1383
1441
  }): Promise<{
1384
1442
  editorText?: string;
1385
1443
  cancelled: boolean;
@@ -1387,7 +1445,32 @@ export declare class AgentSession {
1387
1445
  summaryEntry?: BranchSummaryEntry;
1388
1446
  /** Raw session context built during navigation — pass to renderInitialMessages to skip a second O(N) walk. */
1389
1447
  sessionContext?: SessionContext;
1448
+ /**
1449
+ * Set when `targetId` is an `ask` toolResult, `options.allowAskReopen`
1450
+ * was set, and `options.reanswerAskResult` was not supplied: nothing was
1451
+ * mutated. The caller must re-open the ask picker with these
1452
+ * `questions`, then call `navigateTree(targetId, { ...options,
1453
+ * reanswerAskResult })` with the produced result to actually branch
1454
+ * (issue #5642).
1455
+ */
1456
+ reopenAsk?: {
1457
+ toolCallId: string;
1458
+ questions: AskToolInput["questions"];
1459
+ };
1390
1460
  }>;
1461
+ /**
1462
+ * Build a standalone `AgentToolContext` for running `AskTool.execute()`
1463
+ * outside a normal agent turn, for `/tree` `ask` re-answer (issue #5642).
1464
+ * `SelectorController` has no reachable `ToolContextStore` (that store is
1465
+ * built inside `sdk.ts` and never threaded through to mode controllers),
1466
+ * so this mirrors `refreshMCPTools()`'s `getCustomToolContext` factory
1467
+ * with real session state instead of a `{ ... } as unknown as
1468
+ * AgentToolContext` cast that could silently compile with an incomplete
1469
+ * context (roboomp review on #5895) — every `CustomToolContext` field is
1470
+ * backed by live session state, so a future required field fails to
1471
+ * compile here instead of surfacing as `undefined` at runtime.
1472
+ */
1473
+ buildAskReanswerContext(uiContext: ExtensionUIContext): AgentToolContext;
1391
1474
  /**
1392
1475
  * Get all user messages from session for branch selector.
1393
1476
  */
@@ -21,9 +21,11 @@ export declare function parseSessionEntries(content: string): FileEntry[];
21
21
  export declare function loadEntriesFromFile(filePath: string, storage?: SessionStorage): Promise<FileEntry[]>;
22
22
  export declare function resolveBlobRefsInEntries(entries: FileEntry[], blobStore: BlobStore): Promise<void>;
23
23
  /**
24
- * Read-only message view of a session file: load entries, migrate to the
25
- * current version, resolve blob refs, and build the context along the
26
- * persisted leaf path (last entry). Does NOT create a writer or take the
27
- * session lock safe to call against a file another session is writing.
24
+ * Read-only transcript view of a session file: load entries, migrate to the
25
+ * current version, resolve blob refs, and build the display transcript along
26
+ * the persisted leaf path (last entry). Uses transcript mode (collapsed to the
27
+ * latest compaction) so failed/aborted tail turns stay visible, unlike the
28
+ * provider-context builder which drops them. Does NOT create a writer or take
29
+ * the session lock — safe to call against a file another session is writing.
28
30
  */
29
31
  export declare function loadSessionMessagesReadOnly(filePath: string): Promise<AgentMessage[]>;
@@ -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;
@@ -83,6 +83,7 @@ export declare const taskItemSchema: import("arktype/internal/variants/object.ts
83
83
  name?: string | undefined;
84
84
  agent: import("arktype/internal/attributes.ts").Default<string, "task">;
85
85
  task: string;
86
+ model?: string | string[] | undefined;
86
87
  outputSchema?: string | boolean | object | null | undefined;
87
88
  schemaMode?: "permissive" | "strict" | undefined;
88
89
  }, {}>;
@@ -94,6 +95,8 @@ export interface TaskItem {
94
95
  agent?: string;
95
96
  /** The work; required by the schema. */
96
97
  task?: string;
98
+ /** Explicit model selector or fallback chain for this spawn, including optional reasoning suffixes. */
99
+ model?: string | string[];
97
100
  /** Caller-provided output schema; its presence overrides the selected agent's schema. */
98
101
  outputSchema?: unknown;
99
102
  /** Validation behavior for a caller-provided or inherited output schema. */
@@ -105,6 +108,7 @@ export declare const taskSchema: import("arktype/internal/variants/object.ts").O
105
108
  name?: string | undefined;
106
109
  agent: import("arktype/internal/attributes.ts").Default<string, "task">;
107
110
  task: string;
111
+ model?: string | string[] | undefined;
108
112
  outputSchema?: string | boolean | object | null | undefined;
109
113
  schemaMode?: "permissive" | "strict" | undefined;
110
114
  isolated?: boolean | undefined;
@@ -113,6 +117,7 @@ declare const ALL_TASK_SCHEMAS: readonly [import("arktype/internal/variants/obje
113
117
  name?: string | undefined;
114
118
  agent: import("arktype/internal/attributes.ts").Default<string, "task">;
115
119
  task: string;
120
+ model?: string | string[] | undefined;
116
121
  outputSchema?: string | boolean | object | null | undefined;
117
122
  schemaMode?: "permissive" | "strict" | undefined;
118
123
  isolated?: boolean | undefined;
@@ -120,6 +125,7 @@ declare const ALL_TASK_SCHEMAS: readonly [import("arktype/internal/variants/obje
120
125
  name?: string | undefined;
121
126
  agent: import("arktype/internal/attributes.ts").Default<string, "task">;
122
127
  task: string;
128
+ model?: string | string[] | undefined;
123
129
  outputSchema?: string | boolean | object | null | undefined;
124
130
  schemaMode?: "permissive" | "strict" | undefined;
125
131
  }, {}>, import("arktype/internal/variants/object.ts").ObjectType<{
@@ -128,6 +134,7 @@ declare const ALL_TASK_SCHEMAS: readonly [import("arktype/internal/variants/obje
128
134
  name?: string | undefined;
129
135
  agent: import("arktype/internal/attributes.ts").Default<string, "task">;
130
136
  task: string;
137
+ model?: string | string[] | undefined;
131
138
  outputSchema?: string | boolean | object | null | undefined;
132
139
  schemaMode?: "permissive" | "strict" | undefined;
133
140
  isolated?: boolean | undefined;
@@ -138,6 +145,7 @@ declare const ALL_TASK_SCHEMAS: readonly [import("arktype/internal/variants/obje
138
145
  name?: string | undefined;
139
146
  agent: import("arktype/internal/attributes.ts").Default<string, "task">;
140
147
  task: string;
148
+ model?: string | string[] | undefined;
141
149
  outputSchema?: string | boolean | object | null | undefined;
142
150
  schemaMode?: "permissive" | "strict" | undefined;
143
151
  }[];
@@ -168,6 +176,8 @@ export interface TaskParams {
168
176
  agent?: string;
169
177
  /** The work (flat form). */
170
178
  task?: string;
179
+ /** Explicit model selector or fallback chain for the spawn, including optional reasoning suffixes. */
180
+ model?: string | string[];
171
181
  /** Caller-provided output schema; its presence overrides the selected agent's schema. */
172
182
  outputSchema?: unknown;
173
183
  /** Validation behavior for a caller-provided or inherited output schema. */
@@ -291,6 +301,8 @@ export interface AgentProgress {
291
301
  modelOverride?: string | string[];
292
302
  /** Resolved model display string in the form `<provider>/<id>`, optionally suffixed with `:<thinkingLevel>` when the level was set explicitly. Undefined when the model could not be resolved. */
293
303
  resolvedModel?: string;
304
+ /** True when {@link resolvedModel} is the target of an active retry fallback (not the originally configured model). Lets observer-only UIs (collab guests, Agent Hub rows with no live session) flag the fallback and keep the provider. */
305
+ resolvedModelIsFallback?: boolean;
294
306
  /** Data extracted by registered subprocess tool handlers (keyed by tool name) */
295
307
  extractedToolData?: Record<string, unknown[]>;
296
308
  /**
@@ -357,6 +369,8 @@ export interface SingleResult {
357
369
  modelOverride?: string | string[];
358
370
  /** Resolved model display string in the form `<provider>/<id>`, optionally suffixed with `:<thinkingLevel>` when the level was set explicitly. Omitted from tool-result JSON when undefined to keep wire payloads small. */
359
371
  resolvedModel?: string;
372
+ /** True when {@link resolvedModel} is the target of an active retry fallback. Mirrors {@link AgentProgress.resolvedModelIsFallback} onto the settled result. */
373
+ resolvedModelIsFallback?: boolean;
360
374
  error?: string;
361
375
  aborted?: boolean;
362
376
  abortReason?: string;
@@ -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;
@@ -55,7 +55,7 @@ export declare function noMatchingJobsResult(session: ToolSession, ids: string[]
55
55
  /** Bare `wait` with no running jobs and nobody who could message: nothing to block on. */
56
56
  export declare function nothingToWaitForResult(session: ToolSession): AgentToolResult<CoordinationDetails>;
57
57
  /** `cancel`: kill the named jobs; returns immediately with outcomes + snapshots. */
58
- export declare function executeCancel(session: ToolSession, manager: AsyncJobManager, ownerId: string | undefined, ids: string[]): AgentToolResult<CoordinationDetails>;
58
+ export declare function executeCancel(session: ToolSession, manager: AsyncJobManager, ownerId: string | undefined, ids: string[]): Promise<AgentToolResult<CoordinationDetails>>;
59
59
  /** `jobs`: read-only snapshot of every job plus the jobless running-agent roster. */
60
60
  export declare function executeJobsSnapshot(session: ToolSession, manager: AsyncJobManager, ownerId: string | undefined): AgentToolResult<CoordinationDetails>;
61
61
  /** Pending-call frame for job ops (wait/cancel/jobs). */
@@ -13,6 +13,7 @@ import type { LocalProtocolOptions } from "../internal-urls/index.js";
13
13
  import type { MCPManager } from "../mcp/index.js";
14
14
  import type { MnemopiSessionState } from "../mnemopi/state.js";
15
15
  import type { PlanModeState } from "../plan-mode/state.js";
16
+ import type { AgentLifecycleManager } from "../registry/agent-lifecycle.js";
16
17
  import type { AgentRegistry } from "../registry/agent-registry.js";
17
18
  import type { ArtifactManager } from "../session/artifacts.js";
18
19
  import type { ClientBridge } from "../session/client-bridge.js";
@@ -200,6 +201,8 @@ export interface ToolSession {
200
201
  xdevRegistry?: XdevRegistry;
201
202
  /** Agent registry for IRC routing across live sessions. */
202
203
  agentRegistry?: AgentRegistry;
204
+ /** Idle→parked→revive lifecycle owner; lets the hub kill a non-job-backed agent registration. Default: AgentLifecycleManager.global(). */
205
+ agentLifecycle?: () => AgentLifecycleManager;
203
206
  /** Get artifacts directory for artifact:// URLs */
204
207
  getArtifactsDir?: () => string | null;
205
208
  /** Get the ArtifactManager backing this session (shared across parent + subagents). */