@cortexkit/pi-magic-context 0.42.0 → 0.42.2

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.
package/dist/index.js CHANGED
@@ -17,7 +17,6 @@ import {
17
17
  resolveModelRefForOmp,
18
18
  resolveModelConfigValue,
19
19
  resolveModelConfigOrDefault,
20
- DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE,
21
20
  DEFAULT_HISTORIAN_TIMEOUT_MS,
22
21
  getProtectedTokensTierOverrides,
23
22
  sanitizeDiagnosticText,
@@ -109,11 +108,8 @@ import {
109
108
  adoptNullOwnerToolTag,
110
109
  resolveOpenCodeDbPath,
111
110
  openCodeDbPathExists,
112
- formatOpenCodeDbMissingStatusLine,
113
- formatOpenCodeDbReadFailureStatusLine,
114
111
  recordOpenCodeDbReadFailure,
115
112
  clearOpenCodeDbReadFailure,
116
- getOpenCodeDbReadFailure,
117
113
  claimOpenCodeDbDiagnosticOnce,
118
114
  Database,
119
115
  closeQuietly,
@@ -178,6 +174,7 @@ import {
178
174
  escapeXmlContent,
179
175
  getModuleNoteEvaluationBridge,
180
176
  getContextStoreUuid,
177
+ drainMirrorPages,
181
178
  parseCompartmentOutput,
182
179
  resolveWorkspaceShareCategories,
183
180
  resolveWorkspaceIdentitySet,
@@ -467,7 +464,7 @@ import {
467
464
  registerTodoStateLifecycle,
468
465
  syncCtxMemoryToolEnabled,
469
466
  registerMagicContextTools
470
- } from "./index-yb21jtfd.js";
467
+ } from "./index-6zjn0r2z.js";
471
468
  import {
472
469
  pushNotification2
473
470
  } from "./index-b3eqj1g6.js";
@@ -2418,14 +2415,25 @@ function countActiveMemories(db, projectPath) {
2418
2415
  const row = db.prepare("SELECT COUNT(*) AS cnt FROM memories WHERE project_path = ? AND status IN ('active','permanent')").get(projectPath);
2419
2416
  return row?.cnt ?? 0;
2420
2417
  }
2418
+ function countLiveMemories(db, projectPath, options = {}) {
2419
+ const unclassified = options.unclassifiedOnly && hasMemoryClassifiedAtColumn(db);
2420
+ const row = db.prepare(`SELECT COUNT(*) AS cnt
2421
+ FROM memories
2422
+ WHERE project_path = ?
2423
+ AND status IN ('active','permanent')
2424
+ AND (expires_at IS NULL OR expires_at > ?)
2425
+ ${unclassified ? "AND classified_at IS NULL" : ""}`).get(projectPath, Date.now());
2426
+ return row?.cnt ?? 0;
2427
+ }
2421
2428
  function countUnmappedActiveMemories(db, projectPath) {
2422
2429
  const row = db.prepare(`SELECT COUNT(*) AS cnt
2423
2430
  FROM memories m
2424
2431
  WHERE m.project_path = ?
2425
2432
  AND m.status IN ('active','permanent')
2433
+ AND (m.expires_at IS NULL OR m.expires_at > ?)
2426
2434
  AND NOT EXISTS (
2427
2435
  SELECT 1 FROM memory_verifications v WHERE v.memory_id = m.id
2428
- )`).get(projectPath);
2436
+ )`).get(projectPath, Date.now());
2429
2437
  return row?.cnt ?? 0;
2430
2438
  }
2431
2439
  function countCompartmentsSince(db, projectPath, since) {
@@ -2445,7 +2453,8 @@ function countMappedMemories(db, projectPath) {
2445
2453
  JOIN memory_verifications v ON v.memory_id = m.id
2446
2454
  WHERE m.project_path = ?
2447
2455
  AND m.status IN ('active','permanent')
2448
- AND v.file_path <> ''`).get(projectPath);
2456
+ AND (m.expires_at IS NULL OR m.expires_at > ?)
2457
+ AND v.file_path <> ''`).get(projectPath, Date.now());
2449
2458
  return row?.cnt ?? 0;
2450
2459
  }
2451
2460
  function countUnverifiedMappedMemories(db, projectPath) {
@@ -2454,8 +2463,9 @@ function countUnverifiedMappedMemories(db, projectPath) {
2454
2463
  JOIN memory_verifications v ON v.memory_id = m.id
2455
2464
  WHERE m.project_path = ?
2456
2465
  AND m.status IN ('active','permanent')
2466
+ AND (m.expires_at IS NULL OR m.expires_at > ?)
2457
2467
  AND v.file_path <> ''
2458
- AND v.verified_at = 0`).get(projectPath);
2468
+ AND v.verified_at = 0`).get(projectPath, Date.now());
2459
2469
  return row?.cnt ?? 0;
2460
2470
  }
2461
2471
  function countBroadCycleCandidates(db, projectPath, cycleStartAt) {
@@ -2463,22 +2473,24 @@ function countBroadCycleCandidates(db, projectPath, cycleStartAt) {
2463
2473
  FROM memories m
2464
2474
  WHERE m.project_path = ?
2465
2475
  AND m.status IN ('active','permanent')
2476
+ AND (m.expires_at IS NULL OR m.expires_at > ?)
2466
2477
  AND (
2467
2478
  SELECT MAX(v.verified_at)
2468
2479
  FROM memory_verifications v
2469
2480
  WHERE v.memory_id = m.id
2470
2481
  AND v.file_path <> ''
2471
- ) < ?`).get(projectPath, cycleStartAt);
2482
+ ) < ?`).get(projectPath, Date.now(), cycleStartAt);
2472
2483
  return row?.cnt ?? 0;
2473
2484
  }
2474
2485
  function countCueCandidates(db, projectPath) {
2475
2486
  if (!hasMuralCueColumns(db))
2476
- return countActiveMemories(db, projectPath);
2487
+ return countLiveMemories(db, projectPath);
2477
2488
  const row = db.prepare(`SELECT COUNT(*) AS cnt
2478
2489
  FROM memories
2479
2490
  WHERE project_path = ?
2480
2491
  AND status IN ('active','permanent')
2481
- AND (mural_cue IS NULL OR mural_cue_hash IS NULL OR updated_at > mural_cue_at)`).get(projectPath);
2492
+ AND (expires_at IS NULL OR expires_at > ?)
2493
+ AND (mural_cue IS NULL OR mural_cue_hash IS NULL OR updated_at > mural_cue_at)`).get(projectPath, Date.now());
2482
2494
  return row?.cnt ?? 0;
2483
2495
  }
2484
2496
  function countStalePrimers(db, projectPath) {
@@ -2490,16 +2502,6 @@ function countStalePrimers(db, projectPath) {
2490
2502
  OR last_observed_at > answer_refreshed_at)`).get(projectPath);
2491
2503
  return row?.cnt ?? 0;
2492
2504
  }
2493
- function countUnclassifiedActiveMemories(db, projectPath) {
2494
- if (!hasMemoryClassifiedAtColumn(db))
2495
- return countActiveMemories(db, projectPath);
2496
- const row = db.prepare(`SELECT COUNT(*) AS cnt
2497
- FROM memories
2498
- WHERE project_path = ?
2499
- AND status IN ('active','permanent')
2500
- AND classified_at IS NULL`).get(projectPath);
2501
- return row?.cnt ?? 0;
2502
- }
2503
2505
  function countPendingSmartNotes(db, projectPath) {
2504
2506
  const row = db.prepare("SELECT COUNT(*) AS cnt FROM notes WHERE project_path = ? AND type = 'smart' AND status = 'pending'").get(projectPath);
2505
2507
  return row?.cnt ?? 0;
@@ -2515,7 +2517,7 @@ function countActivePrimers(db, projectPath) {
2515
2517
  function getDreamTaskBacklog(db, projectPath, task, options = {}) {
2516
2518
  switch (task) {
2517
2519
  case "map-memories": {
2518
- const total = countActiveMemories(db, projectPath);
2520
+ const total = countLiveMemories(db, projectPath);
2519
2521
  return { pending: countUnmappedActiveMemories(db, projectPath), total };
2520
2522
  }
2521
2523
  case "verify": {
@@ -2531,16 +2533,19 @@ function getDreamTaskBacklog(db, projectPath, task, options = {}) {
2531
2533
  return { pending, total };
2532
2534
  }
2533
2535
  case "curate": {
2534
- const total = countActiveMemories(db, projectPath);
2536
+ const total = countLiveMemories(db, projectPath);
2535
2537
  return { pending: total, total };
2536
2538
  }
2537
2539
  case "compress-cues": {
2538
- const total = countActiveMemories(db, projectPath);
2540
+ const total = countLiveMemories(db, projectPath);
2539
2541
  return { pending: countCueCandidates(db, projectPath), total };
2540
2542
  }
2541
2543
  case "classify-memories": {
2542
- const total = countActiveMemories(db, projectPath);
2543
- return { pending: countUnclassifiedActiveMemories(db, projectPath), total };
2544
+ const total = countLiveMemories(db, projectPath);
2545
+ return {
2546
+ pending: countLiveMemories(db, projectPath, { unclassifiedOnly: true }),
2547
+ total
2548
+ };
2544
2549
  }
2545
2550
  case "retrospective": {
2546
2551
  const pending = countProjectSessionsSince(db, projectPath, options.retrospectiveWatermarkMs ?? null);
@@ -2585,15 +2590,15 @@ function evaluateTaskGate(task, ctx) {
2585
2590
  case "map-memories":
2586
2591
  return countUnmappedActiveMemories(db, project) > 0;
2587
2592
  case "verify":
2588
- return countActiveMemories(db, project) > 0;
2593
+ return countLiveMemories(db, project) > 0;
2589
2594
  case "verify-broad":
2590
- return getTaskScheduleState(db, project, "verify-broad")?.lastBroadRunAt != null || countActiveMemories(db, project) > 0;
2595
+ return getTaskScheduleState(db, project, "verify-broad")?.lastBroadRunAt != null || countLiveMemories(db, project) > 0;
2591
2596
  case "curate":
2592
2597
  return countActiveMemories(db, project) > 0;
2593
2598
  case "compress-cues":
2594
- return countActiveMemories(db, project) > 0;
2599
+ return countLiveMemories(db, project) > 0;
2595
2600
  case "classify-memories":
2596
- return countActiveMemories(db, project) > 0;
2601
+ return countLiveMemories(db, project) > 0;
2597
2602
  case "retrospective":
2598
2603
  return countProjectSessionsSince(db, project, ctx.retrospectiveWatermarkMs ?? null) > 0;
2599
2604
  case "maintain-docs":
@@ -2613,6 +2618,239 @@ function evaluateTaskGate(task, ctx) {
2613
2618
  }
2614
2619
  }
2615
2620
 
2621
+ // ../plugin/src/shared/user-facing-codes.ts
2622
+ var USER_FACING_FAILURES = {
2623
+ historian_unavailable: {
2624
+ code: "MC-H01",
2625
+ sentence: "History compression could not finish this turn.",
2626
+ action: "It will retry automatically."
2627
+ },
2628
+ recomp_unavailable: {
2629
+ code: "MC-R01",
2630
+ sentence: "History compression could not be rebuilt.",
2631
+ action: "Run /ctx-recomp again."
2632
+ },
2633
+ dream_provider_timeout: {
2634
+ code: "MC-D01",
2635
+ sentence: "Memory maintenance took too long to respond.",
2636
+ action: "Run /ctx-dream again."
2637
+ },
2638
+ dream_provider_error: {
2639
+ code: "MC-D02",
2640
+ sentence: "Memory maintenance could not reach its model.",
2641
+ action: "Check the model connection, then run /ctx-dream again."
2642
+ },
2643
+ dream_empty_completion: {
2644
+ code: "MC-D03",
2645
+ sentence: "Memory maintenance received no usable response.",
2646
+ action: "Run /ctx-dream again."
2647
+ },
2648
+ dream_no_models: {
2649
+ code: "MC-D04",
2650
+ sentence: "Memory maintenance has no model available.",
2651
+ action: "Check the model settings, then run /ctx-dream again."
2652
+ },
2653
+ dream_child_aborted: {
2654
+ code: "MC-D05",
2655
+ sentence: "Memory maintenance was interrupted.",
2656
+ action: "Run /ctx-dream again."
2657
+ },
2658
+ dream_parse_failed: {
2659
+ code: "MC-D06",
2660
+ sentence: "Memory maintenance could not use the model response.",
2661
+ action: "Run /ctx-dream again."
2662
+ },
2663
+ dream_unknown: {
2664
+ code: "MC-D07",
2665
+ sentence: "Memory maintenance could not finish.",
2666
+ action: "Run /ctx-dream again."
2667
+ },
2668
+ embedding_substitution_rejected: {
2669
+ code: "MC-E01",
2670
+ sentence: "Search indexing could not use the selected model.",
2671
+ action: "Check the embedding model setting, then run /ctx-embed start again."
2672
+ },
2673
+ embedding_http_error: {
2674
+ code: "MC-E02",
2675
+ sentence: "The search indexing provider refused the request.",
2676
+ action: "Check the provider connection and credentials, then run /ctx-embed start again."
2677
+ },
2678
+ embedding_transport_error: {
2679
+ code: "MC-E03",
2680
+ sentence: "Search indexing could not reach its provider.",
2681
+ action: "Check the connection, then run /ctx-embed start again."
2682
+ },
2683
+ embedding_invalid_envelope: {
2684
+ code: "MC-E04",
2685
+ sentence: "Search indexing received an unsupported response.",
2686
+ action: "Check the embedding endpoint, then run /ctx-embed start again."
2687
+ },
2688
+ embedding_empty_result: {
2689
+ code: "MC-E05",
2690
+ sentence: "Search indexing received no usable result.",
2691
+ action: "Run /ctx-embed start again."
2692
+ },
2693
+ embedding_certification_refusal: {
2694
+ code: "MC-E06",
2695
+ sentence: "Search indexing is not ready for this provider.",
2696
+ action: "Finish the provider setup, or set a fallback provider in the embedding settings, then run /ctx-embed start again."
2697
+ },
2698
+ embedding_credential_required: {
2699
+ code: "MC-E07",
2700
+ sentence: "Search indexing needs provider credentials.",
2701
+ action: "Sign in to the provider, then run /ctx-embed start again."
2702
+ },
2703
+ embedding_local_binding_missing: {
2704
+ code: "MC-E08",
2705
+ sentence: "Local search indexing is unavailable on this system.",
2706
+ action: "Run `npx @cortexkit/magic-context doctor`, then retry."
2707
+ },
2708
+ embedding_local_fs_unavailable: {
2709
+ code: "MC-E09",
2710
+ sentence: "Local search indexing cannot save its model files.",
2711
+ action: "Update or reinstall Magic Context, then retry."
2712
+ },
2713
+ embedding_local_download_failure: {
2714
+ code: "MC-E10",
2715
+ sentence: "Local search indexing could not download its model.",
2716
+ action: "Check the network connection, then retry."
2717
+ },
2718
+ embedding_local_runtime_error: {
2719
+ code: "MC-E11",
2720
+ sentence: "Local search indexing could not start.",
2721
+ action: "Run `npx @cortexkit/magic-context doctor`, then retry."
2722
+ },
2723
+ embedding_unavailable: {
2724
+ code: "MC-E12",
2725
+ sentence: "Search indexing could not finish.",
2726
+ action: "Run /ctx-embed start again."
2727
+ },
2728
+ status_unavailable: {
2729
+ code: "MC-S01",
2730
+ sentence: "Magic Context status is temporarily unavailable.",
2731
+ action: "Retry /ctx-status in a moment."
2732
+ },
2733
+ transform_update_failed: {
2734
+ code: "MC-S02",
2735
+ sentence: "The last context update did not finish.",
2736
+ action: "Send another message to retry."
2737
+ },
2738
+ configuration_warning: {
2739
+ code: "MC-S03",
2740
+ sentence: "Some configuration settings could not be applied.",
2741
+ action: "Fix the configuration warning shown in /ctx-status diagnostics, then restart."
2742
+ },
2743
+ status_log_unavailable: {
2744
+ code: "MC-S04",
2745
+ sentence: "Some diagnostic details could not be saved.",
2746
+ action: "Retry /ctx-status in a moment."
2747
+ },
2748
+ memory_writes_paused: {
2749
+ code: "MC-C01",
2750
+ sentence: "Memory writes are paused while the engine syncs.",
2751
+ action: "Retry in a moment."
2752
+ },
2753
+ memory_access_unavailable: {
2754
+ code: "MC-C02",
2755
+ sentence: "Memory access is temporarily unavailable.",
2756
+ action: "Retry in a moment."
2757
+ },
2758
+ note_changes_paused: {
2759
+ code: "MC-C03",
2760
+ sentence: "Note changes are paused while the engine syncs.",
2761
+ action: "Retry in a moment."
2762
+ },
2763
+ note_access_unavailable: {
2764
+ code: "MC-C04",
2765
+ sentence: "Notes are temporarily unavailable.",
2766
+ action: "Retry in a moment."
2767
+ },
2768
+ context_cleanup_paused: {
2769
+ code: "MC-C05",
2770
+ sentence: "Context cleanup is paused while the engine syncs.",
2771
+ action: "Retry in a moment."
2772
+ },
2773
+ partial_history_unavailable: {
2774
+ code: "MC-C06",
2775
+ sentence: "Partial history compression is not available in the current mode.",
2776
+ action: "Run /ctx-recomp without a range."
2777
+ },
2778
+ session_upgrade_unavailable: {
2779
+ code: "MC-C07",
2780
+ sentence: "Session upgrade is not available in the current mode.",
2781
+ action: "Run /ctx-recomp instead."
2782
+ },
2783
+ smart_note_conditions_unavailable: {
2784
+ code: "MC-C08",
2785
+ sentence: "Conditional notes are not available in the current mode.",
2786
+ action: "Save a regular note without a condition."
2787
+ },
2788
+ history_compression_paused: {
2789
+ code: "MC-C09",
2790
+ sentence: "History compression is paused while the engine syncs.",
2791
+ action: "Retry in a moment."
2792
+ },
2793
+ context_service_unavailable: {
2794
+ code: "MC-C10",
2795
+ sentence: "Magic Context is temporarily unavailable.",
2796
+ action: "Retry in a moment."
2797
+ }
2798
+ };
2799
+ function renderUserFacingFailure(key, style = "markdown") {
2800
+ const failure = USER_FACING_FAILURES[key];
2801
+ const action = style === "plain" ? failure.action.replaceAll("`", "") : failure.action;
2802
+ return `${failure.sentence} ${action} (${failure.code})`;
2803
+ }
2804
+ function userFacingFailureCode(key) {
2805
+ return USER_FACING_FAILURES[key].code;
2806
+ }
2807
+ var CAPABILITY_FAILURES = {
2808
+ memory_write: "memory_writes_paused",
2809
+ memory_access: "memory_access_unavailable",
2810
+ note_change: "note_changes_paused",
2811
+ note_access: "note_access_unavailable",
2812
+ context_cleanup: "context_cleanup_paused",
2813
+ partial_history: "partial_history_unavailable",
2814
+ session_upgrade: "session_upgrade_unavailable",
2815
+ smart_note_condition: "smart_note_conditions_unavailable",
2816
+ history_compression: "history_compression_paused",
2817
+ context_service: "context_service_unavailable"
2818
+ };
2819
+ function renderCapabilityRefusal(capability) {
2820
+ return renderUserFacingFailure(CAPABILITY_FAILURES[capability]);
2821
+ }
2822
+ var DREAM_FAILURE_KEYS = {
2823
+ provider_timeout: "dream_provider_timeout",
2824
+ provider_error: "dream_provider_error",
2825
+ empty_completion: "dream_empty_completion",
2826
+ no_models: "dream_no_models",
2827
+ child_aborted: "dream_child_aborted",
2828
+ parse_failed: "dream_parse_failed",
2829
+ unknown: "dream_unknown"
2830
+ };
2831
+ var EMBEDDING_FAILURE_KEYS = {
2832
+ substitution_rejected: "embedding_substitution_rejected",
2833
+ http_error: "embedding_http_error",
2834
+ transport_error: "embedding_transport_error",
2835
+ invalid_envelope: "embedding_invalid_envelope",
2836
+ empty_result: "embedding_empty_result",
2837
+ certification_refusal: "embedding_certification_refusal",
2838
+ credential_required: "embedding_credential_required",
2839
+ local_binding_missing: "embedding_local_binding_missing",
2840
+ local_fs_unavailable: "embedding_local_fs_unavailable",
2841
+ local_download_failure: "embedding_local_download_failure",
2842
+ local_runtime_error: "embedding_local_runtime_error"
2843
+ };
2844
+ function renderDreamFailure(failureClass, style = "markdown") {
2845
+ return renderUserFacingFailure(DREAM_FAILURE_KEYS[failureClass], style);
2846
+ }
2847
+ function dreamFailureCode(failureClass) {
2848
+ return userFacingFailureCode(DREAM_FAILURE_KEYS[failureClass]);
2849
+ }
2850
+ function renderEmbeddingFailure(failureClass, style = "markdown") {
2851
+ return renderUserFacingFailure(EMBEDDING_FAILURE_KEYS[failureClass], style);
2852
+ }
2853
+
2616
2854
  // ../plugin/src/features/magic-context/dreamer/open-opencode-db.ts
