cool-workflow 0.1.97 → 0.2.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 (309) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +11 -2
  4. package/apps/architecture-review/app.json +1 -1
  5. package/apps/architecture-review-fast/app.json +1 -1
  6. package/apps/end-to-end-golden-path/app.json +1 -1
  7. package/apps/pr-review-fix-ci/app.json +1 -1
  8. package/apps/release-cut/app.json +1 -1
  9. package/apps/research-synthesis/app.json +1 -1
  10. package/dist/cli/dispatch.js +236 -0
  11. package/dist/cli/entry.js +120 -0
  12. package/dist/cli/io.js +21 -4
  13. package/dist/cli/parseargv.js +157 -0
  14. package/dist/cli.js +6 -33
  15. package/dist/core/capability-table.js +3534 -0
  16. package/dist/core/format/help.js +314 -0
  17. package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
  18. package/dist/core/hash.js +137 -0
  19. package/dist/core/multi-agent/candidate-scoring.js +219 -0
  20. package/dist/core/multi-agent/collaboration.js +481 -0
  21. package/dist/core/multi-agent/coordinator.js +515 -0
  22. package/dist/core/multi-agent/eval-replay.js +306 -0
  23. package/dist/core/multi-agent/runtime.js +929 -0
  24. package/dist/core/multi-agent/topology.js +298 -0
  25. package/dist/core/multi-agent/trust-policy.js +197 -0
  26. package/dist/core/pipeline/commit-gate.js +320 -0
  27. package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
  28. package/dist/core/pipeline/dispatch.js +103 -0
  29. package/dist/core/pipeline/drive-decide.js +227 -0
  30. package/dist/core/pipeline/error-feedback.js +161 -0
  31. package/dist/core/pipeline/loop-expansion.js +124 -0
  32. package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
  33. package/dist/core/pipeline/runner.js +230 -0
  34. package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
  35. package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
  36. package/dist/core/state/node-projection.js +95 -0
  37. package/dist/core/state/node-snapshot.js +230 -0
  38. package/dist/core/state/run-paths.js +93 -0
  39. package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
  40. package/dist/core/state/schema.js +50 -0
  41. package/dist/core/state/state-explosion/digest.js +243 -0
  42. package/dist/core/state/state-explosion/graph.js +527 -0
  43. package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
  44. package/dist/core/state/state-explosion/report.js +187 -0
  45. package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
  46. package/dist/{state-node.js → core/state/state-node.js} +90 -113
  47. package/dist/core/state/types.js +19 -0
  48. package/dist/{validation.js → core/state/validation.js} +64 -131
  49. package/dist/core/trust/evidence-grounding.js +134 -0
  50. package/dist/core/trust/ledger.js +199 -0
  51. package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
  52. package/dist/core/trust/telemetry-ledger.js +127 -0
  53. package/dist/core/types.js +20 -0
  54. package/dist/core/version.js +16 -0
  55. package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
  56. package/dist/mcp/dispatch.js +104 -0
  57. package/dist/mcp/server.js +144 -0
  58. package/dist/mcp-server.js +6 -84
  59. package/dist/{agent-config.js → shell/agent-config.js} +118 -71
  60. package/dist/shell/app-run-cli.js +88 -0
  61. package/dist/shell/audit-cli.js +259 -0
  62. package/dist/shell/audit-provenance.js +83 -0
  63. package/dist/shell/candidate-scoring-io.js +459 -0
  64. package/dist/shell/collaboration-io.js +264 -0
  65. package/dist/shell/commit-summary.js +104 -0
  66. package/dist/shell/commit.js +290 -0
  67. package/dist/shell/coordinator-io.js +476 -0
  68. package/dist/shell/demo-cli.js +19 -0
  69. package/dist/shell/dispatch.js +162 -0
  70. package/dist/{doctor.js → shell/doctor.js} +123 -56
  71. package/dist/shell/drive.js +873 -0
  72. package/dist/shell/error-feedback-io.js +305 -0
  73. package/dist/shell/eval-io.js +473 -0
  74. package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
  75. package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
  76. package/dist/shell/exec-backend-cli.js +88 -0
  77. package/dist/shell/execution-backend/agent.js +475 -0
  78. package/dist/shell/execution-backend/ci.js +15 -0
  79. package/dist/shell/execution-backend/container.js +69 -0
  80. package/dist/shell/execution-backend/envelopes.js +55 -0
  81. package/dist/shell/execution-backend/local.js +113 -0
  82. package/dist/shell/execution-backend/probes.js +175 -0
  83. package/dist/shell/execution-backend/registry.js +402 -0
  84. package/dist/shell/execution-backend/remote.js +128 -0
  85. package/dist/shell/execution-backend/types.js +11 -0
  86. package/dist/shell/feedback-cli.js +81 -0
  87. package/dist/shell/feedback-operations.js +48 -0
  88. package/dist/shell/fs-atomic.js +276 -0
  89. package/dist/shell/harness.js +98 -0
  90. package/dist/shell/ledger-cli.js +212 -0
  91. package/dist/shell/ledger-io.js +169 -0
  92. package/dist/shell/man-cli.js +89 -0
  93. package/dist/shell/metrics-cli.js +98 -0
  94. package/dist/shell/multi-agent-cli.js +1002 -0
  95. package/dist/shell/multi-agent-host.js +563 -0
  96. package/dist/shell/multi-agent-io.js +387 -0
  97. package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
  98. package/dist/shell/node-store.js +124 -0
  99. package/dist/{observability/format.js → shell/observability-format.js} +7 -1
  100. package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
  101. package/dist/{observability.js → shell/observability.js} +159 -332
  102. package/dist/{onramp.js → shell/onramp.js} +11 -0
  103. package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
  104. package/dist/shell/operator-ux.js +431 -0
  105. package/dist/shell/orchestrator.js +231 -0
  106. package/dist/shell/pipeline-cli.js +667 -0
  107. package/dist/shell/pipeline.js +217 -0
  108. package/dist/shell/reclamation-io.js +1366 -0
  109. package/dist/shell/registry-cli.js +329 -0
  110. package/dist/{remote-source.js → shell/remote-source.js} +12 -5
  111. package/dist/shell/report-cli.js +101 -0
  112. package/dist/shell/report-view-cli.js +117 -0
  113. package/dist/{orchestrator → shell}/report.js +289 -282
  114. package/dist/shell/reporter.js +62 -0
  115. package/dist/shell/run-export-cli.js +106 -0
  116. package/dist/shell/run-export.js +680 -0
  117. package/dist/shell/run-registry-io.js +1014 -0
  118. package/dist/shell/run-store.js +164 -0
  119. package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
  120. package/dist/{scheduler.js → shell/scheduler-io.js} +248 -48
  121. package/dist/shell/scheduling-io.js +311 -0
  122. package/dist/shell/state-cli.js +181 -0
  123. package/dist/shell/state-explosion-cli.js +197 -0
  124. package/dist/shell/telemetry-cli.js +85 -0
  125. package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
  126. package/dist/shell/telemetry-ledger-io.js +132 -0
  127. package/dist/{term.js → shell/term.js} +36 -46
  128. package/dist/shell/topology-io.js +361 -0
  129. package/dist/shell/trust-audit.js +471 -0
  130. package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
  131. package/dist/shell/verifier.js +48 -0
  132. package/dist/shell/workbench-host.js +250 -0
  133. package/dist/shell/workbench-text.js +18 -0
  134. package/dist/shell/workbench.js +175 -0
  135. package/dist/shell/worker-cli.js +124 -0
  136. package/dist/shell/worker-isolation.js +852 -0
  137. package/dist/shell/workflow-app-loader.js +650 -0
  138. package/docs/agent-delegation-drive.7.md +4 -0
  139. package/docs/cli-mcp-parity.7.md +284 -211
  140. package/docs/contract-migration-tooling.7.md +4 -0
  141. package/docs/control-plane-scheduling.7.md +4 -0
  142. package/docs/cross-agent-ledger.7.md +217 -0
  143. package/docs/designs/handoff-ledger.md +145 -0
  144. package/docs/durable-state-and-locking.7.md +4 -0
  145. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  146. package/docs/execution-backends.7.md +4 -0
  147. package/docs/handoff-setup.md +120 -0
  148. package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
  149. package/docs/multi-agent-eval-replay-harness.7.md +4 -0
  150. package/docs/multi-agent-operator-ux.7.md +4 -0
  151. package/docs/node-snapshot-diff-replay.7.md +4 -0
  152. package/docs/observability-cost-accounting.7.md +4 -0
  153. package/docs/project-index.md +143 -71
  154. package/docs/real-execution-backends.7.md +4 -0
  155. package/docs/release-and-migration.7.md +4 -0
  156. package/docs/release-tooling.7.md +4 -0
  157. package/docs/run-registry-control-plane.7.md +4 -0
  158. package/docs/run-retention-reclamation.7.md +25 -0
  159. package/docs/state-explosion-management.7.md +4 -0
  160. package/docs/team-collaboration.7.md +4 -0
  161. package/docs/web-desktop-workbench.7.md +4 -0
  162. package/manifest/plugin.manifest.json +1 -1
  163. package/manifest/source-context-profiles.json +9 -13
  164. package/package.json +1 -1
  165. package/scripts/agents/codex-agent.js +34 -4
  166. package/scripts/agents/cw-attest-wrap.js +2 -2
  167. package/scripts/bump-version.js +4 -3
  168. package/scripts/canonical-apps.js +4 -4
  169. package/scripts/children/batch-delegate-child.js +30 -10
  170. package/scripts/dogfood-architecture-review.js +2 -3
  171. package/scripts/dogfood-release.js +3 -3
  172. package/scripts/gen-parity-doc.js +15 -2
  173. package/scripts/golden-path.js +4 -4
  174. package/scripts/onramp-check.js +1 -1
  175. package/scripts/parity-check.js +38 -21
  176. package/scripts/release-flow.js +9 -3
  177. package/scripts/sync-project-index.js +51 -27
  178. package/scripts/validate-run-state-schema.js +2 -2
  179. package/scripts/version-sync-check.js +30 -30
  180. package/dist/candidate-scoring.js +0 -729
  181. package/dist/capability-core.js +0 -1186
  182. package/dist/capability-registry.js +0 -879
  183. package/dist/cli/command-surface.js +0 -490
  184. package/dist/cli/format.js +0 -56
  185. package/dist/cli/handlers/audit.js +0 -82
  186. package/dist/cli/handlers/blackboard.js +0 -81
  187. package/dist/cli/handlers/candidate.js +0 -40
  188. package/dist/cli/handlers/clones.js +0 -34
  189. package/dist/cli/handlers/collaboration.js +0 -61
  190. package/dist/cli/handlers/eval.js +0 -40
  191. package/dist/cli/handlers/maintenance.js +0 -107
  192. package/dist/cli/handlers/multi-agent.js +0 -165
  193. package/dist/cli/handlers/node.js +0 -41
  194. package/dist/cli/handlers/operational.js +0 -155
  195. package/dist/cli/handlers/operator.js +0 -146
  196. package/dist/cli/handlers/registry.js +0 -68
  197. package/dist/cli/handlers/run.js +0 -153
  198. package/dist/cli/handlers/scheduling.js +0 -132
  199. package/dist/cli/handlers/workbench.js +0 -41
  200. package/dist/cli/handlers/worker.js +0 -45
  201. package/dist/cli/run-summary.js +0 -45
  202. package/dist/clones.js +0 -162
  203. package/dist/collaboration.js +0 -726
  204. package/dist/commit.js +0 -592
  205. package/dist/compare.js +0 -18
  206. package/dist/coordinator/classify.js +0 -45
  207. package/dist/coordinator/paths.js +0 -42
  208. package/dist/coordinator/util.js +0 -126
  209. package/dist/coordinator.js +0 -990
  210. package/dist/daemon.js +0 -44
  211. package/dist/dispatch.js +0 -250
  212. package/dist/drive.js +0 -827
  213. package/dist/error-feedback.js +0 -419
  214. package/dist/evidence-grounding.js +0 -184
  215. package/dist/execution-backend/agent.js +0 -294
  216. package/dist/execution-backend/probes.js +0 -112
  217. package/dist/execution-backend/util.js +0 -47
  218. package/dist/execution-backend.js +0 -961
  219. package/dist/gates.js +0 -48
  220. package/dist/harness.js +0 -61
  221. package/dist/loop-expansion.js +0 -60
  222. package/dist/mcp/tool-call.js +0 -434
  223. package/dist/mcp/tool-definitions.js +0 -1040
  224. package/dist/mcp-surface.js +0 -30
  225. package/dist/multi-agent/graph.js +0 -84
  226. package/dist/multi-agent/helpers.js +0 -141
  227. package/dist/multi-agent/ids.js +0 -20
  228. package/dist/multi-agent/paths.js +0 -22
  229. package/dist/multi-agent-eval/normalize.js +0 -51
  230. package/dist/multi-agent-eval.js +0 -678
  231. package/dist/multi-agent-host.js +0 -777
  232. package/dist/multi-agent.js +0 -984
  233. package/dist/node-projection.js +0 -59
  234. package/dist/node-snapshot.js +0 -260
  235. package/dist/operator-ux.js +0 -631
  236. package/dist/orchestrator/app-operations.js +0 -211
  237. package/dist/orchestrator/audit-operations.js +0 -182
  238. package/dist/orchestrator/candidate-operations.js +0 -117
  239. package/dist/orchestrator/cli-options.js +0 -294
  240. package/dist/orchestrator/collaboration-operations.js +0 -86
  241. package/dist/orchestrator/feedback-operations.js +0 -81
  242. package/dist/orchestrator/host-operations.js +0 -78
  243. package/dist/orchestrator/lifecycle-operations.js +0 -626
  244. package/dist/orchestrator/migration-operations.js +0 -44
  245. package/dist/orchestrator/multi-agent-operations.js +0 -362
  246. package/dist/orchestrator/topology-operations.js +0 -84
  247. package/dist/orchestrator.js +0 -917
  248. package/dist/pipeline-runner.js +0 -285
  249. package/dist/reclamation/hash.js +0 -72
  250. package/dist/reclamation.js +0 -812
  251. package/dist/reporter.js +0 -67
  252. package/dist/run-export.js +0 -784
  253. package/dist/run-registry/derive.js +0 -175
  254. package/dist/run-registry/format.js +0 -124
  255. package/dist/run-registry/gc.js +0 -251
  256. package/dist/run-registry/policy.js +0 -16
  257. package/dist/run-registry/queue.js +0 -115
  258. package/dist/run-registry.js +0 -850
  259. package/dist/run-state-schema.js +0 -68
  260. package/dist/scheduling.js +0 -184
  261. package/dist/state-explosion.js +0 -1014
  262. package/dist/state.js +0 -367
  263. package/dist/telemetry-ledger.js +0 -196
  264. package/dist/topology.js +0 -565
  265. package/dist/triggers.js +0 -184
  266. package/dist/trust-audit.js +0 -644
  267. package/dist/types/blackboard.js +0 -2
  268. package/dist/types/candidate.js +0 -2
  269. package/dist/types/collaboration.js +0 -2
  270. package/dist/types/core.js +0 -2
  271. package/dist/types/drive.js +0 -10
  272. package/dist/types/error-feedback.js +0 -2
  273. package/dist/types/evidence-reasoning.js +0 -2
  274. package/dist/types/execution-backend.js +0 -2
  275. package/dist/types/multi-agent.js +0 -2
  276. package/dist/types/observability.js +0 -2
  277. package/dist/types/pipeline.js +0 -2
  278. package/dist/types/reclamation.js +0 -8
  279. package/dist/types/report-bundle.js +0 -6
  280. package/dist/types/result.js +0 -2
  281. package/dist/types/run-registry.js +0 -2
  282. package/dist/types/run.js +0 -2
  283. package/dist/types/sandbox.js +0 -2
  284. package/dist/types/schedule.js +0 -2
  285. package/dist/types/state-node.js +0 -2
  286. package/dist/types/topology.js +0 -2
  287. package/dist/types/trust.js +0 -2
  288. package/dist/types/workbench.js +0 -2
  289. package/dist/types/worker.js +0 -2
  290. package/dist/types/workflow-app.js +0 -2
  291. package/dist/types.js +0 -44
  292. package/dist/util/fingerprint.js +0 -19
  293. package/dist/util/fingerprint.test.js +0 -27
  294. package/dist/verifier.js +0 -78
  295. package/dist/version.js +0 -8
  296. package/dist/workbench-host.js +0 -182
  297. package/dist/workbench.js +0 -192
  298. package/dist/worker-accept/acceptance.js +0 -114
  299. package/dist/worker-accept/blackboard-fanout.js +0 -80
  300. package/dist/worker-accept/blackboard-linkage.js +0 -19
  301. package/dist/worker-accept/context.js +0 -2
  302. package/dist/worker-accept/telemetry-ledger.js +0 -126
  303. package/dist/worker-accept/validation.js +0 -77
  304. package/dist/worker-accept/verifier-completion.js +0 -73
  305. package/dist/worker-isolation/helpers.js +0 -51
  306. package/dist/worker-isolation/paths.js +0 -46
  307. package/dist/worker-isolation.js +0 -656
  308. package/dist/workflow-api.js +0 -131
  309. /package/dist/{types → core/types}/boundary.js +0 -0
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ // shell/run-store.ts — loadRunFromCwd, saveCheckpoint, compactCheckpoint.
3
+ //
4
+ // MILESTONE 3. The ONLY place state.json is read or written. Byte-exact
5
+ // port of the old build's src/state.ts load/save/compact functions, now
6
+ // split so the migration DECISION logic lives in core/state/migrations.ts
7
+ // and this file is the thin impure shell around it (disk read, lock,
8
+ // durable write).
9
+ //
10
+ // Evidence: SPEC/state-core.md "src/state.ts — persistence kernel",
11
+ // "Write ordering and atomic rules", "compactCheckpoint on a run with no
12
+ // empty optional arrays writes nothing".
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ exports.ensureRunDirs = exports.createRunPaths = void 0;
48
+ exports.hashArtifactFile = hashArtifactFile;
49
+ exports.loadRunStateFile = loadRunStateFile;
50
+ exports.checkRunStateFile = checkRunStateFile;
51
+ exports.migrateRunStateFile = migrateRunStateFile;
52
+ exports.loadRunFromCwd = loadRunFromCwd;
53
+ exports.saveCheckpoint = saveCheckpoint;
54
+ exports.compactCheckpoint = compactCheckpoint;
55
+ exports.createRun = createRun;
56
+ const fs = __importStar(require("node:fs"));
57
+ const path = __importStar(require("node:path"));
58
+ const fs_atomic_1 = require("./fs-atomic");
59
+ const run_paths_1 = require("../core/state/run-paths");
60
+ Object.defineProperty(exports, "createRunPaths", { enumerable: true, get: function () { return run_paths_1.createRunPaths; } });
61
+ Object.defineProperty(exports, "ensureRunDirs", { enumerable: true, get: function () { return run_paths_1.ensureRunDirs; } });
62
+ const migrations_1 = require("../core/state/migrations");
63
+ const hash_1 = require("../core/hash");
64
+ /** Read the file at artifact.path and stamp sha256 (the core `sha256:`+hex
65
+ * form) + sizeBytes onto the StateArtifact. A missing/unreadable file is
66
+ * silently skipped so hashing an absent artifact never throws. Byte-exact
67
+ * port of the old flat src/state.ts:hashArtifactFile. */
68
+ function hashArtifactFile(artifact) {
69
+ try {
70
+ const content = fs.readFileSync(artifact.path, "utf8");
71
+ artifact.sha256 = (0, hash_1.sha256)(content);
72
+ artifact.sizeBytes = Buffer.byteLength(content, "utf8");
73
+ }
74
+ catch {
75
+ /* file missing — silently skip */
76
+ }
77
+ return artifact;
78
+ }
79
+ /** Dry-run load + migrate a state.json at an explicit path. */
80
+ function loadRunStateFile(statePath, options = {}) {
81
+ return (0, migrations_1.migrateRunState)((0, fs_atomic_1.readJson)(statePath), {
82
+ statePath,
83
+ dryRun: options.dryRun === undefined ? true : options.dryRun,
84
+ });
85
+ }
86
+ /** Same as `loadRunStateFile` with `dryRun: true`. */
87
+ function checkRunStateFile(statePath) {
88
+ return loadRunStateFile(statePath, { dryRun: true });
89
+ }
90
+ /** Dry-run unless `write: true`; writes the migrated state back with
91
+ * `writeJson` ONLY when status is not `unsupported` AND `write` AND
92
+ * `report.writeRequired`. */
93
+ function migrateRunStateFile(statePath, options = {}) {
94
+ const result = loadRunStateFile(statePath, { dryRun: !options.write });
95
+ if (result.report.status !== "unsupported" && options.write && result.report.writeRequired) {
96
+ (0, fs_atomic_1.writeJson)(statePath, result.run);
97
+ }
98
+ return result;
99
+ }
100
+ /** Refuses an empty id with `Missing run id`; loads
101
+ * `<cwd>/.cw/runs/<runId>/state.json` (dry-run — never writes); throws
102
+ * `Unsupported CW run state: <errors joined by "; ">` on an unsupported
103
+ * verdict; else returns the migrated WorkflowRun in memory. */
104
+ function loadRunFromCwd(runId, cwd = process.cwd()) {
105
+ if (!runId)
106
+ throw new Error("Missing run id");
107
+ (0, fs_atomic_1.assertSafeRunId)(runId);
108
+ const statePath = path.join(cwd, ".cw", "runs", runId, "state.json");
109
+ const result = loadRunStateFile(statePath, { dryRun: true });
110
+ if (result.report.status === "unsupported") {
111
+ throw new Error(`Unsupported CW run state: ${result.report.errors.join("; ")}`);
112
+ }
113
+ return result.run;
114
+ }
115
+ /** state.json is the single source of truth — set `updatedAt`, then write
116
+ * it DURABLY with a lock so concurrent processes never lose an update. */
117
+ function saveCheckpoint(run) {
118
+ run.updatedAt = new Date().toISOString();
119
+ (0, fs_atomic_1.withFileLock)(run.paths.state, () => {
120
+ (0, fs_atomic_1.writeJson)(run.paths.state, run, { durable: true });
121
+ });
122
+ }
123
+ const OPTIONAL_EMPTY_ARRAY_KEYS = ["nodes", "contracts", "feedback", "workers", "sandboxProfiles", "candidates", "candidateSelections"];
124
+ /** Strip the 7 top-level optional-array keys when each is an empty array
125
+ * (normalizeRunState backfills these on load, so stripping saves disk
126
+ * without losing information). Returns the count stripped; writes nothing
127
+ * (and calls `saveCheckpoint` zero times) when nothing was stripped. */
128
+ function compactCheckpoint(run) {
129
+ let stripped = 0;
130
+ const state = run;
131
+ for (const key of OPTIONAL_EMPTY_ARRAY_KEYS) {
132
+ if (Array.isArray(state[key]) && state[key].length === 0) {
133
+ delete state[key];
134
+ stripped++;
135
+ }
136
+ }
137
+ if (stripped > 0)
138
+ saveCheckpoint(run);
139
+ return stripped;
140
+ }
141
+ /** Creates a brand-new run's initial `state.json` on disk: makes the run
142
+ * directory tree, migrates `{}` through `normalizeRunState` (so every
143
+ * default lands exactly like a real legacy-run migration would), stamps
144
+ * `id`/`workflow.id`/`workflow.title`, and durably writes `state.json`.
145
+ * This is the minimal "create a run" mechanism this milestone needs to
146
+ * drive the state kernel end to end; the real `plan`/`quickstart`
147
+ * pipeline (later milestones) layers phases/tasks/inputs on top of this
148
+ * same primitive. */
149
+ function createRun(runDir, runId, workflowId, cwd) {
150
+ const paths = (0, run_paths_1.createRunPaths)(runDir);
151
+ (0, run_paths_1.ensureRunDirs)(paths);
152
+ const seed = {
153
+ id: runId,
154
+ cwd,
155
+ workflowId,
156
+ paths: paths,
157
+ };
158
+ const { run, report } = (0, migrations_1.migrateRunState)(seed, { statePath: paths.state, dryRun: false });
159
+ if (report.status === "unsupported") {
160
+ throw new Error(`Unsupported CW run state: ${report.errors.join("; ")}`);
161
+ }
162
+ (0, fs_atomic_1.writeJson)(paths.state, run, { durable: true });
163
+ return run;
164
+ }
@@ -1,7 +1,44 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
2
+ // shell/sandbox-profile.ts the 4 bundled sandbox profiles, resolution,
3
+ // validation, and the runtime read/write/command/network checks.
4
+ //
5
+ // MILESTONE 5 (v2/PLAN.md build order, step 5). Byte-exact port of
6
+ // plugins/cool-workflow/src/sandbox-profile.ts.
7
+ //
8
+ // Evidence: SPEC/execution-backend.md "Sandbox profiles".
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
5
42
  Object.defineProperty(exports, "__esModule", { value: true });
