@hassangameryt/freeflow 0.8.0 → 0.9.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 (290) hide show
  1. package/.agents/plugins/marketplace.json +2 -2
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/.claude-plugin/plugin.json +2 -2
  4. package/.codex-plugin/plugin.json +3 -3
  5. package/.cursor-plugin/plugin.json +1 -1
  6. package/README.md +96 -149
  7. package/capabilities/cognitive-routing/SKILL.md +21 -129
  8. package/capabilities/cognitive-routing/references/coordinator.md +163 -0
  9. package/capabilities/cognitive-routing/references/worker.md +38 -0
  10. package/capabilities/compaction/SKILL.md +3 -3
  11. package/capabilities/compaction/references/summary-format.md +26 -15
  12. package/capabilities/messaging/SKILL.md +49 -0
  13. package/capabilities/task-memory/SKILL.md +87 -0
  14. package/command-surface.json +6 -54
  15. package/gemini-extension.json +1 -1
  16. package/hooks/claude/hooks.json +13 -3
  17. package/hooks/codex/hooks.json +2 -1
  18. package/hooks/shared/runtime-context.mjs +69 -77
  19. package/package.json +25 -8
  20. package/pi-extension/dist/ask-user/ask.js +108 -0
  21. package/pi-extension/dist/ask-user/form.js +250 -0
  22. package/pi-extension/dist/ask-user/questions.js +66 -0
  23. package/pi-extension/dist/ask-user/tool.js +124 -0
  24. package/pi-extension/dist/cognitive-routing/assembler.js +35 -11
  25. package/pi-extension/dist/cognitive-routing/bootstrap.js +90 -0
  26. package/pi-extension/dist/cognitive-routing/config.js +3 -4
  27. package/pi-extension/dist/cognitive-routing/event-store.js +4 -1
  28. package/pi-extension/dist/cognitive-routing/gate.js +20 -5
  29. package/pi-extension/dist/cognitive-routing/handlers.js +15 -13
  30. package/pi-extension/dist/cognitive-routing/model-control.js +17 -8
  31. package/pi-extension/dist/cognitive-routing/projection.js +23 -6
  32. package/pi-extension/dist/cognitive-routing/provenance.js +3 -1
  33. package/pi-extension/dist/cognitive-routing/render.js +15 -15
  34. package/pi-extension/dist/cognitive-routing/runtime.js +4 -2
  35. package/pi-extension/dist/cognitive-routing/schemas.js +4 -4
  36. package/pi-extension/dist/cognitive-routing/session.js +3 -1
  37. package/pi-extension/dist/cognitive-routing/sources.js +29 -4
  38. package/pi-extension/dist/cognitive-routing/state.js +18 -8
  39. package/pi-extension/dist/cognitive-routing/status.js +4 -3
  40. package/pi-extension/dist/cognitive-routing/tools.js +29 -15
  41. package/pi-extension/dist/cognitive-routing/types.js +10 -1
  42. package/pi-extension/dist/compaction/carry.js +36 -22
  43. package/pi-extension/dist/compaction/config.js +4 -2
  44. package/pi-extension/dist/compaction/controller.js +52 -29
  45. package/pi-extension/dist/compaction/harness.js +19 -3
  46. package/pi-extension/dist/compaction/results.js +14 -13
  47. package/pi-extension/dist/compaction/tool.js +6 -6
  48. package/pi-extension/dist/host/catalog.js +20 -35
  49. package/pi-extension/dist/host/config-validation.js +233 -0
  50. package/pi-extension/dist/host/config.js +78 -91
  51. package/pi-extension/dist/host/inbound.js +142 -0
  52. package/pi-extension/dist/host/projection-tags.js +1 -1
  53. package/pi-extension/dist/host/prompts.js +24 -13
  54. package/pi-extension/dist/host/read-only-session.js +6 -1
  55. package/pi-extension/dist/host/request-history.js +7 -3
  56. package/pi-extension/dist/host/rows.js +25 -0
  57. package/pi-extension/dist/host/runtime-state.js +17 -2
  58. package/pi-extension/dist/host/section-keeper.js +85 -0
  59. package/pi-extension/dist/host/settings/freeflow-command.js +846 -93
  60. package/pi-extension/dist/host/settings/settings-view.js +27 -8
  61. package/pi-extension/dist/host/status.js +19 -5
  62. package/pi-extension/dist/index.js +638 -72
  63. package/pi-extension/dist/messaging/config.js +27 -0
  64. package/pi-extension/dist/messaging/messaging.js +398 -0
  65. package/pi-extension/dist/messaging/registry.js +57 -0
  66. package/pi-extension/dist/messaging/render.js +90 -0
  67. package/pi-extension/dist/messaging/tools.js +109 -0
  68. package/pi-extension/dist/messaging/transport.js +101 -0
  69. package/pi-extension/dist/provider-support/cache/keepalive.js +16 -4
  70. package/pi-extension/dist/task-memory/config.js +49 -0
  71. package/pi-extension/dist/task-memory/registry.js +119 -0
  72. package/pi-extension/dist/task-memory/render.js +107 -0
  73. package/pi-extension/dist/task-memory/runtime.js +994 -0
  74. package/pi-extension/dist/task-memory/tool.js +233 -0
  75. package/pi-extension/dist/tool-execution/apply-patch/tool.js +1 -1
  76. package/pi-extension/dist/tool-execution/background.js +220 -28
  77. package/pi-extension/dist/tool-execution/config.js +14 -3
  78. package/pi-extension/dist/tool-execution/file-tracking.js +24 -4
  79. package/pi-extension/dist/tool-execution/guidance-notices.js +124 -0
  80. package/pi-extension/dist/tool-execution/skill-reads.js +143 -0
  81. package/pi-extension/dist/tool-execution/tools.js +2 -2
  82. package/runtime/prompts/cognitive-routing.md +4 -4
  83. package/runtime/prompts/core.md +24 -14
  84. package/runtime/prompts/interaction-contract.md +7 -4
  85. package/runtime/prompts/tool-execution.md +3 -16
  86. package/runtime/prompts/working-method.md +16 -99
  87. package/skills/deliver-work/SKILL.md +76 -0
  88. package/skills/deliver-work/references/branch-closeout.md +134 -0
  89. package/skills/deliver-work/references/commit.md +83 -0
  90. package/skills/deliver-work/references/launch.md +94 -0
  91. package/skills/deliver-work/references/release.md +85 -0
  92. package/skills/deliver-work/scripts/commit-state.mjs +139 -0
  93. package/skills/deliver-work/scripts/delivery-state.mjs +107 -0
  94. package/skills/deliver-work/scripts/lib/git-state.mjs +178 -0
  95. package/skills/deliver-work/scripts/push-route.mjs +122 -0
  96. package/skills/deliver-work/scripts/release-preflight.mjs +177 -0
  97. package/skills/discuss-work/SKILL.md +148 -0
  98. package/skills/{discuss → discuss-work}/references/discussion-continuity.md +10 -27
  99. package/skills/discuss-work/references/learning-action.md +36 -0
  100. package/skills/discuss-work/scripts/choices.mjs +101 -0
  101. package/skills/discuss-work/scripts/lib/choices.mjs +159 -0
  102. package/skills/evaluate-behavior/SKILL.md +107 -0
  103. package/skills/evaluate-behavior/references/authoring.md +381 -0
  104. package/skills/evaluate-behavior/references/coding-tasks.md +145 -0
  105. package/skills/evaluate-behavior/references/conversations-pipelines-judges.md +177 -0
  106. package/skills/evaluate-behavior/references/design-and-measurement.md +273 -0
  107. package/skills/evaluate-behavior/references/running-and-evidence.md +245 -0
  108. package/skills/evaluate-behavior/scripts/claude-guard.mjs +76 -0
  109. package/skills/evaluate-behavior/scripts/guard-rules.mjs +309 -0
  110. package/skills/evaluate-behavior/scripts/lib/agreement.mjs +90 -0
  111. package/skills/evaluate-behavior/scripts/lib/arms.mjs +55 -0
  112. package/skills/evaluate-behavior/scripts/lib/budget.mjs +87 -0
  113. package/skills/evaluate-behavior/scripts/lib/build-source.mjs +287 -0
  114. package/skills/evaluate-behavior/scripts/lib/clear.mjs +74 -0
  115. package/skills/evaluate-behavior/scripts/lib/container.mjs +395 -0
  116. package/skills/evaluate-behavior/scripts/lib/decision.mjs +418 -0
  117. package/skills/evaluate-behavior/scripts/lib/definitions.mjs +1072 -0
  118. package/skills/evaluate-behavior/scripts/lib/freeflow.mjs +103 -0
  119. package/skills/evaluate-behavior/scripts/lib/grade-command.mjs +591 -0
  120. package/skills/evaluate-behavior/scripts/lib/grade-comparison.mjs +90 -0
  121. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-interaction.mjs +19 -18
  122. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-support.mjs +32 -5
  123. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-workspace.mjs +28 -12
  124. package/skills/evaluate-behavior/scripts/lib/grade.mjs +101 -0
  125. package/skills/evaluate-behavior/scripts/lib/hooks.mjs +98 -0
  126. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/confinement.mjs +196 -0
  127. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/constraints.mjs +111 -0
  128. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/index.mjs +48 -0
  129. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/isolation.mjs +74 -0
  130. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/launch.mjs +123 -0
  131. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/model-call.mjs +73 -0
  132. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/recorder.mjs +178 -0
  133. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/session.mjs +494 -0
  134. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/skills.mjs +81 -0
  135. package/skills/evaluate-behavior/scripts/lib/hosts/index.mjs +31 -0
  136. package/skills/evaluate-behavior/scripts/lib/hosts/names.mjs +3 -0
  137. package/skills/evaluate-behavior/scripts/lib/hosts/pi/confinement.mjs +173 -0
  138. package/skills/evaluate-behavior/scripts/lib/hosts/pi/host.mjs +148 -0
  139. package/skills/evaluate-behavior/scripts/lib/hosts/pi/index.mjs +70 -0
  140. package/skills/evaluate-behavior/scripts/lib/hosts/pi/isolation.mjs +81 -0
  141. package/skills/evaluate-behavior/scripts/lib/hosts/pi/model-call.mjs +79 -0
  142. package/skills/evaluate-behavior/scripts/lib/hosts/pi/process.mjs +241 -0
  143. package/skills/{evaluate-skill/scripts/lib → evaluate-behavior/scripts/lib/hosts/pi}/rpc.mjs +144 -26
  144. package/skills/evaluate-behavior/scripts/lib/identity.mjs +82 -0
  145. package/skills/evaluate-behavior/scripts/lib/judge.mjs +688 -0
  146. package/skills/evaluate-behavior/scripts/lib/metrics.mjs +332 -0
  147. package/skills/evaluate-behavior/scripts/lib/model-call.mjs +17 -0
  148. package/skills/evaluate-behavior/scripts/lib/participants.mjs +616 -0
  149. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/process.mjs +22 -0
  150. package/skills/evaluate-behavior/scripts/lib/queue.mjs +54 -0
  151. package/skills/evaluate-behavior/scripts/lib/reference.mjs +343 -0
  152. package/skills/evaluate-behavior/scripts/lib/reuse.mjs +277 -0
  153. package/skills/evaluate-behavior/scripts/lib/review.mjs +933 -0
  154. package/skills/evaluate-behavior/scripts/lib/runner.mjs +732 -0
  155. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/sandbox.mjs +119 -14
  156. package/skills/evaluate-behavior/scripts/lib/scan-shell.mjs +457 -0
  157. package/skills/evaluate-behavior/scripts/lib/scan.mjs +479 -0
  158. package/skills/evaluate-behavior/scripts/lib/seal.mjs +91 -0
  159. package/skills/evaluate-behavior/scripts/lib/shell.mjs +560 -0
  160. package/skills/evaluate-behavior/scripts/lib/source-override.mjs +64 -0
  161. package/skills/evaluate-behavior/scripts/lib/subject-file.mjs +88 -0
  162. package/skills/{evaluate-skill/scripts/lib/pi.mjs → evaluate-behavior/scripts/lib/subject.mjs} +235 -265
  163. package/skills/evaluate-behavior/scripts/lib/task-copy.mjs +124 -0
  164. package/skills/evaluate-behavior/scripts/lib/trials.mjs +524 -0
  165. package/skills/evaluate-behavior/scripts/lib/validity.mjs +68 -0
  166. package/skills/evaluate-behavior/scripts/lib/view-html.mjs +454 -0
  167. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/view.mjs +159 -18
  168. package/skills/{evaluate-skill → evaluate-behavior}/scripts/pi-command-tool.mjs +21 -6
  169. package/skills/evaluate-behavior/scripts/pi-container-route.mjs +23 -0
  170. package/skills/evaluate-behavior/scripts/pi-guard.mjs +77 -0
  171. package/skills/evaluate-behavior/scripts/pi-login-refresh.mjs +31 -0
  172. package/skills/evaluate-behavior/scripts/skill-eval.mjs +586 -0
  173. package/skills/execute-work/SKILL.md +96 -104
  174. package/skills/execute-work/references/code-practices.md +27 -51
  175. package/skills/execute-work/references/design.md +271 -0
  176. package/skills/execute-work/references/diagnose.md +117 -0
  177. package/skills/execute-work/references/migration.md +108 -0
  178. package/skills/execute-work/references/simplify.md +65 -0
  179. package/skills/execute-work/scripts/self-review.mjs +422 -0
  180. package/skills/execute-work/scripts/touch-map.mjs +290 -0
  181. package/skills/review-work/SKILL.md +89 -65
  182. package/skills/review-work/references/adjudication.md +94 -0
  183. package/skills/review-work/references/artifact.md +198 -0
  184. package/skills/review-work/references/evals-and-judges.md +66 -0
  185. package/skills/review-work/references/work.md +177 -0
  186. package/skills/review-work/scripts/check-brief.mjs +128 -0
  187. package/skills/review-work/scripts/review-packet.mjs +233 -0
  188. package/skills/setup-freeflow/SKILL.md +68 -17
  189. package/skills/track-work/SKILL.md +52 -20
  190. package/skills/track-work/references/handoff.md +190 -0
  191. package/skills/track-work/references/projects.md +51 -0
  192. package/skills/track-work/references/working-record-format.md +36 -18
  193. package/skills/track-work/scripts/lib/document.mjs +17 -13
  194. package/skills/track-work/scripts/lib/format.mjs +2 -0
  195. package/skills/track-work/scripts/lib/recover.mjs +254 -0
  196. package/skills/track-work/scripts/lib/registry.mjs +656 -0
  197. package/skills/track-work/scripts/lib/search.mjs +40 -0
  198. package/skills/track-work/scripts/lib/store.mjs +46 -15
  199. package/skills/track-work/scripts/lib/views.mjs +50 -1
  200. package/skills/track-work/scripts/working-record.mjs +232 -13
  201. package/skills/verify-work/SKILL.md +45 -37
  202. package/skills/verify-work/references/feature-map.md +120 -0
  203. package/skills/verify-work/references/runtime-evidence.md +203 -0
  204. package/skills/verify-work/references/test-design.md +25 -6
  205. package/skills/verify-work/scripts/test-changes.mjs +246 -0
  206. package/skills/write-docs/SKILL.md +113 -25
  207. package/skills/write-docs/references/documentation-shapes.md +68 -3
  208. package/skills/write-docs/references/spec-and-plan-shapes.md +308 -0
  209. package/skills/write-docs/scripts/artifact-check.mjs +148 -0
  210. package/skills/write-docs/scripts/docs-check.mjs +96 -6
  211. package/skills/write-docs/scripts/plan-lint.mjs +123 -0
  212. package/skills/write-skill/SKILL.md +92 -52
  213. package/skills/write-skill/references/activation-and-wording.md +143 -0
  214. package/skills/write-skill/references/placement-and-dependencies.md +154 -0
  215. package/skills/write-skill/scripts/lib/compare.mjs +701 -0
  216. package/skills/write-skill/scripts/lib/skill-author.mjs +197 -3
  217. package/skills/write-skill/scripts/skill-author.mjs +51 -8
  218. package/capabilities/cognitive-routing/references/both-mode.md +0 -36
  219. package/capabilities/cognitive-routing/references/executor-mode.md +0 -48
  220. package/capabilities/cognitive-routing/references/helper-mode.md +0 -47
  221. package/plugin.json +0 -33
  222. package/skills/action-selection/SKILL.md +0 -92
  223. package/skills/action-selection/references/trajectory-stalls.md +0 -73
  224. package/skills/bypass/SKILL.md +0 -153
  225. package/skills/commit-work/SKILL.md +0 -93
  226. package/skills/commit-work/references/git-state-edges.md +0 -94
  227. package/skills/commit-work/references/simple-push.md +0 -94
  228. package/skills/commit-work/references/staging-decisions.md +0 -106
  229. package/skills/decision-gate/SKILL.md +0 -87
  230. package/skills/design-for-depth/SKILL.md +0 -108
  231. package/skills/design-for-depth/references/design-pressure-signals.md +0 -84
  232. package/skills/design-for-depth/references/implementation-decisions.md +0 -76
  233. package/skills/design-for-depth/references/interface-design-loop.md +0 -105
  234. package/skills/design-for-depth/references/module-and-dependency-design.md +0 -117
  235. package/skills/design-for-depth/references/state-and-failure-boundaries.md +0 -125
  236. package/skills/diagnose-failure/SKILL.md +0 -140
  237. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +0 -50
  238. package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +0 -51
  239. package/skills/discuss/SKILL.md +0 -140
  240. package/skills/evaluate-skill/SKILL.md +0 -188
  241. package/skills/evaluate-skill/references/evaluation-definition-schema.md +0 -415
  242. package/skills/evaluate-skill/references/evaluation-design.md +0 -112
  243. package/skills/evaluate-skill/references/execution-and-evidence.md +0 -111
  244. package/skills/evaluate-skill/references/review-and-revision.md +0 -94
  245. package/skills/evaluate-skill/scripts/lib/definitions.mjs +0 -448
  246. package/skills/evaluate-skill/scripts/lib/grade-comparison.mjs +0 -60
  247. package/skills/evaluate-skill/scripts/lib/grade.mjs +0 -64
  248. package/skills/evaluate-skill/scripts/lib/review.mjs +0 -196
  249. package/skills/evaluate-skill/scripts/lib/runner.mjs +0 -405
  250. package/skills/evaluate-skill/scripts/lib/trials.mjs +0 -103
  251. package/skills/evaluate-skill/scripts/pi-guard.mjs +0 -181
  252. package/skills/evaluate-skill/scripts/skill-eval.mjs +0 -182
  253. package/skills/execute-work/references/execute-work-edges.md +0 -78
  254. package/skills/finish-branch/SKILL.md +0 -117
  255. package/skills/finish-branch/references/integration-options.md +0 -80
  256. package/skills/handoff/SKILL.md +0 -82
  257. package/skills/handoff/references/handoff-templates.md +0 -150
  258. package/skills/launch-work/SKILL.md +0 -116
  259. package/skills/launch-work/references/launch-readiness.md +0 -85
  260. package/skills/migration-work/SKILL.md +0 -115
  261. package/skills/migration-work/references/migration-lifecycle.md +0 -82
  262. package/skills/release-work/SKILL.md +0 -118
  263. package/skills/release-work/references/release-evidence.md +0 -78
  264. package/skills/review-artifact/SKILL.md +0 -143
  265. package/skills/review-artifact/references/adjudicate-artifact-review.md +0 -101
  266. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +0 -159
  267. package/skills/review-work/references/adjudicate-work-review.md +0 -97
  268. package/skills/review-work/references/independent-work-reviewer-contract.md +0 -139
  269. package/skills/review-work/references/security-risk-lens.md +0 -67
  270. package/skills/setup-freeflow/references/activation-contract.md +0 -103
  271. package/skills/setup-freeflow/references/host-setup.md +0 -67
  272. package/skills/simplify-code/SKILL.md +0 -85
  273. package/skills/simplify-code/references/simplification-patterns.md +0 -70
  274. package/skills/verify-work/references/browser-runtime-evidence.md +0 -51
  275. package/skills/verify-work/references/integration-evidence.md +0 -49
  276. package/skills/verify-work/references/performance-evidence.md +0 -59
  277. package/skills/workflow/SKILL.md +0 -171
  278. package/skills/workflow/references/domain-skill-composition.md +0 -45
  279. package/skills/write-plan/SKILL.md +0 -92
  280. package/skills/write-plan/references/plan-shapes.md +0 -156
  281. package/skills/write-skill/references/activation-boundaries.md +0 -106
  282. package/skills/write-skill/references/agent-first-instructions.md +0 -133
  283. package/skills/write-skill/references/progressive-disclosure.md +0 -129
  284. package/skills/write-skill/references/skill-dependency-graphs.md +0 -192
  285. package/skills/write-spec/SKILL.md +0 -85
  286. package/skills/write-spec/references/artifact-standards.md +0 -86
  287. package/skills/write-spec/references/decision-records.md +0 -80
  288. package/skills/write-spec/references/spec-shapes.md +0 -143
  289. /package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/evidence.mjs +0 -0
  290. /package/skills/{evaluate-skill → evaluate-behavior}/scripts/pi-observer.mjs +0 -0
