@bitkyc08/opencodex 2.79.0 → 2.81.0
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/README.md +3 -3
- package/gui/dist/assets/App-7yhoys8a.js +54 -0
- package/gui/dist/assets/Tray-EwAOsP0S.js +1 -0
- package/gui/dist/assets/index-RsoaAxXY.js +94 -0
- package/gui/dist/assets/quota-summary-D2lN8tDm.js +1 -0
- package/gui/dist/index.html +1 -1
- package/gui/dist/provider-icons/claude-green.svg +1 -0
- package/package.json +3 -2
- package/src/adapters/anthropic-model-contract.ts +8 -2
- package/src/adapters/anthropic.ts +7 -2
- package/src/adapters/codebuddy/mcp-server.ts +2 -25
- package/src/adapters/codebuddy/tool-bridge.ts +1 -7
- package/src/adapters/coding-agent/mcp-server.ts +33 -0
- package/src/adapters/coding-agent/protocol.ts +122 -17
- package/src/adapters/coding-agent/tool-catalog.ts +75 -0
- package/src/adapters/coding-agent/turn.ts +101 -44
- package/src/adapters/cursor/catalog.ts +8 -0
- package/src/adapters/cursor/effort-map.ts +2 -0
- package/src/adapters/cursor/live-transport.ts +5 -3
- package/src/adapters/cursor/native-exec-fs.ts +12 -9
- package/src/adapters/cursor/native-exec.ts +13 -5
- package/src/adapters/cursor/protobuf-request.ts +13 -7
- package/src/adapters/cursor/request-builder.ts +23 -10
- package/src/adapters/cursor/tool-definitions.ts +9 -6
- package/src/adapters/cursor/tool-guidance.ts +29 -8
- package/src/adapters/cursor/tool-naming.ts +40 -7
- package/src/adapters/cursor/tool-wording.ts +46 -0
- package/src/adapters/cursor.ts +3 -0
- package/src/adapters/devin/live-models.ts +2 -0
- package/src/adapters/devin.ts +25 -11
- package/src/adapters/kiro-retry.ts +9 -4
- package/src/adapters/openai-chat/messages.ts +7 -4
- package/src/adapters/physical-send.ts +8 -5
- package/src/adapters/qoder/adapter.ts +41 -6
- package/src/adapters/qoder/scaffold-guard.ts +2 -3
- package/src/adapters/zed.ts +16 -1
- package/src/claude/inbound-cache-stabilize.ts +4 -9
- package/src/claude/inbound.ts +10 -0
- package/src/claude/intercept/model-bindings.ts +1 -0
- package/src/claude/intercept/picker-listener.ts +71 -4
- package/src/cli/account-anthropic-threshold.ts +14 -5
- package/src/cli/account-api.ts +3 -1
- package/src/cli/account-extended.ts +13 -9
- package/src/cli/account-policy-dto.ts +1 -1
- package/src/cli/account-policy.ts +13 -4
- package/src/cli/account.ts +3 -3
- package/src/cli/capabilities-accounts.ts +2 -2
- package/src/cli/capabilities-agents-routing.ts +24 -0
- package/src/cli/capabilities-integrations.ts +16 -0
- package/src/cli/codex-shim-autorestore.ts +3 -0
- package/src/cli/dispatch.ts +10 -1
- package/src/cli/help.ts +4 -1
- package/src/cli/index.ts +11 -5
- package/src/cli/integration-plan-dto.ts +1 -0
- package/src/cli/integration-preview.ts +8 -1
- package/src/cli/integrations.ts +32 -3
- package/src/cli/message-args.ts +34 -0
- package/src/cli/message-command.ts +43 -0
- package/src/cli/message-runtime.ts +6 -0
- package/src/cli/observe.ts +3 -1
- package/src/cli/provider.ts +29 -6
- package/src/cli/registry.ts +24 -2
- package/src/clients/config-export/commandcode.ts +131 -0
- package/src/clients/config-export/contracts.ts +12 -0
- package/src/clients/config-export.ts +64 -1
- package/src/codex/auth-api/account-list.ts +11 -3
- package/src/codex/auth-api/main-account-probe.ts +25 -8
- package/src/codex/auth-api/pool-mode-gate.ts +33 -9
- package/src/codex/auth-api/pool-quota-probe.ts +15 -1
- package/src/codex/auth-api/routes.ts +1 -0
- package/src/codex/autostart-health.ts +3 -3
- package/src/codex/catalog/auto-review.ts +13 -6
- package/src/codex/catalog/build-entries.ts +15 -7
- package/src/codex/catalog/control-plane.ts +45 -0
- package/src/codex/catalog/effort.ts +11 -1
- package/src/codex/catalog/gather-capture.ts +5 -3
- package/src/codex/catalog/parsing.ts +2 -1
- package/src/codex/catalog/provider-models.ts +15 -9
- package/src/codex/control-plane-models.ts +6 -0
- package/src/codex/journal.ts +36 -5
- package/src/codex/shim-overlay.ts +9 -1
- package/src/combos/jev-decision-contract.ts +25 -2
- package/src/combos/jev.ts +6 -3
- package/src/combos/types.ts +1 -1
- package/src/config/diagnostics.ts +27 -16
- package/src/config/load-degrade.ts +34 -0
- package/src/config/schema/anthropic-account-pool.ts +143 -0
- package/src/config/schema/config-schema.ts +7 -1
- package/src/config/schema/leaf-validators.ts +4 -0
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +347 -199
- package/src/generated/model-metadata.ts +5 -4
- package/src/images/loop.ts +2 -1
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/mutation-plan.ts +7 -0
- package/src/integrations/registry.ts +8 -0
- package/src/integrations/state.ts +9 -2
- package/src/integrations/writer.ts +8 -2
- package/src/lib/lab-live-route-production.ts +9 -1
- package/src/lib/request-execution-budget.ts +236 -58
- package/src/lib/spend-pool-alias-validation.ts +79 -0
- package/src/lib/spend-pool-continuity.ts +34 -0
- package/src/lib/spend-reservation-ledger.ts +412 -50
- package/src/lib/state-store-registrations.ts +20 -3
- package/src/lib/upstream-retry.ts +17 -5
- package/src/lib/workflow-budget.ts +53 -11
- package/src/messaging/budget.ts +45 -0
- package/src/messaging/discovery.ts +57 -0
- package/src/messaging/envelope.ts +49 -0
- package/src/messaging/input.ts +37 -0
- package/src/messaging/rpc.ts +196 -0
- package/src/messaging/send.ts +58 -0
- package/src/messaging/socket.ts +72 -0
- package/src/messaging/types.ts +29 -0
- package/src/oauth/anthropic-account-refusal.ts +51 -17
- package/src/oauth/anthropic-account-threshold.ts +8 -2
- package/src/oauth/anthropic-continuity.ts +15 -1
- package/src/oauth/anthropic-model-quota.ts +120 -48
- package/src/oauth/anthropic-model-routes.ts +11 -3
- package/src/oauth/anthropic-oauth-definitions.ts +98 -0
- package/src/oauth/anthropic-pool-config.ts +30 -0
- package/src/oauth/anthropic-rate-limit-policy.ts +64 -9
- package/src/oauth/anthropic-routing.ts +1202 -866
- package/src/oauth/anthropic-send-ownership.ts +41 -0
- package/src/oauth/anthropic.ts +4 -1
- package/src/oauth/generic-account-failover.ts +3 -1
- package/src/oauth/health.ts +8 -6
- package/src/oauth/index.ts +90 -173
- package/src/oauth/kiro.ts +22 -9
- package/src/oauth/login-cli.ts +2 -0
- package/src/oauth/model-discovery-auth.ts +43 -0
- package/src/oauth/pool-kernel.ts +11 -5
- package/src/oauth/pool-settings-capability.ts +5 -3
- package/src/oauth/store-anthropic-instance.ts +87 -0
- package/src/oauth/store.ts +67 -9
- package/src/oauth/token-guardian.ts +2 -0
- package/src/oauth/types.ts +2 -0
- package/src/protocols/plan-snapshot.ts +5 -0
- package/src/protocols/settings.ts +20 -5
- package/src/providers/anthropic-instance-id.ts +47 -0
- package/src/providers/anthropic-instance.ts +48 -0
- package/src/providers/anthropic-reset-grant-ledger.ts +11 -1
- package/src/providers/derive.ts +2 -0
- package/src/providers/kiro-models.ts +3 -0
- package/src/providers/label.ts +1 -1
- package/src/providers/model-rename-fields.ts +2 -0
- package/src/providers/quota/account-cache.ts +37 -11
- package/src/providers/quota/anthropic-account-quota.ts +93 -0
- package/src/providers/quota/anthropic-cooldown-recovery.ts +222 -111
- package/src/providers/quota/report-cache.ts +24 -1
- package/src/providers/quota/vendor-probes-oauth.ts +46 -14
- package/src/providers/quota.ts +22 -32
- package/src/providers/registry/entries-core.ts +35 -30
- package/src/providers/registry/model-ids.ts +1 -0
- package/src/providers/registry/model-seeds.ts +7 -4
- package/src/providers/registry/types.ts +3 -0
- package/src/providers/registry.ts +3 -3
- package/src/responses/spill-store.ts +5 -1
- package/src/responses/state/metrics.ts +87 -0
- package/src/responses/state/snapshot-codec.ts +6 -0
- package/src/responses/state/spill-queue.ts +1 -0
- package/src/responses/state/unforced-store-false.ts +55 -0
- package/src/responses/state.ts +50 -66
- package/src/router.ts +28 -4
- package/src/routing/compatibility/assemble.ts +1 -1
- package/src/routing/quota.ts +7 -3
- package/src/server/auth-cors.ts +5 -0
- package/src/server/chat-completions.ts +6 -1
- package/src/server/chat-native.ts +45 -25
- package/src/server/claude-messages.ts +16 -2
- package/src/server/gui-static.ts +38 -4
- package/src/server/index/spend-ledger-lifecycle.ts +10 -4
- package/src/server/index.ts +16 -16
- package/src/server/inference/context.ts +1 -1
- package/src/server/lifecycle.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +30 -6
- package/src/server/management/anthropic-account-threshold.ts +12 -9
- package/src/server/management/anthropic-pool-settings.ts +72 -0
- package/src/server/management/anthropic-reset-grant-routes.ts +98 -39
- package/src/server/management/config-routes.ts +27 -7
- package/src/server/management/decision-routes.ts +2 -1
- package/src/server/management/integration-routes.ts +25 -2
- package/src/server/management/oauth-account-routes.ts +79 -98
- package/src/server/management/provider-instance-ownership.ts +44 -0
- package/src/server/management/provider-routes.ts +21 -28
- package/src/server/management/route-registry.ts +3 -1
- package/src/server/management/routing-profile-routes.ts +4 -4
- package/src/server/management/system-restart.ts +44 -14
- package/src/server/management/system-routes.ts +24 -2
- package/src/server/management/vision-sidecar-options.ts +9 -6
- package/src/server/management/web-search-sidecar-options.ts +51 -2
- package/src/server/management-auth.ts +6 -0
- package/src/server/messages-native-eligibility.ts +3 -2
- package/src/server/messages-native-oauth.ts +86 -41
- package/src/server/messages-native-scope.ts +4 -1
- package/src/server/messages-native-selector.ts +39 -0
- package/src/server/messages-native.ts +84 -25
- package/src/server/request-log.ts +5 -0
- package/src/server/responses/adapter-continuation.ts +29 -17
- package/src/server/responses/adapter-dispatch.ts +47 -17
- package/src/server/responses/antigravity-validation-refusal.ts +20 -6
- package/src/server/responses/combo-requested-effort.ts +14 -0
- package/src/server/responses/compact.ts +25 -8
- package/src/server/responses/compaction-recovery.ts +1 -1
- package/src/server/responses/core-combo-native.ts +4 -1
- package/src/server/responses/core-combo.ts +21 -18
- package/src/server/responses/core-normalize.ts +1 -0
- package/src/server/responses/core-options.ts +3 -1
- package/src/server/responses/core-replay.ts +5 -0
- package/src/server/responses/passthrough-delivery.ts +7 -3
- package/src/server/responses/passthrough-dispatch.ts +21 -8
- package/src/server/responses/request-prepare.ts +37 -5
- package/src/server/responses/request-send-budget.ts +74 -37
- package/src/server/responses/request-spend.ts +325 -33
- package/src/server/responses/request-transport.ts +132 -52
- package/src/server/responses/run-turn-execution.ts +11 -3
- package/src/server/responses/sidecar-execution.ts +96 -14
- package/src/server/responses-undeclared-tool-guard.ts +5 -1
- package/src/server/workflow-refusal.ts +30 -0
- package/src/service/desktop-startup.ts +100 -3
- package/src/service/ownership-mutation-lease.d.mts +1 -1
- package/src/service/ownership-mutation-lease.mjs +5 -7
- package/src/sidecar/anthropic-binding.ts +79 -0
- package/src/sidecar/auth.ts +76 -2
- package/src/types/anthropic-account-pool.ts +33 -0
- package/src/types/config.ts +12 -28
- package/src/types/provider.ts +5 -0
- package/src/types/tools.ts +7 -0
- package/src/types.ts +4 -0
- package/src/usage/cost.ts +3 -2
- package/src/usage/expected-prices.ts +66 -14
- package/src/vision/anthropic-describe.ts +16 -3
- package/src/vision/index.ts +3 -0
- package/src/vision/plan.ts +19 -6
- package/src/web-search/alpha-search.ts +7 -1
- package/src/web-search/anthropic-executor.ts +16 -4
- package/src/web-search/header-deadline.ts +67 -0
- package/src/web-search/index.ts +7 -3
- package/src/web-search/loop.ts +32 -10
- package/src/web-search/passthrough-bridge.ts +4 -1
- package/src/web-search/sidecar-providers.ts +4 -3
- package/gui/dist/assets/App-BOUa-ek3.js +0 -54
- package/gui/dist/assets/Tray-DUtzB4zF.js +0 -1
- package/gui/dist/assets/index-DwHNDF4s.js +0 -94
- package/gui/dist/assets/quota-summary-BuL4WIBo.js +0 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { clearableDeadline } from "../lib/abort";
|
|
2
|
+
import { sendTrackingRequestSlot } from "../providers/request-pacing";
|
|
3
|
+
import type { ProviderFetch } from "../server/responses/fetch-helpers";
|
|
4
|
+
|
|
5
|
+
/** One iteration's cumulative header budget; only local pacing admission pauses its clock. */
|
|
6
|
+
export function pacingHeaderDeadline(timeoutMs: number, parent: AbortSignal) {
|
|
7
|
+
const controller = new AbortController();
|
|
8
|
+
const signal = AbortSignal.any([parent, controller.signal]);
|
|
9
|
+
let remainingMs = timeoutMs;
|
|
10
|
+
let armedAt = performance.now();
|
|
11
|
+
let current = clearableDeadline(remainingMs, parent);
|
|
12
|
+
const timeoutReason = current.timeoutReason;
|
|
13
|
+
let pacingWaits = 0;
|
|
14
|
+
let cleared = false;
|
|
15
|
+
const relayExpiry = () => {
|
|
16
|
+
if (current.didExpire()) controller.abort(timeoutReason);
|
|
17
|
+
};
|
|
18
|
+
current.signal.addEventListener("abort", relayExpiry, { once: true });
|
|
19
|
+
|
|
20
|
+
const pause = () => {
|
|
21
|
+
if (cleared || pacingWaits++ > 0) return;
|
|
22
|
+
remainingMs = Math.max(0, remainingMs - (performance.now() - armedAt));
|
|
23
|
+
current.clear();
|
|
24
|
+
current.signal.removeEventListener("abort", relayExpiry);
|
|
25
|
+
// A delayed timer task must not let an already-spent budget escape into the queue.
|
|
26
|
+
if (remainingMs === 0 && !signal.aborted) controller.abort(timeoutReason);
|
|
27
|
+
};
|
|
28
|
+
const resume = () => {
|
|
29
|
+
if (cleared || --pacingWaits > 0 || signal.aborted) return;
|
|
30
|
+
armedAt = performance.now();
|
|
31
|
+
current = clearableDeadline(remainingMs, parent);
|
|
32
|
+
current.signal.addEventListener("abort", relayExpiry, { once: true });
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
signal,
|
|
37
|
+
didExpire: () => signal.aborted && signal.reason === timeoutReason,
|
|
38
|
+
clear: () => {
|
|
39
|
+
cleared = true;
|
|
40
|
+
current.clear();
|
|
41
|
+
current.signal.removeEventListener("abort", relayExpiry);
|
|
42
|
+
},
|
|
43
|
+
/** Preserve the pacing seams used by adapter physical-send helpers, without double admission. */
|
|
44
|
+
pacedFetch(executor: typeof globalThis.fetch): typeof globalThis.fetch {
|
|
45
|
+
const pacing = executor as ProviderFetch;
|
|
46
|
+
if (!pacing.waitForPacing) return executor;
|
|
47
|
+
const waitForPacing: NonNullable<ProviderFetch["waitForPacing"]> = async (sendSignal) => {
|
|
48
|
+
pause();
|
|
49
|
+
try {
|
|
50
|
+
return await pacing.waitForPacing!(sendSignal ? AbortSignal.any([signal, sendSignal]) : signal);
|
|
51
|
+
} finally {
|
|
52
|
+
resume();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const unpacedFetch = pacing.unpacedFetch ?? executor;
|
|
56
|
+
const wrapped = async (input: Parameters<typeof fetch>[0], init?: RequestInit) => {
|
|
57
|
+
const slot = await waitForPacing(init?.signal ?? undefined);
|
|
58
|
+
return sendTrackingRequestSlot(slot, () => {
|
|
59
|
+
signal.throwIfAborted();
|
|
60
|
+
init?.signal?.throwIfAborted();
|
|
61
|
+
return unpacedFetch(input, init);
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
return Object.assign(wrapped, { preconnect: executor.preconnect, waitForPacing, unpacedFetch }) as ProviderFetch;
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
package/src/web-search/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { CodexAuthPolicyConfig } from "../codex/auth-context";
|
|
|
6
6
|
import { isCodexReserveRequestEligible } from "../codex/loopback-target";
|
|
7
7
|
import type { DataPlaneAdmission } from "../server/auth-cors";
|
|
8
8
|
import type { ResolvedOpenAiForwardSidecar } from "../providers/openai-sidecar";
|
|
9
|
-
import { resolveSidecarAuth } from "../sidecar/auth";
|
|
9
|
+
import { resolveSidecarAuth, resolveAnthropicHelperInstance, withAnthropicHelperRefusal } from "../sidecar/auth";
|
|
10
10
|
import { validateXaiSearchOptions, type XaiSearchOptions } from "./xai-executor";
|
|
11
11
|
import type { OcxWebSearchSidecarConfig } from "../types";
|
|
12
12
|
import { DEFAULT_STALL_TIMEOUT_SEC } from "../stall-timeout";
|
|
@@ -168,11 +168,15 @@ export function planWebSearch(
|
|
|
168
168
|
const connectTimeoutMs = config.connectTimeoutMs ?? 200_000;
|
|
169
169
|
// Shared auth state (#2188): presence only — backend PREFERENCE stays with
|
|
170
170
|
// resolveSidecarBackend's explicit-or-openai contract.
|
|
171
|
-
const
|
|
171
|
+
const backend = resolveSidecarBackend(cfg.backend);
|
|
172
|
+
// Only the anthropic backend can be refused; an unavailable selected pool means no plan, so the
|
|
173
|
+
// main request takes the normal path instead of failing or borrowing another pool.
|
|
174
|
+
const auth = withAnthropicHelperRefusal("web-search", () => resolveSidecarAuth(config, resolveAnthropicHelperInstance(config,
|
|
175
|
+
{ backendFamily: backend, anthropicInstance: cfg.anthropicInstance, parentProviderName: options.providerName })));
|
|
176
|
+
if (!auth) return undefined;
|
|
172
177
|
const anthropicSidecar = auth.isAnthropicAuth && auth.anthropicProviderName && auth.anthropicProvider
|
|
173
178
|
? { providerName: auth.anthropicProviderName, provider: auth.anthropicProvider, config }
|
|
174
179
|
: undefined;
|
|
175
|
-
const backend = resolveSidecarBackend(cfg.backend);
|
|
176
180
|
const maxSearches = cfg.maxSearchesPerTurn ?? DEFAULT_MAX_SEARCHES;
|
|
177
181
|
const stallTimeoutSec = webSearchStallTimeoutSec(
|
|
178
182
|
config.stallTimeoutSec,
|
package/src/web-search/loop.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AdapterRequest, IncomingMeta, ProviderAdapter } from "../adapters/base";
|
|
2
2
|
import type { AdapterEvent, OcxConfig, OcxMessage, OcxParsedRequest, OcxProviderConfig, OcxProviderOpaqueToolCallMetadata, OcxThinkingContent, OcxUsage, RateLimitRetryPolicy } from "../types";
|
|
3
3
|
import { namespacedToolName, toolChoiceToolPredicate } from "../types";
|
|
4
|
+
import { isAnthropicInstanceId } from "../providers/anthropic-instance-id";
|
|
4
5
|
import { cloneProviderOpaqueToolCallMetadata } from "../responses/provider-opaque-metadata";
|
|
5
6
|
import type { AttemptRecoveryKind } from "../usage/log";
|
|
6
7
|
import { isTruncatedStopReason } from "../responses/truncated-stop-reason";
|
|
@@ -11,7 +12,7 @@ import { runXaiWebSearch, type XaiSearchOptions } from "./xai-executor";
|
|
|
11
12
|
import { runGeminiWebSearch } from "./gemini-executor";
|
|
12
13
|
import { runExaWebSearch } from "./exa-executor";
|
|
13
14
|
import type { WebSearchBackendId } from "./index";
|
|
14
|
-
import {
|
|
15
|
+
import { pacingHeaderDeadline } from "./header-deadline";
|
|
15
16
|
import { redactSecretString } from "../lib/redact";
|
|
16
17
|
import { readBoundedResponseBody } from "../lib/bounded-body";
|
|
17
18
|
import { applyUpstreamRecoveryInit, fetchWithResetRetry, prepareSameTarget429Wait } from "../lib/upstream-retry";
|
|
@@ -353,6 +354,8 @@ export interface WebSearchLoopDeps {
|
|
|
353
354
|
| { adapter: ProviderAdapter; recoveryKind: AttemptRecoveryKind }
|
|
354
355
|
| null
|
|
355
356
|
| Promise<{ adapter: ProviderAdapter; recoveryKind: AttemptRecoveryKind } | null>;
|
|
357
|
+
/** Release a credential-hop permit if this iteration ended before the adapter used it. */
|
|
358
|
+
onIterationEnd?: () => void;
|
|
356
359
|
/** Opt-in same-target 429 policy (key-auth providers). When present, 429 replays on the SAME key before on429 rotation. */
|
|
357
360
|
retryOn429Policy?: Required<RateLimitRetryPolicy> | null;
|
|
358
361
|
/** Called only when the final bridged Responses stream reaches completed or incomplete. */
|
|
@@ -455,10 +458,10 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
|
|
|
455
458
|
...(recoveringEmptyAnswer ? { options: { ...parsed.options, toolChoice: "none" as const } } : {}),
|
|
456
459
|
context: { ...parsed.context, messages: iterMessages, tools: recoveringEmptyAnswer ? [] : allTools },
|
|
457
460
|
};
|
|
458
|
-
// One cumulative header deadline spans
|
|
461
|
+
// One cumulative header deadline spans key rotations and reset sends, excluding local pacing.
|
|
459
462
|
// clear() stops only its timer after final headers; the direct turn signal remains attached to
|
|
460
463
|
// the returned response body through AbortSignal.any().
|
|
461
|
-
let headerDeadline =
|
|
464
|
+
let headerDeadline = pacingHeaderDeadline(connectTimeoutMs, signal);
|
|
462
465
|
try {
|
|
463
466
|
/**
|
|
464
467
|
* Build and fetch one web-search iteration on the given adapter, under the iteration
|
|
@@ -491,7 +494,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
|
|
|
491
494
|
cachedRequest = request;
|
|
492
495
|
cachedAdapter = requestAdapter;
|
|
493
496
|
}
|
|
494
|
-
const requestFetch = deps.fetchForRequest?.(request, iterParsed) ?? routedProviderFetch;
|
|
497
|
+
const requestFetch = headerDeadline.pacedFetch(deps.fetchForRequest?.(request, iterParsed) ?? routedProviderFetch);
|
|
495
498
|
let response: Response;
|
|
496
499
|
try {
|
|
497
500
|
if (requestAdapter.fetchResponse) {
|
|
@@ -501,6 +504,8 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
|
|
|
501
504
|
timeoutMs: connectTimeoutMs,
|
|
502
505
|
returnRawErrors: true,
|
|
503
506
|
stream: true,
|
|
507
|
+
sendBudget: deps.incomingMeta.sendBudget,
|
|
508
|
+
onPhysicalSend: deps.incomingMeta.onPhysicalSend,
|
|
504
509
|
executor: requestFetch,
|
|
505
510
|
});
|
|
506
511
|
} else {
|
|
@@ -563,7 +568,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
|
|
|
563
568
|
// The deliberate backoff must not consume the cumulative response-header deadline:
|
|
564
569
|
// start a fresh one so the replay gets a new connect budget (504 stays reserved for real
|
|
565
570
|
// upstream latency).
|
|
566
|
-
headerDeadline =
|
|
571
|
+
headerDeadline = pacingHeaderDeadline(connectTimeoutMs, signal);
|
|
567
572
|
// Stall-watchdog seam between bounded retry fetches.
|
|
568
573
|
yield { type: "heartbeat" };
|
|
569
574
|
prepared = await fetchOnce(adapter, "rate-limit-429");
|
|
@@ -571,18 +576,34 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
|
|
|
571
576
|
// 429 key-failover parity with the normal routed path: rotate pool keys until one responds
|
|
572
577
|
// or the pool is exhausted (deps.on429 returns null — cooldown map guarantees termination).
|
|
573
578
|
while ((prepared.response.status === 429
|
|
574
|
-
|| (prepared.response.status === 403
|
|
579
|
+
|| (prepared.response.status === 403
|
|
580
|
+
&& (isAnthropicInstanceId(deps.incomingMeta?.providerName) || deps.incomingMeta?.providerName === "google-antigravity")
|
|
581
|
+
&& !accountRefusalOutputStarted)
|
|
575
582
|
|| (iterParsed._kiroAuthContext && (prepared.response.status === 400 || prepared.response.status === 403))) && deps.on429) {
|
|
576
583
|
const rotated = await deps.on429(prepared.response.headers.get("retry-after"), prepared.response.headers,
|
|
577
584
|
iterParsed, prepared.response);
|
|
578
585
|
if (!rotated) break;
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
586
|
+
const retainRefusal = prepared.response.status === 403
|
|
587
|
+
&& deps.incomingMeta?.providerName === "google-antigravity" && rotated.recoveryKind === "oauth-account-403";
|
|
588
|
+
// Keep the bounded refusal readable until the sibling actually returns headers.
|
|
589
|
+
// Other rotation paths retain their existing cancellation boundary.
|
|
590
|
+
if (!retainRefusal) {
|
|
591
|
+
try { void prepared.response.body?.cancel().catch(() => {}); } catch { /* already closed */ }
|
|
592
|
+
}
|
|
582
593
|
adapter = rotated.adapter;
|
|
583
594
|
// Stall-watchdog seam between bounded retry fetches (audit 011 B3).
|
|
584
595
|
yield { type: "heartbeat" };
|
|
585
|
-
|
|
596
|
+
let replacement: IterationResponse;
|
|
597
|
+
try {
|
|
598
|
+
replacement = await fetchOnce(adapter, rotated.recoveryKind);
|
|
599
|
+
} catch (error) {
|
|
600
|
+
if (!retainRefusal || signal.aborted || headerDeadline.didExpire() || isTranslatorBudgetExceededError(error)) throw error;
|
|
601
|
+
break; // Format the original 403 safely; replacement diagnostics are not client errors.
|
|
602
|
+
}
|
|
603
|
+
if (retainRefusal) {
|
|
604
|
+
try { void prepared.response.body?.cancel().catch(() => {}); } catch { /* already closed */ }
|
|
605
|
+
}
|
|
606
|
+
prepared = replacement;
|
|
586
607
|
}
|
|
587
608
|
|
|
588
609
|
// Final headers have arrived. Clear only the deadline timer before ANY body read.
|
|
@@ -623,6 +644,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
|
|
|
623
644
|
throw new LoopError(502, `Provider unreachable: ${error instanceof Error ? error.message : String(error)}`);
|
|
624
645
|
} finally {
|
|
625
646
|
headerDeadline.clear();
|
|
647
|
+
deps.onIterationEnd?.();
|
|
626
648
|
}
|
|
627
649
|
};
|
|
628
650
|
|
|
@@ -76,6 +76,7 @@ import {
|
|
|
76
76
|
import { providerDestinationConfigError } from "../lib/destination-policy";
|
|
77
77
|
import { redactSecretString } from "../lib/redact";
|
|
78
78
|
import { rememberBridgeSearchReplay } from "../responses/bridge-search-replay-cache";
|
|
79
|
+
import { withAnthropicHelperRefusal } from "../sidecar/auth";
|
|
79
80
|
|
|
80
81
|
/** Canonical Ollama Cloud origin. The only origin the "ollama" backend derives on its own. */
|
|
81
82
|
export const OLLAMA_CLOUD_ORIGIN = "https://ollama.com";
|
|
@@ -246,12 +247,14 @@ export function resolvePassthroughWebSearchBridgeAuth(
|
|
|
246
247
|
backend: ProviderWebSearchBridgeBackend | undefined,
|
|
247
248
|
config: OcxConfig,
|
|
248
249
|
openAiSidecar?: ResolvedOpenAiForwardSidecar,
|
|
250
|
+
parentProviderName?: string,
|
|
249
251
|
): PassthroughWebSearchBridgeAuth {
|
|
250
252
|
switch (backend) {
|
|
251
253
|
case "openai":
|
|
252
254
|
return openAiSidecar ? { openAiSidecar } : {};
|
|
253
255
|
case "anthropic": {
|
|
254
|
-
|
|
256
|
+
// An unavailable selected pool leaves the bridge disarmed; it never fails the passthrough turn.
|
|
257
|
+
const anthropic = withAnthropicHelperRefusal("web-search", () => findAnthropicSidecarProvider(config, parentProviderName));
|
|
255
258
|
return anthropic ? { anthropic } : {};
|
|
256
259
|
}
|
|
257
260
|
case "xai": {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* evaluating when the bridge asks for these names).
|
|
6
6
|
*/
|
|
7
7
|
import type { OcxConfig, OcxProviderConfig, OcxWebSearchSidecarConfig } from "../types";
|
|
8
|
-
import { resolveSidecarAuth } from "../sidecar/auth";
|
|
8
|
+
import { resolveSidecarAuth, resolveAnthropicHelperInstance } from "../sidecar/auth";
|
|
9
9
|
import { getAccountSet } from "../oauth/store";
|
|
10
10
|
import type { XaiSearchOptions } from "./xai-executor";
|
|
11
11
|
|
|
@@ -46,8 +46,9 @@ export interface AnthropicSidecarProvider {
|
|
|
46
46
|
* Delegates to the shared sidecar auth module (#2188) so web-search and vision
|
|
47
47
|
* cannot drift on what "Anthropic auth present" means.
|
|
48
48
|
*/
|
|
49
|
-
export function findAnthropicSidecarProvider(config: OcxConfig): AnthropicSidecarProvider | undefined {
|
|
50
|
-
const
|
|
49
|
+
export function findAnthropicSidecarProvider(config: OcxConfig, parentProviderName?: string): AnthropicSidecarProvider | undefined {
|
|
50
|
+
const instance = resolveAnthropicHelperInstance(config, { backendFamily: "anthropic", anthropicInstance: config.webSearchSidecar?.anthropicInstance, parentProviderName });
|
|
51
|
+
const auth = resolveSidecarAuth(config, instance);
|
|
51
52
|
if (!auth.isAnthropicAuth || !auth.anthropicProviderName || !auth.anthropicProvider) return undefined;
|
|
52
53
|
return { providerName: auth.anthropicProviderName, provider: auth.anthropicProvider, config };
|
|
53
54
|
}
|