@poolzin/pool-bot 2026.2.21 → 2026.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/agents/api-key-rotation.js +47 -0
  3. package/dist/agents/apply-patch-update.js +19 -9
  4. package/dist/agents/apply-patch.js +72 -47
  5. package/dist/agents/bash-tools.exec.js +141 -559
  6. package/dist/agents/cli-backends.js +49 -6
  7. package/dist/agents/cli-runner/helpers.js +69 -152
  8. package/dist/agents/cli-runner.js +70 -19
  9. package/dist/agents/identity.js +20 -1
  10. package/dist/agents/image-sanitization.js +9 -0
  11. package/dist/agents/live-auth-keys.js +123 -26
  12. package/dist/agents/live-model-filter.js +13 -4
  13. package/dist/agents/model-catalog.js +40 -9
  14. package/dist/agents/model-forward-compat.js +60 -23
  15. package/dist/agents/model-selection.js +134 -41
  16. package/dist/agents/pi-auth-json.js +2 -2
  17. package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
  18. package/dist/agents/pi-embedded-helpers/errors.js +140 -15
  19. package/dist/agents/pi-embedded-helpers/images.js +22 -12
  20. package/dist/agents/pi-embedded-helpers.js +2 -2
  21. package/dist/agents/pi-embedded-runner/abort.js +10 -3
  22. package/dist/agents/pi-embedded-runner/compact.js +230 -32
  23. package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
  24. package/dist/agents/pi-embedded-runner/google.js +109 -19
  25. package/dist/agents/pi-embedded-runner/history.js +35 -17
  26. package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
  27. package/dist/agents/pi-embedded-runner/run/images.js +81 -55
  28. package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
  29. package/dist/agents/pi-embedded-runner/run.js +193 -25
  30. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
  31. package/dist/agents/pi-embedded-runner/runs.js +17 -8
  32. package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
  33. package/dist/agents/pi-embedded-runner.js +1 -1
  34. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
  35. package/dist/agents/pi-embedded-subscribe.js +37 -0
  36. package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
  37. package/dist/agents/pi-model-discovery.js +9 -2
  38. package/dist/agents/pi-tool-definition-adapter.js +60 -8
  39. package/dist/agents/pi-tools.before-tool-call.js +1 -1
  40. package/dist/agents/pi-tools.js +113 -94
  41. package/dist/agents/pi-tools.read.js +337 -38
  42. package/dist/agents/poolbot-tools.js +14 -5
  43. package/dist/agents/sandbox/docker.js +10 -5
  44. package/dist/agents/sandbox/registry.js +96 -46
  45. package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
  46. package/dist/agents/sandbox-paths.js +43 -10
  47. package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
  48. package/dist/agents/session-tool-result-guard.js +39 -39
  49. package/dist/agents/session-transcript-repair.js +36 -33
  50. package/dist/agents/session-write-lock.js +62 -44
  51. package/dist/agents/skills/frontmatter.js +49 -88
  52. package/dist/agents/skills/workspace.js +335 -28
  53. package/dist/agents/subagent-announce.js +508 -174
  54. package/dist/agents/subagent-registry.js +45 -4
  55. package/dist/agents/subagent-spawn.js +16 -33
  56. package/dist/agents/system-prompt-report.js +27 -10
  57. package/dist/agents/system-prompt.js +26 -32
  58. package/dist/agents/tool-call-id.js +69 -17
  59. package/dist/agents/tool-display-common.js +1 -1
  60. package/dist/agents/tool-images.js +64 -31
  61. package/dist/agents/tools/canvas-tool.js +17 -11
  62. package/dist/agents/tools/common.js +37 -19
  63. package/dist/agents/tools/cron-tool.js +40 -38
  64. package/dist/agents/tools/gateway.js +70 -2
  65. package/dist/agents/tools/message-tool.js +181 -40
  66. package/dist/agents/tools/nodes-tool.js +128 -36
  67. package/dist/agents/tools/nodes-utils.js +12 -38
  68. package/dist/agents/tools/session-status-tool.js +24 -71
  69. package/dist/agents/tools/sessions-helpers.js +38 -210
  70. package/dist/agents/tools/sessions-spawn-tool.js +28 -198
  71. package/dist/agents/tools/telegram-actions.js +58 -7
  72. package/dist/agents/tools/web-fetch-utils.js +112 -7
  73. package/dist/agents/tools/web-fetch.js +279 -175
  74. package/dist/agents/tools/web-shared.js +71 -8
  75. package/dist/agents/usage.js +25 -16
  76. package/dist/auto-reply/commands-registry.data.js +85 -11
  77. package/dist/auto-reply/dispatch.js +40 -21
  78. package/dist/auto-reply/reply/abort.js +102 -33
  79. package/dist/auto-reply/reply/commands-core.js +82 -33
  80. package/dist/auto-reply/reply/commands-export-session.js +1 -1
  81. package/dist/auto-reply/reply/commands-info.js +41 -12
  82. package/dist/auto-reply/reply/commands-subagents.js +352 -100
  83. package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
  84. package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
  85. package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
  86. package/dist/auto-reply/reply/inbound-meta.js +12 -1
  87. package/dist/auto-reply/reply/mentions.js +18 -11
  88. package/dist/auto-reply/reply/normalize-reply.js +17 -8
  89. package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
  90. package/dist/auto-reply/reply/session.js +102 -21
  91. package/dist/auto-reply/reply/streaming-directives.js +16 -5
  92. package/dist/auto-reply/status.js +73 -50
  93. package/dist/browser/extension-relay.js +3 -3
  94. package/dist/browser/http-auth.js +1 -1
  95. package/dist/browser/paths.js +2 -2
  96. package/dist/build-info.json +3 -3
  97. package/dist/channels/allowlist-match.js +20 -0
  98. package/dist/channels/allowlists/resolve-utils.js +65 -2
  99. package/dist/channels/chat-type.js +8 -4
  100. package/dist/channels/dock.js +127 -35
  101. package/dist/channels/draft-stream-loop.js +6 -2
  102. package/dist/channels/plugins/actions/telegram.js +42 -18
  103. package/dist/channels/plugins/allowlist-match.js +1 -1
  104. package/dist/channels/plugins/group-mentions.js +51 -41
  105. package/dist/channels/plugins/message-action-names.js +2 -0
  106. package/dist/channels/plugins/message-actions.js +24 -5
  107. package/dist/channels/plugins/normalize/discord.js +26 -4
  108. package/dist/channels/plugins/normalize/signal.js +35 -22
  109. package/dist/channels/plugins/onboarding/helpers.js +8 -26
  110. package/dist/channels/plugins/outbound/imessage.js +15 -14
  111. package/dist/channels/registry.js +20 -7
  112. package/dist/cli/acp-cli.js +7 -5
  113. package/dist/cli/browser-cli-extension.js +25 -12
  114. package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
  115. package/dist/cli/browser-cli-state.js +101 -145
  116. package/dist/cli/command-options.js +28 -0
  117. package/dist/cli/completion-cli.js +6 -6
  118. package/dist/cli/cron-cli/register.cron-add.js +25 -1
  119. package/dist/cli/cron-cli/register.cron-edit.js +44 -0
  120. package/dist/cli/cron-cli/shared.js +7 -1
  121. package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
  122. package/dist/cli/daemon-cli/lifecycle.js +23 -247
  123. package/dist/cli/daemon-cli/register-service-commands.js +25 -4
  124. package/dist/cli/daemon-cli.js +1 -0
  125. package/dist/cli/devices-cli.js +33 -20
  126. package/dist/cli/gateway-cli/register.js +37 -105
  127. package/dist/cli/gateway-cli/run.js +49 -11
  128. package/dist/cli/nodes-camera.js +59 -4
  129. package/dist/cli/nodes-cli/register.camera.js +27 -24
  130. package/dist/cli/nodes-cli/rpc.js +21 -38
  131. package/dist/cli/qr-cli.js +2 -2
  132. package/dist/cli/skills-cli.format.js +2 -2
  133. package/dist/cli/update-cli/progress.js +2 -2
  134. package/dist/cli/update-cli/restart-helper.js +28 -7
  135. package/dist/cli/update-cli/shared.js +7 -7
  136. package/dist/cli/update-cli/status.js +1 -1
  137. package/dist/cli/update-cli/update-command.js +14 -8
  138. package/dist/cli/update-cli/wizard.js +2 -2
  139. package/dist/cli/update-cli.js +21 -1027
  140. package/dist/commands/auth-choice.apply.anthropic.js +10 -2
  141. package/dist/commands/channels/add-mutators.js +3 -35
  142. package/dist/commands/channels/add.js +39 -51
  143. package/dist/commands/config-validation.js +1 -1
  144. package/dist/commands/configure.gateway-auth.js +52 -15
  145. package/dist/commands/configure.gateway.js +84 -40
  146. package/dist/commands/doctor-completion.js +3 -3
  147. package/dist/commands/doctor-config-flow.js +536 -16
  148. package/dist/commands/doctor-gateway-services.js +103 -79
  149. package/dist/commands/doctor-memory-search.js +9 -9
  150. package/dist/commands/doctor-platform-notes.js +57 -30
  151. package/dist/commands/doctor-prompter.js +26 -15
  152. package/dist/commands/doctor-session-locks.js +1 -1
  153. package/dist/commands/doctor.js +21 -9
  154. package/dist/commands/model-picker.js +120 -95
  155. package/dist/commands/models/set.js +2 -21
  156. package/dist/commands/models/shared.js +65 -37
  157. package/dist/commands/onboard-helpers.js +81 -39
  158. package/dist/commands/openai-codex-oauth.js +1 -1
  159. package/dist/commands/sessions.js +52 -53
  160. package/dist/commands/status.summary.js +52 -34
  161. package/dist/commands/test-wizard-helpers.js +2 -2
  162. package/dist/config/defaults.js +79 -42
  163. package/dist/config/group-policy.js +50 -18
  164. package/dist/config/includes.js +37 -10
  165. package/dist/config/schema.help.js +5 -4
  166. package/dist/config/schema.hints.js +2 -2
  167. package/dist/config/schema.labels.js +1 -0
  168. package/dist/config/sessions/group.js +12 -11
  169. package/dist/config/sessions/paths.js +137 -11
  170. package/dist/config/sessions/store.js +185 -65
  171. package/dist/config/sessions/types.js +15 -1
  172. package/dist/config/sessions.js +1 -0
  173. package/dist/config/telegram-custom-commands.js +3 -2
  174. package/dist/config/types.js +2 -0
  175. package/dist/config/zod-schema.agent-defaults.js +6 -27
  176. package/dist/config/zod-schema.agent-runtime.js +171 -79
  177. package/dist/config/zod-schema.providers-core.js +138 -65
  178. package/dist/config/zod-schema.session.js +49 -22
  179. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  180. package/dist/cron/isolated-agent/run.js +224 -57
  181. package/dist/cron/normalize.js +48 -45
  182. package/dist/cron/run-log.js +14 -0
  183. package/dist/cron/service/jobs.js +190 -28
  184. package/dist/cron/service/normalize.js +29 -11
  185. package/dist/cron/service/store.js +30 -44
  186. package/dist/cron/service/timer.js +182 -96
  187. package/dist/cron/service.js +3 -0
  188. package/dist/cron/stagger.js +37 -0
  189. package/dist/daemon/inspect.js +132 -92
  190. package/dist/daemon/runtime-paths.js +25 -4
  191. package/dist/daemon/service-audit.js +47 -16
  192. package/dist/discord/accounts.js +23 -20
  193. package/dist/discord/monitor/agent-components.js +1115 -219
  194. package/dist/discord/monitor/allow-list.js +114 -34
  195. package/dist/discord/monitor/listeners.js +204 -97
  196. package/dist/discord/monitor/message-handler.js +21 -10
  197. package/dist/discord/monitor/message-handler.preflight.js +195 -101
  198. package/dist/discord/monitor/message-handler.process.js +384 -123
  199. package/dist/discord/monitor/message-utils.js +86 -23
  200. package/dist/discord/monitor/native-command.js +77 -57
  201. package/dist/discord/monitor/provider.js +122 -117
  202. package/dist/discord/monitor/reply-context.js +20 -16
  203. package/dist/discord/monitor/reply-delivery.js +40 -8
  204. package/dist/discord/monitor/rest-fetch.js +22 -0
  205. package/dist/discord/monitor/threading.js +117 -24
  206. package/dist/discord/send.js +2 -1
  207. package/dist/discord/send.outbound.js +124 -11
  208. package/dist/discord/send.shared.js +112 -72
  209. package/dist/discord/voice-message.js +3 -3
  210. package/dist/gateway/auth.js +119 -44
  211. package/dist/gateway/call.js +76 -34
  212. package/dist/gateway/channel-health-monitor.js +57 -50
  213. package/dist/gateway/client.js +63 -29
  214. package/dist/gateway/control-ui-contract.js +1 -1
  215. package/dist/gateway/gateway-config-prompts.shared.js +2 -2
  216. package/dist/gateway/net.js +109 -1
  217. package/dist/gateway/protocol/index.js +5 -8
  218. package/dist/gateway/protocol/schema/agent.js +19 -1
  219. package/dist/gateway/protocol/schema/channels.js +21 -0
  220. package/dist/gateway/protocol/schema/cron.js +43 -30
  221. package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
  222. package/dist/gateway/protocol/schema/sessions.js +5 -1
  223. package/dist/gateway/protocol/schema.js +0 -1
  224. package/dist/gateway/server/presence-events.js +12 -0
  225. package/dist/gateway/server/ws-connection/message-handler.js +203 -212
  226. package/dist/gateway/server/ws-connection.js +58 -21
  227. package/dist/gateway/server-broadcast.js +18 -13
  228. package/dist/gateway/server-cron.js +177 -10
  229. package/dist/gateway/server-methods/agent-job.js +131 -38
  230. package/dist/gateway/server-methods/send.js +60 -14
  231. package/dist/gateway/server-methods/sessions.js +160 -96
  232. package/dist/gateway/server-methods/system.js +5 -7
  233. package/dist/gateway/server-methods-list.js +8 -0
  234. package/dist/gateway/server-methods.js +24 -8
  235. package/dist/gateway/server-node-events.js +278 -68
  236. package/dist/gateway/session-utils.fs.js +316 -75
  237. package/dist/gateway/session-utils.js +224 -70
  238. package/dist/gateway/sessions-patch.js +63 -20
  239. package/dist/gateway/test-temp-config.js +1 -1
  240. package/dist/gateway/tools-invoke-http.js +118 -70
  241. package/dist/gateway/ws-log.js +135 -107
  242. package/dist/hooks/frontmatter.js +36 -82
  243. package/dist/hooks/install.js +149 -139
  244. package/dist/hooks/internal-hooks.js +29 -4
  245. package/dist/hooks/plugin-hooks.js +2 -1
  246. package/dist/imessage/monitor/deliver.js +10 -4
  247. package/dist/imessage/monitor/monitor-provider.js +138 -375
  248. package/dist/imessage/monitor/runtime.js +4 -8
  249. package/dist/imessage/send.js +65 -19
  250. package/dist/infra/exec-approvals-allowlist.js +7 -0
  251. package/dist/infra/exec-approvals.js +35 -920
  252. package/dist/infra/exec-safe-bin-trust.js +64 -0
  253. package/dist/infra/heartbeat-runner.js +207 -134
  254. package/dist/infra/heartbeat-wake.js +183 -22
  255. package/dist/infra/install-source-utils.js +47 -0
  256. package/dist/infra/net/ssrf.js +170 -36
  257. package/dist/infra/outbound/deliver.js +224 -58
  258. package/dist/infra/outbound/message-action-spec.js +12 -5
  259. package/dist/infra/outbound/outbound-session.js +27 -25
  260. package/dist/infra/poolbot-root.js +32 -22
  261. package/dist/infra/ports.js +14 -11
  262. package/dist/infra/skills-remote.js +48 -37
  263. package/dist/infra/system-events.js +25 -11
  264. package/dist/infra/system-presence.js +26 -33
  265. package/dist/infra/tmp-poolbot-dir.js +81 -2
  266. package/dist/infra/wsl.js +37 -1
  267. package/dist/line/bot-message-context.js +163 -191
  268. package/dist/logging/subsystem.js +59 -22
  269. package/dist/markdown/ir.js +124 -50
  270. package/dist/media/store.js +1 -1
  271. package/dist/media-understanding/runner.entries.js +42 -25
  272. package/dist/media-understanding/runner.js +53 -488
  273. package/dist/memory/embeddings-gemini.js +53 -38
  274. package/dist/memory/manager-embedding-ops.js +48 -69
  275. package/dist/pairing/pairing-store.js +178 -119
  276. package/dist/plugin-sdk/index.js +34 -6
  277. package/dist/plugins/hooks.js +135 -14
  278. package/dist/plugins/install.js +190 -152
  279. package/dist/polls.js +11 -0
  280. package/dist/routing/resolve-route.js +190 -56
  281. package/dist/routing/session-key.js +38 -22
  282. package/dist/runtime.js +35 -9
  283. package/dist/security/audit-channel.js +1 -1
  284. package/dist/sessions/session-key-utils.js +29 -11
  285. package/dist/shared/frontmatter.js +5 -5
  286. package/dist/shared/node-list-types.js +1 -0
  287. package/dist/shared/string-normalization.js +15 -0
  288. package/dist/signal/monitor/event-handler.js +68 -36
  289. package/dist/signal/send.js +29 -37
  290. package/dist/slack/monitor/allow-list.js +10 -11
  291. package/dist/slack/monitor/commands.js +14 -3
  292. package/dist/slack/monitor/events/interactions.js +4 -4
  293. package/dist/slack/monitor/media.js +224 -16
  294. package/dist/slack/monitor/message-handler/dispatch.js +247 -13
  295. package/dist/slack/monitor/message-handler/prepare.js +128 -45
  296. package/dist/slack/monitor/slash.js +357 -144
  297. package/dist/slack/streaming.js +77 -0
  298. package/dist/telegram/accounts.js +40 -13
  299. package/dist/telegram/allowed-updates.js +3 -0
  300. package/dist/telegram/bot/delivery.js +129 -66
  301. package/dist/telegram/bot/helpers.js +136 -122
  302. package/dist/telegram/bot-handlers.js +600 -339
  303. package/dist/telegram/bot-message-context.js +115 -73
  304. package/dist/telegram/bot-message-dispatch.js +235 -104
  305. package/dist/telegram/bot-native-command-menu.js +3 -1
  306. package/dist/telegram/bot-native-commands.js +213 -193
  307. package/dist/telegram/bot.js +24 -132
  308. package/dist/telegram/draft-stream.js +84 -75
  309. package/dist/telegram/format.js +150 -6
  310. package/dist/telegram/send.js +415 -255
  311. package/dist/telegram/targets.js +21 -2
  312. package/dist/telegram/update-offset-store.js +19 -3
  313. package/dist/terminal/restore.js +5 -2
  314. package/dist/test-utils/fetch-mock.js +5 -0
  315. package/dist/version.js +18 -5
  316. package/dist/web/auto-reply/monitor/broadcast.js +7 -3
  317. package/dist/web/auto-reply/monitor/on-message.js +6 -3
  318. package/dist/web/inbound/media.js +34 -8
  319. package/dist/web/inbound/monitor.js +34 -17
  320. package/dist/web/inbound/send-api.js +18 -17
  321. package/dist/web/outbound.js +12 -5
  322. package/dist/wizard/clack-prompter.js +40 -7
  323. package/extensions/bluebubbles/package.json +1 -1
  324. package/extensions/copilot-proxy/package.json +1 -1
  325. package/extensions/device-pair/index.ts +2 -2
  326. package/extensions/diagnostics-otel/package.json +1 -1
  327. package/extensions/discord/package.json +1 -1
  328. package/extensions/feishu/package.json +1 -1
  329. package/extensions/google-antigravity-auth/package.json +1 -1
  330. package/extensions/google-gemini-cli-auth/package.json +1 -1
  331. package/extensions/googlechat/package.json +1 -1
  332. package/extensions/imessage/package.json +1 -1
  333. package/extensions/irc/package.json +1 -1
  334. package/extensions/irc/src/accounts.ts +1 -1
  335. package/extensions/irc/src/onboarding.ts +4 -4
  336. package/extensions/line/package.json +1 -1
  337. package/extensions/llm-task/package.json +1 -1
  338. package/extensions/lobster/package.json +1 -1
  339. package/extensions/matrix/CHANGELOG.md +10 -0
  340. package/extensions/matrix/package.json +1 -1
  341. package/extensions/mattermost/package.json +1 -1
  342. package/extensions/memory-core/package.json +1 -1
  343. package/extensions/memory-lancedb/package.json +1 -1
  344. package/extensions/minimax-portal-auth/package.json +1 -1
  345. package/extensions/msteams/CHANGELOG.md +10 -0
  346. package/extensions/msteams/package.json +1 -1
  347. package/extensions/nextcloud-talk/package.json +1 -1
  348. package/extensions/nostr/CHANGELOG.md +10 -0
  349. package/extensions/nostr/package.json +1 -1
  350. package/extensions/open-prose/package.json +1 -1
  351. package/extensions/openai-codex-auth/package.json +1 -1
  352. package/extensions/signal/package.json +1 -1
  353. package/extensions/slack/package.json +1 -1
  354. package/extensions/telegram/package.json +1 -1
  355. package/extensions/tlon/package.json +1 -1
  356. package/extensions/twitch/CHANGELOG.md +10 -0
  357. package/extensions/twitch/package.json +1 -1
  358. package/extensions/voice-call/CHANGELOG.md +10 -0
  359. package/extensions/voice-call/package.json +1 -1
  360. package/extensions/whatsapp/package.json +1 -1
  361. package/extensions/zalo/CHANGELOG.md +10 -0
  362. package/extensions/zalo/package.json +1 -1
  363. package/extensions/zalouser/CHANGELOG.md +10 -0
  364. package/extensions/zalouser/package.json +1 -1
  365. package/package.json +1 -1
  366. package/skills/apple-reminders/SKILL.md +100 -49
  367. package/skills/coding-agent/SKILL.md +34 -28
  368. package/skills/github/SKILL.md +131 -16
  369. package/skills/imsg/SKILL.md +112 -15
  370. package/skills/openhue/SKILL.md +101 -19
  371. package/skills/tmux/SKILL.md +111 -79
  372. package/skills/weather/SKILL.md +88 -25
  373. package/dist/agents/openclaw-tools.js +0 -151
  374. package/dist/agents/tool-security.js +0 -96
  375. package/dist/gateway/url-validation.js +0 -94
  376. package/dist/infra/openclaw-root.js +0 -109
  377. package/dist/infra/tmp-openclaw-dir.js +0 -81
  378. package/dist/media/path-sanitization.js +0 -78
