maka-agent 0.2.0-dev.64.20260929 → 0.2.0-dev.67.20261002

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/LICENSE +1 -1
  2. package/THIRD_PARTY_NOTICES.txt +3 -3
  3. package/dist/pi-tui-runner.js +26 -29
  4. package/dist/tui-copy-catalog.js +9 -9
  5. package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
  6. package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
  7. package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
  8. package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
  9. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  10. package/node_modules/@maka/core/dist/settings.js +2 -2
  11. package/node_modules/@maka/core/dist/tool-ledger-scanner.js +53 -150
  12. package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +8 -1
  13. package/node_modules/@maka/runtime/dist/conversation-copy.js +6 -0
  14. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +17 -0
  15. package/node_modules/@maka/runtime/dist/session-manager.js +25 -40
  16. package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +79 -13
  17. package/node_modules/@maka/runtime/dist/stream-graph-projection.js +64 -0
  18. package/node_modules/@maka/runtime/dist/stream-graph-read-model.js +138 -20
  19. package/node_modules/@maka/runtime/dist/tool-availability.js +10 -3
  20. package/node_modules/@maka/runtime-host/dist/candidate-entry.js +15 -0
  21. package/node_modules/@maka/runtime-host/dist/candidate-startup-failure.js +5 -0
  22. package/node_modules/@maka/runtime-host/dist/client/session-catalog-summary.js +1 -0
  23. package/node_modules/@maka/runtime-host/dist/client/startup-error.js +5 -0
  24. package/node_modules/@maka/runtime-host/dist/protocol/agent-graph.js +46 -1
  25. package/node_modules/@maka/runtime-host/dist/protocol/index.js +14 -1
  26. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +5 -1
  27. package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +12 -0
  28. package/node_modules/@maka/runtime-host/dist/protocol/session-retirement.js +46 -10
  29. package/node_modules/@maka/runtime-host/dist/protocol/storage-usage.js +155 -0
  30. package/node_modules/@maka/runtime-host/dist/server/agent-graph-coordinator.js +23 -0
  31. package/node_modules/@maka/runtime-host/dist/server/canonical-turn-snapshot.js +5 -0
  32. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +27 -6
  33. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +2 -0
  34. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +6 -0
  35. package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +112 -36
  36. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +27 -1
  37. package/node_modules/@maka/runtime-host/dist/server/storage-usage-coordinator.js +67 -0
  38. package/node_modules/@maka/runtime-host/dist/server/workhub-inspection-tool.js +208 -0
  39. package/node_modules/@maka/storage/dist/git-worktree-child-executor.js +3 -1
  40. package/node_modules/@maka/storage/dist/operational-state-store.js +0 -13
  41. package/node_modules/@maka/storage/dist/session-store.js +9 -6
  42. package/node_modules/@maka/storage/dist/sqlite-file-set.js +22 -0
  43. package/node_modules/@maka/storage/dist/sqlite-long-term-memory-store.js +1 -3
  44. package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +10 -260
  45. package/node_modules/@maka/storage/dist/sqlite-session-catalog-query.js +1 -0
  46. package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +18 -6
  47. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +28 -6
  48. package/node_modules/@maka/storage/dist/storage-footprint.js +239 -0
  49. package/node_modules/@maka/storage/dist/storage-writer-composition.js +10 -1
  50. package/node_modules/axios/CHANGELOG.md +62 -0
  51. package/node_modules/axios/README.md +297 -5
  52. package/node_modules/axios/dist/axios.js +728 -170
  53. package/node_modules/axios/dist/axios.min.js +3 -3
  54. package/node_modules/axios/dist/browser/axios.cjs +871 -188
  55. package/node_modules/axios/dist/esm/axios.js +871 -188
  56. package/node_modules/axios/dist/esm/axios.min.js +2 -2
  57. package/node_modules/axios/dist/node/axios.cjs +1037 -225
  58. package/node_modules/axios/lib/adapters/fetch.js +64 -11
  59. package/node_modules/axios/lib/adapters/http.js +140 -67
  60. package/node_modules/axios/lib/adapters/xhr.js +52 -3
  61. package/node_modules/axios/lib/core/Axios.js +43 -20
  62. package/node_modules/axios/lib/core/AxiosError.js +33 -2
  63. package/node_modules/axios/lib/core/AxiosHeaders.js +124 -1
  64. package/node_modules/axios/lib/core/InterceptorManager.js +107 -8
  65. package/node_modules/axios/lib/core/buildFullPath.js +41 -15
  66. package/node_modules/axios/lib/core/dispatchRequest.js +8 -2
  67. package/node_modules/axios/lib/core/mergeConfig.js +19 -4
  68. package/node_modules/axios/lib/core/methodList.js +17 -0
  69. package/node_modules/axios/lib/core/setFormDataHeaders.js +27 -0
  70. package/node_modules/axios/lib/defaults/index.js +2 -1
  71. package/node_modules/axios/lib/env/data.js +1 -1
  72. package/node_modules/axios/lib/helpers/Http2Sessions.js +2 -0
  73. package/node_modules/axios/lib/helpers/HttpStatusCode.js +12 -1
  74. package/node_modules/axios/lib/helpers/combineURLs.js +11 -3
  75. package/node_modules/axios/lib/helpers/composeSignals.js +12 -1
  76. package/node_modules/axios/lib/helpers/estimateDataURLDecodedBytes.js +115 -54
  77. package/node_modules/axios/lib/helpers/formDataToJSON.js +11 -5
  78. package/node_modules/axios/lib/helpers/fromDataURI.js +5 -4
  79. package/node_modules/axios/lib/helpers/normalizeURLForProtocolCheck.js +24 -0
  80. package/node_modules/axios/lib/helpers/parseHeaders.js +5 -3
  81. package/node_modules/axios/lib/helpers/progressEventReducer.js +4 -4
  82. package/node_modules/axios/lib/helpers/resolveConfig.js +5 -17
  83. package/node_modules/axios/lib/helpers/shouldBypassProxy.js +300 -5
  84. package/node_modules/axios/lib/helpers/throttle.js +3 -2
  85. package/node_modules/axios/lib/helpers/toFormData.js +17 -23
  86. package/node_modules/axios/lib/platform/node/classes/Buffer.js +11 -0
  87. package/node_modules/axios/lib/utils.js +151 -18
  88. package/node_modules/axios/package.json +21 -19
  89. package/node_modules/brace-expansion/dist/commonjs/index.js +69 -25
  90. package/node_modules/brace-expansion/dist/esm/index.js +68 -24
  91. package/node_modules/brace-expansion/package.json +2 -1
  92. package/node_modules/fast-uri/index.js +10 -6
  93. package/node_modules/fast-uri/package.json +1 -1
  94. package/package.json +1 -1