2617
2855
  function openOpenCodeDb() {
2618
2856
  const resolution = resolveOpenCodeDbPath();
@@ -5183,12 +5421,12 @@ async function runClassifyThroughModule(args, chunk, anchors, signal) {
5183
5421
  });
5184
5422
  } catch (error) {
5185
5423
  if (isRustAuthorityDrainingError(error)) {
5186
- throw new Error("Rust memory authority is not ready; TypeScript fallback is disabled.");
5424
+ throw new Error(renderCapabilityRefusal("memory_write"));
5187
5425
  }
5188
5426
  throw error;
5189
5427
  }
5190
5428
  if (isRustAuthorityDrainingError(applied)) {
5191
- throw new Error("Rust memory authority is not ready; TypeScript fallback is disabled.");
5429
+ throw new Error(renderCapabilityRefusal("memory_write"));
5192
5430
  }
5193
5431
  const applyResult = applied?.result ?? applied;
5194
5432
  if (!applyResult || typeof applyResult !== "object") {
@@ -6820,6 +7058,116 @@ Output exactly JSON: {"met": false}`;
6820
7058
  }
6821
7059
  }
6822
7060
 
7061
+ // ../plugin/src/features/magic-context/dreamer/expire-memories.ts
7062
+ var MODULE_ARCHIVE_BATCH_SIZE = 100;
7063
+ var EXPIRED_ARCHIVE_REASON = "expired";
7064
+ function getExpiredActiveMemoryIds(db, projectIdentity, now = Date.now()) {
7065
+ const rows = db.prepare(`SELECT id
7066
+ FROM memories
7067
+ WHERE project_path = ?
7068
+ AND status = 'active'
7069
+ AND expires_at IS NOT NULL
7070
+ AND expires_at <= ?
7071
+ ORDER BY id`).all(projectIdentity, now);
7072
+ return rows.map((row) => row.id);
7073
+ }
7074
+ function assertSuccessfulModuleArchive(response) {
7075
+ const result = response && typeof response === "object" && "result" in response ? response.result : response;
7076
+ if (result && typeof result === "object") {
7077
+ const record = result;
7078
+ if (record.ok === false || record.error) {
7079
+ throw new Error("module rejected expired-memory archive");
7080
+ }
7081
+ }
7082
+ }
7083
+ async function archiveExpiredThroughModule(args) {
7084
+ if (!args.moduleRoute.moduleClient.mirrorPull) {
7085
+ throw new DreamerModuleFailureError("mirror.pull expired archive", new Error("Rust dreamer client omitted the memory mirror route"));
7086
+ }
7087
+ for (let offset = 0;offset < args.expiredContextIds.length; offset += MODULE_ARCHIVE_BATCH_SIZE) {
7088
+ if (!leaseOwnershipMatches(args.db, args.holderId, args.leaseAcquisition.generation, args.leaseKey)) {
7089
+ throw new Error("Dream lease lost before expired-memory archive");
7090
+ }
7091
+ const contextBatch = args.expiredContextIds.slice(offset, offset + MODULE_ARCHIVE_BATCH_SIZE);
7092
+ const identities = getModuleMemoryIdentities(args.db, args.projectIdentity, contextBatch);
7093
+ if (identities.size !== contextBatch.length) {
7094
+ throw new DreamerModuleFailureError("ctx_memory expired archive", new Error("expired memory is missing its module mirror identity"));
7095
+ }
7096
+ const moduleBatch = contextBatch.map((id) => {
7097
+ const identity = identities.get(id);
7098
+ if (!identity)
7099
+ throw new Error(`missing module identity for expired memory ${id}`);
7100
+ return identity.moduleId;
7101
+ });
7102
+ try {
7103
+ const response = await args.moduleRoute.moduleClient.call({
7104
+ sessionId: args.moduleRoute.moduleSessionId,
7105
+ projectRoot: args.moduleRoute.moduleProjectRoot,
7106
+ method: "ctx_memory",
7107
+ body: {
7108
+ name: "ctx_memory",
7109
+ arguments: {
7110
+ action: "archive",
7111
+ memory_project: args.projectIdentity,
7112
+ ids: moduleBatch,
7113
+ reason: EXPIRED_ARCHIVE_REASON,
7114
+ command_id: `${args.moduleRoute.moduleCommandId}:expire:${offset / MODULE_ARCHIVE_BATCH_SIZE}`
7115
+ }
7116
+ }
7117
+ });
7118
+ assertSuccessfulModuleArchive(response);
7119
+ } catch (error) {
7120
+ throw new DreamerModuleFailureError("ctx_memory expired archive", error);
7121
+ }
7122
+ }
7123
+ const mirrorPull = args.moduleRoute.moduleClient.mirrorPull;
7124
+ const drained = await drainMirrorPages({
7125
+ db: args.db,
7126
+ module: {
7127
+ mirrorPull: (request) => mirrorPull({ ...request, projectRoot: args.moduleRoute.moduleProjectRoot })
7128
+ },
7129
+ domain: "memories",
7130
+ limit: 1000
7131
+ });
7132
+ if (!drained.complete) {
7133
+ throw new DreamerModuleFailureError("mirror.pull expired archive", new Error("memory mirror did not reach the module cursor"));
7134
+ }
7135
+ if (!leaseOwnershipMatches(args.db, args.holderId, args.leaseAcquisition.generation, args.leaseKey)) {
7136
+ throw new Error("Dream lease lost during expired-memory archive");
7137
+ }
7138
+ return args.expiredContextIds.length;
7139
+ }
7140
+ async function archiveExpiredMemories(args) {
7141
+ const expiredContextIds = getExpiredActiveMemoryIds(args.db, args.projectIdentity, args.now);
7142
+ if (expiredContextIds.length === 0) {
7143
+ if (!leaseOwnershipMatches(args.db, args.holderId, args.leaseAcquisition.generation, args.leaseKey)) {
7144
+ throw new Error("Dream lease lost before expired-memory probe");
7145
+ }
7146
+ return 0;
7147
+ }
7148
+ if (args.moduleRoute) {
7149
+ return archiveExpiredThroughModule({
7150
+ ...args,
7151
+ expiredContextIds,
7152
+ moduleRoute: args.moduleRoute
7153
+ });
7154
+ }
7155
+ const selectedIds = new Set(expiredContextIds);
7156
+ return runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
7157
+ const stillExpired = getExpiredActiveMemoryIds(args.db, args.projectIdentity, args.now).filter((id) => selectedIds.has(id));
7158
+ for (const id of stillExpired) {
7159
+ archiveMemory(args.db, id, EXPIRED_ARCHIVE_REASON);
7160
+ queueMemoryMutation(args.db, {
7161
+ projectPath: args.projectIdentity,
7162
+ mutationType: "archive",
7163
+ targetMemoryId: id,
7164
+ queuedAt: args.now
7165
+ });
7166
+ }
7167
+ return stillExpired.length;
7168
+ });
7169
+ }
7170
+
6823
7171
  // ../plugin/src/features/magic-context/dreamer/maintain-docs-protected-enforcement.ts
6824
7172
  import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
6825
7173
  import { join as join2 } from "node:path";
@@ -8425,14 +8773,7 @@ function parseJsonRecord(value) {
8425
8773
 
8426
8774
  // ../plugin/src/features/magic-context/dreamer/storage-dream-runs.ts
8427
8775
  function formatDreamRunFailure(failure) {
8428
- const parts = [failure.failure_class];
8429
- if (failure.model_attempted)
8430
- parts.push(`model: ${failure.model_attempted}`);
8431
- if (failure.provider_error)
8432
- parts.push(failure.provider_error.split(/\r?\n/, 1)[0].trim());
8433
- else if (failure.timeout_ms !== null)
8434
- parts.push(`timeout: ${failure.timeout_ms}ms`);
8435
- return parts.join(" · ");
8776
+ return renderDreamFailure(failure.failure_class);
8436
8777
  }
8437
8778
  var insertDreamRunStatements = new WeakMap;
8438
8779
  var getDreamRunsByProjectStatements = new Map;
@@ -9166,6 +9507,9 @@ function inspectCurateMemoryOperations(messages) {
9166
9507
  }
9167
9508
  return summary;
9168
9509
  }
9510
+ function formatExpiredArchiveProgress(count) {
9511
+ return `curate: archived ${count} expired ${count === 1 ? "memory" : "memories"}`;
9512
+ }
9169
9513
  function formatCurateMemoryOperations(actions) {
9170
9514
  const actionCounts = new Map;
9171
9515
  for (const action of actions)
@@ -9216,7 +9560,7 @@ function createDreamTaskExecutor(deps) {
9216
9560
  };
9217
9561
  const parent = await resolveParentSessionId();
9218
9562
  let moduleRoute;
9219
- if (config.task === "map-memories" || config.task === "compress-cues" || config.task === "classify-memories" || config.task === "verify" || config.task === "verify-broad" || config.task === "retrospective") {
9563
+ if (config.task === "curate" || config.task === "map-memories" || config.task === "compress-cues" || config.task === "classify-memories" || config.task === "verify" || config.task === "verify-broad" || config.task === "retrospective") {
9220
9564
  try {
9221
9565
  moduleRoute = await resolveDreamerModuleRoute({
9222
9566
  db,
@@ -9545,13 +9889,15 @@ function createDreamTaskExecutor(deps) {
9545
9889
  parent,
9546
9890
  recordRun,
9547
9891
  computeMemoryDelta,
9548
- reportProgress
9892
+ reportProgress,
9893
+ leaseAcquisition,
9894
+ moduleRoute
9549
9895
  });
9550
9896
  } catch (error) {
9551
9897
  const { transient, brief } = classifyFailure(error);
9552
9898
  const failure = dreamRunFailureDetail(error);
9553
9899
  recordRun("failed", brief, { failure });
9554
- log(`[dreamer] task ${config.task} failed (transient=${transient}): ${brief}`);
9900
+ log(`[dreamer] task ${config.task} failed code=${dreamFailureCode(failure.failure_class)} (transient=${transient}): ${brief}`);
9555
9901
  return {
9556
9902
  status: "failed",
9557
9903
  transient,
@@ -9846,17 +10192,6 @@ async function runAgenticTask(config, ctx, helpers) {
9846
10192
  architecture: existsSync5(`${docsDir}/ARCHITECTURE.md`),
9847
10193
  structure: existsSync5(`${docsDir}/STRUCTURE.md`)
9848
10194
  } : undefined;
9849
- let curateMemories;
9850
- if (task === "curate") {
9851
- curateMemories = loadActiveMemoryPromptMemories(db, projectIdentity);
9852
- log(`[dreamer] curate pool: in_scope=${curateMemories.length}`);
9853
- }
9854
- const taskPrompt = buildDreamTaskPrompt(task, {
9855
- projectPath: projectIdentity,
9856
- lastDreamAt: lastRunAt ? String(lastRunAt) : null,
9857
- existingDocs,
9858
- curate: curateMemories ? { memories: curateMemories } : undefined
9859
- });
9860
10195
  const abortController = new AbortController;
9861
10196
  let leaseLost = false;
9862
10197
  const heartbeat = startLeaseHeartbeat(db, holderId, leaseKey, () => {
@@ -9865,7 +10200,37 @@ async function runAgenticTask(config, ctx, helpers) {
9865
10200
  }, ctx.leaseAcquisition);
9866
10201
  let childSessionId = null;
9867
10202
  let promptSettled = false;
10203
+ let expiredArchived = 0;
9868
10204
  try {
10205
+ let curateMemories;
10206
+ if (task === "curate") {
10207
+ expiredArchived = await archiveExpiredMemories({
10208
+ db,
10209
+ projectIdentity,
10210
+ holderId,
10211
+ leaseKey,
10212
+ leaseAcquisition: helpers.leaseAcquisition,
10213
+ moduleRoute: helpers.moduleRoute
10214
+ });
10215
+ if (leaseLost)
10216
+ throw new Error("Dream lease lost during expired-memory archive");
10217
+ curateMemories = loadActiveMemoryPromptMemories(db, projectIdentity);
10218
+ log(`[dreamer] curate pool: in_scope=${curateMemories.length} expired_archived=${expiredArchived}`);
10219
+ if (curateMemories.length === 0 && expiredArchived > 0) {
10220
+ const progress = formatExpiredArchiveProgress(expiredArchived);
10221
+ helpers.recordRun("completed", null, {
10222
+ memoryChanges: helpers.computeMemoryDelta(memoryBefore),
10223
+ progress
10224
+ });
10225
+ return { status: "completed", detail: progress };
10226
+ }
10227
+ }
10228
+ const taskPrompt = buildDreamTaskPrompt(task, {
10229
+ projectPath: projectIdentity,
10230
+ lastDreamAt: lastRunAt ? String(lastRunAt) : null,
10231
+ existingDocs,
10232
+ curate: curateMemories ? { memories: curateMemories } : undefined
10233
+ });
9869
10234
  const createResponse = await createChildSessionWithFence({
9870
10235
  client: deps.client,
9871
10236
  db,
@@ -9957,6 +10322,7 @@ async function runAgenticTask(config, ctx, helpers) {
9957
10322
  }
9958
10323
  const curateOutput = task === "curate" ? run.validated : undefined;
9959
10324
  const progress = [
10325
+ expiredArchived > 0 ? formatExpiredArchiveProgress(expiredArchived) : null,
9960
10326
  curateOutput && curateOutput.memoryOperations.completedActions.length > 0 ? formatCurateMemoryOperations(curateOutput.memoryOperations.completedActions) : null,
9961
10327
  curateRefused > 0 ? `curate: refused ${curateRefused} unsafe mutation(s)` : null
9962
10328
  ].filter((value) => Boolean(value)).join("; ");
@@ -12897,17 +13263,10 @@ Unknown task "${requested}".`,
12897
13263
  projectIdentity: project.projectIdentity
12898
13264
  });
