@kingsoftcloud/agentkit-harness-node 0.0.0-stage → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/NOTICE +1 -0
- package/README.md +124 -2
- package/THIRD_PARTY_NOTICES.md +66 -0
- package/ZCode-LICENSE +202 -0
- package/dist/index.mjs +4871 -0
- package/dist/native-command-plan.LICENSE.txt +221 -0
- package/dist/native-command-plan.json +16 -0
- package/dist/native-command-plan.mjs +8 -0
- package/dist/rpc-entry.mjs +5682 -0
- package/package.json +169 -4
- package/src/native/a2a-receipt-source.mjs +19 -0
- package/src/native/a2a-tools.mjs +517 -0
- package/src/native/activity-status.mjs +30 -0
- package/src/native/artifact-codec.mjs +481 -0
- package/src/native/artifact-store.mjs +1162 -0
- package/src/native/assistant-stream.mjs +76 -0
- package/src/native/background-command-port.mjs +95 -0
- package/src/native/browser-action-observation.mjs +43 -0
- package/src/native/browser-availability.mjs +22 -0
- package/src/native/browser-canvas-metrics.mjs +71 -0
- package/src/native/browser-dom.mjs +193 -0
- package/src/native/browser-playwright.mjs +1249 -0
- package/src/native/browser-vision.mjs +369 -0
- package/src/native/browser-workspace-policy.mjs +22 -0
- package/src/native/browser-workspace-protocol.mjs +95 -0
- package/src/native/browser-workspace-server.mjs +164 -0
- package/src/native/browser-workspace.mjs +355 -0
- package/src/native/captured-source.mjs +3 -0
- package/src/native/child-budget-allocation.mjs +87 -0
- package/src/native/child-capabilities.mjs +35 -0
- package/src/native/child-command-resource.mjs +20 -0
- package/src/native/child-history.mjs +188 -0
- package/src/native/child-merge.mjs +98 -0
- package/src/native/child-model-surface.mjs +19 -0
- package/src/native/child-observation.mjs +100 -0
- package/src/native/child-owner.mjs +620 -0
- package/src/native/child-publication.mjs +76 -0
- package/src/native/child-readers.mjs +366 -0
- package/src/native/child-tools.mjs +46 -0
- package/src/native/child-workspace-binding.mjs +29 -0
- package/src/native/child-workspace-tools.mjs +1 -0
- package/src/native/child-workspaces.mjs +655 -0
- package/src/native/children.mjs +365 -0
- package/src/native/command-plan-worker.mjs +14 -0
- package/src/native/command-plan.mjs +31 -0
- package/src/native/command-progress.mjs +64 -0
- package/src/native/command-shell.mjs +9 -0
- package/src/native/command-workspace-observation.mjs +36 -0
- package/src/native/component-tools.mjs +280 -0
- package/src/native/context-counter.mjs +133 -0
- package/src/native/context-markers.mjs +51 -0
- package/src/native/context-profile.mjs +72 -0
- package/src/native/context-status.mjs +27 -0
- package/src/native/conversation-command-control.mjs +133 -0
- package/src/native/delivery-timeline.mjs +39 -0
- package/src/native/dsh-background-ownership.mjs +102 -0
- package/src/native/dsh-managed-ownership.mjs +23 -0
- package/src/native/dsh-turn-contract.mjs +3 -0
- package/src/native/dsh-turn-ownership.mjs +54 -0
- package/src/native/dsh-turn-runtime.mjs +498 -0
- package/src/native/e2b-background-supervisor.mjs +347 -0
- package/src/native/e2b-background.mjs +368 -0
- package/src/native/e2b-command-state.mjs +40 -0
- package/src/native/e2b-command-tools.mjs +486 -0
- package/src/native/e2b-sandbox-owner.mjs +227 -0
- package/src/native/hook-executor.mjs +249 -0
- package/src/native/instruction-assembly.mjs +60 -0
- package/src/native/instruction-context.mjs +54 -0
- package/src/native/local-background-supervisor.mjs +108 -0
- package/src/native/local-background.mjs +207 -0
- package/src/native/local-command-evidence.mjs +57 -0
- package/src/native/local-command-policy-worker.mjs +197 -0
- package/src/native/local-command-policy.mjs +46 -0
- package/src/native/local-command-state.mjs +177 -0
- package/src/native/local-command-tools.mjs +302 -0
- package/src/native/local-execution-profile.mjs +36 -0
- package/src/native/local-output-policy.mjs +92 -0
- package/src/native/local-process-bin/darwin-arm64/launcher +0 -0
- package/src/native/local-process-bin/darwin-x64/launcher +0 -0
- package/src/native/local-process-bin/linux-arm64/launcher +0 -0
- package/src/native/local-process-bin/linux-x64/launcher +0 -0
- package/src/native/local-process-launcher.c +183 -0
- package/src/native/local-process-licenses/GCC-COPYING.RUNTIME +72 -0
- package/src/native/local-process-licenses/GCC-COPYING3 +674 -0
- package/src/native/local-process-licenses/NOTICE +21 -0
- package/src/native/local-process-licenses/musl-COPYRIGHT +193 -0
- package/src/native/local-process-manifest.json +32 -0
- package/src/native/local-process.mjs +246 -0
- package/src/native/local-resource-profile.mjs +32 -0
- package/src/native/memory-candidate.mjs +121 -0
- package/src/native/memory-extraction-run.mjs +60 -0
- package/src/native/memory-extraction-tools.mjs +116 -0
- package/src/native/memory-recall.mjs +65 -0
- package/src/native/memory-store.mjs +334 -0
- package/src/native/memory-tools.mjs +329 -0
- package/src/native/model-anthropic-cache.mjs +49 -0
- package/src/native/model-anthropic-tool-search.mjs +85 -0
- package/src/native/model-anthropic.mjs +753 -0
- package/src/native/model-chat-usage.mjs +24 -0
- package/src/native/model-inline-reasoning.mjs +111 -0
- package/src/native/model-media.mjs +174 -0
- package/src/native/model-normalize.mjs +228 -0
- package/src/native/model-openai-chat.mjs +561 -0
- package/src/native/model-port.mjs +361 -0
- package/src/native/model-provider-compat.mjs +129 -0
- package/src/native/model-replay.mjs +153 -0
- package/src/native/model-request.mjs +15 -0
- package/src/native/model-responses.mjs +1205 -0
- package/src/native/model-sse.mjs +171 -0
- package/src/native/model-surface-host.mjs +27 -0
- package/src/native/model-tool-search-policy.mjs +16 -0
- package/src/native/model-transport-observation.mjs +27 -0
- package/src/native/owned-child.mjs +114 -0
- package/src/native/owned-command.mjs +106 -0
- package/src/native/python-syntax-worker.mjs +60 -0
- package/src/native/python-syntax.mjs +50 -0
- package/src/native/read-interruption.mjs +16 -0
- package/src/native/result-stream.mjs +63 -0
- package/src/native/ruff-ast.mjs +95 -0
- package/src/native/run-observation.mjs +128 -0
- package/src/native/session-delivery-journal.mjs +96 -0
- package/src/native/session-delivery.mjs +434 -0
- package/src/native/shell-analysis.mjs +50 -0
- package/src/native/skill-disclosure.mjs +404 -0
- package/src/native/skill-events.mjs +164 -0
- package/src/native/skill-tools.mjs +813 -0
- package/src/native/source-page.mjs +73 -0
- package/src/native/sqlite-session-inbox.mjs +42 -0
- package/src/native/sqlite-store.mjs +617 -0
- package/src/native/tool-authority.mjs +99 -0
- package/src/native/tool-guidance.mjs +12 -0
- package/src/native/tool-observation.mjs +75 -0
- package/src/native/tool-states.mjs +14 -0
- package/src/native/verification-authority.mjs +37 -0
- package/src/native/web-tools.mjs +643 -0
- package/src/native/workspace-checkpoints.mjs +312 -0
- package/src/native/workspace-context.mjs +61 -0
- package/src/native/workspace-display.mjs +39 -0
- package/src/native/workspace-edit-match.mjs +19 -0
- package/src/native/workspace-edit-worker.mjs +15 -0
- package/src/native/workspace-editing.mjs +447 -0
- package/src/native/workspace-image.mjs +72 -0
- package/src/native/workspace-instructions.mjs +107 -0
- package/src/native/workspace-mutation-lock.mjs +74 -0
- package/src/native/workspace-patch-worker.mjs +17 -0
- package/src/native/workspace-patch.mjs +17 -0
- package/src/native/workspace-paths.mjs +225 -0
- package/src/native/workspace-state.mjs +272 -0
- package/src/native/workspace-subject-observer.mjs +84 -0
- 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
|
+
}
|