@runtypelabs/sdk 1.8.0 → 1.8.2
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/index.cjs +782 -128
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +88 -2
- package/dist/index.d.ts +88 -2
- package/dist/index.js +782 -128
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2223,12 +2223,22 @@ function getDefaultPlanPath(taskName) {
|
|
|
2223
2223
|
const slug = sanitizeTaskSlug(taskName || "task");
|
|
2224
2224
|
return `.runtype/marathons/${slug}/plan.md`;
|
|
2225
2225
|
}
|
|
2226
|
+
function getDefaultExternalReportPath(taskName) {
|
|
2227
|
+
const slug = sanitizeTaskSlug(taskName || "task");
|
|
2228
|
+
return `${slug}.md`;
|
|
2229
|
+
}
|
|
2226
2230
|
function sanitizeTaskSlug(taskName) {
|
|
2227
2231
|
return taskName.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
2228
2232
|
}
|
|
2229
2233
|
|
|
2230
2234
|
// src/workflows/default-workflow.ts
|
|
2235
|
+
function isExternalTask(state) {
|
|
2236
|
+
return state.workflowVariant === "external";
|
|
2237
|
+
}
|
|
2231
2238
|
function hasSufficientResearchEvidence(state) {
|
|
2239
|
+
if (isExternalTask(state)) {
|
|
2240
|
+
return false;
|
|
2241
|
+
}
|
|
2232
2242
|
if (state.isCreationTask) {
|
|
2233
2243
|
return (state.recentReadPaths?.length || 0) >= 1;
|
|
2234
2244
|
}
|
|
@@ -2372,6 +2382,17 @@ var researchPhase = {
|
|
|
2372
2382
|
description: "Inspect the repo and identify the correct target file",
|
|
2373
2383
|
buildInstructions(state) {
|
|
2374
2384
|
const planPath = state.planPath || getDefaultPlanPath(state.taskName);
|
|
2385
|
+
const reportPath = state.planPath || getDefaultExternalReportPath(state.taskName);
|
|
2386
|
+
if (isExternalTask(state)) {
|
|
2387
|
+
return [
|
|
2388
|
+
"--- Workflow Phase: Research ---",
|
|
2389
|
+
"This is an external web research task, not a repository editing task.",
|
|
2390
|
+
"Research the requested website or external source directly using built-in web tools first.",
|
|
2391
|
+
"Do NOT inspect the local repo or search for a target file unless the user explicitly asks for local workspace changes.",
|
|
2392
|
+
`Write the final markdown report to exactly: ${reportPath}`,
|
|
2393
|
+
"Do NOT end with TASK_COMPLETE until that markdown file has been written."
|
|
2394
|
+
].join("\n");
|
|
2395
|
+
}
|
|
2375
2396
|
if (state.isCreationTask) {
|
|
2376
2397
|
return [
|
|
2377
2398
|
"--- Workflow Phase: Research ---",
|
|
@@ -2391,6 +2412,15 @@ var researchPhase = {
|
|
|
2391
2412
|
].join("\n");
|
|
2392
2413
|
},
|
|
2393
2414
|
buildToolGuidance(state) {
|
|
2415
|
+
if (isExternalTask(state)) {
|
|
2416
|
+
const reportPath = state.planPath || getDefaultExternalReportPath(state.taskName);
|
|
2417
|
+
return [
|
|
2418
|
+
"This is a web/external research task.",
|
|
2419
|
+
"Start with built-in web tools such as firecrawl or web search, not local repo discovery tools.",
|
|
2420
|
+
"Do not call search_repo, glob_files, tree_directory, list_directory, or read_file unless the user explicitly asked you to inspect local files.",
|
|
2421
|
+
`Use write_file to save the final markdown report to exactly: ${reportPath}.`
|
|
2422
|
+
];
|
|
2423
|
+
}
|
|
2394
2424
|
if (state.isCreationTask) {
|
|
2395
2425
|
return [
|
|
2396
2426
|
"This is a creation task \u2014 you are building new files, not editing existing ones.",
|
|
@@ -2424,10 +2454,45 @@ var researchPhase = {
|
|
|
2424
2454
|
`Next step: write the plan markdown to ${state.planPath} before editing the product file.`
|
|
2425
2455
|
].join("\n");
|
|
2426
2456
|
},
|
|
2427
|
-
interceptToolCall() {
|
|
2457
|
+
interceptToolCall(toolName, _args, ctx) {
|
|
2458
|
+
if (!isExternalTask(ctx.state)) {
|
|
2459
|
+
return void 0;
|
|
2460
|
+
}
|
|
2461
|
+
const normalizedPathArg = typeof _args.path === "string" && _args.path.trim() ? ctx.normalizePath(String(_args.path)) : void 0;
|
|
2462
|
+
const normalizedReportPath = ctx.normalizePath(
|
|
2463
|
+
ctx.state.planPath || getDefaultExternalReportPath(ctx.state.taskName)
|
|
2464
|
+
);
|
|
2465
|
+
if (ctx.isDiscoveryTool(toolName) || toolName === "read_file" || toolName === "restore_file_checkpoint") {
|
|
2466
|
+
return [
|
|
2467
|
+
`Blocked by marathon external research guard: ${toolName} is disabled for web research tasks.`,
|
|
2468
|
+
"Research the requested external source with built-in web tools instead.",
|
|
2469
|
+
"Use local file tools only if the user explicitly asked for workspace inspection or if you are saving the final deliverable."
|
|
2470
|
+
].join(" ");
|
|
2471
|
+
}
|
|
2472
|
+
if (toolName === "write_file" && normalizedPathArg && normalizedPathArg !== normalizedReportPath) {
|
|
2473
|
+
return [
|
|
2474
|
+
`Blocked by marathon external research guard: write_file must target "${normalizedReportPath}".`,
|
|
2475
|
+
"Save the final markdown report to the required workspace path before TASK_COMPLETE."
|
|
2476
|
+
].join(" ");
|
|
2477
|
+
}
|
|
2428
2478
|
return void 0;
|
|
2429
2479
|
},
|
|
2430
2480
|
buildRecoveryMessage(state) {
|
|
2481
|
+
if (isExternalTask(state)) {
|
|
2482
|
+
const recent2 = state.sessions.slice(-2);
|
|
2483
|
+
const reportPath = state.planPath || getDefaultExternalReportPath(state.taskName);
|
|
2484
|
+
if (recent2.length < 2 || !recent2.every((session) => session.wroteFiles === false)) {
|
|
2485
|
+
return void 0;
|
|
2486
|
+
}
|
|
2487
|
+
const repeatedSameActions2 = hasRepeatedSameActions(state.sessions);
|
|
2488
|
+
return [
|
|
2489
|
+
"Recovery instruction:",
|
|
2490
|
+
"This web research task is missing its required markdown artifact.",
|
|
2491
|
+
`Your next action must be write_file to "${reportPath}" with the final findings in markdown.`,
|
|
2492
|
+
"Do not end with TASK_COMPLETE until that file has been written.",
|
|
2493
|
+
...repeatedSameActions2 ? ["You are repeating the same actions; break the loop by writing the final report now."] : []
|
|
2494
|
+
].join("\n");
|
|
2495
|
+
}
|
|
2431
2496
|
const recent = state.sessions.slice(-2);
|
|
2432
2497
|
const normalizedPlanPath = typeof state.planPath === "string" && state.planPath.trim() ? normalizeCandidatePath(state.planPath) : void 0;
|
|
2433
2498
|
const recentPlanOnlyLoop = Boolean(normalizedPlanPath) && recent.length === 2 && recent.every((session) => {
|
|
@@ -2471,6 +2536,12 @@ var researchPhase = {
|
|
|
2471
2536
|
].join("\n");
|
|
2472
2537
|
},
|
|
2473
2538
|
shouldForceEndTurn(snapshot, ctx) {
|
|
2539
|
+
if (isExternalTask(ctx.state)) {
|
|
2540
|
+
if (ctx.isDiscoveryTool(snapshot.toolName) && snapshot.actionKeyCount >= 2) {
|
|
2541
|
+
return "this web research task is looping on local repo discovery instead of using external tools";
|
|
2542
|
+
}
|
|
2543
|
+
return void 0;
|
|
2544
|
+
}
|
|
2474
2545
|
const state = ctx.state;
|
|
2475
2546
|
const sufficientResearch = hasSufficientResearchEvidence(state);
|
|
2476
2547
|
if (sufficientResearch && snapshot.discoveryPauseCount >= 12) {
|
|
@@ -2483,6 +2554,12 @@ var researchPhase = {
|
|
|
2483
2554
|
return "this session exceeded the discovery-tool budget without ending the turn";
|
|
2484
2555
|
}
|
|
2485
2556
|
return void 0;
|
|
2557
|
+
},
|
|
2558
|
+
canAcceptCompletion(state, trace) {
|
|
2559
|
+
if (!isExternalTask(state)) {
|
|
2560
|
+
return true;
|
|
2561
|
+
}
|
|
2562
|
+
return Boolean(state.planWritten || trace.planWritten);
|
|
2486
2563
|
}
|
|
2487
2564
|
};
|
|
2488
2565
|
var planningPhase = {
|
|
@@ -2709,6 +2786,65 @@ var executionPhase = {
|
|
|
2709
2786
|
};
|
|
2710
2787
|
function classifyVariant(message) {
|
|
2711
2788
|
const lower = message.toLowerCase();
|
|
2789
|
+
const externalVerbs = [
|
|
2790
|
+
"fetch",
|
|
2791
|
+
"browse",
|
|
2792
|
+
"scrape",
|
|
2793
|
+
"crawl",
|
|
2794
|
+
"download",
|
|
2795
|
+
"visit",
|
|
2796
|
+
"navigate to",
|
|
2797
|
+
"go to",
|
|
2798
|
+
"open the",
|
|
2799
|
+
"look up",
|
|
2800
|
+
"search for",
|
|
2801
|
+
"find out",
|
|
2802
|
+
"check out",
|
|
2803
|
+
"pull up"
|
|
2804
|
+
];
|
|
2805
|
+
const externalTargets = [
|
|
2806
|
+
"http://",
|
|
2807
|
+
"https://",
|
|
2808
|
+
"www.",
|
|
2809
|
+
".com",
|
|
2810
|
+
".org",
|
|
2811
|
+
".io",
|
|
2812
|
+
".net",
|
|
2813
|
+
".dev",
|
|
2814
|
+
"website",
|
|
2815
|
+
"webpage",
|
|
2816
|
+
"web page",
|
|
2817
|
+
"home page",
|
|
2818
|
+
"homepage",
|
|
2819
|
+
"hacker news",
|
|
2820
|
+
"reddit",
|
|
2821
|
+
"twitter",
|
|
2822
|
+
"github.com",
|
|
2823
|
+
"firecrawl",
|
|
2824
|
+
"exa_search"
|
|
2825
|
+
];
|
|
2826
|
+
const hasExternalVerb = externalVerbs.some((v) => lower.includes(v));
|
|
2827
|
+
const hasExternalTarget = externalTargets.some((t) => lower.includes(t));
|
|
2828
|
+
const modificationVerbs = [
|
|
2829
|
+
"fix",
|
|
2830
|
+
"update",
|
|
2831
|
+
"change",
|
|
2832
|
+
"modify",
|
|
2833
|
+
"edit",
|
|
2834
|
+
"refactor",
|
|
2835
|
+
"improve",
|
|
2836
|
+
"add to",
|
|
2837
|
+
"remove",
|
|
2838
|
+
"delete",
|
|
2839
|
+
"rename",
|
|
2840
|
+
"migrate"
|
|
2841
|
+
];
|
|
2842
|
+
const hasModificationVerb = modificationVerbs.some(
|
|
2843
|
+
(v) => lower.startsWith(v) || new RegExp(`\\b${v}\\b`).test(lower)
|
|
2844
|
+
);
|
|
2845
|
+
if (hasExternalVerb && hasExternalTarget && !hasModificationVerb) {
|
|
2846
|
+
return "external";
|
|
2847
|
+
}
|
|
2712
2848
|
const creationPatterns = [
|
|
2713
2849
|
/^create\b/,
|
|
2714
2850
|
/^build\b/,
|
|
@@ -2729,30 +2865,13 @@ function classifyVariant(message) {
|
|
|
2729
2865
|
/^new\b/
|
|
2730
2866
|
];
|
|
2731
2867
|
const hasCreationStart = creationPatterns.some((p) => p.test(lower));
|
|
2732
|
-
const modificationVerbs = [
|
|
2733
|
-
"fix",
|
|
2734
|
-
"update",
|
|
2735
|
-
"change",
|
|
2736
|
-
"modify",
|
|
2737
|
-
"edit",
|
|
2738
|
-
"refactor",
|
|
2739
|
-
"improve",
|
|
2740
|
-
"add to",
|
|
2741
|
-
"remove",
|
|
2742
|
-
"delete",
|
|
2743
|
-
"rename",
|
|
2744
|
-
"migrate"
|
|
2745
|
-
];
|
|
2746
|
-
const hasModificationVerb = modificationVerbs.some(
|
|
2747
|
-
(v) => lower.startsWith(v) || new RegExp(`\\b${v}\\b`).test(lower)
|
|
2748
|
-
);
|
|
2749
2868
|
if (hasCreationStart && !hasModificationVerb) {
|
|
2750
2869
|
return "create";
|
|
2751
2870
|
}
|
|
2752
2871
|
return "modify";
|
|
2753
2872
|
}
|
|
2754
2873
|
async function generateBootstrapContext(message, localTools, variant) {
|
|
2755
|
-
if (variant === "create") return void 0;
|
|
2874
|
+
if (variant === "create" || variant === "external") return void 0;
|
|
2756
2875
|
if (!localTools) return void 0;
|
|
2757
2876
|
const searchTool = localTools.search_repo;
|
|
2758
2877
|
const globTool = localTools.glob_files;
|
|
@@ -2788,7 +2907,7 @@ async function generateBootstrapContext(message, localTools, variant) {
|
|
|
2788
2907
|
return ["Bootstrap repo hints:", ...lines].join("\n").slice(0, 1500);
|
|
2789
2908
|
}
|
|
2790
2909
|
function buildCandidateBlock(state) {
|
|
2791
|
-
if (!state.bestCandidatePath || state.isCreationTask) return "";
|
|
2910
|
+
if (!state.bestCandidatePath || state.isCreationTask || isExternalTask(state)) return "";
|
|
2792
2911
|
return [
|
|
2793
2912
|
"",
|
|
2794
2913
|
"--- Best Candidate ---",
|
|
@@ -3038,7 +3157,11 @@ var designPhase = {
|
|
|
3038
3157
|
buildTransitionSummary(_state, _nextPhaseName) {
|
|
3039
3158
|
return [
|
|
3040
3159
|
"Automatic phase transition: design \u2192 build.",
|
|
3041
|
-
"Requirements understood. Build the game and deploy it using deploy_sandbox
|
|
3160
|
+
"Requirements understood. Build the game and deploy it using deploy_sandbox.",
|
|
3161
|
+
"",
|
|
3162
|
+
"MANDATORY: Use the `files` parameter for ALL game HTML/JS/CSS.",
|
|
3163
|
+
"code = minimal Express static server (~4 lines), files = game assets.",
|
|
3164
|
+
"NEVER embed HTML inside res.send() template literals."
|
|
3042
3165
|
].join("\n");
|
|
3043
3166
|
},
|
|
3044
3167
|
interceptToolCall(toolName, _args, _ctx) {
|
|
@@ -3098,7 +3221,32 @@ var buildPhase = {
|
|
|
3098
3221
|
" 4. If the deploy fails, read the error output, fix the code, and call deploy_sandbox again.",
|
|
3099
3222
|
" 5. The sandbox is persistent \u2014 subsequent calls reuse the same sandbox.",
|
|
3100
3223
|
" 6. Load game libraries from CDN in your HTML (e.g., Three.js from unpkg/cdnjs) OR include them in packageJson.",
|
|
3101
|
-
" 7. Make the game fullscreen by default (width: 100vw, height: 100vh, no margin/padding on body)."
|
|
3224
|
+
" 7. Make the game fullscreen by default (width: 100vw, height: 100vh, no margin/padding on body).",
|
|
3225
|
+
"",
|
|
3226
|
+
"STARTER TEMPLATES \u2014 Copy and customize based on the game type:",
|
|
3227
|
+
"",
|
|
3228
|
+
"ALL templates use this server code:",
|
|
3229
|
+
' code: `const express = require("express"); const app = express(); app.use(express.static("public")); app.listen(3000, () => console.log("Game server running on port 3000"));`',
|
|
3230
|
+
' packageJson: { "dependencies": { "express": "^4.18.2" } }',
|
|
3231
|
+
' language: "javascript"',
|
|
3232
|
+
"",
|
|
3233
|
+
"THREE.JS (3D games \u2014 racing, flying, FPS, etc.):",
|
|
3234
|
+
" files: {",
|
|
3235
|
+
' "public/index.html": HTML with <script type="importmap">{"imports":{"three":"https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js"}}</script> and <script type="module" src="game.js"></script>',
|
|
3236
|
+
' "public/game.js": Import three, create scene/camera/renderer, game loop with requestAnimationFrame',
|
|
3237
|
+
" }",
|
|
3238
|
+
"",
|
|
3239
|
+
"PHASER (2D games \u2014 platformers, shooters, puzzles):",
|
|
3240
|
+
" files: {",
|
|
3241
|
+
' "public/index.html": HTML with <script src="https://cdn.jsdelivr.net/npm/phaser@3.80.1/dist/phaser.min.js"></script> and <script src="game.js"></script>',
|
|
3242
|
+
' "public/game.js": Phaser.Game config with scenes, preload/create/update lifecycle',
|
|
3243
|
+
" }",
|
|
3244
|
+
"",
|
|
3245
|
+
"CANVAS 2D (simple games \u2014 pong, snake, breakout):",
|
|
3246
|
+
" files: {",
|
|
3247
|
+
' "public/index.html": HTML with <canvas id="game" width="800" height="600"></canvas> and <script src="game.js"></script>',
|
|
3248
|
+
' "public/game.js": Get canvas context, game loop with requestAnimationFrame, draw calls',
|
|
3249
|
+
" }"
|
|
3102
3250
|
].join("\n");
|
|
3103
3251
|
},
|
|
3104
3252
|
buildToolGuidance(_state) {
|
|
@@ -3112,8 +3260,23 @@ var buildPhase = {
|
|
|
3112
3260
|
isComplete() {
|
|
3113
3261
|
return false;
|
|
3114
3262
|
},
|
|
3115
|
-
interceptToolCall(toolName,
|
|
3116
|
-
|
|
3263
|
+
interceptToolCall(toolName, args, _ctx) {
|
|
3264
|
+
const localBlock = blockLocalTools(toolName);
|
|
3265
|
+
if (localBlock) return localBlock;
|
|
3266
|
+
if (toolName === "deploy_sandbox" && args) {
|
|
3267
|
+
const hasFiles = args.files && typeof args.files === "object" && Object.keys(args.files).length > 0;
|
|
3268
|
+
const codeStr = typeof args.code === "string" ? args.code : "";
|
|
3269
|
+
const embedsHtml = /res\.send\s*\(/.test(codeStr) && /<html|<body|<script/i.test(codeStr);
|
|
3270
|
+
if (!hasFiles && embedsHtml) {
|
|
3271
|
+
return [
|
|
3272
|
+
"Blocked: You are embedding HTML inside res.send() \u2014 this will break due to nested template literals.",
|
|
3273
|
+
"You MUST use the `files` parameter. Put your game HTML/JS/CSS in files and use a minimal Express static server for code.",
|
|
3274
|
+
'Example: code: `const express = require("express"); const app = express(); app.use(express.static("public")); app.listen(3000);`',
|
|
3275
|
+
'files: { "public/index.html": "<!DOCTYPE html>...", "public/game.js": "// game code" }'
|
|
3276
|
+
].join("\n");
|
|
3277
|
+
}
|
|
3278
|
+
}
|
|
3279
|
+
return void 0;
|
|
3117
3280
|
},
|
|
3118
3281
|
canAcceptCompletion(_state, trace) {
|
|
3119
3282
|
return trace.entries.some((entry) => entry.startsWith("deploy_sandbox"));
|
|
@@ -4831,8 +4994,10 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
4831
4994
|
return `[${toolName} output (${resultStr.length} chars) saved to ${filePath} \u2014 use read_file to retrieve if needed]`;
|
|
4832
4995
|
}
|
|
4833
4996
|
getDefaultPlanPath(taskName) {
|
|
4834
|
-
|
|
4835
|
-
|
|
4997
|
+
return getDefaultPlanPath(taskName);
|
|
4998
|
+
}
|
|
4999
|
+
getDefaultExternalReportPath(taskName) {
|
|
5000
|
+
return getDefaultExternalReportPath(taskName);
|
|
4836
5001
|
}
|
|
4837
5002
|
dirnameOfCandidatePath(candidatePath) {
|
|
4838
5003
|
const normalized = this.normalizeCandidatePath(candidatePath);
|
|
@@ -4938,7 +5103,10 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
4938
5103
|
}
|
|
4939
5104
|
sanitizeResumeState(resumeState, taskName) {
|
|
4940
5105
|
if (!resumeState) return void 0;
|
|
4941
|
-
const
|
|
5106
|
+
const workflowVariant = resumeState.workflowVariant;
|
|
5107
|
+
const defaultPlanPath = workflowVariant === "external" ? this.getDefaultExternalReportPath(taskName) : this.getDefaultPlanPath(taskName);
|
|
5108
|
+
const planPath = typeof resumeState.planPath === "string" && resumeState.planPath.trim() ? this.normalizeCandidatePath(resumeState.planPath) : defaultPlanPath;
|
|
5109
|
+
const migratedPlanPath = workflowVariant === "external" && planPath === this.getDefaultPlanPath(taskName) ? this.getDefaultExternalReportPath(taskName) : planPath;
|
|
4942
5110
|
const candidatePaths = this.dedupeNormalizedCandidatePaths(resumeState.candidatePaths);
|
|
4943
5111
|
const recentReadPaths = this.dedupeNormalizedCandidatePaths(resumeState.recentReadPaths);
|
|
4944
5112
|
const normalizedBestCandidatePath = typeof resumeState.bestCandidatePath === "string" && resumeState.bestCandidatePath.trim() ? this.normalizeCandidatePath(resumeState.bestCandidatePath) : void 0;
|
|
@@ -4949,7 +5117,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
4949
5117
|
return {
|
|
4950
5118
|
...resumeState,
|
|
4951
5119
|
workflowPhase,
|
|
4952
|
-
planPath,
|
|
5120
|
+
planPath: migratedPlanPath,
|
|
4953
5121
|
planWritten: Boolean(resumeState.planWritten),
|
|
4954
5122
|
bestCandidatePath,
|
|
4955
5123
|
bestCandidateReason: bestCandidatePath ? resumeState.bestCandidateReason : void 0,
|
|
@@ -5375,13 +5543,14 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5375
5543
|
const taskName = typeof options.trackProgress === "string" ? options.trackProgress : options.trackProgress ? `${agent.name} task` : "";
|
|
5376
5544
|
const resolvedTaskName = taskName || `${agent.name} task`;
|
|
5377
5545
|
const seededResumeState = this.sanitizeResumeState(options.resumeState, resolvedTaskName);
|
|
5546
|
+
const classifiedVariant = seededResumeState?.workflowVariant ?? workflow.classifyVariant?.(options.message);
|
|
5378
5547
|
const state = {
|
|
5379
5548
|
agentId: id,
|
|
5380
5549
|
agentName: agent.name,
|
|
5381
5550
|
taskName: resolvedTaskName,
|
|
5382
5551
|
status: "running",
|
|
5383
5552
|
workflowPhase: seededResumeState?.workflowPhase || workflow.phases[0]?.name || "research",
|
|
5384
|
-
planPath: seededResumeState?.planPath || this.getDefaultPlanPath(resolvedTaskName),
|
|
5553
|
+
planPath: seededResumeState?.planPath || (classifiedVariant === "external" ? this.getDefaultExternalReportPath(resolvedTaskName) : this.getDefaultPlanPath(resolvedTaskName)),
|
|
5385
5554
|
planWritten: seededResumeState?.planWritten || false,
|
|
5386
5555
|
bestCandidateNeedsVerification: seededResumeState?.bestCandidateNeedsVerification || false,
|
|
5387
5556
|
bestCandidateVerified: seededResumeState?.bestCandidateVerified || false,
|
|
@@ -5404,11 +5573,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5404
5573
|
...seededResumeState?.recentReadPaths ? { recentReadPaths: seededResumeState.recentReadPaths } : {},
|
|
5405
5574
|
...seededResumeState?.recentActionKeys ? { recentActionKeys: seededResumeState.recentActionKeys } : {}
|
|
5406
5575
|
};
|
|
5407
|
-
state.workflowVariant =
|
|
5576
|
+
state.workflowVariant = classifiedVariant;
|
|
5408
5577
|
state.isCreationTask = seededResumeState?.isCreationTask ?? state.workflowVariant === "create";
|
|
5409
5578
|
this.updateWorkflowPhase(state, this.createEmptyToolTrace(), workflow);
|
|
5410
5579
|
let recordId;
|
|
5411
5580
|
const localToolNames = options.localTools ? Object.keys(options.localTools) : void 0;
|
|
5581
|
+
const builtinToolSchemas = await this.loadBuiltinToolSchemas(options.toolIds);
|
|
5582
|
+
const compactInstructions = this.resolveCompactInstructions(options.compactInstructions, agent);
|
|
5412
5583
|
if (!options.previousMessages) {
|
|
5413
5584
|
if (workflow.generateBootstrapContext) {
|
|
5414
5585
|
state.bootstrapContext = await workflow.generateBootstrapContext(
|
|
@@ -5420,7 +5591,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5420
5591
|
state.bootstrapContext = await this.generateBootstrapDiscoveryContext(
|
|
5421
5592
|
options.message,
|
|
5422
5593
|
options.localTools,
|
|
5423
|
-
state.isCreationTask
|
|
5594
|
+
state.isCreationTask || state.workflowVariant === "external"
|
|
5424
5595
|
);
|
|
5425
5596
|
}
|
|
5426
5597
|
const bootstrapCandidate = this.extractBestCandidateFromBootstrapContext(state.bootstrapContext);
|
|
@@ -5442,7 +5613,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5442
5613
|
if (session === 0 && !options.previousMessages) {
|
|
5443
5614
|
state.originalMessage = options.message;
|
|
5444
5615
|
}
|
|
5445
|
-
const
|
|
5616
|
+
const preparedSession = await this.prepareSessionContext(
|
|
5446
5617
|
options.message,
|
|
5447
5618
|
state,
|
|
5448
5619
|
session,
|
|
@@ -5450,61 +5621,93 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5450
5621
|
localToolNames,
|
|
5451
5622
|
continuationContext,
|
|
5452
5623
|
workflow,
|
|
5453
|
-
options.toolIds
|
|
5624
|
+
options.toolIds,
|
|
5625
|
+
{
|
|
5626
|
+
autoCompactTokenThreshold: options.autoCompactTokenThreshold,
|
|
5627
|
+
contextLimitTokens: options.contextLimitTokens,
|
|
5628
|
+
model: options.model,
|
|
5629
|
+
compactStrategy: options.compactStrategy,
|
|
5630
|
+
compactInstructions,
|
|
5631
|
+
localTools: options.localTools,
|
|
5632
|
+
builtinToolSchemas,
|
|
5633
|
+
onContextCompaction: options.onContextCompaction,
|
|
5634
|
+
onContextNotice: options.onContextNotice
|
|
5635
|
+
}
|
|
5454
5636
|
);
|
|
5637
|
+
const {
|
|
5638
|
+
messages,
|
|
5639
|
+
requestContextManagement,
|
|
5640
|
+
pendingNativeCompactionEvent
|
|
5641
|
+
} = preparedSession;
|
|
5455
5642
|
let sessionResult;
|
|
5456
5643
|
const sessionData = {
|
|
5457
5644
|
messages,
|
|
5458
5645
|
debugMode: options.debugMode,
|
|
5459
5646
|
model: options.model,
|
|
5460
|
-
...options.toolIds?.length ? { tools: { toolIds: options.toolIds } } : {}
|
|
5647
|
+
...options.toolIds?.length ? { tools: { toolIds: options.toolIds } } : {},
|
|
5648
|
+
...requestContextManagement ? { contextManagement: requestContextManagement } : {}
|
|
5461
5649
|
};
|
|
5462
5650
|
let sessionToolMessages = [];
|
|
5463
|
-
if (
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5651
|
+
if (pendingNativeCompactionEvent) {
|
|
5652
|
+
await this.emitContextCompactionEvent(options.onContextCompaction, {
|
|
5653
|
+
phase: "start",
|
|
5654
|
+
...pendingNativeCompactionEvent
|
|
5655
|
+
});
|
|
5656
|
+
}
|
|
5657
|
+
try {
|
|
5658
|
+
if (useStream && options.localTools) {
|
|
5659
|
+
const localToolResult = await this.executeWithLocalTools(
|
|
5660
|
+
id,
|
|
5661
|
+
sessionData,
|
|
5662
|
+
sessionLocalTools || options.localTools,
|
|
5663
|
+
sessionCallbacks,
|
|
5664
|
+
{
|
|
5665
|
+
onLocalToolResult: this.createLocalToolLoopGuard(state, sessionTrace, workflow)
|
|
5666
|
+
},
|
|
5667
|
+
state.taskName
|
|
5668
|
+
);
|
|
5669
|
+
if (!localToolResult) {
|
|
5670
|
+
throw new Error("Agent stream ended without a complete event");
|
|
5671
|
+
}
|
|
5672
|
+
const { completeEvent, toolMessages: captured } = localToolResult;
|
|
5673
|
+
sessionToolMessages = captured;
|
|
5674
|
+
sessionResult = {
|
|
5675
|
+
success: completeEvent.success,
|
|
5676
|
+
result: completeEvent.finalOutput || "",
|
|
5677
|
+
iterations: completeEvent.iterations,
|
|
5678
|
+
totalCost: completeEvent.totalCost || 0,
|
|
5679
|
+
totalTokens: completeEvent.totalTokens,
|
|
5680
|
+
stopReason: completeEvent.stopReason,
|
|
5681
|
+
error: completeEvent.error
|
|
5682
|
+
};
|
|
5683
|
+
} else if (useStream && options.streamCallbacks) {
|
|
5684
|
+
const completeEvent = await this.executeWithCallbacks(
|
|
5685
|
+
id,
|
|
5686
|
+
sessionData,
|
|
5687
|
+
sessionCallbacks || options.streamCallbacks
|
|
5688
|
+
);
|
|
5689
|
+
if (!completeEvent) {
|
|
5690
|
+
throw new Error("Agent stream ended without a complete event");
|
|
5691
|
+
}
|
|
5692
|
+
sessionResult = {
|
|
5693
|
+
success: completeEvent.success,
|
|
5694
|
+
result: completeEvent.finalOutput || "",
|
|
5695
|
+
iterations: completeEvent.iterations,
|
|
5696
|
+
totalCost: completeEvent.totalCost || 0,
|
|
5697
|
+
totalTokens: completeEvent.totalTokens,
|
|
5698
|
+
stopReason: completeEvent.stopReason,
|
|
5699
|
+
error: completeEvent.error
|
|
5700
|
+
};
|
|
5701
|
+
} else {
|
|
5702
|
+
sessionResult = await this.execute(id, sessionData);
|
|
5476
5703
|
}
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
totalCost: completeEvent.totalCost || 0,
|
|
5484
|
-
totalTokens: completeEvent.totalTokens,
|
|
5485
|
-
stopReason: completeEvent.stopReason,
|
|
5486
|
-
error: completeEvent.error
|
|
5487
|
-
};
|
|
5488
|
-
} else if (useStream && options.streamCallbacks) {
|
|
5489
|
-
const completeEvent = await this.executeWithCallbacks(
|
|
5490
|
-
id,
|
|
5491
|
-
sessionData,
|
|
5492
|
-
sessionCallbacks || options.streamCallbacks
|
|
5493
|
-
);
|
|
5494
|
-
if (!completeEvent) {
|
|
5495
|
-
throw new Error("Agent stream ended without a complete event");
|
|
5704
|
+
} finally {
|
|
5705
|
+
if (pendingNativeCompactionEvent) {
|
|
5706
|
+
await this.emitContextCompactionEvent(options.onContextCompaction, {
|
|
5707
|
+
phase: "complete",
|
|
5708
|
+
...pendingNativeCompactionEvent
|
|
5709
|
+
});
|
|
5496
5710
|
}
|
|
5497
|
-
sessionResult = {
|
|
5498
|
-
success: completeEvent.success,
|
|
5499
|
-
result: completeEvent.finalOutput || "",
|
|
5500
|
-
iterations: completeEvent.iterations,
|
|
5501
|
-
totalCost: completeEvent.totalCost || 0,
|
|
5502
|
-
totalTokens: completeEvent.totalTokens,
|
|
5503
|
-
stopReason: completeEvent.stopReason,
|
|
5504
|
-
error: completeEvent.error
|
|
5505
|
-
};
|
|
5506
|
-
} else {
|
|
5507
|
-
sessionResult = await this.execute(id, sessionData);
|
|
5508
5711
|
}
|
|
5509
5712
|
const toolTraceSummary = this.buildToolTraceSummary(sessionTrace);
|
|
5510
5713
|
const effectiveSessionOutput = this.buildEffectiveSessionOutput(
|
|
@@ -5591,7 +5794,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5591
5794
|
}
|
|
5592
5795
|
}
|
|
5593
5796
|
if (!state.messages) state.messages = [];
|
|
5594
|
-
if (
|
|
5797
|
+
if (this.isCompactHistoryMessageSet(messages)) {
|
|
5798
|
+
state.messages = [...messages];
|
|
5799
|
+
} else if (state.messages.length > 0 && messages.length > state.messages.length) {
|
|
5595
5800
|
const newMessages = messages.slice(state.messages.length);
|
|
5596
5801
|
state.messages.push(...newMessages);
|
|
5597
5802
|
} else {
|
|
@@ -5612,13 +5817,15 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5612
5817
|
if (state.sessions.length > 50) {
|
|
5613
5818
|
state.sessions = state.sessions.slice(-50);
|
|
5614
5819
|
}
|
|
5615
|
-
|
|
5820
|
+
const detectedTaskCompletion = this.detectTaskCompletion(sessionResult.result);
|
|
5821
|
+
const acceptedTaskCompletion = detectedTaskCompletion && this.canAcceptTaskCompletion(sessionResult.result, state, sessionTrace, workflow);
|
|
5822
|
+
if (sessionResult.stopReason === "complete" && !detectedTaskCompletion) {
|
|
5616
5823
|
state.status = "complete";
|
|
5617
5824
|
} else if (sessionResult.stopReason === "error") {
|
|
5618
5825
|
state.status = "error";
|
|
5619
5826
|
} else if (sessionResult.stopReason === "max_cost") {
|
|
5620
5827
|
state.status = "budget_exceeded";
|
|
5621
|
-
} else if (
|
|
5828
|
+
} else if (acceptedTaskCompletion) {
|
|
5622
5829
|
state.status = "complete";
|
|
5623
5830
|
} else if (maxCost && state.totalCost >= maxCost) {
|
|
5624
5831
|
state.status = "budget_exceeded";
|
|
@@ -5656,88 +5863,426 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5656
5863
|
const upper = output.toUpperCase();
|
|
5657
5864
|
return _AgentsEndpoint.STOP_PHRASES.some((phrase) => upper.includes(phrase.toUpperCase()));
|
|
5658
5865
|
}
|
|
5866
|
+
resolveReservedOutputTokens(contextLimitTokens) {
|
|
5867
|
+
if (typeof contextLimitTokens !== "number" || !Number.isFinite(contextLimitTokens) || contextLimitTokens <= 0) {
|
|
5868
|
+
return void 0;
|
|
5869
|
+
}
|
|
5870
|
+
return Math.max(8e3, Math.min(64e3, Math.floor(contextLimitTokens * 0.15)));
|
|
5871
|
+
}
|
|
5872
|
+
resolveEffectiveInputBudgetTokens(contextLimitTokens) {
|
|
5873
|
+
if (typeof contextLimitTokens !== "number" || !Number.isFinite(contextLimitTokens) || contextLimitTokens <= 0) {
|
|
5874
|
+
return void 0;
|
|
5875
|
+
}
|
|
5876
|
+
const reservedOutputTokens = this.resolveReservedOutputTokens(contextLimitTokens) ?? 0;
|
|
5877
|
+
return Math.max(1, contextLimitTokens - reservedOutputTokens);
|
|
5878
|
+
}
|
|
5879
|
+
resolveCompactStrategy(strategy, modelId) {
|
|
5880
|
+
if (strategy === "summary_fallback") {
|
|
5881
|
+
return "summary_fallback";
|
|
5882
|
+
}
|
|
5883
|
+
const normalizedModelId = modelId?.trim().toLowerCase() ?? "";
|
|
5884
|
+
const supportsAnthropicNativeCompaction = normalizedModelId.includes("claude") || normalizedModelId.includes("anthropic");
|
|
5885
|
+
if (strategy === "provider_native") {
|
|
5886
|
+
return supportsAnthropicNativeCompaction ? "provider_native" : "summary_fallback";
|
|
5887
|
+
}
|
|
5888
|
+
return supportsAnthropicNativeCompaction ? "provider_native" : "summary_fallback";
|
|
5889
|
+
}
|
|
5890
|
+
estimateTextTokens(text) {
|
|
5891
|
+
if (!text) return 0;
|
|
5892
|
+
return Math.ceil(text.length / 4);
|
|
5893
|
+
}
|
|
5894
|
+
estimateUnknownTokens(value) {
|
|
5895
|
+
if (typeof value === "string") return this.estimateTextTokens(value);
|
|
5896
|
+
try {
|
|
5897
|
+
return this.estimateTextTokens(JSON.stringify(value));
|
|
5898
|
+
} catch {
|
|
5899
|
+
return 0;
|
|
5900
|
+
}
|
|
5901
|
+
}
|
|
5902
|
+
estimateMessageContentTokens(content) {
|
|
5903
|
+
if (typeof content === "string") return this.estimateTextTokens(content);
|
|
5904
|
+
return content.reduce((total, part) => {
|
|
5905
|
+
if (typeof part.text === "string") total += this.estimateTextTokens(part.text);
|
|
5906
|
+
if (typeof part.image === "string") total += 85;
|
|
5907
|
+
if (typeof part.data === "string") {
|
|
5908
|
+
total += Math.min(200, this.estimateTextTokens(part.data));
|
|
5909
|
+
}
|
|
5910
|
+
return total + 4;
|
|
5911
|
+
}, 0);
|
|
5912
|
+
}
|
|
5913
|
+
estimateToolCallTokens(toolCalls) {
|
|
5914
|
+
if (!toolCalls || toolCalls.length === 0) return 0;
|
|
5915
|
+
return toolCalls.reduce(
|
|
5916
|
+
(sum, toolCall) => sum + 12 + this.estimateTextTokens(toolCall.toolName) + this.estimateUnknownTokens(toolCall.args),
|
|
5917
|
+
0
|
|
5918
|
+
);
|
|
5919
|
+
}
|
|
5920
|
+
estimateToolResultTokens(toolResults) {
|
|
5921
|
+
if (!toolResults || toolResults.length === 0) return 0;
|
|
5922
|
+
return toolResults.reduce(
|
|
5923
|
+
(sum, toolResult) => sum + 12 + this.estimateTextTokens(toolResult.toolName) + this.estimateUnknownTokens(toolResult.result),
|
|
5924
|
+
0
|
|
5925
|
+
);
|
|
5926
|
+
}
|
|
5927
|
+
estimateMessageTokens(message) {
|
|
5928
|
+
return 6 + this.estimateMessageContentTokens(message.content) + this.estimateToolCallTokens(message.toolCalls) + this.estimateToolResultTokens(message.toolResults);
|
|
5929
|
+
}
|
|
5930
|
+
estimateConversationTokens(messages) {
|
|
5931
|
+
return messages.reduce((sum, message) => sum + this.estimateMessageTokens(message), 0);
|
|
5932
|
+
}
|
|
5933
|
+
estimateConversationBreakdown(messages) {
|
|
5934
|
+
let historyTokens = 0;
|
|
5935
|
+
let toolOutputTokens = 0;
|
|
5936
|
+
for (const message of messages) {
|
|
5937
|
+
const contentTokens = this.estimateMessageContentTokens(message.content);
|
|
5938
|
+
const toolCallTokens = this.estimateToolCallTokens(message.toolCalls);
|
|
5939
|
+
const toolResultTokens = this.estimateToolResultTokens(message.toolResults);
|
|
5940
|
+
const messageTotal = 6 + contentTokens + toolCallTokens + toolResultTokens;
|
|
5941
|
+
if (message.role === "tool") {
|
|
5942
|
+
toolOutputTokens += messageTotal;
|
|
5943
|
+
} else {
|
|
5944
|
+
historyTokens += 6 + contentTokens + toolCallTokens;
|
|
5945
|
+
toolOutputTokens += toolResultTokens;
|
|
5946
|
+
}
|
|
5947
|
+
}
|
|
5948
|
+
return {
|
|
5949
|
+
historyTokens,
|
|
5950
|
+
toolOutputTokens,
|
|
5951
|
+
estimatedInputTokens: historyTokens + toolOutputTokens
|
|
5952
|
+
};
|
|
5953
|
+
}
|
|
5954
|
+
estimateToolDefinitionTokens(localTools, builtinToolSchemas) {
|
|
5955
|
+
const localToolDefinitions = Object.entries(localTools || {}).map(([name, definition]) => ({
|
|
5956
|
+
name,
|
|
5957
|
+
description: definition.description,
|
|
5958
|
+
parametersSchema: definition.parametersSchema
|
|
5959
|
+
}));
|
|
5960
|
+
const payload = [...localToolDefinitions, ...builtinToolSchemas];
|
|
5961
|
+
if (payload.length === 0) return 0;
|
|
5962
|
+
return this.estimateUnknownTokens(payload);
|
|
5963
|
+
}
|
|
5964
|
+
async loadBuiltinToolSchemas(toolIds) {
|
|
5965
|
+
if (!toolIds || toolIds.length === 0) return [];
|
|
5966
|
+
try {
|
|
5967
|
+
const schemas = await this.client.get("/tools/schema", { toolIds });
|
|
5968
|
+
return Array.isArray(schemas) ? schemas : [];
|
|
5969
|
+
} catch {
|
|
5970
|
+
return [];
|
|
5971
|
+
}
|
|
5972
|
+
}
|
|
5973
|
+
buildDefaultCompactInstructions() {
|
|
5974
|
+
return [
|
|
5975
|
+
"Preserve changed files or best candidate paths.",
|
|
5976
|
+
"Preserve verification or test results.",
|
|
5977
|
+
"Preserve the current workflow phase and remaining work.",
|
|
5978
|
+
"Preserve unresolved blockers or follow-up risks.",
|
|
5979
|
+
"Preserve artifact references and offloaded tool outputs."
|
|
5980
|
+
].join(" ");
|
|
5981
|
+
}
|
|
5982
|
+
resolveCompactInstructions(optionInstructions, agent) {
|
|
5983
|
+
if (typeof optionInstructions === "string" && optionInstructions.trim()) {
|
|
5984
|
+
return optionInstructions.trim();
|
|
5985
|
+
}
|
|
5986
|
+
const config = agent.config;
|
|
5987
|
+
if (!config || typeof config !== "object") {
|
|
5988
|
+
return void 0;
|
|
5989
|
+
}
|
|
5990
|
+
const compactInstructions = config.compactInstructions;
|
|
5991
|
+
return typeof compactInstructions === "string" && compactInstructions.trim() ? compactInstructions.trim() : void 0;
|
|
5992
|
+
}
|
|
5993
|
+
extractArtifactReferences(state) {
|
|
5994
|
+
const references = /* @__PURE__ */ new Set();
|
|
5995
|
+
const offloadPrefix = "[Output saved to ";
|
|
5996
|
+
for (const message of state.messages ?? []) {
|
|
5997
|
+
if (!message.toolResults) continue;
|
|
5998
|
+
for (const toolResult of message.toolResults) {
|
|
5999
|
+
if (typeof toolResult.result !== "string") continue;
|
|
6000
|
+
let startIndex = 0;
|
|
6001
|
+
while (startIndex < toolResult.result.length) {
|
|
6002
|
+
const prefixIndex = toolResult.result.indexOf(offloadPrefix, startIndex);
|
|
6003
|
+
if (prefixIndex === -1) break;
|
|
6004
|
+
const pathStart = prefixIndex + offloadPrefix.length;
|
|
6005
|
+
const closingBracket = toolResult.result.indexOf("]", pathStart);
|
|
6006
|
+
if (closingBracket === -1) break;
|
|
6007
|
+
const bracketContent = toolResult.result.slice(pathStart, closingBracket);
|
|
6008
|
+
const separatorIndex = bracketContent.indexOf(" \u2014 ");
|
|
6009
|
+
const pathText = separatorIndex === -1 ? bracketContent.trim() : bracketContent.slice(0, separatorIndex).trim();
|
|
6010
|
+
if (pathText) {
|
|
6011
|
+
references.add(pathText);
|
|
6012
|
+
}
|
|
6013
|
+
startIndex = closingBracket + 1;
|
|
6014
|
+
}
|
|
6015
|
+
}
|
|
6016
|
+
}
|
|
6017
|
+
if (state.planPath) {
|
|
6018
|
+
references.add(state.planPath);
|
|
6019
|
+
}
|
|
6020
|
+
return [...references].slice(0, 8);
|
|
6021
|
+
}
|
|
6022
|
+
buildContextBudgetBreakdown(details) {
|
|
6023
|
+
const conversationBreakdown = this.estimateConversationBreakdown(details.historyMessages);
|
|
6024
|
+
const currentTurnTokens = this.estimateTextTokens(details.currentTurnContent);
|
|
6025
|
+
const toolDefinitionTokens = this.estimateToolDefinitionTokens(
|
|
6026
|
+
details.localTools,
|
|
6027
|
+
details.builtinToolSchemas
|
|
6028
|
+
);
|
|
6029
|
+
const reservedOutputTokens = this.resolveReservedOutputTokens(details.contextLimitTokens);
|
|
6030
|
+
const effectiveInputBudgetTokens = this.resolveEffectiveInputBudgetTokens(details.contextLimitTokens);
|
|
6031
|
+
const summaryTokens = details.summaryText ? this.estimateTextTokens(
|
|
6032
|
+
`${_AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX}
|
|
6033
|
+
|
|
6034
|
+
${details.summaryText}
|
|
6035
|
+
|
|
6036
|
+
Do NOT redo any of the above work.`
|
|
6037
|
+
) : void 0;
|
|
6038
|
+
return {
|
|
6039
|
+
historyTokens: conversationBreakdown.historyTokens,
|
|
6040
|
+
toolOutputTokens: conversationBreakdown.toolOutputTokens,
|
|
6041
|
+
currentTurnTokens,
|
|
6042
|
+
toolDefinitionTokens,
|
|
6043
|
+
...summaryTokens ? { summaryTokens } : {},
|
|
6044
|
+
...reservedOutputTokens ? { reservedOutputTokens } : {},
|
|
6045
|
+
...effectiveInputBudgetTokens ? { effectiveInputBudgetTokens } : {},
|
|
6046
|
+
estimatedInputTokens: conversationBreakdown.estimatedInputTokens + currentTurnTokens + toolDefinitionTokens
|
|
6047
|
+
};
|
|
6048
|
+
}
|
|
6049
|
+
async emitContextCompactionEvent(onContextCompaction, event) {
|
|
6050
|
+
if (!onContextCompaction) return;
|
|
6051
|
+
try {
|
|
6052
|
+
await onContextCompaction(event);
|
|
6053
|
+
} catch {
|
|
6054
|
+
}
|
|
6055
|
+
}
|
|
6056
|
+
async emitContextNotice(onContextNotice, event) {
|
|
6057
|
+
if (!onContextNotice) return;
|
|
6058
|
+
try {
|
|
6059
|
+
await onContextNotice(event);
|
|
6060
|
+
} catch {
|
|
6061
|
+
}
|
|
6062
|
+
}
|
|
6063
|
+
async buildCompactHistoryMessagesWithLifecycle(state, userContent, sessionIndex, details) {
|
|
6064
|
+
const baseEvent = {
|
|
6065
|
+
mode: details.mode,
|
|
6066
|
+
strategy: details.strategy,
|
|
6067
|
+
sessionIndex: sessionIndex + 1,
|
|
6068
|
+
model: details.model,
|
|
6069
|
+
estimatedTokens: details.beforeTokens,
|
|
6070
|
+
thresholdTokens: details.thresholdTokens,
|
|
6071
|
+
contextLimitTokens: details.contextLimitTokens,
|
|
6072
|
+
effectiveInputBudgetTokens: details.effectiveInputBudgetTokens,
|
|
6073
|
+
reservedOutputTokens: details.reservedOutputTokens,
|
|
6074
|
+
beforeTokens: details.beforeTokens,
|
|
6075
|
+
afterTokens: details.afterTokens,
|
|
6076
|
+
breakdown: details.breakdown
|
|
6077
|
+
};
|
|
6078
|
+
await this.emitContextCompactionEvent(details.onContextCompaction, {
|
|
6079
|
+
phase: "start",
|
|
6080
|
+
...baseEvent
|
|
6081
|
+
});
|
|
6082
|
+
const compactMessages = this.buildCompactHistoryMessages(
|
|
6083
|
+
state,
|
|
6084
|
+
userContent,
|
|
6085
|
+
details.compactInstructions,
|
|
6086
|
+
details.mode
|
|
6087
|
+
);
|
|
6088
|
+
await this.emitContextCompactionEvent(details.onContextCompaction, {
|
|
6089
|
+
phase: "complete",
|
|
6090
|
+
...baseEvent
|
|
6091
|
+
});
|
|
6092
|
+
return compactMessages;
|
|
6093
|
+
}
|
|
6094
|
+
buildCompactHistoryMessages(state, userContent, compactInstructions, mode = "auto") {
|
|
6095
|
+
const summary = this.generateCompactSummary(state, compactInstructions);
|
|
6096
|
+
const prefix = mode === "forced" ? _AgentsEndpoint.FORCED_COMPACT_SUMMARY_PREFIX : _AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX;
|
|
6097
|
+
return [
|
|
6098
|
+
{
|
|
6099
|
+
role: "system",
|
|
6100
|
+
content: `${prefix}
|
|
6101
|
+
|
|
6102
|
+
${summary}
|
|
6103
|
+
|
|
6104
|
+
Do NOT redo any of the above work.`
|
|
6105
|
+
},
|
|
6106
|
+
{
|
|
6107
|
+
role: "user",
|
|
6108
|
+
content: userContent
|
|
6109
|
+
}
|
|
6110
|
+
];
|
|
6111
|
+
}
|
|
6112
|
+
isCompactHistoryMessageSet(messages) {
|
|
6113
|
+
if (messages.length === 0) return false;
|
|
6114
|
+
const firstMessage = messages[0];
|
|
6115
|
+
return firstMessage?.role === "system" && typeof firstMessage.content === "string" && (firstMessage.content.startsWith(_AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX) || firstMessage.content.startsWith(_AgentsEndpoint.FORCED_COMPACT_SUMMARY_PREFIX));
|
|
6116
|
+
}
|
|
5659
6117
|
/**
|
|
5660
6118
|
* Generate a compact summary of prior work for continuation context.
|
|
5661
6119
|
* Used when compact mode is enabled to keep token usage low.
|
|
5662
6120
|
*/
|
|
5663
|
-
generateCompactSummary(state) {
|
|
5664
|
-
const
|
|
5665
|
-
|
|
6121
|
+
generateCompactSummary(state, compactInstructions) {
|
|
6122
|
+
const recentSessions = (state.sessions ?? []).slice(-5);
|
|
6123
|
+
const sessionSummaries = recentSessions.map(
|
|
6124
|
+
(session) => `- Session ${session.index}: ${session.stopReason} ($${session.cost.toFixed(4)}) ${session.outputPreview.slice(0, 160)}`
|
|
5666
6125
|
).join("\n");
|
|
6126
|
+
const candidatePaths = Array.from(
|
|
6127
|
+
new Set(
|
|
6128
|
+
[
|
|
6129
|
+
...state.bestCandidatePath ? [state.bestCandidatePath] : [],
|
|
6130
|
+
...state.candidatePaths ?? []
|
|
6131
|
+
].filter(Boolean)
|
|
6132
|
+
)
|
|
6133
|
+
).slice(0, 8);
|
|
6134
|
+
const verificationSummary = state.bestCandidateVerified ? "Latest candidate verified." : state.bestCandidateNeedsVerification ? "Latest candidate still needs verification." : state.lastVerificationPassed ? "Latest verification passed." : state.verificationRequired ? "Verification is still required." : "No verification requirement recorded.";
|
|
6135
|
+
const artifactReferences = this.extractArtifactReferences(state);
|
|
6136
|
+
const pendingNextStep = state.lastStopReason === "complete" ? "Confirm nothing else remains before declaring the task complete." : `Continue the ${state.workflowPhase || "research"} phase without redoing prior work.`;
|
|
6137
|
+
const instructions = compactInstructions || this.buildDefaultCompactInstructions();
|
|
5667
6138
|
return [
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
...
|
|
6139
|
+
"Task + Phase",
|
|
6140
|
+
`- Task: ${state.taskName}`,
|
|
6141
|
+
`- Status: ${state.status}`,
|
|
6142
|
+
`- Workflow phase: ${state.workflowPhase || "research"}`,
|
|
6143
|
+
`- Sessions completed: ${state.sessionCount}`,
|
|
6144
|
+
`- Total cost: $${state.totalCost.toFixed(4)}`,
|
|
6145
|
+
"",
|
|
6146
|
+
"Completed Work",
|
|
6147
|
+
...sessionSummaries ? [sessionSummaries] : ["- No completed session summaries recorded yet."],
|
|
6148
|
+
"",
|
|
6149
|
+
"Changed Files / Candidate Paths",
|
|
6150
|
+
...candidatePaths.length > 0 ? candidatePaths.map((candidatePath) => `- ${candidatePath}`) : ["- No candidate paths recorded yet."],
|
|
6151
|
+
...state.planPath ? [`- ${state.workflowVariant === "external" ? "Report path" : "Plan path"}: ${state.planPath}`] : [],
|
|
6152
|
+
...state.planWritten ? ["- Planning artifact has been written."] : [],
|
|
6153
|
+
...state.bestCandidateReason ? [`- Best candidate rationale: ${state.bestCandidateReason}`] : [],
|
|
6154
|
+
"",
|
|
6155
|
+
"Verification Status",
|
|
6156
|
+
`- ${verificationSummary}`,
|
|
6157
|
+
...state.lastError ? [`- Last error: ${state.lastError}`] : [],
|
|
6158
|
+
"",
|
|
6159
|
+
"Pending Next Step",
|
|
6160
|
+
`- ${pendingNextStep}`,
|
|
5680
6161
|
"",
|
|
5681
|
-
"
|
|
5682
|
-
|
|
6162
|
+
"Artifact / Tool Output References",
|
|
6163
|
+
...artifactReferences.length > 0 ? artifactReferences.map((reference) => `- ${reference}`) : ["- No offloaded artifacts recorded."],
|
|
5683
6164
|
"",
|
|
5684
|
-
"
|
|
5685
|
-
|
|
6165
|
+
"Compaction Instructions",
|
|
6166
|
+
`- ${instructions}`,
|
|
6167
|
+
"",
|
|
6168
|
+
"Last Output (truncated)",
|
|
6169
|
+
(state.lastOutput || "").slice(0, 1800) || "- No final output recorded yet."
|
|
5686
6170
|
].join("\n");
|
|
5687
6171
|
}
|
|
5688
6172
|
/**
|
|
5689
6173
|
* Build messages for a session, injecting progress context for continuation sessions.
|
|
5690
6174
|
* Optionally accepts continuation context for marathon resume scenarios.
|
|
5691
6175
|
*/
|
|
5692
|
-
buildSessionMessages(originalMessage, state, sessionIndex, maxSessions, localToolNames, continuationContext, workflow, builtinToolIds) {
|
|
6176
|
+
async buildSessionMessages(originalMessage, state, sessionIndex, maxSessions, localToolNames, continuationContext, workflow, builtinToolIds, compactionOptions) {
|
|
6177
|
+
const prepared = await this.prepareSessionContext(
|
|
6178
|
+
originalMessage,
|
|
6179
|
+
state,
|
|
6180
|
+
sessionIndex,
|
|
6181
|
+
maxSessions,
|
|
6182
|
+
localToolNames,
|
|
6183
|
+
continuationContext,
|
|
6184
|
+
workflow,
|
|
6185
|
+
builtinToolIds,
|
|
6186
|
+
compactionOptions
|
|
6187
|
+
);
|
|
6188
|
+
return prepared.messages;
|
|
6189
|
+
}
|
|
6190
|
+
async prepareSessionContext(originalMessage, state, sessionIndex, maxSessions, localToolNames, continuationContext, workflow, builtinToolIds, compactionOptions) {
|
|
5693
6191
|
const wf = workflow ?? defaultWorkflow;
|
|
6192
|
+
const compactInstructions = compactionOptions?.compactInstructions;
|
|
6193
|
+
const resolvedStrategy = continuationContext?.compact ? "summary_fallback" : this.resolveCompactStrategy(compactionOptions?.compactStrategy, compactionOptions?.model);
|
|
6194
|
+
const requestContextManagement = resolvedStrategy === "provider_native" && typeof compactionOptions?.autoCompactTokenThreshold === "number" && compactionOptions.autoCompactTokenThreshold > 0 ? {
|
|
6195
|
+
compactStrategy: resolvedStrategy,
|
|
6196
|
+
...compactInstructions ? { compactInstructions } : {},
|
|
6197
|
+
nativeCompaction: {
|
|
6198
|
+
provider: "anthropic",
|
|
6199
|
+
thresholdTokens: compactionOptions.autoCompactTokenThreshold
|
|
6200
|
+
}
|
|
6201
|
+
} : void 0;
|
|
6202
|
+
const maybeEmitToolDefinitionWarning = async (breakdown) => {
|
|
6203
|
+
if (sessionIndex !== 0) return;
|
|
6204
|
+
if (typeof compactionOptions?.contextLimitTokens !== "number" || compactionOptions.contextLimitTokens <= 0) {
|
|
6205
|
+
return;
|
|
6206
|
+
}
|
|
6207
|
+
if (breakdown.toolDefinitionTokens <= 0) return;
|
|
6208
|
+
const ratio = breakdown.toolDefinitionTokens / compactionOptions.contextLimitTokens;
|
|
6209
|
+
if (ratio < 0.1) return;
|
|
6210
|
+
await this.emitContextNotice(compactionOptions?.onContextNotice, {
|
|
6211
|
+
kind: "tool_definitions_warning",
|
|
6212
|
+
sessionIndex: sessionIndex + 1,
|
|
6213
|
+
model: compactionOptions?.model,
|
|
6214
|
+
message: `Tool definitions consume about ${(ratio * 100).toFixed(1)}% of the ${compactionOptions.contextLimitTokens.toLocaleString()} token context window. Reduce the enabled tool set if context pressure remains high.`,
|
|
6215
|
+
estimatedTokens: breakdown.toolDefinitionTokens,
|
|
6216
|
+
contextLimitTokens: compactionOptions.contextLimitTokens,
|
|
6217
|
+
effectiveInputBudgetTokens: breakdown.effectiveInputBudgetTokens,
|
|
6218
|
+
reservedOutputTokens: breakdown.reservedOutputTokens
|
|
6219
|
+
});
|
|
6220
|
+
};
|
|
6221
|
+
const buildNativeCompactionEvent = (mode, breakdown) => {
|
|
6222
|
+
if (resolvedStrategy !== "provider_native" || typeof compactionOptions?.autoCompactTokenThreshold !== "number" || compactionOptions.autoCompactTokenThreshold <= 0 || breakdown.estimatedInputTokens < compactionOptions.autoCompactTokenThreshold) {
|
|
6223
|
+
return void 0;
|
|
6224
|
+
}
|
|
6225
|
+
return {
|
|
6226
|
+
mode,
|
|
6227
|
+
strategy: resolvedStrategy,
|
|
6228
|
+
sessionIndex: sessionIndex + 1,
|
|
6229
|
+
model: compactionOptions?.model,
|
|
6230
|
+
estimatedTokens: breakdown.estimatedInputTokens,
|
|
6231
|
+
thresholdTokens: compactionOptions.autoCompactTokenThreshold,
|
|
6232
|
+
contextLimitTokens: compactionOptions.contextLimitTokens,
|
|
6233
|
+
effectiveInputBudgetTokens: breakdown.effectiveInputBudgetTokens,
|
|
6234
|
+
reservedOutputTokens: breakdown.reservedOutputTokens,
|
|
6235
|
+
beforeTokens: breakdown.estimatedInputTokens,
|
|
6236
|
+
breakdown
|
|
6237
|
+
};
|
|
6238
|
+
};
|
|
5694
6239
|
const currentPhase = wf.phases.find((p) => p.name === state.workflowPhase);
|
|
5695
6240
|
const toolGuidanceLines = currentPhase?.buildToolGuidance(state) ?? [];
|
|
5696
6241
|
const isDeployWorkflow = wf.name === "deploy";
|
|
6242
|
+
const isExternalTask2 = state.workflowVariant === "external";
|
|
6243
|
+
const requiredArtifactPath = state.planPath;
|
|
5697
6244
|
const localToolsBlock = localToolNames?.length ? [
|
|
5698
6245
|
"",
|
|
5699
6246
|
"--- Local Tools ---",
|
|
5700
6247
|
`You have access to tools (${localToolNames.join(", ")}) that execute directly on the user's machine.`,
|
|
5701
|
-
...isDeployWorkflow ? ["Use deploy_sandbox to deploy code and get a live preview URL."] : [
|
|
6248
|
+
...isDeployWorkflow ? ["Use deploy_sandbox to deploy code and get a live preview URL."] : isExternalTask2 ? [
|
|
6249
|
+
"This is a web/external research task. Do not inspect or edit the local repository unless the user explicitly asked for workspace changes.",
|
|
6250
|
+
...requiredArtifactPath ? [`Write the final markdown findings to exactly: ${requiredArtifactPath}`] : []
|
|
6251
|
+
] : [
|
|
5702
6252
|
"Use these tools to inspect the existing repository and make real file edits \u2014 not just code in your response."
|
|
5703
6253
|
],
|
|
5704
6254
|
...toolGuidanceLines,
|
|
5705
|
-
...isDeployWorkflow ? [] : ["Always use write_file to save your output so the user can run it immediately."]
|
|
6255
|
+
...isDeployWorkflow ? [] : isExternalTask2 ? ["Use write_file only if you want to save the final deliverable into the workspace."] : ["Always use write_file to save your output so the user can run it immediately."]
|
|
5706
6256
|
].join("\n") : "";
|
|
5707
6257
|
const builtinToolNames = builtinToolIds?.map((id) => id.replace(/^builtin:/, ""));
|
|
5708
6258
|
const builtinToolsBlock = builtinToolNames?.length ? [
|
|
5709
6259
|
"",
|
|
5710
6260
|
"--- Built-in Tools ---",
|
|
5711
6261
|
`You have access to built-in tools (${builtinToolNames.join(", ")}) for web search, web scraping, image generation, and other capabilities.`,
|
|
5712
|
-
"Use these tools when the task requires information from the web, generating images, or other capabilities beyond local file operations."
|
|
6262
|
+
isExternalTask2 ? "This task targets an external site or source. Start with these built-in tools before considering any local file tools." : "Use these tools when the task requires information from the web, generating images, or other capabilities beyond local file operations."
|
|
5713
6263
|
].join("\n") : "";
|
|
5714
6264
|
const toolsBlock = localToolsBlock + builtinToolsBlock;
|
|
5715
|
-
const bootstrapBlock = state.bootstrapContext ? [
|
|
6265
|
+
const bootstrapBlock = state.bootstrapContext ? [
|
|
6266
|
+
"",
|
|
6267
|
+
isExternalTask2 ? "--- Initial Research Context ---" : "--- Initial Repository Discovery ---",
|
|
6268
|
+
state.bootstrapContext
|
|
6269
|
+
].join("\n") : "";
|
|
5716
6270
|
const phaseBlock = ["", this.buildPhaseInstructions(state, wf)].join("\n");
|
|
5717
6271
|
const candidateBlock = wf.buildCandidateBlock?.(state) ?? "";
|
|
5718
6272
|
const multiSessionInstruction = `This is a multi-session task (session ${sessionIndex + 1}/${maxSessions}). When you have fully completed the task, end your response with TASK_COMPLETE on its own line.`;
|
|
5719
6273
|
if (continuationContext && sessionIndex === 0) {
|
|
5720
6274
|
const defaultContinueMessage = "Continue the task. Review your prior work above and proceed with any remaining work. If everything is already complete, respond with TASK_COMPLETE.";
|
|
5721
6275
|
const userMessage = continuationContext.newUserMessage || defaultContinueMessage;
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
},
|
|
5733
|
-
{
|
|
5734
|
-
role: "user",
|
|
5735
|
-
content: [userMessage, phaseBlock, toolsBlock, bootstrapBlock, candidateBlock, "", multiSessionInstruction].join("\n")
|
|
5736
|
-
}
|
|
5737
|
-
];
|
|
5738
|
-
return messages2;
|
|
5739
|
-
}
|
|
5740
|
-
const messages = [
|
|
6276
|
+
const userContent = [
|
|
6277
|
+
userMessage,
|
|
6278
|
+
phaseBlock,
|
|
6279
|
+
toolsBlock,
|
|
6280
|
+
bootstrapBlock,
|
|
6281
|
+
candidateBlock,
|
|
6282
|
+
"",
|
|
6283
|
+
multiSessionInstruction
|
|
6284
|
+
].join("\n");
|
|
6285
|
+
const fullHistoryMessages = [
|
|
5741
6286
|
...continuationContext.previousMessages,
|
|
5742
6287
|
{
|
|
5743
6288
|
role: "system",
|
|
@@ -5745,14 +6290,79 @@ Do NOT redo any of the above work.`
|
|
|
5745
6290
|
},
|
|
5746
6291
|
{
|
|
5747
6292
|
role: "user",
|
|
5748
|
-
content:
|
|
6293
|
+
content: userContent
|
|
5749
6294
|
}
|
|
5750
6295
|
];
|
|
5751
|
-
|
|
6296
|
+
const summaryText = this.generateCompactSummary(state, compactInstructions);
|
|
6297
|
+
const breakdown = this.buildContextBudgetBreakdown({
|
|
6298
|
+
historyMessages: continuationContext.previousMessages,
|
|
6299
|
+
currentTurnContent: userContent,
|
|
6300
|
+
localTools: compactionOptions?.localTools,
|
|
6301
|
+
builtinToolSchemas: compactionOptions?.builtinToolSchemas || [],
|
|
6302
|
+
summaryText,
|
|
6303
|
+
contextLimitTokens: compactionOptions?.contextLimitTokens
|
|
6304
|
+
});
|
|
6305
|
+
await maybeEmitToolDefinitionWarning(breakdown);
|
|
6306
|
+
if (continuationContext.compact) {
|
|
6307
|
+
return {
|
|
6308
|
+
messages: await this.buildCompactHistoryMessagesWithLifecycle(
|
|
6309
|
+
state,
|
|
6310
|
+
userContent,
|
|
6311
|
+
sessionIndex,
|
|
6312
|
+
{
|
|
6313
|
+
mode: "forced",
|
|
6314
|
+
strategy: "summary_fallback",
|
|
6315
|
+
model: compactionOptions?.model,
|
|
6316
|
+
beforeTokens: breakdown.estimatedInputTokens,
|
|
6317
|
+
afterTokens: (breakdown.summaryTokens || 0) + breakdown.currentTurnTokens + breakdown.toolDefinitionTokens,
|
|
6318
|
+
thresholdTokens: compactionOptions?.autoCompactTokenThreshold,
|
|
6319
|
+
contextLimitTokens: compactionOptions?.contextLimitTokens,
|
|
6320
|
+
effectiveInputBudgetTokens: breakdown.effectiveInputBudgetTokens,
|
|
6321
|
+
reservedOutputTokens: breakdown.reservedOutputTokens,
|
|
6322
|
+
breakdown,
|
|
6323
|
+
onContextCompaction: compactionOptions?.onContextCompaction,
|
|
6324
|
+
compactInstructions
|
|
6325
|
+
}
|
|
6326
|
+
),
|
|
6327
|
+
requestContextManagement
|
|
6328
|
+
};
|
|
6329
|
+
}
|
|
6330
|
+
if (resolvedStrategy === "summary_fallback" && typeof compactionOptions?.autoCompactTokenThreshold === "number" && compactionOptions.autoCompactTokenThreshold > 0 && breakdown.estimatedInputTokens >= compactionOptions.autoCompactTokenThreshold) {
|
|
6331
|
+
return {
|
|
6332
|
+
messages: await this.buildCompactHistoryMessagesWithLifecycle(
|
|
6333
|
+
state,
|
|
6334
|
+
userContent,
|
|
6335
|
+
sessionIndex,
|
|
6336
|
+
{
|
|
6337
|
+
mode: "auto",
|
|
6338
|
+
strategy: "summary_fallback",
|
|
6339
|
+
model: compactionOptions?.model,
|
|
6340
|
+
beforeTokens: breakdown.estimatedInputTokens,
|
|
6341
|
+
afterTokens: (breakdown.summaryTokens || 0) + breakdown.currentTurnTokens + breakdown.toolDefinitionTokens,
|
|
6342
|
+
thresholdTokens: compactionOptions?.autoCompactTokenThreshold,
|
|
6343
|
+
contextLimitTokens: compactionOptions?.contextLimitTokens,
|
|
6344
|
+
effectiveInputBudgetTokens: breakdown.effectiveInputBudgetTokens,
|
|
6345
|
+
reservedOutputTokens: breakdown.reservedOutputTokens,
|
|
6346
|
+
breakdown,
|
|
6347
|
+
onContextCompaction: compactionOptions?.onContextCompaction,
|
|
6348
|
+
compactInstructions
|
|
6349
|
+
}
|
|
6350
|
+
),
|
|
6351
|
+
requestContextManagement
|
|
6352
|
+
};
|
|
6353
|
+
}
|
|
6354
|
+
return {
|
|
6355
|
+
messages: fullHistoryMessages,
|
|
6356
|
+
requestContextManagement,
|
|
6357
|
+
pendingNativeCompactionEvent: buildNativeCompactionEvent("auto", breakdown)
|
|
6358
|
+
};
|
|
5752
6359
|
}
|
|
5753
6360
|
if (sessionIndex === 0) {
|
|
5754
6361
|
const content2 = [originalMessage, phaseBlock, toolsBlock, bootstrapBlock, candidateBlock, "", multiSessionInstruction].join("\n");
|
|
5755
|
-
return
|
|
6362
|
+
return {
|
|
6363
|
+
messages: [{ role: "user", content: content2 }],
|
|
6364
|
+
requestContextManagement
|
|
6365
|
+
};
|
|
5756
6366
|
}
|
|
5757
6367
|
const recentSessions = state.sessions.slice(-5);
|
|
5758
6368
|
const progressSummary = recentSessions.map(
|
|
@@ -5786,10 +6396,49 @@ Do NOT redo any of the above work.`
|
|
|
5786
6396
|
...historyMessages.slice(-MAX_HISTORY_MESSAGES)
|
|
5787
6397
|
];
|
|
5788
6398
|
}
|
|
5789
|
-
|
|
6399
|
+
const summaryText = this.generateCompactSummary(state, compactInstructions);
|
|
6400
|
+
const breakdown = this.buildContextBudgetBreakdown({
|
|
6401
|
+
historyMessages,
|
|
6402
|
+
currentTurnContent: continuationContent,
|
|
6403
|
+
localTools: compactionOptions?.localTools,
|
|
6404
|
+
builtinToolSchemas: compactionOptions?.builtinToolSchemas || [],
|
|
6405
|
+
summaryText,
|
|
6406
|
+
contextLimitTokens: compactionOptions?.contextLimitTokens
|
|
6407
|
+
});
|
|
6408
|
+
await maybeEmitToolDefinitionWarning(breakdown);
|
|
6409
|
+
const messages = [
|
|
5790
6410
|
...historyMessages,
|
|
5791
6411
|
{ role: "user", content: continuationContent }
|
|
5792
6412
|
];
|
|
6413
|
+
if (resolvedStrategy === "summary_fallback" && typeof compactionOptions?.autoCompactTokenThreshold === "number" && compactionOptions.autoCompactTokenThreshold > 0 && breakdown.estimatedInputTokens >= compactionOptions.autoCompactTokenThreshold) {
|
|
6414
|
+
return {
|
|
6415
|
+
messages: await this.buildCompactHistoryMessagesWithLifecycle(
|
|
6416
|
+
state,
|
|
6417
|
+
continuationContent,
|
|
6418
|
+
sessionIndex,
|
|
6419
|
+
{
|
|
6420
|
+
mode: "auto",
|
|
6421
|
+
strategy: "summary_fallback",
|
|
6422
|
+
model: compactionOptions?.model,
|
|
6423
|
+
beforeTokens: breakdown.estimatedInputTokens,
|
|
6424
|
+
afterTokens: (breakdown.summaryTokens || 0) + breakdown.currentTurnTokens + breakdown.toolDefinitionTokens,
|
|
6425
|
+
thresholdTokens: compactionOptions?.autoCompactTokenThreshold,
|
|
6426
|
+
contextLimitTokens: compactionOptions?.contextLimitTokens,
|
|
6427
|
+
effectiveInputBudgetTokens: breakdown.effectiveInputBudgetTokens,
|
|
6428
|
+
reservedOutputTokens: breakdown.reservedOutputTokens,
|
|
6429
|
+
breakdown,
|
|
6430
|
+
onContextCompaction: compactionOptions?.onContextCompaction,
|
|
6431
|
+
compactInstructions
|
|
6432
|
+
}
|
|
6433
|
+
),
|
|
6434
|
+
requestContextManagement
|
|
6435
|
+
};
|
|
6436
|
+
}
|
|
6437
|
+
return {
|
|
6438
|
+
messages,
|
|
6439
|
+
requestContextManagement,
|
|
6440
|
+
pendingNativeCompactionEvent: buildNativeCompactionEvent("auto", breakdown)
|
|
6441
|
+
};
|
|
5793
6442
|
}
|
|
5794
6443
|
const recoveryMessage = this.buildStuckTurnRecoveryMessage(state, wf);
|
|
5795
6444
|
const content = [
|
|
@@ -5809,7 +6458,10 @@ Do NOT redo any of the above work.`
|
|
|
5809
6458
|
"",
|
|
5810
6459
|
"Continue where you left off. Do not redo previous work. If the task is already complete, respond with TASK_COMPLETE."
|
|
5811
6460
|
].join("\n");
|
|
5812
|
-
return
|
|
6461
|
+
return {
|
|
6462
|
+
messages: [{ role: "user", content }],
|
|
6463
|
+
requestContextManagement
|
|
6464
|
+
};
|
|
5813
6465
|
}
|
|
5814
6466
|
/**
|
|
5815
6467
|
* Upsert a record to sync long-task progress to the dashboard.
|
|
@@ -5859,6 +6511,8 @@ Do NOT redo any of the above work.`
|
|
|
5859
6511
|
}
|
|
5860
6512
|
}
|
|
5861
6513
|
};
|
|
6514
|
+
_AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX = "You are continuing a long-running task. Here is a compact summary of prior work:";
|
|
6515
|
+
_AgentsEndpoint.FORCED_COMPACT_SUMMARY_PREFIX = "You are continuing a previously completed task. Here is a summary of prior work:";
|
|
5862
6516
|
/** Stop phrases that indicate the agent considers its task complete. */
|
|
5863
6517
|
_AgentsEndpoint.STOP_PHRASES = [
|
|
5864
6518
|
"DONE:",
|