@poolzin/pool-bot 2026.2.21 → 2026.2.23

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 (378) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/agents/api-key-rotation.js +47 -0
  3. package/dist/agents/apply-patch-update.js +19 -9
  4. package/dist/agents/apply-patch.js +72 -47
  5. package/dist/agents/bash-tools.exec.js +141 -559
  6. package/dist/agents/cli-backends.js +49 -6
  7. package/dist/agents/cli-runner/helpers.js +69 -152
  8. package/dist/agents/cli-runner.js +70 -19
  9. package/dist/agents/identity.js +20 -1
  10. package/dist/agents/image-sanitization.js +9 -0
  11. package/dist/agents/live-auth-keys.js +123 -26
  12. package/dist/agents/live-model-filter.js +13 -4
  13. package/dist/agents/model-catalog.js +40 -9
  14. package/dist/agents/model-forward-compat.js +60 -23
  15. package/dist/agents/model-selection.js +134 -41
  16. package/dist/agents/pi-auth-json.js +2 -2
  17. package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
  18. package/dist/agents/pi-embedded-helpers/errors.js +140 -15
  19. package/dist/agents/pi-embedded-helpers/images.js +22 -12
  20. package/dist/agents/pi-embedded-helpers.js +2 -2
  21. package/dist/agents/pi-embedded-runner/abort.js +10 -3
  22. package/dist/agents/pi-embedded-runner/compact.js +230 -32
  23. package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
  24. package/dist/agents/pi-embedded-runner/google.js +109 -19
  25. package/dist/agents/pi-embedded-runner/history.js +35 -17
  26. package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
  27. package/dist/agents/pi-embedded-runner/run/images.js +81 -55
  28. package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
  29. package/dist/agents/pi-embedded-runner/run.js +193 -25
  30. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
  31. package/dist/agents/pi-embedded-runner/runs.js +17 -8
  32. package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
  33. package/dist/agents/pi-embedded-runner.js +1 -1
  34. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
  35. package/dist/agents/pi-embedded-subscribe.js +37 -0
  36. package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
  37. package/dist/agents/pi-model-discovery.js +9 -2
  38. package/dist/agents/pi-tool-definition-adapter.js +60 -8
  39. package/dist/agents/pi-tools.before-tool-call.js +1 -1
  40. package/dist/agents/pi-tools.js +113 -94
  41. package/dist/agents/pi-tools.read.js +337 -38
  42. package/dist/agents/poolbot-tools.js +14 -5
  43. package/dist/agents/sandbox/docker.js +10 -5
  44. package/dist/agents/sandbox/registry.js +96 -46
  45. package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
  46. package/dist/agents/sandbox-paths.js +43 -10
  47. package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
  48. package/dist/agents/session-tool-result-guard.js +39 -39
  49. package/dist/agents/session-transcript-repair.js +36 -33
  50. package/dist/agents/session-write-lock.js +62 -44
  51. package/dist/agents/skills/frontmatter.js +49 -88
  52. package/dist/agents/skills/workspace.js +335 -28
  53. package/dist/agents/subagent-announce.js +508 -174
  54. package/dist/agents/subagent-registry.js +45 -4
  55. package/dist/agents/subagent-spawn.js +16 -33
  56. package/dist/agents/system-prompt-report.js +27 -10
  57. package/dist/agents/system-prompt.js +26 -32
  58. package/dist/agents/tool-call-id.js +69 -17
  59. package/dist/agents/tool-display-common.js +1 -1
  60. package/dist/agents/tool-images.js +64 -31
  61. package/dist/agents/tools/canvas-tool.js +17 -11
  62. package/dist/agents/tools/common.js +37 -19
  63. package/dist/agents/tools/cron-tool.js +40 -38
  64. package/dist/agents/tools/gateway.js +70 -2
  65. package/dist/agents/tools/message-tool.js +181 -40
  66. package/dist/agents/tools/nodes-tool.js +128 -36
  67. package/dist/agents/tools/nodes-utils.js +12 -38
  68. package/dist/agents/tools/session-status-tool.js +24 -71
  69. package/dist/agents/tools/sessions-helpers.js +38 -210
  70. package/dist/agents/tools/sessions-spawn-tool.js +28 -198
  71. package/dist/agents/tools/telegram-actions.js +58 -7
  72. package/dist/agents/tools/web-fetch-utils.js +112 -7
  73. package/dist/agents/tools/web-fetch.js +279 -175
  74. package/dist/agents/tools/web-shared.js +71 -8
  75. package/dist/agents/usage.js +25 -16
  76. package/dist/auto-reply/commands-registry.data.js +85 -11
  77. package/dist/auto-reply/dispatch.js +40 -21
  78. package/dist/auto-reply/reply/abort.js +102 -33
  79. package/dist/auto-reply/reply/commands-core.js +82 -33
  80. package/dist/auto-reply/reply/commands-export-session.js +1 -1
  81. package/dist/auto-reply/reply/commands-info.js +41 -12
  82. package/dist/auto-reply/reply/commands-subagents.js +352 -100
  83. package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
  84. package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
  85. package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
  86. package/dist/auto-reply/reply/inbound-meta.js +12 -1
  87. package/dist/auto-reply/reply/mentions.js +18 -11
  88. package/dist/auto-reply/reply/normalize-reply.js +17 -8
  89. package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
  90. package/dist/auto-reply/reply/session.js +102 -21
  91. package/dist/auto-reply/reply/streaming-directives.js +16 -5
  92. package/dist/auto-reply/status.js +73 -50
  93. package/dist/browser/extension-relay.js +3 -3
  94. package/dist/browser/http-auth.js +1 -1
  95. package/dist/browser/paths.js +2 -2
  96. package/dist/build-info.json +3 -3
  97. package/dist/channels/allowlist-match.js +20 -0
  98. package/dist/channels/allowlists/resolve-utils.js +65 -2
  99. package/dist/channels/chat-type.js +8 -4
  100. package/dist/channels/dock.js +127 -35
  101. package/dist/channels/draft-stream-loop.js +6 -2
  102. package/dist/channels/plugins/actions/telegram.js +42 -18
  103. package/dist/channels/plugins/allowlist-match.js +1 -1
  104. package/dist/channels/plugins/group-mentions.js +51 -41
  105. package/dist/channels/plugins/message-action-names.js +2 -0
  106. package/dist/channels/plugins/message-actions.js +24 -5
  107. package/dist/channels/plugins/normalize/discord.js +26 -4
  108. package/dist/channels/plugins/normalize/signal.js +35 -22
  109. package/dist/channels/plugins/onboarding/helpers.js +8 -26
  110. package/dist/channels/plugins/outbound/imessage.js +15 -14
  111. package/dist/channels/registry.js +20 -7
  112. package/dist/cli/acp-cli.js +7 -5
  113. package/dist/cli/browser-cli-extension.js +25 -12
  114. package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
  115. package/dist/cli/browser-cli-state.js +101 -145
  116. package/dist/cli/command-options.js +28 -0
  117. package/dist/cli/completion-cli.js +6 -6
  118. package/dist/cli/cron-cli/register.cron-add.js +25 -1
  119. package/dist/cli/cron-cli/register.cron-edit.js +44 -0
  120. package/dist/cli/cron-cli/shared.js +7 -1
  121. package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
  122. package/dist/cli/daemon-cli/lifecycle.js +23 -247
  123. package/dist/cli/daemon-cli/register-service-commands.js +25 -4
  124. package/dist/cli/daemon-cli.js +1 -0
  125. package/dist/cli/devices-cli.js +33 -20
  126. package/dist/cli/gateway-cli/register.js +37 -105
  127. package/dist/cli/gateway-cli/run.js +49 -11
  128. package/dist/cli/nodes-camera.js +59 -4
  129. package/dist/cli/nodes-cli/register.camera.js +27 -24
  130. package/dist/cli/nodes-cli/rpc.js +21 -38
  131. package/dist/cli/qr-cli.js +2 -2
  132. package/dist/cli/skills-cli.format.js +2 -2
  133. package/dist/cli/update-cli/progress.js +2 -2
  134. package/dist/cli/update-cli/restart-helper.js +28 -7
  135. package/dist/cli/update-cli/shared.js +7 -7
  136. package/dist/cli/update-cli/status.js +1 -1
  137. package/dist/cli/update-cli/update-command.js +14 -8
  138. package/dist/cli/update-cli/wizard.js +2 -2
  139. package/dist/cli/update-cli.js +21 -1027
  140. package/dist/commands/auth-choice.apply.anthropic.js +10 -2
  141. package/dist/commands/channels/add-mutators.js +3 -35
  142. package/dist/commands/channels/add.js +39 -51
  143. package/dist/commands/config-validation.js +1 -1
  144. package/dist/commands/configure.gateway-auth.js +52 -15
  145. package/dist/commands/configure.gateway.js +84 -40
  146. package/dist/commands/doctor-completion.js +3 -3
  147. package/dist/commands/doctor-config-flow.js +536 -16
  148. package/dist/commands/doctor-gateway-services.js +103 -79
  149. package/dist/commands/doctor-memory-search.js +9 -9
  150. package/dist/commands/doctor-platform-notes.js +57 -30
  151. package/dist/commands/doctor-prompter.js +26 -15
  152. package/dist/commands/doctor-session-locks.js +1 -1
  153. package/dist/commands/doctor.js +21 -9
  154. package/dist/commands/model-picker.js +120 -95
  155. package/dist/commands/models/set.js +2 -21
  156. package/dist/commands/models/shared.js +65 -37
  157. package/dist/commands/onboard-helpers.js +81 -39
  158. package/dist/commands/openai-codex-oauth.js +1 -1
  159. package/dist/commands/sessions.js +52 -53
  160. package/dist/commands/status.summary.js +52 -34
  161. package/dist/commands/test-wizard-helpers.js +2 -2
  162. package/dist/config/defaults.js +79 -42
  163. package/dist/config/group-policy.js +50 -18
  164. package/dist/config/includes.js +37 -10
  165. package/dist/config/schema.help.js +5 -4
  166. package/dist/config/schema.hints.js +2 -2
  167. package/dist/config/schema.labels.js +1 -0
  168. package/dist/config/sessions/group.js +12 -11
  169. package/dist/config/sessions/paths.js +137 -11
  170. package/dist/config/sessions/store.js +185 -65
  171. package/dist/config/sessions/types.js +15 -1
  172. package/dist/config/sessions.js +1 -0
  173. package/dist/config/telegram-custom-commands.js +3 -2
  174. package/dist/config/types.js +2 -0
  175. package/dist/config/zod-schema.agent-defaults.js +6 -27
  176. package/dist/config/zod-schema.agent-runtime.js +171 -79
  177. package/dist/config/zod-schema.providers-core.js +138 -65
  178. package/dist/config/zod-schema.session.js +49 -22
  179. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  180. package/dist/cron/isolated-agent/run.js +224 -57
  181. package/dist/cron/normalize.js +48 -45
  182. package/dist/cron/run-log.js +14 -0
  183. package/dist/cron/service/jobs.js +190 -28
  184. package/dist/cron/service/normalize.js +29 -11
  185. package/dist/cron/service/store.js +30 -44
  186. package/dist/cron/service/timer.js +182 -96
  187. package/dist/cron/service.js +3 -0
  188. package/dist/cron/stagger.js +37 -0
  189. package/dist/daemon/inspect.js +132 -92
  190. package/dist/daemon/runtime-paths.js +25 -4
  191. package/dist/daemon/service-audit.js +47 -16
  192. package/dist/discord/accounts.js +23 -20
  193. package/dist/discord/monitor/agent-components.js +1115 -219
  194. package/dist/discord/monitor/allow-list.js +114 -34
  195. package/dist/discord/monitor/listeners.js +204 -97
  196. package/dist/discord/monitor/message-handler.js +21 -10
  197. package/dist/discord/monitor/message-handler.preflight.js +195 -101
  198. package/dist/discord/monitor/message-handler.process.js +384 -123
  199. package/dist/discord/monitor/message-utils.js +86 -23
  200. package/dist/discord/monitor/native-command.js +77 -57
  201. package/dist/discord/monitor/provider.js +122 -117
  202. package/dist/discord/monitor/reply-context.js +20 -16
  203. package/dist/discord/monitor/reply-delivery.js +40 -8
  204. package/dist/discord/monitor/rest-fetch.js +22 -0
  205. package/dist/discord/monitor/threading.js +117 -24
  206. package/dist/discord/send.js +2 -1
  207. package/dist/discord/send.outbound.js +124 -11
  208. package/dist/discord/send.shared.js +112 -72
  209. package/dist/discord/voice-message.js +3 -3
  210. package/dist/gateway/auth.js +119 -44
  211. package/dist/gateway/call.js +76 -34
  212. package/dist/gateway/channel-health-monitor.js +57 -50
  213. package/dist/gateway/client.js +63 -29
  214. package/dist/gateway/control-ui-contract.js +1 -1
  215. package/dist/gateway/gateway-config-prompts.shared.js +2 -2
  216. package/dist/gateway/net.js +109 -1
  217. package/dist/gateway/protocol/index.js +5 -8
  218. package/dist/gateway/protocol/schema/agent.js +19 -1
  219. package/dist/gateway/protocol/schema/channels.js +21 -0
  220. package/dist/gateway/protocol/schema/cron.js +43 -30
  221. package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
  222. package/dist/gateway/protocol/schema/sessions.js +5 -1
  223. package/dist/gateway/protocol/schema.js +0 -1
  224. package/dist/gateway/server/presence-events.js +12 -0
  225. package/dist/gateway/server/ws-connection/message-handler.js +203 -212
  226. package/dist/gateway/server/ws-connection.js +58 -21
  227. package/dist/gateway/server-broadcast.js +18 -13
  228. package/dist/gateway/server-cron.js +177 -10
  229. package/dist/gateway/server-methods/agent-job.js +131 -38
  230. package/dist/gateway/server-methods/send.js +60 -14
  231. package/dist/gateway/server-methods/sessions.js +160 -96
  232. package/dist/gateway/server-methods/system.js +5 -7
  233. package/dist/gateway/server-methods-list.js +8 -0
  234. package/dist/gateway/server-methods.js +24 -8
  235. package/dist/gateway/server-node-events.js +278 -68
  236. package/dist/gateway/session-utils.fs.js +316 -75
  237. package/dist/gateway/session-utils.js +224 -70
  238. package/dist/gateway/sessions-patch.js +63 -20
  239. package/dist/gateway/test-temp-config.js +1 -1
  240. package/dist/gateway/tools-invoke-http.js +118 -70
  241. package/dist/gateway/ws-log.js +135 -107
  242. package/dist/hooks/frontmatter.js +36 -82
  243. package/dist/hooks/install.js +149 -139
  244. package/dist/hooks/internal-hooks.js +29 -4
  245. package/dist/hooks/plugin-hooks.js +2 -1
  246. package/dist/imessage/monitor/deliver.js +10 -4
  247. package/dist/imessage/monitor/monitor-provider.js +138 -375
  248. package/dist/imessage/monitor/runtime.js +4 -8
  249. package/dist/imessage/send.js +65 -19
  250. package/dist/infra/exec-approvals-allowlist.js +7 -0
  251. package/dist/infra/exec-approvals.js +35 -920
  252. package/dist/infra/exec-safe-bin-trust.js +64 -0
  253. package/dist/infra/heartbeat-runner.js +207 -134
  254. package/dist/infra/heartbeat-wake.js +183 -22
  255. package/dist/infra/install-source-utils.js +47 -0
  256. package/dist/infra/net/ssrf.js +170 -36
  257. package/dist/infra/outbound/deliver.js +224 -58
  258. package/dist/infra/outbound/message-action-spec.js +12 -5
  259. package/dist/infra/outbound/outbound-session.js +27 -25
  260. package/dist/infra/poolbot-root.js +32 -22
  261. package/dist/infra/ports.js +14 -11
  262. package/dist/infra/skills-remote.js +48 -37
  263. package/dist/infra/system-events.js +25 -11
  264. package/dist/infra/system-presence.js +26 -33
  265. package/dist/infra/tmp-poolbot-dir.js +81 -2
  266. package/dist/infra/wsl.js +37 -1
  267. package/dist/line/bot-message-context.js +163 -191
  268. package/dist/logging/subsystem.js +59 -22
  269. package/dist/markdown/ir.js +124 -50
  270. package/dist/media/store.js +1 -1
  271. package/dist/media-understanding/runner.entries.js +42 -25
  272. package/dist/media-understanding/runner.js +53 -488
  273. package/dist/memory/embeddings-gemini.js +53 -38
  274. package/dist/memory/manager-embedding-ops.js +48 -69
  275. package/dist/pairing/pairing-store.js +178 -119
  276. package/dist/plugin-sdk/index.js +34 -6
  277. package/dist/plugins/hooks.js +135 -14
  278. package/dist/plugins/install.js +190 -152
  279. package/dist/polls.js +11 -0
  280. package/dist/routing/resolve-route.js +190 -56
  281. package/dist/routing/session-key.js +38 -22
  282. package/dist/runtime.js +35 -9
  283. package/dist/security/audit-channel.js +1 -1
  284. package/dist/sessions/session-key-utils.js +29 -11
  285. package/dist/shared/frontmatter.js +5 -5
  286. package/dist/shared/node-list-types.js +1 -0
  287. package/dist/shared/string-normalization.js +15 -0
  288. package/dist/signal/monitor/event-handler.js +68 -36
  289. package/dist/signal/send.js +29 -37
  290. package/dist/slack/monitor/allow-list.js +10 -11
  291. package/dist/slack/monitor/commands.js +14 -3
  292. package/dist/slack/monitor/events/interactions.js +4 -4
  293. package/dist/slack/monitor/media.js +224 -16
  294. package/dist/slack/monitor/message-handler/dispatch.js +247 -13
  295. package/dist/slack/monitor/message-handler/prepare.js +128 -45
  296. package/dist/slack/monitor/slash.js +357 -144
  297. package/dist/slack/streaming.js +77 -0
  298. package/dist/telegram/accounts.js +40 -13
  299. package/dist/telegram/allowed-updates.js +3 -0
  300. package/dist/telegram/bot/delivery.js +129 -66
  301. package/dist/telegram/bot/helpers.js +136 -122
  302. package/dist/telegram/bot-handlers.js +600 -339
  303. package/dist/telegram/bot-message-context.js +115 -73
  304. package/dist/telegram/bot-message-dispatch.js +235 -104
  305. package/dist/telegram/bot-native-command-menu.js +3 -1
  306. package/dist/telegram/bot-native-commands.js +213 -193
  307. package/dist/telegram/bot.js +24 -132
  308. package/dist/telegram/draft-stream.js +84 -75
  309. package/dist/telegram/format.js +150 -6
  310. package/dist/telegram/send.js +415 -255
  311. package/dist/telegram/targets.js +21 -2
  312. package/dist/telegram/update-offset-store.js +19 -3
  313. package/dist/terminal/restore.js +5 -2
  314. package/dist/test-utils/fetch-mock.js +5 -0
  315. package/dist/version.js +18 -5
  316. package/dist/web/auto-reply/monitor/broadcast.js +7 -3
  317. package/dist/web/auto-reply/monitor/on-message.js +6 -3
  318. package/dist/web/inbound/media.js +34 -8
  319. package/dist/web/inbound/monitor.js +34 -17
  320. package/dist/web/inbound/send-api.js +18 -17
  321. package/dist/web/outbound.js +12 -5
  322. package/dist/wizard/clack-prompter.js +40 -7
  323. package/extensions/bluebubbles/package.json +1 -1
  324. package/extensions/copilot-proxy/package.json +1 -1
  325. package/extensions/device-pair/index.ts +2 -2
  326. package/extensions/diagnostics-otel/package.json +1 -1
  327. package/extensions/discord/package.json +1 -1
  328. package/extensions/feishu/package.json +1 -1
  329. package/extensions/google-antigravity-auth/package.json +1 -1
  330. package/extensions/google-gemini-cli-auth/package.json +1 -1
  331. package/extensions/googlechat/package.json +1 -1
  332. package/extensions/imessage/package.json +1 -1
  333. package/extensions/irc/package.json +1 -1
  334. package/extensions/irc/src/accounts.ts +1 -1
  335. package/extensions/irc/src/onboarding.ts +4 -4
  336. package/extensions/line/package.json +1 -1
  337. package/extensions/llm-task/package.json +1 -1
  338. package/extensions/lobster/package.json +1 -1
  339. package/extensions/matrix/CHANGELOG.md +10 -0
  340. package/extensions/matrix/package.json +1 -1
  341. package/extensions/mattermost/package.json +1 -1
  342. package/extensions/memory-core/package.json +1 -1
  343. package/extensions/memory-lancedb/package.json +1 -1
  344. package/extensions/minimax-portal-auth/package.json +1 -1
  345. package/extensions/msteams/CHANGELOG.md +10 -0
  346. package/extensions/msteams/package.json +1 -1
  347. package/extensions/nextcloud-talk/package.json +1 -1
  348. package/extensions/nostr/CHANGELOG.md +10 -0
  349. package/extensions/nostr/package.json +1 -1
  350. package/extensions/open-prose/package.json +1 -1
  351. package/extensions/openai-codex-auth/package.json +1 -1
  352. package/extensions/signal/package.json +1 -1
  353. package/extensions/slack/package.json +1 -1
  354. package/extensions/telegram/package.json +1 -1
  355. package/extensions/tlon/package.json +1 -1
  356. package/extensions/twitch/CHANGELOG.md +10 -0
  357. package/extensions/twitch/package.json +1 -1
  358. package/extensions/voice-call/CHANGELOG.md +10 -0
  359. package/extensions/voice-call/package.json +1 -1
  360. package/extensions/whatsapp/package.json +1 -1
  361. package/extensions/zalo/CHANGELOG.md +10 -0
  362. package/extensions/zalo/package.json +1 -1
  363. package/extensions/zalouser/CHANGELOG.md +10 -0
  364. package/extensions/zalouser/package.json +1 -1
  365. package/package.json +1 -1
  366. package/skills/apple-reminders/SKILL.md +100 -49
  367. package/skills/coding-agent/SKILL.md +34 -28
  368. package/skills/github/SKILL.md +131 -16
  369. package/skills/imsg/SKILL.md +112 -15
  370. package/skills/openhue/SKILL.md +101 -19
  371. package/skills/tmux/SKILL.md +111 -79
  372. package/skills/weather/SKILL.md +88 -25
  373. package/dist/agents/openclaw-tools.js +0 -151
  374. package/dist/agents/tool-security.js +0 -96
  375. package/dist/gateway/url-validation.js +0 -94
  376. package/dist/infra/openclaw-root.js +0 -109
  377. package/dist/infra/tmp-openclaw-dir.js +0 -81
  378. package/dist/media/path-sanitization.js +0 -78
