@cortexkit/pi-magic-context 0.42.0 → 0.42.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,
@@ -467,7 +463,7 @@ import {
467
463
  registerTodoStateLifecycle,
468
464
  syncCtxMemoryToolEnabled,
469
465
  registerMagicContextTools
470
- } from "./index-yb21jtfd.js";
466
+ } from "./index-pknkkpnp.js";
471
467
  import {
472
468
  pushNotification2
473
469
  } from "./index-b3eqj1g6.js";
@@ -2613,6 +2609,239 @@ function evaluateTaskGate(task, ctx) {
2613
2609
  }
2614
2610
  }
2615
2611
 
2612
+ // ../plugin/src/shared/user-facing-codes.ts
2613
+ var USER_FACING_FAILURES = {
2614
+ historian_unavailable: {
2615
+ code: "MC-H01",
2616
+ sentence: "History compression could not finish this turn.",
2617
+ action: "It will retry automatically."
2618
+ },
2619
+ recomp_unavailable: {
2620
+ code: "MC-R01",
2621
+ sentence: "History compression could not be rebuilt.",
2622
+ action: "Run /ctx-recomp again."
2623
+ },
2624
+ dream_provider_timeout: {
2625
+ code: "MC-D01",
2626
+ sentence: "Memory maintenance took too long to respond.",
2627
+ action: "Run /ctx-dream again."
2628
+ },
2629
+ dream_provider_error: {
2630
+ code: "MC-D02",
2631
+ sentence: "Memory maintenance could not reach its model.",
2632
+ action: "Check the model connection, then run /ctx-dream again."
2633
+ },
2634
+ dream_empty_completion: {
2635
+ code: "MC-D03",
2636
+ sentence: "Memory maintenance received no usable response.",
2637
+ action: "Run /ctx-dream again."
2638
+ },
2639
+ dream_no_models: {
2640
+ code: "MC-D04",
2641
+ sentence: "Memory maintenance has no model available.",
2642
+ action: "Check the model settings, then run /ctx-dream again."
2643
+ },
2644
+ dream_child_aborted: {
2645
+ code: "MC-D05",
2646
+ sentence: "Memory maintenance was interrupted.",
2647
+ action: "Run /ctx-dream again."
2648
+ },
2649
+ dream_parse_failed: {
2650
+ code: "MC-D06",
2651
+ sentence: "Memory maintenance could not use the model response.",
2652
+ action: "Run /ctx-dream again."
2653
+ },
2654
+ dream_unknown: {
2655
+ code: "MC-D07",
2656
+ sentence: "Memory maintenance could not finish.",
2657
+ action: "Run /ctx-dream again."
2658
+ },
2659
+ embedding_substitution_rejected: {
2660
+ code: "MC-E01",
2661
+ sentence: "Search indexing could not use the selected model.",
2662
+ action: "Check the embedding model setting, then run /ctx-embed start again."
2663
+ },
2664
+ embedding_http_error: {
2665
+ code: "MC-E02",
2666
+ sentence: "The search indexing provider refused the request.",
2667
+ action: "Check the provider connection and credentials, then run /ctx-embed start again."
2668
+ },
2669
+ embedding_transport_error: {
2670
+ code: "MC-E03",
2671
+ sentence: "Search indexing could not reach its provider.",
2672
+ action: "Check the connection, then run /ctx-embed start again."
2673
+ },
2674
+ embedding_invalid_envelope: {
2675
+ code: "MC-E04",
2676
+ sentence: "Search indexing received an unsupported response.",
2677
+ action: "Check the embedding endpoint, then run /ctx-embed start again."
2678
+ },
2679
+ embedding_empty_result: {
2680
+ code: "MC-E05",
2681
+ sentence: "Search indexing received no usable result.",
2682
+ action: "Run /ctx-embed start again."
2683
+ },
2684
+ embedding_certification_refusal: {
2685
+ code: "MC-E06",
2686
+ sentence: "Search indexing is not ready for this provider.",
2687
+ action: "Finish the provider setup, or set a fallback provider in the embedding settings, then run /ctx-embed start again."
2688
+ },
2689
+ embedding_credential_required: {
2690
+ code: "MC-E07",
2691
+ sentence: "Search indexing needs provider credentials.",
2692
+ action: "Sign in to the provider, then run /ctx-embed start again."
2693
+ },
2694
+ embedding_local_binding_missing: {
2695
+ code: "MC-E08",
2696
+ sentence: "Local search indexing is unavailable on this system.",
2697
+ action: "Run `npx @cortexkit/magic-context doctor`, then retry."
2698
+ },
2699
+ embedding_local_fs_unavailable: {
2700
+ code: "MC-E09",
2701
+ sentence: "Local search indexing cannot save its model files.",
2702
+ action: "Update or reinstall Magic Context, then retry."
2703
+ },
2704
+ embedding_local_download_failure: {
2705
+ code: "MC-E10",
2706
+ sentence: "Local search indexing could not download its model.",
2707
+ action: "Check the network connection, then retry."
2708
+ },
2709
+ embedding_local_runtime_error: {
2710
+ code: "MC-E11",
2711
+ sentence: "Local search indexing could not start.",
2712
+ action: "Run `npx @cortexkit/magic-context doctor`, then retry."
2713
+ },
2714
+ embedding_unavailable: {
2715
+ code: "MC-E12",
2716
+ sentence: "Search indexing could not finish.",
2717
+ action: "Run /ctx-embed start again."
2718
+ },
2719
+ status_unavailable: {
2720
+ code: "MC-S01",
2721
+ sentence: "Magic Context status is temporarily unavailable.",
2722
+ action: "Retry /ctx-status in a moment."
2723
+ },
2724
+ transform_update_failed: {
2725
+ code: "MC-S02",
2726
+ sentence: "The last context update did not finish.",
2727
+ action: "Send another message to retry."
2728
+ },
2729
+ configuration_warning: {
2730
+ code: "MC-S03",
2731
+ sentence: "Some configuration settings could not be applied.",
2732
+ action: "Fix the configuration warning shown in /ctx-status diagnostics, then restart."
2733
+ },
2734
+ status_log_unavailable: {
2735
+ code: "MC-S04",
2736
+ sentence: "Some diagnostic details could not be saved.",
2737
+ action: "Retry /ctx-status in a moment."
2738
+ },
2739
+ memory_writes_paused: {
2740
+ code: "MC-C01",
2741
+ sentence: "Memory writes are paused while the engine syncs.",
2742
+ action: "Retry in a moment."
2743
+ },
2744
+ memory_access_unavailable: {
2745
+ code: "MC-C02",
2746
+ sentence: "Memory access is temporarily unavailable.",
2747
+ action: "Retry in a moment."
2748
+ },
2749
+ note_changes_paused: {
2750
+ code: "MC-C03",
2751
+ sentence: "Note changes are paused while the engine syncs.",
2752
+ action: "Retry in a moment."
2753
+ },
2754
+ note_access_unavailable: {
2755
+ code: "MC-C04",
2756
+ sentence: "Notes are temporarily unavailable.",
2757
+ action: "Retry in a moment."
2758
+ },
2759
+ context_cleanup_paused: {
2760
+ code: "MC-C05",
2761
+ sentence: "Context cleanup is paused while the engine syncs.",
2762
+ action: "Retry in a moment."
2763
+ },
2764
+ partial_history_unavailable: {
2765
+ code: "MC-C06",
2766
+ sentence: "Partial history compression is not available in the current mode.",
2767
+ action: "Run /ctx-recomp without a range."
2768
+ },
2769
+ session_upgrade_unavailable: {
2770
+ code: "MC-C07",
2771
+ sentence: "Session upgrade is not available in the current mode.",
2772
+ action: "Run /ctx-recomp instead."
2773
+ },
2774
+ smart_note_conditions_unavailable: {
2775
+ code: "MC-C08",
2776
+ sentence: "Conditional notes are not available in the current mode.",
2777
+ action: "Save a regular note without a condition."
2778
+ },
2779
+ history_compression_paused: {
2780
+ code: "MC-C09",
2781
+ sentence: "History compression is paused while the engine syncs.",
2782
+ action: "Retry in a moment."
2783
+ },
2784
+ context_service_unavailable: {
2785
+ code: "MC-C10",
2786
+ sentence: "Magic Context is temporarily unavailable.",
2787
+ action: "Retry in a moment."
2788
+ }
2789
+ };
2790
+ function renderUserFacingFailure(key, style = "markdown") {
2791
+ const failure = USER_FACING_FAILURES[key];
2792
+ const action = style === "plain" ? failure.action.replaceAll("`", "") : failure.action;
2793
+ return `${failure.sentence} ${action} (${failure.code})`;
2794
+ }
2795
+ function userFacingFailureCode(key) {
2796
+ return USER_FACING_FAILURES[key].code;
2797
+ }
2798
+ var CAPABILITY_FAILURES = {
2799
+ memory_write: "memory_writes_paused",
2800
+ memory_access: "memory_access_unavailable",
2801
+ note_change: "note_changes_paused",
2802
+ note_access: "note_access_unavailable",
2803
+ context_cleanup: "context_cleanup_paused",
2804
+ partial_history: "partial_history_unavailable",
2805
+ session_upgrade: "session_upgrade_unavailable",
2806
+ smart_note_condition: "smart_note_conditions_unavailable",
2807
+ history_compression: "history_compression_paused",
2808
+ context_service: "context_service_unavailable"
2809
+ };
2810
+ function renderCapabilityRefusal(capability) {
2811
+ return renderUserFacingFailure(CAPABILITY_FAILURES[capability]);
2812
+ }
2813
+ var DREAM_FAILURE_KEYS = {
2814
+ provider_timeout: "dream_provider_timeout",
2815
+ provider_error: "dream_provider_error",
2816
+ empty_completion: "dream_empty_completion",
2817
+ no_models: "dream_no_models",
2818
+ child_aborted: "dream_child_aborted",
2819
+ parse_failed: "dream_parse_failed",
2820
+ unknown: "dream_unknown"
2821
+ };
2822
+ var EMBEDDING_FAILURE_KEYS = {
2823
+ substitution_rejected: "embedding_substitution_rejected",
2824
+ http_error: "embedding_http_error",
2825
+ transport_error: "embedding_transport_error",
2826
+ invalid_envelope: "embedding_invalid_envelope",
2827
+ empty_result: "embedding_empty_result",
2828
+ certification_refusal: "embedding_certification_refusal",
2829
+ credential_required: "embedding_credential_required",
2830
+ local_binding_missing: "embedding_local_binding_missing",
2831
+ local_fs_unavailable: "embedding_local_fs_unavailable",
2832
+ local_download_failure: "embedding_local_download_failure",
2833
+ local_runtime_error: "embedding_local_runtime_error"
2834
+ };
2835
+ function renderDreamFailure(failureClass, style = "markdown") {
2836
+ return renderUserFacingFailure(DREAM_FAILURE_KEYS[failureClass], style);
2837
+ }
2838
+ function dreamFailureCode(failureClass) {
2839
+ return userFacingFailureCode(DREAM_FAILURE_KEYS[failureClass]);
2840
+ }
2841
+ function renderEmbeddingFailure(failureClass, style = "markdown") {
2842
+ return renderUserFacingFailure(EMBEDDING_FAILURE_KEYS[failureClass], style);
2843
+ }
2844
+
2616
2845
  // ../plugin/src/features/magic-context/dreamer/open-opencode-db.ts
