titan-agent 5.0.2 → 5.0.3

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.
Files changed (176) hide show
  1. package/dist/agent/agent.js +48 -3
  2. package/dist/agent/agent.js.map +1 -1
  3. package/dist/agent/agentLoop.js +83 -5
  4. package/dist/agent/agentLoop.js.map +1 -1
  5. package/dist/agent/commandPost.js +1 -1
  6. package/dist/agent/commandPost.js.map +1 -1
  7. package/dist/agent/goalProposer.js +2 -2
  8. package/dist/agent/goalProposer.js.map +1 -1
  9. package/dist/agent/missionDriver.js +1 -1
  10. package/dist/agent/missionDriver.js.map +1 -1
  11. package/dist/agent/promptBudget.js +85 -0
  12. package/dist/agent/promptBudget.js.map +1 -0
  13. package/dist/agent/structuredSpawn.js +1 -1
  14. package/dist/agent/structuredSpawn.js.map +1 -1
  15. package/dist/agent/subtaskTaxonomy.js +1 -1
  16. package/dist/agent/subtaskTaxonomy.js.map +1 -1
  17. package/dist/agent/systemPromptParts.js +10 -1
  18. package/dist/agent/systemPromptParts.js.map +1 -1
  19. package/dist/agent/toolRunner.js +16 -0
  20. package/dist/agent/toolRunner.js.map +1 -1
  21. package/dist/agent/toolSearch.js +4 -1
  22. package/dist/agent/toolSearch.js.map +1 -1
  23. package/dist/analytics/bugReports.js +1 -1
  24. package/dist/analytics/bugReports.js.map +1 -1
  25. package/dist/channels/messenger.js +1 -1
  26. package/dist/channels/messenger.js.map +1 -1
  27. package/dist/eval/harness.js +141 -0
  28. package/dist/eval/harness.js.map +1 -0
  29. package/dist/gateway/server.js +374 -74
  30. package/dist/gateway/server.js.map +1 -1
  31. package/dist/hooks/shellHooks.js +1 -1
  32. package/dist/hooks/shellHooks.js.map +1 -1
  33. package/dist/lib/auto-heal/repair-strategies.js.map +1 -1
  34. package/dist/memory/promptIncludes.js +58 -0
  35. package/dist/memory/promptIncludes.js.map +1 -0
  36. package/dist/organism/alertsStore.js +70 -0
  37. package/dist/organism/alertsStore.js.map +1 -0
  38. package/dist/plugins/memoryRetrieval.js.map +1 -1
  39. package/dist/providers/ollama.js +7 -7
  40. package/dist/providers/ollama.js.map +1 -1
  41. package/dist/safety/invariants.js +60 -0
  42. package/dist/safety/invariants.js.map +1 -0
  43. package/dist/safety/opusReview.js +1 -1
  44. package/dist/safety/opusReview.js.map +1 -1
  45. package/dist/security/commandScanner.js +2 -2
  46. package/dist/security/commandScanner.js.map +1 -1
  47. package/dist/security/secretGuard.js +4 -4
  48. package/dist/security/secretGuard.js.map +1 -1
  49. package/dist/skills/builtin/widget_gallery.js +28 -1
  50. package/dist/skills/builtin/widget_gallery.js.map +1 -1
  51. package/dist/skills/frontmatterLoader.js +119 -0
  52. package/dist/skills/frontmatterLoader.js.map +1 -0
  53. package/dist/skills/registry.js +20 -0
  54. package/dist/skills/registry.js.map +1 -1
  55. package/dist/testing/testHealthMonitor.js +1 -2
  56. package/dist/testing/testHealthMonitor.js.map +1 -1
  57. package/dist/utils/constants.js +2 -2
  58. package/dist/utils/constants.js.map +1 -1
  59. package/dist/utils/replyQuality.js +1 -1
  60. package/dist/utils/replyQuality.js.map +1 -1
  61. package/dist/utils/tokens.js +1 -1
  62. package/dist/utils/tokens.js.map +1 -1
  63. package/docs/bleeding-edge-agents-2026.md +450 -0
  64. package/docs/langchain-analysis.md +598 -0
  65. package/docs/langchain-code-analysis.md +363 -0
  66. package/docs/space-agent-analysis.md +300 -0
  67. package/package.json +1 -1
  68. package/ui/dist/assets/{AuditPanel-G7YA1HzV.js → AuditPanel-B84Mp16G.js} +2 -2
  69. package/ui/dist/assets/AutonomyPanel-DOtiTFxV.js +11 -0
  70. package/ui/dist/assets/{AutopilotPanel-CHRjxdh0.js → AutopilotPanel-nTb1Dnru.js} +1 -1
  71. package/ui/dist/assets/AutoresearchPanel-D46mX8VF.js +6 -0
  72. package/ui/dist/assets/BackupPanel-DGM1XXbG.js +1 -0
  73. package/ui/dist/assets/BrowserPanel-Cn1tTN3y.js +6 -0
  74. package/ui/dist/assets/{CPAgents-D5533PhK.js → CPAgents-CEraUkME.js} +1 -1
  75. package/ui/dist/assets/{CPDashboard-C-GgqDsI.js → CPDashboard-B_yidGAe.js} +2 -2
  76. package/ui/dist/assets/CPFiles-BBS8jtYH.js +1 -0
  77. package/ui/dist/assets/CPGoals-DL5v21TZ.js +1 -0
  78. package/ui/dist/assets/CPInbox-CyLQJBYF.js +11 -0
  79. package/ui/dist/assets/{CPSocial-mUQsrSh5.js → CPSocial-BkEtQ1Um.js} +3 -3
  80. package/ui/dist/assets/ChannelsPanel-CD2kHhA5.js +1 -0
  81. package/ui/dist/assets/CheckpointsPanel-BrUTFPu_.js +1 -0
  82. package/ui/dist/assets/CommandPostHub-BPPaUv1B.js +29 -0
  83. package/ui/dist/assets/CronPanel-CsfQctFp.js +1 -0
  84. package/ui/dist/assets/DaemonPanel-CNUggBbL.js +1 -0
  85. package/ui/dist/assets/DataTable-DuAEp_QJ.js +1 -0
  86. package/ui/dist/assets/{EmptyState-D60-wQrz.js → EmptyState-DFrAEZDm.js} +1 -1
  87. package/ui/dist/assets/EvalPanel-DEX0a5-b.js +1 -0
  88. package/ui/dist/assets/{FilesPanel-BNN3h_HW.js → FilesPanel-DATsiAqG.js} +1 -1
  89. package/ui/dist/assets/FleetPanel-QYQKqx4W.js +1 -0
  90. package/ui/dist/assets/{HomelabPanel-1mfhRBh6.js → HomelabPanel-DhuXd3ZD.js} +2 -2
  91. package/ui/dist/assets/{InfraView-Df6SFI7b.js → InfraView-eS7cpESw.js} +2 -2
  92. package/ui/dist/assets/InlineEditableField-zIAnW4AR.js +1 -0
  93. package/ui/dist/assets/{Input-DYukme8A.js → Input-bFsLI0fq.js} +1 -1
  94. package/ui/dist/assets/IntegrationsPanel-C_FswSRN.js +1 -0
  95. package/ui/dist/assets/IntelligenceView-smQ6aBwx.js +2 -0
  96. package/ui/dist/assets/{LearningPanel-BPx05bBu.js → LearningPanel-BEgF_iND.js} +1 -1
  97. package/ui/dist/assets/{LogsPanel-D3Qfp2SE.js → LogsPanel-Br1P8ST6.js} +1 -1
  98. package/ui/dist/assets/McpPanel-ByvQ12J_.js +1 -0
  99. package/ui/dist/assets/{MemoryGraphPanel-BFovwaSG.js → MemoryGraphPanel-BGOeSaET.js} +1 -1
  100. package/ui/dist/assets/MemoryWikiPanel-CR8btd66.js +11 -0
  101. package/ui/dist/assets/MeshPanel-BjkcSOMz.js +11 -0
  102. package/ui/dist/assets/NvidiaPanel-NYt42w7L.js +1 -0
  103. package/ui/dist/assets/OrganismPanel-PHvISvVn.js +1 -0
  104. package/ui/dist/assets/OverviewPanel-q35zdMr6.js +6 -0
  105. package/ui/dist/assets/{PageHeader-BdvxKoad.js → PageHeader-Cwn3OALc.js} +1 -1
  106. package/ui/dist/assets/PaperclipPanel-BDpQki0d.js +1 -0
  107. package/ui/dist/assets/{PersonasPanel-BpI6Npxv.js → PersonasPanel-DxrGW5C4.js} +1 -1
  108. package/ui/dist/assets/RecipesPanel-CYRdBx5u.js +1 -0
  109. package/ui/dist/assets/{SecurityPanel-CBDsEAFz.js → SecurityPanel-i1QMctV0.js} +1 -1
  110. package/ui/dist/assets/SelfImprovePanel-DbybAZWp.js +1 -0
  111. package/ui/dist/assets/SelfProposalsPanel-DtcTUDDd.js +2 -0
  112. package/ui/dist/assets/SessionsPanel-B7QmOizR.js +1 -0
  113. package/ui/dist/assets/SessionsTab-BdJj_vsI.js +1 -0
  114. package/ui/dist/assets/{SettingsPanel-BiWHsOAJ.js → SettingsPanel-DnEvJUFe.js} +1 -1
  115. package/ui/dist/assets/SettingsView-C39dk_yr.js +2 -0
  116. package/ui/dist/assets/{SkeletonLoader-CGtpZJ-7.js → SkeletonLoader-CsiR8ED9.js} +1 -1
  117. package/ui/dist/assets/{SkillsPanel-Z_9jA6dU.js → SkillsPanel-DM4qBFDS.js} +1 -1
  118. package/ui/dist/assets/{SomaView-AP3BXqf-.js → SomaView-CWnPKEQI.js} +1 -1
  119. package/ui/dist/assets/{StatCard-CrnvXPg5.js → StatCard-CY8lgeWm.js} +1 -1
  120. package/ui/dist/assets/{StatusBadge-B6r5EWBA.js → StatusBadge-CGvKbP7R.js} +1 -1
  121. package/ui/dist/assets/TeamsPanel-Bf6GaUni.js +1 -0
  122. package/ui/dist/assets/{TelemetryPanel-D6o14H-i.js → TelemetryPanel-JZ90gJXC.js} +1 -1
  123. package/ui/dist/assets/TitanCanvas-Hk49NFcA.js +1092 -0
  124. package/ui/dist/assets/ToolsView-Cq7Fuq3i.js +2 -0
  125. package/ui/dist/assets/{Tooltip-DNsYGHC9.js → Tooltip-CcoZrKsl.js} +1 -1
  126. package/ui/dist/assets/{TraceViewer-TOpdmqLF.js → TraceViewer-ojGf0drx.js} +1 -1
  127. package/ui/dist/assets/TrainingPanel-CWnP4H2l.js +1 -0
  128. package/ui/dist/assets/{VoiceOverlay-XIyCbAP7.js → VoiceOverlay-Dn6iaYgd.js} +1 -1
  129. package/ui/dist/assets/VramPanel-CLd9Ggck.js +1 -0
  130. package/ui/dist/assets/WatchView-CQBemwsm.js +13 -0
  131. package/ui/dist/assets/WorkTab-BOfTN-Bd.js +1 -0
  132. package/ui/dist/assets/WorkflowsPanel-qzNS0p0u.js +11 -0
  133. package/ui/dist/assets/{arrow-left-CQF-yBIU.js → arrow-left-c-8OFZUV.js} +1 -1
  134. package/ui/dist/assets/{chart-column-1smg0GbX.js → chart-column-x6L66Qw7.js} +1 -1
  135. package/ui/dist/assets/{circle-check-big-BiMDFx6C.js → circle-check-big-WaW3U3Xl.js} +1 -1
  136. package/ui/dist/assets/{dollar-sign-DMYH4Q_a.js → dollar-sign-D2Oce4Ru.js} +1 -1
  137. package/ui/dist/assets/{download-BYFd-yl6.js → download-YvPDLlFJ.js} +1 -1
  138. package/ui/dist/assets/eye-off-DIMcxsdQ.js +6 -0
  139. package/ui/dist/assets/{funnel-pWBglhfw.js → funnel-DqD9srZu.js} +1 -1
  140. package/ui/dist/assets/{git-branch-Cgqic2Us.js → git-branch-0FamUEbU.js} +1 -1
  141. package/ui/dist/assets/index-D932CbpQ.css +1 -0
  142. package/ui/dist/assets/index-NatBSFxj.js +227 -0
  143. package/ui/dist/assets/{legacy-BHbi-Nm_.js → legacy-DOO7F5cq.js} +1 -1
  144. package/ui/dist/assets/{lightbulb-D_y0Mtyq.js → lightbulb-Bk6KlR6q.js} +1 -1
  145. package/ui/dist/assets/pause-DDC_zUiJ.js +6 -0
  146. package/ui/dist/assets/{play-2xR4_zUG.js → play-BPXbHToG.js} +1 -1
  147. package/ui/dist/assets/{plug-DhvhYYy_.js → plug-Dxp-sWVF.js} +1 -1
  148. package/ui/dist/assets/proxy-vU7v4NVM.js +9 -0
  149. package/ui/dist/assets/square-Bn_0tYME.js +6 -0
  150. package/ui/dist/assets/target-BrtxUtzl.js +6 -0
  151. package/ui/dist/assets/toggle-right-CYphlpN5.js +11 -0
  152. package/ui/dist/assets/{trash-2-DmRaMz9e.js → trash-2-C_Jsp23A.js} +1 -1
  153. package/ui/dist/assets/{trending-up-DsDcs3Jo.js → trending-up-DrtLViSm.js} +1 -1
  154. package/ui/dist/assets/trophy-DdRzAOfo.js +6 -0
  155. package/ui/dist/index.html +2 -2
  156. package/ui/dist/assets/CPFiles-G7veSjMg.js +0 -6
  157. package/ui/dist/assets/CPGoals-C3DlKJrJ.js +0 -1
  158. package/ui/dist/assets/CPInbox-D10curQs.js +0 -16
  159. package/ui/dist/assets/ChannelsPanel-M3pO2htW.js +0 -1
  160. package/ui/dist/assets/CommandPostHub-CW9OY1A4.js +0 -37
  161. package/ui/dist/assets/InlineEditableField-CH-jR3LC.js +0 -11
  162. package/ui/dist/assets/IntegrationsPanel-EaN999Te.js +0 -1
  163. package/ui/dist/assets/IntelligenceView-Q4DBmJpJ.js +0 -2
  164. package/ui/dist/assets/McpPanel-zC7jTaSx.js +0 -6
  165. package/ui/dist/assets/MeshPanel-CqtYZ74K.js +0 -11
  166. package/ui/dist/assets/NvidiaPanel-BVIZFHet.js +0 -1
  167. package/ui/dist/assets/SelfImprovePanel-PSCYO6sx.js +0 -11
  168. package/ui/dist/assets/SessionsTab-Cn3dGgjX.js +0 -1
  169. package/ui/dist/assets/SettingsView-3BSIzAfW.js +0 -2
  170. package/ui/dist/assets/TitanCanvas-cnb7R1gS.js +0 -1056
  171. package/ui/dist/assets/ToolsView-Dp-xUWJG.js +0 -2
  172. package/ui/dist/assets/WorkTab-Pgq-iLz9.js +0 -1
  173. package/ui/dist/assets/WorkflowsPanel-B91LeW7r.js +0 -21
  174. package/ui/dist/assets/eye-BfW7UcEC.js +0 -11
  175. package/ui/dist/assets/index-BWSnB6Kr.js +0 -227
  176. package/ui/dist/assets/index-Dtw1pbjc.css +0 -1
