@hassangameryt/freeflow 0.7.3 → 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 (426) 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/NOTICE +14 -0
  7. package/README.md +101 -159
  8. package/capabilities/cognitive-routing/SKILL.md +47 -210
  9. package/capabilities/cognitive-routing/references/coordinator.md +163 -0
  10. package/capabilities/cognitive-routing/references/worker.md +38 -0
  11. package/capabilities/compaction/SKILL.md +49 -0
  12. package/capabilities/compaction/references/summary-format.md +250 -0
  13. package/capabilities/messaging/SKILL.md +49 -0
  14. package/capabilities/task-memory/SKILL.md +87 -0
  15. package/command-surface.json +8 -52
  16. package/gemini-extension.json +1 -1
  17. package/hooks/claude/hooks.json +13 -3
  18. package/hooks/codex/hooks.json +2 -1
  19. package/hooks/shared/runtime-context.mjs +73 -83
  20. package/licenses/Apache-2.0.txt +55 -0
  21. package/package.json +42 -12
  22. package/pi-extension/README.md +41 -0
  23. package/pi-extension/dist/ask-user/ask.js +108 -0
  24. package/pi-extension/dist/ask-user/form.js +250 -0
  25. package/pi-extension/dist/ask-user/questions.js +66 -0
  26. package/pi-extension/dist/ask-user/tool.js +124 -0
  27. package/pi-extension/dist/cognitive-routing/assembler.js +417 -0
  28. package/pi-extension/dist/cognitive-routing/bootstrap.js +90 -0
  29. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/config.js +8 -6
  30. package/pi-extension/dist/cognitive-routing/economics.js +45 -0
  31. package/pi-extension/dist/cognitive-routing/event-schema.js +388 -0
  32. package/pi-extension/dist/{session-sources/events.js → cognitive-routing/event-store.js} +107 -41
  33. package/pi-extension/dist/cognitive-routing/gate.js +256 -0
  34. package/pi-extension/dist/cognitive-routing/handlers.js +901 -0
  35. package/pi-extension/dist/cognitive-routing/handoffs.js +350 -0
  36. package/pi-extension/dist/cognitive-routing/model-control.js +476 -0
  37. package/pi-extension/dist/cognitive-routing/projection.js +520 -0
  38. package/pi-extension/dist/cognitive-routing/provenance.js +83 -0
  39. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/render.js +15 -15
  40. package/pi-extension/dist/cognitive-routing/runtime.js +398 -0
  41. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/schemas.js +47 -11
  42. package/pi-extension/dist/cognitive-routing/session.js +223 -0
  43. package/pi-extension/dist/{session-sources → cognitive-routing}/sources.js +126 -87
  44. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/state.js +55 -386
  45. package/pi-extension/dist/cognitive-routing/status.js +81 -0
  46. package/pi-extension/dist/cognitive-routing/tools.js +74 -0
  47. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/types.js +14 -11
  48. package/pi-extension/dist/compaction/carry.js +205 -0
  49. package/pi-extension/dist/compaction/config.js +24 -0
  50. package/pi-extension/dist/compaction/controller.js +494 -0
  51. package/pi-extension/dist/compaction/harness.js +93 -0
  52. package/pi-extension/dist/compaction/results.js +159 -0
  53. package/pi-extension/dist/compaction/thresholds.js +35 -0
  54. package/pi-extension/dist/compaction/tool.js +57 -0
  55. package/pi-extension/dist/host/branch.js +13 -0
  56. package/pi-extension/dist/host/canonical.js +12 -0
  57. package/pi-extension/dist/host/catalog.js +62 -0
  58. package/pi-extension/dist/host/config-validation.js +233 -0
  59. package/pi-extension/dist/host/config.js +361 -0
  60. package/pi-extension/dist/host/inbound.js +142 -0
  61. package/pi-extension/dist/host/projection-tags.js +60 -0
  62. package/pi-extension/dist/host/prompts.js +93 -0
  63. package/pi-extension/dist/{session-sources → host}/read-only-session.js +117 -1
  64. package/pi-extension/dist/{runtime → host}/request-history.js +125 -39
  65. package/pi-extension/dist/host/rows.js +25 -0
  66. package/pi-extension/dist/host/runtime-state.js +204 -0
  67. package/pi-extension/dist/host/section-keeper.js +85 -0
  68. package/pi-extension/dist/host/settings/freeflow-command.js +2323 -0
  69. package/pi-extension/dist/{settings/settings-tui.js → host/settings/settings-view.js} +205 -73
  70. package/pi-extension/dist/host/staging.js +28 -0
  71. package/pi-extension/dist/host/status.js +115 -0
  72. package/pi-extension/dist/index.js +1004 -188
  73. package/pi-extension/dist/messaging/config.js +27 -0
  74. package/pi-extension/dist/messaging/messaging.js +398 -0
  75. package/pi-extension/dist/messaging/registry.js +57 -0
  76. package/pi-extension/dist/messaging/render.js +90 -0
  77. package/pi-extension/dist/messaging/tools.js +109 -0
  78. package/pi-extension/dist/messaging/transport.js +101 -0
  79. package/pi-extension/dist/provider-support/cache/anchor.js +21 -0
  80. package/pi-extension/dist/provider-support/cache/anthropic.js +102 -0
  81. package/pi-extension/dist/provider-support/cache/health.js +73 -0
  82. package/pi-extension/dist/provider-support/cache/index.js +58 -0
  83. package/pi-extension/dist/provider-support/cache/keepalive.js +222 -0
  84. package/pi-extension/dist/provider-support/cache/monitor.js +17 -0
  85. package/pi-extension/dist/provider-support/cache/openai-responses.js +48 -0
  86. package/pi-extension/dist/provider-support/effort.js +13 -0
  87. package/pi-extension/dist/provider-support/index.js +44 -3
  88. package/pi-extension/dist/provider-support/{astra → openai}/adapter.js +46 -29
  89. package/pi-extension/dist/provider-support/{astra → openai}/history.js +10 -8
  90. package/pi-extension/dist/provider-support/{astra → openai}/index.js +8 -4
  91. package/pi-extension/dist/provider-support/{astra → openai}/session-state.js +29 -38
  92. package/pi-extension/dist/provider-support/routes.js +17 -0
  93. package/pi-extension/dist/task-memory/config.js +49 -0
  94. package/pi-extension/dist/task-memory/registry.js +119 -0
  95. package/pi-extension/dist/task-memory/render.js +107 -0
  96. package/pi-extension/dist/task-memory/runtime.js +994 -0
  97. package/pi-extension/dist/task-memory/tool.js +233 -0
  98. package/pi-extension/dist/tool-execution/apply-patch/grammar.js +23 -0
  99. package/pi-extension/dist/tool-execution/apply-patch/match.js +38 -0
  100. package/pi-extension/dist/tool-execution/apply-patch/parser.js +177 -0
  101. package/pi-extension/dist/tool-execution/apply-patch/plan.js +201 -0
  102. package/pi-extension/dist/tool-execution/apply-patch/tool.js +84 -0
  103. package/pi-extension/dist/tool-execution/background.js +430 -0
  104. package/pi-extension/dist/tool-execution/bash-guard.js +23 -0
  105. package/pi-extension/dist/tool-execution/config.js +33 -0
  106. package/pi-extension/dist/tool-execution/edit-messages.js +120 -0
  107. package/pi-extension/dist/tool-execution/file-state.js +138 -0
  108. package/pi-extension/dist/tool-execution/file-tracking.js +163 -0
  109. package/pi-extension/dist/tool-execution/guidance-notices.js +124 -0
  110. package/pi-extension/dist/tool-execution/prompt.js +54 -0
  111. package/pi-extension/dist/tool-execution/skill-reads.js +143 -0
  112. package/pi-extension/dist/tool-execution/tools.js +16 -0
  113. package/pi-extension/freeflow/README.md +9 -0
  114. package/runtime/prompts/cognitive-routing.md +4 -4
  115. package/runtime/prompts/core.md +29 -72
  116. package/runtime/prompts/interaction-contract.md +18 -8
  117. package/runtime/prompts/tool-execution.md +25 -0
  118. package/runtime/prompts/working-method.md +25 -0
  119. package/skills/deliver-work/SKILL.md +76 -0
  120. package/skills/deliver-work/references/branch-closeout.md +134 -0
  121. package/skills/deliver-work/references/commit.md +83 -0
  122. package/skills/deliver-work/references/launch.md +94 -0
  123. package/skills/deliver-work/references/release.md +85 -0
  124. package/skills/deliver-work/scripts/commit-state.mjs +139 -0
  125. package/skills/deliver-work/scripts/delivery-state.mjs +107 -0
  126. package/skills/deliver-work/scripts/lib/git-state.mjs +178 -0
  127. package/skills/deliver-work/scripts/push-route.mjs +122 -0
  128. package/skills/deliver-work/scripts/release-preflight.mjs +177 -0
  129. package/skills/discuss-work/SKILL.md +148 -0
  130. package/skills/{discuss → discuss-work}/references/discussion-continuity.md +13 -50
  131. package/skills/discuss-work/references/learning-action.md +36 -0
  132. package/skills/discuss-work/scripts/choices.mjs +101 -0
  133. package/skills/discuss-work/scripts/lib/choices.mjs +159 -0
  134. package/skills/evaluate-behavior/SKILL.md +107 -0
  135. package/skills/evaluate-behavior/references/authoring.md +381 -0
  136. package/skills/evaluate-behavior/references/coding-tasks.md +145 -0
  137. package/skills/evaluate-behavior/references/conversations-pipelines-judges.md +177 -0
  138. package/skills/evaluate-behavior/references/design-and-measurement.md +273 -0
  139. package/skills/evaluate-behavior/references/running-and-evidence.md +245 -0
  140. package/skills/evaluate-behavior/scripts/claude-guard.mjs +76 -0
  141. package/skills/evaluate-behavior/scripts/guard-rules.mjs +309 -0
  142. package/skills/evaluate-behavior/scripts/lib/agreement.mjs +90 -0
  143. package/skills/evaluate-behavior/scripts/lib/arms.mjs +55 -0
  144. package/skills/evaluate-behavior/scripts/lib/budget.mjs +87 -0
  145. package/skills/evaluate-behavior/scripts/lib/build-source.mjs +287 -0
  146. package/skills/evaluate-behavior/scripts/lib/clear.mjs +74 -0
  147. package/skills/evaluate-behavior/scripts/lib/container.mjs +395 -0
  148. package/skills/evaluate-behavior/scripts/lib/decision.mjs +418 -0
  149. package/skills/evaluate-behavior/scripts/lib/definitions.mjs +1072 -0
  150. package/skills/evaluate-behavior/scripts/lib/freeflow.mjs +103 -0
  151. package/skills/evaluate-behavior/scripts/lib/grade-command.mjs +591 -0
  152. package/skills/evaluate-behavior/scripts/lib/grade-comparison.mjs +90 -0
  153. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-interaction.mjs +19 -18
  154. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-support.mjs +32 -5
  155. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-workspace.mjs +28 -12
  156. package/skills/evaluate-behavior/scripts/lib/grade.mjs +101 -0
  157. package/skills/evaluate-behavior/scripts/lib/hooks.mjs +98 -0
  158. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/confinement.mjs +196 -0
  159. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/constraints.mjs +111 -0
  160. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/index.mjs +48 -0
  161. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/isolation.mjs +74 -0
  162. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/launch.mjs +123 -0
  163. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/model-call.mjs +73 -0
  164. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/recorder.mjs +178 -0
  165. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/session.mjs +494 -0
  166. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/skills.mjs +81 -0
  167. package/skills/evaluate-behavior/scripts/lib/hosts/index.mjs +31 -0
  168. package/skills/evaluate-behavior/scripts/lib/hosts/names.mjs +3 -0
  169. package/skills/evaluate-behavior/scripts/lib/hosts/pi/confinement.mjs +173 -0
  170. package/skills/evaluate-behavior/scripts/lib/hosts/pi/host.mjs +148 -0
  171. package/skills/evaluate-behavior/scripts/lib/hosts/pi/index.mjs +70 -0
  172. package/skills/evaluate-behavior/scripts/lib/hosts/pi/isolation.mjs +81 -0
  173. package/skills/evaluate-behavior/scripts/lib/hosts/pi/model-call.mjs +79 -0
  174. package/skills/evaluate-behavior/scripts/lib/hosts/pi/process.mjs +241 -0
  175. package/skills/{evaluate-skill/scripts/lib → evaluate-behavior/scripts/lib/hosts/pi}/rpc.mjs +144 -26
  176. package/skills/evaluate-behavior/scripts/lib/identity.mjs +82 -0
  177. package/skills/evaluate-behavior/scripts/lib/judge.mjs +688 -0
  178. package/skills/evaluate-behavior/scripts/lib/metrics.mjs +332 -0
  179. package/skills/evaluate-behavior/scripts/lib/model-call.mjs +17 -0
  180. package/skills/evaluate-behavior/scripts/lib/participants.mjs +616 -0
  181. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/process.mjs +22 -0
  182. package/skills/evaluate-behavior/scripts/lib/queue.mjs +54 -0
  183. package/skills/evaluate-behavior/scripts/lib/reference.mjs +343 -0
  184. package/skills/evaluate-behavior/scripts/lib/reuse.mjs +277 -0
  185. package/skills/evaluate-behavior/scripts/lib/review.mjs +933 -0
  186. package/skills/evaluate-behavior/scripts/lib/runner.mjs +732 -0
  187. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/sandbox.mjs +121 -14
  188. package/skills/evaluate-behavior/scripts/lib/scan-shell.mjs +457 -0
  189. package/skills/evaluate-behavior/scripts/lib/scan.mjs +479 -0
  190. package/skills/evaluate-behavior/scripts/lib/seal.mjs +91 -0
  191. package/skills/evaluate-behavior/scripts/lib/shell.mjs +560 -0
  192. package/skills/evaluate-behavior/scripts/lib/source-override.mjs +64 -0
  193. package/skills/evaluate-behavior/scripts/lib/subject-file.mjs +88 -0
  194. package/skills/{evaluate-skill/scripts/lib/pi.mjs → evaluate-behavior/scripts/lib/subject.mjs} +239 -262
  195. package/skills/evaluate-behavior/scripts/lib/task-copy.mjs +124 -0
  196. package/skills/evaluate-behavior/scripts/lib/trials.mjs +524 -0
  197. package/skills/evaluate-behavior/scripts/lib/validity.mjs +68 -0
  198. package/skills/evaluate-behavior/scripts/lib/view-html.mjs +454 -0
  199. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/view.mjs +218 -11
  200. package/skills/evaluate-behavior/scripts/pi-command-tool.mjs +159 -0
  201. package/skills/evaluate-behavior/scripts/pi-container-route.mjs +23 -0
  202. package/skills/evaluate-behavior/scripts/pi-guard.mjs +77 -0
  203. package/skills/evaluate-behavior/scripts/pi-login-refresh.mjs +31 -0
  204. package/skills/evaluate-behavior/scripts/skill-eval.mjs +586 -0
  205. package/skills/execute-work/SKILL.md +96 -72
  206. package/skills/execute-work/references/code-practices.md +27 -49
  207. package/skills/execute-work/references/design.md +271 -0
  208. package/skills/execute-work/references/diagnose.md +117 -0
  209. package/skills/execute-work/references/migration.md +108 -0
  210. package/skills/execute-work/references/simplify.md +65 -0
  211. package/skills/execute-work/scripts/self-review.mjs +422 -0
  212. package/skills/execute-work/scripts/touch-map.mjs +290 -0
  213. package/skills/review-work/SKILL.md +94 -64
  214. package/skills/review-work/references/adjudication.md +94 -0
  215. package/skills/review-work/references/artifact.md +198 -0
  216. package/skills/review-work/references/evals-and-judges.md +66 -0
  217. package/skills/review-work/references/work.md +177 -0
  218. package/skills/review-work/scripts/check-brief.mjs +128 -0
  219. package/skills/review-work/scripts/review-packet.mjs +233 -0
  220. package/skills/setup-freeflow/SKILL.md +69 -16
  221. package/skills/track-work/SKILL.md +135 -151
  222. package/skills/track-work/references/handoff.md +190 -0
  223. package/skills/track-work/references/projects.md +51 -0
  224. package/skills/track-work/references/working-record-format.md +201 -344
  225. package/skills/track-work/scripts/lib/document.mjs +79 -19
  226. package/skills/track-work/scripts/lib/format.mjs +18 -1
  227. package/skills/track-work/scripts/lib/recover.mjs +254 -0
  228. package/skills/track-work/scripts/lib/registry.mjs +656 -0
  229. package/skills/track-work/scripts/lib/search.mjs +40 -0
  230. package/skills/track-work/scripts/lib/store.mjs +53 -21
  231. package/skills/track-work/scripts/lib/transitions.mjs +79 -4
  232. package/skills/track-work/scripts/lib/views.mjs +50 -1
  233. package/skills/track-work/scripts/working-record.mjs +263 -22
  234. package/skills/verify-work/SKILL.md +64 -47
  235. package/skills/verify-work/references/feature-map.md +120 -0
  236. package/skills/verify-work/references/runtime-evidence.md +203 -0
  237. package/skills/verify-work/references/test-design.md +27 -6
  238. package/skills/verify-work/scripts/test-changes.mjs +246 -0
  239. package/skills/write-docs/SKILL.md +171 -0
  240. package/skills/write-docs/references/documentation-shapes.md +184 -0
  241. package/skills/write-docs/references/spec-and-plan-shapes.md +308 -0
  242. package/skills/write-docs/scripts/artifact-check.mjs +148 -0
  243. package/skills/write-docs/scripts/docs-check.mjs +229 -0
  244. package/skills/write-docs/scripts/plan-lint.mjs +123 -0
  245. package/skills/write-skill/SKILL.md +165 -165
  246. package/skills/write-skill/references/activation-and-wording.md +143 -0
  247. package/skills/write-skill/references/placement-and-dependencies.md +154 -0
  248. package/skills/write-skill/scripts/lib/compare.mjs +701 -0
  249. package/skills/write-skill/scripts/lib/similarity.mjs +56 -0
  250. package/skills/write-skill/scripts/lib/skill-author.mjs +197 -3
  251. package/skills/write-skill/scripts/skill-author.mjs +67 -8
  252. package/capabilities/context-virtualization/SKILL.md +0 -103
  253. package/capabilities/conversation-history/SKILL.md +0 -53
  254. package/hooks/tests/check-host-adapters.test.mjs +0 -82
  255. package/hooks/tests/check-runtime-context-hook.sh +0 -212
  256. package/pi-extension/dist/cognitive-routing-v2/projection.js +0 -330
  257. package/pi-extension/dist/cognitive-routing-v2/runtime.js +0 -2181
  258. package/pi-extension/dist/cognitive-routing-v2/tools.js +0 -60
  259. package/pi-extension/dist/context-virtualization/commands.js +0 -89
  260. package/pi-extension/dist/context-virtualization/projector.js +0 -42
  261. package/pi-extension/dist/context-virtualization/runtime.js +0 -378
  262. package/pi-extension/dist/context-virtualization/state.js +0 -57
  263. package/pi-extension/dist/context-virtualization/types.js +0 -10
  264. package/pi-extension/dist/conversation-history/passages.js +0 -66
  265. package/pi-extension/dist/conversation-history/ranking.js +0 -105
  266. package/pi-extension/dist/conversation-history/runtime.js +0 -226
  267. package/pi-extension/dist/conversation-history/search.js +0 -56
  268. package/pi-extension/dist/conversation-history/types.js +0 -1
  269. package/pi-extension/dist/freeflow-context/resolver.js +0 -120
  270. package/pi-extension/dist/freeflow-context/runtime.js +0 -80
  271. package/pi-extension/dist/freeflow-context/source-projector.js +0 -95
  272. package/pi-extension/dist/freeflow-context/tool.js +0 -314
  273. package/pi-extension/dist/freeflow-context/types.js +0 -12
  274. package/pi-extension/dist/freeflow-context/visibility.js +0 -28
  275. package/pi-extension/dist/integrations/mcp-config.js +0 -29
  276. package/pi-extension/dist/runtime/runtime-context.js +0 -754
  277. package/pi-extension/dist/runtime/runtime-identity.js +0 -5
  278. package/pi-extension/dist/session-sources/provenance.js +0 -32
  279. package/pi-extension/dist/settings/settings-ui.js +0 -1360
  280. package/pi-extension/dist/ui/text-component.js +0 -57
  281. package/pi-extension/src/cognitive-routing-v2/budget.ts +0 -42
  282. package/pi-extension/src/cognitive-routing-v2/config.ts +0 -173
  283. package/pi-extension/src/cognitive-routing-v2/projection.ts +0 -375
  284. package/pi-extension/src/cognitive-routing-v2/render.ts +0 -277
  285. package/pi-extension/src/cognitive-routing-v2/runtime.ts +0 -2241
  286. package/pi-extension/src/cognitive-routing-v2/schemas.ts +0 -180
  287. package/pi-extension/src/cognitive-routing-v2/state.ts +0 -839
  288. package/pi-extension/src/cognitive-routing-v2/tools.ts +0 -62
  289. package/pi-extension/src/cognitive-routing-v2/types.ts +0 -254
  290. package/pi-extension/src/context-virtualization/commands.ts +0 -111
  291. package/pi-extension/src/context-virtualization/projector.ts +0 -62
  292. package/pi-extension/src/context-virtualization/runtime.ts +0 -428
  293. package/pi-extension/src/context-virtualization/state.ts +0 -79
  294. package/pi-extension/src/context-virtualization/types.ts +0 -92
  295. package/pi-extension/src/conversation-history/passages.ts +0 -81
  296. package/pi-extension/src/conversation-history/ranking.ts +0 -155
  297. package/pi-extension/src/conversation-history/runtime.ts +0 -250
  298. package/pi-extension/src/conversation-history/search.ts +0 -79
  299. package/pi-extension/src/conversation-history/types.ts +0 -31
  300. package/pi-extension/src/freeflow-context/resolver.ts +0 -139
  301. package/pi-extension/src/freeflow-context/runtime.ts +0 -100
  302. package/pi-extension/src/freeflow-context/source-projector.ts +0 -106
  303. package/pi-extension/src/freeflow-context/tool.ts +0 -366
  304. package/pi-extension/src/freeflow-context/types.ts +0 -70
  305. package/pi-extension/src/freeflow-context/visibility.ts +0 -53
  306. package/pi-extension/src/index.ts +0 -390
  307. package/pi-extension/src/integrations/mcp-config.ts +0 -32
  308. package/pi-extension/src/provider-support/astra/adapter.ts +0 -108
  309. package/pi-extension/src/provider-support/astra/history.ts +0 -145
  310. package/pi-extension/src/provider-support/astra/index.ts +0 -12
  311. package/pi-extension/src/provider-support/astra/session-state.ts +0 -113
  312. package/pi-extension/src/provider-support/index.ts +0 -5
  313. package/pi-extension/src/runtime/request-history.ts +0 -243
  314. package/pi-extension/src/runtime/runtime-context.ts +0 -860
  315. package/pi-extension/src/runtime/runtime-identity.ts +0 -8
  316. package/pi-extension/src/session-sources/events.ts +0 -230
  317. package/pi-extension/src/session-sources/provenance.ts +0 -39
  318. package/pi-extension/src/session-sources/read-only-session.ts +0 -170
  319. package/pi-extension/src/session-sources/sources.ts +0 -334
  320. package/pi-extension/src/settings/settings-tui.ts +0 -501
  321. package/pi-extension/src/settings/settings-ui.ts +0 -1652
  322. package/pi-extension/src/ui/text-component.ts +0 -66
  323. package/pi-extension/tests/cache-reuse/history.test.js +0 -117
  324. package/pi-extension/tests/cache-reuse/native.test.js +0 -91
  325. package/pi-extension/tests/cache-reuse/settings.test.js +0 -211
  326. package/pi-extension/tests/cognitive-routing-v2/communication.test.js +0 -577
  327. package/pi-extension/tests/cognitive-routing-v2/compaction-input.test.js +0 -183
  328. package/pi-extension/tests/cognitive-routing-v2/delegation.test.js +0 -557
  329. package/pi-extension/tests/cognitive-routing-v2/native.test.js +0 -474
  330. package/pi-extension/tests/cognitive-routing-v2/performance.test.js +0 -87
  331. package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +0 -300
  332. package/pi-extension/tests/cognitive-routing-v2/projection.test.js +0 -241
  333. package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +0 -1034
  334. package/pi-extension/tests/cognitive-routing-v2/render.test.js +0 -244
  335. package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +0 -423
  336. package/pi-extension/tests/cognitive-routing-v2/stabilization.test.js +0 -521
  337. package/pi-extension/tests/cognitive-routing-v2/state.test.js +0 -586
  338. package/pi-extension/tests/context-virtualization/extension.test.js +0 -775
  339. package/pi-extension/tests/context-virtualization/projector.test.js +0 -110
  340. package/pi-extension/tests/context-virtualization/runtime.test.js +0 -230
  341. package/pi-extension/tests/conversation-history/runtime.test.js +0 -98
  342. package/pi-extension/tests/conversation-history/search.test.js +0 -97
  343. package/pi-extension/tests/fixtures/pi-host.js +0 -4
  344. package/pi-extension/tests/fixtures/routing-native.js +0 -191
  345. package/pi-extension/tests/integration/mode-free-core.test.js +0 -220
  346. package/pi-extension/tests/integration/pi-extension.test.js +0 -1447
  347. package/pi-extension/tests/integration/prompt-architecture.test.js +0 -447
  348. package/pi-extension/tests/integration/unaffected-regressions.test.js +0 -478
  349. package/pi-extension/tests/provider-support/astra-boundaries.test.js +0 -75
  350. package/pi-extension/tests/provider-support/astra.test.js +0 -306
  351. package/pi-extension/tests/provider-support/cli.test.js +0 -92
  352. package/pi-extension/tests/provider-support/native.test.js +0 -183
  353. package/pi-extension/tests/settings/settings-tui.test.js +0 -243
  354. package/pi-extension/tsconfig.json +0 -16
  355. package/plugin.json +0 -33
  356. package/runtime/prompts/context-virtualization.md +0 -3
  357. package/runtime/prompts/conversation-history.md +0 -3
  358. package/skills/action-selection/SKILL.md +0 -100
  359. package/skills/action-selection/references/trajectory-stalls.md +0 -71
  360. package/skills/bypass/SKILL.md +0 -153
  361. package/skills/commit-work/SKILL.md +0 -145
  362. package/skills/commit-work/references/git-state-edges.md +0 -94
  363. package/skills/commit-work/references/simple-push.md +0 -92
  364. package/skills/commit-work/references/staging-decisions.md +0 -106
  365. package/skills/decision-gate/SKILL.md +0 -106
  366. package/skills/design-for-depth/SKILL.md +0 -117
  367. package/skills/design-for-depth/references/design-pressure-signals.md +0 -182
  368. package/skills/design-for-depth/references/implementation-decisions.md +0 -76
  369. package/skills/design-for-depth/references/interface-design-loop.md +0 -136
  370. package/skills/design-for-depth/references/module-and-dependency-design.md +0 -147
  371. package/skills/design-for-depth/references/software-design-philosophy.md +0 -202
  372. package/skills/design-for-depth/references/state-and-failure-boundaries.md +0 -136
  373. package/skills/diagnose-failure/SKILL.md +0 -125
  374. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +0 -48
  375. package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +0 -51
  376. package/skills/discuss/SKILL.md +0 -116
  377. package/skills/evaluate-skill/SKILL.md +0 -219
  378. package/skills/evaluate-skill/references/evaluation-definition-schema.md +0 -385
  379. package/skills/evaluate-skill/references/evaluation-design.md +0 -112
  380. package/skills/evaluate-skill/references/execution-and-evidence.md +0 -90
  381. package/skills/evaluate-skill/references/review-and-revision.md +0 -92
  382. package/skills/evaluate-skill/scripts/lib/definitions.mjs +0 -407
  383. package/skills/evaluate-skill/scripts/lib/grade-comparison.mjs +0 -60
  384. package/skills/evaluate-skill/scripts/lib/grade.mjs +0 -64
  385. package/skills/evaluate-skill/scripts/lib/runner.mjs +0 -368
  386. package/skills/evaluate-skill/scripts/pi-guard.mjs +0 -164
  387. package/skills/evaluate-skill/scripts/skill-eval.mjs +0 -102
  388. package/skills/execute-work/references/execute-work-edges.md +0 -68
  389. package/skills/finish-branch/SKILL.md +0 -117
  390. package/skills/finish-branch/references/integration-options.md +0 -80
  391. package/skills/handoff/SKILL.md +0 -82
  392. package/skills/handoff/references/handoff-templates.md +0 -150
  393. package/skills/launch-work/SKILL.md +0 -116
  394. package/skills/launch-work/references/launch-readiness.md +0 -85
  395. package/skills/migration-work/SKILL.md +0 -115
  396. package/skills/migration-work/references/migration-lifecycle.md +0 -82
  397. package/skills/release-work/SKILL.md +0 -118
  398. package/skills/release-work/references/release-evidence.md +0 -78
  399. package/skills/review-artifact/SKILL.md +0 -142
  400. package/skills/review-artifact/references/adjudicate-artifact-review.md +0 -101
  401. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +0 -159
  402. package/skills/review-work/references/adjudicate-work-review.md +0 -97
  403. package/skills/review-work/references/independent-work-reviewer-contract.md +0 -139
  404. package/skills/review-work/references/security-risk-lens.md +0 -69
  405. package/skills/setup-freeflow/references/activation-contract.md +0 -109
  406. package/skills/setup-freeflow/references/host-setup.md +0 -67
  407. package/skills/simplify-code/SKILL.md +0 -85
  408. package/skills/simplify-code/references/simplification-patterns.md +0 -70
  409. package/skills/verify-work/references/browser-runtime-evidence.md +0 -55
  410. package/skills/verify-work/references/integration-evidence.md +0 -47
  411. package/skills/verify-work/references/performance-evidence.md +0 -57
  412. package/skills/workflow/SKILL.md +0 -127
  413. package/skills/workflow/references/domain-skill-composition.md +0 -43
  414. package/skills/write-plan/SKILL.md +0 -92
  415. package/skills/write-plan/references/plan-shapes.md +0 -156
  416. package/skills/write-skill/references/activation-boundaries.md +0 -104
  417. package/skills/write-skill/references/agent-first-instructions.md +0 -121
  418. package/skills/write-skill/references/progressive-disclosure.md +0 -127
  419. package/skills/write-skill/references/skill-dependency-graphs.md +0 -190
  420. package/skills/write-spec/SKILL.md +0 -92
  421. package/skills/write-spec/references/artifact-standards.md +0 -86
  422. package/skills/write-spec/references/decision-records.md +0 -78
  423. package/skills/write-spec/references/spec-shapes.md +0 -143
  424. /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/budget.js +0 -0
  425. /package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/evidence.mjs +0 -0
  426. /package/skills/{evaluate-skill → evaluate-behavior}/scripts/pi-observer.mjs +0 -0
