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/worker-isolation.js
CHANGED
|
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.WORKER_ISOLATION_SCHEMA_VERSION = void 0;
|
|
7
|
-
exports.createWorkerIsolation = createWorkerIsolation;
|
|
8
7
|
exports.allocateWorkerScope = allocateWorkerScope;
|
|
9
8
|
exports.writeWorkerManifest = writeWorkerManifest;
|
|
10
9
|
exports.syncWorkerScopeFromTask = syncWorkerScopeFromTask;
|
|
@@ -33,21 +32,10 @@ const multi_agent_1 = require("./multi-agent");
|
|
|
33
32
|
const telemetry_attestation_1 = require("./telemetry-attestation");
|
|
34
33
|
const telemetry_ledger_1 = require("./telemetry-ledger");
|
|
35
34
|
const coordinator_1 = require("./coordinator");
|
|
35
|
+
const helpers_1 = require("./worker-isolation/helpers");
|
|
36
|
+
const paths_1 = require("./worker-isolation/paths");
|
|
37
|
+
const validation_1 = require("./validation");
|
|
36
38
|
exports.WORKER_ISOLATION_SCHEMA_VERSION = 1;
|
|
37
|
-
const WORKER_SCOPE_FILE = "worker.json";
|
|
38
|
-
const WORKER_MANIFEST_FILE = "manifest.json";
|
|
39
|
-
function createWorkerIsolation(options = {}) {
|
|
40
|
-
return {
|
|
41
|
-
allocateWorkerScope: (run, task, allocateOptions) => allocateWorkerScope(run, task, { ...options, ...allocateOptions }),
|
|
42
|
-
writeWorkerManifest,
|
|
43
|
-
listWorkerScopes: (run, listOptions) => listWorkerScopes(run, listOptions),
|
|
44
|
-
getWorkerScope,
|
|
45
|
-
recordWorkerOutput,
|
|
46
|
-
recordWorkerFailure,
|
|
47
|
-
validateWorkerBoundary,
|
|
48
|
-
summarizeWorkers
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
39
|
function allocateWorkerScope(run, task, options = {}) {
|
|
52
40
|
ensureWorkerState(run);
|
|
53
41
|
const existing = task.workerId ? getWorkerScope(run, task.workerId) : undefined;
|
|
@@ -64,7 +52,7 @@ function allocateWorkerScope(run, task, options = {}) {
|
|
|
64
52
|
return existing;
|
|
65
53
|
}
|
|
66
54
|
const now = new Date().toISOString();
|
|
67
|
-
const workerId = options.workerId || createWorkerId(run, task.id);
|
|
55
|
+
const workerId = options.workerId || (0, paths_1.createWorkerId)(run, task.id);
|
|
68
56
|
const workerDir = node_path_1.default.join(workerRoot(run), (0, state_1.safeFileName)(workerId));
|
|
69
57
|
const inputPath = node_path_1.default.join(workerDir, "input.md");
|
|
70
58
|
const resultPath = node_path_1.default.join(workerDir, "result.md");
|
|
@@ -82,7 +70,10 @@ function allocateWorkerScope(run, task, options = {}) {
|
|
|
82
70
|
extraReadPaths: options.policy?.readPaths || [],
|
|
83
71
|
extraWritePaths: [...(options.policy?.writePaths || []), ...(options.policy?.allowedPaths || [])],
|
|
84
72
|
allowArtifacts: options.policy?.allowArtifacts,
|
|
85
|
-
allowLogs: options.policy?.allowLogs
|
|
73
|
+
allowLogs: options.policy?.allowLogs,
|
|
74
|
+
// H7: persisted custom profile definitions so a custom logical id resolves
|
|
75
|
+
// against THIS worker's context (worker-specific path tokens bind correctly).
|
|
76
|
+
customProfiles: run.customSandboxProfiles
|
|
86
77
|
});
|
|
87
78
|
const allowedPaths = (0, sandbox_profile_1.effectiveSandboxWritePaths)(sandboxPolicy);
|
|
88
79
|
(0, sandbox_profile_1.upsertRunSandboxPolicy)(run, sandboxPolicy);
|
|
@@ -123,7 +114,7 @@ function allocateWorkerScope(run, task, options = {}) {
|
|
|
123
114
|
feedbackIds: [],
|
|
124
115
|
errors: [],
|
|
125
116
|
multiAgent: options.multiAgent,
|
|
126
|
-
metadata: compactMetadata({
|
|
117
|
+
metadata: (0, helpers_1.compactMetadata)({
|
|
127
118
|
...options.metadata,
|
|
128
119
|
multiAgent: options.multiAgent,
|
|
129
120
|
phase: task.phase,
|
|
@@ -165,7 +156,7 @@ function allocateWorkerScope(run, task, options = {}) {
|
|
|
165
156
|
});
|
|
166
157
|
}
|
|
167
158
|
task.workerId = scope.id;
|
|
168
|
-
task.workerManifestPath = manifestPath(scope);
|
|
159
|
+
task.workerManifestPath = (0, paths_1.manifestPath)(scope);
|
|
169
160
|
task.sandboxProfileId = sandboxPolicy.id;
|
|
170
161
|
task.sandboxPolicy = sandboxPolicy;
|
|
171
162
|
task.backendId = backendId;
|
|
@@ -180,8 +171,8 @@ function writeWorkerManifest(run, scope) {
|
|
|
180
171
|
const task = run.tasks.find((candidate) => candidate.id === scope.taskId);
|
|
181
172
|
const sandboxPolicy = scope.sandboxPolicy || sandboxPolicyForBoundary(run, scope);
|
|
182
173
|
const sandboxProfileId = scope.sandboxProfileId || sandboxPolicy.id;
|
|
183
|
-
const scopePath = workerScopePath(scope);
|
|
184
|
-
const workerManifestPath = manifestPath(scope);
|
|
174
|
+
const scopePath = (0, paths_1.workerScopePath)(scope);
|
|
175
|
+
const workerManifestPath = (0, paths_1.manifestPath)(scope);
|
|
185
176
|
const manifest = {
|
|
186
177
|
schemaVersion: exports.WORKER_ISOLATION_SCHEMA_VERSION,
|
|
187
178
|
id: scope.id,
|
|
@@ -257,7 +248,7 @@ function syncWorkerScopeFromTask(run, workerId) {
|
|
|
257
248
|
...scope,
|
|
258
249
|
updatedAt: new Date().toISOString(),
|
|
259
250
|
multiAgent: task.multiAgent,
|
|
260
|
-
metadata: compactMetadata({
|
|
251
|
+
metadata: (0, helpers_1.compactMetadata)({
|
|
261
252
|
...(scope.metadata || {}),
|
|
262
253
|
multiAgent: task.multiAgent
|
|
263
254
|
})
|
|
@@ -267,7 +258,7 @@ function syncWorkerScopeFromTask(run, workerId) {
|
|
|
267
258
|
function listWorkerScopes(run, options = {}) {
|
|
268
259
|
ensureWorkerState(run);
|
|
269
260
|
const scopes = loadWorkerScopesFromDisk(run);
|
|
270
|
-
run.workers = mergeScopes(run.workers || [], scopes);
|
|
261
|
+
run.workers = (0, helpers_1.mergeScopes)(run.workers || [], scopes);
|
|
271
262
|
const listed = run.workers || [];
|
|
272
263
|
return options.status ? listed.filter((scope) => scope.status === options.status) : listed;
|
|
273
264
|
}
|
|
@@ -276,14 +267,34 @@ function getWorkerScope(run, workerId) {
|
|
|
276
267
|
const existing = (run.workers || []).find((scope) => scope.id === workerId);
|
|
277
268
|
if (existing)
|
|
278
269
|
return existing;
|
|
279
|
-
const file = node_path_1.default.join(workerRoot(run), (0, state_1.safeFileName)(workerId), WORKER_SCOPE_FILE);
|
|
270
|
+
const file = node_path_1.default.join(workerRoot(run), (0, state_1.safeFileName)(workerId), paths_1.WORKER_SCOPE_FILE);
|
|
280
271
|
if (!node_fs_1.default.existsSync(file))
|
|
281
272
|
return undefined;
|
|
282
|
-
const scope = JSON.parse(node_fs_1.default.readFileSync(file, "utf8"));
|
|
273
|
+
const scope = (0, validation_1.validateWorkerScope)(JSON.parse(node_fs_1.default.readFileSync(file, "utf8")));
|
|
283
274
|
upsertWorkerScope(run, scope);
|
|
284
275
|
return scope;
|
|
285
276
|
}
|
|
286
277
|
function recordWorkerOutput(run, workerId, resultPath, options = {}) {
|
|
278
|
+
// Accept-path orchestrator. The recorded order + side effects of these ordered
|
|
279
|
+
// steps are load-bearing (replay determinism + the hash-chained audit/telemetry
|
|
280
|
+
// ledgers cross-link by parent event ids), so each helper runs exactly where it
|
|
281
|
+
// did before and mutates the shared `accept` context in place. Do NOT reorder.
|
|
282
|
+
const accept = validateWorkerResult(run, workerId, resultPath, options);
|
|
283
|
+
const delegation = attestWorkerDelegation(accept);
|
|
284
|
+
acceptWorkerResult(accept, delegation);
|
|
285
|
+
recordWorkerDelegationLedger(accept, delegation);
|
|
286
|
+
runWorkerVerify(accept);
|
|
287
|
+
recordWorkerCompletion(accept, delegation);
|
|
288
|
+
fanOutWorkerOutput(accept);
|
|
289
|
+
if (options.persist !== false)
|
|
290
|
+
(0, state_1.saveCheckpoint)(run);
|
|
291
|
+
return accept.output;
|
|
292
|
+
}
|
|
293
|
+
/** Step 1 — validateResult: resolve scope/task, enforce the sandbox boundary, the
|
|
294
|
+
* result-file existence, the envelope contract, and (opt-in) resolvable evidence.
|
|
295
|
+
* Fail-closed: any guard records a worker failure and throws BEFORE accept-side
|
|
296
|
+
* state mutation. Returns the partially-filled accept context on success. */
|
|
297
|
+
function validateWorkerResult(run, workerId, resultPath, options) {
|
|
287
298
|
const scope = requireWorkerScope(run, workerId);
|
|
288
299
|
const task = requireWorkerTask(run, scope);
|
|
289
300
|
const absoluteResultPath = node_path_1.default.resolve(resultPath);
|
|
@@ -302,7 +313,7 @@ function recordWorkerOutput(run, workerId, resultPath, options = {}) {
|
|
|
302
313
|
throw new Error(violation.message);
|
|
303
314
|
}
|
|
304
315
|
if (!node_fs_1.default.existsSync(absoluteResultPath)) {
|
|
305
|
-
const error = structuredError("worker-result-missing", `Worker result file does not exist: ${absoluteResultPath}`, {
|
|
316
|
+
const error = (0, helpers_1.structuredError)("worker-result-missing", `Worker result file does not exist: ${absoluteResultPath}`, {
|
|
306
317
|
path: absoluteResultPath,
|
|
307
318
|
retryable: true
|
|
308
319
|
});
|
|
@@ -321,11 +332,35 @@ function recordWorkerOutput(run, workerId, resultPath, options = {}) {
|
|
|
321
332
|
const baseDirs = Array.from(new Set([run.cwd, process.cwd(), scope.workerDir, run.paths.runDir].filter(Boolean)));
|
|
322
333
|
const unresolved = (0, evidence_grounding_1.unresolvedFileEvidence)(parsedResult.evidence, baseDirs);
|
|
323
334
|
if (unresolved.length) {
|
|
324
|
-
const error = structuredError("worker-evidence-unresolvable", `Worker ${workerId} result cites file evidence that does not resolve on disk: ${unresolved.join(", ")}`, { path: absoluteResultPath, retryable: false });
|
|
335
|
+
const error = (0, helpers_1.structuredError)("worker-evidence-unresolvable", `Worker ${workerId} result cites file evidence that does not resolve on disk: ${unresolved.join(", ")}`, { path: absoluteResultPath, retryable: false });
|
|
325
336
|
recordWorkerFailure(run, workerId, error, { ...options, persist: options.persist });
|
|
326
337
|
throw new Error(error.message);
|
|
327
338
|
}
|
|
328
339
|
}
|
|
340
|
+
return {
|
|
341
|
+
run,
|
|
342
|
+
workerId,
|
|
343
|
+
options,
|
|
344
|
+
scope,
|
|
345
|
+
task,
|
|
346
|
+
absoluteResultPath,
|
|
347
|
+
rawResult,
|
|
348
|
+
parsedResult,
|
|
349
|
+
destination: "",
|
|
350
|
+
pathAuditId: "",
|
|
351
|
+
acceptedAuditId: "",
|
|
352
|
+
resultNode: undefined,
|
|
353
|
+
verifierNodeId: "",
|
|
354
|
+
verifierStatus: "",
|
|
355
|
+
output: undefined
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
/** Step 2 — attestSandbox/attestDelegation: verify the agent's signed telemetry
|
|
359
|
+
* BEFORE recording it, enforce the opt-in require-attested-telemetry gate (still
|
|
360
|
+
* fail-closed, pre-mutation), and build the agent-hop provenance. Non-agent hops
|
|
361
|
+
* return an empty delegation. */
|
|
362
|
+
function attestWorkerDelegation(accept) {
|
|
363
|
+
const { run, workerId, options, task, absoluteResultPath, rawResult } = accept;
|
|
329
364
|
// Agent Delegation Drive (v0.1.38): if this worker's result.md was produced by an
|
|
330
365
|
// EXTERNAL agent, record the agent-hop attestation AS PROVENANCE — the agent
|
|
331
366
|
// (kind:process) handle, the agent-REPORTED model (never CW_AGENT_MODEL), the
|
|
@@ -345,7 +380,7 @@ function recordWorkerOutput(run, workerId, resultPath, options = {}) {
|
|
|
345
380
|
// it instead of recording unverifiable usage. Default behavior is unchanged
|
|
346
381
|
// (flag-and-surface). Non-agent hops carry no verdict and are never blocked.
|
|
347
382
|
if (options.requireAttestedTelemetry && telemetry && telemetry.status !== "attested") {
|
|
348
|
-
const error = structuredError("telemetry-unattested-blocked", `Worker ${workerId} telemetry is ${telemetry.status} (${telemetry.reason || "unverified"}) and require-attested-telemetry is enabled — refusing to accept a hop whose usage cannot be cryptographically verified`, { path: absoluteResultPath, retryable: false });
|
|
383
|
+
const error = (0, helpers_1.structuredError)("telemetry-unattested-blocked", `Worker ${workerId} telemetry is ${telemetry.status} (${telemetry.reason || "unverified"}) and require-attested-telemetry is enabled — refusing to accept a hop whose usage cannot be cryptographically verified`, { path: absoluteResultPath, retryable: false });
|
|
349
384
|
recordWorkerFailure(run, workerId, error, { ...options, persist: options.persist });
|
|
350
385
|
throw new Error(error.message);
|
|
351
386
|
}
|
|
@@ -365,6 +400,16 @@ function recordWorkerOutput(run, workerId, resultPath, options = {}) {
|
|
|
365
400
|
...(telemetry ? { usageAttestation: telemetry.status, usageAttestationReason: telemetry.reason } : {})
|
|
366
401
|
}
|
|
367
402
|
: undefined;
|
|
403
|
+
return { agentDelegation, telemetry };
|
|
404
|
+
}
|
|
405
|
+
/** Step 3 — recordStateNode/audit: the irreversible accept. Records the allowed
|
|
406
|
+
* path decision, copies the result into the run results dir, completes the task,
|
|
407
|
+
* builds + appends the result node, emits the accepted audit event, re-normalizes
|
|
408
|
+
* node evidence against both audit ids, and surfaces the empty-capture warning.
|
|
409
|
+
* Writes destination/pathAuditId/acceptedAuditId/resultNode back into `accept`. */
|
|
410
|
+
function acceptWorkerResult(accept, delegation) {
|
|
411
|
+
const { run, workerId, scope, task, absoluteResultPath, parsedResult } = accept;
|
|
412
|
+
const { agentDelegation } = delegation;
|
|
368
413
|
const pathAudit = (0, trust_audit_1.recordSandboxPathDecision)(run, {
|
|
369
414
|
workerId,
|
|
370
415
|
taskId: task.id,
|
|
@@ -453,58 +498,75 @@ function recordWorkerOutput(run, workerId, resultPath, options = {}) {
|
|
|
453
498
|
metadata: { reason: "no findings or evidence captured from result.md", resultPath: destination }
|
|
454
499
|
});
|
|
455
500
|
}
|
|
501
|
+
accept.destination = destination;
|
|
502
|
+
accept.pathAuditId = pathAudit.id;
|
|
503
|
+
accept.acceptedAuditId = acceptedAudit.id;
|
|
504
|
+
accept.resultNode = resultNode;
|
|
505
|
+
}
|
|
506
|
+
/** Step 4 — recordTelemetryLedger: the agent-hop attestation. Binds the telemetry
|
|
507
|
+
* verdict into the append-only hash-chained ledger BEFORE the audit event (so the
|
|
508
|
+
* event can cross-link the record hash), then emits the worker.agent-delegation
|
|
509
|
+
* audit event. No-op for non-agent hops. */
|
|
510
|
+
function recordWorkerDelegationLedger(accept, delegation) {
|
|
511
|
+
const { agentDelegation } = delegation;
|
|
456
512
|
// The agent-hop attestation event — hung off worker.output, alongside
|
|
457
513
|
// worker.backend. Recorded in trust-audit/provenance, NEVER in node evidence.
|
|
458
|
-
if (agentDelegation)
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
promptDigest: agentDelegation.promptDigest,
|
|
468
|
-
reportedUsage: agentDelegation.reportedUsage,
|
|
469
|
-
usageSignature: agentDelegation.usageSignature,
|
|
470
|
-
attestation: agentDelegation.usageAttestation,
|
|
471
|
-
attestationReason: agentDelegation.usageAttestationReason
|
|
472
|
-
})
|
|
473
|
-
: undefined;
|
|
474
|
-
(0, trust_audit_1.recordTrustAuditEvent)(run, {
|
|
475
|
-
kind: "worker.agent-delegation",
|
|
476
|
-
decision: "recorded",
|
|
477
|
-
source: "host-attested",
|
|
514
|
+
if (!agentDelegation)
|
|
515
|
+
return;
|
|
516
|
+
const { run, workerId, scope, task, resultNode, acceptedAuditId } = accept;
|
|
517
|
+
// Track 1 (tamper-evidence): bind this verdict into the append-only,
|
|
518
|
+
// hash-chained telemetry ledger BEFORE the audit event, so the event can
|
|
519
|
+
// cross-link the record hash. Editing the recorded verdict/usage later breaks
|
|
520
|
+
// the chain (verifyTelemetryLedger). Only when a verdict was computed.
|
|
521
|
+
const ledgerRecord = agentDelegation.usageAttestation
|
|
522
|
+
? (0, telemetry_ledger_1.appendTelemetryAttestation)(run, {
|
|
478
523
|
workerId,
|
|
479
524
|
taskId: task.id,
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
525
|
+
promptDigest: agentDelegation.promptDigest,
|
|
526
|
+
reportedUsage: agentDelegation.reportedUsage,
|
|
527
|
+
usageSignature: agentDelegation.usageSignature,
|
|
528
|
+
attestation: agentDelegation.usageAttestation,
|
|
529
|
+
attestationReason: agentDelegation.usageAttestationReason
|
|
530
|
+
})
|
|
531
|
+
: undefined;
|
|
532
|
+
(0, trust_audit_1.recordTrustAuditEvent)(run, {
|
|
533
|
+
kind: "worker.agent-delegation",
|
|
534
|
+
decision: "recorded",
|
|
535
|
+
source: "host-attested",
|
|
536
|
+
workerId,
|
|
537
|
+
taskId: task.id,
|
|
538
|
+
nodeId: resultNode.id,
|
|
539
|
+
sandboxProfileId: scope.sandboxProfileId,
|
|
540
|
+
policySnapshot: scope.sandboxPolicy,
|
|
541
|
+
parentEventIds: [acceptedAuditId],
|
|
542
|
+
metadata: {
|
|
543
|
+
backendId: agentDelegation.backendId,
|
|
544
|
+
handleKind: agentDelegation.handle.kind,
|
|
545
|
+
handleRef: agentDelegation.handle.ref,
|
|
546
|
+
model: agentDelegation.model,
|
|
547
|
+
promptDigest: agentDelegation.promptDigest,
|
|
548
|
+
resultDigest: agentDelegation.resultDigest,
|
|
549
|
+
command: agentDelegation.command,
|
|
550
|
+
args: agentDelegation.args,
|
|
551
|
+
exitCode: agentDelegation.exitCode,
|
|
552
|
+
// Track 1: the telemetry verdict travels with the agent-hop event so the
|
|
553
|
+
// audit report can surface `unattested` usage loudly. Absent ⇒ no usage.
|
|
554
|
+
...(agentDelegation.usageAttestation
|
|
555
|
+
? {
|
|
556
|
+
telemetryAttestation: agentDelegation.usageAttestation,
|
|
557
|
+
...(agentDelegation.usageAttestationReason ? { telemetryAttestationReason: agentDelegation.usageAttestationReason } : {}),
|
|
558
|
+
...(agentDelegation.reportedUsage ? { reportedUsage: agentDelegation.reportedUsage } : {}),
|
|
559
|
+
// Cross-link to the hash-chained ledger entry (tamper-evidence).
|
|
560
|
+
...(ledgerRecord ? { telemetryRecordId: ledgerRecord.recordId, telemetryRecordHash: ledgerRecord.recordHash, telemetryPrevHash: ledgerRecord.prevHash } : {})
|
|
561
|
+
}
|
|
562
|
+
: {})
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
/** Step 5 — runVerify: drive the verify pipeline stage off the accepted result node
|
|
567
|
+
* and record the verifier node id on the task + accept context. */
|
|
568
|
+
function runWorkerVerify(accept) {
|
|
569
|
+
const { run, workerId, scope, task, parsedResult, destination, resultNode } = accept;
|
|
508
570
|
const verifierResult = (0, pipeline_runner_1.createPipelineRunner)({ persist: false }).runPipelineStage(run, "verify", resultNode.id, {
|
|
509
571
|
outputNodeId: `${run.id}:verifier:${task.id}`,
|
|
510
572
|
outputStatus: "verified",
|
|
@@ -517,14 +579,24 @@ function recordWorkerOutput(run, workerId, resultPath, options = {}) {
|
|
|
517
579
|
metadata: { taskId: task.id, workerId, resultNodeId: resultNode.id, sandboxProfileId: scope.sandboxProfileId }
|
|
518
580
|
});
|
|
519
581
|
task.verifierNodeId = verifierResult.outputNodeId;
|
|
582
|
+
accept.verifierNodeId = verifierResult.outputNodeId;
|
|
583
|
+
// Carry the verify verdict for the scope-status transition in recordWorkerCompletion.
|
|
584
|
+
accept.verifierStatus = verifierResult.status;
|
|
585
|
+
}
|
|
586
|
+
/** Step 6 — recordStateNode (worker record): assemble the worker output record +
|
|
587
|
+
* host-attested usage record, then persist the worker scope with the verify-derived
|
|
588
|
+
* status, output digest/size, and (when present) usage. */
|
|
589
|
+
function recordWorkerCompletion(accept, delegation) {
|
|
590
|
+
const { run, workerId, scope, task, absoluteResultPath, rawResult, resultNode, verifierNodeId, verifierStatus, pathAuditId, acceptedAuditId } = accept;
|
|
591
|
+
const { agentDelegation, telemetry } = delegation;
|
|
520
592
|
const output = {
|
|
521
593
|
workerId,
|
|
522
594
|
taskId: task.id,
|
|
523
595
|
resultPath: absoluteResultPath,
|
|
524
596
|
recordedAt: new Date().toISOString(),
|
|
525
597
|
stateNodeId: resultNode.id,
|
|
526
|
-
verifierNodeId
|
|
527
|
-
auditEventIds: [
|
|
598
|
+
verifierNodeId,
|
|
599
|
+
auditEventIds: [pathAuditId, acceptedAuditId]
|
|
528
600
|
};
|
|
529
601
|
// Host-attested usage rides on the worker record. Recorded when the agent
|
|
530
602
|
// REPORTED a model OR token usage — `unreported`/absent stays ABSENT (never
|
|
@@ -547,7 +619,7 @@ function recordWorkerOutput(run, workerId, resultPath, options = {}) {
|
|
|
547
619
|
updateWorkerScope(run, {
|
|
548
620
|
...scope,
|
|
549
621
|
updatedAt: new Date().toISOString(),
|
|
550
|
-
status:
|
|
622
|
+
status: verifierStatus === "advanced" ? "verified" : "completed",
|
|
551
623
|
resultNodeId: resultNode.id,
|
|
552
624
|
output,
|
|
553
625
|
// Output integrity (v0.1.63): SHA256 digest + file size
|
|
@@ -555,20 +627,23 @@ function recordWorkerOutput(run, workerId, resultPath, options = {}) {
|
|
|
555
627
|
outputSizeBytes: Buffer.byteLength(rawResult, "utf8"),
|
|
556
628
|
...(usageRecord ? { usage: usageRecord } : {})
|
|
557
629
|
});
|
|
558
|
-
|
|
630
|
+
accept.output = output;
|
|
631
|
+
}
|
|
632
|
+
/** Step 7 — fanOut: publish the accepted output to the blackboard and record the
|
|
633
|
+
* multi-agent worker output (linking the blackboard message/artifact refs). */
|
|
634
|
+
function fanOutWorkerOutput(accept) {
|
|
635
|
+
const { run, workerId, scope, task, absoluteResultPath, parsedResult, destination, resultNode, verifierNodeId, acceptedAuditId } = accept;
|
|
636
|
+
const blackboardLinks = publishWorkerOutputToBlackboard(run, scope, task, parsedResult.summary, destination, absoluteResultPath, resultNode.evidence, acceptedAuditId);
|
|
559
637
|
(0, multi_agent_1.recordMultiAgentWorkerOutput)(run, {
|
|
560
638
|
workerId,
|
|
561
639
|
taskId: task.id,
|
|
562
640
|
resultNodeId: resultNode.id,
|
|
563
|
-
verifierNodeId
|
|
641
|
+
verifierNodeId,
|
|
564
642
|
evidence: resultNode.evidence,
|
|
565
643
|
artifactPaths: [destination, absoluteResultPath],
|
|
566
644
|
blackboardMessageIds: blackboardLinks.messageIds,
|
|
567
645
|
blackboardArtifactRefIds: blackboardLinks.artifactRefIds
|
|
568
646
|
});
|
|
569
|
-
if (options.persist !== false)
|
|
570
|
-
(0, state_1.saveCheckpoint)(run);
|
|
571
|
-
return output;
|
|
572
647
|
}
|
|
573
648
|
function recordWorkerFailure(run, workerId, error, options = {}) {
|
|
574
649
|
const scope = requireWorkerScope(run, workerId);
|
|
@@ -581,7 +656,7 @@ function recordWorkerFailure(run, workerId, error, options = {}) {
|
|
|
581
656
|
status: "pending",
|
|
582
657
|
loopStage: "adjust",
|
|
583
658
|
inputs: { workerId, taskId: task.id, dispatchId: scope.dispatchId },
|
|
584
|
-
artifacts: workerArtifacts(scope),
|
|
659
|
+
artifacts: (0, paths_1.workerArtifacts)(scope),
|
|
585
660
|
parents: task.stateNodeId ? [task.stateNodeId] : [],
|
|
586
661
|
contractId: pipeline_contract_1.DEFAULT_PIPELINE_CONTRACT_ID,
|
|
587
662
|
metadata: { workerId, taskId: task.id, dispatchId: scope.dispatchId, workerDir: scope.workerDir, sandboxProfileId: scope.sandboxProfileId }
|
|
@@ -636,7 +711,7 @@ function recordWorkerFailure(run, workerId, error, options = {}) {
|
|
|
636
711
|
updatedAt: new Date().toISOString(),
|
|
637
712
|
status: structured.code === "worker-boundary-violation" || structured.code.startsWith("sandbox-") ? "rejected" : "failed",
|
|
638
713
|
retryCount: typeof options.retryCount === "number" ? options.retryCount : scope.retryCount,
|
|
639
|
-
feedbackIds: unique([...(scope.feedbackIds || []), feedback.id]),
|
|
714
|
+
feedbackIds: (0, helpers_1.unique)([...(scope.feedbackIds || []), feedback.id]),
|
|
640
715
|
errors: [...(scope.errors || []), structured]
|
|
641
716
|
});
|
|
642
717
|
if (options.persist !== false)
|
|
@@ -649,7 +724,7 @@ function recordWorkerRetryAttempt(run, workerId, attempts, reason, options = {})
|
|
|
649
724
|
...scope,
|
|
650
725
|
updatedAt: new Date().toISOString(),
|
|
651
726
|
retryCount: attempts,
|
|
652
|
-
metadata: compactMetadata({
|
|
727
|
+
metadata: (0, helpers_1.compactMetadata)({
|
|
653
728
|
...scope.metadata,
|
|
654
729
|
agentDelegationAttempts: attempts,
|
|
655
730
|
agentDelegationLastFailure: reason
|
|
@@ -668,8 +743,8 @@ function summarizeWorkers(run) {
|
|
|
668
743
|
const workers = listWorkerScopes(run);
|
|
669
744
|
return {
|
|
670
745
|
total: workers.length,
|
|
671
|
-
byStatus: countBy(workers, (scope) => scope.status),
|
|
672
|
-
manifestPaths: workers.map(manifestPath),
|
|
746
|
+
byStatus: (0, helpers_1.countBy)(workers, (scope) => scope.status),
|
|
747
|
+
manifestPaths: workers.map(paths_1.manifestPath),
|
|
673
748
|
failed: workers
|
|
674
749
|
.filter((scope) => scope.status === "failed" || scope.status === "rejected")
|
|
675
750
|
.map((scope) => ({ id: scope.id, status: scope.status, feedbackIds: scope.feedbackIds || [] }))
|
|
@@ -705,15 +780,9 @@ function reclaimOrphans(run, now) {
|
|
|
705
780
|
}
|
|
706
781
|
if (orphans.length) {
|
|
707
782
|
writeWorkerIndex(run);
|
|
708
|
-
saveWorkerCheckpoint(run);
|
|
709
783
|
}
|
|
710
784
|
return { runId: run.id, reclaimed: orphans.length, orphans };
|
|
711
785
|
}
|
|
712
|
-
function saveWorkerCheckpoint(run) {
|
|
713
|
-
// Durable write via atomic temp+rename (same contract as saveCheckpoint)
|
|
714
|
-
// For worker index, the atomic write in writeWorkerIndex already handles it.
|
|
715
|
-
// This is a no-op wrapper that signals the checkpoint boundary.
|
|
716
|
-
}
|
|
717
786
|
function ensureWorkerState(run) {
|
|
718
787
|
run.paths.workersDir = run.paths.workersDir || node_path_1.default.join(run.paths.runDir, "workers");
|
|
719
788
|
node_fs_1.default.mkdirSync(run.paths.workersDir, { recursive: true });
|
|
@@ -771,7 +840,7 @@ function updateWorkerScope(run, scope) {
|
|
|
771
840
|
return updated;
|
|
772
841
|
}
|
|
773
842
|
function writeWorkerScope(scope) {
|
|
774
|
-
(0, state_1.writeJson)(workerScopePath(scope), scope);
|
|
843
|
+
(0, state_1.writeJson)((0, paths_1.workerScopePath)(scope), scope);
|
|
775
844
|
}
|
|
776
845
|
function writeWorkerIndex(run) {
|
|
777
846
|
ensureWorkerState(run);
|
|
@@ -784,7 +853,7 @@ function writeWorkerIndex(run) {
|
|
|
784
853
|
dispatchId: scope.dispatchId,
|
|
785
854
|
status: scope.status,
|
|
786
855
|
workerDir: scope.workerDir,
|
|
787
|
-
manifestPath: manifestPath(scope),
|
|
856
|
+
manifestPath: (0, paths_1.manifestPath)(scope),
|
|
788
857
|
resultPath: scope.resultPath,
|
|
789
858
|
sandboxProfileId: scope.sandboxProfileId,
|
|
790
859
|
backendId: scope.backendId,
|
|
@@ -800,9 +869,9 @@ function loadWorkerScopesFromDisk(run) {
|
|
|
800
869
|
return node_fs_1.default
|
|
801
870
|
.readdirSync(workerRoot(run), { withFileTypes: true })
|
|
802
871
|
.filter((entry) => entry.isDirectory())
|
|
803
|
-
.map((entry) => node_path_1.default.join(workerRoot(run), entry.name, WORKER_SCOPE_FILE))
|
|
872
|
+
.map((entry) => node_path_1.default.join(workerRoot(run), entry.name, paths_1.WORKER_SCOPE_FILE))
|
|
804
873
|
.filter((file) => node_fs_1.default.existsSync(file))
|
|
805
|
-
.map((file) => JSON.parse(node_fs_1.default.readFileSync(file, "utf8")));
|
|
874
|
+
.map((file) => (0, validation_1.validateWorkerScope)(JSON.parse(node_fs_1.default.readFileSync(file, "utf8"))));
|
|
806
875
|
}
|
|
807
876
|
function requireWorkerScope(run, workerId) {
|
|
808
877
|
const scope = getWorkerScope(run, workerId);
|
|
@@ -823,6 +892,12 @@ function sandboxPolicyForBoundary(run, scope, options = {}) {
|
|
|
823
892
|
if (scope.sandboxPolicy && !options.policy && !options.sandboxProfileId)
|
|
824
893
|
return scope.sandboxPolicy;
|
|
825
894
|
const profileId = options.sandboxProfileId || options.policy?.sandboxProfileId || scope.sandboxProfileId || sandbox_profile_1.DEFAULT_SANDBOX_PROFILE_ID;
|
|
895
|
+
// H7: when the scope.sandboxPolicy snapshot is LOST, this re-resolves the policy
|
|
896
|
+
// by its logical profileId against the WORKER's paths (scope.workerDir etc.). For
|
|
897
|
+
// a CUSTOM profile the bundled lookup would throw not-found; threading
|
|
898
|
+
// run.customSandboxProfiles lets resolveSandboxProfileById re-resolve the persisted
|
|
899
|
+
// DEFINITION here — re-enforcing the same policy with worker-correct path tokens
|
|
900
|
+
// (NOT the dispatch-time paths), so a legitimate worker write is not falsely denied.
|
|
826
901
|
return (0, sandbox_profile_1.sandboxPolicyForWorker)(profileId, {
|
|
827
902
|
cwd: run.cwd,
|
|
828
903
|
runDir: run.paths.runDir,
|
|
@@ -838,7 +913,8 @@ function sandboxPolicyForBoundary(run, scope, options = {}) {
|
|
|
838
913
|
...(!scope.sandboxPolicy ? scope.allowedPaths || [] : [])
|
|
839
914
|
],
|
|
840
915
|
allowArtifacts: options.policy?.allowArtifacts,
|
|
841
|
-
allowLogs: options.policy?.allowLogs
|
|
916
|
+
allowLogs: options.policy?.allowLogs,
|
|
917
|
+
customProfiles: run.customSandboxProfiles
|
|
842
918
|
});
|
|
843
919
|
}
|
|
844
920
|
function blackboardManifest(run, scope) {
|
|
@@ -927,7 +1003,7 @@ function blackboardLinkage(run, scope) {
|
|
|
927
1003
|
const role = scope.multiAgent?.roleId ? run.multiAgent?.roles.find((entry) => entry.id === scope.multiAgent?.roleId) : undefined;
|
|
928
1004
|
const multiAgentRun = scope.multiAgent?.runId ? run.multiAgent?.runs.find((entry) => entry.id === scope.multiAgent?.runId) : undefined;
|
|
929
1005
|
const blackboardId = membership?.blackboardId || group?.blackboardId || role?.blackboardId || multiAgentRun?.blackboardId;
|
|
930
|
-
const topicIds = unique([
|
|
1006
|
+
const topicIds = (0, helpers_1.unique)([
|
|
931
1007
|
...(membership?.topicIds || []),
|
|
932
1008
|
...(group?.topicIds || []),
|
|
933
1009
|
...(role?.topicIds || []),
|
|
@@ -935,94 +1011,27 @@ function blackboardLinkage(run, scope) {
|
|
|
935
1011
|
]);
|
|
936
1012
|
return { blackboardId, topicIds };
|
|
937
1013
|
}
|
|
938
|
-
function manifestPath(scope) {
|
|
939
|
-
return node_path_1.default.join(scope.workerDir, WORKER_MANIFEST_FILE);
|
|
940
|
-
}
|
|
941
|
-
function workerScopePath(scope) {
|
|
942
|
-
return node_path_1.default.join(scope.workerDir, WORKER_SCOPE_FILE);
|
|
943
|
-
}
|
|
944
|
-
// Deterministic worker id (v0.1.40 self-audit P2): a wall-clock stamp + Math.random()
|
|
945
|
-
// made every dispatch mint a different id, so audit references were not reproducible
|
|
946
|
-
// across re-runs of the same inputs. The id is now derived from the task plus a
|
|
947
|
-
// per-task sequence (count of worker scopes already allocated for that task + 1),
|
|
948
|
-
// so re-running the same workflow yields byte-identical worker ids while retries of
|
|
949
|
-
// the SAME task still get a fresh, unique id. (workerId is excluded from the
|
|
950
|
-
// snapshot source fingerprint, so this does not change replay digests.)
|
|
951
|
-
function createWorkerId(run, taskId) {
|
|
952
|
-
const prefix = `worker-${(0, state_1.safeFileName)(taskId)}-`;
|
|
953
|
-
const seq = (run.workers || []).filter((scope) => scope.id.startsWith(prefix)).length + 1;
|
|
954
|
-
return `${prefix}${String(seq).padStart(4, "0")}`;
|
|
955
|
-
}
|
|
956
|
-
function workerArtifacts(scope) {
|
|
957
|
-
return [
|
|
958
|
-
{ id: "worker", kind: "json", path: workerScopePath(scope) },
|
|
959
|
-
{ id: "worker-manifest", kind: "json", path: manifestPath(scope) },
|
|
960
|
-
{ id: "worker-input", kind: "markdown", path: scope.inputPath }
|
|
961
|
-
];
|
|
962
|
-
}
|
|
963
1014
|
function normalizeWorkerError(error, scope, options) {
|
|
964
|
-
if (isBoundaryViolation(error)) {
|
|
965
|
-
return structuredError(error.code, error.message, {
|
|
1015
|
+
if ((0, helpers_1.isBoundaryViolation)(error)) {
|
|
1016
|
+
return (0, helpers_1.structuredError)(error.code, error.message, {
|
|
966
1017
|
path: error.path,
|
|
967
1018
|
retryable: false,
|
|
968
1019
|
details: { allowedPaths: error.allowedPaths, workerId: scope.id, taskId: scope.taskId, sandboxProfileId: scope.sandboxProfileId }
|
|
969
1020
|
});
|
|
970
1021
|
}
|
|
971
|
-
if (isStateNodeError(error)) {
|
|
1022
|
+
if ((0, helpers_1.isStateNodeError)(error)) {
|
|
972
1023
|
return {
|
|
973
1024
|
...error,
|
|
974
1025
|
at: error.at || new Date().toISOString(),
|
|
975
1026
|
path: options.path || error.path,
|
|
976
1027
|
retryable: options.retryable ?? error.retryable ?? false,
|
|
977
|
-
details: compactMetadata({ ...(error.details || {}), workerId: scope.id, taskId: scope.taskId })
|
|
1028
|
+
details: (0, helpers_1.compactMetadata)({ ...(error.details || {}), workerId: scope.id, taskId: scope.taskId })
|
|
978
1029
|
};
|
|
979
1030
|
}
|
|
980
1031
|
const message = error instanceof Error ? error.message : String(error);
|
|
981
|
-
return structuredError(options.code || "worker-runtime-error", message, {
|
|
1032
|
+
return (0, helpers_1.structuredError)(options.code || "worker-runtime-error", message, {
|
|
982
1033
|
path: options.path,
|
|
983
1034
|
retryable: options.retryable ?? false,
|
|
984
1035
|
details: { workerId: scope.id, taskId: scope.taskId }
|
|
985
1036
|
});
|
|
986
1037
|
}
|
|
987
|
-
function structuredError(code, message, options = {}) {
|
|
988
|
-
return {
|
|
989
|
-
code,
|
|
990
|
-
message,
|
|
991
|
-
at: new Date().toISOString(),
|
|
992
|
-
path: options.path,
|
|
993
|
-
retryable: options.retryable,
|
|
994
|
-
details: options.details
|
|
995
|
-
};
|
|
996
|
-
}
|
|
997
|
-
function isBoundaryViolation(value) {
|
|
998
|
-
return Boolean(value && typeof value === "object" && "allowedPaths" in value && "message" in value);
|
|
999
|
-
}
|
|
1000
|
-
function isStateNodeError(value) {
|
|
1001
|
-
return Boolean(value && typeof value === "object" && "code" in value && "message" in value);
|
|
1002
|
-
}
|
|
1003
|
-
function mergeScopes(left, right) {
|
|
1004
|
-
const merged = [...left];
|
|
1005
|
-
for (const scope of right) {
|
|
1006
|
-
const index = merged.findIndex((candidate) => candidate.id === scope.id);
|
|
1007
|
-
if (index >= 0)
|
|
1008
|
-
merged[index] = scope;
|
|
1009
|
-
else
|
|
1010
|
-
merged.push(scope);
|
|
1011
|
-
}
|
|
1012
|
-
return merged;
|
|
1013
|
-
}
|
|
1014
|
-
function unique(values) {
|
|
1015
|
-
return Array.from(new Set(values.filter(Boolean)));
|
|
1016
|
-
}
|
|
1017
|
-
function compactMetadata(value) {
|
|
1018
|
-
const entries = Object.entries(value).filter(([, entry]) => entry !== undefined);
|
|
1019
|
-
return entries.length ? Object.fromEntries(entries) : undefined;
|
|
1020
|
-
}
|
|
1021
|
-
function countBy(items, key) {
|
|
1022
|
-
const counts = {};
|
|
1023
|
-
for (const item of items) {
|
|
1024
|
-
const value = key(item);
|
|
1025
|
-
counts[value] = (counts[value] || 0) + 1;
|
|
1026
|
-
}
|
|
1027
|
-
return counts;
|
|
1028
|
-
}
|
|
@@ -131,8 +131,17 @@ node dist/cli.js backend probe agent --json # ready iff configured, else un
|
|
|
131
131
|
node dist/cli.js run architecture-review --drive --repo /path/to/repo --question "Is the design sound?"
|
|
132
132
|
node dist/cli.js run architecture-review --drive --once --repo /path/to/repo --question "..." # one step
|
|
133
133
|
node dist/cli.js run drive <run-id> --json # read-only preview of the next step
|
|
134
|
+
|
|
135
|
+
# quickstart --resume: a guided stop-then-resume a newcomer can WITNESS in <5 min
|
|
136
|
+
node dist/cli.js quickstart --resume --repo /path/to/repo --question "..." # advances ONE step, prints a continue line
|
|
137
|
+
node dist/cli.js quickstart --run <run-id> --resume # continues that run to completion
|
|
134
138
|
```
|
|
135
139
|
|
|
140
|
+
`quickstart --resume` with no `--run` drives a single step and prints a
|
|
141
|
+
copy-pasteable `cw quickstart --run <id> --resume` continue line; rerun it with the
|
|
142
|
+
`--run <id>` to finish. The continuing invocation echoes `resumedFrom: <id>`. Bare
|
|
143
|
+
`quickstart` (no `--resume`) is unchanged — it drives straight to completion.
|
|
144
|
+
|
|
136
145
|
For faster first results, use the opt-in fast app instead of changing the full
|
|
137
146
|
review contract:
|
|
138
147
|
|
|
@@ -248,3 +257,8 @@ Migration DAG with reversible edges (v0.1.45), capability auto-discovery (v0.1.4
|
|
|
248
257
|
## Fast Architecture Review (v0.1.80)
|
|
249
258
|
|
|
250
259
|
Adds the opt-in fast architecture-review lane: scoped JSONL source contexts, diff-aware exports, reusable Map and Assess results, measurable wrapper metrics, actionable background full-review handoff, and userland model policy flags for routing fast/strong workers without changing the full review contract.
|
|
260
|
+
|
|
261
|
+
## Resumable Drive & Resume Routing (v0.1.81)
|
|
262
|
+
|
|
263
|
+
Adds `run resume <id> --drive/--once` alongside `quickstart --resume`: a stopped pipeline resumes in-place, advancing to completion (`--drive`) or one deterministic step (`--once`) over the same plan->dispatch->agent-fulfill->accept->commit lifecycle, echoing `resumedFrom: <id>`. Fixes the `run resume --drive` CLI routing so the drive flag reaches the resumed run instead of being read as an app name. Replay determinism and the agent evidence triple are unchanged.
|
|
264
|
+
_No behavioral change in v0.1.82 (drive/quickstart resolve the run repo via an explicit base directory rather than process.chdir; delegation behavior is unchanged)._
|
package/docs/cli-mcp-parity.7.md
CHANGED
|
@@ -385,3 +385,8 @@ Migration DAG with reversible edges (v0.1.45), capability auto-discovery (v0.1.4
|
|
|
385
385
|
## Fast Architecture Review (v0.1.80)
|
|
386
386
|
|
|
387
387
|
Adds the opt-in fast architecture-review lane: scoped JSONL source contexts, diff-aware exports, reusable Map and Assess results, measurable wrapper metrics, actionable background full-review handoff, and userland model policy flags for routing fast/strong workers without changing the full review contract.
|
|
388
|
+
|
|
389
|
+
## Re-Prove Verbs on Both Surfaces (v0.1.81)
|
|
390
|
+
|
|
391
|
+
v0.1.81 grows the parity surface with two new both-surface, fail-closed verbs declared once in the capability registry: `cw audit verify` / `cw_audit_verify` re-proves the trust-audit chain and exits non-zero on any unverified or corrupt chain, and `cw run inspect-archive` / `cw_run_inspect_archive` is a read-only archive integrity check. Each `cw <cmd> --json` is schema-identical to its `cw_<tool>` and validated by the same parity gate.
|
|
392
|
+
_No changes in v0.1.82._
|
|
@@ -127,3 +127,6 @@ Migration DAG with reversible edges (v0.1.45), capability auto-discovery (v0.1.4
|
|
|
127
127
|
## Fast Architecture Review (v0.1.80)
|
|
128
128
|
|
|
129
129
|
Adds the opt-in fast architecture-review lane: scoped JSONL source contexts, diff-aware exports, reusable Map and Assess results, measurable wrapper metrics, actionable background full-review handoff, and userland model policy flags for routing fast/strong workers without changing the full review contract.
|
|
130
|
+
|
|
131
|
+
_No changes to the contract-migration subsystem in v0.1.81._
|
|
132
|
+
_No changes in v0.1.82._
|