@gajae-code/coding-agent 0.2.5 → 0.3.1

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 (234) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/types/async/job-manager.d.ts +91 -2
  3. package/dist/types/cli/args.d.ts +1 -1
  4. package/dist/types/commands/deep-interview.d.ts +3 -0
  5. package/dist/types/commands/harness.d.ts +37 -0
  6. package/dist/types/config/keybindings.d.ts +5 -0
  7. package/dist/types/config/settings-schema.d.ts +10 -4
  8. package/dist/types/config/settings.d.ts +2 -0
  9. package/dist/types/debug/crash-diagnostics.d.ts +45 -0
  10. package/dist/types/debug/runtime-gauges.d.ts +6 -0
  11. package/dist/types/deep-interview/render-middleware.d.ts +6 -0
  12. package/dist/types/eval/py/executor.d.ts +2 -0
  13. package/dist/types/eval/py/kernel.d.ts +2 -0
  14. package/dist/types/exec/bash-executor.d.ts +10 -0
  15. package/dist/types/extensibility/custom-tools/types.d.ts +1 -0
  16. package/dist/types/extensibility/extensions/types.d.ts +6 -0
  17. package/dist/types/extensibility/shared-events.d.ts +1 -0
  18. package/dist/types/gjc-runtime/cli-write-receipt.d.ts +24 -0
  19. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +1 -0
  20. package/dist/types/gjc-runtime/state-graph.d.ts +4 -0
  21. package/dist/types/gjc-runtime/state-migrations.d.ts +33 -0
  22. package/dist/types/gjc-runtime/state-renderer.d.ts +65 -0
  23. package/dist/types/gjc-runtime/state-runtime.d.ts +2 -0
  24. package/dist/types/gjc-runtime/state-schema.d.ts +317 -0
  25. package/dist/types/gjc-runtime/state-validation.d.ts +6 -0
  26. package/dist/types/gjc-runtime/state-writer.d.ts +147 -0
  27. package/dist/types/gjc-runtime/team-runtime.d.ts +81 -7
  28. package/dist/types/gjc-runtime/workflow-command-ref.d.ts +43 -0
  29. package/dist/types/gjc-runtime/workflow-manifest.d.ts +54 -0
  30. package/dist/types/harness-control-plane/classifier.d.ts +13 -0
  31. package/dist/types/harness-control-plane/control-endpoint.d.ts +31 -0
  32. package/dist/types/harness-control-plane/finalize.d.ts +47 -0
  33. package/dist/types/harness-control-plane/frame-mapper.d.ts +29 -0
  34. package/dist/types/harness-control-plane/operate.d.ts +35 -0
  35. package/dist/types/harness-control-plane/owner.d.ts +46 -0
  36. package/dist/types/harness-control-plane/preserve.d.ts +19 -0
  37. package/dist/types/harness-control-plane/receipts.d.ts +88 -0
  38. package/dist/types/harness-control-plane/rpc-adapter.d.ts +66 -0
  39. package/dist/types/harness-control-plane/seams.d.ts +21 -0
  40. package/dist/types/harness-control-plane/session-lease.d.ts +65 -0
  41. package/dist/types/harness-control-plane/state-machine.d.ts +19 -0
  42. package/dist/types/harness-control-plane/storage.d.ts +53 -0
  43. package/dist/types/harness-control-plane/types.d.ts +162 -0
  44. package/dist/types/hooks/skill-keywords.d.ts +2 -1
  45. package/dist/types/hooks/skill-state.d.ts +23 -29
  46. package/dist/types/internal-urls/agent-protocol.d.ts +2 -2
  47. package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
  48. package/dist/types/internal-urls/registry-helpers.d.ts +8 -7
  49. package/dist/types/internal-urls/types.d.ts +4 -0
  50. package/dist/types/lsp/index.d.ts +10 -10
  51. package/dist/types/modes/bridge/auth.d.ts +12 -0
  52. package/dist/types/modes/bridge/bridge-client-bridge.d.ts +9 -0
  53. package/dist/types/modes/bridge/bridge-mode.d.ts +44 -0
  54. package/dist/types/modes/bridge/bridge-ui-context.d.ts +88 -0
  55. package/dist/types/modes/bridge/event-stream.d.ts +8 -0
  56. package/dist/types/modes/components/custom-editor.d.ts +6 -0
  57. package/dist/types/modes/components/hook-selector.d.ts +1 -0
  58. package/dist/types/modes/components/jobs-overlay-model.d.ts +31 -0
  59. package/dist/types/modes/components/jobs-overlay.d.ts +30 -0
  60. package/dist/types/modes/components/status-line/types.d.ts +2 -0
  61. package/dist/types/modes/components/status-line.d.ts +2 -0
  62. package/dist/types/modes/controllers/input-controller.d.ts +1 -0
  63. package/dist/types/modes/controllers/selector-controller.d.ts +8 -0
  64. package/dist/types/modes/index.d.ts +1 -0
  65. package/dist/types/modes/interactive-mode.d.ts +2 -0
  66. package/dist/types/modes/jobs-observer.d.ts +57 -0
  67. package/dist/types/modes/rpc/host-tools.d.ts +1 -16
  68. package/dist/types/modes/rpc/host-uris.d.ts +1 -38
  69. package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +20 -0
  70. package/dist/types/modes/shared/agent-wire/command-validation.d.ts +2 -0
  71. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +24 -0
  72. package/dist/types/modes/shared/agent-wire/handshake.d.ts +46 -0
  73. package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +16 -0
  74. package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +17 -0
  75. package/dist/types/modes/shared/agent-wire/protocol.d.ts +44 -0
  76. package/dist/types/modes/shared/agent-wire/responses.d.ts +4 -0
  77. package/dist/types/modes/shared/agent-wire/scopes.d.ts +18 -0
  78. package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +42 -0
  79. package/dist/types/modes/shared/agent-wire/ui-result.d.ts +27 -0
  80. package/dist/types/modes/types.d.ts +2 -0
  81. package/dist/types/sdk.d.ts +4 -0
  82. package/dist/types/session/agent-session.d.ts +19 -1
  83. package/dist/types/skill-state/active-state.d.ts +2 -0
  84. package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +1 -1
  85. package/dist/types/skill-state/workflow-state-contract.d.ts +25 -2
  86. package/dist/types/skill-state/workflow-state-version.d.ts +3 -0
  87. package/dist/types/task/executor.d.ts +3 -0
  88. package/dist/types/task/id.d.ts +7 -0
  89. package/dist/types/task/index.d.ts +5 -0
  90. package/dist/types/task/receipt.d.ts +85 -0
  91. package/dist/types/task/spawn-gate.d.ts +38 -0
  92. package/dist/types/task/types.d.ts +198 -14
  93. package/dist/types/tools/cron.d.ts +6 -0
  94. package/dist/types/tools/index.d.ts +2 -0
  95. package/dist/types/tools/path-utils.d.ts +1 -0
  96. package/dist/types/tools/subagent.d.ts +26 -1
  97. package/package.json +7 -7
  98. package/scripts/build-binary.ts +7 -0
  99. package/src/async/job-manager.ts +334 -6
  100. package/src/cli/args.ts +9 -2
  101. package/src/cli/auth-broker-cli.ts +1 -0
  102. package/src/cli/config-cli.ts +10 -2
  103. package/src/cli.ts +2 -0
  104. package/src/commands/deep-interview.ts +1 -0
  105. package/src/commands/harness.ts +862 -0
  106. package/src/commands/launch.ts +2 -2
  107. package/src/commands/state.ts +2 -1
  108. package/src/commands/team.ts +54 -39
  109. package/src/config/keybindings.ts +6 -0
  110. package/src/config/settings-schema.ts +13 -3
  111. package/src/config/settings.ts +5 -0
  112. package/src/dap/client.ts +17 -3
  113. package/src/debug/crash-diagnostics.ts +223 -0
  114. package/src/debug/runtime-gauges.ts +20 -0
  115. package/src/deep-interview/render-middleware.ts +372 -0
  116. package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
  117. package/src/defaults/gjc/skills/ralplan/SKILL.md +31 -2
  118. package/src/defaults/gjc/skills/team/SKILL.md +47 -21
  119. package/src/defaults/gjc/skills/ultragoal/SKILL.md +106 -13
  120. package/src/eval/py/executor.ts +21 -1
  121. package/src/eval/py/kernel.ts +15 -0
  122. package/src/exec/bash-executor.ts +41 -0
  123. package/src/extensibility/custom-tools/types.ts +1 -0
  124. package/src/extensibility/extensions/types.ts +6 -0
  125. package/src/extensibility/shared-events.ts +1 -0
  126. package/src/gjc-runtime/cli-write-receipt.ts +31 -0
  127. package/src/gjc-runtime/deep-interview-runtime.ts +98 -42
  128. package/src/gjc-runtime/goal-mode-request.ts +11 -3
  129. package/src/gjc-runtime/ralplan-runtime.ts +235 -43
  130. package/src/gjc-runtime/state-graph.ts +86 -0
  131. package/src/gjc-runtime/state-migrations.ts +179 -0
  132. package/src/gjc-runtime/state-renderer.ts +345 -0
  133. package/src/gjc-runtime/state-runtime.ts +1155 -46
  134. package/src/gjc-runtime/state-schema.ts +192 -0
  135. package/src/gjc-runtime/state-validation.ts +49 -0
  136. package/src/gjc-runtime/state-writer.ts +749 -0
  137. package/src/gjc-runtime/team-runtime.ts +1255 -189
  138. package/src/gjc-runtime/ultragoal-runtime.ts +460 -43
  139. package/src/gjc-runtime/workflow-command-ref.ts +239 -0
  140. package/src/gjc-runtime/workflow-manifest.generated.json +1601 -0
  141. package/src/gjc-runtime/workflow-manifest.ts +427 -0
  142. package/src/harness-control-plane/classifier.ts +128 -0
  143. package/src/harness-control-plane/control-endpoint.ts +148 -0
  144. package/src/harness-control-plane/finalize.ts +222 -0
  145. package/src/harness-control-plane/frame-mapper.ts +286 -0
  146. package/src/harness-control-plane/operate.ts +225 -0
  147. package/src/harness-control-plane/owner.ts +600 -0
  148. package/src/harness-control-plane/preserve.ts +102 -0
  149. package/src/harness-control-plane/receipts.ts +216 -0
  150. package/src/harness-control-plane/rpc-adapter.ts +276 -0
  151. package/src/harness-control-plane/seams.ts +39 -0
  152. package/src/harness-control-plane/session-lease.ts +388 -0
  153. package/src/harness-control-plane/state-machine.ts +98 -0
  154. package/src/harness-control-plane/storage.ts +257 -0
  155. package/src/harness-control-plane/types.ts +214 -0
  156. package/src/hooks/skill-keywords.ts +4 -2
  157. package/src/hooks/skill-state.ts +197 -64
  158. package/src/internal-urls/agent-protocol.ts +68 -21
  159. package/src/internal-urls/artifact-protocol.ts +12 -17
  160. package/src/internal-urls/docs-index.generated.ts +3 -2
  161. package/src/internal-urls/registry-helpers.ts +19 -16
  162. package/src/internal-urls/types.ts +4 -0
  163. package/src/lsp/client.ts +18 -2
  164. package/src/main.ts +21 -5
  165. package/src/modes/bridge/auth.ts +41 -0
  166. package/src/modes/bridge/bridge-client-bridge.ts +47 -0
  167. package/src/modes/bridge/bridge-mode.ts +520 -0
  168. package/src/modes/bridge/bridge-ui-context.ts +200 -0
  169. package/src/modes/bridge/event-stream.ts +70 -0
  170. package/src/modes/components/assistant-message.ts +5 -1
  171. package/src/modes/components/custom-editor.ts +101 -0
  172. package/src/modes/components/hook-selector.ts +133 -20
  173. package/src/modes/components/jobs-overlay-model.ts +109 -0
  174. package/src/modes/components/jobs-overlay.ts +172 -0
  175. package/src/modes/components/status-line/presets.ts +7 -5
  176. package/src/modes/components/status-line/segments.ts +25 -0
  177. package/src/modes/components/status-line/types.ts +2 -0
  178. package/src/modes/components/status-line.ts +9 -1
  179. package/src/modes/controllers/event-controller.ts +71 -6
  180. package/src/modes/controllers/extension-ui-controller.ts +43 -1
  181. package/src/modes/controllers/input-controller.ts +105 -9
  182. package/src/modes/controllers/selector-controller.ts +31 -1
  183. package/src/modes/index.ts +1 -0
  184. package/src/modes/interactive-mode.ts +28 -0
  185. package/src/modes/jobs-observer.ts +204 -0
  186. package/src/modes/rpc/host-tools.ts +1 -186
  187. package/src/modes/rpc/host-uris.ts +1 -235
  188. package/src/modes/rpc/rpc-client.ts +25 -10
  189. package/src/modes/rpc/rpc-mode.ts +12 -381
  190. package/src/modes/shared/agent-wire/command-dispatch.ts +341 -0
  191. package/src/modes/shared/agent-wire/command-validation.ts +131 -0
  192. package/src/modes/shared/agent-wire/event-envelope.ts +108 -0
  193. package/src/modes/shared/agent-wire/handshake.ts +117 -0
  194. package/src/modes/shared/agent-wire/host-tool-bridge.ts +194 -0
  195. package/src/modes/shared/agent-wire/host-uri-bridge.ts +236 -0
  196. package/src/modes/shared/agent-wire/protocol.ts +96 -0
  197. package/src/modes/shared/agent-wire/responses.ts +17 -0
  198. package/src/modes/shared/agent-wire/scopes.ts +89 -0
  199. package/src/modes/shared/agent-wire/ui-request-broker.ts +150 -0
  200. package/src/modes/shared/agent-wire/ui-result.ts +48 -0
  201. package/src/modes/types.ts +2 -0
  202. package/src/prompts/agents/executor.md +13 -0
  203. package/src/prompts/tools/subagent.md +39 -4
  204. package/src/prompts/tools/task-summary.md +3 -9
  205. package/src/prompts/tools/task.md +5 -1
  206. package/src/sdk.ts +8 -0
  207. package/src/session/agent-session.ts +445 -71
  208. package/src/session/session-manager.ts +13 -1
  209. package/src/skill-state/active-state.ts +58 -65
  210. package/src/skill-state/deep-interview-mutation-guard.ts +114 -17
  211. package/src/skill-state/initial-phase.ts +2 -0
  212. package/src/skill-state/workflow-state-contract.ts +33 -4
  213. package/src/skill-state/workflow-state-version.ts +3 -0
  214. package/src/slash-commands/builtin-registry.ts +8 -0
  215. package/src/task/executor.ts +79 -13
  216. package/src/task/id.ts +33 -0
  217. package/src/task/index.ts +376 -74
  218. package/src/task/output-manager.ts +5 -4
  219. package/src/task/receipt.ts +297 -0
  220. package/src/task/render.ts +54 -134
  221. package/src/task/spawn-gate.ts +132 -0
  222. package/src/task/types.ts +104 -10
  223. package/src/tools/ask.ts +88 -27
  224. package/src/tools/ast-edit.ts +1 -0
  225. package/src/tools/ast-grep.ts +1 -0
  226. package/src/tools/bash.ts +1 -1
  227. package/src/tools/cron.ts +48 -0
  228. package/src/tools/find.ts +4 -1
  229. package/src/tools/index.ts +2 -0
  230. package/src/tools/path-utils.ts +3 -2
  231. package/src/tools/read.ts +1 -0
  232. package/src/tools/search.ts +1 -0
  233. package/src/tools/skill.ts +6 -1
  234. package/src/tools/subagent.ts +423 -79