@@ -0,0 +1,395 @@
1
+ // Container environments (optional): a configuration may run the subject's shell and the group's command graders
2
+ // in a digest-pinned image through the standard `docker` command line (Docker Desktop, Colima, OrbStack or Podman
3
+ // all provide it). Docker is looked for only when a group declares a container; an image is never pulled, and a
4
+ // missing CLI, daemon or image refuses the group with what to install or pull. A container environment is a shell
5
+ // isolation like any other: the shell tier's probes verify it before any subject runs.
6
+
7
+ import { execFile } from "node:child_process";
8
+ import { randomBytes } from "node:crypto";
9
+ import { mkdir, mkdtemp, realpath, rm, writeFile } from "node:fs/promises";
10
+ import path from "node:path";
11
+ import { promisify } from "node:util";
12
+
13
+ const run = promisify(execFile);
14
+ const IMAGE = /^[^\s@]+@sha256:[0-9a-f]{64}$/;
15
+ const DOCKER_TIMEOUT_MS = 30_000;
16
+ /** Resource limits for every container the evaluator starts. */
17
+ export const CONTAINER_LIMITS = Object.freeze({ memory: "4g", cpus: "2", pids: "1024" });
18
+ const KEEP_ALIVE = "trap 'exit 0' TERM INT; while :; do sleep 3600 & wait $!; done";
19
+ // Run as root in a subject's container after it starts: replaces the declared workdir (empty, or absent, in the image)
20
+ // with a link to the workspace's mount at its host path. The image's own references to the workdir, such as
21
+ // GIT_WORK_TREE=/testbed or a script that changes into it, then reach the workspace under the same canonical path the
22
+ // shell starts in. A second mount would not do: Git compares the working directory with its work tree by path, so from
23
+ // a subfolder of one mount it would resolve paths against the other's root.
24
+ const LINK_WORKDIR = [
25
+ 'if [ -d "$1" ] && [ ! -L "$1" ]; then',
26
+ ' rmdir "$1" 2>/dev/null || { echo "the image has files at $1, where the workspace goes; use an image whose workdir is empty" >&2; exit 1; }',
27
+ "fi",
28
+ 'mkdir -p "$(dirname "$1")" && ln -s "$2" "$1"',
29
+ ].join("\n");
30
+
31
+ function isPlainObject(value) {
32
+ return value !== null && typeof value === "object" && !Array.isArray(value);
33
+ }
34
+
35
+ /**
36
+ * Validates a configuration's `environment`: `{ kind: "container", image: "<name>@sha256:<digest>", workdir,
37
+ * network: "none" }`.
38
+ *
39
+ * @param {any} value
40
+ * @param {string} label
41
+ * @param {(code: string, message: string) => never} fail
42
+ */
43
+ export function validateContainerEnvironment(value, label, fail) {
44
+ if (!isPlainObject(value)) fail("invalid-definition", `${label} must be an object`);
45
+ for (const key of Object.keys(value)) {
46
+ if (!["kind", "image", "workdir", "network"].includes(key)) {
47
+ fail("invalid-definition", `${label} contains unsupported field ${key}`);
48
+ }
49
+ }
50
+ if (value.kind !== "container") fail("invalid-definition", `${label}.kind must be container`);
51
+ if (typeof value.image !== "string" || !IMAGE.test(value.image)) {
52
+ fail(
53
+ "invalid-definition",
54
+ `${label}.image must be pinned by digest, such as name@sha256:<64 hex characters>; a tag can change under the run`,
55
+ );
56
+ }
57
+ const workdir = value.workdir;
58
+ if (
59
+ typeof workdir !== "string" ||
60
+ !workdir.startsWith("/") ||
61
+ workdir === "/" ||
62
+ workdir.endsWith("/") ||
63
+ workdir
64
+ .split("/")
65
+ .slice(1)
66
+ .some((part) => part === "" || part === "." || part === "..")
67
+ ) {
68
+ fail("invalid-definition", `${label}.workdir must be an absolute path such as /testbed`);
69
+ }
70
+ if (value.network !== "none") fail("invalid-definition", `${label}.network must be "none"`);
71
+ }
72
+
73
+ /**
74
+ * The shell a container environment provides: bash runs in the container, which shows the workspace at its host
75
+ * path, where bash starts, and at workdir, a link to it; `workspace` is that guest name.
76
+ */
77
+ export function containerShell(environment) {
78
+ if (environment?.kind !== "container") return undefined;
79
+ const container = { image: environment.image, workdir: environment.workdir, network: environment.network };
80
+ return { isolation: "container", network: environment.network, workspace: environment.workdir, container };
81
+ }
82
+
83
+ /** The docker CLI's endpoint, so a subject's isolated shell (which has no ~/.docker) reaches the same daemon. */
84
+ export async function dockerHost() {
85
+ if (process.env.DOCKER_HOST) return process.env.DOCKER_HOST;
86
+ try {
87
+ const { stdout } = await run("docker", ["context", "inspect", "--format", "{{.Endpoints.docker.Host}}"], {
88
+ timeout: DOCKER_TIMEOUT_MS,
89
+ });
90
+ return stdout.trim() || null;
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+
96
+ const DOCKER_INSTALL = {
97
+ macos: "brew install colima docker && colima start (or install Docker Desktop or OrbStack)",
98
+ apt: "sudo apt-get install docker.io && sudo systemctl start docker (or install Docker Engine from docs.docker.com, or Podman with podman-docker)",
99
+ dnf: "sudo dnf install podman-docker (or Docker Engine from docs.docker.com, then sudo systemctl start docker)",
100
+ };
101
+ const DOCKER_START = {
102
+ macos: "colima start, or open Docker Desktop or OrbStack",
103
+ apt: "sudo systemctl start docker",
104
+ dnf: "sudo systemctl start docker (or podman machine start)",
105
+ };
106
+
107
+ /**
108
+ * What is missing for a container environment, in the shell tier's refusal shape: the docker CLI, its daemon, the
109
+ * image present locally (with its pull command and, when the registry reports it, its download size), and, given the
110
+ * folder workspaces will live under, that the engine shares that folder with its containers.
111
+ *
112
+ * @param {{ image: string, workdir: string }} container
113
+ * @param {string | null} manager "macos", "apt", "dnf" or null
114
+ * @param {string | null} [workspaceRoot] the folder the run's workspaces are created under
115
+ */
116
+ export async function missingContainerPrerequisites(container, manager, workspaceRoot = null) {
117
+ const reason = "the configuration runs the subject's shell and the command graders in a container";
118
+ try {
119
+ await run("docker", ["version", "--format", "{{.Server.Version}}"], { timeout: DOCKER_TIMEOUT_MS });
120
+ } catch (error) {
121
+ if (error?.code === "ENOENT") {
122
+ return [
123
+ {
124
+ name: "a docker command line",
125
+ need: "docker on PATH (Docker Desktop, Colima, OrbStack or Podman's docker)",
126
+ found: null,
127
+ reason,
128
+ install: manager === null ? null : DOCKER_INSTALL[manager],
129
+ download: null,
130
+ },
131
+ ];
132
+ }
133
+ return [
134
+ {
135
+ name: "a running container engine",
136
+ need: "a docker daemon that answers `docker version`",
137
+ found: firstLine(error?.stderr) ?? "no daemon answered",
138
+ reason,
139
+ install: manager === null ? null : `start it: ${DOCKER_START[manager]}`,
140
+ download: null,
141
+ },
142
+ ];
143
+ }
144
+ try {
145
+ await run("docker", ["image", "inspect", container.image], { timeout: DOCKER_TIMEOUT_MS });
146
+ } catch {
147
+ const size = await imageDownloadSize(container.image);
148
+ return [
149
+ {
150
+ name: `the image ${container.image}`,
151
+ need: "the image present locally (the evaluator never pulls one)",
152
+ found: null,
153
+ reason,
154
+ install: `docker pull ${container.image}`,
155
+ download: size === null ? null : `about ${formatBytes(size)} compressed, from docker manifest inspect`,
156
+ },
157
+ ];
158
+ }
159
+ const hidden = workspaceRoot === null ? null : await hiddenWorkspace(container, workspaceRoot);
160
+ if (hidden === null) return [];
161
+ return [
162
+ {
163
+ name: "a workspace folder the container engine can see",
164
+ need: `containers to see folders under ${workspaceRoot}`,
165
+ found: hidden,
166
+ reason,
167
+ install:
168
+ "a VM-based engine shares only some host folders (Colima, by default, only your home folder): run from a folder it shares (for check-tasks, set TMPDIR to one), pass --results there, or add the folder to the engine's mounts",
169
+ download: null,
170
+ },
171
+ ];
172
+ }
173
+
174
+ /**
175
+ * Whether a folder under `root` reaches containers. A VM-based engine (Colima, Docker Desktop, OrbStack, Podman's
176
+ * machine) mounts only the host folders it shares, and a workspace elsewhere appears empty inside the container, so
177
+ * graders and shells would run on nothing. Writes a marker in a fresh folder under `root` and reads it back through
178
+ * the image. Returns null when it arrives (or when the image has no `cat` to tell), else what went wrong.
179
+ */
180
+ async function hiddenWorkspace(container, root) {
181
+ await mkdir(root, { recursive: true });
182
+ const probe = await mkdtemp(path.join(root, ".skill-eval-share-"));
183
+ const marker = randomBytes(8).toString("hex");
184
+ try {
185
+ await writeFile(path.join(probe, "marker"), marker);
186
+ const { stdout } = await run(
187
+ "docker",
188
+ [
189
+ "run",
190
+ "--rm",
191
+ "--label",
192
+ "skill-eval=1",
193
+ "--network",
194
+ "none",
195
+ "--volume",
196
+ `${probe}:${container.workdir}:ro`,
197
+ "--workdir",
198
+ container.workdir,
199
+ "--entrypoint",
200
+ "cat",
201
+ container.image,
202
+ "marker",
203
+ ],
204
+ { timeout: DOCKER_TIMEOUT_MS },
205
+ );
206
+ if (stdout.trim() === marker) return null;
207
+ return `the container read something other than the marker written in ${probe}`;
208
+ } catch (error) {
209
+ const detail = firstLine(error?.stderr) ?? String(error?.message ?? error);
210
+ // An image without `cat` cannot answer the question; it is not evidence of a hidden folder.
211
+ if (/executable file not found|no such file or directory: unknown|exec: "cat"/i.test(detail)) return null;
212
+ return `a marker written in ${probe} did not reach the container: ${detail}`;
213
+ } finally {
214
+ await rm(probe, { recursive: true, force: true });
215
+ }
216
+ }
217
+
218
+ /** Compressed layer bytes for this machine's platform from `docker manifest inspect -v`, or null when unknown. */
219
+ async function imageDownloadSize(image) {
220
+ let parsed;
221
+ try {
222
+ const { stdout } = await run("docker", ["manifest", "inspect", "-v", image], { timeout: DOCKER_TIMEOUT_MS });
223
+ parsed = JSON.parse(stdout);
224
+ } catch {
225
+ return null;
226
+ }
227
+ const entries = Array.isArray(parsed) ? parsed : [parsed];
228
+ const architecture = process.arch === "arm64" ? "arm64" : "amd64";
229
+ const entry =
230
+ entries.find(
231
+ (candidate) =>
232
+ candidate?.Descriptor?.platform?.os === "linux" && candidate.Descriptor.platform.architecture === architecture,
233
+ ) ?? entries[0];
234
+ const layers = entry?.SchemaV2Manifest?.layers ?? entry?.OCIManifest?.layers;
235
+ if (!Array.isArray(layers) || layers.length === 0) return null;
236
+ return layers.reduce((total, layer) => total + (Number.isFinite(layer?.size) ? layer.size : 0), 0);
237
+ }
238
+
239
+ function formatBytes(bytes) {
240
+ return bytes >= 1024 * 1024
241
+ ? `${Math.round(bytes / (1024 * 1024))} MB`
242
+ : `${Math.max(1, Math.round(bytes / 1024))} KB`;
243
+ }
244
+
245
+ function firstLine(value) {
246
+ const line = String(value ?? "")
247
+ .split("\n")
248
+ .find((entry) => entry.trim() !== "");
249
+ return line?.trim() ?? null;
250
+ }
251
+
252
+ // What the docker client needs from the user's environment to reach its engine, and nothing else.
253
+ const DOCKER_CLIENT_KEYS = [
254
+ "PATH",
255
+ "HOME",
256
+ "USER",
257
+ "LANG",
258
+ "TMPDIR",
259
+ "XDG_RUNTIME_DIR",
260
+ "DOCKER_HOST",
261
+ "DOCKER_CONTEXT",
262
+ "DOCKER_CONFIG",
263
+ "DOCKER_CERT_PATH",
264
+ "DOCKER_TLS_VERIFY",
265
+ ];
266
+
267
+ /** The environment for a docker client the evaluator runs itself (a grader's `docker run`). */
268
+ export function dockerClientEnvironment() {
269
+ return Object.fromEntries(
270
+ DOCKER_CLIENT_KEYS.flatMap((key) => (process.env[key] === undefined ? [] : [[key, process.env[key]]])),
271
+ );
272
+ }
273
+
274
+ export function containerName(purpose) {
275
+ return `skill-eval-${purpose}-${randomBytes(6).toString("hex")}`;
276
+ }
277
+
278
+ /**
279
+ * The options every evaluator container shares: limits, the declared network, the workspace mounted read-write at
280
+ * `at` and used as the working directory, and each declared resource (a skill, context, Freeflow's package) read-only
281
+ * at its own host path, so a path the subject was given, such as `node <skill>/scripts/x.mjs`, resolves in the
282
+ * container exactly as it does for `read`.
283
+ */
284
+ function runOptions(container, { workspace, at, resources = [] }) {
285
+ const mounts = resources.flatMap((resource) => ["--volume", `${resource}:${resource}:ro`]);
286
+ return [
287
+ "--label",
288
+ "skill-eval=1",
289
+ "--network",
290
+ container.network,
291
+ "--memory",
292
+ CONTAINER_LIMITS.memory,
293
+ "--cpus",
294
+ CONTAINER_LIMITS.cpus,
295
+ "--pids-limit",
296
+ CONTAINER_LIMITS.pids,
297
+ "--volume",
298
+ `${workspace}:${at}`,
299
+ ...mounts,
300
+ "--workdir",
301
+ at,
302
+ ];
303
+ }
304
+
305
+ /**
306
+ * Creates and starts a long-lived container for one subject (or one probe run). The workspace is mounted at its own
307
+ * canonical host path, which is also the shell's working directory, so `pwd`, the working directory the host tells
308
+ * the model and the paths its file tools take all name one directory; the declared workdir (such as /testbed) is a
309
+ * link to it. Each of `resources` (absolute host directories) is mounted read-only at the same path. The handle's
310
+ * `workdir` is where bash and declared commands run. `remove()` force-removes the container and is safe to call more
311
+ * than once; callers call it in `finally`, so a cancelled run removes it too.
312
+ */
313
+ export async function startContainer({ container, workspace, resources = [], purpose = "subject" }) {
314
+ const name = containerName(purpose);
315
+ const host = await dockerHost();
316
+ // The host process (Pi, Claude Code) reports its working directory resolved, so the mount uses the same spelling.
317
+ const workdir = await realpath(workspace);
318
+ let removed = false;
319
+ const remove = async () => {
320
+ if (removed) return;
321
+ removed = true;
322
+ await run("docker", ["rm", "--force", name], { timeout: DOCKER_TIMEOUT_MS }).catch(() => {});
323
+ };
324
+ let step = "starting a container";
325
+ try {
326
+ await run(
327
+ "docker",
328
+ [
329
+ "create",
330
+ "--name",
331
+ name,
332
+ ...runOptions(container, { workspace: workdir, at: workdir, resources }),
333
+ "--entrypoint",
334
+ "sh",
335
+ container.image,
336
+ "-c",
337
+ KEEP_ALIVE,
338
+ ],
339
+ { timeout: DOCKER_TIMEOUT_MS },
340
+ );
341
+ await run("docker", ["start", name], { timeout: DOCKER_TIMEOUT_MS });
342
+ step = `linking ${container.workdir} to the workspace in a container`;
343
+ await run("docker", ["exec", "--user", "0", name, "sh", "-c", LINK_WORKDIR, "sh", container.workdir, workdir], {
344
+ timeout: DOCKER_TIMEOUT_MS,
345
+ });
346
+ } catch (error) {
347
+ await remove();
348
+ throw new Error(`${step} from ${container.image} failed: ${firstLine(error?.stderr) ?? error.message}`);
349
+ }
350
+ return { name, host, workdir, remove };
351
+ }
352
+
353
+ /** The value the guard reads to route bash into a running container. */
354
+ export function containerTarget(handle) {
355
+ return JSON.stringify({ name: handle.name, host: handle.host, workdir: handle.workdir });
356
+ }
357
+
358
+ /** The running container from `containerTarget`'s value, or null when there is none. */
359
+ export function parseContainerTarget(value) {
360
+ if (!value) return null;
361
+ try {
362
+ const parsed = JSON.parse(value);
363
+ return typeof parsed?.name === "string" && typeof parsed.workdir === "string" ? parsed : null;
364
+ } catch {
365
+ return null;
366
+ }
367
+ }
368
+
369
+ /** The docker client argv that runs a declared command's argv inside the running container, where bash runs. */
370
+ export function containerExecArgv(target, argv) {
371
+ const host = typeof target.host === "string" && target.host !== "" ? ["--host", target.host] : [];
372
+ return ["docker", ...host, "exec", "--workdir", target.workdir, target.name, ...argv];
373
+ }
374
+
375
+ /**
376
+ * The argv that runs a command grader in a fresh container on the graded copy, with the same limits and network.
377
+ * A grader is the author's, not the subject's, so it sees the copy at the declared workdir, where its argv expects
378
+ * it. The caller removes the container by name afterwards, so a timeout or cancellation leaves nothing running.
379
+ */
380
+ export function graderArgv({ container, copy, argv, name }) {
381
+ return [
382
+ "docker",
383
+ "run",
384
+ "--rm",
385
+ "--name",
386
+ name,
387
+ ...runOptions(container, { workspace: copy, at: container.workdir }),
388
+ container.image,
389
+ ...argv,
390
+ ];
391
+ }
392
+
393
+ export async function removeContainer(name) {
394
+ await run("docker", ["rm", "--force", name], { timeout: DOCKER_TIMEOUT_MS }).catch(() => {});
395
+ }