@cortexkit/pi-magic-context 0.21.4 → 0.21.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -141906,6 +141906,10 @@ function getLastCompartmentEndMessage(db, sessionId) {
141906
141906
  const row = db.prepare("SELECT MAX(end_message) as max_end FROM compartments WHERE session_id = ?").get(sessionId);
141907
141907
  return row?.max_end ?? -1;
141908
141908
  }
141909
+ function getCompartmentsByEndMessageId(db, sessionId, endMessageId) {
141910
+ const rows = db.prepare("SELECT * FROM compartments WHERE session_id = ? AND end_message_id = ? ORDER BY sequence ASC").all(sessionId, endMessageId).filter(isCompartmentRow);
141911
+ return rows.map(toCompartment);
141912
+ }
141909
141913
  function appendCompartments(db, sessionId, compartments) {
141910
141914
  if (compartments.length === 0)
141911
141915
  return;
@@ -142015,7 +142019,7 @@ function promoteRecompStaging(db, sessionId) {
142015
142019
  insertFactRows(db, sessionId, staging.facts, now);
142016
142020
  db.prepare("DELETE FROM recomp_compartments WHERE session_id = ?").run(sessionId);
142017
142021
  db.prepare("DELETE FROM recomp_facts WHERE session_id = ?").run(sessionId);
142018
- db.prepare("UPDATE session_meta SET memory_block_cache = '' WHERE session_id = ?").run(sessionId);
142022
+ db.prepare("UPDATE session_meta SET memory_block_cache = '', memory_block_ids = '' WHERE session_id = ?").run(sessionId);
142019
142023
  return { compartments: staging.compartments, facts: staging.facts };
142020
142024
  })();
142021
142025
  }
