codex-copilot-dx 0.6.15 → 0.6.18

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.
@@ -17,12 +17,103 @@ const MAX_INFLIGHT_BODY_BYTES = HTTP_RUNTIME_CONFIG.maxInflightBodyBytes;
17
17
  const MAX_QUEUED_REQUESTS = HTTP_RUNTIME_CONFIG.maxQueuedRequests;
18
18
  const REQUEST_QUEUE_TIMEOUT_MS = HTTP_RUNTIME_CONFIG.requestQueueTimeoutMs;
19
19
 
20
+ export const MAX_UPSTREAM_ERROR_BODY_BYTES = 1024 * 1024;
21
+ export const MAX_UPSTREAM_MODEL_CATALOG_BYTES = 8 * 1024 * 1024;
22
+ export const MAX_UPSTREAM_RETRY_DRAIN_BYTES = 64 * 1024;
23
+
20
24
  export function httpError(message, statusCode) {
21
25
  const err = new Error(message);
22
26
  err.statusCode = statusCode;
23
27
  return err;
24
28
  }
25
29
 
30
+ function upstreamBodyTooLarge(label, maxBytes) {
31
+ const error = httpError(`${label} exceeds ${maxBytes} bytes`, 502);
32
+ error.code = "ccdx_upstream_response_too_large";
33
+ error.jsonBody = {
34
+ error: {
35
+ message: error.message,
36
+ type: "upstream_error",
37
+ code: error.code,
38
+ },
39
+ };
40
+ return error;
41
+ }
42
+
43
+ function unreadableUpstreamBody(label) {
44
+ const error = httpError(`${label} is not a readable web stream`, 502);
45
+ error.code = "ccdx_upstream_response_unreadable";
46
+ error.jsonBody = {
47
+ error: {
48
+ message: error.message,
49
+ type: "upstream_error",
50
+ code: error.code,
51
+ },
52
+ };
53
+ return error;
54
+ }
55
+
56
+ async function cancelResponseBody(response) {
57
+ try { await response?.body?.cancel?.(); } catch {}
58
+ }
59
+
60
+ function responseContentLength(response) {
61
+ const raw = response?.headers?.get?.("content-length")
62
+ ?? response?.headers?.["content-length"]
63
+ ?? response?.headers?.["Content-Length"];
64
+ const parsed = Number.parseInt(raw || "", 10);
65
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
66
+ }
67
+
68
+ export async function readBoundedResponseBuffer(response, {
69
+ maxBytes,
70
+ label = "Upstream response body",
71
+ } = {}) {
72
+ const limit = Number.isFinite(maxBytes) && maxBytes >= 0 ? Math.floor(maxBytes) : 0;
73
+ if (responseContentLength(response) > limit) {
74
+ await cancelResponseBody(response);
75
+ throw upstreamBodyTooLarge(label, limit);
76
+ }
77
+ if (!response?.body) {
78
+ if ((responseContentLength(response) || 0) > 0) throw unreadableUpstreamBody(label);
79
+ return Buffer.alloc(0);
80
+ }
81
+ const reader = response?.body?.getReader?.();
82
+ if (!reader) throw unreadableUpstreamBody(label);
83
+ const chunks = [];
84
+ let total = 0;
85
+ try {
86
+ while (true) {
87
+ const { done, value } = await reader.read();
88
+ if (done) break;
89
+ const chunk = Buffer.from(value);
90
+ total += chunk.length;
91
+ if (total > limit) {
92
+ await reader.cancel().catch(() => {});
93
+ throw upstreamBodyTooLarge(label, limit);
94
+ }
95
+ chunks.push(chunk);
96
+ }
97
+ return Buffer.concat(chunks, total);
98
+ } finally {
99
+ reader.releaseLock();
100
+ }
101
+ }
102
+
103
+ export async function readBoundedResponseText(response, options) {
104
+ return new TextDecoder().decode(await readBoundedResponseBuffer(response, options));
105
+ }
106
+
107
+ export async function discardBoundedResponseBody(response, maxBytes = MAX_UPSTREAM_RETRY_DRAIN_BYTES) {
108
+ try {
109
+ await readBoundedResponseBuffer(response, { maxBytes, label: "Upstream retry response body" });
110
+ return true;
111
+ } catch {
112
+ await cancelResponseBody(response);
113
+ return false;
114
+ }
115
+ }
116
+
26
117
  function payloadTooLarge(kind, maxBytes) {
27
118
  return httpError(`${kind} request body exceeds ${maxBytes} bytes`, 413);
28
119
  }
