@oh-my-pi/pi-coding-agent 15.10.0 → 15.10.2

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 (238) hide show
  1. package/CHANGELOG.md +142 -1
  2. package/dist/types/cli/dry-balance-cli.d.ts +15 -1
  3. package/dist/types/cli/startup-cwd.d.ts +2 -0
  4. package/dist/types/commands/launch.d.ts +3 -0
  5. package/dist/types/commit/analysis/conventional.d.ts +2 -2
  6. package/dist/types/commit/analysis/summary.d.ts +2 -2
  7. package/dist/types/commit/changelog/generate.d.ts +2 -2
  8. package/dist/types/commit/changelog/index.d.ts +2 -2
  9. package/dist/types/commit/map-reduce/index.d.ts +3 -3
  10. package/dist/types/commit/map-reduce/map-phase.d.ts +2 -2
  11. package/dist/types/commit/map-reduce/reduce-phase.d.ts +2 -2
  12. package/dist/types/commit/model-selection.d.ts +10 -4
  13. package/dist/types/config/api-key-resolver.d.ts +34 -0
  14. package/dist/types/config/keybindings.d.ts +2 -2
  15. package/dist/types/config/model-provider-priority.d.ts +1 -0
  16. package/dist/types/config/model-registry.d.ts +17 -1
  17. package/dist/types/config/model-resolver.d.ts +4 -1
  18. package/dist/types/config/settings-schema.d.ts +9 -0
  19. package/dist/types/config/settings.d.ts +7 -2
  20. package/dist/types/dap/config.d.ts +14 -1
  21. package/dist/types/dap/types.d.ts +10 -0
  22. package/dist/types/debug/report-bundle.d.ts +3 -0
  23. package/dist/types/edit/file-snapshot-store.d.ts +18 -10
  24. package/dist/types/eval/py/__tests__/prelude.test.d.ts +1 -0
  25. package/dist/types/extensibility/extensions/types.d.ts +4 -1
  26. package/dist/types/lsp/client.d.ts +10 -0
  27. package/dist/types/lsp/utils.d.ts +3 -2
  28. package/dist/types/main.d.ts +3 -9
  29. package/dist/types/mcp/tool-bridge.d.ts +2 -0
  30. package/dist/types/modes/components/chat-block.d.ts +64 -0
  31. package/dist/types/modes/components/custom-editor.d.ts +4 -1
  32. package/dist/types/modes/components/overlay-box.d.ts +17 -0
  33. package/dist/types/modes/components/plan-review-overlay.d.ts +59 -0
  34. package/dist/types/modes/components/plan-toc.d.ts +41 -0
  35. package/dist/types/modes/components/read-tool-group.d.ts +2 -0
  36. package/dist/types/modes/components/status-line.d.ts +2 -0
  37. package/dist/types/modes/components/transcript-container.d.ts +11 -0
  38. package/dist/types/modes/controllers/command-controller.d.ts +1 -0
  39. package/dist/types/modes/controllers/event-controller.d.ts +17 -1
  40. package/dist/types/modes/controllers/extension-ui-controller.d.ts +0 -1
  41. package/dist/types/modes/controllers/input-controller.d.ts +1 -1
  42. package/dist/types/modes/controllers/streaming-reveal.d.ts +22 -0
  43. package/dist/types/modes/controllers/tan-command-controller.d.ts +6 -0
  44. package/dist/types/modes/interactive-mode.d.ts +16 -5
  45. package/dist/types/modes/magic-keywords.d.ts +1 -1
  46. package/dist/types/modes/markdown-prose.d.ts +1 -1
  47. package/dist/types/modes/theme/theme.d.ts +1 -1
  48. package/dist/types/modes/types.d.ts +21 -5
  49. package/dist/types/modes/utils/copy-targets.d.ts +21 -1
  50. package/dist/types/modes/workflow.d.ts +3 -3
  51. package/dist/types/plan-mode/approved-plan.d.ts +27 -8
  52. package/dist/types/plan-mode/plan-protection.d.ts +4 -4
  53. package/dist/types/sdk.d.ts +2 -0
  54. package/dist/types/session/agent-session.d.ts +21 -0
  55. package/dist/types/session/auth-storage.d.ts +1 -1
  56. package/dist/types/session/messages.d.ts +12 -0
  57. package/dist/types/session/session-manager.d.ts +8 -3
  58. package/dist/types/slash-commands/types.d.ts +4 -6
  59. package/dist/types/task/executor.d.ts +17 -0
  60. package/dist/types/task/index.d.ts +1 -0
  61. package/dist/types/task/render.d.ts +3 -2
  62. package/dist/types/tools/archive-reader.d.ts +5 -0
  63. package/dist/types/tools/ast-edit.d.ts +3 -0
  64. package/dist/types/tools/ast-grep.d.ts +3 -0
  65. package/dist/types/tools/bash.d.ts +1 -0
  66. package/dist/types/tools/eval.d.ts +8 -0
  67. package/dist/types/tools/find.d.ts +8 -4
  68. package/dist/types/tools/gh-cache-invalidation.d.ts +6 -0
  69. package/dist/types/tools/github-cache.d.ts +12 -0
  70. package/dist/types/tools/grouped-file-output.d.ts +95 -12
  71. package/dist/types/tools/memory-render.d.ts +4 -1
  72. package/dist/types/tools/path-utils.d.ts +8 -0
  73. package/dist/types/tools/plan-mode-guard.d.ts +8 -9
  74. package/dist/types/tools/render-utils.d.ts +5 -9
  75. package/dist/types/tools/search.d.ts +6 -2
  76. package/dist/types/tools/sqlite-reader.d.ts +1 -0
  77. package/dist/types/tools/todo.d.ts +3 -2
  78. package/dist/types/tools/write.d.ts +3 -0
  79. package/dist/types/tools/yield.d.ts +8 -0
  80. package/dist/types/tui/output-block.d.ts +16 -4
  81. package/dist/types/tui/status-line.d.ts +3 -0
  82. package/dist/types/utils/enhanced-paste.d.ts +20 -0
  83. package/dist/types/web/search/providers/kimi.d.ts +1 -1
  84. package/package.json +9 -9
  85. package/src/auto-thinking/classifier.ts +5 -1
  86. package/src/cli/args.ts +3 -1
  87. package/src/cli/dry-balance-cli.ts +54 -21
  88. package/src/cli/gallery-cli.ts +4 -1
  89. package/src/cli/gallery-fixtures/misc.ts +29 -0
  90. package/src/cli/startup-cwd.ts +68 -0
  91. package/src/commands/launch.ts +3 -0
  92. package/src/commit/analysis/conventional.ts +2 -2
  93. package/src/commit/analysis/summary.ts +2 -2
  94. package/src/commit/changelog/generate.ts +2 -2
  95. package/src/commit/changelog/index.ts +2 -2
  96. package/src/commit/map-reduce/index.ts +3 -3
  97. package/src/commit/map-reduce/map-phase.ts +2 -2
  98. package/src/commit/map-reduce/reduce-phase.ts +2 -2
  99. package/src/commit/model-selection.ts +36 -11
  100. package/src/commit/pipeline.ts +4 -4
  101. package/src/config/api-key-resolver.ts +58 -0
  102. package/src/config/model-provider-priority.ts +55 -0
  103. package/src/config/model-registry.ts +29 -24
  104. package/src/config/model-resolver.ts +39 -7
  105. package/src/config/settings-schema.ts +10 -0
  106. package/src/config/settings.ts +106 -43
  107. package/src/dap/config.ts +41 -2
  108. package/src/dap/defaults.json +1 -0
  109. package/src/dap/session.ts +1 -0
  110. package/src/dap/types.ts +10 -0
  111. package/src/debug/index.ts +47 -53
  112. package/src/debug/raw-sse-buffer.ts +7 -4
  113. package/src/debug/report-bundle.ts +9 -0
  114. package/src/edit/file-snapshot-store.ts +33 -1
  115. package/src/edit/hashline/filesystem.ts +2 -1
  116. package/src/edit/renderer.ts +82 -78
  117. package/src/eval/__tests__/llm-bridge.test.ts +110 -31
  118. package/src/eval/js/context-manager.ts +32 -15
  119. package/src/eval/llm-bridge.ts +22 -6
  120. package/src/eval/py/__tests__/prelude.test.ts +19 -0
  121. package/src/eval/py/executor.ts +23 -11
  122. package/src/eval/py/prelude.py +1 -1
  123. package/src/extensibility/extensions/types.ts +10 -1
  124. package/src/goals/tools/goal-tool.ts +36 -26
  125. package/src/internal-urls/docs-index.generated.ts +8 -8
  126. package/src/lsp/client.ts +23 -11
  127. package/src/lsp/config.ts +11 -1
  128. package/src/lsp/index.ts +61 -9
  129. package/src/lsp/utils.ts +3 -2
  130. package/src/main.ts +100 -72
  131. package/src/mcp/tool-bridge.ts +2 -0
  132. package/src/memories/index.ts +14 -7
  133. package/src/mnemopi/backend.ts +5 -1
  134. package/src/modes/acp/acp-agent.ts +33 -26
  135. package/src/modes/components/assistant-message.ts +2 -9
  136. package/src/modes/components/chat-block.ts +111 -0
  137. package/src/modes/components/copy-selector.ts +1 -44
  138. package/src/modes/components/custom-editor.ts +164 -109
  139. package/src/modes/components/custom-message.ts +1 -3
  140. package/src/modes/components/execution-shared.ts +1 -2
  141. package/src/modes/components/hook-message.ts +1 -3
  142. package/src/modes/components/model-selector.ts +59 -13
  143. package/src/modes/components/oauth-selector.ts +33 -7
  144. package/src/modes/components/overlay-box.ts +108 -0
  145. package/src/modes/components/plan-review-overlay.ts +799 -0
  146. package/src/modes/components/plan-toc.ts +138 -0
  147. package/src/modes/components/read-tool-group.ts +20 -4
  148. package/src/modes/components/skill-message.ts +0 -1
  149. package/src/modes/components/status-line.ts +19 -4
  150. package/src/modes/components/tips.txt +2 -1
  151. package/src/modes/components/todo-reminder.ts +0 -2
  152. package/src/modes/components/tool-execution.ts +68 -88
  153. package/src/modes/components/transcript-container.ts +84 -24
  154. package/src/modes/components/user-message.ts +2 -3
  155. package/src/modes/controllers/command-controller-shared.ts +7 -6
  156. package/src/modes/controllers/command-controller.ts +57 -55
  157. package/src/modes/controllers/event-controller.ts +67 -40
  158. package/src/modes/controllers/extension-ui-controller.ts +10 -73
  159. package/src/modes/controllers/input-controller.ts +170 -126
  160. package/src/modes/controllers/mcp-command-controller.ts +69 -60
  161. package/src/modes/controllers/selector-controller.ts +23 -25
  162. package/src/modes/controllers/streaming-reveal.ts +212 -0
  163. package/src/modes/controllers/tan-command-controller.ts +173 -0
  164. package/src/modes/interactive-mode.ts +274 -112
  165. package/src/modes/magic-keywords.ts +1 -1
  166. package/src/modes/markdown-prose.ts +1 -1
  167. package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
  168. package/src/modes/theme/shimmer.ts +20 -9
  169. package/src/modes/theme/theme-schema.json +1 -1
  170. package/src/modes/theme/theme.ts +8 -4
  171. package/src/modes/types.ts +21 -7
  172. package/src/modes/utils/copy-targets.ts +133 -27
  173. package/src/modes/utils/ui-helpers.ts +44 -46
  174. package/src/modes/workflow.ts +10 -10
  175. package/src/plan-mode/approved-plan.ts +66 -43
  176. package/src/plan-mode/plan-protection.ts +4 -4
  177. package/src/prompts/system/background-tan-dispatch.md +8 -0
  178. package/src/prompts/system/plan-mode-active.md +67 -58
  179. package/src/prompts/system/plan-mode-approved.md +1 -1
  180. package/src/prompts/system/workflow-notice.md +1 -1
  181. package/src/prompts/tools/bash.md +9 -0
  182. package/src/prompts/tools/browser.md +1 -1
  183. package/src/prompts/tools/eval.md +2 -1
  184. package/src/prompts/tools/read.md +2 -2
  185. package/src/sdk.ts +37 -46
  186. package/src/session/agent-session.ts +119 -18
  187. package/src/session/auth-storage.ts +2 -0
  188. package/src/session/messages.ts +26 -0
  189. package/src/session/session-manager.ts +109 -28
  190. package/src/slash-commands/builtin-registry.ts +36 -9
  191. package/src/slash-commands/types.ts +4 -6
  192. package/src/task/executor.ts +76 -38
  193. package/src/task/index.ts +4 -0
  194. package/src/task/render.ts +211 -147
  195. package/src/tools/archive-reader.ts +64 -0
  196. package/src/tools/ask.ts +119 -164
  197. package/src/tools/ast-edit.ts +98 -71
  198. package/src/tools/ast-grep.ts +37 -43
  199. package/src/tools/bash.ts +57 -6
  200. package/src/tools/browser/tab-supervisor.ts +13 -1
  201. package/src/tools/browser/tab-worker.ts +33 -4
  202. package/src/tools/debug.ts +20 -8
  203. package/src/tools/eval.ts +13 -2
  204. package/src/tools/fetch.ts +297 -7
  205. package/src/tools/find.ts +51 -30
  206. package/src/tools/gh-cache-invalidation.ts +200 -0
  207. package/src/tools/gh-renderer.ts +81 -42
  208. package/src/tools/github-cache.ts +25 -0
  209. package/src/tools/grouped-file-output.ts +272 -48
  210. package/src/tools/image-gen.ts +150 -103
  211. package/src/tools/inspect-image-renderer.ts +63 -41
  212. package/src/tools/inspect-image.ts +10 -3
  213. package/src/tools/job.ts +3 -4
  214. package/src/tools/memory-render.ts +4 -1
  215. package/src/tools/path-utils.ts +28 -2
  216. package/src/tools/plan-mode-guard.ts +66 -39
  217. package/src/tools/read.ts +48 -28
  218. package/src/tools/render-utils.ts +21 -37
  219. package/src/tools/resolve.ts +14 -0
  220. package/src/tools/search-tool-bm25.ts +36 -23
  221. package/src/tools/search.ts +118 -81
  222. package/src/tools/sqlite-reader.ts +9 -12
  223. package/src/tools/todo.ts +118 -52
  224. package/src/tools/write.ts +83 -64
  225. package/src/tools/yield.ts +10 -1
  226. package/src/tui/output-block.ts +60 -13
  227. package/src/tui/status-line.ts +5 -1
  228. package/src/utils/commit-message-generator.ts +11 -3
  229. package/src/utils/enhanced-paste.ts +230 -0
  230. package/src/utils/title-generator.ts +2 -1
  231. package/src/web/search/providers/anthropic.ts +25 -19
  232. package/src/web/search/providers/codex.ts +37 -8
  233. package/src/web/search/providers/exa.ts +11 -3
  234. package/src/web/search/providers/kimi.ts +28 -17
  235. package/src/web/search/providers/parallel.ts +35 -24
  236. package/src/web/search/providers/synthetic.ts +8 -6
  237. package/src/web/search/providers/tavily.ts +9 -8
  238. package/src/web/search/providers/zai.ts +8 -6
