@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 CHANGED
@@ -327,6 +327,7 @@ __export(index_exports, {
327
327
  defaultWorkflow: () => defaultWorkflow,
328
328
  deployWorkflow: () => deployWorkflow,
329
329
  evaluateGeneratedRuntimeToolProposal: () => evaluateGeneratedRuntimeToolProposal,
330
+ gameWorkflow: () => gameWorkflow,
330
331
  getDefaultPlanPath: () => getDefaultPlanPath,
331
332
  getLikelySupportingCandidatePaths: () => getLikelySupportingCandidatePaths,
332
333
  isDiscoveryToolName: () => isDiscoveryToolName,
@@ -2909,8 +2910,8 @@ var scaffoldPhase = {
2909
2910
  ].join("\n");
2910
2911
  },
2911
2912
  interceptToolCall(toolName, _args, _ctx) {
2912
- const blockedTools = ["write_file", "read_file", "search_repo", "glob_files", "tree_directory", "list_directory", "restore_file_checkpoint"];
2913
- if (blockedTools.includes(toolName)) {
2913
+ const blockedTools2 = ["write_file", "read_file", "search_repo", "glob_files", "tree_directory", "list_directory", "restore_file_checkpoint"];
2914
+ if (blockedTools2.includes(toolName)) {
2914
2915
  return [
2915
2916
  `Blocked: ${toolName} is not available in deploy mode.`,
2916
2917
  "Use deploy_sandbox to deploy your code to a live sandbox instead of writing files to the local repo."
@@ -2949,6 +2950,7 @@ var deployPhase = {
2949
2950
  "",
2950
2951
  "deploy_sandbox takes:",
2951
2952
  " - code: The full source code (written to main.ts/main.js/main.py)",
2953
+ ' - files: Additional files (path \u2192 content), e.g. { "public/index.html": "<html>..." }',
2952
2954
  ' - packageJson: Dependencies as a JSON object, e.g. { "dependencies": { "express": "^4.18.2" } }',
2953
2955
  ' - language: "typescript" (default), "javascript", or "python"',
2954
2956
  " - port: The port your server listens on (default: 3000)",
@@ -2962,7 +2964,8 @@ var deployPhase = {
2962
2964
  " 3. Include ALL dependencies in packageJson \u2014 nothing is pre-installed except Node.js built-ins.",
2963
2965
  " 4. If the deploy fails, read the error output, fix the code, and call deploy_sandbox again.",
2964
2966
  " 5. The sandbox is persistent \u2014 subsequent calls reuse the same sandbox.",
2965
- " 6. When the deploy succeeds and the preview URL is live, tell the user and end with TASK_COMPLETE."
2967
+ " 6. When the deploy succeeds and the preview URL is live, tell the user and end with TASK_COMPLETE.",
2968
+ " 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."
2966
2969
  ].join("\n");
2967
2970
  },
2968
2971
  buildToolGuidance(_state) {
@@ -2977,8 +2980,8 @@ var deployPhase = {
2977
2980
  return false;
2978
2981
  },
2979
2982
  interceptToolCall(toolName, _args, _ctx) {
2980
- const blockedTools = ["write_file", "read_file", "search_repo", "glob_files", "tree_directory", "list_directory", "restore_file_checkpoint"];
2981
- if (blockedTools.includes(toolName)) {
2983
+ const blockedTools2 = ["write_file", "read_file", "search_repo", "glob_files", "tree_directory", "list_directory", "restore_file_checkpoint"];
2984
+ if (blockedTools2.includes(toolName)) {
2982
2985
  return [
2983
2986
  `Blocked: ${toolName} is not available in deploy mode.`,
2984
2987
  "Use deploy_sandbox to deploy your code to a live sandbox instead of writing files to the local repo."
@@ -2986,8 +2989,13 @@ var deployPhase = {
2986
2989
  }
2987
2990
  return void 0;
2988
2991
  },
2989
- canAcceptCompletion(_state, trace) {
2990
- return trace.entries.some((entry) => entry.startsWith("deploy_sandbox"));
2992
+ canAcceptCompletion(state, trace) {
2993
+ if (trace.entries.some((entry) => entry.startsWith("deploy_sandbox"))) {
2994
+ return true;
2995
+ }
2996
+ return state.sessions.some(
2997
+ (s) => s.actionKeys?.some((key) => key.startsWith("deploy_sandbox"))
2998
+ );
2991
2999
  },
2992
3000
  buildRecoveryMessage(state) {
2993
3001
  const recent = state.sessions.slice(-2);
@@ -3058,6 +3066,248 @@ var deployWorkflow = {
3058
3066
  }
3059
3067
  };
3060
3068
 
3069
+ // src/workflows/game-workflow.ts
3070
+ var blockedTools = ["write_file", "read_file", "search_repo", "glob_files", "tree_directory", "list_directory", "restore_file_checkpoint"];
3071
+ function blockLocalTools(toolName) {
3072
+ if (blockedTools.includes(toolName)) {
3073
+ return [
3074
+ `Blocked: ${toolName} is not available in game deploy mode.`,
3075
+ "Use deploy_sandbox to deploy your game to a live sandbox instead of writing files to the local repo."
3076
+ ].join(" ");
3077
+ }
3078
+ return void 0;
3079
+ }
3080
+ var designPhase = {
3081
+ name: "design",
3082
+ description: "Understand game requirements and plan the approach",
3083
+ buildInstructions(_state) {
3084
+ return [
3085
+ "--- Workflow Phase: Design ---",
3086
+ "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.",
3087
+ "Quickly determine:",
3088
+ " 1. What type of game (3D, 2D, platformer, racing, puzzle, etc.)",
3089
+ " 2. Theme and visual style",
3090
+ " 3. Core game mechanics (controls, scoring, win/lose conditions)",
3091
+ " 4. Tech stack (Three.js, Phaser, vanilla Canvas/WebGL, etc.)",
3092
+ " 5. Any specific dependencies needed",
3093
+ "",
3094
+ "Do NOT inspect the local repo or search for files to edit.",
3095
+ "Do NOT write a plan file.",
3096
+ "Once you understand the requirements, proceed to build and deploy."
3097
+ ].join("\n");
3098
+ },
3099
+ buildToolGuidance(_state) {
3100
+ return [
3101
+ "This is a sandbox deploy task \u2014 do NOT use write_file, read_file, search_repo, glob_files, tree_directory, or list_directory.",
3102
+ "Your primary tool is deploy_sandbox. Use it to deploy code to a persistent sandbox and get a live preview URL."
3103
+ ];
3104
+ },
3105
+ isComplete(ctx) {
3106
+ return ctx.state.sessions.length >= 1 || ctx.trace.entries.some((entry) => entry.startsWith("deploy_sandbox"));
3107
+ },
3108
+ buildTransitionSummary(_state, _nextPhaseName) {
3109
+ return [
3110
+ "Automatic phase transition: design \u2192 build.",
3111
+ "Requirements understood. Build the game and deploy it using deploy_sandbox.",
3112
+ "",
3113
+ "MANDATORY: Use the `files` parameter for ALL game HTML/JS/CSS.",
3114
+ "code = minimal Express static server (~4 lines), files = game assets.",
3115
+ "NEVER embed HTML inside res.send() template literals."
3116
+ ].join("\n");
3117
+ },
3118
+ interceptToolCall(toolName, _args, _ctx) {
3119
+ return blockLocalTools(toolName);
3120
+ },
3121
+ shouldForceEndTurn(snapshot, _ctx) {
3122
+ if (snapshot.pauseCount >= 12) {
3123
+ return "design phase is looping without progressing \u2014 end the turn so the system can advance to build";
3124
+ }
3125
+ return void 0;
3126
+ }
3127
+ };
3128
+ var buildPhase = {
3129
+ name: "build",
3130
+ description: "Build the game and deploy to sandbox with live preview",
3131
+ buildInstructions(_state) {
3132
+ return [
3133
+ "--- Workflow Phase: Build ---",
3134
+ "Build the game and deploy it using the deploy_sandbox tool.",
3135
+ "",
3136
+ "CRITICAL: Multi-file deployment pattern",
3137
+ " You MUST use the `files` parameter for the game HTML/JS/CSS.",
3138
+ " NEVER embed HTML inside a JavaScript template literal in Express `res.send()` \u2014 this WILL break",
3139
+ " because game code uses backticks (template literals) which cause nested backtick corruption.",
3140
+ "",
3141
+ " Correct pattern:",
3142
+ " - code: A minimal Express static file server (~5 lines):",
3143
+ " ```",
3144
+ ' const express = require("express");',
3145
+ " const app = express();",
3146
+ ' app.use(express.static("public"));',
3147
+ ' app.listen(3000, () => console.log("Server running on port 3000"));',
3148
+ " ```",
3149
+ " - files: Your game assets as separate files:",
3150
+ " {",
3151
+ ' "public/index.html": "<!DOCTYPE html>...",',
3152
+ ' "public/game.js": "// game logic...",',
3153
+ ' "public/style.css": "body { margin: 0; }"',
3154
+ " }",
3155
+ "",
3156
+ " WRONG pattern (DO NOT DO THIS):",
3157
+ ' - code: `app.get("/", (req, res) => res.send(\\`<html>...game code with backticks...\\`))`',
3158
+ " - This breaks because game code uses template literals inside the res.send template literal.",
3159
+ "",
3160
+ "deploy_sandbox takes:",
3161
+ " - code: The server source code (written to main.ts/main.js/main.py)",
3162
+ ' - files: Additional files (path \u2192 content), e.g. { "public/index.html": "<html>..." }',
3163
+ ' - packageJson: Dependencies as a JSON object, e.g. { "dependencies": { "express": "^4.18.2", "three": "^0.170.0" } }',
3164
+ ' - language: "javascript" (recommended for games), "typescript", or "python"',
3165
+ " - port: The port your server listens on (default: 3000)",
3166
+ " - startCommand: Custom start command (auto-detected by default)",
3167
+ "",
3168
+ "Guidelines:",
3169
+ ' 1. Use `language: "javascript"` \u2014 simpler for static file servers.',
3170
+ " 2. The `code` param should be a minimal Express static server. ALL game code goes in `files`.",
3171
+ " 3. Include ALL dependencies in packageJson (express, three, phaser, etc.).",
3172
+ " 4. If the deploy fails, read the error output, fix the code, and call deploy_sandbox again.",
3173
+ " 5. The sandbox is persistent \u2014 subsequent calls reuse the same sandbox.",
3174
+ " 6. Load game libraries from CDN in your HTML (e.g., Three.js from unpkg/cdnjs) OR include them in packageJson.",
3175
+ " 7. Make the game fullscreen by default (width: 100vw, height: 100vh, no margin/padding on body).",
3176
+ "",
3177
+ "STARTER TEMPLATES \u2014 Copy and customize based on the game type:",
3178
+ "",
3179
+ "ALL templates use this server code:",
3180
+ ' code: `const express = require("express"); const app = express(); app.use(express.static("public")); app.listen(3000, () => console.log("Game server running on port 3000"));`',
3181
+ ' packageJson: { "dependencies": { "express": "^4.18.2" } }',
3182
+ ' language: "javascript"',
3183
+ "",
3184
+ "THREE.JS (3D games \u2014 racing, flying, FPS, etc.):",
3185
+ " files: {",
3186
+ ' "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>',
3187
+ ' "public/game.js": Import three, create scene/camera/renderer, game loop with requestAnimationFrame',
3188
+ " }",
3189
+ "",
3190
+ "PHASER (2D games \u2014 platformers, shooters, puzzles):",
3191
+ " files: {",
3192
+ ' "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>',
3193
+ ' "public/game.js": Phaser.Game config with scenes, preload/create/update lifecycle',
3194
+ " }",
3195
+ "",
3196
+ "CANVAS 2D (simple games \u2014 pong, snake, breakout):",
3197
+ " files: {",
3198
+ ' "public/index.html": HTML with <canvas id="game" width="800" height="600"></canvas> and <script src="game.js"></script>',
3199
+ ' "public/game.js": Get canvas context, game loop with requestAnimationFrame, draw calls',
3200
+ " }"
3201
+ ].join("\n");
3202
+ },
3203
+ buildToolGuidance(_state) {
3204
+ return [
3205
+ "Your primary tool is deploy_sandbox. Call it with code (Express static server), files (game HTML/JS/CSS), and packageJson.",
3206
+ "ALWAYS use the `files` parameter for game HTML, JavaScript, and CSS \u2014 NEVER embed them in template literals.",
3207
+ "Do NOT use write_file, read_file, search_repo, glob_files, tree_directory, or list_directory.",
3208
+ "You may use run_sandbox_code for quick one-off tests if needed."
3209
+ ];
3210
+ },
3211
+ isComplete() {
3212
+ return false;
3213
+ },
3214
+ interceptToolCall(toolName, args, _ctx) {
3215
+ const localBlock = blockLocalTools(toolName);
3216
+ if (localBlock) return localBlock;
3217
+ if (toolName === "deploy_sandbox" && args) {
3218
+ const hasFiles = args.files && typeof args.files === "object" && Object.keys(args.files).length > 0;
3219
+ const codeStr = typeof args.code === "string" ? args.code : "";
3220
+ const embedsHtml = /res\.send\s*\(/.test(codeStr) && /<html|<body|<script/i.test(codeStr);
3221
+ if (!hasFiles && embedsHtml) {
3222
+ return [
3223
+ "Blocked: You are embedding HTML inside res.send() \u2014 this will break due to nested template literals.",
3224
+ "You MUST use the `files` parameter. Put your game HTML/JS/CSS in files and use a minimal Express static server for code.",
3225
+ 'Example: code: `const express = require("express"); const app = express(); app.use(express.static("public")); app.listen(3000);`',
3226
+ 'files: { "public/index.html": "<!DOCTYPE html>...", "public/game.js": "// game code" }'
3227
+ ].join("\n");
3228
+ }
3229
+ }
3230
+ return void 0;
3231
+ },
3232
+ canAcceptCompletion(_state, trace) {
3233
+ return trace.entries.some((entry) => entry.startsWith("deploy_sandbox"));
3234
+ },
3235
+ buildRecoveryMessage(state) {
3236
+ const recent = state.sessions.slice(-2);
3237
+ if (recent.length < 2) return void 0;
3238
+ const noProgress = recent.every(
3239
+ (s) => s.hadTextOutput === false && s.wroteFiles === false
3240
+ );
3241
+ if (!noProgress) return void 0;
3242
+ return [
3243
+ "Recovery instruction:",
3244
+ "You should be deploying the game using deploy_sandbox with the `files` parameter.",
3245
+ "Use `code` for a minimal Express static server and `files` for game HTML/JS/CSS.",
3246
+ "NEVER embed HTML in a JavaScript template literal \u2014 use separate files."
3247
+ ].join("\n");
3248
+ },
3249
+ shouldForceEndTurn(snapshot, _ctx) {
3250
+ if (snapshot.consecutiveDiscoveryPauseCount >= 8) {
3251
+ return "build phase is looping on discovery tools instead of calling deploy_sandbox";
3252
+ }
3253
+ if (snapshot.actionKeyCount >= 4) {
3254
+ return `the same action repeated ${snapshot.actionKeyCount} times \u2014 try a different approach`;
3255
+ }
3256
+ return void 0;
3257
+ }
3258
+ };
3259
+ var verifyPhase = {
3260
+ name: "verify",
3261
+ description: "Verify the game is running and playable",
3262
+ buildInstructions(_state) {
3263
+ return [
3264
+ "--- Workflow Phase: Verify ---",
3265
+ "The game has been deployed. Verify it is working:",
3266
+ " 1. Check the server log for errors. If there are errors, fix and redeploy using deploy_sandbox.",
3267
+ " 2. Tell the user the preview URL so they can play the game.",
3268
+ " 3. End with TASK_COMPLETE.",
3269
+ "",
3270
+ "If the user reports issues, fix the code and redeploy."
3271
+ ].join("\n");
3272
+ },
3273
+ buildToolGuidance(_state) {
3274
+ return [
3275
+ "Use deploy_sandbox to redeploy if fixes are needed.",
3276
+ "Do NOT use local file system tools.",
3277
+ "When the game is working, tell the user the preview URL and end with TASK_COMPLETE."
3278
+ ];
3279
+ },
3280
+ isComplete() {
3281
+ return false;
3282
+ },
3283
+ interceptToolCall(toolName, _args, _ctx) {
3284
+ return blockLocalTools(toolName);
3285
+ },
3286
+ canAcceptCompletion(_state, trace) {
3287
+ return trace.entries.some((entry) => entry.startsWith("deploy_sandbox"));
3288
+ },
3289
+ shouldForceEndTurn(snapshot, _ctx) {
3290
+ if (snapshot.actionKeyCount >= 4) {
3291
+ return `the same action repeated ${snapshot.actionKeyCount} times \u2014 try a different approach`;
3292
+ }
3293
+ return void 0;
3294
+ }
3295
+ };
3296
+ function classifyVariant3() {
3297
+ return "game";
3298
+ }
3299
+ var gameWorkflow = {
3300
+ name: "game",
3301
+ phases: [designPhase, buildPhase, verifyPhase],
3302
+ classifyVariant: classifyVariant3,
3303
+ async generateBootstrapContext() {
3304
+ return void 0;
3305
+ },
3306
+ buildCandidateBlock() {
3307
+ return "";
3308
+ }
3309
+ };
3310
+
3061
3311
  // src/endpoints.ts
3062
3312
  var FlowsEndpoint = class {
3063
3313
  constructor(client) {
@@ -5273,6 +5523,8 @@ var _AgentsEndpoint = class _AgentsEndpoint {
5273
5523
  this.updateWorkflowPhase(state, this.createEmptyToolTrace(), workflow);
5274
5524
  let recordId;
5275
5525
  const localToolNames = options.localTools ? Object.keys(options.localTools) : void 0;
5526
+ const builtinToolSchemas = await this.loadBuiltinToolSchemas(options.toolIds);
5527
+ const compactInstructions = this.resolveCompactInstructions(options.compactInstructions, agent);
5276
5528
  if (!options.previousMessages) {
5277
5529
  if (workflow.generateBootstrapContext) {
5278
5530
  state.bootstrapContext = await workflow.generateBootstrapContext(
@@ -5306,65 +5558,101 @@ var _AgentsEndpoint = class _AgentsEndpoint {
5306
5558
  if (session === 0 && !options.previousMessages) {
5307
5559
  state.originalMessage = options.message;
5308
5560
  }
5309
- const messages = this.buildSessionMessages(
5561
+ const preparedSession = await this.prepareSessionContext(
5310
5562
  options.message,
5311
5563
  state,
5312
5564
  session,
5313
5565
  maxSessions,
5314
5566
  localToolNames,
5315
5567
  continuationContext,
5316
- workflow
5568
+ workflow,
5569
+ options.toolIds,
5570
+ {
5571
+ autoCompactTokenThreshold: options.autoCompactTokenThreshold,
5572
+ contextLimitTokens: options.contextLimitTokens,
5573
+ model: options.model,
5574
+ compactStrategy: options.compactStrategy,
5575
+ compactInstructions,
5576
+ localTools: options.localTools,
5577
+ builtinToolSchemas,
5578
+ onContextCompaction: options.onContextCompaction,
5579
+ onContextNotice: options.onContextNotice
5580
+ }
5317
5581
  );
5582
+ const {
5583
+ messages,
5584
+ requestContextManagement,
5585
+ pendingNativeCompactionEvent
5586
+ } = preparedSession;
5318
5587
  let sessionResult;
5319
5588
  const sessionData = {
5320
5589
  messages,
5321
5590
  debugMode: options.debugMode,
5322
- model: options.model
5591
+ model: options.model,
5592
+ ...options.toolIds?.length ? { tools: { toolIds: options.toolIds } } : {},
5593
+ ...requestContextManagement ? { contextManagement: requestContextManagement } : {}
5323
5594
  };
5324
5595
  let sessionToolMessages = [];
5325
- if (useStream && options.localTools) {
5326
- const localToolResult = await this.executeWithLocalTools(
5327
- id,
5328
- sessionData,
5329
- sessionLocalTools || options.localTools,
5330
- sessionCallbacks,
5331
- {
5332
- onLocalToolResult: this.createLocalToolLoopGuard(state, sessionTrace, workflow)
5333
- },
5334
- state.taskName
5335
- );
5336
- if (!localToolResult) {
5337
- throw new Error("Agent stream ended without a complete event");
5596
+ if (pendingNativeCompactionEvent) {
5597
+ await this.emitContextCompactionEvent(options.onContextCompaction, {
5598
+ phase: "start",
5599
+ ...pendingNativeCompactionEvent
5600
+ });
5601
+ }
5602
+ try {
5603
+ if (useStream && options.localTools) {
5604
+ const localToolResult = await this.executeWithLocalTools(
5605
+ id,
5606
+ sessionData,
5607
+ sessionLocalTools || options.localTools,
5608
+ sessionCallbacks,
5609
+ {
5610
+ onLocalToolResult: this.createLocalToolLoopGuard(state, sessionTrace, workflow)
5611
+ },
5612
+ state.taskName
5613
+ );
5614
+ if (!localToolResult) {
5615
+ throw new Error("Agent stream ended without a complete event");
5616
+ }
5617
+ const { completeEvent, toolMessages: captured } = localToolResult;
5618
+ sessionToolMessages = captured;
5619
+ sessionResult = {
5620
+ success: completeEvent.success,
5621
+ result: completeEvent.finalOutput || "",
5622
+ iterations: completeEvent.iterations,
5623
+ totalCost: completeEvent.totalCost || 0,
5624
+ totalTokens: completeEvent.totalTokens,
5625
+ stopReason: completeEvent.stopReason,
5626
+ error: completeEvent.error
5627
+ };
5628
+ } else if (useStream && options.streamCallbacks) {
5629
+ const completeEvent = await this.executeWithCallbacks(
5630
+ id,
5631
+ sessionData,
5632
+ sessionCallbacks || options.streamCallbacks
5633
+ );
5634
+ if (!completeEvent) {
5635
+ throw new Error("Agent stream ended without a complete event");
5636
+ }
5637
+ sessionResult = {
5638
+ success: completeEvent.success,
5639
+ result: completeEvent.finalOutput || "",
5640
+ iterations: completeEvent.iterations,
5641
+ totalCost: completeEvent.totalCost || 0,
5642
+ totalTokens: completeEvent.totalTokens,
5643
+ stopReason: completeEvent.stopReason,
5644
+ error: completeEvent.error
5645
+ };
5646
+ } else {
5647
+ sessionResult = await this.execute(id, sessionData);
5338
5648
  }
5339
- const { completeEvent, toolMessages: captured } = localToolResult;
5340
- sessionToolMessages = captured;
5341
- sessionResult = {
5342
- success: completeEvent.success,
5343
- result: completeEvent.finalOutput || "",
5344
- iterations: completeEvent.iterations,
5345
- totalCost: completeEvent.totalCost || 0,
5346
- stopReason: completeEvent.stopReason,
5347
- error: completeEvent.error
5348
- };
5349
- } else if (useStream && options.streamCallbacks) {
5350
- const completeEvent = await this.executeWithCallbacks(
5351
- id,
5352
- sessionData,
5353
- sessionCallbacks || options.streamCallbacks
5354
- );
5355
- if (!completeEvent) {
5356
- throw new Error("Agent stream ended without a complete event");
5649
+ } finally {
5650
+ if (pendingNativeCompactionEvent) {
5651
+ await this.emitContextCompactionEvent(options.onContextCompaction, {
5652
+ phase: "complete",
5653
+ ...pendingNativeCompactionEvent
5654
+ });
5357
5655
  }
5358
- sessionResult = {
5359
- success: completeEvent.success,
5360
- result: completeEvent.finalOutput || "",
5361
- iterations: completeEvent.iterations,
5362
- totalCost: completeEvent.totalCost || 0,
5363
- stopReason: completeEvent.stopReason,
5364
- error: completeEvent.error
5365
- };
5366
- } else {
5367
- sessionResult = await this.execute(id, sessionData);
5368
5656
  }
5369
5657
  const toolTraceSummary = this.buildToolTraceSummary(sessionTrace);
5370
5658
  const effectiveSessionOutput = this.buildEffectiveSessionOutput(
@@ -5372,8 +5660,15 @@ var _AgentsEndpoint = class _AgentsEndpoint {
5372
5660
  toolTraceSummary
5373
5661
  );
5374
5662
  const sessionCost = sessionResult.totalCost;
5663
+ const sessionTokens = sessionResult.totalTokens;
5375
5664
  state.sessionCount = session + 1;
5376
5665
  state.totalCost += sessionCost;
5666
+ if (sessionTokens) {
5667
+ state.totalTokens = {
5668
+ input: (state.totalTokens?.input || 0) + sessionTokens.input,
5669
+ output: (state.totalTokens?.output || 0) + sessionTokens.output
5670
+ };
5671
+ }
5377
5672
  state.lastOutput = effectiveSessionOutput;
5378
5673
  state.lastError = sessionResult.stopReason === "error" ? sessionResult.error || "Agent session ended with an error." : void 0;
5379
5674
  state.lastStopReason = sessionResult.stopReason;
@@ -5381,6 +5676,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
5381
5676
  state.sessions.push({
5382
5677
  index: session + 1,
5383
5678
  cost: sessionCost,
5679
+ totalTokens: sessionTokens,
5384
5680
  iterations: sessionResult.iterations,
5385
5681
  stopReason: sessionResult.stopReason,
5386
5682
  outputPreview: effectiveSessionOutput.slice(0, 300),
@@ -5443,7 +5739,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
5443
5739
  }
5444
5740
  }
5445
5741
  if (!state.messages) state.messages = [];
5446
- if (state.messages.length > 0 && messages.length > state.messages.length) {
5742
+ if (this.isCompactHistoryMessageSet(messages)) {
5743
+ state.messages = [...messages];
5744
+ } else if (state.messages.length > 0 && messages.length > state.messages.length) {
5447
5745
  const newMessages = messages.slice(state.messages.length);
5448
5746
  state.messages.push(...newMessages);
5449
5747
  } else {
@@ -5494,6 +5792,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
5494
5792
  status: state.status,
5495
5793
  sessionCount: state.sessionCount,
5496
5794
  totalCost: state.totalCost,
5795
+ totalTokens: state.totalTokens,
5497
5796
  lastOutput: state.lastOutput,
5498
5797
  sessions: state.sessions,
5499
5798
  recordId
@@ -5507,45 +5806,383 @@ var _AgentsEndpoint = class _AgentsEndpoint {
5507
5806
  const upper = output.toUpperCase();
5508
5807
  return _AgentsEndpoint.STOP_PHRASES.some((phrase) => upper.includes(phrase.toUpperCase()));
5509
5808
  }
5809
+ resolveReservedOutputTokens(contextLimitTokens) {
5810
+ if (typeof contextLimitTokens !== "number" || !Number.isFinite(contextLimitTokens) || contextLimitTokens <= 0) {
5811
+ return void 0;
5812
+ }
5813
+ return Math.max(8e3, Math.min(64e3, Math.floor(contextLimitTokens * 0.15)));
5814
+ }
5815
+ resolveEffectiveInputBudgetTokens(contextLimitTokens) {
5816
+ if (typeof contextLimitTokens !== "number" || !Number.isFinite(contextLimitTokens) || contextLimitTokens <= 0) {
5817
+ return void 0;
5818
+ }
5819
+ const reservedOutputTokens = this.resolveReservedOutputTokens(contextLimitTokens) ?? 0;
5820
+ return Math.max(1, contextLimitTokens - reservedOutputTokens);
5821
+ }
5822
+ resolveCompactStrategy(strategy, modelId) {
5823
+ if (strategy === "summary_fallback") {
5824
+ return "summary_fallback";
5825
+ }
5826
+ const normalizedModelId = modelId?.trim().toLowerCase() ?? "";
5827
+ const supportsAnthropicNativeCompaction = normalizedModelId.includes("claude") || normalizedModelId.includes("anthropic");
5828
+ if (strategy === "provider_native") {
5829
+ return supportsAnthropicNativeCompaction ? "provider_native" : "summary_fallback";
5830
+ }
5831
+ return supportsAnthropicNativeCompaction ? "provider_native" : "summary_fallback";
5832
+ }
5833
+ estimateTextTokens(text) {
5834
+ if (!text) return 0;
5835
+ return Math.ceil(text.length / 4);
5836
+ }
5837
+ estimateUnknownTokens(value) {
5838
+ if (typeof value === "string") return this.estimateTextTokens(value);
5839
+ try {
5840
+ return this.estimateTextTokens(JSON.stringify(value));
5841
+ } catch {
5842
+ return 0;
5843
+ }
5844
+ }
5845
+ estimateMessageContentTokens(content) {
5846
+ if (typeof content === "string") return this.estimateTextTokens(content);
5847
+ return content.reduce((total, part) => {
5848
+ if (typeof part.text === "string") total += this.estimateTextTokens(part.text);
5849
+ if (typeof part.image === "string") total += 85;
5850
+ if (typeof part.data === "string") {
5851
+ total += Math.min(200, this.estimateTextTokens(part.data));
5852
+ }
5853
+ return total + 4;
5854
+ }, 0);
5855
+ }
5856
+ estimateToolCallTokens(toolCalls) {
5857
+ if (!toolCalls || toolCalls.length === 0) return 0;
5858
+ return toolCalls.reduce(
5859
+ (sum, toolCall) => sum + 12 + this.estimateTextTokens(toolCall.toolName) + this.estimateUnknownTokens(toolCall.args),
5860
+ 0
5861
+ );
5862
+ }
5863
+ estimateToolResultTokens(toolResults) {
5864
+ if (!toolResults || toolResults.length === 0) return 0;
5865
+ return toolResults.reduce(
5866
+ (sum, toolResult) => sum + 12 + this.estimateTextTokens(toolResult.toolName) + this.estimateUnknownTokens(toolResult.result),
5867
+ 0
5868
+ );
5869
+ }
5870
+ estimateMessageTokens(message) {
5871
+ return 6 + this.estimateMessageContentTokens(message.content) + this.estimateToolCallTokens(message.toolCalls) + this.estimateToolResultTokens(message.toolResults);
5872
+ }
5873
+ estimateConversationTokens(messages) {
5874
+ return messages.reduce((sum, message) => sum + this.estimateMessageTokens(message), 0);
5875
+ }
5876
+ estimateConversationBreakdown(messages) {
5877
+ let historyTokens = 0;
5878
+ let toolOutputTokens = 0;
5879
+ for (const message of messages) {
5880
+ const contentTokens = this.estimateMessageContentTokens(message.content);
5881
+ const toolCallTokens = this.estimateToolCallTokens(message.toolCalls);
5882
+ const toolResultTokens = this.estimateToolResultTokens(message.toolResults);
5883
+ const messageTotal = 6 + contentTokens + toolCallTokens + toolResultTokens;
5884
+ if (message.role === "tool") {
5885
+ toolOutputTokens += messageTotal;
5886
+ } else {
5887
+ historyTokens += 6 + contentTokens + toolCallTokens;
5888
+ toolOutputTokens += toolResultTokens;
5889
+ }
5890
+ }
5891
+ return {
5892
+ historyTokens,
5893
+ toolOutputTokens,
5894
+ estimatedInputTokens: historyTokens + toolOutputTokens
5895
+ };
5896
+ }
5897
+ estimateToolDefinitionTokens(localTools, builtinToolSchemas) {
5898
+ const localToolDefinitions = Object.entries(localTools || {}).map(([name, definition]) => ({
5899
+ name,
5900
+ description: definition.description,
5901
+ parametersSchema: definition.parametersSchema
5902
+ }));
5903
+ const payload = [...localToolDefinitions, ...builtinToolSchemas];
5904
+ if (payload.length === 0) return 0;
5905
+ return this.estimateUnknownTokens(payload);
5906
+ }
5907
+ async loadBuiltinToolSchemas(toolIds) {
5908
+ if (!toolIds || toolIds.length === 0) return [];
5909
+ try {
5910
+ const schemas = await this.client.get("/tools/schema", { toolIds });
5911
+ return Array.isArray(schemas) ? schemas : [];
5912
+ } catch {
5913
+ return [];
5914
+ }
5915
+ }
5916
+ buildDefaultCompactInstructions() {
5917
+ return [
5918
+ "Preserve changed files or best candidate paths.",
5919
+ "Preserve verification or test results.",
5920
+ "Preserve the current workflow phase and remaining work.",
5921
+ "Preserve unresolved blockers or follow-up risks.",
5922
+ "Preserve artifact references and offloaded tool outputs."
5923
+ ].join(" ");
5924
+ }
5925
+ resolveCompactInstructions(optionInstructions, agent) {
5926
+ if (typeof optionInstructions === "string" && optionInstructions.trim()) {
5927
+ return optionInstructions.trim();
5928
+ }
5929
+ const config = agent.config;
5930
+ if (!config || typeof config !== "object") {
5931
+ return void 0;
5932
+ }
5933
+ const compactInstructions = config.compactInstructions;
5934
+ return typeof compactInstructions === "string" && compactInstructions.trim() ? compactInstructions.trim() : void 0;
5935
+ }
5936
+ extractArtifactReferences(state) {
5937
+ const references = /* @__PURE__ */ new Set();
5938
+ const offloadPrefix = "[Output saved to ";
5939
+ for (const message of state.messages ?? []) {
5940
+ if (!message.toolResults) continue;
5941
+ for (const toolResult of message.toolResults) {
5942
+ if (typeof toolResult.result !== "string") continue;
5943
+ let startIndex = 0;
5944
+ while (startIndex < toolResult.result.length) {
5945
+ const prefixIndex = toolResult.result.indexOf(offloadPrefix, startIndex);
5946
+ if (prefixIndex === -1) break;
5947
+ const pathStart = prefixIndex + offloadPrefix.length;
5948
+ const closingBracket = toolResult.result.indexOf("]", pathStart);
5949
+ if (closingBracket === -1) break;
5950
+ const bracketContent = toolResult.result.slice(pathStart, closingBracket);
5951
+ const separatorIndex = bracketContent.indexOf(" \u2014 ");
5952
+ const pathText = separatorIndex === -1 ? bracketContent.trim() : bracketContent.slice(0, separatorIndex).trim();
5953
+ if (pathText) {
5954
+ references.add(pathText);
5955
+ }
5956
+ startIndex = closingBracket + 1;
5957
+ }
5958
+ }
5959
+ }
5960
+ if (state.planPath) {
5961
+ references.add(state.planPath);
5962
+ }
5963
+ return [...references].slice(0, 8);
5964
+ }
5965
+ buildContextBudgetBreakdown(details) {
5966
+ const conversationBreakdown = this.estimateConversationBreakdown(details.historyMessages);
5967
+ const currentTurnTokens = this.estimateTextTokens(details.currentTurnContent);
5968
+ const toolDefinitionTokens = this.estimateToolDefinitionTokens(
5969
+ details.localTools,
5970
+ details.builtinToolSchemas
5971
+ );
5972
+ const reservedOutputTokens = this.resolveReservedOutputTokens(details.contextLimitTokens);
5973
+ const effectiveInputBudgetTokens = this.resolveEffectiveInputBudgetTokens(details.contextLimitTokens);
5974
+ const summaryTokens = details.summaryText ? this.estimateTextTokens(
5975
+ `${_AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX}
5976
+
5977
+ ${details.summaryText}
5978
+
5979
+ Do NOT redo any of the above work.`
5980
+ ) : void 0;
5981
+ return {
5982
+ historyTokens: conversationBreakdown.historyTokens,
5983
+ toolOutputTokens: conversationBreakdown.toolOutputTokens,
5984
+ currentTurnTokens,
5985
+ toolDefinitionTokens,
5986
+ ...summaryTokens ? { summaryTokens } : {},
5987
+ ...reservedOutputTokens ? { reservedOutputTokens } : {},
5988
+ ...effectiveInputBudgetTokens ? { effectiveInputBudgetTokens } : {},
5989
+ estimatedInputTokens: conversationBreakdown.estimatedInputTokens + currentTurnTokens + toolDefinitionTokens
5990
+ };
5991
+ }
5992
+ async emitContextCompactionEvent(onContextCompaction, event) {
5993
+ if (!onContextCompaction) return;
5994
+ try {
5995
+ await onContextCompaction(event);
5996
+ } catch {
5997
+ }
5998
+ }
5999
+ async emitContextNotice(onContextNotice, event) {
6000
+ if (!onContextNotice) return;
6001
+ try {
6002
+ await onContextNotice(event);
6003
+ } catch {
6004
+ }
6005
+ }
6006
+ async buildCompactHistoryMessagesWithLifecycle(state, userContent, sessionIndex, details) {
6007
+ const baseEvent = {
6008
+ mode: details.mode,
6009
+ strategy: details.strategy,
6010
+ sessionIndex: sessionIndex + 1,
6011
+ model: details.model,
6012
+ estimatedTokens: details.beforeTokens,
6013
+ thresholdTokens: details.thresholdTokens,
6014
+ contextLimitTokens: details.contextLimitTokens,
6015
+ effectiveInputBudgetTokens: details.effectiveInputBudgetTokens,
6016
+ reservedOutputTokens: details.reservedOutputTokens,
6017
+ beforeTokens: details.beforeTokens,
6018
+ afterTokens: details.afterTokens,
6019
+ breakdown: details.breakdown
6020
+ };
6021
+ await this.emitContextCompactionEvent(details.onContextCompaction, {
6022
+ phase: "start",
6023
+ ...baseEvent
6024
+ });
6025
+ const compactMessages = this.buildCompactHistoryMessages(
6026
+ state,
6027
+ userContent,
6028
+ details.compactInstructions,
6029
+ details.mode
6030
+ );
6031
+ await this.emitContextCompactionEvent(details.onContextCompaction, {
6032
+ phase: "complete",
6033
+ ...baseEvent
6034
+ });
6035
+ return compactMessages;
6036
+ }
6037
+ buildCompactHistoryMessages(state, userContent, compactInstructions, mode = "auto") {
6038
+ const summary = this.generateCompactSummary(state, compactInstructions);
6039
+ const prefix = mode === "forced" ? _AgentsEndpoint.FORCED_COMPACT_SUMMARY_PREFIX : _AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX;
6040
+ return [
6041
+ {
6042
+ role: "system",
6043
+ content: `${prefix}
6044
+
6045
+ ${summary}
6046
+
6047
+ Do NOT redo any of the above work.`
6048
+ },
6049
+ {
6050
+ role: "user",
6051
+ content: userContent
6052
+ }
6053
+ ];
6054
+ }
6055
+ isCompactHistoryMessageSet(messages) {
6056
+ if (messages.length === 0) return false;
6057
+ const firstMessage = messages[0];
6058
+ return firstMessage?.role === "system" && typeof firstMessage.content === "string" && (firstMessage.content.startsWith(_AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX) || firstMessage.content.startsWith(_AgentsEndpoint.FORCED_COMPACT_SUMMARY_PREFIX));
6059
+ }
5510
6060
  /**
5511
6061
  * Generate a compact summary of prior work for continuation context.
5512
6062
  * Used when compact mode is enabled to keep token usage low.
5513
6063
  */
5514
- generateCompactSummary(state) {
5515
- const sessionSummaries = (state.sessions ?? []).map(
5516
- (s) => `- Session ${s.index}: ${s.stopReason} ($${s.cost.toFixed(4)}) -- ${s.outputPreview.slice(0, 100)}`
6064
+ generateCompactSummary(state, compactInstructions) {
6065
+ const recentSessions = (state.sessions ?? []).slice(-5);
6066
+ const sessionSummaries = recentSessions.map(
6067
+ (session) => `- Session ${session.index}: ${session.stopReason} ($${session.cost.toFixed(4)}) ${session.outputPreview.slice(0, 160)}`
5517
6068
  ).join("\n");
6069
+ const candidatePaths = Array.from(
6070
+ new Set(
6071
+ [
6072
+ ...state.bestCandidatePath ? [state.bestCandidatePath] : [],
6073
+ ...state.candidatePaths ?? []
6074
+ ].filter(Boolean)
6075
+ )
6076
+ ).slice(0, 8);
6077
+ 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.";
6078
+ const artifactReferences = this.extractArtifactReferences(state);
6079
+ const pendingNextStep = state.lastStopReason === "complete" ? "Confirm nothing else remains before declaring the task complete." : `Continue the ${state.workflowPhase || "research"} phase without redoing prior work.`;
6080
+ const instructions = compactInstructions || this.buildDefaultCompactInstructions();
5518
6081
  return [
5519
- `Task: ${state.taskName}`,
5520
- `Status: ${state.status}`,
5521
- `Workflow phase: ${state.workflowPhase || "research"}`,
5522
- `Sessions completed: ${state.sessionCount}`,
5523
- `Total cost: $${state.totalCost.toFixed(4)}`,
5524
- ...state.planPath ? [`Plan path: ${state.planPath}`] : [],
5525
- ...state.planWritten ? ["Plan written: yes"] : [],
5526
- ...state.bestCandidatePath ? [
5527
- `Best candidate: ${state.bestCandidatePath}`,
5528
- ...state.bestCandidateReason ? [`Candidate reason: ${state.bestCandidateReason}`] : []
5529
- ] : [],
5530
- ...state.bootstrapContext ? ["", state.bootstrapContext] : [],
6082
+ "Task + Phase",
6083
+ `- Task: ${state.taskName}`,
6084
+ `- Status: ${state.status}`,
6085
+ `- Workflow phase: ${state.workflowPhase || "research"}`,
6086
+ `- Sessions completed: ${state.sessionCount}`,
6087
+ `- Total cost: $${state.totalCost.toFixed(4)}`,
6088
+ "",
6089
+ "Completed Work",
6090
+ ...sessionSummaries ? [sessionSummaries] : ["- No completed session summaries recorded yet."],
6091
+ "",
6092
+ "Changed Files / Candidate Paths",
6093
+ ...candidatePaths.length > 0 ? candidatePaths.map((candidatePath) => `- ${candidatePath}`) : ["- No candidate paths recorded yet."],
6094
+ ...state.planPath ? [`- Plan path: ${state.planPath}`] : [],
6095
+ ...state.planWritten ? ["- Planning artifact has been written."] : [],
6096
+ ...state.bestCandidateReason ? [`- Best candidate rationale: ${state.bestCandidateReason}`] : [],
5531
6097
  "",
5532
- "Session history:",
5533
- sessionSummaries,
6098
+ "Verification Status",
6099
+ `- ${verificationSummary}`,
6100
+ ...state.lastError ? [`- Last error: ${state.lastError}`] : [],
5534
6101
  "",
5535
- "Last output (truncated):",
5536
- (state.lastOutput || "").slice(0, 1500)
6102
+ "Pending Next Step",
6103
+ `- ${pendingNextStep}`,
6104
+ "",
6105
+ "Artifact / Tool Output References",
6106
+ ...artifactReferences.length > 0 ? artifactReferences.map((reference) => `- ${reference}`) : ["- No offloaded artifacts recorded."],
6107
+ "",
6108
+ "Compaction Instructions",
6109
+ `- ${instructions}`,
6110
+ "",
6111
+ "Last Output (truncated)",
6112
+ (state.lastOutput || "").slice(0, 1800) || "- No final output recorded yet."
5537
6113
  ].join("\n");
5538
6114
  }
5539
6115
  /**
5540
6116
  * Build messages for a session, injecting progress context for continuation sessions.
5541
6117
  * Optionally accepts continuation context for marathon resume scenarios.
5542
6118
  */
5543
- buildSessionMessages(originalMessage, state, sessionIndex, maxSessions, localToolNames, continuationContext, workflow) {
6119
+ async buildSessionMessages(originalMessage, state, sessionIndex, maxSessions, localToolNames, continuationContext, workflow, builtinToolIds, compactionOptions) {
6120
+ const prepared = await this.prepareSessionContext(
6121
+ originalMessage,
6122
+ state,
6123
+ sessionIndex,
6124
+ maxSessions,
6125
+ localToolNames,
6126
+ continuationContext,
6127
+ workflow,
6128
+ builtinToolIds,
6129
+ compactionOptions
6130
+ );
6131
+ return prepared.messages;
6132
+ }
6133
+ async prepareSessionContext(originalMessage, state, sessionIndex, maxSessions, localToolNames, continuationContext, workflow, builtinToolIds, compactionOptions) {
5544
6134
  const wf = workflow ?? defaultWorkflow;
6135
+ const compactInstructions = compactionOptions?.compactInstructions;
6136
+ const resolvedStrategy = continuationContext?.compact ? "summary_fallback" : this.resolveCompactStrategy(compactionOptions?.compactStrategy, compactionOptions?.model);
6137
+ const requestContextManagement = resolvedStrategy === "provider_native" && typeof compactionOptions?.autoCompactTokenThreshold === "number" && compactionOptions.autoCompactTokenThreshold > 0 ? {
6138
+ compactStrategy: resolvedStrategy,
6139
+ ...compactInstructions ? { compactInstructions } : {},
6140
+ nativeCompaction: {
6141
+ provider: "anthropic",
6142
+ thresholdTokens: compactionOptions.autoCompactTokenThreshold
6143
+ }
6144
+ } : void 0;
6145
+ const maybeEmitToolDefinitionWarning = async (breakdown) => {
6146
+ if (sessionIndex !== 0) return;
6147
+ if (typeof compactionOptions?.contextLimitTokens !== "number" || compactionOptions.contextLimitTokens <= 0) {
6148
+ return;
6149
+ }
6150
+ if (breakdown.toolDefinitionTokens <= 0) return;
6151
+ const ratio = breakdown.toolDefinitionTokens / compactionOptions.contextLimitTokens;
6152
+ if (ratio < 0.1) return;
6153
+ await this.emitContextNotice(compactionOptions?.onContextNotice, {
6154
+ kind: "tool_definitions_warning",
6155
+ sessionIndex: sessionIndex + 1,
6156
+ model: compactionOptions?.model,
6157
+ 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.`,
6158
+ estimatedTokens: breakdown.toolDefinitionTokens,
6159
+ contextLimitTokens: compactionOptions.contextLimitTokens,
6160
+ effectiveInputBudgetTokens: breakdown.effectiveInputBudgetTokens,
6161
+ reservedOutputTokens: breakdown.reservedOutputTokens
6162
+ });
6163
+ };
6164
+ const buildNativeCompactionEvent = (mode, breakdown) => {
6165
+ if (resolvedStrategy !== "provider_native" || typeof compactionOptions?.autoCompactTokenThreshold !== "number" || compactionOptions.autoCompactTokenThreshold <= 0 || breakdown.estimatedInputTokens < compactionOptions.autoCompactTokenThreshold) {
6166
+ return void 0;
6167
+ }
6168
+ return {
6169
+ mode,
6170
+ strategy: resolvedStrategy,
6171
+ sessionIndex: sessionIndex + 1,
6172
+ model: compactionOptions?.model,
6173
+ estimatedTokens: breakdown.estimatedInputTokens,
6174
+ thresholdTokens: compactionOptions.autoCompactTokenThreshold,
6175
+ contextLimitTokens: compactionOptions.contextLimitTokens,
6176
+ effectiveInputBudgetTokens: breakdown.effectiveInputBudgetTokens,
6177
+ reservedOutputTokens: breakdown.reservedOutputTokens,
6178
+ beforeTokens: breakdown.estimatedInputTokens,
6179
+ breakdown
6180
+ };
6181
+ };
5545
6182
  const currentPhase = wf.phases.find((p) => p.name === state.workflowPhase);
5546
6183
  const toolGuidanceLines = currentPhase?.buildToolGuidance(state) ?? [];
5547
6184
  const isDeployWorkflow = wf.name === "deploy";
5548
- const toolsBlock = localToolNames?.length ? [
6185
+ const localToolsBlock = localToolNames?.length ? [
5549
6186
  "",
5550
6187
  "--- Local Tools ---",
5551
6188
  `You have access to tools (${localToolNames.join(", ")}) that execute directly on the user's machine.`,
@@ -5555,6 +6192,14 @@ var _AgentsEndpoint = class _AgentsEndpoint {
5555
6192
  ...toolGuidanceLines,
5556
6193
  ...isDeployWorkflow ? [] : ["Always use write_file to save your output so the user can run it immediately."]
5557
6194
  ].join("\n") : "";
6195
+ const builtinToolNames = builtinToolIds?.map((id) => id.replace(/^builtin:/, ""));
6196
+ const builtinToolsBlock = builtinToolNames?.length ? [
6197
+ "",
6198
+ "--- Built-in Tools ---",
6199
+ `You have access to built-in tools (${builtinToolNames.join(", ")}) for web search, web scraping, image generation, and other capabilities.`,
6200
+ "Use these tools when the task requires information from the web, generating images, or other capabilities beyond local file operations."
6201
+ ].join("\n") : "";
6202
+ const toolsBlock = localToolsBlock + builtinToolsBlock;
5558
6203
  const bootstrapBlock = state.bootstrapContext ? ["", "--- Initial Repository Discovery ---", state.bootstrapContext].join("\n") : "";
5559
6204
  const phaseBlock = ["", this.buildPhaseInstructions(state, wf)].join("\n");
5560
6205
  const candidateBlock = wf.buildCandidateBlock?.(state) ?? "";
@@ -5562,25 +6207,16 @@ var _AgentsEndpoint = class _AgentsEndpoint {
5562
6207
  if (continuationContext && sessionIndex === 0) {
5563
6208
  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.";
5564
6209
  const userMessage = continuationContext.newUserMessage || defaultContinueMessage;
5565
- if (continuationContext.compact) {
5566
- const summary = this.generateCompactSummary(state);
5567
- const messages2 = [
5568
- {
5569
- role: "system",
5570
- content: `You are continuing a previously completed task. Here is a summary of prior work:
5571
-
5572
- ${summary}
5573
-
5574
- Do NOT redo any of the above work.`
5575
- },
5576
- {
5577
- role: "user",
5578
- content: [userMessage, phaseBlock, toolsBlock, bootstrapBlock, candidateBlock, "", multiSessionInstruction].join("\n")
5579
- }
5580
- ];
5581
- return messages2;
5582
- }
5583
- const messages = [
6210
+ const userContent = [
6211
+ userMessage,
6212
+ phaseBlock,
6213
+ toolsBlock,
6214
+ bootstrapBlock,
6215
+ candidateBlock,
6216
+ "",
6217
+ multiSessionInstruction
6218
+ ].join("\n");
6219
+ const fullHistoryMessages = [
5584
6220
  ...continuationContext.previousMessages,
5585
6221
  {
5586
6222
  role: "system",
@@ -5588,14 +6224,79 @@ Do NOT redo any of the above work.`
5588
6224
  },
5589
6225
  {
5590
6226
  role: "user",
5591
- content: [userMessage, phaseBlock, toolsBlock, bootstrapBlock, candidateBlock, "", multiSessionInstruction].join("\n")
6227
+ content: userContent
5592
6228
  }
5593
6229
  ];
5594
- return messages;
6230
+ const summaryText = this.generateCompactSummary(state, compactInstructions);
6231
+ const breakdown = this.buildContextBudgetBreakdown({
6232
+ historyMessages: continuationContext.previousMessages,
6233
+ currentTurnContent: userContent,
6234
+ localTools: compactionOptions?.localTools,
6235
+ builtinToolSchemas: compactionOptions?.builtinToolSchemas || [],
6236
+ summaryText,
6237
+ contextLimitTokens: compactionOptions?.contextLimitTokens
6238
+ });
6239
+ await maybeEmitToolDefinitionWarning(breakdown);
6240
+ if (continuationContext.compact) {
6241
+ return {
6242
+ messages: await this.buildCompactHistoryMessagesWithLifecycle(
6243
+ state,
6244
+ userContent,
6245
+ sessionIndex,
6246
+ {
6247
+ mode: "forced",
6248
+ strategy: "summary_fallback",
6249
+ model: compactionOptions?.model,
6250
+ beforeTokens: breakdown.estimatedInputTokens,
6251
+ afterTokens: (breakdown.summaryTokens || 0) + breakdown.currentTurnTokens + breakdown.toolDefinitionTokens,
6252
+ thresholdTokens: compactionOptions?.autoCompactTokenThreshold,
6253
+ contextLimitTokens: compactionOptions?.contextLimitTokens,
6254
+ effectiveInputBudgetTokens: breakdown.effectiveInputBudgetTokens,
6255
+ reservedOutputTokens: breakdown.reservedOutputTokens,
6256
+ breakdown,
6257
+ onContextCompaction: compactionOptions?.onContextCompaction,
6258
+ compactInstructions
6259
+ }
6260
+ ),
6261
+ requestContextManagement
6262
+ };
6263
+ }
6264
+ if (resolvedStrategy === "summary_fallback" && typeof compactionOptions?.autoCompactTokenThreshold === "number" && compactionOptions.autoCompactTokenThreshold > 0 && breakdown.estimatedInputTokens >= compactionOptions.autoCompactTokenThreshold) {
6265
+ return {
6266
+ messages: await this.buildCompactHistoryMessagesWithLifecycle(
6267
+ state,
6268
+ userContent,
6269
+ sessionIndex,
6270
+ {
6271
+ mode: "auto",
6272
+ strategy: "summary_fallback",
6273
+ model: compactionOptions?.model,
6274
+ beforeTokens: breakdown.estimatedInputTokens,
6275
+ afterTokens: (breakdown.summaryTokens || 0) + breakdown.currentTurnTokens + breakdown.toolDefinitionTokens,
6276
+ thresholdTokens: compactionOptions?.autoCompactTokenThreshold,
6277
+ contextLimitTokens: compactionOptions?.contextLimitTokens,
6278
+ effectiveInputBudgetTokens: breakdown.effectiveInputBudgetTokens,
6279
+ reservedOutputTokens: breakdown.reservedOutputTokens,
6280
+ breakdown,
6281
+ onContextCompaction: compactionOptions?.onContextCompaction,
6282
+ compactInstructions
6283
+ }
6284
+ ),
6285
+ requestContextManagement
6286
+ };
6287
+ }
6288
+ return {
6289
+ messages: fullHistoryMessages,
6290
+ requestContextManagement,
6291
+ pendingNativeCompactionEvent: buildNativeCompactionEvent("auto", breakdown)
6292
+ };
5595
6293
  }
5596
6294
  if (sessionIndex === 0) {
5597
6295
  const content2 = [originalMessage, phaseBlock, toolsBlock, bootstrapBlock, candidateBlock, "", multiSessionInstruction].join("\n");
5598
- return [{ role: "user", content: content2 }];
6296
+ return {
6297
+ messages: [{ role: "user", content: content2 }],
6298
+ requestContextManagement
6299
+ };
5599
6300
  }
5600
6301
  const recentSessions = state.sessions.slice(-5);
5601
6302
  const progressSummary = recentSessions.map(
@@ -5629,10 +6330,49 @@ Do NOT redo any of the above work.`
5629
6330
  ...historyMessages.slice(-MAX_HISTORY_MESSAGES)
5630
6331
  ];
5631
6332
  }
5632
- return [
6333
+ const summaryText = this.generateCompactSummary(state, compactInstructions);
6334
+ const breakdown = this.buildContextBudgetBreakdown({
6335
+ historyMessages,
6336
+ currentTurnContent: continuationContent,
6337
+ localTools: compactionOptions?.localTools,
6338
+ builtinToolSchemas: compactionOptions?.builtinToolSchemas || [],
6339
+ summaryText,
6340
+ contextLimitTokens: compactionOptions?.contextLimitTokens
6341
+ });
6342
+ await maybeEmitToolDefinitionWarning(breakdown);
6343
+ const messages = [
5633
6344
  ...historyMessages,
5634
6345
  { role: "user", content: continuationContent }
5635
6346
  ];
6347
+ if (resolvedStrategy === "summary_fallback" && typeof compactionOptions?.autoCompactTokenThreshold === "number" && compactionOptions.autoCompactTokenThreshold > 0 && breakdown.estimatedInputTokens >= compactionOptions.autoCompactTokenThreshold) {
6348
+ return {
6349
+ messages: await this.buildCompactHistoryMessagesWithLifecycle(
6350
+ state,
6351
+ continuationContent,
6352
+ sessionIndex,
6353
+ {
6354
+ mode: "auto",
6355
+ strategy: "summary_fallback",
6356
+ model: compactionOptions?.model,
6357
+ beforeTokens: breakdown.estimatedInputTokens,
6358
+ afterTokens: (breakdown.summaryTokens || 0) + breakdown.currentTurnTokens + breakdown.toolDefinitionTokens,
6359
+ thresholdTokens: compactionOptions?.autoCompactTokenThreshold,
6360
+ contextLimitTokens: compactionOptions?.contextLimitTokens,
6361
+ effectiveInputBudgetTokens: breakdown.effectiveInputBudgetTokens,
6362
+ reservedOutputTokens: breakdown.reservedOutputTokens,
6363
+ breakdown,
6364
+ onContextCompaction: compactionOptions?.onContextCompaction,
6365
+ compactInstructions
6366
+ }
6367
+ ),
6368
+ requestContextManagement
6369
+ };
6370
+ }
6371
+ return {
6372
+ messages,
6373
+ requestContextManagement,
6374
+ pendingNativeCompactionEvent: buildNativeCompactionEvent("auto", breakdown)
6375
+ };
5636
6376
  }
5637
6377
  const recoveryMessage = this.buildStuckTurnRecoveryMessage(state, wf);
5638
6378
  const content = [
@@ -5652,7 +6392,10 @@ Do NOT redo any of the above work.`
5652
6392
  "",
5653
6393
  "Continue where you left off. Do not redo previous work. If the task is already complete, respond with TASK_COMPLETE."
5654
6394
  ].join("\n");
5655
- return [{ role: "user", content }];
6395
+ return {
6396
+ messages: [{ role: "user", content }],
6397
+ requestContextManagement
6398
+ };
5656
6399
  }
5657
6400
  /**
5658
6401
  * Upsert a record to sync long-task progress to the dashboard.
@@ -5702,6 +6445,8 @@ Do NOT redo any of the above work.`
5702
6445
  }
5703
6446
  }
5704
6447
  };
6448
+ _AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX = "You are continuing a long-running task. Here is a compact summary of prior work:";
6449
+ _AgentsEndpoint.FORCED_COMPACT_SUMMARY_PREFIX = "You are continuing a previously completed task. Here is a summary of prior work:";
5705
6450
  /** Stop phrases that indicate the agent considers its task complete. */
5706
6451
  _AgentsEndpoint.STOP_PHRASES = [
5707
6452
  "DONE:",
@@ -7011,6 +7756,7 @@ var ClientEvalBuilder = class extends EvalBuilder {
7011
7756
  defaultWorkflow,
7012
7757
  deployWorkflow,
7013
7758
  evaluateGeneratedRuntimeToolProposal,
7759
+ gameWorkflow,
7014
7760
  getDefaultPlanPath,
7015
7761
  getLikelySupportingCandidatePaths,
7016
7762
  isDiscoveryToolName,