cool-workflow 0.2.0 → 0.2.1
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/README.md +7 -5
- package/apps/architecture-review/app.json +2 -2
- package/apps/architecture-review/workflow.js +12 -12
- 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/cli/dispatch.js +2 -2
- package/dist/cli/parseargv.js +2 -2
- package/dist/core/capability-table.js +13 -29
- package/dist/core/format/help.js +2 -2
- package/dist/core/hash.js +1 -1
- package/dist/core/multi-agent/candidate-scoring.js +1 -1
- package/dist/core/multi-agent/eval-replay.js +1 -1
- package/dist/core/multi-agent/runtime.js +2 -2
- package/dist/core/multi-agent/trust-policy.js +1 -1
- package/dist/core/pipeline/contract.js +1 -1
- package/dist/core/pipeline/drive-decide.js +1 -1
- package/dist/core/state/contract-migration.js +1 -1
- package/dist/core/state/migrations.js +2 -2
- package/dist/core/state/node-snapshot.js +1 -1
- package/dist/core/state/state-explosion/graph.js +4 -4
- package/dist/core/state/state-explosion/helpers.js +3 -3
- package/dist/core/state/state-explosion/report.js +1 -1
- package/dist/core/state/state-explosion/size.js +1 -1
- package/dist/core/state/state-node.js +2 -2
- package/dist/core/state/types.js +1 -1
- package/dist/core/trust/ledger.js +1 -1
- package/dist/core/trust/telemetry-attestation.js +3 -3
- package/dist/core/trust/telemetry-ledger.js +2 -2
- package/dist/core/version.js +1 -1
- package/dist/core/workflow-apps/app-schema.js +1 -1
- package/dist/mcp/dispatch.js +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/shell/agent-config.js +1 -1
- package/dist/shell/doctor.js +17 -4
- package/dist/shell/drive.js +21 -9
- package/dist/shell/execution-backend/agent.js +42 -10
- package/dist/shell/execution-backend/ci.js +1 -1
- package/dist/shell/execution-backend/container.js +1 -1
- package/dist/shell/execution-backend/envelopes.js +1 -1
- package/dist/shell/execution-backend/local.js +1 -1
- package/dist/shell/execution-backend/probes.js +1 -1
- package/dist/shell/execution-backend/registry.js +1 -1
- package/dist/shell/execution-backend/remote.js +1 -1
- package/dist/shell/execution-backend/types.js +1 -1
- package/dist/shell/fs-atomic.js +1 -1
- package/dist/shell/ledger-io.js +1 -1
- package/dist/shell/metrics-cli.js +4 -2
- package/dist/shell/multi-agent-host.js +1 -1
- package/dist/shell/reclamation-io.js +1 -1
- package/dist/shell/registry-cli.js +15 -0
- package/dist/shell/run-registry-io.js +1 -1
- package/dist/shell/sandbox-profile.js +1 -1
- package/dist/shell/scheduler-io.js +46 -37
- package/dist/shell/scheduling-io.js +32 -22
- package/dist/shell/telemetry-ledger-io.js +1 -1
- package/dist/shell/term.js +1 -1
- package/dist/shell/trust-audit.js +4 -3
- package/dist/shell/workbench-host.js +9 -1
- package/dist/shell/workbench.js +6 -11
- package/docs/agent-delegation-drive.7.md +2 -0
- package/docs/cli-mcp-parity.7.md +2 -0
- package/docs/contract-migration-tooling.7.md +2 -0
- package/docs/control-plane-scheduling.7.md +2 -0
- package/docs/durable-state-and-locking.7.md +2 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
- package/docs/execution-backends.7.md +2 -0
- package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
- package/docs/multi-agent-eval-replay-harness.7.md +2 -0
- package/docs/multi-agent-operator-ux.7.md +2 -0
- package/docs/node-snapshot-diff-replay.7.md +2 -0
- package/docs/observability-cost-accounting.7.md +4 -1
- package/docs/project-index.md +8 -3
- package/docs/real-execution-backends.7.md +2 -0
- package/docs/release-and-migration.7.md +2 -0
- package/docs/release-tooling.7.md +2 -0
- package/docs/remote-source-review.7.md +4 -4
- package/docs/report-verifiable-bundle.7.md +1 -1
- package/docs/run-registry-control-plane.7.md +2 -0
- package/docs/run-retention-reclamation.7.md +2 -0
- package/docs/security-trust-hardening.7.md +3 -1
- package/docs/state-explosion-management.7.md +2 -0
- package/docs/team-collaboration.7.md +2 -0
- package/docs/web-desktop-workbench.7.md +13 -1
- package/manifest/plugin.manifest.json +1 -1
- package/package.json +1 -1
- package/scripts/agents/agent-adapter-core.js +22 -2
- package/scripts/agents/claude-p-agent.js +8 -3
- package/scripts/agents/gemini-agent.js +5 -1
- package/scripts/agents/opencode-agent.js +5 -1
- package/scripts/canonical-apps.js +4 -4
- package/scripts/dogfood-release.js +1 -1
- package/scripts/golden-path.js +4 -4
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// shell/scheduler-io.ts — Scheduler (cw schedule/loop), the daemon tick
|
|
3
3
|
// (cw schedule daemon), and routine triggers (cw routine).
|
|
4
4
|
//
|
|
5
|
-
// MILESTONE 10 (
|
|
5
|
+
// MILESTONE 10 (docs/rebuild/PLAN.md build order, step 10). Byte-exact port of the
|
|
6
6
|
// old build's src/scheduler.ts + src/daemon.ts + src/triggers.ts. Reuses
|
|
7
7
|
// shell/fs-atomic.ts's `withFileLock`/`readJson`/`writeJson`/`safeFileName`
|
|
8
8
|
// directly (no reimplementation).
|
|
@@ -387,51 +387,60 @@ class RoutineTriggerBridge {
|
|
|
387
387
|
this.storePath = path.join(this.cwd, ".cw", "routines", "triggers.json");
|
|
388
388
|
this.payloadsDir = path.join(this.cwd, ".cw", "routines", "payloads");
|
|
389
389
|
}
|
|
390
|
+
locked(fn) {
|
|
391
|
+
return (0, fs_atomic_1.withFileLock)(this.storePath, fn);
|
|
392
|
+
}
|
|
390
393
|
create(options) {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
394
|
+
return this.locked(() => {
|
|
395
|
+
const now = new Date().toISOString();
|
|
396
|
+
const store = this.load();
|
|
397
|
+
// Monotonic id, NOT triggers.length: delete shrinks the collection, so
|
|
398
|
+
// a length-based seq would reuse a live id after delete+create.
|
|
399
|
+
const seq = (store.nextTriggerSeq || 0) + 1;
|
|
400
|
+
store.nextTriggerSeq = seq;
|
|
401
|
+
const trigger = {
|
|
402
|
+
id: createTriggerId(normalizeTriggerKind(options.kind), seq),
|
|
403
|
+
kind: normalizeTriggerKind(options.kind),
|
|
404
|
+
createdAt: now,
|
|
405
|
+
updatedAt: now,
|
|
406
|
+
source: String(options.source || options.kind || "api"),
|
|
407
|
+
prompt: requiredString(options.prompt, "prompt"),
|
|
408
|
+
workflowId: stringOption(options.workflowId),
|
|
409
|
+
runId: stringOption(options.runId),
|
|
410
|
+
match: parseJsonObject(options.match),
|
|
411
|
+
metadata: parseJsonObject(options.metadata),
|
|
412
|
+
};
|
|
413
|
+
store.triggers.push(trigger);
|
|
414
|
+
this.save(store);
|
|
415
|
+
return trigger;
|
|
416
|
+
});
|
|
412
417
|
}
|
|
413
418
|
list(kind) {
|
|
414
419
|
const store = this.load();
|
|
415
420
|
return kind ? store.triggers.filter((trigger) => trigger.kind === kind) : store.triggers;
|
|
416
421
|
}
|
|
417
422
|
delete(id) {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
+
return this.locked(() => {
|
|
424
|
+
const store = this.load();
|
|
425
|
+
const before = store.triggers.length;
|
|
426
|
+
store.triggers = store.triggers.filter((trigger) => trigger.id !== id);
|
|
427
|
+
this.save(store);
|
|
428
|
+
return { deleted: store.triggers.length !== before, id };
|
|
429
|
+
});
|
|
423
430
|
}
|
|
424
431
|
fire(kind, payload) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
432
|
+
return this.locked(() => {
|
|
433
|
+
const normalizedKind = normalizeTriggerKind(kind);
|
|
434
|
+
const store = this.load();
|
|
435
|
+
const now = new Date().toISOString();
|
|
436
|
+
const base = store.events.length;
|
|
437
|
+
const events = store.triggers
|
|
438
|
+
.filter((trigger) => trigger.kind === normalizedKind)
|
|
439
|
+
.map((trigger, index) => this.createEvent(trigger, payload, now, base + index + 1));
|
|
440
|
+
store.events.push(...events);
|
|
441
|
+
this.save(store);
|
|
442
|
+
return events;
|
|
443
|
+
});
|
|
435
444
|
}
|
|
436
445
|
events(triggerId) {
|
|
437
446
|
const store = this.load();
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// lease-plan mechanism over the run-registry-io.ts queue store, plus the
|
|
4
4
|
// policy file IO.
|
|
5
5
|
//
|
|
6
|
-
// MILESTONE 10 (
|
|
6
|
+
// MILESTONE 10 (docs/rebuild/PLAN.md build order, step 10). Byte-exact port of the
|
|
7
7
|
// old build's src/scheduling.ts + the sched-specific slice of
|
|
8
8
|
// src/capability-core.ts. `sched` is a DIFFERENT system from `schedule`
|
|
9
9
|
// (see scheduler-io.ts's file header) — this operates on
|
|
@@ -251,43 +251,53 @@ function schedLeaseCli(options = {}) {
|
|
|
251
251
|
const now = nowIso(options);
|
|
252
252
|
const policy = loadSchedulingPolicy(registry).policy;
|
|
253
253
|
const limit = options.limit === undefined ? undefined : Number(options.limit);
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
254
|
+
return (0, fs_atomic_1.withFileLock)(registry.queueFilePath(), () => {
|
|
255
|
+
const { entries, leases } = applyLease(registry.loadQueueEntries(), policy, now, limit);
|
|
256
|
+
registry.saveQueueEntries(entries);
|
|
257
|
+
return { schemaVersion: 1, now, granted: leases.length, leases };
|
|
258
|
+
});
|
|
257
259
|
}
|
|
258
260
|
function schedReleaseCli(leaseId, options = {}) {
|
|
259
261
|
const registry = new run_registry_io_1.RunRegistry(resolveCwd(options));
|
|
260
262
|
const now = nowIso(options);
|
|
261
263
|
const failed = isTrue(options.failed);
|
|
262
264
|
const reason = typeof options.reason === "string" ? options.reason : undefined;
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
265
|
+
return (0, fs_atomic_1.withFileLock)(registry.queueFilePath(), () => {
|
|
266
|
+
const { entries, matched } = leaseRelease(registry.loadQueueEntries(), leaseId, loadSchedulingPolicy(registry).policy, now, { failed, reason });
|
|
267
|
+
if (!matched)
|
|
268
|
+
throw new Error(`No active lease to release: ${leaseId}`);
|
|
269
|
+
registry.saveQueueEntries(entries);
|
|
270
|
+
return { schemaVersion: 1, released: leaseId, failed };
|
|
271
|
+
});
|
|
268
272
|
}
|
|
269
273
|
function schedCompleteCli(leaseId, options = {}) {
|
|
270
274
|
const registry = new run_registry_io_1.RunRegistry(resolveCwd(options));
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
275
|
+
return (0, fs_atomic_1.withFileLock)(registry.queueFilePath(), () => {
|
|
276
|
+
const { entries, matched } = leaseComplete(registry.loadQueueEntries(), leaseId, nowIso(options));
|
|
277
|
+
if (!matched)
|
|
278
|
+
throw new Error(`No active lease to complete: ${leaseId}`);
|
|
279
|
+
registry.saveQueueEntries(entries);
|
|
280
|
+
return { schemaVersion: 1, completed: leaseId };
|
|
281
|
+
});
|
|
276
282
|
}
|
|
277
283
|
function schedReclaimCli(options = {}) {
|
|
278
284
|
const registry = new run_registry_io_1.RunRegistry(resolveCwd(options));
|
|
279
285
|
const now = nowIso(options);
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
286
|
+
return (0, fs_atomic_1.withFileLock)(registry.queueFilePath(), () => {
|
|
287
|
+
const { entries, reclaimed } = reclaimExpired(registry.loadQueueEntries(), loadSchedulingPolicy(registry).policy, now);
|
|
288
|
+
registry.saveQueueEntries(entries);
|
|
289
|
+
return { schemaVersion: 1, now, reclaimed };
|
|
290
|
+
});
|
|
283
291
|
}
|
|
284
292
|
function schedResetCli(id, options = {}) {
|
|
285
293
|
const registry = new run_registry_io_1.RunRegistry(resolveCwd(options));
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
294
|
+
return (0, fs_atomic_1.withFileLock)(registry.queueFilePath(), () => {
|
|
295
|
+
const { entries, matched } = resetEntry(registry.loadQueueEntries(), id);
|
|
296
|
+
if (!matched)
|
|
297
|
+
throw new Error(`No parked entry to reset: ${id}`);
|
|
298
|
+
registry.saveQueueEntries(entries);
|
|
299
|
+
return { schemaVersion: 1, reset: id };
|
|
300
|
+
});
|
|
291
301
|
}
|
|
292
302
|
function schedPolicyShowCli(options = {}) {
|
|
293
303
|
const registry = new run_registry_io_1.RunRegistry(resolveCwd(options));
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// math lives in core/trust/telemetry-ledger.ts.
|
|
5
5
|
//
|
|
6
6
|
// MILESTONE 8. Byte-exact port of the old build's src/telemetry-ledger.ts
|
|
7
|
-
// IO-touching half. CRITICAL invariant (
|
|
7
|
+
// IO-touching half. CRITICAL invariant (docs/rebuild/PLAN.md byte-compat item 12):
|
|
8
8
|
// an ABSENT telemetry.json is an empty, clean-verifying chain
|
|
9
9
|
// (`present:false`); a PRESENT but unparseable one is CORRUPT — reads
|
|
10
10
|
// report `telemetry-ledger-corrupt`, and append THROWS
|
package/dist/shell/term.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// shell/term.ts — zero-dependency terminal styling.
|
|
3
3
|
//
|
|
4
|
-
// MILESTONE 5 (
|
|
4
|
+
// MILESTONE 5 (docs/rebuild/PLAN.md build order, step 5, doctor/fix): TTY-gated
|
|
5
5
|
// ANSI formatting, so a piped run (every conformance case pipes with
|
|
6
6
|
// NO_COLOR=1) prints plain text. EXTENDED at MILESTONE 11 (reporting/
|
|
7
7
|
// observability) with the rest of the old build's src/term.ts: indent,
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// the era-rule check + trustAuditGenesis on top, and switches
|
|
14
14
|
// computeEventHash to use core/hash.ts's named `eventHashInput` export
|
|
15
15
|
// (byte-identical behavior to the pre-existing inline JSON-round-trip,
|
|
16
|
-
// now shared with the rest of the hash-dedup story per
|
|
16
|
+
// now shared with the rest of the hash-dedup story per docs/rebuild/PLAN.md byte-
|
|
17
17
|
// compat item 2).
|
|
18
18
|
//
|
|
19
19
|
// Evidence: SPEC/ledger-trust.md "Trust-audit chain", invariant 10 (era
|
|
@@ -97,7 +97,7 @@ function trustAuditGenesis(runId) {
|
|
|
97
97
|
* itself, via core/hash.ts's `eventHashInput` (the JSON round-trip
|
|
98
98
|
* pre-pass that drops nested `undefined`-valued keys BEFORE the
|
|
99
99
|
* sort-and-stringify step — not a formatting flag on the same shape,
|
|
100
|
-
* see
|
|
100
|
+
* see docs/rebuild/PLAN.md byte-compat item 2). Hashing the PERSISTED form this
|
|
101
101
|
* way makes record-time hashes equal verify-time (parsed-from-disk)
|
|
102
102
|
* hashes. */
|
|
103
103
|
function computeEventHash(event) {
|
|
@@ -142,7 +142,7 @@ function listTrustAuditEvents(run) {
|
|
|
142
142
|
* hash are reported as `unchained` (skipped), NOT treated as a forgery
|
|
143
143
|
* — they predate the chain.
|
|
144
144
|
*
|
|
145
|
-
* ERA RULE (
|
|
145
|
+
* ERA RULE (docs/rebuild/PLAN.md, SPEC/ledger-trust.md invariant 10): a single
|
|
146
146
|
* run is written by one code version, so a log is all-chained (chain
|
|
147
147
|
* era) or all-legacy (pre-chain). An unchained (eventHash-less) line
|
|
148
148
|
* mixed into an otherwise-chained log is a forgery attempt — dropping
|
|
@@ -277,6 +277,7 @@ function recordTrustAuditEvent(run, input) {
|
|
|
277
277
|
normalizedPath: input.normalizedPath ? path.resolve(input.normalizedPath) : undefined,
|
|
278
278
|
command: input.command,
|
|
279
279
|
networkTarget: input.networkTarget,
|
|
280
|
+
envVars: input.envVars?.filter(Boolean).sort(),
|
|
280
281
|
evidence: normalizeEvidence(run, input.evidence || [], { source: input.source, workerId: input.workerId, taskId: input.taskId, resultNodeId: input.nodeId }),
|
|
281
282
|
evidenceRefs: unique(input.evidenceRefs || []).sort(),
|
|
282
283
|
parentEventIds: unique(input.parentEventIds || []).sort(),
|
|
@@ -233,8 +233,16 @@ class WorkbenchHost {
|
|
|
233
233
|
/** `cw workbench serve` entry point: `--once`/`--json` prints ONLY the
|
|
234
234
|
* descriptor (compact JSON) and starts nothing; the default binds and
|
|
235
235
|
* blocks, printing one compact line (`{...descriptor, boundPort}`)
|
|
236
|
-
* once listening.
|
|
236
|
+
* once listening. `--require-token` is a strict opt-in: default behavior
|
|
237
|
+
* (unauthenticated local reads when no token is configured) is
|
|
238
|
+
* unchanged unless the caller explicitly asks to fail closed. */
|
|
237
239
|
async run() {
|
|
240
|
+
const requireToken = Boolean(this.args.requireToken || this.args["require-token"]);
|
|
241
|
+
if (requireToken && !this.token) {
|
|
242
|
+
process.stderr.write("workbench serve --require-token: CW_WORKBENCH_TOKEN is not set; refusing to start.\n");
|
|
243
|
+
process.exitCode = 1;
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
238
246
|
const once = Boolean(this.args.once || this.args.json);
|
|
239
247
|
if (once) {
|
|
240
248
|
process.stdout.write(`${JSON.stringify(this.descriptor(true))}\n`);
|
package/dist/shell/workbench.js
CHANGED
|
@@ -116,18 +116,13 @@ function buildWorkbenchRunView(runId, args = {}) {
|
|
|
116
116
|
}
|
|
117
117
|
return { schemaVersion: 1, surface: "workbench", runId, resolved, ...(resolveError ? { error: resolveError } : {}), panels };
|
|
118
118
|
}
|
|
119
|
+
/** Package-relative resolution only — never falls back to the invocation
|
|
120
|
+
* cwd. `ui/` ships as a sibling of `dist/` in the published package
|
|
121
|
+
* (package.json's `files`), so from `dist/shell/workbench.js` the fixed
|
|
122
|
+
* path is two levels up. Matches the existing precedent in
|
|
123
|
+
* execution-backend/agent.ts's BATCH_DELEGATE_CHILD_SCRIPT resolution. */
|
|
119
124
|
function workbenchUiRoot() {
|
|
120
|
-
|
|
121
|
-
for (let i = 0; i < 8; i++) {
|
|
122
|
-
const candidate = path.join(dir, "plugins", "cool-workflow", exports.WORKBENCH_UI_RELATIVE);
|
|
123
|
-
if (require("node:fs").existsSync(candidate))
|
|
124
|
-
return candidate;
|
|
125
|
-
const parent = path.dirname(dir);
|
|
126
|
-
if (parent === dir)
|
|
127
|
-
break;
|
|
128
|
-
dir = parent;
|
|
129
|
-
}
|
|
130
|
-
return path.join(process.cwd(), exports.WORKBENCH_UI_RELATIVE);
|
|
125
|
+
return path.resolve(__dirname, "..", "..", exports.WORKBENCH_UI_RELATIVE);
|
|
131
126
|
}
|
|
132
127
|
const WORKBENCH_ROUTES = [
|
|
133
128
|
{ method: "GET", path: "/", description: "Index page (or the UI's index.html, when installed)." },
|
package/docs/cli-mcp-parity.7.md
CHANGED
|
@@ -113,7 +113,8 @@ read-only metrics panel from the same payload, showing coverage and
|
|
|
113
113
|
- `cw metrics summary` — the cross-repo rollup over the v0.1.28 run registry:
|
|
114
114
|
pooled rates, summed attested usage/cost with coverage, and per-app and
|
|
115
115
|
per-backend breakdowns. `--scope repo|home`; runs that cannot be read are counted
|
|
116
|
-
(`unreadableRuns`), never quietly dropped.
|
|
116
|
+
(`unreadableRuns`), never quietly dropped. `--limit N` caps how many run
|
|
117
|
+
states are loaded (default 50, matching `run list`/`run search`'s own floor).
|
|
117
118
|
|
|
118
119
|
MCP hosts call `cw_metrics_show` and `cw_metrics_summary` with the same
|
|
119
120
|
payloads. Old runs load and report `unreported` cost while still giving correct
|
|
@@ -243,3 +244,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
|
|
|
243
244
|
0.1.98
|
|
244
245
|
|
|
245
246
|
0.2.0
|
|
247
|
+
|
|
248
|
+
0.2.1
|
package/docs/project-index.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# Cool Workflow Project Index
|
|
2
2
|
|
|
3
|
-
Generated from the current repository code on 2026-07-
|
|
3
|
+
Generated from the current repository code on 2026-07-07 by `npm run sync:project-index`.
|
|
4
4
|
|
|
5
5
|
## Snapshot
|
|
6
6
|
|
|
7
7
|
- Package: `cool-workflow`
|
|
8
|
-
- Version: `0.2.
|
|
8
|
+
- Version: `0.2.1`
|
|
9
9
|
- Source modules: `129`
|
|
10
10
|
- Workflow apps: `8`
|
|
11
11
|
- Docs: `61`
|
|
12
|
-
- Smoke tests: `
|
|
12
|
+
- Smoke tests: `178`
|
|
13
13
|
- Repository: https://github.com/coo1white/cool-workflow
|
|
14
14
|
|
|
15
15
|
## Architecture
|
|
@@ -263,6 +263,8 @@ multi-agent host -> topology -> blackboard/coordinator
|
|
|
263
263
|
|
|
264
264
|
Smoke tests mirror the public contracts. The high-signal suites are:
|
|
265
265
|
|
|
266
|
+
- [agent-backend-concurrent-user-env-smoke.js](../test/agent-backend-concurrent-user-env-smoke.js)
|
|
267
|
+
- [agent-backend-user-env-smoke.js](../test/agent-backend-user-env-smoke.js)
|
|
266
268
|
- [agent-config-atomic-write-smoke.js](../test/agent-config-atomic-write-smoke.js)
|
|
267
269
|
- [agent-delegation-drive-smoke.js](../test/agent-delegation-drive-smoke.js)
|
|
268
270
|
- [agent-stream-gate-smoke.js](../test/agent-stream-gate-smoke.js)
|
|
@@ -270,6 +272,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
|
|
|
270
272
|
- [architecture-review-fast-automation-smoke.js](../test/architecture-review-fast-automation-smoke.js)
|
|
271
273
|
- [architecture-review-fast-phase-cache-smoke.js](../test/architecture-review-fast-phase-cache-smoke.js)
|
|
272
274
|
- [architecture-review-fast-smoke.js](../test/architecture-review-fast-smoke.js)
|
|
275
|
+
- [architecture-review-question-aware-smoke.js](../test/architecture-review-question-aware-smoke.js)
|
|
273
276
|
- [artifact-integrity-smoke.js](../test/artifact-integrity-smoke.js)
|
|
274
277
|
- [audit-verify-smoke.js](../test/audit-verify-smoke.js)
|
|
275
278
|
- [backend-registry-smoke.js](../test/backend-registry-smoke.js)
|
|
@@ -339,6 +342,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
|
|
|
339
342
|
- [mcp-app-surface-smoke.js](../test/mcp-app-surface-smoke.js)
|
|
340
343
|
- [mcp-surface-registry-smoke.js](../test/mcp-surface-registry-smoke.js)
|
|
341
344
|
- [mcp-tool-call-coverage-smoke.js](../test/mcp-tool-call-coverage-smoke.js)
|
|
345
|
+
- [metrics-summary-limit-smoke.js](../test/metrics-summary-limit-smoke.js)
|
|
342
346
|
- [multi-agent-cli-mcp-surface-smoke.js](../test/multi-agent-cli-mcp-surface-smoke.js)
|
|
343
347
|
- [multi-agent-eval-determinism-regression-smoke.js](../test/multi-agent-eval-determinism-regression-smoke.js)
|
|
344
348
|
- [multi-agent-eval-replay-harness-smoke.js](../test/multi-agent-eval-replay-harness-smoke.js)
|
|
@@ -410,6 +414,7 @@ Smoke tests mirror the public contracts. The high-signal suites are:
|
|
|
410
414
|
- [sandbox-profile-smoke.js](../test/sandbox-profile-smoke.js)
|
|
411
415
|
- [sched-policy-validation-smoke.js](../test/sched-policy-validation-smoke.js)
|
|
412
416
|
- [schedule-routine-daemon-smoke.js](../test/schedule-routine-daemon-smoke.js)
|
|
417
|
+
- [scheduling-routine-lock-concurrency-smoke.js](../test/scheduling-routine-lock-concurrency-smoke.js)
|
|
413
418
|
- [schema-validation-smoke.js](../test/schema-validation-smoke.js)
|
|
414
419
|
- [security-trust-hardening-smoke.js](../test/security-trust-hardening-smoke.js)
|
|
415
420
|
- [self-audit-hardening-smoke.js](../test/self-audit-hardening-smoke.js)
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# Remote-Source Review (`--link`)
|
|
2
2
|
|
|
3
3
|
CW v0.1.91 lets you point a review at **any repository on the internet** instead
|
|
4
|
-
of only a local path: `cw -q "
|
|
4
|
+
of only a local path: `cw -q "how does this work?" --link <url>`. CW materializes
|
|
5
5
|
the remote into a local checkout and runs the **existing** review pipeline against
|
|
6
6
|
it — identical downstream to reviewing a folder. A URL passed to `-dir`/`--repo`
|
|
7
7
|
is auto-detected, so `--link <url>` and `-dir <url>` are equivalent.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
cw -q "
|
|
11
|
-
cw -q "What are the risks?" --link git@gitlab.com:owner/repo.git --ref v1.2.0
|
|
12
|
-
cw -q "
|
|
10
|
+
cw -q "How does auth work end-to-end here?" --link https://github.com/owner/repo
|
|
11
|
+
cw -q "What are the security risks?" --link git@gitlab.com:owner/repo.git --ref v1.2.0
|
|
12
|
+
cw -q "Is it safe to swap this queue for Redis?" --link https://github.com/owner/repo/archive/refs/heads/main.tar.gz
|
|
13
13
|
cw -q "..." --link <url> --check # validate the URL + tooling WITHOUT fetching
|
|
14
14
|
```
|
|
15
15
|
|
|
@@ -127,7 +127,7 @@ npx cool-workflow demo bundle
|
|
|
127
127
|
# catching both offline with only the embedded public key.
|
|
128
128
|
|
|
129
129
|
# Run the review AND get a shippable, client-verifiable bundle from ONE command:
|
|
130
|
-
cw quickstart architecture-review --repo . --question "
|
|
130
|
+
cw quickstart architecture-review --repo . --question "How does auth work end-to-end here?" \
|
|
131
131
|
--agent-command "claude -p" --bundle --with-trust-key ./trust-pub.pem
|
|
132
132
|
# -> after the drive COMPLETES, the run is sealed into a self-verified bundle and
|
|
133
133
|
# the verdict is folded into the quickstart JSON (result.bundle). Exits non-zero
|
|
@@ -37,7 +37,9 @@ Event sources are clear:
|
|
|
37
37
|
- `runtime-derived`: CW got the event from run state.
|
|
38
38
|
|
|
39
39
|
CW does not keep secrets or raw environment values. Environment audit records
|
|
40
|
-
keep names only.
|
|
40
|
+
keep names only — for example, `worker.agent-env` records which provider-namespace
|
|
41
|
+
env var NAMES (`CW_`, `ANTHROPIC_`, `OPENAI_`, and similar) were forwarded from
|
|
42
|
+
the host process to a delegated agent child, queryable via `cw audit summary`/`cw audit`.
|
|
41
43
|
|
|
42
44
|
## Enforcement Boundary
|
|
43
45
|
|
|
@@ -96,11 +96,21 @@ The console is read-only. It offers no actions. If a later release adds an actio
|
|
|
96
96
|
capability core entry that already exists — never a side code path — so it cannot drift from the
|
|
97
97
|
CLI/MCP and is covered by the parity gate.
|
|
98
98
|
|
|
99
|
+
Authentication is opt-in, not on by default: set `CW_WORKBENCH_TOKEN` and every
|
|
100
|
+
request must carry it as `Authorization: Bearer <token>` or `?token=<token>`
|
|
101
|
+
(timing-safe compare); left unset, the loopback/read-only/GET-only controls
|
|
102
|
+
above are the only defense and any local process can read. Pass
|
|
103
|
+
`--require-token` to `cw workbench serve` to fail closed instead: the server
|
|
104
|
+
refuses to start at all unless `CW_WORKBENCH_TOKEN` is already set. This is a
|
|
105
|
+
strict opt-in — leaving it off keeps today's default behavior unchanged.
|
|
106
|
+
(`--require-token` has no effect on `cw_workbench_serve`'s MCP path, which
|
|
107
|
+
never actually binds a listener.)
|
|
108
|
+
|
|
99
109
|
## Surfaces
|
|
100
110
|
|
|
101
111
|
```
|
|
102
112
|
cw workbench view <run-id> [--json] # five-panel WorkbenchRunView for one run
|
|
103
|
-
cw workbench serve [--port N] [--scope repo|home] [--once|--json]
|
|
113
|
+
cw workbench serve [--port N] [--scope repo|home] [--once|--json] [--require-token]
|
|
104
114
|
```
|
|
105
115
|
|
|
106
116
|
`cw workbench serve` with `--once`/`--json` prints the serve descriptor (bind
|
|
@@ -264,3 +274,5 @@ _No behavioral change in v0.1.89 (CLI-surface golden-path + help-output fixes on
|
|
|
264
274
|
0.1.98
|
|
265
275
|
|
|
266
276
|
0.2.0
|
|
277
|
+
|
|
278
|
+
0.2.1
|
|
@@ -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.2.
|
|
5
|
+
"version": "0.2.1",
|
|
6
6
|
"license": "BSD-2-Clause",
|
|
7
7
|
"homepage": "https://github.com/coo1white/cool-workflow",
|
|
8
8
|
"author": {
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@ cw:result block that EXACTLY follows this schema:
|
|
|
18
18
|
"findings": [
|
|
19
19
|
{
|
|
20
20
|
"id": "unique-kebab-id",
|
|
21
|
-
"title": "short
|
|
21
|
+
"title": "short finding title",
|
|
22
22
|
"severity": "P0",
|
|
23
23
|
"classification": "real",
|
|
24
24
|
"evidence": ["path/to/file.ts:42"]
|
|
@@ -471,6 +471,25 @@ function persistStderr(resultPath, text) {
|
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
+
// Build the failure-path diagnostic string for a non-zero exit. Real OS-level
|
|
475
|
+
// stderr wins when present (the common case: a crash, a killed process, a
|
|
476
|
+
// vendor CLI that DOES write to stderr) — byte-identical to before this
|
|
477
|
+
// helper existed. When stderr is EMPTY, fall back to whatever partial/
|
|
478
|
+
// structured text the wrapper already parsed from stdout before the child
|
|
479
|
+
// died (a stream-json `result` event with is_error:true is how claude
|
|
480
|
+
// reports terminal errors like an auth failure — that text was being parsed
|
|
481
|
+
// and then silently thrown away). If there is no partial text either, say so
|
|
482
|
+
// plainly instead of just repeating the bare exit code. Never throws; always
|
|
483
|
+
// returns a non-empty string.
|
|
484
|
+
function buildFailureDetail({ label, code, childStderr, partialText }) {
|
|
485
|
+
const stderrText = String(childStderr || "").trim();
|
|
486
|
+
if (stderrText) return stderrText;
|
|
487
|
+
const partial = String(partialText || "").trim();
|
|
488
|
+
const codeText = `${label} exited ${code === null ? "(timeout/killed)" : code}`;
|
|
489
|
+
if (partial) return `${codeText} — ${label} stdout (no stderr) said: ${partial}`;
|
|
490
|
+
return codeText;
|
|
491
|
+
}
|
|
492
|
+
|
|
474
493
|
module.exports = {
|
|
475
494
|
RESULT_CONTRACT,
|
|
476
495
|
buildPrompt,
|
|
@@ -485,5 +504,6 @@ module.exports = {
|
|
|
485
504
|
flushJsonLines,
|
|
486
505
|
writeResult,
|
|
487
506
|
emitReport,
|
|
488
|
-
persistStderr // save a failed agent's stderr to <workerDir>/logs/agent-stderr.log (shared by all wrappers)
|
|
507
|
+
persistStderr, // save a failed agent's stderr to <workerDir>/logs/agent-stderr.log (shared by all wrappers)
|
|
508
|
+
buildFailureDetail // fold in parsed partial stdout text when real stderr is empty (shared by all wrappers)
|
|
489
509
|
};
|