@@ -21,6 +21,7 @@ import {
21
21
  } from "../extensibility/plugins/marketplace";
22
22
  import { resolveMemoryBackend } from "../memory-backend";
23
23
  import type { InteractiveModeContext } from "../modes/types";
24
+ import type { FreshSessionResult } from "../session/agent-session";
24
25
  import { formatShakeSummary, type ShakeMode } from "../session/shake-types";
25
26
  import { getChangelogPath, parseChangelog } from "../utils/changelog";
26
27
  import { buildContextReportText } from "./helpers/context-report";
@@ -52,6 +53,11 @@ function refreshStatusLine(ctx: InteractiveModeContext): void {
52
53
  ctx.ui.requestRender();
53
54
  }
54
55
 
56
+ function formatFreshSessionResult(result: FreshSessionResult): string {
57
+ const stateLabel = result.closedProviderSessions === 1 ? "provider state" : "provider states";
58
+ return `Fresh provider session started (${result.closedProviderSessions} ${stateLabel} pruned).`;
59
+ }
60
+
55
61
  const shutdownHandlerTui = (_command: ParsedSlashCommand, runtime: TuiSlashCommandRuntime): SlashCommandResult => {
56
62
  runtime.ctx.editor.setText("");
57
63
  void runtime.ctx.shutdown();
@@ -770,6 +776,25 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
770
776
  await runtime.ctx.handleClearCommand();
771
777
  },
