@poolzin/pool-bot 2026.1.39 → 2026.2.1

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 (511) hide show
  1. package/assets/chrome-extension/README.md +3 -3
  2. package/assets/chrome-extension/background.js +5 -5
  3. package/assets/chrome-extension/manifest.json +3 -3
  4. package/assets/chrome-extension/options.html +4 -4
  5. package/assets/chrome-extension/options.js +1 -1
  6. package/dist/acp/client.js +3 -3
  7. package/dist/acp/types.js +1 -1
  8. package/dist/agents/agent-paths.js +3 -3
  9. package/dist/agents/auth-profiles/paths.js +3 -3
  10. package/dist/agents/bash-tools.exec.js +76 -25
  11. package/dist/agents/cli-runner/helpers.js +10 -12
  12. package/dist/agents/cli-runner.js +2 -2
  13. package/dist/agents/cloudflare-ai-gateway.js +31 -0
  14. package/dist/agents/compaction.js +16 -2
  15. package/dist/agents/context-window-guard.js +13 -10
  16. package/dist/agents/context.js +4 -4
  17. package/dist/agents/docs-path.js +1 -1
  18. package/dist/agents/identity.js +47 -7
  19. package/dist/agents/memory-search.js +25 -8
  20. package/dist/agents/minimax-vlm.js +1 -1
  21. package/dist/agents/model-auth.js +12 -1
  22. package/dist/agents/model-catalog.js +4 -4
  23. package/dist/agents/model-selection.js +31 -4
  24. package/dist/agents/models-config.js +3 -3
  25. package/dist/agents/models-config.providers.js +147 -39
  26. package/dist/agents/pi-embedded-block-chunker.js +117 -42
  27. package/dist/agents/pi-embedded-helpers/errors.js +183 -78
  28. package/dist/agents/pi-embedded-helpers/openai.js +1 -1
  29. package/dist/agents/pi-embedded-helpers.js +1 -1
  30. package/dist/agents/pi-embedded-runner/compact.js +9 -8
  31. package/dist/agents/pi-embedded-runner/model.js +63 -4
  32. package/dist/agents/pi-embedded-runner/run/attempt.js +27 -17
  33. package/dist/agents/pi-embedded-runner/run.js +203 -50
  34. package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
  35. package/dist/agents/pi-embedded-runner/tool-result-truncation.js +275 -0
  36. package/dist/agents/pi-embedded-runner/utils.js +1 -1
  37. package/dist/agents/pi-embedded-subscribe.js +118 -29
  38. package/dist/agents/pi-model-discovery.js +10 -0
  39. package/dist/agents/pi-tool-definition-adapter.js +50 -9
  40. package/dist/agents/pi-tools.before-tool-call.js +67 -0
  41. package/dist/agents/pi-tools.js +20 -10
  42. package/dist/agents/pi-tools.read.js +2 -2
  43. package/dist/agents/poolbot-tools.js +15 -10
  44. package/dist/agents/sandbox-paths.js +31 -0
  45. package/dist/agents/session-file-repair.js +83 -0
  46. package/dist/agents/session-tool-result-guard.js +94 -15
  47. package/dist/agents/session-transcript-repair.js +68 -0
  48. package/dist/agents/shell-utils.js +51 -0
  49. package/dist/agents/skills/bundled-context.js +23 -0
  50. package/dist/agents/skills/bundled-dir.js +41 -7
  51. package/dist/agents/skills/frontmatter.js +1 -1
  52. package/dist/agents/skills/workspace.js +2 -2
  53. package/dist/agents/skills-install.js +60 -23
  54. package/dist/agents/subagent-announce.js +79 -34
  55. package/dist/agents/system-prompt.js +28 -4
  56. package/dist/agents/together-models.js +127 -0
  57. package/dist/agents/tool-images.js +1 -1
  58. package/dist/agents/tool-policy.conformance.js +14 -0
  59. package/dist/agents/tool-policy.js +25 -1
  60. package/dist/agents/tools/browser-tool.js +3 -3
  61. package/dist/agents/tools/cron-tool.js +166 -19
  62. package/dist/agents/tools/discord-actions-presence.js +78 -0
  63. package/dist/agents/tools/image-tool.js +2 -2
  64. package/dist/agents/tools/memory-tool.js +93 -5
  65. package/dist/agents/tools/message-tool.js +56 -2
  66. package/dist/agents/tools/sessions-history-tool.js +69 -1
  67. package/dist/agents/tools/web-search.js +211 -42
  68. package/dist/agents/usage.js +23 -1
  69. package/dist/agents/workspace-run.js +67 -0
  70. package/dist/agents/workspace-templates.js +44 -0
  71. package/dist/auto-reply/command-auth.js +121 -6
  72. package/dist/auto-reply/commands-registry.data.js +1 -1
  73. package/dist/auto-reply/envelope.js +50 -72
  74. package/dist/auto-reply/reply/commands-compact.js +1 -0
  75. package/dist/auto-reply/reply/commands-context-report.js +3 -2
  76. package/dist/auto-reply/reply/commands-context.js +1 -0
  77. package/dist/auto-reply/reply/commands-models.js +107 -60
  78. package/dist/auto-reply/reply/commands-ptt.js +171 -0
  79. package/dist/auto-reply/reply/commands-session.js +2 -2
  80. package/dist/auto-reply/reply/get-reply-run.js +16 -5
  81. package/dist/auto-reply/reply/groups.js +1 -1
  82. package/dist/auto-reply/reply/inbound-context.js +9 -1
  83. package/dist/auto-reply/reply/inbound-meta.js +130 -0
  84. package/dist/auto-reply/reply/model-selection.js +3 -3
  85. package/dist/auto-reply/reply/untrusted-context.js +15 -0
  86. package/dist/auto-reply/status.js +1 -1
  87. package/dist/auto-reply/thinking.js +88 -43
  88. package/dist/browser/bridge-server.js +13 -0
  89. package/dist/browser/cdp.helpers.js +38 -24
  90. package/dist/browser/client-fetch.js +51 -8
  91. package/dist/browser/config.js +2 -11
  92. package/dist/browser/extension-relay.js +104 -43
  93. package/dist/browser/pw-ai.js +1 -1
  94. package/dist/browser/pw-session.js +143 -8
  95. package/dist/browser/pw-tools-core.interactions.js +125 -27
  96. package/dist/browser/pw-tools-core.responses.js +1 -1
  97. package/dist/browser/pw-tools-core.state.js +1 -1
  98. package/dist/browser/routes/agent.act.js +86 -41
  99. package/dist/browser/routes/dispatcher.js +4 -4
  100. package/dist/browser/screenshot.js +1 -1
  101. package/dist/browser/server-context.js +2 -2
  102. package/dist/browser/server.js +13 -0
  103. package/dist/build-info.json +3 -3
  104. package/dist/canvas-host/a2ui.js +3 -3
  105. package/dist/channels/plugins/catalog.js +2 -2
  106. package/dist/channels/plugins/onboarding/imessage.js +1 -1
  107. package/dist/channels/plugins/onboarding/signal.js +1 -1
  108. package/dist/channels/plugins/onboarding/slack.js +4 -4
  109. package/dist/channels/plugins/onboarding/whatsapp.js +3 -3
  110. package/dist/channels/plugins/pairing-message.js +1 -1
  111. package/dist/channels/reply-prefix.js +8 -1
  112. package/dist/cli/browser-cli-extension.js +2 -2
  113. package/dist/cli/cron-cli/register.cron-add.js +61 -40
  114. package/dist/cli/cron-cli/register.cron-edit.js +60 -34
  115. package/dist/cli/cron-cli/shared.js +56 -41
  116. package/dist/cli/dns-cli.js +26 -14
  117. package/dist/cli/docs-cli.js +1 -1
  118. package/dist/cli/gateway-cli/dev.js +1 -1
  119. package/dist/cli/gateway-cli/register.js +37 -19
  120. package/dist/cli/memory-cli.js +30 -20
  121. package/dist/cli/nodes-cli/register.canvas.js +1 -1
  122. package/dist/cli/parse-bytes.js +37 -0
  123. package/dist/cli/plugins-cli.js +1 -1
  124. package/dist/cli/run-main.js +2 -2
  125. package/dist/cli/security-cli.js +1 -1
  126. package/dist/cli/tagline.js +1 -1
  127. package/dist/cli/update-cli.js +173 -52
  128. package/dist/cli/webhooks-cli.js +5 -5
  129. package/dist/commands/agent.js +1 -0
  130. package/dist/commands/agents.commands.add.js +1 -1
  131. package/dist/commands/auth-choice.apply.api-providers.js +305 -17
  132. package/dist/commands/auth-choice.apply.js +4 -1
  133. package/dist/commands/auth-choice.apply.plugin-provider.js +2 -2
  134. package/dist/commands/auth-choice.apply.xai.js +63 -0
  135. package/dist/commands/auth-choice.preferred-provider.js +7 -1
  136. package/dist/commands/configure.wizard.js +1 -1
  137. package/dist/commands/dashboard.js +1 -1
  138. package/dist/commands/docs.js +1 -1
  139. package/dist/commands/doctor-config-flow.js +61 -5
  140. package/dist/commands/doctor-gateway-services.js +3 -3
  141. package/dist/commands/doctor-state-migrations.js +1 -1
  142. package/dist/commands/doctor-update.js +3 -3
  143. package/dist/commands/doctor.js +1 -1
  144. package/dist/commands/health.js +1 -1
  145. package/dist/commands/model-allowlist.js +29 -0
  146. package/dist/commands/model-picker.js +2 -1
  147. package/dist/commands/models/list.probe.js +2 -2
  148. package/dist/commands/models/list.registry.js +4 -4
  149. package/dist/commands/models/list.status-command.js +44 -24
  150. package/dist/commands/models/shared.js +15 -0
  151. package/dist/commands/onboard-auth.config-core.js +366 -28
  152. package/dist/commands/onboard-auth.credentials.js +71 -9
  153. package/dist/commands/onboard-auth.js +3 -3
  154. package/dist/commands/onboard-auth.models.js +26 -24
  155. package/dist/commands/onboard-custom.js +384 -0
  156. package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +35 -0
  157. package/dist/commands/onboard-non-interactive/local/auth-choice.js +146 -9
  158. package/dist/commands/onboard-skills.js +63 -38
  159. package/dist/commands/openai-model-default.js +41 -0
  160. package/dist/commands/status-all/report-lines.js +1 -1
  161. package/dist/commands/status.command.js +1 -1
  162. package/dist/commands/uninstall.js +3 -3
  163. package/dist/compat/legacy-names.js +1 -1
  164. package/dist/config/defaults.js +3 -2
  165. package/dist/config/io.js +3 -3
  166. package/dist/config/paths.js +136 -35
  167. package/dist/config/plugin-auto-enable.js +21 -5
  168. package/dist/config/redact-snapshot.js +153 -0
  169. package/dist/config/schema.field-metadata.js +590 -0
  170. package/dist/config/schema.js +3 -3
  171. package/dist/config/sessions/store.js +291 -23
  172. package/dist/config/types.memory.js +1 -0
  173. package/dist/config/version.js +4 -4
  174. package/dist/config/zod-schema.agent-defaults.js +3 -0
  175. package/dist/config/zod-schema.agent-runtime.js +13 -2
  176. package/dist/config/zod-schema.providers-core.js +142 -0
  177. package/dist/config/zod-schema.session.js +3 -0
  178. package/dist/cron/delivery.js +57 -0
  179. package/dist/cron/isolated-agent/delivery-target.js +18 -3
  180. package/dist/cron/isolated-agent/helpers.js +22 -5
  181. package/dist/cron/isolated-agent/run.js +171 -63
  182. package/dist/cron/isolated-agent/session.js +2 -0
  183. package/dist/cron/normalize.js +356 -28
  184. package/dist/cron/parse.js +10 -5
  185. package/dist/cron/run-log.js +35 -10
  186. package/dist/cron/schedule.js +41 -6
  187. package/dist/cron/service/jobs.js +208 -35
  188. package/dist/cron/service/ops.js +72 -16
  189. package/dist/cron/service/state.js +2 -0
  190. package/dist/cron/service/store.js +386 -14
  191. package/dist/cron/service/timer.js +390 -147
  192. package/dist/cron/session-reaper.js +86 -0
  193. package/dist/cron/store.js +23 -8
  194. package/dist/cron/validate-timestamp.js +43 -0
  195. package/dist/daemon/constants.js +7 -7
  196. package/dist/daemon/inspect.js +6 -6
  197. package/dist/daemon/systemd-unit.js +1 -1
  198. package/dist/discord/monitor/agent-components.js +438 -0
  199. package/dist/discord/monitor/allow-list.js +28 -5
  200. package/dist/discord/monitor/gateway-registry.js +29 -0
  201. package/dist/discord/monitor/native-command.js +44 -23
  202. package/dist/discord/monitor/sender-identity.js +45 -0
  203. package/dist/discord/pluralkit.js +27 -0
  204. package/dist/discord/send.outbound.js +92 -5
  205. package/dist/discord/send.shared.js +60 -23
  206. package/dist/discord/targets.js +84 -1
  207. package/dist/entry.js +15 -9
  208. package/dist/extensionAPI.js +8 -0
  209. package/dist/gateway/control-ui.js +8 -1
  210. package/dist/gateway/hooks-mapping.js +3 -0
  211. package/dist/gateway/hooks.js +65 -0
  212. package/dist/gateway/live-image-probe.js +1 -66
  213. package/dist/gateway/net.js +96 -31
  214. package/dist/gateway/node-command-policy.js +50 -15
  215. package/dist/gateway/openai-http.js +2 -2
  216. package/dist/gateway/openresponses-http.js +4 -4
  217. package/dist/gateway/origin-check.js +56 -0
  218. package/dist/gateway/protocol/client-info.js +9 -0
  219. package/dist/gateway/protocol/index.js +9 -2
  220. package/dist/gateway/protocol/schema/agents-models-skills.js +71 -1
  221. package/dist/gateway/protocol/schema/cron.js +22 -10
  222. package/dist/gateway/protocol/schema/protocol-schemas.js +16 -2
  223. package/dist/gateway/protocol/schema/sessions.js +12 -0
  224. package/dist/gateway/server/hooks.js +1 -1
  225. package/dist/gateway/server-broadcast.js +26 -9
  226. package/dist/gateway/server-chat.js +112 -23
  227. package/dist/gateway/server-discovery-runtime.js +10 -2
  228. package/dist/gateway/server-discovery.js +2 -2
  229. package/dist/gateway/server-http.js +110 -12
  230. package/dist/gateway/server-methods/agent-timestamp.js +60 -0
  231. package/dist/gateway/server-methods/agents.js +321 -2
  232. package/dist/gateway/server-methods/usage.js +559 -16
  233. package/dist/gateway/server-runtime-state.js +22 -8
  234. package/dist/gateway/server-startup-memory.js +16 -0
  235. package/dist/gateway/server.impl.js +7 -3
  236. package/dist/gateway/session-utils.fs.js +23 -25
  237. package/dist/gateway/session-utils.js +20 -10
  238. package/dist/gateway/sessions-patch.js +7 -22
  239. package/dist/gateway/test-helpers.server.js +35 -2
  240. package/dist/hooks/frontmatter.js +1 -1
  241. package/dist/hooks/hooks-status.js +1 -1
  242. package/dist/hooks/install.js +2 -2
  243. package/dist/hooks/loader.js +1 -1
  244. package/dist/hooks/workspace.js +3 -3
  245. package/dist/imessage/constants.js +2 -0
  246. package/dist/imessage/monitor/deliver.js +4 -1
  247. package/dist/imessage/monitor/monitor-provider.js +51 -1
  248. package/dist/index.js +2 -2
  249. package/dist/infra/bonjour-discovery.js +131 -70
  250. package/dist/infra/bonjour.js +3 -3
  251. package/dist/infra/control-ui-assets.js +134 -12
  252. package/dist/infra/errors.js +12 -0
  253. package/dist/infra/exec-approvals.js +266 -57
  254. package/dist/infra/format-time/format-datetime.js +79 -0
  255. package/dist/infra/format-time/format-duration.js +81 -0
  256. package/dist/infra/format-time/format-relative.js +80 -0
  257. package/dist/infra/heartbeat-runner.js +140 -49
  258. package/dist/infra/home-dir.js +54 -0
  259. package/dist/infra/net/fetch-guard.js +122 -0
  260. package/dist/infra/net/ssrf.js +65 -29
  261. package/dist/infra/outbound/abort.js +14 -0
  262. package/dist/infra/outbound/message-action-runner.js +77 -13
  263. package/dist/infra/outbound/outbound-session.js +143 -37
  264. package/dist/infra/path-env.js +3 -3
  265. package/dist/infra/poolbot-root.js +43 -1
  266. package/dist/infra/provider-usage.fetch.minimax.js +1 -1
  267. package/dist/infra/restart.js +1 -1
  268. package/dist/infra/session-cost-usage.js +631 -41
  269. package/dist/infra/state-migrations.js +317 -47
  270. package/dist/infra/tailscale.js +1 -1
  271. package/dist/infra/update-global.js +35 -0
  272. package/dist/infra/update-runner.js +149 -43
  273. package/dist/infra/warning-filter.js +65 -0
  274. package/dist/infra/widearea-dns.js +30 -9
  275. package/dist/logging/redact-identifier.js +12 -0
  276. package/dist/macos/relay.js +2 -2
  277. package/dist/media/fetch.js +81 -58
  278. package/dist/media/input-files.js +1 -1
  279. package/dist/media/mime.js +4 -0
  280. package/dist/media/png-encode.js +74 -0
  281. package/dist/media-understanding/apply.js +403 -3
  282. package/dist/media-understanding/attachments.js +38 -27
  283. package/dist/media-understanding/defaults.js +16 -0
  284. package/dist/media-understanding/providers/deepgram/audio.js +22 -14
  285. package/dist/media-understanding/providers/google/audio.js +24 -17
  286. package/dist/media-understanding/providers/google/video.js +24 -17
  287. package/dist/media-understanding/providers/image.js +4 -4
  288. package/dist/media-understanding/providers/index.js +4 -1
  289. package/dist/media-understanding/providers/openai/audio.js +22 -14
  290. package/dist/media-understanding/providers/shared.js +16 -11
  291. package/dist/media-understanding/providers/zai/index.js +6 -0
  292. package/dist/media-understanding/runner.js +158 -90
  293. package/dist/memory/backend-config.js +207 -0
  294. package/dist/memory/batch-voyage.js +277 -0
  295. package/dist/memory/embeddings-voyage.js +75 -0
  296. package/dist/memory/embeddings.js +29 -17
  297. package/dist/memory/internal.js +101 -18
  298. package/dist/memory/manager.js +155 -48
  299. package/dist/memory/search-manager.js +173 -0
  300. package/dist/memory/session-files.js +9 -3
  301. package/dist/memory/types.js +1 -0
  302. package/dist/node-host/runner.js +36 -26
  303. package/dist/node-host/with-timeout.js +27 -0
  304. package/dist/pairing/pairing-messages.js +1 -1
  305. package/dist/plugins/commands.js +5 -1
  306. package/dist/plugins/config-state.js +86 -7
  307. package/dist/plugins/discovery.js +1 -1
  308. package/dist/plugins/install.js +2 -2
  309. package/dist/plugins/source-display.js +51 -0
  310. package/dist/plugins/update.js +1 -1
  311. package/dist/process/exec.js +20 -2
  312. package/dist/routing/resolve-route.js +12 -0
  313. package/dist/routing/session-key.js +15 -0
  314. package/dist/runtime.js +2 -0
  315. package/dist/security/audit-extra.async.js +601 -0
  316. package/dist/security/audit-extra.js +2 -830
  317. package/dist/security/audit-extra.sync.js +505 -0
  318. package/dist/security/audit.js +2 -2
  319. package/dist/security/channel-metadata.js +34 -0
  320. package/dist/security/external-content.js +88 -6
  321. package/dist/security/skill-scanner.js +330 -0
  322. package/dist/sessions/session-key-utils.js +7 -0
  323. package/dist/shared/text/reasoning-tags.js +52 -7
  324. package/dist/signal/monitor/event-handler.js +80 -1
  325. package/dist/slack/monitor/media.js +85 -15
  326. package/dist/tailscale/detect.js +145 -0
  327. package/dist/telegram/bot/helpers.js +109 -28
  328. package/dist/telegram/bot-handlers.js +144 -3
  329. package/dist/telegram/bot-message-context.js +38 -11
  330. package/dist/telegram/bot-message-dispatch.js +48 -15
  331. package/dist/telegram/bot-native-commands.js +86 -29
  332. package/dist/telegram/bot.js +30 -29
  333. package/dist/telegram/model-buttons.js +163 -0
  334. package/dist/telegram/monitor.js +110 -85
  335. package/dist/telegram/send.js +129 -47
  336. package/dist/terminal/restore.js +45 -0
  337. package/dist/test-helpers/state-dir-env.js +16 -0
  338. package/dist/test-helpers/workspace.js +11 -0
  339. package/dist/test-utils/channel-plugins.js +82 -0
  340. package/dist/test-utils/ports.js +73 -0
  341. package/dist/tts/tts.js +12 -6
  342. package/dist/tui/tui-session-actions.js +166 -54
  343. package/dist/utils/fetch-timeout.js +20 -0
  344. package/dist/utils/normalize-secret-input.js +19 -0
  345. package/dist/utils/shell-argv.js +61 -0
  346. package/dist/utils/transcript-tools.js +58 -0
  347. package/dist/utils.js +55 -14
  348. package/dist/version.js +42 -5
  349. package/dist/web/qr-image.js +1 -61
  350. package/dist/wizard/onboarding.finalize.js +7 -7
  351. package/dist/wizard/onboarding.js +3 -3
  352. package/docs/RELEASE_WORKFOTS_COMPARISON.md +3 -3
  353. package/docs/_config.yml +2 -2
  354. package/docs/_layouts/default.html +9 -9
  355. package/docs/concepts/typebox.md +1 -1
  356. package/docs/docs.json +1 -1
  357. package/docs/northflank.mdx +7 -7
  358. package/docs/railway.mdx +3 -3
  359. package/docs/render.mdx +5 -5
  360. package/docs/start/lore.md +2 -2
  361. package/extensions/bluebubbles/index.ts +2 -2
  362. package/extensions/bluebubbles/package.json +1 -1
  363. package/extensions/bluebubbles/src/accounts.ts +8 -8
  364. package/extensions/bluebubbles/src/actions.test.ts +22 -22
  365. package/extensions/bluebubbles/src/actions.ts +5 -5
  366. package/extensions/bluebubbles/src/attachments.ts +2 -2
  367. package/extensions/bluebubbles/src/channel.ts +16 -16
  368. package/extensions/bluebubbles/src/chat.ts +2 -2
  369. package/extensions/bluebubbles/src/media-send.ts +2 -2
  370. package/extensions/bluebubbles/src/monitor.test.ts +46 -46
  371. package/extensions/bluebubbles/src/monitor.ts +5 -5
  372. package/extensions/bluebubbles/src/onboarding.ts +7 -7
  373. package/extensions/bluebubbles/src/reactions.ts +2 -2
  374. package/extensions/bluebubbles/src/send.ts +2 -2
  375. package/extensions/copilot-proxy/README.md +1 -1
  376. package/extensions/copilot-proxy/package.json +1 -1
  377. package/extensions/diagnostics-otel/index.ts +2 -2
  378. package/extensions/diagnostics-otel/package.json +1 -1
  379. package/extensions/diagnostics-otel/src/service.ts +3 -3
  380. package/extensions/discord/index.ts +2 -2
  381. package/extensions/discord/package.json +1 -1
  382. package/extensions/google-antigravity-auth/README.md +1 -1
  383. package/extensions/google-antigravity-auth/index.ts +1 -1
  384. package/extensions/google-antigravity-auth/package.json +1 -1
  385. package/extensions/google-gemini-cli-auth/README.md +1 -1
  386. package/extensions/google-gemini-cli-auth/oauth.ts +1 -1
  387. package/extensions/google-gemini-cli-auth/package.json +1 -1
  388. package/extensions/googlechat/index.ts +3 -3
  389. package/extensions/googlechat/package.json +1 -1
  390. package/extensions/googlechat/src/accounts.ts +8 -8
  391. package/extensions/googlechat/src/actions.ts +6 -6
  392. package/extensions/googlechat/src/channel.ts +21 -21
  393. package/extensions/googlechat/src/monitor.ts +8 -8
  394. package/extensions/googlechat/src/onboarding.ts +10 -10
  395. package/extensions/imessage/index.ts +2 -2
  396. package/extensions/imessage/package.json +1 -1
  397. package/extensions/line/index.ts +2 -2
  398. package/extensions/line/package.json +1 -1
  399. package/extensions/line/src/card-command.ts +2 -2
  400. package/extensions/line/src/channel.logout.test.ts +4 -4
  401. package/extensions/line/src/channel.sendPayload.test.ts +8 -8
  402. package/extensions/line/src/channel.ts +3 -3
  403. package/extensions/llm-task/README.md +3 -3
  404. package/extensions/llm-task/index.ts +2 -2
  405. package/extensions/llm-task/package.json +1 -1
  406. package/extensions/llm-task/src/llm-task-tool.ts +4 -4
  407. package/extensions/lobster/README.md +6 -6
  408. package/extensions/lobster/index.ts +2 -2
  409. package/extensions/lobster/src/lobster-tool.test.ts +4 -4
  410. package/extensions/lobster/src/lobster-tool.ts +2 -2
  411. package/extensions/matrix/index.ts +2 -2
  412. package/extensions/matrix/package.json +1 -1
  413. package/extensions/matrix/src/matrix/client/config.ts +1 -1
  414. package/extensions/matrix/src/matrix/monitor/handler.ts +1 -1
  415. package/extensions/matrix/src/onboarding.ts +1 -1
  416. package/extensions/mattermost/index.ts +2 -2
  417. package/extensions/mattermost/package.json +1 -1
  418. package/extensions/mattermost/src/mattermost/accounts.ts +8 -8
  419. package/extensions/mattermost/src/mattermost/monitor-helpers.ts +5 -5
  420. package/extensions/mattermost/src/mattermost/monitor.ts +2 -2
  421. package/extensions/mattermost/src/onboarding-helpers.ts +3 -3
  422. package/extensions/mattermost/src/onboarding.ts +2 -2
  423. package/extensions/memory-core/index.ts +2 -2
  424. package/extensions/memory-core/package.json +1 -1
  425. package/extensions/memory-lancedb/index.ts +3 -3
  426. package/extensions/memory-lancedb/package.json +1 -1
  427. package/extensions/msteams/index.ts +2 -2
  428. package/extensions/msteams/package.json +1 -1
  429. package/extensions/msteams/src/channel.directory.test.ts +2 -2
  430. package/extensions/msteams/src/channel.ts +2 -2
  431. package/extensions/msteams/src/graph-upload.ts +4 -4
  432. package/extensions/msteams/src/monitor-handler.ts +2 -2
  433. package/extensions/msteams/src/monitor.ts +2 -2
  434. package/extensions/msteams/src/onboarding.ts +9 -9
  435. package/extensions/msteams/src/reply-dispatcher.ts +2 -2
  436. package/extensions/msteams/src/send-context.ts +2 -2
  437. package/extensions/msteams/src/send.ts +4 -4
  438. package/extensions/nextcloud-talk/index.ts +2 -2
  439. package/extensions/nextcloud-talk/package.json +1 -1
  440. package/extensions/nextcloud-talk/src/channel.ts +7 -7
  441. package/extensions/nextcloud-talk/src/inbound.ts +7 -7
  442. package/extensions/nextcloud-talk/src/onboarding.ts +1 -1
  443. package/extensions/nostr/README.md +2 -2
  444. package/extensions/nostr/index.ts +5 -5
  445. package/extensions/nostr/package.json +1 -1
  446. package/extensions/nostr/src/types.ts +4 -4
  447. package/extensions/open-prose/index.ts +2 -2
  448. package/extensions/qwen-portal-auth/README.md +1 -1
  449. package/extensions/signal/index.ts +2 -2
  450. package/extensions/signal/package.json +1 -1
  451. package/extensions/slack/index.ts +2 -2
  452. package/extensions/slack/package.json +1 -1
  453. package/extensions/telegram/index.ts +2 -2
  454. package/extensions/telegram/package.json +1 -1
  455. package/extensions/telegram/src/channel.ts +2 -2
  456. package/extensions/tlon/README.md +2 -2
  457. package/extensions/tlon/index.ts +2 -2
  458. package/extensions/tlon/package.json +1 -1
  459. package/extensions/tlon/src/channel.ts +13 -13
  460. package/extensions/tlon/src/monitor/index.ts +3 -3
  461. package/extensions/tlon/src/onboarding.ts +3 -3
  462. package/extensions/tlon/src/types.ts +3 -3
  463. package/extensions/twitch/README.md +1 -1
  464. package/extensions/twitch/index.ts +2 -2
  465. package/extensions/twitch/package.json +1 -1
  466. package/extensions/twitch/src/config.ts +3 -3
  467. package/extensions/twitch/src/monitor.ts +3 -3
  468. package/extensions/twitch/src/onboarding.ts +9 -9
  469. package/extensions/twitch/src/outbound.test.ts +2 -2
  470. package/extensions/twitch/src/plugin.test.ts +2 -2
  471. package/extensions/twitch/src/plugin.ts +8 -8
  472. package/extensions/twitch/src/send.test.ts +2 -2
  473. package/extensions/twitch/src/send.ts +4 -4
  474. package/extensions/twitch/src/token.test.ts +8 -8
  475. package/extensions/twitch/src/token.ts +3 -3
  476. package/extensions/twitch/src/twitch-client.ts +3 -3
  477. package/extensions/twitch/src/types.ts +3 -3
  478. package/extensions/twitch/src/utils/markdown.ts +1 -1
  479. package/extensions/voice-call/README.md +3 -3
  480. package/extensions/voice-call/package.json +1 -1
  481. package/extensions/voice-call/src/core-bridge.ts +2 -2
  482. package/extensions/voice-call/src/response-generator.ts +1 -1
  483. package/extensions/whatsapp/index.ts +2 -2
  484. package/extensions/whatsapp/package.json +1 -1
  485. package/extensions/zalo/README.md +1 -1
  486. package/extensions/zalo/index.ts +2 -2
  487. package/extensions/zalo/package.json +1 -1
  488. package/extensions/zalo/src/accounts.ts +8 -8
  489. package/extensions/zalo/src/actions.ts +4 -4
  490. package/extensions/zalo/src/channel.directory.test.ts +2 -2
  491. package/extensions/zalo/src/channel.ts +18 -18
  492. package/extensions/zalo/src/monitor.ts +9 -9
  493. package/extensions/zalo/src/monitor.webhook.test.ts +2 -2
  494. package/extensions/zalo/src/onboarding.ts +24 -24
  495. package/extensions/zalo/src/send.ts +2 -2
  496. package/extensions/zalouser/README.md +2 -2
  497. package/extensions/zalouser/index.ts +2 -2
  498. package/extensions/zalouser/package.json +1 -1
  499. package/extensions/zalouser/src/accounts.ts +9 -9
  500. package/extensions/zalouser/src/channel.ts +24 -24
  501. package/extensions/zalouser/src/monitor.ts +4 -4
  502. package/extensions/zalouser/src/onboarding.ts +28 -28
  503. package/package.json +13 -251
  504. package/skills/nano-banana-pro/scripts/generate_image.py +1 -1
  505. package/skills/tmux/scripts/find-sessions.sh +1 -1
  506. package/CHANGELOG.md +0 -102
  507. package/README-header.png +0 -0
  508. package/git-hooks/pre-commit +0 -4
  509. package/scripts/format-staged.js +0 -148
  510. package/scripts/postinstall.js +0 -300
  511. package/scripts/setup-git-hooks.js +0 -96
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import readline from "node:readline";
4
4
  import { normalizeUsage } from "../agents/usage.js";