6
43
  exports.SandboxProfileError = exports.DEFAULT_SANDBOX_PROFILE_ID = exports.SANDBOX_PROFILE_SCHEMA_VERSION = void 0;
7
44
  exports.bundledSandboxProfileIds = bundledSandboxProfileIds;
@@ -18,12 +55,12 @@ exports.validateSandboxWrite = validateSandboxWrite;
18
55
  exports.validateSandboxRead = validateSandboxRead;
19
56
  exports.validateSandboxCommand = validateSandboxCommand;
20
57
  exports.validateSandboxNetwork = validateSandboxNetwork;
21
- exports.upsertRunSandboxPolicy = upsertRunSandboxPolicy;
22
- exports.sandboxContextForRun = sandboxContextForRun;
23
58
  exports.sandboxContextForValidation = sandboxContextForValidation;
24
- const node_fs_1 = __importDefault(require("node:fs"));
25
- const node_path_1 = __importDefault(require("node:path"));
26
- const state_1 = require("./state");
59
+ exports.sandboxContextForRun = sandboxContextForRun;
60
+ exports.persistCustomSandboxProfile = persistCustomSandboxProfile;
61
+ const fs = __importStar(require("node:fs"));
62
+ const path = __importStar(require("node:path"));
63
+ const fs_atomic_1 = require("./fs-atomic");
27
64
  exports.SANDBOX_PROFILE_SCHEMA_VERSION = 1;
