cool-workflow 0.1.96 → 0.1.98

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 (74) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/apps/architecture-review/app.json +1 -1
  4. package/apps/architecture-review-fast/app.json +1 -1
  5. package/apps/end-to-end-golden-path/app.json +1 -1
  6. package/apps/pr-review-fix-ci/app.json +1 -1
  7. package/apps/release-cut/app.json +1 -1
  8. package/apps/research-synthesis/app.json +1 -1
  9. package/dist/candidate-scoring.js +3 -3
  10. package/dist/capability-core.js +9 -1
  11. package/dist/capability-registry.js +7 -1
  12. package/dist/cli/command-surface.js +4 -0
  13. package/dist/cli/handlers/ledger.js +169 -0
  14. package/dist/cli/handlers/scheduling.js +7 -1
  15. package/dist/drive.js +108 -61
  16. package/dist/execution-backend/agent.js +84 -24
  17. package/dist/execution-backend.js +25 -5
  18. package/dist/ledger.js +313 -0
  19. package/dist/mcp/tool-call.js +36 -0
  20. package/dist/mcp/tool-definitions.js +26 -0
  21. package/dist/mcp-server.js +4 -0
  22. package/dist/onramp.js +2 -0
  23. package/dist/orchestrator/app-operations.js +6 -0
  24. package/dist/orchestrator/cli-options.js +8 -2
  25. package/dist/orchestrator/lifecycle-operations.js +40 -13
  26. package/dist/orchestrator/migration-operations.js +1 -1
  27. package/dist/orchestrator.js +11 -3
  28. package/dist/remote-source.js +10 -3
  29. package/dist/run-export.js +45 -5
  30. package/dist/sandbox-profile.js +6 -1
  31. package/dist/triggers.js +7 -1
  32. package/dist/version.js +1 -1
  33. package/dist/workbench-host.js +18 -2
  34. package/docs/agent-delegation-drive.7.md +4 -0
  35. package/docs/cli-mcp-parity.7.md +16 -2
  36. package/docs/contract-migration-tooling.7.md +4 -0
  37. package/docs/control-plane-scheduling.7.md +4 -0
  38. package/docs/cross-agent-ledger.7.md +217 -0
  39. package/docs/demo.7.md +80 -0
  40. package/docs/designs/handoff-ledger.md +145 -0
  41. package/docs/doctor.7.md +97 -0
  42. package/docs/durable-state-and-locking.7.md +4 -0
  43. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  44. package/docs/execution-backends.7.md +4 -0
  45. package/docs/fix.7.md +44 -0
  46. package/docs/handoff-setup.md +120 -0
  47. package/docs/init.7.md +62 -0
  48. package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
  49. package/docs/multi-agent-eval-replay-harness.7.md +4 -0
  50. package/docs/multi-agent-operator-ux.7.md +4 -0
  51. package/docs/node-snapshot-diff-replay.7.md +4 -0
  52. package/docs/observability-cost-accounting.7.md +4 -0
  53. package/docs/pipeline-verbs.7.md +93 -0
  54. package/docs/project-index.md +28 -5
  55. package/docs/real-execution-backends.7.md +4 -0
  56. package/docs/release-and-migration.7.md +4 -0
  57. package/docs/release-tooling.7.md +4 -0
  58. package/docs/routine.7.md +73 -0
  59. package/docs/run-registry-control-plane.7.md +4 -0
  60. package/docs/run-retention-reclamation.7.md +4 -0
  61. package/docs/state-explosion-management.7.md +4 -0
  62. package/docs/team-collaboration.7.md +4 -0
  63. package/docs/web-desktop-workbench.7.md +4 -0
  64. package/manifest/README.md +16 -10
  65. package/manifest/plugin.manifest.json +1 -1
  66. package/package.json +1 -1
  67. package/scripts/agents/agent-adapter-core.js +4 -1
  68. package/scripts/agents/codex-agent.js +34 -4
  69. package/scripts/canonical-apps.js +4 -4
  70. package/scripts/children/batch-delegate-child.js +40 -13
  71. package/scripts/children/http-delegate-child.js +2 -1
  72. package/scripts/dogfood-release.js +1 -1
  73. package/scripts/golden-path.js +4 -4
  74. package/scripts/release-flow.js +31 -17
@@ -1,15 +1,15 @@
1
1
  # Cool Workflow Project Index
2
2
 
3
- Generated from the current repository code on 2026-06-28 by `npm run sync:project-index`.
3
+ Generated from the current repository code on 2026-07-03 by `npm run sync:project-index`.
4
4
 
5
5
  ## Snapshot
6
6
 
7
7
  - Package: `cool-workflow`
8
- - Version: `0.1.96`
9
- - Source modules: `68`
8
+ - Version: `0.1.98`
9
+ - Source modules: `69`
10
10
  - Workflow apps: `8`
11
- - Docs: `53`
12
- - Smoke tests: `158`
11
+ - Docs: `61`
12
+ - Smoke tests: `172`
13
13
  - Repository: https://github.com/coo1white/cool-workflow