@@ -12,6 +12,7 @@ import { updateIssue, startRun, endRun, addIssueComment, recordSpend } from "./c
12
12
  import { recordTokenUsage, routeModel } from "./costOptimizer.js";
13
13
  import { calculateActualCost } from "./costEstimator.js";
14
14
  import { getSessionGoal } from "./autonomyContext.js";
15
+ import { initBudget, checkBudget, recordUsage, markExceeded, cleanupBudget, getDefaultBudget } from "./promptBudget.js";
15
16
  import { scanForSecrets } from "../security/secretGuard.js";
16
17
  import { fullExfilScan } from "../security/exfilScan.js";
17
18
  import { runShellHooks } from "../hooks/shellHooks.js";
@@ -148,7 +149,24 @@ function detectToolUseIntent(userMessage) {
148
149
  // Requests to check real system state that REQUIRES a tool
149
150
  /\b(?:what is|what's|show me|get) (?:the )?(?:current|actual) (?:uptime|hostname|ip|path|directory|pwd|time|date|memory|disk)\b/,
150
151
  // Hunt Finding #17 (2026-04-14): added `[\s:]+` so "run: ls" matches too.
151
- /\brun[\s:]+['"`]?(?:echo|ls|pwd|uptime|whoami|date|uname|cat|grep|find|node|npm|git|which|ps|df|free)\b/
152
+ /\brun[\s:]+['"`]?(?:echo|ls|pwd|uptime|whoami|date|uname|cat|grep|find|node|npm|git|which|ps|df|free)\b/,
153
+ // Widget / gallery tool requests — canvas chat explicitly asks for gallery_search/gallery_get
154
+ /\b(?:call|use|run)\b.*?\b(?:gallery_search|gallery_get|gallery_list)\b/,
155
+ /\b(?:create|add|make|build|spawn)\b.*?\b(?:widget|panel|canvas|gallery)\b/,
156
+ /\b(?:widget|gallery)\b.*?\b(?:template|search|find|get|fetch)\b/,
157
+ // New system widget intents (v5.0.2 "forgotten features" surface)
158
+ /\b(?:backup|snapshot|archive)\b/,
159
+ /\b(?:training|train|specialist|model)\b/,
160
+ /\b(?:recipe|playbook|workflow|jarvis)\b/,
161
+ /\b(?:vram|gpu|memory|nvidia)\b/,
162
+ /\b(?:team|member|role|permission|rbac)\b/,
163
+ /\b(?:cron|schedule|job|timer)\b/,
164
+ /\b(?:checkpoint|restore|save state)\b/,
165
+ /\b(?:organism|drive|safety|alert|guardrail)\b/,
166
+ /\b(?:fleet|node|route|mesh)\b/,
167
+ /\b(?:captcha|browser|form fill|web automation)\b/,
168
+ /\b(?:paperclip|sidecar|helper)\b/,
169
+ /\b(?:test|flaky|failing|coverage|eval)\b/
152
170
  ];
153
171
  return intentPatterns.some((p) => p.test(msg));
154
172
  }
@@ -404,6 +422,10 @@ async function runAgentLoop(ctx) {
404
422
  let modelSwitchCount = 0;
405
423
  let selfHealExhausted = false;
406
424
  const failedModels = /* @__PURE__ */ new Set();
425
+ const budgetConfig = getDefaultBudget(ctx.config);
426
+ if (budgetConfig.maxTokens > 0) {
427
+ initBudget(ctx.sessionId, budgetConfig);
428
+ }
407
429
  let noToolsRetryCount = 0;
408
430
  const MAX_MODEL_SWITCHES = 2;
409
431
  let pivotCount = 0;
@@ -618,6 +640,13 @@ Try a COMPLETELY DIFFERENT strategy. Do NOT repeat the same tools or approach.`
618
640
  if (round === 0 && phase === "think" && !ctx.isAutonomous && !ctx.taskEnforcementActive && ctx.activeTools.length > 0 && detectToolUseIntent(ctx.message || "")) {
619
641
  logger.info(COMPONENT, "[ExplicitIntent] User explicitly requested tool use \u2014 forcing tool_choice=required for round 1");
620
642
  }
643
+ const budgetMsg = budgetConfig.maxTokens > 0 ? checkBudget(ctx.sessionId, budgetConfig) : null;
644
+ if (budgetMsg) {
645
+ markExceeded(ctx.sessionId);
646
+ result.content = budgetMsg;
647
+ phase = "done";
648
+ break;
649
+ }
621
650
  let response;
622
651
  const thinkStart = Date.now();
623
652
  const isChatRound0Think = round === 0 && phase === "think" && ctx.completionStrategy === "single-round";
@@ -678,6 +707,9 @@ Try a COMPLETELY DIFFERENT strategy. Do NOT repeat the same tools or approach.`
678
707
  result.promptTokens += promptTokens;
679
708
  result.completionTokens += completionTokens;
680
709
  const costCheck = recordTokenUsage(ctx.sessionId, activeModel, promptTokens, completionTokens);
710
+ if (budgetConfig.maxTokens > 0) {
711
+ recordUsage(ctx.sessionId, promptTokens, completionTokens);
712
+ }
681
713
  const callCost = calculateActualCost(activeModel, { prompt: promptTokens, completion: completionTokens });
682
714
  const sessionGoal = getSessionGoal(ctx.sessionId);
683
715
  recordSpend(ctx.agentId || "default", sessionGoal?.goalId, callCost);
@@ -909,7 +941,7 @@ Execute the next ACTION.` });
909
941
  continue;
910
942
  }
911
943
  }
912
- const finalText = stripToolJson(response.content || "");
944
+ let finalText = stripToolJson(response.content || "");
913
945
  const lastToolResult = result.toolCallDetails.length > 0 ? result.toolCallDetails[result.toolCallDetails.length - 1] : null;
914
946
  const userMsgLower = (ctx.message || "").toLowerCase();
915
947
  const wantsVerbatim = /\b(?:verbatim|exactly|precise|literal|as(?: |-)is|raw output|just the output)\b/.test(userMsgLower);
@@ -924,6 +956,29 @@ Execute the next ACTION.` });
924
956
  break;
925
957
  }
