codex-copilot-dx 0.6.13 → 0.6.15

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.
@@ -0,0 +1,51 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ const DISPOSITIONS = new Set(["native", "relay", "reject"]);
4
+ const MAX_AFFINITY_MODEL_BYTES = 512;
5
+
6
+ function requiredString(value, name) {
7
+ const normalized = String(value || "").trim();
8
+ if (!normalized) throw new Error(`Route plan ${name} is required`);
9
+ return normalized;
10
+ }
11
+
12
+ export function createRoutePlan({
13
+ disposition = "relay",
14
+ model,
15
+ origin,
16
+ profile,
17
+ protocol,
18
+ surface,
19
+ } = {}) {
20
+ if (!DISPOSITIONS.has(disposition)) {
21
+ throw new Error(`Unsupported route disposition: ${disposition}`);
22
+ }
23
+ return Object.freeze({
24
+ disposition,
25
+ origin: requiredString(origin, "origin"),
26
+ profile: requiredString(profile, "profile"),
27
+ protocol: requiredString(protocol, "protocol"),
28
+ model: requiredString(model, "model"),
29
+ surface: requiredString(surface, "surface"),
30
+ });
31
+ }
32
+
33
+ export function routePlanAffinity(plan) {
34
+ if (!plan || typeof plan !== "object") return null;
35
+ const { origin, profile, protocol, model } = plan;
36
+ if (![origin, profile, protocol, model].every((value) => typeof value === "string" && value.length > 0)) {
37
+ return null;
38
+ }
39
+ const boundedModel = Buffer.byteLength(model) <= MAX_AFFINITY_MODEL_BYTES
40
+ ? model
41
+ : `sha256:${createHash("sha256").update(model).digest("hex")}`;
42
+ return Object.freeze({ origin, profile, protocol, model: boundedModel });
43
+ }
44
+
45
+ export function sameRoutePlanAffinity(left, right) {
46
+ return Boolean(left && right)
47
+ && left.origin === right.origin
48
+ && left.profile === right.profile
49
+ && left.protocol === right.protocol
50
+ && left.model === right.model;
51
+ }
package/src/startup.mjs CHANGED
@@ -2,9 +2,15 @@ export async function initializeModelRegistry({
2
2
  loadCached,
3
3
  currentModelDefs,
4
4
  refresh,
5
+ onBackgroundError = () => {},
5
6
  }) {
6
- if (loadCached()) {
7
- return { modelDefs: currentModelDefs(), source: "cache", backgroundRefresh: null };
7
+ const cached = loadCached();
8
+ const loaded = cached === true || cached?.loaded === true;
9
+ if (loaded) {
10
+ const backgroundRefresh = cached?.stale === true
11
+ ? runInBackground(refresh, onBackgroundError)
12
+ : null;
13
+ return { modelDefs: currentModelDefs(), source: "cache", backgroundRefresh };
8
14
  }
9
15
 
10
16
  return { modelDefs: await refresh(), source: "live", backgroundRefresh: null };
package/src/stream.mjs CHANGED
@@ -4,33 +4,47 @@ import { loadRuntimeConfig } from "./runtime-config.mjs";
4
4
 
5
5
  const MAX_SSE_BUFFER_BYTES = loadRuntimeConfig().maxSseBufferBytes;
6
6
 
7
- function assertSseBufferLimit(buffer, maxBufferBytes) {
8
- if (Buffer.byteLength(buffer) > maxBufferBytes) {
7
+ function appendLineFragment(parts, fragment, currentBytes, maxBufferBytes) {
8
+ if (!fragment) return currentBytes;
9
+ const nextBytes = currentBytes + Buffer.byteLength(fragment);
10
+ if (nextBytes > maxBufferBytes) {
9
11
  throw new Error(`Upstream SSE buffer exceeds ${maxBufferBytes} bytes`);
10
12
  }
13
+ parts.push(fragment);
14
+ return nextBytes;
11
15
  }
12
16
 
13
17
  export async function* webStreamLines(response, { onChunk, maxBufferBytes = MAX_SSE_BUFFER_BYTES } = {}) {
14
18
  const reader = response.body.getReader();
15
19
  const decoder = new TextDecoder();
16
- let buf = "";
20
+ let lineParts = [];
21
+ let lineBytes = 0;
17
22
  try {
18
23
  while (true) {
19
24
  const { done, value } = await reader.read();
20
25
  if (done) break;
21
26
  onChunk?.(value);
22
- buf += decoder.decode(value, { stream: true });
23
- const lines = buf.split("\n");
24
- buf = lines.pop();
25
- assertSseBufferLimit(buf, maxBufferBytes);
26
- for (const line of lines) {
27
- assertSseBufferLimit(line, maxBufferBytes);
28
- yield line.replace(/\r$/, "");
27
+ const decoded = decoder.decode(value, { stream: true });
28
+ let start = 0;
29
+ while (start < decoded.length) {
30
+ const newline = decoded.indexOf("\n", start);
31
+ if (newline === -1) {
32
+ lineBytes = appendLineFragment(lineParts, decoded.slice(start), lineBytes, maxBufferBytes);
33
+ break;
34
+ }
35
+ lineBytes = appendLineFragment(lineParts, decoded.slice(start, newline), lineBytes, maxBufferBytes);
36
+ const line = lineParts.length > 1 ? lineParts.join("") : (lineParts[0] || "");
37
+ yield line.endsWith("\r") ? line.slice(0, -1) : line;
38
+ lineParts = [];
39
+ lineBytes = 0;
40
+ start = newline + 1;
29
41
  }
30
42
  }
31
- buf += decoder.decode();
32
- assertSseBufferLimit(buf, maxBufferBytes);
33
- if (buf.length > 0) yield buf.replace(/\r$/, "");
43
+ lineBytes = appendLineFragment(lineParts, decoder.decode(), lineBytes, maxBufferBytes);
44
+ if (lineParts.length > 0) {
45
+ const line = lineParts.length > 1 ? lineParts.join("") : lineParts[0];
46
+ yield line.endsWith("\r") ? line.slice(0, -1) : line;
47
+ }
34
48
  } finally {
35
49
  await reader.cancel().catch(() => {});
36
50
  reader.releaseLock();