@cortexkit/pi-magic-context 0.31.5 → 0.32.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8128,11 +8128,11 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
8128
8128
  }
8129
8129
  transaction(fn) {
8130
8130
  const self = this;
8131
- const wrapped = function(...args) {
8131
+ const execute = (mode, receiver, args) => {
8132
8132
  const nested = self.isTransaction === true;
8133
- self.exec(nested ? `SAVEPOINT ${SAVEPOINT}` : "BEGIN");
8133
+ self.exec(nested ? `SAVEPOINT ${SAVEPOINT}` : `BEGIN${mode ? ` ${mode}` : ""}`);
8134
8134
  try {
8135
- const result = fn.apply(this, args);
8135
+ const result = fn.apply(receiver, args);
8136
8136
  self.exec(nested ? `RELEASE ${SAVEPOINT}` : "COMMIT");
8137
8137
  return result;
8138
8138
  } catch (error) {
@@ -8145,6 +8145,21 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
8145
8145
  throw error;
8146
8146
  }
8147
8147
  };
8148
+ const wrapped = function(...args) {
8149
+ return execute("", this, args);
8150
+ };
8151
+ wrapped.default = function(...args) {
8152
+ return execute("", this, args);
8153
+ };
8154
+ wrapped.deferred = function(...args) {
8155
+ return execute("DEFERRED", this, args);
8156
+ };
8157
+ wrapped.immediate = function(...args) {
8158
+ return execute("IMMEDIATE", this, args);
8159
+ };
8160
+ wrapped.exclusive = function(...args) {
8161
+ return execute("EXCLUSIVE", this, args);
8162
+ };
8148
8163
  return wrapped;
8149
8164
  }
8150
8165
  }
@@ -8188,10 +8203,16 @@ function healAllNullColumns(db) {
8188
8203
  healNullIntegerColumns(db);
8189
8204
  healMissingMemoryBlockIds(db);
8190
8205
  }
8206
+ function getSessionMetaColumns(db) {
8207
+ const rows = db.prepare("PRAGMA table_info(session_meta)").all();
8208
+ return new Set(rows.flatMap((row) => typeof row.name === "string" ? [row.name] : []));
8209
+ }
8191
8210
  function healMissingMemoryBlockIds(db) {
8192
- try {
8193
- db.prepare("UPDATE session_meta SET memory_block_cache = '' WHERE memory_block_cache != '' AND (memory_block_ids IS NULL OR memory_block_ids = '') AND memory_block_count > 0").run();
8194
- } catch {}
8211
+ const columns = getSessionMetaColumns(db);
8212
+ if (!columns.has("memory_block_cache") || !columns.has("memory_block_ids") || !columns.has("memory_block_count")) {
8213
+ return;
8214
+ }
8215
+ db.prepare("UPDATE session_meta SET memory_block_cache = '' WHERE memory_block_cache != '' AND (memory_block_ids IS NULL OR memory_block_ids = '') AND memory_block_count > 0").run();
8195
8216
  }
8196
8217
  function healNullTextColumns(db) {
8197
8218
  const columns = [
@@ -8220,10 +8241,11 @@ function healNullTextColumns(db) {
8220
8241
  ["compaction_marker_state", ""],
8221
8242
  ["key_files", ""]
8222
8243
  ];
8244
+ const existingColumns = getSessionMetaColumns(db);
8223
8245
  for (const [column, fallback] of columns) {
8224
- try {
8225
- db.prepare(`UPDATE session_meta SET ${column} = ? WHERE ${column} IS NULL`).run(fallback);
8226
- } catch (_error) {}
8246
+ if (!existingColumns.has(column))
8247
+ continue;
8248
+ db.prepare(`UPDATE session_meta SET ${column} = ? WHERE ${column} IS NULL`).run(fallback);
8227
8249
  }
8228
8250
  }
8229
8251
  function healNullIntegerColumns(db) {
@@ -8254,10 +8276,11 @@ function healNullIntegerColumns(db) {
8254
8276
  ["emergency_drain_active", 0],
8255
8277
  ["historian_drain_failure_at", 0]
8256
8278
  ];
8279
+ const existingColumns = getSessionMetaColumns(db);
8257
8280
  for (const [column, fallback] of columns) {
8258
- try {
8259
- db.prepare(`UPDATE session_meta SET ${column} = ? WHERE ${column} IS NULL`).run(fallback);
8260
- } catch (_error) {}
8281
+ if (!existingColumns.has(column))
8282
+ continue;
8283
+ db.prepare(`UPDATE session_meta SET ${column} = ? WHERE ${column} IS NULL`).run(fallback);
8261
8284
  }
8262
8285
  }
8263
8286
 
@@ -10209,6 +10232,34 @@ var MIGRATIONS = [
10209
10232
  ensureColumn(db, "session_meta", "wrapup_in_progress_state", "TEXT");
10210
10233
  }
10211
10234
  }
10235
+ },
10236
+ {
10237
+ version: 51,
10238
+ description: "version tool-owner backfill state and repair legacy NULL session metadata",
10239
+ up(db) {
10240
+ db.exec(`
10241
+ CREATE TABLE IF NOT EXISTS tool_owner_backfill_state (
10242
+ session_id TEXT PRIMARY KEY,
10243
+ status TEXT NOT NULL CHECK (status IN ('pending', 'running', 'completed', 'skipped')),
10244
+ started_at INTEGER,
10245
+ lease_expires_at INTEGER,
10246
+ completed_at INTEGER,
10247
+ last_error TEXT
10248
+ );
10249
+ CREATE INDEX IF NOT EXISTS idx_tool_owner_backfill_state_status
10250
+ ON tool_owner_backfill_state(status);
10251
+ `);
10252
+ healAllNullColumns(db);
10253
+ }
10254
+ },
10255
+ {
10256
+ version: 52,
10257
+ description: "persist emergency recovery origin",
10258
+ up(db) {
10259
+ if (tableExists2(db, "session_meta")) {
10260
+ ensureColumn(db, "session_meta", "emergency_recovery_origin", "TEXT DEFAULT ''");
10261
+ }
10262
+ }
10212
10263
  }
10213
10264
  ];
10214
10265
  var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -10238,40 +10289,45 @@ function isSiblingMigrationConflict(db, error, version) {
10238
10289
  }
10239
10290
  function runMigrations(db) {
10240
10291
  ensureMigrationsTable(db);
10241
- let currentVersion = getCurrentVersion(db);
10242
- let pendingMigrations = MIGRATIONS.filter((m) => m.version > currentVersion);
10243
- if (pendingMigrations.length === 0) {
10244
- return;
10245
- }
10246
- log(`[migrations] current schema version: ${currentVersion}, applying ${pendingMigrations.length} migration(s)`);
10247
- let migrationIndex = 0;
10248
- while (migrationIndex < pendingMigrations.length) {
10249
- const migration = pendingMigrations[migrationIndex];
10292
+ let loggedPlan = false;
10293
+ while (true) {
10294
+ let migration;
10295
+ let currentVersion = 0;
10250
10296
  try {
10251
- db.transaction(() => {
10297
+ const applied = db.transaction(() => {
10298
+ currentVersion = getCurrentVersion(db);
10299
+ migration = MIGRATIONS.find((candidate) => candidate.version > currentVersion);
10300
+ if (!migration)
10301
+ return false;
10302
+ if (!loggedPlan) {
10303
+ const pendingCount = MIGRATIONS.filter((candidate) => candidate.version > currentVersion).length;
10304
+ log(`[migrations] current schema version: ${currentVersion}, applying ${pendingCount} migration(s)`);
10305
+ loggedPlan = true;
10306
+ }
10252
10307
  migration.up(db);
10253
10308
  db.prepare("INSERT INTO schema_migrations (version, description, applied_at) VALUES (?, ?, ?)").run(migration.version, migration.description, Date.now());
10254
- })();
10309
+ return true;
10310
+ }).immediate();
10311
+ if (!applied || !migration)
10312
+ break;
10255
10313
  log(`[migrations] applied v${migration.version}: ${migration.description}`);
10256
- migrationIndex += 1;
10257
10314
  } catch (error) {
10258
- if (isSiblingMigrationConflict(db, error, migration.version)) {
10315
+ if (migration && isSiblingMigrationConflict(db, error, migration.version)) {
10259
10316
  log(`[migrations] v${migration.version} already applied by sibling instance — resuming with re-read version`);
10260
10317
  const reReadVersion = getCurrentVersion(db);
10261
- if (reReadVersion <= currentVersion) {
10262
- log(`[migrations] FAILED v${migration.version}: sibling-conflict shape but version not advanced (${reReadVersion} <= ${currentVersion}) — failing closed`);
10263
- throw new Error(`Migration v${migration.version} failed: sibling conflict reported but version did not advance. Database may need manual repair.`);
10264
- }
10265
- currentVersion = reReadVersion;
10266
- pendingMigrations = MIGRATIONS.filter((m) => m.version > currentVersion);
10267
- migrationIndex = 0;
10268
- continue;
10318
+ if (reReadVersion > currentVersion)
10319
+ continue;
10320
+ throw new Error(`Migration v${migration.version} failed: sibling conflict reported but version did not advance. Database may need manual repair.`);
10269
10321
  }
10270
- log(`[migrations] FAILED v${migration.version}: ${migration.description} — ${error instanceof Error ? error.message : String(error)}`);
10271
- throw new Error(`Migration v${migration.version} failed: ${error instanceof Error ? error.message : String(error)}. Database may need manual repair.`);
10322
+ const version = migration?.version ?? currentVersion + 1;
10323
+ const description = migration?.description ?? "acquire migration write lock";
10324
+ log(`[migrations] FAILED v${version}: ${description} — ${error instanceof Error ? error.message : String(error)}`);
10325
+ throw new Error(`Migration v${version} failed: ${error instanceof Error ? error.message : String(error)}. Database may need manual repair.`);
10272
10326
  }
10273
10327
  }
10274
- log(`[migrations] schema version now: ${MIGRATIONS[MIGRATIONS.length - 1].version}`);
10328
+ if (loggedPlan) {
10329
+ log(`[migrations] schema version now: ${MIGRATIONS[MIGRATIONS.length - 1].version}`);
10330
+ }
10275
10331
  }
10276
10332
 
10277
10333
  // ../plugin/src/shared/commit-detection.ts
@@ -143025,7 +143081,8 @@ function countSessionsByStatus(db, status) {
143025
143081
  function acquireSessionLease(db, sessionId, now) {
143026
143082
  const expiresAt = now + LEASE_DURATION_MS;
143027
143083
  const result = db.prepare(`INSERT INTO tool_owner_backfill_state(session_id, status, started_at, lease_expires_at)
143028
- VALUES (?, 'running', ?, ?)
143084
+ SELECT ?, 'running', ?, ?
143085
+ WHERE EXISTS (SELECT 1 FROM tags WHERE session_id = ?)
143029
143086
  ON CONFLICT(session_id) DO UPDATE SET
143030
143087
  status = 'running',
143031
143088
  started_at = excluded.started_at,
@@ -143033,7 +143090,7 @@ function acquireSessionLease(db, sessionId, now) {
143033
143090
  last_error = NULL
143034
143091
  WHERE tool_owner_backfill_state.status IN ('pending', 'skipped')
143035
143092
  OR (tool_owner_backfill_state.status = 'running'
143036
- AND tool_owner_backfill_state.lease_expires_at < ?)`).run(sessionId, now, expiresAt, now);
143093
+ AND tool_owner_backfill_state.lease_expires_at < ?)`).run(sessionId, now, expiresAt, sessionId, now);
143037
143094
  return (result.changes ?? 0) === 1;
143038
143095
  }
143039
143096
  function renewSessionLease(db, sessionId, now) {
@@ -143053,13 +143110,9 @@ function markSessionPendingRetry(db, sessionId) {
143053
143110
  WHERE session_id = ?`).run(sessionId);
143054
143111
  }
143055
143112
  function markSessionSkipped(db, sessionId, now, reason) {
143056
- db.prepare(`INSERT INTO tool_owner_backfill_state(session_id, status, completed_at, last_error)
143057
- VALUES (?, 'skipped', ?, ?)
143058
- ON CONFLICT(session_id) DO UPDATE SET
143059
- status = 'skipped',
143060
- completed_at = excluded.completed_at,
143061
- last_error = excluded.last_error,
143062
- lease_expires_at = NULL`).run(sessionId, now, reason);
143113
+ db.prepare(`UPDATE tool_owner_backfill_state
143114
+ SET status = 'skipped', completed_at = ?, last_error = ?, lease_expires_at = NULL
143115
+ WHERE session_id = ? AND status = 'running'`).run(now, reason, sessionId);
143063
143116
  }
143064
143117
  function markSessionErrored(db, sessionId, error) {
143065
143118
  const message = error instanceof Error ? error.message : String(error);
@@ -143143,7 +143196,7 @@ function applyOwnersForSession(db, sessionId, ownersByCallId) {
143143
143196
  const result = updateRowStmt.run(ownerId, orphan.id);
143144
143197
  rowsUpdated += result.changes ?? 0;
143145
143198
  }
143146
- })();
143199
+ }).immediate();
143147
143200
  const rowsLeftNull = db.prepare(`SELECT COUNT(*) AS c FROM tags
143148
143201
  WHERE session_id = ? AND type = 'tool'
143149
143202
  AND tool_owner_message_id IS NULL`).get(sessionId).c;
@@ -143193,7 +143246,7 @@ var persistenceByDatabase = new WeakMap;
143193
143246
  var persistenceErrorByDatabase = new WeakMap;
143194
143247
  var pathByDatabase = new WeakMap;
143195
143248
  var lastSchemaFenceRejection = null;
143196
- var LATEST_SUPPORTED_VERSION = 50;
143249
+ var LATEST_SUPPORTED_VERSION = 52;
143197
143250
  var PERMISSIONS_ENFORCEABLE = process.platform !== "win32";
143198
143251
  function ensureSecureStorageDir(dir) {
143199
143252
  mkdirSync3(dir, { recursive: true, mode: 448 });
@@ -143886,6 +143939,17 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143886
143939
  pi_stable_id_scheme INTEGER
143887
143940
  );
143888
143941
 
