@runtypelabs/sdk 1.7.3 → 1.8.1
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 +843 -97
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +133 -1
- package/dist/index.d.ts +133 -1
- package/dist/index.js +842 -97
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2840,8 +2840,8 @@ var scaffoldPhase = {
|
|
|
2840
2840
|
].join("\n");
|
|
2841
2841
|
},
|
|
2842
2842
|
interceptToolCall(toolName, _args, _ctx) {
|
|
2843
|
-
const
|
|
2844
|
-
if (
|
|
2843
|
+
const blockedTools2 = ["write_file", "read_file", "search_repo", "glob_files", "tree_directory", "list_directory", "restore_file_checkpoint"];
|
|
2844
|
+
if (blockedTools2.includes(toolName)) {
|
|
2845
2845
|
return [
|
|
2846
2846
|
`Blocked: ${toolName} is not available in deploy mode.`,
|
|
2847
2847
|
"Use deploy_sandbox to deploy your code to a live sandbox instead of writing files to the local repo."
|
|
@@ -2880,6 +2880,7 @@ var deployPhase = {
|
|
|
2880
2880
|
"",
|
|
2881
2881
|
"deploy_sandbox takes:",
|
|
2882
2882
|
" - code: The full source code (written to main.ts/main.js/main.py)",
|
|
2883
|
+
' - files: Additional files (path \u2192 content), e.g. { "public/index.html": "<html>..." }',
|
|
2883
2884
|
' - packageJson: Dependencies as a JSON object, e.g. { "dependencies": { "express": "^4.18.2" } }',
|
|
2884
2885
|
' - language: "typescript" (default), "javascript", or "python"',
|
|
2885
2886
|
" - port: The port your server listens on (default: 3000)",
|
|
@@ -2893,7 +2894,8 @@ var deployPhase = {
|
|
|
2893
2894
|
" 3. Include ALL dependencies in packageJson \u2014 nothing is pre-installed except Node.js built-ins.",
|
|
2894
2895
|
" 4. If the deploy fails, read the error output, fix the code, and call deploy_sandbox again.",
|
|
2895
2896
|
" 5. The sandbox is persistent \u2014 subsequent calls reuse the same sandbox.",
|
|
2896
|
-
" 6. When the deploy succeeds and the preview URL is live, tell the user and end with TASK_COMPLETE."
|
|
2897
|
+
" 6. When the deploy succeeds and the preview URL is live, tell the user and end with TASK_COMPLETE.",
|
|
2898
|
+
" 7. For apps with HTML frontends, use the `files` parameter to write HTML/CSS/JS to separate files and serve them with express.static, rather than embedding HTML in template literals."
|
|
2897
2899
|
].join("\n");
|
|
2898
2900
|
},
|
|
2899
2901
|
buildToolGuidance(_state) {
|
|
@@ -2908,8 +2910,8 @@ var deployPhase = {
|
|
|
2908
2910
|
return false;
|
|
2909
2911
|
},
|
|
2910
2912
|
interceptToolCall(toolName, _args, _ctx) {
|
|
2911
|
-
const
|
|
2912
|
-
if (
|
|
2913
|
+
const blockedTools2 = ["write_file", "read_file", "search_repo", "glob_files", "tree_directory", "list_directory", "restore_file_checkpoint"];
|
|
2914
|
+
if (blockedTools2.includes(toolName)) {
|
|
2913
2915
|
return [
|
|
2914
2916
|
`Blocked: ${toolName} is not available in deploy mode.`,
|
|
2915
2917
|
"Use deploy_sandbox to deploy your code to a live sandbox instead of writing files to the local repo."
|
|
@@ -2917,8 +2919,13 @@ var deployPhase = {
|
|
|
2917
2919
|
}
|
|
2918
2920
|
return void 0;
|
|
2919
2921
|
},
|
|
2920
|
-
canAcceptCompletion(
|
|
2921
|
-
|
|
2922
|
+
canAcceptCompletion(state, trace) {
|
|
2923
|
+
if (trace.entries.some((entry) => entry.startsWith("deploy_sandbox"))) {
|
|
2924
|
+
return true;
|
|
2925
|
+
}
|
|
2926
|
+
return state.sessions.some(
|
|
2927
|
+
(s) => s.actionKeys?.some((key) => key.startsWith("deploy_sandbox"))
|
|
2928
|
+
);
|
|
2922
2929
|
},
|
|
2923
2930
|
buildRecoveryMessage(state) {
|
|
2924
2931
|
const recent = state.sessions.slice(-2);
|
|
@@ -2989,6 +2996,248 @@ var deployWorkflow = {
|
|
|
2989
2996
|
}
|
|
2990
2997
|
};
|
|
2991
2998
|
|
|
2999
|
+
// src/workflows/game-workflow.ts
|
|
3000
|
+
var blockedTools = ["write_file", "read_file", "search_repo", "glob_files", "tree_directory", "list_directory", "restore_file_checkpoint"];
|
|
3001
|
+
function blockLocalTools(toolName) {
|
|
3002
|
+
if (blockedTools.includes(toolName)) {
|
|
3003
|
+
return [
|
|
3004
|
+
`Blocked: ${toolName} is not available in game deploy mode.`,
|
|
3005
|
+
"Use deploy_sandbox to deploy your game to a live sandbox instead of writing files to the local repo."
|
|
3006
|
+
].join(" ");
|
|
3007
|
+
}
|
|
3008
|
+
return void 0;
|
|
3009
|
+
}
|
|
3010
|
+
var designPhase = {
|
|
3011
|
+
name: "design",
|
|
3012
|
+
description: "Understand game requirements and plan the approach",
|
|
3013
|
+
buildInstructions(_state) {
|
|
3014
|
+
return [
|
|
3015
|
+
"--- Workflow Phase: Design ---",
|
|
3016
|
+
"This is a game development task. You will build a game and deploy it to a live sandbox \u2014 you are NOT editing files in the local repository.",
|
|
3017
|
+
"Quickly determine:",
|
|
3018
|
+
" 1. What type of game (3D, 2D, platformer, racing, puzzle, etc.)",
|
|
3019
|
+
" 2. Theme and visual style",
|
|
3020
|
+
" 3. Core game mechanics (controls, scoring, win/lose conditions)",
|
|
3021
|
+
" 4. Tech stack (Three.js, Phaser, vanilla Canvas/WebGL, etc.)",
|
|
3022
|
+
" 5. Any specific dependencies needed",
|
|
3023
|
+
"",
|
|
3024
|
+
"Do NOT inspect the local repo or search for files to edit.",
|
|
3025
|
+
"Do NOT write a plan file.",
|
|
3026
|
+
"Once you understand the requirements, proceed to build and deploy."
|
|
3027
|
+
].join("\n");
|
|
3028
|
+
},
|
|
3029
|
+
buildToolGuidance(_state) {
|
|
3030
|
+
return [
|
|
3031
|
+
"This is a sandbox deploy task \u2014 do NOT use write_file, read_file, search_repo, glob_files, tree_directory, or list_directory.",
|
|
3032
|
+
"Your primary tool is deploy_sandbox. Use it to deploy code to a persistent sandbox and get a live preview URL."
|
|
3033
|
+
];
|
|
3034
|
+
},
|
|
3035
|
+
isComplete(ctx) {
|
|
3036
|
+
return ctx.state.sessions.length >= 1 || ctx.trace.entries.some((entry) => entry.startsWith("deploy_sandbox"));
|
|
3037
|
+
},
|
|
3038
|
+
buildTransitionSummary(_state, _nextPhaseName) {
|
|
3039
|
+
return [
|
|
3040
|
+
"Automatic phase transition: design \u2192 build.",
|
|
3041
|
+
"Requirements understood. Build the game and deploy it using deploy_sandbox.",
|
|
3042
|
+
"",
|
|
3043
|
+
"MANDATORY: Use the `files` parameter for ALL game HTML/JS/CSS.",
|
|
3044
|
+
"code = minimal Express static server (~4 lines), files = game assets.",
|
|
3045
|
+
"NEVER embed HTML inside res.send() template literals."
|
|
3046
|
+
].join("\n");
|
|
3047
|
+
},
|
|
3048
|
+
interceptToolCall(toolName, _args, _ctx) {
|
|
3049
|
+
return blockLocalTools(toolName);
|
|
3050
|
+
},
|
|
3051
|
+
shouldForceEndTurn(snapshot, _ctx) {
|
|
3052
|
+
if (snapshot.pauseCount >= 12) {
|
|
3053
|
+
return "design phase is looping without progressing \u2014 end the turn so the system can advance to build";
|
|
3054
|
+
}
|
|
3055
|
+
return void 0;
|
|
3056
|
+
}
|
|
3057
|
+
};
|
|
3058
|
+
var buildPhase = {
|
|
3059
|
+
name: "build",
|
|
3060
|
+
description: "Build the game and deploy to sandbox with live preview",
|
|
3061
|
+
buildInstructions(_state) {
|
|
3062
|
+
return [
|
|
3063
|
+
"--- Workflow Phase: Build ---",
|
|
3064
|
+
"Build the game and deploy it using the deploy_sandbox tool.",
|
|
3065
|
+
"",
|
|
3066
|
+
"CRITICAL: Multi-file deployment pattern",
|
|
3067
|
+
" You MUST use the `files` parameter for the game HTML/JS/CSS.",
|
|
3068
|
+
" NEVER embed HTML inside a JavaScript template literal in Express `res.send()` \u2014 this WILL break",
|
|
3069
|
+
" because game code uses backticks (template literals) which cause nested backtick corruption.",
|
|
3070
|
+
"",
|
|
3071
|
+
" Correct pattern:",
|
|
3072
|
+
" - code: A minimal Express static file server (~5 lines):",
|
|
3073
|
+
" ```",
|
|
3074
|
+
' const express = require("express");',
|
|
3075
|
+
" const app = express();",
|
|
3076
|
+
' app.use(express.static("public"));',
|
|
3077
|
+
' app.listen(3000, () => console.log("Server running on port 3000"));',
|
|
3078
|
+
" ```",
|
|
3079
|
+
" - files: Your game assets as separate files:",
|
|
3080
|
+
" {",
|
|
3081
|
+
' "public/index.html": "<!DOCTYPE html>...",',
|
|
3082
|
+
' "public/game.js": "// game logic...",',
|
|
3083
|
+
' "public/style.css": "body { margin: 0; }"',
|
|
3084
|
+
" }",
|
|
3085
|
+
"",
|
|
3086
|
+
" WRONG pattern (DO NOT DO THIS):",
|
|
3087
|
+
' - code: `app.get("/", (req, res) => res.send(\\`<html>...game code with backticks...\\`))`',
|
|
3088
|
+
" - This breaks because game code uses template literals inside the res.send template literal.",
|
|
3089
|
+
"",
|
|
3090
|
+
"deploy_sandbox takes:",
|
|
3091
|
+
" - code: The server source code (written to main.ts/main.js/main.py)",
|
|
3092
|
+
' - files: Additional files (path \u2192 content), e.g. { "public/index.html": "<html>..." }',
|
|
3093
|
+
' - packageJson: Dependencies as a JSON object, e.g. { "dependencies": { "express": "^4.18.2", "three": "^0.170.0" } }',
|
|
3094
|
+
' - language: "javascript" (recommended for games), "typescript", or "python"',
|
|
3095
|
+
" - port: The port your server listens on (default: 3000)",
|
|
3096
|
+
" - startCommand: Custom start command (auto-detected by default)",
|
|
3097
|
+
"",
|
|
3098
|
+
"Guidelines:",
|
|
3099
|
+
' 1. Use `language: "javascript"` \u2014 simpler for static file servers.',
|
|
3100
|
+
" 2. The `code` param should be a minimal Express static server. ALL game code goes in `files`.",
|
|
3101
|
+
" 3. Include ALL dependencies in packageJson (express, three, phaser, etc.).",
|
|
3102
|
+
" 4. If the deploy fails, read the error output, fix the code, and call deploy_sandbox again.",
|
|
3103
|
+
" 5. The sandbox is persistent \u2014 subsequent calls reuse the same sandbox.",
|
|
3104
|
+
" 6. Load game libraries from CDN in your HTML (e.g., Three.js from unpkg/cdnjs) OR include them in packageJson.",
|
|
3105
|
+
" 7. Make the game fullscreen by default (width: 100vw, height: 100vh, no margin/padding on body).",
|
|
3106
|
+
"",
|
|
3107
|
+
"STARTER TEMPLATES \u2014 Copy and customize based on the game type:",
|
|
3108
|
+
"",
|
|
3109
|
+
"ALL templates use this server code:",
|
|
3110
|
+
' code: `const express = require("express"); const app = express(); app.use(express.static("public")); app.listen(3000, () => console.log("Game server running on port 3000"));`',
|
|
3111
|
+
' packageJson: { "dependencies": { "express": "^4.18.2" } }',
|
|
3112
|
+
' language: "javascript"',
|
|
3113
|
+
"",
|
|
3114
|
+
"THREE.JS (3D games \u2014 racing, flying, FPS, etc.):",
|
|
3115
|
+
" files: {",
|
|
3116
|
+
' "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>',
|
|
3117
|
+
' "public/game.js": Import three, create scene/camera/renderer, game loop with requestAnimationFrame',
|
|
3118
|
+
" }",
|
|
3119
|
+
"",
|
|
3120
|
+
"PHASER (2D games \u2014 platformers, shooters, puzzles):",
|
|
3121
|
+
" files: {",
|
|
3122
|
+
' "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>',
|
|
3123
|
+
' "public/game.js": Phaser.Game config with scenes, preload/create/update lifecycle',
|
|
3124
|
+
" }",
|
|
3125
|
+
"",
|
|
3126
|
+
"CANVAS 2D (simple games \u2014 pong, snake, breakout):",
|
|
3127
|
+
" files: {",
|
|
3128
|
+
' "public/index.html": HTML with <canvas id="game" width="800" height="600"></canvas> and <script src="game.js"></script>',
|
|
3129
|
+
' "public/game.js": Get canvas context, game loop with requestAnimationFrame, draw calls',
|
|
3130
|
+
" }"
|
|
3131
|
+
].join("\n");
|
|
3132
|
+
},
|
|
3133
|
+
buildToolGuidance(_state) {
|
|
3134
|
+
return [
|
|
3135
|
+
"Your primary tool is deploy_sandbox. Call it with code (Express static server), files (game HTML/JS/CSS), and packageJson.",
|
|
3136
|
+
"ALWAYS use the `files` parameter for game HTML, JavaScript, and CSS \u2014 NEVER embed them in template literals.",
|
|
3137
|
+
"Do NOT use write_file, read_file, search_repo, glob_files, tree_directory, or list_directory.",
|
|
3138
|
+
"You may use run_sandbox_code for quick one-off tests if needed."
|
|
3139
|
+
];
|
|
3140
|
+
},
|
|
3141
|
+
isComplete() {
|
|
3142
|
+
return false;
|
|
3143
|
+
},
|
|
3144
|
+
interceptToolCall(toolName, args, _ctx) {
|
|
3145
|
+
const localBlock = blockLocalTools(toolName);
|
|
3146
|
+
if (localBlock) return localBlock;
|
|
3147
|
+
if (toolName === "deploy_sandbox" && args) {
|
|
3148
|
+
const hasFiles = args.files && typeof args.files === "object" && Object.keys(args.files).length > 0;
|
|
3149
|
+
const codeStr = typeof args.code === "string" ? args.code : "";
|
|
3150
|
+
const embedsHtml = /res\.send\s*\(/.test(codeStr) && /<html|<body|<script/i.test(codeStr);
|
|
3151
|
+
if (!hasFiles && embedsHtml) {
|
|
3152
|
+
return [
|
|
3153
|
+
"Blocked: You are embedding HTML inside res.send() \u2014 this will break due to nested template literals.",
|
|
3154
|
+
"You MUST use the `files` parameter. Put your game HTML/JS/CSS in files and use a minimal Express static server for code.",
|
|
3155
|
+
'Example: code: `const express = require("express"); const app = express(); app.use(express.static("public")); app.listen(3000);`',
|
|
3156
|
+
'files: { "public/index.html": "<!DOCTYPE html>...", "public/game.js": "// game code" }'
|
|
3157
|
+
].join("\n");
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
return void 0;
|
|
3161
|
+
},
|
|
3162
|
+
canAcceptCompletion(_state, trace) {
|
|
3163
|
+
return trace.entries.some((entry) => entry.startsWith("deploy_sandbox"));
|
|
3164
|
+
},
|
|
3165
|
+
buildRecoveryMessage(state) {
|
|
3166
|
+
const recent = state.sessions.slice(-2);
|
|
3167
|
+
if (recent.length < 2) return void 0;
|
|
3168
|
+
const noProgress = recent.every(
|
|
3169
|
+
(s) => s.hadTextOutput === false && s.wroteFiles === false
|
|
3170
|
+
);
|
|
3171
|
+
if (!noProgress) return void 0;
|
|
3172
|
+
return [
|
|
3173
|
+
"Recovery instruction:",
|
|
3174
|
+
"You should be deploying the game using deploy_sandbox with the `files` parameter.",
|
|
3175
|
+
"Use `code` for a minimal Express static server and `files` for game HTML/JS/CSS.",
|
|
3176
|
+
"NEVER embed HTML in a JavaScript template literal \u2014 use separate files."
|
|
3177
|
+
].join("\n");
|
|
3178
|
+
},
|
|
3179
|
+
shouldForceEndTurn(snapshot, _ctx) {
|
|
3180
|
+
if (snapshot.consecutiveDiscoveryPauseCount >= 8) {
|
|
3181
|
+
return "build phase is looping on discovery tools instead of calling deploy_sandbox";
|
|
3182
|
+
}
|
|
3183
|
+
if (snapshot.actionKeyCount >= 4) {
|
|
3184
|
+
return `the same action repeated ${snapshot.actionKeyCount} times \u2014 try a different approach`;
|
|
3185
|
+
}
|
|
3186
|
+
return void 0;
|
|
3187
|
+
}
|
|
3188
|
+
};
|
|
3189
|
+
var verifyPhase = {
|
|
3190
|
+
name: "verify",
|
|
3191
|
+
description: "Verify the game is running and playable",
|
|
3192
|
+
buildInstructions(_state) {
|
|
3193
|
+
return [
|
|
3194
|
+
"--- Workflow Phase: Verify ---",
|
|
3195
|
+
"The game has been deployed. Verify it is working:",
|
|
3196
|
+
" 1. Check the server log for errors. If there are errors, fix and redeploy using deploy_sandbox.",
|
|
3197
|
+
" 2. Tell the user the preview URL so they can play the game.",
|
|
3198
|
+
" 3. End with TASK_COMPLETE.",
|
|
3199
|
+
"",
|
|
3200
|
+
"If the user reports issues, fix the code and redeploy."
|
|
3201
|
+
].join("\n");
|
|
3202
|
+
},
|
|
3203
|
+
buildToolGuidance(_state) {
|
|
3204
|
+
return [
|
|
3205
|
+
"Use deploy_sandbox to redeploy if fixes are needed.",
|
|
3206
|
+
"Do NOT use local file system tools.",
|
|
3207
|
+
"When the game is working, tell the user the preview URL and end with TASK_COMPLETE."
|
|
3208
|
+
];
|
|
3209
|
+
},
|
|
3210
|
+
isComplete() {
|
|
3211
|
+
return false;
|
|
3212
|
+
},
|
|
3213
|
+
interceptToolCall(toolName, _args, _ctx) {
|
|
3214
|
+
return blockLocalTools(toolName);
|
|
3215
|
+
},
|
|
3216
|
+
canAcceptCompletion(_state, trace) {
|
|
3217
|
+
return trace.entries.some((entry) => entry.startsWith("deploy_sandbox"));
|
|
3218
|
+
},
|
|
3219
|
+
shouldForceEndTurn(snapshot, _ctx) {
|
|
3220
|
+
if (snapshot.actionKeyCount >= 4) {
|
|
3221
|
+
return `the same action repeated ${snapshot.actionKeyCount} times \u2014 try a different approach`;
|
|
3222
|
+
}
|
|
3223
|
+
return void 0;
|
|
3224
|
+
}
|
|
3225
|
+
};
|
|
3226
|
+
function classifyVariant3() {
|
|
3227
|
+
return "game";
|
|
3228
|
+
}
|
|
3229
|
+
var gameWorkflow = {
|
|
3230
|
+
name: "game",
|
|
3231
|
+
phases: [designPhase, buildPhase, verifyPhase],
|
|
3232
|
+
classifyVariant: classifyVariant3,
|
|
3233
|
+
async generateBootstrapContext() {
|
|
3234
|
+
return void 0;
|
|
3235
|
+
},
|
|
3236
|
+
buildCandidateBlock() {
|
|
3237
|
+
return "";
|
|
3238
|
+
}
|
|
3239
|
+
};
|
|
3240
|
+
|
|
2992
3241
|
// src/endpoints.ts
|
|
2993
3242
|
var FlowsEndpoint = class {
|
|
2994
3243
|
constructor(client) {
|
|
@@ -5204,6 +5453,8 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5204
5453
|
this.updateWorkflowPhase(state, this.createEmptyToolTrace(), workflow);
|
|
5205
5454
|
let recordId;
|
|
5206
5455
|
const localToolNames = options.localTools ? Object.keys(options.localTools) : void 0;
|
|
5456
|
+
const builtinToolSchemas = await this.loadBuiltinToolSchemas(options.toolIds);
|
|
5457
|
+
const compactInstructions = this.resolveCompactInstructions(options.compactInstructions, agent);
|
|
5207
5458
|
if (!options.previousMessages) {
|
|
5208
5459
|
if (workflow.generateBootstrapContext) {
|
|
5209
5460
|
state.bootstrapContext = await workflow.generateBootstrapContext(
|
|
@@ -5237,65 +5488,101 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5237
5488
|
if (session === 0 && !options.previousMessages) {
|
|
5238
5489
|
state.originalMessage = options.message;
|
|
5239
5490
|
}
|
|
5240
|
-
const
|
|
5491
|
+
const preparedSession = await this.prepareSessionContext(
|
|
5241
5492
|
options.message,
|
|
5242
5493
|
state,
|
|
5243
5494
|
session,
|
|
5244
5495
|
maxSessions,
|
|
5245
5496
|
localToolNames,
|
|
5246
5497
|
continuationContext,
|
|
5247
|
-
workflow
|
|
5498
|
+
workflow,
|
|
5499
|
+
options.toolIds,
|
|
5500
|
+
{
|
|
5501
|
+
autoCompactTokenThreshold: options.autoCompactTokenThreshold,
|
|
5502
|
+
contextLimitTokens: options.contextLimitTokens,
|
|
5503
|
+
model: options.model,
|
|
5504
|
+
compactStrategy: options.compactStrategy,
|
|
5505
|
+
compactInstructions,
|
|
5506
|
+
localTools: options.localTools,
|
|
5507
|
+
builtinToolSchemas,
|
|
5508
|
+
onContextCompaction: options.onContextCompaction,
|
|
5509
|
+
onContextNotice: options.onContextNotice
|
|
5510
|
+
}
|
|
5248
5511
|
);
|
|
5512
|
+
const {
|
|
5513
|
+
messages,
|
|
5514
|
+
requestContextManagement,
|
|
5515
|
+
pendingNativeCompactionEvent
|
|
5516
|
+
} = preparedSession;
|
|
5249
5517
|
let sessionResult;
|
|
5250
5518
|
const sessionData = {
|
|
5251
5519
|
messages,
|
|
5252
5520
|
debugMode: options.debugMode,
|
|
5253
|
-
model: options.model
|
|
5521
|
+
model: options.model,
|
|
5522
|
+
...options.toolIds?.length ? { tools: { toolIds: options.toolIds } } : {},
|
|
5523
|
+
...requestContextManagement ? { contextManagement: requestContextManagement } : {}
|
|
5254
5524
|
};
|
|
5255
5525
|
let sessionToolMessages = [];
|
|
5256
|
-
if (
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5526
|
+
if (pendingNativeCompactionEvent) {
|
|
5527
|
+
await this.emitContextCompactionEvent(options.onContextCompaction, {
|
|
5528
|
+
phase: "start",
|
|
5529
|
+
...pendingNativeCompactionEvent
|
|
5530
|
+
});
|
|
5531
|
+
}
|
|
5532
|
+
try {
|
|
5533
|
+
if (useStream && options.localTools) {
|
|
5534
|
+
const localToolResult = await this.executeWithLocalTools(
|
|
5535
|
+
id,
|
|
5536
|
+
sessionData,
|
|
5537
|
+
sessionLocalTools || options.localTools,
|
|
5538
|
+
sessionCallbacks,
|
|
5539
|
+
{
|
|
5540
|
+
onLocalToolResult: this.createLocalToolLoopGuard(state, sessionTrace, workflow)
|
|
5541
|
+
},
|
|
5542
|
+
state.taskName
|
|
5543
|
+
);
|
|
5544
|
+
if (!localToolResult) {
|
|
5545
|
+
throw new Error("Agent stream ended without a complete event");
|
|
5546
|
+
}
|
|
5547
|
+
const { completeEvent, toolMessages: captured } = localToolResult;
|
|
5548
|
+
sessionToolMessages = captured;
|
|
5549
|
+
sessionResult = {
|
|
5550
|
+
success: completeEvent.success,
|
|
5551
|
+
result: completeEvent.finalOutput || "",
|
|
5552
|
+
iterations: completeEvent.iterations,
|
|
5553
|
+
totalCost: completeEvent.totalCost || 0,
|
|
5554
|
+
totalTokens: completeEvent.totalTokens,
|
|
5555
|
+
stopReason: completeEvent.stopReason,
|
|
5556
|
+
error: completeEvent.error
|
|
5557
|
+
};
|
|
5558
|
+
} else if (useStream && options.streamCallbacks) {
|
|
5559
|
+
const completeEvent = await this.executeWithCallbacks(
|
|
5560
|
+
id,
|
|
5561
|
+
sessionData,
|
|
5562
|
+
sessionCallbacks || options.streamCallbacks
|
|
5563
|
+
);
|
|
5564
|
+
if (!completeEvent) {
|
|
5565
|
+
throw new Error("Agent stream ended without a complete event");
|
|
5566
|
+
}
|
|
5567
|
+
sessionResult = {
|
|
5568
|
+
success: completeEvent.success,
|
|
5569
|
+
result: completeEvent.finalOutput || "",
|
|
5570
|
+
iterations: completeEvent.iterations,
|
|
5571
|
+
totalCost: completeEvent.totalCost || 0,
|
|
5572
|
+
totalTokens: completeEvent.totalTokens,
|
|
5573
|
+
stopReason: completeEvent.stopReason,
|
|
5574
|
+
error: completeEvent.error
|
|
5575
|
+
};
|
|
5576
|
+
} else {
|
|
5577
|
+
sessionResult = await this.execute(id, sessionData);
|
|
5269
5578
|
}
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
totalCost: completeEvent.totalCost || 0,
|
|
5277
|
-
stopReason: completeEvent.stopReason,
|
|
5278
|
-
error: completeEvent.error
|
|
5279
|
-
};
|
|
5280
|
-
} else if (useStream && options.streamCallbacks) {
|
|
5281
|
-
const completeEvent = await this.executeWithCallbacks(
|
|
5282
|
-
id,
|
|
5283
|
-
sessionData,
|
|
5284
|
-
sessionCallbacks || options.streamCallbacks
|
|
5285
|
-
);
|
|
5286
|
-
if (!completeEvent) {
|
|
5287
|
-
throw new Error("Agent stream ended without a complete event");
|
|
5579
|
+
} finally {
|
|
5580
|
+
if (pendingNativeCompactionEvent) {
|
|
5581
|
+
await this.emitContextCompactionEvent(options.onContextCompaction, {
|
|
5582
|
+
phase: "complete",
|
|
5583
|
+
...pendingNativeCompactionEvent
|
|
5584
|
+
});
|
|
5288
5585
|
}
|
|
5289
|
-
sessionResult = {
|
|
5290
|
-
success: completeEvent.success,
|
|
5291
|
-
result: completeEvent.finalOutput || "",
|
|
5292
|
-
iterations: completeEvent.iterations,
|
|
5293
|
-
totalCost: completeEvent.totalCost || 0,
|
|
5294
|
-
stopReason: completeEvent.stopReason,
|
|
5295
|
-
error: completeEvent.error
|
|
5296
|
-
};
|
|
5297
|
-
} else {
|
|
5298
|
-
sessionResult = await this.execute(id, sessionData);
|
|
5299
5586
|
}
|
|
5300
5587
|
const toolTraceSummary = this.buildToolTraceSummary(sessionTrace);
|
|
5301
5588
|
const effectiveSessionOutput = this.buildEffectiveSessionOutput(
|
|
@@ -5303,8 +5590,15 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5303
5590
|
toolTraceSummary
|
|
5304
5591
|
);
|
|
5305
5592
|
const sessionCost = sessionResult.totalCost;
|
|
5593
|
+
const sessionTokens = sessionResult.totalTokens;
|
|
5306
5594
|
state.sessionCount = session + 1;
|
|
5307
5595
|
state.totalCost += sessionCost;
|
|
5596
|
+
if (sessionTokens) {
|
|
5597
|
+
state.totalTokens = {
|
|
5598
|
+
input: (state.totalTokens?.input || 0) + sessionTokens.input,
|
|
5599
|
+
output: (state.totalTokens?.output || 0) + sessionTokens.output
|
|
5600
|
+
};
|
|
5601
|
+
}
|
|
5308
5602
|
state.lastOutput = effectiveSessionOutput;
|
|
5309
5603
|
state.lastError = sessionResult.stopReason === "error" ? sessionResult.error || "Agent session ended with an error." : void 0;
|
|
5310
5604
|
state.lastStopReason = sessionResult.stopReason;
|
|
@@ -5312,6 +5606,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5312
5606
|
state.sessions.push({
|
|
5313
5607
|
index: session + 1,
|
|
5314
5608
|
cost: sessionCost,
|
|
5609
|
+
totalTokens: sessionTokens,
|
|
5315
5610
|
iterations: sessionResult.iterations,
|
|
5316
5611
|
stopReason: sessionResult.stopReason,
|
|
5317
5612
|
outputPreview: effectiveSessionOutput.slice(0, 300),
|
|
@@ -5374,7 +5669,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5374
5669
|
}
|
|
5375
5670
|
}
|
|
5376
5671
|
if (!state.messages) state.messages = [];
|
|
5377
|
-
if (
|
|
5672
|
+
if (this.isCompactHistoryMessageSet(messages)) {
|
|
5673
|
+
state.messages = [...messages];
|
|
5674
|
+
} else if (state.messages.length > 0 && messages.length > state.messages.length) {
|
|
5378
5675
|
const newMessages = messages.slice(state.messages.length);
|
|
5379
5676
|
state.messages.push(...newMessages);
|
|
5380
5677
|
} else {
|
|
@@ -5425,6 +5722,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5425
5722
|
status: state.status,
|
|
5426
5723
|
sessionCount: state.sessionCount,
|
|
5427
5724
|
totalCost: state.totalCost,
|
|
5725
|
+
totalTokens: state.totalTokens,
|
|
5428
5726
|
lastOutput: state.lastOutput,
|
|
5429
5727
|
sessions: state.sessions,
|
|
5430
5728
|
recordId
|
|
@@ -5438,45 +5736,383 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5438
5736
|
const upper = output.toUpperCase();
|
|
5439
5737
|
return _AgentsEndpoint.STOP_PHRASES.some((phrase) => upper.includes(phrase.toUpperCase()));
|
|
5440
5738
|
}
|
|
5739
|
+
resolveReservedOutputTokens(contextLimitTokens) {
|
|
5740
|
+
if (typeof contextLimitTokens !== "number" || !Number.isFinite(contextLimitTokens) || contextLimitTokens <= 0) {
|
|
5741
|
+
return void 0;
|
|
5742
|
+
}
|
|
5743
|
+
return Math.max(8e3, Math.min(64e3, Math.floor(contextLimitTokens * 0.15)));
|
|
5744
|
+
}
|
|
5745
|
+
resolveEffectiveInputBudgetTokens(contextLimitTokens) {
|
|
5746
|
+
if (typeof contextLimitTokens !== "number" || !Number.isFinite(contextLimitTokens) || contextLimitTokens <= 0) {
|
|
5747
|
+
return void 0;
|
|
5748
|
+
}
|
|
5749
|
+
const reservedOutputTokens = this.resolveReservedOutputTokens(contextLimitTokens) ?? 0;
|
|
5750
|
+
return Math.max(1, contextLimitTokens - reservedOutputTokens);
|
|
5751
|
+
}
|
|
5752
|
+
resolveCompactStrategy(strategy, modelId) {
|
|
5753
|
+
if (strategy === "summary_fallback") {
|
|
5754
|
+
return "summary_fallback";
|
|
5755
|
+
}
|
|
5756
|
+
const normalizedModelId = modelId?.trim().toLowerCase() ?? "";
|
|
5757
|
+
const supportsAnthropicNativeCompaction = normalizedModelId.includes("claude") || normalizedModelId.includes("anthropic");
|
|
5758
|
+
if (strategy === "provider_native") {
|
|
5759
|
+
return supportsAnthropicNativeCompaction ? "provider_native" : "summary_fallback";
|
|
5760
|
+
}
|
|
5761
|
+
return supportsAnthropicNativeCompaction ? "provider_native" : "summary_fallback";
|
|
5762
|
+
}
|
|
5763
|
+
estimateTextTokens(text) {
|
|
5764
|
+
if (!text) return 0;
|
|
5765
|
+
return Math.ceil(text.length / 4);
|
|
5766
|
+
}
|
|
5767
|
+
estimateUnknownTokens(value) {
|
|
5768
|
+
if (typeof value === "string") return this.estimateTextTokens(value);
|
|
5769
|
+
try {
|
|
5770
|
+
return this.estimateTextTokens(JSON.stringify(value));
|
|
5771
|
+
} catch {
|
|
5772
|
+
return 0;
|
|
5773
|
+
}
|
|
5774
|
+
}
|
|
5775
|
+
estimateMessageContentTokens(content) {
|
|
5776
|
+
if (typeof content === "string") return this.estimateTextTokens(content);
|
|
5777
|
+
return content.reduce((total, part) => {
|
|
5778
|
+
if (typeof part.text === "string") total += this.estimateTextTokens(part.text);
|
|
5779
|
+
if (typeof part.image === "string") total += 85;
|
|
5780
|
+
if (typeof part.data === "string") {
|
|
5781
|
+
total += Math.min(200, this.estimateTextTokens(part.data));
|
|
5782
|
+
}
|
|
5783
|
+
return total + 4;
|
|
5784
|
+
}, 0);
|
|
5785
|
+
}
|
|
5786
|
+
estimateToolCallTokens(toolCalls) {
|
|
5787
|
+
if (!toolCalls || toolCalls.length === 0) return 0;
|
|
5788
|
+
return toolCalls.reduce(
|
|
5789
|
+
(sum, toolCall) => sum + 12 + this.estimateTextTokens(toolCall.toolName) + this.estimateUnknownTokens(toolCall.args),
|
|
5790
|
+
0
|
|
5791
|
+
);
|
|
5792
|
+
}
|
|
5793
|
+
estimateToolResultTokens(toolResults) {
|
|
5794
|
+
if (!toolResults || toolResults.length === 0) return 0;
|
|
5795
|
+
return toolResults.reduce(
|
|
5796
|
+
(sum, toolResult) => sum + 12 + this.estimateTextTokens(toolResult.toolName) + this.estimateUnknownTokens(toolResult.result),
|
|
5797
|
+
0
|
|
5798
|
+
);
|
|
5799
|
+
}
|
|
5800
|
+
estimateMessageTokens(message) {
|
|
5801
|
+
return 6 + this.estimateMessageContentTokens(message.content) + this.estimateToolCallTokens(message.toolCalls) + this.estimateToolResultTokens(message.toolResults);
|
|
5802
|
+
}
|
|
5803
|
+
estimateConversationTokens(messages) {
|
|
5804
|
+
return messages.reduce((sum, message) => sum + this.estimateMessageTokens(message), 0);
|
|
5805
|
+
}
|
|
5806
|
+
estimateConversationBreakdown(messages) {
|
|
5807
|
+
let historyTokens = 0;
|
|
5808
|
+
let toolOutputTokens = 0;
|
|
5809
|
+
for (const message of messages) {
|
|
5810
|
+
const contentTokens = this.estimateMessageContentTokens(message.content);
|
|
5811
|
+
const toolCallTokens = this.estimateToolCallTokens(message.toolCalls);
|
|
5812
|
+
const toolResultTokens = this.estimateToolResultTokens(message.toolResults);
|
|
5813
|
+
const messageTotal = 6 + contentTokens + toolCallTokens + toolResultTokens;
|
|
5814
|
+
if (message.role === "tool") {
|
|
5815
|
+
toolOutputTokens += messageTotal;
|
|
5816
|
+
} else {
|
|
5817
|
+
historyTokens += 6 + contentTokens + toolCallTokens;
|
|
5818
|
+
toolOutputTokens += toolResultTokens;
|
|
5819
|
+
}
|
|
5820
|
+
}
|
|
5821
|
+
return {
|
|
5822
|
+
historyTokens,
|
|
5823
|
+
toolOutputTokens,
|
|
5824
|
+
estimatedInputTokens: historyTokens + toolOutputTokens
|
|
5825
|
+
};
|
|
5826
|
+
}
|
|
5827
|
+
estimateToolDefinitionTokens(localTools, builtinToolSchemas) {
|
|
5828
|
+
const localToolDefinitions = Object.entries(localTools || {}).map(([name, definition]) => ({
|
|
5829
|
+
name,
|
|
5830
|
+
description: definition.description,
|
|
5831
|
+
parametersSchema: definition.parametersSchema
|
|
5832
|
+
}));
|
|
5833
|
+
const payload = [...localToolDefinitions, ...builtinToolSchemas];
|
|
5834
|
+
if (payload.length === 0) return 0;
|
|
5835
|
+
return this.estimateUnknownTokens(payload);
|
|
5836
|
+
}
|
|
5837
|
+
async loadBuiltinToolSchemas(toolIds) {
|
|
5838
|
+
if (!toolIds || toolIds.length === 0) return [];
|
|
5839
|
+
try {
|
|
5840
|
+
const schemas = await this.client.get("/tools/schema", { toolIds });
|
|
5841
|
+
return Array.isArray(schemas) ? schemas : [];
|
|
5842
|
+
} catch {
|
|
5843
|
+
return [];
|
|
5844
|
+
}
|
|
5845
|
+
}
|
|
5846
|
+
buildDefaultCompactInstructions() {
|
|
5847
|
+
return [
|
|
5848
|
+
"Preserve changed files or best candidate paths.",
|
|
5849
|
+
"Preserve verification or test results.",
|
|
5850
|
+
"Preserve the current workflow phase and remaining work.",
|
|
5851
|
+
"Preserve unresolved blockers or follow-up risks.",
|
|
5852
|
+
"Preserve artifact references and offloaded tool outputs."
|
|
5853
|
+
].join(" ");
|
|
5854
|
+
}
|
|
5855
|
+
resolveCompactInstructions(optionInstructions, agent) {
|
|
5856
|
+
if (typeof optionInstructions === "string" && optionInstructions.trim()) {
|
|
5857
|
+
return optionInstructions.trim();
|
|
5858
|
+
}
|
|
5859
|
+
const config = agent.config;
|
|
5860
|
+
if (!config || typeof config !== "object") {
|
|
5861
|
+
return void 0;
|
|
5862
|
+
}
|
|
5863
|
+
const compactInstructions = config.compactInstructions;
|
|
5864
|
+
return typeof compactInstructions === "string" && compactInstructions.trim() ? compactInstructions.trim() : void 0;
|
|
5865
|
+
}
|
|
5866
|
+
extractArtifactReferences(state) {
|
|
5867
|
+
const references = /* @__PURE__ */ new Set();
|
|
5868
|
+
const offloadPrefix = "[Output saved to ";
|
|
5869
|
+
for (const message of state.messages ?? []) {
|
|
5870
|
+
if (!message.toolResults) continue;
|
|
5871
|
+
for (const toolResult of message.toolResults) {
|
|
5872
|
+
if (typeof toolResult.result !== "string") continue;
|
|
5873
|
+
let startIndex = 0;
|
|
5874
|
+
while (startIndex < toolResult.result.length) {
|
|
5875
|
+
const prefixIndex = toolResult.result.indexOf(offloadPrefix, startIndex);
|
|
5876
|
+
if (prefixIndex === -1) break;
|
|
5877
|
+
const pathStart = prefixIndex + offloadPrefix.length;
|
|
5878
|
+
const closingBracket = toolResult.result.indexOf("]", pathStart);
|
|
5879
|
+
if (closingBracket === -1) break;
|
|
5880
|
+
const bracketContent = toolResult.result.slice(pathStart, closingBracket);
|
|
5881
|
+
const separatorIndex = bracketContent.indexOf(" \u2014 ");
|
|
5882
|
+
const pathText = separatorIndex === -1 ? bracketContent.trim() : bracketContent.slice(0, separatorIndex).trim();
|
|
5883
|
+
if (pathText) {
|
|
5884
|
+
references.add(pathText);
|
|
5885
|
+
}
|
|
5886
|
+
startIndex = closingBracket + 1;
|
|
5887
|
+
}
|
|
5888
|
+
}
|
|
5889
|
+
}
|
|
5890
|
+
if (state.planPath) {
|
|
5891
|
+
references.add(state.planPath);
|
|
5892
|
+
}
|
|
5893
|
+
return [...references].slice(0, 8);
|
|
5894
|
+
}
|
|
5895
|
+
buildContextBudgetBreakdown(details) {
|
|
5896
|
+
const conversationBreakdown = this.estimateConversationBreakdown(details.historyMessages);
|
|
5897
|
+
const currentTurnTokens = this.estimateTextTokens(details.currentTurnContent);
|
|
5898
|
+
const toolDefinitionTokens = this.estimateToolDefinitionTokens(
|
|
5899
|
+
details.localTools,
|
|
5900
|
+
details.builtinToolSchemas
|
|
5901
|
+
);
|
|
5902
|
+
const reservedOutputTokens = this.resolveReservedOutputTokens(details.contextLimitTokens);
|
|
5903
|
+
const effectiveInputBudgetTokens = this.resolveEffectiveInputBudgetTokens(details.contextLimitTokens);
|
|
5904
|
+
const summaryTokens = details.summaryText ? this.estimateTextTokens(
|
|
5905
|
+
`${_AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX}
|
|
5906
|
+
|
|
5907
|
+
${details.summaryText}
|
|
5908
|
+
|
|
5909
|
+
Do NOT redo any of the above work.`
|
|
5910
|
+
) : void 0;
|
|
5911
|
+
return {
|
|
5912
|
+
historyTokens: conversationBreakdown.historyTokens,
|
|
5913
|
+
toolOutputTokens: conversationBreakdown.toolOutputTokens,
|
|
5914
|
+
currentTurnTokens,
|
|
5915
|
+
toolDefinitionTokens,
|
|
5916
|
+
...summaryTokens ? { summaryTokens } : {},
|
|
5917
|
+
...reservedOutputTokens ? { reservedOutputTokens } : {},
|
|
5918
|
+
...effectiveInputBudgetTokens ? { effectiveInputBudgetTokens } : {},
|
|
5919
|
+
estimatedInputTokens: conversationBreakdown.estimatedInputTokens + currentTurnTokens + toolDefinitionTokens
|
|
5920
|
+
};
|
|
5921
|
+
}
|
|
5922
|
+
async emitContextCompactionEvent(onContextCompaction, event) {
|
|
5923
|
+
if (!onContextCompaction) return;
|
|
5924
|
+
try {
|
|
5925
|
+
await onContextCompaction(event);
|
|
5926
|
+
} catch {
|
|
5927
|
+
}
|
|
5928
|
+
}
|
|
5929
|
+
async emitContextNotice(onContextNotice, event) {
|
|
5930
|
+
if (!onContextNotice) return;
|
|
5931
|
+
try {
|
|
5932
|
+
await onContextNotice(event);
|
|
5933
|
+
} catch {
|
|
5934
|
+
}
|
|
5935
|
+
}
|
|
5936
|
+
async buildCompactHistoryMessagesWithLifecycle(state, userContent, sessionIndex, details) {
|
|
5937
|
+
const baseEvent = {
|
|
5938
|
+
mode: details.mode,
|
|
5939
|
+
strategy: details.strategy,
|
|
5940
|
+
sessionIndex: sessionIndex + 1,
|
|
5941
|
+
model: details.model,
|
|
5942
|
+
estimatedTokens: details.beforeTokens,
|
|
5943
|
+
thresholdTokens: details.thresholdTokens,
|
|
5944
|
+
contextLimitTokens: details.contextLimitTokens,
|
|
5945
|
+
effectiveInputBudgetTokens: details.effectiveInputBudgetTokens,
|
|
5946
|
+
reservedOutputTokens: details.reservedOutputTokens,
|
|
5947
|
+
beforeTokens: details.beforeTokens,
|
|
5948
|
+
afterTokens: details.afterTokens,
|
|
5949
|
+
breakdown: details.breakdown
|
|
5950
|
+
};
|
|
5951
|
+
await this.emitContextCompactionEvent(details.onContextCompaction, {
|
|
5952
|
+
phase: "start",
|
|
5953
|
+
...baseEvent
|
|
5954
|
+
});
|
|
5955
|
+
const compactMessages = this.buildCompactHistoryMessages(
|
|
5956
|
+
state,
|
|
5957
|
+
userContent,
|
|
5958
|
+
details.compactInstructions,
|
|
5959
|
+
details.mode
|
|
5960
|
+
);
|
|
5961
|
+
await this.emitContextCompactionEvent(details.onContextCompaction, {
|
|
5962
|
+
phase: "complete",
|
|
5963
|
+
...baseEvent
|
|
5964
|
+
});
|
|
5965
|
+
return compactMessages;
|
|
5966
|
+
}
|
|
5967
|
+
buildCompactHistoryMessages(state, userContent, compactInstructions, mode = "auto") {
|
|
5968
|
+
const summary = this.generateCompactSummary(state, compactInstructions);
|
|
5969
|
+
const prefix = mode === "forced" ? _AgentsEndpoint.FORCED_COMPACT_SUMMARY_PREFIX : _AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX;
|
|
5970
|
+
return [
|
|
5971
|
+
{
|
|
5972
|
+
role: "system",
|
|
5973
|
+
content: `${prefix}
|
|
5974
|
+
|
|
5975
|
+
${summary}
|
|
5976
|
+
|
|
5977
|
+
Do NOT redo any of the above work.`
|
|
5978
|
+
},
|
|
5979
|
+
{
|
|
5980
|
+
role: "user",
|
|
5981
|
+
content: userContent
|
|
5982
|
+
}
|
|
5983
|
+
];
|
|
5984
|
+
}
|
|
5985
|
+
isCompactHistoryMessageSet(messages) {
|
|
5986
|
+
if (messages.length === 0) return false;
|
|
5987
|
+
const firstMessage = messages[0];
|
|
5988
|
+
return firstMessage?.role === "system" && typeof firstMessage.content === "string" && (firstMessage.content.startsWith(_AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX) || firstMessage.content.startsWith(_AgentsEndpoint.FORCED_COMPACT_SUMMARY_PREFIX));
|
|
5989
|
+
}
|
|
5441
5990
|
/**
|
|
5442
5991
|
* Generate a compact summary of prior work for continuation context.
|
|
5443
5992
|
* Used when compact mode is enabled to keep token usage low.
|
|
5444
5993
|
*/
|
|
5445
|
-
generateCompactSummary(state) {
|
|
5446
|
-
const
|
|
5447
|
-
|
|
5994
|
+
generateCompactSummary(state, compactInstructions) {
|
|
5995
|
+
const recentSessions = (state.sessions ?? []).slice(-5);
|
|
5996
|
+
const sessionSummaries = recentSessions.map(
|
|
5997
|
+
(session) => `- Session ${session.index}: ${session.stopReason} ($${session.cost.toFixed(4)}) ${session.outputPreview.slice(0, 160)}`
|
|
5448
5998
|
).join("\n");
|
|
5999
|
+
const candidatePaths = Array.from(
|
|
6000
|
+
new Set(
|
|
6001
|
+
[
|
|
6002
|
+
...state.bestCandidatePath ? [state.bestCandidatePath] : [],
|
|
6003
|
+
...state.candidatePaths ?? []
|
|
6004
|
+
].filter(Boolean)
|
|
6005
|
+
)
|
|
6006
|
+
).slice(0, 8);
|
|
6007
|
+
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.";
|
|
6008
|
+
const artifactReferences = this.extractArtifactReferences(state);
|
|
6009
|
+
const pendingNextStep = state.lastStopReason === "complete" ? "Confirm nothing else remains before declaring the task complete." : `Continue the ${state.workflowPhase || "research"} phase without redoing prior work.`;
|
|
6010
|
+
const instructions = compactInstructions || this.buildDefaultCompactInstructions();
|
|
5449
6011
|
return [
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
...
|
|
6012
|
+
"Task + Phase",
|
|
6013
|
+
`- Task: ${state.taskName}`,
|
|
6014
|
+
`- Status: ${state.status}`,
|
|
6015
|
+
`- Workflow phase: ${state.workflowPhase || "research"}`,
|
|
6016
|
+
`- Sessions completed: ${state.sessionCount}`,
|
|
6017
|
+
`- Total cost: $${state.totalCost.toFixed(4)}`,
|
|
6018
|
+
"",
|
|
6019
|
+
"Completed Work",
|
|
6020
|
+
...sessionSummaries ? [sessionSummaries] : ["- No completed session summaries recorded yet."],
|
|
6021
|
+
"",
|
|
6022
|
+
"Changed Files / Candidate Paths",
|
|
6023
|
+
...candidatePaths.length > 0 ? candidatePaths.map((candidatePath) => `- ${candidatePath}`) : ["- No candidate paths recorded yet."],
|
|
6024
|
+
...state.planPath ? [`- Plan path: ${state.planPath}`] : [],
|
|
6025
|
+
...state.planWritten ? ["- Planning artifact has been written."] : [],
|
|
6026
|
+
...state.bestCandidateReason ? [`- Best candidate rationale: ${state.bestCandidateReason}`] : [],
|
|
5462
6027
|
"",
|
|
5463
|
-
"
|
|
5464
|
-
|
|
6028
|
+
"Verification Status",
|
|
6029
|
+
`- ${verificationSummary}`,
|
|
6030
|
+
...state.lastError ? [`- Last error: ${state.lastError}`] : [],
|
|
5465
6031
|
"",
|
|
5466
|
-
"
|
|
5467
|
-
|
|
6032
|
+
"Pending Next Step",
|
|
6033
|
+
`- ${pendingNextStep}`,
|
|
6034
|
+
"",
|
|
6035
|
+
"Artifact / Tool Output References",
|
|
6036
|
+
...artifactReferences.length > 0 ? artifactReferences.map((reference) => `- ${reference}`) : ["- No offloaded artifacts recorded."],
|
|
6037
|
+
"",
|
|
6038
|
+
"Compaction Instructions",
|
|
6039
|
+
`- ${instructions}`,
|
|
6040
|
+
"",
|
|
6041
|
+
"Last Output (truncated)",
|
|
6042
|
+
(state.lastOutput || "").slice(0, 1800) || "- No final output recorded yet."
|
|
5468
6043
|
].join("\n");
|
|
5469
6044
|
}
|
|
5470
6045
|
/**
|
|
5471
6046
|
* Build messages for a session, injecting progress context for continuation sessions.
|
|
5472
6047
|
* Optionally accepts continuation context for marathon resume scenarios.
|
|
5473
6048
|
*/
|
|
5474
|
-
buildSessionMessages(originalMessage, state, sessionIndex, maxSessions, localToolNames, continuationContext, workflow) {
|
|
6049
|
+
async buildSessionMessages(originalMessage, state, sessionIndex, maxSessions, localToolNames, continuationContext, workflow, builtinToolIds, compactionOptions) {
|
|
6050
|
+
const prepared = await this.prepareSessionContext(
|
|
6051
|
+
originalMessage,
|
|
6052
|
+
state,
|
|
6053
|
+
sessionIndex,
|
|
6054
|
+
maxSessions,
|
|
6055
|
+
localToolNames,
|
|
6056
|
+
continuationContext,
|
|
6057
|
+
workflow,
|
|
6058
|
+
builtinToolIds,
|
|
6059
|
+
compactionOptions
|
|
6060
|
+
);
|
|
6061
|
+
return prepared.messages;
|
|
6062
|
+
}
|
|
6063
|
+
async prepareSessionContext(originalMessage, state, sessionIndex, maxSessions, localToolNames, continuationContext, workflow, builtinToolIds, compactionOptions) {
|
|
5475
6064
|
const wf = workflow ?? defaultWorkflow;
|
|
6065
|
+
const compactInstructions = compactionOptions?.compactInstructions;
|
|
6066
|
+
const resolvedStrategy = continuationContext?.compact ? "summary_fallback" : this.resolveCompactStrategy(compactionOptions?.compactStrategy, compactionOptions?.model);
|
|
6067
|
+
const requestContextManagement = resolvedStrategy === "provider_native" && typeof compactionOptions?.autoCompactTokenThreshold === "number" && compactionOptions.autoCompactTokenThreshold > 0 ? {
|
|
6068
|
+
compactStrategy: resolvedStrategy,
|
|
6069
|
+
...compactInstructions ? { compactInstructions } : {},
|
|
6070
|
+
nativeCompaction: {
|
|
6071
|
+
provider: "anthropic",
|
|
6072
|
+
thresholdTokens: compactionOptions.autoCompactTokenThreshold
|
|
6073
|
+
}
|
|
6074
|
+
} : void 0;
|
|
6075
|
+
const maybeEmitToolDefinitionWarning = async (breakdown) => {
|
|
6076
|
+
if (sessionIndex !== 0) return;
|
|
6077
|
+
if (typeof compactionOptions?.contextLimitTokens !== "number" || compactionOptions.contextLimitTokens <= 0) {
|
|
6078
|
+
return;
|
|
6079
|
+
}
|
|
6080
|
+
if (breakdown.toolDefinitionTokens <= 0) return;
|
|
6081
|
+
const ratio = breakdown.toolDefinitionTokens / compactionOptions.contextLimitTokens;
|
|
6082
|
+
if (ratio < 0.1) return;
|
|
6083
|
+
await this.emitContextNotice(compactionOptions?.onContextNotice, {
|
|
6084
|
+
kind: "tool_definitions_warning",
|
|
6085
|
+
sessionIndex: sessionIndex + 1,
|
|
6086
|
+
model: compactionOptions?.model,
|
|
6087
|
+
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.`,
|
|
6088
|
+
estimatedTokens: breakdown.toolDefinitionTokens,
|
|
6089
|
+
contextLimitTokens: compactionOptions.contextLimitTokens,
|
|
6090
|
+
effectiveInputBudgetTokens: breakdown.effectiveInputBudgetTokens,
|
|
6091
|
+
reservedOutputTokens: breakdown.reservedOutputTokens
|
|
6092
|
+
});
|
|
6093
|
+
};
|
|
6094
|
+
const buildNativeCompactionEvent = (mode, breakdown) => {
|
|
6095
|
+
if (resolvedStrategy !== "provider_native" || typeof compactionOptions?.autoCompactTokenThreshold !== "number" || compactionOptions.autoCompactTokenThreshold <= 0 || breakdown.estimatedInputTokens < compactionOptions.autoCompactTokenThreshold) {
|
|
6096
|
+
return void 0;
|
|
6097
|
+
}
|
|
6098
|
+
return {
|
|
6099
|
+
mode,
|
|
6100
|
+
strategy: resolvedStrategy,
|
|
6101
|
+
sessionIndex: sessionIndex + 1,
|
|
6102
|
+
model: compactionOptions?.model,
|
|
6103
|
+
estimatedTokens: breakdown.estimatedInputTokens,
|
|
6104
|
+
thresholdTokens: compactionOptions.autoCompactTokenThreshold,
|
|
6105
|
+
contextLimitTokens: compactionOptions.contextLimitTokens,
|
|
6106
|
+
effectiveInputBudgetTokens: breakdown.effectiveInputBudgetTokens,
|
|
6107
|
+
reservedOutputTokens: breakdown.reservedOutputTokens,
|
|
6108
|
+
beforeTokens: breakdown.estimatedInputTokens,
|
|
6109
|
+
breakdown
|
|
6110
|
+
};
|
|
6111
|
+
};
|
|
5476
6112
|
const currentPhase = wf.phases.find((p) => p.name === state.workflowPhase);
|
|
5477
6113
|
const toolGuidanceLines = currentPhase?.buildToolGuidance(state) ?? [];
|
|
5478
6114
|
const isDeployWorkflow = wf.name === "deploy";
|
|
5479
|
-
const
|
|
6115
|
+
const localToolsBlock = localToolNames?.length ? [
|
|
5480
6116
|
"",
|
|
5481
6117
|
"--- Local Tools ---",
|
|
5482
6118
|
`You have access to tools (${localToolNames.join(", ")}) that execute directly on the user's machine.`,
|
|
@@ -5486,6 +6122,14 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5486
6122
|
...toolGuidanceLines,
|
|
5487
6123
|
...isDeployWorkflow ? [] : ["Always use write_file to save your output so the user can run it immediately."]
|
|
5488
6124
|
].join("\n") : "";
|
|
6125
|
+
const builtinToolNames = builtinToolIds?.map((id) => id.replace(/^builtin:/, ""));
|
|
6126
|
+
const builtinToolsBlock = builtinToolNames?.length ? [
|
|
6127
|
+
"",
|
|
6128
|
+
"--- Built-in Tools ---",
|
|
6129
|
+
`You have access to built-in tools (${builtinToolNames.join(", ")}) for web search, web scraping, image generation, and other capabilities.`,
|
|
6130
|
+
"Use these tools when the task requires information from the web, generating images, or other capabilities beyond local file operations."
|
|
6131
|
+
].join("\n") : "";
|
|
6132
|
+
const toolsBlock = localToolsBlock + builtinToolsBlock;
|
|
5489
6133
|
const bootstrapBlock = state.bootstrapContext ? ["", "--- Initial Repository Discovery ---", state.bootstrapContext].join("\n") : "";
|
|
5490
6134
|
const phaseBlock = ["", this.buildPhaseInstructions(state, wf)].join("\n");
|
|
5491
6135
|
const candidateBlock = wf.buildCandidateBlock?.(state) ?? "";
|
|
@@ -5493,25 +6137,16 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
5493
6137
|
if (continuationContext && sessionIndex === 0) {
|
|
5494
6138
|
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.";
|
|
5495
6139
|
const userMessage = continuationContext.newUserMessage || defaultContinueMessage;
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
},
|
|
5507
|
-
{
|
|
5508
|
-
role: "user",
|
|
5509
|
-
content: [userMessage, phaseBlock, toolsBlock, bootstrapBlock, candidateBlock, "", multiSessionInstruction].join("\n")
|
|
5510
|
-
}
|
|
5511
|
-
];
|
|
5512
|
-
return messages2;
|
|
5513
|
-
}
|
|
5514
|
-
const messages = [
|
|
6140
|
+
const userContent = [
|
|
6141
|
+
userMessage,
|
|
6142
|
+
phaseBlock,
|
|
6143
|
+
toolsBlock,
|
|
6144
|
+
bootstrapBlock,
|
|
6145
|
+
candidateBlock,
|
|
6146
|
+
"",
|
|
6147
|
+
multiSessionInstruction
|
|
6148
|
+
].join("\n");
|
|
6149
|
+
const fullHistoryMessages = [
|
|
5515
6150
|
...continuationContext.previousMessages,
|
|
5516
6151
|
{
|
|
5517
6152
|
role: "system",
|
|
@@ -5519,14 +6154,79 @@ Do NOT redo any of the above work.`
|
|
|
5519
6154
|
},
|
|
5520
6155
|
{
|
|
5521
6156
|
role: "user",
|
|
5522
|
-
content:
|
|
6157
|
+
content: userContent
|
|
5523
6158
|
}
|
|
5524
6159
|
];
|
|
5525
|
-
|
|
6160
|
+
const summaryText = this.generateCompactSummary(state, compactInstructions);
|
|
6161
|
+
const breakdown = this.buildContextBudgetBreakdown({
|
|
6162
|
+
historyMessages: continuationContext.previousMessages,
|
|
6163
|
+
currentTurnContent: userContent,
|
|
6164
|
+
localTools: compactionOptions?.localTools,
|
|
6165
|
+
builtinToolSchemas: compactionOptions?.builtinToolSchemas || [],
|
|
6166
|
+
summaryText,
|
|
6167
|
+
contextLimitTokens: compactionOptions?.contextLimitTokens
|
|
6168
|
+
});
|
|
6169
|
+
await maybeEmitToolDefinitionWarning(breakdown);
|
|
6170
|
+
if (continuationContext.compact) {
|
|
6171
|
+
return {
|
|
6172
|
+
messages: await this.buildCompactHistoryMessagesWithLifecycle(
|
|
6173
|
+
state,
|
|
6174
|
+
userContent,
|
|
6175
|
+
sessionIndex,
|
|
6176
|
+
{
|
|
6177
|
+
mode: "forced",
|
|
6178
|
+
strategy: "summary_fallback",
|
|
6179
|
+
model: compactionOptions?.model,
|
|
6180
|
+
beforeTokens: breakdown.estimatedInputTokens,
|
|
6181
|
+
afterTokens: (breakdown.summaryTokens || 0) + breakdown.currentTurnTokens + breakdown.toolDefinitionTokens,
|
|
6182
|
+
thresholdTokens: compactionOptions?.autoCompactTokenThreshold,
|
|
6183
|
+
contextLimitTokens: compactionOptions?.contextLimitTokens,
|
|
6184
|
+
effectiveInputBudgetTokens: breakdown.effectiveInputBudgetTokens,
|
|
6185
|
+
reservedOutputTokens: breakdown.reservedOutputTokens,
|
|
6186
|
+
breakdown,
|
|
6187
|
+
onContextCompaction: compactionOptions?.onContextCompaction,
|
|
6188
|
+
compactInstructions
|
|
6189
|
+
}
|
|
6190
|
+
),
|
|
6191
|
+
requestContextManagement
|
|
6192
|
+
};
|
|
6193
|
+
}
|
|
6194
|
+
if (resolvedStrategy === "summary_fallback" && typeof compactionOptions?.autoCompactTokenThreshold === "number" && compactionOptions.autoCompactTokenThreshold > 0 && breakdown.estimatedInputTokens >= compactionOptions.autoCompactTokenThreshold) {
|
|
6195
|
+
return {
|
|
6196
|
+
messages: await this.buildCompactHistoryMessagesWithLifecycle(
|
|
6197
|
+
state,
|
|
6198
|
+
userContent,
|
|
6199
|
+
sessionIndex,
|
|
6200
|
+
{
|
|
6201
|
+
mode: "auto",
|
|
6202
|
+
strategy: "summary_fallback",
|
|
6203
|
+
model: compactionOptions?.model,
|
|
6204
|
+
beforeTokens: breakdown.estimatedInputTokens,
|
|
6205
|
+
afterTokens: (breakdown.summaryTokens || 0) + breakdown.currentTurnTokens + breakdown.toolDefinitionTokens,
|
|
6206
|
+
thresholdTokens: compactionOptions?.autoCompactTokenThreshold,
|
|
6207
|
+
contextLimitTokens: compactionOptions?.contextLimitTokens,
|
|
6208
|
+
effectiveInputBudgetTokens: breakdown.effectiveInputBudgetTokens,
|
|
6209
|
+
reservedOutputTokens: breakdown.reservedOutputTokens,
|
|
6210
|
+
breakdown,
|
|
6211
|
+
onContextCompaction: compactionOptions?.onContextCompaction,
|
|
6212
|
+
compactInstructions
|
|
6213
|
+
}
|
|
6214
|
+
),
|
|
6215
|
+
requestContextManagement
|
|
6216
|
+
};
|
|
6217
|
+
}
|
|
6218
|
+
return {
|
|
6219
|
+
messages: fullHistoryMessages,
|
|
6220
|
+
requestContextManagement,
|
|
6221
|
+
pendingNativeCompactionEvent: buildNativeCompactionEvent("auto", breakdown)
|
|
6222
|
+
};
|
|
5526
6223
|
}
|
|
5527
6224
|
if (sessionIndex === 0) {
|
|
5528
6225
|
const content2 = [originalMessage, phaseBlock, toolsBlock, bootstrapBlock, candidateBlock, "", multiSessionInstruction].join("\n");
|
|
5529
|
-
return
|
|
6226
|
+
return {
|
|
6227
|
+
messages: [{ role: "user", content: content2 }],
|
|
6228
|
+
requestContextManagement
|
|
6229
|
+
};
|
|
5530
6230
|
}
|
|
5531
6231
|
const recentSessions = state.sessions.slice(-5);
|
|
5532
6232
|
const progressSummary = recentSessions.map(
|
|
@@ -5560,10 +6260,49 @@ Do NOT redo any of the above work.`
|
|
|
5560
6260
|
...historyMessages.slice(-MAX_HISTORY_MESSAGES)
|
|
5561
6261
|
];
|
|
5562
6262
|
}
|
|
5563
|
-
|
|
6263
|
+
const summaryText = this.generateCompactSummary(state, compactInstructions);
|
|
6264
|
+
const breakdown = this.buildContextBudgetBreakdown({
|
|
6265
|
+
historyMessages,
|
|
6266
|
+
currentTurnContent: continuationContent,
|
|
6267
|
+
localTools: compactionOptions?.localTools,
|
|
6268
|
+
builtinToolSchemas: compactionOptions?.builtinToolSchemas || [],
|
|
6269
|
+
summaryText,
|
|
6270
|
+
contextLimitTokens: compactionOptions?.contextLimitTokens
|
|
6271
|
+
});
|
|
6272
|
+
await maybeEmitToolDefinitionWarning(breakdown);
|
|
6273
|
+
const messages = [
|
|
5564
6274
|
...historyMessages,
|
|
5565
6275
|
{ role: "user", content: continuationContent }
|
|
5566
6276
|
];
|
|
6277
|
+
if (resolvedStrategy === "summary_fallback" && typeof compactionOptions?.autoCompactTokenThreshold === "number" && compactionOptions.autoCompactTokenThreshold > 0 && breakdown.estimatedInputTokens >= compactionOptions.autoCompactTokenThreshold) {
|
|
6278
|
+
return {
|
|
6279
|
+
messages: await this.buildCompactHistoryMessagesWithLifecycle(
|
|
6280
|
+
state,
|
|
6281
|
+
continuationContent,
|
|
6282
|
+
sessionIndex,
|
|
6283
|
+
{
|
|
6284
|
+
mode: "auto",
|
|
6285
|
+
strategy: "summary_fallback",
|
|
6286
|
+
model: compactionOptions?.model,
|
|
6287
|
+
beforeTokens: breakdown.estimatedInputTokens,
|
|
6288
|
+
afterTokens: (breakdown.summaryTokens || 0) + breakdown.currentTurnTokens + breakdown.toolDefinitionTokens,
|
|
6289
|
+
thresholdTokens: compactionOptions?.autoCompactTokenThreshold,
|
|
6290
|
+
contextLimitTokens: compactionOptions?.contextLimitTokens,
|
|
6291
|
+
effectiveInputBudgetTokens: breakdown.effectiveInputBudgetTokens,
|
|
6292
|
+
reservedOutputTokens: breakdown.reservedOutputTokens,
|
|
6293
|
+
breakdown,
|
|
6294
|
+
onContextCompaction: compactionOptions?.onContextCompaction,
|
|
6295
|
+
compactInstructions
|
|
6296
|
+
}
|
|
6297
|
+
),
|
|
6298
|
+
requestContextManagement
|
|
6299
|
+
};
|
|
6300
|
+
}
|
|
6301
|
+
return {
|
|
6302
|
+
messages,
|
|
6303
|
+
requestContextManagement,
|
|
6304
|
+
pendingNativeCompactionEvent: buildNativeCompactionEvent("auto", breakdown)
|
|
6305
|
+
};
|
|
5567
6306
|
}
|
|
5568
6307
|
const recoveryMessage = this.buildStuckTurnRecoveryMessage(state, wf);
|
|
5569
6308
|
const content = [
|
|
@@ -5583,7 +6322,10 @@ Do NOT redo any of the above work.`
|
|
|
5583
6322
|
"",
|
|
5584
6323
|
"Continue where you left off. Do not redo previous work. If the task is already complete, respond with TASK_COMPLETE."
|
|
5585
6324
|
].join("\n");
|
|
5586
|
-
return
|
|
6325
|
+
return {
|
|
6326
|
+
messages: [{ role: "user", content }],
|
|
6327
|
+
requestContextManagement
|
|
6328
|
+
};
|
|
5587
6329
|
}
|
|
5588
6330
|
/**
|
|
5589
6331
|
* Upsert a record to sync long-task progress to the dashboard.
|
|
@@ -5633,6 +6375,8 @@ Do NOT redo any of the above work.`
|
|
|
5633
6375
|
}
|
|
5634
6376
|
}
|
|
5635
6377
|
};
|
|
6378
|
+
_AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX = "You are continuing a long-running task. Here is a compact summary of prior work:";
|
|
6379
|
+
_AgentsEndpoint.FORCED_COMPACT_SUMMARY_PREFIX = "You are continuing a previously completed task. Here is a summary of prior work:";
|
|
5636
6380
|
/** Stop phrases that indicate the agent considers its task complete. */
|
|
5637
6381
|
_AgentsEndpoint.STOP_PHRASES = [
|
|
5638
6382
|
"DONE:",
|
|
@@ -6941,6 +7685,7 @@ export {
|
|
|
6941
7685
|
defaultWorkflow,
|
|
6942
7686
|
deployWorkflow,
|
|
6943
7687
|
evaluateGeneratedRuntimeToolProposal,
|
|
7688
|
+
gameWorkflow,
|
|
6944
7689
|
getDefaultPlanPath,
|
|
6945
7690
|
getLikelySupportingCandidatePaths,
|
|
6946
7691
|
isDiscoveryToolName,
|