2617
2846
  function openOpenCodeDb() {
2618
2847
  const resolution = resolveOpenCodeDbPath();
@@ -5183,12 +5412,12 @@ async function runClassifyThroughModule(args, chunk, anchors, signal) {
5183
5412
  });
5184
5413
  } catch (error) {
5185
5414
  if (isRustAuthorityDrainingError(error)) {
5186
- throw new Error("Rust memory authority is not ready; TypeScript fallback is disabled.");
5415
+ throw new Error(renderCapabilityRefusal("memory_write"));
5187
5416
  }
5188
5417
  throw error;
5189
5418
  }
5190
5419
  if (isRustAuthorityDrainingError(applied)) {
5191
- throw new Error("Rust memory authority is not ready; TypeScript fallback is disabled.");
5420
+ throw new Error(renderCapabilityRefusal("memory_write"));
5192
5421
  }
5193
5422
  const applyResult = applied?.result ?? applied;
5194
5423
  if (!applyResult || typeof applyResult !== "object") {
@@ -8425,14 +8654,7 @@ function parseJsonRecord(value) {
8425
8654
 
8426
8655
  // ../plugin/src/features/magic-context/dreamer/storage-dream-runs.ts
8427
8656
  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(" · ");
8657
+ return renderDreamFailure(failure.failure_class);
8436
8658
  }
8437
8659
  var insertDreamRunStatements = new WeakMap;
8438
8660
  var getDreamRunsByProjectStatements = new Map;
@@ -9551,7 +9773,7 @@ function createDreamTaskExecutor(deps) {
9551
9773
  const { transient, brief } = classifyFailure(error);
9552
9774
  const failure = dreamRunFailureDetail(error);
9553
9775
  recordRun("failed", brief, { failure });
9554
- log(`[dreamer] task ${config.task} failed (transient=${transient}): ${brief}`);
9776
+ log(`[dreamer] task ${config.task} failed code=${dreamFailureCode(failure.failure_class)} (transient=${transient}): ${brief}`);
9555
9777
  return {
9556
9778
  status: "failed",
9557
9779
  transient,
@@ -12897,17 +13119,10 @@ Unknown task "${requested}".`,
12897
13119
  projectIdentity: project.projectIdentity
12898
13120
  });
12899
13121
  } catch (error) {
12900
- const message = error instanceof Error ? error.message : String(error);
12901
- sessionLog(project.projectIdentity, `/ctx-dream failed: ${message}`);
13122
+ sessionLog(project.projectIdentity, `/ctx-dream failed code=${userFacingFailureCode("dream_unknown")}`, error);
12902
13123
  sendStatus({
12903
13124
  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
- `),
13125
+ text: renderUserFacingFailure("dream_unknown"),
12911
13126
  level: "error"
12912
13127
  }, {
12913
13128
  projectDir: project.projectDir,
@@ -12922,39 +13137,27 @@ Unknown task "${requested}".`,
12922
13137
  var embedPauseBySession = new Set;
12923
13138
  var embedRunStateBySession = new Map;
12924
13139
  var autoEmbedAttemptedBySession = new Set;
13140
+ function getEmbedDrainUiStatus(sessionId, progress) {
13141
+ if (embedPauseBySession.has(sessionId)) {
13142
+ return { status: "paused" };
13143
+ }
13144
+ const activeRun = embedRunStateBySession.get(sessionId);
13145
+ if (activeRun && !activeRun.signal.aborted || progress?.kind === "embed" && progress.phase === "recomp") {
13146
+ return { status: "running" };
13147
+ }
13148
+ if (progress?.kind === "embed" && (progress.phase === "failed" || progress.phase === "skipped") && progress.message) {
13149
+ if (/provider/i.test(progress.message)) {
13150
+ return { status: "stopped", detail: progress.message };
13151
+ }
13152
+ }
13153
+ return { status: "idle" };
13154
+ }
12925
13155
 
12926
13156
  // ../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
- }
13157
+ function formatEmbedFailureSummary(embedded, remaining, failure, style = "markdown") {
13158
+ const historyBlocks = `history block${embedded === 1 ? "" : "s"}`;
13159
+ const progress = `Indexed ${embedded} ${historyBlocks}; ${remaining} remain.`;
13160
+ return `${progress} ${renderEmbeddingFailure(failure?.class ?? "empty_result", style)}`;
12958
13161
  }
12959
13162
 
12960
13163
  // ../plugin/src/hooks/magic-context/format-embed-status.ts
@@ -13112,7 +13315,7 @@ Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`,
13112
13315
  return {
13113
13316
  text: `## /ctx-embed
13114
13317
 
13115
- ${formatEmbedFailureSummary(outcome.embedded, outcome.remaining, outcome.failure)}`,
13318
+ ${formatEmbedFailureSummary(outcome.embedded, outcome.remaining, outcome.failure, "plain")}`,
13116
13319
  level: "info"
13117
13320
  };
13118
13321
  default:
@@ -17836,24 +18039,9 @@ function validateHistorianOutput(text, _sessionId, chunk, _priorCompartments, se
17836
18039
  };
17837
18040
  }
17838
18041
  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(`
18042
+ function buildHistorianFailureNotice(failureCount, _lastError) {
18043
+ const heading = failureCount >= HISTORIAN_PERSISTENT_FAILURE_THRESHOLD ? "## Magic Context — History compression" : "## Magic Context";
18044
+ return [heading, "", renderUserFacingFailure("historian_unavailable")].join(`
17857
18045
  `);
17858
18046
  }
17859
18047
  function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationError, language) {
@@ -30269,10 +30457,10 @@ Found ${existingStaging.compartments.length} staged compartment(s) from ${existi
30269
30457
  }
30270
30458
  }
30271
30459
  }
30460
+ sessionLog(sessionId, `recomp partial code=${userFacingFailureCode("recomp_unavailable")} reason="${reason}"`);
30272
30461
  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}).`
30462
+ `Rebuilt ${promoted.compartments.length} history block${promoted.compartments.length === 1 ? "" : "s"} across ${passCount} successful pass${passCount === 1 ? "" : "es"}.`,
30463
+ renderUserFacingFailure("recomp_unavailable")
30276
30464
  ].join(`