14
14
 
15
15
  ## Architecture
@@ -93,6 +93,7 @@ multi-agent host -> topology -> blackboard/coordinator
93
93
  - [evidence-reasoning.ts](../src/evidence-reasoning.ts)
94
94
  - [execution-backend.ts](../src/execution-backend.ts)
95
95
  - [gates.ts](../src/gates.ts)
96
+ - [ledger.ts](../src/ledger.ts)
96
97
  - [loop-expansion.ts](../src/loop-expansion.ts)
97
98
  - [mcp-surface.ts](../src/mcp-surface.ts)
98
99
  - [multi-agent-eval.ts](../src/multi-agent-eval.ts)
@@ -145,14 +146,20 @@ multi-agent host -> topology -> blackboard/coordinator
145
146
  - [Contract Migration Tooling](contract-migration-tooling.7.md)
146
147
  - [Control-Plane Scheduling](control-plane-scheduling.7.md)
147
148
  - [Coordinator / Blackboard](coordinator-blackboard.7.md)
149
+ - [Cross-Agent Handoff Ledger](cross-agent-ledger.7.md)
150
+ - [DEMO(7)](demo.7.md)
151
+ - [DOCTOR(7)](doctor.7.md)
148
152
  - [Dogfood One Real Repo](dogfood-one-real-repo.7.md)
149
153
  - [Durable State & Locking](durable-state-and-locking.7.md)
150
154
  - [End-to-End Golden Path](end-to-end-golden-path.7.md)
151
155
  - [ERROR-FEEDBACK(7)](error-feedback.7.md)
152
156
  - [Evidence Adoption Reasoning Chain](evidence-adoption-reasoning-chain.7.md)
153
157
  - [EXECUTION-BACKENDS(7)](execution-backends.7.md)
158
+ - [FIX(7)](fix.7.md)
154
159
  - [Getting Started](getting-started.md)
160
+ - [Handoff ledger — shared-repo setup (T2a)](handoff-setup.md)
155
161
  - [Cool Workflow Docs](index.md)
162
+ - [INIT(7)](init.7.md)
156
163
  - [MCP App Surface](mcp-app-surface.7.md)
157
164
  - [Multi-Agent CLI + MCP Surface](multi-agent-cli-mcp-surface.7.md)
158
165
  - [Multi-Agent Eval & Replay Harness](multi-agent-eval-replay-harness.7.md)
@@ -164,6 +171,7 @@ multi-agent host -> topology -> blackboard/coordinator
164
171
  - [Observability + Cost Accounting](observability-cost-accounting.7.md)
165
172
  - [Operator UX](operator-ux.7.md)
166
173
  - [PIPELINE-RUNNER(7)](pipeline-runner.7.md)
174
+ - [PIPELINE-VERBS(7)](pipeline-verbs.7.md)
167
175
  - [Cool Workflow Project Index](project-index.md)
168
176
  - [Cool Workflow](readme-v0.1.87-full.md)
169
177
  - [Real Execution Backend Integrations](real-execution-backends.7.md)
@@ -172,6 +180,7 @@ multi-agent host -> topology -> blackboard/coordinator
172
180
  - [Release Tooling](release-tooling.7.md)
173
181
  - [Remote-Source Review (`--link`)](remote-source-review.7.md)
174
182
  - [Verifiable Report Bundle](report-verifiable-bundle.7.md)
183
+ - [ROUTINE(7)](routine.7.md)
175
184
  - [Routines](routines.md)
176
185
  - [Run Registry / Control Plane](run-registry-control-plane.7.md)
177
186
  - [Run Retention & Provable Reclamation](run-retention-reclamation.7.md)
@@ -196,6 +205,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
196
205
 
197
206
  - [agent-config-atomic-write-smoke.js](../test/agent-config-atomic-write-smoke.js)
198
207
  - [agent-delegation-drive-smoke.js](../test/agent-delegation-drive-smoke.js)
208
+ - [agent-stream-gate-smoke.js](../test/agent-stream-gate-smoke.js)
199
209
  - [append-run-node-no-realloc-smoke.js](../test/append-run-node-no-realloc-smoke.js)
200
210
  - [architecture-review-fast-automation-smoke.js](../test/architecture-review-fast-automation-smoke.js)
201
211
  - [architecture-review-fast-phase-cache-smoke.js](../test/architecture-review-fast-phase-cache-smoke.js)
@@ -203,6 +213,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
203
213
  - [artifact-integrity-smoke.js](../test/artifact-integrity-smoke.js)
204
214
  - [audit-verify-smoke.js](../test/audit-verify-smoke.js)
205
215
  - [backend-registry-smoke.js](../test/backend-registry-smoke.js)
216
+ - [batch-output-overflow-smoke.js](../test/batch-output-overflow-smoke.js)
206
217
  - [blackboard-state-explosion-management-smoke.js](../test/blackboard-state-explosion-management-smoke.js)
207
218
  - [block-unapproved-tag-smoke.js](../test/block-unapproved-tag-smoke.js)
