@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,741 @@
1
+ import {
2
+ modelRefLookupOrder
3
+ } from "./index-cgyfn1s2.js";
4
+ import {
5
+ sessionLog
6
+ } from "./index-8pqfcghm.js";
7
+ import {
8
+ getDataDir,
9
+ getHarness,
10
+ getMagicContextStorageDir
11
+ } from "./index-mmrhhk9a.js";
12
+
13
+ // src/shared/models-dev-cache.ts
14
+ import { mkdirSync, readFileSync as readFileSync2, renameSync, writeFileSync } from "node:fs";
15
+ import { join as join2 } from "node:path";
16
+
17
+ // src/shared/storage-permissions.ts
18
+ var enforcePrivateStoragePermissions = true;
19
+ function setStoragePrivatePermissionEnforcement(enforce) {
20
+ enforcePrivateStoragePermissions = enforce;
21
+ }
22
+ function shouldEnforcePrivateStoragePermissions() {
23
+ return enforcePrivateStoragePermissions;
24
+ }
25
+
26
+ // src/shared/window-geometry.ts
27
+ import { readFileSync } from "node:fs";
28
+ import { join } from "node:path";
29
+ var WINDOW_OVERLAY_SCHEMA = "fusiform-window-overlay/v1";
30
+ var PROMPT_WALL_MARGIN = 4096;
31
+ var PI_OUTPUT_FLOOR = 4096;
32
+ var OPENCODE_OUTPUT_CAP = 32000;
33
+ var MIN_PLAUSIBLE_CONTEXT_LIMIT = 1024;
34
+ var OUTPUT_RESERVE_CAP_RATIO = 0.25;
35
+ var PROVIDER_GEOMETRY = {
36
+ anthropic: "shared_truncating",
37
+ xai: "shared_truncating",
38
+ google: "separate",
39
+ "google-antigravity": "separate"
40
+ };
41
+ var GRADES = new Set([
42
+ "provider_asserted_runtime",
43
+ "measured",
44
+ "provider_asserted_doc",
45
+ "catalog",
46
+ "unknown"
47
+ ]);
48
+ var UNITS = new Set(["provider", "estimate"]);
49
+ var BOUNDARIES = new Set(["Observed", "Asserted", "Corrected"]);
50
+ var UNKNOWN_REASONS = new Set([
51
+ "placeholder_output_equals_context",
52
+ "placeholder_zero",
53
+ "never_measured",
54
+ "not_single_valued_at_key",
55
+ "retracted"
56
+ ]);
57
+ var NUMERIC_FACT_KEYS = new Set([
58
+ "window.advertised",
59
+ "window.enforced",
60
+ "output.advertised",
61
+ "output.enforced",
62
+ "output.default"
63
+ ]);
64
+ var configuredOverlayPath;
65
+ var loadedOverlayPath;
66
+ var loadedOverlay;
67
+ var geometryClampLogSeen = new Set;
68
+ function isRecord(value) {
69
+ return typeof value === "object" && value !== null && !Array.isArray(value);
70
+ }
71
+ function isFinitePositive(value) {
72
+ return typeof value === "number" && Number.isFinite(value) && value > 0;
73
+ }
74
+ function modelKeyLookupOrder(providerID, modelID) {
75
+ const candidates = [...modelRefLookupOrder(`${providerID}/${modelID}`), modelID];
76
+ const colon = modelID.lastIndexOf(":");
77
+ if (colon > 0) {
78
+ const bareModel = modelID.slice(0, colon);
79
+ candidates.push(...modelRefLookupOrder(`${providerID}/${bareModel}`), bareModel);
80
+ }
81
+ return [...new Set(candidates)];
82
+ }
83
+ function configuredOutputReserve(config, providerID, modelID) {
84
+ if (typeof config === "number") {
85
+ return Number.isFinite(config) && config >= 0 ? config : undefined;
86
+ }
87
+ if (!config)
88
+ return;
89
+ for (const candidate of modelKeyLookupOrder(providerID, modelID)) {
90
+ const value = config[candidate];
91
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0)
92
+ return value;
93
+ }
94
+ return Number.isFinite(config.default) && config.default >= 0 ? config.default : undefined;
95
+ }
96
+ function scalarizeFact(value) {
97
+ if (value.kind === "stated")
98
+ return isFinitePositive(value.value) ? value.value : undefined;
99
+ if (value.kind === "bracket") {
100
+ return isFinitePositive(value.at_least) ? value.at_least : undefined;
101
+ }
102
+ return;
103
+ }
104
+ function parseFactValue(value) {
105
+ if (!isRecord(value) || typeof value.kind !== "string")
106
+ return;
107
+ if (value.kind === "stated") {
108
+ if ((typeof value.value !== "number" || !Number.isFinite(value.value)) && typeof value.value !== "string") {
109
+ return;
110
+ }
111
+ return { kind: "stated", value: value.value };
112
+ }
113
+ if (value.kind === "bracket") {
114
+ const atLeast = value.at_least;
115
+ const below = value.below;
116
+ if (atLeast === undefined && below === undefined)
117
+ return { kind: "bracket" };
118
+ if (atLeast !== undefined && !isFinitePositive(atLeast))
119
+ return;
120
+ if (below !== undefined && !isFinitePositive(below))
121
+ return;
122
+ if (isFinitePositive(atLeast) && isFinitePositive(below) && below <= atLeast) {
123
+ return;
124
+ }
125
+ return {
126
+ kind: "bracket",
127
+ ...isFinitePositive(atLeast) ? { at_least: atLeast } : {},
128
+ ...isFinitePositive(below) ? { below } : {}
129
+ };
130
+ }
131
+ if (value.kind === "unknown" && UNKNOWN_REASONS.has(value.why)) {
132
+ return { kind: "unknown", why: value.why };
133
+ }
134
+ return;
135
+ }
136
+ function parseFact(key, value) {
137
+ if (!isRecord(value))
138
+ return;
139
+ const parsedValue = parseFactValue(value.value);
140
+ if (!parsedValue || !GRADES.has(value.grade) || !UNITS.has(value.units) || !BOUNDARIES.has(value.boundary) || typeof value.source_ref !== "string" || value.source_ref.length === 0 || typeof value.observed_at !== "string" || value.observed_at.length === 0) {
141
+ return;
142
+ }
143
+ if (NUMERIC_FACT_KEYS.has(key) && parsedValue.kind === "stated" && !isFinitePositive(parsedValue.value)) {
144
+ return;
145
+ }
146
+ if (key === "geometry" && parsedValue.kind === "stated" && !["shared_upfront", "shared_truncating", "separate"].includes(String(parsedValue.value))) {
147
+ return;
148
+ }
149
+ return {
150
+ ...value,
151
+ value: parsedValue,
152
+ grade: value.grade,
153
+ units: value.units,
154
+ boundary: value.boundary,
155
+ source_ref: value.source_ref,
156
+ observed_at: value.observed_at
157
+ };
158
+ }
159
+ function parseWindowOverlay(value) {
160
+ if (!isRecord(value)) {
161
+ return { badCells: 0, refusal: "overlay root is not an object" };
162
+ }
163
+ if (value.schema !== WINDOW_OVERLAY_SCHEMA) {
164
+ return {
165
+ badCells: 0,
166
+ refusal: `unrecognized schema ${JSON.stringify(value.schema)}`
167
+ };
168
+ }
169
+ if (typeof value.generated_at !== "string" || !Array.isArray(value.minted_provider_ids) || !value.minted_provider_ids.every((id) => typeof id === "string" && id.length > 0) || !Array.isArray(value.cells)) {
170
+ return { badCells: 0, refusal: "invalid v1 envelope" };
171
+ }
172
+ const cells = [];
173
+ let badCells = 0;
174
+ for (const rawCell of value.cells) {
175
+ if (!isRecord(rawCell) || typeof rawCell.provider_id !== "string" || rawCell.provider_id.length === 0 || typeof rawCell.model_id !== "string" || rawCell.model_id.length === 0 || !isRecord(rawCell.facts)) {
176
+ badCells++;
177
+ continue;
178
+ }
179
+ const facts = {};
180
+ let valid = true;
181
+ for (const [key, rawFact] of Object.entries(rawCell.facts)) {
182
+ const fact = parseFact(key, rawFact);
183
+ if (!fact) {
184
+ valid = false;
185
+ break;
186
+ }
187
+ facts[key] = fact;
188
+ }
189
+ if (!valid) {
190
+ badCells++;
191
+ continue;
192
+ }
193
+ cells.push({
194
+ provider_id: rawCell.provider_id,
195
+ model_id: rawCell.model_id,
196
+ facts
197
+ });
198
+ }
199
+ return {
200
+ overlay: {
201
+ schema: WINDOW_OVERLAY_SCHEMA,
202
+ generated_at: value.generated_at,
203
+ minted_provider_ids: [...value.minted_provider_ids],
204
+ cells
205
+ },
206
+ badCells
207
+ };
208
+ }
209
+ function defaultWindowOverlayPath() {
210
+ return join(getDataDir(), "fusiform", "window-overlay.json");
211
+ }
212
+ function readWindowOverlayFile(path, log = (message) => sessionLog("global", message)) {
213
+ let raw;
214
+ try {
215
+ raw = readFileSync(path, "utf8");
216
+ } catch (error) {
217
+ if (error.code === "ENOENT")
218
+ return;
219
+ log(`window-overlay: unable to read ${path}; overlay ignored`);
220
+ return;
221
+ }
222
+ let decoded;
223
+ try {
224
+ decoded = JSON.parse(raw);
225
+ } catch {
226
+ log(`window-overlay: invalid JSON in ${path}; overlay ignored`);
227
+ return;
228
+ }
229
+ const parsed = parseWindowOverlay(decoded);
230
+ if (parsed.refusal) {
231
+ log(`window-overlay: ${parsed.refusal} in ${path}; entire overlay ignored`);
232
+ return;
233
+ }
234
+ if (parsed.badCells > 0) {
235
+ log(`window-overlay: skipped ${parsed.badCells} invalid cell(s) from ${path}`);
236
+ }
237
+ return parsed.overlay;
238
+ }
239
+ function setWindowOverlayPath(path) {
240
+ configuredOverlayPath = path;
241
+ loadedOverlayPath = undefined;
242
+ loadedOverlay = undefined;
243
+ }
244
+ function getWindowOverlay() {
245
+ const path = configuredOverlayPath ?? defaultWindowOverlayPath();
246
+ if (loadedOverlayPath === path && loadedOverlay !== undefined) {
247
+ return loadedOverlay ?? undefined;
248
+ }
249
+ loadedOverlayPath = path;
250
+ loadedOverlay = readWindowOverlayFile(path) ?? null;
251
+ return loadedOverlay ?? undefined;
252
+ }
253
+ function resolveWindowOverlayFacts(providerID, modelID, overlay = getWindowOverlay()) {
254
+ if (!overlay)
255
+ return;
256
+ const modelRefs = modelRefLookupOrder(`${providerID}/${modelID}`);
257
+ const providerCandidates = new Set(modelRefs.map((ref) => ref.slice(0, ref.indexOf("/"))));
258
+ const modelCandidates = new Set([
259
+ modelID,
260
+ ...modelRefs.map((ref) => ref.slice(ref.indexOf("/") + 1))
261
+ ]);
262
+ const colon = modelID.lastIndexOf(":");
263
+ if (colon > 0)
264
+ modelCandidates.add(modelID.slice(0, colon));
265
+ const wildcardFacts = {};
266
+ const specificFacts = {};
267
+ for (const cell of overlay.cells) {
268
+ if (!providerCandidates.has(cell.provider_id))
269
+ continue;
270
+ if (cell.model_id === "*")
271
+ Object.assign(wildcardFacts, cell.facts);
272
+ else if (modelCandidates.has(cell.model_id))
273
+ Object.assign(specificFacts, cell.facts);
274
+ }
275
+ const facts = { ...wildcardFacts, ...specificFacts };
276
+ return Object.keys(facts).length > 0 ? { facts } : undefined;
277
+ }
278
+ function numericOverlayFact(overlay, key) {
279
+ const fact = overlay?.facts[key];
280
+ return fact ? scalarizeFact(fact.value) : undefined;
281
+ }
282
+ function overlayGeometry(overlay) {
283
+ const fact = overlay?.facts.geometry;
284
+ if (fact === undefined)
285
+ return;
286
+ if (fact.value.kind === "unknown")
287
+ return { kind: "unknown" };
288
+ if (fact.value.kind !== "stated")
289
+ return;
290
+ const value = fact.value.value;
291
+ return value === "shared_upfront" || value === "shared_truncating" || value === "separate" ? { kind: "stated", value } : undefined;
292
+ }
293
+ function placeholderFilteredOutput(output, context) {
294
+ if (!isFinitePositive(output))
295
+ return;
296
+ if (isFinitePositive(context) && output >= context)
297
+ return;
298
+ return output;
299
+ }
300
+ function mergePositive(overlayValue, providerValue) {
301
+ return isFinitePositive(providerValue) ? providerValue : overlayValue;
302
+ }
303
+ function logGeometryClampOnce(key, message, log) {
304
+ if (geometryClampLogSeen.has(key))
305
+ return;
306
+ geometryClampLogSeen.add(key);
307
+ (log ?? ((entry) => sessionLog("global", `window-geometry: ${entry}`)))(message);
308
+ }
309
+ function deriveWindowGeometry(providerID, modelID, catalogLimit, options = {}) {
310
+ if (!catalogLimit && !options.providerLimit)
311
+ return;
312
+ const providerLimit = options.providerLimit;
313
+ const catalogContext = isFinitePositive(catalogLimit?.context) ? catalogLimit.context : undefined;
314
+ const advertised = numericOverlayFact(options.overlay, "window.advertised");
315
+ const enforced = numericOverlayFact(options.overlay, "window.enforced");
316
+ let softContext = mergePositive(enforced ?? advertised ?? catalogContext, providerLimit?.context);
317
+ let hardContext = mergePositive(enforced ?? softContext, providerLimit?.context);
318
+ if (isFinitePositive(options.contextCap)) {
319
+ softContext = isFinitePositive(softContext) ? Math.min(softContext, options.contextCap) : options.contextCap;
320
+ hardContext = isFinitePositive(hardContext) ? Math.min(hardContext, options.contextCap) : options.contextCap;
321
+ }
322
+ const input = mergePositive(isFinitePositive(catalogLimit?.input) ? catalogLimit.input : undefined, providerLimit?.input);
323
+ if (!isFinitePositive(softContext) && !isFinitePositive(input))
324
+ return;
325
+ const catalogOutput = options.overlay === undefined && options.providerLimit === undefined ? isFinitePositive(catalogLimit?.output) ? catalogLimit.output : undefined : placeholderFilteredOutput(catalogLimit?.output, softContext);
326
+ const overlayOutput = placeholderFilteredOutput(numericOverlayFact(options.overlay, "output.enforced") ?? numericOverlayFact(options.overlay, "output.default") ?? numericOverlayFact(options.overlay, "output.advertised"), softContext);
327
+ const providerOutput = placeholderFilteredOutput(providerLimit?.output, softContext);
328
+ const output = providerOutput ?? overlayOutput ?? catalogOutput;
329
+ const geometryFact = overlayGeometry(options.overlay);
330
+ const geometry = geometryFact?.kind === "stated" ? geometryFact.value : geometryFact?.kind === "unknown" ? "shared_upfront" : PROVIDER_GEOMETRY[providerID] ?? "shared_upfront";
331
+ const geometryOverride = geometryFact?.kind === "stated" ? geometryFact.value : undefined;
332
+ let usableSoft;
333
+ let softReserve = 0;
334
+ let reserveSource = "none";
335
+ if (isFinitePositive(input) && (!isFinitePositive(softContext) || input < softContext)) {
336
+ usableSoft = input;
337
+ if (isFinitePositive(softContext)) {
338
+ softReserve = Math.max(0, softContext - input);
339
+ reserveSource = "output_catalog";
340
+ }
341
+ } else if (isFinitePositive(softContext)) {
342
+ const configuredReserve = configuredOutputReserve(options.reserveConfig, providerID, modelID);
343
+ if (configuredReserve !== undefined) {
344
+ softReserve = configuredReserve;
345
+ reserveSource = "output_config";
346
+ } else if (geometry === "separate" && (options.overlay === undefined || options.harness === "pi" || geometryOverride !== undefined)) {
347
+ softReserve = 0;
348
+ reserveSource = "none";
349
+ } else {
350
+ softReserve = output ?? 0;
351
+ reserveSource = output === undefined ? "none" : "output_catalog";
352
+ const cap = softContext * OUTPUT_RESERVE_CAP_RATIO;
353
+ softReserve = Math.min(softReserve, options.harness === "pi" || options.overlay === undefined ? cap : Math.min(cap, OPENCODE_OUTPUT_CAP));
354
+ }
355
+ const floor = Math.max(MIN_PLAUSIBLE_CONTEXT_LIMIT, softContext * 0.5);
356
+ const flooredReserve = Math.min(softReserve, Math.max(0, softContext - floor));
357
+ if (flooredReserve < softReserve) {
358
+ logGeometryClampOnce(`soft-floor|${providerID}/${modelID}|${softReserve}|${flooredReserve}`, `output reserve clamped by the half-window floor for ${providerID}/${modelID}: reserve ${softReserve} → ${flooredReserve} (catalog context/output pair is contradictory)`, options.log);
359
+ }
360
+ softReserve = flooredReserve;
361
+ usableSoft = Math.floor(softContext - softReserve);
362
+ } else {
363
+ usableSoft = input;
364
+ }
365
+ const hardWindow = hardContext ?? softContext ?? input;
366
+ if (!isFinitePositive(hardWindow))
367
+ return;
368
+ let usableHard;
369
+ if (geometry === "separate") {
370
+ usableHard = hardWindow;
371
+ } else if (geometry === "shared_truncating") {
372
+ usableHard = hardWindow - PROMPT_WALL_MARGIN;
373
+ } else if (options.harness === "pi" && providerID !== "openai-codex") {
374
+ usableHard = hardWindow - PI_OUTPUT_FLOOR;
375
+ } else if (options.harness === "pi") {
376
+ usableHard = hardWindow - (output ?? OPENCODE_OUTPUT_CAP);
377
+ } else {
378
+ const requestedOutput = Math.min(output ?? OPENCODE_OUTPUT_CAP, OPENCODE_OUTPUT_CAP);
379
+ usableHard = hardWindow - requestedOutput;
380
+ }
381
+ usableHard = Math.max(MIN_PLAUSIBLE_CONTEXT_LIMIT, Math.floor(usableHard));
382
+ if (usableHard < usableSoft) {
383
+ logGeometryClampOnce(`${providerID}/${modelID}|${usableSoft}|${usableHard}`, `usable hard limit clamped for ${providerID}/${modelID}: ${usableHard} → ${usableSoft} (overlay/provider inversion)`, options.log);
384
+ usableHard = usableSoft;
385
+ }
386
+ return {
387
+ usableSoft,
388
+ usableHard,
389
+ geometry,
390
+ derivation: {
391
+ window: Math.floor(softContext ?? usableSoft),
392
+ reserve: Math.floor(Math.max(0, (softContext ?? usableSoft) - usableSoft)),
393
+ reserveSource,
394
+ geometry
395
+ }
396
+ };
397
+ }
398
+ function formatWindowDerivationLine(inputTokens, result) {
399
+ const percentage = result.usableSoft > 0 ? inputTokens / result.usableSoft * 100 : 0;
400
+ const reserveLabel = result.derivation.reserveSource === "wall_margin" ? "wall margin" : result.derivation.reserveSource === "none" ? "reserve" : "output reserve";
401
+ return `Context: ${formatCompactTokens(inputTokens)} / ${formatCompactTokens(result.usableSoft)} usable (${percentage.toFixed(1)}%) — window ${formatCompactTokens(result.derivation.window)} − ${formatCompactTokens(result.derivation.reserve)} ${reserveLabel} [${result.geometry}]`;
402
+ }
403
+ function formatCompactTokens(value) {
404
+ if (Math.abs(value) >= 1e6) {
405
+ return `${(value / 1e6).toFixed(1).replace(/\.0$/, "")}m`;
406
+ }
407
+ if (Math.abs(value) >= 1000) {
408
+ return `${(value / 1000).toFixed(1).replace(/\.0$/, "")}k`;
409
+ }
410
+ return Math.round(value).toLocaleString();
411
+ }
412
+
413
+ // src/shared/models-dev-cache.ts
414
+ var MIN_SANE_LIMIT = 20000;
415
+ var MAX_SANE_LIMIT = 3000000;
416
+ function isSaneLimit(limit) {
417
+ return typeof limit === "number" && limit >= MIN_SANE_LIMIT && limit <= MAX_SANE_LIMIT;
418
+ }
419
+ var SEPARATE_OUTPUT_QUOTA_PROVIDERS = new Set(["google", "google-antigravity"]);
420
+ var MIN_PLAUSIBLE_CONTEXT_LIMIT2 = 1024;
421
+ var OUTPUT_RESERVE_CAP_RATIO2 = 0.25;
422
+ var outputReserveConfig;
423
+ var reserveClampLogSeen = new Set;
424
+ var apiCache = null;
425
+ var apiLoadedAt = 0;
426
+ var persistSeedLoaded = false;
427
+ function persistFilePath() {
428
+ return join2(getMagicContextStorageDir(), `model-context-limits-${getHarness()}.json`);
429
+ }
430
+ function loadPersistedApiCacheOnce() {
431
+ if (persistSeedLoaded || apiCache !== null)
432
+ return;
433
+ persistSeedLoaded = true;
434
+ try {
435
+ const raw = readFileSync2(persistFilePath(), "utf-8");
436
+ const obj = JSON.parse(raw);
437
+ const map = new Map;
438
+ for (const [key, persisted] of Object.entries(obj)) {
439
+ const limit = typeof persisted === "number" ? persisted : persisted.limit;
440
+ const contextLimit = typeof persisted === "number" ? undefined : persisted.contextLimit;
441
+ const inputLimit = typeof persisted === "number" ? undefined : persisted.inputLimit;
442
+ const outputLimit = typeof persisted === "number" ? undefined : persisted.outputLimit;
443
+ const vision = typeof persisted === "number" ? false : persisted.vision === true;
444
+ if (isSaneLimit(contextLimit) || isSaneLimit(limit)) {
445
+ map.set(key, {
446
+ limit: isSaneLimit(limit) ? limit : undefined,
447
+ contextLimit: isSaneLimit(contextLimit) ? contextLimit : undefined,
448
+ inputLimit: isSaneLimit(inputLimit) ? inputLimit : undefined,
449
+ outputLimit: isFinitePositive2(outputLimit) ? outputLimit : undefined,
450
+ vision
451
+ });
452
+ }
453
+ }
454
+ if (map.size > 0) {
455
+ apiCache = map;
456
+ sessionLog("global", `models-dev-cache: seeded ${map.size} entries from persisted cache (cold start)`);
457
+ }
458
+ } catch {}
459
+ }
460
+ function persistApiCache() {
461
+ if (!apiCache)
462
+ return;
463
+ const obj = {};
464
+ for (const [key, value] of apiCache) {
465
+ if (isSaneLimit(value.limit)) {
466
+ obj[key] = {
467
+ limit: value.limit,
468
+ contextLimit: isSaneLimit(value.contextLimit) ? value.contextLimit : undefined,
469
+ inputLimit: isSaneLimit(value.inputLimit) ? value.inputLimit : undefined,
470
+ outputLimit: isFinitePositive2(value.outputLimit) ? value.outputLimit : undefined,
471
+ vision: value.vision === true
472
+ };
473
+ }
474
+ }
475
+ try {
476
+ const dir = getMagicContextStorageDir();
477
+ mkdirSync(dir, { recursive: true });
478
+ const target = persistFilePath();
479
+ const tmp = `${target}.${process.pid}.tmp`;
480
+ if (shouldEnforcePrivateStoragePermissions()) {
481
+ writeFileSync(tmp, JSON.stringify(obj), { encoding: "utf-8", mode: 384 });
482
+ } else {
483
+ writeFileSync(tmp, JSON.stringify(obj), { encoding: "utf-8" });
484
+ }
485
+ renameSync(tmp, target);
486
+ } catch {}
487
+ }
488
+ function isFinitePositive2(value) {
489
+ return typeof value === "number" && Number.isFinite(value) && value > 0;
490
+ }
491
+ function modelKeyLookupOrder2(providerID, modelID) {
492
+ const candidates = [...modelRefLookupOrder(`${providerID}/${modelID}`), modelID];
493
+ const colon = modelID.lastIndexOf(":");
494
+ if (colon > 0) {
495
+ const bareModel = modelID.slice(0, colon);
496
+ candidates.push(...modelRefLookupOrder(`${providerID}/${bareModel}`), bareModel);
497
+ }
498
+ return [...new Set(candidates)];
499
+ }
500
+ function configuredOutputReserve2(config, providerID, modelID) {
501
+ if (typeof config === "number")
502
+ return Number.isFinite(config) && config >= 0 ? config : undefined;
503
+ if (!config)
504
+ return;
505
+ for (const candidate of modelKeyLookupOrder2(providerID, modelID)) {
506
+ const value = config[candidate];
507
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0)
508
+ return value;
509
+ }
510
+ return Number.isFinite(config.default) && config.default >= 0 ? config.default : undefined;
511
+ }
512
+ function logReserveClampOnce(key, message) {
513
+ if (reserveClampLogSeen.has(key))
514
+ return;
515
+ reserveClampLogSeen.add(key);
516
+ sessionLog("global", `models-dev-cache: ${message}`);
517
+ }
518
+ function setOutputReserveConfig(config) {
519
+ outputReserveConfig = config;
520
+ }
521
+ function resolveLimit(limit, providerID, modelID, reserveConfig = outputReserveConfig) {
522
+ if (!limit)
523
+ return;
524
+ const context = isFinitePositive2(limit.context) ? limit.context : undefined;
525
+ const input = isFinitePositive2(limit.input) ? limit.input : undefined;
526
+ if (input !== undefined && (context === undefined || input < context))
527
+ return input;
528
+ if (context === undefined)
529
+ return;
530
+ const configuredReserve = configuredOutputReserve2(reserveConfig, providerID, modelID);
531
+ let reserve;
532
+ if (configuredReserve !== undefined) {
533
+ reserve = configuredReserve;
534
+ } else if (SEPARATE_OUTPUT_QUOTA_PROVIDERS.has(providerID)) {
535
+ reserve = 0;
536
+ } else {
537
+ const output = isFinitePositive2(limit.output) ? limit.output : 0;
538
+ const cap = context * OUTPUT_RESERVE_CAP_RATIO2;
539
+ reserve = Math.min(output, cap);
540
+ if (output > cap) {
541
+ logReserveClampOnce(`cap|${providerID}/${modelID}|${context}|${output}`, `output reserve capped at 25% for ${providerID}/${modelID}: ${output} → ${cap}`);
542
+ }
543
+ }
544
+ const floor = Math.max(MIN_PLAUSIBLE_CONTEXT_LIMIT2, context * 0.5);
545
+ const maxReserve = Math.max(0, context - floor);
546
+ if (reserve > maxReserve) {
547
+ logReserveClampOnce(`floor|${providerID}/${modelID}|${context}|${reserve}`, `output reserve clamped for ${providerID}/${modelID}: ${reserve} → ${maxReserve} (usable floor ${floor})`);
548
+ reserve = maxReserve;
549
+ }
550
+ return Math.floor(context - reserve);
551
+ }
552
+ function setCachedModelMetadata(cache, key, model) {
553
+ const contextLimit = model?.limit?.context;
554
+ const inputLimit = model?.limit?.input;
555
+ const outputLimit = model?.limit?.output;
556
+ const rawLimit = isSaneLimit(contextLimit) ? contextLimit : isSaneLimit(inputLimit) ? inputLimit : undefined;
557
+ if (rawLimit === undefined)
558
+ return;
559
+ const values = [model?.capabilities, model?.modalities, model?.input, model?.attachment];
560
+ const vision = values.some((value2) => JSON.stringify(value2 ?? "").toLowerCase().includes("image") || JSON.stringify(value2 ?? "").toLowerCase().includes("vision"));
561
+ const value = {
562
+ limit: rawLimit,
563
+ contextLimit: isSaneLimit(contextLimit) ? contextLimit : undefined,
564
+ inputLimit: isSaneLimit(inputLimit) ? inputLimit : undefined,
565
+ outputLimit: isFinitePositive2(outputLimit) ? outputLimit : undefined,
566
+ vision
567
+ };
568
+ cache.set(key, value);
569
+ const modes = model?.experimental?.modes;
570
+ if (modes && typeof modes === "object") {
571
+ for (const mode of Object.keys(modes)) {
572
+ cache.set(`${key}-${mode}`, value);
573
+ }
574
+ }
575
+ }
576
+ async function refreshModelLimitsFromApi(client, options) {
577
+ const attempts = Math.max(1, (options?.retries ?? 0) + 1);
578
+ const delayMs = options?.retryDelayMs ?? 1000;
579
+ for (let attempt = 1;attempt <= attempts; attempt++) {
580
+ const ok = await refreshModelLimitsOnce(client);
581
+ if (ok)
582
+ return;
583
+ if (attempt < attempts) {
584
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
585
+ }
586
+ }
587
+ }
588
+ var authRewarmDone = false;
589
+ async function refreshModelLimitsAfterAuthOnce(client) {
590
+ if (authRewarmDone)
591
+ return;
592
+ authRewarmDone = true;
593
+ const ok = await refreshModelLimitsOnce(client);
594
+ if (!ok)
595
+ authRewarmDone = false;
596
+ }
597
+ async function refreshModelLimitsOnce(client) {
598
+ try {
599
+ const result = await client.config.providers();
600
+ const data = result.data;
601
+ const providers = data?.providers;
602
+ if (!Array.isArray(providers) || providers.length === 0) {
603
+ sessionLog("global", "models-dev-cache: API refresh returned no providers payload (will retry if attempts remain)");
604
+ return false;
605
+ }
606
+ const map = new Map;
607
+ for (const entry of providers) {
608
+ const p = entry;
609
+ if (!p?.id || !p.models || typeof p.models !== "object")
610
+ continue;
611
+ for (const [modelId, model] of Object.entries(p.models)) {
612
+ setCachedModelMetadata(map, `${p.id}/${modelId}`, model);
613
+ }
614
+ }
615
+ const previousSize = apiCache?.size ?? null;
616
+ apiCache = map;
617
+ apiLoadedAt = Date.now();
618
+ persistApiCache();
619
+ if (previousSize === null) {
620
+ sessionLog("global", `models-dev-cache: API layer loaded ${map.size} model metadata entries`);
621
+ } else if (previousSize !== map.size) {
622
+ sessionLog("global", `models-dev-cache: API layer loaded ${map.size} model metadata entries (was ${previousSize})`);
623
+ }
624
+ return true;
625
+ } catch (error) {
626
+ sessionLog("global", "models-dev-cache: API refresh failed:", error instanceof Error ? error.message : String(error));
627
+ return false;
628
+ }
629
+ }
630
+ function getSdkWindowGeometry(providerID, modelID, detectedContextLimit, options) {
631
+ loadPersistedApiCacheOnce();
632
+ const metadata = lookupMetadataWithTagFallback(apiCache, providerID, modelID);
633
+ if (!metadata)
634
+ return;
635
+ const rawContext = metadata.contextLimit ?? metadata.limit;
636
+ const promptOnlyDetected = options?.detectedLimitProvenance === "prompt_only" && isFinitePositive2(detectedContextLimit) ? detectedContextLimit : undefined;
637
+ const result = deriveWindowGeometry(providerID, modelID, {
638
+ context: rawContext,
639
+ input: metadata.inputLimit,
640
+ output: metadata.outputLimit
641
+ }, {
642
+ overlay: resolveWindowOverlayFacts(providerID, modelID, getWindowOverlay()),
643
+ reserveConfig: outputReserveConfig,
644
+ harness: options?.harness ?? "opencode",
645
+ contextCap: promptOnlyDetected === undefined && isFinitePositive2(detectedContextLimit) ? detectedContextLimit : undefined
646
+ });
647
+ if (!result || promptOnlyDetected === undefined)
648
+ return result;
649
+ const usableSoft = promptOnlyDetected;
650
+ return {
651
+ ...result,
652
+ usableSoft,
653
+ usableHard: Math.max(usableSoft, Math.min(result.usableHard, promptOnlyDetected))
654
+ };
655
+ }
656
+ function getSdkContextLimit(providerID, modelID, detectedContextLimit, options) {
657
+ if (options?.reservation !== "none") {
658
+ return getSdkWindowGeometry(providerID, modelID, detectedContextLimit, {
659
+ detectedLimitProvenance: options?.detectedLimitProvenance
660
+ })?.usableSoft;
661
+ }
662
+ loadPersistedApiCacheOnce();
663
+ const metadata = lookupMetadataWithTagFallback(apiCache, providerID, modelID);
664
+ if (!metadata)
665
+ return;
666
+ const rawContext = metadata.contextLimit ?? metadata.limit;
667
+ const promptOnlyDetected = options?.detectedLimitProvenance === "prompt_only" && isFinitePositive2(detectedContextLimit) ? detectedContextLimit : undefined;
668
+ const context = promptOnlyDetected === undefined && isFinitePositive2(detectedContextLimit) && isFinitePositive2(rawContext) ? Math.min(rawContext, detectedContextLimit) : promptOnlyDetected === undefined && isFinitePositive2(detectedContextLimit) ? detectedContextLimit : rawContext;
669
+ const inputCandidates = [metadata.inputLimit, promptOnlyDetected].filter(isFinitePositive2);
670
+ const input = inputCandidates.length > 0 ? Math.min(...inputCandidates) : undefined;
671
+ return resolveLimit({
672
+ context,
673
+ input,
674
+ output: metadata.outputLimit
675
+ }, providerID, modelID, options?.reservation === "none" ? 0 : undefined);
676
+ }
677
+ function modelSupportsVision(providerID, modelID) {
678
+ loadPersistedApiCacheOnce();
679
+ if (!apiCache)
680
+ return false;
681
+ const exact = apiCache.get(`${providerID}/${modelID}`);
682
+ if (exact?.vision === true)
683
+ return true;
684
+ const colon = modelID.lastIndexOf(":");
685
+ return colon > 0 ? apiCache.get(`${providerID}/${modelID.slice(0, colon)}`)?.vision === true : false;
686
+ }
687
+ function lookupMetadataWithTagFallback(cache, providerID, modelID) {
688
+ if (!cache)
689
+ return;
690
+ const exact = cache.get(`${providerID}/${modelID}`);
691
+ if (exact)
692
+ return exact;
693
+ const colonIdx = modelID.lastIndexOf(":");
694
+ if (colonIdx > 0) {
695
+ return cache.get(`${providerID}/${modelID.slice(0, colonIdx)}`);
696
+ }
697
+ return;
698
+ }
699
+
700
+ // src/hooks/magic-context/derive-budgets.ts
701
+ var TRIGGER_BUDGET_PERCENTAGE = 0.05;
702
+ var TRIGGER_BUDGET_MIN = 5000;
703
+ var TRIGGER_BUDGET_MAX = 50000;
704
+ var HISTORIAN_CHUNK_PERCENTAGE = 0.25;
705
+ var HISTORIAN_CHUNK_MIN = 8000;
706
+ var HISTORIAN_CHUNK_MAX = 50000;
707
+ var DEFAULT_HISTORIAN_CONTEXT_FALLBACK = 128000;
708
+ function deriveTriggerBudget(mainContextLimit, executeThresholdPercentage) {
709
+ if (!Number.isFinite(mainContextLimit) || mainContextLimit <= 0) {
710
+ return TRIGGER_BUDGET_MIN;
711
+ }
712
+ const thresholdFraction = Math.max(0, executeThresholdPercentage) / 100;
713
+ const usable = mainContextLimit * thresholdFraction;
714
+ const derived = Math.round(usable * TRIGGER_BUDGET_PERCENTAGE);
715
+ return Math.max(TRIGGER_BUDGET_MIN, Math.min(TRIGGER_BUDGET_MAX, derived));
716
+ }
717
+ function deriveHistorianChunkTokens(historianContextLimit) {
718
+ if (!Number.isFinite(historianContextLimit) || historianContextLimit <= 0) {
719
+ return HISTORIAN_CHUNK_MIN;
720
+ }
721
+ const derived = Math.round(historianContextLimit * HISTORIAN_CHUNK_PERCENTAGE);
722
+ return Math.max(HISTORIAN_CHUNK_MIN, Math.min(HISTORIAN_CHUNK_MAX, derived));
723
+ }
724
+ function resolveHistorianContextLimit(historianModelOverride) {
725
+ if (typeof historianModelOverride === "string" && historianModelOverride.includes("/")) {
726
+ const [providerID, ...rest] = historianModelOverride.split("/");
727
+ const modelID = rest.join("/");
728
+ if (providerID && modelID) {
729
+ const limit = getSdkContextLimit(providerID, modelID);
730
+ if (typeof limit === "number" && limit > 0)
731
+ return limit;
732
+ }
733
+ return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
734
+ }
735
+ if (typeof historianModelOverride === "string" && historianModelOverride.trim() !== "") {
736
+ console.warn(`[magic-context] historian.model "${historianModelOverride}" lacks provider prefix ("provider/model-id"); using the default context limit for chunk-budget derivation.`);
737
+ }
738
+ return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
739
+ }
740
+
741
+ export { setStoragePrivatePermissionEnforcement, shouldEnforcePrivateStoragePermissions, setWindowOverlayPath, formatWindowDerivationLine, isSaneLimit, setOutputReserveConfig, refreshModelLimitsFromApi, refreshModelLimitsAfterAuthOnce, getSdkWindowGeometry, getSdkContextLimit, modelSupportsVision, deriveTriggerBudget, deriveHistorianChunkTokens, resolveHistorianContextLimit };