@gajae-code/coding-agent 0.2.4 → 0.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 (266) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +1 -1
  3. package/dist/types/async/job-manager.d.ts +145 -2
  4. package/dist/types/commands/harness.d.ts +37 -0
  5. package/dist/types/config/settings-schema.d.ts +13 -3
  6. package/dist/types/config/settings.d.ts +3 -1
  7. package/dist/types/deep-interview/render-middleware.d.ts +5 -0
  8. package/dist/types/discovery/helpers.d.ts +1 -0
  9. package/dist/types/exec/bash-executor.d.ts +8 -1
  10. package/dist/types/extensibility/custom-tools/types.d.ts +1 -0
  11. package/dist/types/extensibility/extensions/types.d.ts +6 -0
  12. package/dist/types/extensibility/shared-events.d.ts +1 -0
  13. package/dist/types/gjc-runtime/restricted-role-agent-bash.d.ts +2 -0
  14. package/dist/types/gjc-runtime/state-graph.d.ts +4 -0
  15. package/dist/types/gjc-runtime/state-migrations.d.ts +24 -0
  16. package/dist/types/gjc-runtime/state-renderer.d.ts +65 -0
  17. package/dist/types/gjc-runtime/state-runtime.d.ts +2 -0
  18. package/dist/types/gjc-runtime/state-validation.d.ts +6 -0
  19. package/dist/types/gjc-runtime/state-writer.d.ts +137 -0
  20. package/dist/types/gjc-runtime/team-runtime.d.ts +81 -7
  21. package/dist/types/gjc-runtime/workflow-manifest.d.ts +54 -0
  22. package/dist/types/harness-control-plane/classifier.d.ts +13 -0
  23. package/dist/types/harness-control-plane/control-endpoint.d.ts +30 -0
  24. package/dist/types/harness-control-plane/finalize.d.ts +47 -0
  25. package/dist/types/harness-control-plane/frame-mapper.d.ts +29 -0
  26. package/dist/types/harness-control-plane/operate.d.ts +35 -0
  27. package/dist/types/harness-control-plane/owner.d.ts +46 -0
  28. package/dist/types/harness-control-plane/preserve.d.ts +19 -0
  29. package/dist/types/harness-control-plane/receipts.d.ts +88 -0
  30. package/dist/types/harness-control-plane/rpc-adapter.d.ts +66 -0
  31. package/dist/types/harness-control-plane/seams.d.ts +21 -0
  32. package/dist/types/harness-control-plane/session-lease.d.ts +65 -0
  33. package/dist/types/harness-control-plane/state-machine.d.ts +19 -0
  34. package/dist/types/harness-control-plane/storage.d.ts +53 -0
  35. package/dist/types/harness-control-plane/types.d.ts +162 -0
  36. package/dist/types/hooks/skill-keywords.d.ts +2 -1
  37. package/dist/types/hooks/skill-state.d.ts +2 -29
  38. package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
  39. package/dist/types/modes/components/hook-selector.d.ts +1 -0
  40. package/dist/types/modes/components/skill-hud/render.d.ts +1 -1
  41. package/dist/types/modes/interactive-mode.d.ts +2 -0
  42. package/dist/types/modes/theme/defaults/index.d.ts +45 -9477
  43. package/dist/types/modes/theme/theme.d.ts +1 -5
  44. package/dist/types/modes/types.d.ts +2 -0
  45. package/dist/types/sdk.d.ts +4 -0
  46. package/dist/types/session/agent-session.d.ts +8 -0
  47. package/dist/types/session/streaming-output.d.ts +11 -0
  48. package/dist/types/skill-state/active-state.d.ts +3 -0
  49. package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +1 -1
  50. package/dist/types/skill-state/workflow-state-contract.d.ts +24 -0
  51. package/dist/types/task/executor.d.ts +3 -0
  52. package/dist/types/task/types.d.ts +56 -3
  53. package/dist/types/tools/bash-allowed-prefixes.d.ts +5 -0
  54. package/dist/types/tools/bash.d.ts +24 -0
  55. package/dist/types/tools/cron.d.ts +110 -0
  56. package/dist/types/tools/index.d.ts +4 -0
  57. package/dist/types/tools/monitor.d.ts +54 -0
  58. package/dist/types/tools/subagent.d.ts +11 -1
  59. package/dist/types/web/search/index.d.ts +1 -0
  60. package/dist/types/web/search/provider.d.ts +11 -4
  61. package/dist/types/web/search/providers/duckduckgo.d.ts +57 -0
  62. package/dist/types/web/search/types.d.ts +1 -1
  63. package/package.json +7 -7
  64. package/src/async/job-manager.ts +522 -6
  65. package/src/cli/agents-cli.ts +3 -0
  66. package/src/cli/auth-broker-cli.ts +1 -0
  67. package/src/cli/config-cli.ts +10 -2
  68. package/src/cli.ts +2 -0
  69. package/src/commands/harness.ts +592 -0
  70. package/src/commands/team.ts +36 -39
  71. package/src/config/settings-schema.ts +15 -2
  72. package/src/config/settings.ts +49 -7
  73. package/src/deep-interview/render-middleware.ts +366 -0
  74. package/src/defaults/gjc/skills/deep-interview/SKILL.md +9 -2
  75. package/src/defaults/gjc/skills/ralplan/SKILL.md +8 -4
  76. package/src/defaults/gjc/skills/team/SKILL.md +47 -21
  77. package/src/defaults/gjc/skills/ultragoal/SKILL.md +78 -11
  78. package/src/discovery/helpers.ts +5 -0
  79. package/src/eval/js/shared/rewrite-imports.ts +1 -2
  80. package/src/exec/bash-executor.ts +20 -9
  81. package/src/extensibility/custom-tools/types.ts +1 -0
  82. package/src/extensibility/extensions/types.ts +6 -0
  83. package/src/extensibility/shared-events.ts +1 -0
  84. package/src/gjc-runtime/deep-interview-runtime.ts +40 -21
  85. package/src/gjc-runtime/goal-mode-request.ts +11 -3
  86. package/src/gjc-runtime/ralplan-runtime.ts +27 -10
  87. package/src/gjc-runtime/restricted-role-agent-bash.ts +5 -0
  88. package/src/gjc-runtime/state-graph.ts +86 -0
  89. package/src/gjc-runtime/state-migrations.ts +132 -0
  90. package/src/gjc-runtime/state-renderer.ts +345 -0
  91. package/src/gjc-runtime/state-runtime.ts +733 -21
  92. package/src/gjc-runtime/state-validation.ts +49 -0
  93. package/src/gjc-runtime/state-writer.ts +718 -0
  94. package/src/gjc-runtime/team-runtime.ts +1083 -89
  95. package/src/gjc-runtime/ultragoal-runtime.ts +348 -19
  96. package/src/gjc-runtime/workflow-manifest.generated.json +1497 -0
  97. package/src/gjc-runtime/workflow-manifest.ts +425 -0
  98. package/src/harness-control-plane/classifier.ts +128 -0
  99. package/src/harness-control-plane/control-endpoint.ts +137 -0
  100. package/src/harness-control-plane/finalize.ts +222 -0
  101. package/src/harness-control-plane/frame-mapper.ts +286 -0
  102. package/src/harness-control-plane/operate.ts +225 -0
  103. package/src/harness-control-plane/owner.ts +553 -0
  104. package/src/harness-control-plane/preserve.ts +102 -0
  105. package/src/harness-control-plane/receipts.ts +216 -0
  106. package/src/harness-control-plane/rpc-adapter.ts +276 -0
  107. package/src/harness-control-plane/seams.ts +39 -0
  108. package/src/harness-control-plane/session-lease.ts +388 -0
  109. package/src/harness-control-plane/state-machine.ts +97 -0
  110. package/src/harness-control-plane/storage.ts +257 -0
  111. package/src/harness-control-plane/types.ts +214 -0
  112. package/src/hooks/skill-keywords.ts +4 -2
  113. package/src/hooks/skill-state.ts +25 -42
  114. package/src/internal-urls/docs-index.generated.ts +6 -4
  115. package/src/lsp/render.ts +1 -1
  116. package/src/modes/acp/acp-agent.ts +1 -1
  117. package/src/modes/acp/acp-client-bridge.ts +1 -1
  118. package/src/modes/components/agent-dashboard.ts +1 -1
  119. package/src/modes/components/assistant-message.ts +5 -1
  120. package/src/modes/components/diff.ts +2 -2
  121. package/src/modes/components/hook-selector.ts +72 -2
  122. package/src/modes/components/skill-hud/render.ts +7 -2
  123. package/src/modes/controllers/event-controller.ts +71 -6
  124. package/src/modes/controllers/extension-ui-controller.ts +6 -0
  125. package/src/modes/controllers/input-controller.ts +19 -3
  126. package/src/modes/controllers/selector-controller.ts +3 -2
  127. package/src/modes/interactive-mode.ts +21 -2
  128. package/src/modes/theme/defaults/index.ts +0 -196
  129. package/src/modes/theme/theme.ts +35 -35
  130. package/src/modes/types.ts +2 -0
  131. package/src/prompts/agents/architect.md +5 -1
  132. package/src/prompts/agents/critic.md +5 -1
  133. package/src/prompts/agents/executor.md +13 -0
  134. package/src/prompts/agents/frontmatter.md +1 -0
  135. package/src/prompts/agents/planner.md +5 -1
  136. package/src/prompts/tools/bash.md +9 -0
  137. package/src/prompts/tools/cron.md +25 -0
  138. package/src/prompts/tools/monitor.md +30 -0
  139. package/src/prompts/tools/subagent.md +33 -3
  140. package/src/runtime-mcp/oauth-flow.ts +4 -2
  141. package/src/sdk.ts +7 -0
  142. package/src/session/agent-session.ts +247 -38
  143. package/src/session/session-manager.ts +13 -1
  144. package/src/session/streaming-output.ts +21 -0
  145. package/src/skill-state/active-state.ts +222 -78
  146. package/src/skill-state/deep-interview-mutation-guard.ts +91 -13
  147. package/src/skill-state/initial-phase.ts +2 -0
  148. package/src/skill-state/workflow-state-contract.ts +26 -0
  149. package/src/task/agents.ts +1 -0
  150. package/src/task/executor.ts +51 -8
  151. package/src/task/index.ts +120 -8
  152. package/src/task/render.ts +6 -3
  153. package/src/task/types.ts +57 -3
  154. package/src/tools/ask.ts +28 -7
  155. package/src/tools/bash-allowed-prefixes.ts +169 -0
  156. package/src/tools/bash.ts +190 -29
  157. package/src/tools/browser/tab-worker.ts +1 -1
  158. package/src/tools/cron.ts +665 -0
  159. package/src/tools/index.ts +20 -2
  160. package/src/tools/monitor.ts +136 -0
  161. package/src/tools/subagent.ts +255 -64
  162. package/src/vim/engine.ts +3 -3
  163. package/src/web/search/index.ts +31 -18
  164. package/src/web/search/provider.ts +57 -12
  165. package/src/web/search/providers/duckduckgo.ts +279 -0
  166. package/src/web/search/types.ts +2 -0
  167. package/src/modes/theme/dark.json +0 -95
  168. package/src/modes/theme/defaults/alabaster.json +0 -93
  169. package/src/modes/theme/defaults/amethyst.json +0 -96
  170. package/src/modes/theme/defaults/anthracite.json +0 -93
  171. package/src/modes/theme/defaults/basalt.json +0 -91
  172. package/src/modes/theme/defaults/birch.json +0 -95
  173. package/src/modes/theme/defaults/dark-abyss.json +0 -91
  174. package/src/modes/theme/defaults/dark-arctic.json +0 -104
  175. package/src/modes/theme/defaults/dark-aurora.json +0 -95
  176. package/src/modes/theme/defaults/dark-catppuccin.json +0 -107
  177. package/src/modes/theme/defaults/dark-cavern.json +0 -91
  178. package/src/modes/theme/defaults/dark-copper.json +0 -95
  179. package/src/modes/theme/defaults/dark-cosmos.json +0 -90
  180. package/src/modes/theme/defaults/dark-cyberpunk.json +0 -102
  181. package/src/modes/theme/defaults/dark-dracula.json +0 -98
  182. package/src/modes/theme/defaults/dark-eclipse.json +0 -91
  183. package/src/modes/theme/defaults/dark-ember.json +0 -95
  184. package/src/modes/theme/defaults/dark-equinox.json +0 -90
  185. package/src/modes/theme/defaults/dark-forest.json +0 -96
  186. package/src/modes/theme/defaults/dark-github.json +0 -105
  187. package/src/modes/theme/defaults/dark-gruvbox.json +0 -112
  188. package/src/modes/theme/defaults/dark-lavender.json +0 -95
  189. package/src/modes/theme/defaults/dark-lunar.json +0 -89
  190. package/src/modes/theme/defaults/dark-midnight.json +0 -95
  191. package/src/modes/theme/defaults/dark-monochrome.json +0 -94
  192. package/src/modes/theme/defaults/dark-monokai.json +0 -98
  193. package/src/modes/theme/defaults/dark-nebula.json +0 -90
  194. package/src/modes/theme/defaults/dark-nord.json +0 -97
  195. package/src/modes/theme/defaults/dark-ocean.json +0 -101
  196. package/src/modes/theme/defaults/dark-one.json +0 -100
  197. package/src/modes/theme/defaults/dark-poimandres.json +0 -141
  198. package/src/modes/theme/defaults/dark-rainforest.json +0 -91
  199. package/src/modes/theme/defaults/dark-reef.json +0 -91
  200. package/src/modes/theme/defaults/dark-retro.json +0 -92
  201. package/src/modes/theme/defaults/dark-rose-pine.json +0 -96
  202. package/src/modes/theme/defaults/dark-sakura.json +0 -95
  203. package/src/modes/theme/defaults/dark-slate.json +0 -95
  204. package/src/modes/theme/defaults/dark-solarized.json +0 -97
  205. package/src/modes/theme/defaults/dark-solstice.json +0 -90
  206. package/src/modes/theme/defaults/dark-starfall.json +0 -91
  207. package/src/modes/theme/defaults/dark-sunset.json +0 -99
  208. package/src/modes/theme/defaults/dark-swamp.json +0 -90
  209. package/src/modes/theme/defaults/dark-synthwave.json +0 -103
  210. package/src/modes/theme/defaults/dark-taiga.json +0 -91
  211. package/src/modes/theme/defaults/dark-terminal.json +0 -95
  212. package/src/modes/theme/defaults/dark-tokyo-night.json +0 -101
  213. package/src/modes/theme/defaults/dark-tundra.json +0 -91
  214. package/src/modes/theme/defaults/dark-twilight.json +0 -91
  215. package/src/modes/theme/defaults/dark-volcanic.json +0 -91
  216. package/src/modes/theme/defaults/graphite.json +0 -92
  217. package/src/modes/theme/defaults/light-arctic.json +0 -107
  218. package/src/modes/theme/defaults/light-aurora-day.json +0 -91
  219. package/src/modes/theme/defaults/light-canyon.json +0 -91
  220. package/src/modes/theme/defaults/light-catppuccin.json +0 -106
  221. package/src/modes/theme/defaults/light-cirrus.json +0 -90
  222. package/src/modes/theme/defaults/light-coral.json +0 -95
  223. package/src/modes/theme/defaults/light-cyberpunk.json +0 -96
  224. package/src/modes/theme/defaults/light-dawn.json +0 -90
  225. package/src/modes/theme/defaults/light-dunes.json +0 -91
  226. package/src/modes/theme/defaults/light-eucalyptus.json +0 -95
  227. package/src/modes/theme/defaults/light-forest.json +0 -100
  228. package/src/modes/theme/defaults/light-frost.json +0 -95
  229. package/src/modes/theme/defaults/light-github.json +0 -115
  230. package/src/modes/theme/defaults/light-glacier.json +0 -91
  231. package/src/modes/theme/defaults/light-gruvbox.json +0 -108
  232. package/src/modes/theme/defaults/light-haze.json +0 -90
  233. package/src/modes/theme/defaults/light-honeycomb.json +0 -95
  234. package/src/modes/theme/defaults/light-lagoon.json +0 -91
  235. package/src/modes/theme/defaults/light-lavender.json +0 -95
  236. package/src/modes/theme/defaults/light-meadow.json +0 -91
  237. package/src/modes/theme/defaults/light-mint.json +0 -95
  238. package/src/modes/theme/defaults/light-monochrome.json +0 -101
  239. package/src/modes/theme/defaults/light-ocean.json +0 -99
  240. package/src/modes/theme/defaults/light-one.json +0 -99
  241. package/src/modes/theme/defaults/light-opal.json +0 -91
  242. package/src/modes/theme/defaults/light-orchard.json +0 -91
  243. package/src/modes/theme/defaults/light-paper.json +0 -95
  244. package/src/modes/theme/defaults/light-poimandres.json +0 -141
  245. package/src/modes/theme/defaults/light-prism.json +0 -90
  246. package/src/modes/theme/defaults/light-retro.json +0 -98
  247. package/src/modes/theme/defaults/light-sand.json +0 -95
  248. package/src/modes/theme/defaults/light-savanna.json +0 -91
  249. package/src/modes/theme/defaults/light-solarized.json +0 -102
  250. package/src/modes/theme/defaults/light-soleil.json +0 -90
  251. package/src/modes/theme/defaults/light-sunset.json +0 -99
  252. package/src/modes/theme/defaults/light-synthwave.json +0 -98
  253. package/src/modes/theme/defaults/light-tokyo-night.json +0 -111
  254. package/src/modes/theme/defaults/light-wetland.json +0 -91
  255. package/src/modes/theme/defaults/light-zenith.json +0 -89
  256. package/src/modes/theme/defaults/limestone.json +0 -94
  257. package/src/modes/theme/defaults/mahogany.json +0 -97
  258. package/src/modes/theme/defaults/marble.json +0 -93
  259. package/src/modes/theme/defaults/obsidian.json +0 -91
  260. package/src/modes/theme/defaults/onyx.json +0 -91
  261. package/src/modes/theme/defaults/pearl.json +0 -93
  262. package/src/modes/theme/defaults/porcelain.json +0 -91
  263. package/src/modes/theme/defaults/quartz.json +0 -96
  264. package/src/modes/theme/defaults/sandstone.json +0 -95
  265. package/src/modes/theme/defaults/titanium.json +0 -90
  266. package/src/modes/theme/light.json +0 -93