208
219
  - [budget-scaling-loop-smoke.js](../test/budget-scaling-loop-smoke.js)
@@ -228,6 +239,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
228
239
  - [collaboration-ops-unit-smoke.js](../test/collaboration-ops-unit-smoke.js)
229
240
  - [concurrency-default-smoke.js](../test/concurrency-default-smoke.js)
230
241
  - [concurrent-failure-semantics-smoke.js](../test/concurrent-failure-semantics-smoke.js)
242
+ - [concurrent-subworkflow-cache-nesting-smoke.js](../test/concurrent-subworkflow-cache-nesting-smoke.js)
231
243
  - [concurrent-workflow-dsl-smoke.js](../test/concurrent-workflow-dsl-smoke.js)
232
244
  - [contract-migration-tooling-smoke.js](../test/contract-migration-tooling-smoke.js)
233
245
  - [control-plane-scheduling-smoke.js](../test/control-plane-scheduling-smoke.js)
@@ -235,11 +247,14 @@ Smoke tests mirror the public contracts. The high-signal suites are:
235
247
  - [cw-help-per-command-smoke.js](../test/cw-help-per-command-smoke.js)
236
248
  - [dead-export-removal-guard-smoke.js](../test/dead-export-removal-guard-smoke.js)
237
249
  - [deepseek-agent-wrapper-smoke.js](../test/deepseek-agent-wrapper-smoke.js)
250
+ - [deferred-checkpoint-batching-smoke.js](../test/deferred-checkpoint-batching-smoke.js)
238
251
  - [demo-bundle-smoke.js](../test/demo-bundle-smoke.js)
239
252
  - [det-ids-b-smoke.js](../test/det-ids-b-smoke.js)
240
253
  - [doctor-smoke.js](../test/doctor-smoke.js)
241
254
  - [dogfood-architecture-review-smoke.js](../test/dogfood-architecture-review-smoke.js)
242
255
  - [dogfood-release-smoke.js](../test/dogfood-release-smoke.js)
256
+ - [drive-concurrency-flag-smoke.js](../test/drive-concurrency-flag-smoke.js)
257
+ - [drive-exhaustion-blocked-smoke.js](../test/drive-exhaustion-blocked-smoke.js)
243
258
  - [durable-atomic-write-smoke.js](../test/durable-atomic-write-smoke.js)
244
259
  - [end-to-end-demo-smoke.js](../test/end-to-end-demo-smoke.js)
245
260
  - [end-to-end-golden-path-smoke.js](../test/end-to-end-golden-path-smoke.js)
@@ -257,6 +272,9 @@ Smoke tests mirror the public contracts. The high-signal suites are:
257
272
  - [h7-custom-profile-persist-smoke.js](../test/h7-custom-profile-persist-smoke.js)
258
273
  - [headline-commands-smoke.js](../test/headline-commands-smoke.js)
259
274
  - [incremental-resume-smoke.js](../test/incremental-resume-smoke.js)
275
+ - [ledger-apply-smoke.js](../test/ledger-apply-smoke.js)
276
+ - [ledger-resolution-smoke.js](../test/ledger-resolution-smoke.js)
277
+ - [ledger-verify-smoke.js](../test/ledger-verify-smoke.js)
260
278
  - [loop-bounded-expansion-smoke.js](../test/loop-bounded-expansion-smoke.js)
261
279
  - [mcp-app-surface-smoke.js](../test/mcp-app-surface-smoke.js)
262
280
  - [mcp-surface-registry-smoke.js](../test/mcp-surface-registry-smoke.js)
@@ -282,6 +300,9 @@ Smoke tests mirror the public contracts. The high-signal suites are:
282
300
  - [operator-ux-smoke.js](../test/operator-ux-smoke.js)
283
301
  - [parallel-onramp-smoke.js](../test/parallel-onramp-smoke.js)
284
302
  - [parity-doc-sync-smoke.js](../test/parity-doc-sync-smoke.js)
303
+ - [parse-guard-smoke.js](../test/parse-guard-smoke.js)
304
+ - [parse-hardening-round2-smoke.js](../test/parse-hardening-round2-smoke.js)
305
+ - [path-containment-smoke.js](../test/path-containment-smoke.js)
285
306
  - [pdca-blackboard-loop-smoke.js](../test/pdca-blackboard-loop-smoke.js)
286
307
  - [pii-redaction-smoke.js](../test/pii-redaction-smoke.js)
287
308
  - [pipeline-auto-advance-smoke.js](../test/pipeline-auto-advance-smoke.js)
@@ -300,6 +321,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
300
321
  - [release-check-skip-smoke.js](../test/release-check-skip-smoke.js)
301
322
  - [release-flow-smoke.js](../test/release-flow-smoke.js)
302
323
  - [release-gate-smoke.js](../test/release-gate-smoke.js)
324
+ - [release-pipeline-hygiene-smoke.js](../test/release-pipeline-hygiene-smoke.js)
303
325
  - [release-tooling-smoke.js](../test/release-tooling-smoke.js)
