@cortexkit/opencode-magic-context 0.14.0 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/README.md +11 -5
  2. package/dist/hooks/magic-context/compaction-marker-manager.d.ts +29 -0
  3. package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
  4. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  5. package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
  6. package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
  7. package/dist/hooks/magic-context/compartment-runner-types.d.ts +9 -0
  8. package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
  9. package/dist/hooks/magic-context/drop-stale-reduce-calls.d.ts.map +1 -1
  10. package/dist/hooks/magic-context/hook-handlers.d.ts +0 -2
  11. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  12. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  13. package/dist/hooks/magic-context/nudger.d.ts +0 -3
  14. package/dist/hooks/magic-context/nudger.d.ts.map +1 -1
  15. package/dist/hooks/magic-context/sentinel.d.ts +53 -0
  16. package/dist/hooks/magic-context/sentinel.d.ts.map +1 -0
  17. package/dist/hooks/magic-context/strip-content.d.ts +63 -13
  18. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  19. package/dist/hooks/magic-context/strip-structural-noise.d.ts +14 -0
  20. package/dist/hooks/magic-context/strip-structural-noise.d.ts.map +1 -1
  21. package/dist/hooks/magic-context/system-prompt-hash.d.ts +18 -5
  22. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  23. package/dist/hooks/magic-context/transform-compartment-phase.d.ts +2 -0
  24. package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
  25. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  26. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  27. package/dist/index.js +230 -169
  28. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  29. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -152941,6 +152941,7 @@ async function executePartialRecompInternal(deps, range) {
152941
152941
  setRecompPartialRange(db, sessionId, null);
152942
152942
  clearCompressionDepthRange(db, sessionId, snapStart, snapEnd);
152943
152943
  clearInjectionCache(sessionId);
152944
+ deps.onInjectionCacheCleared?.(sessionId);
152944
152945
  const lastEnd = merged[merged.length - 1]?.endMessage ?? snapEnd;
152945
152946
  return { compartmentCount: merged.length, lastEndMessage: lastEnd };
152946
152947
  };
@@ -153333,6 +153334,8 @@ var init_compaction_marker = __esm(() => {
153333
153334
  });
153334
153335
 
153335
153336
  // src/hooks/magic-context/compaction-marker-manager.ts
153337
+ import { Database as SqliteDb } from "bun:sqlite";
153338
+ import { join as join14 } from "path";
153336
153339
  function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, directory) {
153337
153340
  const existing = getPersistedCompactionMarkerState(db, sessionId);
153338
153341
  if (existing) {
@@ -153374,10 +153377,63 @@ function removeCompactionMarkerForSession(db, sessionId) {
153374
153377
  }
153375
153378
  }
153376
153379
  }