772
778
  },
779
+ {
780
+ name: "fresh",
781
+ description: "Reset provider stream state without changing the local transcript",
782
+ handle: async (_command, runtime) => {
783
+ const result = runtime.session.freshSession();
784
+ if (!result) {
785
+ await runtime.output(
786
+ "Wait for the current response to finish or abort it before refreshing provider state.",
787
+ );
788
+ return commandConsumed();
789
+ }
790
+ await runtime.output(formatFreshSessionResult(result));
791
+ return commandConsumed();
792
+ },
793
+ handleTui: async (_command, runtime) => {
794
+ runtime.ctx.editor.setText("");
795
+ await runtime.ctx.handleFreshCommand();
796
+ },
797
+ },
773
798
  {
774
799
  name: "drop",
775
800
  description: "Delete the current session and start a new one",
@@ -868,6 +893,17 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
868
893
  await runtime.ctx.handleBtwCommand(question);
869
894
  },
870
895
  },
896
+ {
897
+ name: "tan",
898
+ description: "Run a full background agent on tangential work",
899
+ inlineHint: "<work>",
900
+ allowArgs: true,
901
+ handleTui: async (command, runtime) => {
902
+ const work = command.text.slice(`/${command.name}`.length).trim();
903
+ runtime.ctx.editor.setText("");
904
+ await runtime.ctx.handleTanCommand(work);
905
+ },
906
+ },
871
907
  {
872
908
  name: "omfg",
873
909
  description: "Forge a TTSR rule from a complaint to stop a recurring behavior",
@@ -890,15 +926,6 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
890
926
  runtime.ctx.editor.setText("");
891
927
  },
