cool-workflow 0.1.78

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.
Files changed (193) hide show
  1. package/.claude-plugin/plugin.json +20 -0
  2. package/.codex-plugin/mcp.json +10 -0
  3. package/.codex-plugin/plugin.json +38 -0
  4. package/.mcp.json +10 -0
  5. package/LICENSE +24 -0
  6. package/README.md +638 -0
  7. package/apps/architecture-review/app.json +51 -0
  8. package/apps/architecture-review/workflow.js +116 -0
  9. package/apps/end-to-end-golden-path/app.json +30 -0
  10. package/apps/end-to-end-golden-path/workflow.js +33 -0
  11. package/apps/pr-review-fix-ci/app.json +59 -0
  12. package/apps/pr-review-fix-ci/workflow.js +90 -0
  13. package/apps/release-cut/app.json +54 -0
  14. package/apps/release-cut/workflow.js +82 -0
  15. package/apps/research-synthesis/app.json +50 -0
  16. package/apps/research-synthesis/workflow.js +76 -0
  17. package/apps/workflow-app-framework-demo/app.json +29 -0
  18. package/apps/workflow-app-framework-demo/workflow.js +44 -0
  19. package/dist/agent-config.js +223 -0
  20. package/dist/candidate-scoring.js +715 -0
  21. package/dist/capability-core.js +630 -0
  22. package/dist/capability-dispatcher.js +86 -0
  23. package/dist/capability-registry.js +523 -0
  24. package/dist/cli.js +1276 -0
  25. package/dist/collaboration.js +727 -0
  26. package/dist/commit.js +570 -0
  27. package/dist/contract-migration.js +234 -0
  28. package/dist/coordinator.js +1163 -0
  29. package/dist/daemon.js +44 -0
  30. package/dist/dispatch.js +201 -0
  31. package/dist/drive.js +503 -0
  32. package/dist/error-feedback.js +415 -0
  33. package/dist/evidence-grounding.js +179 -0
  34. package/dist/evidence-reasoning.js +733 -0
  35. package/dist/execution-backend.js +1279 -0
  36. package/dist/harness.js +61 -0
  37. package/dist/mcp-server.js +1615 -0
  38. package/dist/multi-agent-eval.js +857 -0
  39. package/dist/multi-agent-host.js +764 -0
  40. package/dist/multi-agent-operator-ux.js +537 -0
  41. package/dist/multi-agent-trust.js +366 -0
  42. package/dist/multi-agent.js +1173 -0
  43. package/dist/node-snapshot.js +270 -0
  44. package/dist/observability.js +922 -0
  45. package/dist/operator-ux.js +971 -0
  46. package/dist/orchestrator/audit-operations.js +182 -0
  47. package/dist/orchestrator/candidate-operations.js +117 -0
  48. package/dist/orchestrator/cli-options.js +288 -0
  49. package/dist/orchestrator/collaboration-operations.js +86 -0
  50. package/dist/orchestrator/feedback-operations.js +81 -0
  51. package/dist/orchestrator/host-operations.js +78 -0
  52. package/dist/orchestrator/lifecycle-operations.js +462 -0
  53. package/dist/orchestrator/migration-operations.js +44 -0
  54. package/dist/orchestrator/multi-agent-operations.js +362 -0
  55. package/dist/orchestrator/report.js +369 -0
  56. package/dist/orchestrator/topology-operations.js +84 -0
  57. package/dist/orchestrator.js +874 -0
  58. package/dist/pipeline-contract.js +92 -0
  59. package/dist/pipeline-runner.js +285 -0
  60. package/dist/reclamation.js +882 -0
  61. package/dist/result-normalize.js +194 -0
  62. package/dist/run-export.js +64 -0
  63. package/dist/run-registry.js +1347 -0
  64. package/dist/run-state-schema.js +67 -0
  65. package/dist/sandbox-profile.js +471 -0
  66. package/dist/scheduler.js +266 -0
  67. package/dist/scheduling.js +184 -0
  68. package/dist/schema-validate.js +98 -0
  69. package/dist/state-explosion.js +1213 -0
  70. package/dist/state-migrations.js +463 -0
  71. package/dist/state-node.js +301 -0
  72. package/dist/state.js +308 -0
  73. package/dist/telemetry-attestation.js +156 -0
  74. package/dist/telemetry-ledger.js +145 -0
  75. package/dist/topology.js +527 -0
  76. package/dist/triggers.js +159 -0
  77. package/dist/trust-audit.js +475 -0
  78. package/dist/types/blackboard.js +2 -0
  79. package/dist/types/boundary.js +29 -0
  80. package/dist/types/candidate.js +2 -0
  81. package/dist/types/collaboration.js +2 -0
  82. package/dist/types/core.js +2 -0
  83. package/dist/types/drive.js +10 -0
  84. package/dist/types/error-feedback.js +2 -0
  85. package/dist/types/evidence-reasoning.js +2 -0
  86. package/dist/types/execution-backend.js +2 -0
  87. package/dist/types/multi-agent.js +2 -0
  88. package/dist/types/observability.js +2 -0
  89. package/dist/types/pipeline.js +2 -0
  90. package/dist/types/reclamation.js +8 -0
  91. package/dist/types/result.js +2 -0
  92. package/dist/types/run-registry.js +2 -0
  93. package/dist/types/run.js +2 -0
  94. package/dist/types/sandbox.js +2 -0
  95. package/dist/types/schedule.js +2 -0
  96. package/dist/types/state-node.js +2 -0
  97. package/dist/types/topology.js +2 -0
  98. package/dist/types/trust.js +2 -0
  99. package/dist/types/workbench.js +2 -0
  100. package/dist/types/worker.js +2 -0
  101. package/dist/types/workflow-app.js +2 -0
  102. package/dist/types.js +43 -0
  103. package/dist/verifier-registry.js +46 -0
  104. package/dist/verifier.js +78 -0
  105. package/dist/version.js +8 -0
  106. package/dist/workbench-host.js +172 -0
  107. package/dist/workbench.js +190 -0
  108. package/dist/worker-isolation.js +1028 -0
  109. package/dist/workflow-api.js +98 -0
  110. package/dist/workflow-app-framework.js +626 -0
  111. package/docs/agent-delegation-drive.7.md +190 -0
  112. package/docs/agent-framework.md +176 -0
  113. package/docs/candidate-scoring.7.md +106 -0
  114. package/docs/canonical-workflow-apps.7.md +137 -0
  115. package/docs/capability-topology-registry.7.md +168 -0
  116. package/docs/cli-mcp-parity.7.md +373 -0
  117. package/docs/contract-migration-tooling.7.md +123 -0
  118. package/docs/control-plane-scheduling.7.md +110 -0
  119. package/docs/coordinator-blackboard.7.md +183 -0
  120. package/docs/dogfood/architecture-review-cool-workflow.md +16 -0
  121. package/docs/dogfood-one-real-repo.7.md +168 -0
  122. package/docs/durable-state-and-locking.7.md +107 -0
  123. package/docs/end-to-end-golden-path.7.md +117 -0
  124. package/docs/error-feedback.7.md +153 -0
  125. package/docs/evidence-adoption-reasoning-chain.7.md +270 -0
  126. package/docs/execution-backends.7.md +300 -0
  127. package/docs/getting-started.md +99 -0
  128. package/docs/index.md +41 -0
  129. package/docs/mcp-app-surface.7.md +235 -0
  130. package/docs/multi-agent-cli-mcp-surface.7.md +265 -0
  131. package/docs/multi-agent-eval-replay-harness.7.md +302 -0
  132. package/docs/multi-agent-operator-ux.7.md +314 -0
  133. package/docs/multi-agent-runtime-core.7.md +231 -0
  134. package/docs/multi-agent-topologies.7.md +103 -0
  135. package/docs/multi-agent-trust-policy-audit.7.md +154 -0
  136. package/docs/node-snapshot-diff-replay.7.md +135 -0
  137. package/docs/observability-cost-accounting.7.md +194 -0
  138. package/docs/operator-ux.7.md +180 -0
  139. package/docs/pipeline-runner.7.md +136 -0
  140. package/docs/project-index.md +261 -0
  141. package/docs/real-execution-backends.7.md +142 -0
  142. package/docs/release-and-migration.7.md +280 -0
  143. package/docs/release-tooling.7.md +159 -0
  144. package/docs/routines.md +48 -0
  145. package/docs/run-registry-control-plane.7.md +312 -0
  146. package/docs/run-retention-reclamation.7.md +191 -0
  147. package/docs/sandbox-profiles.7.md +137 -0
  148. package/docs/scheduled-tasks.md +80 -0
  149. package/docs/security-trust-hardening.7.md +117 -0
  150. package/docs/state-explosion-management.7.md +264 -0
  151. package/docs/state-node.7.md +96 -0
  152. package/docs/team-collaboration.7.md +207 -0
  153. package/docs/unix-principles.md +192 -0
  154. package/docs/verifier-gated-commit.7.md +140 -0
  155. package/docs/web-desktop-workbench.7.md +215 -0
  156. package/docs/worker-isolation.7.md +167 -0
  157. package/docs/workflow-app-framework.7.md +274 -0
  158. package/manifest/README.md +43 -0
  159. package/manifest/plugin.manifest.json +316 -0
  160. package/manifest/pricing.policy.json +14 -0
  161. package/package.json +79 -0
  162. package/scripts/agents/claude-p-agent.js +104 -0
  163. package/scripts/agents/claude-p-agent.sh +9 -0
  164. package/scripts/agents/cw-attest-keygen.js +55 -0
  165. package/scripts/agents/cw-attest-wrap.js +143 -0
  166. package/scripts/block-unapproved-tag.sh +39 -0
  167. package/scripts/bump-version.js +249 -0
  168. package/scripts/canonical-apps.js +171 -0
  169. package/scripts/cw.js +4 -0
  170. package/scripts/dist-drift-check.js +79 -0
  171. package/scripts/dogfood-architecture-review.js +237 -0
  172. package/scripts/dogfood-release.js +624 -0
  173. package/scripts/forward-ref-docs.js +73 -0
  174. package/scripts/gen-manifests.js +232 -0
  175. package/scripts/golden-path.js +300 -0
  176. package/scripts/mcp-server.js +4 -0
  177. package/scripts/new-feature.js +121 -0
  178. package/scripts/parity-check.js +213 -0
  179. package/scripts/release-check.js +118 -0
  180. package/scripts/release-flow.js +272 -0
  181. package/scripts/release-gate.sh +85 -0
  182. package/scripts/sync-project-index.js +387 -0
  183. package/scripts/validate-run-state-schema.js +126 -0
  184. package/scripts/verify-container-selfref.js +64 -0
  185. package/scripts/version-sync-check.js +237 -0
  186. package/skills/cool-workflow/SKILL.md +162 -0
  187. package/skills/cool-workflow/references/commands.md +282 -0
  188. package/tsconfig.json +16 -0
  189. package/ui/workbench/app.css +76 -0
  190. package/ui/workbench/app.js +159 -0
  191. package/ui/workbench/index.html +32 -0
  192. package/workflows/architecture-review.workflow.js +84 -0
  193. package/workflows/research-synthesis.workflow.js +47 -0
