@cortexkit/opencode-magic-context 0.32.0 → 0.32.1

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 (107) hide show
  1. package/README.md +3 -3
  2. package/dist/features/magic-context/dreamer/cron.d.ts +2 -2
  3. package/dist/features/magic-context/dreamer/cron.d.ts.map +1 -1
  4. package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
  5. package/dist/features/magic-context/message-index-async.d.ts +4 -1
  6. package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
  7. package/dist/features/magic-context/message-index.d.ts +7 -4
  8. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  9. package/dist/features/magic-context/migrations.d.ts +8 -16
  10. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  11. package/dist/features/magic-context/plugin-messages.d.ts.map +1 -1
  12. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  13. package/dist/features/magic-context/session-project-backfill.d.ts +6 -3
  14. package/dist/features/magic-context/session-project-backfill.d.ts.map +1 -1
  15. package/dist/features/magic-context/session-project-storage.d.ts.map +1 -1
  16. package/dist/features/magic-context/smart-notes/capabilities.d.ts.map +1 -1
  17. package/dist/features/magic-context/smart-notes/compiler.d.ts +3 -1
  18. package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -1
  19. package/dist/features/magic-context/smart-notes/runner.d.ts +1 -0
  20. package/dist/features/magic-context/smart-notes/runner.d.ts.map +1 -1
  21. package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts +8 -1
  22. package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts.map +1 -1
  23. package/dist/features/magic-context/smart-notes/ssrf-guard.d.ts +10 -0
  24. package/dist/features/magic-context/smart-notes/ssrf-guard.d.ts.map +1 -1
  25. package/dist/features/magic-context/smart-notes/storage.d.ts +16 -1
  26. package/dist/features/magic-context/smart-notes/storage.d.ts.map +1 -1
  27. package/dist/features/magic-context/storage-db.d.ts +1 -1
  28. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  29. package/dist/features/magic-context/storage-meta-persisted.d.ts +8 -5
  30. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  31. package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
  32. package/dist/features/magic-context/storage-schema-helpers.d.ts +3 -4
  33. package/dist/features/magic-context/storage-schema-helpers.d.ts.map +1 -1
  34. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  35. package/dist/features/magic-context/tool-owner-backfill.d.ts +2 -0
  36. package/dist/features/magic-context/tool-owner-backfill.d.ts.map +1 -1
  37. package/dist/features/magic-context/transform-decision-log.d.ts +1 -1
  38. package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
  39. package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
  40. package/dist/hooks/magic-context/compartment-render-epoch.d.ts +12 -0
  41. package/dist/hooks/magic-context/compartment-render-epoch.d.ts.map +1 -0
  42. package/dist/hooks/magic-context/compartment-runner-validation.d.ts.map +1 -1
  43. package/dist/hooks/magic-context/decay-render.d.ts.map +1 -1
  44. package/dist/hooks/magic-context/emergency-drop.d.ts +1 -0
  45. package/dist/hooks/magic-context/emergency-drop.d.ts.map +1 -1
  46. package/dist/hooks/magic-context/final-wire-token-estimate.d.ts +29 -0
  47. package/dist/hooks/magic-context/final-wire-token-estimate.d.ts.map +1 -0
  48. package/dist/hooks/magic-context/heuristic-cleanup.d.ts +1 -0
  49. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  50. package/dist/hooks/magic-context/historian-prompt.generated.d.ts +1 -1
  51. package/dist/hooks/magic-context/historian-prompt.generated.d.ts.map +1 -1
  52. package/dist/hooks/magic-context/inject-compartments.d.ts +1 -0
  53. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  54. package/dist/hooks/magic-context/read-session-chunk.d.ts +15 -1
  55. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  56. package/dist/hooks/magic-context/read-session-raw.d.ts +24 -2
  57. package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
  58. package/dist/hooks/magic-context/shadow-sender.d.ts +65 -27
  59. package/dist/hooks/magic-context/shadow-sender.d.ts.map +1 -1
  60. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +27 -0
  61. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  62. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +4128 -1207
  65. package/dist/plugin/rpc-handlers.d.ts +3 -0
  66. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  67. package/dist/shared/models-dev-cache.d.ts +5 -0
  68. package/dist/shared/models-dev-cache.d.ts.map +1 -1
  69. package/dist/shared/rpc-client.d.ts +3 -1
  70. package/dist/shared/rpc-client.d.ts.map +1 -1
  71. package/dist/shared/rpc-notifications.d.ts +11 -6
  72. package/dist/shared/rpc-notifications.d.ts.map +1 -1
  73. package/dist/shared/rpc-server.d.ts +2 -2
  74. package/dist/shared/rpc-server.d.ts.map +1 -1
  75. package/dist/shared/sqlite.d.ts.map +1 -1
  76. package/dist/shared/tui-config.d.ts +5 -3
  77. package/dist/shared/tui-config.d.ts.map +1 -1
  78. package/dist/tui/data/context-db.d.ts +9 -2
  79. package/dist/tui/data/context-db.d.ts.map +1 -1
  80. package/dist/tui/data/notification-socket.d.ts +4 -5
  81. package/dist/tui/data/notification-socket.d.ts.map +1 -1
  82. package/dist/tui-compiled/data/context-db.d.ts +9 -2
  83. package/dist/tui-compiled/data/context-db.d.ts.map +1 -1
  84. package/dist/tui-compiled/data/notification-socket.d.ts +4 -5
  85. package/dist/tui-compiled/data/notification-socket.d.ts.map +1 -1
  86. package/package.json +2 -1
  87. package/src/shared/models-dev-cache.test.ts +5 -0
  88. package/src/shared/models-dev-cache.ts +44 -6
  89. package/src/shared/opencode-compaction-detector.test.ts +11 -2
  90. package/src/shared/rpc-client.test.ts +213 -9
  91. package/src/shared/rpc-client.ts +48 -39
  92. package/src/shared/rpc-notifications.test.ts +26 -5
  93. package/src/shared/rpc-notifications.ts +44 -35
  94. package/src/shared/rpc-server.ts +69 -33
  95. package/src/shared/sqlite.ts +22 -3
  96. package/src/shared/tui-config.test.ts +4 -12
  97. package/src/shared/tui-config.ts +6 -6
  98. package/src/tui/data/context-db.test.ts +113 -0
  99. package/src/tui/data/context-db.ts +26 -19
  100. package/src/tui/data/notification-socket.test.ts +277 -11
  101. package/src/tui/data/notification-socket.ts +257 -86
  102. package/src/tui/index.tsx +33 -25
  103. package/src/tui/slots/sidebar-content.tsx +62 -24
  104. package/src/tui-compiled/data/context-db.ts +26 -19
  105. package/src/tui-compiled/data/notification-socket.ts +257 -86
  106. package/src/tui-compiled/index.tsx +41 -24
  107. package/src/tui-compiled/slots/sidebar-content.tsx +45 -23
