@myagentroam/agent 0.9.150 → 0.9.152
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/prompts/execution-budget.d.ts +1 -1
- package/dist/agent-core/prompts/execution-budget.js +3 -5
- package/dist/agent-core/runtime/execution.js +5 -7
- package/dist/agent-core/runtime/rollout-budget.d.ts +1 -0
- package/dist/agent-core/runtime/rollout-budget.js +4 -0
- package/dist/agent-core/sdk/session.js +5 -8
- package/dist/agent-core/session/jsonl-store.js +44 -9
- package/dist/agent-core/tools/local/exec.js +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { RolloutBudgetStage } from '../runtime/rollout-budget.js';
|
|
2
|
-
export type ExecutionBudgetEnvironmentStage = Exclude<RolloutBudgetStage, 'normal'> | 'inactive';
|
|
2
|
+
export type ExecutionBudgetEnvironmentStage = Exclude<RolloutBudgetStage, 'normal' | 'exhausted'> | 'inactive';
|
|
3
3
|
export declare function executionBudgetEnvironmentEntry(stage: ExecutionBudgetEnvironmentStage): string;
|
|
@@ -2,10 +2,8 @@ export function executionBudgetEnvironmentEntry(stage) {
|
|
|
2
2
|
if (stage === 'inactive')
|
|
3
3
|
return '<execution_guidance status="inactive" />';
|
|
4
4
|
const privateInstruction = 'This is internal runtime guidance. Never mention the budget, thresholds, remaining amount, or budget stage in user-visible progress or the final answer.';
|
|
5
|
-
const guidance = stage === '
|
|
6
|
-
? '
|
|
7
|
-
:
|
|
8
|
-
? 'Enter finalization now. Do not start new investigation or new subagents. Complete only the current atomic change, the minimum necessary verification, and the final answer as soon as possible.'
|
|
9
|
-
: 'Stop expanding scope and stop optional work. Prefer completing the current objective with the evidence already gathered, and avoid new investigation unless it is strictly required for correctness.';
|
|
5
|
+
const guidance = stage === 'finalize'
|
|
6
|
+
? 'Enter finalization now. Do not start new investigation or new subagents. Complete only the current atomic change, the minimum necessary verification, and the final answer as soon as possible.'
|
|
7
|
+
: 'Stop expanding scope and stop optional work. Prefer completing the current objective with the evidence already gathered, and avoid new investigation unless it is strictly required for correctness.';
|
|
10
8
|
return `<execution_guidance stage="${stage}">\n${privateInstruction}\n\n${guidance}\n</execution_guidance>`;
|
|
11
9
|
}
|
|
@@ -34,6 +34,7 @@ export class ExecutionLoop {
|
|
|
34
34
|
const pendingTools = [];
|
|
35
35
|
let incompleteToolCall = false;
|
|
36
36
|
const round = await input.prepareRound(pendingOutputContinuation);
|
|
37
|
+
input.rolloutBudget?.assertAvailable();
|
|
37
38
|
let sawModelSemanticEvent = false;
|
|
38
39
|
let requestStartedAt;
|
|
39
40
|
let firstModelResponseAt;
|
|
@@ -293,11 +294,6 @@ export class ExecutionLoop {
|
|
|
293
294
|
}
|
|
294
295
|
recoveringFromContextOverflow = false;
|
|
295
296
|
input.observeUsage(roundServerUsage);
|
|
296
|
-
if (pendingTools.length === 0 && input.mailbox.hasPending()) {
|
|
297
|
-
await input.mailbox.append();
|
|
298
|
-
this.finalAnswer = '';
|
|
299
|
-
continue;
|
|
300
|
-
}
|
|
301
297
|
const toolResults = await executeRoundTools(pendingTools, round.toolScope);
|
|
302
298
|
for (const result of toolResults) {
|
|
303
299
|
if ('images' in result && result.images?.length)
|
|
@@ -335,11 +331,12 @@ export class ExecutionLoop {
|
|
|
335
331
|
this.finalAnswer = '';
|
|
336
332
|
}
|
|
337
333
|
input.rolloutBudget?.recordUsage(latestUsage);
|
|
334
|
+
if (!input.signal.aborted)
|
|
335
|
+
input.rolloutBudget?.assertAvailable();
|
|
338
336
|
if (mode !== 'compact' && stop === 'max_output') {
|
|
339
337
|
const needsContinuation = !sawTool || incompleteToolCall;
|
|
340
338
|
if (needsContinuation) {
|
|
341
|
-
if (maxOutputContinuationUsed
|
|
342
|
-
(input.rolloutBudget?.active && input.rolloutBudget.stage === 'exhausted'))
|
|
339
|
+
if (maxOutputContinuationUsed)
|
|
343
340
|
throw new MarAgentError('MAR_AGENT_MODEL_OUTPUT_INCOMPLETE', 'Model output reached its limit before completing.');
|
|
344
341
|
maxOutputContinuationUsed = true;
|
|
345
342
|
pendingOutputContinuation = {
|
|
@@ -358,6 +355,7 @@ export class ExecutionLoop {
|
|
|
358
355
|
const aborted = toolResults.find((result) => result.failed)?.error;
|
|
359
356
|
throw aborted ?? new MarAgentError('MAR_AGENT_EXECUTION_CANCELLED', 'Execution cancelled.');
|
|
360
357
|
}
|
|
358
|
+
input.rolloutBudget?.assertAvailable();
|
|
361
359
|
if (input.mailbox.hasPending()) {
|
|
362
360
|
await input.mailbox.append();
|
|
363
361
|
this.finalAnswer = '';
|
|
@@ -19,6 +19,7 @@ export declare class RolloutBudget {
|
|
|
19
19
|
get stage(): RolloutBudgetStage;
|
|
20
20
|
recordUsage(usage: ModelUsage | undefined): number;
|
|
21
21
|
discard(): void;
|
|
22
|
+
assertAvailable(): void;
|
|
22
23
|
}
|
|
23
24
|
export declare function resolveRolloutBudgetOptions(options: false | RolloutBudgetOptions | undefined): ResolvedRolloutBudgetOptions | undefined;
|
|
24
25
|
export declare function createRolloutBudget(options: false | RolloutBudgetOptions | undefined): RolloutBudget | undefined;
|
|
@@ -48,6 +48,10 @@ export class RolloutBudget {
|
|
|
48
48
|
discard() {
|
|
49
49
|
this.#active = false;
|
|
50
50
|
}
|
|
51
|
+
assertAvailable() {
|
|
52
|
+
if (this.stage === 'exhausted')
|
|
53
|
+
throw new MarAgentError('MAR_AGENT_ROLLOUT_BUDGET_EXCEEDED', 'The shared execution token budget has been exhausted.');
|
|
54
|
+
}
|
|
51
55
|
}
|
|
52
56
|
export function resolveRolloutBudgetOptions(options) {
|
|
53
57
|
if (options === false)
|
|
@@ -234,7 +234,10 @@ export function createSessionOpener(services) {
|
|
|
234
234
|
let recordedEnvironment = restored.environmentState;
|
|
235
235
|
let projectedExecutionBudgetStage;
|
|
236
236
|
const appendExecutionBudgetEnvironment = () => {
|
|
237
|
+
rolloutBudget?.assertAvailable();
|
|
237
238
|
const currentStage = rolloutBudget?.active ? rolloutBudget.stage : 'inactive';
|
|
239
|
+
if (currentStage === 'exhausted')
|
|
240
|
+
return;
|
|
238
241
|
if (currentStage === projectedExecutionBudgetStage)
|
|
239
242
|
return;
|
|
240
243
|
const previousStage = projectedExecutionBudgetStage;
|
|
@@ -806,9 +809,7 @@ export function createSessionOpener(services) {
|
|
|
806
809
|
executionEnvironment = { ...currentEnvironment, ...currentLocalTimeContext() };
|
|
807
810
|
await recordEnvironment();
|
|
808
811
|
}
|
|
809
|
-
const
|
|
810
|
-
(!rolloutBudget?.active || rolloutBudget.stage !== 'exhausted');
|
|
811
|
-
const roundTools = mode === 'compact' ? executionTools : toolsAllowed ? executionTools : [];
|
|
812
|
+
const roundTools = executionTools;
|
|
812
813
|
const codeModeEnabled = roundTools.some((tool) => tool.name === 'code_exec');
|
|
813
814
|
const residentTools = mcpRegistry.projectDirect(baseTools());
|
|
814
815
|
const requestTools = (() => {
|
|
@@ -891,11 +892,7 @@ export function createSessionOpener(services) {
|
|
|
891
892
|
system: systemPrompt,
|
|
892
893
|
messages: modelMessages.map((message) => projectCodeModeSearchResult(message, codeModeEnabled)),
|
|
893
894
|
tools: requestTools,
|
|
894
|
-
...(mode === 'compact'
|
|
895
|
-
? { toolChoice: 'none' }
|
|
896
|
-
: toolsAllowed
|
|
897
|
-
? {}
|
|
898
|
-
: { allowTools: false }),
|
|
895
|
+
...(mode === 'compact' ? { toolChoice: 'none' } : {}),
|
|
899
896
|
promptCacheKey: sessionId,
|
|
900
897
|
reasoningEffort,
|
|
901
898
|
...(requestReasoningEffort === undefined ? {} : { requestReasoningEffort }),
|
|
@@ -11,6 +11,8 @@ import { contextGcBaseFromRecords, contextGcBaseSequence, contextGcMinimumUsageG
|
|
|
11
11
|
import { beginSessionStorageTransaction, isUnjournaledTransactionFile, recoverSessionStorageTransaction } from './transaction-recovery.js';
|
|
12
12
|
const MAX_SEGMENT_BYTES = 16 * 1024 * 1024;
|
|
13
13
|
const REVERSE_READ_CHUNK_BYTES = 64 * 1024;
|
|
14
|
+
const MAX_HISTORY_PAGE_FILES = 32;
|
|
15
|
+
const MAX_HISTORY_PAGE_OFFSETS = 128;
|
|
14
16
|
const MAX_INPUT_IMAGES = 5;
|
|
15
17
|
const MAX_INPUT_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
16
18
|
const MAX_INPUT_IMAGES_BYTES = 10 * 1024 * 1024;
|
|
@@ -161,7 +163,8 @@ export class JsonlSessionStore {
|
|
|
161
163
|
recordWrites: new Map(),
|
|
162
164
|
writerLeases: new Map(),
|
|
163
165
|
segments: new Map(),
|
|
164
|
-
executionModelReads: new Map()
|
|
166
|
+
executionModelReads: new Map(),
|
|
167
|
+
historyPagePositions: new Map()
|
|
165
168
|
};
|
|
166
169
|
}
|
|
167
170
|
/** Merge dirty metadata with disk membership; only createdIds publish locally new entries. */
|
|
@@ -1171,7 +1174,7 @@ export class JsonlSessionStore {
|
|
|
1171
1174
|
if (segment !== undefined && segment.firstSequence >= before)
|
|
1172
1175
|
continue;
|
|
1173
1176
|
const needed = limit - page.length;
|
|
1174
|
-
const window = await readRecordPageFromTail(path, before, needed);
|
|
1177
|
+
const window = await readRecordPageFromTail(path, before, needed, this.#shared.historyPagePositions);
|
|
1175
1178
|
page.unshift(...window.records);
|
|
1176
1179
|
if (window.hasEarlier) {
|
|
1177
1180
|
hasEarlier = true;
|
|
@@ -2338,9 +2341,9 @@ async function repairSegmentTail(path) {
|
|
|
2338
2341
|
await handle.close();
|
|
2339
2342
|
}
|
|
2340
2343
|
}
|
|
2341
|
-
async function readRecordPageFromTail(path, beforeSequence, limit) {
|
|
2344
|
+
async function readRecordPageFromTail(path, beforeSequence, limit, positions) {
|
|
2342
2345
|
const newestFirst = [];
|
|
2343
|
-
for await (const record of readRecordsFromTail(path)) {
|
|
2346
|
+
for await (const record of readRecordsFromTail(path, { beforeSequence, limit, positions })) {
|
|
2344
2347
|
if (!(record.sequence < beforeSequence))
|
|
2345
2348
|
continue;
|
|
2346
2349
|
newestFirst.push(record);
|
|
@@ -2353,14 +2356,42 @@ async function readRecordPageFromTail(path, beforeSequence, limit) {
|
|
|
2353
2356
|
};
|
|
2354
2357
|
}
|
|
2355
2358
|
/** Decode complete records newest-first, releasing the file as soon as a reader stops. */
|
|
2356
|
-
async function* readRecordsFromTail(path) {
|
|
2359
|
+
async function* readRecordsFromTail(path, page) {
|
|
2357
2360
|
const handle = await open(path, 'r');
|
|
2358
2361
|
try {
|
|
2359
|
-
const
|
|
2362
|
+
const metadata = await handle.stat({ bigint: true });
|
|
2363
|
+
const size = Number(metadata.size);
|
|
2360
2364
|
let position = size;
|
|
2365
|
+
let cached;
|
|
2366
|
+
if (page !== undefined) {
|
|
2367
|
+
const signature = `${metadata.dev}:${metadata.ino}:${metadata.size}:${metadata.mtimeNs}:${metadata.ctimeNs}`;
|
|
2368
|
+
const previous = page.positions.get(path);
|
|
2369
|
+
cached = previous?.signature === signature ? previous : { signature, offsets: new Map() };
|
|
2370
|
+
page.positions.delete(path);
|
|
2371
|
+
page.positions.set(path, cached);
|
|
2372
|
+
if (page.positions.size > MAX_HISTORY_PAGE_FILES)
|
|
2373
|
+
page.positions.delete(page.positions.keys().next().value);
|
|
2374
|
+
let nearestSequence = Number.POSITIVE_INFINITY;
|
|
2375
|
+
for (const [sequence, offset] of cached.offsets) {
|
|
2376
|
+
if (sequence >= page.beforeSequence && sequence < nearestSequence) {
|
|
2377
|
+
nearestSequence = sequence;
|
|
2378
|
+
position = offset;
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
let accepted = 0;
|
|
2383
|
+
const remember = (record, offset) => {
|
|
2384
|
+
if (page === undefined || cached === undefined || record.sequence >= page.beforeSequence)
|
|
2385
|
+
return;
|
|
2386
|
+
if (++accepted !== page.limit)
|
|
2387
|
+
return;
|
|
2388
|
+
cached.offsets.set(record.sequence, offset);
|
|
2389
|
+
if (cached.offsets.size > MAX_HISTORY_PAGE_OFFSETS)
|
|
2390
|
+
cached.offsets.delete(cached.offsets.keys().next().value);
|
|
2391
|
+
};
|
|
2361
2392
|
let pendingLineParts = [];
|
|
2362
2393
|
let invalidFinalLineMayBeIgnored = false;
|
|
2363
|
-
let inspectedFileEnd =
|
|
2394
|
+
let inspectedFileEnd = position !== size;
|
|
2364
2395
|
const decode = (line, allowInvalidFinalLine) => {
|
|
2365
2396
|
if (line.length === 0)
|
|
2366
2397
|
return undefined;
|
|
@@ -2402,8 +2433,10 @@ async function* readRecordsFromTail(path) {
|
|
|
2402
2433
|
invalidFinalLineMayBeIgnored = false;
|
|
2403
2434
|
pendingLineParts = [];
|
|
2404
2435
|
lineEnd = index;
|
|
2405
|
-
if (record !== undefined)
|
|
2436
|
+
if (record !== undefined) {
|
|
2437
|
+
remember(record, start + index + 1);
|
|
2406
2438
|
yield record;
|
|
2439
|
+
}
|
|
2407
2440
|
}
|
|
2408
2441
|
if (lineEnd > 0)
|
|
2409
2442
|
pendingLineParts.unshift(Buffer.from(value.subarray(0, lineEnd)));
|
|
@@ -2411,8 +2444,10 @@ async function* readRecordsFromTail(path) {
|
|
|
2411
2444
|
}
|
|
2412
2445
|
if (pendingLineParts.length > 0) {
|
|
2413
2446
|
const record = decode(Buffer.concat(pendingLineParts), invalidFinalLineMayBeIgnored);
|
|
2414
|
-
if (record !== undefined)
|
|
2447
|
+
if (record !== undefined) {
|
|
2448
|
+
remember(record, 0);
|
|
2415
2449
|
yield record;
|
|
2450
|
+
}
|
|
2416
2451
|
}
|
|
2417
2452
|
}
|
|
2418
2453
|
finally {
|
|
@@ -50,7 +50,7 @@ export const execToolDefinition = {
|
|
|
50
50
|
},
|
|
51
51
|
cwd: {
|
|
52
52
|
type: 'string',
|
|
53
|
-
description: 'Optional relative/absolute start directory; defaults to workspace.'
|
|
53
|
+
description: 'Optional relative/absolute start directory; defaults to workspace. Command-internal relative paths resolve from this directory.'
|
|
54
54
|
},
|
|
55
55
|
timeoutMs: { type: 'integer', minimum: 1, maximum: TOOL_EXECUTION_LIMITS.execMaxTimeoutMs },
|
|
56
56
|
yieldMs: {
|