reasonix 0.46.0 → 0.47.0

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 (159) hide show
  1. package/README.md +64 -12
  2. package/README.zh-CN.md +54 -9
  3. package/dashboard/dist/app.js +293 -66
  4. package/dashboard/dist/app.js.map +1 -1
  5. package/dist/cli/{acp-LGBLHBKY.js → acp-QK3DMC53.js} +22 -22
  6. package/dist/cli/chat-VV5UWY4V.js +51 -0
  7. package/dist/cli/{chunk-AVFXO2EZ.js → chunk-24A7FHGJ.js} +148 -16
  8. package/dist/cli/chunk-24A7FHGJ.js.map +1 -0
  9. package/dist/cli/chunk-25T6CVUP.js +0 -0
  10. package/dist/cli/chunk-2UQP6H6T.js +0 -0
  11. package/dist/cli/chunk-5QCB62C4.js +0 -0
  12. package/dist/cli/{chunk-YY227BIQ.js → chunk-6J6BSUCR.js} +2 -2
  13. package/dist/cli/chunk-6OWJV3YW.js +0 -0
  14. package/dist/cli/{chunk-A3TSSDS2.js → chunk-BWYVFFKR.js} +2 -2
  15. package/dist/cli/{chunk-C53JQES5.js → chunk-BYYVYJDX.js} +3 -3
  16. package/dist/cli/{chunk-HNXDZGC6.js → chunk-CI2PF5QX.js} +2 -2
  17. package/dist/cli/{chunk-GTZTQNX5.js → chunk-COWPEX54.js} +19 -9
  18. package/dist/cli/chunk-COWPEX54.js.map +1 -0
  19. package/dist/cli/{chunk-QJDDIK3Z.js → chunk-E5WCLUIU.js} +2 -2
  20. package/dist/cli/{chunk-NVURFF27.js → chunk-EQATK2L2.js} +2 -2
  21. package/dist/cli/{chunk-HKWSPKMU.js → chunk-FDKOUJKZ.js} +8 -8
  22. package/dist/cli/chunk-FEZK652I.js +0 -0
  23. package/dist/cli/{chunk-TEUDEGX2.js → chunk-FY4S7TJZ.js} +19 -5
  24. package/dist/cli/chunk-FY4S7TJZ.js.map +1 -0
  25. package/dist/cli/{chunk-RDRC3XDT.js → chunk-GDKB2PPK.js} +2 -2
  26. package/dist/cli/{chunk-XSU4QVFW.js → chunk-HIYTRCSW.js} +27 -14
  27. package/dist/cli/chunk-HIYTRCSW.js.map +1 -0
  28. package/dist/cli/{chunk-WL6SNQ5T.js → chunk-ICAFSZHS.js} +307 -114
  29. package/dist/cli/chunk-ICAFSZHS.js.map +1 -0
  30. package/dist/cli/{chunk-KQU2TYIL.js → chunk-ICSYGIPN.js} +1916 -1098
  31. package/dist/cli/chunk-ICSYGIPN.js.map +1 -0
  32. package/dist/cli/chunk-J5XJHLWM.js +0 -0
  33. package/dist/cli/chunk-JMBMLOBP.js +0 -0
  34. package/dist/cli/{chunk-MJ6W5UN3.js → chunk-K6GUKSXH.js} +3 -2
  35. package/dist/cli/chunk-K6GUKSXH.js.map +1 -0
  36. package/dist/cli/{chunk-IJ7JA32V.js → chunk-KDRUEXII.js} +189 -26
  37. package/dist/cli/chunk-KDRUEXII.js.map +1 -0
  38. package/dist/cli/{chunk-4HCP2UQW.js → chunk-LBLR4CUZ.js} +2 -2
  39. package/dist/cli/{chunk-2425HK6U.js → chunk-LGEKVMMV.js} +7 -2
  40. package/dist/cli/{chunk-2425HK6U.js.map → chunk-LGEKVMMV.js.map} +1 -1
  41. package/dist/cli/{chunk-I4L2GTSE.js → chunk-OJVITDGB.js} +2 -2
  42. package/dist/cli/chunk-PLHAZOLZ.js +0 -0
  43. package/dist/cli/{chunk-W7YGWUWU.js → chunk-QVDWH2A2.js} +3 -3
  44. package/dist/cli/{chunk-R3CTO2HM.js → chunk-QVUFWDD2.js} +2 -2
  45. package/dist/cli/{chunk-HVUZWNSP.js → chunk-R6GQKKBW.js} +2 -2
  46. package/dist/cli/{chunk-5ACMUK4Q.js → chunk-RRXUIPWG.js} +20 -18
  47. package/dist/cli/chunk-RRXUIPWG.js.map +1 -0
  48. package/dist/cli/chunk-S4XVGLRW.js +0 -0
  49. package/dist/cli/chunk-SZ5XES2N.js +0 -0
  50. package/dist/cli/{chunk-CXVWUPA3.js → chunk-TKVXTQ3T.js} +26 -26
  51. package/dist/cli/chunk-TKVXTQ3T.js.map +1 -0
  52. package/dist/cli/chunk-TUK7OWJA.js +0 -0
  53. package/dist/cli/{chunk-JNAQYELD.js → chunk-UDVFBEXC.js} +3 -3
  54. package/dist/cli/{chunk-CBIQWMS6.js → chunk-VC2CQA5D.js} +9 -9
  55. package/dist/cli/{chunk-ZZYBBX5N.js → chunk-VJMBISEI.js} +23 -9
  56. package/dist/cli/chunk-VJMBISEI.js.map +1 -0
  57. package/dist/cli/{chunk-WK3UFQY3.js → chunk-VKYSZKH2.js} +2 -2
  58. package/dist/cli/{chunk-LIR2HBQH.js → chunk-VMUUFWFF.js} +2 -2
  59. package/dist/cli/{chunk-V26WPN3J.js → chunk-VNQGCA3Q.js} +28 -1
  60. package/dist/cli/chunk-VNQGCA3Q.js.map +1 -0
  61. package/dist/cli/{chunk-5I2C4JEO.js → chunk-WF7TPVZM.js} +6 -6
  62. package/dist/cli/{chunk-5I2C4JEO.js.map → chunk-WF7TPVZM.js.map} +1 -1
  63. package/dist/cli/chunk-X53B3JIX.js +0 -0
  64. package/dist/cli/chunk-XJXDHAES.js +0 -0
  65. package/dist/cli/chunk-XXC2BYTV.js +0 -0
  66. package/dist/cli/{chunk-4CTDEJUF.js → chunk-YDPLF7XR.js} +26 -14
  67. package/dist/cli/chunk-YDPLF7XR.js.map +1 -0
  68. package/dist/cli/chunk-ZZM6QJ4W.js +0 -0
  69. package/dist/cli/{code-DFHSASJ4.js → code-C24TUAE5.js} +39 -35
  70. package/dist/cli/code-C24TUAE5.js.map +1 -0
  71. package/dist/cli/{commands-OCU42XG4.js → commands-RR3GIYOK.js} +4 -4
  72. package/dist/cli/{commit-XCQIQCYG.js → commit-FSHPIINM.js} +3 -3
  73. package/dist/cli/{desktop-ZCUG7LMF.js → desktop-7NCHPEFB.js} +263 -36
  74. package/dist/cli/desktop-7NCHPEFB.js.map +1 -0
  75. package/dist/cli/devtools-HW3WDT3Q.js +0 -0
  76. package/dist/cli/{diff-66B2KWOJ.js → diff-RAAHHLHV.js} +8 -8
  77. package/dist/cli/{doctor-Y73CPPRZ.js → doctor-PKVQIXRT.js} +9 -9
  78. package/dist/cli/{events-NGZ2OJYH.js → events-VRYXOSKI.js} +3 -3
  79. package/dist/cli/index.js +84 -92
  80. package/dist/cli/index.js.map +1 -1
  81. package/dist/cli/{mcp-MPVGBBJF.js → mcp-CRJ26PP4.js} +2 -2
  82. package/dist/cli/{mcp-browse-4XOTC3FJ.js → mcp-browse-QPAOWZOP.js} +2 -2
  83. package/dist/cli/{mcp-inspect-CEMGKKAH.js → mcp-inspect-CVCLABRS.js} +4 -4
  84. package/dist/cli/{prompt-2D7ID24X.js → prompt-SKYXERSI.js} +4 -4
  85. package/dist/cli/{prune-sessions-OJEYYLHY.js → prune-sessions-SEWX7GP6.js} +2 -2
  86. package/dist/cli/{replay-AKYQNAQJ.js → replay-KPDW2ZMJ.js} +9 -9
  87. package/dist/cli/{run-5DPQFSP6.js → run-WIKDIXTG.js} +18 -19
  88. package/dist/cli/run-WIKDIXTG.js.map +1 -0
  89. package/dist/cli/{server-TQ2IHYQJ.js → server-P6V2G3P6.js} +82 -34
  90. package/dist/cli/server-P6V2G3P6.js.map +1 -0
  91. package/dist/cli/{sessions-KY54NG45.js → sessions-2NULRMSA.js} +29 -15
  92. package/dist/cli/sessions-2NULRMSA.js.map +1 -0
  93. package/dist/cli/{setup-XPIOZWS7.js → setup-Y5WDBQFL.js} +8 -8
  94. package/dist/cli/setup-Y5WDBQFL.js.map +1 -0
  95. package/dist/cli/{stats-X2VTWKNS.js → stats-T7BL2YOR.js} +6 -6
  96. package/dist/cli/update-6ITLPRDV.js +0 -0
  97. package/dist/cli/{version-7O6A5T7Q.js → version-3KWDNWLN.js} +15 -15
  98. package/dist/index.d.ts +54 -23
  99. package/dist/index.js +1613 -1152
  100. package/dist/index.js.map +1 -1
  101. package/package.json +1 -1
  102. package/dist/cli/.-3G6VX5S7.js +0 -327
  103. package/dist/cli/.-6YRPB2C7.js +0 -329
  104. package/dist/cli/.-EYSVINK3.js +0 -317
  105. package/dist/cli/chat-ECK5ZGMV.js +0 -51
  106. package/dist/cli/chunk-4CTDEJUF.js.map +0 -1
  107. package/dist/cli/chunk-5ACMUK4Q.js.map +0 -1
  108. package/dist/cli/chunk-AVFXO2EZ.js.map +0 -1
  109. package/dist/cli/chunk-CXVWUPA3.js.map +0 -1
  110. package/dist/cli/chunk-GTZTQNX5.js.map +0 -1
  111. package/dist/cli/chunk-IJ7JA32V.js.map +0 -1
  112. package/dist/cli/chunk-KQU2TYIL.js.map +0 -1
  113. package/dist/cli/chunk-MJ6W5UN3.js.map +0 -1
  114. package/dist/cli/chunk-TEUDEGX2.js.map +0 -1
  115. package/dist/cli/chunk-V26WPN3J.js.map +0 -1
  116. package/dist/cli/chunk-WL6SNQ5T.js.map +0 -1
  117. package/dist/cli/chunk-XSU4QVFW.js.map +0 -1
  118. package/dist/cli/chunk-ZZYBBX5N.js.map +0 -1
  119. package/dist/cli/code-DFHSASJ4.js.map +0 -1
  120. package/dist/cli/desktop-ZCUG7LMF.js.map +0 -1
  121. package/dist/cli/doctor-Y73CPPRZ.js.map +0 -1
  122. package/dist/cli/prompt-2D7ID24X.js.map +0 -1
  123. package/dist/cli/run-5DPQFSP6.js.map +0 -1
  124. package/dist/cli/server-TQ2IHYQJ.js.map +0 -1
  125. package/dist/cli/sessions-KY54NG45.js.map +0 -1
  126. package/dist/cli/setup-XPIOZWS7.js.map +0 -1
  127. package/dist/cli/stats-X2VTWKNS.js.map +0 -1
  128. /package/dist/cli/{acp-LGBLHBKY.js.map → acp-QK3DMC53.js.map} +0 -0
  129. /package/dist/cli/{.-3G6VX5S7.js.map → chat-VV5UWY4V.js.map} +0 -0
  130. /package/dist/cli/{chunk-YY227BIQ.js.map → chunk-6J6BSUCR.js.map} +0 -0
  131. /package/dist/cli/{chunk-A3TSSDS2.js.map → chunk-BWYVFFKR.js.map} +0 -0
  132. /package/dist/cli/{chunk-C53JQES5.js.map → chunk-BYYVYJDX.js.map} +0 -0
  133. /package/dist/cli/{chunk-HNXDZGC6.js.map → chunk-CI2PF5QX.js.map} +0 -0
  134. /package/dist/cli/{chunk-QJDDIK3Z.js.map → chunk-E5WCLUIU.js.map} +0 -0
  135. /package/dist/cli/{chunk-NVURFF27.js.map → chunk-EQATK2L2.js.map} +0 -0
  136. /package/dist/cli/{chunk-HKWSPKMU.js.map → chunk-FDKOUJKZ.js.map} +0 -0
  137. /package/dist/cli/{chunk-RDRC3XDT.js.map → chunk-GDKB2PPK.js.map} +0 -0
  138. /package/dist/cli/{chunk-4HCP2UQW.js.map → chunk-LBLR4CUZ.js.map} +0 -0
  139. /package/dist/cli/{chunk-I4L2GTSE.js.map → chunk-OJVITDGB.js.map} +0 -0
  140. /package/dist/cli/{chunk-W7YGWUWU.js.map → chunk-QVDWH2A2.js.map} +0 -0
  141. /package/dist/cli/{chunk-R3CTO2HM.js.map → chunk-QVUFWDD2.js.map} +0 -0
  142. /package/dist/cli/{chunk-HVUZWNSP.js.map → chunk-R6GQKKBW.js.map} +0 -0
  143. /package/dist/cli/{chunk-JNAQYELD.js.map → chunk-UDVFBEXC.js.map} +0 -0
  144. /package/dist/cli/{chunk-CBIQWMS6.js.map → chunk-VC2CQA5D.js.map} +0 -0
  145. /package/dist/cli/{chunk-WK3UFQY3.js.map → chunk-VKYSZKH2.js.map} +0 -0
  146. /package/dist/cli/{chunk-LIR2HBQH.js.map → chunk-VMUUFWFF.js.map} +0 -0
  147. /package/dist/cli/{commands-OCU42XG4.js.map → commands-RR3GIYOK.js.map} +0 -0
  148. /package/dist/cli/{commit-XCQIQCYG.js.map → commit-FSHPIINM.js.map} +0 -0
  149. /package/dist/cli/{diff-66B2KWOJ.js.map → diff-RAAHHLHV.js.map} +0 -0
  150. /package/dist/cli/{.-6YRPB2C7.js.map → doctor-PKVQIXRT.js.map} +0 -0
  151. /package/dist/cli/{events-NGZ2OJYH.js.map → events-VRYXOSKI.js.map} +0 -0
  152. /package/dist/cli/{mcp-MPVGBBJF.js.map → mcp-CRJ26PP4.js.map} +0 -0
  153. /package/dist/cli/{mcp-browse-4XOTC3FJ.js.map → mcp-browse-QPAOWZOP.js.map} +0 -0
  154. /package/dist/cli/{mcp-inspect-CEMGKKAH.js.map → mcp-inspect-CVCLABRS.js.map} +0 -0
  155. /package/dist/cli/{.-EYSVINK3.js.map → prompt-SKYXERSI.js.map} +0 -0
  156. /package/dist/cli/{prune-sessions-OJEYYLHY.js.map → prune-sessions-SEWX7GP6.js.map} +0 -0
  157. /package/dist/cli/{replay-AKYQNAQJ.js.map → replay-KPDW2ZMJ.js.map} +0 -0
  158. /package/dist/cli/{chat-ECK5ZGMV.js.map → stats-T7BL2YOR.js.map} +0 -0
  159. /package/dist/cli/{version-7O6A5T7Q.js.map → version-3KWDNWLN.js.map} +0 -0