926
958
  }
959
+ const galleryGetCalls = result.toolCallDetails.filter((d) => d.name === "gallery_get");
960
+ logger.info(COMPONENT, `[AutoWidgetGate] Checking ${galleryGetCalls.length} gallery_get call(s) for system widgets`);
961
+ for (const call of galleryGetCalls) {
962
+ try {
963
+ const snippet = call.resultSnippet || "";
964
+ logger.info(COMPONENT, `[AutoWidgetGate] gallery_get snippet: ${snippet.slice(0, 200)}`);
965
+ const parsed = JSON.parse(snippet);
966
+ if (parsed.source && typeof parsed.source === "string" && parsed.source.startsWith("system:")) {
967
+ const gateText = `_____widget
968
+ { "name": "${parsed.name || "Widget"}", "format": "system", "source": "${parsed.source}", "w": ${parsed.defaultSize?.w || 6}, "h": ${parsed.defaultSize?.h || 6} }`;
969
+ if (!finalText.includes("_____widget")) {
970
+ finalText = finalText ? `${finalText}
971
+
972
+ ${gateText}` : gateText;
973
+ logger.info(COMPONENT, `[AutoWidgetGate] Injected _____widget gate for ${parsed.source}`);
974
+ }
975
+ } else {
976
+ logger.info(COMPONENT, `[AutoWidgetGate] Skipped \u2014 source is ${parsed.source}`);
977
+ }
978
+ } catch (e) {
979
+ logger.info(COMPONENT, `[AutoWidgetGate] Parse error: ${e.message}`);
980
+ }
981
+ }
927
982
  result.content = finalText;