@@ -1,6 +1,7 @@
1
1
  import { loadConfig } from "../config/config.js";
2
2
  import { callGateway } from "../gateway/call.js";
3
3
  import { onAgentEvent } from "../infra/agent-events.js";
4
+ import { defaultRuntime } from "../runtime.js";
4
5
  import { normalizeDeliveryContext } from "../utils/delivery-context.js";
5
6
  import { resetAnnounceQueuesForTests } from "./subagent-announce-queue.js";
6
7
  import { runSubagentAnnounceFlow } from "./subagent-announce.js";
@@ -13,6 +14,8 @@ let listenerStop = null;
13
14
  // Use var to avoid TDZ when init runs across circular imports during bootstrap.
14
15
  var restoreAttempted = false;
15
16
  const SUBAGENT_ANNOUNCE_TIMEOUT_MS = 120_000;
17
+ const MIN_ANNOUNCE_RETRY_DELAY_MS = 1_000;
18
+ const MAX_ANNOUNCE_RETRY_DELAY_MS = 8_000;
16
19
  /**
17
20
  * Maximum number of announce delivery attempts before giving up.
18
21
  * Prevents infinite retry loops when `runSubagentAnnounceFlow` repeatedly
@@ -24,7 +27,19 @@ const MAX_ANNOUNCE_RETRY_COUNT = 3;
24
27
  * succeeded. Guards against stale registry entries surviving gateway restarts.
25
28
  */
