maka-agent 0.2.0-dev.68.20261003 → 0.2.0-dev.70.20261005

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.
@@ -682,6 +682,18 @@ async function collectHits(deps, input) {
682
682
  scannedFully = false;
683
683
  }
684
684
  }
685
+ // A narrowed scan needs the corpus count to rank the same way as a full
686
+ // scan. Resolve it before reading transcripts so a declined count cannot
687
+ // discard work and reread every candidate during fallback.
688
+ const storedCorpusSize = deps.countSearchableMessages
689
+ ? await deps.countSearchableMessages({ sessionIds: input.sessionIds })
690
+ : null;
691
+ if (input.abortSignal?.aborted)
692
+ return null;
693
+ if (storedCorpusSize === null && !scannedFully) {
694
+ sessionIds = input.sessionIds;
695
+ scannedFully = true;
696
+ }
685
697
  const hits = [];
686
698
  let counted = 0;
687
699
  for (const sessionId of sessionIds) {
@@ -711,20 +723,7 @@ async function collectHits(deps, input) {
711
723
  hits.push(hit);
712
724
  }
713
725
  }
714
- // The narrowed path never sees the Sessions it skipped, so its corpus size
715
- // has to come from the store. The full scan takes the same number when the
716
- // store offers one, which is what keeps a score independent of the path.
717
- let corpusSize = counted;
718
- if (deps.countSearchableMessages) {
719
- corpusSize = await deps.countSearchableMessages({ sessionIds: input.sessionIds });
720
- if (input.abortSignal?.aborted)
721
- return null;
722
- if (corpusSize === null) {
723
- if (!scannedFully)
724
- return collectHits(deps, { ...input, forceFullScan: true });
725
- corpusSize = counted;
726
- }
727
- }
726
+ const corpusSize = storedCorpusSize ?? counted;
728
727
  return { hits, corpusSize, scannedFully, transcripts };
729
728
  }
730
729
  /**
@@ -1127,9 +1127,6 @@ export class AiSdkTurn {
1127
1127
  }
1128
1128
  const requestSystemPromptBase = joinPromptFragments([
1129
1129
  systemPrompt,
1130
- priorUnknownProjection.kind === 'projected'
1131
- ? priorUnknownProjection.systemNotice
1132
- : undefined,
1133
1130
  finalChildSummaryStep ? CHILD_STEP_BUDGET_FINALIZATION_PROMPT : undefined,
1134
1131
  toolRuntime.hasSandboxBoundaryDenial() ? SANDBOX_BOUNDARY_DENIED_FOR_TURN : undefined,
1135
1132
  sandboxBoundaryFinalizationStep ? SANDBOX_BOUNDARY_FINALIZATION_PROMPT : undefined,
@@ -177,20 +177,10 @@ export function inspectPriorUnknownToolOutcomes(events, invocations) {
177
177
  }
178
178
  if (outcomes.length === 0)
179
179
  return { kind: 'none' };
180
- const rows = outcomes
181
- .slice(0, 64)
182
- .map((outcome) => `- ${outcome.toolName} (operation ${outcome.operationId})`);
183
- if (outcomes.length > 64)
184
- rows.push(`- and ${outcomes.length - 64} more unknown operations`);
185
- return {
186
- kind: 'projected',
187
- outcomes,
188
- systemNotice: [
189
- 'A prior execution was interrupted after these tools were dispatched. No durable tool results were recorded; their side effects may or may not have happened.',
190
- 'The outcome_unknown tool responses in history are temporary context, not proof of success or failure. Inspect current state before repeating any action.',
191
- ...rows,
192
- ].join('\n'),
193
- };
180
+ // The unknown is carried only by the request-local tool responses below
181
+ // (appendPriorUnknownToolResponses): the system prompt stays byte-stable,
182
+ // so the provider's cached request prefix is never churned per event.
183
+ return { kind: 'projected', outcomes };
194
184
  }
195
185
  /** Insert request-only tool responses beside their calls so provider history stays well-formed. */
