@poolzin/pool-bot 2026.2.23 → 2026.2.25
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.
- package/CHANGELOG.md +29 -0
- package/dist/acp/client.js +207 -18
- package/dist/acp/secret-file.js +22 -0
- package/dist/agents/agent-scope.js +10 -0
- package/dist/agents/bash-process-registry.test-helpers.js +29 -0
- package/dist/agents/bash-tools.exec-approval-request.js +20 -0
- package/dist/agents/bash-tools.exec-host-gateway.js +230 -0
- package/dist/agents/bash-tools.exec-host-node.js +235 -0
- package/dist/agents/bash-tools.exec-types.js +1 -0
- package/dist/agents/bash-tools.process.js +224 -218
- package/dist/agents/content-blocks.js +16 -0
- package/dist/agents/model-fallback.js +96 -101
- package/dist/agents/models-config.providers.js +299 -182
- package/dist/agents/pi-embedded-payloads.js +1 -0
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
- package/dist/agents/skills.test-helpers.js +13 -0
- package/dist/agents/stable-stringify.js +12 -0
- package/dist/agents/subagent-registry.mocks.shared.js +12 -0
- package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
- package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
- package/dist/agents/tool-policy-shared.js +108 -0
- package/dist/agents/tools/browser-tool.js +160 -54
- package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
- package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
- package/dist/agents/tools/image-tool.js +214 -99
- package/dist/agents/tools/sessions-history-tool.js +140 -108
- package/dist/agents/workspace.js +222 -46
- package/dist/auto-reply/commands-registry.js +15 -18
- package/dist/auto-reply/fallback-state.js +114 -0
- package/dist/auto-reply/model-runtime.js +68 -0
- package/dist/auto-reply/reply/agent-runner-execution.js +36 -4
- package/dist/auto-reply/reply/agent-runner.js +165 -39
- package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
- package/dist/browser/config.js +26 -0
- package/dist/browser/navigation-guard.js +31 -0
- package/dist/browser/routes/agent.act.js +431 -424
- package/dist/browser/routes/agent.shared.js +47 -3
- package/dist/browser/routes/agent.snapshot.js +122 -116
- package/dist/browser/routes/agent.storage.js +303 -297
- package/dist/browser/routes/tabs.js +154 -100
- package/dist/browser/server-lifecycle.js +37 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/allow-from.js +25 -0
- package/dist/channels/plugins/account-action-gate.js +13 -0
- package/dist/channels/plugins/message-actions.js +10 -0
- package/dist/channels/telegram/api.js +18 -0
- package/dist/cli/argv.js +84 -21
- package/dist/cli/banner.js +2 -1
- package/dist/cli/exec-approvals-cli.js +92 -124
- package/dist/cli/memory-cli.js +158 -61
- package/dist/cli/nodes-cli/register.push.js +63 -0
- package/dist/cli/nodes-media-utils.js +21 -0
- package/dist/cli/plugins-cli.js +245 -61
- package/dist/cli/program/build-program.js +3 -1
- package/dist/cli/program/command-registry.js +223 -136
- package/dist/cli/program/help.js +43 -12
- package/dist/cli/route.js +1 -1
- package/dist/cli/test-runtime-capture.js +24 -0
- package/dist/commands/agent.js +163 -87
- package/dist/commands/channels.mock-harness.js +23 -0
- package/dist/commands/daemon-install-runtime-warning.js +11 -0
- package/dist/commands/onboard-helpers.js +4 -4
- package/dist/commands/sessions.test-helpers.js +61 -0
- package/dist/compat/legacy-names.js +2 -2
- package/dist/config/commands.js +3 -0
- package/dist/config/config.js +1 -1
- package/dist/config/env-substitution.js +62 -34
- package/dist/config/env-vars.js +9 -0
- package/dist/config/io.js +571 -171
- package/dist/config/merge-patch.js +50 -4
- package/dist/config/redact-snapshot.js +404 -76
- package/dist/config/schema.js +58 -570
- package/dist/config/validation.js +140 -85
- package/dist/config/zod-schema.hooks.js +40 -11
- package/dist/config/zod-schema.installs.js +20 -0
- package/dist/config/zod-schema.js +8 -7
- package/dist/control-ui/assets/{index-HRr1grwl.js → index-Dvkl4Xlx.js} +2 -1
- package/dist/control-ui/assets/{index-HRr1grwl.js.map → index-Dvkl4Xlx.js.map} +1 -1
- package/dist/control-ui/index.html +1 -1
- package/dist/daemon/cmd-argv.js +21 -0
- package/dist/daemon/cmd-set.js +58 -0
- package/dist/daemon/service-types.js +1 -0
- package/dist/discord/monitor/exec-approvals.js +357 -162
- package/dist/gateway/auth.js +38 -3
- package/dist/gateway/call.js +149 -68
- package/dist/gateway/canvas-capability.js +75 -0
- package/dist/gateway/control-plane-audit.js +28 -0
- package/dist/gateway/control-plane-rate-limit.js +53 -0
- package/dist/gateway/events.js +1 -0
- package/dist/gateway/hooks.js +109 -54
- package/dist/gateway/http-common.js +22 -0
- package/dist/gateway/method-scopes.js +169 -0
- package/dist/gateway/net.js +23 -0
- package/dist/gateway/openresponses-http.js +120 -110
- package/dist/gateway/probe-auth.js +2 -0
- package/dist/gateway/protocol/index.js +3 -2
- package/dist/gateway/protocol/schema/protocol-schemas.js +2 -0
- package/dist/gateway/protocol/schema/push.js +18 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/server-http.js +236 -52
- package/dist/gateway/server-methods/agent.js +162 -24
- package/dist/gateway/server-methods/chat.js +461 -130
- package/dist/gateway/server-methods/config.js +193 -150
- package/dist/gateway/server-methods/nodes.helpers.js +12 -0
- package/dist/gateway/server-methods/nodes.js +251 -69
- package/dist/gateway/server-methods/push.js +53 -0
- package/dist/gateway/server-reload-handlers.js +2 -3
- package/dist/gateway/server-runtime-config.js +5 -0
- package/dist/gateway/server-runtime-state.js +2 -0
- package/dist/gateway/server-ws-runtime.js +1 -0
- package/dist/gateway/server.impl.js +296 -139
- package/dist/gateway/session-preview.test-helpers.js +11 -0
- package/dist/gateway/startup-auth.js +126 -0
- package/dist/gateway/test-helpers.agent-results.js +15 -0
- package/dist/gateway/test-helpers.mocks.js +37 -14
- package/dist/gateway/test-helpers.server.js +161 -77
- package/dist/hooks/bundled/session-memory/handler.js +165 -34
- package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
- package/dist/infra/archive-path.js +49 -0
- package/dist/infra/device-pairing.js +148 -167
- package/dist/infra/exec-approvals-allowlist.js +19 -70
- package/dist/infra/exec-approvals-analysis.js +44 -17
- package/dist/infra/exec-safe-bin-policy.js +269 -0
- package/dist/infra/fixed-window-rate-limit.js +33 -0
- package/dist/infra/git-root.js +61 -0
- package/dist/infra/heartbeat-active-hours.js +2 -2
- package/dist/infra/heartbeat-reason.js +40 -0
- package/dist/infra/heartbeat-runner.js +72 -32
- package/dist/infra/install-source-utils.js +91 -7
- package/dist/infra/node-pairing.js +50 -105
- package/dist/infra/npm-integrity.js +45 -0
- package/dist/infra/npm-pack-install.js +40 -0
- package/dist/infra/outbound/channel-adapters.js +20 -7
- package/dist/infra/outbound/message-action-runner.js +107 -327
- package/dist/infra/outbound/message.js +59 -36
- package/dist/infra/outbound/outbound-policy.js +52 -25
- package/dist/infra/outbound/outbound-send-service.js +58 -71
- package/dist/infra/pairing-files.js +10 -0
- package/dist/infra/plain-object.js +9 -0
- package/dist/infra/push-apns.js +365 -0
- package/dist/infra/restart-sentinel.js +16 -1
- package/dist/infra/restart.js +229 -26
- package/dist/infra/scp-host.js +54 -0
- package/dist/infra/update-startup.js +86 -9
- package/dist/media/inbound-path-policy.js +114 -0
- package/dist/media/input-files.js +16 -0
- package/dist/memory/test-manager.js +8 -0
- package/dist/plugin-sdk/temp-path.js +47 -0
- package/dist/plugins/discovery.js +217 -23
- package/dist/plugins/hook-runner-global.js +16 -0
- package/dist/plugins/loader.js +192 -26
- package/dist/plugins/logger.js +8 -0
- package/dist/plugins/manifest-registry.js +3 -0
- package/dist/plugins/path-safety.js +34 -0
- package/dist/plugins/registry.js +5 -2
- package/dist/plugins/runtime/index.js +271 -206
- package/dist/providers/github-copilot-models.js +4 -1
- package/dist/security/audit-channel.js +8 -19
- package/dist/security/audit-extra.async.js +354 -182
- package/dist/security/audit-extra.js +11 -1
- package/dist/security/audit-extra.sync.js +340 -33
- package/dist/security/audit-fs.js +31 -13
- package/dist/security/audit.js +145 -371
- package/dist/security/dm-policy-shared.js +24 -0
- package/dist/security/external-content.js +20 -8
- package/dist/security/fix.js +49 -85
- package/dist/security/scan-paths.js +20 -0
- package/dist/security/secret-equal.js +3 -7
- package/dist/security/windows-acl.js +30 -15
- package/dist/shared/node-list-parse.js +13 -0
- package/dist/shared/operator-scope-compat.js +37 -0
- package/dist/shared/text-chunking.js +29 -0
- package/dist/slack/blocks.test-helpers.js +31 -0
- package/dist/slack/monitor/mrkdwn.js +8 -0
- package/dist/telegram/bot-message-dispatch.js +366 -164
- package/dist/telegram/draft-stream.js +30 -7
- package/dist/telegram/reasoning-lane-coordinator.js +128 -0
- package/dist/terminal/prompt-select-styled.js +9 -0
- package/dist/test-utils/command-runner.js +6 -0
- package/dist/test-utils/internal-hook-event-payload.js +10 -0
- package/dist/test-utils/model-auth-mock.js +12 -0
- package/dist/test-utils/provider-usage-fetch.js +14 -0
- package/dist/test-utils/temp-home.js +33 -0
- package/dist/tui/components/chat-log.js +9 -0
- package/dist/tui/tui-command-handlers.js +36 -27
- package/dist/tui/tui-event-handlers.js +122 -32
- package/dist/tui/tui.js +181 -45
- package/dist/utils/mask-api-key.js +10 -0
- package/dist/utils/run-with-concurrency.js +39 -0
- package/dist/web/media.js +4 -0
- package/docs/tools/slash-commands.md +5 -1
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/feishu/src/external-keys.ts +19 -0
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/lobster/src/windows-spawn.ts +193 -0
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
- package/extensions/mattermost/package.json +1 -1
- package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +5 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +5 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +5 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +1 -1
|
@@ -1,64 +1,75 @@
|
|
|
1
|
+
import { ensureAuthProfileStore, getSoonestCooldownExpiry, isProfileInCooldown, resolveAuthProfileOrder, } from "./auth-profiles.js";
|
|
1
2
|
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "./defaults.js";
|
|
2
3
|
import { coerceToFailoverError, describeFailoverError, isFailoverError, isTimeoutError, } from "./failover-error.js";
|
|
3
|
-
import { buildModelAliasIndex, modelKey,
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import { buildConfiguredAllowlistKeys, buildModelAliasIndex, modelKey, normalizeModelRef, resolveConfiguredModelRef, resolveModelRefFromString, } from "./model-selection.js";
|
|
5
|
+
import { isLikelyContextOverflowError } from "./pi-embedded-helpers.js";
|
|
6
|
+
/**
|
|
7
|
+
* Fallback abort check. Only treats explicit AbortError names as user aborts.
|
|
8
|
+
* Message-based checks (e.g., "aborted") can mask timeouts and skip fallback.
|
|
9
|
+
*/
|
|
10
|
+
function isFallbackAbortError(err) {
|
|
11
|
+
if (!err || typeof err !== "object") {
|
|
8
12
|
return false;
|
|
9
|
-
|
|
13
|
+
}
|
|
14
|
+
if (isFailoverError(err)) {
|
|
10
15
|
return false;
|
|
16
|
+
}
|
|
11
17
|
const name = "name" in err ? String(err.name) : "";
|
|
12
|
-
// Only treat explicit AbortError names as user aborts.
|
|
13
|
-
// Message-based checks (e.g., "aborted") can mask timeouts and skip fallback.
|
|
14
18
|
return name === "AbortError";
|
|
15
19
|
}
|
|
16
20
|
function shouldRethrowAbort(err) {
|
|
17
|
-
return
|
|
21
|
+
return isFallbackAbortError(err) && !isTimeoutError(err);
|
|
18
22
|
}
|
|
19
|
-
function
|
|
20
|
-
const rawAllowlist = (() => {
|
|
21
|
-
const modelMap = cfg?.agents?.defaults?.models ?? {};
|
|
22
|
-
return Object.keys(modelMap);
|
|
23
|
-
})();
|
|
24
|
-
if (rawAllowlist.length === 0)
|
|
25
|
-
return null;
|
|
26
|
-
const keys = new Set();
|
|
27
|
-
for (const raw of rawAllowlist) {
|
|
28
|
-
const parsed = parseModelRef(String(raw ?? ""), defaultProvider);
|
|
29
|
-
if (!parsed)
|
|
30
|
-
continue;
|
|
31
|
-
keys.add(modelKey(parsed.provider, parsed.model));
|
|
32
|
-
}
|
|
33
|
-
return keys.size > 0 ? keys : null;
|
|
34
|
-
}
|
|
35
|
-
function resolveImageFallbackCandidates(params) {
|
|
36
|
-
const aliasIndex = buildModelAliasIndex({
|
|
37
|
-
cfg: params.cfg ?? {},
|
|
38
|
-
defaultProvider: params.defaultProvider,
|
|
39
|
-
});
|
|
40
|
-
const allowlist = buildAllowedModelKeys(params.cfg, params.defaultProvider);
|
|
23
|
+
function createModelCandidateCollector(allowlist) {
|
|
41
24
|
const seen = new Set();
|
|
42
25
|
const candidates = [];
|
|
43
26
|
const addCandidate = (candidate, enforceAllowlist) => {
|
|
44
|
-
if (!candidate.provider || !candidate.model)
|
|
27
|
+
if (!candidate.provider || !candidate.model) {
|
|
45
28
|
return;
|
|
29
|
+
}
|
|
46
30
|
const key = modelKey(candidate.provider, candidate.model);
|
|
47
|
-
if (seen.has(key))
|
|
31
|
+
if (seen.has(key)) {
|
|
48
32
|
return;
|
|
49
|
-
|
|
33
|
+
}
|
|
34
|
+
if (enforceAllowlist && allowlist && !allowlist.has(key)) {
|
|
50
35
|
return;
|
|
36
|
+
}
|
|
51
37
|
seen.add(key);
|
|
52
38
|
candidates.push(candidate);
|
|
53
39
|
};
|
|
40
|
+
return { candidates, addCandidate };
|
|
41
|
+
}
|
|
42
|
+
function sameModelCandidate(a, b) {
|
|
43
|
+
return a.provider === b.provider && a.model === b.model;
|
|
44
|
+
}
|
|
45
|
+
function throwFallbackFailureSummary(params) {
|
|
46
|
+
if (params.attempts.length <= 1 && params.lastError) {
|
|
47
|
+
throw params.lastError;
|
|
48
|
+
}
|
|
49
|
+
const summary = params.attempts.length > 0 ? params.attempts.map(params.formatAttempt).join(" | ") : "unknown";
|
|
50
|
+
throw new Error(`All ${params.label} failed (${params.attempts.length || params.candidates.length}): ${summary}`, {
|
|
51
|
+
cause: params.lastError instanceof Error ? params.lastError : undefined,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function resolveImageFallbackCandidates(params) {
|
|
55
|
+
const aliasIndex = buildModelAliasIndex({
|
|
56
|
+
cfg: params.cfg ?? {},
|
|
57
|
+
defaultProvider: params.defaultProvider,
|
|
58
|
+
});
|
|
59
|
+
const allowlist = buildConfiguredAllowlistKeys({
|
|
60
|
+
cfg: params.cfg,
|
|
61
|
+
defaultProvider: params.defaultProvider,
|
|
62
|
+
});
|
|
63
|
+
const { candidates, addCandidate } = createModelCandidateCollector(allowlist);
|
|
54
64
|
const addRaw = (raw, enforceAllowlist) => {
|
|
55
65
|
const resolved = resolveModelRefFromString({
|
|
56
66
|
raw: String(raw ?? ""),
|
|
57
67
|
defaultProvider: params.defaultProvider,
|
|
58
68
|
aliasIndex,
|
|
59
69
|
});
|
|
60
|
-
if (!resolved)
|
|
70
|
+
if (!resolved) {
|
|
61
71
|
return;
|
|
72
|
+
}
|
|
62
73
|
addCandidate(resolved.ref, enforceAllowlist);
|
|
63
74
|
};
|
|
64
75
|
if (params.modelOverride?.trim()) {
|
|
@@ -67,8 +78,9 @@ function resolveImageFallbackCandidates(params) {
|
|
|
67
78
|
else {
|
|
68
79
|
const imageModel = params.cfg?.agents?.defaults?.imageModel;
|
|
69
80
|
const primary = typeof imageModel === "string" ? imageModel.trim() : imageModel?.primary;
|
|
70
|
-
if (primary?.trim())
|
|
81
|
+
if (primary?.trim()) {
|
|
71
82
|
addRaw(primary, false);
|
|
83
|
+
}
|
|
72
84
|
}
|
|
73
85
|
const imageFallbacks = (() => {
|
|
74
86
|
const imageModel = params.cfg?.agents?.defaults?.imageModel;
|
|
@@ -92,33 +104,33 @@ function resolveFallbackCandidates(params) {
|
|
|
92
104
|
: null;
|
|
93
105
|
const defaultProvider = primary?.provider ?? DEFAULT_PROVIDER;
|
|
94
106
|
const defaultModel = primary?.model ?? DEFAULT_MODEL;
|
|
95
|
-
const
|
|
96
|
-
const
|
|
107
|
+
const providerRaw = String(params.provider ?? "").trim() || defaultProvider;
|
|
108
|
+
const modelRaw = String(params.model ?? "").trim() || defaultModel;
|
|
109
|
+
const normalizedPrimary = normalizeModelRef(providerRaw, modelRaw);
|
|
110
|
+
const configuredPrimary = normalizeModelRef(defaultProvider, defaultModel);
|
|
97
111
|
const aliasIndex = buildModelAliasIndex({
|
|
98
112
|
cfg: params.cfg ?? {},
|
|
99
113
|
defaultProvider,
|
|
100
114
|
});
|
|
101
|
-
const allowlist =
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const key = modelKey(candidate.provider, candidate.model);
|
|
108
|
-
if (seen.has(key))
|
|
109
|
-
return;
|
|
110
|
-
if (enforceAllowlist && allowlist && !allowlist.has(key))
|
|
111
|
-
return;
|
|
112
|
-
seen.add(key);
|
|
113
|
-
candidates.push(candidate);
|
|
114
|
-
};
|
|
115
|
-
addCandidate({ provider, model }, false);
|
|
115
|
+
const allowlist = buildConfiguredAllowlistKeys({
|
|
116
|
+
cfg: params.cfg,
|
|
117
|
+
defaultProvider,
|
|
118
|
+
});
|
|
119
|
+
const { candidates, addCandidate } = createModelCandidateCollector(allowlist);
|
|
120
|
+
addCandidate(normalizedPrimary, false);
|
|
116
121
|
const modelFallbacks = (() => {
|
|
117
|
-
if (params.fallbacksOverride !== undefined)
|
|
122
|
+
if (params.fallbacksOverride !== undefined) {
|
|
118
123
|
return params.fallbacksOverride;
|
|
124
|
+
}
|
|
125
|
+
// Skip configured fallback chain when the user runs a non-default override.
|
|
126
|
+
// In that case, retry should return directly to configured primary.
|
|
127
|
+
if (!sameModelCandidate(normalizedPrimary, configuredPrimary)) {
|
|
128
|
+
return []; // Override model failed → go straight to configured default
|
|
129
|
+
}
|
|
119
130
|
const model = params.cfg?.agents?.defaults?.model;
|
|
120
|
-
if (model && typeof model === "object")
|
|
131
|
+
if (model && typeof model === "object") {
|
|
121
132
|
return model.fallbacks ?? [];
|
|
133
|
+
}
|
|
122
134
|
return [];
|
|
123
135
|
})();
|
|
124
136
|
for (const raw of modelFallbacks) {
|
|
@@ -127,8 +139,9 @@ function resolveFallbackCandidates(params) {
|
|
|
127
139
|
defaultProvider,
|
|
128
140
|
aliasIndex,
|
|
129
141
|
});
|
|
130
|
-
if (!resolved)
|
|
142
|
+
if (!resolved) {
|
|
131
143
|
continue;
|
|
144
|
+
}
|
|
132
145
|
addCandidate(resolved.ref, true);
|
|
133
146
|
}
|
|
134
147
|
if (params.fallbacksOverride === undefined && primary?.provider && primary.model) {
|
|
@@ -177,8 +190,8 @@ export async function runWithModelFallback(params) {
|
|
|
177
190
|
? ensureAuthProfileStore(params.agentDir, { allowKeychainPrompt: false })
|
|
178
191
|
: null;
|
|
179
192
|
const attempts = [];
|
|
180
|
-
const hasFallbackCandidates = candidates.length > 1;
|
|
181
193
|
let lastError;
|
|
194
|
+
const hasFallbackCandidates = candidates.length > 1;
|
|
182
195
|
for (let i = 0; i < candidates.length; i += 1) {
|
|
183
196
|
const candidate = candidates[i];
|
|
184
197
|
if (authStore) {
|
|
@@ -219,21 +232,6 @@ export async function runWithModelFallback(params) {
|
|
|
219
232
|
lastProbeAttempt.set(probeThrottleKey, now);
|
|
220
233
|
}
|
|
221
234
|
}
|
|
222
|
-
// Provider-level rate limit check (from token pool / rate limit tracking).
|
|
223
|
-
// Complements the per-profile cooldown above — skips candidates when all
|
|
224
|
-
// API keys in the pool are known to be rate-limited.
|
|
225
|
-
const poolLimit = isProviderRateLimited(candidate.provider);
|
|
226
|
-
if (poolLimit.isLimited && hasFallbackCandidates && i > 0) {
|
|
227
|
-
// Skip non-primary candidates that are pool-rate-limited.
|
|
228
|
-
// Primary (i === 0) is never skipped here — it gets a chance to probe.
|
|
229
|
-
attempts.push({
|
|
230
|
-
provider: candidate.provider,
|
|
231
|
-
model: candidate.model,
|
|
232
|
-
error: `Provider ${candidate.provider} pool is rate-limited (wait ${poolLimit.waitTimeMs}ms)`,
|
|
233
|
-
reason: "rate_limit",
|
|
234
|
-
});
|
|
235
|
-
continue;
|
|
236
|
-
}
|
|
237
235
|
try {
|
|
238
236
|
const result = await params.run(candidate.provider, candidate.model);
|
|
239
237
|
return {
|
|
@@ -244,14 +242,24 @@ export async function runWithModelFallback(params) {
|
|
|
244
242
|
};
|
|
245
243
|
}
|
|
246
244
|
catch (err) {
|
|
247
|
-
if (shouldRethrowAbort(err))
|
|
245
|
+
if (shouldRethrowAbort(err)) {
|
|
246
|
+
throw err;
|
|
247
|
+
}
|
|
248
|
+
// Context overflow errors should be handled by the inner runner's
|
|
249
|
+
// compaction/retry logic, not by model fallback. If one escapes as a
|
|
250
|
+
// throw, rethrow it immediately rather than trying a different model
|
|
251
|
+
// that may have a smaller context window and fail worse.
|
|
252
|
+
const errMessage = err instanceof Error ? err.message : String(err);
|
|
253
|
+
if (isLikelyContextOverflowError(errMessage)) {
|
|
248
254
|
throw err;
|
|
255
|
+
}
|
|
249
256
|
const normalized = coerceToFailoverError(err, {
|
|
250
257
|
provider: candidate.provider,
|
|
251
258
|
model: candidate.model,
|
|
252
259
|
}) ?? err;
|
|
253
|
-
if (!isFailoverError(normalized))
|
|
260
|
+
if (!isFailoverError(normalized)) {
|
|
254
261
|
throw err;
|
|
262
|
+
}
|
|
255
263
|
lastError = normalized;
|
|
256
264
|
const described = describeFailoverError(normalized);
|
|
257
265
|
attempts.push({
|
|
@@ -262,14 +270,6 @@ export async function runWithModelFallback(params) {
|
|
|
262
270
|
status: described.status,
|
|
263
271
|
code: described.code,
|
|
264
272
|
});
|
|
265
|
-
// Feed error into provider infrastructure (rate limits + monitoring)
|
|
266
|
-
recordFallbackError({
|
|
267
|
-
provider: candidate.provider,
|
|
268
|
-
model: candidate.model,
|
|
269
|
-
status: described.status,
|
|
270
|
-
reason: described.reason,
|
|
271
|
-
error: described.message,
|
|
272
|
-
});
|
|
273
273
|
await params.onError?.({
|
|
274
274
|
provider: candidate.provider,
|
|
275
275
|
model: candidate.model,
|
|
@@ -279,15 +279,12 @@ export async function runWithModelFallback(params) {
|
|
|
279
279
|
});
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
: "unknown";
|
|
289
|
-
throw new Error(`All models failed (${attempts.length || candidates.length}): ${summary}`, {
|
|
290
|
-
cause: lastError instanceof Error ? lastError : undefined,
|
|
282
|
+
throwFallbackFailureSummary({
|
|
283
|
+
attempts,
|
|
284
|
+
candidates,
|
|
285
|
+
lastError,
|
|
286
|
+
label: "models",
|
|
287
|
+
formatAttempt: (attempt) => `${attempt.provider}/${attempt.model}: ${attempt.error}${attempt.reason ? ` (${attempt.reason})` : ""}`,
|
|
291
288
|
});
|
|
292
289
|
}
|
|
293
290
|
export async function runWithImageModelFallback(params) {
|
|
@@ -313,8 +310,9 @@ export async function runWithImageModelFallback(params) {
|
|
|
313
310
|
};
|
|
314
311
|
}
|
|
315
312
|
catch (err) {
|
|
316
|
-
if (shouldRethrowAbort(err))
|
|
313
|
+
if (shouldRethrowAbort(err)) {
|
|
317
314
|
throw err;
|
|
315
|
+
}
|
|
318
316
|
lastError = err;
|
|
319
317
|
attempts.push({
|
|
320
318
|
provider: candidate.provider,
|
|
@@ -330,14 +328,11 @@ export async function runWithImageModelFallback(params) {
|
|
|
330
328
|
});
|
|
331
329
|
}
|
|
332
330
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
: "unknown";
|
|
340
|
-
throw new Error(`All image models failed (${attempts.length || candidates.length}): ${summary}`, {
|
|
341
|
-
cause: lastError instanceof Error ? lastError : undefined,
|
|
331
|
+
throwFallbackFailureSummary({
|
|
332
|
+
attempts,
|
|
333
|
+
candidates,
|
|
334
|
+
lastError,
|
|
335
|
+
label: "image models",
|
|
336
|
+
formatAttempt: (attempt) => `${attempt.provider}/${attempt.model}: ${attempt.error}`,
|
|
342
337
|
});
|
|
343
338
|
}
|