nodebench-mcp 2.70.0 → 3.0.0

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 (214) hide show
  1. package/README.md +95 -41
  2. package/dist/agents/alertRouter.d.ts +38 -0
  3. package/dist/agents/alertRouter.js +151 -0
  4. package/dist/agents/alertRouter.js.map +1 -0
  5. package/dist/agents/entityMemory.d.ts +40 -0
  6. package/dist/agents/entityMemory.js +64 -0
  7. package/dist/agents/entityMemory.js.map +1 -0
  8. package/dist/agents/subAgents.d.ts +35 -0
  9. package/dist/agents/subAgents.js +62 -0
  10. package/dist/agents/subAgents.js.map +1 -0
  11. package/dist/benchmarks/benchmarkRunner.js +14 -0
  12. package/dist/benchmarks/benchmarkRunner.js.map +1 -1
  13. package/dist/benchmarks/chainEval.js +107 -0
  14. package/dist/benchmarks/chainEval.js.map +1 -1
  15. package/dist/benchmarks/llmJudgeEval.js +85 -0
  16. package/dist/benchmarks/llmJudgeEval.js.map +1 -1
  17. package/dist/benchmarks/searchQualityEval.js +118 -5
  18. package/dist/benchmarks/searchQualityEval.js.map +1 -1
  19. package/dist/cli/search.d.ts +13 -0
  20. package/dist/cli/search.js +130 -0
  21. package/dist/cli/search.js.map +1 -0
  22. package/dist/db.d.ts +6 -2
  23. package/dist/db.js +470 -3
  24. package/dist/db.js.map +1 -1
  25. package/dist/index.js +349 -64
  26. package/dist/index.js.map +1 -1
  27. package/dist/profiler/behaviorStore.d.ts +97 -0
  28. package/dist/profiler/behaviorStore.js +276 -0
  29. package/dist/profiler/behaviorStore.js.map +1 -0
  30. package/dist/profiler/eventCollector.d.ts +119 -0
  31. package/dist/profiler/eventCollector.js +267 -0
  32. package/dist/profiler/eventCollector.js.map +1 -0
  33. package/dist/profiler/index.d.ts +15 -0
  34. package/dist/profiler/index.js +16 -0
  35. package/dist/profiler/index.js.map +1 -0
  36. package/dist/profiler/mcpProxy.d.ts +49 -0
  37. package/dist/profiler/mcpProxy.js +123 -0
  38. package/dist/profiler/mcpProxy.js.map +1 -0
  39. package/dist/profiler/modelRouter.d.ts +30 -0
  40. package/dist/profiler/modelRouter.js +99 -0
  41. package/dist/profiler/modelRouter.js.map +1 -0
  42. package/dist/profiler/otelReceiver.d.ts +17 -0
  43. package/dist/profiler/otelReceiver.js +62 -0
  44. package/dist/profiler/otelReceiver.js.map +1 -0
  45. package/dist/profiler/proofEngine.d.ts +41 -0
  46. package/dist/profiler/proofEngine.js +93 -0
  47. package/dist/profiler/proofEngine.js.map +1 -0
  48. package/dist/profiler/workflowTemplates.d.ts +41 -0
  49. package/dist/profiler/workflowTemplates.js +95 -0
  50. package/dist/profiler/workflowTemplates.js.map +1 -0
  51. package/dist/providers/localMemoryProvider.js +3 -2
  52. package/dist/providers/localMemoryProvider.js.map +1 -1
  53. package/dist/runtimeConfig.d.ts +11 -0
  54. package/dist/runtimeConfig.js +27 -0
  55. package/dist/runtimeConfig.js.map +1 -0
  56. package/dist/security/auditLog.js +8 -3
  57. package/dist/security/auditLog.js.map +1 -1
  58. package/dist/subconscious/blocks.d.ts +43 -0
  59. package/dist/subconscious/blocks.js +158 -0
  60. package/dist/subconscious/blocks.js.map +1 -0
  61. package/dist/subconscious/classifier.d.ts +22 -0
  62. package/dist/subconscious/classifier.js +118 -0
  63. package/dist/subconscious/classifier.js.map +1 -0
  64. package/dist/subconscious/graphEngine.d.ts +65 -0
  65. package/dist/subconscious/graphEngine.js +234 -0
  66. package/dist/subconscious/graphEngine.js.map +1 -0
  67. package/dist/subconscious/index.d.ts +19 -0
  68. package/dist/subconscious/index.js +20 -0
  69. package/dist/subconscious/index.js.map +1 -0
  70. package/dist/subconscious/tools.d.ts +5 -0
  71. package/dist/subconscious/tools.js +255 -0
  72. package/dist/subconscious/tools.js.map +1 -0
  73. package/dist/subconscious/whisperPolicy.d.ts +20 -0
  74. package/dist/subconscious/whisperPolicy.js +171 -0
  75. package/dist/subconscious/whisperPolicy.js.map +1 -0
  76. package/dist/sweep/engine.d.ts +27 -0
  77. package/dist/sweep/engine.js +244 -0
  78. package/dist/sweep/engine.js.map +1 -0
  79. package/dist/sweep/index.d.ts +9 -0
  80. package/dist/sweep/index.js +8 -0
  81. package/dist/sweep/index.js.map +1 -0
  82. package/dist/sweep/sources/github_trending.d.ts +6 -0
  83. package/dist/sweep/sources/github_trending.js +37 -0
  84. package/dist/sweep/sources/github_trending.js.map +1 -0
  85. package/dist/sweep/sources/hackernews.d.ts +7 -0
  86. package/dist/sweep/sources/hackernews.js +57 -0
  87. package/dist/sweep/sources/hackernews.js.map +1 -0
  88. package/dist/sweep/sources/openbb_finance.d.ts +9 -0
  89. package/dist/sweep/sources/openbb_finance.js +46 -0
  90. package/dist/sweep/sources/openbb_finance.js.map +1 -0
  91. package/dist/sweep/sources/producthunt.d.ts +6 -0
  92. package/dist/sweep/sources/producthunt.js +41 -0
  93. package/dist/sweep/sources/producthunt.js.map +1 -0
  94. package/dist/sweep/sources/web_signals.d.ts +7 -0
  95. package/dist/sweep/sources/web_signals.js +63 -0
  96. package/dist/sweep/sources/web_signals.js.map +1 -0
  97. package/dist/sweep/sources/yahoo_finance.d.ts +6 -0
  98. package/dist/sweep/sources/yahoo_finance.js +47 -0
  99. package/dist/sweep/sources/yahoo_finance.js.map +1 -0
  100. package/dist/sweep/types.d.ts +50 -0
  101. package/dist/sweep/types.js +9 -0
  102. package/dist/sweep/types.js.map +1 -0
  103. package/dist/sync/founderEpisodeStore.d.ts +98 -0
  104. package/dist/sync/founderEpisodeStore.js +230 -0
  105. package/dist/sync/founderEpisodeStore.js.map +1 -0
  106. package/dist/sync/hyperloopArchive.d.ts +51 -0
  107. package/dist/sync/hyperloopArchive.js +153 -0
  108. package/dist/sync/hyperloopArchive.js.map +1 -0
  109. package/dist/sync/hyperloopEval.d.ts +123 -0
  110. package/dist/sync/hyperloopEval.js +389 -0
  111. package/dist/sync/hyperloopEval.js.map +1 -0
  112. package/dist/sync/hyperloopEval.test.d.ts +4 -0
  113. package/dist/sync/hyperloopEval.test.js +60 -0
  114. package/dist/sync/hyperloopEval.test.js.map +1 -0
  115. package/dist/sync/protocol.d.ts +172 -0
  116. package/dist/sync/protocol.js +9 -0
  117. package/dist/sync/protocol.js.map +1 -0
  118. package/dist/sync/sessionMemory.d.ts +47 -0
  119. package/dist/sync/sessionMemory.js +138 -0
  120. package/dist/sync/sessionMemory.js.map +1 -0
  121. package/dist/sync/store.d.ts +384 -0
  122. package/dist/sync/store.js +1435 -0
  123. package/dist/sync/store.js.map +1 -0
  124. package/dist/sync/store.test.d.ts +4 -0
  125. package/dist/sync/store.test.js +43 -0
  126. package/dist/sync/store.test.js.map +1 -0
  127. package/dist/sync/syncBridgeClient.d.ts +30 -0
  128. package/dist/sync/syncBridgeClient.js +172 -0
  129. package/dist/sync/syncBridgeClient.js.map +1 -0
  130. package/dist/tools/autonomousDeliveryTools.d.ts +2 -0
  131. package/dist/tools/autonomousDeliveryTools.js +1104 -0
  132. package/dist/tools/autonomousDeliveryTools.js.map +1 -0
  133. package/dist/tools/claudeCodeIngestTools.d.ts +10 -0
  134. package/dist/tools/claudeCodeIngestTools.js +347 -0
  135. package/dist/tools/claudeCodeIngestTools.js.map +1 -0
  136. package/dist/tools/coreWorkflowTools.d.ts +2 -0
  137. package/dist/tools/coreWorkflowTools.js +488 -0
  138. package/dist/tools/coreWorkflowTools.js.map +1 -0
  139. package/dist/tools/deltaTools.d.ts +15 -0
  140. package/dist/tools/deltaTools.js +1522 -0
  141. package/dist/tools/deltaTools.js.map +1 -0
  142. package/dist/tools/entityLookupTools.d.ts +14 -0
  143. package/dist/tools/entityLookupTools.js +159 -0
  144. package/dist/tools/entityLookupTools.js.map +1 -0
  145. package/dist/tools/entityTemporalTools.d.ts +12 -0
  146. package/dist/tools/entityTemporalTools.js +330 -0
  147. package/dist/tools/entityTemporalTools.js.map +1 -0
  148. package/dist/tools/founderLocalPipeline.d.ts +215 -0
  149. package/dist/tools/founderLocalPipeline.js +1516 -2
  150. package/dist/tools/founderLocalPipeline.js.map +1 -1
  151. package/dist/tools/founderOperatingModel.d.ts +120 -0
  152. package/dist/tools/founderOperatingModel.js +469 -0
  153. package/dist/tools/founderOperatingModel.js.map +1 -0
  154. package/dist/tools/founderOperatingModelTools.d.ts +2 -0
  155. package/dist/tools/founderOperatingModelTools.js +169 -0
  156. package/dist/tools/founderOperatingModelTools.js.map +1 -0
  157. package/dist/tools/founderStrategicOpsTools.d.ts +2 -0
  158. package/dist/tools/founderStrategicOpsTools.js +1310 -0
  159. package/dist/tools/founderStrategicOpsTools.js.map +1 -0
  160. package/dist/tools/graphifyTools.d.ts +19 -0
  161. package/dist/tools/graphifyTools.js +375 -0
  162. package/dist/tools/graphifyTools.js.map +1 -0
  163. package/dist/tools/index.d.ts +3 -0
  164. package/dist/tools/index.js +4 -0
  165. package/dist/tools/index.js.map +1 -1
  166. package/dist/tools/monteCarloTools.d.ts +16 -0
  167. package/dist/tools/monteCarloTools.js +225 -0
  168. package/dist/tools/monteCarloTools.js.map +1 -0
  169. package/dist/tools/packetCompilerTools.d.ts +12 -0
  170. package/dist/tools/packetCompilerTools.js +322 -0
  171. package/dist/tools/packetCompilerTools.js.map +1 -0
  172. package/dist/tools/planSynthesisTools.d.ts +15 -0
  173. package/dist/tools/planSynthesisTools.js +455 -0
  174. package/dist/tools/planSynthesisTools.js.map +1 -0
  175. package/dist/tools/profilerTools.d.ts +20 -0
  176. package/dist/tools/profilerTools.js +364 -0
  177. package/dist/tools/profilerTools.js.map +1 -0
  178. package/dist/tools/savingsTools.d.ts +11 -0
  179. package/dist/tools/savingsTools.js +155 -0
  180. package/dist/tools/savingsTools.js.map +1 -0
  181. package/dist/tools/scenarioCompilerTools.d.ts +14 -0
  182. package/dist/tools/scenarioCompilerTools.js +290 -0
  183. package/dist/tools/scenarioCompilerTools.js.map +1 -0
  184. package/dist/tools/sharedContextTools.d.ts +2 -0
  185. package/dist/tools/sharedContextTools.js +423 -0
  186. package/dist/tools/sharedContextTools.js.map +1 -0
  187. package/dist/tools/sitemapTools.d.ts +15 -0
  188. package/dist/tools/sitemapTools.js +560 -0
  189. package/dist/tools/sitemapTools.js.map +1 -0
  190. package/dist/tools/sweepTools.d.ts +9 -0
  191. package/dist/tools/sweepTools.js +112 -0
  192. package/dist/tools/sweepTools.js.map +1 -0
  193. package/dist/tools/syncBridgeTools.d.ts +2 -0
  194. package/dist/tools/syncBridgeTools.js +258 -0
  195. package/dist/tools/syncBridgeTools.js.map +1 -0
  196. package/dist/tools/toolRegistry.js +1216 -49
  197. package/dist/tools/toolRegistry.js.map +1 -1
  198. package/dist/tools/workspaceTools.d.ts +19 -0
  199. package/dist/tools/workspaceTools.js +762 -0
  200. package/dist/tools/workspaceTools.js.map +1 -0
  201. package/dist/toolsetRegistry.js +88 -2
  202. package/dist/toolsetRegistry.js.map +1 -1
  203. package/package.json +36 -36
  204. package/rules/nodebench-agentic-reliability.md +32 -0
  205. package/rules/nodebench-analyst-diagnostic.md +25 -0
  206. package/rules/nodebench-auto-qa.md +31 -0
  207. package/rules/nodebench-completion-traceability.md +22 -0
  208. package/rules/nodebench-flywheel-continuous.md +25 -0
  209. package/rules/nodebench-pre-release-review.md +24 -0
  210. package/rules/nodebench-qa-dogfood.md +26 -0
  211. package/rules/nodebench-scenario-testing.md +30 -0
  212. package/rules/nodebench-self-direction.md +23 -0
  213. package/rules/nodebench-self-judge-loop.md +24 -0
  214. package/scripts/install.sh +215 -0
