@kingsoftcloud/agentkit-harness-node 0.0.0-stage → 0.0.1

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 (151) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +1 -0
  3. package/README.md +124 -2
  4. package/THIRD_PARTY_NOTICES.md +66 -0
  5. package/ZCode-LICENSE +202 -0
  6. package/dist/index.mjs +4871 -0
  7. package/dist/native-command-plan.LICENSE.txt +221 -0
  8. package/dist/native-command-plan.json +16 -0
  9. package/dist/native-command-plan.mjs +8 -0
  10. package/dist/rpc-entry.mjs +5682 -0
  11. package/package.json +169 -4
  12. package/src/native/a2a-receipt-source.mjs +19 -0
  13. package/src/native/a2a-tools.mjs +517 -0
  14. package/src/native/activity-status.mjs +30 -0
  15. package/src/native/artifact-codec.mjs +481 -0
  16. package/src/native/artifact-store.mjs +1162 -0
  17. package/src/native/assistant-stream.mjs +76 -0
  18. package/src/native/background-command-port.mjs +95 -0
  19. package/src/native/browser-action-observation.mjs +43 -0
  20. package/src/native/browser-availability.mjs +22 -0
  21. package/src/native/browser-canvas-metrics.mjs +71 -0
  22. package/src/native/browser-dom.mjs +193 -0
  23. package/src/native/browser-playwright.mjs +1249 -0
  24. package/src/native/browser-vision.mjs +369 -0
  25. package/src/native/browser-workspace-policy.mjs +22 -0
  26. package/src/native/browser-workspace-protocol.mjs +95 -0
  27. package/src/native/browser-workspace-server.mjs +164 -0
  28. package/src/native/browser-workspace.mjs +355 -0
  29. package/src/native/captured-source.mjs +3 -0
  30. package/src/native/child-budget-allocation.mjs +87 -0
  31. package/src/native/child-capabilities.mjs +35 -0
  32. package/src/native/child-command-resource.mjs +20 -0
  33. package/src/native/child-history.mjs +188 -0
  34. package/src/native/child-merge.mjs +98 -0
  35. package/src/native/child-model-surface.mjs +19 -0
  36. package/src/native/child-observation.mjs +100 -0
  37. package/src/native/child-owner.mjs +620 -0
  38. package/src/native/child-publication.mjs +76 -0
  39. package/src/native/child-readers.mjs +366 -0
  40. package/src/native/child-tools.mjs +46 -0
  41. package/src/native/child-workspace-binding.mjs +29 -0
  42. package/src/native/child-workspace-tools.mjs +1 -0
  43. package/src/native/child-workspaces.mjs +655 -0
  44. package/src/native/children.mjs +365 -0
  45. package/src/native/command-plan-worker.mjs +14 -0
  46. package/src/native/command-plan.mjs +31 -0
  47. package/src/native/command-progress.mjs +64 -0
  48. package/src/native/command-shell.mjs +9 -0
  49. package/src/native/command-workspace-observation.mjs +36 -0
  50. package/src/native/component-tools.mjs +280 -0
  51. package/src/native/context-counter.mjs +133 -0
  52. package/src/native/context-markers.mjs +51 -0
  53. package/src/native/context-profile.mjs +72 -0
  54. package/src/native/context-status.mjs +27 -0
  55. package/src/native/conversation-command-control.mjs +133 -0
  56. package/src/native/delivery-timeline.mjs +39 -0
  57. package/src/native/dsh-background-ownership.mjs +102 -0
  58. package/src/native/dsh-managed-ownership.mjs +23 -0
  59. package/src/native/dsh-turn-contract.mjs +3 -0
  60. package/src/native/dsh-turn-ownership.mjs +54 -0
  61. package/src/native/dsh-turn-runtime.mjs +498 -0
  62. package/src/native/e2b-background-supervisor.mjs +347 -0
  63. package/src/native/e2b-background.mjs +368 -0
  64. package/src/native/e2b-command-state.mjs +40 -0
  65. package/src/native/e2b-command-tools.mjs +486 -0
  66. package/src/native/e2b-sandbox-owner.mjs +227 -0
  67. package/src/native/hook-executor.mjs +249 -0
  68. package/src/native/instruction-assembly.mjs +60 -0
  69. package/src/native/instruction-context.mjs +54 -0
  70. package/src/native/local-background-supervisor.mjs +108 -0
  71. package/src/native/local-background.mjs +207 -0
  72. package/src/native/local-command-evidence.mjs +57 -0
  73. package/src/native/local-command-policy-worker.mjs +197 -0
  74. package/src/native/local-command-policy.mjs +46 -0
  75. package/src/native/local-command-state.mjs +177 -0
  76. package/src/native/local-command-tools.mjs +302 -0
  77. package/src/native/local-execution-profile.mjs +36 -0
  78. package/src/native/local-output-policy.mjs +92 -0
  79. package/src/native/local-process-bin/darwin-arm64/launcher +0 -0
  80. package/src/native/local-process-bin/darwin-x64/launcher +0 -0
  81. package/src/native/local-process-bin/linux-arm64/launcher +0 -0
  82. package/src/native/local-process-bin/linux-x64/launcher +0 -0
  83. package/src/native/local-process-launcher.c +183 -0
  84. package/src/native/local-process-licenses/GCC-COPYING.RUNTIME +72 -0
  85. package/src/native/local-process-licenses/GCC-COPYING3 +674 -0
  86. package/src/native/local-process-licenses/NOTICE +21 -0
  87. package/src/native/local-process-licenses/musl-COPYRIGHT +193 -0
  88. package/src/native/local-process-manifest.json +32 -0
  89. package/src/native/local-process.mjs +246 -0
  90. package/src/native/local-resource-profile.mjs +32 -0
  91. package/src/native/memory-candidate.mjs +121 -0
  92. package/src/native/memory-extraction-run.mjs +60 -0
  93. package/src/native/memory-extraction-tools.mjs +116 -0
  94. package/src/native/memory-recall.mjs +65 -0
  95. package/src/native/memory-store.mjs +334 -0
  96. package/src/native/memory-tools.mjs +329 -0
  97. package/src/native/model-anthropic-cache.mjs +49 -0
  98. package/src/native/model-anthropic-tool-search.mjs +85 -0
  99. package/src/native/model-anthropic.mjs +753 -0
  100. package/src/native/model-chat-usage.mjs +24 -0
  101. package/src/native/model-inline-reasoning.mjs +111 -0
  102. package/src/native/model-media.mjs +174 -0
  103. package/src/native/model-normalize.mjs +228 -0
  104. package/src/native/model-openai-chat.mjs +561 -0
  105. package/src/native/model-port.mjs +361 -0
  106. package/src/native/model-provider-compat.mjs +129 -0
  107. package/src/native/model-replay.mjs +153 -0
  108. package/src/native/model-request.mjs +15 -0
  109. package/src/native/model-responses.mjs +1205 -0
  110. package/src/native/model-sse.mjs +171 -0
  111. package/src/native/model-surface-host.mjs +27 -0
  112. package/src/native/model-tool-search-policy.mjs +16 -0
  113. package/src/native/model-transport-observation.mjs +27 -0
  114. package/src/native/owned-child.mjs +114 -0
  115. package/src/native/owned-command.mjs +106 -0
  116. package/src/native/python-syntax-worker.mjs +60 -0
  117. package/src/native/python-syntax.mjs +50 -0
  118. package/src/native/read-interruption.mjs +16 -0
  119. package/src/native/result-stream.mjs +63 -0
  120. package/src/native/ruff-ast.mjs +95 -0
  121. package/src/native/run-observation.mjs +128 -0
  122. package/src/native/session-delivery-journal.mjs +96 -0
  123. package/src/native/session-delivery.mjs +434 -0
  124. package/src/native/shell-analysis.mjs +50 -0
  125. package/src/native/skill-disclosure.mjs +404 -0
  126. package/src/native/skill-events.mjs +164 -0
  127. package/src/native/skill-tools.mjs +813 -0
  128. package/src/native/source-page.mjs +73 -0
  129. package/src/native/sqlite-session-inbox.mjs +42 -0
  130. package/src/native/sqlite-store.mjs +617 -0
  131. package/src/native/tool-authority.mjs +99 -0
  132. package/src/native/tool-guidance.mjs +12 -0
  133. package/src/native/tool-observation.mjs +75 -0
  134. package/src/native/tool-states.mjs +14 -0
  135. package/src/native/verification-authority.mjs +37 -0
  136. package/src/native/web-tools.mjs +643 -0
  137. package/src/native/workspace-checkpoints.mjs +312 -0
  138. package/src/native/workspace-context.mjs +61 -0
  139. package/src/native/workspace-display.mjs +39 -0
  140. package/src/native/workspace-edit-match.mjs +19 -0
  141. package/src/native/workspace-edit-worker.mjs +15 -0
  142. package/src/native/workspace-editing.mjs +447 -0
  143. package/src/native/workspace-image.mjs +72 -0
  144. package/src/native/workspace-instructions.mjs +107 -0
  145. package/src/native/workspace-mutation-lock.mjs +74 -0
  146. package/src/native/workspace-patch-worker.mjs +17 -0
  147. package/src/native/workspace-patch.mjs +17 -0
  148. package/src/native/workspace-paths.mjs +225 -0
  149. package/src/native/workspace-state.mjs +272 -0
  150. package/src/native/workspace-subject-observer.mjs +84 -0
  151. package/src/native/workspace-tools.mjs +1740 -0
