@poolzin/pool-bot 2026.2.0 → 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 (230) hide show
  1. package/dist/agents/bash-tools.exec.js +76 -25
  2. package/dist/agents/cli-runner/helpers.js +9 -11
  3. package/dist/agents/identity.js +47 -7
  4. package/dist/agents/memory-search.js +25 -8
  5. package/dist/agents/model-selection.js +21 -0
  6. package/dist/agents/pi-embedded-block-chunker.js +117 -42
  7. package/dist/agents/pi-embedded-helpers/errors.js +183 -78
  8. package/dist/agents/pi-embedded-helpers.js +1 -1
  9. package/dist/agents/pi-embedded-runner/compact.js +1 -0
  10. package/dist/agents/pi-embedded-runner/model.js +61 -2
  11. package/dist/agents/pi-embedded-runner/run/attempt.js +21 -11
  12. package/dist/agents/pi-embedded-runner/run.js +199 -46
  13. package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
  14. package/dist/agents/pi-embedded-subscribe.js +118 -29
  15. package/dist/agents/pi-tools.js +10 -5
  16. package/dist/agents/poolbot-tools.js +15 -10
  17. package/dist/agents/sandbox-paths.js +31 -0
  18. package/dist/agents/session-tool-result-guard.js +94 -15
  19. package/dist/agents/shell-utils.js +51 -0
  20. package/dist/agents/skills/bundled-context.js +23 -0
  21. package/dist/agents/skills/bundled-dir.js +41 -7
  22. package/dist/agents/skills-install.js +60 -23
  23. package/dist/agents/subagent-announce.js +79 -34
  24. package/dist/agents/tool-policy.conformance.js +14 -0
  25. package/dist/agents/tool-policy.js +24 -0
  26. package/dist/agents/tools/cron-tool.js +166 -19
  27. package/dist/agents/tools/discord-actions-presence.js +78 -0
  28. package/dist/agents/tools/message-tool.js +56 -2
  29. package/dist/agents/tools/sessions-history-tool.js +69 -1
  30. package/dist/agents/tools/web-search.js +211 -42
  31. package/dist/agents/usage.js +23 -1
  32. package/dist/agents/workspace-run.js +67 -0
  33. package/dist/agents/workspace-templates.js +44 -0
  34. package/dist/auto-reply/command-auth.js +121 -6
  35. package/dist/auto-reply/envelope.js +50 -72
  36. package/dist/auto-reply/reply/commands-compact.js +1 -0
  37. package/dist/auto-reply/reply/commands-context-report.js +1 -0
  38. package/dist/auto-reply/reply/commands-context.js +1 -0
  39. package/dist/auto-reply/reply/commands-models.js +107 -60
  40. package/dist/auto-reply/reply/commands-ptt.js +171 -0
  41. package/dist/auto-reply/reply/get-reply-run.js +2 -1
  42. package/dist/auto-reply/reply/inbound-context.js +5 -1
  43. package/dist/auto-reply/reply/model-selection.js +3 -3
  44. package/dist/auto-reply/thinking.js +88 -43
  45. package/dist/browser/bridge-server.js +13 -0
  46. package/dist/browser/cdp.helpers.js +38 -24
  47. package/dist/browser/client-fetch.js +50 -7
  48. package/dist/browser/config.js +1 -10
  49. package/dist/browser/extension-relay.js +101 -40
  50. package/dist/browser/pw-ai.js +1 -1
  51. package/dist/browser/pw-session.js +143 -8
  52. package/dist/browser/pw-tools-core.interactions.js +125 -27
  53. package/dist/browser/pw-tools-core.responses.js +1 -1
  54. package/dist/browser/pw-tools-core.state.js +1 -1
  55. package/dist/browser/routes/agent.act.js +86 -41
  56. package/dist/browser/routes/dispatcher.js +4 -4
  57. package/dist/browser/screenshot.js +1 -1
  58. package/dist/browser/server.js +13 -0
  59. package/dist/build-info.json +3 -3
  60. package/dist/channels/reply-prefix.js +8 -1
  61. package/dist/cli/cron-cli/register.cron-add.js +61 -40
  62. package/dist/cli/cron-cli/register.cron-edit.js +60 -34
  63. package/dist/cli/cron-cli/shared.js +56 -41
  64. package/dist/cli/dns-cli.js +26 -14
  65. package/dist/cli/gateway-cli/register.js +37 -19
  66. package/dist/cli/memory-cli.js +5 -5
  67. package/dist/cli/parse-bytes.js +37 -0
  68. package/dist/cli/update-cli.js +173 -52
  69. package/dist/commands/agent.js +1 -0
  70. package/dist/commands/doctor-config-flow.js +61 -5
  71. package/dist/commands/doctor-state-migrations.js +1 -1
  72. package/dist/commands/health.js +1 -1
  73. package/dist/commands/model-allowlist.js +29 -0
  74. package/dist/commands/model-picker.js +2 -1
  75. package/dist/commands/models/list.status-command.js +43 -23
  76. package/dist/commands/models/shared.js +15 -0
  77. package/dist/commands/onboard-custom.js +384 -0
  78. package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +35 -0
  79. package/dist/commands/onboard-non-interactive/local/auth-choice.js +6 -3
  80. package/dist/commands/onboard-skills.js +63 -38
  81. package/dist/commands/openai-model-default.js +41 -0
  82. package/dist/config/defaults.js +3 -2
  83. package/dist/config/paths.js +136 -35
  84. package/dist/config/plugin-auto-enable.js +21 -5
  85. package/dist/config/redact-snapshot.js +153 -0
  86. package/dist/config/schema.field-metadata.js +590 -0
  87. package/dist/config/schema.js +2 -2
  88. package/dist/config/sessions/store.js +291 -23
  89. package/dist/config/zod-schema.agent-defaults.js +3 -0
  90. package/dist/config/zod-schema.agent-runtime.js +13 -2
  91. package/dist/config/zod-schema.providers-core.js +142 -0
  92. package/dist/config/zod-schema.session.js +3 -0
  93. package/dist/cron/delivery.js +57 -0
  94. package/dist/cron/isolated-agent/delivery-target.js +18 -3
  95. package/dist/cron/isolated-agent/helpers.js +22 -5
  96. package/dist/cron/isolated-agent/run.js +171 -63
  97. package/dist/cron/isolated-agent/session.js +2 -0
  98. package/dist/cron/normalize.js +356 -28
  99. package/dist/cron/parse.js +10 -5
  100. package/dist/cron/run-log.js +35 -10
  101. package/dist/cron/schedule.js +41 -6
  102. package/dist/cron/service/jobs.js +208 -35
  103. package/dist/cron/service/ops.js +72 -16
  104. package/dist/cron/service/state.js +2 -0
  105. package/dist/cron/service/store.js +386 -14
  106. package/dist/cron/service/timer.js +390 -147
  107. package/dist/cron/session-reaper.js +86 -0
  108. package/dist/cron/store.js +23 -8
  109. package/dist/cron/validate-timestamp.js +43 -0
  110. package/dist/discord/monitor/agent-components.js +438 -0
  111. package/dist/discord/monitor/allow-list.js +28 -5
  112. package/dist/discord/monitor/gateway-registry.js +29 -0
  113. package/dist/discord/monitor/native-command.js +44 -23
  114. package/dist/discord/monitor/sender-identity.js +45 -0
  115. package/dist/discord/pluralkit.js +27 -0
  116. package/dist/discord/send.outbound.js +92 -5
  117. package/dist/discord/send.shared.js +60 -23
  118. package/dist/discord/targets.js +84 -1
  119. package/dist/entry.js +15 -9
  120. package/dist/extensionAPI.js +8 -0
  121. package/dist/gateway/control-ui.js +8 -1
  122. package/dist/gateway/hooks-mapping.js +3 -0
  123. package/dist/gateway/hooks.js +65 -0
  124. package/dist/gateway/net.js +96 -31
  125. package/dist/gateway/node-command-policy.js +50 -15
  126. package/dist/gateway/origin-check.js +56 -0
  127. package/dist/gateway/protocol/client-info.js +9 -0
  128. package/dist/gateway/protocol/index.js +9 -2
  129. package/dist/gateway/protocol/schema/agents-models-skills.js +71 -1
  130. package/dist/gateway/protocol/schema/cron.js +22 -10
  131. package/dist/gateway/protocol/schema/protocol-schemas.js +16 -2
  132. package/dist/gateway/protocol/schema/sessions.js +12 -0
  133. package/dist/gateway/server/hooks.js +1 -1
  134. package/dist/gateway/server-broadcast.js +26 -9
  135. package/dist/gateway/server-chat.js +112 -23
  136. package/dist/gateway/server-discovery-runtime.js +10 -2
  137. package/dist/gateway/server-http.js +109 -11
  138. package/dist/gateway/server-methods/agent-timestamp.js +60 -0
  139. package/dist/gateway/server-methods/agents.js +321 -2
  140. package/dist/gateway/server-methods/usage.js +559 -16
  141. package/dist/gateway/server-runtime-state.js +22 -8
  142. package/dist/gateway/server-startup-memory.js +16 -0
  143. package/dist/gateway/server.impl.js +5 -1
  144. package/dist/gateway/session-utils.fs.js +23 -25
  145. package/dist/gateway/session-utils.js +20 -10
  146. package/dist/gateway/sessions-patch.js +7 -22
  147. package/dist/gateway/test-helpers.server.js +35 -2
  148. package/dist/imessage/constants.js +2 -0
  149. package/dist/imessage/monitor/deliver.js +4 -1
  150. package/dist/imessage/monitor/monitor-provider.js +51 -1
  151. package/dist/infra/bonjour-discovery.js +131 -70
  152. package/dist/infra/control-ui-assets.js +134 -12
  153. package/dist/infra/errors.js +12 -0
  154. package/dist/infra/exec-approvals.js +266 -57
  155. package/dist/infra/format-time/format-datetime.js +79 -0
  156. package/dist/infra/format-time/format-duration.js +81 -0
  157. package/dist/infra/format-time/format-relative.js +80 -0
  158. package/dist/infra/heartbeat-runner.js +140 -49
  159. package/dist/infra/home-dir.js +54 -0
  160. package/dist/infra/net/fetch-guard.js +122 -0
  161. package/dist/infra/net/ssrf.js +65 -29
  162. package/dist/infra/outbound/abort.js +14 -0
  163. package/dist/infra/outbound/message-action-runner.js +77 -13
  164. package/dist/infra/outbound/outbound-session.js +143 -37
  165. package/dist/infra/poolbot-root.js +43 -1
  166. package/dist/infra/session-cost-usage.js +631 -41
  167. package/dist/infra/state-migrations.js +317 -47
  168. package/dist/infra/update-global.js +35 -0
  169. package/dist/infra/update-runner.js +149 -43
  170. package/dist/infra/warning-filter.js +65 -0
  171. package/dist/infra/widearea-dns.js +30 -9
  172. package/dist/logging/redact-identifier.js +12 -0
  173. package/dist/media/fetch.js +81 -58
  174. package/dist/media-understanding/apply.js +403 -3
  175. package/dist/media-understanding/attachments.js +38 -27
  176. package/dist/media-understanding/defaults.js +16 -0
  177. package/dist/media-understanding/providers/deepgram/audio.js +22 -14
  178. package/dist/media-understanding/providers/google/audio.js +24 -17
  179. package/dist/media-understanding/providers/google/video.js +24 -17
  180. package/dist/media-understanding/providers/image.js +2 -2
  181. package/dist/media-understanding/providers/index.js +4 -1
  182. package/dist/media-understanding/providers/openai/audio.js +22 -14
  183. package/dist/media-understanding/providers/shared.js +16 -11
  184. package/dist/media-understanding/providers/zai/index.js +6 -0
  185. package/dist/media-understanding/runner.js +158 -90
  186. package/dist/memory/batch-voyage.js +277 -0
  187. package/dist/memory/embeddings-voyage.js +75 -0
  188. package/dist/memory/embeddings.js +28 -16
  189. package/dist/memory/internal.js +101 -18
  190. package/dist/memory/manager.js +154 -48
  191. package/dist/memory/search-manager.js +173 -0
  192. package/dist/memory/session-files.js +9 -3
  193. package/dist/node-host/runner.js +34 -24
  194. package/dist/node-host/with-timeout.js +27 -0
  195. package/dist/plugins/commands.js +5 -1
  196. package/dist/plugins/config-state.js +86 -7
  197. package/dist/plugins/source-display.js +51 -0
  198. package/dist/process/exec.js +20 -2
  199. package/dist/routing/resolve-route.js +12 -0
  200. package/dist/routing/session-key.js +15 -0
  201. package/dist/runtime.js +2 -0
  202. package/dist/security/audit-extra.async.js +601 -0
  203. package/dist/security/audit-extra.js +2 -830
  204. package/dist/security/audit-extra.sync.js +505 -0
  205. package/dist/security/channel-metadata.js +34 -0
  206. package/dist/security/external-content.js +88 -6
  207. package/dist/security/skill-scanner.js +330 -0
  208. package/dist/sessions/session-key-utils.js +7 -0
  209. package/dist/signal/monitor/event-handler.js +80 -1
  210. package/dist/slack/monitor/media.js +85 -15
  211. package/dist/tailscale/detect.js +1 -2
  212. package/dist/telegram/bot/helpers.js +109 -28
  213. package/dist/telegram/bot-handlers.js +144 -3
  214. package/dist/telegram/bot-message-context.js +37 -10
  215. package/dist/telegram/bot-message-dispatch.js +48 -15
  216. package/dist/telegram/bot-native-commands.js +86 -29
  217. package/dist/telegram/bot.js +30 -29
  218. package/dist/telegram/model-buttons.js +163 -0
  219. package/dist/telegram/monitor.js +110 -85
  220. package/dist/telegram/send.js +129 -47
  221. package/dist/terminal/restore.js +45 -0
  222. package/dist/test-helpers/state-dir-env.js +16 -0
  223. package/dist/tts/tts.js +12 -6
  224. package/dist/tui/tui-session-actions.js +166 -54
  225. package/dist/utils/fetch-timeout.js +20 -0
  226. package/dist/utils/normalize-secret-input.js +19 -0
  227. package/dist/utils/transcript-tools.js +58 -0
  228. package/dist/utils.js +45 -14
  229. package/dist/version.js +42 -5
  230. package/package.json +1 -1