304
326
  - [remote-link-archive-smoke.js](../test/remote-link-archive-smoke.js)
305
327
  - [remote-link-git-smoke.js](../test/remote-link-git-smoke.js)
@@ -323,6 +345,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
323
345
  - [run-resume-drive-smoke.js](../test/run-resume-drive-smoke.js)
324
346
  - [run-retention-reclamation-smoke.js](../test/run-retention-reclamation-smoke.js)
325
347
  - [sample-determinism-smoke.js](../test/sample-determinism-smoke.js)
348
+ - [sandbox-env-batch-hardening-smoke.js](../test/sandbox-env-batch-hardening-smoke.js)
326
349
  - [sandbox-profile-smoke.js](../test/sandbox-profile-smoke.js)
327
350
  - [sched-policy-validation-smoke.js](../test/sched-policy-validation-smoke.js)
328
351
  - [schedule-routine-daemon-smoke.js](../test/schedule-routine-daemon-smoke.js)
@@ -185,3 +185,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
185
185
  0.1.95
186
186
 
187
187
  0.1.96
188
+
189
+ 0.1.97
190
+
191
+ 0.1.98
@@ -325,3 +325,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
325
325
  0.1.95
326
326
 
327
327
  0.1.96
328
+
329
+ 0.1.97
330
+
331
+ 0.1.98
@@ -288,3 +288,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
288
288
  0.1.95
289
289
 
290
290
  0.1.96
291
+
292
+ 0.1.97
293
+
294
+ 0.1.98
@@ -0,0 +1,73 @@
1
+ # ROUTINE(7)
2
+
3
+ ## NAME
4
+
5
+ `cw routine` — make and manage trigger-based workflow routines
6
+
7
+ ## SYNOPSIS
8
+
9
+ ```text
10
+ node dist/cli.js routine create --kind api|github --prompt PROMPT [--match JSON]
11
+ node dist/cli.js routine list [--kind KIND]
12
+ node dist/cli.js routine delete <trigger-id>
13
+ node dist/cli.js routine fire <kind> <payload-file>
14
+ node dist/cli.js routine events [<trigger-id>]
15
+ ```
16
+
17
+ ## DESCRIPTION
18
+
19
+ `cw routine` is the local trigger bridge for CW. It lets you make named
20
+ triggers that fire when something happens — an API event, a GitHub webhook, or
21
+ another outside signal. Each trigger carries a prompt template; when fired,
22
+ the prompt gets filled with the event data and handed to an agent host.
23
+
24
+ CW keeps routine data in:
25
+
26
+ ```text
27
+ .cw/routines/triggers.json
28
+ .cw/routines/payloads/
29
+ ```
30
+
31
+ CW itself does not run a web server or listen for webhooks. The routine bridge
32
+ is a local data store that can be joined to GitHub Actions, webhooks, cron, or
33
+ a small HTTP adapter.
34
+
35
+ ## COMMANDS
36
+
37
+ **create**
38
+ : Make a new trigger. `--kind` is `api` or `github`. `--prompt` is the prompt
39
+ template the agent will see. `--match` is an optional JSON object that filters
40
+ events (for example, `{"action":"opened"}` for GitHub pull requests).
41
+
42
+ **list**
43
+ : List all triggers, or filter by kind with `--kind`.
44
+
45
+ **delete**
46
+ : Remove a trigger by its id.
47
+
48
+ **fire**
49
+ : Record an event against a trigger. Give the trigger kind and a path to a
50
+ JSON payload file. CW matches the payload against the trigger's match rules
51
+ and fills out the prompt.
52
+
53
+ **events**
54
+ : List the events that have been recorded for a trigger.
55
+
56
+ ## FILES
57
+
58
+ ```text
59
+ .cw/routines/triggers.json
60
+ .cw/routines/payloads/<event-id>.json
61
+ ```
62
+
63
+ ## EXIT CODES
64
+
65
+ | Exit | Meaning |
66
+ | --- | --- |
67
+ | 0 | Command done |
68
+ | 1 | Error (bad arguments, missing trigger, etc.) |
69
+
70
+ ## SEE ALSO
71
+
72
+ cw sched — durable run-queue scheduling for workflow runs
73
+ control-plane-scheduling.7.md — the full scheduling and run management design
@@ -468,3 +468,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
468
468
  0.1.95
469
469
 
470
470
  0.1.96
471
+
472
+ 0.1.97
473
+
474
+ 0.1.98
@@ -236,3 +236,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
236
236
  0.1.95
237
237
 
238
238
  0.1.96
239
+
240
+ 0.1.97
241
+
242
+ 0.1.98
@@ -314,3 +314,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
314
314
  0.1.95
315
315
 
316
316
  0.1.96
317
+
318
+ 0.1.97
319
+
320
+ 0.1.98
@@ -250,3 +250,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
250
250
  0.1.95
251
251
 
252
252
  0.1.96
253
+
254
+ 0.1.97
255
+
256
+ 0.1.98
@@ -258,3 +258,7 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
258
258
  0.1.95
259
259
 
