@myagentroam/agent 0.10.59 → 0.10.61

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 (58) hide show
  1. package/dist/agent-core/runtime/compaction/history.d.ts +1 -1
  2. package/dist/agent-core/runtime/compaction/retained-context.d.ts +1 -1
  3. package/dist/agent-core/runtime/compaction/retained-context.js +1 -1
  4. package/dist/agent-core/runtime/compaction/smart.d.ts +1 -1
  5. package/dist/agent-core/runtime/compaction/smart.js +1 -1
  6. package/dist/agent-core/runtime/context/gc-runtime.d.ts +1 -1
  7. package/dist/agent-core/runtime/context/gc-runtime.js +20 -14
  8. package/dist/agent-core/runtime/context/gc.d.ts +1 -1
  9. package/dist/agent-core/runtime/context/gc.js +4 -10
  10. package/dist/agent-core/runtime/context/projection.d.ts +1 -1
  11. package/dist/agent-core/runtime/context/projection.js +3 -5
  12. package/dist/agent-core/runtime/context/restore.d.ts +4 -4
  13. package/dist/agent-core/runtime/context/restore.js +1 -1
  14. package/dist/agent-core/runtime/context/usage.d.ts +2 -2
  15. package/dist/agent-core/runtime/context/usage.js +1 -1
  16. package/dist/agent-core/runtime/context/world-state.d.ts +2 -2
  17. package/dist/agent-core/runtime/context/world-state.js +1 -1
  18. package/dist/agent-core/sdk/agent.js +2 -2
  19. package/dist/agent-core/sdk/extension-agents.js +5 -1
  20. package/dist/agent-core/sdk/native-steer.d.ts +1 -1
  21. package/dist/agent-core/sdk/session.d.ts +19 -13
  22. package/dist/agent-core/sdk/session.js +35 -40
  23. package/dist/agent-core/sdk/subagent-backend.d.ts +1 -1
  24. package/dist/agent-core/sdk/subagent-backend.js +20 -17
  25. package/dist/agent-core/session/catalog.js +1 -1
  26. package/dist/agent-core/session/context-gc-checkpoint.d.ts +4 -8
  27. package/dist/agent-core/session/context-gc-checkpoint.js +82 -137
  28. package/dist/agent-core/session/context-gc-observations.d.ts +1 -1
  29. package/dist/agent-core/session/context-gc-replacements.d.ts +4 -7
  30. package/dist/agent-core/session/context-gc-replacements.js +25 -59
  31. package/dist/agent-core/session/context-gc-subagent.d.ts +0 -3
  32. package/dist/agent-core/session/context-gc-subagent.js +2 -16
  33. package/dist/agent-core/session/deferred-input.d.ts +1 -1
  34. package/dist/agent-core/session/store/current-state.d.ts +17 -0
  35. package/dist/agent-core/session/store/current-state.js +53 -0
  36. package/dist/agent-core/session/store/directory-index.d.ts +18 -0
  37. package/dist/agent-core/session/store/directory-index.js +169 -0
  38. package/dist/agent-core/session/store/execution-state.d.ts +18 -0
  39. package/dist/agent-core/session/store/execution-state.js +187 -0
  40. package/dist/agent-core/session/store/history.d.ts +25 -0
  41. package/dist/agent-core/session/store/history.js +157 -0
  42. package/dist/agent-core/session/store/images.d.ts +21 -0
  43. package/dist/agent-core/session/store/images.js +248 -0
  44. package/dist/agent-core/session/store/index.d.ts +4 -0
  45. package/dist/agent-core/session/store/index.js +2 -0
  46. package/dist/agent-core/session/{jsonl-store.d.ts → store/jsonl-store.d.ts} +14 -94
  47. package/dist/agent-core/session/{jsonl-store.js → store/jsonl-store.js} +323 -1269
  48. package/dist/agent-core/session/store/records.d.ts +23 -0
  49. package/dist/agent-core/session/store/records.js +249 -0
  50. package/dist/agent-core/session/store/types.d.ts +84 -0
  51. package/dist/agent-core/session/store/types.js +1 -0
  52. package/dist/agent-core/session/tool-result-resources.d.ts +1 -1
  53. package/dist/agent-core/subagent/scheduler.js +1 -1
  54. package/dist/agent-core/subagent/session-controller.d.ts +5 -2
  55. package/dist/agent-core/subagent/session-controller.js +99 -15
  56. package/dist/agent-core/tools/code-mode/state.d.ts +1 -1
  57. package/dist/agent-core/tools/code-mode/state.js +2 -2
  58. package/package.json +1 -1