@@ -1,11 +1,11 @@
1
+ import { resolveSessionAgentId } from "../../agents/agent-scope.js";
1
2
  import { getChannelPlugin, normalizeChannelId } from "../../channels/plugins/index.js";
2
3
  import { DEFAULT_CHAT_CHANNEL } from "../../channels/registry.js";
3
- import { loadConfig } from "../../config/config.js";
4
4
  import { createOutboundSendDeps } from "../../cli/deps.js";
5
+ import { loadConfig } from "../../config/config.js";
5
6
  import { deliverOutboundPayloads } from "../../infra/outbound/deliver.js";
6
- import { normalizeReplyPayloadsForDelivery } from "../../infra/outbound/payloads.js";
7
7
  import { ensureOutboundSessionEntry, resolveOutboundSessionRoute, } from "../../infra/outbound/outbound-session.js";
8
- import { resolveSessionAgentId } from "../../agents/agent-scope.js";
8
+ import { normalizeReplyPayloadsForDelivery } from "../../infra/outbound/payloads.js";
9
9
  import { resolveOutboundTarget } from "../../infra/outbound/targets.js";
10
10
  import { normalizePollInput } from "../../polls.js";
11
11
  import { ErrorCodes, errorShape, formatValidationErrors, validatePollParams, validateSendParams, } from "../protocol/index.js";