5
5
  import { resolveSessionFilePath, resolveSessionTranscriptsDirForAgent, } from "../config/sessions/paths.js";
6
+ import { countToolResults, extractToolCallNames } from "../utils/transcript-tools.js";
6
7
  import { estimateUsageCost, resolveModelCostConfig } from "../utils/usage-format.js";
7
8
  const emptyTotals = () => ({
8
9
  input: 0,
@@ -11,65 +12,111 @@ const emptyTotals = () => ({
11
12
  cacheWrite: 0,
12
13
  totalTokens: 0,
13
14
  totalCost: 0,
15
+ inputCost: 0,
16
+ outputCost: 0,
17
+ cacheReadCost: 0,
18
+ cacheWriteCost: 0,
14
19
  missingCostEntries: 0,
15
20
  });
16
21
  const toFiniteNumber = (value) => {
17
- if (typeof value !== "number")
22
+ if (typeof value !== "number") {
18
23
  return undefined;
19
- if (!Number.isFinite(value))
24
+ }
25
+ if (!Number.isFinite(value)) {
20
26
  return undefined;
27
+ }
21
28
  return value;
22
29
  };
23
- const extractCostTotal = (usageRaw) => {
24
- if (!usageRaw || typeof usageRaw !== "object")
30
+ const extractCostBreakdown = (usageRaw) => {
31
+ if (!usageRaw || typeof usageRaw !== "object") {
25
32
  return undefined;
33
+ }
26
34
  const record = usageRaw;
27
35
  const cost = record.cost;
28
- const total = toFiniteNumber(cost?.total);
29
- if (total === undefined)
36
+ if (!cost) {
30
37
  return undefined;
31
- if (total < 0)
38
+ }
39
+ const total = toFiniteNumber(cost.total);
40
+ if (total === undefined || total < 0) {
32
41
  return undefined;
33
- return total;
42
+ }
43
+ return {
44
+ total,
45
+ input: toFiniteNumber(cost.input),
46
+ output: toFiniteNumber(cost.output),
47
+ cacheRead: toFiniteNumber(cost.cacheRead),
48
+ cacheWrite: toFiniteNumber(cost.cacheWrite),
49
+ };
34
50
  };
35
51
  const parseTimestamp = (entry) => {
36
52
  const raw = entry.timestamp;
37
53
  if (typeof raw === "string") {
38
54
  const parsed = new Date(raw);
39
- if (!Number.isNaN(parsed.valueOf()))
55
+ if (!Number.isNaN(parsed.valueOf())) {
40
56
  return parsed;
57
+ }
41
58
  }
42
59
  const message = entry.message;
43
60
  const messageTimestamp = toFiniteNumber(message?.timestamp);
44
61
  if (messageTimestamp !== undefined) {
45
62
  const parsed = new Date(messageTimestamp);
46
- if (!Number.isNaN(parsed.valueOf()))
63
+ if (!Number.isNaN(parsed.valueOf())) {
47
64
  return parsed;
65
+ }
48
66
  }
49
67
  return undefined;
50
68
  };
51
- const parseUsageEntry = (entry) => {
69
+ const parseTranscriptEntry = (entry) => {
52
70
  const message = entry.message;
53
- const role = message?.role;
54
- if (role !== "assistant")
71
+ if (!message || typeof message !== "object") {
55
72
  return null;
56
- const usageRaw = message?.usage ?? entry.usage;
57
- const usage = normalizeUsage(usageRaw);
58
- if (!usage)
73
+ }
74
+ const roleRaw = message.role;
75
+ const role = roleRaw === "user" || roleRaw === "assistant" ? roleRaw : undefined;
76
+ if (!role) {
59
77
  return null;
60
- const provider = (typeof message?.provider === "string" ? message?.provider : undefined) ??
78
+ }
79
+ const usageRaw = message.usage ?? entry.usage;
80
+ const usage = usageRaw ? (normalizeUsage(usageRaw) ?? undefined) : undefined;
81
+ const provider = (typeof message.provider === "string" ? message.provider : undefined) ??
61
82
  (typeof entry.provider === "string" ? entry.provider : undefined);
62
- const model = (typeof message?.model === "string" ? message?.model : undefined) ??
83
+ const model = (typeof message.model === "string" ? message.model : undefined) ??
63
84
  (typeof entry.model === "string" ? entry.model : undefined);
85
+ const costBreakdown = extractCostBreakdown(usageRaw);
86
+ const stopReason = typeof message.stopReason === "string" ? message.stopReason : undefined;
87
+ const durationMs = toFiniteNumber(message.durationMs ?? entry.durationMs);
64
88
  return {
89
+ message,
90
+ role,
91
+ timestamp: parseTimestamp(entry),
92
+ durationMs,
65
93
  usage,
66
- costTotal: extractCostTotal(usageRaw),
94
+ costTotal: costBreakdown?.total,
95
+ costBreakdown,
67
96
  provider,
68
97
  model,
69
- timestamp: parseTimestamp(entry),
98
+ stopReason,
99
+ toolNames: extractToolCallNames(message),
100
+ toolResultCounts: countToolResults(message),
70
101
  };
71
102
  };
72
103
  const formatDayKey = (date) => date.toLocaleDateString("en-CA", { timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone });
104
+ const computeLatencyStats = (values) => {
105
+ if (!values.length) {
106
+ return undefined;
107
+ }
108
+ const sorted = [...values].sort((a, b) => a - b);
109
+ const total = sorted.reduce((sum, v) => sum + v, 0);
110
+ const count = sorted.length;
111
+ const p95Index = Math.max(0, Math.ceil(count * 0.95) - 1);
112
+ return {
113
+ count,
114
+ avgMs: total / count,
115
+ p95Ms: sorted[p95Index] ?? sorted[count - 1],
116
+ minMs: sorted[0],
117
+ maxMs: sorted[count - 1],
118
+ };
119
+ };
73
120
  const applyUsageTotals = (totals, usage) => {
74
121
  totals.input += usage.input ?? 0;
75
122
  totals.output += usage.output ?? 0;
@@ -79,6 +126,17 @@ const applyUsageTotals = (totals, usage) => {
79
126
  (usage.input ?? 0) + (usage.output ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
80
127
  totals.totalTokens += totalTokens;
81
128
  };
129
+ const applyCostBreakdown = (totals, costBreakdown) => {
130
+ if (costBreakdown === undefined || costBreakdown.total === undefined) {
131
+ return;
132
+ }
133
+ totals.totalCost += costBreakdown.total;
134
+ totals.inputCost += costBreakdown.input ?? 0;
135
+ totals.outputCost += costBreakdown.output ?? 0;
136
+ totals.cacheReadCost += costBreakdown.cacheRead ?? 0;
137
+ totals.cacheWriteCost += costBreakdown.cacheWrite ?? 0;
138
+ };
139
+ // Legacy function for backwards compatibility (no cost breakdown available)
82
140
  const applyCostTotal = (totals, costTotal) => {
83
141
  if (costTotal === undefined) {
84
142
  totals.missingCostEntries += 1;
@@ -86,19 +144,21 @@ const applyCostTotal = (totals, costTotal) => {
86
144
  }
87
145
  totals.totalCost += costTotal;
88
146
  };
89
- async function scanUsageFile(params) {
147
+ async function scanTranscriptFile(params) {
90
148
  const fileStream = fs.createReadStream(params.filePath, { encoding: "utf-8" });
91
149
  const rl = readline.createInterface({ input: fileStream, crlfDelay: Infinity });
92
150
  for await (const line of rl) {
93
151
  const trimmed = line.trim();
94
- if (!trimmed)
152
+ if (!trimmed) {
95
153
  continue;
154
+ }
96
155
  try {
97
156
  const parsed = JSON.parse(trimmed);
98
- const entry = parseUsageEntry(parsed);
99
- if (!entry)
157
+ const entry = parseTranscriptEntry(parsed);
158
+ if (!entry) {
100
159
  continue;
101
- if (entry.costTotal === undefined) {
160
+ }
161
+ if (entry.usage && entry.costTotal === undefined) {
102
162
  const cost = resolveModelCostConfig({
103
163
  provider: entry.provider,
104
164
  model: entry.model,
@@ -113,12 +173,41 @@ async function scanUsageFile(params) {
113
173
  }
114
174
  }
115
175
  }
176
+ async function scanUsageFile(params) {
177
+ await scanTranscriptFile({
178
+ filePath: params.filePath,
179
+ config: params.config,
180
+ onEntry: (entry) => {
181
+ if (!entry.usage) {
182
+ return;
183
+ }
184
+ params.onEntry({
185
+ usage: entry.usage,
186
+ costTotal: entry.costTotal,
187
+ costBreakdown: entry.costBreakdown,
188
+ provider: entry.provider,
189
+ model: entry.model,
190
+ timestamp: entry.timestamp,
191
+ });
192
+ },
193
+ });
194
+ }
116
195
  export async function loadCostUsageSummary(params) {
117
- const days = Math.max(1, Math.floor(params?.days ?? 30));
118
196
  const now = new Date();
119
- const since = new Date(now);
120
- since.setDate(since.getDate() - (days - 1));
121
- const sinceTime = since.getTime();
197
+ let sinceTime;
198
+ let untilTime;
199
+ if (params?.startMs !== undefined && params?.endMs !== undefined) {
200
+ sinceTime = params.startMs;
201
+ untilTime = params.endMs;
202
+ }
203
+ else {
204
+ // Fallback to days-based calculation for backwards compatibility
205
+ const days = Math.max(1, Math.floor(params?.days ?? 30));
206
+ const since = new Date(now);
207
+ since.setDate(since.getDate() - (days - 1));
208
+ sinceTime = since.getTime();
209
+ untilTime = now.getTime();
210
+ }
122
211
  const dailyMap = new Map();
123
212
  const totals = emptyTotals();
124
213
  const sessionsDir = resolveSessionTranscriptsDirForAgent(params?.agentId);
@@ -128,10 +217,13 @@ export async function loadCostUsageSummary(params) {
128
217
  .map(async (entry) => {
129
218
  const filePath = path.join(sessionsDir, entry.name);
130
219
  const stats = await fs.promises.stat(filePath).catch(() => null);
131
- if (!stats)
220
+ if (!stats) {
132
221
  return null;
133
- if (stats.mtimeMs < sinceTime)
222
+ }
223
+ // Include file if it was modified after our start time
224
+ if (stats.mtimeMs < sinceTime) {
134
225
  return null;
226
+ }
135
227
  return filePath;
136
228
  }))).filter((filePath) => Boolean(filePath));
137
229
  for (const filePath of files) {
@@ -140,21 +232,34 @@ export async function loadCostUsageSummary(params) {
140
232
  config: params?.config,
141
233
  onEntry: (entry) => {
142
234
  const ts = entry.timestamp?.getTime();
143
- if (!ts || ts < sinceTime)
235
+ if (!ts || ts < sinceTime || ts > untilTime) {
144
236
  return;
237
+ }
145
238
  const dayKey = formatDayKey(entry.timestamp ?? now);
146
239
  const bucket = dailyMap.get(dayKey) ?? emptyTotals();
147
240
  applyUsageTotals(bucket, entry.usage);
148
- applyCostTotal(bucket, entry.costTotal);
241
+ if (entry.costBreakdown?.total !== undefined) {
242
+ applyCostBreakdown(bucket, entry.costBreakdown);
243
+ }
244
+ else {
245
+ applyCostTotal(bucket, entry.costTotal);
246
+ }
149
247
  dailyMap.set(dayKey, bucket);
150
248
  applyUsageTotals(totals, entry.usage);
151
- applyCostTotal(totals, entry.costTotal);
249
+ if (entry.costBreakdown?.total !== undefined) {
250
+ applyCostBreakdown(totals, entry.costBreakdown);
251
+ }
252
+ else {
253
+ applyCostTotal(totals, entry.costTotal);
254
+ }
152
255
  },
153
256
  });
154
257
  }
155
258
  const daily = Array.from(dailyMap.entries())
156
- .map(([date, bucket]) => ({ date, ...bucket }))
259
+ .map(([date, bucket]) => Object.assign({ date }, bucket))
157
260
  .sort((a, b) => a.date.localeCompare(b.date));
261
+ // Calculate days for backwards compatibility in response
262
+ const days = Math.ceil((untilTime - sinceTime) / (24 * 60 * 60 * 1000)) + 1;
158
263
  return {
159
264
  updatedAt: Date.now(),
160
265
  days,
@@ -162,29 +267,514 @@ export async function loadCostUsageSummary(params) {
162
267
  totals,
163
268
  };
164
269
  }
270
+ /**
271
+ * Scan all transcript files to discover sessions not in the session store.
272
+ * Returns basic metadata for each discovered session.
273
+ */
274
+ export async function discoverAllSessions(params) {
275
+ const sessionsDir = resolveSessionTranscriptsDirForAgent(params?.agentId);
276
+ const entries = await fs.promises.readdir(sessionsDir, { withFileTypes: true }).catch(() => []);
277
+ const discovered = [];
278
+ for (const entry of entries) {
279
+ if (!entry.isFile() || !entry.name.endsWith(".jsonl")) {
280
+ continue;
281
+ }
282
+ const filePath = path.join(sessionsDir, entry.name);
283
+ const stats = await fs.promises.stat(filePath).catch(() => null);
284
+ if (!stats) {
285
+ continue;
286
+ }
287
+ // Filter by date range if provided
288
+ if (params?.startMs && stats.mtimeMs < params.startMs) {
289
+ continue;
290
+ }
291
+ // Do not exclude by endMs: a session can have activity in range even if it continued later.
292
+ // Extract session ID from filename (remove .jsonl)
293
+ const sessionId = entry.name.slice(0, -6);
294
+ // Try to read first user message for label extraction
295
+ let firstUserMessage;
296
+ try {
297
+ const fileStream = fs.createReadStream(filePath, { encoding: "utf-8" });
298
+ const rl = readline.createInterface({ input: fileStream, crlfDelay: Infinity });
299
+ for await (const line of rl) {
300
+ const trimmed = line.trim();
301
+ if (!trimmed) {
302
+ continue;
303
+ }
304
+ try {
305
+ const parsed = JSON.parse(trimmed);
306
+ const message = parsed.message;
307
+ if (message?.role === "user") {
308
+ const content = message.content;
309
+ if (typeof content === "string") {
310
+ firstUserMessage = content.slice(0, 100);
311
+ }
312
+ else if (Array.isArray(content)) {
313
+ for (const block of content) {
314
+ if (typeof block === "object" &&
315
+ block &&
316
+ block.type === "text") {
317
+ const text = block.text;
318
+ if (typeof text === "string") {
319
+ firstUserMessage = text.slice(0, 100);
320
+ }
321
+ break;
322
+ }
323
+ }
324
+ }
325
+ break; // Found first user message
326
+ }
327
+ }
328
+ catch {
329
+ // Skip malformed lines
330
+ }
331
+ }
332
+ rl.close();
333
+ fileStream.destroy();
334
+ }
335
+ catch {
336
+ // Ignore read errors
337
+ }
338
+ discovered.push({
339
+ sessionId,
340
+ sessionFile: filePath,
341
+ mtime: stats.mtimeMs,
342
+ firstUserMessage,
343
+ });
344
+ }
345
+ // Sort by mtime descending (most recent first)
346
+ return discovered.sort((a, b) => b.mtime - a.mtime);
347
+ }
165
348
  export async function loadSessionCostSummary(params) {
166
349
  const sessionFile = params.sessionFile ??
167
350
  (params.sessionId ? resolveSessionFilePath(params.sessionId, params.sessionEntry) : undefined);
168
- if (!sessionFile || !fs.existsSync(sessionFile))
351
+ if (!sessionFile || !fs.existsSync(sessionFile)) {
169
352
  return null;
353
+ }
170
354
  const totals = emptyTotals();
355
+ let firstActivity;
171
356
  let lastActivity;
172
- await scanUsageFile({
357
+ const activityDatesSet = new Set();
358
+ const dailyMap = new Map();
359
+ const dailyMessageMap = new Map();
360
+ const dailyLatencyMap = new Map();
361
+ const dailyModelUsageMap = new Map();
362
+ const messageCounts = {
363
+ total: 0,
364
+ user: 0,
365
+ assistant: 0,
366
+ toolCalls: 0,
367
+ toolResults: 0,
368
+ errors: 0,
369
+ };
370
+ const toolUsageMap = new Map();
371
+ const modelUsageMap = new Map();
372
+ const errorStopReasons = new Set(["error", "aborted", "timeout"]);
373
+ const latencyValues = [];
374
+ let lastUserTimestamp;
375
+ const MAX_LATENCY_MS = 12 * 60 * 60 * 1000;
376
+ await scanTranscriptFile({
173
377
  filePath: sessionFile,
174
378
  config: params.config,
175
379
  onEntry: (entry) => {
176
- applyUsageTotals(totals, entry.usage);
177
- applyCostTotal(totals, entry.costTotal);
178
380
  const ts = entry.timestamp?.getTime();
179
- if (ts && (!lastActivity || ts > lastActivity)) {
180
- lastActivity = ts;
381
+ // Filter by date range if specified
382
+ if (params.startMs !== undefined && ts !== undefined && ts < params.startMs) {
383
+ return;
384
+ }
385
+ if (params.endMs !== undefined && ts !== undefined && ts > params.endMs) {
386
+ return;
387
+ }
388
+ if (ts !== undefined) {
389
+ if (!firstActivity || ts < firstActivity) {
390
+ firstActivity = ts;
391
+ }
392
+ if (!lastActivity || ts > lastActivity) {
393
+ lastActivity = ts;
394
+ }
395
+ }
396
+ if (entry.role === "user") {
397
+ messageCounts.user += 1;
398
+ messageCounts.total += 1;
399
+ if (entry.timestamp) {
400
+ lastUserTimestamp = entry.timestamp.getTime();
401
+ }
402
+ }
403
+ if (entry.role === "assistant") {
404
+ messageCounts.assistant += 1;
405
+ messageCounts.total += 1;
406
+ const ts = entry.timestamp?.getTime();
407
+ if (ts !== undefined) {
408
+ const latencyMs = entry.durationMs ??
409
+ (lastUserTimestamp !== undefined ? Math.max(0, ts - lastUserTimestamp) : undefined);
410
+ if (latencyMs !== undefined &&
411
+ Number.isFinite(latencyMs) &&
412
+ latencyMs <= MAX_LATENCY_MS) {
413
+ latencyValues.push(latencyMs);
414
+ const dayKey = formatDayKey(entry.timestamp ?? new Date(ts));
415
+ const dailyLatencies = dailyLatencyMap.get(dayKey) ?? [];
416
+ dailyLatencies.push(latencyMs);
417
+ dailyLatencyMap.set(dayKey, dailyLatencies);
418
+ }
419
+ }
420
+ }
421
+ if (entry.toolNames.length > 0) {
422
+ messageCounts.toolCalls += entry.toolNames.length;
423
+ for (const name of entry.toolNames) {
424
+ toolUsageMap.set(name, (toolUsageMap.get(name) ?? 0) + 1);
425
+ }
426
+ }
427
+ if (entry.toolResultCounts.total > 0) {
428
+ messageCounts.toolResults += entry.toolResultCounts.total;
429
+ messageCounts.errors += entry.toolResultCounts.errors;
430
+ }
431
+ if (entry.stopReason && errorStopReasons.has(entry.stopReason)) {
432
+ messageCounts.errors += 1;
433
+ }
434
+ if (entry.timestamp) {
435
+ const dayKey = formatDayKey(entry.timestamp);
436
+ activityDatesSet.add(dayKey);
437
+ const daily = dailyMessageMap.get(dayKey) ?? {
438
+ date: dayKey,
439
+ total: 0,
440
+ user: 0,
441
+ assistant: 0,
442
+ toolCalls: 0,
443
+ toolResults: 0,
444
+ errors: 0,
445
+ };
446
+ daily.total += entry.role === "user" || entry.role === "assistant" ? 1 : 0;
447
+ if (entry.role === "user") {
448
+ daily.user += 1;
449
+ }
450
+ else if (entry.role === "assistant") {
451
+ daily.assistant += 1;
452
+ }
453
+ daily.toolCalls += entry.toolNames.length;
454
+ daily.toolResults += entry.toolResultCounts.total;
455
+ daily.errors += entry.toolResultCounts.errors;
456
+ if (entry.stopReason && errorStopReasons.has(entry.stopReason)) {
457
+ daily.errors += 1;
458
+ }
459
+ dailyMessageMap.set(dayKey, daily);
460
+ }
461
+ if (!entry.usage) {
462
+ return;
463
+ }
464
+ applyUsageTotals(totals, entry.usage);
465
+ if (entry.costBreakdown?.total !== undefined) {
466
+ applyCostBreakdown(totals, entry.costBreakdown);
467
+ }
468
+ else {
469
+ applyCostTotal(totals, entry.costTotal);
470
+ }
471
+ if (entry.timestamp) {
472
+ const dayKey = formatDayKey(entry.timestamp);
473
+ const entryTokens = (entry.usage.input ?? 0) +
474
+ (entry.usage.output ?? 0) +
475
+ (entry.usage.cacheRead ?? 0) +
476
+ (entry.usage.cacheWrite ?? 0);
477
+ const entryCost = entry.costBreakdown?.total ??
478
+ (entry.costBreakdown
479
+ ? (entry.costBreakdown.input ?? 0) +
480
+ (entry.costBreakdown.output ?? 0) +
481
+ (entry.costBreakdown.cacheRead ?? 0) +
482
+ (entry.costBreakdown.cacheWrite ?? 0)
483
+ : (entry.costTotal ?? 0));
484
+ const existing = dailyMap.get(dayKey) ?? { tokens: 0, cost: 0 };
485
+ dailyMap.set(dayKey, {
486
+ tokens: existing.tokens + entryTokens,
487
+ cost: existing.cost + entryCost,
488
+ });
489
+ if (entry.provider || entry.model) {
490
+ const modelKey = `${dayKey}::${entry.provider ?? "unknown"}::${entry.model ?? "unknown"}`;
491
+ const dailyModel = dailyModelUsageMap.get(modelKey) ??
492
+ {
493
+ date: dayKey,
494
+ provider: entry.provider,
495
+ model: entry.model,
496
+ tokens: 0,
497
+ cost: 0,
498
+ count: 0,
499
+ };
500
+ dailyModel.tokens += entryTokens;
501
+ dailyModel.cost += entryCost;
502
+ dailyModel.count += 1;
503
+ dailyModelUsageMap.set(modelKey, dailyModel);
504
+ }
505
+ }
506
+ if (entry.provider || entry.model) {
507
+ const key = `${entry.provider ?? "unknown"}::${entry.model ?? "unknown"}`;
508
+ const existing = modelUsageMap.get(key) ??
509
+ {
510
+ provider: entry.provider,
511
+ model: entry.model,
512
+ count: 0,
513
+ totals: emptyTotals(),
514
+ };
515
+ existing.count += 1;
516
+ applyUsageTotals(existing.totals, entry.usage);
517
+ if (entry.costBreakdown?.total !== undefined) {
518
+ applyCostBreakdown(existing.totals, entry.costBreakdown);
519
+ }
520
+ else {
521
+ applyCostTotal(existing.totals, entry.costTotal);
522
+ }
523
+ modelUsageMap.set(key, existing);
181
524
  }
182
525
  },
183
526
  });
527
+ // Convert daily map to sorted array
528
+ const dailyBreakdown = Array.from(dailyMap.entries())
529
+ .map(([date, data]) => ({ date, tokens: data.tokens, cost: data.cost }))
530
+ .sort((a, b) => a.date.localeCompare(b.date));
531
+ const dailyMessageCounts = Array.from(dailyMessageMap.values()).sort((a, b) => a.date.localeCompare(b.date));
532
+ const dailyLatency = Array.from(dailyLatencyMap.entries())
533
+ .map(([date, values]) => {
534
+ const stats = computeLatencyStats(values);
535
+ if (!stats) {
536
+ return null;
537
+ }
538
+ return { date, ...stats };
539
+ })
540
+ .filter((entry) => Boolean(entry))
541
+ .sort((a, b) => a.date.localeCompare(b.date));
542
+ const dailyModelUsage = Array.from(dailyModelUsageMap.values()).sort((a, b) => a.date.localeCompare(b.date) || b.cost - a.cost);
543
+ const toolUsage = toolUsageMap.size
544
+ ? {
545
+ totalCalls: Array.from(toolUsageMap.values()).reduce((sum, count) => sum + count, 0),
546
+ uniqueTools: toolUsageMap.size,
547
+ tools: Array.from(toolUsageMap.entries())
548
+ .map(([name, count]) => ({ name, count }))
549
+ .sort((a, b) => b.count - a.count),
550
+ }
551
+ : undefined;
552
+ const modelUsage = modelUsageMap.size
553
+ ? Array.from(modelUsageMap.values()).sort((a, b) => {
554
+ const costDiff = b.totals.totalCost - a.totals.totalCost;
555
+ if (costDiff !== 0) {
556
+ return costDiff;
557
+ }
558
+ return b.totals.totalTokens - a.totals.totalTokens;
559
+ })
560
+ : undefined;
184
561
  return {
185
562
  sessionId: params.sessionId,
186
563
  sessionFile,
564
+ firstActivity,
187
565
  lastActivity,
566
+ durationMs: firstActivity !== undefined && lastActivity !== undefined
567
+ ? Math.max(0, lastActivity - firstActivity)
568
+ : undefined,
569
+ activityDates: Array.from(activityDatesSet).sort(),
570
+ dailyBreakdown,
571
+ dailyMessageCounts,
572
+ dailyLatency: dailyLatency.length ? dailyLatency : undefined,
573
+ dailyModelUsage: dailyModelUsage.length ? dailyModelUsage : undefined,
574
+ messageCounts,
575
+ toolUsage,
576
+ modelUsage,
577
+ latency: computeLatencyStats(latencyValues),
188
578
  ...totals,
189
579
  };
190
580
  }
581
+ export async function loadSessionUsageTimeSeries(params) {
582
+ const sessionFile = params.sessionFile ??
583
+ (params.sessionId ? resolveSessionFilePath(params.sessionId, params.sessionEntry) : undefined);
584
+ if (!sessionFile || !fs.existsSync(sessionFile)) {
585
+ return null;
586
+ }
587
+ const points = [];
588
+ let cumulativeTokens = 0;
589
+ let cumulativeCost = 0;
590
+ await scanUsageFile({
591
+ filePath: sessionFile,
592
+ config: params.config,
593
+ onEntry: (entry) => {
594
+ const ts = entry.timestamp?.getTime();
595
+ if (!ts) {
596
+ return;
597
+ }
598
+ const input = entry.usage.input ?? 0;
599
+ const output = entry.usage.output ?? 0;
600
+ const cacheRead = entry.usage.cacheRead ?? 0;
601
+ const cacheWrite = entry.usage.cacheWrite ?? 0;
602
+ const totalTokens = entry.usage.total ?? input + output + cacheRead + cacheWrite;
603
+ const cost = entry.costTotal ?? 0;
604
+ cumulativeTokens += totalTokens;
605
+ cumulativeCost += cost;
606
+ points.push({
607
+ timestamp: ts,
608
+ input,
609
+ output,
610
+ cacheRead,
611
+ cacheWrite,
612
+ totalTokens,
613
+ cost,
614
+ cumulativeTokens,
615
+ cumulativeCost,
616
+ });
617
+ },
618
+ });
619
+ // Sort by timestamp
620
+ const sortedPoints = points.sort((a, b) => a.timestamp - b.timestamp);
621
+ // Optionally downsample if too many points
622
+ const maxPoints = params.maxPoints ?? 100;
623
+ if (sortedPoints.length > maxPoints) {
624
+ const step = Math.ceil(sortedPoints.length / maxPoints);
625
+ const downsampled = [];
626
+ for (let i = 0; i < sortedPoints.length; i += step) {
627
+ downsampled.push(sortedPoints[i]);
628
+ }
629
+ // Always include the last point
630
+ if (downsampled[downsampled.length - 1] !== sortedPoints[sortedPoints.length - 1]) {
631
+ downsampled.push(sortedPoints[sortedPoints.length - 1]);
632
+ }
633
+ return { sessionId: params.sessionId, points: downsampled };
634
+ }
635
+ return { sessionId: params.sessionId, points: sortedPoints };
636
+ }
637
+ export async function loadSessionLogs(params) {
638
+ const sessionFile = params.sessionFile ??
639
+ (params.sessionId ? resolveSessionFilePath(params.sessionId, params.sessionEntry) : undefined);
640
+ if (!sessionFile || !fs.existsSync(sessionFile)) {
641
+ return null;
642
+ }
643
+ const logs = [];
644
+ const limit = params.limit ?? 50;
645
+ const fileStream = fs.createReadStream(sessionFile, { encoding: "utf-8" });
646
+ const rl = readline.createInterface({ input: fileStream, crlfDelay: Infinity });
647
+ for await (const line of rl) {
648
+ const trimmed = line.trim();
649
+ if (!trimmed) {
650
+ continue;
651
+ }
652
+ try {
653
+ const parsed = JSON.parse(trimmed);
654
+ const message = parsed.message;
655
+ if (!message) {
656
+ continue;
657
+ }
658
+ const role = message.role;
659
+ if (role !== "user" && role !== "assistant" && role !== "tool" && role !== "toolResult") {
660
+ continue;
661
+ }
662
+ const contentParts = [];
663
+ const rawToolName = message.toolName ?? message.tool_name ?? message.name ?? message.tool;
664
+ const toolName = typeof rawToolName === "string" && rawToolName.trim() ? rawToolName.trim() : undefined;
665
+ if (role === "tool" || role === "toolResult") {
666
+ contentParts.push(`[Tool: ${toolName ?? "tool"}]`);
667
+ contentParts.push("[Tool Result]");
668
+ }
669
+ // Extract content
670
+ const rawContent = message.content;
671
+ if (typeof rawContent === "string") {
672
+ contentParts.push(rawContent);
673
+ }
674
+ else if (Array.isArray(rawContent)) {
675
+ // Handle content blocks (text, tool_use, etc.)
676
+ const contentText = rawContent
677
+ .map((block) => {
678
+ if (typeof block === "string") {
679
+ return block;
680
+ }
681
+ const b = block;
682
+ if (b.type === "text" && typeof b.text === "string") {
683
+ return b.text;
684
+ }
685
+ if (b.type === "tool_use") {
686
+ const name = typeof b.name === "string" ? b.name : "unknown";
687
+ return `[Tool: ${name}]`;
688
+ }
689
+ if (b.type === "tool_result") {
690
+ return `[Tool Result]`;
691
+ }
692
+ return "";
693
+ })
694
+ .filter(Boolean)
695
+ .join("\n");
696
+ if (contentText) {
697
+ contentParts.push(contentText);
698
+ }
699
+ }
700
+ // OpenAI-style tool calls stored outside the content array.
701
+ const rawToolCalls = message.tool_calls ?? message.toolCalls ?? message.function_call ?? message.functionCall;
702
+ const toolCalls = Array.isArray(rawToolCalls)
703
+ ? rawToolCalls
704
+ : rawToolCalls
705
+ ? [rawToolCalls]
706
+ : [];
707
+ if (toolCalls.length > 0) {
708
+ for (const call of toolCalls) {
709
+ const callObj = call;
710
+ const directName = typeof callObj.name === "string" ? callObj.name : undefined;
711
+ const fn = callObj.function;
712
+ const fnName = typeof fn?.name === "string" ? fn.name : undefined;
713
+ const name = directName ?? fnName ?? "unknown";
714
+ contentParts.push(`[Tool: ${name}]`);
715
+ }
716
+ }
717
+ let content = contentParts.join("\n").trim();
718
+ if (!content) {
719
+ continue;
720
+ }
721
+ // Truncate very long content
722
+ const maxLen = 2000;
723
+ if (content.length > maxLen) {
724
+ content = content.slice(0, maxLen) + "\u2026";
725
+ }
726
+ // Get timestamp
727
+ let timestamp = 0;
728
+ if (typeof parsed.timestamp === "string") {
729
+ timestamp = new Date(parsed.timestamp).getTime();
730
+ }
731
+ else if (typeof message.timestamp === "number") {
732
+ timestamp = message.timestamp;
733
+ }
734
+ // Get usage for assistant messages
735
+ let tokens;
736
+ let cost;
737
+ if (role === "assistant") {
738
+ const usageRaw = message.usage;
739
+ const usage = normalizeUsage(usageRaw);
740
+ if (usage) {
741
+ tokens =
742
+ usage.total ??
743
+ (usage.input ?? 0) +
744
+ (usage.output ?? 0) +
745
+ (usage.cacheRead ?? 0) +
746
+ (usage.cacheWrite ?? 0);
747
+ const breakdown = extractCostBreakdown(usageRaw);
748
+ if (breakdown?.total !== undefined) {
749
+ cost = breakdown.total;
750
+ }
751
+ else {
752
+ const costConfig = resolveModelCostConfig({
753
+ provider: message.provider,
754
+ model: message.model,
755
+ config: params.config,
756
+ });
757
+ cost = estimateUsageCost({ usage, cost: costConfig });
758
+ }
759
+ }
760
+ }
761
+ logs.push({
762
+ timestamp,
763
+ role,
764
+ content,
765
+ tokens,
766
+ cost,
767
+ });
768
+ }
769
+ catch {
770
+ // Ignore malformed lines
771
+ }
772
+ }
773
+ // Sort by timestamp and limit
774
+ const sortedLogs = logs.sort((a, b) => a.timestamp - b.timestamp);
775
+ // Return most recent logs
776
+ if (sortedLogs.length > limit) {
777
+ return sortedLogs.slice(-limit);
778
+ }
779
+ return sortedLogs;
780
+ }