@@ -0,0 +1,248 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { constants } from 'node:fs';
3
+ import { copyFile, lstat, mkdir, open, readdir, rename, rm, writeFile } from 'node:fs/promises';
4
+ import { basename, join } from 'node:path';
5
+ import { MarAgentError } from '../../error.js';
6
+ export const MAX_INPUT_IMAGES = 5;
7
+ const MAX_INPUT_IMAGE_BYTES = 5 * 1024 * 1024;
8
+ const MAX_INPUT_IMAGES_BYTES = 10 * 1024 * 1024;
9
+ const INPUT_IMAGE_MIME_TYPES = new Set(['image/png', 'image/jpeg', 'image/gif', 'image/webp']);
10
+ export function generatedImageFileNames(records) {
11
+ const names = new Set();
12
+ for (const record of records) {
13
+ if (record.type !== 'tool.completed' || !isObject(record.payload))
14
+ continue;
15
+ const artifacts = record.payload.artifacts;
16
+ if (!Array.isArray(artifacts))
17
+ continue;
18
+ for (const artifact of artifacts) {
19
+ if (!isObject(artifact) ||
20
+ artifact.kind !== 'image' ||
21
+ typeof artifact.fileName !== 'string' ||
22
+ basename(artifact.fileName) !== artifact.fileName ||
23
+ !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}\.png$/u.test(artifact.fileName))
24
+ continue;
25
+ names.add(artifact.fileName);
26
+ }
27
+ }
28
+ return [...names];
29
+ }
30
+ export async function copyGeneratedImages(sourceSessionDirectory, targetSessionDirectory, fileNames) {
31
+ if (fileNames.length === 0)
32
+ return;
33
+ const source = join(sourceSessionDirectory, 'generated_images');
34
+ const target = join(targetSessionDirectory, 'generated_images');
35
+ await mkdir(target, { recursive: true });
36
+ for (const fileName of fileNames)
37
+ await copyFile(join(source, fileName), join(target, fileName));
38
+ }
39
+ export function inputImageReferencesFromRecords(records) {
40
+ const references = new Map();
41
+ for (const record of records) {
42
+ if (record.type !== 'turn.user' || !isObject(record.payload))
43
+ continue;
44
+ for (const reference of inputImageReferences(record.payload.images)) {
45
+ const existing = references.get(reference.fileName);
46
+ if (existing !== undefined &&
47
+ (existing.mimeType !== reference.mimeType ||
48
+ existing.byteSize !== reference.byteSize ||
49
+ existing.sha256 !== reference.sha256))
50
+ throw corruptInputImageReference();
51
+ references.set(reference.fileName, reference);
52
+ }
53
+ }
54
+ return [...references.values()];
55
+ }
56
+ export function inputImageReferences(value) {
57
+ if (!Array.isArray(value))
58
+ return [];
59
+ const references = [];
60
+ let totalBytes = 0;
61
+ for (const image of value) {
62
+ const reference = inputImageReference(image);
63
+ if (reference === undefined)
64
+ continue;
65
+ totalBytes += reference.byteSize;
66
+ if (references.length >= MAX_INPUT_IMAGES || totalBytes > MAX_INPUT_IMAGES_BYTES)
67
+ throw corruptInputImageReference();
68
+ references.push(reference);
69
+ }
70
+ return references;
71
+ }
72
+ export function inputImageReference(value) {
73
+ if (!isObject(value) || !Object.hasOwn(value, 'fileName'))
74
+ return undefined;
75
+ if (typeof value.name !== 'string' ||
76
+ typeof value.mimeType !== 'string' ||
77
+ !INPUT_IMAGE_MIME_TYPES.has(value.mimeType) ||
78
+ typeof value.byteSize !== 'number' ||
79
+ !Number.isSafeInteger(value.byteSize) ||
80
+ value.byteSize <= 0 ||
81
+ value.byteSize > MAX_INPUT_IMAGE_BYTES ||
82
+ typeof value.fileName !== 'string' ||
83
+ !validInputImageFileName(value.fileName) ||
84
+ typeof value.sha256 !== 'string' ||
85
+ !/^[a-f0-9]{64}$/u.test(value.sha256))
86
+ throw corruptInputImageReference();
87
+ return {
88
+ name: value.name,
89
+ fileName: value.fileName,
90
+ mimeType: value.mimeType,
91
+ byteSize: value.byteSize,
92
+ sha256: value.sha256
93
+ };
94
+ }
95
+ export function validInputImageFileName(fileName) {
96
+ return (basename(fileName) === fileName && /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}\.bin$/u.test(fileName));
97
+ }
98
+ export async function readInputImageBytes(directory, reference) {
99
+ const path = join(directory, reference.fileName);
100
+ for (let attempt = 0; attempt < 2; attempt++) {
101
+ let handle;
102
+ try {
103
+ const before = await lstat(path);
104
+ if (!before.isFile() || before.size !== reference.byteSize)
105
+ throw corruptInputImageReference();
106
+ const flags = process.platform === 'win32'
107
+ ? constants.O_RDONLY
108
+ : constants.O_RDONLY | constants.O_NOFOLLOW;
109
+ handle = await open(path, flags);
110
+ const opened = await handle.stat();
111
+ if (!opened.isFile() ||
112
+ opened.size !== reference.byteSize ||
113
+ opened.dev !== before.dev ||
114
+ opened.ino !== before.ino)
115
+ continue;
116
+ const data = await readExactBytes(handle, reference.byteSize);
117
+ const after = await handle.stat();
118
+ if (after.size !== opened.size ||
119
+ after.mtimeMs !== opened.mtimeMs ||
120
+ after.ctimeMs !== opened.ctimeMs)
121
+ continue;
122
+ if (createHash('sha256').update(data).digest('hex') !== reference.sha256)
123
+ throw corruptInputImageReference();
124
+ return data;
125
+ }
126
+ catch (cause) {
127
+ if (cause instanceof MarAgentError)
128
+ throw cause;
129
+ throw corruptInputImageReference(cause);
130
+ }
131
+ finally {
132
+ await handle?.close();
133
+ }
134
+ }
135
+ throw corruptInputImageReference();
136
+ }
137
+ async function readExactBytes(handle, byteSize) {
138
+ const data = Buffer.alloc(byteSize);
139
+ let offset = 0;
140
+ while (offset < byteSize) {
141
+ const { bytesRead } = await handle.read(data, offset, byteSize - offset, offset);
142
+ if (bytesRead === 0)
143
+ throw corruptInputImageReference();
144
+ offset += bytesRead;
145
+ }
146
+ const extra = Buffer.allocUnsafe(1);
147
+ if ((await handle.read(extra, 0, 1, byteSize)).bytesRead !== 0)
148
+ throw corruptInputImageReference();
149
+ return data;
150
+ }
151
+ export async function copyInputImages(sourceSessionDirectory, targetSessionDirectory, references) {
152
+ if (references.length === 0)
153
+ return;
154
+ const source = join(sourceSessionDirectory, 'input_images');
155
+ const target = join(targetSessionDirectory, 'input_images');
156
+ await mkdir(target, { recursive: true, mode: 0o700 });
157
+ for (const reference of references) {
158
+ const data = await readInputImageBytes(source, reference);
159
+ await writeFile(join(target, reference.fileName), data, { mode: 0o600, flag: 'wx' });
160
+ }
161
+ }
162
+ function corruptInputImageReference(cause) {
163
+ return new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session input image is corrupt.', {
164
+ ...(cause === undefined ? {} : { cause })
165
+ });
166
+ }
167
+ /** A pending marker is removable only after the committed JSONL references are checked. */
168
+ export async function recoverPendingInputImages(sessionDirectory, readHistory) {
169
+ const directory = join(sessionDirectory, 'input_images');
170
+ let names;
171
+ try {
172
+ names = await readdir(directory);
173
+ }
174
+ catch (error) {
175
+ if (error.code === 'ENOENT')
176
+ return;
177
+ throw error;
178
+ }
179
+ const pending = names.filter((name) => /^\.[0-9a-f-]{36}\.bin\.pending$/iu.test(name));
180
+ const temporary = names.filter((name) => /^\.[0-9a-f-]{36}\.bin\.[0-9a-f-]{36}\.tmp$/iu.test(name));
181
+ if (pending.length === 0 && temporary.length === 0)
182
+ return;
183
+ const referenced = pending.length === 0
184
+ ? new Set()
185
+ : new Set(inputImageReferencesFromRecords(await readHistory()).map((image) => image.fileName));
186
+ for (const name of pending) {
187
+ const fileName = name.slice(1, -'.pending'.length);
188
+ if (!referenced.has(fileName))
189
+ await rm(join(directory, fileName), { force: true });
190
+ await rm(join(directory, name), { force: true });
191
+ }
192
+ for (const name of temporary)
193
+ await rm(join(directory, name), { force: true });
194
+ }
195
+ export async function writeInputImages(directory, images) {
196
+ await mkdir(directory, { recursive: true, mode: 0o700 });
197
+ const written = [];
198
+ const pending = [];
199
+ let temporary;
200
+ let totalBytes = 0;
201
+ try {
202
+ const metadata = [];
203
+ for (const image of images) {
204
+ if (!INPUT_IMAGE_MIME_TYPES.has(image.mimeType))
205
+ throw new MarAgentError('MAR_AGENT_INPUT_CAPABILITY_UNSUPPORTED', 'Image input MIME type is unsupported.');
206
+ const data = Buffer.from(image.dataBase64, 'base64');
207
+ totalBytes += data.byteLength;
208
+ if (data.byteLength === 0 ||
209
+ data.byteLength > MAX_INPUT_IMAGE_BYTES ||
210
+ totalBytes > MAX_INPUT_IMAGES_BYTES)
211
+ throw new MarAgentError('MAR_AGENT_HOST_LIMIT_EXCEEDED', 'Image input exceeds the supported size limit.');
212
+ const fileName = `${randomUUID()}.bin`;
213
+ temporary = join(directory, `.${fileName}.${randomUUID()}.tmp`);
214
+ const handle = await open(temporary, 'wx', 0o600);
215
+ try {
216
+ await handle.writeFile(data);
217
+ await handle.sync();
218
+ }
219
+ finally {
220
+ await handle.close();
221
+ }
222
+ const marker = join(directory, `.${fileName}.pending`);
223
+ await writeFile(marker, '', { mode: 0o600, flag: 'wx', flush: true });
224
+ pending.push(marker);
225
+ await rename(temporary, join(directory, fileName));
226
+ temporary = undefined;
227
+ written.push(fileName);
228
+ metadata.push({
229
+ name: image.name,
230
+ mimeType: image.mimeType,
231
+ byteSize: data.byteLength,
232
+ fileName,
233
+ sha256: createHash('sha256').update(data).digest('hex')
234
+ });
235
+ }
236
+ return metadata;
237
+ }
238
+ catch (error) {
239
+ if (temporary !== undefined)
240
+ await rm(temporary, { force: true }).catch(() => undefined);
241
+ await Promise.all(pending.map((marker) => rm(marker, { force: true })));
242
+ await Promise.all(written.map((fileName) => rm(join(directory, fileName), { force: true }).catch(() => undefined)));
243
+ throw error;
244
+ }
245
+ }
246
+ function isObject(value) {
247
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
248
+ }
@@ -0,0 +1,4 @@
1
+ export type { SessionListInput, SessionSource } from '../types.js';
2
+ export { compactSummaryFromCheckpoint, inputMetadataFromRecord, parseModelInputMetadata, replacementHistoryFromCheckpoint } from './execution-state.js';
3
+ export { JsonlSessionStore } from './jsonl-store.js';
4
+ export type { CompactHistoryMessage, CompactUserInput, RestoredSessionInputImage, SessionExecutionState, SessionInputImage, SessionInputImageData, SessionInputImageMetadata, SessionRecord } from './types.js';
@@ -0,0 +1,2 @@
1
+ export { compactSummaryFromCheckpoint, inputMetadataFromRecord, parseModelInputMetadata, replacementHistoryFromCheckpoint } from './execution-state.js';
2
+ export { JsonlSessionStore } from './jsonl-store.js';
@@ -1,23 +1,11 @@
1
- import { type ToolResultResourceInput } from './tool-result-resources.js';
2
- import { type CoreCheckpoint, type CoreJsonValue } from '../extensions/data.js';
3
- import type { ModelInputMetadata, ModelMessage, ModelUsage } from '../model/contracts.js';
4
- import type { EnvironmentContextState } from '../runtime/context/contracts.js';
5
- import { type CodeModeStateEntry } from '../tools/code-mode/state.js';
6
- import { type TodoSnapshot } from '../tools/agent/todo.js';
7
- import { type ContextGcCheckpointDraft } from './context-gc-checkpoint.js';
8
- import type { SessionListInput, SessionSource, SessionTitleUpdateInput } from './types.js';
9
- export type { SessionListInput, SessionSource } from './types.js';
10
- export interface SessionRecord {
11
- schemaVersion: 1;
12
- recordId: string;
13
- sequence: number;
14
- timestamp: string;
15
- sessionId: string;
16
- executionId?: string;
17
- turnId?: string;
18
- type: string;
19
- payload: unknown;
20
- }
1
+ 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
+ import { type CodeModeStateEntry } from '../../tools/code-mode/state.js';
5
+ import { type ContextGcCheckpointDraft } from '../context-gc-checkpoint.js';
6
+ import { type ToolResultResourceInput } from '../tool-result-resources.js';
7
+ import type { SessionListInput, SessionSource, SessionTitleUpdateInput } from '../types.js';
8
+ import type { CompactHistoryMessage, RestoredSessionInputImage, SessionExecutionState, SessionInputImage, SessionInputImageData, SessionInputImageMetadata, SessionMeta, SessionRecord } from './types.js';
21
9
  interface SessionRecordInput {
22
10
  type: string;
23
11
  payload: unknown;
@@ -25,70 +13,6 @@ interface SessionRecordInput {
25
13
  turnId?: string;
26
14
  timestamp?: string;
27
15
  }
28
- export interface SessionExecutionState {
29
- automaticTitlePending: boolean;
30
- todo: TodoSnapshot;
31
- contextRecords: SessionRecord[];
32
- }
33
- export interface CompactUserInput {
34
- content: string;
35
- metadata?: ModelInputMetadata;
36
- }
37
- export interface CompactHistoryMessage extends CompactUserInput {
38
- role: 'user';
39
- kind: 'user_input' | 'extension_input' | 'summary';
40
- }
41
- export interface SessionInputImage {
42
- readonly name: string;
43
- readonly mimeType: string;
44
- readonly dataBase64: string;
45
- }
46
- export interface SessionInputImageMetadata {
47
- readonly name: string;
48
- readonly mimeType: string;
49
- readonly byteSize: number;
50
- readonly fileName: string;
51
- readonly sha256: string;
52
- }
53
- export interface RestoredSessionInputImage {
54
- readonly mimeType: string;
55
- readonly dataBase64: string;
56
- }
57
- export interface SessionInputImageData extends SessionInputImageMetadata {
58
- readonly dataBase64: string;
59
- }
60
- interface SessionMeta {
61
- id: string;
62
- title: string;
63
- workspace: string;
64
- createdAt: string;
65
- lastActivityAt: string;
66
- source?: SessionSource;
67
- nextSequence: number;
68
- automaticTitlePending?: boolean;
69
- storageTransactionId?: string;
70
- resume?: {
71
- version: 1;
72
- compactSequence?: number;
73
- gcSequence?: number;
74
- gcGeneration?: number;
75
- gcBaseSequence?: number;
76
- todoSequence?: number;
77
- todoRevision: number;
78
- executionModelIndexed?: true;
79
- firstUserMessageIndexed?: true;
80
- firstUserMessagePreview?: string;
81
- modelId?: string;
82
- modelName?: string;
83
- reasoningEffort?: string;
84
- };
85
- segments?: Array<{
86
- number: number;
87
- firstSequence: number;
88
- lastSequence: number;
89
- bytes: number;
90
- }>;
91
- }
92
16
  /** SDK-owned JSONL history with per-HOME in-process sharing and cross-process file locks. */
93
17
  export declare class JsonlSessionStore {
94
18
  #private;
@@ -100,7 +24,6 @@ export declare class JsonlSessionStore {
100
24
  workspace: string;
101
25
  source?: SessionSource;
102
26
  }): Promise<SessionMeta>;
103
- sessionDirectory(sessionId: string): string;
104
27
  readContextualWorldState(sessionId: string): Promise<unknown | undefined>;
105
28
  writeContextualWorldState(sessionId: string, state: unknown): Promise<void>;
106
29
  /** Mark each image pending before publication; turn.user or lease recovery resolves ownership. */
@@ -112,7 +35,7 @@ export declare class JsonlSessionStore {
112
35
  byteSize: number;
113
36
  sha256: string;
114
37
  }>;
115
- readToolResultResource(sessionId: string, input: ToolResultResourceInput): Promise<import("./tool-result-resources.js").ToolResultResourcePage>;
38
+ readToolResultResource(sessionId: string, input: ToolResultResourceInput): Promise<import("../tool-result-resources.js").ToolResultResourcePage>;
116
39
  readInputImages(sessionId: string, value: unknown): Promise<RestoredSessionInputImage[]>;
117
40
  readInputImage(sessionId: string, input: {
118
41
  readonly sequence: number;
@@ -178,10 +101,8 @@ export declare class JsonlSessionStore {
178
101
  readonly expectedState?: readonly CodeModeStateEntry[];
179
102
  }): Promise<CodeModeStateEntry[]>;
180
103
  readExecutionState(sessionId: string): Promise<SessionExecutionState>;
181
- readContext(sessionId: string): Promise<SessionRecord[]>;
182
- contextGcProjectionHash(sessionId: string, boundarySequence: number): Promise<string>;
183
- /** Recheck a planned GC checkpoint against authoritative records before appending it. */
184
- commitContextGc(sessionId: string, draft: ContextGcCheckpointDraft): Promise<SessionRecord>;
104
+ /** Compare the planned sequence inside the existing record writer queue. */
105
+ commitContextGc(sessionId: string, draft: ContextGcCheckpointDraft, planningRecords?: readonly SessionRecord[]): Promise<SessionRecord>;
185
106
  readPage(sessionId: string, input?: {
186
107
  limit?: number;
187
108
  beforeSequence?: number;
@@ -254,13 +175,12 @@ export declare class JsonlSessionStore {
254
175
  readonly reasoningEffort?: string;
255
176
  } | undefined>;
256
177
  sessionTree(sessionId: string): Promise<readonly string[]>;
178
+ /** Counts native input records, not the post-Compact model context. */
179
+ userTurnCount(sessionId: string): Promise<number>;
257
180
  firstUserMessagePreview(sessionId: string): Promise<string | undefined>;
258
181
  delete(sessionId: string): Promise<{
259
182
  readonly deleted: boolean;
260
183
  }>;
261
184
  close(): Promise<void>;
262
185
  }
263
- export declare function compactSummaryFromCheckpoint(record: SessionRecord | undefined): string | undefined;
264
- export declare function replacementHistoryFromCheckpoint(record: SessionRecord): CompactHistoryMessage[] | undefined;
265
- export declare function parseModelInputMetadata(value: unknown): ModelInputMetadata | undefined;
266
- export declare function inputMetadataFromRecord(record: SessionRecord): ModelInputMetadata;
186
+ export {};