@@ -45,11 +45,27 @@ export const sendHandlers = {
45
45
  return;
46
46
  }
47
47
  const to = request.to.trim();
48
- const message = request.message.trim();
49
- const mediaUrls = Array.isArray(request.mediaUrls) ? request.mediaUrls : undefined;
48
+ const message = typeof request.message === "string" ? request.message.trim() : "";
49
+ const mediaUrl = typeof request.mediaUrl === "string" && request.mediaUrl.trim().length > 0
50
+ ? request.mediaUrl.trim()
51
+ : undefined;
52
+ const mediaUrls = Array.isArray(request.mediaUrls)
53
+ ? request.mediaUrls
54
+ .map((entry) => (typeof entry === "string" ? entry.trim() : ""))
55
+ .filter((entry) => entry.length > 0)
56
+ : undefined;
57
+ if (!message && !mediaUrl && (mediaUrls?.length ?? 0) === 0) {
58
+ respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "invalid send params: text or media is required"));
59
+ return;
60
+ }
50
61
  const channelInput = typeof request.channel === "string" ? request.channel : undefined;
51
62
  const normalizedChannel = channelInput ? normalizeChannelId(channelInput) : null;
52
63
  if (channelInput && !normalizedChannel) {
64
+ const normalizedInput = channelInput.trim().toLowerCase();
65
+ if (normalizedInput === "webchat") {
66
+ respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "unsupported channel: webchat (internal-only). Use `chat.send` for WebChat UI messages or choose a deliverable channel."));
67
+ return;
68
+ }
53
69
  respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `unsupported channel: ${channelInput}`));
