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/trust-audit.js
CHANGED
|
@@ -3,7 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.TRUST_AUDIT_SCHEMA_VERSION = void 0;
|
|
6
|
+
exports.CORRELATION_ID_FIELDS = exports.TRUST_AUDIT_SCHEMA_VERSION = void 0;
|
|
7
|
+
exports.trustAuditGenesis = trustAuditGenesis;
|
|
8
|
+
exports.verifyTrustAudit = verifyTrustAudit;
|
|
7
9
|
exports.ensureTrustAudit = ensureTrustAudit;
|
|
8
10
|
exports.recordTrustAuditEvent = recordTrustAuditEvent;
|
|
9
11
|
exports.recordSandboxPathDecision = recordSandboxPathDecision;
|
|
@@ -22,17 +24,198 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
22
24
|
const node_path_1 = __importDefault(require("node:path"));
|
|
23
25
|
const state_1 = require("./state");
|
|
24
26
|
const evidence_grounding_1 = require("./evidence-grounding");
|
|
27
|
+
const execution_backend_1 = require("./execution-backend");
|
|
28
|
+
const telemetry_attestation_1 = require("./telemetry-attestation");
|
|
25
29
|
exports.TRUST_AUDIT_SCHEMA_VERSION = 1;
|
|
30
|
+
// ---- Tamper-evidence chain (v0.1.81) --------------------------------------
|
|
31
|
+
// Same discipline as telemetry-ledger.ts / reclamation.ts: the event log is
|
|
32
|
+
// hash-chained in APPEND order, and verifyTrustAudit recomputes every hash
|
|
33
|
+
// independently (never trusts a stored value), so an edited or removed event is
|
|
34
|
+
// detected. Durability (fsync) only guards against power loss; this guards
|
|
35
|
+
// against post-hoc edits — the threat an external auditor actually cares about.
|
|
36
|
+
//
|
|
37
|
+
// THREAT MODEL (be honest about the limit): the genesis is sha256(runId), so this
|
|
38
|
+
// detects casual/partial tampering, accidental corruption, truncation, removal,
|
|
39
|
+
// and forged-unchained lines — but NOT a determined local writer who re-chains the
|
|
40
|
+
// WHOLE log with this module's own sha256 after an edit. That is the same limit
|
|
41
|
+
// reclamation.ts's tombstone chain has, and it is INHERENT to a local, self-
|
|
42
|
+
// recomputable chain: closing it needs an anchor the writer cannot reproduce.
|
|
43
|
+
// CW cannot self-sign that anchor — by design CW holds NO private key (see
|
|
44
|
+
// telemetry-attestation.ts: "CW never holds the private key"; the AGENT signs its
|
|
45
|
+
// usage, CW only VERIFIES with the operator-provisioned public half). The single
|
|
46
|
+
// cryptographic anchor that exists is therefore the agent's telemetry signature,
|
|
47
|
+
// which binds agent-reported USAGE (worker-isolation cross-links the chain into it)
|
|
48
|
+
// — it does NOT cover CW-only sandbox/policy/commit-gate decisions, which have no
|
|
49
|
+
// external signer. For those, the only stronger guarantee is operational: commit
|
|
50
|
+
// events.jsonl to an external append-only medium (git history / a remote log) the
|
|
51
|
+
// local writer cannot rewrite. The chain is a strict upgrade over a bare append-
|
|
52
|
+
// only log, not a substitute for an external anchor — and that anchor is not
|
|
53
|
+
// something CW can mint for itself.
|
|
54
|
+
/** Single source of truth for a run's audit-paths object: the schemaVersion plus
|
|
55
|
+
* the three derived file paths under auditRoot(run). ensureTrustAudit /
|
|
56
|
+
* refreshTrustAudit spread this, and createEventId reads .eventLogPath from it, so
|
|
57
|
+
* the path-derivation rule lives in exactly one place. */
|
|
58
|
+
function trustAuditPaths(run) {
|
|
59
|
+
const dir = auditRoot(run);
|
|
60
|
+
return {
|
|
61
|
+
schemaVersion: exports.TRUST_AUDIT_SCHEMA_VERSION,
|
|
62
|
+
eventLogPath: node_path_1.default.join(dir, "events.jsonl"),
|
|
63
|
+
summaryPath: node_path_1.default.join(dir, "summary.json"),
|
|
64
|
+
indexPath: node_path_1.default.join(dir, "index.json")
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** Genesis prevHash for a run's chain (no prior event). */
|
|
68
|
+
function trustAuditGenesis(runId) {
|
|
69
|
+
return (0, execution_backend_1.sha256)(`cw-trust-audit:${runId}`);
|
|
70
|
+
}
|
|
71
|
+
/** Canonical bytes the eventHash binds: every field EXCEPT eventHash itself
|
|
72
|
+
* (prevEventHash included, so the chain link is bound).
|
|
73
|
+
*
|
|
74
|
+
* The hash binds the PERSISTED form. `stableStringify` keeps an undefined-valued
|
|
75
|
+
* key as `"k":null`, but the `JSON.stringify` that writes events.jsonl DROPS such
|
|
76
|
+
* keys — so without this round-trip the record-time hash (over the in-memory event,
|
|
77
|
+
* which can carry nested undefined like an absent dispatchId in worker-sandbox
|
|
78
|
+
* metadata) would never match the verify-time hash (over the parsed-from-disk
|
|
79
|
+
* event), false-failing legitimate worker events as `trust-audit-digest-mismatch`.
|
|
80
|
+
* Round-tripping makes record-time == verify-time. It is identity for events with
|
|
81
|
+
* no undefined fields, so existing intact chains hash unchanged. */
|
|
82
|
+
function computeEventHash(event) {
|
|
83
|
+
const { eventHash, ...rest } = event;
|
|
84
|
+
return (0, execution_backend_1.sha256)((0, telemetry_attestation_1.stableStringify)(JSON.parse(JSON.stringify(rest))));
|
|
85
|
+
}
|
|
86
|
+
/** The hash to chain the NEXT event to: the stored eventHash, or a recompute for
|
|
87
|
+
* a legacy event written before the chain existed. */
|
|
88
|
+
function chainHashOf(event) {
|
|
89
|
+
return event.eventHash || computeEventHash(event);
|
|
90
|
+
}
|
|
91
|
+
/** Read events in FILE (append) order, tolerating corrupt lines — one bad line
|
|
92
|
+
* must not brick the whole audit read surface (it is counted, not thrown). The
|
|
93
|
+
* chain links append order, so this is the order verification walks. */
|
|
94
|
+
function readEventsRaw(eventLogPath) {
|
|
95
|
+
if (!node_fs_1.default.existsSync(eventLogPath))
|
|
96
|
+
return { events: [], corruptLines: 0 };
|
|
97
|
+
let corruptLines = 0;
|
|
98
|
+
const events = [];
|
|
99
|
+
for (const line of node_fs_1.default.readFileSync(eventLogPath, "utf8").split(/\n/g)) {
|
|
100
|
+
const trimmed = line.trim();
|
|
101
|
+
if (!trimmed)
|
|
102
|
+
continue;
|
|
103
|
+
try {
|
|
104
|
+
events.push(JSON.parse(trimmed));
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
corruptLines += 1;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { events, corruptLines };
|
|
111
|
+
}
|
|
112
|
+
/** Re-prove the run's trust-audit chain: prevEventHash linkage (append order) +
|
|
113
|
+
* per-event hash recompute. A corrupt line, an edited event, or a removed event
|
|
114
|
+
* flips verified=false. Legacy events without a hash are reported as `unchained`
|
|
115
|
+
* (skipped), NOT treated as a forgery — they predate the chain. */
|
|
116
|
+
function verifyTrustAudit(run) {
|
|
117
|
+
const audit = ensureTrustAudit(run);
|
|
118
|
+
const { events, corruptLines } = readEventsRaw(audit.eventLogPath);
|
|
119
|
+
const checks = [];
|
|
120
|
+
let verified = corruptLines === 0;
|
|
121
|
+
if (corruptLines > 0)
|
|
122
|
+
checks.push({ name: "parse", pass: false, code: "trust-audit-corrupt-line" });
|
|
123
|
+
let chained = 0;
|
|
124
|
+
let unchained = 0;
|
|
125
|
+
let expectedPrev = trustAuditGenesis(run.id);
|
|
126
|
+
for (let i = 0; i < events.length; i++) {
|
|
127
|
+
const event = events[i];
|
|
128
|
+
const recomputed = computeEventHash(event);
|
|
129
|
+
if (event.eventHash === undefined) {
|
|
130
|
+
unchained += 1;
|
|
131
|
+
expectedPrev = recomputed; // advance the chain over legacy events
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
chained += 1;
|
|
135
|
+
if (event.eventHash !== recomputed) {
|
|
136
|
+
verified = false;
|
|
137
|
+
checks.push({ name: `event-hash[${i}]`, pass: false, code: "trust-audit-digest-mismatch" });
|
|
138
|
+
}
|
|
139
|
+
if (event.prevEventHash !== undefined && event.prevEventHash !== expectedPrev) {
|
|
140
|
+
verified = false;
|
|
141
|
+
checks.push({ name: `chain-link[${i}]`, pass: false, code: "trust-audit-chain-broken" });
|
|
142
|
+
}
|
|
143
|
+
expectedPrev = event.eventHash;
|
|
144
|
+
}
|
|
145
|
+
// A log with ANY chained event must have EVERY event chained: a single run is
|
|
146
|
+
// written by one code version, so it is all-chained (chain era) or all-legacy
|
|
147
|
+
// (pre-chain). An unchained (eventHash-less) line mixed into a chained log is a
|
|
148
|
+
// forgery attempt — drop the hash to be waved through as "legacy" — so it fails.
|
|
149
|
+
if (chained > 0 && unchained > 0) {
|
|
150
|
+
verified = false;
|
|
151
|
+
checks.push({ name: "unchained-events", pass: false, code: "trust-audit-unchained-event" });
|
|
152
|
+
}
|
|
153
|
+
return { present: events.length > 0, verified, eventCount: events.length, chained, unchained, corruptLines, checks };
|
|
154
|
+
}
|
|
155
|
+
// ---- Correlation-id fields (single source of truth, F12) -------------------
|
|
156
|
+
// These id fields are plain pass-throughs that flow input -> persisted event ->
|
|
157
|
+
// summary index unchanged. They were previously re-listed by hand in three
|
|
158
|
+
// places (recordTrustAuditEvent, the index writer, and partially in
|
|
159
|
+
// multi-agent-trust), so adding a new correlation id meant editing every list and
|
|
160
|
+
// silently dropping it from the index when one was missed. They now live in ONE
|
|
161
|
+
// array; both spread sites pick from it via pickCorrelationIds, so a new id is
|
|
162
|
+
// added in exactly one place.
|
|
163
|
+
//
|
|
164
|
+
// SERIALIZATION-PRESERVING by design: this is a plain key-copy with NO transform,
|
|
165
|
+
// so the persisted JSON for these keys is byte-identical to the old hand-spread
|
|
166
|
+
// (compact() still drops the undefined ones). The hash chain that binds these
|
|
167
|
+
// fields is therefore unaffected. Fields needing derivation/normalization
|
|
168
|
+
// (feedbackIds, sandboxProfileId, policyRef, multiAgentPolicyRef, normalizedPath,
|
|
169
|
+
// envVars, …) are intentionally NOT here — they keep their bespoke handling at the
|
|
170
|
+
// call site.
|
|
171
|
+
exports.CORRELATION_ID_FIELDS = [
|
|
172
|
+
"candidateId",
|
|
173
|
+
"scoreId",
|
|
174
|
+
"selectionId",
|
|
175
|
+
"commitId",
|
|
176
|
+
"multiAgentRunId",
|
|
177
|
+
"agentRoleId",
|
|
178
|
+
"agentGroupId",
|
|
179
|
+
"agentMembershipId",
|
|
180
|
+
"agentFanoutId",
|
|
181
|
+
"agentFaninId",
|
|
182
|
+
"blackboardId",
|
|
183
|
+
"blackboardTopicId",
|
|
184
|
+
"blackboardMessageId",
|
|
185
|
+
"blackboardContextId",
|
|
186
|
+
"blackboardArtifactRefId",
|
|
187
|
+
"blackboardSnapshotId",
|
|
188
|
+
"coordinatorDecisionId",
|
|
189
|
+
"topologyId",
|
|
190
|
+
"topologyRunId"
|
|
191
|
+
];
|
|
192
|
+
/** Copy exactly the correlation-id keys (and no others) from `source`, preserving
|
|
193
|
+
* each value verbatim (including `undefined`, which compact()/JSON.stringify drop
|
|
194
|
+
* identically to the prior per-key spread). Spread the result; do not transform. */
|
|
195
|
+
function pickCorrelationIds(source) {
|
|
196
|
+
const picked = {};
|
|
197
|
+
for (const field of exports.CORRELATION_ID_FIELDS)
|
|
198
|
+
picked[field] = source[field];
|
|
199
|
+
return picked;
|
|
200
|
+
}
|
|
201
|
+
// The summary index has always carried every correlation id EXCEPT scoreId (the
|
|
202
|
+
// per-candidate score lives in the candidates[] rows, not the flat event index).
|
|
203
|
+
// Pin that exception in ONE place so the index stays byte-identical while still
|
|
204
|
+
// inheriting any NEW id from CORRELATION_ID_FIELDS automatically.
|
|
205
|
+
const INDEX_OMITTED_CORRELATION_IDS = new Set(["scoreId"]);
|
|
206
|
+
/** Correlation ids for the summary index: the full pick minus the keys the index
|
|
207
|
+
* has historically omitted (see INDEX_OMITTED_CORRELATION_IDS). */
|
|
208
|
+
function indexCorrelationIds(event) {
|
|
209
|
+
const picked = pickCorrelationIds(event);
|
|
210
|
+
for (const field of INDEX_OMITTED_CORRELATION_IDS)
|
|
211
|
+
delete picked[field];
|
|
212
|
+
return picked;
|
|
213
|
+
}
|
|
26
214
|
function ensureTrustAudit(run) {
|
|
27
215
|
const auditDir = auditRoot(run);
|
|
28
216
|
node_fs_1.default.mkdirSync(auditDir, { recursive: true });
|
|
29
217
|
run.paths.auditDir = auditDir;
|
|
30
|
-
const audit = {
|
|
31
|
-
schemaVersion: exports.TRUST_AUDIT_SCHEMA_VERSION,
|
|
32
|
-
eventLogPath: node_path_1.default.join(auditDir, "events.jsonl"),
|
|
33
|
-
summaryPath: node_path_1.default.join(auditDir, "summary.json"),
|
|
34
|
-
indexPath: node_path_1.default.join(auditDir, "index.json")
|
|
35
|
-
};
|
|
218
|
+
const audit = { ...trustAuditPaths(run) };
|
|
36
219
|
run.audit = audit;
|
|
37
220
|
if (!node_fs_1.default.existsSync(audit.eventLogPath))
|
|
38
221
|
node_fs_1.default.writeFileSync(audit.eventLogPath, "", "utf8");
|
|
@@ -53,25 +236,11 @@ function recordTrustAuditEvent(run, input) {
|
|
|
53
236
|
taskId: input.taskId,
|
|
54
237
|
nodeId: input.nodeId,
|
|
55
238
|
feedbackIds: input.feedbackIds?.filter(Boolean).sort(),
|
|
56
|
-
candidateId
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
agentRoleId: input.agentRoleId,
|
|
62
|
-
agentGroupId: input.agentGroupId,
|
|
63
|
-
agentMembershipId: input.agentMembershipId,
|
|
64
|
-
agentFanoutId: input.agentFanoutId,
|
|
65
|
-
agentFaninId: input.agentFaninId,
|
|
66
|
-
blackboardId: input.blackboardId,
|
|
67
|
-
blackboardTopicId: input.blackboardTopicId,
|
|
68
|
-
blackboardMessageId: input.blackboardMessageId,
|
|
69
|
-
blackboardContextId: input.blackboardContextId,
|
|
70
|
-
blackboardArtifactRefId: input.blackboardArtifactRefId,
|
|
71
|
-
blackboardSnapshotId: input.blackboardSnapshotId,
|
|
72
|
-
coordinatorDecisionId: input.coordinatorDecisionId,
|
|
73
|
-
topologyId: input.topologyId,
|
|
74
|
-
topologyRunId: input.topologyRunId,
|
|
239
|
+
// Plain correlation-id pass-throughs (candidateId … topologyRunId) come from the
|
|
240
|
+
// single CORRELATION_ID_FIELDS list. Key order is preserved (the list is in the
|
|
241
|
+
// same order the keys used to be hand-written), so the persisted JSON — and thus
|
|
242
|
+
// the eventHash chain — is byte-identical.
|
|
243
|
+
...pickCorrelationIds(input),
|
|
75
244
|
sandboxProfileId: input.sandboxProfileId || input.policySnapshot?.id,
|
|
76
245
|
policyRef: input.policyRef || (input.policySnapshot?.id ? `run.sandboxProfiles.${input.policySnapshot.id}` : undefined),
|
|
77
246
|
multiAgentPolicyRef: input.policyRef,
|
|
@@ -90,6 +259,12 @@ function recordTrustAuditEvent(run, input) {
|
|
|
90
259
|
parentEventIds: unique(input.parentEventIds || []).sort(),
|
|
91
260
|
metadata: scrubMetadata(input.metadata || {})
|
|
92
261
|
});
|
|
262
|
+
// Tamper-evidence chain: link this event to the prior one (append order) and
|
|
263
|
+
// bind its content with eventHash BEFORE persisting. verifyTrustAudit recomputes
|
|
264
|
+
// both independently, so a later edit or removal is detectable.
|
|
265
|
+
const prior = readEventsRaw(audit.eventLogPath).events;
|
|
266
|
+
event.prevEventHash = prior.length ? chainHashOf(prior[prior.length - 1]) : trustAuditGenesis(run.id);
|
|
267
|
+
event.eventHash = computeEventHash(event);
|
|
93
268
|
// DURABLE append (v0.1.40 self-audit P1): the audit log is the one artifact
|
|
94
269
|
// whose loss breaks audit-completeness, so fsync it before returning — never a
|
|
95
270
|
// bare appendFileSync, which can drop the last event on power loss.
|
|
@@ -127,15 +302,7 @@ function recordHostAttestation(run, input) {
|
|
|
127
302
|
}
|
|
128
303
|
function listTrustAuditEvents(run) {
|
|
129
304
|
const audit = ensureTrustAudit(run);
|
|
130
|
-
|
|
131
|
-
return [];
|
|
132
|
-
return node_fs_1.default
|
|
133
|
-
.readFileSync(audit.eventLogPath, "utf8")
|
|
134
|
-
.split(/\n/g)
|
|
135
|
-
.map((line) => line.trim())
|
|
136
|
-
.filter(Boolean)
|
|
137
|
-
.map((line) => JSON.parse(line))
|
|
138
|
-
.sort(compareEvents);
|
|
305
|
+
return readEventsRaw(audit.eventLogPath).events.sort(compareEvents);
|
|
139
306
|
}
|
|
140
307
|
/** Search audit events by kind, worker, or candidate (v0.1.65).
|
|
141
308
|
* Filters are AND-ed; empty filters match all. */
|
|
@@ -159,6 +326,7 @@ function summarizeTrustAudit(run) {
|
|
|
159
326
|
runId: run.id,
|
|
160
327
|
generatedAt: new Date().toISOString(),
|
|
161
328
|
eventCount: events.length,
|
|
329
|
+
integrity: verifyTrustAudit(run),
|
|
162
330
|
eventLogPath: audit.eventLogPath,
|
|
163
331
|
indexPath: audit.indexPath,
|
|
164
332
|
summaryPath: audit.summaryPath,
|
|
@@ -228,24 +396,11 @@ function summarizeTrustAudit(run) {
|
|
|
228
396
|
source: event.source,
|
|
229
397
|
workerId: event.workerId,
|
|
230
398
|
taskId: event.taskId,
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
agentGroupId: event.agentGroupId,
|
|
237
|
-
agentMembershipId: event.agentMembershipId,
|
|
238
|
-
agentFanoutId: event.agentFanoutId,
|
|
239
|
-
agentFaninId: event.agentFaninId,
|
|
240
|
-
blackboardId: event.blackboardId,
|
|
241
|
-
blackboardTopicId: event.blackboardTopicId,
|
|
242
|
-
blackboardMessageId: event.blackboardMessageId,
|
|
243
|
-
blackboardContextId: event.blackboardContextId,
|
|
244
|
-
blackboardArtifactRefId: event.blackboardArtifactRefId,
|
|
245
|
-
blackboardSnapshotId: event.blackboardSnapshotId,
|
|
246
|
-
coordinatorDecisionId: event.coordinatorDecisionId,
|
|
247
|
-
topologyId: event.topologyId,
|
|
248
|
-
topologyRunId: event.topologyRunId,
|
|
399
|
+
// Same single CORRELATION_ID_FIELDS list the record path uses, so the index
|
|
400
|
+
// can never silently omit a correlation id that the event carries. (The index
|
|
401
|
+
// historically omitted scoreId; that is preserved below by deleting it after
|
|
402
|
+
// the pick, keeping this writer's output byte-identical.)
|
|
403
|
+
...indexCorrelationIds(event),
|
|
249
404
|
sandboxProfileId: event.sandboxProfileId,
|
|
250
405
|
policyRef: event.policyRef,
|
|
251
406
|
multiAgentPolicyRef: event.multiAgentPolicyRef
|
|
@@ -255,12 +410,7 @@ function summarizeTrustAudit(run) {
|
|
|
255
410
|
return summary;
|
|
256
411
|
}
|
|
257
412
|
function refreshTrustAudit(run) {
|
|
258
|
-
const audit = {
|
|
259
|
-
schemaVersion: exports.TRUST_AUDIT_SCHEMA_VERSION,
|
|
260
|
-
eventLogPath: node_path_1.default.join(auditRoot(run), "events.jsonl"),
|
|
261
|
-
summaryPath: node_path_1.default.join(auditRoot(run), "summary.json"),
|
|
262
|
-
indexPath: node_path_1.default.join(auditRoot(run), "index.json")
|
|
263
|
-
};
|
|
413
|
+
const audit = { ...trustAuditPaths(run) };
|
|
264
414
|
node_fs_1.default.mkdirSync(node_path_1.default.dirname(audit.eventLogPath), { recursive: true });
|
|
265
415
|
if (!node_fs_1.default.existsSync(audit.eventLogPath))
|
|
266
416
|
node_fs_1.default.writeFileSync(audit.eventLogPath, "", "utf8");
|
|
@@ -365,15 +515,7 @@ function auditRoot(run) {
|
|
|
365
515
|
return run.paths.auditDir || node_path_1.default.join(run.paths.runDir, "audit");
|
|
366
516
|
}
|
|
367
517
|
function readEvents(eventLogPath) {
|
|
368
|
-
|
|
369
|
-
return [];
|
|
370
|
-
return node_fs_1.default
|
|
371
|
-
.readFileSync(eventLogPath, "utf8")
|
|
372
|
-
.split(/\n/g)
|
|
373
|
-
.map((line) => line.trim())
|
|
374
|
-
.filter(Boolean)
|
|
375
|
-
.map((line) => JSON.parse(line))
|
|
376
|
-
.sort(compareEvents);
|
|
518
|
+
return readEventsRaw(eventLogPath).events.sort(compareEvents);
|
|
377
519
|
}
|
|
378
520
|
function workerRows(events, run) {
|
|
379
521
|
const workerIds = unique([...(run.workers || []).map((worker) => worker.id), ...events.map((event) => event.workerId || "")]).sort();
|
|
@@ -419,9 +561,11 @@ function commitRows(events, run) {
|
|
|
419
561
|
});
|
|
420
562
|
}
|
|
421
563
|
function createEventId(run, kind) {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
564
|
+
// Deterministic (FreeBSD-audit L12/L13): chain-local sequence (event-log length),
|
|
565
|
+
// no wall-clock stamp — event.id is bound into the eventHash chain (computeEventHash),
|
|
566
|
+
// so a stable id keeps the chain reproducible on replay.
|
|
567
|
+
const count = readEvents(trustAuditPaths(run).eventLogPath).length + 1;
|
|
568
|
+
return `audit-${(0, state_1.safeFileName)(kind)}-${String(count).padStart(4, "0")}`;
|
|
425
569
|
}
|
|
426
570
|
function redactPolicy(policy) {
|
|
427
571
|
if (!policy)
|