@bitkyc08/opencodex 2.66.0-preview.20260925 → 2.67.0-preview.20260926

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 (60) hide show
  1. package/bin/ocx.mjs +24 -11
  2. package/gui/dist/assets/{App-BVjA0T6O.js → App-B3VRRYSj.js} +15 -15
  3. package/gui/dist/assets/{App-gkoJIEOV.css → App-BJsT8Icc.css} +1 -1
  4. package/gui/dist/assets/{Tray-BCFtiWwD.js → Tray-TYAA0EQY.js} +1 -1
  5. package/gui/dist/assets/index-9Ju7nHsu.js +86 -0
  6. package/gui/dist/assets/index-DtNmX7hW.css +1 -0
  7. package/gui/dist/assets/{tray-data-C4FiNaxk.js → tray-data-xKfS2gSG.js} +1 -1
  8. package/gui/dist/index.html +2 -2
  9. package/package.json +1 -1
  10. package/src/chat/inbound.ts +82 -3
  11. package/src/chat/outbound.ts +5 -3
  12. package/src/claude/desktop-first-party.ts +12 -3
  13. package/src/claude/first-party-settings.ts +97 -0
  14. package/src/claude/intercept/client-class.ts +18 -0
  15. package/src/claude/intercept/listener.ts +5 -0
  16. package/src/claude/intercept/runtime.ts +6 -0
  17. package/src/cli/capabilities.ts +12 -0
  18. package/src/cli/claude-desktop.ts +11 -6
  19. package/src/cli/claude.ts +25 -1
  20. package/src/cli/ensure-desired-integrations.ts +14 -1
  21. package/src/cli/integrations.ts +9 -1
  22. package/src/cli/uninstall-client-state.ts +6 -0
  23. package/src/cli/uninstall-integrations.ts +115 -0
  24. package/src/codex/history-job.ts +2 -1
  25. package/src/codex/main-account-hard-lock.ts +36 -19
  26. package/src/codex/quota.ts +11 -1
  27. package/src/combos/resolve.ts +11 -3
  28. package/src/config/load-degrade.ts +4 -0
  29. package/src/generated/compatibility-version.json +67 -43
  30. package/src/generated/worker-bundles.gen.ts +4 -0
  31. package/src/images/loop.ts +10 -3
  32. package/src/integrations/aside-profile-context.ts +17 -0
  33. package/src/lab/fabric/executor.ts +25 -3
  34. package/src/lab/fabric/producer-isolate.ts +199 -32
  35. package/src/lab/fabric/scratch.ts +9 -2
  36. package/src/lib/shadow-call.ts +11 -4
  37. package/src/lib/worker-embed.ts +33 -0
  38. package/src/oauth/generic-account-failover.ts +5 -3
  39. package/src/oauth/index.ts +19 -29
  40. package/src/oauth/store.ts +106 -34
  41. package/src/remote-control/workspace-rpc.ts +114 -19
  42. package/src/router.ts +7 -1
  43. package/src/server/chat-completions.ts +2 -1
  44. package/src/server/index/claude-intercept-lifecycle.ts +13 -0
  45. package/src/server/management/agent-settings-routes.ts +123 -5
  46. package/src/server/management/context.ts +4 -0
  47. package/src/server/management/native-integration-routes.ts +19 -8
  48. package/src/server/responses/core-normalize.ts +13 -1
  49. package/src/server/responses/request-prepare.ts +5 -3
  50. package/src/server/responses/sidecar-execution.ts +9 -2
  51. package/src/server/search.ts +28 -7
  52. package/src/storage/policy-job.ts +2 -1
  53. package/src/storage/restore-job.ts +2 -1
  54. package/src/types/config.ts +11 -5
  55. package/src/update/transactional-install.mjs +5 -1
  56. package/src/web-search/alpha-search.ts +71 -0
  57. package/src/web-search/devin-executor.ts +192 -0
  58. package/src/web-search/loop.ts +10 -3
  59. package/gui/dist/assets/index-ComVsdSk.css +0 -1
  60. package/gui/dist/assets/index-vO4dgfiU.js +0 -86
