@myagentroam/agent 0.10.60 → 0.10.62

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.
@@ -100,7 +100,7 @@ export async function createMarAgentWithAdapter(options) {
100
100
  return schedule(definition, run);
101
101
  },
102
102
  baseTools: (id, context) => {
103
- const execution = sessionRuntimes.get(id)?.toolSources;
103
+ const execution = sessionRuntimes.get(id)?.execution?.toolSources;
104
104
  if (context?.executionId && execution?.executionId === context.executionId)
105
105
  return execution.bindings;
106
106
  return (options.host.extensionPorts?.tools?.catalog(id, context?.executionId) ?? []).map((source) => ({
@@ -28,7 +28,7 @@ export function createExtensionAgentsBackend(input) {
28
28
  throw new MarAgentError(created.errorCode ?? 'MAR_AGENT_SUBAGENT_FAILED', created.errorMessage ?? 'Agent could not start.');
29
29
  }
30
30
  })),
31
- list: (call) => withController(call, async (controller) => controller.inspectAll()),
31
+ list: (call) => withController(call, (controller) => controller.list(call.signal)),
32
32
  inspect: (target, call) => withController(call, (controller) => controller.inspect(target.agentId)),
33
33
  wait: (target, call) => withController(call, (controller) => {
34
34
  const selection = parseAgentWaitTargets(target);
@@ -14,9 +14,25 @@ import { JsonlSessionStore } from '../session/store/index.js';
14
14
  import { SubagentSessionController, type SubagentMailboxMessage, type SubagentMessageDelivery, type SubagentTurnHandle } from '../subagent/session-controller.js';
15
15
  import { createSessionToolScheduler } from '../tools/scheduler.js';
16
16
  import { TodoStore } from '../tools/agent/todo.js';
17
- import type { CreateMarAgentOptions, ExecutionResult, MarAgentSession } from './types.js';
18
- export interface SessionRuntime {
17
+ import type { CreateMarAgentOptions, ExecutionMode, ExecutionResult, MarAgentSession } from './types.js';
18
+ interface SessionExecutionContext {
19
+ readonly executionId: string;
20
+ readonly modelId: string;
21
+ readonly mode: ExecutionMode;
22
+ readonly models: ReadonlyMap<string, ResolvedModelConfiguration>;
23
+ readonly instruction: string | undefined;
19
24
  steer?: (input: import('./types.js').SteerInput) => Promise<import('./types.js').SteerResult>;
25
+ compactRequests?: CompactRequests;
26
+ requestMessages?: {
27
+ read(): readonly ModelMessage[];
28
+ };
29
+ appendedMessages?: {
30
+ append(recordId: string, message: ModelMessage): void;
31
+ };
32
+ toolSources?: ExecutionToolSources;
33
+ }
34
+ export interface SessionRuntime {
35
+ execution?: SessionExecutionContext;
20
36
  todo?: TodoStore;
21
37
  readonly scheduleTool: ReturnType<typeof createSessionToolScheduler>;
22
38
  readonly sourceType: 'user' | 'subagent';
@@ -26,22 +42,12 @@ export interface SessionRuntime {
26
42
  subagents?: SubagentSessionController;
27
43
  subagentsOpening?: Promise<SubagentSessionController>;
28
44
  inputs?: SessionInputCoordinator;
29
- compactRequests?: CompactRequests;
30
45
  requestSnapshots?: ExtensionRequestSnapshots;
31
- requestMessages?: {
32
- readonly executionId: string;
33
- read(): readonly ModelMessage[];
34
- };
35
46
  recordProjection?: Promise<unknown>;
36
- appendedMessages?: {
37
- readonly executionId: string;
38
- append(recordId: string, message: ModelMessage): void;
39
- };
40
47
  sessionBackend?: CoreSessionBackend & {
41
48
  kick(): void;
42
49
  };
43
50
  agentsBackend?: CoreAgentsBackend;
44
- toolSources?: ExecutionToolSources;
45
51
  }
46
52
  interface InternalMarAgentSession extends MarAgentSession {
47
53
  startSubagentTurn(input: {
@@ -123,7 +123,6 @@ export function createSessionOpener(services) {
123
123
  text: sessionSource.type === 'subagent' ? childSubagentInstruction || null : null
124
124
  }
125
125
  ]);
126
- let promptDefaults;
127
126
  const sessionAdapter = (modelId, configuredModels) => {
128
127
  let selected = sessionRuntime.adapters.get(modelId);
129
128
  if (selected === undefined) {
@@ -187,7 +186,7 @@ export function createSessionOpener(services) {
187
186
  };
188
187
  recordSessionInputCommit(entry, result);
189
188
  if (message)
190
- sessionRuntime.appendedMessages?.append(record.recordId, message);
189
+ sessionRuntime.execution?.appendedMessages?.append(record.recordId, message);
191
190
  return result;
192
191
  });
193
192
  }
@@ -208,7 +207,6 @@ export function createSessionOpener(services) {
208
207
  const messageSources = new WeakMap();
209
208
  let liveModelMessages = [];
210
209
  const compactRequests = new CompactRequests(executionId, () => compactCoordinator.assertNotReentrant());
211
- sessionRuntime.compactRequests = compactRequests;
212
210
  const permissionPolicy = new ExecutionToolPermissions(mode === 'compact' ? 'full-access' : resolvePermissionMode(input.permissionMode), { sessionId, executionId }, host.requestToolApproval
213
211
  ? {
214
212
  request: (request, signal) => host.requestToolApproval(request, signal),
@@ -225,13 +223,15 @@ export function createSessionOpener(services) {
225
223
  ...currentLocalTimeContext()
226
224
  };
227
225
  const executionSystemInstruction = settings.systemInstruction;
228
- promptDefaults = {
226
+ const executionContext = {
229
227
  executionId,
230
228
  modelId: input.modelId ?? executionDefaultModelId,
231
229
  mode: mode === 'compact' ? 'run' : mode,
232
230
  models: executionModels,
233
- instruction: executionSystemInstruction
231
+ instruction: executionSystemInstruction,
232
+ compactRequests
234
233
  };
234
+ sessionRuntime.execution = executionContext;
235
235
  const manualCompactionId = mode === 'compact' ? randomUUID() : undefined;
236
236
  active.set(`${sessionId}:${executionId}`, controller);
237
237
  const inputs = sessionInputs();
@@ -282,6 +282,7 @@ export function createSessionOpener(services) {
282
282
  let releaseSession;
283
283
  let exhaustedModelAttempt;
284
284
  // Serialize durable events before either the local stream or Host observes them.
285
+ let persistedTerminal = false;
285
286
  const emit = (event, assertLive) => {
286
287
  assertLive?.();
287
288
  const full = {
@@ -303,6 +304,10 @@ export function createSessionOpener(services) {
303
304
  executionId,
304
305
  turnId
305
306
  });
307
+ if (full.type === 'execution.completed' ||
308
+ full.type === 'execution.failed' ||
309
+ full.type === 'execution.cancelled')
310
+ persistedTerminal = true;
306
311
  if (full.type === 'message.completed') {
307
312
  const message = liveModelMessages.findLast((item) => item.role === 'assistant' && item.content === full.text);
308
313
  if (message)
@@ -369,8 +374,7 @@ export function createSessionOpener(services) {
369
374
  await inputs.idle();
370
375
  let immediateTarget;
371
376
  const pendingAppends = [];
372
- sessionRuntime.appendedMessages = {
373
- executionId,
377
+ executionContext.appendedMessages = {
374
378
  append(recordId, message) {
375
379
  pendingAppends.push({
376
380
  recordId,
@@ -755,7 +759,7 @@ export function createSessionOpener(services) {
755
759
  const coreExtensions = await services.extensions.prepare(sessionId, controller.signal, sourceBindings, sessionSource.type === 'user' &&
756
760
  (mode === 'run' || mode === 'plan') &&
757
761
  !internal.automaticInput);
758
- sessionRuntime.toolSources = sources;
762
+ executionContext.toolSources = sources;
759
763
  const operation = (run) => services.extensions.operations.run({
760
764
  sessionId,
761
765
  sessionEpoch: coreExtensions.epoch,
@@ -863,6 +867,7 @@ export function createSessionOpener(services) {
863
867
  executionTools = mcpRegistry.project(baseTools());
864
868
  };
865
869
  const loadRetainedSessionResources = async () => {
870
+ await subagents?.prepareSessionResources(controller.signal);
866
871
  const hostResources = (await host.listSessionResources?.(sessionId)) ?? {
867
872
  processes: []
868
873
  };
@@ -1128,8 +1133,7 @@ export function createSessionOpener(services) {
1128
1133
  sources.bind(captureRoundToolScope().toolScope);
1129
1134
  let preparingRequestMessages;
1130
1135
  let preparingRequestBaseLength = 0;
1131
- sessionRuntime.requestMessages = {
1132
- executionId,
1136
+ executionContext.requestMessages = {
1133
1137
  read: () => preparingRequestMessages !== undefined
1134
1138
  ? normalizeMessagesForModel([...preparingRequestMessages, ...messages.slice(preparingRequestBaseLength)], { supportsImages: selectedModel.inputCapabilities.includes('IMAGE') })
1135
1139
  : projectSessionRequestMessages(messages.map((message, index) => index === currentExecutionUserIndex && modelInputImages?.length
@@ -1192,7 +1196,7 @@ export function createSessionOpener(services) {
1192
1196
  return { executionId, status: 'completed', finalAnswer: '', inputHandled: true };
1193
1197
  }
1194
1198
  acceptsSteer = true;
1195
- sessionRuntime.steer = createNativeSteer({
1199
+ executionContext.steer = createNativeSteer({
1196
1200
  executionId,
1197
1201
  inputs,
1198
1202
  signal: controller.signal,
@@ -1885,12 +1889,12 @@ export function createSessionOpener(services) {
1885
1889
  finalAnswer = executionLoop?.finalAnswer ?? finalAnswer;
1886
1890
  acceptsMailbox = false;
1887
1891
  acceptsSteer = false;
1888
- delete sessionRuntime.steer;
1892
+ delete executionContext.steer;
1889
1893
  await sessionRuntime.recordProjection?.catch(() => undefined);
1890
1894
  await compactCoordinator.close(error);
1891
1895
  compactRequests.close(new MarAgentError('MAR_AGENT_CANCELLED', 'Execution ended.'));
1892
- if (sessionRuntime.compactRequests === compactRequests)
1893
- delete sessionRuntime.compactRequests;
1896
+ if (executionContext.compactRequests === compactRequests)
1897
+ delete executionContext.compactRequests;
1894
1898
  if (controller.signal.aborted) {
1895
1899
  subagents?.endParentExecution(executionId);
1896
1900
  await emit({ type: 'execution.cancelled' });
@@ -1912,19 +1916,16 @@ export function createSessionOpener(services) {
1912
1916
  };
1913
1917
  }
1914
1918
  finally {
1915
- if (sessionRuntime.requestMessages?.executionId === executionId)
1916
- delete sessionRuntime.requestMessages;
1919
+ delete executionContext.requestMessages;
1917
1920
  compactRequests.close(new MarAgentError('MAR_AGENT_CANCELLED', 'Execution ended.'));
1918
- if (sessionRuntime.compactRequests === compactRequests)
1919
- delete sessionRuntime.compactRequests;
1920
- if (sessionRuntime.appendedMessages?.executionId === executionId)
1921
- delete sessionRuntime.appendedMessages;
1922
- if (promptDefaults?.executionId === executionId)
1923
- promptDefaults = undefined;
1921
+ if (executionContext.compactRequests === compactRequests)
1922
+ delete executionContext.compactRequests;
1923
+ delete executionContext.appendedMessages;
1924
+ delete executionContext.steer;
1924
1925
  acceptsMailbox = false;
1925
1926
  executionToolSources?.close();
1926
- if (sessionRuntime.toolSources === executionToolSources)
1927
- delete sessionRuntime.toolSources;
1927
+ if (executionContext.toolSources === executionToolSources)
1928
+ delete executionContext.toolSources;
1928
1929
  internal.signal?.removeEventListener('abort', abortSource);
1929
1930
  await compactCoordinator.close(controller.signal.reason ?? new MarAgentError('MAR_AGENT_CANCELLED', 'Execution ended.'));
1930
1931
  await selectedTurn?.close().catch(() => undefined);
@@ -1940,6 +1941,8 @@ export function createSessionOpener(services) {
1940
1941
  }
1941
1942
  subagents?.endParentExecution(executionId);
1942
1943
  await releaseSession?.();
1944
+ if (sessionRuntime.execution === executionContext)
1945
+ delete sessionRuntime.execution;
1943
1946
  active.delete(`${sessionId}:${executionId}`);
1944
1947
  sessionRuntime.sessionBackend?.kick();
1945
1948
  queue.close();
@@ -1955,6 +1958,9 @@ export function createSessionOpener(services) {
1955
1958
  executionResults.delete(executionKey);
1956
1959
  });
1957
1960
  return {
1961
+ get persistedEvents() {
1962
+ return persistedTerminal;
1963
+ },
1958
1964
  executionId,
1959
1965
  turnId,
1960
1966
  [Symbol.asyncIterator]: () => queue[Symbol.asyncIterator](),
@@ -1974,20 +1980,15 @@ export function createSessionOpener(services) {
1974
1980
  sessionRuntime.sessionBackend ??= createExtensionSessionBackend({
1975
1981
  describe: async (call) => {
1976
1982
  call.signal.throwIfAborted();
1983
+ const defaults = sessionRuntime.execution;
1984
+ const state = defaults ?? services.modelState();
1985
+ const modelId = defaults?.modelId ?? services.modelState().defaultModelId;
1977
1986
  const current = await store.getSession(sessionId);
1978
- const records = await store.read(sessionId);
1987
+ const userTurns = await store.userTurnCount(sessionId);
1979
1988
  call.signal.throwIfAborted();
1980
- const state = promptDefaults ?? services.modelState();
1981
- const modelId = promptDefaults?.modelId ?? services.modelState().defaultModelId;
1982
1989
  const model = state.models.get(modelId);
1983
1990
  if (!model)
1984
1991
  throw new MarAgentError('MAR_AGENT_MODEL_DISABLED', 'Selected model is unavailable.');
1985
- const userTurns = records.filter((record) => {
1986
- if (record.type !== 'turn.user')
1987
- return false;
1988
- const payload = record.payload;
1989
- return payload.inputSource === undefined;
1990
- }).length;
1991
1992
  return {
1992
1993
  sessionId,
1993
1994
  ...(logicalWorkspace ? { cwd: logicalWorkspace, root: logicalWorkspace } : {}),
@@ -2000,9 +2001,10 @@ export function createSessionOpener(services) {
2000
2001
  },
2001
2002
  composePrompt: async (facts, call) => {
2002
2003
  call.signal.throwIfAborted();
2003
- const defaults = promptDefaults;
2004
+ const defaults = sessionRuntime.execution;
2004
2005
  const state = defaults ?? services.modelState();
2005
2006
  const modelId = facts.modelId ?? defaults?.modelId ?? services.modelState().defaultModelId;
2007
+ const instruction = defaults ? defaults.instruction : settings.systemInstruction;
2006
2008
  const model = state.models.get(modelId);
2007
2009
  if (!model)
2008
2010
  throw new MarAgentError('MAR_AGENT_MODEL_DISABLED', 'Selected model is unavailable.');
@@ -2047,7 +2049,7 @@ export function createSessionOpener(services) {
2047
2049
  });
2048
2050
  call.signal.throwIfAborted();
2049
2051
  return {
2050
- sections: buildSessionPromptSections(tools, mode, model, defaults?.instruction ?? settings.systemInstruction, instructions?.skillCatalog),
2052
+ sections: buildSessionPromptSections(tools, mode, model, instruction, instructions?.skillCatalog),
2051
2053
  facts: { modelId, mode, tools: tools.map((tool) => tool.name) }
2052
2054
  };
2053
2055
  },
@@ -2075,8 +2077,9 @@ export function createSessionOpener(services) {
2075
2077
  }
2076
2078
  call.signal.throwIfAborted();
2077
2079
  const targetRuntime = sessionRuntimes.get(target);
2078
- if (selection.view === 'request' && targetRuntime?.requestMessages)
2079
- return sessionMessagesPage(targetRuntime.requestMessages.read(), selection);
2080
+ const requestMessages = targetRuntime?.execution?.requestMessages;
2081
+ if (selection.view === 'request' && requestMessages)
2082
+ return sessionMessagesPage(requestMessages.read(), selection);
2080
2083
  const modelState = services.modelState();
2081
2084
  const requestModel = selection.view === 'request'
2082
2085
  ? modelState.models.get(targetModelId ?? modelState.defaultModelId)
@@ -2215,8 +2218,8 @@ export function createSessionOpener(services) {
2215
2218
  id: sessionId,
2216
2219
  run: (input) => execution('run', input),
2217
2220
  plan: (input) => execution('plan', input),
2218
- steer: (input) => sessionRuntime.steer?.(input) ?? Promise.resolve({ status: 'notAccepted' }),
2219
- compact: (input = {}) => sessionRuntime.compactRequests?.request(input) ??
2221
+ steer: (input) => sessionRuntime.execution?.steer?.(input) ?? Promise.resolve({ status: 'notAccepted' }),
2222
+ compact: (input = {}) => sessionRuntime.execution?.compactRequests?.request(input) ??
2220
2223
  execution('compact', {
2221
2224
  prompt: input.prompt ?? DEFAULT_COMPACTION_FOCUS,
2222
2225
  ...input
@@ -14,33 +14,36 @@ export function createSubagentBackend(store, workspace) {
14
14
  },
15
15
  listChildren: async (parentSessionId) => {
16
16
  const sessions = await store.listSubagentChildren(parentSessionId);
17
- return Promise.all(sessions.map(async (session) => ({
17
+ return sessions.map((session) => ({
18
18
  id: session.id,
19
19
  createdAt: session.createdAt,
20
20
  lastActivityAt: session.lastActivityAt,
21
- source: session.source,
22
- records: await store.read(session.id)
23
- })));
21
+ source: session.source
22
+ }));
24
23
  },
25
24
  loadChild: async (parentSessionId, childSessionId) => {
26
- let session;
27
25
  try {
28
- session = await store.getSession(childSessionId);
26
+ const session = await store.getSession(childSessionId);
27
+ if (session.source.type !== 'subagent' ||
28
+ session.source.parentSessionId !== parentSessionId)
29
+ return undefined;
30
+ return {
31
+ id: session.id,
32
+ createdAt: session.createdAt,
33
+ lastActivityAt: session.lastActivityAt,
34
+ source: session.source,
35
+ records: await store.read(session.id)
36
+ };
29
37
  }
30
38
  catch (error) {
31
- if (error instanceof MarAgentError && error.code === 'MAR_AGENT_SESSION_NOT_FOUND')
32
- return undefined;
39
+ if (error instanceof MarAgentError && error.code === 'MAR_AGENT_SESSION_NOT_FOUND') {
40
+ // A history read failure is not proof that the child was deleted.
41
+ const children = await store.listSubagentChildren(parentSessionId);
42
+ if (!children.some((child) => child.id === childSessionId))
43
+ return undefined;
44
+ }
33
45
  throw error;
34
46
  }
35
- if (session.source.type !== 'subagent' || session.source.parentSessionId !== parentSessionId)
36
- return undefined;
37
- return {
38
- id: session.id,
39
- createdAt: session.createdAt,
40
- lastActivityAt: session.lastActivityAt,
41
- source: session.source,
42
- records: await store.read(session.id)
43
- };
44
47
  }
45
48
  };
46
49
  }
@@ -1,7 +1,7 @@
1
1
  import type { SessionListInput, SessionSource } from '../types.js';
2
2
  import type { SessionIndex, SessionMeta } from './types.js';
3
- /** Merge a derived cache only onto the unchanged history it was computed from. */
4
- export declare function mergeFirstUserMessagePreviewCache(current: SessionMeta, cached: SessionMeta): SessionMeta;
3
+ /** Merge only derived fields, and only onto the history they were computed from. */
4
+ export declare function mergeSessionDerivedCache(current: SessionMeta, cached: SessionMeta): SessionMeta;
5
5
  export declare function readSessionIndex(path: string): Promise<SessionIndex | undefined>;
6
6
  export declare function persistSessionIndex(path: string, index: SessionIndex): Promise<void>;
7
7
  export declare function normalizeSessionSource(value: SessionSource | undefined): SessionSource;
@@ -2,21 +2,25 @@ import { randomUUID } from 'node:crypto';
2
2
  import { readFile, rename, rm, writeFile } from 'node:fs/promises';
3
3
  import { isDeepStrictEqual } from 'node:util';
4
4
  import { MarAgentError } from '../../error.js';
5
- /** Merge a derived cache only onto the unchanged history it was computed from. */
6
- export function mergeFirstUserMessagePreviewCache(current, cached) {
7
- if (cached.resume?.firstUserMessageIndexed !== true ||
8
- current.nextSequence !== cached.nextSequence ||
9
- current.storageTransactionId !== cached.storageTransactionId ||
10
- current.resume?.firstUserMessagePreview !== undefined ||
11
- (current.resume?.firstUserMessageIndexed === true &&
12
- cached.resume.firstUserMessagePreview === undefined))
5
+ /** Merge only derived fields, and only onto the history they were computed from. */
6
+ export function mergeSessionDerivedCache(current, cached) {
7
+ if (current.nextSequence !== cached.nextSequence ||
8
+ current.storageTransactionId !== cached.storageTransactionId)
9
+ return current;
10
+ const preview = cached.resume?.firstUserMessageIndexed === true &&
11
+ current.resume?.firstUserMessagePreview === undefined &&
12
+ !(current.resume?.firstUserMessageIndexed === true &&
13
+ cached.resume.firstUserMessagePreview === undefined);
14
+ const count = current.resume?.userTurns === undefined && cached.resume?.userTurns !== undefined;
15
+ if (!preview && !count)
13
16
  return current;
14
17
  return {
15
18
  ...current,
16
19
  resume: {
17
20
  ...(current.resume ?? { version: 1, todoRevision: 0 }),
18
- firstUserMessageIndexed: true,
19
- ...(cached.resume?.firstUserMessagePreview === undefined
21
+ ...(count ? { userTurns: cached.resume.userTurns } : {}),
22
+ ...(preview ? { firstUserMessageIndexed: true } : {}),
23
+ ...(!preview || cached.resume?.firstUserMessagePreview === undefined
20
24
  ? {}
21
25
  : { firstUserMessagePreview: cached.resume.firstUserMessagePreview })
22
26
  }
@@ -45,7 +49,9 @@ export async function readSessionIndex(path) {
45
49
  typeof session.createdAt !== 'string' ||
46
50
  typeof session.lastActivityAt !== 'string' ||
47
51
  !Number.isSafeInteger(session.nextSequence) ||
48
- session.nextSequence < 1))
52
+ session.nextSequence < 1 ||
53
+ (session.resume?.userTurns !== undefined &&
54
+ (!Number.isSafeInteger(session.resume.userTurns) || session.resume.userTurns < 0))))
49
55
  throw new Error('invalid index shape');
50
56
  return value;
51
57
  }
@@ -3,6 +3,7 @@ import type { CompactHistoryMessage, SessionExecutionState, SessionMeta, Session
3
3
  export declare function deriveExecutionState(records: readonly SessionRecord[]): SessionExecutionState & {
4
4
  resume: NonNullable<SessionMeta['resume']>;
5
5
  };
6
+ export declare function isNativeUserTurn(record: SessionRecord): boolean;
6
7
  export declare function userMessagePreviewFromRecord(record: SessionRecord): string | undefined;
7
8
  export declare function executionModelFromRecord(record: SessionRecord): {
8
9
  modelId: string;
@@ -9,7 +9,10 @@ export function deriveExecutionState(records) {
9
9
  let todoItems = [];
10
10
  let executionModel;
11
11
  let firstUserMessagePreview;
12
+ let userTurns = 0;
12
13
  for (const record of records) {
14
+ if (isNativeUserTurn(record))
15
+ userTurns++;
13
16
  firstUserMessagePreview ??= userMessagePreviewFromRecord(record);
14
17
  if (record.type === 'session.created')
15
18
  automaticTitle =
@@ -40,6 +43,7 @@ export function deriveExecutionState(records) {
40
43
  contextRecords: boundary < 0 ? [...records] : records.slice(boundary),
41
44
  resume: {
42
45
  version: 1,
46
+ userTurns,
43
47
  ...(compactSequence === undefined ? {} : { compactSequence }),
44
48
  ...(checkpoint === undefined
45
49
  ? {}
@@ -57,6 +61,10 @@ export function deriveExecutionState(records) {
57
61
  }
58
62
  };
59
63
  }
64
+ export function isNativeUserTurn(record) {
65
+ return (record.type === 'turn.user' &&
66
+ record.payload.inputSource === undefined);
67
+ }
60
68
  export function userMessagePreviewFromRecord(record) {
61
69
  if (record.type !== 'turn.user' ||
62
70
  !isObject(record.payload) ||
@@ -160,6 +168,7 @@ export function isCompactCheckpoint(record) {
160
168
  }
161
169
  export function sameResumeIndex(left, right) {
162
170
  return (left?.version === right.version &&
171
+ left.userTurns === right.userTurns &&
163
172
  left.compactSequence === right.compactSequence &&
164
173
  left.gcSequence === right.gcSequence &&
165
174
  left.gcGeneration === right.gcGeneration &&
@@ -24,7 +24,6 @@ export declare class JsonlSessionStore {
24
24
  workspace: string;
25
25
  source?: SessionSource;
26
26
  }): Promise<SessionMeta>;
27
- sessionDirectory(sessionId: string): string;
28
27
  readContextualWorldState(sessionId: string): Promise<unknown | undefined>;
29
28
  writeContextualWorldState(sessionId: string, state: unknown): Promise<void>;
30
29
  /** Mark each image pending before publication; turn.user or lease recovery resolves ownership. */
@@ -102,7 +101,6 @@ export declare class JsonlSessionStore {
102
101
  readonly expectedState?: readonly CodeModeStateEntry[];
103
102
  }): Promise<CodeModeStateEntry[]>;
104
103
  readExecutionState(sessionId: string): Promise<SessionExecutionState>;
105
- readContext(sessionId: string): Promise<SessionRecord[]>;
106
104
  /** Compare the planned sequence inside the existing record writer queue. */
107
105
  commitContextGc(sessionId: string, draft: ContextGcCheckpointDraft, planningRecords?: readonly SessionRecord[]): Promise<SessionRecord>;
108
106
  readPage(sessionId: string, input?: {
@@ -177,6 +175,8 @@ export declare class JsonlSessionStore {
177
175
  readonly reasoningEffort?: string;
178
176
  } | undefined>;
179
177
  sessionTree(sessionId: string): Promise<readonly string[]>;
178
+ /** Counts native input records, not the post-Compact model context. */
179
+ userTurnCount(sessionId: string): Promise<number>;
180
180
  firstUserMessagePreview(sessionId: string): Promise<string | undefined>;
181
181
  delete(sessionId: string): Promise<{
182
182
  readonly deleted: boolean;