196
186
  export function appendPriorUnknownToolResponses(events, projection) {
@@ -42,9 +42,23 @@ export class RuntimeReadModel {
42
42
  async getSessionView(sessionId) {
43
43
  const diagnostics = [];
44
44
  const inFlightTurnIds = new Set();
45
+ let snapshot;
46
+ if (this.deps.runtimeEventStore.readSessionRuntimeSnapshot) {
47
+ try {
48
+ snapshot = await this.deps.runtimeEventStore.readSessionRuntimeSnapshot(sessionId);
49
+ }
50
+ catch (error) {
51
+ throw new RuntimeReadModelError('RuntimeEvent Session snapshot read failed', [
52
+ readModelDiagnostic('unsupported_event', 'RuntimeEventStore.readSessionRuntimeSnapshot failed', {
53
+ error: errorMessage(error),
54
+ }),
55
+ ]);
56
+ }
57
+ }
45
58
  let invocations;
46
59
  try {
47
- invocations = (await this.deps.runtimeEventStore.listSessionInvocations(sessionId)).filter((invocation) => isSessionInlineInvocation(invocation.opening));
60
+ invocations = (snapshot?.invocations ??
61
+ (await this.deps.runtimeEventStore.listSessionInvocations(sessionId))).filter((invocation) => isSessionInlineInvocation(invocation.opening));
48
62
  }
49
63
  catch (error) {
50
64
  throw new RuntimeReadModelError('RuntimeReadModel could not list Session invocations', [
@@ -56,15 +70,20 @@ export class RuntimeReadModel {
56
70
  if (invocations.length === 0) {
57
71
  return this.buildView({ invocations, events: [], diagnostics });
58
72
  }
59
- const durableEventOrdinals = await this.readSessionRuntimeEventOrdinals(sessionId);
60
- const durableEventOrdinalById = new Map(durableEventOrdinals.map(({ event, ordinal }) => [event.id, ordinal]));
73
+ const durableEventOrdinalById = snapshot?.durableEventOrdinalById ??
74
+ new Map((await this.readSessionRuntimeEventOrdinals(sessionId)).map(({ event, ordinal }) => [
75
+ event.id,
76
+ ordinal,
77
+ ]));
61
78
  const ordered = [];
62
79
  const terminalFacts = [];
63
80
  for (let runIndex = 0; runIndex < invocations.length; runIndex += 1) {
64
81
  const invocation = invocations[runIndex];
65
82
  let runEvents;
66
83
  try {
67
- runEvents = await this.deps.runtimeEventStore.readRuntimeEvents(sessionId, invocation.runId);
84
+ runEvents = snapshot
85
+ ? (snapshot.eventsByRun.get(invocation.runId) ?? [])
86
+ : await this.deps.runtimeEventStore.readRuntimeEvents(sessionId, invocation.runId);
68
87
  }
69
88
  catch (error) {
70
89
  throw new RuntimeReadModelError('RuntimeEvent ledger read failed', [
@@ -354,6 +354,11 @@ async function createExecutionStoresForWrite(lease, provider) {
354
354
  ensureTerminalRuntimeEventDurable: (sessionId, runId, event) => run(() => runtimeEventStore.ensureTerminalRuntimeEventDurable(sessionId, runId, event)),
355
355
  ensureRecoveredTerminalRuntimeEventDurable: (sessionId, runId, event, operationIds) => run(() => runtimeEventStore.ensureRecoveredTerminalRuntimeEventDurable(sessionId, runId, event, operationIds)),
356
356
  readRuntimeEvents: (sessionId, runId) => run(() => runtimeEventStore.readRuntimeEvents(sessionId, runId)),
357
+ ...(runtimeEventStore.readSessionRuntimeSnapshot
358
+ ? {
359
+ readSessionRuntimeSnapshot: (sessionId) => run(() => runtimeEventStore.readSessionRuntimeSnapshot(sessionId)),
360
+ }
361
+ : {}),
357
362
  scanRuntimeEvents: (sessionId, runId, budget, visit) => run(() => runtimeEventStore.scanRuntimeEvents(sessionId, runId, budget, visit)),
358
363
  readRuntimeEventsBounded: (sessionId, runId, budget) => run(() => runtimeEventStore.readRuntimeEventsBounded(sessionId, runId, budget)),
359
364
  readImmutableRuntimeEvents: (sessionId, runId) => run(() => runtimeEventStore.readImmutableRuntimeEvents(sessionId, runId)),
@@ -466,6 +471,11 @@ async function openExecutionStoresForRead(lease, kind, extension) {
466
471
  },
467
472
  runtimeEventStore: {
468
473
  readRuntimeEvents: (sessionId, runId) => run(() => runtimeEventStore.readRuntimeEvents(sessionId, runId)),
474
+ ...(runtimeEventStore.readSessionRuntimeSnapshot
475
+ ? {
476
+ readSessionRuntimeSnapshot: (sessionId) => run(() => runtimeEventStore.readSessionRuntimeSnapshot(sessionId)),
477
+ }
478
+ : {}),
469
479
  readRuntimeEventsBounded: (sessionId, runId, budget) => run(() => runtimeEventStore.readRuntimeEventsBounded(sessionId, runId, budget)),
470
480
  readImmutableRuntimeEvents: (sessionId, runId) => run(() => runtimeEventStore.readImmutableRuntimeEvents(sessionId, runId)),
471
481
  listSessionInvocations: (sessionId) => run(() => runtimeEventStore.listSessionInvocations(sessionId)),
@@ -39,6 +39,7 @@ export async function openRuntimeEventReadPersistence(input) {
39
39
  return {
40
40
  kind: 'sqlite',
41
41
  runtimeEventStore: Object.freeze({
42
+ readSessionRuntimeSnapshot: (sessionId) => store.readSessionRuntimeSnapshot(sessionId),
42
43
  listSessionInvocations: (sessionId) => store.listSessionInvocations(sessionId),
43
44
  listInvocationRecoveryInventory: (sessionIds) => store.listInvocationRecoveryInventory(sessionIds),
44
45
  readRunInvocation: (sessionId, runId) => store.readRunInvocation(sessionId, runId),
@@ -369,6 +369,74 @@ export class SqliteRuntimeStore {
369
369
  async readRuntimeEvents(sessionId, runId) {
370
370
  return this.readRuntimeEventsSync(sessionId, runId);
371
371
  }
372
+ async readSessionRuntimeSnapshot(sessionId) {
373
+ assertRuntimeStorageSafeId(sessionId, 'Invalid session id');
374
+ return this.readTransaction(() => {
375
+ const decodedEvents = new Map();
376
+ const openings = this.readInvocationOpeningsSync(sessionId, { direction: 'asc' }, decodedEvents);
377
+ const eventsByRun = new Map();
378
+ const durableEventOrdinalById = new Map();
379
+ if (openings.length === 0) {
380
+ return { invocations: [], eventsByRun, durableEventOrdinalById };
381
+ }
382
+ // Read each payload once. The opening query already decoded its events;
383
+ // reuse those same objects. Global event_seq order also gives each run
384
+ // its immutable order and each invocation its first terminal fact.
385
+ const rows = this.db
386
+ .prepare(`
387
+ SELECT event_id, session_id, invocation_id, run_id, turn_id,
388
+ CASE WHEN event_kind = 'invocation_opened' THEN '' ELSE payload_json END AS payload_json
389
+ FROM runtime_events
390
+ WHERE session_id = ?
391
+ ORDER BY event_seq ASC, event_id ASC
392
+ `)
393
+ .all(sessionId);
394
+ const terminalByInvocation = new Map();
395
+ for (const row of rows) {
396
+ const event = decodedEvents.get(row.event_id) ?? decodeRuntimeEventStorageRow(row);
397
+ const events = eventsByRun.get(event.runId) ?? [];
398
+ events.push(event);
399
+ eventsByRun.set(event.runId, events);
400
+ if (isTerminalRuntimeEvent(event) && !terminalByInvocation.has(event.invocationId)) {
401
+ terminalByInvocation.set(event.invocationId, event);
402
+ }
403
+ }
404
+ // Only identities and ordinals are needed here, not a second copy of
405
+ // every payload. Keep the same join and identity checks as the entry reader.
406
+ const ordinals = this.db
407
+ .prepare(`
408
+ SELECT o.ordinal, e.event_id, e.session_id
409
+ FROM runtime_session_event_ordinals o
410
+ JOIN runtime_events e ON e.event_id = o.event_id
411
+ WHERE o.session_id = ?
412
+ ORDER BY o.ordinal ASC
413
+ `)
414
+ .all(sessionId);
415
+ for (const row of ordinals) {
416
+ if (!Number.isSafeInteger(row.ordinal) || row.ordinal < 1) {
417
+ throw new Error(`Invalid RuntimeEvent Session ordinal for ${sessionId}`);
418
+ }
419
+ if (row.session_id !== sessionId) {
420
+ throw new Error(`RuntimeEvent Session ordinal identity mismatch for ${row.event_id}`);
421
+ }
422
+ durableEventOrdinalById.set(row.event_id, row.ordinal);
423
+ }
424
+ const partialsByRun = new Map();
425
+ for (const partial of this.readRuntimePartialSnapshotsSync(sessionId)) {
426
+ const partials = partialsByRun.get(partial.event.runId) ?? [];
427
+ partials.push(partial);
428
+ partialsByRun.set(partial.event.runId, partials);
429
+ }
430
+ for (const [runId, partials] of partialsByRun) {
431
+ eventsByRun.set(runId, mergeRuntimePartialSnapshots(eventsByRun.get(runId) ?? [], partials));
432
+ }
433
+ const invocations = openings.map((opening) => {
434
+ const terminalEvent = terminalByInvocation.get(opening.invocationId);
435
+ return { ...opening, ...(terminalEvent ? { terminalEvent } : {}) };
436
+ });
437
+ return { invocations, eventsByRun, durableEventOrdinalById };
438
+ });
439
+ }
372
440
  transcriptQuery() {
373
441
  return new RuntimeTranscriptQuery(this.db, (sessionId, invocationId) => {
374
442
  // By invocation rather than by run: both shelves key their opening on it,
@@ -692,7 +760,7 @@ export class SqliteRuntimeStore {
692
760
  * Ordering and paging therefore happen in SQL, and a bounded caller decodes
693
761
  * only the openings it asked for.
694
762
  */
695
- readInvocationOpeningsSync(sessionId, options) {
763
+ readInvocationOpeningsSync(sessionId, options, decodedEvents) {
696
764
  const order = options.direction === 'desc' ? 'DESC' : 'ASC';
697
765
  const rows = this.db
698
766
  .prepare(`
@@ -767,6 +835,7 @@ export class SqliteRuntimeStore {
767
835
  if (!opening) {
768
836
  throw new Error(`RuntimeEvent ${event.id} is indexed as an opening fact but is not one`);
769
837
  }
838
+ decodedEvents?.set(event.id, event);
770
839
  return {
771
840
  sessionId: event.sessionId,
772
841
  invocationId: event.invocationId,
@@ -958,10 +1027,10 @@ export class SqliteRuntimeStore {
958
1027
  SELECT stream_key, session_id, invocation_id, run_id, turn_id,
959
1028
  payload_json, text_content, after_event_id
960
1029
  FROM runtime_partial_snapshots
961
- WHERE session_id = ? AND run_id = ?
1030
+ WHERE session_id = ? ${runId === undefined ? '' : 'AND run_id = ?'}
962
1031
  ORDER BY updated_at ASC, stream_key ASC
963
1032
  `)
964
- .all(sessionId, runId);
1033
+ .all(...(runId === undefined ? [sessionId] : [sessionId, runId]));
965
1034
  const segmentText = new Map();
966
1035
  const segments = this.db
967
1036
  .prepare(`
@@ -969,10 +1038,10 @@ export class SqliteRuntimeStore {
969
1038
  FROM runtime_partial_segments AS segment
970
1039
  INNER JOIN runtime_partial_snapshots AS snapshot
971
1040
  ON snapshot.stream_key = segment.stream_key
972
- WHERE snapshot.session_id = ? AND snapshot.run_id = ?
1041
+ WHERE snapshot.session_id = ? ${runId === undefined ? '' : 'AND snapshot.run_id = ?'}
973
1042
  ORDER BY segment.stream_key ASC, segment.segment_seq ASC
974
1043
  `)
975
- .iterate(sessionId, runId);
1044
+ .iterate(...(runId === undefined ? [sessionId] : [sessionId, runId]));
976
1045
  let streamKey;
977
1046
  let chunks = [];
978
1047
  let tail = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maka-agent",
3
- "version": "0.2.0-dev.68.20261003",
3
+ "version": "0.2.0-dev.70.20261005",
4
4
  "description": "Apache Maka (Incubating) developer snapshot; not an Apache release.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",