@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
@@ -25,9 +25,17 @@ export function buildEmbeddedSystemPrompt(params) {
25
25
  userTime: params.userTime,
26
26
  userTimeFormat: params.userTimeFormat,
27
27
  contextFiles: params.contextFiles,
28
+ memoryCitationsMode: params.memoryCitationsMode,
28
29
  });
29
30
  }
30
31
  export function createSystemPromptOverride(systemPrompt) {
31
- const trimmed = systemPrompt.trim();
32
- return () => trimmed;
32
+ const override = systemPrompt.trim();
33
+ return (_defaultPrompt) => override;
34
+ }
35
+ export function applySystemPromptOverrideToSession(session, override) {
36
+ const prompt = typeof override === "function" ? override() : override.trim();
37
+ session.agent.setSystemPrompt(prompt);
38
+ const mutableSession = session;
39
+ mutableSession._baseSystemPrompt = prompt;
40
+ mutableSession._rebuildSystemPrompt = () => prompt;
33
41
  }
@@ -0,0 +1,275 @@
1
+ import { SessionManager } from "@mariozechner/pi-coding-agent";
2
+ import { log } from "./logger.js";
3
+ /**
4
+ * Maximum share of the context window a single tool result should occupy.
5
+ * This is intentionally conservative – a single tool result should not
6
+ * consume more than 30% of the context window even without other messages.
7
+ */
8
+ const MAX_TOOL_RESULT_CONTEXT_SHARE = 0.3;
9
+ /**
10
+ * Hard character limit for a single tool result text block.
11
+ * Even for the largest context windows (~2M tokens), a single tool result
12
+ * should not exceed ~400K characters (~100K tokens).
13
+ * This acts as a safety net when we don't know the context window size.
14
+ */
15
+ export const HARD_MAX_TOOL_RESULT_CHARS = 400_000;
16
+ /**
17
+ * Minimum characters to keep when truncating.
18
+ * We always keep at least the first portion so the model understands
19
+ * what was in the content.
20
+ */
21
+ const MIN_KEEP_CHARS = 2_000;
22
+ /**
23
+ * Suffix appended to truncated tool results.
24
+ */
25
+ const TRUNCATION_SUFFIX = "\n\n⚠️ [Content truncated — original was too large for the model's context window. " +
26
+ "The content above is a partial view. If you need more, request specific sections or use " +
27
+ "offset/limit parameters to read smaller chunks.]";
28
+ /**
29
+ * Truncate a single text string to fit within maxChars, preserving the beginning.
30
+ */
31
+ export function truncateToolResultText(text, maxChars) {
32
+ if (text.length <= maxChars) {
33
+ return text;
34
+ }
35
+ const keepChars = Math.max(MIN_KEEP_CHARS, maxChars - TRUNCATION_SUFFIX.length);
36
+ // Try to break at a newline boundary to avoid cutting mid-line
37
+ let cutPoint = keepChars;
38
+ const lastNewline = text.lastIndexOf("\n", keepChars);
39
+ if (lastNewline > keepChars * 0.8) {
40
+ cutPoint = lastNewline;
41
+ }
42
+ return text.slice(0, cutPoint) + TRUNCATION_SUFFIX;
43
+ }
44
+ /**
45
+ * Calculate the maximum allowed characters for a single tool result
46
+ * based on the model's context window tokens.
47
+ *
48
+ * Uses a rough 4 chars ≈ 1 token heuristic (conservative for English text;
49
+ * actual ratio varies by tokenizer).
50
+ */
51
+ export function calculateMaxToolResultChars(contextWindowTokens) {
52
+ const maxTokens = Math.floor(contextWindowTokens * MAX_TOOL_RESULT_CONTEXT_SHARE);
53
+ // Rough conversion: ~4 chars per token on average
54
+ const maxChars = maxTokens * 4;
55
+ return Math.min(maxChars, HARD_MAX_TOOL_RESULT_CHARS);
56
+ }
57
+ /**
58
+ * Get the total character count of text content blocks in a tool result message.
59
+ */
60
+ function getToolResultTextLength(msg) {
61
+ if (!msg || msg.role !== "toolResult") {
62
+ return 0;
63
+ }
64
+ const content = msg.content;
65
+ if (!Array.isArray(content)) {
66
+ return 0;
67
+ }
68
+ let totalLength = 0;
69
+ for (const block of content) {
70
+ if (block && typeof block === "object" && block.type === "text") {
71
+ const text = block.text;
72
+ if (typeof text === "string") {
73
+ totalLength += text.length;
74
+ }
75
+ }
76
+ }
77
+ return totalLength;
78
+ }
79
+ /**
80
+ * Truncate a tool result message's text content blocks to fit within maxChars.
81
+ * Returns a new message (does not mutate the original).
82
+ */
83
+ function truncateToolResultMessage(msg, maxChars) {
84
+ const content = msg.content;
85
+ if (!Array.isArray(content)) {
86
+ return msg;
87
+ }
88
+ // Calculate total text size
89
+ const totalTextChars = getToolResultTextLength(msg);
90
+ if (totalTextChars <= maxChars) {
91
+ return msg;
92
+ }
93
+ // Distribute the budget proportionally among text blocks
94
+ const newContent = content.map((block) => {
95
+ if (!block || typeof block !== "object" || block.type !== "text") {
96
+ return block; // Keep non-text blocks (images) as-is
97
+ }
98
+ const textBlock = block;
99
+ if (typeof textBlock.text !== "string") {
100
+ return block;
101
+ }
102
+ // Proportional budget for this block
103
+ const blockShare = textBlock.text.length / totalTextChars;
104
+ const blockBudget = Math.max(MIN_KEEP_CHARS, Math.floor(maxChars * blockShare));
105
+ return {
106
+ ...textBlock,
107
+ text: truncateToolResultText(textBlock.text, blockBudget),
108
+ };
109
+ });
110
+ return { ...msg, content: newContent };
111
+ }
112
+ /**
113
+ * Find oversized tool result entries in a session and truncate them.
114
+ *
115
+ * This operates on the session file by:
116
+ * 1. Opening the session manager
117
+ * 2. Walking the current branch to find oversized tool results
118
+ * 3. Branching from before the first oversized tool result
119
+ * 4. Re-appending all entries from that point with truncated tool results
120
+ *
121
+ * @returns Object indicating whether any truncation was performed
122
+ */
123
+ export async function truncateOversizedToolResultsInSession(params) {
124
+ const { sessionFile, contextWindowTokens } = params;
125
+ const maxChars = calculateMaxToolResultChars(contextWindowTokens);
126
+ try {
127
+ const sessionManager = SessionManager.open(sessionFile);
128
+ const branch = sessionManager.getBranch();
129
+ if (branch.length === 0) {
130
+ return { truncated: false, truncatedCount: 0, reason: "empty session" };
131
+ }
132
+ // Find oversized tool result entries and their indices in the branch
133
+ const oversizedIndices = [];
134
+ for (let i = 0; i < branch.length; i++) {
135
+ const entry = branch[i];
136
+ if (entry.type !== "message") {
137
+ continue;
138
+ }
139
+ const msg = entry.message;
140
+ if (msg.role !== "toolResult") {
141
+ continue;
142
+ }
143
+ const textLength = getToolResultTextLength(msg);
144
+ if (textLength > maxChars) {
145
+ oversizedIndices.push(i);
146
+ log.info(`[tool-result-truncation] Found oversized tool result: ` +
147
+ `entry=${entry.id} chars=${textLength} maxChars=${maxChars} ` +
148
+ `sessionKey=${params.sessionKey ?? params.sessionId ?? "unknown"}`);
149
+ }
150
+ }
151
+ if (oversizedIndices.length === 0) {
152
+ return { truncated: false, truncatedCount: 0, reason: "no oversized tool results" };
153
+ }
154
+ // Branch from the parent of the first oversized entry
155
+ const firstOversizedIdx = oversizedIndices[0];
156
+ const firstOversizedEntry = branch[firstOversizedIdx];
157
+ const branchFromId = firstOversizedEntry.parentId;
158
+ if (!branchFromId) {
159
+ // The oversized entry is the root - very unusual but handle it
160
+ sessionManager.resetLeaf();
161
+ }
162
+ else {
163
+ sessionManager.branch(branchFromId);
164
+ }
165
+ // Re-append all entries from the first oversized one onwards,
166
+ // with truncated tool results
167
+ const oversizedSet = new Set(oversizedIndices);
168
+ let truncatedCount = 0;
169
+ for (let i = firstOversizedIdx; i < branch.length; i++) {
170
+ const entry = branch[i];
171
+ if (entry.type === "message") {
172
+ let message = entry.message;
173
+ if (oversizedSet.has(i)) {
174
+ message = truncateToolResultMessage(message, maxChars);
175
+ truncatedCount++;
176
+ const newLength = getToolResultTextLength(message);
177
+ log.info(`[tool-result-truncation] Truncated tool result: ` +
178
+ `originalEntry=${entry.id} newChars=${newLength} ` +
179
+ `sessionKey=${params.sessionKey ?? params.sessionId ?? "unknown"}`);
180
+ }
181
+ // appendMessage expects Message | CustomMessage | BashExecutionMessage
182
+ sessionManager.appendMessage(message);
183
+ }
184
+ else if (entry.type === "compaction") {
185
+ sessionManager.appendCompaction(entry.summary, entry.firstKeptEntryId, entry.tokensBefore, entry.details, entry.fromHook);
186
+ }
187
+ else if (entry.type === "thinking_level_change") {
188
+ sessionManager.appendThinkingLevelChange(entry.thinkingLevel);
189
+ }
190
+ else if (entry.type === "model_change") {
191
+ sessionManager.appendModelChange(entry.provider, entry.modelId);
192
+ }
193
+ else if (entry.type === "custom") {
194
+ sessionManager.appendCustomEntry(entry.customType, entry.data);
195
+ }
196
+ else if (entry.type === "custom_message") {
197
+ sessionManager.appendCustomMessageEntry(entry.customType, entry.content, entry.display, entry.details);
198
+ }
199
+ else if (entry.type === "branch_summary") {
200
+ // Branch summaries reference specific entry IDs - skip to avoid inconsistency
201
+ continue;
202
+ }
203
+ else if (entry.type === "label") {
204
+ // Labels reference specific entry IDs - skip to avoid inconsistency
205
+ continue;
206
+ }
207
+ else if (entry.type === "session_info") {
208
+ if (entry.name) {
209
+ sessionManager.appendSessionInfo(entry.name);
210
+ }
211
+ }
212
+ }
213
+ log.info(`[tool-result-truncation] Truncated ${truncatedCount} tool result(s) in session ` +
214
+ `(contextWindow=${contextWindowTokens} maxChars=${maxChars}) ` +
215
+ `sessionKey=${params.sessionKey ?? params.sessionId ?? "unknown"}`);
216
+ return { truncated: true, truncatedCount };
217
+ }
218
+ catch (err) {
219
+ const errMsg = err instanceof Error ? err.message : String(err);
220
+ log.warn(`[tool-result-truncation] Failed to truncate: ${errMsg}`);
221
+ return { truncated: false, truncatedCount: 0, reason: errMsg };
222
+ }
223
+ }
224
+ /**
225
+ * Truncate oversized tool results in an array of messages (in-memory).
226
+ * Returns a new array with truncated messages.
227
+ *
228
+ * This is used as a pre-emptive guard before sending messages to the LLM,
229
+ * without modifying the session file.
230
+ */
231
+ export function truncateOversizedToolResultsInMessages(messages, contextWindowTokens) {
232
+ const maxChars = calculateMaxToolResultChars(contextWindowTokens);
233
+ let truncatedCount = 0;
234
+ const result = messages.map((msg) => {
235
+ if (msg.role !== "toolResult") {
236
+ return msg;
237
+ }
238
+ const textLength = getToolResultTextLength(msg);
239
+ if (textLength <= maxChars) {
240
+ return msg;
241
+ }
242
+ truncatedCount++;
243
+ return truncateToolResultMessage(msg, maxChars);
244
+ });
245
+ return { messages: result, truncatedCount };
246
+ }
247
+ /**
248
+ * Check if a tool result message exceeds the size limit for a given context window.
249
+ */
250
+ export function isOversizedToolResult(msg, contextWindowTokens) {
251
+ if (msg.role !== "toolResult") {
252
+ return false;
253
+ }
254
+ const maxChars = calculateMaxToolResultChars(contextWindowTokens);
255
+ return getToolResultTextLength(msg) > maxChars;
256
+ }
257
+ /**
258
+ * Estimate whether the session likely has oversized tool results that caused
259
+ * a context overflow. Used as a heuristic to decide whether to attempt
260
+ * tool result truncation before giving up.
261
+ */
262
+ export function sessionLikelyHasOversizedToolResults(params) {
263
+ const { messages, contextWindowTokens } = params;
264
+ const maxChars = calculateMaxToolResultChars(contextWindowTokens);
265
+ for (const msg of messages) {
266
+ if (msg.role !== "toolResult") {
267
+ continue;
268
+ }
269
+ const textLength = getToolResultTextLength(msg);
270
+ if (textLength > maxChars) {
271
+ return true;
272
+ }
273
+ }
274
+ return false;
275
+ }
@@ -1,5 +1,5 @@
1
1
  export function mapThinkingLevel(level) {
2
- // pi-agent-core supports "xhigh"; Moltbot enables it for specific models.
2
+ // pi-agent-core supports "xhigh"; Poolbot enables it for specific models.
3
3
  if (!level)
4
4
  return "off";
5
5
  return level;
@@ -6,7 +6,8 @@ import { buildCodeSpanIndex, createInlineCodeState } from "../markdown/code-span
6
6
  import { EmbeddedBlockChunker } from "./pi-embedded-block-chunker.js";
7
7
  import { isMessagingToolDuplicateNormalized, normalizeTextForComparison, } from "./pi-embedded-helpers.js";
8
8
  import { createEmbeddedPiSessionEventHandler } from "./pi-embedded-subscribe.handlers.js";
9
- import { formatReasoningMessage } from "./pi-embedded-utils.js";
9
+ import { formatReasoningMessage, stripDowngradedToolCallText } from "./pi-embedded-utils.js";
10
+ import { hasNonzeroUsage, normalizeUsage } from "./usage.js";
10
11
  const THINKING_TAG_SCAN_RE = /<\s*(\/?)\s*(?:think(?:ing)?|thought|antthinking)\s*>/gi;
11
12
  const FINAL_TAG_SCAN_RE = /<\s*(\/?)\s*final\s*>/gi;
12
13
  const log = createSubsystemLogger("agent/embedded");
@@ -29,7 +30,10 @@ export function subscribeEmbeddedPiSession(params) {
29
30
  blockBuffer: "",
30
31
  // Track if a streamed chunk opened a <think> block (stateful across chunks).
31
32
  blockState: { thinking: false, final: false, inlineCode: createInlineCodeState() },
33
+ partialBlockState: { thinking: false, final: false, inlineCode: createInlineCodeState() },
32
34
  lastStreamedAssistant: undefined,
35
+ lastStreamedAssistantCleaned: undefined,
36
+ emittedAssistantUpdate: false,
33
37
  lastStreamedReasoning: undefined,
34
38
  lastBlockReplyText: undefined,
35
39
  assistantMessageIndex: 0,
@@ -49,6 +53,14 @@ export function subscribeEmbeddedPiSession(params) {
49
53
  pendingMessagingTexts: new Map(),
50
54
  pendingMessagingTargets: new Map(),
51
55
  };
56
+ const usageTotals = {
57
+ input: 0,
58
+ output: 0,
59
+ cacheRead: 0,
60
+ cacheWrite: 0,
61
+ total: 0,
62
+ };
63
+ let compactionCount = 0;
52
64
  const assistantTexts = state.assistantTexts;
53
65
  const toolMetas = state.toolMetas;
54
66
  const toolMetaById = state.toolMetaById;
@@ -59,15 +71,22 @@ export function subscribeEmbeddedPiSession(params) {
59
71
  const pendingMessagingTexts = state.pendingMessagingTexts;
60
72
  const pendingMessagingTargets = state.pendingMessagingTargets;
61
73
  const replyDirectiveAccumulator = createStreamingDirectiveAccumulator();
74
+ const partialReplyDirectiveAccumulator = createStreamingDirectiveAccumulator();
62
75
  const resetAssistantMessageState = (nextAssistantTextBaseline) => {
63
76
  state.deltaBuffer = "";
64
77
  state.blockBuffer = "";
65
78
  blockChunker?.reset();
66
79
  replyDirectiveAccumulator.reset();
80
+ partialReplyDirectiveAccumulator.reset();
67
81
  state.blockState.thinking = false;
68
82
  state.blockState.final = false;
69
83
  state.blockState.inlineCode = createInlineCodeState();
84
+ state.partialBlockState.thinking = false;
85
+ state.partialBlockState.final = false;
86
+ state.partialBlockState.inlineCode = createInlineCodeState();
70
87
  state.lastStreamedAssistant = undefined;
88
+ state.lastStreamedAssistantCleaned = undefined;
89
+ state.emittedAssistantUpdate = false;
71
90
  state.lastBlockReplyText = undefined;
72
91
  state.lastStreamedReasoning = undefined;
73
92
  state.lastReasoningSent = undefined;
@@ -85,21 +104,26 @@ export function subscribeEmbeddedPiSession(params) {
85
104
  state.lastAssistantTextNormalized = normalized.length > 0 ? normalized : undefined;
86
105
  };
87
106
  const shouldSkipAssistantText = (text) => {
88
- if (state.lastAssistantTextMessageIndex !== state.assistantMessageIndex)
107
+ if (state.lastAssistantTextMessageIndex !== state.assistantMessageIndex) {
89
108
  return false;
109
+ }
90
110
  const trimmed = text.trimEnd();
91
- if (trimmed && trimmed === state.lastAssistantTextTrimmed)
111
+ if (trimmed && trimmed === state.lastAssistantTextTrimmed) {
92
112
  return true;
113
+ }
93
114
  const normalized = normalizeTextForComparison(text);
94
- if (normalized.length > 0 && normalized === state.lastAssistantTextNormalized)
115
+ if (normalized.length > 0 && normalized === state.lastAssistantTextNormalized) {
95
116
  return true;
117
+ }
96
118
  return false;
97
119
  };
98
120
  const pushAssistantText = (text) => {
99
- if (!text)
121
+ if (!text) {
100
122
  return;
101
- if (shouldSkipAssistantText(text))
123
+ }
124
+ if (shouldSkipAssistantText(text)) {
102
125
  return;
126
+ }
103
127
  assistantTexts.push(text);
104
128
  rememberAssistantText(text);
105
129
  };
@@ -154,8 +178,9 @@ export function subscribeEmbeddedPiSession(params) {
154
178
  ensureCompactionPromise();
155
179
  };
156
180
  const resolveCompactionRetry = () => {
157
- if (state.pendingCompactionRetry <= 0)
181
+ if (state.pendingCompactionRetry <= 0) {
158
182
  return;
183
+ }
159
184
  state.pendingCompactionRetry -= 1;
160
185
  if (state.pendingCompactionRetry === 0 && !state.compactionInFlight) {
161
186
  state.compactionRetryResolve?.();
@@ -170,6 +195,40 @@ export function subscribeEmbeddedPiSession(params) {
170
195
  state.compactionRetryPromise = null;
171
196
  }
172
197
  };
198
+ const recordAssistantUsage = (usageLike) => {
199
+ const usage = normalizeUsage((usageLike ?? undefined));
200
+ if (!hasNonzeroUsage(usage)) {
201
+ return;
202
+ }
203
+ usageTotals.input += usage.input ?? 0;
204
+ usageTotals.output += usage.output ?? 0;
205
+ usageTotals.cacheRead += usage.cacheRead ?? 0;
206
+ usageTotals.cacheWrite += usage.cacheWrite ?? 0;
207
+ const usageTotal = usage.total ??
208
+ (usage.input ?? 0) + (usage.output ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
209
+ usageTotals.total += usageTotal;
210
+ };
211
+ const getUsageTotals = () => {
212
+ const hasUsage = usageTotals.input > 0 ||
213
+ usageTotals.output > 0 ||
214
+ usageTotals.cacheRead > 0 ||
215
+ usageTotals.cacheWrite > 0 ||
216
+ usageTotals.total > 0;
217
+ if (!hasUsage) {
218
+ return undefined;
219
+ }
220
+ const derivedTotal = usageTotals.input + usageTotals.output + usageTotals.cacheRead + usageTotals.cacheWrite;
221
+ return {
222
+ input: usageTotals.input || undefined,
223
+ output: usageTotals.output || undefined,
224
+ cacheRead: usageTotals.cacheRead || undefined,
225
+ cacheWrite: usageTotals.cacheWrite || undefined,
226
+ total: usageTotals.total || derivedTotal || undefined,
227
+ };
228
+ };
229
+ const incrementCompactionCount = () => {
230
+ compactionCount += 1;
231
+ };
173
232
  const blockChunking = params.blockReplyChunking;
174
233
  const blockChunker = blockChunking ? new EmbeddedBlockChunker(blockChunking) : null;
175
234
  // KNOWN: Provider streams are not strictly once-only or perfectly ordered.
@@ -183,21 +242,25 @@ export function subscribeEmbeddedPiSession(params) {
183
242
  : params.verboseLevel === "full";
184
243
  const formatToolOutputBlock = (text) => {
185
244
  const trimmed = text.trim();
186
- if (!trimmed)
245
+ if (!trimmed) {
187
246
  return "(no output)";
188
- if (!useMarkdown)
247
+ }
248
+ if (!useMarkdown) {
189
249
  return trimmed;
250
+ }
190
251
  return `\`\`\`txt\n${trimmed}\n\`\`\``;
191
252
  };
192
253
  const emitToolSummary = (toolName, meta) => {
193
- if (!params.onToolResult)
254
+ if (!params.onToolResult) {
194
255
  return;
256
+ }
195
257
  const agg = formatToolAggregate(toolName, meta ? [meta] : undefined, {
196
258
  markdown: useMarkdown,
197
259
  });
198
260
  const { text: cleanedText, mediaUrls } = parseReplyDirectives(agg);
199
- if (!cleanedText && (!mediaUrls || mediaUrls.length === 0))
261
+ if (!cleanedText && (!mediaUrls || mediaUrls.length === 0)) {
200
262
  return;
263
+ }
201
264
  try {
202
265
  void params.onToolResult({
203
266
  text: cleanedText,
@@ -209,15 +272,17 @@ export function subscribeEmbeddedPiSession(params) {
209
272
  }
210
273
  };
211
274
  const emitToolOutput = (toolName, meta, output) => {
212
- if (!params.onToolResult || !output)
275
+ if (!params.onToolResult || !output) {
213
276
  return;
277
+ }
214
278
  const agg = formatToolAggregate(toolName, meta ? [meta] : undefined, {
215
279
  markdown: useMarkdown,
216
280
  });
217
281
  const message = `${agg}\n${formatToolOutputBlock(output)}`;
218
282
  const { text: cleanedText, mediaUrls } = parseReplyDirectives(message);
219
- if (!cleanedText && (!mediaUrls || mediaUrls.length === 0))
283
+ if (!cleanedText && (!mediaUrls || mediaUrls.length === 0)) {
220
284
  return;
285
+ }
221
286
  try {
222
287
  void params.onToolResult({
223
288
  text: cleanedText,
@@ -229,8 +294,9 @@ export function subscribeEmbeddedPiSession(params) {
229
294
  }
230
295
  };
231
296
  const stripBlockTags = (text, state) => {
232
- if (!text)
297
+ if (!text) {
233
298
  return text;
299
+ }
234
300
  const inlineStateStart = state.inlineCode ?? createInlineCodeState();
235
301
  const codeSpans = buildCodeSpanIndex(text, inlineStateStart);
236
302
  // 1. Handle <think> blocks (stateful, strip content inside)
@@ -240,8 +306,9 @@ export function subscribeEmbeddedPiSession(params) {
240
306
  let inThinking = state.thinking;
241
307
  for (const match of text.matchAll(THINKING_TAG_SCAN_RE)) {
242
308
  const idx = match.index ?? 0;
243
- if (codeSpans.isInside(idx))
309
+ if (codeSpans.isInside(idx)) {
244
310
  continue;
311
+ }
245
312
  if (!inThinking) {
246
313
  processed += text.slice(lastIndex, idx);
247
314
  }
@@ -271,8 +338,9 @@ export function subscribeEmbeddedPiSession(params) {
271
338
  let everInFinal = state.final;
272
339
  for (const match of processed.matchAll(FINAL_TAG_SCAN_RE)) {
273
340
  const idx = match.index ?? 0;
274
- if (finalCodeSpans.isInside(idx))
341
+ if (finalCodeSpans.isInside(idx)) {
275
342
  continue;
343
+ }
276
344
  const isClose = match[1] === "/";
277
345
  if (!inFinal && !isClose) {
278
346
  // Found <final> start tag.
@@ -309,8 +377,9 @@ export function subscribeEmbeddedPiSession(params) {
309
377
  pattern.lastIndex = 0;
310
378
  for (const match of text.matchAll(pattern)) {
311
379
  const idx = match.index ?? 0;
312
- if (isInside(idx))
380
+ if (isInside(idx)) {
313
381
  continue;
382
+ }
314
383
  output += text.slice(lastIndex, idx);
315
384
  lastIndex = idx + match[0].length;
316
385
  }
@@ -318,14 +387,18 @@ export function subscribeEmbeddedPiSession(params) {
318
387
  return output;
319
388
  };
320
389
  const emitBlockChunk = (text) => {
321
- if (state.suppressBlockChunks)
390
+ if (state.suppressBlockChunks) {
322
391
  return;
392
+ }
323
393
  // Strip <think> and <final> blocks across chunk boundaries to avoid leaking reasoning.
324
- const chunk = stripBlockTags(text, state.blockState).trimEnd();
325
- if (!chunk)
394
+ // Also strip downgraded tool call text ([Tool Call: ...], [Historical context: ...], etc.).
395
+ const chunk = stripDowngradedToolCallText(stripBlockTags(text, state.blockState)).trimEnd();
396
+ if (!chunk) {
326
397
  return;
327
- if (chunk === state.lastBlockReplyText)
398
+ }
399
+ if (chunk === state.lastBlockReplyText) {
328
400
  return;
401
+ }
329
402
  // Only check committed (successful) messaging tool texts - checking pending texts
330
403
  // is risky because if the tool fails after suppression, the user gets no response
331
404
  const normalizedChunk = normalizeTextForComparison(chunk);
@@ -333,20 +406,24 @@ export function subscribeEmbeddedPiSession(params) {
333
406
  log.debug(`Skipping block reply - already sent via messaging tool: ${chunk.slice(0, 50)}...`);
334
407
  return;
335
408
  }
336
- if (shouldSkipAssistantText(chunk))
409
+ if (shouldSkipAssistantText(chunk)) {
337
410
  return;
411
+ }
338
412
  state.lastBlockReplyText = chunk;
339
413
  assistantTexts.push(chunk);
340
414
  rememberAssistantText(chunk);
341
- if (!params.onBlockReply)
415
+ if (!params.onBlockReply) {
342
416
  return;
417
+ }
343
418
  const splitResult = replyDirectiveAccumulator.consume(chunk);
344
- if (!splitResult)
419
+ if (!splitResult) {
345
420
  return;
421
+ }
346
422
  const { text: cleanedText, mediaUrls, audioAsVoice, replyToId, replyToTag, replyToCurrent, } = splitResult;
347
423
  // Skip empty payloads, but always emit if audioAsVoice is set (to propagate the flag)
348
- if (!cleanedText && (!mediaUrls || mediaUrls.length === 0) && !audioAsVoice)
424
+ if (!cleanedText && (!mediaUrls || mediaUrls.length === 0) && !audioAsVoice) {
349
425
  return;
426
+ }
350
427
  void params.onBlockReply({
351
428
  text: cleanedText,
352
429
  mediaUrls: mediaUrls?.length ? mediaUrls : undefined,
@@ -357,9 +434,11 @@ export function subscribeEmbeddedPiSession(params) {
357
434
  });
358
435
  };
359
436
  const consumeReplyDirectives = (text, options) => replyDirectiveAccumulator.consume(text, options);
437
+ const consumePartialReplyDirectives = (text, options) => partialReplyDirectiveAccumulator.consume(text, options);
360
438
  const flushBlockReplyBuffer = () => {
361
- if (!params.onBlockReply)
439
+ if (!params.onBlockReply) {
362
440
  return;
441
+ }
363
442
  if (blockChunker?.hasBuffered()) {
364
443
  blockChunker.drain({ force: true, emit: emitBlockChunk });
365
444
  blockChunker.reset();
@@ -371,13 +450,16 @@ export function subscribeEmbeddedPiSession(params) {
371
450
  }
372
451
  };
373
452
  const emitReasoningStream = (text) => {
374
- if (!state.streamReasoning || !params.onReasoningStream)
453
+ if (!state.streamReasoning || !params.onReasoningStream) {
375
454
  return;
455
+ }
376
456
  const formatted = formatReasoningMessage(text);
377
- if (!formatted)
457
+ if (!formatted) {
378
458
  return;
379
- if (formatted === state.lastStreamedReasoning)
459
+ }
460
+ if (formatted === state.lastStreamedReasoning) {
380
461
  return;
462
+ }
381
463
  state.lastStreamedReasoning = formatted;
382
464
  void params.onReasoningStream({
383
465
  text: formatted,
@@ -411,6 +493,7 @@ export function subscribeEmbeddedPiSession(params) {
411
493
  flushBlockReplyBuffer,
412
494
  emitReasoningStream,
413
495
  consumeReplyDirectives,
496
+ consumePartialReplyDirectives,
414
497
  resetAssistantMessageState,
415
498
  resetForCompactionRetry,
416
499
  finalizeAssistantTexts,
@@ -419,6 +502,10 @@ export function subscribeEmbeddedPiSession(params) {
419
502
  noteCompactionRetry,
420
503
  resolveCompactionRetry,
421
504
  maybeResolveCompactionWait,
505
+ recordAssistantUsage,
506
+ incrementCompactionCount,
507
+ getUsageTotals,
508
+ getCompactionCount: () => compactionCount,
422
509
  };
423
510
  const unsubscribe = params.session.subscribe(createEmbeddedPiSessionEventHandler(ctx));
424
511
  return {
@@ -433,6 +520,8 @@ export function subscribeEmbeddedPiSession(params) {
433
520
  // which is generated AFTER the tool sends the actual answer.
434
521
  didSendViaMessagingTool: () => messagingToolSentTexts.length > 0,
435
522
  getLastToolError: () => (state.lastToolError ? { ...state.lastToolError } : undefined),
523
+ getUsageTotals,
524
+ getCompactionCount: () => compactionCount,
436
525
  waitForCompactionRetry: () => {
437
526
  if (state.compactionInFlight || state.pendingCompactionRetry > 0) {
438
527
  ensureCompactionPromise();
@@ -0,0 +1,10 @@
1
+ import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
2
+ import path from "node:path";
3
+ export { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
4
+ // Compatibility helpers for pi-coding-agent 0.50+ (discover* helpers removed).
5
+ export function discoverAuthStorage(agentDir) {
6
+ return new AuthStorage(path.join(agentDir, "auth.json"));
7
+ }
8
+ export function discoverModels(authStorage, agentDir) {
9
+ return new ModelRegistry(authStorage, path.join(agentDir, "models.json"));
10
+ }