@cortexkit/opencode-magic-context 0.14.0 → 0.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +11 -5
  2. package/dist/cli/diagnostics.d.ts +38 -5
  3. package/dist/cli/diagnostics.d.ts.map +1 -1
  4. package/dist/cli/logs.d.ts.map +1 -1
  5. package/dist/cli.js +158 -7
  6. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  7. package/dist/hooks/magic-context/compaction-marker-manager.d.ts +29 -0
  8. package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
  9. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  10. package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
  11. package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
  12. package/dist/hooks/magic-context/compartment-runner-types.d.ts +9 -0
  13. package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
  14. package/dist/hooks/magic-context/drop-stale-reduce-calls.d.ts.map +1 -1
  15. package/dist/hooks/magic-context/hook-handlers.d.ts +0 -2
  16. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  17. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  18. package/dist/hooks/magic-context/nudger.d.ts +1 -4
  19. package/dist/hooks/magic-context/nudger.d.ts.map +1 -1
  20. package/dist/hooks/magic-context/sentinel.d.ts +53 -0
  21. package/dist/hooks/magic-context/sentinel.d.ts.map +1 -0
  22. package/dist/hooks/magic-context/strip-content.d.ts +63 -13
  23. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  24. package/dist/hooks/magic-context/strip-structural-noise.d.ts +14 -0
  25. package/dist/hooks/magic-context/strip-structural-noise.d.ts.map +1 -1
  26. package/dist/hooks/magic-context/system-prompt-hash.d.ts +18 -5
  27. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  28. package/dist/hooks/magic-context/transform-compartment-phase.d.ts +2 -0
  29. package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
  30. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  31. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  32. package/dist/index.js +321 -180
  33. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  34. package/dist/shared/error-message.d.ts +30 -0
  35. package/dist/shared/error-message.d.ts.map +1 -1
  36. package/package.json +1 -1
  37. package/src/shared/error-message.test.ts +94 -0
  38. package/src/shared/error-message.ts +112 -0
package/dist/index.js CHANGED
@@ -15615,6 +15615,77 @@ var init_data_path = () => {};
15615
15615
  function getErrorMessage(error48) {
15616
15616
  return error48 instanceof Error ? error48.message : String(error48);
15617
15617
  }
15618
+ function readString(value) {
15619
+ if (typeof value === "string" && value.length > 0)
15620
+ return value;
15621
+ if (typeof value === "number")
15622
+ return String(value);
15623
+ return;
15624
+ }
15625
+ function clip(value, max) {
15626
+ if (value.length <= max)
15627
+ return value;
15628
+ return `${value.slice(0, max)}\u2026`;
15629
+ }
15630
+ function describeError(error48) {
15631
+ const stringForm = clip(safeString(error48), 400);
15632
+ if (!(error48 instanceof Error) && !(error48 && typeof error48 === "object")) {
15633
+ return {
15634
+ name: typeof error48,
15635
+ message: "",
15636
+ stringForm,
15637
+ brief: stringForm || "<empty>"
15638
+ };
15639
+ }
15640
+ const obj = error48;
15641
+ const nameFromField = readString(obj.name);
15642
+ const nameFromCtor = error48?.constructor?.name;
15643
+ const name = nameFromField ?? nameFromCtor ?? "Error";
15644
+ const message = readString(obj.message) ?? "";
15645
+ const status = readString(obj.status) ?? readString(obj.statusCode);
15646
+ const code = readString(obj.code);
15647
+ let causeName;
15648
+ const cause = obj.cause;
15649
+ if (cause && typeof cause === "object") {
15650
+ const causeRecord = cause;
15651
+ causeName = readString(causeRecord.name) ?? cause.constructor?.name;
15652
+ }
15653
+ const stack = readString(obj.stack);
15654
+ const stackHead = stack ? stack.split(`
15655
+ `).slice(0, 4).map((l) => l.trim()).filter((l) => l.length > 0).join(" | ") : undefined;
15656
+ const briefParts = [];
15657
+ if (name)
15658
+ briefParts.push(name);
15659
+ if (message)
15660
+ briefParts.push(`message="${clip(message, 200)}"`);
15661
+ if (status)
15662
+ briefParts.push(`status=${status}`);
15663
+ if (code)
15664
+ briefParts.push(`code=${code}`);
15665
+ if (causeName)
15666
+ briefParts.push(`cause=${causeName}`);
15667
+ if (!message && stringForm && stringForm !== name) {
15668
+ briefParts.push(`str="${clip(stringForm, 200)}"`);
15669
+ }
15670
+ const brief = briefParts.join(" ") || stringForm || name;
15671
+ return {
15672
+ name,
15673
+ message,
15674
+ ...status ? { status } : {},
15675
+ ...code ? { code } : {},
15676
+ ...causeName ? { causeName } : {},
15677
+ ...stackHead ? { stackHead } : {},
15678
+ stringForm,
15679
+ brief
15680
+ };
15681
+ }
15682
+ function safeString(value) {
15683
+ try {
15684
+ return String(value);
15685
+ } catch {
15686
+ return "<unstringifiable>";
15687
+ }
15688
+ }
15618
15689
 
15619
15690
  // src/features/magic-context/memory/storage-memory-embeddings.ts