30277
30465
  `);
30278
30466
  }
@@ -30297,11 +30485,10 @@ Recomp stopped because raw history ${offset}-${protectedTailStart - 1} could not
30297
30485
 
30298
30486
  ${partial}`;
30299
30487
  }
30488
+ sessionLog(sessionId, `recomp failed code=${userFacingFailureCode("recomp_unavailable")} reason="${chunkCoverageError}"`);
30300
30489
  return `## Magic Recomp — Failed
30301
30490
 
30302
- Recomp stopped because the raw chunk could not be represented safely: ${chunkCoverageError}
30303
-
30304
- Nothing was written.`;
30491
+ ${renderUserFacingFailure("recomp_unavailable")}`;
30305
30492
  }
30306
30493
  const references = buildReferenceBlocks({
30307
30494
  sessionId,
@@ -30343,11 +30530,10 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
30343
30530
  callbacks: {
30344
30531
  onRepairRetry: async (error) => {
30345
30532
  emitProgress(`Repair retry (pass ${passCount + 1})…`);
30533
+ sessionLog(sessionId, `recomp retry code=${userFacingFailureCode("recomp_unavailable")} reason="${error}"`);
30346
30534
  await sendStatusNotification(client, sessionId, `## Magic Recomp
30347
30535
 
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());
30536
+ History compression is retrying this pass. ${renderUserFacingFailure("recomp_unavailable")}`, notifParams());
30351
30537
  },
30352
30538
  onModelFallback: (modelId, index, total) => {
30353
30539
  const short = modelId.includes("/") ? modelId.split("/").pop() : modelId;
@@ -30362,9 +30548,7 @@ The previous output did not validate: ${error}`, notifParams());
30362
30548
  if (smallerChunk.messageCount > 0 && smallerChunk.endIndex < chunk.endIndex) {
30363
30549
  await sendStatusNotification(client, sessionId, `## Magic Recomp
30364
30550
 
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());
30551
+ History compression is retrying with a smaller set of messages. ${renderUserFacingFailure("recomp_unavailable")}`, notifParams());
30368
30552
  currentTokenBudget = reducedBudget;
30369
30553
  passAttempt += 1;
30370
30554
  continue;
@@ -30381,7 +30565,8 @@ Validator result: ${validatedPass.error}`, notifParams());
30381
30565
  chunkEndOrdinal: chunk.endIndex,
30382
30566
  compartmentsProduced: 0
30383
30567
  });
30384
- const partial = await promoteAndFinalize(`historian failed to validate messages ${chunk.startIndex}-${chunk.endIndex}: ${validatedPass.error}`);
30568
+ sessionLog(sessionId, `recomp failed code=${userFacingFailureCode("recomp_unavailable")} reason="${validatedPass.error}" messageRange=${chunk.startIndex}-${chunk.endIndex}`);
30569
+ const partial = await promoteAndFinalize(`history model response did not validate for messages ${chunk.startIndex}-${chunk.endIndex}`);
30385
30570
  if (partial) {
30386
30571
  return `## Magic Recomp — Partial
30387
30572
 
@@ -30389,9 +30574,7 @@ ${partial}`;
30389
30574
  }
30390
30575
  return `## Magic Recomp — Failed
30391
30576
 
30392
- Recomp failed while rebuilding messages ${chunk.startIndex}-${chunk.endIndex}: ${validatedPass.error}
30393
-
30394
- Nothing was written.`;
30577
+ ${renderUserFacingFailure("recomp_unavailable")}`;
30395
30578
  }
