@cortexkit/pi-magic-context 0.21.6 → 0.21.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8163,10 +8163,10 @@ var require_stringify = __commonJS((exports, module) => {
8163
8163
  replacer = null;
8164
8164
  indent = EMPTY;
8165
8165
  };
8166
- var join9 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
8166
+ var join10 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
8167
8167
  var join_content = (inside, value, gap) => {
8168
8168
  const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
8169
- return join9(comment, inside, gap);
8169
+ return join10(comment, inside, gap);
8170
8170
  };
8171
8171
  var stringify_string = (holder, key, value) => {
8172
8172
  const raw = get_raw_string_literal(holder, key);
@@ -8188,13 +8188,13 @@ var require_stringify = __commonJS((exports, module) => {
8188
8188
  if (i !== 0) {
8189
8189
  inside += COMMA;
8190
8190
  }
8191
- const before = join9(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
8191
+ const before = join10(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
8192
8192
  inside += before || LF + deeper_gap;
8193
8193
  inside += stringify(i, value, deeper_gap) || STR_NULL;
8194
8194
  inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
8195
8195
  after_comma = process_comments(value, AFTER(i), deeper_gap);
8196
8196
  }
8197
- inside += join9(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
8197
+ inside += join10(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
8198
8198
  return BRACKET_OPEN + join_content(inside, value, gap) + BRACKET_CLOSE;
8199
8199
  };
8200
8200
  var object_stringify = (value, gap) => {
@@ -8215,13 +8215,13 @@ var require_stringify = __commonJS((exports, module) => {
8215
8215
  inside += COMMA;
8216
8216
  }
8217
8217
  first = false;
8218
- const before = join9(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
8218
+ const before = join10(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
8219
8219
  inside += before || LF + deeper_gap;
8220
8220
  inside += quote(key) + process_comments(value, AFTER_PROP(key), deeper_gap) + COLON + process_comments(value, AFTER_COLON(key), deeper_gap) + SPACE + sv + process_comments(value, AFTER_VALUE(key), deeper_gap);
8221
8221
  after_comma = process_comments(value, AFTER(key), deeper_gap);
8222
8222
  };
8223
8223
  keys.forEach(iteratee);
8224
- inside += join9(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
8224
+ inside += join10(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
8225
8225
  return CURLY_BRACKET_OPEN + join_content(inside, value, gap) + CURLY_BRACKET_CLOSE;
8226
8226
  };
8227
8227
  function stringify(key, holder, gap) {
@@ -8343,7 +8343,57 @@ var init_rpc_notifications = __esm(() => {
8343
8343
 
8344
8344
  // src/index.ts
8345
8345
  import { createRequire as createRequire4 } from "node:module";
8346
- import { join as join19 } from "node:path";
8346
+ import { join as join20 } from "node:path";
8347
+
8348
+ // ../plugin/src/config/agent-disable.ts
8349
+ function isDreamerRunnable(config) {
8350
+ return !!config.dreamer && config.dreamer.disable !== true;
8351
+ }
8352
+ function clonePlainObject(value) {
8353
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
8354
+ return;
8355
+ }
8356
+ return { ...value };
8357
+ }
8358
+ function migrateLegacyEnabledForAgent(args) {
8359
+ const agent = clonePlainObject(args.patched[args.agentName]);
8360
+ if (!agent || !("enabled" in agent))
8361
+ return;
8362
+ const enabled = agent.enabled;
8363
+ const disable = agent.disable;
8364
+ delete agent.enabled;
8365
+ if (args.agentName === "historian") {
8366
+ args.warnings.push('Removed invalid "historian.enabled" in-memory (run doctor to persist).');
8367
+ args.patched.historian = agent;
8368
+ return;
8369
+ }
8370
+ if (args.agentName === "dreamer") {
8371
+ if (disable !== true && enabled === false) {
8372
+ agent.disable = true;
8373
+ args.warnings.push('Migrated "dreamer.enabled=false" → "dreamer.disable=true" in-memory (run doctor to persist). This now also disables manual /ctx-dream; for manual-only remove disable and set schedule="".');
8374
+ }
8375
+ args.patched.dreamer = agent;
8376
+ return;
8377
+ }
8378
+ if (disable !== true && enabled === false) {
8379
+ agent.disable = true;
8380
+ args.warnings.push('Migrated "sidekick.enabled=false" → "sidekick.disable=true" in-memory (run doctor to persist).');
8381
+ }
8382
+ args.patched.sidekick = agent;
8383
+ }
8384
+ function migrateLegacyAgentEnabledInMemory(rawConfig, warnings) {
8385
+ const shouldPatch = ["dreamer", "sidekick", "historian"].some((key) => {
8386
+ const agent = rawConfig[key];
8387
+ return typeof agent === "object" && agent !== null && !Array.isArray(agent) && "enabled" in agent;
8388
+ });
8389
+ if (!shouldPatch)
8390
+ return rawConfig;
8391
+ const patched = { ...rawConfig };
8392
+ migrateLegacyEnabledForAgent({ patched, agentName: "dreamer", warnings });
8393
+ migrateLegacyEnabledForAgent({ patched, agentName: "sidekick", warnings });
8394
+ migrateLegacyEnabledForAgent({ patched, agentName: "historian", warnings });
8395
+ return patched;
8396
+ }
8347
8397
 
8348
8398
  // ../plugin/src/features/magic-context/memory/project-identity.ts
8349
8399
  import { execSync } from "node:child_process";
@@ -141830,6 +141880,40 @@ function parseReportedLimit(message) {
141830
141880
  }
141831
141881
  return;
141832
141882
  }
141883
+ // ../plugin/src/features/magic-context/compartment-lease.ts
141884
+ var COMPARTMENT_LEASE_TTL_MS = 5 * 60 * 1000;
141885
+ var COMPARTMENT_LEASE_RENEWAL_MS = 60 * 1000;
141886
+ function acquireCompartmentLease(db, sessionId, holderId) {
141887
+ const acquiredAt = Date.now();
141888
+ const expiresAt = acquiredAt + COMPARTMENT_LEASE_TTL_MS;
141889
+ const result = db.prepare(`INSERT INTO compartment_state_lease (session_id, holder_id, acquired_at, expires_at)
141890
+ VALUES (?, ?, ?, ?)
141891
+ ON CONFLICT(session_id) DO UPDATE SET
141892
+ holder_id = excluded.holder_id,
141893
+ acquired_at = excluded.acquired_at,
141894
+ expires_at = excluded.expires_at
141895
+ WHERE compartment_state_lease.holder_id = excluded.holder_id
141896
+ OR compartment_state_lease.expires_at <= ?`).run(sessionId, holderId, acquiredAt, expiresAt, acquiredAt);
141897
+ if (result.changes !== 1) {
141898
+ return null;
141899
+ }
141900
+ return { sessionId, holderId, acquiredAt, expiresAt };
141901
+ }
141902
+ function renewCompartmentLease(db, sessionId, holderId) {
141903
+ const now = Date.now();
141904
+ const expiresAt = now + COMPARTMENT_LEASE_TTL_MS;
141905
+ const result = db.prepare(`UPDATE compartment_state_lease
141906
+ SET expires_at = ?, acquired_at = ?
141907
+ WHERE session_id = ? AND holder_id = ? AND expires_at > ?`).run(expiresAt, now, sessionId, holderId, now);
141908
+ return result.changes === 1;
141909
+ }
141910
+ function releaseCompartmentLease(db, sessionId, holderId) {
141911
+ db.prepare("DELETE FROM compartment_state_lease WHERE session_id = ? AND holder_id = ?").run(sessionId, holderId);
141912
+ }
141913
+ function isCompartmentLeaseHeld(db, sessionId, holderId) {
141914
+ const row = db.prepare("SELECT 1 FROM compartment_state_lease WHERE session_id = ? AND holder_id = ? AND expires_at > ?").get(sessionId, holderId, Date.now());
141915
+ return row != null;
141916
+ }
141833
141917
 
141834
141918
  // ../plugin/src/features/magic-context/compartment-storage.ts
141835
141919
  var insertCompartmentStatements = new WeakMap;
@@ -141940,6 +142024,38 @@ function replaceAllCompartmentState(db, sessionId, compartments, facts) {
141940
142024
  db.prepare("UPDATE session_meta SET memory_block_cache = '', memory_block_ids = '' WHERE session_id = ?").run(sessionId);
141941
142025
  })();
141942
142026
  }
142027
+ function replaceAllCompartmentStateAndBumpDepth(db, holderId, sessionId, compartments, facts, depthStartOrdinal, depthEndOrdinal) {
142028
+ const now = Date.now();
142029
+ db.exec("BEGIN IMMEDIATE");
142030
+ let finished = false;
142031
+ try {
142032
+ if (!isCompartmentLeaseHeld(db, sessionId, holderId)) {
142033
+ db.exec("ROLLBACK");
142034
+ finished = true;
142035
+ return false;
142036
+ }
142037
+ db.prepare("DELETE FROM compartments WHERE session_id = ?").run(sessionId);
142038
+ db.prepare("DELETE FROM session_facts WHERE session_id = ?").run(sessionId);
142039
+ insertCompartmentRows(db, sessionId, compartments, now);
142040
+ insertFactRows(db, sessionId, facts, now);
142041
+ db.prepare("UPDATE session_meta SET memory_block_cache = '', memory_block_ids = '' WHERE session_id = ?").run(sessionId);
142042
+ if (depthEndOrdinal >= depthStartOrdinal) {
142043
+ const stmt = getIncrementDepthStatement(db);
142044
+ for (let ordinal = depthStartOrdinal;ordinal <= depthEndOrdinal; ordinal += 1) {
142045
+ stmt.run(sessionId, ordinal, getHarness());
142046
+ }
142047
+ }
142048
+ db.exec("COMMIT");
142049
+ finished = true;
142050
+ return true;
142051
+ } finally {
142052
+ if (!finished) {
142053
+ try {
142054
+ db.exec("ROLLBACK");
142055
+ } catch {}
142056
+ }
142057
+ }
142058
+ }
141943
142059
  function buildCompartmentBlock(compartments, facts, memoryBlock, dateRanges) {
141944
142060
  const lines = [];
141945
142061
  if (memoryBlock) {
@@ -142007,12 +142123,37 @@ function getRecompStaging(db, sessionId) {
142007
142123
  lastEndMessage: lastEnd
142008
142124
  };
142009
142125
  }
142010
- function promoteRecompStaging(db, sessionId) {
142126
+ function promoteRecompStaging(db, sessionId, holderId) {
142011
142127
  const now = Date.now();
142012
- return db.transaction(() => {
142128
+ if (!holderId) {
142129
+ return db.transaction(() => {
142130
+ const staging = getRecompStaging(db, sessionId);
142131
+ if (!staging || staging.compartments.length === 0)
142132
+ return null;
142133
+ db.prepare("DELETE FROM compartments WHERE session_id = ?").run(sessionId);
142134
+ db.prepare("DELETE FROM session_facts WHERE session_id = ?").run(sessionId);
142135
+ insertCompartmentRows(db, sessionId, staging.compartments, now);
142136
+ insertFactRows(db, sessionId, staging.facts, now);
142137
+ db.prepare("DELETE FROM recomp_compartments WHERE session_id = ?").run(sessionId);
142138
+ db.prepare("DELETE FROM recomp_facts WHERE session_id = ?").run(sessionId);
142139
+ db.prepare("UPDATE session_meta SET memory_block_cache = '', memory_block_ids = '' WHERE session_id = ?").run(sessionId);
142140
+ return { compartments: staging.compartments, facts: staging.facts };
142141
+ })();
142142
+ }
142143
+ db.exec("BEGIN IMMEDIATE");
142144
+ let finished = false;
142145
+ try {
142146
+ if (!isCompartmentLeaseHeld(db, sessionId, holderId)) {
142147
+ db.exec("ROLLBACK");
142148
+ finished = true;
142149
+ return null;
142150
+ }
142013
142151
  const staging = getRecompStaging(db, sessionId);
142014
- if (!staging || staging.compartments.length === 0)
142152
+ if (!staging || staging.compartments.length === 0) {
142153
+ db.exec("ROLLBACK");
142154
+ finished = true;
142015
142155
  return null;
142156
+ }
142016
142157
  db.prepare("DELETE FROM compartments WHERE session_id = ?").run(sessionId);
142017
142158
  db.prepare("DELETE FROM session_facts WHERE session_id = ?").run(sessionId);
142018
142159
  insertCompartmentRows(db, sessionId, staging.compartments, now);
@@ -142020,8 +142161,16 @@ function promoteRecompStaging(db, sessionId) {
142020
142161
  db.prepare("DELETE FROM recomp_compartments WHERE session_id = ?").run(sessionId);
142021
142162
  db.prepare("DELETE FROM recomp_facts WHERE session_id = ?").run(sessionId);
142022
142163
  db.prepare("UPDATE session_meta SET memory_block_cache = '', memory_block_ids = '' WHERE session_id = ?").run(sessionId);
142164
+ db.exec("COMMIT");
142165
+ finished = true;
142023
142166
  return { compartments: staging.compartments, facts: staging.facts };
142024
- })();
142167
+ } finally {
142168
+ if (!finished) {
142169
+ try {
142170
+ db.exec("ROLLBACK");
142171
+ } catch {}
142172
+ }
142173
+ }
142025
142174
  }
142026
142175
  function invalidateAllMemoryBlockCaches(db) {
142027
142176
  try {
@@ -142669,6 +142818,65 @@ var MIGRATIONS = [
142669
142818
  db.exec("ALTER TABLE session_meta ADD COLUMN pending_pi_compaction_marker_state TEXT");
142670
142819
  }
142671
142820
  }
142821
+ },
142822
+ {
142823
+ version: 19,
142824
+ description: "Add compartment state lease table",
142825
+ up: (db) => {
142826
+ db.exec(`
142827
+ CREATE TABLE IF NOT EXISTS compartment_state_lease (
142828
+ session_id TEXT PRIMARY KEY NOT NULL,
142829
+ holder_id TEXT NOT NULL,
142830
+ acquired_at INTEGER NOT NULL,
142831
+ expires_at INTEGER NOT NULL
142832
+ );
142833
+ CREATE INDEX IF NOT EXISTS idx_compartment_state_lease_expires
142834
+ ON compartment_state_lease(expires_at);
142835
+ `);
142836
+ }
142837
+ },
142838
+ {
142839
+ version: 20,
142840
+ description: "Add subagent invocation token accounting",
142841
+ up: (db) => {
142842
+ db.exec(`
142843
+ CREATE TABLE IF NOT EXISTS subagent_invocations (
142844
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
142845
+ session_id TEXT NOT NULL,
142846
+ harness TEXT NOT NULL,
142847
+ subagent TEXT NOT NULL,
142848
+ task TEXT,
142849
+ provider_id TEXT,
142850
+ model_id TEXT,
142851
+ started_at INTEGER NOT NULL,
142852
+ ended_at INTEGER,
142853
+ status TEXT NOT NULL,
142854
+ input_tokens INTEGER NOT NULL DEFAULT 0,
142855
+ output_tokens INTEGER NOT NULL DEFAULT 0,
142856
+ cache_read_tokens INTEGER NOT NULL DEFAULT 0,
142857
+ cache_write_tokens INTEGER NOT NULL DEFAULT 0,
142858
+ error TEXT,
142859
+ parent_invocation_id INTEGER
142860
+ );
142861
+ CREATE INDEX IF NOT EXISTS idx_sai_session_started
142862
+ ON subagent_invocations(session_id, started_at DESC);
142863
+ CREATE INDEX IF NOT EXISTS idx_sai_subagent
142864
+ ON subagent_invocations(subagent, started_at DESC);
142865
+ `);
142866
+ }
142867
+ },
142868
+ {
142869
+ version: 21,
142870
+ description: "Add session lifetime work metrics",
142871
+ up: (db) => {
142872
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
142873
+ if (!cols.some((c) => c.name === "new_work_tokens")) {
142874
+ db.exec("ALTER TABLE session_meta ADD COLUMN new_work_tokens INTEGER NOT NULL DEFAULT 0");
142875
+ }
142876
+ if (!cols.some((c) => c.name === "total_input_tokens")) {
142877
+ db.exec("ALTER TABLE session_meta ADD COLUMN total_input_tokens INTEGER NOT NULL DEFAULT 0");
142878
+ }
142879
+ }
142672
142880
  }
142673
142881
  ];
142674
142882
  function ensureMigrationsTable(db) {
@@ -143124,6 +143332,15 @@ function initializeDatabase(db) {
143124
143332
  );
143125
143333
  CREATE INDEX IF NOT EXISTS idx_compartments_session ON compartments(session_id);
143126
143334
 
143335
+ CREATE TABLE IF NOT EXISTS compartment_state_lease (
143336
+ session_id TEXT PRIMARY KEY NOT NULL,
143337
+ holder_id TEXT NOT NULL,
143338
+ acquired_at INTEGER NOT NULL,
143339
+ expires_at INTEGER NOT NULL
143340
+ );
143341
+ CREATE INDEX IF NOT EXISTS idx_compartment_state_lease_expires
143342
+ ON compartment_state_lease(expires_at);
143343
+
143127
143344
  CREATE TABLE IF NOT EXISTS compression_depth (
143128
143345
  session_id TEXT NOT NULL,
143129
143346
  message_ordinal INTEGER NOT NULL,
@@ -143320,12 +143537,37 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143320
143537
  -- is a valid JSON blob written via setPendingPiCompactionMarkerState.
143321
143538
  -- Excluded from healNullTextColumns.
143322
143539
  pending_pi_compaction_marker_state TEXT,
143540
+ new_work_tokens INTEGER NOT NULL DEFAULT 0,
143541
+ total_input_tokens INTEGER NOT NULL DEFAULT 0,
143323
143542
  -- deferred_execute_state: intentionally NULLABLE without a default.
143324
143543
  -- Absence is SQL NULL; presence is a JSON blob written via
143325
143544
  -- setDeferredExecutePendingIfAbsent. Excluded from healNullTextColumns.
143326
143545
  deferred_execute_state TEXT
143327
143546
  );
143328
143547
 
143548
+ CREATE TABLE IF NOT EXISTS subagent_invocations (
143549
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
143550
+ session_id TEXT NOT NULL,
143551
+ harness TEXT NOT NULL,
143552
+ subagent TEXT NOT NULL,
143553
+ task TEXT,
143554
+ provider_id TEXT,
143555
+ model_id TEXT,
143556
+ started_at INTEGER NOT NULL,
143557
+ ended_at INTEGER,
143558
+ status TEXT NOT NULL,
143559
+ input_tokens INTEGER NOT NULL DEFAULT 0,
143560
+ output_tokens INTEGER NOT NULL DEFAULT 0,
143561
+ cache_read_tokens INTEGER NOT NULL DEFAULT 0,
143562
+ cache_write_tokens INTEGER NOT NULL DEFAULT 0,
143563
+ error TEXT,
143564
+ parent_invocation_id INTEGER
143565
+ );
143566
+ CREATE INDEX IF NOT EXISTS idx_sai_session_started
143567
+ ON subagent_invocations(session_id, started_at DESC);
143568
+ CREATE INDEX IF NOT EXISTS idx_sai_subagent
143569
+ ON subagent_invocations(subagent, started_at DESC);
143570
+
143329
143571
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
143330
143572
  CREATE INDEX IF NOT EXISTS idx_tags_session_message_id ON tags(session_id, message_id);
143331
143573
  CREATE INDEX IF NOT EXISTS idx_pending_ops_session ON pending_ops(session_id);
@@ -143417,6 +143659,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143417
143659
  ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
143418
143660
  ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
143419
143661
  ensureColumn(db, "session_meta", "pending_pi_compaction_marker_state", "TEXT");
143662
+ ensureColumn(db, "session_meta", "new_work_tokens", "INTEGER NOT NULL DEFAULT 0");
143663
+ ensureColumn(db, "session_meta", "total_input_tokens", "INTEGER NOT NULL DEFAULT 0");
143420
143664
  ensureColumn(db, "session_meta", "deferred_execute_state", "TEXT");
143421
143665
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
143422
143666
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -143480,7 +143724,9 @@ function healNullIntegerColumns(db) {
143480
143724
  ["tool_call_tokens", 0],
143481
143725
  ["note_nudge_trigger_pending", 0],
143482
143726
  ["observed_safe_input_tokens", 0],
143483
- ["cache_alert_sent", 0]
143727
+ ["cache_alert_sent", 0],
143728
+ ["new_work_tokens", 0],
143729
+ ["total_input_tokens", 0]
143484
143730
  ];
143485
143731
  for (const [column, fallback] of columns) {
143486
143732
  try {
@@ -144046,6 +144292,7 @@ function getPendingPiCompactionMarkerState(db, sessionId) {
144046
144292
  return parsed;
144047
144293
  }
144048
144294
  } catch {}
144295
+ db.prepare("UPDATE session_meta SET pending_pi_compaction_marker_state = NULL WHERE session_id = ? AND pending_pi_compaction_marker_state = ?").run(sessionId, raw);
144049
144296
  return null;
144050
144297
  }
144051
144298
  function setPendingPiCompactionMarkerState(db, sessionId, state) {
@@ -144089,6 +144336,12 @@ function clearDeferredExecutePendingIfMatches(db, sessionId, expected) {
144089
144336
  WHERE session_id = ? AND deferred_execute_state = ?`).run(sessionId, expectedBlob);
144090
144337
  return result.changes > 0;
144091
144338
  }
144339
+ function setSessionWorkMetrics(db, sessionId, newWorkTokens, totalInputTokens) {
144340
+ ensureSessionMetaRow(db, sessionId);
144341
+ db.prepare(`UPDATE session_meta
144342
+ SET new_work_tokens = ?, total_input_tokens = ?
144343
+ WHERE session_id = ?`).run(Math.max(0, Math.floor(newWorkTokens)), Math.max(0, Math.floor(totalInputTokens)), sessionId);
144344
+ }
144092
144345
  // ../plugin/src/features/magic-context/resolve-subagent-fallback.ts
144093
144346
  init_logger();
144094
144347
  await init_read_session_db();
@@ -144404,6 +144657,19 @@ function getSourceContents(db, sessionId, tagIds) {
144404
144657
  }
144405
144658
  return sources;
144406
144659
  }
144660
+ // ../plugin/src/features/magic-context/storage-subagent-invocations.ts
144661
+ function clampToken(value) {
144662
+ return Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
144663
+ }
144664
+ function recordSubagentInvocation(db, input) {
144665
+ const result = db.prepare(`INSERT INTO subagent_invocations (
144666
+ session_id, harness, subagent, task, provider_id, model_id,
144667
+ started_at, ended_at, status,
144668
+ input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,
144669
+ error, parent_invocation_id
144670
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.sessionId, input.harness, input.subagent, input.task ?? null, input.providerId ?? null, input.modelId ?? null, input.startedAt, input.endedAt, input.status, clampToken(input.inputTokens), clampToken(input.outputTokens), clampToken(input.cacheReadTokens), clampToken(input.cacheWriteTokens), input.error ?? null, input.parentInvocationId ?? null);
144671
+ return Number(result.lastInsertRowid);
144672
+ }
144407
144673
  // ../plugin/src/features/magic-context/storage-tags.ts
144408
144674
  var insertTagStatements = new WeakMap;
144409
144675
  var updateTagStatusStatements = new WeakMap;
@@ -145266,10 +145532,98 @@ function isTodoItem(value) {
145266
145532
  return typeof todo.content === "string" && typeof todo.status === "string" && (todo.priority === undefined || typeof todo.priority === "string");
145267
145533
  }
145268
145534
 
145535
+ // ../plugin/src/shared/announcement.ts
145536
+ init_data_path();
145537
+ import * as fs2 from "node:fs";
145538
+ import * as path5 from "node:path";
145539
+ var ANNOUNCEMENT_VERSION = "0.21.7";
145540
+ var ANNOUNCEMENT_FEATURES = [
145541
+ "Pi parity sweep: 44 audit findings fixed, including a critical SHIP-BLOCKER where /ctx-flush did not drain the pending Pi compaction queue.",
145542
+ "Pi historian recovery fix: empty/no-op historian returns now clear emergency recovery so sessions cannot loop forever at 95%.",
145543
+ "trimPiMessagesToBoundary now sweeps non-contiguous tool-result orphans, fixing provider 400s after compaction in long Pi sessions.",
145544
+ "Hidden subagent tool isolation: historian, dreamer, and sidekick can no longer spawn subagents or run unsafe tools.",
145545
+ "TUI sidebar and /ctx-status header now show execute threshold inline: '47.5% / 65%' on the left, '475K / 1.0M' on the right.",
145546
+ "doctor --issue now caps GitHub issue bodies at ~60KB with a dedicated 'Recent errors' section so reports stay submittable."
145547
+ ];
145548
+ var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU";
145549
+ var STATE_FILENAME = "last_announced_version";
145550
+ function getStateFilePath() {
145551
+ return path5.join(getMagicContextStorageDir(), STATE_FILENAME);
145552
+ }
145553
+ function readLastAnnouncedVersion() {
145554
+ try {
145555
+ const file = getStateFilePath();
145556
+ if (!fs2.existsSync(file))
145557
+ return "";
145558
+ return fs2.readFileSync(file, "utf-8").trim();
145559
+ } catch {
145560
+ return "";
145561
+ }
145562
+ }
145563
+ function markAnnouncementSeen(version) {
145564
+ if (!version)
145565
+ return;
145566
+ try {
145567
+ const dir = getMagicContextStorageDir();
145568
+ fs2.mkdirSync(dir, { recursive: true });
145569
+ fs2.writeFileSync(getStateFilePath(), version);
145570
+ } catch {}
145571
+ }
145572
+ function shouldShowAnnouncement() {
145573
+ if (!ANNOUNCEMENT_VERSION || ANNOUNCEMENT_FEATURES.length === 0)
145574
+ return false;
145575
+ return readLastAnnouncedVersion() !== ANNOUNCEMENT_VERSION;
145576
+ }
145577
+
145269
145578
  // src/index.ts
145270
145579
  init_data_path();
145271
145580
  init_logger();
145272
145581
 
145582
+ // ../plugin/src/shared/resolve-fallbacks.ts
145583
+ function resolveFallbackChain(agentName, userFallbacks) {
145584
+ const userList = normalizeUserFallbacks(userFallbacks);
145585
+ if (userList.length > 0) {
145586
+ return dedupe(userList.filter(isValidModelSpec));
145587
+ }
145588
+ const builtin = getAgentFallbackModels(agentName);
145589
+ if (!builtin || builtin.length === 0)
145590
+ return [];
145591
+ return dedupe(builtin.filter(isValidModelSpec));
145592
+ }
145593
+ function normalizeUserFallbacks(userFallbacks) {
145594
+ if (!userFallbacks)
145595
+ return [];
145596
+ if (typeof userFallbacks === "string") {
145597
+ const trimmed = userFallbacks.trim();
145598
+ return trimmed ? [trimmed] : [];
145599
+ }
145600
+ return userFallbacks.map((s) => s.trim()).filter((s) => s.length > 0);
145601
+ }
145602
+ function isValidModelSpec(spec) {
145603
+ const slash = spec.indexOf("/");
145604
+ return slash > 0 && slash < spec.length - 1;
145605
+ }
145606
+ function dedupe(list) {
145607
+ const seen = new Set;
145608
+ const out = [];
145609
+ for (const item of list) {
145610
+ if (seen.has(item))
145611
+ continue;
145612
+ seen.add(item);
145613
+ out.push(item);
145614
+ }
145615
+ return out;
145616
+ }
145617
+ function parseProviderModel(spec) {
145618
+ const slash = spec.indexOf("/");
145619
+ if (slash < 1 || slash >= spec.length - 1)
145620
+ return null;
145621
+ return {
145622
+ providerID: spec.slice(0, slash).trim(),
145623
+ modelID: spec.slice(slash + 1).trim()
145624
+ };
145625
+ }
145626
+
145273
145627
  // ../plugin/src/features/magic-context/sidekick/core.ts
145274
145628
  var SIDEKICK_SYSTEM_PROMPT = `You are Sidekick, a focused memory-retrieval subagent for an AI coding assistant.
145275
145629
 
@@ -145295,17 +145649,108 @@ init_logger();
145295
145649
 
145296
145650
  // src/subagent-runner.ts
145297
145651
  import * as childProcess from "node:child_process";
145298
- import { existsSync as existsSync6 } from "node:fs";
145652
+ import { existsSync as existsSync7 } from "node:fs";
145299
145653
  import { createRequire as createRequire3 } from "node:module";
145300
- import { dirname as dirname3, join as join8, resolve as resolvePath } from "node:path";
145654
+ import { dirname as dirname3, join as join9, resolve as resolvePath } from "node:path";
145301
145655
  import { createInterface } from "node:readline";
145302
145656
  import { fileURLToPath } from "node:url";
145657
+
145658
+ // ../plugin/src/features/magic-context/subagent-token-capture.ts
145659
+ init_logger();
145660
+ function asNumber(value) {
145661
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
145662
+ }
145663
+ function tokenObjectFromMessage(message) {
145664
+ const info = message.info;
145665
+ if (info && typeof info === "object") {
145666
+ const tokens2 = info.tokens;
145667
+ if (tokens2 && typeof tokens2 === "object")
145668
+ return tokens2;
145669
+ }
145670
+ const tokens = message.tokens;
145671
+ if (tokens && typeof tokens === "object")
145672
+ return tokens;
145673
+ return null;
145674
+ }
145675
+ function isAssistantMessage(message) {
145676
+ if (!message || typeof message !== "object")
145677
+ return false;
145678
+ const record = message;
145679
+ const info = record.info;
145680
+ if (info && typeof info === "object") {
145681
+ return info.role === "assistant";
145682
+ }
145683
+ return record.role === "assistant";
145684
+ }
145685
+ function modelFromMessage(message) {
145686
+ const info = message.info;
145687
+ const source = info && typeof info === "object" ? info : message;
145688
+ return {
145689
+ providerId: typeof source.providerID === "string" ? source.providerID : typeof source.providerId === "string" ? source.providerId : null,
145690
+ modelId: typeof source.modelID === "string" ? source.modelID : typeof source.modelId === "string" ? source.modelId : null
145691
+ };
145692
+ }
145693
+ function emptyTokenTotals() {
145694
+ return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
145695
+ }
145696
+ function sumTokensFromChildMessages(messages) {
145697
+ const totals = emptyTokenTotals();
145698
+ for (const message of messages) {
145699
+ if (!isAssistantMessage(message))
145700
+ continue;
145701
+ const tokens = tokenObjectFromMessage(message);
145702
+ if (!tokens)
145703
+ continue;
145704
+ const cache = tokens.cache && typeof tokens.cache === "object" ? tokens.cache : {};
145705
+ totals.input += asNumber(tokens.input);
145706
+ totals.output += asNumber(tokens.output);
145707
+ totals.cacheRead += asNumber(cache.read ?? tokens.cacheRead ?? tokens.cache_read);
145708
+ totals.cacheWrite += asNumber(cache.write ?? tokens.cacheWrite ?? tokens.cache_write);
145709
+ }
145710
+ return totals;
145711
+ }
145712
+ function findLastAssistantModel(messages) {
145713
+ for (let index = messages.length - 1;index >= 0; index -= 1) {
145714
+ const message = messages[index];
145715
+ if (isAssistantMessage(message))
145716
+ return modelFromMessage(message);
145717
+ }
145718
+ return { providerId: null, modelId: null };
145719
+ }
145720
+ function recordChildInvocation(input) {
145721
+ const tokens = input.tokens ?? sumTokensFromChildMessages(input.messages ?? []);
145722
+ const model = input.providerId !== undefined || input.modelId !== undefined ? { providerId: input.providerId ?? null, modelId: input.modelId ?? null } : findLastAssistantModel(input.messages ?? []);
145723
+ try {
145724
+ return recordSubagentInvocation(input.db, {
145725
+ sessionId: input.parentSessionId,
145726
+ harness: input.harness,
145727
+ subagent: input.subagent,
145728
+ task: input.task ?? null,
145729
+ providerId: model.providerId,
145730
+ modelId: model.modelId,
145731
+ startedAt: input.startedAt,
145732
+ endedAt: input.endedAt ?? Date.now(),
145733
+ status: input.status,
145734
+ inputTokens: tokens.input,
145735
+ outputTokens: tokens.output,
145736
+ cacheReadTokens: tokens.cacheRead,
145737
+ cacheWriteTokens: tokens.cacheWrite,
145738
+ error: input.error ? describeError(input.error).brief : null,
145739
+ parentInvocationId: input.parentInvocationId ?? null
145740
+ });
145741
+ } catch (error) {
145742
+ sessionLog(input.parentSessionId, "subagent token accounting failed:", describeError(error).brief);
145743
+ return null;
145744
+ }
145745
+ }
145746
+
145747
+ // src/subagent-runner.ts
145303
145748
  function resolveBundledPiCli() {
145304
145749
  try {
145305
145750
  const require_ = createRequire3(import.meta.url);
145306
145751
  const pkgJson = require_.resolve("@earendil-works/pi-coding-agent/package.json");
145307
- const cliPath = join8(dirname3(pkgJson), "dist/cli.js");
145308
- if (existsSync6(cliPath))
145752
+ const cliPath = join9(dirname3(pkgJson), "dist/cli.js");
145753
+ if (existsSync7(cliPath))
145309
145754
  return cliPath;
145310
145755
  return null;
145311
145756
  } catch {
@@ -145316,7 +145761,7 @@ function resolveSubagentEntryPath() {
145316
145761
  try {
145317
145762
  const here = dirname3(fileURLToPath(import.meta.url));
145318
145763
  const candidate = resolvePath(here, "subagent-entry.js");
145319
- if (existsSync6(candidate))
145764
+ if (existsSync7(candidate))
145320
145765
  return candidate;
145321
145766
  return;
145322
145767
  } catch {
@@ -145329,6 +145774,17 @@ var DREAMER_ACTION_AGENTS = new Set([
145329
145774
  "dreamer",
145330
145775
  "magic-context-dreamer"
145331
145776
  ]);
145777
+ function inferAccountingSubagent(agent) {
145778
+ if (agent.includes("sidekick"))
145779
+ return "sidekick";
145780
+ if (agent.includes("dreamer"))
145781
+ return "dreamer";
145782
+ if (agent.includes("compressor"))
145783
+ return "compressor";
145784
+ if (agent.includes("recomp"))
145785
+ return "recomp";
145786
+ return "historian";
145787
+ }
145332
145788
 
145333
145789
  class PiSubagentRunner {
145334
145790
  harness = "pi";
@@ -145348,22 +145804,67 @@ class PiSubagentRunner {
145348
145804
  this.spawnImpl = options.spawnImpl ?? childProcess.spawn;
145349
145805
  }
145350
145806
  async run(options) {
145807
+ const models = [options.model, ...options.fallbackModels ?? []].filter((model) => typeof model === "string" && model.length > 0);
145808
+ const attempts = models.length > 0 ? models : [undefined];
145809
+ let lastResult = null;
145810
+ for (let index = 0;index < attempts.length; index += 1) {
145811
+ const model = attempts[index];
145812
+ const attemptOptions = {
145813
+ ...options,
145814
+ model,
145815
+ fallbackModels: undefined
145816
+ };
145817
+ const result = await this.runOnce(attemptOptions);
145818
+ if (result.ok)
145819
+ return result;
145820
+ lastResult = result;
145821
+ if (index >= attempts.length - 1 || !isFallbackEligible(result.reason)) {
145822
+ return result;
145823
+ }
145824
+ }
145825
+ return lastResult ?? this.runOnce({ ...options, fallbackModels: undefined });
145826
+ }
145827
+ async runOnce(options) {
145351
145828
  const startTime = Date.now();
145829
+ let recordedAccounting = false;
145830
+ const recordAccounting = (result, messages = []) => {
145831
+ if (!options.accountingSessionId || recordedAccounting)
145832
+ return;
145833
+ recordedAccounting = true;
145834
+ recordChildInvocation({
145835
+ db: openDatabase(),
145836
+ parentSessionId: options.accountingSessionId,
145837
+ harness: "pi",
145838
+ subagent: options.accountingSubagent ?? inferAccountingSubagent(options.agent),
145839
+ task: options.accountingTask ?? null,
145840
+ startedAt: startTime,
145841
+ status: result.ok ? "completed" : result.reason === "abort" ? "aborted" : "failed",
145842
+ messages,
145843
+ providerId: typeof options.model === "string" ? options.model.split("/")[0] : null,
145844
+ modelId: typeof options.model === "string" ? options.model.split("/").slice(1).join("/") : null,
145845
+ error: result.ok ? null : result.error,
145846
+ parentInvocationId: options.accountingParentInvocationId ?? null
145847
+ });
145848
+ };
145352
145849
  if (options.signal?.aborted) {
145353
- return {
145850
+ const result = {
145354
145851
  ok: false,
145355
145852
  reason: "abort",
145356
145853
  error: "pi subagent aborted by caller",
145357
145854
  durationMs: Date.now() - startTime
145358
145855
  };
145856
+ recordAccounting(result);
145857
+ return result;
145359
145858
  }
145360
145859
  const args = buildArgs(options);
145361
145860
  return new Promise((resolve2) => {
145861
+ let accountingMessages = [];
145362
145862
  let settled = false;
145363
145863
  const settle = (result) => {
145364
145864
  if (settled)
145365
145865
  return;
145366
145866
  settled = true;
145867
+ recordAccounting(result, accountingMessages);
145367
145868
  resolve2(result);
145368
145869
  };
145369
145870
  const emitProgress = (event) => {
@@ -145433,6 +145934,7 @@ class PiSubagentRunner {
145433
145934
  let lastEventType = null;
145434
145935
  let lastEventTimestamp = 0;
145435
145936
  const accumulatedMessages = [];
145937
+ accountingMessages = accumulatedMessages;
145436
145938
  rl.on("line", (line) => {
145437
145939
  if (line.length === 0)
145438
145940
  return;
@@ -145484,7 +145986,7 @@ class PiSubagentRunner {
145484
145986
  const m = e.message;
145485
145987
  if (m.role === "assistant") {
145486
145988
  const hasToolCall = Array.isArray(m.content) && m.content.some((c) => typeof c === "object" && c !== null && c.type === "toolCall");
145487
- const isTerminalStopReason = typeof m.stopReason === "string" && (m.stopReason === "stop" || m.stopReason === "length" || m.stopReason === "error" || m.stopReason === "aborted");
145989
+ const isTerminalStopReason = typeof m.stopReason === "string" && (m.stopReason === "stop" || m.stopReason === "length" || m.stopReason === "aborted");
145488
145990
  if (isTerminalStopReason && !hasToolCall) {
145489
145991
  sawAgentEnd = true;
145490
145992
  const result = extractFinalAssistant(accumulatedMessages);
@@ -145506,6 +146008,10 @@ class PiSubagentRunner {
145506
146008
  }
145507
146009
  if (sawAgentEnd && !drainTimerStarted) {
145508
146010
  drainTimerStarted = true;
146011
+ if (timeoutHandle) {
146012
+ clearTimeout(timeoutHandle);
146013
+ timeoutHandle = undefined;
146014
+ }
145509
146015
  drainTimerHandle = setTimeout(() => {
145510
146016
  if (settled)
145511
146017
  return;
@@ -145588,6 +146094,20 @@ class PiSubagentRunner {
145588
146094
  });
145589
146095
  return;
145590
146096
  }
146097
+ if (code !== 0 || signal !== null) {
146098
+ settle({
146099
+ ok: false,
146100
+ reason: "non_zero_exit",
146101
+ error: `pi exited (code=${code}, signal=${signal}) after terminal assistant event. stderr: ${stderr.slice(0, 500) || "(empty)"}`,
146102
+ durationMs: Date.now() - startTime,
146103
+ meta: {
146104
+ stderr: stderr.length > 0 ? stderr : undefined,
146105
+ exitCode: code,
146106
+ signal
146107
+ }
146108
+ });
146109
+ return;
146110
+ }
145591
146111
  if (finalStopReason === "error" || finalStopReason === "aborted" || finalStopReason === "length") {
145592
146112
  settle({
145593
146113
  ok: false,
@@ -145649,6 +146169,9 @@ class PiSubagentRunner {
145649
146169
  });
145650
146170
  }
145651
146171
  }
146172
+ function isFallbackEligible(reason) {
146173
+ return reason === "model_failed" || reason === "truncated" || reason === "non_zero_exit" || reason === "no_assistant";
146174
+ }
145652
146175
  function buildArgs(options) {
145653
146176
  const args = [
145654
146177
  "--print",
@@ -145659,7 +146182,8 @@ function buildArgs(options) {
145659
146182
  "--no-skills",
145660
146183
  "--no-prompt-templates"
145661
146184
  ];
145662
- if (SUBAGENT_ENTRY_PATH) {
146185
+ const shouldLoadSubagentExtension = SUBAGENT_ENTRY_PATH && (options.agent === "sidekick" || DREAMER_ACTION_AGENTS.has(options.agent));
146186
+ if (shouldLoadSubagentExtension) {
145663
146187
  args.push("--extension", SUBAGENT_ENTRY_PATH);
145664
146188
  if (DREAMER_ACTION_AGENTS.has(options.agent)) {
145665
146189
  args.push("--magic-context-dreamer-actions");
@@ -145668,11 +146192,8 @@ function buildArgs(options) {
145668
146192
  if (options.systemPrompt && options.systemPrompt.length > 0) {
145669
146193
  args.push("--system-prompt", options.systemPrompt);
145670
146194
  }
145671
- const models = [options.model, ...options.fallbackModels ?? []].filter((model) => typeof model === "string" && model.length > 0);
145672
- if (models.length > 1) {
145673
- args.push("--models", models.join(","));
145674
- } else if (models.length === 1) {
145675
- args.push("--model", models[0]);
146195
+ if (typeof options.model === "string" && options.model.length > 0) {
146196
+ args.push("--model", options.model);
145676
146197
  }
145677
146198
  if (options.thinkingLevel) {
145678
146199
  args.push("--thinking", options.thinkingLevel);
@@ -145762,10 +146283,13 @@ function registerCtxAugCommand(pi, config) {
145762
146283
  systemPrompt: config.systemPrompt ?? SIDEKICK_SYSTEM_PROMPT,
145763
146284
  userMessage: prompt,
145764
146285
  model: config.model,
146286
+ fallbackModels: config.fallbackModels,
145765
146287
  timeoutMs: config.timeoutMs ?? 30000,
145766
146288
  cwd: ctx.cwd,
145767
146289
  signal: ctx.signal,
145768
- thinkingLevel: config.thinking_level
146290
+ thinkingLevel: config.thinking_level,
146291
+ accountingSessionId: sessionLabel,
146292
+ accountingSubagent: "sidekick"
145769
146293
  });
145770
146294
  if (!result.ok) {
145771
146295
  log(`[magic-context][pi] /ctx-aug: sidekick failed (${result.reason}): ${result.error}`);
@@ -145972,61 +146496,14 @@ function clearStaleEntries(db, maxAgeMs) {
145972
146496
  // src/commands/ctx-dream.ts
145973
146497
  init_logger();
145974
146498
  // ../plugin/src/features/magic-context/dreamer/runner.ts
145975
- import { existsSync as existsSync8 } from "node:fs";
145976
- import { join as join11 } from "node:path";
146499
+ import { existsSync as existsSync9 } from "node:fs";
146500
+ import { join as join12 } from "node:path";
145977
146501
 
145978
146502
  // ../plugin/src/shared/index.ts
145979
146503
  init_logger();
145980
146504
 
145981
146505
  // ../plugin/src/shared/model-suggestion-retry.ts
145982
146506
  init_logger();
145983
-
145984
- // ../plugin/src/shared/resolve-fallbacks.ts
145985
- function resolveFallbackChain(agentName, userFallbacks) {
145986
- const userList = normalizeUserFallbacks(userFallbacks);
145987
- if (userList.length > 0) {
145988
- return dedupe(userList.filter(isValidModelSpec));
145989
- }
145990
- const builtin = getAgentFallbackModels(agentName);
145991
- if (!builtin || builtin.length === 0)
145992
- return [];
145993
- return dedupe(builtin.filter(isValidModelSpec));
145994
- }
145995
- function normalizeUserFallbacks(userFallbacks) {
145996
- if (!userFallbacks)
145997
- return [];
145998
- if (typeof userFallbacks === "string") {
145999
- const trimmed = userFallbacks.trim();
146000
- return trimmed ? [trimmed] : [];
146001
- }
146002
- return userFallbacks.map((s) => s.trim()).filter((s) => s.length > 0);
146003
- }
146004
- function isValidModelSpec(spec) {
146005
- const slash = spec.indexOf("/");
146006
- return slash > 0 && slash < spec.length - 1;
146007
- }
146008
- function dedupe(list) {
146009
- const seen = new Set;
146010
- const out = [];
146011
- for (const item of list) {
146012
- if (seen.has(item))
146013
- continue;
146014
- seen.add(item);
146015
- out.push(item);
146016
- }
146017
- return out;
146018
- }
146019
- function parseProviderModel(spec) {
146020
- const slash = spec.indexOf("/");
146021
- if (slash < 1 || slash >= spec.length - 1)
146022
- return null;
146023
- return {
146024
- providerID: spec.slice(0, slash).trim(),
146025
- modelID: spec.slice(slash + 1).trim()
146026
- };
146027
- }
146028
-
146029
- // ../plugin/src/shared/model-suggestion-retry.ts
146030
146507
  function extractMessage(error) {
146031
146508
  if (typeof error === "string")
146032
146509
  return error;
@@ -146267,20 +146744,20 @@ init_logger();
146267
146744
  await init_sqlite();
146268
146745
 
146269
146746
  // ../plugin/src/features/magic-context/key-files/identify-key-files.ts
146270
- import { readFileSync as readFileSync4 } from "node:fs";
146271
- import { join as join10 } from "node:path";
146747
+ import { readFileSync as readFileSync5 } from "node:fs";
146748
+ import { join as join11 } from "node:path";
146272
146749
  init_logger();
146273
146750
 
146274
146751
  // ../plugin/src/features/magic-context/key-files/aft-availability.ts
146275
146752
  var import_comment_json = __toESM(require_src2(), 1);
146276
- import { existsSync as existsSync7, readFileSync as readFileSync3 } from "node:fs";
146753
+ import { existsSync as existsSync8, readFileSync as readFileSync4 } from "node:fs";
146277
146754
  import { homedir as homedir3 } from "node:os";
146278
- import { join as join9 } from "node:path";
146755
+ import { join as join10 } from "node:path";
146279
146756
  var overrideAvailability = null;
146280
- function parseConfig(path5) {
146281
- if (!existsSync7(path5))
146757
+ function parseConfig(path6) {
146758
+ if (!existsSync8(path6))
146282
146759
  return null;
146283
- return import_comment_json.parse(readFileSync3(path5, "utf-8"));
146760
+ return import_comment_json.parse(readFileSync4(path6, "utf-8"));
146284
146761
  }
146285
146762
  function entryMatchesAft(entry) {
146286
146763
  const value = Array.isArray(entry) ? entry[0] : entry;
@@ -146302,15 +146779,15 @@ function hasAftAtKeys(value, keys) {
146302
146779
  function getAftAvailability() {
146303
146780
  const home = process.env.HOME || homedir3();
146304
146781
  const opencodePaths = [
146305
- join9(home, ".config", "opencode", "opencode.jsonc"),
146306
- join9(home, ".config", "opencode", "opencode.json")
146782
+ join10(home, ".config", "opencode", "opencode.jsonc"),
146783
+ join10(home, ".config", "opencode", "opencode.json")
146307
146784
  ];
146308
- const piPaths = [join9(home, ".pi", "agent", "settings.json")];
146785
+ const piPaths = [join10(home, ".pi", "agent", "settings.json")];
146309
146786
  const checkedPaths = [...opencodePaths, ...piPaths];
146310
146787
  let opencode = false;
146311
- for (const path5 of opencodePaths) {
146788
+ for (const path6 of opencodePaths) {
146312
146789
  try {
146313
- const config = parseConfig(path5);
146790
+ const config = parseConfig(path6);
146314
146791
  if (hasAftAtKeys(config, ["plugin", "plugins", "mcp", "mcp_servers"])) {
146315
146792
  opencode = true;
146316
146793
  break;
@@ -146318,9 +146795,9 @@ function getAftAvailability() {
146318
146795
  } catch {}
146319
146796
  }
146320
146797
  let pi = false;
146321
- for (const path5 of piPaths) {
146798
+ for (const path6 of piPaths) {
146322
146799
  try {
146323
- const config = parseConfig(path5);
146800
+ const config = parseConfig(path6);
146324
146801
  if (hasAftAtKeys(config, ["packages", "extensions"])) {
146325
146802
  pi = true;
146326
146803
  break;
@@ -146769,7 +147246,7 @@ async function runKeyFilesTask(args) {
146769
147246
  if (row.staleReason !== null || row.generationConfigHash !== configHash)
146770
147247
  return false;
146771
147248
  try {
146772
- return sha256(readFileSync4(join10(projectPath, row.path))) === row.contentHash;
147249
+ return sha256(readFileSync5(join11(projectPath, row.path))) === row.contentHash;
146773
147250
  } catch {
146774
147251
  return false;
146775
147252
  }
@@ -147693,6 +148170,17 @@ function buildDreamTaskPrompt(task, args) {
147693
148170
  }
147694
148171
 
147695
148172
  // ../plugin/src/features/magic-context/user-memory/storage-user-memory.ts
148173
+ function insertUserMemoryCandidates(db, candidates) {
148174
+ if (candidates.length === 0)
148175
+ return;
148176
+ const now = Date.now();
148177
+ const stmt = db.prepare("INSERT INTO user_memory_candidates (content, session_id, source_compartment_start, source_compartment_end, created_at) VALUES (?, ?, ?, ?, ?)");
148178
+ db.transaction(() => {
148179
+ for (const c of candidates) {
148180
+ stmt.run(c.content, c.sessionId, c.sourceCompartmentStart ?? null, c.sourceCompartmentEnd ?? null, now);
148181
+ }
148182
+ })();
148183
+ }
147696
148184
  function getUserMemoryCandidates(db) {
147697
148185
  const rows = db.prepare("SELECT id, content, session_id, source_compartment_start, source_compartment_end, created_at FROM user_memory_candidates ORDER BY created_at ASC").all();
147698
148186
  return rows.map((r) => ({
@@ -147799,6 +148287,23 @@ Return valid JSON (no markdown fencing):
147799
148287
 
147800
148288
  If no promotions are warranted, return empty arrays. Always consume reviewed candidates so they don't accumulate indefinitely.`;
147801
148289
  let agentSessionId = null;
148290
+ const startedAt = Date.now();
148291
+ let invocationRecorded = false;
148292
+ const recordInvocation = (params) => {
148293
+ if (!args.parentSessionId || invocationRecorded)
148294
+ return;
148295
+ invocationRecorded = true;
148296
+ recordChildInvocation({
148297
+ db: args.db,
148298
+ parentSessionId: args.parentSessionId,
148299
+ harness: "opencode",
148300
+ subagent: "user_memory_review",
148301
+ startedAt,
148302
+ status: params.status,
148303
+ messages: params.messages,
148304
+ error: params.error
148305
+ });
148306
+ };
147802
148307
  const abortController = new AbortController;
147803
148308
  const leaseInterval = setInterval(() => {
147804
148309
  try {
@@ -147820,8 +148325,11 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
147820
148325
  });
147821
148326
  const created = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
147822
148327
  agentSessionId = typeof created?.id === "string" ? created.id : null;
147823
- if (!agentSessionId)
147824
- throw new Error("Could not create user memory review session.");
148328
+ if (!agentSessionId) {
148329
+ const error = new Error("Could not create user memory review session.");
148330
+ recordInvocation({ status: "failed", error });
148331
+ throw error;
148332
+ }
147825
148333
  log(`[dreamer] user-memories: child session created ${agentSessionId}`);
147826
148334
  const remainingMs = Math.max(0, args.deadline - Date.now());
147827
148335
  await promptSyncWithModelSuggestionRetry(args.client, {
@@ -147845,6 +148353,7 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
147845
148353
  const messages = normalizeSDKResponse(messagesResponse, [], {
147846
148354
  preferResponseOnMissingData: true
147847
148355
  });
148356
+ recordInvocation({ status: "completed", messages });
147848
148357
  const responseText = extractLatestAssistantText(messages);
147849
148358
  if (!responseText) {
147850
148359
  log("[dreamer] user-memories: no response from review agent");
@@ -147966,11 +148475,11 @@ function logWithStackHead(message, stackHead) {
147966
148475
  log(message, stackHead ? { stackHead } : undefined);
147967
148476
  }
147968
148477
  function getOpenCodeDbPath2() {
147969
- return join11(getDataDir(), "opencode", "opencode.db");
148478
+ return join12(getDataDir(), "opencode", "opencode.db");
147970
148479
  }
147971
148480
  function openOpenCodeDb() {
147972
148481
  const dbPath = getOpenCodeDbPath2();
147973
- if (!existsSync8(dbPath)) {
148482
+ if (!existsSync9(dbPath)) {
147974
148483
  log(`[key-files] OpenCode DB not found at ${dbPath} — skipping`);
147975
148484
  return null;
147976
148485
  }
@@ -148084,6 +148593,24 @@ async function runDream(args) {
148084
148593
  log(`[dreamer] starting task: ${taskName}`);
148085
148594
  const taskStartedAt = Date.now();
148086
148595
  let agentSessionId = null;
148596
+ const invocationStartedAt = Date.now();
148597
+ let invocationRecorded = false;
148598
+ const recordInvocation = (params) => {
148599
+ if (!parentSessionId || invocationRecorded)
148600
+ return;
148601
+ invocationRecorded = true;
148602
+ recordChildInvocation({
148603
+ db: args.db,
148604
+ parentSessionId,
148605
+ harness: "opencode",
148606
+ subagent: "dreamer",
148607
+ task: taskName,
148608
+ startedAt: invocationStartedAt,
148609
+ status: params.status,
148610
+ messages: params.messages,
148611
+ error: params.error
148612
+ });
148613
+ };
148087
148614
  const taskAbortController = new AbortController;
148088
148615
  const leaseRenewalInterval = setInterval(() => {
148089
148616
  try {
@@ -148101,8 +148628,8 @@ async function runDream(args) {
148101
148628
  try {
148102
148629
  const docsDir = args.sessionDirectory ?? args.projectIdentity;
148103
148630
  const existingDocs = taskName === "maintain-docs" ? {
148104
- architecture: existsSync8(join11(docsDir, "ARCHITECTURE.md")),
148105
- structure: existsSync8(join11(docsDir, "STRUCTURE.md"))
148631
+ architecture: existsSync9(join12(docsDir, "ARCHITECTURE.md")),
148632
+ structure: existsSync9(join12(docsDir, "STRUCTURE.md"))
148106
148633
  } : undefined;
148107
148634
  const userMemories = taskName === "archive-stale" ? getActiveUserMemories(args.db).map((um) => ({
148108
148635
  id: um.id,
@@ -148124,7 +148651,9 @@ async function runDream(args) {
148124
148651
  const createdSession = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
148125
148652
  agentSessionId = typeof createdSession?.id === "string" ? createdSession.id : null;
148126
148653
  if (!agentSessionId) {
148127
- throw new Error("Dreamer could not create its child session.");
148654
+ const error = new Error("Dreamer could not create its child session.");
148655
+ recordInvocation({ status: "failed", error });
148656
+ throw error;
148128
148657
  }
148129
148658
  log(`[dreamer] task ${taskName}: child session created ${agentSessionId}`);
148130
148659
  await promptSyncWithModelSuggestionRetry(args.client, {
@@ -148151,6 +148680,7 @@ async function runDream(args) {
148151
148680
  const messages = normalizeSDKResponse(messagesResponse, [], {
148152
148681
  preferResponseOnMissingData: true
148153
148682
  });
148683
+ recordInvocation({ status: "completed", messages });
148154
148684
  const taskResult = extractLatestAssistantText(messages);
148155
148685
  if (!taskResult) {
148156
148686
  throw new Error("Dreamer returned no assistant output.");
@@ -148165,6 +148695,7 @@ async function runDream(args) {
148165
148695
  lastErrorSignature = null;
148166
148696
  consecutiveSameErrorFailures = 0;
148167
148697
  } catch (error) {
148698
+ recordInvocation({ status: lostLease ? "aborted" : "failed", error });
148168
148699
  const durationMs = Date.now() - taskStartedAt;
148169
148700
  const errorDescription = describeError(error);
148170
148701
  logWithStackHead(`[dreamer] task ${taskName}: failed after ${(durationMs / 1000).toFixed(1)}s — ${errorDescription.brief}`, errorDescription.stackHead);
@@ -148434,6 +148965,24 @@ ${noteDescriptions}
148434
148965
  Only include notes whose conditions you could definitively evaluate against external signals. Skip notes where you cannot determine the status (they will be re-evaluated next run, or eventually archived as stale).`;
148435
148966
  const taskStartedAt = Date.now();
148436
148967
  let agentSessionId = null;
148968
+ const startedAt = Date.now();
148969
+ let invocationRecorded = false;
148970
+ const recordInvocation = (params) => {
148971
+ if (!args.parentSessionId || invocationRecorded)
148972
+ return;
148973
+ invocationRecorded = true;
148974
+ recordChildInvocation({
148975
+ db: args.db,
148976
+ parentSessionId: args.parentSessionId,
148977
+ harness: "opencode",
148978
+ subagent: "dreamer",
148979
+ task: "smart-notes",
148980
+ startedAt,
148981
+ status: params.status,
148982
+ messages: params.messages,
148983
+ error: params.error
148984
+ });
148985
+ };
148437
148986
  const abortController = new AbortController;
148438
148987
  const leaseInterval = setInterval(() => {
148439
148988
  try {
@@ -148457,8 +149006,11 @@ Only include notes whose conditions you could definitively evaluate against exte
148457
149006
  });
148458
149007
  const created = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
148459
149008
  agentSessionId = typeof created?.id === "string" ? created.id : null;
148460
- if (!agentSessionId)
148461
- throw new Error("Could not create smart note evaluation session.");
149009
+ if (!agentSessionId) {
149010
+ const error = new Error("Could not create smart note evaluation session.");
149011
+ recordInvocation({ status: "failed", error });
149012
+ throw error;
149013
+ }
148462
149014
  log(`[dreamer] smart notes: child session created ${agentSessionId}`);
148463
149015
  const remainingMs = Math.max(0, args.deadline - Date.now());
148464
149016
  await promptSyncWithModelSuggestionRetry(args.client, {
@@ -148482,6 +149034,7 @@ Only include notes whose conditions you could definitively evaluate against exte
148482
149034
  const messages = normalizeSDKResponse(messagesResponse, [], {
148483
149035
  preferResponseOnMissingData: true
148484
149036
  });
149037
+ recordInvocation({ status: "completed", messages });
148485
149038
  const output = extractLatestAssistantText(messages);
148486
149039
  if (!output)
148487
149040
  throw new Error("Smart note evaluation returned no output.");
@@ -149518,10 +150071,10 @@ function mergeDefs(...defs) {
149518
150071
  function cloneDef(schema) {
149519
150072
  return mergeDefs(schema._zod.def);
149520
150073
  }
149521
- function getElementAtPath(obj, path5) {
149522
- if (!path5)
150074
+ function getElementAtPath(obj, path6) {
150075
+ if (!path6)
149523
150076
  return obj;
149524
- return path5.reduce((acc, key) => acc?.[key], obj);
150077
+ return path6.reduce((acc, key) => acc?.[key], obj);
149525
150078
  }
149526
150079
  function promiseAllObject(promisesObj) {
149527
150080
  const keys = Object.keys(promisesObj);
@@ -149929,11 +150482,11 @@ function explicitlyAborted(x, startIndex = 0) {
149929
150482
  }
149930
150483
  return false;
149931
150484
  }
149932
- function prefixIssues(path5, issues) {
150485
+ function prefixIssues(path6, issues) {
149933
150486
  return issues.map((iss) => {
149934
150487
  var _a2;
149935
150488
  (_a2 = iss).path ?? (_a2.path = []);
149936
- iss.path.unshift(path5);
150489
+ iss.path.unshift(path6);
149937
150490
  return iss;
149938
150491
  });
149939
150492
  }
@@ -150080,16 +150633,16 @@ function flattenError(error, mapper = (issue2) => issue2.message) {
150080
150633
  }
150081
150634
  function formatError(error, mapper = (issue2) => issue2.message) {
150082
150635
  const fieldErrors = { _errors: [] };
150083
- const processError = (error2, path5 = []) => {
150636
+ const processError = (error2, path6 = []) => {
150084
150637
  for (const issue2 of error2.issues) {
150085
150638
  if (issue2.code === "invalid_union" && issue2.errors.length) {
150086
- issue2.errors.map((issues) => processError({ issues }, [...path5, ...issue2.path]));
150639
+ issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
150087
150640
  } else if (issue2.code === "invalid_key") {
150088
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
150641
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
150089
150642
  } else if (issue2.code === "invalid_element") {
150090
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
150643
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
150091
150644
  } else {
150092
- const fullpath = [...path5, ...issue2.path];
150645
+ const fullpath = [...path6, ...issue2.path];
150093
150646
  if (fullpath.length === 0) {
150094
150647
  fieldErrors._errors.push(mapper(issue2));
150095
150648
  } else {
@@ -150116,17 +150669,17 @@ function formatError(error, mapper = (issue2) => issue2.message) {
150116
150669
  }
150117
150670
  function treeifyError(error, mapper = (issue2) => issue2.message) {
150118
150671
  const result = { errors: [] };
150119
- const processError = (error2, path5 = []) => {
150672
+ const processError = (error2, path6 = []) => {
150120
150673
  var _a2, _b;
150121
150674
  for (const issue2 of error2.issues) {
150122
150675
  if (issue2.code === "invalid_union" && issue2.errors.length) {
150123
- issue2.errors.map((issues) => processError({ issues }, [...path5, ...issue2.path]));
150676
+ issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
150124
150677
  } else if (issue2.code === "invalid_key") {
150125
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
150678
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
150126
150679
  } else if (issue2.code === "invalid_element") {
150127
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
150680
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
150128
150681
  } else {
150129
- const fullpath = [...path5, ...issue2.path];
150682
+ const fullpath = [...path6, ...issue2.path];
150130
150683
  if (fullpath.length === 0) {
150131
150684
  result.errors.push(mapper(issue2));
150132
150685
  continue;
@@ -150158,8 +150711,8 @@ function treeifyError(error, mapper = (issue2) => issue2.message) {
150158
150711
  }
150159
150712
  function toDotPath(_path) {
150160
150713
  const segs = [];
150161
- const path5 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
150162
- for (const seg of path5) {
150714
+ const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
150715
+ for (const seg of path6) {
150163
150716
  if (typeof seg === "number")
150164
150717
  segs.push(`[${seg}]`);
150165
150718
  else if (typeof seg === "symbol")
@@ -162618,13 +163171,13 @@ function resolveRef(ref, ctx) {
162618
163171
  if (!ref.startsWith("#")) {
162619
163172
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
162620
163173
  }
162621
- const path5 = ref.slice(1).split("/").filter(Boolean);
162622
- if (path5.length === 0) {
163174
+ const path6 = ref.slice(1).split("/").filter(Boolean);
163175
+ if (path6.length === 0) {
162623
163176
  return ctx.rootSchema;
162624
163177
  }
162625
163178
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
162626
- if (path5[0] === defsKey) {
162627
- const key = path5[1];
163179
+ if (path6[0] === defsKey) {
163180
+ const key = path6[1];
162628
163181
  if (!key || !ctx.defs[key]) {
162629
163182
  throw new Error(`Reference not found: ${ref}`);
162630
163183
  }
@@ -163093,7 +163646,6 @@ var DEFAULT_DREAMER_TASKS = [
163093
163646
  ];
163094
163647
  var PiThinkingLevelSchema = exports_external.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional();
163095
163648
  var DreamerConfigSchema = AgentOverrideConfigSchema.merge(exports_external.object({
163096
- enabled: exports_external.boolean().default(false),
163097
163649
  schedule: exports_external.string().default("02:00-06:00"),
163098
163650
  max_runtime_minutes: exports_external.number().min(10).default(120),
163099
163651
  tasks: exports_external.array(DreamingTaskSchema).default(DEFAULT_DREAMER_TASKS),
@@ -163111,7 +163663,6 @@ var DreamerConfigSchema = AgentOverrideConfigSchema.merge(exports_external.objec
163111
163663
  thinking_level: PiThinkingLevelSchema
163112
163664
  }));
163113
163665
  var SidekickConfigSchema = AgentOverrideConfigSchema.extend({
163114
- enabled: exports_external.boolean().default(false),
163115
163666
  timeout_ms: exports_external.number().default(30000),
163116
163667
  system_prompt: exports_external.string().optional(),
163117
163668
  thinking_level: PiThinkingLevelSchema
@@ -163295,9 +163846,9 @@ function getEmbeddingProviderIdentity(config2) {
163295
163846
  }
163296
163847
 
163297
163848
  // ../plugin/src/features/magic-context/memory/embedding-local.ts
163298
- import { mkdirSync as mkdirSync4 } from "node:fs";
163849
+ import { mkdirSync as mkdirSync5 } from "node:fs";
163299
163850
  import { open, stat, unlink, writeFile } from "node:fs/promises";
163300
- import { dirname as dirname4, join as join12 } from "node:path";
163851
+ import { dirname as dirname4, join as join13 } from "node:path";
163301
163852
  import { pathToFileURL } from "node:url";
163302
163853
  init_data_path();
163303
163854
  init_logger();
@@ -163364,7 +163915,7 @@ async function injectWasmOrtForElectron() {
163364
163915
  const { createRequire: createRequireFn } = await import("node:module");
163365
163916
  const requireFn = createRequireFn(import.meta.url);
163366
163917
  const pkgPath = requireFn.resolve("onnxruntime-web/package.json");
163367
- const distDir = join12(dirname4(pkgPath), "dist");
163918
+ const distDir = join13(dirname4(pkgPath), "dist");
163368
163919
  const wasmPathsPrefix = `${pathToFileURL(distDir).href}/`;
163369
163920
  if (ortWeb.env?.wasm) {
163370
163921
  ortWeb.env.wasm.wasmPaths = wasmPathsPrefix;
@@ -163475,15 +164026,15 @@ class LocalEmbeddingProvider {
163475
164026
  if (LogLevel && "ERROR" in LogLevel) {
163476
164027
  env.logLevel = LogLevel.ERROR;
163477
164028
  }
163478
- const modelCacheDir = join12(getMagicContextStorageDir(), "models");
164029
+ const modelCacheDir = join13(getMagicContextStorageDir(), "models");
163479
164030
  try {
163480
- mkdirSync4(modelCacheDir, { recursive: true });
164031
+ mkdirSync5(modelCacheDir, { recursive: true });
163481
164032
  env.cacheDir = modelCacheDir;
163482
164033
  } catch {
163483
164034
  log("[magic-context] could not create model cache dir, using library default");
163484
164035
  }
163485
164036
  const createPipeline = transformersModule.pipeline;
163486
- const lockPath = join12(modelCacheDir, ".load.lock");
164037
+ const lockPath = join13(modelCacheDir, ".load.lock");
163487
164038
  const releaseLock = await acquireModelLoadLock(lockPath);
163488
164039
  const stopHeartbeat = startLockHeartbeat(lockPath);
163489
164040
  try {
@@ -164651,7 +165202,7 @@ async function startDreamScheduleTimer(args) {
164651
165202
  const db = openDatabase();
164652
165203
  await args.ensureRegistered(args.directory, db);
164653
165204
  const snapshot = getProjectEmbeddingSnapshot(args.projectIdentity);
164654
- const dreamingEnabled = Boolean(args.dreamerConfig?.enabled && args.dreamerConfig.schedule?.trim());
165205
+ const dreamingEnabled = Boolean(args.dreamerConfig && args.dreamerConfig.disable !== true && args.dreamerConfig.schedule?.trim());
164655
165206
  const embeddingSweepEnabled = snapshot?.enabled ?? false;
164656
165207
  const commitIndexingEnabled = snapshot?.gitCommitEnabled ?? false;
164657
165208
  if (!dreamingEnabled && !embeddingSweepEnabled && !commitIndexingEnabled) {
@@ -164707,7 +165258,7 @@ function runTick(origin) {
164707
165258
  })();
164708
165259
  }
164709
165260
  async function sweepProject(reg, origin, db = openDatabase(), gitCommitEnabled = getProjectEmbeddingSnapshot(reg.projectIdentity)?.gitCommitEnabled === true) {
164710
- const dreamingEnabled = Boolean(reg.dreamerConfig?.enabled && reg.dreamerConfig.schedule?.trim());
165261
+ const dreamingEnabled = Boolean(reg.dreamerConfig && reg.dreamerConfig.disable !== true && reg.dreamerConfig.schedule?.trim());
164711
165262
  if (gitCommitEnabled && reg.gitCommitIndexing) {
164712
165263
  await sweepGitCommits({
164713
165264
  directory: reg.directory,
@@ -164835,12 +165386,12 @@ function handleUntrustedLoad(db, projectIdentity, directory, detailed) {
164835
165386
  }
164836
165387
 
164837
165388
  // src/config/index.ts
164838
- import { existsSync as existsSync10, readFileSync as readFileSync6 } from "node:fs";
165389
+ import { existsSync as existsSync11, readFileSync as readFileSync7 } from "node:fs";
164839
165390
  import { homedir as homedir5 } from "node:os";
164840
- import { join as join13 } from "node:path";
165391
+ import { join as join14 } from "node:path";
164841
165392
 
164842
165393
  // ../plugin/src/config/variable.ts
164843
- import { existsSync as existsSync9, readFileSync as readFileSync5 } from "node:fs";
165394
+ import { existsSync as existsSync10, readFileSync as readFileSync6 } from "node:fs";
164844
165395
  import { homedir as homedir4 } from "node:os";
164845
165396
  import { dirname as dirname5, isAbsolute, resolve as resolve3 } from "node:path";
164846
165397
  var ENV_PATTERN = /\{env:([^}]+)\}/g;
@@ -164898,13 +165449,13 @@ function substituteConfigVariables(input) {
164898
165449
  } else if (!isAbsolute(filePath)) {
164899
165450
  filePath = resolve3(configDir, filePath);
164900
165451
  }
164901
- if (!existsSync9(filePath)) {
165452
+ if (!existsSync10(filePath)) {
164902
165453
  warnings.push(`File not found for ${token} (resolved to ${filePath}); using empty string`);
164903
165454
  continue;
164904
165455
  }
164905
165456
  let contents;
164906
165457
  try {
164907
- contents = readFileSync5(filePath, "utf-8").trim();
165458
+ contents = readFileSync6(filePath, "utf-8").trim();
164908
165459
  } catch (error51) {
164909
165460
  const message = error51 instanceof Error ? error51.message : String(error51);
164910
165461
  warnings.push(`Failed to read file for ${token} (${filePath}): ${message}; using empty string`);
@@ -164920,39 +165471,39 @@ function substituteConfigVariables(input) {
164920
165471
  var import_comment_json2 = __toESM(require_src2(), 1);
164921
165472
  var CONFIG_FILE_NAME = "magic-context";
164922
165473
  function getProjectConfigPaths(cwd) {
164923
- const basePath = join13(cwd, ".pi", CONFIG_FILE_NAME);
165474
+ const basePath = join14(cwd, ".pi", CONFIG_FILE_NAME);
164924
165475
  return [`${basePath}.jsonc`, `${basePath}.json`];
164925
165476
  }
164926
165477
  function getUserConfigPaths() {
164927
165478
  const home = process.env.HOME ?? homedir5();
164928
- const basePath = join13(home, ".pi", "agent", CONFIG_FILE_NAME);
165479
+ const basePath = join14(home, ".pi", "agent", CONFIG_FILE_NAME);
164929
165480
  return [`${basePath}.jsonc`, `${basePath}.json`];
164930
165481
  }
164931
165482
  function resolveFirstExisting(paths) {
164932
- return paths.find((path5) => existsSync10(path5));
165483
+ return paths.find((path6) => existsSync11(path6));
164933
165484
  }
164934
- function loadConfigFile(path5, scope) {
165485
+ function loadConfigFile(path6, scope) {
164935
165486
  try {
164936
- const rawText = readFileSync6(path5, "utf-8");
165487
+ const rawText = readFileSync7(path6, "utf-8");
164937
165488
  const substituted = substituteConfigVariables({
164938
165489
  text: rawText,
164939
- configPath: path5
165490
+ configPath: path6
164940
165491
  });
164941
165492
  return {
164942
- path: path5,
165493
+ path: path6,
164943
165494
  scope,
164944
165495
  config: import_comment_json2.parse(substituted.text),
164945
- warnings: substituted.warnings.map((warning) => `${path5}: ${warning}`),
165496
+ warnings: substituted.warnings.map((warning) => `${path6}: ${warning}`),
164946
165497
  loadOutcome: substituted.warnings.length > 0 ? "substitution-failure" : "ok"
164947
165498
  };
164948
165499
  } catch (error51) {
164949
165500
  const message = error51 instanceof Error ? error51.message : String(error51);
164950
165501
  return {
164951
- path: path5,
165502
+ path: path6,
164952
165503
  scope,
164953
165504
  config: {},
164954
165505
  warnings: [
164955
- `${path5}: failed to load config: ${message}; using defaults for this file.`
165506
+ `${path6}: failed to load config: ${message}; using defaults for this file.`
164956
165507
  ],
164957
165508
  loadOutcome: typeof error51.code === "string" ? "project-file-io-error" : "project-file-parse-error"
164958
165509
  };
@@ -164990,9 +165541,11 @@ function mergeRawConfigs(base, override) {
164990
165541
  return merged;
164991
165542
  }
164992
165543
  function parsePiConfig(rawConfig, recoveredTopLevelKeys = []) {
164993
- const parsed = MagicContextConfigSchema.safeParse(rawConfig);
165544
+ const preMigrationWarnings = [];
165545
+ const migrated = migrateLegacyAgentEnabledInMemory(rawConfig, preMigrationWarnings);
165546
+ const parsed = MagicContextConfigSchema.safeParse(migrated);
164994
165547
  if (parsed.success) {
164995
- return { config: parsed.data, warnings: [] };
165548
+ return { config: parsed.data, warnings: preMigrationWarnings };
164996
165549
  }
164997
165550
  const defaults = MagicContextConfigSchema.parse({});
164998
165551
  const errorPaths = new Set;
@@ -165002,8 +165555,8 @@ function parsePiConfig(rawConfig, recoveredTopLevelKeys = []) {
165002
165555
  errorPaths.add(String(topKey));
165003
165556
  }
165004
165557
  }
165005
- const patched = { ...rawConfig };
165006
- const warnings = [];
165558
+ const patched = { ...migrated };
165559
+ const warnings = [...preMigrationWarnings];
165007
165560
  for (const key of errorPaths) {
165008
165561
  recoveredTopLevelKeys.push(key);
165009
165562
  const isAgentConfig = key === "historian" || key === "dreamer" || key === "sidekick";
@@ -165077,9 +165630,9 @@ function bindSubstitutionFailures(loaded) {
165077
165630
  }
165078
165631
  const emptyPaths = collectEmptyStringPaths(loaded.config);
165079
165632
  return loaded.warnings.map((message) => {
165080
- const matchedPath = emptyPaths.find((path5) => {
165081
- const tail = path5.split(".").at(-1) ?? path5;
165082
- return message.includes(path5) || message.toLowerCase().includes(tail.toLowerCase());
165633
+ const matchedPath = emptyPaths.find((path6) => {
165634
+ const tail = path6.split(".").at(-1) ?? path6;
165635
+ return message.includes(path6) || message.toLowerCase().includes(tail.toLowerCase());
165083
165636
  });
165084
165637
  return {
165085
165638
  keyPath: matchedPath ?? "<unknown>",
@@ -165175,7 +165728,7 @@ var sessionCounter = 0;
165175
165728
  var piSubagentRunnerFactory = () => new PiSubagentRunner;
165176
165729
  var startDreamScheduleTimerFn = startDreamScheduleTimer;
165177
165730
  function registerPiDreamerProject(opts) {
165178
- if (!opts.config.enabled) {
165731
+ if (opts.config.disable === true) {
165179
165732
  return;
165180
165733
  }
165181
165734
  const existing = registeredProjects2.get(opts.projectIdentity);
@@ -165410,7 +165963,7 @@ function registerCtxDreamCommand(pi, deps) {
165410
165963
  if (deps.dreamerEnabled === false) {
165411
165964
  sendCtxStatusMessage(pi, {
165412
165965
  title: "/ctx-dream",
165413
- text: "## /ctx-dream\n\nDreamer is not configured for this project (`dreamer.enabled=false`).",
165966
+ text: "## /ctx-dream\n\nDreamer is disabled for this project (`dreamer.disable=true`).",
165414
165967
  level: "info"
165415
165968
  }, {
165416
165969
  projectDir: project.projectDir,
@@ -165456,6 +166009,8 @@ function registerCtxDreamCommand(pi, deps) {
165456
166009
  let summary;
165457
166010
  if (!result) {
165458
166011
  summary = "Dream queued, but another worker is already processing the queue.";
166012
+ } else if (result.tasks.length === 0) {
166013
+ summary = "Dreamer is configured, but no dream tasks are enabled for this project.";
165459
166014
  } else {
165460
166015
  const taskLines = result.tasks.map((task) => {
165461
166016
  const status = task.error ? `error: ${task.error}` : "ok";
@@ -165848,6 +166403,57 @@ function createTagger() {
165848
166403
  };
165849
166404
  }
165850
166405
 
166406
+ // ../plugin/src/features/magic-context/work-metrics.ts
166407
+ function asNumber2(value) {
166408
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
166409
+ }
166410
+ function getPiUsage(entry) {
166411
+ if (!entry || typeof entry !== "object")
166412
+ return null;
166413
+ const record2 = entry;
166414
+ const message = record2.message && typeof record2.message === "object" ? record2.message : record2;
166415
+ if (message.role !== "assistant")
166416
+ return null;
166417
+ if (!message.usage || typeof message.usage !== "object")
166418
+ return null;
166419
+ const usage = message.usage;
166420
+ return {
166421
+ input: asNumber2(usage.input),
166422
+ output: asNumber2(usage.output),
166423
+ cacheRead: asNumber2(usage.cacheRead ?? usage.cache_read),
166424
+ cacheWrite: asNumber2(usage.cacheWrite ?? usage.cache_write)
166425
+ };
166426
+ }
166427
+ function computePiWorkMetrics(sessionEntries) {
166428
+ let previousPrompt = 0;
166429
+ let phasePeak = 0;
166430
+ let newWorkTokens = 0;
166431
+ let totalInputTokens = 0;
166432
+ let lastOutput = 0;
166433
+ let sawAssistant = false;
166434
+ for (const entry of sessionEntries) {
166435
+ const usage = getPiUsage(entry);
166436
+ if (!usage)
166437
+ continue;
166438
+ const prompt = usage.input + usage.cacheRead + usage.cacheWrite;
166439
+ if (sawAssistant && prompt < previousPrompt) {
166440
+ totalInputTokens += phasePeak;
166441
+ phasePeak = prompt;
166442
+ } else {
166443
+ phasePeak = Math.max(phasePeak, prompt);
166444
+ }
166445
+ newWorkTokens += Math.max(0, prompt - previousPrompt);
166446
+ previousPrompt = prompt;
166447
+ lastOutput = usage.output;
166448
+ sawAssistant = true;
166449
+ }
166450
+ if (sawAssistant) {
166451
+ totalInputTokens += phasePeak;
166452
+ newWorkTokens += lastOutput;
166453
+ }
166454
+ return { newWorkTokens, totalInputTokens };
166455
+ }
166456
+
165851
166457
  // ../plugin/src/hooks/magic-context/apply-operations.ts
165852
166458
  var USER_DROP_PREVIEW_CHARS = 250;
165853
166459
  function buildReplacementContent(tagId, target) {
@@ -167787,7 +168393,7 @@ function stripNestedSystemReminders(text) {
167787
168393
  return result;
167788
168394
  }
167789
168395
  function extractUserPromptText(message) {
167790
- return stripNestedSystemReminders(collectUserPromptParts(message)).replace(/§\d+§\s*/g, "").replace(/<!--\s*\+[\d\s.hmdw]+\s*-->/g, "").replace(/<!--\s*OMO_INTERNAL_INITIATOR[\s\S]*?-->/g, "").replace(/<!--\s*ALFONSO_INTERNAL_INITIATOR[\s\S]*?-->/g, "").replace(/<ctx-search-hint>[\s\S]*?<\/ctx-search-hint>/g, "").replace(/<ctx-search-auto>[\s\S]*?<\/ctx-search-auto>/g, "").replace(/<instruction[^>]*>[\s\S]*?<\/instruction>/g, "").replace(/<sidekick-augmentation>[\s\S]*?<\/sidekick-augmentation>/g, "").replace(/[ \t]+\n/g, `
168396
+ return stripNestedSystemReminders(collectUserPromptParts(message)).replace(/<!--[\s\S]*?-->/g, "").replace(/<ctx-search-hint>[\s\S]*?<\/ctx-search-hint>/g, "").replace(/<ctx-search-auto>[\s\S]*?<\/ctx-search-auto>/g, "").replace(/<instruction[^>]*>[\s\S]*?<\/instruction>/g, "").replace(/<sidekick-augmentation>[\s\S]*?<\/sidekick-augmentation>/g, "").replace(/<\/?[a-zA-Z][^<>]*>/g, "").replace(/§\d+§\s*/g, "").replace(/[ \t]+\n/g, `
167791
168397
  `).replace(/\n{3,}/g, `
167792
168398
 
167793
168399
  `).trim();
@@ -167958,18 +168564,28 @@ function applyDeferredPiCompactionMarker(deps, sessionId, pending) {
167958
168564
  return { kind: "stale-skip", reason: "target-superseded" };
167959
168565
  }
167960
168566
  const branchEntries = deps.readBranchEntries();
167961
- if (!branchEntries.some((entry) => getEntryId(entry) === pending.firstKeptEntryId)) {
168567
+ const pendingFirstKeptIndex = findEntryIndex(branchEntries, pending.firstKeptEntryId);
168568
+ if (pendingFirstKeptIndex < 0) {
167962
168569
  return { kind: "stale-skip", reason: "entry-removed" };
167963
168570
  }
167964
168571
  const latestFirstKept = findLatestCompactionFirstKept(branchEntries);
167965
- if (latestFirstKept === pending.firstKeptEntryId) {
167966
- return { kind: "already-current" };
168572
+ if (latestFirstKept !== null) {
168573
+ const latestFirstKeptIndex = findEntryIndex(branchEntries, latestFirstKept);
168574
+ if (latestFirstKeptIndex >= pendingFirstKeptIndex) {
168575
+ return { kind: "already-current" };
168576
+ }
167967
168577
  }
167968
- deps.appendCompaction(pending.summary, pending.firstKeptEntryId, pending.tokensBefore, {
168578
+ const compactionId = deps.appendCompaction(pending.summary, pending.firstKeptEntryId, pending.tokensBefore, {
167969
168579
  source: "magic-context",
167970
168580
  lastCompactedOrdinal: pending.ordinal
167971
168581
  }, true);
167972
- sessionLog(sessionId, `Pi compaction-marker drain: applied firstKept=${pending.firstKeptEntryId} tokensBefore=${pending.tokensBefore}`);
168582
+ if (typeof compactionId !== "string" || compactionId.length === 0) {
168583
+ return {
168584
+ kind: "retryable-failure",
168585
+ error: new Error("Pi appendCompaction returned no compaction id")
168586
+ };
168587
+ }
168588
+ sessionLog(sessionId, `Pi compaction-marker drain: applied compactionId=${compactionId} firstKept=${pending.firstKeptEntryId} endMessageId=${pending.endMessageId} ordinal=${pending.ordinal} tokensBefore=${pending.tokensBefore}`);
167973
168589
  return { kind: "applied", firstKeptEntryId: pending.firstKeptEntryId };
167974
168590
  } catch (err) {
167975
168591
  const error51 = err instanceof Error ? err : new Error(String(err));
@@ -167995,6 +168611,9 @@ function getEntryId(entry) {
167995
168611
  const id = entry.id;
167996
168612
  return typeof id === "string" ? id : null;
167997
168613
  }
168614
+ function findEntryIndex(branchEntries, entryId) {
168615
+ return branchEntries.findIndex((entry) => getEntryId(entry) === entryId);
168616
+ }
167998
168617
 
167999
168618
  // src/detect-recent-commit.ts
168000
168619
  var COMMIT_LOOKBACK = 5;
@@ -168347,36 +168966,70 @@ function trimPiMessagesToBoundary(piMessages, entryIds, cutoffMessageId) {
168347
168966
  }
168348
168967
  if (cutoffIndex < 0)
168349
168968
  return 0;
168350
- const removedCallIds = new Set;
168351
- for (let i = 0;i <= cutoffIndex; i++) {
168352
- const msg = piMessages[i];
168353
- if (!msg || msg.role !== "assistant")
168354
- continue;
168355
- const assistantMsg = msg;
168356
- if (!Array.isArray(assistantMsg.content))
168357
- continue;
168358
- for (const part of assistantMsg.content) {
168359
- if (part && typeof part === "object" && part.type === "toolCall" && typeof part.id === "string") {
168360
- removedCallIds.add(part.id);
168969
+ const remove = new Set;
168970
+ for (let i = 0;i <= cutoffIndex; i++)
168971
+ remove.add(i);
168972
+ let changed = true;
168973
+ while (changed) {
168974
+ changed = false;
168975
+ const removedCallIds = new Set;
168976
+ const removedResultIds = new Set;
168977
+ for (const index of remove) {
168978
+ const msg = piMessages[index];
168979
+ if (!msg)
168980
+ continue;
168981
+ if (msg.role === "assistant") {
168982
+ for (const callId of getPiToolCallIds(msg))
168983
+ removedCallIds.add(callId);
168984
+ } else if (msg.role === "toolResult") {
168985
+ const callId = getPiToolResultCallId(msg);
168986
+ if (callId)
168987
+ removedResultIds.add(callId);
168361
168988
  }
168362
168989
  }
168363
- }
168364
- if (removedCallIds.size > 0) {
168365
- let i = cutoffIndex + 1;
168366
- while (i < piMessages.length) {
168990
+ for (let i = 0;i < piMessages.length; i++) {
168991
+ if (remove.has(i))
168992
+ continue;
168367
168993
  const msg = piMessages[i];
168368
- if (msg && msg.role === "toolResult" && typeof msg.toolCallId === "string" && removedCallIds.has(msg.toolCallId)) {
168369
- cutoffIndex = i;
168370
- i++;
168371
- } else {
168372
- break;
168994
+ if (!msg)
168995
+ continue;
168996
+ if (msg.role === "toolResult") {
168997
+ const callId = getPiToolResultCallId(msg);
168998
+ if (callId && removedCallIds.has(callId)) {
168999
+ remove.add(i);
169000
+ changed = true;
169001
+ }
169002
+ continue;
169003
+ }
169004
+ if (msg.role === "assistant") {
169005
+ const callIds = getPiToolCallIds(msg);
169006
+ if (callIds.some((callId) => removedResultIds.has(callId))) {
169007
+ remove.add(i);
169008
+ changed = true;
169009
+ }
168373
169010
  }
168374
169011
  }
168375
169012
  }
168376
- const removed = cutoffIndex + 1;
168377
- piMessages.splice(0, removed);
169013
+ const kept = piMessages.filter((_, index) => !remove.has(index));
169014
+ const removed = piMessages.length - kept.length;
169015
+ piMessages.splice(0, piMessages.length, ...kept);
168378
169016
  return removed;
168379
169017
  }
169018
+ function getPiToolCallIds(message) {
169019
+ if (!Array.isArray(message.content))
169020
+ return [];
169021
+ const ids = [];
169022
+ for (const part of message.content) {
169023
+ if (part && typeof part === "object" && part.type === "toolCall" && typeof part.id === "string") {
169024
+ ids.push(part.id);
169025
+ }
169026
+ }
169027
+ return ids;
169028
+ }
169029
+ function getPiToolResultCallId(message) {
169030
+ const callId = message.toolCallId;
169031
+ return typeof callId === "string" && callId.length > 0 ? callId : null;
169032
+ }
168380
169033
  function injectHistoryBlockIntoFirstUserMessage(piMessages, historyBlock) {
168381
169034
  for (let i = 0;i < piMessages.length; i++) {
168382
169035
  const msg = piMessages[i];
@@ -168468,6 +169121,7 @@ ${prepared.block}
168468
169121
  // src/note-visibility-pi.ts
168469
169122
  var NOTE_TOOL_NAME = "ctx_note";
168470
169123
  var READ_ACTION = "read";
169124
+ var WHOLE_MESSAGE_PLACEHOLDER_TEXT = "[dropped]";
168471
169125
  function hasVisibleNoteReadCallPi(messages) {
168472
169126
  for (let i = messages.length - 1;i >= 0; i--) {
168473
169127
  const raw = messages[i];
@@ -168479,6 +169133,8 @@ function hasVisibleNoteReadCallPi(messages) {
168479
169133
  if (!Array.isArray(msg.content))
168480
169134
  continue;
168481
169135
  for (const part of msg.content) {
169136
+ if (isSentinelPart(part))
169137
+ continue;
168482
169138
  if (!part || typeof part !== "object")
168483
169139
  continue;
168484
169140
  const p = part;
@@ -168496,6 +169152,12 @@ function hasVisibleNoteReadCallPi(messages) {
168496
169152
  }
168497
169153
  return false;
168498
169154
  }
169155
+ function isSentinelPart(part) {
169156
+ if (!part || typeof part !== "object")
169157
+ return false;
169158
+ const p = part;
169159
+ return p.type === "text" && typeof p.text === "string" && (p.text === "" || p.text === WHOLE_MESSAGE_PLACEHOLDER_TEXT);
169160
+ }
168499
169161
 
168500
169162
  // src/nudge-injector.ts
168501
169163
  var INSTRUCTION_NAME_PATTERN = /<instruction\s+name="([^"]+)">/;
@@ -168553,12 +169215,12 @@ function createAssistantNudgeMessage(text, messages) {
168553
169215
  function findLatestAssistantMessage(messages) {
168554
169216
  for (let i = messages.length - 1;i >= 0; i--) {
168555
169217
  const message = messages[i];
168556
- if (isAssistantMessage(message))
169218
+ if (isAssistantMessage2(message))
168557
169219
  return message;
168558
169220
  }
168559
169221
  return;
168560
169222
  }
168561
- function isAssistantMessage(message) {
169223
+ function isAssistantMessage2(message) {
168562
169224
  return getRole(message) === "assistant";
168563
169225
  }
168564
169226
  function createZeroUsage() {
@@ -169060,6 +169722,8 @@ async function runPiCompressionPassIfNeeded(deps) {
169060
169722
  sessionLog(sessionId, `compressor: history block ~${totalTokens} tokens within budget ${historyBudgetTokens}, skipping`);
169061
169723
  return false;
169062
169724
  }
169725
+ const overage = totalTokens - historyBudgetTokens;
169726
+ sessionLog(sessionId, `compressor: history block ~${totalTokens} tokens exceeds budget ${historyBudgetTokens} by ~${overage} tokens`);
169063
169727
  const lastEndMessage = compartments[compartments.length - 1]?.endMessage ?? 0;
169064
169728
  const floor = Math.max(1, Math.ceil(lastEndMessage / minCompartmentRatio));
169065
169729
  const floorHeadroom = compartments.length - floor;
@@ -169068,6 +169732,17 @@ async function runPiCompressionPassIfNeeded(deps) {
169068
169732
  return false;
169069
169733
  }
169070
169734
  const scored = scoreCompartments(db, sessionId, compartments);
169735
+ const depthHistogram = new Map;
169736
+ for (const scoredCompartment of scored) {
169737
+ const bucket = Math.round(scoredCompartment.averageDepth);
169738
+ depthHistogram.set(bucket, (depthHistogram.get(bucket) ?? 0) + 1);
169739
+ }
169740
+ const histText = [...depthHistogram.entries()].sort((a, b) => a[0] - b[0]).map(([depth, count]) => `d${depth}=${count}`).join(" ");
169741
+ const histKey = `${scored.length}|${histText}`;
169742
+ if (lastDepthHistogramBySession.get(sessionId) !== histKey) {
169743
+ lastDepthHistogramBySession.set(sessionId, histKey);
169744
+ sessionLog(sessionId, `compressor: depth histogram (${scored.length} total) ${histText}`);
169745
+ }
169071
169746
  const band = selectPiCompressionBand(scored, {
169072
169747
  maxPickable: maxCompartmentsPerPass,
169073
169748
  maxMergeDepth,
@@ -169086,7 +169761,7 @@ async function runPiCompressionPassIfNeeded(deps) {
169086
169761
  const outputDepth = Math.min(5, Math.max(1, Math.round(avgDepth) + 1));
169087
169762
  const mergeRatio = COMPRESSOR_MERGE_RATIO_BY_DEPTH[outputDepth] ?? 2;
169088
169763
  const outputCount = mergeRatio > 0 ? Math.max(1, Math.ceil(band.length / mergeRatio)) : 1;
169089
- sessionLog(sessionId, `compressor: picked ${band.length} compartments (${selectedCompartments[0]?.startMessage}-${selectedCompartments[selectedCompartments.length - 1]?.endMessage}, ~${selectedTokens} tokens), avg_depth=${avgDepth.toFixed(1)} → output_depth=${outputDepth}`);
169764
+ sessionLog(sessionId, `compressor: scored ${compartments.length}, picked ${band.length} contiguous (${selectedCompartments[0]?.startMessage}-${selectedCompartments[selectedCompartments.length - 1]?.endMessage}, ~${selectedTokens} tokens), avg_depth=${avgDepth.toFixed(1)} → output_depth=${outputDepth} (ratio=${mergeRatio}, target=${outputCount} compartments)`);
169090
169765
  if (outputDepth === 5) {
169091
169766
  const compressed = selectedCompartments.map((c) => ({
169092
169767
  startMessage: c.startMessage,
@@ -169106,7 +169781,8 @@ async function runPiCompressionPassIfNeeded(deps) {
169106
169781
  originalStart: selectedCompartments[0]?.startMessage ?? 0,
169107
169782
  originalEnd: selectedCompartments[selectedCompartments.length - 1]?.endMessage ?? 0,
169108
169783
  facts,
169109
- logLabel: `depth-5 title-only collapse (${selectedCompartments.length} → ${compressed.length})`
169784
+ logLabel: `depth-5 title-only collapse (${selectedCompartments.length} → ${compressed.length})`,
169785
+ holderId: deps.compartmentLeaseHolderId
169110
169786
  });
169111
169787
  if (ok2)
169112
169788
  onPublished?.();
@@ -169137,7 +169813,8 @@ async function runPiCompressionPassIfNeeded(deps) {
169137
169813
  originalStart: selectedCompartments[0]?.startMessage ?? 0,
169138
169814
  originalEnd: selectedCompartments[selectedCompartments.length - 1]?.endMessage ?? 0,
169139
169815
  facts,
169140
- logLabel: `depth-${outputDepth} (${selectedCompartments.length} → ${finalCompressed.length})`
169816
+ logLabel: `depth-${outputDepth} (${selectedCompartments.length} → ${finalCompressed.length})`,
169817
+ holderId: deps.compartmentLeaseHolderId
169141
169818
  });
169142
169819
  if (ok)
169143
169820
  onPublished?.();
@@ -169154,7 +169831,9 @@ async function runCompressorPass(args) {
169154
169831
  fallbackModels: args.fallbackModels,
169155
169832
  timeoutMs: args.historianTimeoutMs ?? DEFAULT_HISTORIAN_TIMEOUT_MS,
169156
169833
  cwd: args.directory,
169157
- thinkingLevel: args.thinkingLevel
169834
+ thinkingLevel: args.thinkingLevel,
169835
+ accountingSessionId: args.sessionId,
169836
+ accountingSubagent: "compressor"
169158
169837
  });
169159
169838
  if (!result.ok) {
169160
169839
  sessionLog(args.sessionId, `compressor: subagent failed (${result.reason}): ${result.error}`);
@@ -169246,12 +169925,20 @@ function finalizeCompression(args) {
169246
169925
  sequence: leading.length + compressed.length + i
169247
169926
  }))
169248
169927
  ];
169249
- replaceAllCompartmentState(db, sessionId, allCompartments, args.facts);
169250
- incrementCompressionDepth(db, sessionId, originalStart, originalEnd);
169928
+ const published = args.holderId ? replaceAllCompartmentStateAndBumpDepth(db, args.holderId, sessionId, allCompartments, args.facts, originalStart, originalEnd) : (() => {
169929
+ replaceAllCompartmentState(db, sessionId, allCompartments, args.facts);
169930
+ incrementCompressionDepth(db, sessionId, originalStart, originalEnd);
169931
+ return true;
169932
+ })();
169933
+ if (!published) {
169934
+ sessionLog(sessionId, "compressor: publish skipped because compartment lease is no longer held");
169935
+ return false;
169936
+ }
169251
169937
  sessionLog(sessionId, `compressor: completed ${args.logLabel}`);
169252
169938
  return true;
169253
169939
  }
169254
169940
  var lastCompressorRunBySession = new Map;
169941
+ var lastDepthHistogramBySession = new Map;
169255
169942
  function isPiCompressorOnCooldown(sessionId, cooldownMs) {
169256
169943
  const last = lastCompressorRunBySession.get(sessionId);
169257
169944
  return last !== undefined && Date.now() - last < cooldownMs;
@@ -169261,6 +169948,7 @@ function markPiCompressorRun(sessionId) {
169261
169948
  }
169262
169949
  function clearPiCompressorState(sessionId) {
169263
169950
  lastCompressorRunBySession.delete(sessionId);
169951
+ lastDepthHistogramBySession.delete(sessionId);
169264
169952
  }
169265
169953
 
169266
169954
  // ../plugin/src/hooks/magic-context/compartment-runner-drop-queue.ts
@@ -169491,27 +170179,27 @@ function getReducedRecompTokenBudget(currentBudget) {
169491
170179
 
169492
170180
  // ../plugin/src/hooks/magic-context/historian-state-file.ts
169493
170181
  init_data_path();
169494
- import { mkdirSync as mkdirSync5, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
169495
- import { join as join14 } from "node:path";
170182
+ import { mkdirSync as mkdirSync6, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
170183
+ import { join as join15 } from "node:path";
169496
170184
  var HISTORIAN_STATE_INLINE_THRESHOLD = 30000;
169497
170185
  function maybeWriteHistorianStateFile(sessionId, existingState, directory) {
169498
170186
  if (existingState.length <= HISTORIAN_STATE_INLINE_THRESHOLD)
169499
170187
  return;
169500
170188
  try {
169501
170189
  const dir = getProjectMagicContextHistorianDir(directory);
169502
- mkdirSync5(dir, { recursive: true });
169503
- const path5 = join14(dir, `state-${sessionId}-${Date.now()}.xml`);
169504
- writeFileSync2(path5, existingState, "utf8");
169505
- return path5;
170190
+ mkdirSync6(dir, { recursive: true });
170191
+ const path6 = join15(dir, `state-${sessionId}-${Date.now()}.xml`);
170192
+ writeFileSync3(path6, existingState, "utf8");
170193
+ return path6;
169506
170194
  } catch {
169507
170195
  return;
169508
170196
  }
169509
170197
  }
169510
- function cleanupHistorianStateFile(path5) {
169511
- if (!path5)
170198
+ function cleanupHistorianStateFile(path6) {
170199
+ if (!path6)
169512
170200
  return;
169513
170201
  try {
169514
- unlinkSync(path5);
170202
+ unlinkSync(path6);
169515
170203
  } catch {}
169516
170204
  }
169517
170205
 
@@ -169628,7 +170316,7 @@ function convertEntriesToRawMessages(entries) {
169628
170316
  if (pendingToolParts.length > 0) {
169629
170317
  result.push({
169630
170318
  ordinal: nextOrdinal++,
169631
- id: pendingFirstRealId,
170319
+ id: `synth-user-${pendingFirstRealId}`,
169632
170320
  role: "user",
169633
170321
  parts: pendingToolParts
169634
170322
  });
@@ -169653,7 +170341,7 @@ function convertEntriesToRawMessages(entries) {
169653
170341
  if (pendingToolParts.length > 0) {
169654
170342
  result.push({
169655
170343
  ordinal: nextOrdinal,
169656
- id: pendingFirstRealId,
170344
+ id: `synth-user-${pendingFirstRealId}`,
169657
170345
  role: "user",
169658
170346
  parts: pendingToolParts
169659
170347
  });
@@ -169776,6 +170464,7 @@ async function runPiHistorian(deps) {
169776
170464
  memoryEnabled,
169777
170465
  autoPromote,
169778
170466
  onPublished,
170467
+ compartmentLeaseHolderId,
169779
170468
  readBranchEntries,
169780
170469
  notifyIssue
169781
170470
  } = deps;
@@ -169802,23 +170491,27 @@ async function runPiHistorian(deps) {
169802
170491
  const existingValidationError = validateStoredCompartments(priorCompartments);
169803
170492
  if (existingValidationError) {
169804
170493
  sessionLog(sessionId, `historian failure: source=existing-validation reason="${existingValidationError}"`);
170494
+ incrementHistorianFailure(db, sessionId, existingValidationError);
169805
170495
  await notify(`Historian skipped: existing stored compartments are invalid: ${existingValidationError}`);
169806
170496
  return;
169807
170497
  }
169808
170498
  const offset = priorCompartments.length > 0 ? priorCompartments[priorCompartments.length - 1].endMessage + 1 : 1;
169809
170499
  const protectedTailStart = getProtectedTailStartOrdinal(sessionId);
169810
170500
  if (protectedTailStart <= offset) {
169811
- sessionLog(sessionId, `historian skip: protected tail covers all eligible history (offset=${offset}, protectedStart=${protectedTailStart})`);
170501
+ sessionLog(sessionId, `historian no-op: protectedTailStart=${protectedTailStart} <= offset=${offset} — nothing to compact`);
170502
+ clearEmergencyRecovery(db, sessionId);
169812
170503
  return;
169813
170504
  }
169814
170505
  const chunk = readSessionChunk(sessionId, historianChunkTokens, offset, protectedTailStart);
169815
170506
  if (!chunk.text || chunk.messageCount === 0) {
169816
- sessionLog(sessionId, `historian skip: empty chunk (offset=${offset}, protectedStart=${protectedTailStart})`);
170507
+ sessionLog(sessionId, `historian no-op: chunk empty after filtering (messageCount=${chunk.messageCount}, textLen=${chunk.text?.length ?? 0}) range=${offset}-${protectedTailStart - 1}`);
170508
+ clearEmergencyRecovery(db, sessionId);
169817
170509
  return;
169818
170510
  }
169819
170511
  const chunkCoverageError = validateChunkCoverage(chunk);
169820
170512
  if (chunkCoverageError) {
169821
170513
  sessionLog(sessionId, `historian failure: source=chunk-coverage reason="${chunkCoverageError}" chunkRange=${chunk.startIndex}-${chunk.endIndex}`);
170514
+ incrementHistorianFailure(db, sessionId, chunkCoverageError);
169822
170515
  await notify(`Historian skipped: raw chunk could not be safely chunked: ${chunkCoverageError}`);
169823
170516
  return;
169824
170517
  }
@@ -169884,7 +170577,9 @@ ${chunk.text}`, { stateFilePath });
169884
170577
  timeoutMs: historianTimeoutMs,
169885
170578
  cwd: directory,
169886
170579
  thinkingLevel,
169887
- onProgress: buildProgressLogger("first")
170580
+ onProgress: buildProgressLogger("first"),
170581
+ accountingSessionId: sessionId,
170582
+ accountingSubagent: "historian"
169888
170583
  });
169889
170584
  let validatedPass = await validateHistorianResult(firstResult, sessionId, chunk, priorCompartments, sequenceOffset);
169890
170585
  let validatedDraftText = firstResult.ok ? firstResult.assistantText : null;
@@ -169900,7 +170595,9 @@ ${chunk.text}`, { stateFilePath });
169900
170595
  timeoutMs: historianTimeoutMs,
169901
170596
  cwd: directory,
169902
170597
  thinkingLevel,
169903
- onProgress: buildProgressLogger("repair")
170598
+ onProgress: buildProgressLogger("repair"),
170599
+ accountingSessionId: sessionId,
170600
+ accountingSubagent: "historian"
169904
170601
  });
169905
170602
  validatedPass = await validateHistorianResult(repairResult, sessionId, chunk, priorCompartments, sequenceOffset);
169906
170603
  if (validatedPass.kind === "ok" && repairResult.ok) {
@@ -169923,11 +170620,12 @@ ${chunk.text}`, { stateFilePath });
169923
170620
  systemPrompt: HISTORIAN_EDITOR_SYSTEM_PROMPT,
169924
170621
  userMessage: buildHistorianEditorPrompt(draftAssistantText),
169925
170622
  model: historianModel,
169926
- fallbackModels,
169927
170623
  timeoutMs: historianTimeoutMs,
169928
170624
  cwd: directory,
169929
170625
  thinkingLevel,
169930
- onProgress: buildProgressLogger("editor")
170626
+ onProgress: buildProgressLogger("editor"),
170627
+ accountingSessionId: sessionId,
170628
+ accountingSubagent: "historian_editor"
169931
170629
  });
169932
170630
  const editorPass = await validateHistorianResult(editorResult, sessionId, chunk, priorCompartments, sequenceOffset);
169933
170631
  if (editorPass.kind === "ok") {
@@ -169961,10 +170659,30 @@ ${chunk.text}`, { stateFilePath });
169961
170659
  sessionLog(sessionId, `historian: native compaction queue lookup failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
169962
170660
  }
169963
170661
  }
169964
- db.transaction(() => {
170662
+ if (!compartmentLeaseHolderId) {
170663
+ sessionLog(sessionId, "historian publish skipped: missing compartment lease holder");
170664
+ return;
170665
+ }
170666
+ let published = false;
170667
+ db.exec("BEGIN IMMEDIATE");
170668
+ try {
170669
+ if (!isCompartmentLeaseHeld(db, sessionId, compartmentLeaseHolderId)) {
170670
+ db.exec("ROLLBACK");
170671
+ sessionLog(sessionId, "historian publish skipped: compartment lease no longer held");
170672
+ return;
170673
+ }
169965
170674
  appendCompartments(db, sessionId, newCompartments);
169966
170675
  replaceSessionFacts(db, sessionId, validatedPass.facts ?? []);
169967
170676
  clearHistorianFailureState(db, sessionId);
170677
+ clearEmergencyRecovery(db, sessionId);
170678
+ if (validatedPass.userObservations?.length) {
170679
+ insertUserMemoryCandidates(db, validatedPass.userObservations.map((obs) => ({
170680
+ content: obs,
170681
+ sessionId,
170682
+ sourceCompartmentStart: newCompartments[0]?.startMessage,
170683
+ sourceCompartmentEnd: lastNewEnd
170684
+ })));
170685
+ }
169968
170686
  if (firstKeptEntryId && lastNewEndMessageId) {
169969
170687
  setPendingPiCompactionMarkerState(db, sessionId, {
169970
170688
  firstKeptEntryId,
@@ -169975,13 +170693,24 @@ ${chunk.text}`, { stateFilePath });
169975
170693
  publishedAt: Date.now()
169976
170694
  });
169977
170695
  }
169978
- })();
170696
+ db.exec("COMMIT");
170697
+ published = true;
170698
+ } finally {
170699
+ if (!published) {
170700
+ try {
170701
+ db.exec("ROLLBACK");
170702
+ } catch {}
170703
+ }
170704
+ }
169979
170705
  onNoteTrigger(db, sessionId, "historian_complete");
169980
170706
  onPublished?.();
169981
170707
  if (memoryEnabled !== false && autoPromote !== false) {
169982
170708
  promoteSessionFactsToMemory(db, sessionId, projectPath, validatedPass.facts ?? []);
169983
170709
  }
169984
170710
  queueDropsForCompartmentalizedMessages(db, sessionId, lastNewEnd);
170711
+ if (validatedPass.userObservations?.length) {
170712
+ sessionLog(sessionId, `stored ${validatedPass.userObservations.length} user memory candidate(s)`);
170713
+ }
169985
170714
  sessionLog(sessionId, `historian: published ${newCompartments.length} compartment(s), ${validatedPass.facts?.length ?? 0} fact(s) covering messages ${chunk.startIndex}-${lastNewEnd}`);
169986
170715
  completedSuccessfully = true;
169987
170716
  });
@@ -170453,8 +171182,8 @@ function stripPiDroppedPlaceholderMessages(args) {
170453
171182
 
170454
171183
  // src/system-prompt.ts
170455
171184
  import { createHash as createHash8 } from "node:crypto";
170456
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "node:fs";
170457
- import { join as join16 } from "node:path";
171185
+ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
171186
+ import { join as join17 } from "node:path";
170458
171187
 
170459
171188
  // ../plugin/src/agents/magic-context-prompt.ts
170460
171189
  function getToolHistoryGuidance(dropToolStructure) {
@@ -170544,16 +171273,16 @@ Prefer many small targeted operations over one large blanket operation. Compress
170544
171273
  }
170545
171274
 
170546
171275
  // ../plugin/src/hooks/magic-context/key-files-block.ts
170547
- import { readFileSync as readFileSync7, realpathSync as realpathSync3 } from "node:fs";
170548
- import { join as join15, sep as sep2 } from "node:path";
171276
+ import { readFileSync as readFileSync8, realpathSync as realpathSync3 } from "node:fs";
171277
+ import { join as join16, sep as sep2 } from "node:path";
170549
171278
  init_logger();
170550
171279
  var cachedKeyFilesBySession = new Map;
170551
171280
  var staleUpdates = new Map;
170552
171281
  function staleKey(update) {
170553
171282
  return `${update.projectPath}\x00${update.path}\x00${update.generatedAtWitness}\x00${update.staleReason}`;
170554
171283
  }
170555
- function queueStaleUpdate(projectPath, path5, generatedAtWitness, staleReason) {
170556
- const update = { projectPath, path: path5, generatedAtWitness, staleReason };
171284
+ function queueStaleUpdate(projectPath, path6, generatedAtWitness, staleReason) {
171285
+ const update = { projectPath, path: path6, generatedAtWitness, staleReason };
170557
171286
  staleUpdates.set(staleKey(update), update);
170558
171287
  }
170559
171288
  function flushStaleUpdates(db) {
@@ -170598,13 +171327,13 @@ function buildKeyFilesBlock(db, projectPath, config2 = { enabled: true, tokenBud
170598
171327
  let nextStale = null;
170599
171328
  let observed = false;
170600
171329
  try {
170601
- const absPath = join15(projectPath, row.path);
171330
+ const absPath = join16(projectPath, row.path);
170602
171331
  const real = realpathSync3(absPath);
170603
171332
  if (!isUnderProject(projectPath, real)) {
170604
171333
  nextStale = "missing";
170605
171334
  observed = true;
170606
171335
  } else {
170607
- const diskHash = sha256(readFileSync7(real));
171336
+ const diskHash = sha256(readFileSync8(real));
170608
171337
  if (diskHash !== row.contentHash)
170609
171338
  nextStale = "content_drift";
170610
171339
  observed = true;
@@ -170681,10 +171410,10 @@ var DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
170681
171410
  function readProjectDocs(directory) {
170682
171411
  const sections = [];
170683
171412
  for (const filename of DOC_FILES) {
170684
- const filePath = join16(directory, filename);
171413
+ const filePath = join17(directory, filename);
170685
171414
  try {
170686
- if (existsSync11(filePath)) {
170687
- const content = readFileSync8(filePath, "utf-8").trim();
171415
+ if (existsSync12(filePath)) {
171416
+ const content = readFileSync9(filePath, "utf-8").trim();
170688
171417
  if (content.length > 0) {
170689
171418
  sections.push(`<${filename}>
170690
171419
  ${escapeXmlContent(content)}
@@ -171438,6 +172167,16 @@ var recentReduceBySession = new Map;
171438
172167
  var liveModelBySession = new Map;
171439
172168
  var toolUsageSinceUserTurn = new Map;
171440
172169
  var latestUserMessageBySession = new Map;
172170
+ var RECENT_REDUCE_TTL_MS = 5 * 60 * 1000;
172171
+ function hasRecentPiCtxReduceExecution(sessionId) {
172172
+ const recordedAt = recentReduceBySession.get(sessionId);
172173
+ if (recordedAt === undefined)
172174
+ return false;
172175
+ if (Date.now() - recordedAt <= RECENT_REDUCE_TTL_MS)
172176
+ return true;
172177
+ recentReduceBySession.delete(sessionId);
172178
+ return false;
172179
+ }
171441
172180
  function logTransformTiming(sessionId, stage, start, extra) {
171442
172181
  const elapsed = (performance.now() - start).toFixed(1);
171443
172182
  const suffix = extra ? ` ${extra}` : "";
@@ -171527,7 +172266,7 @@ function recordPiToolExecution(sessionId) {
171527
172266
  function onPiNewUserMessage(args) {
171528
172267
  const sessionMeta = getOrCreateSessionMeta(args.db, args.sessionId);
171529
172268
  const turnUsage = toolUsageSinceUserTurn.get(args.sessionId);
171530
- const agentAlreadyReduced = recentReduceBySession.has(args.sessionId);
172269
+ const agentAlreadyReduced = hasRecentPiCtxReduceExecution(args.sessionId);
171531
172270
  if (!sessionMeta.isSubagent && !agentAlreadyReduced && getPersistedStickyTurnReminder(args.db, args.sessionId) === null && turnUsage !== undefined && turnUsage >= TOOL_HEAVY_TURN_REMINDER_THRESHOLD) {
171532
172271
  setPersistedStickyTurnReminder(args.db, args.sessionId, TOOL_HEAVY_TURN_REMINDER_TEXT);
171533
172272
  }
@@ -171648,7 +172387,8 @@ function collectMessageEntryIdsByRef(ctx, messages, sessionId, preloadedBranchEn
171648
172387
  continue;
171649
172388
  }
171650
172389
  const fingerprint = piMessageEntryFingerprint(msg);
171651
- const fingerprintId = fingerprint ? entryIdsByFingerprint.get(fingerprint)?.find((candidate) => !consumedFingerprintIds.has(candidate)) : undefined;
172390
+ const fingerprintBucket = fingerprint ? entryIdsByFingerprint.get(fingerprint) : undefined;
172391
+ const fingerprintId = fingerprintBucket?.length === 1 && !consumedFingerprintIds.has(fingerprintBucket[0]) ? fingerprintBucket[0] : undefined;
171652
172392
  if (typeof fingerprintId === "string") {
171653
172393
  consumedFingerprintIds.add(fingerprintId);
171654
172394
  result[i] = fingerprintId;
@@ -171784,8 +172524,10 @@ function registerPiContextHandler(pi, options) {
171784
172524
  });
171785
172525
  clearHistorianFailureState(options.db, sessionId);
171786
172526
  clearPersistedReasoningWatermark(options.db, sessionId);
171787
- clearDetectedContextLimit(options.db, sessionId);
171788
- clearEmergencyRecovery(options.db, sessionId);
172527
+ if (modelChanged) {
172528
+ clearDetectedContextLimit(options.db, sessionId);
172529
+ clearEmergencyRecovery(options.db, sessionId);
172530
+ }
171789
172531
  sessionMetaForUsage.lastContextPercentage = 0;
171790
172532
  sessionMetaForUsage.lastInputTokens = 0;
171791
172533
  sessionMetaForUsage.observedSafeInputTokens = 0;
@@ -171855,6 +172597,10 @@ function registerPiContextHandler(pi, options) {
171855
172597
  setDeferredExecutePendingIfAbsent(options.db, sessionId, flagPayload);
171856
172598
  }
171857
172599
  schedulerDecision = midTurnAdjustedSchedulerDecision;
172600
+ const deferredExecutePending = peekDeferredExecutePending(options.db, sessionId);
172601
+ if (deferredExecutePending !== null && schedulerDecision === "defer" && !midTurn) {
172602
+ schedulerDecision = "execute";
172603
+ }
171858
172604
  sessionLog(sessionId, `[boundary-exec] base=${schedulerDecisionEarly} bypass=${bypassReason} midTurn=${midTurn} effective=${midTurnAdjustedSchedulerDecision} sideEffect=${sideEffect}`);
171859
172605
  const forceMaterialization = usagePercentage >= FORCE_MATERIALIZATION_PERCENTAGE;
171860
172606
  const isEmergency = usagePercentage >= EMERGENCY_BLOCK_PERCENTAGE;
@@ -171933,7 +172679,7 @@ function registerPiContextHandler(pi, options) {
171933
172679
  db: options.db,
171934
172680
  messages: outputMessages,
171935
172681
  entryIds: entryIds ?? null,
171936
- hasRecentReduceCall: recentReduceBySession.has(sessionId),
172682
+ hasRecentReduceCall: hasRecentPiCtxReduceExecution(sessionId),
171937
172683
  isCacheBustingPass: cacheBustingPass
171938
172684
  });
171939
172685
  } catch (err) {
@@ -171960,8 +172706,7 @@ function registerPiContextHandler(pi, options) {
171960
172706
  db: options.db,
171961
172707
  messages: outputMessages,
171962
172708
  projectIdentity,
171963
- entryIds: strictEntryIds,
171964
- isCacheBusting
172709
+ entryIds: strictEntryIds
171965
172710
  });
171966
172711
  } catch (err) {
171967
172712
  sessionLog(sessionId, `note nudges failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -172002,6 +172747,12 @@ function registerPiContextHandler(pi, options) {
172002
172747
  } catch (err) {
172003
172748
  sessionLog(sessionId, `synthetic todowrite injection failed: ${err instanceof Error ? err.message : String(err)}`);
172004
172749
  }
172750
+ try {
172751
+ const metrics = computePiWorkMetrics(outputMessages);
172752
+ setSessionWorkMetrics(options.db, sessionId, metrics.newWorkTokens, metrics.totalInputTokens);
172753
+ } catch (err) {
172754
+ sessionLog(sessionId, `work-metrics update failed: ${err instanceof Error ? err.message : String(err)}`);
172755
+ }
172005
172756
  logTransformTiming(sessionId, "postTransformPhase", tPostTransform);
172006
172757
  sessionLog(sessionId, `transform completed in ${(performance.now() - transformStartTime).toFixed(1)}ms (${outputMessages.length} messages, ${result.targetCount} targets, watermark: ${result.reasoningWatermark})`);
172007
172758
  clearLastTransformErrorIfSet(options.db, sessionId);
@@ -172084,6 +172835,13 @@ function resolveHistoryBudgetTokensForPi(args) {
172084
172835
  contextLimit: derivedLimit
172085
172836
  }) / 100) * historyBudgetPercentage);
172086
172837
  }
172838
+ function startPiCompartmentLeaseRenewal(db, sessionId, holderId) {
172839
+ return setInterval(() => {
172840
+ if (!renewCompartmentLease(db, sessionId, holderId)) {
172841
+ sessionLog(sessionId, "compartment lease renewal failed; publish will be skipped if holder is stale");
172842
+ }
172843
+ }, COMPARTMENT_LEASE_RENEWAL_MS);
172844
+ }
172087
172845
  function maybeFireCompressor(args) {
172088
172846
  const { ctx, sessionId, db, historian } = args;
172089
172847
  const compressor = historian.compressor;
@@ -172109,30 +172867,46 @@ function maybeFireCompressor(args) {
172109
172867
  });
172110
172868
  if (!historyBudgetTokens || historyBudgetTokens <= 0)
172111
172869
  return;
172112
- const runPromise = runPiCompressionPassIfNeeded({
172113
- db,
172114
- sessionId,
172115
- directory: ctx.cwd,
172116
- runner: historian.runner,
172117
- historianModel: historian.model,
172118
- fallbackModels: historian.fallbackModels,
172119
- historyBudgetTokens,
172120
- historianTimeoutMs: historian.timeoutMs,
172121
- thinkingLevel: historian.thinkingLevel,
172122
- minCompartmentRatio: compressor.minCompartmentRatio,
172123
- maxMergeDepth: compressor.maxMergeDepth,
172124
- maxCompartmentsPerPass: compressor.maxCompartmentsPerPass,
172125
- graceCompartments: compressor.graceCompartments,
172126
- onPublished: () => {
172127
- if (!isContextHandlerSessionActive(sessionId)) {
172128
- sessionLog(sessionId, "compressor publication ignored: session was cleared");
172129
- return;
172130
- }
172131
- signalPiDeferredHistoryRefresh(sessionId);
172132
- signalPiDeferredMaterialization(sessionId);
172133
- historian.onStatusChange?.(ctx, sessionId);
172870
+ const holderId = crypto2.randomUUID();
172871
+ const runPromise = (async () => {
172872
+ const lease2 = acquireCompartmentLease(db, sessionId, holderId);
172873
+ if (!lease2) {
172874
+ sessionLog(sessionId, "compressor skipped: compartment lease held by another process");
172875
+ return false;
172876
+ }
172877
+ const renewal = startPiCompartmentLeaseRenewal(db, sessionId, holderId);
172878
+ try {
172879
+ return await runPiCompressionPassIfNeeded({
172880
+ db,
172881
+ sessionId,
172882
+ directory: ctx.cwd,
172883
+ runner: historian.runner,
172884
+ historianModel: historian.model,
172885
+ fallbackModels: historian.fallbackModels,
172886
+ historyBudgetTokens,
172887
+ historianTimeoutMs: historian.timeoutMs,
172888
+ thinkingLevel: historian.thinkingLevel,
172889
+ minCompartmentRatio: compressor.minCompartmentRatio,
172890
+ maxMergeDepth: compressor.maxMergeDepth,
172891
+ maxCompartmentsPerPass: compressor.maxCompartmentsPerPass,
172892
+ graceCompartments: compressor.graceCompartments,
172893
+ compartmentLeaseHolderId: holderId,
172894
+ onPublished: () => {
172895
+ const sessionStillActive = isContextHandlerSessionActive(sessionId);
172896
+ signalPiDeferredHistoryRefresh(sessionId);
172897
+ signalPiDeferredMaterialization(sessionId);
172898
+ if (sessionStillActive) {
172899
+ historian.onStatusChange?.(ctx, sessionId);
172900
+ } else {
172901
+ sessionLog(sessionId, "compressor publication recorded after session clear; status callback skipped");
172902
+ }
172903
+ }
172904
+ });
172905
+ } finally {
172906
+ clearInterval(renewal);
172907
+ releaseCompartmentLease(db, sessionId, holderId);
172134
172908
  }
172135
- }).then((didPublish) => {
172909
+ })().then((didPublish) => {
172136
172910
  if (didPublish === true) {
172137
172911
  markPiCompressorRun(sessionId);
172138
172912
  }
@@ -172170,37 +172944,53 @@ function sendPiIgnoredNotification(ctx, message) {
172170
172944
  }
172171
172945
  function spawnPiHistorianRun(args) {
172172
172946
  const { ctx, sessionId, db, historian, provider: provider2, unregister } = args;
172173
- const runPromise = runPiHistorian({
172174
- db,
172175
- sessionId,
172176
- directory: ctx.cwd,
172177
- provider: provider2,
172178
- appendCompaction: resolvePiAppendCompaction(ctx),
172179
- readBranchEntries: resolvePiReadBranchEntries(ctx),
172180
- runner: historian.runner,
172181
- historianModel: historian.model,
172182
- fallbackModels: historian.fallbackModels,
172183
- historianChunkTokens: historian.historianChunkTokens,
172184
- historianTimeoutMs: historian.timeoutMs,
172185
- twoPass: historian.twoPass,
172186
- thinkingLevel: historian.thinkingLevel,
172187
- memoryEnabled: historian.memoryEnabled,
172188
- autoPromote: historian.autoPromote,
172189
- onPublished: () => {
172190
- if (!isContextHandlerSessionActive(sessionId)) {
172191
- sessionLog(sessionId, "historian publication ignored: session was cleared");
172192
- return;
172193
- }
172194
- try {
172195
- clearEmergencyRecovery(db, sessionId);
172196
- } catch (err) {
172197
- sessionLog(sessionId, `historian: clearEmergencyRecovery failed: ${err instanceof Error ? err.message : String(err)}`);
172198
- }
172199
- signalPiDeferredHistoryRefresh(sessionId);
172200
- signalPiDeferredMaterialization(sessionId);
172201
- historian.onStatusChange?.(ctx, sessionId);
172947
+ const holderId = crypto2.randomUUID();
172948
+ const runPromise = (async () => {
172949
+ const lease2 = acquireCompartmentLease(db, sessionId, holderId);
172950
+ if (!lease2) {
172951
+ sessionLog(sessionId, "historian skipped: compartment lease held by another process");
172952
+ return;
172202
172953
  }
172203
- }).finally(() => {
172954
+ const renewal = startPiCompartmentLeaseRenewal(db, sessionId, holderId);
172955
+ try {
172956
+ await runPiHistorian({
172957
+ db,
172958
+ sessionId,
172959
+ directory: ctx.cwd,
172960
+ provider: provider2,
172961
+ appendCompaction: resolvePiAppendCompaction(ctx),
172962
+ readBranchEntries: resolvePiReadBranchEntries(ctx),
172963
+ runner: historian.runner,
172964
+ historianModel: historian.model,
172965
+ fallbackModels: historian.fallbackModels,
172966
+ historianChunkTokens: historian.historianChunkTokens,
172967
+ historianTimeoutMs: historian.timeoutMs,
172968
+ twoPass: historian.twoPass,
172969
+ thinkingLevel: historian.thinkingLevel,
172970
+ memoryEnabled: historian.memoryEnabled,
172971
+ autoPromote: historian.autoPromote,
172972
+ compartmentLeaseHolderId: holderId,
172973
+ onPublished: () => {
172974
+ const sessionStillActive = isContextHandlerSessionActive(sessionId);
172975
+ try {
172976
+ clearEmergencyRecovery(db, sessionId);
172977
+ } catch (err) {
172978
+ sessionLog(sessionId, `historian: clearEmergencyRecovery failed: ${err instanceof Error ? err.message : String(err)}`);
172979
+ }
172980
+ signalPiDeferredHistoryRefresh(sessionId);
172981
+ signalPiDeferredMaterialization(sessionId);
172982
+ if (sessionStillActive) {
172983
+ historian.onStatusChange?.(ctx, sessionId);
172984
+ } else {
172985
+ sessionLog(sessionId, "historian publication recorded after session clear; status callback skipped");
172986
+ }
172987
+ }
172988
+ });
172989
+ } finally {
172990
+ clearInterval(renewal);
172991
+ releaseCompartmentLease(db, sessionId, holderId);
172992
+ }
172993
+ })().finally(() => {
172204
172994
  inFlightHistorian.delete(sessionId);
172205
172995
  unregister();
172206
172996
  if (isContextHandlerSessionActive(sessionId)) {
@@ -172221,12 +173011,11 @@ function resolvePiReadBranchEntries(ctx) {
172221
173011
  if (typeof sm?.getBranch !== "function")
172222
173012
  return;
172223
173013
  return () => {
172224
- try {
172225
- const entries = sm.getBranch?.call(sm);
172226
- return Array.isArray(entries) ? entries : [];
172227
- } catch {
172228
- return [];
173014
+ const entries = sm.getBranch?.call(sm);
173015
+ if (!Array.isArray(entries)) {
173016
+ throw new Error("Pi sessionManager.getBranch() did not return an array");
172229
173017
  }
173018
+ return entries;
172230
173019
  };
172231
173020
  }
172232
173021
  function maybeFireHistorian(args) {
@@ -172328,6 +173117,7 @@ Historian previously failed ${failureState.failureCount} time(s), so magic-conte
172328
173117
  async function runPipeline(args) {
172329
173118
  let executedWorkThisPass = false;
172330
173119
  let historyWasConsumedThisPass = false;
173120
+ let materializationSatisfiedThisPass = false;
172331
173121
  let suppressDeferredHistoryDrain = false;
172332
173122
  let casLost = false;
172333
173123
  const deferredHistoryWasPendingAtPassStart = deferredHistoryRefreshSessions.has(args.sessionId);
@@ -172349,7 +173139,7 @@ async function runPipeline(args) {
172349
173139
  return findLatestUserMessageIdPi(args.messages, ids)?.messageId ?? null;
172350
173140
  })();
172351
173141
  const alreadyRanHeuristicsThisTurn = currentTurnId !== null && lastHeuristicsTurnIdBySession.get(args.sessionId) === currentTurnId;
172352
- const shouldRunHeuristics = args.heuristics !== undefined && (args.forceMaterialization === true || args.schedulerDecision === "execute" && !alreadyRanHeuristicsThisTurn);
173142
+ const shouldRunHeuristics = args.heuristics !== undefined && (args.forceMaterialization === true || hasPendingMaterialization(args.sessionId) || deferredMaterializationSessions.has(args.sessionId) || args.schedulerDecision === "execute" && !alreadyRanHeuristicsThisTurn);
172353
173143
  const tTag = performance.now();
172354
173144
  const { targets } = tagTranscript(args.sessionId, transcript, args.tagger, args.db, {
172355
173145
  skipPrefixInjection: !args.ctxReduceEnabled
@@ -172372,22 +173162,26 @@ async function runPipeline(args) {
172372
173162
  const hasPendingMaterializeSignal = hasPendingMaterialization(args.sessionId);
172373
173163
  const deferredMaterializationWasPending = deferredMaterializationSessions.has(args.sessionId);
172374
173164
  const deferredHistoryRefreshWasPending = deferredHistoryWasPendingAtPassStart;
173165
+ const deferredExecuteWasPending = peekDeferredExecutePending(args.db, args.sessionId) !== null;
172375
173166
  const pendingOps = getPendingOps(args.db, args.sessionId);
172376
- const baseShouldApplyPendingOps = args.schedulerDecision === "execute" || args.forceMaterialization || hasPendingMaterializeSignal;
173167
+ const baseShouldApplyPendingOps = args.schedulerDecision === "execute" || args.forceMaterialization || hasPendingMaterializeSignal || deferredExecuteWasPending;
172377
173168
  const canConsumeDeferredLate = baseShouldApplyPendingOps || shouldRunHeuristics;
172378
173169
  const deferredMaterialize = canConsumeDeferredLate && deferredMaterializationWasPending;
172379
173170
  const deferredHistoryRefresh = canConsumeDeferredLate && deferredHistoryRefreshWasPending;
172380
173171
  const shouldApplyPendingOps = baseShouldApplyPendingOps || deferredMaterialize;
172381
173172
  if (shouldApplyPendingOps) {
172382
- const applyReason = hasPendingMaterializeSignal ? "explicit_flush" : deferredMaterialize ? "deferred_publication" : args.forceMaterialization ? "force_materialization" : `scheduler_execute (scheduler=${args.schedulerDecision})`;
173173
+ const applyReason = hasPendingMaterializeSignal ? "explicit_flush" : deferredExecuteWasPending ? "deferred_boundary_execute" : deferredMaterialize ? "deferred_publication" : args.forceMaterialization ? "force_materialization" : `scheduler_execute (scheduler=${args.schedulerDecision})`;
172383
173174
  sessionLog(args.sessionId, `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${pendingOps.length}, context=${args.contextUsage.percentage.toFixed(1)}%`);
172384
173175
  try {
172385
173176
  const tApplyPending = performance.now();
172386
173177
  applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags, undefined, pendingOps);
172387
173178
  logTransformTiming(args.sessionId, "applyPendingOperations", tApplyPending);
172388
173179
  executedWorkThisPass = true;
173180
+ materializationSatisfiedThisPass = true;
172389
173181
  if (hasPendingMaterializeSignal) {
172390
- consumePendingMaterialization(args.sessionId);
173182
+ if (args.heuristics === undefined) {
173183
+ consumePendingMaterialization(args.sessionId);
173184
+ }
172391
173185
  }
172392
173186
  if (deferredMaterialize) {
172393
173187
  consumeDeferredMaterialization(args.sessionId);
@@ -172467,6 +173261,9 @@ async function runPipeline(args) {
172467
173261
  }, activeTags);
172468
173262
  heuristicsExecuted = true;
172469
173263
  executedWorkThisPass = true;
173264
+ if (hasPendingMaterializeSignal) {
173265
+ consumePendingMaterialization(args.sessionId);
173266
+ }
172470
173267
  if (currentTurnId !== null) {
172471
173268
  lastHeuristicsTurnIdBySession.set(args.sessionId, currentTurnId);
172472
173269
  }
@@ -172501,7 +173298,9 @@ async function runPipeline(args) {
172501
173298
  }
172502
173299
  logTransformTiming(args.sessionId, "clearOldReasoning", tClearReasoning);
172503
173300
  logTransformTiming(args.sessionId, "watermarkCleanup", tClearReasoning);
172504
- executedWorkThisPass = true;
173301
+ if (combinedWatermark > prevWatermark || clearOutcome.cleared > 0 || stripOutcome.stripped > 0) {
173302
+ executedWorkThisPass = true;
173303
+ }
172505
173304
  } catch (err) {
172506
173305
  sessionLog(args.sessionId, `reasoning clearing failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
172507
173306
  }
@@ -172531,7 +173330,7 @@ async function runPipeline(args) {
172531
173330
  messages: args.messages,
172532
173331
  isCacheBusting: args.isCacheBusting
172533
173332
  });
172534
- const deferredHistoryDrainEligible = historyWasConsumedThisPass && deferredHistoryWasPendingAtPassStart && !suppressDeferredHistoryDrain && !casLost;
173333
+ const deferredHistoryDrainEligible = historyWasConsumedThisPass && materializationSatisfiedThisPass && (deferredHistoryWasPendingAtPassStart || hasPendingMaterializeSignal) && !suppressDeferredHistoryDrain && !casLost;
172535
173334
  if (deferredHistoryDrainEligible) {
172536
173335
  try {
172537
173336
  const pending = getPendingPiCompactionMarkerState(args.db, args.sessionId);
@@ -172621,7 +173420,7 @@ function applyStickyTurnReminder(args) {
172621
173420
  const messageIdByIndex = buildPiMessageIdByIndex(args.messages, args.entryIds);
172622
173421
  if (reminder.messageId) {
172623
173422
  const reinjected = appendReminderToUserMessageByIdPi(args.messages, messageIdByIndex, reminder.messageId, reminder.text);
172624
- if (!reinjected && args.isCacheBustingPass && args.entryIds !== null) {
173423
+ if (!reinjected && args.isCacheBustingPass && args.entryIds !== null && !args.entryIds.includes(undefined) && !args.entryIds.includes(reminder.messageId)) {
172625
173424
  clearPersistedStickyTurnReminder(args.db, args.sessionId);
172626
173425
  }
172627
173426
  return args.messages;
@@ -172638,14 +173437,15 @@ function applyStickyTurnReminder(args) {
172638
173437
  return args.messages;
172639
173438
  }
172640
173439
  function applyNoteNudges(args) {
172641
- const { sessionId, db, messages, projectIdentity, entryIds, isCacheBusting } = args;
173440
+ const { sessionId, db, messages, projectIdentity, entryIds } = args;
172642
173441
  const messageIdByIndex = buildPiMessageIdByIndex(messages, entryIds);
173442
+ const replayMessageIdByIndex = buildPiMessageIdByIndex(messages, entryIds, true);
172643
173443
  for (const anchor of getNoteNudgeAnchors(db, sessionId)) {
172644
- appendReminderToUserMessageByIdPi(messages, messageIdByIndex, anchor.messageId, anchor.text);
173444
+ appendReminderToUserMessageByIdPi(messages, replayMessageIdByIndex, anchor.messageId, anchor.text);
172645
173445
  }
172646
173446
  for (const decision of getAutoSearchHintDecisions(db, sessionId)) {
172647
173447
  if (decision.decision === "hint") {
172648
- appendReminderToUserMessageByIdPi(messages, messageIdByIndex, decision.messageId, decision.text);
173448
+ appendReminderToUserMessageByIdPi(messages, replayMessageIdByIndex, decision.messageId, decision.text);
172649
173449
  }
172650
173450
  }
172651
173451
  const latestUser = findLatestUserMessageIdPi(messages, messageIdByIndex);
@@ -172661,14 +173461,18 @@ function applyNoteNudges(args) {
172661
173461
 
172662
173462
  <instruction name="deferred_notes">${deferredNoteText}</instruction>`;
172663
173463
  const anchoredId = latestUser?.messageId ?? null;
173464
+ if (!anchoredId) {
173465
+ sessionLog(sessionId, "Pi note-nudge: latest user message has no resolved SessionEntry id; deferring delivery to next pass");
173466
+ return messages;
173467
+ }
172664
173468
  const outcome = markNoteNudgeDelivered(db, sessionId, noteInstruction, anchoredId);
172665
- if (latestUser && anchoredId && outcome.ok) {
173469
+ if (latestUser && outcome.ok) {
172666
173470
  appendReminderToPiUserMessage(messages[latestUser.index], noteInstruction);
172667
- } else if (anchoredId && !outcome.ok) {
173471
+ } else if (!outcome.ok) {
172668
173472
  sessionLog(sessionId, `Pi note-nudge delivery skipped wire append: ${outcome.kind}`);
172669
173473
  }
172670
173474
  }
172671
- if (isCacheBusting && entryIds !== null) {
173475
+ if (entryIds !== null && !entryIds.includes(undefined)) {
172672
173476
  const visibleIds = new Set(entryIds.filter((id) => typeof id === "string"));
172673
173477
  pruneNoteNudgeAnchors(db, sessionId, visibleIds);
172674
173478
  pruneAutoSearchHintDecisions(db, sessionId, visibleIds);
@@ -172700,7 +173504,7 @@ function hasMeaningfulUserTextPi(message) {
172700
173504
  }
172701
173505
  return false;
172702
173506
  }
172703
- function buildPiMessageIdByIndex(messages, entryIds) {
173507
+ function buildPiMessageIdByIndex(messages, entryIds, includeMessageIdFallback = false) {
172704
173508
  const ids = new Map;
172705
173509
  for (let index = 0;index < messages.length; index += 1) {
172706
173510
  const entryId = entryIds?.[index];
@@ -172708,9 +173512,11 @@ function buildPiMessageIdByIndex(messages, entryIds) {
172708
173512
  ids.set(index, entryId);
172709
173513
  continue;
172710
173514
  }
172711
- const messageId = messages[index]?.id;
172712
- if (typeof messageId === "string") {
172713
- ids.set(index, messageId);
173515
+ if (includeMessageIdFallback) {
173516
+ const messageId = messages[index]?.id;
173517
+ if (typeof messageId === "string") {
173518
+ ids.set(index, messageId);
173519
+ }
172714
173520
  }
172715
173521
  }
172716
173522
  return ids;
@@ -172841,7 +173647,7 @@ init_logger();
172841
173647
  init_data_path();
172842
173648
  init_logger();
172843
173649
  await init_sqlite();
172844
- import { join as join17 } from "node:path";
173650
+ import { join as join18 } from "node:path";
172845
173651
  var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
172846
173652
  function randomBase62(length) {
172847
173653
  const chars = [];
@@ -172862,7 +173668,7 @@ function generatePartId(timestampMs, counter = 0n) {
172862
173668
  return generateId("prt", timestampMs, counter);
172863
173669
  }
172864
173670
  function getOpenCodeDbPath3() {
172865
- return join17(getDataDir(), "opencode", "opencode.db");
173671
+ return join18(getDataDir(), "opencode", "opencode.db");
172866
173672
  }
172867
173673
  var cachedWriteDb = null;
172868
173674
  var REQUIRED_MESSAGE_COLUMNS = ["id", "session_id", "time_created", "time_updated", "data"];
@@ -173031,7 +173837,7 @@ function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEn
173031
173837
  // ../plugin/src/hooks/magic-context/compartment-runner-compressor.ts
173032
173838
  init_logger();
173033
173839
  var HISTORIAN_AGENT2 = "historian";
173034
- var lastDepthHistogramBySession = new Map;
173840
+ var lastDepthHistogramBySession2 = new Map;
173035
173841
  function cavemanLevelForDepth2(outputDepth) {
173036
173842
  if (outputDepth <= 1)
173037
173843
  return null;
@@ -173045,6 +173851,10 @@ function cavemanLevelForDepth2(outputDepth) {
173045
173851
  }
173046
173852
  async function runCompressionPassIfNeeded(deps) {
173047
173853
  const { db, sessionId, historyBudgetTokens } = deps;
173854
+ if (deps.historianRunnable === false) {
173855
+ sessionLog(sessionId, "compressor: skipped because historian.disable=true");
173856
+ return false;
173857
+ }
173048
173858
  const minCompartmentRatio = deps.minCompartmentRatio ?? DEFAULT_COMPRESSOR_MIN_COMPARTMENT_RATIO;
173049
173859
  const maxMergeDepth = deps.maxMergeDepth ?? DEFAULT_COMPRESSOR_MAX_MERGE_DEPTH;
173050
173860
  const compartments = getCompartments(db, sessionId);
@@ -173084,8 +173894,8 @@ ${c.content}
173084
173894
  }
173085
173895
  const histText = [...depthHistogram.entries()].sort((a, b) => a[0] - b[0]).map(([d, n]) => `d${d}=${n}`).join(" ");
173086
173896
  const histKey = `${scored.length}|${histText}`;
173087
- if (lastDepthHistogramBySession.get(sessionId) !== histKey) {
173088
- lastDepthHistogramBySession.set(sessionId, histKey);
173897
+ if (lastDepthHistogramBySession2.get(sessionId) !== histKey) {
173898
+ lastDepthHistogramBySession2.set(sessionId, histKey);
173089
173899
  sessionLog(sessionId, `compressor: depth histogram (${scored.length} total) ${histText}`);
173090
173900
  }
173091
173901
  const floorHeadroom = compartments.length - floor;
@@ -173131,7 +173941,8 @@ ${c.content}
173131
173941
  originalStart: selectedCompartments[0].startMessage,
173132
173942
  originalEnd: selectedCompartments[selectedCompartments.length - 1].endMessage,
173133
173943
  facts,
173134
- logLabel: `depth-5 title-only collapse (${selectedCompartments.length} → ${selectedCompartments.length})`
173944
+ logLabel: `depth-5 title-only collapse (${selectedCompartments.length} → ${selectedCompartments.length})`,
173945
+ holderId: deps.compartmentLeaseHolderId
173135
173946
  });
173136
173947
  }
173137
173948
  try {
@@ -173161,7 +173972,8 @@ ${c.content}
173161
173972
  originalStart: selectedCompartments[0].startMessage,
173162
173973
  originalEnd: selectedCompartments[selectedCompartments.length - 1].endMessage,
173163
173974
  facts,
173164
- logLabel: `depth-${outputDepth} (${selectedCompartments.length} → ${finalCompressed.length})`
173975
+ logLabel: `depth-${outputDepth} (${selectedCompartments.length} → ${finalCompressed.length})`,
173976
+ holderId: deps.compartmentLeaseHolderId
173165
173977
  });
173166
173978
  } catch (error51) {
173167
173979
  sessionLog(sessionId, "compressor: unexpected error:", getErrorMessage(error51));
@@ -173286,7 +174098,8 @@ function finalizeCompression2(args) {
173286
174098
  originalStart,
173287
174099
  originalEnd,
173288
174100
  facts,
173289
- logLabel
174101
+ logLabel,
174102
+ holderId
173290
174103
  } = args;
173291
174104
  const compressedStart = compressed[0].startMessage;
173292
174105
  const compressedEnd = compressed[compressed.length - 1].endMessage;
@@ -173337,8 +174150,16 @@ function finalizeCompression2(args) {
173337
174150
  content: c.content
173338
174151
  }))
173339
174152
  ];
173340
- replaceAllCompartmentState(db, sessionId, allCompartments, facts.map((f) => ({ category: f.category, content: f.content })));
173341
- incrementCompressionDepth(db, sessionId, originalStart, originalEnd);
174153
+ const factInputs = facts.map((f) => ({ category: f.category, content: f.content }));
174154
+ const published = holderId ? replaceAllCompartmentStateAndBumpDepth(db, holderId, sessionId, allCompartments, factInputs, originalStart, originalEnd) : (() => {
174155
+ replaceAllCompartmentState(db, sessionId, allCompartments, factInputs);
174156
+ incrementCompressionDepth(db, sessionId, originalStart, originalEnd);
174157
+ return true;
174158
+ })();
174159
+ if (!published) {
174160
+ sessionLog(sessionId, "compressor: publish skipped because compartment lease is no longer held");
174161
+ return false;
174162
+ }
173342
174163
  sessionLog(sessionId, `compressor: completed ${logLabel}`);
173343
174164
  return true;
173344
174165
  }
@@ -173357,6 +174178,27 @@ async function runCompressorPass2(args) {
173357
174178
  } = args;
173358
174179
  const prompt = buildCompressorPrompt(compartments, currentTokens, targetTokens, outputDepth, outputCount);
173359
174180
  let agentSessionId = null;
174181
+ const startedAt = Date.now();
174182
+ let invocationRecorded = false;
174183
+ const recordInvocation = (params) => {
174184
+ if (invocationRecorded)
174185
+ return;
174186
+ invocationRecorded = true;
174187
+ try {
174188
+ recordChildInvocation({
174189
+ db: openDatabase(),
174190
+ parentSessionId: sessionId,
174191
+ harness: "opencode",
174192
+ subagent: "compressor",
174193
+ startedAt,
174194
+ status: params.status,
174195
+ messages: params.messages,
174196
+ error: params.error
174197
+ });
174198
+ } catch (error51) {
174199
+ sessionLog(sessionId, "subagent token accounting unavailable", getErrorMessage(error51));
174200
+ }
174201
+ };
173360
174202
  try {
173361
174203
  const createResponse = await client.session.create({
173362
174204
  body: { parentID: sessionId, title: "magic-context-compressor" },
@@ -173366,6 +174208,7 @@ async function runCompressorPass2(args) {
173366
174208
  agentSessionId = typeof createdSession?.id === "string" ? createdSession.id : null;
173367
174209
  if (!agentSessionId) {
173368
174210
  sessionLog(sessionId, "compressor: could not create child session");
174211
+ recordInvocation({ status: "failed", error: "could not create child session" });
173369
174212
  return null;
173370
174213
  }
173371
174214
  await promptSyncWithModelSuggestionRetry(client, {
@@ -173387,6 +174230,7 @@ async function runCompressorPass2(args) {
173387
174230
  const messages = normalizeSDKResponse(messagesResponse, [], {
173388
174231
  preferResponseOnMissingData: true
173389
174232
  });
174233
+ recordInvocation({ status: "completed", messages });
173390
174234
  const result = extractLatestAssistantText(messages);
173391
174235
  if (!result) {
173392
174236
  sessionLog(sessionId, "compressor: historian returned no output");
@@ -173407,6 +174251,7 @@ async function runCompressorPass2(args) {
173407
174251
  }
173408
174252
  return snapped.result;
173409
174253
  } catch (error51) {
174254
+ recordInvocation({ status: "failed", error: error51 });
173410
174255
  sessionLog(sessionId, "compressor: historian call failed:", getErrorMessage(error51));
173411
174256
  return null;
173412
174257
  } finally {
@@ -173419,8 +174264,8 @@ async function runCompressorPass2(args) {
173419
174264
  }
173420
174265
 
173421
174266
  // ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
173422
- import { mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
173423
- import { join as join18 } from "node:path";
174267
+ import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
174268
+ import { join as join19 } from "node:path";
173424
174269
  init_data_path();
173425
174270
  function historianResponseDumpDir(directory) {
173426
174271
  return getProjectMagicContextHistorianDir(directory);
@@ -173445,7 +174290,8 @@ async function runValidatedHistorianPass(args) {
173445
174290
  ...args,
173446
174291
  draftXml: firstRun.result,
173447
174292
  draftValidation: firstValidation,
173448
- draftDumpPath: firstRun.dumpPath
174293
+ draftDumpPath: firstRun.dumpPath,
174294
+ draftInvocationId: firstRun.invocationId ?? null
173449
174295
  }) : firstValidation;
173450
174296
  cleanupHistorianDump(args.parentSessionId, firstRun.dumpPath);
173451
174297
  return finalResult;
@@ -173471,7 +174317,8 @@ async function runValidatedHistorianPass(args) {
173471
174317
  ...args,
173472
174318
  draftXml: repairRun.result,
173473
174319
  draftValidation: repairValidation,
173474
- draftDumpPath: repairRun.dumpPath
174320
+ draftDumpPath: repairRun.dumpPath,
174321
+ draftInvocationId: repairRun.invocationId ?? null
173475
174322
  }) : repairValidation;
173476
174323
  cleanupHistorianDump(args.parentSessionId, repairRun.dumpPath);
173477
174324
  return finalResult;
@@ -173492,7 +174339,8 @@ async function runEditorPassOrFallback(args) {
173492
174339
  prompt: buildHistorianEditorPrompt(args.draftXml),
173493
174340
  timeoutMs: args.timeoutMs,
173494
174341
  dumpLabel: `${args.dumpLabelBase}-editor`,
173495
- agentId: HISTORIAN_EDITOR_AGENT
174342
+ agentId: HISTORIAN_EDITOR_AGENT,
174343
+ parentInvocationId: args.draftInvocationId ?? null
173496
174344
  });
173497
174345
  if (!editorRun.ok || !editorRun.result) {
173498
174346
  sessionLog(args.parentSessionId, "historian two-pass: editor call failed", {
@@ -173519,9 +174367,29 @@ async function runHistorianPrompt(args) {
173519
174367
  dumpLabel,
173520
174368
  modelOverride,
173521
174369
  agentId = HISTORIAN_AGENT,
173522
- fallbackModels
174370
+ fallbackModels,
174371
+ subagentKind,
174372
+ parentInvocationId
173523
174373
  } = args;
173524
174374
  let agentSessionId = null;
174375
+ const startedAt = Date.now();
174376
+ let invocationRecorded = false;
174377
+ const recordInvocation = (params) => {
174378
+ if (invocationRecorded)
174379
+ return null;
174380
+ invocationRecorded = true;
174381
+ return recordChildInvocation({
174382
+ db: openDatabase(),
174383
+ parentSessionId,
174384
+ harness: "opencode",
174385
+ subagent: agentId === HISTORIAN_EDITOR_AGENT ? "historian_editor" : subagentKind ?? "historian",
174386
+ startedAt,
174387
+ status: params.status,
174388
+ messages: params.messages,
174389
+ error: params.error,
174390
+ parentInvocationId: agentId === HISTORIAN_EDITOR_AGENT ? parentInvocationId ?? null : null
174391
+ });
174392
+ };
173525
174393
  try {
173526
174394
  sessionLog(parentSessionId, `historian: creating child session (agent=${agentId}, model=${modelOverride ? `${modelOverride.providerID}/${modelOverride.modelID}` : `agent:${agentId}`})`);
173527
174395
  const createResponse = await client.session.create({
@@ -173534,6 +174402,10 @@ async function runHistorianPrompt(args) {
173534
174402
  const createdSession = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
173535
174403
  agentSessionId = typeof createdSession?.id === "string" ? createdSession.id : null;
173536
174404
  if (!agentSessionId) {
174405
+ recordInvocation({
174406
+ status: "failed",
174407
+ error: "Historian could not create its child session."
174408
+ });
173537
174409
  return { ok: false, error: "Historian could not create its child session." };
173538
174410
  }
173539
174411
  for (let retryIndex = 0;retryIndex <= MAX_HISTORIAN_RETRIES; retryIndex += 1) {
@@ -173572,15 +174444,21 @@ async function runHistorianPrompt(args) {
173572
174444
  const messages = normalizeSDKResponse(messagesResponse, [], {
173573
174445
  preferResponseOnMissingData: true
173574
174446
  });
174447
+ const invocationId = recordInvocation({ status: "completed", messages });
173575
174448
  const result = extractLatestAssistantText(messages);
173576
174449
  if (!result) {
173577
- return { ok: false, error: "Historian returned no assistant output." };
174450
+ return {
174451
+ ok: false,
174452
+ error: "Historian returned no assistant output.",
174453
+ invocationId: invocationId ?? undefined
174454
+ };
173578
174455
  }
173579
174456
  const dumpPath = dumpHistorianResponse(parentSessionId, sessionDirectory, dumpLabel ?? "historian-response", result);
173580
- return { ok: true, result, dumpPath };
174457
+ return { ok: true, result, dumpPath, invocationId: invocationId ?? undefined };
173581
174458
  } catch (modelError) {
173582
174459
  const desc = describeError(modelError);
173583
174460
  sessionLog(parentSessionId, `historian prompt failed: ${desc.brief} promptLength=${prompt.length}${desc.stackHead ? ` stackHead="${desc.stackHead}"` : ""}`);
174461
+ recordInvocation({ status: "failed", error: modelError });
173584
174462
  return {
173585
174463
  ok: false,
173586
174464
  error: `Historian failed while processing this session: ${desc.brief}`
@@ -173671,11 +174549,11 @@ function cleanupHistorianDump(sessionId, dumpPath) {
173671
174549
  function dumpHistorianResponse(sessionId, directory, label, text) {
173672
174550
  try {
173673
174551
  const dumpDir = historianResponseDumpDir(directory);
173674
- mkdirSync6(dumpDir, { recursive: true });
174552
+ mkdirSync7(dumpDir, { recursive: true });
173675
174553
  const safeSessionId = sanitizeDumpName(sessionId);
173676
174554
  const safeLabel = sanitizeDumpName(label);
173677
- const dumpPath = join18(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
173678
- writeFileSync3(dumpPath, text, "utf8");
174555
+ const dumpPath = join19(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
174556
+ writeFileSync4(dumpPath, text, "utf8");
173679
174557
  sessionLog(sessionId, "compartment agent: historian response dumped", {
173680
174558
  label,
173681
174559
  dumpPath
@@ -173856,6 +174734,12 @@ async function executePartialRecompInternal(deps, range) {
173856
174734
  getNotificationParams
173857
174735
  } = deps;
173858
174736
  const notifParams = () => getNotificationParams?.() ?? {};
174737
+ const holderId = deps.compartmentLeaseHolderId;
174738
+ if (!holderId) {
174739
+ return `## Magic Recomp — Failed
174740
+
174741
+ Could not acquire the compartment-state lease for this session.`;
174742
+ }
173859
174743
  updateSessionMeta(db, sessionId, { compartmentInProgress: true });
173860
174744
  try {
173861
174745
  let promoteFinal = function() {
@@ -173892,7 +174776,7 @@ async function executePartialRecompInternal(deps, range) {
173892
174776
  return null;
173893
174777
  }
173894
174778
  saveRecompStagingPass(db, sessionId, passCount + 1, merged, currentFacts);
173895
- const promoted = promoteRecompStaging(db, sessionId);
174779
+ const promoted = promoteRecompStaging(db, sessionId, holderId);
173896
174780
  if (!promoted) {
173897
174781
  log("[magic-context] partial recomp promote returned null");
173898
174782
  return null;
@@ -174100,6 +174984,7 @@ Staging preserved for resume on next attempt.`;
174100
174984
  }
174101
174985
 
174102
174986
  // ../plugin/src/hooks/magic-context/compartment-runner-recomp.ts
174987
+ init_logger();
174103
174988
  async function executeContextRecompInternal(deps) {
174104
174989
  const {
174105
174990
  client,
@@ -174111,6 +174996,12 @@ async function executeContextRecompInternal(deps) {
174111
174996
  getNotificationParams
174112
174997
  } = deps;
174113
174998
  const notifParams = () => getNotificationParams?.() ?? {};
174999
+ const holderId = deps.compartmentLeaseHolderId;
175000
+ if (!holderId) {
175001
+ return `## Magic Recomp — Skipped
175002
+
175003
+ Could not acquire the compartment-state lease for this session.`;
175004
+ }
174114
175005
  let currentStateFilePath;
174115
175006
  updateSessionMeta(db, sessionId, { compartmentInProgress: true });
174116
175007
  try {
@@ -174147,7 +175038,7 @@ Found ${existingStaging.compartments.length} staged compartment(s) from ${existi
174147
175038
  if (mergedError)
174148
175039
  return null;
174149
175040
  saveRecompStagingPass(db, sessionId, passCount, candidateCompartments, candidateFacts);
174150
- const promoted2 = promoteRecompStaging(db, sessionId);
175041
+ const promoted2 = promoteRecompStaging(db, sessionId, holderId);
174151
175042
  if (!promoted2)
174152
175043
  return null;
174153
175044
  clearCompressionDepth(db, sessionId);
@@ -174228,6 +175119,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
174228
175119
  fallbackModelId: deps.fallbackModelId,
174229
175120
  fallbackModels: deps.fallbackModels,
174230
175121
  twoPass: deps.historianTwoPass,
175122
+ subagentKind: "recomp",
174231
175123
  callbacks: {
174232
175124
  onRepairRetry: async (error51) => {
174233
175125
  await sendIgnoredMessage(client, sessionId, `## Magic Recomp
@@ -174298,10 +175190,12 @@ Recomp completed ${passCount} pass${passCount === 1 ? "" : "es"} but produced an
174298
175190
  Nothing was written.`;
174299
175191
  }
174300
175192
  saveRecompStagingPass(db, sessionId, passCount, candidateCompartments, candidateFacts);
174301
- const promoted = promoteRecompStaging(db, sessionId);
175193
+ const promoted = promoteRecompStaging(db, sessionId, holderId);
174302
175194
  if (!promoted) {
174303
- replaceAllCompartmentState(db, sessionId, candidateCompartments, candidateFacts);
174304
- clearRecompStaging(db, sessionId);
175195
+ sessionLog(sessionId, "recomp publish skipped: compartment lease no longer held");
175196
+ return `## Magic Recomp — Skipped
175197
+
175198
+ Another process acquired the compartment-state lease before recomp could publish. No state was written.`;
174305
175199
  }
174306
175200
  clearCompressionDepth(db, sessionId);
174307
175201
  if (deps.preserveInjectionCacheUntilConsumed !== true) {
@@ -174370,12 +175264,32 @@ function withPublishedCallback(deps) {
174370
175264
  }
174371
175265
  };
174372
175266
  }
174373
- async function executeContextRecomp(deps, options = {}) {
175267
+ function startLeaseRenewal(deps, holderId) {
175268
+ return setInterval(() => {
175269
+ if (!renewCompartmentLease(deps.db, deps.sessionId, holderId)) {
175270
+ sessionLog(deps.sessionId, "compartment lease renewal failed; publish will be skipped if holder is stale");
175271
+ }
175272
+ }, COMPARTMENT_LEASE_RENEWAL_MS);
175273
+ }
175274
+ async function executeContextRecompWithResult(deps, options = {}) {
174374
175275
  const { sessionId } = deps;
174375
175276
  if (activeRuns.has(sessionId)) {
174376
- return "## Magic Recomp\n\nHistorian is already running for this session. Wait for it to finish, then try `/ctx-recomp` again.";
175277
+ return {
175278
+ message: "## Magic Recomp\n\nHistorian is already running for this session. Wait for it to finish, then try `/ctx-recomp` again.",
175279
+ published: false
175280
+ };
175281
+ }
175282
+ const holderId = crypto.randomUUID();
175283
+ const lease2 = acquireCompartmentLease(deps.db, sessionId, holderId);
175284
+ if (!lease2) {
175285
+ sessionLog(sessionId, "recomp skipped: compartment lease held by another process");
175286
+ return {
175287
+ message: "## Magic Recomp\n\nAnother process is already mutating compartment state for this session. Wait for it to finish, then try `/ctx-recomp` again.",
175288
+ published: false
175289
+ };
174377
175290
  }
174378
- const runnerDeps = withPublishedCallback(deps);
175291
+ const renewal = startLeaseRenewal(deps, holderId);
175292
+ const runnerDeps = withPublishedCallback({ ...deps, compartmentLeaseHolderId: holderId });
174379
175293
  const promise2 = options.range ? executePartialRecompInternal(runnerDeps, options.range) : executeContextRecompInternal(runnerDeps);
174380
175294
  const wrappedPromise = promise2.then(() => {
174381
175295
  return;
@@ -174384,8 +175298,14 @@ async function executeContextRecomp(deps, options = {}) {
174384
175298
  });
174385
175299
  activeRuns.set(sessionId, { promise: wrappedPromise, published: false });
174386
175300
  try {
174387
- return await promise2;
175301
+ const message = await promise2;
175302
+ return {
175303
+ message,
175304
+ published: activeRuns.get(sessionId)?.published === true
175305
+ };
174388
175306
  } finally {
175307
+ clearInterval(renewal);
175308
+ releaseCompartmentLease(deps.db, sessionId, holderId);
174389
175309
  if (activeRuns.get(sessionId)?.promise === wrappedPromise) {
174390
175310
  activeRuns.delete(sessionId);
174391
175311
  }
@@ -174558,7 +175478,7 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
174558
175478
  projectIdentity: ctx.cwd
174559
175479
  });
174560
175480
  try {
174561
- const result = await executeContextRecomp({
175481
+ const result = await executeContextRecompWithResult({
174562
175482
  client: createPiRecompClient({
174563
175483
  runner: deps.runner,
174564
175484
  model: deps.historianModel,
@@ -174566,6 +175486,7 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
174566
175486
  timeoutMs: deps.historianTimeoutMs,
174567
175487
  thinkingLevel: deps.historianThinkingLevel,
174568
175488
  directory: ctx.cwd,
175489
+ accountingSessionId: sessionId,
174569
175490
  notify: (text) => {
174570
175491
  sendCtxStatusMessage(pi, {
174571
175492
  title: "/ctx-recomp",
@@ -174584,9 +175505,12 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
174584
175505
  }, parsed.kind === "partial" ? { range: parsed.range } : {});
174585
175506
  sendCtxStatusMessage(pi, {
174586
175507
  title: "/ctx-recomp",
174587
- text: result,
174588
- level: inferLevel(result)
175508
+ text: result.message,
175509
+ level: inferLevel(result.message)
174589
175510
  });
175511
+ if (!result.published) {
175512
+ return;
175513
+ }
174590
175514
  queueAndApplyPiRecompMarker({ db: deps.db, sessionId, ctx });
174591
175515
  signalPiHistoryRefresh(sessionId);
174592
175516
  signalPiPendingMaterialization(sessionId);
@@ -174749,7 +175673,9 @@ function createPiRecompClient(args) {
174749
175673
  fallbackModels: args.fallbackModels,
174750
175674
  timeoutMs: args.timeoutMs,
174751
175675
  cwd: args.directory,
174752
- thinkingLevel: args.thinkingLevel
175676
+ thinkingLevel: args.thinkingLevel,
175677
+ accountingSessionId: args.accountingSessionId,
175678
+ accountingSubagent: "recomp"
174753
175679
  });
174754
175680
  if (!result.ok) {
174755
175681
  throw new Error(`Pi recomp historian failed (${result.reason}): ${result.error}`);
@@ -174780,8 +175706,8 @@ function createPiRecompClient(args) {
174780
175706
  function readPathId(input) {
174781
175707
  if (typeof input !== "object" || input === null)
174782
175708
  return "";
174783
- const path5 = input.path;
174784
- return typeof path5?.id === "string" ? path5.id : "";
175709
+ const path6 = input.path;
175710
+ return typeof path6?.id === "string" ? path6.id : "";
174785
175711
  }
174786
175712
  function readBody(input) {
174787
175713
  if (typeof input !== "object" || input === null)
@@ -174932,10 +175858,20 @@ import {
174932
175858
  truncateToWidth,
174933
175859
  visibleWidth
174934
175860
  } from "@earendil-works/pi-tui";
175861
+
175862
+ // ../plugin/src/shared/format-threshold.ts
175863
+ function formatThresholdPercent(value) {
175864
+ if (typeof value !== "number" || !Number.isFinite(value))
175865
+ return "—";
175866
+ const rounded = Math.round(value);
175867
+ if (Math.abs(value - rounded) < 0.05)
175868
+ return String(rounded);
175869
+ return value.toFixed(1);
175870
+ }
174935
175871
  // package.json
174936
175872
  var package_default = {
174937
175873
  name: "@cortexkit/pi-magic-context",
174938
- version: "0.21.6",
175874
+ version: "0.21.8",
174939
175875
  type: "module",
174940
175876
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
174941
175877
  main: "dist/index.js",
@@ -175099,7 +176035,7 @@ function renderInner(s, theme, innerWidth) {
175099
176035
  lines.push(theme.fg("muted", "Tags"));
175100
176036
  lines.push(`Active ${s.activeTags} (~${formatBytes(s.activeBytes)}) · Dropped ${s.droppedTags} · Total ${s.totalTags}`);
175101
176037
  lines.push(theme.fg("muted", "Rolling Nudges / Context"));
175102
- lines.push(`Execute threshold ${s.executeThreshold}% · Anchor ${fmt2(s.lastNudgeTokens)} tok · Interval ${fmt2(s.nudgeInterval)} tok · Next ${fmt2(s.nextNudgeAfter)} tok`);
176038
+ lines.push(`Execute threshold ${formatThresholdPercent(s.executeThreshold)}% · Anchor ${fmt2(s.lastNudgeTokens)} tok · Interval ${fmt2(s.nudgeInterval)} tok · Next ${fmt2(s.nextNudgeAfter)} tok`);
175103
176039
  lines.push(`Protected tags ${s.protectedTagCount} · Subagent ${s.isSubagent ? "yes" : "no"} · History block ~${fmt2(s.historyBlockTokens)} tok${s.compressionBudget ? ` · Budget ~${fmt2(s.compressionBudget)} tok (${s.compressionUsage} used)` : ""}`);
175104
176040
  if (s.lastTransformError)
175105
176041
  lines.push(theme.fg("error", `⚠ ${s.lastTransformError}`));
@@ -175447,7 +176383,7 @@ function buildStatusDetails(deps, sessionId) {
175447
176383
  status: ["pending", "ready"]
175448
176384
  }).length,
175449
176385
  dreamer: {
175450
- enabled: deps.dreamer?.enabled === true,
176386
+ enabled: deps.dreamer?.runnable === true,
175451
176387
  schedule: deps.dreamer?.schedule ?? null,
175452
176388
  lastRunAt: readNumberState(deps.db, `last_dream_at:${deps.projectIdentity}`)
175453
176389
  },
@@ -180661,7 +181597,7 @@ var PLUGIN_VERSION = (() => {
180661
181597
  setHarness("pi");
180662
181598
  function resolveSidekickFromConfig(config2) {
180663
181599
  const sidekick = config2.sidekick;
180664
- if (!sidekick?.enabled)
181600
+ if (!sidekick || sidekick.disable === true)
180665
181601
  return;
180666
181602
  const model = sidekick.model?.trim();
180667
181603
  if (!model || model.length === 0)
@@ -180670,18 +181606,20 @@ function resolveSidekickFromConfig(config2) {
180670
181606
  model,
180671
181607
  systemPrompt: sidekick.system_prompt,
180672
181608
  timeoutMs: sidekick.timeout_ms,
180673
- thinking_level: sidekick.thinking_level
181609
+ thinking_level: sidekick.thinking_level,
181610
+ fallbackModels: resolveFallbackChain("sidekick", sidekick.fallback_models)
180674
181611
  };
180675
181612
  }
180676
181613
  function resolveHistorianFromConfig(config2) {
180677
181614
  const historian = config2.historian;
181615
+ if (historian?.disable === true)
181616
+ return;
180678
181617
  const model = historian?.model?.trim();
180679
181618
  if (!model || model.length === 0)
180680
181619
  return;
180681
181620
  const historianContextLimit = resolveHistorianContextLimit(model);
180682
181621
  const historianChunkTokens = deriveHistorianChunkTokens(historianContextLimit);
180683
- const fbRaw = historian?.fallback_models;
180684
- const fallbackModels = typeof fbRaw === "string" ? [fbRaw] : fbRaw;
181622
+ const fallbackModels = resolveFallbackChain("historian", historian?.fallback_models);
180685
181623
  return {
180686
181624
  runner: new PiSubagentRunner,
180687
181625
  model,
@@ -180730,11 +181668,11 @@ function resolveAutoSearchFromConfig(config2) {
180730
181668
  };
180731
181669
  }
180732
181670
  function resolveDreamerFromConfig(config2) {
180733
- return config2.dreamer;
181671
+ return config2.dreamer?.disable === true ? undefined : config2.dreamer;
180734
181672
  }
180735
181673
  async function src_default2(pi) {
180736
181674
  const storageDir = getMagicContextStorageDir();
180737
- const dbPath = join19(storageDir, "context.db");
181675
+ const dbPath = join20(storageDir, "context.db");
180738
181676
  let db;
180739
181677
  try {
180740
181678
  db = openDatabase();
@@ -180750,6 +181688,7 @@ async function src_default2(pi) {
180750
181688
  const pendingPiMarkerSessions = getSessionsWithPendingPiMarker(db);
180751
181689
  for (const sid of pendingPiMarkerSessions) {
180752
181690
  signalPiDeferredHistoryRefresh(sid);
181691
+ signalPiPendingMaterialization(sid);
180753
181692
  }
180754
181693
  if (pendingPiMarkerSessions.length > 0) {
180755
181694
  log(`${PREFIX2} rehydrated ${pendingPiMarkerSessions.length} Pi deferred compaction marker session(s)`);
@@ -180781,7 +181720,7 @@ async function src_default2(pi) {
180781
181720
  allowDreamerActions: false,
180782
181721
  ctxReduceEnabled: config2.ctx_reduce_enabled === true,
180783
181722
  protectedTags: config2.protected_tags ?? 20,
180784
- dreamerEnabled: config2.dreamer?.enabled === true
181723
+ dreamerEnabled: isDreamerRunnable(config2)
180785
181724
  });
180786
181725
  info(`registered tools: ctx_search, ctx_memory, ctx_note, ctx_expand${config2.ctx_reduce_enabled === true ? ", ctx_reduce" : ""}`);
180787
181726
  const historianConfig = resolveHistorianFromConfig(config2);
@@ -180825,7 +181764,7 @@ async function src_default2(pi) {
180825
181764
  info(autoSearchConfig.enabled ? `registered auto-search hint (threshold=${autoSearchConfig.scoreThreshold}, minChars=${autoSearchConfig.minPromptChars})` : "registered auto-search hint: DISABLED (experimental.auto_search.enabled=false)");
180826
181765
  const sidekickConfig = resolveSidekickFromConfig(config2);
180827
181766
  registerCtxAugCommand(pi, sidekickConfig);
180828
- info(sidekickConfig ? `registered /ctx-aug (sidekick model=${sidekickConfig.model})` : "registered /ctx-aug (sidekick disabled — set sidekick.enabled=true and sidekick.model in config)");
181767
+ info(sidekickConfig ? `registered /ctx-aug (sidekick model=${sidekickConfig.model})` : "registered /ctx-aug (sidekick disabled — set sidekick.disable=false and sidekick.model in config)");
180829
181768
  registerCtxStatusCommand(pi, {
180830
181769
  db,
180831
181770
  projectIdentity,
@@ -180837,7 +181776,7 @@ async function src_default2(pi) {
180837
181776
  commitClusterTrigger: config2.commit_cluster_trigger,
180838
181777
  executeThresholdTokens: config2.execute_threshold_tokens,
180839
181778
  dreamer: {
180840
- enabled: config2.dreamer?.enabled ?? false,
181779
+ runnable: isDreamerRunnable(config2),
180841
181780
  schedule: config2.dreamer?.schedule
180842
181781
  }
180843
181782
  });
@@ -180863,7 +181802,7 @@ async function src_default2(pi) {
180863
181802
  projectDir,
180864
181803
  projectIdentity,
180865
181804
  resolveProject: resolveCurrentProject,
180866
- dreamerEnabled: config2.dreamer?.enabled ?? false,
181805
+ dreamerEnabled: isDreamerRunnable(config2),
180867
181806
  onProjectSeen: (identity) => seenDreamerProjectIdentities.add(identity)
180868
181807
  });
180869
181808
  info("registered /ctx-dream");
@@ -180879,11 +181818,28 @@ async function src_default2(pi) {
180879
181818
  gitCommitIndexing: config2.experimental.git_commit_indexing,
180880
181819
  onAdjunctsRefreshNeeded: signalPiSystemPromptRefreshForProject
180881
181820
  });
180882
- info(dreamerConfig.enabled ? `registered dreamer (schedule=${dreamerConfig.schedule}, tasks=[${dreamerConfig.tasks.join(",")}])` : "registered dreamer: DISABLED (dreamer.enabled=false)");
181821
+ info(`registered dreamer (schedule=${dreamerConfig.schedule || "manual-only"}, tasks=[${dreamerConfig.tasks.join(",")}])`);
180883
181822
  } else {
180884
- info("registered dreamer: DISABLED (no dreamer config)");
181823
+ info(isDreamerRunnable(config2) ? "registered dreamer: DISABLED (no dreamer config)" : "registered dreamer: DISABLED (dreamer.disable=true or no dreamer config)");
180885
181824
  }
180886
181825
  pi.on("before_agent_start", async (event, ctx) => {
181826
+ try {
181827
+ if (ctx.hasUI && shouldShowAnnouncement()) {
181828
+ const featureText = ANNOUNCEMENT_FEATURES.map((line) => ` • ${line}`).join(`
181829
+ `);
181830
+ const sections = [
181831
+ `✨ Magic Context v${ANNOUNCEMENT_VERSION} — what's new:`,
181832
+ "",
181833
+ featureText
181834
+ ];
181835
+ if (ANNOUNCEMENT_FOOTER && ANNOUNCEMENT_FOOTER.trim().length > 0) {
181836
+ sections.push("", ANNOUNCEMENT_FOOTER);
181837
+ }
181838
+ ctx.ui.notify(sections.join(`
181839
+ `), "info");
181840
+ markAnnouncementSeen(ANNOUNCEMENT_VERSION);
181841
+ }
181842
+ } catch {}
180887
181843
  try {
180888
181844
  const currentProject = resolveCurrentProject(ctx);
180889
181845
  seenDreamerProjectIdentities.add(currentProject.projectIdentity);
@@ -180915,16 +181871,16 @@ async function src_default2(pi) {
180915
181871
  cwd: currentProject.projectDir,
180916
181872
  sessionId,
180917
181873
  memoryEnabled: config2.memory.enabled,
180918
- injectDocs: config2.dreamer?.inject_docs ?? true,
181874
+ injectDocs: isDreamerRunnable(config2) && (config2.dreamer?.inject_docs ?? true),
180919
181875
  includeGuidance: true,
180920
181876
  protectedTags: config2.protected_tags,
180921
181877
  ctxReduceEnabled: config2.ctx_reduce_enabled,
180922
- dreamerEnabled: config2.dreamer?.enabled ?? false,
181878
+ dreamerEnabled: isDreamerRunnable(config2),
180923
181879
  dropToolStructure: config2.drop_tool_structure,
180924
181880
  temporalAwarenessEnabled: config2.experimental?.temporal_awareness ?? false,
180925
181881
  cavemanTextCompressionEnabled: config2.ctx_reduce_enabled === false && config2.experimental?.caveman_text_compression?.enabled === true,
180926
- userMemoriesEnabled: (config2.dreamer?.enabled ?? false) && (config2.dreamer?.user_memories?.enabled ?? false),
180927
- pinKeyFilesEnabled: (config2.dreamer?.enabled ?? false) && (config2.dreamer?.pin_key_files?.enabled ?? false),
181882
+ userMemoriesEnabled: config2.dreamer?.user_memories?.enabled ?? false,
181883
+ pinKeyFilesEnabled: config2.dreamer?.pin_key_files?.enabled ?? false,
180928
181884
  pinKeyFilesTokenBudget: config2.dreamer?.pin_key_files?.token_budget ?? 1e4,
180929
181885
  isCacheBusting,
180930
181886
  existingSystemPrompt: event.systemPrompt
@@ -181063,7 +182019,7 @@ ${block}` : event.systemPrompt;
181063
182019
  continue;
181064
182020
  if (typeof b.name !== "string")
181065
182021
  continue;
181066
- if (!/^todo.*write$|^write.*todo$|^todowrite$/i.test(b.name)) {
182022
+ if (b.name !== "todowrite") {
181067
182023
  continue;
181068
182024
  }
181069
182025
  const args = b.arguments;
@@ -181149,6 +182105,9 @@ ${block}` : event.systemPrompt;
181149
182105
  });
181150
182106
  }
181151
182107
  export {
182108
+ resolveSidekickFromConfig,
182109
+ resolveHistorianFromConfig,
182110
+ resolveDreamerFromConfig,
181152
182111
  persistPiPressureFromMessageEnd,
181153
182112
  persistPiMessageEndModelMeta,
181154
182113
  src_default2 as default