@cortexkit/opencode-magic-context 0.11.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +3 -1
  2. package/dist/agents/historian.d.ts +1 -0
  3. package/dist/agents/historian.d.ts.map +1 -1
  4. package/dist/agents/magic-context-prompt.d.ts +1 -1
  5. package/dist/agents/magic-context-prompt.d.ts.map +1 -1
  6. package/dist/cli/doctor.d.ts.map +1 -1
  7. package/dist/cli.js +362 -83
  8. package/dist/config/index.d.ts.map +1 -1
  9. package/dist/config/schema/magic-context.d.ts +117 -2
  10. package/dist/config/schema/magic-context.d.ts.map +1 -1
  11. package/dist/config/variable.d.ts +46 -0
  12. package/dist/config/variable.d.ts.map +1 -0
  13. package/dist/features/magic-context/compartment-storage.d.ts +27 -1
  14. package/dist/features/magic-context/compartment-storage.d.ts.map +1 -1
  15. package/dist/features/magic-context/compression-depth-storage.d.ts +7 -0
  16. package/dist/features/magic-context/compression-depth-storage.d.ts.map +1 -1
  17. package/dist/features/magic-context/memory/embedding-probe.d.ts +69 -0
  18. package/dist/features/magic-context/memory/embedding-probe.d.ts.map +1 -0
  19. package/dist/features/magic-context/scheduler.d.ts +5 -1
  20. package/dist/features/magic-context/scheduler.d.ts.map +1 -1
  21. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  22. package/dist/hooks/magic-context/caveman.d.ts +35 -0
  23. package/dist/hooks/magic-context/caveman.d.ts.map +1 -0
  24. package/dist/hooks/magic-context/command-handler.d.ts +29 -1
  25. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  26. package/dist/hooks/magic-context/compartment-prompt.d.ts +8 -3
  27. package/dist/hooks/magic-context/compartment-prompt.d.ts.map +1 -1
  28. package/dist/hooks/magic-context/compartment-runner-compressor.d.ts +46 -0
  29. package/dist/hooks/magic-context/compartment-runner-compressor.d.ts.map +1 -1
  30. package/dist/hooks/magic-context/compartment-runner-historian.d.ts +10 -0
  31. package/dist/hooks/magic-context/compartment-runner-historian.d.ts.map +1 -1
  32. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  33. package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts +29 -0
  34. package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -0
  35. package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
  36. package/dist/hooks/magic-context/compartment-runner-types.d.ts +9 -0
  37. package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
  38. package/dist/hooks/magic-context/compartment-runner-validation.d.ts +5 -0
  39. package/dist/hooks/magic-context/compartment-runner-validation.d.ts.map +1 -1
  40. package/dist/hooks/magic-context/compartment-runner.d.ts +31 -1
  41. package/dist/hooks/magic-context/compartment-runner.d.ts.map +1 -1
  42. package/dist/hooks/magic-context/event-handler.d.ts +4 -0
  43. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  44. package/dist/hooks/magic-context/event-resolvers.d.ts +39 -1
  45. package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
  46. package/dist/hooks/magic-context/execute-status.d.ts +4 -1
  47. package/dist/hooks/magic-context/execute-status.d.ts.map +1 -1
  48. package/dist/hooks/magic-context/hook.d.ts +13 -4
  49. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  50. package/dist/hooks/magic-context/inject-compartments.d.ts +1 -1
  51. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  52. package/dist/hooks/magic-context/nudger.d.ts.map +1 -1
  53. package/dist/hooks/magic-context/read-session-chunk.d.ts +11 -0
  54. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  55. package/dist/hooks/magic-context/read-session-db.d.ts +13 -0
  56. package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
  57. package/dist/hooks/magic-context/read-session-formatting.d.ts +7 -0
  58. package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
  59. package/dist/hooks/magic-context/system-prompt-hash.d.ts +2 -0
  60. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  61. package/dist/hooks/magic-context/temporal-awareness.d.ts +73 -0
  62. package/dist/hooks/magic-context/temporal-awareness.d.ts.map +1 -0
  63. package/dist/hooks/magic-context/transform-compartment-phase.d.ts +10 -0
  64. package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
  65. package/dist/hooks/magic-context/transform.d.ts +18 -0
  66. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +2562 -1086
  69. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  70. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  71. package/dist/shared/model-requirements.d.ts.map +1 -1
  72. package/dist/shared/models-dev-cache.d.ts.map +1 -1
  73. package/dist/shared/rpc-types.d.ts +11 -0
  74. package/dist/shared/rpc-types.d.ts.map +1 -1
  75. package/dist/tui/data/context-db.d.ts.map +1 -1
  76. package/package.json +1 -1
  77. package/src/shared/model-requirements.ts +3 -1
  78. package/src/shared/models-dev-cache.ts +11 -1
  79. package/src/shared/rpc-types.ts +11 -0
  80. package/src/tui/data/context-db.ts +1 -0
package/dist/index.js CHANGED
@@ -49,7 +49,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
49
49
  var DREAMER_AGENT = "dreamer";
50
50
 
51
51
  // src/agents/historian.ts
52
- var HISTORIAN_AGENT = "historian";
52
+ var HISTORIAN_AGENT = "historian", HISTORIAN_EDITOR_AGENT = "historian-editor";
53
53
 
54
54
  // src/agents/sidekick.ts
55
55
  var SIDEKICK_AGENT = "sidekick";
@@ -14070,10 +14070,17 @@ var init_agent_overrides = __esm(() => {
14070
14070
  });
14071
14071
 
14072
14072
  // src/config/schema/magic-context.ts