@@ -1,23 +1,15 @@
1
+ // One subject: the host-neutral part of running an arm's agent. It materializes the declared environment, fixture,
2
+ // runtime and Freeflow settings, asks the arm's host adapter (hosts/) to launch the agent, and turns what the host
3
+ // observed into the arm's run record. Everything that depends on the host program lives in its adapter.
4
+
1
5
  import { createHash } from "node:crypto";
2
- import { spawn } from "node:child_process";
3
- import { once } from "node:events";
4
- import { createWriteStream } from "node:fs";
5
6
  import { mkdir, readFile, realpath } from "node:fs/promises";
6
7
  import path from "node:path";
7
- import { fileURLToPath } from "node:url";
8
8
 
9
- import {
10
- mergeUsage,
11
- messageText,
12
- NO_PROGRESS_MS,
13
- pipeStream,
14
- processGroupExists,
15
- signalProcessTree,
16
- TERMINATION_GRACE_MS,
17
- waitForForcedKill,
18
- writeStream,
19
- } from "./process.mjs";
20
- import { runRpcBodySession, runRpcDescriptionSession } from "./rpc.mjs";
9
+ import { baseProcessEnvironment } from "./process.mjs";
10
+ import { writeFreeflowSettings } from "./freeflow.mjs";
11
+ import { runMetrics } from "./hooks.mjs";
12
+ import { hostAdapter } from "./hosts/index.mjs";
21
13
  import {
22
14
  fingerprintDirectory,
23
15
  materializeEnvironment,
@@ -26,28 +18,17 @@ import {
26
18
  snapshotWorkspace,
27
19
  verifyEnvironment,
28
20
  verifyRuntime,
21
+ WORKSPACE,
29
22
  workspaceChanges,
30
23
  } from "./sandbox.mjs";
31
24
 
32
- const guardExtension = fileURLToPath(new URL("../pi-guard.mjs", import.meta.url));
33
- const commandToolExtension = fileURLToPath(new URL("../pi-command-tool.mjs", import.meta.url));
34
- const observerExtension = fileURLToPath(new URL("../pi-observer.mjs", import.meta.url));
35
- const HOST_COMMANDS = { pi: "pi" };
36
- const BASE_PROCESS_ENVIRONMENT_KEYS = [
37
- "PATH",
38
- "HOME",
39
- "TMPDIR",
40
- "TMP",
41
- "TEMP",
42
- "LANG",
43
- "LC_ALL",
44
- "XDG_CONFIG_HOME",
45
- "XDG_DATA_HOME",
46
- "XDG_CACHE_HOME",
47
- "PI_PACKAGE_DIR",
48
- "PI_OFFLINE",
49
- ];
50
25
  const TURN_WORKSPACE_KINDS = new Set(["path", "changed-paths", "file-text", "json"]);
26
+ // Tools whose commands run in the subject's container when it has one: bash, and Freeflow's background commands.
27
+ const CONTAINER_SHELL_TOOLS = new Set(["bash", "bash_background", "monitor"]);
28
+ // Freeflow's background commands save their output under the subject's temp directory and name that file to the
29
+ // model, which reads it with read or from bash; with them declared, that directory is readable (and, in a container,
30
+ // mounted read-only at its own path).
31
+ const BACKGROUND_TOOLS = new Set(["bash_background", "monitor"]);
51
32
 
52
33
  export class VariantSetupError extends Error {
53
34
  constructor(message) {
@@ -56,39 +37,91 @@ export class VariantSetupError extends Error {
56
37
  }
57
38
  }
58
39
 
59
- export async function runDescription({ group, variant, root, variantDirectory, fixture, runtime, signal }) {
60
- if (group.input.turns !== undefined) {
61
- return runPersistentDescription({ group, variant, root, variantDirectory, fixture, runtime, signal });
62
- }
63
- return runOneShotDescription({ group, variant, root, variantDirectory, fixture, runtime, signal });
40
+ export async function runDescription({
41
+ group,
42
+ variant,
43
+ root,
44
+ variantDirectory,
45
+ fixture,
46
+ runtime,
47
+ freeflow,
48
+ hostExtensions,
49
+ signal,
50
+ extendEnvironment = null,
51
+ }) {
52
+ const options = { group, variant, root, variantDirectory, fixture, runtime, freeflow, hostExtensions, signal };
53
+ options.extendEnvironment = extendEnvironment;
54
+ if (group.input.turns !== undefined) return runPersistentDescription(options);
55
+ return runOneShotDescription(options);
64
56
  }
65
57
 
66
- export async function runBody({ group, variant, root, variantDirectory, fixture, runtime, signal }) {
67
- const options = { group, variant, root, variantDirectory, fixture, runtime, signal };
58
+ export async function runBody({
59
+ group,
60
+ variant,
61
+ root,
62
+ variantDirectory,
63
+ fixture,
64
+ runtime,
65
+ freeflow,
66
+ hostExtensions,
67
+ signal,
68
+ extendEnvironment = null,
69
+ nextPrompt = null,
70
+ }) {
71
+ const options = { group, variant, root, variantDirectory, fixture, runtime, freeflow, hostExtensions, signal };
72
+ options.extendEnvironment = extendEnvironment;
68
73
  const subject = await prepareSubject(options, "rpc");
69
- const before = await fingerprintDirectory(subject.workspace);
74
+ const before = await fingerprintDirectory(subject.workspace, WORKSPACE);
70
75
  const declaredPrompts = group.input.prompt === undefined ? [...group.input.turns] : [group.input.prompt];
71
- const observation = await runRpcBodySession({
72
- command: subject.host.command,
73
- args: subject.args,
74
- cwd: subject.workspace,
75
- eventsFile: subject.eventsFile,
76
- stderrFile: subject.stderrFile,
77
- signal,
78
- prompts: declaredPrompts,
79
- targetPath: subject.environment.targetPath,
80
- environment: subject.processEnvironment,
81
- afterTurn: needsTurnWorkspaceEvidence(group)
82
- ? (turn) => snapshotWorkspace({ workspace: subject.workspace, variantDirectory, turn, before })
83
- : null,
84
- });
76
+ // A container environment runs the subject's bash and declared commands in a container of its own, removed even
77
+ // when the run is cancelled. A group with neither needs no subject container; its graders still run in the image.
78
+ const usesContainer =
79
+ group.tools.some((tool) => CONTAINER_SHELL_TOOLS.has(tool)) || (group.commands?.length ?? 0) > 0;
80
+ const declaredContainer = usesContainer ? group.variants[variant].shell?.container : undefined;
81
+ let container = null;
82
+ if (declaredContainer !== undefined) {
83
+ const { containerTarget, startContainer } = await import("./container.mjs");
84
+ // The subject's declared resources are mounted read-only at the paths it was given, so its bash can run a
85
+ // skill's scripts; the workspace is the only writable mount.
86
+ container = await startContainer({
87
+ container: declaredContainer,
88
+ workspace: subject.workspace,
89
+ resources: subject.resourceRoots,
90
+ });
91
+ subject.processEnvironment.SKILL_EVAL_CONTAINER = containerTarget(container);
92
+ }
93
+ let observation;
94
+ try {
95
+ observation = await subject.adapter.runSession({
96
+ subject,
97
+ command: subject.host.command,
98
+ settings: subject.isolation.settings,
99
+ args: subject.args,
100
+ cwd: subject.workspace,
101
+ eventsFile: subject.eventsFile,
102
+ stderrFile: subject.stderrFile,
103
+ signal,
104
+ prompts: declaredPrompts,
105
+ resumeBefore: group.input.resume_before ?? [],
106
+ targetPath: subject.environment.targetPath,
107
+ environment: subject.processEnvironment,
108
+ nextPrompt,
109
+ afterTurn: needsTurnWorkspaceEvidence(group)
110
+ ? (turn) => snapshotWorkspace({ workspace: subject.workspace, variantDirectory, turn, before })
111
+ : null,
112
+ });
113
+ } finally {
114
+ await container?.remove();
115
+ }
85
116
  const contextObservation = await readContextObservations(
86
117
  subject.contextObservationPath,
87
118
  subject.requiresContextObservation,
88
119
  );
89
120
  observation.contextObservationArtifact = contextObservation.artifact;
121
+ if (contextObservation.error) (observation.protocolErrors ??= []).push(contextObservation.error);
90
122
  await verifySubjectResources(subject, observation);
91
- const after = await fingerprintDirectory(subject.workspace);
123
+ // Links the subject made are recorded with their targets, never followed, so an installer's link cannot lose the run.
124
+ const after = await fingerprintDirectory(subject.workspace, WORKSPACE);
92
125
 
93
126
  const turns = [];
94
127
  for (const [index, turn] of observation.turns.entries()) {
@@ -105,6 +138,9 @@ export async function runBody({ group, variant, root, variantDirectory, fixture,
105
138
  assistantError: turn.assistantError,
106
139
  settled: turn.settled,
107
140
  workspace: turn.workspace ?? null,
141
+ ...(turn.resumed ? { resumed: turn.resumed } : {}),
142
+ // Runs Pi started on its own after this turn's prompt (an extension's notice), whose activity is part of it.
143
+ ...(turn.unpromptedRuns ? { unpromptedRuns: turn.unpromptedRuns } : {}),
108
144
  });
109
145
  }
110
146
 
@@ -141,7 +177,7 @@ function bodyRun(options, subject, observation, input, effects) {
141
177
  }
142
178
 
143
179
  function subjectRun({ group, variant }, subject, observation) {
144
- return {
180
+ const run = {
145
181
  schema_version: 1,
146
182
  variant,
147
183
  state: subjectRunState(observation),
@@ -151,7 +187,7 @@ function subjectRun({ group, variant }, subject, observation) {
151
187
  tools: [...group.tools],
152
188
  commands: group.commands ?? [],
153
189
  model: {
154
- declared: group.model ?? null,
190
+ declared: group.variants[variant].model ?? null,
155
191
  observed: observation.model,
156
192
  },
157
193
  resources: {
@@ -160,9 +196,14 @@ function subjectRun({ group, variant }, subject, observation) {
160
196
  skills: subject.environment.skills,
161
197
  context: subject.environment.context,
162
198
  runtime: subject.runtime,
199
+ freeflow: subject.freeflow,
200
+ hostExtensions: subject.hostExtensions,
163
201
  contextDelivery: subject.environment.contextDelivery,
164
202
  targetPath: subject.environment.targetPath,
203
+ // Everything the subject's tools could read: the workspace first, then its declared resources.
204
+ readableRoots: [subject.workspaceRoot, ...subject.resourceRoots],
165
205
  },
206
+ isolation: subject.isolation,
166
207
  contextObservationArtifact: observation.contextObservationArtifact ?? null,
167
208
  response: observation.response,
168
209
  transcript: observation.transcript,
@@ -181,17 +222,24 @@ function subjectRun({ group, variant }, subject, observation) {
181
222
  stderr: subject.stderrFile,
182
223
  },
183
224
  };
225
+ // Run-metrics hooks derive their fields from what this run already recorded.
226
+ const metrics = runMetrics({ group, arm: variant, subject, observation, run });
227
+ if (metrics !== null) run.metrics = metrics;
228
+ return run;
184
229
  }
185
230
 
186
231
  async function runOneShotDescription(options) {
187
232
  const subject = await prepareSubject(options, "json");
188
- const observation = await runPiProcess({
233
+ const observation = await subject.adapter.runOneShot({
234
+ subject,
189
235
  command: subject.host.command,
190
236
  args: subject.args,
191
237
  cwd: subject.workspace,
192
238
  eventsFile: subject.eventsFile,
193
239
  stderrFile: subject.stderrFile,
194
240
  signal: options.signal,
241
+ // Pi takes the prompt as its last argument (`args`); a host that reads prompts from stdin takes it here.
242
+ prompts: [options.group.input.prompt],
195
243
  environment: subject.processEnvironment,
196
244
  });
197
245
  const contextObservation = await readContextObservations(
@@ -199,12 +247,14 @@ async function runOneShotDescription(options) {
199
247
  subject.requiresContextObservation,
200
248
  );
201
249
  observation.contextObservationArtifact = contextObservation.artifact;
250
+ if (contextObservation.error) (observation.protocolErrors ??= []).push(contextObservation.error);
202
251
  await verifySubjectResources(subject, observation);
203
252
  const successfulReadPaths = await canonicalReadPaths(observation.successfulReads, subject.workspace);
204
253
  const targetRead =
205
254
  subject.environment.targetPath !== null && successfulReadPaths.includes(subject.environment.targetPath);
206
255
  return descriptionRun(options, subject, observation, {
207
256
  input: { prompt: options.group.input.prompt },
257
+ toolActivity: observation.toolActivity ?? [],
208
258
  activation: {
209
259
  targetRead,
210
260
  firstReadTurn: targetRead ? 1 : null,
@@ -216,8 +266,10 @@ async function runOneShotDescription(options) {
216
266
 
217
267
  async function runPersistentDescription(options) {
218
268
  const subject = await prepareSubject(options, "rpc");
219
- const observation = await runRpcDescriptionSession({
269
+ const observation = await subject.adapter.runSession({
270
+ subject,
220
271
  command: subject.host.command,
272
+ settings: subject.isolation.settings,
221
273
  args: subject.args,
222
274
  cwd: subject.workspace,
223
275
  eventsFile: subject.eventsFile,
@@ -231,6 +283,7 @@ async function runPersistentDescription(options) {
231
283
  subject.requiresContextObservation,
232
284
  );
233
285
  observation.contextObservationArtifact = contextObservation.artifact;
286
+ if (contextObservation.error) (observation.protocolErrors ??= []).push(contextObservation.error);
234
287
  await verifySubjectResources(subject, observation);
235
288
 
236
289
  const turns = [];
@@ -264,59 +317,139 @@ async function runPersistentDescription(options) {
264
317
  });
265
318
  }
266
319
 
267
- async function prepareSubject({ group, variant, root, variantDirectory, fixture: fixtureSnapshot, runtime }, mode) {
320
+ async function prepareSubject(
321
+ {
322
+ group,
323
+ variant,
324
+ root,
325
+ variantDirectory,
326
+ fixture: fixtureSnapshot,
327
+ runtime,
328
+ freeflow = null,
329
+ hostExtensions = [],
330
+ extendEnvironment = null,
331
+ },
332
+ mode,
333
+ ) {
268
334
  const workspace = path.join(variantDirectory, "workspace");
269
335
 
270
336
  let environment;
271
337
  let fixture;
272
338
  let materializedRuntime;
339
+ let adapter;
340
+ let isolation;
273
341
  try {
274
342
  environment = await materializeEnvironment({
275
343
  environment: group.variants[variant],
276
344
  root,
277
345
  variantDirectory,
278
346
  });
347
+ // A pipeline stage adds what it declares it sees from earlier stages as further declared context.
348
+ if (extendEnvironment !== null) await extendEnvironment(environment, variantDirectory);
279
349
  materializedRuntime = await materializeRuntime({ runtime, variantDirectory });
350
+ // Runtime environment literals may differ between arms (definitions.mjs); each subject gets its arm's own.
351
+ const literal = group.variants[variant].runtimeLiteral;
352
+ if (literal !== undefined) materializedRuntime.environment = { ...materializedRuntime.environment, literal };
280
353
  fixture = await materializeFixture({ fixture: fixtureSnapshot, workspace });
354
+ // Written before the workspace's starting state is taken, so they never count as the subject's changes.
355
+ if (freeflow !== null) await writeFreeflowSettings(workspace, freeflow);
356
+ adapter = hostAdapter(materializedRuntime.host);
357
+ isolation = await adapter.prepareIsolation(variantDirectory, group.variants[variant].model?.model ?? null, {
358
+ settings: adapter.effectiveSettings(group.variants[variant].hostSettings ?? {}),
359
+ });
281
360
  } catch (error) {
282
361
  if (error instanceof VariantSetupError) throw error;
283
362
  throw new VariantSetupError(error instanceof Error ? error.message : String(error));
284
363
  }
285
364
 
286
- const host = { name: materializedRuntime.host, command: HOST_COMMANDS[materializedRuntime.host] };
365
+ const host = { name: materializedRuntime.host, command: adapter.command };
287
366
  const contextObservationPath = path.join(variantDirectory, "context-observations.jsonl");
288
367
  const requiresContextObservation = group.expectations.some((expectation) => expectation.kind === "context-text");
289
368
  const sessionDirectory = path.join(variantDirectory, "session");
290
369
  if (materializedRuntime.session) await mkdir(sessionDirectory, { recursive: true });
291
- const args = piArguments(group, environment, materializedRuntime, mode, requiresContextObservation, sessionDirectory);
292
370
  const allowedRoots = [
293
371
  await realpath(workspace),
294
372
  ...environment.skills.map((skill) => skill.path),
295
373
  ...environment.context.map((entry) => entry.path),
296
374
  ...materializedRuntime.extensions.flatMap((bundle) => bundle.resources.map((resource) => resource.path)),
375
+ // Freeflow points the subject at its own skills and references inside the package.
376
+ ...(freeflow === null ? [] : [freeflow.packageRoot]),
377
+ ...(group.tools.some((tool) => BACKGROUND_TOOLS.has(tool)) && isolation.environment.TMPDIR
378
+ ? [isolation.environment.TMPDIR]
379
+ : []),
297
380
  ];
381
+ let launch;
382
+ try {
383
+ launch = await adapter.prepareLaunch({
384
+ group,
385
+ variant,
386
+ environment,
387
+ runtime: materializedRuntime,
388
+ mode,
389
+ observeContext: requiresContextObservation,
390
+ contextObservationPath,
391
+ sessionDirectory,
392
+ model: group.variants[variant].model,
393
+ freeflowEntry: freeflow?.entry ?? null,
394
+ hostExtensions,
395
+ workspace,
396
+ variantDirectory,
397
+ allowedRoots,
398
+ isolation: isolation.evidence,
399
+ });
400
+ } catch (error) {
401
+ if (error instanceof VariantSetupError) throw error;
402
+ throw new VariantSetupError(error instanceof Error ? error.message : String(error));
403
+ }
298
404
  return {
299
- args,
405
+ adapter,
406
+ args: launch.args,
300
407
  environment,
408
+ hostExtensions,
409
+ freeflow:
410
+ freeflow === null
411
+ ? null
412
+ : {
413
+ ref: freeflow.ref,
414
+ commit: freeflow.commit,
415
+ package: freeflow.package,
416
+ tarballSha256: freeflow.tarballSha256,
417
+ metadata: freeflow.metadata,
418
+ repository: freeflow.repository,
419
+ personal: freeflow.personal,
420
+ },
421
+ // Where the built Freeflow package lies, so metrics can tell its skill reads apart; not recorded in run.json.
422
+ freeflowRoot: freeflow?.packageRoot ?? null,
301
423
  runtime: materializedRuntime,
302
424
  host,
303
425
  fixture,
304
426
  workspace,
427
+ workspaceRoot: allowedRoots[0],
428
+ // Every readable root but the workspace: the declared skills, context, runtime resources and Freeflow's package.
429
+ resourceRoots: allowedRoots.slice(1),
305
430
  contextObservationPath,
306
431
  requiresContextObservation,
307
432
  sessionDirectory,
308
433
  eventsFile: path.join(variantDirectory, "events.jsonl"),
309
434
  stderrFile: path.join(variantDirectory, "stderr.log"),
310
435
  startedAt: new Date().toISOString(),
311
- processEnvironment: createProcessEnvironment({
312
- runtime: materializedRuntime,
313
- group,
314
- workspace,
315
- allowedRoots,
316
- contextManifestPath: environment.contextDelivery.manifestPath,
317
- contextObservationPath,
318
- requiresContextObservation,
319
- }),
436
+ isolation: isolation.evidence,
437
+ processEnvironment: {
438
+ ...createProcessEnvironment({
439
+ runtime: materializedRuntime,
440
+ group,
441
+ workspace,
442
+ allowedRoots,
443
+ contextManifestPath: environment.contextDelivery.manifestPath,
444
+ contextObservationPath,
445
+ requiresContextObservation,
446
+ hostEnvironment: launch.environment,
447
+ }),
448
+ // A declared shell lets the guard allow bash; its isolation extension runs it.
449
+ ...(group.variants[variant].shell ? { SKILL_EVAL_SHELL: JSON.stringify(group.variants[variant].shell) } : {}),
450
+ // Last, so nothing inherited or declared can point the subject back at the parent's home or Pi settings.
451
+ ...isolation.environment,
452
+ },
320
453
  };
321
454
  }
322
455
 
@@ -328,13 +461,14 @@ function createProcessEnvironment({
328
461
  contextManifestPath,
329
462
  contextObservationPath,
330
463
  requiresContextObservation,
464
+ hostEnvironment,
331
465
  }) {
332
466
  const processEnvironment = baseProcessEnvironment();
333
467
  Object.assign(processEnvironment, {
334
468
  PWD: workspace,
335
469
  ...runtime.environment.literal,
336
- PI_SKIP_VERSION_CHECK: "1",
337
- PI_TELEMETRY: "0",
470
+ // The host's own variables, such as Pi's version check and telemetry switches, after declared literals.
471
+ ...hostEnvironment,
338
472
  SKILL_EVAL_ALLOWED_ROOTS: JSON.stringify(allowedRoots),
339
473
  SKILL_EVAL_WRITABLE_ROOT: workspace,
340
474
  SKILL_EVAL_ALLOWED_TOOLS: JSON.stringify(group.tools),
@@ -353,30 +487,39 @@ function createProcessEnvironment({
353
487
  return processEnvironment;
354
488
  }
355
489
 
356
- export function baseProcessEnvironment() {
357
- return Object.fromEntries(
358
- BASE_PROCESS_ENVIRONMENT_KEYS.flatMap((key) => (process.env[key] === undefined ? [] : [[key, process.env[key]]])),
359
- );
360
- }
361
-
490
+ // A missing or malformed observation fails the subject but keeps everything the process already reported:
491
+ // transcript, response, usage, and process evidence stay in run.json.
362
492
  async function readContextObservations(file, required) {
363
- const contents = await readFile(file, "utf8").catch((error) => {
364
- if (error?.code === "ENOENT") {
365
- if (required) throw new Error("context observer produced no evidence");
366
- return "";
367
- }
368
- throw error;
369
- });
493
+ let contents;
494
+ try {
495
+ contents = await readFile(file, "utf8");
496
+ } catch (error) {
497
+ if (error?.code !== "ENOENT") throw error;
498
+ if (!required) return { observations: [], artifact: null, error: null };
499
+ return {
500
+ observations: [],
501
+ artifact: null,
502
+ error: { reason: "context-observer-missing", message: "context observer produced no evidence" },
503
+ };
504
+ }
370
505
  const observations = [];
371
506
  for (const line of contents.split("\n").filter(Boolean)) {
372
507
  try {
373
508
  observations.push(JSON.parse(line));
374
509
  } catch (error) {
375
- throw new Error(`context observation is malformed: ${error instanceof Error ? error.message : String(error)}`);
510
+ return {
511
+ observations: [],
512
+ artifact: null,
513
+ error: {
514
+ reason: "context-observation-malformed",
515
+ message: `context observation is malformed: ${error instanceof Error ? error.message : String(error)}`,
516
+ },
517
+ };
376
518
  }
377
519
  }
378
520
  return {
379
521
  observations,
522
+ error: null,
380
523
  artifact:
381
524
  contents === ""
382
525
  ? null
@@ -403,11 +546,12 @@ async function verifySubjectResources(subject, observation) {
403
546
  }
404
547
  }
405
548
 
406
- function descriptionRun(options, subject, observation, { input, activation }) {
549
+ function descriptionRun(options, subject, observation, { input, activation, toolActivity }) {
407
550
  return {
408
551
  ...subjectRun(options, subject, observation),
409
552
  evaluationType: "description",
410
553
  ...input,
554
+ ...(toolActivity === undefined ? {} : { toolActivity }),
411
555
  activation,
412
556
  };
413
557
  }
@@ -426,180 +570,6 @@ function subjectRunState(observation) {
426
570
  return "infrastructure-failed";
427
571
  }
428
572
 
429
- function piArguments(group, environment, runtime, mode, observeContext, sessionDirectory) {
430
- const args = ["--mode", mode];
431
- if (mode === "json") args.push("-p");
432
- if (runtime.session) args.push("--session-dir", sessionDirectory);
433
- else args.push("--no-session");
434
- args.push("--no-extensions", "--extension", guardExtension);
435
- if (group.commands !== undefined) args.push("--extension", commandToolExtension);
436
- for (const bundle of runtime.extensions) args.push("--extension", bundle.entry);
437
- if (observeContext) args.push("--extension", observerExtension);
438
- args.push("--no-skills", "--no-prompt-templates", "--no-themes", "--no-context-files", "--no-approve");
439
- if (group.tools.length === 0) args.push("--no-tools");
440
- else args.push("--tools", group.tools.join(","));
441
- for (const skill of environment.skills) args.push("--skill", skill.path);
442
- if (group.model?.model) args.push("--model", group.model.model);
443
- if (group.model?.thinking) args.push("--thinking", group.model.thinking);
444
- if (mode === "json") args.push(group.input.prompt);
445
- return args;
446
- }
447
-
448
- export async function runPiProcess({ command, args, cwd, eventsFile, stderrFile, signal, environment }) {
449
- const child = spawn(command, args, {
450
- cwd,
451
- detached: process.platform !== "win32",
452
- env: environment,
453
- shell: false,
454
- stdio: ["ignore", "pipe", "pipe"],
455
- });
456
- const events = createWriteStream(eventsFile, { encoding: "utf8" });
457
- const stderr = createWriteStream(stderrFile, { encoding: "utf8" });
458
- const transcript = [];
459
- const calls = new Map();
460
- const successfulReads = [];
461
- const parseErrors = [];
462
- const protocolErrors = [];
463
- let settled = false;
464
- let response = "";
465
- let usage = null;
466
- let assistantError = null;
467
- let observedModel = null;
468
- let buffer = "";
469
- let eventLine = 0;
470
- const processGroupId = child.pid;
471
- let watchdog;
472
- let forcedKill;
473
- /** @type {Promise<void> | null} */
474
- let forcedKillPromise = null;
475
- let terminationReason = null;
476
-
477
- const resetWatchdog = () => {
478
- clearTimeout(watchdog);
479
- watchdog = setTimeout(() => requestTermination("no-progress-watchdog"), NO_PROGRESS_MS);
480
- watchdog.unref();
481
- };
482
- const requestTermination = (reason) => {
483
- if (terminationReason !== null) return;
484
- terminationReason = reason;
485
- signalProcessTree(processGroupId, child, "SIGTERM");
486
- if (process.platform !== "win32") {
487
- forcedKillPromise = new Promise((resolve) => {
488
- forcedKill = setTimeout(() => {
489
- signalProcessTree(processGroupId, child, "SIGKILL");
490
- resolve();
491
- }, TERMINATION_GRACE_MS);
492
- });
493
- }
494
- };
495
- const onAbort = () => requestTermination("cancelled");
496
- if (signal?.aborted) onAbort();
497
- else signal?.addEventListener("abort", onAbort, { once: true });
498
- resetWatchdog();
499
-
500
- const stderrTask = pipeStream(child.stderr, stderr, resetWatchdog);
501
- const stdoutTask = (async () => {
502
- for await (const chunk of child.stdout) {
503
- resetWatchdog();
504
- buffer += chunk.toString("utf8");
505
- while (true) {
506
- const newline = buffer.indexOf("\n");
507
- if (newline === -1) break;
508
- const line = buffer.slice(0, newline);
509
- buffer = buffer.slice(newline + 1);
510
- await writeStream(events, `${line}\n`);
511
- observeLine(line.endsWith("\r") ? line.slice(0, -1) : line);
512
- }
513
- }
514
- if (buffer.length > 0) {
515
- await writeStream(events, buffer);
516
- observeLine(buffer.endsWith("\r") ? buffer.slice(0, -1) : buffer);
517
- }
518
- events.end();
519
- await once(events, "finish");
520
- })();
521
-
522
- function observeLine(line) {
523
- eventLine += 1;
524
- if (line.trim() === "") return;
525
- let event;
526
- try {
527
- event = JSON.parse(line);
528
- } catch {
529
- parseErrors.push({ line: eventLine, reason: "invalid-json" });
530
- return;
531
- }
532
- if (event.type === "extension_error") {
533
- protocolErrors.push({
534
- reason: "extension-error",
535
- extensionPath: event.extensionPath ?? null,
536
- event: event.event ?? null,
537
- error: event.error ?? null,
538
- });
539
- requestTermination("protocol-error");
540
- }
541
- if (event.type === "agent_settled") settled = true;
542
- if (event.type === "tool_execution_start") {
543
- calls.set(event.toolCallId, { toolName: event.toolName, args: event.args });
544
- }
545
- if (event.type === "tool_execution_end" && !event.isError) {
546
- const call = calls.get(event.toolCallId);
547
- if (call?.toolName === "read" && typeof call.args?.path === "string") {
548
- successfulReads.push(call.args.path);
549
- }
550
- }
551
- if (event.type === "message_end" && event.message) {
552
- transcript.push(event.message);
553
- if (event.message.role === "assistant") {
554
- response = messageText(event.message);
555
- usage = mergeUsage(usage, event.message.usage);
556
- observedModel = {
557
- provider: event.message.provider ?? null,
558
- model: event.message.model ?? null,
559
- };
560
- assistantError =
561
- event.message.stopReason === "error" || event.message.stopReason === "aborted"
562
- ? (event.message.errorMessage ?? `assistant stopped with ${event.message.stopReason}`)
563
- : null;
564
- }
565
- }
566
- }
567
-
568
- let exitCode = null;
569
- let exitSignal = null;
570
- try {
571
- const [code, closedSignal] = await once(child, "close");
572
- exitCode = code;
573
- exitSignal = closedSignal;
574
- await Promise.all([stdoutTask, stderrTask]);
575
- } catch (error) {
576
- requestTermination("process-error");
577
- throw error;
578
- } finally {
579
- clearTimeout(watchdog);
580
- if (forcedKillPromise !== null) {
581
- if (processGroupExists(processGroupId)) await waitForForcedKill(forcedKillPromise);
582
- else clearTimeout(forcedKill);
583
- }
584
- signal?.removeEventListener("abort", onAbort);
585
- }
586
-
587
- return {
588
- exitCode,
589
- signal: exitSignal,
590
- settled,
591
- assistantError,
592
- model: observedModel,
593
- terminationReason,
594
- parseErrors,
595
- protocolErrors,
596
- successfulReads,
597
- transcript,
598
- response,
599
- usage,
600
- };
601
- }
602
-
603
573
  async function canonicalReadPaths(paths, cwd) {
604
574
  const canonical = [];
605
575
  for (const rawPath of paths) {