28
65
  exports.DEFAULT_SANDBOX_PROFILE_ID = "default";
29
66
  class SandboxProfileError extends Error {
@@ -53,8 +90,8 @@ const BUNDLED_PROFILE_DEFINITIONS = [
53
90
  env: { inherit: false, expose: [] },
54
91
  hostInstructions: [
55
92
  "Run with the host's normal process policy.",
56
- "Preserve CW output acceptance checks for result.md, artifacts/, and logs/."
57
- ]
93
+ "Preserve CW output acceptance checks for result.md, artifacts/, and logs/.",
94
+ ],
58
95
  },
59
96
  {
60
97
  schemaVersion: exports.SANDBOX_PROFILE_SCHEMA_VERSION,
@@ -69,8 +106,8 @@ const BUNDLED_PROFILE_DEFINITIONS = [
69
106
  env: { inherit: false, expose: [] },
70
107
  hostInstructions: [
71
108
  "Deny network access unless the operator explicitly layers a site policy over this profile.",
72
- "Mount or expose the workspace read-only when the agent host supports it."
73
- ]
109
+ "Mount or expose the workspace read-only when the agent host supports it.",
110
+ ],
74
111
  },
75
112
  {
76
113
  schemaVersion: exports.SANDBOX_PROFILE_SCHEMA_VERSION,
@@ -85,8 +122,8 @@ const BUNDLED_PROFILE_DEFINITIONS = [
85
122
  env: { inherit: false, expose: [] },
86
123
  hostInstructions: [
87
124
  "Use only for workers that are expected to modify repository files.",
88
- "Keep CW run state writes under CW control; workers should still return results through worker output."
89
- ]
125
+ "Keep CW run state writes under CW control; workers should still return results through worker output.",
126
+ ],
90
127
  },
