@cortexkit/opencode-magic-context 0.22.1 → 0.22.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/config/agent-disable.d.ts +0 -9
  2. package/dist/config/agent-disable.d.ts.map +1 -1
  3. package/dist/config/schema/agent-overrides.d.ts +0 -3
  4. package/dist/config/schema/agent-overrides.d.ts.map +1 -1
  5. package/dist/features/builtin-commands/types.d.ts +0 -2
  6. package/dist/features/builtin-commands/types.d.ts.map +1 -1
  7. package/dist/features/magic-context/dreamer/scheduler.d.ts +0 -4
  8. package/dist/features/magic-context/dreamer/scheduler.d.ts.map +1 -1
  9. package/dist/features/magic-context/git-commits/index.d.ts +1 -0
  10. package/dist/features/magic-context/git-commits/index.d.ts.map +1 -1
  11. package/dist/features/magic-context/git-commits/storage-git-commits.d.ts.map +1 -1
  12. package/dist/features/magic-context/git-commits/sweep-coordinator.d.ts +48 -0
  13. package/dist/features/magic-context/git-commits/sweep-coordinator.d.ts.map +1 -0
  14. package/dist/features/magic-context/key-files/storage-key-files.d.ts +0 -5
  15. package/dist/features/magic-context/key-files/storage-key-files.d.ts.map +1 -1
  16. package/dist/features/magic-context/literal-probes.d.ts +24 -0
  17. package/dist/features/magic-context/literal-probes.d.ts.map +1 -0
  18. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  19. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  20. package/dist/features/magic-context/search.d.ts +7 -0
  21. package/dist/features/magic-context/search.d.ts.map +1 -1
  22. package/dist/features/magic-context/storage-db.d.ts +1 -1
  23. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  24. package/dist/features/magic-context/storage-notes.d.ts +8 -0
  25. package/dist/features/magic-context/storage-notes.d.ts.map +1 -1
  26. package/dist/hooks/magic-context/compartment-runner-types.d.ts +14 -1
  27. package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
  28. package/dist/hooks/magic-context/derive-budgets.d.ts +3 -3
  29. package/dist/hooks/magic-context/event-resolvers.d.ts +1 -0
  30. package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
  31. package/dist/hooks/magic-context/recomp-orchestrator.d.ts +7 -2
  32. package/dist/hooks/magic-context/recomp-orchestrator.d.ts.map +1 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +478 -168
  35. package/dist/plugin/dream-timer.d.ts.map +1 -1
  36. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  37. package/dist/shared/models-dev-cache.d.ts +54 -27
  38. package/dist/shared/models-dev-cache.d.ts.map +1 -1
  39. package/dist/shared/rpc-types.d.ts +3 -1
  40. package/dist/shared/rpc-types.d.ts.map +1 -1
  41. package/dist/tools/ctx-note/tools.d.ts.map +1 -1
  42. package/dist/tools/ctx-search/tools.d.ts.map +1 -1
  43. package/package.json +1 -1
  44. package/src/shared/models-dev-cache.test.ts +192 -360
  45. package/src/shared/models-dev-cache.ts +162 -193
  46. package/src/shared/rpc-types.ts +3 -1
  47. package/src/tui/index.tsx +17 -8
  48. package/src/tui/slots/sidebar-content.tsx +20 -10
package/dist/index.js CHANGED
@@ -15213,9 +15213,6 @@ function getMagicContextStorageDir() {
15213
15213
  function getLegacyOpenCodeMagicContextStorageDir() {
15214
15214
  return path2.join(getOpenCodeStorageDir(), "plugin", "magic-context");
15215
15215
  }
15216
- function getCacheDir() {
15217
- return process.env.XDG_CACHE_HOME ?? path2.join(os.homedir(), ".cache");
15218
- }
15219
15216
  var init_data_path = () => {};
15220
15217
 
15221
15218
  // src/shared/logger.ts
@@ -150727,6 +150724,17 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
150727
150724
  updated_at INTEGER NOT NULL DEFAULT 0
150728
150725
  );
150729
150726
 
