@cortexkit/opencode-magic-context 0.36.0 → 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 (192) 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-dw9fs934.js → conflict-warning-hook-an4fxhwf.js} +4 -4
  7. package/dist/{derive-budgets-88mxk2w1.js → derive-budgets-0f8xpyw2.js} +3 -2
  8. package/dist/features/magic-context/context-authority.d.ts +12 -0
  9. package/dist/features/magic-context/context-authority.d.ts.map +1 -1
  10. package/dist/features/magic-context/dreamer/classify.d.ts +2 -0
  11. package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
  12. package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts +2 -0
  13. package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
  14. package/dist/features/magic-context/dreamer/lease.d.ts +10 -3
  15. package/dist/features/magic-context/dreamer/lease.d.ts.map +1 -1
  16. package/dist/features/magic-context/dreamer/map-memories.d.ts +2 -0
  17. package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
  18. package/dist/features/magic-context/dreamer/module-apply.d.ts +5 -0
  19. package/dist/features/magic-context/dreamer/module-apply.d.ts.map +1 -1
  20. package/dist/features/magic-context/dreamer/promote-primers.d.ts +2 -0
  21. package/dist/features/magic-context/dreamer/promote-primers.d.ts.map +1 -1
  22. package/dist/features/magic-context/dreamer/refresh-primers.d.ts +2 -0
  23. package/dist/features/magic-context/dreamer/refresh-primers.d.ts.map +1 -1
  24. package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
  25. package/dist/features/magic-context/dreamer/task-scheduler.d.ts +5 -2
  26. package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
  27. package/dist/features/magic-context/dreamer/verify.d.ts +2 -0
  28. package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
  29. package/dist/features/magic-context/memory/embedding-openai.d.ts +3 -1
  30. package/dist/features/magic-context/memory/embedding-openai.d.ts.map +1 -1
  31. package/dist/features/magic-context/memory/project-identity.d.ts +6 -0
  32. package/dist/features/magic-context/memory/project-identity.d.ts.map +1 -1
  33. package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
  34. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  35. package/dist/features/magic-context/mural/compress-cues.d.ts +2 -0
  36. package/dist/features/magic-context/mural/compress-cues.d.ts.map +1 -1
  37. package/dist/features/magic-context/overflow-detection.d.ts.map +1 -1
  38. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  39. package/dist/features/magic-context/schema-fence-probe.d.ts +1 -0
  40. package/dist/features/magic-context/schema-fence-probe.d.ts.map +1 -1
  41. package/dist/features/magic-context/smart-notes/condition-compiler.d.ts +1 -0
  42. package/dist/features/magic-context/smart-notes/condition-compiler.d.ts.map +1 -1
  43. package/dist/features/magic-context/smart-notes/runner.d.ts.map +1 -1
  44. package/dist/features/magic-context/smart-notes/wake-plane.d.ts +21 -0
  45. package/dist/features/magic-context/smart-notes/wake-plane.d.ts.map +1 -0
  46. package/dist/features/magic-context/storage-clone.d.ts +6 -0
  47. package/dist/features/magic-context/storage-clone.d.ts.map +1 -1
  48. package/dist/features/magic-context/storage-db.d.ts +14 -3
  49. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  50. package/dist/features/magic-context/storage-identity-merge.d.ts.map +1 -1
  51. package/dist/features/magic-context/storage-meta-persisted.d.ts +22 -0
  52. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  53. package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
  54. package/dist/features/magic-context/storage-meta-shared.d.ts.map +1 -1
  55. package/dist/features/magic-context/storage-primers.d.ts +11 -0
  56. package/dist/features/magic-context/storage-primers.d.ts.map +1 -1
  57. package/dist/features/magic-context/storage-schema-helpers.d.ts.map +1 -1
  58. package/dist/features/magic-context/storage-tags.d.ts +2 -0
  59. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  60. package/dist/features/magic-context/storage.d.ts +1 -1
  61. package/dist/features/magic-context/storage.d.ts.map +1 -1
  62. package/dist/features/magic-context/user-memory/review-user-memories.d.ts +2 -0
  63. package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
  64. package/dist/features/magic-context/user-memory/storage-user-memory.d.ts +7 -0
  65. package/dist/features/magic-context/user-memory/storage-user-memory.d.ts.map +1 -1
  66. package/dist/features/magic-context/window-report-ledger.d.ts +70 -0
  67. package/dist/features/magic-context/window-report-ledger.d.ts.map +1 -0
  68. package/dist/hooks/magic-context/child-session-spawn.d.ts +1 -0
  69. package/dist/hooks/magic-context/child-session-spawn.d.ts.map +1 -1
  70. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  71. package/dist/hooks/magic-context/compaction-off-transition.d.ts.map +1 -1
  72. package/dist/hooks/magic-context/compartment-render-epoch.d.ts +5 -3
  73. package/dist/hooks/magic-context/compartment-render-epoch.d.ts.map +1 -1
  74. package/dist/hooks/magic-context/embed-session-state.d.ts +1 -1
  75. package/dist/hooks/magic-context/embed-session-state.d.ts.map +1 -1
  76. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  77. package/dist/hooks/magic-context/event-resolvers.d.ts +4 -0
  78. package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
  79. package/dist/hooks/magic-context/execute-status.d.ts +2 -1
  80. package/dist/hooks/magic-context/execute-status.d.ts.map +1 -1
  81. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  82. package/dist/hooks/magic-context/inject-compartments.d.ts +8 -0
  83. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  84. package/dist/hooks/magic-context/lkg-replay.d.ts +5 -2
  85. package/dist/hooks/magic-context/lkg-replay.d.ts.map +1 -1
  86. package/dist/hooks/magic-context/lkg-slot.d.ts +20 -0
  87. package/dist/hooks/magic-context/lkg-slot.d.ts.map +1 -1
  88. package/dist/hooks/magic-context/module-state-sync.d.ts +9 -0
  89. package/dist/hooks/magic-context/module-state-sync.d.ts.map +1 -1
  90. package/dist/hooks/magic-context/module-transport.d.ts +20 -3
  91. package/dist/hooks/magic-context/module-transport.d.ts.map +1 -1
  92. package/dist/hooks/magic-context/module-wire.d.ts +6 -1
  93. package/dist/hooks/magic-context/module-wire.d.ts.map +1 -1
  94. package/dist/hooks/magic-context/read-session-formatting.d.ts +1 -0
  95. package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
  96. package/dist/hooks/magic-context/rust-mode-transform.d.ts +20 -0
  97. package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
  98. package/dist/hooks/magic-context/strip-content.d.ts +31 -1
  99. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  100. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  101. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +12 -3
  102. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  103. package/dist/hooks/magic-context/transform.d.ts +10 -0
  104. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  105. package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -1
  106. package/dist/{index-3nfgbsp4.js → index-0ze9p983.js} +897 -114
  107. package/dist/{index-nsx2n9eg.js → index-4cz5nfxf.js} +3 -3
  108. package/dist/{index-0c9dmw9m.js → index-8mtsfhyg.js} +33 -3
  109. package/dist/{index-f1tnb3vm.js → index-8pqfcghm.js} +21 -9
  110. package/dist/{index-37rt2bka.js → index-cae89eas.js} +26 -7
  111. package/dist/index-cgyfn1s2.js +53 -0
  112. package/dist/{index-z8v31d85.js → index-crke4z5j.js} +144 -14
  113. package/dist/{index-tsgs7td8.js → index-pck9vadr.js} +1 -1
  114. package/dist/index-q0t189ay.js +741 -0
  115. package/dist/{index-3tnkymva.js → index-sw8gnq34.js} +1 -1
  116. package/dist/index.d.ts.map +1 -1
  117. package/dist/index.js +1952 -795
  118. package/dist/plugin/hooks/create-session-hooks.d.ts +3 -0
  119. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  120. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  121. package/dist/plugin/rust-tool-backends.d.ts +4 -0
  122. package/dist/plugin/rust-tool-backends.d.ts.map +1 -1
  123. package/dist/{recomp-orchestrator-7wbbvf87.js → recomp-orchestrator-c3ff16k7.js} +8 -7
  124. package/dist/{safe-notification-target-c11fdda2.js → safe-notification-target-b693f9x3.js} +2 -2
  125. package/dist/{send-session-notification-bgtf4853.js → send-session-notification-dxske5zm.js} +3 -3
  126. package/dist/shared/conflict-detector.d.ts +63 -0
  127. package/dist/shared/conflict-detector.d.ts.map +1 -1
  128. package/dist/shared/conflict-fixer.d.ts.map +1 -1
  129. package/dist/shared/harness-provider-map.d.ts +8 -0
  130. package/dist/shared/harness-provider-map.d.ts.map +1 -1
  131. package/dist/shared/jsonc-edit.d.ts +18 -0
  132. package/dist/shared/jsonc-edit.d.ts.map +1 -0
  133. package/dist/shared/jsonc-parser.d.ts +11 -1
  134. package/dist/shared/jsonc-parser.d.ts.map +1 -1
  135. package/dist/shared/logger.d.ts +8 -0
  136. package/dist/shared/logger.d.ts.map +1 -1
  137. package/dist/shared/models-dev-cache.d.ts +5 -0
  138. package/dist/shared/models-dev-cache.d.ts.map +1 -1
  139. package/dist/shared/prompt-surface-runtime.d.ts.map +1 -1
  140. package/dist/shared/prompt-surface.d.ts +7 -3
  141. package/dist/shared/prompt-surface.d.ts.map +1 -1
  142. package/dist/shared/rpc-client.d.ts.map +1 -1
  143. package/dist/shared/rpc-types.d.ts +14 -0
  144. package/dist/shared/rpc-types.d.ts.map +1 -1
  145. package/dist/shared/rpc-utils.d.ts +32 -6
  146. package/dist/shared/rpc-utils.d.ts.map +1 -1
  147. package/dist/shared/tui-preferences.d.ts +9 -0
  148. package/dist/shared/tui-preferences.d.ts.map +1 -1
  149. package/dist/shared/window-geometry.d.ts +89 -0
  150. package/dist/shared/window-geometry.d.ts.map +1 -0
  151. package/dist/{storage-meta-session-kh5egr11.js → storage-meta-session-mk4gsnke.js} +4 -3
  152. package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
  153. package/dist/tools/ctx-note/tools.d.ts.map +1 -1
  154. package/dist/tui/data/context-db.d.ts.map +1 -1
  155. package/dist/tui-compiled/data/context-db.d.ts.map +1 -1
  156. package/package.json +2 -1
  157. package/src/config/index.test.ts +23 -0
  158. package/src/config/index.ts +43 -9
  159. package/src/config/latch-permanence-guard.test.ts +218 -0
  160. package/src/config/project-security.ts +10 -1
  161. package/src/config/schema/magic-context.test.ts +2 -1
  162. package/src/config/schema/magic-context.ts +13 -3
  163. package/src/shared/__fixtures__/window-overlay.v1.json +337 -0
  164. package/src/shared/conflict-detector.test.ts +176 -1
  165. package/src/shared/conflict-detector.ts +126 -3
  166. package/src/shared/conflict-fixer.test.ts +174 -0
  167. package/src/shared/conflict-fixer.ts +66 -70
  168. package/src/shared/harness-provider-map.test.ts +20 -0
  169. package/src/shared/harness-provider-map.ts +19 -0
  170. package/src/shared/jsonc-edit.ts +295 -0
  171. package/src/shared/jsonc-parser.test.ts +33 -0
  172. package/src/shared/jsonc-parser.ts +54 -2
  173. package/src/shared/logger.test.ts +119 -0
  174. package/src/shared/logger.ts +33 -15
  175. package/src/shared/models-dev-cache.test.ts +17 -0
  176. package/src/shared/models-dev-cache.ts +58 -6
  177. package/src/shared/prompt-surface-runtime.ts +5 -3
  178. package/src/shared/prompt-surface.test.ts +91 -2
  179. package/src/shared/prompt-surface.ts +35 -11
  180. package/src/shared/rpc-client.ts +4 -2
  181. package/src/shared/rpc-types.ts +14 -0
  182. package/src/shared/rpc-utils.test.ts +173 -13
  183. package/src/shared/rpc-utils.ts +202 -18
  184. package/src/shared/tui-preferences.test.ts +52 -2
  185. package/src/shared/tui-preferences.ts +42 -17
  186. package/src/shared/window-geometry.test.ts +309 -0
  187. package/src/shared/window-geometry.ts +629 -0
  188. package/src/tui/data/context-db.ts +5 -0
  189. package/src/tui/index.tsx +22 -0
  190. package/src/tui-compiled/data/context-db.ts +5 -0
  191. package/src/tui-compiled/index.tsx +319 -255
  192. package/dist/index-wgvxkqpx.js +0 -342
