@bitkyc08/opencodex 2.76.0 → 2.78.0-preview.20261005
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/bin/ocx.mjs +11 -10
- package/gui/dist/assets/App-CppMz-6w.js +54 -0
- package/gui/dist/assets/App-lCIslZ1L.css +1 -0
- package/gui/dist/assets/{Tray-DjNwwrWS.js → Tray-CJWQqtVv.js} +1 -1
- package/gui/dist/assets/index-DQt9srNi.js +94 -0
- package/gui/dist/assets/index-DmQB8D_R.css +1 -0
- package/gui/dist/assets/quota-summary-BMsyoZH2.js +1 -0
- package/gui/dist/index.html +4 -3
- package/package.json +1 -1
- package/src/adapters/anthropic/beta-allowlist.ts +19 -2
- package/src/adapters/anthropic/client-identity.ts +13 -4
- package/src/adapters/anthropic/native-client-preamble.ts +31 -0
- package/src/adapters/anthropic/passthrough.ts +103 -25
- package/src/adapters/antigravity-refusal.ts +28 -0
- package/src/adapters/cursor/protobuf-request.ts +6 -0
- package/src/adapters/cursor/request-builder.ts +3 -0
- package/src/adapters/cursor/structured-output.ts +13 -0
- package/src/adapters/cursor/types.ts +2 -0
- package/src/adapters/devin/cloud-direct/chat.ts +7 -2
- package/src/adapters/devin/trajectory.ts +49 -0
- package/src/adapters/devin.ts +13 -0
- package/src/adapters/ollama-native.ts +62 -19
- package/src/adapters/openai-responses/passthrough.ts +133 -103
- package/src/adapters/openai-responses/request-strips.ts +10 -4
- package/src/adapters/zed.ts +26 -4
- package/src/bridge/response-json.ts +14 -14
- package/src/bridge/sse.ts +15 -5
- package/src/chatgpt/app-server-shim/bundle-trust.ts +40 -10
- package/src/chatgpt/app-server-shim/gate-rewrite.ts +3 -0
- package/src/claude/agents-inject.ts +4 -43
- package/src/claude/gateway-cache.ts +16 -13
- package/src/claude/inbound-model-options.ts +9 -2
- package/src/claude/intercept/cli-catalog.ts +27 -16
- package/src/claude/intercept/client-hello.ts +103 -0
- package/src/claude/intercept/model-bindings.ts +3 -2
- package/src/claude/intercept/picker-bootstrap.ts +37 -16
- package/src/claude/intercept/picker-budget.ts +41 -0
- package/src/claude/intercept/picker-listener.ts +279 -66
- package/src/claude/outbound.ts +2 -0
- package/src/claude/subagent-force-status.ts +53 -0
- package/src/claude/subagent-model.ts +93 -0
- package/src/cli/access-audio-input.ts +84 -0
- package/src/cli/access-audio-live.ts +99 -0
- package/src/cli/access-audio.ts +123 -0
- package/src/cli/access-data-client.ts +127 -0
- package/src/cli/access-data-plane.ts +104 -0
- package/src/cli/access-data-response.ts +76 -0
- package/src/cli/access.ts +65 -2
- package/src/cli/account-api.ts +33 -6
- package/src/cli/account-auth.ts +111 -42
- package/src/cli/account-key-quota.ts +118 -0
- package/src/cli/account-main.ts +11 -4
- package/src/cli/account-next-actions.ts +63 -0
- package/src/cli/account-policy-dto.ts +68 -0
- package/src/cli/account-policy.ts +167 -0
- package/src/cli/account-target.ts +8 -0
- package/src/cli/account.ts +37 -9
- package/src/cli/agent-runtime-settings.ts +130 -0
- package/src/cli/agent-settings.ts +101 -0
- package/src/cli/agent.ts +29 -2
- package/src/cli/alias.ts +7 -2
- package/src/cli/aside-profiles.ts +2 -2
- package/src/cli/capabilities-access-remote.ts +147 -0
- package/src/cli/capabilities-accounts.ts +195 -0
- package/src/cli/capabilities-agents-routing.ts +414 -0
- package/src/cli/capabilities-base.ts +783 -0
- package/src/cli/capabilities-command.ts +1 -0
- package/src/cli/capabilities-integrations.ts +264 -0
- package/src/cli/capabilities-lab.ts +214 -0
- package/src/cli/capabilities-observe-system.ts +662 -0
- package/src/cli/capabilities-provider-models.ts +302 -0
- package/src/cli/capabilities.ts +30 -1190
- package/src/cli/capability-types.ts +54 -0
- package/src/cli/catalog-command-result.ts +57 -0
- package/src/cli/chatgpt-command.ts +35 -9
- package/src/cli/claude-agent-startup-sync.ts +17 -0
- package/src/cli/claude-desktop-profile.ts +100 -0
- package/src/cli/claude-desktop.ts +39 -6
- package/src/cli/claude.ts +22 -6
- package/src/cli/codex-shim-autorestore.ts +2 -1
- package/src/cli/codex-shim-readiness.ts +109 -0
- package/src/cli/combo-input.ts +88 -0
- package/src/cli/combo-result.ts +52 -0
- package/src/cli/combo-stats.ts +123 -0
- package/src/cli/combo.ts +37 -14
- package/src/cli/companion-timeline.ts +142 -0
- package/src/cli/companion-usage.ts +146 -0
- package/src/cli/companion.ts +12 -3
- package/src/cli/config-command.ts +24 -8
- package/src/cli/connect.ts +19 -0
- package/src/cli/dispatch.ts +55 -82
- package/src/cli/doctor.ts +22 -4
- package/src/cli/export-command.ts +20 -9
- package/src/cli/gui.ts +8 -3
- package/src/cli/help-catalog.ts +101 -0
- package/src/cli/help-models-context.ts +17 -0
- package/src/cli/help-navigation.ts +50 -0
- package/src/cli/help-recovery.ts +66 -0
- package/src/cli/help.ts +69 -11
- package/src/cli/index.ts +38 -27
- package/src/cli/injection-follow.ts +35 -0
- package/src/cli/inspect.ts +5 -0
- package/src/cli/integration-aside-sync.ts +69 -0
- package/src/cli/integration-cursor.ts +106 -0
- package/src/cli/integration-input.ts +60 -0
- package/src/cli/integration-journal.ts +49 -0
- package/src/cli/integration-plan-dto.ts +156 -0
- package/src/cli/integration-preview.ts +215 -0
- package/src/cli/integrations.ts +24 -18
- package/src/cli/json-input.ts +134 -0
- package/src/cli/link.ts +32 -4
- package/src/cli/local-sync-result.ts +41 -0
- package/src/cli/log-follow.ts +84 -0
- package/src/cli/log-view-filter.ts +146 -0
- package/src/cli/logout-command.ts +44 -0
- package/src/cli/model-picker-ordering.ts +137 -0
- package/src/cli/models-custom-input.ts +107 -0
- package/src/cli/models-custom-runtime.ts +112 -0
- package/src/cli/models-order.ts +194 -0
- package/src/cli/models-runtime-subcommands.ts +2 -0
- package/src/cli/models-runtime.ts +9 -1
- package/src/cli/models.ts +66 -81
- package/src/cli/observe-stream.ts +142 -0
- package/src/cli/observe.ts +75 -34
- package/src/cli/opencode.ts +54 -10
- package/src/cli/provider-batch.ts +77 -0
- package/src/cli/provider-lifecycle-runtime.ts +170 -0
- package/src/cli/provider-result.ts +124 -0
- package/src/cli/provider-runtime.ts +15 -12
- package/src/cli/provider-settings.ts +143 -0
- package/src/cli/provider.ts +116 -64
- package/src/cli/registry.ts +90 -24
- package/src/cli/remote-workspace-hub.ts +83 -0
- package/src/cli/remote-workspace.ts +6 -1
- package/src/cli/restart-failure.ts +27 -0
- package/src/cli/root.ts +42 -9
- package/src/cli/route-policy-write.ts +137 -0
- package/src/cli/route-policy.ts +8 -0
- package/src/cli/runtime-api.ts +57 -80
- package/src/cli/secret-args.ts +149 -0
- package/src/cli/settings-result.ts +30 -0
- package/src/cli/storage.ts +77 -7
- package/src/cli/system-command.ts +60 -2
- package/src/cli/system-restart-client.ts +20 -3
- package/src/cli/system-settings-parity.ts +83 -0
- package/src/cli/tray-proxy.ts +3 -2
- package/src/cli/update-restart-candidate.ts +21 -0
- package/src/cli/update-restart-child.ts +81 -0
- package/src/cli/update-restart-home.ts +38 -0
- package/src/cli/update-restart-transport.ts +193 -0
- package/src/cli/update-restart.ts +260 -0
- package/src/cli/usage-model-search.ts +58 -0
- package/src/cli/usage-report.ts +13 -5
- package/src/cli/v2-input.ts +66 -0
- package/src/cli/v2-local-output.ts +37 -0
- package/src/cli/v2-runtime.ts +90 -0
- package/src/cli/v2.ts +102 -144
- package/src/clients/config-export/contracts.ts +100 -0
- package/src/clients/config-export/droid.ts +54 -2
- package/src/clients/config-export/dsh.ts +21 -0
- package/src/clients/config-export/kilo.ts +12 -2
- package/src/clients/config-export/model-metadata.ts +10 -4
- package/src/clients/config-export/reasoning-metadata.ts +47 -0
- package/src/clients/config-export.ts +53 -67
- package/src/clients/omo-role-models.ts +17 -3
- package/src/codex/account-credit-use.ts +4 -2
- package/src/codex/account-runtime-state.ts +21 -0
- package/src/codex/account-store.ts +11 -47
- package/src/codex/account-usability.ts +11 -8
- package/src/codex/auth-api/account-list.ts +73 -63
- package/src/codex/auth-api/main-account-probe.ts +25 -12
- package/src/codex/auth-api/pool-mode-gate.ts +6 -9
- package/src/codex/auth-api/pool-quota-probe.ts +2 -4
- package/src/codex/auth-api/reset-credit-service.ts +3 -2
- package/src/codex/auth-context.ts +30 -11
- package/src/codex/catalog/aggregation.ts +3 -0
- package/src/codex/catalog/build-entries.ts +1 -1
- package/src/codex/catalog/bundled.ts +9 -2
- package/src/codex/catalog/heal-observation.ts +66 -0
- package/src/codex/catalog/model-hints.ts +2 -1
- package/src/codex/catalog/model-visibility.ts +6 -3
- package/src/codex/catalog/parsing.ts +2 -0
- package/src/codex/catalog/provider-models.ts +13 -4
- package/src/codex/catalog/publication-observer.ts +19 -0
- package/src/codex/catalog/remote.ts +13 -15
- package/src/codex/catalog/restore.ts +19 -6
- package/src/codex/catalog/retained-sync.ts +60 -11
- package/src/codex/catalog/routed-removal.ts +116 -0
- package/src/codex/catalog/write-audit-contract.ts +21 -0
- package/src/codex/catalog/write-audit.ts +171 -0
- package/src/codex/catalog-self-heal.ts +284 -0
- package/src/codex/catalog-write-serialization.ts +71 -7
- package/src/codex/chatgpt-refresh-failure.ts +56 -0
- package/src/codex/codex-home-owner.ts +124 -0
- package/src/codex/convergence.ts +57 -16
- package/src/codex/features.ts +8 -1
- package/src/codex/inject/restore.ts +46 -4
- package/src/codex/inject.ts +11 -1
- package/src/codex/internal/catalog-writer.ts +120 -13
- package/src/codex/journal.ts +49 -13
- package/src/codex/main-account.ts +144 -11
- package/src/codex/management-convergence.ts +3 -0
- package/src/codex/quota-refresh-outcome.ts +17 -2
- package/src/codex/quota-types.ts +36 -1
- package/src/codex/quota.ts +67 -16
- package/src/codex/refresh.ts +20 -9
- package/src/codex/routing/cooldown-math.ts +10 -4
- package/src/codex/routing/selection.ts +11 -4
- package/src/codex/routing.ts +9 -9
- package/src/codex/runtime.ts +7 -1
- package/src/codex/shim-diagnostics.ts +80 -0
- package/src/codex/shim-fingerprint.ts +7 -2
- package/src/codex/shim-migration.ts +153 -0
- package/src/codex/shim-overlay.ts +509 -0
- package/src/codex/shim-path-resolution.ts +145 -0
- package/src/codex/shim-probe.ts +4 -3
- package/src/codex/shim-state-file.ts +93 -7
- package/src/codex/shim.ts +54 -107
- package/src/codex/subagent-model-fallback.ts +2 -1
- package/src/codex/sync.ts +24 -4
- package/src/combos/failover.ts +101 -1
- package/src/combos/index.ts +1 -0
- package/src/combos/jev.ts +13 -3
- package/src/combos/resolve.ts +12 -0
- package/src/config/atomic-write.ts +13 -8
- package/src/config/diagnostics.ts +23 -1
- package/src/config/load-degrade.ts +7 -0
- package/src/config/provider-validation.ts +3 -0
- package/src/config/schema/chatgpt-desktop.ts +22 -0
- package/src/config/schema/compaction-triggers.ts +8 -0
- package/src/config/schema/config-schema.ts +43 -14
- package/src/config/schema/leaf-validators.ts +8 -3
- package/src/config/subagent-models.ts +12 -0
- package/src/generated/compatibility-version.json +645 -237
- package/src/grok/session-identity.ts +33 -0
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/merge.ts +13 -5
- package/src/integrations/mutation-plan.ts +50 -5
- package/src/integrations/omp-yaml-source.ts +181 -1
- package/src/integrations/ownership-policy.ts +66 -0
- package/src/integrations/registry.ts +50 -1
- package/src/integrations/state.ts +106 -19
- package/src/integrations/target.ts +25 -2
- package/src/integrations/writer.ts +107 -35
- package/src/lib/gui-pair-capability.ts +17 -0
- package/src/lib/package-tree-integrity.ts +42 -1
- package/src/lib/provider-client-headers.ts +62 -0
- package/src/lib/proxy-env.ts +3 -0
- package/src/lib/test-home-guard.ts +2 -1
- package/src/oauth/anthropic-account-refusal.ts +19 -8
- package/src/oauth/anthropic-continuity.ts +65 -0
- package/src/oauth/anthropic-identity.ts +75 -0
- package/src/oauth/anthropic-routing.ts +20 -15
- package/src/oauth/anthropic.ts +34 -3
- package/src/oauth/generic-account-failover.ts +21 -0
- package/src/oauth/health.ts +32 -5
- package/src/oauth/index.ts +42 -35
- package/src/oauth/local-token-detect.ts +7 -0
- package/src/oauth/pool-settings-capability.ts +9 -2
- package/src/oauth/refresh-policy.ts +2 -0
- package/src/oauth/store.ts +55 -6
- package/src/oauth/types.ts +9 -0
- package/src/protocols/encoders/messages.ts +4 -3
- package/src/protocols/plan-snapshot.ts +1 -1
- package/src/protocols/settings.ts +28 -4
- package/src/providers/antigravity-effort-families.ts +75 -0
- package/src/providers/antigravity-models.ts +54 -16
- package/src/providers/antigravity-wire-snapshot.ts +66 -0
- package/src/providers/derive.ts +2 -0
- package/src/providers/model-rename-fields.ts +3 -0
- package/src/providers/new-model-policy.ts +48 -10
- package/src/providers/registry/entries-extended.ts +3 -0
- package/src/providers/registry/model-ids.ts +2 -0
- package/src/providers/registry/model-seeds.ts +5 -0
- package/src/providers/registry/types.ts +4 -0
- package/src/responses/compaction.ts +19 -0
- package/src/router.ts +6 -0
- package/src/server/auth-cors.ts +9 -0
- package/src/server/caller-session-identity.ts +32 -0
- package/src/server/chat-completions.ts +10 -0
- package/src/server/claude-messages.ts +29 -15
- package/src/server/droid-reasoning-default.ts +50 -0
- package/src/server/github-copilot-responses-repair.ts +3 -6
- package/src/server/grok-responses-control-frame.ts +2 -2
- package/src/server/grok-responses-snapshot-repair.ts +4 -5
- package/src/server/gui-session.ts +33 -11
- package/src/server/index/claude-intercept-lifecycle.ts +4 -0
- package/src/server/index/serve-options.ts +13 -7
- package/src/server/index.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +12 -134
- package/src/server/management/config-routes.ts +1 -1
- package/src/server/management/context.ts +8 -1
- package/src/server/management/cursor-integration-routes.ts +3 -3
- package/src/server/management/integration-routes.ts +69 -11
- package/src/server/management/link-routes.ts +20 -13
- package/src/server/management/model-routes.ts +3 -3
- package/src/server/management/model-row-export-metadata.ts +150 -0
- package/src/server/management/model-rows.ts +148 -9
- package/src/server/management/native-integration-routes.ts +2 -2
- package/src/server/management/oauth-account-routes.ts +96 -30
- package/src/server/management/route-registry.ts +29 -27
- package/src/server/management/shared.ts +19 -5
- package/src/server/management/subagent-model-routes.ts +214 -0
- package/src/server/messages-native-eligibility.ts +3 -6
- package/src/server/messages-native-oauth.ts +82 -35
- package/src/server/messages-native.ts +78 -16
- package/src/server/relay.ts +14 -44
- package/src/server/request-log-generation-window.ts +38 -0
- package/src/server/request-log.ts +13 -0
- package/src/server/responses/adapter-dispatch.ts +152 -28
- package/src/server/responses/codex-auth-error.ts +10 -2
- package/src/server/responses/combo-stream-preflight.ts +29 -3
- package/src/server/responses/compaction-routing.ts +10 -1
- package/src/server/responses/core-auth.ts +67 -30
- package/src/server/responses/core-codex-account.ts +6 -3
- package/src/server/responses/core-combo-failure.ts +5 -1
- package/src/server/responses/core-combo-native.ts +7 -1
- package/src/server/responses/core-combo.ts +64 -7
- package/src/server/responses/core-normalize.ts +7 -0
- package/src/server/responses/core-options.ts +5 -0
- package/src/server/responses/fetch-helpers.ts +9 -0
- package/src/server/responses/passthrough-delivery.ts +1 -1
- package/src/server/responses/policy-fallback.ts +1 -1
- package/src/server/responses/request-prepare.ts +5 -0
- package/src/server/responses/request-spend.ts +30 -30
- package/src/server/responses-custom-tool-repair.ts +2 -2
- package/src/server/responses-hosted-image-display.ts +70 -9
- package/src/server/responses-terminal-repair.ts +15 -2
- package/src/server/sse-frame-buffer.ts +38 -36
- package/src/server/sse-payload-rewrite.ts +78 -49
- package/src/server/ws-bridge.ts +1 -11
- package/src/service/cli.ts +6 -1
- package/src/service/ownership-mutation-lease.d.mts +26 -0
- package/src/service/ownership-mutation-lease.mjs +76 -1
- package/src/service/windows-taskxml.ts +1 -2
- package/src/service-manager-probe.ts +36 -2
- package/src/types/config.ts +6 -0
- package/src/types/provider.ts +28 -10
- package/src/update/index.ts +2 -1
- package/src/update/npm-invocation.mjs +35 -3
- package/src/update/proxy-liveness-probe.mjs +38 -8
- package/src/update/update-failure-guidance.d.mts +6 -0
- package/src/update/update-failure-guidance.mjs +30 -5
- package/src/usage/expected-prices.ts +9 -0
- package/src/usage/log.ts +7 -0
- package/src/usage/summary.ts +82 -1
- package/src/web-search/loop.ts +11 -8
- package/src/web-search/run-turn-loop.ts +10 -7
- package/gui/dist/assets/App-CddorHdA.js +0 -51
- package/gui/dist/assets/App-DMudtISW.css +0 -1
- package/gui/dist/assets/index-Btze1I3T.css +0 -1
- package/gui/dist/assets/index-CvnY7E6i.js +0 -86
- package/gui/dist/assets/quota-summary-B_AWpHfP.js +0 -1
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// Shared client export contracts.
|
|
2
|
+
import { canonicalizeReasoningEfforts } from "../../reasoning-effort";
|
|
2
3
|
import type { OcxConfig } from "../../types";
|
|
3
4
|
import type { ConfigFormat } from "../../integrations/serialize";
|
|
4
5
|
|
|
6
|
+
export const DROID_DEFAULT_EFFORT_HEADER = "x-opencodex-droid-default-effort";
|
|
7
|
+
|
|
5
8
|
/**
|
|
6
9
|
* One entry opencodex owns inside a client's config: the JSON path to it and
|
|
7
10
|
* the value we put there.
|
|
@@ -38,6 +41,12 @@ export interface OpencodeCatalogModel {
|
|
|
38
41
|
id?: string;
|
|
39
42
|
contextWindow?: number;
|
|
40
43
|
maxTokens?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Authoritative input ceiling, distinct from `contextWindow` (GPT-5.6 advertises 922k
|
|
46
|
+
* usable input under a 1.05M window). Carried from the catalog row so a client that has a
|
|
47
|
+
* separate input limit never treats the whole window as prompt budget.
|
|
48
|
+
*/
|
|
49
|
+
maxInputTokens?: number;
|
|
41
50
|
displayName?: string;
|
|
42
51
|
/**
|
|
43
52
|
* Declared input modalities, carried verbatim from `/api/models`. Serialized as opencode's
|
|
@@ -54,6 +63,9 @@ export interface OpencodeCatalogModel {
|
|
|
54
63
|
* model-level setting — see {@link opencodeEffortVariants} for why.
|
|
55
64
|
*/
|
|
56
65
|
defaultReasoningEffort?: string;
|
|
66
|
+
supportsTools?: boolean;
|
|
67
|
+
supportsReasoning?: boolean;
|
|
68
|
+
supportsReasoningSummaries?: boolean;
|
|
57
69
|
}
|
|
58
70
|
|
|
59
71
|
/**
|
|
@@ -73,10 +85,94 @@ export interface ExportModel {
|
|
|
73
85
|
displayName?: string;
|
|
74
86
|
contextWindow?: number;
|
|
75
87
|
maxTokens?: number;
|
|
88
|
+
/** Authoritative input ceiling; optional and never guessed from the context window. */
|
|
89
|
+
maxInputTokens?: number;
|
|
76
90
|
inputModalities?: string[];
|
|
77
91
|
/** Optional effort ladder exported only to clients that support it. */
|
|
78
92
|
reasoningEfforts?: string[];
|
|
79
93
|
defaultReasoningEffort?: string;
|
|
94
|
+
supportsTools?: boolean;
|
|
95
|
+
supportsReasoning?: boolean;
|
|
96
|
+
supportsReasoningSummaries?: boolean;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Effective, override-applied export metadata for one model row: what a client export should
|
|
101
|
+
* serialize, as distinct from the raw editor representation `/api/models` also carries.
|
|
102
|
+
*
|
|
103
|
+
* Custom rows are the reason this exists. Their row fields are the operator's stored
|
|
104
|
+
* OVERRIDES (an absent field means "not overridden", an empty ladder means "no rungs"), so a
|
|
105
|
+
* serializer reading only them drops every capability the model inherits from the provider,
|
|
106
|
+
* the registry, or the gathered catalog. The management row list resolves that projection
|
|
107
|
+
* from the same canonical sources routed rows use and attaches it additively — the raw
|
|
108
|
+
* override fields stay exactly what the editor wrote.
|
|
109
|
+
*/
|
|
110
|
+
export interface EffectiveModelExportMetadata {
|
|
111
|
+
contextWindow?: number;
|
|
112
|
+
/** Input ceiling; distinct from `contextWindow`, carried only when a source asserts one. */
|
|
113
|
+
maxInputTokens?: number;
|
|
114
|
+
/** Output ceiling (a catalog row's `maxOutputTokens`). */
|
|
115
|
+
maxTokens?: number;
|
|
116
|
+
inputModalities?: string[];
|
|
117
|
+
/** Effective ladder; `[]` is an explicit "no rungs" declaration and survives as `[]`. */
|
|
118
|
+
reasoningEfforts?: string[];
|
|
119
|
+
/**
|
|
120
|
+
* A default some canonical source declared — never one synthesized from the ladder's
|
|
121
|
+
* preference order, and only when it is a member of the effective ladder. An empty
|
|
122
|
+
* ladder suppresses it entirely: there is no rung to default to.
|
|
123
|
+
*/
|
|
124
|
+
defaultReasoningEffort?: string;
|
|
125
|
+
supportsTools?: boolean;
|
|
126
|
+
supportsReasoning?: boolean;
|
|
127
|
+
supportsReasoningSummaries?: boolean;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The per-row evidence the capability booleans are derived from. Only positive, canonical
|
|
132
|
+
* signals — no independent provider fact tables, no name-based inference.
|
|
133
|
+
*/
|
|
134
|
+
export interface ExportCapabilityEvidence {
|
|
135
|
+
native?: boolean;
|
|
136
|
+
reasoningEfforts?: readonly string[];
|
|
137
|
+
supportsReasoningSummaries?: boolean;
|
|
138
|
+
/** Normalized upstream capability names a catalog row carried (e.g. `"tools"`). */
|
|
139
|
+
capabilities?: readonly string[];
|
|
140
|
+
/** Provider-level parallel tool call opt-in for this model. */
|
|
141
|
+
parallelToolCalls?: boolean;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Tool support a canonical source positively asserts, or undefined when nothing is known.
|
|
146
|
+
*
|
|
147
|
+
* Mirrors the routing reader (`src/routing/capability.ts`): a catalog row without `"tools"`
|
|
148
|
+
* is UNKNOWN, never a negative — the row may simply not enumerate capabilities. `native`
|
|
149
|
+
* rows and the parallel-call opt-in are positive evidence on their own.
|
|
150
|
+
*/
|
|
151
|
+
export function knownToolsSupport(evidence: ExportCapabilityEvidence): boolean | undefined {
|
|
152
|
+
if (evidence.native === true) return true;
|
|
153
|
+
if (evidence.capabilities?.includes("tools") === true) return true;
|
|
154
|
+
if (evidence.parallelToolCalls === true) return true;
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Reasoning support a canonical source positively asserts, or undefined when nothing is
|
|
160
|
+
* known.
|
|
161
|
+
*
|
|
162
|
+
* Never `false` from a missing or empty effort ladder: an empty ladder says "no ADJUSTABLE
|
|
163
|
+
* effort", which is not the claim "cannot reason" — a model can reason at a fixed depth with
|
|
164
|
+
* no rung to select. Known evidence is a positive reasoning rung (the catalog's own statement
|
|
165
|
+
* that the model accepts reasoning parameters) or delivered reasoning summaries. `none` is
|
|
166
|
+
* the off sentinel, not a rung: a ladder of only `none` offers an off variant and asserts
|
|
167
|
+
* nothing about whether the model reasons, so it is not positive evidence on its own.
|
|
168
|
+
*/
|
|
169
|
+
export function knownReasoningSupport(evidence: ExportCapabilityEvidence): boolean | undefined {
|
|
170
|
+
if (evidence.supportsReasoningSummaries === true) return true;
|
|
171
|
+
if (evidence.reasoningEfforts !== undefined
|
|
172
|
+
&& canonicalizeReasoningEfforts(evidence.reasoningEfforts).some(effort => effort !== "none")) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
return undefined;
|
|
80
176
|
}
|
|
81
177
|
|
|
82
178
|
export interface ExportContext {
|
|
@@ -88,8 +184,12 @@ export interface ExportContext {
|
|
|
88
184
|
* admission from `apiKey` to the `x-opencodex-api-key` header.
|
|
89
185
|
*/
|
|
90
186
|
config?: OcxConfig;
|
|
187
|
+
droidReasoningDefaults?: DroidReasoningDefaults;
|
|
91
188
|
}
|
|
92
189
|
|
|
190
|
+
/** Namespaced model selector to one of that model's declared reasoning efforts. */
|
|
191
|
+
export type DroidReasoningDefaults = Record<string, string>;
|
|
192
|
+
|
|
93
193
|
export type ExportClientId =
|
|
94
194
|
| "opencode"
|
|
95
195
|
| "pi"
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { win32, join } from "node:path";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { exportPresentationLabel } from "../model-presentation";
|
|
4
|
-
import type { ExportContext, ManagedContribution, OpencodeLaunchEnv } from "./contracts";
|
|
4
|
+
import type { DroidReasoningDefaults, ExportContext, ExportModel, ManagedContribution, OpencodeLaunchEnv } from "./contracts";
|
|
5
5
|
import { normalizeExportModels } from "./model-metadata";
|
|
6
|
-
import { formatSelectorConjunction } from "../../integrations/merge";
|
|
6
|
+
import { formatSelectorConjunction, readPath } from "../../integrations/merge";
|
|
7
|
+
|
|
8
|
+
import { DROID_DEFAULT_EFFORT_HEADER } from "./contracts";
|
|
7
9
|
|
|
8
10
|
/** Factory personal settings: https://docs.factory.ai/model-independence/byok */
|
|
9
11
|
export interface DroidModelEntry {
|
|
@@ -12,6 +14,7 @@ export interface DroidModelEntry {
|
|
|
12
14
|
baseUrl: string;
|
|
13
15
|
provider: "generic-chat-completion-api";
|
|
14
16
|
noImageSupport: boolean;
|
|
17
|
+
extraHeaders?: Record<string, string>;
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
export interface DroidGeneratedConfig { customModels: DroidModelEntry[] }
|
|
@@ -43,11 +46,60 @@ function buildDroidRows(ctx: ExportContext): Array<{ row: DroidModelEntry; selec
|
|
|
43
46
|
baseUrl: ctx.baseUrl,
|
|
44
47
|
provider: "generic-chat-completion-api",
|
|
45
48
|
noImageSupport: !model.inputModalities?.includes("image"),
|
|
49
|
+
...(ctx.droidReasoningDefaults && Object.hasOwn(ctx.droidReasoningDefaults, model.namespaced)
|
|
50
|
+
? { extraHeaders: { [DROID_DEFAULT_EFFORT_HEADER]: ctx.droidReasoningDefaults[model.namespaced]! } }
|
|
51
|
+
: {}),
|
|
46
52
|
} });
|
|
47
53
|
}
|
|
48
54
|
return rows;
|
|
49
55
|
}
|
|
50
56
|
|
|
57
|
+
export function droidReasoningModels(models: readonly ExportModel[]): Array<{ model: string; label: string; efforts: string[] }> {
|
|
58
|
+
const effortsByModel = new Map(normalizeExportModels(models).map(model => [model.namespaced, model.reasoningEfforts ?? []]));
|
|
59
|
+
return buildDroidRows({ baseUrl: "", models }).map(({ row }) => ({
|
|
60
|
+
model: row.model,
|
|
61
|
+
label: row.displayName.replace(/^OpenCodex: /, ""),
|
|
62
|
+
efforts: effortsByModel.get(row.model) ?? [],
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function droidDefaultsFromOwnedRows(
|
|
67
|
+
ctx: ExportContext,
|
|
68
|
+
document: unknown,
|
|
69
|
+
fragmentPaths: readonly (readonly string[])[],
|
|
70
|
+
): DroidReasoningDefaults {
|
|
71
|
+
const exportableModels = new Map(droidReasoningModels(ctx.models).map(model => [model.model, model.efforts]));
|
|
72
|
+
const defaults: DroidReasoningDefaults = {};
|
|
73
|
+
for (const path of fragmentPaths) {
|
|
74
|
+
if (path.length !== 2 || path[0] !== "customModels") continue;
|
|
75
|
+
const value = readPath(document, path);
|
|
76
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
77
|
+
const current = value as Record<string, unknown>;
|
|
78
|
+
if (typeof current.model !== "string" || !exportableModels.has(current.model)) continue;
|
|
79
|
+
const headers = current.extraHeaders;
|
|
80
|
+
if (!headers || typeof headers !== "object" || Array.isArray(headers)) continue;
|
|
81
|
+
const header = headers as Record<string, unknown>;
|
|
82
|
+
const effort = header[DROID_DEFAULT_EFFORT_HEADER];
|
|
83
|
+
if (typeof effort !== "string" || !exportableModels.get(current.model)?.includes(effort)) continue;
|
|
84
|
+
defaults[current.model] = effort;
|
|
85
|
+
}
|
|
86
|
+
return defaults;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function validateDroidReasoningDefaults(
|
|
90
|
+
models: readonly ExportModel[],
|
|
91
|
+
defaults: unknown,
|
|
92
|
+
): string | null {
|
|
93
|
+
if (!defaults || typeof defaults !== "object" || Array.isArray(defaults)) return "droidReasoningDefaults must be an object";
|
|
94
|
+
const addressable = new Map(droidReasoningModels(models).map(model => [model.model, model.efforts]));
|
|
95
|
+
for (const [model, effort] of Object.entries(defaults)) {
|
|
96
|
+
const efforts = addressable.get(model);
|
|
97
|
+
if (!efforts) return `model ${model} is not currently exportable to Droid`;
|
|
98
|
+
if (typeof effort !== "string" || !efforts.includes(effort)) return `effort for ${model} is not declared by that model`;
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
51
103
|
export function buildDroidClientConfig(ctx: ExportContext): DroidGeneratedConfig {
|
|
52
104
|
const rows = buildDroidRows(ctx);
|
|
53
105
|
if (ctx.models.length > 0 && rows.length === 0) {
|
|
@@ -130,3 +130,24 @@ export function buildDshContribution(ctx: ExportContext): ManagedContribution {
|
|
|
130
130
|
const doc = buildDshClientConfig(ctx);
|
|
131
131
|
return singleFragment("dsh", ["llm-pi-ai", "providers", OPENCODE_PROVIDER_ID], doc["llm-pi-ai"].providers[OPENCODE_PROVIDER_ID]);
|
|
132
132
|
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Where DSH 0.1.7+ reads provider routes: the `llm-pi-ai` row of a profile
|
|
136
|
+
* patch, a top-level YAML list of loader rows. `$DSH_HOME/settings.yaml` is
|
|
137
|
+
* imported into the first profile that boots and renamed, so a write there
|
|
138
|
+
* reaches DSH once at most. The Desktop app runs the `desktop` profile and
|
|
139
|
+
* owns it alone, and DSH hot reloads that file when it changes.
|
|
140
|
+
*/
|
|
141
|
+
export const DSH_DESKTOP_PROFILE = "desktop";
|
|
142
|
+
export const DSH_PROFILE_PROVIDER_PATH = ["[id=llm-pi-ai]", "config", "providers", OPENCODE_PROVIDER_ID] as const;
|
|
143
|
+
|
|
144
|
+
/** A profile patch DSH itself would load: a list whose every row is a mapping. */
|
|
145
|
+
export function dshProfilePatchEstablished(parsed: unknown): boolean {
|
|
146
|
+
return Array.isArray(parsed)
|
|
147
|
+
&& parsed.every(row => typeof row === "object" && row !== null && !Array.isArray(row));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function buildDshProfilePatchContribution(ctx: ExportContext): ManagedContribution {
|
|
151
|
+
const doc = buildDshClientConfig(ctx);
|
|
152
|
+
return singleFragment("dsh", DSH_PROFILE_PROVIDER_PATH, doc["llm-pi-ai"].providers[OPENCODE_PROVIDER_ID]);
|
|
153
|
+
}
|
|
@@ -14,9 +14,11 @@ import {
|
|
|
14
14
|
normalizeExportModels,
|
|
15
15
|
opencodeModelCapabilities,
|
|
16
16
|
outputBudgetFor,
|
|
17
|
+
inputBudgetFor,
|
|
17
18
|
proxyAdmissionHeaders,
|
|
18
19
|
singleFragment,
|
|
19
20
|
} from "./model-metadata";
|
|
21
|
+
import { legacyReasoningMetadata, type LegacyEffortVariant } from "./reasoning-metadata";
|
|
20
22
|
|
|
21
23
|
export const KILO_CONFIG_CANDIDATES = [
|
|
22
24
|
"kilo.jsonc",
|
|
@@ -28,9 +30,14 @@ export const KILO_CONFIG_CANDIDATES = [
|
|
|
28
30
|
|
|
29
31
|
export interface KiloModelEntry {
|
|
30
32
|
name: string;
|
|
31
|
-
limit?: { context: number; output: number };
|
|
33
|
+
limit?: { context: number; input?: number; output: number };
|
|
32
34
|
attachment?: boolean;
|
|
33
35
|
modalities?: { input: string[]; output: string[] };
|
|
36
|
+
tool_call?: boolean;
|
|
37
|
+
reasoning?: boolean;
|
|
38
|
+
interleaved?: { field: "reasoning_content" };
|
|
39
|
+
options?: { reasoningEffort: string };
|
|
40
|
+
variants?: Record<string, LegacyEffortVariant>;
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
export interface KiloProviderBlock {
|
|
@@ -77,11 +84,14 @@ function kiloProviderBlock(ctx: ExportContext): KiloProviderBlock {
|
|
|
77
84
|
const config = ctx.config ?? OPENCODE_PROVIDER_BLOCK_DEFAULT_CONFIG;
|
|
78
85
|
const models: Record<string, KiloModelEntry> = {};
|
|
79
86
|
for (const model of normalizeExportModels(ctx.models)) {
|
|
80
|
-
const entry: KiloModelEntry = { name: exportModelLabel(model) };
|
|
87
|
+
const entry: KiloModelEntry = { name: exportModelLabel(model), ...legacyReasoningMetadata(model) };
|
|
81
88
|
const context = authoritativeContextWindow(model.contextWindow);
|
|
82
89
|
if (context !== undefined) {
|
|
83
90
|
entry.limit = { context, output: outputBudgetFor(context, model) };
|
|
91
|
+
const input = inputBudgetFor(context, model);
|
|
92
|
+
if (input !== undefined) entry.limit.input = input;
|
|
84
93
|
}
|
|
94
|
+
if (typeof model.supportsTools === "boolean") entry.tool_call = model.supportsTools;
|
|
85
95
|
const capabilities = opencodeModelCapabilities(model.inputModalities);
|
|
86
96
|
if (capabilities) {
|
|
87
97
|
entry.attachment = capabilities.attachment;
|
|
@@ -29,6 +29,12 @@ export function outputBudgetFor(context: number, model: OpencodeCatalogModel): n
|
|
|
29
29
|
return Math.min(limit, context);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/** Input limits are optional: unlike output, a missing limit needs no schema fallback. */
|
|
33
|
+
export function inputBudgetFor(context: number, model: OpencodeCatalogModel): number | undefined {
|
|
34
|
+
const limit = authoritativeContextWindow(model.maxInputTokens);
|
|
35
|
+
return limit === undefined ? undefined : Math.min(limit, context);
|
|
36
|
+
}
|
|
37
|
+
|
|
32
38
|
/**
|
|
33
39
|
* Modalities a given client's schema will actually accept.
|
|
34
40
|
*
|
|
@@ -89,12 +95,12 @@ export function inputModalitiesForClient(
|
|
|
89
95
|
const OPENCODE_INPUT_MODALITIES: ReadonlySet<string> = new Set(["text", "audio", "image", "video", "pdf"]);
|
|
90
96
|
|
|
91
97
|
/**
|
|
92
|
-
*
|
|
98
|
+
* Legacy per-model modality fields for one catalog row, or `undefined` when the row
|
|
93
99
|
* declares nothing.
|
|
94
100
|
*
|
|
95
|
-
* `undefined` rather than `{ input: ["text"] }`:
|
|
96
|
-
*
|
|
97
|
-
*
|
|
101
|
+
* `undefined` rather than guessing `{ input: ["text"] }`: unknown capabilities leave the
|
|
102
|
+
* client's fallback assumptions in place. V2 translates these lists into native capability
|
|
103
|
+
* fields; legacy clients also receive the attachment flag. A declared list is carried across as-is, so
|
|
98
104
|
* an audio-only row keeps `attachment: true` instead of being rewritten to text it cannot
|
|
99
105
|
* read — the same call Pi's exporter makes, in the opposite direction.
|
|
100
106
|
*/
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { canonicalizeReasoningEfforts, isDeclaredReasoningEffort } from "../../reasoning-effort";
|
|
2
|
+
import { knownReasoningSupport, type OpencodeCatalogModel } from "./contracts";
|
|
3
|
+
|
|
4
|
+
/** Unknown and explicitly empty ladders have different client-side meanings. */
|
|
5
|
+
export function exportReasoningEfforts(model: OpencodeCatalogModel): string[] | undefined {
|
|
6
|
+
return model.reasoningEfforts === undefined ? undefined : canonicalizeReasoningEfforts(model.reasoningEfforts);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Carry an authoritative default, never invent one or revive a cleared ladder. */
|
|
10
|
+
export function exportDefaultReasoningEffort(model: OpencodeCatalogModel): string | undefined {
|
|
11
|
+
const effort = model.defaultReasoningEffort;
|
|
12
|
+
if (typeof effort !== "string" || !isDeclaredReasoningEffort(effort)) return undefined;
|
|
13
|
+
const efforts = exportReasoningEfforts(model);
|
|
14
|
+
return efforts === undefined || efforts.includes(effort) ? effort : undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type LegacyEffortVariant = { reasoningEffort: string } | { disabled: true };
|
|
18
|
+
|
|
19
|
+
/** Shared V1-family controls. A disabled-only map suppresses client-invented effort ladders. */
|
|
20
|
+
export function legacyReasoningMetadata(model: OpencodeCatalogModel): {
|
|
21
|
+
reasoning?: boolean;
|
|
22
|
+
options?: { reasoningEffort: string };
|
|
23
|
+
interleaved?: { field: "reasoning_content" };
|
|
24
|
+
variants?: Record<string, LegacyEffortVariant>;
|
|
25
|
+
} {
|
|
26
|
+
const efforts = exportReasoningEfforts(model);
|
|
27
|
+
const reasoning = model.supportsReasoning ?? knownReasoningSupport({
|
|
28
|
+
...model, reasoningEfforts: efforts,
|
|
29
|
+
});
|
|
30
|
+
const defaultEffort = exportDefaultReasoningEffort(model);
|
|
31
|
+
// Both legacy runtimes merge generated variants with config, then filter disabled IDs.
|
|
32
|
+
// An unrelated sentinel leaves their generated choices alive. Disable the actual IDs
|
|
33
|
+
// instead (live-tested on Kilo 7.8.3, checked against stock OpenCode V1's merge).
|
|
34
|
+
const suppressed: Record<string, LegacyEffortVariant> = Object.fromEntries(
|
|
35
|
+
["none", "minimal", "low", "medium", "high", "xhigh", "max"].map(id => [id, { disabled: true }]),
|
|
36
|
+
);
|
|
37
|
+
const variants = efforts?.length
|
|
38
|
+
? Object.fromEntries(efforts.map(effort => [effort, { reasoningEffort: effort }]))
|
|
39
|
+
: reasoning === true ? suppressed : undefined;
|
|
40
|
+
return {
|
|
41
|
+
...(typeof reasoning === "boolean" ? { reasoning } : {}),
|
|
42
|
+
...(defaultEffort !== undefined ? { options: { reasoningEffort: defaultEffort } } : {}),
|
|
43
|
+
...(reasoning === true || model.supportsReasoningSummaries === true
|
|
44
|
+
? { interleaved: { field: "reasoning_content" } } : {}),
|
|
45
|
+
...(variants !== undefined ? { variants } : {}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -30,7 +30,7 @@ import { probeHostname } from "../server/proxy-liveness";
|
|
|
30
30
|
import type { OcxConfig } from "../types";
|
|
31
31
|
|
|
32
32
|
export type { ConfigFormat } from "../integrations/serialize";
|
|
33
|
-
export type { ManagedFragment, ManagedContribution, BuildContribution, OpencodeLaunchEnv, OpencodeCatalogModel, ExportModel, ExportContext, ExportClientId, ExportClientSpec, PiModelEntry } from "./config-export/contracts";
|
|
33
|
+
export type { ManagedFragment, ManagedContribution, BuildContribution, OpencodeLaunchEnv, OpencodeCatalogModel, ExportModel, ExportContext, ExportClientId, ExportClientSpec, PiModelEntry, DroidReasoningDefaults } from "./config-export/contracts";
|
|
34
34
|
export { OPENCODE_PROVIDER_ID, OPENCODE_CONFIG_SCHEMA, OPENCODE_API_KEY_ENV, OPENCODE_API_KEY_ENV_REF, KILO_API_KEY_ENV, KILO_API_KEY_ENV_REF, KILO_CONFIG_SCHEMA, HERMES_API_KEY_ENV, HERMES_API_KEY_ENV_REF, OPENCLAW_API_KEY_ENV, OPENCLAW_API_KEY_ENV_REF, LOOPBACK_API_KEY_PLACEHOLDER, GAJAE_API_KEY_ENV, SCHEMA_REQUIRED_OUTPUT_BUDGET, OPENCODE_PROVIDER_BLOCK_DEFAULT_CONFIG } from "./config-export/constants";
|
|
35
35
|
export { normalizeExportModels } from "./config-export/model-metadata";
|
|
36
36
|
export type { OmpModelEntry, OmpProviderBlock, OmpGeneratedConfig } from "./config-export/omp";
|
|
@@ -48,17 +48,19 @@ export {
|
|
|
48
48
|
zcodeStoreSchemaEstablished,
|
|
49
49
|
} from "./config-export/zcode-store";
|
|
50
50
|
export type { DshReasoningEffort, DshWireReasoningEffort, DshModelEntry, DshProviderBlock, DshGeneratedConfig } from "./config-export/dsh";
|
|
51
|
+
export { DSH_PROFILE_PROVIDER_PATH, dshProfilePatchEstablished, buildDshProfilePatchContribution } from "./config-export/dsh";
|
|
51
52
|
export type { McodeProviderBlock, McodeModelEntry, McodeGeneratedConfig } from "./config-export/mcode";
|
|
52
53
|
export type { RaycastAbility, RaycastAbilityName, RaycastModelEntry, RaycastProviderEntry, RaycastGeneratedConfig } from "./config-export/raycast";
|
|
53
54
|
export { buildRaycastClientConfig, summarizeRaycast, buildRaycastContribution } from "./config-export/raycast";
|
|
54
|
-
export { droidHomeDir, droidConfigPath, buildDroidClientConfig, summarizeDroid, buildDroidContribution } from "./config-export/droid";
|
|
55
|
+
export { droidHomeDir, droidConfigPath, buildDroidClientConfig, summarizeDroid, buildDroidContribution, droidReasoningModels, droidDefaultsFromOwnedRows, validateDroidReasoningDefaults } from "./config-export/droid";
|
|
55
56
|
export type { DroidModelEntry, DroidGeneratedConfig } from "./config-export/droid";
|
|
56
57
|
|
|
57
58
|
import type { OpencodeLaunchEnv, OpencodeCatalogModel, ExportContext, PiModelEntry, ManagedContribution, ManagedFragment, ExportClientId, ExportClientSpec } from "./config-export/contracts";
|
|
58
59
|
import { OPENCODE_API_KEY_ENV_REF, OPENCODE_PROVIDER_BLOCK_DEFAULT_CONFIG, OPENCODE_CONFIG_SCHEMA, OPENCODE_PROVIDER_ID, PI_API_DIALECT, LOOPBACK_API_KEY_PLACEHOLDER, HERMES_API_KEY_ENV_REF, OPENCLAW_API_KEY_ENV_REF, OPENCODE_API_KEY_ENV, HERMES_API_KEY_ENV, OPENCLAW_API_KEY_ENV, KILO_API_KEY_ENV } from "./config-export/constants";
|
|
59
|
-
import { exportModelLabel, authoritativeContextWindow, outputBudgetFor, normalizeExportModels, inputModalitiesForClient, opencodeModelCapabilities, proxyAdmissionHeaders, singleFragment } from "./config-export/model-metadata";
|
|
60
|
+
import { exportModelLabel, authoritativeContextWindow, outputBudgetFor, inputBudgetFor, normalizeExportModels, inputModalitiesForClient, opencodeModelCapabilities, proxyAdmissionHeaders, singleFragment } from "./config-export/model-metadata";
|
|
61
|
+
import { exportReasoningEfforts, exportDefaultReasoningEffort, legacyReasoningMetadata, type LegacyEffortVariant } from "./config-export/reasoning-metadata";
|
|
60
62
|
import { buildOmpClientConfig, summarizeOmp, buildOmpContribution } from "./config-export/omp";
|
|
61
|
-
import { buildDshClientConfig, summarizeDsh, buildDshContribution } from "./config-export/dsh";
|
|
63
|
+
import { buildDshClientConfig, summarizeDsh, buildDshContribution, DSH_DESKTOP_PROFILE } from "./config-export/dsh";
|
|
62
64
|
import { buildMcodeClientConfig, summarizeMcode, buildMcodeContribution } from "./config-export/mcode";
|
|
63
65
|
import { buildZcodeClientConfig, summarizeZcode, buildZcodeContribution } from "./config-export/zcode";
|
|
64
66
|
import { buildClineClientConfig, summarizeCline, buildClineContribution } from "./config-export/cline";
|
|
@@ -72,7 +74,7 @@ import { droidConfigPath, buildDroidClientConfig, summarizeDroid, buildDroidCont
|
|
|
72
74
|
|
|
73
75
|
export interface OpencodeModelEntry {
|
|
74
76
|
name: string;
|
|
75
|
-
limit?: { context: number; output: number };
|
|
77
|
+
limit?: { context: number; input?: number; output: number };
|
|
76
78
|
/**
|
|
77
79
|
* opencode's own capability fields, derived from the catalog row's declared input
|
|
78
80
|
* modalities. Written only when the row declares at least one — an entry without them is
|
|
@@ -81,22 +83,30 @@ export interface OpencodeModelEntry {
|
|
|
81
83
|
*/
|
|
82
84
|
attachment?: boolean;
|
|
83
85
|
modalities?: { input: string[]; output: string[] };
|
|
86
|
+
tool_call?: boolean;
|
|
87
|
+
reasoning?: boolean;
|
|
88
|
+
interleaved?: { field: "reasoning_content" };
|
|
89
|
+
options?: { reasoningEffort: string };
|
|
90
|
+
variants?: Record<string, LegacyEffortVariant>;
|
|
84
91
|
}
|
|
85
92
|
|
|
86
93
|
/**
|
|
87
94
|
* One selectable reasoning effort.
|
|
88
95
|
*
|
|
89
|
-
*
|
|
90
|
-
* legacy `provider` block is parsed and then dropped, so the V1 block stays variant-free
|
|
91
|
-
* rather than carrying fields that look configured but never reach a request.
|
|
96
|
+
* Native V2 variants use settings; legacy clients receive a separate options map.
|
|
92
97
|
*/
|
|
93
98
|
export interface OpencodeModelVariant {
|
|
94
99
|
id: string;
|
|
95
100
|
settings: { reasoningEffort: string };
|
|
96
101
|
}
|
|
97
102
|
|
|
98
|
-
export interface OpencodeV2ModelEntry
|
|
99
|
-
|
|
103
|
+
export interface OpencodeV2ModelEntry {
|
|
104
|
+
name: string;
|
|
105
|
+
limit?: { context: number; input?: number; output: number };
|
|
106
|
+
capabilities?: { tools: boolean; input?: string[]; output?: string[] };
|
|
107
|
+
compatibility?: { reasoningField: "reasoning_content" };
|
|
108
|
+
settings?: { reasoningEffort: string };
|
|
109
|
+
variants: OpencodeModelVariant[];
|
|
100
110
|
}
|
|
101
111
|
|
|
102
112
|
/** Endpoint and admission, spelled once and shared by both block generations. */
|
|
@@ -114,7 +124,7 @@ export interface OpencodeProviderBlock {
|
|
|
114
124
|
models: Record<string, OpencodeModelEntry>;
|
|
115
125
|
}
|
|
116
126
|
|
|
117
|
-
/** opencode V2 provider block: `package` + `settings
|
|
127
|
+
/** opencode V2 provider block: `package` + `settings` and native variant arrays. */
|
|
118
128
|
export interface OpencodeV2ProviderBlock {
|
|
119
129
|
package: string;
|
|
120
130
|
name: string;
|
|
@@ -147,16 +157,10 @@ export interface OpencodeGeneratedConfig {
|
|
|
147
157
|
const OPENCODE_PROVIDER_NPM = "@ai-sdk/openai-compatible";
|
|
148
158
|
|
|
149
159
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* or the provider is not loaded at all.
|
|
153
|
-
*
|
|
154
|
-
* Verified end-to-end against opencode 0.0.0-beta-18684: `GET /api/model` resolves this
|
|
155
|
-
* package for the provider and applies the per-model `variants`. opencode changes its
|
|
156
|
-
* provider package table between releases, so re-verify the supported range whenever it
|
|
157
|
-
* moves; a stale string breaks only the V2 block, silently.
|
|
160
|
+
* Canonical V2 runtime spelling. Current clients alias the old @opencode-ai scope,
|
|
161
|
+
* but exported documents use the documented package rather than depending on that alias.
|
|
158
162
|
*/
|
|
159
|
-
const OPENCODE_V2_PROVIDER_PACKAGE = "@opencode
|
|
163
|
+
const OPENCODE_V2_PROVIDER_PACKAGE = "@opencode/ai/providers/openai-compatible";
|
|
160
164
|
|
|
161
165
|
/** Display name for the provider block, identical in both generations. */
|
|
162
166
|
const OPENCODE_PROVIDER_NAME = "OpenCodex";
|
|
@@ -432,6 +436,11 @@ export function dshConfigPath(env: OpencodeLaunchEnv = process.env, home: string
|
|
|
432
436
|
return join(dshHomeDir(env, home), "settings.yaml");
|
|
433
437
|
}
|
|
434
438
|
|
|
439
|
+
/** The Desktop profile's patch, where DSH 0.1.7+ reads provider routes (see `DSH_PROFILE_PROVIDER_PATH`). */
|
|
440
|
+
export function dshProfilePatchPath(env: OpencodeLaunchEnv = process.env, home: string = homedir()): string {
|
|
441
|
+
return join(dshHomeDir(env, home), "profiles", DSH_DESKTOP_PROFILE, "cordis.patch.yml");
|
|
442
|
+
}
|
|
443
|
+
|
|
435
444
|
/**
|
|
436
445
|
* MiniMax Code stores runtime state under `MINIMAX_DATA_DIR`, then the legacy
|
|
437
446
|
* `MAVIS_DATA_DIR`, and finally `~/.minimax`. Relative overrides are refused
|
|
@@ -672,35 +681,11 @@ function opencodeProviderConnection(baseURL: string, config: OcxConfig): Opencod
|
|
|
672
681
|
}
|
|
673
682
|
|
|
674
683
|
/**
|
|
675
|
-
*
|
|
676
|
-
*
|
|
677
|
-
* No model-level `settings.reasoningEffort` default is emitted: the proxy already applies
|
|
678
|
-
* its own configured default when a request carries no effort, and pinning one here would
|
|
679
|
-
* override a default the user controls in opencodex. Variants are opt-in per selection,
|
|
680
|
-
* which is the same reason we never emit `defaultModel` for MCode.
|
|
681
|
-
*
|
|
682
|
-
* `none` is dropped even when a ladder declares it.
|
|
683
|
-
*
|
|
684
|
-
* The original reason no longer holds and is recorded here so it is not repeated: the chat
|
|
685
|
-
* ingress `OUTPUT_CONFIG_EFFORTS` allowlist DID omit `none`, so selecting it sent no effort
|
|
686
|
-
* at all and fell back to the proxy default. That allowlist now accepts `none` (audit F7),
|
|
687
|
-
* because it is the runtime's disable sentinel and dropping it let a provider default
|
|
688
|
-
* re-enable thinking a caller had turned off.
|
|
689
|
-
*
|
|
690
|
-
* The variant stays filtered anyway, deliberately and narrowly: emitting it would change
|
|
691
|
-
* what this exporter writes into a user's opencode config, and whether opencode's own
|
|
692
|
-
* picker round-trips `reasoningEffort: "none"` to the wire this proxy reads has not been
|
|
693
|
-
* verified here. Re-enabling it is a scoped follow-up that needs that check first, not a
|
|
694
|
-
* side effect of an ingress fix. MCode and ZCode filter `none` for their own separate
|
|
695
|
-
* reasons, documented at their call sites.
|
|
684
|
+
* Exact declared choices. An empty array is intentional: omitting variants lets OpenCode
|
|
685
|
+
* synthesize low/medium/high, even for a model whose upstream declares no adjustable effort.
|
|
696
686
|
*/
|
|
697
|
-
function opencodeEffortVariants(model: OpencodeCatalogModel): OpencodeModelVariant[]
|
|
698
|
-
|
|
699
|
-
// Canonical order (none, minimal, then low..ultra) and dedupe, so the picker order does
|
|
700
|
-
// not depend on whatever order a provider listed its efforts in.
|
|
701
|
-
const efforts = canonicalizeReasoningEfforts(model.reasoningEfforts).filter(effort => effort !== "none");
|
|
702
|
-
if (efforts.length === 0) return undefined;
|
|
703
|
-
return efforts.map(effort => ({ id: effort, settings: { reasoningEffort: effort } }));
|
|
687
|
+
function opencodeEffortVariants(model: OpencodeCatalogModel): OpencodeModelVariant[] {
|
|
688
|
+
return (exportReasoningEfforts(model) ?? []).map(effort => ({ id: effort, settings: { reasoningEffort: effort } }));
|
|
704
689
|
}
|
|
705
690
|
|
|
706
691
|
/**
|
|
@@ -712,7 +697,7 @@ function opencodeEffortVariants(model: OpencodeCatalogModel): OpencodeModelVaria
|
|
|
712
697
|
* the pair) clamped to the context window.
|
|
713
698
|
*
|
|
714
699
|
* Two blocks instead of one because opencode V2 reads the `providers` map and V1 reads
|
|
715
|
-
* `provider`,
|
|
700
|
+
* `provider`, with native arrays versus legacy maps for variants. Emitting both keeps V1 installs
|
|
716
701
|
* working: V2 merges them by provider id and model id, so a model listed in both blocks
|
|
717
702
|
* appears once, with the V2 entry's name, connection, and variants.
|
|
718
703
|
*/
|
|
@@ -725,36 +710,37 @@ export function opencodeProviderBlocks(
|
|
|
725
710
|
const v2Models: Record<string, OpencodeV2ModelEntry> = {};
|
|
726
711
|
for (const model of expandFastExportModels(catalogModels)) {
|
|
727
712
|
const key = model.namespaced;
|
|
728
|
-
const entry: OpencodeModelEntry = { name: exportModelLabel(model) };
|
|
713
|
+
const entry: OpencodeModelEntry = { name: exportModelLabel(model), ...legacyReasoningMetadata(model) };
|
|
729
714
|
const context = authoritativeContextWindow(model.contextWindow);
|
|
730
715
|
if (context !== undefined) {
|
|
731
716
|
entry.limit = { context, output: outputBudgetFor(context, model) };
|
|
717
|
+
const input = inputBudgetFor(context, model);
|
|
718
|
+
if (input !== undefined) entry.limit.input = input;
|
|
732
719
|
}
|
|
733
|
-
//
|
|
734
|
-
//
|
|
735
|
-
// 1.18.30, src/provider/provider.ts: `model.attachment ?? …` / `model.modalities?.input`).
|
|
736
|
-
// They ride on both generations anyway: the two blocks are two spellings of one model list,
|
|
737
|
-
// and the V2 model schema (capabilities.{tools,input,output}, which opencode fills by
|
|
738
|
-
// migrating this same `modalities` field) ignores keys it does not define — its loader
|
|
739
|
-
// decodes with `onExcessProperty: "ignore"`. Same values on both, so a merge cannot make
|
|
740
|
-
// the two entries disagree.
|
|
720
|
+
// Native V2 decoders ignore legacy attachment/modalities fields. Emit each generation's
|
|
721
|
+
// actual capability shape rather than relying on migration of the other provider block.
|
|
741
722
|
const capabilities = opencodeModelCapabilities(model.inputModalities);
|
|
742
723
|
if (capabilities) {
|
|
743
724
|
entry.attachment = capabilities.attachment;
|
|
744
725
|
entry.modalities = capabilities.modalities;
|
|
745
726
|
}
|
|
727
|
+
if (typeof model.supportsTools === "boolean") entry.tool_call = model.supportsTools;
|
|
746
728
|
v1Models[key] = entry;
|
|
747
|
-
const
|
|
748
|
-
//
|
|
749
|
-
//
|
|
750
|
-
// the
|
|
729
|
+
const defaultEffort = exportDefaultReasoningEffort(model);
|
|
730
|
+
// V2 requires tools whenever capabilities exists. A partial object discards the entire
|
|
731
|
+
// native block, losing explicit variants. Unknown tools use the legacy modality migration
|
|
732
|
+
// rather than inventing a tools flag to make the native schema accept the declaration.
|
|
733
|
+
const v2Capabilities: OpencodeV2ModelEntry["capabilities"] = typeof model.supportsTools === "boolean" ? {
|
|
734
|
+
tools: model.supportsTools,
|
|
735
|
+
...(capabilities ? { input: [...capabilities.modalities.input], output: [...capabilities.modalities.output] } : {}),
|
|
736
|
+
} : undefined;
|
|
751
737
|
v2Models[key] = {
|
|
752
|
-
|
|
738
|
+
name: entry.name,
|
|
753
739
|
...(entry.limit ? { limit: { ...entry.limit } } : {}),
|
|
754
|
-
...(
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
740
|
+
...(v2Capabilities ? { capabilities: v2Capabilities } : {}),
|
|
741
|
+
...(entry.interleaved ? { compatibility: { reasoningField: "reasoning_content" } } : {}),
|
|
742
|
+
...(defaultEffort !== undefined ? { settings: { reasoningEffort: defaultEffort } } : {}),
|
|
743
|
+
variants: opencodeEffortVariants(model),
|
|
758
744
|
};
|
|
759
745
|
}
|
|
760
746
|
return {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* dashboard or CLI pick, never creates the file, and refuses a file with comments: the write
|
|
7
7
|
* re-serializes JSON, and a comment the user wrote would be lost without a word.
|
|
8
8
|
*/
|
|
9
|
-
import { lstatSync, readFileSync } from "node:fs";
|
|
9
|
+
import { closeSync, constants, fstatSync, lstatSync, openSync, readFileSync } from "node:fs";
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { atomicWriteFileNoFollowUnclaimed } from "../config/atomic-write";
|
|
@@ -40,12 +40,26 @@ type Loaded =
|
|
|
40
40
|
|
|
41
41
|
function load(path: string): Loaded {
|
|
42
42
|
let text: string;
|
|
43
|
+
let fd: number | undefined;
|
|
43
44
|
try {
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
// POSIX FIFOs must not block before fstat can reject them. Windows omits
|
|
46
|
+
// these flags and retains the descriptor/path identity checks below.
|
|
47
|
+
const flags = process.platform === "win32"
|
|
48
|
+
? constants.O_RDONLY
|
|
49
|
+
: constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK;
|
|
50
|
+
fd = openSync(path, flags);
|
|
51
|
+
const opened = fstatSync(fd, { bigint: true });
|
|
52
|
+
const current = lstatSync(path, { bigint: true });
|
|
53
|
+
if (!opened.isFile() || !current.isFile() || opened.dev !== current.dev || opened.ino !== current.ino) {
|
|
54
|
+
return { kind: "invalid" };
|
|
55
|
+
}
|
|
56
|
+
text = readFileSync(fd, "utf8");
|
|
46
57
|
} catch (error) {
|
|
47
58
|
if ((error as NodeJS.ErrnoException).code === "ENOENT") return { kind: "absent" };
|
|
59
|
+
if ((error as NodeJS.ErrnoException).code === "ELOOP") return { kind: "invalid" };
|
|
48
60
|
throw error;
|
|
61
|
+
} finally {
|
|
62
|
+
if (fd !== undefined) closeSync(fd);
|
|
49
63
|
}
|
|
50
64
|
if (hasJsoncComments(text)) return { kind: "comments" };
|
|
51
65
|
try {
|