928
983
  setCachedResponse(smartMessages, activeModel, result.content);
929
984
  phase = "done";
@@ -1287,8 +1342,18 @@ Examples: use read_file instead of cat/head/tail/grep, and edit_file instead of
1287
1342
  }
1288
1343
  }
1289
1344
  } else {
1290
- phase = "respond";
1291
- logger.info(COMPONENT, `[ThinkAct] Tools executed \u2014 entering respond phase (tools will be stripped)`);
1345
+ const discoveryTools = ["gallery_search", "tool_search"];
1346
+ const lastWasDiscovery = pendingToolCalls.length === 1 && discoveryTools.includes(pendingToolCalls[0].function.name);
1347
+ const resultHintsFollowUp = lastWasDiscovery && toolResults.some(
1348
+ (r) => /call gallery_get/i.test(r.content) || /hint:.*?(call|use|try|fetch|get|search)/i.test(r.content)
1349
+ );
1350
+ if (resultHintsFollowUp && round < ctx.effectiveMaxRounds - 1) {
1351
+ logger.info(COMPONENT, `[DiscoveryChain] ${pendingToolCalls[0].function.name} hinted follow-up \u2014 allowing another think round`);
1352
+ phase = "think";
1353
+ } else {
1354
+ phase = "respond";
1355
+ logger.info(COMPONENT, `[ThinkAct] Tools executed \u2014 entering respond phase (tools will be stripped)`);
1356
+ }
1292
1357
  }
