@xopcai/xopc 0.0.86 → 0.0.88

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 (658) 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/feishu/src/outbound/media-load.js +1 -1
  5. package/dist/extensions/feishu/src/workflow-progress.js +1 -1
  6. package/dist/extensions/telegram/src/delivery-chat-id.d.ts +1 -1
  7. package/dist/extensions/telegram/src/delivery-chat-id.js +1 -1
  8. package/dist/extensions/telegram/src/delivery-chat-id.js.map +1 -1
  9. package/dist/extensions/telegram/src/plugin.js +1 -1
  10. package/dist/extensions/telegram/src/routing-integration.js +3 -2
  11. package/dist/extensions/telegram/src/routing-integration.js.map +1 -1
  12. package/dist/extensions/telegram/src/workflow-progress.js +1 -1
  13. package/dist/extensions/telegram/xopc.extension.json +1 -1
  14. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js +2 -2
  15. package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js.map +1 -1
  16. package/dist/extensions/weixin/src/api/api.js +3 -3
  17. package/dist/extensions/weixin/src/api/api.js.map +1 -1
  18. package/dist/extensions/weixin/src/auth/accounts.js +12 -12
  19. package/dist/extensions/weixin/src/auth/accounts.js.map +1 -1
  20. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  21. package/dist/extensions/weixin/src/delivery-to.js +2 -2
  22. package/dist/extensions/weixin/src/delivery-to.js.map +1 -1
  23. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  24. package/dist/extensions/weixin/src/messaging/debug-mode.js +5 -5
  25. package/dist/extensions/weixin/src/messaging/debug-mode.js.map +1 -1
  26. package/dist/extensions/weixin/src/messaging/inbound.js +11 -11
  27. package/dist/extensions/weixin/src/messaging/inbound.js.map +1 -1
  28. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  29. package/dist/extensions/weixin/src/plugin.js +1 -1
  30. package/dist/extensions/weixin/src/storage/sync-buf.js +4 -4
  31. package/dist/extensions/weixin/src/storage/sync-buf.js.map +1 -1
  32. package/dist/extensions/weixin/src/workflow-progress.d.ts +1 -1
  33. package/dist/extensions/weixin/src/workflow-progress.js +1 -1
  34. package/dist/extensions/weixin/src/workflow-progress.js.map +1 -1
  35. package/dist/gateway/static/root/assets/agents-CRxETUZx.js +222 -0
  36. package/dist/gateway/static/root/assets/{apps-page-DrfytjOb.js → apps-page-wKWf3l57.js} +1 -1
  37. package/dist/gateway/static/root/assets/channels-settings-DDbqVNkx.js +1 -0
  38. package/dist/gateway/static/root/assets/{channels-status-swr-Bs5kMCMI.js → channels-status-swr-DIsl75Y3.js} +1 -1
  39. package/dist/gateway/static/root/assets/copy-SxMW6Xpc.js +1 -0
  40. package/dist/gateway/static/root/assets/{cron-api-BuVcZ5zR.js → cron-api-N9hvuRrn.js} +1 -1
  41. package/dist/gateway/static/root/assets/{cron-page-BMrloeFH.js → cron-page-tlNGNxhP.js} +1 -1
  42. package/dist/gateway/static/root/assets/{dist-CKU1OOTf.js → dist-CJwfHYvT.js} +1 -1
  43. package/dist/gateway/static/root/assets/{extension-debug-page-BdW_46sN.js → extension-debug-page-BVJohZoZ.js} +1 -1
  44. package/dist/gateway/static/root/assets/{extension-page-DW47KI82.js → extension-page-BT2tmElC.js} +1 -1
  45. package/dist/gateway/static/root/assets/extension-settings-page-BSS47c2j.js +1 -0
  46. package/dist/gateway/static/root/assets/{fetch-B2MYHbWg.js → fetch-BaFNUtkE.js} +1 -1
  47. package/dist/gateway/static/root/assets/{field-primitives-DPG-oJmx.js → field-primitives-QwYEq6Hz.js} +1 -1
  48. package/dist/gateway/static/root/assets/{heartbeat-config-api-C8dNts9i.js → heartbeat-config-api-BVSidEDJ.js} +1 -1
  49. package/dist/gateway/static/root/assets/index-CqZzHNEg.css +1 -0
  50. package/dist/gateway/static/root/assets/{index-BmVYculr.js → index-qNrVJp-y.js} +97 -95
  51. package/dist/gateway/static/root/assets/{logs-page-sTsVWz0X.js → logs-page-DDonPVLn.js} +1 -1
  52. package/dist/gateway/static/root/assets/sessions-page-DKt-Wmib.js +1 -0
  53. package/dist/gateway/static/root/assets/{settings-form-section-DuvRQW--.js → settings-form-section-B8N3A3Zo.js} +1 -1
  54. package/dist/gateway/static/root/assets/settings-page-DcJjvvw4.js +3 -0
  55. package/dist/gateway/static/root/assets/{share-preview-page-BtG2kLDh.js → share-preview-page-Q7KqkO-u.js} +1 -1
  56. package/dist/gateway/static/root/assets/skills-page-DuJ4BTO3.js +2 -0
  57. package/dist/gateway/static/root/assets/{theme-store-DryYl3qD.js → theme-store-BbRc5ugR.js} +1 -1
  58. package/dist/gateway/static/root/assets/url-D6jvVYIA.js +7 -0
  59. package/dist/gateway/static/root/assets/{utils-BY7bU1DT.js → utils-CxDGduqK.js} +1 -1
  60. package/dist/gateway/static/root/assets/voice-api-key-field-CTyHz7L_.js +1 -0
  61. package/dist/gateway/static/root/assets/workflows-page-GacJ41Fv.js +27 -0
  62. package/dist/gateway/static/root/index.html +6 -5
  63. package/dist/package.js +1 -1
  64. package/dist/src/agent/agent-manager.js +7 -7
  65. package/dist/src/agent/agent-scope.d.ts +4 -0
  66. package/dist/src/agent/agent-scope.js +53 -10
  67. package/dist/src/agent/agent-scope.js.map +1 -1
  68. package/dist/src/agent/bootstrap/filter-bootstrap-files.js +2 -1
  69. package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
  70. package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
  71. package/dist/src/agent/child-agent-factory.d.ts +15 -0
  72. package/dist/src/agent/child-agent-factory.js +35 -2
  73. package/dist/src/agent/child-agent-factory.js.map +1 -1
  74. package/dist/src/agent/client-error-format.d.ts +20 -0
  75. package/dist/src/agent/client-error-format.js +97 -0
  76. package/dist/src/agent/client-error-format.js.map +1 -0
  77. package/dist/src/agent/context/workspace-seed.js +2 -2
  78. package/dist/src/agent/embedded/run-turn.js +23 -4
  79. package/dist/src/agent/embedded/run-turn.js.map +1 -1
  80. package/dist/src/agent/embedded/session-tool-result-guard.js +2 -1
  81. package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
  82. package/dist/src/agent/embedded/tool-result-truncation.js +2 -1
  83. package/dist/src/agent/embedded/tool-result-truncation.js.map +1 -1
  84. package/dist/src/agent/fallback/candidates.js +2 -2
  85. package/dist/src/agent/fallback/candidates.js.map +1 -1
  86. package/dist/src/agent/goals/goal-locale.d.ts +1 -1
  87. package/dist/src/agent/goals/goal-run-store.js +4 -4
  88. package/dist/src/agent/goals/persistent-goal-apis.d.ts +0 -2
  89. package/dist/src/agent/goals/persistent-goal-service.js +1 -2
  90. package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
  91. package/dist/src/agent/goals/post-turn.js +2 -2
  92. package/dist/src/agent/image/generation/normalization.js +2 -12
  93. package/dist/src/agent/image/generation/normalization.js.map +1 -1
  94. package/dist/src/agent/image/generation/provider-registry.d.ts +4 -8
  95. package/dist/src/agent/image/generation/provider-registry.js.map +1 -1
  96. package/dist/src/agent/image/generation/runtime.d.ts +2 -2
  97. package/dist/src/agent/image/generation/runtime.js.map +1 -1
  98. package/dist/src/agent/image/generation/types.d.ts +0 -18
  99. package/dist/src/agent/image/image-helpers.js +6 -1
  100. package/dist/src/agent/image/image-helpers.js.map +1 -1
  101. package/dist/src/agent/image/index.d.ts +1 -1
  102. package/dist/src/agent/image/load-image-media.js +2 -2
  103. package/dist/src/agent/inbound/inbound-loop.d.ts +5 -0
  104. package/dist/src/agent/inbound/inbound-loop.js +41 -10
  105. package/dist/src/agent/inbound/inbound-loop.js.map +1 -1
  106. package/dist/src/agent/inbound/turn-dispatcher.d.ts +4 -0
  107. package/dist/src/agent/inbound/turn-dispatcher.js +7 -5
  108. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  109. package/dist/src/agent/ipc/bus.js +1 -1
  110. package/dist/src/agent/ipc/inbox.js +2 -2
  111. package/dist/src/agent/ipc/socket.js +1 -1
  112. package/dist/src/agent/mcp/bundle-mcp-materialize.js +2 -1
  113. package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
  114. package/dist/src/agent/mcp/bundle-mcp-names.js +2 -1
  115. package/dist/src/agent/mcp/bundle-mcp-names.js.map +1 -1
  116. package/dist/src/agent/mcp/bundle-mcp-runtime.js +2 -1
  117. package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
  118. package/dist/src/agent/mcp/mcp-transport-config.js +2 -1
  119. package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
  120. package/dist/src/agent/mcp/mcp-transport.js +2 -1
  121. package/dist/src/agent/mcp/mcp-transport.js.map +1 -1
  122. package/dist/src/agent/media-generation/runtime-shared.js +2 -9
  123. package/dist/src/agent/media-generation/runtime-shared.js.map +1 -1
  124. package/dist/src/agent/memory/builtin-memory-store.js +1 -1
  125. package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
  126. package/dist/src/agent/memory/dreaming/events.js +1 -1
  127. package/dist/src/agent/memory/dreaming/last-run.js +1 -1
  128. package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
  129. package/dist/src/agent/memory/dreaming/preview.js +1 -1
  130. package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
  131. package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
  132. package/dist/src/agent/memory/dreaming/utils.js +1 -1
  133. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  134. package/dist/src/agent/messaging/command-handler.d.ts +6 -0
  135. package/dist/src/agent/messaging/command-handler.js +5 -0
  136. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  137. package/dist/src/agent/models/manager.js +1 -1
  138. package/dist/src/agent/orchestration/llm-turn-retry.d.ts +2 -0
  139. package/dist/src/agent/orchestration/llm-turn-retry.js +9 -1
  140. package/dist/src/agent/orchestration/llm-turn-retry.js.map +1 -1
  141. package/dist/src/agent/prompt/safety.d.ts +0 -7
  142. package/dist/src/agent/prompt/safety.js +1 -20
  143. package/dist/src/agent/prompt/safety.js.map +1 -1
  144. package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
  145. package/dist/src/agent/reply/post-compaction-context.js +1 -1
  146. package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
  147. package/dist/src/agent/sandbox/path-policy.js +2 -2
  148. package/dist/src/agent/service/build-direct-message-content.js +2 -2
  149. package/dist/src/agent/service/build-direct-message-content.js.map +1 -1
  150. package/dist/src/agent/service/direct-turn-helpers.d.ts +3 -1
  151. package/dist/src/agent/service/direct-turn-helpers.js +6 -1
  152. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  153. package/dist/src/agent/service/process-direct-one-shot.d.ts +4 -0
  154. package/dist/src/agent/service/process-direct-one-shot.js +15 -2
  155. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  156. package/dist/src/agent/service/process-direct-streaming.d.ts +4 -0
  157. package/dist/src/agent/service/process-direct-streaming.js +53 -7
  158. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  159. package/dist/src/agent/service/webchat-tts.d.ts +1 -2
  160. package/dist/src/agent/service/webchat-tts.js +2 -2
  161. package/dist/src/agent/service/webchat-tts.js.map +1 -1
  162. package/dist/src/agent/service.d.ts +8 -0
  163. package/dist/src/agent/service.js +25 -5
  164. package/dist/src/agent/service.js.map +1 -1
  165. package/dist/src/agent/session/session-inspector.js +1 -1
  166. package/dist/src/agent/skills/config.js +1 -1
  167. package/dist/src/agent/skills/hub-hash.js +2 -2
  168. package/dist/src/agent/skills/hub-lock.js +1 -1
  169. package/dist/src/agent/skills/hub-pull.js +2 -2
  170. package/dist/src/agent/skills/index.js +1 -1
  171. package/dist/src/agent/skills/managed-store.js +1 -1
  172. package/dist/src/agent/skills/scanner.js +1 -1
  173. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  174. package/dist/src/agent/skills/skill-manager.js +1 -1
  175. package/dist/src/agent/tools/create-share-tool.js +27 -20
  176. package/dist/src/agent/tools/create-share-tool.js.map +1 -1
  177. package/dist/src/agent/tools/dreaming-tool.js +1 -1
  178. package/dist/src/agent/tools/factory.js +2 -2
  179. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  180. package/dist/src/agent/tools/index.d.ts +0 -1
  181. package/dist/src/agent/tools/index.js +4 -5
  182. package/dist/src/agent/tools/send-media.js +1 -1
  183. package/dist/src/agent/tools/shell.js +0 -13
  184. package/dist/src/agent/tools/shell.js.map +1 -1
  185. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  186. package/dist/src/agent/tools/workflow-tool.js +70 -16
  187. package/dist/src/agent/tools/workflow-tool.js.map +1 -1
  188. package/dist/src/agent/tools/write.js +1 -1
  189. package/dist/src/agent/workflow/agent-progress.d.ts +5 -0
  190. package/dist/src/agent/workflow/agent-progress.js +65 -0
  191. package/dist/src/agent/workflow/agent-progress.js.map +1 -0
  192. package/dist/src/agent/workflow/builtins/audit-repo.d.ts +1 -1
  193. package/dist/src/agent/workflow/builtins/audit-repo.js +14 -0
  194. package/dist/src/agent/workflow/builtins/audit-repo.js.map +1 -1
  195. package/dist/src/agent/workflow/builtins/debug-incident.d.ts +1 -1
  196. package/dist/src/agent/workflow/builtins/debug-incident.js +14 -0
  197. package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -1
  198. package/dist/src/agent/workflow/builtins/implementation-plan.d.ts +12 -0
  199. package/dist/src/agent/workflow/builtins/implementation-plan.js +175 -0
  200. package/dist/src/agent/workflow/builtins/implementation-plan.js.map +1 -0
  201. package/dist/src/agent/workflow/builtins/index.d.ts +3 -1
  202. package/dist/src/agent/workflow/builtins/index.js +11 -1
  203. package/dist/src/agent/workflow/builtins/index.js.map +1 -1
  204. package/dist/src/agent/workflow/builtins/multi-perspective-review.d.ts +1 -1
  205. package/dist/src/agent/workflow/builtins/multi-perspective-review.js +14 -0
  206. package/dist/src/agent/workflow/builtins/multi-perspective-review.js.map +1 -1
  207. package/dist/src/agent/workflow/builtins/pr-review.d.ts +1 -1
  208. package/dist/src/agent/workflow/builtins/pr-review.js +14 -0
  209. package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -1
  210. package/dist/src/agent/workflow/builtins/release-check.d.ts +11 -0
  211. package/dist/src/agent/workflow/builtins/release-check.js +165 -0
  212. package/dist/src/agent/workflow/builtins/release-check.js.map +1 -0
  213. package/dist/src/agent/workflow/builtins/research.d.ts +1 -1
  214. package/dist/src/agent/workflow/builtins/research.js +14 -0
  215. package/dist/src/agent/workflow/builtins/research.js.map +1 -1
  216. package/dist/src/agent/workflow/catalog.js +1 -1
  217. package/dist/src/agent/workflow/channel-capability.d.ts +3 -3
  218. package/dist/src/agent/workflow/index.d.ts +2 -1
  219. package/dist/src/agent/workflow/index.js +3 -2
  220. package/dist/src/agent/workflow/lint.d.ts +38 -0
  221. package/dist/src/agent/workflow/lint.js +74 -0
  222. package/dist/src/agent/workflow/lint.js.map +1 -0
  223. package/dist/src/agent/workflow/meta-locale.d.ts +12 -0
  224. package/dist/src/agent/workflow/meta-locale.js +62 -0
  225. package/dist/src/agent/workflow/meta-locale.js.map +1 -0
  226. package/dist/src/agent/workflow/parser.js +7 -1
  227. package/dist/src/agent/workflow/parser.js.map +1 -1
  228. package/dist/src/agent/workflow/runtime.d.ts +4 -1
  229. package/dist/src/agent/workflow/runtime.js +88 -8
  230. package/dist/src/agent/workflow/runtime.js.map +1 -1
  231. package/dist/src/agent/workflow/snapshot.js +2 -12
  232. package/dist/src/agent/workflow/snapshot.js.map +1 -1
  233. package/dist/src/agent/workflow/step-labels.d.ts +8 -0
  234. package/dist/src/agent/workflow/step-labels.js +48 -0
  235. package/dist/src/agent/workflow/step-labels.js.map +1 -0
  236. package/dist/src/agent/workflow/subagent-runner.js +46 -1
  237. package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
  238. package/dist/src/agent/workflow/types.d.ts +76 -1
  239. package/dist/src/auth/credentials.d.ts +5 -0
  240. package/dist/src/auth/credentials.js +12 -3
  241. package/dist/src/auth/credentials.js.map +1 -1
  242. package/dist/src/auth/profiles/store.js +1 -1
  243. package/dist/src/auth/sync-provider-auth.js +1 -1
  244. package/dist/src/browser/cache-dir-policy.js +1 -1
  245. package/dist/src/browser/cdp-local-launcher.js +2 -2
  246. package/dist/src/browser/index.js +4 -4
  247. package/dist/src/browser/manager.d.ts +1 -3
  248. package/dist/src/browser/manager.js +0 -6
  249. package/dist/src/browser/manager.js.map +1 -1
  250. package/dist/src/browser/providers/browser-ext-install.d.ts +4 -4
  251. package/dist/src/browser/providers/browser-ext-install.js +41 -88
  252. package/dist/src/browser/providers/browser-ext-install.js.map +1 -1
  253. package/dist/src/browser/providers/cloakbrowser.d.ts +0 -5
  254. package/dist/src/browser/providers/cloakbrowser.js +6 -59
  255. package/dist/src/browser/providers/cloakbrowser.js.map +1 -1
  256. package/dist/src/browser/providers/playwright-doctor.js +1 -1
  257. package/dist/src/browser/stealth.js +1 -1
  258. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  259. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  260. package/dist/src/channels/attachments/voice-stt-webchat.js +10 -8
  261. package/dist/src/channels/attachments/voice-stt-webchat.js.map +1 -1
  262. package/dist/src/channels/outbound/persist-store.js +1 -1
  263. package/dist/src/channels/pairing/allow-from-file.js +9 -9
  264. package/dist/src/channels/pairing/allow-from-file.js.map +1 -1
  265. package/dist/src/channels/pairing/pairing-store.js +7 -7
  266. package/dist/src/channels/pairing/pairing-store.js.map +1 -1
  267. package/dist/src/chat-commands/builtins/config.js +2 -2
  268. package/dist/src/chat-commands/builtins/session.js +1 -1
  269. package/dist/src/chat-commands/builtins/session.js.map +1 -1
  270. package/dist/src/chat-commands/builtins/tts.js +2 -2
  271. package/dist/src/chat-commands/builtins/tts.js.map +1 -1
  272. package/dist/src/chat-commands/context.d.ts +3 -0
  273. package/dist/src/chat-commands/context.js +22 -4
  274. package/dist/src/chat-commands/context.js.map +1 -1
  275. package/dist/src/chat-commands/session-key.d.ts +4 -37
  276. package/dist/src/chat-commands/session-key.js +49 -85
  277. package/dist/src/chat-commands/session-key.js.map +1 -1
  278. package/dist/src/chat-commands/types.d.ts +2 -0
  279. package/dist/src/cli/commands/agent/interactive.js +2 -2
  280. package/dist/src/cli/commands/agent/interactive.js.map +1 -1
  281. package/dist/src/cli/commands/agent/sessions.js +2 -2
  282. package/dist/src/cli/commands/agent/sessions.js.map +1 -1
  283. package/dist/src/cli/commands/agent.js +4 -5
  284. package/dist/src/cli/commands/agent.js.map +1 -1
  285. package/dist/src/cli/commands/channels.js +1 -5
  286. package/dist/src/cli/commands/channels.js.map +1 -1
  287. package/dist/src/cli/commands/config.js +1 -1
  288. package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
  289. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  290. package/dist/src/cli/commands/doctor/checks/session-integrity.js +1 -1
  291. package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
  292. package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
  293. package/dist/src/cli/commands/extension-dev.js +1 -1
  294. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  295. package/dist/src/cli/commands/extension-pack.js +1 -1
  296. package/dist/src/cli/commands/gateway/lifecycle-core.js +1 -1
  297. package/dist/src/cli/commands/gateway/lifecycle-core.js.map +1 -1
  298. package/dist/src/cli/commands/gateway/logs.d.ts +9 -0
  299. package/dist/src/cli/commands/gateway/logs.js +50 -17
  300. package/dist/src/cli/commands/gateway/logs.js.map +1 -1
  301. package/dist/src/cli/commands/image.js +23 -22
  302. package/dist/src/cli/commands/image.js.map +1 -1
  303. package/dist/src/cli/commands/init.js +4 -4
  304. package/dist/src/cli/commands/onboard.js +1 -1
  305. package/dist/src/cli/commands/session/utils.js +2 -2
  306. package/dist/src/cli/commands/session/utils.js.map +1 -1
  307. package/dist/src/cli/commands/update.js +26 -46
  308. package/dist/src/cli/commands/update.js.map +1 -1
  309. package/dist/src/cli/utils/init-workspace-core.js +2 -2
  310. package/dist/src/cli/utils/session.d.ts +0 -5
  311. package/dist/src/cli/utils/session.js +1 -6
  312. package/dist/src/cli/utils/session.js.map +1 -1
  313. package/dist/src/commands/agents.config.js +1 -1
  314. package/dist/src/commands/agents.config.js.map +1 -1
  315. package/dist/src/config/agent-profile.js +6 -28
  316. package/dist/src/config/agent-profile.js.map +1 -1
  317. package/dist/src/config/agent-typed-models.d.ts +18 -0
  318. package/dist/src/config/agent-typed-models.js +53 -0
  319. package/dist/src/config/agent-typed-models.js.map +1 -0
  320. package/dist/src/config/gateway-bind.js +1 -1
  321. package/dist/src/config/index.js +6 -6
  322. package/dist/src/config/loader.js +2 -2
  323. package/dist/src/config/model-input.js +2 -5
  324. package/dist/src/config/model-input.js.map +1 -1
  325. package/dist/src/config/models-json.js +2 -2
  326. package/dist/src/config/paths-state.js +1 -1
  327. package/dist/src/config/profile.js +2 -2
  328. package/dist/src/config/schema.d.ts +253 -217
  329. package/dist/src/config/schema.js +91 -40
  330. package/dist/src/config/schema.js.map +1 -1
  331. package/dist/src/config/voice.d.ts +3 -28
  332. package/dist/src/config/voice.js +27 -261
  333. package/dist/src/config/voice.js.map +1 -1
  334. package/dist/src/config/workspace-path-helpers.d.ts +1 -2
  335. package/dist/src/config/workspace-path-helpers.js.map +1 -1
  336. package/dist/src/config/workspace-path.js +1 -1
  337. package/dist/src/cron/executor.js +2 -2
  338. package/dist/src/cron/persistence.js +1 -1
  339. package/dist/src/cron/run-log-store.js +1 -1
  340. package/dist/src/daemon/constants.js +1 -1
  341. package/dist/src/daemon/install-plan.js +27 -3
  342. package/dist/src/daemon/install-plan.js.map +1 -1
  343. package/dist/src/daemon/launchd.d.ts +8 -0
  344. package/dist/src/daemon/launchd.js +7 -14
  345. package/dist/src/daemon/launchd.js.map +1 -1
  346. package/dist/src/daemon/schtasks.d.ts +25 -0
  347. package/dist/src/daemon/schtasks.js +168 -48
  348. package/dist/src/daemon/schtasks.js.map +1 -1
  349. package/dist/src/daemon/service.js +5 -4
  350. package/dist/src/daemon/service.js.map +1 -1
  351. package/dist/src/daemon/systemd.d.ts +6 -0
  352. package/dist/src/daemon/systemd.js +20 -5
  353. package/dist/src/daemon/systemd.js.map +1 -1
  354. package/dist/src/extensions/activation-context.js +0 -1
  355. package/dist/src/extensions/activation-context.js.map +1 -1
  356. package/dist/src/extensions/bundle-mcp.js +1 -1
  357. package/dist/src/extensions/discover-extensions.js +1 -1
  358. package/dist/src/extensions/health.js +1 -1
  359. package/dist/src/extensions/loader.js +1 -1
  360. package/dist/src/extensions/lockfile.js +2 -2
  361. package/dist/src/extensions/normalize-manifest.js +0 -1
  362. package/dist/src/extensions/normalize-manifest.js.map +1 -1
  363. package/dist/src/extensions/types/manifest.d.ts +0 -2
  364. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  365. package/dist/src/gateway/agent-builtin-tools.js +1 -0
  366. package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
  367. package/dist/src/gateway/agents-admin.d.ts +9 -0
  368. package/dist/src/gateway/agents-admin.js +28 -4
  369. package/dist/src/gateway/agents-admin.js.map +1 -1
  370. package/dist/src/gateway/config-tools-web.js +3 -2
  371. package/dist/src/gateway/config-tools-web.js.map +1 -1
  372. package/dist/src/gateway/file-path-classifier.js +2 -2
  373. package/dist/src/gateway/heartbeat/service.js +2 -2
  374. package/dist/src/gateway/heartbeat/service.js.map +1 -1
  375. package/dist/src/gateway/hono/app.js +1 -1
  376. package/dist/src/gateway/hono/lib/agent-model.d.ts +25 -10
  377. package/dist/src/gateway/hono/lib/agent-model.js +60 -36
  378. package/dist/src/gateway/hono/lib/agent-model.js.map +1 -1
  379. package/dist/src/gateway/hono/lib/config-payload.js +29 -6
  380. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  381. package/dist/src/gateway/hono/lib/extension-store.js +2 -2
  382. package/dist/src/gateway/hono/lib/mask-secret-length.d.ts +6 -0
  383. package/dist/src/gateway/hono/lib/mask-secret-length.js +16 -0
  384. package/dist/src/gateway/hono/lib/mask-secret-length.js.map +1 -0
  385. package/dist/src/gateway/hono/lib/safe-providers-config.d.ts +1 -1
  386. package/dist/src/gateway/hono/lib/safe-providers-config.js +2 -1
  387. package/dist/src/gateway/hono/lib/safe-providers-config.js.map +1 -1
  388. package/dist/src/gateway/hono/lib/safe-voice-config.js +16 -54
  389. package/dist/src/gateway/hono/lib/safe-voice-config.js.map +1 -1
  390. package/dist/src/gateway/hono/lib/static-ui.js +2 -2
  391. package/dist/src/gateway/hono/oauth.js +1 -1
  392. package/dist/src/gateway/hono/routes/agents.js +2 -2
  393. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
  394. package/dist/src/gateway/hono/routes/config-patch/agents.js +25 -7
  395. package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -1
  396. package/dist/src/gateway/hono/routes/config-patch/channels.js +0 -11
  397. package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -1
  398. package/dist/src/gateway/hono/routes/config-patch/gateway.js +3 -2
  399. package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -1
  400. package/dist/src/gateway/hono/routes/config-patch/misc.js +8 -3
  401. package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
  402. package/dist/src/gateway/hono/routes/config.js +59 -0
  403. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  404. package/dist/src/gateway/hono/routes/dreaming.js +1 -1
  405. package/dist/src/gateway/hono/routes/goals.js +1 -1
  406. package/dist/src/gateway/hono/routes/goals.js.map +1 -1
  407. package/dist/src/gateway/hono/routes/host-fs.js +2 -2
  408. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  409. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  410. package/dist/src/gateway/hono/routes/models.js +75 -12
  411. package/dist/src/gateway/hono/routes/models.js.map +1 -1
  412. package/dist/src/gateway/hono/routes/sessions.js +28 -7
  413. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  414. package/dist/src/gateway/hono/routes/shares.js +15 -13
  415. package/dist/src/gateway/hono/routes/shares.js.map +1 -1
  416. package/dist/src/gateway/hono/routes/tunnel.js +1 -1
  417. package/dist/src/gateway/hono/routes/update.js +4 -2
  418. package/dist/src/gateway/hono/routes/update.js.map +1 -1
  419. package/dist/src/gateway/hono/routes/voice.js +75 -0
  420. package/dist/src/gateway/hono/routes/voice.js.map +1 -1
  421. package/dist/src/gateway/hono/routes/workflows.d.ts +3 -0
  422. package/dist/src/gateway/hono/routes/workflows.js +347 -0
  423. package/dist/src/gateway/hono/routes/workflows.js.map +1 -0
  424. package/dist/src/gateway/hono/routes/workspace.js +4 -4
  425. package/dist/src/gateway/hono/sse.js +16 -33
  426. package/dist/src/gateway/hono/sse.js.map +1 -1
  427. package/dist/src/gateway/lock.js +11 -11
  428. package/dist/src/gateway/lock.js.map +1 -1
  429. package/dist/src/gateway/ports.js +6 -6
  430. package/dist/src/gateway/ports.js.map +1 -1
  431. package/dist/src/gateway/resolve-webchat-session-key.d.ts +19 -0
  432. package/dist/src/gateway/resolve-webchat-session-key.js +46 -0
  433. package/dist/src/gateway/resolve-webchat-session-key.js.map +1 -0
  434. package/dist/src/gateway/service/agent-runner.js +2 -2
  435. package/dist/src/gateway/service/marketplace-service.js +2 -2
  436. package/dist/src/gateway/service/run-gateway-agent.js +9 -11
  437. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  438. package/dist/src/gateway/service/sessions-api.d.ts +3 -0
  439. package/dist/src/gateway/service/sessions-api.js +8 -0
  440. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  441. package/dist/src/gateway/service.d.ts +3 -2
  442. package/dist/src/gateway/service.js +9 -8
  443. package/dist/src/gateway/service.js.map +1 -1
  444. package/dist/src/gateway/session-reset-service.d.ts +20 -0
  445. package/dist/src/gateway/session-reset-service.js +54 -0
  446. package/dist/src/gateway/session-reset-service.js.map +1 -0
  447. package/dist/src/gateway/startup-readiness.d.ts +1 -1
  448. package/dist/src/gateway/startup-readiness.js +1 -0
  449. package/dist/src/gateway/startup-readiness.js.map +1 -1
  450. package/dist/src/gateway/workspace-fs-file-list.js +1 -1
  451. package/dist/src/heartbeat/index.js +1 -1
  452. package/dist/src/infra/gateway-processes.js +2 -2
  453. package/dist/src/infra/gateway-processes.js.map +1 -1
  454. package/dist/src/infra/restart.js +2 -2
  455. package/dist/src/infra/run-command.d.ts +16 -0
  456. package/dist/src/infra/run-command.js +67 -0
  457. package/dist/src/infra/run-command.js.map +1 -0
  458. package/dist/src/infra/update-check.js +1 -1
  459. package/dist/src/infra/update-global.d.ts +45 -0
  460. package/dist/src/infra/update-global.js +224 -0
  461. package/dist/src/infra/update-global.js.map +1 -0
  462. package/dist/src/infra/update-lock.js +3 -3
  463. package/dist/src/infra/update-runner.js +1 -1
  464. package/dist/src/infra/update-startup.js +2 -2
  465. package/dist/src/infra/write-file-atomic.js +2 -2
  466. package/dist/src/mcp/channel-shared.js +2 -1
  467. package/dist/src/mcp/channel-shared.js.map +1 -1
  468. package/dist/src/providers/auth-runtime/auth-profile-store.js +2 -2
  469. package/dist/src/providers/auth-runtime/auth-profile-store.js.map +1 -1
  470. package/dist/src/providers/auth-runtime/resolve-auth.js +1 -12
  471. package/dist/src/providers/auth-runtime/resolve-auth.js.map +1 -1
  472. package/dist/src/providers/auth-runtime/types.d.ts +6 -12
  473. package/dist/src/providers/index.js +2 -2
  474. package/dist/src/providers/model-registry.js +1 -1
  475. package/dist/src/routing/agent-session-key.d.ts +58 -0
  476. package/dist/src/routing/agent-session-key.js +164 -0
  477. package/dist/src/routing/agent-session-key.js.map +1 -0
  478. package/dist/src/routing/index.d.ts +1 -1
  479. package/dist/src/routing/index.js +4 -2
  480. package/dist/src/routing/index.js.map +1 -1
  481. package/dist/src/routing/resolve-route.d.ts +15 -0
  482. package/dist/src/routing/resolve-route.js +41 -20
  483. package/dist/src/routing/resolve-route.js.map +1 -1
  484. package/dist/src/routing/resolve-tui-session-key.d.ts +25 -0
  485. package/dist/src/routing/resolve-tui-session-key.js +54 -0
  486. package/dist/src/routing/resolve-tui-session-key.js.map +1 -0
  487. package/dist/src/routing/session-key-utils.d.ts +24 -0
  488. package/dist/src/routing/session-key-utils.js +92 -0
  489. package/dist/src/routing/session-key-utils.js.map +1 -0
  490. package/dist/src/routing/session-key.d.ts +19 -49
  491. package/dist/src/routing/session-key.js +143 -116
  492. package/dist/src/routing/session-key.js.map +1 -1
  493. package/dist/src/session/config-store.js +2 -2
  494. package/dist/src/session/index.d.ts +6 -0
  495. package/dist/src/session/index.js +7 -1
  496. package/dist/src/session/init-session-turn.d.ts +30 -0
  497. package/dist/src/session/init-session-turn.js +102 -0
  498. package/dist/src/session/init-session-turn.js.map +1 -0
  499. package/dist/src/session/lifecycle-timestamps.d.ts +8 -0
  500. package/dist/src/session/lifecycle-timestamps.js +16 -0
  501. package/dist/src/session/lifecycle-timestamps.js.map +1 -0
  502. package/dist/src/session/manager.d.ts +7 -1
  503. package/dist/src/session/manager.js +8 -1
  504. package/dist/src/session/manager.js.map +1 -1
  505. package/dist/src/session/parity/jsonl-transcript-io.js +2 -2
  506. package/dist/src/session/parity/sessions-json-file.js +1 -1
  507. package/dist/src/session/parity/transcript-file-lock.js +2 -2
  508. package/dist/src/session/parity/transcript-paths.js +2 -2
  509. package/dist/src/session/parity/transcript-paths.js.map +1 -1
  510. package/dist/src/session/parity/xopc-session-disk-entry.d.ts +6 -0
  511. package/dist/src/session/reset-policy.d.ts +32 -0
  512. package/dist/src/session/reset-policy.js +65 -0
  513. package/dist/src/session/reset-policy.js.map +1 -0
  514. package/dist/src/session/reset-triggers.d.ts +20 -0
  515. package/dist/src/session/reset-triggers.js +63 -0
  516. package/dist/src/session/reset-triggers.js.map +1 -0
  517. package/dist/src/session/reset-type.d.ts +12 -0
  518. package/dist/src/session/reset-type.js +25 -0
  519. package/dist/src/session/reset-type.js.map +1 -0
  520. package/dist/src/session/resolve-session.d.ts +30 -0
  521. package/dist/src/session/resolve-session.js +93 -0
  522. package/dist/src/session/resolve-session.js.map +1 -0
  523. package/dist/src/session/search-index-cache.js +1 -1
  524. package/dist/src/session/search-index.js +1 -1
  525. package/dist/src/session/session-title.js +3 -2
  526. package/dist/src/session/session-title.js.map +1 -1
  527. package/dist/src/session/store.d.ts +11 -4
  528. package/dist/src/session/store.js +62 -11
  529. package/dist/src/session/store.js.map +1 -1
  530. package/dist/src/session/transcript-events.js +2 -1
  531. package/dist/src/session/transcript-events.js.map +1 -1
  532. package/dist/src/share/share-auto.js +2 -2
  533. package/dist/src/share/share-store.js +3 -3
  534. package/dist/src/share/share-thumbnail.js +2 -2
  535. package/dist/src/share/share-url.d.ts +33 -0
  536. package/dist/src/share/share-url.js +56 -14
  537. package/dist/src/share/share-url.js.map +1 -1
  538. package/dist/src/share/share-zip.js +1 -1
  539. package/dist/src/share/site-share-store.js +3 -3
  540. package/dist/src/share/site-static-serve.js +1 -1
  541. package/dist/src/tui/backends/embedded-backend.js +4 -9
  542. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  543. package/dist/src/tui/backends/gateway-sse-backend.js +1 -1
  544. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  545. package/dist/src/tui/clipboard-image.js +3 -3
  546. package/dist/src/tui/components/chat-log.js +3 -3
  547. package/dist/src/tui/components/chat-log.js.map +1 -1
  548. package/dist/src/tui/theme-manager.js +1 -1
  549. package/dist/src/tui/theme.d.ts +0 -2
  550. package/dist/src/tui/theme.js +1 -3
  551. package/dist/src/tui/theme.js.map +1 -1
  552. package/dist/src/tui/tui-agent-events.js +2 -1
  553. package/dist/src/tui/tui-agent-events.js.map +1 -1
  554. package/dist/src/tui/tui-commands.d.ts +3 -0
  555. package/dist/src/tui/tui-commands.js +45 -10
  556. package/dist/src/tui/tui-commands.js.map +1 -1
  557. package/dist/src/tui/tui-keybindings-file.js +2 -22
  558. package/dist/src/tui/tui-keybindings-file.js.map +1 -1
  559. package/dist/src/tui/tui-scoped-models.js +2 -2
  560. package/dist/src/tui/tui-session-actions.d.ts +28 -0
  561. package/dist/src/tui/tui-session-actions.js +88 -0
  562. package/dist/src/tui/tui-session-actions.js.map +1 -0
  563. package/dist/src/tui/tui-settings.js +1 -1
  564. package/dist/src/tui/tui.js +54 -49
  565. package/dist/src/tui/tui.js.map +1 -1
  566. package/dist/src/tunnel/frpc-binary.js +3 -3
  567. package/dist/src/tunnel/frpc-config.js +1 -1
  568. package/dist/src/tunnel/frpc-extract.js +1 -1
  569. package/dist/src/tunnel/tunnel-state.js +1 -1
  570. package/dist/src/utils/logger/audit.js +1 -1
  571. package/dist/src/utils/logger/log-store.js +1 -1
  572. package/dist/src/utils/logger/rotation.js +1 -1
  573. package/dist/src/utils/string-coerce.d.ts +2 -0
  574. package/dist/src/utils/string-coerce.js +10 -1
  575. package/dist/src/utils/string-coerce.js.map +1 -1
  576. package/dist/src/voice/metadata/builtin.d.ts +2 -0
  577. package/dist/src/voice/metadata/builtin.js +420 -0
  578. package/dist/src/voice/metadata/builtin.js.map +1 -0
  579. package/dist/src/voice/metadata/index.d.ts +4 -0
  580. package/dist/src/voice/metadata/index.js +3 -0
  581. package/dist/src/voice/metadata/registry.d.ts +5 -0
  582. package/dist/src/voice/metadata/registry.js +34 -0
  583. package/dist/src/voice/metadata/registry.js.map +1 -0
  584. package/dist/src/voice/metadata/types.d.ts +41 -0
  585. package/dist/src/voice/metadata/types.js +1 -0
  586. package/dist/src/voice/stt/config-slice.d.ts +2 -5
  587. package/dist/src/voice/stt/config-slice.js +5 -26
  588. package/dist/src/voice/stt/config-slice.js.map +1 -1
  589. package/dist/src/voice/stt/list-providers.d.ts +3 -3
  590. package/dist/src/voice/stt/list-providers.js +41 -6
  591. package/dist/src/voice/stt/list-providers.js.map +1 -1
  592. package/dist/src/voice/stt/types.d.ts +1 -18
  593. package/dist/src/voice/stt/types.js +4 -2
  594. package/dist/src/voice/stt/types.js.map +1 -1
  595. package/dist/src/voice/tts/audio.js +1 -1
  596. package/dist/src/voice/tts/config-slice.d.ts +3 -7
  597. package/dist/src/voice/tts/config-slice.js +7 -38
  598. package/dist/src/voice/tts/config-slice.js.map +1 -1
  599. package/dist/src/voice/tts/list-providers.d.ts +3 -3
  600. package/dist/src/voice/tts/list-providers.js +41 -6
  601. package/dist/src/voice/tts/list-providers.js.map +1 -1
  602. package/dist/src/voice/tts/merge-config.js +2 -48
  603. package/dist/src/voice/tts/merge-config.js.map +1 -1
  604. package/dist/src/voice/tts/providers/alibaba-speech.js +1 -1
  605. package/dist/src/voice/tts/providers/alibaba-speech.js.map +1 -1
  606. package/dist/src/voice/tts/providers/edge-speech.js +2 -2
  607. package/dist/src/voice/tts/types.d.ts +1 -29
  608. package/dist/src/voice/tts/types.js +19 -17
  609. package/dist/src/voice/tts/types.js.map +1 -1
  610. package/dist/src/workflows/domain/command.d.ts +18 -0
  611. package/dist/src/workflows/domain/command.js +1 -0
  612. package/dist/src/workflows/domain/definition.d.ts +62 -0
  613. package/dist/src/workflows/domain/definition.js +1 -0
  614. package/dist/src/workflows/domain/event.d.ts +67 -0
  615. package/dist/src/workflows/domain/event.js +1 -0
  616. package/dist/src/workflows/domain/index.d.ts +5 -0
  617. package/dist/src/workflows/domain/index.js +2 -0
  618. package/dist/src/workflows/domain/result.d.ts +65 -0
  619. package/dist/src/workflows/domain/result.js +1 -0
  620. package/dist/src/workflows/domain/run.d.ts +120 -0
  621. package/dist/src/workflows/domain/run.js +14 -0
  622. package/dist/src/workflows/domain/run.js.map +1 -0
  623. package/dist/src/workflows/engine/index.d.ts +2 -0
  624. package/dist/src/workflows/engine/index.js +3 -0
  625. package/dist/src/workflows/engine/projector.d.ts +3 -0
  626. package/dist/src/workflows/engine/projector.js +205 -0
  627. package/dist/src/workflows/engine/projector.js.map +1 -0
  628. package/dist/src/workflows/engine/workflow-engine.d.ts +31 -0
  629. package/dist/src/workflows/engine/workflow-engine.js +188 -0
  630. package/dist/src/workflows/engine/workflow-engine.js.map +1 -0
  631. package/dist/src/workflows/index.d.ts +6 -0
  632. package/dist/src/workflows/index.js +11 -0
  633. package/dist/src/workflows/runtime/index.d.ts +1 -0
  634. package/dist/src/workflows/runtime/index.js +4 -0
  635. package/dist/src/workflows/runtime/script-runtime.d.ts +3 -0
  636. package/dist/src/workflows/runtime/script-runtime.js +3 -0
  637. package/dist/src/workflows/store/event-store.d.ts +17 -0
  638. package/dist/src/workflows/store/event-store.js +83 -0
  639. package/dist/src/workflows/store/event-store.js.map +1 -0
  640. package/dist/src/workflows/store/paths.d.ts +7 -0
  641. package/dist/src/workflows/store/paths.js +26 -0
  642. package/dist/src/workflows/store/paths.js.map +1 -0
  643. package/dist/src/workflows/store/run-store.d.ts +13 -0
  644. package/dist/src/workflows/store/run-store.js +68 -0
  645. package/dist/src/workflows/store/run-store.js.map +1 -0
  646. package/package.json +5 -8
  647. package/dist/gateway/static/root/assets/agents-mS3_HpRI.js +0 -222
  648. package/dist/gateway/static/root/assets/channels-settings-BG6b9KrW.js +0 -1
  649. package/dist/gateway/static/root/assets/extension-settings-page-B-W4x2xP.js +0 -1
  650. package/dist/gateway/static/root/assets/index-ew_2L2We.css +0 -1
  651. package/dist/gateway/static/root/assets/sessions-page-FaG_Vlkb.js +0 -1
  652. package/dist/gateway/static/root/assets/settings-page-Bet1OerL.js +0 -3
  653. package/dist/gateway/static/root/assets/skills-page-DhUO235y.js +0 -2
  654. package/dist/gateway/static/root/assets/url-BwNL6Rgk.js +0 -3
  655. package/dist/gateway/static/root/assets/voice-api-key-field-CGEydndO.js +0 -1
  656. package/dist/src/agent/tools/browser-legacy-tools.d.ts +0 -17
  657. package/dist/src/agent/tools/browser-legacy-tools.js +0 -766
  658. package/dist/src/agent/tools/browser-legacy-tools.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { createLogger } from "../utils/logger/index.js";