@@ -1,7 +1,9 @@
1
1
  import { formatSandboxToolPolicyBlockedMessage } from "../sandbox.js";
2
+ export const BILLING_ERROR_USER_MESSAGE = "⚠️ API provider returned a billing error — your API key has run out of credits or has an insufficient balance. Check your provider's billing dashboard and top up or switch to a different API key.";
2
3
  export function isContextOverflowError(errorMessage) {
3
- if (!errorMessage)
4
+ if (!errorMessage) {
4
5
  return false;
6
+ }
5
7
  const lower = errorMessage.toLowerCase();
6
8
  const hasRequestSizeExceeds = lower.includes("request size exceeds");
7
9
  const hasContextWindow = lower.includes("context window") ||
@@ -14,34 +16,43 @@ export function isContextOverflowError(errorMessage) {
14
16
  lower.includes("prompt is too long") ||
15
17
  lower.includes("exceeds model context window") ||
16
18
  (hasRequestSizeExceeds && hasContextWindow) ||
17
- lower.includes("context overflow") ||
19
+ lower.includes("context overflow:") ||
18
20
  (lower.includes("413") && lower.includes("too large")));
19
21
  }
20
22
  const CONTEXT_WINDOW_TOO_SMALL_RE = /context window.*(too small|minimum is)/i;
