@bitkyc08/opencodex 2.82.0 → 2.83.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-Ds_qL7o8.js → App-Ba8uG696.js} +4 -4
- package/gui/dist/assets/{Tray-BqHF5Trd.js → Tray-IImbNPRy.js} +1 -1
- package/gui/dist/assets/{index-D3FBCNiP.js → index-BHRZdJuv.js} +12 -12
- package/gui/dist/assets/{quota-summary-DTBC8n7-.js → quota-summary-DiJO6sYY.js} +1 -1
- package/gui/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/adapters/cursor/catalog.ts +2 -2
- package/src/adapters/cursor/effort-map.ts +1 -1
- package/src/adapters/devin/live-models.ts +2 -2
- package/src/adapters/google-truncation.ts +22 -0
- package/src/adapters/google.ts +7 -3
- package/src/adapters/ollama-native.ts +18 -14
- package/src/adapters/openai-responses/passthrough.ts +2 -0
- package/src/adapters/openai-responses/plaintext-reasoning-notice.ts +85 -0
- package/src/adapters/openai-responses/reasoning.ts +11 -1
- package/src/bridge/errors.ts +7 -1
- package/src/claude/context-windows.ts +3 -3
- package/src/claude/desktop-picker.ts +1 -1
- package/src/cli/account-auth.ts +20 -12
- package/src/cli/resolve.ts +1 -1
- package/src/clients/config-export/model-metadata.ts +16 -0
- package/src/clients/config-export/zcode-store.ts +46 -11
- package/src/clients/config-export/zcode.ts +3 -5
- package/src/clients/config-export.ts +1 -1
- package/src/codex/auth-api/pool-mode-gate.ts +4 -5
- package/src/codex/auth-context.ts +8 -3
- package/src/codex/catalog/native-models.ts +1 -1
- package/src/codex/catalog/provider-models.ts +25 -0
- package/src/codex/desktop-app/darwin.ts +1 -1
- package/src/codex/desktop-app/handoff.ts +1 -1
- package/src/codex/desktop-app/linux.ts +1 -1
- package/src/codex/desktop-app/lock.ts +1 -1
- package/src/codex/desktop-app/types.ts +1 -1
- package/src/codex/desktop-app/windows.ts +1 -1
- package/src/codex/desktop-app-restart.ts +1 -1
- package/src/codex/main-account.ts +6 -7
- package/src/generated/compatibility-version.json +91 -75
- package/src/grok/inject.ts +245 -221
- package/src/grok/reset-coupon-ledger.ts +223 -57
- package/src/grok/reset-coupons.ts +13 -6
- package/src/integrations/cursor-detect.ts +1 -1
- package/src/lib/errors.ts +25 -0
- package/src/lib/redact.ts +2 -2
- package/src/oauth/index.ts +1 -1
- package/src/oauth/meta-muse-device.ts +1 -1
- package/src/oauth/types.ts +1 -1
- package/src/providers/anthropic-fast.ts +1 -1
- package/src/providers/anthropic-reset-grant-ledger.ts +1 -1
- package/src/providers/devin-cli-authmode-migration.ts +1 -1
- package/src/providers/devin-provider-merge-migration.ts +1 -1
- package/src/providers/github-copilot-auto.ts +169 -0
- package/src/providers/model-presets.ts +1 -1
- package/src/providers/muse-subscription-usage.ts +1 -1
- package/src/providers/registry/entries-core.ts +6 -6
- package/src/providers/registry/entries-extended.ts +4 -0
- package/src/providers/registry/model-seeds.ts +3 -3
- package/src/providers/xai-fast-model.ts +1 -1
- package/src/server/inbound-body-admission.ts +24 -7
- package/src/server/index/link-listener.ts +30 -17
- package/src/server/index/serve-options.ts +8 -2
- package/src/server/index.ts +1 -1
- package/src/server/lifecycle.ts +31 -4
- package/src/server/management/grok-coupon-routes.ts +277 -52
- package/src/server/management/link-routes.ts +42 -20
- package/src/server/management/route-registry.ts +2 -2
- package/src/server/relay-eager.ts +2 -0
- package/src/server/relay.ts +18 -7
- package/src/server/request-log-failure-attribution.ts +3 -0
- package/src/server/request-log-terminal-status.ts +186 -0
- package/src/server/request-log.ts +15 -118
- package/src/server/responses/adapter-continuation.ts +9 -1
- package/src/server/responses/adapter-dispatch.ts +65 -6
- package/src/server/responses/compact.ts +3 -2
- package/src/server/responses/core-opaque-recovery.ts +5 -5
- package/src/server/responses/core.ts +6 -6
- package/src/server/responses/empty-completion-guard.ts +18 -2
- package/src/server/responses/jev-model-invoke.ts +1 -1
- package/src/server/responses/passthrough-dispatch.ts +115 -60
- package/src/server/responses/passthrough-execution.ts +3 -3
- package/src/server/responses/request-transport.ts +125 -5
- package/src/server/responses/run-turn-execution.ts +13 -5
- package/src/server/responses/sidecar-execution.ts +5 -0
- package/src/server/responses/spend-storage-error.ts +33 -0
- package/src/types/request.ts +5 -0
- package/src/usage/expected-prices.ts +1 -1
package/src/cli/account-auth.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { warnIfCodexCatalogRefreshPending } from "./account-catalog-refresh";
|
|
|
6
6
|
import { isCodexResetCreditOperationId } from "../codex/reset-credit-recovery";
|
|
7
7
|
import { BROWSER_LAUNCH_FAILED_NOTICE } from "../lib/browser-launch-notice";
|
|
8
8
|
import {
|
|
9
|
+
RuntimeApiError,
|
|
9
10
|
CliUsageError,
|
|
10
11
|
printData,
|
|
11
12
|
readSecretLine,
|
|
@@ -465,18 +466,25 @@ async function grokResetCoupons(argv: string[], deps: RuntimeApiDeps): Promise<v
|
|
|
465
466
|
}
|
|
466
467
|
rejectArgs(args, USAGE);
|
|
467
468
|
const accountId = rawId ? (rawId === "main" ? "__main__" : rawId) : undefined;
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
469
|
+
const effectiveOperationId = consume ? operationId ?? crypto.randomUUID() : undefined;
|
|
470
|
+
let result: unknown;
|
|
471
|
+
try {
|
|
472
|
+
result = consume
|
|
473
|
+
? await runtimeRequest("/api/grok/reset-coupons/consume", {
|
|
474
|
+
method: "POST", body: JSON.stringify({ accountId, tokenId, operationId: effectiveOperationId }),
|
|
475
|
+
}, deps)
|
|
476
|
+
: await runtimeRequest(`/api/grok/reset-coupons${accountId ? `?accountId=${encodeURIComponent(accountId)}` : ""}`, {}, deps);
|
|
477
|
+
} catch (error) {
|
|
478
|
+
const apiError = error instanceof RuntimeApiError ? error : undefined;
|
|
479
|
+
const body = apiError?.body as { error?: { code?: string } } | null | undefined;
|
|
480
|
+
const code = body && typeof body === "object" ? body.error?.code : undefined;
|
|
481
|
+
if (consume && (!apiError || (!apiError.code && (
|
|
482
|
+
(!code && apiError.status >= 500) || ["attempt_unresolved", "attempt_in_progress", "attempt_reconcile_failed", "operation_state_changed", "redeem_failed"].includes(code ?? "")
|
|
483
|
+
)))) {
|
|
484
|
+
throw new RuntimeApiError(`Coupon redemption outcome is unconfirmed. Preserve --operation-id ${effectiveOperationId}; reuse it to inspect this attempt and do not create a replacement operation.`, apiError?.status ?? 503, apiError?.body ?? null);
|
|
485
|
+
}
|
|
486
|
+
throw error;
|
|
487
|
+
}
|
|
480
488
|
printData(result, wantsJson);
|
|
481
489
|
}
|
|
482
490
|
|
package/src/cli/resolve.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `ocx resolve` — the machine-readable runtime resolution surface for an embedding shell.
|
|
3
3
|
*
|
|
4
|
-
* D5 of devlog/
|
|
4
|
+
* D5 of devlog/_fin/260921_app_runtime_ownership/: the desktop shell must stop resolving
|
|
5
5
|
* the config home, the port and liveness itself. The tuned probe budgets in
|
|
6
6
|
* src/server/proxy-liveness.ts exist because a shell-side reimplementation answered
|
|
7
7
|
* "nobody listening" twice and started duplicate proxies; this verb exposes that module's
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { getModelMetadata, resolveMetadataProvider } from "../../generated/model-metadata";
|
|
3
3
|
import { SCHEMA_REQUIRED_OUTPUT_BUDGET } from "./constants";
|
|
4
4
|
import { expandFastExportModels } from "./fast-models";
|
|
5
|
+
import { sanitizeCodexReasoningEfforts } from "../../reasoning-effort";
|
|
5
6
|
import type { OpencodeCatalogModel, ExportModel, ExportClientId, ManagedContribution } from "./contracts";
|
|
6
7
|
import type { OcxConfig } from "../../types";
|
|
7
8
|
import { shouldInjectApiAuthHeader } from "../../codex/inject";
|
|
@@ -84,6 +85,21 @@ export function inputModalitiesForClient(
|
|
|
84
85
|
return kept.length > 0 ? kept : null;
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Effort ladder a ZCode surface can offer as picker values, or undefined when the row
|
|
90
|
+
* has nothing selectable.
|
|
91
|
+
*
|
|
92
|
+
* Both ZCode exports — the legacy `v2/config.json` block's `reasoning.variants` and the
|
|
93
|
+
* `v2/provider_config.json` store's `optionSpecs.reasoningLevel.values` — carry the same
|
|
94
|
+
* Codex vocabulary: the client validates the chosen level against the emitted list and
|
|
95
|
+
* forwards it verbatim, and `/v1/responses` reads that vocabulary natively. `none` is a
|
|
96
|
+
* Codex omit-sentinel, not a picker option, so it never survives into the values.
|
|
97
|
+
*/
|
|
98
|
+
export function zcodeSelectableEfforts(efforts: readonly string[] | undefined): string[] | undefined {
|
|
99
|
+
const selectable = sanitizeCodexReasoningEfforts(efforts)?.filter(effort => effort !== "none");
|
|
100
|
+
return selectable && selectable.length > 0 ? selectable : undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
87
103
|
/**
|
|
88
104
|
* Input modalities opencode's model schema accepts (opencode.ai/config.json, both
|
|
89
105
|
* `modalities.input` and `modalities.output`). Wider than our internal `text | image | audio`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// ZCode personal provider store export.
|
|
2
2
|
import type { ExportContext, ManagedContribution, ManagedFragment } from "./contracts";
|
|
3
|
-
import { authoritativeContextWindow, inputModalitiesForClient, normalizeExportModels } from "./model-metadata";
|
|
3
|
+
import { authoritativeContextWindow, inputModalitiesForClient, normalizeExportModels, zcodeSelectableEfforts } from "./model-metadata";
|
|
4
4
|
import { OPENCODE_PROVIDER_ID, LOOPBACK_API_KEY_PLACEHOLDER } from "./constants";
|
|
5
5
|
import { formatSelectorConjunction } from "../../integrations/merge";
|
|
6
6
|
|
|
@@ -49,10 +49,30 @@ export interface ZcodeStoreProviderRule {
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* One model rule, as the client's own store loader accepts it.
|
|
54
|
+
*
|
|
55
|
+
* The capability fields are the client's schema, not ours: ZCode 3.14's CLI
|
|
56
|
+
* bundle parses each `providerModelRules` entry with a strict object whose
|
|
57
|
+
* `config.properties` admits `inputFormat.{supportsText..supportsPdf}` (booleans)
|
|
58
|
+
* and whose `config.optionSpecs` admits `reasoningLevel.values` (non-empty,
|
|
59
|
+
* deduplicated) plus an optional compiled `map`. With no `map`, the chosen level
|
|
60
|
+
* is validated against `values` and forwarded verbatim to the wire field the
|
|
61
|
+
* `openai-responses` api type uses — `reasoning.effort`, which is exactly the
|
|
62
|
+
* Codex vocabulary our ladder already carries, so no mapping is emitted.
|
|
63
|
+
*/
|
|
52
64
|
export interface ZcodeStoreModelRule {
|
|
53
65
|
providerId: string;
|
|
54
66
|
modelId: string;
|
|
55
|
-
config: {
|
|
67
|
+
config: {
|
|
68
|
+
properties: {
|
|
69
|
+
contextWindow?: number;
|
|
70
|
+
inputFormat?: { supportsImage: true };
|
|
71
|
+
};
|
|
72
|
+
optionSpecs?: {
|
|
73
|
+
reasoningLevel: { values: string[] };
|
|
74
|
+
};
|
|
75
|
+
};
|
|
56
76
|
}
|
|
57
77
|
|
|
58
78
|
/**
|
|
@@ -77,7 +97,7 @@ export function zcodeStoreSchemaEstablished(parsed: unknown): boolean {
|
|
|
77
97
|
* carrying only the model would match another provider's rule for the same
|
|
78
98
|
* model. The conjunction grammar reserves `,` and `]`, and a model id holding
|
|
79
99
|
* either cannot be addressed unambiguously — that row ships without its
|
|
80
|
-
*
|
|
100
|
+
* model rule rather than with a selector that points somewhere else.
|
|
81
101
|
*/
|
|
82
102
|
function modelRuleSelector(modelId: string): string | null {
|
|
83
103
|
return formatSelectorConjunction([
|
|
@@ -128,12 +148,18 @@ export function buildZcodeStoreProviderRule(ctx: ExportContext): ZcodeStoreProvi
|
|
|
128
148
|
|
|
129
149
|
/**
|
|
130
150
|
* Everything this project owns inside the store: one provider rule, plus one
|
|
131
|
-
* model rule per row that has
|
|
151
|
+
* model rule per row that has anything authoritative to assert.
|
|
132
152
|
*
|
|
133
|
-
* A
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
153
|
+
* A model rule now exists when the row has any of: an authoritative context
|
|
154
|
+
* window, image input, or a selectable effort ladder. A row with none of the
|
|
155
|
+
* three ships no rule at all, which is the same no-guessed-capability rule the
|
|
156
|
+
* legacy block follows. Image input is asserted only when the catalog declares
|
|
157
|
+
* it — `supportsImage: true` is what gates the client's attachment UI, and a
|
|
158
|
+
* text-only row keeps the client's own defaults rather than being pinned to
|
|
159
|
+
* `false`. `reasoningLevel.values` carries the same ladder (minus the `none`
|
|
160
|
+
* sentinel) the legacy block's `reasoning.variants` carries, so both surfaces
|
|
161
|
+
* offer the picker the catalog declared instead of the store silently losing
|
|
162
|
+
* reasoning and vision the way #5348 lost the provider itself.
|
|
137
163
|
*/
|
|
138
164
|
export function buildZcodeStoreContribution(ctx: ExportContext): ManagedContribution {
|
|
139
165
|
const fragments: ManagedFragment[] = [{
|
|
@@ -141,15 +167,24 @@ export function buildZcodeStoreContribution(ctx: ExportContext): ManagedContribu
|
|
|
141
167
|
value: buildZcodeStoreProviderRule(ctx),
|
|
142
168
|
}];
|
|
143
169
|
for (const model of normalizeExportModels(ctx.models)) {
|
|
144
|
-
|
|
170
|
+
const input = inputModalitiesForClient("pi", model.inputModalities);
|
|
171
|
+
if (input === null) continue;
|
|
145
172
|
const contextWindow = authoritativeContextWindow(model.contextWindow);
|
|
146
|
-
|
|
173
|
+
const supportsImage = input.includes("image");
|
|
174
|
+
const efforts = zcodeSelectableEfforts(model.reasoningEfforts);
|
|
175
|
+
if (contextWindow === undefined && !supportsImage && efforts === undefined) continue;
|
|
147
176
|
const selector = modelRuleSelector(model.namespaced);
|
|
148
177
|
if (selector === null) continue;
|
|
149
178
|
const rule: ZcodeStoreModelRule = {
|
|
150
179
|
providerId: OPENCODE_PROVIDER_ID,
|
|
151
180
|
modelId: model.namespaced,
|
|
152
|
-
config: {
|
|
181
|
+
config: {
|
|
182
|
+
properties: {
|
|
183
|
+
...(contextWindow !== undefined ? { contextWindow } : {}),
|
|
184
|
+
...(supportsImage ? { inputFormat: { supportsImage: true } } : {}),
|
|
185
|
+
},
|
|
186
|
+
...(efforts !== undefined ? { optionSpecs: { reasoningLevel: { values: efforts } } } : {}),
|
|
187
|
+
},
|
|
153
188
|
};
|
|
154
189
|
fragments.push({ path: [...ZCODE_STORE_MODEL_RULES_PATH, selector], value: rule });
|
|
155
190
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// ZCode config export.
|
|
2
2
|
import type { ExportContext, ManagedContribution } from "./contracts";
|
|
3
|
-
import { normalizeExportModels, inputModalitiesForClient, exportModelLabel, authoritativeContextWindow, singleFragment } from "./model-metadata";
|
|
4
|
-
import { sanitizeCodexReasoningEfforts } from "../../reasoning-effort";
|
|
3
|
+
import { normalizeExportModels, inputModalitiesForClient, exportModelLabel, authoritativeContextWindow, singleFragment, zcodeSelectableEfforts } from "./model-metadata";
|
|
5
4
|
import { OPENCODE_PROVIDER_ID, LOOPBACK_API_KEY_PLACEHOLDER } from "./constants";
|
|
6
5
|
|
|
7
6
|
|
|
@@ -86,9 +85,8 @@ export function buildZcodeClientConfig(ctx: ExportContext): ZcodeGeneratedConfig
|
|
|
86
85
|
// `ultra` when present: ZCode forwards the selected variant to the wire field its
|
|
87
86
|
// kind uses — `reasoning.effort` on `openai`, which is what `/v1/responses` reads
|
|
88
87
|
// natively. Set `defaultVariant` only when it survives that filter.
|
|
89
|
-
const efforts =
|
|
90
|
-
|
|
91
|
-
if (efforts && efforts.length > 0) {
|
|
88
|
+
const efforts = zcodeSelectableEfforts(model.reasoningEfforts);
|
|
89
|
+
if (efforts !== undefined) {
|
|
92
90
|
const defaultVariant = model.defaultReasoningEffort?.trim().toLowerCase();
|
|
93
91
|
entry.reasoning = {
|
|
94
92
|
enabled: true,
|
|
@@ -1367,7 +1367,7 @@ function buildAsideContribution(ctx: ExportContext): ManagedContribution {
|
|
|
1367
1367
|
* the keyed `providers`, the `models` ARRAY whose identity is `id`, the
|
|
1368
1368
|
* `openai-completions` dialect, the loopback placeholder, and the
|
|
1369
1369
|
* `thinkingLevelMap` levels. Evidence:
|
|
1370
|
-
* `devlog/
|
|
1370
|
+
* `devlog/_fin/260912_omo_client_integration/001_omo_contract.md`.
|
|
1371
1371
|
*
|
|
1372
1372
|
* The flag is passed HERE as well as in the spec's `build`, which is the one
|
|
1373
1373
|
* thing Prime and Aside do not do. They pass the default on both paths, so they
|
|
@@ -88,14 +88,13 @@ export async function runMainAccountHardLockRecovery(config: OcxConfig): Promise
|
|
|
88
88
|
const now = Date.now();
|
|
89
89
|
const quota = getMainPolicyQuota();
|
|
90
90
|
const observedAt = quota?.credits?.observedAt;
|
|
91
|
-
//
|
|
92
|
-
//
|
|
91
|
+
// Positive evidence renews before expiry; unavailable evidence keeps discovery
|
|
92
|
+
// eligible under the same pacing. Admission still requires fresh spendable funds.
|
|
93
93
|
return codexAccountUsesCreditsAfterLimit(config, MAIN_CODEX_ACCOUNT_ID)
|
|
94
94
|
&& !isCodexAccountPaused(config, MAIN_CODEX_ACCOUNT_ID)
|
|
95
95
|
&& codexUsageLimitResetAt(quota, undefined, now) !== undefined
|
|
96
|
-
&&
|
|
97
|
-
|
|
98
|
-
&& hasSpendableCodexCredits(quota, observedAt);
|
|
96
|
+
&& (!hasSpendableCodexCredits(quota, now)
|
|
97
|
+
|| (observedAt !== undefined && now - observedAt >= MAIN_CREDIT_RENEWAL_AGE_MS));
|
|
99
98
|
};
|
|
100
99
|
const status = getMainAccountHardLockStatus(config);
|
|
101
100
|
if (status.state !== "blocked" && !creditRecoveryNeeded()) { mainHardLockRecoveryAttempt = undefined; return; }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { bindPoolCreditPolicy, poolCreditHoldResetAt, poolContextCreditHoldResetAt } from "./pool-credit-policy";
|
|
2
2
|
import { hasSpendableCodexCredits } from "./quota-types";
|
|
3
|
+
import { nextQuotaQueryDelay } from "./quota-query-backoff";
|
|
3
4
|
import { noteMainAccountActivity } from "./main-account-external-usage";
|
|
4
5
|
import { codexAccountPriorityFailbackEnabled } from "./account-priority";
|
|
5
6
|
import type { PoolQuotaWriter } from "./quota-types";
|
|
@@ -553,8 +554,11 @@ function assertPoolAccountCredits(accountId: string, resetAt: number | undefined
|
|
|
553
554
|
export class CodexMainAccountCreditsOffError extends CodexAccountCooldownError {
|
|
554
555
|
readonly resetAt?: number;
|
|
555
556
|
|
|
556
|
-
constructor(resetAt?: number) {
|
|
557
|
-
|
|
557
|
+
constructor(resetAt?: number, creditRecovery = false) {
|
|
558
|
+
// Keep the included reset as policy evidence, but let consented callers retry
|
|
559
|
+
// within the base metadata recovery interval even when backoff grows longer.
|
|
560
|
+
super(MAIN_CODEX_ACCOUNT_ID, creditRecovery
|
|
561
|
+
? Math.min(resetAt ?? Infinity, Date.now() + nextQuotaQueryDelay()) : resetAt ?? 0);
|
|
558
562
|
this.name = "CodexMainAccountCreditsOffError";
|
|
559
563
|
this.resetAt = resetAt;
|
|
560
564
|
this.message = "Codex main account reached its usage limit, and spending ChatGPT credits is off or no fresh spendable balance is available."
|
|
@@ -778,7 +782,8 @@ function assertMainAccountPolicy(
|
|
|
778
782
|
const status = getMainAccountHardLockStatus(config);
|
|
779
783
|
if (status.state === "blocked") throw new CodexMainAccountHardLockError(status.resetAt, status.thresholds);
|
|
780
784
|
const creditsResetAt = mainCreditsHoldResetAt(config);
|
|
781
|
-
if (creditsResetAt !== undefined) throw new CodexMainAccountCreditsOffError(creditsResetAt
|
|
785
|
+
if (creditsResetAt !== undefined) throw new CodexMainAccountCreditsOffError(creditsResetAt,
|
|
786
|
+
codexAccountUsesCreditsAfterLimit(config, MAIN_CODEX_ACCOUNT_ID));
|
|
782
787
|
}
|
|
783
788
|
// Only an admitted request is opencodex's own use of the main account. Counting a refused one
|
|
784
789
|
// would hide outside usage from the warning exactly while the lock is holding.
|
|
@@ -93,7 +93,7 @@ export const CONFIGURED_NATIVE_OPENAI_TEMPLATE_MODEL = NATIVE_GPT61_SOL_MODEL;
|
|
|
93
93
|
*
|
|
94
94
|
* `gpt-daybreak-blue-latest` stays gated. It has no shipped catalog row anywhere, so absence is
|
|
95
95
|
* the only signal that exists for it, and the ungating decision was scoped to the flagships.
|
|
96
|
-
* Evidence: devlog/
|
|
96
|
+
* Evidence: devlog/_fin/260904_flagship_native_always_visible/.
|
|
97
97
|
*/
|
|
98
98
|
export const ACCOUNT_GATED_NATIVE_OPENAI_MODELS: ReadonlySet<string> = new Set([
|
|
99
99
|
NATIVE_DAYBREAK_BLUE_MODEL,
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { fetchCopilotAutoModels, copilotPickerModels, copilotRequiresAuto } from "../../providers/github-copilot-auto";
|
|
2
|
+
import { resolveGithubCopilotTransport } from "../../providers/github-copilot-transport";
|
|
3
|
+
import { resolveCopilotApiBaseUrl } from "../../oauth/github-copilot";
|
|
1
4
|
import { isAnthropicOAuthInstance } from "../../providers/anthropic-instance";
|
|
2
5
|
import { effectiveProviderAlias, effectiveProviderAliasDecision } from "../../providers/default-aliases";
|
|
3
6
|
import { initialModelSelectionPending } from "../../providers/initial-model-selection";
|
|
@@ -164,6 +167,7 @@ export function observedModelsAuthResolver(
|
|
|
164
167
|
},
|
|
165
168
|
};
|
|
166
169
|
}
|
|
170
|
+
/** Project captured discovery through config hints and retention; Auto-only Copilot exposes only Auto. */
|
|
167
171
|
export async function fetchProviderModelsWithAuth(
|
|
168
172
|
captured: CapturedProviderGather,
|
|
169
173
|
ttlMs: number,
|
|
@@ -282,6 +286,27 @@ export async function fetchProviderModelsWithAuth(
|
|
|
282
286
|
: { apiKey: await resolveModelsAuthToken(name, prov), observed: false }
|
|
283
287
|
: resolveAuth.resolve(name, prov));
|
|
284
288
|
const apiKey = auth.apiKey;
|
|
289
|
+
if (name === "github-copilot" && apiKey) {
|
|
290
|
+
const transport = resolveGithubCopilotTransport({ ...prov, apiKey }, prov.authMode === "oauth"
|
|
291
|
+
? resolveCopilotApiBaseUrl(auth.oauthApiBaseUrl) : undefined);
|
|
292
|
+
try {
|
|
293
|
+
const models = await fetchCopilotAutoModels(transport, AbortSignal.timeout(8_000), undefined, ttlMs);
|
|
294
|
+
const picker = copilotPickerModels(prov, models).map(/** Apply configured hints to the permission-filtered account catalog without changing upstream metadata. */
|
|
295
|
+
model => applyProviderConfigHints(name, prov, {
|
|
296
|
+
id: model.id, provider: name,
|
|
297
|
+
...catalogHintsFromModelsApiItem(name, model as ProviderModelsApiItem),
|
|
298
|
+
}, contextCap, metadataModelIdCaseFold, captured.effectiveAlias));
|
|
299
|
+
if (!isCurrentCacheGeneration()) return observed(configured, "degraded");
|
|
300
|
+
markProviderDiscoveryOk(name, picker.length);
|
|
301
|
+
// Retain callable manual/combo selectors, but do not advertise named selection
|
|
302
|
+
// for an Auto-only account even when its saved configuration still contains it.
|
|
303
|
+
return observed(copilotRequiresAuto(prov, models)
|
|
304
|
+
? picker : withConfiguredRetention(picker, { warnDrops: true }), "authoritative");
|
|
305
|
+
} catch {
|
|
306
|
+
if (isCurrentCacheGeneration()) markProviderDiscoveryFailed(name, { reason: "provider" });
|
|
307
|
+
return observed(configured, "degraded");
|
|
308
|
+
}
|
|
309
|
+
}
|
|
285
310
|
const maySendAnthropicDiscovery = () => {
|
|
286
311
|
if (!isAnthropicOAuthInstance(name) || prov.authMode !== "oauth") return true;
|
|
287
312
|
if (!mayResolveModelsOAuth(name, prov)) return false;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* macOS adapter for the Codex desktop-app restart.
|
|
3
3
|
*
|
|
4
|
-
* Measured shape (devlog/
|
|
4
|
+
* Measured shape (devlog/_fin/260913_cross_platform_desktop_app_restart/001_platform_topology.md):
|
|
5
5
|
*
|
|
6
6
|
* 15901 1 /Applications/ChatGPT.app/Contents/MacOS/ChatGPT
|
|
7
7
|
* 16733 15901 /Applications/ChatGPT.app/Contents/Resources/codex ... app-server ...
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* it passes `allowHandoff: false` so it can only ever take the direct path or refuse.
|
|
24
24
|
* Recursion is structurally impossible rather than merely unlikely.
|
|
25
25
|
*
|
|
26
|
-
* Design: devlog/
|
|
26
|
+
* Design: devlog/_fin/260913_cross_platform_desktop_app_restart/020_phase2_detached_self_handoff.md
|
|
27
27
|
*/
|
|
28
28
|
import { spawn } from "node:child_process";
|
|
29
29
|
import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, unlinkSync, writeSync } from "node:fs";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Linux adapter for the Codex desktop-app restart.
|
|
3
3
|
*
|
|
4
|
-
* Measured shape (devlog/
|
|
4
|
+
* Measured shape (devlog/_fin/260913_cross_platform_desktop_app_restart/001_platform_topology.md):
|
|
5
5
|
*
|
|
6
6
|
* /usr/bin/chatgpt -> /usr/lib/chatgpt/codex-launcher (2-line sh script)
|
|
7
7
|
* that execs /usr/lib/chatgpt/ChatGPT
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* itself. An unconditional unlink would let a late release destroy somebody else's
|
|
23
23
|
* live lock, which is exactly the mutual exclusion this file exists to provide.
|
|
24
24
|
*
|
|
25
|
-
* Design: `devlog/
|
|
25
|
+
* Design: `devlog/_fin/260913_cross_platform_desktop_app_restart/020_phase2_detached_self_handoff.md` §4.1.
|
|
26
26
|
*/
|
|
27
27
|
import { mkdirSync, openSync, closeSync, writeSync, readFileSync, unlinkSync, renameSync } from "node:fs";
|
|
28
28
|
import { join, dirname } from "node:path";
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* PID-reuse and fail-closed reasoning from being re-derived three times, once per
|
|
8
8
|
* operating system, with two of the three getting it subtly wrong.
|
|
9
9
|
*
|
|
10
|
-
* Design and audit history: `devlog/
|
|
10
|
+
* Design and audit history: `devlog/_fin/260913_cross_platform_desktop_app_restart/`.
|
|
11
11
|
*/
|
|
12
12
|
import { sep } from "node:path";
|
|
13
13
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* AUMID. The shape is DesktopAppAdapter so the ladder, not this file, owns
|
|
8
8
|
* PID-reuse re-verification and the fail-closed sequencing.
|
|
9
9
|
*
|
|
10
|
-
* Measured (devlog/
|
|
10
|
+
* Measured (devlog/_fin/260913_cross_platform_desktop_app_restart/001_platform_topology.md §3):
|
|
11
11
|
* OpenAI.Codex MSIX, ChatGPT.exe, InstallLocation under WindowsApps.
|
|
12
12
|
*/
|
|
13
13
|
import { execFileSync } from "node:child_process";
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* never reported as "nothing to do". A stale picker is a much smaller problem than a
|
|
30
30
|
* wrongly killed process.
|
|
31
31
|
*
|
|
32
|
-
* Design and audit history: devlog/
|
|
32
|
+
* Design and audit history: devlog/_fin/260913_cross_platform_desktop_app_restart/.
|
|
33
33
|
*/
|
|
34
34
|
import {
|
|
35
35
|
acquireDesktopRestartLock,
|
|
@@ -510,8 +510,9 @@ async function resolveMainAccountToken(
|
|
|
510
510
|
}
|
|
511
511
|
const refresh = dependencies.refreshToken ?? refreshNativeMainGrant;
|
|
512
512
|
let refreshed: OAuthCredentials;
|
|
513
|
+
if (signal.aborted) throw signal.reason;
|
|
513
514
|
try {
|
|
514
|
-
refreshed = await refresh(locked.refreshToken, { signal });
|
|
515
|
+
refreshed = await refresh(locked.refreshToken, { signal: refreshTimeout });
|
|
515
516
|
} catch (cause) {
|
|
516
517
|
// A refusal already classified at the endpoint keeps that verdict. An injected refresh
|
|
517
518
|
// threw an ordinary Error and has only its message, so prose still decides there --
|
|
@@ -535,16 +536,14 @@ async function resolveMainAccountToken(
|
|
|
535
536
|
}
|
|
536
537
|
throw new MainAccountTokenRefreshError(reason, { cause });
|
|
537
538
|
}
|
|
538
|
-
//
|
|
539
|
-
//
|
|
540
|
-
//
|
|
541
|
-
// of a request that no longer exists -- the same fence the reauth twin applies
|
|
542
|
-
// before its own commit above.
|
|
543
|
-
if (dependencies.signal?.aborted) throw dependencies.signal.reason;
|
|
539
|
+
// Once the exchange starts, only its own timeout may abort it: a successful
|
|
540
|
+
// rotating grant retires the old refresh token. Save the only live grant before
|
|
541
|
+
// honoring caller cancellation, while retaining the external-writer guards.
|
|
544
542
|
const result = persistRefreshedMainAuthJson(locked, refreshed);
|
|
545
543
|
// A permitted in-flight success speaks only for its own profile and grant.
|
|
546
544
|
clearMainRefreshGrantRejection(mainRefreshGrantKey(locked)!);
|
|
547
545
|
if (dependencies.preserveReauth !== true) clearAccountNeedsReauth(MAIN_CODEX_ACCOUNT_ID);
|
|
546
|
+
if (dependencies.signal?.aborted) throw dependencies.signal.reason;
|
|
548
547
|
return result;
|
|
549
548
|
}),
|
|
550
549
|
{ waitMs: 30_000, signal },
|