maka-agent 0.2.0-dev.46.20260922 → 0.2.0-dev.48.20260923
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/DISCLAIMER-WIP +6 -1
- package/README.md +6 -1
- package/dist/acp/maka-acp-agent.js +1 -1
- package/dist/acp/session-mcp.js +212 -0
- package/dist/acp/session-registry.js +52 -8
- package/dist/acp/stdio-server.js +3 -0
- package/dist/cli-core.js +1 -1
- package/dist/mcp-capability-provider.js +6 -1
- package/dist/mcp-capability-publication.js +139 -0
- package/dist/pi-tui-layout.js +46 -17
- package/dist/pi-tui-pickers.js +25 -14
- package/dist/pi-tui-runner.js +24 -15
- package/dist/runtime-host-capability-provider-command.js +25 -6
- package/dist/runtime-host-service-manager.js +26 -2
- package/dist/tui-context-refresh.js +15 -26
- package/dist/tui-mcp-control.js +66 -112
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/acp-executor-plugin/dist/acp-errors.js +26 -0
- package/node_modules/@maka/acp-executor-plugin/dist/acp-filesystem.js +103 -0
- package/node_modules/@maka/acp-executor-plugin/dist/acp-process.js +119 -0
- package/node_modules/@maka/acp-executor-plugin/dist/acp-projection.js +146 -0
- package/node_modules/@maka/acp-executor-plugin/dist/index.js +653 -0
- package/node_modules/@maka/acp-executor-plugin/dist/plugin.mjs +24446 -0
- package/node_modules/@maka/acp-executor-plugin/package.json +15 -0
- package/node_modules/@maka/antigravity-acp-plugin/dist/index.js +88 -0
- package/node_modules/@maka/antigravity-acp-plugin/dist/model-presentation.js +53 -0
- package/node_modules/@maka/antigravity-acp-plugin/dist/plugin.mjs +115 -0
- package/node_modules/@maka/antigravity-acp-plugin/package.json +15 -0
- package/node_modules/@maka/core/dist/client-capability-grant.js +2 -2
- package/node_modules/@maka/core/dist/executor-catalog.js +106 -0
- package/node_modules/@maka/core/dist/mcp.js +4 -1
- package/node_modules/@maka/core/dist/model-metadata.js +28 -1
- package/node_modules/@maka/core/dist/model-thinking.js +36 -1
- package/node_modules/@maka/core/dist/provider-registry.js +0 -1
- package/node_modules/@maka/core/dist/refresh-read-coordinator.js +62 -0
- package/node_modules/@maka/core/dist/runtime-event-store.js +25 -0
- package/node_modules/@maka/core/dist/runtime-invocation.js +5 -0
- package/node_modules/@maka/core/dist/runtime-policy/connection-catalog-codec.js +4 -0
- package/node_modules/@maka/core/dist/runtime-policy/credential-vault-codec.js +6 -0
- package/node_modules/@maka/core/dist/runtime-policy/policy-codec.js +21 -0
- package/node_modules/@maka/core/dist/session.js +19 -1
- package/node_modules/@maka/core/dist/settings.js +3 -0
- package/node_modules/@maka/core/dist/tool-ledger-scanner.js +365 -104
- package/node_modules/@maka/core/dist/turn-origin.js +14 -0
- package/node_modules/@maka/core/package.json +3 -0
- package/node_modules/@maka/mcp/dist/credential-coordinator.js +6 -1
- package/node_modules/@maka/mcp/dist/index.js +31 -5
- package/node_modules/@maka/mcp/dist/oauth.js +54 -6
- package/node_modules/@maka/runtime/dist/agent-run-inspect.js +1 -1
- package/node_modules/@maka/runtime/dist/ai-sdk-message-projection.js +15 -60
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +18 -0
- package/node_modules/@maka/runtime/dist/apply-patch-profile.js +64 -14
- package/node_modules/@maka/runtime/dist/builtin-tools.js +3 -2
- package/node_modules/@maka/runtime/dist/codex-v4a-patch.js +18 -0
- package/node_modules/@maka/runtime/dist/conversation-copy.js +56 -1
- package/node_modules/@maka/runtime/dist/mcp-tools.js +9 -2
- package/node_modules/@maka/runtime/dist/model-adapter.js +80 -3
- package/node_modules/@maka/runtime/dist/model-runtime.js +6 -0
- package/node_modules/@maka/runtime/dist/openai-apply-patch.js +5 -0
- package/node_modules/@maka/runtime/dist/plugin-executor-backend.js +122 -3
- package/node_modules/@maka/runtime/dist/plugin-executor-service.js +212 -16
- package/node_modules/@maka/runtime/dist/runtime-ledger-repair.js +2 -2
- package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +3 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +184 -126
- package/node_modules/@maka/runtime/dist/shell-run-manager.js +3 -0
- package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +29 -4
- package/node_modules/@maka/runtime-host/dist/candidate-entry.js +8 -5
- package/node_modules/@maka/runtime-host/dist/client/capability-provider-service.js +2 -0
- package/node_modules/@maka/runtime-host/dist/client/client-capability-channel.js +59 -20
- package/node_modules/@maka/runtime-host/dist/client/connection.js +6 -4
- package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
- package/node_modules/@maka/runtime-host/dist/client/reconnect-lifecycle.js +8 -1
- package/node_modules/@maka/runtime-host/dist/client/reconnecting-connection.js +4 -4
- package/node_modules/@maka/runtime-host/dist/client/session-catalog-summary.js +6 -1
- package/node_modules/@maka/runtime-host/dist/composition-identity.js +19 -0
- package/node_modules/@maka/runtime-host/dist/protocol/client-capability.js +20 -3
- package/node_modules/@maka/runtime-host/dist/protocol/external-session.js +8 -5
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +20 -2
- package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +34 -0
- package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +75 -5
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +1 -5
- package/node_modules/@maka/runtime-host/dist/server/builtin-external-agent-plugins.js +188 -0
- package/node_modules/@maka/runtime-host/dist/server/candidate.js +12 -7
- package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +234 -70
- package/node_modules/@maka/runtime-host/dist/server/client-capability-invocation-broker.js +14 -8
- package/node_modules/@maka/runtime-host/dist/server/client-capability-recovery.js +44 -46
- package/node_modules/@maka/runtime-host/dist/server/context-coordinator.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/execution-composition-factory.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +188 -21
- package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +6 -0
- package/node_modules/@maka/runtime-host/dist/server/extension-bundle.js +4 -0
- package/node_modules/@maka/runtime-host/dist/server/external-session-coordinator.js +29 -7
- package/node_modules/@maka/runtime-host/dist/server/host-composition-source.js +47 -0
- package/node_modules/@maka/runtime-host/dist/server/host-composition.js +1 -29
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +2 -9
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +75 -9
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +5 -1
- package/node_modules/@maka/runtime-host/dist/server/interaction-coordinator.js +45 -0
- package/node_modules/@maka/runtime-host/dist/server/jev-routing-model.js +157 -0
- package/node_modules/@maka/runtime-host/dist/server/listener-set.js +2 -2
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +4 -0
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +11 -1
- package/node_modules/@maka/runtime-host/dist/server/process-lifecycle.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/process-termination-error.js +27 -0
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +91 -12
- package/node_modules/@maka/runtime-host/dist/server/runtime-policy-coordinator.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/session-bundle-coordinator.js +4 -1
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +128 -13
- package/node_modules/@maka/runtime-host/dist/server/session-effect-coordinator.js +12 -6
- package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +2 -2
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +33 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +4 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-result-coordinator.js +173 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-result-runtime.js +293 -0
- package/node_modules/@maka/runtime-host/package.json +2 -0
- package/node_modules/@maka/storage/dist/agent-run-store-contract.js +8 -0
- package/node_modules/@maka/storage/dist/execution-stores.js +15 -3
- package/node_modules/@maka/storage/dist/mcp-config-store.js +91 -2
- package/node_modules/@maka/storage/dist/operational-state-store.js +15 -1
- package/node_modules/@maka/storage/dist/plan-authority.js +2 -0
- package/node_modules/@maka/storage/dist/plan-store.js +30 -0
- package/node_modules/@maka/storage/dist/runtime-event-persistence.js +1 -0
- package/node_modules/@maka/storage/dist/runtime-policy/credential-vault-document.js +4 -1
- package/node_modules/@maka/storage/dist/runtime-policy/policy-document.js +2 -0
- package/node_modules/@maka/storage/dist/session-store-values.js +16 -4
- package/node_modules/@maka/storage/dist/shell-run-authority.js +1 -0
- package/node_modules/@maka/storage/dist/shell-run-store.js +15 -0
- package/node_modules/@maka/storage/dist/sqlite-runtime-schema.js +74 -1
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +724 -84
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +16 -0
- package/package.json +1 -1
|
@@ -24,11 +24,12 @@ import { assertHandoffClaimSource } from '@maka/core/runtime-handoff';
|
|
|
24
24
|
import { buildWorkspaceBaselineAuthorityEvents, buildWorkspaceSuccessorAuthorityEvent, scanWorkspaceBaselineAuthority, WORKSPACE_AUTHORITY_SESSION_ID, WORKSPACE_VERSION_AUTHORITY_CAPABILITY_V1, } from '@maka/core/workspace-version-authority';
|
|
25
25
|
import { decodeRuntimeEvent, decodeRuntimeInvocationOpened, isPartialRuntimeEvent, isTerminalRuntimeEvent, runtimeEventInvocationOpening, } from '@maka/core/runtime-event';
|
|
26
26
|
import { RunSealedError, RUNTIME_CONTINUATION_AUTHORITY_V1, TOOL_RECOVERY_BUNDLE_CAPABILITY_V1, } from '@maka/core/runtime-event-store';
|
|
27
|
+
import { mayBeTranscriptLedgerInvocationId, } from '@maka/core/runtime-invocation';
|
|
27
28
|
import { stableJsonStringify } from '@maka/core/tool-args-identity';
|
|
28
29
|
import { mergeRuntimePartialSnapshots, groupRuntimePartialSnapshots, compareRuntimePartialSnapshots, partialRuntimeStream, completedPartialRuntimeStreamKey, } from './runtime-partial-values.js';
|
|
29
30
|
import { assertPreparedInput, assertOutcomeInput, assertPreparedIdentity, assertOutcomeIdentity, } from './tool-commit-validation.js';
|
|
30
31
|
import { encodeCanonicalRuntimeEvent } from '@maka/core/canonical-runtime-event';
|
|
31
|
-
import { scanToolLedger, ToolLedgerCorruptionError, ToolLedgerRejectionError, validateGenericToolLedgerAppend,
|
|
32
|
+
import { scanToolLedger, referencedToolOperationIds, ToolLedgerReducer, ToolLedgerCorruptionError, ToolLedgerRejectionError, validateGenericToolLedgerAppend, validateIncrementalToolLedgerTransition, } from '@maka/core/tool-ledger-scanner';
|
|
32
33
|
import { buildImmutableRuntimePrefix, buildImmutableRuntimePrefixProof, continuationStartEventMatchesClaim, decodeContinuationClaim, } from '@maka/core/runtime-boundary';
|
|
33
34
|
import { assertToolRecoveryEventBundle, interpretScannedToolRecovery, } from '@maka/core/tool-recovery-bundle';
|
|
34
35
|
import { configureSqliteRuntimeDatabase, migrateSqliteRuntimeDatabase, readUserVersion, RUNTIME_RECOVERY_AUTHORITY_CAPABILITY, RUNTIME_RECOVERY_AUTHORITY_CAPABILITY_VERSION, RUNTIME_CONTINUATION_AUTHORITY_CAPABILITY, RUNTIME_CONTINUATION_AUTHORITY_CAPABILITY_VERSION, RUNTIME_WORKSPACE_VERSION_AUTHORITY_CAPABILITY, RUNTIME_WORKSPACE_VERSION_AUTHORITY_CAPABILITY_VERSION, runtimeEventKind, SQLITE_RUNTIME_SCHEMA_VERSION, } from './sqlite-runtime-schema.js';
|
|
@@ -41,6 +42,24 @@ import { rebuildTranscriptTurnExtents, recordTranscriptTurnExtent, RuntimeTransc
|
|
|
41
42
|
export { SQLITE_RUNTIME_SCHEMA_VERSION } from './sqlite-runtime-schema.js';
|
|
42
43
|
const RUNTIME_EVENT_SCAN_BATCH_SIZE = 128;
|
|
43
44
|
const RUNTIME_PARTIAL_SEGMENT_TARGET_BYTES = 64 * 1024;
|
|
45
|
+
const RECOVERY_MESSAGE_QUERY_BATCH_SIZE = 400;
|
|
46
|
+
const TOOL_LEDGER_CACHE_EVENT_OVERHEAD_BYTES = 1024;
|
|
47
|
+
const DEFAULT_TOOL_LEDGER_CACHE_BUDGET = {
|
|
48
|
+
maxEntries: 32,
|
|
49
|
+
maxEstimatedBytes: 16 * 1024 * 1024,
|
|
50
|
+
maxSingleEntryEstimatedBytes: 8 * 1024 * 1024,
|
|
51
|
+
};
|
|
52
|
+
const IMMUTABLE_STEERING_SQL_PREDICATE = `
|
|
53
|
+
event_kind = 'text'
|
|
54
|
+
AND CASE
|
|
55
|
+
WHEN json_valid(payload_json)
|
|
56
|
+
THEN json_extract(payload_json, '$.partial')
|
|
57
|
+
END = 0
|
|
58
|
+
AND CASE
|
|
59
|
+
WHEN json_valid(payload_json)
|
|
60
|
+
THEN json_extract(payload_json, '$.content.steering')
|
|
61
|
+
END = 1
|
|
62
|
+
`;
|
|
44
63
|
function assertRuntimeEventScanBudget(budget) {
|
|
45
64
|
for (const [name, value] of Object.entries(budget)) {
|
|
46
65
|
if (!Number.isSafeInteger(value) || value < 1) {
|
|
@@ -61,6 +80,19 @@ function requireRuntimeEventScanCount(value) {
|
|
|
61
80
|
}
|
|
62
81
|
return value;
|
|
63
82
|
}
|
|
83
|
+
function normalizeToolLedgerCacheBudget(override) {
|
|
84
|
+
const budget = { ...DEFAULT_TOOL_LEDGER_CACHE_BUDGET, ...override };
|
|
85
|
+
for (const [name, value] of Object.entries(budget)) {
|
|
86
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
87
|
+
throw new Error(`Invalid tool ledger cache ${name}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return budget;
|
|
91
|
+
}
|
|
92
|
+
function estimateToolLedgerCacheBytes(storedPayloadBytes, eventCount) {
|
|
93
|
+
const estimate = storedPayloadBytes * 2 + eventCount * TOOL_LEDGER_CACHE_EVENT_OVERHEAD_BYTES;
|
|
94
|
+
return Number.isSafeInteger(estimate) ? estimate : Number.MAX_SAFE_INTEGER;
|
|
95
|
+
}
|
|
64
96
|
const require = createRequire(import.meta.url);
|
|
65
97
|
function loadDatabaseSync() {
|
|
66
98
|
return require('node:sqlite').DatabaseSync;
|
|
@@ -82,12 +114,26 @@ export class SqliteRuntimeStore {
|
|
|
82
114
|
workspaceVersionAuthorityCapability = WORKSPACE_VERSION_AUTHORITY_CAPABILITY_V1;
|
|
83
115
|
db;
|
|
84
116
|
databaseLease;
|
|
85
|
-
|
|
117
|
+
toolLedgerCacheBudget;
|
|
118
|
+
toolLedgerCache = new Map();
|
|
119
|
+
toolLedgerCacheEstimatedBytes = 0;
|
|
120
|
+
toolLedgerCacheEventCount = 0;
|
|
121
|
+
toolLedgerCacheMetrics = {
|
|
122
|
+
hits: 0,
|
|
123
|
+
misses: 0,
|
|
124
|
+
budgetEvictions: 0,
|
|
125
|
+
terminalEvictions: 0,
|
|
126
|
+
transientEntries: 0,
|
|
127
|
+
};
|
|
128
|
+
uncommittedToolLedgerStates = new Map();
|
|
129
|
+
toolLedgerCacheVersion;
|
|
130
|
+
localCommittedWriteRevision = 0;
|
|
86
131
|
closed = false;
|
|
87
132
|
uncommittedEventSessions = new Set();
|
|
88
133
|
eventCommitListeners = new Set();
|
|
89
134
|
constructor(path, options = {}) {
|
|
90
135
|
this.options = options;
|
|
136
|
+
this.toolLedgerCacheBudget = normalizeToolLedgerCacheBudget(options.toolLedgerCacheBudget);
|
|
91
137
|
if (options.readOnly && options.databaseLease) {
|
|
92
138
|
throw new Error('Operational state database leases cannot be opened read-only');
|
|
93
139
|
}
|
|
@@ -102,7 +148,6 @@ export class SqliteRuntimeStore {
|
|
|
102
148
|
registerRecallFoldFunction(this.db);
|
|
103
149
|
if (!options.readOnly) {
|
|
104
150
|
this.registerWorkspaceBaselineAuthorityWriter();
|
|
105
|
-
this.refreshToolLedgerHealth();
|
|
106
151
|
}
|
|
107
152
|
return;
|
|
108
153
|
}
|
|
@@ -128,7 +173,6 @@ export class SqliteRuntimeStore {
|
|
|
128
173
|
registerRecallFoldFunction(this.db);
|
|
129
174
|
if (!options.readOnly) {
|
|
130
175
|
this.registerWorkspaceBaselineAuthorityWriter();
|
|
131
|
-
this.refreshToolLedgerHealth();
|
|
132
176
|
}
|
|
133
177
|
}
|
|
134
178
|
catch (error) {
|
|
@@ -152,6 +196,8 @@ export class SqliteRuntimeStore {
|
|
|
152
196
|
if (this.closed)
|
|
153
197
|
return;
|
|
154
198
|
this.closed = true;
|
|
199
|
+
this.clearToolLedgerCache();
|
|
200
|
+
this.uncommittedToolLedgerStates.clear();
|
|
155
201
|
if (this.databaseLease)
|
|
156
202
|
this.databaseLease.close();
|
|
157
203
|
else
|
|
@@ -195,6 +241,7 @@ export class SqliteRuntimeStore {
|
|
|
195
241
|
existing.at(-1)?.id !== canonicalEvent.id) {
|
|
196
242
|
throw new Error('Terminal RuntimeEvent must be the immutable ledger tail');
|
|
197
243
|
}
|
|
244
|
+
this.invalidateToolLedgerCacheForInvocation(canonicalEvent.invocationId);
|
|
198
245
|
return;
|
|
199
246
|
}
|
|
200
247
|
const existingTerminal = existing.find(isTerminalRuntimeEvent);
|
|
@@ -334,6 +381,202 @@ export class SqliteRuntimeStore {
|
|
|
334
381
|
assertRuntimeStorageSafeId(sessionId, 'Invalid session id');
|
|
335
382
|
return this.readTransaction(() => this.readInvocationOpeningsSync(sessionId, { direction: 'asc' }).map((row) => this.completeInvocationRecordSync(row)));
|
|
336
383
|
}
|
|
384
|
+
async listInvocationRecoveryInventory(sessionIds) {
|
|
385
|
+
const selectedSessionIds = [...new Set(sessionIds)];
|
|
386
|
+
for (const sessionId of selectedSessionIds) {
|
|
387
|
+
assertRuntimeStorageSafeId(sessionId, 'Invalid session id');
|
|
388
|
+
}
|
|
389
|
+
if (selectedSessionIds.length === 0)
|
|
390
|
+
return [];
|
|
391
|
+
const selectedJson = JSON.stringify(selectedSessionIds);
|
|
392
|
+
return this.readTransaction(() => {
|
|
393
|
+
const eventRows = this.db
|
|
394
|
+
.prepare(`
|
|
395
|
+
WITH selected(session_id) AS (SELECT value FROM json_each(?))
|
|
396
|
+
SELECT
|
|
397
|
+
opening.rowid AS opening_rowid,
|
|
398
|
+
opening.session_id AS session_id,
|
|
399
|
+
opening.invocation_id AS invocation_id,
|
|
400
|
+
1 AS from_events,
|
|
401
|
+
NULL AS run_id,
|
|
402
|
+
NULL AS turn_id,
|
|
403
|
+
NULL AS opened_at,
|
|
404
|
+
(
|
|
405
|
+
SELECT terminal.rowid
|
|
406
|
+
FROM runtime_events AS terminal INDEXED BY runtime_events_terminal
|
|
407
|
+
WHERE terminal.invocation_id = opening.invocation_id
|
|
408
|
+
AND ${TERMINAL_RUNTIME_EVENT_SQL.replaceAll('payload_json', 'terminal.payload_json')}
|
|
409
|
+
ORDER BY terminal.event_seq
|
|
410
|
+
LIMIT 1
|
|
411
|
+
) AS terminal_rowid
|
|
412
|
+
FROM selected
|
|
413
|
+
JOIN runtime_events AS opening INDEXED BY runtime_events_by_session_kind
|
|
414
|
+
ON opening.session_id = selected.session_id
|
|
415
|
+
AND opening.event_kind = 'invocation_opened'
|
|
416
|
+
`)
|
|
417
|
+
.all(selectedJson);
|
|
418
|
+
const legacyRows = this.db
|
|
419
|
+
.prepare(`
|
|
420
|
+
WITH selected(session_id) AS (SELECT value FROM json_each(?))
|
|
421
|
+
SELECT
|
|
422
|
+
NULL AS opening_rowid,
|
|
423
|
+
legacy.session_id AS session_id,
|
|
424
|
+
legacy.invocation_id AS invocation_id,
|
|
425
|
+
legacy.run_id AS run_id,
|
|
426
|
+
legacy.turn_id AS turn_id,
|
|
427
|
+
legacy.opened_at AS opened_at,
|
|
428
|
+
0 AS from_events,
|
|
429
|
+
(
|
|
430
|
+
SELECT terminal.rowid
|
|
431
|
+
FROM runtime_events AS terminal INDEXED BY runtime_events_terminal
|
|
432
|
+
WHERE terminal.invocation_id = legacy.invocation_id
|
|
433
|
+
AND ${TERMINAL_RUNTIME_EVENT_SQL.replaceAll('payload_json', 'terminal.payload_json')}
|
|
434
|
+
ORDER BY terminal.event_seq
|
|
435
|
+
LIMIT 1
|
|
436
|
+
) AS terminal_rowid
|
|
437
|
+
FROM selected
|
|
438
|
+
JOIN runtime_legacy_invocation_openings AS legacy
|
|
439
|
+
ON legacy.session_id = selected.session_id
|
|
440
|
+
WHERE NOT EXISTS (
|
|
441
|
+
SELECT 1 FROM runtime_events AS opening
|
|
442
|
+
WHERE opening.invocation_id = legacy.invocation_id
|
|
443
|
+
AND opening.event_kind = 'invocation_opened'
|
|
444
|
+
)
|
|
445
|
+
`)
|
|
446
|
+
.all(selectedJson);
|
|
447
|
+
const handoffRows = this.db
|
|
448
|
+
.prepare(`
|
|
449
|
+
WITH selected(session_id) AS (SELECT value FROM json_each(?))
|
|
450
|
+
SELECT
|
|
451
|
+
terminal.rowid AS terminal_rowid,
|
|
452
|
+
terminal.event_id AS event_id,
|
|
453
|
+
terminal.session_id AS session_id,
|
|
454
|
+
terminal.invocation_id AS invocation_id,
|
|
455
|
+
terminal.run_id AS run_id,
|
|
456
|
+
terminal.turn_id AS turn_id,
|
|
457
|
+
terminal.payload_json AS payload_json
|
|
458
|
+
FROM selected
|
|
459
|
+
JOIN runtime_events AS terminal INDEXED BY runtime_events_by_session_kind
|
|
460
|
+
ON terminal.session_id = selected.session_id
|
|
461
|
+
AND terminal.event_kind = 'invocation_end'
|
|
462
|
+
WHERE CASE
|
|
463
|
+
WHEN json_valid(terminal.payload_json)
|
|
464
|
+
THEN json_type(terminal.payload_json, '$.actions.handoffPause')
|
|
465
|
+
END IS NOT NULL
|
|
466
|
+
`)
|
|
467
|
+
.all(selectedJson);
|
|
468
|
+
const handoffByRowId = new Map(handoffRows.map((row) => [row.terminal_rowid, row]));
|
|
469
|
+
const eventIdentity = this.db.prepare(`
|
|
470
|
+
SELECT event_id, session_id, invocation_id, run_id, turn_id, committed_at
|
|
471
|
+
FROM runtime_events
|
|
472
|
+
WHERE rowid = ?
|
|
473
|
+
`);
|
|
474
|
+
const eventCandidate = this.db.prepare(`
|
|
475
|
+
SELECT event_id, session_id, invocation_id, run_id, turn_id, committed_at, payload_json
|
|
476
|
+
FROM runtime_events
|
|
477
|
+
WHERE rowid = ?
|
|
478
|
+
`);
|
|
479
|
+
const legacyCandidate = this.db.prepare(`
|
|
480
|
+
SELECT opening_json
|
|
481
|
+
FROM runtime_legacy_invocation_openings
|
|
482
|
+
WHERE invocation_id = ?
|
|
483
|
+
`);
|
|
484
|
+
return [...eventRows, ...legacyRows]
|
|
485
|
+
.sort((a, b) => a.session_id.localeCompare(b.session_id) ||
|
|
486
|
+
a.invocation_id.localeCompare(b.invocation_id))
|
|
487
|
+
.map((row) => {
|
|
488
|
+
const base = { sessionId: row.session_id, invocationId: row.invocation_id };
|
|
489
|
+
const handoff = row.terminal_rowid === null ? undefined : handoffByRowId.get(row.terminal_rowid);
|
|
490
|
+
const needsCandidate = row.terminal_rowid === null || handoff !== undefined;
|
|
491
|
+
const needsIdentity = needsCandidate || mayBeTranscriptLedgerInvocationId(row.invocation_id);
|
|
492
|
+
if (!needsIdentity)
|
|
493
|
+
return base;
|
|
494
|
+
let identity;
|
|
495
|
+
let openingJson;
|
|
496
|
+
let openingEventId;
|
|
497
|
+
if (row.from_events === 1) {
|
|
498
|
+
if (row.opening_rowid === null) {
|
|
499
|
+
throw new Error(`Runtime invocation opening row is missing: ${row.invocation_id}`);
|
|
500
|
+
}
|
|
501
|
+
const detail = (needsCandidate ? eventCandidate : eventIdentity).get(row.opening_rowid);
|
|
502
|
+
if (!detail ||
|
|
503
|
+
detail.session_id !== row.session_id ||
|
|
504
|
+
detail.invocation_id !== row.invocation_id) {
|
|
505
|
+
throw new Error(`Runtime invocation opening identity changed: ${row.invocation_id}`);
|
|
506
|
+
}
|
|
507
|
+
identity = {
|
|
508
|
+
sessionId: detail.session_id,
|
|
509
|
+
invocationId: detail.invocation_id,
|
|
510
|
+
runId: detail.run_id,
|
|
511
|
+
turnId: detail.turn_id,
|
|
512
|
+
openedAt: detail.committed_at,
|
|
513
|
+
};
|
|
514
|
+
openingEventId = detail.event_id;
|
|
515
|
+
openingJson = detail.payload_json;
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
if (row.run_id === null || row.turn_id === null || row.opened_at === null) {
|
|
519
|
+
throw new Error(`Legacy invocation opening identity is incomplete: ${row.invocation_id}`);
|
|
520
|
+
}
|
|
521
|
+
identity = {
|
|
522
|
+
sessionId: row.session_id,
|
|
523
|
+
invocationId: row.invocation_id,
|
|
524
|
+
runId: row.run_id,
|
|
525
|
+
turnId: row.turn_id,
|
|
526
|
+
openedAt: row.opened_at,
|
|
527
|
+
};
|
|
528
|
+
if (needsCandidate) {
|
|
529
|
+
openingJson = legacyCandidate.get(row.invocation_id)?.opening_json;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
if (!needsCandidate)
|
|
533
|
+
return { ...base, identity };
|
|
534
|
+
if (typeof openingJson !== 'string') {
|
|
535
|
+
throw new Error(`Runtime invocation opening payload is missing: ${row.invocation_id}`);
|
|
536
|
+
}
|
|
537
|
+
let opening;
|
|
538
|
+
if (row.from_events === 1) {
|
|
539
|
+
const event = decodeRuntimeEventStorageRow({
|
|
540
|
+
event_id: openingEventId ?? '',
|
|
541
|
+
session_id: identity.sessionId,
|
|
542
|
+
invocation_id: identity.invocationId,
|
|
543
|
+
run_id: identity.runId,
|
|
544
|
+
turn_id: identity.turnId,
|
|
545
|
+
payload_json: openingJson,
|
|
546
|
+
});
|
|
547
|
+
const decoded = runtimeEventInvocationOpening(event);
|
|
548
|
+
if (!decoded) {
|
|
549
|
+
throw new Error(`RuntimeEvent ${event.id} is indexed as an opening fact but is not one`);
|
|
550
|
+
}
|
|
551
|
+
opening = decoded;
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
opening = decodeRuntimeInvocationOpened(JSON.parse(openingJson));
|
|
555
|
+
}
|
|
556
|
+
let terminalEvent;
|
|
557
|
+
if (handoff) {
|
|
558
|
+
if (handoff.session_id !== identity.sessionId ||
|
|
559
|
+
handoff.invocation_id !== identity.invocationId ||
|
|
560
|
+
handoff.run_id !== identity.runId ||
|
|
561
|
+
handoff.turn_id !== identity.turnId) {
|
|
562
|
+
throw new Error(`Runtime handoff terminal identity changed: ${row.invocation_id}`);
|
|
563
|
+
}
|
|
564
|
+
terminalEvent = decodeRuntimeEventStorageRow(handoff);
|
|
565
|
+
if (!isTerminalRuntimeEvent(terminalEvent)) {
|
|
566
|
+
throw new Error(`Runtime handoff fact is not terminal: ${terminalEvent.id}`);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return {
|
|
570
|
+
...base,
|
|
571
|
+
candidate: {
|
|
572
|
+
...identity,
|
|
573
|
+
opening,
|
|
574
|
+
...(terminalEvent ? { terminalEvent } : {}),
|
|
575
|
+
},
|
|
576
|
+
};
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
}
|
|
337
580
|
async readRunInvocation(sessionId, runId) {
|
|
338
581
|
assertRuntimeStorageSafeId(sessionId, 'Invalid session id');
|
|
339
582
|
assertRuntimeStorageSafeId(runId, 'Invalid run id');
|
|
@@ -964,6 +1207,51 @@ export class SqliteRuntimeStore {
|
|
|
964
1207
|
const row = this.readContinuationClaimRow('boundary_digest = ?', boundaryDigest);
|
|
965
1208
|
return row ? this.decodeContinuationClaimStateRow(row) : undefined;
|
|
966
1209
|
}
|
|
1210
|
+
async listUnsettledContinuationClaimsForRecovery(sessionIds) {
|
|
1211
|
+
const selectedSessionIds = [...new Set(sessionIds)];
|
|
1212
|
+
for (const sessionId of selectedSessionIds) {
|
|
1213
|
+
assertRuntimeStorageSafeId(sessionId, 'Invalid session id');
|
|
1214
|
+
}
|
|
1215
|
+
if (selectedSessionIds.length === 0)
|
|
1216
|
+
return [];
|
|
1217
|
+
const rows = this.db
|
|
1218
|
+
.prepare(`
|
|
1219
|
+
WITH selected(session_id) AS (SELECT value FROM json_each(?))
|
|
1220
|
+
SELECT
|
|
1221
|
+
claim.claim_id,
|
|
1222
|
+
claim.source_session_id,
|
|
1223
|
+
claim.source_invocation_id,
|
|
1224
|
+
claim.source_run_id,
|
|
1225
|
+
claim.source_turn_id,
|
|
1226
|
+
claim.source_event_high_water,
|
|
1227
|
+
claim.source_prefix_digest,
|
|
1228
|
+
claim.boundary_digest,
|
|
1229
|
+
claim.boundary_json,
|
|
1230
|
+
claim.provider_projection_version,
|
|
1231
|
+
claim.provider_replay_digest,
|
|
1232
|
+
claim.target_session_id,
|
|
1233
|
+
claim.target_invocation_id,
|
|
1234
|
+
claim.target_run_id,
|
|
1235
|
+
claim.target_turn_id,
|
|
1236
|
+
claim.target_opening_json,
|
|
1237
|
+
claim.claimed_at,
|
|
1238
|
+
claim.start_event_id,
|
|
1239
|
+
claim.start_kind,
|
|
1240
|
+
claim.protocol_version
|
|
1241
|
+
FROM selected
|
|
1242
|
+
JOIN runtime_continuation_claims AS claim
|
|
1243
|
+
ON claim.target_session_id = selected.session_id
|
|
1244
|
+
WHERE NOT EXISTS (
|
|
1245
|
+
SELECT 1
|
|
1246
|
+
FROM runtime_events AS terminal INDEXED BY runtime_events_terminal
|
|
1247
|
+
WHERE terminal.invocation_id = claim.target_invocation_id
|
|
1248
|
+
AND ${TERMINAL_RUNTIME_EVENT_SQL.replaceAll('payload_json', 'terminal.payload_json')}
|
|
1249
|
+
)
|
|
1250
|
+
ORDER BY claim.claimed_at ASC, claim.claim_id ASC
|
|
1251
|
+
`)
|
|
1252
|
+
.all(JSON.stringify(selectedSessionIds));
|
|
1253
|
+
return rows.map((row) => this.decodeContinuationClaimStateRow(row));
|
|
1254
|
+
}
|
|
967
1255
|
async listContinuationClaimsForRecovery(sessionId) {
|
|
968
1256
|
const rows = this.db
|
|
969
1257
|
.prepare(`
|
|
@@ -1041,25 +1329,119 @@ export class SqliteRuntimeStore {
|
|
|
1041
1329
|
async readImmutableSteeringMessageProof(sessionId, messageId) {
|
|
1042
1330
|
assertRuntimeStorageSafeId(sessionId, 'Invalid session id');
|
|
1043
1331
|
assertRuntimeStorageSafeId(messageId, 'Invalid message id');
|
|
1044
|
-
const matches = this.
|
|
1332
|
+
const matches = this.readImmutableSteeringMessageEvents(sessionId, messageId);
|
|
1045
1333
|
if (matches.length > 1) {
|
|
1046
1334
|
throw new Error(`Immutable steering message identity conflict: ${messageId}`);
|
|
1047
1335
|
}
|
|
1048
1336
|
return matches[0] ? Object.freeze({ event: matches[0] }) : undefined;
|
|
1049
1337
|
}
|
|
1050
|
-
async
|
|
1051
|
-
assertRuntimeStorageSafeId(sessionId, 'Invalid session id');
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1338
|
+
async readRecoveryMessageEvents(input) {
|
|
1339
|
+
assertRuntimeStorageSafeId(input.sessionId, 'Invalid session id');
|
|
1340
|
+
assertEvidenceReadBudget(input.budget);
|
|
1341
|
+
const turnIds = distinctSafeRuntimeIds(input.turnIds, 'Invalid recovery Turn id');
|
|
1342
|
+
const eventIds = distinctSafeRuntimeIds(input.eventIds, 'Invalid recovery event id');
|
|
1343
|
+
const selectedEventIds = new Set();
|
|
1344
|
+
const measuredRows = [];
|
|
1345
|
+
const measureEventsById = (ids) => {
|
|
1346
|
+
for (const batch of batches(ids, RECOVERY_MESSAGE_QUERY_BATCH_SIZE)) {
|
|
1347
|
+
const placeholders = batch.map(() => '?').join(', ');
|
|
1348
|
+
const rows = this.db
|
|
1349
|
+
.prepare(`
|
|
1350
|
+
SELECT event_id, session_id,
|
|
1351
|
+
length(CAST(payload_json AS BLOB)) AS stored_bytes
|
|
1352
|
+
FROM runtime_events
|
|
1353
|
+
WHERE event_id IN (${placeholders})
|
|
1354
|
+
`)
|
|
1355
|
+
.all(...batch);
|
|
1356
|
+
for (const row of rows) {
|
|
1357
|
+
if (typeof row.event_id !== 'string' || typeof row.session_id !== 'string') {
|
|
1358
|
+
throw new Error('Invalid recovery RuntimeEvent measurement row');
|
|
1359
|
+
}
|
|
1360
|
+
if (row.session_id !== input.sessionId || selectedEventIds.has(row.event_id))
|
|
1361
|
+
continue;
|
|
1362
|
+
selectedEventIds.add(row.event_id);
|
|
1363
|
+
measuredRows.push({ event_id: row.event_id, stored_bytes: row.stored_bytes });
|
|
1364
|
+
if (measuredRows.length > input.budget.maxRecords)
|
|
1365
|
+
return false;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
return true;
|
|
1369
|
+
};
|
|
1370
|
+
if (!measureEventsById(eventIds))
|
|
1371
|
+
return { status: 'limit_exceeded' };
|
|
1372
|
+
const additionalTurnEventIds = new Set();
|
|
1373
|
+
for (const batch of batches(turnIds, RECOVERY_MESSAGE_QUERY_BATCH_SIZE)) {
|
|
1374
|
+
const placeholders = batch.map(() => '?').join(', ');
|
|
1375
|
+
const rows = this.db
|
|
1376
|
+
.prepare(`
|
|
1377
|
+
SELECT event_id
|
|
1378
|
+
FROM runtime_events
|
|
1379
|
+
WHERE session_id = ?
|
|
1380
|
+
AND turn_id IN (${placeholders})
|
|
1381
|
+
AND event_kind = 'text'
|
|
1382
|
+
AND CASE
|
|
1383
|
+
WHEN json_valid(payload_json)
|
|
1384
|
+
THEN json_extract(payload_json, '$.partial')
|
|
1385
|
+
END = 0
|
|
1386
|
+
AND CASE
|
|
1387
|
+
WHEN json_valid(payload_json)
|
|
1388
|
+
THEN json_extract(payload_json, '$.role')
|
|
1389
|
+
END = 'user'
|
|
1390
|
+
AND coalesce(CASE
|
|
1391
|
+
WHEN json_valid(payload_json)
|
|
1392
|
+
THEN json_extract(payload_json, '$.content.steering')
|
|
1393
|
+
END, 0) = 0
|
|
1394
|
+
LIMIT ?
|
|
1395
|
+
`)
|
|
1396
|
+
.all(input.sessionId, ...batch, input.budget.maxRecords + 1);
|
|
1397
|
+
for (const row of rows) {
|
|
1398
|
+
if (typeof row.event_id !== 'string') {
|
|
1399
|
+
throw new Error('Invalid recovery RuntimeEvent identity row');
|
|
1400
|
+
}
|
|
1401
|
+
if (!selectedEventIds.has(row.event_id))
|
|
1402
|
+
additionalTurnEventIds.add(row.event_id);
|
|
1403
|
+
if (selectedEventIds.size + additionalTurnEventIds.size > input.budget.maxRecords) {
|
|
1404
|
+
return { status: 'limit_exceeded' };
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
if (!measureEventsById([...additionalTurnEventIds]))
|
|
1409
|
+
return { status: 'limit_exceeded' };
|
|
1410
|
+
const measurement = measureEvidenceRows(measuredRows, input.budget, 'Invalid recovery RuntimeEvent evidence measurement row');
|
|
1411
|
+
if (!measurement)
|
|
1412
|
+
return { status: 'limit_exceeded' };
|
|
1413
|
+
const storedBytesById = new Map(measuredRows.map((row) => [row.event_id, requireRuntimeEventScanCount(row.stored_bytes)]));
|
|
1414
|
+
const events = new Map();
|
|
1415
|
+
for (const batch of batches([...selectedEventIds], RECOVERY_MESSAGE_QUERY_BATCH_SIZE)) {
|
|
1416
|
+
const placeholders = batch.map(() => '?').join(', ');
|
|
1417
|
+
const rows = this.db
|
|
1418
|
+
.prepare(`
|
|
1419
|
+
SELECT event_id, session_id, invocation_id, run_id, turn_id, payload_json,
|
|
1420
|
+
length(CAST(payload_json AS BLOB)) AS stored_bytes
|
|
1421
|
+
FROM runtime_events
|
|
1422
|
+
WHERE event_id IN (${placeholders})
|
|
1423
|
+
`)
|
|
1424
|
+
.all(...batch);
|
|
1425
|
+
for (const row of rows) {
|
|
1426
|
+
const event = decodeRuntimeEventStorageRow(row);
|
|
1427
|
+
if (event.sessionId !== input.sessionId)
|
|
1428
|
+
continue;
|
|
1429
|
+
if (storedBytesById.get(event.id) !== requireRuntimeEventScanCount(row.stored_bytes)) {
|
|
1430
|
+
throw new Error('Recovery RuntimeEvent evidence changed while reading');
|
|
1431
|
+
}
|
|
1432
|
+
events.set(event.id, event);
|
|
1060
1433
|
}
|
|
1061
|
-
messages.set(messageId, event);
|
|
1062
1434
|
}
|
|
1435
|
+
if (events.size !== selectedEventIds.size) {
|
|
1436
|
+
throw new Error('Recovery RuntimeEvent evidence changed while reading');
|
|
1437
|
+
}
|
|
1438
|
+
return {
|
|
1439
|
+
status: 'complete',
|
|
1440
|
+
records: [...events.values()].sort((left, right) => left.ts - right.ts ||
|
|
1441
|
+
left.runId.localeCompare(right.runId) ||
|
|
1442
|
+
left.id.localeCompare(right.id)),
|
|
1443
|
+
...measurement,
|
|
1444
|
+
};
|
|
1063
1445
|
}
|
|
1064
1446
|
/**
|
|
1065
1447
|
* Narrows recall to the Sessions whose ledger could project a message
|
|
@@ -2235,25 +2617,37 @@ export class SqliteRuntimeStore {
|
|
|
2235
2617
|
async readToolOperation(operationId) {
|
|
2236
2618
|
return this.readToolOperationSync(operationId);
|
|
2237
2619
|
}
|
|
2238
|
-
async listUnsettledToolOperations(
|
|
2620
|
+
async listUnsettledToolOperations(sessionIds) {
|
|
2621
|
+
const selectedSessionIds = sessionIds === undefined
|
|
2622
|
+
? undefined
|
|
2623
|
+
: [...new Set(typeof sessionIds === 'string' ? [sessionIds] : sessionIds)];
|
|
2624
|
+
if (selectedSessionIds?.length === 0)
|
|
2625
|
+
return [];
|
|
2239
2626
|
const query = `
|
|
2240
|
-
SELECT
|
|
2241
|
-
|
|
2242
|
-
|
|
2627
|
+
SELECT call_event.session_id,
|
|
2628
|
+
tool_operations.operation_id, tool_operations.invocation_id,
|
|
2629
|
+
tool_operations.run_id, tool_operations.turn_id,
|
|
2630
|
+
tool_operations.provider_tool_call_id, tool_operations.tool_name,
|
|
2631
|
+
tool_operations.canonical_args_hash, tool_operations.recovery_mode,
|
|
2632
|
+
tool_operations.current_state, tool_operations.call_event_id,
|
|
2633
|
+
tool_operations.dispatch_event_id, tool_operations.result_event_id,
|
|
2634
|
+
tool_operations.version
|
|
2243
2635
|
FROM tool_operations
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2636
|
+
JOIN runtime_events AS call_event
|
|
2637
|
+
ON call_event.event_id = tool_operations.call_event_id
|
|
2638
|
+
WHERE tool_operations.current_state = 'prepared'
|
|
2639
|
+
AND tool_operations.result_event_id IS NULL
|
|
2640
|
+
AND tool_operations.dispatch_event_id IS NOT NULL
|
|
2641
|
+
${selectedSessionIds === undefined
|
|
2248
2642
|
? ''
|
|
2249
|
-
: 'AND
|
|
2250
|
-
ORDER BY invocation_id ASC, operation_id ASC
|
|
2643
|
+
: 'AND call_event.session_id IN (SELECT value FROM json_each(?))'}
|
|
2644
|
+
ORDER BY tool_operations.invocation_id ASC, tool_operations.operation_id ASC
|
|
2251
2645
|
`;
|
|
2252
2646
|
const statement = this.db.prepare(query);
|
|
2253
|
-
const rows = (
|
|
2647
|
+
const rows = (selectedSessionIds === undefined
|
|
2254
2648
|
? statement.all()
|
|
2255
|
-
: statement.all(
|
|
2256
|
-
return rows.map(toolOperationFromRow);
|
|
2649
|
+
: statement.all(JSON.stringify(selectedSessionIds)));
|
|
2650
|
+
return rows.map((row) => ({ sessionId: row.session_id, ...toolOperationFromRow(row) }));
|
|
2257
2651
|
}
|
|
2258
2652
|
async readToolJournal(operationId) {
|
|
2259
2653
|
const rows = this.db
|
|
@@ -2268,6 +2662,7 @@ export class SqliteRuntimeStore {
|
|
|
2268
2662
|
.all(operationId);
|
|
2269
2663
|
return rows.map(toolJournalRecordFromRow);
|
|
2270
2664
|
}
|
|
2665
|
+
/** Explicit full-workspace integrity audit and disposable projection rebuild. */
|
|
2271
2666
|
async rebuildToolProjectionsFromRuntimeEvents() {
|
|
2272
2667
|
return this.transaction(() => this.rebuildToolProjectionsFromRuntimeEventsSync());
|
|
2273
2668
|
}
|
|
@@ -2529,8 +2924,16 @@ export class SqliteRuntimeStore {
|
|
|
2529
2924
|
}
|
|
2530
2925
|
}
|
|
2531
2926
|
transaction(operation) {
|
|
2532
|
-
if (this.databaseLease)
|
|
2533
|
-
|
|
2927
|
+
if (this.databaseLease) {
|
|
2928
|
+
const nested = this.db.isTransaction;
|
|
2929
|
+
return this.databaseLease.transaction('write', () => {
|
|
2930
|
+
// A sibling lease may have changed the shared transaction view since
|
|
2931
|
+
// this store last used its reconstructible cache.
|
|
2932
|
+
if (nested)
|
|
2933
|
+
this.clearToolLedgerCache();
|
|
2934
|
+
return operation();
|
|
2935
|
+
});
|
|
2936
|
+
}
|
|
2534
2937
|
this.db.exec('BEGIN IMMEDIATE');
|
|
2535
2938
|
let result;
|
|
2536
2939
|
try {
|
|
@@ -2544,10 +2947,17 @@ export class SqliteRuntimeStore {
|
|
|
2544
2947
|
catch {
|
|
2545
2948
|
// Preserve the protocol failure that caused rollback.
|
|
2546
2949
|
}
|
|
2950
|
+
// Reducers may have loaded rows written earlier in this transaction as
|
|
2951
|
+
// their immutable base. Bump the local view even on rollback so the next
|
|
2952
|
+
// admission discards that phantom base before retrying.
|
|
2953
|
+
this.localCommittedWriteRevision += 1;
|
|
2547
2954
|
this.settleEventCommits(false);
|
|
2955
|
+
this.settleToolLedgerStates(false);
|
|
2548
2956
|
throw error;
|
|
2549
2957
|
}
|
|
2958
|
+
this.localCommittedWriteRevision += 1;
|
|
2550
2959
|
this.settleEventCommits(true);
|
|
2960
|
+
this.settleToolLedgerStates(true);
|
|
2551
2961
|
return result;
|
|
2552
2962
|
}
|
|
2553
2963
|
noteEventCommit(sessionId) {
|
|
@@ -2765,64 +3175,268 @@ export class SqliteRuntimeStore {
|
|
|
2765
3175
|
}
|
|
2766
3176
|
}
|
|
2767
3177
|
assertToolLedgerTransition(candidateEvents, expectedTransition) {
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
const
|
|
2773
|
-
|
|
3178
|
+
const canonicalEncodings = candidateEvents.map(encodeCanonicalRuntimeEvent);
|
|
3179
|
+
const canonicalEvents = canonicalEncodings.map(({ event }) => event);
|
|
3180
|
+
const entry = this.toolLedgerCacheEntry(canonicalEvents);
|
|
3181
|
+
const appendedEncodings = canonicalEncodings.filter(({ event }) => entry.reducer.event(event.id) === undefined);
|
|
3182
|
+
const validation = validateIncrementalToolLedgerTransition({
|
|
3183
|
+
reducer: entry.reducer,
|
|
3184
|
+
candidateEvents: canonicalEvents,
|
|
3185
|
+
expectedTransition,
|
|
3186
|
+
});
|
|
3187
|
+
if (!validation.ok) {
|
|
3188
|
+
if (validation.source === 'existing') {
|
|
3189
|
+
throw new ToolLedgerCorruptionError(validation.code, validation.eventId);
|
|
3190
|
+
}
|
|
3191
|
+
throw new ToolLedgerRejectionError(validation.code, validation.eventId);
|
|
3192
|
+
}
|
|
3193
|
+
if (validation.appendedEvents === 0)
|
|
3194
|
+
return;
|
|
3195
|
+
this.noteToolLedgerState(entry, validation.checkpoint, canonicalEvents.map((event) => event.invocationId), {
|
|
3196
|
+
eventCount: validation.appendedEvents,
|
|
3197
|
+
estimatedBytes: estimateToolLedgerCacheBytes(appendedEncodings.reduce((bytes, encoding) => bytes + Buffer.byteLength(encoding.json, 'utf8'), 0), validation.appendedEvents),
|
|
3198
|
+
});
|
|
3199
|
+
}
|
|
3200
|
+
toolLedgerCacheEntry(candidateEvents) {
|
|
3201
|
+
const currentVersion = this.currentToolLedgerCacheVersion();
|
|
3202
|
+
if (this.toolLedgerCacheVersion !== currentVersion)
|
|
3203
|
+
this.refreshToolLedgerCacheVersion();
|
|
3204
|
+
const directInvocationIds = new Set(candidateEvents.map((event) => event.invocationId));
|
|
3205
|
+
const directOperationIds = referencedToolOperationIds(candidateEvents);
|
|
3206
|
+
for (const invocationId of this.toolLedgerOperationInvocations(directOperationIds)) {
|
|
3207
|
+
directInvocationIds.add(invocationId);
|
|
3208
|
+
}
|
|
3209
|
+
for (const [key, entry] of this.toolLedgerCache) {
|
|
3210
|
+
if (entry.seedInvocationIds.size === directInvocationIds.size &&
|
|
3211
|
+
[...directInvocationIds].every((invocationId) => entry.seedInvocationIds.has(invocationId))) {
|
|
3212
|
+
this.toolLedgerCacheMetrics.hits += 1;
|
|
3213
|
+
this.touchToolLedgerCacheEntry(key, entry);
|
|
3214
|
+
return entry;
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
this.toolLedgerCacheMetrics.misses += 1;
|
|
3218
|
+
const invocationIds = new Set(directInvocationIds);
|
|
3219
|
+
const operationIds = new Set(directOperationIds);
|
|
3220
|
+
const resolvedOperationIds = new Set();
|
|
3221
|
+
const loadedInvocationIds = new Set();
|
|
3222
|
+
const scopedEvents = [];
|
|
3223
|
+
let storedPayloadBytes = 0;
|
|
2774
3224
|
const readInvocation = this.db.prepare(`
|
|
2775
|
-
SELECT event_id, session_id, invocation_id, run_id, turn_id, payload_json
|
|
3225
|
+
SELECT event_id, session_id, invocation_id, run_id, turn_id, event_seq, payload_json,
|
|
3226
|
+
length(CAST(payload_json AS BLOB)) AS stored_bytes
|
|
2776
3227
|
FROM runtime_events
|
|
2777
3228
|
WHERE invocation_id = ?
|
|
2778
3229
|
ORDER BY event_seq ASC, event_id ASC
|
|
2779
3230
|
`);
|
|
2780
|
-
|
|
2781
|
-
|
|
3231
|
+
while (true) {
|
|
3232
|
+
const pendingOperationIds = [...operationIds].filter((operationId) => !resolvedOperationIds.has(operationId));
|
|
3233
|
+
if (pendingOperationIds.length > 0) {
|
|
3234
|
+
for (const operationId of pendingOperationIds)
|
|
3235
|
+
resolvedOperationIds.add(operationId);
|
|
3236
|
+
for (const invocationId of this.toolLedgerOperationInvocations(pendingOperationIds)) {
|
|
3237
|
+
invocationIds.add(invocationId);
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
const pendingInvocationIds = [...invocationIds]
|
|
3241
|
+
.filter((invocationId) => !loadedInvocationIds.has(invocationId))
|
|
3242
|
+
.sort();
|
|
3243
|
+
if (pendingInvocationIds.length === 0 && pendingOperationIds.length === 0)
|
|
3244
|
+
break;
|
|
3245
|
+
for (const invocationId of pendingInvocationIds) {
|
|
3246
|
+
loadedInvocationIds.add(invocationId);
|
|
3247
|
+
const rows = readInvocation.all(invocationId);
|
|
3248
|
+
for (const row of rows) {
|
|
3249
|
+
storedPayloadBytes += requireRuntimeEventScanCount(row.stored_bytes);
|
|
3250
|
+
const event = decodeRuntimeEventStorageRow(row);
|
|
3251
|
+
scopedEvents.push({ event, eventSeq: row.event_seq });
|
|
3252
|
+
if (event.refs?.parentOperationId)
|
|
3253
|
+
operationIds.add(event.refs.parentOperationId);
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
2782
3256
|
}
|
|
2783
|
-
const
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
3257
|
+
const sortedSeedInvocationIds = [...directInvocationIds].sort();
|
|
3258
|
+
const sortedInvocationIds = [...invocationIds].sort();
|
|
3259
|
+
const key = JSON.stringify([sortedSeedInvocationIds, sortedInvocationIds]);
|
|
3260
|
+
const existing = this.toolLedgerCache.get(key);
|
|
3261
|
+
if (existing) {
|
|
3262
|
+
this.touchToolLedgerCacheEntry(key, existing);
|
|
3263
|
+
return existing;
|
|
3264
|
+
}
|
|
3265
|
+
scopedEvents.sort((left, right) => left.event.invocationId.localeCompare(right.event.invocationId) ||
|
|
3266
|
+
left.eventSeq - right.eventSeq ||
|
|
3267
|
+
left.event.id.localeCompare(right.event.id));
|
|
3268
|
+
const entry = {
|
|
3269
|
+
reducer: new ToolLedgerReducer(scopedEvents.map(({ event }) => event)),
|
|
3270
|
+
seedInvocationIds: new Set(sortedSeedInvocationIds),
|
|
3271
|
+
invocationIds: new Set(sortedInvocationIds),
|
|
3272
|
+
eventCount: scopedEvents.length,
|
|
3273
|
+
estimatedBytes: estimateToolLedgerCacheBytes(storedPayloadBytes, scopedEvents.length),
|
|
3274
|
+
};
|
|
3275
|
+
if (this.toolLedgerEntryExceedsAdmissionBudget(entry)) {
|
|
3276
|
+
this.toolLedgerCacheMetrics.transientEntries += 1;
|
|
3277
|
+
return entry;
|
|
3278
|
+
}
|
|
3279
|
+
this.retainToolLedgerCacheEntry(key, entry);
|
|
3280
|
+
this.trimToolLedgerCache();
|
|
3281
|
+
return entry;
|
|
3282
|
+
}
|
|
3283
|
+
toolLedgerOperationInvocations(operationIds) {
|
|
3284
|
+
const ids = [...new Set(operationIds)].sort();
|
|
3285
|
+
if (ids.length === 0)
|
|
3286
|
+
return new Set();
|
|
3287
|
+
const placeholders = ids.map(() => '?').join(', ');
|
|
3288
|
+
const rows = this.db
|
|
3289
|
+
.prepare(`
|
|
3290
|
+
SELECT DISTINCT invocation_id
|
|
3291
|
+
FROM runtime_events
|
|
3292
|
+
WHERE event_kind = 'tool_dispatch'
|
|
3293
|
+
AND CASE
|
|
3294
|
+
WHEN json_valid(payload_json)
|
|
3295
|
+
THEN json_extract(payload_json, '$.actions.toolDispatch.operationId')
|
|
3296
|
+
END IN (${placeholders})
|
|
3297
|
+
`)
|
|
3298
|
+
.all(...ids);
|
|
3299
|
+
return new Set(rows.map((row) => row.invocation_id));
|
|
3300
|
+
}
|
|
3301
|
+
noteToolLedgerState(entry, checkpoint, invocationIds, delta) {
|
|
3302
|
+
const reducer = entry.reducer;
|
|
3303
|
+
const existing = this.uncommittedToolLedgerStates.get(reducer);
|
|
3304
|
+
if (existing) {
|
|
3305
|
+
for (const invocationId of invocationIds)
|
|
3306
|
+
existing.changedInvocationIds.add(invocationId);
|
|
3307
|
+
this.adjustToolLedgerCacheEntry(entry, delta);
|
|
3308
|
+
return;
|
|
2790
3309
|
}
|
|
3310
|
+
if (this.uncommittedToolLedgerStates.size === 0 && this.databaseLease) {
|
|
3311
|
+
this.databaseLease.onTransactionSettled((committed) => this.settleToolLedgerStates(committed));
|
|
3312
|
+
}
|
|
3313
|
+
this.uncommittedToolLedgerStates.set(reducer, {
|
|
3314
|
+
checkpoint,
|
|
3315
|
+
changedInvocationIds: new Set(invocationIds),
|
|
3316
|
+
entry,
|
|
3317
|
+
estimatedBytesBefore: entry.estimatedBytes,
|
|
3318
|
+
eventCountBefore: entry.eventCount,
|
|
3319
|
+
});
|
|
3320
|
+
this.adjustToolLedgerCacheEntry(entry, delta);
|
|
2791
3321
|
}
|
|
2792
|
-
|
|
2793
|
-
const
|
|
2794
|
-
|
|
2795
|
-
|
|
3322
|
+
settleToolLedgerStates(committed) {
|
|
3323
|
+
const states = [...this.uncommittedToolLedgerStates.entries()];
|
|
3324
|
+
this.uncommittedToolLedgerStates.clear();
|
|
3325
|
+
if (states.length === 0)
|
|
3326
|
+
return;
|
|
3327
|
+
if (!committed) {
|
|
3328
|
+
for (const [reducer, state] of states.reverse()) {
|
|
3329
|
+
reducer.rollback(state.checkpoint);
|
|
3330
|
+
this.restoreToolLedgerCacheEntry(state);
|
|
3331
|
+
}
|
|
3332
|
+
this.trimToolLedgerCache();
|
|
3333
|
+
return;
|
|
2796
3334
|
}
|
|
2797
|
-
const
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
3335
|
+
const retained = new Set(states.map(([reducer]) => reducer));
|
|
3336
|
+
const changedInvocationIds = new Set(states.flatMap(([, state]) => [...state.changedInvocationIds]));
|
|
3337
|
+
for (const [key, entry] of this.toolLedgerCache) {
|
|
3338
|
+
if (retained.has(entry.reducer))
|
|
3339
|
+
continue;
|
|
3340
|
+
if ([...entry.invocationIds].some((id) => changedInvocationIds.has(id))) {
|
|
3341
|
+
this.deleteToolLedgerCacheEntry(key);
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
for (const [reducer, state] of states)
|
|
3345
|
+
reducer.commit(state.checkpoint);
|
|
3346
|
+
this.toolLedgerCacheVersion = this.currentToolLedgerCacheVersion();
|
|
3347
|
+
this.trimToolLedgerCache();
|
|
3348
|
+
}
|
|
3349
|
+
refreshToolLedgerCacheVersion() {
|
|
3350
|
+
if (this.uncommittedToolLedgerStates.size > 0) {
|
|
3351
|
+
throw new Error('Cannot invalidate tool ledger state during an open write transaction');
|
|
3352
|
+
}
|
|
3353
|
+
this.clearToolLedgerCache();
|
|
3354
|
+
this.toolLedgerCacheVersion = this.currentToolLedgerCacheVersion();
|
|
3355
|
+
}
|
|
3356
|
+
retainToolLedgerCacheEntry(key, entry) {
|
|
3357
|
+
entry.cacheKey = key;
|
|
3358
|
+
this.toolLedgerCache.set(key, entry);
|
|
3359
|
+
this.toolLedgerCacheEstimatedBytes += entry.estimatedBytes;
|
|
3360
|
+
this.toolLedgerCacheEventCount += entry.eventCount;
|
|
3361
|
+
}
|
|
3362
|
+
touchToolLedgerCacheEntry(key, entry) {
|
|
3363
|
+
this.toolLedgerCache.delete(key);
|
|
3364
|
+
this.toolLedgerCache.set(key, entry);
|
|
3365
|
+
}
|
|
3366
|
+
adjustToolLedgerCacheEntry(entry, delta) {
|
|
3367
|
+
entry.estimatedBytes += delta.estimatedBytes;
|
|
3368
|
+
entry.eventCount += delta.eventCount;
|
|
3369
|
+
if (entry.cacheKey && this.toolLedgerCache.get(entry.cacheKey) === entry) {
|
|
3370
|
+
this.toolLedgerCacheEstimatedBytes += delta.estimatedBytes;
|
|
3371
|
+
this.toolLedgerCacheEventCount += delta.eventCount;
|
|
2807
3372
|
}
|
|
2808
3373
|
}
|
|
2809
|
-
|
|
2810
|
-
const
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
SELECT event_id, session_id, invocation_id, run_id, turn_id, payload_json
|
|
2815
|
-
FROM runtime_events
|
|
2816
|
-
ORDER BY invocation_id ASC, event_seq ASC, event_id ASC
|
|
2817
|
-
`)
|
|
2818
|
-
.all();
|
|
2819
|
-
const scan = scanToolLedger(rows.map(decodeRuntimeEventStorageRow));
|
|
2820
|
-
this.toolLedgerHealth = { dataVersion, issue: scan.issues[0] };
|
|
3374
|
+
restoreToolLedgerCacheEntry(state) {
|
|
3375
|
+
const entry = state.entry;
|
|
3376
|
+
if (entry.cacheKey && this.toolLedgerCache.get(entry.cacheKey) === entry) {
|
|
3377
|
+
this.toolLedgerCacheEstimatedBytes += state.estimatedBytesBefore - entry.estimatedBytes;
|
|
3378
|
+
this.toolLedgerCacheEventCount += state.eventCountBefore - entry.eventCount;
|
|
2821
3379
|
}
|
|
2822
|
-
|
|
2823
|
-
|
|
3380
|
+
entry.estimatedBytes = state.estimatedBytesBefore;
|
|
3381
|
+
entry.eventCount = state.eventCountBefore;
|
|
3382
|
+
}
|
|
3383
|
+
deleteToolLedgerCacheEntry(key, reason) {
|
|
3384
|
+
const entry = this.toolLedgerCache.get(key);
|
|
3385
|
+
if (!entry)
|
|
3386
|
+
return;
|
|
3387
|
+
this.toolLedgerCache.delete(key);
|
|
3388
|
+
if (entry.cacheKey === key)
|
|
3389
|
+
entry.cacheKey = undefined;
|
|
3390
|
+
this.toolLedgerCacheEstimatedBytes -= entry.estimatedBytes;
|
|
3391
|
+
this.toolLedgerCacheEventCount -= entry.eventCount;
|
|
3392
|
+
if (reason === 'budget')
|
|
3393
|
+
this.toolLedgerCacheMetrics.budgetEvictions += 1;
|
|
3394
|
+
if (reason === 'terminal')
|
|
3395
|
+
this.toolLedgerCacheMetrics.terminalEvictions += 1;
|
|
3396
|
+
}
|
|
3397
|
+
clearToolLedgerCache() {
|
|
3398
|
+
for (const entry of this.toolLedgerCache.values())
|
|
3399
|
+
entry.cacheKey = undefined;
|
|
3400
|
+
this.toolLedgerCache.clear();
|
|
3401
|
+
this.toolLedgerCacheEstimatedBytes = 0;
|
|
3402
|
+
this.toolLedgerCacheEventCount = 0;
|
|
3403
|
+
}
|
|
3404
|
+
invalidateToolLedgerCacheForInvocation(invocationId) {
|
|
3405
|
+
for (const [key, entry] of this.toolLedgerCache) {
|
|
3406
|
+
if (entry.invocationIds.has(invocationId)) {
|
|
3407
|
+
this.deleteToolLedgerCacheEntry(key, 'terminal');
|
|
3408
|
+
}
|
|
2824
3409
|
}
|
|
2825
3410
|
}
|
|
3411
|
+
toolLedgerEntryExceedsAdmissionBudget(entry) {
|
|
3412
|
+
return entry.estimatedBytes > this.toolLedgerCacheBudget.maxSingleEntryEstimatedBytes;
|
|
3413
|
+
}
|
|
3414
|
+
toolLedgerCacheExceedsBudget() {
|
|
3415
|
+
return (this.toolLedgerCache.size > this.toolLedgerCacheBudget.maxEntries ||
|
|
3416
|
+
this.toolLedgerCacheEstimatedBytes > this.toolLedgerCacheBudget.maxEstimatedBytes);
|
|
3417
|
+
}
|
|
3418
|
+
trimToolLedgerCache() {
|
|
3419
|
+
for (;;) {
|
|
3420
|
+
let victim;
|
|
3421
|
+
for (const [key, entry] of this.toolLedgerCache) {
|
|
3422
|
+
if (this.uncommittedToolLedgerStates.has(entry.reducer))
|
|
3423
|
+
continue;
|
|
3424
|
+
if (this.toolLedgerEntryExceedsAdmissionBudget(entry)) {
|
|
3425
|
+
victim = key;
|
|
3426
|
+
break;
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
if (!victim && this.toolLedgerCacheExceedsBudget()) {
|
|
3430
|
+
victim = [...this.toolLedgerCache].find(([, entry]) => !this.uncommittedToolLedgerStates.has(entry.reducer))?.[0];
|
|
3431
|
+
}
|
|
3432
|
+
if (!victim)
|
|
3433
|
+
return;
|
|
3434
|
+
this.deleteToolLedgerCacheEntry(victim, 'budget');
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
currentToolLedgerCacheVersion() {
|
|
3438
|
+
return `${this.runtimeDataVersion()}:${this.databaseLease?.writeViewRevision() ?? this.localCommittedWriteRevision}`;
|
|
3439
|
+
}
|
|
2826
3440
|
runtimeDataVersion() {
|
|
2827
3441
|
const row = this.db.prepare('PRAGMA data_version').get();
|
|
2828
3442
|
return row.data_version;
|
|
@@ -3005,21 +3619,28 @@ export class SqliteRuntimeStore {
|
|
|
3005
3619
|
const messageId = immutableSteeringMessageId(event);
|
|
3006
3620
|
if (!messageId)
|
|
3007
3621
|
return;
|
|
3008
|
-
const matches = this.
|
|
3622
|
+
const matches = this.readImmutableSteeringMessageEvents(event.sessionId, messageId);
|
|
3009
3623
|
if (matches.some((candidate) => !isDeepStrictEqual(candidate, event))) {
|
|
3010
3624
|
throw new Error(`Immutable steering message identity conflict: ${messageId}`);
|
|
3011
3625
|
}
|
|
3012
3626
|
}
|
|
3013
|
-
|
|
3627
|
+
readImmutableSteeringMessageEvents(sessionId, messageId) {
|
|
3014
3628
|
const rows = this.db
|
|
3015
3629
|
.prepare(`
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3630
|
+
SELECT event_id, session_id, invocation_id, run_id, turn_id, payload_json
|
|
3631
|
+
FROM runtime_events
|
|
3632
|
+
WHERE session_id = ?
|
|
3633
|
+
AND ${IMMUTABLE_STEERING_SQL_PREDICATE}
|
|
3634
|
+
AND CASE
|
|
3635
|
+
WHEN json_valid(payload_json)
|
|
3636
|
+
THEN json_extract(payload_json, '$.refs.providerEventId')
|
|
3637
|
+
END = ?
|
|
3638
|
+
ORDER BY committed_at ASC, event_id ASC
|
|
3639
|
+
`)
|
|
3640
|
+
.all(sessionId, messageId);
|
|
3641
|
+
return rows
|
|
3642
|
+
.map(decodeRuntimeEventStorageRow)
|
|
3643
|
+
.filter((event) => immutableSteeringMessageId(event) === messageId);
|
|
3023
3644
|
}
|
|
3024
3645
|
insertRuntimeEvent(event, committedAt, allowExactDuplicate, authorizedPendingContinuationClaimId) {
|
|
3025
3646
|
const encoding = encodeCanonicalRuntimeEvent(event);
|
|
@@ -3035,6 +3656,9 @@ export class SqliteRuntimeStore {
|
|
|
3035
3656
|
if (!allowExactDuplicate) {
|
|
3036
3657
|
throw new Error(`RuntimeEvent ${canonicalEvent.id} already exists outside this tool transaction`);
|
|
3037
3658
|
}
|
|
3659
|
+
if (isTerminalRuntimeEvent(canonicalEvent)) {
|
|
3660
|
+
this.invalidateToolLedgerCacheForInvocation(canonicalEvent.invocationId);
|
|
3661
|
+
}
|
|
3038
3662
|
return this.runtimeEventSeq(canonicalEvent.id);
|
|
3039
3663
|
}
|
|
3040
3664
|
this.assertContinuationAuthorityAllowsEvent(canonicalEvent, authorizedPendingContinuationClaimId);
|
|
@@ -3068,6 +3692,9 @@ export class SqliteRuntimeStore {
|
|
|
3068
3692
|
recordTranscriptTurnExtent(this.db, { ...canonicalEvent, kind: runtimeEventKind(canonicalEvent) }, ordinal);
|
|
3069
3693
|
this.noteEventCommit(canonicalEvent.sessionId);
|
|
3070
3694
|
this.deleteCompletedPartialSnapshot(canonicalEvent);
|
|
3695
|
+
if (isTerminalRuntimeEvent(canonicalEvent)) {
|
|
3696
|
+
this.invalidateToolLedgerCacheForInvocation(canonicalEvent.invocationId);
|
|
3697
|
+
}
|
|
3071
3698
|
return next;
|
|
3072
3699
|
}
|
|
3073
3700
|
deleteCompletedPartialSnapshot(event) {
|
|
@@ -3348,6 +3975,19 @@ function assertRuntimeStorageSafeId(value, message) {
|
|
|
3348
3975
|
if (!isRuntimeStorageSafeId(value))
|
|
3349
3976
|
throw new Error(message);
|
|
3350
3977
|
}
|
|
3978
|
+
function distinctSafeRuntimeIds(values, message) {
|
|
3979
|
+
const result = new Set();
|
|
3980
|
+
for (const value of values) {
|
|
3981
|
+
assertRuntimeStorageSafeId(value, message);
|
|
3982
|
+
result.add(value);
|
|
3983
|
+
}
|
|
3984
|
+
return [...result];
|
|
3985
|
+
}
|
|
3986
|
+
function* batches(values, size) {
|
|
3987
|
+
for (let offset = 0; offset < values.length; offset += size) {
|
|
3988
|
+
yield values.slice(offset, offset + size);
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3351
3991
|
function assertInvocationSearchLimit(limit) {
|
|
3352
3992
|
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 256) {
|
|
3353
3993
|
throw new RangeError('Runtime invocation search limit must be an integer between 1 and 256');
|