14073
- var DEFAULT_NUDGE_INTERVAL_TOKENS = 1e4, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65, DEFAULT_HISTORIAN_TIMEOUT_MS = 300000, DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15, DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2", DREAMER_TASKS, DreamingTaskSchema, DEFAULT_DREAMER_TASKS, DreamerConfigSchema, SidekickConfigSchema, BaseEmbeddingConfigSchema, EmbeddingConfigSchema, MagicContextConfigSchema;
14073
+ var DEFAULT_NUDGE_INTERVAL_TOKENS = 1e4, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65, DEFAULT_HISTORIAN_TIMEOUT_MS = 300000, DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15, DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2", DEFAULT_COMPRESSOR_MIN_COMPARTMENT_RATIO = 1000, DEFAULT_COMPRESSOR_MAX_MERGE_DEPTH = 5, DEFAULT_COMPRESSOR_COOLDOWN_MS = 600000, DEFAULT_COMPRESSOR_MAX_COMPARTMENTS_PER_PASS = 15, DEFAULT_COMPRESSOR_GRACE_COMPARTMENTS = 10, COMPRESSOR_MERGE_RATIO_BY_DEPTH, DREAMER_TASKS, DreamingTaskSchema, DEFAULT_DREAMER_TASKS, DreamerConfigSchema, SidekickConfigSchema, HistorianConfigSchema, BaseEmbeddingConfigSchema, EmbeddingConfigSchema, MagicContextConfigSchema;
14074
14074
  var init_magic_context = __esm(() => {
14075
14075
  init_zod();
14076
14076
  init_agent_overrides();
14077
+ COMPRESSOR_MERGE_RATIO_BY_DEPTH = {
14078
+ 1: 1.33,
14079
+ 2: 1.5,
14080
+ 3: 2,
14081
+ 4: 2,
14082
+ 5: 0
14083
+ };
14077
14084
  DREAMER_TASKS = [
14078
14085
  "consolidate",
14079
14086
  "verify",
@@ -14101,6 +14108,9 @@ var init_magic_context = __esm(() => {
14101
14108
  timeout_ms: exports_external.number().default(30000),
14102
14109
  system_prompt: exports_external.string().optional()
14103
14110
  }).optional();
14111
+ HistorianConfigSchema = AgentOverrideConfigSchema.extend({
14112
+ two_pass: exports_external.boolean().default(false)
14113
+ }).optional();
14104
14114
  BaseEmbeddingConfigSchema = exports_external.object({
14105
14115
  provider: exports_external.enum(["local", "openai-compatible", "off"]).default("local"),
14106
14116
  model: exports_external.string().optional(),
@@ -14143,14 +14153,17 @@ var init_magic_context = __esm(() => {
14143
14153
  MagicContextConfigSchema = exports_external.object({
14144
14154
  enabled: exports_external.boolean().default(true),
14145
14155
  ctx_reduce_enabled: exports_external.boolean().default(true),
14146
- historian: AgentOverrideConfigSchema.optional(),
14156
+ historian: HistorianConfigSchema,
14147
14157
  dreamer: DreamerConfigSchema.optional(),
14148
14158
  cache_ttl: exports_external.union([exports_external.string(), exports_external.object({ default: exports_external.string() }).catchall(exports_external.string())]).default("5m"),
14149
14159
  nudge_interval_tokens: exports_external.number().min(1000).default(DEFAULT_NUDGE_INTERVAL_TOKENS),
14150
14160
  execute_threshold_percentage: exports_external.union([
14151
- exports_external.number().min(20).max(95),
14152
- exports_external.object({ default: exports_external.number().min(20).max(95) }).catchall(exports_external.number().min(20).max(95))
14161
+ exports_external.number().min(20).max(80),
14162
+ exports_external.object({ default: exports_external.number().min(20).max(80) }).catchall(exports_external.number().min(20).max(80))
14153
14163
  ]).default(DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE),
14164
+ execute_threshold_tokens: exports_external.object({
14165
+ default: exports_external.number().min(5000).max(2000000).optional()
14166
+ }).catchall(exports_external.number().min(5000).max(2000000)).optional(),
14154
14167
  protected_tags: exports_external.number().min(1).max(100).optional(),
14155
14168
  auto_drop_tool_age: exports_external.number().min(10).default(100),
14156
14169
  drop_tool_structure: exports_external.boolean().default(true),
@@ -14163,6 +14176,21 @@ var init_magic_context = __esm(() => {
14163
14176
  min_clusters: exports_external.number().min(1).default(3)
14164
14177
  }).default({ enabled: true, min_clusters: 3 }),
14165
14178
  compaction_markers: exports_external.boolean().default(true),
14179
+ compressor: exports_external.object({
14180
+ enabled: exports_external.boolean().default(true),
14181
+ min_compartment_ratio: exports_external.number().min(100).max(1e4).default(DEFAULT_COMPRESSOR_MIN_COMPARTMENT_RATIO),
14182
+ max_merge_depth: exports_external.number().min(1).max(5).default(DEFAULT_COMPRESSOR_MAX_MERGE_DEPTH),
14183
+ cooldown_ms: exports_external.number().min(60000).default(DEFAULT_COMPRESSOR_COOLDOWN_MS),
14184
+ max_compartments_per_pass: exports_external.number().min(3).max(50).default(DEFAULT_COMPRESSOR_MAX_COMPARTMENTS_PER_PASS),
14185
+ grace_compartments: exports_external.number().min(0).max(100).default(DEFAULT_COMPRESSOR_GRACE_COMPARTMENTS)
14186
+ }).default({
14187
+ enabled: true,
14188
+ min_compartment_ratio: DEFAULT_COMPRESSOR_MIN_COMPARTMENT_RATIO,
14189
+ max_merge_depth: DEFAULT_COMPRESSOR_MAX_MERGE_DEPTH,
14190
+ cooldown_ms: DEFAULT_COMPRESSOR_COOLDOWN_MS,
14191
+ max_compartments_per_pass: DEFAULT_COMPRESSOR_MAX_COMPARTMENTS_PER_PASS,
14192
+ grace_compartments: DEFAULT_COMPRESSOR_GRACE_COMPARTMENTS
14193
+ }),
14166
14194
  embedding: EmbeddingConfigSchema.default({
14167
14195
  provider: "local",
14168
14196
  model: DEFAULT_LOCAL_EMBEDDING_MODEL
@@ -14176,10 +14204,12 @@ var init_magic_context = __esm(() => {
14176
14204
  enabled: exports_external.boolean().default(false),
14177
14205
  token_budget: exports_external.number().min(2000).max(30000).default(1e4),
14178
14206
  min_reads: exports_external.number().min(2).max(20).default(4)
14179
- }).default({ enabled: false, token_budget: 1e4, min_reads: 4 })
14207
+ }).default({ enabled: false, token_budget: 1e4, min_reads: 4 }),
14208
+ temporal_awareness: exports_external.boolean().default(false)
14180
14209
  }).default({
14181
14210
  user_memories: { enabled: false, promotion_threshold: 3 },
14182
- pin_key_files: { enabled: false, token_budget: 1e4, min_reads: 4 }
14211
+ pin_key_files: { enabled: false, token_budget: 1e4, min_reads: 4 },
14212
+ temporal_awareness: false
14183
14213
  }),
14184
14214
  memory: exports_external.object({
14185
14215
  enabled: exports_external.boolean().default(true),
@@ -14300,6 +14330,7 @@ var init_model_requirements = __esm(() => {
14300
14330
  ];
14301
14331
  AGENT_MODEL_REQUIREMENTS = {
14302
14332
  [HISTORIAN_AGENT]: { fallbackChain: HISTORIAN_FALLBACK_CHAIN },
14333
+ [HISTORIAN_EDITOR_AGENT]: { fallbackChain: HISTORIAN_FALLBACK_CHAIN },
14303
14334
  [DREAMER_AGENT]: { fallbackChain: DREAMER_FALLBACK_CHAIN },
14304
14335
  [SIDEKICK_AGENT]: { fallbackChain: SIDEKICK_FALLBACK_CHAIN }
14305
14336
  };
@@ -14589,14 +14620,16 @@ function replaceAllCompartmentState(db, sessionId, compartments, facts) {
14589
14620
  db.prepare("UPDATE session_meta SET memory_block_cache = '' WHERE session_id = ?").run(sessionId);
14590
14621
  })();
14591
14622
  }
14592
- function buildCompartmentBlock(compartments, facts, memoryBlock) {
14623
+ function buildCompartmentBlock(compartments, facts, memoryBlock, dateRanges) {
14593
14624
  const lines = [];
14594
14625
  if (memoryBlock) {
14595
14626
  lines.push(memoryBlock);
14596
14627
  lines.push("");
14597
14628
  }
14598
14629
  for (const c of compartments) {
14599
- lines.push(`<compartment start="${c.startMessage}" end="${c.endMessage}" title="${escapeXmlAttr(c.title)}">`);
14630
+ const dates = dateRanges?.byId.get(c.id);
14631
+ const dateAttr = dates ? ` start-date="${dates.start}" end-date="${dates.end}"` : "";
14632
+ lines.push(`<compartment start="${c.startMessage}" end="${c.endMessage}"${dateAttr} title="${escapeXmlAttr(c.title)}">`);
14600
14633
  lines.push(escapeXmlContent(c.content));
14601
14634
  lines.push("</compartment>");
14602
14635
  lines.push("");
@@ -14679,8 +14712,29 @@ function clearRecompStaging(db, sessionId) {
14679
14712
  db.transaction(() => {
14680
14713
  db.prepare("DELETE FROM recomp_compartments WHERE session_id = ?").run(sessionId);
14681
14714
  db.prepare("DELETE FROM recomp_facts WHERE session_id = ?").run(sessionId);
14715
+ try {
14716
+ db.prepare("UPDATE session_meta SET recomp_partial_range_start = 0, recomp_partial_range_end = 0 WHERE session_id = ?").run(sessionId);
14717
+ } catch {}
14682
14718
  })();
14683
14719
  }
14720
+ function getRecompPartialRange(db, sessionId) {
14721
+ try {
14722
+ const row = db.prepare("SELECT recomp_partial_range_start AS start, recomp_partial_range_end AS end FROM session_meta WHERE session_id = ?").get(sessionId);
14723
+ const start = typeof row?.start === "number" ? row.start : 0;
14724
+ const end = typeof row?.end === "number" ? row.end : 0;
14725
+ if (start <= 0 || end <= 0)
14726
+ return null;
14727
+ return { start, end };
14728
+ } catch {
14729
+ return null;
14730
+ }
14731
+ }
14732
+ function setRecompPartialRange(db, sessionId, range) {
14733
+ const start = range ? range.start : 0;
14734
+ const end = range ? range.end : 0;
14735
+ db.prepare("INSERT OR IGNORE INTO session_meta (session_id) VALUES (?)").run(sessionId);
14736
+ db.prepare("UPDATE session_meta SET recomp_partial_range_start = ?, recomp_partial_range_end = ? WHERE session_id = ?").run(start, end, sessionId);
14737
+ }
14684
14738
  function isRecompCompartmentRow(row) {
14685
14739
  if (row === null || typeof row !== "object")
14686
14740
  return false;
@@ -14706,23 +14760,36 @@ var init_compartment_storage = __esm(() => {
14706
14760
  });
14707
14761
 
14708
14762
  // src/hooks/magic-context/compartment-prompt.ts
14709
- function buildCompressorPrompt(compartments, currentTokens, targetTokens, averageDepth = 0) {
14763
+ function buildHistorianEditorPrompt(draft) {
14764
+ return [
14765
+ "This is a historian draft. Clean it up following the rules in your system prompt.",
14766
+ "",
14767
+ "<draft>",
14768
+ draft,
14769
+ "</draft>",
14770
+ "",
14771
+ "Return the cleaned draft as valid XML matching the original structure."
14772
+ ].join(`
14773
+ `);
14774
+ }
14775
+ function buildCompressorPrompt(compartments, currentTokens, targetTokens, outputDepth, outputCount) {
14710
14776
  const lines = [];
14711
- lines.push(`These ${compartments.length} compartments use approximately ${currentTokens} tokens. Compress them to approximately ${targetTokens} tokens.`);
14777
+ const densityLabel = outputDepth === 1 ? "MERGE ONLY" : outputDepth === 2 ? "LITE TIGHTEN" : outputDepth === 3 ? "FULL CONDENSE" : "ULTRA TELEGRAPH";
14778
+ const resolvedOutputCount = outputCount ?? Math.max(1, Math.ceil(compartments.length / 2));
14779
+ lines.push(`Density target: LEVEL ${outputDepth} (${densityLabel}). See system prompt for level rules.`);
14780
+ lines.push(`Input: ${compartments.length} compartments, ~${currentTokens} tokens. Target output: exactly ${resolvedOutputCount} compartments, ~${targetTokens} tokens total.`);
14712
14781
  lines.push("");
14713
- if (averageDepth < 2) {
14714
- lines.push("These compartments contain U: lines showing what the user asked. Preserve ALL U: lines exactly as they are. Focus compression on the narrative prose \u2014 merge compartments, remove redundancy, condense descriptions. Do not modify or remove U: lines.");
14715
- } else if (averageDepth < 3) {
14716
- lines.push("These compartments have already been compressed multiple times. Condense each U: line to its core intent in one sentence, but keep them as separate U: lines. Focus aggressive compression on the narrative prose.");
14782
+ if (outputDepth === 1) {
14783
+ lines.push("Merge only. Preserve narrative and all U: lines. Drop only genuine duplicate sentences spanning compartments.");
14784
+ } else if (outputDepth === 2) {
14785
+ lines.push("Merge + drop filler words and hedging. Keep grammar, keep U: lines verbatim.");
14786
+ } else if (outputDepth === 3) {
14787
+ lines.push("Merge into single-paragraph compartments. Drop articles and weak auxiliaries. Keep only IRREPLACEABLE U: lines.");
14717
14788
  } else {
14718
- lines.push("These compartments have been heavily compressed. Fold any remaining U: user intent into the narrative prose \u2014 do not keep separate U: lines. Write fluent summary paragraphs that naturally capture both what was asked and what was done.");
14789
+ lines.push("Merge into telegraphic fragments with symbol connectives (\u2192 + // |). U: lines only if truly irreplaceable.");
14719
14790
  }
14720
14791
  lines.push("");
14721
- lines.push("Rules:");
14722
- lines.push("- Merge adjacent compartments when they cover related work.");
14723
- lines.push("- Each output compartment must use the exact start/end ordinals from the input compartments it covers.");
14724
- lines.push("- Do not invent new ordinal boundaries that don't exist in the input.");
14725
- lines.push("- Preserve commit hashes and key technical details where possible.");
14792
+ lines.push("Preserved literally at all levels: commit hashes, file paths, URLs, code spans.");
14726
14793
  lines.push("");
14727
14794
  for (const c of compartments) {
14728
14795
  lines.push(`<compartment start="${c.startMessage}" end="${c.endMessage}" title="${escapeXmlAttr(c.title)}">`);
@@ -14730,7 +14797,7 @@ function buildCompressorPrompt(compartments, currentTokens, targetTokens, averag
14730
14797
  lines.push("</compartment>");
14731
14798
  lines.push("");
14732
14799
  }
14733
- lines.push("Return compressed compartments as XML.");
14800
+ lines.push("Return merged compartments as XML.");
14734
14801
  return lines.join(`
14735
14802
  `);
14736
14803
  }
@@ -14764,29 +14831,144 @@ var COMPARTMENT_AGENT_SYSTEM_PROMPT = `You condense long AI coding sessions into
14764
14831
  Compartment rules:
14765
14832
  - A compartment is one contiguous completed work unit: investigation, fix, refactor, docs update, feature, or decision.
14766
14833
  - Start a new compartment only when the work clearly pivots to a different objective.
14834
+ - If one broad effort contains multiple completed sub-pivots with distinct outcomes, prefer multiple smaller compartments over one umbrella compartment with many U: lines.
14767
14835
  - Do not create compartments for magic-context commands or tool-only noise.
14768
14836
  - If the input ends mid-topic, leave it out and report its first message index in <unprocessed_from>.
14769
14837
  - All compartment start/end ordinals and <unprocessed_from> must use the absolute raw message numbers shown in the input. Never renumber relative to this chunk.
14838
+ - Every displayed raw message ordinal in the input MUST appear in exactly one compartment. Gaps between compartments are invalid. When a displayed block is pure tool noise (e.g. a long "TC: ..." run with no narrative text), do NOT skip it \u2014 extend the preceding compartment's \`end\` to absorb the range, or include it inside the current compartment if the block falls within an ongoing work unit. Never create a dedicated compartment just to cover a tool-only run.
14770
14839
  - Only emit NEW compartments for the new messages. Do not re-emit existing compartments from the existing state.
14771
14840
  - Write comprehensive, detailed compartments. Include file paths, function names, commit hashes, config keys, and values when they matter.
14772
14841
  - Do not list every changed file. Do not narrate tool calls. Do not preserve dead-end exploration beyond a brief clause when needed.
14773
14842
 
14774
- User message preservation:
14775
- - Include high-signal user messages verbatim inside compartments, prefixed with U:.
14776
- - A high-signal user message states a goal, constraint, design decision, direction, preference, or rationale.
14777
- - Drop trivial messages: yes, continue, I agree, thanks, looks good, go ahead, and similar low-signal steering.
14778
- - Drop large pasted text unless it contains durable rules or requirements; summarize its gist instead.
14779
- - Place U: lines at the point in the summary where the user's direction changed the work.
14780
- - Limit to 3-5 U: lines per compartment \u2014 keep only the most important ones.
14781
-
14782
- Compartment example:
14783
- <compartment start="50" end="120" title="Built the LSP stack">
14784
- U: We need inline diagnostics on every edit, not just on-demand
14785
- Implemented in-process LSP client with per-server reader threads and crossbeam-channel delivery. Added inline edit diagnostics to write, edit, and apply_patch. commits: a3f891, b22c4e
14786
- U: Ship this as 0.2.0
14787
- Updated docs and publish automation, released v0.2.0.
14843
+ # Construction order (MANDATORY)
14844
+
14845
+ For each compartment, build in this exact order:
14846
+
14847
+ 1. Write the narrative summary first \u2014 what was done, why, and the outcome. This is 1\u20134 sentences covering the work unit completely.
14848
+ 2. Re-read your narrative. Ask: does the summary already convey all important decisions and constraints from this work unit?
14849
+ 3. If yes, the compartment is DONE with zero U: lines. Move on.
14850
+ 4. If no, identify the specific signal the narrative cannot capture. Add U: lines ONLY for those signals.
14851
+ 5. Before writing each U: line, run the CROSS-COMPARTMENT CHECK (see below).
14852
+
14853
+ Zero U: lines in a compartment is normal and expected. Most compartments should have 0\u20132 U: lines. Compartments with 3\u20135 are rare and must be justified by genuinely distinct durable signals.
14854
+
14855
+ # DROP rules (check these first \u2014 if any match, drop without exception)
14856
+
14857
+ - Questions in ANY form: "should I X?", "what about Y?", "do you think Z?", "isn't it better to A?", "why don't we B?", "any ideas?" \u2014 the resolved answer belongs in narrative only. If it feels important to keep the question, you are wrong: keep the answer in narrative.
14858
+ - Agreements and acknowledgments: "yes", "okay", "sure", "thanks", "go ahead", "looks good", "perfect", "I agree", "sounds good", "great".
14859
+ - Pure pacing and sequencing: "let's start", "continue", "let's do all", "now we can X", "let's commit", "first do A then B", "before that", "in the meantime".
14860
+ - Tactical observations: "I just noticed X", "we recently did Y", "I'm seeing Z right now", "this seems wrong".
14861
+ - Debugging status: "context is at 78%", "I'm restarting", "the last build failed".
14862
+ - Dogfooding/restart loops: "I restarted, can you check?", "okay we should have updated versions now", "let me try again".
14863
+ - Pasted error output or logs as U: line \u2014 capture the underlying problem in narrative, not the raw paste.
14864
+ - Examples and illustrations: "mine was when an agent wants to see X" \u2014 convert the underlying intent into a directive or drop.
14865
+ - Hype with embedded directive: ALL-CAPS pleas, "PLEASE PLEASE PLEASE just do X" \u2014 extract only the underlying directive into narrative; drop the hype.
14866
+ - Social signals, banter, emoji-only, enthusiasm.
14867
+ - Deferred ideas: "for later", "we can do X later", "another idea for the future".
14868
+ - Mid-process status: "running Y", "checking Z".
14869
+ - Superseded drafts once a later message gives the final decision.
14870
+ - Standing workflow rules ("always run lint before push") \u2014 these belong in WORKFLOW_RULES facts, not U: lines.
14871
+
14872
+ # Wording rule (default: verbatim)
14873
+
14874
+ By default, U: lines use the user's actual wording. The user's exact phrasing often carries negotiation context, emphasis, or technical specificity that paraphrase loses.
14875
+
14876
+ Paraphrase ONLY in these cases:
14877
+ - **Strip agreement prefixes**: "Yes X", "Okay X", "Sure X" \u2192 keep only the substantive part of X, in the user's original wording.
14878
+ - **Split compound directives**: If one message contains two distinct durable directives, split into two U: lines \u2014 each preserving the user's wording for its part.
14879
+ - **Drop conversational noise, keep core**: If a message wraps a directive in exploratory phrasing ("so I was thinking, maybe... but actually..."), drop the exploration and keep the core directive in the user's remaining words. Don't invent new phrasing.
14880
+
14881
+ NEVER:
14882
+ - Rewrite a clear user directive into a formal constraint statement. ("We need tool count at ~8" stays as-is; do NOT convert to "Tool count must be capped at 8.")
14883
+ - Synthesize a directive from multiple messages into one canonical statement. If the signal needs synthesis, it belongs in narrative, not a U: line.
14884
+ - Add technical specificity the user didn't state (file paths, function names, constant names). Canonical technical specificity belongs in narrative or facts, not in U: lines attributed to the user.
14885
+
14886
+ Good example:
14887
+ Original user message: "Yes let's do this. But we need to also make sure that we limit by message count as some sessions have quite a lot of messages."
14888
+ Correct U: line: "We need to also make sure that we limit by message count as some sessions have quite a lot of messages."
14889
+ (Stripped agreement prefix; kept the user's actual wording.)
14890
+
14891
+ Bad example (do not do this):
14892
+ Incorrect U: line: "Cap session history retrieval at a maximum message count to prevent memory issues on large sessions."
14893
+ (Rewrote the user directive into formal language and invented specificity.)
14894
+
14895
+ # KEEP rules (U: line survives only if ALL pass)
14896
+
14897
+ 1. DURABLE: The signal matters after the immediate turn.
14898
+ 2. SPECIFIC: Concrete goal, hard constraint, design decision, rejection, rationale, threshold, source-of-truth correction, or future-work directive.
14899
+ 3. OUTCOME-BACKED: This compartment's narrative clearly states what was done, decided, or changed because of the message.
14900
+ 4. NON-REDUNDANT: Not captured by another U: line (see CROSS-COMPARTMENT CHECK), by a fact, or by the narrative.
14901
+ 5. IRREPLACEABLE: The user's wording adds signal that narrative paraphrase cannot preserve. If the same information could appear as narrative without losing meaning, it should.
14902
+
14903
+ Categories of KEEP:
14904
+ - Hard gates, thresholds, config defaults, percentages, byte sizes with concrete values.
14905
+ - Accepted designs and explicit decisions.
14906
+ - Rejections and negative constraints: "X is wrong because Y", "we should NOT do Z".
14907
+ - Source-of-truth corrections: "follow the code, not the README".
14908
+ - Implementation pivots stated in future tense: "instead of X let's do Y", "switch to Z".
14909
+ - Durable rationale that explains WHY an approach was chosen.
14910
+ - Specific feature requirements stated as durable goals.
14911
+
14912
+ # PIVOT vs OBSERVATION test
14913
+
14914
+ A pivot is FUTURE-TENSE and changes the plan: "instead of X, let's do Y", "switch this to Z", "actually, let's not do A".
14915
+ An observation is PAST-TENSE or PRESENT-TENSE and reports state: "we recently did X", "I just noticed Y", "this is broken right now".
14916
+ Observations may frame narrative context but are NOT pivots and NOT durable. Drop them as U: lines.
14917
+
14918
+ # CROSS-COMPARTMENT CHECK (forward-looking)
14919
+
14920
+ Before writing ANY U: line in the current compartment:
14921
+ 1. Scan U: lines you have ALREADY written in previous compartments in this response.
14922
+ 2. If any prior U: line expresses the same intent, decision, constraint, or rationale \u2014 even in different words \u2014 do NOT write the new U: line.
14923
+ 3. Let the narrative in the current compartment carry the signal instead.
14924
+
14925
+ This is a forward operation: you only need to check what you already wrote, not revisit past compartments.
14926
+
14927
+ Examples of same-intent pairs to collapse:
14928
+ - "X shouldn't cause cache bust" + "X must not bust cache by itself" \u2192 keep only the first, in its original compartment.
14929
+ - "Let's use monorepo" + "Yes, monorepo is the right call" \u2192 keep only the first.
14930
+ - "Add logging" + "We need logs here too" \u2192 keep only the first.
14931
+
14932
+ Never keep two U: lines for the same underlying directive across compartments.
14933
+
14934
+ # Budget
14935
+
14936
+ - HARD LIMIT: 3\u20135 U: lines per compartment. 0\u20132 is typical.
14937
+ - If you have more than 5 candidate U: lines in one compartment, that is a signal to split into two compartments at a natural pivot, not to stuff more.
14938
+ - Every U: line must be immediately followed by 1\u20133 sentences describing the outcome, decision, or effect. Never stack two U: lines without intervening outcome text.
14939
+
14940
+ # Example: CORRECT preservation (narrative-first, verbatim U: line)
14941
+
14942
+ <compartment start="50" end="120" title="Built the auth layer">
14943
+ Implemented JWT auth with hard 60-minute exp claim and refresh-token rotation. Chose Bearer tokens over cookies after finding cookie-based auth broke the SPA flow. Added session_expiry config (read-only at runtime). Commits: a3f891, b22c4e.
14944
+ U: We need session expiry capped at 1 hour, no exceptions
14945
+ Hardcoded the 60-minute cap at the JWT-issuer layer so runtime overrides cannot extend it.
14946
+ </compartment>
14947
+
14948
+ Notice: only one U: line, kept verbatim from the user's actual message. The cookie-to-Bearer pivot is narrative because paraphrase captures the signal fully.
14949
+
14950
+ # Example: OVER-PRESERVATION (avoid)
14951
+
14952
+ <compartment start="200" end="350" title="Refactored data layer">
14953
+ U: Okay let's start on the data layer
14954
+ U: What about transactions?
14955
+ U: Yes that approach looks good
14956
+ U: Actually wait, maybe we need write-ahead logging
14957
+ U: I just noticed the previous commit broke a test
14958
+ U: Let's commit and ship it
14959
+ Refactored data layer with WAL mode and connection pooling.
14960
+ </compartment>
14961
+
14962
+ Problems: pacing, question, agreement, observation, pacing again. Only one message carries signal, and even that is narrative-capturable.
14963
+
14964
+ # CORRECT version of the above
14965
+
14966
+ <compartment start="200" end="350" title="Refactored data layer">
14967
+ Refactored data layer to use WAL mode plus connection pooling. Chose WAL over plain connections for concurrent read performance under sustained write load.
14788
14968
  </compartment>
14789
14969
 
14970
+ Zero U: lines. The pivot to WAL is clear in narrative.
14971
+
14790
14972
  Fact rules:
14791
14973
  - Facts are editable state, not append-only notes. Rewrite, normalize, deduplicate, or drop existing facts whenever needed.
14792
14974
  - Before emitting any fact, check all existing facts in the same category for semantic duplicates. If two facts describe the same decision, constraint, or default with different wording, merge them into one canonical statement. Never emit two facts that could be answered by the same question.
@@ -14849,7 +15031,44 @@ More summary text.
14849
15031
  </meta>
14850
15032
  </output>
14851
15033
 
14852
- Omit empty fact categories. Compartments must be ordered, contiguous for the ranges they cover, and non-overlapping.`, USER_OBSERVATIONS_APPENDIX = `
15034
+ Omit empty fact categories. Compartments must be ordered, contiguous for the ranges they cover, and non-overlapping.`, HISTORIAN_EDITOR_SYSTEM_PROMPT = `You are an editor refining a historian draft. The draft was produced by a first-pass historian and may contain noise \u2014 low-signal U: lines, redundant quotes across compartments, and weak preservation decisions.
15035
+
15036
+ Your job is to clean the draft without changing its structure:
15037
+
15038
+ 1. DROP low-signal U: lines:
15039
+ - Questions in any form \u2014 resolved decision goes in narrative only.
15040
+ - Pacing/agreement: "let's go", "yes", "okay", "sounds good", "I agree".
15041
+ - Pasted error output, debugging status, mid-process observations.
15042
+ - Tactical micro-direction: "now look at X", "first check Y".
15043
+
15044
+ 2. DROP cross-compartment duplicates:
15045
+ - Scan U: lines across ALL compartments in the draft.
15046
+ - If two U: lines express the same intent/decision, keep only ONE \u2014 in the compartment where the outcome is actually described.
15047
+
15048
+ 3. STRIP agreement prefixes:
15049
+ - "Yes we should X" \u2192 keep only the directive content, or drop entirely if nothing substantive remains after "Yes".
15050
+
15051
+ 4. PREFER verbatim over paraphrase:
15052
+ - If the draft rephrased a user directive into formal constraint language, restore the user's wording if available.
15053
+ - Do not invent technical specificity (file paths, function names, constants) the user did not state.
15054
+
15055
+ 5. FOLD into narrative when possible:
15056
+ - If a U: line's signal is already captured in the surrounding narrative, drop the U: line.
15057
+ - Narrative should not need the U: line to be understood.
15058
+
15059
+ 6. KEEP as U: lines ONLY:
15060
+ - Hard constraints with concrete values (thresholds, byte sizes, timeouts).
15061
+ - Explicit rejections ("X is wrong because Y", "NOT Z").
15062
+ - Implementation pivots in future-tense ("instead of A, do B").
15063
+ - Source-of-truth corrections.
15064
+
15065
+ Do NOT change:
15066
+ - Compartment titles, ranges, or ordering.
15067
+ - Narrative summary text unless it directly references a U: line you dropped (in which case integrate the signal into the narrative).
15068
+ - Facts \u2014 leave the facts section untouched.
15069
+ - <meta> section \u2014 leave messages_processed and unprocessed_from exactly as the draft has them.
15070
+
15071
+ Output the cleaned version as valid XML matching the original structure. Preserve all XML tags, compartment ranges, meta, and facts.`, USER_OBSERVATIONS_APPENDIX = `
14853
15072
 
14854
15073
  User observation rules (EXPERIMENTAL):
14855
15074
  - After outputting compartments and facts, also output a <user_observations> section.
@@ -14870,17 +15089,17 @@ var init_compartment_prompt = __esm(() => {
14870
15089
  });
14871
15090
 
14872
15091
  // src/shared/opencode-config-dir.ts
14873
- import { homedir as homedir2 } from "os";
14874
- import { join as join3, resolve } from "path";
15092
+ import { homedir as homedir3 } from "os";
15093
+ import { join as join3, resolve as resolve2 } from "path";
14875
15094
  function getCliConfigDir() {
14876
15095
  const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim();
14877
15096
  if (envConfigDir) {
14878
- return resolve(envConfigDir);
15097
+ return resolve2(envConfigDir);
14879
15098
  }
14880
15099
  if (process.platform === "win32") {
14881
- return join3(homedir2(), ".config", "opencode");
15100
+ return join3(homedir3(), ".config", "opencode");
14882
15101
  }
14883
- return join3(process.env.XDG_CONFIG_HOME || join3(homedir2(), ".config"), "opencode");
15102
+ return join3(process.env.XDG_CONFIG_HOME || join3(homedir3(), ".config"), "opencode");
14884
15103
  }
14885
15104
  function getOpenCodeConfigDir(_options) {
14886
15105
  return getCliConfigDir();
@@ -15087,12 +15306,12 @@ __export(exports_conflict_warning_hook, {
15087
15306
  sendConflictWarning: () => sendConflictWarning,
15088
15307
  cleanupConflictWarnings: () => cleanupConflictWarnings
15089
15308
  });
15090
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
15091
- import { homedir as homedir3, platform } from "os";
15309
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
15310
+ import { homedir as homedir4, platform } from "os";
15092
15311
  import { join as join5 } from "path";
15093
15312
  function getDesktopStatePath() {
15094
15313
  const os2 = platform();
15095
- const home = homedir3();
15314
+ const home = homedir4();
15096
15315
  if (os2 === "darwin") {
15097
15316
  return join5(home, "Library", "Application Support", "ai.opencode.desktop", "opencode.global.dat");
15098
15317
  }
@@ -15108,12 +15327,12 @@ function getDesktopStatePath() {
15108
15327
  }
15109
15328
  function readDesktopState(directory) {
15110
15329
  const statePath = getDesktopStatePath();
15111
- if (!statePath || !existsSync3(statePath)) {
15330
+ if (!statePath || !existsSync4(statePath)) {
15112
15331
  log(`[magic-context] conflict-warning: Desktop state file not found at ${statePath}`);
15113
15332
  return { sessionId: null, sidecarUrl: null };
15114
15333
  }
15115
15334
  try {
15116
- const raw = readFileSync3(statePath, "utf-8");
15335
+ const raw = readFileSync4(statePath, "utf-8");
15117
15336
  const state = JSON.parse(raw);
15118
15337
  let sidecarUrl = null;
15119
15338
  const serverStr = state.server;
@@ -16099,6 +16318,8 @@ var exports_read_session_db = {};
16099
16318
  __export(exports_read_session_db, {
16100
16319
  withReadOnlySessionDb: () => withReadOnlySessionDb,
16101
16320
  getRawSessionMessageCountFromDb: () => getRawSessionMessageCountFromDb,
16321
+ getMessageTimesFromOpenCodeDb: () => getMessageTimesFromOpenCodeDb,
16322
+ findLastAssistantModelFromOpenCodeDb: () => findLastAssistantModelFromOpenCodeDb,
16102
16323
  closeReadOnlySessionDb: () => closeReadOnlySessionDb
16103
16324
  });
16104
16325
  import { Database } from "bun:sqlite";
@@ -16140,6 +16361,47 @@ function getRawSessionMessageCountFromDb(db, sessionId) {
16140
16361
  AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')`).get(sessionId);
16141
16362
  return typeof row?.count === "number" ? row.count : 0;
16142
16363
  }
16364
+ function getMessageTimesFromOpenCodeDb(sessionId, messageIds) {
16365
+ const result = new Map;
16366
+ if (messageIds.length === 0)
16367
+ return result;
16368
+ try {
16369
+ withReadOnlySessionDb((db) => {
16370
+ const placeholders = messageIds.map(() => "?").join(",");
16371
+ const rows = db.prepare(`SELECT id, time_created FROM message WHERE session_id = ? AND id IN (${placeholders})`).all(sessionId, ...messageIds);
16372
+ for (const row of rows) {
16373
+ if (typeof row.id === "string" && typeof row.time_created === "number") {
16374
+ result.set(row.id, row.time_created);
16375
+ }
16376
+ }
16377
+ });
16378
+ } catch (error48) {
16379
+ log("[magic-context] failed to resolve message times from OpenCode DB:", error48);
16380
+ }
16381
+ return result;
16382
+ }
16383
+ function findLastAssistantModelFromOpenCodeDb(sessionId) {
16384
+ try {
16385
+ return withReadOnlySessionDb((db) => {
16386
+ const row = db.prepare(`SELECT json_extract(data, '$.providerID') as providerID,
16387
+ json_extract(data, '$.modelID') as modelID
16388
+ FROM message
16389
+ WHERE session_id = ?
16390
+ AND json_extract(data, '$.role') = 'assistant'
16391
+ AND json_extract(data, '$.providerID') IS NOT NULL
16392
+ AND json_extract(data, '$.modelID') IS NOT NULL
16393
+ ORDER BY time_created DESC
16394
+ LIMIT 1`).get(sessionId);
16395
+ if (!row || typeof row.providerID !== "string" || typeof row.modelID !== "string") {
16396
+ return null;
16397
+ }
16398
+ return { providerID: row.providerID, modelID: row.modelID };
16399
+ });
16400
+ } catch (error48) {
16401
+ log("[magic-context] failed to recover live model from OpenCode DB:", error48);
16402
+ return null;
16403
+ }
16404
+ }
16143
16405
  var cachedReadOnlyDb = null;
16144
16406
  var init_read_session_db = __esm(() => {
16145
16407
  init_data_path();
@@ -16203,7 +16465,7 @@ async function acquireModelLoadLock(lockPath) {
16203
16465
  log("[magic-context] embedding-load lock wait exceeded, proceeding without lock");
16204
16466
  return async () => {};
16205
16467
  }
16206
- await new Promise((resolve2) => setTimeout(resolve2, LOCK_POLL_MS));
16468
+ await new Promise((resolve3) => setTimeout(resolve3, LOCK_POLL_MS));
16207
16469
  }
16208
16470
  }
16209
16471
  }
@@ -16328,7 +16590,7 @@ class LocalEmbeddingProvider {
16328
16590
  }
16329
16591
  const delayMs = 300 * attempt + Math.floor(Math.random() * 200);
16330
16592
  log(`[magic-context] embedding model load attempt ${attempt}/${MAX_ATTEMPTS} failed transiently, retrying in ${delayMs}ms`);
16331
- await new Promise((resolve2) => setTimeout(resolve2, delayMs));
16593
+ await new Promise((resolve3) => setTimeout(resolve3, delayMs));
16332
16594
  }
16333
16595
  }
16334
16596
  if (this.pipeline) {
@@ -16692,14 +16954,6 @@ function getTotalDepthStatement(db) {
16692
16954
  }
16693
16955
  return stmt;
16694
16956
  }
16695
- function getMaxDepthStatement(db) {
16696
- let stmt = maxDepthStatements.get(db);
16697
- if (!stmt) {
16698
- stmt = db.prepare("SELECT COALESCE(MAX(depth), 0) AS max_depth FROM compression_depth WHERE session_id = ?");
16699
- maxDepthStatements.set(db, stmt);
16700
- }
16701
- return stmt;
16702
- }
16703
16957
  function getClearDepthStatement(db) {
16704
16958
  let stmt = clearDepthStatements.get(db);
16705
16959
  if (!stmt) {
@@ -16728,13 +16982,15 @@ function getAverageCompressionDepth(db, sessionId, startOrdinal, endOrdinal) {
16728
16982
  const messageCount = endOrdinal - startOrdinal + 1;
16729
16983
  return totalDepth / messageCount;
16730
16984
  }
16731
- function getMaxCompressionDepth(db, sessionId) {
16732
- const row = getMaxDepthStatement(db).get(sessionId);
16733
- return typeof row?.max_depth === "number" ? row.max_depth : 0;
16734
- }
16735
16985
  function clearCompressionDepth(db, sessionId) {
16736
16986
  getClearDepthStatement(db).run(sessionId);
16737
16987
  }
16988
+ function clearCompressionDepthRange(db, sessionId, startOrdinal, endOrdinal) {
16989
+ if (endOrdinal < startOrdinal) {
16990
+ return;
16991
+ }
16992
+ db.prepare("DELETE FROM compression_depth WHERE session_id = ? AND message_ordinal BETWEEN ? AND ?").run(sessionId, startOrdinal, endOrdinal);
16993
+ }
16738
16994
  var incrementDepthStatements, totalDepthStatements, maxDepthStatements, clearDepthStatements;
16739
16995
  var init_compression_depth_storage = __esm(() => {
16740
16996
  incrementDepthStatements = new WeakMap;
@@ -149526,6 +149782,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
149526
149782
  const startOrdinal = Math.max(1, offset);
149527
149783
  const lines = [];
149528
149784
  const lineMeta = [];
149785
+ const flushedToolOnlyBlocks = [];
149529
149786
  let totalTokens = 0;
149530
149787
  let messagesProcessed = 0;
149531
149788
  let lastOrdinal = startOrdinal - 1;
@@ -149555,6 +149812,12 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
149555
149812
  lines.push(blockText);
149556
149813
  lineMeta.push(...currentBlock.meta);
149557
149814
  totalTokens += blockTokens;
149815
+ if (currentBlock.isToolOnly) {
149816
+ flushedToolOnlyBlocks.push({
149817
+ start: currentBlock.startOrdinal,
149818
+ end: currentBlock.endOrdinal
149819
+ });
149820
+ }
149558
149821
  currentBlock = null;
149559
149822
  return true;
149560
149823
  }
@@ -149585,7 +149848,8 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
149585
149848
  endOrdinal: msg.ordinal,
149586
149849
  parts: [tcText],
149587
149850
  meta: [...pendingNoiseMeta, meta3],
149588
- commitHashes: []
149851
+ commitHashes: [],
149852
+ isToolOnly: true
149589
149853
  };
149590
149854
  pendingNoiseMeta = [];
149591
149855
  }
@@ -149601,11 +149865,14 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
149601
149865
  pendingNoiseMeta.push(meta3);
149602
149866
  continue;
149603
149867
  }
149868
+ const msgHasNarrative = textParts.length > 0;
149604
149869
  if (currentBlock && currentBlock.role === role) {
149605
149870
  currentBlock.endOrdinal = msg.ordinal;
149606
149871
  currentBlock.parts.push(text);
149607
149872
  currentBlock.meta.push(...pendingNoiseMeta, meta3);
149608
149873
  currentBlock.commitHashes = mergeCommitHashes(currentBlock.commitHashes, compacted.commitHashes);
149874
+ if (msgHasNarrative)
149875
+ currentBlock.isToolOnly = false;
149609
149876
  pendingNoiseMeta = [];
149610
149877
  continue;
149611
149878
  }
@@ -149617,11 +149884,21 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
149617
149884
  endOrdinal: msg.ordinal,
149618
149885
  parts: [text],
149619
149886
  meta: [...pendingNoiseMeta, meta3],
149620
- commitHashes: [...compacted.commitHashes]
149887
+ commitHashes: [...compacted.commitHashes],
149888
+ isToolOnly: !msgHasNarrative
149621
149889
  };
149622
149890
  pendingNoiseMeta = [];
149623
149891
  }
149624
149892
  flushCurrentBlock();
149893
+ const toolOnlyRanges = [];
149894
+ for (const range of flushedToolOnlyBlocks) {
149895
+ const last = toolOnlyRanges[toolOnlyRanges.length - 1];
149896
+ if (last && range.start === last.end + 1) {
149897
+ last.end = range.end;
149898
+ } else {
149899
+ toolOnlyRanges.push({ start: range.start, end: range.end });
149900
+ }
149901
+ }
149625
149902
  return {
149626
149903
  startIndex: startOrdinal,
149627
149904
  endIndex: lastOrdinal,
@@ -149633,7 +149910,8 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
149633
149910
  text: lines.join(`
149634
149911
  `),
149635
149912
  lines: lineMeta,
149636
- commitClusterCount: commitClusters
149913
+ commitClusterCount: commitClusters,
149914
+ toolOnlyRanges
149637
149915
  };
149638
149916
  }
149639
149917
  var activeRawMessageCache = null, PROTECTED_TAIL_USER_TURNS = 5;
@@ -150211,6 +150489,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
150211
150489
  ensureColumn(db, "session_meta", "key_files", "TEXT DEFAULT ''");
150212
150490
  ensureColumn(db, "session_meta", "conversation_tokens", "INTEGER DEFAULT 0");
150213
150491
  ensureColumn(db, "session_meta", "tool_call_tokens", "INTEGER DEFAULT 0");
150492
+ ensureColumn(db, "session_meta", "recomp_partial_range_start", "INTEGER DEFAULT 0");
150493
+ ensureColumn(db, "session_meta", "recomp_partial_range_end", "INTEGER DEFAULT 0");
150214
150494
  healNullTextColumns(db);
150215
150495
  healNullIntegerColumns(db);
150216
150496
  }
@@ -150950,8 +151230,8 @@ var init_storage = __esm(() => {
150950
151230
 
150951
151231
  // src/shared/models-dev-cache.ts
150952
151232
  import { createHash } from "crypto";
150953
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
150954
- import { homedir as homedir5, platform as platform2 } from "os";
151233
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
151234
+ import { homedir as homedir6, platform as platform2 } from "os";
150955
151235
  import { join as join11 } from "path";
150956
151236
  function hashFast(input) {
150957
151237
  return createHash("sha1").update(input).digest("hex");
@@ -150966,9 +151246,9 @@ function getModelsJsonPath() {
150966
151246
  if (xdgCache) {
150967
151247
  cacheBase = xdgCache;
150968
151248
  } else if (os3 === "win32") {
150969
- cacheBase = process.env.LOCALAPPDATA ?? join11(homedir5(), "AppData", "Local");
151249
+ cacheBase = process.env.LOCALAPPDATA ?? join11(homedir6(), "AppData", "Local");
150970
151250
  } else {
150971
- cacheBase = join11(homedir5(), ".cache");
151251
+ cacheBase = join11(homedir6(), ".cache");
150972
151252
  }
150973
151253
  const source = process.env.OPENCODE_MODELS_URL?.trim();
150974
151254
  const filename = source && source !== "https://models.dev" ? `models-${hashFast(source)}.json` : "models.json";
@@ -150976,12 +151256,12 @@ function getModelsJsonPath() {
150976
151256
  }
150977
151257
  function getOpencodeConfigPath() {
150978
151258
  const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
150979
- const configDir = envDir ? envDir : platform2() === "win32" ? join11(homedir5(), ".config", "opencode") : join11(process.env.XDG_CONFIG_HOME || join11(homedir5(), ".config"), "opencode");
151259
+ const configDir = envDir ? envDir : platform2() === "win32" ? join11(homedir6(), ".config", "opencode") : join11(process.env.XDG_CONFIG_HOME || join11(homedir6(), ".config"), "opencode");
150980
151260
  const jsonc = join11(configDir, "opencode.jsonc");
150981
- if (existsSync5(jsonc))
151261
+ if (existsSync6(jsonc))
150982
151262
  return jsonc;
150983
151263
  const json2 = join11(configDir, "opencode.json");
150984
- if (existsSync5(json2))
151264
+ if (existsSync6(json2))
150985
151265
  return json2;
150986
151266
  return null;
150987
151267
  }
@@ -150999,9 +151279,9 @@ function loadModelsDevLimitsFromFile() {
150999
151279
  const modelsJsonPath = getModelsJsonPath();
151000
151280
  let fileFound = false;
151001
151281
  try {
151002
- if (existsSync5(modelsJsonPath)) {
151282
+ if (existsSync6(modelsJsonPath)) {
151003
151283
  fileFound = true;
151004
- const raw = readFileSync4(modelsJsonPath, "utf-8");
151284
+ const raw = readFileSync5(modelsJsonPath, "utf-8");
151005
151285
  const data = JSON.parse(raw);
151006
151286
  for (const [providerId, provider2] of Object.entries(data)) {
151007
151287
  if (!provider2?.models || typeof provider2.models !== "object")
@@ -151025,8 +151305,8 @@ function loadModelsDevLimitsFromFile() {
151025
151305
  }
151026
151306
  try {
151027
151307
  const configPath = getOpencodeConfigPath();
151028
- if (configPath && existsSync5(configPath)) {
151029
- let raw = readFileSync4(configPath, "utf-8");
151308
+ if (configPath && existsSync6(configPath)) {
151309
+ let raw = readFileSync5(configPath, "utf-8");
151030
151310
  raw = raw.replace(/"(?:[^"\\]|\\.)*"|\/\/.*$/gm, (match) => match.startsWith('"') ? match : "");
151031
151311
  const config2 = JSON.parse(raw);
151032
151312
  if (config2.provider && typeof config2.provider === "object") {
@@ -151069,9 +151349,12 @@ async function refreshModelLimitsFromApi(client) {
151069
151349
  }
151070
151350
  }
151071
151351
  }
151352
+ const previousSize = apiCache?.size ?? null;
151072
151353
  apiCache = map2;
151073
151354
  apiLoadedAt = Date.now();
151074
- sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model limits`);
151355
+ if (previousSize === null || previousSize !== map2.size) {
151356
+ sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model limits${previousSize !== null ? ` (was ${previousSize})` : ""}`);
151357
+ }
151075
151358
  } catch (error48) {
151076
151359
  sessionLog("global", "models-dev-cache: API refresh failed:", error48 instanceof Error ? error48.message : String(error48));
151077
151360
  }
@@ -151162,608 +151445,6 @@ var init_rpc_notifications = __esm(() => {
151162
151445
  queue2 = [];
151163
151446
  });
151164
151447
 
151165
- // src/hooks/magic-context/send-session-notification.ts
151166
- var exports_send_session_notification = {};
151167
- __export(exports_send_session_notification, {
151168
- sendUserPrompt: () => sendUserPrompt,
151169
- sendIgnoredMessage: () => sendIgnoredMessage
151170
- });
151171
- function hasNotificationSessionClient(client) {
151172
- if (client === null || typeof client !== "object")
151173
- return false;
151174
- const candidate = client;
151175
- if (candidate.session === undefined)
151176
- return true;
151177
- if (candidate.session === null || typeof candidate.session !== "object")
151178
- return false;
151179
- const session = candidate.session;
151180
- return (session.prompt === undefined || typeof session.prompt === "function") && (session.promptAsync === undefined || typeof session.promptAsync === "function");
151181
- }
151182
- function inferToastVariant(text) {
151183
- const lower = text.toLowerCase();
151184
- if (lower.includes("error") || lower.includes("failed") || lower.includes("alert"))
151185
- return "error";
151186
- if (lower.includes("warning") || lower.includes("\u26A0"))
151187
- return "warning";
151188
- if (lower.includes("complete") || lower.includes("success") || lower.includes("\u2713") || lower.includes("finished"))
151189
- return "success";
151190
- return "info";
151191
- }
151192
- function extractToastTitle(text) {
151193
- const headingMatch = text.match(/^#+\s+(.+)/m);
151194
- if (headingMatch)
151195
- return headingMatch[1].trim();
151196
- const firstLine = text.split(`
151197
- `)[0].trim();
151198
- if (firstLine.length <= 80)
151199
- return firstLine;
151200
- return "Magic Context";
151201
- }
151202
- async function sendIgnoredMessage(client, sessionId, text, params) {
151203
- const { isTuiConnected: checkTui } = await Promise.resolve().then(() => (init_rpc_notifications(), exports_rpc_notifications));
151204
- if (checkTui()) {
151205
- try {
151206
- const c2 = client;
151207
- const tui = c2?.tui;
151208
- if (typeof tui?.showToast === "function") {
151209
- const tuiClient = tui;
151210
- await tuiClient.showToast({
151211
- body: {
151212
- title: extractToastTitle(text),
151213
- message: text.length > 200 ? `${text.slice(0, 200)}\u2026` : text,
151214
- variant: inferToastVariant(text),
151215
- duration: 5000
151216
- }
151217
- }).catch(() => {});
151218
- return;
151219
- }
151220
- } catch {}
151221
- }
151222
- const agent = params.agent || undefined;
151223
- const variant = params.variant || undefined;
151224
- const model = params.providerId && params.modelId ? {
151225
- providerID: params.providerId,
151226
- modelID: params.modelId
151227
- } : undefined;
151228
- if (!hasNotificationSessionClient(client)) {
151229
- sessionLog(sessionId, "session prompt API unavailable for notification");
151230
- return;
151231
- }
151232
- const c = client;
151233
- const input = {
151234
- path: { id: sessionId },
151235
- body: {
151236
- noReply: true,
151237
- agent,
151238
- model,
151239
- variant,
151240
- parts: [
151241
- {
151242
- type: "text",
151243
- text,
151244
- ignored: true
151245
- }
151246
- ]
151247
- }
151248
- };
151249
- try {
151250
- if (typeof c.session?.prompt === "function") {
151251
- await Promise.resolve(c.session.prompt(input));
151252
- } else if (typeof c.session?.promptAsync === "function") {
151253
- await c.session.promptAsync(input);
151254
- } else {
151255
- sessionLog(sessionId, "session prompt API unavailable for notification");
151256
- }
151257
- } catch (error48) {
151258
- const msg = getErrorMessage(error48);
151259
- sessionLog(sessionId, "failed to send notification:", msg);
151260
- }
151261
- }
151262
- async function sendUserPrompt(client, sessionId, text) {
151263
- if (!hasNotificationSessionClient(client)) {
151264
- sessionLog(sessionId, "session prompt API unavailable for user prompt");
151265
- return;
151266
- }
151267
- const c = client;
151268
- const input = {
151269
- path: { id: sessionId },
151270
- body: {
151271
- parts: [{ type: "text", text }]
151272
- }
151273
- };
151274
- try {
151275
- if (typeof c.session?.promptAsync === "function") {
151276
- await c.session.promptAsync(input);
151277
- } else if (typeof c.session?.prompt === "function") {
151278
- await Promise.resolve(c.session.prompt(input));
151279
- } else {
151280
- sessionLog(sessionId, "session prompt API unavailable for user prompt");
151281
- }
151282
- } catch (error48) {
151283
- const msg = getErrorMessage(error48);
151284
- sessionLog(sessionId, "failed to send user prompt:", msg);
151285
- }
151286
- }
151287
- var init_send_session_notification = __esm(() => {
151288
- init_logger();
151289
- });
151290
-
151291
- // src/hooks/magic-context/derive-budgets.ts
151292
- var exports_derive_budgets = {};
151293
- __export(exports_derive_budgets, {
151294
- resolveHistorianContextLimit: () => resolveHistorianContextLimit,
151295
- deriveTriggerBudget: () => deriveTriggerBudget,
151296
- deriveHistorianChunkTokens: () => deriveHistorianChunkTokens
151297
- });
151298
- function deriveTriggerBudget(mainContextLimit, executeThresholdPercentage) {
151299
- if (!Number.isFinite(mainContextLimit) || mainContextLimit <= 0) {
151300
- return TRIGGER_BUDGET_MIN;
151301
- }
151302
- const thresholdFraction = Math.max(0, executeThresholdPercentage) / 100;
151303
- const usable = mainContextLimit * thresholdFraction;
151304
- const derived = Math.round(usable * TRIGGER_BUDGET_PERCENTAGE);
151305
- return Math.max(TRIGGER_BUDGET_MIN, Math.min(TRIGGER_BUDGET_MAX, derived));
151306
- }
151307
- function deriveHistorianChunkTokens(historianContextLimit) {
151308
- if (!Number.isFinite(historianContextLimit) || historianContextLimit <= 0) {
151309
- return HISTORIAN_CHUNK_MIN;
151310
- }
151311
- const derived = Math.round(historianContextLimit * HISTORIAN_CHUNK_PERCENTAGE);
151312
- return Math.max(HISTORIAN_CHUNK_MIN, Math.min(HISTORIAN_CHUNK_MAX, derived));
151313
- }
151314
- function resolveHistorianContextLimit(historianModelOverride) {
151315
- if (typeof historianModelOverride === "string" && historianModelOverride.includes("/")) {
151316
- const [providerID, ...rest] = historianModelOverride.split("/");
151317
- const modelID = rest.join("/");
151318
- if (providerID && modelID) {
151319
- const limit = getModelsDevContextLimit(providerID, modelID);
151320
- if (typeof limit === "number" && limit > 0)
151321
- return limit;
151322
- }
151323
- return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
151324
- }
151325
- if (typeof historianModelOverride === "string" && historianModelOverride.trim() !== "") {
151326
- console.warn(`[magic-context] historian.model "${historianModelOverride}" lacks provider prefix ("provider/model-id"); using fallback chain for chunk-budget derivation.`);
151327
- }
151328
- const chain = AGENT_MODEL_REQUIREMENTS[HISTORIAN_AGENT]?.fallbackChain;
151329
- if (!chain || chain.length === 0)
151330
- return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
151331
- const expanded = expandFallbackChain(chain);
151332
- let minLimit;
151333
- for (const key of expanded) {
151334
- const [providerID, ...rest] = key.split("/");
151335
- const modelID = rest.join("/");
151336
- if (!providerID || !modelID)
151337
- continue;
151338
- const limit = getModelsDevContextLimit(providerID, modelID);
151339
- if (typeof limit !== "number" || limit <= 0)
151340
- continue;
151341
- if (minLimit === undefined || limit < minLimit)
151342
- minLimit = limit;
151343
- }
151344
- return minLimit ?? DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
151345
- }
151346
- var TRIGGER_BUDGET_PERCENTAGE = 0.05, TRIGGER_BUDGET_MIN = 5000, TRIGGER_BUDGET_MAX = 50000, HISTORIAN_CHUNK_PERCENTAGE = 0.25, HISTORIAN_CHUNK_MIN = 8000, HISTORIAN_CHUNK_MAX = 50000, DEFAULT_HISTORIAN_CONTEXT_FALLBACK = 128000;
151347
- var init_derive_budgets = __esm(() => {
151348
- init_model_requirements();
151349
- init_models_dev_cache();
151350
- });
151351
-
151352
- // src/features/magic-context/compaction-marker.ts
151353
- import { Database as Database4 } from "bun:sqlite";
151354
- import { join as join12 } from "path";
151355
- function randomBase62(length) {
151356
- const chars = [];
151357
- for (let i = 0;i < length; i++) {
151358
- chars.push(BASE62_CHARS[Math.floor(Math.random() * BASE62_CHARS.length)]);
151359
- }
151360
- return chars.join("");
151361
- }
151362
- function generateId(prefix, timestampMs, counter = 0n) {
151363
- const encoded = BigInt(timestampMs) * 0x1000n + counter;
151364
- const hex3 = encoded.toString(16).padStart(14, "0");
151365
- return `${prefix}_${hex3}${randomBase62(14)}`;
151366
- }
151367
- function generateMessageId(timestampMs, counter = 0n) {
151368
- return generateId("msg", timestampMs, counter);
151369
- }
151370
- function generatePartId(timestampMs, counter = 0n) {
151371
- return generateId("prt", timestampMs, counter);
151372
- }
151373
- function getOpenCodeDbPath3() {
151374
- return join12(getDataDir(), "opencode", "opencode.db");
151375
- }
151376
- function getWritableOpenCodeDb() {
151377
- const dbPath = getOpenCodeDbPath3();
151378
- if (cachedWriteDb?.path === dbPath) {
151379
- return cachedWriteDb.db;
151380
- }
151381
- if (cachedWriteDb) {
151382
- try {
151383
- cachedWriteDb.db.close(false);
151384
- } catch {}
151385
- }
151386
- const db = new Database4(dbPath);
151387
- db.exec("PRAGMA journal_mode=WAL");
151388
- db.exec("PRAGMA busy_timeout=5000");
151389
- cachedWriteDb = { path: dbPath, db };
151390
- return db;
151391
- }
151392
- function findBoundaryUserMessage(sessionId, endOrdinal) {
151393
- const db = getWritableOpenCodeDb();
151394
- const rows = db.prepare("SELECT id, time_created, data FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId);
151395
- const filtered = rows.filter((row) => {
151396
- try {
151397
- const info = JSON.parse(row.data);
151398
- return !(info.summary === true && info.finish === "stop");
151399
- } catch {
151400
- return true;
151401
- }
151402
- });
151403
- let bestMatch = null;
151404
- for (let i = 0;i < filtered.length && i < endOrdinal; i++) {
151405
- const row = filtered[i];
151406
- try {
151407
- const info = JSON.parse(row.data);
151408
- if (info.role === "user") {
151409
- bestMatch = { id: row.id, timeCreated: row.time_created };
151410
- }
151411
- } catch {}
151412
- }
151413
- return bestMatch;
151414
- }
151415
- function injectCompactionMarker(args) {
151416
- const boundary = findBoundaryUserMessage(args.sessionId, args.endOrdinal);
151417
- if (!boundary) {
151418
- log(`[magic-context] compaction-marker: no user message found at or before ordinal ${args.endOrdinal}`);
151419
- return null;
151420
- }
151421
- const db = getWritableOpenCodeDb();
151422
- const boundaryTime = boundary.timeCreated;
151423
- const summaryMsgId = generateMessageId(boundaryTime + 1, 1n);
151424
- const compactionPartId = generatePartId(boundaryTime, 1n);
151425
- const summaryPartId = generatePartId(boundaryTime + 1, 2n);
151426
- const summaryMsgData = JSON.stringify({
151427
- role: "assistant",
151428
- parentID: boundary.id,
151429
- summary: true,
151430
- finish: "stop",
151431
- mode: "compaction",
151432
- agent: "compaction",
151433
- path: { cwd: args.directory, root: args.directory },
151434
- cost: 0,
151435
- tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
151436
- modelID: "magic-context",
151437
- providerID: "magic-context",
151438
- time: { created: boundaryTime + 1 }
151439
- });
151440
- try {
151441
- db.transaction(() => {
151442
- db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(compactionPartId, boundary.id, args.sessionId, boundaryTime, boundaryTime, '{"type":"compaction","auto":true}');
151443
- db.prepare("INSERT INTO message (id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?)").run(summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, summaryMsgData);
151444
- db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(summaryPartId, summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, JSON.stringify({ type: "text", text: args.summaryText }));
151445
- })();
151446
- log(`[magic-context] compaction-marker: injected boundary at user msg ${boundary.id} (ordinal ~${args.endOrdinal}), summary msg ${summaryMsgId}`);
151447
- return {
151448
- boundaryMessageId: boundary.id,
151449
- summaryMessageId: summaryMsgId,
151450
- compactionPartId,
151451
- summaryPartId
151452
- };
151453
- } catch (error48) {
151454
- log(`[magic-context] compaction-marker: injection failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
151455
- return null;
151456
- }
151457
- }
151458
- function removeCompactionMarker(state) {
151459
- try {
151460
- const db = getWritableOpenCodeDb();
151461
- db.transaction(() => {
151462
- db.prepare("DELETE FROM part WHERE id = ?").run(state.summaryPartId);
151463
- db.prepare("DELETE FROM message WHERE id = ?").run(state.summaryMessageId);
151464
- db.prepare("DELETE FROM part WHERE id = ?").run(state.compactionPartId);
151465
- })();
151466
- return true;
151467
- } catch (error48) {
151468
- log(`[magic-context] compaction-marker: removal failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
151469
- return false;
151470
- }
151471
- }
151472
- var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", cachedWriteDb = null;
151473
- var init_compaction_marker = __esm(() => {
151474
- init_data_path();
151475
- init_logger();
151476
- });
151477
-
151478
- // src/hooks/magic-context/compaction-marker-manager.ts
151479
- function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, directory) {
151480
- const existing = getPersistedCompactionMarkerState(db, sessionId);
151481
- if (existing) {
151482
- if (existing.boundaryOrdinal === lastCompartmentEnd) {
151483
- return;
151484
- }
151485
- try {
151486
- removeCompactionMarker(existing);
151487
- setPersistedCompactionMarkerState(db, sessionId, null);
151488
- sessionLog(sessionId, `compaction-marker: removed old boundary at ordinal ${existing.boundaryOrdinal}, moving to ${lastCompartmentEnd}`);
151489
- } catch (error48) {
151490
- sessionLog(sessionId, `compaction-marker: failed to remove old boundary at ordinal ${existing.boundaryOrdinal}, proceeding with new injection:`, error48);
151491
- }
151492
- }
151493
- const result = injectCompactionMarker({
151494
- sessionId,
151495
- endOrdinal: lastCompartmentEnd,
151496
- summaryText: MARKER_SUMMARY_TEXT,
151497
- directory: directory ?? process.cwd()
151498
- });
151499
- if (result) {
151500
- setPersistedCompactionMarkerState(db, sessionId, {
151501
- ...result,
151502
- boundaryOrdinal: lastCompartmentEnd
151503
- });
151504
- sessionLog(sessionId, `compaction-marker: injected at ordinal ${lastCompartmentEnd}, boundary user msg ${result.boundaryMessageId}`);
151505
- }
151506
- }
151507
- function removeCompactionMarkerForSession(db, sessionId) {
151508
- const existing = getPersistedCompactionMarkerState(db, sessionId);
151509
- if (existing) {
151510
- try {
151511
- removeCompactionMarker(existing);
151512
- setPersistedCompactionMarkerState(db, sessionId, null);
151513
- sessionLog(sessionId, "compaction-marker: removed on session cleanup");
151514
- } catch (error48) {
151515
- setPersistedCompactionMarkerState(db, sessionId, null);
151516
- sessionLog(sessionId, "compaction-marker: removal failed during session cleanup, cleared persisted state:", error48);
151517
- }
151518
- }
151519
- }
151520
- var MARKER_SUMMARY_TEXT = "[Compacted by magic-context \u2014 session history is managed by the plugin]";
151521
- var init_compaction_marker_manager = __esm(() => {
151522
- init_compaction_marker();
151523
- init_storage_meta_persisted();
151524
- init_logger();
151525
- });
151526
-
151527
- // src/hooks/magic-context/note-nudger.ts
151528
- function getPersistedNoteNudgeDeliveredAt(_db, sessionId) {
151529
- return lastDeliveredAt.get(sessionId) ?? 0;
151530
- }
151531
- function recordNoteNudgeDeliveryTime(sessionId) {
151532
- lastDeliveredAt.set(sessionId, Date.now());
151533
- }
151534
- function onNoteTrigger(db, sessionId, trigger) {
151535
- setPersistedNoteNudgeTrigger(db, sessionId);
151536
- sessionLog(sessionId, `note-nudge: trigger fired (${trigger}), triggerPending=true`);
151537
- }
151538
- function peekNoteNudgeText(db, sessionId, currentUserMessageId, projectIdentity) {
151539
- const state = getPersistedNoteNudge(db, sessionId);
151540
- if (!state.triggerPending)
151541
- return null;
151542
- if (!state.triggerMessageId && currentUserMessageId) {
151543
- setPersistedNoteNudgeTriggerMessageId(db, sessionId, currentUserMessageId);
151544
- state.triggerMessageId = currentUserMessageId;
151545
- }
151546
- if (state.triggerMessageId && currentUserMessageId && state.triggerMessageId === currentUserMessageId) {
151547
- sessionLog(sessionId, `note-nudge: deferring \u2014 current user message ${currentUserMessageId} is same as trigger-time message`);
151548
- return null;
151549
- }
151550
- const deliveredAt = getPersistedNoteNudgeDeliveredAt(db, sessionId);
151551
- if (deliveredAt > 0 && Date.now() - deliveredAt < NOTE_NUDGE_COOLDOWN_MS) {
151552
- sessionLog(sessionId, `note-nudge: suppressing \u2014 last delivered ${Math.round((Date.now() - deliveredAt) / 1000)}s ago (cooldown ${NOTE_NUDGE_COOLDOWN_MS / 60000}m)`);
151553
- clearPersistedNoteNudge(db, sessionId);
151554
- return null;
151555
- }
151556
- const notes = getSessionNotes(db, sessionId);
151557
- const readySmartCount = projectIdentity ? getReadySmartNotes(db, projectIdentity).length : 0;
151558
- const totalCount = notes.length + readySmartCount;
151559
- if (totalCount === 0) {
151560
- sessionLog(sessionId, "note-nudge: triggerPending but no notes found, skipping");
151561
- clearPersistedNoteNudge(db, sessionId);
151562
- return null;
151563
- }
151564
- const parts = [];
151565
- if (notes.length > 0) {
151566
- parts.push(`${notes.length} deferred note${notes.length === 1 ? "" : "s"}`);
151567
- }
151568
- if (readySmartCount > 0) {
151569
- parts.push(`${readySmartCount} ready smart note${readySmartCount === 1 ? "" : "s"}`);
151570
- }
151571
- sessionLog(sessionId, `note-nudge: delivering nudge for ${parts.join(" and ")}`);
151572
- return `You have ${parts.join(" and ")}. Review with ctx_note read \u2014 some may be actionable now.`;
151573
- }
151574
- function markNoteNudgeDelivered(db, sessionId, text, messageId) {
151575
- setPersistedDeliveredNoteNudge(db, sessionId, messageId ? text : "", messageId ?? "");
151576
- recordNoteNudgeDeliveryTime(sessionId);
151577
- sessionLog(sessionId, messageId ? `note-nudge: marked delivered, sticky anchor=${messageId}` : "note-nudge: marked delivered without anchor");
151578
- }
151579
- function getStickyNoteNudge(db, sessionId) {
151580
- const state = getPersistedNoteNudge(db, sessionId);
151581
- if (!state.stickyText || !state.stickyMessageId)
151582
- return null;
151583
- return { text: state.stickyText, messageId: state.stickyMessageId };
151584
- }
151585
- function clearNoteNudgeState(db, sessionId, options) {
151586
- if (options?.persist !== false) {
151587
- clearPersistedNoteNudge(db, sessionId);
151588
- }
151589
- lastDeliveredAt.delete(sessionId);
151590
- }
151591
- var NOTE_NUDGE_COOLDOWN_MS, lastDeliveredAt;
151592
- var init_note_nudger = __esm(() => {
151593
- init_storage_meta_persisted();
151594
- init_storage_notes();
151595
- init_logger();
151596
- NOTE_NUDGE_COOLDOWN_MS = 15 * 60 * 1000;
151597
- lastDeliveredAt = new Map;
151598
- });
151599
-
151600
- // src/features/magic-context/memory/constants.ts
151601
- var PROMOTABLE_CATEGORIES, CATEGORY_PRIORITY, CATEGORY_DEFAULT_TTL;
151602
- var init_constants = __esm(() => {
151603
- PROMOTABLE_CATEGORIES = [
151604
- "ARCHITECTURE_DECISIONS",
151605
- "CONSTRAINTS",
151606
- "CONFIG_DEFAULTS",
151607
- "NAMING",
151608
- "USER_PREFERENCES",
151609
- "USER_DIRECTIVES",
151610
- "ENVIRONMENT",
151611
- "WORKFLOW_RULES",
151612
- "KNOWN_ISSUES"
151613
- ];
151614
- CATEGORY_PRIORITY = [
151615
- "USER_DIRECTIVES",
151616
- "USER_PREFERENCES",
151617
- "NAMING",
151618
- "CONFIG_DEFAULTS",
151619
- "CONSTRAINTS",
151620
- "ARCHITECTURE_DECISIONS",
151621
- "ENVIRONMENT",
151622
- "WORKFLOW_RULES",
151623
- "KNOWN_ISSUES"
151624
- ];
151625
- CATEGORY_DEFAULT_TTL = {
151626
- WORKFLOW_RULES: 90 * 24 * 60 * 60 * 1000,
151627
- KNOWN_ISSUES: 30 * 24 * 60 * 60 * 1000
151628
- };
151629
- });
151630
-
151631
- // src/features/magic-context/memory/embedding-backfill.ts
151632
- async function ensureMemoryEmbeddings(args) {
151633
- if (!isEmbeddingEnabled()) {
151634
- return args.existingEmbeddings;
151635
- }
151636
- const missingMemories = args.memories.filter((memory) => !args.existingEmbeddings.has(memory.id));
151637
- if (missingMemories.length === 0) {
151638
- return args.existingEmbeddings;
151639
- }
151640
- try {
151641
- const embeddings = await embedBatch(missingMemories.map((memory) => memory.content));
151642
- const modelId = getEmbeddingModelId();
151643
- const staged = new Map;
151644
- args.db.transaction(() => {
151645
- for (const [index, memory] of missingMemories.entries()) {
151646
- const embedding = embeddings[index];
151647
- if (!embedding) {
151648
- continue;
151649
- }
151650
- saveEmbedding(args.db, memory.id, embedding, modelId);
151651
- staged.set(memory.id, embedding);
151652
- }
151653
- })();
151654
- for (const [id, embedding] of staged) {
151655
- args.existingEmbeddings.set(id, embedding);
151656
- }
151657
- } catch (error48) {
151658
- log("[magic-context] failed to backfill memory embeddings:", error48);
151659
- }
151660
- return args.existingEmbeddings;
151661
- }
151662
- var init_embedding_backfill = __esm(() => {
151663
- init_logger();
151664
- init_embedding();
151665
- init_storage_memory_embeddings();
151666
- });
151667
-
151668
- // src/features/magic-context/memory/promotion.ts
151669
- function isPromotableCategory(category) {
151670
- return PROMOTABLE_CATEGORIES.some((promotableCategory) => promotableCategory === category);
151671
- }
151672
- function resolveExpiresAt(category) {
151673
- const ttl = CATEGORY_DEFAULT_TTL[category];
151674
- return ttl === undefined ? null : Date.now() + ttl;
151675
- }
151676
- function promoteSessionFactsToMemory(db, sessionId, projectPath, facts) {
151677
- for (const fact of facts) {
151678
- if (!isPromotableCategory(fact.category)) {
151679
- continue;
151680
- }
151681
- try {
151682
- const normalizedHash = computeNormalizedHash(fact.content);
151683
- const existingMemory = getMemoryByHash(db, projectPath, fact.category, normalizedHash);
151684
- if (existingMemory) {
151685
- updateMemorySeenCount(db, existingMemory.id);
151686
- continue;
151687
- }
151688
- const memoryInput = {
151689
- projectPath,
151690
- category: fact.category,
151691
- content: fact.content,
151692
- sourceSessionId: sessionId,
151693
- sourceType: "historian",
151694
- expiresAt: resolveExpiresAt(fact.category)
151695
- };
151696
- const memory = insertMemory(db, memoryInput);
151697
- embedAndStoreMemory(db, sessionId, memory.id, memory.content);
151698
- } catch (error48) {
151699
- sessionLog(sessionId, `memory promotion failed for fact "${fact.content.slice(0, 60)}":`, error48);
151700
- }
151701
- }
151702
- }
151703
- async function embedAndStoreMemory(db, sessionId, memoryId, content) {
151704
- try {
151705
- const embedding = await embedText(content);
151706
- if (embedding) {
151707
- saveEmbedding(db, memoryId, embedding, getEmbeddingModelId());
151708
- }
151709
- } catch (error48) {
151710
- sessionLog(sessionId, `memory embedding failed for memory ${memoryId}:`, error48);
151711
- }
151712
- }
151713
- var init_promotion = __esm(() => {
151714
- init_logger();
151715
- init_constants();
151716
- init_embedding();
151717
- init_storage_memory();
151718
- init_storage_memory_embeddings();
151719
- });
151720
-
151721
- // src/features/magic-context/memory/storage-memory-fts.ts
151722
- function getSearchStatement(db) {
151723
- let stmt = searchStatements.get(db);
151724
- if (!stmt) {
151725
- const selectColumns = Object.entries(COLUMN_MAP).map(([property, column]) => `memories.${column} AS ${property}`).join(", ");
151726
- stmt = db.prepare(`SELECT ${selectColumns} FROM memories_fts INNER JOIN memories ON memories.id = memories_fts.rowid WHERE memories.project_path = ? AND memories.status IN ('active', 'permanent') AND (memories.expires_at IS NULL OR memories.expires_at > ?) AND memories_fts MATCH ? ORDER BY bm25(memories_fts), memories.updated_at DESC, memories.id ASC LIMIT ?`);
151727
- searchStatements.set(db, stmt);
151728
- }
151729
- return stmt;
151730
- }
151731
- function sanitizeFtsQuery(query) {
151732
- const tokens = query.split(/\s+/).filter((token) => token.length > 0);
151733
- if (tokens.length === 0)
151734
- return "";
151735
- return tokens.map((token) => `"${token.replace(/"/g, '""')}"`).join(" ");
151736
- }
151737
- function searchMemoriesFTS(db, projectPath, query, limit = DEFAULT_SEARCH_LIMIT) {
151738
- const trimmedQuery = query.trim();
151739
- if (trimmedQuery.length === 0 || limit <= 0) {
151740
- return [];
151741
- }
151742
- const sanitized = sanitizeFtsQuery(trimmedQuery);
151743
- if (sanitized.length === 0) {
151744
- return [];
151745
- }
151746
- const rows = getSearchStatement(db).all(projectPath, Date.now(), sanitized, limit).filter(isMemoryRow);
151747
- return rows.map(toMemory);
151748
- }
151749
- var DEFAULT_SEARCH_LIMIT = 10, searchStatements;
151750
- var init_storage_memory_fts = __esm(() => {
151751
- init_storage_memory();
151752
- searchStatements = new WeakMap;
151753
- });
151754
- // src/features/magic-context/memory/index.ts
151755
- var init_memory = __esm(() => {
151756
- init_project_identity();
151757
- init_promotion();
151758
- init_constants();
151759
- init_embedding();
151760
- init_embedding_backfill();
151761
- init_embedding_cache();
151762
- init_storage_memory();
151763
- init_storage_memory_embeddings();
151764
- init_storage_memory_fts();
151765
- });
151766
-
151767
151448
  // src/hooks/magic-context/compartment-parser.ts
151768
151449
  function parseCompartmentOutput(text) {
151769
151450
  const compartments = [];
@@ -151814,290 +151495,6 @@ var init_compartment_parser = __esm(() => {
151814
151495
  USER_OBS_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
151815
151496
  });
151816
151497
 
151817
- // src/hooks/magic-context/compartment-runner-compressor.ts
151818
- async function runCompressionPassIfNeeded(deps) {
151819
- const { db, sessionId, historyBudgetTokens } = deps;
151820
- const compartments = getCompartments(db, sessionId);
151821
- if (compartments.length <= 1)
151822
- return false;
151823
- const facts = getSessionFacts(db, sessionId);
151824
- let totalTokens = 0;
151825
- for (const c of compartments) {
151826
- totalTokens += estimateTokens(`<compartment start="${c.startMessage}" end="${c.endMessage}" title="${c.title}">
151827
- ${c.content}
151828
- </compartment>
151829
- `);
151830
- }
151831
- for (const f of facts) {
151832
- totalTokens += estimateTokens(`* ${f.content}
151833
- `);
151834
- }
151835
- if (totalTokens <= historyBudgetTokens) {
151836
- sessionLog(sessionId, `compressor: history block ~${totalTokens} tokens within budget ${historyBudgetTokens}, skipping`);
151837
- return false;
151838
- }
151839
- const overage = totalTokens - historyBudgetTokens;
151840
- sessionLog(sessionId, `compressor: history block ~${totalTokens} tokens exceeds budget ${historyBudgetTokens} by ~${overage} tokens`);
151841
- const maxDepth = getMaxCompressionDepth(db, sessionId);
151842
- const scoredCompartments = compartments.map((compartment, index) => {
151843
- const tokenEstimate = estimateTokens(`<compartment start="${compartment.startMessage}" end="${compartment.endMessage}" title="${compartment.title}">
151844
- ${compartment.content}
151845
- </compartment>
151846
- `);
151847
- const averageDepth = getAverageCompressionDepth(db, sessionId, compartment.startMessage, compartment.endMessage);
151848
- const normalizedAge = compartments.length > 1 ? 1 - index / (compartments.length - 1) : 1;
151849
- const normalizedDepth = maxDepth > 0 ? 1 - averageDepth / maxDepth : 1;
151850
- const score = 0.7 * normalizedAge + 0.3 * normalizedDepth;
151851
- return {
151852
- compartment,
151853
- index,
151854
- tokenEstimate,
151855
- averageDepth,
151856
- score
151857
- };
151858
- });
151859
- const sortedByScore = [...scoredCompartments].sort((left, right) => right.score - left.score || left.index - right.index);
151860
- const targetSelectionTokens = overage * 2;
151861
- let selectedCandidateTokens = 0;
151862
- const selectedCandidates = [];
151863
- for (const compartment of sortedByScore) {
151864
- if (selectedCandidateTokens >= targetSelectionTokens)
151865
- break;
151866
- selectedCandidates.push(compartment);
151867
- selectedCandidateTokens += compartment.tokenEstimate;
151868
- }
151869
- if (selectedCandidates.length < 2) {
151870
- sessionLog(sessionId, "compressor: not enough compartments to compress, skipping");
151871
- return false;
151872
- }
151873
- const selectedStartIndex = Math.min(...selectedCandidates.map((compartment) => compartment.index));
151874
- const selectedEndIndex = Math.max(...selectedCandidates.map((compartment) => compartment.index));
151875
- let selectedScoredCompartments = scoredCompartments.slice(selectedStartIndex, selectedEndIndex + 1);
151876
- const expandedTokens = selectedScoredCompartments.reduce((t, c) => t + c.tokenEstimate, 0);
151877
- if (expandedTokens > selectedCandidateTokens * 3) {
151878
- const sortedByIndex = [...selectedCandidates].sort((a, b) => a.index - b.index);
151879
- let runEnd = sortedByIndex[0].index;
151880
- for (let i = 1;i < sortedByIndex.length; i++) {
151881
- if (sortedByIndex[i].index === runEnd + 1) {
151882
- runEnd = sortedByIndex[i].index;
151883
- } else {
151884
- break;
151885
- }
151886
- }
151887
- selectedScoredCompartments = scoredCompartments.slice(sortedByIndex[0].index, runEnd + 1);
151888
- sessionLog(sessionId, `compressor: contiguous expansion was ${expandedTokens} tokens (>${selectedCandidateTokens * 3}), fell back to contiguous run of ${selectedScoredCompartments.length}`);
151889
- }
151890
- if (selectedScoredCompartments.length < 2) {
151891
- sessionLog(sessionId, "compressor: not enough adjacent compartments to compress, skipping");
151892
- return false;
151893
- }
151894
- const selectedCompartments = selectedScoredCompartments.map((scoredCompartment) => scoredCompartment.compartment);
151895
- const selectedTokens = selectedScoredCompartments.reduce((total, scoredCompartment) => total + scoredCompartment.tokenEstimate, 0);
151896
- const targetTokens = Math.floor(selectedTokens / 2);
151897
- const minAverageDepth = Math.min(...selectedScoredCompartments.map((compartment) => compartment.averageDepth));
151898
- const maxAverageDepth = Math.max(...selectedScoredCompartments.map((compartment) => compartment.averageDepth));
151899
- const minScore = Math.min(...selectedScoredCompartments.map((compartment) => compartment.score));
151900
- const maxScore = Math.max(...selectedScoredCompartments.map((compartment) => compartment.score));
151901
- sessionLog(sessionId, `compressor: scored ${compartments.length} compartments, selected ${selectedCompartments.length} (avg_depth range: ${minAverageDepth.toFixed(1)}-${maxAverageDepth.toFixed(1)}, score range: ${minScore.toFixed(1)}-${maxScore.toFixed(1)})`);
151902
- const overallAverageDepth = selectedScoredCompartments.reduce((sum, c) => sum + c.averageDepth, 0) / selectedScoredCompartments.length;
151903
- const depthTier = overallAverageDepth < 2 ? "preserve U: lines" : overallAverageDepth < 3 ? "condense U: lines" : "fold U: into prose";
151904
- sessionLog(sessionId, `compressor: selected contiguous range ${selectedCompartments[0].startMessage}-${selectedCompartments[selectedCompartments.length - 1].endMessage} (~${selectedTokens} tokens), target ~${targetTokens} tokens, avg_depth=${overallAverageDepth.toFixed(1)} (${depthTier})`);
151905
- try {
151906
- const compressed = await runCompressorPass({
151907
- ...deps,
151908
- compartments: selectedCompartments,
151909
- currentTokens: selectedTokens,
151910
- targetTokens,
151911
- averageDepth: overallAverageDepth
151912
- });
151913
- if (!compressed) {
151914
- sessionLog(sessionId, "compressor: compression pass failed, keeping existing compartments");
151915
- return false;
151916
- }
151917
- const leadingCompartments = compartments.slice(0, selectedStartIndex);
151918
- const trailingCompartments = compartments.slice(selectedEndIndex + 1);
151919
- const allCompartments = [
151920
- ...leadingCompartments.map((c, i) => ({
151921
- sequence: i,
151922
- startMessage: c.startMessage,
151923
- endMessage: c.endMessage,
151924
- startMessageId: c.startMessageId,
151925
- endMessageId: c.endMessageId,
151926
- title: c.title,
151927
- content: c.content
151928
- })),
151929
- ...compressed.map((c, i) => ({
151930
- sequence: leadingCompartments.length + i,
151931
- startMessage: c.startMessage,
151932
- endMessage: c.endMessage,
151933
- startMessageId: c.startMessageId,
151934
- endMessageId: c.endMessageId,
151935
- title: c.title,
151936
- content: c.content
151937
- })),
151938
- ...trailingCompartments.map((c, i) => ({
151939
- sequence: leadingCompartments.length + compressed.length + i,
151940
- startMessage: c.startMessage,
151941
- endMessage: c.endMessage,
151942
- startMessageId: c.startMessageId,
151943
- endMessageId: c.endMessageId,
151944
- title: c.title,
151945
- content: c.content
151946
- }))
151947
- ];
151948
- const originalStart = selectedCompartments[0].startMessage;
151949
- const originalEnd = selectedCompartments[selectedCompartments.length - 1].endMessage;
151950
- const compressedStart = compressed[0].startMessage;
151951
- const compressedEnd = compressed[compressed.length - 1].endMessage;
151952
- if (compressedStart !== originalStart || compressedEnd !== originalEnd) {
151953
- sessionLog(sessionId, `compressor: compressed range ${compressedStart}-${compressedEnd} doesn't match original ${originalStart}-${originalEnd}, aborting`);
151954
- return false;
151955
- }
151956
- for (let i = 1;i < compressed.length; i++) {
151957
- const prev = compressed[i - 1];
151958
- const curr = compressed[i];
151959
- if (curr.startMessage <= prev.endMessage) {
151960
- sessionLog(sessionId, `compressor: overlap at compartment ${i}: prev ends ${prev.endMessage}, curr starts ${curr.startMessage}, aborting`);
151961
- return false;
151962
- }
151963
- if (curr.startMessage > prev.endMessage + 1) {
151964
- sessionLog(sessionId, `compressor: gap at compartment ${i}: prev ends ${prev.endMessage}, curr starts ${curr.startMessage}, aborting`);
151965
- return false;
151966
- }
151967
- }
151968
- replaceAllCompartmentState(db, sessionId, allCompartments, facts.map((f) => ({ category: f.category, content: f.content })));
151969
- incrementCompressionDepth(db, sessionId, originalStart, originalEnd);
151970
- sessionLog(sessionId, `compressor: replaced ${selectedCompartments.length} compartments with ${compressed.length} compressed compartments`);
151971
- sessionLog(sessionId, `compressor: incremented compression depth for messages ${originalStart}-${originalEnd}`);
151972
- return true;
151973
- } catch (error48) {
151974
- sessionLog(sessionId, "compressor: unexpected error:", getErrorMessage(error48));
151975
- return false;
151976
- }
151977
- }
151978
- async function runCompressorPass(args) {
151979
- const {
151980
- client,
151981
- sessionId,
151982
- directory,
151983
- compartments,
151984
- currentTokens,
151985
- targetTokens,
151986
- averageDepth,
151987
- historianTimeoutMs
151988
- } = args;
151989
- const prompt = buildCompressorPrompt(compartments, currentTokens, targetTokens, averageDepth);
151990
- let agentSessionId = null;
151991
- try {
151992
- const createResponse = await client.session.create({
151993
- body: {
151994
- parentID: sessionId,
151995
- title: "magic-context-compressor"
151996
- },
151997
- query: { directory }
151998
- });
151999
- const createdSession = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
152000
- agentSessionId = typeof createdSession?.id === "string" ? createdSession.id : null;
152001
- if (!agentSessionId) {
152002
- sessionLog(sessionId, "compressor: could not create child session");
152003
- return null;
152004
- }
152005
- await promptSyncWithModelSuggestionRetry(client, {
152006
- path: { id: agentSessionId },
152007
- query: { directory },
152008
- body: {
152009
- agent: HISTORIAN_AGENT2,
152010
- parts: [{ type: "text", text: prompt }]
152011
- }
152012
- }, { timeoutMs: historianTimeoutMs ?? DEFAULT_HISTORIAN_TIMEOUT_MS });
152013
- const messagesResponse = await client.session.messages({
152014
- path: { id: agentSessionId },
152015
- query: { directory }
152016
- });
152017
- const messages = normalizeSDKResponse(messagesResponse, [], {
152018
- preferResponseOnMissingData: true
152019
- });
152020
- const result = extractLatestAssistantText(messages);
152021
- if (!result) {
152022
- sessionLog(sessionId, "compressor: historian returned no output");
152023
- return null;
152024
- }
152025
- const parsed = parseCompartmentOutput(result);
152026
- if (parsed.compartments.length === 0) {
152027
- sessionLog(sessionId, "compressor: historian returned no compartments");
152028
- return null;
152029
- }
152030
- const messageIdMap = new Map;
152031
- for (const c of compartments) {
152032
- messageIdMap.set(c.startMessage, c.startMessageId);
152033
- messageIdMap.set(c.endMessage, c.endMessageId);
152034
- }
152035
- const mapped = parsed.compartments.map((pc) => {
152036
- const startId = messageIdMap.get(pc.startMessage) ?? "";
152037
- const endId = messageIdMap.get(pc.endMessage) ?? "";
152038
- if (!startId || !endId) {
152039
- sessionLog(sessionId, `compressor: messageId miss for ordinals ${pc.startMessage}\u2192${pc.endMessage} (startId=${startId || "MISSING"}, endId=${endId || "MISSING"})`);
152040
- }
152041
- return {
152042
- startMessage: pc.startMessage,
152043
- endMessage: pc.endMessage,
152044
- startMessageId: startId,
152045
- endMessageId: endId,
152046
- title: pc.title,
152047
- content: pc.content
152048
- };
152049
- });
152050
- const hasEmptyIds = mapped.some((c) => !c.startMessageId || !c.endMessageId);
152051
- if (hasEmptyIds) {
152052
- sessionLog(sessionId, "compressor: rejecting \u2014 one or more compartments have empty messageIds");
152053
- return null;
152054
- }
152055
- return mapped;
152056
- } catch (error48) {
152057
- sessionLog(sessionId, "compressor: historian call failed:", getErrorMessage(error48));
152058
- return null;
152059
- } finally {
152060
- if (agentSessionId) {
152061
- await client.session.delete({ path: { id: agentSessionId }, query: { directory } }).catch((e) => {
152062
- sessionLog(sessionId, "compressor: session cleanup failed:", getErrorMessage(e));
152063
- });
152064
- }
152065
- }
152066
- }
152067
- var HISTORIAN_AGENT2 = "historian";
152068
- var init_compartment_runner_compressor = __esm(() => {
152069
- init_magic_context();
152070
- init_storage();
152071
- init_shared();
152072
- init_assistant_message_extractor();
152073
- init_logger();
152074
- init_compartment_parser();
152075
- init_compartment_prompt();
152076
- init_read_session_formatting();
152077
- });
152078
-
152079
- // src/hooks/magic-context/compartment-runner-drop-queue.ts
152080
- function queueDropsForCompartmentalizedMessages(db, sessionId, upToMessageIndex) {
152081
- const tags = getTagsBySession(db, sessionId);
152082
- const rawTagKeys = new Set(getRawSessionTagKeysThrough(sessionId, upToMessageIndex));
152083
- let dropsQueued = 0;
152084
- for (const tag of tags) {
152085
- if (tag.status !== "active")
152086
- continue;
152087
- if (rawTagKeys.has(tag.messageId)) {
152088
- queuePendingOp(db, sessionId, tag.tagNumber, "drop");
152089
- dropsQueued += 1;
152090
- }
152091
- }
152092
- sessionLog(sessionId, `compartment agent: queued ${dropsQueued} drops for messages 0-${upToMessageIndex}`);
152093
- }
152094
- var init_compartment_runner_drop_queue = __esm(() => {
152095
- init_storage_ops();
152096
- init_storage_tags();
152097
- init_logger();
152098
- init_read_session_chunk();
152099
- });
152100
-
152101
151498
  // src/hooks/magic-context/compartment-runner-mapping.ts
152102
151499
  function mapParsedCompartmentsToChunk(compartments, chunk, sequenceOffset) {
152103
151500
  const mapped = [];
@@ -152127,15 +151524,19 @@ var init_compartment_runner_mapping = __esm(() => {
152127
151524
  });
152128
151525
 
152129
151526
  // src/hooks/magic-context/compartment-runner-validation.ts
152130
- function healCompartmentGaps(compartments) {
152131
- const MAX_HEALABLE_GAP = 15;
151527
+ function healCompartmentGaps(compartments, toolOnlyRanges = []) {
151528
+ const SAFETY_HEAL_GAP = 15;
152132
151529
  for (let i = 1;i < compartments.length; i++) {
152133
151530
  const prev = compartments[i - 1];
152134
151531
  const curr = compartments[i];
152135
- const expectedStart = prev.endMessage + 1;
152136
- const gapSize = curr.startMessage - expectedStart;
152137
- if (gapSize > 0 && gapSize <= MAX_HEALABLE_GAP) {
152138
- prev.endMessage = curr.startMessage - 1;
151532
+ const gapStart = prev.endMessage + 1;
151533
+ const gapEnd = curr.startMessage - 1;
151534
+ const gapSize = gapEnd - gapStart + 1;
151535
+ if (gapSize <= 0)
151536
+ continue;
151537
+ const fullyInsideToolOnly = toolOnlyRanges.some((range) => range.start <= gapStart && range.end >= gapEnd);
151538
+ if (fullyInsideToolOnly || gapSize <= SAFETY_HEAL_GAP) {
151539
+ prev.endMessage = gapEnd;
152139
151540
  }
152140
151541
  }
152141
151542
  }
@@ -152147,7 +151548,7 @@ function validateHistorianOutput(text, _sessionId, chunk, _priorCompartments, se
152147
151548
  error: "Historian returned no usable compartments."
152148
151549
  };
152149
151550
  }
152150
- healCompartmentGaps(parsed.compartments);
151551
+ healCompartmentGaps(parsed.compartments, chunk.toolOnlyRanges);
152151
151552
  const mapped = mapParsedCompartmentsToChunk(parsed.compartments, chunk, sequenceOffset);
152152
151553
  if (!mapped.ok) {
152153
151554
  return {
@@ -152266,7 +151667,7 @@ var init_compartment_runner_validation = __esm(() => {
152266
151667
  // src/hooks/magic-context/compartment-runner-historian.ts
152267
151668
  import { mkdirSync as mkdirSync3, unlinkSync, writeFileSync } from "fs";
152268
151669
  import { tmpdir as tmpdir2 } from "os";
152269
- import { join as join13 } from "path";
151670
+ import { join as join12 } from "path";
152270
151671
  async function runValidatedHistorianPass(args) {
152271
151672
  const firstRun = await runHistorianPrompt({
152272
151673
  ...args,
@@ -152282,8 +151683,14 @@ async function runValidatedHistorianPass(args) {
152282
151683
  }
152283
151684
  const firstValidation = validateHistorianOutput(firstRun.result, args.parentSessionId, args.chunk, args.priorCompartments, args.sequenceOffset);
152284
151685
  if (firstValidation.ok) {
151686
+ const finalResult = args.twoPass ? await runEditorPassOrFallback({
151687
+ ...args,
151688
+ draftXml: firstRun.result,
151689
+ draftValidation: firstValidation,
151690
+ draftDumpPath: firstRun.dumpPath
151691
+ }) : firstValidation;
152285
151692
  cleanupHistorianDump(args.parentSessionId, firstRun.dumpPath);
152286
- return firstValidation;
151693
+ return finalResult;
152287
151694
  }
152288
151695
  await args.callbacks?.onRepairRetry?.(firstValidation.error ?? "invalid compartment output");
152289
151696
  const repairPrompt = buildHistorianRepairPrompt(args.prompt, firstRun.result, firstValidation.error ?? "invalid compartment output");
@@ -152302,8 +151709,14 @@ async function runValidatedHistorianPass(args) {
152302
151709
  }
152303
151710
  const repairValidation = validateHistorianOutput(repairRun.result, args.parentSessionId, args.chunk, args.priorCompartments, args.sequenceOffset);
152304
151711
  if (repairValidation.ok) {
151712
+ const finalResult = args.twoPass ? await runEditorPassOrFallback({
151713
+ ...args,
151714
+ draftXml: repairRun.result,
151715
+ draftValidation: repairValidation,
151716
+ draftDumpPath: repairRun.dumpPath
151717
+ }) : repairValidation;
152305
151718
  cleanupHistorianDump(args.parentSessionId, repairRun.dumpPath);
152306
- return repairValidation;
151719
+ return finalResult;
152307
151720
  }
152308
151721
  return runFallbackHistorianPass({
152309
151722
  ...args,
@@ -152312,6 +151725,32 @@ async function runValidatedHistorianPass(args) {
152312
151725
  dumpPaths: [firstRun.dumpPath, repairRun.dumpPath]
152313
151726
  });
152314
151727
  }
151728
+ async function runEditorPassOrFallback(args) {
151729
+ sessionLog(args.parentSessionId, "historian two-pass: running editor on draft");
151730
+ const editorRun = await runHistorianPrompt({
151731
+ client: args.client,
151732
+ parentSessionId: args.parentSessionId,
151733
+ sessionDirectory: args.sessionDirectory,
151734
+ prompt: buildHistorianEditorPrompt(args.draftXml),
151735
+ timeoutMs: args.timeoutMs,
151736
+ dumpLabel: `${args.dumpLabelBase}-editor`,
151737
+ agentId: HISTORIAN_EDITOR_AGENT
151738
+ });
151739
+ if (!editorRun.ok || !editorRun.result) {
151740
+ sessionLog(args.parentSessionId, "historian two-pass: editor call failed", {
151741
+ error: editorRun.error
151742
+ });
151743
+ return args.draftValidation;
151744
+ }
151745
+ const editorValidation = validateHistorianOutput(editorRun.result, args.parentSessionId, args.chunk, args.priorCompartments, args.sequenceOffset);
151746
+ if (!editorValidation.ok) {
151747
+ sessionLog(args.parentSessionId, "historian two-pass: editor validation failed, falling back to draft", { error: editorValidation.error });
151748
+ return args.draftValidation;
151749
+ }
151750
+ cleanupHistorianDump(args.parentSessionId, editorRun.dumpPath);
151751
+ sessionLog(args.parentSessionId, "historian two-pass: editor accepted");
151752
+ return editorValidation;
151753
+ }
152315
151754
  async function runHistorianPrompt(args) {
152316
151755
  const {
152317
151756
  client,
@@ -152320,11 +151759,12 @@ async function runHistorianPrompt(args) {
152320
151759
  prompt,
152321
151760
  timeoutMs,
152322
151761
  dumpLabel,
152323
- modelOverride
151762
+ modelOverride,
151763
+ agentId = HISTORIAN_AGENT
152324
151764
  } = args;
152325
151765
  let agentSessionId = null;
152326
151766
  try {
152327
- sessionLog(parentSessionId, `historian: creating child session (model=${modelOverride ? `${modelOverride.providerID}/${modelOverride.modelID}` : `agent:${HISTORIAN_AGENT}`})`);
151767
+ sessionLog(parentSessionId, `historian: creating child session (agent=${agentId}, model=${modelOverride ? `${modelOverride.providerID}/${modelOverride.modelID}` : `agent:${agentId}`})`);
152328
151768
  const createResponse = await client.session.create({
152329
151769
  body: {
152330
151770
  parentID: parentSessionId,
@@ -152343,7 +151783,7 @@ async function runHistorianPrompt(args) {
152343
151783
  path: { id: agentSessionId },
152344
151784
  query: { directory: sessionDirectory },
152345
151785
  body: {
152346
- agent: HISTORIAN_AGENT,
151786
+ agent: agentId,
152347
151787
  ...modelOverride ? { model: modelOverride } : {},
152348
151788
  parts: [{ type: "text", text: prompt }]
152349
151789
  }
@@ -152451,8 +151891,8 @@ function isTransientHistorianPromptError(message) {
152451
151891
  ].some((token) => normalized.includes(token));
152452
151892
  }
152453
151893
  function sleep(ms) {
152454
- return new Promise((resolve2) => {
152455
- setTimeout(resolve2, ms);
151894
+ return new Promise((resolve3) => {
151895
+ setTimeout(resolve3, ms);
152456
151896
  });
152457
151897
  }
152458
151898
  function cleanupHistorianDump(sessionId, dumpPath) {
@@ -152472,7 +151912,7 @@ function dumpHistorianResponse(sessionId, label, text) {
152472
151912
  mkdirSync3(HISTORIAN_RESPONSE_DUMP_DIR, { recursive: true });
152473
151913
  const safeSessionId = sanitizeDumpName(sessionId);
152474
151914
  const safeLabel = sanitizeDumpName(label);
152475
- const dumpPath = join13(HISTORIAN_RESPONSE_DUMP_DIR, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
151915
+ const dumpPath = join12(HISTORIAN_RESPONSE_DUMP_DIR, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
152476
151916
  writeFileSync(dumpPath, text, "utf8");
152477
151917
  sessionLog(sessionId, "compartment agent: historian response dumped", {
152478
151918
  label,
@@ -152495,8 +151935,9 @@ var init_compartment_runner_historian = __esm(() => {
152495
151935
  init_magic_context();
152496
151936
  init_shared();
152497
151937
  init_assistant_message_extractor();
151938
+ init_compartment_prompt();
152498
151939
  init_compartment_runner_validation();
152499
- HISTORIAN_RESPONSE_DUMP_DIR = join13(tmpdir2(), "magic-context-historian");
151940
+ HISTORIAN_RESPONSE_DUMP_DIR = join12(tmpdir2(), "magic-context-historian");
152500
151941
  });
152501
151942
 
152502
151943
  // src/hooks/magic-context/compartment-runner-state-xml.ts
@@ -152537,6 +151978,131 @@ var init_compartment_runner_state_xml = __esm(() => {
152537
151978
  init_compartment_storage();
152538
151979
  });
152539
151980
 
151981
+ // src/features/magic-context/memory/constants.ts
151982
+ var PROMOTABLE_CATEGORIES, CATEGORY_PRIORITY, CATEGORY_DEFAULT_TTL;
151983
+ var init_constants = __esm(() => {
151984
+ PROMOTABLE_CATEGORIES = [
151985
+ "ARCHITECTURE_DECISIONS",
151986
+ "CONSTRAINTS",
151987
+ "CONFIG_DEFAULTS",
151988
+ "NAMING",
151989
+ "USER_PREFERENCES",
151990
+ "USER_DIRECTIVES",
151991
+ "ENVIRONMENT",
151992
+ "WORKFLOW_RULES",
151993
+ "KNOWN_ISSUES"
151994
+ ];
151995
+ CATEGORY_PRIORITY = [
151996
+ "USER_DIRECTIVES",
151997
+ "USER_PREFERENCES",
151998
+ "NAMING",
151999
+ "CONFIG_DEFAULTS",
152000
+ "CONSTRAINTS",
152001
+ "ARCHITECTURE_DECISIONS",
152002
+ "ENVIRONMENT",
152003
+ "WORKFLOW_RULES",
152004
+ "KNOWN_ISSUES"
152005
+ ];
152006
+ CATEGORY_DEFAULT_TTL = {
152007
+ WORKFLOW_RULES: 90 * 24 * 60 * 60 * 1000,
152008
+ KNOWN_ISSUES: 30 * 24 * 60 * 60 * 1000
152009
+ };
152010
+ });
152011
+
152012
+ // src/hooks/magic-context/temporal-awareness.ts
152013
+ function formatGap(seconds) {
152014
+ if (!Number.isFinite(seconds) || seconds < TEMPORAL_AWARENESS_THRESHOLD_SECONDS) {
152015
+ return null;
152016
+ }
152017
+ if (seconds < SECONDS_PER_HOUR) {
152018
+ const minutes = Math.floor(seconds / SECONDS_PER_MINUTE);
152019
+ return `+${minutes}m`;
152020
+ }
152021
+ if (seconds < SECONDS_PER_DAY) {
152022
+ const hours = Math.floor(seconds / SECONDS_PER_HOUR);
152023
+ const minutes = Math.floor((seconds - hours * SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
152024
+ return minutes === 0 ? `+${hours}h` : `+${hours}h ${minutes}m`;
152025
+ }
152026
+ if (seconds < SECONDS_PER_WEEK) {
152027
+ const days2 = Math.floor(seconds / SECONDS_PER_DAY);
152028
+ const hours = Math.floor((seconds - days2 * SECONDS_PER_DAY) / SECONDS_PER_HOUR);
152029
+ return hours === 0 ? `+${days2}d` : `+${days2}d ${hours}h`;
152030
+ }
152031
+ const weeks = Math.floor(seconds / SECONDS_PER_WEEK);
152032
+ const days = Math.floor((seconds - weeks * SECONDS_PER_WEEK) / SECONDS_PER_DAY);
152033
+ return days === 0 ? `+${weeks}w` : `+${weeks}w ${days}d`;
152034
+ }
152035
+ function formatDate(ms) {
152036
+ const d = new Date(ms);
152037
+ const yyyy = d.getFullYear().toString().padStart(4, "0");
152038
+ const mm = (d.getMonth() + 1).toString().padStart(2, "0");
152039
+ const dd = d.getDate().toString().padStart(2, "0");
152040
+ return `${yyyy}-${mm}-${dd}`;
152041
+ }
152042
+ function temporalMarkerPrefix(seconds) {
152043
+ const marker = formatGap(seconds);
152044
+ if (!marker)
152045
+ return null;
152046
+ return `<!-- ${marker} -->
152047
+ `;
152048
+ }
152049
+ function isMutableTextPart(part) {
152050
+ if (part === null || typeof part !== "object")
152051
+ return false;
152052
+ const p = part;
152053
+ return p.type === "text" && typeof p.text === "string";
152054
+ }
152055
+ function findFirstVisibleTextPart(parts) {
152056
+ for (const p of parts) {
152057
+ if (!isMutableTextPart(p))
152058
+ continue;
152059
+ if (p.ignored === true)
152060
+ continue;
152061
+ return p;
152062
+ }
152063
+ return null;
152064
+ }
152065
+ function injectTemporalMarkers(messages) {
152066
+ let injected = 0;
152067
+ let prev = null;
152068
+ for (const raw of messages) {
152069
+ if (!raw || typeof raw !== "object")
152070
+ continue;
152071
+ const msg = raw;
152072
+ const role = msg.info?.role;
152073
+ if (prev !== null && role === "user") {
152074
+ const prevTime = prev.info?.time;
152075
+ const currTime = msg.info?.time;
152076
+ if (prevTime?.created !== undefined && currTime?.created !== undefined) {
152077
+ const prevEnd = prevTime.completed ?? prevTime.created;
152078
+ const gapSec = (currTime.created - prevEnd) / 1000;
152079
+ const prefix = temporalMarkerPrefix(gapSec);
152080
+ if (prefix && Array.isArray(msg.parts)) {
152081
+ const target = findFirstVisibleTextPart(msg.parts);
152082
+ if (target && typeof target.text === "string") {
152083
+ const tagMatch = target.text.match(/^(?:\u00A7\d+\u00A7\s*)+/);
152084
+ const tagPrefix = tagMatch ? tagMatch[0] : "";
152085
+ const body = target.text.slice(tagPrefix.length);
152086
+ if (!TEMPORAL_MARKER_PATTERN.test(body)) {
152087
+ target.text = tagPrefix + prefix + body;
152088
+ injected++;
152089
+ }
152090
+ }
152091
+ }
152092
+ }
152093
+ }
152094
+ prev = msg;
152095
+ }
152096
+ return injected;
152097
+ }
152098
+ var TEMPORAL_AWARENESS_THRESHOLD_SECONDS = 300, SECONDS_PER_MINUTE = 60, SECONDS_PER_HOUR, SECONDS_PER_DAY, SECONDS_PER_WEEK, TEMPORAL_MARKER_PATTERN;
152099
+ var init_temporal_awareness = __esm(() => {
152100
+ SECONDS_PER_HOUR = 60 * 60;
152101
+ SECONDS_PER_DAY = 24 * 60 * 60;
152102
+ SECONDS_PER_WEEK = 7 * 24 * 60 * 60;
152103
+ TEMPORAL_MARKER_PATTERN = /^<!-- \+[\d]+[mhdw](?: [\d]+[mhdw])? -->\n/;
152104
+ });
152105
+
152540
152106
  // src/hooks/magic-context/inject-compartments.ts
152541
152107
  function clearInjectionCache(sessionId) {
152542
152108
  injectionCache.delete(sessionId);
@@ -152606,7 +152172,7 @@ function trimMemoriesToBudget(sessionId, memories, budgetTokens) {
152606
152172
  }
152607
152173
  return result;
152608
152174
  }
152609
- function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectPath, injectionBudgetTokens) {
152175
+ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectPath, injectionBudgetTokens, temporalAwareness) {
152610
152176
  const cached2 = injectionCache.get(sessionId);
152611
152177
  if (!isCacheBusting && cached2) {
152612
152178
  if (cached2.compartmentEndMessageId.length > 0) {
@@ -152652,7 +152218,28 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
152652
152218
  injectionCache.delete(sessionId);
152653
152219
  return null;
152654
152220
  }
152655
- const block = buildCompartmentBlock(compartments, facts, memoryBlock);
152221
+ let dateRanges;
152222
+ if (temporalAwareness && compartments.length > 0) {
152223
+ const ids = new Set;
152224
+ for (const c of compartments) {
152225
+ if (c.startMessageId)
152226
+ ids.add(c.startMessageId);
152227
+ if (c.endMessageId)
152228
+ ids.add(c.endMessageId);
152229
+ }
152230
+ const times = getMessageTimesFromOpenCodeDb(sessionId, Array.from(ids));
152231
+ const byId = new Map;
152232
+ for (const c of compartments) {
152233
+ const startMs = times.get(c.startMessageId);
152234
+ const endMs = times.get(c.endMessageId);
152235
+ if (startMs !== undefined && endMs !== undefined) {
152236
+ byId.set(c.id, { start: formatDate(startMs), end: formatDate(endMs) });
152237
+ }
152238
+ }
152239
+ if (byId.size > 0)
152240
+ dateRanges = { byId };
152241
+ }
152242
+ const block = buildCompartmentBlock(compartments, facts, memoryBlock, dateRanges);
152656
152243
  if (compartments.length === 0) {
152657
152244
  const result2 = {
152658
152245
  block,
@@ -152754,11 +152341,1523 @@ var init_inject_compartments = __esm(() => {
152754
152341
  init_constants();
152755
152342
  init_storage_memory();
152756
152343
  init_logger();
152344
+ init_read_session_db();
152757
152345
  init_read_session_formatting();
152346
+ init_temporal_awareness();
152758
152347
  injectionCache = new Map;
152759
152348
  CONSTRAINT_KEYWORDS = /\b(must|never|always|cannot|should not|must not)\b/i;
152760
152349
  });
152761
152350
 
152351
+ // src/hooks/magic-context/send-session-notification.ts
152352
+ var exports_send_session_notification = {};
152353
+ __export(exports_send_session_notification, {
152354
+ sendUserPrompt: () => sendUserPrompt,
152355
+ sendIgnoredMessage: () => sendIgnoredMessage
152356
+ });
152357
+ function hasNotificationSessionClient(client) {
152358
+ if (client === null || typeof client !== "object")
152359
+ return false;
152360
+ const candidate = client;
152361
+ if (candidate.session === undefined)
152362
+ return true;
152363
+ if (candidate.session === null || typeof candidate.session !== "object")
152364
+ return false;
152365
+ const session = candidate.session;
152366
+ return (session.prompt === undefined || typeof session.prompt === "function") && (session.promptAsync === undefined || typeof session.promptAsync === "function");
152367
+ }
152368
+ function inferToastVariant(text) {
152369
+ const lower = text.toLowerCase();
152370
+ if (lower.includes("error") || lower.includes("failed") || lower.includes("alert"))
152371
+ return "error";
152372
+ if (lower.includes("warning") || lower.includes("\u26A0"))
152373
+ return "warning";
152374
+ if (lower.includes("complete") || lower.includes("success") || lower.includes("\u2713") || lower.includes("finished"))
152375
+ return "success";
152376
+ return "info";
152377
+ }
152378
+ function extractToastTitle(text) {
152379
+ const headingMatch = text.match(/^#+\s+(.+)/m);
152380
+ if (headingMatch)
152381
+ return headingMatch[1].trim();
152382
+ const firstLine = text.split(`
152383
+ `)[0].trim();
152384
+ if (firstLine.length <= 80)
152385
+ return firstLine;
152386
+ return "Magic Context";
152387
+ }
152388
+ async function sendIgnoredMessage(client, sessionId, text, params) {
152389
+ const { isTuiConnected: checkTui } = await Promise.resolve().then(() => (init_rpc_notifications(), exports_rpc_notifications));
152390
+ if (checkTui()) {
152391
+ try {
152392
+ const c2 = client;
152393
+ const tui = c2?.tui;
152394
+ if (typeof tui?.showToast === "function") {
152395
+ const tuiClient = tui;
152396
+ await tuiClient.showToast({
152397
+ body: {
152398
+ title: extractToastTitle(text),
152399
+ message: text.length > 200 ? `${text.slice(0, 200)}\u2026` : text,
152400
+ variant: inferToastVariant(text),
152401
+ duration: 5000
152402
+ }
152403
+ }).catch(() => {});
152404
+ return;
152405
+ }
152406
+ } catch {}
152407
+ }
152408
+ const agent = params.agent || undefined;
152409
+ const variant = params.variant || undefined;
152410
+ const model = params.providerId && params.modelId ? {
152411
+ providerID: params.providerId,
152412
+ modelID: params.modelId
152413
+ } : undefined;
152414
+ if (!hasNotificationSessionClient(client)) {
152415
+ sessionLog(sessionId, "session prompt API unavailable for notification");
152416
+ return;
152417
+ }
152418
+ const c = client;
152419
+ const input = {
152420
+ path: { id: sessionId },
152421
+ body: {
152422
+ noReply: true,
152423
+ agent,
152424
+ model,
152425
+ variant,
152426
+ parts: [
152427
+ {
152428
+ type: "text",
152429
+ text,
152430
+ ignored: true
152431
+ }
152432
+ ]
152433
+ }
152434
+ };
152435
+ try {
152436
+ if (typeof c.session?.prompt === "function") {
152437
+ await Promise.resolve(c.session.prompt(input));
152438
+ } else if (typeof c.session?.promptAsync === "function") {
152439
+ await c.session.promptAsync(input);
152440
+ } else {
152441
+ sessionLog(sessionId, "session prompt API unavailable for notification");
152442
+ }
152443
+ } catch (error48) {
152444
+ const msg = getErrorMessage(error48);
152445
+ sessionLog(sessionId, "failed to send notification:", msg);
152446
+ }
152447
+ }
152448
+ async function sendUserPrompt(client, sessionId, text) {
152449
+ if (!hasNotificationSessionClient(client)) {
152450
+ sessionLog(sessionId, "session prompt API unavailable for user prompt");
152451
+ return;
152452
+ }
152453
+ const c = client;
152454
+ const input = {
152455
+ path: { id: sessionId },
152456
+ body: {
152457
+ parts: [{ type: "text", text }]
152458
+ }
152459
+ };
152460
+ try {
152461
+ if (typeof c.session?.promptAsync === "function") {
152462
+ await c.session.promptAsync(input);
152463
+ } else if (typeof c.session?.prompt === "function") {
152464
+ await Promise.resolve(c.session.prompt(input));
152465
+ } else {
152466
+ sessionLog(sessionId, "session prompt API unavailable for user prompt");
152467
+ }
152468
+ } catch (error48) {
152469
+ const msg = getErrorMessage(error48);
152470
+ sessionLog(sessionId, "failed to send user prompt:", msg);
152471
+ }
152472
+ }
152473
+ var init_send_session_notification = __esm(() => {
152474
+ init_logger();
152475
+ });
152476
+
152477
+ // src/hooks/magic-context/compartment-runner-partial-recomp.ts
152478
+ function snapRangeToCompartments(compartments, range) {
152479
+ if (compartments.length === 0) {
152480
+ return {
152481
+ error: "No compartments exist yet for this session. Run `/ctx-recomp` (full) first, then use partial recomp to refine specific ranges."
152482
+ };
152483
+ }
152484
+ const sorted = compartments.slice().sort((a, b) => a.sequence - b.sequence);
152485
+ const { start, end } = range;
152486
+ if (start < 1)
152487
+ return { error: `Start must be >= 1 (got ${start}).` };
152488
+ if (end < start)
152489
+ return { error: `End must be >= start (got ${start}-${end}).` };
152490
+ const firstEnclosingIdx = sorted.findIndex((c) => c.endMessage >= start);
152491
+ if (firstEnclosingIdx === -1) {
152492
+ const last = sorted[sorted.length - 1];
152493
+ return {
152494
+ error: `Range ${start}-${end} starts after the last compartment (which ends at message ${last.endMessage}). Nothing to rebuild.`
152495
+ };
152496
+ }
152497
+ let lastEnclosingIdx = -1;
152498
+ for (let i = sorted.length - 1;i >= 0; i--) {
152499
+ if (sorted[i].startMessage <= end) {
152500
+ lastEnclosingIdx = i;
152501
+ break;
152502
+ }
152503
+ }
152504
+ if (lastEnclosingIdx === -1 || lastEnclosingIdx < firstEnclosingIdx) {
152505
+ return {
152506
+ error: `Range ${start}-${end} does not overlap any compartment.`
152507
+ };
152508
+ }
152509
+ return {
152510
+ snapStart: sorted[firstEnclosingIdx].startMessage,
152511
+ snapEnd: sorted[lastEnclosingIdx].endMessage,
152512
+ priorCompartments: sorted.slice(0, firstEnclosingIdx),
152513
+ rangeCompartments: sorted.slice(firstEnclosingIdx, lastEnclosingIdx + 1),
152514
+ tailCompartments: sorted.slice(lastEnclosingIdx + 1)
152515
+ };
152516
+ }
152517
+ function compartmentToInput(c, newSequence) {
152518
+ return {
152519
+ sequence: newSequence,
152520
+ startMessage: c.startMessage,
152521
+ endMessage: c.endMessage,
152522
+ startMessageId: c.startMessageId,
152523
+ endMessageId: c.endMessageId,
152524
+ title: c.title,
152525
+ content: c.content
152526
+ };
152527
+ }
152528
+ async function executePartialRecompInternal(deps, range) {
152529
+ const {
152530
+ client,
152531
+ db,
152532
+ sessionId,
152533
+ historianChunkTokens,
152534
+ directory,
152535
+ historianTimeoutMs,
152536
+ getNotificationParams
152537
+ } = deps;
152538
+ const notifParams = () => getNotificationParams?.() ?? {};
152539
+ updateSessionMeta(db, sessionId, { compartmentInProgress: true });
152540
+ try {
152541
+ let promoteFinal = function() {
152542
+ const newBuilt = candidateCompartments.slice(priorCompartments.length);
152543
+ if (newBuilt.length === 0)
152544
+ return null;
152545
+ const newBuiltError = (() => {
152546
+ let expected = snapStart;
152547
+ for (const c of newBuilt) {
152548
+ if (c.startMessage !== expected) {
152549
+ return c.startMessage < expected ? `overlap in rebuilt range near ${expected}` : `gap in rebuilt range before ${c.startMessage} (expected ${expected})`;
152550
+ }
152551
+ if (c.endMessage < c.startMessage) {
152552
+ return `invalid range ${c.startMessage}-${c.endMessage}`;
152553
+ }
152554
+ expected = c.endMessage + 1;
152555
+ }
152556
+ if (expected - 1 !== snapEnd) {
152557
+ return `rebuilt range ends at ${expected - 1} but snapped end is ${snapEnd}`;
152558
+ }
152559
+ return null;
152560
+ })();
152561
+ if (newBuiltError) {
152562
+ log(`[magic-context] partial recomp validation failed: ${newBuiltError}`);
152563
+ return null;
152564
+ }
152565
+ const merged = [
152566
+ ...candidateCompartments,
152567
+ ...tailCompartments.map((c, idx) => compartmentToInput(c, candidateCompartments.length + idx))
152568
+ ];
152569
+ const mergedError = validateStoredCompartments(merged);
152570
+ if (mergedError) {
152571
+ log(`[magic-context] partial recomp merged validation failed: ${mergedError}`);
152572
+ return null;
152573
+ }
152574
+ saveRecompStagingPass(db, sessionId, passCount + 1, merged, currentFacts);
152575
+ const promoted = promoteRecompStaging(db, sessionId);
152576
+ if (!promoted) {
152577
+ log("[magic-context] partial recomp promote returned null");
152578
+ return null;
152579
+ }
152580
+ setRecompPartialRange(db, sessionId, null);
152581
+ clearCompressionDepthRange(db, sessionId, snapStart, snapEnd);
152582
+ clearInjectionCache(sessionId);
152583
+ const lastEnd = merged[merged.length - 1]?.endMessage ?? snapEnd;
152584
+ return { compartmentCount: merged.length, lastEndMessage: lastEnd };
152585
+ };
152586
+ const protectedTailStart = getProtectedTailStartOrdinal(sessionId);
152587
+ const existingCompartments = getCompartments(db, sessionId);
152588
+ const snapResult = snapRangeToCompartments(existingCompartments, range);
152589
+ if ("error" in snapResult) {
152590
+ return `## Magic Recomp \u2014 Failed
152591
+
152592
+ ${snapResult.error}`;
152593
+ }
152594
+ const { snapStart, snapEnd, priorCompartments, tailCompartments } = snapResult;
152595
+ if (snapEnd >= protectedTailStart) {
152596
+ return `## Magic Recomp \u2014 Failed
152597
+
152598
+ Snapped range ${snapStart}-${snapEnd} would cross into the protected tail (starting at ${protectedTailStart}). Partial recomp cannot rebuild recent messages. Try an earlier range.`;
152599
+ }
152600
+ const storedRange = getRecompPartialRange(db, sessionId);
152601
+ const existingStaging = getRecompStaging(db, sessionId);
152602
+ if (existingStaging && storedRange && (storedRange.start !== snapStart || storedRange.end !== snapEnd)) {
152603
+ return [
152604
+ "## Magic Recomp \u2014 Failed",
152605
+ "",
152606
+ `An unfinished partial recomp is already staged for range ${storedRange.start}-${storedRange.end}, which does not match the requested range ${snapStart}-${snapEnd}.`,
152607
+ "",
152608
+ "Resume that range by running `/ctx-recomp` with the same original arguments,",
152609
+ "or cancel it by running `/ctx-flush` before starting a new partial recomp."
152610
+ ].join(`
152611
+ `);
152612
+ }
152613
+ if (existingStaging && !storedRange) {
152614
+ return [
152615
+ "## Magic Recomp \u2014 Failed",
152616
+ "",
152617
+ "An unfinished full recomp is already staged for this session.",
152618
+ "Resume it by running `/ctx-recomp` without arguments,",
152619
+ "or cancel it before starting a partial recomp."
152620
+ ].join(`
152621
+ `);
152622
+ }
152623
+ const currentFacts = getSessionFacts(db, sessionId).map((f) => ({
152624
+ category: f.category,
152625
+ content: f.content
152626
+ }));
152627
+ const parentSessionResponse = await client.session.get({ path: { id: sessionId } }).catch(() => null);
152628
+ const parentSession = normalizeSDKResponse(parentSessionResponse, null, { preferResponseOnMissingData: true });
152629
+ const sessionDirectory = parentSession?.directory ?? directory;
152630
+ const projectPath = directory ? resolveProjectIdentity(directory) : undefined;
152631
+ const memories = projectPath ? getMemoriesByProject(db, projectPath, ["active", "permanent"]) : [];
152632
+ const memoryBlockForExistingState = memories.length > 0 ? undefined : undefined;
152633
+ let candidateCompartments;
152634
+ let passCount;
152635
+ let offset;
152636
+ const resumed = existingStaging !== null && storedRange !== null;
152637
+ if (resumed && existingStaging) {
152638
+ candidateCompartments = existingStaging.compartments;
152639
+ passCount = existingStaging.passCount;
152640
+ const lastInStaging = existingStaging.lastEndMessage;
152641
+ offset = lastInStaging >= snapStart ? lastInStaging + 1 : snapStart;
152642
+ } else {
152643
+ candidateCompartments = priorCompartments.map((c, idx) => compartmentToInput(c, idx));
152644
+ passCount = 0;
152645
+ offset = snapStart;
152646
+ saveRecompStagingPass(db, sessionId, 0, candidateCompartments, currentFacts);
152647
+ setRecompPartialRange(db, sessionId, { start: snapStart, end: snapEnd });
152648
+ }
152649
+ let currentTokenBudget = historianChunkTokens;
152650
+ let passAttempt = 1;
152651
+ await sendIgnoredMessage(client, sessionId, resumed ? `## Magic Recomp \u2014 Resumed (Partial)
152652
+
152653
+ Found ${candidateCompartments.length - priorCompartments.length} newly built compartment(s) from ${passCount} previous pass(es), covering messages ${snapStart}-${offset - 1}. Resuming from message ${offset} toward ${snapEnd}.` : `## Magic Recomp \u2014 Partial
152654
+
152655
+ Snapped to compartment boundaries: rebuilding messages ${snapStart}-${snapEnd} (${tailCompartments.length} tail compartment(s) preserved).`, notifParams());
152656
+ while (offset <= snapEnd) {
152657
+ const chunk = readSessionChunk(sessionId, currentTokenBudget, offset, snapEnd + 1);
152658
+ if (!chunk.text || chunk.messageCount === 0 || chunk.endIndex < offset) {
152659
+ return `## Magic Recomp \u2014 Failed
152660
+
152661
+ Recomp stopped because raw history ${offset}-${snapEnd} could not be turned into a valid historian chunk. Partial recomp preserved original state (staging kept for retry).`;
152662
+ }
152663
+ const chunkCoverageError = validateChunkCoverage(chunk);
152664
+ if (chunkCoverageError) {
152665
+ return `## Magic Recomp \u2014 Failed
152666
+
152667
+ Partial recomp stopped because the raw chunk could not be represented safely: ${chunkCoverageError}
152668
+
152669
+ Original state preserved (staging kept for retry).`;
152670
+ }
152671
+ const existingState = buildExistingStateXml(candidateCompartments, currentFacts, undefined);
152672
+ const prompt = buildCompartmentAgentPrompt(existingState, `Messages ${chunk.startIndex}-${chunk.endIndex}:
152673
+
152674
+ ${chunk.text}`);
152675
+ await sendIgnoredMessage(client, sessionId, `## Magic Recomp \u2014 Partial
152676
+
152677
+ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${chunk.startIndex}-${chunk.endIndex}.`, notifParams());
152678
+ const validatedPass = await runValidatedHistorianPass({
152679
+ client,
152680
+ parentSessionId: sessionId,
152681
+ sessionDirectory,
152682
+ prompt,
152683
+ chunk,
152684
+ priorCompartments: candidateCompartments,
152685
+ sequenceOffset: candidateCompartments.length,
152686
+ dumpLabelBase: `partial-recomp-${sessionId}-${chunk.startIndex}-${chunk.endIndex}-pass-${passCount + 1}`,
152687
+ timeoutMs: historianTimeoutMs,
152688
+ fallbackModelId: deps.fallbackModelId,
152689
+ twoPass: deps.historianTwoPass,
152690
+ callbacks: {
152691
+ onRepairRetry: async (error48) => {
152692
+ await sendIgnoredMessage(client, sessionId, `## Magic Recomp \u2014 Partial
152693
+
152694
+ Historian pass ${passCount + 1}, attempt ${passAttempt} is continuing with a repair retry for messages ${chunk.startIndex}-${chunk.endIndex}.
152695
+
152696
+ The previous output did not validate: ${error48}`, notifParams());
152697
+ }
152698
+ }
152699
+ });
152700
+ if (!validatedPass.ok) {
152701
+ const reducedBudget = getReducedRecompTokenBudget(currentTokenBudget);
152702
+ if (reducedBudget !== null) {
152703
+ const smallerChunk = readSessionChunk(sessionId, reducedBudget, offset, snapEnd + 1);
152704
+ if (smallerChunk.messageCount > 0 && smallerChunk.endIndex < chunk.endIndex) {
152705
+ await sendIgnoredMessage(client, sessionId, `## Magic Recomp \u2014 Partial
152706
+
152707
+ 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.
152708
+
152709
+ Validator result: ${validatedPass.error}`, notifParams());
152710
+ currentTokenBudget = reducedBudget;
152711
+ passAttempt += 1;
152712
+ continue;
152713
+ }
152714
+ }
152715
+ return `## Magic Recomp \u2014 Failed
152716
+
152717
+ Partial recomp failed while rebuilding messages ${chunk.startIndex}-${chunk.endIndex}: ${validatedPass.error}
152718
+
152719
+ Original state preserved (staging kept for retry).`;
152720
+ }
152721
+ candidateCompartments = [
152722
+ ...candidateCompartments,
152723
+ ...validatedPass.compartments ?? []
152724
+ ];
152725
+ passCount += 1;
152726
+ currentTokenBudget = historianChunkTokens;
152727
+ passAttempt = 1;
152728
+ saveRecompStagingPass(db, sessionId, passCount, candidateCompartments, currentFacts);
152729
+ const nextOffset = (validatedPass.compartments?.[validatedPass.compartments.length - 1]?.endMessage ?? chunk.endIndex) + 1;
152730
+ if (nextOffset <= offset) {
152731
+ return `## Magic Recomp \u2014 Failed
152732
+
152733
+ Partial recomp made no forward progress after messages ${chunk.startIndex}-${chunk.endIndex}. Staging kept for retry.`;
152734
+ }
152735
+ offset = nextOffset;
152736
+ }
152737
+ const finalResult = promoteFinal();
152738
+ if (!finalResult) {
152739
+ return `## Magic Recomp \u2014 Failed
152740
+
152741
+ Partial recomp completed historian passes but the final compartment set failed validation. Original state preserved (staging kept for inspection).`;
152742
+ }
152743
+ return [
152744
+ "## Magic Recomp \u2014 Partial Complete",
152745
+ "",
152746
+ ...resumed ? ["Resumed from previous interrupted partial run."] : [],
152747
+ `Rebuilt compartments covering messages ${snapStart}-${snapEnd} using ${passCount} historian pass${passCount === 1 ? "" : "es"}.`,
152748
+ `Preserved ${priorCompartments.length} prior compartment(s) and ${tailCompartments.length} tail compartment(s) unchanged.`,
152749
+ `Facts unchanged (${currentFacts.length} entr${currentFacts.length === 1 ? "y" : "ies"}).`,
152750
+ `Total compartments: ${finalResult.compartmentCount}.`
152751
+ ].join(`
152752
+ `);
152753
+ } catch (error48) {
152754
+ const message = getErrorMessage(error48);
152755
+ return `## Magic Recomp \u2014 Failed
152756
+
152757
+ Partial recomp failed unexpectedly: ${message}
152758
+
152759
+ Staging preserved for resume on next attempt.`;
152760
+ } finally {
152761
+ updateSessionMeta(db, sessionId, { compartmentInProgress: false });
152762
+ const leftoverStaging = getRecompStaging(db, sessionId);
152763
+ const leftoverRange = getRecompPartialRange(db, sessionId);
152764
+ if (leftoverStaging && leftoverRange) {} else if (leftoverStaging && !leftoverRange) {
152765
+ log(`[magic-context] partial recomp cleanup: clearing orphaned staging without range marker for session ${sessionId}`);
152766
+ clearRecompStaging(db, sessionId);
152767
+ }
152768
+ }
152769
+ }
152770
+ var init_compartment_runner_partial_recomp = __esm(() => {
152771
+ init_compartment_storage();
152772
+ init_compression_depth_storage();
152773
+ init_project_identity();
152774
+ init_storage_memory();
152775
+ init_storage_meta();
152776
+ init_shared();
152777
+ init_logger();
152778
+ init_compartment_prompt();
152779
+ init_compartment_runner_historian();
152780
+ init_compartment_runner_state_xml();
152781
+ init_compartment_runner_validation();
152782
+ init_inject_compartments();
152783
+ init_read_session_chunk();
152784
+ init_send_session_notification();
152785
+ });
152786
+
152787
+ // src/hooks/magic-context/derive-budgets.ts
152788
+ var exports_derive_budgets = {};
152789
+ __export(exports_derive_budgets, {
152790
+ resolveHistorianContextLimit: () => resolveHistorianContextLimit,
152791
+ deriveTriggerBudget: () => deriveTriggerBudget,
152792
+ deriveHistorianChunkTokens: () => deriveHistorianChunkTokens
152793
+ });
152794
+ function deriveTriggerBudget(mainContextLimit, executeThresholdPercentage) {
152795
+ if (!Number.isFinite(mainContextLimit) || mainContextLimit <= 0) {
152796
+ return TRIGGER_BUDGET_MIN;
152797
+ }
152798
+ const thresholdFraction = Math.max(0, executeThresholdPercentage) / 100;
152799
+ const usable = mainContextLimit * thresholdFraction;
152800
+ const derived = Math.round(usable * TRIGGER_BUDGET_PERCENTAGE);
152801
+ return Math.max(TRIGGER_BUDGET_MIN, Math.min(TRIGGER_BUDGET_MAX, derived));
152802
+ }
152803
+ function deriveHistorianChunkTokens(historianContextLimit) {
152804
+ if (!Number.isFinite(historianContextLimit) || historianContextLimit <= 0) {
152805
+ return HISTORIAN_CHUNK_MIN;
152806
+ }
152807
+ const derived = Math.round(historianContextLimit * HISTORIAN_CHUNK_PERCENTAGE);
152808
+ return Math.max(HISTORIAN_CHUNK_MIN, Math.min(HISTORIAN_CHUNK_MAX, derived));
152809
+ }
152810
+ function resolveHistorianContextLimit(historianModelOverride) {
152811
+ if (typeof historianModelOverride === "string" && historianModelOverride.includes("/")) {
152812
+ const [providerID, ...rest] = historianModelOverride.split("/");
152813
+ const modelID = rest.join("/");
152814
+ if (providerID && modelID) {
152815
+ const limit = getModelsDevContextLimit(providerID, modelID);
152816
+ if (typeof limit === "number" && limit > 0)
152817
+ return limit;
152818
+ }
152819
+ return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
152820
+ }
152821
+ if (typeof historianModelOverride === "string" && historianModelOverride.trim() !== "") {
152822
+ console.warn(`[magic-context] historian.model "${historianModelOverride}" lacks provider prefix ("provider/model-id"); using fallback chain for chunk-budget derivation.`);
152823
+ }
152824
+ const chain = AGENT_MODEL_REQUIREMENTS[HISTORIAN_AGENT]?.fallbackChain;
152825
+ if (!chain || chain.length === 0)
152826
+ return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
152827
+ const expanded = expandFallbackChain(chain);
152828
+ let minLimit;
152829
+ for (const key of expanded) {
152830
+ const [providerID, ...rest] = key.split("/");
152831
+ const modelID = rest.join("/");
152832
+ if (!providerID || !modelID)
152833
+ continue;
152834
+ const limit = getModelsDevContextLimit(providerID, modelID);
152835
+ if (typeof limit !== "number" || limit <= 0)
152836
+ continue;
152837
+ if (minLimit === undefined || limit < minLimit)
152838
+ minLimit = limit;
152839
+ }
152840
+ return minLimit ?? DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
152841
+ }
152842
+ var TRIGGER_BUDGET_PERCENTAGE = 0.05, TRIGGER_BUDGET_MIN = 5000, TRIGGER_BUDGET_MAX = 50000, HISTORIAN_CHUNK_PERCENTAGE = 0.25, HISTORIAN_CHUNK_MIN = 8000, HISTORIAN_CHUNK_MAX = 50000, DEFAULT_HISTORIAN_CONTEXT_FALLBACK = 128000;
152843
+ var init_derive_budgets = __esm(() => {
152844
+ init_model_requirements();
152845
+ init_models_dev_cache();
152846
+ });
152847
+
152848
+ // src/features/magic-context/compaction-marker.ts
152849
+ import { Database as Database4 } from "bun:sqlite";
152850
+ import { join as join13 } from "path";
152851
+ function randomBase62(length) {
152852
+ const chars = [];
152853
+ for (let i = 0;i < length; i++) {
152854
+ chars.push(BASE62_CHARS[Math.floor(Math.random() * BASE62_CHARS.length)]);
152855
+ }
152856
+ return chars.join("");
152857
+ }
152858
+ function generateId(prefix, timestampMs, counter = 0n) {
152859
+ const encoded = BigInt(timestampMs) * 0x1000n + counter;
152860
+ const hex3 = encoded.toString(16).padStart(14, "0");
152861
+ return `${prefix}_${hex3}${randomBase62(14)}`;
152862
+ }
152863
+ function generateMessageId(timestampMs, counter = 0n) {
152864
+ return generateId("msg", timestampMs, counter);
152865
+ }
152866
+ function generatePartId(timestampMs, counter = 0n) {
152867
+ return generateId("prt", timestampMs, counter);
152868
+ }
152869
+ function getOpenCodeDbPath3() {
152870
+ return join13(getDataDir(), "opencode", "opencode.db");
152871
+ }
152872
+ function getWritableOpenCodeDb() {
152873
+ const dbPath = getOpenCodeDbPath3();
152874
+ if (cachedWriteDb?.path === dbPath) {
152875
+ return cachedWriteDb.db;
152876
+ }
152877
+ if (cachedWriteDb) {
152878
+ try {
152879
+ cachedWriteDb.db.close(false);
152880
+ } catch {}
152881
+ }
152882
+ const db = new Database4(dbPath);
152883
+ db.exec("PRAGMA journal_mode=WAL");
152884
+ db.exec("PRAGMA busy_timeout=5000");
152885
+ cachedWriteDb = { path: dbPath, db };
152886
+ return db;
152887
+ }
152888
+ function findBoundaryUserMessage(sessionId, endOrdinal) {
152889
+ const db = getWritableOpenCodeDb();
152890
+ const rows = db.prepare("SELECT id, time_created, data FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId);
152891
+ const filtered = rows.filter((row) => {
152892
+ try {
152893
+ const info = JSON.parse(row.data);
152894
+ return !(info.summary === true && info.finish === "stop");
152895
+ } catch {
152896
+ return true;
152897
+ }
152898
+ });
152899
+ let bestMatch = null;
152900
+ for (let i = 0;i < filtered.length && i < endOrdinal; i++) {
152901
+ const row = filtered[i];
152902
+ try {
152903
+ const info = JSON.parse(row.data);
152904
+ if (info.role === "user") {
152905
+ bestMatch = { id: row.id, timeCreated: row.time_created };
152906
+ }
152907
+ } catch {}
152908
+ }
152909
+ return bestMatch;
152910
+ }
152911
+ function injectCompactionMarker(args) {
152912
+ const boundary = findBoundaryUserMessage(args.sessionId, args.endOrdinal);
152913
+ if (!boundary) {
152914
+ log(`[magic-context] compaction-marker: no user message found at or before ordinal ${args.endOrdinal}`);
152915
+ return null;
152916
+ }
152917
+ const db = getWritableOpenCodeDb();
152918
+ const boundaryTime = boundary.timeCreated;
152919
+ const summaryMsgId = generateMessageId(boundaryTime + 1, 1n);
152920
+ const compactionPartId = generatePartId(boundaryTime, 1n);
152921
+ const summaryPartId = generatePartId(boundaryTime + 1, 2n);
152922
+ const summaryMsgData = JSON.stringify({
152923
+ role: "assistant",
152924
+ parentID: boundary.id,
152925
+ summary: true,
152926
+ finish: "stop",
152927
+ mode: "compaction",
152928
+ agent: "compaction",
152929
+ path: { cwd: args.directory, root: args.directory },
152930
+ cost: 0,
152931
+ tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
152932
+ modelID: "magic-context",
152933
+ providerID: "magic-context",
152934
+ time: { created: boundaryTime + 1 }
152935
+ });
152936
+ try {
152937
+ db.transaction(() => {
152938
+ db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(compactionPartId, boundary.id, args.sessionId, boundaryTime, boundaryTime, '{"type":"compaction","auto":true}');
152939
+ db.prepare("INSERT INTO message (id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?)").run(summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, summaryMsgData);
152940
+ db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(summaryPartId, summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, JSON.stringify({ type: "text", text: args.summaryText }));
152941
+ })();
152942
+ log(`[magic-context] compaction-marker: injected boundary at user msg ${boundary.id} (ordinal ~${args.endOrdinal}), summary msg ${summaryMsgId}`);
152943
+ return {
152944
+ boundaryMessageId: boundary.id,
152945
+ summaryMessageId: summaryMsgId,
152946
+ compactionPartId,
152947
+ summaryPartId
152948
+ };
152949
+ } catch (error48) {
152950
+ log(`[magic-context] compaction-marker: injection failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
152951
+ return null;
152952
+ }
152953
+ }
152954
+ function removeCompactionMarker(state) {
152955
+ try {
152956
+ const db = getWritableOpenCodeDb();
152957
+ db.transaction(() => {
152958
+ db.prepare("DELETE FROM part WHERE id = ?").run(state.summaryPartId);
152959
+ db.prepare("DELETE FROM message WHERE id = ?").run(state.summaryMessageId);
152960
+ db.prepare("DELETE FROM part WHERE id = ?").run(state.compactionPartId);
152961
+ })();
152962
+ return true;
152963
+ } catch (error48) {
152964
+ log(`[magic-context] compaction-marker: removal failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
152965
+ return false;
152966
+ }
152967
+ }
152968
+ var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", cachedWriteDb = null;
152969
+ var init_compaction_marker = __esm(() => {
152970
+ init_data_path();
152971
+ init_logger();
152972
+ });
152973
+
152974
+ // src/hooks/magic-context/compaction-marker-manager.ts
152975
+ function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, directory) {
152976
+ const existing = getPersistedCompactionMarkerState(db, sessionId);
152977
+ if (existing) {
152978
+ if (existing.boundaryOrdinal === lastCompartmentEnd) {
152979
+ return;
152980
+ }
152981
+ try {
152982
+ removeCompactionMarker(existing);
152983
+ setPersistedCompactionMarkerState(db, sessionId, null);
152984
+ sessionLog(sessionId, `compaction-marker: removed old boundary at ordinal ${existing.boundaryOrdinal}, moving to ${lastCompartmentEnd}`);
152985
+ } catch (error48) {
152986
+ sessionLog(sessionId, `compaction-marker: failed to remove old boundary at ordinal ${existing.boundaryOrdinal}, proceeding with new injection:`, error48);
152987
+ }
152988
+ }
152989
+ const result = injectCompactionMarker({
152990
+ sessionId,
152991
+ endOrdinal: lastCompartmentEnd,
152992
+ summaryText: MARKER_SUMMARY_TEXT,
152993
+ directory: directory ?? process.cwd()
152994
+ });
152995
+ if (result) {
152996
+ setPersistedCompactionMarkerState(db, sessionId, {
152997
+ ...result,
152998
+ boundaryOrdinal: lastCompartmentEnd
152999
+ });
153000
+ sessionLog(sessionId, `compaction-marker: injected at ordinal ${lastCompartmentEnd}, boundary user msg ${result.boundaryMessageId}`);
153001
+ }
153002
+ }
153003
+ function removeCompactionMarkerForSession(db, sessionId) {
153004
+ const existing = getPersistedCompactionMarkerState(db, sessionId);
153005
+ if (existing) {
153006
+ try {
153007
+ removeCompactionMarker(existing);
153008
+ setPersistedCompactionMarkerState(db, sessionId, null);
153009
+ sessionLog(sessionId, "compaction-marker: removed on session cleanup");
153010
+ } catch (error48) {
153011
+ setPersistedCompactionMarkerState(db, sessionId, null);
153012
+ sessionLog(sessionId, "compaction-marker: removal failed during session cleanup, cleared persisted state:", error48);
153013
+ }
153014
+ }
153015
+ }
153016
+ var MARKER_SUMMARY_TEXT = "[Compacted by magic-context \u2014 session history is managed by the plugin]";
153017
+ var init_compaction_marker_manager = __esm(() => {
153018
+ init_compaction_marker();
153019
+ init_storage_meta_persisted();
153020
+ init_logger();
153021
+ });
153022
+
153023
+ // src/hooks/magic-context/note-nudger.ts
153024
+ function getPersistedNoteNudgeDeliveredAt(_db, sessionId) {
153025
+ return lastDeliveredAt.get(sessionId) ?? 0;
153026
+ }
153027
+ function recordNoteNudgeDeliveryTime(sessionId) {
153028
+ lastDeliveredAt.set(sessionId, Date.now());
153029
+ }
153030
+ function onNoteTrigger(db, sessionId, trigger) {
153031
+ setPersistedNoteNudgeTrigger(db, sessionId);
153032
+ sessionLog(sessionId, `note-nudge: trigger fired (${trigger}), triggerPending=true`);
153033
+ }
153034
+ function peekNoteNudgeText(db, sessionId, currentUserMessageId, projectIdentity) {
153035
+ const state = getPersistedNoteNudge(db, sessionId);
153036
+ if (!state.triggerPending)
153037
+ return null;
153038
+ if (!state.triggerMessageId && currentUserMessageId) {
153039
+ setPersistedNoteNudgeTriggerMessageId(db, sessionId, currentUserMessageId);
153040
+ state.triggerMessageId = currentUserMessageId;
153041
+ }
153042
+ if (state.triggerMessageId && currentUserMessageId && state.triggerMessageId === currentUserMessageId) {
153043
+ sessionLog(sessionId, `note-nudge: deferring \u2014 current user message ${currentUserMessageId} is same as trigger-time message`);
153044
+ return null;
153045
+ }
153046
+ const deliveredAt = getPersistedNoteNudgeDeliveredAt(db, sessionId);
153047
+ if (deliveredAt > 0 && Date.now() - deliveredAt < NOTE_NUDGE_COOLDOWN_MS) {
153048
+ sessionLog(sessionId, `note-nudge: suppressing \u2014 last delivered ${Math.round((Date.now() - deliveredAt) / 1000)}s ago (cooldown ${NOTE_NUDGE_COOLDOWN_MS / 60000}m)`);
153049
+ clearPersistedNoteNudge(db, sessionId);
153050
+ return null;
153051
+ }
153052
+ const notes = getSessionNotes(db, sessionId);
153053
+ const readySmartCount = projectIdentity ? getReadySmartNotes(db, projectIdentity).length : 0;
153054
+ const totalCount = notes.length + readySmartCount;
153055
+ if (totalCount === 0) {
153056
+ sessionLog(sessionId, "note-nudge: triggerPending but no notes found, skipping");
153057
+ clearPersistedNoteNudge(db, sessionId);
153058
+ return null;
153059
+ }
153060
+ const parts = [];
153061
+ if (notes.length > 0) {
153062
+ parts.push(`${notes.length} deferred note${notes.length === 1 ? "" : "s"}`);
153063
+ }
153064
+ if (readySmartCount > 0) {
153065
+ parts.push(`${readySmartCount} ready smart note${readySmartCount === 1 ? "" : "s"}`);
153066
+ }
153067
+ sessionLog(sessionId, `note-nudge: delivering nudge for ${parts.join(" and ")}`);
153068
+ return `You have ${parts.join(" and ")}. Review with ctx_note read \u2014 some may be actionable now.`;
153069
+ }
153070
+ function markNoteNudgeDelivered(db, sessionId, text, messageId) {
153071
+ setPersistedDeliveredNoteNudge(db, sessionId, messageId ? text : "", messageId ?? "");
153072
+ recordNoteNudgeDeliveryTime(sessionId);
153073
+ sessionLog(sessionId, messageId ? `note-nudge: marked delivered, sticky anchor=${messageId}` : "note-nudge: marked delivered without anchor");
153074
+ }
153075
+ function getStickyNoteNudge(db, sessionId) {
153076
+ const state = getPersistedNoteNudge(db, sessionId);
153077
+ if (!state.stickyText || !state.stickyMessageId)
153078
+ return null;
153079
+ return { text: state.stickyText, messageId: state.stickyMessageId };
153080
+ }
153081
+ function clearNoteNudgeState(db, sessionId, options) {
153082
+ if (options?.persist !== false) {
153083
+ clearPersistedNoteNudge(db, sessionId);
153084
+ }
153085
+ lastDeliveredAt.delete(sessionId);
153086
+ }
153087
+ var NOTE_NUDGE_COOLDOWN_MS, lastDeliveredAt;
153088
+ var init_note_nudger = __esm(() => {
153089
+ init_storage_meta_persisted();
153090
+ init_storage_notes();
153091
+ init_logger();
153092
+ NOTE_NUDGE_COOLDOWN_MS = 15 * 60 * 1000;
153093
+ lastDeliveredAt = new Map;
153094
+ });
153095
+
153096
+ // src/features/magic-context/memory/embedding-backfill.ts
153097
+ async function ensureMemoryEmbeddings(args) {
153098
+ if (!isEmbeddingEnabled()) {
153099
+ return args.existingEmbeddings;
153100
+ }
153101
+ const missingMemories = args.memories.filter((memory) => !args.existingEmbeddings.has(memory.id));
153102
+ if (missingMemories.length === 0) {
153103
+ return args.existingEmbeddings;
153104
+ }
153105
+ try {
153106
+ const embeddings = await embedBatch(missingMemories.map((memory) => memory.content));
153107
+ const modelId = getEmbeddingModelId();
153108
+ const staged = new Map;
153109
+ args.db.transaction(() => {
153110
+ for (const [index, memory] of missingMemories.entries()) {
153111
+ const embedding = embeddings[index];
153112
+ if (!embedding) {
153113
+ continue;
153114
+ }
153115
+ saveEmbedding(args.db, memory.id, embedding, modelId);
153116
+ staged.set(memory.id, embedding);
153117
+ }
153118
+ })();
153119
+ for (const [id, embedding] of staged) {
153120
+ args.existingEmbeddings.set(id, embedding);
153121
+ }
153122
+ } catch (error48) {
153123
+ log("[magic-context] failed to backfill memory embeddings:", error48);
153124
+ }
153125
+ return args.existingEmbeddings;
153126
+ }
153127
+ var init_embedding_backfill = __esm(() => {
153128
+ init_logger();
153129
+ init_embedding();
153130
+ init_storage_memory_embeddings();
153131
+ });
153132
+
153133
+ // src/features/magic-context/memory/promotion.ts
153134
+ function isPromotableCategory(category) {
153135
+ return PROMOTABLE_CATEGORIES.some((promotableCategory) => promotableCategory === category);
153136
+ }
153137
+ function resolveExpiresAt(category) {
153138
+ const ttl = CATEGORY_DEFAULT_TTL[category];
153139
+ return ttl === undefined ? null : Date.now() + ttl;
153140
+ }
153141
+ function promoteSessionFactsToMemory(db, sessionId, projectPath, facts) {
153142
+ for (const fact of facts) {
153143
+ if (!isPromotableCategory(fact.category)) {
153144
+ continue;
153145
+ }
153146
+ try {
153147
+ const normalizedHash = computeNormalizedHash(fact.content);
153148
+ const existingMemory = getMemoryByHash(db, projectPath, fact.category, normalizedHash);
153149
+ if (existingMemory) {
153150
+ updateMemorySeenCount(db, existingMemory.id);
153151
+ continue;
153152
+ }
153153
+ const memoryInput = {
153154
+ projectPath,
153155
+ category: fact.category,
153156
+ content: fact.content,
153157
+ sourceSessionId: sessionId,
153158
+ sourceType: "historian",
153159
+ expiresAt: resolveExpiresAt(fact.category)
153160
+ };
153161
+ const memory = insertMemory(db, memoryInput);
153162
+ embedAndStoreMemory(db, sessionId, memory.id, memory.content);
153163
+ } catch (error48) {
153164
+ sessionLog(sessionId, `memory promotion failed for fact "${fact.content.slice(0, 60)}":`, error48);
153165
+ }
153166
+ }
153167
+ }
153168
+ async function embedAndStoreMemory(db, sessionId, memoryId, content) {
153169
+ try {
153170
+ const embedding = await embedText(content);
153171
+ if (embedding) {
153172
+ saveEmbedding(db, memoryId, embedding, getEmbeddingModelId());
153173
+ }
153174
+ } catch (error48) {
153175
+ sessionLog(sessionId, `memory embedding failed for memory ${memoryId}:`, error48);
153176
+ }
153177
+ }
153178
+ var init_promotion = __esm(() => {
153179
+ init_logger();
153180
+ init_constants();
153181
+ init_embedding();
153182
+ init_storage_memory();
153183
+ init_storage_memory_embeddings();
153184
+ });
153185
+
153186
+ // src/features/magic-context/memory/storage-memory-fts.ts
153187
+ function getSearchStatement(db) {
153188
+ let stmt = searchStatements.get(db);
153189
+ if (!stmt) {
153190
+ const selectColumns = Object.entries(COLUMN_MAP).map(([property, column]) => `memories.${column} AS ${property}`).join(", ");
153191
+ stmt = db.prepare(`SELECT ${selectColumns} FROM memories_fts INNER JOIN memories ON memories.id = memories_fts.rowid WHERE memories.project_path = ? AND memories.status IN ('active', 'permanent') AND (memories.expires_at IS NULL OR memories.expires_at > ?) AND memories_fts MATCH ? ORDER BY bm25(memories_fts), memories.updated_at DESC, memories.id ASC LIMIT ?`);
153192
+ searchStatements.set(db, stmt);
153193
+ }
153194
+ return stmt;
153195
+ }
153196
+ function sanitizeFtsQuery(query) {
153197
+ const tokens = query.split(/\s+/).filter((token) => token.length > 0);
153198
+ if (tokens.length === 0)
153199
+ return "";
153200
+ return tokens.map((token) => `"${token.replace(/"/g, '""')}"`).join(" ");
153201
+ }
153202
+ function searchMemoriesFTS(db, projectPath, query, limit = DEFAULT_SEARCH_LIMIT) {
153203
+ const trimmedQuery = query.trim();
153204
+ if (trimmedQuery.length === 0 || limit <= 0) {
153205
+ return [];
153206
+ }
153207
+ const sanitized = sanitizeFtsQuery(trimmedQuery);
153208
+ if (sanitized.length === 0) {
153209
+ return [];
153210
+ }
153211
+ const rows = getSearchStatement(db).all(projectPath, Date.now(), sanitized, limit).filter(isMemoryRow);
153212
+ return rows.map(toMemory);
153213
+ }
153214
+ var DEFAULT_SEARCH_LIMIT = 10, searchStatements;
153215
+ var init_storage_memory_fts = __esm(() => {
153216
+ init_storage_memory();
153217
+ searchStatements = new WeakMap;
153218
+ });
153219
+ // src/features/magic-context/memory/index.ts
153220
+ var init_memory = __esm(() => {
153221
+ init_project_identity();
153222
+ init_promotion();
153223
+ init_constants();
153224
+ init_embedding();
153225
+ init_embedding_backfill();
153226
+ init_embedding_cache();
153227
+ init_storage_memory();
153228
+ init_storage_memory_embeddings();
153229
+ init_storage_memory_fts();
153230
+ });
153231
+
153232
+ // src/hooks/magic-context/caveman.ts
153233
+ function protectRegions(text) {
153234
+ const preserved = [];
153235
+ let working = text;
153236
+ for (const pattern of PRESERVATION_PATTERNS) {
153237
+ working = working.replace(pattern, (match) => {
153238
+ const placeholder = `\x00MC_PRES_${preserved.length}\x00`;
153239
+ preserved.push({ placeholder, original: match });
153240
+ return placeholder;
153241
+ });
153242
+ }
153243
+ return { text: working, preserved };
153244
+ }
153245
+ function restoreRegions(text, preserved) {
153246
+ let working = text;
153247
+ for (let i = preserved.length - 1;i >= 0; i--) {
153248
+ working = working.split(preserved[i].placeholder).join(preserved[i].original);
153249
+ }
153250
+ return working;
153251
+ }
153252
+ function buildPhraseDropRegex(phrases) {
153253
+ const escaped = phrases.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
153254
+ return new RegExp(`(\\s+)?\\b(?:${escaped.join("|")})\\b`, "gi");
153255
+ }
153256
+ function dropPhrases(text, phrases) {
153257
+ return text.replace(buildPhraseDropRegex(phrases), "");
153258
+ }
153259
+ function dropArticles(text) {
153260
+ let working = text.replace(/\b(?:the|a|an)\b\s+/gi, "");
153261
+ working = working.replace(/ +/g, " ");
153262
+ return working;
153263
+ }
153264
+ function dropAuxiliaries(text) {
153265
+ const sorted = [...AUXILIARIES].sort((a, b) => b.length - a.length);
153266
+ const escaped = sorted.map((a) => a.replace(/\s+/g, "\\s+"));
153267
+ const pattern = new RegExp(`\\s+\\b(?:${escaped.join("|")})\\b\\s+(?=\\w+(?:ed|en|ing|ized|ised)\\b)`, "gi");
153268
+ let working = text.replace(pattern, " ");
153269
+ working = working.replace(/ +/g, " ");
153270
+ return working;
153271
+ }
153272
+ function applyPhraseShortenings(text) {
153273
+ let working = text;
153274
+ for (const [pattern, replacement] of PHRASE_SHORTENINGS) {
153275
+ working = working.replace(pattern, replacement);
153276
+ }
153277
+ return working;
153278
+ }
153279
+ function applyUltraConnectives(text) {
153280
+ let working = text;
153281
+ for (const [pattern, replacement] of ULTRA_CONNECTIVE_REPLACEMENTS) {
153282
+ working = working.replace(pattern, replacement);
153283
+ }
153284
+ return working;
153285
+ }
153286
+ function countWordOccurrences(text, term) {
153287
+ const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
153288
+ const matches = text.match(new RegExp(`\\b${escaped}\\b`, "gi"));
153289
+ return matches ? matches.length : 0;
153290
+ }
153291
+ function applyUltraAbbreviations(text) {
153292
+ let working = text;
153293
+ for (const [term, abbreviation] of Object.entries(ULTRA_ABBREVIATIONS)) {
153294
+ if (countWordOccurrences(working, term) < 3)
153295
+ continue;
153296
+ const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
153297
+ working = working.replace(new RegExp(`\\b${escaped}\\b`, "gi"), (match) => {
153298
+ return match[0] === match[0].toUpperCase() ? abbreviation[0].toUpperCase() + abbreviation.slice(1) : abbreviation;
153299
+ });
153300
+ }
153301
+ return working;
153302
+ }
153303
+ function transformPreservingUserLines(text, transform2) {
153304
+ const lines = text.split(`
153305
+ `);
153306
+ const output = [];
153307
+ let buffer2 = [];
153308
+ const flushBuffer = () => {
153309
+ if (buffer2.length === 0)
153310
+ return;
153311
+ const joined = buffer2.join(`
153312
+ `);
153313
+ output.push(transform2(joined));
153314
+ buffer2 = [];
153315
+ };
153316
+ for (const line of lines) {
153317
+ if (line.startsWith("U: ")) {
153318
+ flushBuffer();
153319
+ output.push(line);
153320
+ } else {
153321
+ buffer2.push(line);
153322
+ }
153323
+ }
153324
+ flushBuffer();
153325
+ return output.join(`
153326
+ `);
153327
+ }
153328
+ function normalizeWhitespace(text) {
153329
+ return text.split(`
153330
+ `).map((line) => line.replace(/[ \t]+/g, " ").replace(/[ \t]+$/, "")).join(`
153331
+ `).replace(/\n{3,}/g, `
153332
+
153333
+ `);
153334
+ }
153335
+ function cavemanCompress(text, level) {
153336
+ if (text.length === 0)
153337
+ return text;
153338
+ const { text: protectedText, preserved } = protectRegions(text);
153339
+ const transformed = transformPreservingUserLines(protectedText, (chunk) => {
153340
+ let working = chunk;
153341
+ working = dropPhrases(working, FILLER_WORDS);
153342
+ working = dropPhrases(working, HEDGING_PHRASES);
153343
+ working = dropPhrases(working, PLEASANTRIES);
153344
+ working = applyPhraseShortenings(working);
153345
+ if (level === "full" || level === "ultra") {
153346
+ working = dropAuxiliaries(working);
153347
+ working = dropArticles(working);
153348
+ }
153349
+ if (level === "ultra") {
153350
+ working = applyUltraConnectives(working);
153351
+ working = applyUltraAbbreviations(working);
153352
+ }
153353
+ return working;
153354
+ });
153355
+ const restored = restoreRegions(transformed, preserved);
153356
+ return normalizeWhitespace(restored).trim();
153357
+ }
153358
+ var PRESERVATION_PATTERNS, FILLER_WORDS, HEDGING_PHRASES, PLEASANTRIES, AUXILIARIES, PHRASE_SHORTENINGS, ULTRA_CONNECTIVE_REPLACEMENTS, ULTRA_ABBREVIATIONS;
153359
+ var init_caveman = __esm(() => {
153360
+ PRESERVATION_PATTERNS = [
153361
+ /```[\s\S]*?```/g,
153362
+ /`[^`\n]+`/g,
153363
+ /https?:\/\/\S+/g,
153364
+ /\u00A7\d+\u00A7/g,
153365
+ /\b(?:msg|ses|toolu)_[A-Za-z0-9]+/g,
153366
+ /(?:\.{1,2}\/)?(?:[\w.-]+\/)+[\w.-]+\.\w{1,6}/g,
153367
+ /(?<![a-z0-9])[0-9a-f]{7,40}(?![a-z0-9])/gi
153368
+ ];
153369
+ FILLER_WORDS = [
153370
+ "just",
153371
+ "really",
153372
+ "basically",
153373
+ "actually",
153374
+ "essentially",
153375
+ "simply",
153376
+ "clearly",
153377
+ "obviously",
153378
+ "quite",
153379
+ "very",
153380
+ "somewhat",
153381
+ "rather",
153382
+ "fairly",
153383
+ "sort of",
153384
+ "kind of",
153385
+ "a bit"
153386
+ ];
153387
+ HEDGING_PHRASES = [
153388
+ "i think",
153389
+ "i believe",
153390
+ "i feel",
153391
+ "probably",
153392
+ "perhaps",
153393
+ "maybe",
153394
+ "it seems",
153395
+ "it appears",
153396
+ "arguably",
153397
+ "i suppose",
153398
+ "i guess"
153399
+ ];
153400
+ PLEASANTRIES = ["please", "thanks", "thank you", "kindly", "if possible"];
153401
+ AUXILIARIES = [
153402
+ "was",
153403
+ "were",
153404
+ "is",
153405
+ "are",
153406
+ "am",
153407
+ "be",
153408
+ "been",
153409
+ "being",
153410
+ "has been",
153411
+ "had been",
153412
+ "have been",
153413
+ "will be",
153414
+ "would be",
153415
+ "could be",
153416
+ "should be",
153417
+ "might be",
153418
+ "may be"
153419
+ ];
153420
+ PHRASE_SHORTENINGS = [
153421
+ [/\bin order to\b/gi, "to"],
153422
+ [/\bdue to the fact that\b/gi, "because"],
153423
+ [/\bat this point in time\b/gi, "now"],
153424
+ [/\bat the moment\b/gi, "now"],
153425
+ [/\bin the event that\b/gi, "if"],
153426
+ [/\bfor the purpose of\b/gi, "for"],
153427
+ [/\bwith regard to\b/gi, "about"],
153428
+ [/\bin spite of the fact that\b/gi, "though"],
153429
+ [/\bon the grounds that\b/gi, "because"],
153430
+ [/\bfor the reason that\b/gi, "because"]
153431
+ ];
153432
+ ULTRA_CONNECTIVE_REPLACEMENTS = [
153433
+ [/\b(?:and then|then after|afterwards)\b/gi, "\u2192"],
153434
+ [/\bbecause of\b/gi, "//"],
153435
+ [/\btherefore\b/gi, "\u2192"],
153436
+ [/\bbecause\b/gi, "//"],
153437
+ [/\bhowever\b/gi, "but"],
153438
+ [/\bfurthermore\b/gi, "+"],
153439
+ [/\badditionally\b/gi, "+"],
153440
+ [/\bas well as\b/gi, "+"],
153441
+ [/ and /gi, " + "],
153442
+ [/ or /gi, " | "]
153443
+ ];
153444
+ ULTRA_ABBREVIATIONS = {
153445
+ historian: "hist",
153446
+ compartment: "cmpt",
153447
+ compartments: "cmpts",
153448
+ compressor: "cmp",
153449
+ compression: "cmp",
153450
+ context: "ctx",
153451
+ message: "msg",
153452
+ messages: "msgs",
153453
+ session: "ses",
153454
+ configuration: "cfg",
153455
+ config: "cfg",
153456
+ implementation: "impl",
153457
+ implemented: "impl",
153458
+ repository: "repo",
153459
+ database: "db",
153460
+ directory: "dir"
153461
+ };
153462
+ });
153463
+
153464
+ // src/hooks/magic-context/compartment-runner-compressor.ts
153465
+ function cavemanLevelForDepth(outputDepth) {
153466
+ if (outputDepth <= 1)
153467
+ return null;
153468
+ if (outputDepth === 2)
153469
+ return "lite";
153470
+ if (outputDepth === 3)
153471
+ return "full";
153472
+ if (outputDepth === 4)
153473
+ return "ultra";
153474
+ return null;
153475
+ }
153476
+ async function runCompressionPassIfNeeded(deps) {
153477
+ const { db, sessionId, historyBudgetTokens } = deps;
153478
+ const minCompartmentRatio = deps.minCompartmentRatio ?? DEFAULT_COMPRESSOR_MIN_COMPARTMENT_RATIO;
153479
+ const maxMergeDepth = deps.maxMergeDepth ?? DEFAULT_COMPRESSOR_MAX_MERGE_DEPTH;
153480
+ const compartments = getCompartments(db, sessionId);
153481
+ if (compartments.length <= 1)
153482
+ return false;
153483
+ const facts = getSessionFacts(db, sessionId);
153484
+ let totalTokens = 0;
153485
+ for (const c of compartments) {
153486
+ totalTokens += estimateTokens(`<compartment start="${c.startMessage}" end="${c.endMessage}" title="${c.title}">
153487
+ ${c.content}
153488
+ </compartment>
153489
+ `);
153490
+ }
153491
+ for (const f of facts) {
153492
+ totalTokens += estimateTokens(`* ${f.content}
153493
+ `);
153494
+ }
153495
+ if (totalTokens <= historyBudgetTokens) {
153496
+ sessionLog(sessionId, `compressor: history block ~${totalTokens} tokens within budget ${historyBudgetTokens}, skipping`);
153497
+ return false;
153498
+ }
153499
+ const lastEndMessage = compartments[compartments.length - 1].endMessage;
153500
+ const floor = Math.max(1, Math.ceil(lastEndMessage / minCompartmentRatio));
153501
+ if (compartments.length <= floor) {
153502
+ sessionLog(sessionId, `compressor: at floor (${compartments.length} compartments, floor=${floor} from ${lastEndMessage} msgs), skipping`);
153503
+ return false;
153504
+ }
153505
+ const overage = totalTokens - historyBudgetTokens;
153506
+ sessionLog(sessionId, `compressor: history block ~${totalTokens} tokens exceeds budget ${historyBudgetTokens} by ~${overage} tokens`);
153507
+ const maxCompartmentsPerPass = deps.maxCompartmentsPerPass ?? DEFAULT_COMPRESSOR_MAX_COMPARTMENTS_PER_PASS;
153508
+ const graceCompartments = deps.graceCompartments ?? DEFAULT_COMPRESSOR_GRACE_COMPARTMENTS;
153509
+ const scored = scoreCompartments(db, sessionId, compartments);
153510
+ const floorHeadroom = compartments.length - floor;
153511
+ if (floorHeadroom < 1) {
153512
+ sessionLog(sessionId, `compressor: no floor headroom (${compartments.length} compartments, floor=${floor}), skipping`);
153513
+ return false;
153514
+ }
153515
+ const contiguous = findOldestContiguousSameDepthBand(scored, {
153516
+ maxPickable: maxCompartmentsPerPass,
153517
+ maxMergeDepth,
153518
+ graceCompartments,
153519
+ floorHeadroom
153520
+ });
153521
+ if (contiguous.length < 2) {
153522
+ sessionLog(sessionId, `compressor: no eligible same-depth band found (floor=${floor}, maxDepth=${maxMergeDepth}, grace=${graceCompartments}, maxPerPass=${maxCompartmentsPerPass}), skipping`);
153523
+ return false;
153524
+ }
153525
+ const firstIndex = contiguous[0].index;
153526
+ const lastIndex = contiguous[contiguous.length - 1].index;
153527
+ const selectedCompartments = contiguous.map((s) => s.compartment);
153528
+ const selectedTokens = contiguous.reduce((t, s) => t + s.tokenEstimate, 0);
153529
+ const overallAverageDepth = contiguous.reduce((sum, s) => sum + s.averageDepth, 0) / contiguous.length;
153530
+ const outputDepth = Math.min(5, Math.max(1, Math.round(overallAverageDepth) + 1));
153531
+ const mergeRatio = COMPRESSOR_MERGE_RATIO_BY_DEPTH[outputDepth] ?? 2;
153532
+ const outputCount = mergeRatio > 0 ? Math.max(1, Math.ceil(contiguous.length / mergeRatio)) : 1;
153533
+ sessionLog(sessionId, `compressor: scored ${compartments.length}, picked ${contiguous.length} contiguous (${selectedCompartments[0].startMessage}-${selectedCompartments[selectedCompartments.length - 1].endMessage}, ~${selectedTokens} tokens), avg_depth=${overallAverageDepth.toFixed(1)} \u2192 output_depth=${outputDepth} (ratio=${mergeRatio}, target=${outputCount} compartments)`);
153534
+ if (outputDepth === 5) {
153535
+ return finalizeCompression({
153536
+ db,
153537
+ sessionId,
153538
+ compartments,
153539
+ leadingCount: firstIndex,
153540
+ trailingIndex: lastIndex + 1,
153541
+ selectedCompartments,
153542
+ compressed: selectedCompartments.map((c) => ({
153543
+ startMessage: c.startMessage,
153544
+ endMessage: c.endMessage,
153545
+ startMessageId: c.startMessageId,
153546
+ endMessageId: c.endMessageId,
153547
+ title: c.title,
153548
+ content: ""
153549
+ })),
153550
+ originalStart: selectedCompartments[0].startMessage,
153551
+ originalEnd: selectedCompartments[selectedCompartments.length - 1].endMessage,
153552
+ facts,
153553
+ logLabel: `depth-5 title-only collapse (${selectedCompartments.length} \u2192 ${selectedCompartments.length})`
153554
+ });
153555
+ }
153556
+ try {
153557
+ const targetTokens = Math.max(200, Math.floor(selectedTokens / mergeRatio));
153558
+ const llmCompressed = await runCompressorPass({
153559
+ ...deps,
153560
+ compartments: selectedCompartments,
153561
+ currentTokens: selectedTokens,
153562
+ targetTokens,
153563
+ outputCount,
153564
+ outputDepth
153565
+ });
153566
+ if (!llmCompressed) {
153567
+ sessionLog(sessionId, "compressor: LLM pass failed, keeping existing compartments");
153568
+ return false;
153569
+ }
153570
+ const level = cavemanLevelForDepth(outputDepth);
153571
+ const finalCompressed = level ? llmCompressed.map((c) => ({ ...c, content: cavemanCompress(c.content, level) })) : llmCompressed;
153572
+ return finalizeCompression({
153573
+ db,
153574
+ sessionId,
153575
+ compartments,
153576
+ leadingCount: firstIndex,
153577
+ trailingIndex: lastIndex + 1,
153578
+ selectedCompartments,
153579
+ compressed: finalCompressed,
153580
+ originalStart: selectedCompartments[0].startMessage,
153581
+ originalEnd: selectedCompartments[selectedCompartments.length - 1].endMessage,
153582
+ facts,
153583
+ logLabel: `depth-${outputDepth} (${selectedCompartments.length} \u2192 ${finalCompressed.length})`
153584
+ });
153585
+ } catch (error48) {
153586
+ sessionLog(sessionId, "compressor: unexpected error:", getErrorMessage(error48));
153587
+ return false;
153588
+ }
153589
+ }
153590
+ function scoreCompartments(db, sessionId, compartments) {
153591
+ let maxDepthAcrossSession = 0;
153592
+ for (const c of compartments) {
153593
+ const d = getAverageCompressionDepth(db, sessionId, c.startMessage, c.endMessage);
153594
+ if (d > maxDepthAcrossSession)
153595
+ maxDepthAcrossSession = d;
153596
+ }
153597
+ return compartments.map((compartment, index) => {
153598
+ const tokenEstimate = estimateTokens(`<compartment start="${compartment.startMessage}" end="${compartment.endMessage}" title="${compartment.title}">
153599
+ ${compartment.content}
153600
+ </compartment>
153601
+ `);
153602
+ const averageDepth = getAverageCompressionDepth(db, sessionId, compartment.startMessage, compartment.endMessage);
153603
+ const normalizedAge = compartments.length > 1 ? 1 - index / (compartments.length - 1) : 1;
153604
+ const normalizedDepth = maxDepthAcrossSession > 0 ? 1 - averageDepth / maxDepthAcrossSession : 1;
153605
+ const score = 0.7 * normalizedAge + 0.3 * normalizedDepth;
153606
+ return { compartment, index, tokenEstimate, averageDepth, score };
153607
+ });
153608
+ }
153609
+ function findOldestContiguousSameDepthBand(scored, constraints) {
153610
+ const { maxPickable, maxMergeDepth, graceCompartments, floorHeadroom } = constraints;
153611
+ const hardMaxPick = Math.max(0, Math.min(maxPickable, floorHeadroom));
153612
+ if (hardMaxPick < 2)
153613
+ return [];
153614
+ const scanEnd = Math.max(0, scored.length - graceCompartments);
153615
+ if (scanEnd < 2)
153616
+ return [];
153617
+ let i = 0;
153618
+ while (i < scanEnd) {
153619
+ const c = scored[i];
153620
+ if (!c || c.averageDepth >= maxMergeDepth) {
153621
+ i++;
153622
+ continue;
153623
+ }
153624
+ const anchorDepth = Math.round(c.averageDepth);
153625
+ let j = i;
153626
+ while (j < scanEnd) {
153627
+ const entry = scored[j];
153628
+ if (!entry)
153629
+ break;
153630
+ if (entry.averageDepth >= maxMergeDepth)
153631
+ break;
153632
+ if (Math.round(entry.averageDepth) !== anchorDepth)
153633
+ break;
153634
+ if (j - i >= hardMaxPick)
153635
+ break;
153636
+ j++;
153637
+ }
153638
+ const runLen = j - i;
153639
+ if (runLen >= 2) {
153640
+ return scored.slice(i, j);
153641
+ }
153642
+ i = Math.max(j, i + 1);
153643
+ }
153644
+ return [];
153645
+ }
153646
+ function snapLLMOutputToInputBoundaries(llmOutput, inputCompartments) {
153647
+ const sorted = [...inputCompartments].sort((a, b) => a.startMessage - b.startMessage);
153648
+ const containing = (ord) => {
153649
+ let lo = 0;
153650
+ let hi = sorted.length - 1;
153651
+ while (lo <= hi) {
153652
+ const mid = lo + hi >> 1;
153653
+ const c = sorted[mid];
153654
+ if (!c)
153655
+ return null;
153656
+ if (ord < c.startMessage)
153657
+ hi = mid - 1;
153658
+ else if (ord > c.endMessage)
153659
+ lo = mid + 1;
153660
+ else
153661
+ return c;
153662
+ }
153663
+ return null;
153664
+ };
153665
+ const result = [];
153666
+ let snapCount = 0;
153667
+ for (const pc of llmOutput) {
153668
+ const startOwner = containing(pc.startMessage);
153669
+ const endOwner = containing(pc.endMessage);
153670
+ if (!startOwner || !endOwner) {
153671
+ return null;
153672
+ }
153673
+ if (startOwner.startMessage !== pc.startMessage)
153674
+ snapCount++;
153675
+ if (endOwner.endMessage !== pc.endMessage)
153676
+ snapCount++;
153677
+ result.push({
153678
+ startMessage: startOwner.startMessage,
153679
+ endMessage: endOwner.endMessage,
153680
+ startMessageId: startOwner.startMessageId,
153681
+ endMessageId: endOwner.endMessageId,
153682
+ title: pc.title,
153683
+ content: pc.content
153684
+ });
153685
+ }
153686
+ return { result, snapCount };
153687
+ }
153688
+ function finalizeCompression(args) {
153689
+ const {
153690
+ db,
153691
+ sessionId,
153692
+ compartments,
153693
+ leadingCount,
153694
+ trailingIndex,
153695
+ selectedCompartments: _selectedCompartments,
153696
+ compressed,
153697
+ originalStart,
153698
+ originalEnd,
153699
+ facts,
153700
+ logLabel
153701
+ } = args;
153702
+ const compressedStart = compressed[0].startMessage;
153703
+ const compressedEnd = compressed[compressed.length - 1].endMessage;
153704
+ if (compressedStart !== originalStart || compressedEnd !== originalEnd) {
153705
+ sessionLog(sessionId, `compressor: compressed range ${compressedStart}-${compressedEnd} doesn't match original ${originalStart}-${originalEnd}, aborting`);
153706
+ return false;
153707
+ }
153708
+ for (let i = 1;i < compressed.length; i++) {
153709
+ const prev = compressed[i - 1];
153710
+ const curr = compressed[i];
153711
+ if (curr.startMessage <= prev.endMessage) {
153712
+ sessionLog(sessionId, `compressor: overlap at compartment ${i}, aborting`);
153713
+ return false;
153714
+ }
153715
+ if (curr.startMessage > prev.endMessage + 1) {
153716
+ sessionLog(sessionId, `compressor: gap at compartment ${i}, aborting`);
153717
+ return false;
153718
+ }
153719
+ }
153720
+ const leading = compartments.slice(0, leadingCount);
153721
+ const trailing = compartments.slice(trailingIndex);
153722
+ const allCompartments = [
153723
+ ...leading.map((c, i) => ({
153724
+ sequence: i,
153725
+ startMessage: c.startMessage,
153726
+ endMessage: c.endMessage,
153727
+ startMessageId: c.startMessageId,
153728
+ endMessageId: c.endMessageId,
153729
+ title: c.title,
153730
+ content: c.content
153731
+ })),
153732
+ ...compressed.map((c, i) => ({
153733
+ sequence: leading.length + i,
153734
+ startMessage: c.startMessage,
153735
+ endMessage: c.endMessage,
153736
+ startMessageId: c.startMessageId,
153737
+ endMessageId: c.endMessageId,
153738
+ title: c.title,
153739
+ content: c.content
153740
+ })),
153741
+ ...trailing.map((c, i) => ({
153742
+ sequence: leading.length + compressed.length + i,
153743
+ startMessage: c.startMessage,
153744
+ endMessage: c.endMessage,
153745
+ startMessageId: c.startMessageId,
153746
+ endMessageId: c.endMessageId,
153747
+ title: c.title,
153748
+ content: c.content
153749
+ }))
153750
+ ];
153751
+ replaceAllCompartmentState(db, sessionId, allCompartments, facts.map((f) => ({ category: f.category, content: f.content })));
153752
+ incrementCompressionDepth(db, sessionId, originalStart, originalEnd);
153753
+ sessionLog(sessionId, `compressor: completed ${logLabel}`);
153754
+ return true;
153755
+ }
153756
+ async function runCompressorPass(args) {
153757
+ const {
153758
+ client,
153759
+ sessionId,
153760
+ directory,
153761
+ compartments,
153762
+ currentTokens,
153763
+ targetTokens,
153764
+ outputCount,
153765
+ outputDepth,
153766
+ historianTimeoutMs
153767
+ } = args;
153768
+ const prompt = buildCompressorPrompt(compartments, currentTokens, targetTokens, outputDepth, outputCount);
153769
+ let agentSessionId = null;
153770
+ try {
153771
+ const createResponse = await client.session.create({
153772
+ body: { parentID: sessionId, title: "magic-context-compressor" },
153773
+ query: { directory }
153774
+ });
153775
+ const createdSession = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
153776
+ agentSessionId = typeof createdSession?.id === "string" ? createdSession.id : null;
153777
+ if (!agentSessionId) {
153778
+ sessionLog(sessionId, "compressor: could not create child session");
153779
+ return null;
153780
+ }
153781
+ await promptSyncWithModelSuggestionRetry(client, {
153782
+ path: { id: agentSessionId },
153783
+ query: { directory },
153784
+ body: {
153785
+ agent: HISTORIAN_AGENT2,
153786
+ parts: [{ type: "text", text: prompt }]
153787
+ }
153788
+ }, { timeoutMs: historianTimeoutMs ?? DEFAULT_HISTORIAN_TIMEOUT_MS });
153789
+ const messagesResponse = await client.session.messages({
153790
+ path: { id: agentSessionId },
153791
+ query: { directory }
153792
+ });
153793
+ const messages = normalizeSDKResponse(messagesResponse, [], {
153794
+ preferResponseOnMissingData: true
153795
+ });
153796
+ const result = extractLatestAssistantText(messages);
153797
+ if (!result) {
153798
+ sessionLog(sessionId, "compressor: historian returned no output");
153799
+ return null;
153800
+ }
153801
+ const parsed = parseCompartmentOutput(result);
153802
+ if (parsed.compartments.length === 0) {
153803
+ sessionLog(sessionId, "compressor: historian returned no compartments");
153804
+ return null;
153805
+ }
153806
+ const snapped = snapLLMOutputToInputBoundaries(parsed.compartments, compartments);
153807
+ if (!snapped) {
153808
+ sessionLog(sessionId, "compressor: rejecting \u2014 LLM output contains ordinal(s) outside input range");
153809
+ return null;
153810
+ }
153811
+ if (snapped.snapCount > 0) {
153812
+ sessionLog(sessionId, `compressor: snapped ${snapped.snapCount} LLM boundary value(s) to input compartment boundaries`);
153813
+ }
153814
+ return snapped.result;
153815
+ } catch (error48) {
153816
+ sessionLog(sessionId, "compressor: historian call failed:", getErrorMessage(error48));
153817
+ return null;
153818
+ } finally {
153819
+ if (agentSessionId) {
153820
+ await client.session.delete({ path: { id: agentSessionId }, query: { directory } }).catch((e) => {
153821
+ sessionLog(sessionId, "compressor: session cleanup failed:", getErrorMessage(e));
153822
+ });
153823
+ }
153824
+ }
153825
+ }
153826
+ var HISTORIAN_AGENT2 = "historian";
153827
+ var init_compartment_runner_compressor = __esm(() => {
153828
+ init_magic_context();
153829
+ init_storage();
153830
+ init_shared();
153831
+ init_assistant_message_extractor();
153832
+ init_logger();
153833
+ init_caveman();
153834
+ init_compartment_parser();
153835
+ init_compartment_prompt();
153836
+ init_read_session_formatting();
153837
+ });
153838
+
153839
+ // src/hooks/magic-context/compartment-runner-drop-queue.ts
153840
+ function queueDropsForCompartmentalizedMessages(db, sessionId, upToMessageIndex) {
153841
+ const tags = getTagsBySession(db, sessionId);
153842
+ const rawTagKeys = new Set(getRawSessionTagKeysThrough(sessionId, upToMessageIndex));
153843
+ let dropsQueued = 0;
153844
+ for (const tag of tags) {
153845
+ if (tag.status !== "active")
153846
+ continue;
153847
+ if (rawTagKeys.has(tag.messageId)) {
153848
+ queuePendingOp(db, sessionId, tag.tagNumber, "drop");
153849
+ dropsQueued += 1;
153850
+ }
153851
+ }
153852
+ sessionLog(sessionId, `compartment agent: queued ${dropsQueued} drops for messages 0-${upToMessageIndex}`);
153853
+ }
153854
+ var init_compartment_runner_drop_queue = __esm(() => {
153855
+ init_storage_ops();
153856
+ init_storage_tags();
153857
+ init_logger();
153858
+ init_read_session_chunk();
153859
+ });
153860
+
152762
153861
  // src/hooks/magic-context/compartment-runner-incremental.ts
152763
153862
  function shouldSuppressHistorianAlert(sessionId) {
152764
153863
  const lastAlert = lastHistorianAlertBySession.get(sessionId);
@@ -152833,6 +153932,8 @@ ${chunk.text}`);
152833
153932
  const parentSessionResponse = await client.session.get({ path: { id: sessionId } }).catch(() => null);
152834
153933
  const parentSession = normalizeSDKResponse(parentSessionResponse, null, { preferResponseOnMissingData: true });
152835
153934
  const sessionDirectory = parentSession?.directory ?? directory;
153935
+ const maxExistingSequence = priorCompartments.reduce((max, c) => c.sequence > max ? c.sequence : max, -1);
153936
+ const sequenceOffset = priorCompartments.length === 0 ? 0 : maxExistingSequence + 1;
152836
153937
  const validatedPass = await runValidatedHistorianPass({
152837
153938
  client,
152838
153939
  parentSessionId: sessionId,
@@ -152840,10 +153941,11 @@ ${chunk.text}`);
152840
153941
  prompt,
152841
153942
  chunk,
152842
153943
  priorCompartments,
152843
- sequenceOffset: priorCompartments.length,
153944
+ sequenceOffset,
152844
153945
  dumpLabelBase: `incremental-${sessionId}-${chunk.startIndex}-${chunk.endIndex}`,
152845
153946
  timeoutMs: historianTimeoutMs,
152846
- fallbackModelId: deps.fallbackModelId
153947
+ fallbackModelId: deps.fallbackModelId,
153948
+ twoPass: deps.historianTwoPass
152847
153949
  });
152848
153950
  if (!validatedPass.ok) {
152849
153951
  incrementHistorianFailure(db, sessionId, validatedPass.error);
@@ -152885,7 +153987,9 @@ No new compartments or facts were written. Check the historian model/output and
152885
153987
  sessionId,
152886
153988
  directory: sessionDirectory,
152887
153989
  historyBudgetTokens: deps.historyBudgetTokens,
152888
- historianTimeoutMs
153990
+ historianTimeoutMs,
153991
+ minCompartmentRatio: deps.compressorMinCompartmentRatio,
153992
+ maxMergeDepth: deps.compressorMaxMergeDepth
152889
153993
  });
152890
153994
  }
152891
153995
  updateSessionMeta(db, sessionId, { compartmentInProgress: false });
@@ -152969,6 +154073,7 @@ async function executeContextRecompInternal(deps) {
152969
154073
  const promoted2 = promoteRecompStaging(db, sessionId);
152970
154074
  if (!promoted2)
152971
154075
  return null;
154076
+ clearCompressionDepth(db, sessionId);
152972
154077
  clearInjectionCache(sessionId);
152973
154078
  if (deps.directory) {
152974
154079
  promoteSessionFactsToMemory(db, sessionId, resolveProjectIdentity(deps.directory), promoted2.facts);
@@ -153060,6 +154165,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
153060
154165
  dumpLabelBase: `recomp-${sessionId}-${chunk.startIndex}-${chunk.endIndex}-pass-${passCount + 1}`,
153061
154166
  timeoutMs: historianTimeoutMs,
153062
154167
  fallbackModelId: deps.fallbackModelId,
154168
+ twoPass: deps.historianTwoPass,
153063
154169
  callbacks: {
153064
154170
  onRepairRetry: async (error48) => {
153065
154171
  await sendIgnoredMessage(client, sessionId, `## Magic Recomp
@@ -153135,6 +154241,7 @@ Nothing was written.`;
153135
154241
  replaceAllCompartmentState(db, sessionId, candidateCompartments, candidateFacts);
153136
154242
  clearRecompStaging(db, sessionId);
153137
154243
  }
154244
+ clearCompressionDepth(db, sessionId);
153138
154245
  clearInjectionCache(sessionId);
153139
154246
  const finalCompartments = promoted?.compartments ?? candidateCompartments;
153140
154247
  const finalFacts = promoted?.facts ?? candidateFacts;
@@ -153153,7 +154260,9 @@ Nothing was written.`;
153153
154260
  sessionId,
153154
154261
  directory: sessionDirectory,
153155
154262
  historyBudgetTokens: deps.historyBudgetTokens,
153156
- historianTimeoutMs
154263
+ historianTimeoutMs,
154264
+ minCompartmentRatio: deps.compressorMinCompartmentRatio,
154265
+ maxMergeDepth: deps.compressorMaxMergeDepth
153157
154266
  });
153158
154267
  }
153159
154268
  return [
@@ -153179,6 +154288,7 @@ Staging data preserved for resume on next attempt.`;
153179
154288
  }
153180
154289
  var init_compartment_runner_recomp = __esm(() => {
153181
154290
  init_compartment_storage();
154291
+ init_compression_depth_storage();
153182
154292
  init_memory();
153183
154293
  init_project_identity();
153184
154294
  init_storage_memory();
@@ -153201,12 +154311,21 @@ var exports_compartment_runner = {};
153201
154311
  __export(exports_compartment_runner, {
153202
154312
  startCompartmentAgent: () => startCompartmentAgent,
153203
154313
  runCompartmentAgent: () => runCompartmentAgent,
154314
+ registerActiveCompartmentRun: () => registerActiveCompartmentRun,
153204
154315
  getActiveCompartmentRun: () => getActiveCompartmentRun,
153205
154316
  executeContextRecomp: () => executeContextRecomp
153206
154317
  });
153207
154318
  function getActiveCompartmentRun(sessionId) {
153208
154319
  return activeRuns.get(sessionId);
153209
154320
  }
154321
+ function registerActiveCompartmentRun(sessionId, promise2) {
154322
+ const wrapped = promise2.finally(() => {
154323
+ if (activeRuns.get(sessionId) === wrapped) {
154324
+ activeRuns.delete(sessionId);
154325
+ }
154326
+ });
154327
+ activeRuns.set(sessionId, wrapped);
154328
+ }
153210
154329
  function startCompartmentAgent(deps) {
153211
154330
  const existing = activeRuns.get(deps.sessionId);
153212
154331
  if (existing) {
@@ -153222,12 +154341,12 @@ function startCompartmentAgent(deps) {
153222
154341
  });
153223
154342
  activeRuns.set(deps.sessionId, promise2);
153224
154343
  }
153225
- async function executeContextRecomp(deps) {
154344
+ async function executeContextRecomp(deps, options = {}) {
153226
154345
  const { sessionId } = deps;
153227
154346
  if (activeRuns.has(sessionId)) {
153228
154347
  return "## Magic Recomp\n\nHistorian is already running for this session. Wait for it to finish, then try `/ctx-recomp` again.";
153229
154348
  }
153230
- const promise2 = executeContextRecompInternal(deps);
154349
+ const promise2 = options.range ? executePartialRecompInternal(deps, options.range) : executeContextRecompInternal(deps);
153231
154350
  activeRuns.set(sessionId, promise2.then(() => {
153232
154351
  return;
153233
154352
  }).catch((err) => {
@@ -153244,6 +154363,7 @@ var init_compartment_runner = __esm(() => {
153244
154363
  init_storage_meta();
153245
154364
  init_logger();
153246
154365
  init_compartment_runner_incremental();
154366
+ init_compartment_runner_partial_recomp();
153247
154367
  init_compartment_runner_recomp();
153248
154368
  init_compartment_runner_incremental();
153249
154369
  activeRuns = new Map;
@@ -161011,15 +162131,15 @@ var exports_tui_config = {};
161011
162131
  __export(exports_tui_config, {
161012
162132
  ensureTuiPluginEntry: () => ensureTuiPluginEntry
161013
162133
  });
161014
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
161015
- import { dirname as dirname2, join as join16 } from "path";
162134
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
162135
+ import { dirname as dirname3, join as join16 } from "path";
161016
162136
  function resolveTuiConfigPath() {
161017
162137
  const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
161018
162138
  const jsoncPath = join16(configDir, "tui.jsonc");
161019
162139
  const jsonPath = join16(configDir, "tui.json");
161020
- if (existsSync7(jsoncPath))
162140
+ if (existsSync8(jsoncPath))
161021
162141
  return jsoncPath;
161022
- if (existsSync7(jsonPath))
162142
+ if (existsSync8(jsonPath))
161023
162143
  return jsonPath;
161024
162144
  return jsonPath;
161025
162145
  }
@@ -161027,8 +162147,8 @@ function ensureTuiPluginEntry() {
161027
162147
  try {
161028
162148
  const configPath = resolveTuiConfigPath();
161029
162149
  let config2 = {};
161030
- if (existsSync7(configPath)) {
161031
- const raw = readFileSync6(configPath, "utf-8");
162150
+ if (existsSync8(configPath)) {
162151
+ const raw = readFileSync7(configPath, "utf-8");
161032
162152
  config2 = import_comment_json.parse(raw) ?? {};
161033
162153
  }
161034
162154
  const plugins = Array.isArray(config2.plugin) ? config2.plugin.filter((p) => typeof p === "string") : [];
@@ -161047,7 +162167,7 @@ function ensureTuiPluginEntry() {
161047
162167
  plugins.push(PLUGIN_ENTRY);
161048
162168
  }
161049
162169
  config2.plugin = plugins;
161050
- mkdirSync5(dirname2(configPath), { recursive: true });
162170
+ mkdirSync5(dirname3(configPath), { recursive: true });
161051
162171
  writeFileSync3(configPath, `${import_comment_json.stringify(config2, null, 2)}
161052
162172
  `);
161053
162173
  log(`[magic-context] updated TUI plugin entry in ${configPath}`);
@@ -161067,12 +162187,76 @@ var init_tui_config = __esm(() => {
161067
162187
  // src/config/index.ts
161068
162188
  init_jsonc_parser();
161069
162189
  init_magic_context();
162190
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
162191
+ import { homedir as homedir2 } from "os";
162192
+ import { join } from "path";
162193
+
162194
+ // src/config/variable.ts
161070
162195
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
161071
162196
  import { homedir } from "os";
161072
- import { join } from "path";
162197
+ import { dirname, isAbsolute, resolve } from "path";
162198
+ var ENV_PATTERN = /\{env:([^}]+)\}/g;
162199
+ var FILE_PATTERN = /\{file:([^}]+)\}/g;
162200
+ function substituteConfigVariables(input) {
162201
+ const warnings = [];
162202
+ let text = input.text;
162203
+ text = text.replace(ENV_PATTERN, (_, rawName) => {
162204
+ const varName = rawName.trim();
162205
+ const value = varName ? process.env[varName] : undefined;
162206
+ if (value === undefined || value === "") {
162207
+ warnings.push(`Environment variable ${varName} is not set (referenced via {env:${varName}}); using empty string`);
162208
+ return "";
162209
+ }
162210
+ return value;
162211
+ });
162212
+ const fileMatches = Array.from(text.matchAll(FILE_PATTERN));
162213
+ if (fileMatches.length === 0) {
162214
+ return { text, warnings };
162215
+ }
162216
+ const configDir = input.configPath ? dirname(input.configPath) : process.cwd();
162217
+ let output = "";
162218
+ let cursor = 0;
162219
+ for (const match of fileMatches) {
162220
+ const token = match[0];
162221
+ const rawPath = match[1] ?? "";
162222
+ const index = match.index ?? 0;
162223
+ output += text.slice(cursor, index);
162224
+ cursor = index + token.length;
162225
+ const lineStart = text.lastIndexOf(`
162226
+ `, index - 1) + 1;
162227
+ const prefix = text.slice(lineStart, index).trimStart();
162228
+ if (prefix.startsWith("//")) {
162229
+ output += token;
162230
+ continue;
162231
+ }
162232
+ let filePath = rawPath.trim();
162233
+ if (filePath.startsWith("~/")) {
162234
+ filePath = resolve(homedir(), filePath.slice(2));
162235
+ } else if (!isAbsolute(filePath)) {
162236
+ filePath = resolve(configDir, filePath);
162237
+ }
162238
+ if (!existsSync2(filePath)) {
162239
+ warnings.push(`File not found for ${token} (resolved to ${filePath}); using empty string`);
162240
+ continue;
162241
+ }
162242
+ let contents;
162243
+ try {
162244
+ contents = readFileSync2(filePath, "utf-8").trim();
162245
+ } catch (error48) {
162246
+ const message = error48 instanceof Error ? error48.message : String(error48);
162247
+ warnings.push(`Failed to read file for ${token} (${filePath}): ${message}; using empty string`);
162248
+ continue;
162249
+ }
162250
+ output += JSON.stringify(contents).slice(1, -1);
162251
+ }
162252
+ output += text.slice(cursor);
162253
+ return { text: output, warnings };
162254
+ }
162255
+
162256
+ // src/config/index.ts
161073
162257
  var CONFIG_FILE_BASENAME = "magic-context";
161074
162258
  function getUserConfigBasePath() {
161075
- const configRoot = process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
162259
+ const configRoot = process.env.XDG_CONFIG_HOME ?? join(homedir2(), ".config");
161076
162260
  return join(configRoot, "opencode", CONFIG_FILE_BASENAME);
161077
162261
  }
161078
162262
  function getProjectConfigBasePath(directory) {
@@ -161080,10 +162264,15 @@ function getProjectConfigBasePath(directory) {
161080
162264
  }
161081
162265
  function loadConfigFile(configPath) {
161082
162266
  try {
161083
- if (!existsSync2(configPath)) {
162267
+ if (!existsSync3(configPath)) {
161084
162268
  return null;
161085
162269
  }
161086
- return parseJsonc(readFileSync2(configPath, "utf-8"));
162270
+ const rawText = readFileSync3(configPath, "utf-8");
162271
+ const substituted = substituteConfigVariables({ text: rawText, configPath });
162272
+ return {
162273
+ config: parseJsonc(substituted.text),
162274
+ warnings: substituted.warnings.map((w) => `${configPath}: ${w}`)
162275
+ };
161087
162276
  } catch (error48) {
161088
162277
  console.warn(`[magic-context] failed to load config from ${configPath}:`, error48 instanceof Error ? error48.message : String(error48));
161089
162278
  return null;
@@ -161117,6 +162306,25 @@ function mergeConfigs(base, override) {
161117
162306
  };
161118
162307
  return config2;
161119
162308
  }
162309
+ function redactConfigValue(value) {
162310
+ if (value === undefined)
162311
+ return "<missing>";
162312
+ if (value === null)
162313
+ return "null";
162314
+ if (typeof value === "string")
162315
+ return `string, ${value.length} char${value.length === 1 ? "" : "s"}`;
162316
+ if (typeof value === "number")
162317
+ return `number ${value}`;
162318
+ if (typeof value === "boolean")
162319
+ return `boolean ${value}`;
162320
+ if (Array.isArray(value))
162321
+ return `array, ${value.length} item${value.length === 1 ? "" : "s"}`;
162322
+ if (typeof value === "object") {
162323
+ const keys = Object.keys(value);
162324
+ return `object with keys [${keys.join(", ")}]`;
162325
+ }
162326
+ return typeof value;
162327
+ }
161120
162328
  function parsePluginConfig(rawConfig) {
161121
162329
  const parsed = MagicContextConfigSchema.safeParse(rawConfig);
161122
162330
  const disabledHooks = Array.isArray(rawConfig.disabled_hooks) ? rawConfig.disabled_hooks.filter((value) => typeof value === "string") : undefined;
@@ -161146,7 +162354,7 @@ function parsePluginConfig(rawConfig) {
161146
162354
  } else {
161147
162355
  delete patched[key];
161148
162356
  const defaultVal = defaults[key];
161149
- warnings.push(`"${key}": invalid value ${JSON.stringify(rawConfig[key])}, using default ${JSON.stringify(defaultVal)}.`);
162357
+ warnings.push(`"${key}": invalid value (${redactConfigValue(rawConfig[key])}), using default ${JSON.stringify(defaultVal)}.`);
161150
162358
  }
161151
162359
  }
161152
162360
  const retryParsed = MagicContextConfigSchema.safeParse(patched);
@@ -161166,19 +162374,21 @@ function loadPluginConfig(directory) {
161166
162374
  const rootDetected = detectConfigFile(join(directory, CONFIG_FILE_BASENAME));
161167
162375
  const dotOpenCodeDetected = detectConfigFile(getProjectConfigBasePath(directory));
161168
162376
  const projectDetected = rootDetected.format !== "none" ? rootDetected : dotOpenCodeDetected;
161169
- const userConfig = userDetected.format === "none" ? null : loadConfigFile(userDetected.path);
161170
- const projectConfig = projectDetected.format === "none" ? null : loadConfigFile(projectDetected.path);
162377
+ const userLoaded = userDetected.format === "none" ? null : loadConfigFile(userDetected.path);
162378
+ const projectLoaded = projectDetected.format === "none" ? null : loadConfigFile(projectDetected.path);
161171
162379
  let config2 = parsePluginConfig({});
161172
162380
  const allWarnings = [];
161173
- if (userConfig) {
161174
- const parsed = parsePluginConfig(userConfig);
162381
+ if (userLoaded) {
162382
+ allWarnings.push(...userLoaded.warnings.map((w) => `[user config] ${w}`));
162383
+ const parsed = parsePluginConfig(userLoaded.config);
161175
162384
  if (parsed.configWarnings?.length) {
161176
162385
  allWarnings.push(...parsed.configWarnings.map((w) => `[user config] ${w}`));
161177
162386
  }
161178
162387
  config2 = mergeConfigs(config2, parsed);
161179
162388
  }
161180
- if (projectConfig) {
161181
- const parsed = parsePluginConfig(projectConfig);
162389
+ if (projectLoaded) {
162390
+ allWarnings.push(...projectLoaded.warnings.map((w) => `[project config] ${w}`));
162391
+ const parsed = parsePluginConfig(projectLoaded.config);
161182
162392
  if (parsed.configWarnings?.length) {
161183
162393
  allWarnings.push(...parsed.configWarnings.map((w) => `[project config] ${w}`));
161184
162394
  }
@@ -161831,7 +163041,7 @@ init_assistant_message_extractor();
161831
163041
  init_data_path();
161832
163042
  init_logger();
161833
163043
  import { Database as Database2 } from "bun:sqlite";
161834
- import { existsSync as existsSync4 } from "fs";
163044
+ import { existsSync as existsSync5 } from "fs";
161835
163045
  import { join as join8 } from "path";
161836
163046
 
161837
163047
  // src/features/magic-context/key-files/identify-key-files.ts
@@ -162227,7 +163437,7 @@ function getOpenCodeDbPath2() {
162227
163437
  }
162228
163438
  function openOpenCodeDb() {
162229
163439
  const dbPath = getOpenCodeDbPath2();
162230
- if (!existsSync4(dbPath)) {
163440
+ if (!existsSync5(dbPath)) {
162231
163441
  log(`[key-files] OpenCode DB not found at ${dbPath} \u2014 skipping`);
162232
163442
  return null;
162233
163443
  }
@@ -162477,8 +163687,8 @@ async function runDream(args) {
162477
163687
  try {
162478
163688
  const docsDir = args.sessionDirectory ?? args.projectIdentity;
162479
163689
  const existingDocs = taskName === "maintain-docs" ? {
162480
- architecture: existsSync4(join8(docsDir, "ARCHITECTURE.md")),
162481
- structure: existsSync4(join8(docsDir, "STRUCTURE.md"))
163690
+ architecture: existsSync5(join8(docsDir, "ARCHITECTURE.md")),
163691
+ structure: existsSync5(join8(docsDir, "STRUCTURE.md"))
162482
163692
  } : undefined;
162483
163693
  const userMemories = taskName === "archive-stale" ? getActiveUserMemories(args.db).map((um) => ({
162484
163694
  id: um.id,
@@ -163033,8 +164243,10 @@ function createCompactionHandler() {
163033
164243
  };
163034
164244
  }
163035
164245
  // src/hooks/magic-context/event-resolvers.ts
164246
+ init_logger();
163036
164247
  init_models_dev_cache();
163037
164248
  var DEFAULT_CONTEXT_LIMIT = 128000;
164249
+ var MAX_EXECUTE_THRESHOLD = 80;
163038
164250
  function resolveContextLimit(providerID, modelID) {
163039
164251
  if (!providerID) {
163040
164252
  return DEFAULT_CONTEXT_LIMIT;
@@ -163062,6 +164274,10 @@ function resolveCacheTtl(cacheTtl, modelKey) {
163062
164274
  }
163063
164275
  return cacheTtl.default ?? "5m";
163064
164276
  }
164277
+ var clampWarnSeen = new Set;
164278
+ function isFinitePositive(v) {
164279
+ return typeof v === "number" && Number.isFinite(v) && v > 0;
164280
+ }
163065
164281
  function* modelKeyLookupOrder(modelKey) {
163066
164282
  const slash = modelKey.indexOf("/");
163067
164283
  const provider2 = slash >= 0 ? modelKey.slice(0, slash) : "";
@@ -163076,9 +164292,36 @@ function* modelKeyLookupOrder(modelKey) {
163076
164292
  modelId = modelId.slice(0, lastDash);
163077
164293
  }
163078
164294
  }
163079
- function resolveExecuteThreshold(config2, modelKey, fallback) {
163080
- const MAX_EXECUTE_THRESHOLD = 80;
164295
+ function resolveExecuteThresholdDetail(config2, modelKey, fallback, options) {
164296
+ if (options?.tokensConfig && isFinitePositive(options.contextLimit)) {
164297
+ const contextLimit = options.contextLimit;
164298
+ const tokenMatch = resolveTokensMatchWithKey(options.tokensConfig, modelKey);
164299
+ if (tokenMatch && isFinitePositive(tokenMatch.value)) {
164300
+ const cap = contextLimit * (MAX_EXECUTE_THRESHOLD / 100);
164301
+ const effectiveTokens = Math.min(tokenMatch.value, cap);
164302
+ if (effectiveTokens < tokenMatch.value) {
164303
+ const dedupeKey = `${options.sessionId ?? "__global__"}|${modelKey ?? "__default__"}|${tokenMatch.value}|${cap}`;
164304
+ if (!clampWarnSeen.has(dedupeKey)) {
164305
+ clampWarnSeen.add(dedupeKey);
164306
+ const msg = `execute_threshold_tokens clamped: ${tokenMatch.value} \u2192 ${effectiveTokens} (80% of ${contextLimit}) for ${modelKey ?? "default"}`;
164307
+ if (options.sessionId) {
164308
+ sessionLog(options.sessionId, `WARN: ${msg}`);
164309
+ } else {
164310
+ log(`[magic-context] WARN: ${msg}`);
164311
+ }
164312
+ }
164313
+ }
164314
+ const percentage = effectiveTokens / contextLimit * 100;
164315
+ return {
164316
+ percentage: Math.min(percentage, MAX_EXECUTE_THRESHOLD),
164317
+ mode: "tokens",
164318
+ absoluteTokens: Math.floor(effectiveTokens),
164319
+ matchedKey: tokenMatch.matchedKey
164320
+ };
164321
+ }
164322
+ }
163081
164323
  let resolved;
164324
+ let matchedKey;
163082
164325
  if (typeof config2 === "number") {
163083
164326
  resolved = config2;
163084
164327
  } else if (modelKey) {
@@ -163086,14 +164329,50 @@ function resolveExecuteThreshold(config2, modelKey, fallback) {
163086
164329
  for (const candidate of modelKeyLookupOrder(modelKey)) {
163087
164330
  if (typeof config2[candidate] === "number") {
163088
164331
  matched = config2[candidate];
164332
+ matchedKey = candidate;
163089
164333
  break;
163090
164334
  }
163091
164335
  }
163092
- resolved = matched ?? config2.default ?? fallback;
164336
+ if (matched === undefined && typeof config2.default === "number") {
164337
+ resolved = config2.default;
164338
+ matchedKey = "default";
164339
+ } else {
164340
+ resolved = matched ?? fallback;
164341
+ }
164342
+ } else if (typeof config2.default === "number") {
164343
+ resolved = config2.default;
164344
+ matchedKey = "default";
163093
164345
  } else {
163094
- resolved = config2.default ?? fallback;
164346
+ resolved = fallback;
164347
+ }
164348
+ if (!Number.isFinite(resolved) || resolved < 0) {
164349
+ resolved = fallback;
163095
164350
  }
163096
- return Math.min(resolved, MAX_EXECUTE_THRESHOLD);
164351
+ return {
164352
+ percentage: Math.min(resolved, MAX_EXECUTE_THRESHOLD),
164353
+ mode: "percentage",
164354
+ matchedKey
164355
+ };
164356
+ }
164357
+ function resolveExecuteThreshold(config2, modelKey, fallback, options) {
164358
+ return resolveExecuteThresholdDetail(config2, modelKey, fallback, options).percentage;
164359
+ }
164360
+ function resolveTokensMatchWithKey(tokensConfig, modelKey) {
164361
+ if (!tokensConfig) {
164362
+ return;
164363
+ }
164364
+ if (modelKey) {
164365
+ for (const candidate of modelKeyLookupOrder(modelKey)) {
164366
+ const value = tokensConfig[candidate];
164367
+ if (typeof value === "number") {
164368
+ return { value, matchedKey: candidate };
164369
+ }
164370
+ }
164371
+ }
164372
+ if (typeof tokensConfig.default === "number") {
164373
+ return { value: tokensConfig.default, matchedKey: "default" };
164374
+ }
164375
+ return;
163097
164376
  }
163098
164377
  function resolveModelKey(providerID, modelID) {
163099
164378
  if (!providerID || !modelID) {
@@ -163143,11 +164422,16 @@ function parseCacheTtl(ttl) {
163143
164422
  }
163144
164423
  function createScheduler(config2) {
163145
164424
  return {
163146
- shouldExecute(sessionMeta, contextUsage, currentTime = Date.now(), sessionId, modelKey) {
164425
+ shouldExecute(sessionMeta, contextUsage, currentTime = Date.now(), sessionId, modelKey, contextLimit) {
163147
164426
  if (contextUsage.percentage === 0 && sessionMeta.lastResponseTime === 0) {
163148
164427
  return "defer";
163149
164428
  }
163150
- const threshold = resolveExecuteThreshold(config2.executeThresholdPercentage, modelKey, 65);
164429
+ const effectiveContextLimit = contextLimit ?? (contextUsage.percentage > 0 && contextUsage.inputTokens > 0 ? contextUsage.inputTokens / (contextUsage.percentage / 100) : undefined);
164430
+ const threshold = resolveExecuteThreshold(config2.executeThresholdPercentage, modelKey, 65, {
164431
+ tokensConfig: config2.executeThresholdTokens,
164432
+ contextLimit: effectiveContextLimit,
164433
+ sessionId
164434
+ });
163151
164435
  if (contextUsage.percentage >= threshold) {
163152
164436
  return "execute";
163153
164437
  }
@@ -163295,6 +164579,7 @@ init_logger();
163295
164579
  init_storage();
163296
164580
  init_shared();
163297
164581
  init_rpc_notifications();
164582
+ init_compartment_runner_partial_recomp();
163298
164583
 
163299
164584
  // src/hooks/magic-context/execute-flush.ts
163300
164585
  init_storage();
@@ -163536,8 +164821,25 @@ function getRollingNudgeIntervalTokens(baseIntervalTokens, band) {
163536
164821
 
163537
164822
  // src/hooks/magic-context/execute-status.ts
163538
164823
  init_read_session_formatting();
163539
- function executeStatus(db, sessionId, protectedTags, nudgeIntervalTokens = DEFAULT_NUDGE_INTERVAL_TOKENS, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger) {
163540
- const executeThresholdPercentage = resolveExecuteThreshold(executeThresholdPercentageConfig, liveModelKey, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE);
164824
+ function formatExecuteThreshold(thresholdPercentage, mode, contextLimit) {
164825
+ if (mode === "tokens" && contextLimit > 0) {
164826
+ const tokens = Math.floor(thresholdPercentage / 100 * contextLimit);
164827
+ return `${tokens.toLocaleString()} tokens (${thresholdPercentage.toFixed(1)}% of ${contextLimit.toLocaleString()}) [token-mode]`;
164828
+ }
164829
+ if (contextLimit > 0) {
164830
+ const tokens = Math.floor(thresholdPercentage / 100 * contextLimit);
164831
+ return `${thresholdPercentage}% (${tokens.toLocaleString()} of ${contextLimit.toLocaleString()})`;
164832
+ }
164833
+ return `${thresholdPercentage}%`;
164834
+ }
164835
+ function executeStatus(db, sessionId, protectedTags, nudgeIntervalTokens = DEFAULT_NUDGE_INTERVAL_TOKENS, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit) {
164836
+ const thresholdDetail = resolveExecuteThresholdDetail(executeThresholdPercentageConfig, liveModelKey, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, {
164837
+ tokensConfig: executeThresholdTokens,
164838
+ contextLimit,
164839
+ sessionId
164840
+ });
164841
+ const executeThresholdPercentage = thresholdDetail.percentage;
164842
+ const thresholdMode = thresholdDetail.mode;
163541
164843
  try {
163542
164844
  const meta3 = getOrCreateSessionMeta(db, sessionId);
163543
164845
  const tags = getTagsBySession(db, sessionId);
@@ -163558,6 +164860,7 @@ function executeStatus(db, sessionId, protectedTags, nudgeIntervalTokens = DEFAU
163558
164860
  const currentBand = getRollingNudgeBand(meta3.lastContextPercentage, executeThresholdPercentage);
163559
164861
  const nudgeInterval = getRollingNudgeIntervalTokens(nudgeIntervalTokens, currentBand);
163560
164862
  const proactiveCompartmentTrigger = getProactiveCompartmentTriggerPercentage(executeThresholdPercentage);
164863
+ const displayContextLimit = contextLimit && contextLimit > 0 ? contextLimit : meta3.lastContextPercentage > 0 ? Math.round(meta3.lastInputTokens / (meta3.lastContextPercentage / 100)) : 0;
163561
164864
  const lines = [
163562
164865
  "## Magic Status",
163563
164866
  "",
@@ -163581,7 +164884,7 @@ function executeStatus(db, sessionId, protectedTags, nudgeIntervalTokens = DEFAU
163581
164884
  `- Queue will auto-execute: ${cacheExpired ? "yes (cache expired)" : `when TTL expires or context >= ${executeThresholdPercentage}%`}`,
163582
164885
  "",
163583
164886
  "### Rolling Nudges",
163584
- `- Execute threshold: ${executeThresholdPercentage}%`,
164887
+ `- Execute threshold: ${formatExecuteThreshold(executeThresholdPercentage, thresholdMode, displayContextLimit)}`,
163585
164888
  `- Rolling anchor: ${meta3.lastNudgeTokens.toLocaleString()} tokens`,
163586
164889
  `- Effective interval: ${nudgeInterval.toLocaleString()} tokens`,
163587
164890
  `- Next rolling nudge after: ${(meta3.lastNudgeTokens + nudgeInterval).toLocaleString()} tokens`,
@@ -163592,9 +164895,8 @@ function executeStatus(db, sessionId, protectedTags, nudgeIntervalTokens = DEFAU
163592
164895
  `**Protected tags:** ${protectedTags}`,
163593
164896
  `**Subagent session:** ${meta3.isSubagent}`
163594
164897
  ];
163595
- const contextLimit = meta3.lastContextPercentage > 0 ? Math.round(meta3.lastInputTokens / (meta3.lastContextPercentage / 100)) : 0;
163596
164898
  if (meta3.lastContextPercentage > 0 || meta3.lastInputTokens > 0) {
163597
- lines.push("", "### Context Usage", `- Last percentage: ${meta3.lastContextPercentage.toFixed(1)}%`, `- Last input tokens: ${meta3.lastInputTokens.toLocaleString()}`, `- Resolved context limit: ${contextLimit > 0 ? contextLimit.toLocaleString() : "unknown"}`, `- 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`);
164899
+ lines.push("", "### Context Usage", `- Last percentage: ${meta3.lastContextPercentage.toFixed(1)}%`, `- Last input tokens: ${meta3.lastInputTokens.toLocaleString()}`, `- Resolved context limit: ${displayContextLimit > 0 ? displayContextLimit.toLocaleString() : "unknown"}`, `- 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`);
163598
164900
  }
163599
164901
  const compartments = getCompartments(db, sessionId);
163600
164902
  const facts = getSessionFacts(db, sessionId);
@@ -163609,7 +164911,7 @@ ${c.content}
163609
164911
  historyBlockTokens += estimateTokens(`* ${f.content}
163610
164912
  `);
163611
164913
  }
163612
- const budgetTokens = historyBudgetPercentage && contextLimit > 0 ? Math.floor(contextLimit * (Math.min(executeThresholdPercentage, 80) / 100) * historyBudgetPercentage) : null;
164914
+ const budgetTokens = historyBudgetPercentage && displayContextLimit > 0 ? Math.floor(displayContextLimit * (Math.min(executeThresholdPercentage, 80) / 100) * historyBudgetPercentage) : null;
163613
164915
  const budgetUsage = budgetTokens ? (historyBlockTokens / budgetTokens * 100).toFixed(0) : null;
163614
164916
  lines.push("", "### History Compression", `- Compartments: ${compartments.length}`, `- Facts: ${facts.length}`, `- History block: ~${historyBlockTokens.toLocaleString()} tokens`, ...budgetTokens ? [
163615
164917
  `- Compression budget: ~${budgetTokens.toLocaleString()} tokens (${budgetUsage}% used)`,
@@ -163633,6 +164935,37 @@ ${c.content}
163633
164935
  init_send_session_notification();
163634
164936
  var recompConfirmationBySession = new Map;
163635
164937
  var RECOMP_CONFIRMATION_WINDOW_MS = 60000;
164938
+ function parseRecompArgs(raw) {
164939
+ const trimmed = raw.trim();
164940
+ if (trimmed === "")
164941
+ return { kind: "full" };
164942
+ const match = trimmed.match(/^(\d+)\s*-\s*(\d+)$/);
164943
+ if (!match) {
164944
+ return {
164945
+ kind: "error",
164946
+ message: `Invalid /ctx-recomp arguments: \`${trimmed}\`.
164947
+
164948
+ Usage:
164949
+ - \`/ctx-recomp\` \u2014 full rebuild from message 1 to the protected tail
164950
+ - \`/ctx-recomp <start>-<end>\` \u2014 partial rebuild of a message range (e.g. \`/ctx-recomp 1-11322\`)`
164951
+ };
164952
+ }
164953
+ const start = Number.parseInt(match[1], 10);
164954
+ const end = Number.parseInt(match[2], 10);
164955
+ if (!Number.isFinite(start) || !Number.isFinite(end)) {
164956
+ return { kind: "error", message: "Range values must be finite integers." };
164957
+ }
164958
+ if (start < 1) {
164959
+ return { kind: "error", message: `Start must be >= 1 (got ${start}).` };
164960
+ }
164961
+ if (end < start) {
164962
+ return {
164963
+ kind: "error",
164964
+ message: `End must be >= start (got ${start}-${end}).`
164965
+ };
164966
+ }
164967
+ return { kind: "partial", range: { start, end } };
164968
+ }
163636
164969
  var SENTINEL_PREFIX = "__CONTEXT_MANAGEMENT_";
163637
164970
  function throwSentinel(command) {
163638
164971
  throw new Error(`${SENTINEL_PREFIX}${command.toUpperCase()}_HANDLED__`);
@@ -163755,49 +165088,102 @@ function createMagicContextCommandHandler(deps) {
163755
165088
  throwSentinel(input.command);
163756
165089
  }
163757
165090
  const liveModelKey = deps.getLiveModelKey?.(sessionId);
163758
- const statusOutput = executeStatus(deps.db, sessionId, deps.protectedTags, deps.nudgeIntervalTokens, deps.executeThresholdPercentage, liveModelKey, deps.historyBudgetPercentage, deps.commitClusterTrigger);
165091
+ const liveContextLimit = deps.getContextLimit?.(sessionId);
165092
+ const statusOutput = executeStatus(deps.db, sessionId, deps.protectedTags, deps.nudgeIntervalTokens, deps.executeThresholdPercentage, liveModelKey, deps.historyBudgetPercentage, deps.commitClusterTrigger, deps.executeThresholdTokens, liveContextLimit);
163759
165093
  result += result ? `
163760
165094
 
163761
165095
  ${statusOutput}` : statusOutput;
163762
165096
  }
163763
165097
  if (isRecomp) {
163764
- if (isTuiConnected()) {
165098
+ const parsedArgs = parseRecompArgs(input.arguments);
165099
+ if (parsedArgs.kind === "error") {
165100
+ result = `## Magic Recomp \u2014 Invalid Arguments
165101
+
165102
+ ${parsedArgs.message}`;
165103
+ } else if (isTuiConnected()) {
163765
165104
  pushNotification("action", { action: "show-recomp-dialog" }, sessionId);
163766
165105
  sessionLog(sessionId, "command ctx-recomp: pushed show-recomp-dialog to TUI");
163767
165106
  throwSentinel(input.command);
163768
- }
163769
- if (!deps.executeRecomp) {
165107
+ } else if (!deps.executeRecomp) {
163770
165108
  result = `## Magic Recomp
163771
165109
 
163772
165110
  /ctx-recomp is unavailable because the recomp handler is not configured.`;
163773
165111
  } else {
165112
+ const argsKey = parsedArgs.kind === "partial" ? `${parsedArgs.range.start}-${parsedArgs.range.end}` : "";
163774
165113
  const lastConfirmation = recompConfirmationBySession.get(sessionId);
163775
165114
  const now = Date.now();
163776
- if (lastConfirmation && now - lastConfirmation < RECOMP_CONFIRMATION_WINDOW_MS) {
165115
+ const confirmationValid = lastConfirmation && now - lastConfirmation.timestamp < RECOMP_CONFIRMATION_WINDOW_MS && lastConfirmation.argsKey === argsKey;
165116
+ if (confirmationValid) {
163777
165117
  recompConfirmationBySession.delete(sessionId);
163778
- await deps.sendNotification(sessionId, `## Magic Recomp
165118
+ if (parsedArgs.kind === "partial") {
165119
+ await deps.sendNotification(sessionId, `## Magic Recomp
165120
+
165121
+ Partial recomp started for range ${parsedArgs.range.start}-${parsedArgs.range.end}. Rebuilding the matching compartments now (facts unchanged).`, {});
165122
+ result = await deps.executeRecomp(sessionId, {
165123
+ range: parsedArgs.range
165124
+ });
165125
+ } else {
165126
+ await deps.sendNotification(sessionId, `## Magic Recomp
163779
165127
 
163780
165128
  Historian recomp started. Rebuilding compartments and facts from raw session history now.`, {});
163781
- result = await deps.executeRecomp(sessionId);
165129
+ result = await deps.executeRecomp(sessionId);
165130
+ }
163782
165131
  } else {
163783
- recompConfirmationBySession.set(sessionId, now);
165132
+ recompConfirmationBySession.set(sessionId, {
165133
+ timestamp: now,
165134
+ argsKey
165135
+ });
163784
165136
  const compartments = getCompartments(deps.db, sessionId);
163785
165137
  const compartmentCount = compartments.length;
163786
- const warningLines = [
163787
- "## \u26A0\uFE0F Recomp Confirmation Required",
163788
- "",
163789
- `You currently have **${compartmentCount}** compartments.`,
163790
- "Running /ctx-recomp will **regenerate all compartments and facts** from raw session history.",
163791
- "",
163792
- "This operation:",
163793
- "- May take a long time (minutes to hours for long sessions)",
163794
- "- Will consume significant tokens on your historian model",
163795
- "- Cannot be interrupted cleanly once started",
163796
- "",
163797
- "**To confirm, run `/ctx-recomp` again within 60 seconds.**"
163798
- ];
163799
- result = warningLines.join(`
165138
+ if (parsedArgs.kind === "partial") {
165139
+ const snap = snapRangeToCompartments(compartments, parsedArgs.range);
165140
+ if ("error" in snap) {
165141
+ recompConfirmationBySession.delete(sessionId);
165142
+ result = `## Magic Recomp \u2014 Failed
165143
+
165144
+ ${snap.error}`;
165145
+ } else {
165146
+ const replaced = snap.rangeCompartments.length;
165147
+ const preserved = snap.priorCompartments.length + snap.tailCompartments.length;
165148
+ const warningLines = [
165149
+ "## \u26A0\uFE0F Partial Recomp Confirmation Required",
165150
+ "",
165151
+ `Requested range: \`${parsedArgs.range.start}-${parsedArgs.range.end}\``,
165152
+ `Snapped to compartment boundaries: **messages ${snap.snapStart}-${snap.snapEnd}**`,
165153
+ "",
165154
+ `This will **rebuild ${replaced} compartment(s)** in the snapped range.`,
165155
+ `**${preserved} compartment(s)** outside the range will be preserved unchanged.`,
165156
+ "Facts will not be re-extracted.",
165157
+ "",
165158
+ "This operation:",
165159
+ "- May take several minutes to tens of minutes depending on range size",
165160
+ "- Will consume historian-model tokens for each chunk",
165161
+ "- Is resumable if interrupted (staging preserved on failure)",
165162
+ "",
165163
+ `**To confirm, run \`/ctx-recomp ${parsedArgs.range.start}-${parsedArgs.range.end}\` again within 60 seconds.**`
165164
+ ];
165165
+ result = warningLines.join(`
163800
165166
  `);
165167
+ }
165168
+ } else {
165169
+ const warningLines = [
165170
+ "## \u26A0\uFE0F Recomp Confirmation Required",
165171
+ "",
165172
+ `You currently have **${compartmentCount}** compartments.`,
165173
+ "Running /ctx-recomp will **regenerate all compartments and facts** from raw session history.",
165174
+ "",
165175
+ "This operation:",
165176
+ "- May take a long time (minutes to hours for long sessions)",
165177
+ "- Will consume significant tokens on your historian model",
165178
+ "- Cannot be interrupted cleanly once started",
165179
+ "",
165180
+ "Tip: to rebuild only a specific message range, use `/ctx-recomp <start>-<end>` (e.g. `/ctx-recomp 1-11322`).",
165181
+ "",
165182
+ "**To confirm, run `/ctx-recomp` again within 60 seconds.**"
165183
+ ];
165184
+ result = warningLines.join(`
165185
+ `);
165186
+ }
163801
165187
  }
163802
165188
  }
163803
165189
  }
@@ -164420,7 +165806,11 @@ function stripProcessedImages(messages, watermark, messageTagNumbers) {
164420
165806
  return stripped;
164421
165807
  }
164422
165808
 
165809
+ // src/hooks/magic-context/transform.ts
165810
+ init_temporal_awareness();
165811
+
164423
165812
  // src/hooks/magic-context/transform-compartment-phase.ts
165813
+ init_magic_context();
164424
165814
  init_compartment_storage();
164425
165815
  init_storage();
164426
165816
  init_logger();
@@ -164430,11 +165820,11 @@ init_inject_compartments();
164430
165820
  init_read_session_chunk();
164431
165821
  init_send_session_notification();
164432
165822
  var lastCompressorRunBySession = new Map;
164433
- function isCompressorOnCooldown(sessionId) {
165823
+ function isCompressorOnCooldown(sessionId, cooldownMs) {
164434
165824
  const lastRun = lastCompressorRunBySession.get(sessionId);
164435
165825
  if (!lastRun)
164436
165826
  return false;
164437
- return Date.now() - lastRun < 600000;
165827
+ return Date.now() - lastRun < cooldownMs;
164438
165828
  }
164439
165829
  function markCompressorRun(sessionId) {
164440
165830
  lastCompressorRunBySession.set(sessionId, Date.now());
@@ -164471,14 +165861,14 @@ async function runCompartmentPhase(args) {
164471
165861
  async function awaitCompartmentRun(activeRun, reason) {
164472
165862
  sessionLog(args.sessionId, reason);
164473
165863
  const timeoutMs = args.historianTimeoutMs ?? 120000;
164474
- const timeout = new Promise((resolve2) => setTimeout(() => resolve2("timeout"), timeoutMs));
165864
+ const timeout = new Promise((resolve3) => setTimeout(() => resolve3("timeout"), timeoutMs));
164475
165865
  const result = await Promise.race([activeRun.then(() => "done"), timeout]);
164476
165866
  if (result === "timeout") {
164477
165867
  sessionLog(args.sessionId, `transform: compartment await timed out after ${timeoutMs}ms \u2014 proceeding without waiting`);
164478
165868
  return "timed_out";
164479
165869
  }
164480
165870
  sessionLog(args.sessionId, "transform: compartment agent completed, refreshing compartment coverage");
164481
- pendingCompartmentInjection = prepareCompartmentInjection(args.db, args.resolvedSessionId, args.messages, args.cacheAlreadyBusting ?? false, args.projectPath, args.injectionBudgetTokens);
165871
+ pendingCompartmentInjection = prepareCompartmentInjection(args.db, args.resolvedSessionId, args.messages, args.cacheAlreadyBusting ?? false, args.projectPath, args.injectionBudgetTokens, args.experimentalTemporalAwareness);
164482
165872
  return "completed";
164483
165873
  }
164484
165874
  if (args.canRunCompartments && args.sessionMeta.compartmentInProgress && !getActiveCompartmentRun(args.sessionId)) {
@@ -164503,7 +165893,10 @@ async function runCompartmentPhase(args) {
164503
165893
  fallbackModelId: args.fallbackModelId,
164504
165894
  getNotificationParams: args.getNotificationParams,
164505
165895
  experimentalCompactionMarkers: args.experimentalCompactionMarkers,
164506
- experimentalUserMemories: args.experimentalUserMemories
165896
+ experimentalUserMemories: args.experimentalUserMemories,
165897
+ historianTwoPass: args.historianTwoPass,
165898
+ compressorMinCompartmentRatio: args.compressorMinCompartmentRatio,
165899
+ compressorMaxMergeDepth: args.compressorMaxMergeDepth
164507
165900
  });
164508
165901
  compartmentInProgress = true;
164509
165902
  }
@@ -164524,7 +165917,10 @@ async function runCompartmentPhase(args) {
164524
165917
  fallbackModelId: args.fallbackModelId,
164525
165918
  getNotificationParams: args.getNotificationParams,
164526
165919
  experimentalCompactionMarkers: args.experimentalCompactionMarkers,
164527
- experimentalUserMemories: args.experimentalUserMemories
165920
+ experimentalUserMemories: args.experimentalUserMemories,
165921
+ historianTwoPass: args.historianTwoPass,
165922
+ compressorMinCompartmentRatio: args.compressorMinCompartmentRatio,
165923
+ compressorMaxMergeDepth: args.compressorMaxMergeDepth
164528
165924
  });
164529
165925
  activeRun = getActiveCompartmentRun(args.sessionId);
164530
165926
  } else if (!activeRun && hasEligibleHistoryForCompartment()) {
@@ -164544,15 +165940,17 @@ async function runCompartmentPhase(args) {
164544
165940
  }
164545
165941
  }
164546
165942
  }
164547
- if (args.cacheAlreadyBusting && args.historyBudgetTokens && args.historyBudgetTokens > 0 && args.client && !compartmentInProgress && !awaitedCompartmentRun && !isCompressorOnCooldown(args.sessionId)) {
165943
+ if (args.cacheAlreadyBusting && args.historyBudgetTokens && args.historyBudgetTokens > 0 && args.client && !compartmentInProgress && !awaitedCompartmentRun && !isCompressorOnCooldown(args.sessionId, args.compressorCooldownMs ?? DEFAULT_COMPRESSOR_COOLDOWN_MS)) {
164548
165944
  markCompressorRun(args.sessionId);
164549
- runCompressionPassIfNeeded({
165945
+ const compressorPromise = runCompressionPassIfNeeded({
164550
165946
  client: args.client,
164551
165947
  db: args.db,
164552
165948
  sessionId: args.sessionId,
164553
165949
  directory: args.compartmentDirectory,
164554
165950
  historyBudgetTokens: args.historyBudgetTokens,
164555
- historianTimeoutMs: args.historianTimeoutMs
165951
+ historianTimeoutMs: args.historianTimeoutMs,
165952
+ minCompartmentRatio: args.compressorMinCompartmentRatio,
165953
+ maxMergeDepth: args.compressorMaxMergeDepth
164556
165954
  }).then((compressed) => {
164557
165955
  if (compressed) {
164558
165956
  sessionLog(args.sessionId, "independent compressor completed in background \u2014 compressed history will appear on next cache-busting pass");
@@ -164560,6 +165958,7 @@ async function runCompartmentPhase(args) {
164560
165958
  }).catch((error48) => {
164561
165959
  sessionLog(args.sessionId, "independent compressor failed in background:", getErrorMessage(error48));
164562
165960
  });
165961
+ registerActiveCompartmentRun(args.sessionId, compressorPromise);
164563
165962
  }
164564
165963
  return { pendingCompartmentInjection, awaitedCompartmentRun, compartmentInProgress };
164565
165964
  }
@@ -166076,7 +167475,7 @@ function createTransform(deps) {
166076
167475
  }
166077
167476
  }
166078
167477
  const contextUsageEarly = loadContextUsage(deps.contextUsageMap, db, sessionId);
166079
- const historyBudgetTokens = resolveHistoryBudgetTokens(deps.historyBudgetPercentage, contextUsageEarly, deps.executeThresholdPercentage, deps.getModelKey?.(sessionId));
167478
+ const historyBudgetTokens = resolveHistoryBudgetTokens(deps.historyBudgetPercentage, contextUsageEarly, deps.executeThresholdPercentage, deps.getModelKey?.(sessionId), deps.executeThresholdTokens);
166080
167479
  const schedulerDecisionEarly = resolveSchedulerDecision(deps.scheduler, sessionMeta, contextUsageEarly, sessionId, deps.getModelKey?.(sessionId));
166081
167480
  const isCacheBusting = deps.flushedSessions.has(sessionId);
166082
167481
  if (historianFailureState.failureCount === 0) {
@@ -166110,7 +167509,11 @@ function createTransform(deps) {
166110
167509
  fallbackModelId,
166111
167510
  getNotificationParams: () => notificationParams,
166112
167511
  experimentalCompactionMarkers: deps.experimentalCompactionMarkers,
166113
- experimentalUserMemories: deps.experimentalUserMemories
167512
+ experimentalUserMemories: deps.experimentalUserMemories,
167513
+ experimentalTemporalAwareness: deps.experimentalTemporalAwareness,
167514
+ historianTwoPass: deps.historianTwoPass,
167515
+ compressorMinCompartmentRatio: deps.compressorMinCompartmentRatio,
167516
+ compressorMaxMergeDepth: deps.compressorMaxMergeDepth
166114
167517
  });
166115
167518
  skipCompartmentAwaitForThisPass = true;
166116
167519
  return true;
@@ -166144,7 +167547,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
166144
167547
  if (fullFeatureMode) {
166145
167548
  const tInj = performance.now();
166146
167549
  const projectPath = deps.memoryConfig?.enabled ? resolveProjectIdentity(deps.directory ?? process.cwd()) : undefined;
166147
- pendingCompartmentInjection = prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectPath, deps.memoryConfig?.injectionBudgetTokens);
167550
+ pendingCompartmentInjection = prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectPath, deps.memoryConfig?.injectionBudgetTokens, deps.experimentalTemporalAwareness);
166148
167551
  logTransformTiming(sessionId, "prepareCompartmentInjection", tInj);
166149
167552
  }
166150
167553
  let targets = new Map;
@@ -166152,6 +167555,14 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
166152
167555
  let messageTagNumbers = new Map;
166153
167556
  let batch = null;
166154
167557
  let hasRecentReduceCall = false;
167558
+ if (deps.experimentalTemporalAwareness) {
167559
+ const tTemporal = performance.now();
167560
+ const injected = injectTemporalMarkers(messages);
167561
+ if (injected > 0) {
167562
+ sessionLog(sessionId, `temporal: injected ${injected} gap markers`);
167563
+ }
167564
+ logTransformTiming(sessionId, "injectTemporalMarkers", tTemporal);
167565
+ }
166155
167566
  try {
166156
167567
  const t0 = performance.now();
166157
167568
  deps.tagger.initFromDb(sessionId, db);
@@ -166243,7 +167654,12 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
166243
167654
  cacheAlreadyBusting: isCacheBusting || schedulerDecisionEarly === "execute",
166244
167655
  skipAwaitForThisPass: skipCompartmentAwaitForThisPass,
166245
167656
  experimentalCompactionMarkers: deps.experimentalCompactionMarkers,
166246
- experimentalUserMemories: deps.experimentalUserMemories
167657
+ experimentalUserMemories: deps.experimentalUserMemories,
167658
+ experimentalTemporalAwareness: deps.experimentalTemporalAwareness,
167659
+ historianTwoPass: deps.historianTwoPass,
167660
+ compressorMinCompartmentRatio: deps.compressorMinCompartmentRatio,
167661
+ compressorMaxMergeDepth: deps.compressorMaxMergeDepth,
167662
+ compressorCooldownMs: deps.compressorCooldownMs
166247
167663
  });
166248
167664
  pendingCompartmentInjection = compartmentPhase.pendingCompartmentInjection;
166249
167665
  const awaitedCompartmentRun = compartmentPhase.awaitedCompartmentRun;
@@ -166413,11 +167829,15 @@ function hasEligibleCompartmentHistory(db, sessionId) {
166413
167829
  return false;
166414
167830
  }
166415
167831
  }
166416
- function resolveHistoryBudgetTokens(historyBudgetPercentage, contextUsage, executeThresholdPercentage, modelKey) {
167832
+ function resolveHistoryBudgetTokens(historyBudgetPercentage, contextUsage, executeThresholdPercentage, modelKey, executeThresholdTokens) {
166417
167833
  if (!historyBudgetPercentage || contextUsage.percentage <= 0) {
166418
167834
  return;
166419
167835
  }
166420
- return Math.floor(contextUsage.inputTokens / (contextUsage.percentage / 100) * (resolveExecuteThreshold(executeThresholdPercentage ?? 65, modelKey, 65) / 100) * historyBudgetPercentage);
167836
+ const derivedContextLimit = contextUsage.inputTokens / (contextUsage.percentage / 100);
167837
+ return Math.floor(derivedContextLimit * (resolveExecuteThreshold(executeThresholdPercentage ?? 65, modelKey, 65, {
167838
+ tokensConfig: executeThresholdTokens,
167839
+ contextLimit: derivedContextLimit
167840
+ }) / 100) * historyBudgetPercentage);
166421
167841
  }
166422
167842
  function truncateHistorianEmergencyError(error48) {
166423
167843
  const normalized = (error48 ?? "unknown error").replace(/\s+/g, " ").trim();
@@ -166558,7 +167978,11 @@ function createEventHandler2(deps) {
166558
167978
  const sessionMeta = getOrCreateSessionMeta(deps.db, info.sessionID);
166559
167979
  const previousPercentage = sessionMeta.lastContextPercentage;
166560
167980
  if (!sessionMeta.isSubagent) {
166561
- const effectiveExecuteThreshold = resolveExecuteThreshold(deps.config.execute_threshold_percentage ?? 65, modelKey, 65);
167981
+ const effectiveExecuteThreshold = resolveExecuteThreshold(deps.config.execute_threshold_percentage ?? 65, modelKey, 65, {
167982
+ tokensConfig: deps.config.execute_threshold_tokens,
167983
+ contextLimit,
167984
+ sessionId: info.sessionID
167985
+ });
166562
167986
  const triggerBudget = deriveTriggerBudget(contextLimit, effectiveExecuteThreshold);
166563
167987
  const triggerResult = checkCompartmentTrigger(deps.db, info.sessionID, sessionMeta, { percentage, inputTokens: totalInputTokens }, previousPercentage, effectiveExecuteThreshold, triggerBudget, deps.config.auto_drop_tool_age ?? 100, deps.config.protected_tags, deps.config.clear_reasoning_age ?? 50, deps.config.drop_tool_structure ?? true, deps.config.commit_cluster_trigger);
166564
167988
  if (triggerResult.shouldFire) {
@@ -166832,6 +168256,9 @@ function generateEmergencyNudgeText(db, sessionId, contextUsage, config2) {
166832
168256
  `);
166833
168257
  }
166834
168258
 
168259
+ // src/hooks/magic-context/hook.ts
168260
+ init_read_session_db();
168261
+
166835
168262
  // src/hooks/magic-context/text-complete.ts
166836
168263
  var TAG_PREFIX_REGEX2 = /^(\u00a7\d+\u00a7\s*)+/;
166837
168264
  function createTextCompleteHandler() {
@@ -167010,8 +168437,8 @@ function createToolExecuteAfterHook(args) {
167010
168437
  init_send_session_notification();
167011
168438
 
167012
168439
  // src/hooks/magic-context/system-prompt-hash.ts
167013
- import { existsSync as existsSync6, readFileSync as readFileSync5, realpathSync } from "fs";
167014
- import { join as join14, resolve as resolve2, sep } from "path";
168440
+ import { existsSync as existsSync7, readFileSync as readFileSync6, realpathSync } from "fs";
168441
+ import { join as join14, resolve as resolve3, sep } from "path";
167015
168442
 
167016
168443
  // src/agents/magic-context-prompt.ts
167017
168444
  function getToolHistoryGuidance(dropToolStructure) {
@@ -167197,20 +168624,23 @@ function detectAgentFromSystemPrompt(systemPrompt) {
167197
168624
  }
167198
168625
  return null;
167199
168626
  }
167200
- function buildMagicContextSection(agent, protectedTags, ctxReduceEnabled = true, dreamerEnabled = false, dropToolStructure = true) {
168627
+ var TEMPORAL_AWARENESS_GUIDANCE = `
168628
+ **Temporal awareness**: User messages may be preceded by HTML comments like \`<!-- +12m -->\`, \`<!-- +2h 15m -->\`, or \`<!-- +3d 4h -->\` indicating time elapsed since the previous message's completion. Compartments in \`<session-history>\` carry \`start-date\` and \`end-date\` attributes (YYYY-MM-DD) showing real-time boundaries. Use these when reasoning about workflow pacing, log durations, build times, or how long ago something happened.`;
168629
+ function buildMagicContextSection(agent, protectedTags, ctxReduceEnabled = true, dreamerEnabled = false, dropToolStructure = true, temporalAwarenessEnabled = false) {
167201
168630
  const smartNoteGuidance = dreamerEnabled ? `
167202
168631
  When \`surface_condition\` is provided with \`write\`, the note becomes a project-scoped smart note.
167203
168632
  The dreamer evaluates smart note conditions during nightly runs and surfaces them when conditions are met.
167204
168633
  Example: \`ctx_note(action="write", content="Implement X because Y", surface_condition="When PR #42 is merged in this repo")\`` : "";
168634
+ const temporalGuidance = temporalAwarenessEnabled ? TEMPORAL_AWARENESS_GUIDANCE : "";
167205
168635
  if (!ctxReduceEnabled) {
167206
168636
  return `## Magic Context
167207
168637
 
167208
- ${BASE_INTRO_NO_REDUCE(dropToolStructure)}${smartNoteGuidance}`;
168638
+ ${BASE_INTRO_NO_REDUCE(dropToolStructure)}${smartNoteGuidance}${temporalGuidance}`;
167209
168639
  }
167210
168640
  const section = agent ? AGENT_SECTIONS[agent] : GENERIC_SECTION;
167211
168641
  return `## Magic Context
167212
168642
 
167213
- ${BASE_INTRO(protectedTags, dropToolStructure)}${smartNoteGuidance}
168643
+ ${BASE_INTRO(protectedTags, dropToolStructure)}${smartNoteGuidance}${temporalGuidance}
167214
168644
  ${section}
167215
168645
 
167216
168646
  Prefer many small targeted operations over one large blanket operation. Compress early and often \u2014 don't wait for warnings.`;
@@ -167233,8 +168663,8 @@ function readProjectDocs(directory) {
167233
168663
  for (const filename of DOC_FILES) {
167234
168664
  const filePath = join14(directory, filename);
167235
168665
  try {
167236
- if (existsSync6(filePath)) {
167237
- const content = readFileSync5(filePath, "utf-8").trim();
168666
+ if (existsSync7(filePath)) {
168667
+ const content = readFileSync6(filePath, "utf-8").trim();
167238
168668
  if (content.length > 0) {
167239
168669
  sections.push(`<${filename}>
167240
168670
  ${content}
@@ -167265,7 +168695,7 @@ function createSystemPromptHashHandler(deps) {
167265
168695
  `);
167266
168696
  if (fullPrompt.length > 0 && !fullPrompt.includes(MAGIC_CONTEXT_MARKER)) {
167267
168697
  const detectedAgent = detectAgentFromSystemPrompt(fullPrompt);
167268
- const guidance = buildMagicContextSection(detectedAgent, deps.protectedTags, deps.ctxReduceEnabled, deps.dreamerEnabled, deps.dropToolStructure);
168698
+ const guidance = buildMagicContextSection(detectedAgent, deps.protectedTags, deps.ctxReduceEnabled, deps.dreamerEnabled, deps.dropToolStructure, deps.experimentalTemporalAwareness);
167269
168699
  output.system.push(guidance);
167270
168700
  sessionLog(sessionId, `injected ${detectedAgent ?? "generic"} guidance into system prompt`);
167271
168701
  }
@@ -167314,16 +168744,16 @@ ${items}
167314
168744
  const keyFileEntries = getKeyFiles(deps.db, sessionId);
167315
168745
  if (keyFileEntries.length > 0) {
167316
168746
  const sections = [];
167317
- const projectRoot = resolve2(deps.directory);
168747
+ const projectRoot = resolve3(deps.directory);
167318
168748
  let remainingBudgetTokens = deps.experimentalPinKeyFilesTokenBudget ?? 1e4;
167319
168749
  for (const entry of keyFileEntries) {
167320
168750
  try {
167321
- const absPath = resolve2(deps.directory, entry.filePath);
168751
+ const absPath = resolve3(deps.directory, entry.filePath);
167322
168752
  if (!absPath.startsWith(projectRoot + sep) && absPath !== projectRoot) {
167323
168753
  log(`[magic-context] key file path escapes project root, skipping: ${entry.filePath}`);
167324
168754
  continue;
167325
168755
  }
167326
- if (!existsSync6(absPath))
168756
+ if (!existsSync7(absPath))
167327
168757
  continue;
167328
168758
  let realPath;
167329
168759
  try {
@@ -167335,7 +168765,7 @@ ${items}
167335
168765
  log(`[magic-context] key file symlink escapes project root, skipping: ${entry.filePath} \u2192 ${realPath}`);
167336
168766
  continue;
167337
168767
  }
167338
- const content = readFileSync5(realPath, "utf-8").trim();
168768
+ const content = readFileSync6(realPath, "utf-8").trim();
167339
168769
  if (content.length === 0)
167340
168770
  continue;
167341
168771
  const fileTokens = estimateTokens(content);
@@ -167473,6 +168903,17 @@ function createMagicContextHook(deps) {
167473
168903
  const agentBySession = deps.liveSessionState?.agentBySession ?? new Map;
167474
168904
  const recentReduceBySession = new Map;
167475
168905
  const toolUsageSinceUserTurn = new Map;
168906
+ const resolveLiveModel = (sessionId) => {
168907
+ const cached2 = liveModelBySession.get(sessionId);
168908
+ if (cached2)
168909
+ return cached2;
168910
+ const recovered = findLastAssistantModelFromOpenCodeDb(sessionId);
168911
+ if (recovered) {
168912
+ liveModelBySession.set(sessionId, recovered);
168913
+ return recovered;
168914
+ }
168915
+ return;
168916
+ };
167476
168917
  const ctxReduceEnabled = deps.config.ctx_reduce_enabled !== false;
167477
168918
  const nudgerWithRecentReduce = ctxReduceEnabled ? createNudger({
167478
168919
  protected_tags: deps.config.protected_tags,
@@ -167504,6 +168945,7 @@ function createMagicContextHook(deps) {
167504
168945
  getHistorianChunkTokens,
167505
168946
  historyBudgetPercentage: deps.config.history_budget_percentage,
167506
168947
  executeThresholdPercentage: deps.config.execute_threshold_percentage,
168948
+ executeThresholdTokens: deps.config.execute_threshold_tokens,
167507
168949
  historianTimeoutMs: deps.config.historian_timeout_ms ?? DEFAULT_HISTORIAN_TIMEOUT_MS,
167508
168950
  getNotificationParams: (sessionId) => getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
167509
168951
  getModelKey: (sessionId) => {
@@ -167517,6 +168959,11 @@ function createMagicContextHook(deps) {
167517
168959
  projectPath,
167518
168960
  experimentalCompactionMarkers: deps.config.compaction_markers,
167519
168961
  experimentalUserMemories: deps.config.experimental?.user_memories?.enabled,
168962
+ experimentalTemporalAwareness: deps.config.experimental?.temporal_awareness === true,
168963
+ historianTwoPass: deps.config.historian?.two_pass === true,
168964
+ compressorMinCompartmentRatio: deps.config.compressor?.enabled === false ? undefined : deps.config.compressor?.min_compartment_ratio,
168965
+ compressorMaxMergeDepth: deps.config.compressor?.enabled === false ? undefined : deps.config.compressor?.max_merge_depth,
168966
+ compressorCooldownMs: deps.config.compressor?.enabled === false ? undefined : deps.config.compressor?.cooldown_ms,
167520
168967
  liveModelBySession
167521
168968
  });
167522
168969
  const eventHandler = createEventHandler2({
@@ -167571,14 +169018,21 @@ function createMagicContextHook(deps) {
167571
169018
  protectedTags: deps.config.protected_tags,
167572
169019
  nudgeIntervalTokens: deps.config.nudge_interval_tokens ?? DEFAULT_NUDGE_INTERVAL_TOKENS,
167573
169020
  executeThresholdPercentage: deps.config.execute_threshold_percentage ?? 65,
169021
+ executeThresholdTokens: deps.config.execute_threshold_tokens,
167574
169022
  historyBudgetPercentage: deps.config.history_budget_percentage,
167575
169023
  commitClusterTrigger: deps.config.commit_cluster_trigger,
167576
169024
  getLiveModelKey: (sessionId) => {
167577
- const model = liveModelBySession.get(sessionId);
169025
+ const model = resolveLiveModel(sessionId);
167578
169026
  return model ? `${model.providerID}/${model.modelID}` : undefined;
167579
169027
  },
169028
+ getContextLimit: (sessionId) => {
169029
+ const model = resolveLiveModel(sessionId);
169030
+ if (!model)
169031
+ return;
169032
+ return resolveContextLimit(model.providerID, model.modelID);
169033
+ },
167580
169034
  onFlush: (sessionId) => flushedSessions.add(sessionId),
167581
- executeRecomp: async (sessionId) => executeContextRecomp({
169035
+ executeRecomp: async (sessionId, options) => executeContextRecomp({
167582
169036
  client: deps.client,
167583
169037
  db,
167584
169038
  sessionId,
@@ -167586,11 +169040,12 @@ function createMagicContextHook(deps) {
167586
169040
  historianTimeoutMs: deps.config.historian_timeout_ms ?? DEFAULT_HISTORIAN_TIMEOUT_MS,
167587
169041
  directory: deps.directory,
167588
169042
  fallbackModelId: (() => {
167589
- const model = liveModelBySession.get(sessionId);
169043
+ const model = resolveLiveModel(sessionId);
167590
169044
  return model ? `${model.providerID}/${model.modelID}` : undefined;
167591
169045
  })(),
167592
- getNotificationParams: () => getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession)
167593
- }),
169046
+ getNotificationParams: () => getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
169047
+ historianTwoPass: deps.config.historian?.two_pass === true
169048
+ }, options),
167594
169049
  sendNotification: async (sessionId, text, params) => {
167595
169050
  await sendIgnoredMessage(deps.client, sessionId, text, {
167596
169051
  ...getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
@@ -167632,7 +169087,8 @@ function createMagicContextHook(deps) {
167632
169087
  lastHeuristicsTurnId,
167633
169088
  experimentalUserMemories: deps.config.experimental?.user_memories?.enabled,
167634
169089
  experimentalPinKeyFiles: deps.config.experimental?.pin_key_files?.enabled ?? false,
167635
- experimentalPinKeyFilesTokenBudget: deps.config.experimental?.pin_key_files?.token_budget
169090
+ experimentalPinKeyFilesTokenBudget: deps.config.experimental?.pin_key_files?.token_budget,
169091
+ experimentalTemporalAwareness: deps.config.experimental?.temporal_awareness === true
167636
169092
  });
167637
169093
  const eventHook = createEventHook({
167638
169094
  eventHandler,
@@ -167687,7 +169143,8 @@ function createSessionHooks(args) {
167687
169143
  }
167688
169144
  const tagger = createTagger();
167689
169145
  const scheduler2 = createScheduler({
167690
- executeThresholdPercentage: pluginConfig.execute_threshold_percentage ?? DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE
169146
+ executeThresholdPercentage: pluginConfig.execute_threshold_percentage ?? DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE,
169147
+ executeThresholdTokens: pluginConfig.execute_threshold_tokens
167691
169148
  });
167692
169149
  const compactionHandler = createCompactionHandler();
167693
169150
  return {
@@ -167708,12 +169165,14 @@ function createSessionHooks(args) {
167708
169165
  clear_reasoning_age: pluginConfig.clear_reasoning_age,
167709
169166
  iteration_nudge_threshold: pluginConfig.iteration_nudge_threshold,
167710
169167
  execute_threshold_percentage: pluginConfig.execute_threshold_percentage ?? DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE,
169168
+ execute_threshold_tokens: pluginConfig.execute_threshold_tokens,
167711
169169
  historian: pluginConfig.historian,
167712
169170
  history_budget_percentage: pluginConfig.history_budget_percentage,
167713
169171
  historian_timeout_ms: pluginConfig.historian_timeout_ms,
167714
169172
  memory: pluginConfig.memory,
167715
169173
  sidekick: pluginConfig.sidekick,
167716
169174
  dreamer: pluginConfig.dreamer,
169175
+ compressor: pluginConfig.compressor,
167717
169176
  experimental: pluginConfig.experimental
167718
169177
  }
167719
169178
  })
@@ -167969,6 +169428,7 @@ function buildStatusDetail(db, sessionId, directory, modelKey, config2) {
167969
169428
  cacheRemainingMs: 0,
167970
169429
  cacheExpired: false,
167971
169430
  executeThreshold: 65,
169431
+ executeThresholdMode: "percentage",
167972
169432
  protectedTagCount: 20,
167973
169433
  nudgeInterval: 20000,
167974
169434
  historyBudgetPercentage: 0.15,
@@ -167999,9 +169459,20 @@ function buildStatusDetail(db, sessionId, directory, modelKey, config2) {
167999
169459
  const ops = db.query("SELECT tag_id, operation FROM pending_ops WHERE session_id = ?").all(sessionId);
168000
169460
  detail.pendingOps = ops.map((o) => ({ tagId: o.tag_id, operation: o.operation }));
168001
169461
  } catch {}
169462
+ const contextLimitForTokens = base.usagePercentage > 0 ? Math.round(base.inputTokens / (base.usagePercentage / 100)) : 0;
168002
169463
  if (config2) {
168003
- const etp = resolveConfigValue(config2, "execute_threshold_percentage", modelKey, 65);
168004
- detail.executeThreshold = Math.min(etp, 80);
169464
+ const pctCfg = config2.execute_threshold_percentage;
169465
+ const tokensCfg = config2.execute_threshold_tokens;
169466
+ const thresholdDetail = resolveExecuteThresholdDetail(pctCfg ?? 65, modelKey, 65, {
169467
+ tokensConfig: tokensCfg,
169468
+ contextLimit: contextLimitForTokens || undefined,
169469
+ sessionId
169470
+ });
169471
+ detail.executeThreshold = thresholdDetail.percentage;
169472
+ detail.executeThresholdMode = thresholdDetail.mode;
169473
+ if (thresholdDetail.absoluteTokens !== undefined) {
169474
+ detail.executeThresholdTokens = thresholdDetail.absoluteTokens;
169475
+ }
168005
169476
  const ct = resolveConfigValue(config2, "cache_ttl", modelKey, "5m");
168006
169477
  detail.cacheTtl = ct;
168007
169478
  if (typeof config2.protected_tag_count === "number") {
@@ -169311,7 +170782,7 @@ init_models_dev_cache();
169311
170782
  init_logger();
169312
170783
  import { mkdirSync as mkdirSync4, renameSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
169313
170784
  import { createServer } from "http";
169314
- import { dirname } from "path";
170785
+ import { dirname as dirname2 } from "path";
169315
170786
 
169316
170787
  // src/shared/rpc-utils.ts
169317
170788
  import { createHash as createHash2 } from "crypto";
@@ -169337,7 +170808,7 @@ class MagicContextRpcServer {
169337
170808
  this.handlers.set(method, handler);
169338
170809
  }
169339
170810
  async start() {
169340
- return new Promise((resolve3, reject) => {
170811
+ return new Promise((resolve4, reject) => {
169341
170812
  const server = createServer((req, res) => this.dispatch(req, res));
169342
170813
  server.on("error", (err) => {
169343
170814
  log(`[rpc] server error: ${err.message}`);
@@ -169352,7 +170823,7 @@ class MagicContextRpcServer {
169352
170823
  this.port = addr.port;
169353
170824
  this.server = server;
169354
170825
  try {
169355
- const dir = dirname(this.portFilePath);
170826
+ const dir = dirname2(this.portFilePath);
169356
170827
  mkdirSync4(dir, { recursive: true });
169357
170828
  const tmpPath = `${this.portFilePath}.tmp`;
169358
170829
  writeFileSync2(tmpPath, String(this.port), "utf-8");
@@ -169361,7 +170832,7 @@ class MagicContextRpcServer {
169361
170832
  } catch (err) {
169362
170833
  log(`[rpc] failed to write port file: ${err}`);
169363
170834
  }
169364
- resolve3(this.port);
170835
+ resolve4(this.port);
169365
170836
  });
169366
170837
  server.unref();
169367
170838
  });
@@ -169582,10 +171053,15 @@ var plugin = async (ctx) => {
169582
171053
  } = pluginConfig.sidekick;
169583
171054
  return agentOverrides;
169584
171055
  })() : undefined;
171056
+ const historianAgentOverrides = pluginConfig.historian ? (() => {
171057
+ const { two_pass: _twoPass, ...agentOverrides } = pluginConfig.historian;
171058
+ return agentOverrides;
171059
+ })() : undefined;
169585
171060
  config2.agent = {
169586
171061
  ...config2.agent ?? {},
169587
171062
  [DREAMER_AGENT]: buildHiddenAgentConfig(DREAMER_AGENT, DREAMER_SYSTEM_PROMPT, dreamerAgentOverrides),
169588
- [HISTORIAN_AGENT]: buildHiddenAgentConfig(HISTORIAN_AGENT, pluginConfig.experimental?.user_memories?.enabled ? COMPARTMENT_AGENT_SYSTEM_PROMPT + USER_OBSERVATIONS_APPENDIX : COMPARTMENT_AGENT_SYSTEM_PROMPT, pluginConfig.historian),
171063
+ [HISTORIAN_AGENT]: buildHiddenAgentConfig(HISTORIAN_AGENT, pluginConfig.experimental?.user_memories?.enabled ? COMPARTMENT_AGENT_SYSTEM_PROMPT + USER_OBSERVATIONS_APPENDIX : COMPARTMENT_AGENT_SYSTEM_PROMPT, historianAgentOverrides),
171064
+ [HISTORIAN_EDITOR_AGENT]: buildHiddenAgentConfig(HISTORIAN_EDITOR_AGENT, HISTORIAN_EDITOR_SYSTEM_PROMPT, historianAgentOverrides),
169589
171065
  [SIDEKICK_AGENT]: buildHiddenAgentConfig(SIDEKICK_AGENT, SIDEKICK_SYSTEM_PROMPT, sidekickAgentOverrides)
169590
171066
  };
169591
171067
  }