@@ -4,11 +4,14 @@
4
4
  * Runs each subagent on the main thread and forwards AgentEvents for progress tracking.
5
5
  */
6
6
 
7
+ import { createHash } from "node:crypto";
8
+ import * as fs from "node:fs/promises";
7
9
  import path from "node:path";
8
10
  import type { AgentEvent, AgentIdentity, AgentTelemetryConfig, ThinkingLevel } from "@gajae-code/agent-core";
9
11
  import { recordHandoff, resolveTelemetry } from "@gajae-code/agent-core";
10
12
  import { type JsonSchemaValidationIssue, validateJsonSchemaValue } from "@gajae-code/ai/utils/schema";
11
13
  import { logger, prompt, untilAborted } from "@gajae-code/utils";
14
+ import { AsyncJobManager } from "../async";
12
15
  import { ModelRegistry } from "../config/model-registry";
13
16
  import { resolveModelOverrideWithAuthFallback } from "../config/model-resolver";
14
17
  import type { PromptTemplate } from "../config/prompt-templates";
@@ -112,6 +115,9 @@ export interface ExecutorOptions {
112
115
  index: number;
113
116
  id: string;
114
117
  modelOverride?: string | string[];
118
+ runMode?: "initial" | "resume" | "message";
119
+ resumeMessage?: string;
120
+ subagentId?: string;
115
121
  /**
116
122
  * Active model selector of the parent session, used as an auth-aware fallback
117
123
  * if the resolved subagent model has no working credentials. See #985.
@@ -550,8 +556,8 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
550
556
  }
551
557
 
552
558
  // Set up artifact paths and write input file upfront if artifacts dir provided
553
- let subtaskSessionFile: string | undefined;
554
- if (options.artifactsDir) {
559
+ let subtaskSessionFile: string | undefined = options.sessionFile ?? undefined;
560
+ if (!subtaskSessionFile && options.artifactsDir) {
555
561
  subtaskSessionFile = path.join(options.artifactsDir, `${id}.jsonl`);
556
562
  }
557
563
 
@@ -617,6 +623,8 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
617
623
  let activeSession: AgentSession | null = null;
618
624
  let unsubscribe: (() => void) | null = null;
619
625
  let yieldCalled = false;
626
+ let pauseRequested = false;
627
+ let paused = false;
620
628
 
621
629
  // Accumulate usage incrementally from message_end events (no memory for streaming events)
622
630
  const accumulatedUsage = {
@@ -1006,6 +1014,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1006
1014
  }
1007
1015
  }
1008
1016
  }
1017
+ paused = (event as { stopReason?: string }).stopReason === "paused";
1009
1018
  flushProgress = true;
1010
1019
  break;
1011
1020
  }
@@ -1182,6 +1191,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1182
1191
  hasUI: false,
1183
1192
  spawns: spawnsEnv,
1184
1193
  taskDepth: childDepth,
1194
+ currentAgentType: agent.name,
1185
1195
  parentHindsightSessionState: options.parentHindsightSessionState,
1186
1196
  parentTaskPrefix: id,
1187
1197
  agentId: id,
@@ -1193,10 +1203,27 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1193
1203
  localProtocolOptions: options.localProtocolOptions,
1194
1204
  telemetry: subagentTelemetry,
1195
1205
  forkContextSeed: options.forkContextSeed,
1206
+ shouldPause: () => pauseRequested,
1196
1207
  }),
1197
1208
  );
1198
1209
 
1199
1210
  activeSession = session;
1211
+ const liveSubagentId = options.subagentId ?? id;
1212
+ const manager = AsyncJobManager.instance();
1213
+ if (manager) {
1214
+ manager.registerLiveHandle(liveSubagentId, {
1215
+ requestPause: () => {
1216
+ pauseRequested = true;
1217
+ },
1218
+ injectMessage: async (content, deliverAs) => {
1219
+ if (deliverAs === "nextTurn") {
1220
+ await session.prompt(content, { attribution: "agent" });
1221
+ return;
1222
+ }
1223
+ await session.sendUserMessage(content, { deliverAs });
1224
+ },
1225
+ });
1226
+ }
1200
1227
 
1201
1228
  // Emit lifecycle start event
1202
1229
  if (options.eventBus) {
@@ -1306,6 +1333,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1306
1333
  progress.retryState = {
1307
1334
  attempt: event.attempt,
1308
1335
  maxAttempts: event.maxAttempts,
1336
+ unbounded: event.unbounded,
1309
1337
  delayMs: event.delayMs,
1310
1338
  errorMessage: event.errorMessage,
1311
1339
  startedAtMs: Date.now(),
@@ -1354,13 +1382,24 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1354
1382
  );
1355
1383
  }
1356
1384
  }
1357
- await awaitAbortable(session.prompt(task, { attribution: "agent" }));
1358
- await awaitAbortable(session.waitForIdle());
1385
+ const runMode = options.runMode ?? "initial";
1386
+ if (runMode === "message") {
1387
+ await awaitAbortable(session.prompt(options.resumeMessage ?? "", { attribution: "agent" }));
1388
+ await awaitAbortable(session.waitForIdle());
1389
+ } else if (runMode === "resume") {
1390
+ await awaitAbortable(
1391
+ session.prompt("Continue from the paused subagent session state.", { attribution: "agent" }),
1392
+ );
1393
+ await awaitAbortable(session.waitForIdle());
1394
+ } else {
1395
+ await awaitAbortable(session.prompt(task, { attribution: "agent" }));
1396
+ await awaitAbortable(session.waitForIdle());
1397
+ }
1359
1398
 
1360
1399
  const reminderToolChoice = buildNamedToolChoice("yield", session.model);
1361
1400
 
1362
1401
  let retryCount = 0;
1363
- while (!yieldCalled && retryCount < MAX_YIELD_RETRIES && !abortSignal.aborted) {
1402
+ while (!paused && !yieldCalled && retryCount < MAX_YIELD_RETRIES && !abortSignal.aborted) {
1364
1403
  // Skip reminders when the model returned a terminal error (e.g.
1365
1404
  // rate-limit cap hit, auth failure). Re-prompting would just
1366
1405
  // hit the same wall, multiplying the failure noise without
@@ -1390,7 +1429,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1390
1429
  }
1391
1430
 
1392
1431
  await awaitAbortable(session.waitForIdle());
1393
- if (!yieldCalled && !abortSignal.aborted) {
1432
+ if (!paused && !yieldCalled && !abortSignal.aborted) {
1394
1433
  exitCode = 0;
1395
1434
  }
1396
1435
 
@@ -1406,6 +1445,10 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1406
1445
  exitCode = 1;
1407
1446
  error ??= lastAssistant.errorMessage || "Subagent failed";
1408
1447
  }
1448
+ if (paused) {
1449
+ exitCode = 0;
1450
+ error = undefined;
1451
+ }
1409
1452
  }
1410
1453
  } catch (err) {
1411
1454
  exitCode = 1;
@@ -1421,6 +1464,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1421
1464
  if (exitCode === 0) exitCode = 1;
1422
1465
  }
1423
1466
  sessionAbortController.abort();
1467
+ AsyncJobManager.instance()?.removeLiveHandle(options.subagentId ?? id);
1424
1468
  if (unsubscribe) {
1425
1469
  try {
1426
1470
  unsubscribe();
@@ -1495,18 +1539,39 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1495
1539
 
1496
1540
  // Write output artifact (input and jsonl already written in real-time)
1497
1541
  // Compute output metadata for agent:// URL integration
1498
- let outputMeta: { lineCount: number; charCount: number } | undefined;
1542
+ let outputMeta: { lineCount: number; charCount: number; byteSize?: number; sha256?: string } | undefined;
1499
1543
  let outputPath: string | undefined;
1500
1544
  if (options.artifactsDir) {
1501
- outputPath = path.join(options.artifactsDir, `${id}.md`);
1545
+ const candidateOutputPath = path.join(options.artifactsDir, `${id}.md`);
1502
1546
  try {
1503
- await Bun.write(outputPath, rawOutput);
1547
+ await Bun.write(candidateOutputPath, rawOutput);
1548
+ const byteSize = Buffer.byteLength(rawOutput, "utf8");
1549
+ const lineCount = rawOutput.split("\n").length;
1550
+ const sha256 = createHash("sha256").update(rawOutput).digest("hex");
1551
+ const createdAt = new Date().toISOString();
1552
+ await Bun.write(
1553
+ `${candidateOutputPath}.meta.json`,
1554
+ JSON.stringify({ id, kind: "agent-output", sizeBytes: byteSize, lineCount, sha256, createdAt }, null, 2),
1555
+ );
1556
+ outputPath = candidateOutputPath;
1504
1557
  outputMeta = {
1505
- lineCount: rawOutput.split("\n").length,
1558
+ lineCount,
1506
1559
  charCount: rawOutput.length,
1560
+ byteSize,
1561
+ sha256,
1507
1562
  };
1508
1563
  } catch {
1509
- // Non-fatal
1564
+ // Output or metadata write failed: never advertise an unverifiable
1565
+ // artifact. Best-effort remove any orphaned `.md`/sidecar so a later
1566
+ // agent:// read cannot serve unverified content. Non-fatal.
1567
+ outputPath = undefined;
1568
+ outputMeta = undefined;
1569
+ try {
1570
+ await fs.rm(candidateOutputPath, { force: true });
1571
+ await fs.rm(`${candidateOutputPath}.meta.json`, { force: true });
1572
+ } catch {
1573
+ // best-effort cleanup; ignore
1574
+ }
1510
1575
  }
1511
1576
  }
1512
1577
 
@@ -1527,7 +1592,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1527
1592
  ? yieldAbortReason
1528
1593
  : (done.abortReason ?? (signal?.aborted ? resolveSignalAbortReason() : resolveAbortReasonText()))
1529
1594
  : undefined;
1530
- progress.status = wasAborted ? "aborted" : exitCode === 0 ? "completed" : "failed";
1595
+ progress.status = paused ? "paused" : wasAborted ? "aborted" : exitCode === 0 ? "completed" : "failed";
1531
1596
  scheduleProgress(true);
1532
1597
 
1533
1598
  // Emit lifecycle end event after finalization so yield status is reflected
@@ -1537,7 +1602,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1537
1602
  agent: agent.name,
1538
1603
  agentSource: agent.source,
1539
1604
  description: options.description,
1540
- status: progress.status as "completed" | "failed" | "aborted",
1605
+ status: progress.status as "completed" | "failed" | "aborted" | "paused",
1541
1606
  sessionFile: subtaskSessionFile,
1542
1607
  index,
1543
1608
  });
@@ -1564,6 +1629,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
1564
1629
  error: exitCode !== 0 && stderr ? stderr : undefined,
1565
1630
  aborted: wasAborted,
1566
1631
  abortReason: finalAbortReason,
1632
+ paused,
1567
1633
  usage: hasUsage ? accumulatedUsage : undefined,
1568
1634
  outputPath,
1569
1635
  extractedToolData: progress.extractedToolData,
package/src/task/id.ts ADDED
@@ -0,0 +1,33 @@
1
+ export const TASK_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/;
2
+ export const TASK_ID_DESCRIPTION = "filesystem-safe identifier matching ^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$";
3
+
4
+ const ALLOCATED_TASK_ID_PATTERN = new RegExp(
5
+ `^\\d+-${TASK_ID_PATTERN.source.slice(1, -1)}(?:\\.\\d+-${TASK_ID_PATTERN.source.slice(1, -1)})*$`,
6
+ );
7
+
8
+ export function isValidTaskId(id: string): boolean {
9
+ return TASK_ID_PATTERN.test(id);
10
+ }
11
+
12
+ export function getTaskIdValidationError(id: unknown): string | undefined {
13
+ if (typeof id !== "string") return "Task id must be a string.";
14
+ if (isValidTaskId(id)) return undefined;
15
+ return `Task id ${JSON.stringify(id)} is invalid. Use ${TASK_ID_DESCRIPTION}.`;
16
+ }
17
+
18
+ export function validateTaskId(id: string): string {
19
+ const error = getTaskIdValidationError(id);
20
+ if (error) throw new Error(error);
21
+ return id;
22
+ }
23
+
24
+ export function isValidAllocatedTaskId(id: string): boolean {
25
+ return ALLOCATED_TASK_ID_PATTERN.test(id);
26
+ }
27
+
28
+ export function validateAllocatedTaskId(id: string): string {
29
+ if (!isValidAllocatedTaskId(id)) {
30
+ throw new Error(`Allocated task id ${JSON.stringify(id)} is invalid for filesystem artifact paths.`);
31
+ }
32
+ return id;
33
+ }