turbollm 1.10.4 → 1.10.5

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 (121) hide show
  1. package/dist/cli.js +271 -107
  2. package/dist/webdist/assets/{AgentEditPage-DZU5Ps30.js → AgentEditPage-Bq0To2Kn.js} +1 -1
  3. package/dist/webdist/assets/{ChatScreen-CWjg1zrs.js → ChatScreen-JfS4dp94.js} +9 -9
  4. package/dist/webdist/assets/{CodeComposer-C8c0eQe4.js → CodeComposer-BPcKbMuH.js} +1 -1
  5. package/dist/webdist/assets/CodeHomeScreen-CbY_UCs6.js +1 -0
  6. package/dist/webdist/assets/{CodeSessionScreen-DOVBiJIZ.js → CodeSessionScreen-nU50H3fR.js} +11 -11
  7. package/dist/webdist/assets/{CustomizeScreen-Dg69A2mU.js → CustomizeScreen-jI99vrYL.js} +1 -1
  8. package/dist/webdist/assets/{DeveloperScreen-CwW1Z3BJ.js → DeveloperScreen-BfYnTO7e.js} +1 -1
  9. package/dist/webdist/assets/{EnginesScreen-CiMymt2y.js → EnginesScreen-CU8ZNHcl.js} +1 -1
  10. package/dist/webdist/assets/{ModelDetailDialog-Ch1FFdDi.js → ModelDetailDialog-DKUMTU2w.js} +1 -1
  11. package/dist/webdist/assets/{ModelLoadMenu-DmJ6pW_S.js → ModelLoadMenu-CBj32oEE.js} +1 -1
  12. package/dist/webdist/assets/{ModelsScreen-Ch83Gnvx.js → ModelsScreen-CTYIbZyc.js} +1 -1
  13. package/dist/webdist/assets/RoutineEditPage-gqLu8cXv.js +1 -0
  14. package/dist/webdist/assets/RoutinesPanel-Dv9Shubg.js +1 -0
  15. package/dist/webdist/assets/{SettingsScreen-fWaRXbXZ.js → SettingsScreen-C7LQLNGe.js} +1 -1
  16. package/dist/webdist/assets/SidebarResizeHandle-Djhjx6qR.js +2 -0
  17. package/dist/webdist/assets/{SkillEditPage-Cpg3YkPw.js → SkillEditPage-ClBJXbC3.js} +1 -1
  18. package/dist/webdist/assets/{TokensScreen-CYGdXUbH.js → TokensScreen-gk_Mqi5g.js} +1 -1
  19. package/dist/webdist/assets/{ToolApprovalBar-Ba2BoF9X.js → ToolApprovalBar-B3WSFcyU.js} +3 -3
  20. package/dist/webdist/assets/WorkspaceScreen-jL5YURRX.js +1 -0
  21. package/dist/webdist/assets/{abnfDiagram-N423BO3Z-CxAZuY4F.js → abnfDiagram-N423BO3Z-QSevWJ37.js} +1 -1
  22. package/dist/webdist/assets/{agent-api-BdnGAV-Y.js → agent-api-CpgzOfg0.js} +1 -1
  23. package/dist/webdist/assets/{alert-dialog-CEf-XUyg.js → alert-dialog-CMvNzcK0.js} +1 -1
  24. package/dist/webdist/assets/{arc-DEU8Rl3Q.js → arc-BWbVGHju.js} +1 -1
  25. package/dist/webdist/assets/{architectureDiagram-T3A2C74G-DFWEfaYa.js → architectureDiagram-T3A2C74G-Boh67Blv.js} +1 -1
  26. package/dist/webdist/assets/{arrow-left-BHr6DbXW.js → arrow-left-CU7vJBtU.js} +1 -1
  27. package/dist/webdist/assets/{badge-DiZD__AA.js → badge-BOGctZvx.js} +1 -1
  28. package/dist/webdist/assets/{blockDiagram-VBNYF7ZC-BhTU3G_O.js → blockDiagram-VBNYF7ZC-BkIBotfv.js} +1 -1
  29. package/dist/webdist/assets/{bot-BR1o3u2z.js → bot-CwYS2B1G.js} +1 -1
  30. package/dist/webdist/assets/{c4Diagram-5PPSVZJV-CJNhzTh5.js → c4Diagram-5PPSVZJV-CWCZQHIc.js} +1 -1
  31. package/dist/webdist/assets/channel-Yxxa5Jtv.js +1 -0
  32. package/dist/webdist/assets/{chat-api-WdKnt_cR.js → chat-api-BTcC6kED.js} +1 -1
  33. package/dist/webdist/assets/{chat-queries--BKqKvZT.js → chat-queries-D9fFhHaQ.js} +1 -1
  34. package/dist/webdist/assets/{chevron-left-tBRMVkX5.js → chevron-left-BVxkM758.js} +1 -1
  35. package/dist/webdist/assets/{chevron-right-B16ur-An.js → chevron-right-BJZRbTUj.js} +1 -1
  36. package/dist/webdist/assets/{chunk-2GRJ4B5K-Y-VLx_rW.js → chunk-2GRJ4B5K-C5CXDrO_.js} +1 -1
  37. package/dist/webdist/assets/{chunk-2Q5K7J3B-BUKWb833.js → chunk-2Q5K7J3B-Bk_wrlB0.js} +1 -1
  38. package/dist/webdist/assets/{chunk-5RXB4S5H-BV3L2yyf.js → chunk-5RXB4S5H-BAT4FnUI.js} +1 -1
  39. package/dist/webdist/assets/{chunk-5VM5RSS4-xI1dTTCA.js → chunk-5VM5RSS4-C9eEV1Kl.js} +1 -1
  40. package/dist/webdist/assets/{chunk-6Q2QTUOP-C3F611uv.js → chunk-6Q2QTUOP-CJDFqmYe.js} +1 -1
  41. package/dist/webdist/assets/{chunk-GF5L2VYU-xzyWIvtP.js → chunk-GF5L2VYU-CsgBXjfk.js} +1 -1
  42. package/dist/webdist/assets/{chunk-JWPE2WC7-PCn1DNAq.js → chunk-JWPE2WC7-CRt7uW9O.js} +1 -1
  43. package/dist/webdist/assets/{chunk-KBJHAD2P-B2V4Qn1_.js → chunk-KBJHAD2P-CSsVpcf6.js} +1 -1
  44. package/dist/webdist/assets/{chunk-RYQCIY6F-BXnb7awQ.js → chunk-RYQCIY6F-eTll7wC1.js} +1 -1
  45. package/dist/webdist/assets/{chunk-XXDRQBXY-BjzDsMy6.js → chunk-XXDRQBXY-D2-Dj7EK.js} +1 -1
  46. package/dist/webdist/assets/{circle-check-BHQ4Ky5u.js → circle-check-g29PWBwU.js} +1 -1
  47. package/dist/webdist/assets/{circle-x-BbJarEnu.js → circle-x-BgFrKCxR.js} +1 -1
  48. package/dist/webdist/assets/classDiagram-JCYQIIEL-Cr7eMrK7.js +1 -0
  49. package/dist/webdist/assets/classDiagram-v2-OCEON4UE-Cr7eMrK7.js +1 -0
  50. package/dist/webdist/assets/{common-B_Ugw5Ue.js → common-Fwu8t36v.js} +1 -1
  51. package/dist/webdist/assets/{cose-bilkent-JH36ORCC-8GVWCg2F.js → cose-bilkent-JH36ORCC-DXafCcEB.js} +1 -1
  52. package/dist/webdist/assets/{cynefin-VYW2F7L2-D4EwNxuN.js → cynefin-VYW2F7L2-CecX93d_.js} +1 -1
  53. package/dist/webdist/assets/{cynefinDiagram-MW4NZA55-BchWtjzK.js → cynefinDiagram-MW4NZA55-tu8xDmsR.js} +1 -1
  54. package/dist/webdist/assets/{dagre-VZM6K2ZE-C-tKBzor.js → dagre-VZM6K2ZE-UG2o94YJ.js} +1 -1
  55. package/dist/webdist/assets/{diagram-7IWD3JNH-CIzXwEou.js → diagram-7IWD3JNH-3DRTAZAF.js} +1 -1
  56. package/dist/webdist/assets/{diagram-B4RE2ZJO-B0-fu81p.js → diagram-B4RE2ZJO-CwH0bQ2A.js} +1 -1
  57. package/dist/webdist/assets/{diagram-LBJQPF4R-DKUCmHSn.js → diagram-LBJQPF4R-DuybxKFU.js} +1 -1
  58. package/dist/webdist/assets/{diagram-Q27KOJAE-BMVmaaBq.js → diagram-Q27KOJAE-BwoB84ui.js} +1 -1
  59. package/dist/webdist/assets/{diagram-UB23O5K3-C-GsMRNn.js → diagram-UB23O5K3-DEjbnHCQ.js} +1 -1
  60. package/dist/webdist/assets/{dropdown-menu-BHlT_PrH.js → dropdown-menu-D25-JVEy.js} +1 -1
  61. package/dist/webdist/assets/{ebnfDiagram-BXEA7PRR-D2cgRlFo.js → ebnfDiagram-BXEA7PRR-DG9Xbpyw.js} +1 -1
  62. package/dist/webdist/assets/{erDiagram-JOGREHBK-MG0Qt0Dc.js → erDiagram-JOGREHBK-eysaP8yv.js} +1 -1
  63. package/dist/webdist/assets/{external-link-BYsIzaRn.js → external-link-Xn8kJljc.js} +1 -1
  64. package/dist/webdist/assets/{file-text-D5LVQPdf.js → file-text-BmAkLphS.js} +1 -1
  65. package/dist/webdist/assets/{flowDiagram-UKHOOZJN-wAus-SqB.js → flowDiagram-UKHOOZJN-Bfha7bpG.js} +1 -1
  66. package/dist/webdist/assets/{folder-input-CPFYQQYm.js → folder-input-BRjaJam7.js} +1 -1
  67. package/dist/webdist/assets/{folder-plus-BS4MX3uc.js → folder-plus-DBJ9oQjK.js} +1 -1
  68. package/dist/webdist/assets/{ganttDiagram-PKOTCBZU-iFTGTcny.js → ganttDiagram-PKOTCBZU-CJotaSgm.js} +1 -1
  69. package/dist/webdist/assets/{git-branch-CH_JRbeH.js → git-branch-DerJ3eCK.js} +1 -1
  70. package/dist/webdist/assets/{gitGraphDiagram-DS77QQ5N-B3Lim-un.js → gitGraphDiagram-DS77QQ5N-C8za5q5H.js} +1 -1
  71. package/dist/webdist/assets/{index-m7O9h-cc.js → index-CRd__4R1.js} +6 -6
  72. package/dist/webdist/assets/{infoDiagram-6WML65LV-2U3_w76t.js → infoDiagram-6WML65LV-C95kKxPZ.js} +1 -1
  73. package/dist/webdist/assets/{ishikawaDiagram-WSZJBQD7-8qFZeY06.js → ishikawaDiagram-WSZJBQD7-DOIqcMyH.js} +1 -1
  74. package/dist/webdist/assets/{journeyDiagram-NVQOT4AX-BiQ4W5bp.js → journeyDiagram-NVQOT4AX-BBirkGE7.js} +1 -1
  75. package/dist/webdist/assets/{kanban-definition-27J2QSJJ-xUKbXLDF.js → kanban-definition-27J2QSJJ-CPhjxJyV.js} +1 -1
  76. package/dist/webdist/assets/{linear-Cfppb_KP.js → linear-BWhxZFqF.js} +1 -1
  77. package/dist/webdist/assets/{lock-_kPFqMMF.js → lock-CpPOIG8p.js} +1 -1
  78. package/dist/webdist/assets/{mermaid.core-Cqc4S48f.js → mermaid.core-DocKtbj4.js} +4 -4
  79. package/dist/webdist/assets/{mindmap-definition-FAOFIHXS-DfAXBfEp.js → mindmap-definition-FAOFIHXS-wL4xGOU_.js} +1 -1
  80. package/dist/webdist/assets/{pegDiagram-VL7TDLO6-BDnh7A9L.js → pegDiagram-VL7TDLO6-D19VvRVO.js} +1 -1
  81. package/dist/webdist/assets/{pencil-B65o2Xsj.js → pencil-c-PJkJes.js} +1 -1
  82. package/dist/webdist/assets/{pieDiagram-7S7Q4E2Y-BoEHVtTF.js → pieDiagram-7S7Q4E2Y-Doy7WZyw.js} +1 -1
  83. package/dist/webdist/assets/{plus-BCCJyhZC.js → plus-CP7d5krT.js} +1 -1
  84. package/dist/webdist/assets/{quadrantDiagram-CIZ2JOQS-BvKFv-w0.js → quadrantDiagram-CIZ2JOQS-BO535Mye.js} +1 -1
  85. package/dist/webdist/assets/{railroadDiagram-AXF67PYL-BLrX-HQS.js → railroadDiagram-AXF67PYL-C_F6C_-j.js} +1 -1
  86. package/dist/webdist/assets/{refresh-cw-BMe9yq9k.js → refresh-cw-CUFncOr4.js} +1 -1
  87. package/dist/webdist/assets/{requirementDiagram-LRYGKXZP-r4LjKdJa.js → requirementDiagram-LRYGKXZP-COStqNVI.js} +1 -1
  88. package/dist/webdist/assets/{rotate-ccw-QlaGmqOe.js → rotate-ccw-gR3mMTRM.js} +1 -1
  89. package/dist/webdist/assets/{sankeyDiagram-W5VNT64P-CbTbNO1R.js → sankeyDiagram-W5VNT64P-DrpcfTw1.js} +1 -1
  90. package/dist/webdist/assets/{sequenceDiagram-SI44F4Z6-C3cZYRGT.js → sequenceDiagram-SI44F4Z6-CQ1WUoj0.js} +1 -1
  91. package/dist/webdist/assets/{sizeCapture-X5ZJPWSS-CYYQ8wtn.js → sizeCapture-X5ZJPWSS-CExk2dPw.js} +1 -1
  92. package/dist/webdist/assets/{skeleton-CC9ODrk4.js → skeleton-CapS3LhZ.js} +1 -1
  93. package/dist/webdist/assets/{sliders-horizontal-BFSPlX5c.js → sliders-horizontal-VjLq-9i0.js} +1 -1
  94. package/dist/webdist/assets/{sparkles-CmEUYmzj.js → sparkles-BS98k72s.js} +1 -1
  95. package/dist/webdist/assets/{star-CyudL0Do.js → star-Ce5Pu8jP.js} +1 -1
  96. package/dist/webdist/assets/{stateDiagram-OKZ733FA-Bdj39Nof.js → stateDiagram-OKZ733FA-6IQ9pQNr.js} +1 -1
  97. package/dist/webdist/assets/stateDiagram-v2-UEYNNEHI-D-qEGyyy.js +1 -0
  98. package/dist/webdist/assets/{swimlanes-SLNWSIFB-W3xWeGOJ.js → swimlanes-SLNWSIFB-0AoFXo4W.js} +2 -2
  99. package/dist/webdist/assets/swimlanesDiagram-ULZ7WXOC-DJV5Gciz.js +8 -0
  100. package/dist/webdist/assets/{switch-BDarVW8W.js → switch-BVPOy4cS.js} +1 -1
  101. package/dist/webdist/assets/{terminal-Bvnu3OHJ.js → terminal-WfRPZiaW.js} +1 -1
  102. package/dist/webdist/assets/{timeline-definition-Z64GVDOM-DnavzBwI.js → timeline-definition-Z64GVDOM-LPqGGX5H.js} +1 -1
  103. package/dist/webdist/assets/{trash-2-C43ex79G.js → trash-2-DZYSgl2R.js} +1 -1
  104. package/dist/webdist/assets/{useIsDesktop-BC85xPnA.js → useIsDesktop-CjFyl6hu.js} +1 -1
  105. package/dist/webdist/assets/{vennDiagram-T6HMQDX7-DdJs_uLj.js → vennDiagram-T6HMQDX7--BcdXECz.js} +1 -1
  106. package/dist/webdist/assets/{vram-BWaHTGD0.js → vram-DlcIXX82.js} +1 -1
  107. package/dist/webdist/assets/{wardleyDiagram-T6FBY63Y-91gPbjKx.js → wardleyDiagram-T6FBY63Y-Cdki47W1.js} +1 -1
  108. package/dist/webdist/assets/{wrench-Dlm2fJiY.js → wrench-DOaBHWWN.js} +1 -1
  109. package/dist/webdist/assets/{xychartDiagram-ELKLHX3M-DDKDUQ8u.js → xychartDiagram-ELKLHX3M-3hkxEbMo.js} +1 -1
  110. package/dist/webdist/index.html +1 -1
  111. package/package.json +1 -1
  112. package/dist/webdist/assets/CodeHomeScreen-BbrTdGuL.js +0 -1
  113. package/dist/webdist/assets/RoutineEditPage-DqDEDN7O.js +0 -1
  114. package/dist/webdist/assets/RoutinesPanel-C0CgIiju.js +0 -1
  115. package/dist/webdist/assets/SidebarResizeHandle-eJlzaNXV.js +0 -2
  116. package/dist/webdist/assets/WorkspaceScreen-CLXpSocV.js +0 -1
  117. package/dist/webdist/assets/channel-D7Or72nc.js +0 -1
  118. package/dist/webdist/assets/classDiagram-JCYQIIEL-CoD8BgVw.js +0 -1
  119. package/dist/webdist/assets/classDiagram-v2-OCEON4UE-CoD8BgVw.js +0 -1
  120. package/dist/webdist/assets/stateDiagram-v2-UEYNNEHI-B3xKgUl_.js +0 -1
  121. package/dist/webdist/assets/swimlanesDiagram-ULZ7WXOC-DSpzEq_Q.js +0 -8
