@oh-my-pi/pi-coding-agent 16.2.12 → 16.3.0

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 (171) hide show
  1. package/CHANGELOG.md +115 -7
  2. package/dist/cli.js +5996 -5945
  3. package/dist/types/advisor/config.d.ts +4 -2
  4. package/dist/types/collab/host.d.ts +16 -0
  5. package/dist/types/collab/protocol.d.ts +9 -3
  6. package/dist/types/commit/model-selection.d.ts +5 -0
  7. package/dist/types/config/model-resolver.d.ts +12 -10
  8. package/dist/types/config/models-config-schema.d.ts +12 -0
  9. package/dist/types/config/models-config.d.ts +9 -0
  10. package/dist/types/config/settings-schema.d.ts +28 -5
  11. package/dist/types/edit/modes/patch.d.ts +11 -0
  12. package/dist/types/eval/agent-bridge.d.ts +5 -1
  13. package/dist/types/exec/bash-executor.d.ts +1 -0
  14. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +86 -2
  15. package/dist/types/extensibility/skills.d.ts +2 -1
  16. package/dist/types/mnemopi/state.d.ts +12 -0
  17. package/dist/types/modes/components/agent-hub.d.ts +9 -5
  18. package/dist/types/modes/components/status-line/component.test.d.ts +1 -0
  19. package/dist/types/modes/components/usage-row.d.ts +1 -1
  20. package/dist/types/modes/controllers/command-controller.d.ts +0 -1
  21. package/dist/types/modes/controllers/extension-ui-controller.d.ts +6 -0
  22. package/dist/types/modes/controllers/streaming-reveal.d.ts +17 -1
  23. package/dist/types/modes/controllers/tool-args-reveal.d.ts +30 -3
  24. package/dist/types/modes/interactive-mode.d.ts +12 -7
  25. package/dist/types/modes/rpc/rpc-client.d.ts +5 -0
  26. package/dist/types/modes/rpc/rpc-mode.d.ts +64 -1
  27. package/dist/types/modes/session-teardown.d.ts +63 -0
  28. package/dist/types/modes/session-teardown.test.d.ts +1 -0
  29. package/dist/types/modes/theme/theme.d.ts +9 -4
  30. package/dist/types/modes/types.d.ts +2 -3
  31. package/dist/types/modes/utils/copy-targets.d.ts +2 -0
  32. package/dist/types/plan-mode/approved-plan-prompt.test.d.ts +1 -0
  33. package/dist/types/sdk.d.ts +9 -0
  34. package/dist/types/session/agent-session.d.ts +46 -13
  35. package/dist/types/session/exit-diagnostics.d.ts +48 -0
  36. package/dist/types/session/session-loader.d.ts +5 -0
  37. package/dist/types/task/executor.d.ts +12 -5
  38. package/dist/types/task/parallel.d.ts +1 -0
  39. package/dist/types/task/render.test.d.ts +1 -0
  40. package/dist/types/thinking.d.ts +2 -0
  41. package/dist/types/tools/checkpoint.d.ts +8 -0
  42. package/dist/types/tools/eval-render.d.ts +1 -0
  43. package/dist/types/tools/fetch.d.ts +11 -1
  44. package/dist/types/tools/index.d.ts +3 -1
  45. package/dist/types/tools/irc.d.ts +1 -0
  46. package/dist/types/tools/output-meta.d.ts +7 -0
  47. package/dist/types/tools/output-schema-validator.d.ts +7 -4
  48. package/dist/types/tools/path-utils.d.ts +16 -0
  49. package/dist/types/tools/render-utils.d.ts +4 -1
  50. package/dist/types/utils/git.d.ts +47 -2
  51. package/dist/types/web/search/provider.d.ts +7 -0
  52. package/dist/types/web/search/types.d.ts +1 -1
  53. package/package.json +12 -12
  54. package/src/advisor/config.ts +4 -2
  55. package/src/cli/bench-cli.ts +7 -2
  56. package/src/collab/guest.ts +94 -5
  57. package/src/collab/host.ts +80 -3
  58. package/src/collab/protocol.ts +9 -2
  59. package/src/commit/model-selection.ts +8 -2
  60. package/src/config/keybindings.ts +3 -1
  61. package/src/config/model-discovery.ts +66 -2
  62. package/src/config/model-registry.ts +7 -3
  63. package/src/config/model-resolver.ts +51 -23
  64. package/src/config/models-config-schema.ts +13 -0
  65. package/src/config/settings-schema.ts +44 -14
  66. package/src/edit/hashline/diff.ts +13 -2
  67. package/src/edit/index.ts +58 -8
  68. package/src/edit/modes/patch.ts +53 -18
  69. package/src/edit/streaming.ts +7 -6
  70. package/src/eval/__tests__/agent-bridge.test.ts +57 -1
  71. package/src/eval/agent-bridge.ts +15 -5
  72. package/src/exec/bash-executor.ts +7 -12
  73. package/src/extensibility/legacy-pi-coding-agent-shim.ts +534 -16
  74. package/src/extensibility/skills.ts +29 -6
  75. package/src/goals/guided-setup.ts +4 -3
  76. package/src/internal-urls/docs-index.generated.txt +1 -1
  77. package/src/lsp/client.ts +11 -14
  78. package/src/main.ts +38 -10
  79. package/src/mnemopi/state.ts +43 -7
  80. package/src/modes/acp/acp-agent.ts +1 -1
  81. package/src/modes/components/agent-hub.ts +10 -2
  82. package/src/modes/components/agent-transcript-viewer.ts +39 -7
  83. package/src/modes/components/assistant-message.ts +16 -10
  84. package/src/modes/components/chat-transcript-builder.ts +11 -1
  85. package/src/modes/components/custom-editor.test.ts +20 -0
  86. package/src/modes/components/hook-selector.ts +44 -25
  87. package/src/modes/components/model-selector.ts +1 -1
  88. package/src/modes/components/status-line/component.test.ts +44 -0
  89. package/src/modes/components/status-line/component.ts +9 -1
  90. package/src/modes/components/status-line/segments.ts +3 -3
  91. package/src/modes/components/usage-row.ts +16 -2
  92. package/src/modes/controllers/command-controller.ts +6 -21
  93. package/src/modes/controllers/event-controller.ts +11 -9
  94. package/src/modes/controllers/extension-ui-controller.ts +84 -3
  95. package/src/modes/controllers/input-controller.ts +16 -13
  96. package/src/modes/controllers/selector-controller.ts +3 -8
  97. package/src/modes/controllers/streaming-reveal.ts +75 -53
  98. package/src/modes/controllers/tool-args-reveal.ts +340 -10
  99. package/src/modes/interactive-mode.ts +122 -79
  100. package/src/modes/rpc/rpc-client.ts +21 -0
  101. package/src/modes/rpc/rpc-mode.ts +197 -46
  102. package/src/modes/session-teardown.test.ts +219 -0
  103. package/src/modes/session-teardown.ts +82 -0
  104. package/src/modes/setup-wizard/scenes/theme.ts +2 -2
  105. package/src/modes/skill-command.ts +7 -20
  106. package/src/modes/theme/theme.ts +29 -15
  107. package/src/modes/types.ts +2 -3
  108. package/src/modes/utils/copy-targets.ts +12 -0
  109. package/src/modes/utils/ui-helpers.ts +19 -2
  110. package/src/plan-mode/approved-plan-prompt.test.ts +36 -0
  111. package/src/prompts/advisor/system.md +1 -1
  112. package/src/prompts/agents/tester.md +6 -2
  113. package/src/prompts/skills/autoload.md +8 -0
  114. package/src/prompts/skills/user-invocation.md +11 -0
  115. package/src/prompts/steering/parent-irc.md +5 -0
  116. package/src/prompts/system/irc-incoming.md +2 -0
  117. package/src/prompts/system/mid-run-todo-nudge.md +3 -0
  118. package/src/prompts/system/plan-mode-approved.md +7 -10
  119. package/src/prompts/system/plan-mode-compact-instructions.md +2 -1
  120. package/src/prompts/system/plan-mode-reference.md +3 -4
  121. package/src/prompts/system/rewind-report.md +6 -0
  122. package/src/prompts/system/subagent-system-prompt.md +3 -0
  123. package/src/prompts/tools/irc.md +2 -1
  124. package/src/prompts/tools/job.md +2 -2
  125. package/src/prompts/tools/rewind.md +3 -2
  126. package/src/prompts/tools/ssh.md +1 -0
  127. package/src/prompts/tools/task.md +4 -0
  128. package/src/sdk.ts +18 -4
  129. package/src/session/agent-session.ts +660 -114
  130. package/src/session/exit-diagnostics.ts +202 -0
  131. package/src/session/session-context.ts +25 -13
  132. package/src/session/session-loader.ts +58 -25
  133. package/src/session/session-manager.ts +0 -1
  134. package/src/session/session-persistence.ts +30 -4
  135. package/src/session/settings-stream-fn.ts +14 -0
  136. package/src/slash-commands/builtin-registry.ts +35 -3
  137. package/src/system-prompt.ts +15 -1
  138. package/src/task/executor.ts +31 -8
  139. package/src/task/index.ts +31 -9
  140. package/src/task/isolation-runner.ts +18 -2
  141. package/src/task/parallel.ts +7 -2
  142. package/src/task/render.test.ts +121 -0
  143. package/src/task/render.ts +48 -2
  144. package/src/task/worktree.ts +12 -13
  145. package/src/task/yield-assembly.ts +8 -5
  146. package/src/thinking.ts +5 -0
  147. package/src/tools/ask.ts +188 -9
  148. package/src/tools/ast-edit.ts +7 -0
  149. package/src/tools/ast-grep.ts +11 -0
  150. package/src/tools/bash.ts +6 -30
  151. package/src/tools/checkpoint.ts +15 -1
  152. package/src/tools/eval-render.ts +15 -0
  153. package/src/tools/fetch.ts +82 -18
  154. package/src/tools/gh.ts +1 -1
  155. package/src/tools/grep.ts +45 -27
  156. package/src/tools/index.ts +3 -1
  157. package/src/tools/irc.ts +24 -9
  158. package/src/tools/output-meta.ts +50 -0
  159. package/src/tools/output-schema-validator.ts +152 -15
  160. package/src/tools/path-utils.ts +55 -10
  161. package/src/tools/read.ts +1 -1
  162. package/src/tools/render-utils.ts +5 -3
  163. package/src/tools/ssh.ts +8 -1
  164. package/src/tools/yield.ts +41 -1
  165. package/src/utils/commit-message-generator.ts +2 -2
  166. package/src/utils/git.ts +271 -29
  167. package/src/utils/thinking-display.ts +13 -0
  168. package/src/web/search/index.ts +9 -28
  169. package/src/web/search/provider.ts +26 -1
  170. package/src/web/search/providers/duckduckgo.ts +1 -1
  171. package/src/web/search/types.ts +5 -1