@@ -9,6 +9,7 @@ import type { AgentEvent, AgentIdentity, AgentTelemetryConfig, ThinkingLevel } f
9
9
  import { recordHandoff, resolveTelemetry } from "@gajae-code/agent-core";
10
10
  import { type JsonSchemaValidationIssue, validateJsonSchemaValue } from "@gajae-code/ai/utils/schema";
11
11
  import { logger, prompt, untilAborted } from "@gajae-code/utils";
12
+ import { AsyncJobManager } from "../async";
12
13
  import { ModelRegistry } from "../config/model-registry";
13
14
  import { resolveModelOverrideWithAuthFallback } from "../config/model-resolver";
14
15
  import type { PromptTemplate } from "../config/prompt-templates";
@@ -112,6 +113,9 @@ export interface ExecutorOptions {
112
113
  index: number;
113
114
  id: string;
114
115
  modelOverride?: string | string[];
116
+ runMode?: "initial" | "resume" | "message";
117
+ resumeMessage?: string;
118
+ subagentId?: string;
115
119
  /**
116
120
  * Active model selector of the parent session, used as an auth-aware fallback
117
121
  * if the resolved subagent model has no working credentials. See #985.
@@ -550,8 +554,8 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
550
554
  }
551
555
 
552
556
  // Set up artifact paths and write input file upfront if artifacts dir provided
553
- let subtaskSessionFile: string | undefined;
554
- if (options.artifactsDir) {
557
+ let subtaskSessionFile: string | undefined = options.sessionFile ?? undefined;
558
+ if (!subtaskSessionFile && options.artifactsDir) {
555
559
  subtaskSessionFile = path.join(options.artifactsDir, `${id}.jsonl`);
556
560
  }
557
561
 
@@ -617,6 +621,8 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
617
621
  let activeSession: AgentSession | null = null;
618
622
  let unsubscribe: (() => void) | null = null;
619
623
  let yieldCalled = false;
624
+ let pauseRequested = false;
625
+ let paused = false;
620
626
 
621
627
  // Accumulate usage incrementally from message_end events (no memory for streaming events)
622
628
  const accumulatedUsage = {
@@ -1006,6 +1012,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1006
1012
  }
1007
1013
  }
1008
1014
  }
1015
+ paused = (event as { stopReason?: string }).stopReason === "paused";
1009
1016
  flushProgress = true;
1010
1017
  break;
1011
1018
  }
@@ -1186,16 +1193,34 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1186
1193
  parentTaskPrefix: id,
1187
1194
  agentId: id,
1188
1195
  agentDisplayName: agent.name,
1196
+ bashAllowedPrefixes: agent.bashAllowedPrefixes,
1189
1197
  enableLsp: lspEnabled,
1190
1198
  skipPythonPreflight,
1191
1199
  enableMCP,
1192
1200
  localProtocolOptions: options.localProtocolOptions,
1193
1201
  telemetry: subagentTelemetry,
1194
1202
  forkContextSeed: options.forkContextSeed,
1203
+ shouldPause: () => pauseRequested,
1195
1204
  }),
1196
1205
  );
1197
1206
 
1198
1207
  activeSession = session;
1208
+ const liveSubagentId = options.subagentId ?? id;
1209
+ const manager = AsyncJobManager.instance();
1210
+ if (manager) {
1211
+ manager.registerLiveHandle(liveSubagentId, {
1212
+ requestPause: () => {
1213
+ pauseRequested = true;
1214
+ },
1215
+ injectMessage: async (content, deliverAs) => {
1216
+ if (deliverAs === "nextTurn") {
1217
+ await session.prompt(content, { attribution: "agent" });
1218
+ return;
1219
+ }
1220
+ await session.sendUserMessage(content, { deliverAs });
1221
+ },
1222
+ });
1223
+ }
1199
1224
 
1200
1225
  // Emit lifecycle start event
1201
1226
  if (options.eventBus) {
@@ -1305,6 +1330,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1305
1330
  progress.retryState = {
1306
1331
  attempt: event.attempt,
1307
1332
  maxAttempts: event.maxAttempts,
1333
+ unbounded: event.unbounded,
1308
1334
  delayMs: event.delayMs,
1309
1335
  errorMessage: event.errorMessage,
1310
1336
  startedAtMs: Date.now(),
@@ -1353,13 +1379,24 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1353
1379
  );
1354
1380
  }
1355
1381
  }
1356
- await awaitAbortable(session.prompt(task, { attribution: "agent" }));
1357
- await awaitAbortable(session.waitForIdle());
1382
+ const runMode = options.runMode ?? "initial";
1383
+ if (runMode === "message") {
1384
+ await awaitAbortable(session.prompt(options.resumeMessage ?? "", { attribution: "agent" }));
1385
+ await awaitAbortable(session.waitForIdle());
1386
+ } else if (runMode === "resume") {
1387
+ await awaitAbortable(
1388
+ session.prompt("Continue from the paused subagent session state.", { attribution: "agent" }),
1389
+ );
1390
+ await awaitAbortable(session.waitForIdle());
1391
+ } else {
1392
+ await awaitAbortable(session.prompt(task, { attribution: "agent" }));
1393
+ await awaitAbortable(session.waitForIdle());
1394
+ }
1358
1395
 
1359
1396
  const reminderToolChoice = buildNamedToolChoice("yield", session.model);
1360
1397
 
1361
1398
  let retryCount = 0;
1362
- while (!yieldCalled && retryCount < MAX_YIELD_RETRIES && !abortSignal.aborted) {
1399
+ while (!paused && !yieldCalled && retryCount < MAX_YIELD_RETRIES && !abortSignal.aborted) {
1363
1400
  // Skip reminders when the model returned a terminal error (e.g.
1364
1401
  // rate-limit cap hit, auth failure). Re-prompting would just
1365
1402
  // hit the same wall, multiplying the failure noise without
@@ -1389,7 +1426,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1389
1426
  }
1390
1427
 
1391
1428
  await awaitAbortable(session.waitForIdle());
1392
- if (!yieldCalled && !abortSignal.aborted) {
1429
+ if (!paused && !yieldCalled && !abortSignal.aborted) {
1393
1430
  exitCode = 0;
1394
1431
  }
1395
1432
 
@@ -1405,6 +1442,10 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1405
1442
  exitCode = 1;
1406
1443
  error ??= lastAssistant.errorMessage || "Subagent failed";
1407
1444
  }
1445
+ if (paused) {
1446
+ exitCode = 0;
1447
+ error = undefined;
1448
+ }
1408
1449
  }
1409
1450
  } catch (err) {
1410
1451
  exitCode = 1;
@@ -1420,6 +1461,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1420
1461
  if (exitCode === 0) exitCode = 1;
1421
1462
  }
1422
1463
  sessionAbortController.abort();
1464
+ AsyncJobManager.instance()?.removeLiveHandle(options.subagentId ?? id);
1423
1465
  if (unsubscribe) {
1424
1466
  try {
1425
1467
  unsubscribe();
@@ -1526,7 +1568,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1526
1568
  ? yieldAbortReason
1527
1569
  : (done.abortReason ?? (signal?.aborted ? resolveSignalAbortReason() : resolveAbortReasonText()))
1528
1570
  : undefined;
1529
- progress.status = wasAborted ? "aborted" : exitCode === 0 ? "completed" : "failed";
1571
+ progress.status = paused ? "paused" : wasAborted ? "aborted" : exitCode === 0 ? "completed" : "failed";
1530
1572
  scheduleProgress(true);
1531
1573
 
1532
1574
  // Emit lifecycle end event after finalization so yield status is reflected
@@ -1536,7 +1578,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1536
1578
  agent: agent.name,
1537
1579
  agentSource: agent.source,
1538
1580
  description: options.description,
1539
- status: progress.status as "completed" | "failed" | "aborted",
1581
+ status: progress.status as "completed" | "failed" | "aborted" | "paused",
1540
1582
  sessionFile: subtaskSessionFile,
1541
1583
  index,
1542
1584
  });
@@ -1563,6 +1605,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1563
1605
  error: exitCode !== 0 && stderr ? stderr : undefined,
1564
1606
  aborted: wasAborted,
1565
1607
  abortReason: finalAbortReason,
1608
+ paused,
1566
1609
  usage: hasUsage ? accumulatedUsage : undefined,
1567
1610
  outputPath,
1568
1611
  extractedToolData: progress.extractedToolData,
package/src/task/index.ts CHANGED
@@ -65,6 +65,18 @@ import {
65
65
  type WorktreeBaseline,
66
66
  } from "./worktree";
67
67
 
68
+ interface TaskResumeDescriptor {
69
+ toolCallId: string;
70
+ params: TaskParams;
71
+ task: TaskItem & { id: string };
72
+ sessionFile: string | null;
73
+ forkContextSeed?: ForkContextSeed;
74
+ agentSource: AgentDefinition["source"];
75
+ }
76
+
77
+ function isTaskResumeDescriptor(value: unknown): value is TaskResumeDescriptor {
78
+ return typeof value === "object" && value !== null && "task" in value && "params" in value;
79
+ }
68
80
  function renderSubagentUserPrompt(assignment: string, simpleMode: TaskSimpleMode): string {
69
81
  return prompt.render(subagentUserPromptTemplate, {
70
82
  assignment: assignment.trim(),
@@ -416,6 +428,50 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
416
428
  };
417
429
 
418
430
  const maxConcurrency = this.session.settings.get("task.maxConcurrency");
431
+ if (typeof manager.setResumeRunner === "function") {
432
+ manager.setResumeRunner((_subagentId, message, resumeDescriptor) => {
433
+ const descriptor = isTaskResumeDescriptor(resumeDescriptor?.data) ? resumeDescriptor.data : undefined;
434
+ if (!descriptor) return undefined;
435
+ const forkSeeds = descriptor.forkContextSeed
436
+ ? new Map([[descriptor.task.id, descriptor.forkContextSeed]])
437
+ : undefined;
438
+ return manager.register(
439
+ "task",
440
+ descriptor.task.id,
441
+ async ({ signal: runSignal }) => {
442
+ const result = await this.#executeSync(
443
+ descriptor.toolCallId,
444
+ { ...descriptor.params, tasks: [descriptor.task] },
445
+ runSignal,
446
+ undefined,
447
+ [descriptor.task.id],
448
+ forkSeeds,
449
+ {
450
+ runMode: message ? "message" : "resume",
451
+ resumeMessage: message,
452
+ sessionFiles: new Map([[descriptor.task.id, descriptor.sessionFile]]),
453
+ },
454
+ );
455
+ const finalText = result.content.find(part => part.type === "text")?.text ?? "(no output)";
456
+ const singleResult = result.details?.results[0];
457
+ return singleResult?.paused ? { kind: "paused" } : finalText;
458
+ },
459
+ {
460
+ id: `${descriptor.task.id}-resume-${Snowflake.next()}`,
461
+ ownerId: this.session.getAgentId?.() ?? undefined,
462
+ metadata: {
463
+ subagent: {
464
+ id: descriptor.task.id,
465
+ agent: descriptor.params.agent,
466
+ agentSource: descriptor.agentSource,
467
+ description: descriptor.task.description,
468
+ assignment: descriptor.task.assignment.trim(),
469
+ },
470
+ },
471
+ },
472
+ );
473
+ });
474
+ }
419
475
  const semaphore = new Semaphore(maxConcurrency);
420
476
  const buildForkContextSeedForTask = async (task: TaskItem): Promise<ForkContextSeed | undefined> => {
421
477
  if (task.inheritContext !== true) return undefined;
@@ -431,6 +487,8 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
431
487
  });
432
488
  };
433
489
  const frozenForkSeeds = new Map<string, ForkContextSeed>();
490
+ const parentSessionFileForBatch = this.session.getSessionFile();
491
+ const batchArtifactsDir = parentSessionFileForBatch ? parentSessionFileForBatch.slice(0, -6) : null;
434
492
 
435
493
  for (let i = 0; i < taskItems.length; i++) {
436
494
  const taskItem = taskItems[i];
@@ -449,6 +507,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
449
507
  const singleParams: TaskParams = { ...params, tasks: [taskItem] };
450
508
  const label = uniqueId;
451
509
  try {
510
+ const subtaskSessionFile = batchArtifactsDir ? path.join(batchArtifactsDir, `${uniqueId}.jsonl`) : null;
452
511
  const jobId = manager.register(
453
512
  "task",
454
513
  label,
@@ -478,15 +537,20 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
478
537
  undefined,
479
538
  [uniqueId],
480
539
  frozenForkSeeds,
540
+ {
541
+ sessionFiles: new Map([[uniqueId, subtaskSessionFile]]),
542
+ },
481
543
  );
482
544
  const finalText = result.content.find(part => part.type === "text")?.text ?? "(no output)";
483
545
  const singleResult = result.details?.results[0];
484
546
  if (progress) {
485
- progress.status = singleResult?.aborted
486
- ? "aborted"
487
- : (singleResult?.exitCode ?? 0) === 0
488
- ? "completed"
489
- : "failed";
547
+ progress.status = singleResult?.paused
548
+ ? "paused"
549
+ : singleResult?.aborted
550
+ ? "aborted"
551
+ : (singleResult?.exitCode ?? 0) === 0
552
+ ? "completed"
553
+ : "failed";
490
554
  progress.durationMs = singleResult?.durationMs ?? Math.max(0, Date.now() - startedAt);
491
555
  progress.tokens = singleResult?.tokens ?? 0;
492
556
  progress.contextTokens = singleResult?.contextTokens;
@@ -517,6 +581,9 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
517
581
  `Background task batch complete: ${completedJobs}/${taskItems.length} finished.`,
518
582
  );
519
583
  }
584
+ if (singleResult?.paused) {
585
+ return { kind: "paused" };
586
+ }
520
587
  return finalText;
521
588
  } catch (error) {
522
589
  if (progress) {
@@ -568,6 +635,31 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
568
635
  },
569
636
  );
570
637
  startedJobs.push({ jobId, taskId: taskItem.id });
638
+ if (typeof manager.registerResumeDescriptor === "function") {
639
+ manager.registerResumeDescriptor({
640
+ subagentId: uniqueId,
641
+ ownerId: this.session.getAgentId?.() ?? undefined,
642
+ data: {
643
+ toolCallId: _toolCallId,
644
+ params,
645
+ task: { ...taskItem, id: uniqueId },
646
+ sessionFile: subtaskSessionFile,
647
+ forkContextSeed: frozenForkSeed,
648
+ agentSource: fallbackAgentSource,
649
+ } satisfies TaskResumeDescriptor,
650
+ });
651
+ }
652
+ if (typeof manager.registerSubagentRecord === "function") {
653
+ manager.registerSubagentRecord({
654
+ subagentId: uniqueId,
655
+ ownerId: this.session.getAgentId?.() ?? undefined,
656
+ currentJobId: jobId,
657
+ historicalJobIds: [],
658
+ status: manager.getJob(jobId)?.status ?? "running",
659
+ sessionFile: subtaskSessionFile,
660
+ resumable: !!batchArtifactsDir,
661
+ });
662
+ }
571
663
  } catch (error) {
572
664
  const message = error instanceof Error ? error.message : String(error);
573
665
  failedSchedules.push(`${taskItem.id}: ${message}`);
@@ -637,6 +729,11 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
637
729
  onUpdate?: AgentToolUpdateCallback<TaskToolDetails>,
638
730
  preAllocatedIds?: string[],
639
731
  prebuiltForkContextSeeds?: ReadonlyMap<string, ForkContextSeed>,
732
+ executionOverrides?: {
733
+ runMode?: "initial" | "resume" | "message";
734
+ resumeMessage?: string;
735
+ sessionFiles?: ReadonlyMap<string, string | null>;
736
+ },
640
737
  ): Promise<AgentToolResult<TaskToolDetails>> {
641
738
  const startTime = Date.now();
642
739
  const { agents, projectAgentsDir } = await discoverAgents(this.session.cwd);
@@ -996,8 +1093,17 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
996
1093
  });
997
1094
  };
998
1095
 
999
- const runTask = async (task: (typeof tasksWithUniqueIds)[number], index: number) => {
1096
+ const runTask = async (
1097
+ task: (typeof tasksWithUniqueIds)[number],
1098
+ index: number,
1099
+ overrides?: {
1100
+ runMode?: "initial" | "resume" | "message";
1101
+ resumeMessage?: string;
1102
+ sessionFile?: string | null;
1103
+ },
1104
+ ) => {
1000
1105
  const forkContextSeed = prebuiltForkContextSeeds?.get(task.id) ?? (await buildForkContextSeed(task));
1106
+ const taskSessionFile = overrides?.sessionFile ?? executionOverrides?.sessionFiles?.get(task.id) ?? null;
1001
1107
  if (!isIsolated) {
1002
1108
  return runSubprocess({
1003
1109
  cwd: this.session.cwd,
@@ -1008,12 +1114,15 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
1008
1114
  description: task.description,
1009
1115
  index,
1010
1116
  id: task.id,
1117
+ runMode: overrides?.runMode ?? executionOverrides?.runMode,
1118
+ resumeMessage: overrides?.resumeMessage ?? executionOverrides?.resumeMessage,
1119
+ subagentId: task.id,
1011
1120
  taskDepth,
1012
1121
  modelOverride,
1013
1122
  parentActiveModelPattern,
1014
1123
  thinkingLevel: thinkingLevelOverride,
1015
1124
  outputSchema: effectiveOutputSchema,
1016
- sessionFile,
1125
+ sessionFile: taskSessionFile,
1017
1126
  persistArtifacts: !!artifactsDir,
1018
1127
  artifactsDir: effectiveArtifactsDir,
1019
1128
  contextFile: contextFilePath,
@@ -1063,12 +1172,15 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
1063
1172
  description: task.description,
1064
1173
  index,
1065
1174
  id: task.id,
1175
+ runMode: overrides?.runMode ?? executionOverrides?.runMode,
1176
+ resumeMessage: overrides?.resumeMessage ?? executionOverrides?.resumeMessage,
1177
+ subagentId: task.id,
1066
1178
  taskDepth,
1067
1179
  modelOverride,
1068
1180
  parentActiveModelPattern,
1069
1181
  thinkingLevel: thinkingLevelOverride,
1070
1182
  outputSchema: effectiveOutputSchema,
1071
- sessionFile,
1183
+ sessionFile: taskSessionFile,
1072
1184
  persistArtifacts: !!artifactsDir,
1073
1185
  artifactsDir: effectiveArtifactsDir,
1074
1186
  contextFile: contextFilePath,
@@ -47,6 +47,8 @@ function getStatusIcon(status: AgentProgress["status"], theme: Theme, spinnerFra
47
47
  return formatStatusIcon("error", theme);
48
48
  case "aborted":
49
49
  return formatStatusIcon("aborted", theme);
50
+ case "paused":
51
+ return formatStatusIcon("pending", theme);
50
52
  }
51
53
  }
52
54
 
@@ -611,9 +613,10 @@ function renderAgentProgress(
611
613
  if (progress.retryState && progress.status === "running") {
612
614
  const remainingMs = Math.max(0, progress.retryState.startedAtMs + progress.retryState.delayMs - Date.now());
613
615
  const waitLabel = remainingMs > 0 ? `in ${formatDuration(remainingMs)}` : "now";
614
- const summary =
615
- `retrying ${progress.retryState.attempt}/${progress.retryState.maxAttempts} ${waitLabel}: ` +
616
- truncateToWidth(replaceTabs(progress.retryState.errorMessage), 60);
616
+ const attemptLabel = progress.retryState.unbounded
617
+ ? `attempt ${progress.retryState.attempt}`
618
+ : `${progress.retryState.attempt}/${progress.retryState.maxAttempts}`;
619
+ const summary = `retrying ${attemptLabel} ${waitLabel}: ${truncateToWidth(replaceTabs(progress.retryState.errorMessage), 60)}`;
617
620
  lines.push(`${continuePrefix}${theme.tree.hook} ${theme.fg("warning", summary)}`);
618
621
  } else if (progress.retryFailure && progress.status !== "running") {
619
622
  const summary = `auto-retry gave up after ${progress.retryFailure.attempt} attempt${
package/src/task/types.ts CHANGED
@@ -53,7 +53,7 @@ export interface SubagentLifecyclePayload {
53
53
  agent: string;
54
54
  agentSource: AgentSource;
55
55
  description?: string;
56
- status: "started" | "completed" | "failed" | "aborted";
56
+ status: "started" | "completed" | "failed" | "aborted" | "paused";
57
57
  sessionFile?: string;
58
58
  index: number;
59
59
  }
@@ -181,6 +181,7 @@ export interface AgentDefinition {
181
181
  autoloadSkills?: string[];
182
182
  hide?: boolean;
183
183
  forkContext?: ForkContextPolicy;
184
+ bashAllowedPrefixes?: string[];
184
185
  source: AgentSource;
185
186
  filePath?: string;
186
187
  }
@@ -191,7 +192,7 @@ export interface AgentProgress {
191
192
  id: string;
192
193
  agent: string;
193
194
  agentSource: AgentSource;
194
- status: "pending" | "running" | "completed" | "failed" | "aborted";
195
+ status: "pending" | "running" | "completed" | "failed" | "aborted" | "paused";
195
196
  task: string;
196
197
  assignment?: string;
197
198
  description?: string;
@@ -229,6 +230,7 @@ export interface AgentProgress {
229
230
  retryState?: {
230
231
  attempt: number;
231
232
  maxAttempts: number;
233
+ unbounded?: boolean;
232
234
  delayMs: number;
233
235
  errorMessage: string;
234
236
  startedAtMs: number;
@@ -278,6 +280,7 @@ export interface SingleResult {
278
280
  error?: string;
279
281
  aborted?: boolean;
280
282
  abortReason?: string;
283
+ paused?: boolean;
281
284
  /** Aggregated usage from the subprocess, accumulated incrementally from message_end events. */
282
285
  usage?: Usage;
283
286
  /** Output path for the task result */
@@ -314,8 +317,59 @@ export interface TaskToolDetails {
314
317
  outputPaths?: string[];
315
318
  progress?: AgentProgress[];
316
319
  async?: {
317
- state: "running" | "completed" | "failed";
320
+ state: "running" | "paused" | "queued" | "completed" | "failed";
318
321
  jobId: string;
319
322
  type: "task";
320
323
  };
321
324
  }
325
+ /**
326
+ * Persisted per-turn / per-subagent token record (Phase 0 instrumentation).
327
+ *
328
+ * Additive: this does not alter any existing task result shape. It is the
329
+ * durable, model-independent unit the deterministic orchestration-token
330
+ * benchmark (`@gajae-code/orchestration-token-benchmark`) consumes to measure
331
+ * token efficiency without any live-model calls.
332
+ */
333
+ export interface TaskTokenLog {
334
+ /** Subagent id, or "root" for the orchestrator's own turn. */
335
+ subagentId: string;
336
+ /** Agent name for attribution, when known. */
337
+ agent?: string;
338
+ /** 1-based turn index within the subagent's session. */
339
+ turn: number;
340
+ /** ISO-8601 timestamp the turn completed. */
341
+ at: string;
342
+ /** Cost-bearing input tokens (excludes cache reads), mirrors `Usage.input`. */
343
+ input: number;
344
+ /** Total output tokens for the turn, mirrors `Usage.output`. */
345
+ output: number;
346
+ /** Tokens read from the prompt cache, mirrors `Usage.cacheRead`. */
347
+ cacheRead: number;
348
+ /** Tokens written to the prompt cache, mirrors `Usage.cacheWrite`. */
349
+ cacheWrite: number;
350
+ /** input + output + cacheRead + cacheWrite. */
351
+ totalTokens: number;
352
+ /** Latest per-turn context-window occupancy, when known. */
353
+ contextTokens?: number;
354
+ /** Estimated USD cost for the turn, when known. */
355
+ cost?: number;
356
+ /** Model id used for the turn, when known. */
357
+ model?: string;
358
+ }
359
+
360
+ /**
361
+ * Deterministic aggregate token metrics computed from a set of `TaskTokenLog`
362
+ * entries. The cache-hit-rate field is the primary prompt-cache signal called
363
+ * out by the prefix-stability invariant (see the approved plan).
364
+ */
365
+ export interface TaskTokenMetrics {
366
+ /** Number of token-log entries aggregated. */
367
+ turns: number;
368
+ inputTokens: number;
369
+ outputTokens: number;
370
+ cacheReadTokens: number;
371
+ cacheWriteTokens: number;
372
+ totalTokens: number;
373
+ /** cacheRead / (input + cacheRead); 0 when there is no input-class traffic. */
374
+ cacheHitRate: number;
375
+ }
package/src/tools/ask.ts CHANGED
@@ -28,6 +28,7 @@ import {
28
28
  } from "@gajae-code/tui";
29
29
  import { prompt, untilAborted } from "@gajae-code/utils";
30
30
  import * as z from "zod/v4";
31
+ import { formatDeepInterviewSelectorPrompt, renderDeepInterviewAskQuestion } from "../deep-interview/render-middleware";
31
32
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
32
33
  import { getMarkdownTheme, type Theme, theme } from "../modes/theme/theme";
33
34
  import askDescription from "../prompts/tools/ask.md" with { type: "text" };
@@ -84,6 +85,7 @@ export interface AskToolDetails {
84
85
 
85
86
  const OTHER_OPTION = "Other (type your own)";
86
87
  const RECOMMENDED_SUFFIX = " (Recommended)";
88
+ const DEEP_INTERVIEW_SELECTOR_SCROLL_TITLE_ROWS = 12;
87
89
 
88
90
  function getDoneOptionLabel(): string {
89
91
  return `${theme.status.success} Done selecting`;
@@ -138,6 +140,7 @@ interface AskSingleQuestionOptions {
138
140
  signal?: AbortSignal;
139
141
  initialSelection?: Pick<SelectionResult, "selectedOptions" | "customInput">;
140
142
  navigation?: NavigationControls;
143
+ scrollTitleRows?: number;
141
144
  }
142
145
 
143
146
  interface UIContext {
@@ -150,6 +153,7 @@ interface UIContext {
150
153
  signal?: AbortSignal;
151
154
  outline?: boolean;
152
155
  wrapFocused?: boolean;
156
+ scrollTitleRows?: number;
153
157
  onTimeout?: () => void;
154
158
  onLeft?: () => void;
155
159
  onRight?: () => void;
@@ -171,7 +175,7 @@ async function askSingleQuestion(
171
175
  multi: boolean,
172
176
  options: AskSingleQuestionOptions = {},
173
177
  ): Promise<SelectionResult> {
174
- const { recommended, timeout, signal, initialSelection, navigation } = options;
178
+ const { recommended, timeout, signal, initialSelection, navigation, scrollTitleRows } = options;
175
179
  const doneLabel = getDoneOptionLabel();
176
180
  let selectedOptions = [...(initialSelection?.selectedOptions ?? [])];
177
181
  let customInput = initialSelection?.customInput;
@@ -187,15 +191,17 @@ async function askSingleQuestion(
187
191
  timeoutTriggered = true;
188
192
  };
189
193
  let navigationAction: "back" | "forward" | undefined;
190
- const helpText = navigation
194
+ const baseHelpText = navigation
191
195
  ? "up/down navigate enter select ←/→ question esc cancel"
192
196
  : "up/down navigate enter select esc cancel";
197
+ const helpText = scrollTitleRows === undefined ? baseHelpText : `${baseHelpText} PgUp/PgDn scroll question`;
193
198
  const dialogOptions = {
194
199
  initialIndex,
195
200
  timeout,
196
201
  signal,
197
202
  outline: true,
198
203
  wrapFocused: true,
204
+ scrollTitleRows,
199
205
  onTimeout,
200
206
  helpText,
201
207
  onLeft: navigation?.allowBack
@@ -454,9 +460,11 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
454
460
  ) => {
455
461
  const optionLabels = q.options.map(o => o.label);
456
462
  try {
463
+ const deepInterviewPrompt = formatDeepInterviewSelectorPrompt(q.question);
464
+ const displayQuestion = deepInterviewPrompt ?? q.question;
457
465
  const { selectedOptions, customInput, navigation, cancelled, timedOut } = await askSingleQuestion(
458
466
  ui,
459
- q.question,
467
+ displayQuestion,
460
468
  optionLabels,
461
469
  q.multi ?? false,
462
470
  {
@@ -465,6 +473,7 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
465
473
  signal,
466
474
  initialSelection: options?.previous,
467
475
  navigation: options?.navigation,
476
+ scrollTitleRows: deepInterviewPrompt === null ? undefined : DEEP_INTERVIEW_SELECTOR_SCROLL_TITLE_ROWS,
468
477
  },
469
478
  );
470
479
  return { optionLabels, selectedOptions, customInput, navigation, cancelled, timedOut };
@@ -659,7 +668,10 @@ export const askToolRenderer = {
659
668
  container.addChild(
660
669
  new Text(` ${uiTheme.fg("dim", qBranch)} ${uiTheme.fg("dim", `[${q.id}]`)}${metaStr}`, 0, 0),
661
670
  );
662
- container.addChild(new Markdown(q.question, 3, 0, mdTheme, accentStyle));
671
+ container.addChild(
672
+ renderDeepInterviewAskQuestion(q.question, uiTheme) ??
673
+ new Markdown(q.question, 3, 0, mdTheme, accentStyle),
674
+ );
663
675
 
664
676
  const qOptions = q.options;
665
677
  if (qOptions?.length) {
@@ -688,7 +700,10 @@ export const askToolRenderer = {
688
700
  if (args.multi) meta.push("multi");
689
701
  if (args.options?.length) meta.push(`options:${args.options.length}`);
690
702
  container.addChild(new Text(`${label}${formatMeta(meta, uiTheme)}`, 0, 0));
691
- container.addChild(new Markdown(args.question, 1, 0, mdTheme, accentStyle));
703
+ container.addChild(
704
+ renderDeepInterviewAskQuestion(args.question, uiTheme) ??
705
+ new Markdown(args.question, 1, 0, mdTheme, accentStyle),
706
+ );
692
707
 
693
708
  const options = args.options;
694
709
  if (options?.length) {
@@ -752,7 +767,10 @@ export const askToolRenderer = {
752
767
  container.addChild(
753
768
  new Text(` ${uiTheme.fg("dim", branch)} ${statusIcon} ${uiTheme.fg("dim", `[${r.id}]`)}`, 0, 0),
754
769
  );
755
- container.addChild(new Markdown(r.question, 3, 0, mdTheme, accentStyle));
770
+ container.addChild(
771
+ renderDeepInterviewAskQuestion(r.question, uiTheme) ??
772
+ new Markdown(r.question, 3, 0, mdTheme, accentStyle),
773
+ );
756
774
 
757
775
  const answerLines: string[] = [];
758
776
  for (let j = 0; j < r.selectedOptions.length; j++) {
@@ -795,7 +813,10 @@ export const askToolRenderer = {
795
813
  const header = renderStatusLine({ icon: hasSelection ? "success" : "warning", title: "Ask" }, uiTheme);
796
814
  const container = new Container();
797
815
  container.addChild(new Text(header, 0, 0));
798
- container.addChild(new Markdown(details.question, 1, 0, mdTheme, accentStyle));
816
+ container.addChild(
817
+ renderDeepInterviewAskQuestion(details.question, uiTheme) ??
818
+ new Markdown(details.question, 1, 0, mdTheme, accentStyle),
819
+ );
799
820
 
800
821
  const answerLines: string[] = [];
801
822
  if (details.selectedOptions && details.selectedOptions.length > 0) {