30396
30579
  {
30397
30580
  const passComps = validatedPass.compartments ?? [];
@@ -30442,11 +30625,10 @@ Recomp made no forward progress after messages ${chunk.startIndex}-${chunk.endIn
30442
30625
  const mergedValidationError = validateStoredCompartments(candidateCompartments);
30443
30626
  if (mergedValidationError) {
30444
30627
  clearRecompStaging(db, sessionId);
30628
+ sessionLog(sessionId, `recomp failed code=${userFacingFailureCode("recomp_unavailable")} reason="${mergedValidationError}"`);
30445
30629
  return `## Magic Recomp — Failed
30446
30630
 
30447
- Recomp completed ${passCount} pass${passCount === 1 ? "" : "es"} but produced an invalid final compartment set: ${mergedValidationError}
30448
-
30449
- Nothing was written.`;
30631
+ ${renderUserFacingFailure("recomp_unavailable")}`;
30450
30632
  }
30451
30633
  saveRecompStagingPass(db, sessionId, passCount, candidateCompartments, candidateFacts);
30452
30634
  const lastCompartmentEnd = candidateCompartments[candidateCompartments.length - 1]?.endMessage ?? 0;
@@ -30498,11 +30680,10 @@ Another process acquired the compartment-state lease before recomp could publish
30498
30680
  `);
30499
30681
  } catch (error) {
30500
30682
  const message = getErrorMessage(error);
30683
+ sessionLog(sessionId, `recomp failed code=${userFacingFailureCode("recomp_unavailable")} reason="${message}"`);
30501
30684
  return `## Magic Recomp — Failed
30502
30685
 
30503
- Recomp failed unexpectedly: ${message}
30504
-
30505
- Staging data preserved for resume on next attempt.`;
30686
+ ${renderUserFacingFailure("recomp_unavailable")}`;
30506
30687
  } finally {
30507
30688
  updateSessionMeta(db, sessionId, { compartmentInProgress: false });
30508
30689
  cleanupHistorianStateFile(currentStateFilePath);
@@ -30720,11 +30901,10 @@ Recomp stopped because raw history ${offset}-${snapEnd} could not be turned into
30720
30901
  }
30721
30902
  const chunkCoverageError = validateChunkCoverage(chunk);
30722
30903
  if (chunkCoverageError) {
30904
+ log(`[magic-context] partial recomp failed session=${sessionId} code=${userFacingFailureCode("recomp_unavailable")} reason="${chunkCoverageError}"`);
30723
30905
  return `## Magic Recomp — Failed
30724
30906
 
30725
- Partial recomp stopped because the raw chunk could not be represented safely: ${chunkCoverageError}
30726
-
30727
- Original state preserved (staging kept for retry).`;
30907
+ ${renderUserFacingFailure("recomp_unavailable")}`;
30728
30908
  }
30729
30909
  const references = buildReferenceBlocks({
30730
30910
  sessionId,
@@ -30764,11 +30944,10 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
30764
30944
  language: deps.language,
30765
30945
  callbacks: {
30766
30946
  onRepairRetry: async (error) => {
30947
+ log(`[magic-context] partial recomp retry session=${sessionId} code=${userFacingFailureCode("recomp_unavailable")} reason="${error}"`);
30767
30948
  await sendStatusNotification(client, sessionId, `## Magic Recomp — Partial
30768
30949
 
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());
30950
+ History compression is retrying this pass. ${renderUserFacingFailure("recomp_unavailable")}`, notifParams());
30772
30951
  }
30773
30952
  }
30774
30953
  });
@@ -30779,19 +30958,16 @@ The previous output did not validate: ${error}`, notifParams());
30779
30958
  if (smallerChunk.messageCount > 0 && smallerChunk.endIndex < chunk.endIndex) {
30780
30959
  await sendStatusNotification(client, sessionId, `## Magic Recomp — Partial
30781
30960
 
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());
30961
+ History compression is retrying with a smaller set of messages. ${renderUserFacingFailure("recomp_unavailable")}`, notifParams());
30785
30962
  currentTokenBudget = reducedBudget;
30786
30963
  passAttempt += 1;
30787
30964
  continue;
30788
30965
  }
30789
30966
  }
30967
+ log(`[magic-context] partial recomp failed session=${sessionId} code=${userFacingFailureCode("recomp_unavailable")} reason="${validatedPass.error}" messageRange=${chunk.startIndex}-${chunk.endIndex}`);
30790
30968
  return `## Magic Recomp — Failed
30791
30969
 
30792
- Partial recomp failed while rebuilding messages ${chunk.startIndex}-${chunk.endIndex}: ${validatedPass.error}
30793
-
30794
- Original state preserved (staging kept for retry).`;
30970
+ ${renderUserFacingFailure("recomp_unavailable")}`;
30795
30971
  }
