@bitkyc08/opencodex 2.18.2 → 2.20.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-DF_UFrGS.css +1 -0
- package/gui/dist/assets/index-DSK3S5HY.js +76 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/client-fingerprint.ts +14 -10
- package/src/adapters/google-antigravity-wire.ts +4 -3
- package/src/adapters/google.ts +1 -1
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat.ts +367 -32
- package/src/adapters/registry.ts +144 -0
- package/src/chat/inbound.ts +37 -8
- package/src/cli/claude.ts +2 -1
- package/src/cli/combo.ts +3 -0
- package/src/cli/dispatch.ts +8 -0
- package/src/cli/export-command.ts +2 -2
- package/src/cli/help.ts +2 -0
- package/src/cli/index.ts +3 -2
- package/src/cli/lab.ts +135 -1
- package/src/cli/minimax.ts +491 -0
- package/src/cli/models-runtime.ts +22 -1
- package/src/cli/models.ts +67 -2
- package/src/cli/opencode.ts +2 -1
- package/src/cli/registry.ts +22 -2
- package/src/clients/config-export.ts +125 -7
- package/src/codex/app-server-processes.ts +57 -2
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/catalog/aggregation.ts +10 -1
- package/src/codex/catalog/effort.ts +15 -3
- package/src/codex/catalog/parsing.ts +3 -1
- package/src/codex/catalog/provider-fetch.ts +45 -5
- package/src/codex/catalog/sync.ts +74 -4
- package/src/codex/convergence.ts +2 -0
- package/src/codex/shim.ts +100 -5
- package/src/combos/index.ts +1 -0
- package/src/combos/request.ts +30 -0
- package/src/combos/types.ts +6 -0
- package/src/config.ts +52 -0
- package/src/generated/compatibility-version.json +239 -87
- package/src/images/loop.ts +11 -1
- package/src/integrations/registry.ts +7 -0
- package/src/lab/conformance/jcs.ts +42 -2
- package/src/lab/conformance/negative-controls.ts +6 -2
- package/src/lab/conformance/runner.ts +16 -5
- package/src/lab/fabric/observe.ts +49 -14
- package/src/lab/index.ts +16 -0
- package/src/lab/ledger/purge.ts +152 -83
- package/src/lab/ledger/store.ts +168 -54
- package/src/lab/observe/from-conformance.ts +8 -6
- package/src/lab/observe/from-live.ts +8 -2
- package/src/lab/paths.ts +23 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +219 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/errors.ts +27 -0
- package/src/lib/lab-activation.ts +109 -47
- package/src/lib/lab-live-pinned-sender.ts +16 -5
- package/src/lib/pinned-http.ts +70 -16
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/state-store-registrations.ts +2 -0
- package/src/lib/upstream-reachability.ts +4 -0
- package/src/lib/windows-elevation.ts +10 -1
- package/src/providers/derive.ts +24 -4
- package/src/providers/registry.ts +9 -3
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +143 -0
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/reasoning-effort.ts +27 -1
- package/src/responses/spill-store.ts +20 -1
- package/src/responses/state.ts +159 -3
- package/src/router.ts +23 -6
- package/src/routing/capability.ts +4 -2
- package/src/routing/compatibility/behavior.ts +5 -1
- package/src/server/adapter-resolve.ts +2 -32
- package/src/server/auth-cors.ts +8 -0
- package/src/server/chat-completions.ts +74 -34
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +371 -0
- package/src/server/effort-policy.ts +18 -0
- package/src/server/management/combo-routes.ts +16 -2
- package/src/server/management/config-routes.ts +6 -4
- package/src/server/management/context.ts +17 -0
- package/src/server/management/lab-routes.ts +181 -19
- package/src/server/management/model-routes.ts +76 -2
- package/src/server/management/model-rows.ts +8 -0
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +76 -4
- package/src/server/management/system-restart.ts +4 -2
- package/src/server/management/system-routes.ts +38 -0
- package/src/server/relay.ts +17 -3
- package/src/server/responses/compact.ts +4 -1
- package/src/server/responses/core.ts +278 -48
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/fetch-helpers.ts +35 -4
- package/src/server/responses/input-admission.ts +169 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/policy-fallback.ts +16 -2
- package/src/server/responses/terminal-guard.ts +1 -1
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses.ts +17 -2
- package/src/service-manager-probe.ts +2 -3
- package/src/types.ts +76 -6
- package/src/update/index.ts +6 -5
- package/src/update/job.ts +5 -6
- package/src/update/notify.ts +5 -3
- package/src/usage/log.ts +11 -1
- package/src/vision/index.ts +21 -4
- package/src/web-search/index.ts +2 -1
- package/gui/dist/assets/index-CXI1262_.js +0 -76
- package/gui/dist/assets/index-DUCH59lJ.css +0 -1
package/src/types.ts
CHANGED
|
@@ -32,11 +32,19 @@ export interface OcxParsedRequest {
|
|
|
32
32
|
stream: boolean;
|
|
33
33
|
options: OcxRequestOptions;
|
|
34
34
|
_rawBody?: unknown;
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Boundary between replayed history and this turn's newly appended input. Usually the
|
|
37
|
+
* items the proxy restored from local previous_response_id state; also set when the
|
|
38
|
+
* CLIENT already carried that history verbatim and the proxy skipped the prepend.
|
|
39
|
+
*/
|
|
36
40
|
_replayPrefixLen?: number;
|
|
37
41
|
/** Parsed-message index before the first conversational item in a continuation's current delta. */
|
|
38
42
|
_continuationConversationMessageIndex?: number;
|
|
39
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* True when the full history for a previous_response_id request is present in the input —
|
|
45
|
+
* whether the proxy expanded it or the client already sent it. Consumers read this as
|
|
46
|
+
* "this request is self-contained", never as "the proxy mutated it".
|
|
47
|
+
*/
|
|
40
48
|
_previousResponseInputExpanded?: boolean;
|
|
41
49
|
/** Provider-private stable Cursor conversation id resolved from the Responses previous_response_id chain. */
|
|
42
50
|
_cursorConversationId?: string;
|
|
@@ -579,6 +587,13 @@ export interface OcxCustomModel {
|
|
|
579
587
|
contextWindow?: number;
|
|
580
588
|
/** 입력 모달리티 (선택, 기본 ["text"]) */
|
|
581
589
|
inputModalities?: string[];
|
|
590
|
+
/**
|
|
591
|
+
* Reasoning ladder (Codex labels) this custom row explicitly advertises. An empty array
|
|
592
|
+
* hides the effort control; an omitted key leaves the provider-derived ladder in charge.
|
|
593
|
+
*/
|
|
594
|
+
reasoningEfforts?: string[];
|
|
595
|
+
/** Default effort label when `reasoningEfforts` is non-empty. */
|
|
596
|
+
defaultReasoningEffort?: string;
|
|
582
597
|
/** 추가 시각 (ISO 8601) */
|
|
583
598
|
addedAt?: string;
|
|
584
599
|
}
|
|
@@ -615,6 +630,8 @@ export interface OcxClientIntegrationsConfig {
|
|
|
615
630
|
|
|
616
631
|
export interface OcxConfig {
|
|
617
632
|
port: number;
|
|
633
|
+
/** Opt in to one identical-turn retry when a Responses completion has no text or tool call. */
|
|
634
|
+
emptyCompletionRetry?: boolean;
|
|
618
635
|
/** Maximum usage-log bytes read for one management snapshot. */
|
|
619
636
|
managementUsageMaxReadBytes?: number;
|
|
620
637
|
providers: Record<string, OcxProviderConfig>;
|
|
@@ -637,6 +654,20 @@ export interface OcxConfig {
|
|
|
637
654
|
* into a selector-qualified group; Codex still advertises only the first 5 visible rows.
|
|
638
655
|
*/
|
|
639
656
|
subagentModels?: string[];
|
|
657
|
+
/**
|
|
658
|
+
* Optional full picker ordering for the Codex model catalog, independent of the
|
|
659
|
+
* 5-slot `subagentModels` spawn_agent cap. DISPLAY-ONLY: it controls the visual order of
|
|
660
|
+
* the Codex model picker for large routed catalogs (10-20+ models) that would otherwise sort
|
|
661
|
+
* arbitrarily and reshuffle on every rebuild. Values are routed `<provider>/<model>` catalog
|
|
662
|
+
* slugs (matched by exact slug or `provider/id`); native OpenAI passthrough rows and
|
|
663
|
+
* account-qualified native rows are not reordered (order native rows via `subagentModels`).
|
|
664
|
+
* Listed routed rows appear in array order; rows not listed keep their normal display order.
|
|
665
|
+
* `subagentModels`-featured rows keep their top position. When unset or empty, catalog
|
|
666
|
+
* priority is unchanged. This changes ONLY what the user sees in the picker: the spawn_agent
|
|
667
|
+
* candidate set is derived from each row's natural priority and is provably unaffected, even
|
|
668
|
+
* when every routed row is listed (see opencodex_spawn_priority / effectiveSubagentRoster).
|
|
669
|
+
*/
|
|
670
|
+
modelPickerOrder?: string[];
|
|
640
671
|
/**
|
|
641
672
|
* Priority-ordered fallback models for spawned sub-agents. When the requested
|
|
642
673
|
* model is quota-exhausted or recently failed, opencodex rewrites the child
|
|
@@ -958,6 +989,12 @@ export interface OcxComboConfig {
|
|
|
958
989
|
stickyLimit?: number;
|
|
959
990
|
/** Used when the client omits reasoning.effort. null/omitted leaves the target default unchanged. */
|
|
960
991
|
defaultEffort?: OcxComboDefaultEffort | null;
|
|
992
|
+
/**
|
|
993
|
+
* Disable image input even when every target supports it.
|
|
994
|
+
* Omitted / `"auto"` keeps automatic capability derivation (default: enabled when
|
|
995
|
+
* the target intersection includes image).
|
|
996
|
+
*/
|
|
997
|
+
imageInput?: "auto" | "disabled";
|
|
961
998
|
/**
|
|
962
999
|
* Optional public model name replacing the default `combo/<id>` slug. Bare names
|
|
963
1000
|
* without "/" are allowed (e.g. "deepseek-v4-flash") so the combo can answer to a
|
|
@@ -1226,12 +1263,28 @@ export interface ProviderCostOverlay {
|
|
|
1226
1263
|
cacheWrite: number;
|
|
1227
1264
|
}
|
|
1228
1265
|
|
|
1266
|
+
export interface RequestPacingRule {
|
|
1267
|
+
/** Evenly spread request starts to this many requests per minute. */
|
|
1268
|
+
requestsPerMinute?: number;
|
|
1269
|
+
/** Minimum delay between request starts. The slower configured value wins. */
|
|
1270
|
+
minIntervalMs?: number;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
export interface ProviderRequestPacingConfig extends RequestPacingRule {
|
|
1274
|
+
/** False preserves legacy behavior with no client-side waiting. */
|
|
1275
|
+
enabled: boolean;
|
|
1276
|
+
/** Exact upstream model-id overrides; other models inherit the provider rule. */
|
|
1277
|
+
models?: Record<string, RequestPacingRule>;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1229
1280
|
/**
|
|
1230
1281
|
* One configured provider entry. `authMode` (default `"key"`) decides whether same-target 429
|
|
1231
1282
|
* retries are allowed; OAuth/forward credentials and local runtimes are never replayed.
|
|
1232
1283
|
*/
|
|
1233
1284
|
export interface OcxProviderConfig {
|
|
1234
1285
|
adapter: string;
|
|
1286
|
+
/** Optional outbound request-start pacing shared by this provider and its model overrides. */
|
|
1287
|
+
requestPacing?: ProviderRequestPacingConfig;
|
|
1235
1288
|
/** Cursor MCP compatibility bounds; positive integers when configured. */
|
|
1236
1289
|
mcpMaxTools?: number;
|
|
1237
1290
|
mcpMaxSchemaBytes?: number;
|
|
@@ -1274,8 +1327,10 @@ export interface OcxProviderConfig {
|
|
|
1274
1327
|
*/
|
|
1275
1328
|
requiresAdjacentResponsesToolResults?: boolean;
|
|
1276
1329
|
/**
|
|
1277
|
-
*
|
|
1278
|
-
*
|
|
1330
|
+
* Provider fallback for the OpenAI `service_tier` parameter. On Responses routes this
|
|
1331
|
+
* is the complete wire opt-in; Chat routes additionally require `chatServiceTier` or an
|
|
1332
|
+
* exact-model true declaration.
|
|
1333
|
+
* Tri-state: `true` lets fast mode inject/remove the field (an unset
|
|
1279
1334
|
* fast mode preserves a caller-supplied value); `false` strips the field and
|
|
1280
1335
|
* never injects, because an upstream documented as not supporting the parameter
|
|
1281
1336
|
* must not receive it; absent (`undefined`) leaves the provider unclassified —
|
|
@@ -1283,6 +1338,8 @@ export interface OcxProviderConfig {
|
|
|
1283
1338
|
* An explicit config value always wins over the registry default.
|
|
1284
1339
|
*/
|
|
1285
1340
|
supportsServiceTier?: boolean;
|
|
1341
|
+
/** Exact upstream model ids that override the provider-level service-tier capability. */
|
|
1342
|
+
modelSupportsServiceTier?: Record<string, boolean>;
|
|
1286
1343
|
/**
|
|
1287
1344
|
* Responses upstream whose native contract accepts plaintext reasoning replay
|
|
1288
1345
|
* (DeepSeek documents reasoning items with plaintext content). When set, the
|
|
@@ -1460,6 +1517,18 @@ export interface OcxProviderConfig {
|
|
|
1460
1517
|
* No effect unless `parallelToolCalls === false`; ignored by non-`openai-chat` adapters.
|
|
1461
1518
|
*/
|
|
1462
1519
|
pinParallelToolCallsFalse?: boolean;
|
|
1520
|
+
/**
|
|
1521
|
+
* Opt-in: extend the no-tool-call terminal continuation guard to this provider's
|
|
1522
|
+
* `openai-chat` routed turns. The guard (originally Anthropic-only, see
|
|
1523
|
+
* devlog/_fin/260706_previous-response-id-400) issues one bounded internal re-ask when a
|
|
1524
|
+
* model announces work but ends the turn without emitting a tool call. Self-hosted
|
|
1525
|
+
* OpenAI-compatible gateways (GLM/Kimi-family, etc.) hit the same premature-completion
|
|
1526
|
+
* pattern, but the heuristic that decides a "suspicious no-tool stop" was tuned on
|
|
1527
|
+
* Anthropic turns, so it stays OFF by default for the many registry providers that share
|
|
1528
|
+
* the `openai-chat` adapter. Enable only for a provider whose models are known to stop
|
|
1529
|
+
* mid-work; non-`openai-chat` adapters ignore this flag.
|
|
1530
|
+
*/
|
|
1531
|
+
terminalContinuationGuard?: boolean;
|
|
1463
1532
|
/**
|
|
1464
1533
|
* Opt-in: forward `prompt_cache_key` to the upstream `/chat/completions` body.
|
|
1465
1534
|
* OpenAI-specific extension; strict backends (Groq, Cerebras, etc.) reject unknown
|
|
@@ -1471,8 +1540,9 @@ export interface OcxProviderConfig {
|
|
|
1471
1540
|
* OpenAI-specific extension with the same hazard as `promptCacheKey` — strict backends
|
|
1472
1541
|
* reject unknown fields, and 66 registry providers share the `openai-chat` adapter, so a
|
|
1473
1542
|
* caller-supplied `service_tier` would otherwise turn working requests into upstream 400s.
|
|
1474
|
-
*
|
|
1475
|
-
* Default off; only enable for
|
|
1543
|
+
* Exact models may opt in through `modelSupportsServiceTier` instead; provider-level
|
|
1544
|
+
* `supportsServiceTier: false` remains a global denial. Default off; only enable for
|
|
1545
|
+
* providers that document this parameter on the chat wire.
|
|
1476
1546
|
*/
|
|
1477
1547
|
chatServiceTier?: boolean;
|
|
1478
1548
|
/**
|
package/src/update/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "./npm-cache-preflight.mjs";
|
|
11
11
|
import { handoffWindowsTrayForUpdate, planWindowsTrayUpdate } from "./tray-update-plan.mjs";
|
|
12
12
|
import { withProcessRuntimeProvenance } from "../lib/bun-runtime";
|
|
13
|
+
import { selfLaunchArgv } from "../lib/self-launch-argv";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* A `codex-history-backup-*.json` surviving a stop means the native-history restore was
|
|
@@ -248,7 +249,7 @@ export async function runUpdate(): Promise<void> {
|
|
|
248
249
|
if (serviceWasInstalled || readPid() || readRuntimePort()) {
|
|
249
250
|
console.log("⏹ Stopping the running proxy before updating...");
|
|
250
251
|
const stopStdio = updateChildStdio();
|
|
251
|
-
const stop = spawnSync(process.execPath, [
|
|
252
|
+
const stop = spawnSync(process.execPath, selfLaunchArgv(["stop"]), {
|
|
252
253
|
stdio: stopStdio,
|
|
253
254
|
encoding: stopStdio === "pipe" ? "utf8" : undefined,
|
|
254
255
|
windowsHide: true,
|
|
@@ -298,13 +299,13 @@ export async function runUpdate(): Promise<void> {
|
|
|
298
299
|
console.warn(`⚠️ Shim repair skipped: ${e instanceof Error ? e.message : e}`);
|
|
299
300
|
}
|
|
300
301
|
if (trayWasInstalled) {
|
|
301
|
-
const trayArgs =
|
|
302
|
+
const trayArgs = selfLaunchArgv(planWindowsTrayUpdate({ installed: trayWasInstalled, running: trayWasRunning }).installArgs);
|
|
302
303
|
const tray = spawnSync(process.execPath, trayArgs, { stdio: "inherit", windowsHide: true });
|
|
303
304
|
if (tray.status === 0) {
|
|
304
305
|
console.log("🔧 Refreshed Windows tray startup paths.");
|
|
305
306
|
} else {
|
|
306
307
|
console.warn("⚠️ Windows tray refresh failed. Run 'ocx tray install'.");
|
|
307
|
-
if (trayWasRunning) spawnSync(process.execPath, [
|
|
308
|
+
if (trayWasRunning) spawnSync(process.execPath, selfLaunchArgv(["tray", "start"]), { stdio: "ignore", windowsHide: true });
|
|
308
309
|
}
|
|
309
310
|
}
|
|
310
311
|
// The stop above unloaded any managed service; repair it with the NEW files
|
|
@@ -328,7 +329,7 @@ export async function runUpdate(): Promise<void> {
|
|
|
328
329
|
process.env.OCX_BAKE_PORT = String(capturedListen.port);
|
|
329
330
|
try {
|
|
330
331
|
const svcStdio = updateChildStdio();
|
|
331
|
-
const svc = spawnSync(process.execPath,
|
|
332
|
+
const svc = spawnSync(process.execPath, selfLaunchArgv(serviceReinstallArgs()), {
|
|
332
333
|
stdio: svcStdio,
|
|
333
334
|
encoding: svcStdio === "pipe" ? "utf8" : undefined,
|
|
334
335
|
windowsHide: true,
|
|
@@ -372,7 +373,7 @@ export async function runUpdate(): Promise<void> {
|
|
|
372
373
|
: " Run 'ocx service repair' to refresh the background service and see why it failed.");
|
|
373
374
|
const env = { ...process.env };
|
|
374
375
|
delete env.OCX_SERVICE;
|
|
375
|
-
const child = spawn(process.execPath, [
|
|
376
|
+
const child = spawn(process.execPath, selfLaunchArgv(["start", "--port", String(capturedListen.port)]), {
|
|
376
377
|
detached: true,
|
|
377
378
|
stdio: "ignore",
|
|
378
379
|
windowsHide: true,
|
package/src/update/job.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
verifyPidIdentity,
|
|
14
14
|
} from "../config";
|
|
15
15
|
import { isProcessAlive, killProxy } from "../lib/process-control";
|
|
16
|
+
import { selfLaunchArgv } from "../lib/self-launch-argv";
|
|
16
17
|
import {
|
|
17
18
|
buildWindowsElevatedArgumentList,
|
|
18
19
|
resolveTrustedWindowsPowerShellExe,
|
|
@@ -549,14 +550,12 @@ export function spawnGuiUpdateWorker(
|
|
|
549
550
|
channel: Channel,
|
|
550
551
|
restart: boolean,
|
|
551
552
|
): UpdateWorkerProcess {
|
|
552
|
-
const
|
|
553
|
-
const args = [
|
|
554
|
-
script,
|
|
553
|
+
const args = selfLaunchArgv([
|
|
555
554
|
"__gui-update-worker",
|
|
556
555
|
jobId,
|
|
557
556
|
channel,
|
|
558
557
|
restart ? "restart" : "no-restart",
|
|
559
|
-
];
|
|
558
|
+
]);
|
|
560
559
|
if (process.platform !== "win32") {
|
|
561
560
|
return spawn(process.execPath, args, {
|
|
562
561
|
detached: true,
|
|
@@ -1866,11 +1865,11 @@ export async function runGuiUpdateWorker(
|
|
|
1866
1865
|
}
|
|
1867
1866
|
|
|
1868
1867
|
if (trayWasInstalled) {
|
|
1869
|
-
const trayArgs =
|
|
1868
|
+
const trayArgs = selfLaunchArgv(planWindowsTrayUpdate({ installed: trayWasInstalled, running: trayWasRunning }).installArgs);
|
|
1870
1869
|
const tray = runLoggedCommand(job, process.execPath, trayArgs, 20_000);
|
|
1871
1870
|
if (tray.status !== 0) {
|
|
1872
1871
|
updateJob(job, {}, "Windows tray refresh failed; run 'ocx tray install'.");
|
|
1873
|
-
if (trayWasRunning) runLoggedCommand(job, process.execPath, [
|
|
1872
|
+
if (trayWasRunning) runLoggedCommand(job, process.execPath, selfLaunchArgv(["tray", "start"]), 15_000);
|
|
1874
1873
|
}
|
|
1875
1874
|
}
|
|
1876
1875
|
|
package/src/update/notify.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import { createInterface } from "node:readline/promises";
|
|
5
5
|
import { atomicWriteFile, getConfigDir } from "../config";
|
|
6
6
|
import { hasStarPromptRun } from "../cli/star-prompt";
|
|
7
|
+
import { selfLaunchArgv } from "../lib/self-launch-argv";
|
|
7
8
|
import {
|
|
8
9
|
type Channel,
|
|
9
10
|
currentVersion,
|
|
@@ -166,9 +167,10 @@ function cacheIsStale(cache: VersionCache | null): boolean {
|
|
|
166
167
|
export function triggerBackgroundRefreshIfStale(channel: Channel, cache: VersionCache | null): void {
|
|
167
168
|
if (!cacheIsStale(cache)) return;
|
|
168
169
|
try {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
const commandArgs = ["__refresh-version", channel];
|
|
171
|
+
const args = selfLaunchArgv(commandArgs);
|
|
172
|
+
if (args.length > commandArgs.length && (!args[0] || !existsSync(args[0]))) return;
|
|
173
|
+
const child = spawn(process.execPath, args, {
|
|
172
174
|
detached: true,
|
|
173
175
|
stdio: "ignore",
|
|
174
176
|
windowsHide: true,
|
package/src/usage/log.ts
CHANGED
|
@@ -27,7 +27,8 @@ export type AttemptRecoveryKind =
|
|
|
27
27
|
| "key-429"
|
|
28
28
|
| "rate-limit-429"
|
|
29
29
|
| "anthropic-oauth-429"
|
|
30
|
-
| "image-413"
|
|
30
|
+
| "image-413"
|
|
31
|
+
| "empty-completion";
|
|
31
32
|
|
|
32
33
|
export interface PersistedUsageAttempt {
|
|
33
34
|
ordinal: number;
|
|
@@ -36,6 +37,12 @@ export interface PersistedUsageAttempt {
|
|
|
36
37
|
adapter: string;
|
|
37
38
|
status: number;
|
|
38
39
|
durationMs: number;
|
|
40
|
+
/**
|
|
41
|
+
* True only when the upstream stream died after its 200 head was committed,
|
|
42
|
+
* so the row must not meter as a success the client never received.
|
|
43
|
+
* Absent on ordinary attempts so old rows keep their exact shape.
|
|
44
|
+
*/
|
|
45
|
+
streamAborted?: boolean;
|
|
39
46
|
/** TTFT relative to THIS attempt's start (WP4); unset for non-streaming/tool-only. */
|
|
40
47
|
firstOutputMs?: number;
|
|
41
48
|
sendCount: number;
|
|
@@ -202,6 +209,7 @@ const ATTEMPT_RECOVERY_KINDS = new Set<AttemptRecoveryKind>([
|
|
|
202
209
|
"rate-limit-429",
|
|
203
210
|
"anthropic-oauth-429",
|
|
204
211
|
"image-413",
|
|
212
|
+
"empty-completion",
|
|
205
213
|
]);
|
|
206
214
|
const USAGE_STATUSES = new Set<UsageStatus>([
|
|
207
215
|
"reported",
|
|
@@ -277,6 +285,8 @@ function normalizeUsageAttempt(raw: unknown): PersistedUsageAttempt | null {
|
|
|
277
285
|
adapter: attempt.adapter,
|
|
278
286
|
status: attempt.status,
|
|
279
287
|
durationMs: attempt.durationMs,
|
|
288
|
+
// Absent by default; only the literal `true` marker survives the round trip.
|
|
289
|
+
...(attempt.streamAborted === true ? { streamAborted: true } : {}),
|
|
280
290
|
...(isNonNegativeFiniteNumber(attempt.firstOutputMs)
|
|
281
291
|
? { firstOutputMs: attempt.firstOutputMs }
|
|
282
292
|
: {}),
|
package/src/vision/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import type { OcxConfig, OcxContentPart, OcxMessage, OcxParsedRequest, OcxProviderConfig, OcxTextContent } from "../types";
|
|
3
3
|
import { modelInList } from "../types";
|
|
4
|
+
import { modelRecordValue } from "../reasoning-effort";
|
|
4
5
|
import type { VisionReasoningEffort } from "../reasoning-effort";
|
|
5
6
|
import { describeImage, type DescribeOutcome, type VisionSettings } from "./describe";
|
|
6
7
|
import { describeImageAnthropic } from "./anthropic-describe";
|
|
@@ -18,6 +19,22 @@ import {
|
|
|
18
19
|
} from "./timeout-bounds";
|
|
19
20
|
|
|
20
21
|
export { describeImage } from "./describe";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* True when the model is explicitly known to be text-only — either listed in
|
|
25
|
+
* `noVisionModels` or declared with `modelInputModalities` that exclude "image".
|
|
26
|
+
* Returns false for unknown models (no evidence either way) so they fall through
|
|
27
|
+
* to native image passthrough, which is the safe default for an unclassified model.
|
|
28
|
+
*/
|
|
29
|
+
export function isModelTextOnly(
|
|
30
|
+
provider: OcxProviderConfig,
|
|
31
|
+
modelId: string,
|
|
32
|
+
): boolean {
|
|
33
|
+
if (modelInList(provider.noVisionModels, modelId)) return true;
|
|
34
|
+
const modalities = modelRecordValue(provider.modelInputModalities, modelId);
|
|
35
|
+
if (Array.isArray(modalities) && modalities.length > 0 && !modalities.includes("image")) return true;
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
21
38
|
export { describeImageAnthropic, parseAnthropicVisionSSE } from "./anthropic-describe";
|
|
22
39
|
export {
|
|
23
40
|
BASELINE_VISION_MODELS,
|
|
@@ -245,10 +262,10 @@ function messagesHaveImage(parsed: OcxParsedRequest): boolean {
|
|
|
245
262
|
export function shouldResolveOpenAiVisionSidecar(
|
|
246
263
|
config: OcxConfig,
|
|
247
264
|
provider: OcxProviderConfig,
|
|
248
|
-
|
|
249
|
-
|
|
265
|
+
modelId: string,
|
|
266
|
+
parsed: OcxParsedRequest,
|
|
250
267
|
): boolean {
|
|
251
|
-
if (!
|
|
268
|
+
if (!isModelTextOnly(provider, modelId) || !messagesHaveImage(parsed)) return false;
|
|
252
269
|
const cfg = config.visionSidecar ?? {};
|
|
253
270
|
if (cfg.enabled === false) return false;
|
|
254
271
|
return resolveVisionBackend(cfg.backend, findAnthropicVisionProvider(config)) === "openai";
|
|
@@ -275,7 +292,7 @@ export function planVisionSidecar(
|
|
|
275
292
|
parsed: OcxParsedRequest,
|
|
276
293
|
openAiSidecar?: ResolvedOpenAiForwardSidecar,
|
|
277
294
|
): VisionPlan | undefined {
|
|
278
|
-
if (!
|
|
295
|
+
if (!isModelTextOnly(provider, modelId)) return undefined;
|
|
279
296
|
if (!messagesHaveImage(parsed)) return undefined;
|
|
280
297
|
const cfg = config.visionSidecar ?? {};
|
|
281
298
|
if (cfg.enabled === false) return undefined;
|
package/src/web-search/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { OcxConfig, OcxParsedRequest, OcxProviderConfig } from "../types";
|
|
2
2
|
import { modelInList, toolChoiceToolPredicate } from "../types";
|
|
3
|
+
import { isModelTextOnly } from "../vision";
|
|
3
4
|
import type { SidecarSettings } from "./executor";
|
|
4
5
|
import type { ResolvedOpenAiForwardSidecar } from "../providers/openai-sidecar";
|
|
5
6
|
import { getAccountSet } from "../oauth/store";
|
|
@@ -166,7 +167,7 @@ export function planWebSearch(
|
|
|
166
167
|
timeoutMs,
|
|
167
168
|
);
|
|
168
169
|
// The routed model being text-only means the search model must verbalize image results (either backend).
|
|
169
|
-
const describeImages =
|
|
170
|
+
const describeImages = isModelTextOnly(provider, modelId);
|
|
170
171
|
const reasoning = cfg.reasoning ?? DEFAULT_SIDECAR_REASONING;
|
|
171
172
|
const streamRoutedModelOutput = cfg.streamRoutedModelOutput === true;
|
|
172
173
|
|