package/dist/cli.js CHANGED
@@ -5753,6 +5753,12 @@ var SUPPORTED = {
5753
5753
  hermes: { bin: "hermes", label: "Hermes Agent", install: "npm install -g hermes-agent", prepareConfig: prepareHermes },
5754
5754
  pi: { bin: "pi", label: "pi", install: "npm install -g @earendil-works/pi-coding-agent", prepareConfig: preparePi }
5755
5755
  };
5756
+ var CLAUDE_AUTO_COMPACT_WINDOW_MIN = 1e5;
5757
+ var CLAUDE_AUTO_COMPACT_WINDOW_MAX = 1e6;
5758
+ function clampAutoCompactWindow(ctx) {
5759
+ return Math.min(CLAUDE_AUTO_COMPACT_WINDOW_MAX, Math.max(CLAUDE_AUTO_COMPACT_WINDOW_MIN, Math.round(ctx)));
5760
+ }
5761
+ var CLAUDE_AUTOCOMPACT_PCT_OVERRIDE = "80";
5756
5762
  var realSpawn = (cmd, args, opts) => {
5757
5763
  if (!opts?.shell) return spawn4(cmd, args, opts);
5758
5764
  const resolved = resolveExecutable(cmd);
@@ -6180,6 +6186,15 @@ Open ${base2} \u2192 Models \u2192 Load a model, then run this again.
6180
6186
  // Opt into gateway model discovery: Claude Code queries our /v1/models at startup and
6181
6187
  // populates the /model picker with the local library (gateway synthesises the entries).
6182
6188
  CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: "1",
6189
+ // Founder-reported: the ctx-fill display "jumps between full and empty" and the CLI "never
6190
+ // auto-compacts at 80%, always reaches 100% and fails". The second half is Claude Code
6191
+ // assuming a generic ~200K context window for our unrecognized model id and only compacting
6192
+ // near THAT — see clampAutoCompactWindow's own doc comment for the full root cause and its
6193
+ // documented [100_000, 1_000_000] floor/ceiling. Only set when the daemon actually reports a
6194
+ // real ctx (status.model.ctx) — an absent/zero value means "don't know", and guessing a
6195
+ // window here would be worse than leaving the CLI's own generic default in place.
6196
+ ...status.model?.ctx ? { CLAUDE_CODE_AUTO_COMPACT_WINDOW: String(clampAutoCompactWindow(status.model.ctx)) } : {},
6197
+ CLAUDE_AUTOCOMPACT_PCT_OVERRIDE,
6183
6198
  // Cap background-agent fan-out at what the engine can actually run concurrently.
6184
6199
  //
6185
6200
  // Claude Code spawns subagents in parallel, and each is a full, independent request to the
@@ -7140,13 +7155,16 @@ var AUTO_MODE_ALLOWED_TOOLS = {
7140
7155
  claude: ["WebSearch", "WebFetch"]
7141
7156
  };
7142
7157
  var MAX_SEEDED_MESSAGE_CHARS = 4e3;
7143
- var UNSEEDABLE = /["\u0000-\u001f]/;
7158
+ var UNSEEDABLE = /["\u0000-\u0008\u000b\u000c\u000e-\u001f]/;
7159
+ function normalizeSeededMessage(message) {
7160
+ return message.replace(/[\r\n\t]+/g, " ").trim();
7161
+ }
7144
7162
  function canSeedFirstMessage(message, agent) {
7145
7163
  if (agent !== "claude") return false;
7146
- const trimmed = message.trim();
7147
- if (trimmed.length === 0 || trimmed.length > MAX_SEEDED_MESSAGE_CHARS) return false;
7148
- if (UNSEEDABLE.test(trimmed)) return false;
7149
- if (trimmed.endsWith("\\")) return false;
7164
+ const normalized = normalizeSeededMessage(message);
7165
+ if (normalized.length === 0 || normalized.length > MAX_SEEDED_MESSAGE_CHARS) return false;
7166
+ if (UNSEEDABLE.test(normalized)) return false;
7167
+ if (normalized.endsWith("\\")) return false;
7150
7168
  return true;
7151
7169
  }
7152
7170
  function buildTerminalLaunchCommand(agent, port2, token, sessionId, launchedOnce, permissionMode, mode, firstMessage) {
@@ -7155,7 +7173,7 @@ function buildTerminalLaunchCommand(agent, port2, token, sessionId, launchedOnce
7155
7173
  const modeArg = permissionMode ? ` --permission-mode ${permissionMode}` : "";
7156
7174
  const allowed = mode === "auto" ? AUTO_MODE_ALLOWED_TOOLS[agent] : void 0;
7157
7175
  const allowedArg = allowed?.length ? ` --allowedTools ${allowed.join(",")}` : "";
7158
- const promptArg = firstMessage && canSeedFirstMessage(firstMessage, agent) ? ` ${quotePtyShellArg(firstMessage.trim())}` : "";
7176
+ const promptArg = firstMessage && canSeedFirstMessage(firstMessage, agent) ? ` ${quotePtyShellArg(normalizeSeededMessage(firstMessage))}` : "";
7159
7177
  return `turbollm launch ${agent}${promptArg} --port ${port2} --token ${token}${sessionArg}${modeArg}${allowedArg}`;
7160
7178
  }
7161
7179
  function err(c, s, code, msg2) {
@@ -13488,6 +13506,10 @@ var ConversationStore = class {
13488
13506
  if (!this.hasColumn("api_usage", "harness")) this.db.exec(`ALTER TABLE api_usage ADD COLUMN harness TEXT;`);
13489
13507
  this.db.exec(`PRAGMA user_version = 43;`);
13490
13508
  }
13509
+ if (v < 44) {
13510
+ this.db.exec(`CREATE INDEX IF NOT EXISTS idx_api_usage_code_session_tokens ON api_usage(code_session_id, prompt_tokens);`);
13511
+ this.db.exec(`PRAGMA user_version = 44;`);
13512
+ }
13491
13513
  }
13492
13514
  listConversations(q, kind = "all") {
13493
13515
  if (q) {
@@ -13874,10 +13896,37 @@ var ConversationStore = class {
13874
13896
  $harness: rec.harness ?? null
13875
13897
  });
13876
13898
  }
13877
- /** Most recent completed gateway request attributed to a terminal-agent Code session
13878
- * (ADR-284) — powers TerminalToolbar.tsx's composer-parity stats row the same way
13879
- * `lastRealStats` already does for a 'turbollm' chat session's last turn. null when the
13880
- * session has made no gateway requests yet (fresh session, or a non-terminal-agent one). */
13899
+ /** The session's own gateway-request stats (ADR-284, revised twice — founder-reported "ctx
13900
+ * fill jumps between full and empty in the same session", then a review pass on that same fix
13901
+ * catching that it had gone on to freeze the LAST-TURN stats too). Two different questions,
13902
+ * two different rows:
13903
+ *
13904
+ * `ctxUsed` is the MAX prompt_tokens row, not literally the most-recently-INSERTED one. Every
13905
+ * request a real `claude` CLI process makes — the main conversation's own turn AND every
13906
+ * parallel Task-tool sub-agent it spawns — shares the SAME code_session_id: `resolveCodeSession`
13907
+ * (gateway.ts) derives it purely from the bearer token, and the CLI mints exactly one token
13908
+ * (ANTHROPIC_AUTH_TOKEN, set once at launch) for its whole process, sub-agents included. A
13909
+ * sub-agent's own prompt is a small, isolated sub-task — nothing like the full resent
13910
+ * conversation — so whichever of the two finishes last used to decide what the ring showed:
13911
+ * the real, large, ever-growing main-turn prompt one poll, a tiny sub-agent prompt the next,
13912
+ * with no change to the actual conversation in between. The MAX is the honest answer to "how
13913
+ * full has this session's context gotten" — a real Claude Code CLI resends the whole
13914
+ * conversation every turn, so its size only grows within one continuous conversation, and a
13915
+ * sub-agent (always smaller) can never legitimately raise that high-water mark. Trade-off,
13916
+ * accepted: after a real `/clear` inside the CLI (invisible to the daemon — a terminal-agent
13917
+ * session's turns are never persisted here, see ADR-297), this keeps reporting the pre-clear
13918
+ * high-water mark until the new conversation grows past it. A temporarily-stale-high ring
13919
+ * reading after a rare, deliberate user action beats an unpredictable full/empty flicker on
13920
+ * every ordinary agentic turn with sub-agents.
13921
+ *
13922
+ * `promptTokens`/`genTokens`/`promptTps`/`genTps` are the temporally-LAST row instead — these
13923
+ * power TerminalToolbar.tsx's composer-parity stats row, which reads as "your last turn", not
13924
+ * "session high-water mark". Sharing the MAX row here (the original version of this fix) meant
13925
+ * a sub-agent call landing after the real turn hid its own genTokens/tps behind the earlier,
13926
+ * larger turn's numbers, and after a `/clear` these froze at the pre-clear turn's numbers
13927
+ * forever, same failure shape as the ctxUsed bug this method was written to fix in the first
13928
+ * place. null when the session has made no gateway requests yet (fresh session, or a
13929
+ * non-terminal-agent one). */
13881
13930
  getLastApiUsageForSession(codeSessionId) {
13882
13931
  const row = this.db.prepare(`
13883
13932
  SELECT prompt_tokens, gen_tokens, duration_ms, prompt_tps, gen_tps FROM api_usage
@@ -13885,8 +13934,12 @@ var ConversationStore = class {
13885
13934
  ORDER BY created_at DESC LIMIT 1
13886
13935
  `).get({ $codeSessionId: codeSessionId });
13887
13936
  if (!row) return null;
13937
+ const ctxUsed = this.db.prepare(`
13938
+ SELECT MAX(prompt_tokens) as maxPromptTokens FROM api_usage WHERE code_session_id = $codeSessionId
13939
+ `).get({ $codeSessionId: codeSessionId }).maxPromptTokens;
13888
13940
  const seconds = row.duration_ms != null && row.duration_ms > 0 ? row.duration_ms / 1e3 : null;
13889
13941
  return {
13942
+ ctxUsed,
13890
13943
  promptTokens: row.prompt_tokens,
13891
13944
  genTokens: row.gen_tokens,
13892
13945
  promptTps: row.prompt_tps ?? (seconds ? row.prompt_tokens / seconds : null),
@@ -24252,7 +24305,59 @@ function mapFromOpenAI(oai, modelName) {
24252
24305
  usage: { input_tokens: usage?.prompt_tokens ?? 0, output_tokens: usage?.completion_tokens ?? 0 }
24253
24306
  };
24254
24307
  }
24255
- async function* streamToAnthropic(oaiStream, modelName, msgId, onUsage, onLive, onToolCalls) {
24308
+ var DEFAULT_PING_INTERVAL_MS = 1e4;
24309
+ function waitOrPing(pending2, ms) {
24310
+ return new Promise((resolve5) => {
24311
+ const timer = setTimeout(() => resolve5("ping"), ms);
24312
+ pending2.then(
24313
+ () => {
24314
+ clearTimeout(timer);
24315
+ resolve5("data");
24316
+ },
24317
+ () => {
24318
+ clearTimeout(timer);
24319
+ resolve5("data");
24320
+ }
24321
+ );
24322
+ });
24323
+ }
24324
+ async function pingWhilePending(pending2, emitPing, pingIntervalMs = DEFAULT_PING_INTERVAL_MS, onOrphan) {
24325
+ pending2.catch(() => {
24326
+ });
24327
+ let nextPingAt = Date.now() + pingIntervalMs;
24328
+ while (true) {
24329
+ const remaining = Math.max(0, nextPingAt - Date.now());
24330
+ const outcome = remaining === 0 ? "ping" : await waitOrPing(pending2, remaining);
24331
+ if (outcome === "ping") {
24332
+ try {
24333
+ await emitPing();
24334
+ } catch (e) {
24335
+ if (onOrphan) pending2.then(onOrphan).catch(() => {
24336
+ });
24337
+ throw e;
24338
+ }
24339
+ nextPingAt = Date.now() + pingIntervalMs;
24340
+ continue;
24341
+ }
24342
+ return pending2;
24343
+ }
24344
+ }
24345
+ function messageStartEvent(msgId, modelName) {
24346
+ return sse("message_start", {
24347
+ message: {
24348
+ id: msgId,
24349
+ type: "message",
24350
+ role: "assistant",
24351
+ content: [],
24352
+ model: modelName,
24353
+ stop_reason: null,
24354
+ stop_sequence: null,
24355
+ usage: { input_tokens: 0, output_tokens: 1 }
24356
+ }
24357
+ });
24358
+ }
24359
+ async function* streamToAnthropic(oaiStream, modelName, msgId, opts = {}) {
24360
+ const { onUsage, onLive, onToolCalls, pingIntervalMs = DEFAULT_PING_INTERVAL_MS, skipMessageStart = false } = opts;
24256
24361
  let blockIdx = 0;
24257
24362
  let inThinking = false;
24258
24363
  let inText = false;
@@ -24265,25 +24370,30 @@ async function* streamToAnthropic(oaiStream, modelName, msgId, onUsage, onLive,
24265
24370
  let promptTps = 0;
24266
24371
  let genTps = 0;
24267
24372
  let liveOut = 0;
24268
- yield sse("message_start", {
24269
- message: {
24270
- id: msgId,
24271
- type: "message",
24272
- role: "assistant",
24273
- content: [],
24274
- model: modelName,
24275
- stop_reason: null,
24276
- stop_sequence: null,
24277
- usage: { input_tokens: 0, output_tokens: 1 }
24278
- }
24279
- });
24373
+ if (!skipMessageStart) yield messageStartEvent(msgId, modelName);
24280
24374
  const reader = oaiStream.getReader();
24281
24375
  const dec = new TextDecoder();
24282
24376
  let buf = "";
24283
24377
  let failed = false;
24378
+ let nextPingAt = Date.now() + pingIntervalMs;
24284
24379
  try {
24285
24380
  outer: while (true) {
24286
- const { done, value } = await reader.read();
24381
+ const readPromise = reader.read();
24382
+ readPromise.catch(() => {
24383
+ });
24384
+ let result;
24385
+ while (true) {
24386
+ const remaining = Math.max(0, nextPingAt - Date.now());
24387
+ const outcome = remaining === 0 ? "ping" : await waitOrPing(readPromise, remaining);
24388
+ if (outcome === "ping") {
24389
+ yield sse("ping", {});
24390
+ nextPingAt = Date.now() + pingIntervalMs;
24391
+ continue;
24392
+ }
24393
+ result = await readPromise;
24394
+ break;
24395
+ }
24396
+ const { done, value } = result;
24287
24397
  if (done) break;
24288
24398
  buf += dec.decode(value, { stream: true });
24289
24399
  const lines = buf.split("\n");
@@ -24828,7 +24938,23 @@ function anthropicErrorType(status) {
24828
24938
  function asClientStatus(status) {
24829
24939
  return status >= 400 && status <= 599 ? status : 500;
24830
24940
  }
24831
- function registerGateway(app2, d) {
24941
+ function classifyGateError(e) {
24942
+ const aborted = e.message === "gate_acquire_aborted";
24943
+ return aborted ? { status: 400, type: "invalid_request_error", message: "Client disconnected while queued for the engine." } : { status: 503, type: "overloaded_error", message: "Timed out waiting for a free engine slot." };
24944
+ }
24945
+ function classifyFetchError(e, ac) {
24946
+ const err8 = e;
24947
+ const isAbort = err8.name === "AbortError" || ac.signal.aborted;
24948
+ const cause = err8.cause instanceof Error ? `: ${err8.cause.message}` : "";
24949
+ return {
24950
+ status: 500,
24951
+ type: "api_error",
24952
+ message: isAbort ? "Client disconnected before the engine responded." : `${err8.message || "Engine unreachable."}${cause}`
24953
+ };
24954
+ }
24955
+ function registerGateway(app2, d, opts = {}) {
24956
+ const pingIntervalMs = opts.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS;
24957
+ const gateAcquireTimeoutMs = opts.gateAcquireTimeoutMs ?? 6e5;
24832
24958
  app2.post("/v1/messages", async (c) => {
24833
24959
  let req;
24834
24960
  try {
@@ -24888,23 +25014,131 @@ function registerGateway(app2, d) {
24888
25014
  if (guidance?.forceTextOnly) oaiBody.tool_choice = "none";
24889
25015
  const oaiAlias = engineModelAlias(d.registry.active()?.kind ?? "");
24890
25016
  if (oaiAlias) oaiBody.model = oaiAlias;
25017
+ const ac = clientAbort(c);
25018
+ if (req.stream) {
25019
+ const msgId = `msg_${randomUUID13().replace(/-/g, "")}`;
25020
+ return streamSSE4(c, async (stream) => {
25021
+ let gateRelease2 = null;
25022
+ let generationStarted = false;
25023
+ stream.onAbort(() => ac.abort());
25024
+ const ping = () => stream.writeSSE({ event: "ping", data: JSON.stringify({ type: "ping" }) });
25025
+ try {
25026
+ try {
25027
+ await stream.writeSSE(messageStartEvent(msgId, modelName));
25028
+ if (d.gate) {
25029
+ try {
25030
+ gateRelease2 = await pingWhilePending(
25031
+ d.gate.acquire("bg", { signal: c.req.raw.signal, timeoutMs: gateAcquireTimeoutMs }),
25032
+ ping,
25033
+ pingIntervalMs,
25034
+ (release) => release()
25035
+ );
25036
+ } catch (e) {
25037
+ const { type, message } = classifyGateError(e);
25038
+ await stream.writeSSE({ event: "error", data: JSON.stringify({ type: "error", error: { type, message } }) });
25039
+ return;
25040
+ }
25041
+ }
25042
+ d.manager.generationStart();
25043
+ generationStarted = true;
25044
+ const requestStart2 = Date.now();
25045
+ let res2;
25046
+ try {
25047
+ res2 = await pingWhilePending(
25048
+ fetch(`${target}/v1/chat/completions`, {
25049
+ method: "POST",
25050
+ headers: { "Content-Type": "application/json" },
25051
+ body: JSON.stringify(oaiBody),
25052
+ signal: ac.signal
25053
+ }),
25054
+ ping,
25055
+ pingIntervalMs
25056
+ // No onOrphan: an orphaned Response has nothing to release — its body is simply
25057
+ // never read and gets garbage-collected, unlike gate.acquire()'s grant above.
25058
+ );
25059
+ } catch (e) {
25060
+ const { type, message } = classifyFetchError(e, ac);
25061
+ await stream.writeSSE({ event: "error", data: JSON.stringify({ type: "error", error: { type, message } }) });
25062
+ return;
25063
+ }
25064
+ if (!res2.ok || !res2.body) {
25065
+ const { message, type } = await describeEngineError(res2);
25066
+ await stream.writeSSE({
25067
+ event: "error",
25068
+ data: JSON.stringify({ type: "error", error: { type: type ?? anthropicErrorType(res2.status), message } })
25069
+ });
25070
+ return;
25071
+ }
25072
+ const gen = streamToAnthropic(res2.body, modelName, msgId, {
25073
+ onUsage: (u) => {
25074
+ try {
25075
+ d.manager.recordCompletion({
25076
+ inputTokens: u.inputTokens,
25077
+ outputTokens: u.outputTokens,
25078
+ promptTps: u.promptTps,
25079
+ genTps: u.genTps
25080
+ });
25081
+ d.db.recordApiUsage({
25082
+ source: "anthropic",
25083
+ modelKey: req.model ?? null,
25084
+ promptTokens: u.inputTokens,
25085
+ genTokens: u.outputTokens,
25086
+ codeSessionId: anthropicCodeSessionId,
25087
+ durationMs: Date.now() - requestStart2,
25088
+ promptTps: u.promptTps,
25089
+ genTps: u.genTps,
25090
+ harness: anthropicHarness
25091
+ });
25092
+ } catch {
25093
+ }
25094
+ },
25095
+ // Live per-request progress for the engine card (prefill % + token count),
25096
+ // so Claude Code traffic shows the same live row as in-app chat.
25097
+ onLive: (live) => {
25098
+ try {
25099
+ d.manager.setLiveGen(live);
25100
+ } catch {
25101
+ }
25102
+ },
25103
+ // Coding-activity attribution for terminal-agent sessions — see
25104
+ // observeCodeSessionTurn. Nothing to attribute for any other client.
25105
+ onToolCalls: (calls) => {
25106
+ if (!anthropicCodeSessionId) return;
25107
+ try {
25108
+ observeCodeSessionTurn(d, anthropicCodeSessionId, calls);
25109
+ } catch {
25110
+ }
25111
+ },
25112
+ skipMessageStart: true
25113
+ // already sent above, before the queue wait
25114
+ });
25115
+ for await (const evt of gen) {
25116
+ await stream.writeSSE({ event: evt.event, data: evt.data });
25117
+ }
25118
+ } finally {
25119
+ ac.abort();
25120
+ if (generationStarted) d.manager.generationEnd();
25121
+ gateRelease2?.();
25122
+ }
25123
+ } catch (e) {
25124
+ await stream.writeSSE({
25125
+ event: "error",
25126
+ data: JSON.stringify({ type: "error", error: { type: "api_error", message: e?.message || "Internal gateway error." } })
25127
+ }).catch(() => {
25128
+ });
25129
+ }
25130
+ });
25131
+ }
24891
25132
  let gateRelease = null;
24892
25133
  if (d.gate) {
24893
25134
  try {
24894
- gateRelease = await d.gate.acquire("bg", { signal: c.req.raw.signal, timeoutMs: 6e5 });
25135
+ gateRelease = await d.gate.acquire("bg", { signal: c.req.raw.signal, timeoutMs: gateAcquireTimeoutMs });
24895
25136
  } catch (e) {
24896
- const aborted = e.message === "gate_acquire_aborted";
24897
- return c.json(
24898
- {
24899
- type: "error",
24900
- error: aborted ? { type: "invalid_request_error", message: "Client disconnected while queued for the engine." } : { type: "overloaded_error", message: "Timed out waiting for a free engine slot." }
24901
- },
24902
- aborted ? 400 : 503
24903
- );
25137
+ const { status: status2, type, message } = classifyGateError(e);
25138
+ return c.json({ type: "error", error: { type, message } }, status2);
24904
25139
  }
24905
25140
  }
24906
25141
  d.manager.generationStart();
24907
- const ac = clientAbort(c);
24908
25142
  const requestStart = Date.now();
24909
25143
  let res;
24910
25144
  try {
@@ -24917,19 +25151,8 @@ function registerGateway(app2, d) {
24917
25151
  } catch (e) {
24918
25152
  d.manager.generationEnd();
24919
25153
  gateRelease?.();
24920
- const err8 = e;
24921
- const isAbort = err8.name === "AbortError" || ac.signal.aborted;
24922
- const cause = err8.cause instanceof Error ? `: ${err8.cause.message}` : "";
24923
- return c.json(
24924
- {
24925
- type: "error",
24926
- error: {
24927
- type: "api_error",
24928
- message: isAbort ? "Client disconnected before the engine responded." : `${err8.message || "Engine unreachable."}${cause}`
24929
- }
24930
- },
24931
- 500
24932
- );
25154
+ const { status: status2, type, message } = classifyFetchError(e, ac);
25155
+ return c.json({ type: "error", error: { type, message } }, status2);
24933
25156
  }
24934
25157
  if (!res.ok || !res.body) {
24935
25158
  d.manager.generationEnd();
@@ -24940,65 +25163,6 @@ function registerGateway(app2, d) {
24940
25163
  asClientStatus(res.status)
24941
25164
  );
24942
25165
  }
24943
- if (req.stream) {
24944
- const msgId = `msg_${randomUUID13().replace(/-/g, "")}`;
24945
- const gen = streamToAnthropic(
24946
- res.body,
24947
- modelName,
24948
- msgId,
24949
- (u) => {
24950
- try {
24951
- d.manager.recordCompletion({
24952
- inputTokens: u.inputTokens,
24953
- outputTokens: u.outputTokens,
24954
- promptTps: u.promptTps,
24955
- genTps: u.genTps
24956
- });
24957
- d.db.recordApiUsage({
24958
- source: "anthropic",
24959
- modelKey: req.model ?? null,
24960
- promptTokens: u.inputTokens,
24961
- genTokens: u.outputTokens,
24962
- codeSessionId: anthropicCodeSessionId,
24963
- durationMs: Date.now() - requestStart,
24964
- promptTps: u.promptTps,
24965
- genTps: u.genTps,
24966
- harness: anthropicHarness
24967
- });
24968
- } catch {
24969
- }
24970
- },
24971
- // Live per-request progress for the engine card (prefill % + token count),
24972
- // so Claude Code traffic shows the same live row as in-app chat.
24973
- (live) => {
24974
- try {
24975
- d.manager.setLiveGen(live);
24976
- } catch {
24977
- }
24978
- },
24979
- // Coding-activity attribution for terminal-agent sessions — see
24980
- // observeCodeSessionTurn. Nothing to attribute for any other client.
24981
- (calls) => {
24982
- if (!anthropicCodeSessionId) return;
24983
- try {
24984
- observeCodeSessionTurn(d, anthropicCodeSessionId, calls);
24985
- } catch {
24986
- }
24987
- }
24988
- );
24989
- return streamSSE4(c, async (stream) => {
24990
- stream.onAbort(() => ac.abort());
24991
- try {
24992
- for await (const evt of gen) {
24993
- await stream.writeSSE({ event: evt.event, data: evt.data });
24994
- }
24995
- } finally {
24996
- ac.abort();
24997
- d.manager.generationEnd();
24998
- gateRelease?.();
24999
- }
25000
- });
25001
- }
25002
25166
  try {
25003
25167
  const oaiRes = await res.json();
25004
25168
  recordOpenAiUsage(d, oaiRes, "anthropic", req.model ?? null, anthropicCodeSessionId, Date.now() - requestStart, anthropicHarness);
@@ -1 +1 @@
1
- import{j as e}from"./radix-B0YBjihE.js";import{b as W,u as $,r as b}from"./vendor-C1I6euDx.js";import{u as G,d as H,e as Y,f as J,g as U,a as z,B as C,t as j,c as q,b as h,A as _}from"./index-m7O9h-cc.js";import{f as V}from"./chat-api-WdKnt_cR.js";import{f as X,a as Z}from"./agent-api-BdnGAV-Y.js";import{P as B}from"./personas-B0p0BX69.js";import{C as ee}from"./chevron-left-tBRMVkX5.js";import{R as te}from"./rotate-ccw-QlaGmqOe.js";import{T as se}from"./trash-2-C43ex79G.js";import{C as ne}from"./chevron-right-B16ur-An.js";const ae=()=>({name:"",description:"",systemPrompt:"",skillIds:[],tools:[]});function oe(s){const d=[],x=new Map;for(const n of s){const a=/^mcp__([^_]+(?:_[^_]+)*)__/.exec(n.name);if(!a){d.push(n);continue}const i=a[1],p=/^\[([^\]]+)\]/.exec(n.description),c=p?p[1]:i.replace(/_/g," ");x.has(i)||x.set(i,{key:i,label:c,tools:[]}),x.get(i).tools.push(n)}return{builtin:d,groups:[...x.values()].sort((n,a)=>n.label.localeCompare(a.label))}}function le(s){return s.replace(/^mcp__[^_]+(?:_[^_]+)*__/,"")}function re(s){return s.replace(/^\[[^\]]+\]\s*/,"")}function D({tool:s,checked:d,onToggle:x}){return e.jsxs("label",{className:"flex cursor-pointer items-start gap-2 text-[12px]",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:d,onChange:x}),e.jsxs("span",{className:"flex flex-col",children:[e.jsx("span",{className:"font-mono text-ink",children:le(s.name)}),s.description&&e.jsx("span",{className:"text-faint",children:re(s.description)})]})]})}function ie({group:s,selected:d,onToggleAll:x,onToggleOne:n}){const[a,i]=b.useState(!1),p=s.tools.map(l=>l.name),c=p.filter(l=>d.has(l)).length,o=c===p.length,g=c>0&&!o,u=b.useRef(null);return b.useEffect(()=>{u.current&&(u.current.indeterminate=g)},[g]),e.jsxs("div",{className:"rounded-md border border-border",children:[e.jsxs("div",{className:"flex items-center gap-2 px-2.5 py-2",children:[e.jsx("input",{ref:u,type:"checkbox",checked:o,onChange:()=>x(p,!o)}),e.jsxs("button",{type:"button",onClick:()=>{j("agents","toggle_agent_tool_group"),i(l=>!l)},className:"flex flex-1 items-center gap-1.5 text-left text-[13px] text-ink",children:[e.jsx(ne,{size:12,className:q("shrink-0 text-faint transition-transform",a&&"rotate-90")}),s.label,e.jsxs("span",{className:"text-[11px] text-faint",children:["(",c,"/",p.length,")"]})]})]}),a&&e.jsx("div",{className:"flex flex-col gap-1.5 border-t border-border px-3 py-2 pl-9",children:s.tools.map(l=>e.jsx(D,{tool:l,checked:d.has(l.name),onToggle:()=>n(l.name)},l.name))})]})}function ce({tools:s,selected:d,onChange:x}){const n=new Set(d),{builtin:a,groups:i}=oe(s),p=o=>x(n.has(o)?d.filter(g=>g!==o):[...d,o]),c=(o,g)=>{const u=d.filter(l=>!o.includes(l));x(g?[...u,...o]:u)};return s.length===0?e.jsx("p",{className:"text-[12px] text-faint",children:"No tools available yet."}):e.jsxs("div",{className:"flex flex-col gap-2",children:[a.length>0&&e.jsx("div",{className:"flex flex-col gap-2 rounded-md border border-border bg-panel-2 p-3",children:a.map(o=>e.jsx(D,{tool:o,checked:n.has(o.name),onToggle:()=>p(o.name)},o.name))}),i.map(o=>e.jsx(ie,{group:o,selected:n,onToggleAll:c,onToggleOne:p},o.key))]})}function ve(){const{agentId:s="new"}=W(),d=$(),x=G(),n=s==="new",a=!n&&B.some(t=>t.id===s),i=a?B.find(t=>t.id===s):void 0,p=H(),c=!n&&!a?(p.data??[]).find(t=>t.id===s):void 0,o=Y(),g=J(),u=U(),l=a?g.data?.[s]:void 0,I=z({queryKey:Z.list(),queryFn:X,staleTime:0}),k=I.data??[],w=z({queryKey:["available-tools"],queryFn:V}),N=w.data??[],[m,f]=b.useState(ae()),[S,T]=b.useState(!1),[A,P]=b.useState(!1),[L,R]=b.useState(!1),[E,Q]=b.useState("skills");n&&!S&&(k.length||N.length)&&(f(t=>({...t,skillIds:k.map(r=>r.id),tools:N.map(r=>r.name)})),T(!0)),!n&&!a&&!S&&c&&(f({name:c.name,description:c.description,systemPrompt:c.systemPrompt,skillIds:c.skillIds,tools:c.tools}),T(!0)),a&&!S&&i&&g.isSuccess&&(k.length||N.length)&&(f({name:l?.name??i.name,description:l?.description??i.description,systemPrompt:l?.systemPrompt??i.systemPrompt,skillIds:l?.skillIds??k.map(t=>t.id),tools:l?.tools??N.map(t=>t.name)}),T(!0));const y=()=>{j("agents","back_to_agents"),d("/customize")},O=t=>f(r=>({...r,skillIds:r.skillIds.includes(t)?r.skillIds.filter(v=>v!==t):[...r.skillIds,t]})),F=()=>{if(!m.name.trim()){h.error("Name is required.");return}P(!0);const t={name:m.name.trim(),description:m.description.trim(),systemPrompt:m.systemPrompt,skillIds:m.skillIds,tools:m.tools};if(a){u.save.mutate({id:s,override:t},{onSuccess:()=>{h.success("Agent saved."),y()},onError:v=>{h.error(v instanceof _?v.message:"Could not save agent."),P(!1)}});return}const r={onSuccess:()=>{x.invalidateQueries({queryKey:["chat-agents"]}),h.success(n?"Agent created.":"Agent saved."),y()},onError:v=>{h.error(v instanceof _?v.message:"Could not save agent."),P(!1)}};n?o.add.mutate(t,r):o.update.mutate({id:s,patch:t},r)},K=()=>{u.reset.mutate(s,{onSuccess:()=>{h.success("Reset to default."),i&&f({name:i.name,description:i.description,systemPrompt:i.systemPrompt,skillIds:k.map(t=>t.id),tools:N.map(t=>t.name)})},onError:t=>h.error(t instanceof _?t.message:"Could not reset agent.")})},M=()=>{o.remove.mutate(s,{onSuccess:()=>{h.success("Agent deleted."),y()},onError:t=>h.error(t instanceof _?t.message:"Could not delete agent.")})};return!n&&!a&&p.isSuccess&&!c?e.jsxs("div",{className:"flex w-full flex-col items-center gap-3 px-8 py-16",children:[e.jsx("p",{className:"text-[14px] text-muted",children:"That agent no longer exists."}),e.jsx(C,{size:"sm",variant:"outline",onClick:y,children:"Back to agents"})]}):e.jsxs("div",{className:"flex w-full max-w-4xl flex-col gap-5 px-4 py-6 md:px-8",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("button",{type:"button",onClick:y,className:"text-faint hover:text-ink",title:"Back to agents",children:e.jsx(ee,{size:18})}),e.jsx("span",{className:"text-[15px] font-medium text-ink",children:n?"New agent":a?l?"Edit agent (modified built-in)":"Edit agent (built-in)":"Edit agent"}),e.jsxs("div",{className:"ml-auto flex items-center gap-2",children:[a&&e.jsxs(C,{size:"sm",variant:"outline",onClick:()=>{j("agents","reset_agent_to_default"),K()},disabled:u.reset.isPending,children:[e.jsx(te,{size:13})," Reset to default"]}),e.jsx(C,{size:"sm",variant:"outline",onClick:y,children:"Cancel"}),e.jsx(C,{size:"sm",onClick:()=>{j("agents","save_agent"),F()},disabled:A||!m.name.trim(),children:A?"Saving…":"Save"})]})]}),a&&e.jsx("p",{className:"rounded-md border border-border bg-panel px-3 py-2 text-[12px] text-muted",children:"Built-in agents can be edited in place — Reset to default discards your changes and restores TurboLLM's original prompt and access."}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{className:"text-[12px] font-medium text-muted",children:"Name"}),e.jsx("input",{className:"rounded-md border border-border bg-bg px-3 py-1.5 text-[13px] text-ink outline-none focus:border-accent placeholder:text-faint",placeholder:"Release Notes Writer",value:m.name,onChange:t=>f(r=>({...r,name:t.target.value}))})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{className:"text-[12px] font-medium text-muted",children:"Description"}),e.jsx("input",{className:"rounded-md border border-border bg-bg px-3 py-1.5 text-[13px] text-ink outline-none focus:border-accent placeholder:text-faint",placeholder:"What this agent is for, in one line",value:m.description,onChange:t=>f(r=>({...r,description:t.target.value}))})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{className:"text-[12px] font-medium text-muted",children:"System prompt"}),e.jsx("textarea",{className:"min-h-[160px] resize-y rounded-md border border-border bg-bg px-3 py-2 font-mono text-[12px] leading-relaxed text-ink outline-none focus:border-accent placeholder:text-faint",placeholder:"You are…",value:m.systemPrompt,onChange:t=>f(r=>({...r,systemPrompt:t.target.value}))})]}),e.jsxs("div",{className:"flex flex-col gap-2.5",children:[e.jsx("div",{className:"inline-flex w-fit rounded-md border border-border p-0.5",children:["skills","tools"].map(t=>e.jsx("button",{type:"button",onClick:()=>{j("agents","switch_agent_form_tab"),Q(t)},className:q("rounded px-3 py-1.5 text-[13px] font-medium transition-colors",E===t?"bg-accent/12 text-accent":"text-muted hover:text-ink"),children:t==="skills"?`Skills (${m.skillIds.length})`:`Tools (${m.tools.length})`},t))}),E==="skills"?I.isLoading?e.jsx("p",{className:"text-[12px] text-faint",children:"Loading…"}):k.length===0?e.jsx("p",{className:"text-[12px] text-faint",children:"No skills yet — create one under Customize → Skills."}):e.jsx("div",{className:"flex flex-col gap-2 rounded-md border border-border bg-panel-2 p-3",children:k.map(t=>e.jsxs("label",{className:"flex cursor-pointer items-start gap-2 text-[13px]",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:m.skillIds.includes(t.id),onChange:()=>O(t.id)}),e.jsxs("span",{className:"flex flex-col",children:[e.jsx("span",{className:"text-ink",children:t.name}),t.description&&e.jsx("span",{className:"text-[12px] text-muted",children:t.description})]})]},t.id))}):w.isLoading?e.jsx("p",{className:"text-[12px] text-faint",children:"Loading…"}):e.jsx(ce,{tools:N,selected:m.tools,onChange:t=>f(r=>({...r,tools:t}))})]}),!n&&!a&&(L?e.jsxs("div",{className:"flex items-center gap-2 self-start rounded-md border border-border bg-panel p-2 text-[12px]",children:[e.jsx("span",{className:"flex-1 text-muted",children:"Delete this agent?"}),e.jsx("button",{type:"button",onClick:()=>{j("agents","delete_agent"),M()},className:"rounded px-2 py-1 text-[color:var(--err)] hover:bg-[color:color-mix(in_srgb,var(--err)_12%,transparent)]",children:"Delete"}),e.jsx("button",{type:"button",onClick:()=>{j("agents","cancel_delete_agent"),R(!1)},className:"rounded px-2 py-1 text-faint hover:text-ink",children:"Cancel"})]}):e.jsxs("button",{type:"button",onClick:()=>R(!0),className:"flex items-center gap-1.5 self-start text-[12px] text-faint hover:text-[color:var(--err)]",children:[e.jsx(se,{size:13})," Delete agent"]}))]})}export{ve as AgentEditPage};
1
+ import{j as e}from"./radix-B0YBjihE.js";import{b as W,u as $,r as b}from"./vendor-C1I6euDx.js";import{u as G,d as H,e as Y,f as J,g as U,a as z,B as C,t as j,c as q,b as h,A as _}from"./index-CRd__4R1.js";import{f as V}from"./chat-api-BTcC6kED.js";import{f as X,a as Z}from"./agent-api-CpgzOfg0.js";import{P as B}from"./personas-B0p0BX69.js";import{C as ee}from"./chevron-left-BVxkM758.js";import{R as te}from"./rotate-ccw-gR3mMTRM.js";import{T as se}from"./trash-2-DZYSgl2R.js";import{C as ne}from"./chevron-right-BJZRbTUj.js";const ae=()=>({name:"",description:"",systemPrompt:"",skillIds:[],tools:[]});function oe(s){const d=[],x=new Map;for(const n of s){const a=/^mcp__([^_]+(?:_[^_]+)*)__/.exec(n.name);if(!a){d.push(n);continue}const i=a[1],p=/^\[([^\]]+)\]/.exec(n.description),c=p?p[1]:i.replace(/_/g," ");x.has(i)||x.set(i,{key:i,label:c,tools:[]}),x.get(i).tools.push(n)}return{builtin:d,groups:[...x.values()].sort((n,a)=>n.label.localeCompare(a.label))}}function le(s){return s.replace(/^mcp__[^_]+(?:_[^_]+)*__/,"")}function re(s){return s.replace(/^\[[^\]]+\]\s*/,"")}function D({tool:s,checked:d,onToggle:x}){return e.jsxs("label",{className:"flex cursor-pointer items-start gap-2 text-[12px]",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:d,onChange:x}),e.jsxs("span",{className:"flex flex-col",children:[e.jsx("span",{className:"font-mono text-ink",children:le(s.name)}),s.description&&e.jsx("span",{className:"text-faint",children:re(s.description)})]})]})}function ie({group:s,selected:d,onToggleAll:x,onToggleOne:n}){const[a,i]=b.useState(!1),p=s.tools.map(l=>l.name),c=p.filter(l=>d.has(l)).length,o=c===p.length,g=c>0&&!o,u=b.useRef(null);return b.useEffect(()=>{u.current&&(u.current.indeterminate=g)},[g]),e.jsxs("div",{className:"rounded-md border border-border",children:[e.jsxs("div",{className:"flex items-center gap-2 px-2.5 py-2",children:[e.jsx("input",{ref:u,type:"checkbox",checked:o,onChange:()=>x(p,!o)}),e.jsxs("button",{type:"button",onClick:()=>{j("agents","toggle_agent_tool_group"),i(l=>!l)},className:"flex flex-1 items-center gap-1.5 text-left text-[13px] text-ink",children:[e.jsx(ne,{size:12,className:q("shrink-0 text-faint transition-transform",a&&"rotate-90")}),s.label,e.jsxs("span",{className:"text-[11px] text-faint",children:["(",c,"/",p.length,")"]})]})]}),a&&e.jsx("div",{className:"flex flex-col gap-1.5 border-t border-border px-3 py-2 pl-9",children:s.tools.map(l=>e.jsx(D,{tool:l,checked:d.has(l.name),onToggle:()=>n(l.name)},l.name))})]})}function ce({tools:s,selected:d,onChange:x}){const n=new Set(d),{builtin:a,groups:i}=oe(s),p=o=>x(n.has(o)?d.filter(g=>g!==o):[...d,o]),c=(o,g)=>{const u=d.filter(l=>!o.includes(l));x(g?[...u,...o]:u)};return s.length===0?e.jsx("p",{className:"text-[12px] text-faint",children:"No tools available yet."}):e.jsxs("div",{className:"flex flex-col gap-2",children:[a.length>0&&e.jsx("div",{className:"flex flex-col gap-2 rounded-md border border-border bg-panel-2 p-3",children:a.map(o=>e.jsx(D,{tool:o,checked:n.has(o.name),onToggle:()=>p(o.name)},o.name))}),i.map(o=>e.jsx(ie,{group:o,selected:n,onToggleAll:c,onToggleOne:p},o.key))]})}function ve(){const{agentId:s="new"}=W(),d=$(),x=G(),n=s==="new",a=!n&&B.some(t=>t.id===s),i=a?B.find(t=>t.id===s):void 0,p=H(),c=!n&&!a?(p.data??[]).find(t=>t.id===s):void 0,o=Y(),g=J(),u=U(),l=a?g.data?.[s]:void 0,I=z({queryKey:Z.list(),queryFn:X,staleTime:0}),k=I.data??[],w=z({queryKey:["available-tools"],queryFn:V}),N=w.data??[],[m,f]=b.useState(ae()),[S,T]=b.useState(!1),[A,P]=b.useState(!1),[L,R]=b.useState(!1),[E,Q]=b.useState("skills");n&&!S&&(k.length||N.length)&&(f(t=>({...t,skillIds:k.map(r=>r.id),tools:N.map(r=>r.name)})),T(!0)),!n&&!a&&!S&&c&&(f({name:c.name,description:c.description,systemPrompt:c.systemPrompt,skillIds:c.skillIds,tools:c.tools}),T(!0)),a&&!S&&i&&g.isSuccess&&(k.length||N.length)&&(f({name:l?.name??i.name,description:l?.description??i.description,systemPrompt:l?.systemPrompt??i.systemPrompt,skillIds:l?.skillIds??k.map(t=>t.id),tools:l?.tools??N.map(t=>t.name)}),T(!0));const y=()=>{j("agents","back_to_agents"),d("/customize")},O=t=>f(r=>({...r,skillIds:r.skillIds.includes(t)?r.skillIds.filter(v=>v!==t):[...r.skillIds,t]})),F=()=>{if(!m.name.trim()){h.error("Name is required.");return}P(!0);const t={name:m.name.trim(),description:m.description.trim(),systemPrompt:m.systemPrompt,skillIds:m.skillIds,tools:m.tools};if(a){u.save.mutate({id:s,override:t},{onSuccess:()=>{h.success("Agent saved."),y()},onError:v=>{h.error(v instanceof _?v.message:"Could not save agent."),P(!1)}});return}const r={onSuccess:()=>{x.invalidateQueries({queryKey:["chat-agents"]}),h.success(n?"Agent created.":"Agent saved."),y()},onError:v=>{h.error(v instanceof _?v.message:"Could not save agent."),P(!1)}};n?o.add.mutate(t,r):o.update.mutate({id:s,patch:t},r)},K=()=>{u.reset.mutate(s,{onSuccess:()=>{h.success("Reset to default."),i&&f({name:i.name,description:i.description,systemPrompt:i.systemPrompt,skillIds:k.map(t=>t.id),tools:N.map(t=>t.name)})},onError:t=>h.error(t instanceof _?t.message:"Could not reset agent.")})},M=()=>{o.remove.mutate(s,{onSuccess:()=>{h.success("Agent deleted."),y()},onError:t=>h.error(t instanceof _?t.message:"Could not delete agent.")})};return!n&&!a&&p.isSuccess&&!c?e.jsxs("div",{className:"flex w-full flex-col items-center gap-3 px-8 py-16",children:[e.jsx("p",{className:"text-[14px] text-muted",children:"That agent no longer exists."}),e.jsx(C,{size:"sm",variant:"outline",onClick:y,children:"Back to agents"})]}):e.jsxs("div",{className:"flex w-full max-w-4xl flex-col gap-5 px-4 py-6 md:px-8",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("button",{type:"button",onClick:y,className:"text-faint hover:text-ink",title:"Back to agents",children:e.jsx(ee,{size:18})}),e.jsx("span",{className:"text-[15px] font-medium text-ink",children:n?"New agent":a?l?"Edit agent (modified built-in)":"Edit agent (built-in)":"Edit agent"}),e.jsxs("div",{className:"ml-auto flex items-center gap-2",children:[a&&e.jsxs(C,{size:"sm",variant:"outline",onClick:()=>{j("agents","reset_agent_to_default"),K()},disabled:u.reset.isPending,children:[e.jsx(te,{size:13})," Reset to default"]}),e.jsx(C,{size:"sm",variant:"outline",onClick:y,children:"Cancel"}),e.jsx(C,{size:"sm",onClick:()=>{j("agents","save_agent"),F()},disabled:A||!m.name.trim(),children:A?"Saving…":"Save"})]})]}),a&&e.jsx("p",{className:"rounded-md border border-border bg-panel px-3 py-2 text-[12px] text-muted",children:"Built-in agents can be edited in place — Reset to default discards your changes and restores TurboLLM's original prompt and access."}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{className:"text-[12px] font-medium text-muted",children:"Name"}),e.jsx("input",{className:"rounded-md border border-border bg-bg px-3 py-1.5 text-[13px] text-ink outline-none focus:border-accent placeholder:text-faint",placeholder:"Release Notes Writer",value:m.name,onChange:t=>f(r=>({...r,name:t.target.value}))})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{className:"text-[12px] font-medium text-muted",children:"Description"}),e.jsx("input",{className:"rounded-md border border-border bg-bg px-3 py-1.5 text-[13px] text-ink outline-none focus:border-accent placeholder:text-faint",placeholder:"What this agent is for, in one line",value:m.description,onChange:t=>f(r=>({...r,description:t.target.value}))})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{className:"text-[12px] font-medium text-muted",children:"System prompt"}),e.jsx("textarea",{className:"min-h-[160px] resize-y rounded-md border border-border bg-bg px-3 py-2 font-mono text-[12px] leading-relaxed text-ink outline-none focus:border-accent placeholder:text-faint",placeholder:"You are…",value:m.systemPrompt,onChange:t=>f(r=>({...r,systemPrompt:t.target.value}))})]}),e.jsxs("div",{className:"flex flex-col gap-2.5",children:[e.jsx("div",{className:"inline-flex w-fit rounded-md border border-border p-0.5",children:["skills","tools"].map(t=>e.jsx("button",{type:"button",onClick:()=>{j("agents","switch_agent_form_tab"),Q(t)},className:q("rounded px-3 py-1.5 text-[13px] font-medium transition-colors",E===t?"bg-accent/12 text-accent":"text-muted hover:text-ink"),children:t==="skills"?`Skills (${m.skillIds.length})`:`Tools (${m.tools.length})`},t))}),E==="skills"?I.isLoading?e.jsx("p",{className:"text-[12px] text-faint",children:"Loading…"}):k.length===0?e.jsx("p",{className:"text-[12px] text-faint",children:"No skills yet — create one under Customize → Skills."}):e.jsx("div",{className:"flex flex-col gap-2 rounded-md border border-border bg-panel-2 p-3",children:k.map(t=>e.jsxs("label",{className:"flex cursor-pointer items-start gap-2 text-[13px]",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:m.skillIds.includes(t.id),onChange:()=>O(t.id)}),e.jsxs("span",{className:"flex flex-col",children:[e.jsx("span",{className:"text-ink",children:t.name}),t.description&&e.jsx("span",{className:"text-[12px] text-muted",children:t.description})]})]},t.id))}):w.isLoading?e.jsx("p",{className:"text-[12px] text-faint",children:"Loading…"}):e.jsx(ce,{tools:N,selected:m.tools,onChange:t=>f(r=>({...r,tools:t}))})]}),!n&&!a&&(L?e.jsxs("div",{className:"flex items-center gap-2 self-start rounded-md border border-border bg-panel p-2 text-[12px]",children:[e.jsx("span",{className:"flex-1 text-muted",children:"Delete this agent?"}),e.jsx("button",{type:"button",onClick:()=>{j("agents","delete_agent"),M()},className:"rounded px-2 py-1 text-[color:var(--err)] hover:bg-[color:color-mix(in_srgb,var(--err)_12%,transparent)]",children:"Delete"}),e.jsx("button",{type:"button",onClick:()=>{j("agents","cancel_delete_agent"),R(!1)},className:"rounded px-2 py-1 text-faint hover:text-ink",children:"Cancel"})]}):e.jsxs("button",{type:"button",onClick:()=>R(!0),className:"flex items-center gap-1.5 self-start text-[12px] text-faint hover:text-[color:var(--err)]",children:[e.jsx(se,{size:13})," Delete agent"]}))]})}export{ve as AgentEditPage};