30796
30972
  candidateCompartments = [
30797
30973
  ...candidateCompartments,
@@ -30826,11 +31002,10 @@ Partial recomp completed historian passes but the final compartment set failed v
30826
31002
  `);
30827
31003
  } catch (error) {
30828
31004
  const message = getErrorMessage(error);
31005
+ log(`[magic-context] partial recomp failed session=${sessionId} code=${userFacingFailureCode("recomp_unavailable")} reason="${message}"`);
30829
31006
  return `## Magic Recomp — Failed
30830
31007
 
30831
- Partial recomp failed unexpectedly: ${message}
30832
-
30833
- Staging preserved for resume on next attempt.`;
31008
+ ${renderUserFacingFailure("recomp_unavailable")}`;
30834
31009
  } finally {
30835
31010
  updateSessionMeta(db, sessionId, { compartmentInProgress: false });
30836
31011
  const leftoverStaging = getRecompStaging(db, sessionId);
@@ -31952,6 +32127,78 @@ ${migrationSummary}` : "",
31952
32127
  });
31953
32128
  }
31954
32129
 
32130
+ // src/dialogs/status-dialog.ts
32131
+ import {
32132
+ matchesKey as matchesKey2,
32133
+ truncateToWidth,
32134
+ visibleWidth
32135
+ } from "@earendil-works/pi-tui";
32136
+
32137
+ // ../plugin/src/shared/format-bytes.ts
32138
+ function formatBytes(bytes) {
32139
+ if (bytes < 1024)
32140
+ return `${bytes}B`;
32141
+ if (bytes < 1024 * 1024)
32142
+ return `${(bytes / 1024).toFixed(1)}KB`;
32143
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
32144
+ }
32145
+ // ../plugin/src/hooks/magic-context/m0-token-breakdown.ts
32146
+ function computeM0BlockTokens(db, sessionId, args) {
32147
+ const {
32148
+ m0Text,
32149
+ projectIdentity,
32150
+ injectionBudgetTokens,
32151
+ memoryBlockCount,
32152
+ compartmentTokensOverride
32153
+ } = args;
32154
+ const docsBlock = extractM0Block(m0Text, "project-docs");
32155
+ const docsTokens = docsBlock ? estimateTokens(docsBlock) : 0;
32156
+ const profileBlock = extractM0Block(m0Text, "user-profile");
32157
+ const profileTokens = profileBlock ? estimateTokens(profileBlock) : 0;
32158
+ let memoryTokens = 0;
32159
+ let memoryFromM0 = false;
32160
+ const memoryBlock = extractM0Block(m0Text, "project-memory");
32161
+ if (memoryBlock) {
32162
+ memoryTokens = estimateTokens(memoryBlock);
32163
+ memoryFromM0 = true;
32164
+ }
32165
+ const muralTokens = m0Text.includes("<memory-mural>") ? 1521 : 0;
32166
+ let compartmentTokens = 0;
32167
+ const historyBlock = extractM0Block(m0Text, "session-history");
32168
+ if (typeof compartmentTokensOverride === "number" && Number.isFinite(compartmentTokensOverride) && compartmentTokensOverride >= 0) {
32169
+ compartmentTokens = compartmentTokensOverride;
32170
+ } else if (historyBlock) {
32171
+ compartmentTokens = estimateTokens(historyBlock);
32172
+ } else {
32173
+ try {
32174
+ const compRows = db.prepare("SELECT content, title, start_message, end_message FROM compartments WHERE session_id = ?").all(sessionId);
32175
+ for (const c of compRows) {
32176
+ compartmentTokens += estimateTokens(`## ${c.start_message}-${c.end_message} · ${c.title}
32177
+ ${c.content}
32178
+ `);
32179
+ }
32180
+ } catch {}
32181
+ }
32182
+ if (!memoryFromM0 && memoryBlockCount > 0 && projectIdentity) {
32183
+ try {
32184
+ const memories = getMemoriesByProject(db, projectIdentity, ["active", "permanent"]);
32185
+ const selected = injectionBudgetTokens ? trimMemoriesToBudgetV2(sessionId, memories, injectionBudgetTokens).renderOrder : memories;
32186
+ const block = renderMemoryBlockV2(selected);
32187
+ memoryTokens = block ? estimateTokens(block) : 0;
32188
+ } catch {
32189
+ memoryTokens = 0;
32190
+ }
32191
+ }
32192
+ return {
32193
+ docsTokens,
32194
+ profileTokens,
32195
+ memoryTokens,
32196
+ muralTokens,
32197
+ compartmentTokens,
32198
+ factTokens: 0
32199
+ };
32200
+ }
32201
+
31955
32202
  // ../plugin/src/shared/cache-ttl-display.ts
31956
32203
  function resolveCacheTtlDisplay(args) {
31957
32204
  if (args.sessionModelKey && (!args.modelKey || args.sessionModelKey === args.modelKey) || !args.modelKey && !args.sessionModelKey && args.sessionValue !== "5m") {
@@ -32005,8 +32252,75 @@ function formatThresholdClampNote(opts) {
32005
32252
  return ` [clamped: ${opts.configuredValue}% > ${opts.maxPercentage}%]`;
32006
32253
  }
32007
32254
 
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";
32255
+ // ../plugin/src/shared/status-summary.ts
32256
+ function formatCount(value) {
32257
+ return Math.round(value).toLocaleString();
32258
+ }
32259
+ function compressionText(summary) {
32260
+ const historyBlocks = `${summary.compression.historyBlockCount} history block${summary.compression.historyBlockCount === 1 ? "" : "s"}`;
32261
+ switch (summary.compression.state) {
32262
+ case "off":
32263
+ return "Off";
32264
+ case "compressing":
32265
+ return `Compressing history · ${historyBlocks}`;
32266
+ case "ready":
32267
+ return `Ready · ${historyBlocks}`;
32268
+ case "waiting":
32269
+ return "Waiting for enough conversation history";
32270
+ }
32271
+ }
32272
+ function reclaimableText(summary) {
32273
+ const count = Math.max(0, Math.floor(summary.reclaimable.toolOutputCount));
32274
+ if (count === 0)
32275
+ return "none";
32276
+ const outputs = `${count} spent tool output${count === 1 ? "" : "s"}`;
32277
+ return `${outputs} (~${Math.round(Math.max(0, summary.reclaimable.tokens) / 1000)}k tokens)`;
32278
+ }
32279
+ function embeddingText(summary) {
32280
+ if (summary.embedding.state === "off")
32281
+ return "Off";
32282
+ const coverage = `${formatCount(summary.embedding.indexed)} / ${formatCount(summary.embedding.total)} history blocks indexed`;
32283
+ switch (summary.embedding.state) {
32284
+ case "running":
32285
+ return `Running · ${coverage}`;
32286
+ case "paused":
32287
+ return `Paused · ${coverage}`;
32288
+ case "stopped":
32289
+ return `Paused after an issue · ${coverage}`;
32290
+ case "ready":
32291
+ return `Ready · ${coverage}`;
32292
+ case "waiting":
32293
+ return `Waiting · ${coverage}`;
32294
+ }
32295
+ }
32296
+ function renderUserStatusSummary(summary, style) {
32297
+ const context = `${summary.usagePercentage.toFixed(1)}% of usable context (${formatCount(summary.inputTokens)} / ${summary.usableContextTokens > 0 ? formatCount(summary.usableContextTokens) : "?"} tokens)`;
32298
+ const values = [
32299
+ ["Context", context],
32300
+ ["Cache lifetime", summary.cacheLifetime],
32301
+ [
32302
+ "Automatic compression",
32303
+ summary.automaticCompressionThreshold === null ? "Off" : `at ${summary.automaticCompressionThreshold.toFixed(1)}% of usable context`
32304
+ ],
32305
+ ["History compression", compressionText(summary)],
32306
+ ["Reclaimable", reclaimableText(summary)],
32307
+ [
32308
+ "Memory",
32309
+ `${formatCount(summary.memoryCount)} memories · ${formatCount(summary.noteCount)} notes`
32310
+ ],
32311
+ ["Search indexing", embeddingText(summary)]
32312
+ ];
32313
+ const lines = style === "markdown" ? [
32314
+ "## Magic Context Status",
32315
+ "",
32316
+ ...values.map(([label, value]) => `- **${label}:** ${value}`)
32317
+ ] : ["Magic Context Status", ...values.map(([label, value]) => `${label}: ${value}`)];
32318
+ for (const warning of summary.warnings) {
32319
+ lines.push(style === "markdown" ? `- **Warning:** ${renderUserFacingFailure(warning)}` : `Warning: ${renderUserFacingFailure(warning)}`);
32320
+ }
32321
+ return lines.join(`
32322
+ `);
32323
+ }
32010
32324
 
32011
32325
  // ../plugin/src/shared/tail-hygiene-status.ts
32012
32326
  function finiteNumber(value, fallback = 0) {
@@ -32023,7 +32337,8 @@ function resolveTailHygieneStatus(tsBaseline, rustBaseline) {
32023
32337
  evaluable: rustBaseline.evaluable === true,
32024
32338
  generationInvalidated: rustBaseline.generation_invalidated === true,
32025
32339
  baselineGeneration: Math.max(0, finiteNumber(rustBaseline.baseline_generation)),
32026
- computedAt: Math.max(0, finiteNumber(rustBaseline.computed_at_ms))
32340
+ computedAt: Math.max(0, finiteNumber(rustBaseline.computed_at_ms)),
32341
+ reclaimableToolOutputCount: Math.max(0, Math.floor(finiteNumber(rustBaseline.reclaimable_tool_output_count)))
32027
32342
  };
32028
32343
  }
32029
32344
  if (tsBaseline === undefined)
@@ -32037,7 +32352,8 @@ function resolveTailHygieneStatus(tsBaseline, rustBaseline) {
32037
32352
  evaluable: tsBaseline.evaluable && !tsBaseline.generationInvalidated,
32038
32353
  generationInvalidated: tsBaseline.generationInvalidated,
32039
32354
  baselineGeneration: Math.max(0, tsBaseline.baselineGeneration),
32040
- computedAt: Math.max(0, tsBaseline.computedAt)
32355
+ computedAt: Math.max(0, tsBaseline.computedAt),
32356
+ reclaimableToolOutputCount: tsBaseline.baselineParts.filter((part) => part.kind === "toolOutput" && part.uTokens > 0).length
32041
32357
  };
32042
32358
  }
32043
32359
  function formatTailHygiene(status) {
@@ -32045,223 +32361,10 @@ function formatTailHygiene(status) {
32045
32361
  const state = status.evaluable ? "" : " · held until baseline refresh";
32046
32362
  return `${percentage}% · ${status.u.toLocaleString()} / ${status.t.toLocaleString()} tok${state}`;
32047
32363
  }
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
32364
  // package.json
32262
32365
  var package_default = {
32263
32366
  name: "@cortexkit/pi-magic-context",
32264
- version: "0.42.0",
32367
+ version: "0.42.1",
32265
32368
  type: "module",
32266
32369
  description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
32267
32370
  main: "dist/index.js",
@@ -32366,7 +32469,7 @@ var COLORS = {
32366
32469
  toolDefs: "#f472b6"
32367
32470
  };
32368
32471
  var REFRESH_INTERVAL_MS = 1000;
32369
- async function showStatusDialog(pi, ctx, deps) {
32472
+ async function showStatusDialog(pi, ctx, deps, initialDiagnostics = false) {
32370
32473
  const sessionId = resolveSessionId(ctx);
32371
32474
  if (!sessionId)
32372
32475
  throw new Error("No active Pi session is available.");
@@ -32377,7 +32480,8 @@ async function showStatusDialog(pi, ctx, deps) {
32377
32480
  sessionId,
32378
32481
  theme,
32379
32482
  tui,
32380
- done
32483
+ done,
32484
+ initialDiagnostics
32381
32485
  }), {
32382
32486
  overlay: true,
32383
32487
  overlayOptions: { anchor: "center", width: 78 }
@@ -32389,8 +32493,10 @@ class StatusDialogComponent {
32389
32493
  detail;
32390
32494
  refreshTimer = null;
32391
32495
  closed = false;
32496
+ diagnostics;
32392
32497
  constructor(props) {
32393
32498
  this.props = props;
32499
+ this.diagnostics = props.initialDiagnostics;
32394
32500
  this.detail = buildPiStatusDetail(props.pi, props.ctx, props.deps, props.sessionId);
32395
32501
  this.refreshTimer = setInterval(() => {
32396
32502
  if (this.closed)
@@ -32402,6 +32508,11 @@ class StatusDialogComponent {
32402
32508
  }, REFRESH_INTERVAL_MS);
32403
32509
  }
32404
32510
  handleInput(data) {
32511
+ if (matchesKey2(data, "d")) {
32512
+ this.diagnostics = !this.diagnostics;
32513
+ this.props.tui.requestRender();
32514
+ return;
32515
+ }
32405
32516
  if (matchesKey2(data, "escape") || matchesKey2(data, "ctrl+c") || matchesKey2(data, "return")) {
32406
32517
  this.close();
32407
32518
  }
@@ -32419,7 +32530,7 @@ class StatusDialogComponent {
32419
32530
  invalidate() {}
32420
32531
  render(width) {
32421
32532
  const innerWidth = Math.max(20, width - 4);
32422
- const inner = renderInner(this.detail, this.props.theme, innerWidth);
32533
+ const inner = renderInner(this.detail, this.props.theme, innerWidth, this.diagnostics);
32423
32534
  return drawBorder(inner, width, this.props.theme);
32424
32535
  }
32425
32536
  dispose() {
@@ -32429,10 +32540,70 @@ class StatusDialogComponent {
32429
32540
  }
32430
32541
  }
32431
32542
  }
32432
- function renderInner(s, theme, innerWidth) {
32543
+ function formatPiStatusSummary(s) {
32544
+ const warnings = [];
32545
+ if (s.lastTransformError)
32546
+ warnings.push("transform_update_failed");
32547
+ if (s.historianFailureCount > 0)
32548
+ warnings.push("historian_unavailable");
32549
+ if (s.configParseFailures.length > 0 || s.hasDeprecatedProtectedTags) {
32550
+ warnings.push("configuration_warning");
32551
+ }
32552
+ if (s.embedding.state === "stopped")
32553
+ warnings.push("embedding_unavailable");
32554
+ return renderUserStatusSummary({
32555
+ inputTokens: s.inputTokens,
32556
+ usableContextTokens: s.contextLimit,
32557
+ usagePercentage: s.usagePercentage,
32558
+ cacheLifetime: formatCacheTtlDisplay({
32559
+ value: s.cacheTtl,
32560
+ source: s.cacheTtlSource,
32561
+ modelKey: s.cacheTtlModelKey
32562
+ }).replace(/^Cache TTL:\s*/, ""),
32563
+ automaticCompressionThreshold: s.compactionEnabled ? s.executeThreshold : null,
32564
+ compression: {
32565
+ state: s.historianRunning ? "compressing" : s.compartmentCount > 0 ? "ready" : "waiting",
32566
+ historyBlockCount: s.compartmentCount
32567
+ },
32568
+ reclaimable: {
32569
+ toolOutputCount: s.tailHygiene?.reclaimableToolOutputCount ?? 0,
32570
+ tokens: s.tailHygiene?.u ?? 0
32571
+ },
32572
+ memoryCount: s.memoryCount,
32573
+ noteCount: s.sessionNoteCount + s.readySmartNoteCount,
32574
+ embedding: s.embedding,
32575
+ warnings
32576
+ }, "plain");
32577
+ }
32578
+ function formatPiStatusDiagnostics(s) {
32579
+ const summary = formatPiStatusSummary(s).replace("Magic Context Status", "Magic Context Diagnostics");
32580
+ return [
32581
+ summary,
32582
+ "",
32583
+ `Session: ${s.sessionId}`,
32584
+ `Active profile: ${s.activeProfile ?? "none"}`,
32585
+ `Work tokens: ${fmt2(s.newWorkTokens)} new · ${fmt2(s.totalInputTokens)} total input`,
32586
+ ...s.tailHygiene ? [`Hygiene: ${formatTailHygiene(s.tailHygiene)}`] : [],
32587
+ `Tags: ${s.activeTags} active · ${s.droppedTags} dropped · ${s.totalTags} total`,
32588
+ `Pending drops: ${s.pendingOpsCount}`,
32589
+ `Protected tokens: ${fmt2(s.protectedTokens.protectedMass)} (${s.protectedTokens.protectedCount} tags / floor ${fmt2(s.protectedTokens.floor)})`,
32590
+ `History block tokens: ${fmt2(s.historyBlockTokens)}`,
32591
+ `Compression budget: ${s.compressionBudget ? `${fmt2(s.compressionBudget)} (${s.compressionUsage} used)` : "unavailable"}`,
32592
+ `Subagent: ${s.isSubagent ? "yes" : "no"}`
32593
+ ].join(`
32594
+ `);
32595
+ }
32596
+ function renderInner(s, theme, innerWidth, diagnostics) {
32433
32597
  const pctColor = s.usagePercentage >= 80 ? "error" : s.usagePercentage >= 65 ? "warning" : "accent";
32434
32598
  const lines = [];
32435
32599
  lines.push(`${theme.fg("accent", theme.bold("⚡ Magic Context Status"))} ${theme.fg("muted", `v${package_default.version}`)}`);
32600
+ lines.push(theme.fg("muted", `[D] Diagnostics: ${diagnostics ? "on" : "off"}`));
32601
+ if (!diagnostics) {
32602
+ lines.push("", ...formatPiStatusSummary(s).split(`
32603
+ `).slice(1));
32604
+ lines.push("", theme.fg("muted", "Press D for diagnostics · Escape to close"));
32605
+ return lines;
32606
+ }
32436
32607
  lines.push("");
32437
32608
  for (const failure of s.configParseFailures) {
32438
32609
  lines.push(theme.fg("error", formatConfigParseStatusLine(failure)));
@@ -32484,9 +32655,9 @@ function renderInner(s, theme, innerWidth) {
32484
32655
  })}`);