143942
+ CREATE TABLE IF NOT EXISTS tool_owner_backfill_state (
143943
+ session_id TEXT PRIMARY KEY,
143944
+ status TEXT NOT NULL CHECK (status IN ('pending', 'running', 'completed', 'skipped')),
143945
+ started_at INTEGER,
143946
+ lease_expires_at INTEGER,
143947
+ completed_at INTEGER,
143948
+ last_error TEXT
143949
+ );
143950
+ CREATE INDEX IF NOT EXISTS idx_tool_owner_backfill_state_status
143951
+ ON tool_owner_backfill_state(status);
143952
+
143889
143953
  CREATE TABLE IF NOT EXISTS subagent_invocations (
143890
143954
  id INTEGER PRIMARY KEY AUTOINCREMENT,
143891
143955
  session_id TEXT NOT NULL,
@@ -144119,6 +144183,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
144119
144183
  ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
144120
144184
  ensureColumn(db, "session_meta", "detected_context_limit_model_key", "TEXT");
144121
144185
  ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
144186
+ ensureColumn(db, "session_meta", "emergency_recovery_origin", "TEXT DEFAULT ''");
144122
144187
  ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
144123
144188
  ensureColumn(db, "session_meta", "pending_pi_compaction_marker_state", "TEXT");
144124
144189
  ensureColumn(db, "session_meta", "new_work_tokens", "INTEGER NOT NULL DEFAULT 0");
@@ -159576,10 +159641,10 @@ function civilMinuteKey(date5) {
159576
159641
  const p = (n, w = 2) => String(n).padStart(w, "0");
159577
159642
  return `${p(date5.getFullYear(), 4)}-${p(date5.getMonth() + 1)}-${p(date5.getDate())} ${p(date5.getHours())}:${p(date5.getMinutes())}`;
159578
159643
  }
159579
- function nextOccurrence(cron, after, excludeCivilMinute) {
159644
+ function nextOccurrence(cron, after, excludeCivilMinute, maxSearchMs = MAX_SEARCH_MS) {
159580
159645
  const afterMs = after.getTime();
159581
159646
  let cursorMs = Math.floor(afterMs / MINUTE_MS) * MINUTE_MS + MINUTE_MS;
159582
- const capMs = afterMs + MAX_SEARCH_MS;
159647
+ const capMs = afterMs + Math.max(0, Math.min(MAX_SEARCH_MS, maxSearchMs));
159583
159648
  while (cursorMs <= capMs) {
159584
159649
  const candidate = new Date(cursorMs);
159585
159650
  if (matchesCron(cron, candidate)) {
@@ -159591,14 +159656,14 @@ function nextOccurrence(cron, after, excludeCivilMinute) {
159591
159656
  }
159592
159657
  return null;
159593
159658
  }
159594
- function nextDueAtMs(expression, afterMs, consumedScheduledAtMs) {
159659
+ function nextDueAtMs(expression, afterMs, consumedScheduledAtMs, maxSearchMs = MAX_SEARCH_MS) {
159595
159660
  if (expression.trim().length === 0)
159596
159661
  return null;
159597
159662
  const parsed = parseCron(expression);
159598
159663
  if (!parsed.ok)
159599
159664
  return null;
159600
159665
  const exclude = consumedScheduledAtMs !== undefined ? civilMinuteKey(new Date(consumedScheduledAtMs)) : undefined;
159601
- const next = nextOccurrence(parsed.cron, new Date(afterMs), exclude);
159666
+ const next = nextOccurrence(parsed.cron, new Date(afterMs), exclude, maxSearchMs);
159602
159667
  return next ? next.getTime() : null;
159603
159668
  }
159604
159669
 
@@ -159849,8 +159914,11 @@ var MagicContextConfigSchema = exports_external.object({
159849
159914
  provider: "local",
159850
159915
  model: DEFAULT_LOCAL_EMBEDDING_MODEL
159851
159916
  }).describe("Embedding provider configuration"),
159852
- temporal_awareness: exports_external.boolean().default(true).describe('Inject wall-clock gap markers (<!-- +Xm -->) between user messages where > 5 min elapsed since the previous message, and add start/end date attributes on compartments. Gives the agent a sense of session pacing and "how long ago" across multi-day sessions. Graduated from experimental.temporal_awareness; default: true (set false to opt out).'),
159917
+ temporal_awareness: exports_external.boolean().default(true).describe('Inject wall-clock gap markers (<!-- +Xm -->) between user messages where > 5 min elapsed since the previous message, and add compact date ranges to compartment headings. Gives the agent a sense of session pacing and "how long ago" across multi-day sessions. Graduated from experimental.temporal_awareness; default: true (set false to opt out).'),
159853
159918
  keep_subagents: exports_external.boolean().default(false).describe("Debug: keep the child sessions Magic Context spawns for its own subagents (historian, dreamer, sidekick, memory-migration) instead of deleting them on success. Useful for short-term inspection/data collection — their full transcript (prompt, tool calls, token usage, output) stays in the host session store. Kept sessions accumulate until manually cleared; leave false for normal use. Requires a restart to take effect."),
159919
+ shadow_transform: exports_external.object({
159920
+ enabled: exports_external.boolean().default(false).describe("Dev flag: mirror every transform pass to the Magic Context Rust module over subc and byte-compare outputs. No behavior impact; default off.")
159921
+ }).default({ enabled: false }).describe("Developer-only shadow transform lane. When enabled, the plugin fire-and-forgets a copy of each finalized transform to the Rust module for comparison; the live output is unchanged."),
159854
159922
  todowrite: exports_external.object({
159855
159923
  enabled: exports_external.boolean().default(true).describe("Pi only: register Magic Context's todowrite task-list tool. Disable if you use your own todo extension. OpenCode ships its own built-in todowrite; this setting has no effect there."),
159856
159924
  overlay: exports_external.boolean().default(true).describe("Pi only: show the persistent todo overlay above the editor while tasks are active.")
@@ -160007,6 +160075,10 @@ function stripUnsafeProjectConfigFields(projectRaw) {
160007
160075
  delete projectRaw.sqlite;
160008
160076
  warnings.push("Ignoring sqlite.* from project config (security: SQLite cache/mmap PRAGMAs apply to the " + "process-global shared database handle; only user-level config may set them).");
160009
160077
  }
160078
+ if ("shadow_transform" in projectRaw) {
160079
+ delete projectRaw.shadow_transform;
160080
+ warnings.push("Ignoring shadow_transform from project config (security: this developer-only mirror lane is user-level only).");
160081
+ }
160010
160082
  const embedding = projectRaw.embedding;
160011
160083
  if (isPlainObject3(embedding)) {
160012
160084
  const removed = [];
@@ -160679,6 +160751,9 @@ function loadPiConfigDetailed(opts = {}) {
160679
160751
  };
160680
160752
  }
160681
160753
 
160754
+ // src/embedding-bootstrap.ts
160755
+ import { statSync as statSync3 } from "node:fs";
160756
+
160682
160757
  // ../plugin/src/features/magic-context/memory/embedding.ts
160683
160758
  init_logger();
160684
160759
 
@@ -160832,22 +160907,6 @@ function getInsertEmbeddingStatement(db) {
160832
160907
  }
160833
160908
  return stmt;
160834
160909
  }
160835
- function getClearProjectStatement(db) {
160836
- let stmt = clearProjectStatements.get(db);
160837
- if (!stmt) {
160838
- stmt = db.prepare("DELETE FROM compartment_chunk_embeddings WHERE project_path = ?");
160839
- clearProjectStatements.set(db, stmt);
160840
- }
160841
- return stmt;
160842
- }
160843
- function getClearProjectModelStatement(db) {
160844
- let stmt = clearProjectModelStatements.get(db);
160845
- if (!stmt) {
160846
- stmt = db.prepare("DELETE FROM compartment_chunk_embeddings WHERE project_path = ? AND model_id = ?");
160847
- clearProjectModelStatements.set(db, stmt);
160848
- }
160849
- return stmt;
160850
- }
160851
160910
  function getSearchRowsStatement(db, withModel) {
160852
160911
  const map2 = withModel ? searchRowsByModelStatements : searchRowsStatements;
160853
160912
  let stmt = map2.get(db);
@@ -161151,12 +161210,6 @@ function replaceCompartmentChunkEmbeddings(db, rows) {
161151
161210
  }
161152
161211
  })();
161153
161212
  }
161154
- function clearChunkEmbeddingsForProject(db, projectPath, modelId) {
161155
- if (modelId) {
161156
- return getClearProjectModelStatement(db).run(projectPath, modelId).changes;
161157
- }
161158
- return getClearProjectStatement(db).run(projectPath).changes;
161159
- }
161160
161213
  function loadCompartmentChunkEmbeddingsForSearch(db, sessionId, projectPath, modelId) {
161161
161214
  if (!modelId) {
161162
161215
  throw new Error("loadCompartmentChunkEmbeddingsForSearch requires a current model id");
@@ -162081,22 +162134,6 @@ function getLoadAllEmbeddingsStatement(db) {
162081
162134
  }
162082
162135
  return stmt;
162083
162136
  }
162084
- function getClearAllEmbeddingsStatement(db) {
162085
- let stmt = clearAllEmbeddingsStatements.get(db);
162086
- if (!stmt) {
162087
- stmt = db.prepare("DELETE FROM memory_embeddings WHERE memory_id IN (SELECT id FROM memories WHERE project_path = ?)");
162088
- clearAllEmbeddingsStatements.set(db, stmt);
162089
- }
162090
- return stmt;
162091
- }
162092
- function getClearModelEmbeddingsStatement(db) {
162093
- let stmt = clearModelEmbeddingsStatements.get(db);
162094
- if (!stmt) {
162095
- stmt = db.prepare("DELETE FROM memory_embeddings WHERE model_id = ? AND memory_id IN (SELECT id FROM memories WHERE project_path = ?)");
162096
- clearModelEmbeddingsStatements.set(db, stmt);
162097
- }
162098
- return stmt;
162099
- }
162100
162137
  function saveEmbedding(db, memoryId, embedding, modelId) {
162101
162138
  const blob = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
162102
162139
  getSaveEmbeddingStatement(db).run(memoryId, blob, modelId);
@@ -162116,12 +162153,6 @@ function loadAllEmbeddings(db, projectPath, modelId) {
162116
162153
  }
162117
162154
  return embeddings;
162118
162155
  }
162119
- function clearEmbeddingsForProject(db, projectPath, modelId) {
162120
- if (modelId) {
162121
- return getClearModelEmbeddingsStatement(db).run(modelId, projectPath).changes;
162122
- }
162123
- return getClearAllEmbeddingsStatement(db).run(projectPath).changes;
162124
- }
162125
162156
  function getMemoryEmbedCoverage(db, projectPath, modelId) {
162126
162157
  const row = db.prepare(`SELECT
162127
162158
  COUNT(*) AS total,
@@ -162194,24 +162225,6 @@ function getCountEmbeddedStatement(db) {
162194
162225
  }
162195
162226
  return stmt;
162196
162227
  }
162197
- function getClearProjectStatement2(db) {
162198
- let stmt = clearProjectStatements2.get(db);
162199
- if (!stmt) {
162200
- stmt = db.prepare(`DELETE FROM git_commit_embeddings
162201
- WHERE sha IN (SELECT sha FROM git_commits WHERE project_path = ?)`);
162202
- clearProjectStatements2.set(db, stmt);
162203
- }
162204
- return stmt;
162205
- }
162206
- function getClearProjectModelStatement2(db) {
162207
- let stmt = clearProjectModelStatements2.get(db);
162208
- if (!stmt) {
162209
- stmt = db.prepare(`DELETE FROM git_commit_embeddings
162210
- WHERE model_id = ? AND sha IN (SELECT sha FROM git_commits WHERE project_path = ?)`);
162211
- clearProjectModelStatements2.set(db, stmt);
162212
- }
162213
- return stmt;
162214
- }
162215
162228
  function saveCommitEmbedding(db, sha, embedding, modelId) {
162216
162229
  const bytes = new Uint8Array(embedding.buffer, embedding.byteOffset, embedding.byteLength);
162217
162230
  getSaveStatement(db).run(sha, bytes, modelId, Date.now());
@@ -162232,12 +162245,6 @@ function countEmbeddedCommits(db, projectPath, modelId) {
162232
162245
  const row = getCountEmbeddedStatement(db).get(projectPath, modelId);
162233
162246
  return row?.count ?? 0;
162234
162247
  }
162235
- function clearProjectCommitEmbeddings(db, projectPath, modelId) {
162236
- if (modelId) {
162237
- return getClearProjectModelStatement2(db).run(modelId, projectPath).changes;
162238
- }
162239
- return getClearProjectStatement2(db).run(projectPath).changes;
162240
- }
162241
162248
 
162242
162249
  // ../plugin/src/features/magic-context/git-commits/storage-git-commits.ts
162243
162250
  init_logger();
@@ -162539,6 +162546,7 @@ function invalidateMemory(projectPath, memoryId) {
162539
162546
  }
162540
162547
 
162541
162548
  // ../plugin/src/features/magic-context/session-project-storage.ts
162549
+ var SESSION_CHUNK_REPAIR_BATCH_SIZE = 100;
162542
162550
  var upsertSessionProjectStatements = new WeakMap;
162543
162551
  var repairSessionChunkProjectStatements = new WeakMap;
162544
162552
  var repairProjectChunkProjectStatements = new WeakMap;
@@ -162560,9 +162568,14 @@ function getRepairSessionChunkProjectStatement(db) {
162560
162568
  if (!stmt) {
162561
162569
  stmt = db.prepare(`UPDATE compartment_chunk_embeddings
162562
162570
  SET project_path = ?
162563
- WHERE session_id = ?
162564
- AND harness = ?
162565
- AND project_path <> ?`);
162571
+ WHERE id IN (
162572
+ SELECT id
162573
+ FROM compartment_chunk_embeddings
162574
+ WHERE session_id = ?
162575
+ AND harness = ?
162576
+ AND project_path <> ?
162577
+ LIMIT ?
162578
+ )`);
162566
162579
  repairSessionChunkProjectStatements.set(db, stmt);
162567
162580
  }
162568
162581
  return stmt;
@@ -162600,7 +162613,7 @@ function recordSessionProjectIdentity(db, sessionId, projectPath) {
162600
162613
  const now = Date.now();
162601
162614
  db.transaction(() => {
162602
162615
  getUpsertSessionProjectStatement(db).run(sessionId, harness, projectPath, now);
162603
- getRepairSessionChunkProjectStatement(db).run(projectPath, sessionId, harness, projectPath);
162616
+ getRepairSessionChunkProjectStatement(db).run(projectPath, sessionId, harness, projectPath, SESSION_CHUNK_REPAIR_BATCH_SIZE);
162604
162617
  })();
162605
162618
  }
162606
162619
  function repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectPath) {
@@ -162616,6 +162629,7 @@ var COMMIT_DRAIN_BATCH_SIZE = 16;
162616
162629
  var COMMIT_DRAIN_MAX_PER_SWEEP = 500;
162617
162630
  var CHUNK_DRAIN_BATCH_SIZE = 8;
162618
162631
  var EMBEDDING_IDENTITY_GC_GRACE_MS = 14 * 24 * 60 * 60 * 1000;
162632
+ var STALE_EMBEDDING_GC_BATCH_SIZE = 250;
162619
162633
  var MAX_WINDOWS_PER_EMBED_CALL = 2;
162620
162634
  var SESSION_EMBED_LEASE_RENEWAL_MS = 60 * 1000;
162621
162635
  var EMBED_SLICE_RETRY_ATTEMPTS = 3;
@@ -162844,6 +162858,55 @@ function staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff)
162844
162858
  const rows = getStaleIdentityStatement(db, scope).all(projectIdentity, scope, currentModelId, cutoff);
162845
162859
  return rows.map((row) => row.modelId).filter((modelId) => typeof modelId === "string");
162846
162860
  }
162861
+ function deleteStaleEmbeddingBatch(db, scope, projectIdentity, modelId, limit) {
162862
+ if (scope === "memory") {
162863
+ return db.prepare(`DELETE FROM memory_embeddings
162864
+ WHERE rowid IN (
162865
+ SELECT me.rowid
162866
+ FROM memory_embeddings me
162867
+ JOIN memories m ON m.id = me.memory_id
162868
+ WHERE m.project_path = ? AND me.model_id = ?
162869
+ LIMIT ?
162870
+ )`).run(projectIdentity, modelId, limit).changes;
162871
+ }
162872
+ if (scope === "commit") {
162873
+ return db.prepare(`DELETE FROM git_commit_embeddings
162874
+ WHERE rowid IN (
162875
+ SELECT gce.rowid
162876
+ FROM git_commit_embeddings gce
162877
+ JOIN git_commits gc ON gc.sha = gce.sha
162878
+ WHERE gc.project_path = ? AND gce.model_id = ?
162879
+ LIMIT ?
162880
+ )`).run(projectIdentity, modelId, limit).changes;
162881
+ }
162882
+ return db.prepare(`DELETE FROM compartment_chunk_embeddings
162883
+ WHERE id IN (
162884
+ SELECT id
162885
+ FROM compartment_chunk_embeddings
162886
+ WHERE project_path = ? AND model_id = ?
162887
+ LIMIT ?
162888
+ )`).run(projectIdentity, modelId, limit).changes;
162889
+ }
162890
+ function hasStaleEmbeddingRows(db, scope, projectIdentity, modelId) {
162891
+ if (scope === "memory") {
162892
+ return Boolean(db.prepare(`SELECT 1
162893
+ FROM memory_embeddings me
162894
+ JOIN memories m ON m.id = me.memory_id
162895
+ WHERE m.project_path = ? AND me.model_id = ?
162896
+ LIMIT 1`).get(projectIdentity, modelId));
162897
+ }
162898
+ if (scope === "commit") {
162899
+ return Boolean(db.prepare(`SELECT 1
162900
+ FROM git_commit_embeddings gce
162901
+ JOIN git_commits gc ON gc.sha = gce.sha
162902
+ WHERE gc.project_path = ? AND gce.model_id = ?
162903
+ LIMIT 1`).get(projectIdentity, modelId));
162904
+ }
162905
+ return Boolean(db.prepare(`SELECT 1
162906
+ FROM compartment_chunk_embeddings
162907
+ WHERE project_path = ? AND model_id = ?
162908
+ LIMIT 1`).get(projectIdentity, modelId));
162909
+ }
162847
162910
  function sweepStaleEmbeddingIdentitiesForProject(db, projectIdentity, now = Date.now()) {
162848
162911
  const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
162849
162912
  const result = {
@@ -162858,24 +162921,45 @@ function sweepStaleEmbeddingIdentitiesForProject(db, projectIdentity, now = Date
162858
162921
  return result;
162859
162922
  const cutoff = now - EMBEDDING_IDENTITY_GC_GRACE_MS;
162860
162923
  const deleteTracking = getDeleteActiveIdentityStatement(db);
162924
+ const scopes = [
162925
+ {
162926
+ scope: "memory",
162927
+ enabled: snapshot.enabled && snapshot.modelId !== "off",
162928
+ currentModelId: snapshot.modelId
162929
+ },
162930
+ {
162931
+ scope: "commit",
162932
+ enabled: snapshot.gitCommitEnabled && snapshot.modelId !== "off",
162933
+ currentModelId: snapshot.modelId
162934
+ },
162935
+ {
162936
+ scope: "chunk",
162937
+ enabled: snapshot.enabled && snapshot.chunkModelId !== "off",
162938
+ currentModelId: snapshot.chunkModelId
162939
+ }
162940
+ ];
162941
+ let remainingBudget = STALE_EMBEDDING_GC_BATCH_SIZE;
162861
162942
  db.exec("BEGIN IMMEDIATE");
162862
162943
  try {
162863
- if (snapshot.enabled && snapshot.modelId !== "off") {
162864
- for (const modelId of staleModelsForScope(db, projectIdentity, "memory", snapshot.modelId, cutoff)) {
162865
- result.memoryRowsDeleted += clearEmbeddingsForProject(db, projectIdentity, modelId);
162866
- result.trackingRowsDeleted += deleteTracking.run(projectIdentity, "memory", modelId).changes;
162867
- }
162868
- }
162869
- if (snapshot.gitCommitEnabled && snapshot.modelId !== "off") {
162870
- for (const modelId of staleModelsForScope(db, projectIdentity, "commit", snapshot.modelId, cutoff)) {
162871
- result.commitRowsDeleted += clearProjectCommitEmbeddings(db, projectIdentity, modelId);
162872
- result.trackingRowsDeleted += deleteTracking.run(projectIdentity, "commit", modelId).changes;
162873
- }
162874
- }
162875
- if (snapshot.enabled && snapshot.chunkModelId !== "off") {
162876
- for (const modelId of staleModelsForScope(db, projectIdentity, "chunk", snapshot.chunkModelId, cutoff)) {
162877
- result.chunkRowsDeleted += clearChunkEmbeddingsForProject(db, projectIdentity, modelId);
162878
- result.trackingRowsDeleted += deleteTracking.run(projectIdentity, "chunk", modelId).changes;
162944
+ for (const { scope, enabled, currentModelId } of scopes) {
162945
+ if (!enabled || remainingBudget === 0)
162946
+ continue;
162947
+ for (const modelId of staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff)) {
162948
+ if (remainingBudget === 0)
162949
+ break;
162950
+ const deleted = deleteStaleEmbeddingBatch(db, scope, projectIdentity, modelId, remainingBudget);
162951
+ remainingBudget -= deleted;
162952
+ if (scope === "memory")
162953
+ result.memoryRowsDeleted += deleted;
162954
+ else if (scope === "commit")
162955
+ result.commitRowsDeleted += deleted;
162956
+ else
162957
+ result.chunkRowsDeleted += deleted;
162958
+ if (!hasStaleEmbeddingRows(db, scope, projectIdentity, modelId)) {
162959
+ result.trackingRowsDeleted += deleteTracking.run(projectIdentity, scope, modelId).changes;
162960
+ } else if (deleted === 0) {
162961
+ remainingBudget = 0;
162962
+ }
162879
162963
  }
162880
162964
  }
162881
162965
  db.exec("COMMIT");
@@ -162885,9 +162969,8 @@ function sweepStaleEmbeddingIdentitiesForProject(db, projectIdentity, now = Date
162885
162969
  } catch {}
162886
162970
  throw error51;
162887
162971
  }
162888
- if (result.memoryRowsDeleted > 0) {
162972
+ if (result.memoryRowsDeleted > 0)
162889
162973
  invalidateProject(projectIdentity);
162890
- }
162891
162974
  return result;
162892
162975
  }
162893
162976
  function registerProjectEmbedding(db, projectIdentity, config2, features, sourceDirectory) {
@@ -163107,16 +163190,27 @@ async function drainCommitBacklogForProject(db, projectIdentity, deadline) {
163107
163190
  return 0;
163108
163191
  }
163109
163192
  let total = 0;
163193
+ let leaseLost = false;
163194
+ const renewal = setInterval(() => {
163195
+ try {
163196
+ if (!renewGitSweepLease(db, projectIdentity, holderId))
163197
+ leaseLost = true;
163198
+ } catch {}
163199
+ }, SESSION_EMBED_LEASE_RENEWAL_MS);
163200
+ renewal.unref?.();
163110
163201
  try {
163111
- while (Date.now() < deadline && total < COMMIT_DRAIN_MAX_PER_SWEEP) {
163202
+ while (!leaseLost && Date.now() < deadline && total < COMMIT_DRAIN_MAX_PER_SWEEP) {
163112
163203
  const embedded = await embedCommitBatch(db, projectIdentity, COMMIT_DRAIN_BATCH_SIZE);
163113
- if (embedded === 0)
163204
+ if (!renewGitSweepLease(db, projectIdentity, holderId))
163205
+ leaseLost = true;
163206
+ if (leaseLost || embedded === 0)
163114
163207
  break;
163115
163208
  total += embedded;
163116
163209
  if (embedded < COMMIT_DRAIN_BATCH_SIZE)
163117
163210
  break;
163118
163211
  }
163119
163212
  } finally {
163213
+ clearInterval(renewal);
163120
163214
  releaseGitSweepLease(db, projectIdentity, holderId);
163121
163215
  }
163122
163216
  return total;
@@ -163227,9 +163321,19 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
163227
163321
  const lease = acquireGitSweepLease(db, projectIdentity, holderId, { ignoreCooldown: true });
163228
163322
  if (!lease.acquired)
163229
163323
  return { status: "busy", embedded: 0, total };
163324
+ let leaseLost = false;
163325
+ const drainAbort = new AbortController;
163326
+ const forwardCallerAbort = () => drainAbort.abort();
163327
+ if (options?.signal?.aborted)
163328
+ drainAbort.abort();
163329
+ else
163330
+ options?.signal?.addEventListener("abort", forwardCallerAbort, { once: true });
163230
163331
  const renewal = setInterval(() => {
163231
163332
  try {
163232
- renewGitSweepLease(db, projectIdentity, holderId);
163333
+ if (!renewGitSweepLease(db, projectIdentity, holderId)) {
163334
+ leaseLost = true;
163335
+ drainAbort.abort();
163336
+ }
163233
163337
  } catch {}
163234
163338
  }, SESSION_EMBED_LEASE_RENEWAL_MS);
163235
163339
  renewal.unref?.();
@@ -163243,7 +163347,7 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
163243
163347
  try {
163244
163348
  options?.onProgress?.({ embedded, total });
163245
163349
  for (;; ) {
163246
- if (options?.signal?.aborted) {
163350
+ if (leaseLost || drainAbort.signal.aborted) {
163247
163351
  aborted2 = true;
163248
163352
  break;
163249
163353
  }
@@ -163254,7 +163358,13 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
163254
163358
  embedded: n,
163255
163359
  noWork,
163256
163360
  failed
163257
- } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
163361
+ } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, drainAbort.signal);
163362
+ if (leaseLost || !renewGitSweepLease(db, projectIdentity, holderId)) {
163363
+ leaseLost = true;
163364
+ drainAbort.abort();
163365
+ aborted2 = true;
163366
+ break;
163367
+ }
163258
163368
  for (const id of noWork)
163259
163369
  skipIds.push(id);
163260
163370
  for (const id of failed)
@@ -163274,6 +163384,7 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
163274
163384
  }
163275
163385
  } finally {
163276
163386
  clearInterval(renewal);
163387
+ options?.signal?.removeEventListener("abort", forwardCallerAbort);
163277
163388
  try {
163278
163389
  releaseGitSweepLease(db, projectIdentity, holderId);
163279
163390
  } catch (error51) {
@@ -163471,8 +163582,38 @@ function handleUntrustedLoad(db, projectIdentity, directory, detailed) {
163471
163582
  }
163472
163583
 
163473
163584
  // src/embedding-bootstrap.ts
163585
+ var registrationFingerprintsByDatabase = new WeakMap;
163586
+ function configCandidatePaths(directory, loadedPaths) {
163587
+ const projectBase = cortexKitProjectConfigBasePath(directory);
163588
+ const userBase = cortexKitUserConfigBasePath();
163589
+ return [
163590
+ `${projectBase}.jsonc`,
163591
+ `${projectBase}.json`,
163592
+ `${userBase}.jsonc`,
163593
+ `${userBase}.json`,
163594
+ ...loadedPaths
163595
+ ].filter((path4, index, paths) => paths.indexOf(path4) === index);
163596
+ }
163597
+ function configFingerprint(paths) {
163598
+ return paths.map((path4) => {
163599
+ try {
163600
+ const stat2 = statSync3(path4);
163601
+ return `${path4}:${stat2.size}:${stat2.mtimeMs}`;
163602
+ } catch {
163603
+ return `${path4}:missing`;
163604
+ }
163605
+ }).join("|");
163606
+ }
163474
163607
  async function ensureProjectRegisteredFromPiDirectory(directory, db) {
163475
163608
  const projectIdentity = resolveProjectIdentity(directory);
163609
+ let registrationFingerprints = registrationFingerprintsByDatabase.get(db);
163610
+ if (!registrationFingerprints) {
163611
+ registrationFingerprints = new Map;
163612
+ registrationFingerprintsByDatabase.set(db, registrationFingerprints);
163613
+ }
163614
+ const cached2 = registrationFingerprints.get(projectIdentity);
163615
+ if (cached2 && configFingerprint(cached2.paths) === cached2.fingerprint)
163616
+ return;
163476
163617
  const detailed = loadPiConfigDetailed({ cwd: directory });
163477
163618
  if (isConfigLoadUntrusted(detailed)) {
163478
163619
  handleUntrustedLoad(db, projectIdentity, directory, detailed);
@@ -163483,6 +163624,11 @@ async function ensureProjectRegisteredFromPiDirectory(directory, db) {
163483
163624
  gitCommitEnabled: detailed.config.memory.git_commit_indexing.enabled
163484
163625
  };
163485
163626
  registerProjectEmbedding(db, projectIdentity, detailed.config.embedding, features, directory);
163627
+ const fingerprintPaths = configCandidatePaths(directory, detailed.loadedFromPaths);
163628
+ registrationFingerprints.set(projectIdentity, {
163629
+ paths: fingerprintPaths,
163630
+ fingerprint: configFingerprint(fingerprintPaths)
163631
+ });
163486
163632
  }
163487
163633
  // ../plugin/src/features/magic-context/compartment-lease.ts
163488
163634
  var COMPARTMENT_LEASE_TTL_MS = 5 * 60 * 1000;
@@ -164102,6 +164248,12 @@ function parseJsonRecord(value) {
164102
164248
  return null;
164103
164249
  }
164104
164250
  }
164251
+ function isRawCompactionSummaryInfo(info) {
164252
+ if (info === null || typeof info !== "object" || Array.isArray(info))
164253
+ return false;
164254
+ const candidate = info;
164255
+ return candidate.summary === true && candidate.finish === "stop";
164256
+ }
164105
164257
  function parseJsonUnknown(value) {
164106
164258
  try {
164107
164259
  return JSON.parse(value);
@@ -164124,7 +164276,7 @@ function attachRawPartVersion(value, timeUpdated) {
164124
164276
  return value;
164125
164277
  }
164126
164278
  function readRawSessionMessagesFromDb(db, sessionId) {
164127
- const messageRows = db.prepare("SELECT id, data, time_updated FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId).filter(isRawMessageRow);
164279
+ const messageRows = db.prepare("SELECT id, data, time_created, time_updated FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId).filter(isRawMessageRow);
164128
164280
  const partRows = db.prepare("SELECT message_id, data, time_updated FROM part WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId).filter(isRawPartRow);
164129
164281
  const partsByMessageId = new Map;
164130
164282
  for (const part of partRows) {
@@ -164132,10 +164284,7 @@ function readRawSessionMessagesFromDb(db, sessionId) {
164132
164284
  list.push(attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated));
164133
164285
  partsByMessageId.set(part.message_id, list);
164134
164286
  }
164135
- const filtered = messageRows.filter((row) => {
164136
- const info = parseJsonRecord(row.data);
164137
- return !(info?.summary === true && info?.finish === "stop");
164138
- });
164287
+ const filtered = messageRows.filter((row) => !isRawCompactionSummaryInfo(parseJsonRecord(row.data)));
164139
164288
  return filtered.flatMap((row, index) => {
164140
164289
  const info = parseJsonRecord(row.data);
164141
164290
  if (!info)
@@ -164146,10 +164295,71 @@ function readRawSessionMessagesFromDb(db, sessionId) {
164146
164295
  id: row.id,
164147
164296
  role,
164148
164297
  parts: partsByMessageId.get(row.id) ?? [],
164298
+ createdAt: row.time_created ?? null,
164299
+ version: row.time_updated ?? null
164300
+ };
164301
+ });
164302
+ }
164303
+ function readRawSessionMessagePageFromDb(db, sessionId, afterOrdinal, limit, finalWatermark = Number.MAX_SAFE_INTEGER) {
164304
+ const remaining = Math.max(0, Math.floor(finalWatermark) - Math.floor(afterOrdinal));
164305
+ const pageSize = Math.min(Math.max(1, Math.floor(limit)), remaining);
164306
+ if (pageSize === 0)
164307
+ return [];
164308
+ const messageRows = db.prepare(`SELECT id, data, time_created, time_updated
164309
+ FROM message
164310
+ WHERE session_id = ?
164311
+ AND NOT (
164312
+ CASE WHEN json_valid(data) = 1
164313
+ THEN COALESCE(json_extract(data, '$.summary'), 0)
164314
+ ELSE 0 END = 1
164315
+ AND CASE WHEN json_valid(data) = 1
164316
+ THEN COALESCE(json_extract(data, '$.finish'), '')
164317
+ ELSE '' END = 'stop'
164318
+ )
164319
+ ORDER BY time_created ASC, id ASC
164320
+ LIMIT ? OFFSET ?`).all(sessionId, pageSize, Math.max(0, Math.floor(afterOrdinal))).filter(isRawMessageRow).map((row, index) => ({
164321
+ ...row,
164322
+ ordinal: Math.floor(afterOrdinal) + index + 1
164323
+ }));
164324
+ if (messageRows.length === 0)
164325
+ return [];
164326
+ const placeholders2 = messageRows.map(() => "?").join(", ");
164327
+ const partRows = db.prepare(`SELECT message_id, data, time_updated
164328
+ FROM part
164329
+ WHERE session_id = ? AND message_id IN (${placeholders2})
164330
+ ORDER BY time_created ASC, id ASC`).all(sessionId, ...messageRows.map((row) => row.id)).filter(isRawPartRow);
164331
+ const partsByMessageId = new Map;
164332
+ for (const part of partRows) {
164333
+ const list = partsByMessageId.get(part.message_id) ?? [];
164334
+ list.push(attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated));
164335
+ partsByMessageId.set(part.message_id, list);
164336
+ }
164337
+ return messageRows.map((row) => {
164338
+ const info = parseJsonRecord(row.data);
164339
+ return {
164340
+ ordinal: row.ordinal,
164341
+ id: row.id,
164342
+ role: typeof info?.role === "string" ? info.role : "unknown",
164343
+ parts: partsByMessageId.get(row.id) ?? [],
164344
+ createdAt: row.time_created ?? null,
164149
164345
  version: row.time_updated ?? null
164150
164346
  };
164151
164347
  });
164152
164348
  }
164349
+ function countRawSessionMessageOrdinalsFromDb(db, sessionId) {
164350
+ const row = db.prepare(`SELECT COUNT(*) AS count
164351
+ FROM message
164352
+ WHERE session_id = ?
164353
+ AND NOT (
164354
+ CASE WHEN json_valid(data) = 1
164355
+ THEN COALESCE(json_extract(data, '$.summary'), 0)
164356
+ ELSE 0 END = 1
164357
+ AND CASE WHEN json_valid(data) = 1
164358
+ THEN COALESCE(json_extract(data, '$.finish'), '')
164359
+ ELSE '' END = 'stop'
164360
+ )`).get(sessionId);
164361
+ return typeof row?.count === "number" ? row.count : 0;
164362
+ }
164153
164363
  function isAnchorRow(row) {
164154
164364
  return row !== null && typeof row === "object" && typeof row.time_created === "number" && typeof row.id === "string";
164155
164365
  }
@@ -164160,7 +164370,7 @@ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
164160
164370
  const anchorInfo = parseJsonRecord(anchorRow.data ?? "");
164161
164371
  if (anchorInfo?.summary === true && anchorInfo?.finish === "stop")
164162
164372
  return null;
164163
- const messageRows = db.prepare(`SELECT id, data, time_updated FROM message
164373
+ const messageRows = db.prepare(`SELECT id, data, time_created, time_updated FROM message
164164
164374
  WHERE session_id = ?
164165
164375
  AND (time_created > ? OR (time_created = ? AND id >= ?))
164166
164376
  ORDER BY time_created ASC, id ASC`).all(sessionId, anchorRow.time_created, anchorRow.time_created, anchorRow.id).filter(isRawMessageRow);
@@ -164196,6 +164406,7 @@ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
164196
164406
  id: row.id,
164197
164407
  role: typeof info.role === "string" ? info.role : "unknown",
164198
164408
  parts: partsByMessageId.get(row.id) ?? [],
164409
+ createdAt: row.time_created ?? null,
164199
164410
  version: row.time_updated ?? null
164200
164411
  });
164201
164412
  ord += 1;
@@ -164456,6 +164667,31 @@ function readRawSessionMessages(sessionId) {
164456
164667
  }
164457
164668
  return readRawSessionMessagesFromSource(sessionId);
164458
164669
  }
164670
+ function readRawSessionMessagePage(sessionId, afterOrdinal, limit, finalWatermark) {
164671
+ const provider2 = sessionProviders.get(sessionId);
164672
+ if (provider2?.readMessagePage) {
164673
+ return provider2.readMessagePage(afterOrdinal, limit, finalWatermark);
164674
+ }
164675
+ if (provider2) {
164676
+ return provider2.readMessages().filter((message) => message.ordinal > afterOrdinal && message.ordinal <= finalWatermark).slice(0, limit);
164677
+ }
164678
+ if (!openCodeDbExists())
164679
+ return [];
164680
+ return withReadOnlySessionDb((db) => readRawSessionMessagePageFromDb(db, sessionId, afterOrdinal, limit, finalWatermark));
164681
+ }
164682
+ function getRawSessionMessageOrdinalCount(sessionId) {
164683
+ const provider2 = sessionProviders.get(sessionId);
164684
+ if (provider2) {
164685
+ if (provider2.getMessageCount)
164686
+ return provider2.getMessageCount();
164687
+ return provider2.readMessages().length;
164688
+ }
164689
+ if (!openCodeDbExists())
164690
+ return 0;
164691
+ return withReadOnlySessionDb((db) => countRawSessionMessageOrdinalsFromDb(db, sessionId));
164692
+ }
164693
+ readRawSessionMessages.readPage = readRawSessionMessagePage;
164694
+ readRawSessionMessages.getCount = getRawSessionMessageOrdinalCount;
164459
164695
  function primeTailRawMessageCache(args) {
164460
164696
  const { sessionId, lastCompartmentEnd, anchorMessageId } = args;
164461
164697
  if (!activeRawMessageCache)
@@ -164714,9 +164950,9 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
164714
164950
  // ../plugin/src/tools/ctx-expand/constants.ts
164715
164951
  var CTX_EXPAND_DESCRIPTION = `Recover the original conversation from your compacted history.
164716
164952
 
164717
- Older parts of this session are summarized into <compartment> blocks inside <session-history> — e.g. <compartment start="120" end="245" title="Fixed tagger collision">. Each one replaces the raw messages in that ordinal range with a summary. When the summary isn't enough — you need exact wording, a specific value, an error message, or the reasoning behind a decision — expand the range:
164953
+ Older parts of this session are summarized under \`## start-end · date · title\` headings inside <session-history> — e.g. \`## 120-245 · … · Fixed tagger collision\`. Each heading replaces the raw messages in that ordinal range with a summary. When the summary isn't enough — you need exact wording, a specific value, an error message, or the reasoning behind a decision — expand the range:
164718
164954
 
164719
- ctx_expand(start=120, end=245) ← the compartment's own start/end attributes
164955
+ ctx_expand(start=120, end=245) ← the heading's start/end range
164720
164956
 
164721
164957
  Returns the raw transcript as [N] U:/A: lines, capped at ~15K tokens; an oversized range returns the head and tells you where to continue. Also works with ordinals from ctx_search message results — expand a window around a hit (e.g. start=N-10, end=N+5). Ranges after the last compartment are your live tail — already visible in context, not expandable.
164722
164958
 
@@ -169128,7 +169364,7 @@ function resolvePiStableId(msg, index, entryIds, entryIdByRef) {
169128
169364
  const role = m.role ?? "unknown";
169129
169365
  return typeof m.timestamp === "number" ? `pi-msg-${index}-${m.timestamp}-${role}` : `pi-msg-${index}-${role}`;
169130
169366
  }
169131
- function isMidTurnPi(event, _sessionId) {
169367
+ function isMidTurnPi(event, _sessionId, branchEntries) {
169132
169368
  const messages = event?.messages;
169133
169369
  if (!Array.isArray(messages))
169134
169370
  return false;
@@ -169147,7 +169383,7 @@ function isMidTurnPi(event, _sessionId) {
169147
169383
  }
169148
169384
  if (latestAssistant === null)
169149
169385
  return false;
169150
- if (hasRealUserAfter(messages, latestAssistantIndex))
169386
+ if (hasRealUserAfter(messages, latestAssistantIndex, branchEntries))
169151
169387
  return false;
169152
169388
  if (latestAssistant.stopReason === "toolUse")
169153
169389
  return true;
@@ -169171,12 +169407,27 @@ function isMidTurnPi(event, _sessionId) {
169171
169407
  }
169172
169408
  return false;
169173
169409
  }
169174
- function hasRealUserAfter(messages, latestAssistantIndex) {
169175
- for (const msg of messages.slice(latestAssistantIndex + 1)) {
169176
- if (msg === null || typeof msg !== "object")
169410
+ function hasRealUserAfter(messages, latestAssistantIndex, branchEntries) {
169411
+ if (!branchEntries)
169412
+ return false;
169413
+ const genuineUserMessages = new Set;
169414
+ for (const entry of branchEntries) {
169415
+ if (entry === null || typeof entry !== "object")
169177
169416
  continue;
169178
- if (msg.role === "user")
169417
+ const record4 = entry;
169418
+ if (record4.type !== "message")
169419
+ continue;
169420
+ const message = record4.message;
169421
+ if (message === null || typeof message !== "object")
169422
+ continue;
169423
+ if (message.role === "user") {
169424
+ genuineUserMessages.add(message);
169425
+ }
169426
+ }
169427
+ for (const msg of messages.slice(latestAssistantIndex + 1)) {
169428
+ if (msg !== null && typeof msg === "object" && genuineUserMessages.has(msg)) {
169179
169429
  return true;
169430
+ }
169180
169431
  }
169181
169432
  return false;
169182
169433
  }
@@ -170406,7 +170657,7 @@ function getMemoryVerifications(db, memoryIds) {
170406
170657
  }
170407
170658
  // ../plugin/src/features/magic-context/memory/verification-paths.ts
170408
170659
  import { execFile } from "node:child_process";
170409
- import { existsSync as existsSync9, realpathSync as realpathSync2, statSync as statSync3 } from "node:fs";
170660
+ import { existsSync as existsSync9, realpathSync as realpathSync2, statSync as statSync4 } from "node:fs";
170410
170661
  import path4 from "node:path";
170411
170662
  import { promisify } from "node:util";
170412
170663
  var execFileAsync = promisify(execFile);
@@ -170548,7 +170799,7 @@ async function normalizeVerificationFiles(args) {
170548
170799
  }
170549
170800
  if (existsSync9(candidate)) {
170550
170801
  try {
170551
- if (statSync3(candidate).isDirectory()) {
170802
+ if (statSync4(candidate).isDirectory()) {
170552
170803
  warnings.push(`Skipped verification path "${value}" because it is a directory.`);
170553
170804
  continue;
170554
170805
  }
@@ -170598,11 +170849,10 @@ async function normalizeVerificationFiles(args) {
170598
170849
  // ../plugin/src/features/magic-context/message-index.ts
170599
170850
  var lastIndexedStatements = new WeakMap;
170600
170851
  var insertMessageStatements = new WeakMap;
170601
- var upsertIndexStatements = new WeakMap;
170602
- var upsertCleanIndexStatements = new WeakMap;
170852
+ var upsertProgressStatements = new WeakMap;
170603
170853
  var upsertDirtyFloorStatements = new WeakMap;
170604
170854
  var deleteFtsStatements = new WeakMap;
170605
- var deleteFtsFromOrdinalStatements = new WeakMap;
170855
+ var deleteFtsRangeStatements = new WeakMap;
170606
170856
  var deleteIndexStatements = new WeakMap;
170607
170857
  var countIndexedMessageStatements = new WeakMap;
170608
170858
  function normalizeIndexText(text) {
@@ -170624,19 +170874,11 @@ function getInsertMessageStatement(db) {
170624
170874
  }
170625
170875
  return stmt;
170626
170876
  }
170627
- function getUpsertIndexStatement(db) {
170628
- let stmt = upsertIndexStatements.get(db);
170877
+ function getUpsertProgressStatement(db) {
170878
+ let stmt = upsertProgressStatements.get(db);
170629
170879
  if (!stmt) {
170630
- stmt = db.prepare("INSERT INTO message_history_index (session_id, last_indexed_ordinal, updated_at, harness) VALUES (?, ?, ?, ?) ON CONFLICT(session_id) DO UPDATE SET last_indexed_ordinal = excluded.last_indexed_ordinal, updated_at = excluded.updated_at");
170631
- upsertIndexStatements.set(db, stmt);
170632
- }
170633
- return stmt;
170634
- }
170635
- function getUpsertCleanIndexStatement(db) {
170636
- let stmt = upsertCleanIndexStatements.get(db);
170637
- if (!stmt) {
170638
- stmt = db.prepare("INSERT INTO message_history_index (session_id, last_indexed_ordinal, dirty_floor_ordinal, updated_at, harness) VALUES (?, ?, 0, ?, ?) ON CONFLICT(session_id) DO UPDATE SET last_indexed_ordinal = excluded.last_indexed_ordinal, dirty_floor_ordinal = 0, updated_at = excluded.updated_at");
170639
- upsertCleanIndexStatements.set(db, stmt);
170880
+ stmt = db.prepare("INSERT INTO message_history_index (session_id, last_indexed_ordinal, dirty_floor_ordinal, updated_at, harness) VALUES (?, ?, ?, ?, ?) ON CONFLICT(session_id) DO UPDATE SET last_indexed_ordinal = excluded.last_indexed_ordinal, dirty_floor_ordinal = excluded.dirty_floor_ordinal, updated_at = excluded.updated_at");
170881
+ upsertProgressStatements.set(db, stmt);
170640
170882
  }
170641
170883
  return stmt;
170642
170884
  }
@@ -170648,27 +170890,11 @@ function getUpsertDirtyFloorStatement(db) {
170648
170890
  }
170649
170891
  return stmt;
170650
170892
  }
170651
- function getDeleteFtsStatement(db) {
170652
- let stmt = deleteFtsStatements.get(db);
170653
- if (!stmt) {
170654
- stmt = db.prepare("DELETE FROM message_history_fts WHERE session_id = ?");
170655
- deleteFtsStatements.set(db, stmt);
170656
- }
170657
- return stmt;
170658
- }
170659
- function getDeleteFtsFromOrdinalStatement(db) {
170660
- let stmt = deleteFtsFromOrdinalStatements.get(db);
170893
+ function getDeleteFtsRangeStatement(db) {
170894
+ let stmt = deleteFtsRangeStatements.get(db);
170661
170895
  if (!stmt) {
170662
- stmt = db.prepare("DELETE FROM message_history_fts WHERE session_id = ? AND CAST(message_ordinal AS INTEGER) >= ?");
170663
- deleteFtsFromOrdinalStatements.set(db, stmt);
170664
- }
170665
- return stmt;
170666
- }
170667
- function getDeleteIndexStatement(db) {
170668
- let stmt = deleteIndexStatements.get(db);
170669
- if (!stmt) {
170670
- stmt = db.prepare("DELETE FROM message_history_index WHERE session_id = ?");
170671
- deleteIndexStatements.set(db, stmt);
170896
+ stmt = db.prepare("DELETE FROM message_history_fts WHERE session_id = ? AND CAST(message_ordinal AS INTEGER) BETWEEN ? AND ?");
170897
+ deleteFtsRangeStatements.set(db, stmt);
170672
170898
  }
170673
170899
  return stmt;
170674
170900
  }
@@ -170696,16 +170922,17 @@ function isMessageAlreadyIndexed(db, sessionId, messageId) {
170696
170922
  const row = getCountIndexedMessageStatement(db).get(sessionId, messageId);
170697
170923
  return (typeof row?.count === "number" ? row.count : 0) > 0;
170698
170924
  }
170699
- function advanceIndexWatermark(db, sessionId, ordinal, now) {
170700
- const current = getLastIndexedOrdinal(db, sessionId);
170701
- getUpsertIndexStatement(db).run(sessionId, Math.max(current, ordinal), now, getHarness());
170925
+ function setIndexProgress(db, sessionId, watermark, dirtyFloor, now) {
170926
+ getUpsertProgressStatement(db).run(sessionId, Math.max(0, Math.floor(watermark)), dirtyFloor ?? 0, now, getHarness());
170702
170927
  }
170703
- function clearIndexedMessages(db, sessionId) {
170704
- db.transaction(() => {
170705
- getDeleteFtsStatement(db).run(sessionId);
170706
- getDeleteIndexStatement(db).run(sessionId);
170707
- clearCompressionDepth(db, sessionId);
170708
- })();
170928
+ function getMessageIndexReconciliationStartOrdinal(db, sessionId) {
170929
+ const watermark = getLastIndexedOrdinal(db, sessionId);
170930
+ const dirtyFloor = getDirtyIndexFloor(db, sessionId);
170931
+ return dirtyFloor === null ? watermark : Math.min(watermark, dirtyFloor - 1);
170932
+ }
170933
+ function isMessageIndexReconciledThrough(db, sessionId, finalWatermark) {
170934
+ const dirtyFloor = getDirtyIndexFloor(db, sessionId);
170935
+ return getLastIndexedOrdinal(db, sessionId) >= finalWatermark && dirtyFloor === null;
170709
170936
  }
170710
170937
  function getIndexableContent(role, parts) {
170711
170938
  if (role === "user") {
@@ -170720,22 +170947,21 @@ function getIndexableContent(role, parts) {
170720
170947
  return "";
170721
170948
  }
170722
170949
  function indexSingleMessageInTransaction(db, sessionId, message, now) {
170723
- if (message.role !== "user" && message.role !== "assistant") {
170724
- advanceIndexWatermark(db, sessionId, message.ordinal, now);
170725
- return false;
170726
- }
170727
- const content = getIndexableContent(message.role, message.parts);
170728
- if (content.length === 0) {
170729
- advanceIndexWatermark(db, sessionId, message.ordinal, now);
170950
+ const currentWatermark = getLastIndexedOrdinal(db, sessionId);
170951
+ const dirtyFloor = getDirtyIndexFloor(db, sessionId);
170952
+ if (message.ordinal !== currentWatermark + 1 || dirtyFloor !== null && dirtyFloor !== message.ordinal) {
170730
170953
  return false;
170731
170954
  }
170732
- if (isMessageAlreadyIndexed(db, sessionId, message.id)) {
170733
- advanceIndexWatermark(db, sessionId, message.ordinal, now);
170734
- return false;
170955
+ let inserted = false;
170956
+ if (message.role === "user" || message.role === "assistant") {
170957
+ const content = getIndexableContent(message.role, message.parts);
170958
+ if (content.length > 0 && !isMessageAlreadyIndexed(db, sessionId, message.id)) {
170959
+ getInsertMessageStatement(db).run(sessionId, message.ordinal, message.id, message.role, content);
170960
+ inserted = true;
170961
+ }
170735
170962
  }
170736
- getInsertMessageStatement(db).run(sessionId, message.ordinal, message.id, message.role, content);
170737
- advanceIndexWatermark(db, sessionId, message.ordinal, now);
170738
- return true;
170963
+ setIndexProgress(db, sessionId, message.ordinal, null, now);
170964
+ return inserted;
170739
170965
  }
170740
170966
  function indexSingleMessage(db, sessionId, message) {
170741
170967
  db.exec("BEGIN IMMEDIATE");
@@ -170753,38 +170979,44 @@ function indexSingleMessage(db, sessionId, message) {
170753
170979
  }
170754
170980
  }
170755
170981
  }
170756
- function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal, finalWatermark = messages.length) {
170982
+ function indexMessagesAfterOrdinal(db, sessionId, messages, _lastIndexedOrdinal, finalWatermark = messages.length) {
170757
170983
  const now = Date.now();
170758
170984
  let inserted = 0;
170759
170985
  db.exec("BEGIN IMMEDIATE");
170760
170986
  let committed = false;
170761
170987
  try {
170762
- let effectiveWatermark = Math.max(lastIndexedOrdinal, getLastIndexedOrdinal(db, sessionId));
170988
+ const currentWatermark = getLastIndexedOrdinal(db, sessionId);
170763
170989
  const dirtyFloor = getDirtyIndexFloor(db, sessionId);
170764
- if (dirtyFloor !== null) {
170765
- const rewindOrdinal = Math.max(1, Math.min(dirtyFloor, finalWatermark + 1));
170766
- if (rewindOrdinal <= finalWatermark) {
170767
- getDeleteFtsFromOrdinalStatement(db).run(sessionId, rewindOrdinal);
170768
- }
170769
- effectiveWatermark = Math.min(effectiveWatermark, rewindOrdinal - 1);
170990
+ const effectiveWatermark = dirtyFloor === null ? currentWatermark : Math.min(currentWatermark, Math.max(0, dirtyFloor - 1));
170991
+ if (dirtyFloor !== null && dirtyFloor <= finalWatermark) {
170992
+ getDeleteFtsRangeStatement(db).run(sessionId, dirtyFloor, finalWatermark);
170770
170993
  }
170771
- const insertMessage = getInsertMessageStatement(db);
170994
+ const messagesByOrdinal = new Map;
170772
170995
  for (const message of messages) {
170773
- if (message.ordinal <= effectiveWatermark) {
170774
- continue;
170996
+ if (message.ordinal > effectiveWatermark && message.ordinal <= finalWatermark) {
170997
+ messagesByOrdinal.set(message.ordinal, message);
170775
170998
  }
170776
- if (message.role !== "user" && message.role !== "assistant") {
170999
+ }
171000
+ let coveredWatermark = effectiveWatermark;
171001
+ while (coveredWatermark < finalWatermark && messagesByOrdinal.has(coveredWatermark + 1)) {
171002
+ coveredWatermark += 1;
171003
+ }
171004
+ for (let ordinal = effectiveWatermark + 1;ordinal <= coveredWatermark; ordinal++) {
171005
+ const message = messagesByOrdinal.get(ordinal);
171006
+ if (!message || message.role !== "user" && message.role !== "assistant") {
170777
171007
  continue;
170778
171008
  }
170779
171009
  const content = getIndexableContent(message.role, message.parts);
170780
- if (content.length === 0) {
171010
+ if (content.length === 0 || isMessageAlreadyIndexed(db, sessionId, message.id)) {
170781
171011
  continue;
170782
171012
  }
170783
- insertMessage.run(sessionId, message.ordinal, message.id, message.role, content);
170784
- inserted++;
171013
+ getInsertMessageStatement(db).run(sessionId, message.ordinal, message.id, message.role, content);
171014
+ inserted += 1;
170785
171015
  }
170786
- const newWatermark = Math.max(effectiveWatermark, finalWatermark);
170787
- getUpsertCleanIndexStatement(db).run(sessionId, newWatermark, now, getHarness());
171016
+ const missingFloor = coveredWatermark < finalWatermark ? coveredWatermark + 1 : null;
171017
+ const preservedFloor = dirtyFloor !== null && dirtyFloor > finalWatermark ? dirtyFloor : null;
171018
+ const nextDirtyFloor = missingFloor === null ? preservedFloor : preservedFloor === null ? missingFloor : Math.min(missingFloor, preservedFloor);
171019
+ setIndexProgress(db, sessionId, coveredWatermark, nextDirtyFloor, now);
170788
171020
  db.exec("COMMIT");
170789
171021
  committed = true;
170790
171022
  } finally {
@@ -170798,7 +171030,7 @@ function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal,
170798
171030
  }
170799
171031
  // ../plugin/src/features/magic-context/project-docs-hash.ts
170800
171032
  import { createHash as createHash7 } from "node:crypto";
170801
- import { lstatSync, readFileSync as readFileSync5, statSync as statSync4 } from "node:fs";
171033
+ import { lstatSync, readFileSync as readFileSync5, statSync as statSync5 } from "node:fs";
170802
171034
  import path5 from "node:path";
170803
171035
  var PROJECT_DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
170804
171036
  var PROJECT_DOCS_DELIMITER = `
@@ -170829,7 +171061,7 @@ function fingerprintFile(filePath) {
170829
171061
  }
170830
171062
  function readDirectoryMtimeMs(projectDirectory) {
170831
171063
  try {
170832
- return statSync4(projectDirectory).mtimeMs;
171064
+ return statSync5(projectDirectory).mtimeMs;
170833
171065
  } catch {
170834
171066
  return 0;
170835
171067
  }
@@ -170919,6 +171151,160 @@ function readProjectDocsCanonical(projectDirectory) {
170919
171151
  });
170920
171152
  return { renderedBlock, canonicalHash };
170921
171153
  }
171154
+ // ../plugin/src/features/magic-context/storage-clone.ts
171155
+ function runImmediate2(db, body) {
171156
+ db.exec("BEGIN IMMEDIATE");
171157
+ let committed = false;
171158
+ try {
171159
+ const result = body();
171160
+ db.exec("COMMIT");
171161
+ committed = true;
171162
+ return result;
171163
+ } finally {
171164
+ if (!committed) {
171165
+ try {
171166
+ db.exec("ROLLBACK");
171167
+ } catch {}
171168
+ }
171169
+ }
171170
+ }
171171
+ function countRows(db, table, sessionId) {
171172
+ const row = db.prepare(`SELECT COUNT(*) AS count FROM ${table} WHERE session_id = ?`).get(sessionId);
171173
+ return typeof row?.count === "number" ? row.count : 0;
171174
+ }
171175
+ function filterIdBlob(raw, includeMessageId) {
171176
+ if (!raw)
171177
+ return "";
171178
+ try {
171179
+ const value = JSON.parse(raw);
171180
+ if (!Array.isArray(value))
171181
+ return "";
171182
+ const filtered = value.filter((id) => typeof id === "string" && includeMessageId(id));
171183
+ return filtered.length > 0 ? JSON.stringify(filtered) : "";
171184
+ } catch {
171185
+ return "";
171186
+ }
171187
+ }
171188
+ function clampWatermark(value, maxCopiedTag) {
171189
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0)
171190
+ return 0;
171191
+ return Math.min(Math.floor(value), maxCopiedTag);
171192
+ }
171193
+ function copySessionStateForClone(db, sourceSessionId, destinationSessionId, filter) {
171194
+ return runImmediate2(db, () => {
171195
+ if (countRows(db, "compartments", destinationSessionId) > 0 || countRows(db, "tags", destinationSessionId) > 0) {
171196
+ return {
171197
+ kind: "destination-not-empty",
171198
+ compartmentsCopied: 0,
171199
+ tagsCopied: 0,
171200
+ pendingOpsCopied: 0,
171201
+ pendingMarkerMigrated: false
171202
+ };
171203
+ }
171204
+ const sourceCompartments = db.prepare(`SELECT sequence, start_message, end_message, start_message_id, end_message_id,
171205
+ title, content, p1, p2, p3, p4, importance, episode_type, legacy,
171206
+ created_at, harness
171207
+ FROM compartments WHERE session_id = ? ORDER BY sequence ASC`).all(sourceSessionId);
171208
+ const insertCompartment = db.prepare(`INSERT INTO compartments
171209
+ (session_id, sequence, start_message, end_message, start_message_id,
171210
+ end_message_id, title, content, p1, p2, p3, p4, importance,
171211
+ episode_type, legacy, created_at, harness)
171212
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
171213
+ const copiedCompartments = [];
171214
+ for (const row of sourceCompartments) {
171215
+ const startMessage = filter.resolveBoundaryOrdinal(row.start_message_id);
171216
+ const endMessage = filter.resolveBoundaryOrdinal(row.end_message_id);
171217
+ if (startMessage === undefined || endMessage === undefined)
171218
+ continue;
171219
+ insertCompartment.run(destinationSessionId, row.sequence, startMessage, endMessage, row.start_message_id, row.end_message_id, row.title, row.content, row.p1, row.p2, row.p3, row.p4, row.importance, row.episode_type, row.legacy, row.created_at, row.harness);
171220
+ copiedCompartments.push({
171221
+ sequence: row.sequence,
171222
+ startMessage,
171223
+ endMessage,
171224
+ startMessageId: row.start_message_id,
171225
+ endMessageId: row.end_message_id
171226
+ });
171227
+ }
171228
+ const sourceTags = db.prepare(`SELECT message_id, type, status, byte_size, tag_number, harness,
171229
+ entry_fingerprint, token_count, input_token_count,
171230
+ reasoning_token_count, reasoning_byte_size, drop_mode, tool_name,
171231
+ input_byte_size, caveman_depth, tool_owner_message_id
171232
+ FROM tags WHERE session_id = ? ORDER BY tag_number ASC`).all(sourceSessionId);
171233
+ const insertTag = db.prepare(`INSERT INTO tags
171234
+ (session_id, message_id, type, status, byte_size, tag_number, harness,
171235
+ entry_fingerprint, token_count, input_token_count, reasoning_token_count,
171236
+ reasoning_byte_size, drop_mode, tool_name, input_byte_size,
171237
+ caveman_depth, tool_owner_message_id)
171238
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
171239
+ const copiedTagNumbers = [];
171240
+ for (const row of sourceTags) {
171241
+ if (!filter.includeTag({
171242
+ messageId: row.message_id,
171243
+ type: row.type,
171244
+ tagNumber: row.tag_number,
171245
+ toolOwnerMessageId: row.tool_owner_message_id
171246
+ })) {
171247
+ continue;
171248
+ }
171249
+ insertTag.run(destinationSessionId, row.message_id, row.type, row.status, row.byte_size, row.tag_number, row.harness, row.entry_fingerprint, row.token_count, row.input_token_count, row.reasoning_token_count, row.reasoning_byte_size, row.drop_mode, row.tool_name, row.input_byte_size, row.caveman_depth, row.tool_owner_message_id);
171250
+ copiedTagNumbers.push(row.tag_number);
171251
+ }
171252
+ if (copiedTagNumbers.length > 0) {
171253
+ const placeholders3 = copiedTagNumbers.map(() => "?").join(", ");
171254
+ db.prepare(`INSERT INTO source_contents (tag_id, session_id, content, created_at, harness)
171255
+ SELECT tag_id, ?, content, created_at, harness
171256
+ FROM source_contents
171257
+ WHERE session_id = ? AND tag_id IN (${placeholders3})`).run(destinationSessionId, sourceSessionId, ...copiedTagNumbers);
171258
+ db.prepare(`INSERT INTO pending_ops (session_id, tag_id, operation, queued_at, harness)
171259
+ SELECT ?, tag_id, operation, queued_at, harness
171260
+ FROM pending_ops
171261
+ WHERE session_id = ? AND tag_id IN (${placeholders3})`).run(destinationSessionId, sourceSessionId, ...copiedTagNumbers);
171262
+ }
171263
+ const meta3 = db.prepare(`SELECT cleared_reasoning_through_tag, tool_reclaim_watermark,
171264
+ pi_stable_id_scheme, stripped_placeholder_ids,
171265
+ stale_reduce_stripped_ids, processed_image_stripped_ids,
171266
+ pending_pi_compaction_marker_state, last_todo_state,
171267
+ todo_synthetic_call_id, todo_synthetic_anchor_message_id,
171268
+ todo_synthetic_state_json
171269
+ FROM session_meta WHERE session_id = ?`).get(sourceSessionId);
171270
+ const maxCopiedTag = copiedTagNumbers.reduce((max, value) => Math.max(max, value), 0);
171271
+ const pendingMarker = filter.selectPendingPiMarker(meta3?.pending_pi_compaction_marker_state ?? null, copiedCompartments);
171272
+ const todoAnchor = meta3?.todo_synthetic_anchor_message_id ?? "";
171273
+ const migrateTodo = todoAnchor.length > 0 && filter.includeMessageId(todoAnchor);
171274
+ db.prepare(`INSERT INTO session_meta
171275
+ (session_id, harness, counter, cleared_reasoning_through_tag,
171276
+ tool_reclaim_watermark, pi_stable_id_scheme, stripped_placeholder_ids,
171277
+ stale_reduce_stripped_ids, processed_image_stripped_ids,
171278
+ pending_pi_compaction_marker_state, last_todo_state,
171279
+ todo_synthetic_call_id, todo_synthetic_anchor_message_id,
171280
+ todo_synthetic_state_json)
171281
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
171282
+ ON CONFLICT(session_id) DO UPDATE SET
171283
+ harness = excluded.harness,
171284
+ counter = excluded.counter,
171285
+ cleared_reasoning_through_tag = excluded.cleared_reasoning_through_tag,
171286
+ tool_reclaim_watermark = excluded.tool_reclaim_watermark,
171287
+ pi_stable_id_scheme = excluded.pi_stable_id_scheme,
171288
+ stripped_placeholder_ids = excluded.stripped_placeholder_ids,
171289
+ stale_reduce_stripped_ids = excluded.stale_reduce_stripped_ids,
171290
+ processed_image_stripped_ids = excluded.processed_image_stripped_ids,
171291
+ pending_pi_compaction_marker_state = excluded.pending_pi_compaction_marker_state,
171292
+ last_todo_state = excluded.last_todo_state,
171293
+ todo_synthetic_call_id = excluded.todo_synthetic_call_id,
171294
+ todo_synthetic_anchor_message_id = excluded.todo_synthetic_anchor_message_id,
171295
+ todo_synthetic_state_json = excluded.todo_synthetic_state_json,
171296
+ cached_m0_bytes = NULL,
171297
+ cached_m1_bytes = NULL`).run(destinationSessionId, getHarness(), maxCopiedTag, clampWatermark(meta3?.cleared_reasoning_through_tag ?? null, maxCopiedTag), clampWatermark(meta3?.tool_reclaim_watermark ?? null, maxCopiedTag), typeof meta3?.pi_stable_id_scheme === "number" && Number.isFinite(meta3.pi_stable_id_scheme) ? meta3.pi_stable_id_scheme : null, filterIdBlob(meta3?.stripped_placeholder_ids ?? null, filter.includeMessageId), filterIdBlob(meta3?.stale_reduce_stripped_ids ?? null, filter.includeMessageId), filterIdBlob(meta3?.processed_image_stripped_ids ?? null, filter.includeMessageId), pendingMarker, migrateTodo ? meta3?.last_todo_state ?? "" : "", migrateTodo ? meta3?.todo_synthetic_call_id ?? "" : "", migrateTodo ? todoAnchor : "", migrateTodo ? meta3?.todo_synthetic_state_json ?? "" : "");
171298
+ const pendingOpsRow = db.prepare("SELECT COUNT(*) AS count FROM pending_ops WHERE session_id = ?").get(destinationSessionId);
171299
+ return {
171300
+ kind: "migrated",
171301
+ compartmentsCopied: copiedCompartments.length,
171302
+ tagsCopied: copiedTagNumbers.length,
171303
+ pendingOpsCopied: typeof pendingOpsRow?.count === "number" ? pendingOpsRow.count : 0,
171304
+ pendingMarkerMigrated: pendingMarker !== null
171305
+ };
171306
+ });
171307
+ }
170922
171308
  // ../plugin/src/features/magic-context/storage-m0-mutation-log.ts
170923
171309
  var M0_MUTATION_TYPES = new Set([
170924
171310
  "compartment_delete",
@@ -171771,13 +172157,17 @@ function clearHistorianFailureState(db, sessionId) {
171771
172157
  function normalizeDetectedLimitModelKey(modelKey) {
171772
172158
  return typeof modelKey === "string" && modelKey.length > 0 ? modelKey : null;
171773
172159
  }
172160
+ function normalizeEmergencyRecoveryOrigin(value) {
172161
+ return value === "provider_overflow" || value === "proactive_model_shrink" ? value : null;
172162
+ }
171774
172163
  function getOverflowState(db, sessionId, modelKey) {
171775
- const result = db.prepare("SELECT detected_context_limit, detected_context_limit_model_key, needs_emergency_recovery FROM session_meta WHERE session_id = ?").get(sessionId);
172164
+ const result = db.prepare("SELECT detected_context_limit, detected_context_limit_model_key, needs_emergency_recovery, emergency_recovery_origin FROM session_meta WHERE session_id = ?").get(sessionId);
171776
172165
  if (!result) {
171777
172166
  return {
171778
172167
  detectedContextLimit: 0,
171779
172168
  detectedContextLimitModelKey: null,
171780
- needsEmergencyRecovery: false
172169
+ needsEmergencyRecovery: false,
172170
+ emergencyRecoveryOrigin: null
171781
172171
  };
171782
172172
  }
171783
172173
  const storedModelKey = normalizeDetectedLimitModelKey(result.detected_context_limit_model_key);
@@ -171785,19 +172175,22 @@ function getOverflowState(db, sessionId, modelKey) {
171785
172175
  const limit = typeof result.detected_context_limit === "number" && result.detected_context_limit > 0 ? result.detected_context_limit : 0;
171786
172176
  const modelMatches = limit > 0 && requestedModelKey && storedModelKey ? requestedModelKey === storedModelKey : true;
171787
172177
  const needs = typeof result.needs_emergency_recovery === "number" && result.needs_emergency_recovery > 0;
172178
+ const persistedOrigin = normalizeEmergencyRecoveryOrigin(result.emergency_recovery_origin);
172179
+ const recoveryOrigin = needs ? persistedOrigin ?? (limit > 0 ? "provider_overflow" : null) : null;
171788
172180
  return {
171789
172181
  detectedContextLimit: modelMatches ? limit : 0,
171790
172182
  detectedContextLimitModelKey: storedModelKey,
171791
- needsEmergencyRecovery: needs
172183
+ needsEmergencyRecovery: needs,
172184
+ emergencyRecoveryOrigin: recoveryOrigin
171792
172185
  };
171793
172186
  }
171794
- function recordOverflowDetected(db, sessionId, reportedLimit, modelKey) {
172187
+ function recordOverflowDetected(db, sessionId, reportedLimit, modelKey, origin = "provider_overflow") {
171795
172188
  db.transaction(() => {
171796
172189
  ensureSessionMetaRow(db, sessionId);
171797
172190
  if (typeof reportedLimit === "number" && reportedLimit > 0) {
171798
- db.prepare("UPDATE session_meta SET detected_context_limit = ?, detected_context_limit_model_key = ?, needs_emergency_recovery = 1, observed_safe_input_tokens = 0, cache_alert_sent = 0 WHERE session_id = ?").run(reportedLimit, normalizeDetectedLimitModelKey(modelKey), sessionId);
172191
+ db.prepare("UPDATE session_meta SET detected_context_limit = ?, detected_context_limit_model_key = ?, needs_emergency_recovery = 1, emergency_recovery_origin = ?, observed_safe_input_tokens = 0, cache_alert_sent = 0 WHERE session_id = ?").run(reportedLimit, normalizeDetectedLimitModelKey(modelKey), origin, sessionId);
171799
172192
  } else {
171800
- db.prepare("UPDATE session_meta SET needs_emergency_recovery = 1, observed_safe_input_tokens = 0, cache_alert_sent = 0 WHERE session_id = ?").run(sessionId);
172193
+ db.prepare("UPDATE session_meta SET needs_emergency_recovery = 1, emergency_recovery_origin = ?, observed_safe_input_tokens = 0, cache_alert_sent = 0 WHERE session_id = ?").run(origin, sessionId);
171801
172194
  }
171802
172195
  })();
171803
172196
  }
@@ -171805,9 +172198,9 @@ function clearEmergencyRecovery(db, sessionId) {
171805
172198
  db.transaction(() => {
171806
172199
  ensureSessionMetaRow(db, sessionId);
171807
172200
  try {
171808
- db.prepare("UPDATE session_meta SET needs_emergency_recovery = 0, recovery_no_eligible_head_count = 0 WHERE session_id = ?").run(sessionId);
172201
+ db.prepare("UPDATE session_meta SET needs_emergency_recovery = 0, emergency_recovery_origin = '', recovery_no_eligible_head_count = 0 WHERE session_id = ?").run(sessionId);
171809
172202
  } catch {
171810
- db.prepare("UPDATE session_meta SET needs_emergency_recovery = 0 WHERE session_id = ?").run(sessionId);
172203
+ db.prepare("UPDATE session_meta SET needs_emergency_recovery = 0, emergency_recovery_origin = '' WHERE session_id = ?").run(sessionId);
171811
172204
  }
171812
172205
  })();
171813
172206
  }
@@ -171885,6 +172278,36 @@ function parseStrippedBlob(raw) {
171885
172278
  } catch {}
171886
172279
  return [];
171887
172280
  }
172281
+ function getProcessedImageStrippedIds(db, sessionId) {
172282
+ const row = db.prepare("SELECT processed_image_stripped_ids FROM session_meta WHERE session_id = ?").get(sessionId);
172283
+ return new Set(parseStrippedBlob(row?.processed_image_stripped_ids));
172284
+ }
172285
+ function addProcessedImageStrippedIds(db, sessionId, ids) {
172286
+ const add = [...ids];
172287
+ if (add.length === 0)
172288
+ return true;
172289
+ ensureSessionMetaRow(db, sessionId);
172290
+ for (let attempt = 0;attempt < CAS_RETRY_LIMIT; attempt += 1) {
172291
+ const row = db.prepare("SELECT processed_image_stripped_ids FROM session_meta WHERE session_id = ?").get(sessionId);
172292
+ const rawStored = row ? row.processed_image_stripped_ids ?? null : null;
172293
+ const current = new Set(parseStrippedBlob(rawStored));
172294
+ let changed = false;
172295
+ for (const id of add) {
172296
+ if (!current.has(id)) {
172297
+ current.add(id);
172298
+ changed = true;
172299
+ }
172300
+ }
172301
+ if (!changed)
172302
+ return true;
172303
+ const nextBlob = JSON.stringify([...current]);
172304
+ const result = db.prepare("UPDATE session_meta SET processed_image_stripped_ids = ? WHERE session_id = ? AND processed_image_stripped_ids IS ?").run(nextBlob, sessionId, rawStored);
172305
+ if (result.changes > 0)
172306
+ return true;
172307
+ }
172308
+ sessionLog(sessionId, `processed_image_stripped_ids CAS: ${CAS_RETRY_LIMIT} retries exhausted`);
172309
+ return false;
172310
+ }
171888
172311
  function isPendingCompactionMarker(value) {
171889
172312
  return typeof value === "object" && value !== null && typeof value.ordinal === "number" && typeof value.endMessageId === "string" && typeof value.publishedAt === "number";
171890
172313
  }
@@ -172607,6 +173030,7 @@ var getTagNumbersByMessageIdStatements = new WeakMap;
172607
173030
  var deleteTagsByMessageIdStatements = new WeakMap;
172608
173031
  var getMaxTagNumberBySessionStatements = new WeakMap;
172609
173032
  var getTagNumberByMessageIdStatements = new WeakMap;
173033
+ var hasPiFallbackMessageTagStatements = new WeakMap;
172610
173034
  function getInsertTagStatement(db) {
172611
173035
  let stmt = insertTagStatements.get(db);
172612
173036
  if (!stmt) {
@@ -172750,6 +173174,18 @@ function getUpdateTagInputTokenCountStatement(db) {
172750
173174
  function updateTagTokenCount(db, sessionId, tagNumber, newTokenCount) {
172751
173175
  getUpdateTagTokenCountStatement(db).run(newTokenCount, sessionId, tagNumber);
172752
173176
  }
173177
+ function getPersistedToolTagAccounting(db, sessionId, tagNumber) {
173178
+ const row = db.prepare(`SELECT byte_size AS byteSize,
173179
+ token_count AS tokenCount,
173180
+ input_byte_size AS inputByteSize,
173181
+ input_token_count AS inputTokenCount
173182
+ FROM tags
173183
+ WHERE session_id = ? AND tag_number = ? AND type = 'tool'`).get(sessionId, tagNumber);
173184
+ if (!row || typeof row.byteSize !== "number" || row.tokenCount !== null && typeof row.tokenCount !== "number" || typeof row.inputByteSize !== "number" || row.inputTokenCount !== null && typeof row.inputTokenCount !== "number") {
173185
+ return null;
173186
+ }
173187
+ return row;
173188
+ }
172753
173189
  function getAllStatusTagTokenTotalsFlat(db, sessionId, floor = 0) {
172754
173190
  const rows = floor > 0 ? db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
172755
173191
  FROM tags
@@ -172851,6 +173287,19 @@ function updateTagDropMode(db, sessionId, tagNumber, dropMode) {
172851
173287
  function updateCavemanDepth(db, sessionId, tagNumber, depth) {
172852
173288
  db.prepare("UPDATE tags SET caveman_depth = ? WHERE session_id = ? AND tag_number = ?").run(depth, sessionId, tagNumber);
172853
173289
  }
173290
+ function hasPiFallbackMessageTags(db, sessionId) {
173291
+ let statement = hasPiFallbackMessageTagStatements.get(db);
173292
+ if (!statement) {
173293
+ statement = db.prepare(`SELECT 1
173294
+ FROM tags
173295
+ WHERE session_id = ?
173296
+ AND type = 'message'
173297
+ AND message_id LIKE 'pi-msg-%'
173298
+ LIMIT 1`);
173299
+ hasPiFallbackMessageTagStatements.set(db, statement);
173300
+ }
173301
+ return statement.get(sessionId) != null;
173302
+ }
172854
173303
  function findAdoptableFallbackTags(db, sessionId, entryFingerprint) {
172855
173304
  const rows = db.prepare(`SELECT tag_number AS tagNumber, message_id AS messageId
172856
173305
  FROM tags
@@ -173021,27 +173470,24 @@ function adoptPiFallbackToolOwnerTag(db, sessionId, tagNumber, callId, oldOwnerM
173021
173470
  }
173022
173471
  const existing = getPiFallbackToolFoldTagRowByOwner(db, sessionId, callId, newOwnerMessageId);
173023
173472
  if (existing === null) {
173024
- const result2 = db.prepare(`UPDATE tags
173473
+ const result = db.prepare(`UPDATE tags
173025
173474
  SET tool_owner_message_id = ?
173026
173475
  WHERE session_id = ?
173027
173476
  AND tag_number = ?
173028
173477
  AND type = 'tool'
173029
173478
  AND message_id = ?
173030
173479
  AND tool_owner_message_id = ?`).run(newOwnerMessageId, sessionId, tagNumber, callId, oldOwnerMessageId);
173031
- return (result2.changes ?? 0) === 1 ? { action: "rekeyed", tagNumber } : { action: "skipped" };
173480
+ return (result.changes ?? 0) === 1 ? { action: "rekeyed", tagNumber } : { action: "skipped" };
173032
173481
  }
173033
173482
  if (existing.tagNumber === tagNumber) {
173034
173483
  return { action: "skipped" };
173035
173484
  }
173036
- foldDuplicateIntoSurvivor(db, sessionId, survivor, existing);
173037
- const result = db.prepare(`UPDATE tags
173038
- SET tool_owner_message_id = ?
173039
- WHERE session_id = ?
173040
- AND tag_number = ?
173041
- AND type = 'tool'
173042
- AND message_id = ?
173043
- AND tool_owner_message_id = ?`).run(newOwnerMessageId, sessionId, tagNumber, callId, oldOwnerMessageId);
173044
- return (result.changes ?? 0) === 1 ? { action: "folded", tagNumber, deletedTagNumbers: [existing.tagNumber] } : { action: "skipped" };
173485
+ foldDuplicateIntoSurvivor(db, sessionId, existing, survivor);
173486
+ return {
173487
+ action: "folded",
173488
+ tagNumber: existing.tagNumber,
173489
+ deletedTagNumbers: [tagNumber]
173490
+ };
173045
173491
  }
173046
173492
  function adoptPiFallbackMessageTag(db, sessionId, tagNumber, oldFallbackMessageId, newRealMessageId) {
173047
173493
  const survivor = getPiFallbackFoldTagRowByNumber(db, sessionId, tagNumber);
@@ -173050,26 +173496,28 @@ function adoptPiFallbackMessageTag(db, sessionId, tagNumber, oldFallbackMessageI
173050
173496
  }
173051
173497
  const duplicates = getPiFallbackMessageFoldTagRowsByMessageId(db, sessionId, newRealMessageId).filter((row) => row.tagNumber !== tagNumber);
173052
173498
  if (duplicates.length === 0) {
173053
- const result2 = db.prepare(`UPDATE tags
173499
+ const result = db.prepare(`UPDATE tags
173054
173500
  SET message_id = ?
173055
173501
  WHERE session_id = ?
173056
173502
  AND tag_number = ?
173057
173503
  AND type = 'message'
173058
173504
  AND message_id = ?`).run(newRealMessageId, sessionId, tagNumber, oldFallbackMessageId);
173059
- return (result2.changes ?? 0) === 1 ? { action: "rekeyed", tagNumber } : { action: "skipped" };
173505
+ return (result.changes ?? 0) === 1 ? { action: "rekeyed", tagNumber } : { action: "skipped" };
173060
173506
  }
173061
- const deletedTagNumbers = [];
173062
- for (const duplicate of duplicates) {
173063
- foldDuplicateIntoSurvivor(db, sessionId, survivor, duplicate);
173507
+ const realSurvivor = duplicates[0];
173508
+ if (!realSurvivor)
173509
+ return { action: "skipped" };
173510
+ const deletedTagNumbers = [tagNumber];
173511
+ foldDuplicateIntoSurvivor(db, sessionId, realSurvivor, survivor);
173512
+ for (const duplicate of duplicates.slice(1)) {
173513
+ foldDuplicateIntoSurvivor(db, sessionId, realSurvivor, duplicate);
173064
173514
  deletedTagNumbers.push(duplicate.tagNumber);
173065
173515
  }
173066
- const result = db.prepare(`UPDATE tags
173067
- SET message_id = ?
173068
- WHERE session_id = ?
173069
- AND tag_number = ?
173070
- AND type = 'message'
173071
- AND message_id = ?`).run(newRealMessageId, sessionId, tagNumber, oldFallbackMessageId);
173072
- return (result.changes ?? 0) === 1 ? { action: "folded", tagNumber, deletedTagNumbers } : { action: "skipped" };
173516
+ return {
173517
+ action: "folded",
173518
+ tagNumber: realSurvivor.tagNumber,
173519
+ deletedTagNumbers
173520
+ };
173073
173521
  }
173074
173522
  var getOwnerScopedToolTagNumbersStatements = new WeakMap;
173075
173523
  function getMaxTagNumberBySession(db, sessionId) {
@@ -173141,10 +173589,40 @@ function getActiveTagsBySessionStatement(db) {
173141
173589
  }
173142
173590
  return stmt;
173143
173591
  }
173592
+ function getMaxDroppedTagNumberStatement(db) {
173593
+ let stmt = getMaxDroppedTagNumberStatements.get(db);
173594
+ if (!stmt) {
173595
+ stmt = db.prepare("SELECT COALESCE(MAX(tag_number), 0) AS max_tag_number FROM tags WHERE session_id = ? AND status = 'dropped'");
173596
+ getMaxDroppedTagNumberStatements.set(db, stmt);
173597
+ }
173598
+ return stmt;
173599
+ }
173144
173600
  function getActiveTagsBySession(db, sessionId) {
173145
173601
  const rows = getActiveTagsBySessionStatement(db).all(sessionId).filter(isTagRow);
173146
173602
  return rows.map(toTagEntry);
173147
173603
  }
173604
+ function getTagsForPendingOperations(db, sessionId, pendingTagNumbers, protectedTags, recentToolWindow) {
173605
+ const byNumber = new Map;
173606
+ for (const tag of getTagsByNumbers(db, sessionId, pendingTagNumbers)) {
173607
+ byNumber.set(tag.tagNumber, tag);
173608
+ }
173609
+ const addRows = (sql, limit) => {
173610
+ if (limit <= 0)
173611
+ return;
173612
+ const rows = db.prepare(sql).all(sessionId, limit).filter(isTagRow);
173613
+ for (const row of rows) {
173614
+ const tag = toTagEntry(row);
173615
+ byNumber.set(tag.tagNumber, tag);
173616
+ }
173617
+ };
173618
+ addRows(`SELECT ${TAG_SELECT_COLUMNS} FROM tags
173619
+ WHERE session_id = ? AND status = 'active'
173620
+ ORDER BY tag_number DESC, id DESC LIMIT ?`, protectedTags);
173621
+ addRows(`SELECT ${TAG_SELECT_COLUMNS} FROM tags
173622
+ WHERE session_id = ? AND type = 'tool'
173623
+ ORDER BY tag_number DESC, id DESC LIMIT ?`, recentToolWindow);
173624
+ return [...byNumber.values()].sort((left, right) => left.tagNumber - right.tagNumber);
173625
+ }
173148
173626
  function getTagsByNumbers(db, sessionId, tagNumbers) {
173149
173627
  if (tagNumbers.length === 0)
173150
173628
  return [];
@@ -173159,6 +173637,10 @@ function getTagsByNumbers(db, sessionId, tagNumbers) {
173159
173637
  const rows = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? AND tag_number IN (${placeholders4}) ORDER BY tag_number ASC, id ASC`).all(sessionId, ...tagNumbers).filter(isTagRow);
173160
173638
  return rows.map(toTagEntry);
173161
173639
  }
173640
+ function getMaxDroppedTagNumber(db, sessionId) {
173641
+ const row = getMaxDroppedTagNumberStatement(db).get(sessionId);
173642
+ return isMaxTagNumberRow(row) ? row.max_tag_number : 0;
173643
+ }
173162
173644
  var getToolTagNumberByOwnerStatements = new WeakMap;
173163
173645
  var getNullOwnerToolTagStatements = new WeakMap;
173164
173646
  var adoptNullOwnerToolTagStatements = new WeakMap;
@@ -175460,6 +175942,30 @@ class BoundedSessionMap {
175460
175942
  // ../plugin/src/hooks/magic-context/inject-compartments.ts
175461
175943
  init_logger();
175462
175944
 
175945
+ // ../plugin/src/hooks/magic-context/compartment-render-epoch.ts
175946
+ var COMPARTMENT_RENDER_EPOCH = "cre2";
175947
+ var EPOCH_COMPONENT_PREFIX = "|compartment-render:";
175948
+ function encodeCachedM0UpgradeIdentity(upgradeState, compartmentRenderEpoch = COMPARTMENT_RENDER_EPOCH) {
175949
+ if (compartmentRenderEpoch === null)
175950
+ return upgradeState;
175951
+ return `${upgradeState ?? ""}${EPOCH_COMPONENT_PREFIX}${compartmentRenderEpoch}`;
175952
+ }
175953
+ function decodeCachedM0UpgradeIdentity(value) {
175954
+ if (value === null) {
175955
+ return { upgradeState: null, compartmentRenderEpoch: null };
175956
+ }
175957
+ const componentIndex = value.lastIndexOf(EPOCH_COMPONENT_PREFIX);
175958
+ if (componentIndex < 0) {
175959
+ return { upgradeState: value, compartmentRenderEpoch: null };
175960
+ }
175961
+ const upgradeState = value.slice(0, componentIndex);
175962
+ const compartmentRenderEpoch = value.slice(componentIndex + EPOCH_COMPONENT_PREFIX.length);
175963
+ return {
175964
+ upgradeState: upgradeState.length > 0 ? upgradeState : null,
175965
+ compartmentRenderEpoch: compartmentRenderEpoch.length > 0 ? compartmentRenderEpoch : null
175966
+ };
175967
+ }
175968
+
175463
175969
  // ../plugin/src/hooks/magic-context/decay-curve.ts
175464
175970
  var H50 = 24;
175465
175971
  var D = 25;
@@ -175517,12 +176023,29 @@ function computeBudgetPressure(compartments, historyBudget) {
175517
176023
 
175518
176024
  // ../plugin/src/hooks/magic-context/decay-render.ts
175519
176025
  var DEFAULT_HISTORY_BUDGET_TOKENS = 60000;
175520
- function escapeXmlAttr2(s) {
175521
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
175522
- }
175523
176026
  function escapeXmlContent2(s) {
175524
176027
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
175525
176028
  }
176029
+ function formatDateRange(startDate, endDate) {
176030
+ if (!startDate || !endDate)
176031
+ return "";
176032
+ if (startDate === endDate)
176033
+ return startDate;
176034
+ if (startDate.slice(0, 7) === endDate.slice(0, 7))
176035
+ return `${startDate}→${endDate.slice(8)}`;
176036
+ return `${startDate}→${endDate}`;
176037
+ }
176038
+ function sanitizeCompartmentTitle(title) {
176039
+ return escapeXmlContent2(title.replace(/[\p{Cc}\p{Zl}\p{Zp}]+/gu, " "));
176040
+ }
176041
+ function compartmentHeading(c) {
176042
+ const dateRange = formatDateRange(c.startDate, c.endDate);
176043
+ const dateSegment = dateRange ? ` · ${dateRange}` : "";
176044
+ return `## ${c.startMessage}-${c.endMessage}${dateSegment} · ${sanitizeCompartmentTitle(c.title)}`;
176045
+ }
176046
+ function guardCompartmentBody(body) {
176047
+ return body.replace(/^## /gm, " ## ");
176048
+ }
175526
176049
  function isTieredRow(c) {
175527
176050
  return typeof c.p1 === "string" && c.p1.length > 0;
175528
176051
  }
@@ -175552,25 +176075,22 @@ function renderCompartmentAtTier(c, tier2) {
175552
176075
  return renderOneCompartment(c, tier2);
175553
176076
  }
175554
176077
  function renderOneCompartment(c, tier2) {
175555
- const baseAttrs = `start="${c.startMessage}" end="${c.endMessage}" title="${escapeXmlAttr2(c.title)}"`;
175556
176078
  if (tier2 >= 5)
175557
176079
  return "";
176080
+ const heading = compartmentHeading(c);
175558
176081
  if (c.legacy === 1 || !isTieredRow(c)) {
175559
176082
  const flat = (c.content ?? "").trim();
175560
176083
  if (tier2 >= 4 || flat.length === 0)
175561
- return `<compartment ${baseAttrs} />`;
175562
- return [
175563
- `<compartment ${baseAttrs}>`,
175564
- escapeXmlContent2(legacyBodyForTier(flat, tier2)),
175565
- "</compartment>"
175566
- ].join(`
175567
- `);
176084
+ return heading;
176085
+ const body2 = guardCompartmentBody(escapeXmlContent2(legacyBodyForTier(flat, tier2)));
176086
+ return `${heading}
176087
+ ${body2}`;
175568
176088
  }
175569
176089
  const body = tierBody(c, tier2);
175570
176090
  if (body.length === 0)
175571
- return `<compartment ${baseAttrs} />`;
175572
- return [`<compartment ${baseAttrs}>`, escapeXmlContent2(body), "</compartment>"].join(`
175573
- `);
176091
+ return heading;
176092
+ return `${heading}
176093
+ ${guardCompartmentBody(escapeXmlContent2(body))}`;
175574
176094
  }
175575
176095
  function computeTiers(compartments, historyBudgetTokens) {
175576
176096
  const v2Compartments = compartments.map((c, originalIndex) => ({ c, originalIndex })).filter(({ c }) => c.legacy !== 1);
@@ -175693,33 +176213,9 @@ function getVisibleMemoryIds(db, sessionId) {
175693
176213
  }
175694
176214
  }
175695
176215
  function renderMemoryBlock(memories) {
175696
- const byCategory = new Map;
175697
- for (const m of memories) {
175698
- const existing = byCategory.get(m.category);
175699
- if (existing) {
175700
- existing.push(m);
175701
- } else {
175702
- byCategory.set(m.category, [m]);
175703
- }
175704
- }
175705
- const sections = [];
175706
- for (const category of CATEGORY_PRIORITY) {
175707
- const categoryMemories = byCategory.get(category);
175708
- if (!categoryMemories || categoryMemories.length === 0) {
175709
- continue;
175710
- }
175711
- sections.push(`<${category}>`, ...categoryMemories.map((m) => `- ${escapeXmlContent(m.content)}`), `</${category}>`);
175712
- }
175713
- if (sections.length === 0) {
175714
- return null;
175715
- }
175716
- return `<project-memory>
175717
- ${sections.join(`
175718
- `)}
175719
- </project-memory>`;
176216
+ return renderMemoryBlockV2(memories) || null;
175720
176217
  }
175721
176218
  var DEFAULT_MEMORY_BUDGET_TOKENS = 8000;
175722
- var MEMORY_BLOCK_WRAPPER_TOKENS = 6;
175723
176219
  var DEFAULT_USER_PROFILE_BUDGET_TOKENS = 4000;
175724
176220
  function memoryCanonicalIdentity(memory2, workspace) {
175725
176221
  return resolveStoredPathWorkspaceIdentity(memory2.projectPath, workspace.identities, workspace.canonicalIdentityByStoredPath);
@@ -175737,11 +176233,18 @@ function memorySelectionOrder(left, right) {
175737
176233
  return left.id - right.id;
175738
176234
  }
175739
176235
  function memoryRenderOrder(left, right) {
175740
- const aPriority = MEMORY_CATEGORY_ORDER_PRIORITY[left.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
175741
- const bPriority = MEMORY_CATEGORY_ORDER_PRIORITY[right.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
175742
- const categoryDiff = aPriority - bPriority;
175743
- if (categoryDiff !== 0)
175744
- return categoryDiff;
176236
+ const leftPriority = V2_MEMORY_CATEGORIES.indexOf(left.category);
176237
+ const rightPriority = V2_MEMORY_CATEGORIES.indexOf(right.category);
176238
+ if (leftPriority >= 0 || rightPriority >= 0) {
176239
+ if (leftPriority < 0)
176240
+ return 1;
176241
+ if (rightPriority < 0)
176242
+ return -1;
176243
+ if (leftPriority !== rightPriority)
176244
+ return leftPriority - rightPriority;
176245
+ } else if (left.category !== right.category) {
176246
+ return left.category < right.category ? -1 : 1;
176247
+ }
175745
176248
  return left.id - right.id;
175746
176249
  }
175747
176250
  var maxCompartmentSeqStatements = new WeakMap;
@@ -175752,13 +176255,13 @@ var newCompartmentStatements = new WeakMap;
175752
176255
  function trimMemoriesToBudgetV2(sessionId, memories, budgetTokens, renderOptions = {}) {
175753
176256
  const selectionOrder = [...memories].sort(memorySelectionOrder);
175754
176257
  const selected = [];
175755
- let usedTokens = MEMORY_BLOCK_WRAPPER_TOKENS;
176258
+ const accounting = createMemoryBlockAccounting(renderOptions);
175756
176259
  for (const memory2 of selectionOrder) {
175757
- const memoryTokens = estimateTokens(renderMemoryLineV2(memory2, renderOptions.sourceNameByMemoryId?.get(memory2.id)));
175758
- if (usedTokens + memoryTokens > budgetTokens)
176260
+ const cost = accounting.candidateCost(memory2);
176261
+ if (accounting.usedTokens + cost > budgetTokens)
175759
176262
  continue;
176263
+ accounting.admit(memory2, cost);
175760
176264
  selected.push(memory2);
175761
- usedTokens += memoryTokens;
175762
176265
  }
175763
176266
  if (selected.length < memories.length) {
175764
176267
  sessionLog(sessionId, `v2 trimmed memories from ${memories.length} to ${selected.length} to fit injection budget of ${budgetTokens} tokens`);
@@ -175772,23 +176275,22 @@ function trimWorkspaceMemoriesToBudgetV2(sessionId, memories, budgetTokens, work
175772
176275
  }
175773
176276
  const selected = [];
175774
176277
  const selectedIds = new Set;
175775
- let usedTokens = MEMORY_BLOCK_WRAPPER_TOKENS;
175776
- const tokenCost = (memory2) => estimateTokens(renderMemoryLineV2(memory2, renderOptions.sourceNameByMemoryId?.get(memory2.id)));
176278
+ const accounting = createMemoryBlockAccounting(renderOptions);
175777
176279
  const trySelect = (memory2) => {
175778
176280
  if (selectedIds.has(memory2.id))
175779
176281
  return false;
175780
- const tokens = tokenCost(memory2);
175781
- if (usedTokens + tokens > budgetTokens)
176282
+ const cost = accounting.candidateCost(memory2);
176283
+ if (accounting.usedTokens + cost > budgetTokens)
175782
176284
  return false;
175783
176285
  selected.push(memory2);
175784
176286
  selectedIds.add(memory2.id);
175785
- usedTokens += tokens;
176287
+ accounting.admit(memory2, cost);
175786
176288
  return true;
175787
176289
  };
175788
176290
  for (const memory2 of memories.filter((candidate) => candidate.status === "permanent").sort(memorySelectionOrder)) {
175789
176291
  trySelect(memory2);
175790
176292
  }
175791
- const remainingAfterPermanent = Math.max(0, budgetTokens - usedTokens);
176293
+ const remainingAfterPermanent = Math.max(0, budgetTokens - accounting.usedTokens);
175792
176294
  const floorTokens = remainingAfterPermanent / Math.max(1, workspace.identities.length);
175793
176295
  const byIdentity = new Map;
175794
176296
  for (const memory2 of memories) {
@@ -175807,15 +176309,15 @@ function trimWorkspaceMemoriesToBudgetV2(sessionId, memories, budgetTokens, work
175807
176309
  for (const memory2 of candidates2) {
175808
176310
  if (selectedIds.has(memory2.id))
175809
176311
  continue;
175810
- const tokens = tokenCost(memory2);
175811
- if (memberTokens + tokens > floorTokens)
176312
+ const cost = accounting.candidateCost(memory2);
176313
+ if (memberTokens + cost > floorTokens)
175812
176314
  continue;
175813
- if (usedTokens + tokens > budgetTokens)
176315
+ if (accounting.usedTokens + cost > budgetTokens)
175814
176316
  continue;
175815
176317
  selected.push(memory2);
175816
176318
  selectedIds.add(memory2.id);
175817
- usedTokens += tokens;
175818
- memberTokens += tokens;
176319
+ accounting.admit(memory2, cost);
176320
+ memberTokens += cost;
175819
176321
  }
175820
176322
  }
175821
176323
  const remaining = memories.filter((memory2) => !selectedIds.has(memory2.id)).sort(memorySelectionOrder);
@@ -175839,17 +176341,55 @@ function trimUserMemoriesToBudget(memories, budgetTokens) {
175839
176341
  }
175840
176342
  return selected;
175841
176343
  }
176344
+ function createMemoryBlockAccounting(renderOptions) {
176345
+ const seenCategories = new Set;
176346
+ const categoryCost = new Map;
176347
+ return {
176348
+ usedTokens: estimateTokens(`<project-memory>
176349
+ </project-memory>`),
176350
+ candidateCost(memory2) {
176351
+ const line = renderMemoryLineV2(memory2, renderOptions.sourceNameByMemoryId?.get(memory2.id));
176352
+ let cost = estimateTokens(`${line}
176353
+ `);
176354
+ if (!seenCategories.has(memory2.category)) {
176355
+ let tags = categoryCost.get(memory2.category);
176356
+ if (tags === undefined) {
176357
+ tags = estimateTokens(`<${escapeXmlAttr(memory2.category)}>
176358
+ </${escapeXmlAttr(memory2.category)}>
176359
+ `);
176360
+ categoryCost.set(memory2.category, tags);
176361
+ }
176362
+ cost += tags;
176363
+ }
176364
+ return cost;
176365
+ },
176366
+ admit(memory2, cost) {
176367
+ this.usedTokens += cost;
176368
+ seenCategories.add(memory2.category);
176369
+ }
176370
+ };
176371
+ }
175842
176372
  function renderMemoryLineV2(memory2, sourceName) {
175843
- const sourceAttr = sourceName ? ` source="${escapeXmlAttr(sourceName)}"` : "";
175844
- return ` <memory id="${memory2.id}" category="${escapeXmlAttr(memory2.category)}"${sourceAttr} importance="${memory2.importance ?? 50}">${escapeXmlContent(memory2.content)}</memory>`;
176373
+ const source = sourceName ? ` [${escapeXmlContent(sourceName)}]` : "";
176374
+ return `#${memory2.id}${source}: ${escapeXmlContent(memory2.content)}`;
175845
176375
  }
175846
176376
  function renderMemoryBlockV2(memories, wrapper = "project-memory", renderOptions = {}) {
175847
176377
  if (memories.length === 0)
175848
176378
  return "";
176379
+ const ordered = [...memories].sort(memoryRenderOrder);
175849
176380
  const lines = [`<${wrapper}>`];
175850
- for (const memory2 of memories) {
176381
+ let openCategory;
176382
+ for (const memory2 of ordered) {
176383
+ if (memory2.category !== openCategory) {
176384
+ if (openCategory !== undefined)
176385
+ lines.push(`</${escapeXmlAttr(openCategory)}>`);
176386
+ openCategory = memory2.category;
176387
+ lines.push(`<${escapeXmlAttr(openCategory)}>`);
176388
+ }
175851
176389
  lines.push(renderMemoryLineV2(memory2, renderOptions.sourceNameByMemoryId?.get(memory2.id)));
175852
176390
  }
176391
+ if (openCategory !== undefined)
176392
+ lines.push(`</${escapeXmlAttr(openCategory)}>`);
175853
176393
  lines.push(`</${wrapper}>`);
175854
176394
  return lines.join(`
175855
176395
  `);
@@ -176163,6 +176703,8 @@ var STATUS_COLOR = {
176163
176703
  cancelled: "error"
176164
176704
  };
176165
176705
  var snapshotsBySession = new Map;
176706
+ var MAX_TODOWRITE_RENDER_CACHE_ENTRIES = 50;
176707
+ var cachedTodowriteTodosByToolCallId = new Map;
176166
176708
  function isTodoStatus2(value) {
176167
176709
  return TODO_STATUSES.includes(value);
176168
176710
  }
@@ -176172,6 +176714,27 @@ function isTodoPriority2(value) {
176172
176714
  function cloneTodos(todos) {
176173
176715
  return todos.map((todo) => ({ ...todo }));
176174
176716
  }
176717
+ function rememberTodowriteToolCallTodos(toolCallId, todos) {
176718
+ if (!toolCallId)
176719
+ return;
176720
+ cachedTodowriteTodosByToolCallId.delete(toolCallId);
176721
+ cachedTodowriteTodosByToolCallId.set(toolCallId, cloneTodos(todos));
176722
+ while (cachedTodowriteTodosByToolCallId.size > MAX_TODOWRITE_RENDER_CACHE_ENTRIES) {
176723
+ const oldestKey = cachedTodowriteTodosByToolCallId.keys().next().value;
176724
+ if (typeof oldestKey !== "string")
176725
+ break;
176726
+ cachedTodowriteTodosByToolCallId.delete(oldestKey);
176727
+ }
176728
+ }
176729
+ function getCachedTodowriteToolCallTodos(toolCallId) {
176730
+ if (!toolCallId)
176731
+ return null;
176732
+ const todos = cachedTodowriteTodosByToolCallId.get(toolCallId);
176733
+ return todos ? cloneTodos(todos) : null;
176734
+ }
176735
+ function clearTodowriteToolCallTodos() {
176736
+ cachedTodowriteTodosByToolCallId.clear();
176737
+ }
176175
176738
  function parseTodos(input) {
176176
176739
  if (!Array.isArray(input))
176177
176740
  return null;
@@ -176308,25 +176871,27 @@ function capTodoRows(rows) {
176308
176871
  hiddenCount: rows.length - visibleCount
176309
176872
  };
176310
176873
  }
176311
- function renderTodowriteCall(args, theme) {
176312
- const todos = parseTodos(args.todos) ?? [];
176874
+ function renderTodowriteCall(args, theme, context) {
176875
+ const todos = getCachedTodowriteToolCallTodos(context?.toolCallId) ?? parseTodos(args.todos) ?? [];
176313
176876
  const active = activeTitleCount(todos);
176314
176877
  const activeColor = active > 0 ? "warning" : "success";
176315
176878
  const line = `${theme.fg("toolTitle", theme.bold("Todos"))} ${theme.fg("muted", "—")} ${theme.fg(activeColor, `${active} active`)}`;
176316
176879
  return lineComponent((width) => [truncateToWidth(line, width, "…")]);
176317
176880
  }
176318
- function renderTodowriteResult(result, theme) {
176881
+ function renderTodowriteResult(result, theme, context) {
176319
176882
  const detailsTodos = result.details?.todos;
176320
176883
  let todos = parseTodos(detailsTodos);
176321
- if (todos === null && Array.isArray(result.content)) {
176884
+ if ((todos === null || todos.length === 0) && Array.isArray(result.content)) {
176322
176885
  const text = result.content.find((part) => part !== null && typeof part === "object" && part.type === "text" && typeof part.text === "string")?.text;
176323
176886
  if (text) {
176324
176887
  try {
176325
- todos = parseTodos(JSON.parse(text));
176888
+ const parsed = parseTodos(JSON.parse(text));
176889
+ if (parsed !== null)
176890
+ todos = parsed;
176326
176891
  } catch {}
176327
176892
  }
176328
176893
  }
176329
- const renderedTodos = todos ?? [];
176894
+ const renderedTodos = todos !== null && todos.length > 0 ? todos : getCachedTodowriteToolCallTodos(context?.toolCallId) ?? todos ?? [];
176330
176895
  return lineComponent((width) => {
176331
176896
  if (renderedTodos.length === 0)
176332
176897
  return [theme.fg("dim", "No todos")];
@@ -176527,6 +177092,7 @@ function registerTodoOverlay(pi, deps) {
176527
177092
  overlay.hideCompletedTasksFromPreviousTurn();
176528
177093
  });
176529
177094
  pi.on("session_shutdown", async (_event, ctx) => {
177095
+ clearTodowriteToolCallTodos();
176530
177096
  const sessionId = getSessionId(ctx);
176531
177097
  if (!sessionId)
176532
177098
  return;
@@ -176534,6 +177100,7 @@ function registerTodoOverlay(pi, deps) {
176534
177100
  overlay.disposeSession(sessionId);
176535
177101
  });
176536
177102
  pi.on("session_before_switch", async (_event, ctx) => {
177103
+ clearTodowriteToolCallTodos();
176537
177104
  const sessionId = getSessionId(ctx);
176538
177105
  if (!sessionId)
176539
177106
  return;
@@ -176550,11 +177117,13 @@ function registerTodoStateLifecycle(pi, deps) {
176550
177117
  seedTodoSnapshotFromStateJson(sessionId, deps.readLastTodoState(sessionId));
176551
177118
  });
176552
177119
  pi.on("session_shutdown", async (_event, ctx) => {
177120
+ clearTodowriteToolCallTodos();
176553
177121
  const sessionId = getSessionId(ctx);
176554
177122
  if (sessionId)
176555
177123
  clearTodoSnapshot(sessionId);
176556
177124
  });
176557
177125
  pi.on("session_before_switch", async (_event, ctx) => {
177126
+ clearTodowriteToolCallTodos();
176558
177127
  const sessionId = getSessionId(ctx);
176559
177128
  if (sessionId)
176560
177129
  clearTodoSnapshot(sessionId);
@@ -176605,11 +177174,11 @@ function createTodowriteTool() {
176605
177174
  }
176606
177175
  };
176607
177176
  },
176608
- renderCall(args, theme) {
176609
- return renderTodowriteCall(args, theme);
177177
+ renderCall(args, theme, context) {
177178
+ return renderTodowriteCall(args, theme, context);
176610
177179
  },
176611
- renderResult(result, _opts, theme) {
176612
- return renderTodowriteResult(result, theme);
177180
+ renderResult(result, _opts, theme, context) {
177181
+ return renderTodowriteResult(result, theme, context);
176613
177182
  }
176614
177183
  };
176615
177184
  }