1293
1358
  break;
1294
1359
  }
@@ -1311,6 +1376,13 @@ Examples: use read_file instead of cat/head/tail/grep, and edit_file instead of
1311
1376
  break;
1312
1377
  }
1313
1378
  logger.info(COMPONENT, `Respond phase \u2014 calling LLM without tools to generate final answer`);
1379
+ const respondBudgetMsg = budgetConfig.maxTokens > 0 ? checkBudget(ctx.sessionId, budgetConfig) : null;
1380
+ if (respondBudgetMsg) {
1381
+ markExceeded(ctx.sessionId);
1382
+ result.content = respondBudgetMsg;
1383
+ phase = "done";
1384
+ break;
1385
+ }
1314
1386
  let smartMessages;
1315
1387
  if (ctx.voiceFastPath) {
1316
1388
  smartMessages = ctx.messages;
@@ -1326,7 +1398,7 @@ Examples: use read_file instead of cat/head/tail/grep, and edit_file instead of
1326
1398
  }
1327
1399
  const respondDirective = {
1328
1400
  role: "user",
1329
- content: '[System directive for this reply only] Write the final answer for the user. RULES: (1) Do NOT narrate what the user asked \u2014 they already know. (2) Do NOT describe your reasoning, thinking, or past tool attempts. (3) Do NOT start with "The user asked", "Let me", "Actually", "Looking at", "Wait" \u2014 start with the result. (4) Report outcomes as facts in 1-3 sentences. (5) No XML, no tool call blocks, no meta-commentary. Just the answer.'
1401
+ content: '[System directive for this reply only] Write the final answer for the user. RULES: (1) Do NOT narrate what the user asked \u2014 they already know. (2) Do NOT describe your reasoning, thinking, or past tool attempts. (3) Do NOT start with "The user asked", "Let me", "Actually", "Looking at", "Wait" \u2014 start with the result. (4) Report outcomes as facts in 1-3 sentences. (5) No XML, no tool call blocks, no meta-commentary. Just the answer. (6) EXCEPTION: if you need to create or update a widget on the canvas, you MAY emit a _____react or _____widget gate \u2014 these are NOT meta-commentary, they are the actual deliverable.'
1330
1402
  };