54
70
  return;
55
71
  }
@@ -57,6 +73,9 @@ export const sendHandlers = {
57
73
  const accountId = typeof request.accountId === "string" && request.accountId.trim().length
58
74
  ? request.accountId.trim()
59
75
  : undefined;
76
+ const threadId = typeof request.threadId === "string" && request.threadId.trim().length
77
+ ? request.threadId.trim()
78
+ : undefined;
60
79
  const outboundChannel = channel;
61
80
  const plugin = getChannelPlugin(channel);
62
81
  if (!plugin) {
@@ -82,7 +101,7 @@ export const sendHandlers = {
82
101
  }
83
102
  const outboundDeps = context.deps ? createOutboundSendDeps(context.deps) : undefined;
84
103
  const mirrorPayloads = normalizeReplyPayloadsForDelivery([
85
- { text: message, mediaUrl: request.mediaUrl, mediaUrls },
104
+ { text: message, mediaUrl, mediaUrls },
86
105
  ]);
87
106
  const mirrorText = mirrorPayloads
88
107
  .map((payload) => payload.text)
@@ -101,6 +120,7 @@ export const sendHandlers = {
101
120
  agentId: derivedAgentId,
102
121
  accountId,
103
122
  target: resolved.to,
123
+ threadId,
104
124
  })
105
125
  : null;
106
126
  if (derivedRoute) {
@@ -117,8 +137,12 @@ export const sendHandlers = {
117
137
  channel: outboundChannel,
118
138
  to: resolved.to,
119
139
  accountId,
120
- payloads: [{ text: message, mediaUrl: request.mediaUrl, mediaUrls }],
140
+ payloads: [{ text: message, mediaUrl, mediaUrls }],
141
+ agentId: providedSessionKey
142
+ ? resolveSessionAgentId({ sessionKey: providedSessionKey, config: cfg })
143
+ : derivedAgentId,
121
144
  gifPlayback: request.gifPlayback,
145
+ threadId: threadId ?? null,
122
146
  deps: outboundDeps,
123
147
  mirror: providedSessionKey
124
148
  ? {
@@ -145,12 +169,15 @@ export const sendHandlers = {
145
169
  messageId: result.messageId,
146
170
  channel,
147
171
  };
148
- if ("chatId" in result)
172
+ if ("chatId" in result) {
149
173
  payload.chatId = result.chatId;
150
- if ("channelId" in result)
174
+ }
175
+ if ("channelId" in result) {
151
176
  payload.channelId = result.channelId;
152
- if ("toJid" in result)
177
+ }
178
+ if ("toJid" in result) {
153
179
  payload.toJid = result.toJid;
180
+ }
154
181
  if ("conversationId" in result) {
155
182
  payload.conversationId = result.conversationId;
156
183
  }
@@ -207,12 +234,24 @@ export const sendHandlers = {
207
234
  return;
208
235
  }
209
236
  const channel = normalizedChannel ?? DEFAULT_CHAT_CHANNEL;
237
+ if (typeof request.durationSeconds === "number" && channel !== "telegram") {
238
+ respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "durationSeconds is only supported for Telegram polls"));
239
+ return;
240
+ }
241
+ if (typeof request.isAnonymous === "boolean" && channel !== "telegram") {
242
+ respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "isAnonymous is only supported for Telegram polls"));
243
+ return;
244
+ }
210
245
  const poll = {
211
246
  question: request.question,
212
247
  options: request.options,
213
248
  maxSelections: request.maxSelections,
249
+ durationSeconds: request.durationSeconds,
214
250
  durationHours: request.durationHours,
215
251
  };