@@ -2,6 +2,8 @@
2
2
  type ToolArgsRevealComponent = {
3
3
  updateArgs(args: unknown, toolCallId?: string): void;
4
4
  };
5
+ /** String fields the streamed-args decode reads incrementally for `toolName`. */
6
+ export declare function streamingStringKeysForTool(toolName: string, rawInput: boolean): readonly string[] | undefined;
5
7
  type ToolArgsRevealControllerOptions = {
6
8
  getSmoothStreaming(): boolean;
7
9
  requestRender(): void;
@@ -9,8 +11,29 @@ type ToolArgsRevealControllerOptions = {
9
11
  type ToolArgsRevealTarget = {
10
12
  rawInput: boolean;
11
13
  exposeRawPartialJson: boolean;
12
- fullArgs: Record<string, unknown>;
14
+ streamingStringKeys?: readonly string[];
13
15
  };
16
+ type StreamedToolArgsSource = {
17
+ /** Custom-tool raw text stream (`customWireName` tools): never JSON-parsed. */
18
+ rawInput: boolean;
19
+ /** Provider-parsed arguments, spread UNDER the fresh decode: a dialect
20
+ * projector may carry keys a raw re-parse cannot recover, but any key the
21
+ * fresh parse does recover wins — provider parses lag the stream by up to
22
+ * STREAMING_JSON_PARSE_MIN_GROWTH bytes mid-stream. */
23
+ fullArgs?: Record<string, unknown>;
24
+ /** See {@link streamingStringKeysForTool}. */
25
+ streamingStringKeys?: readonly string[];
26
+ };
27
+ /**
28
+ * One-shot decode of a streamed tool-call argument buffer into display args —
29
+ * the same decode the live reveal applies frame-by-frame, for paths that see
30
+ * the buffer once (transcript rebuilds on theme change, settings, focus
31
+ * replay). Keeps a rebuilt preview identical to the live preview: parsed
32
+ * fields come from a fresh parse of the full buffer, `streamingStringKeys`
33
+ * fields from the incremental string decoder (which also wins ties in the
34
+ * live path), never from the provider's throttled `arguments`.
35
+ */
36
+ export declare function decodeStreamedToolArgs(partialJson: string, source: StreamedToolArgsSource): Record<string, unknown>;
14
37
  /**
15
38
  * Paces streamed tool-call arguments the same way StreamingRevealController
16
39
  * paces assistant text: providers that deliver `partialJson` in large batches
@@ -30,8 +53,12 @@ export declare class ToolArgsRevealController {
30
53
  constructor(options: ToolArgsRevealControllerOptions);
31
54
  /**
32
55
  * Record the latest streamed argument text for a tool call and return the
33
- * args to render right now. With smoothing disabled the full target passes
34
- * through in the caller's legacy shape (`{ ...args, __partialJson }`).
56
+ * args to render right now. With smoothing disabled nothing is paced — the
57
+ * full received buffer decodes in one step but the entry still runs the
58
+ * incremental string decoder + parse throttle, so streamed text fields
59
+ * (write `content`, edit bodies, eval `code`) stay fresh between the
60
+ * provider's own throttled full-JSON parses instead of lagging up to
61
+ * STREAMING_JSON_PARSE_MIN_GROWTH bytes behind.
35
62
  */
36
63
  setTarget(id: string, partialJson: string, target: ToolArgsRevealTarget): Record<string, unknown>;
37
64
  /** Attach the component future ticks push frames into. */
@@ -9,9 +9,10 @@ import { KeybindingsManager } from "../config/keybindings";
9
9
  import { Settings } from "../config/settings";
10
10
  import type { ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectItem, ExtensionWidgetContent, ExtensionWidgetOptions } from "../extensibility/extensions";
11
11
  import type { CompactOptions } from "../extensibility/extensions/types";
12
+ import type { Skill } from "../extensibility/skills";
12
13
  import type { MCPManager } from "../mcp";
13
14
  import { type PlanApprovalDetails } from "../plan-mode/approved-plan";
14
- import type { AgentSession } from "../session/agent-session";
15
+ import { type AgentSession } from "../session/agent-session";
15
16
  import type { CompactMode } from "../session/compact-modes";
16
17
  import { HistoryStorage } from "../session/history-storage";
17
18
  import type { SessionContext } from "../session/session-context";
@@ -62,8 +63,10 @@ export interface InteractiveModeOptions {
62
63
  }
63
64
  /**
64
65
  * Build the anchored subagent HUD block: a bold accent "Subagents" header plus
65
- * one hooked row per running agent in the same `Id: description` shape the
66
+ * one tree row per running agent in the same `Id: description` shape the
66
67
  * inline task rows use (muted task preview when no description was given).
68
+ * Layout mirrors the Todos HUD exactly: unindented header, then
69
+ * `renderTreeList` rows (dim connectors) shifted right by one space.
67
70
  * Only detached background spawns are listed: a sync task call blocks the
68
71
  * parent turn and its inline tool block already renders progress live, and
69
72
  * eval `agent()` spawns are rendered by their own eval cell tree.
@@ -78,7 +81,6 @@ export declare class InteractiveMode implements InteractiveModeContext {
78
81
  keybindings: KeybindingsManager;
79
82
  agent: Agent;
80
83
  historyStorage?: HistoryStorage;
81
- titleSystemPrompt?: string;
82
84
  ui: TUI;
83
85
  chatContainer: TranscriptContainer;
84
86
  pendingMessagesContainer: Container;
@@ -153,7 +155,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
153
155
  lastStatusSpacer: Spacer | undefined;
154
156
  lastStatusText: Text | undefined;
155
157
  fileSlashCommands: Set<string>;
156
- skillCommands: Map<string, string>;
158
+ skillCommands: Map<string, Skill>;
157
159
  oauthManualInput: OAuthManualInputManager;
158
160
  collabHost?: CollabHost;
159
161
  collabGuest?: CollabGuestLink;
@@ -167,10 +169,14 @@ export declare class InteractiveMode implements InteractiveModeContext {
167
169
  focusParentSession(): Promise<void>;
168
170
  unfocusSession(): Promise<void>;
169
171
  clearTransientSessionUi(): void;
170
- constructor(session: AgentSession, version: string, changelogMarkdown?: string | undefined, setToolUIContext?: (uiContext: ExtensionUIContext, hasUI: boolean) => void, lspServers?: LspStartupServerInfo[] | undefined, mcpManager?: MCPManager, eventBus?: EventBus, titleSystemPrompt?: string);
172
+ constructor(session: AgentSession, version: string, changelogMarkdown?: string | undefined, setToolUIContext?: (uiContext: ExtensionUIContext, hasUI: boolean) => void, lspServers?: LspStartupServerInfo[] | undefined, mcpManager?: MCPManager, eventBus?: EventBus);
171
173
  playWelcomeIntro(): void;
172
174
  init(options?: InteractiveModeInitOptions): Promise<void>;
173
- /** Reload the title-generation system prompt override for the provided working directory. */
175
+ /** Reload the title-generation system prompt override for the provided working
176
+ * directory and stash it on the session so first-input titling
177
+ * ({@link input-controller}) and replan-driven refresh
178
+ * ({@link AgentSession.#refreshTitleAfterReplan}) share one source
179
+ * ({@link discoverTitleSystemPromptFile}; issue #3734). */
174
180
  refreshTitleSystemPrompt(cwd?: string): Promise<void>;
175
181
  /** Reload slash commands and autocomplete for the provided working directory. */
176
182
  refreshSlashCommandState(cwd?: string): Promise<void>;
@@ -212,7 +218,6 @@ export declare class InteractiveMode implements InteractiveModeContext {
212
218
  updateEditorBorderColor(): void;
213
219
  /** Refresh the running-subagents status badge from the active local or collab registry. */
214
220
  syncRunningSubagentBadge(): void;
215
- updateEditorTopBorder(): void;
216
221
  rebuildChatFromMessages(): void;
217
222
  /**
218
223
  * Render the ctrl+p model-role cycle chip track into its own anchored
@@ -51,6 +51,11 @@ export declare class RpcClient {
51
51
  constructor(options?: RpcClientOptions);
52
52
  /**
53
53
  * Start the RPC agent process.
54
+ *
55
+ * Safe to call again after {@link stop} on the same instance: a fresh
56
+ * {@link AbortController} is minted for each start, and any failure after
57
+ * the child spawn kills the child and clears internal state so callers may
58
+ * retry without leaking processes.
54
59
  */
55
60
  start(): Promise<void>;
56
61
  /**
@@ -2,7 +2,7 @@ import { type ExtensionUIContext, type ExtensionUIDialogOptions } from "../../ex
2
2
  import type { AgentSession } from "../../session/agent-session";
3
3
  import type { EventBus } from "../../utils/event-bus";
4
4
  import { RpcSubagentRegistry } from "./rpc-subagents";
5
- import type { RpcCommand, RpcExtensionUIRequest, RpcExtensionUIResponse, RpcHostToolCallRequest, RpcHostToolCancelRequest, RpcHostUriCancelRequest, RpcHostUriRequest, RpcResponse } from "./rpc-types";
5
+ import type { RpcCommand, RpcExtensionUIRequest, RpcExtensionUIResponse, RpcHostToolCallRequest, RpcHostToolCancelRequest, RpcHostToolResult, RpcHostToolUpdate, RpcHostUriCancelRequest, RpcHostUriRequest, RpcHostUriResult, RpcResponse } from "./rpc-types";
6
6
  export type * from "./rpc-types";
7
7
  export type PendingExtensionRequest = {
8
8
  resolve: (response: RpcExtensionUIResponse) => void;
@@ -70,6 +70,69 @@ export declare function watchAndReportLocalOnlyPromptResult(input: {
70
70
  onError: (error: Error) => void;
71
71
  extensionUserMessageTracker: RpcExtensionUserMessageTracker;
72
72
  }): void;
73
+ /**
74
+ * Dependencies for {@link dispatchRpcInputFrame}. Provided by the RPC mode
75
+ * entrypoint; broken out so tests can drive the input loop with stubs.
76
+ */
77
+ export interface RpcInputFrameDeps {
78
+ handleCommand: (command: RpcCommand) => Promise<RpcResponse>;
79
+ output: RpcOutput;
80
+ errorResponse: (id: string | undefined, command: string, message: string) => RpcResponse;
81
+ trackBackgroundTask?: (task: Promise<void>) => void;
82
+ pendingExtensionRequests: Map<string, PendingExtensionRequest>;
83
+ onHostToolResult: (frame: RpcHostToolResult) => void;
84
+ onHostToolUpdate: (frame: RpcHostToolUpdate) => void;
85
+ onHostUriResult: (frame: RpcHostUriResult) => void;
86
+ }
87
+ /**
88
+ * Dispatch a single parsed frame from the RPC input stream.
89
+ *
90
+ * Bash commands are dispatched in the background so the caller (the stdin loop
91
+ * in {@link runRpcMode}) can keep reading subsequent frames while a shell
92
+ * command is still running. This lets a client send `abort_bash` (or any other
93
+ * command) while a long-running `bash` is in flight. Response correlation is
94
+ * preserved via each command's `id`; ordering across concurrent commands is
95
+ * not guaranteed and clients MUST match on `id`.
96
+ *
97
+ * @returns `undefined` when the frame was routed to a side-channel handler
98
+ * (extension UI response, host tool/URI frames) or dispatched in the
99
+ * background (`bash`). Otherwise a promise that resolves once the response
100
+ * for the command has been emitted via `output`. Errors from `handleCommand`
101
+ * on non-`bash` commands propagate; the caller is expected to wrap them.
102
+ */
103
+ export declare function dispatchRpcInputFrame(parsed: unknown, deps: RpcInputFrameDeps): Promise<void> | undefined;
104
+ /**
105
+ * Coordinates deferred shutdown with in-flight background input tasks.
106
+ *
107
+ * `pi.shutdown()` from an extension only *requests* shutdown; the process must
108
+ * not exit while a background-dispatched command (`bash`, see
109
+ * {@link dispatchRpcInputFrame}) still owes the client a response frame. The
110
+ * coordinator tracks those tasks, re-checks the shutdown request whenever one
111
+ * settles (covering a shutdown requested mid-bash with no follow-up client
112
+ * frame), and drains every tracked task before invoking `performShutdown`.
113
+ * The shutdown sequence is latched so concurrent triggers (input loop and
114
+ * settling tasks) run it exactly once.
115
+ */
116
+ export declare class RpcShutdownCoordinator {
117
+ #private;
118
+ constructor(options: {
119
+ isShutdownRequested: () => boolean;
120
+ performShutdown: () => Promise<void>;
121
+ });
122
+ /**
123
+ * Track a background input task. When it settles it is untracked and the
124
+ * shutdown request is re-checked, so a deferred shutdown fires even when
125
+ * no further client frames arrive.
126
+ */
127
+ track(task: Promise<void>): void;
128
+ /** Await every tracked task, including tasks tracked while draining. */
129
+ drain(): Promise<void>;
130
+ /**
131
+ * If shutdown was requested, drain background tasks (so every owed
132
+ * response frame is written) before running the shutdown sequence.
133
+ */
134
+ checkShutdownRequested(): Promise<void>;
135
+ }
73
136
  export type RpcSubagentResetRegistry = Pick<RpcSubagentRegistry, "clear">;
74
137
  export declare function handleRpcSessionChange(session: RpcSessionChangeSession, command: RpcSessionChangeCommand, subagentRegistry?: RpcSubagentResetRegistry): Promise<RpcSessionChangeResult>;
75
138
  export declare function requestRpcEditor(pendingRequests: Map<string, PendingExtensionRequest>, output: RpcOutput, title: string, prefill?: string, dialogOptions?: ExtensionUIDialogOptions, editorOptions?: {
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Signal-safe session teardown: persists the in-progress editor draft, then
3
+ * disposes the session (which emits `session_shutdown`, cancels the session's
4
+ * background async jobs, and closes the session manager). Shared by the TUI
5
+ * Ctrl+C/Ctrl+D/`/exit` keypress path in `InteractiveMode.shutdown()` and by
6
+ * the postmortem `SIGINT`/`SIGTERM`/`SIGHUP`/`uncaughtException` handlers so a
7
+ * real kernel signal executes the exact same teardown as a keypress exit.
8
+ *
9
+ * Extracted (rather than inlined into `InteractiveMode`) so the callback body
10
+ * is directly unit-testable without instantiating the full TUI stack.
11
+ */
12
+ import { type postmortem } from "@oh-my-pi/pi-utils";
13
+ /** Dependencies the teardown captures at construction time. */
14
+ export interface SessionTeardownDeps {
15
+ /** Snapshot the current editor text; called once, before disposal touches session state. */
16
+ getDraftText: () => string;
17
+ /**
18
+ * Synchronously mark the session as disposing before any awaited teardown
19
+ * work. This closes the async gap where deferred jobs could otherwise start
20
+ * after a signal requested shutdown but before `disposeSession()` begins.
21
+ */
22
+ beginDispose: () => void;
23
+ /**
24
+ * Persist the snapshotted draft. Called even for an empty string so a
25
+ * previously-persisted draft sidecar is cleared on a clean exit.
26
+ */
27
+ saveDraft: (text: string) => Promise<void>;
28
+ /**
29
+ * Dispose the session — emits `session_shutdown`, drains async jobs, closes
30
+ * the manager. Receives the postmortem reason that triggered the teardown
31
+ * (undefined on the keypress/`/exit` path) so `AgentSession.dispose()` can
32
+ * persist the real exit reason instead of the generic `"dispose"`.
33
+ */
34
+ disposeSession: (reason?: postmortem.Reason) => Promise<void>;
35
+ }
36
+ /**
37
+ * Idempotent teardown: concurrent/repeat invocations share one settled
38
+ * promise. The optional `reason` is the postmortem reason that triggered the
39
+ * teardown (`sigterm`, `sighup`, `uncaught_exception`, …); only the FIRST
40
+ * call's reason is used — later callers await the same settled promise.
41
+ */
42
+ export type SessionTeardown = (reason?: postmortem.Reason) => Promise<void>;
43
+ /**
44
+ * Build a promise-memoized teardown function. The first call snapshots the
45
+ * draft text, marks the session disposing synchronously, runs `saveDraft`
46
+ * (draft-loss protection for `--resume`), then `disposeSession`; subsequent
47
+ * calls await the same settled promise, so the keypress
48
+ * `InteractiveMode.shutdown()` path and the postmortem signal callback cannot
49
+ * double-emit `session_shutdown`, double-dispose the session's async-job
50
+ * manager, or race each other.
51
+ *
52
+ * The postmortem callback forwards its `Reason` so the persisted
53
+ * `session_exit` diagnostic carries the real trigger (`sigterm`, `sighup`,
54
+ * `uncaught_exception`, …) instead of the generic `"dispose"` that plain
55
+ * programmatic disposal records. First call wins: a signal arriving after a
56
+ * keypress-initiated teardown awaits the in-flight promise and its reason is
57
+ * dropped — by then the exit entry is already being written as a normal exit.
58
+ *
59
+ * `saveDraft` failures are logged but never abort the disposal chain — a
60
+ * draft-write error must not leak background bash/task jobs or skip the
61
+ * extension `session_shutdown` event.
62
+ */
63
+ export declare function createSessionTeardown(deps: SessionTeardownDeps): SessionTeardown;
@@ -0,0 +1 @@
1
+ export {};
@@ -6,7 +6,7 @@ export type SymbolPreset = "unicode" | "nerd" | "ascii";
6
6
  /**
7
7
  * All available symbol keys organized by category.
8
8
  */
9
- export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.worktree" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.time" | "icon.pi" | "icon.ghost" | "icon.agents" | "icon.job" | "icon.cache" | "icon.cacheMiss" | "icon.input" | "icon.output" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.mic" | "icon.camera" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.julia" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.files" | "tab.shell" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.inspectImage" | "tool.goal" | "tool.irc" | "tool.delete" | "tool.move";
9
+ export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.worktree" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.time" | "icon.pi" | "icon.ghost" | "icon.agents" | "icon.job" | "icon.cache" | "icon.cacheMiss" | "icon.input" | "icon.output" | "icon.throughput" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.mic" | "icon.camera" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.julia" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.files" | "tab.shell" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.inspectImage" | "tool.goal" | "tool.irc" | "tool.delete" | "tool.move";
10
10
  export type SpinnerType = "status" | "activity";
11
11
  export type ThemeColor = "accent" | "border" | "borderAccent" | "borderMuted" | "success" | "error" | "warning" | "muted" | "dim" | "text" | "thinkingText" | "userMessageText" | "customMessageText" | "customMessageLabel" | "toolTitle" | "toolOutput" | "mdHeading" | "mdLink" | "mdLinkUrl" | "mdCode" | "mdCodeBlock" | "mdCodeBlockBorder" | "mdQuote" | "mdQuoteBorder" | "mdHr" | "mdListBullet" | "toolDiffAdded" | "toolDiffRemoved" | "toolDiffContext" | "syntaxComment" | "syntaxKeyword" | "syntaxFunction" | "syntaxVariable" | "syntaxString" | "syntaxNumber" | "syntaxType" | "syntaxOperator" | "syntaxPunctuation" | "thinkingOff" | "thinkingMinimal" | "thinkingLow" | "thinkingMedium" | "thinkingHigh" | "thinkingXhigh" | "bashMode" | "pythonMode" | "statusLineSep" | "statusLineModel" | "statusLinePath" | "statusLineGitClean" | "statusLineGitDirty" | "statusLineContext" | "statusLineSpend" | "statusLineStaged" | "statusLineDirty" | "statusLineUntracked" | "statusLineOutput" | "statusLineCost" | "statusLineSubagents";
12
12
  /** Check if a string is a valid ThemeColor value */
@@ -182,6 +182,7 @@ export declare class Theme {
182
182
  cacheMiss: string;
183
183
  input: string;
184
184
  output: string;
185
+ throughput: string;
185
186
  host: string;
186
187
  session: string;
187
188
  package: string;
@@ -262,19 +263,23 @@ export declare var theme: Theme;
262
263
  export declare function getCurrentThemeName(): string | undefined;
263
264
  /** Returns unstyled `text` before `initTheme()` assigns the global theme; use only for early-render paths. */
264
265
  export declare function fgOrPlain(color: ThemeColor, text: string, styledText?: string): string;
266
+ export interface ThemeChangeEvent {
267
+ /** Preview/presentation-only changes should repaint live UI without replacing native scrollback. */
268
+ ephemeral?: boolean;
269
+ }
265
270
  export declare function initTheme(enableWatcher?: boolean, symbolPreset?: SymbolPreset, colorBlindMode?: boolean, darkTheme?: string, lightTheme?: string): Promise<void>;
266
271
  export declare function setTheme(name: string, enableWatcher?: boolean): Promise<{
267
272
  success: boolean;
268
273
  error?: string;
269
274
  }>;
270
- export declare function previewTheme(name: string): Promise<{
275
+ export declare function previewTheme(name: string, event?: ThemeChangeEvent): Promise<{
271
276
  success: boolean;
272
277
  error?: string;
273
278
  }>;
274
279
  /**
275
280
  * Enable auto-detection mode, switching to the appropriate dark/light theme.
276
281
  */
277
- export declare function enableAutoTheme(): void;
282
+ export declare function enableAutoTheme(event?: ThemeChangeEvent): void;
278
283
  /**
279
284
  * Update the theme mappings for auto-detection mode.
280
285
  * When a dark/light mapping changes and auto-detection is active, re-evaluate the theme.
@@ -304,7 +309,7 @@ export declare function setColorBlindMode(enabled: boolean): Promise<void>;
304
309
  * Get the current color blind mode setting.
305
310
  */
306
311
  export declare function getColorBlindMode(): boolean;
307
- export declare function onThemeChange(callback: () => void): () => void;
312
+ export declare function onThemeChange(callback: (event: ThemeChangeEvent) => void): () => void;
308
313
  /**
309
314
  * Monotonic counter bumped on any theme-affecting change that should invalidate
310
315
  * cached renders: theme swaps and reloads (including the invalid-theme dark
@@ -8,6 +8,7 @@ import type { KeybindingsManager } from "../config/keybindings";
8
8
  import type { Settings } from "../config/settings";
9
9
  import type { ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectItem, ExtensionWidgetContent, ExtensionWidgetOptions } from "../extensibility/extensions";
10
10
  import type { CompactOptions } from "../extensibility/extensions/types";
11
+ import type { Skill } from "../extensibility/skills";
11
12
  import type { MCPManager } from "../mcp";
12
13
  import type { PlanApprovalDetails } from "../plan-mode/approved-plan";
13
14
  import type { AgentSession } from "../session/agent-session";
@@ -112,7 +113,6 @@ export interface InteractiveModeContext {
112
113
  historyStorage?: HistoryStorage;
113
114
  mcpManager?: MCPManager;
114
115
  lspServers?: LspStartupServerInfo[];
115
- titleSystemPrompt?: string;
116
116
  collabHost?: CollabHost;
117
117
  collabGuest?: CollabGuestLink;
118
118
  eventController: EventController;
@@ -186,7 +186,7 @@ export interface InteractiveModeContext {
186
186
  lastStatusSpacer: Spacer | undefined;
187
187
  lastStatusText: Text | undefined;
188
188
  fileSlashCommands: Set<string>;
189
- skillCommands: Map<string, string>;
189
+ skillCommands: Map<string, Skill>;
190
190
  oauthManualInput: OAuthManualInputManager;
191
191
  todoPhases: TodoPhase[];
192
192
  init(options?: InteractiveModeInitOptions): Promise<void>;
@@ -278,7 +278,6 @@ export interface InteractiveModeContext {
278
278
  getUserMessageText(message: Message): string;
279
279
  findLastAssistantMessage(): AssistantMessage | undefined;
280
280
  extractAssistantText(message: AssistantMessage): string;
281
- updateEditorTopBorder(): void;
282
281
  /** Refresh the running-subagents status badge from the active local or collab registry. */
283
282
  syncRunningSubagentBadge(): void;
284
283
  updateEditorBorderColor(): void;
@@ -60,6 +60,8 @@ export interface CopySource {
60
60
  export declare function extractBlocks(text: string): MessageBlock[];
61
61
  /** Extract fenced code blocks from assistant markdown, in document order. */
62
62
  export declare function extractCodeBlocks(text: string): CodeBlock[];
63
+ /** Walk the transcript backwards for the most recent fenced assistant code block. */
64
+ export declare function extractLastCodeBlock(messages: readonly AgentMessage[]): CodeBlock | undefined;
63
65
  /** Extract `>`-quoted blocks from assistant markdown, in document order. */
64
66
  export declare function extractQuoteBlocks(text: string): QuoteBlock[];
65
67
  /** Walk the transcript backwards for the most recent bash command or eval code. */
@@ -54,6 +54,15 @@ export interface CreateAgentSessionOptions {
54
54
  customSystemPrompt?: string;
55
55
  /** Already-loaded text appended through the bundled system prompt templates. */
56
56
  appendSystemPrompt?: string;
57
+ /**
58
+ * Already-loaded title-generation system prompt override (typically
59
+ * {@link discoverTitleSystemPromptFile} → {@link resolvePromptInput}). When
60
+ * set, every automatic session-title generation path on this session — the
61
+ * first-input title and the replan-driven refresh — uses this prompt
62
+ * instead of the bundled default. Refresh on cwd change via
63
+ * {@link AgentSession.setTitleSystemPrompt}.
64
+ */
65
+ titleSystemPrompt?: string;
57
66
  /** Optional provider-facing session identifier for prompt caches and sticky auth selection.
58
67
  * Keeps persisted session files isolated while reusing provider-side caches. */
59
68
  providerSessionId?: string;
@@ -17,6 +17,7 @@ import { Agent, type AgentEvent, type AgentMessage, type AgentState, type AgentT
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";
20
+ import { postmortem } from "@oh-my-pi/pi-utils";
20
21
  import { type AdvisorConfig } from "../advisor";
21
22
  import { type AsyncJob, type AsyncJobDeliveryState, AsyncJobManager } from "../async";
22
23
  import type { Rule } from "../capability/rule";
@@ -43,7 +44,7 @@ import type { PlanModeState } from "../plan-mode/state";
43
44
  import { type SecretObfuscator } from "../secrets/obfuscator";
44
45
  import { type ConfiguredThinkingLevel } from "../thinking";
45
46
  import { type DiscoverableTool, type DiscoverableToolSearchIndex } from "../tool-discovery/tool-index";
46
- import type { CheckpointState } from "../tools/checkpoint";
47
+ import type { CheckpointState, CompletedRewindState } from "../tools/checkpoint";
47
48
  import { type TodoItem, type TodoPhase } from "../tools/todo";
48
49
  import type { ClientBridge } from "./client-bridge";
49
50
  import { type CustomMessage } from "./messages";
@@ -120,6 +121,25 @@ export type AgentSessionEvent = AgentEvent | {
120
121
  };
121
122
  /** Listener function for agent session events */
122
123
  export type AgentSessionEventListener = (event: AgentSessionEvent) => void;
124
+ /**
125
+ * Budget for callers on the user-visible `/quit` / `/exit` shutdown path that
126
+ * want to cap how long they wait for `MnemopiSessionState.dispose()` to finish
127
+ * its consolidate pass. Consolidate fires fresh LLM fact extractions, each a
128
+ * 1–3 s round-trip, so interactive shutdown passes this budget to keep the
129
+ * UI responsive. Callers that keep the process/session host alive must omit it
130
+ * so dispose still awaits the full consolidate-then-close pipeline.
131
+ */
132
+ export declare const SHUTDOWN_CONSOLIDATE_BUDGET_MS = 1500;
133
+ export interface AgentSessionDisposeOptions {
134
+ mnemopiConsolidateTimeoutMs?: number;
135
+ /**
136
+ * Postmortem reason that triggered this dispose (signal/fatal teardown
137
+ * paths). When set, the persisted `session_exit` diagnostic records it
138
+ * instead of the generic `"dispose"` used for normal programmatic disposal
139
+ * (`/quit`, test teardown, subagent completion).
140
+ */
141
+ reason?: postmortem.Reason;
142
+ }
123
143
  export type CommandMetadataChangedListener = () => void | Promise<void>;
124
144
  export type AsyncJobSnapshotItem = Pick<AsyncJob, "id" | "type" | "status" | "label" | "startTime">;
125
145
  export interface AsyncJobSnapshot {
@@ -295,6 +315,14 @@ export interface AgentSessionConfig {
295
315
  * teardown never tears down the shared servers.
296
316
  */
297
317
  disconnectOwnedMcpManager?: () => Promise<void>;
318
+ /**
319
+ * Override the bundled system prompt used by automatic session-title
320
+ * generation paths (initial title + replan refresh). Source-of-truth is
321
+ * `TITLE_SYSTEM.md` discovered via {@link discoverTitleSystemPromptFile} and
322
+ * resolved through {@link resolvePromptInput}; refresh after a `/move`-style
323
+ * cwd change via {@link AgentSession.setTitleSystemPrompt}.
324
+ */
325
+ titleSystemPrompt?: string;
298
326
  }
299
327
  /** Options for AgentSession.prompt() */
300
328
  export interface PromptOptions {
@@ -346,7 +374,7 @@ export interface RoleModelCycleResult {
346
374
  export interface ResolvedRoleModel {
347
375
  role: string;
348
376
  model: Model;
349
- thinkingLevel?: ThinkingLevel;
377
+ thinkingLevel?: ConfiguredThinkingLevel;
350
378
  explicitThinkingLevel: boolean;
351
379
  }
352
380
  /** The set of resolvable role models plus the index of the currently active
@@ -530,11 +558,7 @@ export declare class AgentSession {
530
558
  * gap slips past the disposal guards.
531
559
  */
532
560
  beginDispose(): void;
533
- /**
534
- * Remove all listeners, flush pending writes, and disconnect from agent.
535
- * Call this when completely done with the session.
536
- */
537
- dispose(): Promise<void>;
561
+ dispose(options?: AgentSessionDisposeOptions): Promise<void>;
538
562
  freshSession(): FreshSessionResult | undefined;
539
563
  /** Full agent state */
540
564
  get state(): AgentState;
@@ -692,6 +716,7 @@ export declare class AgentSession {
692
716
  get clientBridge(): ClientBridge | undefined;
693
717
  setClientBridge(bridge: ClientBridge | undefined): void;
694
718
  getCheckpointState(): CheckpointState | undefined;
719
+ getLastCompletedRewind(): CompletedRewindState | undefined;
695
720
  setCheckpointState(state: CheckpointState | undefined): void;
696
721
  /**
697
722
  * Inject the plan mode context message into the conversation history.
@@ -812,6 +837,15 @@ export declare class AgentSession {
812
837
  get skillWarnings(): readonly SkillWarning[];
813
838
  getTodoPhases(): TodoPhase[];
814
839
  setTodoPhases(phases: TodoPhase[]): void;
840
+ /** Currently-applied {@link TITLE_SYSTEM.md} override, or undefined when the
841
+ * bundled prompt is in effect. Consumed by {@link InteractiveMode} so the
842
+ * first-input title path and the replan refresh share one source. */
843
+ get titleSystemPrompt(): string | undefined;
844
+ /** Replace the title-generation system prompt override. Called by
845
+ * {@link InteractiveMode.refreshTitleSystemPrompt} after the session cwd
846
+ * changes (e.g. `/move` relocation) so the next replan refresh resolves
847
+ * against the destination project's override. */
848
+ setTitleSystemPrompt(prompt: string | undefined): void;
815
849
  /**
816
850
  * Abort current operation and wait for agent to become idle.
817
851
  *
@@ -1112,14 +1146,13 @@ export declare class AgentSession {
1112
1146
  /** Whether there are pending Python messages waiting to be flushed */
1113
1147
  get hasPendingPythonMessages(): boolean;
1114
1148
  /**
1115
- * Surfaces (and consumes) IRC incoming asides that have reached this running
1116
- * session but have not yet been folded into the next model step.
1149
+ * Surfaces (and consumes) pending IRC incoming records before the next model
1150
+ * step can inject them automatically.
1117
1151
  *
1118
1152
  * The inbox tool injects the formatted body into the tool result, so the
1119
- * model sees it once via the result. Leaving the record in
1120
- * {@link #pendingIrcAsides} would let the aside provider deliver it a second
1121
- * time at the next step boundary — including on `peek`, which is why peek
1122
- * also drains here.
1153
+ * model sees it once via the result. Leaving the record in either pending
1154
+ * IRC queue would deliver it a second time at the next step boundary —
1155
+ * including on `peek`, which is why peek also drains here.
1123
1156
  */
1124
1157
  drainPendingIrcInboxMessages(agentId: string): IrcMessage[];
1125
1158
  /**
@@ -0,0 +1,48 @@
1
+ import type { SessionEntry } from "./session-entries";
2
+ export declare const TOOL_EXECUTION_START_CUSTOM_TYPE = "tool_execution_start";
3
+ export declare const SESSION_EXIT_CUSTOM_TYPE = "session_exit";
4
+ /**
5
+ * Compact projection of tool-call arguments persisted with the start marker.
6
+ * The assistant message already carries the full arguments; this exists only
7
+ * so `appendArgumentSummary` can name the command/path in resume warnings
8
+ * without duplicating whole argument payloads into the session JSONL.
9
+ */
10
+ export interface ToolArgumentSummary {
11
+ command?: string;
12
+ path?: string;
13
+ }
14
+ /** Persisted marker written before a tool implementation starts running. */
15
+ export interface ToolExecutionStartData {
16
+ toolCallId: string;
17
+ toolName: string;
18
+ args?: ToolArgumentSummary;
19
+ intent?: string;
20
+ startedAt: string;
21
+ }
22
+ /** Tool call left without a matching toolResult at the end of a branch. */
23
+ export interface PendingToolCallDiagnostic {
24
+ toolCallId?: string;
25
+ toolName: string;
26
+ args?: unknown;
27
+ intent?: string;
28
+ assistantTimestamp?: number;
29
+ startedAt?: string;
30
+ }
31
+ /** Session shutdown marker written during normal and fatal process teardown. */
32
+ export interface SessionExitData {
33
+ reason: string;
34
+ kind: "normal" | "signal" | "fatal" | "process_exit";
35
+ recordedAt: string;
36
+ pendingToolCalls?: PendingToolCallDiagnostic[];
37
+ }
38
+ /**
39
+ * Project full tool-call arguments down to the fields the pending-tool-call
40
+ * resume warning actually renders (`command`/`path`), truncated. Returns
41
+ * `undefined` when the arguments carry neither, so callers can omit `args`
42
+ * entirely instead of persisting an empty object.
43
+ */
44
+ export declare function summarizeToolArguments(args: unknown): ToolArgumentSummary | undefined;
45
+ /** Finds tool calls left pending at the end of a session branch. */
46
+ export declare function collectPendingToolCalls(entries: readonly SessionEntry[]): PendingToolCallDiagnostic[];
47
+ /** Builds the resume warning shown when a prior branch ended mid-tool-call. */
48
+ export declare function describePendingToolCalls(entries: readonly SessionEntry[]): string | undefined;
@@ -8,6 +8,11 @@ export declare function parseSessionContent(content: string): {
8
8
  entries: FileEntry[];
9
9
  titleSlot: SessionTitleUpdate | undefined;
10
10
  };
11
+ /** Exported for testing — the ≥8MiB streaming path (works on any file size). */
12
+ export declare function loadEntriesFromFileStream(filePath: string): Promise<{
13
+ entries: FileEntry[];
14
+ titleSlot: SessionTitleUpdate | undefined;
15
+ }>;
11
16
  /** Read only the fixed-size head window to detect a physical title slot. */
12
17
  export declare function readTitleSlotFromFile(filePath: string, storage?: SessionStorage): Promise<SessionTitleSlotEntry | undefined>;
13
18
  /** Exported for compaction.test.ts */
@@ -27,13 +27,15 @@ import { type AgentDefinition, type AgentProgress, type ReviewFinding, type Sing
27
27
  export type { YieldItem } from "./types";
28
28
  /**
29
29
  * Soft per-agent request budgets (assistant requests per run). When a subagent
30
- * crosses its budget it receives ONE steering notice asking it to wrap up; at
31
- * 1.5x the budget the run is aborted gracefully so partial output is salvaged.
32
- * The `default` key applies to agents without an explicit entry and can be
33
- * overridden via the `task.softRequestBudget` setting (0 disables the guard).
30
+ * crosses its budget it can receive an optional steering notice asking it to
31
+ * wrap up; at 1.5x the budget the run is aborted gracefully so partial output is
32
+ * salvaged. The `default` key applies to agents without an explicit entry and
33
+ * can be overridden via the `task.softRequestBudget` setting (0 disables the
34
+ * guard). The notice is off by default and controlled separately by
35
+ * `task.softRequestBudgetNotice`.
34
36
  */
35
37
  export declare const SOFT_REQUEST_BUDGET: Record<string, number>;
36
- /** Steering notice injected once when a subagent crosses its soft request budget. */
38
+ /** Optional steering notice injected when a subagent crosses its soft request budget. */
37
39
  export declare function buildBudgetNotice(requests: number): string;
38
40
  /** Options for subagent execution */
39
41
  export interface ExecutorOptions {
@@ -74,6 +76,11 @@ export interface ExecutorOptions {
74
76
  parentActiveModelPattern?: string;
75
77
  thinkingLevel?: ThinkingLevel;
76
78
  outputSchema?: unknown;
79
+ /**
80
+ * Caller supplied a schema that supersedes the agent's native output prompt.
81
+ * Eval `agent(..., schema=...)` sets this so built-in agents ignore stale yield labels.
82
+ */
83
+ outputSchemaOverridesAgent?: boolean;
77
84
  /** Parent task recursion depth (0 = top-level, 1 = first child, etc.) */
78
85
  taskDepth?: number;
79
86
  /**
@@ -30,6 +30,7 @@ export declare function mapWithConcurrencyLimit<T, R>(items: T[], concurrency: n
30
30
  * immediately — matching `task.maxConcurrency = 0`'s "Unlimited" semantics in the
31
31
  * settings UI ([#3305](https://github.com/can1357/oh-my-pi/issues/3305)).
32
32
  */
33
+ export declare function normalizeConcurrencyLimit(max: number): number;
33
34
  export declare class Semaphore {
34
35
  #private;
35
36
  constructor(max: number);