@@ -0,0 +1,1163 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BLACKBOARD_SCHEMA_VERSION = void 0;
7
+ exports.ensureBlackboardState = ensureBlackboardState;
8
+ exports.resolveBlackboard = resolveBlackboard;
9
+ exports.createBlackboardTopic = createBlackboardTopic;
10
+ exports.postBlackboardMessage = postBlackboardMessage;
11
+ exports.putBlackboardContext = putBlackboardContext;
12
+ exports.addBlackboardArtifact = addBlackboardArtifact;
13
+ exports.createBlackboardSnapshot = createBlackboardSnapshot;
14
+ exports.recordCoordinatorDecision = recordCoordinatorDecision;
15
+ exports.summarizeBlackboard = summarizeBlackboard;
16
+ exports.listBlackboardMessages = listBlackboardMessages;
17
+ exports.listBlackboardArtifacts = listBlackboardArtifacts;
18
+ exports.buildBlackboardGraph = buildBlackboardGraph;
19
+ exports.persistBlackboardState = persistBlackboardState;
20
+ exports.bridgeStateArtifactToBlackboard = bridgeStateArtifactToBlackboard;
21
+ exports.evidenceFromArtifactRef = evidenceFromArtifactRef;
22
+ const node_crypto_1 = __importDefault(require("node:crypto"));
23
+ const node_fs_1 = __importDefault(require("node:fs"));
24
+ const node_path_1 = __importDefault(require("node:path"));
25
+ const pipeline_contract_1 = require("./pipeline-contract");
26
+ const state_1 = require("./state");
27
+ const state_node_1 = require("./state-node");
28
+ const multi_agent_1 = require("./multi-agent");
29
+ const trust_audit_1 = require("./trust-audit");
30
+ const multi_agent_trust_1 = require("./multi-agent-trust");
31
+ exports.BLACKBOARD_SCHEMA_VERSION = 1;
32
+ function ensureBlackboardState(run) {
33
+ run.paths.blackboardDir = blackboardRoot(run);
34
+ node_fs_1.default.mkdirSync(run.paths.blackboardDir, { recursive: true });
35
+ for (const dir of ["topics", "contexts", "artifacts", "snapshots", "decisions"]) {
36
+ node_fs_1.default.mkdirSync(node_path_1.default.join(run.paths.blackboardDir, dir), { recursive: true });
37
+ }
38
+ if (!run.blackboard) {
39
+ run.blackboard = emptyState();
40
+ }
41
+ run.blackboard.schemaVersion = exports.BLACKBOARD_SCHEMA_VERSION;
42
+ run.blackboard.boards = run.blackboard.boards || [];
43
+ run.blackboard.topics = run.blackboard.topics || [];
44
+ run.blackboard.messages = run.blackboard.messages || [];
45
+ run.blackboard.contexts = run.blackboard.contexts || [];
46
+ run.blackboard.artifacts = run.blackboard.artifacts || [];
47
+ run.blackboard.snapshots = run.blackboard.snapshots || [];
48
+ run.blackboard.decisions = run.blackboard.decisions || [];
49
+ return run.blackboard;
50
+ }
51
+ function resolveBlackboard(run, input = {}) {
52
+ const state = ensureBlackboardState(run);
53
+ const existing = input.id
54
+ ? state.boards.find((board) => board.id === input.id)
55
+ : input.multiAgentRunId
56
+ ? state.boards.find((board) => board.links.multiAgentRunId === input.multiAgentRunId)
57
+ : state.boards[0];
58
+ if (existing) {
59
+ linkMultiAgent(run, existing.id, existing.topicIds, input);
60
+ touch(existing);
61
+ persistBlackboardState(run);
62
+ return existing;
63
+ }
64
+ const id = input.id || createId("bb");
65
+ assertUnique(state.boards, id, "Blackboard");
66
+ const now = timestamp();
67
+ const author = normalizeAuthor(input.author, "runtime");
68
+ const scope = normalizeScope(input.scope, input.multiAgentRunId ? { kind: "multi-agent-run", id: input.multiAgentRunId } : { kind: "run", id: run.id });
69
+ const board = {
70
+ schemaVersion: exports.BLACKBOARD_SCHEMA_VERSION,
71
+ id,
72
+ runId: run.id,
73
+ createdAt: now,
74
+ updatedAt: now,
75
+ author,
76
+ scope,
77
+ status: "active",
78
+ parentIds: [],
79
+ tags: sortTags(input.tags),
80
+ title: input.title || id,
81
+ topicIds: [],
82
+ messageCount: 0,
83
+ contextIds: [],
84
+ artifactRefIds: [],
85
+ snapshotIds: [],
86
+ decisionIds: [],
87
+ links: compactLinks(run, {
88
+ multiAgentRunId: input.multiAgentRunId,
89
+ agentGroupId: input.groupId,
90
+ agentRoleId: input.roleId,
91
+ agentMembershipId: input.membershipId
92
+ }),
93
+ paths: boardPaths(run),
94
+ metadata: scrub(input.metadata)
95
+ };
96
+ linkMultiAgent(run, board.id, [], input);
97
+ state.boards.push(board);
98
+ appendBlackboardNode(run, "blackboard", board.id, "running", board.title, board.paths.index);
99
+ const audit = (0, trust_audit_1.recordTrustAuditEvent)(run, {
100
+ kind: "blackboard.create",
101
+ decision: "recorded",
102
+ source: "runtime-derived",
103
+ actor: author.id,
104
+ multiAgentRunId: input.multiAgentRunId,
105
+ agentGroupId: input.groupId,
106
+ agentRoleId: input.roleId,
107
+ agentMembershipId: input.membershipId,
108
+ blackboardId: board.id,
109
+ metadata: { scope, tags: board.tags }
110
+ });
111
+ board.links.auditEventIds = [audit.id];
112
+ persistBlackboardState(run);
113
+ return board;
114
+ }
115
+ function createBlackboardTopic(run, input) {
116
+ const board = resolveBlackboard(run, { id: input.blackboardId });
117
+ const state = ensureBlackboardState(run);
118
+ const id = input.id || createId("topic");
119
+ assertUnique(state.topics, id, "BlackboardTopic");
120
+ const topicLinks = compactLinks(run, { ...board.links, ...roleLinkFromAuthor(input.author), ...input.scope });
121
+ const now = timestamp();
122
+ const topic = {
123
+ ...base(run, board.id, id, input.author, input.scope, "open", input.tags, input.metadata),
124
+ createdAt: now,
125
+ updatedAt: now,
126
+ title: input.title,
127
+ description: input.description,
128
+ messageIds: [],
129
+ contextIds: [],
130
+ artifactRefIds: [],
131
+ links: topicLinks
132
+ };
133
+ state.topics.push(topic);
134
+ board.topicIds = unique([...board.topicIds, topic.id]);
135
+ touch(board);
136
+ linkMultiAgent(run, board.id, [topic.id], board.links);
137
+ appendBlackboardNode(run, "blackboard-topic", topic.id, "running", topic.title, recordPath(run, "topics", topic.id), [`${run.id}:blackboard:${board.id}`]);
138
+ const audit = (0, trust_audit_1.recordTrustAuditEvent)(run, {
139
+ kind: "blackboard.topic",
140
+ decision: "recorded",
141
+ source: "operator-recorded",
142
+ actor: topic.author.id,
143
+ blackboardId: board.id,
144
+ blackboardTopicId: topic.id,
145
+ multiAgentRunId: topic.links.multiAgentRunId,
146
+ agentGroupId: topic.links.agentGroupId,
147
+ agentRoleId: topic.links.agentRoleId,
148
+ agentMembershipId: topic.links.agentMembershipId,
149
+ metadata: { title: topic.title, tags: topic.tags }
150
+ });
151
+ topic.links.auditEventIds = unique([...(topic.links.auditEventIds || []), audit.id]);
152
+ (0, multi_agent_trust_1.recordBlackboardWriteAudit)(run, {
153
+ operation: "topic",
154
+ status: topic.status,
155
+ actor: topic.author,
156
+ blackboardId: board.id,
157
+ blackboardTopicId: topic.id,
158
+ multiAgentRunId: topic.links.multiAgentRunId,
159
+ agentGroupId: topic.links.agentGroupId,
160
+ agentRoleId: topic.links.agentRoleId,
161
+ agentMembershipId: topic.links.agentMembershipId,
162
+ parentEventIds: [audit.id],
163
+ metadata: { title: topic.title }
164
+ });
165
+ persistBlackboardState(run);
166
+ return topic;
167
+ }
168
+ function postBlackboardMessage(run, input) {
169
+ const state = ensureBlackboardState(run);
170
+ const topic = requireTopic(run, input.topicId);
171
+ const board = requireBoard(run, input.blackboardId || topic.blackboardId);
172
+ if (input.replyToId && !state.messages.some((message) => message.id === input.replyToId)) {
173
+ throw new Error(`Unknown parent BlackboardMessage id: ${input.replyToId}`);
174
+ }
175
+ if (!input.body.trim())
176
+ throw new Error("Blackboard message body is required");
177
+ const id = input.id || createId("msg");
178
+ assertUnique(state.messages, id, "BlackboardMessage");
179
+ const author = normalizeAuthor(input.author, "operator");
180
+ const links = compactLinks(run, { ...topic.links, ...roleLinkFromAuthor(author), ...(input.links || {}), evidenceRefs: input.evidenceRefs, auditEventIds: input.auditEventIds });
181
+ const enforcePolicy = shouldEnforcePolicy(author, links);
182
+ const permission = enforcePolicy
183
+ ? (0, multi_agent_trust_1.assertMultiAgentActionAllowed)(run, {
184
+ operation: "message",
185
+ actor: author,
186
+ multiAgentRunId: links.multiAgentRunId,
187
+ agentRoleId: links.agentRoleId,
188
+ agentGroupId: links.agentGroupId,
189
+ agentMembershipId: links.agentMembershipId,
190
+ agentFanoutId: links.agentFanoutId,
191
+ agentFaninId: links.agentFaninId,
192
+ blackboardId: board.id,
193
+ blackboardTopicId: topic.id,
194
+ blackboardMessageId: id,
195
+ evidenceRefs: input.evidenceRefs || []
196
+ })
197
+ : undefined;
198
+ const message = {
199
+ ...base(run, board.id, id, author, input.scope, "active", input.tags, input.metadata),
200
+ topicId: topic.id,
201
+ body: input.body,
202
+ visibility: input.visibility || "public",
203
+ replyToId: input.replyToId,
204
+ parentIds: unique([...(input.parentIds || []), ...(input.replyToId ? [input.replyToId] : [])]),
205
+ linkedEvidenceRefs: unique(input.evidenceRefs || []),
206
+ linkedArtifactRefIds: requireArtifactRefs(run, input.artifactRefIds || []),
207
+ linkedAuditEventIds: unique(input.auditEventIds || []),
208
+ links,
209
+ provenance: {
210
+ schemaVersion: 1,
211
+ authorKind: author.kind,
212
+ authorId: author.id,
213
+ multiAgentRunId: links.multiAgentRunId,
214
+ agentRoleId: links.agentRoleId,
215
+ agentGroupId: links.agentGroupId,
216
+ agentMembershipId: links.agentMembershipId,
217
+ agentFanoutId: links.agentFanoutId,
218
+ agentFaninId: links.agentFaninId,
219
+ workerId: links.workerId || (author.kind === "worker" ? author.id : undefined),
220
+ source: (0, multi_agent_trust_1.sourceForActor)(author),
221
+ linkedEvidenceRefs: unique(input.evidenceRefs || []),
222
+ linkedAuditEventIds: unique(input.auditEventIds || []),
223
+ parentMessageIds: unique([...(input.parentIds || []), ...(input.replyToId ? [input.replyToId] : [])]),
224
+ topicScope: topic.id,
225
+ bodyHash: (0, multi_agent_trust_1.hashText)(input.body),
226
+ locator: `${board.id}/messages/${id}`
227
+ }
228
+ };
229
+ state.messages.push(message);
230
+ topic.messageIds = unique([...topic.messageIds, message.id]);
231
+ board.messageCount = state.messages.filter((entry) => entry.blackboardId === board.id).length;
232
+ touch(topic);
233
+ touch(board);
234
+ appendBlackboardNode(run, "blackboard-message", message.id, "completed", truncate(message.body), messagesPath(run), [`${run.id}:blackboard:topic:${topic.id}`]);
235
+ const audit = (0, trust_audit_1.recordTrustAuditEvent)(run, {
236
+ kind: "blackboard.message",
237
+ decision: "recorded",
238
+ source: sourceForAuthor(message.author),
239
+ actor: message.author.id,
240
+ blackboardId: board.id,
241
+ blackboardTopicId: topic.id,
242
+ blackboardMessageId: message.id,
243
+ workerId: message.links.workerId || (message.author.kind === "worker" ? message.author.id : undefined),
244
+ taskId: message.links.taskId,
245
+ multiAgentRunId: message.links.multiAgentRunId,
246
+ agentGroupId: message.links.agentGroupId,
247
+ agentRoleId: message.links.agentRoleId,
248
+ agentMembershipId: message.links.agentMembershipId,
249
+ evidenceRefs: message.linkedEvidenceRefs,
250
+ parentEventIds: message.linkedAuditEventIds,
251
+ metadata: { visibility: message.visibility }
252
+ });
253
+ const writeAudit = (0, multi_agent_trust_1.recordBlackboardWriteAudit)(run, {
254
+ operation: "message",
255
+ status: message.status,
256
+ actor: message.author,
257
+ multiAgentRunId: message.links.multiAgentRunId,
258
+ agentGroupId: message.links.agentGroupId,
259
+ agentRoleId: message.links.agentRoleId,
260
+ agentMembershipId: message.links.agentMembershipId,
261
+ agentFanoutId: message.links.agentFanoutId,
262
+ agentFaninId: message.links.agentFaninId,
263
+ blackboardId: board.id,
264
+ blackboardTopicId: topic.id,
265
+ blackboardMessageId: message.id,
266
+ evidenceRefs: message.linkedEvidenceRefs,
267
+ parentEventIds: unique([...(permission ? [permission.event.id] : []), audit.id]),
268
+ policyRef: permission?.policyRef,
269
+ metadata: { visibility: message.visibility }
270
+ });
271
+ const provenanceAudit = (0, multi_agent_trust_1.recordMessageProvenanceAudit)(run, {
272
+ messageId: message.id,
273
+ topicId: topic.id,
274
+ blackboardId: board.id,
275
+ actor: message.author,
276
+ body: message.body,
277
+ multiAgentRunId: message.links.multiAgentRunId,
278
+ agentRoleId: message.links.agentRoleId,
279
+ agentGroupId: message.links.agentGroupId,
280
+ agentMembershipId: message.links.agentMembershipId,
281
+ workerId: message.links.workerId,
282
+ evidenceRefs: message.linkedEvidenceRefs,
283
+ parentMessageIds: message.parentIds,
284
+ parentEventIds: [audit.id, writeAudit.id],
285
+ policyRef: permission?.policyRef
286
+ });
287
+ if (message.metadata?.judgeRationale || message.tags.includes("judge-rationale")) {
288
+ const rationaleAudit = (0, multi_agent_trust_1.recordJudgeRationaleAudit)(run, {
289
+ kind: "judge.rationale",
290
+ actor: message.author,
291
+ multiAgentRunId: message.links.multiAgentRunId,
292
+ agentRoleId: message.links.agentRoleId,
293
+ agentGroupId: message.links.agentGroupId,
294
+ agentMembershipId: message.links.agentMembershipId,
295
+ blackboardId: board.id,
296
+ blackboardTopicId: topic.id,
297
+ blackboardMessageId: message.id,
298
+ evidenceRefs: message.linkedEvidenceRefs,
299
+ rationale: message.body,
300
+ policyRef: permission?.policyRef,
301
+ parentEventIds: [audit.id, writeAudit.id, provenanceAudit.id]
302
+ });
303
+ message.linkedAuditEventIds = unique([...message.linkedAuditEventIds, rationaleAudit.id]);
304
+ }
305
+ message.linkedAuditEventIds = unique([...message.linkedAuditEventIds, audit.id, writeAudit.id, provenanceAudit.id]);
306
+ message.links.auditEventIds = unique([...(message.links.auditEventIds || []), audit.id, writeAudit.id, provenanceAudit.id]);
307
+ if (message.provenance) {
308
+ message.provenance.linkedAuditEventIds = unique([...message.provenance.linkedAuditEventIds, audit.id, writeAudit.id, provenanceAudit.id]);
309
+ }
310
+ persistBlackboardState(run);
311
+ return message;
312
+ }
313
+ function putBlackboardContext(run, input) {
314
+ const state = ensureBlackboardState(run);
315
+ const topic = requireTopic(run, input.topicId);
316
+ const board = requireBoard(run, input.blackboardId || topic.blackboardId);
317
+ const key = input.key || input.kind;
318
+ const id = input.id || createId("ctx");
319
+ assertUnique(state.contexts, id, "BlackboardContext");
320
+ const author = normalizeAuthor(input.author, "operator");
321
+ const links = compactLinks(run, { ...topic.links, ...roleLinkFromAuthor(author), ...(input.links || {}), evidenceRefs: input.evidenceRefs });
322
+ const permission = shouldEnforcePolicy(author, links)
323
+ ? (0, multi_agent_trust_1.assertMultiAgentActionAllowed)(run, {
324
+ operation: "context",
325
+ actor: author,
326
+ multiAgentRunId: links.multiAgentRunId,
327
+ agentRoleId: links.agentRoleId,
328
+ agentGroupId: links.agentGroupId,
329
+ agentMembershipId: links.agentMembershipId,
330
+ blackboardId: board.id,
331
+ blackboardTopicId: topic.id,
332
+ blackboardContextId: id,
333
+ evidenceRefs: input.evidenceRefs || []
334
+ })
335
+ : undefined;
336
+ const conflicts = state.contexts.filter((context) => context.blackboardId === board.id &&
337
+ context.topicId === topic.id &&
338
+ context.kind === input.kind &&
339
+ context.key === key &&
340
+ context.status !== "superseded" &&
341
+ !input.supersedesContextIds?.includes(context.id) &&
342
+ context.value !== input.value);
343
+ for (const supersededId of input.supersedesContextIds || []) {
344
+ const superseded = requireContext(run, supersededId);
345
+ superseded.status = "superseded";
346
+ superseded.supersededByContextId = id;
347
+ touch(superseded);
348
+ }
349
+ const status = conflicts.length ? "conflicting" : input.kind === "question" ? "open" : "active";
350
+ const context = {
351
+ ...base(run, board.id, id, author, input.scope, status, input.tags, input.metadata),
352
+ topicId: topic.id,
353
+ kind: input.kind,
354
+ key,
355
+ value: input.value,
356
+ supersedesContextIds: unique(input.supersedesContextIds || []),
357
+ conflictingContextIds: conflicts.map((entry) => entry.id),
358
+ evidenceRefs: unique(input.evidenceRefs || []),
359
+ artifactRefIds: requireArtifactRefs(run, input.artifactRefIds || []),
360
+ links
361
+ };
362
+ for (const conflict of conflicts) {
363
+ conflict.status = "conflicting";
364
+ conflict.conflictingContextIds = unique([...conflict.conflictingContextIds, context.id]);
365
+ touch(conflict);
366
+ }
367
+ state.contexts.push(context);
368
+ topic.contextIds = unique([...topic.contextIds, context.id]);
369
+ board.contextIds = unique([...board.contextIds, context.id]);
370
+ touch(topic);
371
+ touch(board);
372
+ const decision = recordCoordinatorDecision(run, {
373
+ blackboardId: board.id,
374
+ topicId: topic.id,
375
+ kind: conflicts.length ? "conflict-resolution" : "context-update",
376
+ outcome: conflicts.length ? "conflicting" : "accepted",
377
+ reason: conflicts.length
378
+ ? `Context ${context.id} conflicts with ${conflicts.map((entry) => entry.id).join(", ")}`
379
+ : `Accepted ${input.kind} context ${context.id}`,
380
+ subjectIds: [context.id, ...conflicts.map((entry) => entry.id)],
381
+ evidenceRefs: context.evidenceRefs,
382
+ artifactRefIds: context.artifactRefIds,
383
+ author: { kind: "coordinator", id: "cw" },
384
+ scope: context.scope,
385
+ parentIds: context.parentIds,
386
+ tags: ["context", input.kind]
387
+ });
388
+ context.decisionId = decision.id;
389
+ appendBlackboardNode(run, "blackboard-context", context.id, statusToNodeStatus(context.status), `${context.kind}:${context.key}`, recordPath(run, "contexts", context.id), [`${run.id}:blackboard:topic:${topic.id}`]);
390
+ const audit = (0, trust_audit_1.recordTrustAuditEvent)(run, {
391
+ kind: "blackboard.context",
392
+ decision: conflicts.length ? "failed" : "accepted",
393
+ source: sourceForAuthor(context.author),
394
+ actor: context.author.id,
395
+ blackboardId: board.id,
396
+ blackboardTopicId: topic.id,
397
+ blackboardContextId: context.id,
398
+ coordinatorDecisionId: decision.id,
399
+ evidenceRefs: context.evidenceRefs,
400
+ multiAgentRunId: context.links.multiAgentRunId,
401
+ agentGroupId: context.links.agentGroupId,
402
+ agentRoleId: context.links.agentRoleId,
403
+ agentMembershipId: context.links.agentMembershipId,
404
+ metadata: { kind: context.kind, key: context.key, conflicts: context.conflictingContextIds }
405
+ });
406
+ const writeAudit = (0, multi_agent_trust_1.recordBlackboardWriteAudit)(run, {
407
+ operation: "context",
408
+ status: context.status,
409
+ actor: context.author,
410
+ multiAgentRunId: context.links.multiAgentRunId,
411
+ agentGroupId: context.links.agentGroupId,
412
+ agentRoleId: context.links.agentRoleId,
413
+ agentMembershipId: context.links.agentMembershipId,
414
+ blackboardId: board.id,
415
+ blackboardTopicId: topic.id,
416
+ blackboardContextId: context.id,
417
+ coordinatorDecisionId: decision.id,
418
+ evidenceRefs: context.evidenceRefs,
419
+ parentEventIds: unique([...(permission ? [permission.event.id] : []), audit.id]),
420
+ policyRef: permission?.policyRef,
421
+ metadata: { kind: context.kind, key: context.key, conflicts: context.conflictingContextIds }
422
+ });
423
+ context.links.auditEventIds = unique([...(context.links.auditEventIds || []), audit.id]);
424
+ context.links.auditEventIds = unique([...(context.links.auditEventIds || []), writeAudit.id]);
425
+ persistBlackboardState(run);
426
+ return context;
427
+ }
428
+ function addBlackboardArtifact(run, input) {
429
+ if (!input.path && !input.locator)
430
+ throw new Error("Blackboard artifact requires --path or --locator");
431
+ const state = ensureBlackboardState(run);
432
+ const board = resolveBlackboard(run, { id: input.blackboardId });
433
+ const topic = input.topicId ? requireTopic(run, input.topicId) : undefined;
434
+ if (topic && topic.blackboardId !== board.id)
435
+ throw new Error(`Topic ${topic.id} does not belong to blackboard ${board.id}`);
436
+ const id = input.id || createId("artifact");
437
+ assertUnique(state.artifacts, id, "BlackboardArtifactRef");
438
+ const author = normalizeAuthor(input.author, "operator");
439
+ const links = compactLinks(run, { ...board.links, ...(topic?.links || {}), ...roleLinkFromAuthor(author), ...(input.links || {}), evidenceRefs: input.evidenceRefs, auditEventIds: input.auditEventIds });
440
+ const permission = shouldEnforcePolicy(author, links)
441
+ ? (0, multi_agent_trust_1.assertMultiAgentActionAllowed)(run, {
442
+ operation: "artifact",
443
+ actor: author,
444
+ multiAgentRunId: links.multiAgentRunId,
445
+ agentRoleId: links.agentRoleId,
446
+ agentGroupId: links.agentGroupId,
447
+ agentMembershipId: links.agentMembershipId,
448
+ blackboardId: board.id,
449
+ blackboardTopicId: topic?.id,
450
+ blackboardArtifactRefId: id,
451
+ evidenceRefs: input.evidenceRefs || []
452
+ })
453
+ : undefined;
454
+ const absolutePath = input.path ? node_path_1.default.resolve(input.path) : undefined;
455
+ const artifact = {
456
+ ...base(run, board.id, id, author, input.scope, "active", input.tags, input.metadata),
457
+ topicId: topic?.id,
458
+ kind: input.kind,
459
+ path: absolutePath,
460
+ locator: input.locator,
461
+ owner: normalizeAuthor(input.owner || input.author, "operator"),
462
+ source: input.source || "operator-recorded",
463
+ provenance: compactLinks(run, { ...(input.provenance || {}), ...links }),
464
+ evidenceRefs: unique(input.evidenceRefs || []),
465
+ checksum: absolutePath && node_fs_1.default.existsSync(absolutePath) && node_fs_1.default.statSync(absolutePath).isFile() ? checksumFile(absolutePath) : undefined,
466
+ trustAuditEventIds: unique(input.auditEventIds || [])
467
+ };
468
+ state.artifacts.push(artifact);
469
+ board.artifactRefIds = unique([...board.artifactRefIds, artifact.id]);
470
+ if (topic)
471
+ topic.artifactRefIds = unique([...topic.artifactRefIds, artifact.id]);
472
+ touch(board);
473
+ if (topic)
474
+ touch(topic);
475
+ const decision = recordCoordinatorDecision(run, {
476
+ blackboardId: board.id,
477
+ topicId: topic?.id,
478
+ kind: "artifact-index",
479
+ outcome: "accepted",
480
+ reason: `Indexed ${artifact.kind} artifact ${artifact.id}`,
481
+ subjectIds: [artifact.id],
482
+ evidenceRefs: artifact.evidenceRefs,
483
+ artifactRefIds: [artifact.id],
484
+ author: { kind: "coordinator", id: "cw" },
485
+ scope: artifact.scope,
486
+ tags: ["artifact", artifact.kind]
487
+ });
488
+ appendBlackboardNode(run, "blackboard-artifact", artifact.id, "completed", artifact.kind, recordPath(run, "artifacts", artifact.id), [
489
+ topic ? `${run.id}:blackboard:topic:${topic.id}` : `${run.id}:blackboard:${board.id}`
490
+ ]);
491
+ const audit = (0, trust_audit_1.recordTrustAuditEvent)(run, {
492
+ kind: "blackboard.artifact",
493
+ decision: "accepted",
494
+ source: sourceForAuthor(artifact.author),
495
+ actor: artifact.author.id,
496
+ blackboardId: board.id,
497
+ blackboardTopicId: topic?.id,
498
+ blackboardArtifactRefId: artifact.id,
499
+ coordinatorDecisionId: decision.id,
500
+ workerId: artifact.provenance.workerId,
501
+ taskId: artifact.provenance.taskId,
502
+ candidateId: artifact.provenance.candidateId,
503
+ commitId: artifact.provenance.commitId,
504
+ normalizedPath: absolutePath,
505
+ evidenceRefs: artifact.evidenceRefs,
506
+ parentEventIds: artifact.trustAuditEventIds,
507
+ metadata: { kind: artifact.kind, locator: artifact.locator, checksum: artifact.checksum }
508
+ });
509
+ const writeAudit = (0, multi_agent_trust_1.recordBlackboardWriteAudit)(run, {
510
+ operation: "artifact",
511
+ status: artifact.status,
512
+ actor: artifact.author,
513
+ multiAgentRunId: artifact.provenance.multiAgentRunId,
514
+ agentGroupId: artifact.provenance.agentGroupId,
515
+ agentRoleId: artifact.provenance.agentRoleId,
516
+ agentMembershipId: artifact.provenance.agentMembershipId,
517
+ blackboardId: board.id,
518
+ blackboardTopicId: topic?.id,
519
+ blackboardArtifactRefId: artifact.id,
520
+ coordinatorDecisionId: decision.id,
521
+ evidenceRefs: artifact.evidenceRefs,
522
+ parentEventIds: unique([...(permission ? [permission.event.id] : []), audit.id]),
523
+ policyRef: permission?.policyRef,
524
+ metadata: { kind: artifact.kind, locator: artifact.locator, checksum: artifact.checksum }
525
+ });
526
+ artifact.trustAuditEventIds = unique([...artifact.trustAuditEventIds, audit.id, writeAudit.id]);
527
+ persistBlackboardState(run);
528
+ return artifact;
529
+ }
530
+ function createBlackboardSnapshot(run, blackboardId) {
531
+ const state = ensureBlackboardState(run);
532
+ const board = resolveBlackboard(run, { id: blackboardId });
533
+ const id = createId("snapshot");
534
+ const snapshotPath = recordPath(run, "snapshots", id);
535
+ const summary = summarizeBlackboard(run, board.id);
536
+ const snapshot = {
537
+ ...base(run, board.id, id, { kind: "runtime", id: "cw" }, { kind: "run", id: run.id }, "active", ["snapshot"], undefined),
538
+ topicIds: [...board.topicIds].sort(),
539
+ messageIds: state.messages.filter((entry) => entry.blackboardId === board.id).map((entry) => entry.id).sort(),
540
+ contextIds: [...board.contextIds].sort(),
541
+ artifactRefIds: [...board.artifactRefIds].sort(),
542
+ decisionIds: [...board.decisionIds].sort(),
543
+ snapshotPath,
544
+ indexPath: board.paths.index,
545
+ summary,
546
+ links: compactLinks(run, board.links)
547
+ };
548
+ state.snapshots.push(snapshot);
549
+ board.snapshotIds = unique([...board.snapshotIds, snapshot.id]);
550
+ touch(board);
551
+ appendBlackboardNode(run, "blackboard-snapshot", snapshot.id, "completed", snapshot.id, snapshotPath, [`${run.id}:blackboard:${board.id}`]);
552
+ const audit = (0, trust_audit_1.recordTrustAuditEvent)(run, {
553
+ kind: "blackboard.snapshot",
554
+ decision: "recorded",
555
+ source: "runtime-derived",
556
+ actor: "cw",
557
+ blackboardId: board.id,
558
+ blackboardSnapshotId: snapshot.id,
559
+ metadata: { snapshotPath, counts: summary }
560
+ });
561
+ const writeAudit = (0, multi_agent_trust_1.recordBlackboardWriteAudit)(run, {
562
+ operation: "snapshot",
563
+ status: snapshot.status,
564
+ actor: snapshot.author,
565
+ multiAgentRunId: snapshot.links.multiAgentRunId,
566
+ agentGroupId: snapshot.links.agentGroupId,
567
+ agentRoleId: snapshot.links.agentRoleId,
568
+ agentMembershipId: snapshot.links.agentMembershipId,
569
+ blackboardId: board.id,
570
+ blackboardSnapshotId: snapshot.id,
571
+ parentEventIds: [audit.id],
572
+ metadata: { snapshotPath }
573
+ });
574
+ snapshot.links.auditEventIds = [audit.id];
575
+ snapshot.links.auditEventIds = unique([...snapshot.links.auditEventIds, writeAudit.id]);
576
+ persistBlackboardState(run);
577
+ return snapshot;
578
+ }
579
+ function recordCoordinatorDecision(run, input) {
580
+ const state = ensureBlackboardState(run);
581
+ const board = resolveBlackboard(run, { id: input.blackboardId });
582
+ const id = input.id || createId("decision");
583
+ assertUnique(state.decisions, id, "CoordinatorDecision");
584
+ const decision = {
585
+ ...base(run, board.id, id, input.author || { kind: "coordinator", id: "cw" }, input.scope, decisionStatus(input.outcome), input.tags, input.metadata),
586
+ kind: input.kind,
587
+ outcome: input.outcome,
588
+ subjectIds: unique(input.subjectIds || []),
589
+ reason: input.reason,
590
+ evidenceRefs: unique(input.evidenceRefs || []),
591
+ artifactRefIds: requireArtifactRefs(run, input.artifactRefIds || []),
592
+ messageIds: requireMessages(run, input.messageIds || []),
593
+ links: compactLinks(run, { ...board.links, ...roleLinkFromAuthor(input.author), ...(input.links || {}), evidenceRefs: input.evidenceRefs })
594
+ };
595
+ state.decisions.push(decision);
596
+ board.decisionIds = unique([...board.decisionIds, decision.id]);
597
+ touch(board);
598
+ appendBlackboardNode(run, "coordinator-decision", decision.id, statusToNodeStatus(decision.status), `${decision.kind}:${decision.outcome}`, recordPath(run, "decisions", decision.id), [
599
+ `${run.id}:blackboard:${board.id}`,
600
+ ...(input.topicId ? [`${run.id}:blackboard:topic:${input.topicId}`] : [])
601
+ ]);
602
+ const audit = (0, trust_audit_1.recordTrustAuditEvent)(run, {
603
+ kind: "coordinator.decision",
604
+ decision: auditDecision(input.outcome),
605
+ source: "cw-validated",
606
+ actor: decision.author.id,
607
+ blackboardId: board.id,
608
+ blackboardTopicId: input.topicId,
609
+ coordinatorDecisionId: decision.id,
610
+ multiAgentRunId: decision.links.multiAgentRunId,
611
+ agentGroupId: decision.links.agentGroupId,
612
+ agentRoleId: decision.links.agentRoleId,
613
+ agentMembershipId: decision.links.agentMembershipId,
614
+ evidenceRefs: decision.evidenceRefs,
615
+ metadata: {
616
+ kind: decision.kind,
617
+ outcome: decision.outcome,
618
+ subjectIds: decision.subjectIds,
619
+ reason: decision.reason
620
+ }
621
+ });
622
+ const writeAudit = (0, multi_agent_trust_1.recordBlackboardWriteAudit)(run, {
623
+ operation: "coordinator-decision",
624
+ status: decision.status,
625
+ actor: decision.author,
626
+ multiAgentRunId: decision.links.multiAgentRunId,
627
+ agentGroupId: decision.links.agentGroupId,
628
+ agentRoleId: decision.links.agentRoleId,
629
+ agentMembershipId: decision.links.agentMembershipId,
630
+ blackboardId: board.id,
631
+ blackboardTopicId: input.topicId,
632
+ coordinatorDecisionId: decision.id,
633
+ evidenceRefs: decision.evidenceRefs,
634
+ parentEventIds: [audit.id],
635
+ metadata: { kind: decision.kind, outcome: decision.outcome }
636
+ });
637
+ if (decision.kind === "candidate-synthesis" || decision.tags.includes("panel-decision")) {
638
+ const panelAudit = (0, multi_agent_trust_1.recordJudgeRationaleAudit)(run, {
639
+ kind: "judge.panel-decision",
640
+ actor: decision.author,
641
+ multiAgentRunId: decision.links.multiAgentRunId,
642
+ agentGroupId: decision.links.agentGroupId,
643
+ agentRoleId: decision.links.agentRoleId,
644
+ agentMembershipId: decision.links.agentMembershipId,
645
+ blackboardId: board.id,
646
+ blackboardTopicId: input.topicId,
647
+ coordinatorDecisionId: decision.id,
648
+ evidenceRefs: decision.evidenceRefs,
649
+ rationale: decision.reason,
650
+ parentEventIds: [audit.id, writeAudit.id]
651
+ });
652
+ decision.links.auditEventIds = unique([...(decision.links.auditEventIds || []), panelAudit.id]);
653
+ }
654
+ decision.links.auditEventIds = unique([...(decision.links.auditEventIds || []), audit.id, writeAudit.id]);
655
+ persistBlackboardState(run);
656
+ return decision;
657
+ }
658
+ function summarizeBlackboard(run, blackboardId) {
659
+ const state = ensureBlackboardState(run);
660
+ const board = blackboardId ? state.boards.find((entry) => entry.id === blackboardId) : state.boards[0];
661
+ const scoped = (items) => board ? items.filter((item) => item.blackboardId === board.id) : [];
662
+ const contexts = scoped(state.contexts);
663
+ const artifacts = scoped(state.artifacts);
664
+ const openQuestions = contexts.filter((context) => context.kind === "question" && context.status === "open");
665
+ const conflicts = contexts.filter((context) => context.status === "conflicting" || context.conflictingContextIds.length);
666
+ const missingEvidence = [
667
+ ...openQuestions.filter((context) => !context.evidenceRefs.length && !context.artifactRefIds.length).map((context) => `question ${context.id} has no indexed evidence`),
668
+ ...contexts.filter((context) => context.kind !== "question" && context.status !== "superseded" && !context.evidenceRefs.length && !context.artifactRefIds.length).map((context) => `context ${context.id} has no indexed evidence`)
669
+ ].sort();
670
+ const readyForFanin = Boolean(board && !openQuestions.length && !conflicts.length && artifacts.length > 0 && missingEvidence.length === 0);
671
+ const latestSnapshot = scoped(state.snapshots).sort((left, right) => left.createdAt.localeCompare(right.createdAt)).at(-1);
672
+ return {
673
+ runId: run.id,
674
+ blackboardId: board?.id,
675
+ topics: scoped(state.topics).length,
676
+ messages: scoped(state.messages).length,
677
+ contexts: contexts.length,
678
+ artifacts: artifacts.length,
679
+ snapshots: scoped(state.snapshots).length,
680
+ decisions: scoped(state.decisions).length,
681
+ openQuestions,
682
+ conflicts,
683
+ missingEvidence,
684
+ readyForFanin,
685
+ latestSnapshotPath: latestSnapshot?.snapshotPath,
686
+ indexPath: board?.paths.index || node_path_1.default.join(blackboardRoot(run), "index.json"),
687
+ nextAction: nextAction(run, board, openQuestions, conflicts, artifacts)
688
+ };
689
+ }
690
+ function listBlackboardMessages(run, options = {}) {
691
+ const state = ensureBlackboardState(run);
692
+ return state.messages
693
+ .filter((message) => (!options.blackboardId || message.blackboardId === options.blackboardId) && (!options.topicId || message.topicId === options.topicId))
694
+ .sort((left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id));
695
+ }
696
+ function listBlackboardArtifacts(run, options = {}) {
697
+ const state = ensureBlackboardState(run);
698
+ return state.artifacts
699
+ .filter((artifact) => (!options.blackboardId || artifact.blackboardId === options.blackboardId) && (!options.topicId || artifact.topicId === options.topicId))
700
+ .sort((left, right) => left.id.localeCompare(right.id));
701
+ }
702
+ function buildBlackboardGraph(run) {
703
+ const state = ensureBlackboardState(run);
704
+ const nodes = [];
705
+ const edges = [];
706
+ for (const board of state.boards) {
707
+ nodes.push({ id: `${run.id}:blackboard:${board.id}`, kind: "blackboard", status: board.status, label: board.title, path: board.paths.index });
708
+ edges.push({ from: `${run.id}:run`, to: `${run.id}:blackboard:${board.id}` });
709
+ if (board.links.multiAgentRunId)
710
+ edges.push({ from: `${run.id}:multi-agent:${board.links.multiAgentRunId}`, to: `${run.id}:blackboard:${board.id}`, label: "coordinates" });
711
+ }
712
+ for (const topic of state.topics) {
713
+ nodes.push({ id: `${run.id}:blackboard:topic:${topic.id}`, kind: "blackboard-topic", status: topic.status, label: topic.title, path: recordPath(run, "topics", topic.id) });
714
+ edges.push({ from: `${run.id}:blackboard:${topic.blackboardId}`, to: `${run.id}:blackboard:topic:${topic.id}` });
715
+ }
716
+ for (const context of state.contexts) {
717
+ nodes.push({ id: `${run.id}:blackboard:context:${context.id}`, kind: "blackboard-context", status: context.status, label: `${context.kind}:${context.key}`, path: recordPath(run, "contexts", context.id) });
718
+ edges.push({ from: `${run.id}:blackboard:topic:${context.topicId}`, to: `${run.id}:blackboard:context:${context.id}` });
719
+ for (const conflicting of context.conflictingContextIds)
720
+ edges.push({ from: `${run.id}:blackboard:context:${context.id}`, to: `${run.id}:blackboard:context:${conflicting}`, label: "conflicts" });
721
+ }
722
+ for (const artifact of state.artifacts) {
723
+ nodes.push({ id: `${run.id}:blackboard:artifact:${artifact.id}`, kind: "blackboard-artifact", status: artifact.status, label: artifact.kind, path: recordPath(run, "artifacts", artifact.id) });
724
+ edges.push({ from: artifact.topicId ? `${run.id}:blackboard:topic:${artifact.topicId}` : `${run.id}:blackboard:${artifact.blackboardId}`, to: `${run.id}:blackboard:artifact:${artifact.id}` });
725
+ }
726
+ for (const message of state.messages) {
727
+ nodes.push({ id: `${run.id}:blackboard:message:${message.id}`, kind: "blackboard-message", status: message.status, label: truncate(message.body), path: messagesPath(run) });
728
+ edges.push({ from: `${run.id}:blackboard:topic:${message.topicId}`, to: `${run.id}:blackboard:message:${message.id}` });
729
+ if (message.replyToId)
730
+ edges.push({ from: `${run.id}:blackboard:message:${message.replyToId}`, to: `${run.id}:blackboard:message:${message.id}`, label: "reply" });
731
+ for (const artifactId of message.linkedArtifactRefIds)
732
+ edges.push({ from: `${run.id}:blackboard:message:${message.id}`, to: `${run.id}:blackboard:artifact:${artifactId}`, label: "cites" });
733
+ }
734
+ for (const decision of state.decisions) {
735
+ nodes.push({ id: `${run.id}:coordinator:decision:${decision.id}`, kind: "coordinator-decision", status: decision.status, label: `${decision.kind}:${decision.outcome}`, path: recordPath(run, "decisions", decision.id) });
736
+ edges.push({ from: `${run.id}:blackboard:${decision.blackboardId}`, to: `${run.id}:coordinator:decision:${decision.id}` });
737
+ for (const subjectId of decision.subjectIds)
738
+ edges.push({ from: `${run.id}:coordinator:decision:${decision.id}`, to: graphSubject(run, subjectId), label: "subject" });
739
+ }
740
+ for (const snapshot of state.snapshots) {
741
+ nodes.push({ id: `${run.id}:blackboard:snapshot:${snapshot.id}`, kind: "blackboard-snapshot", status: snapshot.status, label: snapshot.id, path: snapshot.snapshotPath });
742
+ edges.push({ from: `${run.id}:blackboard:${snapshot.blackboardId}`, to: `${run.id}:blackboard:snapshot:${snapshot.id}` });
743
+ }
744
+ return { nodes, edges: uniqueEdges(edges) };
745
+ }
746
+ function persistBlackboardState(run) {
747
+ const state = ensureBlackboardState(run);
748
+ const root = blackboardRoot(run);
749
+ assertNoRecordPathCollisions("BlackboardTopic", state.topics);
750
+ assertNoRecordPathCollisions("BlackboardContext", state.contexts);
751
+ assertNoRecordPathCollisions("BlackboardArtifactRef", state.artifacts);
752
+ assertNoRecordPathCollisions("BlackboardSnapshot", state.snapshots);
753
+ assertNoRecordPathCollisions("CoordinatorDecision", state.decisions);
754
+ const index = {
755
+ schemaVersion: exports.BLACKBOARD_SCHEMA_VERSION,
756
+ runId: run.id,
757
+ generatedAt: timestamp(),
758
+ counts: {
759
+ boards: state.boards.length,
760
+ topics: state.topics.length,
761
+ messages: state.messages.length,
762
+ contexts: state.contexts.length,
763
+ artifacts: state.artifacts.length,
764
+ snapshots: state.snapshots.length,
765
+ decisions: state.decisions.length
766
+ },
767
+ boards: state.boards.map(indexRow),
768
+ topics: state.topics.map(indexRow),
769
+ contexts: state.contexts.map(indexRow),
770
+ artifacts: state.artifacts.map(indexRow),
771
+ snapshots: state.snapshots.map(indexRow),
772
+ decisions: state.decisions.map(indexRow),
773
+ messages: state.messages.map((message) => ({
774
+ id: message.id,
775
+ blackboardId: message.blackboardId,
776
+ topicId: message.topicId,
777
+ createdAt: message.createdAt,
778
+ status: message.status,
779
+ author: message.author,
780
+ evidenceRefs: message.linkedEvidenceRefs,
781
+ artifactRefIds: message.linkedArtifactRefIds
782
+ }))
783
+ };
784
+ (0, state_1.writeJson)(node_path_1.default.join(root, "index.json"), index);
785
+ node_fs_1.default.writeFileSync(messagesPath(run), state.messages.sort(compareRecords).map((message) => JSON.stringify(message)).join("\n") + (state.messages.length ? "\n" : ""), "utf8");
786
+ for (const topic of state.topics)
787
+ (0, state_1.writeJson)(recordPath(run, "topics", topic.id), topic);
788
+ for (const context of state.contexts)
789
+ (0, state_1.writeJson)(recordPath(run, "contexts", context.id), context);
790
+ for (const artifact of state.artifacts)
791
+ (0, state_1.writeJson)(recordPath(run, "artifacts", artifact.id), artifact);
792
+ for (const snapshot of state.snapshots)
793
+ (0, state_1.writeJson)(recordPath(run, "snapshots", snapshot.id), snapshot);
794
+ for (const decision of state.decisions)
795
+ (0, state_1.writeJson)(recordPath(run, "decisions", decision.id), decision);
796
+ }
797
+ function bridgeStateArtifactToBlackboard(run, artifact, input = {}) {
798
+ return addBlackboardArtifact(run, {
799
+ kind: artifact.kind,
800
+ path: artifact.path,
801
+ locator: artifact.path,
802
+ metadata: { description: artifact.description, stateArtifactId: artifact.id },
803
+ ...input
804
+ });
805
+ }
806
+ function evidenceFromArtifactRef(artifact) {
807
+ return {
808
+ id: artifact.id,
809
+ source: "blackboard-artifact",
810
+ path: artifact.path,
811
+ locator: artifact.locator || artifact.path,
812
+ summary: `${artifact.kind} ${artifact.path || artifact.locator || artifact.id}`,
813
+ provenance: {
814
+ schemaVersion: 1,
815
+ runId: artifact.runId,
816
+ source: "cw-validated",
817
+ workerId: artifact.provenance.workerId,
818
+ taskId: artifact.provenance.taskId,
819
+ candidateId: artifact.provenance.candidateId,
820
+ commitId: artifact.provenance.commitId,
821
+ auditEventIds: artifact.trustAuditEventIds
822
+ }
823
+ };
824
+ }
825
+ function emptyState() {
826
+ return {
827
+ schemaVersion: exports.BLACKBOARD_SCHEMA_VERSION,
828
+ boards: [],
829
+ topics: [],
830
+ messages: [],
831
+ contexts: [],
832
+ artifacts: [],
833
+ snapshots: [],
834
+ decisions: []
835
+ };
836
+ }
837
+ function roleLinkFromAuthor(author) {
838
+ if (!author?.id)
839
+ return {};
840
+ if (author.kind === "role")
841
+ return { agentRoleId: author.id };
842
+ if (author.kind === "group")
843
+ return { agentGroupId: author.id };
844
+ if (author.kind === "membership")
845
+ return { agentMembershipId: author.id };
846
+ if (author.kind === "worker")
847
+ return { workerId: author.id };
848
+ return {};
849
+ }
850
+ function shouldEnforcePolicy(author, links) {
851
+ if (author.kind === "role" || author.kind === "group" || author.kind === "membership" || author.kind === "worker")
852
+ return true;
853
+ return Boolean(links.agentRoleId || links.agentGroupId || links.agentMembershipId);
854
+ }
855
+ function base(run, blackboardId, id, author, scope, status = "active", tags, metadata) {
856
+ const now = timestamp();
857
+ return {
858
+ schemaVersion: exports.BLACKBOARD_SCHEMA_VERSION,
859
+ id,
860
+ runId: run.id,
861
+ blackboardId,
862
+ createdAt: now,
863
+ updatedAt: now,
864
+ author: normalizeAuthor(author, "operator"),
865
+ scope: normalizeScope(scope, { kind: "run", id: run.id }),
866
+ status,
867
+ parentIds: [],
868
+ tags: sortTags(tags),
869
+ metadata: scrub(metadata)
870
+ };
871
+ }
872
+ function normalizeAuthor(input, fallbackKind) {
873
+ const kind = input?.kind || fallbackKind;
874
+ const id = input?.id || (kind === "runtime" || kind === "coordinator" ? "cw" : kind === "operator" ? "operator" : undefined);
875
+ if (!id)
876
+ throw new Error("Blackboard author requires an explicit id");
877
+ return { kind, id, displayName: input?.displayName };
878
+ }
879
+ function normalizeScope(input, fallback) {
880
+ const kind = input?.kind || fallback.kind;
881
+ const id = input?.id || fallback.id;
882
+ if (!kind || !id)
883
+ throw new Error("Blackboard scope requires kind and id");
884
+ return { kind, id };
885
+ }
886
+ function compactLinks(run, input) {
887
+ return compact({
888
+ workflowRunId: run.id,
889
+ multiAgentRunId: input.multiAgentRunId,
890
+ agentGroupId: input.agentGroupId,
891
+ agentRoleId: input.agentRoleId,
892
+ agentMembershipId: input.agentMembershipId,
893
+ agentFanoutId: input.agentFanoutId,
894
+ agentFaninId: input.agentFaninId,
895
+ taskId: input.taskId,
896
+ workerId: input.workerId,
897
+ candidateId: input.candidateId,
898
+ verifierNodeId: input.verifierNodeId,
899
+ commitId: input.commitId,
900
+ auditEventIds: unique(input.auditEventIds || []),
901
+ evidenceRefs: unique(input.evidenceRefs || [])
902
+ });
903
+ }
904
+ function linkMultiAgent(run, blackboardId, topicIds, input) {
905
+ const groupId = "agentGroupId" in input ? input.agentGroupId : ("groupId" in input ? input.groupId : undefined);
906
+ const roleId = "agentRoleId" in input ? input.agentRoleId : ("roleId" in input ? input.roleId : undefined);
907
+ const membershipId = "agentMembershipId" in input ? input.agentMembershipId : ("membershipId" in input ? input.membershipId : undefined);
908
+ if (input.multiAgentRunId) {
909
+ const record = (0, multi_agent_1.getMultiAgentRun)(run, input.multiAgentRunId);
910
+ if (record) {
911
+ record.blackboardId = blackboardId;
912
+ record.topicIds = unique([...(record.topicIds || []), ...topicIds]);
913
+ record.links.blackboardId = blackboardId;
914
+ record.links.blackboardTopicIds = unique([...(record.links.blackboardTopicIds || []), ...topicIds]);
915
+ }
916
+ }
917
+ if (groupId) {
918
+ const record = (0, multi_agent_1.getAgentGroup)(run, groupId);
919
+ if (record) {
920
+ record.blackboardId = blackboardId;
921
+ record.topicIds = unique([...(record.topicIds || []), ...topicIds]);
922
+ }
923
+ }
924
+ if (roleId) {
925
+ const record = (0, multi_agent_1.getAgentRole)(run, roleId);
926
+ if (record) {
927
+ record.blackboardId = blackboardId;
928
+ record.topicIds = unique([...(record.topicIds || []), ...topicIds]);
929
+ }
930
+ }
931
+ if (membershipId) {
932
+ const record = (0, multi_agent_1.getAgentMembership)(run, membershipId);
933
+ if (record) {
934
+ record.blackboardId = blackboardId;
935
+ record.topicIds = unique([...(record.topicIds || []), ...topicIds]);
936
+ }
937
+ }
938
+ }
939
+ function requireBoard(run, id) {
940
+ const board = ensureBlackboardState(run).boards.find((entry) => entry.id === id);
941
+ if (!board)
942
+ throw new Error(`Unknown Blackboard id: ${id}`);
943
+ return board;
944
+ }
945
+ function requireTopic(run, id) {
946
+ const topic = ensureBlackboardState(run).topics.find((entry) => entry.id === id);
947
+ if (!topic)
948
+ throw new Error(`Unknown BlackboardTopic id: ${id}`);
949
+ return topic;
950
+ }
951
+ function requireContext(run, id) {
952
+ const context = ensureBlackboardState(run).contexts.find((entry) => entry.id === id);
953
+ if (!context)
954
+ throw new Error(`Unknown BlackboardContext id: ${id}`);
955
+ return context;
956
+ }
957
+ function requireArtifactRefs(run, ids) {
958
+ const state = ensureBlackboardState(run);
959
+ for (const id of ids) {
960
+ if (!state.artifacts.some((artifact) => artifact.id === id))
961
+ throw new Error(`Unknown BlackboardArtifactRef id: ${id}`);
962
+ }
963
+ return unique(ids);
964
+ }
965
+ function requireMessages(run, ids) {
966
+ const state = ensureBlackboardState(run);
967
+ for (const id of ids) {
968
+ if (!state.messages.some((message) => message.id === id))
969
+ throw new Error(`Unknown BlackboardMessage id: ${id}`);
970
+ }
971
+ return unique(ids);
972
+ }
973
+ function appendBlackboardNode(run, kind, id, status, label, artifactPath, parents = []) {
974
+ const nodeId = kind === "blackboard"
975
+ ? `${run.id}:blackboard:${id}`
976
+ : kind === "coordinator-decision"
977
+ ? `${run.id}:coordinator:decision:${id}`
978
+ : `${run.id}:blackboard:${kind.replace("blackboard-", "")}:${id}`;
979
+ (0, state_node_1.appendRunNode)(run, (0, state_node_1.createStateNode)({
980
+ id: nodeId,
981
+ kind,
982
+ status,
983
+ loopStage: run.loopStage,
984
+ outputs: { id, label },
985
+ artifacts: [{ id: kind, kind: "json", path: artifactPath }],
986
+ parents,
987
+ contractId: pipeline_contract_1.DEFAULT_PIPELINE_CONTRACT_ID,
988
+ metadata: { id, label }
989
+ }));
990
+ }
991
+ function statusToNodeStatus(status) {
992
+ switch (status) {
993
+ case "active":
994
+ case "open":
995
+ return "running";
996
+ case "resolved":
997
+ case "superseded":
998
+ return "completed";
999
+ case "conflicting":
1000
+ return "blocked";
1001
+ case "rejected":
1002
+ return "rejected";
1003
+ default:
1004
+ return "completed";
1005
+ }
1006
+ }
1007
+ function decisionStatus(outcome) {
1008
+ if (outcome === "conflicting" || outcome === "blocked")
1009
+ return "conflicting";
1010
+ if (outcome === "rejected")
1011
+ return "rejected";
1012
+ if (outcome === "superseded")
1013
+ return "superseded";
1014
+ return "active";
1015
+ }
1016
+ function auditDecision(outcome) {
1017
+ if (outcome === "rejected")
1018
+ return "rejected";
1019
+ if (outcome === "blocked" || outcome === "conflicting")
1020
+ return "failed";
1021
+ return "accepted";
1022
+ }
1023
+ function sourceForAuthor(author) {
1024
+ if (author.kind === "runtime" || author.kind === "coordinator")
1025
+ return "runtime-derived";
1026
+ if (author.kind === "worker" || author.kind === "verifier")
1027
+ return "cw-validated";
1028
+ return "operator-recorded";
1029
+ }
1030
+ function boardPaths(run) {
1031
+ const root = blackboardRoot(run);
1032
+ return {
1033
+ root,
1034
+ index: node_path_1.default.join(root, "index.json"),
1035
+ messages: messagesPath(run),
1036
+ topicsDir: node_path_1.default.join(root, "topics"),
1037
+ contextsDir: node_path_1.default.join(root, "contexts"),
1038
+ artifactsDir: node_path_1.default.join(root, "artifacts"),
1039
+ snapshotsDir: node_path_1.default.join(root, "snapshots"),
1040
+ decisionsDir: node_path_1.default.join(root, "decisions")
1041
+ };
1042
+ }
1043
+ function blackboardRoot(run) {
1044
+ return run.paths.blackboardDir || node_path_1.default.join(run.paths.runDir, "blackboard");
1045
+ }
1046
+ function messagesPath(run) {
1047
+ return node_path_1.default.join(blackboardRoot(run), "messages.jsonl");
1048
+ }
1049
+ function recordPath(run, kind, id) {
1050
+ return node_path_1.default.join(blackboardRoot(run), kind, `${(0, state_1.safeFileName)(id)}.json`);
1051
+ }
1052
+ function graphSubject(run, id) {
1053
+ const state = ensureBlackboardState(run);
1054
+ if (state.contexts.some((entry) => entry.id === id))
1055
+ return `${run.id}:blackboard:context:${id}`;
1056
+ if (state.artifacts.some((entry) => entry.id === id))
1057
+ return `${run.id}:blackboard:artifact:${id}`;
1058
+ if (state.messages.some((entry) => entry.id === id))
1059
+ return `${run.id}:blackboard:message:${id}`;
1060
+ return id;
1061
+ }
1062
+ function nextAction(run, board, openQuestions, conflicts, artifacts) {
1063
+ if (!board)
1064
+ return `node scripts/cw.js blackboard topic create ${run.id} --id <topic-id> --title "<title>"`;
1065
+ if (conflicts.length)
1066
+ return `node scripts/cw.js coordinator decision ${run.id} --kind conflict-resolution --outcome accepted --subject ${conflicts[0].id} --reason "<reason>"`;
1067
+ if (openQuestions.length)
1068
+ return `node scripts/cw.js blackboard message post ${run.id} --topic ${openQuestions[0].topicId} --body "<answer with evidence>"`;
1069
+ if (!artifacts.length)
1070
+ return `node scripts/cw.js blackboard artifact add ${run.id} --path <path> --kind <kind>`;
1071
+ return `node scripts/cw.js blackboard snapshot ${run.id}`;
1072
+ }
1073
+ function checksumFile(file) {
1074
+ return `sha256:${node_crypto_1.default.createHash("sha256").update(node_fs_1.default.readFileSync(file)).digest("hex")}`;
1075
+ }
1076
+ function assertUnique(items, id, label) {
1077
+ if (items.some((item) => item.id === id))
1078
+ throw new Error(`Duplicate ${label} id: ${id}`);
1079
+ }
1080
+ function assertNoRecordPathCollisions(label, records) {
1081
+ const seen = new Map();
1082
+ for (const record of records) {
1083
+ const safe = (0, state_1.safeFileName)(record.id);
1084
+ const existing = seen.get(safe);
1085
+ if (existing && existing !== record.id) {
1086
+ throw new Error(`${label} ids ${existing} and ${record.id} collide on safe file name ${safe}`);
1087
+ }
1088
+ seen.set(safe, record.id);
1089
+ }
1090
+ }
1091
+ function indexRow(record) {
1092
+ return { id: record.id, blackboardId: record.blackboardId, topicId: record.topicId, status: record.status, updatedAt: record.updatedAt };
1093
+ }
1094
+ function compareRecords(left, right) {
1095
+ return left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id);
1096
+ }
1097
+ function uniqueEdges(edges) {
1098
+ const seen = new Set();
1099
+ const result = [];
1100
+ for (const edge of edges) {
1101
+ const key = `${edge.from}\0${edge.to}\0${edge.label || ""}`;
1102
+ if (seen.has(key))
1103
+ continue;
1104
+ seen.add(key);
1105
+ result.push(edge);
1106
+ }
1107
+ return result;
1108
+ }
1109
+ function createId(prefix) {
1110
+ const stamp = new Date().toISOString().replace(/[-:]/g, "").replace(/\..+/, "Z");
1111
+ return `${prefix}-${stamp}-${Math.random().toString(36).slice(2, 8)}`;
1112
+ }
1113
+ function touch(record) {
1114
+ record.updatedAt = timestamp();
1115
+ return record;
1116
+ }
1117
+ function timestamp() {
1118
+ return new Date().toISOString();
1119
+ }
1120
+ function unique(values) {
1121
+ return Array.from(new Set(values.filter(Boolean))).sort();
1122
+ }
1123
+ function sortTags(values) {
1124
+ return unique(values || []);
1125
+ }
1126
+ function truncate(value) {
1127
+ return value.length > 64 ? `${value.slice(0, 61)}...` : value;
1128
+ }
1129
+ function compact(value) {
1130
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0)));
1131
+ }
1132
+ // Recursive secret redaction (v0.1.40 self-audit P3): the previous scrub only
1133
+ // inspected TOP-LEVEL keys, so a secret nested under an allowed key
1134
+ // (e.g. `metadata.config.token`) leaked into the recorded coordinator decision.
1135
+ // Now we recurse into nested objects and arrays so a secret-named key at any depth
1136
+ // is dropped and an obvious credential value is redacted.
1137
+ const SECRET_KEY_RE = /secret|token|password|credential|authorization|api[_-]?key|env/i;
1138
+ const SECRET_VALUE_RE = /secret|token|password|credential/i;
1139
+ function scrubValue(value) {
1140
+ if (Array.isArray(value))
1141
+ return value.map(scrubValue);
1142
+ if (value && typeof value === "object")
1143
+ return scrub(value);
1144
+ if (typeof value === "string" && SECRET_VALUE_RE.test(value))
1145
+ return "[redacted]";
1146
+ return value;
1147
+ }
1148
+ function scrub(value) {
1149
+ if (!value)
1150
+ return undefined;
1151
+ const result = {};
1152
+ for (const [key, entry] of Object.entries(value)) {
1153
+ if (entry === undefined)
1154
+ continue;
1155
+ if (SECRET_KEY_RE.test(key)) {
1156
+ result[key] = "[redacted]";
1157
+ }
1158
+ else {
1159
+ result[key] = scrubValue(entry);
1160
+ }
1161
+ }
1162
+ return Object.keys(result).length ? result : undefined;
1163
+ }