@@ -0,0 +1,1435 @@
1
+ import { createHash } from "node:crypto";
2
+ import { EventEmitter } from "node:events";
3
+ import { genId, getDb, withObjectNodesFtsRepair } from "../db.js";
4
+ function json(value, fallback = {}) {
5
+ return JSON.stringify(value ?? fallback);
6
+ }
7
+ function parseJson(value, fallback) {
8
+ if (!value)
9
+ return fallback;
10
+ try {
11
+ return JSON.parse(value);
12
+ }
13
+ catch {
14
+ return fallback;
15
+ }
16
+ }
17
+ function hashPayload(payload) {
18
+ return createHash("sha256").update(JSON.stringify(payload ?? null)).digest("hex");
19
+ }
20
+ const sharedContextEventBus = new EventEmitter();
21
+ sharedContextEventBus.setMaxListeners(100);
22
+ export function getSharedContextEventBus() {
23
+ return sharedContextEventBus;
24
+ }
25
+ function emitSharedContextEvent(type, payload) {
26
+ sharedContextEventBus.emit("shared_context", {
27
+ type,
28
+ payload,
29
+ timestamp: new Date().toISOString(),
30
+ });
31
+ }
32
+ function parseSharedContextPeerRow(row) {
33
+ return {
34
+ peerId: row.peer_id,
35
+ product: row.product,
36
+ tenantId: row.tenant_id ?? null,
37
+ workspaceId: row.workspace_id ?? null,
38
+ surface: row.surface,
39
+ role: row.role,
40
+ capabilities: parseJson(row.capabilities_json, []),
41
+ contextScopes: parseJson(row.context_scopes_json, []),
42
+ status: row.status,
43
+ summary: parseJson(row.summary_json, {}),
44
+ metadata: parseJson(row.metadata_json, {}),
45
+ lastHeartbeatAt: row.last_heartbeat_at,
46
+ };
47
+ }
48
+ function parseSharedContextPacketRow(row) {
49
+ return {
50
+ contextId: row.context_id,
51
+ contextType: row.context_type,
52
+ producerPeerId: row.producer_peer_id,
53
+ tenantId: row.tenant_id ?? null,
54
+ workspaceId: row.workspace_id ?? null,
55
+ scope: parseJson(row.scope_json, []),
56
+ subject: row.subject,
57
+ summary: row.summary,
58
+ claims: parseJson(row.claims_json, []),
59
+ evidenceRefs: parseJson(row.evidence_refs_json, []),
60
+ stateSnapshot: parseJson(row.state_snapshot_json, {}),
61
+ timeWindow: parseJson(row.time_window_json, {}),
62
+ freshness: parseJson(row.freshness_json, {}),
63
+ permissions: parseJson(row.permissions_json, {}),
64
+ confidence: row.confidence ?? undefined,
65
+ lineage: parseJson(row.lineage_json, {}),
66
+ invalidates: parseJson(row.invalidates_json, []),
67
+ nextActions: parseJson(row.next_actions_json, []),
68
+ version: row.version,
69
+ status: row.status,
70
+ metadata: parseJson(row.metadata_json, {}),
71
+ };
72
+ }
73
+ function parseSharedContextTaskRow(row) {
74
+ return {
75
+ taskId: row.task_id,
76
+ taskType: row.task_type,
77
+ proposerPeerId: row.proposer_peer_id,
78
+ assigneePeerId: row.assignee_peer_id,
79
+ status: row.status,
80
+ taskSpec: parseJson(row.task_spec_json, {}),
81
+ inputContextIds: parseJson(row.input_context_ids_json, []),
82
+ outputContextId: row.output_context_id ?? null,
83
+ reason: row.reason ?? null,
84
+ createdAt: row.created_at,
85
+ updatedAt: row.updated_at,
86
+ };
87
+ }
88
+ function requireSharedContextPeer(peerId) {
89
+ const peer = getSharedContextPeer(peerId);
90
+ if (!peer) {
91
+ throw new Error(`Shared context peer not found: ${peerId}`);
92
+ }
93
+ return peer;
94
+ }
95
+ function normalizeVisibility(permissions) {
96
+ const visibility = permissions?.visibility;
97
+ if (visibility === "tenant" || visibility === "workspace" || visibility === "internal") {
98
+ return visibility;
99
+ }
100
+ return "workspace";
101
+ }
102
+ function assertPeersShareScope(sourcePeer, destinationPeer, action) {
103
+ const sourceWorkspace = sourcePeer.workspaceId ?? null;
104
+ const destinationWorkspace = destinationPeer.workspaceId ?? null;
105
+ const sourceTenant = sourcePeer.tenantId ?? null;
106
+ const destinationTenant = destinationPeer.tenantId ?? null;
107
+ if (sourceTenant && destinationTenant && sourceTenant !== destinationTenant) {
108
+ throw new Error(`${action} denied: peers do not share tenant scope`);
109
+ }
110
+ if (sourceWorkspace && destinationWorkspace && sourceWorkspace !== destinationWorkspace) {
111
+ throw new Error(`${action} denied: peers do not share workspace scope`);
112
+ }
113
+ }
114
+ function canPeerAccessPacket(peer, packet) {
115
+ const visibility = normalizeVisibility(packet.permissions);
116
+ const allowedRoles = Array.isArray(packet.permissions?.allowedRoles)
117
+ ? packet.permissions?.allowedRoles
118
+ : [];
119
+ if (packet.status === "invalidated")
120
+ return false;
121
+ if (allowedRoles.length > 0 && !allowedRoles.includes(peer.role))
122
+ return false;
123
+ if (packet.tenantId && peer.tenantId && packet.tenantId !== peer.tenantId)
124
+ return false;
125
+ if (packet.workspaceId && peer.workspaceId && packet.workspaceId !== peer.workspaceId)
126
+ return false;
127
+ if (visibility === "internal") {
128
+ return packet.producerPeerId === peer.peerId || ((!packet.workspaceId || packet.workspaceId === peer.workspaceId) &&
129
+ (!packet.tenantId || packet.tenantId === peer.tenantId) &&
130
+ peer.product === requireSharedContextPeer(packet.producerPeerId).product);
131
+ }
132
+ if (visibility === "tenant") {
133
+ if (packet.tenantId && peer.tenantId)
134
+ return packet.tenantId === peer.tenantId;
135
+ return !packet.workspaceId || packet.workspaceId === peer.workspaceId;
136
+ }
137
+ return !packet.workspaceId || packet.workspaceId === peer.workspaceId;
138
+ }
139
+ function requirePacketForPeer(contextId, peerId, action) {
140
+ const peer = requireSharedContextPeer(peerId);
141
+ const row = getDb().prepare(`
142
+ SELECT *
143
+ FROM shared_context_packets
144
+ WHERE context_id = ?
145
+ LIMIT 1
146
+ `).get(contextId);
147
+ if (!row) {
148
+ throw new Error(`Shared context packet not found: ${contextId}`);
149
+ }
150
+ const packet = parseSharedContextPacketRow(row);
151
+ if (!canPeerAccessPacket(peer, packet)) {
152
+ throw new Error(`${action} denied: peer ${peerId} cannot access packet ${contextId}`);
153
+ }
154
+ return packet;
155
+ }
156
+ export function upsertDurableObject(input) {
157
+ const db = getDb();
158
+ const now = new Date().toISOString();
159
+ const objectId = input.id ?? genId(String(input.kind));
160
+ const metadataJson = json(input.metadata);
161
+ withObjectNodesFtsRepair(() => {
162
+ db.prepare(`
163
+ INSERT INTO object_nodes (id, kind, label, source, status, metadata_json, created_at, updated_at)
164
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
165
+ ON CONFLICT(id) DO UPDATE SET
166
+ kind = excluded.kind,
167
+ label = excluded.label,
168
+ source = excluded.source,
169
+ status = excluded.status,
170
+ metadata_json = excluded.metadata_json,
171
+ updated_at = excluded.updated_at
172
+ `).run(objectId, input.kind, input.label, input.source ?? "local", input.status ?? "active", metadataJson, now, now);
173
+ });
174
+ const queuedSyncId = input.queueForSync === false
175
+ ? undefined
176
+ : enqueueSyncOperation({
177
+ objectId,
178
+ objectKind: input.kind,
179
+ opType: "upsert_object",
180
+ payload: {
181
+ id: objectId,
182
+ kind: input.kind,
183
+ label: input.label,
184
+ source: input.source ?? "local",
185
+ status: input.status ?? "active",
186
+ metadata: input.metadata ?? {},
187
+ },
188
+ }).queueId;
189
+ return { objectId, queuedSyncId };
190
+ }
191
+ export function linkDurableObjects(input) {
192
+ const db = getDb();
193
+ const edgeId = input.id ?? genId("edge");
194
+ db.prepare(`
195
+ INSERT INTO object_edges (id, from_id, to_id, edge_type, confidence, metadata_json, created_at)
196
+ VALUES (?, ?, ?, ?, ?, ?, ?)
197
+ ON CONFLICT(from_id, to_id, edge_type) DO UPDATE SET
198
+ confidence = excluded.confidence,
199
+ metadata_json = excluded.metadata_json
200
+ `).run(edgeId, input.fromId, input.toId, input.edgeType, input.confidence ?? 1, json(input.metadata), new Date().toISOString());
201
+ const queuedSyncId = input.queueForSync === false
202
+ ? undefined
203
+ : enqueueSyncOperation({
204
+ objectId: input.fromId,
205
+ objectKind: "workflow",
206
+ opType: "link_object",
207
+ payload: {
208
+ id: edgeId,
209
+ fromId: input.fromId,
210
+ toId: input.toId,
211
+ edgeType: input.edgeType,
212
+ confidence: input.confidence ?? 1,
213
+ metadata: input.metadata ?? {},
214
+ },
215
+ }).queueId;
216
+ return { edgeId, queuedSyncId };
217
+ }
218
+ export function recordExecutionReceipt(input) {
219
+ const db = getDb();
220
+ const receiptId = input.id ?? genId("receipt");
221
+ const inputHash = input.input === undefined ? null : hashPayload(input.input);
222
+ const outputHash = input.output === undefined ? null : hashPayload(input.output);
223
+ db.prepare(`
224
+ INSERT INTO execution_receipts (id, run_id, trace_id, step_id, object_id, tool_name, action_type, summary, input_hash, output_hash, status, metadata_json, created_at)
225
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
226
+ `).run(receiptId, input.runId ?? null, input.traceId ?? null, input.stepId ?? null, input.objectId ?? null, input.toolName ?? null, input.actionType, input.summary, inputHash, outputHash, input.status ?? "recorded", json({
227
+ ...(input.metadata ?? {}),
228
+ input: input.input ?? null,
229
+ output: input.output ?? null,
230
+ }), new Date().toISOString());
231
+ const queuedSyncId = input.queueForSync === false
232
+ ? undefined
233
+ : enqueueSyncOperation({
234
+ objectId: input.objectId ?? input.runId ?? receiptId,
235
+ objectKind: "trace",
236
+ opType: "record_receipt",
237
+ payload: {
238
+ id: receiptId,
239
+ runId: input.runId ?? null,
240
+ traceId: input.traceId ?? null,
241
+ stepId: input.stepId ?? null,
242
+ objectId: input.objectId ?? null,
243
+ toolName: input.toolName ?? null,
244
+ actionType: input.actionType,
245
+ summary: input.summary,
246
+ status: input.status ?? "recorded",
247
+ inputHash,
248
+ outputHash,
249
+ metadata: input.metadata ?? {},
250
+ },
251
+ }).queueId;
252
+ return { receiptId, queuedSyncId };
253
+ }
254
+ export function recordLocalArtifact(input) {
255
+ const db = getDb();
256
+ const artifactId = input.id ?? genId("artifact");
257
+ const contentHash = input.content ? hashPayload(input.content) : null;
258
+ db.prepare(`
259
+ INSERT INTO local_artifacts (id, run_id, object_id, kind, path, content_hash, summary, verification_status, metadata_json, created_at)
260
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
261
+ `).run(artifactId, input.runId ?? null, input.objectId ?? null, input.kind, input.path ?? null, contentHash, input.summary ?? null, input.verificationStatus ?? "unverified", json({
262
+ ...(input.metadata ?? {}),
263
+ contentPreview: typeof input.content === "string" ? input.content.slice(0, 500) : null,
264
+ }), new Date().toISOString());
265
+ const queuedSyncId = input.queueForSync === false
266
+ ? undefined
267
+ : enqueueSyncOperation({
268
+ objectId: input.objectId ?? artifactId,
269
+ objectKind: "artifact",
270
+ opType: "record_artifact",
271
+ payload: {
272
+ id: artifactId,
273
+ runId: input.runId ?? null,
274
+ objectId: input.objectId ?? null,
275
+ kind: input.kind,
276
+ path: input.path ?? null,
277
+ contentHash,
278
+ summary: input.summary ?? null,
279
+ verificationStatus: input.verificationStatus ?? "unverified",
280
+ metadata: input.metadata ?? {},
281
+ },
282
+ }).queueId;
283
+ return { artifactId, queuedSyncId };
284
+ }
285
+ export function recordLocalOutcome(input) {
286
+ const db = getDb();
287
+ const outcomeId = input.id ?? genId("outcome");
288
+ db.prepare(`
289
+ INSERT INTO local_outcomes (id, run_id, object_id, outcome_type, headline, user_value, stakeholder_value, status, evidence_json, metadata_json, created_at)
290
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
291
+ `).run(outcomeId, input.runId ?? null, input.objectId ?? null, input.outcomeType, input.headline, input.userValue ?? null, input.stakeholderValue ?? null, input.status ?? "draft", json(input.evidence ?? [], []), json(input.metadata), new Date().toISOString());
292
+ const queuedSyncId = input.queueForSync === false
293
+ ? undefined
294
+ : enqueueSyncOperation({
295
+ objectId: input.objectId ?? outcomeId,
296
+ objectKind: "outcome",
297
+ opType: "record_outcome",
298
+ payload: {
299
+ id: outcomeId,
300
+ runId: input.runId ?? null,
301
+ objectId: input.objectId ?? null,
302
+ outcomeType: input.outcomeType,
303
+ headline: input.headline,
304
+ userValue: input.userValue ?? null,
305
+ stakeholderValue: input.stakeholderValue ?? null,
306
+ status: input.status ?? "draft",
307
+ evidence: input.evidence ?? [],
308
+ metadata: input.metadata ?? {},
309
+ },
310
+ }).queueId;
311
+ return { outcomeId, queuedSyncId };
312
+ }
313
+ export function upsertDeviceBinding(input) {
314
+ const now = new Date().toISOString();
315
+ getDb().prepare(`
316
+ INSERT INTO device_bindings (device_id, device_name, platform, app_version, bridge_url, device_token, binding_status, metadata_json, paired_at, last_seen_at, created_at, updated_at)
317
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
318
+ ON CONFLICT(device_id) DO UPDATE SET
319
+ device_name = excluded.device_name,
320
+ platform = excluded.platform,
321
+ app_version = excluded.app_version,
322
+ bridge_url = excluded.bridge_url,
323
+ device_token = excluded.device_token,
324
+ binding_status = excluded.binding_status,
325
+ metadata_json = excluded.metadata_json,
326
+ paired_at = COALESCE(excluded.paired_at, device_bindings.paired_at),
327
+ last_seen_at = excluded.last_seen_at,
328
+ updated_at = excluded.updated_at
329
+ `).run(input.deviceId, input.deviceName, input.platform ?? null, input.appVersion ?? null, input.bridgeUrl ?? null, input.deviceToken ?? null, input.bindingStatus ?? "unpaired", json(input.metadata), input.bindingStatus === "paired" ? now : null, now, now, now);
330
+ }
331
+ export function bindDeviceToAccount(input) {
332
+ const bindingId = genId("acct");
333
+ const now = new Date().toISOString();
334
+ getDb().prepare(`
335
+ INSERT INTO account_bindings (id, device_id, user_id, workspace_id, scopes_json, sync_enabled, sync_mode, metadata_json, paired_at, created_at, updated_at)
336
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
337
+ `).run(bindingId, input.deviceId, input.userId, input.workspaceId ?? null, json(input.scopes, []), input.syncEnabled === false ? 0 : 1, input.syncMode ?? "connected", json(input.metadata), now, now, now);
338
+ return { bindingId };
339
+ }
340
+ export function getActiveAccountBinding(deviceId) {
341
+ const row = getDb().prepare(`
342
+ SELECT id, device_id, user_id, workspace_id, scopes_json, sync_enabled, sync_mode, last_synced_at, revoked_at
343
+ FROM account_bindings
344
+ WHERE (? IS NULL OR device_id = ?)
345
+ AND revoked_at IS NULL
346
+ ORDER BY created_at DESC
347
+ LIMIT 1
348
+ `).get(deviceId ?? null, deviceId ?? null);
349
+ if (!row)
350
+ return null;
351
+ return {
352
+ bindingId: row.id,
353
+ deviceId: row.device_id,
354
+ userId: row.user_id,
355
+ workspaceId: row.workspace_id ?? null,
356
+ scopes: parseJson(row.scopes_json, []),
357
+ syncEnabled: Boolean(row.sync_enabled),
358
+ syncMode: row.sync_mode,
359
+ lastSyncedAt: row.last_synced_at ?? null,
360
+ revokedAt: row.revoked_at ?? null,
361
+ };
362
+ }
363
+ export function getDeviceBinding(deviceId) {
364
+ const row = getDb().prepare(`
365
+ SELECT device_id, device_name, platform, app_version, bridge_url, device_token, binding_status, last_seen_at
366
+ FROM device_bindings
367
+ WHERE device_id = ?
368
+ LIMIT 1
369
+ `).get(deviceId);
370
+ if (!row)
371
+ return null;
372
+ return {
373
+ deviceId: row.device_id,
374
+ deviceName: row.device_name,
375
+ platform: row.platform ?? null,
376
+ appVersion: row.app_version ?? null,
377
+ bridgeUrl: row.bridge_url ?? null,
378
+ deviceToken: row.device_token ?? null,
379
+ bindingStatus: row.binding_status,
380
+ lastSeenAt: row.last_seen_at ?? null,
381
+ };
382
+ }
383
+ export function enqueueSyncOperation(input) {
384
+ const queueId = genId("sync");
385
+ const payloadHash = hashPayload(input.payload);
386
+ const now = new Date().toISOString();
387
+ getDb().prepare(`
388
+ INSERT INTO sync_queue (id, object_id, object_kind, op_type, payload_json, payload_hash, sync_status, created_at, updated_at)
389
+ VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?)
390
+ `).run(queueId, input.objectId, input.objectKind, input.opType, json(input.payload), payloadHash, now, now);
391
+ return { queueId, payloadHash };
392
+ }
393
+ export function listPendingSyncOperations(limit = 50) {
394
+ const rows = getDb().prepare(`
395
+ SELECT id, object_id, object_kind, op_type, payload_json, payload_hash, created_at
396
+ FROM sync_queue
397
+ WHERE sync_status IN ('pending', 'retry')
398
+ ORDER BY created_at ASC
399
+ LIMIT ?
400
+ `).all(limit);
401
+ return rows.map((row) => ({
402
+ id: row.id,
403
+ objectId: row.object_id ?? null,
404
+ objectKind: row.object_kind,
405
+ opType: row.op_type,
406
+ payload: parseJson(row.payload_json, {}),
407
+ payloadHash: row.payload_hash,
408
+ createdAt: row.created_at,
409
+ }));
410
+ }
411
+ export function markSyncAttempt(queueIds, error) {
412
+ if (queueIds.length === 0)
413
+ return;
414
+ const db = getDb();
415
+ const placeholders = queueIds.map(() => "?").join(", ");
416
+ const now = new Date().toISOString();
417
+ db.prepare(`
418
+ UPDATE sync_queue
419
+ SET sync_status = ?, retry_count = retry_count + 1, last_error = ?, last_attempt_at = ?, updated_at = ?
420
+ WHERE id IN (${placeholders})
421
+ `).run(error ? "retry" : "in_flight", error ?? null, now, now, ...queueIds);
422
+ }
423
+ export function acknowledgeSyncOperations(args) {
424
+ if (args.queueIds.length === 0)
425
+ return;
426
+ const db = getDb();
427
+ const placeholders = args.queueIds.map(() => "?").join(", ");
428
+ const now = new Date().toISOString();
429
+ db.prepare(`
430
+ UPDATE sync_queue
431
+ SET sync_status = 'acknowledged', acknowledged_at = ?, last_attempt_at = ?, updated_at = ?, last_error = NULL
432
+ WHERE id IN (${placeholders})
433
+ `).run(now, now, now, ...args.queueIds);
434
+ const insertReceipt = db.prepare(`
435
+ INSERT INTO sync_receipts (id, queue_id, server_receipt_id, device_id, user_id, workspace_id, status, detail_json, created_at)
436
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
437
+ `);
438
+ for (const queueId of args.queueIds) {
439
+ insertReceipt.run(genId("sync_receipt"), queueId, args.serverReceiptId ?? null, args.deviceId ?? null, args.userId ?? null, args.workspaceId ?? null, "acknowledged", json(args.detail), now);
440
+ }
441
+ if (args.deviceId && args.userId) {
442
+ db.prepare(`
443
+ UPDATE account_bindings
444
+ SET last_synced_at = ?, updated_at = ?
445
+ WHERE device_id = ? AND user_id = ? AND revoked_at IS NULL
446
+ `).run(now, now, args.deviceId, args.userId);
447
+ }
448
+ }
449
+ export function failSyncOperations(args) {
450
+ if (args.rejected.length === 0)
451
+ return;
452
+ const db = getDb();
453
+ const now = new Date().toISOString();
454
+ const update = db.prepare(`
455
+ UPDATE sync_queue
456
+ SET sync_status = 'retry', retry_count = retry_count + 1, last_error = ?, last_attempt_at = ?, updated_at = ?
457
+ WHERE id = ?
458
+ `);
459
+ const insertReceipt = db.prepare(`
460
+ INSERT INTO sync_receipts (id, queue_id, device_id, user_id, workspace_id, status, detail_json, created_at)
461
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
462
+ `);
463
+ for (const rejected of args.rejected) {
464
+ update.run(rejected.reason, now, now, rejected.id);
465
+ insertReceipt.run(genId("sync_receipt"), rejected.id, args.deviceId ?? null, args.userId ?? null, args.workspaceId ?? null, "rejected", json({ reason: rejected.reason }), now);
466
+ }
467
+ }
468
+ export function getSyncBridgeStatus(deviceId) {
469
+ const db = getDb();
470
+ const counts = db.prepare(`
471
+ SELECT
472
+ SUM(CASE WHEN sync_status = 'pending' THEN 1 ELSE 0 END) AS pending_count,
473
+ SUM(CASE WHEN sync_status = 'retry' THEN 1 ELSE 0 END) AS retry_count,
474
+ SUM(CASE WHEN sync_status = 'acknowledged' THEN 1 ELSE 0 END) AS acknowledged_count,
475
+ MAX(acknowledged_at) AS last_acknowledged_at
476
+ FROM sync_queue
477
+ `).get();
478
+ const activeBinding = getActiveAccountBinding(deviceId);
479
+ return {
480
+ mode: activeBinding && activeBinding.syncEnabled ? "connected" : "offline",
481
+ activeBinding,
482
+ pendingCount: counts?.pending_count ?? 0,
483
+ retryCount: counts?.retry_count ?? 0,
484
+ acknowledgedCount: counts?.acknowledged_count ?? 0,
485
+ lastAcknowledgedAt: counts?.last_acknowledged_at ?? null,
486
+ };
487
+ }
488
+ export function registerSharedContextPeer(input) {
489
+ const db = getDb();
490
+ const now = new Date().toISOString();
491
+ const peerId = input.peerId ?? genId("peer");
492
+ db.prepare(`
493
+ INSERT INTO shared_context_peers (
494
+ peer_id, product, tenant_id, workspace_id, surface, role, capabilities_json,
495
+ context_scopes_json, status, summary_json, metadata_json, last_heartbeat_at, created_at, updated_at
496
+ )
497
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
498
+ ON CONFLICT(peer_id) DO UPDATE SET
499
+ product = excluded.product,
500
+ tenant_id = excluded.tenant_id,
501
+ workspace_id = excluded.workspace_id,
502
+ surface = excluded.surface,
503
+ role = excluded.role,
504
+ capabilities_json = excluded.capabilities_json,
505
+ context_scopes_json = excluded.context_scopes_json,
506
+ status = excluded.status,
507
+ summary_json = excluded.summary_json,
508
+ metadata_json = excluded.metadata_json,
509
+ last_heartbeat_at = excluded.last_heartbeat_at,
510
+ updated_at = excluded.updated_at
511
+ `).run(peerId, input.product, input.tenantId ?? null, input.workspaceId ?? null, input.surface, input.role, json(input.capabilities ?? [], []), json(input.contextScopes ?? [], []), input.status ?? "active", json(input.summary ?? {}, {}), json(input.metadata), now, now, now);
512
+ upsertDurableObject({
513
+ id: peerId,
514
+ kind: "peer",
515
+ label: `${input.product}:${input.role}:${input.surface}`,
516
+ source: "local",
517
+ status: input.status ?? "active",
518
+ metadata: {
519
+ product: input.product,
520
+ tenantId: input.tenantId ?? null,
521
+ workspaceId: input.workspaceId ?? null,
522
+ surface: input.surface,
523
+ role: input.role,
524
+ capabilities: input.capabilities ?? [],
525
+ contextScopes: input.contextScopes ?? [],
526
+ summary: input.summary ?? {},
527
+ ...(input.metadata ?? {}),
528
+ },
529
+ queueForSync: false,
530
+ });
531
+ const queuedSyncId = input.queueForSync === false
532
+ ? undefined
533
+ : enqueueSyncOperation({
534
+ objectId: peerId,
535
+ objectKind: "peer",
536
+ opType: "register_peer",
537
+ payload: {
538
+ peerId,
539
+ product: input.product,
540
+ tenantId: input.tenantId ?? null,
541
+ workspaceId: input.workspaceId ?? null,
542
+ surface: input.surface,
543
+ role: input.role,
544
+ capabilities: input.capabilities ?? [],
545
+ contextScopes: input.contextScopes ?? [],
546
+ status: input.status ?? "active",
547
+ summary: input.summary ?? {},
548
+ metadata: input.metadata ?? {},
549
+ lastHeartbeatAt: now,
550
+ },
551
+ }).queueId;
552
+ emitSharedContextEvent("peer_registered", {
553
+ peerId,
554
+ product: input.product,
555
+ workspaceId: input.workspaceId ?? null,
556
+ role: input.role,
557
+ surface: input.surface,
558
+ status: input.status ?? "active",
559
+ });
560
+ return { peerId, queuedSyncId };
561
+ }
562
+ export function heartbeatSharedContextPeer(peerId, summary) {
563
+ const now = new Date().toISOString();
564
+ getDb().prepare(`
565
+ UPDATE shared_context_peers
566
+ SET status = 'active',
567
+ summary_json = COALESCE(?, summary_json),
568
+ last_heartbeat_at = ?,
569
+ updated_at = ?
570
+ WHERE peer_id = ?
571
+ `).run(summary ? json(summary, {}) : null, now, now, peerId);
572
+ const existing = getSharedContextPeer(peerId);
573
+ if (existing) {
574
+ upsertDurableObject({
575
+ id: peerId,
576
+ kind: "peer",
577
+ label: `${existing.product}:${existing.role}:${existing.surface}`,
578
+ source: "local",
579
+ status: "active",
580
+ metadata: {
581
+ product: existing.product,
582
+ tenantId: existing.tenantId ?? null,
583
+ workspaceId: existing.workspaceId ?? null,
584
+ surface: existing.surface,
585
+ role: existing.role,
586
+ capabilities: existing.capabilities,
587
+ contextScopes: existing.contextScopes,
588
+ summary: summary ?? existing.summary,
589
+ ...(existing.metadata ?? {}),
590
+ },
591
+ queueForSync: false,
592
+ });
593
+ }
594
+ emitSharedContextEvent("peer_heartbeat", {
595
+ peerId,
596
+ summary: summary ?? existing?.summary ?? {},
597
+ lastHeartbeatAt: now,
598
+ });
599
+ return { peerId, lastHeartbeatAt: now };
600
+ }
601
+ export function listSharedContextPeers(filters = {}) {
602
+ const rows = getDb().prepare(`
603
+ SELECT *
604
+ FROM shared_context_peers
605
+ WHERE (? IS NULL OR product = ?)
606
+ AND (? IS NULL OR workspace_id = ?)
607
+ AND (? IS NULL OR tenant_id = ?)
608
+ AND (? IS NULL OR role = ?)
609
+ AND (? IS NULL OR surface = ?)
610
+ AND (? IS NULL OR status = ?)
611
+ AND (? IS NULL OR capabilities_json LIKE '%' || ? || '%')
612
+ AND (? IS NULL OR context_scopes_json LIKE '%' || ? || '%')
613
+ ORDER BY last_heartbeat_at DESC
614
+ LIMIT ?
615
+ `).all(filters.product ?? null, filters.product ?? null, filters.workspaceId ?? null, filters.workspaceId ?? null, filters.tenantId ?? null, filters.tenantId ?? null, filters.role ?? null, filters.role ?? null, filters.surface ?? null, filters.surface ?? null, filters.status ?? null, filters.status ?? null, filters.capability ?? null, filters.capability ?? null, filters.scope ?? null, filters.scope ?? null, filters.limit ?? 50);
616
+ return rows.map(parseSharedContextPeerRow);
617
+ }
618
+ export function getSharedContextPeer(peerId) {
619
+ const row = getDb().prepare(`
620
+ SELECT *
621
+ FROM shared_context_peers
622
+ WHERE peer_id = ?
623
+ LIMIT 1
624
+ `).get(peerId);
625
+ return row ? parseSharedContextPeerRow(row) : null;
626
+ }
627
+ export function getSharedContextPacket(contextId, requestingPeerId) {
628
+ const row = getDb().prepare(`
629
+ SELECT *
630
+ FROM shared_context_packets
631
+ WHERE context_id = ?
632
+ LIMIT 1
633
+ `).get(contextId);
634
+ if (!row)
635
+ return null;
636
+ const packet = parseSharedContextPacketRow(row);
637
+ if (!requestingPeerId)
638
+ return packet;
639
+ const peer = requireSharedContextPeer(requestingPeerId);
640
+ return canPeerAccessPacket(peer, packet) ? packet : null;
641
+ }
642
+ function getPrimaryPacketScope(packet) {
643
+ return packet.scope.find((scope) => scope !== "workspace");
644
+ }
645
+ export function buildSharedContextPacketResource(packet, requestingPeerId) {
646
+ const primaryScope = getPrimaryPacketScope(packet);
647
+ return {
648
+ packet,
649
+ resourceUri: `shared-context://packet/${encodeURIComponent(packet.contextId)}`,
650
+ pullQuery: {
651
+ contextType: packet.contextType,
652
+ producerPeerId: packet.producerPeerId,
653
+ workspaceId: packet.workspaceId ?? undefined,
654
+ tenantId: packet.tenantId ?? undefined,
655
+ scopeIncludes: primaryScope,
656
+ subjectIncludes: packet.subject,
657
+ },
658
+ subscriptionQuery: {
659
+ peerId: requestingPeerId ?? undefined,
660
+ workspaceId: packet.workspaceId ?? undefined,
661
+ contextType: packet.contextType,
662
+ producerPeerId: packet.producerPeerId,
663
+ scopeIncludes: primaryScope,
664
+ subjectIncludes: packet.subject,
665
+ eventTypes: [
666
+ "packet_published",
667
+ "packet_invalidated",
668
+ "packet_acknowledged",
669
+ "task_proposed",
670
+ "task_status_changed",
671
+ ],
672
+ },
673
+ };
674
+ }
675
+ export function getSharedContextPacketResource(contextId, requestingPeerId) {
676
+ const packet = getSharedContextPacket(contextId, requestingPeerId);
677
+ if (!packet)
678
+ return null;
679
+ return buildSharedContextPacketResource(packet, requestingPeerId);
680
+ }
681
+ function normalizeSubscriptionEventTypes(eventTypes) {
682
+ return eventTypes && eventTypes.length > 0
683
+ ? Array.from(new Set(eventTypes))
684
+ : [
685
+ "packet_published",
686
+ "packet_invalidated",
687
+ "packet_acknowledged",
688
+ "task_proposed",
689
+ "task_status_changed",
690
+ "message_sent",
691
+ ];
692
+ }
693
+ function taskMatchesSubscription(task, packets, filters) {
694
+ if (filters.taskType && task.taskType !== filters.taskType)
695
+ return false;
696
+ if (filters.peerId && task.proposerPeerId !== filters.peerId && task.assigneePeerId !== filters.peerId) {
697
+ const relatedPacket = task.outputContextId
698
+ ? packets.some((packet) => packet.contextId === task.outputContextId)
699
+ : false;
700
+ if (!relatedPacket)
701
+ return false;
702
+ }
703
+ if (!filters.workspaceId)
704
+ return true;
705
+ const packetWorkspaceMatch = packets.some((packet) => packet.contextId === task.outputContextId || task.inputContextIds.includes(packet.contextId));
706
+ if (packetWorkspaceMatch)
707
+ return true;
708
+ return false;
709
+ }
710
+ function messageMatchesSubscription(message, peers, filters) {
711
+ if (filters.messageClass && message.messageClass !== filters.messageClass)
712
+ return false;
713
+ if (filters.peerId && message.fromPeerId !== filters.peerId && message.toPeerId !== filters.peerId) {
714
+ return false;
715
+ }
716
+ if (!filters.workspaceId)
717
+ return true;
718
+ return peers.some((peer) => peer.workspaceId === filters.workspaceId &&
719
+ (peer.peerId === message.fromPeerId || peer.peerId === message.toPeerId));
720
+ }
721
+ export function buildSharedContextSubscriptionManifest(query = {}) {
722
+ const limit = query.limit ?? 10;
723
+ const packets = pullSharedContextPackets({
724
+ contextType: query.contextType,
725
+ producerPeerId: query.producerPeerId,
726
+ requestingPeerId: query.peerId ?? query.requestingPeerId,
727
+ tenantId: query.tenantId,
728
+ workspaceId: query.workspaceId,
729
+ status: query.status,
730
+ scopeIncludes: query.scopeIncludes,
731
+ subjectIncludes: query.subjectIncludes,
732
+ limit,
733
+ });
734
+ const packetResources = packets.slice(0, limit).map((packet) => {
735
+ const resource = buildSharedContextPacketResource(packet, query.peerId ?? query.requestingPeerId);
736
+ return {
737
+ contextId: packet.contextId,
738
+ contextType: packet.contextType,
739
+ subject: packet.subject,
740
+ resourceUri: resource.resourceUri,
741
+ };
742
+ });
743
+ return {
744
+ peerId: query.peerId ?? query.requestingPeerId,
745
+ snapshotQuery: {
746
+ limit,
747
+ peerId: query.peerId ?? query.requestingPeerId,
748
+ workspaceId: query.workspaceId ?? undefined,
749
+ contextType: query.contextType,
750
+ producerPeerId: query.producerPeerId,
751
+ scopeIncludes: query.scopeIncludes,
752
+ subjectIncludes: query.subjectIncludes,
753
+ taskType: query.taskType,
754
+ messageClass: query.messageClass,
755
+ },
756
+ pullQuery: {
757
+ contextType: query.contextType,
758
+ producerPeerId: query.producerPeerId,
759
+ requestingPeerId: query.peerId ?? query.requestingPeerId,
760
+ tenantId: query.tenantId,
761
+ workspaceId: query.workspaceId,
762
+ status: query.status,
763
+ scopeIncludes: query.scopeIncludes,
764
+ subjectIncludes: query.subjectIncludes,
765
+ limit,
766
+ },
767
+ subscriptionQuery: {
768
+ peerId: query.peerId ?? query.requestingPeerId,
769
+ workspaceId: query.workspaceId ?? undefined,
770
+ contextType: query.contextType,
771
+ producerPeerId: query.producerPeerId,
772
+ scopeIncludes: query.scopeIncludes,
773
+ subjectIncludes: query.subjectIncludes,
774
+ taskType: query.taskType,
775
+ messageClass: query.messageClass,
776
+ eventTypes: normalizeSubscriptionEventTypes(query.eventTypes),
777
+ },
778
+ packetResources,
779
+ };
780
+ }
781
+ export function publishSharedContextPacket(input) {
782
+ const db = getDb();
783
+ const now = new Date().toISOString();
784
+ const contextId = input.contextId ?? genId("context");
785
+ const producerPeer = requireSharedContextPeer(input.producerPeerId);
786
+ const workspaceId = input.workspaceId ?? producerPeer.workspaceId ?? null;
787
+ const tenantId = input.tenantId ?? producerPeer.tenantId ?? null;
788
+ const permissions = {
789
+ visibility: "workspace",
790
+ ...(input.permissions ?? {}),
791
+ };
792
+ if (producerPeer.workspaceId && workspaceId && producerPeer.workspaceId !== workspaceId) {
793
+ throw new Error(`publish_shared_context denied: producer peer ${input.producerPeerId} cannot publish outside its workspace`);
794
+ }
795
+ if (producerPeer.tenantId && tenantId && producerPeer.tenantId !== tenantId) {
796
+ throw new Error(`publish_shared_context denied: producer peer ${input.producerPeerId} cannot publish outside its tenant`);
797
+ }
798
+ db.prepare(`
799
+ INSERT INTO shared_context_packets (
800
+ context_id, context_type, producer_peer_id, tenant_id, workspace_id, scope_json, subject, summary,
801
+ claims_json, evidence_refs_json, state_snapshot_json, time_window_json, freshness_json, permissions_json,
802
+ confidence, lineage_json, invalidates_json, next_actions_json, version, status, metadata_json, created_at, updated_at
803
+ )
804
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
805
+ ON CONFLICT(context_id) DO UPDATE SET
806
+ context_type = excluded.context_type,
807
+ producer_peer_id = excluded.producer_peer_id,
808
+ tenant_id = excluded.tenant_id,
809
+ workspace_id = excluded.workspace_id,
810
+ scope_json = excluded.scope_json,
811
+ subject = excluded.subject,
812
+ summary = excluded.summary,
813
+ claims_json = excluded.claims_json,
814
+ evidence_refs_json = excluded.evidence_refs_json,
815
+ state_snapshot_json = excluded.state_snapshot_json,
816
+ time_window_json = excluded.time_window_json,
817
+ freshness_json = excluded.freshness_json,
818
+ permissions_json = excluded.permissions_json,
819
+ confidence = excluded.confidence,
820
+ lineage_json = excluded.lineage_json,
821
+ invalidates_json = excluded.invalidates_json,
822
+ next_actions_json = excluded.next_actions_json,
823
+ version = excluded.version,
824
+ status = excluded.status,
825
+ metadata_json = excluded.metadata_json,
826
+ updated_at = excluded.updated_at
827
+ `).run(contextId, input.contextType, input.producerPeerId, tenantId, workspaceId, json(input.scope ?? [], []), input.subject, input.summary, json(input.claims ?? [], []), json(input.evidenceRefs ?? [], []), json(input.stateSnapshot ?? {}, {}), json(input.timeWindow ?? {}, {}), json(input.freshness ?? {}, {}), json(permissions, {}), input.confidence ?? null, json(input.lineage ?? {}, {}), json(input.invalidates ?? [], []), json(input.nextActions ?? [], []), input.version ?? 1, input.status ?? "active", json(input.metadata), now, now);
828
+ if ((input.invalidates ?? []).length > 0) {
829
+ const invalidate = db.prepare(`
830
+ UPDATE shared_context_packets
831
+ SET status = 'invalidated', updated_at = ?
832
+ WHERE context_id = ?
833
+ `);
834
+ for (const invalidatedId of input.invalidates ?? []) {
835
+ invalidate.run(now, invalidatedId);
836
+ }
837
+ }
838
+ upsertDurableObject({
839
+ id: contextId,
840
+ kind: "context_packet",
841
+ label: input.subject,
842
+ source: "local",
843
+ status: input.status ?? "active",
844
+ metadata: {
845
+ contextType: input.contextType,
846
+ producerPeerId: input.producerPeerId,
847
+ tenantId,
848
+ workspaceId,
849
+ scope: input.scope ?? [],
850
+ summary: input.summary,
851
+ claims: input.claims ?? [],
852
+ evidenceRefs: input.evidenceRefs ?? [],
853
+ freshness: input.freshness ?? {},
854
+ permissions,
855
+ confidence: input.confidence ?? null,
856
+ lineage: input.lineage ?? {},
857
+ invalidates: input.invalidates ?? [],
858
+ nextActions: input.nextActions ?? [],
859
+ version: input.version ?? 1,
860
+ ...(input.metadata ?? {}),
861
+ },
862
+ queueForSync: false,
863
+ });
864
+ linkDurableObjects({
865
+ fromId: input.producerPeerId,
866
+ toId: contextId,
867
+ edgeType: "produced_context",
868
+ metadata: { contextType: input.contextType },
869
+ queueForSync: false,
870
+ });
871
+ for (const invalidatedId of input.invalidates ?? []) {
872
+ linkDurableObjects({
873
+ fromId: contextId,
874
+ toId: invalidatedId,
875
+ edgeType: "invalidates",
876
+ queueForSync: false,
877
+ });
878
+ }
879
+ const queuedSyncId = input.queueForSync === false
880
+ ? undefined
881
+ : enqueueSyncOperation({
882
+ objectId: contextId,
883
+ objectKind: "context_packet",
884
+ opType: "publish_context",
885
+ payload: {
886
+ contextId,
887
+ contextType: input.contextType,
888
+ producerPeerId: input.producerPeerId,
889
+ tenantId,
890
+ workspaceId,
891
+ scope: input.scope ?? [],
892
+ subject: input.subject,
893
+ summary: input.summary,
894
+ claims: input.claims ?? [],
895
+ evidenceRefs: input.evidenceRefs ?? [],
896
+ stateSnapshot: input.stateSnapshot ?? {},
897
+ timeWindow: input.timeWindow ?? {},
898
+ freshness: input.freshness ?? {},
899
+ permissions,
900
+ confidence: input.confidence ?? null,
901
+ lineage: input.lineage ?? {},
902
+ invalidates: input.invalidates ?? [],
903
+ nextActions: input.nextActions ?? [],
904
+ version: input.version ?? 1,
905
+ status: input.status ?? "active",
906
+ metadata: input.metadata ?? {},
907
+ },
908
+ }).queueId;
909
+ emitSharedContextEvent("packet_published", {
910
+ contextId,
911
+ producerPeerId: input.producerPeerId,
912
+ contextType: input.contextType,
913
+ workspaceId,
914
+ tenantId,
915
+ status: input.status ?? "active",
916
+ });
917
+ return { contextId, queuedSyncId };
918
+ }
919
+ export function pullSharedContextPackets(query = {}) {
920
+ const rows = getDb().prepare(`
921
+ SELECT *
922
+ FROM shared_context_packets
923
+ WHERE (? IS NULL OR context_type = ?)
924
+ AND (? IS NULL OR producer_peer_id = ?)
925
+ AND (? IS NULL OR tenant_id = ?)
926
+ AND (? IS NULL OR workspace_id = ?)
927
+ AND (? IS NULL OR status = ?)
928
+ AND (? IS NULL OR scope_json LIKE '%' || ? || '%')
929
+ AND (? IS NULL OR subject LIKE '%' || ? || '%')
930
+ ORDER BY created_at DESC
931
+ LIMIT ?
932
+ `).all(query.contextType ?? null, query.contextType ?? null, query.producerPeerId ?? null, query.producerPeerId ?? null, query.tenantId ?? null, query.tenantId ?? null, query.workspaceId ?? null, query.workspaceId ?? null, query.status ?? null, query.status ?? null, query.scopeIncludes ?? null, query.scopeIncludes ?? null, query.subjectIncludes ?? null, query.subjectIncludes ?? null, query.limit ?? 50);
933
+ const packets = rows.map(parseSharedContextPacketRow);
934
+ if (!query.requestingPeerId)
935
+ return packets;
936
+ const peer = requireSharedContextPeer(query.requestingPeerId);
937
+ return packets.filter((packet) => canPeerAccessPacket(peer, packet));
938
+ }
939
+ export function acknowledgeSharedContextPacket(contextId, peerId, detail) {
940
+ requirePacketForPeer(contextId, peerId, "ack_shared_context");
941
+ const ackId = genId("context_ack");
942
+ const now = new Date().toISOString();
943
+ getDb().prepare(`
944
+ INSERT INTO shared_context_packet_acks (id, context_id, peer_id, ack_status, detail_json, created_at)
945
+ VALUES (?, ?, ?, 'acknowledged', ?, ?)
946
+ ON CONFLICT(context_id, peer_id) DO UPDATE SET
947
+ ack_status = 'acknowledged',
948
+ detail_json = excluded.detail_json,
949
+ created_at = excluded.created_at
950
+ `).run(ackId, contextId, peerId, json(detail), now);
951
+ const queuedSyncId = enqueueSyncOperation({
952
+ objectId: contextId,
953
+ objectKind: "context_packet",
954
+ opType: "ack_context",
955
+ payload: {
956
+ ackId,
957
+ contextId,
958
+ peerId,
959
+ detail: detail ?? {},
960
+ acknowledgedAt: now,
961
+ },
962
+ }).queueId;
963
+ emitSharedContextEvent("packet_acknowledged", {
964
+ contextId,
965
+ peerId,
966
+ acknowledgedAt: now,
967
+ });
968
+ return { ackId, queuedSyncId };
969
+ }
970
+ export function invalidateSharedContextPacket(contextId, producerPeerId, reason, invalidates = []) {
971
+ const now = new Date().toISOString();
972
+ const packet = requirePacketForPeer(contextId, producerPeerId, "invalidate_shared_context");
973
+ if (packet.producerPeerId !== producerPeerId) {
974
+ throw new Error(`invalidate_shared_context denied: only the producer peer can invalidate packet ${contextId}`);
975
+ }
976
+ getDb().prepare(`
977
+ UPDATE shared_context_packets
978
+ SET status = 'invalidated',
979
+ invalidates_json = ?,
980
+ metadata_json = json_set(metadata_json, '$.invalidationReason', ?),
981
+ updated_at = ?
982
+ WHERE context_id = ? AND producer_peer_id = ?
983
+ `).run(json(invalidates, []), reason, now, contextId, producerPeerId);
984
+ upsertDurableObject({
985
+ id: contextId,
986
+ kind: "context_packet",
987
+ label: getDb().prepare("SELECT subject FROM shared_context_packets WHERE context_id = ?").get(contextId)?.subject ?? contextId,
988
+ source: "local",
989
+ status: "invalidated",
990
+ metadata: {
991
+ invalidationReason: reason,
992
+ invalidates,
993
+ },
994
+ queueForSync: false,
995
+ });
996
+ const queuedSyncId = enqueueSyncOperation({
997
+ objectId: contextId,
998
+ objectKind: "context_packet",
999
+ opType: "invalidate_context",
1000
+ payload: {
1001
+ contextId,
1002
+ producerPeerId,
1003
+ reason,
1004
+ invalidates,
1005
+ invalidatedAt: now,
1006
+ },
1007
+ }).queueId;
1008
+ emitSharedContextEvent("packet_invalidated", {
1009
+ contextId,
1010
+ producerPeerId,
1011
+ reason,
1012
+ invalidates,
1013
+ invalidatedAt: now,
1014
+ });
1015
+ return { contextId, queuedSyncId };
1016
+ }
1017
+ export function sendSharedContextMessage(input) {
1018
+ const fromPeer = requireSharedContextPeer(input.fromPeerId);
1019
+ const toPeer = requireSharedContextPeer(input.toPeerId);
1020
+ assertPeersShareScope(fromPeer, toPeer, "send_peer_message");
1021
+ const referencedContextId = typeof input.payload.contextId === "string" ? input.payload.contextId : null;
1022
+ if (referencedContextId) {
1023
+ requirePacketForPeer(referencedContextId, input.fromPeerId, "send_peer_message");
1024
+ requirePacketForPeer(referencedContextId, input.toPeerId, "send_peer_message");
1025
+ }
1026
+ const messageId = genId("message");
1027
+ const now = new Date().toISOString();
1028
+ getDb().prepare(`
1029
+ INSERT INTO shared_context_messages (id, from_peer_id, to_peer_id, message_class, payload_json, status, created_at)
1030
+ VALUES (?, ?, ?, ?, ?, 'unread', ?)
1031
+ `).run(messageId, input.fromPeerId, input.toPeerId, input.messageClass, json(input.payload), now);
1032
+ upsertDurableObject({
1033
+ id: messageId,
1034
+ kind: "message",
1035
+ label: `${input.messageClass}:${input.fromPeerId}->${input.toPeerId}`,
1036
+ source: "local",
1037
+ status: "unread",
1038
+ metadata: {
1039
+ fromPeerId: input.fromPeerId,
1040
+ toPeerId: input.toPeerId,
1041
+ messageClass: input.messageClass,
1042
+ payload: input.payload,
1043
+ },
1044
+ queueForSync: false,
1045
+ });
1046
+ linkDurableObjects({
1047
+ fromId: input.fromPeerId,
1048
+ toId: messageId,
1049
+ edgeType: "sent_message",
1050
+ queueForSync: false,
1051
+ });
1052
+ linkDurableObjects({
1053
+ fromId: messageId,
1054
+ toId: input.toPeerId,
1055
+ edgeType: "addressed_to",
1056
+ queueForSync: false,
1057
+ });
1058
+ const queuedSyncId = input.queueForSync === false
1059
+ ? undefined
1060
+ : enqueueSyncOperation({
1061
+ objectId: messageId,
1062
+ objectKind: "message",
1063
+ opType: "send_peer_message",
1064
+ payload: {
1065
+ messageId,
1066
+ fromPeerId: input.fromPeerId,
1067
+ toPeerId: input.toPeerId,
1068
+ messageClass: input.messageClass,
1069
+ payload: input.payload,
1070
+ createdAt: now,
1071
+ },
1072
+ }).queueId;
1073
+ emitSharedContextEvent("message_sent", {
1074
+ messageId,
1075
+ fromPeerId: input.fromPeerId,
1076
+ toPeerId: input.toPeerId,
1077
+ messageClass: input.messageClass,
1078
+ createdAt: now,
1079
+ });
1080
+ return { messageId, queuedSyncId };
1081
+ }
1082
+ export function listSharedContextMessages(peerId, unreadOnly = false) {
1083
+ const rows = getDb().prepare(`
1084
+ SELECT id, from_peer_id, to_peer_id, message_class, payload_json, status, created_at
1085
+ FROM shared_context_messages
1086
+ WHERE to_peer_id = ?
1087
+ AND (? = 0 OR status = 'unread')
1088
+ ORDER BY created_at DESC
1089
+ `).all(peerId, unreadOnly ? 1 : 0);
1090
+ return rows.map((row) => ({
1091
+ messageId: row.id,
1092
+ fromPeerId: row.from_peer_id,
1093
+ toPeerId: row.to_peer_id,
1094
+ messageClass: row.message_class,
1095
+ payload: parseJson(row.payload_json, {}),
1096
+ status: row.status,
1097
+ createdAt: row.created_at,
1098
+ }));
1099
+ }
1100
+ export function proposeSharedContextTask(input) {
1101
+ const db = getDb();
1102
+ const now = new Date().toISOString();
1103
+ const taskId = input.taskId ?? genId("task");
1104
+ const proposerPeer = requireSharedContextPeer(input.proposerPeerId);
1105
+ const assigneePeer = requireSharedContextPeer(input.assigneePeerId);
1106
+ assertPeersShareScope(proposerPeer, assigneePeer, "propose_shared_task");
1107
+ for (const contextId of input.inputContextIds ?? []) {
1108
+ requirePacketForPeer(contextId, input.proposerPeerId, "propose_shared_task");
1109
+ requirePacketForPeer(contextId, input.assigneePeerId, "propose_shared_task");
1110
+ }
1111
+ db.prepare(`
1112
+ INSERT INTO shared_context_tasks (
1113
+ task_id, task_type, proposer_peer_id, assignee_peer_id, status,
1114
+ task_spec_json, input_context_ids_json, output_context_id, reason, metadata_json, created_at, updated_at
1115
+ )
1116
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1117
+ ON CONFLICT(task_id) DO UPDATE SET
1118
+ task_type = excluded.task_type,
1119
+ proposer_peer_id = excluded.proposer_peer_id,
1120
+ assignee_peer_id = excluded.assignee_peer_id,
1121
+ status = excluded.status,
1122
+ task_spec_json = excluded.task_spec_json,
1123
+ input_context_ids_json = excluded.input_context_ids_json,
1124
+ output_context_id = excluded.output_context_id,
1125
+ reason = excluded.reason,
1126
+ metadata_json = excluded.metadata_json,
1127
+ updated_at = excluded.updated_at
1128
+ `).run(taskId, input.taskType, input.proposerPeerId, input.assigneePeerId, input.status ?? "proposed", json(input.taskSpec ?? {}, {}), json(input.inputContextIds ?? [], []), input.outputContextId ?? null, input.reason ?? null, json(input.metadata), now, now);
1129
+ upsertDurableObject({
1130
+ id: taskId,
1131
+ kind: "task",
1132
+ label: input.taskType,
1133
+ source: "local",
1134
+ status: input.status ?? "proposed",
1135
+ metadata: {
1136
+ proposerPeerId: input.proposerPeerId,
1137
+ assigneePeerId: input.assigneePeerId,
1138
+ taskSpec: input.taskSpec ?? {},
1139
+ inputContextIds: input.inputContextIds ?? [],
1140
+ outputContextId: input.outputContextId ?? null,
1141
+ reason: input.reason ?? null,
1142
+ ...(input.metadata ?? {}),
1143
+ },
1144
+ queueForSync: false,
1145
+ });
1146
+ linkDurableObjects({
1147
+ fromId: input.proposerPeerId,
1148
+ toId: taskId,
1149
+ edgeType: "proposed_task",
1150
+ queueForSync: false,
1151
+ });
1152
+ linkDurableObjects({
1153
+ fromId: taskId,
1154
+ toId: input.assigneePeerId,
1155
+ edgeType: "assigned_to",
1156
+ queueForSync: false,
1157
+ });
1158
+ for (const contextId of input.inputContextIds ?? []) {
1159
+ linkDurableObjects({
1160
+ fromId: taskId,
1161
+ toId: contextId,
1162
+ edgeType: "input_context",
1163
+ queueForSync: false,
1164
+ });
1165
+ }
1166
+ const queuedSyncId = input.queueForSync === false
1167
+ ? undefined
1168
+ : enqueueSyncOperation({
1169
+ objectId: taskId,
1170
+ objectKind: "task",
1171
+ opType: "task_handoff",
1172
+ payload: {
1173
+ taskId,
1174
+ taskType: input.taskType,
1175
+ proposerPeerId: input.proposerPeerId,
1176
+ assigneePeerId: input.assigneePeerId,
1177
+ status: input.status ?? "proposed",
1178
+ taskSpec: input.taskSpec ?? {},
1179
+ inputContextIds: input.inputContextIds ?? [],
1180
+ outputContextId: input.outputContextId ?? null,
1181
+ reason: input.reason ?? null,
1182
+ metadata: input.metadata ?? {},
1183
+ createdAt: now,
1184
+ },
1185
+ }).queueId;
1186
+ emitSharedContextEvent("task_proposed", {
1187
+ taskId,
1188
+ taskType: input.taskType,
1189
+ proposerPeerId: input.proposerPeerId,
1190
+ assigneePeerId: input.assigneePeerId,
1191
+ status: input.status ?? "proposed",
1192
+ createdAt: now,
1193
+ });
1194
+ return { taskId, queuedSyncId };
1195
+ }
1196
+ function updateSharedContextTaskStatus(taskId, expectedPeerId, status, reason, outputContextId) {
1197
+ const now = new Date().toISOString();
1198
+ getDb().prepare(`
1199
+ UPDATE shared_context_tasks
1200
+ SET status = ?,
1201
+ reason = COALESCE(?, reason),
1202
+ output_context_id = COALESCE(?, output_context_id),
1203
+ updated_at = ?
1204
+ WHERE task_id = ?
1205
+ AND assignee_peer_id = ?
1206
+ `).run(status, reason ?? null, outputContextId ?? null, now, taskId, expectedPeerId);
1207
+ const row = getDb().prepare(`
1208
+ SELECT *
1209
+ FROM shared_context_tasks
1210
+ WHERE task_id = ?
1211
+ LIMIT 1
1212
+ `).get(taskId);
1213
+ if (!row) {
1214
+ throw new Error(`Shared context task not found: ${taskId}`);
1215
+ }
1216
+ const task = parseSharedContextTaskRow(row);
1217
+ const assigneePeer = requireSharedContextPeer(task.assigneePeerId);
1218
+ const proposerPeer = requireSharedContextPeer(task.proposerPeerId);
1219
+ assertPeersShareScope(proposerPeer, assigneePeer, "update_shared_task");
1220
+ if (outputContextId) {
1221
+ requirePacketForPeer(outputContextId, expectedPeerId, "update_shared_task");
1222
+ }
1223
+ upsertDurableObject({
1224
+ id: taskId,
1225
+ kind: "task",
1226
+ label: task.taskType,
1227
+ source: "local",
1228
+ status,
1229
+ metadata: {
1230
+ proposerPeerId: task.proposerPeerId,
1231
+ assigneePeerId: task.assigneePeerId,
1232
+ taskSpec: task.taskSpec,
1233
+ inputContextIds: task.inputContextIds,
1234
+ outputContextId: task.outputContextId ?? null,
1235
+ reason: task.reason ?? null,
1236
+ },
1237
+ queueForSync: false,
1238
+ });
1239
+ emitSharedContextEvent("task_status_changed", {
1240
+ taskId,
1241
+ peerId: expectedPeerId,
1242
+ status,
1243
+ outputContextId: outputContextId ?? null,
1244
+ updatedAt: task.updatedAt,
1245
+ });
1246
+ return task;
1247
+ }
1248
+ export function acceptSharedContextTask(taskId, peerId) {
1249
+ const task = updateSharedContextTaskStatus(taskId, peerId, "accepted");
1250
+ const queuedSyncId = enqueueSyncOperation({
1251
+ objectId: taskId,
1252
+ objectKind: "task",
1253
+ opType: "task_handoff",
1254
+ payload: {
1255
+ taskId,
1256
+ peerId,
1257
+ status: "accepted",
1258
+ updatedAt: task.updatedAt,
1259
+ },
1260
+ }).queueId;
1261
+ return { task, queuedSyncId };
1262
+ }
1263
+ export function rejectSharedContextTask(taskId, peerId, reason) {
1264
+ const task = updateSharedContextTaskStatus(taskId, peerId, "rejected", reason);
1265
+ const queuedSyncId = enqueueSyncOperation({
1266
+ objectId: taskId,
1267
+ objectKind: "task",
1268
+ opType: "task_handoff",
1269
+ payload: {
1270
+ taskId,
1271
+ peerId,
1272
+ status: "rejected",
1273
+ reason,
1274
+ updatedAt: task.updatedAt,
1275
+ },
1276
+ }).queueId;
1277
+ return { task, queuedSyncId };
1278
+ }
1279
+ export function completeSharedContextTask(taskId, peerId, outputContextId) {
1280
+ const task = updateSharedContextTaskStatus(taskId, peerId, "completed", null, outputContextId ?? null);
1281
+ if (outputContextId) {
1282
+ linkDurableObjects({
1283
+ fromId: taskId,
1284
+ toId: outputContextId,
1285
+ edgeType: "completed_with",
1286
+ queueForSync: false,
1287
+ });
1288
+ }
1289
+ const queuedSyncId = enqueueSyncOperation({
1290
+ objectId: taskId,
1291
+ objectKind: "task",
1292
+ opType: "task_handoff",
1293
+ payload: {
1294
+ taskId,
1295
+ peerId,
1296
+ status: "completed",
1297
+ outputContextId: outputContextId ?? null,
1298
+ updatedAt: task.updatedAt,
1299
+ },
1300
+ }).queueId;
1301
+ return { task, queuedSyncId };
1302
+ }
1303
+ export function escalateSharedContextTask(taskId, peerId, reason, outputContextId) {
1304
+ const task = updateSharedContextTaskStatus(taskId, peerId, "escalated", reason, outputContextId ?? null);
1305
+ const queuedSyncId = enqueueSyncOperation({
1306
+ objectId: taskId,
1307
+ objectKind: "task",
1308
+ opType: "task_handoff",
1309
+ payload: {
1310
+ taskId,
1311
+ peerId,
1312
+ status: "escalated",
1313
+ reason,
1314
+ outputContextId: outputContextId ?? null,
1315
+ updatedAt: task.updatedAt,
1316
+ },
1317
+ }).queueId;
1318
+ return { task, queuedSyncId };
1319
+ }
1320
+ export function getSharedContextSnapshot(limit = 10, requestingPeerId) {
1321
+ const db = getDb();
1322
+ const counts = db.prepare(`
1323
+ SELECT
1324
+ (SELECT COUNT(*) FROM shared_context_peers WHERE status = 'active') AS active_peers,
1325
+ (SELECT COUNT(*) FROM shared_context_packets WHERE status = 'active') AS active_packets,
1326
+ (SELECT COUNT(*) FROM shared_context_packets WHERE status = 'invalidated') AS invalidated_packets,
1327
+ (SELECT COUNT(*) FROM shared_context_tasks WHERE status IN ('proposed', 'accepted')) AS open_tasks,
1328
+ (SELECT COUNT(*) FROM shared_context_messages WHERE status = 'unread') AS unread_messages
1329
+ `).get();
1330
+ const recentMessages = db.prepare(`
1331
+ SELECT id, from_peer_id, to_peer_id, message_class, payload_json, status, created_at
1332
+ FROM shared_context_messages
1333
+ ORDER BY created_at DESC
1334
+ LIMIT ?
1335
+ `).all(limit).map((row) => ({
1336
+ messageId: row.id,
1337
+ fromPeerId: row.from_peer_id,
1338
+ toPeerId: row.to_peer_id,
1339
+ messageClass: row.message_class,
1340
+ payload: parseJson(row.payload_json, {}),
1341
+ status: row.status,
1342
+ createdAt: row.created_at,
1343
+ }));
1344
+ const allPeers = listSharedContextPeers({ limit });
1345
+ const allPackets = pullSharedContextPackets({ limit });
1346
+ const allTasks = db.prepare(`
1347
+ SELECT *
1348
+ FROM shared_context_tasks
1349
+ ORDER BY updated_at DESC
1350
+ LIMIT ?
1351
+ `).all(limit).map(parseSharedContextTaskRow);
1352
+ if (!requestingPeerId) {
1353
+ return {
1354
+ peers: allPeers,
1355
+ recentPackets: allPackets,
1356
+ recentTasks: allTasks,
1357
+ recentMessages,
1358
+ counts: {
1359
+ activePeers: counts?.active_peers ?? 0,
1360
+ activePackets: counts?.active_packets ?? 0,
1361
+ invalidatedPackets: counts?.invalidated_packets ?? 0,
1362
+ openTasks: counts?.open_tasks ?? 0,
1363
+ unreadMessages: counts?.unread_messages ?? 0,
1364
+ },
1365
+ };
1366
+ }
1367
+ const requestingPeer = requireSharedContextPeer(requestingPeerId);
1368
+ const filteredPeers = allPeers.filter((peer) => {
1369
+ try {
1370
+ assertPeersShareScope(requestingPeer, peer, "get_shared_context_snapshot");
1371
+ return true;
1372
+ }
1373
+ catch {
1374
+ return peer.peerId === requestingPeer.peerId;
1375
+ }
1376
+ });
1377
+ const filteredPackets = allPackets.filter((packet) => canPeerAccessPacket(requestingPeer, packet));
1378
+ const filteredTasks = allTasks.filter((task) => {
1379
+ if (task.proposerPeerId === requestingPeer.peerId || task.assigneePeerId === requestingPeer.peerId)
1380
+ return true;
1381
+ const relatedOutput = task.outputContextId ? filteredPackets.some((packet) => packet.contextId === task.outputContextId) : false;
1382
+ return relatedOutput;
1383
+ });
1384
+ const filteredMessages = recentMessages.filter((message) => message.toPeerId === requestingPeer.peerId || message.fromPeerId === requestingPeer.peerId);
1385
+ return {
1386
+ peers: filteredPeers,
1387
+ recentPackets: filteredPackets,
1388
+ recentTasks: filteredTasks,
1389
+ recentMessages: filteredMessages,
1390
+ counts: {
1391
+ activePeers: filteredPeers.filter((peer) => peer.status === "active").length,
1392
+ activePackets: filteredPackets.filter((packet) => packet.status === "active").length,
1393
+ invalidatedPackets: filteredPackets.filter((packet) => packet.status === "invalidated").length,
1394
+ openTasks: filteredTasks.filter((task) => task.status === "proposed" || task.status === "accepted").length,
1395
+ unreadMessages: filteredMessages.filter((message) => message.status === "unread").length,
1396
+ },
1397
+ };
1398
+ }
1399
+ export function getSharedContextScopedSnapshot(filters = {}) {
1400
+ const snapshot = getSharedContextSnapshot(filters.limit ?? 10, filters.peerId ?? filters.requestingPeerId);
1401
+ const peers = filters.workspaceId
1402
+ ? snapshot.peers.filter((peer) => peer.workspaceId === filters.workspaceId)
1403
+ : snapshot.peers;
1404
+ const recentPackets = snapshot.recentPackets.filter((packet) => {
1405
+ if (filters.workspaceId && packet.workspaceId !== filters.workspaceId)
1406
+ return false;
1407
+ if (filters.contextType && packet.contextType !== filters.contextType)
1408
+ return false;
1409
+ if (filters.producerPeerId && packet.producerPeerId !== filters.producerPeerId)
1410
+ return false;
1411
+ if (filters.scopeIncludes && !packet.scope.includes(filters.scopeIncludes))
1412
+ return false;
1413
+ if (filters.subjectIncludes &&
1414
+ !packet.subject.toLowerCase().includes(filters.subjectIncludes.toLowerCase())) {
1415
+ return false;
1416
+ }
1417
+ return true;
1418
+ });
1419
+ const recentTasks = snapshot.recentTasks.filter((task) => taskMatchesSubscription(task, recentPackets, filters));
1420
+ const recentMessages = snapshot.recentMessages.filter((message) => messageMatchesSubscription(message, peers, filters));
1421
+ return {
1422
+ peers,
1423
+ recentPackets,
1424
+ recentTasks,
1425
+ recentMessages,
1426
+ counts: {
1427
+ activePeers: peers.filter((peer) => peer.status === "active").length,
1428
+ activePackets: recentPackets.filter((packet) => packet.status === "active").length,
1429
+ invalidatedPackets: recentPackets.filter((packet) => packet.status === "invalidated").length,
1430
+ openTasks: recentTasks.filter((task) => task.status === "proposed" || task.status === "accepted").length,
1431
+ unreadMessages: recentMessages.filter((message) => message.status === "unread").length,
1432
+ },
1433
+ };
1434
+ }
1435
+ //# sourceMappingURL=store.js.map