1331
1403
  smartMessages = [...smartMessages, respondDirective];
1332
1404
  const thinkingMode = ctx.thinkingOverride || ctx.config.agent.thinkingMode || "off";
@@ -1383,6 +1455,9 @@ Examples: use read_file instead of cat/head/tail/grep, and edit_file instead of
1383
1455
  break;
1384
1456
  }
1385
1457
  const costCheck = recordTokenUsage(ctx.sessionId, activeModel, response.usage?.promptTokens || 0, response.usage?.completionTokens || 0);
1458
+ if (budgetConfig.maxTokens > 0) {
1459
+ recordUsage(ctx.sessionId, response.usage?.promptTokens || 0, response.usage?.completionTokens || 0);
1460
+ }
1386
1461
  const callCost = calculateActualCost(activeModel, { prompt: response.usage?.promptTokens || 0, completion: response.usage?.completionTokens || 0 });
1387
1462
  const sessionGoal = getSessionGoal(ctx.sessionId);
1388
1463
  recordSpend(ctx.agentId || "default", sessionGoal?.goalId, callCost);
@@ -1500,6 +1575,9 @@ Examples: use read_file instead of cat/head/tail/grep, and edit_file instead of
1500
1575
  budgetExhausted: result.budgetExhausted
1501
1576
  });
1502
1577
  result.traceContext = { traceId: traceCtx.traceId, spanId: traceCtx.spanId };
1578
+ if (budgetConfig.maxTokens > 0) {
1579
+ cleanupBudget(ctx.sessionId);
1580
+ }
1503
1581
  return result;
1504
1582
  }
1505
1583
  async function checkAndProcessInbox(agentId) {