252
+ const threadId = typeof request.threadId === "string" && request.threadId.trim().length
253
+ ? request.threadId.trim()
254
+ : undefined;
216
255
  const accountId = typeof request.accountId === "string" && request.accountId.trim().length
217
256
  ? request.accountId.trim()
218
257
  : undefined;
@@ -243,20 +282,27 @@ export const sendHandlers = {
243
282
  to: resolved.to,
244
283
  poll: normalized,
245
284
  accountId,
285
+ threadId,
286
+ silent: request.silent,
287
+ isAnonymous: request.isAnonymous,
246
288
  });
247
289
  const payload = {
248
290
  runId: idem,
249
291
  messageId: result.messageId,
250
292
  channel,
251
293
  };
252
- if (result.toJid)
294
+ if (result.toJid) {
253
295
  payload.toJid = result.toJid;
254
- if (result.channelId)
296
+ }
297
+ if (result.channelId) {
255
298
  payload.channelId = result.channelId;
256
- if (result.conversationId)
299
+ }
300
+ if (result.conversationId) {
257
301
  payload.conversationId = result.conversationId;
258
- if (result.pollId)
302
+ }
303
+ if (result.pollId) {
259
304
  payload.pollId = result.pollId;
305
+ }
260
306
  context.dedupe.set(`poll:${idem}`, {
261
307
  ts: Date.now(),
262
308
  ok: true,
@@ -1,18 +1,91 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import fs from "node:fs";
3
+ import { resolveDefaultAgentId } from "../../agents/agent-scope.js";
3
4
  import { abortEmbeddedPiRun, waitForEmbeddedPiRunEnd } from "../../agents/pi-embedded.js";
4
5
  import { stopSubagentsForRequester } from "../../auto-reply/reply/abort.js";
5
6
  import { clearSessionQueues } from "../../auto-reply/reply/queue.js";
6
7
  import { loadConfig } from "../../config/config.js";
7
8
  import { loadSessionStore, snapshotSessionOrigin, resolveMainSessionKey, updateSessionStore, } from "../../config/sessions.js";
8
- import { ErrorCodes, errorShape, formatValidationErrors, validateSessionsCompactParams, validateSessionsDeleteParams, validateSessionsListParams, validateSessionsPatchParams, validateSessionsPreviewParams, validateSessionsResetParams, validateSessionsResolveParams, } from "../protocol/index.js";
9
- import { archiveFileOnDisk, listSessionsFromStore, loadCombinedSessionStoreForGateway, loadSessionEntry, readSessionPreviewItemsFromTranscript, resolveGatewaySessionStoreTarget, resolveSessionTranscriptCandidates, } from "../session-utils.js";
9
+ import { createInternalHookEvent, triggerInternalHook } from "../../hooks/internal-hooks.js";
10
+ import { normalizeAgentId, parseAgentSessionKey } from "../../routing/session-key.js";
11
+ import { ErrorCodes, errorShape, validateSessionsCompactParams, validateSessionsDeleteParams, validateSessionsListParams, validateSessionsPatchParams, validateSessionsPreviewParams, validateSessionsResetParams, validateSessionsResolveParams, } from "../protocol/index.js";
12
+ import { archiveFileOnDisk, archiveSessionTranscripts, listSessionsFromStore, loadCombinedSessionStoreForGateway, loadSessionEntry, pruneLegacyStoreKeys, readSessionPreviewItemsFromTranscript, resolveGatewaySessionStoreTarget, resolveSessionModelRef, resolveSessionTranscriptCandidates, } from "../session-utils.js";
10
13
  import { applySessionsPatchToStore } from "../sessions-patch.js";
11
14
  import { resolveSessionKeyFromResolveParams } from "../sessions-resolve.js";
15
+ import { assertValidParams } from "./validation.js";
16
+ function requireSessionKey(key, respond) {
17
+ const raw = typeof key === "string"
18
+ ? key
19
+ : typeof key === "number"
20
+ ? String(key)
21
+ : typeof key === "bigint"
22
+ ? String(key)
23
+ : "";
24
+ const normalized = raw.trim();
25
+ if (!normalized) {
26
+ respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "key required"));
27
+ return null;
28
+ }
29
+ return normalized;
30
+ }
31
+ function resolveGatewaySessionTargetFromKey(key) {
32
+ const cfg = loadConfig();
33
+ const target = resolveGatewaySessionStoreTarget({ cfg, key });
34
+ return { cfg, target, storePath: target.storePath };
35
+ }
36
+ function migrateAndPruneSessionStoreKey(params) {
37
+ const target = resolveGatewaySessionStoreTarget({
38
+ cfg: params.cfg,
39
+ key: params.key,
40
+ store: params.store,
41
+ });
42
+ const primaryKey = target.canonicalKey;
43
+ if (!params.store[primaryKey]) {
44
+ const existingKey = target.storeKeys.find((candidate) => Boolean(params.store[candidate]));
45
+ if (existingKey) {
46
+ params.store[primaryKey] = params.store[existingKey];
47
+ }
48
+ }
49
+ pruneLegacyStoreKeys({
50
+ store: params.store,
51
+ canonicalKey: primaryKey,
52
+ candidates: target.storeKeys,
53
+ });
54
+ return { target, primaryKey, entry: params.store[primaryKey] };
55
+ }
56
+ function archiveSessionTranscriptsForSession(params) {
57
+ if (!params.sessionId) {
58
+ return [];
59
+ }
60
+ return archiveSessionTranscripts({
61
+ sessionId: params.sessionId,
62
+ storePath: params.storePath,
63
+ sessionFile: params.sessionFile,
64
+ agentId: params.agentId,
65
+ reason: params.reason,
66
+ });
67
+ }
68
+ async function ensureSessionRuntimeCleanup(params) {
69
+ const queueKeys = new Set(params.target.storeKeys);
70
+ queueKeys.add(params.target.canonicalKey);
71
+ if (params.sessionId) {
72
+ queueKeys.add(params.sessionId);
73
+ }
74
+ clearSessionQueues([...queueKeys]);
75
+ stopSubagentsForRequester({ cfg: params.cfg, requesterSessionKey: params.target.canonicalKey });
76
+ if (!params.sessionId) {
77
+ return undefined;
78
+ }
79
+ abortEmbeddedPiRun(params.sessionId);
80
+ const ended = await waitForEmbeddedPiRunEnd(params.sessionId, 15_000);
81
+ if (ended) {
82
+ return undefined;
83
+ }
84
+ return errorShape(ErrorCodes.UNAVAILABLE, `Session ${params.key} is still active; try again in a moment.`);
85
+ }
12
86
  export const sessionsHandlers = {
13
87
  "sessions.list": ({ params, respond }) => {
14
- if (!validateSessionsListParams(params)) {
15
- respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `invalid sessions.list params: ${formatValidationErrors(validateSessionsListParams.errors)}`));
88
+ if (!assertValidParams(params, validateSessionsListParams, "sessions.list", respond)) {
16
89
  return;
17
90
  }
18
91
  const p = params;
@@ -27,8 +100,7 @@ export const sessionsHandlers = {
27
100
  respond(true, result, undefined);
28
101
  },
29
102
  "sessions.preview": ({ params, respond }) => {
30
- if (!validateSessionsPreviewParams(params)) {
31
- respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `invalid sessions.preview params: ${formatValidationErrors(validateSessionsPreviewParams.errors)}`));
103
+ if (!assertValidParams(params, validateSessionsPreviewParams, "sessions.preview", respond)) {
32
104
  return;
33
105
  }
34
106
  const p = params;
@@ -50,11 +122,15 @@ export const sessionsHandlers = {
50
122
  const previews = [];
51
123
  for (const key of keys) {
52
124
  try {
53
- const target = resolveGatewaySessionStoreTarget({ cfg, key });
54
- const store = storeCache.get(target.storePath) ?? loadSessionStore(target.storePath);
55
- storeCache.set(target.storePath, store);
56
- const entry = target.storeKeys.map((candidate) => store[candidate]).find(Boolean) ??
57
- store[target.canonicalKey];
125
+ const storeTarget = resolveGatewaySessionStoreTarget({ cfg, key, scanLegacyKeys: false });
126
+ const store = storeCache.get(storeTarget.storePath) ?? loadSessionStore(storeTarget.storePath);
127
+ storeCache.set(storeTarget.storePath, store);
128
+ const target = resolveGatewaySessionStoreTarget({
129
+ cfg,
130
+ key,
131
+ store,
132
+ });
133
+ const entry = target.storeKeys.map((candidate) => store[candidate]).find(Boolean);
58
134
  if (!entry?.sessionId) {
59
135
  previews.push({ key, status: "missing", items: [] });
60
136
  continue;
@@ -72,14 +148,13 @@ export const sessionsHandlers = {
72
148
  }
73
149
  respond(true, { ts: Date.now(), previews }, undefined);
74
150
  },
75
- "sessions.resolve": ({ params, respond }) => {
76
- if (!validateSessionsResolveParams(params)) {
77
- respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `invalid sessions.resolve params: ${formatValidationErrors(validateSessionsResolveParams.errors)}`));
151
+ "sessions.resolve": async ({ params, respond }) => {
152
+ if (!assertValidParams(params, validateSessionsResolveParams, "sessions.resolve", respond)) {
78
153
  return;
79
154
  }
80
155
  const p = params;
81
156
  const cfg = loadConfig();
82
- const resolved = resolveSessionKeyFromResolveParams({ cfg, p });
157
+ const resolved = await resolveSessionKeyFromResolveParams({ cfg, p });
83
158
  if (!resolved.ok) {
84
159
  respond(false, undefined, resolved.error);
85
160
  return;
@@ -87,26 +162,17 @@ export const sessionsHandlers = {
87
162
  respond(true, { ok: true, key: resolved.key }, undefined);
88
163
  },
89
164
  "sessions.patch": async ({ params, respond, context }) => {
90
- if (!validateSessionsPatchParams(params)) {
91
- respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `invalid sessions.patch params: ${formatValidationErrors(validateSessionsPatchParams.errors)}`));
165
+ if (!assertValidParams(params, validateSessionsPatchParams, "sessions.patch", respond)) {
92
166
  return;
93
167
  }
94
168
  const p = params;
95
- const key = String(p.key ?? "").trim();
169
+ const key = requireSessionKey(p.key, respond);
96
170
  if (!key) {
97
- respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "key required"));
98
171
  return;
99
172
  }
100
- const cfg = loadConfig();
101
- const target = resolveGatewaySessionStoreTarget({ cfg, key });
102
- const storePath = target.storePath;
173
+ const { cfg, target, storePath } = resolveGatewaySessionTargetFromKey(key);
103
174
  const applied = await updateSessionStore(storePath, async (store) => {
104
- const primaryKey = target.storeKeys[0] ?? key;
105
- const existingKey = target.storeKeys.find((candidate) => store[candidate]);
106
- if (existingKey && existingKey !== primaryKey && !store[primaryKey]) {
107
- store[primaryKey] = store[existingKey];
108
- delete store[existingKey];
109
- }
175
+ const { primaryKey } = migrateAndPruneSessionStoreKey({ cfg, key, store });
110
176
  return await applySessionsPatchToStore({
111
177
  cfg,
112
178
  store,
@@ -119,36 +185,53 @@ export const sessionsHandlers = {
119
185
  respond(false, undefined, applied.error);
120
186
  return;
121
187
  }
188
+ const parsed = parseAgentSessionKey(target.canonicalKey ?? key);
189
+ const agentId = normalizeAgentId(parsed?.agentId ?? resolveDefaultAgentId(cfg));
190
+ const resolved = resolveSessionModelRef(cfg, applied.entry, agentId);
122
191
  const result = {
123
192
  ok: true,
124
193
  path: storePath,
125
194
  key: target.canonicalKey,
126
195
  entry: applied.entry,
196
+ resolved: {
197
+ modelProvider: resolved.provider,
198
+ model: resolved.model,
199
+ },
127
200
  };
128
201
  respond(true, result, undefined);
129
202
  },
130
203
  "sessions.reset": async ({ params, respond }) => {
131
- if (!validateSessionsResetParams(params)) {
132
- respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `invalid sessions.reset params: ${formatValidationErrors(validateSessionsResetParams.errors)}`));
204
+ if (!assertValidParams(params, validateSessionsResetParams, "sessions.reset", respond)) {
133
205
  return;
134
206
  }
135
207
  const p = params;
136
- const key = String(p.key ?? "").trim();
208
+ const key = requireSessionKey(p.key, respond);
137
209
  if (!key) {
138
- respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "key required"));
139
210
  return;
140
211
  }
141
- const cfg = loadConfig();
142
- const target = resolveGatewaySessionStoreTarget({ cfg, key });
143
- const storePath = target.storePath;
212
+ const { cfg, target, storePath } = resolveGatewaySessionTargetFromKey(key);
213
+ const { entry } = loadSessionEntry(key);
214
+ const commandReason = p.reason === "new" ? "new" : "reset";
215
+ const hookEvent = createInternalHookEvent("command", commandReason, target.canonicalKey ?? key, {
216
+ sessionEntry: entry,
217
+ previousSessionEntry: entry,
218
+ commandSource: "gateway:sessions.reset",
219
+ cfg,
220
+ });
221
+ await triggerInternalHook(hookEvent);
222
+ const sessionId = entry?.sessionId;
223
+ const cleanupError = await ensureSessionRuntimeCleanup({ cfg, key, target, sessionId });
224
+ if (cleanupError) {
225
+ respond(false, undefined, cleanupError);
226
+ return;
227
+ }
228
+ let oldSessionId;
229
+ let oldSessionFile;
144
230
  const next = await updateSessionStore(storePath, (store) => {
145
- const primaryKey = target.storeKeys[0] ?? key;
146
- const existingKey = target.storeKeys.find((candidate) => store[candidate]);
147
- if (existingKey && existingKey !== primaryKey && !store[primaryKey]) {
148
- store[primaryKey] = store[existingKey];
149
- delete store[existingKey];
150
- }
231
+ const { primaryKey } = migrateAndPruneSessionStoreKey({ cfg, key, store });
151
232
  const entry = store[primaryKey];
233
+ oldSessionId = entry?.sessionId;
234
+ oldSessionFile = entry?.sessionFile;
152
235
  const now = Date.now();
153
236
  const nextEntry = {
154
237
  sessionId: randomUUID(),
@@ -171,100 +254,79 @@ export const sessionsHandlers = {
171
254
  inputTokens: 0,
172
255
  outputTokens: 0,
173
256
  totalTokens: 0,
257
+ totalTokensFresh: true,
174
258
  };
175
259
  store[primaryKey] = nextEntry;
176
260
  return nextEntry;
177
261
  });
262
+ // Archive old transcript so it doesn't accumulate on disk (#14869).
263
+ archiveSessionTranscriptsForSession({
264
+ sessionId: oldSessionId,
265
+ storePath,
266
+ sessionFile: oldSessionFile,
267
+ agentId: target.agentId,
268
+ reason: "reset",
269
+ });
178
270
  respond(true, { ok: true, key: target.canonicalKey, entry: next }, undefined);
179
271
  },
180
272
  "sessions.delete": async ({ params, respond }) => {
181
- if (!validateSessionsDeleteParams(params)) {
182
- respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `invalid sessions.delete params: ${formatValidationErrors(validateSessionsDeleteParams.errors)}`));
273
+ if (!assertValidParams(params, validateSessionsDeleteParams, "sessions.delete", respond)) {
183
274
  return;
184
275
  }
185
276
  const p = params;
186
- const key = String(p.key ?? "").trim();
277
+ const key = requireSessionKey(p.key, respond);
187
278
  if (!key) {
188
- respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "key required"));
189
279
  return;
190
280
  }
191
- const cfg = loadConfig();
281
+ const { cfg, target, storePath } = resolveGatewaySessionTargetFromKey(key);
192
282
  const mainKey = resolveMainSessionKey(cfg);
193
- const target = resolveGatewaySessionStoreTarget({ cfg, key });
194
283
  if (target.canonicalKey === mainKey) {
195
284
  respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `Cannot delete the main session (${mainKey}).`));
196
285
  return;
197
286
  }
198
287
  const deleteTranscript = typeof p.deleteTranscript === "boolean" ? p.deleteTranscript : true;
199
- const storePath = target.storePath;
200
288
  const { entry } = loadSessionEntry(key);
201
289
  const sessionId = entry?.sessionId;
202
290
  const existed = Boolean(entry);
203
- const queueKeys = new Set(target.storeKeys);
204
- queueKeys.add(target.canonicalKey);
205
- if (sessionId)
206
- queueKeys.add(sessionId);
207
- clearSessionQueues([...queueKeys]);
208
- stopSubagentsForRequester({ cfg, requesterSessionKey: target.canonicalKey });
209
- if (sessionId) {
210
- abortEmbeddedPiRun(sessionId);
211
- const ended = await waitForEmbeddedPiRunEnd(sessionId, 15_000);
212
- if (!ended) {
213
- respond(false, undefined, errorShape(ErrorCodes.UNAVAILABLE, `Session ${key} is still active; try again in a moment.`));
214
- return;
215
- }
291
+ const cleanupError = await ensureSessionRuntimeCleanup({ cfg, key, target, sessionId });
292
+ if (cleanupError) {
293
+ respond(false, undefined, cleanupError);
294
+ return;
216
295
  }
217
296
  await updateSessionStore(storePath, (store) => {
218
- const primaryKey = target.storeKeys[0] ?? key;
219
- const existingKey = target.storeKeys.find((candidate) => store[candidate]);
220
- if (existingKey && existingKey !== primaryKey && !store[primaryKey]) {
221
- store[primaryKey] = store[existingKey];
222
- delete store[existingKey];
223
- }
224
- if (store[primaryKey])
297
+ const { primaryKey } = migrateAndPruneSessionStoreKey({ cfg, key, store });
298
+ if (store[primaryKey]) {
225
299
  delete store[primaryKey];
226
- });
227
- const archived = [];
228
- if (deleteTranscript && sessionId) {
229
- for (const candidate of resolveSessionTranscriptCandidates(sessionId, storePath, entry?.sessionFile, target.agentId)) {
230
- if (!fs.existsSync(candidate))
231
- continue;
232
- try {
233
- archived.push(archiveFileOnDisk(candidate, "deleted"));
234
- }
235
- catch {
236
- // Best-effort.
237
- }
238
300
  }
239
- }
301
+ });
302
+ const archived = deleteTranscript
303
+ ? archiveSessionTranscriptsForSession({
304
+ sessionId,
305
+ storePath,
306
+ sessionFile: entry?.sessionFile,
307
+ agentId: target.agentId,
308
+ reason: "deleted",
309
+ })
310
+ : [];
240
311
  respond(true, { ok: true, key: target.canonicalKey, deleted: existed, archived }, undefined);
241
312
  },