12899
13265
  } catch (error) {
12900
- const message = error instanceof Error ? error.message : String(error);
12901
- sessionLog(project.projectIdentity, `/ctx-dream failed: ${message}`);
13266
+ sessionLog(project.projectIdentity, `/ctx-dream failed code=${userFacingFailureCode("dream_unknown")}`, error);
12902
13267
  sendStatus({
12903
13268
  title: "/ctx-dream",
12904
- text: [
12905
- "## /ctx-dream",
12906
- "",
12907
- `Dream run failed: ${message}`,
12908
- "The registered timer will retry due tasks on its next tick."
12909
- ].join(`
12910
- `),
13269
+ text: renderUserFacingFailure("dream_unknown"),
12911
13270
  level: "error"
12912
13271
  }, {
12913
13272
  projectDir: project.projectDir,
@@ -12922,39 +13281,27 @@ Unknown task "${requested}".`,
12922
13281
  var embedPauseBySession = new Set;
12923
13282
  var embedRunStateBySession = new Map;
12924
13283
  var autoEmbedAttemptedBySession = new Set;
13284
+ function getEmbedDrainUiStatus(sessionId, progress) {
13285
+ if (embedPauseBySession.has(sessionId)) {
13286
+ return { status: "paused" };
13287
+ }
13288
+ const activeRun = embedRunStateBySession.get(sessionId);
13289
+ if (activeRun && !activeRun.signal.aborted || progress?.kind === "embed" && progress.phase === "recomp") {
13290
+ return { status: "running" };
13291
+ }
13292
+ if (progress?.kind === "embed" && (progress.phase === "failed" || progress.phase === "skipped") && progress.message) {
13293
+ if (/provider/i.test(progress.message)) {
13294
+ return { status: "stopped", detail: progress.message };
13295
+ }
13296
+ }
13297
+ return { status: "idle" };
13298
+ }
12925
13299
 
12926
13300
  // ../plugin/src/hooks/magic-context/format-embed-failure.ts
12927
- function formatEmbedFailureSummary(embedded, remaining, failure) {
12928
- const compartment = `compartment${embedded === 1 ? "" : "s"}`;
12929
- if (!failure) {
12930
- return `Embedded ${embedded} ${compartment}; ${remaining} could not be embedded (the provider returned no result). Run /ctx-embed start again to retry them.`;
12931
- }
12932
- const retry = " Run /ctx-embed start again to retry them.";
12933
- switch (failure.class) {
12934
- case "substitution_rejected":
12935
- return `Embedded ${embedded} ${compartment}; ${remaining} rejected: ${failure.reason}. Fix: set embedding.model to the served spelling.`;
12936
- case "http_error": {
12937
- const fix = failure.reason.startsWith("HTTP 401") || failure.reason.startsWith("HTTP 403") ? " Fix: check embedding.api_key." : failure.reason.startsWith("HTTP 402") ? " Fix: check provider quota or billing." : failure.reason.startsWith("HTTP 404") ? " Fix: check embedding.endpoint and embedding.model." : failure.retryable ? retry : " Fix: check embedding.endpoint, embedding.model, and credentials.";
12938
- return `Embedded ${embedded} ${compartment}; ${remaining} failed: ${failure.reason}.${fix}`;
12939
- }
12940
- case "invalid_envelope":
12941
- return `Embedded ${embedded} ${compartment}; ${remaining} failed: ${failure.reason}. Fix: configure an OpenAI-compatible embedding endpoint that returns data[].embedding.`;
12942
- case "certification_refusal":
12943
- return `Embedded ${embedded} ${compartment}; ${remaining} rejected: ${failure.reason}. Fix: recertify SYNAPSE or configure embedding.fallback_provider, then run /ctx-embed start again.`;
12944
- case "credential_required":
12945
- return `Embedded ${embedded} ${compartment}; ${remaining} rejected: ${failure.reason}. Fix: reauthenticate SYNAPSE, then run /ctx-embed start again.`;
12946
- case "local_binding_missing":
12947
- return `Embedded ${embedded} ${compartment}; ${remaining} failed: ${failure.reason}. Fix: run \`npx @cortexkit/magic-context@latest doctor\` to verify the WASM fallback. On Intel macOS, onnxruntime-node@1.23.0 may be pinned manually for optional native speed.`;
12948
- case "local_fs_unavailable":
12949
- return `Embedded ${embedded} ${compartment}; ${remaining} failed: ${failure.reason}. Fix: update or reinstall Magic Context so its Node WASM bundle can persist the model, then run /ctx-embed start again.`;
12950
- case "local_download_failure":
12951
- return `Embedded ${embedded} ${compartment}; ${remaining} failed: ${failure.reason}. Fix: check network/proxy access to the model host.${retry}`;
12952
- case "local_runtime_error":
12953
- return `Embedded ${embedded} ${compartment}; ${remaining} failed: ${failure.reason}. Fix: run \`npx @cortexkit/magic-context@latest doctor\` for local runtime diagnostics.`;
12954
- case "empty_result":
12955
- case "transport_error":
12956
- return `Embedded ${embedded} ${compartment}; ${remaining} failed: ${failure.reason}.${failure.retryable ? retry : ""}`;
12957
- }
13301
+ function formatEmbedFailureSummary(embedded, remaining, failure, style = "markdown") {
13302
+ const historyBlocks = `history block${embedded === 1 ? "" : "s"}`;
13303
+ const progress = `Indexed ${embedded} ${historyBlocks}; ${remaining} remain.`;
13304
+ return `${progress} ${renderEmbeddingFailure(failure?.class ?? "empty_result", style)}`;
12958
13305
  }
12959
13306
 
12960
13307
  // ../plugin/src/hooks/magic-context/format-embed-status.ts
@@ -13112,7 +13459,7 @@ Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`,
13112
13459
  return {
13113
13460
  text: `## /ctx-embed
13114
13461
 
13115
- ${formatEmbedFailureSummary(outcome.embedded, outcome.remaining, outcome.failure)}`,
13462
+ ${formatEmbedFailureSummary(outcome.embedded, outcome.remaining, outcome.failure, "plain")}`,
13116
13463
  level: "info"
13117
13464
  };
13118
13465
  default:
@@ -14007,78 +14354,93 @@ function buildReplacementContent(tagId) {
14007
14354
  }
14008
14355
  function applyPendingOperations(sessionId, db, targets, protectedTagIds, preloadedTags, preloadedPendingOps, syntheticPendingOps = [], editMarkerTagIds = new Set) {
14009
14356
  let didMutateMessage = false;
14010
- db.transaction(() => {
14011
- const tags = preloadedTags ?? getTagsBySession(db, sessionId);
14012
- const tagStatusById = new Map(tags.map((tag) => [tag.tagNumber, tag.status]));
14013
- const tagTypeById = new Map(tags.map((tag) => [tag.tagNumber, tag.type]));
14014
- const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
14015
- const opsToApply = [
14016
- ...pendingOps.map((op) => ({ op, synthetic: false })),
14017
- ...syntheticPendingOps.map((op) => ({ op, synthetic: true }))
14018
- ];
14019
- const skeletonWindow = new Set(tags.filter((tag) => tag.type === "tool").map((tag) => tag.tagNumber).sort((left, right) => right - left).slice(0, RECENT_TOOL_SKELETON_WINDOW));
14020
- for (const { op: pendingOp, synthetic } of opsToApply) {
14021
- const tagStatus = tagStatusById.get(pendingOp.tagId);
14022
- if (tagStatus === "compacted" || tagStatus === "dropped") {
14023
- if (!synthetic)
14024
- removePendingOp(db, sessionId, pendingOp.tagId);
14025
- continue;
14026
- }
14027
- if (protectedTagIds.has(pendingOp.tagId)) {
14028
- continue;
14029
- }
14030
- const target = targets.get(pendingOp.tagId);
14031
- const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
14032
- if (synthetic) {
14033
- if (!isToolTag || target?.canDrop?.() !== true)
14357
+ let admitted = false;
14358
+ let startedAt = 0;
14359
+ try {
14360
+ db.transaction(() => {
14361
+ admitted = true;
14362
+ startedAt = performance.now();
14363
+ const tags = preloadedTags ?? getTagsBySession(db, sessionId);
14364
+ const tagStatusById = new Map(tags.map((tag) => [tag.tagNumber, tag.status]));
14365
+ const tagTypeById = new Map(tags.map((tag) => [tag.tagNumber, tag.type]));
14366
+ const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
14367
+ const opsToApply = [
14368
+ ...pendingOps.map((op) => ({ op, synthetic: false })),
14369
+ ...syntheticPendingOps.map((op) => ({ op, synthetic: true }))
14370
+ ];
14371
+ const skeletonWindow = new Set(tags.filter((tag) => tag.type === "tool").map((tag) => tag.tagNumber).sort((left, right) => right - left).slice(0, RECENT_TOOL_SKELETON_WINDOW));
14372
+ for (const { op: pendingOp, synthetic } of opsToApply) {
14373
+ const tagStatus = tagStatusById.get(pendingOp.tagId);
14374
+ if (tagStatus === "compacted" || tagStatus === "dropped") {
14375
+ if (!synthetic)
14376
+ removePendingOp(db, sessionId, pendingOp.tagId);
14034
14377
  continue;
14035
- }
14036
- let shouldPersistDrop = false;
14037
- if (isToolTag) {
14038
- if (editMarkerTagIds.has(pendingOp.tagId)) {
14039
- const markResult = target?.editMarker?.() ?? "absent";
14040
- if (markResult === "incomplete" || markResult === "absent") {
14041
- continue;
14042
- }
14043
- didMutateMessage = true;
14044
- updateTagDropMode(db, sessionId, pendingOp.tagId, "edit_marker");
14045
- shouldPersistDrop = true;
14046
- } else if (skeletonWindow.has(pendingOp.tagId)) {
14047
- const truncResult = target?.truncate?.() ?? "absent";
14048
- if (truncResult === "incomplete" || synthetic && truncResult !== "truncated") {
14378
+ }
14379
+ if (protectedTagIds.has(pendingOp.tagId)) {
14380
+ continue;
14381
+ }
14382
+ const target = targets.get(pendingOp.tagId);
14383
+ const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
14384
+ if (synthetic) {
14385
+ if (!isToolTag || target?.canDrop?.() !== true)
14049
14386
  continue;
14050
- }
14051
- if (truncResult === "truncated") {
14387
+ }
14388
+ let shouldPersistDrop = false;
14389
+ if (isToolTag) {
14390
+ if (editMarkerTagIds.has(pendingOp.tagId)) {
14391
+ const markResult = target?.editMarker?.() ?? "absent";
14392
+ if (markResult === "incomplete" || markResult === "absent") {
14393
+ continue;
14394
+ }
14052
14395
  didMutateMessage = true;
14396
+ updateTagDropMode(db, sessionId, pendingOp.tagId, "edit_marker");
14397
+ shouldPersistDrop = true;
14398
+ } else if (skeletonWindow.has(pendingOp.tagId)) {
14399
+ const truncResult = target?.truncate?.() ?? "absent";
14400
+ if (truncResult === "incomplete" || synthetic && truncResult !== "truncated") {
14401
+ continue;
14402
+ }
14403
+ if (truncResult === "truncated") {
14404
+ didMutateMessage = true;
14405
+ }
14406
+ updateTagDropMode(db, sessionId, pendingOp.tagId, "truncated");
14407
+ shouldPersistDrop = true;
14408
+ } else {
14409
+ const dropResult = target?.drop?.() ?? "absent";
14410
+ if (dropResult === "incomplete" || synthetic && dropResult !== "removed") {
14411
+ continue;
14412
+ }
14413
+ if (dropResult === "removed") {
14414
+ didMutateMessage = true;
14415
+ }
14416
+ updateTagDropMode(db, sessionId, pendingOp.tagId, "full");
14417
+ shouldPersistDrop = true;
14053
14418
  }
14054
- updateTagDropMode(db, sessionId, pendingOp.tagId, "truncated");
14055
- shouldPersistDrop = true;
14056
- } else {
14057
- const dropResult = target?.drop?.() ?? "absent";
14058
- if (dropResult === "incomplete" || synthetic && dropResult !== "removed") {
14059
- continue;
14060
- }
14061
- if (dropResult === "removed") {
14419
+ } else if (target) {
14420
+ const changed = target.setContent(buildReplacementContent(pendingOp.tagId));
14421
+ if (changed)
14062
14422
  didMutateMessage = true;
14063
- }
14064
- updateTagDropMode(db, sessionId, pendingOp.tagId, "full");
14423
+ shouldPersistDrop = true;
14424
+ } else if (!synthetic) {
14065
14425
  shouldPersistDrop = true;
14066
14426
  }
14067
- } else if (target) {
14068
- const changed = target.setContent(buildReplacementContent(pendingOp.tagId));
14069
- if (changed)
14070
- didMutateMessage = true;
14071
- shouldPersistDrop = true;
14072
- } else if (!synthetic) {
14073
- shouldPersistDrop = true;
14427
+ if (!shouldPersistDrop)
14428
+ continue;
14429
+ updateTagStatus(db, sessionId, pendingOp.tagId, "dropped");
14430
+ if (!synthetic)
14431
+ removePendingOp(db, sessionId, pendingOp.tagId);
14074
14432
  }
14075
- if (!shouldPersistDrop)
14076
- continue;
14077
- updateTagStatus(db, sessionId, pendingOp.tagId, "dropped");
14078
- if (!synthetic)
14079
- removePendingOp(db, sessionId, pendingOp.tagId);
14433
+ }).immediate();
14434
+ } catch (error) {
14435
+ if (!admitted && /database (?:table )?is locked|sqlite_(busy|locked)/i.test(String(error))) {
14436
+ sessionLog(sessionId, "pending operations write admission busy; retaining prior bytes for retry");
14437
+ return false;
14080
14438
  }
14081
- })();
14439
+ throw error;
14440
+ } finally {
14441
+ if (admitted)
14442
+ logSlowWriteTransaction("apply_pending_operations", startedAt);
14443
+ }
14082
14444
  return didMutateMessage;
14083
14445
  }
14084
14446
  function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
@@ -16253,11 +16615,16 @@ function buildAggregateTarget(tagId, occurrences, requiresToolArcSkeleton) {
16253
16615
  const complete = occurrences.some((occ) => occ.kind === "tool_use") && occurrences.some((occ) => occ.kind === "tool_result");
16254
16616
  if (!complete)
16255
16617
  return "incomplete";
16256
- if (!requiresToolArcSkeleton && occurrences.every((occ) => occ.part.remove && occ.part.canRemove?.() !== false)) {
16257
- let removed = false;
16258
- for (const occ of occurrences)
16259
- removed = occ.part.remove?.() || removed;
16260
- return removed ? "removed" : "absent";
16618
+ if (!requiresToolArcSkeleton) {
16619
+ const authorization = occurrences.map((occ) => occ.part.canRemove?.());
16620
+ if (authorization.includes("defer"))
16621
+ return "incomplete";
16622
+ if (occurrences.every((occ, index) => occ.part.remove && authorization[index] !== false)) {
16623
+ let removed = false;
16624
+ for (const occ of occurrences)
16625
+ removed = occ.part.remove?.() || removed;
16626
+ return removed ? "removed" : "absent";
16627
+ }
16261
16628
  }
16262
16629
  const sentinel = `[dropped §${tagId}§]`;
16263
16630
  let any = false;
@@ -17836,24 +18203,9 @@ function validateHistorianOutput(text, _sessionId, chunk, _priorCompartments, se
17836
18203
  };
17837
18204
  }
17838
18205
  var HISTORIAN_PERSISTENT_FAILURE_THRESHOLD = 3;
17839
- function buildHistorianFailureNotice(failureCount, lastError) {
17840
- if (failureCount >= HISTORIAN_PERSISTENT_FAILURE_THRESHOLD) {
17841
- return [
17842
- "## Magic Context — history comparting needs attention",
17843
- "",
17844
- `Magic Context has been unable to compart this session's history ${failureCount} times in a row. This usually means the configured historian model is misconfigured or unreachable (Magic Context already retried every fallback model automatically).`,
17845
- "",
17846
- `Last error: ${lastError}`,
17847
- "",
17848
- "Check your historian model in magic-context.jsonc, then restart. Your conversation keeps working normally in the meantime — this only affects how older history is summarized."
17849
- ].join(`
17850
- `);
17851
- }
17852
- return [
17853
- "## Magic Context",
17854
- "",
17855
- "Hit a transient issue comparting history this turn — Magic Context will retry automatically on the next turn. Nothing is lost and your conversation continues normally. You'll only be alerted again if this keeps happening."
17856
- ].join(`
18206
+ function buildHistorianFailureNotice(failureCount, _lastError) {
18207
+ const heading = failureCount >= HISTORIAN_PERSISTENT_FAILURE_THRESHOLD ? "## Magic Context — History compression" : "## Magic Context";
18208
+ return [heading, "", renderUserFacingFailure("historian_unavailable")].join(`
17857
18209
  `);
17858
18210
  }
17859
18211
  function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationError, language) {
@@ -20916,7 +21268,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
20916
21268
  emergencyDroppedTools++;
20917
21269
  }
20918
21270
  }
20919
- })();
21271
+ }).immediate();
20920
21272
  sessionLog(sessionId, `emergency tiered drop: ${plan.reason}`);
20921
21273
  } else {
20922
21274
  sessionLog(sessionId, `emergency tiered drop skipped: ${plan.reason}`);
@@ -20945,7 +21297,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
20945
21297
  droppedStaleReduceCalls++;
20946
21298
  }
20947
21299
  }
20948
- })();
21300
+ }).immediate();
20949
21301
  }
20950
21302
  if (routine) {
20951
21303
  db.transaction(() => {
@@ -20982,7 +21334,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
20982
21334
  }
20983
21335
  }
20984
21336
  }
20985
- })();
21337
+ }).immediate();
20986
21338
  }
20987
21339
  const toolFingerprints = routine ? buildPiToolFingerprints(piMessages, resolveStableId) : new Map;
20988
21340
  if (toolFingerprints.size > 0) {
@@ -21020,7 +21372,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
21020
21372
  }
21021
21373
  }
21022
21374
  }
21023
- })();
21375
+ }).immediate();
21024
21376
  }
21025
21377
  if (droppedTools > 0 || deduplicatedTools > 0 || droppedInjections > 0 || droppedStaleReduceCalls > 0) {
21026
21378
  sessionLog(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, stale ctx_reduce=${droppedStaleReduceCalls}, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
@@ -23356,6 +23708,23 @@ function applyNativeReasoningReplayPi(args, saved) {
23356
23708
  args.messages[index] = message;
23357
23709
  return nextIds.size;
23358
23710
  }
23711
+ function authorizePiToolRemoval(args) {
23712
+ if (!args.saved)
23713
+ return false;
23714
+ if (args.saved.get(args.callId) === NATIVE_TOOL_REMOVAL_MARKER)
23715
+ return true;
23716
+ if (!args.canApply)
23717
+ return false;
23718
+ try {
23719
+ saveNativeToolInputs(args.db, args.sessionId, new Map([[args.callId, NATIVE_TOOL_REMOVAL_MARKER]]));
23720
+ args.saved.set(args.callId, NATIVE_TOOL_REMOVAL_MARKER);
23721
+ return true;
23722
+ } catch (error) {
23723
+ sessionLog(args.sessionId, `tool arc removal persistence failed; retaining pair: ${String(error)}`);
23724
+ const priorDrop = args.db.prepare("SELECT 1 FROM tags WHERE session_id = ? AND type = 'tool' AND message_id = ? AND status = 'dropped' LIMIT 1").get(args.sessionId, args.callId);
23725
+ return priorDrop ? false : "defer";
23726
+ }
23727
+ }
23359
23728
 
23360
23729
  // src/note-visibility-pi.ts
23361
23730
  var NOTE_TOOL_NAME = "ctx_note";
@@ -23515,13 +23884,38 @@ function parsePersistedLkgSlot(row) {
23515
23884
  const jsonPrefix = record.json_prefix;
23516
23885
  const lastInputMessageId = record.last_input_message_id;
23517
23886
  const capturedAt = record.captured_at;
23518
- const inputIdSeq = parseStringArray(record.input_id_seq);
23887
+ let inputIdsRaw = record.input_id_seq;
23888
+ let piOutputEntryIds;
23889
+ if (typeof inputIdsRaw === "string") {
23890
+ try {
23891
+ const metadata = JSON.parse(inputIdsRaw);
23892
+ if (metadata && !Array.isArray(metadata) && metadata.version === 1 && Array.isArray(metadata.piOutputEntryIds)) {
23893
+ if (!metadata.piOutputEntryIds.every((id) => id === null || typeof id === "string" && id.length > 0))
23894
+ return;
23895
+ piOutputEntryIds = metadata.piOutputEntryIds;
23896
+ inputIdsRaw = JSON.stringify(metadata.inputIds);
23897
+ }
23898
+ } catch {
23899
+ return;
23900
+ }
23901
+ }
23902
+ const inputIdSeq = parseStringArray(inputIdsRaw);
23519
23903
  const inputContentDigests = parseStringArray(record.input_content_digests);
23520
23904
  const modelKey = parseNullableString(record.model_key);
23521
23905
  const providerKey = parseNullableString(record.provider_key);
23522
23906
  if (typeof jsonPrefix !== "string" || typeof lastInputMessageId !== "string" || lastInputMessageId.length === 0 || typeof capturedAt !== "number" || !Number.isFinite(capturedAt) || inputIdSeq === null || inputContentDigests === null || inputContentDigests.length !== inputIdSeq.length || modelKey === undefined || providerKey === undefined) {
23523
23907
  return;
23524
23908
  }
23909
+ if (piOutputEntryIds) {
23910
+ try {
23911
+ const output = JSON.parse(jsonPrefix);
23912
+ const inputs = new Set(inputIdSeq);
23913
+ if (!Array.isArray(output) || output.length !== piOutputEntryIds.length || piOutputEntryIds.some((id) => id !== null && !inputs.has(id)))
23914
+ return;
23915
+ } catch {
23916
+ return;
23917
+ }
23918
+ }
23525
23919
  let inputContentSignatures;
23526
23920
  if (record.input_content_signatures !== null && record.input_content_signatures !== undefined) {
23527
23921
  const parsed = parseStringArray(record.input_content_signatures);
@@ -23540,6 +23934,8 @@ function parsePersistedLkgSlot(row) {
23540
23934
  };
23541
23935
  if (inputContentSignatures)
23542
23936
  slot.inputContentSignatures = inputContentSignatures;
23937
+ if (piOutputEntryIds)
23938
+ slot.piOutputEntryIds = piOutputEntryIds;
23543
23939
  const rowVersion = parseNullableInteger(record.row_version);
23544
23940
  if (rowVersion !== undefined)
23545
23941
  slot.rowVersion = rowVersion;
@@ -23565,7 +23961,11 @@ function saveLkgSlotToDb(db, sessionId, slot) {
23565
23961
  provider_key = excluded.provider_key,
23566
23962
  captured_at = excluded.captured_at,
23567
23963
  row_version = excluded.row_version,
23568
- capture_sequence = excluded.capture_sequence`).run(sessionId, slot.jsonPrefix, JSON.stringify(slot.inputIdSeq), JSON.stringify(slot.inputContentDigests), slot.inputContentSignatures ? JSON.stringify(slot.inputContentSignatures) : null, slot.lastInputMessageId, slot.modelKey, slot.providerKey, slot.capturedAt, slot.rowVersion ?? null, slot.captureSequence ?? null);
23964
+ capture_sequence = excluded.capture_sequence`).run(sessionId, slot.jsonPrefix, JSON.stringify(slot.piOutputEntryIds ? {
23965
+ version: 1,
23966
+ inputIds: slot.inputIdSeq,
23967
+ piOutputEntryIds: slot.piOutputEntryIds
23968
+ } : slot.inputIdSeq), JSON.stringify(slot.inputContentDigests), slot.inputContentSignatures ? JSON.stringify(slot.inputContentSignatures) : null, slot.lastInputMessageId, slot.modelKey, slot.providerKey, slot.capturedAt, slot.rowVersion ?? null, slot.captureSequence ?? null);
23569
23969
  return true;
23570
23970
  } catch (error) {
23571
23971
  sessionLog(sessionId, "LKG snapshot persistence failed (in-memory slot retained):", error);
@@ -23624,7 +24024,8 @@ function registerLkgPersistence(backend) {
23624
24024
  }
23625
24025
  function slotBytes(slot) {
23626
24026
  const digestBytes = slot.inputContentDigests.reduce((total, digest) => total + 2 * digest.length, 0);
23627
- return 2 * slot.jsonPrefix.length + digestBytes + LKG_METADATA_BYTES;
24027
+ const ownershipBytes = slot.piOutputEntryIds?.reduce((total, id) => total + (id?.length ?? 0) * 2 + 8, 0) ?? 0;
24028
+ return 2 * slot.jsonPrefix.length + digestBytes + ownershipBytes + LKG_METADATA_BYTES;
23628
24029
  }
23629
24030
  var LKG_SNAPSHOT_ARRAY = Symbol("array");
23630
24031
  var LKG_SNAPSHOT_OBJECT = Symbol("object");
@@ -23785,6 +24186,7 @@ function captureSlot(sessionId, slot) {
23785
24186
  const entry = {
23786
24187
  slot: {
23787
24188
  ...slot,
24189
+ ...slot.piOutputEntryIds ? { piOutputEntryIds: [...slot.piOutputEntryIds] } : {},
23788
24190
  inputIdSeq: [...slot.inputIdSeq],
23789
24191
  inputContentDigests: [...slot.inputContentDigests],
23790
24192
  inputContentSignatures: slot.inputContentSignatures ? [...slot.inputContentSignatures] : undefined
@@ -23823,6 +24225,7 @@ function installHydratedSlot(sessionId, slot) {
23823
24225
  const entry = {
23824
24226
  slot: {
23825
24227
  ...slot,
24228
+ ...slot.piOutputEntryIds ? { piOutputEntryIds: [...slot.piOutputEntryIds] } : {},
23826
24229
  inputIdSeq: [...slot.inputIdSeq],
23827
24230
  inputContentDigests: [...slot.inputContentDigests],
23828
24231
  inputContentSignatures: slot.inputContentSignatures ? [...slot.inputContentSignatures] : undefined
@@ -23855,6 +24258,7 @@ function hydrateSlotFromPersistence(sessionId) {
23855
24258
  function copySlotForRead(slot) {
23856
24259
  return {
23857
24260
  ...slot,
24261
+ ...slot.piOutputEntryIds ? { piOutputEntryIds: [...slot.piOutputEntryIds] } : {},
23858
24262
  inputIdSeq: [...slot.inputIdSeq],
23859
24263
  inputContentDigests: [...slot.inputContentDigests],
23860
24264
  inputContentSignatures: slot.inputContentSignatures ? [...slot.inputContentSignatures] : undefined
@@ -24333,16 +24737,13 @@ function snapshotInputs(messages, entryIds) {
24333
24737
  }
24334
24738
  const inputs = [];
24335
24739
  const seen = new Set;
24336
- for (let index = firstStableIndex;index < entryIds.length; index += 1) {
24337
- const id = entryIds[index];
24338
- if (typeof id !== "string" || id.length === 0) {
24339
- return { inputs: [], failure: "lkg_entry_id_gap" };
24340
- }
24341
- if (seen.has(id))
24342
- return { inputs: [], failure: "lkg_duplicate_entry_id" };
24740
+ for (let index = 0;index < entryIds.length; index += 1) {
24343
24741
  const fields = lkgContentFields(messages[index]);
24344
24742
  if (!fields)
24345
24743
  return { inputs: [], failure: "lkg_content_snapshot_failed" };
24744
+ const id = entryIds[index] || `pi-lkg-unmapped:${lkgContentDigestFromFields(fields)}`;
24745
+ if (seen.has(id))
24746
+ return { inputs: [], failure: "lkg_duplicate_entry_id" };
24346
24747
  seen.add(id);
24347
24748
  inputs.push({ id, messageIndex: index, fields });
24348
24749
  }
@@ -24438,6 +24839,29 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
24438
24839
  if (snapshot.replayAnchorInputIndex === null || snapshot.pristineTail === null) {
24439
24840
  return { ok: false, reason: "lkg_miss" };
24440
24841
  }
24842
+ const slot = getSlot(snapshot.sessionId);
24843
+ const start = slot?.inputIdSeq.indexOf(snapshot.inputs[0]?.id ?? "") ?? -1;
24844
+ if (slot && start > 0) {
24845
+ const ownership = slot.piOutputEntryIds;
24846
+ if (!ownership)
24847
+ return { ok: false, reason: "lkg_output_mapping_unavailable" };
24848
+ if (slot.modelKey !== snapshot.modelKey || slot.providerKey !== snapshot.providerKey)
24849
+ return { ok: false, reason: "lkg_model_mismatch" };
24850
+ const surviving = snapshot.inputs.slice(0, snapshot.replayAnchorInputIndex + 1);
24851
+ if (surviving.length !== slot.inputIdSeq.length - start || surviving.some((input, index) => input.id !== slot.inputIdSeq[start + index]))
24852
+ return { ok: false, reason: "lkg_invalidated_reshape" };
24853
+ if (surviving.some((input, index) => lkgContentDigestFromFields(input.fields) !== slot.inputContentDigests[start + index]))
24854
+ return { ok: false, reason: "lkg_content_mismatch" };
24855
+ const removed = new Set(slot.inputIdSeq.slice(0, start));
24856
+ const prefix = JSON.parse(slot.jsonPrefix);
24857
+ return {
24858
+ ok: true,
24859
+ messages: [
24860
+ ...prefix.filter((_, index) => ownership[index] === null || !removed.has(ownership[index] ?? "")),
24861
+ ...snapshot.pristineTail
24862
+ ]
24863
+ };
24864
+ }
24441
24865
  const entry = {
24442
24866
  pristineTail: snapshot.pristineTail,
24443
24867
  entryInputIds: snapshot.inputs.map((input) => input.id),
@@ -24474,6 +24898,22 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
24474
24898
  return;
24475
24899
  }
24476
24900
  const state = stateFor(snapshot.sessionId);
24901
+ const idsByDigest = new Map;
24902
+ if (!args.outputEntryIds)
24903
+ for (const input of snapshot.inputs) {
24904
+ const digest = lkgContentDigestFromFields(input.fields);
24905
+ idsByDigest.set(digest, idsByDigest.has(digest) ? undefined : input.id);
24906
+ }
24907
+ const inferredIds = args.outputEntryIds ?? args.outputMessages.map((message) => {
24908
+ const fields = lkgContentFields(message);
24909
+ if (!fields)
24910
+ return;
24911
+ const digest = lkgContentDigestFromFields(fields);
24912
+ return idsByDigest.get(digest);
24913
+ });
24914
+ const outputIds = args.outputEntryIds ?? inferredIds;
24915
+ const inputIds = new Set(snapshot.inputs.map((input) => input.id));
24916
+ const ownership = outputIds.length === args.outputMessages.length && outputIds.every((id) => id === null || typeof id === "string" && inputIds.has(id)) ? [...outputIds] : undefined;
24477
24917
  state.captureSequence += 1;
24478
24918
  const plan = {
24479
24919
  sessionId: snapshot.sessionId,
@@ -24514,6 +24954,7 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
24514
24954
  reusedPrefix = incremental.reusedPrefix;
24515
24955
  const slot = {
24516
24956
  jsonPrefix: plan.jsonPrefix,
24957
+ piOutputEntryIds: ownership,
24517
24958
  inputIdSeq,
24518
24959
  inputContentDigests: incremental.digests,
24519
24960
  inputContentSignatures,
@@ -24561,6 +25002,24 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
24561
25002
  };
24562
25003
  return { beginPass, replay, captureAppliedPass };
24563
25004
  }
25005
+ function resolvePiLkgOutputEntryIds(messages, syntheticLeadingCount, entryId) {
25006
+ const ids = messages.map((message, index) => index < syntheticLeadingCount ? null : isRecord(message) ? entryId(message) : undefined);
25007
+ const syntheticOwners = new Map;
25008
+ for (let index = 0;index < messages.length; index++) {
25009
+ const message = messages[index];
25010
+ const owner = ids[index];
25011
+ if (typeof owner !== "string" || !isRecord(message) || message.role !== "assistant" || !Array.isArray(message.content))
25012
+ continue;
25013
+ for (const part of message.content) {
25014
+ if (isRecord(part) && part.type === "toolCall" && part.syntheticTodoMarker === true && typeof part.id === "string")
25015
+ syntheticOwners.set(part.id, syntheticOwners.has(part.id) ? undefined : owner);
25016
+ }
25017
+ }
25018
+ return ids.map((id, index) => {
25019
+ const message = messages[index];
25020
+ return id === undefined && isRecord(message) && message.role === "toolResult" && message.syntheticTodoMarker === true && typeof message.toolCallId === "string" ? syntheticOwners.get(message.toolCallId) : id;
25021
+ });
25022
+ }
24564
25023
 
24565
25024
  // src/pi-pressure.ts
24566
25025
  function extractAssistantUsage(message) {
@@ -26405,7 +26864,9 @@ function createPiAssistantPart(working, messageIndex, partIndex, markDirty) {
26405
26864
  canRemove() {
26406
26865
  const message = working[messageIndex];
26407
26866
  const part = message.content[partIndex];
26408
- return part?.type === "toolCall" && canRemoveNativeToolCall(message, part.id) && toolRemovalAuthorization.get(working)?.(part.id) !== false;
26867
+ if (part?.type !== "toolCall" || !canRemoveNativeToolCall(message, part.id))
26868
+ return false;
26869
+ return toolRemovalAuthorization.get(working)?.(part.id) ?? true;
26409
26870
  },
26410
26871
  remove() {
26411
26872
  return markToolRemoval(working, messageIndex, partIndex);
@@ -27439,6 +27900,9 @@ function registerPiContextHandler(pi, baseOptions) {
27439
27900
  modelKey: lkgModelKey,
27440
27901
  providerKey: lkgProviderKey
27441
27902
  });
27903
+ const lkgInputIdByRef = new Map;
27904
+ for (const input of lkgPassSnapshot.inputs)
27905
+ lkgInputIdByRef.set(event.messages[input.messageIndex], input.id);
27442
27906
  const thinkingBindingRecoveryApplied = applyPiThinkingBindingRecovery({
27443
27907
  db: options.db,
27444
27908
  sessionId,
@@ -27747,6 +28211,7 @@ function registerPiContextHandler(pi, baseOptions) {
27747
28211
  projectDirectory,
27748
28212
  sessionMeta,
27749
28213
  messages: event.messages,
28214
+ lkgEntryIds: event.messages.map((message) => lkgInputIdByRef.get(message)),
27750
28215
  smartDrops: options.smartDrops === true,
27751
28216
  protectedTags: options.protectedTags ?? 20,
27752
28217
  protectedTokens: options.protectedTokens,
@@ -28026,6 +28491,7 @@ function registerPiContextHandler(pi, baseOptions) {
28026
28491
  lkgCoordinator.captureAppliedPass({
28027
28492
  snapshot: lkgPassSnapshot,
28028
28493
  outputMessages,
28494
+ outputEntryIds: resolvePiLkgOutputEntryIds(outputMessages, result.syntheticLeadingCount, (message) => result.postCommitEntryIdByRef.get(message) ?? result.lkgEntryIdByRef.get(message)),
28029
28495
  cacheBusting: result.bustedThisPass
28030
28496
  });
28031
28497
  }
@@ -28586,7 +29052,8 @@ async function runCompactionOffPipeline(args) {
28586
29052
  protectedTagNumbers: new Set,
28587
29053
  pendingDropTagNumbers: new Set
28588
29054
  },
28589
- postCommitEntryIdByRef: new Map
29055
+ postCommitEntryIdByRef: new Map,
29056
+ lkgEntryIdByRef: new Map
28590
29057
  };
28591
29058
  }
28592
29059
  async function runPipeline(args) {
@@ -28628,22 +29095,13 @@ async function runPipeline(args) {
28628
29095
  const tTranscriptBuild = performance.now();
28629
29096
  const transcript = createPiTranscript(args.messages, args.sessionId, args.entryIds, {
28630
29097
  preserveReasoningToolArcs: args.reasoningClearing?.preserveReasoningToolArcs,
28631
- authorizeToolRemoval: (callId) => {
28632
- if (!nativeRemovalInputs)
28633
- return false;
28634
- if (nativeRemovalInputs.get(callId) === NATIVE_TOOL_REMOVAL_MARKER)
28635
- return true;
28636
- if (!isCacheBustingPass)
28637
- return false;
28638
- try {
28639
- saveNativeToolInputs(args.db, args.sessionId, new Map([[callId, NATIVE_TOOL_REMOVAL_MARKER]]));
28640
- nativeRemovalInputs.set(callId, NATIVE_TOOL_REMOVAL_MARKER);
28641
- return true;
28642
- } catch (error) {
28643
- sessionLog(args.sessionId, `tool arc removal persistence failed; retaining pair: ${String(error)}`);
28644
- return false;
28645
- }
28646
- }
29098
+ authorizeToolRemoval: (callId) => authorizePiToolRemoval({
29099
+ db: args.db,
29100
+ sessionId: args.sessionId,
29101
+ callId,
29102
+ saved: nativeRemovalInputs,
29103
+ canApply: isCacheBustingPass
29104
+ })
28647
29105
  });
28648
29106
  logTransformTiming(args.sessionId, "transcriptBuild", tTranscriptBuild);
28649
29107
  const workingMessages = transcript.getWorkingMessages();
@@ -29189,6 +29647,7 @@ async function runPipeline(args) {
29189
29647
  const tPostCommitStableIdMaps = performance.now();
29190
29648
  const postCommitStableIdByRef = new Map;
29191
29649
  const postCommitEntryIdByRef = new Map;
29650
+ const lkgEntryIdByRef = new Map;
29192
29651
  for (let i = 0;i < args.messages.length; i++) {
29193
29652
  const m = args.messages[i];
29194
29653
  if (!m || typeof m !== "object")
@@ -29196,6 +29655,9 @@ async function runPipeline(args) {
29196
29655
  const id = resolvePiStableId(m, i, args.entryIds, args.entryIdByRef ?? undefined);
29197
29656
  if (id)
29198
29657
  postCommitStableIdByRef.set(m, id);
29658
+ const lkgId = args.lkgEntryIds?.[i];
29659
+ if (lkgId)
29660
+ lkgEntryIdByRef.set(m, lkgId);
29199
29661
  const realId = args.entryIds?.[i];
29200
29662
  if (typeof realId === "string" && realId.length > 0) {
29201
29663
  postCommitEntryIdByRef.set(m, realId);
@@ -29360,7 +29822,8 @@ async function runPipeline(args) {
29360
29822
  reasoningWatermark: args.sessionMeta.clearedReasoningThroughTag ?? 0,
29361
29823
  activeTags,
29362
29824
  channelBaselineSnapshot,
29363
- postCommitEntryIdByRef
29825
+ postCommitEntryIdByRef,
29826
+ lkgEntryIdByRef
29364
29827
  };
29365
29828
  }
29366
29829
  var AUTO_SEARCH_NO_HINT_REASONS = new Set([
@@ -30269,10 +30732,10 @@ Found ${existingStaging.compartments.length} staged compartment(s) from ${existi
30269
30732
  }
30270
30733
  }
30271
30734
  }
30735
+ sessionLog(sessionId, `recomp partial code=${userFacingFailureCode("recomp_unavailable")} reason="${reason}"`);
30272
30736
  return [
30273
- `Persisted ${promoted.compartments.length} compartment${promoted.compartments.length === 1 ? "" : "s"} from ${passCount} successful pass${passCount === 1 ? "" : "es"}.`,
30274
- `Covered raw history 1-${lastCompartmentEnd} out of ${rawMessageCount} total messages.`,
30275
- `Remaining messages ${lastCompartmentEnd + 1}-${protectedTailStart - 1} were not rebuilt (${reason}).`
30737
+ `Rebuilt ${promoted.compartments.length} history block${promoted.compartments.length === 1 ? "" : "s"} across ${passCount} successful pass${passCount === 1 ? "" : "es"}.`,
30738
+ renderUserFacingFailure("recomp_unavailable")
30276
30739
  ].join(`
30277
30740
  `);
30278
30741
  }
@@ -30297,11 +30760,10 @@ Recomp stopped because raw history ${offset}-${protectedTailStart - 1} could not
30297
30760
 
30298
30761
  ${partial}`;
30299
30762
  }
30763
+ sessionLog(sessionId, `recomp failed code=${userFacingFailureCode("recomp_unavailable")} reason="${chunkCoverageError}"`);
30300
30764
  return `## Magic Recomp — Failed
30301
30765
 
30302
- Recomp stopped because the raw chunk could not be represented safely: ${chunkCoverageError}
30303
-
30304
- Nothing was written.`;
30766
+ ${renderUserFacingFailure("recomp_unavailable")}`;
30305
30767
  }
30306
30768
  const references = buildReferenceBlocks({
30307
30769
  sessionId,
@@ -30343,11 +30805,10 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
30343
30805
  callbacks: {
30344
30806
  onRepairRetry: async (error) => {
30345
30807
  emitProgress(`Repair retry (pass ${passCount + 1})…`);
30808
+ sessionLog(sessionId, `recomp retry code=${userFacingFailureCode("recomp_unavailable")} reason="${error}"`);
30346
30809
  await sendStatusNotification(client, sessionId, `## Magic Recomp
30347
30810
 
30348
- Historian pass ${passCount + 1}, attempt ${passAttempt} is continuing with a repair retry for messages ${chunk.startIndex}-${chunk.endIndex}.
30349
-
30350
- The previous output did not validate: ${error}`, notifParams());
30811
+ History compression is retrying this pass. ${renderUserFacingFailure("recomp_unavailable")}`, notifParams());
30351
30812
  },
30352
30813
  onModelFallback: (modelId, index, total) => {
30353
30814
  const short = modelId.includes("/") ? modelId.split("/").pop() : modelId;
@@ -30362,9 +30823,7 @@ The previous output did not validate: ${error}`, notifParams());
30362
30823
  if (smallerChunk.messageCount > 0 && smallerChunk.endIndex < chunk.endIndex) {
30363
30824
  await sendStatusNotification(client, sessionId, `## Magic Recomp
30364
30825
 
30365
- Historian pass ${passCount + 1}, attempt ${passAttempt} is continuing with a smaller chunk ending at ${smallerChunk.endIndex} because messages ${chunk.startIndex}-${chunk.endIndex} could not be validated.
30366
-
30367
- Validator result: ${validatedPass.error}`, notifParams());
30826
+ History compression is retrying with a smaller set of messages. ${renderUserFacingFailure("recomp_unavailable")}`, notifParams());
30368
30827
  currentTokenBudget = reducedBudget;
30369
30828
  passAttempt += 1;
30370
30829
  continue;
@@ -30381,7 +30840,8 @@ Validator result: ${validatedPass.error}`, notifParams());
30381
30840
  chunkEndOrdinal: chunk.endIndex,
30382
30841
  compartmentsProduced: 0
30383
30842
  });
30384
- const partial = await promoteAndFinalize(`historian failed to validate messages ${chunk.startIndex}-${chunk.endIndex}: ${validatedPass.error}`);
30843
+ sessionLog(sessionId, `recomp failed code=${userFacingFailureCode("recomp_unavailable")} reason="${validatedPass.error}" messageRange=${chunk.startIndex}-${chunk.endIndex}`);
30844
+ const partial = await promoteAndFinalize(`history model response did not validate for messages ${chunk.startIndex}-${chunk.endIndex}`);
30385
30845
  if (partial) {
30386
30846
  return `## Magic Recomp — Partial
30387
30847
 
@@ -30389,9 +30849,7 @@ ${partial}`;
30389
30849
  }
30390
30850
  return `## Magic Recomp — Failed
30391
30851
 
30392
- Recomp failed while rebuilding messages ${chunk.startIndex}-${chunk.endIndex}: ${validatedPass.error}
30393
-
30394
- Nothing was written.`;
30852
+ ${renderUserFacingFailure("recomp_unavailable")}`;
30395
30853
  }
30396
30854
  {
30397
30855
  const passComps = validatedPass.compartments ?? [];
@@ -30442,11 +30900,10 @@ Recomp made no forward progress after messages ${chunk.startIndex}-${chunk.endIn
30442
30900
  const mergedValidationError = validateStoredCompartments(candidateCompartments);
30443
30901
  if (mergedValidationError) {
30444
30902
  clearRecompStaging(db, sessionId);
30903
+ sessionLog(sessionId, `recomp failed code=${userFacingFailureCode("recomp_unavailable")} reason="${mergedValidationError}"`);
30445
30904
  return `## Magic Recomp — Failed
30446
30905
 
30447
- Recomp completed ${passCount} pass${passCount === 1 ? "" : "es"} but produced an invalid final compartment set: ${mergedValidationError}
30448
-
30449
- Nothing was written.`;
30906
+ ${renderUserFacingFailure("recomp_unavailable")}`;
30450
30907
  }
30451
30908
  saveRecompStagingPass(db, sessionId, passCount, candidateCompartments, candidateFacts);
30452
30909
  const lastCompartmentEnd = candidateCompartments[candidateCompartments.length - 1]?.endMessage ?? 0;
@@ -30498,11 +30955,10 @@ Another process acquired the compartment-state lease before recomp could publish
30498
30955
  `);
30499
30956
  } catch (error) {
30500
30957
  const message = getErrorMessage(error);
30958
+ sessionLog(sessionId, `recomp failed code=${userFacingFailureCode("recomp_unavailable")} reason="${message}"`);
30501
30959
  return `## Magic Recomp — Failed
30502
30960
 
30503
- Recomp failed unexpectedly: ${message}
30504
-
30505
- Staging data preserved for resume on next attempt.`;
30961
+ ${renderUserFacingFailure("recomp_unavailable")}`;
30506
30962
  } finally {
30507
30963
  updateSessionMeta(db, sessionId, { compartmentInProgress: false });
30508
30964
  cleanupHistorianStateFile(currentStateFilePath);
@@ -30720,11 +31176,10 @@ Recomp stopped because raw history ${offset}-${snapEnd} could not be turned into
30720
31176
  }
30721
31177
  const chunkCoverageError = validateChunkCoverage(chunk);
30722
31178
  if (chunkCoverageError) {
31179
+ log(`[magic-context] partial recomp failed session=${sessionId} code=${userFacingFailureCode("recomp_unavailable")} reason="${chunkCoverageError}"`);
30723
31180
  return `## Magic Recomp — Failed
30724
31181
 
30725
- Partial recomp stopped because the raw chunk could not be represented safely: ${chunkCoverageError}
30726
-
30727
- Original state preserved (staging kept for retry).`;
31182
+ ${renderUserFacingFailure("recomp_unavailable")}`;
30728
31183
  }
30729
31184
  const references = buildReferenceBlocks({
30730
31185
  sessionId,
@@ -30764,11 +31219,10 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
30764
31219
  language: deps.language,
30765
31220
  callbacks: {
30766
31221
  onRepairRetry: async (error) => {
31222
+ log(`[magic-context] partial recomp retry session=${sessionId} code=${userFacingFailureCode("recomp_unavailable")} reason="${error}"`);
30767
31223
  await sendStatusNotification(client, sessionId, `## Magic Recomp — Partial
30768
31224
 
30769
- Historian pass ${passCount + 1}, attempt ${passAttempt} is continuing with a repair retry for messages ${chunk.startIndex}-${chunk.endIndex}.
30770
-
30771
- The previous output did not validate: ${error}`, notifParams());
31225
+ History compression is retrying this pass. ${renderUserFacingFailure("recomp_unavailable")}`, notifParams());
30772
31226
  }
30773
31227
  }
30774
31228
  });
@@ -30779,19 +31233,16 @@ The previous output did not validate: ${error}`, notifParams());
30779
31233
  if (smallerChunk.messageCount > 0 && smallerChunk.endIndex < chunk.endIndex) {
30780
31234
  await sendStatusNotification(client, sessionId, `## Magic Recomp — Partial
30781
31235
 
30782
- Historian pass ${passCount + 1}, attempt ${passAttempt} is continuing with a smaller chunk ending at ${smallerChunk.endIndex} because messages ${chunk.startIndex}-${chunk.endIndex} could not be validated.
30783
-
30784
- Validator result: ${validatedPass.error}`, notifParams());
31236
+ History compression is retrying with a smaller set of messages. ${renderUserFacingFailure("recomp_unavailable")}`, notifParams());
30785
31237
  currentTokenBudget = reducedBudget;
30786
31238
  passAttempt += 1;
30787
31239
  continue;
30788
31240
  }
30789
31241
  }
31242
+ log(`[magic-context] partial recomp failed session=${sessionId} code=${userFacingFailureCode("recomp_unavailable")} reason="${validatedPass.error}" messageRange=${chunk.startIndex}-${chunk.endIndex}`);
30790
31243
  return `## Magic Recomp — Failed
30791
31244
 
30792
- Partial recomp failed while rebuilding messages ${chunk.startIndex}-${chunk.endIndex}: ${validatedPass.error}
30793
-
30794
- Original state preserved (staging kept for retry).`;
31245
+ ${renderUserFacingFailure("recomp_unavailable")}`;
30795
31246
  }
30796
31247
  candidateCompartments = [
30797
31248
  ...candidateCompartments,
@@ -30826,11 +31277,10 @@ Partial recomp completed historian passes but the final compartment set failed v
30826
31277
  `);
30827
31278
  } catch (error) {
30828
31279
  const message = getErrorMessage(error);
31280
+ log(`[magic-context] partial recomp failed session=${sessionId} code=${userFacingFailureCode("recomp_unavailable")} reason="${message}"`);
30829
31281
  return `## Magic Recomp — Failed
30830
31282
 
30831
- Partial recomp failed unexpectedly: ${message}
30832
-
30833
- Staging preserved for resume on next attempt.`;
31283
+ ${renderUserFacingFailure("recomp_unavailable")}`;
30834
31284
  } finally {
30835
31285
  updateSessionMeta(db, sessionId, { compartmentInProgress: false });
30836
31286
  const leftoverStaging = getRecompStaging(db, sessionId);
@@ -31952,6 +32402,78 @@ ${migrationSummary}` : "",
31952
32402
  });
31953
32403
  }
31954
32404
 
32405
+ // src/dialogs/status-dialog.ts
32406
+ import {
32407
+ matchesKey as matchesKey2,
32408
+ truncateToWidth,
32409
+ visibleWidth
32410
+ } from "@earendil-works/pi-tui";
32411
+
32412
+ // ../plugin/src/shared/format-bytes.ts
32413
+ function formatBytes(bytes) {
32414
+ if (bytes < 1024)
32415
+ return `${bytes}B`;
32416
+ if (bytes < 1024 * 1024)
32417
+ return `${(bytes / 1024).toFixed(1)}KB`;
32418
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
32419
+ }
32420
+ // ../plugin/src/hooks/magic-context/m0-token-breakdown.ts
32421
+ function computeM0BlockTokens(db, sessionId, args) {
32422
+ const {
32423
+ m0Text,
32424
+ projectIdentity,
32425
+ injectionBudgetTokens,
32426
+ memoryBlockCount,
32427
+ compartmentTokensOverride
32428
+ } = args;
32429
+ const docsBlock = extractM0Block(m0Text, "project-docs");
32430
+ const docsTokens = docsBlock ? estimateTokens(docsBlock) : 0;
32431
+ const profileBlock = extractM0Block(m0Text, "user-profile");
32432
+ const profileTokens = profileBlock ? estimateTokens(profileBlock) : 0;
32433
+ let memoryTokens = 0;
32434
+ let memoryFromM0 = false;
32435
+ const memoryBlock = extractM0Block(m0Text, "project-memory");
32436
+ if (memoryBlock) {
32437
+ memoryTokens = estimateTokens(memoryBlock);
32438
+ memoryFromM0 = true;
32439
+ }
32440
+ const muralTokens = m0Text.includes("<memory-mural>") ? 1521 : 0;
32441
+ let compartmentTokens = 0;
32442
+ const historyBlock = extractM0Block(m0Text, "session-history");
32443
+ if (typeof compartmentTokensOverride === "number" && Number.isFinite(compartmentTokensOverride) && compartmentTokensOverride >= 0) {
32444
+ compartmentTokens = compartmentTokensOverride;
32445
+ } else if (historyBlock) {
32446
+ compartmentTokens = estimateTokens(historyBlock);
32447
+ } else {
32448
+ try {
32449
+ const compRows = db.prepare("SELECT content, title, start_message, end_message FROM compartments WHERE session_id = ?").all(sessionId);
32450
+ for (const c of compRows) {
32451
+ compartmentTokens += estimateTokens(`## ${c.start_message}-${c.end_message} · ${c.title}
32452
+ ${c.content}
32453
+ `);
32454
+ }
32455
+ } catch {}
32456
+ }
32457
+ if (!memoryFromM0 && memoryBlockCount > 0 && projectIdentity) {
32458
+ try {
32459
+ const memories = getMemoriesByProject(db, projectIdentity, ["active", "permanent"]);
32460
+ const selected = injectionBudgetTokens ? trimMemoriesToBudgetV2(sessionId, memories, injectionBudgetTokens).renderOrder : memories;
32461
+ const block = renderMemoryBlockV2(selected);
32462
+ memoryTokens = block ? estimateTokens(block) : 0;
32463
+ } catch {
32464
+ memoryTokens = 0;
32465
+ }
32466
+ }
32467
+ return {
32468
+ docsTokens,
32469
+ profileTokens,
32470
+ memoryTokens,
32471
+ muralTokens,
32472
+ compartmentTokens,
32473
+ factTokens: 0
32474
+ };
32475
+ }
32476
+
31955
32477
  // ../plugin/src/shared/cache-ttl-display.ts
31956
32478
  function resolveCacheTtlDisplay(args) {
31957
32479
  if (args.sessionModelKey && (!args.modelKey || args.sessionModelKey === args.modelKey) || !args.modelKey && !args.sessionModelKey && args.sessionValue !== "5m") {
@@ -32005,8 +32527,75 @@ function formatThresholdClampNote(opts) {
32005
32527
  return ` [clamped: ${opts.configuredValue}% > ${opts.maxPercentage}%]`;
32006
32528
  }
32007
32529
 
32008
- // ../plugin/src/shared/rust-mode-status.ts
32009
- var RUST_MODE_HOST_PATHS_LINE = "Host backends → MODULE: ctx_memory, ctx_note; historian: module-side";
32530
+ // ../plugin/src/shared/status-summary.ts
32531
+ function formatCount(value) {
32532
+ return Math.round(value).toLocaleString();
32533
+ }
32534
+ function compressionText(summary) {
32535
+ const historyBlocks = `${summary.compression.historyBlockCount} history block${summary.compression.historyBlockCount === 1 ? "" : "s"}`;
32536
+ switch (summary.compression.state) {
32537
+ case "off":
32538
+ return "Off";
32539
+ case "compressing":
32540
+ return `Compressing history · ${historyBlocks}`;
32541
+ case "ready":
32542
+ return `Ready · ${historyBlocks}`;
32543
+ case "waiting":
32544
+ return "Waiting for enough conversation history";
32545
+ }
32546
+ }
32547
+ function reclaimableText(summary) {
32548
+ const count = Math.max(0, Math.floor(summary.reclaimable.toolOutputCount));
32549
+ if (count === 0)
32550
+ return "none";
32551
+ const outputs = `${count} spent tool output${count === 1 ? "" : "s"}`;
32552
+ return `${outputs} (~${Math.round(Math.max(0, summary.reclaimable.tokens) / 1000)}k tokens)`;
32553
+ }
32554
+ function embeddingText(summary) {
32555
+ if (summary.embedding.state === "off")
32556
+ return "Off";
32557
+ const coverage = `${formatCount(summary.embedding.indexed)} / ${formatCount(summary.embedding.total)} history blocks indexed`;
32558
+ switch (summary.embedding.state) {
32559
+ case "running":
32560
+ return `Running · ${coverage}`;
32561
+ case "paused":
32562
+ return `Paused · ${coverage}`;
32563
+ case "stopped":
32564
+ return `Paused after an issue · ${coverage}`;
32565
+ case "ready":
32566
+ return `Ready · ${coverage}`;
32567
+ case "waiting":
32568
+ return `Waiting · ${coverage}`;
32569
+ }
32570
+ }
32571
+ function renderUserStatusSummary(summary, style) {
32572
+ const context = `${summary.usagePercentage.toFixed(1)}% of usable context (${formatCount(summary.inputTokens)} / ${summary.usableContextTokens > 0 ? formatCount(summary.usableContextTokens) : "?"} tokens)`;
32573
+ const values = [
32574
+ ["Context", context],
32575
+ ["Cache lifetime", summary.cacheLifetime],
32576
+ [
32577
+ "Automatic compression",
32578
+ summary.automaticCompressionThreshold === null ? "Off" : `at ${summary.automaticCompressionThreshold.toFixed(1)}% of usable context`
32579
+ ],
32580
+ ["History compression", compressionText(summary)],
32581
+ ["Reclaimable", reclaimableText(summary)],
32582
+ [
32583
+ "Memory",
32584
+ `${formatCount(summary.memoryCount)} memories · ${formatCount(summary.noteCount)} notes`
32585
+ ],
32586
+ ["Search indexing", embeddingText(summary)]
32587
+ ];
32588
+ const lines = style === "markdown" ? [
32589
+ "## Magic Context Status",
32590
+ "",
32591
+ ...values.map(([label, value]) => `- **${label}:** ${value}`)
32592
+ ] : ["Magic Context Status", ...values.map(([label, value]) => `${label}: ${value}`)];
32593
+ for (const warning of summary.warnings) {
32594
+ lines.push(style === "markdown" ? `- **Warning:** ${renderUserFacingFailure(warning)}` : `Warning: ${renderUserFacingFailure(warning)}`);
32595
+ }
32596
+ return lines.join(`
32597
+ `);
32598
+ }
32010
32599
 
32011
32600
  // ../plugin/src/shared/tail-hygiene-status.ts
32012
32601
  function finiteNumber(value, fallback = 0) {
@@ -32023,7 +32612,8 @@ function resolveTailHygieneStatus(tsBaseline, rustBaseline) {
32023
32612
  evaluable: rustBaseline.evaluable === true,
32024
32613
  generationInvalidated: rustBaseline.generation_invalidated === true,
32025
32614
  baselineGeneration: Math.max(0, finiteNumber(rustBaseline.baseline_generation)),
32026
- computedAt: Math.max(0, finiteNumber(rustBaseline.computed_at_ms))
32615
+ computedAt: Math.max(0, finiteNumber(rustBaseline.computed_at_ms)),
32616
+ reclaimableToolOutputCount: Math.max(0, Math.floor(finiteNumber(rustBaseline.reclaimable_tool_output_count)))
32027
32617
  };
32028
32618
  }
32029
32619
  if (tsBaseline === undefined)
@@ -32037,7 +32627,8 @@ function resolveTailHygieneStatus(tsBaseline, rustBaseline) {
32037
32627
  evaluable: tsBaseline.evaluable && !tsBaseline.generationInvalidated,
32038
32628
  generationInvalidated: tsBaseline.generationInvalidated,
32039
32629
  baselineGeneration: Math.max(0, tsBaseline.baselineGeneration),
32040
- computedAt: Math.max(0, tsBaseline.computedAt)
32630
+ computedAt: Math.max(0, tsBaseline.computedAt),
32631
+ reclaimableToolOutputCount: tsBaseline.baselineParts.filter((part) => part.kind === "toolOutput" && part.uTokens > 0).length
32041
32632
  };
32042
32633
  }
32043
32634
  function formatTailHygiene(status) {
@@ -32045,223 +32636,10 @@ function formatTailHygiene(status) {
32045
32636
  const state = status.evaluable ? "" : " · held until baseline refresh";
32046
32637
  return `${percentage}% · ${status.u.toLocaleString()} / ${status.t.toLocaleString()} tok${state}`;
32047
32638
  }
32048
-
32049
- // ../plugin/src/shared/format-bytes.ts
32050
- function formatBytes(bytes) {
32051
- if (bytes < 1024)
32052
- return `${bytes}B`;
32053
- if (bytes < 1024 * 1024)
32054
- return `${(bytes / 1024).toFixed(1)}KB`;
32055
- return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
32056
- }
32057
- // ../plugin/src/hooks/magic-context/execute-status.ts
32058
- function formatExecuteThreshold(detail, contextLimit) {
32059
- const { percentage, mode } = detail;
32060
- const clampNote = formatThresholdClampNote({
32061
- clamped: detail.clamped,
32062
- mode,
32063
- configuredValue: detail.configuredValue,
32064
- contextLimit,
32065
- maxPercentage: MAX_EXECUTE_THRESHOLD
32066
- });
32067
- if (mode === "tokens" && contextLimit > 0) {
32068
- const tokens = Math.floor(percentage / 100 * contextLimit);
32069
- return `${tokens.toLocaleString()} tokens (${percentage.toFixed(1)}% of ${contextLimit.toLocaleString()}) [token-mode]${clampNote}`;
32070
- }
32071
- if (contextLimit > 0) {
32072
- const tokens = Math.floor(percentage / 100 * contextLimit);
32073
- return `${percentage}% (${tokens.toLocaleString()} of ${contextLimit.toLocaleString()})${clampNote}`;
32074
- }
32075
- return `${percentage}%${clampNote}`;
32076
- }
32077
- function executeStatus(db, sessionId, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit, dreamer, windowGeometry, tailHygiene, contextUsage, rustMode = false, display) {
32078
- const thresholdDetail = resolveExecuteThresholdDetail(executeThresholdPercentageConfig, liveModelKey, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, {
32079
- tokensConfig: executeThresholdTokens,
32080
- contextLimit,
32081
- sessionId
32082
- });
32083
- const executeThresholdPercentage = thresholdDetail.percentage;
32084
- const openCodeDbResolution = resolveOpenCodeDbPath();
32085
- const openCodeDbReadFailure = getOpenCodeDbReadFailure();
32086
- const openCodeDbStatusLine = !openCodeDbPathExists(openCodeDbResolution) ? formatOpenCodeDbMissingStatusLine(openCodeDbResolution) : openCodeDbReadFailure?.path === openCodeDbResolution.path ? formatOpenCodeDbReadFailureStatusLine(openCodeDbReadFailure) : null;
32087
- try {
32088
- const meta = getOrCreateSessionMeta(db, sessionId);
32089
- const tags = getTagsBySession(db, sessionId);
32090
- const pendingOps = getPendingOps(db, sessionId);
32091
- const protectionWindow = getProtectionWindowForSession(db, sessionId);
32092
- const activeTags = tags.filter((t) => t.status === "active");
32093
- const droppedTags = tags.filter((t) => t.status === "dropped");
32094
- const totalBytes = activeTags.reduce((sum, t) => sum + t.byteSize, 0);
32095
- const ttlDisplay = resolveCacheTtlDisplay({
32096
- configured: display?.cacheTtlConfig ?? "5m",
32097
- configuredExplicitly: display?.cacheTtlConfigured === true,
32098
- modelKey: liveModelKey,
32099
- sessionValue: meta.cacheTtl,
32100
- sessionModelKey: meta.lastObservedModelKey
32101
- });
32102
- let ttlMs;
32103
- try {
32104
- ttlMs = parseCacheTtl(ttlDisplay.value);
32105
- } catch (error) {
32106
- sessionLog(sessionId, `invalid cache_ttl "${ttlDisplay.value}" in ctx-status; falling back to default 5m`, error);
32107
- ttlMs = parseCacheTtl("5m");
32108
- }
32109
- const elapsed = Date.now() - meta.lastResponseTime;
32110
- const remainingMs = Math.max(0, ttlMs - elapsed);
32111
- const cacheExpired = remainingMs === 0 && meta.lastResponseTime > 0;
32112
- const proactiveCompartmentTrigger = getProactiveCompartmentTriggerPercentage(executeThresholdPercentage);
32113
- const displayInputTokens = contextUsage?.inputTokens ?? meta.lastInputTokens;
32114
- const displayPercentage = contextUsage?.percentage ?? meta.lastContextPercentage;
32115
- const displayContextLimit = contextLimit && contextLimit > 0 ? contextLimit : displayPercentage > 0 ? Math.round(displayInputTokens / (displayPercentage / 100)) : 0;
32116
- const parseFailureLines = (display?.configParseFailures ?? []).map(formatConfigParseStatusLine);
32117
- const lines = [
32118
- ...openCodeDbStatusLine ? [openCodeDbStatusLine, ""] : [],
32119
- ...parseFailureLines,
32120
- ...parseFailureLines.length > 0 ? [""] : [],
32121
- "## Magic Status",
32122
- "",
32123
- `**Session:** ${sessionId}`,
32124
- `**Tag counter:** ${meta.counter}`,
32125
- "",
32126
- "### Tags",
32127
- `- Active: ${activeTags.length} (~${formatBytes(totalBytes)})`,
32128
- `- Dropped: ${droppedTags.length}`,
32129
- `- Total: ${tags.length}`,
32130
- "",
32131
- "### Pending Queue",
32132
- `- Drops: ${pendingOps.length}`,
32133
- `- Total queued: ${pendingOps.length}`,
32134
- "",
32135
- ...meta.lastTransformError ? ["### Last Transform Error", `- ${meta.lastTransformError}`, ""] : [],
32136
- "### Cache TTL",
32137
- `- ${formatCacheTtlDisplay(ttlDisplay)}`,
32138
- `- Last response: ${meta.lastResponseTime > 0 ? `${Math.round(elapsed / 1000)}s ago` : "never"}`,
32139
- `- Remaining: ${cacheExpired ? "expired" : ttlMs === Number.POSITIVE_INFINITY ? "never (MC never assumes expiry — external cache-keep)" : `${Math.round(remainingMs / 1000)}s`}`,
32140
- `- Queue will auto-execute: ${cacheExpired ? "yes (cache expired)" : ttlMs === Number.POSITIVE_INFINITY ? `when context >= ${executeThresholdPercentage}%` : `when TTL expires or context >= ${executeThresholdPercentage}%`}`,
32141
- "",
32142
- "### Execute Threshold",
32143
- `- Execute threshold: ${formatExecuteThreshold(thresholdDetail, displayContextLimit)}`,
32144
- `- Last input tokens: ${displayInputTokens.toLocaleString()} tokens`,
32145
- "",
32146
- `**Protected tool tags:** ${protectionWindow.status.protectedCount} (${protectionWindow.status.protectedMass.toLocaleString()} tokens)`,
32147
- `**Protection floor:** ${protectionWindow.status.floor.toLocaleString()} tokens`,
32148
- `**Subagent session:** ${meta.isSubagent}`
32149
- ];
32150
- const storage = getMagicContextStorageResolution();
32151
- lines.push("", `**Storage:** ${storage.path} (${storage.source})`);
32152
- if (rustMode)
32153
- lines.push("", "### Rust Mode", `- ${RUST_MODE_HOST_PATHS_LINE}`);
32154
- if (tailHygiene !== undefined) {
32155
- lines.push("", "### Tail Hygiene", `- Reclaimable / eligible: ${formatTailHygiene(tailHygiene)}`, "- Reasoning is excluded from both terms.");
32156
- }
32157
- if (dreamer?.backlog && Object.keys(dreamer.backlog).length > 0) {
32158
- lines.push("", "### Dreamer", ...dreamer.progress ? [
32159
- `- Running: ${dreamer.progress.task} — ${dreamer.progress.processed}/${dreamer.progress.total} processed`
32160
- ] : [], ...formatDreamTaskBacklogs(dreamer.backlog).split("\\n"));
32161
- }
32162
- if (displayPercentage > 0 || displayInputTokens > 0) {
32163
- lines.push("", "### Context Usage", `- Last percentage: ${displayPercentage.toFixed(1)}%`, `- Last input tokens: ${displayInputTokens.toLocaleString()}`, `- Resolved context limit: ${displayContextLimit > 0 ? displayContextLimit.toLocaleString() : "unknown"}`, ...windowGeometry ? [`- ${formatWindowDerivationLine(displayInputTokens, windowGeometry)}`] : [], `- Proactive compartment evaluation: ${proactiveCompartmentTrigger}%`, `- Post-drop target for historian: ${(executeThresholdPercentage * POST_DROP_TARGET_RATIO).toFixed(0)}% (${executeThresholdPercentage}% * ${POST_DROP_TARGET_RATIO})`, `- Commit cluster trigger: ${commitClusterTrigger?.enabled !== false ? `enabled (min ${commitClusterTrigger?.min_clusters ?? 3} clusters)` : "disabled"}, tail-size trigger: > 3x compartment budget`);
32164
- }
32165
- const compartments = getCompartments(db, sessionId);
32166
- let historyBlockTokens = 0;
32167
- for (const c of compartments) {
32168
- historyBlockTokens += estimateTokens(`## ${c.startMessage}-${c.endMessage} · ${c.title}
32169
- ${c.content}
32170
- `);
32171
- }
32172
- const budgetTokens = historyBudgetPercentage && displayContextLimit > 0 ? Math.floor(displayContextLimit * (Math.min(executeThresholdPercentage, 80) / 100) * historyBudgetPercentage) : null;
32173
- const budgetUsage = budgetTokens ? (historyBlockTokens / budgetTokens * 100).toFixed(0) : null;
32174
- lines.push("", "### History Compression", `- Compartments: ${compartments.length}`, `- History block: ~${historyBlockTokens.toLocaleString()} tokens`, ...budgetTokens ? [
32175
- `- History budget: ~${budgetTokens.toLocaleString()} tokens (${budgetUsage}% used)`,
32176
- `- Older compartments demote tiers automatically at render time to fit the budget`
32177
- ] : [`- History budget: not configured (history_budget_percentage not set)`]);
32178
- if (pendingOps.length > 0) {
32179
- lines.push("", "### Queued Operations");
32180
- for (const op of pendingOps) {
32181
- lines.push(`- §${op.tagId}§ → ${op.operation}`);
32182
- }
32183
- }
32184
- if (dreamer?.backlog && Object.keys(dreamer.backlog).length > 0) {
32185
- lines.push("", "### Dreamer Backlog", formatDreamTaskBacklogs(dreamer.backlog));
32186
- }
32187
- if (dreamer?.progress) {
32188
- lines.push("", "### Dreamer Progress", `- ${dreamer.progress.task}: ${dreamer.progress.processed}/${dreamer.progress.total} processed this run`);
32189
- }
32190
- return lines.join(`
32191
- `);
32192
- } catch (error) {
32193
- sessionLog(sessionId, "ctx-status failed:", error);
32194
- return `Error: Failed to read context status. ${getErrorMessage(error)}`;
32195
- }
32196
- }
32197
-
32198
- // src/dialogs/status-dialog.ts
32199
- import {
32200
- matchesKey as matchesKey2,
32201
- truncateToWidth,
32202
- visibleWidth
32203
- } from "@earendil-works/pi-tui";
32204
-
32205
- // ../plugin/src/hooks/magic-context/m0-token-breakdown.ts
32206
- function computeM0BlockTokens(db, sessionId, args) {
32207
- const {
32208
- m0Text,
32209
- projectIdentity,
32210
- injectionBudgetTokens,
32211
- memoryBlockCount,
32212
- compartmentTokensOverride
32213
- } = args;
32214
- const docsBlock = extractM0Block(m0Text, "project-docs");
32215
- const docsTokens = docsBlock ? estimateTokens(docsBlock) : 0;
32216
- const profileBlock = extractM0Block(m0Text, "user-profile");
32217
- const profileTokens = profileBlock ? estimateTokens(profileBlock) : 0;
32218
- let memoryTokens = 0;
32219
- let memoryFromM0 = false;
32220
- const memoryBlock = extractM0Block(m0Text, "project-memory");
32221
- if (memoryBlock) {
32222
- memoryTokens = estimateTokens(memoryBlock);
32223
- memoryFromM0 = true;
32224
- }
32225
- const muralTokens = m0Text.includes("<memory-mural>") ? 1521 : 0;
32226
- let compartmentTokens = 0;
32227
- const historyBlock = extractM0Block(m0Text, "session-history");
32228
- if (typeof compartmentTokensOverride === "number" && Number.isFinite(compartmentTokensOverride) && compartmentTokensOverride >= 0) {
32229
- compartmentTokens = compartmentTokensOverride;
32230
- } else if (historyBlock) {
32231
- compartmentTokens = estimateTokens(historyBlock);
32232
- } else {
32233
- try {
32234
- const compRows = db.prepare("SELECT content, title, start_message, end_message FROM compartments WHERE session_id = ?").all(sessionId);
32235
- for (const c of compRows) {
32236
- compartmentTokens += estimateTokens(`## ${c.start_message}-${c.end_message} · ${c.title}
32237
- ${c.content}
32238
- `);
32239
- }
32240
- } catch {}
32241
- }
32242
- if (!memoryFromM0 && memoryBlockCount > 0 && projectIdentity) {
32243
- try {
32244
- const memories = getMemoriesByProject(db, projectIdentity, ["active", "permanent"]);
32245
- const selected = injectionBudgetTokens ? trimMemoriesToBudgetV2(sessionId, memories, injectionBudgetTokens).renderOrder : memories;
32246
- const block = renderMemoryBlockV2(selected);
32247
- memoryTokens = block ? estimateTokens(block) : 0;
32248
- } catch {
32249
- memoryTokens = 0;
32250
- }
32251
- }
32252
- return {
32253
- docsTokens,
32254
- profileTokens,
32255
- memoryTokens,
32256
- muralTokens,
32257
- compartmentTokens,
32258
- factTokens: 0
32259
- };
32260
- }
32261
32639
  // package.json
32262
32640
  var package_default = {
32263
32641
  name: "@cortexkit/pi-magic-context",
32264
- version: "0.42.0",
32642
+ version: "0.42.2",
32265
32643
  type: "module",
32266
32644
  description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
32267
32645
  main: "dist/index.js",
@@ -32292,7 +32670,7 @@ var package_default = {
32292
32670
  build: "tsc -p ../retina-local-fs/tsconfig.build.json && rm -f dist/index.js dist/subagent-entry.js dist/historian-calibration-extension.js dist/transformers-web.js dist/transformers-node-wasm.js dist/*-*.js && bun build ../plugin/src/features/magic-context/memory/transformers-web-entry.ts --outfile dist/transformers-web.js --target browser --format esm --external onnxruntime-web && bun ../plugin/scripts/build-transformers-node-wasm.ts dist && bun build src/index.ts src/subagent-entry.ts src/historian-calibration-extension.ts --outdir dist --target node --format esm --splitting --external @earendil-works/pi-coding-agent --external @earendil-works/pi-tui --external onnxruntime-node --external onnxruntime-web --external sharp --external node:sqlite",
32293
32671
  "build:e2e-argv": "bun build src/subagent-runner.ts --outfile ../../tests/docker/.generated/subagent-runner-e2e.mjs --target node --format esm --external node:sqlite",
32294
32672
  typecheck: "tsc -p ../retina-local-fs/tsconfig.build.json && tsc --noEmit",
32295
- test: "bun install --frozen-lockfile && bun test",
32673
+ test: "bun install --frozen-lockfile && bun test --parallel --timeout 30000",
32296
32674
  lint: "biome check src",
32297
32675
  "lint:fix": "biome check --write src",
32298
32676
  format: "biome format --write src",
@@ -32366,7 +32744,7 @@ var COLORS = {
32366
32744
  toolDefs: "#f472b6"
32367
32745
  };
32368
32746
  var REFRESH_INTERVAL_MS = 1000;
32369
- async function showStatusDialog(pi, ctx, deps) {
32747
+ async function showStatusDialog(pi, ctx, deps, initialDiagnostics = false) {
32370
32748
  const sessionId = resolveSessionId(ctx);
32371
32749
  if (!sessionId)
32372
32750
  throw new Error("No active Pi session is available.");
@@ -32377,7 +32755,8 @@ async function showStatusDialog(pi, ctx, deps) {
32377
32755
  sessionId,
32378
32756
  theme,
32379
32757
  tui,
32380
- done
32758
+ done,
32759
+ initialDiagnostics
32381
32760
  }), {
32382
32761
  overlay: true,
32383
32762
  overlayOptions: { anchor: "center", width: 78 }
@@ -32389,8 +32768,10 @@ class StatusDialogComponent {
32389
32768
  detail;
32390
32769
  refreshTimer = null;
32391
32770
  closed = false;
32771
+ diagnostics;
32392
32772
  constructor(props) {
32393
32773
  this.props = props;
32774
+ this.diagnostics = props.initialDiagnostics;
32394
32775
  this.detail = buildPiStatusDetail(props.pi, props.ctx, props.deps, props.sessionId);
32395
32776
  this.refreshTimer = setInterval(() => {
32396
32777
  if (this.closed)
@@ -32402,6 +32783,11 @@ class StatusDialogComponent {
32402
32783
  }, REFRESH_INTERVAL_MS);
32403
32784
  }
32404
32785
  handleInput(data) {
32786
+ if (matchesKey2(data, "d")) {
32787
+ this.diagnostics = !this.diagnostics;
32788
+ this.props.tui.requestRender();
32789
+ return;
32790
+ }
32405
32791
  if (matchesKey2(data, "escape") || matchesKey2(data, "ctrl+c") || matchesKey2(data, "return")) {
32406
32792
  this.close();
32407
32793
  }
@@ -32419,7 +32805,7 @@ class StatusDialogComponent {
32419
32805
  invalidate() {}
32420
32806
  render(width) {
32421
32807
  const innerWidth = Math.max(20, width - 4);
32422
- const inner = renderInner(this.detail, this.props.theme, innerWidth);
32808
+ const inner = renderInner(this.detail, this.props.theme, innerWidth, this.diagnostics);
32423
32809
  return drawBorder(inner, width, this.props.theme);
32424
32810
  }
32425
32811
  dispose() {
@@ -32429,10 +32815,70 @@ class StatusDialogComponent {
32429
32815
  }
32430
32816
  }
32431
32817
  }
32432
- function renderInner(s, theme, innerWidth) {
32818
+ function formatPiStatusSummary(s) {
32819
+ const warnings = [];
32820
+ if (s.lastTransformError)
32821
+ warnings.push("transform_update_failed");
32822
+ if (s.historianFailureCount > 0)
32823
+ warnings.push("historian_unavailable");
32824
+ if (s.configParseFailures.length > 0 || s.hasDeprecatedProtectedTags) {
32825
+ warnings.push("configuration_warning");
32826
+ }
32827
+ if (s.embedding.state === "stopped")
32828
+ warnings.push("embedding_unavailable");
32829
+ return renderUserStatusSummary({
32830
+ inputTokens: s.inputTokens,
32831
+ usableContextTokens: s.contextLimit,
32832
+ usagePercentage: s.usagePercentage,
32833
+ cacheLifetime: formatCacheTtlDisplay({
32834
+ value: s.cacheTtl,
32835
+ source: s.cacheTtlSource,
32836
+ modelKey: s.cacheTtlModelKey
32837
+ }).replace(/^Cache TTL:\s*/, ""),
32838
+ automaticCompressionThreshold: s.compactionEnabled ? s.executeThreshold : null,
32839
+ compression: {
32840
+ state: s.historianRunning ? "compressing" : s.compartmentCount > 0 ? "ready" : "waiting",
32841
+ historyBlockCount: s.compartmentCount
32842
+ },
32843
+ reclaimable: {
32844
+ toolOutputCount: s.tailHygiene?.reclaimableToolOutputCount ?? 0,
32845
+ tokens: s.tailHygiene?.u ?? 0
32846
+ },
32847
+ memoryCount: s.memoryCount,
32848
+ noteCount: s.sessionNoteCount + s.readySmartNoteCount,
32849
+ embedding: s.embedding,
32850
+ warnings
32851
+ }, "plain");
32852
+ }
32853
+ function formatPiStatusDiagnostics(s) {
32854
+ const summary = formatPiStatusSummary(s).replace("Magic Context Status", "Magic Context Diagnostics");
32855
+ return [
32856
+ summary,
32857
+ "",
32858
+ `Session: ${s.sessionId}`,
32859
+ `Active profile: ${s.activeProfile ?? "none"}`,
32860
+ `Work tokens: ${fmt2(s.newWorkTokens)} new · ${fmt2(s.totalInputTokens)} total input`,
32861
+ ...s.tailHygiene ? [`Hygiene: ${formatTailHygiene(s.tailHygiene)}`] : [],
32862
+ `Tags: ${s.activeTags} active · ${s.droppedTags} dropped · ${s.totalTags} total`,
32863
+ `Pending drops: ${s.pendingOpsCount}`,
32864
+ `Protected tokens: ${fmt2(s.protectedTokens.protectedMass)} (${s.protectedTokens.protectedCount} tags / floor ${fmt2(s.protectedTokens.floor)})`,
32865
+ `History block tokens: ${fmt2(s.historyBlockTokens)}`,
32866
+ `Compression budget: ${s.compressionBudget ? `${fmt2(s.compressionBudget)} (${s.compressionUsage} used)` : "unavailable"}`,
32867
+ `Subagent: ${s.isSubagent ? "yes" : "no"}`
32868
+ ].join(`
32869
+ `);
32870
+ }
32871
+ function renderInner(s, theme, innerWidth, diagnostics) {
32433
32872
  const pctColor = s.usagePercentage >= 80 ? "error" : s.usagePercentage >= 65 ? "warning" : "accent";
32434
32873
  const lines = [];
32435
32874
  lines.push(`${theme.fg("accent", theme.bold("⚡ Magic Context Status"))} ${theme.fg("muted", `v${package_default.version}`)}`);
32875
+ lines.push(theme.fg("muted", `[D] Diagnostics: ${diagnostics ? "on" : "off"}`));
32876
+ if (!diagnostics) {
32877
+ lines.push("", ...formatPiStatusSummary(s).split(`
32878
+ `).slice(1));
32879
+ lines.push("", theme.fg("muted", "Press D for diagnostics · Escape to close"));
32880
+ return lines;
32881
+ }
32436
32882
  lines.push("");
32437
32883
  for (const failure of s.configParseFailures) {
32438
32884
  lines.push(theme.fg("error", formatConfigParseStatusLine(failure)));
@@ -32484,9 +32930,9 @@ function renderInner(s, theme, innerWidth) {
32484
32930
  })}`);
32485
32931
  lines.push(`Protected tokens ${fmt2(s.protectedTokens.protectedMass)} tok (${s.protectedTokens.protectedCount} tags / floor ${fmt2(s.protectedTokens.floor)}) · Subagent ${s.isSubagent ? "yes" : "no"} · History block ~${fmt2(s.historyBlockTokens)} tok${s.compressionBudget ? ` · Budget ~${fmt2(s.compressionBudget)} tok (${s.compressionUsage} used)` : ""}`);
32486
32932
  if (s.lastTransformError)
32487
- lines.push(theme.fg("error", `⚠ ${s.lastTransformError}`));
32933
+ lines.push(theme.fg("error", renderUserFacingFailure("transform_update_failed")));
32488
32934
  if (s.historianLastError)
32489
- lines.push(theme.fg("error", `⚠ ${s.historianLastError}`));
32935
+ lines.push(theme.fg("error", renderUserFacingFailure("historian_unavailable")));
32490
32936
  lines.push("");
32491
32937
  lines.push(theme.fg("muted", "Press Escape to close"));
32492
32938
  return lines;
@@ -32606,6 +33052,17 @@ ${safeStringify(tool.parameters)}`);
32606
33052
  const cacheRemainingMs = neverExpires ? Number.POSITIVE_INFINITY : meta.lastResponseTime > 0 ? Math.max(0, cacheTtlMs - elapsed) : cacheTtlMs;
32607
33053
  const cacheExpired = meta.lastResponseTime > 0 && cacheRemainingMs === 0;
32608
33054
  const historyBlockTokens = compartmentTokens + factTokens;
33055
+ const embeddingCoverage = safeRead(() => getEmbeddingCoverageStatus(deps.db, deps.projectIdentity, sessionId), {
33056
+ enabled: false,
33057
+ model: "off",
33058
+ provider: "off",
33059
+ session: { embedded: 0, total: 0 },
33060
+ memories: { embedded: 0, total: 0 },
33061
+ commits: { embedded: 0, total: 0, gitEnabled: false },
33062
+ shadowBackfillStalls: []
33063
+ });
33064
+ const embeddingRunState = getEmbedDrainUiStatus(sessionId, undefined).status;
33065
+ const embeddingState = !embeddingCoverage.enabled ? "off" : embeddingRunState !== "idle" ? embeddingRunState : embeddingCoverage.session.total > 0 && embeddingCoverage.session.embedded >= embeddingCoverage.session.total ? "ready" : "waiting";
32609
33066
  const historyBudgetPercentage = deps.historyBudgetPercentage ?? 0.15;
32610
33067
  const compressionBudget = contextLimit > 0 ? Math.floor(contextLimit * (Math.min(threshold.percentage, 80) / 100) * historyBudgetPercentage) : null;
32611
33068
  return {
@@ -32615,6 +33072,10 @@ ${safeStringify(tool.parameters)}`);
32615
33072
  inputTokens,
32616
33073
  systemPromptTokens,
32617
33074
  compartmentCount: compartments.length,
33075
+ lastCompartmentRange: (() => {
33076
+ const last = compartments.at(-1);
33077
+ return last ? `${last.startMessage}-${last.endMessage}` : null;
33078
+ })(),
32618
33079
  memoryCount: safeRead(() => getMemoryCount(deps.db, deps.projectIdentity), 0),
32619
33080
  memoryBlockCount,
32620
33081
  sessionNoteCount: safeRead(() => getNotes(deps.db, {
@@ -32629,6 +33090,7 @@ ${safeStringify(tool.parameters)}`);
32629
33090
  }).length, 0),
32630
33091
  pendingOpsCount: pendingOps,
32631
33092
  historianRunning: meta.compartmentInProgress,
33093
+ timesExecuteThresholdReached: meta.timesExecuteThresholdReached,
32632
33094
  historianFailureCount: Number(metaRow?.historian_failure_count ?? 0),
32633
33095
  historianLastFailureAt: typeof metaRow?.historian_last_failure_at === "number" ? metaRow.historian_last_failure_at : null,
32634
33096
  historianLastError: metaRow?.historian_last_error ?? null,
@@ -32670,7 +33132,19 @@ ${safeStringify(tool.parameters)}`);
32670
33132
  totalInputTokens: workMetrics.totalInputTokens,
32671
33133
  upgradeNeededCount: safeRead(() => countCompartmentsNeedingUpgrade(deps.db, sessionId), 0),
32672
33134
  recompInFlight: isPiRecompInFlight(sessionId),
32673
- hasDeprecatedProtectedTags: deps.hasDeprecatedProtectedTags ?? false
33135
+ hasDeprecatedProtectedTags: deps.hasDeprecatedProtectedTags ?? false,
33136
+ compactionEnabled: deps.compactionEnabled ?? true,
33137
+ dreamer: {
33138
+ enabled: deps.dreamer?.runnable === true,
33139
+ scheduleSummary: deps.dreamer?.scheduleSummary ?? null,
33140
+ lastRunAt: safeRead(() => getMostRecentTaskRunAt(deps.db, deps.projectIdentity), null),
33141
+ backlog: safeRead(() => getDreamTaskBacklogs(deps.db, deps.projectIdentity, CANONICAL_DREAM_TASKS), {})
33142
+ },
33143
+ embedding: {
33144
+ state: embeddingState,
33145
+ indexed: embeddingCoverage.session.embedded,
33146
+ total: embeddingCoverage.session.total
33147
+ }
32674
33148
  };
32675
33149
  }
32676
33150
  function safeStringify(value) {
@@ -32806,8 +33280,18 @@ function relTime(ts) {
32806
33280
  function registerCtxStatusCommand(pi, deps) {
32807
33281
  pi.registerCommand("ctx-status", {
32808
33282
  description: "Show Magic Context status for the current Pi session",
32809
- handler: async (_args, ctx) => {
33283
+ handler: async (args, ctx) => {
32810
33284
  const sendStatus = createCtxStatusSender(pi, ctx);
33285
+ const mode = args.trim().toLowerCase();
33286
+ if (mode !== "" && mode !== "diagnostics") {
33287
+ sendStatus({
33288
+ title: "/ctx-status",
33289
+ text: "Usage: /ctx-status [diagnostics]",
33290
+ level: "info"
33291
+ });
33292
+ return;
33293
+ }
33294
+ const diagnostics = mode === "diagnostics";
32811
33295
  const runtimeDeps = deps.resolveStatusDeps?.(ctx) ?? deps;
32812
33296
  const projectIdentity = runtimeDeps.resolveProject?.(ctx).projectIdentity ?? runtimeDeps.projectIdentity;
32813
33297
  const currentDeps = { ...runtimeDeps, projectIdentity };
@@ -32824,41 +33308,12 @@ No active Pi session is available.`,
32824
33308
  }
32825
33309
  try {
32826
33310
  if (ctx.hasUI) {
32827
- await showStatusDialog(pi, ctx, currentDeps);
33311
+ await showStatusDialog(pi, ctx, currentDeps, diagnostics);
32828
33312
  return;
32829
33313
  }
32830
- const usage = ctx.getContextUsage?.();
32831
- const modelKey = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
32832
- let detectedContextLimit;
32833
- try {
32834
- const detected = getOverflowState(currentDeps.db, sessionId).detectedContextLimit;
32835
- if (detected > 0)
32836
- detectedContextLimit = detected;
32837
- } catch {}
32838
- const meta = getOrCreateSessionMeta(currentDeps.db, sessionId);
32839
- const windowGeometry = resolvePiWindowGeometry({
32840
- rawContextWindow: usage?.contextWindow ?? ctx.model?.contextWindow,
32841
- rawContextWindowSource: usage?.contextWindow === undefined ? "catalog" : "observed",
32842
- model: ctx.model,
32843
- detectedContextLimit,
32844
- persistedInputTokens: meta.lastInputTokens,
32845
- persistedPercentage: meta.lastContextPercentage
32846
- });
32847
- const usableContextLimit = windowGeometry?.usableSoft;
32848
- const pressure = resolvePiPressureSnapshot({
32849
- persistedPercentage: meta.lastContextPercentage,
32850
- persistedInputTokens: meta.lastInputTokens,
32851
- liveInputTokens: usage?.tokens,
32852
- usableContextLimit
32853
- });
32854
- const statusText = executeStatus(currentDeps.db, sessionId, currentDeps.executeThresholdPercentage, modelKey, currentDeps.historyBudgetPercentage, currentDeps.commitClusterTrigger, currentDeps.executeThresholdTokens, usableContextLimit, {
32855
- backlog: getDreamTaskBacklogs(currentDeps.db, currentDeps.projectIdentity, CANONICAL_DREAM_TASKS)
32856
- }, windowGeometry, resolveTailHygieneStatus(getPiChannel1Baseline(sessionId)), pressure, false, {
32857
- cacheTtlConfig: currentDeps.cacheTtlConfig ?? "5m",
32858
- cacheTtlConfigured: currentDeps.cacheTtlConfigured === true,
32859
- configParseFailures: currentDeps.configParseFailures ?? []
32860
- });
32861
- const details = buildStatusDetails(currentDeps, sessionId);
33314
+ const statusDetail = buildPiStatusDetail(pi, ctx, currentDeps, sessionId);
33315
+ const statusText = diagnostics ? formatPiStatusDiagnostics(statusDetail) : formatPiStatusSummary(statusDetail);
33316
+ const details = buildStatusDetails(currentDeps, statusDetail);
32862
33317
  const profileStatus = currentDeps.activeProfile ?? "none";
32863
33318
  const storage = getMagicContextStorageResolution();
32864
33319
  const deprecationNotice = currentDeps.hasDeprecatedProtectedTags ? `
@@ -32866,68 +33321,47 @@ No active Pi session is available.`,
32866
33321
  ⚠️ Config: "protected_tags" is deprecated and ignored; use "protected_tokens" instead.` : "";
32867
33322
  sendStatus({
32868
33323
  title: "/ctx-status",
32869
- text: `${statusText}${deprecationNotice}
33324
+ text: diagnostics ? `${statusText}${deprecationNotice}
32870
33325
 
32871
33326
  Active profile: ${profileStatus}
32872
33327
 
32873
- Storage: ${storage.path} (${storage.source})`,
33328
+ Storage: ${storage.path} (${storage.source})` : statusText,
32874
33329
  level: "info",
32875
33330
  rpcDisplay: "dialog"
32876
33331
  }, details);
32877
33332
  } catch (error) {
33333
+ sessionLog(sessionId, `ctx-status failed code=${userFacingFailureCode("status_unavailable")}`, error);
32878
33334
  sendStatus({
32879
33335
  title: "/ctx-status",
32880
- text: `## Magic Status — Failed
32881
-
32882
- ${describeError(error).brief}`,
33336
+ text: renderUserFacingFailure("status_unavailable"),
32883
33337
  level: "error"
32884
33338
  });
32885
33339
  }
32886
33340
  }
32887
33341
  });
32888
33342
  }
32889
- function buildStatusDetails(deps, sessionId) {
32890
- const meta = getOrCreateSessionMeta(deps.db, sessionId);
32891
- const tags = getTagsBySession(deps.db, sessionId);
32892
- const activeTags = tags.filter((tag) => tag.status === "active");
32893
- const droppedTags = tags.filter((tag) => tag.status === "dropped");
32894
- const compartments = getCompartments(deps.db, sessionId);
32895
- const lastCompartment = compartments[compartments.length - 1];
32896
- const totalBytes = activeTags.reduce((sum, tag) => sum + tag.byteSize, 0);
33343
+ function buildStatusDetails(deps, status) {
32897
33344
  return {
32898
- sessionId,
33345
+ sessionId: status.sessionId,
32899
33346
  projectIdentity: deps.projectIdentity,
32900
- activeProfile: deps.activeProfile ?? null,
32901
- activeTags: activeTags.length,
32902
- droppedTags: droppedTags.length,
32903
- totalBytes,
32904
- pendingOps: getPendingOps(deps.db, sessionId).length,
32905
- lastExecuteThreshold: meta.timesExecuteThresholdReached,
32906
- compartmentCount: compartments.length,
32907
- lastCompartmentRange: lastCompartment ? `${lastCompartment.startMessage}-${lastCompartment.endMessage}` : null,
32908
- memoryCount: getMemoryCount(deps.db, deps.projectIdentity),
32909
- noteCount: getNotes(deps.db, { sessionId, type: "session", status: "active" }).length + getNotes(deps.db, {
32910
- projectPath: deps.projectIdentity,
32911
- type: "smart",
32912
- status: ["pending", "ready"]
32913
- }).length,
32914
- dreamer: {
32915
- enabled: deps.dreamer?.runnable === true,
32916
- scheduleSummary: deps.dreamer?.scheduleSummary ?? null,
32917
- backlog: getDreamTaskBacklogs(deps.db, deps.projectIdentity, CANONICAL_DREAM_TASKS),
32918
- lastRunAt: getMostRecentTaskRunAt(deps.db, deps.projectIdentity)
32919
- },
32920
- historian: readHistorianState(deps.db, sessionId, meta)
32921
- };
32922
- }
32923
- function readHistorianState(db, sessionId, meta) {
32924
- const row = db.prepare("SELECT historian_failure_count, historian_last_error, historian_last_failure_at FROM session_meta WHERE session_id = ?").get(sessionId);
32925
- return {
32926
- lastFireCount: meta.timesExecuteThresholdReached,
32927
- inProgress: meta.compartmentInProgress,
32928
- lastFailureAt: typeof row?.historian_last_failure_at === "number" ? row.historian_last_failure_at : null,
32929
- lastError: row?.historian_last_error ?? null,
32930
- failureCount: row?.historian_failure_count ?? 0
33347
+ activeProfile: status.activeProfile,
33348
+ activeTags: status.activeTags,
33349
+ droppedTags: status.droppedTags,
33350
+ totalBytes: status.activeBytes,
33351
+ pendingOps: status.pendingOpsCount,
33352
+ lastExecuteThreshold: status.timesExecuteThresholdReached,
33353
+ compartmentCount: status.compartmentCount,
33354
+ lastCompartmentRange: status.lastCompartmentRange,
33355
+ memoryCount: status.memoryCount,
33356
+ noteCount: status.sessionNoteCount + status.readySmartNoteCount,
33357
+ dreamer: status.dreamer,
33358
+ historian: {
33359
+ lastFireCount: status.timesExecuteThresholdReached,
33360
+ inProgress: status.historianRunning,
33361
+ lastFailureAt: status.historianLastFailureAt,
33362
+ lastError: status.historianLastError,
33363
+ failureCount: status.historianFailureCount
33364
+ }
32931
33365
  };
32932
33366
  }
32933
33367
 
@@ -34280,6 +34714,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
34280
34714
  cacheTtlConfigured: bootProjectDeps.cacheTtlConfigured,
34281
34715
  configParseFailures: bootProjectDeps.configParseFailures,
34282
34716
  hasDeprecatedProtectedTags: bootProjectDeps.hasDeprecatedProtectedTags,
34717
+ compactionEnabled: isCompactionEnabled(bootProjectDeps.config),
34283
34718
  resolveStatusDeps: (ctx) => {
34284
34719
  const current = resolveCurrentProjectDeps(ctx);
34285
34720
  return {
@@ -34299,7 +34734,8 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
34299
34734
  cacheTtlConfig: current.config.cache_ttl,
34300
34735
  cacheTtlConfigured: current.cacheTtlConfigured,
34301
34736
  configParseFailures: current.configParseFailures,
34302
- hasDeprecatedProtectedTags: current.hasDeprecatedProtectedTags
34737
+ hasDeprecatedProtectedTags: current.hasDeprecatedProtectedTags,
34738
+ compactionEnabled: isCompactionEnabled(current.config)
34303
34739
  };
34304
34740
  }
34305
34741
  });