@myagentroam/agent 0.11.13 → 0.11.15

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 (36) hide show
  1. package/dist/agent-core/host/contracts.d.ts +2 -2
  2. package/dist/agent-core/host/contracts.js +1 -2
  3. package/dist/agent-core/host/local-agent-host.js +1 -2
  4. package/dist/agent-core/runtime/compaction/requests.d.ts +1 -1
  5. package/dist/agent-core/runtime/compaction/retained-context.js +1 -1
  6. package/dist/agent-core/runtime/compaction/smart.d.ts +1 -1
  7. package/dist/agent-core/runtime/compaction/smart.js +1 -1
  8. package/dist/agent-core/runtime/context/gc-runtime.d.ts +2 -2
  9. package/dist/agent-core/runtime/context/gc-runtime.js +9 -6
  10. package/dist/agent-core/runtime/context/gc.js +43 -29
  11. package/dist/agent-core/runtime/context/projection.d.ts +1 -1
  12. package/dist/agent-core/runtime/context/projection.js +4 -4
  13. package/dist/agent-core/runtime/context/restore.d.ts +3 -2
  14. package/dist/agent-core/runtime/context/restore.js +2 -3
  15. package/dist/agent-core/runtime/context/usage.js +1 -1
  16. package/dist/agent-core/runtime/context/world-state.d.ts +1 -1
  17. package/dist/agent-core/runtime/context/world-state.js +1 -1
  18. package/dist/agent-core/runtime/execution-handle.d.ts +12 -0
  19. package/dist/agent-core/runtime/execution-handle.js +1 -0
  20. package/dist/agent-core/sdk/agent.js +140 -63
  21. package/dist/agent-core/sdk/session.d.ts +3 -3
  22. package/dist/agent-core/sdk/session.js +3 -1
  23. package/dist/agent-core/sdk/subagent-backend.d.ts +2 -2
  24. package/dist/agent-core/sdk/types.d.ts +6 -13
  25. package/dist/agent-core/session/catalog.d.ts +8 -2
  26. package/dist/agent-core/session/catalog.js +48 -10
  27. package/dist/agent-core/session/context-gc-checkpoint.d.ts +4 -2
  28. package/dist/agent-core/session/context-gc-checkpoint.js +15 -7
  29. package/dist/agent-core/session/store/contracts.d.ts +148 -0
  30. package/dist/agent-core/session/store/contracts.js +1 -0
  31. package/dist/agent-core/session/store/execution-state.js +0 -12
  32. package/dist/agent-core/session/store/jsonl-store.d.ts +7 -30
  33. package/dist/agent-core/session/store/jsonl-store.js +133 -47
  34. package/dist/agent-core/session/store/types.d.ts +0 -3
  35. package/dist/index.d.ts +2 -0
  36. package/package.json +1 -1