150727
+ CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
150728
+ project_path TEXT PRIMARY KEY,
150729
+ lease_holder TEXT,
150730
+ lease_expires_at INTEGER,
150731
+ last_swept_at INTEGER
150732
+ );
150733
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
150734
+ ON git_sweep_coordinator(lease_expires_at);
150735
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
150736
+ ON git_sweep_coordinator(last_swept_at);
150737
+
150730
150738
  CREATE TABLE IF NOT EXISTS m0_mutation_log (
150731
150739
  id INTEGER PRIMARY KEY AUTOINCREMENT,
150732
150740
  session_id TEXT NOT NULL,
@@ -151256,7 +151264,7 @@ function getDatabasePersistenceError(db) {
151256
151264
  return null;
151257
151265
  return persistenceErrorByDatabase.get(db) ?? null;
151258
151266
  }
151259
- var databases, persistenceByDatabase, persistenceErrorByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 27, sqlitePragmaConfig;
151267
+ var databases, persistenceByDatabase, persistenceErrorByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 29, sqlitePragmaConfig;
151260
151268
  var init_storage_db = __esm(async () => {
151261
151269
  init_data_path();
151262
151270
  init_logger();
@@ -152047,6 +152055,38 @@ var init_migrations = __esm(async () => {
152047
152055
  ON tags(session_id, entry_fingerprint)
152048
152056
  WHERE type='message' AND entry_fingerprint IS NOT NULL`);
152049
152057
  }
152058
+ },
152059
+ {
152060
+ version: 28,
152061
+ description: "Add git commit sweep coordinator lease/cooldown table",
152062
+ up: (db) => {
152063
+ db.exec(`
152064
+ CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
152065
+ project_path TEXT PRIMARY KEY,
152066
+ lease_holder TEXT,
152067
+ lease_expires_at INTEGER,
152068
+ last_swept_at INTEGER
152069
+ );
152070
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
152071
+ ON git_sweep_coordinator(lease_expires_at);
152072
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
152073
+ ON git_sweep_coordinator(last_swept_at);
152074
+ `);
152075
+ }
152076
+ },
152077
+ {
152078
+ version: 29,
152079
+ description: "Add anchor_ordinal to notes (traceback to the conversation tail)",
152080
+ up: (db) => {
152081
+ const notesExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='notes'").get();
152082
+ if (!notesExists) {
152083
+ return;
152084
+ }
152085
+ const columns = db.prepare("PRAGMA table_info(notes)").all();
152086
+ if (!columns.some((column) => column.name === "anchor_ordinal")) {
152087
+ db.exec("ALTER TABLE notes ADD COLUMN anchor_ordinal INTEGER");
152088
+ }
152089
+ }
152050
152090
  }
152051
152091
  ];
152052
152092
  LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -153004,7 +153044,8 @@ function toNote(row) {
153004
153044
  updatedAt: row.updated_at,
153005
153045
  lastCheckedAt: toNullableNumber(row.last_checked_at),
153006
153046
  readyAt: toNullableNumber(row.ready_at),
153007
- readyReason: toNullableString(row.ready_reason)
153047
+ readyReason: toNullableString(row.ready_reason),
153048
+ anchorOrdinal: toNullableNumber(row.anchor_ordinal)
153008
153049
  };
153009
153050
  }
153010
153051
  function getNoteById(db, noteId) {
@@ -153056,7 +153097,7 @@ function getNotes(db, options = {}) {
153056
153097
  }
153057
153098
  function addNote(db, type, options) {
153058
153099
  const now = Date.now();
153059
- 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());
153100
+ 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);
153060
153101
  if (!isNoteRow(result)) {
153061
153102
  throw new Error("[notes] failed to insert note");
153062
153103
  }
@@ -164319,8 +164360,130 @@ var init_storage_git_commit_embeddings = __esm(() => {
164319
164360
  distinctModelIdStatements = new WeakMap;
164320
164361
  });
164321
164362
 
164363
+ // src/features/magic-context/git-commits/sweep-coordinator.ts
164364
+ function runImmediate2(db, body) {
164365
+ db.exec("BEGIN IMMEDIATE");
164366
+ let committed = false;
164367
+ try {
164368
+ const result = body();
164369
+ db.exec("COMMIT");
164370
+ committed = true;
164371
+ return result;
164372
+ } finally {
164373
+ if (!committed) {
164374
+ try {
164375
+ db.exec("ROLLBACK");
164376
+ } catch {}
164377
+ }
164378
+ }
164379
+ }
164380
+ function rowToState(row) {
164381
+ return {
164382
+ projectPath: row.project_path,
164383
+ leaseHolder: row.lease_holder,
164384
+ leaseExpiresAt: row.lease_expires_at,
164385
+ lastSweptAt: row.last_swept_at
164386
+ };
164387
+ }
164388
+ function getGitSweepCoordinatorState(db, projectPath) {
164389
+ const row = db.prepare(`SELECT project_path, lease_holder, lease_expires_at, last_swept_at
164390
+ FROM git_sweep_coordinator
164391
+ WHERE project_path = ?`).get(projectPath);
164392
+ return row ? rowToState(row) : null;
164393
+ }
164394
+ function acquireGitSweepLease(db, projectPath, holderId, options = {}) {
164395
+ const cooldownMs = options.cooldownMs ?? GIT_SWEEP_COOLDOWN_MS;
164396
+ const leaseTtlMs = options.leaseTtlMs ?? GIT_SWEEP_LEASE_TTL_MS;
164397
+ return runImmediate2(db, () => {
164398
+ const now = Date.now();
164399
+ const row = getGitSweepCoordinatorState(db, projectPath);
164400
+ if (row?.leaseHolder && row.leaseExpiresAt !== null && row.leaseExpiresAt > now) {
164401
+ return {
164402
+ acquired: false,
164403
+ projectPath,
164404
+ reason: "lease_active",
164405
+ leaseHolder: row.leaseHolder,
164406
+ leaseExpiresAt: row.leaseExpiresAt,
164407
+ lastSweptAt: row.lastSweptAt,
164408
+ nextAllowedAt: null
164409
+ };
164410
+ }
164411
+ if (!options.ignoreCooldown && row?.lastSweptAt !== null && row?.lastSweptAt !== undefined) {
164412
+ const nextAllowedAt = row.lastSweptAt + cooldownMs;
164413
+ if (nextAllowedAt > now) {
164414
+ return {
164415
+ acquired: false,
164416
+ projectPath,
164417
+ reason: "cooldown_active",
164418
+ leaseHolder: row.leaseHolder,
164419
+ leaseExpiresAt: row.leaseExpiresAt,
164420
+ lastSweptAt: row.lastSweptAt,
164421
+ nextAllowedAt
164422
+ };
164423
+ }
164424
+ }
164425
+ const leaseExpiresAt = now + leaseTtlMs;
164426
+ db.prepare(`INSERT INTO git_sweep_coordinator (
164427
+ project_path,
164428
+ lease_holder,
164429
+ lease_expires_at,
164430
+ last_swept_at
164431
+ ) VALUES (?, ?, ?, NULL)
164432
+ ON CONFLICT(project_path) DO UPDATE SET
164433
+ lease_holder = excluded.lease_holder,
164434
+ lease_expires_at = excluded.lease_expires_at`).run(projectPath, holderId, leaseExpiresAt);
164435
+ return {
164436
+ acquired: true,
164437
+ projectPath,
164438
+ holderId,
164439
+ acquiredAt: now,
164440
+ leaseExpiresAt
164441
+ };
164442
+ });
164443
+ }
164444
+ function renewGitSweepLease(db, projectPath, holderId, leaseTtlMs = GIT_SWEEP_LEASE_TTL_MS) {
164445
+ return runImmediate2(db, () => {
164446
+ const now = Date.now();
164447
+ const leaseExpiresAt = now + leaseTtlMs;
164448
+ const result = db.prepare(`UPDATE git_sweep_coordinator
164449
+ SET lease_expires_at = ?
164450
+ WHERE project_path = ?
164451
+ AND lease_holder = ?
164452
+ AND lease_expires_at > ?`).run(leaseExpiresAt, projectPath, holderId, now);
164453
+ return result.changes === 1;
164454
+ });
164455
+ }
164456
+ function markGitSweepSuccessAndRelease(db, projectPath, holderId) {
164457
+ return runImmediate2(db, () => {
164458
+ const now = Date.now();
164459
+ const result = db.prepare(`UPDATE git_sweep_coordinator
164460
+ SET lease_holder = NULL,
164461
+ lease_expires_at = NULL,
164462
+ last_swept_at = ?
164463
+ WHERE project_path = ?
164464
+ AND lease_holder = ?
164465
+ AND lease_expires_at > ?`).run(now, projectPath, holderId, now);
164466
+ return result.changes === 1;
164467
+ });
164468
+ }
164469
+ function releaseGitSweepLease(db, projectPath, holderId) {
164470
+ runImmediate2(db, () => {
164471
+ db.prepare(`UPDATE git_sweep_coordinator
164472
+ SET lease_holder = NULL,
164473
+ lease_expires_at = NULL
164474
+ WHERE project_path = ?
164475
+ AND lease_holder = ?`).run(projectPath, holderId);
164476
+ });
164477
+ }
164478
+ var GIT_SWEEP_COOLDOWN_MS, GIT_SWEEP_LEASE_TTL_MS, GIT_SWEEP_LEASE_RENEWAL_MS;
164479
+ var init_sweep_coordinator = __esm(() => {
164480
+ GIT_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
164481
+ GIT_SWEEP_LEASE_TTL_MS = 5 * 60 * 1000;
164482
+ GIT_SWEEP_LEASE_RENEWAL_MS = 60 * 1000;
164483
+ });
164484
+
164322
164485
  // src/features/magic-context/project-embedding-registry.ts
164323
- import { createHash as createHash7 } from "node:crypto";
164486
+ import { createHash as createHash7, randomUUID } from "node:crypto";
164324
164487
  function resolveEmbeddingConfig(config2) {
164325
164488
  if (!config2 || config2.provider === "local") {
164326
164489
  return {
@@ -164584,6 +164747,7 @@ var init_project_embedding_registry = __esm(() => {
164584
164747
  init_magic_context();
164585
164748
  init_logger();
164586
164749
  init_storage_git_commit_embeddings();
164750
+ init_sweep_coordinator();
164587
164751
  init_embedding_cache();
164588
164752
  init_embedding_identity();
164589
164753
  init_embedding_local();
@@ -164682,32 +164846,48 @@ var init_storage_memory_fts = __esm(() => {
164682
164846
  });
164683
164847
 
164684
164848
  // src/shared/models-dev-cache.ts
164685
- import { createHash as createHash9 } from "node:crypto";
164686
- import { existsSync as existsSync14, readFileSync as readFileSync12 } from "node:fs";
164687
- import { homedir as homedir9, platform as platform3 } from "node:os";
164849
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync12, renameSync as renameSync3, writeFileSync as writeFileSync4 } from "node:fs";
164688
164850
  import { join as join18 } from "node:path";
164689
- function hashFast(input) {
164690
- return createHash9("sha1").update(input).digest("hex");
164691
- }
164692
- function getModelsJsonPath() {
164693
- const explicit = process.env.OPENCODE_MODELS_PATH?.trim();
164694
- if (explicit)
164695
- return explicit;
164696
- const cacheBase = getCacheDir();
164697
- const source = process.env.OPENCODE_MODELS_URL?.trim();
164698
- const filename = source && source !== "https://models.dev" ? `models-${hashFast(source)}.json` : "models.json";
164699
- return join18(cacheBase, "opencode", filename);
164700
- }
164701
- function getOpencodeConfigPath() {
164702
- const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
164703
- const configDir = envDir ? envDir : platform3() === "win32" ? join18(homedir9(), ".config", "opencode") : join18(process.env.XDG_CONFIG_HOME || join18(homedir9(), ".config"), "opencode");
164704
- const jsonc = join18(configDir, "opencode.jsonc");
164705
- if (existsSync14(jsonc))
164706
- return jsonc;
164707
- const json2 = join18(configDir, "opencode.json");
164708
- if (existsSync14(json2))
164709
- return json2;
164710
- return null;
164851
+ function isSaneLimit(limit) {
164852
+ return typeof limit === "number" && limit >= MIN_SANE_LIMIT && limit <= MAX_SANE_LIMIT;
164853
+ }
164854
+ function persistFilePath() {
164855
+ return join18(getMagicContextStorageDir(), `model-context-limits-${getHarness()}.json`);
164856
+ }
164857
+ function loadPersistedApiCacheOnce() {
164858
+ if (persistSeedLoaded || apiCache !== null)
164859
+ return;
164860
+ persistSeedLoaded = true;
164861
+ try {
164862
+ const raw = readFileSync12(persistFilePath(), "utf-8");
164863
+ const obj = JSON.parse(raw);
164864
+ const map2 = new Map;
164865
+ for (const [key, limit] of Object.entries(obj)) {
164866
+ if (isSaneLimit(limit))
164867
+ map2.set(key, { limit });
164868
+ }
164869
+ if (map2.size > 0) {
164870
+ apiCache = map2;
164871
+ sessionLog("global", `models-dev-cache: seeded ${map2.size} entries from persisted cache (cold start)`);
164872
+ }
164873
+ } catch {}
164874
+ }
164875
+ function persistApiCache() {
164876
+ if (!apiCache)
164877
+ return;
164878
+ const obj = {};
164879
+ for (const [key, value] of apiCache) {
164880
+ if (isSaneLimit(value.limit))
164881
+ obj[key] = value.limit;
164882
+ }
164883
+ try {
164884
+ const dir = getMagicContextStorageDir();
164885
+ mkdirSync5(dir, { recursive: true });
164886
+ const target = persistFilePath();
164887
+ const tmp = `${target}.${process.pid}.tmp`;
164888
+ writeFileSync4(tmp, JSON.stringify(obj), { encoding: "utf-8", mode: 384 });
164889
+ renameSync3(tmp, target);
164890
+ } catch {}
164711
164891
  }
164712
164892
  function resolveLimit(limit) {
164713
164893
  if (!limit)
@@ -164720,7 +164900,7 @@ function resolveLimit(limit) {
164720
164900
  }
164721
164901
  function setCachedModelMetadata(cache, key, model) {
164722
164902
  const limit = resolveLimit(model?.limit);
164723
- if (limit === undefined) {
164903
+ if (!isSaneLimit(limit)) {
164724
164904
  return;
164725
164905
  }
164726
164906
  const value = { limit };
@@ -164732,54 +164912,26 @@ function setCachedModelMetadata(cache, key, model) {
164732
164912
  }
164733
164913
  }
164734
164914
  }
164735
- function loadModelsDevMetadataFromFile() {
164736
- const metadata = new Map;
164737
- const modelsJsonPath = getModelsJsonPath();
164738
- let fileFound = false;
164739
- try {
164740
- if (existsSync14(modelsJsonPath)) {
164741
- fileFound = true;
164742
- const raw = readFileSync12(modelsJsonPath, "utf-8");
164743
- const data = JSON.parse(raw);
164744
- for (const [providerId, provider2] of Object.entries(data)) {
164745
- if (!provider2?.models || typeof provider2.models !== "object")
164746
- continue;
164747
- for (const [modelId, model] of Object.entries(provider2.models)) {
164748
- setCachedModelMetadata(metadata, `${providerId}/${modelId}`, model);
164749
- }
164750
- }
164751
- }
164752
- } catch (error51) {
164753
- sessionLog("global", `models-dev-cache: failed to read models.json at ${modelsJsonPath}:`, error51 instanceof Error ? error51.message : String(error51));
164754
- }
164755
- try {
164756
- const configPath = getOpencodeConfigPath();
164757
- if (configPath && existsSync14(configPath)) {
164758
- const config2 = parseJsonc(readFileSync12(configPath, "utf-8"));
164759
- if (config2.provider && typeof config2.provider === "object") {
164760
- for (const [providerId, provider2] of Object.entries(config2.provider)) {
164761
- if (!provider2?.models || typeof provider2.models !== "object")
164762
- continue;
164763
- for (const [modelId, model] of Object.entries(provider2.models)) {
164764
- setCachedModelMetadata(metadata, `${providerId}/${modelId}`, model);
164765
- }
164766
- }
164767
- }
164915
+ async function refreshModelLimitsFromApi(client, options) {
164916
+ const attempts = Math.max(1, (options?.retries ?? 0) + 1);
164917
+ const delayMs = options?.retryDelayMs ?? 1000;
164918
+ for (let attempt = 1;attempt <= attempts; attempt++) {
164919
+ const ok = await refreshModelLimitsOnce(client);
164920
+ if (ok)
164921
+ return;
164922
+ if (attempt < attempts) {
164923
+ await new Promise((resolve6) => setTimeout(resolve6, delayMs));
164768
164924
  }
164769
- } catch (error51) {
164770
- sessionLog("global", "models-dev-cache: failed to read opencode config for custom models:", error51 instanceof Error ? error51.message : String(error51));
164771
164925
  }
164772
- sessionLog("global", `models-dev-cache: file-layer loaded ${metadata.size} model metadata entries (modelsJsonPath=${modelsJsonPath}, found=${fileFound})`);
164773
- return metadata;
164774
164926
  }
164775
- async function refreshModelLimitsFromApi(client) {
164927
+ async function refreshModelLimitsOnce(client) {
164776
164928
  try {
164777
164929
  const result = await client.config.providers();
164778
164930
  const data = result.data;
164779
164931
  const providers = data?.providers;
164780
- if (!Array.isArray(providers)) {
164781
- sessionLog("global", "models-dev-cache: API refresh returned no providers payload");
164782
- return;
164932
+ if (!Array.isArray(providers) || providers.length === 0) {
164933
+ sessionLog("global", "models-dev-cache: API refresh returned no providers payload (will retry if attempts remain)");
164934
+ return false;
164783
164935
  }
164784
164936
  const map2 = new Map;
164785
164937
  for (const entry of providers) {
@@ -164793,27 +164945,22 @@ async function refreshModelLimitsFromApi(client) {
164793
164945
  const previousSize = apiCache?.size ?? null;
164794
164946
  apiCache = map2;
164795
164947
  apiLoadedAt = Date.now();
164948
+ persistApiCache();
164796
164949
  if (previousSize === null) {
164797
164950
  sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model metadata entries`);
164798
164951
  } else if (previousSize !== map2.size) {
164799
164952
  sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model metadata entries (was ${previousSize})`);
164800
164953
  }
164954
+ return true;
164801
164955
  } catch (error51) {
164802
164956
  sessionLog("global", "models-dev-cache: API refresh failed:", error51 instanceof Error ? error51.message : String(error51));
164957
+ return false;
164803
164958
  }
164804
164959
  }
164805
- function getModelsDevContextLimit(providerID, modelID) {
164806
- const now = Date.now();
164807
- if (!fileCache || now - fileLastAttempt > RELOAD_INTERVAL_MS) {
164808
- fileLastAttempt = now;
164809
- fileCache = loadModelsDevMetadataFromFile();
164810
- }
164960
+ function getSdkContextLimit(providerID, modelID) {
164961
+ loadPersistedApiCacheOnce();
164811
164962
  const fromApi = lookupLimitWithTagFallback(apiCache, providerID, modelID);
164812
- const fromFile = lookupLimitWithTagFallback(fileCache, providerID, modelID);
164813
- if (typeof fromApi === "number" && typeof fromFile === "number") {
164814
- return Math.max(fromApi, fromFile);
164815
- }
164816
- return fromApi ?? fromFile;
164963
+ return isSaneLimit(fromApi) ? fromApi : undefined;
164817
164964
  }
164818
164965
  function lookupLimitWithTagFallback(cache, providerID, modelID) {
164819
164966
  if (!cache)
@@ -164830,12 +164977,10 @@ function lookupLimitWithTagFallback(cache, providerID, modelID) {
164830
164977
  }
164831
164978
  return;
164832
164979
  }
164833
- var RELOAD_INTERVAL_MS, apiCache = null, apiLoadedAt = 0, fileCache = null, fileLastAttempt = 0;
164980
+ var MIN_SANE_LIMIT = 20000, MAX_SANE_LIMIT = 3000000, apiCache = null, apiLoadedAt = 0, persistSeedLoaded = false;
164834
164981
  var init_models_dev_cache = __esm(() => {
164835
164982
  init_data_path();
164836
- init_jsonc_parser();
164837
164983
  init_logger();
164838
- RELOAD_INTERVAL_MS = 5 * 60 * 1000;
164839
164984
  });
164840
164985
 
164841
164986
  // src/shared/rpc-notifications.ts
@@ -165561,7 +165706,7 @@ var init_compartment_runner_validation = __esm(async () => {
165561
165706
  });
165562
165707
 
165563
165708
  // src/hooks/magic-context/compartment-runner-historian.ts
165564
- import { mkdirSync as mkdirSync5, unlinkSync, writeFileSync as writeFileSync4 } from "node:fs";
165709
+ import { mkdirSync as mkdirSync6, unlinkSync, writeFileSync as writeFileSync5 } from "node:fs";
165565
165710
  import { join as join21 } from "node:path";
165566
165711
  function historianResponseDumpDir(directory) {
165567
165712
  return getProjectMagicContextHistorianDir(directory);
@@ -165864,11 +166009,11 @@ function cleanupHistorianDump(sessionId, dumpPath) {
165864
166009
  function dumpHistorianResponse(sessionId, directory, label, text) {
165865
166010
  try {
165866
166011
  const dumpDir = historianResponseDumpDir(directory);
165867
- mkdirSync5(dumpDir, { recursive: true });
166012
+ mkdirSync6(dumpDir, { recursive: true });
165868
166013
  const safeSessionId = sanitizeDumpName(sessionId);
165869
166014
  const safeLabel = sanitizeDumpName(label);
165870
166015
  const dumpPath = join21(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
165871
- writeFileSync4(dumpPath, text, "utf8");
166016
+ writeFileSync5(dumpPath, text, "utf8");
165872
166017
  sessionLog(sessionId, "compartment agent: historian response dumped", {
165873
166018
  label,
165874
166019
  dumpPath
@@ -165990,7 +166135,7 @@ function insertCompartmentEvents(db, sessionId, events, compartmentIds) {
165990
166135
  var init_compartment_events = () => {};
165991
166136
 
165992
166137
  // src/hooks/magic-context/historian-state-file.ts
165993
- import { mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "node:fs";
166138
+ import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "node:fs";
165994
166139
  function cleanupHistorianStateFile(path6) {
165995
166140
  if (!path6)
165996
166141
  return;
@@ -170826,7 +170971,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
170826
170971
  const [providerID, ...rest] = historianModelOverride.split("/");
170827
170972
  const modelID = rest.join("/");
170828
170973
  if (providerID && modelID) {
170829
- const limit = getModelsDevContextLimit(providerID, modelID);
170974
+ const limit = getSdkContextLimit(providerID, modelID);
170830
170975
  if (typeof limit === "number" && limit > 0)
170831
170976
  return limit;
170832
170977
  }
@@ -170845,7 +170990,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
170845
170990
  const modelID = rest.join("/");
170846
170991
  if (!providerID || !modelID)
170847
170992
  continue;
170848
- const limit = getModelsDevContextLimit(providerID, modelID);
170993
+ const limit = getSdkContextLimit(providerID, modelID);
170849
170994
  if (typeof limit !== "number" || limit <= 0)
170850
170995
  continue;
170851
170996
  if (minLimit === undefined || limit < minLimit)
@@ -171212,6 +171357,7 @@ __export(exports_recomp_orchestrator, {
171212
171357
  setRecompNote: () => setRecompNote,
171213
171358
  runManagedUpgrade: () => runManagedUpgrade,
171214
171359
  runManagedRecomp: () => runManagedRecomp,
171360
+ isRecompSkip: () => isRecompSkip,
171215
171361
  isRecompFailure: () => isRecompFailure,
171216
171362
  isRecompComplete: () => isRecompComplete,
171217
171363
  extractRecompReason: () => extractRecompReason,
@@ -171224,6 +171370,9 @@ function resolveLiveModelKey(liveSessionState, sessionId) {
171224
171370
  function isRecompFailure(message) {
171225
171371
  return /—\s*(Failed|Skipped)/.test(message);
171226
171372
  }
171373
+ function isRecompSkip(message) {
171374
+ return /—\s*Skipped|already mutating compartment state|already running/i.test(message);
171375
+ }
171227
171376
  function isRecompComplete(message) {
171228
171377
  return /—\s*Complete/.test(message);
171229
171378
  }
@@ -171239,9 +171388,10 @@ function contextualizeUpgradeReason(reason) {
171239
171388
  }
171240
171389
  return rewritten;
171241
171390
  }
171242
- function setRecompStarting(liveSessionState, sessionId, note) {
171391
+ function setRecompStarting(liveSessionState, sessionId, note, kind = "recomp") {
171243
171392
  liveSessionState.recompProgressBySession.set(sessionId, {
171244
171393
  sessionId,
171394
+ kind,
171245
171395
  phase: "recomp",
171246
171396
  processedMessages: 0,
171247
171397
  totalMessages: 0,
@@ -171266,6 +171416,7 @@ function setRecompTerminal(liveSessionState, sessionId, phase, message) {
171266
171416
  const existing = liveSessionState.recompProgressBySession.get(sessionId);
171267
171417
  liveSessionState.recompProgressBySession.set(sessionId, {
171268
171418
  sessionId,
171419
+ kind: existing?.kind ?? "recomp",
171269
171420
  phase,
171270
171421
  processedMessages: existing?.processedMessages ?? 0,
171271
171422
  totalMessages: existing?.totalMessages ?? 0,
@@ -171275,10 +171426,10 @@ function setRecompTerminal(liveSessionState, sessionId, phase, message) {
171275
171426
  updatedAt: Date.now(),
171276
171427
  message
171277
171428
  });
171278
- if (phase === "done") {
171429
+ if (phase === "done" || phase === "skipped") {
171279
171430
  const t = setTimeout(() => {
171280
171431
  const cur = liveSessionState.recompProgressBySession.get(sessionId);
171281
- if (cur?.phase === "done")
171432
+ if (cur?.phase === phase)
171282
171433
  liveSessionState.recompProgressBySession.delete(sessionId);
171283
171434
  }, RECOMP_DONE_GRACE_MS);
171284
171435
  t.unref?.();
@@ -171307,7 +171458,11 @@ function buildRecompDeps(ctx, sessionId) {
171307
171458
  ctx.liveSessionState.deferredHistoryRefreshSessions.add(sid);
171308
171459
  },
171309
171460
  onRecompProgress: (p) => {
171310
- ctx.liveSessionState.recompProgressBySession.set(sessionId, p);
171461
+ const prevKind = ctx.liveSessionState.recompProgressBySession.get(sessionId)?.kind ?? "recomp";
171462
+ ctx.liveSessionState.recompProgressBySession.set(sessionId, {
171463
+ ...p,
171464
+ kind: p.kind ?? prevKind
171465
+ });
171311
171466
  }
171312
171467
  };
171313
171468
  }
@@ -171326,10 +171481,11 @@ async function resolveSessionDirectory(ctx, sessionId) {
171326
171481
  return ctx.directory;
171327
171482
  }
171328
171483
  async function runManagedRecomp(ctx, sessionId, options) {
171329
- setRecompStarting(ctx.liveSessionState, sessionId, "Starting recomp…");
171484
+ setRecompStarting(ctx.liveSessionState, sessionId, "Starting recomp…", "recomp");
171330
171485
  try {
171331
171486
  const message = await executeContextRecomp(buildRecompDeps(ctx, sessionId), options);
171332
- setRecompTerminal(ctx.liveSessionState, sessionId, isRecompFailure(message) ? "failed" : "done", extractRecompReason(message));
171487
+ const terminalPhase = isRecompSkip(message) ? "skipped" : isRecompFailure(message) ? "failed" : "done";
171488
+ setRecompTerminal(ctx.liveSessionState, sessionId, terminalPhase, extractRecompReason(message));
171333
171489
  return message;
171334
171490
  } catch (error51) {
171335
171491
  setRecompTerminal(ctx.liveSessionState, sessionId, "failed", `Recomp crashed: ${String(error51)}`);
@@ -171339,7 +171495,7 @@ Recomp crashed: ${String(error51)}`;
171339
171495
  }
171340
171496
  }
171341
171497
  async function runManagedUpgrade(ctx, sessionId) {
171342
- setRecompStarting(ctx.liveSessionState, sessionId, "Starting upgrade…");
171498
+ setRecompStarting(ctx.liveSessionState, sessionId, "Starting upgrade…", "upgrade");
171343
171499
  try {
171344
171500
  const compartments = getCompartments(ctx.db, sessionId);
171345
171501
  const legacyCount = compartments.filter((c) => c.legacy === 1 || !c.p1 || c.p1.trim() === "").length;
@@ -171397,6 +171553,7 @@ async function runUpgradeMemoryMigration(ctx, sessionId, migrationDirectory) {
171397
171553
  const prev = ctx.liveSessionState.recompProgressBySession.get(sessionId);
171398
171554
  ctx.liveSessionState.recompProgressBySession.set(sessionId, {
171399
171555
  sessionId,
171556
+ kind: prev?.kind ?? "upgrade",
171400
171557
  phase: "migration",
171401
171558
  processedMessages: prev?.processedMessages ?? 0,
171402
171559
  totalMessages: prev?.totalMessages ?? 0,
@@ -171493,7 +171650,7 @@ var exports_tui_config = {};
171493
171650
  __export(exports_tui_config, {
171494
171651
  ensureTuiPluginEntry: () => ensureTuiPluginEntry
171495
171652
  });
171496
- import { existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync16, writeFileSync as writeFileSync8 } from "node:fs";
171653
+ import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "node:fs";
171497
171654
  import { dirname as dirname9, join as join25 } from "node:path";
171498
171655
  function isMagicContextEntry(entry) {
171499
171656
  if (!entry)
@@ -171510,9 +171667,9 @@ function resolveTuiConfigPath() {
171510
171667
  const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
171511
171668
  const jsoncPath = join25(configDir, "tui.jsonc");
171512
171669
  const jsonPath = join25(configDir, "tui.json");
171513
- if (existsSync16(jsoncPath))
171670
+ if (existsSync15(jsoncPath))
171514
171671
  return jsoncPath;
171515
- if (existsSync16(jsonPath))
171672
+ if (existsSync15(jsonPath))
171516
171673
  return jsonPath;
171517
171674
  return jsonPath;
171518
171675
  }
@@ -171520,7 +171677,7 @@ function ensureTuiPluginEntry() {
171520
171677
  try {
171521
171678
  const configPath = resolveTuiConfigPath();
171522
171679
  let config2 = {};
171523
- if (existsSync16(configPath)) {
171680
+ if (existsSync15(configPath)) {
171524
171681
  const raw = readFileSync16(configPath, "utf-8");
171525
171682
  config2 = import_comment_json4.parse(raw) ?? {};
171526
171683
  }
@@ -171540,8 +171697,8 @@ function ensureTuiPluginEntry() {
171540
171697
  plugins.push(PLUGIN_ENTRY);
171541
171698
  }
171542
171699
  config2.plugin = plugins;
171543
- mkdirSync9(dirname9(configPath), { recursive: true });
171544
- writeFileSync8(configPath, `${import_comment_json4.stringify(config2, null, 2)}
171700
+ mkdirSync10(dirname9(configPath), { recursive: true });
171701
+ writeFileSync9(configPath, `${import_comment_json4.stringify(config2, null, 2)}
171545
171702
  `);
171546
171703
  log(`[magic-context] updated TUI plugin entry in ${configPath}`);
171547
171704
  return true;
@@ -175288,6 +175445,7 @@ var insertStatements = new WeakMap;
175288
175445
  var existingShasStatements = new WeakMap;
175289
175446
  var projectCountStatements = new WeakMap;
175290
175447
  var evictStatements = new WeakMap;
175448
+ var evictOverflowStatements = new WeakMap;
175291
175449
  var latestCommitTimeStatements = new WeakMap;
175292
175450
  function getInsertStatement(db) {
175293
175451
  let stmt = insertStatements.get(db);
@@ -175330,17 +175488,17 @@ function getLatestCommitTimeStatement(db) {
175330
175488
  }
175331
175489
  return stmt;
175332
175490
  }
175333
- function getEvictStatement(db) {
175334
- let stmt = evictStatements.get(db);
175491
+ function getEvictOverflowStatement(db) {
175492
+ let stmt = evictOverflowStatements.get(db);
175335
175493
  if (!stmt) {
175336
175494
  stmt = db.prepare(`DELETE FROM git_commits
175337
- WHERE sha IN (
175338
- SELECT sha FROM git_commits
175495
+ WHERE rowid IN (
175496
+ SELECT rowid FROM git_commits
175339
175497
  WHERE project_path = ?
175340
- ORDER BY committed_at ASC
175341
- LIMIT ?
175498
+ ORDER BY committed_at DESC, sha DESC
175499
+ LIMIT -1 OFFSET ?
175342
175500
  )`);
175343
- evictStatements.set(db, stmt);
175501
+ evictOverflowStatements.set(db, stmt);
175344
175502
  }
175345
175503
  return stmt;
175346
175504
  }
@@ -175378,22 +175536,15 @@ function getLatestIndexedCommitTimeMs(db, projectPath) {
175378
175536
  const row = getLatestCommitTimeStatement(db).get(projectPath);
175379
175537
  return row?.latest ?? null;
175380
175538
  }
175381
- function evictOldestCommits(db, projectPath, excess) {
175382
- if (excess <= 0)
175383
- return 0;
175384
- const before = getCommitCount(db, projectPath);
175385
- getEvictStatement(db).run(projectPath, excess);
175386
- const after = getCommitCount(db, projectPath);
175387
- return Math.max(0, before - after);
175388
- }
175389
175539
  function enforceProjectCap(db, projectPath, maxCommits) {
175390
175540
  if (maxCommits <= 0)
175391
175541
  return 0;
175392
175542
  const count = getCommitCount(db, projectPath);
175393
175543
  if (count <= maxCommits)
175394
175544
  return 0;
175395
- const excess = count - maxCommits;
175396
- const evicted = evictOldestCommits(db, projectPath, excess);
175545
+ getEvictOverflowStatement(db).run(projectPath, maxCommits);
175546
+ const after = getCommitCount(db, projectPath);
175547
+ const evicted = Math.max(0, count - after);
175397
175548
  if (evicted > 0) {
175398
175549
  log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
175399
175550
  }
@@ -175637,6 +175788,7 @@ function searchGitCommitsSync(db, projectPath, query, options) {
175637
175788
 
175638
175789
  // src/features/magic-context/git-commits/index.ts
175639
175790
  init_storage_git_commit_embeddings();
175791
+ init_sweep_coordinator();
175640
175792
 
175641
175793
  // src/plugin/dream-timer.ts
175642
175794
  init_embedding();
@@ -175750,10 +175902,31 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
175750
175902
  log(`[dreamer] timer-triggered queue processing failed for ${reg.directory}:`, error51);
175751
175903
  }
175752
175904
  }
175905
+ function startGitSweepLeaseRenewal(db, projectIdentity, holderId) {
175906
+ const timer = setInterval(() => {
175907
+ try {
175908
+ if (!renewGitSweepLease(db, projectIdentity, holderId)) {
175909
+ log(`[git-commits] sweep lease renewal failed for ${projectIdentity}`);
175910
+ }
175911
+ } catch (error51) {
175912
+ log(`[git-commits] sweep lease renewal errored for ${projectIdentity}: ${error51 instanceof Error ? error51.message : String(error51)}`);
175913
+ }
175914
+ }, GIT_SWEEP_LEASE_RENEWAL_MS);
175915
+ timer.unref?.();
175916
+ return () => clearInterval(timer);
175917
+ }
175753
175918
  async function sweepGitCommits(args) {
175754
175919
  const { directory, projectIdentity, db, gitCommitIndexing } = args;
175920
+ const holderId = crypto.randomUUID();
175921
+ const lease2 = acquireGitSweepLease(db, projectIdentity, holderId);
175922
+ if (!lease2.acquired) {
175923
+ const reason = lease2.reason === "cooldown_active" ? `cooldown active until ${lease2.nextAllowedAt}` : `lease held by ${lease2.leaseHolder ?? "another holder"} until ${lease2.leaseExpiresAt ?? "unknown"}`;
175924
+ log(`[git-commits] sweep skipped for ${projectIdentity} (${directory}): ${reason}`);
175925
+ return;
175926
+ }
175755
175927
  const startedAt = Date.now();
175756
- log(`[git-commits] sweep starting for ${directory} (sinceDays=${gitCommitIndexing.since_days} maxCommits=${gitCommitIndexing.max_commits})`);
175928
+ const stopRenewal = startGitSweepLeaseRenewal(db, projectIdentity, holderId);
175929
+ log(`[git-commits] sweep starting for ${directory} (project=${projectIdentity} sinceDays=${gitCommitIndexing.since_days} maxCommits=${gitCommitIndexing.max_commits})`);
175757
175930
  try {
175758
175931
  const result = await indexCommitsForProject(db, projectIdentity, directory, {
175759
175932
  sinceDays: gitCommitIndexing.since_days,
@@ -175763,11 +175936,19 @@ async function sweepGitCommits(args) {
175763
175936
  if (result.embedded > 0) {
175764
175937
  drainedEmbeddings = await embedUnembeddedCommits(db, projectIdentity);
175765
175938
  }
175939
+ const cooldownMarked = markGitSweepSuccessAndRelease(db, projectIdentity, holderId);
175940
+ if (!cooldownMarked) {
175941
+ releaseGitSweepLease(db, projectIdentity, holderId);
175942
+ log(`[git-commits] sweep finished for ${projectIdentity}, but lease was no longer active; cooldown not advanced`);
175943
+ }
175766
175944
  const elapsedMs = Date.now() - startedAt;
175767
175945
  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}`);
175768
175946
  } catch (error51) {
175947
+ releaseGitSweepLease(db, projectIdentity, holderId);
175769
175948
  const elapsedMs = Date.now() - startedAt;
175770
175949
  log(`[git-commits] sweep failed for ${directory} after ${elapsedMs}ms: ${error51 instanceof Error ? error51.message : String(error51)}`);
175950
+ } finally {
175951
+ stopRenewal();
175771
175952
  }
175772
175953
  }
175773
175954
 
@@ -175908,7 +176089,7 @@ init_models_dev_cache();
175908
176089
  var DEFAULT_CONTEXT_LIMIT = 128000;
175909
176090
  var MAX_EXECUTE_THRESHOLD = 80;
175910
176091
  function resolveContextLimit(providerID, modelID, ctx) {
175911
- const fromModelsDev = providerID && modelID ? getModelsDevContextLimit(providerID, modelID) : undefined;
176092
+ const fromModelsDev = providerID && modelID ? getSdkContextLimit(providerID, modelID) : undefined;
175912
176093
  const baseline = fromModelsDev ?? DEFAULT_CONTEXT_LIMIT;
175913
176094
  if (ctx?.db && ctx.sessionID) {
175914
176095
  try {
@@ -175921,7 +176102,7 @@ function resolveContextLimit(providerID, modelID, ctx) {
175921
176102
  return baseline;
175922
176103
  }
175923
176104
  function resolveTrustedContextLimit(providerID, modelID, ctx) {
175924
- const fromModelsDev = providerID && modelID ? getModelsDevContextLimit(providerID, modelID) : undefined;
176105
+ const fromModelsDev = providerID && modelID ? getSdkContextLimit(providerID, modelID) : undefined;
175925
176106
  let detected;
175926
176107
  if (ctx?.db && ctx.sessionID) {
175927
176108
  try {
@@ -179346,6 +179527,64 @@ init_embedding();
179346
179527
 
179347
179528
  // src/features/magic-context/search.ts
179348
179529
  init_logger();
179530
+
179531
+ // src/features/magic-context/literal-probes.ts
179532
+ var MAX_PROBES = 5;
179533
+ var MIN_PROBE_LENGTH = 3;
179534
+ var SLASH_COMMAND_RE = /\/[a-z][a-z0-9]*(?:-[a-z0-9]+)+/gi;
179535
+ var KEBAB_SNAKE_RE = /[a-z][a-z0-9]*(?:[-_][a-z0-9]+)+/gi;
179536
+ var DOTTED_RE = /[a-z0-9][a-z0-9_-]*(?:\.[a-z0-9_-]+)+/gi;
179537
+ var CAMEL_RE = /\b[a-zA-Z][a-z0-9]*(?:[A-Z][a-z0-9]*)+\b/g;
179538
+ var SHA_RE = /\b[0-9a-f]{7,40}\b/gi;
179539
+ var ERROR_CODE_RE = /\b(?:TS\d{4,}|ERR_[A-Z][A-Z0-9_]*)\b/g;
179540
+ var QUOTED_RE = /["`]([^"`]{3,80})["`]/g;
179541
+ function looksLikeSha(token) {
179542
+ return /[0-9]/.test(token) && /^[0-9a-f]{7,40}$/i.test(token);
179543
+ }
179544
+ function extractLiteralProbes(query) {
179545
+ const trimmed = query.trim();
179546
+ if (trimmed.length === 0)
179547
+ return [];
179548
+ const ordered = [];
179549
+ const seen = new Set;
179550
+ const add = (raw) => {
179551
+ if (!raw)
179552
+ return;
179553
+ const probe = raw.trim();
179554
+ if (probe.length < MIN_PROBE_LENGTH)
179555
+ return;
179556
+ const key = probe.toLowerCase();
179557
+ if (seen.has(key))
179558
+ return;
179559
+ seen.add(key);
179560
+ ordered.push(probe);
179561
+ };
179562
+ for (const m of trimmed.matchAll(QUOTED_RE))
179563
+ add(m[1]);
179564
+ for (const m of trimmed.matchAll(SLASH_COMMAND_RE))
179565
+ add(m[0]);
179566
+ for (const m of trimmed.matchAll(ERROR_CODE_RE))
179567
+ add(m[0]);
179568
+ for (const m of trimmed.matchAll(DOTTED_RE))
179569
+ add(m[0]);
179570
+ for (const m of trimmed.matchAll(KEBAB_SNAKE_RE))
179571
+ add(m[0]);
179572
+ for (const m of trimmed.matchAll(CAMEL_RE))
179573
+ add(m[0]);
179574
+ for (const m of trimmed.matchAll(SHA_RE)) {
179575
+ if (looksLikeSha(m[0]))
179576
+ add(m[0]);
179577
+ }
179578
+ return ordered.slice(0, MAX_PROBES);
179579
+ }
179580
+ function containsProbeVerbatim(text, probes) {
179581
+ if (probes.length === 0)
179582
+ return false;
179583
+ const haystack = text.toLowerCase();
179584
+ return probes.some((probe) => haystack.includes(probe.toLowerCase()));
179585
+ }
179586
+
179587
+ // src/features/magic-context/search.ts
179349
179588
  init_memory();
179350
179589
  init_embedding();
179351
179590
  init_storage_memory_fts();
@@ -179525,36 +179764,82 @@ function linearDecayScore(rank, total) {
179525
179764
  return 0;
179526
179765
  return Math.max(0, 1 - rank / total);
179527
179766
  }
179528
- function searchMessages(args) {
179529
- const sanitizedQuery = sanitizeFtsQuery(args.query.trim());
179530
- if (sanitizedQuery.length === 0) {
179767
+ function runMessageFtsQuery(db, sessionId, ftsQuery, fetchLimit, cutoff) {
179768
+ if (ftsQuery.length === 0)
179531
179769
  return [];
179532
- }
179533
- const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
179534
- const rows = getMessageSearchStatement(args.db).all(args.sessionId, sanitizedQuery, fetchLimit).map((row) => row);
179535
- const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
179536
- const filtered = rows.map((row) => {
179770
+ const rows = getMessageSearchStatement(db).all(sessionId, ftsQuery, fetchLimit).map((row) => row);
179771
+ const result = [];
179772
+ for (const row of rows) {
179537
179773
  const messageOrdinal = getMessageOrdinal(row.messageOrdinal);
179538
179774
  if (messageOrdinal === null || typeof row.messageId !== "string" || typeof row.role !== "string" || typeof row.content !== "string") {
179539
- return null;
179775
+ continue;
179540
179776
  }
179541
179777
  if (cutoff !== null && messageOrdinal > cutoff) {
179542
- return null;
179778
+ continue;
179543
179779
  }
179544
- return {
179780
+ result.push({
179545
179781
  messageOrdinal,
179546
179782
  messageId: row.messageId,
179547
179783
  role: row.role,
179548
179784
  content: row.content
179549
- };
179550
- }).filter((result) => result !== null).slice(0, args.limit);
179551
- return filtered.map((row, rank) => ({
179785
+ });
179786
+ }
179787
+ return result;
179788
+ }
179789
+ var RRF_K = 60;
179790
+ var VERBATIM_PROBE_BONUS = 0.5;
179791
+ function searchMessages(args) {
179792
+ const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
179793
+ const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
179794
+ const baseQuery = sanitizeFtsQuery(args.query.trim());
179795
+ const probes = args.probes ?? [];
179796
+ if (probes.length === 0) {
179797
+ const filtered = runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff).slice(0, args.limit);
179798
+ return filtered.map((row, rank) => ({
179799
+ source: "message",
179800
+ content: previewText(row.content),
179801
+ score: linearDecayScore(rank, filtered.length),
179802
+ messageOrdinal: row.messageOrdinal,
179803
+ messageId: row.messageId,
179804
+ role: row.role
179805
+ }));
179806
+ }
179807
+ const queryLists = [];
179808
+ if (baseQuery.length > 0) {
179809
+ queryLists.push(runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff));
179810
+ }
179811
+ for (const probe of probes) {
179812
+ const probeQuery = sanitizeFtsQuery(probe);
179813
+ if (probeQuery.length === 0)
179814
+ continue;
179815
+ queryLists.push(runMessageFtsQuery(args.db, args.sessionId, probeQuery, fetchLimit, cutoff));
179816
+ }
179817
+ const fused = new Map;
179818
+ for (const list of queryLists) {
179819
+ list.forEach((row, rank) => {
179820
+ const rrf = 1 / (RRF_K + rank);
179821
+ const existing = fused.get(row.messageId);
179822
+ if (existing) {
179823
+ existing.score += rrf;
179824
+ } else {
179825
+ fused.set(row.messageId, { row, score: rrf });
179826
+ }
179827
+ });
179828
+ }
179829
+ for (const entry of fused.values()) {
179830
+ if (containsProbeVerbatim(entry.row.content, probes)) {
179831
+ entry.score += VERBATIM_PROBE_BONUS;
179832
+ }
179833
+ }
179834
+ 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);
179835
+ const maxScore = ranked.length > 0 ? ranked[0].score : 1;
179836
+ return ranked.map((entry) => ({
179552
179837
  source: "message",
179553
- content: previewText(row.content),
179554
- score: linearDecayScore(rank, filtered.length),
179555
- messageOrdinal: row.messageOrdinal,
179556
- messageId: row.messageId,
179557
- role: row.role
179838
+ content: previewText(entry.row.content),
179839
+ score: maxScore > 0 ? entry.score / maxScore : 0,
179840
+ messageOrdinal: entry.row.messageOrdinal,
179841
+ messageId: entry.row.messageId,
179842
+ role: entry.row.role
179558
179843
  }));
179559
179844
  }
179560
179845
  function getSourceBoost(result) {
@@ -179638,12 +179923,14 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
179638
179923
  return null;
179639
179924
  }) : Promise.resolve(null);
179640
179925
  await Promise.resolve();
179926
+ const messageProbes = options.explicitSearch ? extractLiteralProbes(trimmedQuery) : [];
179641
179927
  const messageResults = runMessages ? searchMessages({
179642
179928
  db,
179643
179929
  sessionId,
179644
179930
  query: trimmedQuery,
179645
179931
  limit: tierLimit,
179646
- maxOrdinal: options.maxMessageOrdinal
179932
+ maxOrdinal: options.maxMessageOrdinal,
179933
+ probes: messageProbes
179647
179934
  }) : [];
179648
179935
  const queryEmbedding = await queryEmbeddingPromise;
179649
179936
  const [memoryResults, gitCommitResults] = await Promise.all([
@@ -180226,7 +180513,7 @@ function isVisibleNoteReadPart(part) {
180226
180513
  }
180227
180514
 
180228
180515
  // src/hooks/magic-context/todo-view.ts
180229
- import { createHash as createHash10 } from "node:crypto";
180516
+ import { createHash as createHash9 } from "node:crypto";
180230
180517
  var TERMINAL_STATUSES = new Set(["completed", "cancelled"]);
180231
180518
  var TITLE_DONE_STATUSES = new Set(["completed"]);
180232
180519
  var SYNTHETIC_CALL_ID_PREFIX = "mc_synthetic_todo_";
@@ -180271,7 +180558,7 @@ function buildSyntheticTodoPart(stateJson) {
180271
180558
  };
180272
180559
  }
180273
180560
  function computeSyntheticCallId(stateJson) {
180274
- const hash2 = createHash10("sha256").update(stateJson).digest("hex").slice(0, 16);
180561
+ const hash2 = createHash9("sha256").update(stateJson).digest("hex").slice(0, 16);
180275
180562
  return `${SYNTHETIC_CALL_ID_PREFIX}${hash2}`;
180276
180563
  }
180277
180564
  function parseTodoState(stateJson) {
@@ -182138,7 +182425,7 @@ function createToolExecuteAfterHook(args) {
182138
182425
  init_send_session_notification();
182139
182426
 
182140
182427
  // src/hooks/magic-context/system-prompt-hash.ts
182141
- import { createHash as createHash11 } from "node:crypto";
182428
+ import { createHash as createHash10 } from "node:crypto";
182142
182429
 
182143
182430
  // src/agents/magic-context-prompt.ts
182144
182431
  var LONG_TERM_PARTNER_FRAME = `### You are the user's long-term partner on this project — not a one-off hire
@@ -182322,7 +182609,7 @@ function createSystemPromptHashHandler(deps) {
182322
182609
  `);
182323
182610
  if (systemContent.length === 0)
182324
182611
  return;
182325
- const currentHash = createHash11("md5").update(systemContent).digest("hex");
182612
+ const currentHash = createHash10("md5").update(systemContent).digest("hex");
182326
182613
  if (!sessionMetaEarly) {
182327
182614
  return;
182328
182615
  }
@@ -183551,6 +183838,7 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
183551
183838
  if (!p)
183552
183839
  return null;
183553
183840
  return {
183841
+ kind: p.kind ?? "recomp",
183554
183842
  phase: p.phase,
183555
183843
  processedMessages: p.processedMessages,
183556
183844
  totalMessages: p.totalMessages,
@@ -183570,6 +183858,7 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
183570
183858
  return {
183571
183859
  ...empty,
183572
183860
  recompProgress: {
183861
+ kind: p.kind ?? "recomp",
183573
183862
  phase: p.phase,
183574
183863
  processedMessages: p.processedMessages,
183575
183864
  totalMessages: p.totalMessages,
@@ -184286,16 +184575,30 @@ Example: \`ctx_note(action="write", content="Implement X because Y", surface_con
184286
184575
 
184287
184576
  Historian reads these notes, deduplicates them, and rewrites the remaining useful notes over time.`;
184288
184577
  // src/tools/ctx-note/tools.ts
184289
- await init_storage();
184578
+ await __promiseAll([
184579
+ init_message_index(),
184580
+ init_storage()
184581
+ ]);
184290
184582
  import { tool as tool3 } from "@opencode-ai/plugin";
184583
+ function captureAnchorOrdinal(db, sessionId) {
184584
+ try {
184585
+ const ordinal = getLastIndexedOrdinal(db, sessionId);
184586
+ return ordinal > 0 ? ordinal : null;
184587
+ } catch {
184588
+ return null;
184589
+ }
184590
+ }
184591
+ function anchorSuffix(note) {
184592
+ return note.anchorOrdinal !== null ? ` ↳ @msg ${note.anchorOrdinal}` : "";
184593
+ }
184291
184594
  function formatNoteLine(note) {
184292
184595
  const statusSuffix = note.status === "active" ? "" : ` (${note.status})`;
184293
184596
  if (note.type === "session") {
184294
- return `- **#${note.id}**${statusSuffix}: ${note.content}`;
184597
+ return `- **#${note.id}**${statusSuffix}: ${note.content}${anchorSuffix(note)}`;
184295
184598
  }
184296
184599
  const conditionText = note.status === "ready" ? note.readyReason ?? note.surfaceCondition ?? "Condition satisfied" : note.surfaceCondition ?? "No condition recorded";
184297
184600
  const conditionLabel = note.status === "ready" ? "Condition met" : "Condition";
184298
- return `- **#${note.id}**${statusSuffix}: ${note.content}
184601
+ return `- **#${note.id}**${statusSuffix}: ${note.content}${anchorSuffix(note)}
184299
184602
  ${conditionLabel}: ${conditionText}`;
184300
184603
  }
184301
184604
  var DISMISS_FOOTER = `
@@ -184373,6 +184676,7 @@ function createCtxNoteTool(deps) {
184373
184676
  if (!content) {
184374
184677
  return "Error: 'content' is required when action is 'write'.";
184375
184678
  }
184679
+ const anchorOrdinal = captureAnchorOrdinal(deps.db, sessionId);
184376
184680
  if (args.surface_condition?.trim()) {
184377
184681
  if (!deps.dreamerEnabled) {
184378
184682
  return "Error: Smart notes require dreamer to be enabled. Enable dreamer in magic-context.jsonc to use surface_condition.";
@@ -184384,13 +184688,14 @@ function createCtxNoteTool(deps) {
184384
184688
  content,
184385
184689
  projectPath: projectIdentity,
184386
184690
  sessionId,
184387
- surfaceCondition: args.surface_condition.trim()
184691
+ surfaceCondition: args.surface_condition.trim(),
184692
+ anchorOrdinal
184388
184693
  });
184389
184694
  return `Created smart note #${note2.id}. Dreamer will evaluate the condition during nightly runs:
184390
184695
  - Content: ${content}
184391
184696
  - Condition: ${args.surface_condition.trim()}`;
184392
184697
  }
184393
- const note = addNote(deps.db, "session", { sessionId, content });
184698
+ const note = addNote(deps.db, "session", { sessionId, content, anchorOrdinal });
184394
184699
  return `Saved session note #${note.id}. Historian will rewrite or deduplicate notes as needed.`;
184395
184700
  }
184396
184701
  if (action === "dismiss") {
@@ -184453,9 +184758,13 @@ ${parts.join(`
184453
184758
 
184454
184759
  No session notes or smart notes.`;
184455
184760
  }
184456
- return sections.join(`
184761
+ const body = sections.join(`
184457
184762
 
184458
- `) + DISMISS_FOOTER;
184763
+ `);
184764
+ const anchorHint = body.includes("↳ @msg ") ? `
184765
+
184766
+ ↳ @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).` : "";
184767
+ return body + anchorHint + DISMISS_FOOTER;
184459
184768
  }
184460
184769
  });
184461
184770
  }
@@ -184746,7 +185055,8 @@ function createCtxSearchTool(deps) {
184746
185055
  maxMessageOrdinal: lastCompartmentEnd >= 0 ? lastCompartmentEnd : undefined,
184747
185056
  gitCommitsEnabled,
184748
185057
  sources: normalizeSources(args.sources),
184749
- visibleMemoryIds
185058
+ visibleMemoryIds,
185059
+ explicitSearch: true
184750
185060
  });
184751
185061
  return formatSearchResults(query, results);
184752
185062
  }
@@ -184847,22 +185157,22 @@ init_models_dev_cache();
184847
185157
  init_logger();
184848
185158
  import { randomBytes } from "node:crypto";
184849
185159
  import {
184850
- mkdirSync as mkdirSync8,
185160
+ mkdirSync as mkdirSync9,
184851
185161
  readdirSync,
184852
185162
  readFileSync as readFileSync15,
184853
- renameSync as renameSync3,
185163
+ renameSync as renameSync4,
184854
185164
  unlinkSync as unlinkSync3,
184855
- writeFileSync as writeFileSync7
185165
+ writeFileSync as writeFileSync8
184856
185166
  } from "node:fs";
184857
185167
  import { createServer } from "node:http";
184858
185168
  import { dirname as dirname8 } from "node:path";
184859
185169
 
184860
185170
  // src/shared/rpc-utils.ts
184861
- import { createHash as createHash12 } from "node:crypto";
185171
+ import { createHash as createHash11 } from "node:crypto";
184862
185172
  import { join as join24 } from "node:path";
184863
185173
  function projectHash(directory) {
184864
185174
  const normalized = directory.replace(/\/+$/, "");
184865
- return createHash12("sha256").update(normalized).digest("hex").slice(0, 16);
185175
+ return createHash11("sha256").update(normalized).digest("hex").slice(0, 16);
184866
185176
  }
184867
185177
  function rpcPortDir(storageDir, directory) {
184868
185178
  return join24(storageDir, "rpc", projectHash(directory));
@@ -184945,15 +185255,15 @@ class MagicContextRpcServer {
184945
185255
  try {
184946
185256
  this.warnIfOtherLiveInstance();
184947
185257
  const dir = dirname8(this.portFilePath);
184948
- mkdirSync8(dir, { recursive: true, mode: 448 });
185258
+ mkdirSync9(dir, { recursive: true, mode: 448 });
184949
185259
  const tmpPath = `${this.portFilePath}.tmp`;
184950
- writeFileSync7(tmpPath, JSON.stringify({
185260
+ writeFileSync8(tmpPath, JSON.stringify({
184951
185261
  port: this.port,
184952
185262
  pid: process.pid,
184953
185263
  started_at: this.startedAt,
184954
185264
  token: this.token
184955
185265
  }), { encoding: "utf-8", mode: 384 });
184956
- renameSync3(tmpPath, this.portFilePath);
185266
+ renameSync4(tmpPath, this.portFilePath);
184957
185267
  log(`[rpc] server listening on 127.0.0.1:${this.port}`);
184958
185268
  } catch (err) {
184959
185269
  log(`[rpc] failed to write port file: ${err}`);
@@ -185153,7 +185463,7 @@ var plugin = async (ctx) => {
185153
185463
  rpcServer.start().catch((err) => {
185154
185464
  log(`[magic-context] RPC server failed to start: ${err}`);
185155
185465
  });
185156
- refreshModelLimitsFromApi(ctx.client);
185466
+ refreshModelLimitsFromApi(ctx.client, { retries: 3, retryDelayMs: 1000 });
185157
185467
  }
185158
185468
  {
185159
185469
  const fence = getSchemaFenceRejection();