242
313
  "sessions.compact": async ({ params, respond }) => {
243
- if (!validateSessionsCompactParams(params)) {
244
- respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, `invalid sessions.compact params: ${formatValidationErrors(validateSessionsCompactParams.errors)}`));
314
+ if (!assertValidParams(params, validateSessionsCompactParams, "sessions.compact", respond)) {
245
315
  return;
246
316
  }
247
317
  const p = params;
248
- const key = String(p.key ?? "").trim();
318
+ const key = requireSessionKey(p.key, respond);
249
319
  if (!key) {
250
- respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "key required"));
251
320
  return;
252
321
  }
253
322
  const maxLines = typeof p.maxLines === "number" && Number.isFinite(p.maxLines)
254
323
  ? Math.max(1, Math.floor(p.maxLines))
255
324
  : 400;
256
- const cfg = loadConfig();
257
- const target = resolveGatewaySessionStoreTarget({ cfg, key });
258
- const storePath = target.storePath;
325
+ const { cfg, target, storePath } = resolveGatewaySessionTargetFromKey(key);
259
326
  // Lock + read in a short critical section; transcript work happens outside.
260
327
  const compactTarget = await updateSessionStore(storePath, (store) => {
261
- const primaryKey = target.storeKeys[0] ?? key;
262
- const existingKey = target.storeKeys.find((candidate) => store[candidate]);
263
- if (existingKey && existingKey !== primaryKey && !store[primaryKey]) {
264
- store[primaryKey] = store[existingKey];
265
- delete store[existingKey];
266
- }
267
- return { entry: store[primaryKey], primaryKey };
328
+ const { entry, primaryKey } = migrateAndPruneSessionStoreKey({ cfg, key, store });
329
+ return { entry, primaryKey };
268
330
  });
