@cortexkit/opencode-magic-context 0.37.0 → 0.38.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 (94) hide show
  1. package/README.md +2 -0
  2. package/dist/{derive-budgets-0f8xpyw2.js → derive-budgets-da4yg3tz.js} +1 -1
  3. package/dist/features/magic-context/dreamer/classify-prompt.d.ts +5 -1
  4. package/dist/features/magic-context/dreamer/classify-prompt.d.ts.map +1 -1
  5. package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
  6. package/dist/features/magic-context/dreamer/manifest-parser.d.ts +8 -0
  7. package/dist/features/magic-context/dreamer/manifest-parser.d.ts.map +1 -1
  8. package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts +7 -1
  9. package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts.map +1 -1
  10. package/dist/features/magic-context/dreamer/map-memories.d.ts +18 -0
  11. package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
  12. package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts +5 -0
  13. package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts.map +1 -1
  14. package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
  15. package/dist/features/magic-context/dreamer/verify-prompt.d.ts +5 -1
  16. package/dist/features/magic-context/dreamer/verify-prompt.d.ts.map +1 -1
  17. package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
  18. package/dist/features/magic-context/fail-closed-block.d.ts +6 -1
  19. package/dist/features/magic-context/fail-closed-block.d.ts.map +1 -1
  20. package/dist/features/magic-context/reclaim-protection.d.ts +13 -0
  21. package/dist/features/magic-context/reclaim-protection.d.ts.map +1 -0
  22. package/dist/features/magic-context/storage-db.d.ts +6 -1
  23. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  24. package/dist/features/magic-context/storage-meta-persisted.d.ts +7 -6
  25. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  26. package/dist/features/magic-context/storage-tags.d.ts +13 -5
  27. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  28. package/dist/hooks/auto-update-checker/checker.d.ts +8 -1
  29. package/dist/hooks/auto-update-checker/checker.d.ts.map +1 -1
  30. package/dist/hooks/auto-update-checker/index.d.ts.map +1 -1
  31. package/dist/hooks/magic-context/channel2-cycle.d.ts +16 -0
  32. package/dist/hooks/magic-context/channel2-cycle.d.ts.map +1 -0
  33. package/dist/hooks/magic-context/channel2-delivery.d.ts +3 -9
  34. package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
  35. package/dist/hooks/magic-context/command-handler.d.ts +2 -0
  36. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  37. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts +33 -103
  38. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts.map +1 -1
  39. package/dist/hooks/magic-context/emergency-drop.d.ts.map +1 -1
  40. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  41. package/dist/hooks/magic-context/execute-status.d.ts +2 -1
  42. package/dist/hooks/magic-context/execute-status.d.ts.map +1 -1
  43. package/dist/hooks/magic-context/fold-execution-gate.d.ts +2 -0
  44. package/dist/hooks/magic-context/fold-execution-gate.d.ts.map +1 -0
  45. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  46. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  47. package/dist/hooks/magic-context/live-session-state.d.ts +3 -0
  48. package/dist/hooks/magic-context/live-session-state.d.ts.map +1 -1
  49. package/dist/hooks/magic-context/rust-mode-transform.d.ts +4 -2
  50. package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
  51. package/dist/hooks/magic-context/strip-content.d.ts +10 -8
  52. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  53. package/dist/hooks/magic-context/supersession-reclaim.d.ts.map +1 -1
  54. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  55. package/dist/hooks/magic-context/tail-hygiene-walk.d.ts +78 -0
  56. package/dist/hooks/magic-context/tail-hygiene-walk.d.ts.map +1 -0
  57. package/dist/hooks/magic-context/tool-reclaim.d.ts.map +1 -1
  58. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +3 -0
  59. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  60. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  61. package/dist/{index-q0t189ay.js → index-9zhhvr4w.js} +27 -35
  62. package/dist/{index-0ze9p983.js → index-mmvhrqqb.js} +1428 -191
  63. package/dist/index.js +604 -763
  64. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  65. package/dist/plugin/messages-transform.d.ts.map +1 -1
  66. package/dist/plugin/rpc-handlers.d.ts +2 -0
  67. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  68. package/dist/{recomp-orchestrator-c3ff16k7.js → recomp-orchestrator-jsde4ygw.js} +2 -2
  69. package/dist/shared/models-dev-cache.d.ts +2 -0
  70. package/dist/shared/models-dev-cache.d.ts.map +1 -1
  71. package/dist/shared/rpc-server.d.ts.map +1 -1
  72. package/dist/shared/rpc-types.d.ts +15 -0
  73. package/dist/shared/rpc-types.d.ts.map +1 -1
  74. package/dist/shared/rpc-utils.d.ts +9 -0
  75. package/dist/shared/rpc-utils.d.ts.map +1 -1
  76. package/dist/shared/tail-hygiene-status.d.ts +15 -0
  77. package/dist/shared/tail-hygiene-status.d.ts.map +1 -0
  78. package/dist/shared/window-geometry.d.ts +3 -2
  79. package/dist/shared/window-geometry.d.ts.map +1 -1
  80. package/package.json +3 -1
  81. package/src/config/schema/magic-context.ts +2 -2
  82. package/src/shared/models-dev-cache.test.ts +25 -0
  83. package/src/shared/models-dev-cache.ts +5 -4
  84. package/src/shared/rpc-server.ts +1 -0
  85. package/src/shared/rpc-types.ts +16 -0
  86. package/src/shared/rpc-utils.test.ts +25 -0
  87. package/src/shared/rpc-utils.ts +80 -0
  88. package/src/shared/tail-hygiene-status.ts +57 -0
  89. package/src/shared/window-geometry.test.ts +34 -1
  90. package/src/shared/window-geometry.ts +37 -40
  91. package/src/tui/index.tsx +11 -1
  92. package/src/tui/slots/sidebar-content.tsx +17 -5
  93. package/src/tui-compiled/index.tsx +311 -289
  94. package/src/tui-compiled/slots/sidebar-content.tsx +316 -287