153380
+ function checkCompactionMarkerConsistency(db) {
153381
+ const opencodeDbPath = join14(getDataDir(), "opencode", "opencode.db");
153382
+ let opencodeDb;
153383
+ try {
153384
+ opencodeDb = new SqliteDb(opencodeDbPath, { readonly: true });
153385
+ } catch (error48) {
153386
+ log(`[magic-context] compaction-marker consistency check skipped: ${error48 instanceof Error ? error48.message : String(error48)}`);
153387
+ return;
153388
+ }
153389
+ try {
153390
+ const persistedRows = db.prepare("SELECT session_id, compaction_marker_state FROM session_meta WHERE compaction_marker_state IS NOT NULL AND compaction_marker_state != ''").all();
153391
+ if (persistedRows.length === 0)
153392
+ return;
153393
+ const checkMessage = opencodeDb.prepare("SELECT 1 FROM message WHERE id = ? LIMIT 1");
153394
+ const checkPart = opencodeDb.prepare("SELECT 1 FROM part WHERE id = ? LIMIT 1");
153395
+ let reconciledCount = 0;
153396
+ for (const row of persistedRows) {
153397
+ const state = getPersistedCompactionMarkerState(db, row.session_id);
153398
+ if (!state)
153399
+ continue;
153400
+ const boundaryExists = checkMessage.get(state.boundaryMessageId) !== null;
153401
+ const summaryMessageExists = checkMessage.get(state.summaryMessageId) !== null;
153402
+ const compactionPartExists = checkPart.get(state.compactionPartId) !== null;
153403
+ const summaryPartExists = checkPart.get(state.summaryPartId) !== null;
153404
+ const allPresent = boundaryExists && summaryMessageExists && compactionPartExists && summaryPartExists;
153405
+ if (allPresent)
153406
+ continue;
153407
+ let removedOk = false;
153408
+ try {
153409
+ removedOk = removeCompactionMarker(state);
153410
+ } catch (error48) {
153411
+ sessionLog(row.session_id, "compaction-marker consistency: partial cleanup of half-written marker failed:", error48);
153412
+ }
153413
+ if (removedOk) {
153414
+ setPersistedCompactionMarkerState(db, row.session_id, null);
153415
+ sessionLog(row.session_id, `compaction-marker consistency: cleared orphaned state (boundary=${boundaryExists} summary=${summaryMessageExists} cPart=${compactionPartExists} sPart=${summaryPartExists}); next publication will re-inject`);
153416
+ reconciledCount++;
153417
+ } else {
153418
+ sessionLog(row.session_id, `compaction-marker consistency: cleanup failed for orphaned state (boundary=${boundaryExists} summary=${summaryMessageExists} cPart=${compactionPartExists} sPart=${summaryPartExists}); will retry on next startup`);
153419
+ }
153420
+ }
153421
+ if (reconciledCount > 0) {
153422
+ log(`[magic-context] compaction-marker consistency: reconciled ${reconciledCount} session(s) with orphaned marker state at startup`);
153423
+ }
153424
+ } catch (error48) {
153425
+ log(`[magic-context] compaction-marker consistency check failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
153426
+ } finally {
153427
+ try {
153428
+ opencodeDb.close(false);
153429
+ } catch {}
153430
+ }
153431
+ }
153377
153432
  var MARKER_SUMMARY_TEXT = "[Compacted by magic-context \u2014 session history is managed by the plugin]";
153378
153433
  var init_compaction_marker_manager = __esm(() => {
153379
153434
  init_compaction_marker();
153380
153435
  init_storage_meta_persisted();
153436
+ init_data_path();
153381
153437
  init_logger();
153382
153438
  });
153383
153439
 
@@ -154339,6 +154395,7 @@ No new compartments or facts were written. Check the historian model/output and
154339
154395
  clearEmergencyRecovery(db, sessionId);
154340
154396
  })();
154341
154397
  clearInjectionCache(sessionId);
154398
+ deps.onInjectionCacheCleared?.(sessionId);
154342
154399
  if (deps.directory) {
154343
154400
  promoteSessionFactsToMemory(db, sessionId, resolveProjectIdentity(deps.directory), validatedPass.facts ?? []);
154344
154401
  }
@@ -154442,6 +154499,7 @@ async function executeContextRecompInternal(deps) {
154442
154499
  return null;
154443
154500
  clearCompressionDepth(db, sessionId);
154444
154501
  clearInjectionCache(sessionId);
154502
+ deps.onInjectionCacheCleared?.(sessionId);
154445
154503
  if (deps.directory) {
154446
154504
  promoteSessionFactsToMemory(db, sessionId, resolveProjectIdentity(deps.directory), promoted2.facts);
154447
154505
  }
@@ -154610,6 +154668,7 @@ Nothing was written.`;
154610
154668
  }
154611
154669
  clearCompressionDepth(db, sessionId);
154612
154670
  clearInjectionCache(sessionId);
154671
+ deps.onInjectionCacheCleared?.(sessionId);
154613
154672
  const finalCompartments = promoted?.compartments ?? candidateCompartments;
154614
154673
  const finalFacts = promoted?.facts ?? candidateFacts;
154615
154674
  if (deps.directory) {
@@ -162343,10 +162402,10 @@ var require_stringify = __commonJS((exports, module) => {
162343
162402
  replacer = null;
162344
162403
  indent = EMPTY;
162345
162404
  };
162346
- var join16 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
162405
+ var join17 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
162347
162406
  var join_content = (inside, value, gap) => {
162348
162407
  const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
162349
- return join16(comment, inside, gap);
162408
+ return join17(comment, inside, gap);
162350
162409
  };
162351
162410
  var stringify_string = (holder, key, value) => {
162352
162411
  const raw = get_raw_string_literal(holder, key);
@@ -162368,13 +162427,13 @@ var require_stringify = __commonJS((exports, module) => {
162368
162427
  if (i !== 0) {
162369
162428
  inside += COMMA;
162370
162429
  }
162371
- const before = join16(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
162430
+ const before = join17(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
162372
162431
  inside += before || LF + deeper_gap;
162373
162432
  inside += stringify(i, value, deeper_gap) || STR_NULL;
162374
162433
  inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
162375
162434
  after_comma = process_comments(value, AFTER(i), deeper_gap);
162376
162435
  }
162377
- inside += join16(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
162436
+ inside += join17(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
162378
162437
  return BRACKET_OPEN + join_content(inside, value, gap) + BRACKET_CLOSE;
162379
162438
  };
162380
162439
  var object_stringify = (value, gap) => {
@@ -162395,13 +162454,13 @@ var require_stringify = __commonJS((exports, module) => {
162395
162454
  inside += COMMA;
162396
162455
  }
162397
162456
  first = false;
162398
- const before = join16(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
162457
+ const before = join17(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
162399
162458
  inside += before || LF + deeper_gap;
162400
162459
  inside += quote(key) + process_comments(value, AFTER_PROP(key), deeper_gap) + COLON + process_comments(value, AFTER_COLON(key), deeper_gap) + SPACE + sv + process_comments(value, AFTER_VALUE(key), deeper_gap);
162401
162460
  after_comma = process_comments(value, AFTER(key), deeper_gap);
162402
162461
  };
162403
162462
  keys.forEach(iteratee);
162404
- inside += join16(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
162463
+ inside += join17(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
162405
162464
  return CURLY_BRACKET_OPEN + join_content(inside, value, gap) + CURLY_BRACKET_CLOSE;
162406
162465
  };
162407
162466
  function stringify(key, holder, gap) {
@@ -162499,11 +162558,11 @@ __export(exports_tui_config, {
162499
162558
  ensureTuiPluginEntry: () => ensureTuiPluginEntry
162500
162559
  });
162501
162560
  import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
162502
- import { dirname as dirname3, join as join16 } from "path";
162561
+ import { dirname as dirname3, join as join17 } from "path";
162503
162562
  function resolveTuiConfigPath() {
162504
162563
  const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
162505
- const jsoncPath = join16(configDir, "tui.jsonc");
162506
- const jsonPath = join16(configDir, "tui.json");
162564
+ const jsoncPath = join17(configDir, "tui.jsonc");
162565
+ const jsonPath = join17(configDir, "tui.json");
162507
162566
  if (existsSync8(jsoncPath))
162508
162567
  return jsoncPath;
162509
162568
  if (existsSync8(jsonPath))
@@ -166549,6 +166608,47 @@ function modelRequiresInterleavedReasoning(model) {
166549
166608
 
166550
166609
  // src/hooks/magic-context/transform.ts
166551
166610
  init_send_session_notification();
166611
+ // src/hooks/magic-context/sentinel.ts
166612
+ function makeSentinel(originalPart) {
166613
+ const sentinel = {
166614
+ type: "text",
166615
+ text: ""
166616
+ };
166617
+ if (isRecord(originalPart)) {
166618
+ if (originalPart.cache_control !== undefined) {
166619
+ sentinel.cache_control = originalPart.cache_control;
166620
+ }
166621
+ if (originalPart.cacheControl !== undefined) {
166622
+ sentinel.cacheControl = originalPart.cacheControl;
166623
+ }
166624
+ }
166625
+ return sentinel;
166626
+ }
166627
+ function isSentinel(part) {
166628
+ return isRecord(part) && part.type === "text" && typeof part.text === "string" && part.text === "";
166629
+ }
166630
+ function replaySentinelByMessageIds(messages, ids) {
166631
+ if (ids.size === 0)
166632
+ return { replayed: 0, missingIds: [] };
166633
+ const seen = new Set;
166634
+ let replayed = 0;
166635
+ for (const msg of messages) {
166636
+ const id = msg.info.id;
166637
+ if (!id || !ids.has(id))
166638
+ continue;
166639
+ seen.add(id);
166640
+ if (msg.parts.length === 1 && isSentinel(msg.parts[0]))
166641
+ continue;
166642
+ msg.parts.length = 0;
166643
+ msg.parts.push(makeSentinel(undefined));
166644
+ replayed++;
166645
+ }
166646
+ const missingIds = [];
166647
+ for (const id of ids)
166648
+ if (!seen.has(id))
166649
+ missingIds.push(id);
166650
+ return { replayed, missingIds };
166651
+ }
166552
166652
 
166553
166653
  // src/hooks/magic-context/strip-content.ts
166554
166654
  var DROPPED_PLACEHOLDER_PATTERN = /^\[dropped \u00A7\d+\u00A7\]$/;
@@ -166570,12 +166670,15 @@ function isSystemInjectedText(text) {
166570
166670
  }
166571
166671
  function stripSystemInjectedMessages(messages, protectedTailStart) {
166572
166672
  let stripped = 0;
166573
- for (let i = messages.length - 1;i >= 0; i--) {
166673
+ const sentineledIds = [];
166674
+ for (let i = 0;i < messages.length; i++) {
166574
166675
  if (i >= protectedTailStart)
166575
166676
  continue;
166576
166677
  const msg = messages[i];
166577
166678
  if (msg.parts.length === 0)
166578
166679
  continue;
166680
+ if (msg.parts.length === 1 && isSentinel(msg.parts[0]))
166681
+ continue;
166579
166682
  let hasContentPart = false;
166580
166683
  let allContentIsSystemInjection = true;
166581
166684
  for (const part of msg.parts) {
@@ -166602,11 +166705,14 @@ function stripSystemInjectedMessages(messages, protectedTailStart) {
166602
166705
  break;
166603
166706
  }
166604
166707
  if (hasContentPart && allContentIsSystemInjection) {
166605
- messages.splice(i, 1);
166708
+ msg.parts.length = 0;
166709
+ msg.parts.push(makeSentinel(undefined));
166606
166710
  stripped++;
166711
+ if (typeof msg.info.id === "string")
166712
+ sentineledIds.push(msg.info.id);
166607
166713
  }
166608
166714
  }
166609
- return stripped;
166715
+ return { stripped, sentineledIds };
166610
166716
  }
166611
166717
  var METADATA_PART_TYPES = new Set([
166612
166718
  "step-start",
@@ -166620,12 +166726,15 @@ var METADATA_PART_TYPES = new Set([
166620
166726
  ]);
166621
166727
  function stripDroppedPlaceholderMessages(messages) {
166622
166728
  let stripped = 0;
166623
- for (let i = messages.length - 1;i >= 0; i--) {
166729
+ const sentineledIds = [];
166730
+ for (let i = 0;i < messages.length; i++) {
166624
166731
  const msg = messages[i];
166625
166732
  if (msg.parts.length === 0)
166626
166733
  continue;
166627
166734
  if (msg.info.role === "user")
166628
166735
  continue;
166736
+ if (msg.parts.length === 1 && isSentinel(msg.parts[0]))
166737
+ continue;
166629
166738
  let hasContentPart = false;
166630
166739
  let hasNonDroppedContent = false;
166631
166740
  for (const part of msg.parts) {
@@ -166666,11 +166775,14 @@ function stripDroppedPlaceholderMessages(messages) {
166666
166775
  break;
166667
166776
  }
166668
166777
  if (hasContentPart && !hasNonDroppedContent) {
166669
- messages.splice(i, 1);
166778
+ msg.parts.length = 0;
166779
+ msg.parts.push(makeSentinel(undefined));
166670
166780
  stripped++;
166781
+ if (typeof msg.info.id === "string")
166782
+ sentineledIds.push(msg.info.id);
166671
166783
  }
166672
166784
  }
166673
- return stripped;
166785
+ return { stripped, sentineledIds };
166674
166786
  }
166675
166787
  function replayClearedReasoning(messages, reasoningByMessage, messageTagNumbers, persistedWatermark, skipTypedReasoningCleanup = false) {
166676
166788
  if (skipTypedReasoningCleanup)
@@ -166764,23 +166876,22 @@ function stripClearedReasoning(messages, skipTypedReasoningCleanup = false) {
166764
166876
  for (const message of messages) {
166765
166877
  if (message.info.role !== "assistant")
166766
166878
  continue;
166767
- const originalLength = message.parts.length;
166768
- const kept = message.parts.filter((part) => {
166879
+ for (let i = 0;i < message.parts.length; i++) {
166880
+ const part = message.parts[i];
166769
166881
  if (!isRecord(part))
166770
- return true;
166882
+ continue;
166771
166883
  const partType = part.type;
166772
166884
  if (!CLEARED_REASONING_TYPES.has(partType))
166773
- return true;
166885
+ continue;
166774
166886
  if (!("thinking" in part) && !("text" in part))
166775
- return true;
166887
+ continue;
166776
166888
  const thinking = "thinking" in part ? part.thinking : undefined;
166777
166889
  const text = "text" in part ? part.text : undefined;
166778
- return thinking !== undefined && thinking !== "[cleared]" || text !== undefined && text !== "[cleared]";
166779
- });
166780
- if (kept.length < originalLength) {
166781
- message.parts.length = 0;
166782
- message.parts.push(...kept);
166783
- stripped += originalLength - kept.length;
166890
+ const isCleared = (thinking === undefined || thinking === "[cleared]") && (text === undefined || text === "[cleared]");
166891
+ if (!isCleared)
166892
+ continue;
166893
+ message.parts[i] = makeSentinel(part);
166894
+ stripped++;
166784
166895
  }
166785
166896
  }
166786
166897
  return stripped;
@@ -166868,13 +166979,15 @@ function stripReasoningFromMergedAssistants(messages) {
166868
166979
  continue;
166869
166980
  if (part.ignored === true)
166870
166981
  continue;
166982
+ if (isSentinel(part))
166983
+ continue;
166871
166984
  if (REASONING_PART_TYPES.has(partType)) {
166872
166985
  keepIndex = i;
166873
166986
  }
166874
166987
  break;
166875
166988
  }
166876
166989
  }
166877
- for (let i = message.parts.length - 1;i >= 0; i--) {
166990
+ for (let i = 0;i < message.parts.length; i++) {
166878
166991
  const part = message.parts[i];
166879
166992
  if (!isRecord(part))
166880
166993
  continue;
@@ -166884,7 +166997,7 @@ function stripReasoningFromMergedAssistants(messages) {
166884
166997
  keptReasoningInRun = true;
166885
166998
  continue;
166886
166999
  }
166887
- message.parts.splice(i, 1);
167000
+ message.parts[i] = makeSentinel(part);
166888
167001
  stripped++;
166889
167002
  }
166890
167003
  prevRole = role;
@@ -166907,7 +167020,7 @@ function stripProcessedImages(messages, watermark, messageTagNumbers) {
166907
167020
  if (maxTag > watermark) {
166908
167021
  continue;
166909
167022
  }
166910
- for (let j = msg.parts.length - 1;j >= 0; j--) {
167023
+ for (let j = 0;j < msg.parts.length; j++) {
166911
167024
  const part = msg.parts[j];
166912
167025
  if (!isRecord(part) || part.type !== "file") {
166913
167026
  continue;
@@ -166916,7 +167029,7 @@ function stripProcessedImages(messages, watermark, messageTagNumbers) {
166916
167029
  continue;
166917
167030
  }
166918
167031
  if (typeof part.url === "string" && part.url.startsWith("data:") && part.url.length > 200) {
166919
- msg.parts.splice(j, 1);
167032
+ msg.parts[j] = makeSentinel(part);
166920
167033
  stripped++;
166921
167034
  }
166922
167035
  }
@@ -167014,7 +167127,8 @@ async function runCompartmentPhase(args) {
167014
167127
  experimentalUserMemories: args.experimentalUserMemories,
167015
167128
  historianTwoPass: args.historianTwoPass,
167016
167129
  compressorMinCompartmentRatio: args.compressorMinCompartmentRatio,
167017
- compressorMaxMergeDepth: args.compressorMaxMergeDepth
167130
+ compressorMaxMergeDepth: args.compressorMaxMergeDepth,
167131
+ onInjectionCacheCleared: args.onInjectionCacheCleared
167018
167132
  });
167019
167133
  compartmentInProgress = true;
167020
167134
  }
@@ -167038,7 +167152,8 @@ async function runCompartmentPhase(args) {
167038
167152
  experimentalUserMemories: args.experimentalUserMemories,
167039
167153
  historianTwoPass: args.historianTwoPass,
167040
167154
  compressorMinCompartmentRatio: args.compressorMinCompartmentRatio,
167041
- compressorMaxMergeDepth: args.compressorMaxMergeDepth
167155
+ compressorMaxMergeDepth: args.compressorMaxMergeDepth,
167156
+ onInjectionCacheCleared: args.onInjectionCacheCleared
167042
167157
  });
167043
167158
  activeRun = getActiveCompartmentRun(args.sessionId);
167044
167159
  } else if (!activeRun && hasEligibleHistoryForCompartment()) {
@@ -167284,19 +167399,20 @@ function stripStructuralNoise(messages) {
167284
167399
  if (!Array.isArray(message.parts)) {
167285
167400
  continue;
167286
167401
  }
167287
- const originalLength = message.parts.length;
167288
- const keptParts = message.parts.filter((part) => !isStructuralNoisePart(part));
167289
- if (keptParts.length < originalLength && keptParts.length > 0) {
167290
- message.parts.length = 0;
167291
- message.parts.push(...keptParts);
167292
- strippedParts += originalLength - keptParts.length;
167402
+ for (let i = 0;i < message.parts.length; i++) {
167403
+ const part = message.parts[i];
167404
+ if (isSentinel(part))
167405
+ continue;
167406
+ if (!isStructuralNoisePart(part))
167407
+ continue;
167408
+ message.parts[i] = makeSentinel(part);
167409
+ strippedParts++;
167293
167410
  }
167294
167411
  }
167295
167412
  return strippedParts;
167296
167413
  }
167297
167414
  // src/hooks/magic-context/tag-messages.ts
167298
167415
  init_storage();
167299
-
167300
167416
  // src/hooks/magic-context/drop-stale-reduce-calls.ts
167301
167417
  var STALE_TOOL_NAMES = new Set(["ctx_reduce"]);
167302
167418
  function isReduceToolPart(part) {
@@ -167328,20 +167444,25 @@ function hasAnyMeaningfulPart(parts) {
167328
167444
  function dropStaleReduceCalls(messages, protectedCount = 0) {
167329
167445
  let didDrop = false;
167330
167446
  const protectedStart = messages.length - protectedCount;
167331
- for (let i = messages.length - 1;i >= 0; i -= 1) {
167447
+ for (let i = 0;i < messages.length; i++) {
167332
167448
  if (i >= protectedStart)
167333
- continue;
167449
+ break;
167334
167450
  const message = messages[i];
167335
- const originalLength = message.parts.length;
167336
- for (let j = message.parts.length - 1;j >= 0; j -= 1) {
167337
- if (isReduceToolPart(message.parts[j])) {
167338
- message.parts.splice(j, 1);
167451
+ let touched = false;
167452
+ for (let j = 0;j < message.parts.length; j++) {
167453
+ const part = message.parts[j];
167454
+ if (isSentinel(part))
167455
+ continue;
167456
+ if (isReduceToolPart(part)) {
167457
+ message.parts[j] = makeSentinel(part);
167458
+ touched = true;
167339
167459
  }
167340
167460
  }
167341
- if (message.parts.length < originalLength) {
167461
+ if (touched) {
167342
167462
  didDrop = true;
167343
167463
  if (!hasAnyMeaningfulPart(message.parts)) {
167344
- messages.splice(i, 1);
167464
+ message.parts.length = 0;
167465
+ message.parts.push(makeSentinel(undefined));
167345
167466
  }
167346
167467
  }
167347
167468
  }
@@ -168906,55 +169027,31 @@ async function runPostTransformPhase(args) {
168906
169027
  {
168907
169028
  const persistedIds = getStrippedPlaceholderIds(args.db, args.sessionId);
168908
169029
  if (persistedIds.size > 0) {
168909
- let replayed = 0;
168910
- for (let i = args.messages.length - 1;i >= 0; i--) {
168911
- const msgId = args.messages[i].info.id;
168912
- if (msgId && persistedIds.has(msgId)) {
168913
- args.messages.splice(i, 1);
168914
- replayed++;
168915
- }
168916
- }
169030
+ const { replayed, missingIds } = replaySentinelByMessageIds(args.messages, persistedIds);
168917
169031
  if (replayed > 0) {
168918
- sessionLog(args.sessionId, `placeholder replay: removed ${replayed} previously-stripped messages`);
169032
+ sessionLog(args.sessionId, `sentinel replay: neutralized ${replayed} previously-stripped messages`);
169033
+ }
169034
+ if (missingIds.length > 0) {
169035
+ for (const id of missingIds)
169036
+ persistedIds.delete(id);
169037
+ setStrippedPlaceholderIds(args.db, args.sessionId, persistedIds);
168919
169038
  }
168920
169039
  }
168921
169040
  if (isCacheBustingPass) {
168922
- const preStripIds = new Set;
168923
- for (const msg of args.messages) {
168924
- if (msg.info.id)
168925
- preStripIds.add(msg.info.id);
168926
- }
168927
- const strippedDropped = stripDroppedPlaceholderMessages(args.messages);
168928
- if (strippedDropped > 0) {
168929
- const postStripIds = new Set;
168930
- for (const msg of args.messages) {
168931
- if (msg.info.id)
168932
- postStripIds.add(msg.info.id);
168933
- }
168934
- let newlyStrippedCount = 0;
168935
- for (const id of preStripIds) {
168936
- if (!postStripIds.has(id)) {
168937
- persistedIds.add(id);
168938
- newlyStrippedCount++;
168939
- }
168940
- }
168941
- for (const id of persistedIds) {
168942
- if (!preStripIds.has(id) && !postStripIds.has(id)) {
168943
- persistedIds.delete(id);
168944
- }
168945
- }
169041
+ const droppedResult = stripDroppedPlaceholderMessages(args.messages);
169042
+ const protectedTailStart = Math.max(0, args.messages.length - args.protectedTags * 2);
169043
+ const systemInjectedResult = stripSystemInjectedMessages(args.messages, protectedTailStart);
169044
+ const newlyNeutralized = droppedResult.sentineledIds.length + systemInjectedResult.sentineledIds.length;
169045
+ if (newlyNeutralized > 0) {
169046
+ for (const id of droppedResult.sentineledIds)
169047
+ persistedIds.add(id);
169048
+ for (const id of systemInjectedResult.sentineledIds)
169049
+ persistedIds.add(id);
168946
169050
  setStrippedPlaceholderIds(args.db, args.sessionId, persistedIds);
168947
- sessionLog(args.sessionId, `stripped ${strippedDropped} placeholder messages (${newlyStrippedCount} new, ${persistedIds.size} total persisted)`);
169051
+ sessionLog(args.sessionId, `neutralized ${droppedResult.stripped} dropped + ${systemInjectedResult.stripped} system-injected messages (${newlyNeutralized} new, ${persistedIds.size} total persisted)`);
168948
169052
  }
168949
169053
  }
168950
169054
  }
168951
- if (isCacheBustingPass) {
168952
- const protectedTailStart = Math.max(0, args.messages.length - args.protectedTags * 2);
168953
- const strippedSystemInjected = stripSystemInjectedMessages(args.messages, protectedTailStart);
168954
- if (strippedSystemInjected > 0) {
168955
- sessionLog(args.sessionId, `stripped ${strippedSystemInjected} system-injected messages (notifications/reminders)`);
168956
- }
168957
- }
168958
169055
  const pendingUserTurnReminder = getPersistedStickyTurnReminder(args.db, args.sessionId);
168959
169056
  if (pendingUserTurnReminder) {
168960
169057
  if (args.hasRecentReduceCall && isCacheBustingPass) {
@@ -169243,7 +169340,10 @@ function createTransform(deps) {
169243
169340
  experimentalTemporalAwareness: deps.experimentalTemporalAwareness,
169244
169341
  historianTwoPass: deps.historianTwoPass,
169245
169342
  compressorMinCompartmentRatio: deps.compressorMinCompartmentRatio,
169246
- compressorMaxMergeDepth: deps.compressorMaxMergeDepth
169343
+ compressorMaxMergeDepth: deps.compressorMaxMergeDepth,
169344
+ onInjectionCacheCleared: (sid) => {
169345
+ deps.flushedSessions.add(sid);
169346
+ }
169247
169347
  });
169248
169348
  skipCompartmentAwaitForThisPass = true;
169249
169349
  return true;
@@ -169391,7 +169491,10 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
169391
169491
  historianTwoPass: deps.historianTwoPass,
169392
169492
  compressorMinCompartmentRatio: deps.compressorMinCompartmentRatio,
169393
169493
  compressorMaxMergeDepth: deps.compressorMaxMergeDepth,
169394
- compressorCooldownMs: deps.compressorCooldownMs
169494
+ compressorCooldownMs: deps.compressorCooldownMs,
169495
+ onInjectionCacheCleared: (sid) => {
169496
+ deps.flushedSessions.add(sid);
169497
+ }
169395
169498
  });
169396
169499
  pendingCompartmentInjection = compartmentPhase.pendingCompartmentInjection;
169397
169500
  const awaitedCompartmentRun = compartmentPhase.awaitedCompartmentRun;
@@ -170000,29 +170103,6 @@ function estimateProjectedPercentage(db, sessionId, contextUsage, preloadedTags)
170000
170103
  const dropRatio = pendingDropBytes / totalActiveBytes;
170001
170104
  return contextUsage.percentage * (1 - dropRatio);
170002
170105
  }
170003
- function generateEmergencyNudgeText(db, sessionId, contextUsage, config2) {
170004
- const largest = formatLargestTags(getTopNBySize(db, sessionId, 3));
170005
- const protectedCount = config2.protected_tags;
170006
- const activeTags = getTagsBySession(db, sessionId).filter((t) => t.status === "active");
170007
- const highestProtected = activeTags.map((t) => t.tagNumber).sort((a, b) => b - a).slice(0, protectedCount)[0];
170008
- const protectedHint = highestProtected ? ` Tags \xA7${highestProtected}\xA7 and above are protected (last ${protectedCount}) \u2014 You MUST NOT try to reduce those.` : "";
170009
- const oldToolHint = formatOldToolTags(activeTags, protectedCount, 5);
170010
- return [
170011
- `<instruction name="context_emergency">`,
170012
- `CONTEXT EMERGENCY \u2014 ~${Math.round(contextUsage.percentage)}%. STOP all current work immediately.`,
170013
- ``,
170014
- `You MUST use \`ctx_reduce\` RIGHT NOW to free space. If context overflows, you lose all work.`,
170015
- ``,
170016
- `Steps:`,
170017
- `1. Find OLD tool outputs (grep results, file reads, build logs) you already processed \u2014 look at \xA7N\xA7 tags`,
170018
- `2. Drop those specifically: e.g. drop="3,7,12" \u2014 NEVER drop large ranges like "1-50"`,
170019
- `3. KEEP anything related to current task, recent errors, or decisions`,
170020
- ``,
170021
- `Largest tags: ${largest}.${oldToolHint}${protectedHint}`,
170022
- `</instruction>`
170023
- ].join(`
170024
- `);
170025
- }
170026
170106
 
170027
170107
  // src/hooks/magic-context/hook.ts
170028
170108
  init_read_session_db();
@@ -170036,6 +170116,7 @@ function createTextCompleteHandler() {
170036
170116
  }
170037
170117
 
170038
170118
  // src/hooks/magic-context/hook.ts
170119
+ init_compaction_marker_manager();
170039
170120
  init_compartment_runner();
170040
170121
 
170041
170122
  // src/hooks/magic-context/hook-handlers.ts
@@ -170121,59 +170202,12 @@ function createEventHook(args) {
170121
170202
  args.agentBySession.delete(sessionId);
170122
170203
  args.recentReduceBySession.delete(sessionId);
170123
170204
  args.toolUsageSinceUserTurn.delete(sessionId);
170124
- args.emergencyNudgeFired.delete(sessionId);
170125
170205
  args.flushedSessions.delete(sessionId);
170126
170206
  args.lastHeuristicsTurnId.delete(sessionId);
170127
170207
  args.commitSeenLastPass?.delete(sessionId);
170128
170208
  clearNoteNudgeState(args.db, sessionId);
170129
170209
  clearAutoSearchForSession(sessionId);
170130
170210
  }
170131
- if (input.event.type === "message.removed") {
170132
- return;
170133
- }
170134
- const entry = args.contextUsageMap.get(sessionId);
170135
- if (!entry)
170136
- return;
170137
- if (entry.usage.percentage < FORCE_COMPARTMENT_PERCENTAGE) {
170138
- args.emergencyNudgeFired.delete(sessionId);
170139
- return;
170140
- }
170141
- if (args.ctxReduceEnabled === false)
170142
- return;
170143
- if (args.emergencyNudgeFired.has(sessionId))
170144
- return;
170145
- const meta3 = getOrCreateSessionMeta(args.db, sessionId);
170146
- if (meta3.isSubagent)
170147
- return;
170148
- args.emergencyNudgeFired.add(sessionId);
170149
- updateSessionMeta(args.db, sessionId, { lastNudgeTokens: entry.usage.inputTokens });
170150
- const nudgeText = generateEmergencyNudgeText(args.db, sessionId, entry.usage, {
170151
- protected_tags: args.protectedTags
170152
- });
170153
- sessionLog(sessionId, "firing 80% emergency nudge as ignored notification");
170154
- try {
170155
- const model = args.liveModelBySession.get(sessionId);
170156
- const variant = args.variantBySession.get(sessionId);
170157
- const agent = args.agentBySession.get(sessionId);
170158
- const c = args.client;
170159
- if (typeof c.session?.promptAsync !== "function") {
170160
- sessionLog(sessionId, "emergency nudge: promptAsync unavailable");
170161
- args.emergencyNudgeFired.delete(sessionId);
170162
- return;
170163
- }
170164
- await c.session.promptAsync({
170165
- path: { id: sessionId },
170166
- body: {
170167
- ...agent ? { agent } : {},
170168
- ...model ? { model } : {},
170169
- ...variant ? { variant } : {},
170170
- parts: [{ type: "text", text: nudgeText }]
170171
- }
170172
- });
170173
- } catch (error48) {
170174
- sessionLog(sessionId, "emergency nudge promptAsync failed:", error48);
170175
- args.emergencyNudgeFired.delete(sessionId);
170176
- }
170177
170211
  };
170178
170212
  }
170179
170213
  function createCommandExecuteBeforeHook(commandHandler) {
@@ -170217,7 +170251,7 @@ init_send_session_notification();
170217
170251
 
170218
170252
  // src/hooks/magic-context/system-prompt-hash.ts
170219
170253
  import { existsSync as existsSync7, readFileSync as readFileSync6, realpathSync } from "fs";
170220
- import { join as join14, resolve as resolve3, sep } from "path";
170254
+ import { join as join15, resolve as resolve3, sep } from "path";
170221
170255
 
170222
170256
  // src/agents/magic-context-prompt.ts
170223
170257
  function getToolHistoryGuidance(dropToolStructure) {
@@ -170436,11 +170470,17 @@ var USER_PROFILE_MARKER = "<user-profile>";
170436
170470
  var KEY_FILES_MARKER = "<key-files>";
170437
170471
  var cachedUserProfileBySession = new Map;
170438
170472
  var cachedKeyFilesBySession = new Map;
170473
+ function clearSystemPromptHashSession(sessionId, handleMaps) {
170474
+ cachedUserProfileBySession.delete(sessionId);
170475
+ cachedKeyFilesBySession.delete(sessionId);
170476
+ handleMaps.stickyDateBySession.delete(sessionId);
170477
+ handleMaps.cachedDocsBySession.delete(sessionId);
170478
+ }
170439
170479
  var DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
170440
170480
  function readProjectDocs(directory) {
170441
170481
  const sections = [];
170442
170482
  for (const filename of DOC_FILES) {
170443
- const filePath = join14(directory, filename);
170483
+ const filePath = join15(directory, filename);
170444
170484
  try {
170445
170485
  if (existsSync7(filePath)) {
170446
170486
  const content = readFileSync6(filePath, "utf-8").trim();
@@ -170466,7 +170506,7 @@ function createSystemPromptHashHandler(deps) {
170466
170506
  const stickyDateBySession = new Map;
170467
170507
  const cachedDocsBySession = new Map;
170468
170508
  const shouldInjectDocs = deps.dreamerEnabled && deps.injectDocs;
170469
- return async (input, output) => {
170509
+ const handler = async (input, output) => {
170470
170510
  const sessionId = input.sessionID;
170471
170511
  if (!sessionId)
170472
170512
  return;
@@ -170633,6 +170673,15 @@ ${sections.join(`
170633
170673
  updateSessionMeta(deps.db, sessionId, { systemPromptTokens });
170634
170674
  }
170635
170675
  };
170676
+ return {
170677
+ handler,
170678
+ clearSession: (sessionId) => {
170679
+ clearSystemPromptHashSession(sessionId, {
170680
+ stickyDateBySession,
170681
+ cachedDocsBySession
170682
+ });
170683
+ }
170684
+ };
170636
170685
  }
170637
170686
 
170638
170687
  // src/hooks/magic-context/hook.ts
@@ -170671,6 +170720,13 @@ function createMagicContextHook(deps) {
170671
170720
  }
170672
170721
  const projectPath = resolveProjectIdentity(deps.directory);
170673
170722
  registerDreamProjectDirectory(projectPath, deps.directory);
170723
+ if (deps.config.compaction_markers !== false) {
170724
+ try {
170725
+ checkCompactionMarkerConsistency(db);
170726
+ } catch (error48) {
170727
+ log("[magic-context] startup compaction-marker consistency check failed:", error48);
170728
+ }
170729
+ }
170674
170730
  let lastScheduleCheckMs = 0;
170675
170731
  const getHistorianChunkTokens = () => deriveHistorianChunkTokens(resolveHistorianContextLimit(deps.config.historian?.model));
170676
170732
  const nudgePlacements = createNudgePlacementStore(db);
@@ -170763,6 +170819,9 @@ function createMagicContextHook(deps) {
170763
170819
  onSessionCacheInvalidated: (sessionId) => {
170764
170820
  clearInjectionCache(sessionId);
170765
170821
  deps.onSessionCacheInvalidated?.(sessionId);
170822
+ },
170823
+ onSessionDeleted: (sessionId) => {
170824
+ systemPromptHash.clearSession(sessionId);
170766
170825
  }
170767
170826
  });
170768
170827
  const runDreamQueueInBackground = () => {
@@ -170831,7 +170890,10 @@ function createMagicContextHook(deps) {
170831
170890
  return model ? `${model.providerID}/${model.modelID}` : undefined;
170832
170891
  })(),
170833
170892
  getNotificationParams: () => getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
170834
- historianTwoPass: deps.config.historian?.two_pass === true
170893
+ historianTwoPass: deps.config.historian?.two_pass === true,
170894
+ onInjectionCacheCleared: (sid) => {
170895
+ flushedSessions.add(sid);
170896
+ }
170835
170897
  }, options),
170836
170898
  sendNotification: async (sessionId, text, params) => {
170837
170899
  await sendIgnoredMessage(deps.client, sessionId, text, {
@@ -170861,8 +170923,7 @@ function createMagicContextHook(deps) {
170861
170923
  } : undefined
170862
170924
  } : undefined
170863
170925
  });
170864
- const emergencyNudgeFired = new Set;
170865
- const systemPromptHashHandler = createSystemPromptHashHandler({
170926
+ const systemPromptHash = createSystemPromptHashHandler({
170866
170927
  db,
170867
170928
  protectedTags: deps.config.protected_tags,
170868
170929
  ctxReduceEnabled,
@@ -170877,6 +170938,7 @@ function createMagicContextHook(deps) {
170877
170938
  experimentalPinKeyFilesTokenBudget: deps.config.dreamer?.pin_key_files?.token_budget,
170878
170939
  experimentalTemporalAwareness: deps.config.experimental?.temporal_awareness === true
170879
170940
  });
170941
+ const systemPromptHashHandler = systemPromptHash.handler;
170880
170942
  const eventHook = createEventHook({
170881
170943
  eventHandler,
170882
170944
  contextUsageMap,
@@ -170886,7 +170948,6 @@ function createMagicContextHook(deps) {
170886
170948
  agentBySession,
170887
170949
  recentReduceBySession,
170888
170950
  toolUsageSinceUserTurn,
170889
- emergencyNudgeFired,
170890
170951
  flushedSessions,
170891
170952
  lastHeuristicsTurnId,
170892
170953
  commitSeenLastPass,
@@ -172322,13 +172383,13 @@ import { dirname as dirname2 } from "path";
172322
172383
 
172323
172384
  // src/shared/rpc-utils.ts
172324
172385
  import { createHash as createHash2 } from "crypto";
172325
- import { join as join15 } from "path";
172386
+ import { join as join16 } from "path";
172326
172387
  function projectHash(directory) {
172327
172388
  const normalized = directory.replace(/\/+$/, "");
172328
172389
  return createHash2("sha256").update(normalized).digest("hex").slice(0, 16);
172329
172390
  }
172330
172391
  function rpcPortFilePath(storageDir, directory) {
172331
- return join15(storageDir, "rpc", projectHash(directory), "port");
172392
+ return join16(storageDir, "rpc", projectHash(directory), "port");
172332
172393
  }
172333
172394
 
172334
172395
  // src/shared/rpc-server.ts