32485
32656
  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
32657
  if (s.lastTransformError)
32487
- lines.push(theme.fg("error", `⚠ ${s.lastTransformError}`));
32658
+ lines.push(theme.fg("error", renderUserFacingFailure("transform_update_failed")));
32488
32659
  if (s.historianLastError)
32489
- lines.push(theme.fg("error", `⚠ ${s.historianLastError}`));
32660
+ lines.push(theme.fg("error", renderUserFacingFailure("historian_unavailable")));
32490
32661
  lines.push("");
32491
32662
  lines.push(theme.fg("muted", "Press Escape to close"));
32492
32663
  return lines;
@@ -32606,6 +32777,17 @@ ${safeStringify(tool.parameters)}`);
32606
32777
  const cacheRemainingMs = neverExpires ? Number.POSITIVE_INFINITY : meta.lastResponseTime > 0 ? Math.max(0, cacheTtlMs - elapsed) : cacheTtlMs;
32607
32778
  const cacheExpired = meta.lastResponseTime > 0 && cacheRemainingMs === 0;
32608
32779
  const historyBlockTokens = compartmentTokens + factTokens;
32780
+ const embeddingCoverage = safeRead(() => getEmbeddingCoverageStatus(deps.db, deps.projectIdentity, sessionId), {
32781
+ enabled: false,
32782
+ model: "off",
32783
+ provider: "off",
32784
+ session: { embedded: 0, total: 0 },
32785
+ memories: { embedded: 0, total: 0 },
32786
+ commits: { embedded: 0, total: 0, gitEnabled: false },
32787
+ shadowBackfillStalls: []
32788
+ });
32789
+ const embeddingRunState = getEmbedDrainUiStatus(sessionId, undefined).status;
32790
+ const embeddingState = !embeddingCoverage.enabled ? "off" : embeddingRunState !== "idle" ? embeddingRunState : embeddingCoverage.session.total > 0 && embeddingCoverage.session.embedded >= embeddingCoverage.session.total ? "ready" : "waiting";
32609
32791
  const historyBudgetPercentage = deps.historyBudgetPercentage ?? 0.15;
32610
32792
  const compressionBudget = contextLimit > 0 ? Math.floor(contextLimit * (Math.min(threshold.percentage, 80) / 100) * historyBudgetPercentage) : null;
32611
32793
  return {
@@ -32615,6 +32797,10 @@ ${safeStringify(tool.parameters)}`);
32615
32797
  inputTokens,