@@ -2,7 +2,7 @@ import { afterEach, describe, expect, test } from "bun:test";
2
2
  import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
- import { resolveLimit } from "./models-dev-cache";
5
+ import { resolveLimit, resolveOutputReserve } from "./models-dev-cache";
6
6
  import {
7
7
  deriveWindowGeometry,
8
8
  formatWindowDerivationLine,
@@ -240,6 +240,39 @@ describe("window geometry", () => {
240
240
  expect(result?.usableSoft).toBe(1_016_576);
241
241
  });
242
242
 
243
+ test("output_reserve forms override a pre-carved input and overlay output facts", () => {
244
+ const data = overlay("openai-codex", "gpt-5.6-sol", {
245
+ "output.enforced": fact({ kind: "stated", value: 68_000 }),
246
+ });
247
+ const cases = [
248
+ 16_384,
249
+ { default: 16_384 },
250
+ { default: 32_000, "openai-codex/gpt-5.6-sol": 16_384 },
251
+ ] as const;
252
+
253
+ for (const reserveConfig of cases) {
254
+ const result = deriveWindowGeometry(
255
+ "openai-codex",
256
+ "gpt-5.6-sol",
257
+ { context: 400_000, input: 272_000, output: 128_000 },
258
+ {
259
+ overlay: resolveWindowOverlayFacts("openai-codex", "gpt-5.6-sol", data),
260
+ outputReserveOverride: resolveOutputReserve(
261
+ "openai-codex",
262
+ "gpt-5.6-sol",
263
+ reserveConfig,
264
+ ),
265
+ },
266
+ );
267
+ expect(result?.usableSoft).toBe(272_000 - 16_384);
268
+ expect(result?.derivation).toMatchObject({
269
+ window: 272_000,
270
+ reserve: 16_384,
271
+ reserveSource: "output_config",
272
+ });
273
+ }
274
+ });
275
+
243
276
  test("provider hook wins over overlay, while overlay beats catalog", () => {
244
277
  const data = overlay("provider", "model", {
245
278
  "window.enforced": fact({ kind: "stated", value: 300_000 }),
@@ -3,7 +3,7 @@ import { join } from "node:path";
3
3
  import { getDataDir } from "./data-path";
4
4
  import { modelRefLookupOrder } from "./harness-provider-map";
5
5
  import { sessionLog } from "./logger";
6
- import type { ModelLimit, OutputReserveConfig } from "./models-dev-cache";
6
+ import type { ModelLimit } from "./models-dev-cache";
7
7
 
8
8
  export const WINDOW_OVERLAY_SCHEMA = "fusiform-window-overlay/v1";
9
9
  export const PROMPT_WALL_MARGIN = 4_096;
@@ -88,7 +88,8 @@ export interface DeriveWindowGeometryOptions {
88
88
  overlay?: ResolvedWindowOverlayFacts;
89
89
  /** A provider/auth hook has higher precedence than the overlay, field by field. */
90
90
  providerLimit?: ModelLimit;
91
- reserveConfig?: OutputReserveConfig;
91
+ /** Resolved user override; higher precedence than catalog, overlay, and geometry defaults. */
92
+ outputReserveOverride?: number;
92
93
  harness?: "opencode" | "pi";
93
94
  /** Detected overflow remains the final downward cap regardless of other sources. */
94
95
  contextCap?: number;
@@ -139,32 +140,6 @@ function isFinitePositive(value: unknown): value is number {
139
140
  return typeof value === "number" && Number.isFinite(value) && value > 0;
140
141
  }
141
142
 
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
143
  /** Convert the ratified tagged numeric union into the conservative scalar used by derivation. */
169
144
  export function scalarizeFact(value: WindowOverlayFactValue): number | undefined {
170
145
  if (value.kind === "stated") return isFinitePositive(value.value) ? value.value : undefined;
@@ -509,25 +484,46 @@ export function deriveWindowGeometry(
509
484
  : (PROVIDER_GEOMETRY[providerID] ?? "shared_upfront");
510
485
  const geometryOverride = geometryFact?.kind === "stated" ? geometryFact.value : undefined;
511
486
 
487
+ const preCarvedInput =
488
+ isFinitePositive(input) && (!isFinitePositive(softContext) || input < softContext);
489
+ const outputReserveOverride =
490
+ typeof options.outputReserveOverride === "number" &&
491
+ Number.isFinite(options.outputReserveOverride) &&
492
+ options.outputReserveOverride >= 0
493
+ ? options.outputReserveOverride
494
+ : undefined;
495
+ let derivationWindow = softContext ?? input;
512
496
  let usableSoft: number;
513
497
  let softReserve = 0;
514
498
  let reserveSource: WindowReserveSource = "none";
515
- if (isFinitePositive(input) && (!isFinitePositive(softContext) || input < softContext)) {
499
+ if (outputReserveOverride !== undefined) {
500
+ // An explicit user reserve owns the usable-window carve. When the
501
+ // provider also publishes a smaller input cap, use that cap as the
502
+ // window; treating the cap as final would silently ignore the config.
503
+ const reserveWindow = preCarvedInput ? input : (softContext ?? input);
504
+ if (!isFinitePositive(reserveWindow)) return undefined;
505
+ derivationWindow = reserveWindow;
506
+ softReserve = outputReserveOverride;
507
+ reserveSource = "output_config";
508
+ const floor = Math.max(MIN_PLAUSIBLE_CONTEXT_LIMIT, reserveWindow * 0.5);
509
+ const flooredReserve = Math.min(softReserve, Math.max(0, reserveWindow - floor));
510
+ if (flooredReserve < softReserve) {
511
+ logGeometryClampOnce(
512
+ `soft-floor|${providerID}/${modelID}|${softReserve}|${flooredReserve}`,
513
+ `output reserve clamped by the half-window floor for ${providerID}/${modelID}: reserve ${softReserve} → ${flooredReserve}`,
514
+ options.log,
515
+ );
516
+ }
517
+ softReserve = flooredReserve;
518
+ usableSoft = Math.floor(reserveWindow - softReserve);
519
+ } else if (preCarvedInput) {
516
520
  usableSoft = input;
517
521
  if (isFinitePositive(softContext)) {
518
522
  softReserve = Math.max(0, softContext - input);
519
523
  reserveSource = "output_catalog";
520
524
  }
521
525
  } 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 (
526
+ if (
531
527
  geometry === "separate" &&
532
528
  (options.overlay === undefined ||
533
529
  options.harness === "pi" ||
@@ -591,13 +587,14 @@ export function deriveWindowGeometry(
591
587
  usableHard = usableSoft;
592
588
  }
593
589
 
590
+ const resolvedWindow = derivationWindow ?? usableSoft;
594
591
  return {
595
592
  usableSoft,
596
593
  usableHard,
597
594
  geometry,
598
595
  derivation: {
599
- window: Math.floor(softContext ?? usableSoft),
600
- reserve: Math.floor(Math.max(0, (softContext ?? usableSoft) - usableSoft)),
596
+ window: Math.floor(resolvedWindow),
597
+ reserve: Math.floor(Math.max(0, resolvedWindow - usableSoft)),
601
598
  reserveSource,
602
599
  geometry,
603
600
  },
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 { formatTailHygiene } from "../shared/tail-hygiene-status"
14
15
  import { formatWindowDerivationLine } from "../shared/window-geometry"
15
16
  import { compactionOffSidebarRows, nativeCompactionContextLabel } from "./compaction-off"
16
17
  import { isCompactionEnabled } from "../config/agent-disable"
@@ -202,7 +203,7 @@ const StatusDialog = (props: { api: TuiPluginApi; s: StatusDetail }) => {
202
203
  segs.push({ label: "User Profile", tokens: d.profileTokens, color: COLORS.profile })
203
204
 
204
205
  if (d.conversationTokens > 0)
205
- segs.push({ label: "Conversation", tokens: d.conversationTokens, color: COLORS.conversation })
206
+ segs.push({ label: "Conversation*", tokens: d.conversationTokens, color: COLORS.conversation })
206
207
  if (d.toolCallTokens > 0)
207
208
  segs.push({ label: "Tool Calls", tokens: d.toolCallTokens, color: COLORS.toolCalls })
208
209
  if (d.toolDefinitionTokens > 0)
@@ -271,6 +272,15 @@ const StatusDialog = (props: { api: TuiPluginApi; s: StatusDetail }) => {
271
272
  </box>
272
273
  )
273
274
  })}
275
+ <text fg={t().textMuted}>* Conversation includes Reasoning; hygiene excludes it</text>
276
+ {s().tailHygiene !== undefined && (
277
+ <R
278
+ t={t()}
279
+ l="Hygiene"
280
+ v={formatTailHygiene(s().tailHygiene!)}
281
+ fg={s().tailHygiene!.evaluable ? t().accent : t().warning}
282
+ />
283
+ )}
274
284
  </box>
275
285
 
276
286
  {/* Recomp / session-upgrade live progress (full width, only while
@@ -5,6 +5,7 @@ import packageJson from "../../../package.json"
5
5
  import { badgeTextColor } from '../badge-contrast';
6
6
  import { loadSidebarSnapshot, type SidebarSnapshot } from "../data/context-db"
7
7
  import { formatThresholdPercent } from "../../shared/format-threshold"
8
+ import { formatTailHygiene } from "../../shared/tail-hygiene-status"
8
9
  import { compactionOffSidebarRows, nativeCompactionContextLabel } from "../compaction-off"
9
10
  import {
10
11
  computeEffectiveOrder,
@@ -239,7 +240,7 @@ const TokenBreakdown = (props: {
239
240
  key: "conv",
240
241
  tokens: s.conversationTokens,
241
242
  color: COLORS.conversation,
242
- label: "Conversation",
243
+ label: "Conversation*",
243
244
  })
244
245
 
245
246
  // Tool Calls = tool_use/tool_result/tool/tool-invocation parts in messages
@@ -320,6 +321,7 @@ const TokenBreakdown = (props: {
320
321
  </box>
321
322
  )
322
323
  })}
324
+ <text fg={props.theme.textMuted}>* includes Reasoning; hygiene excludes it</text>
323
325
  </box>
324
326
  )}
325
327
  </box>
@@ -787,16 +789,26 @@ const SidebarContent = (props: {
787
789
  <b>{s()!.usagePercentage.toFixed(1)}%</b> / {formatThresholdPercent(s()!.executeThreshold)}%{s()!.executeThresholdClamped ? "*" : ""}
788
790
  </text>
789
791
  )}
790
- {/* Right: absolute token usage vs the model's
791
- full context window (separate from the
792
- execute threshold so users still know how
793
- much headroom remains beyond compaction). */}
792
+ {/* Right: absolute token usage against the usable
793
+ scheduler window — the same denominator as the
794
+ percentage and nudge/trigger scheduling. */}
794
795
  <text fg={contextSummaryColor()}>
795
796
  {compactTokens(s()!.inputTokens)} / {compactTokens(s()!.contextLimit)}
796
797
  </text>
797
798
  </box>
798
799
  )}
799
800
  <TokenBreakdown theme={props.theme} snapshot={s()!} collapsed={collapsed()} />
801
+ {!collapsed() && (
802
+ <text fg={props.theme.textMuted}>Conversation includes reasoning estimates; hygiene excludes reasoning.</text>
803
+ )}
804
+ {s()!.tailHygiene !== undefined && (
805
+ <StatRow
806
+ theme={props.theme}
807
+ label="Hygiene"
808
+ value={formatTailHygiene(s()!.tailHygiene!)}
809
+ warning={!s()!.tailHygiene!.evaluable}
810
+ />
811
+ )}
800
812
  </box>
801
813
  )}
802
814