21
23
  const CONTEXT_OVERFLOW_HINT_RE = /context.*overflow|context window.*(too (?:large|long)|exceed|over|limit|max(?:imum)?|requested|sent|tokens)|(?:prompt|request|input).*(too (?:large|long)|exceed|over|limit|max(?:imum)?)/i;
22
24
  export function isLikelyContextOverflowError(errorMessage) {
23
- if (!errorMessage)
25
+ if (!errorMessage) {
24
26
  return false;
25
- if (CONTEXT_WINDOW_TOO_SMALL_RE.test(errorMessage))
27
+ }
28
+ if (CONTEXT_WINDOW_TOO_SMALL_RE.test(errorMessage)) {
26
29
  return false;
27
- if (isContextOverflowError(errorMessage))
30
+ }
31
+ if (isContextOverflowError(errorMessage)) {
28
32
  return true;
33
+ }
29
34
  return CONTEXT_OVERFLOW_HINT_RE.test(errorMessage);
30
35
  }
31
36
  export function isCompactionFailureError(errorMessage) {
32
- if (!errorMessage)
33
- return false;
34
- if (!isContextOverflowError(errorMessage))
37
+ if (!errorMessage) {
35
38
  return false;
39
+ }
36
40
  const lower = errorMessage.toLowerCase();
37
- return (lower.includes("summarization failed") ||
41
+ const hasCompactionTerm = lower.includes("summarization failed") ||
38
42
  lower.includes("auto-compaction") ||
39
43
  lower.includes("compaction failed") ||
40
- lower.includes("compaction"));
44
+ lower.includes("compaction");
45
+ if (!hasCompactionTerm) {
46
+ return false;
47
+ }
48
+ // For compaction failures, also accept "context overflow" without colon
49
+ // since the error message itself describes a compaction/summarization failure
50
+ return isContextOverflowError(errorMessage) || lower.includes("context overflow");
41
51
  }