package/src/lock.mjs CHANGED
@@ -32,23 +32,171 @@ function processIsAlive(pid) {
32
32
  }
33
33
  }
34
34
 
35
+ function readStaleLockSnapshot(lockPath, io) {
36
+ const before = io.lstatSync(lockPath);
37
+ if (!before.isFile() || before.isSymbolicLink()) return { safe: false };
38
+
39
+ const noFollow = io.constants?.O_NOFOLLOW || 0;
40
+ let fd;
41
+ try {
42
+ fd = io.openSync(lockPath, io.constants.O_RDONLY | noFollow);
43
+ const opened = io.fstatSync(fd);
44
+ if (!opened.isFile()
45
+ || opened.dev !== before.dev
46
+ || opened.ino !== before.ino
47
+ || opened.size !== before.size) {
48
+ return { safe: false };
49
+ }
50
+ const text = io.readFileSync(fd, "utf8");
51
+ const after = io.fstatSync(fd);
52
+ if (after.dev !== opened.dev
53
+ || after.ino !== opened.ino
54
+ || after.size !== opened.size
55
+ || after.mtimeMs !== opened.mtimeMs
56
+ || after.ctimeMs !== opened.ctimeMs) {
57
+ return { safe: false };
58
+ }
59
+ let record = {};
60
+ try {
61
+ record = JSON.parse(text);
62
+ } catch {}
63
+ return {
64
+ safe: true,
65
+ dev: after.dev,
66
+ ino: after.ino,
67
+ mtimeMs: after.mtimeMs,
68
+ size: after.size,
69
+ text,
70
+ pid: Number(record?.pid),
71
+ owner: typeof record?.owner === "string" ? record.owner : "",
72
+ };
73
+ } finally {
74
+ if (fd !== undefined) io.closeSync(fd);
75
+ }
76
+ }
77
+
35
78
  function sameLock(left, right) {
36
79
  return left.dev === right.dev
37
80
  && left.ino === right.ino
38
81
  && left.mtimeMs === right.mtimeMs
39
82
  && left.size === right.size
40
- && left.owner === right.owner;
83
+ && left.owner === right.owner
84
+ && left.text === right.text;
41
85
  }
42
86
 