32616
32798
  systemPromptTokens,
32617
32799
  compartmentCount: compartments.length,
32800
+ lastCompartmentRange: (() => {
32801
+ const last = compartments.at(-1);
32802
+ return last ? `${last.startMessage}-${last.endMessage}` : null;
32803
+ })(),
32618
32804
  memoryCount: safeRead(() => getMemoryCount(deps.db, deps.projectIdentity), 0),
32619
32805
  memoryBlockCount,
32620
32806
  sessionNoteCount: safeRead(() => getNotes(deps.db, {
@@ -32629,6 +32815,7 @@ ${safeStringify(tool.parameters)}`);
32629
32815
  }).length, 0),
32630
32816
  pendingOpsCount: pendingOps,
32631
32817
  historianRunning: meta.compartmentInProgress,
32818
+ timesExecuteThresholdReached: meta.timesExecuteThresholdReached,
32632
32819
  historianFailureCount: Number(metaRow?.historian_failure_count ?? 0),
32633
32820
  historianLastFailureAt: typeof metaRow?.historian_last_failure_at === "number" ? metaRow.historian_last_failure_at : null,
32634
32821
  historianLastError: metaRow?.historian_last_error ?? null,
@@ -32670,7 +32857,19 @@ ${safeStringify(tool.parameters)}`);
32670
32857
  totalInputTokens: workMetrics.totalInputTokens,
32671
32858
  upgradeNeededCount: safeRead(() => countCompartmentsNeedingUpgrade(deps.db, sessionId), 0),
32672
32859
  recompInFlight: isPiRecompInFlight(sessionId),
32673
- hasDeprecatedProtectedTags: deps.hasDeprecatedProtectedTags ?? false
32860
+ hasDeprecatedProtectedTags: deps.hasDeprecatedProtectedTags ?? false,
32861
+ compactionEnabled: deps.compactionEnabled ?? true,
32862
+ dreamer: {
32863
+ enabled: deps.dreamer?.runnable === true,
32864
+ scheduleSummary: deps.dreamer?.scheduleSummary ?? null,
32865
+ lastRunAt: safeRead(() => getMostRecentTaskRunAt(deps.db, deps.projectIdentity), null),
32866
+ backlog: safeRead(() => getDreamTaskBacklogs(deps.db, deps.projectIdentity, CANONICAL_DREAM_TASKS), {})
32867
+ },
32868
+ embedding: {
32869
+ state: embeddingState,
32870
+ indexed: embeddingCoverage.session.embedded,
32871
+ total: embeddingCoverage.session.total
32872
+ }
32674
32873
  };
32675
32874
  }
