@wolfx/pi-magic-context 0.22.1-patch.0 → 0.22.3

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
@@ -86,9 +86,6 @@ function getMagicContextStorageDir() {
86
86
  function getLegacyOpenCodeMagicContextStorageDir() {
87
87
  return path2.join(getOpenCodeStorageDir(), "plugin", "magic-context");
88
88
  }
89
- function getCacheDir() {
90
- return process.env.XDG_CACHE_HOME ?? path2.join(os.homedir(), ".cache");
91
- }
92
89
  var init_data_path = () => {};
93
90
 
94
91
  // ../plugin/src/shared/logger.ts
@@ -140945,14 +140942,48 @@ function readRawSessionMessagesFromDb(db, sessionId) {
140945
140942
  var encoder = new TextEncoder;
140946
140943
  var TAG_PREFIX_REGEX = /^(?:§\d+§\s*)+/;
140947
140944
  var MALFORMED_TAG_PREFIX_REGEX = /^(?:§\d+">§(?:\d+§)?\s*)+/;
140945
+ var COMPLETE_TAG_PAIR_GLOBAL_REGEX = /\u00a7\d+\u00a7/g;
140946
+ var MALFORMED_TAG_GLOBAL_REGEX = /\u00a7\d+">(?:\u00a7(?:\d+\u00a7)?)?/g;
140947
+ var STRAY_SECTION_CHAR_REGEX = /\u00a7/g;
140948
+ function stripWellFormedLeadingTagPrefix(value) {
140949
+ return value.replace(/^(\u00a7\d+\u00a7\s*)+/, "");
140950
+ }
140951
+ function stripCompleteTagPairsGlobally(value) {
140952
+ return value.replace(COMPLETE_TAG_PAIR_GLOBAL_REGEX, "");
140953
+ }
140954
+ function stripMalformedTagNotationGlobally(value) {
140955
+ return value.replace(MALFORMED_TAG_GLOBAL_REGEX, "");
140956
+ }
140957
+ function stripTagSectionCharacters(value) {
140958
+ return value.replace(STRAY_SECTION_CHAR_REGEX, "");
140959
+ }
140960
+ function stripPersistedAssistantText(value) {
140961
+ let text = stripWellFormedLeadingTagPrefix(value);
140962
+ text = stripCompleteTagPairsGlobally(text);
140963
+ text = stripMalformedTagNotationGlobally(text);
140964
+ text = stripTagSectionCharacters(text);
140965
+ return text.trim();
140966
+ }
140948
140967
  function byteSize(value) {
140949
140968
  return encoder.encode(value).length;
140950
140969
  }
140951
140970
  function stripTagPrefix(value) {
140952
- let stripped = value.replace(MALFORMED_TAG_PREFIX_REGEX, "");
140953
- stripped = stripped.replace(TAG_PREFIX_REGEX, "");
140971
+ let stripped = value;
140972
+ for (let pass = 0;pass < 8; pass++) {
140973
+ const prev = stripped;
140974
+ stripped = stripped.replace(MALFORMED_TAG_PREFIX_REGEX, "");
140975
+ stripped = stripped.replace(TAG_PREFIX_REGEX, "");
140976
+ if (stripped === prev)
140977
+ break;
140978
+ }
140954
140979
  return stripped;
140955
140980
  }
140981
+ function peelLeadingMcTagNotation(value) {
140982
+ const body = stripTagPrefix(value);
140983
+ if (body === value)
140984
+ return { tagPrefix: "", body };
140985
+ return { tagPrefix: value.slice(0, value.length - body.length), body };
140986
+ }
140956
140987
  function prependTag(tagId, value) {
140957
140988
  const stripped = stripTagPrefix(value);
140958
140989
  return `§${tagId}§ ${stripped}`;
@@ -140994,7 +141025,9 @@ function hasMeaningfulPart(part) {
140994
141025
  return false;
140995
141026
  const type = part.type;
140996
141027
  if (type === "text") {
140997
- return typeof part.text === "string" && part.text.trim().length > 0;
141028
+ if (typeof part.text !== "string")
141029
+ return false;
141030
+ return stripTagPrefix(part.text).trim().length > 0;
140998
141031
  }
140999
141032
  if (typeof type !== "string")
141000
141033
  return false;
@@ -142698,7 +142731,7 @@ var databases = new Map;
142698
142731
  var persistenceByDatabase = new WeakMap;
142699
142732
  var persistenceErrorByDatabase = new WeakMap;
142700
142733
  var lastSchemaFenceRejection = null;
142701
- var LATEST_SUPPORTED_VERSION = 27;
142734
+ var LATEST_SUPPORTED_VERSION = 29;
142702
142735
  function resolveDatabasePath(dbPathOverride) {
142703
142736
  if (dbPathOverride) {
142704
142737
  return { dbDir: dirname2(dbPathOverride), dbPath: dbPathOverride };
@@ -143018,6 +143051,17 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143018
143051
  updated_at INTEGER NOT NULL DEFAULT 0
143019
143052
  );
143020
143053
 
143054
+ CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
143055
+ project_path TEXT PRIMARY KEY,
143056
+ lease_holder TEXT,
143057
+ lease_expires_at INTEGER,
143058
+ last_swept_at INTEGER
143059
+ );
143060
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
143061
+ ON git_sweep_coordinator(lease_expires_at);
143062
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
143063
+ ON git_sweep_coordinator(last_swept_at);
143064
+
143021
143065
  CREATE TABLE IF NOT EXISTS m0_mutation_log (
143022
143066
  id INTEGER PRIMARY KEY AUTOINCREMENT,
143023
143067
  session_id TEXT NOT NULL,
@@ -144240,6 +144284,38 @@ var MIGRATIONS = [
144240
144284
  ON tags(session_id, entry_fingerprint)
144241
144285
  WHERE type='message' AND entry_fingerprint IS NOT NULL`);
144242
144286
  }
144287
+ },
144288
+ {
144289
+ version: 28,
144290
+ description: "Add git commit sweep coordinator lease/cooldown table",
144291
+ up: (db) => {
144292
+ db.exec(`
144293
+ CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
144294
+ project_path TEXT PRIMARY KEY,
144295
+ lease_holder TEXT,
144296
+ lease_expires_at INTEGER,
144297
+ last_swept_at INTEGER
144298
+ );
144299
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
144300
+ ON git_sweep_coordinator(lease_expires_at);
144301
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
144302
+ ON git_sweep_coordinator(last_swept_at);
144303
+ `);
144304
+ }
144305
+ },
144306
+ {
144307
+ version: 29,
144308
+ description: "Add anchor_ordinal to notes (traceback to the conversation tail)",
144309
+ up: (db) => {
144310
+ const notesExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='notes'").get();
144311
+ if (!notesExists) {
144312
+ return;
144313
+ }
144314
+ const columns = db.prepare("PRAGMA table_info(notes)").all();
144315
+ if (!columns.some((column) => column.name === "anchor_ordinal")) {
144316
+ db.exec("ALTER TABLE notes ADD COLUMN anchor_ordinal INTEGER");
144317
+ }
144318
+ }
144243
144319
  }
144244
144320
  ];
144245
144321
  var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -145127,7 +145203,8 @@ function toNote(row) {
145127
145203
  updatedAt: row.updated_at,
145128
145204
  lastCheckedAt: toNullableNumber(row.last_checked_at),
145129
145205
  readyAt: toNullableNumber(row.ready_at),
145130
- readyReason: toNullableString(row.ready_reason)
145206
+ readyReason: toNullableString(row.ready_reason),
145207
+ anchorOrdinal: toNullableNumber(row.anchor_ordinal)
145131
145208
  };
145132
145209
  }
145133
145210
  function getNoteById(db, noteId) {
@@ -145179,7 +145256,7 @@ function getNotes(db, options = {}) {
145179
145256
  }
145180
145257
  function addNote(db, type, options) {
145181
145258
  const now = Date.now();
145182
- const result = type === "session" ? db.prepare("INSERT INTO notes (type, status, content, session_id, created_at, updated_at, harness) VALUES ('session', 'active', ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId, now, now, getHarness()) : db.prepare("INSERT INTO notes (type, status, content, session_id, project_path, surface_condition, created_at, updated_at, harness) VALUES ('smart', 'pending', ?, ?, ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId ?? null, options.projectPath, options.surfaceCondition, now, now, getHarness());
145259
+ const result = type === "session" ? db.prepare("INSERT INTO notes (type, status, content, session_id, created_at, updated_at, harness, anchor_ordinal) VALUES ('session', 'active', ?, ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId, now, now, getHarness(), options.anchorOrdinal ?? null) : db.prepare("INSERT INTO notes (type, status, content, session_id, project_path, surface_condition, created_at, updated_at, harness, anchor_ordinal) VALUES ('smart', 'pending', ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId ?? null, options.projectPath, options.surfaceCondition, now, now, getHarness(), options.anchorOrdinal ?? null);
145183
145260
  if (!isNoteRow(result)) {
145184
145261
  throw new Error("[notes] failed to insert note");
145185
145262
  }
@@ -145949,212 +146026,41 @@ function getAgentFallbackModels(agent) {
145949
146026
 
145950
146027
  // ../plugin/src/shared/models-dev-cache.ts
145951
146028
  init_data_path();
145952
- import { createHash as createHash5 } from "node:crypto";
145953
- import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
145954
- import { homedir as homedir2, platform } from "node:os";
146029
+ init_logger();
146030
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync, writeFileSync } from "node:fs";
145955
146031
  import { join as join6 } from "node:path";
145956
-
145957
- // ../plugin/src/shared/jsonc-parser.ts
145958
- function stripJsonComments(content) {
145959
- let result = "";
145960
- let inString = false;
145961
- let escaped = false;
145962
- let inLineComment = false;
145963
- let inBlockComment = false;
145964
- for (let index = 0;index < content.length; index += 1) {
145965
- const char = content[index];
145966
- const next = content[index + 1];
145967
- if (inLineComment) {
145968
- if (char === `
145969
- `) {
145970
- inLineComment = false;
145971
- result += char;
145972
- }
145973
- continue;
145974
- }
145975
- if (inBlockComment) {
145976
- if (char === "*" && next === "/") {
145977
- inBlockComment = false;
145978
- index += 1;
145979
- }
145980
- continue;
145981
- }
145982
- if (inString) {
145983
- result += char;
145984
- if (escaped) {
145985
- escaped = false;
145986
- } else if (char === "\\") {
145987
- escaped = true;
145988
- } else if (char === '"') {
145989
- inString = false;
145990
- }
145991
- continue;
145992
- }
145993
- if (char === '"') {
145994
- inString = true;
145995
- result += char;
145996
- continue;
145997
- }
145998
- if (char === "/" && next === "/") {
145999
- inLineComment = true;
146000
- index += 1;
146001
- continue;
146002
- }
146003
- if (char === "/" && next === "*") {
146004
- inBlockComment = true;
146005
- index += 1;
146006
- continue;
146007
- }
146008
- result += char;
146009
- }
146010
- return result;
146032
+ var MIN_SANE_LIMIT = 20000;
146033
+ var MAX_SANE_LIMIT = 3000000;
146034
+ function isSaneLimit(limit) {
146035
+ return typeof limit === "number" && limit >= MIN_SANE_LIMIT && limit <= MAX_SANE_LIMIT;
146011
146036
  }
146012
- function stripTrailingCommas(content) {
146013
- let result = "";
146014
- let inString = false;
146015
- let escaped = false;
146016
- for (let index = 0;index < content.length; index += 1) {
146017
- const char = content[index];
146018
- if (inString) {
146019
- result += char;
146020
- if (escaped) {
146021
- escaped = false;
146022
- } else if (char === "\\") {
146023
- escaped = true;
146024
- } else if (char === '"') {
146025
- inString = false;
146026
- }
146027
- continue;
146028
- }
146029
- if (char === '"') {
146030
- inString = true;
146031
- result += char;
146032
- continue;
146033
- }
146034
- if (char === ",") {
146035
- let lookahead = index + 1;
146036
- while (lookahead < content.length && /\s/.test(content[lookahead] ?? "")) {
146037
- lookahead += 1;
146038
- }
146039
- const next = content[lookahead];
146040
- if (next === "}" || next === "]") {
146041
- continue;
146042
- }
146043
- }
146044
- result += char;
146045
- }
146046
- return result;
146047
- }
146048
- function parseJsonc(content) {
146049
- const normalized = stripTrailingCommas(stripJsonComments(content));
146050
- return JSON.parse(normalized);
146051
- }
146052
-
146053
- // ../plugin/src/shared/models-dev-cache.ts
146054
- init_logger();
146055
- var RELOAD_INTERVAL_MS = 5 * 60 * 1000;
146056
146037
  var apiCache = null;
146057
- var apiLoadedAt = 0;
146058
- var fileCache = null;
146059
- var fileLastAttempt = 0;
146060
- function hashFast(input) {
146061
- return createHash5("sha1").update(input).digest("hex");
146062
- }
146063
- function getModelsJsonPath() {
146064
- const explicit = process.env.OPENCODE_MODELS_PATH?.trim();
146065
- if (explicit)
146066
- return explicit;
146067
- const cacheBase = getCacheDir();
146068
- const source = process.env.OPENCODE_MODELS_URL?.trim();
146069
- const filename = source && source !== "https://models.dev" ? `models-${hashFast(source)}.json` : "models.json";
146070
- return join6(cacheBase, "opencode", filename);
146071
- }
146072
- function getOpencodeConfigPath() {
146073
- const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
146074
- const configDir = envDir ? envDir : platform() === "win32" ? join6(homedir2(), ".config", "opencode") : join6(process.env.XDG_CONFIG_HOME || join6(homedir2(), ".config"), "opencode");
146075
- const jsonc = join6(configDir, "opencode.jsonc");
146076
- if (existsSync5(jsonc))
146077
- return jsonc;
146078
- const json = join6(configDir, "opencode.json");
146079
- if (existsSync5(json))
146080
- return json;
146081
- return null;
146082
- }
146083
- function resolveLimit(limit) {
146084
- if (!limit)
146085
- return;
146086
- if (typeof limit.input === "number" && limit.input > 0)
146087
- return limit.input;
146088
- if (typeof limit.context === "number" && limit.context > 0)
146089
- return limit.context;
146090
- return;
146038
+ var persistSeedLoaded = false;
146039
+ function persistFilePath() {
146040
+ return join6(getMagicContextStorageDir(), `model-context-limits-${getHarness()}.json`);
146091
146041
  }
146092
- function setCachedModelMetadata(cache, key, model) {
146093
- const limit = resolveLimit(model?.limit);
146094
- if (limit === undefined) {
146042
+ function loadPersistedApiCacheOnce() {
146043
+ if (persistSeedLoaded || apiCache !== null)
146095
146044
  return;
146096
- }
146097
- const value = { limit };
146098
- cache.set(key, value);
146099
- const modes = model?.experimental?.modes;
146100
- if (modes && typeof modes === "object") {
146101
- for (const mode of Object.keys(modes)) {
146102
- cache.set(`${key}-${mode}`, value);
146103
- }
146104
- }
146105
- }
146106
- function loadModelsDevMetadataFromFile() {
146107
- const metadata = new Map;
146108
- const modelsJsonPath = getModelsJsonPath();
146109
- let fileFound = false;
146045
+ persistSeedLoaded = true;
146110
146046
  try {
146111
- if (existsSync5(modelsJsonPath)) {
146112
- fileFound = true;
146113
- const raw = readFileSync3(modelsJsonPath, "utf-8");
146114
- const data = JSON.parse(raw);
146115
- for (const [providerId, provider] of Object.entries(data)) {
146116
- if (!provider?.models || typeof provider.models !== "object")
146117
- continue;
146118
- for (const [modelId, model] of Object.entries(provider.models)) {
146119
- setCachedModelMetadata(metadata, `${providerId}/${modelId}`, model);
146120
- }
146121
- }
146047
+ const raw = readFileSync3(persistFilePath(), "utf-8");
146048
+ const obj = JSON.parse(raw);
146049
+ const map = new Map;
146050
+ for (const [key, limit] of Object.entries(obj)) {
146051
+ if (isSaneLimit(limit))
146052
+ map.set(key, { limit });
146122
146053
  }
146123
- } catch (error) {
146124
- sessionLog("global", `models-dev-cache: failed to read models.json at ${modelsJsonPath}:`, error instanceof Error ? error.message : String(error));
146125
- }
146126
- try {
146127
- const configPath = getOpencodeConfigPath();
146128
- if (configPath && existsSync5(configPath)) {
146129
- const config = parseJsonc(readFileSync3(configPath, "utf-8"));
146130
- if (config.provider && typeof config.provider === "object") {
146131
- for (const [providerId, provider] of Object.entries(config.provider)) {
146132
- if (!provider?.models || typeof provider.models !== "object")
146133
- continue;
146134
- for (const [modelId, model] of Object.entries(provider.models)) {
146135
- setCachedModelMetadata(metadata, `${providerId}/${modelId}`, model);
146136
- }
146137
- }
146138
- }
146054
+ if (map.size > 0) {
146055
+ apiCache = map;
146056
+ sessionLog("global", `models-dev-cache: seeded ${map.size} entries from persisted cache (cold start)`);
146139
146057
  }
146140
- } catch (error) {
146141
- sessionLog("global", "models-dev-cache: failed to read opencode config for custom models:", error instanceof Error ? error.message : String(error));
146142
- }
146143
- sessionLog("global", `models-dev-cache: file-layer loaded ${metadata.size} model metadata entries (modelsJsonPath=${modelsJsonPath}, found=${fileFound})`);
146144
- return metadata;
146058
+ } catch {}
146145
146059
  }
146146
- function getModelsDevContextLimit(providerID, modelID) {
146147
- const now = Date.now();
146148
- if (!fileCache || now - fileLastAttempt > RELOAD_INTERVAL_MS) {
146149
- fileLastAttempt = now;
146150
- fileCache = loadModelsDevMetadataFromFile();
146151
- }
146060
+ function getSdkContextLimit(providerID, modelID) {
146061
+ loadPersistedApiCacheOnce();
146152
146062
  const fromApi = lookupLimitWithTagFallback(apiCache, providerID, modelID);
146153
- const fromFile = lookupLimitWithTagFallback(fileCache, providerID, modelID);
146154
- if (typeof fromApi === "number" && typeof fromFile === "number") {
146155
- return Math.max(fromApi, fromFile);
146156
- }
146157
- return fromApi ?? fromFile;
146063
+ return isSaneLimit(fromApi) ? fromApi : undefined;
146158
146064
  }
146159
146065
  function lookupLimitWithTagFallback(cache, providerID, modelID) {
146160
146066
  if (!cache)
@@ -146171,12 +146077,6 @@ function lookupLimitWithTagFallback(cache, providerID, modelID) {
146171
146077
  }
146172
146078
  return;
146173
146079
  }
146174
- function clearModelsDevCache() {
146175
- apiCache = null;
146176
- apiLoadedAt = 0;
146177
- fileCache = null;
146178
- fileLastAttempt = 0;
146179
- }
146180
146080
 
146181
146081
  // ../plugin/src/hooks/magic-context/derive-budgets.ts
146182
146082
  var TRIGGER_BUDGET_PERCENTAGE = 0.05;
@@ -146207,7 +146107,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
146207
146107
  const [providerID, ...rest] = historianModelOverride.split("/");
146208
146108
  const modelID = rest.join("/");
146209
146109
  if (providerID && modelID) {
146210
- const limit = getModelsDevContextLimit(providerID, modelID);
146110
+ const limit = getSdkContextLimit(providerID, modelID);
146211
146111
  if (typeof limit === "number" && limit > 0)
146212
146112
  return limit;
146213
146113
  }
@@ -146226,7 +146126,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
146226
146126
  const modelID = rest.join("/");
146227
146127
  if (!providerID || !modelID)
146228
146128
  continue;
146229
- const limit = getModelsDevContextLimit(providerID, modelID);
146129
+ const limit = getSdkContextLimit(providerID, modelID);
146230
146130
  if (typeof limit !== "number" || limit <= 0)
146231
146131
  continue;
146232
146132
  if (minLimit === undefined || limit < minLimit)
@@ -146239,37 +146139,6 @@ function resolveHistorianContextLimit(historianModelOverride) {
146239
146139
  init_logger();
146240
146140
  var DEFAULT_CONTEXT_LIMIT = 128000;
146241
146141
  var MAX_EXECUTE_THRESHOLD = 80;
146242
- function resolveContextLimit(providerID, modelID, ctx) {
146243
- const fromModelsDev = providerID && modelID ? getModelsDevContextLimit(providerID, modelID) : undefined;
146244
- const baseline = fromModelsDev ?? DEFAULT_CONTEXT_LIMIT;
146245
- if (ctx?.db && ctx.sessionID) {
146246
- try {
146247
- const overflow = getOverflowState(ctx.db, ctx.sessionID);
146248
- if (overflow.detectedContextLimit > 0 && overflow.detectedContextLimit < baseline) {
146249
- return overflow.detectedContextLimit;
146250
- }
146251
- } catch {}
146252
- }
146253
- return baseline;
146254
- }
146255
- function resolveTrustedContextLimit(providerID, modelID, ctx) {
146256
- const fromModelsDev = providerID && modelID ? getModelsDevContextLimit(providerID, modelID) : undefined;
146257
- let detected;
146258
- if (ctx?.db && ctx.sessionID) {
146259
- try {
146260
- const overflow = getOverflowState(ctx.db, ctx.sessionID);
146261
- if (overflow.detectedContextLimit > 0) {
146262
- detected = overflow.detectedContextLimit;
146263
- }
146264
- } catch {}
146265
- }
146266
- if (typeof fromModelsDev === "number" && fromModelsDev > 0) {
146267
- if (detected !== undefined && detected < fromModelsDev)
146268
- return detected;
146269
- return fromModelsDev;
146270
- }
146271
- return detected;
146272
- }
146273
146142
  function resolveCacheTtl(cacheTtl, modelKey) {
146274
146143
  if (typeof cacheTtl === "string") {
146275
146144
  return cacheTtl;
@@ -146490,7 +146359,7 @@ function clearNoteNudgeTriggerOnly(db, sessionId) {
146490
146359
  }
146491
146360
 
146492
146361
  // ../plugin/src/hooks/magic-context/todo-view.ts
146493
- import { createHash as createHash6 } from "node:crypto";
146362
+ import { createHash as createHash5 } from "node:crypto";
146494
146363
  var TERMINAL_STATUSES = new Set(["completed", "cancelled"]);
146495
146364
  var TITLE_DONE_STATUSES = new Set(["completed"]);
146496
146365
  var SYNTHETIC_CALL_ID_PREFIX = "mc_synthetic_todo_";
@@ -146535,7 +146404,7 @@ function buildSyntheticTodoPart(stateJson) {
146535
146404
  };
146536
146405
  }
146537
146406
  function computeSyntheticCallId(stateJson) {
146538
- const hash = createHash6("sha256").update(stateJson).digest("hex").slice(0, 16);
146407
+ const hash = createHash5("sha256").update(stateJson).digest("hex").slice(0, 16);
146539
146408
  return `${SYNTHETIC_CALL_ID_PREFIX}${hash}`;
146540
146409
  }
146541
146410
  function parseTodoState(stateJson) {
@@ -146789,7 +146658,7 @@ init_logger();
146789
146658
 
146790
146659
  // src/subagent-runner.ts
146791
146660
  import * as childProcess from "node:child_process";
146792
- import { existsSync as existsSync7 } from "node:fs";
146661
+ import { existsSync as existsSync6 } from "node:fs";
146793
146662
  import { createRequire as createRequire2 } from "node:module";
146794
146663
  import { dirname as dirname3, join as join8, resolve as resolvePath } from "node:path";
146795
146664
  import { createInterface } from "node:readline";
@@ -146893,7 +146762,7 @@ function resolveBundledPiCli() {
146893
146762
  const require_ = createRequire2(import.meta.url);
146894
146763
  const pkgJson = require_.resolve("@earendil-works/pi-coding-agent/package.json");
146895
146764
  const cliPath = join8(dirname3(pkgJson), "dist/cli.js");
146896
- if (existsSync7(cliPath))
146765
+ if (existsSync6(cliPath))
146897
146766
  return cliPath;
146898
146767
  return null;
146899
146768
  } catch {
@@ -146904,7 +146773,7 @@ function resolveSubagentEntryPath() {
146904
146773
  try {
146905
146774
  const here = dirname3(fileURLToPath(import.meta.url));
146906
146775
  const candidate = resolvePath(here, "subagent-entry.js");
146907
- if (existsSync7(candidate))
146776
+ if (existsSync6(candidate))
146908
146777
  return candidate;
146909
146778
  return;
146910
146779
  } catch {
@@ -147664,7 +147533,7 @@ function clearStaleEntries(db, maxAgeMs, projectIdentity) {
147664
147533
  // src/commands/ctx-dream.ts
147665
147534
  init_logger();
147666
147535
  // ../plugin/src/features/magic-context/dreamer/runner.ts
147667
- import { existsSync as existsSync9 } from "node:fs";
147536
+ import { existsSync as existsSync8 } from "node:fs";
147668
147537
  import { join as join11 } from "node:path";
147669
147538
 
147670
147539
  // ../plugin/src/shared/index.ts
@@ -147918,12 +147787,12 @@ init_logger();
147918
147787
 
147919
147788
  // ../plugin/src/features/magic-context/key-files/aft-availability.ts
147920
147789
  var import_comment_json = __toESM(require_src2(), 1);
147921
- import { existsSync as existsSync8, readFileSync as readFileSync5 } from "node:fs";
147922
- import { homedir as homedir3 } from "node:os";
147790
+ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
147791
+ import { homedir as homedir2 } from "node:os";
147923
147792
  import { join as join9 } from "node:path";
147924
147793
  var overrideAvailability = null;
147925
147794
  function parseConfig(path7) {
147926
- if (!existsSync8(path7))
147795
+ if (!existsSync7(path7))
147927
147796
  return null;
147928
147797
  return import_comment_json.parse(readFileSync5(path7, "utf-8"));
147929
147798
  }
@@ -147945,7 +147814,7 @@ function hasAftAtKeys(value, keys) {
147945
147814
  return false;
147946
147815
  }
147947
147816
  function getAftAvailability() {
147948
- const home = process.env.HOME || homedir3();
147817
+ const home = process.env.HOME || homedir2();
147949
147818
  const opencodePaths = [
147950
147819
  join9(home, ".config", "opencode", "opencode.jsonc"),
147951
147820
  join9(home, ".config", "opencode", "opencode.json")
@@ -148589,13 +148458,13 @@ function invalidateMemory(projectPath, memoryId) {
148589
148458
  }
148590
148459
 
148591
148460
  // ../plugin/src/features/magic-context/memory/normalize-hash.ts
148592
- import { createHash as createHash7 } from "node:crypto";
148461
+ import { createHash as createHash6 } from "node:crypto";
148593
148462
  function normalizeMemoryContent(content) {
148594
148463
  return content.toLowerCase().replace(/\s+/g, " ").trim();
148595
148464
  }
148596
148465
  function computeNormalizedHash(content) {
148597
148466
  const normalized = normalizeMemoryContent(content);
148598
- return createHash7("md5").update(normalized).digest("hex");
148467
+ return createHash6("md5").update(normalized).digest("hex");
148599
148468
  }
148600
148469
 
148601
148470
  // ../plugin/src/features/magic-context/memory/storage-memory.ts
@@ -149746,7 +149615,7 @@ function getOpenCodeDbPath2() {
149746
149615
  }
149747
149616
  function openOpenCodeDb() {
149748
149617
  const dbPath = getOpenCodeDbPath2();
149749
- if (!existsSync9(dbPath)) {
149618
+ if (!existsSync8(dbPath)) {
149750
149619
  log(`[key-files] OpenCode DB not found at ${dbPath} — skipping`);
149751
149620
  return null;
149752
149621
  }
@@ -149896,8 +149765,8 @@ async function runDream(args) {
149896
149765
  try {
149897
149766
  const docsDir = args.sessionDirectory ?? args.projectIdentity;
149898
149767
  const existingDocs = taskName === "maintain-docs" ? {
149899
- architecture: existsSync9(join11(docsDir, "ARCHITECTURE.md")),
149900
- structure: existsSync9(join11(docsDir, "STRUCTURE.md"))
149768
+ architecture: existsSync8(join11(docsDir, "ARCHITECTURE.md")),
149769
+ structure: existsSync8(join11(docsDir, "STRUCTURE.md"))
149901
149770
  } : undefined;
149902
149771
  const userMemories = taskName === "archive-stale" ? getActiveUserMemories(args.db).map((um) => ({
149903
149772
  id: um.id,
@@ -150387,7 +150256,7 @@ async function processDreamQueue(args) {
150387
150256
  return null;
150388
150257
  }
150389
150258
  const projectDirectory = args.sessionDirectoryOverride ?? resolveDreamSessionDirectory(entry.projectIdentity);
150390
- log(`[dreamer] dequeued project ${entry.projectIdentity} (dir=${projectDirectory}), starting dream run`);
150259
+ log(`[dreamer] dequeued project ${entry.projectIdentity}, starting dream run`);
150391
150260
  let result;
150392
150261
  try {
150393
150262
  result = await runDream({
@@ -150511,6 +150380,7 @@ async function readGitCommits(directory, options = {}) {
150511
150380
  if (revision.startsWith("-")) {
150512
150381
  throw new Error(`readGitCommits: refusing revision that looks like an option: "${revision}"`);
150513
150382
  }
150383
+ const projectLabel = options.projectIdentity ?? "<project>";
150514
150384
  const args = [
150515
150385
  "log",
150516
150386
  revision,
@@ -150533,11 +150403,11 @@ async function readGitCommits(directory, options = {}) {
150533
150403
  stdout = result.stdout;
150534
150404
  } catch (error) {
150535
150405
  const message = error instanceof Error ? error.message : String(error);
150536
- log(`[git-commits] readGitCommits failed at cwd=${directory}: ${message.slice(0, 500)}`);
150406
+ log(`[git-commits] readGitCommits failed for ${projectLabel}: ${message.slice(0, 500)}`);
150537
150407
  return [];
150538
150408
  }
150539
150409
  if (stdout.trim().length === 0) {
150540
- log(`[git-commits] readGitCommits returned empty stdout at cwd=${directory} (sinceMs=${options.sinceMs ?? "none"} args=${args.slice(0, 4).join(" ")})`);
150410
+ log(`[git-commits] readGitCommits returned empty stdout for ${projectLabel} (sinceMs=${options.sinceMs ?? "none"} args=${args.slice(0, 4).join(" ")})`);
150541
150411
  }
150542
150412
  return parseGitLogOutput(stdout);
150543
150413
  }
@@ -165106,7 +164976,7 @@ function getEmbeddingProviderIdentity(config2) {
165106
164976
  }
165107
164977
 
165108
164978
  // ../plugin/src/features/magic-context/memory/embedding-local.ts
165109
- import { mkdirSync as mkdirSync4 } from "node:fs";
164979
+ import { mkdirSync as mkdirSync5 } from "node:fs";
165110
164980
  import { open, stat, unlink, writeFile } from "node:fs/promises";
165111
164981
  import { dirname as dirname4, join as join12 } from "node:path";
165112
164982
  import { pathToFileURL } from "node:url";
@@ -165287,7 +165157,7 @@ class LocalEmbeddingProvider {
165287
165157
  }
165288
165158
  const modelCacheDir = join12(getMagicContextStorageDir(), "models");
165289
165159
  try {
165290
- mkdirSync4(modelCacheDir, { recursive: true });
165160
+ mkdirSync5(modelCacheDir, { recursive: true });
165291
165161
  env.cacheDir = modelCacheDir;
165292
165162
  } catch {
165293
165163
  log("[magic-context] could not create model cache dir, using library default");
@@ -165662,7 +165532,7 @@ class OpenAICompatibleEmbeddingProvider {
165662
165532
  }
165663
165533
 
165664
165534
  // ../plugin/src/features/magic-context/project-embedding-registry.ts
165665
- import { createHash as createHash8 } from "node:crypto";
165535
+ import { createHash as createHash7, randomUUID } from "node:crypto";
165666
165536
  init_logger();
165667
165537
 
165668
165538
  // ../plugin/src/features/magic-context/git-commits/storage-git-commit-embeddings.ts
@@ -165766,6 +165636,125 @@ function getDistinctCommitEmbeddingModelIds(db, projectPath) {
165766
165636
  return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
165767
165637
  }
165768
165638
 
165639
+ // ../plugin/src/features/magic-context/git-commits/sweep-coordinator.ts
165640
+ var GIT_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
165641
+ var GIT_SWEEP_LEASE_TTL_MS = 5 * 60 * 1000;
165642
+ var GIT_SWEEP_LEASE_RENEWAL_MS = 60 * 1000;
165643
+ function runImmediate2(db, body) {
165644
+ db.exec("BEGIN IMMEDIATE");
165645
+ let committed = false;
165646
+ try {
165647
+ const result = body();
165648
+ db.exec("COMMIT");
165649
+ committed = true;
165650
+ return result;
165651
+ } finally {
165652
+ if (!committed) {
165653
+ try {
165654
+ db.exec("ROLLBACK");
165655
+ } catch {}
165656
+ }
165657
+ }
165658
+ }
165659
+ function rowToState(row) {
165660
+ return {
165661
+ projectPath: row.project_path,
165662
+ leaseHolder: row.lease_holder,
165663
+ leaseExpiresAt: row.lease_expires_at,
165664
+ lastSweptAt: row.last_swept_at
165665
+ };
165666
+ }
165667
+ function getGitSweepCoordinatorState(db, projectPath) {
165668
+ const row = db.prepare(`SELECT project_path, lease_holder, lease_expires_at, last_swept_at
165669
+ FROM git_sweep_coordinator
165670
+ WHERE project_path = ?`).get(projectPath);
165671
+ return row ? rowToState(row) : null;
165672
+ }
165673
+ function acquireGitSweepLease(db, projectPath, holderId, options = {}) {
165674
+ const cooldownMs = options.cooldownMs ?? GIT_SWEEP_COOLDOWN_MS;
165675
+ const leaseTtlMs = options.leaseTtlMs ?? GIT_SWEEP_LEASE_TTL_MS;
165676
+ return runImmediate2(db, () => {
165677
+ const now = Date.now();
165678
+ const row = getGitSweepCoordinatorState(db, projectPath);
165679
+ if (row?.leaseHolder && row.leaseExpiresAt !== null && row.leaseExpiresAt > now) {
165680
+ return {
165681
+ acquired: false,
165682
+ projectPath,
165683
+ reason: "lease_active",
165684
+ leaseHolder: row.leaseHolder,
165685
+ leaseExpiresAt: row.leaseExpiresAt,
165686
+ lastSweptAt: row.lastSweptAt,
165687
+ nextAllowedAt: null
165688
+ };
165689
+ }
165690
+ if (!options.ignoreCooldown && row?.lastSweptAt !== null && row?.lastSweptAt !== undefined) {
165691
+ const nextAllowedAt = row.lastSweptAt + cooldownMs;
165692
+ if (nextAllowedAt > now) {
165693
+ return {
165694
+ acquired: false,
165695
+ projectPath,
165696
+ reason: "cooldown_active",
165697
+ leaseHolder: row.leaseHolder,
165698
+ leaseExpiresAt: row.leaseExpiresAt,
165699
+ lastSweptAt: row.lastSweptAt,
165700
+ nextAllowedAt
165701
+ };
165702
+ }
165703
+ }
165704
+ const leaseExpiresAt = now + leaseTtlMs;
165705
+ db.prepare(`INSERT INTO git_sweep_coordinator (
165706
+ project_path,
165707
+ lease_holder,
165708
+ lease_expires_at,
165709
+ last_swept_at
165710
+ ) VALUES (?, ?, ?, NULL)
165711
+ ON CONFLICT(project_path) DO UPDATE SET
165712
+ lease_holder = excluded.lease_holder,
165713
+ lease_expires_at = excluded.lease_expires_at`).run(projectPath, holderId, leaseExpiresAt);
165714
+ return {
165715
+ acquired: true,
165716
+ projectPath,
165717
+ holderId,
165718
+ acquiredAt: now,
165719
+ leaseExpiresAt
165720
+ };
165721
+ });
165722
+ }
165723
+ function renewGitSweepLease(db, projectPath, holderId, leaseTtlMs = GIT_SWEEP_LEASE_TTL_MS) {
165724
+ return runImmediate2(db, () => {
165725
+ const now = Date.now();
165726
+ const leaseExpiresAt = now + leaseTtlMs;
165727
+ const result = db.prepare(`UPDATE git_sweep_coordinator
165728
+ SET lease_expires_at = ?
165729
+ WHERE project_path = ?
165730
+ AND lease_holder = ?
165731
+ AND lease_expires_at > ?`).run(leaseExpiresAt, projectPath, holderId, now);
165732
+ return result.changes === 1;
165733
+ });
165734
+ }
165735
+ function markGitSweepSuccessAndRelease(db, projectPath, holderId) {
165736
+ return runImmediate2(db, () => {
165737
+ const now = Date.now();
165738
+ const result = db.prepare(`UPDATE git_sweep_coordinator
165739
+ SET lease_holder = NULL,
165740
+ lease_expires_at = NULL,
165741
+ last_swept_at = ?
165742
+ WHERE project_path = ?
165743
+ AND lease_holder = ?
165744
+ AND lease_expires_at > ?`).run(now, projectPath, holderId, now);
165745
+ return result.changes === 1;
165746
+ });
165747
+ }
165748
+ function releaseGitSweepLease(db, projectPath, holderId) {
165749
+ runImmediate2(db, () => {
165750
+ db.prepare(`UPDATE git_sweep_coordinator
165751
+ SET lease_holder = NULL,
165752
+ lease_expires_at = NULL
165753
+ WHERE project_path = ?
165754
+ AND lease_holder = ?`).run(projectPath, holderId);
165755
+ });
165756
+ }
165757
+
165769
165758
  // ../plugin/src/features/magic-context/project-embedding-registry.ts
165770
165759
  var OFF_PROVIDER_IDENTITY = "embedding-provider:off";
165771
165760
  var SWEEP_MAX_WALL_CLOCK_MS = 10 * 60 * 1000;
@@ -165818,7 +165807,7 @@ function stableStringify2(value) {
165818
165807
  return JSON.stringify(value);
165819
165808
  }
165820
165809
  function sha256Prefix(value, length = 16) {
165821
- return createHash8("sha256").update(value).digest("hex").slice(0, length);
165810
+ return createHash7("sha256").update(value).digest("hex").slice(0, length);
165822
165811
  }
165823
165812
  function getRuntimeFingerprint(config2) {
165824
165813
  if (config2.provider === "off") {
@@ -166083,6 +166072,7 @@ var insertStatements = new WeakMap;
166083
166072
  var existingShasStatements = new WeakMap;
166084
166073
  var projectCountStatements = new WeakMap;
166085
166074
  var evictStatements = new WeakMap;
166075
+ var evictOverflowStatements = new WeakMap;
166086
166076
  var latestCommitTimeStatements = new WeakMap;
166087
166077
  function getInsertStatement(db) {
166088
166078
  let stmt = insertStatements.get(db);
@@ -166125,17 +166115,17 @@ function getLatestCommitTimeStatement(db) {
166125
166115
  }
166126
166116
  return stmt;
166127
166117
  }
166128
- function getEvictStatement(db) {
166129
- let stmt = evictStatements.get(db);
166118
+ function getEvictOverflowStatement(db) {
166119
+ let stmt = evictOverflowStatements.get(db);
166130
166120
  if (!stmt) {
166131
166121
  stmt = db.prepare(`DELETE FROM git_commits
166132
- WHERE sha IN (
166133
- SELECT sha FROM git_commits
166122
+ WHERE rowid IN (
166123
+ SELECT rowid FROM git_commits
166134
166124
  WHERE project_path = ?
166135
- ORDER BY committed_at ASC
166136
- LIMIT ?
166125
+ ORDER BY committed_at DESC, sha DESC
166126
+ LIMIT -1 OFFSET ?
166137
166127
  )`);
166138
- evictStatements.set(db, stmt);
166128
+ evictOverflowStatements.set(db, stmt);
166139
166129
  }
166140
166130
  return stmt;
166141
166131
  }
@@ -166173,22 +166163,15 @@ function getLatestIndexedCommitTimeMs(db, projectPath) {
166173
166163
  const row = getLatestCommitTimeStatement(db).get(projectPath);
166174
166164
  return row?.latest ?? null;
166175
166165
  }
166176
- function evictOldestCommits(db, projectPath, excess) {
166177
- if (excess <= 0)
166178
- return 0;
166179
- const before = getCommitCount(db, projectPath);
166180
- getEvictStatement(db).run(projectPath, excess);
166181
- const after = getCommitCount(db, projectPath);
166182
- return Math.max(0, before - after);
166183
- }
166184
166166
  function enforceProjectCap(db, projectPath, maxCommits) {
166185
166167
  if (maxCommits <= 0)
166186
166168
  return 0;
166187
166169
  const count = getCommitCount(db, projectPath);
166188
166170
  if (count <= maxCommits)
166189
166171
  return 0;
166190
- const excess = count - maxCommits;
166191
- const evicted = evictOldestCommits(db, projectPath, excess);
166172
+ getEvictOverflowStatement(db).run(projectPath, maxCommits);
166173
+ const after = getCommitCount(db, projectPath);
166174
+ const evicted = Math.max(0, count - after);
166192
166175
  if (evicted > 0) {
166193
166176
  log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
166194
166177
  }
@@ -166220,7 +166203,8 @@ async function indexCommitsForProject(db, projectPath, directory, options) {
166220
166203
  const sinceMs = latestIndexed !== null ? Math.max(latestIndexed - 60000, Date.now() - options.sinceDays * MS_PER_DAY) : Date.now() - options.sinceDays * MS_PER_DAY;
166221
166204
  const commits = await readGitCommits(directory, {
166222
166205
  sinceMs,
166223
- maxCommits: options.maxCommits
166206
+ maxCommits: options.maxCommits,
166207
+ projectIdentity: projectPath
166224
166208
  });
166225
166209
  result.scanned = commits.length;
166226
166210
  if (commits.length === 0) {
@@ -166487,7 +166471,7 @@ async function startDreamScheduleTimer(args) {
166487
166471
  const isNewRegistration = !registeredProjects.has(args.directory);
166488
166472
  registeredProjects.set(args.directory, args);
166489
166473
  if (isNewRegistration) {
166490
- log(`[dreamer] registered project ${args.directory} (dreaming=${dreamingEnabled} embeddings=${embeddingSweepEnabled} commits=${commitIndexingEnabled}; total=${registeredProjects.size})`);
166474
+ log(`[dreamer] registered project ${args.projectIdentity} (dreaming=${dreamingEnabled} embeddings=${embeddingSweepEnabled} commits=${commitIndexingEnabled}; total=${registeredProjects.size})`);
166491
166475
  }
166492
166476
  if (!activeTimer) {
166493
166477
  log(`[dreamer] started independent schedule timer (every ${DREAM_TIMER_INTERVAL_MS / 60000}m)`);
@@ -166502,7 +166486,7 @@ async function startDreamScheduleTimer(args) {
166502
166486
  }
166503
166487
  return () => {
166504
166488
  registeredProjects.delete(args.directory);
166505
- log(`[dreamer] unregistered project ${args.directory} (remaining=${registeredProjects.size})`);
166489
+ log(`[dreamer] unregistered project ${args.projectIdentity} (remaining=${registeredProjects.size})`);
166506
166490
  if (registeredProjects.size === 0 && activeTimer) {
166507
166491
  clearInterval(activeTimer);
166508
166492
  activeTimer = null;
@@ -166550,7 +166534,7 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
166550
166534
  return;
166551
166535
  }
166552
166536
  try {
166553
- log(`[dreamer] timer tick (${origin}) ${reg.directory} — checking schedule window "${reg.dreamerConfig.schedule}"`);
166537
+ log(`[dreamer] timer tick (${origin}) ${reg.projectIdentity} — checking schedule window "${reg.dreamerConfig.schedule}"`);
166554
166538
  checkScheduleAndEnqueue(db, reg.dreamerConfig.schedule, reg.projectIdentity);
166555
166539
  await processDreamQueue({
166556
166540
  db,
@@ -166565,13 +166549,34 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
166565
166549
  fallbackModels: resolveFallbackChain(DREAMER_AGENT, reg.dreamerConfig.fallback_models)
166566
166550
  });
166567
166551
  } catch (error51) {
166568
- log(`[dreamer] timer-triggered queue processing failed for ${reg.directory}:`, error51);
166552
+ log(`[dreamer] timer-triggered queue processing failed for ${reg.projectIdentity}:`, error51);
166569
166553
  }
166570
166554
  }
166555
+ function startGitSweepLeaseRenewal(db, projectIdentity, holderId) {
166556
+ const timer = setInterval(() => {
166557
+ try {
166558
+ if (!renewGitSweepLease(db, projectIdentity, holderId)) {
166559
+ log(`[git-commits] sweep lease renewal failed for ${projectIdentity}`);
166560
+ }
166561
+ } catch (error51) {
166562
+ log(`[git-commits] sweep lease renewal errored for ${projectIdentity}: ${error51 instanceof Error ? error51.message : String(error51)}`);
166563
+ }
166564
+ }, GIT_SWEEP_LEASE_RENEWAL_MS);
166565
+ timer.unref?.();
166566
+ return () => clearInterval(timer);
166567
+ }
166571
166568
  async function sweepGitCommits(args) {
166572
166569
  const { directory, projectIdentity, db, gitCommitIndexing } = args;
166570
+ const holderId = crypto.randomUUID();
166571
+ const lease2 = acquireGitSweepLease(db, projectIdentity, holderId);
166572
+ if (!lease2.acquired) {
166573
+ const reason = lease2.reason === "cooldown_active" ? `cooldown active until ${lease2.nextAllowedAt}` : `lease held by ${lease2.leaseHolder ?? "another holder"} until ${lease2.leaseExpiresAt ?? "unknown"}`;
166574
+ log(`[git-commits] sweep skipped for ${projectIdentity}: ${reason}`);
166575
+ return;
166576
+ }
166573
166577
  const startedAt = Date.now();
166574
- log(`[git-commits] sweep starting for ${directory} (sinceDays=${gitCommitIndexing.since_days} maxCommits=${gitCommitIndexing.max_commits})`);
166578
+ const stopRenewal = startGitSweepLeaseRenewal(db, projectIdentity, holderId);
166579
+ log(`[git-commits] sweep starting for ${projectIdentity} (sinceDays=${gitCommitIndexing.since_days} maxCommits=${gitCommitIndexing.max_commits})`);
166575
166580
  try {
166576
166581
  const result = await indexCommitsForProject(db, projectIdentity, directory, {
166577
166582
  sinceDays: gitCommitIndexing.since_days,
@@ -166581,16 +166586,24 @@ async function sweepGitCommits(args) {
166581
166586
  if (result.embedded > 0) {
166582
166587
  drainedEmbeddings = await embedUnembeddedCommits(db, projectIdentity);
166583
166588
  }
166589
+ const cooldownMarked = markGitSweepSuccessAndRelease(db, projectIdentity, holderId);
166590
+ if (!cooldownMarked) {
166591
+ releaseGitSweepLease(db, projectIdentity, holderId);
166592
+ log(`[git-commits] sweep finished for ${projectIdentity}, but lease was no longer active; cooldown not advanced`);
166593
+ }
166584
166594
  const elapsedMs = Date.now() - startedAt;
166585
166595
  log(`[git-commits] sweep finished for ${projectIdentity} in ${elapsedMs}ms: scanned=${result.scanned} inserted=${result.inserted} updated=${result.updated} evicted=${result.evicted} embedded=${result.embedded} drained=${drainedEmbeddings}`);
166586
166596
  } catch (error51) {
166597
+ releaseGitSweepLease(db, projectIdentity, holderId);
166587
166598
  const elapsedMs = Date.now() - startedAt;
166588
- log(`[git-commits] sweep failed for ${directory} after ${elapsedMs}ms: ${error51 instanceof Error ? error51.message : String(error51)}`);
166599
+ log(`[git-commits] sweep failed for ${projectIdentity} after ${elapsedMs}ms: ${error51 instanceof Error ? error51.message : String(error51)}`);
166600
+ } finally {
166601
+ stopRenewal();
166589
166602
  }
166590
166603
  }
166591
166604
 
166592
166605
  // ../plugin/src/plugin/embedding-bootstrap-helpers.ts
166593
- import { createHash as createHash9 } from "node:crypto";
166606
+ import { createHash as createHash8 } from "node:crypto";
166594
166607
  init_logger();
166595
166608
  var EMBEDDING_AFFECTING_KEYS = new Set([
166596
166609
  "embedding.api_key",
@@ -166602,7 +166615,7 @@ var EMBEDDING_AFFECTING_TOP_LEVEL_KEYS = new Set(["embedding", "memory", "experi
166602
166615
  var EMBEDDING_WARNING_TERMS = ["api_key", "endpoint", "model", "provider", "embedding"];
166603
166616
  var loggedFailureSignatures = new Map;
166604
166617
  function sha256Prefix2(value, length = 16) {
166605
- return createHash9("sha256").update(value).digest("hex").slice(0, length);
166618
+ return createHash8("sha256").update(value).digest("hex").slice(0, length);
166606
166619
  }
166607
166620
  function warningLooksEmbeddingRelated(message) {
166608
166621
  const lower = message.toLowerCase();
@@ -166666,8 +166679,8 @@ function handleUntrustedLoad(db, projectIdentity, directory, detailed) {
166666
166679
  }
166667
166680
 
166668
166681
  // src/config/index.ts
166669
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "node:fs";
166670
- import { homedir as homedir5 } from "node:os";
166682
+ import { existsSync as existsSync10, readFileSync as readFileSync8 } from "node:fs";
166683
+ import { homedir as homedir4 } from "node:os";
166671
166684
  import { join as join13 } from "node:path";
166672
166685
 
166673
166686
  // ../plugin/src/config/migrate-experimental.ts
@@ -166761,9 +166774,67 @@ function migrateLegacyExperimental(rawConfig, warnings) {
166761
166774
  }
166762
166775
 
166763
166776
  // ../plugin/src/config/variable.ts
166764
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "node:fs";
166765
- import { homedir as homedir4 } from "node:os";
166777
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
166778
+ import { homedir as homedir3 } from "node:os";
166766
166779
  import { dirname as dirname5, isAbsolute as isAbsolute2, resolve as resolve3 } from "node:path";
166780
+
166781
+ // ../plugin/src/shared/jsonc-parser.ts
166782
+ function stripJsonComments(content) {
166783
+ let result = "";
166784
+ let inString = false;
166785
+ let escaped = false;
166786
+ let inLineComment = false;
166787
+ let inBlockComment = false;
166788
+ for (let index = 0;index < content.length; index += 1) {
166789
+ const char = content[index];
166790
+ const next = content[index + 1];
166791
+ if (inLineComment) {
166792
+ if (char === `
166793
+ `) {
166794
+ inLineComment = false;
166795
+ result += char;
166796
+ }
166797
+ continue;
166798
+ }
166799
+ if (inBlockComment) {
166800
+ if (char === "*" && next === "/") {
166801
+ inBlockComment = false;
166802
+ index += 1;
166803
+ }
166804
+ continue;
166805
+ }
166806
+ if (inString) {
166807
+ result += char;
166808
+ if (escaped) {
166809
+ escaped = false;
166810
+ } else if (char === "\\") {
166811
+ escaped = true;
166812
+ } else if (char === '"') {
166813
+ inString = false;
166814
+ }
166815
+ continue;
166816
+ }
166817
+ if (char === '"') {
166818
+ inString = true;
166819
+ result += char;
166820
+ continue;
166821
+ }
166822
+ if (char === "/" && next === "/") {
166823
+ inLineComment = true;
166824
+ index += 1;
166825
+ continue;
166826
+ }
166827
+ if (char === "/" && next === "*") {
166828
+ inBlockComment = true;
166829
+ index += 1;
166830
+ continue;
166831
+ }
166832
+ result += char;
166833
+ }
166834
+ return result;
166835
+ }
166836
+
166837
+ // ../plugin/src/config/variable.ts
166767
166838
  var ENV_PATTERN = /\{env:([^}]+)\}/g;
166768
166839
  var FILE_PATTERN = /\{file:([^}]+)\}/g;
166769
166840
  function substituteConfigVariables(input) {
@@ -166815,11 +166886,11 @@ function substituteConfigVariables(input) {
166815
166886
  }
166816
166887
  let filePath = rawPath.trim();
166817
166888
  if (filePath.startsWith("~/")) {
166818
- filePath = resolve3(homedir4(), filePath.slice(2));
166889
+ filePath = resolve3(homedir3(), filePath.slice(2));
166819
166890
  } else if (!isAbsolute2(filePath)) {
166820
166891
  filePath = resolve3(configDir, filePath);
166821
166892
  }
166822
- if (!existsSync10(filePath)) {
166893
+ if (!existsSync9(filePath)) {
166823
166894
  warnings.push(`File not found for ${token} (resolved to ${filePath}); using empty string`);
166824
166895
  continue;
166825
166896
  }
@@ -166845,12 +166916,12 @@ function getProjectConfigPaths(cwd) {
166845
166916
  return [`${basePath}.jsonc`, `${basePath}.json`];
166846
166917
  }
166847
166918
  function getUserConfigPaths() {
166848
- const home = process.env.HOME ?? homedir5();
166919
+ const home = process.env.HOME ?? homedir4();
166849
166920
  const basePath = join13(home, ".pi", "agent", CONFIG_FILE_NAME);
166850
166921
  return [`${basePath}.jsonc`, `${basePath}.json`];
166851
166922
  }
166852
166923
  function resolveFirstExisting(paths) {
166853
- return paths.find((path7) => existsSync11(path7));
166924
+ return paths.find((path7) => existsSync10(path7));
166854
166925
  }
166855
166926
  function loadConfigFile(path7, scope) {
166856
166927
  try {
@@ -169454,6 +169525,62 @@ function buildToolTarget(part, message) {
169454
169525
 
169455
169526
  // ../plugin/src/features/magic-context/search.ts
169456
169527
  init_logger();
169528
+
169529
+ // ../plugin/src/features/magic-context/literal-probes.ts
169530
+ var MAX_PROBES = 5;
169531
+ var MIN_PROBE_LENGTH = 3;
169532
+ var SLASH_COMMAND_RE = /\/[a-z][a-z0-9]*(?:-[a-z0-9]+)+/gi;
169533
+ var KEBAB_SNAKE_RE = /[a-z][a-z0-9]*(?:[-_][a-z0-9]+)+/gi;
169534
+ var DOTTED_RE = /[a-z0-9][a-z0-9_-]*(?:\.[a-z0-9_-]+)+/gi;
169535
+ var CAMEL_RE = /\b[a-zA-Z][a-z0-9]*(?:[A-Z][a-z0-9]*)+\b/g;
169536
+ var SHA_RE = /\b[0-9a-f]{7,40}\b/gi;
169537
+ var ERROR_CODE_RE = /\b(?:TS\d{4,}|ERR_[A-Z][A-Z0-9_]*)\b/g;
169538
+ var QUOTED_RE = /["`]([^"`]{3,80})["`]/g;
169539
+ function looksLikeSha(token) {
169540
+ return /[0-9]/.test(token) && /^[0-9a-f]{7,40}$/i.test(token);
169541
+ }
169542
+ function extractLiteralProbes(query) {
169543
+ const trimmed = query.trim();
169544
+ if (trimmed.length === 0)
169545
+ return [];
169546
+ const ordered = [];
169547
+ const seen = new Set;
169548
+ const add = (raw) => {
169549
+ if (!raw)
169550
+ return;
169551
+ const probe = raw.trim();
169552
+ if (probe.length < MIN_PROBE_LENGTH)
169553
+ return;
169554
+ const key = probe.toLowerCase();
169555
+ if (seen.has(key))
169556
+ return;
169557
+ seen.add(key);
169558
+ ordered.push(probe);
169559
+ };
169560
+ for (const m of trimmed.matchAll(QUOTED_RE))
169561
+ add(m[1]);
169562
+ for (const m of trimmed.matchAll(SLASH_COMMAND_RE))
169563
+ add(m[0]);
169564
+ for (const m of trimmed.matchAll(ERROR_CODE_RE))
169565
+ add(m[0]);
169566
+ for (const m of trimmed.matchAll(DOTTED_RE))
169567
+ add(m[0]);
169568
+ for (const m of trimmed.matchAll(KEBAB_SNAKE_RE))
169569
+ add(m[0]);
169570
+ for (const m of trimmed.matchAll(CAMEL_RE))
169571
+ add(m[0]);
169572
+ for (const m of trimmed.matchAll(SHA_RE)) {
169573
+ if (looksLikeSha(m[0]))
169574
+ add(m[0]);
169575
+ }
169576
+ return ordered.slice(0, MAX_PROBES);
169577
+ }
169578
+ function containsProbeVerbatim(text, probes) {
169579
+ if (probes.length === 0)
169580
+ return false;
169581
+ const haystack = text.toLowerCase();
169582
+ return probes.some((probe) => haystack.includes(probe.toLowerCase()));
169583
+ }
169457
169584
  // ../plugin/src/features/magic-context/memory/embedding-backfill.ts
169458
169585
  init_logger();
169459
169586
  async function ensureMemoryEmbeddings(args) {
@@ -169716,36 +169843,82 @@ function linearDecayScore(rank, total) {
169716
169843
  return 0;
169717
169844
  return Math.max(0, 1 - rank / total);
169718
169845
  }
169719
- function searchMessages(args) {
169720
- const sanitizedQuery = sanitizeFtsQuery(args.query.trim());
169721
- if (sanitizedQuery.length === 0) {
169846
+ function runMessageFtsQuery(db, sessionId, ftsQuery, fetchLimit, cutoff) {
169847
+ if (ftsQuery.length === 0)
169722
169848
  return [];
169723
- }
169724
- const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
169725
- const rows = getMessageSearchStatement(args.db).all(args.sessionId, sanitizedQuery, fetchLimit).map((row) => row);
169726
- const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
169727
- const filtered = rows.map((row) => {
169849
+ const rows = getMessageSearchStatement(db).all(sessionId, ftsQuery, fetchLimit).map((row) => row);
169850
+ const result = [];
169851
+ for (const row of rows) {
169728
169852
  const messageOrdinal = getMessageOrdinal(row.messageOrdinal);
169729
169853
  if (messageOrdinal === null || typeof row.messageId !== "string" || typeof row.role !== "string" || typeof row.content !== "string") {
169730
- return null;
169854
+ continue;
169731
169855
  }
169732
169856
  if (cutoff !== null && messageOrdinal > cutoff) {
169733
- return null;
169857
+ continue;
169734
169858
  }
169735
- return {
169859
+ result.push({
169736
169860
  messageOrdinal,
169737
169861
  messageId: row.messageId,
169738
169862
  role: row.role,
169739
169863
  content: row.content
169740
- };
169741
- }).filter((result) => result !== null).slice(0, args.limit);
169742
- return filtered.map((row, rank) => ({
169864
+ });
169865
+ }
169866
+ return result;
169867
+ }
169868
+ var RRF_K = 60;
169869
+ var VERBATIM_PROBE_BONUS = 0.5;
169870
+ function searchMessages(args) {
169871
+ const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
169872
+ const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
169873
+ const baseQuery = sanitizeFtsQuery(args.query.trim());
169874
+ const probes = args.probes ?? [];
169875
+ if (probes.length === 0) {
169876
+ const filtered = runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff).slice(0, args.limit);
169877
+ return filtered.map((row, rank) => ({
169878
+ source: "message",
169879
+ content: previewText(row.content),
169880
+ score: linearDecayScore(rank, filtered.length),
169881
+ messageOrdinal: row.messageOrdinal,
169882
+ messageId: row.messageId,
169883
+ role: row.role
169884
+ }));
169885
+ }
169886
+ const queryLists = [];
169887
+ if (baseQuery.length > 0) {
169888
+ queryLists.push(runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff));
169889
+ }
169890
+ for (const probe of probes) {
169891
+ const probeQuery = sanitizeFtsQuery(probe);
169892
+ if (probeQuery.length === 0)
169893
+ continue;
169894
+ queryLists.push(runMessageFtsQuery(args.db, args.sessionId, probeQuery, fetchLimit, cutoff));
169895
+ }
169896
+ const fused = new Map;
169897
+ for (const list of queryLists) {
169898
+ list.forEach((row, rank) => {
169899
+ const rrf = 1 / (RRF_K + rank);
169900
+ const existing = fused.get(row.messageId);
169901
+ if (existing) {
169902
+ existing.score += rrf;
169903
+ } else {
169904
+ fused.set(row.messageId, { row, score: rrf });
169905
+ }
169906
+ });
169907
+ }
169908
+ for (const entry of fused.values()) {
169909
+ if (containsProbeVerbatim(entry.row.content, probes)) {
169910
+ entry.score += VERBATIM_PROBE_BONUS;
169911
+ }
169912
+ }
169913
+ const ranked = [...fused.values()].sort((a, b) => b.score !== a.score ? b.score - a.score : a.row.messageOrdinal - b.row.messageOrdinal).slice(0, args.limit);
169914
+ const maxScore = ranked.length > 0 ? ranked[0].score : 1;
169915
+ return ranked.map((entry) => ({
169743
169916
  source: "message",
169744
- content: previewText(row.content),
169745
- score: linearDecayScore(rank, filtered.length),
169746
- messageOrdinal: row.messageOrdinal,
169747
- messageId: row.messageId,
169748
- role: row.role
169917
+ content: previewText(entry.row.content),
169918
+ score: maxScore > 0 ? entry.score / maxScore : 0,
169919
+ messageOrdinal: entry.row.messageOrdinal,
169920
+ messageId: entry.row.messageId,
169921
+ role: entry.row.role
169749
169922
  }));
169750
169923
  }
169751
169924
  function getSourceBoost(result) {
@@ -169829,12 +170002,14 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
169829
170002
  return null;
169830
170003
  }) : Promise.resolve(null);
169831
170004
  await Promise.resolve();
170005
+ const messageProbes = options.explicitSearch ? extractLiteralProbes(trimmedQuery) : [];
169832
170006
  const messageResults = runMessages ? searchMessages({
169833
170007
  db,
169834
170008
  sessionId,
169835
170009
  query: trimmedQuery,
169836
170010
  limit: tierLimit,
169837
- maxOrdinal: options.maxMessageOrdinal
170011
+ maxOrdinal: options.maxMessageOrdinal,
170012
+ probes: messageProbes
169838
170013
  }) : [];
169839
170014
  const queryEmbedding = await queryEmbeddingPromise;
169840
170015
  const [memoryResults, gitCommitResults] = await Promise.all([
@@ -175697,7 +175872,7 @@ function stripPiDroppedPlaceholderMessages(args) {
175697
175872
  }
175698
175873
 
175699
175874
  // src/system-prompt.ts
175700
- import { createHash as createHash10 } from "node:crypto";
175875
+ import { createHash as createHash9 } from "node:crypto";
175701
175876
 
175702
175877
  // ../plugin/src/agents/magic-context-prompt.ts
175703
175878
  var LONG_TERM_PARTNER_FRAME = `### You are the user's long-term partner on this project — not a one-off hire
@@ -175842,7 +176017,7 @@ function processSystemPromptForCache(args) {
175842
176017
  sessionLog(sessionId, `system prompt date frozen: real=${liveDate}, using=${stickyDate} (cache-stable pass)`);
175843
176018
  }
175844
176019
  }
175845
- const currentHash = createHash10("md5").update(frozenPrompt).digest("hex");
176020
+ const currentHash = createHash9("md5").update(frozenPrompt).digest("hex");
175846
176021
  const hashChanged = !isFirstHash && currentHash !== previousHash;
175847
176022
  if (hashChanged) {
175848
176023
  sessionLog(sessionId, `system prompt hash changed: ${previousHash} → ${currentHash} (len=${frozenPrompt.length})`);
@@ -175887,10 +176062,8 @@ function injectPiTemporalMarkers(messages) {
175887
176062
  if (prefix !== null) {
175888
176063
  const userMsg = msg;
175889
176064
  if (typeof userMsg.content === "string") {
175890
- if (!TEMPORAL_MARKER_PATTERN.test(stripTagPrefix2(userMsg.content))) {
175891
- const tagMatch = userMsg.content.match(/^(?:§\d+§\s*)+/);
175892
- const tagPrefix = tagMatch ? tagMatch[0] : "";
175893
- const body = userMsg.content.slice(tagPrefix.length);
176065
+ if (!TEMPORAL_MARKER_PATTERN.test(stripTagPrefix(userMsg.content))) {
176066
+ const { tagPrefix, body } = peelLeadingMcTagNotation(userMsg.content);
175894
176067
  messages[i] = {
175895
176068
  ...userMsg,
175896
176069
  content: tagPrefix + prefix + body
@@ -175901,9 +176074,7 @@ function injectPiTemporalMarkers(messages) {
175901
176074
  const firstTextIndex = userMsg.content.findIndex((p) => p && typeof p === "object" && p.type === "text");
175902
176075
  if (firstTextIndex >= 0) {
175903
176076
  const existing = userMsg.content[firstTextIndex];
175904
- const tagMatch = existing.text.match(/^(?:§\d+§\s*)+/);
175905
- const tagPrefix = tagMatch ? tagMatch[0] : "";
175906
- const body = existing.text.slice(tagPrefix.length);
176077
+ const { tagPrefix, body } = peelLeadingMcTagNotation(existing.text);
175907
176078
  if (!TEMPORAL_MARKER_PATTERN.test(body)) {
175908
176079
  const newContent = userMsg.content.slice();
175909
176080
  newContent[firstTextIndex] = {
@@ -175926,12 +176097,6 @@ function injectPiTemporalMarkers(messages) {
175926
176097
  }
175927
176098
  return injected;
175928
176099
  }
175929
- function stripTagPrefix2(text) {
175930
- const match = text.match(/^(?:§\d+§\s*)+/);
175931
- if (!match)
175932
- return text;
175933
- return text.slice(match[0].length);
175934
- }
175935
176100
 
175936
176101
  // src/timeout.ts
175937
176102
  function withTimeout(p, ms) {
@@ -176958,14 +177123,10 @@ function registerPiContextHandler(pi, options) {
176958
177123
  }
176959
177124
  const sessionMeta = sessionMetaForUsage;
176960
177125
  const modelKey = liveModelBySession.get(sessionId);
176961
- if (usageContextLimit === undefined && modelKey) {
176962
- const { providerID, modelID } = splitModelKeyForPi(modelKey);
176963
- const trusted = resolveTrustedContextLimit(providerID, modelID, {
176964
- db: options.db,
176965
- sessionID: sessionId
176966
- });
176967
- if (trusted !== undefined && trusted > 0) {
176968
- usageContextLimit = trusted;
177126
+ if (usageContextLimit === undefined) {
177127
+ const modelWindow = ctx.model?.contextWindow;
177128
+ if (isSaneLimit(modelWindow)) {
177129
+ usageContextLimit = modelWindow;
176969
177130
  }
176970
177131
  }
176971
177132
  let schedulerDecision;
@@ -177218,27 +177379,8 @@ async function awaitInFlightHistorians() {
177218
177379
  return;
177219
177380
  await Promise.allSettled(Array.from(inFlightHistorian.values()));
177220
177381
  }
177221
- function splitModelKeyForPi(modelKey) {
177222
- if (!modelKey)
177223
- return { providerID: undefined, modelID: undefined };
177224
- const slash = modelKey.indexOf("/");
177225
- if (slash <= 0 || slash === modelKey.length - 1) {
177226
- return { providerID: undefined, modelID: undefined };
177227
- }
177228
- return {
177229
- providerID: modelKey.slice(0, slash),
177230
- modelID: modelKey.slice(slash + 1)
177231
- };
177232
- }
177233
177382
  function resolvePiHistorianTriggerInputs(args) {
177234
- const { providerID, modelID } = splitModelKeyForPi(args.modelKey);
177235
- let contextLimit = resolveContextLimit(providerID, modelID, {
177236
- db: args.db,
177237
- sessionID: args.sessionId
177238
- });
177239
- if ((providerID === undefined || modelID === undefined) && typeof args.usageContextLimit === "number" && Number.isFinite(args.usageContextLimit) && args.usageContextLimit > 0) {
177240
- contextLimit = args.usageContextLimit;
177241
- }
177383
+ const contextLimit = typeof args.usageContextLimit === "number" && Number.isFinite(args.usageContextLimit) && args.usageContextLimit > 0 ? args.usageContextLimit : DEFAULT_CONTEXT_LIMIT;
177242
177384
  const executeThresholdPercentage = resolveExecuteThreshold(args.historian.executeThresholdPercentage ?? 65, args.modelKey, 65, {
177243
177385
  tokensConfig: args.historian.executeThresholdTokens,
177244
177386
  contextLimit,
@@ -178060,7 +178202,7 @@ ${result}`;
178060
178202
  init_logger();
178061
178203
 
178062
178204
  // ../plugin/src/hooks/magic-context/historian-state-file.ts
178063
- import { mkdirSync as mkdirSync5, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
178205
+ import { mkdirSync as mkdirSync6, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
178064
178206
  init_data_path();
178065
178207
  function cleanupHistorianStateFile(path7) {
178066
178208
  if (!path7)
@@ -178264,7 +178406,7 @@ function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEn
178264
178406
  }
178265
178407
 
178266
178408
  // ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
178267
- import { mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
178409
+ import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
178268
178410
  import { join as join16 } from "node:path";
178269
178411
  init_data_path();
178270
178412
  function historianResponseDumpDir(directory) {
@@ -178569,11 +178711,11 @@ function cleanupHistorianDump(sessionId, dumpPath) {
178569
178711
  function dumpHistorianResponse(sessionId, directory, label, text) {
178570
178712
  try {
178571
178713
  const dumpDir = historianResponseDumpDir(directory);
178572
- mkdirSync6(dumpDir, { recursive: true });
178714
+ mkdirSync7(dumpDir, { recursive: true });
178573
178715
  const safeSessionId = sanitizeDumpName(sessionId);
178574
178716
  const safeLabel = sanitizeDumpName(label);
178575
178717
  const dumpPath = join16(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
178576
- writeFileSync3(dumpPath, text, "utf8");
178718
+ writeFileSync4(dumpPath, text, "utf8");
178577
178719
  sessionLog(sessionId, "compartment agent: historian response dumped", {
178578
178720
  label,
178579
178721
  dumpPath
@@ -180485,7 +180627,7 @@ function formatThresholdPercent(value) {
180485
180627
  // package.json
180486
180628
  var package_default = {
180487
180629
  name: "@wolfx/pi-magic-context",
180488
- version: "0.22.1-patch.0",
180630
+ version: "0.22.3",
180489
180631
  type: "module",
180490
180632
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
180491
180633
  main: "dist/index.js",
@@ -181070,10 +181212,6 @@ function computePiPressure(usage, contextLimit) {
181070
181212
  }
181071
181213
 
181072
181214
  // src/strip-tag-prefix.ts
181073
- var DEFAULT_PATTERNS = [
181074
- /^(\u00a7\d+\u00a7\s*)+/,
181075
- /\u00a7/g
181076
- ];
181077
181215
  function parseRegex(src) {
181078
181216
  if (src.startsWith("/")) {
181079
181217
  const lastSlash = src.lastIndexOf("/");
@@ -181085,43 +181223,42 @@ function parseRegex(src) {
181085
181223
  }
181086
181224
  return new RegExp(src);
181087
181225
  }
181088
- function resolveStripPatterns(stripConfig) {
181226
+ function stripTagPrefixFromAssistantMessage(message, stripConfig) {
181227
+ if (message.role !== "assistant")
181228
+ return false;
181229
+ if (!Array.isArray(message.content))
181230
+ return false;
181089
181231
  const isEnabled = stripConfig?.enabled ?? true;
181090
181232
  if (!isEnabled)
181091
- return [];
181233
+ return false;
181234
+ let patterns = null;
181092
181235
  if (stripConfig?.patterns && stripConfig.patterns.length > 0) {
181093
- return stripConfig.patterns.map(parseRegex);
181236
+ patterns = stripConfig.patterns.map(parseRegex);
181094
181237
  }
181095
- return DEFAULT_PATTERNS;
181096
- }
181097
- function createStripTagPrefixHandler(stripConfig) {
181098
- const patterns = resolveStripPatterns(stripConfig);
181099
- return function stripTagPrefixFromAssistantMessage(message) {
181100
- if (message.role !== "assistant")
181101
- return false;
181102
- if (!Array.isArray(message.content))
181103
- return false;
181104
- let mutated = false;
181105
- for (const part of message.content) {
181106
- if (part === null || typeof part !== "object" || part.type !== "text") {
181107
- continue;
181108
- }
181109
- const textPart = part;
181110
- if (typeof textPart.text !== "string")
181111
- continue;
181112
- let stripped = textPart.text;
181238
+ let mutated = false;
181239
+ for (const part of message.content) {
181240
+ if (part === null || typeof part !== "object" || part.type !== "text") {
181241
+ continue;
181242
+ }
181243
+ const textPart = part;
181244
+ if (typeof textPart.text !== "string")
181245
+ continue;
181246
+ let stripped;
181247
+ if (patterns) {
181248
+ stripped = textPart.text;
181113
181249
  for (const regex of patterns) {
181114
181250
  stripped = stripped.replace(regex, "");
181115
181251
  }
181116
- if (stripped !== textPart.text) {
181117
- textPart.text = stripped;
181118
- mutated = true;
181119
- }
181252
+ } else {
181253
+ stripped = stripPersistedAssistantText(textPart.text);
181120
181254
  }
181121
- return mutated;
181122
- };
181255
+ if (stripped !== textPart.text) {
181256
+ textPart.text = stripped;
181257
+ mutated = true;
181258
+ }
181259
+ }
181260
+ return mutated;
181123
181261
  }
181124
- var defaultHandler = createStripTagPrefixHandler();
181125
181262
 
181126
181263
  // ../plugin/src/tools/ctx-expand/constants.ts
181127
181264
  var CTX_EXPAND_DESCRIPTION = "Decompress a compartment range to see the original conversation transcript. " + 'Use start/end from <compartment start="N" end="M"> attributes. ' + "Returns the compacted U:/A: transcript for that message range, capped at ~15K tokens.";
@@ -185618,15 +185755,26 @@ function err3(text) {
185618
185755
  isError: true
185619
185756
  };
185620
185757
  }
185758
+ function captureAnchorOrdinal(db, sessionId) {
185759
+ try {
185760
+ const ordinal = getLastIndexedOrdinal(db, sessionId);
185761
+ return ordinal > 0 ? ordinal : null;
185762
+ } catch {
185763
+ return null;
185764
+ }
185765
+ }
185766
+ function anchorSuffix(note) {
185767
+ return note.anchorOrdinal !== null ? ` ↳ @msg ${note.anchorOrdinal}` : "";
185768
+ }
185621
185769
  function formatNoteLine(note) {
185622
185770
  if (note.type === "smart") {
185623
185771
  const conditionLine = note.status === "ready" ? note.readyReason ?? note.surfaceCondition ?? "Condition satisfied" : note.surfaceCondition ?? "No condition recorded";
185624
185772
  const statusSuffix2 = note.status === "active" ? "" : ` (${note.status})`;
185625
- return `- **#${note.id}**${statusSuffix2}: ${note.content}
185773
+ return `- **#${note.id}**${statusSuffix2}: ${note.content}${anchorSuffix(note)}
185626
185774
  *Condition*: ${conditionLine}`;
185627
185775
  }
185628
185776
  const statusSuffix = note.status === "active" ? "" : ` (${note.status})`;
185629
- return `- **#${note.id}**${statusSuffix}: ${note.content}`;
185777
+ return `- **#${note.id}**${statusSuffix}: ${note.content}${anchorSuffix(note)}`;
185630
185778
  }
185631
185779
  var DISMISS_FOOTER = `
185632
185780
 
@@ -185648,6 +185796,7 @@ function createCtxNoteTool(deps) {
185648
185796
  const content = params.content?.trim();
185649
185797
  if (!content)
185650
185798
  return err3("Error: 'content' is required when action is 'write'.");
185799
+ const anchorOrdinal = captureAnchorOrdinal(deps.db, sessionId);
185651
185800
  const surfaceCondition = params.surface_condition?.trim();
185652
185801
  if (surfaceCondition) {
185653
185802
  if (deps.dreamerEnabled !== true) {
@@ -185660,13 +185809,18 @@ function createCtxNoteTool(deps) {
185660
185809
  const note2 = addNote(deps.db, "smart", {
185661
185810
  content,
185662
185811
  projectPath: projectIdentity,
185663
- surfaceCondition
185812
+ surfaceCondition,
185813
+ anchorOrdinal
185664
185814
  });
185665
185815
  return ok3(`Created smart note #${note2.id}. Dreamer will evaluate the condition during nightly runs:
185666
185816
  - Content: ${content}
185667
185817
  - Condition: ${surfaceCondition}`);
185668
185818
  }
185669
- const note = addNote(deps.db, "session", { sessionId, content });
185819
+ const note = addNote(deps.db, "session", {
185820
+ sessionId,
185821
+ content,
185822
+ anchorOrdinal
185823
+ });
185670
185824
  return ok3(`Saved session note #${note.id}.`);
185671
185825
  }
185672
185826
  if (action2 === "dismiss") {
@@ -185729,9 +185883,13 @@ function createCtxNoteTool(deps) {
185729
185883
 
185730
185884
  No notes for the current filter.`);
185731
185885
  }
185732
- return ok3(`${sections.join(`
185886
+ const body = sections.join(`
185733
185887
 
185734
- `)}${DISMISS_FOOTER}`);
185888
+ `);
185889
+ const anchorHint = body.includes("↳ @msg ") ? `
185890
+
185891
+ ↳ @msg N marks the conversation tail when a note was written. To see what led to it: ctx_expand(start=N-x, end=N) (pick x for how far back to look).` : "";
185892
+ return ok3(`${body}${anchorHint}${DISMISS_FOOTER}`);
185735
185893
  }
185736
185894
  };
185737
185895
  }
@@ -186076,7 +186234,8 @@ function createCtxSearchTool(deps) {
186076
186234
  maxMessageOrdinal: lastCompartmentEnd >= 0 ? lastCompartmentEnd : undefined,
186077
186235
  gitCommitsEnabled,
186078
186236
  sources: params.sources,
186079
- visibleMemoryIds
186237
+ visibleMemoryIds,
186238
+ explicitSearch: true
186080
186239
  });
186081
186240
  return {
186082
186241
  content: [{ type: "text", text: formatSearchResults(query, results) }],
@@ -186224,8 +186383,7 @@ function getPiMessageModel(message) {
186224
186383
  };
186225
186384
  }
186226
186385
  function resolvePiPressureContextLimit(args) {
186227
- const cachedLimit = args.provider && args.model ? getModelsDevContextLimit(args.provider, args.model) : undefined;
186228
- let effectiveContextLimit = cachedLimit ?? args.piContextWindow;
186386
+ let effectiveContextLimit = isSaneLimit(args.piContextWindow) ? args.piContextWindow : 0;
186229
186387
  try {
186230
186388
  const overflowState = getOverflowState(args.db, args.sessionId);
186231
186389
  if (overflowState.detectedContextLimit > 0) {
@@ -186241,8 +186399,6 @@ async function persistPiPressureFromMessageEnd(args) {
186241
186399
  const effectiveContextLimit = resolvePiPressureContextLimit({
186242
186400
  db: args.db,
186243
186401
  sessionId: args.sessionId,
186244
- provider: provider2,
186245
- model,
186246
186402
  piContextWindow: args.piContextWindow
186247
186403
  });
186248
186404
  const usage = extractAssistantUsage(args.message);
@@ -186251,28 +186407,16 @@ async function persistPiPressureFromMessageEnd(args) {
186251
186407
  const messageHadOverflowError = typeof msg?.errorMessage === "string" && detectOverflow(msg.errorMessage).isOverflow;
186252
186408
  const updates = { lastResponseTime: Date.now() };
186253
186409
  if (pressure) {
186254
- let percentage = pressure.percentage;
186255
- let contextLimit = effectiveContextLimit;
186410
+ const percentage = pressure.percentage;
186411
+ const contextLimit = effectiveContextLimit;
186256
186412
  const meta3 = getOrCreateSessionMeta(args.db, args.sessionId);
186257
186413
  const observedSafeInputTokens = meta3.observedSafeInputTokens ?? 0;
186258
186414
  if (percentage > 100 && observedSafeInputTokens > 0 && pressure.inputTokens <= observedSafeInputTokens * 2) {
186259
- const oldLimit = contextLimit;
186260
- clearModelsDevCache();
186261
- contextLimit = resolvePiPressureContextLimit({
186262
- db: args.db,
186263
- sessionId: args.sessionId,
186264
- provider: provider2,
186265
- model,
186266
- piContextWindow: args.piContextWindow
186267
- });
186268
- if (contextLimit >= pressure.inputTokens) {
186269
- percentage = pressure.inputTokens / contextLimit * 100;
186270
- log(`${PREFIX} models-dev-cache: regression recovered for ${provider2}/${model} via Pi cache reload (was=${oldLimit}, now=${contextLimit})`);
186271
- } else if (!meta3.cacheAlertSent) {
186415
+ if (!meta3.cacheAlertSent) {
186272
186416
  updates.cacheAlertSent = true;
186273
186417
  const safeTokens = Math.max(observedSafeInputTokens, pressure.inputTokens);
186274
186418
  const modelLabel = provider2 && model ? `${provider2}/${model}` : "the active model";
186275
- await args.notifyIssue?.(`⚠️ Magic Context: Pi reports a context limit of ${formatTokens(contextLimit)} tokens for ${modelLabel} but you've successfully sent ${formatTokens(safeTokens)} tokens in this session — the cached limit looks wrong. Restart Pi if you suspect this is incorrect.`);
186419
+ await args.notifyIssue?.(`⚠️ Magic Context: Pi reports a context limit of ${formatTokens(contextLimit)} tokens for ${modelLabel} but you've successfully sent ${formatTokens(safeTokens)} tokens in this session — the reported limit looks wrong. Restart Pi if you suspect this is incorrect.`);
186276
186420
  }
186277
186421
  }
186278
186422
  updates.lastContextPercentage = percentage;
@@ -186710,15 +186854,14 @@ ${block}` : event.systemPrompt;
186710
186854
  info("session_before_compact: cancelling — magic-context owns compaction");
186711
186855
  return { cancel: true };
186712
186856
  });
186713
- const stripTagPrefix3 = createStripTagPrefixHandler(config2.text_complete_strip);
186714
186857
  pi.on("message_end", async (event, ctx) => {
186715
186858
  try {
186716
186859
  const msg = event.message;
186717
186860
  if (msg !== null && typeof msg === "object") {
186718
- stripTagPrefix3(msg);
186861
+ stripTagPrefixFromAssistantMessage(msg, config2.text_complete_strip);
186719
186862
  }
186720
186863
  } catch (err5) {
186721
- warn("message_end: stripTagPrefix threw:", err5);
186864
+ warn("message_end: stripTagPrefixFromAssistantMessage threw:", err5);
186722
186865
  }
186723
186866
  try {
186724
186867
  const sm = ctx.sessionManager;
@@ -186740,7 +186883,7 @@ ${block}` : event.systemPrompt;
186740
186883
  cacheTtlConfig: config2.cache_ttl
186741
186884
  });
186742
186885
  const piUsage = ctx.getContextUsage?.();
186743
- const piContextWindow = piUsage && typeof piUsage.contextWindow === "number" ? piUsage.contextWindow : 0;
186886
+ const piContextWindow = piUsage && typeof piUsage.contextWindow === "number" && piUsage.contextWindow > 0 ? piUsage.contextWindow : ctx.model?.contextWindow ?? 0;
186744
186887
  await persistPiPressureFromMessageEnd({
186745
186888
  db,
186746
186889
  sessionId,