43
- function maybeRemoveStaleLock(lockPath, staleMs, nowMs) {
44
- if (!Number.isFinite(staleMs) || staleMs <= 0) return false;
87
+ function lockPathExists(lockPath, io) {
88
+ try {
89
+ io.lstatSync(lockPath);
90
+ return true;
91
+ } catch (error) {
92
+ if (error?.code === "ENOENT") return false;
93
+ throw error;
94
+ }
95
+ }
96
+
97
+ function writeLockRecord(lockPath, record, io) {
98
+ const fd = io.openSync(lockPath, "wx", 0o600);
99
+ try {
100
+ io.writeFileSync(fd, JSON.stringify(record));
101
+ } catch (error) {
102
+ io.closeSync(fd);
103
+ try { io.unlinkSync(lockPath); } catch {}
104
+ throw error;
105
+ }
106
+ return fd;
107
+ }
108
+
109
+ function removeOwnedLock(lockPath, owner, io) {
110
+ try {
111
+ const snapshot = readStaleLockSnapshot(lockPath, io);
112
+ if (snapshot.safe && snapshot.owner === owner) io.unlinkSync(lockPath);
113
+ } catch (error) {
114
+ if (error?.code !== "ENOENT") throw error;
115
+ }
116
+ }
117
+
118
+ function reclaimGuardPath(lockPath) {
119
+ return `${lockPath}.reclaim`;
120
+ }
121
+
122
+ function acquireReclaimGuard(lockPath, io) {
123
+ const guardPath = reclaimGuardPath(lockPath);
124
+ const owner = randomUUID();
125
+ let fd;
126
+ try {
127
+ fd = writeLockRecord(guardPath, {
128
+ pid: process.pid,
129
+ owner,
130
+ created_at: new Date().toISOString(),
131
+ }, io);
132
+ io.closeSync(fd);
133
+ return { guardPath, owner };
134
+ } catch (error) {
135
+ if (fd !== undefined) try { io.closeSync(fd); } catch {}
136
+ if (error?.code === "EEXIST") return null;
137
+ throw error;
138
+ }
139
+ }
140
+
141
+ function maybeRemoveAbandonedGuard(guardPath, staleMs, nowMs, io, processAlive) {
45
142
  try {
46
- const first = readLockSnapshot(lockPath);
47
- if (nowMs - first.mtimeMs <= staleMs || processIsAlive(first.pid)) return false;
48
- const current = readLockSnapshot(lockPath);
49
- if (!sameLock(first, current)) return false;
50
- fs.unlinkSync(lockPath);
143
+ const snapshot = readStaleLockSnapshot(guardPath, io);
144
+ if (!snapshot.safe || nowMs - snapshot.mtimeMs <= staleMs || processAlive(snapshot.pid)) return false;
145
+ const quarantinePath = `${guardPath}.stale-${randomUUID()}`;
146
+ io.renameSync(guardPath, quarantinePath);
147
+ const moved = readStaleLockSnapshot(quarantinePath, io);
148
+ if (!moved.safe || !sameLock(snapshot, moved)) {
149
+ restoreQuarantinedLock(guardPath, quarantinePath, io);
150
+ return false;
151
+ }
152
+ io.unlinkSync(quarantinePath);
51
153
  return true;
154
+ } catch (error) {
155
+ return error?.code === "ENOENT";
156
+ }
157
+ }
158
+
159
+ function restoreQuarantinedLock(lockPath, quarantinePath, io) {
160
+ try {
161
+ io.linkSync(quarantinePath, lockPath);
162
+ } catch {
163
+ return;
164
+ }
165
+ try {
166
+ io.unlinkSync(quarantinePath);
167
+ } catch {}
168
+ }
169
+
170
+ function maybeRemoveStaleLock(lockPath, staleMs, nowMs, io, processAlive) {
171
+ if (!Number.isFinite(staleMs) || staleMs <= 0) return false;
172
+ try {
173
+ const first = readStaleLockSnapshot(lockPath, io);
174
+ if (!first.safe || nowMs - first.mtimeMs <= staleMs || processAlive(first.pid)) return false;
175
+ const guard = acquireReclaimGuard(lockPath, io);
176
+ if (!guard) return false;
177
+ try {
178
+ const current = readStaleLockSnapshot(lockPath, io);
179
+ if (!sameLock(first, current)
180
+ || nowMs - current.mtimeMs <= staleMs
181
+ || processAlive(current.pid)) {
182
+ return false;
183
+ }
184
+ const quarantinePath = `${lockPath}.stale-${randomUUID()}`;
185
+ try {
186
+ io.renameSync(lockPath, quarantinePath);
187
+ } catch (e) {
188
+ return e?.code === "ENOENT";
189
+ }
190
+ const moved = readStaleLockSnapshot(quarantinePath, io);
191
+ if (!moved.safe || !sameLock(current, moved)) {
192
+ restoreQuarantinedLock(lockPath, quarantinePath, io);
193
+ return false;
194
+ }
195
+ io.unlinkSync(quarantinePath);
196
+ return true;
197
+ } finally {
198
+ removeOwnedLock(guard.guardPath, guard.owner, io);
199
+ }
52
200
  } catch (e) {
53
201
  if (e?.code === "ENOENT") return true;
54
202
  return false;
@@ -60,28 +208,42 @@ export async function withFileLock(lockPath, fn, {
60
208
  staleMs = 15 * 60 * 1000,
61
209
  pollMs = 50,
62
210
  now = Date.now,
211
+ fsImpl = fs,
212
+ processAlive = processIsAlive,
63
213
  } = {}) {
64
214
  fs.mkdirSync(path.dirname(lockPath), { recursive: true });
65
215
  const started = now();
66
216
  const owner = randomUUID();
67
217
  let fd = null;
218
+ const guardPath = reclaimGuardPath(lockPath);
68
219
 
69
220
  while (fd === null) {
221
+ const beforeAcquire = now();
222
+ if (lockPathExists(guardPath, fsImpl)) {
223
+ maybeRemoveAbandonedGuard(guardPath, staleMs, beforeAcquire, fsImpl, processAlive);
224
+ if (lockPathExists(guardPath, fsImpl)) {
225
+ if (beforeAcquire - started >= timeoutMs) throw new Error(`Timed out waiting for lock: ${lockPath}`);
226
+ await sleep(Math.min(pollMs, Math.max(1, timeoutMs - (beforeAcquire - started))));
227
+ continue;
228
+ }
229
+ }
70
230
  try {
71
- const candidateFd = fs.openSync(lockPath, "wx", 0o600);
72
- try {
73
- fs.writeFileSync(candidateFd, JSON.stringify({ pid: process.pid, owner, created_at: new Date().toISOString() }));
74
- fd = candidateFd;
75
- } catch (e) {
76
- fs.closeSync(candidateFd);
77
- try { fs.unlinkSync(lockPath); } catch {}
78
- throw e;
231
+ fd = writeLockRecord(lockPath, {
232
+ pid: process.pid,
233
+ owner,
234
+ created_at: new Date().toISOString(),
235
+ }, fs);
236
+ if (lockPathExists(guardPath, fsImpl)) {
237
+ fs.closeSync(fd);
238
+ fd = null;
239
+ removeOwnedLock(lockPath, owner, fs);
240
+ continue;
79
241
  }
80
242
  break;
81
243
  } catch (e) {
82
244
  if (e?.code !== "EEXIST") throw e;
83
245
  const nowMs = now();
84
- if (maybeRemoveStaleLock(lockPath, staleMs, nowMs)) continue;
246
+ if (maybeRemoveStaleLock(lockPath, staleMs, nowMs, fsImpl, processAlive)) continue;
85
247
  if (nowMs - started >= timeoutMs) {
86
248
  throw new Error(`Timed out waiting for lock: ${lockPath}`);
87
249
  }
package/src/models.mjs CHANGED
@@ -117,6 +117,23 @@ export function modelIsResponsesOnly(model) {
117
117
  return hasResponses && !endpoints.includes("/chat/completions");
118
118
  }
119
119
 
120
+ export function resolveCopilotPriorityTierModel(model, serviceTier, models) {
121
+ const baseModel = String(model || "").trim();
122
+ if (baseModel !== "gpt-5.6-sol" || serviceTier !== "priority") return null;
123
+ const fastModelId = "gpt-5.6-sol-fast";
124
+ const fastModel = copilotModelData(models)
125
+ .find((entry) => String(entry?.id || "").trim() === fastModelId);
126
+ const vendor = String(fastModel?.vendor || fastModel?.owned_by || "").trim().toLowerCase();
127
+ const policy = String(fastModel?.policy?.state || "").trim().toLowerCase();
128
+ if (vendor !== "openai"
129
+ || policy !== "enabled"
130
+ || fastModel?.model_picker_enabled !== true
131
+ || !modelIsResponsesOnly(fastModel)) {
132
+ return null;
133
+ }
134
+ return fastModelId;
135
+ }
136
+
120
137
  function copilotModelData(models) {
121
138
  const data = Array.isArray(models) ? models : models?.data;
122
139
  return Array.isArray(data) ? data : [];