2
2
  import { init_logger } from "../utils/logger.js";
3
- import { platform } from "node:os";
4
3
  import { chmod, stat } from "node:fs/promises";
4
+ import { platform } from "node:os";
5
5
  import { exec } from "node:child_process";
6
6
  import { promisify } from "node:util";
7
7
  //#region src/browser/stealth.ts
@@ -1,7 +1,7 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
- import { join, resolve } from "path";
4
3
  import { mkdir, writeFile } from "fs/promises";
4
+ import { join, resolve } from "path";
5
5
  import { randomBytes } from "crypto";
6
6
  //#region src/channels/attachments/inbound-persist.ts
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
- import { join, resolve } from "path";
4
3
  import { mkdir, writeFile } from "fs/promises";
4
+ import { join, resolve } from "path";
5
5
  import { randomBytes } from "crypto";
6
6
  //#region src/channels/attachments/outbound-tts-persist.ts
7
7
  /**
@@ -9,19 +9,21 @@ import { readFile } from "fs/promises";
9
9
  * STT for webchat voice attachments: merge transcripts into user text and track inbound voice for TTS trigger.
10
10
  */
11
11
  const STT_MAX_BYTES = 25 * 1024 * 1024;
12
+ function mergeSttProviders(base, patch) {
13
+ if (!base && !patch) return void 0;
14
+ const merged = { ...base ?? {} };
15
+ for (const [id, slice] of Object.entries(patch ?? {})) merged[id] = {
16
+ ...merged[id] ?? {},
17
+ ...slice
18
+ };
19
+ return merged;
20
+ }
12
21
  function mergeSttConfigFromAppConfig(stt, toolsMedia) {
13
22
  const p = stt ?? {};
14
23
  return {
15
24
  ...DEFAULT_STT_CONFIG,
16
25
  ...p,
17
- alibaba: {
18
- ...DEFAULT_STT_CONFIG.alibaba,
19
- ...p.alibaba
20
- },
21
- openai: {
22
- ...DEFAULT_STT_CONFIG.openai,
23
- ...p.openai
24
- },
26
+ providers: mergeSttProviders(DEFAULT_STT_CONFIG.providers, p.providers),
25
27
  fallback: {
26
28
  ...DEFAULT_STT_CONFIG.fallback,
27
29
  ...p.fallback
@@ -1 +1 @@
1
- {"version":3,"file":"voice-stt-webchat.js","names":[],"sources":["../../../../src/channels/attachments/voice-stt-webchat.ts"],"sourcesContent":["/**\n * STT for webchat voice attachments: merge transcripts into user text and track inbound voice for TTS trigger.\n */\n\nimport { readFile } from 'fs/promises';\n\nimport type { STTConfig } from '../../voice/stt/types.js';\nimport { DEFAULT_STT_CONFIG } from '../../voice/stt/types.js';\nimport { isSTTAvailable, transcribe } from '../../voice/stt/index.js';\nimport {\n resolveSafeInboundFilePath,\n type InboundAttachmentInput,\n type InternalAttachmentRoots,\n decodeInboundAttachmentBase64,\n} from './inbound-persist.js';\n\nconst STT_MAX_BYTES = 25 * 1024 * 1024;\n\nexport function mergeSttConfigFromAppConfig(\n stt: Partial<STTConfig> | undefined,\n toolsMedia?: { models?: STTConfig['sharedModels'] },\n): STTConfig {\n const p = stt ?? {};\n return {\n ...DEFAULT_STT_CONFIG,\n ...p,\n alibaba: { ...DEFAULT_STT_CONFIG.alibaba, ...p.alibaba },\n openai: { ...DEFAULT_STT_CONFIG.openai, ...p.openai },\n fallback: { ...DEFAULT_STT_CONFIG.fallback!, ...p.fallback },\n ...(toolsMedia?.models?.length ? { sharedModels: toolsMedia.models } : {}),\n };\n}\n\nexport function isVoiceLikeAttachment(att: InboundAttachmentInput): boolean {\n if (att.type === 'voice') return true;\n const m = att.mimeType?.toLowerCase() ?? '';\n return m.startsWith('audio/');\n}\n\nexport async function mergeVoiceTranscriptsIntoUserText(\n attachmentRoots: InternalAttachmentRoots,\n prepared: InboundAttachmentInput[] | undefined,\n userText: string,\n sttConfig: STTConfig,\n opts?: { skipVoiceTranscription?: boolean },\n): Promise<{ text: string; inboundVoice: boolean; voiceTranscripts: string[] }> {\n if (!prepared?.length) {\n return { text: userText, inboundVoice: false, voiceTranscripts: [] };\n }\n\n const hasVoice = prepared.some(isVoiceLikeAttachment);\n if (!hasVoice) {\n return { text: userText, inboundVoice: false, voiceTranscripts: [] };\n }\n\n if (opts?.skipVoiceTranscription === true) {\n return { text: userText, inboundVoice: true, voiceTranscripts: [] };\n }\n\n if (!isSTTAvailable(sttConfig)) {\n return { text: userText, inboundVoice: true, voiceTranscripts: [] };\n }\n\n const transcripts: string[] = [];\n\n for (const att of prepared) {\n if (!isVoiceLikeAttachment(att)) continue;\n\n let buf: Buffer | null = null;\n if (att.workspaceRelativePath) {\n const abs = resolveSafeInboundFilePath(attachmentRoots, att.workspaceRelativePath);\n if (abs) {\n try {\n buf = await readFile(abs);\n } catch {\n buf = null;\n }\n }\n } else if (att.data) {\n try {\n buf = decodeInboundAttachmentBase64(att.data);\n } catch {\n buf = null;\n }\n }\n\n if (!buf || buf.length === 0) {\n transcripts.push('[Voice: empty]');\n continue;\n }\n if (buf.length > STT_MAX_BYTES) {\n transcripts.push('[Voice: file too large]');\n continue;\n }\n\n try {\n const r = await transcribe(buf, sttConfig, {\n language: sttConfig.provider === 'alibaba' ? 'zh' : undefined,\n });\n transcripts.push(r.text.trim() || '[Voice: no speech detected]');\n } catch {\n transcripts.push('[STT failed]');\n }\n }\n\n const merged = [transcripts.filter(Boolean).join('\\n'), userText.trim()].filter(Boolean).join('\\n\\n');\n return { text: merged || userText, inboundVoice: true, voiceTranscripts: transcripts };\n}\n"],"mappings":";;;;;;;;;;AAgBA,MAAM,gBAAgB,KAAK,OAAO;AAElC,SAAgB,4BACd,KACA,YACW;CACX,MAAM,IAAI,OAAO,EAAE;AACnB,QAAO;EACL,GAAG;EACH,GAAG;EACH,SAAS;GAAE,GAAG,mBAAmB;GAAS,GAAG,EAAE;GAAS;EACxD,QAAQ;GAAE,GAAG,mBAAmB;GAAQ,GAAG,EAAE;GAAQ;EACrD,UAAU;GAAE,GAAG,mBAAmB;GAAW,GAAG,EAAE;GAAU;EAC5D,GAAI,YAAY,QAAQ,SAAS,EAAE,cAAc,WAAW,QAAQ,GAAG,EAAE;EAC1E;;AAGH,SAAgB,sBAAsB,KAAsC;AAC1E,KAAI,IAAI,SAAS,QAAS,QAAO;AAEjC,SADU,IAAI,UAAU,aAAa,IAAI,IAChC,WAAW,SAAS;;AAG/B,eAAsB,kCACpB,iBACA,UACA,UACA,WACA,MAC8E;AAC9E,KAAI,CAAC,UAAU,OACb,QAAO;EAAE,MAAM;EAAU,cAAc;EAAO,kBAAkB,EAAE;EAAE;AAItE,KAAI,CADa,SAAS,KAAK,sBAClB,CACX,QAAO;EAAE,MAAM;EAAU,cAAc;EAAO,kBAAkB,EAAE;EAAE;AAGtE,KAAI,MAAM,2BAA2B,KACnC,QAAO;EAAE,MAAM;EAAU,cAAc;EAAM,kBAAkB,EAAE;EAAE;AAGrE,KAAI,CAAC,eAAe,UAAU,CAC5B,QAAO;EAAE,MAAM;EAAU,cAAc;EAAM,kBAAkB,EAAE;EAAE;CAGrE,MAAM,cAAwB,EAAE;AAEhC,MAAK,MAAM,OAAO,UAAU;AAC1B,MAAI,CAAC,sBAAsB,IAAI,CAAE;EAEjC,IAAI,MAAqB;AACzB,MAAI,IAAI,uBAAuB;GAC7B,MAAM,MAAM,2BAA2B,iBAAiB,IAAI,sBAAsB;AAClF,OAAI,IACF,KAAI;AACF,UAAM,MAAM,SAAS,IAAI;WACnB;AACN,UAAM;;aAGD,IAAI,KACb,KAAI;AACF,SAAM,8BAA8B,IAAI,KAAK;UACvC;AACN,SAAM;;AAIV,MAAI,CAAC,OAAO,IAAI,WAAW,GAAG;AAC5B,eAAY,KAAK,iBAAiB;AAClC;;AAEF,MAAI,IAAI,SAAS,eAAe;AAC9B,eAAY,KAAK,0BAA0B;AAC3C;;AAGF,MAAI;GACF,MAAM,IAAI,MAAM,WAAW,KAAK,WAAW,EACzC,UAAU,UAAU,aAAa,YAAY,OAAO,KAAA,GACrD,CAAC;AACF,eAAY,KAAK,EAAE,KAAK,MAAM,IAAI,8BAA8B;UAC1D;AACN,eAAY,KAAK,eAAe;;;AAKpC,QAAO;EAAE,MADM,CAAC,YAAY,OAAO,QAAQ,CAAC,KAAK,KAAK,EAAE,SAAS,MAAM,CAAC,CAAC,OAAO,QAAQ,CAAC,KAAK,OACzE,IAAI;EAAU,cAAc;EAAM,kBAAkB;EAAa"}
1
+ {"version":3,"file":"voice-stt-webchat.js","names":[],"sources":["../../../../src/channels/attachments/voice-stt-webchat.ts"],"sourcesContent":["/**\n * STT for webchat voice attachments: merge transcripts into user text and track inbound voice for TTS trigger.\n */\n\nimport { readFile } from 'fs/promises';\n\nimport type { STTConfig } from '../../voice/stt/types.js';\nimport { DEFAULT_STT_CONFIG } from '../../voice/stt/types.js';\nimport { isSTTAvailable, transcribe } from '../../voice/stt/index.js';\nimport {\n resolveSafeInboundFilePath,\n type InboundAttachmentInput,\n type InternalAttachmentRoots,\n decodeInboundAttachmentBase64,\n} from './inbound-persist.js';\n\nconst STT_MAX_BYTES = 25 * 1024 * 1024;\n\nfunction mergeSttProviders(\n base: STTConfig['providers'],\n patch: STTConfig['providers'],\n): STTConfig['providers'] {\n if (!base && !patch) return undefined;\n const merged: Record<string, Record<string, unknown>> = { ...(base ?? {}) };\n for (const [id, slice] of Object.entries(patch ?? {})) {\n merged[id] = { ...(merged[id] ?? {}), ...slice };\n }\n return merged;\n}\n\nexport function mergeSttConfigFromAppConfig(\n stt: Partial<STTConfig> | undefined,\n toolsMedia?: { models?: STTConfig['sharedModels'] },\n): STTConfig {\n const p = stt ?? {};\n return {\n ...DEFAULT_STT_CONFIG,\n ...p,\n providers: mergeSttProviders(DEFAULT_STT_CONFIG.providers, p.providers),\n fallback: { ...DEFAULT_STT_CONFIG.fallback!, ...p.fallback },\n ...(toolsMedia?.models?.length ? { sharedModels: toolsMedia.models } : {}),\n };\n}\n\nexport function isVoiceLikeAttachment(att: InboundAttachmentInput): boolean {\n if (att.type === 'voice') return true;\n const m = att.mimeType?.toLowerCase() ?? '';\n return m.startsWith('audio/');\n}\n\nexport async function mergeVoiceTranscriptsIntoUserText(\n attachmentRoots: InternalAttachmentRoots,\n prepared: InboundAttachmentInput[] | undefined,\n userText: string,\n sttConfig: STTConfig,\n opts?: { skipVoiceTranscription?: boolean },\n): Promise<{ text: string; inboundVoice: boolean; voiceTranscripts: string[] }> {\n if (!prepared?.length) {\n return { text: userText, inboundVoice: false, voiceTranscripts: [] };\n }\n\n const hasVoice = prepared.some(isVoiceLikeAttachment);\n if (!hasVoice) {\n return { text: userText, inboundVoice: false, voiceTranscripts: [] };\n }\n\n if (opts?.skipVoiceTranscription === true) {\n return { text: userText, inboundVoice: true, voiceTranscripts: [] };\n }\n\n if (!isSTTAvailable(sttConfig)) {\n return { text: userText, inboundVoice: true, voiceTranscripts: [] };\n }\n\n const transcripts: string[] = [];\n\n for (const att of prepared) {\n if (!isVoiceLikeAttachment(att)) continue;\n\n let buf: Buffer | null = null;\n if (att.workspaceRelativePath) {\n const abs = resolveSafeInboundFilePath(attachmentRoots, att.workspaceRelativePath);\n if (abs) {\n try {\n buf = await readFile(abs);\n } catch {\n buf = null;\n }\n }\n } else if (att.data) {\n try {\n buf = decodeInboundAttachmentBase64(att.data);\n } catch {\n buf = null;\n }\n }\n\n if (!buf || buf.length === 0) {\n transcripts.push('[Voice: empty]');\n continue;\n }\n if (buf.length > STT_MAX_BYTES) {\n transcripts.push('[Voice: file too large]');\n continue;\n }\n\n try {\n const r = await transcribe(buf, sttConfig, {\n language: sttConfig.provider === 'alibaba' ? 'zh' : undefined,\n });\n transcripts.push(r.text.trim() || '[Voice: no speech detected]');\n } catch {\n transcripts.push('[STT failed]');\n }\n }\n\n const merged = [transcripts.filter(Boolean).join('\\n'), userText.trim()].filter(Boolean).join('\\n\\n');\n return { text: merged || userText, inboundVoice: true, voiceTranscripts: transcripts };\n}\n"],"mappings":";;;;;;;;;;AAgBA,MAAM,gBAAgB,KAAK,OAAO;AAElC,SAAS,kBACP,MACA,OACwB;AACxB,KAAI,CAAC,QAAQ,CAAC,MAAO,QAAO,KAAA;CAC5B,MAAM,SAAkD,EAAE,GAAI,QAAQ,EAAE,EAAG;AAC3E,MAAK,MAAM,CAAC,IAAI,UAAU,OAAO,QAAQ,SAAS,EAAE,CAAC,CACnD,QAAO,MAAM;EAAE,GAAI,OAAO,OAAO,EAAE;EAAG,GAAG;EAAO;AAElD,QAAO;;AAGT,SAAgB,4BACd,KACA,YACW;CACX,MAAM,IAAI,OAAO,EAAE;AACnB,QAAO;EACL,GAAG;EACH,GAAG;EACH,WAAW,kBAAkB,mBAAmB,WAAW,EAAE,UAAU;EACvE,UAAU;GAAE,GAAG,mBAAmB;GAAW,GAAG,EAAE;GAAU;EAC5D,GAAI,YAAY,QAAQ,SAAS,EAAE,cAAc,WAAW,QAAQ,GAAG,EAAE;EAC1E;;AAGH,SAAgB,sBAAsB,KAAsC;AAC1E,KAAI,IAAI,SAAS,QAAS,QAAO;AAEjC,SADU,IAAI,UAAU,aAAa,IAAI,IAChC,WAAW,SAAS;;AAG/B,eAAsB,kCACpB,iBACA,UACA,UACA,WACA,MAC8E;AAC9E,KAAI,CAAC,UAAU,OACb,QAAO;EAAE,MAAM;EAAU,cAAc;EAAO,kBAAkB,EAAE;EAAE;AAItE,KAAI,CADa,SAAS,KAAK,sBAClB,CACX,QAAO;EAAE,MAAM;EAAU,cAAc;EAAO,kBAAkB,EAAE;EAAE;AAGtE,KAAI,MAAM,2BAA2B,KACnC,QAAO;EAAE,MAAM;EAAU,cAAc;EAAM,kBAAkB,EAAE;EAAE;AAGrE,KAAI,CAAC,eAAe,UAAU,CAC5B,QAAO;EAAE,MAAM;EAAU,cAAc;EAAM,kBAAkB,EAAE;EAAE;CAGrE,MAAM,cAAwB,EAAE;AAEhC,MAAK,MAAM,OAAO,UAAU;AAC1B,MAAI,CAAC,sBAAsB,IAAI,CAAE;EAEjC,IAAI,MAAqB;AACzB,MAAI,IAAI,uBAAuB;GAC7B,MAAM,MAAM,2BAA2B,iBAAiB,IAAI,sBAAsB;AAClF,OAAI,IACF,KAAI;AACF,UAAM,MAAM,SAAS,IAAI;WACnB;AACN,UAAM;;aAGD,IAAI,KACb,KAAI;AACF,SAAM,8BAA8B,IAAI,KAAK;UACvC;AACN,SAAM;;AAIV,MAAI,CAAC,OAAO,IAAI,WAAW,GAAG;AAC5B,eAAY,KAAK,iBAAiB;AAClC;;AAEF,MAAI,IAAI,SAAS,eAAe;AAC9B,eAAY,KAAK,0BAA0B;AAC3C;;AAGF,MAAI;GACF,MAAM,IAAI,MAAM,WAAW,KAAK,WAAW,EACzC,UAAU,UAAU,aAAa,YAAY,OAAO,KAAA,GACrD,CAAC;AACF,eAAY,KAAK,EAAE,KAAK,MAAM,IAAI,8BAA8B;UAC1D;AACN,eAAY,KAAK,eAAe;;;AAKpC,QAAO;EAAE,MADM,CAAC,YAAY,OAAO,QAAQ,CAAC,KAAK,KAAK,EAAE,SAAS,MAAM,CAAC,CAAC,OAAO,QAAQ,CAAC,KAAK,OACzE,IAAI;EAAU,cAAc;EAAM,kBAAkB;EAAa"}
@@ -1,7 +1,7 @@
1
1
  import { init_write_file_atomic, writeTextAtomicSync } from "../../infra/write-file-atomic.js";
2
+ import { randomUUID } from "node:crypto";
2
3
  import { join } from "path";
3
4
  import { existsSync, mkdirSync, readFileSync } from "fs";
4
- import { randomUUID } from "node:crypto";
5
5
  //#region src/channels/outbound/persist-store.ts
6
6
  /**
7
7
  * Durable outbound queue (crash recovery): JSON file under agent internal dir.
@@ -1,13 +1,13 @@
1
+ import fs from "node:fs";
1
2
  import path from "node:path";
2
- import fsSync from "node:fs";
3
3
  //#region src/channels/pairing/allow-from-file.ts
4
4
  function readAllowFromFile(filePath) {
5
5
  try {
6
- if (!fsSync.existsSync(filePath)) return {
6
+ if (!fs.existsSync(filePath)) return {
7
7
  version: 1,
8
8
  allowFrom: []
9
9
  };
10
- const raw = fsSync.readFileSync(filePath, "utf-8");
10
+ const raw = fs.readFileSync(filePath, "utf-8");
11
11
  const parsed = JSON.parse(raw);
12
12
  if (Array.isArray(parsed.allowFrom)) return {
13
13
  version: 1,
@@ -26,13 +26,13 @@ function appendAllowFromIdSync(filePath, userId) {
26
26
  const trimmed = userId.trim();
27
27
  if (!trimmed) return { changed: false };
28
28
  const dir = path.dirname(filePath);
29
- fsSync.mkdirSync(dir, { recursive: true });
29
+ fs.mkdirSync(dir, { recursive: true });
30
30
  const content = readAllowFromFile(filePath);
31
31
  if (content.allowFrom.includes(trimmed)) return { changed: false };
32
32
  content.allowFrom.push(trimmed);
33
- fsSync.writeFileSync(filePath, JSON.stringify(content, null, 2), "utf-8");
33
+ fs.writeFileSync(filePath, JSON.stringify(content, null, 2), "utf-8");
34
34
  try {
35
- fsSync.chmodSync(filePath, 384);
35
+ fs.chmodSync(filePath, 384);
36
36
  } catch {}
37
37
  return { changed: true };
38
38
  }
@@ -43,13 +43,13 @@ function removeAllowFromIdSync(filePath, userId) {
43
43
  const next = content.allowFrom.filter((id) => id !== trimmed);
44
44
  if (next.length === content.allowFrom.length) return { changed: false };
45
45
  const dir = path.dirname(filePath);
46
- fsSync.mkdirSync(dir, { recursive: true });
47
- fsSync.writeFileSync(filePath, JSON.stringify({
46
+ fs.mkdirSync(dir, { recursive: true });
47
+ fs.writeFileSync(filePath, JSON.stringify({
48
48
  version: 1,
49
49
  allowFrom: next
50
50
  }, null, 2), "utf-8");
51
51
  try {
52
- fsSync.chmodSync(filePath, 384);
52
+ fs.chmodSync(filePath, 384);
53
53
  } catch {}
54
54
  return { changed: true };
55
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"allow-from-file.js","names":["fs"],"sources":["../../../../src/channels/pairing/allow-from-file.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\ntype AllowFromFileContent = {\n version: number;\n allowFrom: string[];\n};\n\nfunction readAllowFromFile(filePath: string): AllowFromFileContent {\n try {\n if (!fs.existsSync(filePath)) return { version: 1, allowFrom: [] };\n const raw = fs.readFileSync(filePath, 'utf-8');\n const parsed = JSON.parse(raw) as AllowFromFileContent;\n if (Array.isArray(parsed.allowFrom)) {\n return {\n version: 1,\n allowFrom: parsed.allowFrom.filter((id): id is string => typeof id === 'string' && id.trim() !== ''),\n };\n }\n } catch {\n /* best-effort */\n }\n return { version: 1, allowFrom: [] };\n}\n\nexport function readAllowFromIdsSync(filePath: string): string[] {\n return readAllowFromFile(filePath).allowFrom;\n}\n\nexport function appendAllowFromIdSync(filePath: string, userId: string): { changed: boolean } {\n const trimmed = userId.trim();\n if (!trimmed) return { changed: false };\n\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n\n const content = readAllowFromFile(filePath);\n if (content.allowFrom.includes(trimmed)) {\n return { changed: false };\n }\n\n content.allowFrom.push(trimmed);\n fs.writeFileSync(filePath, JSON.stringify(content, null, 2), 'utf-8');\n try {\n fs.chmodSync(filePath, 0o600);\n } catch {\n /* ignore */\n }\n return { changed: true };\n}\n\nexport function removeAllowFromIdSync(filePath: string, userId: string): { changed: boolean } {\n const trimmed = userId.trim();\n if (!trimmed) return { changed: false };\n\n const content = readAllowFromFile(filePath);\n const next = content.allowFrom.filter((id) => id !== trimmed);\n if (next.length === content.allowFrom.length) {\n return { changed: false };\n }\n\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(filePath, JSON.stringify({ version: 1, allowFrom: next }, null, 2), 'utf-8');\n try {\n fs.chmodSync(filePath, 0o600);\n } catch {\n /* ignore */\n }\n return { changed: true };\n}\n"],"mappings":";;;AAQA,SAAS,kBAAkB,UAAwC;AACjE,KAAI;AACF,MAAI,CAACA,OAAG,WAAW,SAAS,CAAE,QAAO;GAAE,SAAS;GAAG,WAAW,EAAE;GAAE;EAClE,MAAM,MAAMA,OAAG,aAAa,UAAU,QAAQ;EAC9C,MAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,MAAI,MAAM,QAAQ,OAAO,UAAU,CACjC,QAAO;GACL,SAAS;GACT,WAAW,OAAO,UAAU,QAAQ,OAAqB,OAAO,OAAO,YAAY,GAAG,MAAM,KAAK,GAAG;GACrG;SAEG;AAGR,QAAO;EAAE,SAAS;EAAG,WAAW,EAAE;EAAE;;AAGtC,SAAgB,qBAAqB,UAA4B;AAC/D,QAAO,kBAAkB,SAAS,CAAC;;AAGrC,SAAgB,sBAAsB,UAAkB,QAAsC;CAC5F,MAAM,UAAU,OAAO,MAAM;AAC7B,KAAI,CAAC,QAAS,QAAO,EAAE,SAAS,OAAO;CAEvC,MAAM,MAAM,KAAK,QAAQ,SAAS;AAClC,QAAG,UAAU,KAAK,EAAE,WAAW,MAAM,CAAC;CAEtC,MAAM,UAAU,kBAAkB,SAAS;AAC3C,KAAI,QAAQ,UAAU,SAAS,QAAQ,CACrC,QAAO,EAAE,SAAS,OAAO;AAG3B,SAAQ,UAAU,KAAK,QAAQ;AAC/B,QAAG,cAAc,UAAU,KAAK,UAAU,SAAS,MAAM,EAAE,EAAE,QAAQ;AACrE,KAAI;AACF,SAAG,UAAU,UAAU,IAAM;SACvB;AAGR,QAAO,EAAE,SAAS,MAAM;;AAG1B,SAAgB,sBAAsB,UAAkB,QAAsC;CAC5F,MAAM,UAAU,OAAO,MAAM;AAC7B,KAAI,CAAC,QAAS,QAAO,EAAE,SAAS,OAAO;CAEvC,MAAM,UAAU,kBAAkB,SAAS;CAC3C,MAAM,OAAO,QAAQ,UAAU,QAAQ,OAAO,OAAO,QAAQ;AAC7D,KAAI,KAAK,WAAW,QAAQ,UAAU,OACpC,QAAO,EAAE,SAAS,OAAO;CAG3B,MAAM,MAAM,KAAK,QAAQ,SAAS;AAClC,QAAG,UAAU,KAAK,EAAE,WAAW,MAAM,CAAC;AACtC,QAAG,cAAc,UAAU,KAAK,UAAU;EAAE,SAAS;EAAG,WAAW;EAAM,EAAE,MAAM,EAAE,EAAE,QAAQ;AAC7F,KAAI;AACF,SAAG,UAAU,UAAU,IAAM;SACvB;AAGR,QAAO,EAAE,SAAS,MAAM"}
1
+ {"version":3,"file":"allow-from-file.js","names":[],"sources":["../../../../src/channels/pairing/allow-from-file.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\ntype AllowFromFileContent = {\n version: number;\n allowFrom: string[];\n};\n\nfunction readAllowFromFile(filePath: string): AllowFromFileContent {\n try {\n if (!fs.existsSync(filePath)) return { version: 1, allowFrom: [] };\n const raw = fs.readFileSync(filePath, 'utf-8');\n const parsed = JSON.parse(raw) as AllowFromFileContent;\n if (Array.isArray(parsed.allowFrom)) {\n return {\n version: 1,\n allowFrom: parsed.allowFrom.filter((id): id is string => typeof id === 'string' && id.trim() !== ''),\n };\n }\n } catch {\n /* best-effort */\n }\n return { version: 1, allowFrom: [] };\n}\n\nexport function readAllowFromIdsSync(filePath: string): string[] {\n return readAllowFromFile(filePath).allowFrom;\n}\n\nexport function appendAllowFromIdSync(filePath: string, userId: string): { changed: boolean } {\n const trimmed = userId.trim();\n if (!trimmed) return { changed: false };\n\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n\n const content = readAllowFromFile(filePath);\n if (content.allowFrom.includes(trimmed)) {\n return { changed: false };\n }\n\n content.allowFrom.push(trimmed);\n fs.writeFileSync(filePath, JSON.stringify(content, null, 2), 'utf-8');\n try {\n fs.chmodSync(filePath, 0o600);\n } catch {\n /* ignore */\n }\n return { changed: true };\n}\n\nexport function removeAllowFromIdSync(filePath: string, userId: string): { changed: boolean } {\n const trimmed = userId.trim();\n if (!trimmed) return { changed: false };\n\n const content = readAllowFromFile(filePath);\n const next = content.allowFrom.filter((id) => id !== trimmed);\n if (next.length === content.allowFrom.length) {\n return { changed: false };\n }\n\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(filePath, JSON.stringify({ version: 1, allowFrom: next }, null, 2), 'utf-8');\n try {\n fs.chmodSync(filePath, 0o600);\n } catch {\n /* ignore */\n }\n return { changed: true };\n}\n"],"mappings":";;;AAQA,SAAS,kBAAkB,UAAwC;AACjE,KAAI;AACF,MAAI,CAAC,GAAG,WAAW,SAAS,CAAE,QAAO;GAAE,SAAS;GAAG,WAAW,EAAE;GAAE;EAClE,MAAM,MAAM,GAAG,aAAa,UAAU,QAAQ;EAC9C,MAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,MAAI,MAAM,QAAQ,OAAO,UAAU,CACjC,QAAO;GACL,SAAS;GACT,WAAW,OAAO,UAAU,QAAQ,OAAqB,OAAO,OAAO,YAAY,GAAG,MAAM,KAAK,GAAG;GACrG;SAEG;AAGR,QAAO;EAAE,SAAS;EAAG,WAAW,EAAE;EAAE;;AAGtC,SAAgB,qBAAqB,UAA4B;AAC/D,QAAO,kBAAkB,SAAS,CAAC;;AAGrC,SAAgB,sBAAsB,UAAkB,QAAsC;CAC5F,MAAM,UAAU,OAAO,MAAM;AAC7B,KAAI,CAAC,QAAS,QAAO,EAAE,SAAS,OAAO;CAEvC,MAAM,MAAM,KAAK,QAAQ,SAAS;AAClC,IAAG,UAAU,KAAK,EAAE,WAAW,MAAM,CAAC;CAEtC,MAAM,UAAU,kBAAkB,SAAS;AAC3C,KAAI,QAAQ,UAAU,SAAS,QAAQ,CACrC,QAAO,EAAE,SAAS,OAAO;AAG3B,SAAQ,UAAU,KAAK,QAAQ;AAC/B,IAAG,cAAc,UAAU,KAAK,UAAU,SAAS,MAAM,EAAE,EAAE,QAAQ;AACrE,KAAI;AACF,KAAG,UAAU,UAAU,IAAM;SACvB;AAGR,QAAO,EAAE,SAAS,MAAM;;AAG1B,SAAgB,sBAAsB,UAAkB,QAAsC;CAC5F,MAAM,UAAU,OAAO,MAAM;AAC7B,KAAI,CAAC,QAAS,QAAO,EAAE,SAAS,OAAO;CAEvC,MAAM,UAAU,kBAAkB,SAAS;CAC3C,MAAM,OAAO,QAAQ,UAAU,QAAQ,OAAO,OAAO,QAAQ;AAC7D,KAAI,KAAK,WAAW,QAAQ,UAAU,OACpC,QAAO,EAAE,SAAS,OAAO;CAG3B,MAAM,MAAM,KAAK,QAAQ,SAAS;AAClC,IAAG,UAAU,KAAK,EAAE,WAAW,MAAM,CAAC;AACtC,IAAG,cAAc,UAAU,KAAK,UAAU;EAAE,SAAS;EAAG,WAAW;EAAM,EAAE,MAAM,EAAE,EAAE,QAAQ;AAC7F,KAAI;AACF,KAAG,UAAU,UAAU,IAAM;SACvB;AAGR,QAAO,EAAE,SAAS,MAAM"}
@@ -1,8 +1,8 @@
1
1
  import { appendAllowFromIdSync } from "./allow-from-file.js";
2
2
  import "./pairing-constants.js";
3
- import path from "node:path";
4
- import fsSync from "node:fs";
5
3
  import crypto from "node:crypto";
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
6
  //#region src/channels/pairing/pairing-store.ts
7
7
  const PAIRING_CODE_LENGTH = 8;
8
8
  const PAIRING_CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
@@ -36,11 +36,11 @@ function requestAccountId(entry) {
36
36
  }
37
37
  function readStore(filePath) {
38
38
  try {
39
- if (!fsSync.existsSync(filePath)) return {
39
+ if (!fs.existsSync(filePath)) return {
40
40
  version: 1,
41
41
  requests: []
42
42
  };
43
- const raw = fsSync.readFileSync(filePath, "utf-8");
43
+ const raw = fs.readFileSync(filePath, "utf-8");
44
44
  const parsed = JSON.parse(raw);
45
45
  if (parsed && Array.isArray(parsed.requests)) return {
46
46
  version: 1,
@@ -54,10 +54,10 @@ function readStore(filePath) {
54
54
  }
55
55
  function writeStore(filePath, store) {
56
56
  const dir = path.dirname(filePath);
57
- fsSync.mkdirSync(dir, { recursive: true });
58
- fsSync.writeFileSync(filePath, JSON.stringify(store, null, 2), "utf-8");
57
+ fs.mkdirSync(dir, { recursive: true });
58
+ fs.writeFileSync(filePath, JSON.stringify(store, null, 2), "utf-8");
59
59
  try {
60
- fsSync.chmodSync(filePath, 384);
60
+ fs.chmodSync(filePath, 384);
61
61
  } catch {}
62
62
  }
63
63
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"pairing-store.js","names":["fs"],"sources":["../../../../src/channels/pairing/pairing-store.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { appendAllowFromIdSync } from './allow-from-file.js';\nimport { PAIRING_PENDING_MAX } from './pairing-constants.js';\n\nconst PAIRING_CODE_LENGTH = 8;\nconst PAIRING_CODE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';\nexport const PAIRING_PENDING_TTL_MS = 60 * 60 * 1000;\n\nexport type PairingRequest = {\n id: string;\n code: string;\n createdAt: string;\n lastSeenAt: string;\n meta?: Record<string, string>;\n};\n\ntype PairingStoreFile = {\n version: 1;\n requests: PairingRequest[];\n};\n\nfunction randomCode(): string {\n let out = '';\n for (let i = 0; i < PAIRING_CODE_LENGTH; i++) {\n const idx = crypto.randomInt(0, PAIRING_CODE_ALPHABET.length);\n out += PAIRING_CODE_ALPHABET[idx]!;\n }\n return out;\n}\n\nfunction parseTs(iso: string | undefined): number | null {\n if (!iso) return null;\n const n = Date.parse(iso);\n return Number.isFinite(n) ? n : null;\n}\n\nfunction isExpired(entry: PairingRequest, nowMs: number): boolean {\n const t = parseTs(entry.createdAt);\n if (t == null) return true;\n return nowMs - t > PAIRING_PENDING_TTL_MS;\n}\n\nfunction pruneExpired(reqs: PairingRequest[], nowMs: number): PairingRequest[] {\n return reqs.filter((r) => !isExpired(r, nowMs));\n}\n\nfunction normalizeAccountId(accountId: string | undefined): string {\n return (accountId ?? 'default').trim().toLowerCase() || 'default';\n}\n\nfunction requestAccountId(entry: PairingRequest): string {\n const fromMeta = entry.meta?.accountId?.trim().toLowerCase();\n return fromMeta || 'default';\n}\n\nfunction readStore(filePath: string): PairingStoreFile {\n try {\n if (!fs.existsSync(filePath)) return { version: 1, requests: [] };\n const raw = fs.readFileSync(filePath, 'utf-8');\n const parsed = JSON.parse(raw) as PairingStoreFile;\n if (parsed && Array.isArray(parsed.requests)) {\n return { version: 1, requests: parsed.requests };\n }\n } catch {\n /* ignore */\n }\n return { version: 1, requests: [] };\n}\n\nfunction writeStore(filePath: string, store: PairingStoreFile): void {\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(filePath, JSON.stringify(store, null, 2), 'utf-8');\n try {\n fs.chmodSync(filePath, 0o600);\n } catch {\n /* ignore */\n }\n}\n\n/**\n * Create or refresh a pending pairing request for `id` (e.g. Telegram user id).\n * Returns `created: true` when a new code was minted (caller should notify the user).\n */\nexport function upsertPairingRequestSync(params: {\n pairingFilePath: string;\n id: string;\n accountId: string;\n meta?: Record<string, string | undefined | null>;\n}): { code: string; created: boolean } {\n const now = new Date().toISOString();\n const nowMs = Date.now();\n const id = String(params.id).trim();\n if (!id) return { code: '', created: false };\n\n const normalizedAccountId = normalizeAccountId(params.accountId);\n const metaClean =\n params.meta && typeof params.meta === 'object'\n ? Object.fromEntries(\n Object.entries(params.meta)\n .map(([k, v]) => [k, typeof v === 'string' ? v.trim() : ''] as const)\n .filter(([, v]) => Boolean(v)),\n )\n : {};\n const meta: Record<string, string> = { ...metaClean, accountId: normalizedAccountId };\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, nowMs);\n\n const idx = reqs.findIndex((r) => r.id === id && requestAccountId(r) === normalizedAccountId);\n const existingCodes = new Set(reqs.map((r) => r.code.toUpperCase()));\n\n if (idx >= 0) {\n const existing = reqs[idx]!;\n const code = existing.code || randomCode();\n reqs[idx] = {\n id,\n code,\n createdAt: existing.createdAt || now,\n lastSeenAt: now,\n meta: { ...existing.meta, ...meta },\n };\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { code, created: false };\n }\n\n const forAccount = reqs.filter((r) => requestAccountId(r) === normalizedAccountId);\n if (forAccount.length >= PAIRING_PENDING_MAX) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { code: '', created: false };\n }\n\n let code = randomCode();\n for (let attempt = 0; attempt < 50 && existingCodes.has(code.toUpperCase()); attempt++) {\n code = randomCode();\n }\n existingCodes.add(code.toUpperCase());\n\n reqs = [...reqs, { id, code, createdAt: now, lastSeenAt: now, meta }];\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { code, created: true };\n}\n\n/**\n * Approve a pairing code: remove pending request and append sender id to allowFrom file.\n */\n/** Non-expired pending requests; prunes expired entries from disk. */\nexport function listPendingPairingRequestsSync(pairingFilePath: string): PairingRequest[] {\n const nowMs = Date.now();\n let store = readStore(pairingFilePath);\n const reqs = pruneExpired(store.requests, nowMs);\n if (reqs.length !== store.requests.length) {\n writeStore(pairingFilePath, { version: 1, requests: reqs });\n }\n return reqs;\n}\n\nexport function approvePairingCodeSync(params: {\n pairingFilePath: string;\n allowFromFilePath: string;\n code: string;\n /** When set, only requests tagged with this account id match. */\n accountId?: string;\n}): { senderId: string } | null {\n const want = (params.code ?? '').trim().toUpperCase();\n if (!want) return null;\n const normalizedAccount = normalizeAccountId(params.accountId);\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, Date.now());\n const idx = reqs.findIndex(\n (r) => r.code.toUpperCase() === want && requestAccountId(r) === normalizedAccount,\n );\n if (idx < 0) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return null;\n }\n const entry = reqs[idx]!;\n reqs.splice(idx, 1);\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n\n appendAllowFromIdSync(params.allowFromFilePath, entry.id);\n return { senderId: entry.id };\n}\n\n/** Approve a pending request by sender id (gateway-authenticated quick approve). */\nexport function approvePairingBySenderIdSync(params: {\n pairingFilePath: string;\n allowFromFilePath: string;\n senderId: string;\n accountId?: string;\n}): { senderId: string } | null {\n const normalizedAccount = normalizeAccountId(params.accountId);\n const wantId = String(params.senderId ?? '').trim();\n if (!wantId) return null;\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, Date.now());\n const idx = reqs.findIndex(\n (r) => r.id === wantId && requestAccountId(r) === normalizedAccount,\n );\n if (idx < 0) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return null;\n }\n const entry = reqs[idx]!;\n reqs.splice(idx, 1);\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n\n appendAllowFromIdSync(params.allowFromFilePath, entry.id);\n return { senderId: entry.id };\n}\n\n/** Remove a pending request without approving (admin dismiss). */\nexport function dismissPairingBySenderIdSync(params: {\n pairingFilePath: string;\n senderId: string;\n accountId?: string;\n}): { senderId: string } | null {\n const normalizedAccount = normalizeAccountId(params.accountId);\n const wantId = String(params.senderId ?? '').trim();\n if (!wantId) return null;\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, Date.now());\n const idx = reqs.findIndex(\n (r) => r.id === wantId && requestAccountId(r) === normalizedAccount,\n );\n if (idx < 0) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return null;\n }\n const entry = reqs[idx]!;\n reqs.splice(idx, 1);\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { senderId: entry.id };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pairing-store.js","names":[],"sources":["../../../../src/channels/pairing/pairing-store.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { appendAllowFromIdSync } from './allow-from-file.js';\nimport { PAIRING_PENDING_MAX } from './pairing-constants.js';\n\nconst PAIRING_CODE_LENGTH = 8;\nconst PAIRING_CODE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';\nexport const PAIRING_PENDING_TTL_MS = 60 * 60 * 1000;\n\nexport type PairingRequest = {\n id: string;\n code: string;\n createdAt: string;\n lastSeenAt: string;\n meta?: Record<string, string>;\n};\n\ntype PairingStoreFile = {\n version: 1;\n requests: PairingRequest[];\n};\n\nfunction randomCode(): string {\n let out = '';\n for (let i = 0; i < PAIRING_CODE_LENGTH; i++) {\n const idx = crypto.randomInt(0, PAIRING_CODE_ALPHABET.length);\n out += PAIRING_CODE_ALPHABET[idx]!;\n }\n return out;\n}\n\nfunction parseTs(iso: string | undefined): number | null {\n if (!iso) return null;\n const n = Date.parse(iso);\n return Number.isFinite(n) ? n : null;\n}\n\nfunction isExpired(entry: PairingRequest, nowMs: number): boolean {\n const t = parseTs(entry.createdAt);\n if (t == null) return true;\n return nowMs - t > PAIRING_PENDING_TTL_MS;\n}\n\nfunction pruneExpired(reqs: PairingRequest[], nowMs: number): PairingRequest[] {\n return reqs.filter((r) => !isExpired(r, nowMs));\n}\n\nfunction normalizeAccountId(accountId: string | undefined): string {\n return (accountId ?? 'default').trim().toLowerCase() || 'default';\n}\n\nfunction requestAccountId(entry: PairingRequest): string {\n const fromMeta = entry.meta?.accountId?.trim().toLowerCase();\n return fromMeta || 'default';\n}\n\nfunction readStore(filePath: string): PairingStoreFile {\n try {\n if (!fs.existsSync(filePath)) return { version: 1, requests: [] };\n const raw = fs.readFileSync(filePath, 'utf-8');\n const parsed = JSON.parse(raw) as PairingStoreFile;\n if (parsed && Array.isArray(parsed.requests)) {\n return { version: 1, requests: parsed.requests };\n }\n } catch {\n /* ignore */\n }\n return { version: 1, requests: [] };\n}\n\nfunction writeStore(filePath: string, store: PairingStoreFile): void {\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(filePath, JSON.stringify(store, null, 2), 'utf-8');\n try {\n fs.chmodSync(filePath, 0o600);\n } catch {\n /* ignore */\n }\n}\n\n/**\n * Create or refresh a pending pairing request for `id` (e.g. Telegram user id).\n * Returns `created: true` when a new code was minted (caller should notify the user).\n */\nexport function upsertPairingRequestSync(params: {\n pairingFilePath: string;\n id: string;\n accountId: string;\n meta?: Record<string, string | undefined | null>;\n}): { code: string; created: boolean } {\n const now = new Date().toISOString();\n const nowMs = Date.now();\n const id = String(params.id).trim();\n if (!id) return { code: '', created: false };\n\n const normalizedAccountId = normalizeAccountId(params.accountId);\n const metaClean =\n params.meta && typeof params.meta === 'object'\n ? Object.fromEntries(\n Object.entries(params.meta)\n .map(([k, v]) => [k, typeof v === 'string' ? v.trim() : ''] as const)\n .filter(([, v]) => Boolean(v)),\n )\n : {};\n const meta: Record<string, string> = { ...metaClean, accountId: normalizedAccountId };\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, nowMs);\n\n const idx = reqs.findIndex((r) => r.id === id && requestAccountId(r) === normalizedAccountId);\n const existingCodes = new Set(reqs.map((r) => r.code.toUpperCase()));\n\n if (idx >= 0) {\n const existing = reqs[idx]!;\n const code = existing.code || randomCode();\n reqs[idx] = {\n id,\n code,\n createdAt: existing.createdAt || now,\n lastSeenAt: now,\n meta: { ...existing.meta, ...meta },\n };\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { code, created: false };\n }\n\n const forAccount = reqs.filter((r) => requestAccountId(r) === normalizedAccountId);\n if (forAccount.length >= PAIRING_PENDING_MAX) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { code: '', created: false };\n }\n\n let code = randomCode();\n for (let attempt = 0; attempt < 50 && existingCodes.has(code.toUpperCase()); attempt++) {\n code = randomCode();\n }\n existingCodes.add(code.toUpperCase());\n\n reqs = [...reqs, { id, code, createdAt: now, lastSeenAt: now, meta }];\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { code, created: true };\n}\n\n/**\n * Approve a pairing code: remove pending request and append sender id to allowFrom file.\n */\n/** Non-expired pending requests; prunes expired entries from disk. */\nexport function listPendingPairingRequestsSync(pairingFilePath: string): PairingRequest[] {\n const nowMs = Date.now();\n let store = readStore(pairingFilePath);\n const reqs = pruneExpired(store.requests, nowMs);\n if (reqs.length !== store.requests.length) {\n writeStore(pairingFilePath, { version: 1, requests: reqs });\n }\n return reqs;\n}\n\nexport function approvePairingCodeSync(params: {\n pairingFilePath: string;\n allowFromFilePath: string;\n code: string;\n /** When set, only requests tagged with this account id match. */\n accountId?: string;\n}): { senderId: string } | null {\n const want = (params.code ?? '').trim().toUpperCase();\n if (!want) return null;\n const normalizedAccount = normalizeAccountId(params.accountId);\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, Date.now());\n const idx = reqs.findIndex(\n (r) => r.code.toUpperCase() === want && requestAccountId(r) === normalizedAccount,\n );\n if (idx < 0) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return null;\n }\n const entry = reqs[idx]!;\n reqs.splice(idx, 1);\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n\n appendAllowFromIdSync(params.allowFromFilePath, entry.id);\n return { senderId: entry.id };\n}\n\n/** Approve a pending request by sender id (gateway-authenticated quick approve). */\nexport function approvePairingBySenderIdSync(params: {\n pairingFilePath: string;\n allowFromFilePath: string;\n senderId: string;\n accountId?: string;\n}): { senderId: string } | null {\n const normalizedAccount = normalizeAccountId(params.accountId);\n const wantId = String(params.senderId ?? '').trim();\n if (!wantId) return null;\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, Date.now());\n const idx = reqs.findIndex(\n (r) => r.id === wantId && requestAccountId(r) === normalizedAccount,\n );\n if (idx < 0) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return null;\n }\n const entry = reqs[idx]!;\n reqs.splice(idx, 1);\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n\n appendAllowFromIdSync(params.allowFromFilePath, entry.id);\n return { senderId: entry.id };\n}\n\n/** Remove a pending request without approving (admin dismiss). */\nexport function dismissPairingBySenderIdSync(params: {\n pairingFilePath: string;\n senderId: string;\n accountId?: string;\n}): { senderId: string } | null {\n const normalizedAccount = normalizeAccountId(params.accountId);\n const wantId = String(params.senderId ?? '').trim();\n if (!wantId) return null;\n\n let store = readStore(params.pairingFilePath);\n let reqs = pruneExpired(store.requests, Date.now());\n const idx = reqs.findIndex(\n (r) => r.id === wantId && requestAccountId(r) === normalizedAccount,\n );\n if (idx < 0) {\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return null;\n }\n const entry = reqs[idx]!;\n reqs.splice(idx, 1);\n writeStore(params.pairingFilePath, { version: 1, requests: reqs });\n return { senderId: entry.id };\n}\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
- import { ConfigSchema, init_schema } from "../../config/schema.js";
2
- import { init_session_key, parseSessionKey } from "../../routing/session-key.js";
3
1
  import { createLogger } from "../../utils/logger/index.js";
4
2
  import { init_logger } from "../../utils/logger.js";
5
3
  import { init_paths, resolveConfigPath } from "../../config/paths.js";
4
+ import { ConfigSchema, init_schema } from "../../config/schema.js";
6
5
  import { init_loader, loadConfig, saveConfig } from "../../config/loader.js";
6
+ import { init_session_key, parseSessionKey } from "../../routing/session-key.js";
7
7
  import { commandRegistry } from "../registry.js";
8
8
  import { resolveAllowlistMatchSimple } from "../../channels/security.js";
9
9
  import { parseConfigValue } from "../config-value.js";
@@ -14,7 +14,7 @@ const newCommand = {
14
14
  ],
15
15
  handler: async (ctx) => {
16
16
  await ctx.setTyping(true);
17
- await ctx.clearSession();
17
+ await ctx.resetSession();
18
18
  return {
19
19
  content: "",
20
20
  success: true
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","names":[],"sources":["../../../../src/chat-commands/builtins/session.ts"],"sourcesContent":["/**\n * Session Commands\n * \n * Built-in commands for session management:\n * - /new - Start a new session\n * - /list - List all sessions\n * - /switch - Switch to a different session\n * - /clear - Clear current session without archiving\n * - /abort - Cancel in-flight assistant reply (generation / streaming)\n */\n\nimport type { CommandDefinition, CommandContext, UIComponent } from '../types.js';\nimport { commandRegistry } from '../registry.js';\nimport { getSessionDisplayName } from '../session-key.js';\n\nconst newCommand: CommandDefinition = {\n id: 'session.new',\n name: 'new',\n aliases: ['reset', 'restart'],\n description: 'Start a new session (archive current)',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n await ctx.clearSession();\n \n // Note: clearSession already sends confirmation message\n return {\n content: '',\n success: true,\n };\n },\n};\n\nconst listCommand: CommandDefinition = {\n id: 'session.list',\n name: 'list',\n aliases: ['sessions'],\n description: 'List all your sessions',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n const sessions = await ctx.listSessions();\n \n if (sessions.length === 0) {\n return {\n content: '📋 No sessions found.',\n success: true,\n };\n }\n \n // Sort by updatedAt desc\n sessions.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());\n \n // Build text response\n const lines = sessions.slice(0, 10).map(s => {\n const indicator = s.isActive ? '▶️' : ' ';\n const name = getSessionDisplayName(s.key);\n const date = s.updatedAt.toLocaleDateString();\n return `${indicator} ${name}\\n ${s.messageCount} messages · ${date}`;\n });\n \n const content = '📋 Your Sessions:\\n\\n' + lines.join('\\n\\n');\n \n // Create UI component if supported\n if (ctx.supports('buttons')) {\n const component: UIComponent = {\n type: 'session-list',\n sessions: sessions.slice(0, 5).map(s => ({\n ...s,\n name: getSessionDisplayName(s.key),\n })),\n currentSession: ctx.sessionKey,\n };\n \n return {\n content,\n success: true,\n components: [component],\n };\n }\n \n return {\n content,\n success: true,\n };\n },\n};\n\nconst clearCommand: CommandDefinition = {\n id: 'session.clear',\n name: 'clear',\n description: 'Clear current session without archiving',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n // Just delete without archiving\n const messages = await ctx.getSession();\n await ctx.clearSession();\n \n return {\n content: `🗑️ Session cleared. ${messages.length} messages deleted.`,\n success: true,\n };\n },\n};\n\nconst abortCommand: CommandDefinition = {\n id: 'session.abort',\n name: 'abort',\n aliases: ['stop', 'cancel'],\n description: 'Stop the current assistant reply (in-flight generation or stream)',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n if (!ctx.abortCurrentTurn) {\n return {\n content: 'Abort is not available in this environment.',\n success: false,\n };\n }\n await ctx.abortCurrentTurn();\n return {\n content: '⏹️ Current reply cancelled.',\n success: true,\n };\n },\n};\n\nconst compactCommand: CommandDefinition = {\n id: 'session.compact',\n name: 'compact',\n description: 'Compact session history (LLM summary + keep recent turns) to save context',\n category: 'session',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/compact', '/compact focus on API design'],\n handler: async (ctx: CommandContext, args: string) => {\n await ctx.setTyping(true);\n const instructions = args.trim() || undefined;\n const result = await ctx.compactSession?.({ instructions, force: true });\n if (result === null || result === undefined) {\n return {\n content: '⚠️ Session compaction is not available in this environment.',\n success: false,\n };\n }\n if (!result.compacted) {\n return {\n content:\n 'ℹ️ Nothing to compact yet. Need at least two messages, or the session is already small.\\n' +\n 'Tip: add optional focus text, e.g. `/compact emphasize decisions about auth`.',\n success: true,\n };\n }\n const preview =\n result.summary && result.summary.length > 600\n ? `${result.summary.slice(0, 600)}…`\n : result.summary || '';\n return {\n content:\n `🗜️ *Session compacted*\\n\\n` +\n `Tokens (approx): ${result.tokensBefore} → ${result.tokensAfter}\\n\\n` +\n (preview ? `*Summary:*\\n${preview}` : ''),\n success: true,\n };\n },\n};\n\nconst btwCommand: CommandDefinition = {\n id: 'session.btw',\n name: 'btw',\n aliases: ['aside'],\n description: 'Ask a side question without adding to the session transcript',\n category: 'session',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/btw What does that error code mean?', '/aside Summarize the last topic in one line'],\n handler: async (ctx: CommandContext, args: string) => {\n await ctx.setTyping(true);\n const q = args.trim();\n if (!q) {\n return {\n content:\n '💬 *Side question*\\n\\n' +\n 'Usage: `/btw <question>`\\n' +\n 'Answers use your current chat as background only; the reply is not saved to the session.',\n success: true,\n };\n }\n const out = await ctx.btwQuery?.(q);\n if (!out) {\n return { content: '⚠️ /btw is not available here.', success: false };\n }\n if (out.error) {\n return { content: `⚠️ ${out.error}`, success: false };\n }\n return { content: `💬 *BTW*\\n\\n${out.text}`, success: true };\n },\n};\n\nconst exportSessionCommand: CommandDefinition = {\n id: 'session.export',\n name: 'export-session',\n aliases: ['export'],\n description: 'Export this session to workspace exports/ (markdown, html, or json)',\n category: 'session',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/export-session', '/export-session html', '/export json'],\n handler: async (ctx: CommandContext, args: string) => {\n await ctx.setTyping(true);\n const raw = args.trim().toLowerCase();\n const fmt =\n raw === 'json' || raw === 'html' || raw === 'markdown'\n ? (raw as 'json' | 'html' | 'markdown')\n : 'markdown';\n if (!ctx.exportSessionToWorkspace) {\n return { content: '⚠️ Export is not available in this environment.', success: false };\n }\n try {\n const { path } = await ctx.exportSessionToWorkspace(fmt);\n return {\n content: `📄 Exported (${fmt}) to:\\n\\`${path}\\``,\n success: true,\n };\n } catch (e) {\n const em = e instanceof Error ? e.message : String(e);\n return { content: `⚠️ Export failed: ${em}`, success: false };\n }\n },\n};\n\nconst archiveCommand: CommandDefinition = {\n id: 'session.archive',\n name: 'archive',\n description: 'Archive current session',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n await ctx.archiveSession();\n \n return {\n content: '📦 Current session has been archived.',\n success: true,\n };\n },\n};\n\n// Register all session commands\nexport function registerSessionCommands(): void {\n commandRegistry.register(newCommand);\n commandRegistry.register(listCommand);\n commandRegistry.register(clearCommand);\n commandRegistry.register(abortCommand);\n commandRegistry.register(compactCommand);\n commandRegistry.register(btwCommand);\n commandRegistry.register(exportSessionCommand);\n commandRegistry.register(archiveCommand);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"session.js","names":[],"sources":["../../../../src/chat-commands/builtins/session.ts"],"sourcesContent":["/**\n * Session Commands\n * \n * Built-in commands for session management:\n * - /new - Start a new session\n * - /list - List all sessions\n * - /switch - Switch to a different session\n * - /clear - Clear current session without archiving\n * - /abort - Cancel in-flight assistant reply (generation / streaming)\n */\n\nimport type { CommandDefinition, CommandContext, UIComponent } from '../types.js';\nimport { commandRegistry } from '../registry.js';\nimport { getSessionDisplayName } from '../session-key.js';\n\nconst newCommand: CommandDefinition = {\n id: 'session.new',\n name: 'new',\n aliases: ['reset', 'restart'],\n description: 'Start a new session (archive current)',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n await ctx.resetSession();\n \n // Note: resetSession already sends confirmation message\n return {\n content: '',\n success: true,\n };\n },\n};\n\nconst listCommand: CommandDefinition = {\n id: 'session.list',\n name: 'list',\n aliases: ['sessions'],\n description: 'List all your sessions',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n const sessions = await ctx.listSessions();\n \n if (sessions.length === 0) {\n return {\n content: '📋 No sessions found.',\n success: true,\n };\n }\n \n // Sort by updatedAt desc\n sessions.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());\n \n // Build text response\n const lines = sessions.slice(0, 10).map(s => {\n const indicator = s.isActive ? '▶️' : ' ';\n const name = getSessionDisplayName(s.key);\n const date = s.updatedAt.toLocaleDateString();\n return `${indicator} ${name}\\n ${s.messageCount} messages · ${date}`;\n });\n \n const content = '📋 Your Sessions:\\n\\n' + lines.join('\\n\\n');\n \n // Create UI component if supported\n if (ctx.supports('buttons')) {\n const component: UIComponent = {\n type: 'session-list',\n sessions: sessions.slice(0, 5).map(s => ({\n ...s,\n name: getSessionDisplayName(s.key),\n })),\n currentSession: ctx.sessionKey,\n };\n \n return {\n content,\n success: true,\n components: [component],\n };\n }\n \n return {\n content,\n success: true,\n };\n },\n};\n\nconst clearCommand: CommandDefinition = {\n id: 'session.clear',\n name: 'clear',\n description: 'Clear current session without archiving',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n // Just delete without archiving\n const messages = await ctx.getSession();\n await ctx.clearSession();\n \n return {\n content: `🗑️ Session cleared. ${messages.length} messages deleted.`,\n success: true,\n };\n },\n};\n\nconst abortCommand: CommandDefinition = {\n id: 'session.abort',\n name: 'abort',\n aliases: ['stop', 'cancel'],\n description: 'Stop the current assistant reply (in-flight generation or stream)',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n if (!ctx.abortCurrentTurn) {\n return {\n content: 'Abort is not available in this environment.',\n success: false,\n };\n }\n await ctx.abortCurrentTurn();\n return {\n content: '⏹️ Current reply cancelled.',\n success: true,\n };\n },\n};\n\nconst compactCommand: CommandDefinition = {\n id: 'session.compact',\n name: 'compact',\n description: 'Compact session history (LLM summary + keep recent turns) to save context',\n category: 'session',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/compact', '/compact focus on API design'],\n handler: async (ctx: CommandContext, args: string) => {\n await ctx.setTyping(true);\n const instructions = args.trim() || undefined;\n const result = await ctx.compactSession?.({ instructions, force: true });\n if (result === null || result === undefined) {\n return {\n content: '⚠️ Session compaction is not available in this environment.',\n success: false,\n };\n }\n if (!result.compacted) {\n return {\n content:\n 'ℹ️ Nothing to compact yet. Need at least two messages, or the session is already small.\\n' +\n 'Tip: add optional focus text, e.g. `/compact emphasize decisions about auth`.',\n success: true,\n };\n }\n const preview =\n result.summary && result.summary.length > 600\n ? `${result.summary.slice(0, 600)}…`\n : result.summary || '';\n return {\n content:\n `🗜️ *Session compacted*\\n\\n` +\n `Tokens (approx): ${result.tokensBefore} → ${result.tokensAfter}\\n\\n` +\n (preview ? `*Summary:*\\n${preview}` : ''),\n success: true,\n };\n },\n};\n\nconst btwCommand: CommandDefinition = {\n id: 'session.btw',\n name: 'btw',\n aliases: ['aside'],\n description: 'Ask a side question without adding to the session transcript',\n category: 'session',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/btw What does that error code mean?', '/aside Summarize the last topic in one line'],\n handler: async (ctx: CommandContext, args: string) => {\n await ctx.setTyping(true);\n const q = args.trim();\n if (!q) {\n return {\n content:\n '💬 *Side question*\\n\\n' +\n 'Usage: `/btw <question>`\\n' +\n 'Answers use your current chat as background only; the reply is not saved to the session.',\n success: true,\n };\n }\n const out = await ctx.btwQuery?.(q);\n if (!out) {\n return { content: '⚠️ /btw is not available here.', success: false };\n }\n if (out.error) {\n return { content: `⚠️ ${out.error}`, success: false };\n }\n return { content: `💬 *BTW*\\n\\n${out.text}`, success: true };\n },\n};\n\nconst exportSessionCommand: CommandDefinition = {\n id: 'session.export',\n name: 'export-session',\n aliases: ['export'],\n description: 'Export this session to workspace exports/ (markdown, html, or json)',\n category: 'session',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: ['/export-session', '/export-session html', '/export json'],\n handler: async (ctx: CommandContext, args: string) => {\n await ctx.setTyping(true);\n const raw = args.trim().toLowerCase();\n const fmt =\n raw === 'json' || raw === 'html' || raw === 'markdown'\n ? (raw as 'json' | 'html' | 'markdown')\n : 'markdown';\n if (!ctx.exportSessionToWorkspace) {\n return { content: '⚠️ Export is not available in this environment.', success: false };\n }\n try {\n const { path } = await ctx.exportSessionToWorkspace(fmt);\n return {\n content: `📄 Exported (${fmt}) to:\\n\\`${path}\\``,\n success: true,\n };\n } catch (e) {\n const em = e instanceof Error ? e.message : String(e);\n return { content: `⚠️ Export failed: ${em}`, success: false };\n }\n },\n};\n\nconst archiveCommand: CommandDefinition = {\n id: 'session.archive',\n name: 'archive',\n description: 'Archive current session',\n category: 'session',\n scope: ['global', 'private', 'group'],\n handler: async (ctx: CommandContext) => {\n await ctx.setTyping(true);\n \n await ctx.archiveSession();\n \n return {\n content: '📦 Current session has been archived.',\n success: true,\n };\n },\n};\n\n// Register all session commands\nexport function registerSessionCommands(): void {\n commandRegistry.register(newCommand);\n commandRegistry.register(listCommand);\n commandRegistry.register(clearCommand);\n commandRegistry.register(abortCommand);\n commandRegistry.register(compactCommand);\n commandRegistry.register(btwCommand);\n commandRegistry.register(exportSessionCommand);\n commandRegistry.register(archiveCommand);\n}\n"],"mappings":"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"}
@@ -41,8 +41,8 @@ const ttsCommand = {
41
41
  const isEnabled = ttsConfig?.enabled ?? false;
42
42
  const currentTrigger = ttsConfig?.trigger ?? "off";
43
43
  const currentProvider = ttsConfig?.provider ?? "openai";
44
- const voicePack = ttsConfig;
45
- const currentVoice = (currentProvider === "openai" ? voicePack?.openai?.voice : currentProvider === "alibaba" ? voicePack?.alibaba?.voice : currentProvider === "edge" ? voicePack?.edge?.voice : currentProvider === "minimax" ? voicePack?.minimax?.voice : void 0) ?? defaultTtsVoiceForProvider(currentProvider);
44
+ const providerSlice = ttsConfig?.providers?.[currentProvider];
45
+ const currentVoice = typeof providerSlice?.voice === "string" && providerSlice.voice.trim() ? providerSlice.voice : defaultTtsVoiceForProvider(currentProvider);
46
46
  const ttsRuntimeOk = isTTSAvailable(mergeTtsConfigFromAppConfig(ttsConfig));
47
47
  const arg = args.trim().toLowerCase();
48
48
  const formatTimeAgo = (timestamp) => {
@@ -1 +1 @@
1
- {"version":3,"file":"tts.js","names":[],"sources":["../../../../src/chat-commands/builtins/tts.ts"],"sourcesContent":["/**\n * TTS Commands\n *\n * Built-in commands for TTS management:\n * - /tts - Show TTS status\n * - /tts on - Enable TTS\n * - /tts off - Disable TTS\n * - /tts always - Set trigger mode to always\n * - /tts inbound - Set trigger mode to inbound\n * - /tts tagged - Set trigger mode to tagged\n * - /tts never - Set trigger mode to off\n * - /tts provider <provider> - Set TTS provider\n * - /tts voice <voice> - Set TTS voice\n */\n\nimport type { CommandDefinition, CommandContext } from '../types.js';\nimport { commandRegistry } from '../registry.js';\nimport type { TTSAutoMode, TTSProvider } from '../../voice/tts/types.js';\nimport {\n appendTtsReadinessNote,\n formatTtsSetupHint,\n isTTSAvailable,\n mergeTtsConfigFromAppConfig,\n} from '../../voice/tts/index.js';\nimport { ttsStatusTracker } from '../../voice/tts/status-tracker.js';\n\nfunction defaultTtsVoiceForProvider(provider: string): string {\n switch (provider) {\n case 'openai':\n return 'alloy';\n case 'alibaba':\n return 'Cherry';\n case 'edge':\n return 'en-US-MichelleNeural';\n case 'minimax':\n return 'male-qn-qingse';\n default:\n return 'alloy';\n }\n}\n\nconst ttsCommand: CommandDefinition = {\n id: 'tts.manage',\n name: 'tts',\n description: 'Manage TTS (Text-to-Speech) settings',\n category: 'system',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: [\n '/tts',\n '/tts on',\n '/tts off',\n '/tts always',\n '/tts inbound',\n '/tts tagged',\n '/tts provider openai',\n '/tts provider minimax',\n '/tts voice alloy',\n '/tts status',\n ],\n handler: async (ctx: CommandContext, args: string) => {\n const config = ctx.getConfig?.();\n const ttsConfig = config?.messages?.tts;\n\n // Get current TTS status\n const isEnabled = ttsConfig?.enabled ?? false;\n const currentTrigger = ttsConfig?.trigger ?? 'off';\n const currentProvider = ttsConfig?.provider ?? 'openai';\n const voicePack = ttsConfig as\n | {\n openai?: { voice?: string };\n alibaba?: { voice?: string };\n edge?: { voice?: string };\n minimax?: { voice?: string };\n }\n | undefined;\n const currentVoice =\n (currentProvider === 'openai'\n ? voicePack?.openai?.voice\n : currentProvider === 'alibaba'\n ? voicePack?.alibaba?.voice\n : currentProvider === 'edge'\n ? voicePack?.edge?.voice\n : currentProvider === 'minimax'\n ? voicePack?.minimax?.voice\n : undefined) ?? defaultTtsVoiceForProvider(currentProvider);\n\n const effectiveTts = mergeTtsConfigFromAppConfig(ttsConfig);\n const ttsRuntimeOk = isTTSAvailable(effectiveTts);\n\n // Parse arguments\n const arg = args.trim().toLowerCase();\n\n const formatTimeAgo = (timestamp: number): string => {\n const seconds = Math.floor((Date.now() - timestamp) / 1000);\n if (seconds < 60) return `${seconds}s ago`;\n if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;\n if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;\n return `${Math.floor(seconds / 86400)}d ago`;\n };\n\n const formatBytes = (bytes: number): string => {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n };\n\n if (!arg) {\n // Show current status\n const triggerLabels: Record<string, string> = {\n off: 'Off',\n always: 'Always',\n inbound: 'Inbound',\n tagged: 'Tagged',\n };\n\n const status = isEnabled ? '✅ Enabled' : '❌ Disabled';\n const trigger = triggerLabels[currentTrigger] ?? currentTrigger;\n\n const runtimeLine =\n !isEnabled\n ? ''\n : ttsRuntimeOk\n ? `Runtime: ✅ *Ready* (audio can be generated)\n`\n : `Runtime: ⚠️ *Not ready* — no provider can run with current config\n`;\n\n const setupHint =\n isEnabled && !ttsRuntimeOk ? `\\n${formatTtsSetupHint()}\\n` : ''\n\n return {\n content:\n `🔊 *TTS Settings*\n\n` +\n `Status: ${status}\n` +\n `Trigger Mode: *${trigger}*\n` +\n `Provider: *${currentProvider}*\n` +\n `Voice: *${currentVoice}*\n` +\n (runtimeLine ? `\\n${runtimeLine}` : '') +\n setupHint +\n `*Commands:*\n` +\n `/tts on - Enable TTS\n` +\n `/tts off - Disable TTS\n` +\n `/tts always - Always use TTS\n` +\n `/tts inbound - Only reply to voice with voice\n` +\n `/tts tagged - Only use TTS with [[tts]] directive\n` +\n `/tts status - Runtime TTS diagnostics (last call + stats)\n` +\n `/tts provider <openai|alibaba|minimax|edge> - Set provider\n` +\n `/tts voice <voice-id> - Set voice`,\n success: true,\n };\n }\n\n // Handle subcommands\n switch (arg) {\n case 'on':\n case 'enable': {\n const success = await ctx.updateConfig?.('tts.enabled', true);\n const base = success\n ? '✅ TTS enabled. Use `/tts always` or `/tts inbound` to set trigger mode.'\n : '❌ Failed to enable TTS.';\n return {\n content: success ? appendTtsReadinessNote(base, ctx.getConfig?.()) : base,\n success: !!success,\n };\n }\n\n case 'off':\n case 'disable': {\n const success = await ctx.updateConfig?.('tts.enabled', false);\n return {\n content: success\n ? '✅ TTS disabled.'\n : '❌ Failed to disable TTS.',\n success: !!success,\n };\n }\n\n case 'always':\n case 'inbound':\n case 'tagged': {\n const mode = arg as TTSAutoMode;\n const success = await ctx.updateConfig?.('tts.trigger', mode);\n if (success && !isEnabled) {\n // Also enable TTS if setting a trigger mode\n await ctx.updateConfig?.('tts.enabled', true);\n }\n const base = success\n ? `✅ TTS trigger mode set to *${mode}*${!isEnabled ? ' and TTS enabled' : ''}.`\n : `❌ Failed to set TTS trigger mode.`;\n return {\n content: success ? appendTtsReadinessNote(base, ctx.getConfig?.()) : base,\n success: !!success,\n };\n }\n\n case 'never': {\n const success = await ctx.updateConfig?.('tts.trigger', 'off');\n return {\n content: success\n ? '✅ TTS trigger mode set to *off*.'\n : '❌ Failed to set TTS trigger mode.',\n success: !!success,\n };\n }\n\n case 'status': {\n const status = ttsStatusTracker.getStatus();\n const lines: string[] = ['📊 *TTS Status*', ''];\n\n if (status.lastAttempt) {\n const last = status.lastAttempt;\n const timeAgo = formatTimeAgo(last.timestamp);\n const statusIcon = last.success ? '✅' : '❌';\n\n lines.push(`*Last attempt*: ${statusIcon} ${timeAgo}`);\n\n if (last.success) {\n lines.push(` Provider: ${last.provider ?? '—'}`);\n lines.push(` Latency: ${last.latencyMs ?? '—'}ms`);\n lines.push(\n ` Text: ${last.textLength ?? '—'} chars → Audio: ${formatBytes(last.audioSize || 0)}`,\n );\n if (last.usedFallback) lines.push(` ⚠️ Used fallback provider`);\n if (last.wasSummarized) lines.push(` 📝 Text was summarized`);\n } else {\n lines.push(` Error: ${last.error ?? '—'}`);\n if (last.provider) lines.push(` Provider: ${last.provider}`);\n lines.push(` Latency: ${last.latencyMs ?? '—'}ms`);\n }\n } else {\n lines.push('No TTS calls recorded yet.');\n }\n\n lines.push('');\n lines.push(\n `*Statistics*: ${status.totalCalls} calls, ${status.totalSuccesses} success, ${status.totalFailures} failed`,\n );\n\n if (status.recentSuccessRate !== undefined && status.totalCalls > 0) {\n const rate = (status.recentSuccessRate * 100).toFixed(0);\n const window = Math.min(status.totalCalls, 20);\n lines.push(`*Recent success rate*: ${rate}% (last ${window} calls)`);\n }\n\n return {\n content: lines.join('\\n'),\n success: true,\n };\n }\n\n default: {\n // Check for provider or voice subcommand with args\n const parts = arg.split(/\\s+/);\n const subcommand = parts[0];\n const subarg = parts[1];\n\n if (subcommand === 'provider' && subarg) {\n const provider = subarg as TTSProvider;\n if (!['openai', 'alibaba', 'minimax', 'edge'].includes(provider)) {\n return {\n content: `❌ Invalid provider: ${provider}\\nValid providers: openai, alibaba, minimax, edge`,\n success: false,\n };\n }\n const success = await ctx.updateConfig?.('tts.provider', provider);\n const base = success\n ? `✅ TTS provider set to *${provider}*.`\n : '❌ Failed to set TTS provider.';\n return {\n content: success ? appendTtsReadinessNote(base, ctx.getConfig?.()) : base,\n success: !!success,\n };\n }\n\n if (subcommand === 'voice' && subarg) {\n const voice = subarg;\n const provider = currentProvider;\n const success = await ctx.updateConfig?.(`tts.${provider}.voice`, voice);\n return {\n content: success\n ? `✅ TTS voice set to *${voice}* for ${provider}.`\n : '❌ Failed to set TTS voice.',\n success: !!success,\n };\n }\n\n return {\n content: `❌ Unknown TTS command: ${arg}\\n\\nUse /tts to see available commands.`,\n success: false,\n };\n }\n }\n },\n};\n\n// Register TTS commands\nexport function registerTTSCommands(): void {\n commandRegistry.register(ttsCommand);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tts.js","names":[],"sources":["../../../../src/chat-commands/builtins/tts.ts"],"sourcesContent":["/**\n * TTS Commands\n *\n * Built-in commands for TTS management:\n * - /tts - Show TTS status\n * - /tts on - Enable TTS\n * - /tts off - Disable TTS\n * - /tts always - Set trigger mode to always\n * - /tts inbound - Set trigger mode to inbound\n * - /tts tagged - Set trigger mode to tagged\n * - /tts never - Set trigger mode to off\n * - /tts provider <provider> - Set TTS provider\n * - /tts voice <voice> - Set TTS voice\n */\n\nimport type { CommandDefinition, CommandContext } from '../types.js';\nimport { commandRegistry } from '../registry.js';\nimport type { TTSAutoMode, TTSProvider } from '../../voice/tts/types.js';\nimport {\n appendTtsReadinessNote,\n formatTtsSetupHint,\n isTTSAvailable,\n mergeTtsConfigFromAppConfig,\n} from '../../voice/tts/index.js';\nimport { ttsStatusTracker } from '../../voice/tts/status-tracker.js';\n\nfunction defaultTtsVoiceForProvider(provider: string): string {\n switch (provider) {\n case 'openai':\n return 'alloy';\n case 'alibaba':\n return 'Cherry';\n case 'edge':\n return 'en-US-MichelleNeural';\n case 'minimax':\n return 'male-qn-qingse';\n default:\n return 'alloy';\n }\n}\n\nconst ttsCommand: CommandDefinition = {\n id: 'tts.manage',\n name: 'tts',\n description: 'Manage TTS (Text-to-Speech) settings',\n category: 'system',\n scope: ['global', 'private', 'group'],\n acceptsArgs: true,\n examples: [\n '/tts',\n '/tts on',\n '/tts off',\n '/tts always',\n '/tts inbound',\n '/tts tagged',\n '/tts provider openai',\n '/tts provider minimax',\n '/tts voice alloy',\n '/tts status',\n ],\n handler: async (ctx: CommandContext, args: string) => {\n const config = ctx.getConfig?.();\n const ttsConfig = config?.messages?.tts;\n\n // Get current TTS status\n const isEnabled = ttsConfig?.enabled ?? false;\n const currentTrigger = ttsConfig?.trigger ?? 'off';\n const currentProvider = ttsConfig?.provider ?? 'openai';\n const providerSlice = ttsConfig?.providers?.[currentProvider] as\n | { voice?: string }\n | undefined;\n const currentVoice =\n typeof providerSlice?.voice === 'string' && providerSlice.voice.trim()\n ? providerSlice.voice\n : defaultTtsVoiceForProvider(currentProvider);\n\n const effectiveTts = mergeTtsConfigFromAppConfig(ttsConfig);\n const ttsRuntimeOk = isTTSAvailable(effectiveTts);\n\n // Parse arguments\n const arg = args.trim().toLowerCase();\n\n const formatTimeAgo = (timestamp: number): string => {\n const seconds = Math.floor((Date.now() - timestamp) / 1000);\n if (seconds < 60) return `${seconds}s ago`;\n if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;\n if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;\n return `${Math.floor(seconds / 86400)}d ago`;\n };\n\n const formatBytes = (bytes: number): string => {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n };\n\n if (!arg) {\n // Show current status\n const triggerLabels: Record<string, string> = {\n off: 'Off',\n always: 'Always',\n inbound: 'Inbound',\n tagged: 'Tagged',\n };\n\n const status = isEnabled ? '✅ Enabled' : '❌ Disabled';\n const trigger = triggerLabels[currentTrigger] ?? currentTrigger;\n\n const runtimeLine =\n !isEnabled\n ? ''\n : ttsRuntimeOk\n ? `Runtime: ✅ *Ready* (audio can be generated)\n`\n : `Runtime: ⚠️ *Not ready* — no provider can run with current config\n`;\n\n const setupHint =\n isEnabled && !ttsRuntimeOk ? `\\n${formatTtsSetupHint()}\\n` : ''\n\n return {\n content:\n `🔊 *TTS Settings*\n\n` +\n `Status: ${status}\n` +\n `Trigger Mode: *${trigger}*\n` +\n `Provider: *${currentProvider}*\n` +\n `Voice: *${currentVoice}*\n` +\n (runtimeLine ? `\\n${runtimeLine}` : '') +\n setupHint +\n `*Commands:*\n` +\n `/tts on - Enable TTS\n` +\n `/tts off - Disable TTS\n` +\n `/tts always - Always use TTS\n` +\n `/tts inbound - Only reply to voice with voice\n` +\n `/tts tagged - Only use TTS with [[tts]] directive\n` +\n `/tts status - Runtime TTS diagnostics (last call + stats)\n` +\n `/tts provider <openai|alibaba|minimax|edge> - Set provider\n` +\n `/tts voice <voice-id> - Set voice`,\n success: true,\n };\n }\n\n // Handle subcommands\n switch (arg) {\n case 'on':\n case 'enable': {\n const success = await ctx.updateConfig?.('tts.enabled', true);\n const base = success\n ? '✅ TTS enabled. Use `/tts always` or `/tts inbound` to set trigger mode.'\n : '❌ Failed to enable TTS.';\n return {\n content: success ? appendTtsReadinessNote(base, ctx.getConfig?.()) : base,\n success: !!success,\n };\n }\n\n case 'off':\n case 'disable': {\n const success = await ctx.updateConfig?.('tts.enabled', false);\n return {\n content: success\n ? '✅ TTS disabled.'\n : '❌ Failed to disable TTS.',\n success: !!success,\n };\n }\n\n case 'always':\n case 'inbound':\n case 'tagged': {\n const mode = arg as TTSAutoMode;\n const success = await ctx.updateConfig?.('tts.trigger', mode);\n if (success && !isEnabled) {\n // Also enable TTS if setting a trigger mode\n await ctx.updateConfig?.('tts.enabled', true);\n }\n const base = success\n ? `✅ TTS trigger mode set to *${mode}*${!isEnabled ? ' and TTS enabled' : ''}.`\n : `❌ Failed to set TTS trigger mode.`;\n return {\n content: success ? appendTtsReadinessNote(base, ctx.getConfig?.()) : base,\n success: !!success,\n };\n }\n\n case 'never': {\n const success = await ctx.updateConfig?.('tts.trigger', 'off');\n return {\n content: success\n ? '✅ TTS trigger mode set to *off*.'\n : '❌ Failed to set TTS trigger mode.',\n success: !!success,\n };\n }\n\n case 'status': {\n const status = ttsStatusTracker.getStatus();\n const lines: string[] = ['📊 *TTS Status*', ''];\n\n if (status.lastAttempt) {\n const last = status.lastAttempt;\n const timeAgo = formatTimeAgo(last.timestamp);\n const statusIcon = last.success ? '✅' : '❌';\n\n lines.push(`*Last attempt*: ${statusIcon} ${timeAgo}`);\n\n if (last.success) {\n lines.push(` Provider: ${last.provider ?? '—'}`);\n lines.push(` Latency: ${last.latencyMs ?? '—'}ms`);\n lines.push(\n ` Text: ${last.textLength ?? '—'} chars → Audio: ${formatBytes(last.audioSize || 0)}`,\n );\n if (last.usedFallback) lines.push(` ⚠️ Used fallback provider`);\n if (last.wasSummarized) lines.push(` 📝 Text was summarized`);\n } else {\n lines.push(` Error: ${last.error ?? '—'}`);\n if (last.provider) lines.push(` Provider: ${last.provider}`);\n lines.push(` Latency: ${last.latencyMs ?? '—'}ms`);\n }\n } else {\n lines.push('No TTS calls recorded yet.');\n }\n\n lines.push('');\n lines.push(\n `*Statistics*: ${status.totalCalls} calls, ${status.totalSuccesses} success, ${status.totalFailures} failed`,\n );\n\n if (status.recentSuccessRate !== undefined && status.totalCalls > 0) {\n const rate = (status.recentSuccessRate * 100).toFixed(0);\n const window = Math.min(status.totalCalls, 20);\n lines.push(`*Recent success rate*: ${rate}% (last ${window} calls)`);\n }\n\n return {\n content: lines.join('\\n'),\n success: true,\n };\n }\n\n default: {\n // Check for provider or voice subcommand with args\n const parts = arg.split(/\\s+/);\n const subcommand = parts[0];\n const subarg = parts[1];\n\n if (subcommand === 'provider' && subarg) {\n const provider = subarg as TTSProvider;\n if (!['openai', 'alibaba', 'minimax', 'edge'].includes(provider)) {\n return {\n content: `❌ Invalid provider: ${provider}\\nValid providers: openai, alibaba, minimax, edge`,\n success: false,\n };\n }\n const success = await ctx.updateConfig?.('tts.provider', provider);\n const base = success\n ? `✅ TTS provider set to *${provider}*.`\n : '❌ Failed to set TTS provider.';\n return {\n content: success ? appendTtsReadinessNote(base, ctx.getConfig?.()) : base,\n success: !!success,\n };\n }\n\n if (subcommand === 'voice' && subarg) {\n const voice = subarg;\n const provider = currentProvider;\n const success = await ctx.updateConfig?.(`tts.${provider}.voice`, voice);\n return {\n content: success\n ? `✅ TTS voice set to *${voice}* for ${provider}.`\n : '❌ Failed to set TTS voice.',\n success: !!success,\n };\n }\n\n return {\n content: `❌ Unknown TTS command: ${arg}\\n\\nUse /tts to see available commands.`,\n success: false,\n };\n }\n }\n },\n};\n\n// Register TTS commands\nexport function registerTTSCommands(): void {\n commandRegistry.register(ttsCommand);\n}\n"],"mappings":";;;;;;AA0BA,SAAS,2BAA2B,UAA0B;AAC5D,SAAQ,UAAR;EACE,KAAK,SACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,QACE,QAAO;;;AAIb,MAAM,aAAgC;CACpC,IAAI;CACJ,MAAM;CACN,aAAa;CACb,UAAU;CACV,OAAO;EAAC;EAAU;EAAW;EAAQ;CACrC,aAAa;CACb,UAAU;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACD,SAAS,OAAO,KAAqB,SAAiB;EAEpD,MAAM,aADS,IAAI,aAAa,GACN,UAAU;EAGpC,MAAM,YAAY,WAAW,WAAW;EACxC,MAAM,iBAAiB,WAAW,WAAW;EAC7C,MAAM,kBAAkB,WAAW,YAAY;EAC/C,MAAM,gBAAgB,WAAW,YAAY;EAG7C,MAAM,eACJ,OAAO,eAAe,UAAU,YAAY,cAAc,MAAM,MAAM,GAClE,cAAc,QACd,2BAA2B,gBAAgB;EAGjD,MAAM,eAAe,eADA,4BAA4B,UACD,CAAC;EAGjD,MAAM,MAAM,KAAK,MAAM,CAAC,aAAa;EAErC,MAAM,iBAAiB,cAA8B;GACnD,MAAM,UAAU,KAAK,OAAO,KAAK,KAAK,GAAG,aAAa,IAAK;AAC3D,OAAI,UAAU,GAAI,QAAO,GAAG,QAAQ;AACpC,OAAI,UAAU,KAAM,QAAO,GAAG,KAAK,MAAM,UAAU,GAAG,CAAC;AACvD,OAAI,UAAU,MAAO,QAAO,GAAG,KAAK,MAAM,UAAU,KAAK,CAAC;AAC1D,UAAO,GAAG,KAAK,MAAM,UAAU,MAAM,CAAC;;EAGxC,MAAM,eAAe,UAA0B;AAC7C,OAAI,QAAQ,KAAM,QAAO,GAAG,MAAM;AAClC,OAAI,QAAQ,OAAO,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,EAAE,CAAC;AAC7D,UAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,EAAE,CAAC;;AAG/C,MAAI,CAAC,KAAK;GAER,MAAM,gBAAwC;IAC5C,KAAK;IACL,QAAQ;IACR,SAAS;IACT,QAAQ;IACT;GAED,MAAM,SAAS,YAAY,cAAc;GACzC,MAAM,UAAU,cAAc,mBAAmB;GAEjD,MAAM,cACJ,CAAC,YACG,KACA,eACE;IAEA;;GAGR,MAAM,YACJ,aAAa,CAAC,eAAe,KAAK,oBAAoB,CAAC,MAAM;AAE/D,UAAO;IACL,SACE;;UAGW,OAAO;iBAEA,QAAQ;aAEZ,gBAAgB;UAEnB,aAAa;KAEvB,cAAc,KAAK,gBAAgB,MACpC,YACA;IAiBF,SAAS;IACV;;AAIH,UAAQ,KAAR;GACE,KAAK;GACL,KAAK,UAAU;IACb,MAAM,UAAU,MAAM,IAAI,eAAe,eAAe,KAAK;IAC7D,MAAM,OAAO,UACT,4EACA;AACJ,WAAO;KACL,SAAS,UAAU,uBAAuB,MAAM,IAAI,aAAa,CAAC,GAAG;KACrE,SAAS,CAAC,CAAC;KACZ;;GAGH,KAAK;GACL,KAAK,WAAW;IACd,MAAM,UAAU,MAAM,IAAI,eAAe,eAAe,MAAM;AAC9D,WAAO;KACL,SAAS,UACL,oBACA;KACJ,SAAS,CAAC,CAAC;KACZ;;GAGH,KAAK;GACL,KAAK;GACL,KAAK,UAAU;IACb,MAAM,OAAO;IACb,MAAM,UAAU,MAAM,IAAI,eAAe,eAAe,KAAK;AAC7D,QAAI,WAAW,CAAC,UAEd,OAAM,IAAI,eAAe,eAAe,KAAK;IAE/C,MAAM,OAAO,UACT,8BAA8B,KAAK,GAAG,CAAC,YAAY,qBAAqB,GAAG,KAC3E;AACJ,WAAO;KACL,SAAS,UAAU,uBAAuB,MAAM,IAAI,aAAa,CAAC,GAAG;KACrE,SAAS,CAAC,CAAC;KACZ;;GAGH,KAAK,SAAS;IACZ,MAAM,UAAU,MAAM,IAAI,eAAe,eAAe,MAAM;AAC9D,WAAO;KACL,SAAS,UACL,qCACA;KACJ,SAAS,CAAC,CAAC;KACZ;;GAGH,KAAK,UAAU;IACb,MAAM,SAAS,iBAAiB,WAAW;IAC3C,MAAM,QAAkB,CAAC,mBAAmB,GAAG;AAE/C,QAAI,OAAO,aAAa;KACtB,MAAM,OAAO,OAAO;KACpB,MAAM,UAAU,cAAc,KAAK,UAAU;KAC7C,MAAM,aAAa,KAAK,UAAU,MAAM;AAExC,WAAM,KAAK,mBAAmB,WAAW,GAAG,UAAU;AAEtD,SAAI,KAAK,SAAS;AAChB,YAAM,KAAK,eAAe,KAAK,YAAY,MAAM;AACjD,YAAM,KAAK,cAAc,KAAK,aAAa,IAAI,IAAI;AACnD,YAAM,KACJ,WAAW,KAAK,cAAc,IAAI,kBAAkB,YAAY,KAAK,aAAa,EAAE,GACrF;AACD,UAAI,KAAK,aAAc,OAAM,KAAK,8BAA8B;AAChE,UAAI,KAAK,cAAe,OAAM,KAAK,2BAA2B;YACzD;AACL,YAAM,KAAK,YAAY,KAAK,SAAS,MAAM;AAC3C,UAAI,KAAK,SAAU,OAAM,KAAK,eAAe,KAAK,WAAW;AAC7D,YAAM,KAAK,cAAc,KAAK,aAAa,IAAI,IAAI;;UAGrD,OAAM,KAAK,6BAA6B;AAG1C,UAAM,KAAK,GAAG;AACd,UAAM,KACJ,iBAAiB,OAAO,WAAW,UAAU,OAAO,eAAe,YAAY,OAAO,cAAc,SACrG;AAED,QAAI,OAAO,sBAAsB,KAAA,KAAa,OAAO,aAAa,GAAG;KACnE,MAAM,QAAQ,OAAO,oBAAoB,KAAK,QAAQ,EAAE;KACxD,MAAM,SAAS,KAAK,IAAI,OAAO,YAAY,GAAG;AAC9C,WAAM,KAAK,0BAA0B,KAAK,UAAU,OAAO,SAAS;;AAGtE,WAAO;KACL,SAAS,MAAM,KAAK,KAAK;KACzB,SAAS;KACV;;GAGH,SAAS;IAEP,MAAM,QAAQ,IAAI,MAAM,MAAM;IAC9B,MAAM,aAAa,MAAM;IACzB,MAAM,SAAS,MAAM;AAErB,QAAI,eAAe,cAAc,QAAQ;KACvC,MAAM,WAAW;AACjB,SAAI,CAAC;MAAC;MAAU;MAAW;MAAW;MAAO,CAAC,SAAS,SAAS,CAC9D,QAAO;MACL,SAAS,uBAAuB,SAAS;MACzC,SAAS;MACV;KAEH,MAAM,UAAU,MAAM,IAAI,eAAe,gBAAgB,SAAS;KAClE,MAAM,OAAO,UACT,0BAA0B,SAAS,MACnC;AACJ,YAAO;MACL,SAAS,UAAU,uBAAuB,MAAM,IAAI,aAAa,CAAC,GAAG;MACrE,SAAS,CAAC,CAAC;MACZ;;AAGH,QAAI,eAAe,WAAW,QAAQ;KACpC,MAAM,QAAQ;KACd,MAAM,WAAW;KACjB,MAAM,UAAU,MAAM,IAAI,eAAe,OAAO,SAAS,SAAS,MAAM;AACxE,YAAO;MACL,SAAS,UACL,uBAAuB,MAAM,QAAQ,SAAS,KAC9C;MACJ,SAAS,CAAC,CAAC;MACZ;;AAGH,WAAO;KACL,SAAS,0BAA0B,IAAI;KACvC,SAAS;KACV;;;;CAIR;AAGD,SAAgB,sBAA4B;AAC1C,iBAAgB,SAAS,WAAW"}
@@ -31,6 +31,8 @@ export interface CommandContextDeps {
31
31
  supportedFeatures: PlatformFeature[];
32
32
  /** Called after session files are removed so in-memory agents match disk */
33
33
  invalidateAgentSession?: (sessionKey: string) => void;
34
+ /** Reset session in place (archive + new session id); optional — falls back to clearSession */
35
+ resetSession?: (sessionKey: string) => Promise<void>;
34
36
  getCurrentModel?: () => string;
35
37
  switchModel?: (modelId: string) => Promise<boolean>;
36
38
  listModels?: () => Promise<ModelInfo[]>;
@@ -64,6 +66,7 @@ export declare class CommandContextImpl implements CommandContext {
64
66
  replyComponent(component: UIComponent): Promise<void>;
65
67
  setTyping(typing: boolean): Promise<void>;
66
68
  getSession(): Promise<AgentMessage[]>;
69
+ resetSession(): Promise<void>;
67
70
  clearSession(): Promise<void>;
68
71
  archiveSession(): Promise<void>;
69
72
  listSessions(): Promise<SessionInfo[]>;
@@ -4,8 +4,8 @@ import { init_loader, saveConfig } from "../config/loader.js";
4
4
  import { effectiveWorkspacePathForSession } from "../session/session-workspace.js";
5
5
  import { getRoutingInfo, getSessionDisplayName } from "./session-key.js";
6
6
  import { wrapMarkdownExportAsHtml } from "../session/chat-export.js";
7
- import { join } from "path";
8
7
  import { mkdir, writeFile } from "fs/promises";
8
+ import { join } from "path";
9
9
  //#region src/chat-commands/context.ts
10
10
  init_logger();
11
11
  init_loader();
@@ -51,6 +51,25 @@ var CommandContextImpl = class {
51
51
  async getSession() {
52
52
  return this.deps.sessionStore.load(this.sessionKey);
53
53
  }
54
+ async resetSession() {
55
+ if (this.deps.resetSession) await this.deps.resetSession(this.sessionKey);
56
+ else if (typeof this.deps.sessionStore.reset === "function") {
57
+ if (!await this.deps.sessionStore.reset(this.sessionKey)) throw new Error("Session not found");
58
+ this.deps.invalidateAgentSession?.(this.sessionKey);
59
+ } else {
60
+ await this.clearSession();
61
+ return;
62
+ }
63
+ const routing = getRoutingInfo(this.sessionKey);
64
+ await this.deps.bus.publishOutbound({
65
+ channel: routing.channel,
66
+ chat_id: routing.chatId,
67
+ content: "✅ New session started. Previous transcript archived; model and session overrides kept.",
68
+ type: "message",
69
+ metadata: { threadId: routing.threadId }
70
+ });
71
+ log.info({ sessionKey: this.sessionKey }, "Session reset");
72
+ }
54
73
  async clearSession() {
55
74
  const messages = await this.getSession();
56
75
  if (messages.length > 0) {
@@ -66,7 +85,7 @@ var CommandContextImpl = class {
66
85
  await this.deps.bus.publishOutbound({
67
86
  channel: routing.channel,
68
87
  chat_id: routing.chatId,
69
- content: "✅ New session started. Previous session has been archived.",
88
+ content: "✅ Session cleared.",
70
89
  type: "message",
71
90
  metadata: { threadId: routing.threadId }
72
91
  });
@@ -95,8 +114,7 @@ var CommandContextImpl = class {
95
114
  }
96
115
  getCurrentModel() {
97
116
  if (this.deps.getCurrentModel) return this.deps.getCurrentModel();
98
- const modelConfig = this.config.agents?.defaults?.model;
99
- return typeof modelConfig === "string" ? modelConfig : modelConfig?.primary || "minimax/minimax-m2.1";
117
+ return this.config.agents?.defaults?.model?.primary || "minimax/minimax-m2.1";
100
118
  }
101
119
  async listModels() {
102
120
  if (this.deps.listModels) return this.deps.listModels();