@bitkyc08/opencodex 2.54.0-preview.20260914 → 2.55.0-preview.20260914

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.
Files changed (86) hide show
  1. package/gui/dist/assets/{index-B4VYfZcY.js → index-DH2PUHqr.js} +10 -10
  2. package/gui/dist/index.html +1 -1
  3. package/package.json +1 -1
  4. package/src/adapters/anthropic-image-codec.ts +57 -0
  5. package/src/adapters/anthropic-image-normalize.ts +28 -1
  6. package/src/adapters/anthropic.ts +68 -6
  7. package/src/adapters/base.ts +8 -0
  8. package/src/adapters/coding-agent/protocol.ts +41 -16
  9. package/src/adapters/cursor/cursor-errors.ts +1 -1
  10. package/src/adapters/cursor/live-transport.ts +5 -1
  11. package/src/adapters/cursor/native-exec-fs.ts +10 -10
  12. package/src/adapters/cursor/native-exec-network.ts +2 -2
  13. package/src/adapters/cursor/native-exec-shell.ts +13 -12
  14. package/src/adapters/cursor/native-exec.ts +51 -10
  15. package/src/adapters/cursor/policy-error.ts +75 -0
  16. package/src/adapters/cursor/protobuf-request.ts +105 -1
  17. package/src/adapters/devin/cloud-direct/catalog.ts +34 -2
  18. package/src/adapters/devin/live-models.ts +33 -2
  19. package/src/adapters/google-wire-compiler.ts +8 -0
  20. package/src/adapters/google.ts +46 -0
  21. package/src/adapters/input-media-guard.ts +45 -0
  22. package/src/adapters/kiro/adapter.ts +8 -0
  23. package/src/adapters/kiro/payload.ts +28 -6
  24. package/src/adapters/kiro-events.ts +25 -6
  25. package/src/adapters/kiro-images.ts +30 -0
  26. package/src/adapters/kiro-retry.ts +8 -0
  27. package/src/adapters/openai-chat.ts +33 -4
  28. package/src/adapters/openai-responses.ts +26 -0
  29. package/src/adapters/registry.ts +4 -0
  30. package/src/bridge.ts +163 -116
  31. package/src/chat/image-parts.ts +151 -0
  32. package/src/chat/inbound.ts +70 -33
  33. package/src/cli/connect.ts +30 -9
  34. package/src/cli/dispatch.ts +7 -3
  35. package/src/cli/index.ts +3 -0
  36. package/src/cli/runtime-api.ts +25 -0
  37. package/src/cli/status.ts +21 -19
  38. package/src/cli/system-restart-client.ts +25 -0
  39. package/src/clients/config-export.ts +14 -4
  40. package/src/codex/app-server-processes.ts +25 -0
  41. package/src/codex/auth-context.ts +8 -0
  42. package/src/codex/autostart-health.ts +36 -2
  43. package/src/codex/catalog/provider-fetch.ts +41 -0
  44. package/src/codex/catalog-auto-refresh.ts +182 -0
  45. package/src/codex/catalog-refresh-status.ts +93 -0
  46. package/src/codex/history-provider.ts +55 -0
  47. package/src/codex/model-entitlements.ts +78 -0
  48. package/src/codex/native-profile-processes.ts +114 -15
  49. package/src/codex/prompt-text-probe.ts +274 -41
  50. package/src/codex/routing-adoption.ts +189 -0
  51. package/src/codex/routing.ts +520 -48
  52. package/src/codex/runtime.ts +249 -7
  53. package/src/combos/failover.ts +45 -0
  54. package/src/config.ts +124 -4
  55. package/src/generated/compatibility-version.json +110 -74
  56. package/src/generated/model-metadata.ts +1 -0
  57. package/src/lib/request-execution-budget.ts +202 -0
  58. package/src/lib/upstream-retry.ts +95 -8
  59. package/src/lib/workflow-budget.ts +172 -0
  60. package/src/oauth/devin.ts +57 -12
  61. package/src/providers/quota.ts +37 -6
  62. package/src/providers/registry.ts +53 -6
  63. package/src/responses/input-media.ts +65 -0
  64. package/src/responses/parser-content.ts +42 -0
  65. package/src/responses/schema.ts +12 -2
  66. package/src/server/audio-live.ts +1 -2
  67. package/src/server/audio-transcriptions.ts +1 -2
  68. package/src/server/auth-cors.ts +1 -1
  69. package/src/server/background-lifecycle.ts +18 -0
  70. package/src/server/chat-completions.ts +23 -8
  71. package/src/server/chat-native.ts +17 -17
  72. package/src/server/index.ts +24 -0
  73. package/src/server/management/request-history-routes.ts +5 -0
  74. package/src/server/request-log.ts +8 -2
  75. package/src/server/responses/compact.ts +51 -3
  76. package/src/server/responses/core.ts +238 -28
  77. package/src/server/search.ts +7 -9
  78. package/src/types/config.ts +51 -6
  79. package/src/usage/log.ts +37 -0
  80. package/src/vision/eligibility.ts +37 -4
  81. package/src/vision/index.ts +1 -0
  82. package/src/vision/plan.ts +45 -10
  83. package/src/web-search/alpha-search.ts +324 -0
  84. package/src/web-search/index.ts +13 -22
  85. package/src/web-search/passthrough-bridge.ts +195 -22
  86. package/src/web-search/sidecar-providers.ts +22 -0