32676
32875
  function safeStringify(value) {
@@ -32806,8 +33005,18 @@ function relTime(ts) {
32806
33005
  function registerCtxStatusCommand(pi, deps) {
32807
33006
  pi.registerCommand("ctx-status", {
32808
33007
  description: "Show Magic Context status for the current Pi session",
32809
- handler: async (_args, ctx) => {
33008
+ handler: async (args, ctx) => {
32810
33009
  const sendStatus = createCtxStatusSender(pi, ctx);
33010
+ const mode = args.trim().toLowerCase();
33011
+ if (mode !== "" && mode !== "diagnostics") {
33012
+ sendStatus({
33013
+ title: "/ctx-status",
33014
+ text: "Usage: /ctx-status [diagnostics]",
33015
+ level: "info"
33016
+ });
33017
+ return;
33018
+ }
33019
+ const diagnostics = mode === "diagnostics";
32811
33020
  const runtimeDeps = deps.resolveStatusDeps?.(ctx) ?? deps;
32812
33021
  const projectIdentity = runtimeDeps.resolveProject?.(ctx).projectIdentity ?? runtimeDeps.projectIdentity;
32813
33022
  const currentDeps = { ...runtimeDeps, projectIdentity };
@@ -32824,41 +33033,12 @@ No active Pi session is available.`,
32824
33033
  }
32825
33034
  try {
32826
33035
  if (ctx.hasUI) {
32827
- await showStatusDialog(pi, ctx, currentDeps);
33036
+ await showStatusDialog(pi, ctx, currentDeps, diagnostics);
32828
33037
  return;
32829
33038
  }
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);
33039
+ const statusDetail = buildPiStatusDetail(pi, ctx, currentDeps, sessionId);
33040
+ const statusText = diagnostics ? formatPiStatusDiagnostics(statusDetail) : formatPiStatusSummary(statusDetail);
33041
+ const details = buildStatusDetails(currentDeps, statusDetail);
32862
33042
  const profileStatus = currentDeps.activeProfile ?? "none";
32863
33043
  const storage = getMagicContextStorageResolution();
32864
33044
  const deprecationNotice = currentDeps.hasDeprecatedProtectedTags ? `
@@ -32866,68 +33046,47 @@ No active Pi session is available.`,
32866
33046
  ⚠️ Config: "protected_tags" is deprecated and ignored; use "protected_tokens" instead.` : "";
32867
33047
  sendStatus({
32868
33048
  title: "/ctx-status",
32869
- text: `${statusText}${deprecationNotice}
33049
+ text: diagnostics ? `${statusText}${deprecationNotice}
32870
33050
 
32871
33051
  Active profile: ${profileStatus}
32872
33052
 
32873
- Storage: ${storage.path} (${storage.source})`,
33053
+ Storage: ${storage.path} (${storage.source})` : statusText,
32874
33054
  level: "info",
32875
33055
  rpcDisplay: "dialog"
32876
33056
  }, details);
32877
33057
  } catch (error) {
33058
+ sessionLog(sessionId, `ctx-status failed code=${userFacingFailureCode("status_unavailable")}`, error);
32878
33059
  sendStatus({
32879
33060
  title: "/ctx-status",
32880
- text: `## Magic Status — Failed
32881
-
32882
- ${describeError(error).brief}`,
33061
+ text: renderUserFacingFailure("status_unavailable"),
32883
33062
  level: "error"
32884
33063
  });
32885
33064
  }
32886
33065
  }
32887
33066
  });
32888
33067
  }
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);
33068
+ function buildStatusDetails(deps, status) {
32897
33069
  return {
32898
- sessionId,
33070
+ sessionId: status.sessionId,
32899
33071
  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
33072
+ activeProfile: status.activeProfile,
33073
+ activeTags: status.activeTags,
33074
+ droppedTags: status.droppedTags,
33075
+ totalBytes: status.activeBytes,
33076
+ pendingOps: status.pendingOpsCount,
33077
+ lastExecuteThreshold: status.timesExecuteThresholdReached,
33078
+ compartmentCount: status.compartmentCount,
33079
+ lastCompartmentRange: status.lastCompartmentRange,
33080
+ memoryCount: status.memoryCount,
33081
+ noteCount: status.sessionNoteCount + status.readySmartNoteCount,
33082
+ dreamer: status.dreamer,
33083
+ historian: {
33084
+ lastFireCount: status.timesExecuteThresholdReached,
33085
+ inProgress: status.historianRunning,
33086
+ lastFailureAt: status.historianLastFailureAt,
33087
+ lastError: status.historianLastError,
33088
+ failureCount: status.historianFailureCount
33089
+ }
32931
33090
  };
32932
33091
  }
32933
33092
 
@@ -34280,6 +34439,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
34280
34439
  cacheTtlConfigured: bootProjectDeps.cacheTtlConfigured,
34281
34440
  configParseFailures: bootProjectDeps.configParseFailures,
34282
34441
  hasDeprecatedProtectedTags: bootProjectDeps.hasDeprecatedProtectedTags,
34442
+ compactionEnabled: isCompactionEnabled(bootProjectDeps.config),
34283
34443
  resolveStatusDeps: (ctx) => {
34284
34444
  const current = resolveCurrentProjectDeps(ctx);
34285
34445
  return {
@@ -34299,7 +34459,8 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
34299
34459
  cacheTtlConfig: current.config.cache_ttl,
34300
34460
  cacheTtlConfigured: current.cacheTtlConfigured,
34301
34461
  configParseFailures: current.configParseFailures,
34302
- hasDeprecatedProtectedTags: current.hasDeprecatedProtectedTags
34462
+ hasDeprecatedProtectedTags: current.hasDeprecatedProtectedTags,
34463
+ compactionEnabled: isCompactionEnabled(current.config)
34303
34464
  };
34304
34465
  }
34305
34466
  });