15620
15691
  function isEmbeddingBlob(value) {
@@ -151183,7 +151254,10 @@ function incrementHistorianFailure(db, sessionId, error48) {
151183
151254
  db.transaction(() => {
151184
151255
  ensureSessionMetaRow(db, sessionId);
151185
151256
  const current = getHistorianFailureState(db, sessionId);
151186
- db.prepare("UPDATE session_meta SET historian_failure_count = ?, historian_last_error = ?, historian_last_failure_at = ? WHERE session_id = ?").run(current.failureCount + 1, error48, Date.now(), sessionId);
151257
+ const nextCount = current.failureCount + 1;
151258
+ db.prepare("UPDATE session_meta SET historian_failure_count = ?, historian_last_error = ?, historian_last_failure_at = ? WHERE session_id = ?").run(nextCount, error48, Date.now(), sessionId);
151259
+ const reason = error48.replace(/\s+/g, " ").trim().slice(0, 300);
151260
+ sessionLog(sessionId, `historian failure recorded: count=${nextCount} reason="${reason}"`);
151187
151261
  })();
151188
151262
  }
151189
151263
  function clearHistorianFailureState(db, sessionId) {
@@ -151267,6 +151341,7 @@ function removeStrippedPlaceholderId(db, sessionId, messageId) {
151267
151341
  return true;
151268
151342
  }
151269
151343
  var init_storage_meta_persisted = __esm(() => {
151344
+ init_logger();
151270
151345
  init_storage_meta_shared();
151271
151346
  });
151272
151347
 
@@ -152157,14 +152232,12 @@ async function runHistorianPrompt(args) {
152157
152232
  const dumpPath = dumpHistorianResponse(parentSessionId, dumpLabel ?? "historian-response", result);
152158
152233
  return { ok: true, result, dumpPath };
152159
152234
  } catch (modelError) {
152160
- const modelMsg = getErrorMessage(modelError);
152161
- const modelStack = modelError instanceof Error ? modelError.stack : undefined;
152162
- sessionLog(parentSessionId, "compartment agent: historian attempt failed", {
152163
- error: modelMsg,
152164
- promptLength: prompt.length,
152165
- stack: modelStack
152166
- });
152167
- return { ok: false, error: `Historian failed while processing this session: ${modelMsg}` };
152235
+ const desc = describeError(modelError);
152236
+ sessionLog(parentSessionId, `historian prompt failed: ${desc.brief} promptLength=${prompt.length}${desc.stackHead ? ` stackHead="${desc.stackHead}"` : ""}`);
152237
+ return {
152238
+ ok: false,
152239
+ error: `Historian failed while processing this session: ${desc.brief}`
152240
+ };
152168
152241
  } finally {
152169
152242
  if (agentSessionId) {
152170
152243
  await client.session.delete({ path: { id: agentSessionId }, query: { directory: sessionDirectory } }).catch((e) => {
@@ -152941,6 +153014,7 @@ async function executePartialRecompInternal(deps, range) {
152941
153014
  setRecompPartialRange(db, sessionId, null);
152942
153015
  clearCompressionDepthRange(db, sessionId, snapStart, snapEnd);
152943
153016
  clearInjectionCache(sessionId);
153017
+ deps.onInjectionCacheCleared?.(sessionId);
152944
153018
  const lastEnd = merged[merged.length - 1]?.endMessage ?? snapEnd;
152945
153019
  return { compartmentCount: merged.length, lastEndMessage: lastEnd };
152946
153020
  };
@@ -153333,6 +153407,8 @@ var init_compaction_marker = __esm(() => {
153333
153407
  });
153334
153408
 
153335
153409
  // src/hooks/magic-context/compaction-marker-manager.ts
153410
+ import { Database as SqliteDb } from "bun:sqlite";
153411
+ import { join as join14 } from "path";
153336
153412
  function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, directory) {
153337
153413
  const existing = getPersistedCompactionMarkerState(db, sessionId);
153338
153414
  if (existing) {
@@ -153374,10 +153450,63 @@ function removeCompactionMarkerForSession(db, sessionId) {
153374
153450
  }
153375
153451
  }
153376
153452
  }
153453
+ function checkCompactionMarkerConsistency(db) {
153454
+ const opencodeDbPath = join14(getDataDir(), "opencode", "opencode.db");
153455
+ let opencodeDb;
153456
+ try {
153457
+ opencodeDb = new SqliteDb(opencodeDbPath, { readonly: true });
153458
+ } catch (error48) {
153459
+ log(`[magic-context] compaction-marker consistency check skipped: ${error48 instanceof Error ? error48.message : String(error48)}`);
153460
+ return;
153461
+ }
153462
+ try {
153463
+ 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();
153464
+ if (persistedRows.length === 0)
153465
+ return;
153466
+ const checkMessage = opencodeDb.prepare("SELECT 1 FROM message WHERE id = ? LIMIT 1");
153467
+ const checkPart = opencodeDb.prepare("SELECT 1 FROM part WHERE id = ? LIMIT 1");
153468
+ let reconciledCount = 0;
153469
+ for (const row of persistedRows) {
153470
+ const state = getPersistedCompactionMarkerState(db, row.session_id);
153471
+ if (!state)
153472
+ continue;
153473
+ const boundaryExists = checkMessage.get(state.boundaryMessageId) !== null;
153474
+ const summaryMessageExists = checkMessage.get(state.summaryMessageId) !== null;
153475
+ const compactionPartExists = checkPart.get(state.compactionPartId) !== null;
153476
+ const summaryPartExists = checkPart.get(state.summaryPartId) !== null;
153477
+ const allPresent = boundaryExists && summaryMessageExists && compactionPartExists && summaryPartExists;
153478
+ if (allPresent)
153479
+ continue;
153480
+ let removedOk = false;
153481
+ try {
153482
+ removedOk = removeCompactionMarker(state);
153483
+ } catch (error48) {
153484
+ sessionLog(row.session_id, "compaction-marker consistency: partial cleanup of half-written marker failed:", error48);
153485
+ }
153486
+ if (removedOk) {
153487
+ setPersistedCompactionMarkerState(db, row.session_id, null);
153488
+ sessionLog(row.session_id, `compaction-marker consistency: cleared orphaned state (boundary=${boundaryExists} summary=${summaryMessageExists} cPart=${compactionPartExists} sPart=${summaryPartExists}); next publication will re-inject`);
153489
+ reconciledCount++;
153490
+ } else {
153491
+ 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`);
153492
+ }
153493
+ }
153494
+ if (reconciledCount > 0) {
153495
+ log(`[magic-context] compaction-marker consistency: reconciled ${reconciledCount} session(s) with orphaned marker state at startup`);
153496
+ }
153497
+ } catch (error48) {
153498
+ log(`[magic-context] compaction-marker consistency check failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
153499
+ } finally {
153500
+ try {
153501
+ opencodeDb.close(false);
153502
+ } catch {}
153503
+ }
153504
+ }
153377
153505
  var MARKER_SUMMARY_TEXT = "[Compacted by magic-context \u2014 session history is managed by the plugin]";
153378
153506
  var init_compaction_marker_manager = __esm(() => {
153379
153507
  init_compaction_marker();
153380
153508
  init_storage_meta_persisted();
153509
+ init_data_path();
153381
153510
  init_logger();
153382
153511
  });
153383
153512
 
@@ -154261,6 +154390,7 @@ async function runCompartmentAgent(deps) {
154261
154390
  const priorFacts = existingFacts;
154262
154391
  const existingValidationError = validateStoredCompartments(priorCompartments);
154263
154392
  if (existingValidationError) {
154393
+ sessionLog(sessionId, `historian failure: source=existing-validation reason="${existingValidationError}"`);
154264
154394
  await notifyHistorianIssue(`## Historian alert
154265
154395
 
154266
154396
  Historian skipped this session because existing stored compartments are invalid: ${existingValidationError}
@@ -154279,6 +154409,7 @@ No new compartments or facts were written. Rebuild or clear the broken compartme
154279
154409
  }
154280
154410
  const chunkCoverageError = validateChunkCoverage(chunk);
154281
154411
  if (chunkCoverageError) {
154412
+ sessionLog(sessionId, `historian failure: source=chunk-coverage reason="${chunkCoverageError}" chunkRange=${chunk.startIndex}-${chunk.endIndex}`);
154282
154413
  await notifyHistorianIssue(`## Historian alert
154283
154414
 
154284
154415
  Historian skipped this session because the raw chunk could not be represented safely: ${chunkCoverageError}
@@ -154314,6 +154445,7 @@ ${chunk.text}`);
154314
154445
  twoPass: deps.historianTwoPass
154315
154446
  });
154316
154447
  if (!validatedPass.ok) {
154448
+ sessionLog(sessionId, `historian failure: source=validation reason="${validatedPass.error}" chunkRange=${chunk.startIndex}-${chunk.endIndex} fallbackModel=${deps.fallbackModelId ?? "<none>"} twoPass=${deps.historianTwoPass ? "true" : "false"}`);
154317
154449
  incrementHistorianFailure(db, sessionId, validatedPass.error);
154318
154450
  await notifyHistorianIssue(`## Historian alert
154319
154451
 
@@ -154325,6 +154457,8 @@ No new compartments or facts were written. Check the historian model/output and
154325
154457
  const newCompartments = validatedPass.compartments;
154326
154458
  const lastNewEnd = newCompartments[newCompartments.length - 1]?.endMessage ?? 0;
154327
154459
  if (lastNewEnd + 1 <= offset) {
154460
+ sessionLog(sessionId, `historian failure: source=no-progress reason="historian returned compartments that did not advance past raw message ${offset - 1}" newCompartmentCount=${newCompartments.length} lastNewEnd=${lastNewEnd} priorEnd=${offset - 1}`);
154461
+ incrementHistorianFailure(db, sessionId, `no forward progress beyond raw message ${offset - 1}`);
154328
154462
  await notifyHistorianIssue(`## Historian alert
154329
154463
 
154330
154464
  Historian returned compartments that made no forward progress beyond raw message ${offset - 1}.
@@ -154339,6 +154473,7 @@ No new compartments or facts were written. Check the historian model/output and
154339
154473
  clearEmergencyRecovery(db, sessionId);
154340
154474
  })();
154341
154475
  clearInjectionCache(sessionId);
154476
+ deps.onInjectionCacheCleared?.(sessionId);
154342
154477
  if (deps.directory) {
154343
154478
  promoteSessionFactsToMemory(db, sessionId, resolveProjectIdentity(deps.directory), validatedPass.facts ?? []);
154344
154479
  }
@@ -154377,11 +154512,13 @@ No new compartments or facts were written. Check the historian model/output and
154377
154512
  }
154378
154513
  }
154379
154514
  } catch (error48) {
154380
- const msg = getErrorMessage(error48);
154515
+ const desc = describeError(error48);
154516
+ sessionLog(sessionId, `historian failure: source=exception ${desc.brief}${desc.stackHead ? ` stackHead="${desc.stackHead}"` : ""}`);
154381
154517
  if (!issueNotified) {
154518
+ incrementHistorianFailure(db, sessionId, desc.brief);
154382
154519
  await notifyHistorianIssue(`## Historian alert
154383
154520
 
154384
- Historian failed unexpectedly: ${msg}
154521
+ Historian failed unexpectedly: ${desc.brief}
154385
154522
 
154386
154523
  No new compartments or facts were written. Check the historian model/output and try again.`);
154387
154524
  }
@@ -154442,6 +154579,7 @@ async function executeContextRecompInternal(deps) {
154442
154579
  return null;
154443
154580
  clearCompressionDepth(db, sessionId);
154444
154581
  clearInjectionCache(sessionId);
154582
+ deps.onInjectionCacheCleared?.(sessionId);
154445
154583
  if (deps.directory) {
154446
154584
  promoteSessionFactsToMemory(db, sessionId, resolveProjectIdentity(deps.directory), promoted2.facts);
154447
154585
  }
@@ -154610,6 +154748,7 @@ Nothing was written.`;
154610
154748
  }
154611
154749
  clearCompressionDepth(db, sessionId);
154612
154750
  clearInjectionCache(sessionId);
154751
+ deps.onInjectionCacheCleared?.(sessionId);
154613
154752
  const finalCompartments = promoted?.compartments ?? candidateCompartments;
154614
154753
  const finalFacts = promoted?.facts ?? candidateFacts;
154615
154754
  if (deps.directory) {
@@ -162343,10 +162482,10 @@ var require_stringify = __commonJS((exports, module) => {
162343
162482
  replacer = null;
162344
162483
  indent = EMPTY;
162345
162484
  };
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;
162485
+ 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
162486
  var join_content = (inside, value, gap) => {
162348
162487
  const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
162349
- return join16(comment, inside, gap);
162488
+ return join17(comment, inside, gap);
162350
162489
  };
162351
162490
  var stringify_string = (holder, key, value) => {
162352
162491
  const raw = get_raw_string_literal(holder, key);
@@ -162368,13 +162507,13 @@ var require_stringify = __commonJS((exports, module) => {
162368
162507
  if (i !== 0) {
162369
162508
  inside += COMMA;
162370
162509
  }
162371
- const before = join16(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
162510
+ const before = join17(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
162372
162511
  inside += before || LF + deeper_gap;
162373
162512
  inside += stringify(i, value, deeper_gap) || STR_NULL;
162374
162513
  inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
162375
162514
  after_comma = process_comments(value, AFTER(i), deeper_gap);
162376
162515
  }
162377
- inside += join16(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
162516
+ inside += join17(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
162378
162517
  return BRACKET_OPEN + join_content(inside, value, gap) + BRACKET_CLOSE;
162379
162518
  };
162380
162519
  var object_stringify = (value, gap) => {
@@ -162395,13 +162534,13 @@ var require_stringify = __commonJS((exports, module) => {
162395
162534
  inside += COMMA;
162396
162535
  }
162397
162536
  first = false;
162398
- const before = join16(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
162537
+ const before = join17(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
162399
162538
  inside += before || LF + deeper_gap;
162400
162539
  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
162540
  after_comma = process_comments(value, AFTER(key), deeper_gap);
162402
162541
  };
162403
162542
  keys.forEach(iteratee);
162404
- inside += join16(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
162543
+ inside += join17(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
162405
162544
  return CURLY_BRACKET_OPEN + join_content(inside, value, gap) + CURLY_BRACKET_CLOSE;
162406
162545
  };
162407
162546
  function stringify(key, holder, gap) {
@@ -162499,11 +162638,11 @@ __export(exports_tui_config, {
162499
162638
  ensureTuiPluginEntry: () => ensureTuiPluginEntry
162500
162639
  });
162501
162640
  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";
162641
+ import { dirname as dirname3, join as join17 } from "path";
162503
162642
  function resolveTuiConfigPath() {
162504
162643
  const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
162505
- const jsoncPath = join16(configDir, "tui.jsonc");
162506
- const jsonPath = join16(configDir, "tui.json");
162644
+ const jsoncPath = join17(configDir, "tui.jsonc");
162645
+ const jsonPath = join17(configDir, "tui.json");
162507
162646
  if (existsSync8(jsoncPath))
162508
162647
  return jsoncPath;
162509
162648
  if (existsSync8(jsonPath))
@@ -166549,6 +166688,47 @@ function modelRequiresInterleavedReasoning(model) {
166549
166688
 
166550
166689
  // src/hooks/magic-context/transform.ts
166551
166690
  init_send_session_notification();
166691
+ // src/hooks/magic-context/sentinel.ts
166692
+ function makeSentinel(originalPart) {
166693
+ const sentinel = {
166694
+ type: "text",
166695
+ text: ""
166696
+ };
166697
+ if (isRecord(originalPart)) {
166698
+ if (originalPart.cache_control !== undefined) {
166699
+ sentinel.cache_control = originalPart.cache_control;
166700
+ }
166701
+ if (originalPart.cacheControl !== undefined) {
166702
+ sentinel.cacheControl = originalPart.cacheControl;
166703
+ }
166704
+ }
166705
+ return sentinel;
166706
+ }
166707
+ function isSentinel(part) {
166708
+ return isRecord(part) && part.type === "text" && typeof part.text === "string" && part.text === "";
166709
+ }
166710
+ function replaySentinelByMessageIds(messages, ids) {
166711
+ if (ids.size === 0)
166712
+ return { replayed: 0, missingIds: [] };
166713
+ const seen = new Set;
166714
+ let replayed = 0;
166715
+ for (const msg of messages) {
166716
+ const id = msg.info.id;
166717
+ if (!id || !ids.has(id))
166718
+ continue;
166719
+ seen.add(id);
166720
+ if (msg.parts.length === 1 && isSentinel(msg.parts[0]))
166721
+ continue;
166722
+ msg.parts.length = 0;
166723
+ msg.parts.push(makeSentinel(undefined));
166724
+ replayed++;
166725
+ }
166726
+ const missingIds = [];
166727
+ for (const id of ids)
166728
+ if (!seen.has(id))
166729
+ missingIds.push(id);
166730
+ return { replayed, missingIds };
166731
+ }
166552
166732
 
166553
166733
  // src/hooks/magic-context/strip-content.ts
166554
166734
  var DROPPED_PLACEHOLDER_PATTERN = /^\[dropped \u00A7\d+\u00A7\]$/;
@@ -166570,12 +166750,15 @@ function isSystemInjectedText(text) {
166570
166750
  }
166571
166751
  function stripSystemInjectedMessages(messages, protectedTailStart) {
166572
166752
  let stripped = 0;
166573
- for (let i = messages.length - 1;i >= 0; i--) {
166753
+ const sentineledIds = [];
166754
+ for (let i = 0;i < messages.length; i++) {
166574
166755
  if (i >= protectedTailStart)
166575
166756
  continue;
166576
166757
  const msg = messages[i];
166577
166758
  if (msg.parts.length === 0)
166578
166759
  continue;
166760
+ if (msg.parts.length === 1 && isSentinel(msg.parts[0]))
166761
+ continue;
166579
166762
  let hasContentPart = false;
166580
166763
  let allContentIsSystemInjection = true;
166581
166764
  for (const part of msg.parts) {
@@ -166602,11 +166785,14 @@ function stripSystemInjectedMessages(messages, protectedTailStart) {
166602
166785
  break;
166603
166786
  }
166604
166787
  if (hasContentPart && allContentIsSystemInjection) {
166605
- messages.splice(i, 1);
166788
+ msg.parts.length = 0;
166789
+ msg.parts.push(makeSentinel(undefined));
166606
166790
  stripped++;
166791
+ if (typeof msg.info.id === "string")
166792
+ sentineledIds.push(msg.info.id);
166607
166793
  }
166608
166794
  }
166609
- return stripped;
166795
+ return { stripped, sentineledIds };
166610
166796
  }
166611
166797
  var METADATA_PART_TYPES = new Set([
166612
166798
  "step-start",
@@ -166620,12 +166806,15 @@ var METADATA_PART_TYPES = new Set([
166620
166806
  ]);
166621
166807
  function stripDroppedPlaceholderMessages(messages) {
166622
166808
  let stripped = 0;
166623
- for (let i = messages.length - 1;i >= 0; i--) {
166809
+ const sentineledIds = [];
166810
+ for (let i = 0;i < messages.length; i++) {
166624
166811
  const msg = messages[i];
166625
166812
  if (msg.parts.length === 0)
166626
166813
  continue;
166627
166814
  if (msg.info.role === "user")
166628
166815
  continue;
166816
+ if (msg.parts.length === 1 && isSentinel(msg.parts[0]))
166817
+ continue;
166629
166818
  let hasContentPart = false;
166630
166819
  let hasNonDroppedContent = false;
166631
166820
  for (const part of msg.parts) {
@@ -166666,11 +166855,14 @@ function stripDroppedPlaceholderMessages(messages) {
166666
166855
  break;
166667
166856
  }
166668
166857
  if (hasContentPart && !hasNonDroppedContent) {
166669
- messages.splice(i, 1);
166858
+ msg.parts.length = 0;
166859
+ msg.parts.push(makeSentinel(undefined));
166670
166860
  stripped++;
166861
+ if (typeof msg.info.id === "string")
166862
+ sentineledIds.push(msg.info.id);
166671
166863
  }
166672
166864
  }
166673
- return stripped;
166865
+ return { stripped, sentineledIds };
166674
166866
  }
166675
166867
  function replayClearedReasoning(messages, reasoningByMessage, messageTagNumbers, persistedWatermark, skipTypedReasoningCleanup = false) {
166676
166868
  if (skipTypedReasoningCleanup)
@@ -166764,23 +166956,22 @@ function stripClearedReasoning(messages, skipTypedReasoningCleanup = false) {
166764
166956
  for (const message of messages) {
166765
166957
  if (message.info.role !== "assistant")
166766
166958
  continue;
166767
- const originalLength = message.parts.length;
166768
- const kept = message.parts.filter((part) => {
166959
+ for (let i = 0;i < message.parts.length; i++) {
166960
+ const part = message.parts[i];
166769
166961
  if (!isRecord(part))
166770
- return true;
166962
+ continue;
166771
166963
  const partType = part.type;
166772
166964
  if (!CLEARED_REASONING_TYPES.has(partType))
166773
- return true;
166965
+ continue;
166774
166966
  if (!("thinking" in part) && !("text" in part))
166775
- return true;
166967
+ continue;
166776
166968
  const thinking = "thinking" in part ? part.thinking : undefined;
166777
166969
  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;
166970
+ const isCleared = (thinking === undefined || thinking === "[cleared]") && (text === undefined || text === "[cleared]");
166971
+ if (!isCleared)
166972
+ continue;
166973
+ message.parts[i] = makeSentinel(part);
166974
+ stripped++;
166784
166975
  }
166785
166976
  }
166786
166977
  return stripped;
@@ -166868,13 +167059,15 @@ function stripReasoningFromMergedAssistants(messages) {
166868
167059
  continue;
166869
167060
  if (part.ignored === true)
166870
167061
  continue;
167062
+ if (isSentinel(part))
167063
+ continue;
166871
167064
  if (REASONING_PART_TYPES.has(partType)) {
166872
167065
  keepIndex = i;
166873
167066
  }
166874
167067
  break;
166875
167068
  }
166876
167069
  }
166877
- for (let i = message.parts.length - 1;i >= 0; i--) {
167070
+ for (let i = 0;i < message.parts.length; i++) {
166878
167071
  const part = message.parts[i];
166879
167072
  if (!isRecord(part))
166880
167073
  continue;
@@ -166884,7 +167077,7 @@ function stripReasoningFromMergedAssistants(messages) {
166884
167077
  keptReasoningInRun = true;
166885
167078
  continue;
166886
167079
  }
166887
- message.parts.splice(i, 1);
167080
+ message.parts[i] = makeSentinel(part);
166888
167081
  stripped++;
166889
167082
  }
166890
167083
  prevRole = role;
@@ -166907,7 +167100,7 @@ function stripProcessedImages(messages, watermark, messageTagNumbers) {
166907
167100
  if (maxTag > watermark) {
166908
167101
  continue;
166909
167102
  }
166910
- for (let j = msg.parts.length - 1;j >= 0; j--) {
167103
+ for (let j = 0;j < msg.parts.length; j++) {
166911
167104
  const part = msg.parts[j];
166912
167105
  if (!isRecord(part) || part.type !== "file") {
166913
167106
  continue;
@@ -166916,7 +167109,7 @@ function stripProcessedImages(messages, watermark, messageTagNumbers) {
166916
167109
  continue;
166917
167110
  }
166918
167111
  if (typeof part.url === "string" && part.url.startsWith("data:") && part.url.length > 200) {
166919
- msg.parts.splice(j, 1);
167112
+ msg.parts[j] = makeSentinel(part);
166920
167113
  stripped++;
166921
167114
  }
166922
167115
  }
@@ -167014,7 +167207,8 @@ async function runCompartmentPhase(args) {
167014
167207
  experimentalUserMemories: args.experimentalUserMemories,
167015
167208
  historianTwoPass: args.historianTwoPass,
167016
167209
  compressorMinCompartmentRatio: args.compressorMinCompartmentRatio,
167017
- compressorMaxMergeDepth: args.compressorMaxMergeDepth
167210
+ compressorMaxMergeDepth: args.compressorMaxMergeDepth,
167211
+ onInjectionCacheCleared: args.onInjectionCacheCleared
167018
167212
  });
167019
167213
  compartmentInProgress = true;
167020
167214
  }
@@ -167038,7 +167232,8 @@ async function runCompartmentPhase(args) {
167038
167232
  experimentalUserMemories: args.experimentalUserMemories,
167039
167233
  historianTwoPass: args.historianTwoPass,
167040
167234
  compressorMinCompartmentRatio: args.compressorMinCompartmentRatio,
167041
- compressorMaxMergeDepth: args.compressorMaxMergeDepth
167235
+ compressorMaxMergeDepth: args.compressorMaxMergeDepth,
167236
+ onInjectionCacheCleared: args.onInjectionCacheCleared
167042
167237
  });
167043
167238
  activeRun = getActiveCompartmentRun(args.sessionId);
167044
167239
  } else if (!activeRun && hasEligibleHistoryForCompartment()) {
@@ -167284,19 +167479,20 @@ function stripStructuralNoise(messages) {
167284
167479
  if (!Array.isArray(message.parts)) {
167285
167480
  continue;
167286
167481
  }
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;
167482
+ for (let i = 0;i < message.parts.length; i++) {
167483
+ const part = message.parts[i];
167484
+ if (isSentinel(part))
167485
+ continue;
167486
+ if (!isStructuralNoisePart(part))
167487
+ continue;
167488
+ message.parts[i] = makeSentinel(part);
167489
+ strippedParts++;
167293
167490
  }
167294
167491
  }
167295
167492
  return strippedParts;
167296
167493
  }
167297
167494
  // src/hooks/magic-context/tag-messages.ts
167298
167495
  init_storage();
167299
-
167300
167496
  // src/hooks/magic-context/drop-stale-reduce-calls.ts
167301
167497
  var STALE_TOOL_NAMES = new Set(["ctx_reduce"]);
167302
167498
  function isReduceToolPart(part) {
@@ -167328,20 +167524,25 @@ function hasAnyMeaningfulPart(parts) {
167328
167524
  function dropStaleReduceCalls(messages, protectedCount = 0) {
167329
167525
  let didDrop = false;
167330
167526
  const protectedStart = messages.length - protectedCount;
167331
- for (let i = messages.length - 1;i >= 0; i -= 1) {
167527
+ for (let i = 0;i < messages.length; i++) {
167332
167528
  if (i >= protectedStart)
167333
- continue;
167529
+ break;
167334
167530
  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);
167531
+ let touched = false;
167532
+ for (let j = 0;j < message.parts.length; j++) {
167533
+ const part = message.parts[j];
167534
+ if (isSentinel(part))
167535
+ continue;
167536
+ if (isReduceToolPart(part)) {
167537
+ message.parts[j] = makeSentinel(part);
167538
+ touched = true;
167339
167539
  }
167340
167540
  }
167341
- if (message.parts.length < originalLength) {
167541
+ if (touched) {
167342
167542
  didDrop = true;
167343
167543
  if (!hasAnyMeaningfulPart(message.parts)) {
167344
- messages.splice(i, 1);
167544
+ message.parts.length = 0;
167545
+ message.parts.push(makeSentinel(undefined));
167345
167546
  }
167346
167547
  }
167347
167548
  }
@@ -168906,55 +169107,31 @@ async function runPostTransformPhase(args) {
168906
169107
  {
168907
169108
  const persistedIds = getStrippedPlaceholderIds(args.db, args.sessionId);
168908
169109
  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
- }
169110
+ const { replayed, missingIds } = replaySentinelByMessageIds(args.messages, persistedIds);
168917
169111
  if (replayed > 0) {
168918
- sessionLog(args.sessionId, `placeholder replay: removed ${replayed} previously-stripped messages`);
169112
+ sessionLog(args.sessionId, `sentinel replay: neutralized ${replayed} previously-stripped messages`);
169113
+ }
169114
+ if (missingIds.length > 0) {
169115
+ for (const id of missingIds)
169116
+ persistedIds.delete(id);
169117
+ setStrippedPlaceholderIds(args.db, args.sessionId, persistedIds);
168919
169118
  }
168920
169119
  }
168921
169120
  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
- }
169121
+ const droppedResult = stripDroppedPlaceholderMessages(args.messages);
169122
+ const protectedTailStart = Math.max(0, args.messages.length - args.protectedTags * 2);
169123
+ const systemInjectedResult = stripSystemInjectedMessages(args.messages, protectedTailStart);
169124
+ const newlyNeutralized = droppedResult.sentineledIds.length + systemInjectedResult.sentineledIds.length;
169125
+ if (newlyNeutralized > 0) {
169126
+ for (const id of droppedResult.sentineledIds)
169127
+ persistedIds.add(id);
169128
+ for (const id of systemInjectedResult.sentineledIds)
169129
+ persistedIds.add(id);
168946
169130
  setStrippedPlaceholderIds(args.db, args.sessionId, persistedIds);
168947
- sessionLog(args.sessionId, `stripped ${strippedDropped} placeholder messages (${newlyStrippedCount} new, ${persistedIds.size} total persisted)`);
169131
+ sessionLog(args.sessionId, `neutralized ${droppedResult.stripped} dropped + ${systemInjectedResult.stripped} system-injected messages (${newlyNeutralized} new, ${persistedIds.size} total persisted)`);
168948
169132
  }
168949
169133
  }
168950
169134
  }
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
169135
  const pendingUserTurnReminder = getPersistedStickyTurnReminder(args.db, args.sessionId);
168959
169136
  if (pendingUserTurnReminder) {
168960
169137
  if (args.hasRecentReduceCall && isCacheBustingPass) {
@@ -169243,7 +169420,10 @@ function createTransform(deps) {
169243
169420
  experimentalTemporalAwareness: deps.experimentalTemporalAwareness,
169244
169421
  historianTwoPass: deps.historianTwoPass,
169245
169422
  compressorMinCompartmentRatio: deps.compressorMinCompartmentRatio,
169246
- compressorMaxMergeDepth: deps.compressorMaxMergeDepth
169423
+ compressorMaxMergeDepth: deps.compressorMaxMergeDepth,
169424
+ onInjectionCacheCleared: (sid) => {
169425
+ deps.flushedSessions.add(sid);
169426
+ }
169247
169427
  });
169248
169428
  skipCompartmentAwaitForThisPass = true;
169249
169429
  return true;
@@ -169391,7 +169571,10 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
169391
169571
  historianTwoPass: deps.historianTwoPass,
169392
169572
  compressorMinCompartmentRatio: deps.compressorMinCompartmentRatio,
169393
169573
  compressorMaxMergeDepth: deps.compressorMaxMergeDepth,
169394
- compressorCooldownMs: deps.compressorCooldownMs
169574
+ compressorCooldownMs: deps.compressorCooldownMs,
169575
+ onInjectionCacheCleared: (sid) => {
169576
+ deps.flushedSessions.add(sid);
169577
+ }
169395
169578
  });
169396
169579
  pendingCompartmentInjection = compartmentPhase.pendingCompartmentInjection;
169397
169580
  const awaitedCompartmentRun = compartmentPhase.awaitedCompartmentRun;
@@ -170000,29 +170183,6 @@ function estimateProjectedPercentage(db, sessionId, contextUsage, preloadedTags)
170000
170183
  const dropRatio = pendingDropBytes / totalActiveBytes;
170001
170184
  return contextUsage.percentage * (1 - dropRatio);
170002
170185
  }
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
170186
 
170027
170187
  // src/hooks/magic-context/hook.ts
170028
170188
  init_read_session_db();
@@ -170036,6 +170196,7 @@ function createTextCompleteHandler() {
170036
170196
  }
170037
170197
 
170038
170198
  // src/hooks/magic-context/hook.ts
170199
+ init_compaction_marker_manager();
170039
170200
  init_compartment_runner();
170040
170201
 
170041
170202
  // src/hooks/magic-context/hook-handlers.ts
@@ -170121,59 +170282,12 @@ function createEventHook(args) {
170121
170282
  args.agentBySession.delete(sessionId);
170122
170283
  args.recentReduceBySession.delete(sessionId);
170123
170284
  args.toolUsageSinceUserTurn.delete(sessionId);
170124
- args.emergencyNudgeFired.delete(sessionId);
170125
170285
  args.flushedSessions.delete(sessionId);
170126
170286
  args.lastHeuristicsTurnId.delete(sessionId);
170127
170287
  args.commitSeenLastPass?.delete(sessionId);
170128
170288
  clearNoteNudgeState(args.db, sessionId);
170129
170289
  clearAutoSearchForSession(sessionId);
170130
170290
  }
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
170291
  };
170178
170292
  }
170179
170293
  function createCommandExecuteBeforeHook(commandHandler) {
@@ -170217,7 +170331,7 @@ init_send_session_notification();
170217
170331
 
170218
170332
  // src/hooks/magic-context/system-prompt-hash.ts
170219
170333
  import { existsSync as existsSync7, readFileSync as readFileSync6, realpathSync } from "fs";
170220
- import { join as join14, resolve as resolve3, sep } from "path";
170334
+ import { join as join15, resolve as resolve3, sep } from "path";
170221
170335
 
170222
170336
  // src/agents/magic-context-prompt.ts
170223
170337
  function getToolHistoryGuidance(dropToolStructure) {
@@ -170436,11 +170550,17 @@ var USER_PROFILE_MARKER = "<user-profile>";
170436
170550
  var KEY_FILES_MARKER = "<key-files>";
170437
170551
  var cachedUserProfileBySession = new Map;
170438
170552
  var cachedKeyFilesBySession = new Map;
170553
+ function clearSystemPromptHashSession(sessionId, handleMaps) {
170554
+ cachedUserProfileBySession.delete(sessionId);
170555
+ cachedKeyFilesBySession.delete(sessionId);
170556
+ handleMaps.stickyDateBySession.delete(sessionId);
170557
+ handleMaps.cachedDocsBySession.delete(sessionId);
170558
+ }
170439
170559
  var DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
170440
170560
  function readProjectDocs(directory) {
170441
170561
  const sections = [];
170442
170562
  for (const filename of DOC_FILES) {
170443
- const filePath = join14(directory, filename);
170563
+ const filePath = join15(directory, filename);
170444
170564
  try {
170445
170565
  if (existsSync7(filePath)) {
170446
170566
  const content = readFileSync6(filePath, "utf-8").trim();
@@ -170466,7 +170586,7 @@ function createSystemPromptHashHandler(deps) {
170466
170586
  const stickyDateBySession = new Map;
170467
170587
  const cachedDocsBySession = new Map;
170468
170588
  const shouldInjectDocs = deps.dreamerEnabled && deps.injectDocs;
170469
- return async (input, output) => {
170589
+ const handler = async (input, output) => {
170470
170590
  const sessionId = input.sessionID;
170471
170591
  if (!sessionId)
170472
170592
  return;
@@ -170633,6 +170753,15 @@ ${sections.join(`
170633
170753
  updateSessionMeta(deps.db, sessionId, { systemPromptTokens });
170634
170754
  }
170635
170755
  };
170756
+ return {
170757
+ handler,
170758
+ clearSession: (sessionId) => {
170759
+ clearSystemPromptHashSession(sessionId, {
170760
+ stickyDateBySession,
170761
+ cachedDocsBySession
170762
+ });
170763
+ }
170764
+ };
170636
170765
  }
170637
170766
 
170638
170767
  // src/hooks/magic-context/hook.ts
@@ -170671,6 +170800,13 @@ function createMagicContextHook(deps) {
170671
170800
  }
170672
170801
  const projectPath = resolveProjectIdentity(deps.directory);
170673
170802
  registerDreamProjectDirectory(projectPath, deps.directory);
170803
+ if (deps.config.compaction_markers !== false) {
170804
+ try {
170805
+ checkCompactionMarkerConsistency(db);
170806
+ } catch (error48) {
170807
+ log("[magic-context] startup compaction-marker consistency check failed:", error48);
170808
+ }
170809
+ }
170674
170810
  let lastScheduleCheckMs = 0;
170675
170811
  const getHistorianChunkTokens = () => deriveHistorianChunkTokens(resolveHistorianContextLimit(deps.config.historian?.model));
170676
170812
  const nudgePlacements = createNudgePlacementStore(db);
@@ -170763,6 +170899,9 @@ function createMagicContextHook(deps) {
170763
170899
  onSessionCacheInvalidated: (sessionId) => {
170764
170900
  clearInjectionCache(sessionId);
170765
170901
  deps.onSessionCacheInvalidated?.(sessionId);
170902
+ },
170903
+ onSessionDeleted: (sessionId) => {
170904
+ systemPromptHash.clearSession(sessionId);
170766
170905
  }
170767
170906
  });
170768
170907
  const runDreamQueueInBackground = () => {
@@ -170831,7 +170970,10 @@ function createMagicContextHook(deps) {
170831
170970
  return model ? `${model.providerID}/${model.modelID}` : undefined;
170832
170971
  })(),
170833
170972
  getNotificationParams: () => getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
170834
- historianTwoPass: deps.config.historian?.two_pass === true
170973
+ historianTwoPass: deps.config.historian?.two_pass === true,
170974
+ onInjectionCacheCleared: (sid) => {
170975
+ flushedSessions.add(sid);
170976
+ }
170835
170977
  }, options),
170836
170978
  sendNotification: async (sessionId, text, params) => {
170837
170979
  await sendIgnoredMessage(deps.client, sessionId, text, {
@@ -170861,8 +171003,7 @@ function createMagicContextHook(deps) {
170861
171003
  } : undefined
170862
171004
  } : undefined
170863
171005
  });
170864
- const emergencyNudgeFired = new Set;
170865
- const systemPromptHashHandler = createSystemPromptHashHandler({
171006
+ const systemPromptHash = createSystemPromptHashHandler({
170866
171007
  db,
170867
171008
  protectedTags: deps.config.protected_tags,
170868
171009
  ctxReduceEnabled,
@@ -170877,6 +171018,7 @@ function createMagicContextHook(deps) {
170877
171018
  experimentalPinKeyFilesTokenBudget: deps.config.dreamer?.pin_key_files?.token_budget,
170878
171019
  experimentalTemporalAwareness: deps.config.experimental?.temporal_awareness === true
170879
171020
  });
171021
+ const systemPromptHashHandler = systemPromptHash.handler;
170880
171022
  const eventHook = createEventHook({
170881
171023
  eventHandler,
170882
171024
  contextUsageMap,
@@ -170886,7 +171028,6 @@ function createMagicContextHook(deps) {
170886
171028
  agentBySession,
170887
171029
  recentReduceBySession,
170888
171030
  toolUsageSinceUserTurn,
170889
- emergencyNudgeFired,
170890
171031
  flushedSessions,
170891
171032
  lastHeuristicsTurnId,
170892
171033
  commitSeenLastPass,
@@ -172322,13 +172463,13 @@ import { dirname as dirname2 } from "path";
172322
172463
 
172323
172464
  // src/shared/rpc-utils.ts
172324
172465
  import { createHash as createHash2 } from "crypto";
172325
- import { join as join15 } from "path";
172466
+ import { join as join16 } from "path";
172326
172467
  function projectHash(directory) {
172327
172468
  const normalized = directory.replace(/\/+$/, "");
172328
172469
  return createHash2("sha256").update(normalized).digest("hex").slice(0, 16);
172329
172470
  }
172330
172471
  function rpcPortFilePath(storageDir, directory) {
172331
- return join15(storageDir, "rpc", projectHash(directory), "port");
172472
+ return join16(storageDir, "rpc", projectHash(directory), "port");
172332
172473
  }
172333
172474
 
172334
172475
  // src/shared/rpc-server.ts