26
29
  const ANNOUNCE_EXPIRY_MS = 5 * 60_000; // 5 minutes
27
- // (Backoff constant removed — max-retry + expiry guards are sufficient.)
30
+ function resolveAnnounceRetryDelayMs(retryCount) {
31
+ const boundedRetryCount = Math.max(0, Math.min(retryCount, 10));
32
+ // retryCount is "attempts already made", so retry #1 waits 1s, then 2s, 4s...
33
+ const backoffExponent = Math.max(0, boundedRetryCount - 1);
34
+ const baseDelay = MIN_ANNOUNCE_RETRY_DELAY_MS * 2 ** backoffExponent;
35
+ return Math.min(baseDelay, MAX_ANNOUNCE_RETRY_DELAY_MS);
36
+ }
37
+ function logAnnounceGiveUp(entry, reason) {
38
+ const retryCount = entry.announceRetryCount ?? 0;
39
+ const endedAgoMs = typeof entry.endedAt === "number" ? Math.max(0, Date.now() - entry.endedAt) : undefined;
40
+ const endedAgoLabel = endedAgoMs != null ? `${Math.round(endedAgoMs / 1000)}s` : "n/a";
41
+ defaultRuntime.log(`[warn] Subagent announce give up (${reason}) run=${entry.runId} child=${entry.childSessionKey} requester=${entry.requesterSessionKey} retries=${retryCount} endedAgo=${endedAgoLabel}`);
42
+ }
28
43
  function persistSubagentRuns() {
29
44
  try {
30
45
  saveSubagentRegistryToDisk(subagentRuns);
@@ -49,6 +64,7 @@ function startSubagentAnnounceCleanupFlow(runId, entry) {
49
64
  requesterOrigin,
50
65
  requesterDisplayKey: entry.requesterDisplayKey,
51
66
  task: entry.task,
67
+ expectsCompletionMessage: entry.expectsCompletionMessage,
52
68
  timeoutMs: SUBAGENT_ANNOUNCE_TIMEOUT_MS,
53
69
  cleanup: entry.cleanup,
54
70
  waitForCompletion: false,
@@ -74,15 +90,30 @@ function resumeSubagentRun(runId) {
74
90
  }
75
91
  // Skip entries that have exhausted their retry budget or expired (#18264).
76
92
  if ((entry.announceRetryCount ?? 0) >= MAX_ANNOUNCE_RETRY_COUNT) {
93
+ logAnnounceGiveUp(entry, "retry-limit");
77
94
  entry.cleanupCompletedAt = Date.now();
78
95
  persistSubagentRuns();
79
96
  return;
80
97
  }
81
98
  if (typeof entry.endedAt === "number" && Date.now() - entry.endedAt > ANNOUNCE_EXPIRY_MS) {
99
+ logAnnounceGiveUp(entry, "expiry");
82
100
  entry.cleanupCompletedAt = Date.now();
83
101
  persistSubagentRuns();
84
102
  return;
85
103
  }
104
+ const now = Date.now();
105
+ const delayMs = resolveAnnounceRetryDelayMs(entry.announceRetryCount ?? 0);
106
+ const earliestRetryAt = (entry.lastAnnounceRetryAt ?? 0) + delayMs;
107
+ if (entry.expectsCompletionMessage === true &&
108
+ entry.lastAnnounceRetryAt &&
109
+ now < earliestRetryAt) {
110
+ const waitMs = Math.max(1, earliestRetryAt - now);
111
+ setTimeout(() => {
112
+ resumeSubagentRun(runId);
113
+ }, waitMs).unref?.();
114
+ resumedRuns.add(runId);
115
+ return;
116
+ }
86
117
  if (typeof entry.endedAt === "number" && entry.endedAt > 0) {
87
118
  if (suppressAnnounceForSteerRestart(entry)) {
88
119
  resumedRuns.add(runId);
@@ -238,14 +269,16 @@ function finalizeSubagentCleanup(runId, cleanup, didAnnounce) {
238
269
  return;
239
270
  }
240
271
  if (!didAnnounce) {
272
+ const now = Date.now();
241
273
  const retryCount = (entry.announceRetryCount ?? 0) + 1;
242
274
  entry.announceRetryCount = retryCount;
243
- entry.lastAnnounceRetryAt = Date.now();
275
+ entry.lastAnnounceRetryAt = now;
244
276
  // Check if the announce has exceeded retry limits or expired (#18264).
245
- const endedAgo = typeof entry.endedAt === "number" ? Date.now() - entry.endedAt : 0;
277
+ const endedAgo = typeof entry.endedAt === "number" ? now - entry.endedAt : 0;
246
278
  if (retryCount >= MAX_ANNOUNCE_RETRY_COUNT || endedAgo > ANNOUNCE_EXPIRY_MS) {
247
279
  // Give up: mark as completed to break the infinite retry loop.
248
- entry.cleanupCompletedAt = Date.now();
280
+ logAnnounceGiveUp(entry, retryCount >= MAX_ANNOUNCE_RETRY_COUNT ? "retry-limit" : "expiry");
281
+ entry.cleanupCompletedAt = now;
249
282
  persistSubagentRuns();
250
283
  retryDeferredCompletedAnnounces(runId);
251
284
  return;
@@ -254,6 +287,12 @@ function finalizeSubagentCleanup(runId, cleanup, didAnnounce) {
254
287
  entry.cleanupHandled = false;
255
288
  resumedRuns.delete(runId);
256
289
  persistSubagentRuns();
290
+ if (entry.expectsCompletionMessage !== true) {
291
+ return;
292
+ }
293
+ setTimeout(() => {
294
+ resumeSubagentRun(runId);
295
+ }, resolveAnnounceRetryDelayMs(entry.announceRetryCount ?? 0)).unref?.();
257
296
  return;
258
297
  }
259
298
  if (cleanup === "delete") {
@@ -284,6 +323,7 @@ function retryDeferredCompletedAnnounces(excludeRunId) {
284
323
  // Force-expire announces that have been pending too long (#18264).
285
324
  const endedAgo = now - (entry.endedAt ?? now);
286
325
  if (endedAgo > ANNOUNCE_EXPIRY_MS) {
326
+ logAnnounceGiveUp(entry, "expiry");
287
327
  entry.cleanupCompletedAt = now;
288
328
  persistSubagentRuns();
289
329
  continue;
@@ -405,6 +445,7 @@ export function registerSubagentRun(params) {
405
445
  requesterDisplayKey: params.requesterDisplayKey,
406
446
  task: params.task,
407
447
  cleanup: params.cleanup,
448
+ expectsCompletionMessage: params.expectsCompletionMessage,
408
449
  label: params.label,
409
450
  model: params.model,
410
451
  runTimeoutSeconds,
@@ -6,12 +6,13 @@ import { normalizeAgentId, parseAgentSessionKey } from "../routing/session-key.j
6
6
  import { normalizeDeliveryContext } from "../utils/delivery-context.js";
7
7
  import { resolveAgentConfig } from "./agent-scope.js";
8
8
  import { AGENT_LANE_SUBAGENT } from "./lanes.js";
9
- import { resolveDefaultModelForAgent } from "./model-selection.js";
9
+ import { resolveSubagentSpawnModelSelection } from "./model-selection.js";
10
10
  import { buildSubagentSystemPrompt } from "./subagent-announce.js";
11
11
  import { getSubagentDepthFromSessionStore } from "./subagent-depth.js";
12
12
  import { countActiveRunsForSession, registerSubagentRun } from "./subagent-registry.js";
13
13
  import { readStringParam } from "./tools/common.js";
14
14
  import { resolveDisplaySessionKey, resolveInternalSessionKey, resolveMainSessionAlias, } from "./tools/sessions-helpers.js";
15
+ export const SUBAGENT_SPAWN_ACCEPTED_NOTE = "auto-announces on completion, do not poll/sleep. The response will be sent back as an agent message.";
15
16
  export function splitModelRef(ref) {
16
17
  if (!ref) {
17
18
  return { provider: undefined, model: undefined };
@@ -26,20 +27,6 @@ export function splitModelRef(ref) {
26
27
  }
27
28
  return { provider: undefined, model: trimmed };
28
29
  }
29
- export function normalizeModelSelection(value) {
30
- if (typeof value === "string") {
31
- const trimmed = value.trim();
32
- return trimmed || undefined;
33
- }
34
- if (!value || typeof value !== "object") {
35
- return undefined;
36
- }
37
- const primary = value.primary;
38
- if (typeof primary === "string" && primary.trim()) {
39
- return primary.trim();
40
- }
41
- return undefined;
42
- }
43
30
  export async function spawnSubagentDirect(params, ctx) {
44
31
  const task = params.task;
45
32
  const label = params.label?.trim() || "";
@@ -56,7 +43,6 @@ export async function spawnSubagentDirect(params, ctx) {
56
43
  const runTimeoutSeconds = typeof params.runTimeoutSeconds === "number" && Number.isFinite(params.runTimeoutSeconds)
57
44
  ? Math.max(0, Math.floor(params.runTimeoutSeconds))
58
45
  : 0;
59
- let modelWarning;
60
46
  let modelApplied = false;
61
47
  const cfg = loadConfig();
62
48
  const { mainKey, alias } = resolveMainSessionAlias(cfg);
@@ -110,15 +96,11 @@ export async function spawnSubagentDirect(params, ctx) {
110
96
  const childDepth = callerDepth + 1;
111
97
  const spawnedByKey = requesterInternalKey;
112
98
  const targetAgentConfig = resolveAgentConfig(cfg, targetAgentId);
113
- const runtimeDefaultModel = resolveDefaultModelForAgent({
99
+ const resolvedModel = resolveSubagentSpawnModelSelection({
114
100
  cfg,
115
101
  agentId: targetAgentId,
102
+ modelOverride,
116
103
  });
117
- const resolvedModel = normalizeModelSelection(modelOverride) ??
118
- normalizeModelSelection(targetAgentConfig?.subagents?.model) ??
119
- normalizeModelSelection(cfg.agents?.defaults?.subagents?.model) ??
120
- normalizeModelSelection(cfg.agents?.defaults?.model?.primary) ??
121
- normalizeModelSelection(`${runtimeDefaultModel.provider}/${runtimeDefaultModel.model}`);
122
104
  const resolvedThinkingDefaultRaw = readStringParam(targetAgentConfig?.subagents ?? {}, "thinking") ??
123
105
  readStringParam(cfg.agents?.defaults?.subagents ?? {}, "thinking");
124
106
  let thinkingOverride;
@@ -161,15 +143,11 @@ export async function spawnSubagentDirect(params, ctx) {
161
143
  }
162
144
  catch (err) {
163
145
  const messageText = err instanceof Error ? err.message : typeof err === "string" ? err : "error";
164
- const recoverable = messageText.includes("invalid model") || messageText.includes("model not allowed");
165
- if (!recoverable) {
166
- return {
167
- status: "error",
168
- error: messageText,
169
- childSessionKey,
170
- };
171
- }
172
- modelWarning = messageText;
146
+ return {
147
+ status: "error",
148
+ error: messageText,
149
+ childSessionKey,
150
+ };
173
151
  }
174
152
  }
175
153
  if (thinkingOverride !== undefined) {
@@ -201,13 +179,17 @@ export async function spawnSubagentDirect(params, ctx) {
201
179
  childDepth,
202
180
  maxSpawnDepth,
203
181
  });
182
+ const childTaskMessage = [
183
+ `[Subagent Context] You are running as a subagent (depth ${childDepth}/${maxSpawnDepth}). Results auto-announce to your requester; do not busy-poll for status.`,
184
+ `[Subagent Task]: ${task}`,
185
+ ].join("\n\n");
204
186
  const childIdem = crypto.randomUUID();
205
187
  let childRunId = childIdem;
206
188
  try {
207
189
  const response = await callGateway({
208
190
  method: "agent",
209
191
  params: {
210
- message: task,
192
+ message: childTaskMessage,
211
193
  sessionKey: childSessionKey,
212
194
  channel: requesterOrigin?.channel,
213
195
  to: requesterOrigin?.to ?? undefined,
@@ -251,12 +233,13 @@ export async function spawnSubagentDirect(params, ctx) {
251
233
  label: label || undefined,
252
234
  model: resolvedModel,
253
235
  runTimeoutSeconds,
236
+ expectsCompletionMessage: params.expectsCompletionMessage === true,
254
237
  });
255
238
  return {
256
239
  status: "accepted",
257
240
  childSessionKey,
258
241
  runId: childRunId,
242
+ note: SUBAGENT_SPAWN_ACCEPTED_NOTE,
259
243
  modelApplied: resolvedModel ? modelApplied : undefined,
260
- warning: modelWarning,
261
244
  };
262
245
  }
@@ -1,16 +1,20 @@
1
+ import path from "node:path";
1
2
  function extractBetween(input, startMarker, endMarker) {
2
3
  const start = input.indexOf(startMarker);
3
- if (start === -1)
4
+ if (start === -1) {
4
5
  return { text: "", found: false };
6
+ }
5
7
  const end = input.indexOf(endMarker, start + startMarker.length);
6
- if (end === -1)
8
+ if (end === -1) {
7
9
  return { text: input.slice(start), found: true };
10
+ }
8
11
  return { text: input.slice(start, end), found: true };
9
12
  }
10
13
  function parseSkillBlocks(skillsPrompt) {
11
14
  const prompt = skillsPrompt.trim();
12
- if (!prompt)
15
+ if (!prompt) {
13
16
  return [];
17
+ }
14
18
  const blocks = Array.from(prompt.matchAll(/<skill>[\s\S]*?<\/skill>/gi)).map((match) => match[0] ?? "");
15
19
  return blocks
16
20
  .map((block) => {
@@ -20,12 +24,22 @@ function parseSkillBlocks(skillsPrompt) {
20
24
  .filter((b) => b.blockChars > 0);
21
25
  }
22
26
  function buildInjectedWorkspaceFiles(params) {
23
- const injectedByName = new Map(params.injectedFiles.map((f) => [f.path, f.content]));
27
+ const injectedByPath = new Map(params.injectedFiles.map((f) => [f.path, f.content]));
28
+ const injectedByBaseName = new Map();
29
+ for (const file of params.injectedFiles) {
30
+ const normalizedPath = file.path.replace(/\\/g, "/");
31
+ const baseName = path.posix.basename(normalizedPath);
32
+ if (!injectedByBaseName.has(baseName)) {
33
+ injectedByBaseName.set(baseName, file.content);
34
+ }
35
+ }
24
36
  return params.bootstrapFiles.map((file) => {
25
37
  const rawChars = file.missing ? 0 : (file.content ?? "").trimEnd().length;
26
- const injected = injectedByName.get(file.name);
38
+ const injected = injectedByPath.get(file.path) ??
39
+ injectedByPath.get(file.name) ??
40
+ injectedByBaseName.get(file.name);
27
41
  const injectedChars = injected ? injected.length : 0;
28
- const truncated = !file.missing && rawChars > params.bootstrapMaxChars;
42
+ const truncated = !file.missing && injectedChars < rawChars;
29
43
  return {
30
44
  name: file.name,
31
45
  path: file.path,
@@ -42,8 +56,9 @@ function buildToolsEntries(tools) {
42
56
  const summary = tool.description?.trim() || tool.label?.trim() || "";
43
57
  const summaryChars = summary.length;
44
58
  const schemaChars = (() => {
45
- if (!tool.parameters || typeof tool.parameters !== "object")
59
+ if (!tool.parameters || typeof tool.parameters !== "object") {
46
60
  return 0;
61
+ }
47
62
  try {
48
63
  return JSON.stringify(tool.parameters).length;
49
64
  }
@@ -56,8 +71,9 @@ function buildToolsEntries(tools) {
56
71
  ? tool.parameters
57
72
  : null;
58
73
  const props = schema && typeof schema.properties === "object" ? schema.properties : null;
59
- if (!props || typeof props !== "object")
74
+ if (!props || typeof props !== "object") {
60
75
  return null;
76
+ }
61
77
  return Object.keys(props).length;
62
78
  })();
63
79
  return { name, summaryChars, schemaChars, propertiesCount };
@@ -67,8 +83,9 @@ function extractToolListText(systemPrompt) {
67
83
  const markerA = "Tool names are case-sensitive. Call tools exactly as listed.\n";
68
84
  const markerB = "\nTOOLS.md does not control tool availability; it is user guidance for how to use external tools.";
69
85
  const extracted = extractBetween(systemPrompt, markerA, markerB);
70
- if (!extracted.found)
86
+ if (!extracted.found) {
71
87
  return "";
88
+ }
72
89
  return extracted.text.replace(markerA, "").trim();
73
90
  }
74
91
  export function buildSystemPromptReport(params) {
@@ -89,6 +106,7 @@ export function buildSystemPromptReport(params) {
89
106
  model: params.model,
90
107
  workspaceDir: params.workspaceDir,
91
108
  bootstrapMaxChars: params.bootstrapMaxChars,
109
+ bootstrapTotalMaxChars: params.bootstrapTotalMaxChars,
92
110
  sandbox: params.sandbox,
93
111
  systemPrompt: {
94
112
  chars: systemPrompt.length,
@@ -98,7 +116,6 @@ export function buildSystemPromptReport(params) {
98
116
  injectedWorkspaceFiles: buildInjectedWorkspaceFiles({
99
117
  bootstrapFiles: params.bootstrapFiles,
100
118
  injectedFiles: params.injectedFiles,
101
- bootstrapMaxChars: params.bootstrapMaxChars,
102
119
  }),
103
120
  skills: {
104
121
  promptChars: params.skillsPrompt.length,
@@ -2,11 +2,13 @@ import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js";
2
2
  import { listDeliverableMessageChannels } from "../utils/message-channel.js";
3
3
  import { sanitizeForPromptLiteral } from "./sanitize-for-prompt.js";
4
4
  function buildSkillsSection(params) {
5
- if (params.isMinimal)
5
+ if (params.isMinimal) {
6
6
  return [];
7
+ }
7
8
  const trimmed = params.skillsPrompt?.trim();
8
- if (!trimmed)
9
+ if (!trimmed) {
9
10
  return [];
11
+ }
10
12
  return [
11
13
  "## Skills (mandatory)",
12
14
  "Before replying: scan <available_skills> <description> entries.",
@@ -19,8 +21,9 @@ function buildSkillsSection(params) {
19
21
  ];
20
22
  }
21
23
  function buildMemorySection(params) {
22
- if (params.isMinimal)
24
+ if (params.isMinimal) {
23
25
  return [];
26
+ }
24
27
  if (!params.availableTools.has("memory_search") && !params.availableTools.has("memory_get")) {
25
28
  return [];
26
29
  }
@@ -38,21 +41,25 @@ function buildMemorySection(params) {
38
41
  return lines;
39
42
  }
40
43
  function buildUserIdentitySection(ownerLine, isMinimal) {
41
- if (!ownerLine || isMinimal)
44
+ if (!ownerLine || isMinimal) {
42
45
  return [];
46
+ }
43
47
  return ["## User Identity", ownerLine, ""];
44
48
  }
45
49
  function buildTimeSection(params) {
46
- if (!params.userTimezone)
50
+ if (!params.userTimezone) {
47
51
  return [];
52
+ }
48
53
  return ["## Current Date & Time", `Time zone: ${params.userTimezone}`, ""];
49
54
  }
50
55
  function buildReplyTagsSection(isMinimal) {
51
- if (isMinimal)
56
+ if (isMinimal) {
52
57
  return [];
58
+ }
53
59
  return [
54
60
  "## Reply Tags",
55
61
  "To request a native reply/quote on supported surfaces, include one tag in your reply:",
62
+ "- Reply tags must be the very first token in the message (no leading text/newlines): [[reply_to_current]] your reply.",
56
63
  "- [[reply_to_current]] replies to the triggering message.",
57
64
  "- Prefer [[reply_to_current]]. Use [[reply_to:<id>]] only when an id was explicitly provided (e.g. by the user or a tool).",
58
65
  "Whitespace inside the tag is allowed (e.g. [[ reply_to_current ]] / [[ reply_to: 123 ]]).",
@@ -61,8 +68,9 @@ function buildReplyTagsSection(isMinimal) {
61
68
  ];
62
69
  }
63
70
  function buildMessagingSection(params) {
64
- if (params.isMinimal)
71
+ if (params.isMinimal) {
65
72
  return [];
73
+ }
66
74
  return [
67
75
  "## Messaging",
68
76
  "- Reply in current session → automatically routes to the source channel (Signal, Telegram, etc.)",
@@ -93,40 +101,27 @@ function buildMessagingSection(params) {
93
101
  ];
94
102
  }
95
103
  function buildVoiceSection(params) {
96
- if (params.isMinimal)
97
- return [];
98
- const hint = params.ttsHint?.trim();
99
- if (!hint)
100
- return [];
101
- return ["## Voice (TTS)", hint, ""];
102
- }
103
- function buildLlmsTxtSection(params) {
104
104
  if (params.isMinimal) {
105
105
  return [];
106
106
  }
107
- if (!params.availableTools.has("web_fetch")) {
107
+ const hint = params.ttsHint?.trim();
108
+ if (!hint) {
108
109
  return [];
109
110
  }
110
- return [
111
- "## llms.txt Discovery",
112
- "When exploring a new domain or website (via web_fetch or browser), check for an llms.txt file that describes how AI agents should interact with the site:",
113
- "- Try `/llms.txt` or `/.well-known/llms.txt` at the domain root",
114
- "- If found, follow its guidance for interacting with that site's content and APIs",
115
- "- llms.txt is an emerging standard (like robots.txt for AI) — not all sites have one, so don't warn if missing",
116
- "",
117
- ];
111
+ return ["## Voice (TTS)", hint, ""];
118
112
  }
119
113
  function buildDocsSection(params) {
120
114
  const docsPath = params.docsPath?.trim();
121
- if (!docsPath || params.isMinimal)
115
+ if (!docsPath || params.isMinimal) {
122
116
  return [];
117
+ }
123
118
  return [
124
119
  "## Documentation",
125
120
  `Pool Bot docs: ${docsPath}`,
126
121
  "Mirror: https://docs.poolbot.dev",
127
- "Source: https://github.com/poolzin/pool-bot",
128
- "Community: https://discord.com/discord.gg/poolbot",
129
- "Find new skills: https://poolhub.com",
122
+ "Source: https://github.com/poolbot/poolbot",
123
+ "Community: https://discord.com/invite/clawd",
124
+ "Find new skills: https://clawhub.com",
130
125
  "For Pool Bot behavior, commands, config, or architecture: consult local docs first.",
131
126
  "When diagnosing issues, run `poolbot status` yourself when possible; only ask the user if you lack access (e.g., sandboxed).",
132
127
  "",
@@ -183,7 +178,6 @@ export function buildAgentSystemPrompt(params) {
183
178
  "sessions_list",
184
179
  "sessions_history",
185
180
  "sessions_send",
186
- "sessions_spawn",
187
181
  "subagents",
188
182
  "session_status",
189
183
  "image",
@@ -204,8 +198,9 @@ export function buildAgentSystemPrompt(params) {
204
198
  const externalToolSummaries = new Map();
205
199
  for (const [key, value] of Object.entries(params.toolSummaries ?? {})) {
206
200
  const normalized = key.trim().toLowerCase();
207
- if (!normalized || !value?.trim())
201
+ if (!normalized || !value?.trim()) {
208
202
  continue;
203
+ }
209
204
  externalToolSummaries.set(normalized, value.trim());
210
205
  }
211
206
  const extraTools = Array.from(new Set(normalizedTools.filter((tool) => !toolOrder.includes(tool))));
@@ -215,7 +210,7 @@ export function buildAgentSystemPrompt(params) {
215
210
  const name = resolveToolName(tool);
216
211
  return summary ? `- ${name}: ${summary}` : `- ${name}`;
217
212
  });
218
- for (const tool of extraTools.sort()) {
213
+ for (const tool of extraTools.toSorted()) {
219
214
  const summary = coreToolSummaries[tool] ?? externalToolSummaries.get(tool);
220
215
  const name = resolveToolName(tool);
221
216
  toolLines.push(summary ? `- ${name}: ${summary}` : `- ${name}`);
@@ -437,7 +432,6 @@ export function buildAgentSystemPrompt(params) {
437
432
  messageToolHints: params.messageToolHints,
438
433
  }),
439
434
  ...buildVoiceSection({ isMinimal, ttsHint: params.ttsHint }),
440
- ...buildLlmsTxtSection({ isMinimal, availableTools }),
441
435
  ];
442
436
  if (extraSystemPrompt) {
443
437
  // Use "Subagent Context" header for minimal mode (subagents), otherwise "Group Chat Context"
@@ -1,5 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
2
  const STRICT9_LEN = 9;
3
+ const TOOL_CALL_TYPES = new Set(["toolCall", "toolUse", "functionCall"]);
3
4
  /**
4
5
  * Sanitize a tool call ID to be compatible with various providers.
5
6
  *
@@ -8,25 +9,63 @@ const STRICT9_LEN = 9;
8
9
  */
9
10
  export function sanitizeToolCallId(id, mode = "strict") {
10
11
  if (!id || typeof id !== "string") {
11
- if (mode === "strict9")
12
+ if (mode === "strict9") {
12
13
  return "defaultid";
14
+ }
13
15
  return "defaulttoolid";
14
16
  }
15
17
  if (mode === "strict9") {
16
18
  const alphanumericOnly = id.replace(/[^a-zA-Z0-9]/g, "");
17
- if (alphanumericOnly.length >= STRICT9_LEN)
19
+ if (alphanumericOnly.length >= STRICT9_LEN) {
18
20
  return alphanumericOnly.slice(0, STRICT9_LEN);
19
- if (alphanumericOnly.length > 0)
21
+ }
22
+ if (alphanumericOnly.length > 0) {
20
23
  return shortHash(alphanumericOnly, STRICT9_LEN);
24
+ }
21
25
  return shortHash("sanitized", STRICT9_LEN);
22
26
  }
23
27
  // Some providers require strictly alphanumeric tool call IDs.
24
28
  const alphanumericOnly = id.replace(/[^a-zA-Z0-9]/g, "");
25
29
  return alphanumericOnly.length > 0 ? alphanumericOnly : "sanitizedtoolid";
26
30
  }
31
+ export function extractToolCallsFromAssistant(msg) {
32
+ const content = msg.content;
33
+ if (!Array.isArray(content)) {
34
+ return [];
35
+ }
36
+ const toolCalls = [];
37
+ for (const block of content) {
38
+ if (!block || typeof block !== "object") {
39
+ continue;
40
+ }
41
+ const rec = block;
42
+ if (typeof rec.id !== "string" || !rec.id) {
43
+ continue;
44
+ }
45
+ if (typeof rec.type === "string" && TOOL_CALL_TYPES.has(rec.type)) {
46
+ toolCalls.push({
47
+ id: rec.id,
48
+ name: typeof rec.name === "string" ? rec.name : undefined,
49
+ });
50
+ }
51
+ }
52
+ return toolCalls;
53
+ }
54
+ export function extractToolResultId(msg) {
55
+ const toolCallId = msg.toolCallId;
56
+ if (typeof toolCallId === "string" && toolCallId) {
57
+ return toolCallId;
58
+ }
59
+ const toolUseId = msg.toolUseId;
60
+ if (typeof toolUseId === "string" && toolUseId) {
61
+ return toolUseId;
62
+ }
63
+ return null;
64
+ }
27
65
  export function isValidCloudCodeAssistToolId(id, mode = "strict") {
28
- if (!id || typeof id !== "string")
66
+ if (!id || typeof id !== "string") {
29
67
  return false;
68
+ }
30
69
  if (mode === "strict9") {
31
70
  return /^[a-zA-Z0-9]{9}$/.test(id);
32
71
  }
@@ -40,44 +79,51 @@ function makeUniqueToolId(params) {
40
79
  if (params.mode === "strict9") {
41
80
  const base = sanitizeToolCallId(params.id, params.mode);
42
81
  const candidate = base.length >= STRICT9_LEN ? base.slice(0, STRICT9_LEN) : "";
43
- if (candidate && !params.used.has(candidate))
82
+ if (candidate && !params.used.has(candidate)) {
44
83
  return candidate;
84
+ }
45
85
  for (let i = 0; i < 1000; i += 1) {
46
86
  const hashed = shortHash(`${params.id}:${i}`, STRICT9_LEN);
47
- if (!params.used.has(hashed))
87
+ if (!params.used.has(hashed)) {
48
88
  return hashed;
89
+ }
49
90
  }
50
91
  return shortHash(`${params.id}:${Date.now()}`, STRICT9_LEN);
51
92
  }
52
93
  const MAX_LEN = 40;
53
94
  const base = sanitizeToolCallId(params.id, params.mode).slice(0, MAX_LEN);
54
- if (!params.used.has(base))
95
+ if (!params.used.has(base)) {
55
96
  return base;
97
+ }
56
98
  const hash = shortHash(params.id);
57
99
  // Use separator based on mode: none for strict, underscore for non-strict variants
58
100
  const separator = params.mode === "strict" ? "" : "_";
59
101
  const maxBaseLen = MAX_LEN - separator.length - hash.length;
60
102
  const clippedBase = base.length > maxBaseLen ? base.slice(0, maxBaseLen) : base;
61
103
  const candidate = `${clippedBase}${separator}${hash}`;
62
- if (!params.used.has(candidate))
104
+ if (!params.used.has(candidate)) {
63
105
  return candidate;
106
+ }
64
107
  for (let i = 2; i < 1000; i += 1) {
65
108
  const suffix = params.mode === "strict" ? `x${i}` : `_${i}`;
66
109
  const next = `${candidate.slice(0, MAX_LEN - suffix.length)}${suffix}`;
67
- if (!params.used.has(next))
110
+ if (!params.used.has(next)) {
68
111
  return next;
112
+ }
69
113
  }
70
114
  const ts = params.mode === "strict" ? `t${Date.now()}` : `_${Date.now()}`;
71
115
  return `${candidate.slice(0, MAX_LEN - ts.length)}${ts}`;
72
116
  }
73
117
  function rewriteAssistantToolCallIds(params) {
74
118
  const content = params.message.content;
75
- if (!Array.isArray(content))
119
+ if (!Array.isArray(content)) {
76
120
  return params.message;
121
+ }
77
122
  let changed = false;
78
123
  const next = content.map((block) => {
79
- if (!block || typeof block !== "object")
124
+ if (!block || typeof block !== "object") {
80
125
  return block;
126
+ }
81
127
  const rec = block;
82
128
  const type = rec.type;
83
129
  const id = rec.id;
@@ -87,13 +133,15 @@ function rewriteAssistantToolCallIds(params) {
87
133
  return block;
88
134
  }
89
135
  const nextId = params.resolve(id);
90
- if (nextId === id)
136
+ if (nextId === id) {
91
137
  return block;
138
+ }
92
139
  changed = true;
93
140
  return { ...block, id: nextId };
94
141
  });
95
- if (!changed)
142
+ if (!changed) {
96
143
  return params.message;
144
+ }
97
145
  return { ...params.message, content: next };
98
146
  }
99
147
  function rewriteToolResultIds(params) {
@@ -128,8 +176,9 @@ export function sanitizeToolCallIdsForCloudCodeAssist(messages, mode = "strict")
128
176
  const used = new Set();
129
177
  const resolve = (id) => {
130
178
  const existing = map.get(id);
131
- if (existing)
179
+ if (existing) {
132
180
  return existing;
181
+ }
133
182
  const next = makeUniqueToolId({ id, used, mode });
134
183
  map.set(id, next);
135
184
  used.add(next);
@@ -137,16 +186,18 @@ export function sanitizeToolCallIdsForCloudCodeAssist(messages, mode = "strict")
137
186
  };
138
187
  let changed = false;
139
188
  const out = messages.map((msg) => {
140
- if (!msg || typeof msg !== "object")
189
+ if (!msg || typeof msg !== "object") {
141
190
  return msg;
191
+ }
142
192
  const role = msg.role;
143
193
  if (role === "assistant") {
144
194
  const next = rewriteAssistantToolCallIds({
145
195
  message: msg,
146
196
  resolve,
147
197
  });
148
- if (next !== msg)
198
+ if (next !== msg) {
149
199
  changed = true;
200
+ }
150
201
  return next;
151
202
  }
152
203
  if (role === "toolResult") {
@@ -154,8 +205,9 @@ export function sanitizeToolCallIdsForCloudCodeAssist(messages, mode = "strict")
154
205
  message: msg,
155
206
  resolve,
156
207
  });
157
- if (next !== msg)
208
+ if (next !== msg) {
158
209
  changed = true;
210
+ }
159
211
  return next;
160
212
  }
161
213
  return msg;