@myagentroam/agent 0.9.132 → 0.9.134
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/model/catalog-file.d.ts +2 -0
- package/dist/model/configuration.d.ts +1 -0
- package/dist/model/configuration.js +11 -0
- package/dist/model/openai-responses.js +20 -2
- package/dist/session/catalog.d.ts +8 -0
- package/dist/session/catalog.js +1 -0
- package/dist/session/jsonl-store.d.ts +15 -0
- package/dist/session/jsonl-store.js +182 -30
- package/package.json +1 -1
|
@@ -31,6 +31,7 @@ export declare function serializeModel(model: CliModelConfiguration): {
|
|
|
31
31
|
fastMode?: boolean | undefined;
|
|
32
32
|
hostedWebSearch: boolean;
|
|
33
33
|
responsesEncoding?: "STANDARD" | "LITE" | undefined;
|
|
34
|
+
responsesToolNamespace?: string | undefined;
|
|
34
35
|
enabled: boolean;
|
|
35
36
|
} | {
|
|
36
37
|
credential: {
|
|
@@ -56,5 +57,6 @@ export declare function serializeModel(model: CliModelConfiguration): {
|
|
|
56
57
|
fastMode?: boolean | undefined;
|
|
57
58
|
hostedWebSearch: boolean;
|
|
58
59
|
responsesEncoding?: "STANDARD" | "LITE" | undefined;
|
|
60
|
+
responsesToolNamespace?: string | undefined;
|
|
59
61
|
enabled: boolean;
|
|
60
62
|
};
|
|
@@ -85,6 +85,7 @@ declare const rawModelSchema: z.ZodObject<{
|
|
|
85
85
|
STANDARD: "STANDARD";
|
|
86
86
|
LITE: "LITE";
|
|
87
87
|
}>>;
|
|
88
|
+
responsesToolNamespace: z.ZodOptional<z.ZodString>;
|
|
88
89
|
responsesPreviousResponseId: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
89
90
|
enabled: z.ZodBoolean;
|
|
90
91
|
}, z.core.$strict>;
|
|
@@ -54,6 +54,10 @@ const rawModelSchema = z
|
|
|
54
54
|
hostedWebSearch: z.boolean(),
|
|
55
55
|
responsesTransport: rawResponsesTransportSchema.optional(),
|
|
56
56
|
responsesEncoding: z.enum(['STANDARD', 'LITE']).optional(),
|
|
57
|
+
responsesToolNamespace: z
|
|
58
|
+
.string()
|
|
59
|
+
.regex(/^[a-zA-Z][a-zA-Z0-9_]{0,63}$/u)
|
|
60
|
+
.optional(),
|
|
57
61
|
responsesPreviousResponseId: z.boolean().optional().default(false),
|
|
58
62
|
enabled: z.boolean()
|
|
59
63
|
})
|
|
@@ -90,6 +94,13 @@ const rawModelSchema = z
|
|
|
90
94
|
context.addIssue({ code: 'custom', message: 'Responses transport requires Responses.' });
|
|
91
95
|
if (value.protocol !== 'OPENAI_RESPONSES' && value.responsesEncoding !== undefined)
|
|
92
96
|
context.addIssue({ code: 'custom', message: 'Responses encoding requires Responses.' });
|
|
97
|
+
if (value.responsesToolNamespace !== undefined &&
|
|
98
|
+
(value.protocol !== 'OPENAI_RESPONSES' || value.responsesEncoding === 'LITE'))
|
|
99
|
+
context.addIssue({
|
|
100
|
+
code: 'custom',
|
|
101
|
+
path: ['responsesToolNamespace'],
|
|
102
|
+
message: 'Tool namespaces require Standard OpenAI Responses.'
|
|
103
|
+
});
|
|
93
104
|
if (value.protocol !== 'OPENAI_RESPONSES' && value.fastMode === true)
|
|
94
105
|
context.addIssue({ code: 'custom', message: 'Fast mode requires Responses.' });
|
|
95
106
|
});
|
|
@@ -513,7 +513,7 @@ export class OpenAiResponsesAdapter {
|
|
|
513
513
|
const status = upstreamHttpStatus(event.status, topLevel.status, record(event.response).status, nested.status);
|
|
514
514
|
const connectionLimit = this.configuration.responsesTransport?.transport === 'WEBSOCKET' &&
|
|
515
515
|
isWebSocketConnectionLimitCode(code);
|
|
516
|
-
const retryable = connectionLimit ||
|
|
516
|
+
const retryable = connectionLimit || isRetryableResponsesUpstreamError(code, message);
|
|
517
517
|
throw new MarAgentError(code ?? 'MAR_AGENT_MODEL_PROTOCOL_ERROR', message ?? 'OpenAI Responses request failed.', {
|
|
518
518
|
details: {
|
|
519
519
|
...(status === undefined ? {} : { status }),
|
|
@@ -852,6 +852,15 @@ function responsesRequestBody(configuration, request, messages, prefixIdentity =
|
|
|
852
852
|
parameters: strictFunctionSchema(tool.inputSchema),
|
|
853
853
|
strict: true
|
|
854
854
|
});
|
|
855
|
+
if (configuration.responsesToolNamespace && tools.length > 0) {
|
|
856
|
+
const nested = tools.splice(0);
|
|
857
|
+
tools.push({
|
|
858
|
+
type: 'namespace',
|
|
859
|
+
name: configuration.responsesToolNamespace,
|
|
860
|
+
description: 'Client-side agent tools.',
|
|
861
|
+
tools: nested
|
|
862
|
+
});
|
|
863
|
+
}
|
|
855
864
|
if (request.allowTools !== false && configuration.hostedWebSearch)
|
|
856
865
|
tools.push({ type: 'web_search' });
|
|
857
866
|
const toolChoice = request.toolChoice ?? 'auto';
|
|
@@ -1306,12 +1315,21 @@ const NON_SEMANTIC_OPENAI_RESPONSES_EVENTS = new Set([
|
|
|
1306
1315
|
'response.reasoning_summary_text.delta',
|
|
1307
1316
|
'responsesapi.websocket_timing'
|
|
1308
1317
|
]);
|
|
1318
|
+
function isRetryableResponsesUpstreamError(code, message) {
|
|
1319
|
+
if (isRetryableUpstreamOverloadCode(code))
|
|
1320
|
+
return true;
|
|
1321
|
+
if (code !== 'invalid_request_error')
|
|
1322
|
+
return false;
|
|
1323
|
+
// Exact compatibility exception, not a general access/authentication retry rule.
|
|
1324
|
+
const summary = message?.startsWith(`${code}: `) ? message.slice(code.length + 2) : message;
|
|
1325
|
+
return summary === 'Unable to verify Daybreak Blue access. Please try again.';
|
|
1326
|
+
}
|
|
1309
1327
|
export function isOpenAiResponsesRecoverableStreamError(error) {
|
|
1310
1328
|
return (error instanceof MarAgentError &&
|
|
1311
1329
|
error.retryable &&
|
|
1312
1330
|
(error.details?.reason === OPENAI_RESPONSES_EARLY_EOF_REASON ||
|
|
1313
1331
|
error.details?.reason === OPENAI_RESPONSES_WEBSOCKET_CONNECTION_LIMIT_REASON ||
|
|
1314
|
-
|
|
1332
|
+
isRetryableResponsesUpstreamError(error.code, error.message)));
|
|
1315
1333
|
}
|
|
1316
1334
|
function isWebSocketConnectionLimitCode(code) {
|
|
1317
1335
|
return code?.trim().toLowerCase() === 'websocket_connection_limit_reached';
|
|
@@ -7,6 +7,9 @@ export interface MarAgentSessionSummary {
|
|
|
7
7
|
createdAt: string;
|
|
8
8
|
lastActivityAt: string;
|
|
9
9
|
source: SessionSource;
|
|
10
|
+
modelId?: string;
|
|
11
|
+
modelName?: string;
|
|
12
|
+
reasoningEffort?: string;
|
|
10
13
|
}
|
|
11
14
|
export interface MarAgentSessionPage {
|
|
12
15
|
sessions: MarAgentSessionSummary[];
|
|
@@ -50,6 +53,11 @@ export interface MarAgentSessionImage {
|
|
|
50
53
|
/** Public session catalog facade. Storage details remain SDK-private. */
|
|
51
54
|
export interface MarAgentSessionCatalog {
|
|
52
55
|
list(input?: SessionListInput): Promise<MarAgentSessionPage>;
|
|
56
|
+
executionConfiguration(sessionId: string): Promise<{
|
|
57
|
+
readonly modelId: string;
|
|
58
|
+
readonly modelName?: string;
|
|
59
|
+
readonly reasoningEffort?: string;
|
|
60
|
+
} | undefined>;
|
|
53
61
|
history(sessionId: string, input?: {
|
|
54
62
|
limit?: number;
|
|
55
63
|
cursor?: string;
|
package/dist/session/catalog.js
CHANGED
|
@@ -6,6 +6,7 @@ export async function openMarAgentSessionCatalog(input) {
|
|
|
6
6
|
const store = await JsonlSessionStore.open(input.homeDirectory);
|
|
7
7
|
return {
|
|
8
8
|
list: (options) => store.list(options),
|
|
9
|
+
executionConfiguration: (sessionId) => store.executionConfiguration(sessionId),
|
|
9
10
|
history: async (sessionId, options = {}) => {
|
|
10
11
|
const beforeSequence = parseCursor(options.cursor);
|
|
11
12
|
const page = await store.readPage(sessionId, {
|
|
@@ -62,6 +62,10 @@ interface SessionMeta {
|
|
|
62
62
|
gcBaseSequence?: number;
|
|
63
63
|
todoSequence?: number;
|
|
64
64
|
todoRevision: number;
|
|
65
|
+
executionModelIndexed?: true;
|
|
66
|
+
modelId?: string;
|
|
67
|
+
modelName?: string;
|
|
68
|
+
reasoningEffort?: string;
|
|
65
69
|
};
|
|
66
70
|
segments?: Array<{
|
|
67
71
|
number: number;
|
|
@@ -170,6 +174,9 @@ export declare class JsonlSessionStore {
|
|
|
170
174
|
list(input?: SessionListInput): Promise<{
|
|
171
175
|
nextCursor?: string;
|
|
172
176
|
sessions: {
|
|
177
|
+
reasoningEffort?: string;
|
|
178
|
+
modelName?: string;
|
|
179
|
+
modelId?: string;
|
|
173
180
|
id: string;
|
|
174
181
|
title: string;
|
|
175
182
|
workspace: string;
|
|
@@ -179,6 +186,9 @@ export declare class JsonlSessionStore {
|
|
|
179
186
|
}[];
|
|
180
187
|
}>;
|
|
181
188
|
listSubagentChildren(parentSessionId: string): Promise<{
|
|
189
|
+
reasoningEffort?: string;
|
|
190
|
+
modelName?: string;
|
|
191
|
+
modelId?: string;
|
|
182
192
|
id: string;
|
|
183
193
|
title: string;
|
|
184
194
|
workspace: string;
|
|
@@ -193,6 +203,11 @@ export declare class JsonlSessionStore {
|
|
|
193
203
|
readonly description: string;
|
|
194
204
|
};
|
|
195
205
|
}[]>;
|
|
206
|
+
executionConfiguration(sessionId: string): Promise<{
|
|
207
|
+
readonly modelId: string;
|
|
208
|
+
readonly modelName?: string;
|
|
209
|
+
readonly reasoningEffort?: string;
|
|
210
|
+
} | undefined>;
|
|
196
211
|
sessionTree(sessionId: string): Promise<readonly string[]>;
|
|
197
212
|
delete(sessionId: string): Promise<{
|
|
198
213
|
readonly deleted: boolean;
|
|
@@ -156,7 +156,8 @@ export class JsonlSessionStore {
|
|
|
156
156
|
indexDirty: false,
|
|
157
157
|
dirtySessionVersions: new Map(),
|
|
158
158
|
pendingPublishIds: new Set(),
|
|
159
|
-
segments: new Map()
|
|
159
|
+
segments: new Map(),
|
|
160
|
+
executionModelReads: new Map()
|
|
160
161
|
};
|
|
161
162
|
}
|
|
162
163
|
/** Merge dirty metadata with disk membership; only createdIds publish locally new entries. */
|
|
@@ -264,7 +265,7 @@ export class JsonlSessionStore {
|
|
|
264
265
|
source: normalizeSessionSource(input.source),
|
|
265
266
|
nextSequence: 1,
|
|
266
267
|
automaticTitlePending: input.title === undefined,
|
|
267
|
-
resume: { version: 1, todoRevision: 0 },
|
|
268
|
+
resume: { version: 1, todoRevision: 0, executionModelIndexed: true },
|
|
268
269
|
segments: []
|
|
269
270
|
};
|
|
270
271
|
const publication = await this.#beginUnpublishedSession(id);
|
|
@@ -512,7 +513,7 @@ export class JsonlSessionStore {
|
|
|
512
513
|
source: { type: 'user' },
|
|
513
514
|
nextSequence: 1,
|
|
514
515
|
automaticTitlePending: false,
|
|
515
|
-
resume: { version: 1, todoRevision: 0 },
|
|
516
|
+
resume: { version: 1, todoRevision: 0, executionModelIndexed: true },
|
|
516
517
|
segments: []
|
|
517
518
|
};
|
|
518
519
|
const publication = await this.#beginUnpublishedSession(target.id);
|
|
@@ -1166,7 +1167,18 @@ export class JsonlSessionStore {
|
|
|
1166
1167
|
workspace: session.workspace,
|
|
1167
1168
|
createdAt: session.createdAt,
|
|
1168
1169
|
lastActivityAt: session.lastActivityAt,
|
|
1169
|
-
source: normalizeSessionSource(session.source)
|
|
1170
|
+
source: normalizeSessionSource(session.source),
|
|
1171
|
+
...(session.resume?.modelId === undefined
|
|
1172
|
+
? {}
|
|
1173
|
+
: {
|
|
1174
|
+
modelId: session.resume.modelId,
|
|
1175
|
+
...(session.resume.modelName === undefined
|
|
1176
|
+
? {}
|
|
1177
|
+
: { modelName: session.resume.modelName }),
|
|
1178
|
+
...(session.resume.reasoningEffort === undefined
|
|
1179
|
+
? {}
|
|
1180
|
+
: { reasoningEffort: session.resume.reasoningEffort })
|
|
1181
|
+
})
|
|
1170
1182
|
}));
|
|
1171
1183
|
const next = start + limit < ordered.length
|
|
1172
1184
|
? encodeSessionListCursor(this.#shared.index.revision, start + limit, filter)
|
|
@@ -1185,9 +1197,42 @@ export class JsonlSessionStore {
|
|
|
1185
1197
|
workspace: session.workspace,
|
|
1186
1198
|
createdAt: session.createdAt,
|
|
1187
1199
|
lastActivityAt: session.lastActivityAt,
|
|
1188
|
-
source
|
|
1200
|
+
source,
|
|
1201
|
+
...(session.resume?.modelId === undefined
|
|
1202
|
+
? {}
|
|
1203
|
+
: {
|
|
1204
|
+
modelId: session.resume.modelId,
|
|
1205
|
+
...(session.resume.modelName === undefined
|
|
1206
|
+
? {}
|
|
1207
|
+
: { modelName: session.resume.modelName }),
|
|
1208
|
+
...(session.resume.reasoningEffort === undefined
|
|
1209
|
+
? {}
|
|
1210
|
+
: { reasoningEffort: session.resume.reasoningEffort })
|
|
1211
|
+
})
|
|
1189
1212
|
}));
|
|
1190
1213
|
}
|
|
1214
|
+
async executionConfiguration(sessionId) {
|
|
1215
|
+
this.#assertOpen();
|
|
1216
|
+
await this.#refreshIndex();
|
|
1217
|
+
let meta = this.#shared.index.sessions.find((session) => session.id === sessionId);
|
|
1218
|
+
if (meta === undefined)
|
|
1219
|
+
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1220
|
+
if (meta.resume?.executionModelIndexed !== true) {
|
|
1221
|
+
await this.#ensureExecutionModelIndexed(sessionId);
|
|
1222
|
+
meta = this.#shared.index.sessions.find((session) => session.id === sessionId);
|
|
1223
|
+
if (meta === undefined)
|
|
1224
|
+
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1225
|
+
}
|
|
1226
|
+
return meta.resume?.modelId === undefined
|
|
1227
|
+
? undefined
|
|
1228
|
+
: {
|
|
1229
|
+
modelId: meta.resume.modelId,
|
|
1230
|
+
...(meta.resume.modelName === undefined ? {} : { modelName: meta.resume.modelName }),
|
|
1231
|
+
...(meta.resume.reasoningEffort === undefined
|
|
1232
|
+
? {}
|
|
1233
|
+
: { reasoningEffort: meta.resume.reasoningEffort })
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1191
1236
|
async sessionTree(sessionId) {
|
|
1192
1237
|
this.#assertOpen();
|
|
1193
1238
|
await this.#refreshIndex();
|
|
@@ -1201,6 +1246,19 @@ export class JsonlSessionStore {
|
|
|
1201
1246
|
const resume = meta.resume;
|
|
1202
1247
|
if (resume?.version !== 1 || !Number.isSafeInteger(resume.todoRevision))
|
|
1203
1248
|
return;
|
|
1249
|
+
const executionModel = executionModelFromRecord(record);
|
|
1250
|
+
if (executionModel !== undefined) {
|
|
1251
|
+
resume.executionModelIndexed = true;
|
|
1252
|
+
resume.modelId = executionModel.modelId;
|
|
1253
|
+
if (executionModel.modelName === undefined)
|
|
1254
|
+
delete resume.modelName;
|
|
1255
|
+
else
|
|
1256
|
+
resume.modelName = executionModel.modelName;
|
|
1257
|
+
if (executionModel.reasoningEffort === undefined)
|
|
1258
|
+
delete resume.reasoningEffort;
|
|
1259
|
+
else
|
|
1260
|
+
resume.reasoningEffort = executionModel.reasoningEffort;
|
|
1261
|
+
}
|
|
1204
1262
|
if (isCompactCheckpoint(record)) {
|
|
1205
1263
|
resume.compactSequence = record.sequence;
|
|
1206
1264
|
delete resume.gcSequence;
|
|
@@ -1273,6 +1331,68 @@ export class JsonlSessionStore {
|
|
|
1273
1331
|
contextRecords: state.contextRecords
|
|
1274
1332
|
};
|
|
1275
1333
|
}
|
|
1334
|
+
async #ensureExecutionModelIndexed(sessionId) {
|
|
1335
|
+
const pending = this.#shared.executionModelReads.get(sessionId);
|
|
1336
|
+
if (pending !== undefined)
|
|
1337
|
+
return pending;
|
|
1338
|
+
const read = this.#backfillExecutionModel(sessionId).finally(() => {
|
|
1339
|
+
if (this.#shared.executionModelReads.get(sessionId) === read)
|
|
1340
|
+
this.#shared.executionModelReads.delete(sessionId);
|
|
1341
|
+
});
|
|
1342
|
+
this.#shared.executionModelReads.set(sessionId, read);
|
|
1343
|
+
return read;
|
|
1344
|
+
}
|
|
1345
|
+
async #backfillExecutionModel(sessionId) {
|
|
1346
|
+
for (;;) {
|
|
1347
|
+
const before = this.#shared.index.sessions.find((session) => session.id === sessionId);
|
|
1348
|
+
if (before === undefined)
|
|
1349
|
+
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1350
|
+
if (before.resume?.executionModelIndexed === true)
|
|
1351
|
+
return;
|
|
1352
|
+
const nextSequence = before.nextSequence;
|
|
1353
|
+
const transactionId = before.storageTransactionId;
|
|
1354
|
+
const model = await this.#readLatestExecutionModel(sessionId);
|
|
1355
|
+
// Refresh can replace metadata objects while unrelated sessions are being read.
|
|
1356
|
+
// Merge only into the current entry, and retry if the authoritative history changed.
|
|
1357
|
+
await this.#refreshIndex();
|
|
1358
|
+
const current = this.#shared.index.sessions.find((session) => session.id === sessionId);
|
|
1359
|
+
if (current === undefined)
|
|
1360
|
+
throw new MarAgentError('MAR_AGENT_SESSION_NOT_FOUND', 'Session was not found.');
|
|
1361
|
+
if (current.resume?.executionModelIndexed === true)
|
|
1362
|
+
return;
|
|
1363
|
+
if (current.nextSequence !== nextSequence || current.storageTransactionId !== transactionId)
|
|
1364
|
+
continue;
|
|
1365
|
+
// A missing resume index stays incomplete (no compact boundary); execution
|
|
1366
|
+
// recovery will rebuild it separately instead of doing so during discovery.
|
|
1367
|
+
const resume = (current.resume ??= { version: 1, todoRevision: 0 });
|
|
1368
|
+
resume.executionModelIndexed = true;
|
|
1369
|
+
delete resume.modelId;
|
|
1370
|
+
delete resume.modelName;
|
|
1371
|
+
delete resume.reasoningEffort;
|
|
1372
|
+
if (model !== undefined)
|
|
1373
|
+
Object.assign(resume, model);
|
|
1374
|
+
this.#shared.index.revision++;
|
|
1375
|
+
this.#markIndexDirty(sessionId);
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
async #readLatestExecutionModel(sessionId) {
|
|
1380
|
+
try {
|
|
1381
|
+
for (const path of (await this.#segmentPaths(sessionId)).reverse())
|
|
1382
|
+
for await (const record of readRecordsFromTail(path)) {
|
|
1383
|
+
const model = executionModelFromRecord(record);
|
|
1384
|
+
if (model !== undefined)
|
|
1385
|
+
return model;
|
|
1386
|
+
}
|
|
1387
|
+
return undefined;
|
|
1388
|
+
}
|
|
1389
|
+
catch (error) {
|
|
1390
|
+
if (error.code === 'ENOENT' ||
|
|
1391
|
+
(error instanceof MarAgentError && error.code === 'MAR_AGENT_SESSION_NOT_FOUND'))
|
|
1392
|
+
return undefined;
|
|
1393
|
+
throw error;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1276
1396
|
async delete(sessionId) {
|
|
1277
1397
|
this.#assertOpen();
|
|
1278
1398
|
await this.#refreshIndex();
|
|
@@ -1878,6 +1998,7 @@ function deriveExecutionState(records) {
|
|
|
1878
1998
|
let todoSequence;
|
|
1879
1999
|
let todoRevision = 0;
|
|
1880
2000
|
let todoItems = [];
|
|
2001
|
+
let executionModel;
|
|
1881
2002
|
for (const record of records) {
|
|
1882
2003
|
if (record.type === 'session.created')
|
|
1883
2004
|
automaticTitle =
|
|
@@ -1894,6 +2015,9 @@ function deriveExecutionState(records) {
|
|
|
1894
2015
|
todoRevision++;
|
|
1895
2016
|
todoItems = items;
|
|
1896
2017
|
}
|
|
2018
|
+
const candidate = executionModelFromRecord(record);
|
|
2019
|
+
if (candidate !== undefined)
|
|
2020
|
+
executionModel = candidate;
|
|
1897
2021
|
}
|
|
1898
2022
|
const boundary = records.findLastIndex(isCompactCheckpoint);
|
|
1899
2023
|
const checkpoint = latestValidContextGcCheckpoint(records);
|
|
@@ -1912,10 +2036,28 @@ function deriveExecutionState(records) {
|
|
|
1912
2036
|
gcBaseSequence: contextGcBaseSequence(checkpoint.payload.base)
|
|
1913
2037
|
}),
|
|
1914
2038
|
...(todoSequence === undefined ? {} : { todoSequence }),
|
|
1915
|
-
todoRevision
|
|
2039
|
+
todoRevision,
|
|
2040
|
+
executionModelIndexed: true,
|
|
2041
|
+
...(executionModel === undefined ? {} : executionModel)
|
|
1916
2042
|
}
|
|
1917
2043
|
};
|
|
1918
2044
|
}
|
|
2045
|
+
function executionModelFromRecord(record) {
|
|
2046
|
+
if (record.type !== 'execution.header' || !isObject(record.payload))
|
|
2047
|
+
return undefined;
|
|
2048
|
+
const modelId = record.payload.modelId;
|
|
2049
|
+
if (typeof modelId !== 'string' || modelId.trim().length === 0)
|
|
2050
|
+
return undefined;
|
|
2051
|
+
const modelName = record.payload.modelName;
|
|
2052
|
+
const reasoningEffort = record.payload.reasoningEffort;
|
|
2053
|
+
return {
|
|
2054
|
+
modelId,
|
|
2055
|
+
...(typeof modelName === 'string' && modelName.trim().length > 0 ? { modelName } : {}),
|
|
2056
|
+
...(typeof reasoningEffort === 'string' && reasoningEffort.trim().length > 0
|
|
2057
|
+
? { reasoningEffort }
|
|
2058
|
+
: {})
|
|
2059
|
+
};
|
|
2060
|
+
}
|
|
1919
2061
|
export function compactSummaryFromCheckpoint(record) {
|
|
1920
2062
|
if (record?.type !== 'compact.completed')
|
|
1921
2063
|
return undefined;
|
|
@@ -1950,7 +2092,11 @@ function sameResumeIndex(left, right) {
|
|
|
1950
2092
|
left.gcGeneration === right.gcGeneration &&
|
|
1951
2093
|
left.gcBaseSequence === right.gcBaseSequence &&
|
|
1952
2094
|
left.todoSequence === right.todoSequence &&
|
|
1953
|
-
left.todoRevision === right.todoRevision
|
|
2095
|
+
left.todoRevision === right.todoRevision &&
|
|
2096
|
+
left.executionModelIndexed === right.executionModelIndexed &&
|
|
2097
|
+
left.modelId === right.modelId &&
|
|
2098
|
+
left.modelName === right.modelName &&
|
|
2099
|
+
left.reasoningEffort === right.reasoningEffort);
|
|
1954
2100
|
}
|
|
1955
2101
|
async function readRecords(path) {
|
|
1956
2102
|
const value = await readFile(path, 'utf8');
|
|
@@ -2036,6 +2182,21 @@ async function repairSegmentTail(path) {
|
|
|
2036
2182
|
}
|
|
2037
2183
|
}
|
|
2038
2184
|
async function readRecordPageFromTail(path, beforeSequence, limit) {
|
|
2185
|
+
const newestFirst = [];
|
|
2186
|
+
for await (const record of readRecordsFromTail(path)) {
|
|
2187
|
+
if (!(record.sequence < beforeSequence))
|
|
2188
|
+
continue;
|
|
2189
|
+
newestFirst.push(record);
|
|
2190
|
+
if (newestFirst.length > limit)
|
|
2191
|
+
break;
|
|
2192
|
+
}
|
|
2193
|
+
return {
|
|
2194
|
+
records: newestFirst.slice(0, limit).reverse(),
|
|
2195
|
+
hasEarlier: newestFirst.length > limit
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
/** Decode complete records newest-first, releasing the file as soon as a reader stops. */
|
|
2199
|
+
async function* readRecordsFromTail(path) {
|
|
2039
2200
|
const handle = await open(path, 'r');
|
|
2040
2201
|
try {
|
|
2041
2202
|
const size = (await handle.stat()).size;
|
|
@@ -2043,26 +2204,21 @@ async function readRecordPageFromTail(path, beforeSequence, limit) {
|
|
|
2043
2204
|
let pendingLineParts = [];
|
|
2044
2205
|
let invalidFinalLineMayBeIgnored = false;
|
|
2045
2206
|
let inspectedFileEnd = false;
|
|
2046
|
-
|
|
2047
|
-
const newestFirst = [];
|
|
2048
|
-
const accept = (line, allowInvalidFinalLine) => {
|
|
2207
|
+
const decode = (line, allowInvalidFinalLine) => {
|
|
2049
2208
|
if (line.length === 0)
|
|
2050
|
-
return;
|
|
2051
|
-
let record;
|
|
2209
|
+
return undefined;
|
|
2052
2210
|
try {
|
|
2053
|
-
|
|
2211
|
+
return JSON.parse(line.toString('utf8'));
|
|
2054
2212
|
}
|
|
2055
2213
|
catch (cause) {
|
|
2056
2214
|
if (allowInvalidFinalLine)
|
|
2057
|
-
return;
|
|
2215
|
+
return undefined;
|
|
2058
2216
|
throw new MarAgentError('MAR_AGENT_SESSION_CORRUPT', 'Session history is corrupt.', {
|
|
2059
2217
|
cause
|
|
2060
2218
|
});
|
|
2061
2219
|
}
|
|
2062
|
-
if (record.sequence < beforeSequence)
|
|
2063
|
-
newestFirst.push(record);
|
|
2064
2220
|
};
|
|
2065
|
-
while (position > 0
|
|
2221
|
+
while (position > 0) {
|
|
2066
2222
|
const start = Math.max(0, position - REVERSE_READ_CHUNK_BYTES);
|
|
2067
2223
|
const chunk = Buffer.allocUnsafe(position - start);
|
|
2068
2224
|
let bytesRead = 0;
|
|
@@ -2085,26 +2241,22 @@ async function readRecordPageFromTail(path, beforeSequence, limit) {
|
|
|
2085
2241
|
const line = pendingLineParts.length === 0
|
|
2086
2242
|
? currentPart
|
|
2087
2243
|
: Buffer.concat([currentPart, ...pendingLineParts]);
|
|
2088
|
-
|
|
2244
|
+
const record = decode(line, invalidFinalLineMayBeIgnored);
|
|
2089
2245
|
invalidFinalLineMayBeIgnored = false;
|
|
2090
2246
|
pendingLineParts = [];
|
|
2091
2247
|
lineEnd = index;
|
|
2092
|
-
if (
|
|
2093
|
-
|
|
2094
|
-
break;
|
|
2095
|
-
}
|
|
2248
|
+
if (record !== undefined)
|
|
2249
|
+
yield record;
|
|
2096
2250
|
}
|
|
2097
|
-
if (
|
|
2251
|
+
if (lineEnd > 0)
|
|
2098
2252
|
pendingLineParts.unshift(Buffer.from(value.subarray(0, lineEnd)));
|
|
2099
2253
|
position = start;
|
|
2100
2254
|
}
|
|
2101
|
-
if (
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
hasEarlier
|
|
2107
|
-
};
|
|
2255
|
+
if (pendingLineParts.length > 0) {
|
|
2256
|
+
const record = decode(Buffer.concat(pendingLineParts), invalidFinalLineMayBeIgnored);
|
|
2257
|
+
if (record !== undefined)
|
|
2258
|
+
yield record;
|
|
2259
|
+
}
|
|
2108
2260
|
}
|
|
2109
2261
|
finally {
|
|
2110
2262
|
await handle.close();
|