@@ -0,0 +1,189 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { readProcessStartMsBatch } from "./app-server-processes";
3
+ import type { CodexRoutingKind } from "./inject";
4
+ import { JOURNAL_PATH } from "./journal";
5
+ import {
6
+ listCodexClientProcesses,
7
+ type CodexClientProcessList,
8
+ } from "./native-profile-processes";
9
+
10
+ export type RoutingAdoption = "not-applicable" | "adopted" | "pending-client-restart" | "unknown";
11
+
12
+ export interface RoutingAdoptionEvidence {
13
+ adoption: RoutingAdoption;
14
+ injectedAtMs: number | null;
15
+ staleClients: Array<{ pid: number; startedAtMs: number }>;
16
+ observedClients: number;
17
+ }
18
+
19
+ export interface CollectRoutingAdoptionOptions {
20
+ routingKind: CodexRoutingKind;
21
+ platform?: NodeJS.Platform;
22
+ listClients?: () => CodexClientProcessList;
23
+ readStartMsBatch?: (pids: readonly number[], platform: NodeJS.Platform) => Map<number, number | null>;
24
+ injectedAtMs?: number | null;
25
+ }
26
+
27
+ /**
28
+ * Infer whether running Codex clients had an opportunity to read the
29
+ * injected opencodex-local route (#4550).
30
+ *
31
+ * "adopted" is not an observation of live traffic. It means every matched
32
+ * running Codex client started after the route was written. That is the
33
+ * inference the operator needed, and it is also the overclaim we must not
34
+ * make. Known sources of a false adopted this evidence does not cover:
35
+ * a client the matcher does not recognise; a restored or resumed thread
36
+ * that keeps an already-open direct WebSocket even though the process
37
+ * started after injection; OPENAI_BASE_URL / profile overrides in the
38
+ * client's own environment; a start time in the SAME second as the
39
+ * injection, which we deliberately treat as not stale; an empty match
40
+ * set, which is vacuously adopted; a client running against a different
41
+ * CODEX_HOME or config path than the journal we read; and Codex surfaces
42
+ * the CLI predicate does not match at all, such as codex-code-mode-host,
43
+ * Electron helpers, and VS Code extension hosts. Anything that cannot
44
+ * be verified is "unknown", never a clean bill of health — matching the
45
+ * #476 restart contract where enumeration failure means no verified
46
+ * targets.
47
+ */
48
+ export function deriveRoutingAdoption(inputs: {
49
+ routingKind: CodexRoutingKind;
50
+ injectedAtMs: number | null;
51
+ clients: ReadonlyArray<{ pid: number; startedAtMs: number | null }>;
52
+ enumerationFailed?: boolean;
53
+ }): RoutingAdoptionEvidence {
54
+ const observedClients = inputs.clients.length;
55
+ if (inputs.routingKind !== "opencodex-local") {
56
+ return { adoption: "not-applicable", injectedAtMs: null, staleClients: [], observedClients: 0 };
57
+ }
58
+ if (inputs.enumerationFailed) {
59
+ return { adoption: "unknown", injectedAtMs: inputs.injectedAtMs, staleClients: [], observedClients };
60
+ }
61
+ if (inputs.injectedAtMs === null) {
62
+ return { adoption: "unknown", injectedAtMs: null, staleClients: [], observedClients };
63
+ }
64
+ const injectedAtMs = inputs.injectedAtMs;
65
+ const staleClients: Array<{ pid: number; startedAtMs: number }> = [];
66
+ let unreadableStart = false;
67
+ for (const client of inputs.clients) {
68
+ if (client.startedAtMs === null) {
69
+ unreadableStart = true;
70
+ continue;
71
+ }
72
+ if (startedBeforeInjection(client.startedAtMs, injectedAtMs)) {
73
+ staleClients.push({ pid: client.pid, startedAtMs: client.startedAtMs });
74
+ }
75
+ }
76
+ staleClients.sort((left, right) => left.pid - right.pid);
77
+ if (staleClients.length > 0) {
78
+ return { adoption: "pending-client-restart", injectedAtMs, staleClients, observedClients };
79
+ }
80
+ if (unreadableStart) {
81
+ return { adoption: "unknown", injectedAtMs, staleClients: [], observedClients };
82
+ }
83
+ return { adoption: "adopted", injectedAtMs, staleClients: [], observedClients };
84
+ }
85
+
86
+ /**
87
+ * ps lstart is second-granularity; app-server-processes.ts uses <= for catalog
88
+ * staleness for the opposite reason (a rewrite in the same second may be unseen).
89
+ * Here a start in the same wall-clock second as the injection cannot be proven
90
+ * to predate it, so a rounding artifact must not produce a false pending-restart
91
+ * warning (#4550). Truncate both sides to seconds; strictly earlier seconds are
92
+ * stale.
93
+ */
94
+ function startedBeforeInjection(startedAtMs: number, injectedAtMs: number): boolean {
95
+ return Math.floor(startedAtMs / 1000) < Math.floor(injectedAtMs / 1000);
96
+ }
97
+
98
+ /**
99
+ * Gather journal + process evidence for deriveRoutingAdoption.
100
+ *
101
+ * Injection time is the newer of the journal timestamp and JOURNAL_PATH mtime:
102
+ * writeJournal records the native snapshot time and then no-ops, while
103
+ * markJournalInjectedState rewrites the file (mtime moves) without touching
104
+ * timestamp. The journal is parsed here instead of through readJournal, which
105
+ * can delete a corrupt file; a status read must never mutate state. Ownership
106
+ * matches journaledInjectedOpenaiBaseUrl plus journalOwner: we only bound a
107
+ * route we recorded writing.
108
+ */
109
+ export function collectRoutingAdoption(options: CollectRoutingAdoptionOptions): RoutingAdoptionEvidence {
110
+ const { routingKind } = options;
111
+ if (routingKind !== "opencodex-local") {
112
+ return deriveRoutingAdoption({ routingKind, injectedAtMs: null, clients: [] });
113
+ }
114
+ const injectedAtMs = options.injectedAtMs !== undefined
115
+ ? options.injectedAtMs
116
+ : readOwnedInjectionBoundMs();
117
+ let listed: CodexClientProcessList;
118
+ try {
119
+ listed = (options.listClients ?? (() => listCodexClientProcesses({ platform: options.platform })))();
120
+ } catch {
121
+ listed = { status: "unavailable" };
122
+ }
123
+ if (listed.status === "unavailable") {
124
+ return deriveRoutingAdoption({
125
+ routingKind,
126
+ injectedAtMs,
127
+ clients: [],
128
+ enumerationFailed: true,
129
+ });
130
+ }
131
+ const platform = options.platform ?? process.platform;
132
+ const pids = listed.processes.map(proc => proc.pid);
133
+ let starts: Map<number, number | null>;
134
+ try {
135
+ starts = pids.length === 0
136
+ ? new Map()
137
+ : (options.readStartMsBatch ?? readProcessStartMsBatch)(pids, platform);
138
+ } catch {
139
+ starts = new Map(pids.map(pid => [pid, null]));
140
+ }
141
+ const clients = listed.processes.map(proc => ({
142
+ pid: proc.pid,
143
+ startedAtMs: starts.get(proc.pid) ?? null,
144
+ }));
145
+ return deriveRoutingAdoption({ routingKind, injectedAtMs, clients });
146
+ }
147
+
148
+ function readOwnedInjectionBoundMs(): number | null {
149
+ try {
150
+ if (!existsSync(JOURNAL_PATH)) return null;
151
+ const journal = JSON.parse(readFileSync(JOURNAL_PATH, "utf-8")) as {
152
+ version?: unknown;
153
+ timestamp?: unknown;
154
+ injectedOpenaiBaseUrl?: unknown;
155
+ owner?: { kind?: unknown; pid?: unknown; apiKeyId?: unknown };
156
+ pid?: unknown;
157
+ };
158
+ if (journal === null || typeof journal !== "object" || journal.version !== 1) return null;
159
+ const injectedUrl = typeof journal.injectedOpenaiBaseUrl === "string"
160
+ ? journal.injectedOpenaiBaseUrl
161
+ : "";
162
+ if (!injectedUrl) return null;
163
+ if (!journalHasOwner(journal)) return null;
164
+ const recordedMs = typeof journal.timestamp === "string" ? Date.parse(journal.timestamp) : Number.NaN;
165
+ let mtimeMs = Number.NaN;
166
+ try {
167
+ mtimeMs = statSync(JOURNAL_PATH).mtimeMs;
168
+ } catch {
169
+ mtimeMs = Number.NaN;
170
+ }
171
+ const bound = Math.max(
172
+ Number.isFinite(recordedMs) ? recordedMs : Number.NEGATIVE_INFINITY,
173
+ Number.isFinite(mtimeMs) ? mtimeMs : Number.NEGATIVE_INFINITY,
174
+ );
175
+ return Number.isFinite(bound) ? bound : null;
176
+ } catch {
177
+ return null;
178
+ }
179
+ }
180
+
181
+ function journalHasOwner(journal: {
182
+ owner?: { kind?: unknown; pid?: unknown; apiKeyId?: unknown };
183
+ pid?: unknown;
184
+ }): boolean {
185
+ const owner = journal.owner;
186
+ if (owner?.kind === "client" && typeof owner.apiKeyId === "string" && owner.apiKeyId) return true;
187
+ if (owner?.kind === "process" && Number.isSafeInteger(owner.pid) && Number(owner.pid) > 0) return true;
188
+ return Number.isSafeInteger(journal.pid) && Number(journal.pid) > 0;
189
+ }