91
128
  {
92
129
  schemaVersion: exports.SANDBOX_PROFILE_SCHEMA_VERSION,
@@ -101,9 +138,9 @@ const BUNDLED_PROFILE_DEFINITIONS = [
101
138
  env: { inherit: false, expose: [] },
102
139
  hostInstructions: [
103
140
  "Expose only input.md and result.md to the worker when host sandboxing is available.",
104
- "Do not provide shell command execution, network access, or inherited environment variables."
105
- ]
106
- }
141
+ "Do not provide shell command execution, network access, or inherited environment variables.",
142
+ ],
143
+ },
107
144
  ];
108
145
  function bundledSandboxProfileIds() {
109
146
  return BUNDLED_PROFILE_DEFINITIONS.map((profile) => profile.id).sort();
@@ -118,7 +155,7 @@ function showBundledSandboxProfile(id, context = defaultSandboxContext()) {
118
155
  const profile = BUNDLED_PROFILE_DEFINITIONS.find((candidate) => candidate.id === id);
119
156
  if (!profile) {
120
157
  throw new SandboxProfileError("sandbox-profile-not-found", `Sandbox profile not found: ${id}`, {
121
- details: { requestedProfileId: id, bundledProfileIds: BUNDLED_PROFILE_DEFINITIONS.map((candidate) => candidate.id) }
158
+ details: { requestedProfileId: id, bundledProfileIds: BUNDLED_PROFILE_DEFINITIONS.map((candidate) => candidate.id) },
122
159
  });
123
160
  }
124
161
  return resolveSandboxProfile(profile, context);
@@ -127,34 +164,21 @@ function resolveSandboxProfileById(id, context = defaultSandboxContext()) {
127
164
  const requested = id || exports.DEFAULT_SANDBOX_PROFILE_ID;
128
165
  if (isBundledSandboxProfileId(requested))
129
166
  return showBundledSandboxProfile(requested, context);
130
- // A non-bundled id that resolves to a readable profile FILE is a CUSTOM profile:
131
- // validate and ENFORCE it (the resolved policy snapshots onto the worker scope).
132
- // This closes the gap where `sandbox validate` accepted a custom profile that
133
- // dispatch/worker-isolation then refused — validated but never enforceable.
134
- // A non-bundled, non-file id still fails closed via showBundledSandboxProfile.
135
- const absolute = node_path_1.default.resolve(context.cwd, requested);
136
- if (!absolute.startsWith(node_path_1.default.resolve(context.cwd) + node_path_1.default.sep) && absolute !== node_path_1.default.resolve(context.cwd)) {
167
+ const absolute = path.resolve(context.cwd, requested);
168
+ if (!absolute.startsWith(path.resolve(context.cwd) + path.sep) && absolute !== path.resolve(context.cwd)) {
137
169
  throw new SandboxProfileError("sandbox-profile-path-escape", `Custom profile path traversal denied: ${requested}`, {
138
- details: { requested }
170
+ details: { requested },
139
171
  });
140
172
  }
141
- if (node_fs_1.default.existsSync(absolute) && node_fs_1.default.statSync(absolute).isFile()) {
173
+ if (fs.existsSync(absolute) && fs.statSync(absolute).isFile()) {
142
174
  const result = validateSandboxProfileFile(requested, context);
143
175
  if (!result.valid || !result.profile) {
144
176
  throw new SandboxProfileError("sandbox-profile-invalid", `Custom sandbox profile is invalid: ${requested}`, {
145
- details: { issues: result.issues }
177
+ details: { issues: result.issues },
146
178
  });
147
179
  }
148
180
  return result.profile;
149
181
  }
150
- // H7: a custom profile loaded from a FILE at dispatch persists as a DEFINITION
151
- // in run.customSandboxProfiles (threaded here as context.customProfiles). After a
152
- // worker scope snapshot is lost, the boundary re-resolves by the profile's
153
- // LOGICAL id (e.g. "my-custom"), and the dispatch-time file path is gone. Resolve
154
- // the persisted definition against the CURRENT (worker) context so worker-specific
155
- // path tokens ($workerDir etc.) bind to THIS worker — re-enforcing the same
156
- // policy instead of throwing not-found. This runs only after the bundled +
157
- // file-path branches, so a custom id never shadows a bundled or on-disk profile.
158
182
  const customDefinition = context.customProfiles?.[requested];
159
183
  if (customDefinition) {
160
184
  return resolveSandboxProfile(customDefinition, context);
@@ -165,7 +189,7 @@ function resolveSandboxProfile(profile, context = defaultSandboxContext()) {
165
189
  const issues = validateSandboxProfileDefinition(profile, context);
166
190
  if (issues.length) {
167
191
  throw new SandboxProfileError("sandbox-profile-invalid", `Sandbox profile ${profile.id || "(unknown)"} is invalid`, {
168
- details: { issues }
192
+ details: { issues },
169
193
  });
170
194
  }
171
195
  const workerOutput = normalizeWorkerOutput(profile.workerOutput, context);
@@ -190,44 +214,40 @@ function resolveSandboxProfile(profile, context = defaultSandboxContext()) {
190
214
  "profile validation",
191
215
  "path normalization",
192
216
  "worker result acceptance against sandbox write policy",
193
- "durable ErrorFeedback for denied worker output"
217
+ "durable ErrorFeedback for denied worker output",
194
218
  ],
195
219
  hostRequired: [
196
220
  "OS-level read isolation",
197
221
  "OS-level write isolation before result acceptance",
198
222
  "process execution restrictions",
199
223
  "network restrictions",
200
- "environment variable filtering"
201
- ]
224
+ "environment variable filtering",
225
+ ],
202
226
  },
203
227
  hostInstructions: profile.hostInstructions || [],
204
228
  resolvedAt: new Date().toISOString(),
205
- metadata: profile.metadata
229
+ metadata: profile.metadata,
206
230
  };
207
231
  }
208
232
  function validateSandboxProfileFile(profileFile, context = defaultSandboxContext()) {
209
- const absolutePath = node_path_1.default.resolve(profileFile);
233
+ const absolutePath = path.resolve(profileFile);
210
234
  const issues = [];
211
235
  if (hasTraversal(profileFile)) {
212
236
  issues.push(issue("sandbox-profile-invalid", `Profile file path contains traversal: ${profileFile}`, profileFile));
213
237
  return { valid: false, profileFile: absolutePath, issues };
214
238
  }
215
- if (!node_fs_1.default.existsSync(absolutePath)) {
239
+ if (!fs.existsSync(absolutePath)) {
216
240
  issues.push(issue("sandbox-profile-invalid", `Profile file does not exist: ${absolutePath}`, absolutePath));
217
241
  return { valid: false, profileFile: absolutePath, issues };
218
242
  }
219
243
  let profile;
220
244
  try {
221
- profile = JSON.parse(node_fs_1.default.readFileSync(absolutePath, "utf8"));
245
+ profile = JSON.parse(fs.readFileSync(absolutePath, "utf8"));
222
246
  }
223
247
  catch (error) {
224
248
  issues.push(issue("sandbox-profile-invalid", `Profile file is not valid JSON: ${messageOf(error)}`, absolutePath));
225
249
  return { valid: false, profileFile: absolutePath, issues };
226
250
  }
227
- // Fail closed if a CUSTOM file reuses a BUNDLED id (H7 hardening): resolution is
228
- // bundled-first, so a custom "default"/"workspace-write"/... would be silently
229
- // shadowed by the WIDER bundled policy on a snapshot-loss re-resolve — widening
230
- // the sandbox with no error. Reserve the bundled names for bundled profiles.
231
251
  if (profile && typeof profile.id === "string" && isBundledSandboxProfileId(profile.id)) {
232
252
  issues.push(issue("sandbox-profile-invalid", `Custom sandbox profile id "${profile.id}" is reserved (collides with a bundled profile); choose a different id`, absolutePath));
233
253
  return { valid: false, profileFile: absolutePath, issues };
@@ -260,7 +280,7 @@ function effectiveSandboxWritePaths(policy) {
260
280
  const workerPaths = [
261
281
  policy.workerOutput.result ? policy.metadata?.resultPath : undefined,
262
282
  policy.workerOutput.artifacts ? policy.metadata?.artifactsDir : undefined,
263
- policy.workerOutput.logs ? policy.metadata?.logsDir : undefined
283
+ policy.workerOutput.logs ? policy.metadata?.logsDir : undefined,
264
284
  ].filter((value) => typeof value === "string" && value.length > 0);
265
285
  return uniqueResolvedPaths([...policy.writePaths, ...workerPaths], defaultSandboxContext());
266
286
  }
@@ -276,8 +296,8 @@ function sandboxPolicyForWorker(profileId, context) {
276
296
  inputPath: context.inputPath,
277
297
  resultPath: context.resultPath,
278
298
  artifactsDir: context.artifactsDir,
279
- logsDir: context.logsDir
280
- })
299
+ logsDir: context.logsDir,
300
+ }),
281
301
  };
282
302
  }
283
303
  function validateSandboxWrite(policy, rawPath, workerId = "") {
@@ -312,36 +332,65 @@ function validateSandboxNetwork(policy, target, workerId = "") {
312
332
  }
313
333
  return null;
314
334
  }
315
- function upsertRunSandboxPolicy(run, policy) {
316
- run.sandboxProfiles = run.sandboxProfiles || [];
317
- const existing = run.sandboxProfiles.findIndex((candidate) => candidate.id === policy.id);
318
- run.sandboxProfiles =
319
- existing >= 0
320
- ? run.sandboxProfiles.map((candidate) => (candidate.id === policy.id ? policy : candidate))
321
- : [...run.sandboxProfiles, policy];
335
+ function sandboxContextForValidation(cwd = process.cwd()) {
336
+ const root = path.resolve(cwd);
337
+ const runDir = path.join(root, ".cw", "runs", "_sandbox-profile-validation");
338
+ const workerDir = path.join(runDir, "workers", "_worker");
339
+ return {
340
+ cwd: root,
341
+ runDir,
342
+ workerDir,
343
+ inputPath: path.join(workerDir, "input.md"),
344
+ resultPath: path.join(workerDir, "result.md"),
345
+ artifactsDir: path.join(workerDir, "artifacts"),
346
+ logsDir: path.join(workerDir, "logs"),
347
+ };
322
348
  }
349
+ // H7: a run-scoped resolution context. Threads run.customSandboxProfiles as
350
+ // customProfiles so a worker-boundary re-resolve by logical id can find and
351
+ // re-resolve a CUSTOM profile after a scope snapshot is lost — re-resolving
352
+ // against the worker context (not a dispatch-time file path).
323
353
  function sandboxContextForRun(run) {
324
354
  return {
325
355
  cwd: run.cwd,
326
356
  runDir: run.paths.runDir,
327
- // H7: thread persisted custom profile DEFINITIONS so a boundary re-resolve by
328
- // logical id can find + re-resolve a custom profile after snapshot loss.
329
- customProfiles: run.customSandboxProfiles
357
+ customProfiles: run.customSandboxProfiles,
330
358
  };
331
359
  }
332
- function sandboxContextForValidation(cwd = process.cwd()) {
333
- const root = node_path_1.default.resolve(cwd);
334
- const runDir = node_path_1.default.join(root, ".cw", "runs", "_sandbox-profile-validation");
335
- const workerDir = node_path_1.default.join(runDir, "workers", "_worker");
336
- return {
337
- cwd: root,
338
- runDir,
339
- workerDir,
340
- inputPath: node_path_1.default.join(workerDir, "input.md"),
341
- resultPath: node_path_1.default.join(workerDir, "result.md"),
342
- artifactsDir: node_path_1.default.join(workerDir, "artifacts"),
343
- logsDir: node_path_1.default.join(workerDir, "logs")
344
- };
360
+ // H7: if the requested profile is a CUSTOM profile loaded from a FILE (a
361
+ // non-bundled id that resolves to a readable, valid profile file), persist its
362
+ // raw DEFINITION onto run.customSandboxProfiles keyed by the definition's logical
363
+ // id. We store the DEFINITION (path tokens intact), not a resolved policy, so
364
+ // worker-specific tokens re-bind to the correct worker context on every later
365
+ // re-resolve. Bundled ids and unknown ids are left untouched, so this never
366
+ // shadows a bundled profile or masks a fail-closed. Throws on an id collision:
367
+ // the same logical id mapped to a DIFFERENT profile file is a caller mistake.
368
+ function persistCustomSandboxProfile(run, requested) {
369
+ if (!requested || isBundledSandboxProfileId(requested))
370
+ return;
371
+ const absolute = path.resolve(requested);
372
+ if (!fs.existsSync(absolute) || !fs.statSync(absolute).isFile())
373
+ return;
374
+ const validation = validateSandboxProfileFile(requested, sandboxContextForValidation(run.cwd));
375
+ if (!validation.valid || !validation.profile)
376
+ return;
377
+ let definition;
378
+ try {
379
+ definition = JSON.parse(fs.readFileSync(absolute, "utf8"));
380
+ }
381
+ catch {
382
+ return;
383
+ }
384
+ if (!definition || typeof definition !== "object" || typeof definition.id !== "string" || !definition.id)
385
+ return;
386
+ const profiles = (run.customSandboxProfiles || {});
387
+ const previous = profiles[definition.id];
388
+ if (previous && JSON.stringify(previous) !== JSON.stringify(definition)) {
389
+ throw new Error(`Sandbox profile id collision: "${definition.id}" is already defined by a different custom profile ` +
390
+ `(source: ${absolute}). Use a unique id in each custom profile file.`);
391
+ }
392
+ profiles[definition.id] = definition;
393
+ run.customSandboxProfiles = profiles;
345
394
  }
346
395
  function validateSandboxPathAccess(mode, policy, rawPath, allowedPaths, workerId) {
347
396
  if (hasTraversal(rawPath)) {
@@ -350,23 +399,15 @@ function validateSandboxPathAccess(mode, policy, rawPath, allowedPaths, workerId
350
399
  if (hasControlCharacters(rawPath)) {
351
400
  return denied(`sandbox-${mode}-denied`, `Worker ${workerId} ${mode} path is malformed: ${rawPath}`, rawPath, allowedPaths);
352
401
  }
353
- const candidate = node_path_1.default.resolve(rawPath);
354
- // Symlink-hardened (v0.1.40 self-audit P1): isContainedPath realpaths both sides
355
- // so a planted symlink whose textual path looks "inside" an allowed root but
356
- // whose real target escapes it is denied, not silently accepted.
357
- const insideAllowedPath = allowedPaths.some((allowed) => (0, state_1.isContainedPath)(candidate, allowed));
402
+ const candidate = path.resolve(rawPath);
403
+ const insideAllowedPath = allowedPaths.some((allowed) => (0, fs_atomic_1.isContainedPath)(candidate, allowed));
358
404
  if (!insideAllowedPath) {
359
405
  return denied(`sandbox-${mode}-denied`, `Worker ${workerId} ${mode} path is outside sandbox profile ${policy.id}: ${candidate}`, candidate, allowedPaths);
360
406
  }
361
407
  return null;
362
408
  }
363
409
  function denied(code, message, candidatePath, allowedPaths) {
364
- return {
365
- code,
366
- message,
367
- path: candidatePath,
368
- allowedPaths
369
- };
410
+ return { code, message, path: candidatePath, allowedPaths };
370
411
  }
371
412
  function defaultSandboxContext() {
372
413
  return sandboxContextForValidation(process.cwd());
@@ -414,7 +455,7 @@ function resolveProfilePath(value, context) {
414
455
  if (hasTraversal(expanded)) {
415
456
  throw new SandboxProfileError("sandbox-profile-invalid", `Sandbox path contains traversal: ${value}`, { path: value });
416
457
  }
417
- return node_path_1.default.resolve(context.cwd, expanded);
458
+ return path.resolve(context.cwd, expanded);
418
459
  }
419
460
  function expandPathToken(value, context) {
420
461
  const tokens = {
@@ -424,15 +465,13 @@ function expandPathToken(value, context) {
424
465
  $inputPath: context.inputPath,
425
466
  $resultPath: context.resultPath,
426
467
  $artifactsDir: context.artifactsDir,
427
- $logsDir: context.logsDir
468
+ $logsDir: context.logsDir,
428
469
  };
429
470
  if (!value.startsWith("$"))
430
471
  return value;
431
472
  const replacement = tokens[value];
432
473
  if (!replacement) {
433
- throw new SandboxProfileError("sandbox-profile-invalid", `Unknown or unavailable sandbox path token: ${value}`, {
434
- path: value
435
- });
474
+ throw new SandboxProfileError("sandbox-profile-invalid", `Unknown or unavailable sandbox path token: ${value}`, { path: value });
436
475
  }
437
476
  return replacement;
438
477
  }
@@ -440,27 +479,27 @@ function normalizeWorkerOutput(policy, context) {
440
479
  return {
441
480
  result: policy?.result ?? true,
442
481
  artifacts: context.allowArtifacts ?? policy?.artifacts ?? true,
443
- logs: context.allowLogs ?? policy?.logs ?? true
482
+ logs: context.allowLogs ?? policy?.logs ?? true,
444
483
  };
445
484
  }
446
485
  function normalizeExecute(policy) {
447
486
  return {
448
487
  mode: policy?.mode || "none",
449
488
  allow: policy?.allow ? [...policy.allow] : undefined,
450
- deny: policy?.deny ? [...policy.deny] : undefined
489
+ deny: policy?.deny ? [...policy.deny] : undefined,
451
490
  };
452
491
  }
453
492
  function normalizeNetwork(policy) {
454
493
  return {
455
494
  mode: policy?.mode || "none",
456
- allow: policy?.allow ? [...policy.allow] : undefined
495
+ allow: policy?.allow ? [...policy.allow] : undefined,
457
496
  };
458
497
  }
459
498
  function normalizeEnv(policy) {
460
499
  return {
461
500
  inherit: Boolean(policy?.inherit),
462
501
  expose: policy?.expose ? [...policy.expose] : [],
463
- deny: policy?.deny ? [...policy.deny] : undefined
502
+ deny: policy?.deny ? [...policy.deny] : undefined,
464
503
  };
465
504
  }
466
505
  function validateCommandPolicy(policy, issues) {
@@ -506,7 +545,7 @@ function hasTraversal(value) {
506
545
  return value.split(/[\\/]+/).includes("..");
507
546
  }
508
547
  function hasControlCharacters(value) {
509
- return /[\u0000-\u001f]/.test(value);
548
+ return /[\x00-\x1f]/.test(value);
510
549
  }
511
550
  function compactMetadata(value) {
512
551
  const entries = Object.entries(value).filter(([, entry]) => entry !== undefined);