@@ -0,0 +1,629 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { getDataDir } from "./data-path";
4
+ import { modelRefLookupOrder } from "./harness-provider-map";
5
+ import { sessionLog } from "./logger";
6
+ import type { ModelLimit, OutputReserveConfig } from "./models-dev-cache";
7
+
8
+ export const WINDOW_OVERLAY_SCHEMA = "fusiform-window-overlay/v1";
9
+ export const PROMPT_WALL_MARGIN = 4_096;
10
+ export const PI_OUTPUT_FLOOR = 4_096;
11
+ export const OPENCODE_OUTPUT_CAP = 32_000;
12
+
13
+ const MIN_PLAUSIBLE_CONTEXT_LIMIT = 1_024;
14
+ const OUTPUT_RESERVE_CAP_RATIO = 0.25;
15
+
16
+ export type WindowGeometry = "shared_upfront" | "shared_truncating" | "separate";
17
+ export type WindowReserveSource = "output_catalog" | "output_config" | "wall_margin" | "none";
18
+ export type WindowOverlayGrade =
19
+ | "provider_asserted_runtime"
20
+ | "measured"
21
+ | "provider_asserted_doc"
22
+ | "catalog"
23
+ | "unknown";
24
+ export type WindowOverlayUnits = "provider" | "estimate";
25
+ export type WindowOverlayBoundary = "Observed" | "Asserted" | "Corrected";
26
+ export type WindowOverlayUnknownWhy =
27
+ | "placeholder_output_equals_context"
28
+ | "placeholder_zero"
29
+ | "never_measured"
30
+ // Asserts the KEY cannot hold one fact (e.g. a router samples heterogeneous
31
+ // backends per request), so measurement cannot settle it and measured
32
+ // reports must not be promoted into stated/bracket cells at this key.
33
+ // Ratified terminal at fusiform 080e70208aad713d (2026-08-13), added without a schema bump: an
34
+ // additive vocabulary value degrades per-cell for ignorant consumers,
35
+ // whereas a version bump would trigger the file-level refusal rule.
36
+ | "not_single_valued_at_key"
37
+ | "retracted";
38
+
39
+ export type WindowOverlayFactValue =
40
+ | { kind: "stated"; value: number | string }
41
+ | { kind: "bracket"; at_least?: number; below?: number }
42
+ | { kind: "unknown"; why: WindowOverlayUnknownWhy };
43
+
44
+ export interface WindowOverlayFact {
45
+ value: WindowOverlayFactValue;
46
+ grade: WindowOverlayGrade;
47
+ units: WindowOverlayUnits;
48
+ boundary: WindowOverlayBoundary;
49
+ source_ref: string;
50
+ observed_at: string;
51
+ [unknownField: string]: unknown;
52
+ }
53
+
54
+ export interface WindowOverlayCell {
55
+ provider_id: string;
56
+ model_id: string;
57
+ /** Unknown fact keys are retained so a newer producer can coexist with this v1 consumer. */
58
+ facts: Record<string, WindowOverlayFact>;
59
+ }
60
+
61
+ export interface WindowOverlay {
62
+ schema: typeof WINDOW_OVERLAY_SCHEMA;
63
+ generated_at: string;
64
+ minted_provider_ids: string[];
65
+ cells: WindowOverlayCell[];
66
+ }
67
+
68
+ export interface ResolvedWindowOverlayFacts {
69
+ /** Includes facts whose tagged value is explicitly unknown. */
70
+ facts: Record<string, WindowOverlayFact>;
71
+ }
72
+
73
+ export interface WindowDerivation {
74
+ window: number;
75
+ reserve: number;
76
+ reserveSource: WindowReserveSource;
77
+ geometry: WindowGeometry;
78
+ }
79
+
80
+ export interface WindowGeometryResult {
81
+ usableSoft: number;
82
+ usableHard: number;
83
+ geometry: WindowGeometry;
84
+ derivation: WindowDerivation;
85
+ }
86
+
87
+ export interface DeriveWindowGeometryOptions {
88
+ overlay?: ResolvedWindowOverlayFacts;
89
+ /** A provider/auth hook has higher precedence than the overlay, field by field. */
90
+ providerLimit?: ModelLimit;
91
+ reserveConfig?: OutputReserveConfig;
92
+ harness?: "opencode" | "pi";
93
+ /** Detected overflow remains the final downward cap regardless of other sources. */
94
+ contextCap?: number;
95
+ log?: (message: string) => void;
96
+ }
97
+
98
+ const PROVIDER_GEOMETRY: Readonly<Record<string, WindowGeometry>> = {
99
+ anthropic: "shared_truncating",
100
+ xai: "shared_truncating",
101
+ google: "separate",
102
+ "google-antigravity": "separate",
103
+ };
104
+
105
+ const GRADES = new Set<WindowOverlayGrade>([
106
+ "provider_asserted_runtime",
107
+ "measured",
108
+ "provider_asserted_doc",
109
+ "catalog",
110
+ "unknown",
111
+ ]);
112
+ const UNITS = new Set<WindowOverlayUnits>(["provider", "estimate"]);
113
+ const BOUNDARIES = new Set<WindowOverlayBoundary>(["Observed", "Asserted", "Corrected"]);
114
+ const UNKNOWN_REASONS = new Set<WindowOverlayUnknownWhy>([
115
+ "placeholder_output_equals_context",
116
+ "placeholder_zero",
117
+ "never_measured",
118
+ "not_single_valued_at_key",
119
+ "retracted",
120
+ ]);
121
+ const NUMERIC_FACT_KEYS = new Set([
122
+ "window.advertised",
123
+ "window.enforced",
124
+ "output.advertised",
125
+ "output.enforced",
126
+ "output.default",
127
+ ]);
128
+
129
+ let configuredOverlayPath: string | undefined;
130
+ let loadedOverlayPath: string | undefined;
131
+ let loadedOverlay: WindowOverlay | null | undefined;
132
+ const geometryClampLogSeen = new Set<string>();
133
+
134
+ function isRecord(value: unknown): value is Record<string, unknown> {
135
+ return typeof value === "object" && value !== null && !Array.isArray(value);
136
+ }
137
+
138
+ function isFinitePositive(value: unknown): value is number {
139
+ return typeof value === "number" && Number.isFinite(value) && value > 0;
140
+ }
141
+
142
+ function modelKeyLookupOrder(providerID: string, modelID: string): string[] {
143
+ const candidates = [...modelRefLookupOrder(`${providerID}/${modelID}`), modelID];
144
+ const colon = modelID.lastIndexOf(":");
145
+ if (colon > 0) {
146
+ const bareModel = modelID.slice(0, colon);
147
+ candidates.push(...modelRefLookupOrder(`${providerID}/${bareModel}`), bareModel);
148
+ }
149
+ return [...new Set(candidates)];
150
+ }
151
+
152
+ function configuredOutputReserve(
153
+ config: OutputReserveConfig | undefined,
154
+ providerID: string,
155
+ modelID: string,
156
+ ): number | undefined {
157
+ if (typeof config === "number") {
158
+ return Number.isFinite(config) && config >= 0 ? config : undefined;
159
+ }
160
+ if (!config) return undefined;
161
+ for (const candidate of modelKeyLookupOrder(providerID, modelID)) {
162
+ const value = config[candidate];
163
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) return value;
164
+ }
165
+ return Number.isFinite(config.default) && config.default >= 0 ? config.default : undefined;
166
+ }
167
+
168
+ /** Convert the ratified tagged numeric union into the conservative scalar used by derivation. */
169
+ export function scalarizeFact(value: WindowOverlayFactValue): number | undefined {
170
+ if (value.kind === "stated") return isFinitePositive(value.value) ? value.value : undefined;
171
+ if (value.kind === "bracket") {
172
+ return isFinitePositive(value.at_least) ? value.at_least : undefined;
173
+ }
174
+ return undefined;
175
+ }
176
+
177
+ function parseFactValue(value: unknown): WindowOverlayFactValue | undefined {
178
+ if (!isRecord(value) || typeof value.kind !== "string") return undefined;
179
+ if (value.kind === "stated") {
180
+ if (
181
+ (typeof value.value !== "number" || !Number.isFinite(value.value)) &&
182
+ typeof value.value !== "string"
183
+ ) {
184
+ return undefined;
185
+ }
186
+ return { kind: "stated", value: value.value };
187
+ }
188
+ if (value.kind === "bracket") {
189
+ const atLeast = value.at_least;
190
+ const below = value.below;
191
+ if (atLeast === undefined && below === undefined) return { kind: "bracket" };
192
+ if (atLeast !== undefined && !isFinitePositive(atLeast)) return undefined;
193
+ if (below !== undefined && !isFinitePositive(below)) return undefined;
194
+ if (isFinitePositive(atLeast) && isFinitePositive(below) && below <= atLeast) {
195
+ return undefined;
196
+ }
197
+ return {
198
+ kind: "bracket",
199
+ ...(isFinitePositive(atLeast) ? { at_least: atLeast } : {}),
200
+ ...(isFinitePositive(below) ? { below } : {}),
201
+ };
202
+ }
203
+ if (value.kind === "unknown" && UNKNOWN_REASONS.has(value.why as WindowOverlayUnknownWhy)) {
204
+ return { kind: "unknown", why: value.why as WindowOverlayUnknownWhy };
205
+ }
206
+ return undefined;
207
+ }
208
+
209
+ function parseFact(key: string, value: unknown): WindowOverlayFact | undefined {
210
+ if (!isRecord(value)) return undefined;
211
+ const parsedValue = parseFactValue(value.value);
212
+ if (
213
+ !parsedValue ||
214
+ !GRADES.has(value.grade as WindowOverlayGrade) ||
215
+ !UNITS.has(value.units as WindowOverlayUnits) ||
216
+ !BOUNDARIES.has(value.boundary as WindowOverlayBoundary) ||
217
+ typeof value.source_ref !== "string" ||
218
+ value.source_ref.length === 0 ||
219
+ typeof value.observed_at !== "string" ||
220
+ value.observed_at.length === 0
221
+ ) {
222
+ return undefined;
223
+ }
224
+ if (
225
+ NUMERIC_FACT_KEYS.has(key) &&
226
+ parsedValue.kind === "stated" &&
227
+ !isFinitePositive(parsedValue.value)
228
+ ) {
229
+ return undefined;
230
+ }
231
+ if (
232
+ key === "geometry" &&
233
+ parsedValue.kind === "stated" &&
234
+ !["shared_upfront", "shared_truncating", "separate"].includes(String(parsedValue.value))
235
+ ) {
236
+ return undefined;
237
+ }
238
+ return {
239
+ ...value,
240
+ value: parsedValue,
241
+ grade: value.grade as WindowOverlayGrade,
242
+ units: value.units as WindowOverlayUnits,
243
+ boundary: value.boundary as WindowOverlayBoundary,
244
+ source_ref: value.source_ref,
245
+ observed_at: value.observed_at,
246
+ } as WindowOverlayFact;
247
+ }
248
+
249
+ export function parseWindowOverlay(value: unknown): {
250
+ overlay?: WindowOverlay;
251
+ badCells: number;
252
+ refusal?: string;
253
+ } {
254
+ if (!isRecord(value)) {
255
+ return { badCells: 0, refusal: "overlay root is not an object" };
256
+ }
257
+ if (value.schema !== WINDOW_OVERLAY_SCHEMA) {
258
+ return {
259
+ badCells: 0,
260
+ refusal: `unrecognized schema ${JSON.stringify(value.schema)}`,
261
+ };
262
+ }
263
+ if (
264
+ typeof value.generated_at !== "string" ||
265
+ !Array.isArray(value.minted_provider_ids) ||
266
+ !value.minted_provider_ids.every((id) => typeof id === "string" && id.length > 0) ||
267
+ !Array.isArray(value.cells)
268
+ ) {
269
+ return { badCells: 0, refusal: "invalid v1 envelope" };
270
+ }
271
+
272
+ const cells: WindowOverlayCell[] = [];
273
+ let badCells = 0;
274
+ for (const rawCell of value.cells) {
275
+ if (
276
+ !isRecord(rawCell) ||
277
+ typeof rawCell.provider_id !== "string" ||
278
+ rawCell.provider_id.length === 0 ||
279
+ typeof rawCell.model_id !== "string" ||
280
+ rawCell.model_id.length === 0 ||
281
+ !isRecord(rawCell.facts)
282
+ ) {
283
+ badCells++;
284
+ continue;
285
+ }
286
+ const facts: Record<string, WindowOverlayFact> = {};
287
+ let valid = true;
288
+ for (const [key, rawFact] of Object.entries(rawCell.facts)) {
289
+ const fact = parseFact(key, rawFact);
290
+ if (!fact) {
291
+ valid = false;
292
+ break;
293
+ }
294
+ facts[key] = fact;
295
+ }
296
+ if (!valid) {
297
+ badCells++;
298
+ continue;
299
+ }
300
+ cells.push({
301
+ provider_id: rawCell.provider_id,
302
+ model_id: rawCell.model_id,
303
+ facts,
304
+ });
305
+ }
306
+
307
+ return {
308
+ overlay: {
309
+ schema: WINDOW_OVERLAY_SCHEMA,
310
+ generated_at: value.generated_at,
311
+ minted_provider_ids: [...value.minted_provider_ids],
312
+ cells,
313
+ },
314
+ badCells,
315
+ };
316
+ }
317
+
318
+ export function defaultWindowOverlayPath(): string {
319
+ return join(getDataDir(), "fusiform", "window-overlay.json");
320
+ }
321
+
322
+ export function readWindowOverlayFile(
323
+ path: string,
324
+ log: (message: string) => void = (message) => sessionLog("global", message),
325
+ ): WindowOverlay | undefined {
326
+ let raw: string;
327
+ try {
328
+ raw = readFileSync(path, "utf8");
329
+ } catch (error) {
330
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
331
+ log(`window-overlay: unable to read ${path}; overlay ignored`);
332
+ return undefined;
333
+ }
334
+ let decoded: unknown;
335
+ try {
336
+ decoded = JSON.parse(raw);
337
+ } catch {
338
+ log(`window-overlay: invalid JSON in ${path}; overlay ignored`);
339
+ return undefined;
340
+ }
341
+ const parsed = parseWindowOverlay(decoded);
342
+ if (parsed.refusal) {
343
+ log(`window-overlay: ${parsed.refusal} in ${path}; entire overlay ignored`);
344
+ return undefined;
345
+ }
346
+ if (parsed.badCells > 0) {
347
+ log(`window-overlay: skipped ${parsed.badCells} invalid cell(s) from ${path}`);
348
+ }
349
+ return parsed.overlay;
350
+ }
351
+
352
+ /** Set the user-tier overlay path. Undefined restores the Fusiform data-dir default. */
353
+ export function setWindowOverlayPath(path: string | undefined): void {
354
+ configuredOverlayPath = path;
355
+ loadedOverlayPath = undefined;
356
+ loadedOverlay = undefined;
357
+ }
358
+
359
+ export function clearWindowOverlayCacheForTest(): void {
360
+ loadedOverlayPath = undefined;
361
+ loadedOverlay = undefined;
362
+ geometryClampLogSeen.clear();
363
+ }
364
+
365
+ export function getWindowOverlay(): WindowOverlay | undefined {
366
+ const path = configuredOverlayPath ?? defaultWindowOverlayPath();
367
+ if (loadedOverlayPath === path && loadedOverlay !== undefined) {
368
+ return loadedOverlay ?? undefined;
369
+ }
370
+ loadedOverlayPath = path;
371
+ loadedOverlay = readWindowOverlayFile(path) ?? null;
372
+ return loadedOverlay ?? undefined;
373
+ }
374
+
375
+ export function resolveWindowOverlayFacts(
376
+ providerID: string,
377
+ modelID: string,
378
+ overlay: WindowOverlay | undefined = getWindowOverlay(),
379
+ ): ResolvedWindowOverlayFacts | undefined {
380
+ if (!overlay) return undefined;
381
+ const modelRefs = modelRefLookupOrder(`${providerID}/${modelID}`);
382
+ const providerCandidates = new Set(modelRefs.map((ref) => ref.slice(0, ref.indexOf("/"))));
383
+ const modelCandidates = new Set([
384
+ modelID,
385
+ ...modelRefs.map((ref) => ref.slice(ref.indexOf("/") + 1)),
386
+ ]);
387
+ const colon = modelID.lastIndexOf(":");
388
+ if (colon > 0) modelCandidates.add(modelID.slice(0, colon));
389
+
390
+ const wildcardFacts: Record<string, WindowOverlayFact> = {};
391
+ const specificFacts: Record<string, WindowOverlayFact> = {};
392
+ for (const cell of overlay.cells) {
393
+ if (!providerCandidates.has(cell.provider_id)) continue;
394
+ if (cell.model_id === "*") Object.assign(wildcardFacts, cell.facts);
395
+ else if (modelCandidates.has(cell.model_id)) Object.assign(specificFacts, cell.facts);
396
+ }
397
+ const facts = { ...wildcardFacts, ...specificFacts };
398
+ return Object.keys(facts).length > 0 ? { facts } : undefined;
399
+ }
400
+
401
+ function numericOverlayFact(
402
+ overlay: ResolvedWindowOverlayFacts | undefined,
403
+ key: string,
404
+ ): number | undefined {
405
+ const fact = overlay?.facts[key];
406
+ return fact ? scalarizeFact(fact.value) : undefined;
407
+ }
408
+
409
+ /**
410
+ * Three-state geometry resolution from the overlay. The absent-vs-unknown
411
+ * distinction is ratified contract semantics: a fact ABSENT from the cell was
412
+ * never considered (our static provider table still applies), while a fact
413
+ * present with kind "unknown" was considered and has no answer — the dataset
414
+ * owner checked the primary source and found the usual inference unsupported
415
+ * (e.g. Google's own docs contradict the separate-quota reading), so the
416
+ * static assumption built on that same inference must NOT apply either.
417
+ */
418
+ function overlayGeometry(
419
+ overlay: ResolvedWindowOverlayFacts | undefined,
420
+ ): { kind: "stated"; value: WindowGeometry } | { kind: "unknown" } | undefined {
421
+ const fact = overlay?.facts.geometry;
422
+ if (fact === undefined) return undefined;
423
+ if (fact.value.kind === "unknown") return { kind: "unknown" };
424
+ if (fact.value.kind !== "stated") return undefined;
425
+ const value = fact.value.value;
426
+ return value === "shared_upfront" || value === "shared_truncating" || value === "separate"
427
+ ? { kind: "stated", value }
428
+ : undefined;
429
+ }
430
+
431
+ /** Placeholder filtering is deliberately per output field, never a row-level rejection. */
432
+ export function placeholderFilteredOutput(
433
+ output: number | undefined,
434
+ context: number | undefined,
435
+ ): number | undefined {
436
+ if (!isFinitePositive(output)) return undefined;
437
+ if (isFinitePositive(context) && output >= context) return undefined;
438
+ return output;
439
+ }
440
+
441
+ function mergePositive(
442
+ overlayValue: number | undefined,
443
+ providerValue: number | undefined,
444
+ ): number | undefined {
445
+ return isFinitePositive(providerValue) ? providerValue : overlayValue;
446
+ }
447
+
448
+ function logGeometryClampOnce(key: string, message: string, log?: (message: string) => void): void {
449
+ if (geometryClampLogSeen.has(key)) return;
450
+ geometryClampLogSeen.add(key);
451
+ (log ?? ((entry) => sessionLog("global", `window-geometry: ${entry}`)))(message);
452
+ }
453
+
454
+ export function deriveWindowGeometry(
455
+ providerID: string,
456
+ modelID: string,
457
+ catalogLimit: ModelLimit | undefined,
458
+ options: DeriveWindowGeometryOptions = {},
459
+ ): WindowGeometryResult | undefined {
460
+ if (!catalogLimit && !options.providerLimit) return undefined;
461
+ const providerLimit = options.providerLimit;
462
+ const catalogContext = isFinitePositive(catalogLimit?.context)
463
+ ? catalogLimit.context
464
+ : undefined;
465
+ const advertised = numericOverlayFact(options.overlay, "window.advertised");
466
+ const enforced = numericOverlayFact(options.overlay, "window.enforced");
467
+ let softContext = mergePositive(
468
+ enforced ?? advertised ?? catalogContext,
469
+ providerLimit?.context,
470
+ );
471
+ let hardContext = mergePositive(enforced ?? softContext, providerLimit?.context);
472
+ if (isFinitePositive(options.contextCap)) {
473
+ softContext = isFinitePositive(softContext)
474
+ ? Math.min(softContext, options.contextCap)
475
+ : options.contextCap;
476
+ hardContext = isFinitePositive(hardContext)
477
+ ? Math.min(hardContext, options.contextCap)
478
+ : options.contextCap;
479
+ }
480
+ const input = mergePositive(
481
+ isFinitePositive(catalogLimit?.input) ? catalogLimit.input : undefined,
482
+ providerLimit?.input,
483
+ );
484
+ if (!isFinitePositive(softContext) && !isFinitePositive(input)) return undefined;
485
+
486
+ const catalogOutput =
487
+ options.overlay === undefined && options.providerLimit === undefined
488
+ ? isFinitePositive(catalogLimit?.output)
489
+ ? catalogLimit.output
490
+ : undefined
491
+ : placeholderFilteredOutput(catalogLimit?.output, softContext);
492
+ const overlayOutput = placeholderFilteredOutput(
493
+ numericOverlayFact(options.overlay, "output.enforced") ??
494
+ numericOverlayFact(options.overlay, "output.default") ??
495
+ numericOverlayFact(options.overlay, "output.advertised"),
496
+ softContext,
497
+ );
498
+ const providerOutput = placeholderFilteredOutput(providerLimit?.output, softContext);
499
+ const output = providerOutput ?? overlayOutput ?? catalogOutput;
500
+ const geometryFact = overlayGeometry(options.overlay);
501
+ // Considered-unknown demotes to shared_upfront — the conservative geometry
502
+ // in both directions (largest soft reserve, lowest hard wall) — until a
503
+ // measurement settles the cell. Absence falls through to the static table.
504
+ const geometry =
505
+ geometryFact?.kind === "stated"
506
+ ? geometryFact.value
507
+ : geometryFact?.kind === "unknown"
508
+ ? "shared_upfront"
509
+ : (PROVIDER_GEOMETRY[providerID] ?? "shared_upfront");
510
+ const geometryOverride = geometryFact?.kind === "stated" ? geometryFact.value : undefined;
511
+
512
+ let usableSoft: number;
513
+ let softReserve = 0;
514
+ let reserveSource: WindowReserveSource = "none";
515
+ if (isFinitePositive(input) && (!isFinitePositive(softContext) || input < softContext)) {
516
+ usableSoft = input;
517
+ if (isFinitePositive(softContext)) {
518
+ softReserve = Math.max(0, softContext - input);
519
+ reserveSource = "output_catalog";
520
+ }
521
+ } else if (isFinitePositive(softContext)) {
522
+ const configuredReserve = configuredOutputReserve(
523
+ options.reserveConfig,
524
+ providerID,
525
+ modelID,
526
+ );
527
+ if (configuredReserve !== undefined) {
528
+ softReserve = configuredReserve;
529
+ reserveSource = "output_config";
530
+ } else if (
531
+ geometry === "separate" &&
532
+ (options.overlay === undefined ||
533
+ options.harness === "pi" ||
534
+ geometryOverride !== undefined)
535
+ ) {
536
+ softReserve = 0;
537
+ reserveSource = "none";
538
+ } else {
539
+ softReserve = output ?? 0;
540
+ reserveSource = output === undefined ? "none" : "output_catalog";
541
+ const cap = softContext * OUTPUT_RESERVE_CAP_RATIO;
542
+ softReserve = Math.min(
543
+ softReserve,
544
+ options.harness === "pi" || options.overlay === undefined
545
+ ? cap
546
+ : Math.min(cap, OPENCODE_OUTPUT_CAP),
547
+ );
548
+ }
549
+ const floor = Math.max(MIN_PLAUSIBLE_CONTEXT_LIMIT, softContext * 0.5);
550
+ const flooredReserve = Math.min(softReserve, Math.max(0, softContext - floor));
551
+ if (flooredReserve < softReserve) {
552
+ // A reserve large enough to hit the half-window floor means the
553
+ // catalog's context/output pair contradicts itself (93 live rows
554
+ // publish output > context — fusiform's negative-window detector).
555
+ // The floor keeps the window functional; the log keeps the
556
+ // degradation visible instead of silently halving the window.
557
+ logGeometryClampOnce(
558
+ `soft-floor|${providerID}/${modelID}|${softReserve}|${flooredReserve}`,
559
+ `output reserve clamped by the half-window floor for ${providerID}/${modelID}: reserve ${softReserve} → ${flooredReserve} (catalog context/output pair is contradictory)`,
560
+ options.log,
561
+ );
562
+ }
563
+ softReserve = flooredReserve;
564
+ usableSoft = Math.floor(softContext - softReserve);
565
+ } else {
566
+ usableSoft = input as number;
567
+ }
568
+
569
+ const hardWindow = hardContext ?? softContext ?? input;
570
+ if (!isFinitePositive(hardWindow)) return undefined;
571
+ let usableHard: number;
572
+ if (geometry === "separate") {
573
+ usableHard = hardWindow;
574
+ } else if (geometry === "shared_truncating") {
575
+ usableHard = hardWindow - PROMPT_WALL_MARGIN;
576
+ } else if (options.harness === "pi" && providerID !== "openai-codex") {
577
+ usableHard = hardWindow - PI_OUTPUT_FLOOR;
578
+ } else if (options.harness === "pi") {
579
+ usableHard = hardWindow - (output ?? OPENCODE_OUTPUT_CAP);
580
+ } else {
581
+ const requestedOutput = Math.min(output ?? OPENCODE_OUTPUT_CAP, OPENCODE_OUTPUT_CAP);
582
+ usableHard = hardWindow - requestedOutput;
583
+ }
584
+ usableHard = Math.max(MIN_PLAUSIBLE_CONTEXT_LIMIT, Math.floor(usableHard));
585
+ if (usableHard < usableSoft) {
586
+ logGeometryClampOnce(
587
+ `${providerID}/${modelID}|${usableSoft}|${usableHard}`,
588
+ `usable hard limit clamped for ${providerID}/${modelID}: ${usableHard} → ${usableSoft} (overlay/provider inversion)`,
589
+ options.log,
590
+ );
591
+ usableHard = usableSoft;
592
+ }
593
+
594
+ return {
595
+ usableSoft,
596
+ usableHard,
597
+ geometry,
598
+ derivation: {
599
+ window: Math.floor(softContext ?? usableSoft),
600
+ reserve: Math.floor(Math.max(0, (softContext ?? usableSoft) - usableSoft)),
601
+ reserveSource,
602
+ geometry,
603
+ },
604
+ };
605
+ }
606
+
607
+ export function formatWindowDerivationLine(
608
+ inputTokens: number,
609
+ result: WindowGeometryResult,
610
+ ): string {
611
+ const percentage = result.usableSoft > 0 ? (inputTokens / result.usableSoft) * 100 : 0;
612
+ const reserveLabel =
613
+ result.derivation.reserveSource === "wall_margin"
614
+ ? "wall margin"
615
+ : result.derivation.reserveSource === "none"
616
+ ? "reserve"
617
+ : "output reserve";
618
+ return `Context: ${formatCompactTokens(inputTokens)} / ${formatCompactTokens(result.usableSoft)} usable (${percentage.toFixed(1)}%) — window ${formatCompactTokens(result.derivation.window)} − ${formatCompactTokens(result.derivation.reserve)} ${reserveLabel} [${result.geometry}]`;
619
+ }
620
+
621
+ export function formatCompactTokens(value: number): string {
622
+ if (Math.abs(value) >= 1_000_000) {
623
+ return `${(value / 1_000_000).toFixed(1).replace(/\.0$/, "")}m`;
624
+ }
625
+ if (Math.abs(value) >= 1_000) {
626
+ return `${(value / 1_000).toFixed(1).replace(/\.0$/, "")}k`;
627
+ }
628
+ return Math.round(value).toLocaleString();
629
+ }
@@ -204,6 +204,11 @@ export async function loadStatusDetail(
204
204
  compressionBudget: null,
205
205
  compressionUsage: null,
206
206
  toastDurationMs: 5000,
207
+ loggerDiagnostics: {
208
+ swallowedWriteCount: 0,
209
+ lastErrorMessage: null,
210
+ lastErrorTime: null,
211
+ },
207
212
  storage_versions: {
208
213
  context_db_schema_version: null,
209
214
  plugin_supported_version: 0,
package/src/tui/index.tsx CHANGED
@@ -11,6 +11,7 @@ import packageJson from "../../package.json"
11
11
  import { closeRpc, dismissUpgradeReminder, getAnnouncement, getCompartmentCount, getRpcGeneration, initRpcClient, loadEmbedDetail, loadStatusDetail, loadToastDurationMs, markAnnounced, requestRecomp, requestUpgrade, type EmbedDetail, type StatusDetail } from "./data/context-db"
12
12
  import { startNotificationSocket, stopNotificationSocket, type SocketNotification } from "./data/notification-socket"
13
13
  import { formatThresholdPercent } from "../shared/format-threshold"
14
+ import { formatWindowDerivationLine } from "../shared/window-geometry"
14
15
  import { compactionOffSidebarRows, nativeCompactionContextLabel } from "./compaction-off"
15
16
  import { isCompactionEnabled } from "../config/agent-disable"
16
17
  import { loadPluginConfig } from "../config"
@@ -239,6 +240,11 @@ const StatusDialog = (props: { api: TuiPluginApi; s: StatusDetail }) => {
239
240
  {fmt(s().inputTokens)} / {contextLimit() > 0 ? fmt(contextLimit()) : "?"} tokens
240
241
  </text>
241
242
  </box>
243
+ {s().windowGeometry && (
244
+ <text fg={t().textMuted}>
245
+ {formatWindowDerivationLine(s().inputTokens, s().windowGeometry!)}
246
+ </text>
247
+ )}
242
248
 
243
249
  {/* Segmented breakdown bar: flex row of colored boxes filling
244
250
  the dialog width. See barSegments comment above. */}
@@ -388,6 +394,22 @@ const StatusDialog = (props: { api: TuiPluginApi; s: StatusDetail }) => {
388
394
  </box>
389
395
  )}
390
396
 
397
+ <box marginTop={1} width="100%">
398
+ <text fg={t().text}><b>Logger</b></text>
399
+ <R
400
+ t={t()}
401
+ l="Swallowed writes"
402
+ v={String(s().loggerDiagnostics?.swallowedWriteCount ?? 0)}
403
+ fg={(s().loggerDiagnostics?.swallowedWriteCount ?? 0) > 0 ? t().error : t().textMuted}
404
+ />
405
+ {s().loggerDiagnostics?.lastErrorMessage && (
406
+ <R t={t()} l="Last error" v={s().loggerDiagnostics.lastErrorMessage} fg={t().error} />
407
+ )}
408
+ {s().loggerDiagnostics?.lastErrorTime && (
409
+ <R t={t()} l="Last error time" v={s().loggerDiagnostics.lastErrorTime} fg={t().textMuted} />
410
+ )}
411
+ </box>
412
+
391
413
  {/* Footer */}
392
414
  <box marginTop={1} justifyContent="flex-end" width="100%">
393
415
  <text fg={t().textMuted}>Esc to close</text>
@@ -204,6 +204,11 @@ export async function loadStatusDetail(
204
204
  compressionBudget: null,
205
205
  compressionUsage: null,
206
206
  toastDurationMs: 5000,
207
+ loggerDiagnostics: {
208
+ swallowedWriteCount: 0,
209
+ lastErrorMessage: null,
210
+ lastErrorTime: null,
211
+ },
207
212
  storage_versions: {
208
213
  context_db_schema_version: null,
209
214
  plugin_supported_version: 0,