@@ -19316,7 +19316,16 @@ var en = {
19316
19316
  resumeDesc: "Mid-session swap requires a restart so the message log can rewind cleanly. Quit your current session, then run:",
19317
19317
  loadingTranscript: "loading transcript\u2026",
19318
19318
  emptyTranscript: "empty transcript.",
19319
- messages: "{count} message{s}"
19319
+ messages: "{count} message{s}",
19320
+ newBtn: "New session",
19321
+ newHint: "Archive the current conversation and start a fresh one",
19322
+ switchBtn: "Switch to this session",
19323
+ deleteBtn: "Delete",
19324
+ deleteConfirm: 'Delete session "{name}"? This removes the transcript file and cannot be undone.',
19325
+ cantDeleteActive: "Switch to a different session before deleting this one.",
19326
+ attachRequired: "Live session operations need an attached CLI session. Launch via reasonix chat or open the dashboard from inside a TUI session.",
19327
+ activeChip: "active",
19328
+ activePill: "active"
19320
19329
  },
19321
19330
  tools: {
19322
19331
  loading: "loading tools\u2026",
@@ -19410,6 +19419,9 @@ var en = {
19410
19419
  whyUnbridged: "Why unbridged?",
19411
19420
  whyUnbridgedDesc: "This spec lives in your config.json but isn't bridged into the live session. MCP servers attach when reasonix code starts; the dashboard alone can't spawn the child process.",
19412
19421
  whyUnbridgedHint: "To activate: restart reasonix code, then refresh this dashboard.",
19422
+ bridgeFailed: "Bridge failed",
19423
+ bridgeFailedTitle: "bridge failed \xB7 in config",
19424
+ bridgeFailedHint: "Reasonix tried to bridge this server and the attempt errored. Common causes: wrong URL, missing auth header, upstream 404/5xx, missing local command. Fix and restart reasonix code to retry.",
19413
19425
  pickHint: "Pick an MCP server on the left to inspect tools / resources / prompts.",
19414
19426
  toolsTitle: "Tools \xB7 {count}",
19415
19427
  resourcesTitle: "Resources \xB7 {count}",
@@ -19585,6 +19597,7 @@ var en = {
19585
19597
  customRequestBodyMustBeObject: "Custom request body must be a JSON object.",
19586
19598
  saveBeforeIndex: "Save semantic settings before starting an index.",
19587
19599
  extraBody: "extra body",
19600
+ batchSize: "batch size",
19588
19601
  keepExistingKey: "leave blank to keep existing key",
19589
19602
  remoteProvider: "Remote embedding provider",
19590
19603
  remoteProviderDesc: "Configure the full OpenAI-compatible embeddings URL here. Reasonix will send requests exactly to the URL you provide.",
@@ -20014,7 +20027,16 @@ var zhCN = {
20014
20027
  resumeDesc: "\u4F1A\u8BDD\u4E2D\u9014\u5207\u6362\u9700\u8981\u91CD\u542F\uFF0C\u4EE5\u4FBF\u6D88\u606F\u65E5\u5FD7\u53EF\u4EE5\u5E72\u51C0\u5730\u56DE\u9000\u3002\u8BF7\u9000\u51FA\u5F53\u524D\u4F1A\u8BDD\uFF0C\u7136\u540E\u8FD0\u884C\uFF1A",
20015
20028
  loadingTranscript: "\u52A0\u8F7D\u8F6C\u5F55\u7A3F\u2026",
20016
20029
  emptyTranscript: "\u7A7A\u7684\u8F6C\u5F55\u7A3F\u3002",
20017
- messages: "{count} \u6761\u6D88\u606F"
20030
+ messages: "{count} \u6761\u6D88\u606F",
20031
+ newBtn: "\u65B0\u5EFA\u4F1A\u8BDD",
20032
+ newHint: "\u5F52\u6863\u5F53\u524D\u4F1A\u8BDD\u5E76\u5F00\u542F\u65B0\u4F1A\u8BDD",
20033
+ switchBtn: "\u5207\u6362\u5230\u6B64\u4F1A\u8BDD",
20034
+ deleteBtn: "\u5220\u9664",
20035
+ deleteConfirm: "\u786E\u5B9A\u5220\u9664\u4F1A\u8BDD\u300C{name}\u300D\uFF1F\u8F6C\u5F55\u6587\u4EF6\u5C06\u88AB\u79FB\u9664\uFF0C\u65E0\u6CD5\u64A4\u9500\u3002",
20036
+ cantDeleteActive: "\u8BF7\u5148\u5207\u6362\u5230\u5176\u4ED6\u4F1A\u8BDD\uFF0C\u518D\u5220\u9664\u5F53\u524D\u4F1A\u8BDD\u3002",
20037
+ attachRequired: "\u5B9E\u65F6\u4F1A\u8BDD\u64CD\u4F5C\u9700\u8981\u5DF2\u8FDE\u63A5\u7684 CLI \u4F1A\u8BDD\u3002\u8BF7\u901A\u8FC7 reasonix chat \u542F\u52A8\uFF0C\u6216\u5728 TUI \u4F1A\u8BDD\u4E2D\u6253\u5F00\u4EEA\u8868\u76D8\u3002",
20038
+ activeChip: "\u5F53\u524D",
20039
+ activePill: "\u5F53\u524D"
20018
20040
  },
20019
20041
  tools: {
20020
20042
  loading: "\u52A0\u8F7D\u5DE5\u5177\u2026",
@@ -20108,6 +20130,9 @@ var zhCN = {
20108
20130
  whyUnbridged: "\u4E3A\u4EC0\u4E48\u672A\u6865\u63A5\uFF1F",
20109
20131
  whyUnbridgedDesc: "\u6B64\u89C4\u683C\u5B58\u5728\u4E8E\u60A8\u7684 config.json \u4E2D\uFF0C\u4F46\u672A\u6865\u63A5\u5230\u5B9E\u65F6\u4F1A\u8BDD\u3002MCP \u670D\u52A1\u5668\u5728 reasonix code \u542F\u52A8\u65F6\u8FDE\u63A5\uFF1B\u4EEA\u8868\u76D8\u672C\u8EAB\u65E0\u6CD5\u751F\u6210\u5B50\u8FDB\u7A0B\u3002",
20110
20132
  whyUnbridgedHint: "\u6FC0\u6D3B\u65B9\u6CD5\uFF1A\u91CD\u542F reasonix code\uFF0C\u7136\u540E\u5237\u65B0\u6B64\u4EEA\u8868\u76D8\u3002",
20133
+ bridgeFailed: "\u6865\u63A5\u5931\u8D25",
20134
+ bridgeFailedTitle: "\u6865\u63A5\u5931\u8D25 \xB7 \u5728\u914D\u7F6E\u4E2D",
20135
+ bridgeFailedHint: "\u5DF2\u5C1D\u8BD5\u6865\u63A5\u4F46\u5931\u8D25\u3002\u5E38\u89C1\u539F\u56E0\uFF1AURL \u9519\u8BEF\u3001\u9700\u8981\u9274\u6743\u3001\u4E0A\u6E38 404 / 5xx\u3001\u672C\u5730\u547D\u4EE4\u7F3A\u5931\u3002\u4FEE\u590D\u540E\u91CD\u542F reasonix code \u91CD\u8BD5\u3002",
20111
20136
  pickHint: "\u9009\u62E9\u5DE6\u4FA7\u7684 MCP \u670D\u52A1\u5668\u4EE5\u68C0\u67E5\u5DE5\u5177 / \u8D44\u6E90 / \u63D0\u793A\u3002",
20112
20137
  toolsTitle: "\u5DE5\u5177 \xB7 {count}",
20113
20138
  resourcesTitle: "\u8D44\u6E90 \xB7 {count}",
@@ -20283,6 +20308,7 @@ var zhCN = {
20283
20308
  customRequestBodyMustBeObject: "\u81EA\u5B9A\u4E49\u8BF7\u6C42\u4F53\u5FC5\u987B\u662F JSON \u5BF9\u8C61\u3002",
20284
20309
  saveBeforeIndex: "\u8BF7\u5148\u4FDD\u5B58\u8BED\u4E49\u8BBE\u7F6E\uFF0C\u518D\u542F\u52A8\u7D22\u5F15\u3002",
20285
20310
  extraBody: "\u6269\u5C55\u8BF7\u6C42\u4F53",
20311
+ batchSize: "\u6279\u6B21\u5927\u5C0F",
20286
20312
  keepExistingKey: "\u7559\u7A7A\u5219\u4FDD\u7559\u73B0\u6709 Key",
20287
20313
  remoteProvider: "\u8FDC\u7A0B\u5411\u91CF\u670D\u52A1",
20288
20314
  remoteProviderDesc: "\u5728\u8FD9\u91CC\u914D\u7F6E OpenAI-Compatible embeddings \u7684\u5B8C\u6574 URL\u3002Reasonix \u4F1A\u4E25\u683C\u4F7F\u7528\u4F60\u63D0\u4F9B\u7684 URL \u53D1\u8D77\u8BF7\u6C42\u3002",
@@ -25059,12 +25085,22 @@ function CodeViewer(props) {
25059
25085
  </div>
25060
25086
  `;
25061
25087
  }
25088
+ function summarizeTool(activeTool) {
25089
+ if (!activeTool) return null;
25090
+ const name = activeTool.toolName ?? "tool";
25091
+ const args = parseToolArgs(activeTool.args);
25092
+ const path = args?.path ?? args?.file_path ?? args?.filename;
25093
+ if (path) return `${name} \u2192 ${path}`;
25094
+ return name;
25095
+ }
25062
25096
  function ChatPane(props) {
25063
25097
  useLang();
25064
25098
  const [messages, setMessages] = d2([]);
25065
25099
  const [streaming, setStreaming] = d2(null);
25066
25100
  const [activeTool, setActiveTool] = d2(null);
25067
25101
  const [busy, setBusy] = d2(false);
25102
+ const [turnStartedAt, setTurnStartedAt] = d2(null);
25103
+ const [nowTick, setNowTick] = d2(0);
25068
25104
  const [input, setInput] = d2("");
25069
25105
  const [error, setError] = d2(null);
25070
25106
  const [statusLine, setStatusLine] = d2(null);
@@ -25079,6 +25115,7 @@ function ChatPane(props) {
25079
25115
  const [popoverKind, setPopoverKind] = d2(null);
25080
25116
  const [popoverItems, setPopoverItems] = d2([]);
25081
25117
  const [popoverSel, setPopoverSel] = d2(0);
25118
+ const composing = A2(false);
25082
25119
  y2(() => {
25083
25120
  let cancelled = false;
25084
25121
  (async () => {
@@ -25092,6 +25129,29 @@ function ChatPane(props) {
25092
25129
  cancelled = true;
25093
25130
  };
25094
25131
  }, []);
25132
+ y2(() => {
25133
+ if (!busy) return;
25134
+ const id = setInterval(() => setNowTick((n3) => n3 + 1), 500);
25135
+ return () => clearInterval(id);
25136
+ }, [busy]);
25137
+ y2(() => {
25138
+ if (busy) {
25139
+ if (!turnStartedAt) setTurnStartedAt(Date.now());
25140
+ } else {
25141
+ setTurnStartedAt(null);
25142
+ }
25143
+ }, [busy, turnStartedAt]);
25144
+ y2(() => {
25145
+ if (!busy) return;
25146
+ const handler = (e3) => {
25147
+ if (e3.key === "Escape") {
25148
+ e3.preventDefault();
25149
+ api("/abort", { method: "POST" }).catch(() => void 0);
25150
+ }
25151
+ };
25152
+ document.addEventListener("keydown", handler);
25153
+ return () => document.removeEventListener("keydown", handler);
25154
+ }, [busy]);
25095
25155
  y2(() => {
25096
25156
  let cancelled = false;
25097
25157
  (async () => {
@@ -25286,6 +25346,7 @@ function ChatPane(props) {
25286
25346
  (e3) => {
25287
25347
  const v3 = e3.target.value;
25288
25348
  setInput(v3);
25349
+ if (composing.current) return;
25289
25350
  updatePopover(v3);
25290
25351
  },
25291
25352
  [updatePopover]
@@ -25364,8 +25425,19 @@ ${commentRefs}` : commentRefs;
25364
25425
  setError(t4("changes.clearFailed", { error: err.message }));
25365
25426
  }
25366
25427
  }, []);
25428
+ const onCompositionStart = q2(() => {
25429
+ composing.current = true;
25430
+ }, []);
25431
+ const onCompositionEnd = q2(
25432
+ (e3) => {
25433
+ composing.current = false;
25434
+ void updatePopover(e3.target.value);
25435
+ },
25436
+ [updatePopover]
25437
+ );
25367
25438
  const onKeyDown = q2(
25368
25439
  (e3) => {
25440
+ if (composing.current) return;
25369
25441
  if (popoverKind && popoverItems.length > 0) {
25370
25442
  if (e3.key === "ArrowDown") {
25371
25443
  e3.preventDefault();
@@ -25388,11 +25460,6 @@ ${commentRefs}` : commentRefs;
25388
25460
  return;
25389
25461
  }
25390
25462
  }
25391
- if (e3.key === "Escape" && busy) {
25392
- e3.preventDefault();
25393
- abort();
25394
- return;
25395
- }
25396
25463
  if (e3.key === "Enter" && !e3.shiftKey) {
25397
25464
  e3.preventDefault();
25398
25465
  send();
@@ -25476,11 +25543,14 @@ ${commentRefs}` : commentRefs;
25476
25543
  <textarea
25477
25544
  class="input"
25478
25545
  style=${{ width: "100%", resize: "none", minHeight: "36px", fontFamily: "inherit", fontSize: "13px", padding: "8px 10px", lineHeight: "1.4", background: "var(--bg-input)", border: "1px solid var(--bd)", borderRadius: "4px", color: "var(--fg-0)" }}
25479
- placeholder=${props.comments.length > 0 ? "\u603B\u7ED3\u8BC4\u8BBA..." : t4("changes.chatPlaceholder")}
25546
+ placeholder=${busy ? t4("chat.placeholderBusy") : props.comments.length > 0 ? "\u603B\u7ED3\u8BC4\u8BBA..." : t4("changes.chatPlaceholder")}
25480
25547
  value=${input}
25481
25548
  onInput=${onInput}
25482
25549
  onKeyDown=${onKeyDown}
25550
+ onCompositionStart=${onCompositionStart}
25551
+ onCompositionEnd=${onCompositionEnd}
25483
25552
  onBlur=${() => setTimeout(() => setPopoverKind(null), 150)}
25553
+ disabled=${busy}
25484
25554
  rows="2"
25485
25555
  />
25486
25556
  </div>
@@ -25493,6 +25563,32 @@ ${commentRefs}` : commentRefs;
25493
25563
  </div>
25494
25564
  </div>
25495
25565
  </div>
25566
+ ${busy ? (() => {
25567
+ const elapsedMs = turnStartedAt ? Date.now() - turnStartedAt : 0;
25568
+ const elapsed = (elapsedMs / 1e3).toFixed(1);
25569
+ const textLen = streaming?.text?.length ?? 0;
25570
+ const reasoningLen = streaming?.reasoning?.length ?? 0;
25571
+ const toolSummary = summarizeTool(activeTool);
25572
+ const phase = toolSummary ? t4("chat.inflightRunning") : reasoningLen > 0 && textLen === 0 ? t4("chat.inflightThinking") : textLen > 0 ? t4("chat.inflightStreaming") : t4("chat.inflightWaiting");
25573
+ return html6`
25574
+ <div class="chat-inflight">
25575
+ <span class="spinner"></span>
25576
+ <span class="chat-inflight-phase">${phase}</span>
25577
+ <span class="chat-inflight-sep">·</span>
25578
+ <span class="muted">${elapsed}s</span>
25579
+ ${toolSummary ? html6`<span class="chat-inflight-sep">·</span><span class="chat-inflight-tool" title=${toolSummary}>${toolSummary}</span>` : null}
25580
+ ${!toolSummary && (textLen > 0 || reasoningLen > 0) ? html6`
25581
+ <span class="chat-inflight-sep">·</span>
25582
+ <span class="muted">
25583
+ ${reasoningLen > 0 ? t4("chat.inflightReasoning", { count: reasoningLen.toLocaleString() }) : null}
25584
+ ${reasoningLen > 0 && textLen > 0 ? html6`<span> · </span>` : null}
25585
+ ${textLen > 0 ? t4("chat.inflightOut", { count: textLen.toLocaleString() }) : null}
25586
+ </span>
25587
+ ` : null}
25588
+ <button class="chat-inflight-abort" onClick=${abort}>${t4("chat.abortBtn")}</button>
25589
+ </div>
25590
+ `;
25591
+ })() : null}
25496
25592
  <${ChatStatusBar} stats=${stats} model=${model} />
25497
25593
  </div>
25498
25594
  `;
@@ -26651,6 +26747,7 @@ function McpPanel() {
26651
26747
  useLang();
26652
26748
  const [data, setData] = d2(null);
26653
26749
  const [specs, setSpecs] = d2(null);
26750
+ const [failures, setFailures] = d2([]);
26654
26751
  const [error, setError] = d2(null);
26655
26752
  const [info, setInfo] = d2(null);
26656
26753
  const [newSpec, setNewSpec] = d2("");
@@ -26722,6 +26819,22 @@ function McpPanel() {
26722
26819
  const specResponse = await api("/mcp/specs");
26723
26820
  const normalized = (Array.isArray(specResponse.specs) ? specResponse.specs : []).map(normalizeMcpSpec).filter((spec) => spec !== null && spec.length > 0);
26724
26821
  setSpecs(normalized);
26822
+ const rawFailures = Array.isArray(specResponse.failures) ? specResponse.failures : [];
26823
+ const validFailures = [];
26824
+ for (const f3 of rawFailures) {
26825
+ if (typeof f3 !== "object" || f3 === null) continue;
26826
+ const o3 = f3;
26827
+ const rawSpec = typeof o3.spec === "string" ? o3.spec : "";
26828
+ const norm = normalizeMcpSpec(rawSpec);
26829
+ if (!norm) continue;
26830
+ validFailures.push({
26831
+ spec: norm,
26832
+ name: typeof o3.name === "string" ? o3.name : "",
26833
+ reason: typeof o3.reason === "string" ? o3.reason : "",
26834
+ at: typeof o3.at === "number" ? o3.at : 0
26835
+ });
26836
+ }
26837
+ setFailures(validFailures);
26725
26838
  } catch (err) {
26726
26839
  setError(err.message);
26727
26840
  }
@@ -26857,8 +26970,9 @@ function McpPanel() {
26857
26970
  </div>
26858
26971
  `
26859
26972
  ) : null}
26860
- ${showUnbridged ? unbridgedSpecs.map(
26861
- (spec) => html4`
26973
+ ${showUnbridged ? unbridgedSpecs.map((spec) => {
26974
+ const failure = failures.find((f3) => f3.spec === spec);
26975
+ return html4`
26862
26976
  <div
26863
26977
  class=${`ssl-row ${openUnbridged === spec ? "sel" : ""}`}
26864
26978
  onClick=${() => {
@@ -26866,12 +26980,12 @@ function McpPanel() {
26866
26980
  setOpen(null);
26867
26981
  }}
26868
26982
  >
26869
- <span class="name">${mcpSpecLabel(spec)} <span class="pill">${t4("mcp.unbridged")}</span></span>
26983
+ <span class="name">${mcpSpecLabel(spec)} <span class=${`pill ${failure ? "err" : ""}`}>${failure ? t4("mcp.bridgeFailed") : t4("mcp.unbridged")}</span></span>
26870
26984
  <span class="preview">${mcpSpecCommand(spec)}</span>
26871
- <span class="meta"><span class="dim">${t4("mcp.inConfig")}</span></span>
26985
+ <span class="meta"><span class=${failure ? "" : "dim"} style=${failure ? "color:var(--c-err)" : ""}>${failure ? failure.reason : t4("mcp.inConfig")}</span></span>
26872
26986
  </div>
26873
- `
26874
- ) : null}
26987
+ `;
26988
+ }) : null}
26875
26989
  </div>
26876
26990
  </div>
26877
26991
 
@@ -26886,10 +27000,12 @@ function McpPanel() {
26886
27000
  onInstall: () => installFromRegistry(openRegistry),
26887
27001
  onUninstall: (spec) => removeSpec(spec),
26888
27002
  onClose: () => setOpenRegistry(null)
26889
- }) : openUnbridged != null ? html4`
27003
+ }) : openUnbridged != null ? (() => {
27004
+ const failure = failures.find((f3) => f3.spec === openUnbridged);
27005
+ return html4`
26890
27006
  <div class="sessions-detail-h">
26891
27007
  <span class="name">${mcpSpecLabel(openUnbridged)}</span>
26892
- <span class="ws"><span class="pill">${t4("mcp.unbridgedTitle")}</span></span>
27008
+ <span class="ws"><span class="pill">${failure ? t4("mcp.bridgeFailedTitle") : t4("mcp.unbridgedTitle")}</span></span>
26893
27009
  <span class="actions">
26894
27010
  <button class="btn" disabled=${busy} onClick=${() => removeSpec(openUnbridged)}
26895
27011
  style="border-color:var(--c-err);color:var(--c-err)">${t4("mcp.removeBtn")}</button>
@@ -26900,16 +27016,25 @@ function McpPanel() {
26900
27016
  <div class="card-h"><span class="title">${t4("mcp.spec")}</span></div>
26901
27017
  <code class="mono" style="font-size:11.5px;color:var(--fg-2);word-break:break-all">${openUnbridged}</code>
26902
27018
  </div>
26903
- <div class="card accent-warn">
26904
- <div class="card-h"><span class="title" style="color:var(--c-warn)">${t4("mcp.whyUnbridged")}</span></div>
26905
- <div class="card-b" style="font-size:13px;line-height:1.6">
26906
- ${t4("mcp.whyUnbridgedDesc")}
26907
- <div style="margin-top:10px;color:var(--fg-3);font-size:12px">
26908
- ${t4("mcp.whyUnbridgedHint")}
26909
- </div>
26910
- </div>
26911
- </div>
26912
- ` : open == null ? html4`<div style="color:var(--fg-3);font-size:13px;text-align:center;padding:60px 20px">
27019
+ ${failure ? html4`<div class="card accent-err">
27020
+ <div class="card-h"><span class="title" style="color:var(--c-err)">${t4("mcp.bridgeFailed")}</span></div>
27021
+ <div class="card-b" style="font-size:13px;line-height:1.6">
27022
+ <code class="mono" style="font-size:12px;color:var(--fg-1);word-break:break-word;white-space:pre-wrap">${failure.reason}</code>
27023
+ <div style="margin-top:10px;color:var(--fg-3);font-size:12px">
27024
+ ${t4("mcp.bridgeFailedHint")}
27025
+ </div>
27026
+ </div>
27027
+ </div>` : html4`<div class="card accent-warn">
27028
+ <div class="card-h"><span class="title" style="color:var(--c-warn)">${t4("mcp.whyUnbridged")}</span></div>
27029
+ <div class="card-b" style="font-size:13px;line-height:1.6">
27030
+ ${t4("mcp.whyUnbridgedDesc")}
27031
+ <div style="margin-top:10px;color:var(--fg-3);font-size:12px">
27032
+ ${t4("mcp.whyUnbridgedHint")}
27033
+ </div>
27034
+ </div>
27035
+ </div>`}
27036
+ `;
27037
+ })() : open == null ? html4`<div style="color:var(--fg-3);font-size:13px;text-align:center;padding:60px 20px">
26913
27038
  ${showMarketplace ? t4("mcp.marketplacePickHint") : t4("mcp.pickHint")}
26914
27039
  </div>` : html4`
26915
27040
  <div class="sessions-detail-h">
@@ -27962,7 +28087,8 @@ function SemanticPanel() {
27962
28087
  baseUrl: draft.openaiCompat.baseUrl,
27963
28088
  apiKey: draft.openaiCompat.apiKey,
27964
28089
  model: draft.openaiCompat.model,
27965
- extraBody
28090
+ extraBody,
28091
+ batchSize: draft.openaiCompat.batchSize
27966
28092
  }
27967
28093
  }
27968
28094
  });
@@ -28117,6 +28243,27 @@ function SemanticPanel() {
28117
28243
  model: e3.target.value
28118
28244
  }
28119
28245
  });
28246
+ }}
28247
+ />
28248
+ </div>
28249
+ <div class="form-row">
28250
+ <span class="lbl">${t4("semantic.batchSize")}</span>
28251
+ <input
28252
+ class="input mono"
28253
+ type="number"
28254
+ min="1"
28255
+ value=${draft.openaiCompat.batchSize}
28256
+ onInput=${(e3) => {
28257
+ const v3 = Number.parseInt(e3.target.value, 10);
28258
+ draftDirtyRef.current = true;
28259
+ setDraftDirty(true);
28260
+ setDraft({
28261
+ ...draft,
28262
+ openaiCompat: {
28263
+ ...draft.openaiCompat,
28264
+ batchSize: Number.isInteger(v3) && v3 > 0 ? v3 : 10
28265
+ }
28266
+ });
28120
28267
  }}
28121
28268
  />
28122
28269
  </div>
@@ -28252,6 +28399,7 @@ function SemanticPanel() {
28252
28399
  <div class="rail-kv"><span class="k">${t4("semantic.apiKey")}</span><span class="v">${remote?.apiKeySet ? html4`<span class="pill ok">${t4("semantic.found")}</span>` : html4`<span class="pill warn">${t4("semantic.missing")}</span>`}</span></div>
28253
28400
  <div class="rail-kv"><span class="k">${t4("semantic.model")}</span><span class="v" style="font-size:11px">${remote?.model ?? draft.openaiCompat.model}</span></div>
28254
28401
  <div class="rail-kv"><span class="k">${t4("semantic.extraBody")}</span><span class="v">${fmtNum(remote?.extraBodyKeys.length ?? 0)}</span></div>
28402
+ <div class="rail-kv"><span class="k">${t4("semantic.batchSize")}</span><span class="v">${remote?.batchSize ?? 10}</span></div>
28255
28403
  `}
28256
28404
  </div>
28257
28405
 
@@ -28272,6 +28420,7 @@ function toConfigDraft(config) {
28272
28420
  apiKey: "",
28273
28421
  model: config.openaiCompat.model,
28274
28422
  extraBodyText: JSON.stringify(config.openaiCompat.extraBody ?? {}, null, 2),
28423
+ batchSize: config.openaiCompat.batchSize,
28275
28424
  apiKeySet: config.openaiCompat.apiKeySet
28276
28425
  }
28277
28426
  };
@@ -28687,10 +28836,12 @@ function isPlainObject(value) {
28687
28836
  // dashboard/src/panels/sessions.ts
28688
28837
  function SessionsPanel() {
28689
28838
  useLang();
28690
- const { data, error, loading } = usePoll("/sessions", 5e3);
28839
+ const { data, error, loading, refresh } = usePoll("/sessions", 5e3);
28691
28840
  const [open, setOpen] = d2(null);
28692
28841
  const [openLoading, setOpenLoading] = d2(false);
28693
28842
  const [filter, setFilter] = d2("");
28843
+ const [busy, setBusy] = d2(null);
28844
+ const [actionError, setActionError] = d2(null);
28694
28845
  const view = q2(async (name) => {
28695
28846
  setOpen({ name, messages: null });
28696
28847
  setOpenLoading(true);
@@ -28703,17 +28854,64 @@ function SessionsPanel() {
28703
28854
  setOpenLoading(false);
28704
28855
  }
28705
28856
  }, []);
28857
+ const newSession = q2(async () => {
28858
+ setBusy("new");
28859
+ setActionError(null);
28860
+ try {
28861
+ await api("/sessions/new", { method: "POST" });
28862
+ setOpen(null);
28863
+ await refresh();
28864
+ } catch (err) {
28865
+ setActionError(err.message);
28866
+ } finally {
28867
+ setBusy(null);
28868
+ }
28869
+ }, [refresh]);
28870
+ const switchTo = q2(
28871
+ async (name) => {
28872
+ setBusy(`switch:${name}`);
28873
+ setActionError(null);
28874
+ try {
28875
+ await api(`/sessions/${encodeURIComponent(name)}/switch`, { method: "POST" });
28876
+ setOpen(null);
28877
+ await refresh();
28878
+ } catch (err) {
28879
+ setActionError(err.message);
28880
+ } finally {
28881
+ setBusy(null);
28882
+ }
28883
+ },
28884
+ [refresh]
28885
+ );
28886
+ const remove = q2(
28887
+ async (name) => {
28888
+ if (!confirm(t4("sessions.deleteConfirm", { name }))) return;
28889
+ setBusy(`delete:${name}`);
28890
+ setActionError(null);
28891
+ try {
28892
+ await api(`/sessions/${encodeURIComponent(name)}`, { method: "DELETE" });
28893
+ if (open?.name === name) setOpen(null);
28894
+ await refresh();
28895
+ } catch (err) {
28896
+ setActionError(err.message);
28897
+ } finally {
28898
+ setBusy(null);
28899
+ }
28900
+ },
28901
+ [open, refresh]
28902
+ );
28706
28903
  if (loading && !data)
28707
28904
  return html4`<div class="card" style="color:var(--fg-3)">${t4("sessions.loading")}</div>`;
28708
- if (error) return html4`<div class="card accent-err">${t4("common.loadingFailed", { name: "sessions", error: error.message })}</div>`;
28905
+ if (error)
28906
+ return html4`<div class="card accent-err">${t4("common.loadingFailed", { name: "sessions", error: error.message })}</div>`;
28709
28907
  const sessions = data?.sessions ?? [];
28710
- if (sessions.length === 0)
28711
- return html4`<div class="card" style="color:var(--fg-3)">${t4("sessions.noSessions")}</div>`;
28908
+ const currentSession = data?.currentSession ?? null;
28909
+ const canSwitch = data?.canSwitch ?? false;
28712
28910
  const filtered = filter.trim() ? sessions.filter((s3) => s3.name.toLowerCase().includes(filter.toLowerCase())) : sessions;
28713
28911
  return html4`
28714
28912
  <div class="sessions-grid">
28715
28913
  <div class="sessions-list">
28716
- <div class="ssl-h">
28914
+ <div class="ssl-h" style="display:flex;gap:6px">
28717
28915
  <input
28718
28916
  type="text"
28719
28917
  placeholder=${t4("sessions.filterPlaceholder")}
@@ -28721,66 +28919,95 @@ function SessionsPanel() {
28721
28919
  onInput=${(e3) => setFilter(e3.target.value)}
28722
28920
  style="flex:1"
28723
28921
  />
28922
+ <button
28923
+ class="btn primary"
28924
+ disabled=${!canSwitch || busy === "new"}
28925
+ title=${canSwitch ? t4("sessions.newHint") : t4("sessions.attachRequired")}
28926
+ onClick=${newSession}
28927
+ >
28928
+ ${busy === "new" ? t4("common.loading") : `+ ${t4("sessions.newBtn")}`}
28929
+ </button>
28724
28930
  </div>
28931
+ ${!canSwitch ? html4`<div style="padding:0 12px 6px;font-size:11.5px;color:var(--fg-3)">${t4("sessions.attachRequired")}</div>` : null}
28932
+ ${actionError ? html4`<div class="card accent-err" style="margin:0 12px 8px;padding:6px 10px;font-size:12px">${actionError}</div>` : null}
28725
28933
  <div class="chips" style="padding:0 12px 8px">
28726
28934
  <span class="chip-f static active">${t4("common.all")} <span class="ct">${sessions.length}</span></span>
28935
+ ${currentSession ? html4`<span class="chip-f static">${t4("sessions.activeChip")} <span class="ct">${currentSession}</span></span>` : null}
28727
28936
  </div>
28728
- <div class="ssl-rows">
28729
- ${filtered.map(
28730
- (s3) => html4`
28731
- <div
28732
- class=${`ssl-row ${open?.name === s3.name ? "sel" : ""}`}
28733
- onClick=${() => view(s3.name)}
28734
- >
28735
- <span class="name">${s3.name}</span>
28736
- <span class="meta">
28737
- <span><span class="v">${fmtNum(s3.messageCount)}</span> ${t4("sessions.msgs")}</span>
28738
- <span><span class="v">${fmtBytes(s3.size)}</span></span>
28739
- <span>${fmtRelativeTime(s3.mtime)}</span>
28740
- </span>
28741
- </div>
28742
- `
28743
- )}
28744
- </div>
28937
+ ${sessions.length === 0 ? html4`<div class="ctx-empty" style="padding:24px 12px;color:var(--fg-3)">${t4("sessions.noSessions")}</div>` : html4`<div class="ssl-rows">
28938
+ ${filtered.map((s3) => {
28939
+ const isCurrent = currentSession === s3.name;
28940
+ return html4`
28941
+ <div
28942
+ class=${`ssl-row ${open?.name === s3.name ? "sel" : ""}`}
28943
+ onClick=${() => view(s3.name)}
28944
+ >
28945
+ <span class="name">
28946
+ ${isCurrent ? html4`<span class="pill ok" style="margin-right:6px">${t4("sessions.activePill")}</span>` : null}
28947
+ ${s3.name}
28948
+ </span>
28949
+ <span class="meta">
28950
+ <span><span class="v">${fmtNum(s3.messageCount)}</span> ${t4("sessions.msgs")}</span>
28951
+ <span><span class="v">${fmtBytes(s3.size)}</span></span>
28952
+ <span>${fmtRelativeTime(s3.mtime)}</span>
28953
+ </span>
28954
+ </div>
28955
+ `;
28956
+ })}
28957
+ </div>`}
28745
28958
  </div>
28746
28959
 
28747
28960
  <div class="sessions-detail">
28748
28961
  ${open == null ? html4`<div style="color:var(--fg-3);font-size:13px;text-align:center;padding:60px 20px">
28749
28962
  ${t4("sessions.pickHint")}
28750
- </div>` : html4`
28963
+ </div>` : (() => {
28964
+ const isCurrent = currentSession === open.name;
28965
+ return html4`
28751
28966
  <div class="sessions-detail-h">
28752
- <span class="name">${open.name}</span>
28967
+ <span class="name">
28968
+ ${isCurrent ? html4`<span class="pill ok" style="margin-right:6px">${t4("sessions.activePill")}</span>` : null}
28969
+ ${open.name}
28970
+ </span>
28753
28971
  <span class="ws">
28754
28972
  ${open.messages ? t4("sessions.messages", { count: open.messages.length, s: open.messages.length === 1 ? "" : "s" }) : t4("common.loading")}
28755
28973
  </span>
28756
28974
  <span class="actions">
28975
+ ${canSwitch && !isCurrent ? html4`<button class="btn primary" disabled=${busy === `switch:${open.name}`} onClick=${() => switchTo(open.name)}>${busy === `switch:${open.name}` ? t4("common.loading") : t4("sessions.switchBtn")}</button>` : null}
28976
+ <button
28977
+ class="btn"
28978
+ disabled=${isCurrent || busy === `delete:${open.name}`}
28979
+ title=${isCurrent ? t4("sessions.cantDeleteActive") : t4("sessions.deleteBtn")}
28980
+ style="border-color:var(--c-err);color:var(--c-err)"
28981
+ onClick=${() => remove(open.name)}
28982
+ >${busy === `delete:${open.name}` ? t4("common.loading") : t4("sessions.deleteBtn")}</button>
28757
28983
  <button class="btn ghost" onClick=${() => setOpen(null)}>${t4("common.back")}</button>
28758
28984
  </span>
28759
28985
  </div>
28760
- <div class="card accent-brand" style="margin-bottom:10px">
28761
- <div class="card-h"><span class="title">${t4("sessions.resumeTitle")}</span></div>
28762
- <div class="card-b" style="font-size:12.5px;color:var(--fg-2)">
28763
- ${t4("sessions.resumeDesc")}
28764
- <code class="mono" style="display:block;margin-top:8px;padding:8px 10px;background:var(--bg-input);border-radius:var(--r);color:var(--fg-0);font-size:12px;user-select:all">reasonix chat --session ${open.name}</code>
28765
- </div>
28766
- </div>
28986
+ ${!canSwitch ? html4`<div class="card accent-brand" style="margin-bottom:10px">
28987
+ <div class="card-h"><span class="title">${t4("sessions.resumeTitle")}</span></div>
28988
+ <div class="card-b" style="font-size:12.5px;color:var(--fg-2)">
28989
+ ${t4("sessions.resumeDesc")}
28990
+ <code class="mono" style="display:block;margin-top:8px;padding:8px 10px;background:var(--bg-input);border-radius:var(--r);color:var(--fg-0);font-size:12px;user-select:all">reasonix chat --session ${open.name}</code>
28991
+ </div>
28992
+ </div>` : null}
28767
28993
  ${openLoading ? html4`<div style="color:var(--fg-3)">${t4("sessions.loadingTranscript")}</div>` : open.error ? html4`<div class="card accent-err">${open.error}</div>` : open.messages && open.messages.length > 0 ? html4`<div class="chat-feed" style="max-height:calc(100vh - 220px);overflow-y:auto">
28768
28994
  ${open.messages.map(
28769
- (m3, i3) => html4`
28995
+ (m3, i3) => html4`
28770
28996
  <${ChatMessage}
28771
28997
  key=${i3}
28772
28998
  msg=${{
28773
- id: `r-${i3}`,
28774
- role: m3.role === "tool" ? "tool" : m3.role === "assistant" ? "assistant" : m3.role === "user" ? "user" : "info",
28775
- text: m3.content ?? "",
28776
- toolName: m3.toolName
28777
- }}
28999
+ id: `r-${i3}`,
29000
+ role: m3.role === "tool" ? "tool" : m3.role === "assistant" ? "assistant" : m3.role === "user" ? "user" : "info",
29001
+ text: m3.content ?? "",
29002
+ toolName: m3.toolName
29003
+ }}
28778
29004
  streaming=${false}
28779
29005
  />
28780
29006
  `
28781
- )}
29007
+ )}
28782
29008
  </div>` : html4`<div style="color:var(--fg-3)">${t4("sessions.emptyTranscript")}</div>`}
28783
- `}
29009
+ `;
29010
+ })()}
28784
29011
  </div>
28785
29012
  </div>
28786
29013
  `;