@runtypelabs/sdk 1.8.0 → 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
@@ -3108,7 +3108,11 @@ var designPhase = {
3108
3108
  buildTransitionSummary(_state, _nextPhaseName) {
3109
3109
  return [
3110
3110
  "Automatic phase transition: design \u2192 build.",
3111
- "Requirements understood. Build the game and deploy it using deploy_sandbox with the files parameter."
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."
3112
3116
  ].join("\n");
3113
3117
  },
3114
3118
  interceptToolCall(toolName, _args, _ctx) {
@@ -3168,7 +3172,32 @@ var buildPhase = {
3168
3172
  " 4. If the deploy fails, read the error output, fix the code, and call deploy_sandbox again.",
3169
3173
  " 5. The sandbox is persistent \u2014 subsequent calls reuse the same sandbox.",
3170
3174
  " 6. Load game libraries from CDN in your HTML (e.g., Three.js from unpkg/cdnjs) OR include them in packageJson.",
3171
- " 7. Make the game fullscreen by default (width: 100vw, height: 100vh, no margin/padding on body)."
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
+ " }"
3172
3201
  ].join("\n");
3173
3202
  },
3174
3203
  buildToolGuidance(_state) {
@@ -3182,8 +3211,23 @@ var buildPhase = {
3182
3211
  isComplete() {
3183
3212
  return false;
3184
3213
  },
3185
- interceptToolCall(toolName, _args, _ctx) {
3186
- return blockLocalTools(toolName);
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;
3187
3231
  },
3188
3232
  canAcceptCompletion(_state, trace) {
3189
3233
  return trace.entries.some((entry) => entry.startsWith("deploy_sandbox"));
@@ -5479,6 +5523,8 @@ var _AgentsEndpoint = class _AgentsEndpoint {
5479
5523
  this.updateWorkflowPhase(state, this.createEmptyToolTrace(), workflow);
5480
5524
  let recordId;
5481
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);
5482
5528
  if (!options.previousMessages) {
5483
5529
  if (workflow.generateBootstrapContext) {
5484
5530
  state.bootstrapContext = await workflow.generateBootstrapContext(
@@ -5512,7 +5558,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
5512
5558
  if (session === 0 && !options.previousMessages) {
5513
5559
  state.originalMessage = options.message;
5514
5560
  }
5515
- const messages = this.buildSessionMessages(
5561
+ const preparedSession = await this.prepareSessionContext(
5516
5562
  options.message,
5517
5563
  state,
5518
5564
  session,
@@ -5520,61 +5566,93 @@ var _AgentsEndpoint = class _AgentsEndpoint {
5520
5566
  localToolNames,
5521
5567
  continuationContext,
5522
5568
  workflow,
5523
- options.toolIds
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
+ }
5524
5581
  );
5582
+ const {
5583
+ messages,
5584
+ requestContextManagement,
5585
+ pendingNativeCompactionEvent
5586
+ } = preparedSession;
5525
5587
  let sessionResult;
5526
5588
  const sessionData = {
5527
5589
  messages,
5528
5590
  debugMode: options.debugMode,
5529
5591
  model: options.model,
5530
- ...options.toolIds?.length ? { tools: { toolIds: options.toolIds } } : {}
5592
+ ...options.toolIds?.length ? { tools: { toolIds: options.toolIds } } : {},
5593
+ ...requestContextManagement ? { contextManagement: requestContextManagement } : {}
5531
5594
  };
5532
5595
  let sessionToolMessages = [];
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");
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);
5546
5648
  }
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");
5649
+ } finally {
5650
+ if (pendingNativeCompactionEvent) {
5651
+ await this.emitContextCompactionEvent(options.onContextCompaction, {
5652
+ phase: "complete",
5653
+ ...pendingNativeCompactionEvent
5654
+ });
5566
5655
  }
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);
5578
5656
  }
5579
5657
  const toolTraceSummary = this.buildToolTraceSummary(sessionTrace);
5580
5658
  const effectiveSessionOutput = this.buildEffectiveSessionOutput(
@@ -5661,7 +5739,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
5661
5739
  }
5662
5740
  }
5663
5741
  if (!state.messages) state.messages = [];
5664
- 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) {
5665
5745
  const newMessages = messages.slice(state.messages.length);
5666
5746
  state.messages.push(...newMessages);
5667
5747
  } else {
@@ -5726,41 +5806,379 @@ var _AgentsEndpoint = class _AgentsEndpoint {
5726
5806
  const upper = output.toUpperCase();
5727
5807
  return _AgentsEndpoint.STOP_PHRASES.some((phrase) => upper.includes(phrase.toUpperCase()));
5728
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
+ }
5729
6060
  /**
5730
6061
  * Generate a compact summary of prior work for continuation context.
5731
6062
  * Used when compact mode is enabled to keep token usage low.
5732
6063
  */
5733
- generateCompactSummary(state) {
5734
- const sessionSummaries = (state.sessions ?? []).map(
5735
- (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)}`
5736
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();
5737
6081
  return [
5738
- `Task: ${state.taskName}`,
5739
- `Status: ${state.status}`,
5740
- `Workflow phase: ${state.workflowPhase || "research"}`,
5741
- `Sessions completed: ${state.sessionCount}`,
5742
- `Total cost: $${state.totalCost.toFixed(4)}`,
5743
- ...state.planPath ? [`Plan path: ${state.planPath}`] : [],
5744
- ...state.planWritten ? ["Plan written: yes"] : [],
5745
- ...state.bestCandidatePath ? [
5746
- `Best candidate: ${state.bestCandidatePath}`,
5747
- ...state.bestCandidateReason ? [`Candidate reason: ${state.bestCandidateReason}`] : []
5748
- ] : [],
5749
- ...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}`] : [],
6097
+ "",
6098
+ "Verification Status",
6099
+ `- ${verificationSummary}`,
6100
+ ...state.lastError ? [`- Last error: ${state.lastError}`] : [],
6101
+ "",
6102
+ "Pending Next Step",
6103
+ `- ${pendingNextStep}`,
5750
6104
  "",
5751
- "Session history:",
5752
- sessionSummaries,
6105
+ "Artifact / Tool Output References",
6106
+ ...artifactReferences.length > 0 ? artifactReferences.map((reference) => `- ${reference}`) : ["- No offloaded artifacts recorded."],
5753
6107
  "",
5754
- "Last output (truncated):",
5755
- (state.lastOutput || "").slice(0, 1500)
6108
+ "Compaction Instructions",
6109
+ `- ${instructions}`,
6110
+ "",
6111
+ "Last Output (truncated)",
6112
+ (state.lastOutput || "").slice(0, 1800) || "- No final output recorded yet."
5756
6113
  ].join("\n");
5757
6114
  }
5758
6115
  /**
5759
6116
  * Build messages for a session, injecting progress context for continuation sessions.
5760
6117
  * Optionally accepts continuation context for marathon resume scenarios.
5761
6118
  */
5762
- buildSessionMessages(originalMessage, state, sessionIndex, maxSessions, localToolNames, continuationContext, workflow, builtinToolIds) {
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) {
5763
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
+ };
5764
6182
  const currentPhase = wf.phases.find((p) => p.name === state.workflowPhase);
5765
6183
  const toolGuidanceLines = currentPhase?.buildToolGuidance(state) ?? [];
5766
6184
  const isDeployWorkflow = wf.name === "deploy";
@@ -5789,25 +6207,16 @@ var _AgentsEndpoint = class _AgentsEndpoint {
5789
6207
  if (continuationContext && sessionIndex === 0) {
5790
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.";
5791
6209
  const userMessage = continuationContext.newUserMessage || defaultContinueMessage;
5792
- if (continuationContext.compact) {
5793
- const summary = this.generateCompactSummary(state);
5794
- const messages2 = [
5795
- {
5796
- role: "system",
5797
- content: `You are continuing a previously completed task. Here is a summary of prior work:
5798
-
5799
- ${summary}
5800
-
5801
- Do NOT redo any of the above work.`
5802
- },
5803
- {
5804
- role: "user",
5805
- content: [userMessage, phaseBlock, toolsBlock, bootstrapBlock, candidateBlock, "", multiSessionInstruction].join("\n")
5806
- }
5807
- ];
5808
- return messages2;
5809
- }
5810
- const messages = [
6210
+ const userContent = [
6211
+ userMessage,
6212
+ phaseBlock,
6213
+ toolsBlock,
6214
+ bootstrapBlock,
6215
+ candidateBlock,
6216
+ "",
6217
+ multiSessionInstruction
6218
+ ].join("\n");
6219
+ const fullHistoryMessages = [
5811
6220
  ...continuationContext.previousMessages,
5812
6221
  {
5813
6222
  role: "system",
@@ -5815,14 +6224,79 @@ Do NOT redo any of the above work.`
5815
6224
  },
5816
6225
  {
5817
6226
  role: "user",
5818
- content: [userMessage, phaseBlock, toolsBlock, bootstrapBlock, candidateBlock, "", multiSessionInstruction].join("\n")
6227
+ content: userContent
5819
6228
  }
5820
6229
  ];
5821
- 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
+ };
5822
6293
  }
5823
6294
  if (sessionIndex === 0) {
5824
6295
  const content2 = [originalMessage, phaseBlock, toolsBlock, bootstrapBlock, candidateBlock, "", multiSessionInstruction].join("\n");
5825
- return [{ role: "user", content: content2 }];
6296
+ return {
6297
+ messages: [{ role: "user", content: content2 }],
6298
+ requestContextManagement
6299
+ };
5826
6300
  }
5827
6301
  const recentSessions = state.sessions.slice(-5);
5828
6302
  const progressSummary = recentSessions.map(
@@ -5856,10 +6330,49 @@ Do NOT redo any of the above work.`
5856
6330
  ...historyMessages.slice(-MAX_HISTORY_MESSAGES)
5857
6331
  ];
5858
6332
  }
5859
- 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 = [
5860
6344
  ...historyMessages,
5861
6345
  { role: "user", content: continuationContent }
5862
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
+ };
5863
6376
  }
5864
6377
  const recoveryMessage = this.buildStuckTurnRecoveryMessage(state, wf);
5865
6378
  const content = [
@@ -5879,7 +6392,10 @@ Do NOT redo any of the above work.`
5879
6392
  "",
5880
6393
  "Continue where you left off. Do not redo previous work. If the task is already complete, respond with TASK_COMPLETE."
5881
6394
  ].join("\n");
5882
- return [{ role: "user", content }];
6395
+ return {
6396
+ messages: [{ role: "user", content }],
6397
+ requestContextManagement
6398
+ };
5883
6399
  }
5884
6400
  /**
5885
6401
  * Upsert a record to sync long-task progress to the dashboard.
@@ -5929,6 +6445,8 @@ Do NOT redo any of the above work.`
5929
6445
  }
5930
6446
  }
5931
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:";
5932
6450
  /** Stop phrases that indicate the agent considers its task complete. */
5933
6451
  _AgentsEndpoint.STOP_PHRASES = [
5934
6452
  "DONE:",