@poolzin/pool-bot 2026.2.21 → 2026.2.22

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 (369) hide show
  1. package/CHANGELOG.md +17 -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/diagnostics-otel/package.json +1 -1
  326. package/extensions/discord/package.json +1 -1
  327. package/extensions/feishu/package.json +1 -1
  328. package/extensions/google-antigravity-auth/package.json +1 -1
  329. package/extensions/google-gemini-cli-auth/package.json +1 -1
  330. package/extensions/googlechat/package.json +1 -1
  331. package/extensions/imessage/package.json +1 -1
  332. package/extensions/irc/package.json +1 -1
  333. package/extensions/line/package.json +1 -1
  334. package/extensions/llm-task/package.json +1 -1
  335. package/extensions/lobster/package.json +1 -1
  336. package/extensions/matrix/CHANGELOG.md +5 -0
  337. package/extensions/matrix/package.json +1 -1
  338. package/extensions/mattermost/package.json +1 -1
  339. package/extensions/memory-core/package.json +1 -1
  340. package/extensions/memory-lancedb/package.json +1 -1
  341. package/extensions/minimax-portal-auth/package.json +1 -1
  342. package/extensions/msteams/CHANGELOG.md +5 -0
  343. package/extensions/msteams/package.json +1 -1
  344. package/extensions/nextcloud-talk/package.json +1 -1
  345. package/extensions/nostr/CHANGELOG.md +5 -0
  346. package/extensions/nostr/package.json +1 -1
  347. package/extensions/open-prose/package.json +1 -1
  348. package/extensions/openai-codex-auth/package.json +1 -1
  349. package/extensions/signal/package.json +1 -1
  350. package/extensions/slack/package.json +1 -1
  351. package/extensions/telegram/package.json +1 -1
  352. package/extensions/tlon/package.json +1 -1
  353. package/extensions/twitch/CHANGELOG.md +5 -0
  354. package/extensions/twitch/package.json +1 -1
  355. package/extensions/voice-call/CHANGELOG.md +5 -0
  356. package/extensions/voice-call/package.json +1 -1
  357. package/extensions/whatsapp/package.json +1 -1
  358. package/extensions/zalo/CHANGELOG.md +5 -0
  359. package/extensions/zalo/package.json +1 -1
  360. package/extensions/zalouser/CHANGELOG.md +5 -0
  361. package/extensions/zalouser/package.json +1 -1
  362. package/package.json +1 -1
  363. package/skills/apple-reminders/SKILL.md +100 -49
  364. package/skills/coding-agent/SKILL.md +34 -28
  365. package/skills/github/SKILL.md +131 -16
  366. package/skills/imsg/SKILL.md +112 -15
  367. package/skills/openhue/SKILL.md +101 -19
  368. package/skills/tmux/SKILL.md +111 -79
  369. package/skills/weather/SKILL.md +88 -25
@@ -3,15 +3,15 @@ import path from "node:path";
3
3
  import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
4
4
  import { lookupContextTokens } from "../agents/context.js";