260
260
  0.1.96
261
+
262
+ 0.1.97
263
+
264
+ 0.1.98
@@ -1,11 +1,12 @@
1
1
  # Vendor Manifest Source of Truth
2
2
 
3
3
  Every agent host scans a different, hard-coded manifest directory
4
- (`.claude-plugin/`, `.codex-plugin/`, `.agents/`) with a different JSON shape.
5
- You cannot unify the directory or the schema so we do not try. Instead, all
6
- vendor manifests are **generated** from one neutral source and point at the same
7
- shared runtime (`skills/`, `dist/`, `apps/`, the MCP server). No vendor forks the
8
- logic; each manifest is a thin adapter.
4
+ (`.claude-plugin/`, `.codex-plugin/`, `.agents/`, `.gemini-plugin/`,
5
+ `.opencode-plugin/`) with a different JSON shape. You cannot unify the directory
6
+ or the schema so we do not try. Instead, all vendor manifests are **generated**
7
+ from one neutral source and point at the same shared runtime (`skills/`, `dist/`,
8
+ `apps/`, the MCP server). No vendor forks the logic; each manifest is a thin
9
+ adapter.
9
10
 
10
11
  This is the mechanism/policy split: shared assets are mechanism, per-vendor
11
12
  manifests are policy.
@@ -25,13 +26,18 @@ npm run gen:manifests -- --check # fail (exit 1) if any generated file drifted
25
26
 
26
27
  ## Generated outputs (do NOT hand-edit)
27
28
 
29
+ Five vendors are generated today. Paths are repo-root-relative.
30
+
28
31
  | Vendor | Marketplace | Plugin manifest | MCP config | MCP path var |
29
32
  | --- | --- | --- | --- | --- |
30
- | Claude Code | `../../../.claude-plugin/marketplace.json` | `../.claude-plugin/plugin.json` | `../.mcp.json` (auto-discovered) | `${CLAUDE_PLUGIN_ROOT}/` |
31
- | Codex / `.agents` | `../../../.agents/plugins/marketplace.json` | `../.codex-plugin/plugin.json` | `../.codex-plugin/mcp.json` | `./` |
32
-
33
- The two vendors read **different** MCP files, so the plugin-root path variable
34
- never collides.
33
+ | Claude Code | `.claude-plugin/marketplace.json` | `plugins/cool-workflow/.claude-plugin/plugin.json` | `plugins/cool-workflow/.mcp.json` (auto-discovered) | `${CLAUDE_PLUGIN_ROOT}/` |
34
+ | Codex | `.agents/plugins/marketplace.json` | `plugins/cool-workflow/.codex-plugin/plugin.json` | `plugins/cool-workflow/.codex-plugin/mcp.json` | `./` |
35
+ | `.agents` | — | `.agents/plugins/cool-workflow/plugin.json` | `.agents/plugins/cool-workflow/mcp.json` | `./` |
36
+ | Gemini | | `plugins/cool-workflow/.gemini-plugin/plugin.json` | `plugins/cool-workflow/.gemini-plugin/mcp.json` | `./` |
37
+ | OpenCode | — | `plugins/cool-workflow/.opencode-plugin/plugin.json` | `plugins/cool-workflow/.opencode-plugin/mcp.json` | `./` |
38
+
39
+ Each vendor reads its **own** MCP file, so the plugin-root path variable never
40
+ collides.
35
41
 
36
42
  ## Adding a new vendor (Cursor, Windsurf, …)
37
43
 
@@ -2,7 +2,7 @@
2
2
  "_comment": "SINGLE SOURCE OF TRUTH for every vendor manifest. Edit THIS file, then run `npm run gen:manifests`. Do NOT hand-edit the generated vendor manifests (.claude-plugin/, .codex-plugin/, .agents/, .mcp.json) — `npm run gen:manifests -- --check` (run by release:check) will fail if they drift from this source.",
