@cortexkit/opencode-magic-context 0.36.1 → 0.37.0

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 (125) hide show
  1. package/dist/config/index.d.ts.map +1 -1
  2. package/dist/config/project-security.d.ts +2 -1
  3. package/dist/config/project-security.d.ts.map +1 -1
  4. package/dist/config/schema/magic-context.d.ts +10 -0
  5. package/dist/config/schema/magic-context.d.ts.map +1 -1
  6. package/dist/{conflict-warning-hook-mt0ttc4x.js → conflict-warning-hook-an4fxhwf.js} +3 -3
  7. package/dist/{derive-budgets-88mxk2w1.js → derive-budgets-0f8xpyw2.js} +3 -2
  8. package/dist/features/magic-context/context-authority.d.ts.map +1 -1
  9. package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
  10. package/dist/features/magic-context/memory/embedding-openai.d.ts +3 -1
  11. package/dist/features/magic-context/memory/embedding-openai.d.ts.map +1 -1
  12. package/dist/features/magic-context/memory/project-identity.d.ts +6 -0
  13. package/dist/features/magic-context/memory/project-identity.d.ts.map +1 -1
  14. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  15. package/dist/features/magic-context/overflow-detection.d.ts.map +1 -1
  16. package/dist/features/magic-context/smart-notes/runner.d.ts.map +1 -1
  17. package/dist/features/magic-context/smart-notes/wake-plane.d.ts +21 -0
  18. package/dist/features/magic-context/smart-notes/wake-plane.d.ts.map +1 -0
  19. package/dist/features/magic-context/storage-db.d.ts +13 -2
  20. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  21. package/dist/features/magic-context/storage-meta-persisted.d.ts +22 -0
  22. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  23. package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
  24. package/dist/features/magic-context/storage-meta-shared.d.ts.map +1 -1
  25. package/dist/features/magic-context/storage-primers.d.ts +11 -0
  26. package/dist/features/magic-context/storage-primers.d.ts.map +1 -1
  27. package/dist/features/magic-context/storage-schema-helpers.d.ts.map +1 -1
  28. package/dist/features/magic-context/user-memory/storage-user-memory.d.ts +7 -0
  29. package/dist/features/magic-context/user-memory/storage-user-memory.d.ts.map +1 -1
  30. package/dist/features/magic-context/window-report-ledger.d.ts +70 -0
  31. package/dist/features/magic-context/window-report-ledger.d.ts.map +1 -0
  32. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  33. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  34. package/dist/hooks/magic-context/event-resolvers.d.ts +4 -0
  35. package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
  36. package/dist/hooks/magic-context/execute-status.d.ts +2 -1
  37. package/dist/hooks/magic-context/execute-status.d.ts.map +1 -1
  38. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  39. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  40. package/dist/hooks/magic-context/lkg-replay.d.ts.map +1 -1
  41. package/dist/hooks/magic-context/lkg-slot.d.ts +20 -0
  42. package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
  43. package/dist/hooks/magic-context/module-state-sync.d.ts +8 -0
  44. package/dist/hooks/magic-context/module-state-sync.d.ts.map +1 -1
  45. package/dist/hooks/magic-context/module-transport.d.ts.map +1 -1
  46. package/dist/hooks/magic-context/module-wire.d.ts +6 -1
  47. package/dist/hooks/magic-context/module-wire.d.ts.map +1 -1
  48. package/dist/hooks/magic-context/rust-mode-transform.d.ts +17 -0
  49. package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
  50. package/dist/hooks/magic-context/strip-content.d.ts +25 -0
  51. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  52. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  53. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +11 -3
  54. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  55. package/dist/hooks/magic-context/transform.d.ts +10 -0
  56. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  57. package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -1
  58. package/dist/{index-1skzp4dc.js → index-0ze9p983.js} +572 -92
  59. package/dist/{index-nsx2n9eg.js → index-4cz5nfxf.js} +3 -3
  60. package/dist/{index-f1tnb3vm.js → index-8pqfcghm.js} +21 -9
  61. package/dist/{index-zp6x6s6a.js → index-cae89eas.js} +25 -6
  62. package/dist/index-cgyfn1s2.js +53 -0
  63. package/dist/{index-3zqcdwqr.js → index-crke4z5j.js} +7 -4
  64. package/dist/{index-tsgs7td8.js → index-pck9vadr.js} +1 -1
  65. package/dist/index-q0t189ay.js +741 -0
  66. package/dist/{index-3tnkymva.js → index-sw8gnq34.js} +1 -1
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +1172 -287
  69. package/dist/plugin/hooks/create-session-hooks.d.ts +3 -0
  70. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  71. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  72. package/dist/{recomp-orchestrator-a0g9b52q.js → recomp-orchestrator-c3ff16k7.js} +7 -6
  73. package/dist/{safe-notification-target-c11fdda2.js → safe-notification-target-b693f9x3.js} +2 -2
  74. package/dist/{send-session-notification-bgtf4853.js → send-session-notification-dxske5zm.js} +3 -3
  75. package/dist/shared/conflict-detector.d.ts +63 -0
  76. package/dist/shared/conflict-detector.d.ts.map +1 -1
  77. package/dist/shared/harness-provider-map.d.ts +8 -0
  78. package/dist/shared/harness-provider-map.d.ts.map +1 -1
  79. package/dist/shared/logger.d.ts +8 -0
  80. package/dist/shared/logger.d.ts.map +1 -1
  81. package/dist/shared/models-dev-cache.d.ts +5 -0
  82. package/dist/shared/models-dev-cache.d.ts.map +1 -1
  83. package/dist/shared/prompt-surface-runtime.d.ts.map +1 -1
  84. package/dist/shared/prompt-surface.d.ts +2 -0
  85. package/dist/shared/prompt-surface.d.ts.map +1 -1
  86. package/dist/shared/rpc-client.d.ts.map +1 -1
  87. package/dist/shared/rpc-types.d.ts +14 -0
  88. package/dist/shared/rpc-types.d.ts.map +1 -1
  89. package/dist/shared/rpc-utils.d.ts +19 -9
  90. package/dist/shared/rpc-utils.d.ts.map +1 -1
  91. package/dist/shared/window-geometry.d.ts +89 -0
  92. package/dist/shared/window-geometry.d.ts.map +1 -0
  93. package/dist/{storage-meta-session-df9tstzr.js → storage-meta-session-mk4gsnke.js} +4 -3
  94. package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
  95. package/dist/tools/ctx-note/tools.d.ts.map +1 -1
  96. package/dist/tui/data/context-db.d.ts.map +1 -1
  97. package/dist/tui-compiled/data/context-db.d.ts.map +1 -1
  98. package/package.json +1 -1
  99. package/src/config/index.ts +2 -0
  100. package/src/config/latch-permanence-guard.test.ts +218 -0
  101. package/src/config/project-security.ts +10 -1
  102. package/src/config/schema/magic-context.ts +10 -0
  103. package/src/shared/__fixtures__/window-overlay.v1.json +337 -0
  104. package/src/shared/conflict-detector.test.ts +176 -1
  105. package/src/shared/conflict-detector.ts +126 -3
  106. package/src/shared/harness-provider-map.test.ts +20 -0
  107. package/src/shared/harness-provider-map.ts +19 -0
  108. package/src/shared/logger.test.ts +119 -0
  109. package/src/shared/logger.ts +33 -15
  110. package/src/shared/models-dev-cache.test.ts +17 -0
  111. package/src/shared/models-dev-cache.ts +58 -6
  112. package/src/shared/prompt-surface-runtime.ts +5 -3
  113. package/src/shared/prompt-surface.test.ts +38 -0
  114. package/src/shared/prompt-surface.ts +15 -2
  115. package/src/shared/rpc-client.ts +4 -2
  116. package/src/shared/rpc-types.ts +14 -0
  117. package/src/shared/rpc-utils.test.ts +137 -13
  118. package/src/shared/rpc-utils.ts +144 -31
  119. package/src/shared/window-geometry.test.ts +309 -0
  120. package/src/shared/window-geometry.ts +629 -0
  121. package/src/tui/data/context-db.ts +5 -0
  122. package/src/tui/index.tsx +22 -0
  123. package/src/tui-compiled/data/context-db.ts +5 -0
  124. package/src/tui-compiled/index.tsx +319 -255
  125. package/dist/index-wgvxkqpx.js +0 -342