892
928
  },
893
- {
894
- name: "background",
895
- aliases: ["bg"],
896
- description: "Detach UI and continue running in background",
897
- handleTui: (_command, runtime) => {
898
- runtime.ctx.editor.setText("");
899
- runtime.handleBackgroundCommand();
900
- },
901
- },
902
929
  {
903
930
  name: "debug",
904
931
  description: "Open debug tools selector",
@@ -71,15 +71,13 @@ export interface SlashCommandRuntime {
71
71
 
72
72
  /**
73
73
  * Runtime visible to TUI-only handlers (`handleTui`). Carries the interactive
74
- * mode context plus the background-detach hook. Intentionally narrower than
75
- * `SlashCommandRuntime` so existing callers can keep building it from just
76
- * `{ ctx, handleBackgroundCommand }`; when the TUI dispatcher needs to invoke
77
- * a `handle` (no `handleTui` override), it synthesizes a `SlashCommandRuntime`
78
- * from `ctx`.
74
+ * mode context. Intentionally narrower than `SlashCommandRuntime` so existing
75
+ * callers can keep building it from just `{ ctx }`; when the TUI dispatcher
76
+ * needs to invoke a `handle` (no `handleTui` override), it synthesizes a
77
+ * `SlashCommandRuntime` from `ctx`.
79
78
  */
80
79
  export interface TuiSlashCommandRuntime {
81
80
  ctx: InteractiveModeContext;
82
- handleBackgroundCommand: () => void;
83
81
  }
84
82
 
85
83
  /** Unified slash-command spec consumed by both TUI and ACP dispatchers. */
@@ -34,7 +34,11 @@ import { SessionManager } from "../session/session-manager";
34
34
  import { truncateTail } from "../session/streaming-output";
35
35
  import type { ContextFileEntry } from "../tools";
36
36
  import { normalizeSchema } from "../tools/jtd-to-json-schema";
37
- import { buildOutputValidator, summarizeValidationFailure } from "../tools/output-schema-validator";
37
+ import {
38
+ buildOutputValidator,
39
+ type OutputValidator,
40
+ summarizeValidationFailure,
41
+ } from "../tools/output-schema-validator";
38
42
 
39
43
  import { type ReportFindingDetails, toReviewFinding } from "../tools/review";
40
44
  import { ToolAbortError } from "../tools/tool-errors";
@@ -256,21 +260,40 @@ function extractCompletionData(parsed: unknown): unknown {
256
260
  return parsed;
257
261
  }
258
262
 
259
- function normalizeCompleteData(data: unknown, reportFindings?: ReviewFinding[]): unknown {
260
- let normalized = parseStringifiedJson(data ?? null);
263
+ /**
264
+ * Resolve the final yielded payload, optionally splicing collected
265
+ * `report_finding` entries into a top-level `findings` array.
266
+ *
267
+ * Injection is suppressed when an active validator would reject the augmented
268
+ * payload (e.g. a caller-supplied schema with `additionalProperties: false`
269
+ * that does not declare `findings`). That keeps the in-tool yield validator
270
+ * (which only sees the raw, pre-injection data) in lockstep with this
271
+ * post-mortem validator — honoring the "accepted in-tool ⇒ accepted
272
+ * post-mortem" guarantee documented in `output-schema-validator.ts`. The
273
+ * dropped findings are still preserved verbatim in the agent's progress
274
+ * stream and JSONL artifact, so no information is lost when injection is
275
+ * suppressed.
276
+ */
277
+ function normalizeCompleteData(
278
+ data: unknown,
279
+ reportFindings: ReviewFinding[] | undefined,
280
+ validator: OutputValidator | undefined,
281
+ ): unknown {
282
+ const normalized = parseStringifiedJson(data ?? null);
261
283
  if (
262
- Array.isArray(reportFindings) &&
263
- reportFindings.length > 0 &&
264
- normalized &&
265
- typeof normalized === "object" &&
266
- !Array.isArray(normalized)
284
+ !Array.isArray(reportFindings) ||
285
+ reportFindings.length === 0 ||
286
+ !normalized ||
287
+ typeof normalized !== "object" ||
288
+ Array.isArray(normalized)
267
289
  ) {
268
- const record = normalized as Record<string, unknown>;
269
- if (!("findings" in record)) {
270
- normalized = { ...record, findings: reportFindings };
271
- }
290
+ return normalized;
272
291
  }
273
- return normalized;
292
+ const record = normalized as Record<string, unknown>;
293
+ if ("findings" in record) return normalized;
294
+ const injected = { ...record, findings: reportFindings };
295
+ if (validator && !validator.validate(injected).success) return normalized;
296
+ return injected;
274
297
  }
275
298
 
276
299
  function resolveFallbackCompletion(rawOutput: string, outputSchema: unknown): { data: unknown } | null {
@@ -288,6 +311,15 @@ export interface YieldItem {
288
311
  data?: unknown;
289
312
  status?: "success" | "aborted";
290
313
  error?: string;
314
+ /**
315
+ * Set by the in-tool yield validator when it exhausted its retry budget
316
+ * (MAX_SCHEMA_RETRIES) and accepted a schema-invalid payload anyway.
317
+ * `finalizeSubprocessOutput` honors this by serializing the payload and
318
+ * surfacing a stderr warning, instead of re-emitting `schema_violation`
319
+ * — which would silently swap the subagent's "accepted" view for a
320
+ * different, opaque error blob in the parent's view of the result.
321
+ */
322
+ schemaOverridden?: boolean;
291
323
  }
292
324
 
293
325
  interface FinalizeSubprocessOutputArgs {
@@ -308,7 +340,8 @@ interface FinalizeSubprocessOutputResult {
308
340
  abortedViaYield: boolean;
309
341
  hasYield: boolean;
310
342
  }
311
-
343
+ export const SUBAGENT_WARNING_SCHEMA_OVERRIDDEN =
344
+ "SYSTEM WARNING: Subagent exhausted schema-retry budget; result was accepted despite failing the output schema.";
312
345
  export const SUBAGENT_WARNING_NULL_YIELD = "SYSTEM WARNING: Subagent called yield with null data.";
313
346
  export const SUBAGENT_WARNING_MISSING_YIELD =
314
347
  "SYSTEM WARNING: Subagent exited without calling yield tool after 3 reminders.";
@@ -360,30 +393,32 @@ export function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): Fi
360
393
  if (submitData === null || submitData === undefined) {
361
394
  rawOutput = rawOutput ? `${SUBAGENT_WARNING_NULL_YIELD}\n\n${rawOutput}` : SUBAGENT_WARNING_NULL_YIELD;
362
395
  } else {
363
- const completeData = normalizeCompleteData(submitData, reportFindings);
364
396
  const { validator, error: schemaError } = buildOutputValidator(outputSchema);
365
- if (schemaError) {
366
- rawOutput = `{"error":"schema_violation","message":"invalid output schema: ${schemaError.replace(/"/g, '\\"')}"}`;
367
- stderr = `schema_violation: invalid output schema: ${schemaError}`;
368
- exitCode = 1;
397
+ const overridden = lastYield?.schemaOverridden === true;
398
+ const completeData = normalizeCompleteData(submitData, reportFindings, validator);
399
+ const result =
400
+ schemaError || overridden
401
+ ? { success: true as const }
402
+ : (validator?.validate(completeData) ?? { success: true as const });
403
+ if (!result.success) {
404
+ const summary = summarizeValidationFailure(result, completeData, validator?.requiredFields ?? []);
405
+ const outcome = buildSchemaViolationOutcome(summary, completeData);
406
+ rawOutput = outcome.rawOutput;
407
+ stderr = outcome.stderr;
408
+ exitCode = outcome.exitCode;
369
409
  } else {
370
- const result = validator?.validate(completeData) ?? { success: true as const };
371
- if (!result.success) {
372
- const summary = summarizeValidationFailure(result, completeData, validator?.requiredFields ?? []);
373
- const outcome = buildSchemaViolationOutcome(summary, completeData);
374
- rawOutput = outcome.rawOutput;
375
- stderr = outcome.stderr;
376
- exitCode = outcome.exitCode;
377
- } else {
378
- try {
379
- rawOutput = JSON.stringify(completeData, null, 2) ?? "null";
380
- } catch (err) {
381
- const errorMessage = err instanceof Error ? err.message : String(err);
382
- rawOutput = `{"error":"Failed to serialize yield data: ${errorMessage}"}`;
383
- }
384
- exitCode = 0;
385
- stderr = "";
410
+ try {
411
+ rawOutput = JSON.stringify(completeData, null, 2) ?? "null";
412
+ } catch (err) {
413
+ const errorMessage = err instanceof Error ? err.message : String(err);
414
+ rawOutput = `{"error":"Failed to serialize yield data: ${errorMessage}"}`;
386
415
  }
416
+ exitCode = 0;
417
+ stderr = overridden
418
+ ? SUBAGENT_WARNING_SCHEMA_OVERRIDDEN
419
+ : schemaError
420
+ ? `invalid output schema: ${schemaError}`
421
+ : "";
387
422
  }
388
423
  }
389
424
  }
@@ -393,8 +428,8 @@ export function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): Fi
393
428
  const hasOutputSchema = normalizedSchema !== undefined && !schemaError;
394
429
  const fallback = allowFallback ? resolveFallbackCompletion(rawOutput, outputSchema) : null;
395
430
  if (fallback) {
396
- const completeData = normalizeCompleteData(fallback.data, reportFindings);
397
431
  const { validator } = buildOutputValidator(outputSchema);
432
+ const completeData = normalizeCompleteData(fallback.data, reportFindings, validator);
398
433
  const result = validator?.validate(completeData) ?? { success: true as const };
399
434
  if (!result.success) {
400
435
  const summary = summarizeValidationFailure(result, completeData, validator?.requiredFields ?? []);
@@ -488,7 +523,7 @@ function getUsageTokens(usage: unknown): number {
488
523
  /**
489
524
  * Create proxy tools that reuse the parent's MCP connections.
490
525
  */
491
- function createMCPProxyTools(mcpManager: MCPManager): CustomTool[] {
526
+ export function createMCPProxyTools(mcpManager: MCPManager): CustomTool[] {
492
527
  return mcpManager.getTools().map(tool => {
493
528
  const mcpTool = tool as { mcpToolName?: string; mcpServerName?: string };
494
529
  return {
@@ -538,7 +573,10 @@ function createMCPProxyTools(mcpManager: MCPManager): CustomTool[] {
538
573
  });
539
574
  }
540
575
 
541
- function createSubagentSettings(baseSettings: Settings, overrides?: Partial<Record<SettingPath, unknown>>): Settings {
576
+ export function createSubagentSettings(
577
+ baseSettings: Settings,
578
+ overrides?: Partial<Record<SettingPath, unknown>>,
579
+ ): Settings {
542
580
  const snapshot: Partial<Record<SettingPath, unknown>> = {};
543
581
  for (const key of Object.keys(SETTINGS_SCHEMA) as SettingPath[]) {
544
582
  snapshot[key] = baseSettings.get(key);
package/src/task/index.ts CHANGED
@@ -275,6 +275,10 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
275
275
  readonly strict = true;
276
276
  readonly loadMode = "discoverable";
277
277
  readonly renderResult = renderResult;
278
+ // Suppress the streaming call preview once a (partial or final) result exists
279
+ // so the task renders as ONE block that transitions in place — not a pending
280
+ // call frame stacked above the result frame. Mirrors `taskToolRenderer`.
281
+ readonly mergeCallAndResult = true;
278
282
  readonly #discoveredAgents: AgentDefinition[];
279
283
  readonly #blockedAgent: string | undefined;
280
284