@@ -0,0 +1,148 @@
1
+ import type { CoreCheckpoint, CoreJsonValue } from '../../extensions/data.js';
2
+ import type { ModelMessage, ModelUsage } from '../../model/contracts.js';
3
+ import type { EnvironmentContextState } from '../../runtime/context/contracts.js';
4
+ import type { CodeModeStateEntry } from '../../tools/code-mode/state.js';
5
+ import type { AvailableToolResultResource } from '../../tools/result-extensions.js';
6
+ import type { MarAgentSessionPage, MarAgentSessionSummary } from '../catalog.js';
7
+ import type { ContextGcCheckpointDraft } from '../context-gc-checkpoint.js';
8
+ import type { ToolResultResourceInput, ToolResultResourcePage } from '../tool-result-resources.js';
9
+ import type { SessionListInput, SessionSource, SessionTitleUpdateInput } from '../types.js';
10
+ import type { CompactHistoryMessage, RestoredSessionInputImage, SessionExecutionState, SessionInputImage, SessionInputImageData, SessionInputImageMetadata, SessionRecord } from './types.js';
11
+ export type { CompactHistoryMessage, CompactUserInput, RestoredSessionInputImage, SessionExecutionState, SessionInputImage, SessionInputImageData, SessionInputImageMetadata, SessionRecord } from './types.js';
12
+ export type { CodeModeStateEntry } from '../../tools/code-mode/state.js';
13
+ export type { TodoSnapshot, TodoItem } from '../../tools/contracts.js';
14
+ export type { CodeToolCallRecord, CodeObservation } from '../../tools/code-mode/observations.js';
15
+ export type { ModelMessage, ModelUsage, ModelInputMetadata, ModelTextBlock, ModelContextualBlock } from '../../model/contracts.js';
16
+ export type { EnvironmentContextState } from '../../runtime/context/contracts.js';
17
+ export type { ContextGcCheckpointDraft, ContextGcBase, ContextGcPlatform, ContextGcEconomicsPolicy, ContextGcMetrics, ContextGcReplacement, ContextGcTarget, ContextGcReadItemReplacement } from '../context-gc-checkpoint.js';
18
+ export type { ContextGcWebToolName } from '../context-gc-replacements.js';
19
+ export type { ContextGcSubagentToolName } from '../context-gc-subagent.js';
20
+ export type { AvailableToolResultResource } from '../../tools/result-extensions.js';
21
+ /** Logical Session identity, without a physical index or recovery layout. */
22
+ export interface SessionStoreSession {
23
+ id: string;
24
+ title: string;
25
+ workspace: string;
26
+ createdAt: string;
27
+ lastActivityAt: string;
28
+ source?: SessionSource | undefined;
29
+ }
30
+ export interface SessionRecordInput {
31
+ type: string;
32
+ payload: unknown;
33
+ executionId?: string;
34
+ turnId?: string;
35
+ timestamp?: string;
36
+ }
37
+ export interface SessionStoreCompactInput {
38
+ summary: string;
39
+ replacementHistory?: readonly CompactHistoryMessage[];
40
+ retainedModelContext?: readonly ModelMessage[];
41
+ pendingInputs?: readonly SessionRecord[];
42
+ snapshotRecordId?: string;
43
+ snapshotMessageCount?: number;
44
+ environment?: EnvironmentContextState;
45
+ automatic?: boolean;
46
+ modelId?: string;
47
+ contextWindowTokens?: number;
48
+ usage?: ModelUsage;
49
+ executionId?: string;
50
+ turnId?: string;
51
+ }
52
+ /** Store-issued ownership spanning SDK cleanup and the final durable mutation. */
53
+ export interface SessionStoreMaintenance {
54
+ readonly sessionIds: readonly string[];
55
+ /** Idempotent; releases maintenance only, never another owner's Execution/retained references. */
56
+ release(): Promise<void>;
57
+ }
58
+ /**
59
+ * Session persistence port. Implementations preserve record order, committed-resource
60
+ * visibility and the existing per-Session writer/checkpoint atomic boundaries.
61
+ * Owned handles are closed by their creator; injected instances are borrowed.
62
+ */
63
+ export interface SessionStore {
64
+ createSession(input: {
65
+ title?: string;
66
+ workspace: string;
67
+ source?: SessionSource;
68
+ }): Promise<SessionStoreSession>;
69
+ getSession(sessionId: string): Promise<MarAgentSessionSummary & {
70
+ automaticTitlePending: boolean;
71
+ }>;
72
+ list(input?: SessionListInput): Promise<MarAgentSessionPage>;
73
+ listSubagentChildren(parentSessionId: string): Promise<Array<MarAgentSessionSummary & {
74
+ source: Extract<SessionSource, {
75
+ type: 'subagent';
76
+ }>;
77
+ }>>;
78
+ executionConfiguration(sessionId: string): Promise<{
79
+ readonly modelId: string;
80
+ readonly modelName?: string;
81
+ readonly reasoningEffort?: string;
82
+ } | undefined>;
83
+ firstUserMessagePreview(sessionId: string): Promise<string | undefined>;
84
+ sessionTree(sessionId: string): Promise<readonly string[]>;
85
+ updateTitle(sessionId: string, title: string, input?: SessionTitleUpdateInput, owner?: object): Promise<boolean>;
86
+ append(sessionId: string, input: SessionRecordInput, options?: {
87
+ readonly signal?: AbortSignal;
88
+ }): Promise<SessionRecord>;
89
+ read(sessionId: string): Promise<SessionRecord[]>;
90
+ readPage(sessionId: string, input?: {
91
+ limit?: number;
92
+ beforeSequence?: number;
93
+ }): Promise<{
94
+ records: SessionRecord[];
95
+ previousBeforeSequence?: number;
96
+ }>;
97
+ readExecutionState(sessionId: string): Promise<SessionExecutionState>;
98
+ userTurnCount(sessionId: string): Promise<number>;
99
+ readContextualWorldState(sessionId: string): Promise<unknown | undefined>;
100
+ writeContextualWorldState(sessionId: string, state: unknown): Promise<void>;
101
+ readCodeModeState(sessionId: string): Promise<CodeModeStateEntry[]>;
102
+ commitCodeModeState(sessionId: string, mutations: unknown, input: {
103
+ readonly executionId: string;
104
+ readonly turnId: string;
105
+ readonly expectedState?: readonly CodeModeStateEntry[];
106
+ }): Promise<CodeModeStateEntry[]>;
107
+ readExtensionCheckpoint(sessionId: string, extensionId: string, key: string): Promise<CoreCheckpoint>;
108
+ writeExtensionCheckpoint(sessionId: string, extensionId: string, key: string, value: CoreJsonValue | undefined, expectedRevision: number | undefined, input: {
109
+ owner: object;
110
+ signal: AbortSignal;
111
+ executionId?: string;
112
+ turnId?: string;
113
+ }): Promise<CoreCheckpoint>;
114
+ compact(sessionId: string, input: SessionStoreCompactInput, options?: {
115
+ readonly signal?: AbortSignal;
116
+ }): Promise<SessionRecord>;
117
+ commitContextGc(sessionId: string, draft: ContextGcCheckpointDraft, planningRecords?: readonly SessionRecord[]): Promise<SessionRecord>;
118
+ persistInputImages(sessionId: string, images: readonly SessionInputImage[]): Promise<SessionInputImageMetadata[]>;
119
+ readInputImages(sessionId: string, value: unknown): Promise<RestoredSessionInputImage[]>;
120
+ readInputImage(sessionId: string, input: {
121
+ readonly sequence: number;
122
+ readonly imageIndex: number;
123
+ }): Promise<SessionInputImageData | undefined>;
124
+ writeToolResultResource(sessionId: string, text: string): Promise<AvailableToolResultResource>;
125
+ readToolResultResource(sessionId: string, input: ToolResultResourceInput): Promise<ToolResultResourcePage>;
126
+ /** One main Execution per Session; an owner may reuse its healthy retained writer. */
127
+ acquireSessionLease(sessionId: string, owner?: object): Promise<() => Promise<void>>;
128
+ /** Native writes join the same owner's writer, without admitting another Execution. */
129
+ acquireSessionWriteLease(sessionId: string, owner: object): Promise<() => Promise<void>>;
130
+ /** Retention requires the originating owner to hold an active Execution lease. */
131
+ retainSessionLease(sessionId: string, owner: object): Promise<() => Promise<void>>;
132
+ hasRetainedSessionLease(sessionId: string, owner: object): boolean;
133
+ /** Conflicts fail before cleanup; delete includes the Store's current Session tree. */
134
+ acquireSessionMaintenance(sessionId: string, kind: 'clear' | 'delete', owner?: object): Promise<SessionStoreMaintenance>;
135
+ forkSession(sessionId: string, input: {
136
+ readonly boundaryTurnId: string | null;
137
+ readonly title: string;
138
+ }): Promise<SessionStoreSession>;
139
+ rollbackSession(sessionId: string, input: {
140
+ readonly boundaryTurnId: string | null;
141
+ }): Promise<MarAgentSessionSummary>;
142
+ clearSession(sessionId: string, maintenance?: SessionStoreMaintenance): Promise<MarAgentSessionSummary>;
143
+ delete(sessionId: string, maintenance?: SessionStoreMaintenance): Promise<{
144
+ readonly deleted: boolean;
145
+ }>;
146
+ /** Stop using the handle and release its leases before closing it. Never preempts other owners. */
147
+ close(): Promise<void>;
148
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,4 @@
1
1
  import { todoItemsFromSnapshot } from '../../tools/agent/todo.js';
2
- import { contextGcBaseSequence, latestValidContextGcCheckpoint } from '../context-gc-checkpoint.js';
3
2
  export function deriveExecutionState(records) {
4
3
  let automaticTitle = false;
5
4
  let sawUserTurn = false;
@@ -36,7 +35,6 @@ export function deriveExecutionState(records) {
36
35
  executionModel = candidate;
37
36
  }
38
37
  const boundary = records.findLastIndex(isCompactCheckpoint);
39
- const checkpoint = latestValidContextGcCheckpoint(records);
40
38
  return {
41
39
  automaticTitlePending: automaticTitle && !sawUserTurn,
42
40
  todo: { revision: todoRevision, items: todoItems },
@@ -45,13 +43,6 @@ export function deriveExecutionState(records) {
45
43
  version: 1,
46
44
  userTurns,
47
45
  ...(compactSequence === undefined ? {} : { compactSequence }),
48
- ...(checkpoint === undefined
49
- ? {}
50
- : {
51
- gcSequence: checkpoint.record.sequence,
52
- gcGeneration: checkpoint.payload.generation,
53
- gcBaseSequence: contextGcBaseSequence(checkpoint.payload.base)
54
- }),
55
46
  ...(todoSequence === undefined ? {} : { todoSequence }),
56
47
  todoRevision,
57
48
  executionModelIndexed: true,
@@ -170,9 +161,6 @@ export function sameResumeIndex(left, right) {
170
161
  return (left?.version === right.version &&
171
162
  left.userTurns === right.userTurns &&
172
163
  left.compactSequence === right.compactSequence &&
173
- left.gcSequence === right.gcSequence &&
174
- left.gcGeneration === right.gcGeneration &&
175
- left.gcBaseSequence === right.gcBaseSequence &&
176
164
  left.todoSequence === right.todoSequence &&
177
165
  left.todoRevision === right.todoRevision &&
178
166
  left.executionModelIndexed === right.executionModelIndexed &&
@@ -1,24 +1,15 @@
1
+ import type { SessionStore, SessionStoreMaintenance, SessionRecordInput, SessionStoreCompactInput } from './contracts.js';
1
2
  import { type CoreCheckpoint, type CoreJsonValue } from '../../extensions/data.js';
2
- import type { ModelMessage, ModelUsage } from '../../model/contracts.js';
3
- import type { EnvironmentContextState } from '../../runtime/context/contracts.js';
4
3
  import { type CodeModeStateEntry } from '../../tools/code-mode/state.js';
5
4
  import { type ContextGcCheckpointDraft } from '../context-gc-checkpoint.js';
6
5
  import { type ToolResultResourceInput } from '../tool-result-resources.js';
7
6
  import type { SessionListInput, SessionSource, SessionTitleUpdateInput } from '../types.js';
8
- import type { CompactHistoryMessage, RestoredSessionInputImage, SessionExecutionState, SessionInputImage, SessionInputImageData, SessionInputImageMetadata, SessionMeta, SessionRecord } from './types.js';
9
- interface SessionRecordInput {
10
- type: string;
11
- payload: unknown;
12
- executionId?: string;
13
- turnId?: string;
14
- timestamp?: string;
15
- }
7
+ import type { RestoredSessionInputImage, SessionExecutionState, SessionInputImage, SessionInputImageData, SessionInputImageMetadata, SessionMeta, SessionRecord } from './types.js';
16
8
  /** SDK-owned JSONL history with per-HOME in-process sharing and cross-process file locks. */
17
- export declare class JsonlSessionStore {
9
+ export declare class JsonlSessionStore implements SessionStore {
18
10
  #private;
19
11
  private constructor();
20
12
  static open(home: string): Promise<JsonlSessionStore>;
21
- /** Append the durable creation record before making this Session visible in the index. */
22
13
  createSession(input: {
23
14
  title?: string;
24
15
  workspace: string;
@@ -65,7 +56,7 @@ export declare class JsonlSessionStore {
65
56
  }>;
66
57
  updateTitle(sessionId: string, title: string, input?: SessionTitleUpdateInput, owner?: object): Promise<boolean>;
67
58
  /** Reset history with the same recovery journal while preserving the Session identity. */
68
- clearSession(sessionId: string): Promise<{
59
+ clearSession(sessionId: string, maintenance?: SessionStoreMaintenance): Promise<{
69
60
  id: string;
70
61
  title: string;
71
62
  workspace: string;
@@ -110,26 +101,13 @@ export declare class JsonlSessionStore {
110
101
  records: SessionRecord[];
111
102
  previousBeforeSequence?: number;
112
103
  }>;
113
- compact(sessionId: string, input: {
114
- summary: string;
115
- replacementHistory?: readonly CompactHistoryMessage[];
116
- retainedModelContext?: readonly ModelMessage[];
117
- pendingInputs?: readonly SessionRecord[];
118
- snapshotRecordId?: string;
119
- snapshotMessageCount?: number;
120
- environment?: EnvironmentContextState;
121
- automatic?: boolean;
122
- modelId?: string;
123
- contextWindowTokens?: number;
124
- usage?: ModelUsage;
125
- executionId?: string;
126
- turnId?: string;
127
- }, options?: {
104
+ compact(sessionId: string, input: SessionStoreCompactInput, options?: {
128
105
  readonly signal?: AbortSignal;
129
106
  }): Promise<SessionRecord>;
130
107
  /** A warm cell retains physical writer ownership between Executions of the same Agent. */
131
108
  acquireSessionLease(sessionId: string, owner?: object): Promise<() => Promise<void>>;
132
109
  hasRetainedSessionLease(sessionId: string, owner: object): boolean;
110
+ acquireSessionMaintenance(sessionId: string, kind: 'clear' | 'delete', owner?: object): Promise<SessionStoreMaintenance>;
133
111
  /** Native writes join their Agent's sole writer, without admitting another Execution. */
134
112
  acquireSessionWriteLease(sessionId: string, owner: object): Promise<() => Promise<void>>;
135
113
  retainSessionLease(sessionId: string, owner: object): Promise<() => Promise<void>>;
@@ -178,9 +156,8 @@ export declare class JsonlSessionStore {
178
156
  /** Counts native input records, not the post-Compact model context. */
179
157
  userTurnCount(sessionId: string): Promise<number>;
180
158
  firstUserMessagePreview(sessionId: string): Promise<string | undefined>;
181
- delete(sessionId: string): Promise<{
159
+ delete(sessionId: string, maintenance?: SessionStoreMaintenance): Promise<{
182
160
  readonly deleted: boolean;
183
161
  }>;
184
162
  close(): Promise<void>;
185
163
  }
186
- export {};
@@ -7,7 +7,7 @@ import { cloneCoreJson } from '../../extensions/data.js';
7
7
  import { acquireFileLock } from '../../storage/file-lock.js';
8
8
  import { todoItemsFromSnapshot } from '../../tools/agent/todo.js';
9
9
  import { applyCodeModeStateMutations, parseCodeModeStateMutations, restoreCodeModeState } from '../../tools/code-mode/state.js';
10
- import { contextGcBaseFromRecords, contextGcBaseSequence, contextGcMinimumUsageGrowth, latestValidContextGcCheckpoint, normalizeContextGcReplacements, parseContextGcCheckpoint, sameContextGcBase, validateContextGcCheckpoint } from '../context-gc-checkpoint.js';
10
+ import { contextGcBaseFromRecords, contextGcCheckpointIntervalEligible, latestValidContextGcCheckpoint, normalizeContextGcReplacements, sameContextGcBase, validateContextGcCheckpoint } from '../context-gc-checkpoint.js';
11
11
  import { bindToolResultResources, cleanupUnreferencedToolResources, completeToolResultResources, copyToolResultResources, readToolResultResource, toolResultResourceFiles, writeToolResultResource } from '../tool-result-resources.js';
12
12
  import { beginSessionStorageTransaction, isUnjournaledTransactionFile, recoverSessionStorageTransaction } from '../transaction-recovery.js';
13
13
  import { extensionCheckpointFromPayload, indexExtensionCheckpoint, restoreExtensionCheckpoint, updateSessionCurrentState } from './current-state.js';
@@ -23,6 +23,7 @@ export class JsonlSessionStore {
23
23
  static #instances = new Map();
24
24
  static #opening = new Map();
25
25
  #shared;
26
+ #maintenance = new WeakMap();
26
27
  #leaseClosed = false;
27
28
  constructor(shared) {
28
29
  this.#shared = shared;
@@ -283,9 +284,22 @@ export class JsonlSessionStore {
283
284
  }
284
285
  return { directory, release };
285
286
  }
286
- /** Append the durable creation record before making this Session visible in the index. */
287
287
  async createSession(input) {
288
288
  this.#assertOpen();
289
+ const source = normalizeSessionSource(input.source);
290
+ if (source.type !== 'subagent')
291
+ return this.#createSession(input, source);
292
+ // Publish under the parent's existing serial domain so tree admission cannot miss a child.
293
+ return this.#serializeRecordOperation(source.parentSessionId, async () => {
294
+ this.#assertOpen();
295
+ if (this.#shared.writerLeases.get(source.parentSessionId)?.maintenance)
296
+ throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', 'Session is under maintenance.');
297
+ await this.getSession(source.parentSessionId);
298
+ return this.#createSession(input, source);
299
+ });
300
+ }
301
+ /** Append the durable creation record before making this Session visible in the index. */
302
+ async #createSession(input, source) {
289
303
  const id = randomUUID();
290
304
  const now = new Date().toISOString();
291
305
  const meta = {
@@ -294,7 +308,7 @@ export class JsonlSessionStore {
294
308
  workspace: input.workspace,
295
309
  createdAt: now,
296
310
  lastActivityAt: now,
297
- source: normalizeSessionSource(input.source),
311
+ source,
298
312
  nextSequence: 1,
299
313
  automaticTitlePending: input.title === undefined,
300
314
  resume: {
@@ -697,9 +711,11 @@ export class JsonlSessionStore {
697
711
  }
698
712
  }
699
713
  /** Reset history with the same recovery journal while preserving the Session identity. */
700
- async clearSession(sessionId) {
714
+ async clearSession(sessionId, maintenance) {
701
715
  this.#assertOpen();
702
- const release = await this.acquireSessionLease(sessionId);
716
+ const holding = maintenance ?? (await this.acquireSessionMaintenance(sessionId, 'clear'));
717
+ this.#assertMaintenance(sessionId, 'clear', holding);
718
+ let failed = false;
703
719
  try {
704
720
  const meta = this.#shared.index.sessions.find((item) => item.id === sessionId);
705
721
  if (!meta)
@@ -726,8 +742,18 @@ export class JsonlSessionStore {
726
742
  ];
727
743
  return await this.#replaceHistory(meta, retained, { kind: 'clear', clearedAt });
728
744
  }
745
+ catch (error) {
746
+ failed = true;
747
+ throw error;
748
+ }
729
749
  finally {
730
- await release();
750
+ if (!maintenance) {
751
+ const releasing = holding.release();
752
+ if (failed)
753
+ await releasing.catch(() => undefined);
754
+ else
755
+ await releasing;
756
+ }
731
757
  }
732
758
  }
733
759
  /** Called only while rollback or clear holds the maintenance lease. */
@@ -1123,14 +1149,9 @@ export class JsonlSessionStore {
1123
1149
  records.at(-1)?.sessionId !== sessionId ||
1124
1150
  !sameContextGcBase(contextGcBaseFromRecords(records, boundarySequence), draft.base))
1125
1151
  throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Context GC planning boundary is invalid.');
1126
- const previous = latestValidContextGcCheckpoint(records, boundarySequence, meta.workspace);
1127
- if (previous) {
1128
- const minimumUsageGrowth = contextGcMinimumUsageGrowth(draft.trigger.contextWindowTokens);
1129
- if (draft.trigger.normalRolloutOrdinal - previous.payload.trigger.normalRolloutOrdinal < 2 ||
1130
- draft.trigger.contextInputTokens - previous.payload.trigger.contextInputTokens <
1131
- minimumUsageGrowth)
1132
- throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Context GC checkpoint interval validation failed.');
1133
- }
1152
+ const previous = latestValidContextGcCheckpoint(records, boundarySequence);
1153
+ if (!contextGcCheckpointIntervalEligible(draft.trigger, previous?.payload.trigger))
1154
+ throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Context GC checkpoint interval validation failed.');
1134
1155
  const plannedReplacements = normalizeContextGcReplacements(draft.replacements);
1135
1156
  if (!plannedReplacements || plannedReplacements.length === 0)
1136
1157
  throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Context GC checkpoint must contain a replacement.');
@@ -1236,7 +1257,7 @@ export class JsonlSessionStore {
1236
1257
  this.#assertOpen();
1237
1258
  let lease = this.#shared.writerLeases.get(sessionId);
1238
1259
  if (lease) {
1239
- if (lease.executing || owner === undefined || lease.owner !== owner)
1260
+ if (lease.maintenance || lease.executing || owner === undefined || lease.owner !== owner)
1240
1261
  throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', 'Session already has a writer.');
1241
1262
  if (lease.appendFailure)
1242
1263
  throw lease.appendFailure.error;
@@ -1262,12 +1283,89 @@ export class JsonlSessionStore {
1262
1283
  const lease = this.#shared.writerLeases.get(sessionId);
1263
1284
  return lease?.owner === owner && lease.retained > 0;
1264
1285
  }
1286
+ async acquireSessionMaintenance(sessionId, kind, owner) {
1287
+ this.#assertOpen();
1288
+ const acquired = new Map();
1289
+ let sessionIds = [];
1290
+ let released = false;
1291
+ const holding = Object.freeze({
1292
+ get sessionIds() {
1293
+ return sessionIds;
1294
+ },
1295
+ release: async () => {
1296
+ if (released)
1297
+ return;
1298
+ released = true;
1299
+ this.#maintenance.delete(holding);
1300
+ let failure;
1301
+ for (const [id, writer] of [...acquired].reverse()) {
1302
+ try {
1303
+ await this.#serializeRecordOperation(id, async () => {
1304
+ delete writer.maintenance;
1305
+ await this.#releaseUnusedWriter(id, writer);
1306
+ });
1307
+ }
1308
+ catch (error) {
1309
+ failure ??= { error };
1310
+ }
1311
+ }
1312
+ if (failure)
1313
+ throw failure.error;
1314
+ }
1315
+ });
1316
+ try {
1317
+ // Re-read membership after acquiring writers; never hand the SDK a pre-lock tree snapshot.
1318
+ for (;;) {
1319
+ const tree = kind === 'delete' ? await this.sessionTree(sessionId) : [sessionId];
1320
+ for (const id of tree.toReversed()) {
1321
+ if (acquired.has(id))
1322
+ continue;
1323
+ await this.#serializeRecordOperation(id, async () => {
1324
+ this.#assertOpen();
1325
+ let writer = this.#shared.writerLeases.get(id);
1326
+ if (writer &&
1327
+ (writer.maintenance ||
1328
+ owner === undefined ||
1329
+ writer.owner !== owner ||
1330
+ (kind === 'clear' && writer.executing)))
1331
+ throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', 'Session already has a writer.');
1332
+ if (!writer) {
1333
+ // Deleting a stale index entry must not recreate its missing directory.
1334
+ const release = kind === 'delete' && !(await sessionDirectoryExists(this.#sessionDirectory(id)))
1335
+ ? async () => { }
1336
+ : await this.#acquireSessionLease(id);
1337
+ writer = { owner, executing: false, retained: 0, release };
1338
+ this.#shared.writerLeases.set(id, writer);
1339
+ }
1340
+ writer.maintenance = holding;
1341
+ acquired.set(id, writer);
1342
+ });
1343
+ }
1344
+ const current = kind === 'clear' ? tree : await this.sessionTree(sessionId);
1345
+ if (current.every((id) => acquired.has(id))) {
1346
+ sessionIds = Object.freeze(current);
1347
+ break;
1348
+ }
1349
+ }
1350
+ this.#maintenance.set(holding, { sessionId, kind });
1351
+ return holding;
1352
+ }
1353
+ catch (error) {
1354
+ await holding.release().catch(() => undefined);
1355
+ throw error;
1356
+ }
1357
+ }
1358
+ #assertMaintenance(sessionId, kind, holding) {
1359
+ const current = this.#maintenance.get(holding);
1360
+ if (current?.sessionId !== sessionId || current.kind !== kind)
1361
+ throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', 'Session maintenance ownership is invalid.');
1362
+ }
1265
1363
  /** Native writes join their Agent's sole writer, without admitting another Execution. */
1266
1364
  async acquireSessionWriteLease(sessionId, owner) {
1267
1365
  return this.#serializeRecordOperation(sessionId, async () => {
1268
1366
  this.#assertOpen();
1269
1367
  let lease = this.#shared.writerLeases.get(sessionId);
1270
- if (lease && lease.owner !== owner)
1368
+ if (lease && (lease.maintenance || lease.owner !== owner))
1271
1369
  throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', 'Session already has a writer.');
1272
1370
  if (lease?.appendFailure)
1273
1371
  throw lease.appendFailure.error;
@@ -1291,7 +1389,7 @@ export class JsonlSessionStore {
1291
1389
  async retainSessionLease(sessionId, owner) {
1292
1390
  return this.#serializeRecordOperation(sessionId, async () => {
1293
1391
  const lease = this.#shared.writerLeases.get(sessionId);
1294
- if (!lease || !lease.executing || lease.owner !== owner)
1392
+ if (!lease || lease.maintenance || !lease.executing || lease.owner !== owner)
1295
1393
  throw new MarAgentError('MAR_AGENT_SESSION_LOCKED', 'Retained work requires its originating Session writer.');
1296
1394
  lease.retained++;
1297
1395
  let released = false;
@@ -1305,7 +1403,7 @@ export class JsonlSessionStore {
1305
1403
  });
1306
1404
  }
1307
1405
  async #releaseUnusedWriter(sessionId, lease) {
1308
- if (lease.executing || lease.retained)
1406
+ if (lease.maintenance || lease.executing || lease.retained)
1309
1407
  return;
1310
1408
  try {
1311
1409
  await lease.release();
@@ -1472,20 +1570,8 @@ export class JsonlSessionStore {
1472
1570
  else
1473
1571
  resume.reasoningEffort = executionModel.reasoningEffort;
1474
1572
  }
1475
- if (isCompactCheckpoint(record)) {
1573
+ if (isCompactCheckpoint(record))
1476
1574
  resume.compactSequence = record.sequence;
1477
- delete resume.gcSequence;
1478
- delete resume.gcGeneration;
1479
- delete resume.gcBaseSequence;
1480
- }
1481
- else {
1482
- const checkpoint = parseContextGcCheckpoint(record);
1483
- if (checkpoint) {
1484
- resume.gcSequence = record.sequence;
1485
- resume.gcGeneration = checkpoint.generation;
1486
- resume.gcBaseSequence = contextGcBaseSequence(checkpoint.base);
1487
- }
1488
- }
1489
1575
  if (record.type !== 'todo.updated' || !todoItemsFromSnapshot(record.payload))
1490
1576
  return;
1491
1577
  resume.todoSequence = record.sequence;
@@ -1723,22 +1809,12 @@ export class JsonlSessionStore {
1723
1809
  throw error;
1724
1810
  }
1725
1811
  }
1726
- async delete(sessionId) {
1812
+ async delete(sessionId, maintenance) {
1727
1813
  this.#assertOpen();
1728
- await this.#refreshIndex();
1729
- const tree = sessionTreeIds(this.#shared.index.sessions, sessionId);
1730
- if (tree.length === 0)
1731
- throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
1732
- const releases = [];
1733
- try {
1734
- for (const id of tree.toReversed())
1735
- if (await sessionDirectoryExists(join(this.#shared.root, 'sessions', id)))
1736
- releases.push(await this.acquireSessionLease(id));
1737
- }
1738
- catch (error) {
1739
- await Promise.allSettled(releases.toReversed().map((release) => release()));
1740
- throw error;
1741
- }
1814
+ const holding = maintenance ?? (await this.acquireSessionMaintenance(sessionId, 'delete'));
1815
+ this.#assertMaintenance(sessionId, 'delete', holding);
1816
+ const tree = holding.sessionIds;
1817
+ let failed = false;
1742
1818
  try {
1743
1819
  const deleted = await sessionDirectoryExists(join(this.#shared.root, 'sessions', sessionId));
1744
1820
  for (const id of tree.toReversed()) {
@@ -1751,8 +1827,18 @@ export class JsonlSessionStore {
1751
1827
  await this.#saveIndex(tree);
1752
1828
  return { deleted };
1753
1829
  }
1830
+ catch (error) {
1831
+ failed = true;
1832
+ throw error;
1833
+ }
1754
1834
  finally {
1755
- await Promise.allSettled(releases.toReversed().map((release) => release()));
1835
+ if (!maintenance) {
1836
+ const releasing = holding.release();
1837
+ if (failed)
1838
+ await releasing.catch(() => undefined);
1839
+ else
1840
+ await releasing;
1841
+ }
1756
1842
  }
1757
1843
  }
1758
1844
  #assertOpen() {
@@ -58,9 +58,6 @@ export interface SessionMeta {
58
58
  version: 1;
59
59
  userTurns?: number;
60
60
  compactSequence?: number;
61
- gcSequence?: number;
62
- gcGeneration?: number;
63
- gcBaseSequence?: number;
64
61
  todoSequence?: number;
65
62
  todoRevision: number;
66
63
  executionModelIndexed?: true;
package/dist/index.d.ts CHANGED
@@ -41,6 +41,8 @@ export type { CoreProcessesApi, CoreProcessesBackend, CoreProcessInput, CoreProc
41
41
  export type { CreateMarAgentOptions, CreateSessionInput, ExecutionHandle, CompactInput, SteerInput, SteerResult, MarAgent, MarAgentSession, SessionTitleUpdateInput } from './agent-core/sdk/types.js';
42
42
  export { openMarAgentSessionCatalog, type MarAgentContextUsage, type MarAgentHistoryEntry, type MarAgentHistoryPage, type MarAgentSessionCatalog, type MarAgentSessionDeleteResult, type MarAgentSessionImage, type MarAgentSessionPage, type MarAgentSessionSummary } from './agent-core/session/catalog.js';
43
43
  export type { SessionListInput, SessionSource } from './agent-core/session/types.js';
44
+ export type { SessionStore, SessionStoreMaintenance, SessionStoreSession, SessionRecordInput, SessionStoreCompactInput, SessionRecord, SessionExecutionState, CompactHistoryMessage, CompactUserInput, SessionInputImage, SessionInputImageMetadata, SessionInputImageData, RestoredSessionInputImage, CodeModeStateEntry, TodoSnapshot, TodoItem, CodeToolCallRecord, CodeObservation, ModelMessage, ModelUsage, ModelInputMetadata, ModelTextBlock, ModelContextualBlock, EnvironmentContextState, ContextGcCheckpointDraft, ContextGcBase, ContextGcPlatform, ContextGcEconomicsPolicy, ContextGcMetrics, ContextGcReplacement, ContextGcTarget, ContextGcReadItemReplacement, ContextGcWebToolName, ContextGcSubagentToolName, AvailableToolResultResource } from './agent-core/session/store/contracts.js';
45
+ export type { OpenMarAgentSessionCatalogInput } from './agent-core/session/catalog.js';
44
46
  export type { ApplyPatchFileResult, ApplyPatchResult, ApplyPatchPartialResult, ClientToolDefinition, MarAgentToolArtifact, MarAgentToolImageArtifact, McpCatalogTool, McpToolCallContext, McpToolCatalog, McpToolCatalogContext, ToolCall, ToolCaller, ToolExecutionContext, ToolExecutionResult } from './agent-core/tools/contracts.js';
45
47
  export { MCP_DEFINITION_LIMITS, assertMcpToolCatalog, assertMcpToolDefinition } from './agent-core/tools/mcp/catalog.js';
46
48
  export type { WorkspacePathMount } from './agent-core/tools/shared/path-resolver.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myagentroam/agent",
3
- "version": "0.11.13",
3
+ "version": "0.11.15",
4
4
  "description": "Embeddable MAR coding agent SDK and CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",