@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.
- package/dist/agent-core/sdk/agent.js +1 -1
- package/dist/agent-core/sdk/extension-agents.js +1 -1
- package/dist/agent-core/sdk/session.d.ts +18 -12
- package/dist/agent-core/sdk/session.js +42 -39
- package/dist/agent-core/sdk/subagent-backend.js +20 -17
- package/dist/agent-core/session/store/directory-index.d.ts +2 -2
- package/dist/agent-core/session/store/directory-index.js +17 -11
- package/dist/agent-core/session/store/execution-state.d.ts +1 -0
- package/dist/agent-core/session/store/execution-state.js +9 -0
- package/dist/agent-core/session/store/jsonl-store.d.ts +2 -2
- package/dist/agent-core/session/store/jsonl-store.js +217 -218
- package/dist/agent-core/session/store/types.d.ts +1 -0
- package/dist/agent-core/subagent/scheduler.js +1 -1
- package/dist/agent-core/subagent/session-controller.d.ts +7 -1
- package/dist/agent-core/subagent/session-controller.js +560 -352
- package/package.json +1 -1
|
@@ -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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
1893
|
-
delete
|
|
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
|
-
|
|
1916
|
-
delete sessionRuntime.requestMessages;
|
|
1919
|
+
delete executionContext.requestMessages;
|
|
1917
1920
|
compactRequests.close(new MarAgentError('MAR_AGENT_CANCELLED', 'Execution ended.'));
|
|
1918
|
-
if (
|
|
1919
|
-
delete
|
|
1920
|
-
|
|
1921
|
-
|
|
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 (
|
|
1927
|
-
delete
|
|
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
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
2079
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4
|
-
export declare function
|
|
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
|
|
6
|
-
export function
|
|
7
|
-
if (
|
|
8
|
-
current.
|
|
9
|
-
current
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
19
|
-
...(
|
|
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;
|