3
3
  "identity": {
4
4
  "name": "cool-workflow",
5
- "version": "0.1.96",
5
+ "version": "0.1.98",
6
6
  "license": "BSD-2-Clause",
7
7
  "homepage": "https://github.com/coo1white/cool-workflow",
8
8
  "author": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cool-workflow",
3
- "version": "0.1.96",
3
+ "version": "0.1.98",
4
4
  "bin": {
5
5
  "cool-workflow": "scripts/cw.js",
6
6
  "cw": "scripts/cw.js"
@@ -457,9 +457,12 @@ function emitReport(model, usage, resultText) {
457
457
  // `logs/agent-stderr.log` sibling is the natural home. Advisory only: never throws,
458
458
  // never changes the exit code or the recorded evidence.
459
459
  function persistStderr(resultPath, text) {
460
- const t = String(text || "").trim();
460
+ let t = String(text || "").trim();
461
461
  if (!t || !resultPath) return;
462
462
  try {
463
+ t = String(t).replace(/\b(sk-[A-Za-z0-9_-]{20,}|ghp_[A-Za-z0-9]{20,}|xox[bprs]-[A-Za-z0-9-]{20,}|Bearer\s+\S+|Authorization:\s*\S+|api[_-]?key[=:]\s*\S+|token[=:]\s*\S+)/gi, (match) => match.slice(0, 4) + "***[REDACTED]");
464
+ const cap = 4096;
465
+ if (t.length > cap) t = t.slice(0, cap) + `\n [truncated from ${t.length} bytes]`;
463
466
  const dir = path.join(path.dirname(resultPath), "logs");
464
467
  fs.mkdirSync(dir, { recursive: true });
465
468
  fs.writeFileSync(path.join(dir, "agent-stderr.log"), `${t}\n`, "utf8");
@@ -21,6 +21,16 @@
21
21
  // per-run override that does NOT touch the user's interactive codex. Tune with
22
22
  // CW_CODEX_REASONING_EFFORT (default "low"); set it to "medium"/"high" to opt back
23
23
  // into more thinking.
24
+ //
25
+ // REVIEW MODE: the default low-effort / read-only sandbox is right for a fast
26
+ // delegated worker or a liveness probe — but WRONG for an independent RELEASE
27
+ // reviewer, which must actually RE-RUN the gate (build, tests, regenerate dist)
28
+ // to earn its verdict. A read-only sandbox can't execute that gate, so the model
29
+ // is structurally unable to verify and tends to fabricate a REJECTED. The release
30
+ // path therefore sets CW_RELEASE_REVIEW=1 (a vendor-agnostic signal from
31
+ // release-flow.js): on that signal this wrapper raises reasoning to "high" and
32
+ // opens the sandbox to "workspace-write" so codex can run the gate it is judging.
33
+ // Explicit CW_CODEX_REASONING_EFFORT / CW_CODEX_SANDBOX always win over the signal.
24
34
 
25
35
  const fs = require("node:fs");
26
36
  const os = require("node:os");
@@ -80,11 +90,31 @@ function recordJsonLine(line) {
80
90
  }
81
91
  }
82
92
 
83
- render.action("codex: reading the repo (read-only)…");
93
+ // A release review (CW_RELEASE_REVIEW=1) must execute the gate it judges, so it
94
+ // needs both stronger reasoning and a sandbox that can write inside the workspace.
95
+ // Explicit env overrides win; otherwise the review signal lifts the fast defaults.
96
+ const reviewMode = process.env.CW_RELEASE_REVIEW === "1";
84
97
 
85
98
  // Cap codex's reasoning effort for CW runs (speed) — overrides config.toml for
86
- // THIS invocation only. Default "low"; CW_CODEX_REASONING_EFFORT opts back up.
87
- const effort = process.env.CW_CODEX_REASONING_EFFORT || "low";
99
+ // THIS invocation only. Default "low"; CW_CODEX_REASONING_EFFORT opts back up, and
100
+ // a release review defaults to "high".
101
+ const effort = process.env.CW_CODEX_REASONING_EFFORT || (reviewMode ? "high" : "low");
102
+
103
+ // Sandbox: read-only is the POLA default (a worker/probe only reads). A release
104
+ // review opens to workspace-write so codex can build/test/regenerate the gate.
105
+ // CW_CODEX_SANDBOX overrides both; an unknown value fails closed (never silently
106
+ // downgraded to read-only, which would re-create the can't-verify failure mode).
107
+ const SANDBOX_MODES = new Set(["read-only", "workspace-write", "danger-full-access"]);
108
+ const sandbox = process.env.CW_CODEX_SANDBOX || (reviewMode ? "workspace-write" : "read-only");
109
+ if (!SANDBOX_MODES.has(sandbox)) {
110
+ process.stderr.write(
111
+ `codex-agent: invalid CW_CODEX_SANDBOX="${sandbox}" — expected one of ${[...SANDBOX_MODES].join(", ")}\n`
112
+ );
113
+ process.exit(2);
114
+ }
115
+
116
+ render.action(`codex: reading the repo (${sandbox})…`);
117
+
88
118
  const args = [
89
119
  "exec",
90
120
  "--json",
@@ -93,7 +123,7 @@ const args = [
93
123
  "--output-last-message",
94
124
  finalPath,
95
125
  "--sandbox",
96
- "read-only",
126
+ sandbox,
97
127
  "--color",
98
128
  "never",
99
129
  "-"
@@ -83,7 +83,7 @@ const canonicalApps = [
83
83
  "--source",
84
84
  "plugins/cool-workflow/docs/workflow-app-framework.7.md",
85
85
  "--scope",
86
- "Cool Workflow v0.1.96",
86
+ "Cool Workflow v0.1.98",
87
87
  "--freshness",
88
88
  "as of release preparation"
89
89
  ]
@@ -117,14 +117,14 @@ function main() {
117
117
  assert.ok(summary, `${app.id} must appear in app list`);
118
118
  assert.equal(summary.sourceKind, "app-directory");
119
119
  assert.equal(summary.legacy, false);
120
- assert.equal(summary.version, "0.1.96");
120
+ assert.equal(summary.version, "0.1.98");
121
121
 
122
122
  const validation = runJson(["app", "validate", manifestPath]);
123
123
  assert.equal(validation.valid, true, `${app.id} manifest must validate`);
124
124
 
125
125
  const shown = runJson(["app", "show", app.id]);
126
126
  assert.equal(shown.app.id, app.id);
127
- assert.equal(shown.app.version, "0.1.96");
127
+ assert.equal(shown.app.version, "0.1.98");
128
128
  assert.ok(shown.app.metadata.canonical, `${app.id} must be marked canonical`);
129
129
  assert.ok(shown.app.sandboxProfiles.length > 0, `${app.id} must declare sandbox profiles`);
130
130
  assertTaskIdsUnique(shown);
@@ -135,7 +135,7 @@ function main() {
135
135
  const plan = runJson(["plan", app.id, ...app.args(workspace)]);
136
136
  const state = JSON.parse(fs.readFileSync(plan.statePath, "utf8"));
137
137
  assert.equal(state.workflow.app.id, app.id);
138
- assert.equal(state.workflow.app.version, "0.1.96");
138
+ assert.equal(state.workflow.app.version, "0.1.98");
139
139
  assert.equal(state.workflow.app.metadata.canonical, true);
140
140
  assert.ok(state.tasks.some((task) => task.requiresEvidence), `${app.id} plan must include evidence gates`);
141
141
  assert.ok(state.tasks.every((task) => task.sandboxProfileId), `${app.id} plan must include sandbox hints`);
@@ -8,43 +8,66 @@
8
8
  //
9
9
  // Reads jobs JSON on stdin, spawns ALL concurrently (shell:false, inherited env —
10
10
  // the agent's own credentials resolve; CW never reads them), per-job SIGTERM at
11
- // timeoutMs + SIGKILL at +5s, caps each captured stdout at 32MB, and prints the
12
- // outcome array when every job has settled. stderr is drained (a full pipe must
13
- // never wedge a child). A kill yields exitCode null the no-exit-code refusal.
11
+ // timeoutMs + SIGKILL at +5s, caps each captured stdout at 32MB. Streams ONE
12
+ // NDJSON line per job `{i, spawnError?, exitCode, stdout}\n` the INSTANT
13
+ // that job settles (not once at the end): the parent's spawnSync call has its
14
+ // own combined-output cap, so writing incrementally means a job whose line
15
+ // already flushed keeps its real outcome even if a LATER job's output pushes
16
+ // the combined stream over that cap and the whole child gets killed. `i` is
17
+ // the job's index (settle order is concurrent, not submission order — the
18
+ // parent cannot infer which line belongs to which job without it). stderr is
19
+ // drained (a full pipe must never wedge a child). A kill yields exitCode null
20
+ // — the no-exit-code refusal.
14
21
  //
15
22
  // THE RED LINE: this child only `spawn`s the operator-resolved agent binary with
16
- // shell:false. It imports NO model SDK and reads NO credentials. Behavior MUST
17
- // stay byte-identical to the previous embedded string.
23
+ // shell:false. It imports NO model SDK and reads NO credentials.
18
24
 
19
25
  const { spawn } = require("node:child_process");
20
26
  let raw = "";
27
+ const MAX_STDIN_BYTES = 32 * 1024 * 1024;
21
28
  process.stdin.setEncoding("utf8");
22
- process.stdin.on("data", (d) => (raw += d));
29
+ process.stdin.on("data", (d) => { if (raw.length < MAX_STDIN_BYTES) raw += d; });
23
30
  process.stdin.on("end", () => {
24
- const jobs = JSON.parse(raw);
31
+ let jobs;
32
+ try {
33
+ jobs = JSON.parse(raw);
34
+ } catch (e) {
35
+ process.stdout.write(JSON.stringify([{ spawnError: `invalid stdin JSON: ${String(e && e.message || e)}`, exitCode: null, stdout: "" }]));
36
+ return;
37
+ }
25
38
  if (!jobs.length) { process.stdout.write("[]"); return; }
26
- const out = new Array(jobs.length);
27
- let pending = jobs.length;
28
39
  const CAP = 32 * 1024 * 1024;
29
40
  jobs.forEach((job, i) => {
30
41
  let stdout = "";
42
+ let stdoutBytes = 0;
43
+ let stdoutTruncated = false;
31
44
  let settled = false;
32
45
  const settle = (o) => {
33
46
  if (settled) return;
34
47
  settled = true;
35
- out[i] = o;
36
- if (--pending === 0) process.stdout.write(JSON.stringify(out));
48
+ process.stdout.write(JSON.stringify({ i, ...o }) + "\n");
37
49
  };
38
50
  let child;
39
51
  try {
40
- child = spawn(job.binary, job.args, { cwd: job.cwd, env: process.env, shell: false });
52
+ child = spawn(job.binary, job.args, { cwd: job.cwd, env: job.env || process.env, shell: false });
41
53
  } catch (error) {
42
54
  settle({ spawnError: String((error && error.message) || error), exitCode: null, stdout: "" });
43
55
  return;
44
56
  }
45
57
  const term = setTimeout(() => { try { child.kill("SIGTERM"); } catch {} }, job.timeoutMs);
46
58
  const kill = setTimeout(() => { try { child.kill("SIGKILL"); } catch {} }, job.timeoutMs + 5000);
47
- child.stdout.on("data", (d) => { if (stdout.length < CAP) stdout += d; });
59
+ child.stdout.on("data", (d) => {
60
+ const chunk = Buffer.isBuffer(d) ? d : Buffer.from(String(d));
61
+ stdoutBytes += chunk.length;
62
+ if (stdoutTruncated) return;
63
+ const remaining = CAP - Buffer.byteLength(stdout);
64
+ if (remaining <= 0 || chunk.length > remaining) {
65
+ stdoutTruncated = true;
66
+ if (remaining > 0) stdout += chunk.subarray(0, remaining).toString();
67
+ return;
68
+ }
69
+ stdout += chunk.toString();
70
+ });
48
71
  child.stderr.on("data", () => {});
49
72
  child.on("error", (error) => {
50
73
  clearTimeout(term); clearTimeout(kill);
@@ -52,6 +75,10 @@ process.stdin.on("end", () => {
52
75
  });
53
76
  child.on("close", (code) => {
54
77
  clearTimeout(term); clearTimeout(kill);
78
+ if (stdoutTruncated) {
79
+ settle({ spawnError: `stdout exceeded ${CAP} byte cap (${stdoutBytes} bytes)`, exitCode: null, stdout: "" });
80
+ return;
81
+ }
55
82
  settle({ exitCode: typeof code === "number" ? code : null, stdout });
56
83
  });
57
84
  });
@@ -15,7 +15,8 @@
15
15
  // API request. Behavior MUST stay byte-identical to the previous embedded string.
16
16
 
17
17
  (async () => {
18
- const read = () => new Promise((res) => { let b = ""; process.stdin.on("data", (c) => (b += c)); process.stdin.on("end", () => res(b)); });
18
+ const MAX_STDIN_BYTES = 32 * 1024 * 1024;
19
+ const read = () => new Promise((res) => { let b = ""; process.stdin.on("data", (c) => { if (b.length < MAX_STDIN_BYTES) b += c; }); process.stdin.on("end", () => res(b)); });
19
20
  try {
20
21
  const job = JSON.parse((await read()) || "{}");
21
22
  const endpoint = process.env.CW_DELEGATE_ENDPOINT;
@@ -6,7 +6,7 @@ const fs = require("node:fs");
6
6
  const path = require("node:path");
7
7
  const { CoolWorkflowRunner } = require("../dist/orchestrator.js");
8
8
 
9
- const TARGET_VERSION = "0.1.96";
9
+ const TARGET_VERSION = "0.1.98";
10
10
  const PREVIOUS_VERSION = "0.1.31";
11
11
  const pluginRoot = path.resolve(__dirname, "..");
12
12
  const repoRoot = path.resolve(pluginRoot, "..", "..");
@@ -33,7 +33,7 @@ function main() {
33
33
  const appValidation = runJson(["app", "validate", "end-to-end-golden-path"], pluginRoot);
34
34
  assert.equal(appValidation.valid, true);
35
35
  assert.equal(appValidation.summary.id, "end-to-end-golden-path");
36
- assert.equal(appValidation.summary.version, "0.1.96");
36
+ assert.equal(appValidation.summary.version, "0.1.98");
37
37
 
38
38
  const plan = runJson(
39
39
  [
@@ -42,7 +42,7 @@ function main() {
42
42
  "--repo",
43
43
  tmp,
44
44
  "--question",
45
- "Prove the deterministic v0.1.96 end-to-end golden path."
45
+ "Prove the deterministic v0.1.98 end-to-end golden path."
46
46
  ],
47
47
  pluginRoot
48
48
  );
@@ -52,7 +52,7 @@ function main() {
52
52
 
53
53
  let state = readJson(plan.statePath);
54
54
  assert.equal(state.workflow.app.id, "end-to-end-golden-path");
55
- assert.equal(state.workflow.app.version, "0.1.96");
55
+ assert.equal(state.workflow.app.version, "0.1.98");
56
56
  assert.equal(state.loopStage, "interpret");
57
57
 
58
58
  const dispatch = runJson(["dispatch", plan.runId, "--limit", "1", "--sandbox", "readonly"], tmp);
@@ -195,7 +195,7 @@ function main() {
195
195
  assert.equal(reportPath, plan.reportPath);
196
196
  assert.ok(fs.existsSync(reportPath));
197
197
  const report = fs.readFileSync(reportPath, "utf8");
198
- assert.match(report, /Workflow App: end-to-end-golden-path@0\.1\.96/);
198
+ assert.match(report, /Workflow App: end-to-end-golden-path@0\.1\.98/);
199
199
  assert.match(report, /## Candidates/);
200
200
  assert.match(report, /## Trust Audit/);
201
201
  assert.match(report, /## Acceptance Rationale/);