@@ -40,11 +40,10 @@ function createSidebarController(initialPrefs) {
40
40
  let lastPersistedCollapsed = initialPrefs.collapsed;
41
41
  let lastApplied = JSON.stringify(initialPrefs);
42
42
 
43
- // Watcher lives for the process lifetime — intentionally never disposed.
44
43
  // Collapse echo guard: lastPersistedCollapsed advances only once our own
45
44
  // write lands, so a watcher echo of the value we just wrote is rejected by
46
45
  // the `!==` check and cannot revert a user click.
47
- watchTuiPreferences(() => {
46
+ const stopWatchingPreferences = watchTuiPreferences(() => {
48
47
  void (async () => {
49
48
  const next = resolveMagicContextPrefs(await readTuiPreferencesFile());
50
49
  const serialized = JSON.stringify(next);
@@ -69,7 +68,8 @@ function createSidebarController(initialPrefs) {
69
68
  return {
70
69
  prefs,
71
70
  collapsed,
72
- toggleCollapsed
71
+ toggleCollapsed,
72
+ dispose: stopWatchingPreferences
73
73
  };
74
74
  }
75
75
  function compactTokens(value) {
@@ -524,6 +524,8 @@ const SidebarContent = props => {
524
524
  let recompSawPhase = false;
525
525
  let recompPollCount = 0;
526
526
  let recompConsecutiveAbsent = 0;
527
+ let recompSessionId = null;
528
+ let snapshotRequestSequence = 0;
527
529
  const RECOMP_PROBE_MAX = 12; // ~15s for the server's "Starting…" to land
528
530
  // After we've SEEN an active phase, a momentarily absent snapshot is almost
529
531
  // always transient — the server's sticky cache serves a pre-recomp snapshot
@@ -539,12 +541,13 @@ const SidebarContent = props => {
539
541
  const refresh = () => {
540
542
  const sid = props.sessionID();
541
543
  if (!sid) return;
544
+ const sequence = ++snapshotRequestSequence;
542
545
  const directory = props.api.state.path.directory ?? "";
543
546
  void loadSidebarSnapshot(sid, directory).then(data => {
544
547
  // Guard against a session switch while this load was in flight:
545
548
  // painting session A's snapshot into the now-active session B shows
546
549
  // the wrong session's numbers until B's own refresh resolves.
547
- if (props.sessionID() !== sid) return;
550
+ if (props.sessionID() !== sid || sequence !== snapshotRequestSequence) return;
548
551
  setSnapshot(data);
549
552
  try {
550
553
  props.api.renderer.requestRender();
@@ -557,10 +560,15 @@ const SidebarContent = props => {
557
560
  const phase = data?.recompProgress?.phase;
558
561
  if ((phase === "recomp" || phase === "migration") && !recompActive) {
559
562
  kickRecompPoll();
563
+ } else if (recompActive && recompSessionId === sid) {
564
+ scheduleRecompTick();
560
565
  }
561
566
  }).catch(() => {
562
- // one-shot refresh failure is non-fatal; the recomp loop (if any)
563
- // has its own resilient retry.
567
+ // A one-shot refresh failure is non-fatal. If it superseded a fast
568
+ // poll request, keep that poll moving for the captured session.
569
+ if (recompActive && recompSessionId === sid && props.sessionID() === sid) {
570
+ scheduleRecompTick();
571
+ }
564
572
  });
565
573
  };
566
574
  const scheduleRefresh = () => {
@@ -570,25 +578,33 @@ const SidebarContent = props => {
570
578
  refresh();
571
579
  }, REFRESH_DEBOUNCE_MS);
572
580
  };
581
+ const stopRecompPoll = () => {
582
+ recompActive = false;
583
+ recompSessionId = null;
584
+ snapshotRequestSequence += 1;
585
+ if (recompPollTimer) clearTimeout(recompPollTimer);
586
+ recompPollTimer = undefined;
587
+ };
573
588
  const scheduleRecompTick = () => {
574
589
  if (!recompActive) return;
575
590
  if (recompPollTimer) clearTimeout(recompPollTimer);
576
591
  recompPollTimer = setTimeout(recompTick, RECOMP_POLL_MS);
577
592
  };
578
593
  function recompTick() {
579
- if (!recompActive) return;
580
- recompPollCount += 1;
581
- if (recompPollCount > RECOMP_MAX_POLLS) {
582
- recompActive = false;
594
+ const sid = recompSessionId;
595
+ if (!recompActive || !sid || props.sessionID() !== sid) {
596
+ stopRecompPoll();
583
597
  return;
584
598
  }
585
- const sid = props.sessionID();
586
- if (!sid) {
587
- recompActive = false;
599
+ recompPollCount += 1;
600
+ if (recompPollCount > RECOMP_MAX_POLLS) {
601
+ stopRecompPoll();
588
602
  return;
589
603
  }
604
+ const sequence = ++snapshotRequestSequence;
590
605
  const directory = props.api.state.path.directory ?? "";
591
606
  void loadSidebarSnapshot(sid, directory).then(data => {
607
+ if (!recompActive || recompSessionId !== sid || props.sessionID() !== sid || sequence !== snapshotRequestSequence) return;
592
608
  const phase = data?.recompProgress?.phase;
593
609
  // While a recomp is known-active, a transient snapshot that lost
594
610
  // recompProgress (sticky cache / busy-DB empty) must NOT wipe the
@@ -613,14 +629,14 @@ const SidebarContent = props => {
613
629
  // Terminal state rendered — stop. The server keeps "done"/
614
630
  // "skipped" for a grace window and "failed" until the next run,
615
631
  // so the outcome stays visible without further polling.
616
- recompActive = false;
632
+ stopRecompPoll();
617
633
  } else {
618
634
  // Phase absent this poll.
619
635
  recompConsecutiveAbsent += 1;
620
636
  if (!recompSawPhase) {
621
637
  // Still waiting for the server's first "Starting…".
622
638
  if (recompPollCount < RECOMP_PROBE_MAX) scheduleRecompTick();else {
623
- recompActive = false;
639
+ stopRecompPoll();
624
640
  }
625
641
  } else if (recompConsecutiveAbsent < RECOMP_ABSENT_GIVEUP) {
626
642
  // Seen it active — absent is almost certainly the sticky
@@ -630,13 +646,13 @@ const SidebarContent = props => {
630
646
  scheduleRecompTick();
631
647
  } else {
632
648
  // Long continuous absence — the entry is genuinely gone.
633
- recompActive = false;
649
+ stopRecompPoll();
634
650
  }
635
651
  }
636
- }).catch(err => {
637
- // CRITICAL: a failed/slow fetch must NOT kill the loop — keep
638
- // polling while active so we still catch the terminal state.
639
- scheduleRecompTick();
652
+ }).catch(() => {
653
+ // A failed fetch must not kill the loop, but a response from a
654
+ // superseded session or sequence must not restart it either.
655
+ if (recompActive && recompSessionId === sid && props.sessionID() === sid && sequence === snapshotRequestSequence) scheduleRecompTick();
640
656
  });
641
657
  }
642
658
 
@@ -644,8 +660,12 @@ const SidebarContent = props => {
644
660
  // first detects an active recomp). The server emits an immediate "Starting…"
645
661
  // entry; the probe window covers the brief RPC race before it lands.
646
662
  function kickRecompPoll() {
647
- if (recompActive) return; // already running
663
+ const sid = props.sessionID();
664
+ if (!sid) return;
665
+ if (recompActive && recompSessionId === sid) return;
666
+ stopRecompPoll();
648
667
  recompActive = true;
668
+ recompSessionId = sid;
649
669
  recompSawPhase = false;
650
670
  recompPollCount = 0;
651
671
  recompConsecutiveAbsent = 0;
@@ -654,13 +674,14 @@ const SidebarContent = props => {
654
674
  activeRecompPollKick = kickRecompPoll;
655
675
  onCleanup(() => {
656
676
  if (refreshTimer) clearTimeout(refreshTimer);
657
- if (recompPollTimer) clearTimeout(recompPollTimer);
658
- recompActive = false;
677
+ stopRecompPoll();
659
678
  if (activeRecompPollKick === kickRecompPoll) activeRecompPollKick = null;
660
679
  });
661
680
 
662
681
  // Refresh on session change
663
682
  createEffect(on(props.sessionID, () => {
683
+ stopRecompPoll();
684
+ setSnapshot(null);
664
685
  refresh();
665
686
  }));
666
687
 
@@ -1035,6 +1056,7 @@ export function createSidebarContentSlot(api) {
1035
1056
  const effectiveOrder = computeEffectiveOrder(seedRoot, PLUGIN_KEY, DEFAULT_SLOT_ORDER);
1036
1057
  return {
1037
1058
  order: effectiveOrder,
1059
+ dispose: controller.dispose,
1038
1060
  slots: {
1039
1061
  sidebar_content: (ctx, value) => {
1040
1062
  const theme = createMemo(() => ctx.theme.current);