42
52
  const ERROR_PAYLOAD_PREFIX_RE = /^(?:error|api\s*error|apierror|openai\s*error|anthropic\s*error|gateway\s*error)[:\s-]+/i;
43
53
  const FINAL_TAG_RE = /<\s*\/?\s*final\s*>/gi;
44
54
  const ERROR_PREFIX_RE = /^(?:error|api\s*error|openai\s*error|anthropic\s*error|gateway\s*error|request failed|failed|exception)[:\s-]+/i;
55
+ const CONTEXT_OVERFLOW_ERROR_HEAD_RE = /^(?:context overflow:|request_too_large\b|request size exceeds\b|request exceeds the maximum size\b|context length exceeded\b|maximum context length\b|prompt is too long\b|exceeds model context window\b)/i;
45
56
  const HTTP_STATUS_PREFIX_RE = /^(?:http\s*)?(\d{3})\s+(.+)$/i;
46
57
  const HTTP_ERROR_HINTS = [
47
58
  "error",
@@ -61,17 +72,20 @@ const HTTP_ERROR_HINTS = [
61
72
  "permission",
62
73
  ];
63
74
  function stripFinalTagsFromText(text) {
64
- if (!text)
75
+ if (!text) {
65
76
  return text;
77
+ }
66
78
  return text.replace(FINAL_TAG_RE, "");
67
79
  }
68
80
  function collapseConsecutiveDuplicateBlocks(text) {
69
81
  const trimmed = text.trim();
70
- if (!trimmed)
82
+ if (!trimmed) {
71
83
  return text;
84
+ }
72
85
  const blocks = trimmed.split(/\n{2,}/);
73
- if (blocks.length < 2)
86
+ if (blocks.length < 2) {
74
87
  return text;
88
+ }
75
89
  const normalizeBlock = (value) => value.trim().replace(/\s+/g, " ");
76
90
  const result = [];
77
91
  let lastNormalized = null;
@@ -83,28 +97,43 @@ function collapseConsecutiveDuplicateBlocks(text) {
83
97
  result.push(block.trim());
84
98
  lastNormalized = normalized;
85
99
  }
86
- if (result.length === blocks.length)
100
+ if (result.length === blocks.length) {
87
101
  return text;
102
+ }
88
103
  return result.join("\n\n");
89
104
  }
90
105
  function isLikelyHttpErrorText(raw) {
91
106
  const match = raw.match(HTTP_STATUS_PREFIX_RE);
92
- if (!match)
107
+ if (!match) {
93
108
  return false;
109
+ }
94
110
  const code = Number(match[1]);
95
- if (!Number.isFinite(code) || code < 400)
111
+ if (!Number.isFinite(code) || code < 400) {
96
112
  return false;
113
+ }
97
114
  const message = match[2].toLowerCase();
98
115
  return HTTP_ERROR_HINTS.some((hint) => message.includes(hint));
99
116
  }
117
+ function shouldRewriteContextOverflowText(raw) {
118
+ if (!isContextOverflowError(raw)) {
119
+ return false;
120
+ }
121
+ return (isRawApiErrorPayload(raw) ||
122
+ isLikelyHttpErrorText(raw) ||
123
+ ERROR_PREFIX_RE.test(raw) ||
124
+ CONTEXT_OVERFLOW_ERROR_HEAD_RE.test(raw));
125
+ }
100
126
  function isErrorPayloadObject(payload) {
101
- if (!payload || typeof payload !== "object" || Array.isArray(payload))
127
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
102
128
  return false;
129
+ }
103
130
  const record = payload;
104
- if (record.type === "error")
131
+ if (record.type === "error") {
105
132
  return true;
106
- if (typeof record.request_id === "string" || typeof record.requestId === "string")
133
+ }
134
+ if (typeof record.request_id === "string" || typeof record.requestId === "string") {
107
135
  return true;
136
+ }
108
137
  if ("error" in record) {
109
138
  const err = record.error;
110
139
  if (err && typeof err === "object" && !Array.isArray(err)) {
@@ -119,22 +148,26 @@ function isErrorPayloadObject(payload) {
119
148
  return false;
120
149
  }
121
150
  function parseApiErrorPayload(raw) {
122
- if (!raw)
151
+ if (!raw) {
123
152
  return null;
153
+ }
124
154
  const trimmed = raw.trim();
125
- if (!trimmed)
155
+ if (!trimmed) {
126
156
  return null;
157
+ }
127
158
  const candidates = [trimmed];
128
159
  if (ERROR_PAYLOAD_PREFIX_RE.test(trimmed)) {
129
160
  candidates.push(trimmed.replace(ERROR_PAYLOAD_PREFIX_RE, "").trim());
130
161
  }
131
162
  for (const candidate of candidates) {
132
- if (!candidate.startsWith("{") || !candidate.endsWith("}"))
163
+ if (!candidate.startsWith("{") || !candidate.endsWith("}")) {
133
164
  continue;
165
+ }
134
166
  try {
135
167
  const parsed = JSON.parse(candidate);
136
- if (isErrorPayloadObject(parsed))
168
+ if (isErrorPayloadObject(parsed)) {
137
169
  return parsed;
170
+ }
138
171
  }
139
172
  catch {
140
173
  // ignore parse errors
@@ -150,27 +183,31 @@ function stableStringify(value) {
150
183
  return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
151
184
  }
152
185
  const record = value;
153
- const keys = Object.keys(record).sort();
186
+ const keys = Object.keys(record).toSorted();
154
187
  const entries = keys.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`);
155
188
  return `{${entries.join(",")}}`;
156
189
  }
157
190
  export function getApiErrorPayloadFingerprint(raw) {
158
- if (!raw)
191
+ if (!raw) {
159
192
  return null;
193
+ }
160
194
  const payload = parseApiErrorPayload(raw);
161
- if (!payload)
195
+ if (!payload) {
162
196
  return null;
197
+ }
163
198
  return stableStringify(payload);
164
199
  }
165
200
  export function isRawApiErrorPayload(raw) {
166
201
  return getApiErrorPayloadFingerprint(raw) !== null;
167
202
  }
168
203
  export function parseApiErrorInfo(raw) {
169
- if (!raw)
204
+ if (!raw) {
170
205
  return null;
206
+ }
171
207
  const trimmed = raw.trim();
172
- if (!trimmed)
208
+ if (!trimmed) {
173
209
  return null;
210
+ }
174
211
  let httpCode;
175
212
  let candidate = trimmed;
176
213
  const httpPrefixMatch = candidate.match(/^(\d{3})\s+(.+)$/s);
@@ -179,8 +216,9 @@ export function parseApiErrorInfo(raw) {
179
216
  candidate = httpPrefixMatch[2].trim();
180
217
  }
181
218
  const payload = parseApiErrorPayload(candidate);
182
- if (!payload)
219
+ if (!payload) {
183
220
  return null;
221
+ }
184
222
  const requestId = typeof payload.request_id === "string"
185
223
  ? payload.request_id
186
224
  : typeof payload.requestId === "string"
@@ -192,12 +230,15 @@ export function parseApiErrorInfo(raw) {
192
230
  let errMessage;
193
231
  if (payload.error && typeof payload.error === "object" && !Array.isArray(payload.error)) {
194
232
  const err = payload.error;
195
- if (typeof err.type === "string")
233
+ if (typeof err.type === "string") {
196
234
  errType = err.type;
197
- if (typeof err.code === "string" && !errType)
235
+ }
236
+ if (typeof err.code === "string" && !errType) {
198
237
  errType = err.code;
199
- if (typeof err.message === "string")
238
+ }
239
+ if (typeof err.message === "string") {
200
240
  errMessage = err.message;
241
+ }
201
242
  }
202
243
  return {
203
244
  httpCode,
@@ -208,8 +249,9 @@ export function parseApiErrorInfo(raw) {
208
249
  }
209
250
  export function formatRawAssistantErrorForUi(raw) {
210
251
  const trimmed = (raw ?? "").trim();
211
- if (!trimmed)
252
+ if (!trimmed) {
212
253
  return "LLM request failed with an unknown error.";
254
+ }
213
255
  const httpMatch = trimmed.match(HTTP_STATUS_PREFIX_RE);
214
256
  if (httpMatch) {
215
257
  const rest = httpMatch[2].trim();
@@ -229,10 +271,12 @@ export function formatRawAssistantErrorForUi(raw) {
229
271
  export function formatAssistantErrorText(msg, opts) {
230
272
  // Also format errors if errorMessage is present, even if stopReason isn't "error"
231
273
  const raw = (msg.errorMessage ?? "").trim();
232
- if (msg.stopReason !== "error" && !raw)
274
+ if (msg.stopReason !== "error" && !raw) {
233
275
  return undefined;
234
- if (!raw)
276
+ }
277
+ if (!raw) {
235
278
  return "LLM request failed with an unknown error.";
279
+ }
236
280
  const unknownTool = raw.match(/unknown tool[:\s]+["']?([a-z0-9_-]+)["']?/i) ??
237
281
  raw.match(/tool\s+["']?([a-z0-9_-]+)["']?\s+(?:not found|is not available)/i);
238
282
  if (unknownTool?.[1]) {
@@ -241,18 +285,24 @@ export function formatAssistantErrorText(msg, opts) {
241
285
  sessionKey: opts?.sessionKey,
242
286
  toolName: unknownTool[1],
243
287
  });
244
- if (rewritten)
288
+ if (rewritten) {
245
289
  return rewritten;
290
+ }
246
291
  }
247
292
  if (isContextOverflowError(raw)) {
248
293
  return ("Context overflow: prompt too large for the model. " +
249
- "Try again with less input or a larger-context model.");
294
+ "Try /reset (or /new) to start a fresh session, or use a larger-context model.");
250
295
  }
251
296
  // Catch role ordering errors - including JSON-wrapped and "400" prefix variants
252
297
  if (/incorrect role information|roles must alternate|400.*role|"message".*role.*information/i.test(raw)) {
253
298
  return ("Message ordering conflict - please try again. " +
254
299
  "If this persists, use /new to start a fresh session.");
255
300
  }
301
+ if (isMissingToolCallInputError(raw)) {
302
+ return ("Session history looks corrupted (tool call input missing). " +
303
+ "Use /new to start a fresh session. " +
304
+ "If this keeps happening, reset the session or delete the corrupted session transcript.");
305
+ }
256
306
  const invalidRequest = raw.match(/"type":"invalid_request_error".*?"message":"([^"]+)"/);
257
307
  if (invalidRequest?.[1]) {
258
308
  return `LLM request rejected: ${invalidRequest[1]}`;
@@ -260,6 +310,9 @@ export function formatAssistantErrorText(msg, opts) {
260
310
  if (isOverloadedErrorMessage(raw)) {
261
311
  return "The AI service is temporarily overloaded. Please try again in a moment.";
262
312
  }
313
+ if (isBillingErrorMessage(raw)) {
314
+ return BILLING_ERROR_USER_MESSAGE;
315
+ }
263
316
  if (isLikelyHttpErrorText(raw) || isRawApiErrorPayload(raw)) {
264
317
  return formatRawAssistantErrorForUi(raw);
265
318
  }
@@ -269,38 +322,49 @@ export function formatAssistantErrorText(msg, opts) {
269
322
  }
270
323
  return raw.length > 600 ? `${raw.slice(0, 600)}…` : raw;
271
324
  }
272
- export function sanitizeUserFacingText(text) {
273
- if (!text)
325
+ export function sanitizeUserFacingText(text, opts) {
326
+ if (!text) {
274
327
  return text;
328
+ }
329
+ const errorContext = opts?.errorContext ?? false;
275
330
  const stripped = stripFinalTagsFromText(text);
276
331
  const trimmed = stripped.trim();
277
- if (!trimmed)
332
+ if (!trimmed) {
278
333
  return stripped;
279
- if (/incorrect role information|roles must alternate/i.test(trimmed)) {
280
- return ("Message ordering conflict - please try again. " +
281
- "If this persists, use /new to start a fresh session.");
282
334
  }
283
- if (isContextOverflowError(trimmed)) {
284
- return ("Context overflow: prompt too large for the model. " +
285
- "Try again with less input or a larger-context model.");
286
- }
287
- if (isRawApiErrorPayload(trimmed) || isLikelyHttpErrorText(trimmed)) {
288
- return formatRawAssistantErrorForUi(trimmed);
289
- }
290
- if (ERROR_PREFIX_RE.test(trimmed)) {
291
- if (isOverloadedErrorMessage(trimmed) || isRateLimitErrorMessage(trimmed)) {
292
- return "The AI service is temporarily overloaded. Please try again in a moment.";
335
+ // Only apply error-pattern rewrites when the caller knows this text is an error payload.
336
+ // Otherwise we risk swallowing legitimate assistant text that merely *mentions* these errors.
337
+ if (errorContext) {
338
+ if (/incorrect role information|roles must alternate/i.test(trimmed)) {
339
+ return ("Message ordering conflict - please try again. " +
340
+ "If this persists, use /new to start a fresh session.");
341
+ }
342
+ if (shouldRewriteContextOverflowText(trimmed)) {
343
+ return ("Context overflow: prompt too large for the model. " +
344
+ "Try /reset (or /new) to start a fresh session, or use a larger-context model.");
293
345
  }
294
- if (isTimeoutErrorMessage(trimmed)) {
295
- return "LLM request timed out.";
346
+ if (isBillingErrorMessage(trimmed)) {
347
+ return BILLING_ERROR_USER_MESSAGE;
348
+ }
349
+ if (isRawApiErrorPayload(trimmed) || isLikelyHttpErrorText(trimmed)) {
350
+ return formatRawAssistantErrorForUi(trimmed);
351
+ }
352
+ if (ERROR_PREFIX_RE.test(trimmed)) {
353
+ if (isOverloadedErrorMessage(trimmed) || isRateLimitErrorMessage(trimmed)) {
354
+ return "The AI service is temporarily overloaded. Please try again in a moment.";
355
+ }
356
+ if (isTimeoutErrorMessage(trimmed)) {
357
+ return "LLM request timed out.";
358
+ }
359
+ return formatRawAssistantErrorForUi(trimmed);
296
360
  }
297
- return formatRawAssistantErrorForUi(trimmed);
298
361
  }
299
362
  return collapseConsecutiveDuplicateBlocks(stripped);
300
363
  }
301
364
  export function isRateLimitAssistantError(msg) {
302
- if (!msg || msg.stopReason !== "error")
365
+ if (!msg || msg.stopReason !== "error") {
303
366
  return false;
367
+ }
304
368
  return isRateLimitErrorMessage(msg.errorMessage ?? "");
305
369
  }
306
370
  const ERROR_PATTERNS = {
@@ -320,6 +384,7 @@ const ERROR_PATTERNS = {
320
384
  "insufficient credits",
321
385
  "credit balance",
322
386
  "plans & billing",
387
+ "insufficient balance",
323
388
  ],
324
389
  auth: [
325
390
  /invalid[_ ]?api[_ ]?key/,
@@ -346,11 +411,15 @@ const ERROR_PATTERNS = {
346
411
  "invalid request format",
347
412
  ],
348
413
  };
414
+ const TOOL_CALL_INPUT_MISSING_RE = /tool_(?:use|call)\.(?:input|arguments).*?(?:field required|required)/i;
415
+ const TOOL_CALL_INPUT_PATH_RE = /messages\.\d+\.content\.\d+\.tool_(?:use|call)\.(?:input|arguments)/i;
349
416
  const IMAGE_DIMENSION_ERROR_RE = /image dimensions exceed max allowed size for many-image requests:\s*(\d+)\s*pixels/i;
350
417
  const IMAGE_DIMENSION_PATH_RE = /messages\.(\d+)\.content\.(\d+)\.image/i;
418
+ const IMAGE_SIZE_ERROR_RE = /image exceeds\s*(\d+(?:\.\d+)?)\s*mb/i;
351
419
  function matchesErrorPatterns(raw, patterns) {
352
- if (!raw)
420
+ if (!raw) {
353
421
  return false;
422
+ }
354
423
  const value = raw.toLowerCase();
355
424
  return patterns.some((pattern) => pattern instanceof RegExp ? pattern.test(value) : value.includes(pattern));
356
425
  }
@@ -362,19 +431,21 @@ export function isTimeoutErrorMessage(raw) {
362
431
  }
363
432
  export function isBillingErrorMessage(raw) {
364
433
  const value = raw.toLowerCase();
365
- if (!value)
434
+ if (!value) {
366
435
  return false;
367
- if (matchesErrorPatterns(value, ERROR_PATTERNS.billing))
368
- return true;
369
- return (value.includes("billing") &&
370
- (value.includes("upgrade") ||
371
- value.includes("credits") ||
372
- value.includes("payment") ||
373
- value.includes("plan")));
436
+ }
437
+ return matchesErrorPatterns(value, ERROR_PATTERNS.billing);
438
+ }
439
+ export function isMissingToolCallInputError(raw) {
440
+ if (!raw) {
441
+ return false;
442
+ }
443
+ return TOOL_CALL_INPUT_MISSING_RE.test(raw) || TOOL_CALL_INPUT_PATH_RE.test(raw);
374
444
  }
375
445
  export function isBillingAssistantError(msg) {
376
- if (!msg || msg.stopReason !== "error")
446
+ if (!msg || msg.stopReason !== "error") {
377
447
  return false;
448
+ }
378
449
  return isBillingErrorMessage(msg.errorMessage ?? "");
379
450
  }
380
451
  export function isAuthErrorMessage(raw) {
@@ -384,11 +455,13 @@ export function isOverloadedErrorMessage(raw) {
384
455
  return matchesErrorPatterns(raw, ERROR_PATTERNS.overloaded);
385
456
  }
386
457
  export function parseImageDimensionError(raw) {
387
- if (!raw)
458
+ if (!raw) {
388
459
  return null;
460
+ }
389
461
  const lower = raw.toLowerCase();
390
- if (!lower.includes("image dimensions exceed max allowed size"))
462
+ if (!lower.includes("image dimensions exceed max allowed size")) {
391
463
  return null;
464
+ }
392
465
  const limitMatch = raw.match(IMAGE_DIMENSION_ERROR_RE);
393
466
  const pathMatch = raw.match(IMAGE_DIMENSION_PATH_RE);
394
467
  return {
@@ -401,36 +474,68 @@ export function parseImageDimensionError(raw) {
401
474
  export function isImageDimensionErrorMessage(raw) {
402
475
  return Boolean(parseImageDimensionError(raw));
403
476
  }
477
+ export function parseImageSizeError(raw) {
478
+ if (!raw) {
479
+ return null;
480
+ }
481
+ const lower = raw.toLowerCase();
482
+ if (!lower.includes("image exceeds") || !lower.includes("mb")) {
483
+ return null;
484
+ }
485
+ const match = raw.match(IMAGE_SIZE_ERROR_RE);
486
+ return {
487
+ maxMb: match?.[1] ? Number.parseFloat(match[1]) : undefined,
488
+ raw,
489
+ };
490
+ }
491
+ export function isImageSizeError(errorMessage) {
492
+ if (!errorMessage) {
493
+ return false;
494
+ }
495
+ return Boolean(parseImageSizeError(errorMessage));
496
+ }
404
497
  export function isCloudCodeAssistFormatError(raw) {
405
498
  return !isImageDimensionErrorMessage(raw) && matchesErrorPatterns(raw, ERROR_PATTERNS.format);
406
499
  }
407
500
  export function isAuthAssistantError(msg) {
408
- if (!msg || msg.stopReason !== "error")
501
+ if (!msg || msg.stopReason !== "error") {
409
502
  return false;
503
+ }
410
504
  return isAuthErrorMessage(msg.errorMessage ?? "");
411
505
  }
412
506
  export function classifyFailoverReason(raw) {
413
- if (isImageDimensionErrorMessage(raw))
507
+ if (isImageDimensionErrorMessage(raw)) {
508
+ return null;
509
+ }
510
+ if (isImageSizeError(raw)) {
414
511
  return null;
415
- if (isRateLimitErrorMessage(raw))
512
+ }
513
+ if (isRateLimitErrorMessage(raw)) {
416
514
  return "rate_limit";
417
- if (isOverloadedErrorMessage(raw))
515
+ }
516
+ if (isOverloadedErrorMessage(raw)) {
418
517
  return "rate_limit";
419
- if (isCloudCodeAssistFormatError(raw))
518
+ }
519
+ if (isCloudCodeAssistFormatError(raw)) {
420
520
  return "format";
421
- if (isBillingErrorMessage(raw))
521
+ }
522
+ if (isBillingErrorMessage(raw)) {
422
523
  return "billing";
423
- if (isTimeoutErrorMessage(raw))
524
+ }
525
+ if (isTimeoutErrorMessage(raw)) {
424
526
  return "timeout";
425
- if (isAuthErrorMessage(raw))
527
+ }
528
+ if (isAuthErrorMessage(raw)) {
426
529
  return "auth";
530
+ }
427
531
  return null;
428
532
  }
429
533
  export function isFailoverErrorMessage(raw) {
430
534
  return classifyFailoverReason(raw) !== null;
431
535
  }
432
536
  export function isFailoverAssistantError(msg) {
433
- if (!msg || msg.stopReason !== "error")
537
+ if (!msg || msg.stopReason !== "error") {
434
538
  return false;
539
+ }
435
540
  return isFailoverErrorMessage(msg.errorMessage ?? "");
436
541
  }
@@ -1,5 +1,5 @@
1
1
  export { buildBootstrapContextFiles, DEFAULT_BOOTSTRAP_MAX_CHARS, ensureSessionHeader, resolveBootstrapMaxChars, stripThoughtSignatures, } from "./pi-embedded-helpers/bootstrap.js";
2
- export { classifyFailoverReason, formatRawAssistantErrorForUi, formatAssistantErrorText, getApiErrorPayloadFingerprint, isAuthAssistantError, isAuthErrorMessage, isBillingAssistantError, parseApiErrorInfo, sanitizeUserFacingText, isBillingErrorMessage, isCloudCodeAssistFormatError, isCompactionFailureError, isContextOverflowError, isLikelyContextOverflowError, isFailoverAssistantError, isFailoverErrorMessage, isImageDimensionErrorMessage, isOverloadedErrorMessage, isRawApiErrorPayload, isRateLimitAssistantError, isRateLimitErrorMessage, isTimeoutErrorMessage, parseImageDimensionError, } from "./pi-embedded-helpers/errors.js";
2
+ export { BILLING_ERROR_USER_MESSAGE, classifyFailoverReason, formatRawAssistantErrorForUi, formatAssistantErrorText, getApiErrorPayloadFingerprint, isAuthAssistantError, isAuthErrorMessage, isBillingAssistantError, parseApiErrorInfo, parseImageSizeError, sanitizeUserFacingText, isBillingErrorMessage, isCloudCodeAssistFormatError, isCompactionFailureError, isContextOverflowError, isLikelyContextOverflowError, isFailoverAssistantError, isFailoverErrorMessage, isImageDimensionErrorMessage, isImageSizeError, isOverloadedErrorMessage, isRawApiErrorPayload, isRateLimitAssistantError, isRateLimitErrorMessage, isTimeoutErrorMessage, parseImageDimensionError, } from "./pi-embedded-helpers/errors.js";
3
3
  export { isGoogleModelApi, sanitizeGoogleTurnOrdering } from "./pi-embedded-helpers/google.js";
4
4
  export { downgradeOpenAIReasoningBlocks } from "./pi-embedded-helpers/openai.js";
5
5
  export { isEmptyAssistantMessageContent, sanitizeSessionMessagesImages, } from "./pi-embedded-helpers/images.js";
@@ -152,6 +152,7 @@ export async function compactEmbeddedPiSessionDirect(params) {
152
152
  groupChannel: params.groupChannel,
153
153
  groupSpace: params.groupSpace,
154
154
  spawnedBy: params.spawnedBy,
155
+ senderIsOwner: params.senderIsOwner,
155
156
  agentDir,
156
157
  workspaceDir: effectiveWorkspace,
157
158
  config: params.config,
@@ -8,7 +8,12 @@ export function buildInlineProviderModels(providers) {
8
8
  const trimmed = providerId.trim();
9
9
  if (!trimmed)
10
10
  return [];
11
- return (entry?.models ?? []).map((model) => ({ ...model, provider: trimmed }));
11
+ return (entry?.models ?? []).map((model) => ({
12
+ ...model,
13
+ provider: trimmed,
14
+ baseUrl: entry?.baseUrl,
15
+ ...(entry?.api && !model.api ? { api: entry.api } : {}),
16
+ }));
12
17
  });
13
18
  }
14
19
  export function buildModelAliasLines(cfg) {
@@ -24,15 +29,68 @@ export function buildModelAliasLines(cfg) {
24
29
  entries.push({ alias, model });
25
30
  }
26
31
  return entries
27
- .sort((a, b) => a.alias.localeCompare(b.alias))
32
+ .toSorted((a, b) => a.alias.localeCompare(b.alias))
28
33
  .map((entry) => `- ${entry.alias}: ${entry.model}`);
29
34
  }
35
+ // Well-known model IDs for forward-compatible resolution
36
+ export const OPENAI_CODEX_GPT_53_MODEL_ID = "codex-gpt-5.3";
37
+ export const OPENAI_CODEX_TEMPLATE_MODEL_IDS = ["codex-mini-*"];
38
+ export const ANTHROPIC_OPUS_46_MODEL_ID = "claude-opus-4-0624";
39
+ export const ANTHROPIC_OPUS_46_DOT_MODEL_ID = "claude-opus-4.6";
40
+ export const ANTHROPIC_OPUS_TEMPLATE_MODEL_IDS = ["claude-opus-4-*", "claude-opus-4.*"];
41
+ /**
42
+ * Resolve forward-compat fallback for OpenAI codex-gpt-5.3 requests.
43
+ * If the exact model is not in the registry, try `codex-mini-*` template matches.
44
+ */
45
+ export function resolveOpenAICodexGpt53FallbackModel(modelRegistry, provider) {
46
+ for (const templateId of OPENAI_CODEX_TEMPLATE_MODEL_IDS) {
47
+ const match = modelRegistry.find(provider, templateId);
48
+ if (match)
49
+ return match;
50
+ }
51
+ return null;
52
+ }
53
+ /**
54
+ * Resolve forward-compat fallback for Anthropic claude-opus-4.6 requests.
55
+ * If the exact model is not in the registry, try `claude-opus-4-*` template matches.
56
+ */
57
+ export function resolveAnthropicOpus46ForwardCompatModel(modelRegistry, provider, modelId) {
58
+ if (modelId !== ANTHROPIC_OPUS_46_MODEL_ID && modelId !== ANTHROPIC_OPUS_46_DOT_MODEL_ID) {
59
+ return null;
60
+ }
61
+ for (const templateId of ANTHROPIC_OPUS_TEMPLATE_MODEL_IDS) {
62
+ const match = modelRegistry.find(provider, templateId);
63
+ if (match)
64
+ return match;
65
+ }
66
+ return null;
67
+ }
30
68
  export function resolveModel(provider, modelId, agentDir, cfg) {
31
69
  const resolvedAgentDir = agentDir ?? resolvePoolbotAgentDir();
32
70
  const authStorage = discoverAuthStorage(resolvedAgentDir);
33
71
  const modelRegistry = discoverModels(authStorage, resolvedAgentDir);
34
72
  const model = modelRegistry.find(provider, modelId);
35
73
  if (!model) {
74
+ // Try codex forward-compat fallback
75
+ if (modelId === OPENAI_CODEX_GPT_53_MODEL_ID) {
76
+ const codexFallback = resolveOpenAICodexGpt53FallbackModel(modelRegistry, provider);
77
+ if (codexFallback) {
78
+ return {
79
+ model: normalizeModelCompat(codexFallback),
80
+ authStorage,
81
+ modelRegistry,
82
+ };
83
+ }
84
+ }
85
+ // Try Anthropic opus forward-compat fallback
86
+ const opusFallback = resolveAnthropicOpus46ForwardCompatModel(modelRegistry, provider, modelId);
87
+ if (opusFallback) {
88
+ return {
89
+ model: normalizeModelCompat(opusFallback),
90
+ authStorage,
91
+ modelRegistry,
92
+ };
93
+ }
36
94
  const providers = cfg?.models?.providers ?? {};
37
95
  const inlineModels = buildInlineProviderModels(providers);
38
96
  const normalizedProvider = normalizeProviderId(provider);
@@ -57,6 +115,7 @@ export function resolveModel(provider, modelId, agentDir, cfg) {
57
115
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
58
116
  contextWindow: providerCfg?.models?.[0]?.contextWindow ?? DEFAULT_CONTEXT_TOKENS,
59
117
  maxTokens: providerCfg?.models?.[0]?.maxTokens ?? DEFAULT_CONTEXT_TOKENS,
118
+ baseUrl: providerCfg?.baseUrl,
60
119
  });
61
120
  return { model: fallbackModel, authStorage, modelRegistry };
62
121
  }