cool-workflow 0.1.80 → 0.1.82
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 +42 -2
- package/apps/architecture-review/app.json +1 -1
- 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 +21 -7
- package/dist/candidate-scoring.js +62 -48
- package/dist/capability-core.js +152 -96
- package/dist/capability-registry.js +160 -174
- package/dist/cli.js +90 -100
- package/dist/collaboration.js +5 -6
- package/dist/commit.js +232 -209
- package/dist/compare.js +18 -0
- package/dist/coordinator/classify.js +45 -0
- package/dist/coordinator/paths.js +42 -0
- package/dist/coordinator/util.js +126 -0
- package/dist/coordinator.js +127 -300
- package/dist/dispatch.js +46 -3
- package/dist/drive.js +7 -7
- package/dist/error-feedback.js +8 -4
- package/dist/evidence-reasoning.js +5 -2
- package/dist/execution-backend/agent.js +294 -0
- package/dist/execution-backend/probes.js +96 -0
- package/dist/execution-backend/util.js +47 -0
- package/dist/execution-backend.js +77 -450
- package/dist/gates.js +48 -0
- package/dist/mcp-server.js +34 -173
- package/dist/multi-agent/graph.js +84 -0
- package/dist/multi-agent/helpers.js +141 -0
- package/dist/multi-agent/ids.js +20 -0
- package/dist/multi-agent/paths.js +22 -0
- package/dist/multi-agent-eval/format.js +194 -0
- package/dist/multi-agent-eval/normalize.js +51 -0
- package/dist/multi-agent-eval.js +63 -242
- package/dist/multi-agent-host.js +53 -40
- package/dist/multi-agent-operator-ux.js +2 -1
- package/dist/multi-agent-trust.js +5 -5
- package/dist/multi-agent.js +125 -314
- package/dist/node-projection.js +59 -0
- package/dist/node-snapshot.js +11 -21
- package/dist/observability/format.js +61 -0
- package/dist/observability/intake.js +98 -0
- package/dist/observability.js +14 -160
- package/dist/operator-ux/format.js +364 -0
- package/dist/operator-ux.js +22 -363
- package/dist/orchestrator/lifecycle-operations.js +22 -1
- package/dist/orchestrator/report.js +8 -0
- package/dist/orchestrator.js +41 -10
- package/dist/reclamation.js +32 -55
- package/dist/run-export.js +138 -14
- package/dist/run-registry/derive.js +172 -0
- package/dist/run-registry/format.js +124 -0
- package/dist/run-registry/gc.js +251 -0
- package/dist/run-registry/policy.js +16 -0
- package/dist/run-registry/queue.js +116 -0
- package/dist/run-registry.js +78 -593
- package/dist/run-state-schema.js +1 -0
- package/dist/sandbox-profile.js +43 -2
- package/dist/scheduler.js +34 -4
- package/dist/state-explosion/format.js +159 -0
- package/dist/state-explosion/helpers.js +82 -0
- package/dist/state-explosion.js +65 -283
- package/dist/state-node.js +19 -4
- package/dist/telemetry-attestation.js +55 -0
- package/dist/telemetry-demo.js +15 -3
- package/dist/telemetry-ledger.js +60 -15
- package/dist/topology.js +50 -12
- package/dist/triggers.js +33 -14
- package/dist/trust-audit.js +215 -71
- package/dist/validation.js +328 -0
- package/dist/version.js +1 -1
- package/dist/worker-isolation/helpers.js +51 -0
- package/dist/worker-isolation/paths.js +46 -0
- package/dist/worker-isolation.js +182 -173
- package/docs/agent-delegation-drive.7.md +14 -0
- package/docs/cli-mcp-parity.7.md +5 -0
- package/docs/contract-migration-tooling.7.md +3 -0
- package/docs/control-plane-scheduling.7.md +3 -0
- package/docs/dogfood/resume-drive-real-agent-2026-06-14.md +40 -0
- package/docs/durable-state-and-locking.7.md +5 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +3 -0
- package/docs/execution-backends.7.md +3 -0
- package/docs/index.md +1 -0
- package/docs/launch/launch-kit.md +46 -23
- package/docs/launch/pre-launch-checklist.md +14 -14
- package/docs/multi-agent-cli-mcp-surface.7.md +5 -0
- package/docs/multi-agent-eval-replay-harness.7.md +3 -0
- package/docs/multi-agent-operator-ux.7.md +3 -0
- package/docs/multi-agent-trust-policy-audit.7.md +27 -0
- package/docs/node-snapshot-diff-replay.7.md +3 -0
- package/docs/observability-cost-accounting.7.md +3 -0
- package/docs/project-index.md +23 -6
- package/docs/real-execution-backends.7.md +3 -0
- package/docs/release-and-migration.7.md +5 -0
- package/docs/release-tooling.7.md +35 -2
- package/docs/run-registry-control-plane.7.md +55 -8
- package/docs/run-retention-reclamation.7.md +5 -0
- package/docs/state-explosion-management.7.md +3 -0
- package/docs/team-collaboration.7.md +3 -0
- package/docs/trust-model.md +267 -0
- package/docs/vendor-manifest-loadability.7.md +43 -0
- package/docs/web-desktop-workbench.7.md +3 -0
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +1 -1
- package/package.json +4 -2
- package/scripts/agents/builtin-templates.json +7 -0
- package/scripts/bump-version.js +5 -11
- package/scripts/canonical-apps-list.js +64 -0
- package/scripts/canonical-apps.js +19 -4
- package/scripts/children/batch-delegate-child.js +58 -0
- package/scripts/children/http-delegate-child.js +39 -0
- package/scripts/dogfood-release.js +1 -1
- package/scripts/golden-path.js +4 -4
- package/scripts/parity-check.js +5 -0
- package/scripts/release-check.js +5 -1
- package/scripts/release-flow.js +181 -5
- package/scripts/version-sync-check.js +5 -8
- package/dist/capability-dispatcher.js +0 -86
package/dist/capability-core.js
CHANGED
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
// expressed here and called identically by both surfaces. A composite that lives
|
|
10
10
|
// in only one surface is exactly the cross-surface drift v0.1.27 forbids.
|
|
11
11
|
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// the
|
|
15
|
-
//
|
|
16
|
-
//
|
|
12
|
+
// Capability metadata (which entry is on which surface, tool names, jsonMode) is
|
|
13
|
+
// declared in ONE place — the BUILTIN_CAPABILITIES table in capability-registry.ts,
|
|
14
|
+
// the single source of truth both surfaces and the parity gate read. New
|
|
15
|
+
// capabilities add a row there. (A v0.1.46 "self-register at load time" mechanism
|
|
16
|
+
// was removed: the registry snapshot was taken before those registrations ran, so
|
|
17
|
+
// they were silently dead duplicates of the table — see capability-registry.ts.)
|
|
17
18
|
//
|
|
18
19
|
// See docs/cli-mcp-parity.7.md and src/capability-registry.ts.
|
|
19
20
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
@@ -37,6 +38,7 @@ exports.runArchive = runArchive;
|
|
|
37
38
|
exports.runRerun = runRerun;
|
|
38
39
|
exports.runExportArchive = runExportArchive;
|
|
39
40
|
exports.runImportArchive = runImportArchive;
|
|
41
|
+
exports.runInspectArchive = runInspectArchive;
|
|
40
42
|
exports.runVerifyImport = runVerifyImport;
|
|
41
43
|
exports.queueAdd = queueAdd;
|
|
42
44
|
exports.queueList = queueList;
|
|
@@ -65,13 +67,15 @@ exports.withoutRuntimeKeys = withoutRuntimeKeys;
|
|
|
65
67
|
exports.optionalString = optionalString;
|
|
66
68
|
exports.isRecord = isRecord;
|
|
67
69
|
exports.telemetryVerify = telemetryVerify;
|
|
70
|
+
exports.auditVerify = auditVerify;
|
|
68
71
|
exports.demoTamper = demoTamper;
|
|
69
|
-
const capability_registry_1 = require("./capability-registry");
|
|
70
72
|
const drive_1 = require("./drive");
|
|
71
73
|
const agent_config_1 = require("./agent-config");
|
|
72
74
|
const run_registry_1 = require("./run-registry");
|
|
73
75
|
const observability_1 = require("./observability");
|
|
74
76
|
const telemetry_ledger_1 = require("./telemetry-ledger");
|
|
77
|
+
const telemetry_attestation_1 = require("./telemetry-attestation");
|
|
78
|
+
const trust_audit_1 = require("./trust-audit");
|
|
75
79
|
const telemetry_demo_1 = require("./telemetry-demo");
|
|
76
80
|
const state_1 = require("./state");
|
|
77
81
|
const run_export_1 = require("./run-export");
|
|
@@ -90,8 +94,6 @@ function planSummary(runner, workflowId, options) {
|
|
|
90
94
|
pendingTasks: run.tasks.filter((task) => task.status === "pending").length
|
|
91
95
|
};
|
|
92
96
|
}
|
|
93
|
-
// Auto-register with the capability registry (v0.1.46 — no need to edit capability-registry.ts)
|
|
94
|
-
(0, capability_registry_1.registerCapability)({ capability: "plan", summary: "Plan a workflow run on a repo + app.", entry: "planSummary", surface: "both", cli: { path: ["plan"], jsonMode: "default" }, mcp: { tool: "cw_plan" } });
|
|
95
97
|
// ---- canonical app-run payload --------------------------------------------
|
|
96
98
|
// Both `cw app run` and `cw_app_run` resolve to this exact object. Structured
|
|
97
99
|
// app inputs + optional sandbox resolution, then a compact operator status.
|
|
@@ -117,7 +119,6 @@ function appRun(runner, args) {
|
|
|
117
119
|
sandboxProfile: resolvedSandbox
|
|
118
120
|
};
|
|
119
121
|
}
|
|
120
|
-
(0, capability_registry_1.registerCapability)({ capability: "app.run", summary: "Plan a run on a named app with structured inputs.", entry: "appRun", surface: "both", cli: { path: ["app", "run"], caseTokens: ["app"], jsonMode: "default" }, mcp: { tool: "cw_app_run" } });
|
|
121
122
|
// ---- canonical sandbox choice payload -------------------------------------
|
|
122
123
|
// Both `cw sandbox choose|resolve` and `cw_sandbox_choose|cw_sandbox_resolve`
|
|
123
124
|
// resolve to this exact object.
|
|
@@ -154,7 +155,6 @@ function commitEnvelope(runner, runId, args) {
|
|
|
154
155
|
commit
|
|
155
156
|
};
|
|
156
157
|
}
|
|
157
|
-
(0, capability_registry_1.registerCapability)({ capability: "commit", summary: "Create a verifier-gated state commit with evidence.", entry: "commitEnvelope", surface: "both", cli: { path: ["commit"], jsonMode: "default" }, mcp: { tool: "cw_commit" }, payloadIdentical: false, reason: "CLI renders a human summary with path hints; MCP returns the structured commit payload alone." });
|
|
158
158
|
function compactOperatorStatus(status) {
|
|
159
159
|
return {
|
|
160
160
|
runId: status.runId,
|
|
@@ -198,18 +198,16 @@ function flag(value) {
|
|
|
198
198
|
return false;
|
|
199
199
|
return Boolean(value);
|
|
200
200
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
process.chdir(previous);
|
|
212
|
-
}
|
|
201
|
+
// F7: explicit invocation cwd — no more process.chdir bracket.
|
|
202
|
+
// The runner resolves a run from process.cwd() by default; to operate WITH a run's
|
|
203
|
+
// repo as base (run --drive --repo X from anywhere; cross-directory quickstart; run
|
|
204
|
+
// import/export against a target dir) we now pass that base EXPLICITLY —
|
|
205
|
+
// runner.withBaseDir(dir).loadRun(...) for run resolution (see
|
|
206
|
+
// CoolWorkflowRunner.withBaseDir), and invocationCwd(args) to anchor relative path
|
|
207
|
+
// args. Nothing mutates the global process.cwd(), so concurrent in-process callers
|
|
208
|
+
// can no longer corrupt each other's working directory (the former reentrancy hazard).
|
|
209
|
+
function invocationCwd(args) {
|
|
210
|
+
return node_path_1.default.resolve(optionalString(args.cwd) || process.cwd());
|
|
213
211
|
}
|
|
214
212
|
function runRegistryRefresh(reg, args) {
|
|
215
213
|
return reg.refresh({ scope: scopeOf(args, "repo") });
|
|
@@ -242,11 +240,19 @@ function runList(reg, args) {
|
|
|
242
240
|
function runShow(reg, runId, args) {
|
|
243
241
|
return reg.showRun(runId, { scope: scopeOf(args, "home") });
|
|
244
242
|
}
|
|
245
|
-
function runResume(reg, runId, args) {
|
|
246
|
-
|
|
243
|
+
function runResume(reg, runner, runId, args) {
|
|
244
|
+
const base = reg.resume(runId, {
|
|
247
245
|
scope: scopeOf(args, "home"),
|
|
248
246
|
limit: args.limit === undefined ? undefined : Number(args.limit)
|
|
249
247
|
});
|
|
248
|
+
// Default (no --drive/--once): read-only, byte-identical to before.
|
|
249
|
+
if (!isTrue(args.drive) && !isTrue(args.once))
|
|
250
|
+
return base;
|
|
251
|
+
// Opt-in continuation: hand the resolved run to the EXISTING agent-delegation
|
|
252
|
+
// drive loop (re-plans nothing; picks up pending/running tasks from durable
|
|
253
|
+
// state). An unconfigured agent surfaces drive.status="blocked" (fail-closed).
|
|
254
|
+
const drive = runDrive(runner, { ...args, runId: base.runId, repo: base.repo, once: isTrue(args.once) });
|
|
255
|
+
return { ...base, drive };
|
|
250
256
|
}
|
|
251
257
|
function runArchive(reg, runId, args) {
|
|
252
258
|
if (runId) {
|
|
@@ -269,25 +275,33 @@ function runRerun(reg, runId, args) {
|
|
|
269
275
|
return reg.rerun(runId, { scope: scopeOf(args, "home"), reason: optionalString(args.reason) });
|
|
270
276
|
}
|
|
271
277
|
function runExportArchive(runner, runId, args) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
});
|
|
278
|
+
const base = invocationCwd(args);
|
|
279
|
+
const output = optionalString(args.output || args.path || args.archive) || `${runId}.cwrun.json`;
|
|
280
|
+
return (0, run_export_1.exportRun)(runner.withBaseDir(optionalString(args.cwd)).loadRun(runId), node_path_1.default.resolve(base, output));
|
|
276
281
|
}
|
|
277
282
|
function runImportArchive(runner, args) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
283
|
+
const base = invocationCwd(args);
|
|
284
|
+
const archive = optionalString(args.archive || args.path || args.file);
|
|
285
|
+
if (!archive)
|
|
286
|
+
throw new Error("run import requires an archive path (positional, --archive, --path, or --file)");
|
|
287
|
+
const target = node_path_1.default.resolve(base, optionalString(args.target || args.repo || args.cwd) || base);
|
|
288
|
+
const imported = (0, run_export_1.importRun)(node_path_1.default.resolve(base, archive), target);
|
|
289
|
+
const registry = new run_registry_1.RunRegistry(target, runner.withBaseDir(target));
|
|
290
|
+
const registryReport = registry.refresh({ scope: "repo" });
|
|
291
|
+
return { ...imported, registry: registryReport };
|
|
292
|
+
}
|
|
293
|
+
// Read-only: inspect a portable archive's integrity WITHOUT importing it. Routes
|
|
294
|
+
// both surfaces through one shared core entry. The runner is unused (no registry
|
|
295
|
+
// touch — inspection writes nothing) but kept for dispatch-signature symmetry.
|
|
296
|
+
function runInspectArchive(_runner, args) {
|
|
297
|
+
const base = invocationCwd(args);
|
|
298
|
+
const archive = optionalString(args.archive || args.path || args.file);
|
|
299
|
+
if (!archive)
|
|
300
|
+
throw new Error("run inspect-archive requires an archive path (positional, --archive, --path, or --file)");
|
|
301
|
+
return (0, run_export_1.inspectArchive)(node_path_1.default.resolve(base, archive));
|
|
288
302
|
}
|
|
289
303
|
function runVerifyImport(runner, runId, args) {
|
|
290
|
-
return
|
|
304
|
+
return (0, run_export_1.verifyImportedRun)(runner.withBaseDir(optionalString(args.cwd)).loadRun(runId));
|
|
291
305
|
}
|
|
292
306
|
function queueAdd(reg, args) {
|
|
293
307
|
return reg.queueAdd({
|
|
@@ -422,7 +436,8 @@ const DRIVE_RUNTIME_KEYS = [
|
|
|
422
436
|
"agentModel",
|
|
423
437
|
"agent-model",
|
|
424
438
|
"agentTimeoutMs",
|
|
425
|
-
"agent-timeout-ms"
|
|
439
|
+
"agent-timeout-ms",
|
|
440
|
+
"resume"
|
|
426
441
|
];
|
|
427
442
|
function planInputsFor(args) {
|
|
428
443
|
const copy = withoutRuntimeKeys(args);
|
|
@@ -438,26 +453,25 @@ function planInputsFor(args) {
|
|
|
438
453
|
* the golden path runs each verb from the repo dir) — then restores cwd. This lets
|
|
439
454
|
* `cw run <app> --drive --repo X` work when invoked from anywhere. */
|
|
440
455
|
function runDrive(runner, args) {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
if (!
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
runId = run.id;
|
|
451
|
-
repoCwd = run.cwd;
|
|
452
|
-
}
|
|
453
|
-
if (repoCwd && repoCwd !== process.cwd() && node_fs_1.default.existsSync(repoCwd))
|
|
454
|
-
process.chdir(repoCwd);
|
|
455
|
-
return (0, drive_1.drive)(runner, runId, { once: isTrue(args.once), now: optionalString(args.now), args });
|
|
456
|
-
}
|
|
457
|
-
finally {
|
|
458
|
-
if (process.cwd() !== cwd0)
|
|
459
|
-
process.chdir(cwd0);
|
|
456
|
+
let runId = optionalString(args.runId || args.run);
|
|
457
|
+
let repoCwd = optionalString(args.repo);
|
|
458
|
+
if (!runId) {
|
|
459
|
+
const appId = String(args.appId || args.workflowId || args.app || "");
|
|
460
|
+
if (!appId)
|
|
461
|
+
throw new Error("run --drive requires an app id (or --run <run-id> to continue)");
|
|
462
|
+
const run = runner.plan(appId, planInputsFor(args));
|
|
463
|
+
runId = run.id;
|
|
464
|
+
repoCwd = run.cwd;
|
|
460
465
|
}
|
|
466
|
+
// The runner resolves the run from its baseDir, so the drive must run WITH the
|
|
467
|
+
// run's repo as base. Pass it explicitly via withBaseDir (F7 — no process.chdir).
|
|
468
|
+
const target = repoCwd && node_fs_1.default.existsSync(repoCwd) ? repoCwd : undefined;
|
|
469
|
+
const driveRunId = runId;
|
|
470
|
+
return (0, drive_1.drive)(runner.withBaseDir(target), driveRunId, {
|
|
471
|
+
once: isTrue(args.once),
|
|
472
|
+
now: optionalString(args.now),
|
|
473
|
+
args
|
|
474
|
+
});
|
|
461
475
|
}
|
|
462
476
|
/** The app the one-command quickstart plans when none is named. */
|
|
463
477
|
exports.QUICKSTART_DEFAULT_APP = "architecture-review";
|
|
@@ -474,50 +488,44 @@ exports.QUICKSTART_DEFAULT_APP = "architecture-review";
|
|
|
474
488
|
function quickstart(runner, args) {
|
|
475
489
|
const appId = String(args.appId || args.app || args.workflowId || exports.QUICKSTART_DEFAULT_APP);
|
|
476
490
|
const agentConfigured = Boolean((0, agent_config_1.resolveAgentConfig)(args).command || (0, agent_config_1.resolveAgentConfig)(args).endpoint);
|
|
491
|
+
// `--resume`: a discoverability flag over the existing continuation. With no
|
|
492
|
+
// `--run`, advance exactly ONE step (reuse the `--once` path) and print a
|
|
493
|
+
// copy-pasteable continue line; with `--run <id>`, continue that run to
|
|
494
|
+
// completion (the default drive). It adds no new execution path.
|
|
495
|
+
const resume = isTrue(args.resume);
|
|
496
|
+
const resumeRunId = resume ? optionalString(args.runId || args.run) : undefined;
|
|
477
497
|
// `--preview`: read-only, deterministic next-step projection (no spawn, no commit).
|
|
478
498
|
// Plan a fresh run (the read-only first verb) then project the next drive step.
|
|
479
499
|
if (isTrue(args.preview)) {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
runId = run.id;
|
|
487
|
-
repoCwd = run.cwd;
|
|
488
|
-
}
|
|
489
|
-
if (repoCwd && repoCwd !== process.cwd() && node_fs_1.default.existsSync(repoCwd))
|
|
490
|
-
process.chdir(repoCwd);
|
|
491
|
-
return (0, drive_1.drivePreview)(runner, runId, args);
|
|
492
|
-
}
|
|
493
|
-
finally {
|
|
494
|
-
if (process.cwd() !== cwd0)
|
|
495
|
-
process.chdir(cwd0);
|
|
500
|
+
let runId = optionalString(args.runId || args.run);
|
|
501
|
+
let repoCwd = optionalString(args.repo);
|
|
502
|
+
if (!runId) {
|
|
503
|
+
const run = runner.plan(appId, planInputsFor(args));
|
|
504
|
+
runId = run.id;
|
|
505
|
+
repoCwd = run.cwd;
|
|
496
506
|
}
|
|
507
|
+
const previewRunId = runId;
|
|
508
|
+
const target = repoCwd && node_fs_1.default.existsSync(repoCwd) ? repoCwd : undefined;
|
|
509
|
+
return (0, drive_1.drivePreview)(runner.withBaseDir(target), previewRunId, args);
|
|
497
510
|
}
|
|
498
511
|
// Drive end-to-end (or one `--once` step). runDrive plans the run, delegates each
|
|
499
512
|
// worker to the agent backend, and commits — we add only the report write + a
|
|
500
513
|
// single assembled payload. No orchestration is duplicated here.
|
|
501
|
-
|
|
514
|
+
// `--resume` with no run id advances a single step so a newcomer WITNESSES the
|
|
515
|
+
// stop-then-resume; with a run id it continues to completion. Non-resume paths
|
|
516
|
+
// are untouched (byte-identical default).
|
|
517
|
+
const result = runDrive(runner, { ...args, appId, ...(resume && !resumeRunId ? { once: true } : {}) });
|
|
502
518
|
// Always (re)write the report so the one command yields a report.md on disk, even
|
|
503
519
|
// when the drive blocked/parked (a partial report is still useful triage).
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
if (runRepoCwd !== process.cwd() && node_fs_1.default.existsSync(runRepoCwd))
|
|
514
|
-
process.chdir(runRepoCwd);
|
|
515
|
-
reportPath = runner.report(result.runId).path;
|
|
516
|
-
}
|
|
517
|
-
finally {
|
|
518
|
-
if (process.cwd() !== cwd0)
|
|
519
|
-
process.chdir(cwd0);
|
|
520
|
-
}
|
|
520
|
+
//
|
|
521
|
+
// runDrive restored cwd, so the runs root would resolve against the CALLER's cwd
|
|
522
|
+
// here — orphaning the run when quickstart is invoked cross-directory (cwd =
|
|
523
|
+
// plugin dir, --repo elsewhere: the README's headline command). The run's
|
|
524
|
+
// statePath (<repo>/.cw/runs/<id>/state.json) is authoritative however the run
|
|
525
|
+
// was planned or continued; resolve to ITS repo BEFORE any run read, reentrant-safe.
|
|
526
|
+
const runRepoCwd = node_path_1.default.resolve(node_path_1.default.dirname(result.statePath), "..", "..", "..");
|
|
527
|
+
const reportTarget = node_fs_1.default.existsSync(runRepoCwd) ? runRepoCwd : undefined;
|
|
528
|
+
const reportPath = runner.withBaseDir(reportTarget).report(result.runId).path;
|
|
521
529
|
let hint;
|
|
522
530
|
if (!agentConfigured) {
|
|
523
531
|
hint =
|
|
@@ -530,7 +538,9 @@ function quickstart(runner, args) {
|
|
|
530
538
|
hint = `the drive is blocked — inspect: cw run drive ${result.runId}`;
|
|
531
539
|
}
|
|
532
540
|
else if (result.status === "in-progress") {
|
|
533
|
-
hint =
|
|
541
|
+
hint = resume
|
|
542
|
+
? `one step advanced — continue: cw quickstart ${appId} --run ${result.runId} --resume`
|
|
543
|
+
: `one step advanced (--once) — continue: cw quickstart ${appId} --run ${result.runId} --once`;
|
|
534
544
|
}
|
|
535
545
|
return {
|
|
536
546
|
schemaVersion: 1,
|
|
@@ -546,7 +556,11 @@ function quickstart(runner, args) {
|
|
|
546
556
|
statePath: result.statePath,
|
|
547
557
|
agentConfigured,
|
|
548
558
|
steps: result.steps,
|
|
549
|
-
hint
|
|
559
|
+
hint,
|
|
560
|
+
// Stamp resumedFrom ONLY when we continued an explicit run. Conditional spread
|
|
561
|
+
// keeps the key absent on the default/fresh path (own-property absent + omitted
|
|
562
|
+
// by JSON.stringify), so default output is byte-identical.
|
|
563
|
+
...(resumeRunId ? { resumedFrom: resumeRunId } : {})
|
|
550
564
|
};
|
|
551
565
|
}
|
|
552
566
|
/** Read-only, deterministic projection of the effective agent config (secret-stripped). */
|
|
@@ -680,15 +694,57 @@ function telemetryVerify(runner, args) {
|
|
|
680
694
|
throw new Error("telemetry verify requires a run id (cw telemetry verify <run-id>)");
|
|
681
695
|
const run = runner.loadRun(runId);
|
|
682
696
|
const v = (0, telemetry_ledger_1.verifyTelemetryLedger)(run);
|
|
697
|
+
// Opt-in independent signature re-verification. verifyTelemetryLedger re-proves
|
|
698
|
+
// the chain (so the stored attestation verdicts were not edited); supplying the
|
|
699
|
+
// trust public key (--pubkey / CW_AGENT_ATTEST_PUBKEY) additionally RE-RUNS the
|
|
700
|
+
// ed25519 check over each `attested` record's stored raw usage rather than
|
|
701
|
+
// trusting that verdict, so a forged signature can no longer ride a green chain.
|
|
702
|
+
const trustPublicKeyInput = optionalString(args.pubkey || args.pubKey || args.publicKey) || process.env.CW_AGENT_ATTEST_PUBKEY;
|
|
703
|
+
const trustPublicKey = (0, telemetry_attestation_1.resolveTrustPublicKey)(trustPublicKeyInput);
|
|
704
|
+
const keyChecks = trustPublicKeyInput && !trustPublicKey
|
|
705
|
+
? [{ name: "signature-key", pass: false, code: "telemetry-pubkey-unreadable" }]
|
|
706
|
+
: [];
|
|
707
|
+
const sig = (0, telemetry_attestation_1.verifyTelemetrySignatures)(v.records, trustPublicKey);
|
|
708
|
+
const failedChecks = [...v.checks.filter((c) => !c.pass), ...keyChecks, ...sig.checks.filter((c) => !c.pass)];
|
|
683
709
|
return {
|
|
684
710
|
schemaVersion: 1,
|
|
685
711
|
runId: run.id,
|
|
686
712
|
present: v.present,
|
|
687
|
-
|
|
713
|
+
// Chain integrity AND (when a key was supplied) every attested signature must
|
|
714
|
+
// re-verify. With no key, sig.failed is 0 → unchanged chain-only behavior.
|
|
715
|
+
verified: v.verified && keyChecks.length === 0 && sig.failed === 0,
|
|
688
716
|
records: v.records.length,
|
|
689
717
|
attested: v.attested,
|
|
690
718
|
unattested: v.unattested,
|
|
691
719
|
absent: v.absent,
|
|
720
|
+
signatureKeyProvided: sig.keyProvided,
|
|
721
|
+
signaturesChecked: sig.checked,
|
|
722
|
+
signaturesReverified: sig.reverified,
|
|
723
|
+
signaturesFailed: sig.failed,
|
|
724
|
+
failedChecks: failedChecks.map((c) => ({ name: c.name, code: c.code }))
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
// audit.verify — fail-closed re-prove of a run's trust-audit hash chain. The peer
|
|
728
|
+
// of telemetry.verify for the sandbox/policy/commit-gate decision log: recomputes
|
|
729
|
+
// every event hash from genesis, checks chain linkage, and catches the
|
|
730
|
+
// unchained-event forgery. Exposed as a verb (not just embedded in `audit summary`,
|
|
731
|
+
// which always exits 0) so `cw audit verify <run> && deploy` can gate on the exit
|
|
732
|
+
// code. POLA: a run with no audit log is present:false / verified:true / exit 0.
|
|
733
|
+
function auditVerify(runner, args) {
|
|
734
|
+
const runId = optionalString(args.runId || args.run);
|
|
735
|
+
if (!runId)
|
|
736
|
+
throw new Error("audit verify requires a run id (cw audit verify <run-id>)");
|
|
737
|
+
const run = runner.loadRun(runId);
|
|
738
|
+
const v = (0, trust_audit_1.verifyTrustAudit)(run);
|
|
739
|
+
return {
|
|
740
|
+
schemaVersion: 1,
|
|
741
|
+
runId: run.id,
|
|
742
|
+
present: v.present,
|
|
743
|
+
verified: v.verified,
|
|
744
|
+
eventCount: v.eventCount,
|
|
745
|
+
chained: v.chained,
|
|
746
|
+
unchained: v.unchained,
|
|
747
|
+
corruptLines: v.corruptLines,
|
|
692
748
|
failedChecks: v.checks.filter((c) => !c.pass).map((c) => ({ name: c.name, code: c.code }))
|
|
693
749
|
};
|
|
694
750
|
}
|