269
331
  const entry = compactTarget.entry;
270
332
  const sessionId = entry?.sessionId;
@@ -304,11 +366,13 @@ export const sessionsHandlers = {
304
366
  await updateSessionStore(storePath, (store) => {
305
367
  const entryKey = compactTarget.primaryKey;
306
368
  const entryToUpdate = store[entryKey];
307
- if (!entryToUpdate)
369
+ if (!entryToUpdate) {
308
370
  return;
371
+ }
309
372
  delete entryToUpdate.inputTokens;
310
373
  delete entryToUpdate.outputTokens;
311
374
  delete entryToUpdate.totalTokens;
375
+ delete entryToUpdate.totalTokensFresh;
312
376
  entryToUpdate.updatedAt = Date.now();
313
377
  });
314
378
  respond(true, {
@@ -4,6 +4,7 @@ import { setHeartbeatsEnabled } from "../../infra/heartbeat-runner.js";
4
4
  import { enqueueSystemEvent, isSystemEventContextChanged } from "../../infra/system-events.js";
5
5
  import { listSystemPresence, updateSystemPresence } from "../../infra/system-presence.js";
6
6
  import { ErrorCodes, errorShape } from "../protocol/index.js";
7
+ import { broadcastPresenceSnapshot } from "../server/presence-events.js";
7
8
  export const systemHandlers = {
8
9
  "last-heartbeat": ({ respond }) => {
9
10
  respond(true, getLastHeartbeatEvent(), undefined);
@@ -109,13 +110,10 @@ export const systemHandlers = {
109
110
  else {
110
111
  enqueueSystemEvent(text, { sessionKey });
111
112
  }
112
- const nextPresenceVersion = context.incrementPresenceVersion();
113
- context.broadcast("presence", { presence: listSystemPresence() }, {
114
- dropIfSlow: true,
115
- stateVersion: {
116
- presence: nextPresenceVersion,
117
- health: context.getHealthVersion(),
118
- },
113
+ broadcastPresenceSnapshot({
114
+ broadcast: context.broadcast,
115
+ incrementPresenceVersion: context.incrementPresenceVersion,
116
+ getHealthVersion: context.getHealthVersion,
119
117
  });
120
118
  respond(true, { ok: true }, undefined);
121
119
  },
@@ -23,14 +23,22 @@ const BASE_METHODS = [
23
23
  "exec.approvals.node.get",
24
24
  "exec.approvals.node.set",
25
25
  "exec.approval.request",
26
+ "exec.approval.waitDecision",
26
27
  "exec.approval.resolve",
27
28
  "wizard.start",
28
29
  "wizard.next",
29
30
  "wizard.cancel",
30
31
  "wizard.status",
32
+ "talk.config",
31
33
  "talk.mode",
32
34
  "models.list",
33
35
  "agents.list",
36
+ "agents.create",
37
+ "agents.update",
38
+ "agents.delete",
39
+ "agents.files.list",
40
+ "agents.files.get",
41
+ "agents.files.set",
34
42
  "skills.status",
35
43
  "skills.bins",
36
44
  "skills.install",