@@ -311,7 +311,7 @@ class SqliteSessionStore {
311
311
  return (await this.metadata.list(filter, 'ordinary'))
312
312
  .filter((record) => record.header.transcriptLedgerVersion !== 0)
313
313
  .filter((record) => record.header.conversationCopy?.state !== 'preparing')
314
- .map((record) => toCatalogSummary(record.header, record.lastMessagePreview));
314
+ .map(toCatalogRecordSummary);
315
315
  }
316
316
  async listCatalogPage(filter, cursor, limit, expectedRevision) {
317
317
  await this.ensureCatalogProjectionReadable();
@@ -330,7 +330,7 @@ class SqliteSessionStore {
330
330
  records: page.records.map((record) => ({
331
331
  ...projectHeaderSnapshot(record),
332
332
  activityAt: record.activityAt,
333
- summary: toCatalogSummary(record.header, record.lastMessagePreview),
333
+ summary: toCatalogRecordSummary(record),
334
334
  })),
335
335
  hasMore: page.hasMore,
336
336
  };
@@ -360,7 +360,7 @@ class SqliteSessionStore {
360
360
  return {
361
361
  ...projectHeaderSnapshot(record),
362
362
  activityAt: record.activityAt,
363
- summary: toCatalogSummary(record.header, record.lastMessagePreview),
363
+ summary: toCatalogRecordSummary(record),
364
364
  };
365
365
  }
366
366
  async readMessagesSnapshot(sessionId) {
@@ -597,9 +597,12 @@ function projectStableSessionCreateProbe(probe) {
597
597
  ? { kind: 'existing', record: projectHeaderSnapshot(probe.record) }
598
598
  : probe;
599
599
  }
600
- function toCatalogSummary(header, lastMessagePreview) {
600
+ function toCatalogRecordSummary(record) {
601
601
  return {
602
- ...toSummary(header),
603
- ...(lastMessagePreview === undefined ? {} : { lastMessagePreview }),
602
+ ...toSummary(record.header),
603
+ ...(record.lastMessagePreview === undefined
604
+ ? {}
605
+ : { lastMessagePreview: record.lastMessagePreview }),
606
+ ...(record.archivedAt === undefined ? {} : { archivedAt: record.archivedAt }),
604
607
  };
605
608
  }
@@ -0,0 +1,22 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ /** The files SQLite may keep beside a database, in any journal mode. */
20
+ export function sqliteDatabaseSidecars(path) {
21
+ return [`${path}-wal`, `${path}-shm`, `${path}-journal`];
22
+ }
@@ -20,6 +20,7 @@ import { createHash, randomUUID } from 'node:crypto';
20
20
  import { closeSync, constants as fsConstants, fchmodSync, fstatSync, lstatSync, openSync, } from 'node:fs';
21
21
  import { createRequire } from 'node:module';
22
22
  import { MemoryItemStoreConflictError, isMemoryItemKind, isMemoryItemOrigin, isMemoryKeyOrigin, isMemoryKeyType, isMemoryLifecycleState, isMemoryScopeType, isMemoryStatementType, isMemoryTemporalType, normalizeLongTermMemoryContent, validateMemoryTemporalBounds, } from '@maka/core/long-term-memory';
23
+ import { sqliteDatabaseSidecars as databaseSidecars } from './sqlite-file-set.js';
23
24
  import { assertSupportedSqliteLongTermMemorySchemaVersion, configureSqliteLongTermMemoryDatabase, migrateSqliteLongTermMemoryDatabase, readSqliteLongTermMemorySchemaVersion, } from './sqlite-long-term-memory-schema.js';
24
25
  const MAX_MUTATIONS_PER_OPERATION = 32;
25
26
  const MAX_KEYS_PER_ITEM = 32;
@@ -1591,9 +1592,6 @@ function secureFile(path, create, allowUnlinked) {
1591
1592
  closeSync(descriptor);
1592
1593
  }
1593
1594
  }
1594
- function databaseSidecars(path) {
1595
- return [`${path}-wal`, `${path}-shm`, `${path}-journal`];
1596
- }
1597
1595
  function isNodeError(error) {
1598
1596
  return typeof error === 'object' && error !== null && 'code' in error;
1599
1597
  }
@@ -29,7 +29,7 @@ import { stableJsonStringify } from '@maka/core/tool-args-identity';
29
29
  import { mergeRuntimePartialSnapshots, groupRuntimePartialSnapshots, compareRuntimePartialSnapshots, partialRuntimeStream, completedPartialRuntimeStreamKey, } from './runtime-partial-values.js';
30
30
  import { assertPreparedInput, assertOutcomeInput, assertPreparedIdentity, assertOutcomeIdentity, } from './tool-commit-validation.js';
31
31
  import { encodeCanonicalRuntimeEvent } from '@maka/core/canonical-runtime-event';
32
- import { scanToolLedger, referencedToolOperationIds, ToolLedgerReducer, ToolLedgerCorruptionError, ToolLedgerRejectionError, validateGenericToolLedgerAppend, validateIncrementalToolLedgerTransition, } from '@maka/core/tool-ledger-scanner';
32
+ import { scanToolLedger, referencedToolOperationIds, ToolLedgerCorruptionError, ToolLedgerRejectionError, validateGenericToolLedgerAppend, validateToolLedgerTransition, } from '@maka/core/tool-ledger-scanner';
33
33
  import { buildImmutableRuntimePrefix, buildImmutableRuntimePrefixProof, continuationStartEventMatchesClaim, decodeContinuationClaim, } from '@maka/core/runtime-boundary';
34
34
  import { assertToolRecoveryEventBundle, interpretScannedToolRecovery, } from '@maka/core/tool-recovery-bundle';
35
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';
@@ -43,12 +43,6 @@ export { SQLITE_RUNTIME_SCHEMA_VERSION } from './sqlite-runtime-schema.js';
43
43
  const RUNTIME_EVENT_SCAN_BATCH_SIZE = 128;
44
44
  const RUNTIME_PARTIAL_SEGMENT_TARGET_BYTES = 64 * 1024;
45
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
46
  const IMMUTABLE_STEERING_SQL_PREDICATE = `
53
47
  event_kind = 'text'
54
48
  AND CASE
@@ -80,19 +74,6 @@ function requireRuntimeEventScanCount(value) {
80
74
  }
81
75
  return value;
82
76
  }
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
- }
96
77
  const require = createRequire(import.meta.url);
97
78
  function loadDatabaseSync() {
98
79
  return require('node:sqlite').DatabaseSync;
@@ -114,26 +95,11 @@ export class SqliteRuntimeStore {
114
95
  workspaceVersionAuthorityCapability = WORKSPACE_VERSION_AUTHORITY_CAPABILITY_V1;
115
96
  db;
116
97
  databaseLease;
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;
131
98
  closed = false;
132
99
  uncommittedEventSessions = new Set();
133
100
  eventCommitListeners = new Set();
134
101
  constructor(path, options = {}) {
135
102
  this.options = options;
136
- this.toolLedgerCacheBudget = normalizeToolLedgerCacheBudget(options.toolLedgerCacheBudget);
137
103
  if (options.readOnly && options.databaseLease) {
138
104
  throw new Error('Operational state database leases cannot be opened read-only');
139
105
  }
@@ -196,8 +162,6 @@ export class SqliteRuntimeStore {
196
162
  if (this.closed)
197
163
  return;
198
164
  this.closed = true;
199
- this.clearToolLedgerCache();
200
- this.uncommittedToolLedgerStates.clear();
201
165
  if (this.databaseLease)
202
166
  this.databaseLease.close();
203
167
  else
@@ -241,7 +205,6 @@ export class SqliteRuntimeStore {
241
205
  existing.at(-1)?.id !== canonicalEvent.id) {
242
206
  throw new Error('Terminal RuntimeEvent must be the immutable ledger tail');
243
207
  }
244
- this.invalidateToolLedgerCacheForInvocation(canonicalEvent.invocationId);
245
208
  return;
246
209
  }
247
210
  const existingTerminal = existing.find(isTerminalRuntimeEvent);
@@ -3086,14 +3049,7 @@ export class SqliteRuntimeStore {
3086
3049
  }
3087
3050
  transaction(operation) {
3088
3051
  if (this.databaseLease) {
3089
- const nested = this.db.isTransaction;
3090
- return this.databaseLease.transaction('write', () => {
3091
- // A sibling lease may have changed the shared transaction view since
3092
- // this store last used its reconstructible cache.
3093
- if (nested)
3094
- this.clearToolLedgerCache();
3095
- return operation();
3096
- });
3052
+ return this.databaseLease.transaction('write', operation);
3097
3053
  }
3098
3054
  this.db.exec('BEGIN IMMEDIATE');
3099
3055
  let result;
@@ -3108,17 +3064,10 @@ export class SqliteRuntimeStore {
3108
3064
  catch {
3109
3065
  // Preserve the protocol failure that caused rollback.
3110
3066
  }
3111
- // Reducers may have loaded rows written earlier in this transaction as
3112
- // their immutable base. Bump the local view even on rollback so the next
3113
- // admission discards that phantom base before retrying.
3114
- this.localCommittedWriteRevision += 1;
3115
3067
  this.settleEventCommits(false);
3116
- this.settleToolLedgerStates(false);
3117
3068
  throw error;
3118
3069
  }
3119
- this.localCommittedWriteRevision += 1;
3120
3070
  this.settleEventCommits(true);
3121
- this.settleToolLedgerStates(true);
3122
3071
  return result;
3123
3072
  }
3124
3073
  noteEventCommit(sessionId) {
@@ -3336,12 +3285,9 @@ export class SqliteRuntimeStore {
3336
3285
  }
3337
3286
  }
3338
3287
  assertToolLedgerTransition(candidateEvents, expectedTransition) {
3339
- const canonicalEncodings = candidateEvents.map(encodeCanonicalRuntimeEvent);
3340
- const canonicalEvents = canonicalEncodings.map(({ event }) => event);
3341
- const entry = this.toolLedgerCacheEntry(canonicalEvents);
3342
- const appendedEncodings = canonicalEncodings.filter(({ event }) => entry.reducer.event(event.id) === undefined);
3343
- const validation = validateIncrementalToolLedgerTransition({
3344
- reducer: entry.reducer,
3288
+ const canonicalEvents = candidateEvents.map((event) => encodeCanonicalRuntimeEvent(event).event);
3289
+ const validation = validateToolLedgerTransition({
3290
+ existingEvents: this.readToolLedgerDependencies(canonicalEvents),
3345
3291
  candidateEvents: canonicalEvents,
3346
3292
  expectedTransition,
3347
3293
  });
@@ -3351,40 +3297,15 @@ export class SqliteRuntimeStore {
3351
3297
  }
3352
3298
  throw new ToolLedgerRejectionError(validation.code, validation.eventId);
3353
3299
  }
3354
- if (validation.appendedEvents === 0)
3355
- return;
3356
- this.noteToolLedgerState(entry, validation.checkpoint, canonicalEvents.map((event) => event.invocationId), {
3357
- eventCount: validation.appendedEvents,
3358
- estimatedBytes: estimateToolLedgerCacheBytes(appendedEncodings.reduce((bytes, encoding) => bytes + Buffer.byteLength(encoding.json, 'utf8'), 0), validation.appendedEvents),
3359
- });
3360
3300
  }
3361
- toolLedgerCacheEntry(candidateEvents) {
3362
- const currentVersion = this.currentToolLedgerCacheVersion();
3363
- if (this.toolLedgerCacheVersion !== currentVersion)
3364
- this.refreshToolLedgerCacheVersion();
3365
- const directInvocationIds = new Set(candidateEvents.map((event) => event.invocationId));
3366
- const directOperationIds = referencedToolOperationIds(candidateEvents);
3367
- for (const invocationId of this.toolLedgerOperationInvocations(directOperationIds)) {
3368
- directInvocationIds.add(invocationId);
3369
- }
3370
- for (const [key, entry] of this.toolLedgerCache) {
3371
- if (entry.seedInvocationIds.size === directInvocationIds.size &&
3372
- [...directInvocationIds].every((invocationId) => entry.seedInvocationIds.has(invocationId))) {
3373
- this.toolLedgerCacheMetrics.hits += 1;
3374
- this.touchToolLedgerCacheEntry(key, entry);
3375
- return entry;
3376
- }
3377
- }
3378
- this.toolLedgerCacheMetrics.misses += 1;
3379
- const invocationIds = new Set(directInvocationIds);
3380
- const operationIds = new Set(directOperationIds);
3301
+ readToolLedgerDependencies(candidateEvents) {
3302
+ const invocationIds = new Set(candidateEvents.map((event) => event.invocationId));
3303
+ const operationIds = referencedToolOperationIds(candidateEvents);
3381
3304
  const resolvedOperationIds = new Set();
3382
3305
  const loadedInvocationIds = new Set();
3383
3306
  const scopedEvents = [];
3384
- let storedPayloadBytes = 0;
3385
3307
  const readInvocation = this.db.prepare(`
3386
- SELECT event_id, session_id, invocation_id, run_id, turn_id, event_seq, payload_json,
3387
- length(CAST(payload_json AS BLOB)) AS stored_bytes
3308
+ SELECT event_id, session_id, invocation_id, run_id, turn_id, event_seq, payload_json
3388
3309
  FROM runtime_events
3389
3310
  WHERE invocation_id = ?
3390
3311
  ORDER BY event_seq ASC, event_id ASC
@@ -3407,7 +3328,6 @@ export class SqliteRuntimeStore {
3407
3328
  loadedInvocationIds.add(invocationId);
3408
3329
  const rows = readInvocation.all(invocationId);
3409
3330
  for (const row of rows) {
3410
- storedPayloadBytes += requireRuntimeEventScanCount(row.stored_bytes);
3411
3331
  const event = decodeRuntimeEventStorageRow(row);
3412
3332
  scopedEvents.push({ event, eventSeq: row.event_seq });
3413
3333
  if (event.refs?.parentOperationId)
@@ -3415,31 +3335,10 @@ export class SqliteRuntimeStore {
3415
3335
  }
3416
3336
  }
3417
3337
  }
3418
- const sortedSeedInvocationIds = [...directInvocationIds].sort();
3419
- const sortedInvocationIds = [...invocationIds].sort();
3420
- const key = JSON.stringify([sortedSeedInvocationIds, sortedInvocationIds]);
3421
- const existing = this.toolLedgerCache.get(key);
3422
- if (existing) {
3423
- this.touchToolLedgerCacheEntry(key, existing);
3424
- return existing;
3425
- }
3426
3338
  scopedEvents.sort((left, right) => left.event.invocationId.localeCompare(right.event.invocationId) ||
3427
3339
  left.eventSeq - right.eventSeq ||
3428
3340
  left.event.id.localeCompare(right.event.id));
3429
- const entry = {
3430
- reducer: new ToolLedgerReducer(scopedEvents.map(({ event }) => event)),
3431
- seedInvocationIds: new Set(sortedSeedInvocationIds),
3432
- invocationIds: new Set(sortedInvocationIds),
3433
- eventCount: scopedEvents.length,
3434
- estimatedBytes: estimateToolLedgerCacheBytes(storedPayloadBytes, scopedEvents.length),
3435
- };
3436
- if (this.toolLedgerEntryExceedsAdmissionBudget(entry)) {
3437
- this.toolLedgerCacheMetrics.transientEntries += 1;
3438
- return entry;
3439
- }
3440
- this.retainToolLedgerCacheEntry(key, entry);
3441
- this.trimToolLedgerCache();
3442
- return entry;
3341
+ return scopedEvents.map(({ event }) => event);
3443
3342
  }
3444
3343
  toolLedgerOperationInvocations(operationIds) {
3445
3344
  const ids = [...new Set(operationIds)].sort();
@@ -3459,149 +3358,6 @@ export class SqliteRuntimeStore {
3459
3358
  .all(...ids);
3460
3359
  return new Set(rows.map((row) => row.invocation_id));
3461
3360
  }
3462
- noteToolLedgerState(entry, checkpoint, invocationIds, delta) {
3463
- const reducer = entry.reducer;
3464
- const existing = this.uncommittedToolLedgerStates.get(reducer);
3465
- if (existing) {
3466
- for (const invocationId of invocationIds)
3467
- existing.changedInvocationIds.add(invocationId);
3468
- this.adjustToolLedgerCacheEntry(entry, delta);
3469
- return;
3470
- }
3471
- if (this.uncommittedToolLedgerStates.size === 0 && this.databaseLease) {
3472
- this.databaseLease.onTransactionSettled((committed) => this.settleToolLedgerStates(committed));
3473
- }
3474
- this.uncommittedToolLedgerStates.set(reducer, {
3475
- checkpoint,
3476
- changedInvocationIds: new Set(invocationIds),
3477
- entry,
3478
- estimatedBytesBefore: entry.estimatedBytes,
3479
- eventCountBefore: entry.eventCount,
3480
- });
3481
- this.adjustToolLedgerCacheEntry(entry, delta);
3482
- }
3483
- settleToolLedgerStates(committed) {
3484
- const states = [...this.uncommittedToolLedgerStates.entries()];
3485
- this.uncommittedToolLedgerStates.clear();
3486
- if (states.length === 0)
3487
- return;
3488
- if (!committed) {
3489
- for (const [reducer, state] of states.reverse()) {
3490
- reducer.rollback(state.checkpoint);
3491
- this.restoreToolLedgerCacheEntry(state);
3492
- }
3493
- this.trimToolLedgerCache();
3494
- return;
3495
- }
3496
- const retained = new Set(states.map(([reducer]) => reducer));
3497
- const changedInvocationIds = new Set(states.flatMap(([, state]) => [...state.changedInvocationIds]));
3498
- for (const [key, entry] of this.toolLedgerCache) {
3499
- if (retained.has(entry.reducer))
3500
- continue;
3501
- if ([...entry.invocationIds].some((id) => changedInvocationIds.has(id))) {
3502
- this.deleteToolLedgerCacheEntry(key);
3503
- }
3504
- }
3505
- for (const [reducer, state] of states)
3506
- reducer.commit(state.checkpoint);
3507
- this.toolLedgerCacheVersion = this.currentToolLedgerCacheVersion();
3508
- this.trimToolLedgerCache();
3509
- }
3510
- refreshToolLedgerCacheVersion() {
3511
- if (this.uncommittedToolLedgerStates.size > 0) {
3512
- throw new Error('Cannot invalidate tool ledger state during an open write transaction');
3513
- }
3514
- this.clearToolLedgerCache();
3515
- this.toolLedgerCacheVersion = this.currentToolLedgerCacheVersion();
3516
- }
3517
- retainToolLedgerCacheEntry(key, entry) {
3518
- entry.cacheKey = key;
3519
- this.toolLedgerCache.set(key, entry);
3520
- this.toolLedgerCacheEstimatedBytes += entry.estimatedBytes;
3521
- this.toolLedgerCacheEventCount += entry.eventCount;
3522
- }
3523
- touchToolLedgerCacheEntry(key, entry) {
3524
- this.toolLedgerCache.delete(key);
3525
- this.toolLedgerCache.set(key, entry);
3526
- }
3527
- adjustToolLedgerCacheEntry(entry, delta) {
3528
- entry.estimatedBytes += delta.estimatedBytes;
3529
- entry.eventCount += delta.eventCount;
3530
- if (entry.cacheKey && this.toolLedgerCache.get(entry.cacheKey) === entry) {
3531
- this.toolLedgerCacheEstimatedBytes += delta.estimatedBytes;
3532
- this.toolLedgerCacheEventCount += delta.eventCount;
3533
- }
3534
- }
3535
- restoreToolLedgerCacheEntry(state) {
3536
- const entry = state.entry;
3537
- if (entry.cacheKey && this.toolLedgerCache.get(entry.cacheKey) === entry) {
3538
- this.toolLedgerCacheEstimatedBytes += state.estimatedBytesBefore - entry.estimatedBytes;
3539
- this.toolLedgerCacheEventCount += state.eventCountBefore - entry.eventCount;
3540
- }
3541
- entry.estimatedBytes = state.estimatedBytesBefore;
3542
- entry.eventCount = state.eventCountBefore;
3543
- }
3544
- deleteToolLedgerCacheEntry(key, reason) {
3545
- const entry = this.toolLedgerCache.get(key);
3546
- if (!entry)
3547
- return;
3548
- this.toolLedgerCache.delete(key);
3549
- if (entry.cacheKey === key)
3550
- entry.cacheKey = undefined;
3551
- this.toolLedgerCacheEstimatedBytes -= entry.estimatedBytes;
3552
- this.toolLedgerCacheEventCount -= entry.eventCount;
3553
- if (reason === 'budget')
3554
- this.toolLedgerCacheMetrics.budgetEvictions += 1;
3555
- if (reason === 'terminal')
3556
- this.toolLedgerCacheMetrics.terminalEvictions += 1;
3557
- }
3558
- clearToolLedgerCache() {
3559
- for (const entry of this.toolLedgerCache.values())
3560
- entry.cacheKey = undefined;
3561
- this.toolLedgerCache.clear();
3562
- this.toolLedgerCacheEstimatedBytes = 0;
3563
- this.toolLedgerCacheEventCount = 0;
3564
- }
3565
- invalidateToolLedgerCacheForInvocation(invocationId) {
3566
- for (const [key, entry] of this.toolLedgerCache) {
3567
- if (entry.invocationIds.has(invocationId)) {
3568
- this.deleteToolLedgerCacheEntry(key, 'terminal');
3569
- }
3570
- }
3571
- }
3572
- toolLedgerEntryExceedsAdmissionBudget(entry) {
3573
- return entry.estimatedBytes > this.toolLedgerCacheBudget.maxSingleEntryEstimatedBytes;
3574
- }
3575
- toolLedgerCacheExceedsBudget() {
3576
- return (this.toolLedgerCache.size > this.toolLedgerCacheBudget.maxEntries ||
3577
- this.toolLedgerCacheEstimatedBytes > this.toolLedgerCacheBudget.maxEstimatedBytes);
3578
- }
3579
- trimToolLedgerCache() {
3580
- for (;;) {
3581
- let victim;
3582
- for (const [key, entry] of this.toolLedgerCache) {
3583
- if (this.uncommittedToolLedgerStates.has(entry.reducer))
3584
- continue;
3585
- if (this.toolLedgerEntryExceedsAdmissionBudget(entry)) {
3586
- victim = key;
3587
- break;
3588
- }
3589
- }
3590
- if (!victim && this.toolLedgerCacheExceedsBudget()) {
3591
- victim = [...this.toolLedgerCache].find(([, entry]) => !this.uncommittedToolLedgerStates.has(entry.reducer))?.[0];
3592
- }
3593
- if (!victim)
3594
- return;
3595
- this.deleteToolLedgerCacheEntry(victim, 'budget');
3596
- }
3597
- }
3598
- currentToolLedgerCacheVersion() {
3599
- return `${this.runtimeDataVersion()}:${this.databaseLease?.writeViewRevision() ?? this.localCommittedWriteRevision}`;
3600
- }
3601
- runtimeDataVersion() {
3602
- const row = this.db.prepare('PRAGMA data_version').get();
3603
- return row.data_version;
3604
- }
3605
3361
  assertInvocationIdentity(events) {
3606
3362
  const candidates = new Map();
3607
3363
  const runs = new Map();
@@ -3832,9 +3588,6 @@ export class SqliteRuntimeStore {
3832
3588
  if (!allowExactDuplicate) {
3833
3589
  throw new Error(`RuntimeEvent ${canonicalEvent.id} already exists outside this tool transaction`);
3834
3590
  }
3835
- if (isTerminalRuntimeEvent(canonicalEvent)) {
3836
- this.invalidateToolLedgerCacheForInvocation(canonicalEvent.invocationId);
3837
- }
3838
3591
  return this.runtimeEventSeq(canonicalEvent.id);
3839
3592
  }
3840
3593
  this.assertContinuationAuthorityAllowsEvent(canonicalEvent, authorizedPendingContinuationClaimId);
@@ -3868,9 +3621,6 @@ export class SqliteRuntimeStore {
3868
3621
  recordTranscriptTurnExtent(this.db, { ...canonicalEvent, kind: runtimeEventKind(canonicalEvent) }, ordinal);
3869
3622
  this.noteEventCommit(canonicalEvent.sessionId);
3870
3623
  this.deleteCompletedPartialSnapshot(canonicalEvent);
3871
- if (isTerminalRuntimeEvent(canonicalEvent)) {
3872
- this.invalidateToolLedgerCacheForInvocation(canonicalEvent.invocationId);
3873
- }
3874
3624
  return next;
3875
3625
  }
3876
3626
  deleteCompletedPartialSnapshot(event) {
@@ -49,6 +49,7 @@ export function buildSqliteSessionCatalogPageQuery(filter, cursor) {
49
49
  metadata.payload_json,
50
50
  metadata.metadata_version,
51
51
  metadata.committed_at,
52
+ metadata.archived_at,
52
53
  projection.activity_at,
53
54
  projection.last_message_preview
54
55
  FROM session_catalog_projection projection
@@ -16,7 +16,7 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
- export const SQLITE_SESSION_METADATA_SCHEMA_VERSION = 40;
19
+ export const SQLITE_SESSION_METADATA_SCHEMA_VERSION = 41;
20
20
  export const SQLITE_SESSION_MESSAGE_CHUNK_BYTES = 64 * 1024;
21
21
  export const SQLITE_SESSION_MESSAGE_CHUNK_MARKER = '{"$maka":"session-message-chunks-v1"}';
22
22
  export const SQLITE_AGENT_GRAPH_CONTROL_TABLES = [
@@ -32,6 +32,16 @@ export const SQLITE_AGENT_GRAPH_CONTROL_TABLES = [
32
32
  'agent_graph_supervisor_wake_attempts',
33
33
  ];
34
34
  const MIGRATIONS = new Map([
35
+ [
36
+ 41,
37
+ `
38
+ -- When the Session last entered the archive, owned by the lifecycle
39
+ -- writer alone. Rows archived before this column existed keep NULL: the
40
+ -- time they were archived was never recorded, and neither committed_at nor
41
+ -- activity says when.
42
+ ALTER TABLE session_metadata ADD COLUMN archived_at INTEGER;
43
+ `,
44
+ ],
35
45
  [
36
46
  40,
37
47
  `
@@ -1370,13 +1380,15 @@ export function migrateSqliteSessionMetadataDatabase(db, options = {}) {
1370
1380
  const sql = MIGRATIONS.get(version);
1371
1381
  if (!sql)
1372
1382
  throw new Error(`Missing SQLite session metadata migration ${version}`);
1373
- // Versions 32, 35, and 37 each add one column, and the post-merge convergence
1374
- // path can replay them onto a database that already carries the current
1375
- // table shape. SQLite has no `ADD COLUMN IF NOT EXISTS`, so the guards
1376
- // live here.
1383
+ // Versions 32, 35, 37, and 41 each add one column, and the post-merge
1384
+ // convergence path can replay them onto a database that already carries
1385
+ // the current table shape. SQLite has no `ADD COLUMN IF NOT EXISTS`, so
1386
+ // the guards live here.
1377
1387
  const columnAlreadyPresent = (version === 32 && hasColumn(db, 'message_admissions', 'submitted_intent_json')) ||
1378
1388
  (version === 35 && hasColumn(db, 'message_admissions', 'skill_invocation_json')) ||
1379
- (version === 37 && hasColumn(db, 'cancelled_message_admissions', 'cancellation_claim_id'));
1389
+ (version === 37 &&
1390
+ hasColumn(db, 'cancelled_message_admissions', 'cancellation_claim_id')) ||
1391
+ (version === 41 && hasColumn(db, 'session_metadata', 'archived_at'));
1380
1392
  if (!columnAlreadyPresent) {
1381
1393
  db.exec(sql);
1382
1394
  }
@@ -740,6 +740,7 @@ export class SqliteSessionMetadataStore {
740
740
  metadata.payload_json,
741
741
  metadata.metadata_version,
742
742
  metadata.committed_at,
743
+ metadata.archived_at,
743
744
  projection.activity_at,
744
745
  projection.last_message_preview
745
746
  FROM session_catalog_projection projection
@@ -954,6 +955,7 @@ export class SqliteSessionMetadataStore {
954
955
  metadata.payload_json,
955
956
  metadata.metadata_version,
956
957
  metadata.committed_at,
958
+ metadata.archived_at,
957
959
  COALESCE(projection.activity_at, 0) AS activity_at,
958
960
  projection.last_message_preview
959
961
  FROM session_metadata metadata
@@ -3307,7 +3309,10 @@ export class SqliteSessionMetadataStore {
3307
3309
  this.assertOpen();
3308
3310
  const identities = uniqueVersionedSessionIdentities(sessions);
3309
3311
  return this.transaction(() => {
3310
- const records = identities.map(({ sessionId, expectedVersion }) => this.setArchivedSync(sessionId, expectedVersion, isArchived));
3312
+ // One instant for the whole set, so a family archived together reads as
3313
+ // archived together.
3314
+ const archivedAt = this.now();
3315
+ const records = identities.map(({ sessionId, expectedVersion }) => this.setArchivedSync(sessionId, expectedVersion, isArchived, archivedAt));
3311
3316
  if (isArchived)
3312
3317
  this.deleteGoalAuthorities(identities);
3313
3318
  return records;
@@ -3349,7 +3354,7 @@ export class SqliteSessionMetadataStore {
3349
3354
  }
3350
3355
  const deletedAt = this.now();
3351
3356
  for (const { sessionId, expectedVersion } of archiveIdentities) {
3352
- this.setArchivedSync(sessionId, expectedVersion, true);
3357
+ this.setArchivedSync(sessionId, expectedVersion, true, deletedAt);
3353
3358
  }
3354
3359
  for (const { sessionId } of present) {
3355
3360
  const deleted = this.db
@@ -3570,7 +3575,7 @@ export class SqliteSessionMetadataStore {
3570
3575
  }, sessionId);
3571
3576
  return this.persistHeaderSync(sessionId, current, next, options);
3572
3577
  }
3573
- setArchivedSync(sessionId, expectedVersion, isArchived) {
3578
+ setArchivedSync(sessionId, expectedVersion, isArchived, archivedAt) {
3574
3579
  const current = this.readRecordSync(sessionId);
3575
3580
  if (!current)
3576
3581
  throw new SessionNotFoundError(sessionId);
@@ -3578,7 +3583,13 @@ export class SqliteSessionMetadataStore {
3578
3583
  throw new SessionMetadataVersionConflictError(sessionId, expectedVersion, current.metadataVersion);
3579
3584
  }
3580
3585
  const next = normalizeSessionHeader({ ...current.header, isArchived }, sessionId);
3581
- return this.persistHeaderSync(sessionId, current, next, { skipNoop: true });
3586
+ // A Session already in the requested state is a no-op below, so an
3587
+ // archived Session keeps the time it was first archived; only a real
3588
+ // transition writes the column. Every other writer leaves it alone.
3589
+ return this.persistHeaderSync(sessionId, current, next, {
3590
+ skipNoop: true,
3591
+ archivedAt: isArchived ? archivedAt : null,
3592
+ });
3582
3593
  }
3583
3594
  persistHeaderSync(sessionId, current, next, options = {}) {
3584
3595
  if (next.id !== sessionId) {
@@ -3610,10 +3621,11 @@ export class SqliteSessionMetadataStore {
3610
3621
  llm_connection_slug = ?,
3611
3622
  model = ?,
3612
3623
  metadata_version = ?,
3613
- committed_at = ?
3624
+ committed_at = ?,
3625
+ archived_at = CASE WHEN ? = 1 THEN ? ELSE archived_at END
3614
3626
  WHERE session_id = ? AND metadata_version = ?
3615
3627
  `)
3616
- .run(JSON.stringify(next), next.createdAt, next.lastMessageAt ?? null, next.name, booleanInteger(next.isFlagged), booleanInteger(next.isArchived), next.parentSessionId ?? null, next.subagentParent?.parentSessionId ?? null, next.revisionRootSessionId ?? null, next.revisionIndex ?? null, booleanInteger(next.hasUnread), next.backend, next.llmConnectionSlug, next.model, metadataVersion, committedAt, sessionId, current.metadataVersion);
3628
+ .run(JSON.stringify(next), next.createdAt, next.lastMessageAt ?? null, next.name, booleanInteger(next.isFlagged), booleanInteger(next.isArchived), next.parentSessionId ?? null, next.subagentParent?.parentSessionId ?? null, next.revisionRootSessionId ?? null, next.revisionIndex ?? null, booleanInteger(next.hasUnread), next.backend, next.llmConnectionSlug, next.model, metadataVersion, committedAt, options.archivedAt === undefined ? 0 : 1, options.archivedAt ?? null, sessionId, current.metadataVersion);
3617
3629
  if (updated.changes !== 1) {
3618
3630
  throw new SessionMetadataConflictError(`Session metadata compare-and-set failed: ${sessionId}`);
3619
3631
  }
@@ -4495,12 +4507,22 @@ function decodeCatalogRecord(row) {
4495
4507
  throw new Error(`Invalid SQLite Session catalog activity for ${row.session_id}`);
4496
4508
  }
4497
4509
  const lastMessagePreview = decodeCatalogPreview(row.last_message_preview, row.session_id);
4510
+ const archivedAt = decodeCatalogArchivedAt(row.archived_at, row.session_id);
4498
4511
  return {
4499
4512
  ...decodeRecord(row),
4500
4513
  activityAt: row.activity_at,
4501
4514
  ...(lastMessagePreview === undefined ? {} : { lastMessagePreview }),
4515
+ ...(archivedAt === undefined ? {} : { archivedAt }),
4502
4516
  };
4503
4517
  }
4518
+ function decodeCatalogArchivedAt(value, sessionId) {
4519
+ if (value === null || value === undefined)
4520
+ return undefined;
4521
+ if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {
4522
+ throw new Error(`Invalid SQLite Session archive time for ${sessionId}`);
4523
+ }
4524
+ return value;
4525
+ }
4504
4526
  function decodeCatalogPreview(value, sessionId) {
4505
4527
  if (value === null || value === undefined)
4506
4528
  return undefined;