@@ -0,0 +1,192 @@
1
+ import { buildMetadata } from "../adapters/devin/cloud-direct/metadata";
2
+ import { encodeMessage, encodeString, encodeVarintField, iterFields } from "../adapters/devin/cloud-direct/wire";
3
+ import { cancelBodyOnAbort } from "../lib/abort";
4
+ import { readBoundedResponseBytes } from "../lib/bounded-body";
5
+ import { redactSecretString } from "../lib/redact";
6
+ import { sidecarEnter } from "../lib/sidecar-tracker";
7
+ import { getValidAccessTokenSnapshot, publicOAuthAuthenticationErrorMessage, type OAuthAccessSnapshot } from "../oauth";
8
+ import { captureOAuthAccountSelection, commitOAuthAccountSelection } from "../oauth/store";
9
+ import { resolveDevinApiBaseUrl } from "../oauth/devin/api-base";
10
+ import type { SidecarOutcome } from "./executor";
11
+ import { MAX_SIDECAR_RESPONSE_BYTES, type WebSearchSource } from "./parse";
12
+ import { safeWebSearchSources } from "./sources";
13
+
14
+ const DEVIN_WEB_SEARCH_PATH = "/exa.api_server_pb.ApiServerService/GetWebSearchResults";
15
+ const DEVIN_WEB_SEARCH_RESULTS = 5;
16
+ const DEVIN_WEB_SEARCH_SNIPPET_CHARS = 2_000;
17
+
18
+ interface DevinSearchResult extends WebSearchSource {
19
+ snippet?: string;
20
+ }
21
+
22
+ function assertCompleteProtobuf(buf: Buffer): void {
23
+ let offset = 0;
24
+ while (offset < buf.length) {
25
+ const readVarint = (): bigint => {
26
+ let value = 0n;
27
+ for (let shift = 0n; shift < 70n; shift += 7n) {
28
+ if (offset >= buf.length) throw new Error("truncated varint");
29
+ const byte = buf[offset++]!;
30
+ value |= BigInt(byte & 0x7f) << shift;
31
+ if ((byte & 0x80) === 0) return value;
32
+ }
33
+ throw new Error("overlong varint");
34
+ };
35
+ const tag = readVarint();
36
+ const wire = Number(tag & 7n);
37
+ if (tag >> 3n === 0n) throw new Error("invalid field number");
38
+ if (wire === 0) readVarint();
39
+ else if (wire === 1) offset += 8;
40
+ else if (wire === 2) {
41
+ const length = Number(readVarint());
42
+ if (!Number.isSafeInteger(length)) throw new Error("invalid field length");
43
+ offset += length;
44
+ } else if (wire === 5) offset += 4;
45
+ else throw new Error("unsupported wire type");
46
+ if (offset > buf.length) throw new Error("truncated field");
47
+ }
48
+ }
49
+
50
+ function fields(buf: Buffer): ReturnType<typeof iterFields> {
51
+ assertCompleteProtobuf(buf);
52
+ return iterFields(buf);
53
+ }
54
+
55
+ function decodeText(value: Buffer): string | undefined {
56
+ try {
57
+ return new TextDecoder("utf-8", { fatal: true }).decode(value);
58
+ } catch {
59
+ return undefined;
60
+ }
61
+ }
62
+
63
+ function chunkText(chunk: Buffer): string | undefined {
64
+ for (const field of fields(chunk)) {
65
+ if (field.num === 1 && field.wire === 2 && Buffer.isBuffer(field.value)) return decodeText(field.value);
66
+ if (field.num === 3 && field.wire === 2 && Buffer.isBuffer(field.value)) {
67
+ for (const markdownField of fields(field.value)) {
68
+ if (markdownField.num === 2 && markdownField.wire === 2 && Buffer.isBuffer(markdownField.value)) {
69
+ return decodeText(markdownField.value);
70
+ }
71
+ }
72
+ }
73
+ }
74
+ return undefined;
75
+ }
76
+
77
+ function parseResult(buf: Buffer): DevinSearchResult | undefined {
78
+ let url: string | undefined;
79
+ let title: string | undefined;
80
+ let text: string | undefined;
81
+ let summary: string | undefined;
82
+ const chunks: string[] = [];
83
+ for (const field of fields(buf)) {
84
+ if (field.wire !== 2 || !Buffer.isBuffer(field.value)) continue;
85
+ if (field.num === 2) text = decodeText(field.value);
86
+ else if (field.num === 3) url = decodeText(field.value);
87
+ else if (field.num === 4) title = decodeText(field.value);
88
+ else if (field.num === 6) {
89
+ const chunk = chunkText(field.value);
90
+ if (chunk) chunks.push(chunk);
91
+ } else if (field.num === 7) summary = decodeText(field.value);
92
+ }
93
+ if (!url) return undefined;
94
+ const snippet = (summary || text || chunks.join("\n")).trim().slice(0, DEVIN_WEB_SEARCH_SNIPPET_CHARS);
95
+ return { url, ...(title ? { title } : {}), ...(snippet ? { snippet } : {}) };
96
+ }
97
+
98
+ /** Map Cognition's GetWebSearchResults protobuf response to the shared search outcome. */
99
+ export function mapDevinWebSearchResponse(buf: Buffer): SidecarOutcome {
100
+ const parsed: DevinSearchResult[] = [];
101
+ let summary = "";
102
+ for (const field of fields(buf)) {
103
+ if (field.wire !== 2 || !Buffer.isBuffer(field.value)) continue;
104
+ if (field.num === 1) {
105
+ const result = parseResult(field.value);
106
+ if (result) parsed.push(result);
107
+ } else if (field.num === 3) {
108
+ summary = decodeText(field.value)?.trim().slice(0, DEVIN_WEB_SEARCH_SNIPPET_CHARS) ?? "";
109
+ }
110
+ }
111
+ const sources = safeWebSearchSources(parsed);
112
+ if (sources.length === 0) return { text: "", sources: [], error: "devin web search returned no results" };
113
+ const byUrl = new Map(parsed.map(result => [result.url, result]));
114
+ const lines = sources.map(source => {
115
+ const result = byUrl.get(source.url);
116
+ return `- ${source.title ?? source.url}: ${result?.snippet || "(no excerpt)"} [${source.url}]`;
117
+ });
118
+ return { text: [summary, `Search results:\n${lines.join("\n")}`].filter(Boolean).join("\n\n"), sources };
119
+ }
120
+
121
+ /** Resolve one account snapshot for an entire alpha/search request. */
122
+ export async function resolveDevinWebSearchSnapshot(
123
+ providerName: string,
124
+ ): Promise<{ snapshot: OAuthAccessSnapshot } | { error: string }> {
125
+ const credentialProvider = providerName === "devin-cli" ? "devin" : providerName;
126
+ try {
127
+ const selection = captureOAuthAccountSelection(credentialProvider);
128
+ if (!selection) return { error: "devin web search auth failed: no signed-in account" };
129
+ const snapshot = await getValidAccessTokenSnapshot(credentialProvider);
130
+ const committed = await commitOAuthAccountSelection(credentialProvider, snapshot.accountId, {
131
+ expectedSelection: selection,
132
+ expectedCredentialGeneration: snapshot.generation,
133
+ requireUsableAccount: true,
134
+ });
135
+ return committed ? { snapshot } : { error: "devin web search auth changed; retry the request" };
136
+ } catch (error) {
137
+ return { error: `devin web search auth failed: ${publicOAuthAuthenticationErrorMessage(error)}` };
138
+ }
139
+ }
140
+
141
+ /** Execute one search through the signed-in Devin/Cognition account. */
142
+ export async function runDevinWebSearch(
143
+ query: string,
144
+ snapshot: OAuthAccessSnapshot,
145
+ abortSignal: AbortSignal,
146
+ ): Promise<SidecarOutcome> {
147
+ const apiServerUrl = resolveDevinApiBaseUrl(snapshot.apiBaseUrl);
148
+ const metadata = buildMetadata({
149
+ apiKey: snapshot.accessToken,
150
+ sessionId: crypto.randomUUID(),
151
+ requestId: BigInt(Date.now()),
152
+ triggerId: crypto.randomUUID(),
153
+ });
154
+ const body = Buffer.concat([
155
+ encodeMessage(1, metadata),
156
+ encodeString(2, query),
157
+ encodeVarintField(3, DEVIN_WEB_SEARCH_RESULTS),
158
+ ]);
159
+ const sidecarExit = sidecarEnter("web-search");
160
+ try {
161
+ const response = await fetch(`${apiServerUrl}${DEVIN_WEB_SEARCH_PATH}`, {
162
+ method: "POST",
163
+ headers: { "Content-Type": "application/proto", "Connect-Protocol-Version": "1" },
164
+ body: new Uint8Array(body),
165
+ signal: abortSignal,
166
+ redirect: "error",
167
+ });
168
+ const detachBodyGuard = cancelBodyOnAbort(response.body, abortSignal);
169
+ try {
170
+ const bounded = await readBoundedResponseBytes(response, {
171
+ maxBytes: MAX_SIDECAR_RESPONSE_BYTES,
172
+ signal: abortSignal,
173
+ });
174
+ if (bounded.oversized) return { text: "", sources: [], error: "devin web search response exceeded byte bound" };
175
+ if (!response.ok) return { text: "", sources: [], error: `devin web search HTTP ${response.status}` };
176
+ try {
177
+ return mapDevinWebSearchResponse(Buffer.from(bounded.bytes));
178
+ } catch {
179
+ return { text: "", sources: [], error: "devin web search returned malformed protobuf" };
180
+ }
181
+ } finally {
182
+ detachBodyGuard();
183
+ }
184
+ } catch (error) {
185
+ const kind = abortSignal.reason instanceof Error && abortSignal.reason.name === "TimeoutError"
186
+ ? "timeout"
187
+ : "connect_error";
188
+ return { text: "", sources: [], error: `devin web search ${kind}: ${redactSecretString(error instanceof Error ? error.message : String(error))}` };
189
+ } finally {
190
+ sidecarExit();
191
+ }
192
+ }
@@ -337,12 +337,19 @@ export interface WebSearchLoopDeps {
337
337
  * `retryParsed` is the exact iteration-local request the retry will be built from. The loop
338
338
  * sends a shallow copy of the outer parsed request, so a rotation that rebinds only the outer
339
339
  * object never reaches the wire. Optional so existing callers keep compiling.
340
+ *
341
+ * A rotation may cross ACCOUNTS, not just keys, and the attempt row is where an operator reads
342
+ * which one happened. A rotator that knows which kind it performed returns it alongside the
343
+ * adapter -- the same `{ adapter, recoveryKind }` shape `onCredentialError` already uses below.
340
344
  */
341
345
  on429?: (
342
346
  retryAfterHeader: string | null,
343
347
  responseHeaders?: Headers,
344
348
  retryParsed?: OcxParsedRequest,
345
- ) => ProviderAdapter | null | Promise<ProviderAdapter | null>;
349
+ ) =>
350
+ | { adapter: ProviderAdapter; recoveryKind: AttemptRecoveryKind }
351
+ | null
352
+ | Promise<{ adapter: ProviderAdapter; recoveryKind: AttemptRecoveryKind } | null>;
346
353
  /** Opt-in same-target 429 policy (key-auth providers). When present, 429 replays on the SAME key before on429 rotation. */
347
354
  retryOn429Policy?: Required<RateLimitRetryPolicy> | null;
348
355
  /** Called only when the final bridged Responses stream reaches completed or incomplete. */
@@ -568,10 +575,10 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
568
575
  // Never let a broken body's cancel promise outlive the cumulative header deadline. Observe
569
576
  // it, but proceed immediately to the rotated fetch under the SAME deadline signal.
570
577
  try { void prepared.response.body?.cancel().catch(() => {}); } catch { /* already closed */ }
571
- adapter = rotated;
578
+ adapter = rotated.adapter;
572
579
  // Stall-watchdog seam between bounded retry fetches (audit 011 B3).
573
580
  yield { type: "heartbeat" };
574
- prepared = await fetchOnce(adapter, "key-429");
581
+ prepared = await fetchOnce(adapter, rotated.recoveryKind);
575
582
  }
576
583
 
577
584
  // Final headers have arrived. Clear only the deadline timer before ANY body read.