@@ -142245,8 +142249,10 @@ function deleteOrphanProjectKeyFiles(db, now = Date.now()) {
142245
142249
  if (existsSync2(row.projectPath))
142246
142250
  continue;
142247
142251
  try {
142248
- db.prepare("DELETE FROM project_key_files WHERE project_path = ?").run(row.projectPath);
142249
- db.prepare("DELETE FROM project_key_files_version WHERE project_path = ?").run(row.projectPath);
142252
+ db.transaction(() => {
142253
+ db.prepare("DELETE FROM project_key_files WHERE project_path = ?").run(row.projectPath);
142254
+ db.prepare("DELETE FROM project_key_files_version WHERE project_path = ?").run(row.projectPath);
142255
+ })();
142250
142256
  deletedProjects++;
142251
142257
  } catch (error) {
142252
142258
  log(`[key-files] orphan GC failed for ${row.projectPath}:`, error);
@@ -142653,6 +142659,16 @@ var MIGRATIONS = [
142653
142659
  WHERE auto_search_hint_decisions IS NULL
142654
142660
  `);
142655
142661
  }
142662
+ },
142663
+ {
142664
+ version: 18,
142665
+ description: "Add pending_pi_compaction_marker_state column for Pi deferred marker drain",
142666
+ up: (db) => {
142667
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
142668
+ if (!cols.some((c) => c.name === "pending_pi_compaction_marker_state")) {
142669
+ db.exec("ALTER TABLE session_meta ADD COLUMN pending_pi_compaction_marker_state TEXT");
142670
+ }
142671
+ }
142656
142672
  }
142657
142673
  ];
142658
142674
  function ensureMigrationsTable(db) {
@@ -142668,7 +142684,7 @@ function getCurrentVersion(db) {
142668
142684
  const row = db.prepare("SELECT MAX(version) as version FROM schema_migrations").get();
142669
142685
  return row?.version ?? 0;
142670
142686
  }
142671
- function isSiblingMigrationConflict(error, version) {
142687
+ function isSiblingMigrationConflict(db, error, version) {
142672
142688
  if (!(error instanceof Error))
142673
142689
  return false;
142674
142690
  const code = error.code;
@@ -142680,7 +142696,8 @@ function isSiblingMigrationConflict(error, version) {
142680
142696
  return false;
142681
142697
  if (!msg.toLowerCase().includes("version"))
142682
142698
  return false;
142683
- return true;
142699
+ const confirmed = db.prepare("SELECT 1 FROM schema_migrations WHERE version = ?").get(version);
142700
+ return confirmed != null;
142684
142701
  }
142685
142702
  function runMigrations(db) {
142686
142703
  ensureMigrationsTable(db);
@@ -142701,7 +142718,7 @@ function runMigrations(db) {
142701
142718
  log(`[migrations] applied v${migration.version}: ${migration.description}`);
142702
142719
  migrationIndex += 1;
142703
142720
  } catch (error) {
142704
- if (isSiblingMigrationConflict(error, migration.version)) {
142721
+ if (isSiblingMigrationConflict(db, error, migration.version)) {
142705
142722
  log(`[migrations] v${migration.version} already applied by sibling instance — resuming with re-read version`);
142706
142723
  const reReadVersion = getCurrentVersion(db);
142707
142724
  if (reReadVersion <= currentVersion) {
@@ -143298,6 +143315,11 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143298
143315
  -- Excluded from healNullTextColumns. Readers filter IS NOT NULL AND
143299
143316
  -- != empty-string defensively. Plan v6 section 3.
143300
143317
  pending_compaction_marker_state TEXT,
143318
+ -- pending_pi_compaction_marker_state: intentionally NULLABLE without a
143319
+ -- default. Absence of a deferred Pi-native marker is SQL NULL; presence
143320
+ -- is a valid JSON blob written via setPendingPiCompactionMarkerState.
143321
+ -- Excluded from healNullTextColumns.
143322
+ pending_pi_compaction_marker_state TEXT,
143301
143323
  -- deferred_execute_state: intentionally NULLABLE without a default.
143302
143324
  -- Absence is SQL NULL; presence is a JSON blob written via
143303
143325
  -- setDeferredExecutePendingIfAbsent. Excluded from healNullTextColumns.
@@ -143394,6 +143416,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143394
143416
  ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
143395
143417
  ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
143396
143418
  ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
143419
+ ensureColumn(db, "session_meta", "pending_pi_compaction_marker_state", "TEXT");
143397
143420
  ensureColumn(db, "session_meta", "deferred_execute_state", "TEXT");
143398
143421
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
143399
143422
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -144009,6 +144032,39 @@ function clearPendingCompactionMarkerStateIf(db, sessionId, expected) {
144009
144032
  WHERE session_id = ? AND pending_compaction_marker_state = ?`).run(sessionId, expectedBlob);
144010
144033
  return result.changes > 0;
144011
144034
  }
144035
+ function isPendingPiCompactionMarker(value) {
144036
+ return typeof value === "object" && value !== null && typeof value.firstKeptEntryId === "string" && typeof value.endMessageId === "string" && typeof value.ordinal === "number" && typeof value.tokensBefore === "number" && typeof value.summary === "string" && typeof value.publishedAt === "number";
144037
+ }
144038
+ function getPendingPiCompactionMarkerState(db, sessionId) {
144039
+ const row = db.prepare("SELECT pending_pi_compaction_marker_state FROM session_meta WHERE session_id = ?").get(sessionId);
144040
+ const raw = row?.pending_pi_compaction_marker_state;
144041
+ if (raw === null || raw === undefined || raw === "")
144042
+ return null;
144043
+ try {
144044
+ const parsed = JSON.parse(raw);
144045
+ if (isPendingPiCompactionMarker(parsed)) {
144046
+ return parsed;
144047
+ }
144048
+ } catch {}
144049
+ return null;
144050
+ }
144051
+ function setPendingPiCompactionMarkerState(db, sessionId, state) {
144052
+ ensureSessionMetaRow(db, sessionId);
144053
+ const blob = state ? stableStringify(state) : null;
144054
+ db.prepare("UPDATE session_meta SET pending_pi_compaction_marker_state = ? WHERE session_id = ?").run(blob, sessionId);
144055
+ }
144056
+ function clearPendingPiCompactionMarkerStateIf(db, sessionId, expected) {
144057
+ const expectedBlob = stableStringify(expected);
144058
+ const result = db.prepare(`UPDATE session_meta SET pending_pi_compaction_marker_state = NULL
144059
+ WHERE session_id = ? AND pending_pi_compaction_marker_state = ?`).run(sessionId, expectedBlob);
144060
+ return result.changes > 0;
144061
+ }
144062
+ function getSessionsWithPendingPiMarker(db) {
144063
+ const rows = db.prepare(`SELECT session_id FROM session_meta
144064
+ WHERE pending_pi_compaction_marker_state IS NOT NULL
144065
+ AND pending_pi_compaction_marker_state != ''`).all();
144066
+ return rows.map((r) => r.session_id);
144067
+ }
144012
144068
  function peekDeferredExecutePending(db, sessionId) {
144013
144069
  const row = db.prepare("SELECT deferred_execute_state FROM session_meta WHERE session_id = ?").get(sessionId);
144014
144070
  const raw = row?.deferred_execute_state;
@@ -144129,6 +144185,12 @@ function getNoteById(db, noteId) {
144129
144185
  const row = db.prepare("SELECT * FROM notes WHERE id = ?").get(noteId);
144130
144186
  return isNoteRow(row) ? toNote(row) : null;
144131
144187
  }
144188
+ function noteBelongsToScope(note, scope) {
144189
+ if (note.type === "session") {
144190
+ return note.sessionId === scope.sessionId;
144191
+ }
144192
+ return note.projectPath === scope.projectPath;
144193
+ }
144132
144194
  function buildStatusClause(status) {
144133
144195
  if (status === undefined) {
144134
144196
  return null;
@@ -144190,9 +144252,9 @@ function getPendingSmartNotes(db, projectPath) {
144190
144252
  function getReadySmartNotes(db, projectPath) {
144191
144253
  return getSmartNotes(db, projectPath, "ready");
144192
144254
  }
144193
- function updateNote(db, noteId, updates) {
144255
+ function updateNote(db, noteId, updates, scope) {
144194
144256
  const existing = getNoteById(db, noteId);
144195
- if (!existing) {
144257
+ if (!existing || !noteBelongsToScope(existing, scope)) {
144196
144258
  return null;
144197
144259
  }
144198
144260
  const now = Date.now();
@@ -144239,7 +144301,11 @@ function updateNote(db, noteId, updates) {
144239
144301
  const result = db.prepare(`UPDATE notes SET ${sets.join(", ")} WHERE id = ? RETURNING *`).get(...params);
144240
144302
  return isNoteRow(result) ? toNote(result) : null;
144241
144303
  }
144242
- function dismissNote(db, noteId) {
144304
+ function dismissNote(db, noteId, scope) {
144305
+ const existing = getNoteById(db, noteId);
144306
+ if (!existing || !noteBelongsToScope(existing, scope)) {
144307
+ return false;
144308
+ }
144243
144309
  const result = db.prepare("UPDATE notes SET status = 'dismissed', updated_at = ? WHERE id = ? AND status != 'dismissed'").run(Date.now(), noteId);
144244
144310
  return result.changes > 0;
144245
144311
  }
@@ -144901,7 +144967,21 @@ function resolveHistorianContextLimit(historianModelOverride) {
144901
144967
 
144902
144968
  // ../plugin/src/hooks/magic-context/event-resolvers.ts
144903
144969
  init_logger();
144970
+ var DEFAULT_CONTEXT_LIMIT = 128000;
144904
144971
  var MAX_EXECUTE_THRESHOLD = 80;
144972
+ function resolveContextLimit(providerID, modelID, ctx) {
144973
+ const fromModelsDev = providerID && modelID ? getModelsDevContextLimit(providerID, modelID) : undefined;
144974
+ const baseline = fromModelsDev ?? DEFAULT_CONTEXT_LIMIT;
144975
+ if (ctx?.db && ctx.sessionID) {
144976
+ try {
144977
+ const overflow = getOverflowState(ctx.db, ctx.sessionID);
144978
+ if (overflow.detectedContextLimit > 0 && overflow.detectedContextLimit < baseline) {
144979
+ return overflow.detectedContextLimit;
144980
+ }
144981
+ } catch {}
144982
+ }
144983
+ return baseline;
144984
+ }
144905
144985
  function resolveCacheTtl(cacheTtl, modelKey) {
144906
144986
  if (typeof cacheTtl === "string") {
144907
144987
  return cacheTtl;
@@ -145269,6 +145349,14 @@ class PiSubagentRunner {
145269
145349
  }
145270
145350
  async run(options) {
145271
145351
  const startTime = Date.now();
145352
+ if (options.signal?.aborted) {
145353
+ return {
145354
+ ok: false,
145355
+ reason: "abort",
145356
+ error: "pi subagent aborted by caller",
145357
+ durationMs: Date.now() - startTime
145358
+ };
145359
+ }
145272
145360
  const args = buildArgs(options);
145273
145361
  return new Promise((resolve2) => {
145274
145362
  let settled = false;
@@ -145301,6 +145389,16 @@ class PiSubagentRunner {
145301
145389
  });
145302
145390
  return;
145303
145391
  }
145392
+ if (options.signal?.aborted) {
145393
+ terminateChild(child);
145394
+ settle({
145395
+ ok: false,
145396
+ reason: "abort",
145397
+ error: "pi subagent aborted by caller",
145398
+ durationMs: Date.now() - startTime
145399
+ });
145400
+ return;
145401
+ }
145304
145402
  emitProgress({ type: "spawned", argv: args, pid: child.pid });
145305
145403
  let stderr = "";
145306
145404
  child.stderr?.on("data", (chunk) => {
@@ -164750,6 +164848,21 @@ var FILE_PATTERN = /\{file:([^}]+)\}/g;
164750
164848
  function substituteConfigVariables(input) {
164751
164849
  const warnings = [];
164752
164850
  let text = input.text;
164851
+ if (input.isProjectConfig) {
164852
+ const hasEnvTokens = ENV_PATTERN.test(text);
164853
+ const hasFileTokens = FILE_PATTERN.test(text);
164854
+ ENV_PATTERN.lastIndex = 0;
164855
+ FILE_PATTERN.lastIndex = 0;
164856
+ if (hasEnvTokens || hasFileTokens) {
164857
+ const tokenTypes = [
164858
+ hasEnvTokens ? "{env:}" : undefined,
164859
+ hasFileTokens ? "{file:}" : undefined
164860
+ ].filter(Boolean).join(" and ");
164861
+ warnings.push(`Project-level config no longer supports ${tokenTypes} tokens for security reasons; leaving tokens literal. Move secret expansion to user-level config.`);
164862
+ }
164863
+ return { text, warnings };
164864
+ }
164865
+ text = stripJsonComments(text);
164753
164866
  text = text.replace(ENV_PATTERN, (_, rawName) => {
164754
164867
  const varName = rawName.trim();
164755
164868
  const value = varName ? process.env[varName] : undefined;
@@ -164757,7 +164870,7 @@ function substituteConfigVariables(input) {
164757
164870
  warnings.push(`Environment variable ${varName} is not set (referenced via {env:${varName}}); using empty string`);
164758
164871
  return "";
164759
164872
  }
164760
- return value;
164873
+ return JSON.stringify(value).slice(1, -1);
164761
164874
  });
164762
164875
  const fileMatches = Array.from(text.matchAll(FILE_PATTERN));
164763
164876
  if (fileMatches.length === 0) {
@@ -166195,8 +166308,7 @@ var FORCE_COMPARTMENT_PERCENTAGE = 80;
166195
166308
  function getProactiveCompartmentTriggerPercentage(executeThresholdPercentage) {
166196
166309
  return Math.max(0, executeThresholdPercentage - PROACTIVE_TRIGGER_OFFSET_PERCENTAGE);
166197
166310
  }
166198
- function estimateProjectedPostDropPercentage(db, sessionId, usage, autoDropToolAge, protectedTags, clearReasoningAge, clearedReasoningThroughTag, dropToolStructure = true) {
166199
- const activeTags = getTagsBySession(db, sessionId).filter((tag) => tag.status === "active");
166311
+ function estimateProjectedPostDropPercentage(db, sessionId, usage, activeTags, autoDropToolAge, protectedTags, clearReasoningAge, clearedReasoningThroughTag, dropToolStructure = true) {
166200
166312
  const totalActiveBytes = activeTags.reduce((sum, tag) => sum + tag.byteSize + tag.reasoningByteSize, 0);
166201
166313
  if (totalActiveBytes === 0)
166202
166314
  return null;
@@ -166283,15 +166395,24 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget) {
166283
166395
  }
166284
166396
  });
166285
166397
  }
166286
- function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, autoDropToolAge, protectedTagCount, clearReasoningAge, dropToolStructure = true, commitClusterTrigger) {
166398
+ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, autoDropToolAge, protectedTagCount, clearReasoningAge, dropToolStructure = true, commitClusterTrigger, preloadedActiveTags) {
166287
166399
  if (sessionMeta.compartmentInProgress) {
166400
+ sessionLog(sessionId, `compartment trigger: skipped — historian already in progress (usage=${usage.percentage.toFixed(1)}%)`);
166288
166401
  return { shouldFire: false };
166289
166402
  }
166290
166403
  const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget);
166291
166404
  if (!tailInfo.hasNewRawHistory) {
166405
+ try {
166406
+ const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
166407
+ const rawMessageCount = getRawSessionMessageCount(sessionId);
166408
+ const protectedTailStart = getProtectedTailStartOrdinal(sessionId);
166409
+ sessionLog(sessionId, `compartment trigger: skipped — no new raw history (usage=${usage.percentage.toFixed(1)}% nextStartOrdinal=${tailInfo.nextStartOrdinal} lastCompartmentEnd=${lastCompartmentEnd} rawMessageCount=${rawMessageCount} protectedTailStart=${protectedTailStart})`);
166410
+ } catch (error51) {
166411
+ sessionLog(sessionId, `compartment trigger: skipped — no new raw history (usage=${usage.percentage.toFixed(1)}% nextStartOrdinal=${tailInfo.nextStartOrdinal} diagnostic-collection-failed: ${error51 instanceof Error ? error51.message : String(error51)})`);
166412
+ }
166292
166413
  return { shouldFire: false };
166293
166414
  }
166294
- const projectedPostDropPercentage = estimateProjectedPostDropPercentage(db, sessionId, usage, autoDropToolAge, protectedTagCount, clearReasoningAge, sessionMeta.clearedReasoningThroughTag, dropToolStructure);
166415
+ const projectedPostDropPercentage = estimateProjectedPostDropPercentage(db, sessionId, usage, preloadedActiveTags ?? getActiveTagsBySession(db, sessionId), autoDropToolAge, protectedTagCount, clearReasoningAge, sessionMeta.clearedReasoningThroughTag, dropToolStructure);
166295
166416
  const relativePostDropTarget = executeThresholdPercentage * POST_DROP_TARGET_RATIO;
166296
166417
  if (usage.percentage >= FORCE_COMPARTMENT_PERCENTAGE) {
166297
166418
  if (projectedPostDropPercentage !== null && projectedPostDropPercentage <= relativePostDropTarget) {
@@ -166313,6 +166434,7 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
166313
166434
  }
166314
166435
  const proactiveTriggerPercentage = getProactiveCompartmentTriggerPercentage(executeThresholdPercentage);
166315
166436
  if (usage.percentage < proactiveTriggerPercentage) {
166437
+ sessionLog(sessionId, `compartment trigger: not firing at ${usage.percentage.toFixed(1)}% — below proactive floor (${proactiveTriggerPercentage}%)`);
166316
166438
  return { shouldFire: false };
166317
166439
  }
166318
166440
  if (projectedPostDropPercentage !== null && projectedPostDropPercentage <= relativePostDropTarget) {
@@ -167714,11 +167836,12 @@ async function runAutoSearchHintForPi(args) {
167714
167836
  }) : args.entryIds;
167715
167837
  if (!options.enabled)
167716
167838
  return messages;
167717
- if (entryIds === null) {
167718
- sessionLog(sessionId, "Pi auto-search: strict entry-id resolution failed; skipping fresh decision");
167719
- return messages;
167720
- }
167721
- const found = findLatestMeaningfulUserMessage(messages, entryIds);
167839
+ const strictResolutionFailed = entryIds === null;
167840
+ const effectiveEntryIds = strictResolutionFailed ? messages.map((message) => {
167841
+ const id = message.id;
167842
+ return typeof id === "string" ? id : undefined;
167843
+ }) : entryIds;
167844
+ const found = findLatestMeaningfulUserMessage(messages, effectiveEntryIds);
167722
167845
  if (found === null)
167723
167846
  return messages;
167724
167847
  const { message: userMsg, messageId: userMsgId } = found;
@@ -167730,6 +167853,10 @@ async function runAutoSearchHintForPi(args) {
167730
167853
  }
167731
167854
  return messages;
167732
167855
  }
167856
+ if (strictResolutionFailed) {
167857
+ sessionLog(sessionId, "Pi auto-search: strict entry-id resolution failed; replayed persisted decisions only");
167858
+ return messages;
167859
+ }
167733
167860
  const writeNoHintAndReconcile = (reason) => {
167734
167861
  const outcome2 = appendAutoSearchHintDecision(db, sessionId, {
167735
167862
  messageId: userMsgId,
@@ -167816,6 +167943,59 @@ ${hintText}`;
167816
167943
  }
167817
167944
  function clearAutoSearchForPiSession(_sessionId) {}
167818
167945
 
167946
+ // src/compaction-marker-manager-pi.ts
167947
+ init_logger();
167948
+ function applyDeferredPiCompactionMarker(deps, sessionId, pending) {
167949
+ try {
167950
+ const matches = getCompartmentsByEndMessageId(deps.db, sessionId, pending.endMessageId);
167951
+ if (matches.length === 0 || matches.length > 1) {
167952
+ if (matches.length > 1) {
167953
+ sessionLog(sessionId, `Pi compaction-marker drain: ${matches.length} compartments share endMessageId=${pending.endMessageId}; treating as stale`);
167954
+ }
167955
+ return { kind: "stale-skip", reason: "compartment-removed" };
167956
+ }
167957
+ if (matches[0]?.endMessage !== pending.ordinal) {
167958
+ return { kind: "stale-skip", reason: "target-superseded" };
167959
+ }
167960
+ const branchEntries = deps.readBranchEntries();
167961
+ if (!branchEntries.some((entry) => getEntryId(entry) === pending.firstKeptEntryId)) {
167962
+ return { kind: "stale-skip", reason: "entry-removed" };
167963
+ }
167964
+ const latestFirstKept = findLatestCompactionFirstKept(branchEntries);
167965
+ if (latestFirstKept === pending.firstKeptEntryId) {
167966
+ return { kind: "already-current" };
167967
+ }
167968
+ deps.appendCompaction(pending.summary, pending.firstKeptEntryId, pending.tokensBefore, {
167969
+ source: "magic-context",
167970
+ lastCompactedOrdinal: pending.ordinal
167971
+ }, true);
167972
+ sessionLog(sessionId, `Pi compaction-marker drain: applied firstKept=${pending.firstKeptEntryId} tokensBefore=${pending.tokensBefore}`);
167973
+ return { kind: "applied", firstKeptEntryId: pending.firstKeptEntryId };
167974
+ } catch (err) {
167975
+ const error51 = err instanceof Error ? err : new Error(String(err));
167976
+ sessionLog(sessionId, `Pi compaction-marker drain: retryable failure for ordinal ${pending.ordinal}:`, error51);
167977
+ return { kind: "retryable-failure", error: error51 };
167978
+ }
167979
+ }
167980
+ function findLatestCompactionFirstKept(branchEntries) {
167981
+ for (let i = branchEntries.length - 1;i >= 0; i--) {
167982
+ const entry = branchEntries[i];
167983
+ if (entry === null || typeof entry !== "object")
167984
+ continue;
167985
+ const record2 = entry;
167986
+ if (record2.type === "compaction" && typeof record2.firstKeptEntryId === "string") {
167987
+ return record2.firstKeptEntryId;
167988
+ }
167989
+ }
167990
+ return null;
167991
+ }
167992
+ function getEntryId(entry) {
167993
+ if (entry === null || typeof entry !== "object")
167994
+ return null;
167995
+ const id = entry.id;
167996
+ return typeof id === "string" ? id : null;
167997
+ }
167998
+
167819
167999
  // src/detect-recent-commit.ts
167820
168000
  var COMMIT_LOOKBACK = 5;
167821
168001
  var COMMIT_HASH_PATTERN2 = /\b[0-9a-f]{7,12}\b/;
@@ -169596,7 +169776,6 @@ async function runPiHistorian(deps) {
169596
169776
  memoryEnabled,
169597
169777
  autoPromote,
169598
169778
  onPublished,
169599
- appendCompaction,
169600
169779
  readBranchEntries,
169601
169780
  notifyIssue
169602
169781
  } = deps;
@@ -169769,20 +169948,34 @@ ${chunk.text}`, { stateFilePath });
169769
169948
  await notify(`Historian failed: ${errorMsg}`);
169770
169949
  return;
169771
169950
  }
169951
+ const markerSummary = buildPiCompactionSummary(newCompartments);
169952
+ const lastNewEndMessageId = newCompartments[newCompartments.length - 1]?.endMessageId;
169953
+ let firstKeptEntryId = null;
169954
+ if (readBranchEntries) {
169955
+ try {
169956
+ firstKeptEntryId = findFirstKeptEntryId(readBranchEntries(), lastNewEnd);
169957
+ if (!firstKeptEntryId) {
169958
+ sessionLog(sessionId, `historian: native compaction queue skipped; no firstKeptEntryId after ordinal ${lastNewEnd}`);
169959
+ }
169960
+ } catch (error51) {
169961
+ sessionLog(sessionId, `historian: native compaction queue lookup failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
169962
+ }
169963
+ }
169772
169964
  db.transaction(() => {
169773
169965
  appendCompartments(db, sessionId, newCompartments);
169774
169966
  replaceSessionFacts(db, sessionId, validatedPass.facts ?? []);
169775
169967
  clearHistorianFailureState(db, sessionId);
169968
+ if (firstKeptEntryId && lastNewEndMessageId) {
169969
+ setPendingPiCompactionMarkerState(db, sessionId, {
169970
+ firstKeptEntryId,
169971
+ endMessageId: lastNewEndMessageId,
169972
+ ordinal: lastNewEnd,
169973
+ tokensBefore: chunk.tokenEstimate,
169974
+ summary: markerSummary,
169975
+ publishedAt: Date.now()
169976
+ });
169977
+ }
169776
169978
  })();
169777
- appendPiNativeCompaction({
169778
- sessionId,
169779
- appendCompaction,
169780
- readBranchEntries,
169781
- lastCompactedOrdinal: lastNewEnd,
169782
- tokensBefore: chunk.tokenEstimate,
169783
- summary: buildPiCompactionSummary(newCompartments)
169784
- });
169785
- clearInjectionCache(sessionId);
169786
169979
  onNoteTrigger(db, sessionId, "historian_complete");
169787
169980
  onPublished?.();
169788
169981
  if (memoryEnabled !== false && autoPromote !== false) {
@@ -169843,25 +170036,6 @@ function buildPiCompactionSummary(compartments) {
169843
170036
  }
169844
170037
  return `Magic Context compacted: ${titles.join("; ")}`;
169845
170038
  }
169846
- function appendPiNativeCompaction(args) {
169847
- const { appendCompaction, readBranchEntries } = args;
169848
- if (!appendCompaction || !readBranchEntries)
169849
- return;
169850
- try {
169851
- const firstKeptEntryId = findFirstKeptEntryId(readBranchEntries(), args.lastCompactedOrdinal);
169852
- if (!firstKeptEntryId) {
169853
- sessionLog(args.sessionId, `historian: native compaction skipped; no firstKeptEntryId after ordinal ${args.lastCompactedOrdinal}`);
169854
- return;
169855
- }
169856
- appendCompaction(args.summary, firstKeptEntryId, args.tokensBefore, {
169857
- source: "magic-context",
169858
- lastCompactedOrdinal: args.lastCompactedOrdinal
169859
- }, true);
169860
- sessionLog(args.sessionId, `historian: appended native compaction firstKept=${firstKeptEntryId} tokensBefore=${args.tokensBefore}`);
169861
- } catch (error51) {
169862
- sessionLog(args.sessionId, `historian: native compaction append failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
169863
- }
169864
- }
169865
170039
  function findFirstKeptEntryId(entries, lastCompactedOrdinal) {
169866
170040
  const rawMessages = convertEntriesToRawMessages(entries);
169867
170041
  const target = lastCompactedOrdinal + 1;
@@ -170742,6 +170916,19 @@ function stripTagPrefix2(text) {
170742
170916
  return text.slice(match[0].length);
170743
170917
  }
170744
170918
 
170919
+ // src/timeout.ts
170920
+ function withTimeout(p, ms) {
170921
+ let handle;
170922
+ const timeout = new Promise((resolve4) => {
170923
+ handle = setTimeout(() => resolve4(undefined), ms);
170924
+ handle.unref?.();
170925
+ });
170926
+ return Promise.race([p, timeout]).finally(() => {
170927
+ if (handle)
170928
+ clearTimeout(handle);
170929
+ });
170930
+ }
170931
+
170745
170932
  // src/tokenize-pi-messages.ts
170746
170933
  function tokenizePiMessages(messages) {
170747
170934
  let conversation = 0;
@@ -171244,6 +171431,7 @@ var deferredMaterializationSessions = new Set;
171244
171431
  var sessionsByProject = new Map;
171245
171432
  var lastSeenProjectIdentityBySession = new Map;
171246
171433
  var rawMessageProviderUnregistersBySession = new Map;
171434
+ var activeContextHandlerSessions = new Set;
171247
171435
  var lastHeuristicsTurnIdBySession = new Map;
171248
171436
  var firstContextPassSeenBySession = new Set;
171249
171437
  var recentReduceBySession = new Map;
@@ -171295,6 +171483,7 @@ function consumeDeferredMaterialization(sessionId) {
171295
171483
  return wasSet;
171296
171484
  }
171297
171485
  function trackSessionForProject(projectIdentity, sessionId) {
171486
+ activeContextHandlerSessions.add(sessionId);
171298
171487
  let sessions = sessionsByProject.get(projectIdentity);
171299
171488
  if (!sessions) {
171300
171489
  sessions = new Set;
@@ -171302,6 +171491,9 @@ function trackSessionForProject(projectIdentity, sessionId) {
171302
171491
  }
171303
171492
  sessions.add(sessionId);
171304
171493
  }
171494
+ function isContextHandlerSessionActive(sessionId) {
171495
+ return activeContextHandlerSessions.has(sessionId);
171496
+ }
171305
171497
  function updateSessionProjectTracking(sessionId, projectIdentity) {
171306
171498
  const prev = lastSeenProjectIdentityBySession.get(sessionId);
171307
171499
  if (prev && prev !== projectIdentity) {
@@ -171398,20 +171590,26 @@ function resolveSessionId2(ctx) {
171398
171590
  return;
171399
171591
  }
171400
171592
  }
171401
- function collectMessageEntryIdsByRef(ctx, messages, sessionId) {
171402
- const sm = ctx.sessionManager;
171403
- if (typeof sm?.getBranch !== "function")
171404
- return null;
171593
+ function collectMessageEntryIdsByRef(ctx, messages, sessionId, preloadedBranchEntries) {
171405
171594
  let entries;
171406
- try {
171407
- entries = sm.getBranch.call(sm);
171408
- } catch (error51) {
171409
- sessionLog(sessionId ?? "pi", `collectMessageEntryIdsByRef getBranch failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
171410
- return null;
171595
+ if (preloadedBranchEntries !== undefined) {
171596
+ entries = preloadedBranchEntries;
171597
+ } else {
171598
+ const sm = ctx.sessionManager;
171599
+ if (typeof sm?.getBranch !== "function")
171600
+ return null;
171601
+ try {
171602
+ const branch = sm.getBranch.call(sm);
171603
+ if (!Array.isArray(branch))
171604
+ return null;
171605
+ entries = branch;
171606
+ } catch (error51) {
171607
+ sessionLog(sessionId ?? "pi", `collectMessageEntryIdsByRef getBranch failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
171608
+ return null;
171609
+ }
171411
171610
  }
171412
- if (!Array.isArray(entries))
171413
- return null;
171414
171611
  const entryIdByMsgRef = new Map;
171612
+ const entryIdsByFingerprint = new Map;
171415
171613
  for (const entry of entries) {
171416
171614
  if (!entry || typeof entry !== "object")
171417
171615
  continue;
@@ -171422,10 +171620,21 @@ function collectMessageEntryIdsByRef(ctx, messages, sessionId) {
171422
171620
  continue;
171423
171621
  if (!e.message || typeof e.message !== "object")
171424
171622
  continue;
171425
- entryIdByMsgRef.set(e.message, e.id);
171623
+ const message = e.message;
171624
+ entryIdByMsgRef.set(message, e.id);
171625
+ const fingerprint = piMessageEntryFingerprint(e.message);
171626
+ if (fingerprint) {
171627
+ const bucket = entryIdsByFingerprint.get(fingerprint);
171628
+ if (bucket)
171629
+ bucket.push(e.id);
171630
+ else
171631
+ entryIdsByFingerprint.set(fingerprint, [e.id]);
171632
+ }
171426
171633
  }
171427
171634
  const result = new Array(messages.length);
171428
171635
  let resolved = 0;
171636
+ let fingerprintResolved = 0;
171637
+ const consumedFingerprintIds = new Set;
171429
171638
  for (let i = 0;i < messages.length; i++) {
171430
171639
  const msg = messages[i];
171431
171640
  if (!msg || typeof msg !== "object") {
@@ -171436,15 +171645,67 @@ function collectMessageEntryIdsByRef(ctx, messages, sessionId) {
171436
171645
  if (typeof id === "string") {
171437
171646
  result[i] = id;
171438
171647
  resolved += 1;
171648
+ continue;
171649
+ }
171650
+ const fingerprint = piMessageEntryFingerprint(msg);
171651
+ const fingerprintId = fingerprint ? entryIdsByFingerprint.get(fingerprint)?.find((candidate) => !consumedFingerprintIds.has(candidate)) : undefined;
171652
+ if (typeof fingerprintId === "string") {
171653
+ consumedFingerprintIds.add(fingerprintId);
171654
+ result[i] = fingerprintId;
171655
+ resolved += 1;
171656
+ fingerprintResolved += 1;
171439
171657
  } else {
171440
171658
  result[i] = undefined;
171441
171659
  }
171442
171660
  }
171443
171661
  if (resolved < messages.length) {
171444
- log(`[magic-context][pi]${sessionId ? `[${sessionId}]` : ""} ` + `collectMessageEntryIdsByRef: resolved=${resolved}/${messages.length} ` + `(branchEntries=${entries.length}, messageEntries=${entryIdByMsgRef.size}) — ` + `unmapped slots fall through to synthesized ids; boundary lookup still works ` + `for any compartment whose start/end message is among the resolved set`);
171662
+ log(`[magic-context][pi]${sessionId ? `[${sessionId}]` : ""} ` + `collectMessageEntryIdsByRef: resolved=${resolved}/${messages.length} ` + `(fingerprint=${fingerprintResolved}, branchEntries=${entries.length}, messageEntries=${entryIdByMsgRef.size}) — ` + `unmapped slots fall through to synthesized ids; boundary lookup still works ` + `for any compartment whose start/end message is among the resolved set`);
171445
171663
  }
171446
171664
  return result;
171447
171665
  }
171666
+ function readPiBranchEntriesForContext(ctx, sessionId) {
171667
+ const sm = ctx.sessionManager;
171668
+ if (typeof sm?.getBranch !== "function")
171669
+ return null;
171670
+ try {
171671
+ const entries = sm.getBranch.call(sm);
171672
+ return Array.isArray(entries) ? entries : null;
171673
+ } catch (error51) {
171674
+ sessionLog(sessionId, `Pi branch pre-read failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
171675
+ return null;
171676
+ }
171677
+ }
171678
+ function piMessageEntryFingerprint(message) {
171679
+ if (!message || typeof message !== "object")
171680
+ return null;
171681
+ const record2 = message;
171682
+ if (typeof record2.role !== "string")
171683
+ return null;
171684
+ const firstText = firstPiTextContent(record2.content);
171685
+ const firstTextHash = crypto2.createHash("sha256").update(firstText ?? "").digest("hex").slice(0, 16);
171686
+ return JSON.stringify([
171687
+ typeof record2.responseId === "string" ? record2.responseId : null,
171688
+ typeof record2.timestamp === "number" || typeof record2.timestamp === "string" ? record2.timestamp : null,
171689
+ record2.role,
171690
+ typeof record2.toolCallId === "string" ? record2.toolCallId : null,
171691
+ firstTextHash
171692
+ ]);
171693
+ }
171694
+ function firstPiTextContent(content) {
171695
+ if (typeof content === "string")
171696
+ return content;
171697
+ if (!Array.isArray(content))
171698
+ return null;
171699
+ for (const part of content) {
171700
+ if (!part || typeof part !== "object")
171701
+ continue;
171702
+ const record2 = part;
171703
+ if (record2.type === "text" && typeof record2.text === "string") {
171704
+ return record2.text;
171705
+ }
171706
+ }
171707
+ return null;
171708
+ }
171448
171709
  function registerPiContextHandler(pi, options) {
171449
171710
  const tagger = createTagger();
171450
171711
  const schedulerConfig = options.scheduler ?? {
@@ -171476,15 +171737,16 @@ function registerPiContextHandler(pi, options) {
171476
171737
  const projectIdentity = resolveProjectIdentity(projectDirectory);
171477
171738
  updateSessionProjectTracking(sessionId, projectIdentity);
171478
171739
  logTransformTiming(sessionId, "findSessionId", tFindSession, `messages=${event.messages.length}`);
171740
+ const branchEntries = readPiBranchEntriesForContext(ctx, sessionId);
171479
171741
  const rawMessageProvider = {
171480
- readMessages: () => readPiSessionMessages(ctx),
171742
+ readMessages: () => branchEntries !== null ? convertEntriesToRawMessages([...branchEntries]) : readPiSessionMessages(ctx),
171481
171743
  readMessageById: (messageId) => readPiSessionMessageById(ctx, messageId)
171482
171744
  };
171483
171745
  rawMessageProviderUnregistersBySession.get(sessionId)?.();
171484
171746
  const unregisterRaw = setRawMessageProvider(sessionId, rawMessageProvider);
171485
171747
  rawMessageProviderUnregistersBySession.set(sessionId, unregisterRaw);
171486
171748
  scheduleReconciliation(options.db, sessionId, readRawSessionMessages);
171487
- const strictEntryIds = collectMessageEntryIdsByRef(ctx, event.messages, sessionId);
171749
+ const strictEntryIds = branchEntries === null ? null : collectMessageEntryIdsByRef(ctx, event.messages, sessionId, branchEntries);
171488
171750
  const tLastUser = performance.now();
171489
171751
  const latestUser = findLatestUserMessageIdPi(event.messages, buildPiMessageIdByIndex(event.messages, strictEntryIds));
171490
171752
  logTransformTiming(sessionId, "findLastUserMessageId", tLastUser);
@@ -171606,10 +171868,7 @@ function registerPiContextHandler(pi, options) {
171606
171868
  const histPromise = inFlightHistorian.get(sessionId);
171607
171869
  if (histPromise) {
171608
171870
  try {
171609
- await Promise.race([
171610
- histPromise,
171611
- new Promise((resolve4) => setTimeout(resolve4, 30000))
171612
- ]);
171871
+ await withTimeout(histPromise, 30000);
171613
171872
  sessionLog(sessionId, "EMERGENCY: historian wait completed (or timed out)");
171614
171873
  } catch {}
171615
171874
  }
@@ -171617,7 +171876,7 @@ function registerPiContextHandler(pi, options) {
171617
171876
  const isCacheBusting = historyRefreshSessions.has(sessionId);
171618
171877
  sessionLog(sessionId, `transform: usage=${usagePercentage.toFixed(1)}% (${usageInputTokens} tokens, limit=${usageContextLimit ?? "?"}) decision=${schedulerDecision}${forceMaterialization ? " force=true" : ""}${isEmergency ? " EMERGENCY=true" : ""}${isCacheBusting ? " busting=true" : ""}`);
171619
171878
  logTransformTiming(sessionId, "emergencyRecoveryBlock", tEmergencyRecovery);
171620
- const entryIds = collectMessageEntryIdsByRef(ctx, event.messages, sessionId) ?? undefined;
171879
+ const entryIds = strictEntryIds ?? undefined;
171621
171880
  const result = await runPipeline({
171622
171881
  db: options.db,
171623
171882
  tagger,
@@ -171639,7 +171898,9 @@ function registerPiContextHandler(pi, options) {
171639
171898
  reasoningClearing: {
171640
171899
  clearReasoningAge: options.heuristics?.clearReasoningAge ?? DEFAULT_CLEAR_REASONING_AGE
171641
171900
  },
171642
- temporalAwareness: options.injection?.temporalAwareness === true
171901
+ temporalAwareness: options.injection?.temporalAwareness === true,
171902
+ appendCompaction: resolvePiAppendCompaction(ctx),
171903
+ readBranchEntries: resolvePiReadBranchEntries(ctx)
171643
171904
  });
171644
171905
  if (options.historian) {
171645
171906
  maybeFireHistorian({
@@ -171647,7 +171908,9 @@ function registerPiContextHandler(pi, options) {
171647
171908
  sessionId,
171648
171909
  db: options.db,
171649
171910
  historian: options.historian,
171650
- isFirstContextPassForSession
171911
+ isFirstContextPassForSession,
171912
+ activeTags: result.activeTags,
171913
+ rawMessageProvider
171651
171914
  });
171652
171915
  maybeFireCompressor({
171653
171916
  ctx,
@@ -171766,6 +172029,43 @@ async function awaitInFlightHistorians() {
171766
172029
  ];
171767
172030
  await Promise.allSettled(promises);
171768
172031
  }
172032
+ function splitModelKeyForPi(modelKey) {
172033
+ if (!modelKey)
172034
+ return { providerID: undefined, modelID: undefined };
172035
+ const slash = modelKey.indexOf("/");
172036
+ if (slash <= 0 || slash === modelKey.length - 1) {
172037
+ return { providerID: undefined, modelID: undefined };
172038
+ }
172039
+ return {
172040
+ providerID: modelKey.slice(0, slash),
172041
+ modelID: modelKey.slice(slash + 1)
172042
+ };
172043
+ }
172044
+ function resolvePiHistorianTriggerInputs(args) {
172045
+ const { providerID, modelID } = splitModelKeyForPi(args.modelKey);
172046
+ let contextLimit = resolveContextLimit(providerID, modelID, {
172047
+ db: args.db,
172048
+ sessionID: args.sessionId
172049
+ });
172050
+ if ((providerID === undefined || modelID === undefined) && typeof args.usageContextLimit === "number" && Number.isFinite(args.usageContextLimit) && args.usageContextLimit > 0) {
172051
+ contextLimit = args.usageContextLimit;
172052
+ }
172053
+ const executeThresholdPercentage = resolveExecuteThreshold(args.historian.executeThresholdPercentage ?? 65, args.modelKey, 65, {
172054
+ tokensConfig: args.historian.executeThresholdTokens,
172055
+ contextLimit,
172056
+ sessionId: args.sessionId
172057
+ });
172058
+ return {
172059
+ executeThresholdPercentage,
172060
+ triggerBudget: deriveTriggerBudget(contextLimit, executeThresholdPercentage),
172061
+ autoDropToolAge: args.historian.autoDropToolAge ?? 100,
172062
+ protectedTags: args.historian.protectedTags,
172063
+ clearReasoningAge: args.historian.clearReasoningAge ?? DEFAULT_CLEAR_REASONING_AGE,
172064
+ dropToolStructure: args.historian.dropToolStructure ?? true,
172065
+ commitClusterTrigger: args.historian.commitClusterTrigger,
172066
+ contextLimit
172067
+ };
172068
+ }
171769
172069
  function resolveHistoryBudgetTokensForPi(args) {
171770
172070
  const {
171771
172071
  historyBudgetPercentage,
@@ -171824,6 +172124,10 @@ function maybeFireCompressor(args) {
171824
172124
  maxCompartmentsPerPass: compressor.maxCompartmentsPerPass,
171825
172125
  graceCompartments: compressor.graceCompartments,
171826
172126
  onPublished: () => {
172127
+ if (!isContextHandlerSessionActive(sessionId)) {
172128
+ sessionLog(sessionId, "compressor publication ignored: session was cleared");
172129
+ return;
172130
+ }
171827
172131
  signalPiDeferredHistoryRefresh(sessionId);
171828
172132
  signalPiDeferredMaterialization(sessionId);
171829
172133
  historian.onStatusChange?.(ctx, sessionId);
@@ -171836,7 +172140,9 @@ function maybeFireCompressor(args) {
171836
172140
  sessionLog(sessionId, `compressor failed in background: ${err instanceof Error ? err.message : String(err)}`);
171837
172141
  }).finally(() => {
171838
172142
  inFlightCompressor.delete(sessionId);
171839
- historian.onStatusChange?.(ctx, sessionId);
172143
+ if (isContextHandlerSessionActive(sessionId)) {
172144
+ historian.onStatusChange?.(ctx, sessionId);
172145
+ }
171840
172146
  });
171841
172147
  inFlightCompressor.set(sessionId, runPromise);
171842
172148
  }
@@ -171881,6 +172187,10 @@ function spawnPiHistorianRun(args) {
171881
172187
  memoryEnabled: historian.memoryEnabled,
171882
172188
  autoPromote: historian.autoPromote,
171883
172189
  onPublished: () => {
172190
+ if (!isContextHandlerSessionActive(sessionId)) {
172191
+ sessionLog(sessionId, "historian publication ignored: session was cleared");
172192
+ return;
172193
+ }
171884
172194
  try {
171885
172195
  clearEmergencyRecovery(db, sessionId);
171886
172196
  } catch (err) {
@@ -171893,7 +172203,9 @@ function spawnPiHistorianRun(args) {
171893
172203
  }).finally(() => {
171894
172204
  inFlightHistorian.delete(sessionId);
171895
172205
  unregister();
171896
- historian.onStatusChange?.(ctx, sessionId);
172206
+ if (isContextHandlerSessionActive(sessionId)) {
172207
+ historian.onStatusChange?.(ctx, sessionId);
172208
+ }
171897
172209
  });
171898
172210
  inFlightHistorian.set(sessionId, runPromise);
171899
172211
  historian.onStatusChange?.(ctx, sessionId);
@@ -171924,7 +172236,10 @@ function maybeFireHistorian(args) {
171924
172236
  return;
171925
172237
  }
171926
172238
  let usage;
172239
+ let usageContextLimit;
171927
172240
  try {
172241
+ const piUsage = ctx.getContextUsage?.();
172242
+ usageContextLimit = typeof piUsage?.contextWindow === "number" && piUsage.contextWindow > 0 ? piUsage.contextWindow : undefined;
171928
172243
  const sessionMetaForUsage = getOrCreateSessionMeta(db, sessionId);
171929
172244
  if (sessionMetaForUsage.lastContextPercentage > 0 && sessionMetaForUsage.lastInputTokens > 0) {
171930
172245
  usage = {
@@ -171933,7 +172248,6 @@ function maybeFireHistorian(args) {
171933
172248
  };
171934
172249
  sessionLog(sessionId, `historian trigger eval: usage=${usage.percentage.toFixed(1)}% (${usage.inputTokens} tokens) [from session_meta], checking trigger...`);
171935
172250
  } else {
171936
- const piUsage = ctx.getContextUsage?.();
171937
172251
  if (!piUsage || piUsage.tokens === null || piUsage.percent === null || piUsage.contextWindow === 0) {
171938
172252
  sessionLog(sessionId, `historian trigger eval: no usage info yet (tokens=${piUsage?.tokens ?? "<no piUsage>"}, percent=${piUsage?.percent ?? "<no piUsage>"}, contextWindow=${piUsage?.contextWindow ?? "<no piUsage>"})`);
171939
172253
  return;
@@ -171948,7 +172262,7 @@ function maybeFireHistorian(args) {
171948
172262
  sessionLog(sessionId, `historian trigger eval: getContextUsage threw: ${err instanceof Error ? err.message : String(err)}`);
171949
172263
  return;
171950
172264
  }
171951
- const provider2 = {
172265
+ const provider2 = args.rawMessageProvider ?? {
171952
172266
  readMessages: () => readPiSessionMessages(ctx)
171953
172267
  };
171954
172268
  const unregister = setRawMessageProvider(sessionId, provider2);
@@ -171980,7 +172294,15 @@ Historian previously failed ${failureState.failureCount} time(s), so magic-conte
171980
172294
  }
171981
172295
  }
171982
172296
  const sessionMeta = getOrCreateSessionMeta(db, sessionId);
171983
- const trigger = checkCompartmentTrigger(db, sessionId, sessionMeta, usage, 0, resolveExecuteThreshold(historian.executeThresholdPercentage ?? 65, liveModelBySession.get(sessionId), 65), historian.triggerBudget ?? 8000);
172297
+ const modelKey = liveModelBySession.get(sessionId);
172298
+ const triggerInputs = resolvePiHistorianTriggerInputs({
172299
+ db,
172300
+ sessionId,
172301
+ historian,
172302
+ modelKey,
172303
+ usageContextLimit
172304
+ });
172305
+ const trigger = checkCompartmentTrigger(db, sessionId, sessionMeta, usage, 0, triggerInputs.executeThresholdPercentage, triggerInputs.triggerBudget, triggerInputs.autoDropToolAge, triggerInputs.protectedTags, triggerInputs.clearReasoningAge, triggerInputs.dropToolStructure, triggerInputs.commitClusterTrigger, args.activeTags);
171984
172306
  if (!trigger.shouldFire) {
171985
172307
  sessionLog(sessionId, `historian trigger eval: shouldFire=false (no trigger condition met)`);
171986
172308
  return;
@@ -171998,12 +172320,17 @@ Historian previously failed ${failureState.failureCount} time(s), so magic-conte
171998
172320
  } catch (err) {
171999
172321
  const message = err instanceof Error ? err.message : String(err);
172000
172322
  sessionLog(sessionId, `historian trigger eval failed: ${message}`);
172323
+ } finally {
172001
172324
  if (!triggered)
172002
172325
  unregister();
172003
172326
  }
172004
172327
  }
172005
172328
  async function runPipeline(args) {
172006
172329
  let executedWorkThisPass = false;
172330
+ let historyWasConsumedThisPass = false;
172331
+ let suppressDeferredHistoryDrain = false;
172332
+ let casLost = false;
172333
+ const deferredHistoryWasPendingAtPassStart = deferredHistoryRefreshSessions.has(args.sessionId);
172007
172334
  if (args.temporalAwareness) {
172008
172335
  const tTemporal = performance.now();
172009
172336
  try {
@@ -172044,7 +172371,7 @@ async function runPipeline(args) {
172044
172371
  }
172045
172372
  const hasPendingMaterializeSignal = hasPendingMaterialization(args.sessionId);
172046
172373
  const deferredMaterializationWasPending = deferredMaterializationSessions.has(args.sessionId);
172047
- const deferredHistoryRefreshWasPending = deferredHistoryRefreshSessions.has(args.sessionId);
172374
+ const deferredHistoryRefreshWasPending = deferredHistoryWasPendingAtPassStart;
172048
172375
  const pendingOps = getPendingOps(args.db, args.sessionId);
172049
172376
  const baseShouldApplyPendingOps = args.schedulerDecision === "execute" || args.forceMaterialization || hasPendingMaterializeSignal;
172050
172377
  const canConsumeDeferredLate = baseShouldApplyPendingOps || shouldRunHeuristics;
@@ -172187,9 +172514,10 @@ async function runPipeline(args) {
172187
172514
  injectionResult = injectSessionHistoryIntoPi(args.db, args.sessionId, args.messages, args.isCacheBusting || deferredHistoryRefresh, args.projectIdentity, args.injection.injectionBudgetTokens, args.injection.temporalAwareness, args.entryIds);
172188
172515
  if (args.isCacheBusting) {
172189
172516
  historyRefreshSessions.delete(args.sessionId);
172517
+ historyWasConsumedThisPass = true;
172190
172518
  }
172191
172519
  if (deferredHistoryRefresh) {
172192
- consumeDeferredHistoryRefresh(args.sessionId);
172520
+ historyWasConsumedThisPass = true;
172193
172521
  }
172194
172522
  logTransformTiming(args.sessionId, "prepareCompartmentInjection", tInjection);
172195
172523
  logTransformTiming(args.sessionId, "compartmentPhase", tInjection);
@@ -172203,6 +172531,34 @@ async function runPipeline(args) {
172203
172531
  messages: args.messages,
172204
172532
  isCacheBusting: args.isCacheBusting
172205
172533
  });
172534
+ const deferredHistoryDrainEligible = historyWasConsumedThisPass && deferredHistoryWasPendingAtPassStart && !suppressDeferredHistoryDrain && !casLost;
172535
+ if (deferredHistoryDrainEligible) {
172536
+ try {
172537
+ const pending = getPendingPiCompactionMarkerState(args.db, args.sessionId);
172538
+ if (!pending) {
172539
+ consumeDeferredHistoryRefresh(args.sessionId);
172540
+ } else if (!args.appendCompaction || !args.readBranchEntries) {
172541
+ suppressDeferredHistoryDrain = true;
172542
+ sessionLog(args.sessionId, "Pi compaction-marker drain skipped: sessionManager appendCompaction/getBranch unavailable; preserving deferred-history signal");
172543
+ } else {
172544
+ const outcome = applyDeferredPiCompactionMarker({
172545
+ db: args.db,
172546
+ appendCompaction: args.appendCompaction,
172547
+ readBranchEntries: args.readBranchEntries
172548
+ }, args.sessionId, pending);
172549
+ if (outcome.kind === "retryable-failure") {
172550
+ sessionLog(args.sessionId, `Pi compaction-marker drain retryable failure: ${outcome.error.message}`);
172551
+ } else if (clearPendingPiCompactionMarkerStateIf(args.db, args.sessionId, pending)) {
172552
+ consumeDeferredHistoryRefresh(args.sessionId);
172553
+ } else {
172554
+ casLost = true;
172555
+ sessionLog(args.sessionId, "CAS-clear failed (newer blob written or another actor cleared); preserving deferred-history signal");
172556
+ }
172557
+ }
172558
+ } catch (err) {
172559
+ sessionLog(args.sessionId, `Pi compaction-marker drain failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
172560
+ }
172561
+ }
172206
172562
  if (executedWorkThisPass) {
172207
172563
  try {
172208
172564
  const currentFlag = peekDeferredExecutePending(args.db, args.sessionId);
@@ -172232,7 +172588,8 @@ async function runPipeline(args) {
172232
172588
  heuristicsResult,
172233
172589
  injectionResult,
172234
172590
  targetCount: targets.size,
172235
- reasoningWatermark: getOrCreateSessionMeta(args.db, args.sessionId).clearedReasoningThroughTag ?? 0
172591
+ reasoningWatermark: getOrCreateSessionMeta(args.db, args.sessionId).clearedReasoningThroughTag ?? 0,
172592
+ activeTags
172236
172593
  };
172237
172594
  }
172238
172595
  function applyRollingNudge(args) {
@@ -172264,11 +172621,15 @@ function applyStickyTurnReminder(args) {
172264
172621
  const messageIdByIndex = buildPiMessageIdByIndex(args.messages, args.entryIds);
172265
172622
  if (reminder.messageId) {
172266
172623
  const reinjected = appendReminderToUserMessageByIdPi(args.messages, messageIdByIndex, reminder.messageId, reminder.text);
172267
- if (!reinjected && args.isCacheBustingPass) {
172624
+ if (!reinjected && args.isCacheBustingPass && args.entryIds !== null) {
172268
172625
  clearPersistedStickyTurnReminder(args.db, args.sessionId);
172269
172626
  }
172270
172627
  return args.messages;
172271
172628
  }
172629
+ if (args.entryIds === null) {
172630
+ sessionLog(args.sessionId, "Pi sticky turn reminder: strict resolution failed; replay-only until next pass");
172631
+ return args.messages;
172632
+ }
172272
172633
  const latest = findLatestUserMessageIdPi(args.messages, messageIdByIndex);
172273
172634
  if (latest) {
172274
172635
  appendReminderToPiUserMessage(args.messages[latest.index], reminder.text);
@@ -172350,8 +172711,6 @@ function buildPiMessageIdByIndex(messages, entryIds) {
172350
172711
  const messageId = messages[index]?.id;
172351
172712
  if (typeof messageId === "string") {
172352
172713
  ids.set(index, messageId);
172353
- } else if (entryIds === null) {
172354
- ids.set(index, `pi:${index}:${readTimestamp(messages[index])}`);
172355
172714
  }
172356
172715
  }
172357
172716
  return ids;
@@ -172380,10 +172739,6 @@ function appendReminderToUserMessageByIdPi(messages, messageIdByIndex, messageId
172380
172739
  }
172381
172740
  return false;
172382
172741
  }
172383
- function readTimestamp(message) {
172384
- const ts = message.timestamp;
172385
- return typeof ts === "number" && Number.isFinite(ts) ? String(ts) : "no-ts";
172386
- }
172387
172742
  function appendReminderToPiUserMessage(message, reminder) {
172388
172743
  if (message.role !== "user")
172389
172744
  return;
@@ -172410,6 +172765,7 @@ function appendReminderToPiUserMessage(message, reminder) {
172410
172765
  contentArr.push({ type: "text", text: reminder.trimStart() });
172411
172766
  }
172412
172767
  function clearContextHandlerSession(sessionId) {
172768
+ activeContextHandlerSessions.delete(sessionId);
172413
172769
  clearAutoSearchForPiSession(sessionId);
172414
172770
  lastEmergencyNotificationAtMs.delete(sessionId);
172415
172771
  historyRefreshSessions.delete(sessionId);
@@ -174231,6 +174587,7 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
174231
174587
  text: result,
174232
174588
  level: inferLevel(result)
174233
174589
  });
174590
+ queueAndApplyPiRecompMarker({ db: deps.db, sessionId, ctx });
174234
174591
  signalPiHistoryRefresh(sessionId);
174235
174592
  signalPiPendingMaterialization(sessionId);
174236
174593
  clearPiCompressorState(sessionId);
@@ -174253,6 +174610,60 @@ ${describeError(error51).brief}`,
174253
174610
  }
174254
174611
  });
174255
174612
  }
174613
+ function queueAndApplyPiRecompMarker(args) {
174614
+ const appendCompaction = resolvePiAppendCompaction2(args.ctx);
174615
+ const readBranchEntries = resolvePiReadBranchEntries2(args.ctx);
174616
+ if (!appendCompaction || !readBranchEntries)
174617
+ return;
174618
+ const compartments = getCompartments(args.db, args.sessionId);
174619
+ const last = compartments[compartments.length - 1];
174620
+ if (!last)
174621
+ return;
174622
+ let firstKeptEntryId = null;
174623
+ try {
174624
+ firstKeptEntryId = findFirstKeptEntryId(readBranchEntries(), last.endMessage);
174625
+ } catch {
174626
+ firstKeptEntryId = null;
174627
+ }
174628
+ if (!firstKeptEntryId || last.endMessageId.length === 0)
174629
+ return;
174630
+ const pending = {
174631
+ firstKeptEntryId,
174632
+ endMessageId: last.endMessageId,
174633
+ ordinal: last.endMessage,
174634
+ tokensBefore: 0,
174635
+ summary: buildPiCompactionSummary(compartments),
174636
+ publishedAt: Date.now()
174637
+ };
174638
+ setPendingPiCompactionMarkerState(args.db, args.sessionId, pending);
174639
+ const outcome = applyDeferredPiCompactionMarker({ db: args.db, appendCompaction, readBranchEntries }, args.sessionId, pending);
174640
+ if (outcome.kind === "retryable-failure") {
174641
+ signalPiDeferredHistoryRefresh(args.sessionId);
174642
+ return;
174643
+ }
174644
+ if (!clearPendingPiCompactionMarkerStateIf(args.db, args.sessionId, pending)) {
174645
+ signalPiDeferredHistoryRefresh(args.sessionId);
174646
+ }
174647
+ }
174648
+ function resolvePiAppendCompaction2(ctx) {
174649
+ const sm = ctx?.sessionManager;
174650
+ if (typeof sm?.appendCompaction !== "function")
174651
+ return;
174652
+ return sm.appendCompaction.bind(sm);
174653
+ }
174654
+ function resolvePiReadBranchEntries2(ctx) {
174655
+ const sm = ctx?.sessionManager;
174656
+ if (typeof sm?.getBranch !== "function")
174657
+ return;
174658
+ return () => {
174659
+ try {
174660
+ const entries = sm.getBranch?.call(sm);
174661
+ return Array.isArray(entries) ? entries : [];
174662
+ } catch {
174663
+ return [];
174664
+ }
174665
+ };
174666
+ }
174256
174667
  function parseRecompArgs(raw) {
174257
174668
  const trimmed = raw.trim();
174258
174669
  if (trimmed.length === 0)
@@ -174524,7 +174935,7 @@ import {
174524
174935
  // package.json
174525
174936
  var package_default = {
174526
174937
  name: "@cortexkit/pi-magic-context",
174527
- version: "0.21.4",
174938
+ version: "0.21.5",
174528
174939
  type: "module",
174529
174940
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
174530
174941
  main: "dist/index.js",
@@ -179626,8 +180037,15 @@ function createCtxNoteTool(deps) {
179626
180037
  if (typeof params.note_id !== "number") {
179627
180038
  return err3("Error: 'note_id' is required when action is 'dismiss'.");
179628
180039
  }
179629
- const dismissed = dismissNote(deps.db, params.note_id);
179630
- return ok3(dismissed ? `Note #${params.note_id} dismissed.` : `Note #${params.note_id} not found or already dismissed.`);
180040
+ const projectIdentity = resolveProjectIdentity(ctx.cwd);
180041
+ if (!projectIdentity) {
180042
+ return err3("Error: Could not resolve project identity for note dismiss.");
180043
+ }
180044
+ const dismissed = dismissNote(deps.db, params.note_id, {
180045
+ projectPath: projectIdentity,
180046
+ sessionId
180047
+ });
180048
+ return dismissed ? ok3(`Note #${params.note_id} dismissed.`) : err3(`Error: Note #${params.note_id} not found in your session/project or already dismissed.`);
179631
180049
  }
179632
180050
  if (action2 === "update") {
179633
180051
  if (typeof params.note_id !== "number") {
@@ -179641,9 +180059,16 @@ function createCtxNoteTool(deps) {
179641
180059
  if (!updates.content && !updates.surfaceCondition) {
179642
180060
  return err3("Error: Provide 'content' and/or 'surface_condition' to update.");
179643
180061
  }
179644
- const updated = updateNote(deps.db, params.note_id, updates);
180062
+ const projectIdentity = resolveProjectIdentity(ctx.cwd);
180063
+ if (!projectIdentity) {
180064
+ return err3("Error: Could not resolve project identity for note update.");
180065
+ }
180066
+ const updated = updateNote(deps.db, params.note_id, updates, {
180067
+ projectPath: projectIdentity,
180068
+ sessionId
180069
+ });
179645
180070
  if (!updated) {
179646
- return err3(`Note #${params.note_id} not found.`);
180071
+ return err3(`Error: Note #${params.note_id} not found in your session/project.`);
179647
180072
  }
179648
180073
  const parts = [];
179649
180074
  if (updates.content)
@@ -180254,7 +180679,6 @@ function resolveHistorianFromConfig(config2) {
180254
180679
  if (!model || model.length === 0)
180255
180680
  return;
180256
180681
  const historianContextLimit = resolveHistorianContextLimit(model);
180257
- const triggerBudget = deriveTriggerBudget(historianContextLimit, 65);
180258
180682
  const historianChunkTokens = deriveHistorianChunkTokens(historianContextLimit);
180259
180683
  const fbRaw = historian?.fallback_models;
180260
180684
  const fallbackModels = typeof fbRaw === "string" ? [fbRaw] : fbRaw;
@@ -180267,7 +180691,12 @@ function resolveHistorianFromConfig(config2) {
180267
180691
  twoPass: historian?.two_pass === true,
180268
180692
  thinkingLevel: historian?.thinking_level,
180269
180693
  executeThresholdPercentage: config2.execute_threshold_percentage,
180270
- triggerBudget,
180694
+ executeThresholdTokens: config2.execute_threshold_tokens,
180695
+ commitClusterTrigger: config2.commit_cluster_trigger,
180696
+ autoDropToolAge: config2.auto_drop_tool_age,
180697
+ protectedTags: config2.protected_tags,
180698
+ clearReasoningAge: config2.clear_reasoning_age,
180699
+ dropToolStructure: config2.drop_tool_structure,
180271
180700
  historyBudgetPercentage: config2.history_budget_percentage,
180272
180701
  compressor: {
180273
180702
  enabled: config2.compressor.enabled,
@@ -180317,6 +180746,17 @@ async function src_default2(pi) {
180317
180746
  const projectDir = process.cwd();
180318
180747
  const projectIdentity = resolveProjectIdentity(projectDir);
180319
180748
  const seenDreamerProjectIdentities = new Set([projectIdentity]);
180749
+ try {
180750
+ const pendingPiMarkerSessions = getSessionsWithPendingPiMarker(db);
180751
+ for (const sid of pendingPiMarkerSessions) {
180752
+ signalPiDeferredHistoryRefresh(sid);
180753
+ }
180754
+ if (pendingPiMarkerSessions.length > 0) {
180755
+ log(`${PREFIX2} rehydrated ${pendingPiMarkerSessions.length} Pi deferred compaction marker session(s)`);
180756
+ }
180757
+ } catch (err5) {
180758
+ warn(`Magic Context (pi) failed to rehydrate deferred Pi compaction markers: ${err5 instanceof Error ? err5.message : String(err5)}`);
180759
+ }
180320
180760
  info(`loaded v${PLUGIN_VERSION} | harness=pi | db=${dbPath} | ` + `project=${projectIdentity} | dir=${projectDir}`);
180321
180761
  const { config: config2, warnings, loadedFromPaths } = loadPiConfig({
180322
180762
  cwd: projectDir
@@ -180672,18 +181112,12 @@ ${block}` : event.systemPrompt;
180672
181112
  pi.on("session_shutdown", async (_event, ctx) => {
180673
181113
  const SHUTDOWN_DRAIN_MS = 5000;
180674
181114
  try {
180675
- await Promise.race([
180676
- awaitInFlightHistorians(),
180677
- new Promise((resolve4) => setTimeout(resolve4, SHUTDOWN_DRAIN_MS))
180678
- ]);
181115
+ await withTimeout(awaitInFlightHistorians(), SHUTDOWN_DRAIN_MS);
180679
181116
  } catch (err5) {
180680
181117
  warn("shutdown: historian drain threw:", err5);
180681
181118
  }
180682
181119
  try {
180683
- await Promise.race([
180684
- awaitInFlightDreamers(),
180685
- new Promise((resolve4) => setTimeout(resolve4, SHUTDOWN_DRAIN_MS))
180686
- ]);
181120
+ await withTimeout(awaitInFlightDreamers(), SHUTDOWN_DRAIN_MS);
180687
181121
  } catch (err5) {
180688
181122
  warn("shutdown: dreamer drain threw:", err5);
180689
181123
  }