@@ -0,0 +1,617 @@
1
+ import { createSqliteSessionInbox } from './sqlite-session-inbox.mjs';
2
+ import { DatabaseSync } from 'node:sqlite';
3
+ import { createHash } from 'node:crypto';
4
+ import { mkdirSync } from 'node:fs';
5
+ import { dirname } from 'node:path';
6
+ import { deliveryJournalHead, matchesDeliveryJournal, sessionDeliveryEntries, deliveryStateFromEntries,
7
+ DELIVERY_SNAPSHOT_FIELDS } from './session-delivery-journal.mjs';
8
+ import { toolContext } from '@kingsoftcloud/agentkit-harness-core/tool-context';
9
+ import { NATIVE_RUN_OBSERVATION, NATIVE_COMMIT_PROJECTION, projectNativeRunObservation,
10
+ advanceNativeRunControlObservation, advanceNativeRunCommitObservation } from './run-observation.mjs';
11
+ import { TransactionalRunStore, canonical, invariant, scopeKey, runKey, parseNativeJson,
12
+ TOOL_AUTHORITY_SNAPSHOT_FIELDS, TOOL_AUTHORITY_CONFIGURATION_FIELDS,
13
+ projectLedgerCommitState, applyLedgerCommitChanges, workspaceTouchEvidence } from '@kingsoftcloud/agentkit-harness-core';
14
+
15
+ const collections = {
16
+ intents: item => item.operationKey,
17
+ receipts: item => item.operationKey,
18
+ commands: item => item.command.commandId,
19
+ commits: item => item.commitId,
20
+ };
21
+ const commitFingerprint = value => `sha256/v1:${createHash('sha256').update(value, 'utf8').digest('hex')}`;
22
+
23
+ /** Dedicated Native ledger. Historical events are append-only, never rewritten. */
24
+ export class SqliteRunStore extends TransactionalRunStore {
25
+ constructor(filename, { busyTimeoutMs = 5000, beforeCommit } = {}) {
26
+ if (filename !== ':memory:') mkdirSync(dirname(filename), { recursive: true });
27
+ const db = new DatabaseSync(filename);
28
+ try {
29
+ invariant(Number.isSafeInteger(busyTimeoutMs) && busyTimeoutMs >= 0, 'invalid_busy_timeout');
30
+ db.exec(`PRAGMA busy_timeout=${busyTimeoutMs}; PRAGMA journal_mode=WAL; PRAGMA synchronous=FULL; PRAGMA foreign_keys=ON;`);
31
+ db.exec('BEGIN IMMEDIATE');
32
+ try {
33
+ const hadOpenIndex = Boolean(db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='native_open_intents'").get());
34
+ db.exec(`
35
+ CREATE TABLE IF NOT EXISTS native_schema (version INTEGER NOT NULL);
36
+ CREATE TABLE IF NOT EXISTS native_runs (key TEXT PRIMARY KEY, data TEXT NOT NULL);
37
+ CREATE INDEX IF NOT EXISTS native_session_heads ON native_runs(
38
+ json_extract(data,'$.snapshot.scope'), json_extract(data,'$.snapshot.sessionId'));
39
+ CREATE TABLE IF NOT EXISTS native_events (
40
+ run_key TEXT NOT NULL REFERENCES native_runs(key), seq INTEGER NOT NULL,
41
+ event_id TEXT NOT NULL UNIQUE, data TEXT NOT NULL, PRIMARY KEY(run_key,seq));
42
+ CREATE TABLE IF NOT EXISTS native_items (
43
+ run_key TEXT NOT NULL REFERENCES native_runs(key), collection TEXT NOT NULL,
44
+ item_key TEXT NOT NULL, data TEXT NOT NULL, PRIMARY KEY(run_key,collection,item_key));
45
+ CREATE TABLE IF NOT EXISTS native_run_observations (
46
+ run_key TEXT PRIMARY KEY REFERENCES native_runs(key), data TEXT NOT NULL);
47
+ CREATE TABLE IF NOT EXISTS native_delivery_heads (
48
+ run_key TEXT PRIMARY KEY REFERENCES native_runs(key), data TEXT NOT NULL);
49
+ CREATE TABLE IF NOT EXISTS native_delivery_items (
50
+ run_key TEXT NOT NULL REFERENCES native_runs(key), collection TEXT NOT NULL,
51
+ item_key TEXT NOT NULL, data TEXT NOT NULL, PRIMARY KEY(run_key,collection,item_key));
52
+ CREATE TABLE IF NOT EXISTS native_open_intents (
53
+ run_key TEXT NOT NULL REFERENCES native_runs(key), item_key TEXT NOT NULL,
54
+ PRIMARY KEY(run_key,item_key));
55
+ CREATE TRIGGER IF NOT EXISTS native_intent_open AFTER INSERT ON native_items
56
+ WHEN NEW.collection='intents' BEGIN
57
+ INSERT OR IGNORE INTO native_open_intents SELECT NEW.run_key,NEW.item_key
58
+ WHERE NOT EXISTS (SELECT 1 FROM native_items WHERE run_key=NEW.run_key
59
+ AND collection='receipts' AND item_key=NEW.item_key);
60
+ END;
61
+ CREATE TRIGGER IF NOT EXISTS native_intent_settled AFTER INSERT ON native_items
62
+ WHEN NEW.collection='receipts' BEGIN
63
+ DELETE FROM native_open_intents WHERE run_key=NEW.run_key AND item_key=NEW.item_key;
64
+ END;
65
+ `);
66
+ // Derived key index; triggers also cover older schema-compatible
67
+ // writers. No historical payload, event or identity is rewritten.
68
+ if (!hadOpenIndex) db.exec(`INSERT INTO native_open_intents
69
+ SELECT i.run_key,i.item_key FROM native_items i WHERE i.collection='intents'
70
+ AND NOT EXISTS (SELECT 1 FROM native_items r WHERE r.run_key=i.run_key
71
+ AND r.collection='receipts' AND r.item_key=i.item_key)`);
72
+ const versions = db.prepare('SELECT version FROM native_schema').all();
73
+ if (!versions.length) db.prepare('INSERT INTO native_schema VALUES (?)').run(2);
74
+ else invariant(versions.length === 1 && [1, 2].includes(versions[0].version), 'unsupported_native_schema');
75
+ // Older stores duplicated the complete canonical batch in every commit
76
+ // fingerprint. Compact these immutable identifiers atomically so a
77
+ // long run does not repeatedly deserialize its full history. Preserve
78
+ // the commit IDs and keep all snapshots, events and receipts intact.
79
+ const legacy = db.prepare(`SELECT rowid,data FROM native_items WHERE collection='commits'
80
+ AND substr(json_extract(data,'$.fingerprint'),1,1)='{'`).all();
81
+ const compact = db.prepare('UPDATE native_items SET data=? WHERE rowid=?');
82
+ for (const row of legacy) {
83
+ const item = parseNativeJson(row.data);
84
+ invariant(typeof item.fingerprint === 'string', 'invalid_commit_fingerprint');
85
+ compact.run(canonical({ ...item, fingerprint: commitFingerprint(item.fingerprint) }), row.rowid);
86
+ }
87
+ if (versions.length && versions[0].version === 1) db.prepare('UPDATE native_schema SET version=2').run();
88
+ db.exec('COMMIT');
89
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
90
+ } catch (error) { db.close(); throw error; }
91
+
92
+ let closed = false;
93
+ const open = () => invariant(!closed, 'store_closed');
94
+ let inbox;
95
+ try { inbox = createSqliteSessionInbox(db, open); }
96
+ catch (error) { db.close(); throw error; }
97
+ const sessionHead = (scope, sessionId) => {
98
+ open(); scopeKey(scope);
99
+ const row = db.prepare(`SELECT data FROM native_runs
100
+ WHERE json_extract(data,'$.snapshot.scope')=? AND json_extract(data,'$.snapshot.sessionId')=?
101
+ ORDER BY rowid DESC LIMIT 1`).get(canonical(scope), sessionId);
102
+ return row ? parseNativeJson(row.data).snapshot : null;
103
+ };
104
+ const read = key => {
105
+ open();
106
+ const row = db.prepare('SELECT data FROM native_runs WHERE key=?').get(key);
107
+ if (!row) return null;
108
+ const record = parseNativeJson(row.data);
109
+ record.events = db.prepare('SELECT data FROM native_events WHERE run_key=? ORDER BY seq').all(key).map(item => parseNativeJson(item.data));
110
+ for (const collection of Object.keys(collections)) record[collection] = [];
111
+ for (const item of db.prepare('SELECT collection,data FROM native_items WHERE run_key=? ORDER BY rowid').all(key)) {
112
+ invariant(Object.hasOwn(collections, item.collection), 'invalid_ledger_collection');
113
+ record[item.collection].push(parseNativeJson(item.data));
114
+ }
115
+ return record;
116
+ };
117
+ const readControl = key => {
118
+ const row = db.prepare('SELECT data FROM native_runs WHERE key=?').get(key);
119
+ if (!row) return null;
120
+ const { configuration, snapshot, lease, fence, projections } = parseNativeJson(row.data);
121
+ const pending = snapshot.pendingIntent === null ? null : db.prepare(`SELECT data FROM native_items
122
+ WHERE run_key=? AND collection='intents' AND item_key=?`).get(key, snapshot.pendingIntent);
123
+ const receipt = snapshot.pendingIntent === null ? null : db.prepare(`SELECT 1 FROM native_items
124
+ WHERE run_key=? AND collection='receipts' AND item_key=?`).get(key, snapshot.pendingIntent);
125
+ const commands = db.prepare(`SELECT data FROM native_items WHERE run_key=? AND collection='commands' ORDER BY rowid`)
126
+ .all(key).map(item => parseNativeJson(item.data));
127
+ return { configuration, snapshot, lease, fence, projections, commands,
128
+ pendingIntent: pending ? parseNativeJson(pending.data) : null, pendingIntentHasReceipt: Boolean(receipt) };
129
+ };
130
+ const readCommit = (key, batch) => {
131
+ const row = db.prepare('SELECT data FROM native_runs WHERE key=?').get(key);
132
+ if (!row) return null;
133
+ const metadata = parseNativeJson(row.data);
134
+ const intents = new Map(db.prepare(`SELECT i.item_key,i.data FROM native_open_intents o
135
+ JOIN native_items i ON i.run_key=o.run_key AND i.collection='intents' AND i.item_key=o.item_key
136
+ WHERE o.run_key=?`)
137
+ .all(key).map(row => [row.item_key, parseNativeJson(row.data)]));
138
+ const keys = new Set([...batch.intents, ...batch.receipts].map(item => item.operationKey));
139
+ const intentAt = db.prepare("SELECT data FROM native_items WHERE run_key=? AND collection='intents' AND item_key=?");
140
+ for (const operationKey of keys) if (!intents.has(operationKey)) {
141
+ const found = intentAt.get(key, operationKey);
142
+ if (found) intents.set(operationKey, parseNativeJson(found.data));
143
+ }
144
+ const receiptAt = db.prepare("SELECT 1 FROM native_items WHERE run_key=? AND collection='receipts' AND item_key=?");
145
+ const existingReceiptKeys = [...new Set([...keys, ...intents.keys()])].filter(operationKey => receiptAt.get(key, operationKey));
146
+ const eventAt = db.prepare('SELECT 1 FROM native_events WHERE event_id=?');
147
+ const duplicate = db.prepare("SELECT data FROM native_items WHERE run_key=? AND collection='commits' AND item_key=?")
148
+ .get(key, batch.commitId);
149
+ const commands = db.prepare(`SELECT data FROM native_items WHERE run_key=? AND collection='commands'
150
+ AND json_extract(data,'$.consumed')=0 ORDER BY rowid`).all(key).map(row => parseNativeJson(row.data));
151
+ return { ...metadata, commands, intents: [...intents.values()], existingReceiptKeys,
152
+ existingEventIds: batch.events.filter(event => eventAt.get(event.eventId)).map(event => event.eventId),
153
+ duplicateCommit: duplicate ? parseNativeJson(duplicate.data) : null };
154
+ };
155
+ const deliveryHead = key => {
156
+ const row = db.prepare('SELECT data FROM native_delivery_heads WHERE run_key=?').get(key);
157
+ return row ? parseNativeJson(row.data) : null;
158
+ };
159
+ const writeDeliveryHead = (key, snapshot) => db.prepare(`INSERT INTO native_delivery_heads VALUES (?,?)
160
+ ON CONFLICT(run_key) DO UPDATE SET data=excluded.data`).run(key, canonical(deliveryJournalHead(snapshot)));
161
+ const appendDelivery = (key, evidence, snapshot) => {
162
+ const insert = db.prepare(`INSERT INTO native_delivery_items VALUES (?,?,?,?)
163
+ ON CONFLICT(run_key,collection,item_key) DO UPDATE SET data=CASE
164
+ WHEN native_delivery_items.collection='tools'
165
+ OR native_delivery_items.collection='outcomes' AND native_delivery_items.data='"not_dispatched"'
166
+ THEN native_delivery_items.data ELSE excluded.data END`);
167
+ for (const row of sessionDeliveryEntries(evidence)) insert.run(key, row.collection, row.key, canonical(row.data));
168
+ writeDeliveryHead(key, snapshot);
169
+ };
170
+ const rebuildDelivery = (key, record) => {
171
+ db.prepare('DELETE FROM native_delivery_items WHERE run_key=?').run(key);
172
+ appendDelivery(key, record, record.snapshot);
173
+ };
174
+ const persist = (key, previous, record) => {
175
+ const { events, intents, receipts, commands, commits, ...metadata } = record;
176
+ db.prepare('INSERT INTO native_runs(key,data) VALUES (?,?) ON CONFLICT(key) DO UPDATE SET data=excluded.data').run(key, canonical(metadata));
177
+ invariant(events.length >= (previous?.events.length ?? 0), 'events_immutable');
178
+ for (const event of events.slice(previous?.events.length ?? 0))
179
+ db.prepare('INSERT INTO native_events VALUES (?,?,?,?)').run(key, event.seq, event.eventId, canonical(event));
180
+ for (const [collection, identify] of Object.entries(collections)) {
181
+ const old = new Map((previous?.[collection] ?? []).map(item => [identify(item), canonical(item)]));
182
+ invariant(record[collection].length >= old.size, 'ledger_items_immutable');
183
+ for (const item of record[collection]) {
184
+ const id = identify(item), data = canonical(item);
185
+ if (old.get(id) !== data) db.prepare('INSERT INTO native_items VALUES (?,?,?,?) ON CONFLICT(run_key,collection,item_key) DO UPDATE SET data=excluded.data')
186
+ .run(key, collection, id, data);
187
+ }
188
+ }
189
+ if (!previous) inbox.consume(record.snapshot);
190
+ db.prepare(`INSERT INTO native_run_observations VALUES (?,?)
191
+ ON CONFLICT(run_key) DO UPDATE SET data=excluded.data`).run(key, canonical(projectNativeRunObservation(record)));
192
+ rebuildDelivery(key, record);
193
+ beforeCommit?.({ key, record });
194
+ };
195
+ super({
196
+ enqueueSessionInjection: inbox.enqueueSessionInjection,
197
+ pendingSessionInjections: inbox.pendingSessionInjections,
198
+ workspaceTouchEvidence: key => {
199
+ open(); db.exec('BEGIN');
200
+ try {
201
+ const row = db.prepare(`SELECT json_object('scope',json_extract(data,'$.snapshot.scope'),
202
+ 'runId',json_extract(data,'$.snapshot.runId'),'sessionId',json_extract(data,'$.snapshot.sessionId'),
203
+ 'revision',json_extract(data,'$.snapshot.revision'),'eventSeq',json_extract(data,'$.snapshot.eventSeq'),
204
+ 'instructionScopes',json_extract(data,'$.snapshot.context.instructionScopes')) AS data
205
+ FROM native_runs WHERE key=?`).get(key);
206
+ let view = null;
207
+ if (row) {
208
+ const identity = parseNativeJson(row.data);
209
+ // The existing (run_key,seq) primary key bounds both the reverse
210
+ // boundary lookup and the subsequent committed-event suffix.
211
+ const after = db.prepare(`SELECT seq FROM native_events WHERE run_key=? AND seq<=?
212
+ AND json_extract(data,'$.kind')='model.completed' ORDER BY seq DESC LIMIT 1`).get(key, identity.eventSeq)?.seq ?? 0;
213
+ const observations = db.prepare(`SELECT seq, json_extract(data,'$.payload.name') AS toolName,
214
+ CASE WHEN json_type(data,'$.payload.content.path')='text' THEN json_extract(data,'$.payload.content.path') END AS path,
215
+ (SELECT json_group_array(target) FROM (SELECT CASE WHEN item.type='object' THEN
216
+ CASE WHEN json_type(item.value,'$.path')='text' THEN json_extract(item.value,'$.path') END END AS target
217
+ FROM json_each(CASE WHEN json_type(e.data,'$.payload.content.written')='array'
218
+ THEN json_extract(e.data,'$.payload.content.written') ELSE '[]' END) item) WHERE target IS NOT NULL) AS writtenPaths,
219
+ (SELECT json_group_array(item.value) FROM json_each(CASE WHEN json_type(e.data,'$.payload.content.changed_paths')='array'
220
+ THEN json_extract(e.data,'$.payload.content.changed_paths') ELSE '[]' END) item WHERE item.type='text') AS changedPaths
221
+ FROM native_events e WHERE run_key=? AND seq>? AND seq<=?
222
+ AND json_extract(data,'$.kind')='tool.completed' AND json_extract(data,'$.payload.status')='ok'
223
+ AND json_type(data,'$.payload.name')='text' AND length(trim(json_extract(data,'$.payload.name')))>0 ORDER BY seq`)
224
+ .all(key, after, identity.eventSeq).map(item => ({ seq: item.seq, toolName: item.toolName,
225
+ ...(item.path === null ? {} : { path: item.path }), writtenPaths: parseNativeJson(item.writtenPaths),
226
+ changedPaths: parseNativeJson(item.changedPaths) }));
227
+ view = workspaceTouchEvidence(identity, after, observations, identity.instructionScopes ?? undefined);
228
+ }
229
+ db.exec('COMMIT'); return view;
230
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
231
+ },
232
+ commitFingerprint,
233
+ sessionHead,
234
+ ...(!beforeCommit ? { updateCommit: (key, batch, change) => {
235
+ open(); db.exec('BEGIN IMMEDIATE');
236
+ try {
237
+ const current = readCommit(key, batch);
238
+ const row = db.prepare('SELECT data FROM native_run_observations WHERE run_key=?').get(key);
239
+ const observation = row ? parseNativeJson(row.data) : null;
240
+ // Reconciliation/operator review may inspect complete historical
241
+ // uncertainty. Old observation formats rebuild once, never guess.
242
+ const exceptional = !current || ['interrupted', 'awaiting_reconciliation', 'reconciling_tool']
243
+ .some(phase => current.snapshot.phase === phase || batch.snapshot.phase === phase);
244
+ const compatible = observation?.contract === NATIVE_RUN_OBSERVATION
245
+ && observation.commitProjection?.contract === NATIVE_COMMIT_PROJECTION
246
+ && Array.isArray(observation.commitProjection?.tools?.calls)
247
+ && Array.isArray(observation.facts?.toolOutcomes)
248
+ && observation.snapshot.revision === current?.snapshot.revision
249
+ && observation.snapshot.eventSeq === current?.snapshot.eventSeq
250
+ && observation.snapshot.configDigest === current?.snapshot.configDigest
251
+ && observation.snapshot.runId === current?.snapshot.runId
252
+ && observation.snapshot.sessionId === current?.snapshot.sessionId
253
+ && canonical(observation.snapshot.scope) === canonical(current?.snapshot.scope);
254
+ if (exceptional || !compatible || !matchesDeliveryJournal(deliveryHead(key), current.snapshot)) {
255
+ const previous = read(key), result = change(previous ? projectLedgerCommitState(previous, batch) : null);
256
+ if (result.changes) persist(key, previous, applyLedgerCommitChanges(previous, result.changes));
257
+ db.exec('COMMIT'); return result.value;
258
+ }
259
+ const { changes, value } = change(current);
260
+ if (changes) {
261
+ const { commands, intents, existingReceiptKeys, existingEventIds, duplicateCommit, ...metadata } = current;
262
+ db.prepare('UPDATE native_runs SET data=? WHERE key=?').run(canonical({ ...metadata, snapshot: changes.snapshot }), key);
263
+ const eventInsert = db.prepare('INSERT INTO native_events VALUES (?,?,?,?)');
264
+ for (const event of changes.events) eventInsert.run(key, event.seq, event.eventId, canonical(event));
265
+ const itemInsert = db.prepare('INSERT INTO native_items VALUES (?,?,?,?)');
266
+ for (const [collection, items] of [['intents', changes.intents], ['receipts', changes.receipts], ['commits', [changes.commit]]])
267
+ for (const item of items) itemInsert.run(key, collection, collections[collection](item), canonical(item));
268
+ const commandUpdate = db.prepare("UPDATE native_items SET data=? WHERE run_key=? AND collection='commands' AND item_key=?");
269
+ for (const row of changes.commands) commandUpdate.run(canonical(row), key, row.command.commandId);
270
+ const advanced = advanceNativeRunCommitObservation(observation, current, changes);
271
+ db.prepare('UPDATE native_run_observations SET data=? WHERE run_key=?').run(canonical(advanced), key);
272
+ appendDelivery(key, { events: changes.events, receipts: changes.receipts,
273
+ intents: [...current.intents, ...changes.intents] }, changes.snapshot);
274
+ }
275
+ db.exec('COMMIT'); return value;
276
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
277
+ } } : {}),
278
+ controlState: key => {
279
+ open(); db.exec('BEGIN');
280
+ try { const value = readControl(key); db.exec('COMMIT'); return value; }
281
+ catch (error) { db.exec('ROLLBACK'); throw error; }
282
+ },
283
+ // Control-plane writes use the same shared Core validators as the full
284
+ // backend, but never deserialize or replace historical effect results.
285
+ // Fault-injection hooks intentionally retain the full transaction path.
286
+ ...(!beforeCommit ? { updateControl: (key, change) => {
287
+ open(); db.exec('BEGIN IMMEDIATE');
288
+ try {
289
+ const current = readControl(key), { changes, value } = change(current);
290
+ invariant(current, 'run_not_found');
291
+ if (changes.claim || changes.commands || changes.projections) {
292
+ const presentationCurrent = matchesDeliveryJournal(deliveryHead(key), current.snapshot);
293
+ const cached = db.prepare('SELECT data FROM native_run_observations WHERE run_key=?').get(key);
294
+ let observation = cached ? parseNativeJson(cached.data) : null;
295
+ if (!observation || observation.contract !== NATIVE_RUN_OBSERVATION
296
+ || observation.commitProjection?.contract !== NATIVE_COMMIT_PROJECTION
297
+ || !Array.isArray(observation.commitProjection?.tools?.calls) || !Array.isArray(observation.facts?.toolOutcomes)
298
+ || observation.snapshot.revision !== current.snapshot.revision
299
+ || observation.snapshot.eventSeq !== current.snapshot.eventSeq
300
+ || observation.snapshot.configDigest !== current.snapshot.configDigest
301
+ || observation.snapshot.runId !== current.snapshot.runId
302
+ || observation.snapshot.sessionId !== current.snapshot.sessionId
303
+ || canonical(observation.snapshot.scope) !== canonical(current.snapshot.scope))
304
+ observation = projectNativeRunObservation(read(key));
305
+ if (changes.claim) {
306
+ const { snapshot, intent } = changes.claim;
307
+ db.prepare("UPDATE native_runs SET data=json_set(data,'$.snapshot',json(?)) WHERE key=?")
308
+ .run(canonical(snapshot), key);
309
+ const result = db.prepare(`UPDATE native_items SET data=?
310
+ WHERE run_key=? AND collection='intents' AND item_key=?`).run(canonical(intent), key, intent.operationKey);
311
+ invariant(result.changes === 1, 'pending_intent_missing');
312
+ }
313
+ if (changes.commands) {
314
+ const previous = new Map(current.commands.map(row => [row.command.commandId, canonical(row)]));
315
+ for (const row of changes.commands) {
316
+ const data = canonical(row), id = row.command.commandId;
317
+ if (previous.get(id) !== data) db.prepare(`INSERT INTO native_items VALUES (?,?,?,?)
318
+ ON CONFLICT(run_key,collection,item_key) DO UPDATE SET data=excluded.data`).run(key, 'commands', id, data);
319
+ }
320
+ }
321
+ if (changes.projections) db.prepare("UPDATE native_runs SET data=json_set(data,'$.projections',json(?)) WHERE key=?")
322
+ .run(canonical(changes.projections), key);
323
+ const updated = advanceNativeRunControlObservation(observation, current, changes);
324
+ db.prepare(`INSERT INTO native_run_observations VALUES (?,?)
325
+ ON CONFLICT(run_key) DO UPDATE SET data=excluded.data`).run(key, canonical(updated));
326
+ if (presentationCurrent) writeDeliveryHead(key, changes.claim?.snapshot ?? current.snapshot);
327
+ }
328
+ db.exec('COMMIT'); return value;
329
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
330
+ } } : {}),
331
+ sessionHeadView: (scope, sessionId) => {
332
+ open();
333
+ const row = db.prepare(`SELECT json_object(
334
+ 'scope',json_extract(data,'$.snapshot.scope'), 'runId',json_extract(data,'$.snapshot.runId'),
335
+ 'sessionId',json_extract(data,'$.snapshot.sessionId'), 'phase',json_extract(data,'$.snapshot.phase'),
336
+ 'revision',json_extract(data,'$.snapshot.revision'), 'eventSeq',json_extract(data,'$.snapshot.eventSeq'),
337
+ 'backgroundCommands',json_extract(data,'$.snapshot.backgroundCommands'), 'lease',json_extract(data,'$.lease')) AS head
338
+ FROM native_runs WHERE json_extract(data,'$.snapshot.scope')=? AND json_extract(data,'$.snapshot.sessionId')=?
339
+ ORDER BY rowid DESC LIMIT 1`).get(canonical(scope), sessionId);
340
+ return row ? parseNativeJson(row.head) : null;
341
+ },
342
+ projectionCursors: key => {
343
+ open();
344
+ const row = db.prepare("SELECT json_extract(data,'$.projections') AS cursors FROM native_runs WHERE key=?").get(key);
345
+ return row ? parseNativeJson(row.cursors) : null;
346
+ },
347
+ authority: key => {
348
+ open(); db.exec('BEGIN');
349
+ try {
350
+ const row = db.prepare('SELECT data FROM native_runs WHERE key=?').get(key);
351
+ let view = null;
352
+ if (row) {
353
+ const { configuration, snapshot, lease, fence } = parseNativeJson(row.data);
354
+ const pending = snapshot.pendingIntent === null ? null : db.prepare(`SELECT data FROM native_items
355
+ WHERE run_key=? AND collection='intents' AND item_key=?`).get(key, snapshot.pendingIntent);
356
+ view = { configuration, snapshot, lease, fence, pendingIntent: pending ? parseNativeJson(pending.data) : null };
357
+ }
358
+ db.exec('COMMIT'); return view;
359
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
360
+ },
361
+ toolAuthority: key => {
362
+ open(); db.exec('BEGIN');
363
+ try {
364
+ // Static Core-owned field lists are the authority contract. SQLite
365
+ // projects these fields before Node parses/copies any JSON, so old
366
+ // screenshots, model transcripts and receipts stay outside this path.
367
+ const fields = (names, path, operation = 'json_extract') => names.map(name => `'${name}',${operation}(data,'$.${path}.${name}')`).join(',');
368
+ const row = db.prepare(`SELECT json_object(
369
+ 'snapshot',json_object(${fields(TOOL_AUTHORITY_SNAPSHOT_FIELDS, 'snapshot')}),
370
+ 'configuration',json_object(${fields(TOOL_AUTHORITY_CONFIGURATION_FIELDS, 'configuration')}),
371
+ 'splitModelObservation',json(CASE WHEN json_type(data,'$.configuration.modelSurface')='object' THEN 'true' ELSE 'false' END),
372
+ 'presence',json_object('snapshot',json_object(${fields(TOOL_AUTHORITY_SNAPSHOT_FIELDS, 'snapshot', 'json_type')}),
373
+ 'configuration',json_object(${fields(TOOL_AUTHORITY_CONFIGURATION_FIELDS, 'configuration', 'json_type')})),
374
+ 'lease',json_extract(data,'$.lease'),'fence',json_extract(data,'$.fence')) AS data
375
+ FROM native_runs WHERE key=?`).get(key);
376
+ let view = null;
377
+ if (row) {
378
+ view = parseNativeJson(row.data);
379
+ for (const section of ['snapshot', 'configuration']) for (const [field, type] of Object.entries(view.presence[section]))
380
+ if (type === null) delete view[section][field];
381
+ delete view.presence;
382
+ const pending = view.snapshot.phase === 'executing_tools' && view.snapshot.pendingIntent
383
+ ? db.prepare("SELECT data FROM native_items WHERE run_key=? AND collection='intents' AND item_key=?")
384
+ .get(key, view.snapshot.pendingIntent) : null;
385
+ const declared = view.snapshot.sourceReadQuote ? db.prepare(`SELECT m.value AS data FROM native_runs r,
386
+ json_each(r.data,'$.snapshot.messages') m WHERE r.key=? AND json_extract(m.value,'$.role')='assistant'
387
+ AND json_array_length(json_extract(m.value,'$.toolCalls'))>0 ORDER BY cast(m.key AS INTEGER) DESC LIMIT 1`).get(key) : null;
388
+ view.pendingIntent = pending ? parseNativeJson(pending.data) : null;
389
+ view.sourceReadMessage = declared ? parseNativeJson(declared.data) : null;
390
+ }
391
+ db.exec('COMMIT'); return view;
392
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
393
+ },
394
+ // A lease update changes only the owned run header. Keeping this path
395
+ // independent of the event/receipt journal prevents heartbeat cost from
396
+ // growing with the length of the conversation. Fault-injection callers
397
+ // retain the full update path and its beforeCommit hook.
398
+ ...(!beforeCommit ? { updateLease: (key, change) => {
399
+ open(); db.exec('BEGIN IMMEDIATE');
400
+ try {
401
+ const row = db.prepare(`SELECT json_extract(data,'$.lease') AS lease,
402
+ json_extract(data,'$.fence') AS fence, json_extract(data,'$.resourceRetirementReview') AS retirement FROM native_runs WHERE key=?`).get(key);
403
+ const { record, value } = change(row ? { lease: row.lease === null ? null : parseNativeJson(row.lease), fence: row.fence,
404
+ ...(row.retirement === null ? {} : {resourceRetirementReview:parseNativeJson(row.retirement)}) } : null);
405
+ invariant(row, 'run_not_found');
406
+ db.prepare(`UPDATE native_runs SET data=json_set(data,'$.lease',json(?),'$.fence',?) WHERE key=?`)
407
+ .run(canonical(record.lease), record.fence, key);
408
+ db.exec('COMMIT'); return value;
409
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
410
+ } } : {}),
411
+ eventsAfter: (key, after) => {
412
+ open(); db.exec('BEGIN');
413
+ try {
414
+ const exists = db.prepare('SELECT 1 AS present FROM native_runs WHERE key=?').get(key);
415
+ const events = exists ? db.prepare('SELECT data FROM native_events WHERE run_key=? AND seq>? ORDER BY seq')
416
+ .all(key, after).map(row => parseNativeJson(row.data)) : null;
417
+ db.exec('COMMIT'); return events;
418
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
419
+ },
420
+ commands: key => {
421
+ open(); db.exec('BEGIN');
422
+ try {
423
+ const exists = db.prepare('SELECT 1 FROM native_runs WHERE key=?').get(key);
424
+ const commands = exists ? db.prepare(`SELECT data FROM native_items
425
+ WHERE run_key=? AND collection='commands' AND json_extract(data,'$.consumed')=0 ORDER BY rowid`)
426
+ .all(key).map(row => parseNativeJson(row.data)) : null;
427
+ db.exec('COMMIT'); return commands;
428
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
429
+ },
430
+ get: key => {
431
+ open(); db.exec('BEGIN');
432
+ try { const value = read(key); db.exec('COMMIT'); return value; }
433
+ catch (error) { db.exec('ROLLBACK'); throw error; }
434
+ },
435
+ snapshots: (scope, sessionId) => {
436
+ open();
437
+ return db.prepare(`SELECT json_extract(data,'$.snapshot') AS snapshot FROM native_runs
438
+ WHERE json_extract(data,'$.snapshot.scope')=?
439
+ ${sessionId === undefined ? '' : "AND json_extract(data,'$.snapshot.sessionId')=?"} ORDER BY rowid`)
440
+ .all(canonical(scope), ...(sessionId === undefined ? [] : [sessionId])).map(row => parseNativeJson(row.snapshot));
441
+ },
442
+ heads: (scope, sessionId) => {
443
+ open();
444
+ return db.prepare(`SELECT json_object(
445
+ 'scope',json_extract(data,'$.snapshot.scope'), 'runId',json_extract(data,'$.snapshot.runId'),
446
+ 'sessionId',json_extract(data,'$.snapshot.sessionId'), 'phase',json_extract(data,'$.snapshot.phase'),
447
+ 'revision',json_extract(data,'$.snapshot.revision'), 'eventSeq',json_extract(data,'$.snapshot.eventSeq'),
448
+ 'backgroundCommands',json_extract(data,'$.snapshot.backgroundCommands'), 'lease',json_extract(data,'$.lease')) AS head
449
+ FROM native_runs WHERE json_extract(data,'$.snapshot.scope')=?
450
+ ${sessionId === undefined ? '' : "AND json_extract(data,'$.snapshot.sessionId')=?"} ORDER BY rowid`)
451
+ .all(canonical(scope), ...(sessionId === undefined ? [] : [sessionId])).map(row => parseNativeJson(row.head));
452
+ },
453
+ values: () => {
454
+ open(); db.exec('BEGIN');
455
+ try {
456
+ const values = db.prepare('SELECT key FROM native_runs ORDER BY rowid').all().map(row => read(row.key));
457
+ db.exec('COMMIT'); return values;
458
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
459
+ },
460
+ update: (key, change) => {
461
+ open(); db.exec('BEGIN IMMEDIATE');
462
+ try {
463
+ const previous = read(key);
464
+ const { record, value } = change(previous, sessionHead);
465
+ persist(key, previous, record);
466
+ db.exec('COMMIT'); return value;
467
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
468
+ },
469
+ updatePair: (keys, change) => {
470
+ open(); db.exec('BEGIN IMMEDIATE');
471
+ try {
472
+ const previous = keys.map(read), { records, value } = change(previous);
473
+ keys.forEach((key, index) => persist(key, previous[index], records[index]));
474
+ db.exec('COMMIT'); return value;
475
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
476
+ },
477
+ });
478
+ /** A presentation-only read: current transcript, display events and exact
479
+ * unresolved closures. Historical raw tool bodies never enter this view. */
480
+ this.sessionDeliveryState = (scope, runId) => {
481
+ open(); const key = runKey(scope, runId);
482
+ const header = () => {
483
+ const fields = (operation) => DELIVERY_SNAPSHOT_FIELDS.map(name => `'${name}',${operation}(data,'$.snapshot.${name}')`).join(',');
484
+ const row = db.prepare(`SELECT json_object(${fields('json_extract')}) AS snapshot,
485
+ json_object(${fields('json_type')}) AS presence, json_extract(data,'$.configuration') AS configuration,
486
+ json_extract(data,'$.projections') AS projections FROM native_runs WHERE key=?`).get(key);
487
+ invariant(row, 'run_not_found');
488
+ const snapshot = parseNativeJson(row.snapshot), presence = parseNativeJson(row.presence);
489
+ for (const [name, type] of Object.entries(presence)) if (type === null) delete snapshot[name];
490
+ invariant(snapshot.runId === runId && canonical(snapshot.scope) === canonical(scope), 'delivery_identity_mismatch');
491
+ return { snapshot, configuration: parseNativeJson(row.configuration), projections: parseNativeJson(row.projections) };
492
+ };
493
+ db.exec('BEGIN');
494
+ try {
495
+ let current = header();
496
+ if (!matchesDeliveryJournal(deliveryHead(key), current.snapshot)) {
497
+ db.exec('ROLLBACK'); db.exec('BEGIN IMMEDIATE'); current = header();
498
+ // A compatible older writer or a missing/foreign cache rebuilds
499
+ // once from its own authoritative ledger, without rewriting it.
500
+ if (!matchesDeliveryJournal(deliveryHead(key), current.snapshot)) rebuildDelivery(key, read(key));
501
+ }
502
+ const entries = db.prepare('SELECT collection,item_key,data FROM native_delivery_items WHERE run_key=? ORDER BY rowid')
503
+ .all(key).map(row => ({ collection: row.collection, key: row.item_key, data: parseNativeJson(row.data) }));
504
+ const closure = (_callId, reference) => {
505
+ let intent = null, receipt = null;
506
+ if (reference) {
507
+ intent = db.prepare("SELECT json_extract(data,'$.status') AS status FROM native_items WHERE run_key=? AND collection='intents' AND item_key=?")
508
+ .get(key, reference.operationKey);
509
+ const path = `$.result.results[${reference.index}]`;
510
+ receipt = db.prepare(`SELECT json_extract(data,'$.status') AS status, json_type(data,?) AS observed,
511
+ json_extract(data,?) AS resultStatus, json_extract(data,?) AS context,
512
+ json_quote(json_extract(data,?)) AS content FROM native_items
513
+ WHERE run_key=? AND collection='receipts' AND item_key=?`)
514
+ .get(path, path + '.status', path + '.context', path + '.content', key, reference.operationKey);
515
+ }
516
+ if (receipt?.status === 'committed' && receipt.observed === 'object') return {
517
+ context: receipt.context === null ? { content: canonical(parseNativeJson(receipt.content)) } : toolContext(parseNativeJson(receipt.context)),
518
+ isError: receipt.resultStatus !== 'ok' };
519
+ const unknown = intent?.status === 'running' && (!receipt || receipt.status === 'uncertain');
520
+ return { context: { content: canonical({ status: unknown ? 'uncertain' : 'not_dispatched',
521
+ reason: unknown ? 'effect_outcome_unknown' : current.snapshot.reason ?? 'run_ended' }) }, isError: true };
522
+ };
523
+ const view = deliveryStateFromEntries(current, entries, closure);
524
+ db.exec('COMMIT'); return view;
525
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
526
+ };
527
+ // Host-only recovery discovery. The caller supplies its trusted principal;
528
+ // a deleted mutable Agent draft must not hide a committed background Run.
529
+ // Return only scope identities, never configurations, inputs or receipts.
530
+ this.observation = (scope, runId) => {
531
+ open();
532
+ const key = runKey(scope, runId);
533
+ db.exec('BEGIN');
534
+ try {
535
+ const readHead = () => db.prepare(`SELECT json_extract(data,'$.snapshot.revision') AS revision,
536
+ json_extract(data,'$.snapshot.eventSeq') AS eventSeq, json_extract(data,'$.snapshot.configDigest') AS configDigest,
537
+ json_extract(data,'$.snapshot.sessionId') AS sessionId, json_extract(data,'$.lease') AS lease,
538
+ json_extract(data,'$.fence') AS fence, json_extract(data,'$.projections') AS projections
539
+ FROM native_runs WHERE key=?`).get(key);
540
+ let head = readHead();
541
+ invariant(head, 'run_not_found');
542
+ const row = db.prepare('SELECT data FROM native_run_observations WHERE run_key=?').get(key);
543
+ let view = row ? parseNativeJson(row.data) : null;
544
+ if (!view || view.contract !== NATIVE_RUN_OBSERVATION || view.commitProjection?.contract !== NATIVE_COMMIT_PROJECTION
545
+ || !Array.isArray(view.commitProjection?.tools?.calls) || !Array.isArray(view.facts?.toolOutcomes) || view.snapshot.revision !== head.revision
546
+ || view.snapshot.eventSeq !== head.eventSeq || view.snapshot.configDigest !== head.configDigest) {
547
+ // One transactional, lazy migration for old ledgers. Subsequent reads
548
+ // do not touch any event, intent, receipt, transcript or image data.
549
+ db.exec('ROLLBACK'); db.exec('BEGIN IMMEDIATE'); head = readHead();
550
+ invariant(head, 'run_not_found');
551
+ view = projectNativeRunObservation(read(key));
552
+ db.prepare(`INSERT INTO native_run_observations VALUES (?,?)
553
+ ON CONFLICT(run_key) DO UPDATE SET data=excluded.data`).run(key, canonical(view));
554
+ }
555
+ invariant(canonical(view.snapshot.scope) === canonical(scope) && view.snapshot.runId === runId
556
+ && view.snapshot.sessionId === head.sessionId, 'observation_identity_mismatch');
557
+ // Lease heartbeats and delivery acknowledgments can change without a
558
+ // snapshot revision. Read those live facts from the authoritative header.
559
+ view.lease = head.lease === null ? null : parseNativeJson(head.lease);
560
+ view.fence = head.fence; view.projections = parseNativeJson(head.projections);
561
+ db.exec('COMMIT'); return view;
562
+ } catch (error) { db.exec('ROLLBACK'); throw error; }
563
+ };
564
+ this.sessionToolOutcomes = (scope, sessionId) => {
565
+ open(); scopeKey(scope);
566
+ invariant(typeof sessionId === 'string' && sessionId.trim(), 'invalid_session_id');
567
+ // Old ledgers lazily build each missing Run view once. The steady-state
568
+ // session query touches only materialized identities, never tool outputs.
569
+ const missing = db.prepare(`SELECT json_extract(r.data,'$.snapshot.runId') AS runId
570
+ FROM native_runs r LEFT JOIN native_run_observations v ON v.run_key=r.key
571
+ WHERE json_extract(r.data,'$.snapshot.scope')=? AND json_extract(r.data,'$.snapshot.sessionId')=?
572
+ AND (v.data IS NULL OR json_extract(v.data,'$.contract')<>?
573
+ OR coalesce(json_extract(v.data,'$.commitProjection.contract'),'')<>?
574
+ OR coalesce(json_type(v.data,'$.commitProjection.tools.calls'),'null')<>'array'
575
+ OR coalesce(json_type(v.data,'$.facts.toolOutcomes'),'null')<>'array'
576
+ OR json_extract(v.data,'$.snapshot.revision')<>json_extract(r.data,'$.snapshot.revision')
577
+ OR json_extract(v.data,'$.snapshot.eventSeq')<>json_extract(r.data,'$.snapshot.eventSeq')
578
+ OR json_extract(v.data,'$.snapshot.configDigest')<>json_extract(r.data,'$.snapshot.configDigest')
579
+ OR json_extract(v.data,'$.snapshot.scope')<>json_extract(r.data,'$.snapshot.scope')
580
+ OR json_extract(v.data,'$.snapshot.sessionId')<>json_extract(r.data,'$.snapshot.sessionId')
581
+ OR json_extract(v.data,'$.snapshot.runId')<>json_extract(r.data,'$.snapshot.runId'))`)
582
+ .all(canonical(scope), sessionId, NATIVE_RUN_OBSERVATION, NATIVE_COMMIT_PROJECTION);
583
+ for (const { runId } of missing) this.observation(scope, runId);
584
+ return db.prepare(`SELECT json_extract(v.data,'$.snapshot.runId') AS runId,
585
+ json_extract(v.data,'$.snapshot.turnId') AS turnId, json_extract(c.value,'$.callId') AS callId,
586
+ json_extract(c.value,'$.status') AS status
587
+ FROM native_runs r JOIN native_run_observations v ON v.run_key=r.key
588
+ JOIN json_each(v.data,'$.facts.toolOutcomes') c
589
+ WHERE json_extract(r.data,'$.snapshot.scope')=? AND json_extract(r.data,'$.snapshot.sessionId')=?
590
+ AND json_extract(v.data,'$.snapshot.scope')=json_extract(r.data,'$.snapshot.scope')
591
+ AND json_extract(v.data,'$.snapshot.sessionId')=json_extract(r.data,'$.snapshot.sessionId')
592
+ AND json_extract(v.data,'$.snapshot.runId')=json_extract(r.data,'$.snapshot.runId')
593
+ AND json_extract(c.value,'$.status') IN ('uncertain','not_dispatched')
594
+ ORDER BY r.rowid,callId`).all(canonical(scope), sessionId).map(row => ({ ...row }));
595
+ };
596
+ this.uncertainToolCalls = (scope, sessionId) => this.sessionToolOutcomes(scope, sessionId)
597
+ .filter(row => row.status === 'uncertain').map(({ status, ...row }) => row);
598
+ this.listScopes = principal => {
599
+ open();
600
+ invariant(principal && typeof principal === 'object' && !Array.isArray(principal)
601
+ && Object.keys(principal).length === 3
602
+ && ['tenantId', 'workspaceId', 'userId'].every(key => Object.hasOwn(principal, key)
603
+ && typeof principal[key] === 'string' && principal[key].trim()), 'invalid_recovery_principal');
604
+ return db.prepare(`SELECT DISTINCT json_extract(data,'$.snapshot.scope') AS scope FROM native_runs
605
+ WHERE json_extract(data,'$.snapshot.scope.tenantId')=?
606
+ AND json_extract(data,'$.snapshot.scope.workspaceId')=?
607
+ AND json_extract(data,'$.snapshot.scope.userId')=? ORDER BY scope`)
608
+ .all(principal.tenantId, principal.workspaceId, principal.userId).map(row => {
609
+ const scope = parseNativeJson(row.scope);
610
+ scopeKey(scope);
611
+ invariant(['tenantId', 'workspaceId', 'userId'].every(key => scope[key] === principal[key]), 'recovery_scope_mismatch');
612
+ return scope;
613
+ });
614
+ };
615
+ this.close = () => { if (!closed) { db.close(); closed = true; } };
616
+ }
617
+ }