@bitkyc08/opencodex 2.86.0 → 2.88.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/gui/dist/assets/App-CHC89EXQ.js +58 -0
- package/gui/dist/assets/App-wT3mXqFz.css +1 -0
- package/gui/dist/assets/{Tray-DyR9yBee.js → Tray-B5bC7AWL.js} +1 -1
- package/gui/dist/assets/index-B5bsvxor.js +94 -0
- package/gui/dist/assets/index-DPeB9HpV.css +1 -0
- package/gui/dist/assets/quota-summary-D8y2wTsB.js +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic/beta-allowlist.ts +9 -8
- package/src/adapters/anthropic/beta-body-pairs.ts +93 -0
- package/src/adapters/anthropic/passthrough.ts +7 -23
- package/src/adapters/anthropic-progress.ts +84 -0
- package/src/adapters/anthropic-rejection-memo.ts +71 -0
- package/src/adapters/anthropic-replay.ts +135 -0
- package/src/adapters/anthropic-tool-catalog.ts +11 -0
- package/src/adapters/anthropic.ts +151 -33
- package/src/adapters/base.ts +2 -0
- package/src/adapters/claude-cli/adapter.ts +111 -23
- package/src/adapters/claude-cli/profiles.ts +2 -1
- package/src/adapters/claude-cli/stable-replay.ts +108 -0
- package/src/adapters/claude-cli/usage-admission.ts +230 -0
- package/src/adapters/coding-agent/protocol.ts +1 -1
- package/src/adapters/coding-agent/turn.ts +18 -2
- package/src/adapters/dsh-account.ts +97 -0
- package/src/adapters/google-progress-history.ts +53 -0
- package/src/adapters/google.ts +52 -9
- package/src/adapters/openai-chat/messages.ts +4 -3
- package/src/adapters/openai-chat/passthrough.ts +8 -2
- package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
- package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
- package/src/adapters/openai-responses/passthrough.ts +57 -125
- package/src/adapters/openai-responses/request-strips.ts +25 -3
- package/src/adapters/openai-responses/transport.ts +81 -0
- package/src/adapters/qoder/adapter.ts +4 -2
- package/src/adapters/registry.ts +5 -0
- package/src/adapters/zed.ts +26 -1
- package/src/advisor/activity.ts +55 -0
- package/src/advisor/budget.ts +62 -0
- package/src/advisor/consult.ts +4 -1
- package/src/advisor/context.ts +16 -4
- package/src/advisor/evidence-tool.ts +21 -0
- package/src/advisor/evidence.ts +117 -0
- package/src/advisor/runtime.ts +79 -31
- package/src/advisor/settings.ts +11 -2
- package/src/bridge/errors.ts +19 -1
- package/src/bridge/internal.ts +7 -1
- package/src/bridge/response-json.ts +18 -3
- package/src/bridge/sse.ts +59 -8
- package/src/bridge/terminal-refusal.ts +59 -0
- package/src/claude/desktop-3p-library.ts +32 -7
- package/src/claude/desktop-3p.ts +55 -13
- package/src/claude/desktop-first-party.ts +1 -1
- package/src/claude/desktop-picker-platform.ts +4 -0
- package/src/claude/desktop-picker.ts +15 -8
- package/src/claude/desktop-system-proxy.ts +50 -4
- package/src/claude/inbound-content-options.ts +77 -0
- package/src/claude/inbound.ts +33 -0
- package/src/claude/intercept/picker-ca-startup.ts +2 -1
- package/src/claude/intercept/picker-runtime.ts +6 -5
- package/src/claude/intercept/picker-trust-windows.ts +338 -0
- package/src/claude/intercept/picker-trust.ts +19 -0
- package/src/claude/intercept/port-policy.ts +30 -0
- package/src/claude/intercept/runtime.ts +97 -94
- package/src/claude/outbound.ts +46 -13
- package/src/cli/account-api.ts +1 -0
- package/src/cli/account-policy-dto.ts +6 -0
- package/src/cli/account-policy.ts +34 -5
- package/src/cli/account-preservation.ts +44 -0
- package/src/cli/account.ts +8 -2
- package/src/cli/advisor.ts +17 -3
- package/src/cli/capabilities-accounts.ts +20 -1
- package/src/cli/capabilities-agents-routing.ts +5 -2
- package/src/cli/capabilities-base.ts +38 -10
- package/src/cli/capabilities-observe-system.ts +63 -3
- package/src/cli/capabilities-provider-models.ts +11 -0
- package/src/cli/codex-cli-update.ts +151 -41
- package/src/cli/codex-provider.ts +44 -0
- package/src/cli/combo-input.ts +10 -1
- package/src/cli/combo-result.ts +1 -1
- package/src/cli/combo.ts +2 -1
- package/src/cli/cross-home-owner.ts +33 -16
- package/src/cli/dispatch.ts +25 -4
- package/src/cli/doctor.ts +6 -1
- package/src/cli/ensure-desktop-cold-start.ts +60 -7
- package/src/cli/help.ts +3 -2
- package/src/cli/index.ts +1 -1
- package/src/cli/integration-plan-dto.ts +1 -0
- package/src/cli/log-history.ts +172 -0
- package/src/cli/loopback-family.ts +31 -0
- package/src/cli/models-runtime-subcommands.ts +1 -0
- package/src/cli/models-runtime.ts +72 -8
- package/src/cli/observe-stream.ts +2 -0
- package/src/cli/observe.ts +16 -4
- package/src/cli/provider.ts +27 -5
- package/src/cli/registry.ts +28 -8
- package/src/cli/runtime-api.ts +5 -3
- package/src/cli/system-command.ts +40 -8
- package/src/cli/uninstall-client-state.ts +32 -0
- package/src/cli/usage-compare.ts +309 -0
- package/src/cli/usage-model-search.ts +4 -4
- package/src/cli/usage-report.ts +2 -2
- package/src/cli/v2.ts +1 -1
- package/src/client/machine-listener.ts +1 -3
- package/src/client/runtime.ts +1 -0
- package/src/clients/client-path.ts +51 -0
- package/src/clients/config-export/contracts.ts +2 -1
- package/src/clients/config-export.ts +18 -44
- package/src/clients/gentle-shell-path.ts +87 -0
- package/src/codex/account-auto-switch.ts +2 -0
- package/src/codex/account-identity.ts +29 -0
- package/src/codex/account-label.ts +1 -1
- package/src/codex/account-lifecycle.ts +21 -5
- package/src/codex/account-preservation.ts +28 -0
- package/src/codex/account-use-remaining.ts +23 -0
- package/src/codex/app-server-processes.ts +97 -7
- package/src/codex/app-server-restart-service.ts +55 -4
- package/src/codex/auth-api/account-list.ts +4 -0
- package/src/codex/auth-api/account-pause-group.ts +6 -50
- package/src/codex/auth-api/account-preservation.ts +57 -0
- package/src/codex/auth-api/routes.ts +8 -0
- package/src/codex/auth-api/use-remaining-route.ts +36 -0
- package/src/codex/auth-api.ts +0 -1
- package/src/codex/auth-context.ts +2 -2
- package/src/codex/catalog/auto-review.ts +20 -3
- package/src/codex/catalog/build-entries.ts +9 -1
- package/src/codex/catalog/combo-member.ts +14 -5
- package/src/codex/catalog/configured-tiers.ts +1 -1
- package/src/codex/catalog/gather-capture.ts +2 -0
- package/src/codex/catalog/metadata.ts +11 -9
- package/src/codex/catalog/model-hints.ts +21 -16
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/parsing.ts +30 -86
- package/src/codex/catalog/provider-models.ts +36 -34
- package/src/codex/catalog/routed-gather.ts +14 -13
- package/src/codex/cli-install-provenance.ts +105 -17
- package/src/codex/cli-update-plan.ts +545 -0
- package/src/codex/config-write-lock.ts +23 -2
- package/src/codex/desktop-app/picker-freshness.ts +96 -0
- package/src/codex/desktop-app-restart.ts +21 -0
- package/src/codex/desktop-compatibility/installed-build.ts +24 -6
- package/src/codex/desktop-compatibility/routing-preflight.ts +1 -1
- package/src/codex/desktop-compatibility/runtime.ts +12 -3
- package/src/codex/desktop-compatibility/windows-key-protection.ts +115 -9
- package/src/codex/features.ts +85 -45
- package/src/codex/inject/history-failure.ts +27 -0
- package/src/codex/inject/restore.ts +24 -2
- package/src/codex/inject/routing-classify.ts +4 -3
- package/src/codex/inject.ts +18 -27
- package/src/codex/main-account-hard-lock.ts +30 -1
- package/src/codex/native-profile-startup.ts +1 -1
- package/src/codex/ocx-compaction-history.ts +12 -10
- package/src/codex/paths.ts +86 -1
- package/src/codex/provider-routing.ts +174 -0
- package/src/codex/refresh.ts +2 -2
- package/src/codex/routing/health-store.ts +1 -1
- package/src/codex/routing/selection.ts +4 -3
- package/src/codex/routing/thread-affinity.ts +1 -1
- package/src/codex/signed-in-identity.ts +43 -0
- package/src/codex/subagent-model-fallback.ts +1 -1
- package/src/codex/sync.ts +6 -1
- package/src/codex/user-identity.ts +16 -5
- package/src/codex/websocket-activity.ts +161 -0
- package/src/codex/websocket-registry.ts +25 -1
- package/src/codex/windows-local-appdata.ts +84 -0
- package/src/combos/failover.ts +6 -1
- package/src/combos/index.ts +1 -0
- package/src/combos/resolve.ts +46 -10
- package/src/combos/types.ts +15 -0
- package/src/config/diagnostics.ts +33 -6
- package/src/config/live-reconcile.ts +38 -2
- package/src/config/load-degrade.ts +17 -0
- package/src/config/opencode-wire-upgrade.ts +54 -0
- package/src/config/opencode-zen-upgrade.ts +53 -0
- package/src/config/owner-registry.ts +22 -17
- package/src/config/provider-validation.ts +45 -2
- package/src/config/schema/config-schema.ts +22 -0
- package/src/config/schema/leaf-validators.ts +38 -9
- package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
- package/src/config/schema/native-reasoning-retention.ts +26 -0
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +657 -297
- package/src/grok/sync.ts +1 -1
- package/src/images/loop.ts +116 -27
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/mutation-plan.ts +1 -0
- package/src/integrations/registry.ts +8 -0
- package/src/lib/codex-restart-contract.ts +15 -0
- package/src/lib/desktop-upstream-tunnel.ts +18 -53
- package/src/lib/empty-tool-catalog.ts +62 -0
- package/src/lib/errors.ts +19 -3
- package/src/lib/gcp-adc.ts +96 -9
- package/src/lib/gcp-credential-input.ts +12 -0
- package/src/lib/lab-live-route-production.ts +5 -0
- package/src/lib/opaque-proxy-tunnel.ts +217 -0
- package/src/lib/redact.ts +2 -2
- package/src/lib/request-failure-model.ts +2 -0
- package/src/oauth/account-quota-rank.ts +69 -2
- package/src/oauth/anthropic-account-refusal.ts +20 -2
- package/src/oauth/anthropic-oauth-definitions.ts +3 -1
- package/src/oauth/anthropic-routing.ts +1 -21
- package/src/oauth/anthropic.ts +38 -7
- package/src/oauth/callback-server.ts +27 -1
- package/src/oauth/chatgpt-device.ts +1 -1
- package/src/oauth/chatgpt-shared.ts +83 -0
- package/src/oauth/chatgpt.ts +8 -72
- package/src/oauth/dsh.ts +203 -0
- package/src/oauth/generic-account-failover.ts +40 -11
- package/src/oauth/generic-session-affinity.ts +125 -0
- package/src/oauth/index.ts +184 -109
- package/src/oauth/local-token-detect.ts +21 -1
- package/src/oauth/login-cli-errors.ts +44 -0
- package/src/oauth/login-cli.ts +69 -7
- package/src/oauth/login-flow-state.ts +371 -23
- package/src/oauth/meta-muse-errors.ts +41 -0
- package/src/oauth/meta-muse-keychain.ts +52 -0
- package/src/oauth/meta-muse.ts +51 -84
- package/src/oauth/model-discovery-auth.ts +3 -0
- package/src/oauth/pool-settings-capability.ts +4 -2
- package/src/oauth/refresh-flight-retirement.ts +48 -0
- package/src/oauth/session-affinity-key.ts +23 -0
- package/src/oauth/store-anthropic-instance.ts +9 -2
- package/src/oauth/store.ts +12 -5
- package/src/protocols/encoders/adapter-events.ts +6 -5
- package/src/protocols/encoders/messages.ts +9 -6
- package/src/protocols/provider-summary.ts +2 -1
- package/src/providers/api-key-cooldowns.ts +49 -0
- package/src/providers/api-key-resolve.ts +11 -6
- package/src/providers/api-key-response-ownership.ts +57 -0
- package/src/providers/api-key-selection.ts +31 -6
- package/src/providers/api-keys.ts +214 -3
- package/src/providers/claude-cli-usage.ts +96 -0
- package/src/providers/dsh-account-destination.ts +31 -0
- package/src/providers/dsh-account-models.ts +41 -0
- package/src/providers/gcp-credential-cleanup.ts +59 -0
- package/src/providers/key-failover.ts +54 -50
- package/src/providers/key-store.ts +7 -2
- package/src/providers/model-rename-fields.ts +3 -0
- package/src/providers/model-rename-migration.ts +2 -2
- package/src/providers/openai-tiers-destination.ts +11 -2
- package/src/providers/openai-tiers.ts +5 -12
- package/src/providers/opencode-go-transport.ts +3 -1
- package/src/providers/opencode-zen-rate-limit.ts +7 -2
- package/src/providers/quota/account-cache.ts +6 -5
- package/src/providers/quota/account-row.ts +34 -0
- package/src/providers/quota/antigravity.ts +28 -7
- package/src/providers/quota/dsh-account.ts +70 -0
- package/src/providers/quota/vendor-probes-oauth.ts +1 -93
- package/src/providers/quota-types.ts +2 -0
- package/src/providers/quota.ts +14 -20
- package/src/providers/registry/entries-core.ts +37 -8
- package/src/providers/registry/types.ts +1 -1
- package/src/providers/request-pacing.ts +77 -19
- package/src/providers/routed-config.ts +339 -0
- package/src/providers/service-tier.ts +2 -1
- package/src/quota/window-mapping.ts +5 -2
- package/src/responses/anthropic-progress.ts +34 -0
- package/src/responses/assistant-replay.ts +35 -0
- package/src/responses/compaction-history-framing.ts +65 -0
- package/src/responses/compaction.ts +6 -1
- package/src/responses/incomplete-reason.ts +31 -0
- package/src/responses/parser.ts +50 -5
- package/src/responses/reasoning-envelope.ts +4 -0
- package/src/responses/reasoning-replay-cache.ts +74 -6
- package/src/responses/reasoning-retention.ts +173 -0
- package/src/responses/retained-compaction.ts +46 -0
- package/src/responses/schema.ts +23 -0
- package/src/router.ts +8 -321
- package/src/routing/analytics.ts +7 -15
- package/src/routing/health.ts +37 -22
- package/src/routing/history/indexer.ts +6 -5
- package/src/routing/history/ingest.ts +6 -2
- package/src/routing/profile.ts +1 -1
- package/src/server/admission-model-scope.ts +17 -0
- package/src/server/auth-cors.ts +11 -0
- package/src/server/chat-completions.ts +7 -1
- package/src/server/chat-native-reasoning.ts +123 -0
- package/src/server/chat-native-sse.ts +110 -7
- package/src/server/chat-native.ts +76 -17
- package/src/server/claude-messages.ts +56 -414
- package/src/server/effort-policy.ts +1 -1
- package/src/server/images.ts +15 -2
- package/src/server/index/live-sideband.ts +3 -2
- package/src/server/index/model-alias-id.ts +36 -0
- package/src/server/index/optional-listeners.ts +2 -0
- package/src/server/index/serve-options.ts +20 -6
- package/src/server/index/startup-warnings.ts +2 -0
- package/src/server/index/websocket-handler.ts +18 -4
- package/src/server/index.ts +8 -8
- package/src/server/inference/advisor-diagnostics.ts +31 -0
- package/src/server/lifecycle.ts +13 -1
- package/src/server/management/advisor-routes.ts +15 -4
- package/src/server/management/claude-desktop-picker-routes.ts +1 -1
- package/src/server/management/combo-routes.ts +176 -50
- package/src/server/management/companion-routes.ts +1 -1
- package/src/server/management/config-routes.ts +75 -4
- package/src/server/management/cursor-integration-routes.ts +1 -1
- package/src/server/management/model-routes.ts +11 -6
- package/src/server/management/model-rows.ts +14 -0
- package/src/server/management/native-integration-routes.ts +2 -7
- package/src/server/management/native-reasoning-retention-routes.ts +41 -0
- package/src/server/management/oauth-account-routes.ts +87 -14
- package/src/server/management/provider-gcp-credential-routes.ts +29 -0
- package/src/server/management/provider-key-pacing-routes.ts +31 -0
- package/src/server/management/provider-overwrite-carry.ts +7 -0
- package/src/server/management/provider-routes.ts +25 -30
- package/src/server/management/route-registry.ts +10 -1
- package/src/server/management-api.ts +3 -1
- package/src/server/messages-native-tool-guard.ts +185 -0
- package/src/server/messages-native.ts +117 -24
- package/src/server/messages-shared.ts +431 -0
- package/src/server/relay.ts +1 -1
- package/src/server/request-log-terminal-status.ts +1 -1
- package/src/server/request-log.ts +6 -1
- package/src/server/responses/account-change-state.ts +8 -2
- package/src/server/responses/adapter-continuation.ts +47 -19
- package/src/server/responses/adapter-delivery.ts +72 -12
- package/src/server/responses/adapter-dispatch.ts +46 -3
- package/src/server/responses/advisor-retention.ts +117 -0
- package/src/server/responses/advisor-slot.ts +73 -41
- package/src/server/responses/anthropic-thinking-recovery.ts +153 -0
- package/src/server/responses/codex-ws-wire.ts +15 -8
- package/src/server/responses/combo-stream-preflight.ts +3 -2
- package/src/server/responses/compact.ts +303 -125
- package/src/server/responses/compaction-recovery.ts +20 -10
- package/src/server/responses/compaction-retention.ts +61 -0
- package/src/server/responses/context-overflow.ts +11 -6
- package/src/server/responses/core-auth.ts +6 -0
- package/src/server/responses/core-codex-account.ts +11 -1
- package/src/server/responses/core-combo-failure.ts +2 -0
- package/src/server/responses/core-combo.ts +121 -36
- package/src/server/responses/core-errors.ts +3 -3
- package/src/server/responses/core-lifetime.ts +8 -4
- package/src/server/responses/core-opaque-recovery.ts +35 -0
- package/src/server/responses/core-options.ts +7 -0
- package/src/server/responses/core-pipeline.ts +141 -0
- package/src/server/responses/core-replay.ts +23 -3
- package/src/server/responses/core.ts +2 -115
- package/src/server/responses/fetch-helpers.ts +73 -19
- package/src/server/responses/main-hard-lock-routing.ts +109 -0
- package/src/server/responses/native-response-control.ts +4 -2
- package/src/server/responses/passthrough-delivery.ts +3 -1
- package/src/server/responses/passthrough-dispatch.ts +15 -13
- package/src/server/responses/passthrough-execution.ts +3 -2
- package/src/server/responses/request-prepare.ts +77 -12
- package/src/server/responses/request-sidecar-auth.ts +2 -2
- package/src/server/responses/request-transport.ts +104 -27
- package/src/server/responses/response-effects.ts +2 -0
- package/src/server/responses/response-tool-ownership.ts +21 -0
- package/src/server/responses/run-turn-execution.ts +96 -30
- package/src/server/responses/sidecar-execution.ts +63 -9
- package/src/server/responses/sidecar-send-budget.ts +2 -2
- package/src/server/responses/terminal-guard.ts +2 -16
- package/src/server/responses-undeclared-tool-guard.ts +1 -1
- package/src/server/ws-bridge.ts +2 -0
- package/src/service/auth-preflight.ts +71 -0
- package/src/service/cli.ts +4 -2
- package/src/service/definition.ts +97 -0
- package/src/service/guards.ts +1 -61
- package/src/service/health-sentinel-linux.ts +112 -0
- package/src/service/health-sentinel-main.ts +76 -57
- package/src/service/health.ts +5 -89
- package/src/service/launchd.ts +27 -7
- package/src/service/logging.ts +72 -9
- package/src/service/repair.ts +2 -1
- package/src/service/state.ts +7 -8
- package/src/service/systemd.ts +1 -1
- package/src/service/windows-guarded-manager.ts +2 -1
- package/src/service/windows-ops.ts +1 -1
- package/src/service/windows-scheduler.ts +2 -7
- package/src/service/windows-taskxml.ts +2 -2
- package/src/service/windows-wscript.ts +14 -0
- package/src/service.ts +4 -2
- package/src/sidecar/auth.ts +2 -0
- package/src/types/config.ts +64 -3
- package/src/types/opencode-go.ts +61 -0
- package/src/types/opencode-zen.ts +35 -0
- package/src/types/provider.ts +32 -6
- package/src/types/request.ts +31 -2
- package/src/types/wire.ts +31 -37
- package/src/types.ts +2 -0
- package/src/update/npm-invocation.mjs +12 -2
- package/src/usage/cost.ts +48 -12
- package/src/usage/expected-prices.ts +3 -0
- package/src/usage/ledger-retention.ts +2 -1
- package/src/usage/ledger-scanner.ts +6 -5
- package/src/usage/log.ts +17 -8
- package/src/usage/summary.ts +30 -11
- package/src/usage/telemetry-contract.ts +1 -0
- package/src/usage/user-cost-overlays.ts +14 -19
- package/src/usage/user-cost-validation.ts +38 -0
- package/src/vision/eligibility.ts +13 -0
- package/src/web-search/anthropic-history.ts +59 -0
- package/src/web-search/loop.ts +236 -73
- package/src/web-search/progress-stream.ts +15 -2
- package/src/web-search/run-turn-loop.ts +9 -1
- package/gui/dist/assets/App-BjlttVLy.js +0 -58
- package/gui/dist/assets/App-CSJvmjNs.css +0 -1
- package/gui/dist/assets/index-BB6uK6tU.css +0 -1
- package/gui/dist/assets/index-BMssP4k3.js +0 -94
- package/gui/dist/assets/quota-summary-D8M1bc_R.js +0 -1
package/src/server/auth-cors.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
providerForwardClientHeadersConfigError,
|
|
31
31
|
reasoningSummaryDeliveryRecordConfigError,
|
|
32
32
|
upstreamHttpVersionConfigError,
|
|
33
|
+
credentialPinnedProviderConfigError,
|
|
33
34
|
} from "../config/provider-validation";
|
|
34
35
|
import { providerDestinationConfigError } from "../lib/destination-policy";
|
|
35
36
|
import { providerEgressConfigError } from "../lib/provider-egress";
|
|
@@ -784,6 +785,8 @@ export function providerManagementConfigError(
|
|
|
784
785
|
delete canonicalCandidate.pinnedReasoningEffort;
|
|
785
786
|
delete canonicalCandidate.modelPinnedReasoningEfforts;
|
|
786
787
|
delete canonicalCandidate.responsesSnapshotRepair;
|
|
788
|
+
// Explicit history-loss opt-in changes request content, never auth or transport identity.
|
|
789
|
+
delete canonicalCandidate.projectResponsesLiteHostedSearchHistory;
|
|
787
790
|
// modelCosts is a user-owned display overlay, not part of the canonical
|
|
788
791
|
// forward seed; it is validated separately below (providerModelCostsConfigError).
|
|
789
792
|
delete canonicalCandidate.modelCosts;
|
|
@@ -912,6 +915,8 @@ export function providerManagementConfigError(
|
|
|
912
915
|
if (reasoningSummaryDeliveryError) return `provider ${name} ${reasoningSummaryDeliveryError}`;
|
|
913
916
|
const modelAdaptersError = modelAdapterRecordConfigError(raw.modelAdapters, "modelAdapters", name, typed);
|
|
914
917
|
if (modelAdaptersError) return `provider ${name} ${modelAdaptersError}`;
|
|
918
|
+
const credentialPinError = credentialPinnedProviderConfigError(name, typed);
|
|
919
|
+
if (credentialPinError) return `provider ${name} ${credentialPinError}`;
|
|
915
920
|
const preferHostedToolsError = modelPreferHostedToolsConfigError(
|
|
916
921
|
raw.modelPreferHostedTools,
|
|
917
922
|
"modelPreferHostedTools",
|
|
@@ -919,6 +924,9 @@ export function providerManagementConfigError(
|
|
|
919
924
|
typed,
|
|
920
925
|
);
|
|
921
926
|
if (preferHostedToolsError) return `provider ${name} ${preferHostedToolsError}`;
|
|
927
|
+
if (raw.projectResponsesLiteHostedSearchHistory !== undefined && typeof raw.projectResponsesLiteHostedSearchHistory !== "boolean") {
|
|
928
|
+
return `provider ${name} projectResponsesLiteHostedSearchHistory must be a boolean`;
|
|
929
|
+
}
|
|
922
930
|
if (raw.responsesSnapshotRepair !== undefined && typeof raw.responsesSnapshotRepair !== "boolean") {
|
|
923
931
|
return `provider ${name} responsesSnapshotRepair must be a boolean`;
|
|
924
932
|
}
|
|
@@ -1053,9 +1061,12 @@ const PROVIDER_CONFIG_FIELD_POLICY = {
|
|
|
1053
1061
|
preserveResponsesReasoningContent: "editor",
|
|
1054
1062
|
preserveResponsesInputItemIds: "editor",
|
|
1055
1063
|
preserveResponsesMessageMetadata: "editor",
|
|
1064
|
+
projectResponsesLiteHostedSearchHistory: "editor",
|
|
1056
1065
|
dropResponsesReasoningItems: "editor",
|
|
1057
1066
|
modelReasoningEffortsAuthoritative: "editor",
|
|
1058
1067
|
decodesNativeCompactionBlobs: "editor",
|
|
1068
|
+
supportsNativeCompactionTrigger: "editor",
|
|
1069
|
+
supportsNativeCompactEndpoint: "editor",
|
|
1059
1070
|
allowEncryptedV2AgentTasks: "editor",
|
|
1060
1071
|
allowPrivateNetwork: "editor",
|
|
1061
1072
|
// A proxy URL routinely embeds `user:password@`, so it never reaches the dashboard DTO and
|
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
} from "./request-log";
|
|
55
55
|
import { createFinalRequestLog } from "./inference/final-log";
|
|
56
56
|
import { clientWireLogOf, clientWireOf } from "./inference/client-wire";
|
|
57
|
+
import { attachAdvisorReasoning } from "./inference/advisor-diagnostics";
|
|
57
58
|
import { directEncodersApply } from "./inference/client-encoder-delivery";
|
|
58
59
|
import { responseWithDeferredRequestLog } from "./relay";
|
|
59
60
|
import { handleResponses } from "./responses";
|
|
@@ -114,8 +115,10 @@ export async function handleChatCompletions(
|
|
|
114
115
|
): Promise<Response> {
|
|
115
116
|
const translatorBudget = createTranslatorBudget();
|
|
116
117
|
try {
|
|
118
|
+
let response = await handleChatCompletionsWithBudget(req, config, logCtx, translatorBudget, logIds);
|
|
119
|
+
if (isInternalCallCapability(req.headers.get(ADVISOR_INTERNAL_CAPABILITY_HEADER))) response = attachAdvisorReasoning(response, logCtx);
|
|
117
120
|
return finalizeTranslatorBudgetResponse(
|
|
118
|
-
|
|
121
|
+
response,
|
|
119
122
|
translatorBudget,
|
|
120
123
|
req.signal,
|
|
121
124
|
);
|
|
@@ -601,6 +604,9 @@ async function handleChatCompletionsWithBudget(
|
|
|
601
604
|
// below could never restore this one -- the translated client was told the provider
|
|
602
605
|
// throttled the turn, and handed a two-second wait to send it again.
|
|
603
606
|
classified.code = UPSTREAM_RESET_REPLAY_REFUSED_CODE;
|
|
607
|
+
} else if (upstreamCode === "claude_subscription_cooldown") {
|
|
608
|
+
classified.code = "claude_subscription_cooldown";
|
|
609
|
+
classified.type = "rate_limit_error";
|
|
604
610
|
} else if (upstreamCode === "model_not_found") {
|
|
605
611
|
// Structured model_not_found must win over classifyError's generic remaps.
|
|
606
612
|
classified.code = "model_not_found";
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { OcxProviderConfig, OcxReasoningReplayScopeRef } from "../types";
|
|
2
|
+
import type { TranslatorBudget } from "../lib/translator-budget";
|
|
3
|
+
import { chatReasoningReplayPolicy, isChatRecord } from "../adapters/openai-chat/reasoning-replay";
|
|
4
|
+
import { bindReasoningReplayScope, reasoningReplayDestinationIdentity,
|
|
5
|
+
reasoningReplayKeyCredentialIdentity, rememberReasoningForCall } from "../responses/reasoning-replay-cache";
|
|
6
|
+
import { reasoningReplayConversationIdFromResponsesRequest, sessionIdHeaderFromRequest } from "./request-log-conversation";
|
|
7
|
+
|
|
8
|
+
type Rec = Record<string, unknown>;
|
|
9
|
+
|
|
10
|
+
export function nativeChatReplayScope(headers: Headers): OcxReasoningReplayScopeRef | undefined {
|
|
11
|
+
const clientThreadId = reasoningReplayConversationIdFromResponsesRequest({
|
|
12
|
+
threadIdHeader: headers.get("thread-id"), sessionIdHeader: sessionIdHeaderFromRequest(headers),
|
|
13
|
+
});
|
|
14
|
+
return clientThreadId ? { clientThreadId } : undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Rebind before EVERY build: a key rotation must not retrieve another credential's reasoning. */
|
|
18
|
+
export function bindNativeChatReplayScope(
|
|
19
|
+
scope: OcxReasoningReplayScopeRef | undefined,
|
|
20
|
+
provider: OcxProviderConfig,
|
|
21
|
+
providerName: string,
|
|
22
|
+
modelId: string,
|
|
23
|
+
): void {
|
|
24
|
+
const providerDestinationIdentity = reasoningReplayDestinationIdentity(provider.baseUrl);
|
|
25
|
+
const credentialIdentity = reasoningReplayKeyCredentialIdentity(provider);
|
|
26
|
+
bindReasoningReplayScope(scope, providerDestinationIdentity && credentialIdentity ? {
|
|
27
|
+
providerName, modelId, adapterName: "openai-chat",
|
|
28
|
+
providerDestinationIdentity, credentialIdentity,
|
|
29
|
+
} : undefined);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Observe the native wire, never translate it or retain tool arguments/content. */
|
|
33
|
+
export function nativeChatReasoningCapture(
|
|
34
|
+
provider: OcxProviderConfig,
|
|
35
|
+
modelId: string,
|
|
36
|
+
scope: OcxReasoningReplayScopeRef | undefined,
|
|
37
|
+
budget: TranslatorBudget,
|
|
38
|
+
) {
|
|
39
|
+
const choices = new Map<number, { text: string; calls: Map<number, string> }>();
|
|
40
|
+
const accounting = { kind: "retained_collectors" as const };
|
|
41
|
+
// This optional fallback cannot retain more than the cache it feeds (256 KiB / 64 calls).
|
|
42
|
+
const maxBytes = 256 * 1024;
|
|
43
|
+
let bytes = 0;
|
|
44
|
+
let callCount = 0;
|
|
45
|
+
let disabled = !scope?.current || !chatReasoningReplayPolicy(provider, modelId).preserve;
|
|
46
|
+
const discard = () => {
|
|
47
|
+
budget.releaseRetained(bytes, accounting);
|
|
48
|
+
bytes = 0;
|
|
49
|
+
choices.clear();
|
|
50
|
+
disabled = true;
|
|
51
|
+
};
|
|
52
|
+
const remember = (text: string, calls: Iterable<string>) => {
|
|
53
|
+
for (const id of calls) rememberReasoningForCall(id, text, scope);
|
|
54
|
+
};
|
|
55
|
+
const observe = (value: Rec) => {
|
|
56
|
+
if (disabled || !Array.isArray(value.choices)) return;
|
|
57
|
+
for (const [ordinal, raw] of value.choices.entries()) {
|
|
58
|
+
if (!isChatRecord(raw) || !isChatRecord(raw.delta)) continue;
|
|
59
|
+
const index = typeof raw.index === "number" ? raw.index : ordinal;
|
|
60
|
+
if (!choices.has(index)) {
|
|
61
|
+
if (choices.size >= 64) { discard(); return; }
|
|
62
|
+
choices.set(index, { text: "", calls: new Map() });
|
|
63
|
+
}
|
|
64
|
+
const state = choices.get(index)!;
|
|
65
|
+
const delta = raw.delta;
|
|
66
|
+
const append = (previous: string, fragment: string): string | undefined => {
|
|
67
|
+
const added = Buffer.byteLength(fragment) * 2;
|
|
68
|
+
if (bytes + added > maxBytes) { discard(); return undefined; }
|
|
69
|
+
// Account the new retained fragment before concatenation; the transient reservation
|
|
70
|
+
// covers the old string while its replacement is allocated.
|
|
71
|
+
const reservation = budget.reserveTransient(Buffer.byteLength(previous) * 2, accounting);
|
|
72
|
+
try {
|
|
73
|
+
budget.chargeRetained(added, accounting);
|
|
74
|
+
bytes += added;
|
|
75
|
+
return previous + fragment;
|
|
76
|
+
} finally { reservation.release(); }
|
|
77
|
+
};
|
|
78
|
+
if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
|
|
79
|
+
const text = append(state.text, delta.reasoning_content);
|
|
80
|
+
if (text === undefined) return;
|
|
81
|
+
state.text = text;
|
|
82
|
+
}
|
|
83
|
+
if (Array.isArray(delta.tool_calls)) {
|
|
84
|
+
for (const [toolOrdinal, tool] of delta.tool_calls.entries()) {
|
|
85
|
+
if (!isChatRecord(tool) || typeof tool.id !== "string" || tool.id.length === 0) continue;
|
|
86
|
+
const toolIndex = typeof tool.index === "number" ? tool.index : toolOrdinal;
|
|
87
|
+
if (!state.calls.has(toolIndex)) {
|
|
88
|
+
if (callCount >= 64) { discard(); return; }
|
|
89
|
+
callCount += 1;
|
|
90
|
+
}
|
|
91
|
+
const previous = state.calls.get(toolIndex) ?? "";
|
|
92
|
+
if (previous === tool.id) continue;
|
|
93
|
+
const id = append(previous, tool.id);
|
|
94
|
+
if (id === undefined) return;
|
|
95
|
+
state.calls.set(toolIndex, id);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// The completed choice is valid even if a caller dispatches its tool before [DONE].
|
|
99
|
+
if (typeof raw.finish_reason === "string" && raw.finish_reason.length > 0) {
|
|
100
|
+
remember(state.text, state.calls.values());
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
observe,
|
|
106
|
+
finish(success: boolean) {
|
|
107
|
+
if (success && !disabled) {
|
|
108
|
+
for (const state of choices.values()) remember(state.text, state.calls.values());
|
|
109
|
+
}
|
|
110
|
+
discard();
|
|
111
|
+
},
|
|
112
|
+
json(value: Rec) {
|
|
113
|
+
if (disabled || !Array.isArray(value.choices)) return;
|
|
114
|
+
for (const choice of value.choices) {
|
|
115
|
+
if (!isChatRecord(choice) || !isChatRecord(choice.message)) continue;
|
|
116
|
+
const message = choice.message;
|
|
117
|
+
if (typeof message.reasoning_content !== "string" || !Array.isArray(message.tool_calls)) continue;
|
|
118
|
+
remember(message.reasoning_content, message.tool_calls.filter(isChatRecord)
|
|
119
|
+
.flatMap(tool => typeof tool.id === "string" ? [tool.id] : []));
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { chatCompletionsErrorBody } from "../chat/outbound";
|
|
2
|
-
import { classifyError, cyberPolicyErrorType, CYBER_POLICY_ERROR_CODE, isCyberPolicyCode } from "../lib/errors";
|
|
2
|
+
import { classifyError, cyberPolicyErrorType, CYBER_POLICY_ERROR_CODE, isCyberPolicyCode, UNDECLARED_TOOL_CALL_ERROR_CODE } from "../lib/errors";
|
|
3
3
|
import { redactSecretString } from "../lib/redact";
|
|
4
4
|
import {
|
|
5
5
|
isTranslatorBudgetExceededError,
|
|
@@ -16,6 +16,39 @@ function isRec(value: unknown): value is Rec {
|
|
|
16
16
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
export const UNDECLARED_CHAT_TOOL_MESSAGE = "Upstream emitted a client tool call outside this request's declared tool catalog.";
|
|
20
|
+
|
|
21
|
+
function* chatToolCalls(value: Rec): Generator<{ key: string; name: string; snapshot: boolean; invalid?: boolean }> {
|
|
22
|
+
const choices = Array.isArray(value.choices) ? value.choices : [];
|
|
23
|
+
for (const [position, choice] of choices.entries()) {
|
|
24
|
+
if (!isRec(choice)) continue;
|
|
25
|
+
const choiceIndex = typeof choice.index === "number" ? choice.index : position;
|
|
26
|
+
for (const [payload, snapshot] of [[choice.delta, false], [choice.message, true]] as const) {
|
|
27
|
+
if (!isRec(payload)) continue;
|
|
28
|
+
const tools = Array.isArray(payload.tool_calls) ? payload.tool_calls : [];
|
|
29
|
+
for (const [index, tool] of tools.entries()) {
|
|
30
|
+
const fn = isRec(tool) && isRec(tool.function) ? tool.function : {};
|
|
31
|
+
const toolIndex = isRec(tool) && typeof tool.index === "number" ? tool.index : index;
|
|
32
|
+
yield {
|
|
33
|
+
key: `${choiceIndex}:tool:${toolIndex}`, name: typeof fn.name === "string" ? fn.name : "", snapshot,
|
|
34
|
+
invalid: !isRec(tool) || (tool.type !== undefined && tool.type !== "function"),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (payload.function_call !== undefined && payload.function_call !== null) {
|
|
38
|
+
const fn = isRec(payload.function_call) ? payload.function_call : {};
|
|
39
|
+
yield { key: `${choiceIndex}:legacy`, name: typeof fn.name === "string" ? fn.name : "", snapshot };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function chatResponseHasUndeclaredToolCalls(value: Rec, declared: ReadonlySet<string>): boolean {
|
|
46
|
+
for (const call of chatToolCalls(value)) {
|
|
47
|
+
if (call.invalid || !declared.has(call.name)) return true;
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
19
52
|
export function usageFromChat(value: unknown): OcxUsage | undefined {
|
|
20
53
|
if (!isRec(value)) return undefined;
|
|
21
54
|
const prompt = typeof value.prompt_tokens === "number" ? value.prompt_tokens : undefined;
|
|
@@ -134,8 +167,11 @@ interface NativeChatSseOptions {
|
|
|
134
167
|
* `stallTimeoutSec` then resolves to disabled so a slow local model is not cut mid-turn.
|
|
135
168
|
*/
|
|
136
169
|
localUpstream?: boolean;
|
|
170
|
+
clientDeclaredEmptyToolCatalog?: boolean;
|
|
171
|
+
declaredLegacyFunctionNames?: ReadonlySet<string>;
|
|
137
172
|
onFirstOutput?: () => void;
|
|
138
173
|
onUsage: (usage: OcxUsage) => void;
|
|
174
|
+
onChunk?: (value: Rec) => void;
|
|
139
175
|
onTerminal?: (status: number, message?: string) => void;
|
|
140
176
|
onCancel?: () => void;
|
|
141
177
|
}
|
|
@@ -154,6 +190,29 @@ export function nativeChatSse(
|
|
|
154
190
|
}
|
|
155
191
|
});
|
|
156
192
|
let queuedBytes = 0;
|
|
193
|
+
const declared = options.declaredLegacyFunctionNames ?? new Set<string>();
|
|
194
|
+
const toolNames = new Map<string, string>();
|
|
195
|
+
const pendingToolNames = new Set<string>();
|
|
196
|
+
const heldFrames: string[] = [];
|
|
197
|
+
let heldBytes = 0;
|
|
198
|
+
let toolNameBytes = 0;
|
|
199
|
+
const hasPendingToolName = () => pendingToolNames.size > 0;
|
|
200
|
+
const inspectToolCalls = (value: Rec): boolean => {
|
|
201
|
+
for (const call of chatToolCalls(value)) {
|
|
202
|
+
if (call.invalid) return false;
|
|
203
|
+
const previous = toolNames.get(call.key);
|
|
204
|
+
const name = call.snapshot ? call.name : (previous ?? "") + call.name;
|
|
205
|
+
if (!declared.has(name) && ![...declared].some(allowed => allowed.startsWith(name))) return false;
|
|
206
|
+
const bytes = previous === undefined ? 128 + Buffer.byteLength(call.key) * 2 + Buffer.byteLength(name) * 2
|
|
207
|
+
: Math.max(0, Buffer.byteLength(name) - Buffer.byteLength(previous)) * 2;
|
|
208
|
+
options.translatorBudget.chargeRetained(bytes, scope);
|
|
209
|
+
toolNameBytes += bytes;
|
|
210
|
+
toolNames.set(call.key, name);
|
|
211
|
+
if (declared.has(name)) pendingToolNames.delete(call.key);
|
|
212
|
+
else pendingToolNames.add(call.key);
|
|
213
|
+
}
|
|
214
|
+
return true;
|
|
215
|
+
};
|
|
157
216
|
let sawFinish = false;
|
|
158
217
|
let sawDone = false;
|
|
159
218
|
let firstOutput = false;
|
|
@@ -191,7 +250,31 @@ export function nativeChatSse(
|
|
|
191
250
|
throw error;
|
|
192
251
|
}
|
|
193
252
|
};
|
|
194
|
-
const
|
|
253
|
+
const releaseHeld = () => {
|
|
254
|
+
options.translatorBudget.releaseRetained(heldBytes, scope);
|
|
255
|
+
heldBytes = 0;
|
|
256
|
+
heldFrames.length = 0;
|
|
257
|
+
};
|
|
258
|
+
const relay = (controller: ReadableStreamDefaultController<Uint8Array>, text: string) => {
|
|
259
|
+
if (hasPendingToolName()) {
|
|
260
|
+
const bytes = Buffer.byteLength(text) * 2;
|
|
261
|
+
options.translatorBudget.chargeRetained(bytes, scope);
|
|
262
|
+
heldBytes += bytes;
|
|
263
|
+
heldFrames.push(text);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
for (const frame of heldFrames) enqueue(controller, frame);
|
|
267
|
+
releaseHeld();
|
|
268
|
+
enqueue(controller, text);
|
|
269
|
+
};
|
|
270
|
+
const releaseBuffer = () => {
|
|
271
|
+
buffer.clear();
|
|
272
|
+
releaseHeld();
|
|
273
|
+
options.translatorBudget.releaseRetained(toolNameBytes, scope);
|
|
274
|
+
toolNameBytes = 0;
|
|
275
|
+
toolNames.clear();
|
|
276
|
+
pendingToolNames.clear();
|
|
277
|
+
};
|
|
195
278
|
const settle = (status: number, message?: string) => {
|
|
196
279
|
if (settled) return;
|
|
197
280
|
settled = true;
|
|
@@ -206,12 +289,12 @@ export function nativeChatSse(
|
|
|
206
289
|
type = "upstream_error",
|
|
207
290
|
) => {
|
|
208
291
|
const safeMessage = redactSecretString(message);
|
|
292
|
+
releaseBuffer();
|
|
209
293
|
try {
|
|
210
294
|
const frame = `data: ${JSON.stringify(chatCompletionsErrorBody(status, safeMessage, type, code))}\n\n`;
|
|
211
295
|
if (code === "translation_buffer_limit") controller.enqueue(encoder.encode(frame));
|
|
212
296
|
else enqueue(controller, frame);
|
|
213
297
|
} catch { /* controller may already be closed */ }
|
|
214
|
-
releaseBuffer();
|
|
215
298
|
settle(status, safeMessage);
|
|
216
299
|
try { void reader.cancel(new Error(safeMessage)).catch(() => {}); } catch { /* already closed */ }
|
|
217
300
|
try { controller.close(); } catch { /* already closed */ }
|
|
@@ -224,16 +307,20 @@ export function nativeChatSse(
|
|
|
224
307
|
const payload = sseDataPayload(block);
|
|
225
308
|
if (payload === null) {
|
|
226
309
|
if (performance.now() >= pullDeadline) throw stalled;
|
|
227
|
-
|
|
310
|
+
relay(controller, block + delimiter);
|
|
228
311
|
return;
|
|
229
312
|
}
|
|
230
313
|
const trimmed = payload.trim();
|
|
231
314
|
if (trimmed === "") {
|
|
232
315
|
if (performance.now() >= pullDeadline) throw stalled;
|
|
233
|
-
|
|
316
|
+
relay(controller, block + delimiter);
|
|
234
317
|
return;
|
|
235
318
|
}
|
|
236
319
|
if (trimmed === "[DONE]") {
|
|
320
|
+
if (hasPendingToolName()) {
|
|
321
|
+
fail(controller, UNDECLARED_CHAT_TOOL_MESSAGE, UNDECLARED_TOOL_CALL_ERROR_CODE);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
237
324
|
sawDone = true;
|
|
238
325
|
enqueue(controller, replaceSseDataPayload(block, "[DONE]") + delimiter);
|
|
239
326
|
releaseBuffer();
|
|
@@ -272,9 +359,20 @@ export function nativeChatSse(
|
|
|
272
359
|
return;
|
|
273
360
|
}
|
|
274
361
|
const usage = usageFromChat(parsed.usage);
|
|
362
|
+
options.onChunk?.(parsed);
|
|
275
363
|
if (usage) options.onUsage(usage);
|
|
364
|
+
if (options.clientDeclaredEmptyToolCatalog && !inspectToolCalls(parsed)) {
|
|
365
|
+
fail(controller, UNDECLARED_CHAT_TOOL_MESSAGE, UNDECLARED_TOOL_CALL_ERROR_CODE);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
276
368
|
const choices = Array.isArray(parsed.choices) ? parsed.choices : [];
|
|
277
369
|
const finished = choices.some(choice => isRec(choice) && typeof choice.finish_reason === "string" && choice.finish_reason.length > 0);
|
|
370
|
+
if (hasPendingToolName() && choices.some((choice, position) => isRec(choice)
|
|
371
|
+
&& typeof choice.finish_reason === "string" && choice.finish_reason.length > 0
|
|
372
|
+
&& [...pendingToolNames].some(key => key.startsWith(`${typeof choice.index === "number" ? choice.index : position}:`)))) {
|
|
373
|
+
fail(controller, UNDECLARED_CHAT_TOOL_MESSAGE, UNDECLARED_TOOL_CALL_ERROR_CODE);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
278
376
|
if (finished) sawFinish = true;
|
|
279
377
|
const hasText = choices.some(choice => {
|
|
280
378
|
if (!isRec(choice) || !isRec(choice.delta)) return false;
|
|
@@ -302,11 +400,11 @@ export function nativeChatSse(
|
|
|
302
400
|
const hasProgress = hasText || hasTool;
|
|
303
401
|
if (hasProgress || finished) pullDeadline = performance.now() + stallMs;
|
|
304
402
|
else if (performance.now() >= pullDeadline) throw stalled;
|
|
305
|
-
if (!firstOutput && hasProgress) {
|
|
403
|
+
if (!firstOutput && hasProgress && !hasPendingToolName()) {
|
|
306
404
|
firstOutput = true;
|
|
307
405
|
options.onFirstOutput?.();
|
|
308
406
|
}
|
|
309
|
-
|
|
407
|
+
relay(controller, replaceSseDataPayload(block, JSON.stringify(normalizedChunk(parsed, options.requestedModel))) + delimiter);
|
|
310
408
|
};
|
|
311
409
|
|
|
312
410
|
function onAbort() {
|
|
@@ -336,6 +434,7 @@ export function nativeChatSse(
|
|
|
336
434
|
if (next) {
|
|
337
435
|
buffer.compact();
|
|
338
436
|
processBlock(controller, next.block, next.delimiter);
|
|
437
|
+
if (heldFrames.length > 0 && !settled) continue;
|
|
339
438
|
return;
|
|
340
439
|
}
|
|
341
440
|
if (performance.now() >= pullDeadline) throw stalled;
|
|
@@ -370,6 +469,10 @@ export function nativeChatSse(
|
|
|
370
469
|
continue;
|
|
371
470
|
}
|
|
372
471
|
buffer.append(decoder.decode());
|
|
472
|
+
if (hasPendingToolName()) {
|
|
473
|
+
fail(controller, UNDECLARED_CHAT_TOOL_MESSAGE, UNDECLARED_TOOL_CALL_ERROR_CODE);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
373
476
|
if (buffer.tail().trim().length > 0) {
|
|
374
477
|
fail(controller, "upstream SSE ended with an unterminated event", "upstream_sse_unterminated");
|
|
375
478
|
return;
|