@xopcai/xopc 0.0.86 → 0.0.87

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 (380) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/adapters/cli-login.js +3 -3
  3. package/dist/extensions/feishu/src/adapters/cli-login.js.map +1 -1
  4. package/dist/extensions/telegram/src/delivery-chat-id.d.ts +1 -1
  5. package/dist/extensions/telegram/src/delivery-chat-id.js +1 -1
  6. package/dist/extensions/telegram/src/delivery-chat-id.js.map +1 -1
  7. package/dist/extensions/telegram/src/routing-integration.js +1 -0
  8. package/dist/extensions/telegram/src/routing-integration.js.map +1 -1
  9. package/dist/extensions/telegram/xopc.extension.json +1 -1
  10. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js +2 -2
  11. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js.map +1 -1
  12. package/dist/extensions/weixin/src/api/api.js +2 -2
  13. package/dist/extensions/weixin/src/api/api.js.map +1 -1
  14. package/dist/extensions/weixin/src/auth/accounts.js +12 -12
  15. package/dist/extensions/weixin/src/auth/accounts.js.map +1 -1
  16. package/dist/extensions/weixin/src/delivery-to.js +2 -2
  17. package/dist/extensions/weixin/src/delivery-to.js.map +1 -1
  18. package/dist/extensions/weixin/src/messaging/debug-mode.js +5 -5
  19. package/dist/extensions/weixin/src/messaging/debug-mode.js.map +1 -1
  20. package/dist/extensions/weixin/src/messaging/inbound.js +11 -11
  21. package/dist/extensions/weixin/src/messaging/inbound.js.map +1 -1
  22. package/dist/extensions/weixin/src/storage/sync-buf.js +4 -4
  23. package/dist/extensions/weixin/src/storage/sync-buf.js.map +1 -1
  24. package/dist/extensions/weixin/src/workflow-progress.d.ts +1 -1
  25. package/dist/extensions/weixin/src/workflow-progress.js.map +1 -1
  26. package/dist/gateway/static/root/assets/{agents-mS3_HpRI.js → agents-BEAbXpuP.js} +6 -6
  27. package/dist/gateway/static/root/assets/{apps-page-DrfytjOb.js → apps-page-Dg8R-Szf.js} +1 -1
  28. package/dist/gateway/static/root/assets/{channels-settings-BG6b9KrW.js → channels-settings-yohw9YSu.js} +1 -1
  29. package/dist/gateway/static/root/assets/{channels-status-swr-Bs5kMCMI.js → channels-status-swr-BSHqqCF1.js} +1 -1
  30. package/dist/gateway/static/root/assets/{cron-api-BuVcZ5zR.js → cron-api-0h_QT8U3.js} +1 -1
  31. package/dist/gateway/static/root/assets/{cron-page-BMrloeFH.js → cron-page-BkfKFfFk.js} +1 -1
  32. package/dist/gateway/static/root/assets/{dist-CKU1OOTf.js → dist-Cmjp2APP.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-debug-page-BdW_46sN.js → extension-debug-page-CFa9z_1N.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-page-DW47KI82.js → extension-page-BI8eaTPq.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-settings-page-B-W4x2xP.js → extension-settings-page-x4BB7q1X.js} +1 -1
  36. package/dist/gateway/static/root/assets/{fetch-B2MYHbWg.js → fetch-DRqwef_Q.js} +1 -1
  37. package/dist/gateway/static/root/assets/{field-primitives-DPG-oJmx.js → field-primitives-BiNHBo2Y.js} +1 -1
  38. package/dist/gateway/static/root/assets/{heartbeat-config-api-C8dNts9i.js → heartbeat-config-api-ZRb8qhuz.js} +1 -1
  39. package/dist/gateway/static/root/assets/{index-BmVYculr.js → index-Cu7bKuUi.js} +96 -94
  40. package/dist/gateway/static/root/assets/index-a5gWIdZQ.css +1 -0
  41. package/dist/gateway/static/root/assets/{logs-page-sTsVWz0X.js → logs-page-BFZ8GgCv.js} +1 -1
  42. package/dist/gateway/static/root/assets/{sessions-page-FaG_Vlkb.js → sessions-page-CD7AfB-2.js} +1 -1
  43. package/dist/gateway/static/root/assets/{settings-form-section-DuvRQW--.js → settings-form-section-DiqqVs6m.js} +1 -1
  44. package/dist/gateway/static/root/assets/{settings-page-Bet1OerL.js → settings-page-BBOjEQW3.js} +1 -1
  45. package/dist/gateway/static/root/assets/{share-preview-page-BtG2kLDh.js → share-preview-page-n1Gprylk.js} +1 -1
  46. package/dist/gateway/static/root/assets/{skills-page-DhUO235y.js → skills-page-CcN_gj--.js} +1 -1
  47. package/dist/gateway/static/root/assets/{theme-store-DryYl3qD.js → theme-store-CZOh1nT3.js} +1 -1
  48. package/dist/gateway/static/root/assets/url-Dd8Q7kZZ.js +3 -0
  49. package/dist/gateway/static/root/assets/{utils-BY7bU1DT.js → utils-CkWBfxs4.js} +1 -1
  50. package/dist/gateway/static/root/assets/{voice-api-key-field-CGEydndO.js → voice-api-key-field-O6awz9hi.js} +1 -1
  51. package/dist/gateway/static/root/index.html +5 -5
  52. package/dist/package.js +1 -1
  53. package/dist/src/agent/agent-scope.d.ts +4 -0
  54. package/dist/src/agent/agent-scope.js +53 -10
  55. package/dist/src/agent/agent-scope.js.map +1 -1
  56. package/dist/src/agent/bootstrap/filter-bootstrap-files.js +2 -1
  57. package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
  58. package/dist/src/agent/embedded/session-tool-result-guard.js +2 -1
  59. package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
  60. package/dist/src/agent/embedded/tool-result-truncation.js +2 -1
  61. package/dist/src/agent/embedded/tool-result-truncation.js.map +1 -1
  62. package/dist/src/agent/fallback/candidates.js +2 -2
  63. package/dist/src/agent/fallback/candidates.js.map +1 -1
  64. package/dist/src/agent/goals/persistent-goal-apis.d.ts +0 -2
  65. package/dist/src/agent/goals/persistent-goal-service.js +0 -1
  66. package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
  67. package/dist/src/agent/image/generation/normalization.js +2 -12
  68. package/dist/src/agent/image/generation/normalization.js.map +1 -1
  69. package/dist/src/agent/image/generation/provider-registry.d.ts +4 -8
  70. package/dist/src/agent/image/generation/provider-registry.js.map +1 -1
  71. package/dist/src/agent/image/generation/runtime.d.ts +2 -2
  72. package/dist/src/agent/image/generation/runtime.js.map +1 -1
  73. package/dist/src/agent/image/generation/types.d.ts +0 -18
  74. package/dist/src/agent/image/image-helpers.js +6 -1
  75. package/dist/src/agent/image/image-helpers.js.map +1 -1
  76. package/dist/src/agent/image/index.d.ts +1 -1
  77. package/dist/src/agent/inbound/inbound-loop.d.ts +5 -0
  78. package/dist/src/agent/inbound/inbound-loop.js +41 -10
  79. package/dist/src/agent/inbound/inbound-loop.js.map +1 -1
  80. package/dist/src/agent/inbound/turn-dispatcher.d.ts +4 -0
  81. package/dist/src/agent/inbound/turn-dispatcher.js +6 -4
  82. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  83. package/dist/src/agent/mcp/bundle-mcp-materialize.js +2 -1
  84. package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
  85. package/dist/src/agent/mcp/bundle-mcp-names.js +2 -1
  86. package/dist/src/agent/mcp/bundle-mcp-names.js.map +1 -1
  87. package/dist/src/agent/mcp/bundle-mcp-runtime.js +2 -1
  88. package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
  89. package/dist/src/agent/mcp/mcp-transport-config.js +2 -1
  90. package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
  91. package/dist/src/agent/mcp/mcp-transport.js +2 -1
  92. package/dist/src/agent/mcp/mcp-transport.js.map +1 -1
  93. package/dist/src/agent/media-generation/runtime-shared.js +2 -9
  94. package/dist/src/agent/media-generation/runtime-shared.js.map +1 -1
  95. package/dist/src/agent/messaging/command-handler.d.ts +6 -0
  96. package/dist/src/agent/messaging/command-handler.js +5 -0
  97. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  98. package/dist/src/agent/prompt/safety.d.ts +0 -7
  99. package/dist/src/agent/prompt/safety.js +1 -20
  100. package/dist/src/agent/prompt/safety.js.map +1 -1
  101. package/dist/src/agent/service/build-direct-message-content.js +1 -1
  102. package/dist/src/agent/service/build-direct-message-content.js.map +1 -1
  103. package/dist/src/agent/service/direct-turn-helpers.d.ts +3 -1
  104. package/dist/src/agent/service/direct-turn-helpers.js +6 -1
  105. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  106. package/dist/src/agent/service/process-direct-one-shot.d.ts +4 -0
  107. package/dist/src/agent/service/process-direct-one-shot.js +15 -2
  108. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  109. package/dist/src/agent/service/process-direct-streaming.d.ts +4 -0
  110. package/dist/src/agent/service/process-direct-streaming.js +34 -4
  111. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  112. package/dist/src/agent/service/webchat-tts.js +1 -1
  113. package/dist/src/agent/service/webchat-tts.js.map +1 -1
  114. package/dist/src/agent/service.d.ts +8 -0
  115. package/dist/src/agent/service.js +21 -1
  116. package/dist/src/agent/service.js.map +1 -1
  117. package/dist/src/agent/tools/create-share-tool.js +27 -20
  118. package/dist/src/agent/tools/create-share-tool.js.map +1 -1
  119. package/dist/src/agent/tools/factory.js +1 -1
  120. package/dist/src/agent/tools/index.d.ts +0 -1
  121. package/dist/src/agent/tools/index.js +4 -5
  122. package/dist/src/agent/tools/shell.js +0 -13
  123. package/dist/src/agent/tools/shell.js.map +1 -1
  124. package/dist/src/agent/tools/workflow-tool.js +7 -1
  125. package/dist/src/agent/tools/workflow-tool.js.map +1 -1
  126. package/dist/src/agent/workflow/channel-capability.d.ts +3 -3
  127. package/dist/src/agent/workflow/lint.d.ts +38 -0
  128. package/dist/src/agent/workflow/lint.js +74 -0
  129. package/dist/src/agent/workflow/lint.js.map +1 -0
  130. package/dist/src/agent/workflow/parser.js +4 -1
  131. package/dist/src/agent/workflow/parser.js.map +1 -1
  132. package/dist/src/agent/workflow/runtime.d.ts +3 -0
  133. package/dist/src/agent/workflow/runtime.js +76 -3
  134. package/dist/src/agent/workflow/runtime.js.map +1 -1
  135. package/dist/src/agent/workflow/types.d.ts +3 -1
  136. package/dist/src/browser/index.js +4 -4
  137. package/dist/src/browser/manager.d.ts +1 -3
  138. package/dist/src/browser/manager.js +0 -6
  139. package/dist/src/browser/manager.js.map +1 -1
  140. package/dist/src/browser/providers/browser-ext-install.d.ts +4 -4
  141. package/dist/src/browser/providers/browser-ext-install.js +38 -85
  142. package/dist/src/browser/providers/browser-ext-install.js.map +1 -1
  143. package/dist/src/browser/providers/cloakbrowser.d.ts +0 -5
  144. package/dist/src/browser/providers/cloakbrowser.js +2 -55
  145. package/dist/src/browser/providers/cloakbrowser.js.map +1 -1
  146. package/dist/src/channels/attachments/voice-stt-webchat.js +10 -8
  147. package/dist/src/channels/attachments/voice-stt-webchat.js.map +1 -1
  148. package/dist/src/channels/pairing/allow-from-file.js +9 -9
  149. package/dist/src/channels/pairing/allow-from-file.js.map +1 -1
  150. package/dist/src/channels/pairing/pairing-store.js +6 -6
  151. package/dist/src/channels/pairing/pairing-store.js.map +1 -1
  152. package/dist/src/chat-commands/builtins/session.js +1 -1
  153. package/dist/src/chat-commands/builtins/session.js.map +1 -1
  154. package/dist/src/chat-commands/builtins/tts.js +2 -2
  155. package/dist/src/chat-commands/builtins/tts.js.map +1 -1
  156. package/dist/src/chat-commands/context.d.ts +3 -0
  157. package/dist/src/chat-commands/context.js +21 -3
  158. package/dist/src/chat-commands/context.js.map +1 -1
  159. package/dist/src/chat-commands/session-key.d.ts +4 -37
  160. package/dist/src/chat-commands/session-key.js +49 -85
  161. package/dist/src/chat-commands/session-key.js.map +1 -1
  162. package/dist/src/chat-commands/types.d.ts +2 -0
  163. package/dist/src/cli/commands/agent/interactive.js +2 -2
  164. package/dist/src/cli/commands/agent/interactive.js.map +1 -1
  165. package/dist/src/cli/commands/agent/sessions.js +2 -2
  166. package/dist/src/cli/commands/agent/sessions.js.map +1 -1
  167. package/dist/src/cli/commands/agent.js +4 -5
  168. package/dist/src/cli/commands/agent.js.map +1 -1
  169. package/dist/src/cli/commands/channels.js +1 -5
  170. package/dist/src/cli/commands/channels.js.map +1 -1
  171. package/dist/src/cli/commands/gateway/lifecycle-core.js +1 -1
  172. package/dist/src/cli/commands/gateway/lifecycle-core.js.map +1 -1
  173. package/dist/src/cli/commands/gateway/logs.d.ts +9 -0
  174. package/dist/src/cli/commands/gateway/logs.js +50 -17
  175. package/dist/src/cli/commands/gateway/logs.js.map +1 -1
  176. package/dist/src/cli/commands/image.js +22 -21
  177. package/dist/src/cli/commands/image.js.map +1 -1
  178. package/dist/src/cli/commands/session/utils.js +2 -2
  179. package/dist/src/cli/commands/session/utils.js.map +1 -1
  180. package/dist/src/cli/commands/update.js +26 -46
  181. package/dist/src/cli/commands/update.js.map +1 -1
  182. package/dist/src/cli/utils/session.d.ts +0 -5
  183. package/dist/src/cli/utils/session.js +1 -6
  184. package/dist/src/cli/utils/session.js.map +1 -1
  185. package/dist/src/commands/agents.config.js +1 -1
  186. package/dist/src/commands/agents.config.js.map +1 -1
  187. package/dist/src/config/agent-profile.js +5 -27
  188. package/dist/src/config/agent-profile.js.map +1 -1
  189. package/dist/src/config/index.js +2 -2
  190. package/dist/src/config/model-input.js +2 -5
  191. package/dist/src/config/model-input.js.map +1 -1
  192. package/dist/src/config/schema.d.ts +201 -217
  193. package/dist/src/config/schema.js +54 -39
  194. package/dist/src/config/schema.js.map +1 -1
  195. package/dist/src/config/workspace-path-helpers.d.ts +1 -2
  196. package/dist/src/config/workspace-path-helpers.js.map +1 -1
  197. package/dist/src/daemon/install-plan.js +25 -1
  198. package/dist/src/daemon/install-plan.js.map +1 -1
  199. package/dist/src/daemon/launchd.d.ts +8 -0
  200. package/dist/src/daemon/launchd.js +5 -12
  201. package/dist/src/daemon/launchd.js.map +1 -1
  202. package/dist/src/daemon/schtasks.d.ts +25 -0
  203. package/dist/src/daemon/schtasks.js +166 -46
  204. package/dist/src/daemon/schtasks.js.map +1 -1
  205. package/dist/src/daemon/service.js +5 -4
  206. package/dist/src/daemon/service.js.map +1 -1
  207. package/dist/src/daemon/systemd.d.ts +6 -0
  208. package/dist/src/daemon/systemd.js +18 -3
  209. package/dist/src/daemon/systemd.js.map +1 -1
  210. package/dist/src/extensions/activation-context.js +0 -1
  211. package/dist/src/extensions/activation-context.js.map +1 -1
  212. package/dist/src/extensions/normalize-manifest.js +0 -1
  213. package/dist/src/extensions/normalize-manifest.js.map +1 -1
  214. package/dist/src/extensions/types/manifest.d.ts +0 -2
  215. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  216. package/dist/src/gateway/agent-builtin-tools.js +1 -0
  217. package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
  218. package/dist/src/gateway/agents-admin.js +10 -2
  219. package/dist/src/gateway/agents-admin.js.map +1 -1
  220. package/dist/src/gateway/heartbeat/service.js +2 -2
  221. package/dist/src/gateway/heartbeat/service.js.map +1 -1
  222. package/dist/src/gateway/hono/app.js +1 -1
  223. package/dist/src/gateway/hono/lib/agent-model.d.ts +18 -10
  224. package/dist/src/gateway/hono/lib/agent-model.js +24 -35
  225. package/dist/src/gateway/hono/lib/agent-model.js.map +1 -1
  226. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  227. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  228. package/dist/src/gateway/hono/lib/safe-voice-config.js +14 -53
  229. package/dist/src/gateway/hono/lib/safe-voice-config.js.map +1 -1
  230. package/dist/src/gateway/hono/routes/config-patch/agents.js +17 -5
  231. package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -1
  232. package/dist/src/gateway/hono/routes/config-patch/channels.js +0 -11
  233. package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -1
  234. package/dist/src/gateway/hono/routes/goals.js +1 -1
  235. package/dist/src/gateway/hono/routes/goals.js.map +1 -1
  236. package/dist/src/gateway/hono/routes/sessions.js +28 -7
  237. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  238. package/dist/src/gateway/hono/routes/shares.js +14 -12
  239. package/dist/src/gateway/hono/routes/shares.js.map +1 -1
  240. package/dist/src/gateway/hono/routes/tunnel.js +1 -1
  241. package/dist/src/gateway/hono/routes/update.js +4 -2
  242. package/dist/src/gateway/hono/routes/update.js.map +1 -1
  243. package/dist/src/gateway/hono/sse.js +16 -33
  244. package/dist/src/gateway/hono/sse.js.map +1 -1
  245. package/dist/src/gateway/lock.js +10 -10
  246. package/dist/src/gateway/lock.js.map +1 -1
  247. package/dist/src/gateway/ports.js +6 -6
  248. package/dist/src/gateway/ports.js.map +1 -1
  249. package/dist/src/gateway/resolve-webchat-session-key.d.ts +19 -0
  250. package/dist/src/gateway/resolve-webchat-session-key.js +46 -0
  251. package/dist/src/gateway/resolve-webchat-session-key.js.map +1 -0
  252. package/dist/src/gateway/service/run-gateway-agent.js +27 -11
  253. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  254. package/dist/src/gateway/service/sessions-api.d.ts +3 -0
  255. package/dist/src/gateway/service/sessions-api.js +8 -0
  256. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  257. package/dist/src/gateway/service.d.ts +0 -2
  258. package/dist/src/gateway/service.js +2 -7
  259. package/dist/src/gateway/service.js.map +1 -1
  260. package/dist/src/gateway/session-reset-service.d.ts +20 -0
  261. package/dist/src/gateway/session-reset-service.js +54 -0
  262. package/dist/src/gateway/session-reset-service.js.map +1 -0
  263. package/dist/src/gateway/startup-readiness.d.ts +1 -1
  264. package/dist/src/gateway/startup-readiness.js +1 -0
  265. package/dist/src/gateway/startup-readiness.js.map +1 -1
  266. package/dist/src/heartbeat/index.js +1 -1
  267. package/dist/src/infra/gateway-processes.js +2 -2
  268. package/dist/src/infra/gateway-processes.js.map +1 -1
  269. package/dist/src/infra/run-command.d.ts +16 -0
  270. package/dist/src/infra/run-command.js +67 -0
  271. package/dist/src/infra/run-command.js.map +1 -0
  272. package/dist/src/infra/update-global.d.ts +45 -0
  273. package/dist/src/infra/update-global.js +224 -0
  274. package/dist/src/infra/update-global.js.map +1 -0
  275. package/dist/src/mcp/channel-bridge.js +1 -1
  276. package/dist/src/mcp/channel-shared.js +2 -1
  277. package/dist/src/mcp/channel-shared.js.map +1 -1
  278. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  279. package/dist/src/providers/auth-runtime/auth-profile-store.js.map +1 -1
  280. package/dist/src/providers/auth-runtime/resolve-auth.js +1 -12
  281. package/dist/src/providers/auth-runtime/resolve-auth.js.map +1 -1
  282. package/dist/src/providers/auth-runtime/types.d.ts +6 -12
  283. package/dist/src/routing/agent-session-key.d.ts +58 -0
  284. package/dist/src/routing/agent-session-key.js +164 -0
  285. package/dist/src/routing/agent-session-key.js.map +1 -0
  286. package/dist/src/routing/index.d.ts +1 -1
  287. package/dist/src/routing/index.js +4 -2
  288. package/dist/src/routing/index.js.map +1 -1
  289. package/dist/src/routing/resolve-route.d.ts +15 -0
  290. package/dist/src/routing/resolve-route.js +41 -20
  291. package/dist/src/routing/resolve-route.js.map +1 -1
  292. package/dist/src/routing/resolve-tui-session-key.d.ts +25 -0
  293. package/dist/src/routing/resolve-tui-session-key.js +54 -0
  294. package/dist/src/routing/resolve-tui-session-key.js.map +1 -0
  295. package/dist/src/routing/session-key-utils.d.ts +24 -0
  296. package/dist/src/routing/session-key-utils.js +92 -0
  297. package/dist/src/routing/session-key-utils.js.map +1 -0
  298. package/dist/src/routing/session-key.d.ts +19 -49
  299. package/dist/src/routing/session-key.js +143 -116
  300. package/dist/src/routing/session-key.js.map +1 -1
  301. package/dist/src/session/index.d.ts +6 -0
  302. package/dist/src/session/index.js +7 -1
  303. package/dist/src/session/init-session-turn.d.ts +30 -0
  304. package/dist/src/session/init-session-turn.js +102 -0
  305. package/dist/src/session/init-session-turn.js.map +1 -0
  306. package/dist/src/session/lifecycle-timestamps.d.ts +8 -0
  307. package/dist/src/session/lifecycle-timestamps.js +16 -0
  308. package/dist/src/session/lifecycle-timestamps.js.map +1 -0
  309. package/dist/src/session/manager.d.ts +7 -1
  310. package/dist/src/session/manager.js +8 -1
  311. package/dist/src/session/manager.js.map +1 -1
  312. package/dist/src/session/parity/transcript-paths.js +2 -2
  313. package/dist/src/session/parity/transcript-paths.js.map +1 -1
  314. package/dist/src/session/parity/xopc-session-disk-entry.d.ts +6 -0
  315. package/dist/src/session/reset-policy.d.ts +32 -0
  316. package/dist/src/session/reset-policy.js +65 -0
  317. package/dist/src/session/reset-policy.js.map +1 -0
  318. package/dist/src/session/reset-triggers.d.ts +20 -0
  319. package/dist/src/session/reset-triggers.js +63 -0
  320. package/dist/src/session/reset-triggers.js.map +1 -0
  321. package/dist/src/session/reset-type.d.ts +12 -0
  322. package/dist/src/session/reset-type.js +25 -0
  323. package/dist/src/session/reset-type.js.map +1 -0
  324. package/dist/src/session/resolve-session.d.ts +30 -0
  325. package/dist/src/session/resolve-session.js +93 -0
  326. package/dist/src/session/resolve-session.js.map +1 -0
  327. package/dist/src/session/session-title.js +3 -2
  328. package/dist/src/session/session-title.js.map +1 -1
  329. package/dist/src/session/store.d.ts +11 -4
  330. package/dist/src/session/store.js +57 -6
  331. package/dist/src/session/store.js.map +1 -1
  332. package/dist/src/session/transcript-events.js +2 -1
  333. package/dist/src/session/transcript-events.js.map +1 -1
  334. package/dist/src/share/share-url.d.ts +33 -0
  335. package/dist/src/share/share-url.js +56 -14
  336. package/dist/src/share/share-url.js.map +1 -1
  337. package/dist/src/tui/backends/embedded-backend.js +4 -9
  338. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  339. package/dist/src/tui/backends/gateway-sse-backend.js +1 -1
  340. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  341. package/dist/src/tui/components/chat-log.js +3 -3
  342. package/dist/src/tui/components/chat-log.js.map +1 -1
  343. package/dist/src/tui/theme.d.ts +0 -2
  344. package/dist/src/tui/theme.js +1 -3
  345. package/dist/src/tui/theme.js.map +1 -1
  346. package/dist/src/tui/tui-commands.d.ts +3 -0
  347. package/dist/src/tui/tui-commands.js +45 -10
  348. package/dist/src/tui/tui-commands.js.map +1 -1
  349. package/dist/src/tui/tui-keybindings-file.js +1 -21
  350. package/dist/src/tui/tui-keybindings-file.js.map +1 -1
  351. package/dist/src/tui/tui-session-actions.d.ts +28 -0
  352. package/dist/src/tui/tui-session-actions.js +88 -0
  353. package/dist/src/tui/tui-session-actions.js.map +1 -0
  354. package/dist/src/tui/tui.js +52 -47
  355. package/dist/src/tui/tui.js.map +1 -1
  356. package/dist/src/utils/string-coerce.d.ts +2 -0
  357. package/dist/src/utils/string-coerce.js +10 -1
  358. package/dist/src/utils/string-coerce.js.map +1 -1
  359. package/dist/src/voice/stt/config-slice.d.ts +2 -5
  360. package/dist/src/voice/stt/config-slice.js +5 -26
  361. package/dist/src/voice/stt/config-slice.js.map +1 -1
  362. package/dist/src/voice/stt/types.d.ts +1 -18
  363. package/dist/src/voice/stt/types.js +4 -2
  364. package/dist/src/voice/stt/types.js.map +1 -1
  365. package/dist/src/voice/tts/config-slice.d.ts +3 -7
  366. package/dist/src/voice/tts/config-slice.js +7 -38
  367. package/dist/src/voice/tts/config-slice.js.map +1 -1
  368. package/dist/src/voice/tts/merge-config.js +2 -48
  369. package/dist/src/voice/tts/merge-config.js.map +1 -1
  370. package/dist/src/voice/tts/providers/alibaba-speech.js +1 -1
  371. package/dist/src/voice/tts/providers/alibaba-speech.js.map +1 -1
  372. package/dist/src/voice/tts/types.d.ts +1 -29
  373. package/dist/src/voice/tts/types.js +19 -17
  374. package/dist/src/voice/tts/types.js.map +1 -1
  375. package/package.json +1 -4
  376. package/dist/gateway/static/root/assets/index-ew_2L2We.css +0 -1
  377. package/dist/gateway/static/root/assets/url-BwNL6Rgk.js +0 -3
  378. package/dist/src/agent/tools/browser-legacy-tools.d.ts +0 -17
  379. package/dist/src/agent/tools/browser-legacy-tools.js +0 -766
  380. package/dist/src/agent/tools/browser-legacy-tools.js.map +0 -1