@@ -50,13 +50,22 @@ export function legacyRpcPortFilePath(storageDir: string, directory: string): st
50
50
  return join(rpcPortDir(storageDir, directory), "port");
51
51
  }
52
52
 
53
- export function isPidAlive(pid: number): boolean {
54
- if (!Number.isInteger(pid) || pid <= 0) return false;
53
+ export type PidLiveness = "alive" | "dead" | "inconclusive";
54
+
55
+ /**
56
+ * Check whether the platform confirms a PID is live without treating a denied
57
+ * probe as confirmation. Windows uses tasklist because MSYS2/Cygwin ps does not
58
+ * support the options used by the Unix probe. Sandboxes commonly reject
59
+ * `kill(pid, 0)` with EPERM even when the PID does not exist outside their view.
60
+ */
61
+ export function isPidAlive(pid: number): PidLiveness {
62
+ if (!Number.isInteger(pid) || pid <= 0) return "dead";
63
+ if (rpcIdentityPlatform === "win32") return readWindowsProcess(pid).state;
55
64
  try {
56
- process.kill(pid, 0);
57
- return true;
58
- } catch (err) {
59
- return (err as NodeJS.ErrnoException).code === "EPERM";
65
+ rpcIdentityProcessKill(pid, 0);
66
+ return "alive";
67
+ } catch (error) {
68
+ return (error as NodeJS.ErrnoException).code === "ESRCH" ? "dead" : "inconclusive";
60
69
  }
61
70
  }
62
71
 
@@ -64,9 +73,12 @@ const RPC_IDENTITY_SKEW_TOLERANCE_MS = 120_000;
64
73
  const LINUX_CLOCK_TICKS_PER_SECOND = 100;
65
74
  const PS_PROBE_TIMEOUT_MS = 1_000;
66
75
  const OPEN_CODE_COMMAND_MARKERS = ["opencode", "node", "bun", "electron"];
76
+ const TASKLIST_NO_TASKS_PATTERN =
77
+ /^INFO:\s+No tasks are running which match the specified criteria\.?$/im;
67
78
 
68
79
  let rpcIdentityReadFileSync: typeof readFileSync = readFileSync;
69
80
  let rpcIdentityExecFileSync: typeof execFileSync = execFileSync;
81
+ let rpcIdentityProcessKill: typeof process.kill = process.kill;
70
82
  let rpcProcessListExecFileSync: typeof execFileSync = execFileSync;
71
83
  let rpcProcessListTestOverride = false;
72
84
  let rpcIdentityPlatform: NodeJS.Platform = process.platform;
@@ -116,6 +128,7 @@ function readPsProcessStartTime(pid: number): number | null {
116
128
  const output = rpcIdentityExecFileSync("ps", ["-p", String(pid), "-o", "lstart="], {
117
129
  encoding: "utf8",
118
130
  timeout: PS_PROBE_TIMEOUT_MS,
131
+ stdio: ["ignore", "pipe", "pipe"],
119
132
  });
120
133
  const processStartTime = Date.parse(String(output).trim());
121
134
  return Number.isFinite(processStartTime) ? processStartTime : null;
@@ -124,6 +137,72 @@ function readPsProcessStartTime(pid: number): number | null {
124
137
  }
125
138
  }
126
139
 
140
+ interface ProcessListEntry {
141
+ pid: number;
142
+ command: string;
143
+ }
144
+
145
+ function parseCsvLine(line: string): string[] | null {
146
+ const fields: string[] = [];
147
+ let field = "";
148
+ let quoted = false;
149
+ for (let index = 0; index < line.length; index += 1) {
150
+ const character = line[index];
151
+ if (character === '"') {
152
+ if (quoted && line[index + 1] === '"') {
153
+ field += '"';
154
+ index += 1;
155
+ } else {
156
+ quoted = !quoted;
157
+ }
158
+ } else if (character === "," && !quoted) {
159
+ fields.push(field);
160
+ field = "";
161
+ } else {
162
+ field += character;
163
+ }
164
+ }
165
+ if (quoted) return null;
166
+ fields.push(field);
167
+ return fields;
168
+ }
169
+
170
+ function parseTasklistOutput(output: string): ProcessListEntry[] | null {
171
+ const entries: ProcessListEntry[] = [];
172
+ let sawHeader = false;
173
+ for (const rawLine of output.split(/\r?\n/)) {
174
+ const line = rawLine.trim();
175
+ if (!line) continue;
176
+ if (TASKLIST_NO_TASKS_PATTERN.test(line)) return [];
177
+ const fields = parseCsvLine(line);
178
+ if (!fields) continue;
179
+ if (fields[1]?.trim().toLowerCase() === "pid") {
180
+ sawHeader = true;
181
+ continue;
182
+ }
183
+ const pid = Number(fields[1]);
184
+ if (!Number.isInteger(pid) || pid <= 0 || !fields[0]) continue;
185
+ entries.push({ pid, command: fields[0] });
186
+ }
187
+ return entries.length > 0 || sawHeader ? entries : null;
188
+ }
189
+
190
+ function readWindowsProcess(pid: number): { state: PidLiveness; command?: string } {
191
+ try {
192
+ const output = rpcIdentityExecFileSync("tasklist", ["/FO", "CSV", "/FI", `PID eq ${pid}`], {
193
+ encoding: "utf8",
194
+ timeout: PS_PROBE_TIMEOUT_MS,
195
+ stdio: ["ignore", "pipe", "pipe"],
196
+ });
197
+ const entries = parseTasklistOutput(String(output));
198
+ if (entries === null) return { state: "inconclusive" };
199
+ const process = entries.find((entry) => entry.pid === pid);
200
+ return process ? { state: "alive", command: process.command } : { state: "dead" };
201
+ } catch {
202
+ return { state: "inconclusive" };
203
+ }
204
+ }
205
+
127
206
  function readLinuxProcessCommand(pid: number): string | null {
128
207
  try {
129
208
  return String(rpcIdentityReadFileSync(`/proc/${pid}/cmdline`, "utf8"));
@@ -137,6 +216,7 @@ function readPsProcessCommand(pid: number): string | null {
137
216
  const output = rpcIdentityExecFileSync("ps", ["-p", String(pid), "-o", "command="], {
138
217
  encoding: "utf8",
139
218
  timeout: PS_PROBE_TIMEOUT_MS,
219
+ stdio: ["ignore", "pipe", "pipe"],
140
220
  });
141
221
  return String(output);
142
222
  } catch {
@@ -153,40 +233,51 @@ function commandLooksLikeOpenCode(command: string): boolean {
153
233
  * Verify that a live PID still belongs to the process that wrote a port record.
154
234
  *
155
235
  * A PID can be reused after its original process exits. On Linux, procfs gives
156
- * us a process start time without spawning a helper; other platforms use `ps`
157
- * only on this cold database-open guard path. Legacy records without a start
158
- * time use a weaker command-name check, and every probe failure stays live so
159
- * this identity check cannot weaken the migration guard on uncertainty.
236
+ * us a process start time without spawning a helper; macOS and other Unix-like
237
+ * platforms use `ps`, while Windows uses `tasklist`, only on this cold
238
+ * database-open guard path. Legacy records without a start time use a weaker
239
+ * command-name check. A failed filesystem or process probe is inconclusive,
240
+ * not proof that this port record still belongs to OpenCode.
160
241
  */
161
- export function isPidIdentityPlausible(record: RpcPortFileRecord): boolean {
162
- if (!Number.isInteger(record.pid) || record.pid <= 0) return false;
242
+ export type PidIdentityPlausibility = "plausible" | "implausible" | "inconclusive";
243
+
244
+ export function isPidIdentityPlausible(record: RpcPortFileRecord): PidIdentityPlausibility {
245
+ if (!Number.isInteger(record.pid) || record.pid <= 0) return "implausible";
163
246
 
164
247
  if (Number.isFinite(record.started_at) && record.started_at > 0) {
165
248
  const processStartTime =
166
249
  rpcIdentityPlatform === "linux"
167
250
  ? readLinuxProcessStartTime(record.pid)
168
- : readPsProcessStartTime(record.pid);
169
- if (processStartTime === null) return true;
170
- return processStartTime <= record.started_at + RPC_IDENTITY_SKEW_TOLERANCE_MS;
251
+ : rpcIdentityPlatform === "win32"
252
+ ? null
253
+ : readPsProcessStartTime(record.pid);
254
+ if (processStartTime === null) return "inconclusive";
255
+ return processStartTime <= record.started_at + RPC_IDENTITY_SKEW_TOLERANCE_MS
256
+ ? "plausible"
257
+ : "implausible";
171
258
  }
172
259
 
173
260
  const command =
174
261
  rpcIdentityPlatform === "linux"
175
262
  ? readLinuxProcessCommand(record.pid)
176
- : readPsProcessCommand(record.pid);
177
- if (command === null) return true;
178
- return commandLooksLikeOpenCode(command);
263
+ : rpcIdentityPlatform === "win32"
264
+ ? (readWindowsProcess(record.pid).command ?? null)
265
+ : readPsProcessCommand(record.pid);
266
+ if (command === null) return "inconclusive";
267
+ return commandLooksLikeOpenCode(command) ? "plausible" : "implausible";
179
268
  }
180
269
 
181
270
  export function __setRpcIdentityTestHooks(hooks: {
182
271
  readFileSync?: typeof readFileSync;
183
272
  execFileSync?: typeof execFileSync;
273
+ processKill?: typeof process.kill;
184
274
  processListExecFileSync?: typeof execFileSync;
185
275
  platform?: NodeJS.Platform;
186
276
  nowMs?: () => number;
187
277
  }): void {
188
278
  rpcIdentityReadFileSync = hooks.readFileSync ?? readFileSync;
189
279
  rpcIdentityExecFileSync = hooks.execFileSync ?? execFileSync;
280
+ rpcIdentityProcessKill = hooks.processKill ?? process.kill;
190
281
  rpcProcessListExecFileSync = hooks.processListExecFileSync ?? execFileSync;
191
282
  rpcProcessListTestOverride = hooks.processListExecFileSync !== undefined;
192
283
  rpcIdentityPlatform = hooks.platform ?? process.platform;
@@ -196,6 +287,7 @@ export function __setRpcIdentityTestHooks(hooks: {
196
287
  export function __resetRpcIdentityTestHooks(): void {
197
288
  rpcIdentityReadFileSync = readFileSync;
198
289
  rpcIdentityExecFileSync = execFileSync;
290
+ rpcIdentityProcessKill = process.kill;
199
291
  rpcProcessListExecFileSync = execFileSync;
200
292
  rpcProcessListTestOverride = false;
201
293
  rpcIdentityPlatform = process.platform;
@@ -228,28 +320,49 @@ export interface PiProcessDiscovery {
228
320
 
229
321
  /**
230
322
  * Inspect Pi/OMP processes without converting a failed process-list probe into
231
- * false evidence that no harness is running. Destructive maintenance callers
232
- * use the unreadable state to fail closed; ordinary migration guards retain
233
- * their historical best-effort process list through discoverLivePiProcessIds().
323
+ * false evidence that no harness is running. Callers choose their own policy
324
+ * for the unreadable state: destructive maintenance can fail closed, while a
325
+ * migration guard can proceed after reporting that no live Pi process was confirmed.
234
326
  */
235
327
  export function inspectLivePiProcesses(): PiProcessDiscovery {
236
328
  if (process.env.NODE_ENV === "test" && !rpcProcessListTestOverride) {
237
329
  return { state: "known", processIds: [] };
238
330
  }
239
331
  try {
332
+ const isWindows = rpcIdentityPlatform === "win32";
240
333
  const output = String(
241
- rpcProcessListExecFileSync("ps", ["-axo", "pid=,command="], {
242
- encoding: "utf8",
243
- timeout: PS_PROBE_TIMEOUT_MS,
244
- }),
334
+ rpcProcessListExecFileSync(
335
+ isWindows ? "tasklist" : "ps",
336
+ isWindows ? ["/FO", "CSV"] : ["-axo", "pid=,command="],
337
+ {
338
+ encoding: "utf8",
339
+ timeout: PS_PROBE_TIMEOUT_MS,
340
+ stdio: ["ignore", "pipe", "pipe"],
341
+ },
342
+ ),
245
343
  );
246
344
  const pids = new Set<number>();
247
- for (const line of output.split(/\r?\n/)) {
248
- const match = /^\s*(\d+)\s+(.+)$/.exec(line);
249
- if (!match) continue;
250
- const pid = Number(match[1]);
251
- if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid) continue;
252
- if (commandLooksLikePi(match[2])) pids.add(pid);
345
+ if (isWindows) {
346
+ const entries = parseTasklistOutput(output);
347
+ if (entries === null) {
348
+ return {
349
+ state: "unreadable",
350
+ processIds: [],
351
+ error: "tasklist output unavailable",
352
+ };
353
+ }
354
+ for (const entry of entries) {
355
+ if (entry.pid === process.pid) continue;
356
+ if (commandLooksLikePi(entry.command)) pids.add(entry.pid);
357
+ }
358
+ } else {
359
+ for (const line of output.split(/\r?\n/)) {
360
+ const match = /^\s*(\d+)\s+(.+)$/.exec(line);
361
+ if (!match) continue;
362
+ const pid = Number(match[1]);
363
+ if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid) continue;
364
+ if (commandLooksLikePi(match[2])) pids.add(pid);
365
+ }
253
366
  }
254
367
  return { state: "known", processIds: [...pids].sort((left, right) => left - right) };
255
368
  } catch (error) {
@@ -0,0 +1,309 @@
1
+ import { afterEach, describe, expect, test } from "bun:test";
2
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { resolveLimit } from "./models-dev-cache";
6
+ import {
7
+ deriveWindowGeometry,
8
+ formatWindowDerivationLine,
9
+ parseWindowOverlay,
10
+ placeholderFilteredOutput,
11
+ readWindowOverlayFile,
12
+ resolveWindowOverlayFacts,
13
+ scalarizeFact,
14
+ type WindowOverlay,
15
+ type WindowOverlayFact,
16
+ } from "./window-geometry";
17
+
18
+ const fixture = JSON.parse(
19
+ readFileSync(new URL("./__fixtures__/window-overlay.v1.json", import.meta.url), "utf8"),
20
+ ) as unknown;
21
+ const parsedFixture = parseWindowOverlay(fixture).overlay as WindowOverlay;
22
+ const tempDirs: string[] = [];
23
+
24
+ function fact(
25
+ value: WindowOverlayFact["value"],
26
+ overrides: Partial<WindowOverlayFact> = {},
27
+ ): WindowOverlayFact {
28
+ return {
29
+ value,
30
+ grade: "measured",
31
+ units: "provider",
32
+ boundary: "Observed",
33
+ source_ref: "test",
34
+ observed_at: "2026-08-13T00:00:00Z",
35
+ ...overrides,
36
+ };
37
+ }
38
+
39
+ function overlay(
40
+ provider: string,
41
+ model: string,
42
+ facts: Record<string, WindowOverlayFact>,
43
+ ): WindowOverlay {
44
+ return {
45
+ schema: "fusiform-window-overlay/v1",
46
+ generated_at: "2026-08-13T00:00:00Z",
47
+ minted_provider_ids: [],
48
+ cells: [{ provider_id: provider, model_id: model, facts }],
49
+ };
50
+ }
51
+
52
+ afterEach(() => {
53
+ for (const path of tempDirs.splice(0)) rmSync(path, { recursive: true, force: true });
54
+ });
55
+
56
+ describe("Fusiform overlay v1", () => {
57
+ test("scalarizes every tagged union conservatively", () => {
58
+ expect(scalarizeFact({ kind: "stated", value: 272_000 })).toBe(272_000);
59
+ expect(scalarizeFact({ kind: "bracket", at_least: 340_000, below: 372_001 })).toBe(340_000);
60
+ expect(scalarizeFact({ kind: "bracket", below: 372_001 })).toBeUndefined();
61
+ expect(scalarizeFact({ kind: "unknown", why: "never_measured" })).toBeUndefined();
62
+ });
63
+
64
+ test("preserves a half-open bracket and derives exactly from at_least", () => {
65
+ const halfOpen = parseWindowOverlay({
66
+ schema: "fusiform-window-overlay/v1",
67
+ generated_at: "2026-08-13T00:00:00Z",
68
+ minted_provider_ids: [],
69
+ cells: [
70
+ {
71
+ provider_id: "openai-chatgpt-oauth",
72
+ model_id: "gpt-5.6-sol",
73
+ facts: {
74
+ "window.advertised": fact({ kind: "stated", value: 272_000 }),
75
+ "window.enforced": fact({ kind: "bracket", at_least: 340_000 }),
76
+ },
77
+ },
78
+ ],
79
+ }).overlay;
80
+ const parsedValue = halfOpen?.cells[0]?.facts["window.enforced"]?.value;
81
+ expect(parsedValue).toEqual({ kind: "bracket", at_least: 340_000 });
82
+ expect(parsedValue && "below" in parsedValue).toBe(false);
83
+ const halfOpenResult = deriveWindowGeometry(
84
+ "openai-chatgpt-oauth",
85
+ "gpt-5.6-sol",
86
+ { context: 272_000, output: 128_000 },
87
+ {
88
+ overlay: resolveWindowOverlayFacts("openai-chatgpt-oauth", "gpt-5.6-sol", halfOpen),
89
+ },
90
+ );
91
+ const statedResult = deriveWindowGeometry(
92
+ "openai-chatgpt-oauth",
93
+ "gpt-5.6-sol",
94
+ { context: 272_000, output: 128_000 },
95
+ {
96
+ overlay: resolveWindowOverlayFacts(
97
+ "openai-chatgpt-oauth",
98
+ "gpt-5.6-sol",
99
+ overlay("openai-chatgpt-oauth", "gpt-5.6-sol", {
100
+ "window.enforced": fact({ kind: "stated", value: 340_000 }),
101
+ }),
102
+ ),
103
+ },
104
+ );
105
+ expect(halfOpenResult).toEqual(statedResult);
106
+ });
107
+
108
+ test("specific cells override wildcard facts per fact", () => {
109
+ const data: WindowOverlay = {
110
+ schema: "fusiform-window-overlay/v1",
111
+ generated_at: "2026-08-13T00:00:00Z",
112
+ minted_provider_ids: [],
113
+ cells: [
114
+ {
115
+ provider_id: "ollama-cloud",
116
+ model_id: "*",
117
+ facts: {
118
+ "output.enforced": fact({ kind: "stated", value: 65_536 }),
119
+ geometry: fact({ kind: "stated", value: "shared_upfront" }),
120
+ },
121
+ },
122
+ {
123
+ provider_id: "ollama-cloud",
124
+ model_id: "deepseek-v4",
125
+ facts: {
126
+ "output.enforced": fact({ kind: "stated", value: 32_000 }),
127
+ },
128
+ },
129
+ ],
130
+ };
131
+ const facts = resolveWindowOverlayFacts("ollama-cloud", "deepseek-v4", data)?.facts;
132
+ expect(scalarizeFact(facts?.["output.enforced"]?.value as never)).toBe(32_000);
133
+ expect(facts?.geometry?.value).toEqual({ kind: "stated", value: "shared_upfront" });
134
+ });
135
+
136
+ test("refuses an unrecognized schema before considering familiar cells", () => {
137
+ const v2 = {
138
+ ...(fixture as Record<string, unknown>),
139
+ schema: "fusiform-window-overlay/v2",
140
+ };
141
+ const parsed = parseWindowOverlay(v2);
142
+ expect(parsed.overlay).toBeUndefined();
143
+ expect(parsed.refusal).toContain("unrecognized schema");
144
+ });
145
+
146
+ test("skips bad cells once and keeps valid cells", () => {
147
+ const badCell = {
148
+ provider_id: "bad",
149
+ model_id: "model",
150
+ facts: { "window.enforced": { value: { kind: "stated", value: 1_000_000 } } },
151
+ };
152
+ const parsed = parseWindowOverlay({
153
+ ...(fixture as Record<string, unknown>),
154
+ cells: [badCell, ...(fixture as { cells: unknown[] }).cells],
155
+ });
156
+ expect(parsed.badCells).toBe(1);
157
+ expect(parsed.overlay?.cells.length).toBe(parsedFixture.cells.length);
158
+ });
159
+
160
+ test("a missing file is silent and a bad file logs one summary", () => {
161
+ const dir = mkdtempSync(join(tmpdir(), "window-overlay-test-"));
162
+ tempDirs.push(dir);
163
+ const logs: string[] = [];
164
+ expect(
165
+ readWindowOverlayFile(join(dir, "missing.json"), (message) => logs.push(message)),
166
+ ).toBeUndefined();
167
+ expect(logs).toEqual([]);
168
+ const path = join(dir, "bad.json");
169
+ writeFileSync(path, JSON.stringify({ ...(fixture as object), schema: "v2" }));
170
+ expect(readWindowOverlayFile(path, (message) => logs.push(message))).toBeUndefined();
171
+ expect(logs).toHaveLength(1);
172
+ });
173
+ });
174
+
175
+ describe("window geometry", () => {
176
+ test("filters placeholders independently in both directions", () => {
177
+ expect(placeholderFilteredOutput(0, 1_000_000)).toBeUndefined();
178
+ expect(placeholderFilteredOutput(1_000_000, 1_000_000)).toBeUndefined();
179
+ expect(placeholderFilteredOutput(1_000_001, 1_000_000)).toBeUndefined();
180
+ expect(placeholderFilteredOutput(65_536, 1_000_000)).toBe(65_536);
181
+ expect(
182
+ deriveWindowGeometry("xai", "mixed", {
183
+ context: 500_000,
184
+ input: 300_000,
185
+ output: 500_000,
186
+ })?.usableSoft,
187
+ ).toBe(300_000);
188
+ });
189
+
190
+ test("fixture table yields the 65% execution bases from the spec", () => {
191
+ const cases = [
192
+ ["openai-chatgpt-oauth", "gpt-5.6", 272_000, 128_000, 200_200],
193
+ ["anthropic", "claude-opus-5", 1_000_000, 128_000, 629_200],
194
+ // Producer batch downgraded google's geometry to considered-unknown
195
+ // (Google's docs contradict the separate-quota reading), so the
196
+ // derivation demotes to shared_upfront and reserves OpenCode's
197
+ // actual request cap B = min(65_536, 32_000):
198
+ // floor(0.65 * (1_048_576 - 32_000)) — not the no-reserve 681_574.
199
+ ["google", "gemini-3.5-flash", 1_048_576, 65_536, 660_774],
200
+ ["xai", "grok-4.6", 500_000, 500_000, 304_200],
201
+ ["ollama-cloud", "deepseek-v4-flash", 1_048_576, 384_000, 660_774],
202
+ ["moonshotai", "kimi-k3", 1_048_576, 1_048_576, 660_774],
203
+ ] as const;
204
+ for (const [provider, model, context, output, expectedExecute] of cases) {
205
+ const result = deriveWindowGeometry(
206
+ provider,
207
+ model,
208
+ { context, output },
209
+ {
210
+ overlay: resolveWindowOverlayFacts(provider, model, parsedFixture),
211
+ },
212
+ );
213
+ expect(Math.floor((result?.usableSoft ?? 0) * 0.65)).toBe(expectedExecute);
214
+ }
215
+ });
216
+
217
+ test("grok derives from geometry, window, and default output without an enforced output", () => {
218
+ const facts = resolveWindowOverlayFacts("xai", "grok-4.6", parsedFixture);
219
+ expect(facts?.facts["output.enforced"]).toBeUndefined();
220
+ const result = deriveWindowGeometry(
221
+ "xai",
222
+ "grok-4.6",
223
+ { context: 500_000, output: 500_000 },
224
+ { overlay: facts },
225
+ );
226
+ expect(result?.geometry).toBe("shared_truncating");
227
+ expect(result?.usableSoft).toBe(468_000);
228
+ });
229
+
230
+ test("kimi reserves its default output instead of placeholder advertised output", () => {
231
+ const result = deriveWindowGeometry(
232
+ "moonshotai",
233
+ "kimi-k3",
234
+ { context: 1_048_576, output: 1_048_576 },
235
+ {
236
+ overlay: resolveWindowOverlayFacts("moonshotai", "kimi-k3", parsedFixture),
237
+ },
238
+ );
239
+ expect(result?.derivation.reserve).toBe(32_000);
240
+ expect(result?.usableSoft).toBe(1_016_576);
241
+ });
242
+
243
+ test("provider hook wins over overlay, while overlay beats catalog", () => {
244
+ const data = overlay("provider", "model", {
245
+ "window.enforced": fact({ kind: "stated", value: 300_000 }),
246
+ "output.enforced": fact({ kind: "stated", value: 20_000 }),
247
+ });
248
+ const overlayOnly = deriveWindowGeometry(
249
+ "provider",
250
+ "model",
251
+ { context: 200_000, output: 10_000 },
252
+ { overlay: resolveWindowOverlayFacts("provider", "model", data) },
253
+ );
254
+ const hooked = deriveWindowGeometry(
255
+ "provider",
256
+ "model",
257
+ { context: 200_000, output: 10_000 },
258
+ {
259
+ overlay: resolveWindowOverlayFacts("provider", "model", data),
260
+ providerLimit: { context: 400_000, output: 30_000 },
261
+ },
262
+ );
263
+ expect(overlayOnly?.derivation.window).toBe(300_000);
264
+ expect(hooked?.derivation.window).toBe(400_000);
265
+ expect(hooked?.derivation.reserve).toBe(30_000);
266
+ });
267
+
268
+ test("clamps an inversion and logs it", () => {
269
+ const logs: string[] = [];
270
+ const result = deriveWindowGeometry(
271
+ "provider",
272
+ "model",
273
+ { context: 200_000, input: 190_000, output: 10_000 },
274
+ {
275
+ providerLimit: { context: 192_000, input: 190_000, output: 10_000 },
276
+ log: (message) => logs.push(message),
277
+ },
278
+ );
279
+ expect(result?.usableHard).toBe(result?.usableSoft);
280
+ expect(logs).toHaveLength(1);
281
+ expect(logs[0]).toContain("clamped");
282
+ });
283
+
284
+ test("keeps no-overlay usableSoft byte-identical to legacy resolveLimit", () => {
285
+ const corpus = [
286
+ ["openai", "real", { context: 200_000, output: 32_000 }],
287
+ ["openai", "zero", { context: 200_000, output: 0 }],
288
+ ["xai", "placeholder", { context: 500_000, output: 500_000 }],
289
+ ["google", "separate", { context: 1_000_000, output: 65_536 }],
290
+ ["anthropic", "input", { context: 1_000_000, input: 900_000, output: 128_000 }],
291
+ ] as const;
292
+ for (const [provider, model, limit] of corpus) {
293
+ const result = deriveWindowGeometry(provider, model, limit);
294
+ expect(result?.usableSoft).toBe(resolveLimit(limit, provider, model));
295
+ }
296
+ });
297
+
298
+ test("display percentage uses the same usableSoft scheduler base", () => {
299
+ const result = deriveWindowGeometry("openai", "model", {
300
+ context: 204_000,
301
+ output: 30_600,
302
+ }) as NonNullable<ReturnType<typeof deriveWindowGeometry>>;
303
+ const input = 105_900;
304
+ const schedulerPercentage = (input / result.usableSoft) * 100;
305
+ expect(formatWindowDerivationLine(input, result)).toContain(
306
+ `(${schedulerPercentage.toFixed(1)}%)`,
307
+ );
308
+ });
309
+ });