cool-workflow 0.1.95 → 0.1.97
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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/apps/architecture-review/app.json +1 -1
- package/apps/architecture-review/workflow.js +3 -3
- package/apps/architecture-review-fast/app.json +1 -1
- package/apps/end-to-end-golden-path/app.json +1 -1
- package/apps/pr-review-fix-ci/app.json +1 -1
- package/apps/release-cut/app.json +1 -1
- package/apps/research-synthesis/app.json +1 -1
- package/dist/agent-config.js +2 -1
- package/dist/candidate-scoring.js +3 -3
- package/dist/capability-core.js +6 -1
- package/dist/cli/handlers/scheduling.js +7 -1
- package/dist/dispatch.js +12 -6
- package/dist/drive.js +10 -0
- package/dist/evidence-grounding.js +18 -13
- package/dist/execution-backend/agent.js +1 -1
- package/dist/execution-backend/probes.js +22 -6
- package/dist/execution-backend.js +61 -8
- package/dist/mcp-server.js +4 -0
- package/dist/node-snapshot.js +3 -3
- package/dist/onramp.js +2 -0
- package/dist/orchestrator/app-operations.js +6 -0
- package/dist/orchestrator/cli-options.js +8 -2
- package/dist/orchestrator/lifecycle-operations.js +16 -5
- package/dist/orchestrator/migration-operations.js +1 -1
- package/dist/orchestrator.js +26 -1
- package/dist/reclamation.js +8 -2
- package/dist/run-export.js +10 -1
- package/dist/run-registry/derive.js +4 -1
- package/dist/sandbox-profile.js +6 -1
- package/dist/scheduler.js +14 -14
- package/dist/schema-validate.js +8 -2
- package/dist/state-explosion/helpers.js +4 -21
- package/dist/state-explosion/size.js +63 -0
- package/dist/state-explosion.js +18 -71
- package/dist/state.js +47 -9
- package/dist/triggers.js +7 -1
- package/dist/trust-audit.js +27 -2
- package/dist/util/fingerprint.js +19 -0
- package/dist/util/fingerprint.test.js +27 -0
- package/dist/version.js +1 -1
- package/dist/workbench-host.js +17 -0
- package/dist/workbench.js +19 -17
- package/dist/worker-isolation.js +25 -1
- package/docs/agent-delegation-drive.7.md +66 -1
- package/docs/cli-mcp-parity.7.md +4 -0
- package/docs/contract-migration-tooling.7.md +4 -0
- package/docs/control-plane-scheduling.7.md +4 -0
- package/docs/demo.7.md +80 -0
- package/docs/doctor.7.md +97 -0
- package/docs/durable-state-and-locking.7.md +4 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
- package/docs/execution-backends.7.md +4 -0
- package/docs/fix.7.md +44 -0
- package/docs/init.7.md +62 -0
- package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
- package/docs/multi-agent-eval-replay-harness.7.md +4 -0
- package/docs/multi-agent-operator-ux.7.md +4 -0
- package/docs/node-snapshot-diff-replay.7.md +4 -0
- package/docs/observability-cost-accounting.7.md +4 -0
- package/docs/pipeline-verbs.7.md +93 -0
- package/docs/project-index.md +20 -4
- package/docs/real-execution-backends.7.md +4 -0
- package/docs/release-and-migration.7.md +4 -0
- package/docs/release-tooling.7.md +20 -0
- package/docs/routine.7.md +73 -0
- package/docs/routines.md +30 -0
- package/docs/run-registry-control-plane.7.md +4 -0
- package/docs/run-retention-reclamation.7.md +4 -0
- package/docs/sandbox-profiles.7.md +15 -0
- package/docs/state-explosion-management.7.md +4 -0
- package/docs/team-collaboration.7.md +4 -0
- package/docs/web-desktop-workbench.7.md +4 -0
- package/manifest/README.md +16 -10
- package/manifest/plugin.manifest.json +1 -1
- package/package.json +5 -3
- package/scripts/agents/agent-adapter-core.js +25 -1
- package/scripts/agents/claude-p-agent.js +10 -2
- package/scripts/agents/codex-agent.js +22 -2
- package/scripts/agents/gemini-agent.js +10 -2
- package/scripts/agents/opencode-agent.js +10 -2
- package/scripts/canonical-apps.js +4 -4
- package/scripts/children/batch-delegate-child.js +10 -3
- package/scripts/children/http-delegate-child.js +2 -1
- package/scripts/dogfood-release.js +1 -1
- package/scripts/golden-path.js +4 -4
- package/scripts/release-flow.js +24 -6
- package/scripts/release-gate.sh +1 -1
package/dist/trust-audit.js
CHANGED
|
@@ -11,6 +11,8 @@ exports.recordTrustAuditEvent = recordTrustAuditEvent;
|
|
|
11
11
|
exports.recordSandboxPathDecision = recordSandboxPathDecision;
|
|
12
12
|
exports.recordSandboxPolicyDecision = recordSandboxPolicyDecision;
|
|
13
13
|
exports.recordHostAttestation = recordHostAttestation;
|
|
14
|
+
exports.setAuditEventCache = setAuditEventCache;
|
|
15
|
+
exports.clearAuditEventCache = clearAuditEventCache;
|
|
14
16
|
exports.listTrustAuditEvents = listTrustAuditEvents;
|
|
15
17
|
exports.searchAuditEvents = searchAuditEvents;
|
|
16
18
|
exports.summarizeTrustAudit = summarizeTrustAudit;
|
|
@@ -300,9 +302,25 @@ function recordHostAttestation(run, input) {
|
|
|
300
302
|
source: "host-attested"
|
|
301
303
|
});
|
|
302
304
|
}
|
|
305
|
+
// Per-request event log cache (v0.1.95). When set, readEvents returns
|
|
306
|
+
// memoized results keyed by event log path. Clears after each request.
|
|
307
|
+
let _eventLogCache = null;
|
|
308
|
+
function setAuditEventCache(cache) {
|
|
309
|
+
_eventLogCache = cache;
|
|
310
|
+
}
|
|
311
|
+
function clearAuditEventCache() {
|
|
312
|
+
_eventLogCache = null;
|
|
313
|
+
}
|
|
303
314
|
function listTrustAuditEvents(run) {
|
|
304
315
|
const audit = ensureTrustAudit(run);
|
|
305
|
-
|
|
316
|
+
if (_eventLogCache) {
|
|
317
|
+
const cached = _eventLogCache.get(audit.eventLogPath);
|
|
318
|
+
if (cached)
|
|
319
|
+
return cached;
|
|
320
|
+
}
|
|
321
|
+
const events = readEventsRaw(audit.eventLogPath).events.sort(compareEvents);
|
|
322
|
+
_eventLogCache?.set(audit.eventLogPath, events);
|
|
323
|
+
return events;
|
|
306
324
|
}
|
|
307
325
|
/** Search audit events by kind, worker, or candidate (v0.1.65).
|
|
308
326
|
* Filters are AND-ed; empty filters match all. */
|
|
@@ -515,7 +533,14 @@ function auditRoot(run) {
|
|
|
515
533
|
return run.paths.auditDir || node_path_1.default.join(run.paths.runDir, "audit");
|
|
516
534
|
}
|
|
517
535
|
function readEvents(eventLogPath) {
|
|
518
|
-
|
|
536
|
+
if (_eventLogCache) {
|
|
537
|
+
const cached = _eventLogCache.get(eventLogPath);
|
|
538
|
+
if (cached)
|
|
539
|
+
return cached;
|
|
540
|
+
}
|
|
541
|
+
const events = readEventsRaw(eventLogPath).events.sort(compareEvents);
|
|
542
|
+
_eventLogCache?.set(eventLogPath, events);
|
|
543
|
+
return events;
|
|
519
544
|
}
|
|
520
545
|
function workerRows(events, run) {
|
|
521
546
|
const workerIds = unique([...(run.workers || []).map((worker) => worker.id), ...events.map((event) => event.workerId || "")]).sort();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.fingerprintStrings = fingerprintStrings;
|
|
7
|
+
exports.fingerprintRecords = fingerprintRecords;
|
|
8
|
+
// Deterministic content fingerprint — the single canonical implementation.
|
|
9
|
+
// Replaces duplicated copies in observability.ts and run-registry.ts (v0.1.95).
|
|
10
|
+
// Pure function of its arguments; never imports run state or high-level modules.
|
|
11
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
12
|
+
function fingerprintStrings(values) {
|
|
13
|
+
const hash = node_crypto_1.default.createHash("sha256");
|
|
14
|
+
hash.update(JSON.stringify([...values].sort()));
|
|
15
|
+
return `sha256:${hash.digest("hex").slice(0, 32)}`;
|
|
16
|
+
}
|
|
17
|
+
function fingerprintRecords(records) {
|
|
18
|
+
return fingerprintStrings(records.map((r) => `${r.id}:${r.status || ""}`).sort());
|
|
19
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
// Unit test for the canonical fingerprint utility (v0.1.95).
|
|
4
|
+
// Pure function — no run state or tmpdir needed.
|
|
5
|
+
const strict_1 = require("node:assert/strict");
|
|
6
|
+
const node_test_1 = require("node:test");
|
|
7
|
+
const fingerprint_1 = require("../util/fingerprint");
|
|
8
|
+
(0, node_test_1.test)("fingerprintStrings returns a sha256: prefix", () => {
|
|
9
|
+
const fp = (0, fingerprint_1.fingerprintStrings)(["a", "b"]);
|
|
10
|
+
(0, strict_1.ok)(fp.startsWith("sha256:"), "must have sha256: prefix");
|
|
11
|
+
(0, strict_1.equal)(fp.length, 32 + "sha256:".length, "must be 32 hex chars");
|
|
12
|
+
});
|
|
13
|
+
(0, node_test_1.test)("fingerprintStrings is deterministic and order-independent", () => {
|
|
14
|
+
const a = (0, fingerprint_1.fingerprintStrings)(["b", "a", "c"]);
|
|
15
|
+
const b = (0, fingerprint_1.fingerprintStrings)(["c", "b", "a"]);
|
|
16
|
+
(0, strict_1.equal)(a, b, "same values in different order must produce same fingerprint");
|
|
17
|
+
});
|
|
18
|
+
(0, node_test_1.test)("fingerprintStrings produces distinct values for different inputs", () => {
|
|
19
|
+
const a = (0, fingerprint_1.fingerprintStrings)(["x"]);
|
|
20
|
+
const b = (0, fingerprint_1.fingerprintStrings)(["y"]);
|
|
21
|
+
(0, strict_1.ok)(a !== b, "different inputs must produce different fingerprints");
|
|
22
|
+
});
|
|
23
|
+
(0, node_test_1.test)("fingerprintRecords uses id:status sorted", () => {
|
|
24
|
+
const a = (0, fingerprint_1.fingerprintRecords)([{ id: "b", status: "ok" }, { id: "a", status: "fail" }]);
|
|
25
|
+
const b = (0, fingerprint_1.fingerprintRecords)([{ id: "a", status: "fail" }, { id: "b", status: "ok" }]);
|
|
26
|
+
(0, strict_1.equal)(a, b, "records in different order must produce same fingerprint");
|
|
27
|
+
});
|
package/dist/version.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MIN_SUPPORTED_RUN_STATE_SCHEMA_VERSION = exports.LEGACY_RUN_STATE_SCHEMA_VERSION = exports.CURRENT_RUN_STATE_SCHEMA_VERSION = exports.WORKFLOW_APP_SCHEMA_VERSION = exports.CURRENT_COOL_WORKFLOW_VERSION = void 0;
|
|
4
|
-
exports.CURRENT_COOL_WORKFLOW_VERSION = "0.1.
|
|
4
|
+
exports.CURRENT_COOL_WORKFLOW_VERSION = "0.1.97";
|
|
5
5
|
exports.WORKFLOW_APP_SCHEMA_VERSION = 1;
|
|
6
6
|
exports.CURRENT_RUN_STATE_SCHEMA_VERSION = 1;
|
|
7
7
|
exports.LEGACY_RUN_STATE_SCHEMA_VERSION = 0;
|
package/dist/workbench-host.js
CHANGED
|
@@ -24,6 +24,7 @@ exports.WorkbenchHost = void 0;
|
|
|
24
24
|
const node_http_1 = __importDefault(require("node:http"));
|
|
25
25
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
26
26
|
const node_path_1 = __importDefault(require("node:path"));
|
|
27
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
27
28
|
const workbench_1 = require("./workbench");
|
|
28
29
|
const ALLOWED_HOSTNAMES = new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
|
|
29
30
|
const CONTENT_TYPES = {
|
|
@@ -85,7 +86,23 @@ class WorkbenchHost {
|
|
|
85
86
|
if ((req.method || "GET").toUpperCase() !== "GET") {
|
|
86
87
|
return this.send(res, 405, { error: "read-only: only GET is permitted" }, { Allow: "GET" });
|
|
87
88
|
}
|
|
89
|
+
// Optional token auth (CW_WORKBENCH_TOKEN). When set, every request must
|
|
90
|
+
// carry the token as an Authorization: Bearer header or ?token= query param.
|
|
91
|
+
// Off by default — single-user loopback is already gated by the OS boundary.
|
|
88
92
|
const url = new URL(req.url || "/", `http://${this.host}`);
|
|
93
|
+
const requiredToken = (process.env.CW_WORKBENCH_TOKEN || "").trim();
|
|
94
|
+
if (requiredToken) {
|
|
95
|
+
const bearer = (req.headers.authorization || "").replace(/^Bearer\s+/i, "").trim();
|
|
96
|
+
const queryToken = url.searchParams.get("token") || "";
|
|
97
|
+
const tokenBuf = Buffer.from(requiredToken);
|
|
98
|
+
const bearerBuf = Buffer.from(bearer);
|
|
99
|
+
const queryBuf = Buffer.from(queryToken);
|
|
100
|
+
const tokenOk = bearerBuf.length === tokenBuf.length && node_crypto_1.default.timingSafeEqual(bearerBuf, tokenBuf);
|
|
101
|
+
const queryOk = queryBuf.length === tokenBuf.length && node_crypto_1.default.timingSafeEqual(queryBuf, tokenBuf);
|
|
102
|
+
if (!tokenOk && !queryOk) {
|
|
103
|
+
return this.send(res, 401, { error: "unauthorized: token mismatch" });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
89
106
|
const route = decodeURIComponent(url.pathname);
|
|
90
107
|
if (route === "/" || route === "/index.html")
|
|
91
108
|
return this.sendAsset(res, "index.html");
|
package/dist/workbench.js
CHANGED
|
@@ -110,23 +110,25 @@ function buildPanels(runner, runId) {
|
|
|
110
110
|
* never fabricated. */
|
|
111
111
|
function buildWorkbenchRunView(runner, runId) {
|
|
112
112
|
const id = String(runId || "");
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
113
|
+
return runner.loadWithCache((r) => {
|
|
114
|
+
let resolved = true;
|
|
115
|
+
let error;
|
|
116
|
+
try {
|
|
117
|
+
r.loadRun(id);
|
|
118
|
+
}
|
|
119
|
+
catch (caught) {
|
|
120
|
+
resolved = false;
|
|
121
|
+
error = caught instanceof Error ? caught.message : String(caught);
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
schemaVersion: 1,
|
|
125
|
+
surface: "workbench",
|
|
126
|
+
runId: id,
|
|
127
|
+
resolved,
|
|
128
|
+
...(error ? { error } : {}),
|
|
129
|
+
panels: buildPanels(r, id)
|
|
130
|
+
};
|
|
131
|
+
});
|
|
130
132
|
}
|
|
131
133
|
// ---------------------------------------------------------------------------
|
|
132
134
|
// Cross-run entry (v0.1.28 Run Registry) — composed from already-declared
|
package/dist/worker-isolation.js
CHANGED
|
@@ -393,7 +393,31 @@ function recordWorkerRetryAttempt(run, workerId, attempts, reason, options = {})
|
|
|
393
393
|
function validateWorkerBoundary(run, workerId, options = {}) {
|
|
394
394
|
const scope = requireWorkerScope(run, workerId);
|
|
395
395
|
const rawPath = String(options.path || scope.resultPath);
|
|
396
|
-
|
|
396
|
+
const policy = sandboxPolicyForBoundary(run, scope, options);
|
|
397
|
+
const violation = (0, sandbox_profile_1.validateSandboxWrite)(policy, rawPath, workerId);
|
|
398
|
+
if (!violation) {
|
|
399
|
+
// Write paths are enforced by CW at this boundary. Command and network limits
|
|
400
|
+
// are declared in the sandbox policy but enforced by the execution backend
|
|
401
|
+
// (the host/container runtime). Record the policy split transparently so the
|
|
402
|
+
// audit trail shows what CW checked vs what was delegated to the host.
|
|
403
|
+
(0, trust_audit_1.recordTrustAuditEvent)(run, {
|
|
404
|
+
kind: "worker.sandbox-boundary",
|
|
405
|
+
decision: "allowed",
|
|
406
|
+
source: "cw-validated",
|
|
407
|
+
workerId,
|
|
408
|
+
taskId: scope.taskId,
|
|
409
|
+
sandboxProfileId: policy.id,
|
|
410
|
+
policyRef: `execute=${policy.execute.mode} network=${policy.network.mode} env.inherit=${policy.env.inherit}`,
|
|
411
|
+
command: policy.execute.mode,
|
|
412
|
+
networkTarget: policy.network.mode,
|
|
413
|
+
metadata: {
|
|
414
|
+
enforced_by_cw: ["write-paths"],
|
|
415
|
+
delegated_to_host: ["execute", "network", "env"],
|
|
416
|
+
env_inherit: policy.env.inherit
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
return violation;
|
|
397
421
|
}
|
|
398
422
|
function summarizeWorkers(run) {
|
|
399
423
|
const workers = listWorkerScopes(run);
|
|
@@ -27,6 +27,52 @@ HTTP API. Any API key comes from the agent's *own* inherited env; CW never reads
|
|
|
27
27
|
or keeps a record of it. Adding a provider SDK to `package.json` would lose the
|
|
28
28
|
neutral-audit moat and is the red line.
|
|
29
29
|
|
|
30
|
+
## Architecture — the boundary (core ↔ agent backend ↔ wrappers)
|
|
31
|
+
|
|
32
|
+
The core gives only an INTERFACE: the `agent` execution backend plus a small
|
|
33
|
+
text/process contract. The four vendors (claude / codex / gemini / deepseek)
|
|
34
|
+
live OUTSIDE the core as out-of-process wrapper scripts in `scripts/agents/` —
|
|
35
|
+
pure config, never imported by `src/`, behind the same seam.
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
user
|
|
39
|
+
│ cw -q "…" -codex (headline shortcut; also -claude/-gemini/-deepseek)
|
|
40
|
+
▼
|
|
41
|
+
┌──────────── CW core (src/ — zero runtime deps, imports NO model SDK, holds NO key) ───────────┐
|
|
42
|
+
│ │
|
|
43
|
+
│ command-surface.ts agent-config.ts execution-backend ("agent" driver) │
|
|
44
|
+
│ -codex → builtin:codex ─► builtin:<name> ─► runAgentProcess: │
|
|
45
|
+
│ node <dir>/<name>-agent.js spawnSync(binary, args, shell:false)│
|
|
46
|
+
│ {{input}} {{result}} · inherits env · captures stdout │
|
|
47
|
+
│ (builtin-templates.json: DATA) · records handle{process}+attestation│
|
|
48
|
+
└───────────────────────────────────────┬───────────────────────────────────────────────────────┘
|
|
49
|
+
THE SEAM (text / process contract) │
|
|
50
|
+
in : argv {{input}}=worker input.md {{result}}=worker result.md
|
|
51
|
+
out: wrapper writes result.md + ONE stdout JSON line {model,usage,result} → parseAgentReport
|
|
52
|
+
│
|
|
53
|
+
── red line ── core never reads a key; each wrapper resolves its OWN key from inherited env
|
|
54
|
+
│
|
|
55
|
+
┌──────── external wrappers (scripts/agents/*.js — "CONFIG, not a CW runtime dependency") ───────┐
|
|
56
|
+
│ claude-p-agent.js codex-agent.js gemini-opencode-agent.js deepseek-agent.js │
|
|
57
|
+
│ │ │ └──────────┬──────────────┘ │
|
|
58
|
+
│ ▼ ▼ ▼ (3-line shims) │
|
|
59
|
+
│ claude -p codex exec opencode-agent.js │
|
|
60
|
+
│ (Anthropic CLI) (-c effort, sandbox) opencode run --model … │
|
|
61
|
+
│ (deepseek + gemini keys live here) │
|
|
62
|
+
└─────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
63
|
+
Add a vendor = drop a wrapper script + one line in builtin-templates.json — NO core edit.
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Each box maps to one source seam: the flag map lives in `command-surface.ts`
|
|
67
|
+
(`-codex` → `builtin:codex`); `agent-config.ts` expands `builtin:<name>` into
|
|
68
|
+
`node <dir>/<name>-agent.js {{input}} {{result}}` by reading
|
|
69
|
+
`builtin-templates.json` (the registry is DATA, not a kernel literal); the
|
|
70
|
+
`agent` driver's `runAgentProcess` does the `spawnSync` and reads back the one
|
|
71
|
+
stdout JSON line via `parseAgentReport`. So the four vendors are **delegated
|
|
72
|
+
agent wrappers, not an event-"hook" system**. The seam is generic: any
|
|
73
|
+
`CW_AGENT_COMMAND="node my-agent.js {{input}} {{result}}"` or a configured HTTP
|
|
74
|
+
endpoint plugs in the same way — the four builtins are just bundled examples.
|
|
75
|
+
|
|
30
76
|
## Operator-chosen model is policy; agent-reported model is the attestation
|
|
31
77
|
|
|
32
78
|
Any model id CW passes **into** the agent invocation (`CW_AGENT_MODEL`
|
|
@@ -250,7 +296,22 @@ The built-in templates are:
|
|
|
250
296
|
claude and codex run their own CLIs; gemini and deepseek route through opencode
|
|
251
297
|
(where their keys live), each proven by a local, deterministic wrapper smoke
|
|
252
298
|
(override the model with CW_GEMINI_MODEL / CW_DEEPSEEK_MODEL). GLM stays an
|
|
253
|
-
external agent command or HTTP endpoint. CW still imports no model SDK.
|
|
299
|
+
external agent command or HTTP endpoint. CW still imports no model SDK. The same
|
|
300
|
+
headline shortcuts pick these builtins on the top-level CLI: `cw -q "..."
|
|
301
|
+
-claude` / `-codex` / `-gemini` / `-deepseek`.
|
|
302
|
+
|
|
303
|
+
The codex wrapper caps codex's reasoning effort for CW runs so a heavy
|
|
304
|
+
`model_reasoning_effort = "high"` in the user's `~/.codex/config.toml` does not
|
|
305
|
+
make every read/grep turn slow. It passes `codex exec -c
|
|
306
|
+
model_reasoning_effort=<effort>` (default `low`) for THAT run only — the user's
|
|
307
|
+
interactive codex is untouched. Raise it with `CW_CODEX_REASONING_EFFORT`
|
|
308
|
+
(`low` | `medium` | `high`).
|
|
309
|
+
|
|
310
|
+
When an agent hop fails, CW core keeps only the child's stdout + exit code, so a
|
|
311
|
+
bare `failed (exit 1)` hid the real cause (a relay 5xx, an auth error, a killed
|
|
312
|
+
run). Each wrapper now also drops the failed child's stderr to
|
|
313
|
+
`<run>/workers/<worker>/logs/agent-stderr.log`, so the reason is readable after
|
|
314
|
+
the fact without changing the recorded, byte-stable evidence.
|
|
254
315
|
|
|
255
316
|
## Compatibility
|
|
256
317
|
|
|
@@ -346,3 +407,7 @@ The one-command `cw -q` headline now routes the question and defaults the repo t
|
|
|
346
407
|
0.1.94
|
|
347
408
|
|
|
348
409
|
0.1.95
|
|
410
|
+
|
|
411
|
+
0.1.96
|
|
412
|
+
|
|
413
|
+
0.1.97
|
package/docs/cli-mcp-parity.7.md
CHANGED
package/docs/demo.7.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# DEMO(7)
|
|
2
|
+
|
|
3
|
+
## NAME
|
|
4
|
+
|
|
5
|
+
`cw demo` — prove CW trust guarantees with one command
|
|
6
|
+
|
|
7
|
+
## SYNOPSIS
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
node dist/cli.js demo tamper [--json]
|
|
11
|
+
node dist/cli.js demo bundle [--json]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## DESCRIPTION
|
|
15
|
+
|
|
16
|
+
`cw demo` is a self-contained proof of CW's central trust claims. It works
|
|
17
|
+
without an agent and without a network connection. Every run is hermetic
|
|
18
|
+
(fully self-contained) — it builds its own state, tampers with it in known
|
|
19
|
+
ways, and checks that the tampering is caught. Nothing is read from or
|
|
20
|
+
written to the real file system outside a short-term temp directory.
|
|
21
|
+
|
|
22
|
+
No agent is needed; both demos work when the setup has no agent at all.
|
|
23
|
+
|
|
24
|
+
## DEMO TAMPER
|
|
25
|
+
|
|
26
|
+
`cw demo tamper` proves that CW catches forged records offline — with only a
|
|
27
|
+
public key, no server. It:
|
|
28
|
+
|
|
29
|
+
1. Builds a signed telemetry ledger with three hops.
|
|
30
|
+
2. Tampers with it in three layers:
|
|
31
|
+
- **Hashes**: Changes a record's data and recomputes the record hash to hide
|
|
32
|
+
it. The hash chain breaks — the next record's `previousHash` does not
|
|
33
|
+
match, so the chain is no longer valid.
|
|
34
|
+
- **Signatures**: Inflates token counts and keeps the old signature. The
|
|
35
|
+
signature does not match the new data — the verifier catches it.
|
|
36
|
+
- **Findings**: Edits a signed finding (severity HIGH → LOW) after it was
|
|
37
|
+
signed by the agent. The signature check on the ed25519 envelope fails
|
|
38
|
+
because the signed bytes changed.
|
|
39
|
+
3. Verifies each tampered ledger with only the public key.
|
|
40
|
+
|
|
41
|
+
If all three forgeries are caught, the proof holds and the demo exits 0.
|
|
42
|
+
If any tamper goes undetected, the demo exits 1 — this is a regression in
|
|
43
|
+
the integrity guarantee.
|
|
44
|
+
|
|
45
|
+
## DEMO BUNDLE
|
|
46
|
+
|
|
47
|
+
`cw demo bundle` proves that exported report bundles are verifiable offline. It:
|
|
48
|
+
|
|
49
|
+
1. Builds a full telemetry chain, signs it, and exports a sealed portable
|
|
50
|
+
bundle (archive bytes + telemetry chain + trust-audit chain + embedded
|
|
51
|
+
public key).
|
|
52
|
+
2. Tampers with the bundle in two ways:
|
|
53
|
+
- **Telemetry chain**: Forges a record in the chain. The archive's file
|
|
54
|
+
digests stay valid (the archive was built from the tampered bytes), but
|
|
55
|
+
`report verify-bundle` re-checks the chain and catches it.
|
|
56
|
+
- **Signature + usage**: Inflates token counts and reseals. The signature
|
|
57
|
+
check and hash chain both break.
|
|
58
|
+
3. Verifies each tampered bundle with `report verify-bundle`.
|
|
59
|
+
|
|
60
|
+
If all forgeries are caught with only the bundle's own public key, the proof
|
|
61
|
+
holds. No repo, no server, no key handed over.
|
|
62
|
+
|
|
63
|
+
## EXIT CODES
|
|
64
|
+
|
|
65
|
+
| Exit | Meaning |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| 0 | All tampering was caught — trust guarantees hold |
|
|
68
|
+
| 1 | A tamper went undetected — integrity guarantee regression |
|
|
69
|
+
|
|
70
|
+
## FILES
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
src/telemetry-demo.ts
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## SEE ALSO
|
|
77
|
+
|
|
78
|
+
report-verifiable-bundle.7.md — offline bundle verification in detail
|
|
79
|
+
trust-model.md — the trust model and its limits
|
|
80
|
+
security-trust-hardening.7.md — security and trust hardening
|
package/docs/doctor.7.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# DOCTOR(7)
|
|
2
|
+
|
|
3
|
+
## NAME
|
|
4
|
+
|
|
5
|
+
`cw doctor` — check the setup and name all problems with their fixes
|
|
6
|
+
|
|
7
|
+
## SYNOPSIS
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
node dist/cli.js doctor
|
|
11
|
+
node dist/cli.js doctor --json
|
|
12
|
+
node dist/cli.js doctor --fix
|
|
13
|
+
node dist/cli.js doctor --onramp
|
|
14
|
+
node dist/cli.js doctor --onramp --changed-from origin/main
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## DESCRIPTION
|
|
18
|
+
|
|
19
|
+
`cw doctor` is a read-only check of your CW setup, based on `brew doctor`. It
|
|
20
|
+
probes your machine and says what is wrong and what to do about it — before a
|
|
21
|
+
run fails with a strange error.
|
|
22
|
+
|
|
23
|
+
The command never makes any file; it only reads. Running it changes nothing on
|
|
24
|
+
disk.
|
|
25
|
+
|
|
26
|
+
It gives back a report with one line for every check. Each check has a status
|
|
27
|
+
(`ok`, `warn`, or `fail`) and a clear note. Checks that are not `ok` carry a
|
|
28
|
+
`fix` line with the right command or step to put things right.
|
|
29
|
+
|
|
30
|
+
If any check has status `fail`, the command exits with code 1 (non-zero). A
|
|
31
|
+
`warn` (for example, no agent yet — demos and previews still work) does not
|
|
32
|
+
make the exit fail.
|
|
33
|
+
|
|
34
|
+
## CHECKS
|
|
35
|
+
|
|
36
|
+
The command runs six checks in order:
|
|
37
|
+
|
|
38
|
+
**node**
|
|
39
|
+
: The Node.js version. CW needs v18 or higher. A `fail` here stops everything.
|
|
40
|
+
|
|
41
|
+
**agent**
|
|
42
|
+
: The AI agent backend. CW can auto-detect agents (Claude, Codex, Gemini,
|
|
43
|
+
OpenCode) or take one from `CW_AGENT_COMMAND` / `--agent-command`. Without one,
|
|
44
|
+
real runs report `status: blocked`, but `demo` and `--preview` still work.
|
|
45
|
+
|
|
46
|
+
**agent-binary**
|
|
47
|
+
: When the agent is set by a command name (not auto or HTTP), this check sees if
|
|
48
|
+
the binary is on `$PATH`. Missing here gives a `warn` — the run will get a clear
|
|
49
|
+
error later, but CW will not guess at a different agent.
|
|
50
|
+
|
|
51
|
+
**git**
|
|
52
|
+
: The `git` command. CW uses it for commit place of origin. A `warn` here means
|
|
53
|
+
commit roots will be recorded as absent; no other part of a run needs git.
|
|
54
|
+
|
|
55
|
+
**home-registry**
|
|
56
|
+
: The cross-repo run index at `$CW_HOME` (default `$HOME/.local/state/cool-workflow`).
|
|
57
|
+
This location must be writable. A `fail` here blocks discovery across repos.
|
|
58
|
+
|
|
59
|
+
**repo-state**
|
|
60
|
+
: The per-repo run store under `<cwd>/.cw`. Must be writable. A `warn` here
|
|
61
|
+
means runs stay in-memory only — you can use `--cwd PATH` to point at another
|
|
62
|
+
writable root.
|
|
63
|
+
|
|
64
|
+
## OPTIONS
|
|
65
|
+
|
|
66
|
+
`--json`
|
|
67
|
+
: Give back the full report as a stable JSON object. Good for scripts.
|
|
68
|
+
|
|
69
|
+
`--fix`
|
|
70
|
+
: Give back only the fix commands for every non-ok check. Same as running `cw fix`
|
|
71
|
+
by itself.
|
|
72
|
+
|
|
73
|
+
`--onramp`
|
|
74
|
+
: Add a quick-start guide to the human output, with recommended checks and a
|
|
75
|
+
three-step path to your first report.
|
|
76
|
+
|
|
77
|
+
`--changed-from <ref>`
|
|
78
|
+
: When used with `--onramp`, make the quick-start checks cover only files changed
|
|
79
|
+
since `<ref>` (a Git branch, tag, or commit). Good for CI and code reading.
|
|
80
|
+
|
|
81
|
+
## FILES
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
src/doctor.ts
|
|
85
|
+
dist/doctor.js
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## EXIT CODES
|
|
89
|
+
|
|
90
|
+
| Exit | Meaning |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| 0 | All checks ok (may have warnings) |
|
|
93
|
+
| 1 | One or more checks have status `fail` |
|
|
94
|
+
|
|
95
|
+
## SEE ALSO
|
|
96
|
+
|
|
97
|
+
cw fix — the same checks, but gives back only the fix commands
|
package/docs/fix.7.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# FIX(7)
|
|
2
|
+
|
|
3
|
+
## NAME
|
|
4
|
+
|
|
5
|
+
`cw fix` — give back the fix commands for all setup problems
|
|
6
|
+
|
|
7
|
+
## SYNOPSIS
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
node dist/cli.js fix
|
|
11
|
+
node dist/cli.js fix --json
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## DESCRIPTION
|
|
15
|
+
|
|
16
|
+
`cw fix` runs the same setup checks as `cw doctor`, but gives back only the
|
|
17
|
+
fix commands — one numbered step for every check that has a problem. No
|
|
18
|
+
running check detail, no status glyphs; just the directions you need to put
|
|
19
|
+
things right.
|
|
20
|
+
|
|
21
|
+
When the output is empty ("No fixes needed."), the setup is clean and nothing
|
|
22
|
+
needs doing.
|
|
23
|
+
|
|
24
|
+
Like `cw doctor`, the command only reads — it never makes a file or does a
|
|
25
|
+
fix on its own. You are meant to run the fix commands yourself.
|
|
26
|
+
|
|
27
|
+
If any check has status `fail`, the command exits with code 1.
|
|
28
|
+
|
|
29
|
+
## OPTIONS
|
|
30
|
+
|
|
31
|
+
`--json`
|
|
32
|
+
: Give back the full doctor report as a stable JSON object, with the same shape
|
|
33
|
+
as `cw doctor --json`. The `checks` array carries every fix string.
|
|
34
|
+
|
|
35
|
+
## EXIT CODES
|
|
36
|
+
|
|
37
|
+
| Exit | Meaning |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| 0 | No fixes needed — all checks ok or only warnings |
|
|
40
|
+
| 1 | One or more checks have status `fail` |
|
|
41
|
+
|
|
42
|
+
## SEE ALSO
|
|
43
|
+
|
|
44
|
+
cw doctor — the full setup check with detail for every check
|
package/docs/init.7.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# INIT(7)
|
|
2
|
+
|
|
3
|
+
## NAME
|
|
4
|
+
|
|
5
|
+
`cw init` — scaffold a new workflow definition from nothing
|
|
6
|
+
|
|
7
|
+
## SYNOPSIS
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
node dist/cli.js init <workflow-id> [--title TITLE] [--output PATH] [--force]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## DESCRIPTION
|
|
14
|
+
|
|
15
|
+
`cw init` makes a new workflow definition file — a `.workflow.js` file filled
|
|
16
|
+
with a simple template. The template has a basic run shape: one step with a
|
|
17
|
+
sandbox profile, one evidence gate, and the hooks you need to add your own
|
|
18
|
+
steps.
|
|
19
|
+
|
|
20
|
+
This is how you start a new workflow app from zero. After `init`, you have a
|
|
21
|
+
real file you can edit to make your own run shape.
|
|
22
|
+
|
|
23
|
+
The workflow id you give is turned into a safe file name (spaces become dashes,
|
|
24
|
+
special signs are taken out). By default, the file is written to the current
|
|
25
|
+
working directory, but you can point it somewhere else with `--output`.
|
|
26
|
+
|
|
27
|
+
If a file of that name is already there, the command refuses to overwrite it
|
|
28
|
+
unless you pass `--force`.
|
|
29
|
+
|
|
30
|
+
## OPTIONS
|
|
31
|
+
|
|
32
|
+
`--title TITLE`
|
|
33
|
+
: A human name for the workflow. If not given, a title is made from the id.
|
|
34
|
+
|
|
35
|
+
`--output PATH`
|
|
36
|
+
: Where to write the workflow file. Default is `<id>.workflow.js` in the
|
|
37
|
+
current directory.
|
|
38
|
+
|
|
39
|
+
`--force`
|
|
40
|
+
: Overwrite an existing file. Without this flag, the command fails if the
|
|
41
|
+
file already exists.
|
|
42
|
+
|
|
43
|
+
## EXIT CODES
|
|
44
|
+
|
|
45
|
+
| Exit | Meaning |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| 0 | Workflow file written |
|
|
48
|
+
| 1 | Missing workflow id, invalid id, or file exists without `--force` |
|
|
49
|
+
|
|
50
|
+
## FILES
|
|
51
|
+
|
|
52
|
+
```text
|
|
53
|
+
src/orchestrator.ts (init method)
|
|
54
|
+
src/workflow-app-framework.ts (template renderer)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## SEE ALSO
|
|
58
|
+
|
|
59
|
+
cw list — see all workflow apps you have
|
|
60
|
+
cw info <id> — read the shape of a workflow app
|
|
61
|
+
workflow-app-framework.7.md — the full framework for writing workflow apps
|
|
62
|
+
pipeline-verbs.7.md — plan, dispatch, result (the pipeline engine)
|