@bitkyc08/opencodex 2.47.0 → 2.48.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/index-B5r7LNHN.js +115 -0
- package/gui/dist/assets/index-D5SiRo8X.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/openai-responses.ts +16 -2
- package/src/adapters/opencode-go-additional-tools.ts +35 -0
- package/src/adapters/routed-agent-messages.ts +46 -0
- package/src/claude/inbound-content-options.ts +6 -0
- package/src/codex/auth-api.ts +211 -67
- package/src/codex/features.ts +3 -1
- package/src/codex/multi-agent-mode-policy.ts +24 -0
- package/src/codex/reset-credit-auto-redeem.ts +54 -13
- package/src/codex/routing.ts +85 -4
- package/src/codex/shim.ts +10 -1
- package/src/config/atomic-write.ts +2 -3
- package/src/config/initialize.ts +11 -6
- package/src/generated/compatibility-version.json +35 -23
- package/src/lib/state-store-registrations.ts +2 -0
- package/src/providers/default-aliases.ts +4 -1
- package/src/providers/registry.ts +6 -1
- package/src/router.ts +5 -2
- package/src/server/claude-messages.ts +21 -4
- package/src/server/live.ts +8 -19
- package/src/server/management/agent-settings-routes.ts +3 -0
- package/src/server/responses/collaboration.ts +23 -12
- package/src/server/responses/combo-session-recall.ts +89 -0
- package/src/server/responses/compact.ts +19 -1
- package/src/server/responses/core.ts +189 -51
- package/src/types/config.ts +3 -3
- package/gui/dist/assets/index-B8ZZ0ivI.css +0 -1
- package/gui/dist/assets/index-BoXZzNaV.js +0 -115
- package/src/adapters/opencode-go.ts +0 -35
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { ConfigMutationLockError, withConfigMutationLockSync } from "../config";
|
|
4
5
|
import { atomicWriteFile } from "../config/atomic-write";
|
|
5
6
|
import { getConfigDir } from "../config/paths";
|
|
6
7
|
import { registerOptionalShutdownHook } from "../lib/optional-shutdown-hooks";
|
|
@@ -91,9 +92,9 @@ function readJournal(path: string): Journal {
|
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
function writeJournal(path: string, journal: Journal): void {
|
|
95
|
+
function writeJournal(path: string, journal: Journal, now: number): void {
|
|
95
96
|
// Keep only entries whose credit could still matter: settled ones older than a week are noise.
|
|
96
|
-
const cutoff =
|
|
97
|
+
const cutoff = now - 7 * 24 * 60 * 60_000;
|
|
97
98
|
journal.entries = journal.entries.filter(e => e.state !== "settled" || e.updatedAt > cutoff);
|
|
98
99
|
atomicWriteFile(path, JSON.stringify(journal, null, 2));
|
|
99
100
|
}
|
|
@@ -112,6 +113,7 @@ export interface AutoRedeemDeps {
|
|
|
112
113
|
now?: () => number;
|
|
113
114
|
setTimer?: (fn: () => void, ms: number) => unknown;
|
|
114
115
|
clearTimer?: (handle: unknown) => void;
|
|
116
|
+
/** Callers sharing an overridden journal must also share the OPENCODEX_HOME mutation coordinator. */
|
|
115
117
|
journalFile?: string;
|
|
116
118
|
log?: (line: string) => void;
|
|
117
119
|
/** Upper bound on one sleep so a laptop sleep or clock jump re-checks rather than trusting a stale plan. */
|
|
@@ -155,15 +157,38 @@ export function createResetCreditAutoRedeemer(deps: AutoRedeemDeps): ResetCredit
|
|
|
155
157
|
handle = setTimer(() => { handle = null; void tick(); }, Math.max(0, Math.min(ms, maxSleepMs)));
|
|
156
158
|
};
|
|
157
159
|
|
|
160
|
+
const retryJournal = (error: unknown): void => {
|
|
161
|
+
const cause = error instanceof ConfigMutationLockError ? error.cause : error;
|
|
162
|
+
const code = cause && typeof cause === "object" && "code" in cause ? String(cause.code) : "";
|
|
163
|
+
const busy = code === "SQLITE_BUSY" || code === "SQLITE_LOCKED"
|
|
164
|
+
|| (cause instanceof Error && /database (?:is|table is) locked/i.test(cause.message));
|
|
165
|
+
schedule(busy ? 1_000 : idleRecheckMs);
|
|
166
|
+
};
|
|
167
|
+
|
|
158
168
|
const dispatch = async (plan: AutoRedeemPlan): Promise<AutoRedeemOutcome> => {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
169
|
+
// Reserve under the shared config-mutation lock. `inFlight` only serializes ticks inside
|
|
170
|
+
// ONE process; two servers on the same config dir would otherwise both read a journal with
|
|
171
|
+
// no entry, each mint a different `redeem_request_id`, and spend two credits for one plan.
|
|
172
|
+
let entry: JournalEntry;
|
|
173
|
+
try {
|
|
174
|
+
entry = withConfigMutationLockSync(() => {
|
|
175
|
+
const journal = readJournal(path);
|
|
176
|
+
const existing = journal.entries.find(e => e.accountKey === accountKey && e.grantedAt === plan.grantedAt && e.expiresAt === plan.expiresAt);
|
|
177
|
+
if (existing) return existing;
|
|
178
|
+
const created: JournalEntry = { accountKey, grantedAt: plan.grantedAt, expiresAt: plan.expiresAt, redeemRequestId: randomUUID(), state: "dispatched", updatedAt: now() };
|
|
179
|
+
journal.entries.push(created);
|
|
180
|
+
// Journal BEFORE the network call: a crash after this line replays the same request id.
|
|
181
|
+
writeJournal(path, journal, created.updatedAt);
|
|
182
|
+
return created;
|
|
183
|
+
});
|
|
184
|
+
} catch (error) {
|
|
185
|
+
// Only contention gets a short retry; persistent storage failures must not spin.
|
|
186
|
+
retryJournal(error);
|
|
187
|
+
return { kind: "error", message: error instanceof Error ? error.message : "journal reservation failed" };
|
|
188
|
+
}
|
|
189
|
+
if (entry.state === "settled") {
|
|
190
|
+
schedule(idleRecheckMs);
|
|
191
|
+
return { kind: "skipped", reason: "credit-gone" };
|
|
167
192
|
}
|
|
168
193
|
log(`[opencodex] reset-credit auto-redeem: dispatching for account ${accountKey} (credit expires ${plan.expiresAt})`);
|
|
169
194
|
let result: { code: string };
|
|
@@ -174,9 +199,25 @@ export function createResetCreditAutoRedeemer(deps: AutoRedeemDeps): ResetCredit
|
|
|
174
199
|
schedule(60_000);
|
|
175
200
|
return { kind: "ambiguous", redeemRequestId: entry.redeemRequestId };
|
|
176
201
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
202
|
+
// Re-read under the lock: a peer may have appended its own entries since the reservation,
|
|
203
|
+
// and writing a stale in-memory journal would drop them.
|
|
204
|
+
try {
|
|
205
|
+
withConfigMutationLockSync(() => {
|
|
206
|
+
const journal = readJournal(path);
|
|
207
|
+
const current = journal.entries.find(e => e.accountKey === accountKey && e.grantedAt === plan.grantedAt && e.expiresAt === plan.expiresAt);
|
|
208
|
+
if (!current || current.redeemRequestId !== entry.redeemRequestId) {
|
|
209
|
+
throw new Error("auto-redeem journal reservation changed before settlement");
|
|
210
|
+
}
|
|
211
|
+
current.state = "settled";
|
|
212
|
+
current.updatedAt = now();
|
|
213
|
+
writeJournal(path, journal, current.updatedAt);
|
|
214
|
+
});
|
|
215
|
+
} catch (error) {
|
|
216
|
+
// Upstream answered, but settlement could not be committed. Preserve any reservation;
|
|
217
|
+
// a later dispatch must reuse its request id. A vanished credit may never dispatch again.
|
|
218
|
+
retryJournal(error);
|
|
219
|
+
return { kind: "error", message: error instanceof Error ? error.message : "journal settlement failed" };
|
|
220
|
+
}
|
|
180
221
|
log(`[opencodex] reset-credit auto-redeem: upstream answered ${result.code} for account ${accountKey}`);
|
|
181
222
|
schedule(idleRecheckMs);
|
|
182
223
|
return { kind: "dispatched", code: result.code, redeemRequestId: entry.redeemRequestId };
|
package/src/codex/routing.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { saveConfigPreservingClaudeCode } from "../config";
|
|
3
|
-
import { isCodexAccountGenerationLive, readCodexAccountRecord } from "./account-store";
|
|
3
|
+
import { isCodexAccountGenerationLive, readCodexAccountRecord, type CodexRefreshProvenance } from "./account-store";
|
|
4
4
|
import { codexAccountLogLabel } from "./account-label";
|
|
5
5
|
import { NATIVE_RESERVE_MODEL } from "./catalog/native-models";
|
|
6
6
|
import { isCodexAccountPaused } from "./account-pause";
|
|
@@ -642,6 +642,80 @@ export function claimDueCodexQuotaRecoveryProbes(
|
|
|
642
642
|
});
|
|
643
643
|
}
|
|
644
644
|
|
|
645
|
+
type CooldownRecoveryLease = Pick<CodexQuotaRecoveryProbeClaim,
|
|
646
|
+
"accountId" | "scope" | "leaseId" | "cooldownGeneration">;
|
|
647
|
+
|
|
648
|
+
export type ManualResetCooldownClaim =
|
|
649
|
+
| { kind: "pool"; probe: CodexQuotaRecoveryProbeClaim }
|
|
650
|
+
| { kind: "main"; probe: CooldownRecoveryLease };
|
|
651
|
+
|
|
652
|
+
function manualResetAccountEligible(config: OcxConfig, accountId: string): boolean {
|
|
653
|
+
return !isCodexAccountPaused(config, accountId) && !isAccountNeedsReauth(accountId)
|
|
654
|
+
&& (accountId === MAIN_CODEX_ACCOUNT_ID
|
|
655
|
+
|| (config.codexAccounts ?? []).some(account => account.id === accountId && isSelectableCodexPoolAccount(account)));
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/** Explicit reset bypasses probe pacing, never another owner's lease or quota scope. */
|
|
659
|
+
export function claimManualResetCooldowns(
|
|
660
|
+
config: OcxConfig,
|
|
661
|
+
accountId: string,
|
|
662
|
+
now = Date.now(),
|
|
663
|
+
expectedPoolGeneration?: number,
|
|
664
|
+
): ManualResetCooldownClaim[] {
|
|
665
|
+
if (!manualResetAccountEligible(config, accountId)) return [];
|
|
666
|
+
const record = accountId === MAIN_CODEX_ACCOUNT_ID ? undefined : readCodexAccountRecord(accountId);
|
|
667
|
+
if (accountId !== MAIN_CODEX_ACCOUNT_ID && (!record?.credential || record.deletedAt != null)) return [];
|
|
668
|
+
if (record && expectedPoolGeneration !== undefined && record.generation !== expectedPoolGeneration) return [];
|
|
669
|
+
const claims: ManualResetCooldownClaim[] = [];
|
|
670
|
+
for (const scope of [undefined, "shared"] as const) {
|
|
671
|
+
const health = scope ? scopedHealthFor(accountId, scope) : upstreamHealth.get(accountId);
|
|
672
|
+
if (!health || health.cooldownSource !== "reset-derived" || health.probeLeaseId !== undefined
|
|
673
|
+
|| !Number.isFinite(health.cooldownUntil) || !(health.cooldownUntil! > now)) continue;
|
|
674
|
+
const leaseId = randomUUID();
|
|
675
|
+
const cooldownGeneration = health.cooldownGeneration ?? 0;
|
|
676
|
+
const next = { ...health, probeLeaseId: leaseId, probeLeaseGeneration: cooldownGeneration, lastProbeAt: now };
|
|
677
|
+
if (scope) setScopedHealth(accountId, scope, next);
|
|
678
|
+
else upstreamHealth.set(accountId, next);
|
|
679
|
+
const probe = { accountId, scope, leaseId, cooldownGeneration };
|
|
680
|
+
claims.push(record ? { kind: "pool", probe: {
|
|
681
|
+
...probe, credentialGeneration: record.generation, credentialReplacedAt: record.replacedAt,
|
|
682
|
+
} } : { kind: "main", probe });
|
|
683
|
+
}
|
|
684
|
+
return claims;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
export type ManualResetRefreshLineage = Readonly<{
|
|
688
|
+
fromGeneration: number;
|
|
689
|
+
toGeneration: number;
|
|
690
|
+
provenance: CodexRefreshProvenance;
|
|
691
|
+
}>;
|
|
692
|
+
|
|
693
|
+
type ManualResetQuotaProof = CodexQuotaRecoveryProbeProof & {
|
|
694
|
+
refreshLineage?: ManualResetRefreshLineage;
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
/** Main proof is checked by the already-owned auth operation, never by a Pool record. */
|
|
698
|
+
export function settleManualResetCooldown(
|
|
699
|
+
config: OcxConfig,
|
|
700
|
+
claim: ManualResetCooldownClaim,
|
|
701
|
+
recovered: boolean,
|
|
702
|
+
proof: ManualResetQuotaProof = {},
|
|
703
|
+
now = Date.now(),
|
|
704
|
+
): boolean {
|
|
705
|
+
if (!recovered) return settleCooldownRecoveryLease(claim.probe, false, now);
|
|
706
|
+
const eligible = manualResetAccountEligible(config, claim.probe.accountId);
|
|
707
|
+
if (claim.kind === "main") return settleCooldownRecoveryLease(claim.probe, eligible, now);
|
|
708
|
+
const lineage = proof.refreshLineage;
|
|
709
|
+
// Equal wall-clock replacement stamps do not establish ancestry. Manual +1
|
|
710
|
+
// recovery additionally needs the actual forced-refresh result for this edge.
|
|
711
|
+
const ownedGeneration = proof.credentialGeneration === claim.probe.credentialGeneration
|
|
712
|
+
|| (proof.credentialGeneration === claim.probe.credentialGeneration + 1
|
|
713
|
+
&& lineage?.fromGeneration === claim.probe.credentialGeneration
|
|
714
|
+
&& lineage.toGeneration === proof.credentialGeneration
|
|
715
|
+
&& (lineage.provenance === "self-refresh" || lineage.provenance === "joined-lineage"));
|
|
716
|
+
return settleCodexQuotaRecoveryProbe(claim.probe, eligible && ownedGeneration, proof, now);
|
|
717
|
+
}
|
|
718
|
+
|
|
645
719
|
/** Settle one background recovery claim without mutating account-wide outcome state. */
|
|
646
720
|
export function settleCodexQuotaRecoveryProbe(
|
|
647
721
|
claim: CodexQuotaRecoveryProbeClaim,
|
|
@@ -665,9 +739,16 @@ export function settleCodexQuotaRecoveryProbe(
|
|
|
665
739
|
: proofGeneration === claim.credentialGeneration + 1
|
|
666
740
|
&& currentRecord?.replacedAt === claim.credentialReplacedAt
|
|
667
741
|
&& isCodexAccountGenerationLive(claim.accountId, proofGeneration));
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
742
|
+
return settleCooldownRecoveryLease(claim, recovered && generationFenced, now);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function settleCooldownRecoveryLease(claim: CooldownRecoveryLease, recovered: boolean, now: number): boolean {
|
|
746
|
+
const health = claim.scope ? scopedHealthFor(claim.accountId, claim.scope) : upstreamHealth.get(claim.accountId);
|
|
747
|
+
if (!health || health.probeLeaseId !== claim.leaseId) return false;
|
|
748
|
+
const fenced = (claim.scope === undefined || claim.scope === "shared")
|
|
749
|
+
&& health.cooldownSource === "reset-derived"
|
|
750
|
+
&& (health.cooldownGeneration ?? 0) === claim.cooldownGeneration
|
|
751
|
+
&& (health.probeLeaseGeneration ?? 0) === claim.cooldownGeneration;
|
|
671
752
|
if (!recovered || !fenced) {
|
|
672
753
|
const released = withProbeLeaseReleased(health, now);
|
|
673
754
|
if (claim.scope) setScopedHealth(claim.accountId, claim.scope, released);
|
package/src/codex/shim.ts
CHANGED
|
@@ -1069,6 +1069,7 @@ export function buildWindowsCodexShim(realCodexPath: string, bunPath: string, cl
|
|
|
1069
1069
|
const valueOptionChecks = CODEX_GLOBAL_OPTIONS_WITH_VALUE.map(option => `if /I "%~1"=="${option}" goto skip_option_value`).join("\r\n");
|
|
1070
1070
|
return `@echo off\r
|
|
1071
1071
|
rem ${SHIM_MARKER}\r
|
|
1072
|
+
setlocal\r
|
|
1072
1073
|
${windowsBatchSet("OCX_REAL_CODEX", realCodexPath)}\r
|
|
1073
1074
|
${windowsBatchSet("OCX_BUN", bunPath)}\r
|
|
1074
1075
|
${windowsBatchSet("OCX_CLI", cliPath)}\r
|
|
@@ -1115,6 +1116,9 @@ export function buildWindowsPowerShellCodexShim(realCodexPath: string, bunPath:
|
|
|
1115
1116
|
const tokenFile = serviceApiTokenFilePath();
|
|
1116
1117
|
return `#!/usr/bin/env pwsh
|
|
1117
1118
|
# ${SHIM_MARKER}
|
|
1119
|
+
$hadApiAuthToken = Test-Path Env:\\OPENCODEX_API_AUTH_TOKEN
|
|
1120
|
+
$priorApiAuthToken = $env:OPENCODEX_API_AUTH_TOKEN
|
|
1121
|
+
try {
|
|
1118
1122
|
if (-not $env:OPENCODEX_API_AUTH_TOKEN -and (Test-Path -LiteralPath ${psString(tokenFile)})) {
|
|
1119
1123
|
$env:OPENCODEX_API_AUTH_TOKEN = (Get-Content -Raw -LiteralPath ${psString(tokenFile)}).Trim()
|
|
1120
1124
|
}
|
|
@@ -1147,7 +1151,12 @@ if (-not $skipEnsure) {
|
|
|
1147
1151
|
}
|
|
1148
1152
|
}
|
|
1149
1153
|
& ${psString(realCodexPath)} @args
|
|
1150
|
-
|
|
1154
|
+
$codexExitCode = $LASTEXITCODE
|
|
1155
|
+
} finally {
|
|
1156
|
+
if ($hadApiAuthToken) { $env:OPENCODEX_API_AUTH_TOKEN = $priorApiAuthToken }
|
|
1157
|
+
else { Remove-Item Env:\\OPENCODEX_API_AUTH_TOKEN -ErrorAction SilentlyContinue }
|
|
1158
|
+
}
|
|
1159
|
+
exit $codexExitCode
|
|
1151
1160
|
`;
|
|
1152
1161
|
}
|
|
1153
1162
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
chmodSync,
|
|
3
3
|
closeSync,
|
|
4
|
-
constants,
|
|
5
4
|
fchmodSync,
|
|
6
5
|
fstatSync,
|
|
7
6
|
lstatSync,
|
|
@@ -121,7 +120,7 @@ function writePrivateTempFile(
|
|
|
121
120
|
timeoutMemoKey: string,
|
|
122
121
|
onCreated: () => void,
|
|
123
122
|
): void {
|
|
124
|
-
const descriptor = openSync(path,
|
|
123
|
+
const descriptor = openSync(path, "wx", 0o600);
|
|
125
124
|
onCreated();
|
|
126
125
|
try {
|
|
127
126
|
if (process.platform === "win32") {
|
|
@@ -142,7 +141,7 @@ async function writePrivateTempFileAsync(
|
|
|
142
141
|
timeoutMemoKey: string,
|
|
143
142
|
onCreated: () => void,
|
|
144
143
|
): Promise<void> {
|
|
145
|
-
const descriptor = openSync(path,
|
|
144
|
+
const descriptor = openSync(path, "wx", 0o600);
|
|
146
145
|
onCreated();
|
|
147
146
|
try {
|
|
148
147
|
if (process.platform === "win32") {
|
package/src/config/initialize.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
closeSync,
|
|
2
|
+
closeSync, fchmodSync, fstatSync, linkSync, lstatSync,
|
|
3
3
|
openSync, unlinkSync, writeFileSync,
|
|
4
4
|
} from "node:fs";
|
|
5
5
|
import { dirname } from "node:path";
|
|
@@ -15,10 +15,12 @@ export class InitialConfigPublicationError extends Error {
|
|
|
15
15
|
readonly publication: PublicationState,
|
|
16
16
|
readonly residualTemp: boolean,
|
|
17
17
|
readonly hardLinkUnavailable: boolean,
|
|
18
|
-
options?: ErrorOptions,
|
|
18
|
+
options?: ErrorOptions & { hardeningFailed?: boolean },
|
|
19
19
|
) {
|
|
20
|
-
super(
|
|
21
|
-
? "Initial config
|
|
20
|
+
super(options?.hardeningFailed
|
|
21
|
+
? "Initial config permissions could not be secured. Choose an OPENCODEX_HOME location that supports private file permissions (NTFS ACLs on Windows), then rerun `ocx init`."
|
|
22
|
+
: hardLinkUnavailable
|
|
23
|
+
? "Initial config requires hard-link publication; the filesystem or its permissions denied it. Inspect the config directory before retrying. Choose an OPENCODEX_HOME location that supports hard links and private file permissions, then rerun `ocx init`."
|
|
22
24
|
: "Initial config publication did not finish.", options);
|
|
23
25
|
this.name = "InitialConfigPublicationError";
|
|
24
26
|
}
|
|
@@ -90,10 +92,13 @@ export function publishInitialConfigNoReplace(
|
|
|
90
92
|
let failure: unknown;
|
|
91
93
|
let failed = false;
|
|
92
94
|
let hardLinkUnavailable = false;
|
|
95
|
+
let hardeningFailed = false;
|
|
93
96
|
let residualTemp = false;
|
|
94
97
|
try {
|
|
95
|
-
fd = openSync(temp,
|
|
98
|
+
fd = openSync(temp, "wx", 0o600);
|
|
99
|
+
hardeningFailed = true;
|
|
96
100
|
(io.harden ?? hardenInitialConfig)(fd, temp, target);
|
|
101
|
+
hardeningFailed = false;
|
|
97
102
|
verifyPrivateTemp(fd, temp);
|
|
98
103
|
(io.write ?? ((descriptor: number, value: string) => writeFileSync(descriptor, value, { encoding: "utf8" })))(fd, bytes);
|
|
99
104
|
verifyPrivateTemp(fd, temp);
|
|
@@ -126,7 +131,7 @@ export function publishInitialConfigNoReplace(
|
|
|
126
131
|
}
|
|
127
132
|
}
|
|
128
133
|
if (failed || residualTemp) {
|
|
129
|
-
throw new InitialConfigPublicationError(publication, residualTemp, hardLinkUnavailable, { cause: failure });
|
|
134
|
+
throw new InitialConfigPublicationError(publication, residualTemp, hardLinkUnavailable, { cause: failure, hardeningFailed });
|
|
130
135
|
}
|
|
131
136
|
return !collided;
|
|
132
137
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"path": "package.json",
|
|
13
|
-
"sha256": "
|
|
13
|
+
"sha256": "8c97c97d74c4f42c86a4165fdcaeb5d3bce90b7d0bc2ed085593702f63cea20d"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"path": "scripts/model-metadata.source.json",
|
|
@@ -366,11 +366,11 @@
|
|
|
366
366
|
},
|
|
367
367
|
{
|
|
368
368
|
"path": "src/adapters/openai-responses.ts",
|
|
369
|
-
"sha256": "
|
|
369
|
+
"sha256": "c1ede0d8538bbefdd60e85eac045992fc740971fa93b3529cc5417de2b091e40"
|
|
370
370
|
},
|
|
371
371
|
{
|
|
372
|
-
"path": "src/adapters/opencode-go.ts",
|
|
373
|
-
"sha256": "
|
|
372
|
+
"path": "src/adapters/opencode-go-additional-tools.ts",
|
|
373
|
+
"sha256": "42090cb27e20503d3d6ad0680b1a45b918b19d878c2e6db6ed031ca57f17c1ee"
|
|
374
374
|
},
|
|
375
375
|
{
|
|
376
376
|
"path": "src/adapters/registry.ts",
|
|
@@ -384,6 +384,10 @@
|
|
|
384
384
|
"path": "src/adapters/responses-tool-schema.ts",
|
|
385
385
|
"sha256": "affeb179617d9abfb8cd18d47b132ff02d05c4438e5395b90103c7b03c184f55"
|
|
386
386
|
},
|
|
387
|
+
{
|
|
388
|
+
"path": "src/adapters/routed-agent-messages.ts",
|
|
389
|
+
"sha256": "3617b10e10db2ebfd573a479f7c345c254c49f3b139cc2d0290011c007c05c8b"
|
|
390
|
+
},
|
|
387
391
|
{
|
|
388
392
|
"path": "src/adapters/run-turn-queue.ts",
|
|
389
393
|
"sha256": "31b12f11c9c35f3e499c418bf943762f299755d8a68d7bba9fa9371fa5684a43"
|
|
@@ -506,7 +510,7 @@
|
|
|
506
510
|
},
|
|
507
511
|
{
|
|
508
512
|
"path": "src/claude/inbound-content-options.ts",
|
|
509
|
-
"sha256": "
|
|
513
|
+
"sha256": "5bd23dd382a52ed889ad5b42535f86cd214bc35617fdbf80c8daa8f34a4a763d"
|
|
510
514
|
},
|
|
511
515
|
{
|
|
512
516
|
"path": "src/claude/inbound-debug.ts",
|
|
@@ -946,7 +950,7 @@
|
|
|
946
950
|
},
|
|
947
951
|
{
|
|
948
952
|
"path": "src/codex/auth-api.ts",
|
|
949
|
-
"sha256": "
|
|
953
|
+
"sha256": "10ea42a877146d9d4adca8590c0207affa9a12c4dc429d14441cd256cd88e796"
|
|
950
954
|
},
|
|
951
955
|
{
|
|
952
956
|
"path": "src/codex/auth-collision.ts",
|
|
@@ -1070,7 +1074,7 @@
|
|
|
1070
1074
|
},
|
|
1071
1075
|
{
|
|
1072
1076
|
"path": "src/codex/features.ts",
|
|
1073
|
-
"sha256": "
|
|
1077
|
+
"sha256": "2d2e3ff6e5c0cddd31188fbe76f38c5fc50bce83b0800ff3a44fa36a2362aa7e"
|
|
1074
1078
|
},
|
|
1075
1079
|
{
|
|
1076
1080
|
"path": "src/codex/forward-transport-headers.ts",
|
|
@@ -1208,6 +1212,10 @@
|
|
|
1208
1212
|
"path": "src/codex/model-entitlements.ts",
|
|
1209
1213
|
"sha256": "a21db9fc98bfad86aaaf6aff404c2ee1e3990c1b1598c7d9ab7af91af1c2b601"
|
|
1210
1214
|
},
|
|
1215
|
+
{
|
|
1216
|
+
"path": "src/codex/multi-agent-mode-policy.ts",
|
|
1217
|
+
"sha256": "8559eddf8c697993997778665daf21060ae1971c289fbb70bc971e5bbef6084a"
|
|
1218
|
+
},
|
|
1211
1219
|
{
|
|
1212
1220
|
"path": "src/codex/native-main-admission.ts",
|
|
1213
1221
|
"sha256": "1f4956299bf243980bb06c7ddcfdadf71497cd874d3c1c49cb28465f27f6c85f"
|
|
@@ -1366,7 +1374,7 @@
|
|
|
1366
1374
|
},
|
|
1367
1375
|
{
|
|
1368
1376
|
"path": "src/codex/reset-credit-auto-redeem.ts",
|
|
1369
|
-
"sha256": "
|
|
1377
|
+
"sha256": "7d5a443e6fa6b4c36b8daa59d6d2ca8131ac7a086b3e85bc51d2b337ff43047d"
|
|
1370
1378
|
},
|
|
1371
1379
|
{
|
|
1372
1380
|
"path": "src/codex/reset-credit-operation-ledger.ts",
|
|
@@ -1378,7 +1386,7 @@
|
|
|
1378
1386
|
},
|
|
1379
1387
|
{
|
|
1380
1388
|
"path": "src/codex/routing.ts",
|
|
1381
|
-
"sha256": "
|
|
1389
|
+
"sha256": "31009533a9eeb8aab35f77bd626de9c01b15a74d5e06ee98282c5b07572cf2b9"
|
|
1382
1390
|
},
|
|
1383
1391
|
{
|
|
1384
1392
|
"path": "src/codex/runtime.ts",
|
|
@@ -1386,7 +1394,7 @@
|
|
|
1386
1394
|
},
|
|
1387
1395
|
{
|
|
1388
1396
|
"path": "src/codex/shim.ts",
|
|
1389
|
-
"sha256": "
|
|
1397
|
+
"sha256": "08c28d3daafaeee903ddab78ff396d6a6bf46118893640ae316ece111e8a3e38"
|
|
1390
1398
|
},
|
|
1391
1399
|
{
|
|
1392
1400
|
"path": "src/codex/subagent-defaults.ts",
|
|
@@ -1470,11 +1478,11 @@
|
|
|
1470
1478
|
},
|
|
1471
1479
|
{
|
|
1472
1480
|
"path": "src/config/atomic-write.ts",
|
|
1473
|
-
"sha256": "
|
|
1481
|
+
"sha256": "e4e0e2c3bb3adc7a2e02c7411718d4faad8417870c27143742ba72f9d6f6ab3a"
|
|
1474
1482
|
},
|
|
1475
1483
|
{
|
|
1476
1484
|
"path": "src/config/initialize.ts",
|
|
1477
|
-
"sha256": "
|
|
1485
|
+
"sha256": "eddee7d5cbef36444f4d73d193b5e16bbe623d5fbf408ef4a47b40bc9daefa3c"
|
|
1478
1486
|
},
|
|
1479
1487
|
{
|
|
1480
1488
|
"path": "src/config/paths.ts",
|
|
@@ -2378,7 +2386,7 @@
|
|
|
2378
2386
|
},
|
|
2379
2387
|
{
|
|
2380
2388
|
"path": "src/lib/state-store-registrations.ts",
|
|
2381
|
-
"sha256": "
|
|
2389
|
+
"sha256": "7aa40b75b379f4b8f4c450bf3a0eeea2d0d7619c919be09df3aba73b58f9f57d"
|
|
2382
2390
|
},
|
|
2383
2391
|
{
|
|
2384
2392
|
"path": "src/lib/state-store-sweeper.ts",
|
|
@@ -2662,7 +2670,7 @@
|
|
|
2662
2670
|
},
|
|
2663
2671
|
{
|
|
2664
2672
|
"path": "src/providers/default-aliases.ts",
|
|
2665
|
-
"sha256": "
|
|
2673
|
+
"sha256": "190c2b85e92ff3d686efefa4c7dac0db75a4897b670e5ee5ff3d3e8055d4eb35"
|
|
2666
2674
|
},
|
|
2667
2675
|
{
|
|
2668
2676
|
"path": "src/providers/derive.ts",
|
|
@@ -2802,7 +2810,7 @@
|
|
|
2802
2810
|
},
|
|
2803
2811
|
{
|
|
2804
2812
|
"path": "src/providers/registry.ts",
|
|
2805
|
-
"sha256": "
|
|
2813
|
+
"sha256": "e636da79c8dbabe1702fd00e9b47199bc00dde5e871b931a6e12598e970f7c20"
|
|
2806
2814
|
},
|
|
2807
2815
|
{
|
|
2808
2816
|
"path": "src/providers/request-pacing.ts",
|
|
@@ -2978,7 +2986,7 @@
|
|
|
2978
2986
|
},
|
|
2979
2987
|
{
|
|
2980
2988
|
"path": "src/router.ts",
|
|
2981
|
-
"sha256": "
|
|
2989
|
+
"sha256": "9fca9c7d3736dcb4cd07b58af87ff3b19abe14280fff8e6ff9b053841c2e51b4"
|
|
2982
2990
|
},
|
|
2983
2991
|
{
|
|
2984
2992
|
"path": "src/routing/analytics.ts",
|
|
@@ -3106,7 +3114,7 @@
|
|
|
3106
3114
|
},
|
|
3107
3115
|
{
|
|
3108
3116
|
"path": "src/server/claude-messages.ts",
|
|
3109
|
-
"sha256": "
|
|
3117
|
+
"sha256": "09c50f4454e990ccd5161ec5acdc49da2c75bf3bc04c7534209081756eed2b31"
|
|
3110
3118
|
},
|
|
3111
3119
|
{
|
|
3112
3120
|
"path": "src/server/direct-local-http.ts",
|
|
@@ -3162,7 +3170,7 @@
|
|
|
3162
3170
|
},
|
|
3163
3171
|
{
|
|
3164
3172
|
"path": "src/server/live.ts",
|
|
3165
|
-
"sha256": "
|
|
3173
|
+
"sha256": "5e68c7830a9a143f218bed2686dd794d0df7aa9a79cacc118c11b13690c630d4"
|
|
3166
3174
|
},
|
|
3167
3175
|
{
|
|
3168
3176
|
"path": "src/server/local-management-read-client.ts",
|
|
@@ -3186,7 +3194,7 @@
|
|
|
3186
3194
|
},
|
|
3187
3195
|
{
|
|
3188
3196
|
"path": "src/server/management/agent-settings-routes.ts",
|
|
3189
|
-
"sha256": "
|
|
3197
|
+
"sha256": "a771aa9da4a4b2b0efebd4367f10b792347881baec056bc375ee8821373f8df8"
|
|
3190
3198
|
},
|
|
3191
3199
|
{
|
|
3192
3200
|
"path": "src/server/management/api-access.ts",
|
|
@@ -3486,7 +3494,11 @@
|
|
|
3486
3494
|
},
|
|
3487
3495
|
{
|
|
3488
3496
|
"path": "src/server/responses/collaboration.ts",
|
|
3489
|
-
"sha256": "
|
|
3497
|
+
"sha256": "5982f3f249f5e3e1da217029597c56e1d154a1226e816d884c29c31b28ae125e"
|
|
3498
|
+
},
|
|
3499
|
+
{
|
|
3500
|
+
"path": "src/server/responses/combo-session-recall.ts",
|
|
3501
|
+
"sha256": "4401cbfadaeaeff14dd14c8afb25f0b8e61cadc1009393b35fe9bd25c6796f20"
|
|
3490
3502
|
},
|
|
3491
3503
|
{
|
|
3492
3504
|
"path": "src/server/responses/combo-stream-preflight.ts",
|
|
@@ -3494,7 +3506,7 @@
|
|
|
3494
3506
|
},
|
|
3495
3507
|
{
|
|
3496
3508
|
"path": "src/server/responses/compact.ts",
|
|
3497
|
-
"sha256": "
|
|
3509
|
+
"sha256": "205d9eaf2ce0f7c3d1af3bd58f7e3167479deec2c3a99e3743747f4fcb205e91"
|
|
3498
3510
|
},
|
|
3499
3511
|
{
|
|
3500
3512
|
"path": "src/server/responses/context-overflow.ts",
|
|
@@ -3502,7 +3514,7 @@
|
|
|
3502
3514
|
},
|
|
3503
3515
|
{
|
|
3504
3516
|
"path": "src/server/responses/core.ts",
|
|
3505
|
-
"sha256": "
|
|
3517
|
+
"sha256": "2d90436a0b09a6455c6bf666d49b5e4bbd1236fd38f4375a83b8af6e3dfe84d4"
|
|
3506
3518
|
},
|
|
3507
3519
|
{
|
|
3508
3520
|
"path": "src/server/responses/empty-completion-guard.ts",
|
|
@@ -3706,7 +3718,7 @@
|
|
|
3706
3718
|
},
|
|
3707
3719
|
{
|
|
3708
3720
|
"path": "src/types/config.ts",
|
|
3709
|
-
"sha256": "
|
|
3721
|
+
"sha256": "ec0942e86fcbb8a7ac4e8fb2bd44e6b180b35782e3d0dc15c85c6061d4998bfa"
|
|
3710
3722
|
},
|
|
3711
3723
|
{
|
|
3712
3724
|
"path": "src/types/provider.ts",
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from "../combos/failover";
|
|
22
22
|
import { reconcileComboWarningMemos } from "../combos/request";
|
|
23
23
|
import { reconcileComboRotationState } from "../combos/resolve";
|
|
24
|
+
import { reconcileComboRecall } from "../server/responses/combo-session-recall";
|
|
24
25
|
import { listLiveComboTargetKeys } from "../combos/types";
|
|
25
26
|
import {
|
|
26
27
|
listLiveConfigOwnershipRoots,
|
|
@@ -111,6 +112,7 @@ export const STATE_STORE_REGISTRATIONS = [
|
|
|
111
112
|
{ name: "model-cache-history", reconcileGeneration: reconcileModelCacheGeneration },
|
|
112
113
|
{ name: "pool-rotation", reconcileGeneration: reconcilePoolRotationState },
|
|
113
114
|
{ name: "combo-rotation", reconcileGeneration: reconcileComboRotationState },
|
|
115
|
+
{ name: "combo-session-recall", reconcileGeneration: reconcileComboRecall },
|
|
114
116
|
{ name: "guardian-backoff", reconcileGeneration: reconcileGuardianBackoff },
|
|
115
117
|
{ name: "codex-reauth", reconcileGeneration: reconcileCodexReauthState },
|
|
116
118
|
{ name: "oauth-reauth", reconcileGeneration: reconcileOAuthReauthState },
|
|
@@ -15,7 +15,10 @@ export function effectiveProviderAlias(
|
|
|
15
15
|
if (config?.providers) {
|
|
16
16
|
const lower = regAlias.toLowerCase();
|
|
17
17
|
const claimedByOther = Object.entries(config.providers).some(([name, p]) =>
|
|
18
|
-
name !== providerName &&
|
|
18
|
+
name !== providerName && (
|
|
19
|
+
name.toLowerCase() === lower
|
|
20
|
+
|| (typeof p.alias === "string" && p.alias.trim().toLowerCase() === lower)
|
|
21
|
+
)
|
|
19
22
|
);
|
|
20
23
|
if (claimedByOther) return undefined;
|
|
21
24
|
}
|
|
@@ -1539,8 +1539,10 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
1539
1539
|
modelDiscovery: {
|
|
1540
1540
|
// Resolves against effectiveBaseUrl (registry baseUrl .../v1) to the same
|
|
1541
1541
|
// canonical endpoint https://inference-api.nousresearch.com/v1/models.
|
|
1542
|
+
// Nous returns a mixed paid/free catalog whose JSON can exceed 256 KiB;
|
|
1543
|
+
// keep the provider-specific limit below the process-wide 4 MiB ceiling.
|
|
1542
1544
|
path: "models",
|
|
1543
|
-
maxResponseBytes:
|
|
1545
|
+
maxResponseBytes: 1_048_576,
|
|
1544
1546
|
maxModels: 512,
|
|
1545
1547
|
},
|
|
1546
1548
|
note: "Nous Research subscription gateway. OAuth device login with your own Portal account; mixed paid + :free models discovered live (fallback seed 2026-08-10: tencent/hy3:free, poolside/laguna-s-2.1:free, stepfun/step-3.7-flash:free, poolside/laguna-xs-2.1:free).",
|
|
@@ -1674,6 +1676,9 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
1674
1676
|
// Zen Go can close a Chat stream after a fully assembled function call without sending
|
|
1675
1677
|
// finish_reason or [DONE] (#2260). The adapter still rejects incomplete argument JSON.
|
|
1676
1678
|
openaiChatEofTolerance: true,
|
|
1679
|
+
// Go rejects reasoning.encrypted_content with previous_response_id (#3838).
|
|
1680
|
+
// Use explicit replay history and the existing stateless Responses policy.
|
|
1681
|
+
statelessResponses: true,
|
|
1677
1682
|
/* [Decision Log]
|
|
1678
1683
|
- 목적과 의도: Route the exact models OpenCode Go documents on the Responses endpoint — GPT 5.6 Luna, Grok 4.6, and Muse Spark Contributor (#2617).
|
|
1679
1684
|
- 기존 구현 및 제약 조건: The provider is mixed-wire but its provider-wide `openai-chat` adapter sent Luna to `/chat/completions`; explicit user `modelAdapters` entries must remain authoritative.
|
package/src/router.ts
CHANGED
|
@@ -703,13 +703,16 @@ function routeModelInternal(
|
|
|
703
703
|
throw new Error("provider alias '" + requestedProvider + "' is ambiguous: " + configuredMatches.map(([n]) => n).sort().join(", "));
|
|
704
704
|
} else {
|
|
705
705
|
// Pass 2: built-in registry aliases, only for providers that do NOT have an explicit alias override
|
|
706
|
-
// and whose registry alias has not been claimed by another configured provider
|
|
706
|
+
// and whose registry alias has not been claimed by another configured provider name or alias
|
|
707
707
|
const registryMatches = Object.entries(config.providers).filter(([name, provider]) => {
|
|
708
708
|
if (provider.alias !== undefined) return false;
|
|
709
709
|
const regAlias = PROVIDER_REGISTRY.find(e => e.id === name)?.alias;
|
|
710
710
|
if (!regAlias || regAlias.toLowerCase() !== requestedLower) return false;
|
|
711
711
|
const claimedByOther = Object.entries(config.providers).some(([otherName, p]) =>
|
|
712
|
-
otherName !== name &&
|
|
712
|
+
otherName !== name && (
|
|
713
|
+
otherName.toLowerCase() === requestedLower
|
|
714
|
+
|| (typeof p.alias === "string" && p.alias.trim().toLowerCase() === requestedLower)
|
|
715
|
+
)
|
|
713
716
|
);
|
|
714
717
|
return !claimedByOther;
|
|
715
718
|
});
|