5
5
  import { DEFAULT_CONTEXT_TOKENS, DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js";
6
- import { resolveConfiguredModelRef, resolveDefaultModelForAgent, } from "../agents/model-selection.js";
6
+ import { parseModelRef, resolveConfiguredModelRef, resolveDefaultModelForAgent, } from "../agents/model-selection.js";
7
7
  import { loadConfig } from "../config/config.js";
8
8
  import { resolveStateDir } from "../config/paths.js";
9
- import { buildGroupDisplayName, canonicalizeMainSessionAlias, loadSessionStore, resolveMainSessionKey, resolveStorePath, } from "../config/sessions.js";
9
+ import { buildGroupDisplayName, canonicalizeMainSessionAlias, loadSessionStore, resolveAgentMainSessionKey, resolveFreshSessionTotalTokens, resolveMainSessionKey, resolveStorePath, } from "../config/sessions.js";
10
10
  import { normalizeAgentId, normalizeMainKey, parseAgentSessionKey, } from "../routing/session-key.js";
11
11
  import { isCronRunSessionKey } from "../sessions/session-key-utils.js";
12
12
  import { normalizeSessionDeliveryFields } from "../utils/delivery-context.js";
13
- import { readFirstUserMessageFromTranscript, readLastMessagePreviewFromTranscript, } from "./session-utils.fs.js";
14
- export { archiveFileOnDisk, capArrayByJsonBytes, readFirstUserMessageFromTranscript, readLastMessagePreviewFromTranscript, readSessionPreviewItemsFromTranscript, readSessionMessages, resolveSessionTranscriptCandidates, } from "./session-utils.fs.js";
13
+ import { readSessionTitleFieldsFromTranscript } from "./session-utils.fs.js";
14
+ export { archiveFileOnDisk, archiveSessionTranscripts, capArrayByJsonBytes, readFirstUserMessageFromTranscript, readLastMessagePreviewFromTranscript, readSessionTitleFieldsFromTranscript, readSessionPreviewItemsFromTranscript, readSessionMessages, resolveSessionTranscriptCandidates, } from "./session-utils.fs.js";
15
15
  const DERIVED_TITLE_MAX_LEN = 60;
16
16
  const AVATAR_MAX_BYTES = 2 * 1024 * 1024;
17
17
  const AVATAR_DATA_RE = /^data:/i;
@@ -34,34 +34,43 @@ function resolveAvatarMime(filePath) {
34
34
  return AVATAR_MIME_BY_EXT[ext] ?? "application/octet-stream";
35
35
  }
36
36
  function isWorkspaceRelativePath(value) {
37
- if (!value)
37
+ if (!value) {
38
38
  return false;
39
- if (value.startsWith("~"))
39
+ }
40
+ if (value.startsWith("~")) {
40
41
  return false;
41
- if (AVATAR_SCHEME_RE.test(value) && !WINDOWS_ABS_RE.test(value))
42
+ }
43
+ if (AVATAR_SCHEME_RE.test(value) && !WINDOWS_ABS_RE.test(value)) {
42
44
  return false;
45
+ }
43
46
  return true;
44
47
  }
45
48
  function resolveIdentityAvatarUrl(cfg, agentId, avatar) {
46
- if (!avatar)
49
+ if (!avatar) {
47
50
  return undefined;
51
+ }
48
52
  const trimmed = avatar.trim();
49
- if (!trimmed)
53
+ if (!trimmed) {
50
54
  return undefined;
51
- if (AVATAR_DATA_RE.test(trimmed) || AVATAR_HTTP_RE.test(trimmed))
55
+ }
56
+ if (AVATAR_DATA_RE.test(trimmed) || AVATAR_HTTP_RE.test(trimmed)) {
52
57
  return trimmed;
53
- if (!isWorkspaceRelativePath(trimmed))
58
+ }
59
+ if (!isWorkspaceRelativePath(trimmed)) {
54
60
  return undefined;
61
+ }
55
62
  const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
56
63
  const workspaceRoot = path.resolve(workspaceDir);
57
64
  const resolved = path.resolve(workspaceRoot, trimmed);
58
65
  const relative = path.relative(workspaceRoot, resolved);
59
- if (relative.startsWith("..") || path.isAbsolute(relative))
66
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
60
67
  return undefined;
68
+ }
61
69
  try {
62
70
  const stat = fs.statSync(resolved);
63
- if (!stat.isFile() || stat.size > AVATAR_MAX_BYTES)
71
+ if (!stat.isFile() || stat.size > AVATAR_MAX_BYTES) {
64
72
  return undefined;
73
+ }
65
74
  const buffer = fs.readFileSync(resolved);
66
75
  const mime = resolveAvatarMime(resolved);
67
76
  return `data:${mime};base64,${buffer.toString("base64")}`;
@@ -80,17 +89,20 @@ function formatSessionIdPrefix(sessionId, updatedAt) {
80
89
  return prefix;
81
90
  }
82
91
  function truncateTitle(text, maxLen) {
83
- if (text.length <= maxLen)
92
+ if (text.length <= maxLen) {
84
93
  return text;
94
+ }
85
95
  const cut = text.slice(0, maxLen - 1);
86
96
  const lastSpace = cut.lastIndexOf(" ");
87
- if (lastSpace > maxLen * 0.6)
97
+ if (lastSpace > maxLen * 0.6) {
88
98
  return cut.slice(0, lastSpace) + "…";
99
+ }
89
100
  return cut + "…";
90
101
  }
91
102
  export function deriveSessionTitle(entry, firstUserMessage) {
92
- if (!entry)
103
+ if (!entry) {
93
104
  return undefined;
105
+ }
94
106
  if (entry.displayName?.trim()) {
95
107
  return entry.displayName.trim();
96
108
  }
@@ -113,14 +125,76 @@ export function loadSessionEntry(sessionKey) {
113
125
  const agentId = resolveSessionStoreAgentId(cfg, canonicalKey);
114
126
  const storePath = resolveStorePath(sessionCfg?.store, { agentId });
115
127
  const store = loadSessionStore(storePath);
116
- const entry = store[canonicalKey];
117
- return { cfg, storePath, store, entry, canonicalKey };
128
+ const match = findStoreMatch(store, canonicalKey, sessionKey.trim());
129
+ const legacyKey = match?.key !== canonicalKey ? match?.key : undefined;
130
+ return { cfg, storePath, store, entry: match?.entry, canonicalKey, legacyKey };
131
+ }
132
+ /**
133
+ * Find a session entry by exact or case-insensitive key match.
134
+ * Returns both the entry and the actual store key it was found under,
135
+ * so callers can clean up legacy mixed-case keys when they differ from canonicalKey.
136
+ */
137
+ function findStoreMatch(store, ...candidates) {
138
+ // Exact match first.
139
+ for (const candidate of candidates) {
140
+ if (candidate && store[candidate]) {
141
+ return { entry: store[candidate], key: candidate };
142
+ }
143
+ }
144
+ // Case-insensitive scan for ALL candidates.
145
+ const loweredSet = new Set(candidates.filter(Boolean).map((c) => c.toLowerCase()));
146
+ for (const key of Object.keys(store)) {
147
+ if (loweredSet.has(key.toLowerCase())) {
148
+ return { entry: store[key], key };
149
+ }
150
+ }
151
+ return undefined;
152
+ }
153
+ /**
154
+ * Find all on-disk store keys that match the given key case-insensitively.
155
+ * Returns every key from the store whose lowercased form equals the target's lowercased form.
156
+ */
157
+ export function findStoreKeysIgnoreCase(store, targetKey) {
158
+ const lowered = targetKey.toLowerCase();
159
+ const matches = [];
160
+ for (const key of Object.keys(store)) {
161
+ if (key.toLowerCase() === lowered) {
162
+ matches.push(key);
163
+ }
164
+ }
165
+ return matches;
166
+ }
167
+ /**
168
+ * Remove legacy key variants for one canonical session key.
169
+ * Candidates can include aliases (for example, "agent:ops:main" when canonical is "agent:ops:work").
170
+ */
171
+ export function pruneLegacyStoreKeys(params) {
172
+ const keysToDelete = new Set();
173
+ for (const candidate of params.candidates) {
174
+ const trimmed = String(candidate ?? "").trim();
175
+ if (!trimmed) {
176
+ continue;
177
+ }
178
+ if (trimmed !== params.canonicalKey) {
179
+ keysToDelete.add(trimmed);
180
+ }
181
+ for (const match of findStoreKeysIgnoreCase(params.store, trimmed)) {
182
+ if (match !== params.canonicalKey) {
183
+ keysToDelete.add(match);
184
+ }
185
+ }
186
+ }
187
+ for (const key of keysToDelete) {
188
+ delete params.store[key];
189
+ }
118
190
  }
119
191
  export function classifySessionKey(key, entry) {
120
- if (key === "global")
192
+ if (key === "global") {
121
193
  return "global";
122
- if (key === "unknown")
194
+ }
195
+ if (key === "unknown") {
123
196
  return "unknown";
197
+ }
124
198
  if (entry?.chatType === "group" || entry?.chatType === "channel") {
125
199
  return "group";
126
200
  }
@@ -164,8 +238,9 @@ function listConfiguredAgentIds(cfg) {
164
238
  if (agents.length > 0) {
165
239
  const ids = new Set();
166
240
  for (const entry of agents) {
167
- if (entry?.id)
241
+ if (entry?.id) {
168
242
  ids.add(normalizeAgentId(entry.id));
243
+ }
169
244
  }
170
245
  const defaultId = normalizeAgentId(resolveDefaultAgentId(cfg));
171
246
  ids.add(defaultId);
@@ -178,8 +253,9 @@ function listConfiguredAgentIds(cfg) {
178
253
  const ids = new Set();
179
254
  const defaultId = normalizeAgentId(resolveDefaultAgentId(cfg));
180
255
  ids.add(defaultId);
181
- for (const id of listExistingAgentIdsFromDisk())
256
+ for (const id of listExistingAgentIdsFromDisk()) {
182
257
  ids.add(id);
258
+ }
183
259
  const sorted = Array.from(ids).filter(Boolean);
184
260
  sorted.sort((a, b) => a.localeCompare(b));
185
261
  if (sorted.includes(defaultId)) {
@@ -193,8 +269,9 @@ export function listAgentsForGateway(cfg) {
193
269
  const scope = cfg.session?.scope ?? "per-sender";
194
270
  const configuredById = new Map();
195
271
  for (const entry of cfg.agents?.list ?? []) {
196
- if (!entry?.id)
272
+ if (!entry?.id) {
197
273
  continue;
274
+ }
198
275
  const identity = entry.identity
199
276
  ? {
200
277
  name: entry.identity.name?.trim() || undefined,
@@ -214,7 +291,7 @@ export function listAgentsForGateway(cfg) {
214
291
  .filter(Boolean));
215
292
  const allowedIds = explicitIds.size > 0 ? new Set([...explicitIds, defaultId]) : null;
216
293
  let agentIds = listConfiguredAgentIds(cfg).filter((id) => allowedIds ? allowedIds.has(id) : true);
217
- if (mainKey && !agentIds.includes(mainKey)) {
294
+ if (mainKey && !agentIds.includes(mainKey) && (!allowedIds || allowedIds.has(mainKey))) {
218
295
  agentIds = [...agentIds, mainKey];
219
296
  }
220
297
  const agents = agentIds.map((id) => {
@@ -228,58 +305,79 @@ export function listAgentsForGateway(cfg) {
228
305
  return { defaultId, mainKey, scope, agents };
229
306
  }
230
307
  function canonicalizeSessionKeyForAgent(agentId, key) {
231
- if (key === "global" || key === "unknown")
232
- return key;
233
- if (key.startsWith("agent:"))
234
- return key;
235
- return `agent:${normalizeAgentId(agentId)}:${key}`;
308
+ const lowered = key.toLowerCase();
309
+ if (lowered === "global" || lowered === "unknown") {
310
+ return lowered;
311
+ }
312
+ if (lowered.startsWith("agent:")) {
313
+ return lowered;
314
+ }
315
+ return `agent:${normalizeAgentId(agentId)}:${lowered}`;
236
316
  }
237
317
  function resolveDefaultStoreAgentId(cfg) {
238
318
  return normalizeAgentId(resolveDefaultAgentId(cfg));
239
319
  }
240
320
  export function resolveSessionStoreKey(params) {
241
321
  const raw = params.sessionKey.trim();
242
- if (!raw)
243
- return raw;
244
- if (raw === "global" || raw === "unknown")
322
+ if (!raw) {
245
323
  return raw;
324
+ }
325
+ const rawLower = raw.toLowerCase();
326
+ if (rawLower === "global" || rawLower === "unknown") {
327
+ return rawLower;
328
+ }
246
329
  const parsed = parseAgentSessionKey(raw);
247
330
  if (parsed) {
248
331
  const agentId = normalizeAgentId(parsed.agentId);
332
+ const lowered = raw.toLowerCase();
249
333
  const canonical = canonicalizeMainSessionAlias({
250
334
  cfg: params.cfg,
251
335
  agentId,
252
- sessionKey: raw,
336
+ sessionKey: lowered,
253
337
  });
254
- if (canonical !== raw)
338
+ if (canonical !== lowered) {
255
339
  return canonical;
256
- return raw;
340
+ }
341
+ return lowered;
257
342
  }
343
+ const lowered = raw.toLowerCase();
258
344
  const rawMainKey = normalizeMainKey(params.cfg.session?.mainKey);
259
- if (raw === "main" || raw === rawMainKey) {
345
+ if (lowered === "main" || lowered === rawMainKey) {
260
346
  return resolveMainSessionKey(params.cfg);
261
347
  }
262
348
  const agentId = resolveDefaultStoreAgentId(params.cfg);
263
- return canonicalizeSessionKeyForAgent(agentId, raw);
349
+ return canonicalizeSessionKeyForAgent(agentId, lowered);
264
350
  }
265
351
  function resolveSessionStoreAgentId(cfg, canonicalKey) {
266
352
  if (canonicalKey === "global" || canonicalKey === "unknown") {
267
353
  return resolveDefaultStoreAgentId(cfg);
268
354
  }
269
355
  const parsed = parseAgentSessionKey(canonicalKey);
270
- if (parsed?.agentId)
356
+ if (parsed?.agentId) {
271
357
  return normalizeAgentId(parsed.agentId);
358
+ }
272
359
  return resolveDefaultStoreAgentId(cfg);
273
360
  }
274
- function canonicalizeSpawnedByForAgent(agentId, spawnedBy) {
361
+ export function canonicalizeSpawnedByForAgent(cfg, agentId, spawnedBy) {
275
362
  const raw = spawnedBy?.trim();
276
- if (!raw)
363
+ if (!raw) {
277
364
  return undefined;
278
- if (raw === "global" || raw === "unknown")
279
- return raw;
280
- if (raw.startsWith("agent:"))
281
- return raw;
282
- return `agent:${normalizeAgentId(agentId)}:${raw}`;
365
+ }
366
+ const lower = raw.toLowerCase();
367
+ if (lower === "global" || lower === "unknown") {
368
+ return lower;
369
+ }
370
+ let result;
371
+ if (raw.toLowerCase().startsWith("agent:")) {
372
+ result = raw.toLowerCase();
373
+ }
374
+ else {
375
+ result = `agent:${normalizeAgentId(agentId)}:${lower}`;
376
+ }
377
+ // Resolve main-alias references (e.g. agent:ops:main → configured main key).
378
+ const parsed = parseAgentSessionKey(result);
379
+ const resolvedAgent = parsed?.agentId ? normalizeAgentId(parsed.agentId) : agentId;
380
+ return canonicalizeMainSessionAlias({ cfg, agentId: resolvedAgent, sessionKey: result });
283
381
  }
284
382
  export function resolveGatewaySessionStoreTarget(params) {
285
383
  const key = params.key.trim();
@@ -296,8 +394,26 @@ export function resolveGatewaySessionStoreTarget(params) {
296
394
  }
297
395
  const storeKeys = new Set();
298
396
  storeKeys.add(canonicalKey);
299
- if (key && key !== canonicalKey)
397
+ if (key && key !== canonicalKey) {
300
398
  storeKeys.add(key);
399
+ }
400
+ if (params.scanLegacyKeys !== false) {
401
+ // Build a set of scan targets: all known keys plus the main alias key so we
402
+ // catch legacy entries stored under "agent:{id}:MAIN" when mainKey != "main".
403
+ const scanTargets = new Set(storeKeys);
404
+ const agentMainKey = resolveAgentMainSessionKey({ cfg: params.cfg, agentId });
405
+ if (canonicalKey === agentMainKey) {
406
+ scanTargets.add(`agent:${agentId}:main`);
407
+ }
408
+ // Scan the on-disk store for case variants of every target to find
409
+ // legacy mixed-case entries (e.g. "agent:ops:MAIN" when canonical is "agent:ops:work").
410
+ const store = params.store ?? loadSessionStore(storePath);
411
+ for (const seed of scanTargets) {
412
+ for (const legacyKey of findStoreKeysIgnoreCase(store, seed)) {
413
+ storeKeys.add(legacyKey);
414
+ }
415
+ }
416
+ }
301
417
  return {
302
418
  agentId,
303
419
  storePath,
@@ -307,20 +423,20 @@ export function resolveGatewaySessionStoreTarget(params) {
307
423
  }
308
424
  // Merge with existing entry based on latest timestamp to ensure data consistency and avoid overwriting with less complete data.
309
425
  function mergeSessionEntryIntoCombined(params) {
310
- const { combined, entry, agentId, canonicalKey } = params;
426
+ const { cfg, combined, entry, agentId, canonicalKey } = params;
311
427
  const existing = combined[canonicalKey];
312
428
  if (existing && (existing.updatedAt ?? 0) > (entry.updatedAt ?? 0)) {
313
429
  combined[canonicalKey] = {
314
430
  ...entry,
315
431
  ...existing,
316
- spawnedBy: canonicalizeSpawnedByForAgent(agentId, existing.spawnedBy ?? entry.spawnedBy),
432
+ spawnedBy: canonicalizeSpawnedByForAgent(cfg, agentId, existing.spawnedBy ?? entry.spawnedBy),
317
433
  };
318
434
  }
319
435
  else {
320
436
  combined[canonicalKey] = {
321
437
  ...existing,
322
438
  ...entry,
323
- spawnedBy: canonicalizeSpawnedByForAgent(agentId, entry.spawnedBy ?? existing?.spawnedBy),
439
+ spawnedBy: canonicalizeSpawnedByForAgent(cfg, agentId, entry.spawnedBy ?? existing?.spawnedBy),
324
440
  };
325
441
  }
326
442
  }
@@ -334,6 +450,7 @@ export function loadCombinedSessionStoreForGateway(cfg) {
334
450
  for (const [key, entry] of Object.entries(store)) {
335
451
  const canonicalKey = canonicalizeSessionKeyForAgent(defaultAgentId, key);
336
452
  mergeSessionEntryIntoCombined({
453
+ cfg,
337
454
  combined,
338
455
  entry,
339
456
  agentId: defaultAgentId,
@@ -350,6 +467,7 @@ export function loadCombinedSessionStoreForGateway(cfg) {
350
467
  for (const [key, entry] of Object.entries(store)) {
351
468
  const canonicalKey = canonicalizeSessionKeyForAgent(agentId, key);
352
469
  mergeSessionEntryIntoCombined({
470
+ cfg,
353
471
  combined,
354
472
  entry,
355
473
  agentId,
@@ -383,12 +501,38 @@ export function resolveSessionModelRef(cfg, entry, agentId) {
383
501
  defaultProvider: DEFAULT_PROVIDER,
384
502
  defaultModel: DEFAULT_MODEL,
385
503
  });
504
+ // Prefer the last runtime model recorded on the session entry.
505
+ // This is the actual model used by the latest run and must win over defaults.
386
506
  let provider = resolved.provider;
387
507
  let model = resolved.model;
508
+ const runtimeModel = entry?.model?.trim();
509
+ const runtimeProvider = entry?.modelProvider?.trim();
510
+ if (runtimeModel) {
511
+ const parsedRuntime = parseModelRef(runtimeModel, runtimeProvider || provider || DEFAULT_PROVIDER);
512
+ if (parsedRuntime) {
513
+ provider = parsedRuntime.provider;
514
+ model = parsedRuntime.model;
515
+ }
516
+ else {
517
+ provider = runtimeProvider || provider;
518
+ model = runtimeModel;
519
+ }
520
+ return { provider, model };
521
+ }
522
+ // Fall back to explicit per-session override (set at spawn/model-patch time),
523
+ // then finally to configured defaults.
388
524
  const storedModelOverride = entry?.modelOverride?.trim();
389
525
  if (storedModelOverride) {
390
- provider = entry?.providerOverride?.trim() || provider;
391
- model = storedModelOverride;
526
+ const overrideProvider = entry?.providerOverride?.trim() || provider || DEFAULT_PROVIDER;
527
+ const parsedOverride = parseModelRef(storedModelOverride, overrideProvider);
528
+ if (parsedOverride) {
529
+ provider = parsedOverride.provider;
530
+ model = parsedOverride.model;
531
+ }
532
+ else {
533
+ provider = overrideProvider;
534
+ model = storedModelOverride;
535
+ }
392
536
  }
393
537
  return { provider, model };
394
538
  }
@@ -408,39 +552,46 @@ export function listSessionsFromStore(params) {
408
552
  : undefined;
409
553
  let sessions = Object.entries(store)
410
554
  .filter(([key]) => {
411
- if (isCronRunSessionKey(key))
555
+ if (isCronRunSessionKey(key)) {
412
556
  return false;
413
- if (!includeGlobal && key === "global")
557
+ }
558
+ if (!includeGlobal && key === "global") {
414
559
  return false;
415
- if (!includeUnknown && key === "unknown")
560
+ }
561
+ if (!includeUnknown && key === "unknown") {
416
562
  return false;
563
+ }
417
564
  if (agentId) {
418
- if (key === "global" || key === "unknown")
565
+ if (key === "global" || key === "unknown") {
419
566
  return false;
567
+ }
420
568
  const parsed = parseAgentSessionKey(key);
421
- if (!parsed)
569
+ if (!parsed) {
422
570
  return false;
571
+ }
423
572
  return normalizeAgentId(parsed.agentId) === agentId;
424
573
  }
425
574
  return true;
426
575
  })
427
576
  .filter(([key, entry]) => {
428
- if (!spawnedBy)
577
+ if (!spawnedBy) {
429
578
  return true;
430
- if (key === "unknown" || key === "global")
579
+ }
580
+ if (key === "unknown" || key === "global") {
431
581
  return false;
582
+ }
432
583
  return entry?.spawnedBy === spawnedBy;
433
584
  })
434
585
  .filter(([, entry]) => {
435
- if (!label)
586
+ if (!label) {
436
587
  return true;
588
+ }
437
589
  return entry?.label === label;
438
590
  })
439
591
  .map(([key, entry]) => {
440
592
  const updatedAt = entry?.updatedAt ?? null;
441
- const input = entry?.inputTokens ?? 0;
442
- const output = entry?.outputTokens ?? 0;
443
- const total = entry?.totalTokens ?? input + output;
593
+ const total = resolveFreshSessionTotalTokens(entry);
594
+ const totalTokensFresh = typeof entry?.totalTokens === "number" ? entry?.totalTokensFresh !== false : false;
444
595
  const parsed = parseGroupKey(key);
445
596
  const channel = entry?.channel ?? parsed?.channel;
446
597
  const subject = entry?.subject;
@@ -492,6 +643,7 @@ export function listSessionsFromStore(params) {
492
643
  inputTokens: entry?.inputTokens,
493
644
  outputTokens: entry?.outputTokens,
494
645
  totalTokens: total,
646
+ totalTokensFresh,
495
647
  responseUsage: entry?.responseUsage,
496
648
  modelProvider,
497
649
  model,
@@ -522,14 +674,16 @@ export function listSessionsFromStore(params) {
522
674
  let derivedTitle;
523
675
  let lastMessagePreview;
524
676
  if (entry?.sessionId) {
525
- if (includeDerivedTitles) {
526
- const firstUserMsg = readFirstUserMessageFromTranscript(entry.sessionId, storePath, entry.sessionFile);
527
- derivedTitle = deriveSessionTitle(entry, firstUserMsg);
528
- }
529
- if (includeLastMessage) {
530
- const lastMsg = readLastMessagePreviewFromTranscript(entry.sessionId, storePath, entry.sessionFile);
531
- if (lastMsg)
532
- lastMessagePreview = lastMsg;
677
+ if (includeDerivedTitles || includeLastMessage) {
678
+ const parsed = parseAgentSessionKey(s.key);
679
+ const agentId = parsed && parsed.agentId ? normalizeAgentId(parsed.agentId) : resolveDefaultAgentId(cfg);
680
+ const fields = readSessionTitleFieldsFromTranscript(entry.sessionId, storePath, entry.sessionFile, agentId);
681
+ if (includeDerivedTitles) {
682
+ derivedTitle = deriveSessionTitle(entry, fields.firstUserMessage);
683
+ }
684
+ if (includeLastMessage && fields.lastMessagePreview) {
685
+ lastMessagePreview = fields.lastMessagePreview;
686
+ }
533
687
  }
534
688
  }
535
689
  return { ...rest, derivedTitle, lastMessagePreview };