@@ -1 +1 @@
1
- import{$n as e,Zn as t,er as n,nr as r}from"./index-BmVYculr.js";function i(){return r(`w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 text-sm text-fg`,`placeholder:text-fg-subtle`,n,`dark:border-edge`)}function a(){return r(e,t)}function o(e){return e.split(/[,\n]/).flatMap(e=>{let t=e.trim();return t?[/^-?\d+$/.test(t)?Number(t):t]:[]})}function s(e){return e.map(String).join(`, `)}function c(e){let t=e.accounts?.default?.botToken;return typeof t==`string`?t:``}function l(e){return Object.values(e.accounts??{}).some(e=>typeof e.botToken==`string`&&e.botToken.trim().length>0)}function u(e){return Object.keys(e.accounts??{}).length>0||e.allowFrom.length>0}function d(e){return!!(e.appId?.trim()&&e.appSecret?.trim())||Object.keys(e.accounts??{}).length>0}export{u as a,c,l as i,a as n,s as o,d as r,o as s,i as t};
1
+ import{$n as e,Zn as t,er as n,nr as r}from"./index-Cu7bKuUi.js";function i(){return r(`w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 text-sm text-fg`,`placeholder:text-fg-subtle`,n,`dark:border-edge`)}function a(){return r(e,t)}function o(e){return e.split(/[,\n]/).flatMap(e=>{let t=e.trim();return t?[/^-?\d+$/.test(t)?Number(t):t]:[]})}function s(e){return e.map(String).join(`, `)}function c(e){let t=e.accounts?.default?.botToken;return typeof t==`string`?t:``}function l(e){return Object.values(e.accounts??{}).some(e=>typeof e.botToken==`string`&&e.botToken.trim().length>0)}function u(e){return Object.keys(e.accounts??{}).length>0||e.allowFrom.length>0}function d(e){return!!(e.appId?.trim()&&e.appSecret?.trim())||Object.keys(e.accounts??{}).length>0}export{u as a,c,l as i,a as n,s as o,d as r,o as s,i as t};
@@ -1 +1 @@
1
- import{i as e}from"./rolldown-runtime-aKtaBQYM.js";import{a as t,n,t as r}from"./vendor-react-BOUWij0V.js";import{t as i}from"./url-BwNL6Rgk.js";import{o as a}from"./vendor-swr-BIWyz7Rc.js";import{a as o,n as s}from"./fetch-B2MYHbWg.js";import{Ai as c,Ci as l,Gt as u,Ir as d,Ji as f,O as p,S as m,Ti as h,Vi as g,_n as _,er as v,gn as y,ii as b,k as x,nr as S,oa as C,qi as w,tr as ee,wi as T,zt as E}from"./index-BmVYculr.js";var D=`/api/image/providers`;async function O(){return(await s(i(`/api/image/providers`)))?.payload?.providers??[]}var k=e(t(),1),te=n();function A(){return{apiKey:``,region:``,baseUrl:``,imageBaseUrl:``}}function j(e){return e?.apiKey?`••••••••••••`:``}function M(e,t){let n=(()=>{if(!e||typeof e!=`object`||!(`providersConfig`in e))return;let t=e.providersConfig;if(!(!t||typeof t!=`object`||Array.isArray(t)))return t})(),r={};for(let e of t){let t=n?.[e];r[e]={apiKey:j(t),region:t?.region??``,baseUrl:t?.baseUrl??``,imageBaseUrl:t?.imageBaseUrl??``}}return r}function N(e,t,n){let r=e[n].trim();if(r!==t[n].trim())return r||null}function P(e,t){let n=e.trim(),r=t.trim();if(n!==r&&!(m(n)&&m(r)))return n||(r?null:void 0)}function F(e,t,n){let r={};for(let i of e){let e=t[i]??A(),a=n[i]??A();if(JSON.stringify(e)===JSON.stringify(a))continue;let o={},s=P(e.apiKey,a.apiKey);s!==void 0&&(o.apiKey=s);let c=N(e,a,`region`);c!==void 0&&(o.region=c);let l=N(e,a,`baseUrl`);l!==void 0&&(o.baseUrl=l);let u=N(e,a,`imageBaseUrl`);u!==void 0&&(o.imageBaseUrl=u),Object.keys(o).length>0&&(r[i]=o)}return r}async function I(e){let t=await s(i(`/api/image/providers/${encodeURIComponent(e)}/reveal-api-key`),{method:`POST`,headers:{"Content-Type":`application/json`},body:`{}`});if(!t.ok||!t.payload)throw Error(t.error?.message??`Reveal failed`);return t.payload}async function L(e){Object.keys(e).length!==0&&(await s(i(`/api/config`),{method:`PATCH`,body:JSON.stringify({providersConfig:e})}),await y())}var R={showKey:!1,revealed:void 0,revealLoading:!1,revealErr:null,copied:!1};function z(e,t){switch(t.type){case`reset-reveal`:return{...e,showKey:!1,revealed:void 0,revealErr:null};case`toggle-show`:return{...e,showKey:!e.showKey};case`reveal-start`:return{...e,revealLoading:!0,revealErr:null};case`reveal-success`:return{...e,revealed:t.value,revealLoading:!1,showKey:!0,revealErr:null};case`reveal-error`:return{...e,revealed:null,revealLoading:!1,revealErr:t.message};case`copied`:return{...e,copied:!0};case`clear-copied`:return{...e,copied:!1};case`invalidate-reveal`:return{...e,revealed:void 0,showKey:!1}}}function ne(e){let{value:t,reveal:n,loadFailedLabel:r}=e,[i,a]=(0,k.useReducer)(z,R),o=m(t),s=(0,k.useRef)(n);s.current=n;let c=(0,k.useRef)({masked:o,value:t});!o&&(c.current.masked||c.current.value!==t)&&a({type:`reset-reveal`}),c.current={masked:o,value:t};let l=o&&i.showKey&&typeof i.revealed==`string`?i.revealed:t,d=!o||o&&i.showKey&&typeof i.revealed==`string`?`text`:`password`,f=!o&&t.trim().length>0&&!m(t)||!!i.showKey&&typeof i.revealed==`string`&&i.revealed.length>0,p=(0,k.useCallback)(async()=>{let e=!o&&t.trim()&&!m(t)?t.trim():typeof i.revealed==`string`&&i.revealed.length>0?i.revealed:``;e&&await u(e)&&(a({type:`copied`}),window.setTimeout(()=>a({type:`clear-copied`}),2e3))},[o,i.revealed,t]),h=(0,k.useCallback)(async()=>{if(!o){a({type:`toggle-show`});return}if(i.revealed!==void 0){a({type:`toggle-show`});return}a({type:`reveal-start`});try{a({type:`reveal-success`,value:await s.current()})}catch(e){a({type:`reveal-error`,message:e instanceof Error?e.message:r})}},[r,o,i.revealed]),g=(0,k.useCallback)((e,t)=>{o&&typeof i.revealed==`string`&&i.showKey&&e!==i.revealed&&a({type:`invalidate-reveal`}),t(e)},[o,i.revealed,i.showKey]);return{masked:o,showKey:i.showKey,revealed:i.revealed,revealLoading:i.revealLoading,revealErr:i.revealErr,copied:i.copied,inputValue:l,inputType:d,copyEnabled:f,copyKey:p,toggleEye:h,onInputChange:g}}var B=r();function V(e){let t=(0,te.c)(64),{providerId:n,value:r,onChange:i,labels:a,apiKeyLinks:o,apiKeyLinkLabels:s}=e,u;t[0]===n?u=t[1]:(u=()=>I(n).then(H),t[0]=n,t[1]=u);let d=u,f;t[2]!==a.loadFailed||t[3]!==d||t[4]!==r?(f={value:r,reveal:d,loadFailedLabel:a.loadFailed},t[2]=a.loadFailed,t[3]=d,t[4]=r,t[5]=f):f=t[5];let{masked:p,showKey:m,revealed:_,revealLoading:y,revealErr:C,copied:w,inputValue:ee,inputType:D,copyEnabled:O,copyKey:k,toggleEye:A,onInputChange:j}=ne(f),M=`img-cred-key-${n}`,N;t[6]!==a.apiKeyLabel||t[7]!==M?(N=(0,B.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:M,children:a.apiKeyLabel}),t[6]=a.apiKeyLabel,t[7]=M,t[8]=N):N=t[8];let P;t[9]!==s||t[10]!==o?(P=o.length>0?(0,B.jsx)(`div`,{className:`flex flex-col gap-1`,children:o.map(e=>(0,B.jsxs)(`a`,{href:e.href,target:`_blank`,rel:`noopener noreferrer`,className:`inline-flex w-fit items-center gap-1 text-xs font-medium text-accent-fg hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent`,children:[x(e.kind,s),(0,B.jsx)(h,{className:`size-3`,"aria-hidden":!0})]},`${e.kind}-${e.href}`))}):null,t[9]=s,t[10]=o,t[11]=P):P=t[11];let F;t[12]!==a.maskedHelp||t[13]!==p?(F=p?(0,B.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:a.maskedHelp}):null,t[12]=a.maskedHelp,t[13]=p,t[14]=F):F=t[14];let L=`img-cred-key-${n}`,R;t[15]===Symbol.for(`react.memo_cache_sentinel`)?(R=S(`w-full rounded-lg border border-edge bg-surface-panel py-2 pl-3 pr-24 font-mono text-sm text-fg`,`placeholder:text-fg-subtle`,v),t[15]=R):R=t[15];let z=p?`••••••••`:a.optionalPlaceholder,V;t[16]!==i||t[17]!==j?(V=e=>j(e.target.value,i),t[16]=i,t[17]=j,t[18]=V):V=t[18];let U;t[19]!==D||t[20]!==ee||t[21]!==V||t[22]!==L||t[23]!==z?(U=(0,B.jsx)(`input`,{id:L,type:D,autoComplete:`off`,spellCheck:!1,className:R,value:ee,placeholder:z,onChange:V}),t[19]=D,t[20]=ee,t[21]=V,t[22]=L,t[23]=z,t[24]=U):U=t[24];let W;t[25]!==w||t[26]!==O||t[27]!==k||t[28]!==a.copied||t[29]!==a.copy?(W=O?(0,B.jsx)(`button`,{type:`button`,className:S(`rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg`,E.transition,E.press,E.focusRingPanel),title:w?a.copied:a.copy,"aria-label":w?a.copied:a.copy,onClick:()=>void k(),children:w?(0,B.jsx)(g,{className:`size-4`}):(0,B.jsx)(c,{className:`size-4`})}):null,t[25]=w,t[26]=O,t[27]=k,t[28]=a.copied,t[29]=a.copy,t[30]=W):W=t[30];let G;t[31]===Symbol.for(`react.memo_cache_sentinel`)?(G=S(`rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg disabled:opacity-40`,E.transition,E.press,E.focusRingPanel),t[31]=G):G=t[31];let K=m?a.hide:a.show,q=m?a.hide:a.show,J;t[32]===A?J=t[33]:(J=()=>void A(),t[32]=A,t[33]=J);let re;t[34]!==y||t[35]!==m?(re=y?(0,B.jsx)(b,{className:`size-4 animate-spin`,"aria-hidden":!0}):m?(0,B.jsx)(T,{className:`size-4`,"aria-hidden":!0}):(0,B.jsx)(l,{className:`size-4`,"aria-hidden":!0}),t[34]=y,t[35]=m,t[36]=re):re=t[36];let Y;t[37]!==y||t[38]!==K||t[39]!==q||t[40]!==J||t[41]!==re?(Y=(0,B.jsx)(`button`,{type:`button`,className:G,title:K,"aria-label":q,disabled:y,onClick:J,children:re}),t[37]=y,t[38]=K,t[39]=q,t[40]=J,t[41]=re,t[42]=Y):Y=t[42];let X;t[43]!==W||t[44]!==Y?(X=(0,B.jsxs)(`div`,{className:`absolute right-1 top-1/2 flex -translate-y-1/2 gap-0.5`,children:[W,Y]}),t[43]=W,t[44]=Y,t[45]=X):X=t[45];let ie;t[46]!==U||t[47]!==X?(ie=(0,B.jsxs)(`div`,{className:`relative min-w-0`,children:[U,X]}),t[46]=U,t[47]=X,t[48]=ie):ie=t[48];let Z;t[49]!==a.notInConfigFile||t[50]!==p||t[51]!==C||t[52]!==_||t[53]!==m?(Z=p&&m&&_===null&&!C?(0,B.jsx)(`p`,{className:`text-xs text-amber-700 dark:text-amber-400/90`,children:a.notInConfigFile}):null,t[49]=a.notInConfigFile,t[50]=p,t[51]=C,t[52]=_,t[53]=m,t[54]=Z):Z=t[54];let ae;t[55]===C?ae=t[56]:(ae=C?(0,B.jsx)(`p`,{className:`text-xs text-red-600 dark:text-red-400`,children:C}):null,t[55]=C,t[56]=ae);let oe;return t[57]!==ie||t[58]!==Z||t[59]!==ae||t[60]!==N||t[61]!==P||t[62]!==F?(oe=(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[N,P,F,ie,Z,ae]}),t[57]=ie,t[58]=Z,t[59]=ae,t[60]=N,t[61]=P,t[62]=F,t[63]=oe):oe=t[63],oe}function H(e){return e.apiKey??null}function U(){return S(`w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 text-sm text-fg`,`placeholder:text-fg-subtle`,v)}function W(){return S(U(),`appearance-none bg-[length:1rem] bg-[right_0.5rem_center] bg-no-repeat pr-9`)}var G=`__custom__`;function K(e,t){if(!e.region.trim()&&!e.imageBaseUrl.trim())return``;let n=e.region.trim().toLowerCase();return t.some(e=>e.value===n)?n:G}function q(e,t){let n=e.baseUrl.trim().replace(/\/+$/,``);if(!n)return``;let r=t.map(e=>e.value.replace(/\/+$/,``)).indexOf(n);return r>=0?t[r].value:G}function J(e,t,n){return t===`beijing`?e.dashscopeRegion_beijing:t===`singapore`?e.dashscopeRegion_singapore:t===`us`?e.dashscopeRegion_us:n}function re(e,t){return t===`minimax`?e.minimaxClusterLabel:t===`fal`?e.falQueueBaseLabel:e.baseUrlLabel}function Y(e,t){return t===`minimax`?e.minimaxClusterHint:t===`fal`?e.falQueueBaseHint:null}function X(e,t){return JSON.stringify(e)!==JSON.stringify(t)}function ie(e,t){return t===1?e.modelCountOne:e.modelCountMany.replace(`{count}`,String(t))}function Z(e){let t=(0,te.c)(76),{summaries:n,credDraft:r,credBaseline:i,credDirty:a,credSaving:o,credError:s,credSavedFlash:c,credNoopFlash:l,updateCredRow:u,onDiscardCredentials:f,onSaveCredentials:m,extensionIds:g,showExtensionLinks:_,showImageModelsLink:v,hideActions:y,language:x,apiKeyLinkLabels:T,messages:E}=e,D=y===void 0?!1:y,O;t[0]===n?O=t[1]:(O=n.some(le),t[0]=n,t[1]=O);let j=O,M;t[2]===n?M=t[3]:(M=n.some(ce),t[2]=n,t[3]=M);let N=M,[P,F]=(0,k.useState)(se),[I,L]=(0,k.useState)(oe),R;if(t[4]!==i||t[5]!==r||t[6]!==n){R=new Set;for(let e of n)X(r[e.id]??A(),i[e.id]??A())&&R.add(e.id);t[4]=i,t[5]=r,t[6]=n,t[7]=R}else R=t[7];let z=R,ne;t[8]!==z||t[9]!==I||t[10]!==P?(ne=e=>I.has(e)?!1:P.has(e)?!0:z.has(e),t[8]=z,t[9]=I,t[10]=P,t[11]=ne):ne=t[11];let H=ne,Z;t[12]===H?Z=t[13]:(Z=e=>{if(H(e)){L(t=>new Set(t).add(e)),F(t=>{let n=new Set(t);return n.delete(e),n});return}L(t=>{let n=new Set(t);return n.delete(e),n}),F(t=>new Set(t).add(e))},t[12]=H,t[13]=Z);let ue=Z;if(n.length===0)return null;let de;t[14]===E.credentialsIntro?de=t[15]:(de=(0,B.jsx)(`p`,{children:E.credentialsIntro}),t[14]=E.credentialsIntro,t[15]=de);let fe;t[16]!==j||t[17]!==E.regionHint?(fe=j?(0,B.jsx)(`p`,{className:`text-fg-subtle`,children:E.regionHint}):null,t[16]=j,t[17]=E.regionHint,t[18]=fe):fe=t[18];let Q;t[19]!==N||t[20]!==E.endpointPresetsHint?(Q=N?(0,B.jsx)(`p`,{className:`text-fg-subtle`,children:E.endpointPresetsHint}):null,t[19]=N,t[20]=E.endpointPresetsHint,t[21]=Q):Q=t[21];let pe;t[22]!==v||t[23]!==E.imageModelsLinkTitle||t[24]!==E.openImageModelsPage?(pe=v?(0,B.jsx)(`p`,{children:(0,B.jsx)(C,{to:`/settings/image-models`,className:`font-medium text-accent hover:underline`,title:E.imageModelsLinkTitle,children:E.openImageModelsPage})}):null,t[22]=v,t[23]=E.imageModelsLinkTitle,t[24]=E.openImageModelsPage,t[25]=pe):pe=t[25];let me;t[26]!==de||t[27]!==fe||t[28]!==Q||t[29]!==pe?(me=(0,B.jsxs)(`div`,{className:`flex flex-col gap-1 text-xs leading-relaxed text-fg-muted`,children:[de,fe,Q,pe]}),t[26]=de,t[27]=fe,t[28]=Q,t[29]=pe,t[30]=me):me=t[30];let he;t[31]===s?he=t[32]:(he=s?(0,B.jsx)(`div`,{className:`rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm text-red-700 dark:text-red-300`,children:s}):null,t[31]=s,t[32]=he);let ge;t[33]!==a||t[34]!==l||t[35]!==c||t[36]!==o||t[37]!==D||t[38]!==f||t[39]!==m||t[40]!==E.credentialsNothingToSave||t[41]!==E.credentialsSaved||t[42]!==E.discardCredentials||t[43]!==E.saveCredentials||t[44]!==E.savingCredentials?(ge=D?null:(0,B.jsxs)(`div`,{className:`flex flex-wrap items-center justify-end gap-2`,children:[c?(0,B.jsx)(`span`,{className:`text-sm text-fg-muted`,children:E.credentialsSaved}):null,l?(0,B.jsx)(`span`,{className:`text-sm text-fg-muted`,children:E.credentialsNothingToSave}):null,(0,B.jsx)(ee,{type:`button`,variant:`secondary`,onClick:f,disabled:!a||o,children:E.discardCredentials}),(0,B.jsx)(ee,{type:`button`,variant:`primary`,onClick:m,disabled:!a||o,children:o?(0,B.jsxs)(B.Fragment,{children:[(0,B.jsx)(b,{className:`size-3.5 animate-spin`}),(0,B.jsx)(`span`,{className:`ml-1.5`,children:E.savingCredentials})]}):(0,B.jsxs)(B.Fragment,{children:[(0,B.jsx)(d,{className:`size-3.5`}),(0,B.jsx)(`span`,{className:`ml-1.5`,children:E.saveCredentials})]})})]}),t[33]=a,t[34]=l,t[35]=c,t[36]=o,t[37]=D,t[38]=f,t[39]=m,t[40]=E.credentialsNothingToSave,t[41]=E.credentialsSaved,t[42]=E.discardCredentials,t[43]=E.saveCredentials,t[44]=E.savingCredentials,t[45]=ge):ge=t[45];let $;if(t[46]!==T||t[47]!==i||t[48]!==r||t[49]!==g||t[50]!==H||t[51]!==x||t[52]!==_||t[53]!==n||t[54]!==E||t[55]!==ue||t[56]!==u){let e;t[58]!==T||t[59]!==i||t[60]!==r||t[61]!==g||t[62]!==H||t[63]!==x||t[64]!==_||t[65]!==E||t[66]!==ue||t[67]!==u?(e=e=>{let t=r[e.id]??A(),n=e.ui,a=_&&g.has(e.id)?`/settings/ext/${encodeURIComponent(e.id)}`:null,o=X(t,i[e.id]??A()),s=H(e.id),c=e.label??e.id,l=ie(E,e.models.length);return(0,B.jsxs)(`section`,{className:S(`overflow-hidden rounded-xl border bg-surface-panel shadow-sm transition-colors dark:shadow-none`,o?`border-accent/50`:`border-edge`),children:[(0,B.jsxs)(`button`,{type:`button`,className:`flex w-full items-start justify-between gap-3 px-4 py-3 text-left hover:bg-surface-hover/70`,"aria-expanded":s,"aria-controls":`img-provider-credentials-${e.id}`,onClick:()=>ue(e.id),children:[(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-1 flex-col gap-2`,children:[(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-2`,children:[(0,B.jsx)(`span`,{className:`truncate text-sm font-semibold text-fg`,children:c}),(0,B.jsx)(`span`,{className:`rounded-full bg-surface-subtle px-2 py-0.5 text-[11px] font-medium text-fg-muted`,children:e.id}),o?(0,B.jsx)(`span`,{className:`rounded-full bg-accent-soft px-2 py-0.5 text-[11px] font-medium text-accent-fg`,children:E.unsavedChanges}):null,e.configured?(0,B.jsx)(`span`,{className:`rounded-full bg-accent-soft px-2 py-0.5 text-[11px] font-medium text-accent-fg`,children:E.configured}):(0,B.jsx)(`span`,{className:`rounded-full border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-[11px] font-medium text-amber-700 dark:text-amber-300`,children:E.missingKey})]}),(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1 text-xs text-fg-subtle`,children:[(0,B.jsx)(`span`,{children:l}),e.defaultModel?(0,B.jsxs)(`span`,{className:`min-w-0 truncate`,children:[(0,B.jsxs)(`span`,{className:`text-fg-muted`,children:[E.defaultModel,`:`]}),` `,e.id,`/`,e.defaultModel]}):null]})]}),(0,B.jsx)(w,{className:S(`mt-0.5 size-4 shrink-0 text-fg-subtle transition-transform`,s?`rotate-180`:`rotate-0`),"aria-hidden":`true`}),(0,B.jsx)(`span`,{className:`sr-only`,children:s?E.collapseProvider:E.expandProvider})]}),s?(0,B.jsxs)(`div`,{id:`img-provider-credentials-${e.id}`,className:`border-t border-edge px-4 py-4`,children:[a?(0,B.jsxs)(C,{to:a,className:`mb-3 inline-flex items-center gap-1 text-xs font-medium text-accent hover:underline`,title:E.extensionSettingsLinkTitle,children:[(0,B.jsx)(h,{className:`size-3`}),E.openExtensionSettings]}):null,e.models.length>0?(0,B.jsxs)(`p`,{className:`mb-3 text-xs leading-relaxed text-fg-subtle`,children:[(0,B.jsxs)(`span`,{className:`text-fg-muted`,children:[E.modelsLabel,`:`]}),` `,e.models.map(t=>`${e.id}/${t}`).join(`, `)]}):null,(0,B.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,B.jsx)(V,{providerId:e.id,value:t.apiKey,onChange:t=>u(e.id,{apiKey:t}),apiKeyLinks:p(e.id,x),apiKeyLinkLabels:T,labels:{apiKeyLabel:E.apiKeyLabel,optionalPlaceholder:E.optionalPlaceholder,maskedHelp:E.apiKeyMaskedHelp,copy:E.apiKeyCopy,copied:E.apiKeyCopied,show:E.apiKeyShow,hide:E.apiKeyHide,notInConfigFile:E.apiKeyNotInConfigFile,loadFailed:E.apiKeyRevealFailed}}),n?.regions?.length?(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,B.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-region-preset-${e.id}`,children:E.regionLabel}),(0,B.jsxs)(`select`,{id:`img-cred-region-preset-${e.id}`,className:W(),value:K(t,n.regions),onChange:t=>{let r=t.target.value;if(r===``){u(e.id,{region:``,imageBaseUrl:``});return}if(r===G){u(e.id,{region:``,imageBaseUrl:``});return}let i=n.regions.find(e=>e.value===r);i&&u(e.id,{region:i.value,imageBaseUrl:i.imageBaseUrl})},children:[(0,B.jsx)(`option`,{value:``,children:E.regionPresetDefault}),n.regions.map(e=>(0,B.jsx)(`option`,{value:e.value,children:J(E,e.value,e.label)},e.value)),(0,B.jsx)(`option`,{value:G,children:E.regionPresetCustom})]}),K(t,n.regions)===G?(0,B.jsxs)(`div`,{className:`mt-2 grid gap-2 sm:grid-cols-2`,children:[(0,B.jsx)(`input`,{type:`text`,className:U(),value:t.region,placeholder:`region`,onChange:t=>u(e.id,{region:t.target.value})}),(0,B.jsx)(`input`,{type:`url`,className:U(),value:t.imageBaseUrl,placeholder:E.imageBaseUrlLabel,onChange:t=>u(e.id,{imageBaseUrl:t.target.value})})]}):null]}):null,n?.baseUrlPresets?.length?(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,B.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-base-preset-${e.id}`,children:re(E,n.baseUrlPresetKind)}),Y(E,n.baseUrlPresetKind)?(0,B.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:Y(E,n.baseUrlPresetKind)}):null,(0,B.jsxs)(`select`,{id:`img-cred-base-preset-${e.id}`,className:W(),value:q(t,n.baseUrlPresets),onChange:t=>{let n=t.target.value;if(n===``){u(e.id,{baseUrl:``});return}if(n===G){u(e.id,{baseUrl:``});return}u(e.id,{baseUrl:n.replace(/\/+$/,``)})},children:[(0,B.jsx)(`option`,{value:``,children:E.baseUrlPresetDefault}),n.baseUrlPresets.map(ae),(0,B.jsx)(`option`,{value:G,children:E.baseUrlPresetCustom})]}),q(t,n.baseUrlPresets)===G?(0,B.jsx)(`input`,{type:`url`,className:S(U(),`mt-2`),value:t.baseUrl,placeholder:`https://…`,onChange:t=>u(e.id,{baseUrl:t.target.value})}):null]}):null,n?.regions?.length&&K(t,n.regions)!==G?(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,B.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-imgbase-ro-${e.id}`,children:E.imageBaseUrlLabel}),(0,B.jsx)(`input`,{id:`img-cred-imgbase-ro-${e.id}`,type:`url`,readOnly:!0,className:S(U(),`cursor-not-allowed opacity-90`),value:t.imageBaseUrl,title:E.imageBaseUrlPresetHint}),(0,B.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:E.imageBaseUrlPresetHint})]}):null]})]}):null]},e.id)},t[58]=T,t[59]=i,t[60]=r,t[61]=g,t[62]=H,t[63]=x,t[64]=_,t[65]=E,t[66]=ue,t[67]=u,t[68]=e):e=t[68],$=n.map(e),t[46]=T,t[47]=i,t[48]=r,t[49]=g,t[50]=H,t[51]=x,t[52]=_,t[53]=n,t[54]=E,t[55]=ue,t[56]=u,t[57]=$}else $=t[57];let _e;t[69]===$?_e=t[70]:(_e=(0,B.jsx)(`div`,{className:`flex flex-col gap-4`,children:$}),t[69]=$,t[70]=_e);let ve;return t[71]!==me||t[72]!==he||t[73]!==ge||t[74]!==_e?(ve=(0,B.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[me,he,ge,_e]}),t[71]=me,t[72]=he,t[73]=ge,t[74]=_e,t[75]=ve):ve=t[75],ve}function ae(e){return(0,B.jsx)(`option`,{value:e.value,children:e.label},e.value)}function oe(){return new Set}function se(){return new Set}function ce(e){return(e.ui?.baseUrlPresets?.length??0)>0}function le(e){return(e.ui?.regions?.length??0)>0}function ue(e){let t=_(o(e=>!!e.token)),n=t.data,r=(0,k.useMemo)(()=>e.map(e=>e.id),[e]),s=(0,k.useRef)(!1),[c,l]=(0,k.useState)(null),[u,d]=(0,k.useState)(!1),[f,p]=(0,k.useState)(null),[m,h]=(0,k.useState)(!1),[g,v]=(0,k.useState)(!1),y=(0,k.useMemo)(()=>M(n?.payload?.config,r),[n?.payload?.config,r]),b=c??y,x=y;return{gwSwr:t,credDraft:b,credBaseline:x,credDirty:(0,k.useMemo)(()=>JSON.stringify(b)!==JSON.stringify(x),[b,x]),credSaving:u,credError:f,credSavedFlash:m,credNoopFlash:g,updateCredRow:(0,k.useCallback)((e,t)=>{s.current=!0,l(n=>{let r=n??y,i=r[e]??A();return{...r,[e]:{...i,...t}}})},[y]),onDiscardCredentials:(0,k.useCallback)(()=>{s.current=!1,l(null),p(null),h(!1),v(!1)},[]),saveCredentials:(0,k.useCallback)(async e=>{let n=F(r,b,x);if(Object.keys(n).length===0){v(!0),window.setTimeout(()=>v(!1),2200);return}d(!0),p(null),h(!1);try{await L(n),await t.mutate?.(),a(i(D)),s.current=!1,l(null),h(!0),window.setTimeout(()=>h(!1),2e3)}catch(t){p(t instanceof Error?t.message:e)}finally{d(!1)}},[r,b,x,t])}}function de(){return{enabled:!1,provider:`alibaba`,alibaba:{model:`paraformer-v2`},openai:{model:`whisper-1`},fallback:{enabled:!0,order:[`alibaba`,`openai`]}}}function fe(){return{enabled:!1,provider:`openai`,trigger:`always`,maxTextLength:512,timeoutMs:6e4,alibaba:{model:`qwen-tts`,voice:`Cherry`},openai:{model:`tts-1`,voice:`alloy`},edge:{voice:`en-US-MichelleNeural`},minimax:{model:`speech-2.8-hd`,voice:`male-qn-qingse`},"tts-local-cli":{command:``,outputFormat:`wav`}}}function Q(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function pe(e){return typeof e==`string`&&e.trim().length>0?e.trim():`alibaba`}function me(e){let t=de();if(!Q(e))return t;let n=pe(e.provider),r=Q(e.alibaba)?{...t.alibaba,...e.alibaba}:t.alibaba,i=Q(e.openai)?{...t.openai,...e.openai}:t.openai,a=t.fallback??{enabled:!0,order:[`alibaba`,`openai`]};if(Q(e.fallback)){let t=Array.isArray(e.fallback.order)?e.fallback.order.filter(e=>typeof e==`string`&&e.trim().length>0):a.order;a={enabled:typeof e.fallback.enabled==`boolean`?e.fallback.enabled:a.enabled,order:t.length?t:a.order}}let o=Q(e.providers)?Object.fromEntries(Object.entries(e.providers).map(([e,t])=>[e,Q(t)?{...t}:{}])):void 0;return{enabled:!!e.enabled,provider:n,alibaba:r,openai:i,...o?{providers:o}:{},fallback:a}}function he(e){return typeof e==`string`&&e.trim().length>0?e.trim():`openai`}function ge(e){let t=fe();if(!Q(e))return t;let n=he(e.provider),r=e.trigger===`off`||e.trigger===`always`||e.trigger===`inbound`||e.trigger===`tagged`?e.trigger:`always`,i=e[`tts-local-cli`];return{enabled:!!e.enabled,provider:n,trigger:r,maxTextLength:typeof e.maxTextLength==`number`&&Number.isFinite(e.maxTextLength)?e.maxTextLength:t.maxTextLength,timeoutMs:typeof e.timeoutMs==`number`&&Number.isFinite(e.timeoutMs)?e.timeoutMs:t.timeoutMs,alibaba:Q(e.alibaba)?{...t.alibaba,...e.alibaba}:t.alibaba,openai:Q(e.openai)?{...t.openai,...e.openai}:t.openai,edge:Q(e.edge)?{...t.edge,...e.edge}:t.edge,minimax:Q(e.minimax)?{...t.minimax,...e.minimax}:t.minimax,"tts-local-cli":Q(i)?{...t[`tts-local-cli`],...i}:t[`tts-local-cli`]}}function $(e){let t=Q(e)?e:{};return{stt:me(t.stt),tts:ge(t.tts)}}async function _e(){return $((await s(i(`/api/config`))).payload?.config??{})}async function ve(e){await s(i(`/api/config`),{method:`PATCH`,body:JSON.stringify({stt:e.stt,tts:e.tts})}),y()}async function ye(){let e=await s(i(`/api/voice/models`));if(!e.payload?.models)throw Error(`Missing voice models payload`);return e.payload.models}async function be(){let e=await s(i(`/api/voice/providers`));if(!e.payload?.providers)throw Error(`Missing voice providers payload`);return e.payload}async function xe(){let e=await s(i(`/api/voice/stt-providers`));if(!e.payload?.providers)throw Error(`Missing STT providers payload`);return e.payload}async function Se(e){let t=await s(i(`/api/voice/reveal-api-key`),{method:`POST`,body:JSON.stringify(e)});if(!t.payload)throw Error(`Missing reveal payload`);return t.payload}function Ce(e){let t=(0,te.c)(56),{kind:n,providerId:r,fieldId:i,value:a,onChange:o,labels:s,placeholder:u,className:d}=e,p=u===void 0?`sk-...`:u,m;t[0]!==n||t[1]!==r?(m=()=>Se({kind:n,provider:r}).then(we),t[0]=n,t[1]=r,t[2]=m):m=t[2];let h=m,g;t[3]!==s.loadFailed||t[4]!==h||t[5]!==a?(g={value:a,reveal:h,loadFailedLabel:s.loadFailed},t[3]=s.loadFailed,t[4]=h,t[5]=a,t[6]=g):g=t[6];let{masked:_,showKey:y,revealed:x,revealLoading:C,revealErr:w,copied:E,inputValue:D,inputType:O,copyEnabled:k,copyKey:A,toggleEye:j,onInputChange:M}=ne(g),N;t[7]===d?N=t[8]:(N=S(`flex min-w-0 flex-col gap-1.5`,d),t[7]=d,t[8]=N);let P;t[9]!==s.maskedHelp||t[10]!==_?(P=_?(0,B.jsx)(`p`,{className:`text-xs text-fg-subtle`,children:s.maskedHelp}):null,t[9]=s.maskedHelp,t[10]=_,t[11]=P):P=t[11];let F;t[12]===Symbol.for(`react.memo_cache_sentinel`)?(F=S(`w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 text-fg`,`placeholder:text-fg-subtle`,v,`dark:border-edge`,`min-w-0 flex-1 font-mono text-xs`),t[12]=F):F=t[12];let I=_?`••••••••`:p,L;t[13]!==o||t[14]!==M?(L=e=>M(e.target.value,o),t[13]=o,t[14]=M,t[15]=L):L=t[15];let R;t[16]!==i||t[17]!==O||t[18]!==D||t[19]!==I||t[20]!==L?(R=(0,B.jsx)(`input`,{id:i,type:O,autoComplete:`off`,spellCheck:!1,className:F,value:D,placeholder:I,onChange:L}),t[16]=i,t[17]=O,t[18]=D,t[19]=I,t[20]=L,t[21]=R):R=t[21];let z;t[22]!==E||t[23]!==k||t[24]!==A||t[25]!==s.copied||t[26]!==s.copy?(z=k?(0,B.jsxs)(ee,{type:`button`,variant:`secondary`,className:`px-2 py-1 text-xs`,onClick:()=>void A(),children:[E?(0,B.jsx)(f,{className:`size-3.5`}):(0,B.jsx)(c,{className:`size-3.5`}),E?s.copied:s.copy]}):null,t[22]=E,t[23]=k,t[24]=A,t[25]=s.copied,t[26]=s.copy,t[27]=z):z=t[27];let V;t[28]===j?V=t[29]:(V=()=>void j(),t[28]=j,t[29]=V);let H;t[30]!==C||t[31]!==y?(H=C?(0,B.jsx)(b,{className:`size-3.5 animate-spin`,"aria-hidden":!0}):y?(0,B.jsx)(T,{className:`size-3.5`}):(0,B.jsx)(l,{className:`size-3.5`}),t[30]=C,t[31]=y,t[32]=H):H=t[32];let U=y?s.hide:s.show,W;t[33]!==C||t[34]!==V||t[35]!==H||t[36]!==U?(W=(0,B.jsxs)(ee,{type:`button`,variant:`secondary`,className:`px-2 py-1 text-xs`,disabled:C,onClick:V,children:[H,U]}),t[33]=C,t[34]=V,t[35]=H,t[36]=U,t[37]=W):W=t[37];let G;t[38]!==z||t[39]!==W||t[40]!==R?(G=(0,B.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[R,z,W]}),t[38]=z,t[39]=W,t[40]=R,t[41]=G):G=t[41];let K;t[42]!==s.notInConfigFile||t[43]!==_||t[44]!==w||t[45]!==x||t[46]!==y?(K=_&&y&&x===null&&!w?(0,B.jsx)(`p`,{className:`text-xs text-amber-700 dark:text-amber-400/90`,children:s.notInConfigFile}):null,t[42]=s.notInConfigFile,t[43]=_,t[44]=w,t[45]=x,t[46]=y,t[47]=K):K=t[47];let q;t[48]===w?q=t[49]:(q=w?(0,B.jsx)(`p`,{className:`text-xs text-red-600 dark:text-red-400`,children:w}):null,t[48]=w,t[49]=q);let J;return t[50]!==G||t[51]!==K||t[52]!==q||t[53]!==N||t[54]!==P?(J=(0,B.jsxs)(`div`,{className:N,children:[P,G,K,q]}),t[50]=G,t[51]=K,t[52]=q,t[53]=N,t[54]=P,t[55]=J):J=t[55],J}function we(e){return e.apiKey??null}export{xe as a,ue as c,D as d,_e as i,Z as l,ye as n,$ as o,be as r,ve as s,Ce as t,O as u};
1
+ import{i as e}from"./rolldown-runtime-aKtaBQYM.js";import{a as t,n,t as r}from"./vendor-react-BOUWij0V.js";import{t as i}from"./url-Dd8Q7kZZ.js";import{o as a}from"./vendor-swr-BIWyz7Rc.js";import{a as o,n as s}from"./fetch-DRqwef_Q.js";import{Ai as c,Ci as l,Gt as u,Ir as d,Ji as f,O as p,S as m,Ti as h,Vi as g,_n as _,er as v,gn as y,ii as b,k as x,nr as S,oa as C,qi as w,tr as ee,wi as T,zt as E}from"./index-Cu7bKuUi.js";var D=`/api/image/providers`;async function O(){return(await s(i(`/api/image/providers`)))?.payload?.providers??[]}var k=e(t(),1),te=n();function A(){return{apiKey:``,region:``,baseUrl:``,imageBaseUrl:``}}function j(e){return e?.apiKey?`••••••••••••`:``}function M(e,t){let n=(()=>{if(!e||typeof e!=`object`||!(`providersConfig`in e))return;let t=e.providersConfig;if(!(!t||typeof t!=`object`||Array.isArray(t)))return t})(),r={};for(let e of t){let t=n?.[e];r[e]={apiKey:j(t),region:t?.region??``,baseUrl:t?.baseUrl??``,imageBaseUrl:t?.imageBaseUrl??``}}return r}function N(e,t,n){let r=e[n].trim();if(r!==t[n].trim())return r||null}function P(e,t){let n=e.trim(),r=t.trim();if(n!==r&&!(m(n)&&m(r)))return n||(r?null:void 0)}function F(e,t,n){let r={};for(let i of e){let e=t[i]??A(),a=n[i]??A();if(JSON.stringify(e)===JSON.stringify(a))continue;let o={},s=P(e.apiKey,a.apiKey);s!==void 0&&(o.apiKey=s);let c=N(e,a,`region`);c!==void 0&&(o.region=c);let l=N(e,a,`baseUrl`);l!==void 0&&(o.baseUrl=l);let u=N(e,a,`imageBaseUrl`);u!==void 0&&(o.imageBaseUrl=u),Object.keys(o).length>0&&(r[i]=o)}return r}async function I(e){let t=await s(i(`/api/image/providers/${encodeURIComponent(e)}/reveal-api-key`),{method:`POST`,headers:{"Content-Type":`application/json`},body:`{}`});if(!t.ok||!t.payload)throw Error(t.error?.message??`Reveal failed`);return t.payload}async function L(e){Object.keys(e).length!==0&&(await s(i(`/api/config`),{method:`PATCH`,body:JSON.stringify({providersConfig:e})}),await y())}var R={showKey:!1,revealed:void 0,revealLoading:!1,revealErr:null,copied:!1};function z(e,t){switch(t.type){case`reset-reveal`:return{...e,showKey:!1,revealed:void 0,revealErr:null};case`toggle-show`:return{...e,showKey:!e.showKey};case`reveal-start`:return{...e,revealLoading:!0,revealErr:null};case`reveal-success`:return{...e,revealed:t.value,revealLoading:!1,showKey:!0,revealErr:null};case`reveal-error`:return{...e,revealed:null,revealLoading:!1,revealErr:t.message};case`copied`:return{...e,copied:!0};case`clear-copied`:return{...e,copied:!1};case`invalidate-reveal`:return{...e,revealed:void 0,showKey:!1}}}function ne(e){let{value:t,reveal:n,loadFailedLabel:r}=e,[i,a]=(0,k.useReducer)(z,R),o=m(t),s=(0,k.useRef)(n);s.current=n;let c=(0,k.useRef)({masked:o,value:t});!o&&(c.current.masked||c.current.value!==t)&&a({type:`reset-reveal`}),c.current={masked:o,value:t};let l=o&&i.showKey&&typeof i.revealed==`string`?i.revealed:t,d=!o||o&&i.showKey&&typeof i.revealed==`string`?`text`:`password`,f=!o&&t.trim().length>0&&!m(t)||!!i.showKey&&typeof i.revealed==`string`&&i.revealed.length>0,p=(0,k.useCallback)(async()=>{let e=!o&&t.trim()&&!m(t)?t.trim():typeof i.revealed==`string`&&i.revealed.length>0?i.revealed:``;e&&await u(e)&&(a({type:`copied`}),window.setTimeout(()=>a({type:`clear-copied`}),2e3))},[o,i.revealed,t]),h=(0,k.useCallback)(async()=>{if(!o){a({type:`toggle-show`});return}if(i.revealed!==void 0){a({type:`toggle-show`});return}a({type:`reveal-start`});try{a({type:`reveal-success`,value:await s.current()})}catch(e){a({type:`reveal-error`,message:e instanceof Error?e.message:r})}},[r,o,i.revealed]),g=(0,k.useCallback)((e,t)=>{o&&typeof i.revealed==`string`&&i.showKey&&e!==i.revealed&&a({type:`invalidate-reveal`}),t(e)},[o,i.revealed,i.showKey]);return{masked:o,showKey:i.showKey,revealed:i.revealed,revealLoading:i.revealLoading,revealErr:i.revealErr,copied:i.copied,inputValue:l,inputType:d,copyEnabled:f,copyKey:p,toggleEye:h,onInputChange:g}}var B=r();function V(e){let t=(0,te.c)(64),{providerId:n,value:r,onChange:i,labels:a,apiKeyLinks:o,apiKeyLinkLabels:s}=e,u;t[0]===n?u=t[1]:(u=()=>I(n).then(H),t[0]=n,t[1]=u);let d=u,f;t[2]!==a.loadFailed||t[3]!==d||t[4]!==r?(f={value:r,reveal:d,loadFailedLabel:a.loadFailed},t[2]=a.loadFailed,t[3]=d,t[4]=r,t[5]=f):f=t[5];let{masked:p,showKey:m,revealed:_,revealLoading:y,revealErr:C,copied:w,inputValue:ee,inputType:D,copyEnabled:O,copyKey:k,toggleEye:A,onInputChange:j}=ne(f),M=`img-cred-key-${n}`,N;t[6]!==a.apiKeyLabel||t[7]!==M?(N=(0,B.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:M,children:a.apiKeyLabel}),t[6]=a.apiKeyLabel,t[7]=M,t[8]=N):N=t[8];let P;t[9]!==s||t[10]!==o?(P=o.length>0?(0,B.jsx)(`div`,{className:`flex flex-col gap-1`,children:o.map(e=>(0,B.jsxs)(`a`,{href:e.href,target:`_blank`,rel:`noopener noreferrer`,className:`inline-flex w-fit items-center gap-1 text-xs font-medium text-accent-fg hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent`,children:[x(e.kind,s),(0,B.jsx)(h,{className:`size-3`,"aria-hidden":!0})]},`${e.kind}-${e.href}`))}):null,t[9]=s,t[10]=o,t[11]=P):P=t[11];let F;t[12]!==a.maskedHelp||t[13]!==p?(F=p?(0,B.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:a.maskedHelp}):null,t[12]=a.maskedHelp,t[13]=p,t[14]=F):F=t[14];let L=`img-cred-key-${n}`,R;t[15]===Symbol.for(`react.memo_cache_sentinel`)?(R=S(`w-full rounded-lg border border-edge bg-surface-panel py-2 pl-3 pr-24 font-mono text-sm text-fg`,`placeholder:text-fg-subtle`,v),t[15]=R):R=t[15];let z=p?`••••••••`:a.optionalPlaceholder,V;t[16]!==i||t[17]!==j?(V=e=>j(e.target.value,i),t[16]=i,t[17]=j,t[18]=V):V=t[18];let U;t[19]!==D||t[20]!==ee||t[21]!==V||t[22]!==L||t[23]!==z?(U=(0,B.jsx)(`input`,{id:L,type:D,autoComplete:`off`,spellCheck:!1,className:R,value:ee,placeholder:z,onChange:V}),t[19]=D,t[20]=ee,t[21]=V,t[22]=L,t[23]=z,t[24]=U):U=t[24];let W;t[25]!==w||t[26]!==O||t[27]!==k||t[28]!==a.copied||t[29]!==a.copy?(W=O?(0,B.jsx)(`button`,{type:`button`,className:S(`rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg`,E.transition,E.press,E.focusRingPanel),title:w?a.copied:a.copy,"aria-label":w?a.copied:a.copy,onClick:()=>void k(),children:w?(0,B.jsx)(g,{className:`size-4`}):(0,B.jsx)(c,{className:`size-4`})}):null,t[25]=w,t[26]=O,t[27]=k,t[28]=a.copied,t[29]=a.copy,t[30]=W):W=t[30];let G;t[31]===Symbol.for(`react.memo_cache_sentinel`)?(G=S(`rounded p-1.5 text-fg-subtle hover:bg-surface-hover hover:text-fg disabled:opacity-40`,E.transition,E.press,E.focusRingPanel),t[31]=G):G=t[31];let K=m?a.hide:a.show,q=m?a.hide:a.show,J;t[32]===A?J=t[33]:(J=()=>void A(),t[32]=A,t[33]=J);let re;t[34]!==y||t[35]!==m?(re=y?(0,B.jsx)(b,{className:`size-4 animate-spin`,"aria-hidden":!0}):m?(0,B.jsx)(T,{className:`size-4`,"aria-hidden":!0}):(0,B.jsx)(l,{className:`size-4`,"aria-hidden":!0}),t[34]=y,t[35]=m,t[36]=re):re=t[36];let Y;t[37]!==y||t[38]!==K||t[39]!==q||t[40]!==J||t[41]!==re?(Y=(0,B.jsx)(`button`,{type:`button`,className:G,title:K,"aria-label":q,disabled:y,onClick:J,children:re}),t[37]=y,t[38]=K,t[39]=q,t[40]=J,t[41]=re,t[42]=Y):Y=t[42];let X;t[43]!==W||t[44]!==Y?(X=(0,B.jsxs)(`div`,{className:`absolute right-1 top-1/2 flex -translate-y-1/2 gap-0.5`,children:[W,Y]}),t[43]=W,t[44]=Y,t[45]=X):X=t[45];let ie;t[46]!==U||t[47]!==X?(ie=(0,B.jsxs)(`div`,{className:`relative min-w-0`,children:[U,X]}),t[46]=U,t[47]=X,t[48]=ie):ie=t[48];let Z;t[49]!==a.notInConfigFile||t[50]!==p||t[51]!==C||t[52]!==_||t[53]!==m?(Z=p&&m&&_===null&&!C?(0,B.jsx)(`p`,{className:`text-xs text-amber-700 dark:text-amber-400/90`,children:a.notInConfigFile}):null,t[49]=a.notInConfigFile,t[50]=p,t[51]=C,t[52]=_,t[53]=m,t[54]=Z):Z=t[54];let ae;t[55]===C?ae=t[56]:(ae=C?(0,B.jsx)(`p`,{className:`text-xs text-red-600 dark:text-red-400`,children:C}):null,t[55]=C,t[56]=ae);let oe;return t[57]!==ie||t[58]!==Z||t[59]!==ae||t[60]!==N||t[61]!==P||t[62]!==F?(oe=(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[N,P,F,ie,Z,ae]}),t[57]=ie,t[58]=Z,t[59]=ae,t[60]=N,t[61]=P,t[62]=F,t[63]=oe):oe=t[63],oe}function H(e){return e.apiKey??null}function U(){return S(`w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 text-sm text-fg`,`placeholder:text-fg-subtle`,v)}function W(){return S(U(),`appearance-none bg-[length:1rem] bg-[right_0.5rem_center] bg-no-repeat pr-9`)}var G=`__custom__`;function K(e,t){if(!e.region.trim()&&!e.imageBaseUrl.trim())return``;let n=e.region.trim().toLowerCase();return t.some(e=>e.value===n)?n:G}function q(e,t){let n=e.baseUrl.trim().replace(/\/+$/,``);if(!n)return``;let r=t.map(e=>e.value.replace(/\/+$/,``)).indexOf(n);return r>=0?t[r].value:G}function J(e,t,n){return t===`beijing`?e.dashscopeRegion_beijing:t===`singapore`?e.dashscopeRegion_singapore:t===`us`?e.dashscopeRegion_us:n}function re(e,t){return t===`minimax`?e.minimaxClusterLabel:t===`fal`?e.falQueueBaseLabel:e.baseUrlLabel}function Y(e,t){return t===`minimax`?e.minimaxClusterHint:t===`fal`?e.falQueueBaseHint:null}function X(e,t){return JSON.stringify(e)!==JSON.stringify(t)}function ie(e,t){return t===1?e.modelCountOne:e.modelCountMany.replace(`{count}`,String(t))}function Z(e){let t=(0,te.c)(76),{summaries:n,credDraft:r,credBaseline:i,credDirty:a,credSaving:o,credError:s,credSavedFlash:c,credNoopFlash:l,updateCredRow:u,onDiscardCredentials:f,onSaveCredentials:m,extensionIds:g,showExtensionLinks:_,showImageModelsLink:v,hideActions:y,language:x,apiKeyLinkLabels:T,messages:E}=e,D=y===void 0?!1:y,O;t[0]===n?O=t[1]:(O=n.some(le),t[0]=n,t[1]=O);let j=O,M;t[2]===n?M=t[3]:(M=n.some(ce),t[2]=n,t[3]=M);let N=M,[P,F]=(0,k.useState)(se),[I,L]=(0,k.useState)(oe),R;if(t[4]!==i||t[5]!==r||t[6]!==n){R=new Set;for(let e of n)X(r[e.id]??A(),i[e.id]??A())&&R.add(e.id);t[4]=i,t[5]=r,t[6]=n,t[7]=R}else R=t[7];let z=R,ne;t[8]!==z||t[9]!==I||t[10]!==P?(ne=e=>I.has(e)?!1:P.has(e)?!0:z.has(e),t[8]=z,t[9]=I,t[10]=P,t[11]=ne):ne=t[11];let H=ne,Z;t[12]===H?Z=t[13]:(Z=e=>{if(H(e)){L(t=>new Set(t).add(e)),F(t=>{let n=new Set(t);return n.delete(e),n});return}L(t=>{let n=new Set(t);return n.delete(e),n}),F(t=>new Set(t).add(e))},t[12]=H,t[13]=Z);let ue=Z;if(n.length===0)return null;let de;t[14]===E.credentialsIntro?de=t[15]:(de=(0,B.jsx)(`p`,{children:E.credentialsIntro}),t[14]=E.credentialsIntro,t[15]=de);let fe;t[16]!==j||t[17]!==E.regionHint?(fe=j?(0,B.jsx)(`p`,{className:`text-fg-subtle`,children:E.regionHint}):null,t[16]=j,t[17]=E.regionHint,t[18]=fe):fe=t[18];let Q;t[19]!==N||t[20]!==E.endpointPresetsHint?(Q=N?(0,B.jsx)(`p`,{className:`text-fg-subtle`,children:E.endpointPresetsHint}):null,t[19]=N,t[20]=E.endpointPresetsHint,t[21]=Q):Q=t[21];let pe;t[22]!==v||t[23]!==E.imageModelsLinkTitle||t[24]!==E.openImageModelsPage?(pe=v?(0,B.jsx)(`p`,{children:(0,B.jsx)(C,{to:`/settings/image-models`,className:`font-medium text-accent hover:underline`,title:E.imageModelsLinkTitle,children:E.openImageModelsPage})}):null,t[22]=v,t[23]=E.imageModelsLinkTitle,t[24]=E.openImageModelsPage,t[25]=pe):pe=t[25];let me;t[26]!==de||t[27]!==fe||t[28]!==Q||t[29]!==pe?(me=(0,B.jsxs)(`div`,{className:`flex flex-col gap-1 text-xs leading-relaxed text-fg-muted`,children:[de,fe,Q,pe]}),t[26]=de,t[27]=fe,t[28]=Q,t[29]=pe,t[30]=me):me=t[30];let he;t[31]===s?he=t[32]:(he=s?(0,B.jsx)(`div`,{className:`rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm text-red-700 dark:text-red-300`,children:s}):null,t[31]=s,t[32]=he);let ge;t[33]!==a||t[34]!==l||t[35]!==c||t[36]!==o||t[37]!==D||t[38]!==f||t[39]!==m||t[40]!==E.credentialsNothingToSave||t[41]!==E.credentialsSaved||t[42]!==E.discardCredentials||t[43]!==E.saveCredentials||t[44]!==E.savingCredentials?(ge=D?null:(0,B.jsxs)(`div`,{className:`flex flex-wrap items-center justify-end gap-2`,children:[c?(0,B.jsx)(`span`,{className:`text-sm text-fg-muted`,children:E.credentialsSaved}):null,l?(0,B.jsx)(`span`,{className:`text-sm text-fg-muted`,children:E.credentialsNothingToSave}):null,(0,B.jsx)(ee,{type:`button`,variant:`secondary`,onClick:f,disabled:!a||o,children:E.discardCredentials}),(0,B.jsx)(ee,{type:`button`,variant:`primary`,onClick:m,disabled:!a||o,children:o?(0,B.jsxs)(B.Fragment,{children:[(0,B.jsx)(b,{className:`size-3.5 animate-spin`}),(0,B.jsx)(`span`,{className:`ml-1.5`,children:E.savingCredentials})]}):(0,B.jsxs)(B.Fragment,{children:[(0,B.jsx)(d,{className:`size-3.5`}),(0,B.jsx)(`span`,{className:`ml-1.5`,children:E.saveCredentials})]})})]}),t[33]=a,t[34]=l,t[35]=c,t[36]=o,t[37]=D,t[38]=f,t[39]=m,t[40]=E.credentialsNothingToSave,t[41]=E.credentialsSaved,t[42]=E.discardCredentials,t[43]=E.saveCredentials,t[44]=E.savingCredentials,t[45]=ge):ge=t[45];let $;if(t[46]!==T||t[47]!==i||t[48]!==r||t[49]!==g||t[50]!==H||t[51]!==x||t[52]!==_||t[53]!==n||t[54]!==E||t[55]!==ue||t[56]!==u){let e;t[58]!==T||t[59]!==i||t[60]!==r||t[61]!==g||t[62]!==H||t[63]!==x||t[64]!==_||t[65]!==E||t[66]!==ue||t[67]!==u?(e=e=>{let t=r[e.id]??A(),n=e.ui,a=_&&g.has(e.id)?`/settings/ext/${encodeURIComponent(e.id)}`:null,o=X(t,i[e.id]??A()),s=H(e.id),c=e.label??e.id,l=ie(E,e.models.length);return(0,B.jsxs)(`section`,{className:S(`overflow-hidden rounded-xl border bg-surface-panel shadow-sm transition-colors dark:shadow-none`,o?`border-accent/50`:`border-edge`),children:[(0,B.jsxs)(`button`,{type:`button`,className:`flex w-full items-start justify-between gap-3 px-4 py-3 text-left hover:bg-surface-hover/70`,"aria-expanded":s,"aria-controls":`img-provider-credentials-${e.id}`,onClick:()=>ue(e.id),children:[(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-1 flex-col gap-2`,children:[(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-2`,children:[(0,B.jsx)(`span`,{className:`truncate text-sm font-semibold text-fg`,children:c}),(0,B.jsx)(`span`,{className:`rounded-full bg-surface-subtle px-2 py-0.5 text-[11px] font-medium text-fg-muted`,children:e.id}),o?(0,B.jsx)(`span`,{className:`rounded-full bg-accent-soft px-2 py-0.5 text-[11px] font-medium text-accent-fg`,children:E.unsavedChanges}):null,e.configured?(0,B.jsx)(`span`,{className:`rounded-full bg-accent-soft px-2 py-0.5 text-[11px] font-medium text-accent-fg`,children:E.configured}):(0,B.jsx)(`span`,{className:`rounded-full border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-[11px] font-medium text-amber-700 dark:text-amber-300`,children:E.missingKey})]}),(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1 text-xs text-fg-subtle`,children:[(0,B.jsx)(`span`,{children:l}),e.defaultModel?(0,B.jsxs)(`span`,{className:`min-w-0 truncate`,children:[(0,B.jsxs)(`span`,{className:`text-fg-muted`,children:[E.defaultModel,`:`]}),` `,e.id,`/`,e.defaultModel]}):null]})]}),(0,B.jsx)(w,{className:S(`mt-0.5 size-4 shrink-0 text-fg-subtle transition-transform`,s?`rotate-180`:`rotate-0`),"aria-hidden":`true`}),(0,B.jsx)(`span`,{className:`sr-only`,children:s?E.collapseProvider:E.expandProvider})]}),s?(0,B.jsxs)(`div`,{id:`img-provider-credentials-${e.id}`,className:`border-t border-edge px-4 py-4`,children:[a?(0,B.jsxs)(C,{to:a,className:`mb-3 inline-flex items-center gap-1 text-xs font-medium text-accent hover:underline`,title:E.extensionSettingsLinkTitle,children:[(0,B.jsx)(h,{className:`size-3`}),E.openExtensionSettings]}):null,e.models.length>0?(0,B.jsxs)(`p`,{className:`mb-3 text-xs leading-relaxed text-fg-subtle`,children:[(0,B.jsxs)(`span`,{className:`text-fg-muted`,children:[E.modelsLabel,`:`]}),` `,e.models.map(t=>`${e.id}/${t}`).join(`, `)]}):null,(0,B.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,B.jsx)(V,{providerId:e.id,value:t.apiKey,onChange:t=>u(e.id,{apiKey:t}),apiKeyLinks:p(e.id,x),apiKeyLinkLabels:T,labels:{apiKeyLabel:E.apiKeyLabel,optionalPlaceholder:E.optionalPlaceholder,maskedHelp:E.apiKeyMaskedHelp,copy:E.apiKeyCopy,copied:E.apiKeyCopied,show:E.apiKeyShow,hide:E.apiKeyHide,notInConfigFile:E.apiKeyNotInConfigFile,loadFailed:E.apiKeyRevealFailed}}),n?.regions?.length?(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,B.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-region-preset-${e.id}`,children:E.regionLabel}),(0,B.jsxs)(`select`,{id:`img-cred-region-preset-${e.id}`,className:W(),value:K(t,n.regions),onChange:t=>{let r=t.target.value;if(r===``){u(e.id,{region:``,imageBaseUrl:``});return}if(r===G){u(e.id,{region:``,imageBaseUrl:``});return}let i=n.regions.find(e=>e.value===r);i&&u(e.id,{region:i.value,imageBaseUrl:i.imageBaseUrl})},children:[(0,B.jsx)(`option`,{value:``,children:E.regionPresetDefault}),n.regions.map(e=>(0,B.jsx)(`option`,{value:e.value,children:J(E,e.value,e.label)},e.value)),(0,B.jsx)(`option`,{value:G,children:E.regionPresetCustom})]}),K(t,n.regions)===G?(0,B.jsxs)(`div`,{className:`mt-2 grid gap-2 sm:grid-cols-2`,children:[(0,B.jsx)(`input`,{type:`text`,className:U(),value:t.region,placeholder:`region`,onChange:t=>u(e.id,{region:t.target.value})}),(0,B.jsx)(`input`,{type:`url`,className:U(),value:t.imageBaseUrl,placeholder:E.imageBaseUrlLabel,onChange:t=>u(e.id,{imageBaseUrl:t.target.value})})]}):null]}):null,n?.baseUrlPresets?.length?(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,B.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-base-preset-${e.id}`,children:re(E,n.baseUrlPresetKind)}),Y(E,n.baseUrlPresetKind)?(0,B.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:Y(E,n.baseUrlPresetKind)}):null,(0,B.jsxs)(`select`,{id:`img-cred-base-preset-${e.id}`,className:W(),value:q(t,n.baseUrlPresets),onChange:t=>{let n=t.target.value;if(n===``){u(e.id,{baseUrl:``});return}if(n===G){u(e.id,{baseUrl:``});return}u(e.id,{baseUrl:n.replace(/\/+$/,``)})},children:[(0,B.jsx)(`option`,{value:``,children:E.baseUrlPresetDefault}),n.baseUrlPresets.map(ae),(0,B.jsx)(`option`,{value:G,children:E.baseUrlPresetCustom})]}),q(t,n.baseUrlPresets)===G?(0,B.jsx)(`input`,{type:`url`,className:S(U(),`mt-2`),value:t.baseUrl,placeholder:`https://…`,onChange:t=>u(e.id,{baseUrl:t.target.value})}):null]}):null,n?.regions?.length&&K(t,n.regions)!==G?(0,B.jsxs)(`div`,{className:`flex min-w-0 flex-col gap-1 sm:col-span-2`,children:[(0,B.jsx)(`label`,{className:`text-xs font-medium text-fg-muted`,htmlFor:`img-cred-imgbase-ro-${e.id}`,children:E.imageBaseUrlLabel}),(0,B.jsx)(`input`,{id:`img-cred-imgbase-ro-${e.id}`,type:`url`,readOnly:!0,className:S(U(),`cursor-not-allowed opacity-90`),value:t.imageBaseUrl,title:E.imageBaseUrlPresetHint}),(0,B.jsx)(`p`,{className:`text-[11px] text-fg-subtle`,children:E.imageBaseUrlPresetHint})]}):null]})]}):null]},e.id)},t[58]=T,t[59]=i,t[60]=r,t[61]=g,t[62]=H,t[63]=x,t[64]=_,t[65]=E,t[66]=ue,t[67]=u,t[68]=e):e=t[68],$=n.map(e),t[46]=T,t[47]=i,t[48]=r,t[49]=g,t[50]=H,t[51]=x,t[52]=_,t[53]=n,t[54]=E,t[55]=ue,t[56]=u,t[57]=$}else $=t[57];let _e;t[69]===$?_e=t[70]:(_e=(0,B.jsx)(`div`,{className:`flex flex-col gap-4`,children:$}),t[69]=$,t[70]=_e);let ve;return t[71]!==me||t[72]!==he||t[73]!==ge||t[74]!==_e?(ve=(0,B.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[me,he,ge,_e]}),t[71]=me,t[72]=he,t[73]=ge,t[74]=_e,t[75]=ve):ve=t[75],ve}function ae(e){return(0,B.jsx)(`option`,{value:e.value,children:e.label},e.value)}function oe(){return new Set}function se(){return new Set}function ce(e){return(e.ui?.baseUrlPresets?.length??0)>0}function le(e){return(e.ui?.regions?.length??0)>0}function ue(e){let t=_(o(e=>!!e.token)),n=t.data,r=(0,k.useMemo)(()=>e.map(e=>e.id),[e]),s=(0,k.useRef)(!1),[c,l]=(0,k.useState)(null),[u,d]=(0,k.useState)(!1),[f,p]=(0,k.useState)(null),[m,h]=(0,k.useState)(!1),[g,v]=(0,k.useState)(!1),y=(0,k.useMemo)(()=>M(n?.payload?.config,r),[n?.payload?.config,r]),b=c??y,x=y;return{gwSwr:t,credDraft:b,credBaseline:x,credDirty:(0,k.useMemo)(()=>JSON.stringify(b)!==JSON.stringify(x),[b,x]),credSaving:u,credError:f,credSavedFlash:m,credNoopFlash:g,updateCredRow:(0,k.useCallback)((e,t)=>{s.current=!0,l(n=>{let r=n??y,i=r[e]??A();return{...r,[e]:{...i,...t}}})},[y]),onDiscardCredentials:(0,k.useCallback)(()=>{s.current=!1,l(null),p(null),h(!1),v(!1)},[]),saveCredentials:(0,k.useCallback)(async e=>{let n=F(r,b,x);if(Object.keys(n).length===0){v(!0),window.setTimeout(()=>v(!1),2200);return}d(!0),p(null),h(!1);try{await L(n),await t.mutate?.(),a(i(D)),s.current=!1,l(null),h(!0),window.setTimeout(()=>h(!1),2e3)}catch(t){p(t instanceof Error?t.message:e)}finally{d(!1)}},[r,b,x,t])}}function de(){return{enabled:!1,provider:`alibaba`,alibaba:{model:`paraformer-v2`},openai:{model:`whisper-1`},fallback:{enabled:!0,order:[`alibaba`,`openai`]}}}function fe(){return{enabled:!1,provider:`openai`,trigger:`always`,maxTextLength:512,timeoutMs:6e4,alibaba:{model:`qwen-tts`,voice:`Cherry`},openai:{model:`tts-1`,voice:`alloy`},edge:{voice:`en-US-MichelleNeural`},minimax:{model:`speech-2.8-hd`,voice:`male-qn-qingse`},"tts-local-cli":{command:``,outputFormat:`wav`}}}function Q(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function pe(e){return typeof e==`string`&&e.trim().length>0?e.trim():`alibaba`}function me(e){let t=de();if(!Q(e))return t;let n=pe(e.provider),r=Q(e.alibaba)?{...t.alibaba,...e.alibaba}:t.alibaba,i=Q(e.openai)?{...t.openai,...e.openai}:t.openai,a=t.fallback??{enabled:!0,order:[`alibaba`,`openai`]};if(Q(e.fallback)){let t=Array.isArray(e.fallback.order)?e.fallback.order.filter(e=>typeof e==`string`&&e.trim().length>0):a.order;a={enabled:typeof e.fallback.enabled==`boolean`?e.fallback.enabled:a.enabled,order:t.length?t:a.order}}let o=Q(e.providers)?Object.fromEntries(Object.entries(e.providers).map(([e,t])=>[e,Q(t)?{...t}:{}])):void 0;return{enabled:!!e.enabled,provider:n,alibaba:r,openai:i,...o?{providers:o}:{},fallback:a}}function he(e){return typeof e==`string`&&e.trim().length>0?e.trim():`openai`}function ge(e){let t=fe();if(!Q(e))return t;let n=he(e.provider),r=e.trigger===`off`||e.trigger===`always`||e.trigger===`inbound`||e.trigger===`tagged`?e.trigger:`always`,i=e[`tts-local-cli`];return{enabled:!!e.enabled,provider:n,trigger:r,maxTextLength:typeof e.maxTextLength==`number`&&Number.isFinite(e.maxTextLength)?e.maxTextLength:t.maxTextLength,timeoutMs:typeof e.timeoutMs==`number`&&Number.isFinite(e.timeoutMs)?e.timeoutMs:t.timeoutMs,alibaba:Q(e.alibaba)?{...t.alibaba,...e.alibaba}:t.alibaba,openai:Q(e.openai)?{...t.openai,...e.openai}:t.openai,edge:Q(e.edge)?{...t.edge,...e.edge}:t.edge,minimax:Q(e.minimax)?{...t.minimax,...e.minimax}:t.minimax,"tts-local-cli":Q(i)?{...t[`tts-local-cli`],...i}:t[`tts-local-cli`]}}function $(e){let t=Q(e)?e:{};return{stt:me(t.stt),tts:ge(t.tts)}}async function _e(){return $((await s(i(`/api/config`))).payload?.config??{})}async function ve(e){await s(i(`/api/config`),{method:`PATCH`,body:JSON.stringify({stt:e.stt,tts:e.tts})}),y()}async function ye(){let e=await s(i(`/api/voice/models`));if(!e.payload?.models)throw Error(`Missing voice models payload`);return e.payload.models}async function be(){let e=await s(i(`/api/voice/providers`));if(!e.payload?.providers)throw Error(`Missing voice providers payload`);return e.payload}async function xe(){let e=await s(i(`/api/voice/stt-providers`));if(!e.payload?.providers)throw Error(`Missing STT providers payload`);return e.payload}async function Se(e){let t=await s(i(`/api/voice/reveal-api-key`),{method:`POST`,body:JSON.stringify(e)});if(!t.payload)throw Error(`Missing reveal payload`);return t.payload}function Ce(e){let t=(0,te.c)(56),{kind:n,providerId:r,fieldId:i,value:a,onChange:o,labels:s,placeholder:u,className:d}=e,p=u===void 0?`sk-...`:u,m;t[0]!==n||t[1]!==r?(m=()=>Se({kind:n,provider:r}).then(we),t[0]=n,t[1]=r,t[2]=m):m=t[2];let h=m,g;t[3]!==s.loadFailed||t[4]!==h||t[5]!==a?(g={value:a,reveal:h,loadFailedLabel:s.loadFailed},t[3]=s.loadFailed,t[4]=h,t[5]=a,t[6]=g):g=t[6];let{masked:_,showKey:y,revealed:x,revealLoading:C,revealErr:w,copied:E,inputValue:D,inputType:O,copyEnabled:k,copyKey:A,toggleEye:j,onInputChange:M}=ne(g),N;t[7]===d?N=t[8]:(N=S(`flex min-w-0 flex-col gap-1.5`,d),t[7]=d,t[8]=N);let P;t[9]!==s.maskedHelp||t[10]!==_?(P=_?(0,B.jsx)(`p`,{className:`text-xs text-fg-subtle`,children:s.maskedHelp}):null,t[9]=s.maskedHelp,t[10]=_,t[11]=P):P=t[11];let F;t[12]===Symbol.for(`react.memo_cache_sentinel`)?(F=S(`w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 text-fg`,`placeholder:text-fg-subtle`,v,`dark:border-edge`,`min-w-0 flex-1 font-mono text-xs`),t[12]=F):F=t[12];let I=_?`••••••••`:p,L;t[13]!==o||t[14]!==M?(L=e=>M(e.target.value,o),t[13]=o,t[14]=M,t[15]=L):L=t[15];let R;t[16]!==i||t[17]!==O||t[18]!==D||t[19]!==I||t[20]!==L?(R=(0,B.jsx)(`input`,{id:i,type:O,autoComplete:`off`,spellCheck:!1,className:F,value:D,placeholder:I,onChange:L}),t[16]=i,t[17]=O,t[18]=D,t[19]=I,t[20]=L,t[21]=R):R=t[21];let z;t[22]!==E||t[23]!==k||t[24]!==A||t[25]!==s.copied||t[26]!==s.copy?(z=k?(0,B.jsxs)(ee,{type:`button`,variant:`secondary`,className:`px-2 py-1 text-xs`,onClick:()=>void A(),children:[E?(0,B.jsx)(f,{className:`size-3.5`}):(0,B.jsx)(c,{className:`size-3.5`}),E?s.copied:s.copy]}):null,t[22]=E,t[23]=k,t[24]=A,t[25]=s.copied,t[26]=s.copy,t[27]=z):z=t[27];let V;t[28]===j?V=t[29]:(V=()=>void j(),t[28]=j,t[29]=V);let H;t[30]!==C||t[31]!==y?(H=C?(0,B.jsx)(b,{className:`size-3.5 animate-spin`,"aria-hidden":!0}):y?(0,B.jsx)(T,{className:`size-3.5`}):(0,B.jsx)(l,{className:`size-3.5`}),t[30]=C,t[31]=y,t[32]=H):H=t[32];let U=y?s.hide:s.show,W;t[33]!==C||t[34]!==V||t[35]!==H||t[36]!==U?(W=(0,B.jsxs)(ee,{type:`button`,variant:`secondary`,className:`px-2 py-1 text-xs`,disabled:C,onClick:V,children:[H,U]}),t[33]=C,t[34]=V,t[35]=H,t[36]=U,t[37]=W):W=t[37];let G;t[38]!==z||t[39]!==W||t[40]!==R?(G=(0,B.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[R,z,W]}),t[38]=z,t[39]=W,t[40]=R,t[41]=G):G=t[41];let K;t[42]!==s.notInConfigFile||t[43]!==_||t[44]!==w||t[45]!==x||t[46]!==y?(K=_&&y&&x===null&&!w?(0,B.jsx)(`p`,{className:`text-xs text-amber-700 dark:text-amber-400/90`,children:s.notInConfigFile}):null,t[42]=s.notInConfigFile,t[43]=_,t[44]=w,t[45]=x,t[46]=y,t[47]=K):K=t[47];let q;t[48]===w?q=t[49]:(q=w?(0,B.jsx)(`p`,{className:`text-xs text-red-600 dark:text-red-400`,children:w}):null,t[48]=w,t[49]=q);let J;return t[50]!==G||t[51]!==K||t[52]!==q||t[53]!==N||t[54]!==P?(J=(0,B.jsxs)(`div`,{className:N,children:[P,G,K,q]}),t[50]=G,t[51]=K,t[52]=q,t[53]=N,t[54]=P,t[55]=J):J=t[55],J}function we(e){return e.apiKey??null}export{xe as a,ue as c,D as d,_e as i,Z as l,ye as n,$ as o,be as r,ve as s,Ce as t,O as u};
@@ -9,17 +9,17 @@
9
9
  <link rel="icon" type="image/svg+xml" href="/logo.svg" />
10
10
  <link rel="apple-touch-icon" href="/logo.svg" />
11
11
  <title>xopc</title>
12
- <script type="module" crossorigin src="/assets/index-BmVYculr.js"></script>
12
+ <script type="module" crossorigin src="/assets/index-Cu7bKuUi.js"></script>
13
13
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-aKtaBQYM.js">
14
14
  <link rel="modulepreload" crossorigin href="/assets/vendor-codemirror-D0yxdRpg.js">
15
15
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-BOUWij0V.js">
16
16
  <link rel="modulepreload" crossorigin href="/assets/dist-BTWC-BTN.js">
17
17
  <link rel="modulepreload" crossorigin href="/assets/vendor-swr-BIWyz7Rc.js">
18
- <link rel="modulepreload" crossorigin href="/assets/url-BwNL6Rgk.js">
18
+ <link rel="modulepreload" crossorigin href="/assets/url-Dd8Q7kZZ.js">
19
19
  <link rel="modulepreload" crossorigin href="/assets/createLucideIcon-DPHK1VkS.js">
20
- <link rel="modulepreload" crossorigin href="/assets/theme-store-DryYl3qD.js">
21
- <link rel="modulepreload" crossorigin href="/assets/fetch-B2MYHbWg.js">
22
- <link rel="stylesheet" crossorigin href="/assets/index-ew_2L2We.css">
20
+ <link rel="modulepreload" crossorigin href="/assets/theme-store-CZOh1nT3.js">
21
+ <link rel="modulepreload" crossorigin href="/assets/fetch-DRqwef_Q.js">
22
+ <link rel="stylesheet" crossorigin href="/assets/index-a5gWIdZQ.css">
23
23
  </head>
24
24
  <body>
25
25
  <div id="root"></div>
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.0.86";
2
+ var version = "0.0.87";
3
3
  //#endregion
4
4
  export { version };
5
5
 
@@ -39,6 +39,10 @@ export declare function resolveAgentProfileDir(cfg: Config, agentId: string): st
39
39
  /** Resolved path for a single profile Markdown basename (e.g. `SOUL.md`). */
40
40
  export declare function resolveAgentProfileMarkdownPath(cfg: Config, agentId: string, filename: string): string;
41
41
  export declare function resolveSessionsDir(cfg: Config, agentId: string): string;
42
+ /** Agent ids whose workspace root contains `workspacePath` (longest match first). */
43
+ export declare function resolveAgentIdsByWorkspacePath(cfg: Config, workspacePath: string): string[];
44
+ /** Best-matching agent id for cwd, or `undefined` when no workspace contains the path. */
45
+ export declare function resolveAgentIdByWorkspacePath(cfg: Config, workspacePath: string): string | undefined;
42
46
  /**
43
47
  * Find the agent id whose resolved markdown workspace matches `resolvedWorkspacePath`.
44
48
  * Falls back to {@link resolveDefaultAgentId} when no list entry matches.
@@ -2,7 +2,8 @@ import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
2
2
  import { init_paths_state, resolveStateDir } from "../config/paths-state.js";
3
3
  import { expandWorkspacePathString, init_workspace_path } from "../config/workspace-path.js";
4
4
  import { init_workspace_defaults, resolveDefaultAgentWorkspaceDir } from "../config/workspace-defaults.js";
5
- import { basename, join, resolve } from "node:path";
5
+ import { basename, isAbsolute, join, relative, resolve } from "node:path";
6
+ import fs from "node:fs";
6
7
  //#region src/agent/agent-scope.ts
7
8
  /**
8
9
  * Agent path and list resolution (config is the single source of truth).
@@ -121,19 +122,61 @@ function resolveAgentProfileMarkdownPath(cfg, agentId, filename) {
121
122
  function resolveSessionsDir(cfg, agentId) {
122
123
  return join(resolveAgentHomeDir(cfg, agentId), "sessions");
123
124
  }
125
+ function normalizePathForComparison(input) {
126
+ const resolved = resolve(resolveUserPath(input));
127
+ let normalized = resolved;
128
+ try {
129
+ normalized = fs.realpathSync.native(resolved);
130
+ } catch {}
131
+ if (process.platform === "win32") return normalized.toLowerCase();
132
+ return normalized;
133
+ }
134
+ function isPathWithinRoot(candidatePath, rootPath) {
135
+ const rel = relative(rootPath, candidatePath);
136
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
137
+ }
138
+ /** Agent ids whose workspace root contains `workspacePath` (longest match first). */
139
+ function resolveAgentIdsByWorkspacePath(cfg, workspacePath) {
140
+ const normalizedWorkspacePath = normalizePathForComparison(workspacePath);
141
+ const entries = listAgentEntries(cfg);
142
+ const matches = [];
143
+ for (let index = 0; index < entries.length; index += 1) {
144
+ const entry = entries[index];
145
+ const id = normalizeAgentId(entry.id);
146
+ const workspaceDir = normalizePathForComparison(resolveAgentWorkspaceDir(cfg, id));
147
+ if (!isPathWithinRoot(normalizedWorkspacePath, workspaceDir)) continue;
148
+ matches.push({
149
+ id,
150
+ workspaceDir,
151
+ order: index
152
+ });
153
+ }
154
+ const defaultId = resolveDefaultAgentId(cfg);
155
+ if (!entries.some((e) => normalizeAgentId(e.id) === defaultId)) {
156
+ const workspaceDir = normalizePathForComparison(resolveAgentWorkspaceDir(cfg, defaultId));
157
+ if (isPathWithinRoot(normalizedWorkspacePath, workspaceDir)) matches.push({
158
+ id: defaultId,
159
+ workspaceDir,
160
+ order: entries.length
161
+ });
162
+ }
163
+ matches.sort((left, right) => {
164
+ const workspaceLengthDelta = right.workspaceDir.length - left.workspaceDir.length;
165
+ if (workspaceLengthDelta !== 0) return workspaceLengthDelta;
166
+ return left.order - right.order;
167
+ });
168
+ return matches.map((entry) => entry.id);
169
+ }
170
+ /** Best-matching agent id for cwd, or `undefined` when no workspace contains the path. */
171
+ function resolveAgentIdByWorkspacePath(cfg, workspacePath) {
172
+ return resolveAgentIdsByWorkspacePath(cfg, workspacePath)[0];
173
+ }
124
174
  /**
125
175
  * Find the agent id whose resolved markdown workspace matches `resolvedWorkspacePath`.
126
176
  * Falls back to {@link resolveDefaultAgentId} when no list entry matches.
127
177
  */
128
178
  function resolveAgentIdForWorkspacePath(cfg, resolvedWorkspacePath) {
129
- const target = resolveUserPath(resolvedWorkspacePath);
130
- for (const e of listAgentEntries(cfg)) {
131
- const id = normalizeAgentId(e.id);
132
- if (resolveAgentWorkspaceDir(cfg, id) === target) return id;
133
- }
134
- const def = resolveDefaultAgentId(cfg);
135
- if (resolveAgentWorkspaceDir(cfg, def) === target) return def;
136
- return def;
179
+ return resolveAgentIdByWorkspacePath(cfg, resolvedWorkspacePath) ?? resolveDefaultAgentId(cfg);
137
180
  }
138
181
  function getDefaultWorkspacePath(cfg) {
139
182
  return resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));
@@ -176,6 +219,6 @@ var init_agent_scope = __esmMin((() => {
176
219
  }));
177
220
  //#endregion
178
221
  init_agent_scope();
179
- export { DEFAULT_AGENT_ID, STRICT_AGENT_ID_RE, getDefaultWorkspacePath, init_agent_scope, listAgentEntries, normalizeAgentId, resolveAgentDir, resolveAgentHomeDir, resolveAgentIdForWorkspacePath, resolveAgentProfileDir, resolveAgentProfileMarkdownPath, resolveAgentWorkspaceDir, resolveDefaultAgentId, resolveSessionsDir, resolveUserPath, validateAgentIdForNewAgent };
222
+ export { DEFAULT_AGENT_ID, STRICT_AGENT_ID_RE, getDefaultWorkspacePath, init_agent_scope, listAgentEntries, normalizeAgentId, resolveAgentDir, resolveAgentHomeDir, resolveAgentIdByWorkspacePath, resolveAgentIdForWorkspacePath, resolveAgentIdsByWorkspacePath, resolveAgentProfileDir, resolveAgentProfileMarkdownPath, resolveAgentWorkspaceDir, resolveDefaultAgentId, resolveSessionsDir, resolveUserPath, validateAgentIdForNewAgent };
180
223
 
181
224
  //# sourceMappingURL=agent-scope.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-scope.js","names":[],"sources":["../../../src/agent/agent-scope.ts"],"sourcesContent":["/**\n * Agent path and list resolution (config is the single source of truth).\n */\n\nimport { basename, join, resolve } from 'node:path';\n\nimport type { Config } from '../config/schema.js';\nimport { resolveStateDir } from '../config/paths-state.js';\nimport { expandWorkspacePathString } from '../config/workspace-path.js';\nimport { resolveDefaultAgentWorkspaceDir } from '../config/workspace-defaults.js';\n\nexport const DEFAULT_AGENT_ID = 'main';\n\nconst VALID_ID_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i;\nconst INVALID_CHARS_RE = /[^a-z0-9_-]+/g;\nconst LEADING_DASH_RE = /^-+/;\nconst TRAILING_DASH_RE = /-+$/;\n\n/** Normalized id segment safe for directory names under `agents/<id>/`. */\nexport const STRICT_AGENT_ID_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/;\n\nconst WINDOWS_RESERVED_AGENT_IDS = new Set<string>([\n 'con',\n 'prn',\n 'aux',\n 'nul',\n 'com1',\n 'com2',\n 'com3',\n 'com4',\n 'com5',\n 'com6',\n 'com7',\n 'com8',\n 'com9',\n 'lpt1',\n 'lpt2',\n 'lpt3',\n 'lpt4',\n 'lpt5',\n 'lpt6',\n 'lpt7',\n 'lpt8',\n 'lpt9',\n]);\n\nfunction normalizeLowercaseStringOrEmpty(s: string): string {\n return s.trim().toLowerCase();\n}\n\n/** Path-safe agent id. */\nexport function normalizeAgentId(value: string | undefined | null): string {\n const trimmed = (value ?? '').trim();\n if (!trimmed) {\n return DEFAULT_AGENT_ID;\n }\n const normalized = normalizeLowercaseStringOrEmpty(trimmed);\n if (VALID_ID_RE.test(trimmed)) {\n return normalized;\n }\n return (\n normalized\n .replace(INVALID_CHARS_RE, '-')\n .replace(LEADING_DASH_RE, '')\n .replace(TRAILING_DASH_RE, '')\n .slice(0, 64) || DEFAULT_AGENT_ID\n );\n}\n\n/**\n * Validate agent id for new agents (folder-safe path segment).\n * When `explicitId` is set, it must already match {@link STRICT_AGENT_ID_RE} (case-insensitive input, stored lowercase).\n * When omitted, id is derived from `displayNameForDerivation` via {@link normalizeAgentId}.\n */\nexport function validateAgentIdForNewAgent(\n explicitId: string | undefined | null,\n displayNameForDerivation: string,\n): { ok: true; agentId: string } | { ok: false; error: string } {\n const explicit = explicitId?.trim();\n if (explicit) {\n const id = explicit.toLowerCase();\n if (!STRICT_AGENT_ID_RE.test(id)) {\n return {\n ok: false,\n error:\n 'Invalid agent id: use 1–64 characters; letters, digits, underscores, and hyphens only; start with a letter or digit.',\n };\n }\n if (id === DEFAULT_AGENT_ID) {\n return { ok: false, error: `\"${DEFAULT_AGENT_ID}\" is reserved` };\n }\n if (WINDOWS_RESERVED_AGENT_IDS.has(id)) {\n return { ok: false, error: `Agent id \"${id}\" is reserved (Windows device name).` };\n }\n return { ok: true, agentId: id };\n }\n\n const agentId = normalizeAgentId(displayNameForDerivation.trim());\n if (agentId === DEFAULT_AGENT_ID) {\n return {\n ok: false,\n error:\n 'Display name cannot produce a valid agent folder id. Set an explicit Agent id (letters, digits, underscores, hyphens only).',\n };\n }\n if (WINDOWS_RESERVED_AGENT_IDS.has(agentId)) {\n return {\n ok: false,\n error: `That display name resolves to \"${agentId}\", which is reserved. Use a different display name or set an explicit Agent id.`,\n };\n }\n if (!STRICT_AGENT_ID_RE.test(agentId)) {\n return {\n ok: false,\n error:\n 'Could not derive a folder-safe agent id from the display name. Set an explicit Agent id (letters, digits, underscores, hyphens only).',\n };\n }\n return { ok: true, agentId };\n}\n\n/** Expand `~` and resolve to an absolute path. */\nexport function resolveUserPath(raw: string): string {\n const expanded = expandWorkspacePathString(raw.trim());\n return resolve(expanded);\n}\n\ntype AgentEntry = NonNullable<NonNullable<Config['agents']>['list']>[number];\n\nexport function listAgentEntries(cfg: Config): AgentEntry[] {\n const list = cfg.agents?.list;\n if (!Array.isArray(list)) {\n return [];\n }\n return list.filter((e): e is AgentEntry => Boolean(e && typeof e === 'object'));\n}\n\nexport function resolveDefaultAgentId(cfg: Config): string {\n const explicit = cfg.agents?.default?.trim();\n if (explicit) {\n return normalizeAgentId(explicit);\n }\n const agents = listAgentEntries(cfg);\n if (agents.length === 0) {\n return DEFAULT_AGENT_ID;\n }\n const defaults = agents.filter((a) => a?.default === true);\n const chosen = (defaults[0] ?? agents[0])?.id?.trim();\n return chosen ? normalizeAgentId(chosen) : DEFAULT_AGENT_ID;\n}\n\nfunction resolveAgentEntry(cfg: Config, agentId: string): AgentEntry | undefined {\n const id = normalizeAgentId(agentId);\n return listAgentEntries(cfg).find((e) => normalizeAgentId(e.id) === id);\n}\n\n/**\n * Markdown workspace root for an agent.\n */\nexport function resolveAgentWorkspaceDir(cfg: Config, agentId: string): string {\n const id = normalizeAgentId(agentId);\n const configured = resolveAgentEntry(cfg, id)?.workspace?.trim();\n if (configured) {\n return resolveUserPath(configured);\n }\n const defaultAgentId = resolveDefaultAgentId(cfg);\n const fallback = cfg.agents?.defaults?.workspace?.trim();\n if (fallback) {\n return join(resolveUserPath(fallback), id);\n }\n if (id === defaultAgentId) {\n return resolveDefaultAgentWorkspaceDir(process.env);\n }\n const stateDir = resolveStateDir(process.env);\n return join(stateDir, `workspace-${id}`);\n}\n\n/**\n * Internal agent state dir: credentials, `agent.json`, pid, inbox (`…/agent/`).\n */\nexport function resolveAgentDir(cfg: Config, agentId: string): string {\n const id = normalizeAgentId(agentId);\n const configured = resolveAgentEntry(cfg, id)?.agentDir?.trim();\n if (configured) {\n return resolveUserPath(configured);\n }\n const root = resolveStateDir(process.env);\n return join(root, 'agents', id, 'agent');\n}\n\n/** Parent of `sessions/` and `agent/`: `<stateDir>/agents/<id>/`. */\nexport function resolveAgentHomeDir(cfg: Config, agentId: string): string {\n return join(resolveStateDir(process.env), 'agents', normalizeAgentId(agentId));\n}\n\n/** Profile Markdown + gateway avatar files: `<stateDir>/agents/<id>/profile/`. */\nexport function resolveAgentProfileDir(cfg: Config, agentId: string): string {\n return join(resolveAgentHomeDir(cfg, agentId), 'profile');\n}\n\n/** Resolved path for a single profile Markdown basename (e.g. `SOUL.md`). */\nexport function resolveAgentProfileMarkdownPath(cfg: Config, agentId: string, filename: string): string {\n const base = basename(filename.trim().replace(/\\\\/g, '/'));\n return join(resolveAgentProfileDir(cfg, agentId), base);\n}\n\nexport function resolveSessionsDir(cfg: Config, agentId: string): string {\n return join(resolveAgentHomeDir(cfg, agentId), 'sessions');\n}\n\n/**\n * Find the agent id whose resolved markdown workspace matches `resolvedWorkspacePath`.\n * Falls back to {@link resolveDefaultAgentId} when no list entry matches.\n */\nexport function resolveAgentIdForWorkspacePath(cfg: Config, resolvedWorkspacePath: string): string {\n const target = resolveUserPath(resolvedWorkspacePath);\n for (const e of listAgentEntries(cfg)) {\n const id = normalizeAgentId(e.id);\n if (resolveAgentWorkspaceDir(cfg, id) === target) {\n return id;\n }\n }\n const def = resolveDefaultAgentId(cfg);\n if (resolveAgentWorkspaceDir(cfg, def) === target) {\n return def;\n }\n return def;\n}\n\nexport function getDefaultWorkspacePath(cfg: Config): string {\n return resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agent-scope.js","names":[],"sources":["../../../src/agent/agent-scope.ts"],"sourcesContent":["/**\n * Agent path and list resolution (config is the single source of truth).\n */\n\nimport fs from 'node:fs';\nimport { basename, join, relative, resolve, isAbsolute } from 'node:path';\n\nimport type { Config } from '../config/schema.js';\nimport { resolveStateDir } from '../config/paths-state.js';\nimport { expandWorkspacePathString } from '../config/workspace-path.js';\nimport { resolveDefaultAgentWorkspaceDir } from '../config/workspace-defaults.js';\n\nexport const DEFAULT_AGENT_ID = 'main';\n\nconst VALID_ID_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i;\nconst INVALID_CHARS_RE = /[^a-z0-9_-]+/g;\nconst LEADING_DASH_RE = /^-+/;\nconst TRAILING_DASH_RE = /-+$/;\n\n/** Normalized id segment safe for directory names under `agents/<id>/`. */\nexport const STRICT_AGENT_ID_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/;\n\nconst WINDOWS_RESERVED_AGENT_IDS = new Set<string>([\n 'con',\n 'prn',\n 'aux',\n 'nul',\n 'com1',\n 'com2',\n 'com3',\n 'com4',\n 'com5',\n 'com6',\n 'com7',\n 'com8',\n 'com9',\n 'lpt1',\n 'lpt2',\n 'lpt3',\n 'lpt4',\n 'lpt5',\n 'lpt6',\n 'lpt7',\n 'lpt8',\n 'lpt9',\n]);\n\nfunction normalizeLowercaseStringOrEmpty(s: string): string {\n return s.trim().toLowerCase();\n}\n\n/** Path-safe agent id. */\nexport function normalizeAgentId(value: string | undefined | null): string {\n const trimmed = (value ?? '').trim();\n if (!trimmed) {\n return DEFAULT_AGENT_ID;\n }\n const normalized = normalizeLowercaseStringOrEmpty(trimmed);\n if (VALID_ID_RE.test(trimmed)) {\n return normalized;\n }\n return (\n normalized\n .replace(INVALID_CHARS_RE, '-')\n .replace(LEADING_DASH_RE, '')\n .replace(TRAILING_DASH_RE, '')\n .slice(0, 64) || DEFAULT_AGENT_ID\n );\n}\n\n/**\n * Validate agent id for new agents (folder-safe path segment).\n * When `explicitId` is set, it must already match {@link STRICT_AGENT_ID_RE} (case-insensitive input, stored lowercase).\n * When omitted, id is derived from `displayNameForDerivation` via {@link normalizeAgentId}.\n */\nexport function validateAgentIdForNewAgent(\n explicitId: string | undefined | null,\n displayNameForDerivation: string,\n): { ok: true; agentId: string } | { ok: false; error: string } {\n const explicit = explicitId?.trim();\n if (explicit) {\n const id = explicit.toLowerCase();\n if (!STRICT_AGENT_ID_RE.test(id)) {\n return {\n ok: false,\n error:\n 'Invalid agent id: use 1–64 characters; letters, digits, underscores, and hyphens only; start with a letter or digit.',\n };\n }\n if (id === DEFAULT_AGENT_ID) {\n return { ok: false, error: `\"${DEFAULT_AGENT_ID}\" is reserved` };\n }\n if (WINDOWS_RESERVED_AGENT_IDS.has(id)) {\n return { ok: false, error: `Agent id \"${id}\" is reserved (Windows device name).` };\n }\n return { ok: true, agentId: id };\n }\n\n const agentId = normalizeAgentId(displayNameForDerivation.trim());\n if (agentId === DEFAULT_AGENT_ID) {\n return {\n ok: false,\n error:\n 'Display name cannot produce a valid agent folder id. Set an explicit Agent id (letters, digits, underscores, hyphens only).',\n };\n }\n if (WINDOWS_RESERVED_AGENT_IDS.has(agentId)) {\n return {\n ok: false,\n error: `That display name resolves to \"${agentId}\", which is reserved. Use a different display name or set an explicit Agent id.`,\n };\n }\n if (!STRICT_AGENT_ID_RE.test(agentId)) {\n return {\n ok: false,\n error:\n 'Could not derive a folder-safe agent id from the display name. Set an explicit Agent id (letters, digits, underscores, hyphens only).',\n };\n }\n return { ok: true, agentId };\n}\n\n/** Expand `~` and resolve to an absolute path. */\nexport function resolveUserPath(raw: string): string {\n const expanded = expandWorkspacePathString(raw.trim());\n return resolve(expanded);\n}\n\ntype AgentEntry = NonNullable<NonNullable<Config['agents']>['list']>[number];\n\nexport function listAgentEntries(cfg: Config): AgentEntry[] {\n const list = cfg.agents?.list;\n if (!Array.isArray(list)) {\n return [];\n }\n return list.filter((e): e is AgentEntry => Boolean(e && typeof e === 'object'));\n}\n\nexport function resolveDefaultAgentId(cfg: Config): string {\n const explicit = cfg.agents?.default?.trim();\n if (explicit) {\n return normalizeAgentId(explicit);\n }\n const agents = listAgentEntries(cfg);\n if (agents.length === 0) {\n return DEFAULT_AGENT_ID;\n }\n const defaults = agents.filter((a) => a?.default === true);\n const chosen = (defaults[0] ?? agents[0])?.id?.trim();\n return chosen ? normalizeAgentId(chosen) : DEFAULT_AGENT_ID;\n}\n\nfunction resolveAgentEntry(cfg: Config, agentId: string): AgentEntry | undefined {\n const id = normalizeAgentId(agentId);\n return listAgentEntries(cfg).find((e) => normalizeAgentId(e.id) === id);\n}\n\n/**\n * Markdown workspace root for an agent.\n */\nexport function resolveAgentWorkspaceDir(cfg: Config, agentId: string): string {\n const id = normalizeAgentId(agentId);\n const configured = resolveAgentEntry(cfg, id)?.workspace?.trim();\n if (configured) {\n return resolveUserPath(configured);\n }\n const defaultAgentId = resolveDefaultAgentId(cfg);\n const fallback = cfg.agents?.defaults?.workspace?.trim();\n if (fallback) {\n return join(resolveUserPath(fallback), id);\n }\n if (id === defaultAgentId) {\n return resolveDefaultAgentWorkspaceDir(process.env);\n }\n const stateDir = resolveStateDir(process.env);\n return join(stateDir, `workspace-${id}`);\n}\n\n/**\n * Internal agent state dir: credentials, `agent.json`, pid, inbox (`…/agent/`).\n */\nexport function resolveAgentDir(cfg: Config, agentId: string): string {\n const id = normalizeAgentId(agentId);\n const configured = resolveAgentEntry(cfg, id)?.agentDir?.trim();\n if (configured) {\n return resolveUserPath(configured);\n }\n const root = resolveStateDir(process.env);\n return join(root, 'agents', id, 'agent');\n}\n\n/** Parent of `sessions/` and `agent/`: `<stateDir>/agents/<id>/`. */\nexport function resolveAgentHomeDir(cfg: Config, agentId: string): string {\n return join(resolveStateDir(process.env), 'agents', normalizeAgentId(agentId));\n}\n\n/** Profile Markdown + gateway avatar files: `<stateDir>/agents/<id>/profile/`. */\nexport function resolveAgentProfileDir(cfg: Config, agentId: string): string {\n return join(resolveAgentHomeDir(cfg, agentId), 'profile');\n}\n\n/** Resolved path for a single profile Markdown basename (e.g. `SOUL.md`). */\nexport function resolveAgentProfileMarkdownPath(cfg: Config, agentId: string, filename: string): string {\n const base = basename(filename.trim().replace(/\\\\/g, '/'));\n return join(resolveAgentProfileDir(cfg, agentId), base);\n}\n\nexport function resolveSessionsDir(cfg: Config, agentId: string): string {\n return join(resolveAgentHomeDir(cfg, agentId), 'sessions');\n}\n\nfunction normalizePathForComparison(input: string): string {\n const resolved = resolve(resolveUserPath(input));\n let normalized = resolved;\n try {\n normalized = fs.realpathSync.native(resolved);\n } catch {\n // Keep lexical path for non-existent directories.\n }\n if (process.platform === 'win32') {\n return normalized.toLowerCase();\n }\n return normalized;\n}\n\nfunction isPathWithinRoot(candidatePath: string, rootPath: string): boolean {\n const rel = relative(rootPath, candidatePath);\n return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));\n}\n\n/** Agent ids whose workspace root contains `workspacePath` (longest match first). */\nexport function resolveAgentIdsByWorkspacePath(cfg: Config, workspacePath: string): string[] {\n const normalizedWorkspacePath = normalizePathForComparison(workspacePath);\n const entries = listAgentEntries(cfg);\n const matches: Array<{ id: string; workspaceDir: string; order: number }> = [];\n\n for (let index = 0; index < entries.length; index += 1) {\n const entry = entries[index]!;\n const id = normalizeAgentId(entry.id);\n const workspaceDir = normalizePathForComparison(resolveAgentWorkspaceDir(cfg, id));\n if (!isPathWithinRoot(normalizedWorkspacePath, workspaceDir)) {\n continue;\n }\n matches.push({ id, workspaceDir, order: index });\n }\n\n const defaultId = resolveDefaultAgentId(cfg);\n if (!entries.some((e) => normalizeAgentId(e.id) === defaultId)) {\n const workspaceDir = normalizePathForComparison(resolveAgentWorkspaceDir(cfg, defaultId));\n if (isPathWithinRoot(normalizedWorkspacePath, workspaceDir)) {\n matches.push({ id: defaultId, workspaceDir, order: entries.length });\n }\n }\n\n matches.sort((left, right) => {\n const workspaceLengthDelta = right.workspaceDir.length - left.workspaceDir.length;\n if (workspaceLengthDelta !== 0) {\n return workspaceLengthDelta;\n }\n return left.order - right.order;\n });\n\n return matches.map((entry) => entry.id);\n}\n\n/** Best-matching agent id for cwd, or `undefined` when no workspace contains the path. */\nexport function resolveAgentIdByWorkspacePath(\n cfg: Config,\n workspacePath: string,\n): string | undefined {\n return resolveAgentIdsByWorkspacePath(cfg, workspacePath)[0];\n}\n\n/**\n * Find the agent id whose resolved markdown workspace matches `resolvedWorkspacePath`.\n * Falls back to {@link resolveDefaultAgentId} when no list entry matches.\n */\nexport function resolveAgentIdForWorkspacePath(cfg: Config, resolvedWorkspacePath: string): string {\n return (\n resolveAgentIdByWorkspacePath(cfg, resolvedWorkspacePath) ??\n resolveDefaultAgentId(cfg)\n );\n}\n\nexport function getDefaultWorkspacePath(cfg: Config): string {\n return resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
- import { init_session_key, isCronSessionKey, isSubagentSessionKey } from "../../routing/session-key.js";
1
+ import { isCronSessionKey, isSubagentSessionKey } from "../../routing/session-key-utils.js";
2
+ import { init_session_key } from "../../routing/session-key.js";
2
3
  import { DEFAULT_AGENTS_FILENAME, DEFAULT_IDENTITY_FILENAME, DEFAULT_SOUL_FILENAME, DEFAULT_TOOLS_FILENAME, DEFAULT_USER_FILENAME } from "../context/workspace.js";
3
4
  //#region src/agent/bootstrap/filter-bootstrap-files.ts
4
5
  init_session_key();
@@ -1 +1 @@
1
- {"version":3,"file":"filter-bootstrap-files.js","names":[],"sources":["../../../../src/agent/bootstrap/filter-bootstrap-files.ts"],"sourcesContent":["import { isCronSessionKey, isSubagentSessionKey } from '../../routing/session-key.js';\nimport {\n DEFAULT_AGENTS_FILENAME,\n DEFAULT_IDENTITY_FILENAME,\n DEFAULT_SOUL_FILENAME,\n DEFAULT_TOOLS_FILENAME,\n DEFAULT_USER_FILENAME,\n} from '../context/workspace.js';\nimport type { WorkspaceBootstrapFile } from './types.js';\n\nconst MINIMAL_BOOTSTRAP_ALLOWLIST = new Set<string>([\n DEFAULT_AGENTS_FILENAME,\n DEFAULT_TOOLS_FILENAME,\n DEFAULT_SOUL_FILENAME,\n DEFAULT_IDENTITY_FILENAME,\n DEFAULT_USER_FILENAME,\n]);\n\n/** Subagent and cron sessions omit MEMORY.md and other heavy profile files. */\nexport function filterBootstrapFilesForSession(\n files: WorkspaceBootstrapFile[],\n sessionKey?: string,\n): WorkspaceBootstrapFile[] {\n if (!sessionKey || (!isSubagentSessionKey(sessionKey) && !isCronSessionKey(sessionKey))) {\n return files;\n }\n return files.filter((file) => MINIMAL_BOOTSTRAP_ALLOWLIST.has(file.name));\n}\n"],"mappings":";;;kBAAsF;AAUtF,MAAM,8BAA8B,IAAI,IAAY;CAClD;CACA;CACA;CACA;CACA;CACD,CAAC;;AAGF,SAAgB,+BACd,OACA,YAC0B;AAC1B,KAAI,CAAC,cAAe,CAAC,qBAAqB,WAAW,IAAI,CAAC,iBAAiB,WAAW,CACpF,QAAO;AAET,QAAO,MAAM,QAAQ,SAAS,4BAA4B,IAAI,KAAK,KAAK,CAAC"}
1
+ {"version":3,"file":"filter-bootstrap-files.js","names":[],"sources":["../../../../src/agent/bootstrap/filter-bootstrap-files.ts"],"sourcesContent":["import { isCronSessionKey, isSubagentSessionKey } from '../../routing/session-key.js';\nimport {\n DEFAULT_AGENTS_FILENAME,\n DEFAULT_IDENTITY_FILENAME,\n DEFAULT_SOUL_FILENAME,\n DEFAULT_TOOLS_FILENAME,\n DEFAULT_USER_FILENAME,\n} from '../context/workspace.js';\nimport type { WorkspaceBootstrapFile } from './types.js';\n\nconst MINIMAL_BOOTSTRAP_ALLOWLIST = new Set<string>([\n DEFAULT_AGENTS_FILENAME,\n DEFAULT_TOOLS_FILENAME,\n DEFAULT_SOUL_FILENAME,\n DEFAULT_IDENTITY_FILENAME,\n DEFAULT_USER_FILENAME,\n]);\n\n/** Subagent and cron sessions omit MEMORY.md and other heavy profile files. */\nexport function filterBootstrapFilesForSession(\n files: WorkspaceBootstrapFile[],\n sessionKey?: string,\n): WorkspaceBootstrapFile[] {\n if (!sessionKey || (!isSubagentSessionKey(sessionKey) && !isCronSessionKey(sessionKey))) {\n return files;\n }\n return files.filter((file) => MINIMAL_BOOTSTRAP_ALLOWLIST.has(file.name));\n}\n"],"mappings":";;;;kBAAsF;AAUtF,MAAM,8BAA8B,IAAI,IAAY;CAClD;CACA;CACA;CACA;CACA;CACD,CAAC;;AAGF,SAAgB,+BACd,OACA,YAC0B;AAC1B,KAAI,CAAC,cAAe,CAAC,qBAAqB,WAAW,IAAI,CAAC,iBAAiB,WAAW,CACpF,QAAO;AAET,QAAO,MAAM,QAAQ,SAAS,4BAA4B,IAAI,KAAK,KAAK,CAAC"}
@@ -1,4 +1,4 @@
1
- import { normalizeOptionalString } from "../../utils/string-coerce.js";
1
+ import { init_string_coerce, normalizeOptionalString } from "../../utils/string-coerce.js";
2
2
  import { emitSessionTranscriptUpdate } from "../../session/transcript-events.js";
3
3
  import { boundedJsonUtf8Bytes, firstEnumerableOwnKeys, jsonUtf8BytesOrInfinity } from "../../infra/json-utf8-bytes.js";
4
4
  import { formatContextLimitTruncationNotice } from "./tool-result-context-guard.js";
@@ -6,6 +6,7 @@ import { resolveLiveToolResultMaxChars, truncateToolResultMessage } from "./tool
6
6
  import { extractToolCallsFromAssistant, extractToolResultId } from "../transcript/tool-call-id.js";
7
7
  import { makeMissingToolResult, sanitizeToolCallInputs } from "../transcript/session-transcript-repair.js";
8
8
  //#region src/agent/embedded/session-tool-result-guard.ts
9
+ init_string_coerce();
9
10
  /**
10
11
  * Install the guard on a SessionManager and return its control API.
11
12
  * Subsequent assistant/toolResult writes by pi-coding-agent flow through the
@@ -1 +1 @@
1
- {"version":3,"file":"session-tool-result-guard.js","names":[],"sources":["../../../../src/agent/embedded/session-tool-result-guard.ts"],"sourcesContent":["/**\n * Session tool-result guard — wraps a pi `SessionManager.appendMessage` to:\n * 1. cap oversized tool-result text + details so they cannot blow up the next\n * LLM request (size + persistence limits).\n * 2. track pending tool-call IDs so missing tool results can be synthesised\n * (some providers refuse a turn that has an orphan tool_use block).\n * 3. drop assistant `toolCall` blocks whose tool name is not in the allowlist\n * (these would also trigger provider 400s).\n * 4. broadcast `xopc:transcript-row` updates so the gateway UI can stream them.\n *\n * Previously this module shipped with three sibling files\n * (`session-tool-result-state.ts`, `session-raw-append-message.ts`,\n * `session-tool-result-guard-wrapper.ts`). They are now consolidated here as\n * private constructs around the `ToolResultGuard` class. Pi-coding-agent owns\n * the `SessionManager` instance and calls `appendMessage` from inside the\n * runtime, so we still need to monkey-patch that method — but the patched\n * implementation is just `guard.guardedAppend.bind(guard)` and all state lives\n * on the class.\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { SessionManager } from '@earendil-works/pi-coding-agent';\n\nimport type { Config } from '../../config/schema.js';\nimport {\n boundedJsonUtf8Bytes,\n firstEnumerableOwnKeys,\n jsonUtf8BytesOrInfinity,\n type BoundedJsonUtf8Bytes,\n} from '../../infra/json-utf8-bytes.js';\nimport { emitSessionTranscriptUpdate } from '../../session/transcript-events.js';\nimport { normalizeOptionalString } from '../../utils/string-coerce.js';\nimport { formatContextLimitTruncationNotice } from './tool-result-context-guard.js';\nimport {\n DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS,\n resolveLiveToolResultMaxChars,\n truncateToolResultMessage,\n} from './tool-result-truncation.js';\nimport {\n makeMissingToolResult,\n sanitizeToolCallInputs,\n} from '../transcript/session-transcript-repair.js';\nimport {\n extractToolCallsFromAssistant,\n extractToolResultId,\n} from '../transcript/tool-call-id.js';\n\n// ── Public surface ──────────────────────────────────────────────────────────\n\nexport type BeforeMessageWriteHookEvent = { message: AgentMessage };\nexport type BeforeMessageWriteHookResult =\n | { block?: boolean; message?: AgentMessage }\n | undefined;\n\nexport interface ToolResultGuardOptions {\n /** Optional session key for transcript update broadcasts. */\n sessionKey?: string;\n /** Optional transform applied to any message before persistence. */\n transformMessageForPersistence?: (message: AgentMessage) => AgentMessage;\n /**\n * Optional, synchronous transform applied to toolResult messages *before* they are\n * persisted to the session transcript.\n */\n transformToolResultForPersistence?: (\n message: AgentMessage,\n meta: { toolCallId?: string; toolName?: string; isSynthetic?: boolean },\n ) => AgentMessage;\n /**\n * Whether to synthesize missing tool results to satisfy strict providers.\n * Defaults to true.\n */\n allowSyntheticToolResults?: boolean;\n missingToolResultText?: string;\n /**\n * Optional set/list of tool names accepted for assistant toolCall/toolUse blocks.\n * When set, tool calls with unknown names are dropped before persistence.\n */\n allowedToolNames?: Iterable<string>;\n /**\n * Synchronous hook invoked before any message is written to the session JSONL.\n * If the hook returns { block: true }, the message is silently dropped.\n * If it returns { message }, the modified message is written instead.\n */\n beforeMessageWriteHook?: (event: BeforeMessageWriteHookEvent) => BeforeMessageWriteHookResult;\n maxToolResultChars?: number;\n}\n\nexport interface InstallSessionToolResultGuardResult {\n flushPendingToolResults: () => void;\n clearPendingToolResults: () => void;\n getPendingIds: () => string[];\n}\n\n/** Idempotent wrapper that also adds the helper methods consumers expect. */\nexport type GuardedPiTranscriptManager = SessionManager & {\n flushPendingToolResults?: () => void;\n clearPendingToolResults?: () => void;\n};\n\n/**\n * Install the guard on a SessionManager and return its control API.\n * Subsequent assistant/toolResult writes by pi-coding-agent flow through the\n * guard transparently.\n */\nexport function installSessionToolResultGuard(\n sessionManager: SessionManager,\n opts: ToolResultGuardOptions = {},\n): InstallSessionToolResultGuardResult {\n const guard = new ToolResultGuard(sessionManager, opts);\n guard.attach();\n return {\n flushPendingToolResults: () => guard.flushPending(),\n clearPendingToolResults: () => guard.clearPending(),\n getPendingIds: () => guard.getPendingIds(),\n };\n}\n\n/**\n * Convenience wrapper used by the embedded runner pool: install the guard\n * (idempotent), pin the size cap from the model context window, and expose\n * `flushPendingToolResults` / `clearPendingToolResults` directly on the\n * SessionManager instance so callers do not need to keep the install result.\n */\nexport function guardSessionManager(\n sessionManager: SessionManager,\n opts?: {\n agentId?: string;\n sessionKey?: string;\n config?: Config;\n contextWindowTokens?: number;\n allowSyntheticToolResults?: boolean;\n missingToolResultText?: string;\n allowedToolNames?: Iterable<string>;\n },\n): GuardedPiTranscriptManager {\n if (typeof (sessionManager as GuardedPiTranscriptManager).flushPendingToolResults === 'function') {\n return sessionManager as GuardedPiTranscriptManager;\n }\n\n const result = installSessionToolResultGuard(sessionManager, {\n sessionKey: opts?.sessionKey,\n allowSyntheticToolResults: opts?.allowSyntheticToolResults,\n missingToolResultText: opts?.missingToolResultText,\n allowedToolNames: opts?.allowedToolNames,\n maxToolResultChars:\n typeof opts?.contextWindowTokens === 'number'\n ? resolveLiveToolResultMaxChars({\n contextWindowTokens: opts.contextWindowTokens,\n cfg: opts?.config,\n agentId: opts?.agentId,\n })\n : undefined,\n });\n const tagged = sessionManager as GuardedPiTranscriptManager;\n tagged.flushPendingToolResults = result.flushPendingToolResults;\n tagged.clearPendingToolResults = result.clearPendingToolResults;\n return tagged;\n}\n\n/**\n * Recover the original (un-guarded) appendMessage for a session manager.\n * Useful for callers that need a low-level \"bypass the guard\" write path.\n */\nexport function getRawSessionAppendMessage(\n sessionManager: SessionManager,\n): SessionManager['appendMessage'] {\n const stored = (sessionManager as SessionManagerWithRawAppend)[RAW_APPEND_MESSAGE];\n return stored ?? sessionManager.appendMessage.bind(sessionManager);\n}\n\n// ── Internal: persistence + truncation helpers ──────────────────────────────\n\nfunction resolveMaxToolResultChars(opts: { maxToolResultChars?: number }): number {\n return Math.max(1, opts.maxToolResultChars ?? DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS);\n}\n\nfunction capToolResultSize(msg: AgentMessage, maxChars: number): AgentMessage {\n if ((msg as { role?: string }).role !== 'toolResult') {\n return msg;\n }\n return truncateToolResultMessage(msg, maxChars, {\n suffix: (truncatedChars) => formatContextLimitTruncationNotice(truncatedChars),\n minKeepChars: 2_000,\n });\n}\n\n// `details` is runtime/UI metadata, not model-visible tool output. Keep the\n// session JSONL useful for debugging without letting metadata blobs dominate\n// disk, replay repair, transcript broadcasts, or future tooling that reads raw\n// sessions. Model-visible text belongs in tool result `content`.\nconst MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES = 8_192;\nconst MAX_PERSISTED_DETAIL_STRING_CHARS = 2_000;\nconst MAX_PERSISTED_DETAIL_SESSION_COUNT = 10;\nconst MAX_PERSISTED_DETAIL_FALLBACK_STRING_CHARS = 200;\n\nfunction originalDetailsSizeFields(size: BoundedJsonUtf8Bytes): Record<string, number> {\n return size.complete\n ? { originalDetailsBytes: size.bytes }\n : { originalDetailsBytesAtLeast: size.bytes };\n}\n\nfunction truncatePersistedDetailString(\n value: string,\n maxChars = MAX_PERSISTED_DETAIL_STRING_CHARS,\n): string {\n if (value.length <= maxChars) {\n return value;\n }\n return `${value.slice(0, maxChars)}\\n\\n[xopc persisted detail truncated: ${\n value.length - maxChars\n } chars omitted]`;\n}\n\nfunction sanitizePersistedSessionDetail(value: unknown): unknown {\n if (!value || typeof value !== 'object') {\n return value;\n }\n const src = value as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const key of [\n 'sessionId',\n 'status',\n 'pid',\n 'startedAt',\n 'endedAt',\n 'runtimeMs',\n 'cwd',\n 'name',\n 'truncated',\n 'exitCode',\n 'exitSignal',\n ]) {\n const field = src[key];\n if (field !== undefined) {\n out[key] = typeof field === 'string' ? truncatePersistedDetailString(field, 500) : field;\n }\n }\n if (typeof src.command === 'string') {\n out.command = truncatePersistedDetailString(src.command, 500);\n }\n return out;\n}\n\nfunction buildPersistedDetailsFallback(\n src: Record<string, unknown> | undefined,\n originalSize: BoundedJsonUtf8Bytes,\n sanitizedBytes?: number,\n): Record<string, unknown> {\n const fallback: Record<string, unknown> = {\n persistedDetailsTruncated: true,\n finalDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n };\n if (sanitizedBytes !== undefined) {\n fallback.sanitizedDetailsBytes = sanitizedBytes;\n }\n if (src) {\n fallback.originalDetailKeys = firstEnumerableOwnKeys(src, 40);\n for (const key of ['status', 'sessionId', 'pid', 'exitCode', 'exitSignal', 'truncated']) {\n const field = src[key];\n if (field !== undefined) {\n fallback[key] =\n typeof field === 'string'\n ? truncatePersistedDetailString(field, MAX_PERSISTED_DETAIL_FALLBACK_STRING_CHARS)\n : field;\n }\n }\n }\n return fallback;\n}\n\nfunction enforcePersistedDetailsByteCap(\n value: Record<string, unknown>,\n src: Record<string, unknown> | undefined,\n originalSize: BoundedJsonUtf8Bytes,\n): Record<string, unknown> {\n const sanitizedBytes = jsonUtf8BytesOrInfinity(value);\n if (sanitizedBytes <= MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES) {\n return value;\n }\n const fallback = buildPersistedDetailsFallback(src, originalSize, sanitizedBytes);\n if (jsonUtf8BytesOrInfinity(fallback) <= MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES) {\n return fallback;\n }\n return {\n persistedDetailsTruncated: true,\n finalDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n sanitizedDetailsBytes: sanitizedBytes,\n };\n}\n\nfunction sanitizeToolResultDetailsForPersistence(details: unknown): unknown {\n if (details === undefined || details === null) {\n return details;\n }\n const originalSize = boundedJsonUtf8Bytes(details, MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES);\n if (originalSize.complete && originalSize.bytes <= MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES) {\n return details;\n }\n if (typeof details !== 'object') {\n return enforcePersistedDetailsByteCap(\n {\n persistedDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n valueType: typeof details,\n },\n undefined,\n originalSize,\n );\n }\n const src = details as Record<string, unknown>;\n const out: Record<string, unknown> = {\n persistedDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n originalDetailKeys: firstEnumerableOwnKeys(src, 40),\n };\n for (const key of [\n 'status',\n 'sessionId',\n 'pid',\n 'startedAt',\n 'endedAt',\n 'cwd',\n 'name',\n 'exitCode',\n 'exitSignal',\n 'retryInMs',\n 'total',\n 'totalLines',\n 'totalChars',\n 'truncated',\n 'fullOutputPath',\n 'truncation',\n ]) {\n const field = src[key];\n if (field !== undefined) {\n out[key] = typeof field === 'string' ? truncatePersistedDetailString(field) : field;\n }\n }\n if (typeof src.tail === 'string') {\n out.tail = truncatePersistedDetailString(src.tail);\n }\n if (Array.isArray(src.sessions)) {\n out.sessions = src.sessions\n .slice(0, MAX_PERSISTED_DETAIL_SESSION_COUNT)\n .map(sanitizePersistedSessionDetail);\n if (src.sessions.length > MAX_PERSISTED_DETAIL_SESSION_COUNT) {\n out.sessionsTruncated = src.sessions.length - MAX_PERSISTED_DETAIL_SESSION_COUNT;\n }\n }\n return enforcePersistedDetailsByteCap(out, src, originalSize);\n}\n\nfunction capToolResultDetails(msg: AgentMessage): AgentMessage {\n if ((msg as { role?: string }).role !== 'toolResult') {\n return msg;\n }\n const details = (msg as { details?: unknown }).details;\n const sanitizedDetails = sanitizeToolResultDetailsForPersistence(details);\n if (sanitizedDetails === details) {\n return msg;\n }\n const next = { ...msg } as AgentMessage & { details?: unknown };\n next.details = sanitizedDetails;\n return next;\n}\n\nfunction capToolResultForPersistence(msg: AgentMessage, maxChars: number): AgentMessage {\n return capToolResultDetails(capToolResultSize(msg, maxChars));\n}\n\nfunction normalizePersistedToolResultName(\n message: AgentMessage,\n fallbackName?: string,\n): AgentMessage {\n if ((message as { role?: unknown }).role !== 'toolResult') {\n return message;\n }\n const toolResult = message as Extract<AgentMessage, { role: 'toolResult' }>;\n const rawToolName = (toolResult as { toolName?: unknown }).toolName;\n const normalizedToolName = normalizeOptionalString(rawToolName);\n if (normalizedToolName) {\n if (rawToolName === normalizedToolName) {\n return toolResult;\n }\n return { ...toolResult, toolName: normalizedToolName };\n }\n\n const normalizedFallback = normalizeOptionalString(fallbackName);\n if (normalizedFallback) {\n return { ...toolResult, toolName: normalizedFallback };\n }\n\n if (typeof rawToolName === 'string') {\n return { ...toolResult, toolName: 'unknown' };\n }\n return toolResult;\n}\n\n// ── Internal: raw-append symbol storage ─────────────────────────────────────\n\nconst RAW_APPEND_MESSAGE = Symbol('xopc.session.rawAppendMessage');\n\ntype SessionManagerWithRawAppend = SessionManager & {\n [RAW_APPEND_MESSAGE]?: SessionManager['appendMessage'];\n};\n\nfunction rememberOriginalAppend(\n sessionManager: SessionManager,\n): SessionManager['appendMessage'] {\n const tagged = sessionManager as SessionManagerWithRawAppend;\n const stored = tagged[RAW_APPEND_MESSAGE];\n if (stored) {\n return stored;\n }\n const original = sessionManager.appendMessage.bind(sessionManager);\n tagged[RAW_APPEND_MESSAGE] = original;\n return original;\n}\n\n// ── Internal: pending tool-call state ──────────────────────────────────────\n\ntype PendingToolCall = { id: string; name?: string };\n\nclass PendingToolCallTracker {\n private readonly pending = new Map<string, string | undefined>();\n\n size(): number {\n return this.pending.size;\n }\n\n getToolName(id: string): string | undefined {\n return this.pending.get(id);\n }\n\n delete(id: string): void {\n this.pending.delete(id);\n }\n\n clear(): void {\n this.pending.clear();\n }\n\n trackToolCalls(calls: readonly PendingToolCall[]): void {\n for (const call of calls) {\n this.pending.set(call.id, call.name);\n }\n }\n\n entries(): IterableIterator<[string, string | undefined]> {\n return this.pending.entries();\n }\n\n getPendingIds(): string[] {\n return Array.from(this.pending.keys());\n }\n\n shouldFlushForSanitizedDrop(): boolean {\n return this.pending.size > 0;\n }\n\n shouldFlushBeforeNonToolResult(nextRole: unknown, toolCallCount: number): boolean {\n return this.pending.size > 0 && (toolCallCount === 0 || nextRole !== 'assistant');\n }\n\n shouldFlushBeforeNewToolCalls(toolCallCount: number): boolean {\n return this.pending.size > 0 && toolCallCount > 0;\n }\n}\n\n// ── ToolResultGuard class ──────────────────────────────────────────────────\n\nclass ToolResultGuard {\n private readonly sessionManager: SessionManager;\n private readonly opts: ToolResultGuardOptions;\n private readonly pending = new PendingToolCallTracker();\n private readonly originalAppend: SessionManager['appendMessage'];\n private readonly allowSyntheticToolResults: boolean;\n private readonly maxToolResultChars: number;\n\n constructor(sessionManager: SessionManager, opts: ToolResultGuardOptions) {\n this.sessionManager = sessionManager;\n this.opts = opts;\n this.originalAppend = rememberOriginalAppend(sessionManager);\n this.allowSyntheticToolResults = opts.allowSyntheticToolResults ?? true;\n this.maxToolResultChars = resolveMaxToolResultChars(opts);\n }\n\n /** Monkey-patch the session manager so pi-coding-agent's internal appendMessage flows through us. */\n attach(): void {\n const bound = this.guardedAppend.bind(this);\n this.sessionManager.appendMessage = bound as SessionManager['appendMessage'];\n }\n\n flushPending(): void {\n if (this.pending.size() === 0) {\n return;\n }\n if (this.allowSyntheticToolResults) {\n for (const [id, name] of this.pending.entries()) {\n const synthetic = makeMissingToolResult({\n toolCallId: id,\n toolName: name,\n text: this.opts.missingToolResultText,\n });\n const flushed = this.applyBeforeWriteHook(\n this.persistToolResult(this.persistMessage(synthetic), {\n toolCallId: id,\n toolName: name,\n isSynthetic: true,\n }),\n );\n if (flushed) {\n this.originalAppend(capToolResultForPersistence(flushed, this.maxToolResultChars) as never);\n }\n }\n }\n this.pending.clear();\n }\n\n clearPending(): void {\n this.pending.clear();\n }\n\n getPendingIds(): string[] {\n return this.pending.getPendingIds();\n }\n\n private persistMessage(message: AgentMessage): AgentMessage {\n const transformer = this.opts.transformMessageForPersistence;\n return transformer ? transformer(message) : message;\n }\n\n private persistToolResult(\n message: AgentMessage,\n meta: { toolCallId?: string; toolName?: string; isSynthetic?: boolean },\n ): AgentMessage {\n const transformer = this.opts.transformToolResultForPersistence;\n return transformer ? transformer(message, meta) : message;\n }\n\n /**\n * Run the before_message_write hook. Returns the (possibly modified) message,\n * or null if the message should be blocked.\n */\n private applyBeforeWriteHook(msg: AgentMessage): AgentMessage | null {\n const beforeWrite = this.opts.beforeMessageWriteHook;\n if (!beforeWrite) {\n return msg;\n }\n const result = beforeWrite({ message: msg });\n if (result?.block) {\n return null;\n }\n if (result?.message) {\n return result.message;\n }\n return msg;\n }\n\n private guardedAppend(message: AgentMessage): unknown {\n let nextMessage = message;\n const role = (message as { role?: unknown }).role;\n if (role === 'assistant') {\n const sanitized = sanitizeToolCallInputs([message], {\n allowedToolNames: this.opts.allowedToolNames,\n });\n if (sanitized.length === 0) {\n if (this.pending.shouldFlushForSanitizedDrop()) {\n this.flushPending();\n }\n return undefined;\n }\n nextMessage = sanitized[0];\n }\n const nextRole = (nextMessage as { role?: unknown }).role;\n\n if (nextRole === 'toolResult') {\n const id = extractToolResultId(nextMessage as Extract<AgentMessage, { role: 'toolResult' }>);\n const toolName = id ? this.pending.getToolName(id) : undefined;\n if (id) {\n this.pending.delete(id);\n }\n const normalizedToolResult = normalizePersistedToolResultName(nextMessage, toolName);\n // Apply hard size cap before persistence to prevent oversized tool results\n // from consuming the entire context window on subsequent LLM calls.\n const capped = capToolResultForPersistence(\n this.persistMessage(normalizedToolResult),\n this.maxToolResultChars,\n );\n const persisted = this.applyBeforeWriteHook(\n this.persistToolResult(capped, {\n toolCallId: id ?? undefined,\n toolName,\n isSynthetic: false,\n }),\n );\n if (!persisted) {\n return undefined;\n }\n return this.originalAppend(capToolResultForPersistence(persisted, this.maxToolResultChars) as never);\n }\n\n // Skip tool call extraction for aborted/errored assistant messages.\n // When stopReason is \"error\" or \"aborted\", the tool_use blocks may be incomplete\n // and should not have synthetic tool_results created. Creating synthetic results\n // for incomplete tool calls causes API 400 errors:\n // \"unexpected tool_use_id found in tool_result blocks\"\n // This matches the behavior in repairToolUseResultPairing (session-transcript-repair.ts)\n const stopReason = (nextMessage as { stopReason?: string }).stopReason;\n const toolCalls =\n nextRole === 'assistant' && stopReason !== 'aborted' && stopReason !== 'error'\n ? extractToolCallsFromAssistant(nextMessage as Extract<AgentMessage, { role: 'assistant' }>)\n : [];\n\n // Always clear pending tool call state before appending non-tool-result messages.\n // flushPendingToolResults() only inserts synthetic results when allowSyntheticToolResults\n // is true; it always clears the pending map. Without this, providers that disable\n // synthetic results (e.g. OpenAI) accumulate stale pending state when a user message\n // interrupts in-flight tool calls, leaving orphaned tool_use blocks in the transcript\n // that cause API 400 errors on subsequent requests.\n if (this.pending.shouldFlushBeforeNonToolResult(nextRole, toolCalls.length)) {\n this.flushPending();\n }\n // If new tool calls arrive while older ones are pending, flush the old ones first.\n if (this.pending.shouldFlushBeforeNewToolCalls(toolCalls.length)) {\n this.flushPending();\n }\n\n const finalMessage = this.applyBeforeWriteHook(this.persistMessage(nextMessage));\n if (!finalMessage) {\n return undefined;\n }\n const result = this.originalAppend(finalMessage as never);\n\n const sessionFile = (\n this.sessionManager as { getSessionFile?: () => string | null }\n ).getSessionFile?.();\n if (sessionFile) {\n emitSessionTranscriptUpdate({\n sessionFile,\n sessionKey: this.opts.sessionKey,\n message: finalMessage,\n messageId: typeof result === 'string' ? result : undefined,\n });\n }\n\n if (toolCalls.length > 0) {\n this.pending.trackToolCalls(toolCalls);\n }\n\n return result;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"session-tool-result-guard.js","names":[],"sources":["../../../../src/agent/embedded/session-tool-result-guard.ts"],"sourcesContent":["/**\n * Session tool-result guard — wraps a pi `SessionManager.appendMessage` to:\n * 1. cap oversized tool-result text + details so they cannot blow up the next\n * LLM request (size + persistence limits).\n * 2. track pending tool-call IDs so missing tool results can be synthesised\n * (some providers refuse a turn that has an orphan tool_use block).\n * 3. drop assistant `toolCall` blocks whose tool name is not in the allowlist\n * (these would also trigger provider 400s).\n * 4. broadcast `xopc:transcript-row` updates so the gateway UI can stream them.\n *\n * Previously this module shipped with three sibling files\n * (`session-tool-result-state.ts`, `session-raw-append-message.ts`,\n * `session-tool-result-guard-wrapper.ts`). They are now consolidated here as\n * private constructs around the `ToolResultGuard` class. Pi-coding-agent owns\n * the `SessionManager` instance and calls `appendMessage` from inside the\n * runtime, so we still need to monkey-patch that method — but the patched\n * implementation is just `guard.guardedAppend.bind(guard)` and all state lives\n * on the class.\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { SessionManager } from '@earendil-works/pi-coding-agent';\n\nimport type { Config } from '../../config/schema.js';\nimport {\n boundedJsonUtf8Bytes,\n firstEnumerableOwnKeys,\n jsonUtf8BytesOrInfinity,\n type BoundedJsonUtf8Bytes,\n} from '../../infra/json-utf8-bytes.js';\nimport { emitSessionTranscriptUpdate } from '../../session/transcript-events.js';\nimport { normalizeOptionalString } from '../../utils/string-coerce.js';\nimport { formatContextLimitTruncationNotice } from './tool-result-context-guard.js';\nimport {\n DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS,\n resolveLiveToolResultMaxChars,\n truncateToolResultMessage,\n} from './tool-result-truncation.js';\nimport {\n makeMissingToolResult,\n sanitizeToolCallInputs,\n} from '../transcript/session-transcript-repair.js';\nimport {\n extractToolCallsFromAssistant,\n extractToolResultId,\n} from '../transcript/tool-call-id.js';\n\n// ── Public surface ──────────────────────────────────────────────────────────\n\nexport type BeforeMessageWriteHookEvent = { message: AgentMessage };\nexport type BeforeMessageWriteHookResult =\n | { block?: boolean; message?: AgentMessage }\n | undefined;\n\nexport interface ToolResultGuardOptions {\n /** Optional session key for transcript update broadcasts. */\n sessionKey?: string;\n /** Optional transform applied to any message before persistence. */\n transformMessageForPersistence?: (message: AgentMessage) => AgentMessage;\n /**\n * Optional, synchronous transform applied to toolResult messages *before* they are\n * persisted to the session transcript.\n */\n transformToolResultForPersistence?: (\n message: AgentMessage,\n meta: { toolCallId?: string; toolName?: string; isSynthetic?: boolean },\n ) => AgentMessage;\n /**\n * Whether to synthesize missing tool results to satisfy strict providers.\n * Defaults to true.\n */\n allowSyntheticToolResults?: boolean;\n missingToolResultText?: string;\n /**\n * Optional set/list of tool names accepted for assistant toolCall/toolUse blocks.\n * When set, tool calls with unknown names are dropped before persistence.\n */\n allowedToolNames?: Iterable<string>;\n /**\n * Synchronous hook invoked before any message is written to the session JSONL.\n * If the hook returns { block: true }, the message is silently dropped.\n * If it returns { message }, the modified message is written instead.\n */\n beforeMessageWriteHook?: (event: BeforeMessageWriteHookEvent) => BeforeMessageWriteHookResult;\n maxToolResultChars?: number;\n}\n\nexport interface InstallSessionToolResultGuardResult {\n flushPendingToolResults: () => void;\n clearPendingToolResults: () => void;\n getPendingIds: () => string[];\n}\n\n/** Idempotent wrapper that also adds the helper methods consumers expect. */\nexport type GuardedPiTranscriptManager = SessionManager & {\n flushPendingToolResults?: () => void;\n clearPendingToolResults?: () => void;\n};\n\n/**\n * Install the guard on a SessionManager and return its control API.\n * Subsequent assistant/toolResult writes by pi-coding-agent flow through the\n * guard transparently.\n */\nexport function installSessionToolResultGuard(\n sessionManager: SessionManager,\n opts: ToolResultGuardOptions = {},\n): InstallSessionToolResultGuardResult {\n const guard = new ToolResultGuard(sessionManager, opts);\n guard.attach();\n return {\n flushPendingToolResults: () => guard.flushPending(),\n clearPendingToolResults: () => guard.clearPending(),\n getPendingIds: () => guard.getPendingIds(),\n };\n}\n\n/**\n * Convenience wrapper used by the embedded runner pool: install the guard\n * (idempotent), pin the size cap from the model context window, and expose\n * `flushPendingToolResults` / `clearPendingToolResults` directly on the\n * SessionManager instance so callers do not need to keep the install result.\n */\nexport function guardSessionManager(\n sessionManager: SessionManager,\n opts?: {\n agentId?: string;\n sessionKey?: string;\n config?: Config;\n contextWindowTokens?: number;\n allowSyntheticToolResults?: boolean;\n missingToolResultText?: string;\n allowedToolNames?: Iterable<string>;\n },\n): GuardedPiTranscriptManager {\n if (typeof (sessionManager as GuardedPiTranscriptManager).flushPendingToolResults === 'function') {\n return sessionManager as GuardedPiTranscriptManager;\n }\n\n const result = installSessionToolResultGuard(sessionManager, {\n sessionKey: opts?.sessionKey,\n allowSyntheticToolResults: opts?.allowSyntheticToolResults,\n missingToolResultText: opts?.missingToolResultText,\n allowedToolNames: opts?.allowedToolNames,\n maxToolResultChars:\n typeof opts?.contextWindowTokens === 'number'\n ? resolveLiveToolResultMaxChars({\n contextWindowTokens: opts.contextWindowTokens,\n cfg: opts?.config,\n agentId: opts?.agentId,\n })\n : undefined,\n });\n const tagged = sessionManager as GuardedPiTranscriptManager;\n tagged.flushPendingToolResults = result.flushPendingToolResults;\n tagged.clearPendingToolResults = result.clearPendingToolResults;\n return tagged;\n}\n\n/**\n * Recover the original (un-guarded) appendMessage for a session manager.\n * Useful for callers that need a low-level \"bypass the guard\" write path.\n */\nexport function getRawSessionAppendMessage(\n sessionManager: SessionManager,\n): SessionManager['appendMessage'] {\n const stored = (sessionManager as SessionManagerWithRawAppend)[RAW_APPEND_MESSAGE];\n return stored ?? sessionManager.appendMessage.bind(sessionManager);\n}\n\n// ── Internal: persistence + truncation helpers ──────────────────────────────\n\nfunction resolveMaxToolResultChars(opts: { maxToolResultChars?: number }): number {\n return Math.max(1, opts.maxToolResultChars ?? DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS);\n}\n\nfunction capToolResultSize(msg: AgentMessage, maxChars: number): AgentMessage {\n if ((msg as { role?: string }).role !== 'toolResult') {\n return msg;\n }\n return truncateToolResultMessage(msg, maxChars, {\n suffix: (truncatedChars) => formatContextLimitTruncationNotice(truncatedChars),\n minKeepChars: 2_000,\n });\n}\n\n// `details` is runtime/UI metadata, not model-visible tool output. Keep the\n// session JSONL useful for debugging without letting metadata blobs dominate\n// disk, replay repair, transcript broadcasts, or future tooling that reads raw\n// sessions. Model-visible text belongs in tool result `content`.\nconst MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES = 8_192;\nconst MAX_PERSISTED_DETAIL_STRING_CHARS = 2_000;\nconst MAX_PERSISTED_DETAIL_SESSION_COUNT = 10;\nconst MAX_PERSISTED_DETAIL_FALLBACK_STRING_CHARS = 200;\n\nfunction originalDetailsSizeFields(size: BoundedJsonUtf8Bytes): Record<string, number> {\n return size.complete\n ? { originalDetailsBytes: size.bytes }\n : { originalDetailsBytesAtLeast: size.bytes };\n}\n\nfunction truncatePersistedDetailString(\n value: string,\n maxChars = MAX_PERSISTED_DETAIL_STRING_CHARS,\n): string {\n if (value.length <= maxChars) {\n return value;\n }\n return `${value.slice(0, maxChars)}\\n\\n[xopc persisted detail truncated: ${\n value.length - maxChars\n } chars omitted]`;\n}\n\nfunction sanitizePersistedSessionDetail(value: unknown): unknown {\n if (!value || typeof value !== 'object') {\n return value;\n }\n const src = value as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const key of [\n 'sessionId',\n 'status',\n 'pid',\n 'startedAt',\n 'endedAt',\n 'runtimeMs',\n 'cwd',\n 'name',\n 'truncated',\n 'exitCode',\n 'exitSignal',\n ]) {\n const field = src[key];\n if (field !== undefined) {\n out[key] = typeof field === 'string' ? truncatePersistedDetailString(field, 500) : field;\n }\n }\n if (typeof src.command === 'string') {\n out.command = truncatePersistedDetailString(src.command, 500);\n }\n return out;\n}\n\nfunction buildPersistedDetailsFallback(\n src: Record<string, unknown> | undefined,\n originalSize: BoundedJsonUtf8Bytes,\n sanitizedBytes?: number,\n): Record<string, unknown> {\n const fallback: Record<string, unknown> = {\n persistedDetailsTruncated: true,\n finalDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n };\n if (sanitizedBytes !== undefined) {\n fallback.sanitizedDetailsBytes = sanitizedBytes;\n }\n if (src) {\n fallback.originalDetailKeys = firstEnumerableOwnKeys(src, 40);\n for (const key of ['status', 'sessionId', 'pid', 'exitCode', 'exitSignal', 'truncated']) {\n const field = src[key];\n if (field !== undefined) {\n fallback[key] =\n typeof field === 'string'\n ? truncatePersistedDetailString(field, MAX_PERSISTED_DETAIL_FALLBACK_STRING_CHARS)\n : field;\n }\n }\n }\n return fallback;\n}\n\nfunction enforcePersistedDetailsByteCap(\n value: Record<string, unknown>,\n src: Record<string, unknown> | undefined,\n originalSize: BoundedJsonUtf8Bytes,\n): Record<string, unknown> {\n const sanitizedBytes = jsonUtf8BytesOrInfinity(value);\n if (sanitizedBytes <= MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES) {\n return value;\n }\n const fallback = buildPersistedDetailsFallback(src, originalSize, sanitizedBytes);\n if (jsonUtf8BytesOrInfinity(fallback) <= MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES) {\n return fallback;\n }\n return {\n persistedDetailsTruncated: true,\n finalDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n sanitizedDetailsBytes: sanitizedBytes,\n };\n}\n\nfunction sanitizeToolResultDetailsForPersistence(details: unknown): unknown {\n if (details === undefined || details === null) {\n return details;\n }\n const originalSize = boundedJsonUtf8Bytes(details, MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES);\n if (originalSize.complete && originalSize.bytes <= MAX_PERSISTED_TOOL_RESULT_DETAILS_BYTES) {\n return details;\n }\n if (typeof details !== 'object') {\n return enforcePersistedDetailsByteCap(\n {\n persistedDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n valueType: typeof details,\n },\n undefined,\n originalSize,\n );\n }\n const src = details as Record<string, unknown>;\n const out: Record<string, unknown> = {\n persistedDetailsTruncated: true,\n ...originalDetailsSizeFields(originalSize),\n originalDetailKeys: firstEnumerableOwnKeys(src, 40),\n };\n for (const key of [\n 'status',\n 'sessionId',\n 'pid',\n 'startedAt',\n 'endedAt',\n 'cwd',\n 'name',\n 'exitCode',\n 'exitSignal',\n 'retryInMs',\n 'total',\n 'totalLines',\n 'totalChars',\n 'truncated',\n 'fullOutputPath',\n 'truncation',\n ]) {\n const field = src[key];\n if (field !== undefined) {\n out[key] = typeof field === 'string' ? truncatePersistedDetailString(field) : field;\n }\n }\n if (typeof src.tail === 'string') {\n out.tail = truncatePersistedDetailString(src.tail);\n }\n if (Array.isArray(src.sessions)) {\n out.sessions = src.sessions\n .slice(0, MAX_PERSISTED_DETAIL_SESSION_COUNT)\n .map(sanitizePersistedSessionDetail);\n if (src.sessions.length > MAX_PERSISTED_DETAIL_SESSION_COUNT) {\n out.sessionsTruncated = src.sessions.length - MAX_PERSISTED_DETAIL_SESSION_COUNT;\n }\n }\n return enforcePersistedDetailsByteCap(out, src, originalSize);\n}\n\nfunction capToolResultDetails(msg: AgentMessage): AgentMessage {\n if ((msg as { role?: string }).role !== 'toolResult') {\n return msg;\n }\n const details = (msg as { details?: unknown }).details;\n const sanitizedDetails = sanitizeToolResultDetailsForPersistence(details);\n if (sanitizedDetails === details) {\n return msg;\n }\n const next = { ...msg } as AgentMessage & { details?: unknown };\n next.details = sanitizedDetails;\n return next;\n}\n\nfunction capToolResultForPersistence(msg: AgentMessage, maxChars: number): AgentMessage {\n return capToolResultDetails(capToolResultSize(msg, maxChars));\n}\n\nfunction normalizePersistedToolResultName(\n message: AgentMessage,\n fallbackName?: string,\n): AgentMessage {\n if ((message as { role?: unknown }).role !== 'toolResult') {\n return message;\n }\n const toolResult = message as Extract<AgentMessage, { role: 'toolResult' }>;\n const rawToolName = (toolResult as { toolName?: unknown }).toolName;\n const normalizedToolName = normalizeOptionalString(rawToolName);\n if (normalizedToolName) {\n if (rawToolName === normalizedToolName) {\n return toolResult;\n }\n return { ...toolResult, toolName: normalizedToolName };\n }\n\n const normalizedFallback = normalizeOptionalString(fallbackName);\n if (normalizedFallback) {\n return { ...toolResult, toolName: normalizedFallback };\n }\n\n if (typeof rawToolName === 'string') {\n return { ...toolResult, toolName: 'unknown' };\n }\n return toolResult;\n}\n\n// ── Internal: raw-append symbol storage ─────────────────────────────────────\n\nconst RAW_APPEND_MESSAGE = Symbol('xopc.session.rawAppendMessage');\n\ntype SessionManagerWithRawAppend = SessionManager & {\n [RAW_APPEND_MESSAGE]?: SessionManager['appendMessage'];\n};\n\nfunction rememberOriginalAppend(\n sessionManager: SessionManager,\n): SessionManager['appendMessage'] {\n const tagged = sessionManager as SessionManagerWithRawAppend;\n const stored = tagged[RAW_APPEND_MESSAGE];\n if (stored) {\n return stored;\n }\n const original = sessionManager.appendMessage.bind(sessionManager);\n tagged[RAW_APPEND_MESSAGE] = original;\n return original;\n}\n\n// ── Internal: pending tool-call state ──────────────────────────────────────\n\ntype PendingToolCall = { id: string; name?: string };\n\nclass PendingToolCallTracker {\n private readonly pending = new Map<string, string | undefined>();\n\n size(): number {\n return this.pending.size;\n }\n\n getToolName(id: string): string | undefined {\n return this.pending.get(id);\n }\n\n delete(id: string): void {\n this.pending.delete(id);\n }\n\n clear(): void {\n this.pending.clear();\n }\n\n trackToolCalls(calls: readonly PendingToolCall[]): void {\n for (const call of calls) {\n this.pending.set(call.id, call.name);\n }\n }\n\n entries(): IterableIterator<[string, string | undefined]> {\n return this.pending.entries();\n }\n\n getPendingIds(): string[] {\n return Array.from(this.pending.keys());\n }\n\n shouldFlushForSanitizedDrop(): boolean {\n return this.pending.size > 0;\n }\n\n shouldFlushBeforeNonToolResult(nextRole: unknown, toolCallCount: number): boolean {\n return this.pending.size > 0 && (toolCallCount === 0 || nextRole !== 'assistant');\n }\n\n shouldFlushBeforeNewToolCalls(toolCallCount: number): boolean {\n return this.pending.size > 0 && toolCallCount > 0;\n }\n}\n\n// ── ToolResultGuard class ──────────────────────────────────────────────────\n\nclass ToolResultGuard {\n private readonly sessionManager: SessionManager;\n private readonly opts: ToolResultGuardOptions;\n private readonly pending = new PendingToolCallTracker();\n private readonly originalAppend: SessionManager['appendMessage'];\n private readonly allowSyntheticToolResults: boolean;\n private readonly maxToolResultChars: number;\n\n constructor(sessionManager: SessionManager, opts: ToolResultGuardOptions) {\n this.sessionManager = sessionManager;\n this.opts = opts;\n this.originalAppend = rememberOriginalAppend(sessionManager);\n this.allowSyntheticToolResults = opts.allowSyntheticToolResults ?? true;\n this.maxToolResultChars = resolveMaxToolResultChars(opts);\n }\n\n /** Monkey-patch the session manager so pi-coding-agent's internal appendMessage flows through us. */\n attach(): void {\n const bound = this.guardedAppend.bind(this);\n this.sessionManager.appendMessage = bound as SessionManager['appendMessage'];\n }\n\n flushPending(): void {\n if (this.pending.size() === 0) {\n return;\n }\n if (this.allowSyntheticToolResults) {\n for (const [id, name] of this.pending.entries()) {\n const synthetic = makeMissingToolResult({\n toolCallId: id,\n toolName: name,\n text: this.opts.missingToolResultText,\n });\n const flushed = this.applyBeforeWriteHook(\n this.persistToolResult(this.persistMessage(synthetic), {\n toolCallId: id,\n toolName: name,\n isSynthetic: true,\n }),\n );\n if (flushed) {\n this.originalAppend(capToolResultForPersistence(flushed, this.maxToolResultChars) as never);\n }\n }\n }\n this.pending.clear();\n }\n\n clearPending(): void {\n this.pending.clear();\n }\n\n getPendingIds(): string[] {\n return this.pending.getPendingIds();\n }\n\n private persistMessage(message: AgentMessage): AgentMessage {\n const transformer = this.opts.transformMessageForPersistence;\n return transformer ? transformer(message) : message;\n }\n\n private persistToolResult(\n message: AgentMessage,\n meta: { toolCallId?: string; toolName?: string; isSynthetic?: boolean },\n ): AgentMessage {\n const transformer = this.opts.transformToolResultForPersistence;\n return transformer ? transformer(message, meta) : message;\n }\n\n /**\n * Run the before_message_write hook. Returns the (possibly modified) message,\n * or null if the message should be blocked.\n */\n private applyBeforeWriteHook(msg: AgentMessage): AgentMessage | null {\n const beforeWrite = this.opts.beforeMessageWriteHook;\n if (!beforeWrite) {\n return msg;\n }\n const result = beforeWrite({ message: msg });\n if (result?.block) {\n return null;\n }\n if (result?.message) {\n return result.message;\n }\n return msg;\n }\n\n private guardedAppend(message: AgentMessage): unknown {\n let nextMessage = message;\n const role = (message as { role?: unknown }).role;\n if (role === 'assistant') {\n const sanitized = sanitizeToolCallInputs([message], {\n allowedToolNames: this.opts.allowedToolNames,\n });\n if (sanitized.length === 0) {\n if (this.pending.shouldFlushForSanitizedDrop()) {\n this.flushPending();\n }\n return undefined;\n }\n nextMessage = sanitized[0];\n }\n const nextRole = (nextMessage as { role?: unknown }).role;\n\n if (nextRole === 'toolResult') {\n const id = extractToolResultId(nextMessage as Extract<AgentMessage, { role: 'toolResult' }>);\n const toolName = id ? this.pending.getToolName(id) : undefined;\n if (id) {\n this.pending.delete(id);\n }\n const normalizedToolResult = normalizePersistedToolResultName(nextMessage, toolName);\n // Apply hard size cap before persistence to prevent oversized tool results\n // from consuming the entire context window on subsequent LLM calls.\n const capped = capToolResultForPersistence(\n this.persistMessage(normalizedToolResult),\n this.maxToolResultChars,\n );\n const persisted = this.applyBeforeWriteHook(\n this.persistToolResult(capped, {\n toolCallId: id ?? undefined,\n toolName,\n isSynthetic: false,\n }),\n );\n if (!persisted) {\n return undefined;\n }\n return this.originalAppend(capToolResultForPersistence(persisted, this.maxToolResultChars) as never);\n }\n\n // Skip tool call extraction for aborted/errored assistant messages.\n // When stopReason is \"error\" or \"aborted\", the tool_use blocks may be incomplete\n // and should not have synthetic tool_results created. Creating synthetic results\n // for incomplete tool calls causes API 400 errors:\n // \"unexpected tool_use_id found in tool_result blocks\"\n // This matches the behavior in repairToolUseResultPairing (session-transcript-repair.ts)\n const stopReason = (nextMessage as { stopReason?: string }).stopReason;\n const toolCalls =\n nextRole === 'assistant' && stopReason !== 'aborted' && stopReason !== 'error'\n ? extractToolCallsFromAssistant(nextMessage as Extract<AgentMessage, { role: 'assistant' }>)\n : [];\n\n // Always clear pending tool call state before appending non-tool-result messages.\n // flushPendingToolResults() only inserts synthetic results when allowSyntheticToolResults\n // is true; it always clears the pending map. Without this, providers that disable\n // synthetic results (e.g. OpenAI) accumulate stale pending state when a user message\n // interrupts in-flight tool calls, leaving orphaned tool_use blocks in the transcript\n // that cause API 400 errors on subsequent requests.\n if (this.pending.shouldFlushBeforeNonToolResult(nextRole, toolCalls.length)) {\n this.flushPending();\n }\n // If new tool calls arrive while older ones are pending, flush the old ones first.\n if (this.pending.shouldFlushBeforeNewToolCalls(toolCalls.length)) {\n this.flushPending();\n }\n\n const finalMessage = this.applyBeforeWriteHook(this.persistMessage(nextMessage));\n if (!finalMessage) {\n return undefined;\n }\n const result = this.originalAppend(finalMessage as never);\n\n const sessionFile = (\n this.sessionManager as { getSessionFile?: () => string | null }\n ).getSessionFile?.();\n if (sessionFile) {\n emitSessionTranscriptUpdate({\n sessionFile,\n sessionKey: this.opts.sessionKey,\n message: finalMessage,\n messageId: typeof result === 'string' ? result : undefined,\n });\n }\n\n if (toolCalls.length > 0) {\n this.pending.trackToolCalls(toolCalls);\n }\n\n return result;\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
- import { normalizeLowercaseStringOrEmpty } from "../../utils/string-coerce.js";
1
+ import { init_string_coerce, normalizeLowercaseStringOrEmpty } from "../../utils/string-coerce.js";
2
2
  import { formatContextLimitTruncationNotice } from "./tool-result-context-guard.js";
3
3
  //#region src/agent/embedded/tool-result-truncation.ts
4
+ init_string_coerce();
4
5
  const MAX_TOOL_RESULT_CONTEXT_SHARE = .3;
5
6
  const DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS = 16e3;
6
7
  const HARD_MAX_TOOL_RESULT_CHARS = DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS;
@@ -1 +1 @@
1
- {"version":3,"file":"tool-result-truncation.js","names":[],"sources":["../../../../src/agent/embedded/tool-result-truncation.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { TextContent } from '@earendil-works/pi-ai';\n\nimport type { Config } from '../../config/schema.js';\nimport { normalizeLowercaseStringOrEmpty } from '../../utils/string-coerce.js';\nimport { formatContextLimitTruncationNotice } from './tool-result-context-guard.js';\n\nconst MAX_TOOL_RESULT_CONTEXT_SHARE = 0.3;\n\nexport const DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS = 16_000;\nexport const HARD_MAX_TOOL_RESULT_CHARS = DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS;\n\nconst MIN_KEEP_CHARS = 2_000;\n\ntype ToolResultTruncationOptions = {\n suffix?: string | ((truncatedChars: number) => string);\n minKeepChars?: number;\n};\n\nconst DEFAULT_SUFFIX = (truncatedChars: number) => formatContextLimitTruncationNotice(truncatedChars);\n\nfunction resolveSuffixFactory(\n suffix: ToolResultTruncationOptions['suffix'],\n): (truncatedChars: number) => string {\n if (typeof suffix === 'function') {\n return suffix;\n }\n if (typeof suffix === 'string') {\n return () => suffix;\n }\n return DEFAULT_SUFFIX;\n}\n\nfunction resolveEffectiveMinKeepChars(params: {\n maxChars: number;\n minKeepChars: number;\n suffixFactory: (truncatedChars: number) => string;\n}): number {\n const suffixFloor = params.suffixFactory(1).length;\n return Math.max(0, Math.min(params.minKeepChars, Math.max(0, params.maxChars - suffixFloor)));\n}\n\nfunction appendBoundedTruncationSuffix(params: {\n keptText: string;\n originalTextLength: number;\n maxChars: number;\n suffixFactory: (truncatedChars: number) => string;\n}): string {\n const build = (keptText: string) =>\n keptText + params.suffixFactory(Math.max(1, params.originalTextLength - keptText.length));\n\n let keptText = params.keptText;\n while (true) {\n const finalText = build(keptText);\n if (finalText.length <= params.maxChars) {\n return finalText;\n }\n if (keptText.length === 0) {\n return finalText.slice(0, params.maxChars);\n }\n const overflow = finalText.length - params.maxChars;\n const nextKeptText = keptText.slice(0, Math.max(0, keptText.length - overflow));\n keptText = nextKeptText.length < keptText.length ? nextKeptText : keptText.slice(0, -1);\n }\n}\n\nconst MIDDLE_OMISSION_MARKER =\n '\\n\\n[... middle content omitted — showing head and tail ...]\\n\\n';\n\nfunction hasImportantTail(text: string): boolean {\n const tail = normalizeLowercaseStringOrEmpty(text.slice(-2000));\n return (\n /\\b(error|exception|failed|fatal|traceback|panic|stack trace|errno|exit code)\\b/.test(tail) ||\n /\\}\\s*$/.test(tail.trim()) ||\n /\\b(total|summary|result|complete|finished|done)\\b/.test(tail)\n );\n}\n\nexport function truncateToolResultText(\n text: string,\n maxChars: number,\n options: ToolResultTruncationOptions = {},\n): string {\n const suffixFactory = resolveSuffixFactory(options.suffix);\n const minKeepChars = resolveEffectiveMinKeepChars({\n maxChars,\n minKeepChars: options.minKeepChars ?? MIN_KEEP_CHARS,\n suffixFactory,\n });\n if (text.length <= maxChars) {\n return text;\n }\n const defaultSuffix = suffixFactory(Math.max(1, text.length - maxChars));\n const budget = Math.max(minKeepChars, maxChars - defaultSuffix.length);\n\n if (hasImportantTail(text) && budget > minKeepChars * 2) {\n const tailBudget = Math.min(Math.floor(budget * 0.3), 4_000);\n const headBudget = budget - tailBudget - MIDDLE_OMISSION_MARKER.length;\n\n if (headBudget > minKeepChars) {\n let headCut = headBudget;\n const headNewline = text.lastIndexOf('\\n', headBudget);\n if (headNewline > headBudget * 0.8) {\n headCut = headNewline;\n }\n\n let tailStart = text.length - tailBudget;\n const tailNewline = text.indexOf('\\n', tailStart);\n if (tailNewline !== -1 && tailNewline < tailStart + tailBudget * 0.2) {\n tailStart = tailNewline + 1;\n }\n\n const keptText = text.slice(0, headCut) + MIDDLE_OMISSION_MARKER + text.slice(tailStart);\n return appendBoundedTruncationSuffix({\n keptText,\n originalTextLength: text.length,\n maxChars,\n suffixFactory,\n });\n }\n }\n\n let cutPoint = budget;\n const lastNewline = text.lastIndexOf('\\n', budget);\n if (lastNewline > budget * 0.8) {\n cutPoint = lastNewline;\n }\n const keptText = text.slice(0, cutPoint);\n return appendBoundedTruncationSuffix({\n keptText,\n originalTextLength: text.length,\n maxChars,\n suffixFactory,\n });\n}\n\nexport function calculateMaxToolResultCharsWithCap(\n contextWindowTokens: number,\n hardCapChars: number,\n): number {\n const maxTokens = Math.floor(contextWindowTokens * MAX_TOOL_RESULT_CONTEXT_SHARE);\n const maxChars = maxTokens * 4;\n return Math.min(maxChars, Math.max(1, hardCapChars));\n}\n\nexport function resolveLiveToolResultMaxChars(params: {\n contextWindowTokens: number;\n cfg?: Config;\n agentId?: string | null;\n}): number {\n const configuredCap = DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS;\n return calculateMaxToolResultCharsWithCap(params.contextWindowTokens, configuredCap);\n}\n\nexport function getToolResultTextLength(msg: AgentMessage): number {\n if (!msg || (msg as { role?: string }).role !== 'toolResult') {\n return 0;\n }\n const content = (msg as { content?: unknown }).content;\n if (!Array.isArray(content)) {\n return 0;\n }\n let totalLength = 0;\n for (const block of content) {\n if (block && typeof block === 'object' && (block as { type?: string }).type === 'text') {\n const text = (block as TextContent).text;\n if (typeof text === 'string') {\n totalLength += text.length;\n }\n }\n }\n return totalLength;\n}\n\nexport function truncateToolResultMessage(\n msg: AgentMessage,\n maxChars: number,\n options: ToolResultTruncationOptions = {},\n): AgentMessage {\n const suffixFactory = resolveSuffixFactory(options.suffix);\n const minKeepChars = resolveEffectiveMinKeepChars({\n maxChars,\n minKeepChars: options.minKeepChars ?? MIN_KEEP_CHARS,\n suffixFactory,\n });\n const content = (msg as { content?: unknown }).content;\n if (!Array.isArray(content)) {\n return msg;\n }\n\n const totalTextChars = getToolResultTextLength(msg);\n if (totalTextChars <= maxChars) {\n return msg;\n }\n\n const newContent = content.map((block: unknown) => {\n if (!block || typeof block !== 'object' || (block as { type?: string }).type !== 'text') {\n return block;\n }\n const textBlock = block as TextContent;\n if (typeof textBlock.text !== 'string') {\n return block;\n }\n const blockShare = textBlock.text.length / totalTextChars;\n const defaultSuffix = suffixFactory(\n Math.max(1, textBlock.text.length - Math.floor(maxChars * blockShare)),\n );\n const proportionalBudget = Math.floor(maxChars * blockShare);\n const blockBudget = Math.max(\n 1,\n Math.min(maxChars, Math.max(minKeepChars + defaultSuffix.length, proportionalBudget)),\n );\n return Object.assign({}, textBlock, {\n text: truncateToolResultText(textBlock.text, blockBudget, {\n suffix: suffixFactory,\n minKeepChars,\n }),\n });\n });\n\n return { ...msg, content: newContent } as AgentMessage;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tool-result-truncation.js","names":[],"sources":["../../../../src/agent/embedded/tool-result-truncation.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { TextContent } from '@earendil-works/pi-ai';\n\nimport type { Config } from '../../config/schema.js';\nimport { normalizeLowercaseStringOrEmpty } from '../../utils/string-coerce.js';\nimport { formatContextLimitTruncationNotice } from './tool-result-context-guard.js';\n\nconst MAX_TOOL_RESULT_CONTEXT_SHARE = 0.3;\n\nexport const DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS = 16_000;\nexport const HARD_MAX_TOOL_RESULT_CHARS = DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS;\n\nconst MIN_KEEP_CHARS = 2_000;\n\ntype ToolResultTruncationOptions = {\n suffix?: string | ((truncatedChars: number) => string);\n minKeepChars?: number;\n};\n\nconst DEFAULT_SUFFIX = (truncatedChars: number) => formatContextLimitTruncationNotice(truncatedChars);\n\nfunction resolveSuffixFactory(\n suffix: ToolResultTruncationOptions['suffix'],\n): (truncatedChars: number) => string {\n if (typeof suffix === 'function') {\n return suffix;\n }\n if (typeof suffix === 'string') {\n return () => suffix;\n }\n return DEFAULT_SUFFIX;\n}\n\nfunction resolveEffectiveMinKeepChars(params: {\n maxChars: number;\n minKeepChars: number;\n suffixFactory: (truncatedChars: number) => string;\n}): number {\n const suffixFloor = params.suffixFactory(1).length;\n return Math.max(0, Math.min(params.minKeepChars, Math.max(0, params.maxChars - suffixFloor)));\n}\n\nfunction appendBoundedTruncationSuffix(params: {\n keptText: string;\n originalTextLength: number;\n maxChars: number;\n suffixFactory: (truncatedChars: number) => string;\n}): string {\n const build = (keptText: string) =>\n keptText + params.suffixFactory(Math.max(1, params.originalTextLength - keptText.length));\n\n let keptText = params.keptText;\n while (true) {\n const finalText = build(keptText);\n if (finalText.length <= params.maxChars) {\n return finalText;\n }\n if (keptText.length === 0) {\n return finalText.slice(0, params.maxChars);\n }\n const overflow = finalText.length - params.maxChars;\n const nextKeptText = keptText.slice(0, Math.max(0, keptText.length - overflow));\n keptText = nextKeptText.length < keptText.length ? nextKeptText : keptText.slice(0, -1);\n }\n}\n\nconst MIDDLE_OMISSION_MARKER =\n '\\n\\n[... middle content omitted — showing head and tail ...]\\n\\n';\n\nfunction hasImportantTail(text: string): boolean {\n const tail = normalizeLowercaseStringOrEmpty(text.slice(-2000));\n return (\n /\\b(error|exception|failed|fatal|traceback|panic|stack trace|errno|exit code)\\b/.test(tail) ||\n /\\}\\s*$/.test(tail.trim()) ||\n /\\b(total|summary|result|complete|finished|done)\\b/.test(tail)\n );\n}\n\nexport function truncateToolResultText(\n text: string,\n maxChars: number,\n options: ToolResultTruncationOptions = {},\n): string {\n const suffixFactory = resolveSuffixFactory(options.suffix);\n const minKeepChars = resolveEffectiveMinKeepChars({\n maxChars,\n minKeepChars: options.minKeepChars ?? MIN_KEEP_CHARS,\n suffixFactory,\n });\n if (text.length <= maxChars) {\n return text;\n }\n const defaultSuffix = suffixFactory(Math.max(1, text.length - maxChars));\n const budget = Math.max(minKeepChars, maxChars - defaultSuffix.length);\n\n if (hasImportantTail(text) && budget > minKeepChars * 2) {\n const tailBudget = Math.min(Math.floor(budget * 0.3), 4_000);\n const headBudget = budget - tailBudget - MIDDLE_OMISSION_MARKER.length;\n\n if (headBudget > minKeepChars) {\n let headCut = headBudget;\n const headNewline = text.lastIndexOf('\\n', headBudget);\n if (headNewline > headBudget * 0.8) {\n headCut = headNewline;\n }\n\n let tailStart = text.length - tailBudget;\n const tailNewline = text.indexOf('\\n', tailStart);\n if (tailNewline !== -1 && tailNewline < tailStart + tailBudget * 0.2) {\n tailStart = tailNewline + 1;\n }\n\n const keptText = text.slice(0, headCut) + MIDDLE_OMISSION_MARKER + text.slice(tailStart);\n return appendBoundedTruncationSuffix({\n keptText,\n originalTextLength: text.length,\n maxChars,\n suffixFactory,\n });\n }\n }\n\n let cutPoint = budget;\n const lastNewline = text.lastIndexOf('\\n', budget);\n if (lastNewline > budget * 0.8) {\n cutPoint = lastNewline;\n }\n const keptText = text.slice(0, cutPoint);\n return appendBoundedTruncationSuffix({\n keptText,\n originalTextLength: text.length,\n maxChars,\n suffixFactory,\n });\n}\n\nexport function calculateMaxToolResultCharsWithCap(\n contextWindowTokens: number,\n hardCapChars: number,\n): number {\n const maxTokens = Math.floor(contextWindowTokens * MAX_TOOL_RESULT_CONTEXT_SHARE);\n const maxChars = maxTokens * 4;\n return Math.min(maxChars, Math.max(1, hardCapChars));\n}\n\nexport function resolveLiveToolResultMaxChars(params: {\n contextWindowTokens: number;\n cfg?: Config;\n agentId?: string | null;\n}): number {\n const configuredCap = DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS;\n return calculateMaxToolResultCharsWithCap(params.contextWindowTokens, configuredCap);\n}\n\nexport function getToolResultTextLength(msg: AgentMessage): number {\n if (!msg || (msg as { role?: string }).role !== 'toolResult') {\n return 0;\n }\n const content = (msg as { content?: unknown }).content;\n if (!Array.isArray(content)) {\n return 0;\n }\n let totalLength = 0;\n for (const block of content) {\n if (block && typeof block === 'object' && (block as { type?: string }).type === 'text') {\n const text = (block as TextContent).text;\n if (typeof text === 'string') {\n totalLength += text.length;\n }\n }\n }\n return totalLength;\n}\n\nexport function truncateToolResultMessage(\n msg: AgentMessage,\n maxChars: number,\n options: ToolResultTruncationOptions = {},\n): AgentMessage {\n const suffixFactory = resolveSuffixFactory(options.suffix);\n const minKeepChars = resolveEffectiveMinKeepChars({\n maxChars,\n minKeepChars: options.minKeepChars ?? MIN_KEEP_CHARS,\n suffixFactory,\n });\n const content = (msg as { content?: unknown }).content;\n if (!Array.isArray(content)) {\n return msg;\n }\n\n const totalTextChars = getToolResultTextLength(msg);\n if (totalTextChars <= maxChars) {\n return msg;\n }\n\n const newContent = content.map((block: unknown) => {\n if (!block || typeof block !== 'object' || (block as { type?: string }).type !== 'text') {\n return block;\n }\n const textBlock = block as TextContent;\n if (typeof textBlock.text !== 'string') {\n return block;\n }\n const blockShare = textBlock.text.length / totalTextChars;\n const defaultSuffix = suffixFactory(\n Math.max(1, textBlock.text.length - Math.floor(maxChars * blockShare)),\n );\n const proportionalBudget = Math.floor(maxChars * blockShare);\n const blockBudget = Math.max(\n 1,\n Math.min(maxChars, Math.max(minKeepChars + defaultSuffix.length, proportionalBudget)),\n );\n return Object.assign({}, textBlock, {\n text: truncateToolResultText(textBlock.text, blockBudget, {\n suffix: suffixFactory,\n minKeepChars,\n }),\n });\n });\n\n return { ...msg, content: newContent } as AgentMessage;\n}\n"],"mappings":"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"}
@@ -24,8 +24,8 @@ function parseModelRef(raw, defaultProvider) {
24
24
  function resolveFallbackCandidates(params) {
25
25
  const { cfg, provider: inputProvider, model: inputModel, fallbacksOverride } = params;
26
26
  const modelConfig = cfg?.agents?.defaults?.model;
27
- const primaryRef = typeof modelConfig === "string" ? modelConfig : modelConfig?.primary;
28
- const fallbacks = fallbacksOverride ?? (typeof modelConfig === "object" ? modelConfig.fallbacks : void 0);
27
+ const primaryRef = modelConfig?.primary;
28
+ const fallbacks = fallbacksOverride ?? modelConfig?.fallbacks;
29
29
  const defaultParts = getDefaultModelParts(cfg);
30
30
  const primaryResolved = parseModelRef(primaryRef || getDefaultModelSync(cfg));
31
31
  const provider = inputProvider.trim() || primaryResolved?.provider || defaultParts.provider;