@bitkyc08/opencodex 2.86.0 → 2.87.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-ZfYbLHkB.js +58 -0
- package/gui/dist/assets/App-wT3mXqFz.css +1 -0
- package/gui/dist/assets/{Tray-DyR9yBee.js → Tray-DCd-A1az.js} +1 -1
- package/gui/dist/assets/index-CfYDMj34.js +94 -0
- package/gui/dist/assets/index-DPeB9HpV.css +1 -0
- package/gui/dist/assets/quota-summary-ChvmKGOk.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.ts +108 -21
- 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.ts +35 -8
- 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 +3 -1
- package/src/bridge/response-json.ts +18 -3
- package/src/bridge/sse.ts +58 -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 +11 -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 +38 -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 +17 -14
- 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/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 +12 -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/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 +52 -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 +608 -284
- package/src/grok/sync.ts +1 -1
- package/src/images/loop.ts +46 -8
- 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/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 +33 -7
- 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 +5 -4
- 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/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 +24 -2
- 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/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 +55 -414
- package/src/server/effort-policy.ts +1 -1
- package/src/server/images.ts +15 -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 +9 -1
- package/src/server/index/startup-warnings.ts +2 -0
- package/src/server/index.ts +1 -1
- package/src/server/inference/advisor-diagnostics.ts +31 -0
- 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 +101 -21
- 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 +13 -1
- package/src/server/responses/adapter-delivery.ts +19 -6
- package/src/server/responses/adapter-dispatch.ts +19 -2
- package/src/server/responses/advisor-retention.ts +117 -0
- package/src/server/responses/advisor-slot.ts +73 -41
- package/src/server/responses/codex-ws-wire.ts +15 -8
- package/src/server/responses/compact.ts +303 -125
- package/src/server/responses/compaction-recovery.ts +18 -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 +120 -36
- package/src/server/responses/core-errors.ts +3 -3
- package/src/server/responses/core-lifetime.ts +5 -2
- package/src/server/responses/core-opaque-recovery.ts +35 -0
- package/src/server/responses/core-options.ts +5 -0
- package/src/server/responses/core-pipeline.ts +141 -0
- package/src/server/responses/core-replay.ts +18 -2
- 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/request-prepare.ts +76 -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 +1 -0
- package/src/server/responses/run-turn-execution.ts +96 -30
- package/src/server/responses/sidecar-execution.ts +20 -2
- package/src/server/responses/terminal-guard.ts +2 -16
- 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 +29 -0
- package/src/types/provider.ts +32 -6
- package/src/types/request.ts +23 -2
- package/src/types/wire.ts +29 -37
- package/src/types.ts +1 -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 +22 -9
- 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/loop.ts +55 -5
- package/src/web-search/progress-stream.ts +4 -1
- 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/responses/parser.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { anthropicProgressEnvelope, isAnthropicProgressMirror } from "./anthropic-progress";
|
|
1
2
|
import type {
|
|
2
3
|
OcxAssistantMessage,
|
|
3
4
|
OcxContentPart,
|
|
@@ -15,7 +16,8 @@ import { createToolChoiceResolver, namespacedToolName } from "../types";
|
|
|
15
16
|
import { responsesRequestSchema } from "./schema";
|
|
16
17
|
import { providerMetadataFromResponsesFunctionCall } from "./provider-opaque-metadata";
|
|
17
18
|
import { lookupReplayThoughtSignature } from "./thought-signature-replay";
|
|
18
|
-
import {
|
|
19
|
+
import { decodeRetainedCompaction, RETAINED_COMPACTION_PREFIX } from "./retained-compaction";
|
|
20
|
+
import { CHECKPOINT_CONTINUATION_NOTE, compactionItemToText, decodeCompactionSummary, isCompactionItemType, SUMMARY_PREFIX } from "./compaction";
|
|
19
21
|
import { previousResponseReplayPrefixLength } from "./state";
|
|
20
22
|
import { decodeReasoningEnvelope } from "./reasoning-envelope";
|
|
21
23
|
import { hasRoutedIdentity, nameRoutedIdentity } from "../adapters/identity";
|
|
@@ -28,6 +30,7 @@ import { mapToolChoice, buildTools, customToolNamespaces } from "./parser-tools"
|
|
|
28
30
|
import { parseTextFormat } from "./parser-text-format";
|
|
29
31
|
import { externalTaskInputContent } from "./task-input";
|
|
30
32
|
import { normalizeVisualizationContext } from "./visualization-directives";
|
|
33
|
+
import { frameCompactionHistory } from "./compaction-history-framing";
|
|
31
34
|
|
|
32
35
|
/**
|
|
33
36
|
* Wrap a remembered proxy-side signature as provider metadata for a replayed tool call.
|
|
@@ -66,10 +69,35 @@ function repairJsonObjectEnvelope(text: string): Record<string, unknown> | undef
|
|
|
66
69
|
return undefined;
|
|
67
70
|
}
|
|
68
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Repair the observed MiMo tool-call corruption: the model emits its proprietary
|
|
74
|
+
* `</function></tool_call>` closing tags INSIDE the JSON string value of the
|
|
75
|
+
* function arguments, and drops the JSON object's closing brace. Strip the
|
|
76
|
+
* suffix, restore the brace, and only accept the repair when the result parses
|
|
77
|
+
* into an object.
|
|
78
|
+
*/
|
|
79
|
+
function repairMimoToolCallEnvelope(text: string): Record<string, unknown> | undefined {
|
|
80
|
+
const suffix = "</function></tool_call>";
|
|
81
|
+
if (!text.endsWith(suffix)) return undefined;
|
|
82
|
+
const stripped = text.slice(0, -suffix.length);
|
|
83
|
+
if (!stripped.startsWith("{")) return undefined;
|
|
84
|
+
// A trailing brace or quote can itself belong to the unterminated string.
|
|
85
|
+
// Try only the three observed envelope endings, accepting an object parse.
|
|
86
|
+
for (const candidate of [stripped, `${stripped}}`, `${stripped}"}`]) {
|
|
87
|
+
try {
|
|
88
|
+
const parsed: unknown = JSON.parse(candidate);
|
|
89
|
+
if (isObj(parsed)) return parsed;
|
|
90
|
+
} catch {
|
|
91
|
+
/* try the next bounded envelope ending */
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
69
97
|
/**
|
|
70
98
|
* Decode a function call's serialized arguments the way history replay tolerates them: empty
|
|
71
|
-
* or non-JSON text becomes `{}` instead of failing every later turn, and the
|
|
72
|
-
* lost-brace
|
|
99
|
+
* or non-JSON text becomes `{}` instead of failing every later turn, and the bounded
|
|
100
|
+
* lost-brace and MiMo closing-tag corruptions above are repaired. Shared with the direct Chat decoder
|
|
73
101
|
* (`src/protocols/codecs/chat-ir.ts`) so both decode the same call to the same object.
|
|
74
102
|
*/
|
|
75
103
|
export function functionCallArgumentsFromText(callId: string, rawArguments: string | undefined): Record<string, unknown> {
|
|
@@ -80,6 +108,15 @@ export function functionCallArgumentsFromText(callId: string, rawArguments: stri
|
|
|
80
108
|
const parsed: unknown = JSON.parse(rawArgs);
|
|
81
109
|
if (isObj(parsed)) args = parsed;
|
|
82
110
|
} catch {
|
|
111
|
+
// Corruptions observed on Xiaomi MiMo routed requests, each with a bounded
|
|
112
|
+
// repair so a serialized call the model can still see is restored instead
|
|
113
|
+
// of replaying {} forever.
|
|
114
|
+
const mimoRepaired = repairMimoToolCallEnvelope(rawArgs);
|
|
115
|
+
if (mimoRepaired !== undefined) {
|
|
116
|
+
args = mimoRepaired;
|
|
117
|
+
console.warn(`[parser] function_call ${callId} repaired MiMo tool-call envelope (tags inside string)`);
|
|
118
|
+
return args;
|
|
119
|
+
}
|
|
83
120
|
// One observed serialization corruption loses exactly the JSON object's opening
|
|
84
121
|
// brace; the closed envelope is tight enough to repair back into a call the
|
|
85
122
|
// routed model can still see and retry, instead of replaying {} forever.
|
|
@@ -189,7 +226,7 @@ export function parseRequest(
|
|
|
189
226
|
if (!parsed.success) {
|
|
190
227
|
throw new Error(`responses parse error: ${parsed.error.message}`);
|
|
191
228
|
}
|
|
192
|
-
const data = parsed.data;
|
|
229
|
+
const data = frameCompactionHistory(parsed.data);
|
|
193
230
|
const now = Date.now();
|
|
194
231
|
const messages: OcxMessage[] = [];
|
|
195
232
|
// Built before the item loop: a custom_tool_call echoed back in `input` needs the
|
|
@@ -239,6 +276,7 @@ export function parseRequest(
|
|
|
239
276
|
} else if (data.input) {
|
|
240
277
|
for (let inputIndex = 0; inputIndex < data.input.length; inputIndex++) {
|
|
241
278
|
const item = data.input[inputIndex];
|
|
279
|
+
if (inputIndex > 0 && isAnthropicProgressMirror(item, anthropicProgressEnvelope(data.input[inputIndex - 1]))) continue;
|
|
242
280
|
const effectiveType = (item as { type?: string }).type ?? ("role" in item ? "message" : undefined);
|
|
243
281
|
const itemRole = (item as { role?: string }).role;
|
|
244
282
|
const externalTaskInput = effectiveType === "function_call_output" ? externalTaskInputContent(item) : undefined;
|
|
@@ -281,7 +319,7 @@ export function parseRequest(
|
|
|
281
319
|
}
|
|
282
320
|
|
|
283
321
|
if (isCompactionItemType(effectiveType)) {
|
|
284
|
-
// A stored summary from a previous compaction. Decode our ocx1
|
|
322
|
+
// A stored summary from a previous compaction. Decode our ocx1/ocx2 envelopes into plain text so
|
|
285
323
|
// the routed model keeps the compacted context; real OpenAI-encrypted blobs degrade to a note.
|
|
286
324
|
// `context_compaction` (encrypted_content optional) is codex-rs's local-compaction marker;
|
|
287
325
|
// with no payload it is a pure marker (the summary follows as its own user message), so it
|
|
@@ -293,6 +331,13 @@ export function parseRequest(
|
|
|
293
331
|
if (effectiveType === "context_compaction" && typeof encrypted !== "string") continue;
|
|
294
332
|
if (typeof encrypted === "string" && decodeCompactionSummary(encrypted) !== null) decodedCheckpoint = true;
|
|
295
333
|
pendingReasoning.length = 0;
|
|
334
|
+
if (typeof encrypted === "string" && encrypted.startsWith(RETAINED_COMPACTION_PREFIX)) {
|
|
335
|
+
const retained = decodeRetainedCompaction(encrypted);
|
|
336
|
+
if (!retained) throw new Error("Invalid OpenCodex retained compaction envelope");
|
|
337
|
+
messages.push({ role: "user", content: `${SUMMARY_PREFIX}\n\n${retained.summary}`, timestamp: now });
|
|
338
|
+
messages.push(...retained.reasoning.map(content => ({ role: "user" as const, content, timestamp: now })));
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
296
341
|
messages.push({
|
|
297
342
|
role: "user",
|
|
298
343
|
content: compactionItemToText(typeof encrypted === "string" ? encrypted : undefined),
|
|
@@ -20,6 +20,8 @@ export const OCX_REASONING_PREFIX = "ocxr1:";
|
|
|
20
20
|
export interface ReasoningEnvelope {
|
|
21
21
|
/** Anthropic thinking-block signature (signature_delta), if captured. */
|
|
22
22
|
sig?: string;
|
|
23
|
+
/** ID of the immediately following commentary copy of this signed thinking block. */
|
|
24
|
+
upd?: string;
|
|
23
25
|
/** Raw redacted_thinking block data payloads, order preserved. */
|
|
24
26
|
red?: string[];
|
|
25
27
|
/**
|
|
@@ -133,6 +135,8 @@ export function decodeReasoningEnvelope(encryptedContent: string, budget?: Trans
|
|
|
133
135
|
const obj = parsed as { sig?: unknown; red?: unknown };
|
|
134
136
|
const envelope: ReasoningEnvelope = {};
|
|
135
137
|
if (typeof obj.sig === "string") envelope.sig = obj.sig;
|
|
138
|
+
const upd = (parsed as { upd?: unknown }).upd;
|
|
139
|
+
if (typeof upd === "string" && /^msg_[a-f0-9]{32}$/.test(upd)) envelope.upd = upd;
|
|
136
140
|
if (Array.isArray(obj.red)) {
|
|
137
141
|
const red = obj.red.filter((r): r is string => typeof r === "string");
|
|
138
142
|
if (red.length > 0) envelope.red = red;
|
|
@@ -200,12 +200,34 @@ function sweepExpiredOpaqueBlobRejections(at: number): void {
|
|
|
200
200
|
export function reasoningReplayServingIdentityChanged(
|
|
201
201
|
scope: OcxReasoningReplayScopeRef | undefined,
|
|
202
202
|
): boolean {
|
|
203
|
+
return reasoningReplayServingIdentityChange(scope) !== undefined;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Known durable route differences; an unknown issuer never establishes compatibility. */
|
|
207
|
+
export function reasoningReplayServingIdentityChange(
|
|
208
|
+
scope: OcxReasoningReplayScopeRef | undefined,
|
|
209
|
+
): {
|
|
210
|
+
providerChanged: boolean;
|
|
211
|
+
destinationChanged: boolean;
|
|
212
|
+
adapterChanged: boolean;
|
|
213
|
+
modelChanged: boolean;
|
|
214
|
+
credentialChanged: boolean;
|
|
215
|
+
} | undefined {
|
|
203
216
|
const current = servingIdentityFor(scope);
|
|
204
|
-
if (!current) return
|
|
217
|
+
if (!current) return undefined;
|
|
205
218
|
const at = now();
|
|
206
219
|
sweepExpiredServingIdentities(at);
|
|
207
220
|
const previous = servingIdentities.get(current.threadId);
|
|
208
|
-
|
|
221
|
+
if (previous === undefined || previous.identity === current.identity) return undefined;
|
|
222
|
+
const before = JSON.parse(previous.identity) as ReasoningReplayIdentityTuple;
|
|
223
|
+
const after = JSON.parse(current.identity) as ReasoningReplayIdentityTuple;
|
|
224
|
+
return {
|
|
225
|
+
providerChanged: before[0] !== after[0],
|
|
226
|
+
destinationChanged: before[1] !== after[1],
|
|
227
|
+
adapterChanged: before[2] !== after[2],
|
|
228
|
+
modelChanged: before[3] !== after[3],
|
|
229
|
+
credentialChanged: before[4] !== after[4],
|
|
230
|
+
};
|
|
209
231
|
}
|
|
210
232
|
|
|
211
233
|
/**
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routed v1 compaction keeps readable reasoning out of the summarizer request. Text within
|
|
3
|
+
* the configured budget is held locally until success; oversized text is archived on disk.
|
|
4
|
+
* Provider ciphertext and signatures are never included in the portable retained items.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { expandRetainedCompactionInput } from "./retained-compaction";
|
|
8
|
+
import { closeSync, mkdirSync, openSync, rmSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { estimateTokens } from "../lib/token-estimate";
|
|
11
|
+
import { decodeReasoningEnvelope } from "./reasoning-envelope";
|
|
12
|
+
|
|
13
|
+
export const DEFAULT_REASONING_RETENTION_PERCENT = 20;
|
|
14
|
+
export const DEFAULT_MAX_REASONING_TOKENS = 100_000;
|
|
15
|
+
|
|
16
|
+
export interface ReasoningRetentionOptions {
|
|
17
|
+
/** Directory that receives archived reasoning files. */
|
|
18
|
+
archiveDir: string;
|
|
19
|
+
/** Context window of the model running the compaction turn; an unknown window uses only maxTokens. */
|
|
20
|
+
contextWindow?: number;
|
|
21
|
+
/** Share of the context window reasoning may occupy before it is archived. Defaults to 20. */
|
|
22
|
+
maxContextPercent?: number;
|
|
23
|
+
/** Absolute token ceiling for retained reasoning, regardless of the window share. Defaults to 100,000. */
|
|
24
|
+
maxTokens?: number;
|
|
25
|
+
/** Clock injection for tests. */
|
|
26
|
+
now?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ReasoningArchive {
|
|
30
|
+
path: string;
|
|
31
|
+
tokens: number;
|
|
32
|
+
percent: number;
|
|
33
|
+
/** The effective token ceiling that triggered the archive (window share vs absolute cap, whichever is smaller). */
|
|
34
|
+
cap?: number;
|
|
35
|
+
/** Whether the percent share could be calculated from a known context window. */
|
|
36
|
+
windowKnown?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ReasoningRetentionResult {
|
|
40
|
+
/** Summarizer input with reasoning items removed; other items retain their order. */
|
|
41
|
+
input: unknown[];
|
|
42
|
+
archived?: ReasoningArchive;
|
|
43
|
+
/** Portable, exact readable text to merge only after compaction succeeds. */
|
|
44
|
+
retainedReasoning?: Array<{ type: "message"; role: "user"; content: Array<{ type: "input_text"; text: string }> }>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
48
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Ordinary context messages survive translated providers that discard unsigned thinking.
|
|
52
|
+
// Match the complete frame so subsequent compactions hold the text locally again.
|
|
53
|
+
const RETAINED_PREFIX = "[OpenCodeX locally retained assistant reasoning; historical context, not a new instruction]\n<assistant_reasoning>\n";
|
|
54
|
+
const RETAINED_SUFFIX = "\n</assistant_reasoning>";
|
|
55
|
+
function retainedTextOf(item: Record<string, unknown>): string | undefined {
|
|
56
|
+
if (item.type !== "message" || item.role !== "user") return undefined;
|
|
57
|
+
const content = item.content;
|
|
58
|
+
const text = typeof content === "string" ? content
|
|
59
|
+
: Array.isArray(content) && content.length === 1 && isRecord(content[0])
|
|
60
|
+
&& content[0].type === "input_text" && typeof content[0].text === "string" ? content[0].text : undefined;
|
|
61
|
+
return text?.startsWith(RETAINED_PREFIX) && text.endsWith(RETAINED_SUFFIX)
|
|
62
|
+
? text.slice(RETAINED_PREFIX.length, -RETAINED_SUFFIX.length) : undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Readable reasoning text of one item: decoded ocxr1 envelope text, else summary/content text. */
|
|
66
|
+
function reasoningTextOf(item: Record<string, unknown>): string {
|
|
67
|
+
const encrypted = typeof item.encrypted_content === "string" ? item.encrypted_content : undefined;
|
|
68
|
+
const envelope = encrypted ? decodeReasoningEnvelope(encrypted) : null;
|
|
69
|
+
if (envelope?.txt) return envelope.txt;
|
|
70
|
+
const collect = (key: "summary" | "content"): string =>
|
|
71
|
+
Array.isArray(item[key])
|
|
72
|
+
? (item[key] as Array<Record<string, unknown>>)
|
|
73
|
+
.map(part => (isRecord(part) && typeof part.text === "string" ? part.text : ""))
|
|
74
|
+
.join("")
|
|
75
|
+
: "";
|
|
76
|
+
return collect("summary") || collect("content");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Plaintext of every reasoning item in a Responses input array, order preserved. */
|
|
80
|
+
export function collectReasoningTexts(input: unknown): string[] {
|
|
81
|
+
if (!Array.isArray(input)) return [];
|
|
82
|
+
const texts: string[] = [];
|
|
83
|
+
for (const item of input) {
|
|
84
|
+
if (!isRecord(item)) continue;
|
|
85
|
+
const text = item.type === "reasoning" ? reasoningTextOf(item) : retainedTextOf(item) ?? "";
|
|
86
|
+
if (text.length > 0) texts.push(text);
|
|
87
|
+
}
|
|
88
|
+
return texts;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function retentionPercent(value: number | undefined): number {
|
|
92
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 && value <= 100
|
|
93
|
+
? value
|
|
94
|
+
: DEFAULT_REASONING_RETENTION_PERCENT;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function retentionTokenCap(value: number | undefined): number {
|
|
98
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0
|
|
99
|
+
? value
|
|
100
|
+
: DEFAULT_MAX_REASONING_TOKENS;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Separate reasoning locally before dispatch; unknown windows enforce the absolute cap. */
|
|
104
|
+
export function applyReasoningRetention(
|
|
105
|
+
input: unknown,
|
|
106
|
+
options: ReasoningRetentionOptions,
|
|
107
|
+
): ReasoningRetentionResult {
|
|
108
|
+
if (!Array.isArray(input)) return { input: [] };
|
|
109
|
+
input = expandRetainedCompactionInput(input);
|
|
110
|
+
if (!Array.isArray(input)) return { input: [] };
|
|
111
|
+
const texts = collectReasoningTexts(input);
|
|
112
|
+
const retained = input.filter(item => !(isRecord(item) && (item.type === "reasoning" || retainedTextOf(item) !== undefined)));
|
|
113
|
+
if (texts.length === 0) return { input: retained.length === input.length ? input : retained };
|
|
114
|
+
const window = options.contextWindow;
|
|
115
|
+
|
|
116
|
+
const percent = retentionPercent(options.maxContextPercent);
|
|
117
|
+
const cap = retentionTokenCap(options.maxTokens);
|
|
118
|
+
// The window share alone inflates without bound on 1M-window models (20% of 1M is ~210K
|
|
119
|
+
// tokens), so an absolute ceiling applies on top of it: reasoning keeps at most the
|
|
120
|
+
// SMALLER of the two limits. An unknown window leaves the percent share unusable, so the
|
|
121
|
+
// absolute cap alone stands guard.
|
|
122
|
+
const windowKnown = typeof window === "number" && Number.isInteger(window) && window > 0;
|
|
123
|
+
const limit = windowKnown ? Math.min(Math.floor(window * (percent / 100)), cap) : cap;
|
|
124
|
+
const framedTexts = texts.map(text => RETAINED_PREFIX + text + RETAINED_SUFFIX);
|
|
125
|
+
// Budget the historical context that is actually replayed, including every block's frame.
|
|
126
|
+
const tokens = estimateTokens(framedTexts.join("\n"));
|
|
127
|
+
if (tokens <= limit) return {
|
|
128
|
+
input: retained,
|
|
129
|
+
retainedReasoning: framedTexts.map(text => ({ type: "message", role: "user",
|
|
130
|
+
content: [{ type: "input_text", text }] })),
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
mkdirSync(options.archiveDir, { recursive: true, mode: 0o700 });
|
|
134
|
+
const stamp = new Date(options.now ?? Date.now()).toISOString().replace(/[:.]/g, "-");
|
|
135
|
+
const path = join(options.archiveDir, "reasoning-" + stamp + "-" + Math.random().toString(36).slice(2, 8) + ".md");
|
|
136
|
+
// Only a successful exclusive open grants ownership; an occupied path is never removed.
|
|
137
|
+
const fd = openSync(path, "wx", 0o600);
|
|
138
|
+
let failure: unknown;
|
|
139
|
+
try { writeFileSync(fd, texts.join("\n\n---\n\n"), "utf-8"); } catch (error) { failure = error; }
|
|
140
|
+
try { closeSync(fd); } catch (error) { failure ??= error; }
|
|
141
|
+
if (failure !== undefined) {
|
|
142
|
+
try { rmSync(path); } catch { /* preserve the original I/O error; cleanup is best-effort */ }
|
|
143
|
+
throw failure;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const limitDescription = windowKnown
|
|
147
|
+
? percent + "% of the context window, capped at " + limit + " tokens"
|
|
148
|
+
: "absolute cap of " + limit + " tokens (context window unknown)";
|
|
149
|
+
const note = [
|
|
150
|
+
"[reasoning archived locally] " + texts.length + " reasoning block(s) totalling ~" + tokens + " tokens",
|
|
151
|
+
"exceeded the configured retention limit (" + limitDescription + ") and were moved out of band.",
|
|
152
|
+
"Full text: " + path,
|
|
153
|
+
].join(" ");
|
|
154
|
+
const triggerIndex = retained.findIndex(item => isRecord(item) && item.type === "compaction_trigger");
|
|
155
|
+
const noteItem = { type: "message", role: "user", content: note };
|
|
156
|
+
if (triggerIndex >= 0) retained.splice(triggerIndex, 0, noteItem);
|
|
157
|
+
else retained.push(noteItem);
|
|
158
|
+
return { input: retained, archived: { path, tokens, percent, cap: limit, windowKnown } };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* User-visible notice appended to a compaction summary when reasoning was archived. The summary
|
|
163
|
+
* is what the next model and the user both read, so the notice lives there rather than in a log.
|
|
164
|
+
*/
|
|
165
|
+
export function appendRetentionNotice(summary: string, archived: ReasoningArchive | undefined): string {
|
|
166
|
+
if (!archived) return summary;
|
|
167
|
+
const limitDescription = archived.windowKnown === false
|
|
168
|
+
? "absolute cap of " + (archived.cap ?? "unknown") + " tokens (context window unknown)"
|
|
169
|
+
: archived.percent + "% of the context window (effective cap: " + (archived.cap ?? "unknown") + " tokens)";
|
|
170
|
+
return summary + "\n\n[reasoning retention notice] Reasoning of ~" + archived.tokens + " tokens exceeded " + limitDescription
|
|
171
|
+
+ " and was archived locally at " + archived.path
|
|
172
|
+
+ ". This summary covers conclusions only; read that file for the full reasoning.";
|
|
173
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Versioned, proxy-owned compaction data. This is encoding, never provider ciphertext. */
|
|
2
|
+
export const RETAINED_COMPACTION_PREFIX = "ocx2:";
|
|
3
|
+
|
|
4
|
+
export interface RetainedCompaction {
|
|
5
|
+
version: 2;
|
|
6
|
+
summary: string;
|
|
7
|
+
/** Already framed historical context, preserved byte-for-byte on replay. */
|
|
8
|
+
reasoning: string[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function encodeRetainedCompaction(summary: string, reasoning: string[]): string {
|
|
12
|
+
return RETAINED_COMPACTION_PREFIX + Buffer.from(JSON.stringify({ version: 2, summary, reasoning }), "utf-8").toString("base64");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function decodeRetainedCompaction(value: string): RetainedCompaction | null {
|
|
16
|
+
if (!value.startsWith(RETAINED_COMPACTION_PREFIX)) return null;
|
|
17
|
+
try {
|
|
18
|
+
const payload = value.slice(RETAINED_COMPACTION_PREFIX.length);
|
|
19
|
+
const bytes = Buffer.from(payload, "base64");
|
|
20
|
+
if (bytes.toString("base64") !== payload) return null;
|
|
21
|
+
const parsed: unknown = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
22
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
23
|
+
const item = parsed as Record<string, unknown>;
|
|
24
|
+
if (Object.keys(item).some(key => !["version", "summary", "reasoning"].includes(key))
|
|
25
|
+
|| item.version !== 2 || typeof item.summary !== "string" || !item.summary.trim()
|
|
26
|
+
|| !Array.isArray(item.reasoning) || !item.reasoning.every(text => typeof text === "string")) return null;
|
|
27
|
+
return item as unknown as RetainedCompaction;
|
|
28
|
+
} catch { return null; }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Expand only our structured envelopes; callers retain their existing native/ocx1 policy. */
|
|
32
|
+
export function expandRetainedCompactionInput(input: unknown): unknown {
|
|
33
|
+
if (!Array.isArray(input)) return input;
|
|
34
|
+
if (!input.some(item => item && typeof item === "object" && ["compaction", "compaction_summary", "context_compaction"].includes(item.type)
|
|
35
|
+
&& typeof item.encrypted_content === "string" && item.encrypted_content.startsWith(RETAINED_COMPACTION_PREFIX))) return input;
|
|
36
|
+
return input.flatMap(item => {
|
|
37
|
+
if (!item || typeof item !== "object" || !["compaction", "compaction_summary", "context_compaction"].includes(item.type)
|
|
38
|
+
|| typeof item.encrypted_content !== "string" || !item.encrypted_content.startsWith(RETAINED_COMPACTION_PREFIX)) return [item];
|
|
39
|
+
const data = decodeRetainedCompaction(item.encrypted_content);
|
|
40
|
+
if (!data) throw new Error("Invalid OpenCodex retained compaction envelope");
|
|
41
|
+
return [
|
|
42
|
+
{ ...item, encrypted_content: "ocx1:" + Buffer.from(data.summary, "utf-8").toString("base64") },
|
|
43
|
+
...data.reasoning.map(text => ({ type: "message", role: "user", content: [{ type: "input_text", text }] })),
|
|
44
|
+
];
|
|
45
|
+
});
|
|
46
|
+
}
|
package/src/responses/schema.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as z from "zod/v4";
|
|
2
|
+
import { isCompactionItemType } from "./compaction";
|
|
2
3
|
|
|
3
4
|
const inputTextSchema = z.object({ type: z.literal("input_text"), text: z.string() });
|
|
4
5
|
const plainTextSchema = z.object({ type: z.literal("text"), text: z.string() });
|
|
@@ -75,6 +76,28 @@ const reasoningItemSchema = z.object({
|
|
|
75
76
|
// Round-tripped opaque payload (native OpenAI encryption OR the proxy's ocxr1 envelope).
|
|
76
77
|
encrypted_content: z.string().optional(),
|
|
77
78
|
});
|
|
79
|
+
|
|
80
|
+
/** A native v1 replacement history may establish replay provenance only after body success. */
|
|
81
|
+
export const nativeCompactResponseSchema = z.object({
|
|
82
|
+
status: z.literal("completed").optional(),
|
|
83
|
+
error: z.null().optional(),
|
|
84
|
+
output: z.array(z.union([
|
|
85
|
+
userMessageItemSchema.required({ content: true }),
|
|
86
|
+
systemMessageItemSchema.required({ content: true }),
|
|
87
|
+
assistantMessageItemSchema.required({ content: true }),
|
|
88
|
+
reasoningItemSchema,
|
|
89
|
+
z.object({
|
|
90
|
+
type: z.string().refine(isCompactionItemType),
|
|
91
|
+
encrypted_content: z.string().refine(value => value.trim().length > 0),
|
|
92
|
+
}),
|
|
93
|
+
])).min(1).refine(items => items.some(item => {
|
|
94
|
+
if (isCompactionItemType(item.type)) return true;
|
|
95
|
+
if (!("role" in item)) return false;
|
|
96
|
+
return typeof item.content === "string" ? item.content.trim().length > 0
|
|
97
|
+
: item.content.some(part => "text" in part && part.text.trim().length > 0);
|
|
98
|
+
})),
|
|
99
|
+
});
|
|
100
|
+
|
|
78
101
|
const functionCallItemSchema = z.object({
|
|
79
102
|
type: z.literal("function_call"),
|
|
80
103
|
id: z.string().optional(),
|