codex-copilot-dx 0.6.12 → 0.6.14

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/README.md CHANGED
@@ -7,7 +7,7 @@ Use [Codex Desktop](https://openai.com/codex), [Claude Code](https://claude.com/
7
7
  A single in-process adapter (port `2026`) exposes both APIs over one Copilot
8
8
  account by default, or over isolated Codex and Claude accounts when configured:
9
9
 
10
- - **Codex** -> OpenAI Responses API (`/v1/responses`, `/v1/responses/compact`); Responses-only models and compaction proxy directly, chat models convert to Chat Completions.
10
+ - **Codex** -> OpenAI Responses API (`/v1/responses`, `/v1/responses/compact`); Responses-only models and compaction proxy directly, while chat models convert the text, image, and function-tool subset and explicitly reject incompatible top-level items, tools, and tool choices.
11
11
  - **Claude Code** -> Anthropic Messages API (`/v1/messages`, `/v1/messages/count_tokens`), translated to/from Chat Completions.
12
12
  - **Claude App** -> optional Claude Desktop App gateway profile using the same local Messages API plus local model discovery for the configured gateway key.
13
13
  - **PM Studio** -> optional macOS app patch that keeps the active PM profile for GPT models while routing enabled Claude models through an isolated CCDX Claude account.
@@ -118,15 +118,15 @@ discovery tries the compatible local CCDX merge first, and only exact enabled
118
118
  Anthropic model IDs use the isolated CCDX Claude profile. Neither credential is
119
119
  copied into the other application.
120
120
 
121
- CCDX keeps exact built-in recipes for PM Studio 2.9.7 and 2.9.10, and can also
122
- derive a source-bound recipe for a newer macOS build when its signed App layout,
123
- ASAR integrity metadata, and one exact Copilot configuration module remain
124
- structurally compatible. This is best-effort semantic compatibility, not a
125
- promise that every future PM Studio architecture can be patched. A missing or
126
- ambiguous module, changed request transport, unsupported integrity policy, or
127
- unverifiable source is rejected without modifying the App. Authorize the
128
- isolated Claude profile first, quit PM Studio and its updater, then run the
129
- one-time setup command:
121
+ CCDX treats the local PM Studio App selected for setup as the source of truth.
122
+ Its version and build label backup and recovery records; neither they nor the
123
+ installation source or original signature are compatibility admission gates.
124
+ Setup derives a source-bound recipe whenever the local ASAR integrity metadata
125
+ and exactly one compatible Copilot configuration module can be verified. A
126
+ missing or ambiguous module, changed request transport, or unsupported
127
+ integrity policy is rejected without modifying the App. Authorize the isolated
128
+ Claude profile first, quit PM Studio and its updater, then run the one-time
129
+ setup command:
130
130
 
131
131
  ```bash
132
132
  ccdx auth login claude --reauth --github-login <personal-github-login>
@@ -142,29 +142,20 @@ ccdx pms status
142
142
 
143
143
  `ccdx pm-studio status` and `ccdx pm-studio setup` are equivalent long-form aliases.
144
144
 
145
- `ccdx pms setup` performs preflight, source verification, backup, staging,
146
- integrity, signing, and replacement work. It never starts Device Flow, the
147
- adapter, Codex, or PM Studio, and it does not modify PM profiles, cached model
148
- lists, or saved PM tokens. A structurally compatible unknown version with an
149
- intact vendor signature is verified locally. If the upstream release has the
150
- known invalid-signature shape, setup downloads only the exact stable asset from
151
- the fixed official GitHub repository, enforces its API-provided SHA-256 and
152
- size, extracts it privately, and requires the installed App to match that tree
153
- (apart from strictly scoped locale pruning) before any backup or staging write.
154
- Network, hash, tree, structure, partial-patch, running-process, space, and every
155
- other pre-replacement failure leave the installed App unchanged. If final
145
+ `ccdx pms setup` performs local preflight, structural verification, backup,
146
+ staging, integrity, signing, and replacement work. It does not access a PM
147
+ Studio repository, release asset, PAT, or source-signing service. It never
148
+ starts Device Flow, the adapter, Codex, or PM Studio, and it does not modify PM
149
+ profiles, cached model lists, or saved PM tokens. Local fingerprints bind the
150
+ recipe, backup, staged App, and installed App without deciding whether the
151
+ original package is trusted. Structure, partial-patch, running-process, space,
152
+ and every other pre-replacement failure leave the installed App unchanged. If final
156
153
  read-only verification after an atomic replacement detects exceptional
157
154
  filesystem drift, setup does not attempt a second automatic write: it preserves
158
155
  the verified backup, retains any diagnostic stage still present, and prints
159
156
  recovery paths. Repeating setup reuses the version/source-bound verified backup
160
- and is idempotent; status and repeated setup do not redownload the release.
161
-
162
- Some official PM Studio artifacts can fail macOS's vendor-signature verifier
163
- even though their signing metadata remains readable. CCDX does not generally
164
- ignore that failure: exact built-in recipes require their frozen official
165
- fingerprint, while compatible unknown versions require the verified official
166
- GitHub asset and tree comparison described above. The staged and installed
167
- CCDX patch must still pass strict ad-hoc signature verification and match the
157
+ and is idempotent. The staged and installed CCDX patch must still pass strict
158
+ ad-hoc signature verification, use the App's bundle identifier, and match the
168
159
  complete patched-tree record in the backup manifest.
169
160
 
170
161
  The patch uses split origins, not an App-wide proxy. GPT and other non-Claude
@@ -183,9 +174,9 @@ CCDX uses the PM bearer only to fetch or validate official model access; it is
183
174
  never used as the credential for the isolated Claude upstream request. Keep
184
175
  port 2026 local to the machine.
185
176
 
186
- Patching replaces the vendor signature with an ad-hoc signature. PM Studio's
177
+ Patching applies an ad-hoc signature to the staged App. PM Studio's
187
178
  updater is not disabled; an official update may overwrite the patch or may
188
- require reinstalling the official App. Setup prints the verified backup and
179
+ require reinstalling PM Studio. Setup prints the verified backup and
189
180
  manifest paths plus the version-matched restore procedure. Never restore a
190
181
  backup over a different PM Studio version or build.
191
182
 
@@ -202,7 +193,7 @@ BACKUP_APP="<verified-backup-path-from-ccdx-pms-setup>"
202
193
  ```
203
194
 
204
195
  Only when all four values match each other and the version/build recorded in the
205
- source-bound manifest, move the patched App aside, restore the complete vendor
196
+ source-bound manifest, move the patched App aside, restore the complete local
206
197
  bundle, and verify it before launch:
207
198
 
208
199
  ```bash
@@ -214,12 +205,11 @@ mv "/Applications/PM Studio.app" "$PATCHED_APP"
214
205
  ccdx pms status
215
206
  ```
216
207
 
217
- For an official artifact with the documented upstream signature failure, the
218
- App-patch row from `ccdx pms status` must still classify the restored bundle as
219
- verified clean content rather than integrity drift. If the version check, copy,
220
- or both verification paths fail,
208
+ The App-patch row from `ccdx pms status` must classify the restored bundle as
209
+ the recorded clean local content rather than integrity drift. If the version
210
+ check, copy, or either verification path fails,
221
211
  do not launch the restored App. Keep the moved patched bundle at `$PATCHED_APP`
222
- and reinstall the official PM Studio instead of applying a version-mismatched
212
+ and reinstall PM Studio instead of applying a version-mismatched
223
213
  backup. CCDX never runs these restore commands or `sudo` automatically.
224
214
 
225
215
  To change the model used by Codex Auto-review, run:
@@ -311,7 +301,7 @@ To actively verify the protocol path through an already-running adapter, run:
311
301
  ccdx doctor --compat
312
302
  ```
313
303
 
314
- The compatibility doctor sends a few minimal Copilot requests to check Codex Auto-review, native Responses, streaming history, compaction, image tool namespace handling, and Anthropic streaming. It consumes a small amount of Copilot usage, never starts the adapter or device flow, and does not change client configuration. Combine it with `--online` when both the saved-token entitlement check and the adapter protocol checks are needed.
304
+ The compatibility doctor sends a few minimal Copilot requests to check Codex Auto-review, native Responses, streaming history, compaction, and Anthropic streaming. It consumes a small amount of Copilot usage, never starts the adapter or device flow, and does not change client configuration. Combine it with `--online` when both the saved-token entitlement check and the adapter protocol checks are needed.
315
305
 
316
306
  For a read-only summary of the running adapter, use:
317
307
 
@@ -448,9 +438,9 @@ Before image encoding, CCDX leaves ordinary visual history byte-for-byte unchang
448
438
 
449
439
  When expanded Responses history exceeds Copilot's 50-image request limit, the adapter removes older duplicate image occurrences first, then keeps the 50 most recent images. This applies only to the forwarded request: local history remains complete, current images are preferred over older history, and requests at or below the limit are unchanged.
450
440
 
451
- For `/v1/responses/compact`, the adapter sends one terminal `compaction_trigger` in unary mode, validates that Copilot returned completed compaction state, and rebuilds a replayable snapshot from recent system, developer, user, and assistant messages plus every compaction item. Only a validated snapshot becomes a new local history root. Later turns continue from it without re-expanding pre-compaction history; existing older response branches remain available until normal history eviction.
441
+ For `/v1/responses/compact`, the adapter sends one terminal `compaction_trigger` in unary mode, validates that Copilot returned completed compaction state, and preserves the complete upstream compacted window as the canonical next context. Only a validated window becomes a new local history root. Later turns continue from it without re-expanding pre-compaction history; existing older response branches remain available until normal history eviction.
452
442
 
453
- Successful streaming responses are accepted only when the upstream body is SSE and reaches its protocol terminal event. Unexpected EOF becomes a protocol-native stream error, and repeated blank tool-argument deltas are stopped per tool call. Inference responses forward safe quota, retry, model, trace, and upstream request-ID metadata; cookies, authorization, body-length, and encoding headers are not forwarded.
443
+ Successful streaming responses are accepted only when the upstream body is SSE and reaches its protocol terminal event; bytes after that terminal are not forwarded. Unexpected EOF becomes a protocol-native stream error, and repeated blank tool-argument deltas are stopped per tool call. Inference responses forward safe quota, retry, model, trace, and upstream request-ID metadata; cookies, authorization, body-length, and encoding headers are not forwarded.
454
444
 
455
445
  Newer ChatGPT/Codex clients can advertise an `image_gen` namespace that already exists upstream. The adapter removes that exact conflicting client tool before forwarding and retries once only when Copilot explicitly reports an image namespace collision. Image inputs and screenshot optimization remain enabled.
456
446
 
@@ -466,4 +456,4 @@ npm run verify
466
456
  npm run bench:payload
467
457
  ```
468
458
 
469
- `npm test` runs the unit and handler-level suite. `npm run test:smoke` starts a real local HTTP adapter with fully injected offline upstreams. `npm run bench:check` enforces relative token-counting performance and request-admission resource limits. `npm run pack:check` verifies the npm tarball contents without publishing. `npm run bench:payload` remains a report-only, isolated-process benchmark for 5-60 MiB image payloads and does not contact Copilot. The CI workflow runs the verification checks on supported Node.js release lines.
459
+ `npm test` runs the unit and handler-level suite. `npm run test:smoke` starts a real local HTTP adapter with fully injected offline upstreams. `npm run bench:check` enforces relative token-counting performance, linear SSE scanning, and request-admission resource limits. `npm run pack:check` verifies the npm tarball contents without publishing. `npm run bench:payload` remains a report-only, isolated-process benchmark for 5-60 MiB image payloads and does not contact Copilot. The CI workflow runs the verification checks on supported Node.js release lines.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-copilot-dx",
3
- "version": "0.6.12",
3
+ "version": "0.6.14",
4
4
  "description": "Use Codex Desktop, Claude Code, and Claude App with GitHub Copilot",
5
5
  "author": "Dale Xiao",
6
6
  "type": "module",
@@ -31,6 +31,65 @@ function median(values) {
31
31
  : ordered[middle];
32
32
  }
33
33
 
34
+ function sseLinearityProbe() {
35
+ return runProbe(`
36
+ import { webStreamLines } from "./src/stream.mjs";
37
+
38
+ const chunkBytes = 16 * 1024;
39
+ const maxBufferBytes = 8 * 1024 * 1024;
40
+ const chunk = new TextEncoder().encode("x".repeat(chunkBytes));
41
+ const results = [];
42
+ for (const inputMiB of [1, 2, 4, 8]) {
43
+ const inputBytes = inputMiB * 1024 * 1024;
44
+ let remaining = inputBytes;
45
+ const response = {
46
+ body: new ReadableStream({
47
+ pull(controller) {
48
+ if (remaining === 0) {
49
+ controller.close();
50
+ return;
51
+ }
52
+ const nextBytes = Math.min(chunk.byteLength, remaining);
53
+ controller.enqueue(nextBytes === chunk.byteLength ? chunk : chunk.subarray(0, nextBytes));
54
+ remaining -= nextBytes;
55
+ },
56
+ }, { highWaterMark: 0 }),
57
+ };
58
+ const originalByteLength = Buffer.byteLength;
59
+ let byteLengthCalls = 0;
60
+ let scannedBytes = 0;
61
+ Buffer.byteLength = function trackedByteLength(value, ...args) {
62
+ const bytes = originalByteLength(value, ...args);
63
+ byteLengthCalls += 1;
64
+ scannedBytes += bytes;
65
+ return bytes;
66
+ };
67
+ let lines = 0;
68
+ let outputBytes = 0;
69
+ const started = performance.now();
70
+ try {
71
+ for await (const line of webStreamLines(response, { maxBufferBytes })) {
72
+ lines += 1;
73
+ outputBytes += originalByteLength(line);
74
+ }
75
+ } finally {
76
+ Buffer.byteLength = originalByteLength;
77
+ }
78
+ results.push({
79
+ input_mib: inputMiB,
80
+ chunks: Math.ceil(inputBytes / chunkBytes),
81
+ lines,
82
+ output_bytes: outputBytes,
83
+ byte_length_calls: byteLengthCalls,
84
+ scanned_bytes: scannedBytes,
85
+ scan_ratio: +(scannedBytes / inputBytes).toFixed(3),
86
+ elapsed_ms: +(performance.now() - started).toFixed(1),
87
+ });
88
+ }
89
+ process.stdout.write(JSON.stringify({ chunk_kib: chunkBytes / 1024, max_buffer_mib: 8, results }));
90
+ `);
91
+ }
92
+
34
93
  function deterministicPixels(byteLength, initialSeed) {
35
94
  const pixels = Buffer.alloc(byteLength);
36
95
  let seed = initialSeed;
@@ -422,6 +481,7 @@ const report = {
422
481
  };
423
482
 
424
483
  if (checkMode) {
484
+ report.sse_linearity = sseLinearityProbe();
425
485
  report.large_token_count = {
426
486
  proxy: largeTokenCountProbe("proxy"),
427
487
  materialized_array: largeTokenCountProbe("array"),
@@ -466,6 +526,20 @@ if (checkMode) {
466
526
  if (report.tool_output_parse_cache.parse_calls !== 1) {
467
527
  failures.push(`stringified tool output parsed ${report.tool_output_parse_cache.parse_calls} times instead of once`);
468
528
  }
529
+ const expectedSseSizes = [1, 2, 4, 8];
530
+ if (report.sse_linearity.results.length !== expectedSseSizes.length
531
+ || report.sse_linearity.results.some((sample, index) => sample.input_mib !== expectedSseSizes[index])) {
532
+ failures.push("SSE linearity probe did not cover 1/2/4/8 MiB inputs");
533
+ }
534
+ for (const sample of report.sse_linearity.results) {
535
+ const inputBytes = sample.input_mib * 1024 * 1024;
536
+ if (sample.lines !== 1 || sample.output_bytes !== inputBytes) {
537
+ failures.push(`SSE parser changed the ${sample.input_mib}MiB fragmented line`);
538
+ }
539
+ if (sample.scanned_bytes > inputBytes * 1.01 || sample.byte_length_calls > sample.chunks + 1) {
540
+ failures.push(`SSE parser scan complexity is not linear at ${sample.input_mib}MiB`);
541
+ }
542
+ }
469
543
  for (const payload of report.large_payload_peak) {
470
544
  if (payload.admission_max_active_mib > payload.admission_budget_mib) {
471
545
  failures.push(`request admission exceeded its budget at ${payload.target_request_mib}MiB x${payload.concurrency}`);
package/src/anthropic.mjs CHANGED
@@ -130,6 +130,14 @@ export function anthropicToChat(body, options = {}) {
130
130
 
131
131
  function uid() { return randomUUID().replace(/-/g, ""); }
132
132
 
133
+ function invalidToolArguments(toolName) {
134
+ const name = String(toolName || "unknown").slice(0, 100);
135
+ return invalidUpstreamStream(
136
+ `Upstream Chat Completions tool "${name}" did not return valid JSON object arguments`,
137
+ "upstream_tool_arguments_json_invalid",
138
+ );
139
+ }
140
+
133
141
  export function chatToAnthropic(openaiResp, model, options = {}) {
134
142
  const choice = openaiResp.choices?.[0];
135
143
  const msg = choice?.message || {};
@@ -138,8 +146,20 @@ export function chatToAnthropic(openaiResp, model, options = {}) {
138
146
  if (msg.content) content.push({ type: "text", text: msg.content });
139
147
  if (Array.isArray(msg.tool_calls)) {
140
148
  for (const tc of msg.tool_calls) {
141
- let input = {};
142
- try { input = JSON.parse(tc.function.arguments || "{}"); } catch { input = {}; }
149
+ const rawArguments = tc.function?.arguments;
150
+ let input;
151
+ if (rawArguments === undefined || rawArguments === null || rawArguments === "") {
152
+ input = {};
153
+ } else {
154
+ try {
155
+ input = JSON.parse(rawArguments);
156
+ } catch {
157
+ throw invalidToolArguments(tc.function?.name);
158
+ }
159
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
160
+ throw invalidToolArguments(tc.function?.name);
161
+ }
162
+ }
143
163
  content.push({ type: "tool_use", id: tc.id, name: tc.function.name, input });
144
164
  }
145
165
  }
package/src/config.mjs CHANGED
@@ -9,8 +9,39 @@ const CONFIG_PATH = path.join(os.homedir(), ".codex", "config.toml");
9
9
  const MODEL_CONTEXT_WINDOW = 1_000_000;
10
10
  const MODEL_AUTO_COMPACT_TOKEN_LIMIT = 900_000;
11
11
 
12
+ function isTomlTableHeader(line) {
13
+ const source = line.trimStart();
14
+ const openingBrackets = source.startsWith("[[") ? 2 : source.startsWith("[") ? 1 : 0;
15
+ if (!openingBrackets) return false;
16
+
17
+ let quote = "";
18
+ let escaped = false;
19
+ for (let index = openingBrackets; index < source.length; index += 1) {
20
+ const char = source[index];
21
+ if (quote) {
22
+ if (quote === '"' && char === "\\" && !escaped) {
23
+ escaped = true;
24
+ continue;
25
+ }
26
+ if (char === quote && !escaped) quote = "";
27
+ escaped = false;
28
+ continue;
29
+ }
30
+ if (char === '"' || char === "'") {
31
+ quote = char;
32
+ continue;
33
+ }
34
+ if (char !== "]" || (openingBrackets === 2 && source[index + 1] !== "]")) continue;
35
+
36
+ if (!source.slice(openingBrackets, index).trim()) return false;
37
+ const tail = source.slice(index + openingBrackets).trim();
38
+ return tail === "" || tail.startsWith("#");
39
+ }
40
+ return false;
41
+ }
42
+
12
43
  function setTopLevelTomlDefault(lines, key, value) {
13
- let end = lines.findIndex((line) => /^\s*\[[^\]]+\]\s*$/.test(line));
44
+ let end = lines.findIndex(isTomlTableHeader);
14
45
  if (end === -1) end = lines.length;
15
46
 
16
47
  const keyRe = new RegExp(`^\\s*${key}\\s*=`);
@@ -28,7 +59,7 @@ function setTomlKey(lines, sectionName, key, value) {
28
59
 
29
60
  let end = lines.length;
30
61
  for (let i = start + 1; i < lines.length; i++) {
31
- if (/^\s*\[[^\]]+\]\s*$/.test(lines[i])) {
62
+ if (isTomlTableHeader(lines[i])) {
32
63
  end = i;
33
64
  break;
34
65
  }
@@ -57,7 +88,11 @@ export function computeUpdatedCodexConfig(content, adapterPort = 2026, adapterHo
57
88
 
58
89
  let changed = false;
59
90
  const openaiLine = `openai_base_url = "${baseUrl}"`;
60
- const openaiIndex = lines.findIndex((line) => /^openai_base_url\s*=/.test(line));
91
+ const firstSection = lines.findIndex(isTomlTableHeader);
92
+ const topLevelEnd = firstSection === -1 ? lines.length : firstSection;
93
+ const openaiIndex = lines.findIndex((line, index) => (
94
+ index < topLevelEnd && /^\s*openai_base_url\s*=/.test(line)
95
+ ));
61
96
  if (openaiIndex === -1) {
62
97
  lines.unshift(openaiLine);
63
98
  changed = true;
package/src/doctor.mjs CHANGED
@@ -181,13 +181,12 @@ export async function inspectAdapterCompatibility({
181
181
  });
182
182
 
183
183
  if (firstResponse) {
184
- await runCompatibilityCheck(checks, "Responses stream, history, and image tool compatibility", async () => {
184
+ await runCompatibilityCheck(checks, "Responses stream and history compatibility", async () => {
185
185
  const text = await compatibilityRequest(fetchImpl, `${baseUrl}/v1/responses`, {
186
186
  model: responsesModel,
187
187
  stream: true,
188
188
  previous_response_id: firstResponse.id,
189
189
  input: "Reply with OK again.",
190
- tools: [{ type: "image_generation" }],
191
190
  }, timeoutMs);
192
191
  if (!/^event:\s*response\.completed\s*$/m.test(text)) throw new Error("stream did not contain response.completed");
193
192
  });
@@ -599,11 +598,11 @@ export async function collectDoctorChecks({
599
598
  } else if (pm.app.state === "clean") {
600
599
  checks.push({
601
600
  kind: "warn",
602
- message: `PM Studio ${version} is supported but not patched`,
601
+ message: `PM Studio ${version} has a compatible local patch structure but is not patched`,
603
602
  fix: "ccdx pms setup",
604
603
  });
605
604
  } else if (pm.app.state === "unsupported") {
606
- checks.push({ kind: "warn", message: `PM Studio ${version} compatibility cannot be safely verified; no files will be changed` });
605
+ checks.push({ kind: "warn", message: `PM Studio ${version} does not expose one uniquely compatible patch structure; no files will be changed` });
607
606
  } else if (pm.app.state === "drift" || pm.app.state === "error") {
608
607
  checks.push({ kind: "err", message: `PM Studio integrity check failed: ${pm.app.issues.join("; ")}` });
609
608
  }
@@ -8,17 +8,42 @@ export const ELECTRON_ASAR_INTEGRITY_SENTINEL = "AGbevlPCksUGKNL8TSn7wGmJEuJsXb2
8
8
  const PM_STUDIO_COPILOT_CONFIG_MODULE_LENGTH = 2_121;
9
9
  const PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE_SHA256 = "3da6245d676015ab6cd26e4d6a0d2f54894a687c370aa419c5ad170533d086aa";
10
10
  const PM_STUDIO_COPILOT_CONFIG_MODULE_ANCHOR = "47024(I,e,t){";
11
+ const PM_STUDIO_COPILOT_CONFIG_MODULE_SENTINEL = `API_ENDPOINT:"${PM_STUDIO_ORIGIN}"`;
11
12
  const PM_STUDIO_COPILOT_CONFIG_MODULE_PATH = "dist/main/main.js";
12
13
  const PM_STUDIO_COMPATIBILITY_ID = "exact-copilot-config-module-v1";
13
14
  const PM_STUDIO_COMPATIBLE_TARGET_MAX_BYTES = 128 * 1024 * 1024;
14
15
  const PM_STUDIO_COMPATIBLE_BLOCK_MAX_COUNT = 16_384;
16
+ const PM_STUDIO_COMPATIBLE_MODULE_PREFIX_MAX_BYTES = 512;
17
+ const PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE = '47024(I,e,t){"use strict";t.d(e,{qn:()=>l});const l={CLIENT_ID:"Iv1.b507a08c87ecfe98",CLIENT_SECRET:void 0,API_ENDPOINT:"https://api.githubcopilot.com",DEVICE_CODE_URL:"https://github.com/login/device/code",ACCESS_TOKEN_URL:"https://github.com/login/oauth/access_token",COPILOT_TOKEN_URL:"https://api.github.com/copilot_internal/v2/token",USER_AGENT:"GitHubCopilotChat/0.26.7",EDITOR_VERSION:"vscode/1.99.3",EDITOR_PLUGIN_VERSION:"copilot-chat/0.26.7",INTEGRATION_ID:"vscode-chat",STANDARD_HEADERS:{Accept:"application/json","Content-Type":"application/json","User-Agent":"GitHubCopilotChat/0.26.7","Editor-Version":"vscode/1.99.3","Editor-Plugin-Version":"copilot-chat/0.26.7","Copilot-Integration-Id":"vscode-chat","X-Request-Id":()=>`req_${Date.now()}_${Math.random().toString(36).substr(2,9)}`}};class c{constructor(){this.config={...l}}static getInstance(){return c.instance||(c.instance=new c),c.instance}getConfig(){return{...this.config}}updateConfig(I){this.config={...this.config,...I}}resetConfig(){this.config={...l}}validateConfig(){return function(){const I=[];return l.CLIENT_ID||I.push("GitHub Copilot Client ID is required"),l.API_ENDPOINT||I.push("GitHub Copilot API endpoint is required"),{valid:0===I.length,errors:I}}()}getStandardHeaders(I){return function(I){const e={Accept:l.STANDARD_HEADERS.Accept,"Content-Type":l.STANDARD_HEADERS["Content-Type"],"User-Agent":l.STANDARD_HEADERS["User-Agent"],"Editor-Version":l.STANDARD_HEADERS["Editor-Version"],"Editor-Plugin-Version":l.STANDARD_HEADERS["Editor-Plugin-Version"],"Copilot-Integration-Id":l.STANDARD_HEADERS["Copilot-Integration-Id"],"X-Request-Id":"function"==typeof l.STANDARD_HEADERS["X-Request-Id"]?l.STANDARD_HEADERS["X-Request-Id"]():l.STANDARD_HEADERS["X-Request-Id"]};return I&&(e.Authorization=`Bearer ${I}`),e}(I)}getCopilotTokenHeaders(I){return function(I){return{Authorization:`Bearer ${I}`,Accept:"application/json","User-Agent":l.USER_AGENT,"Editor-Version":l.EDITOR_VERSION,"Editor-Plugin-Version":l.EDITOR_PLUGIN_VERSION}}(I)}getDeviceCodeHeaders(){return{Accept:"application/json","User-Agent":l.USER_AGENT}}}c.getInstance()}';
18
+
19
+ function copilotConfigAlphaTemplate() {
20
+ // This is a reviewed byte template, not a generic JavaScript normalizer:
21
+ // only the five minifier bindings vary; every other byte remains exact.
22
+ const parts = [];
23
+ const moduleIdEnd = PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE.indexOf("(");
24
+ const pattern = /(?<![$\w])(?:I|e|t|l|c)(?![$\w])/g;
25
+ pattern.lastIndex = moduleIdEnd;
26
+ let offset = moduleIdEnd;
27
+ for (let match = pattern.exec(PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE); match; match = pattern.exec(PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE)) {
28
+ parts.push(PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE.slice(offset, match.index));
29
+ parts.push(Object.freeze({ identifier: match[0] }));
30
+ offset = match.index + match[0].length;
31
+ }
32
+ parts.push(PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE.slice(offset));
33
+ return Object.freeze(parts);
34
+ }
35
+
36
+ const PM_STUDIO_COPILOT_CONFIG_ALPHA_TEMPLATE = copilotConfigAlphaTemplate();
15
37
 
16
- function splitOriginConfigModule() {
17
- const source = `47024(I,e,t){"use strict";t.d(e,{qn:()=>l});const l={CLIENT_ID:"Iv1.b507a08c87ecfe98",CLIENT_SECRET:void 0,API_ENDPOINT:"${PM_STUDIO_ORIGIN}",DEVICE_CODE_URL:"https://github.com/login/device/code",ACCESS_TOKEN_URL:"https://github.com/login/oauth/access_token",COPILOT_TOKEN_URL:"https://api.github.com/copilot_internal/v2/token",USER_AGENT:"GitHubCopilotChat/0.26.7",EDITOR_VERSION:"vscode/1.99.3",EDITOR_PLUGIN_VERSION:"copilot-chat/0.26.7",INTEGRATION_ID:"vscode-chat",STANDARD_HEADERS:{Accept:"application/json","Content-Type":"application/json","User-Agent":"GitHubCopilotChat/0.26.7","Editor-Version":"vscode/1.99.3","Editor-Plugin-Version":"copilot-chat/0.26.7","Copilot-Integration-Id":"vscode-chat","X-Request-Id":()=>\`req_\${Date.now()}_\${Math.random().toString(36).substr(2,9)}\`}};const c=fetch.bind(globalThis),n="${CCDX_PM_STUDIO_ORIGIN}",a="${PM_STUDIO_SPLIT_ORIGIN_MARKER}",h="X-CCDX-PM-Relay",d=new Set,m=I=>I.headers.get(h)===a,q=I=>AbortSignal.any([I,AbortSignal.timeout(750)].filter(Boolean)),f=(I,e=400)=>Response.json({error:{code:I}},{status:e,headers:{[h]:a}});globalThis.fetch=async(I,e)=>{const t=I.url||I+"";if(t===\`\${l.API_ENDPOINT}/models\`){try{const t=await c(\`\${n}/models\`,e);if(t.ok&&m(t))try{let I=await t.clone().json(),e=I.data||I;if(Array.isArray(e)){d.clear();for(const I of e)/anthropic/i.test(I.vendor||I.owned_by)&&d.add(I.id);return t}}catch{}t.body?.cancel?.()?.catch?.(()=>{})}catch(I){if(e?.signal?.aborted)throw e.signal.reason||I}return c(I,e)}const s=t.startsWith(l.API_ENDPOINT)?t.slice(l.API_ENDPOINT.length):"";if("POST"===e?.method&&["/chat/completions","/responses","/embeddings"].includes(s)){let t="";try{t=(JSON.parse(e.body).model||"").trim()}catch{}if(d.has(t)||/^claude-/i.test(t)){if("/chat/completions"!==s)return f("model_not_supported");try{const I=await c(\`\${n}/models\`,{signal:q(e?.signal)}),t=401===I.status&&m(I);await I.text();if(!t)throw 0;const l=await c(\`\${n}\${s}\`,e);if(!m(l)){await l.body?.cancel?.();throw 0}return l}catch(I){if(e?.signal?.aborted)throw e.signal.reason||I;return f("relay_incompatible",503)}}}return c(I,e)}}`;
18
- if (source.length > PM_STUDIO_COPILOT_CONFIG_MODULE_LENGTH) {
38
+ function splitOriginConfigModule(
39
+ moduleId = "47024",
40
+ windowLength = PM_STUDIO_COPILOT_CONFIG_MODULE_LENGTH,
41
+ ) {
42
+ const source = `${moduleId}(I,e,t){"use strict";t.d(e,{qn:()=>l});const l={CLIENT_ID:"Iv1.b507a08c87ecfe98",CLIENT_SECRET:void 0,API_ENDPOINT:"${PM_STUDIO_ORIGIN}",DEVICE_CODE_URL:"https://github.com/login/device/code",ACCESS_TOKEN_URL:"https://github.com/login/oauth/access_token",COPILOT_TOKEN_URL:"https://api.github.com/copilot_internal/v2/token",USER_AGENT:"GitHubCopilotChat/0.26.7",EDITOR_VERSION:"vscode/1.99.3",EDITOR_PLUGIN_VERSION:"copilot-chat/0.26.7",INTEGRATION_ID:"vscode-chat",STANDARD_HEADERS:{Accept:"application/json","Content-Type":"application/json","User-Agent":"GitHubCopilotChat/0.26.7","Editor-Version":"vscode/1.99.3","Editor-Plugin-Version":"copilot-chat/0.26.7","Copilot-Integration-Id":"vscode-chat","X-Request-Id":()=>\`req_\${Date.now()}_\${Math.random().toString(36).substr(2,9)}\`}};const c=fetch.bind(globalThis),n="${CCDX_PM_STUDIO_ORIGIN}",a="${PM_STUDIO_SPLIT_ORIGIN_MARKER}",h="X-CCDX-PM-Relay",d=new Set,m=I=>I.headers.get(h)===a,q=I=>AbortSignal.any([I,AbortSignal.timeout(750)].filter(Boolean)),f=(I,e=400)=>Response.json({error:{code:I}},{status:e,headers:{[h]:a}});globalThis.fetch=async(I,e)=>{const t=I.url||I+"";if(t===\`\${l.API_ENDPOINT}/models\`){try{const t=await c(\`\${n}/models\`,e);if(t.ok&&m(t))try{let I=await t.clone().json(),e=I.data||I;if(Array.isArray(e)){d.clear();for(const I of e)/anthropic/i.test(I.vendor||I.owned_by)&&d.add(I.id);return t}}catch{}t.body?.cancel?.()?.catch?.(()=>{})}catch(I){if(e?.signal?.aborted)throw e.signal.reason||I}return c(I,e)}const s=t.startsWith(l.API_ENDPOINT)?t.slice(l.API_ENDPOINT.length):"";if("POST"===e?.method&&["/chat/completions","/responses","/embeddings"].includes(s)){let t="";try{t=(JSON.parse(e.body).model||"").trim()}catch{}if(d.has(t)||/^claude-/i.test(t)){if("/chat/completions"!==s)return f("model_not_supported");try{const I=await c(\`\${n}/models\`,{signal:q(e?.signal)}),t=401===I.status&&m(I);await I.text();if(!t)throw 0;const l=await c(\`\${n}\${s}\`,e);if(!m(l)){await l.body?.cancel?.();throw 0}return l}catch(I){if(e?.signal?.aborted)throw e.signal.reason||I;return f("relay_incompatible",503)}}}return c(I,e)}}`;
43
+ if (source.length > windowLength) {
19
44
  throw new Error("PM Studio split-origin module exceeds its exact source window");
20
45
  }
21
- return `${source.slice(0, -1)}${" ".repeat(PM_STUDIO_COPILOT_CONFIG_MODULE_LENGTH - source.length)}}`;
46
+ return `${source.slice(0, -1)}${" ".repeat(windowLength - source.length)}}`;
22
47
  }
23
48
 
24
49
  export const PM_STUDIO_SPLIT_ORIGIN_CONFIG_MODULE = splitOriginConfigModule();
@@ -320,6 +345,93 @@ function occurrencePositions(haystack, needle) {
320
345
  return positions;
321
346
  }
322
347
 
348
+ function asciiDigit(byte) {
349
+ return byte >= 48 && byte <= 57;
350
+ }
351
+
352
+ function asciiIdentifierStart(byte) {
353
+ return byte === 36
354
+ || byte === 95
355
+ || (byte >= 65 && byte <= 90)
356
+ || (byte >= 97 && byte <= 122);
357
+ }
358
+
359
+ function matchCopilotConfigAlphaTemplate(source, offset) {
360
+ let moduleIdEnd = offset;
361
+ while (moduleIdEnd < source.length && asciiDigit(source[moduleIdEnd])) moduleIdEnd += 1;
362
+ const moduleIdLength = moduleIdEnd - offset;
363
+ if (moduleIdLength === 0
364
+ || moduleIdLength > 10
365
+ || (moduleIdLength > 1 && source[offset] === 48)) return null;
366
+ const moduleId = source.subarray(offset, moduleIdEnd).toString("ascii");
367
+ if (!Number.isSafeInteger(Number(moduleId))) return null;
368
+
369
+ let cursor = moduleIdEnd;
370
+ const identifiers = new Map();
371
+ const reverseIdentifiers = new Map();
372
+ for (const part of PM_STUDIO_COPILOT_CONFIG_ALPHA_TEMPLATE) {
373
+ if (typeof part === "string") {
374
+ const literal = Buffer.from(part);
375
+ if (cursor + literal.length > source.length
376
+ || !source.subarray(cursor, cursor + literal.length).equals(literal)) return null;
377
+ cursor += literal.length;
378
+ continue;
379
+ }
380
+
381
+ if (!asciiIdentifierStart(source[cursor])
382
+ || asciiIdentifierStart(source[cursor + 1])
383
+ || asciiDigit(source[cursor + 1])) return null;
384
+ const candidate = String.fromCharCode(source[cursor]);
385
+ // The audited module uses single-byte minifier names. Keeping that bound
386
+ // rejects capture-prone "semantic" guesses while allowing permutations.
387
+ const mapped = identifiers.get(part.identifier);
388
+ const reverse = reverseIdentifiers.get(candidate);
389
+ if ((mapped && mapped !== candidate) || (reverse && reverse !== part.identifier)) return null;
390
+ identifiers.set(part.identifier, candidate);
391
+ reverseIdentifiers.set(candidate, part.identifier);
392
+ cursor += 1;
393
+ }
394
+
395
+ if (cursor < source.length && source[cursor] !== 44 && source[cursor] !== 125) return null;
396
+ return {
397
+ offset,
398
+ end: cursor,
399
+ length: cursor - offset,
400
+ moduleId,
401
+ anchor: `${moduleId}(`,
402
+ };
403
+ }
404
+
405
+ function locateCompatibleCopilotConfigModule(source) {
406
+ const sentinel = Buffer.from(PM_STUDIO_COPILOT_CONFIG_MODULE_SENTINEL);
407
+ const matches = new Map();
408
+ let sentinelCount = 0;
409
+ let searchOffset = 0;
410
+ while (searchOffset <= source.length - sentinel.length) {
411
+ const sentinelOffset = source.indexOf(sentinel, searchOffset);
412
+ if (sentinelOffset < 0) break;
413
+ sentinelCount += 1;
414
+ searchOffset = sentinelOffset + sentinel.length;
415
+
416
+ const lowerBound = Math.max(0, sentinelOffset - PM_STUDIO_COMPATIBLE_MODULE_PREFIX_MAX_BYTES);
417
+ for (let offset = lowerBound; offset <= sentinelOffset; offset += 1) {
418
+ if (!asciiDigit(source[offset]) || (offset > 0 && asciiDigit(source[offset - 1]))) continue;
419
+ if (offset > 0 && source[offset - 1] !== 44 && source[offset - 1] !== 123) continue;
420
+ const match = matchCopilotConfigAlphaTemplate(source, offset);
421
+ if (match && sentinelOffset >= match.offset && sentinelOffset < match.end) {
422
+ matches.set(match.offset, match);
423
+ }
424
+ }
425
+ }
426
+ if (sentinelCount === 0) {
427
+ throw incompatibleAsar(`${PM_STUDIO_COPILOT_CONFIG_MODULE_PATH} has no compatible Copilot configuration sentinel`);
428
+ }
429
+ if (matches.size !== 1) {
430
+ throw incompatibleAsar(`${PM_STUDIO_COPILOT_CONFIG_MODULE_PATH} has ${matches.size} alpha-compatible Copilot configuration modules, expected exactly one`);
431
+ }
432
+ return matches.values().next().value;
433
+ }
434
+
323
435
  export function blockSha256(buffer, blockSize) {
324
436
  const size = integer(blockSize, "block size");
325
437
  if (size === 0) throw new Error("Invalid ASAR block size: 0");
@@ -385,22 +497,24 @@ export function createCompatiblePmStudioRecipe(value, options = {}) {
385
497
  throw incompatibleAsar(`${PM_STUDIO_COPILOT_CONFIG_MODULE_PATH} integrity metadata does not match its bytes`);
386
498
  }
387
499
 
388
- const anchorPositions = occurrencePositions(
389
- sourceTarget,
390
- Buffer.from(PM_STUDIO_COPILOT_CONFIG_MODULE_ANCHOR),
391
- );
392
- if (anchorPositions.length !== 1) {
393
- throw incompatibleAsar(`${PM_STUDIO_COPILOT_CONFIG_MODULE_PATH} has ${anchorPositions.length} compatible module anchors, expected exactly one`);
500
+ const compatibleModule = locateCompatibleCopilotConfigModule(sourceTarget);
501
+ const editOffset = compatibleModule.offset;
502
+ const editEnd = compatibleModule.end;
503
+ const sourceEdit = sourceTarget.subarray(editOffset, editEnd);
504
+ const anchorPositions = occurrencePositions(sourceTarget, Buffer.from(compatibleModule.anchor));
505
+ if (anchorPositions.length !== 1 || anchorPositions[0] !== editOffset) {
506
+ throw incompatibleAsar(`${PM_STUDIO_COPILOT_CONFIG_MODULE_PATH} compatible module header is not unique`);
507
+ }
508
+ let replacementSource;
509
+ try {
510
+ replacementSource = splitOriginConfigModule(compatibleModule.moduleId, compatibleModule.length);
511
+ } catch (error) {
512
+ throw incompatibleAsar(error.message);
394
513
  }
395
- const editOffset = anchorPositions[0];
396
- const editEnd = editOffset + PM_STUDIO_COPILOT_CONFIG_MODULE_LENGTH;
397
- if (editEnd > sourceTarget.length
398
- || sha256Hex(sourceTarget.subarray(editOffset, editEnd))
399
- !== PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE_SHA256) {
400
- throw incompatibleAsar("PM Studio Copilot configuration module is not the exact compatible source window");
514
+ const replacement = Buffer.from(replacementSource);
515
+ if (replacement.length !== compatibleModule.length) {
516
+ throw incompatibleAsar("PM Studio split-origin replacement does not preserve the compatible module size");
401
517
  }
402
-
403
- const replacement = Buffer.from(PM_STUDIO_SPLIT_ORIGIN_CONFIG_MODULE);
404
518
  const patchedTarget = Buffer.from(sourceTarget);
405
519
  replacement.copy(patchedTarget, editOffset);
406
520
  const patchedTargetSha256 = sha256Hex(patchedTarget);
@@ -436,12 +550,12 @@ export function createCompatiblePmStudioRecipe(value, options = {}) {
436
550
  const edit = Object.freeze({
437
551
  offset: editOffset,
438
552
  absoluteOffset: absoluteOffset + editOffset,
439
- length: PM_STUDIO_COPILOT_CONFIG_MODULE_LENGTH,
440
- anchor: PM_STUDIO_COPILOT_CONFIG_MODULE_ANCHOR,
553
+ length: compatibleModule.length,
554
+ anchor: compatibleModule.anchor,
441
555
  anchorCount: 1,
442
- sourceSha256: PM_STUDIO_COPILOT_CONFIG_MODULE_SOURCE_SHA256,
556
+ sourceSha256: sha256Hex(sourceEdit),
443
557
  patchedSha256: sha256Hex(replacement),
444
- replacement: PM_STUDIO_SPLIT_ORIGIN_CONFIG_MODULE,
558
+ replacement: replacementSource,
445
559
  });
446
560
  const target = Object.freeze({
447
561
  path: PM_STUDIO_COPILOT_CONFIG_MODULE_PATH,