@cortexkit/opencode-magic-context 0.22.1 → 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.
Files changed (70) 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/runner.d.ts.map +1 -1
  8. package/dist/features/magic-context/dreamer/scheduler.d.ts +0 -4
  9. package/dist/features/magic-context/dreamer/scheduler.d.ts.map +1 -1
  10. package/dist/features/magic-context/git-commits/git-log-reader.d.ts +8 -0
  11. package/dist/features/magic-context/git-commits/git-log-reader.d.ts.map +1 -1
  12. package/dist/features/magic-context/git-commits/index.d.ts +1 -0
  13. package/dist/features/magic-context/git-commits/index.d.ts.map +1 -1
  14. package/dist/features/magic-context/git-commits/indexer.d.ts.map +1 -1
  15. package/dist/features/magic-context/git-commits/storage-git-commits.d.ts.map +1 -1
  16. package/dist/features/magic-context/git-commits/sweep-coordinator.d.ts +48 -0
  17. package/dist/features/magic-context/git-commits/sweep-coordinator.d.ts.map +1 -0
  18. package/dist/features/magic-context/key-files/storage-key-files.d.ts +0 -5
  19. package/dist/features/magic-context/key-files/storage-key-files.d.ts.map +1 -1
  20. package/dist/features/magic-context/literal-probes.d.ts +24 -0
  21. package/dist/features/magic-context/literal-probes.d.ts.map +1 -0
  22. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  23. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  24. package/dist/features/magic-context/search.d.ts +7 -0
  25. package/dist/features/magic-context/search.d.ts.map +1 -1
  26. package/dist/features/magic-context/storage-db.d.ts +1 -1
  27. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  28. package/dist/features/magic-context/storage-notes.d.ts +8 -0
  29. package/dist/features/magic-context/storage-notes.d.ts.map +1 -1
  30. package/dist/hooks/magic-context/compartment-runner-types.d.ts +14 -1
  31. package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
  32. package/dist/hooks/magic-context/derive-budgets.d.ts +3 -3
  33. package/dist/hooks/magic-context/event-handler.d.ts +7 -0
  34. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  35. package/dist/hooks/magic-context/event-payloads.d.ts +7 -0
  36. package/dist/hooks/magic-context/event-payloads.d.ts.map +1 -1
  37. package/dist/hooks/magic-context/event-resolvers.d.ts +1 -0
  38. package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
  39. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  40. package/dist/hooks/magic-context/live-session-state.d.ts +12 -0
  41. package/dist/hooks/magic-context/live-session-state.d.ts.map +1 -1
  42. package/dist/hooks/magic-context/recomp-orchestrator.d.ts +7 -2
  43. package/dist/hooks/magic-context/recomp-orchestrator.d.ts.map +1 -1
  44. package/dist/hooks/magic-context/system-prompt-hash.d.ts +9 -0
  45. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  46. package/dist/hooks/magic-context/tag-content-primitives.d.ts +23 -0
  47. package/dist/hooks/magic-context/tag-content-primitives.d.ts.map +1 -1
  48. package/dist/hooks/magic-context/temporal-awareness.d.ts.map +1 -1
  49. package/dist/hooks/magic-context/text-complete.d.ts +23 -0
  50. package/dist/hooks/magic-context/text-complete.d.ts.map +1 -1
  51. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  52. package/dist/hooks/magic-context/transform.d.ts +9 -0
  53. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +561 -190
  56. package/dist/plugin/dream-timer.d.ts.map +1 -1
  57. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  58. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  59. package/dist/shared/models-dev-cache.d.ts +54 -27
  60. package/dist/shared/models-dev-cache.d.ts.map +1 -1
  61. package/dist/shared/rpc-types.d.ts +3 -1
  62. package/dist/shared/rpc-types.d.ts.map +1 -1
  63. package/dist/tools/ctx-note/tools.d.ts.map +1 -1
  64. package/dist/tools/ctx-search/tools.d.ts.map +1 -1
  65. package/package.json +1 -1
  66. package/src/shared/models-dev-cache.test.ts +192 -360
  67. package/src/shared/models-dev-cache.ts +162 -193
  68. package/src/shared/rpc-types.ts +3 -1
  69. package/src/tui/index.tsx +17 -8
  70. 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
@@ -149161,14 +149158,45 @@ function readRawSessionMessageByIdFromDb(db, sessionId, messageId) {
149161
149158
  }
149162
149159
 
149163
149160
  // src/hooks/magic-context/tag-content-primitives.ts
149161
+ function stripWellFormedLeadingTagPrefix(value) {
149162
+ return value.replace(/^(\u00a7\d+\u00a7\s*)+/, "");
149163
+ }
149164
+ function stripCompleteTagPairsGlobally(value) {
149165
+ return value.replace(COMPLETE_TAG_PAIR_GLOBAL_REGEX, "");
149166
+ }
149167
+ function stripMalformedTagNotationGlobally(value) {
149168
+ return value.replace(MALFORMED_TAG_GLOBAL_REGEX, "");
149169
+ }
149170
+ function stripTagSectionCharacters(value) {
149171
+ return value.replace(STRAY_SECTION_CHAR_REGEX, "");
149172
+ }
149173
+ function stripPersistedAssistantText(value) {
149174
+ let text = stripWellFormedLeadingTagPrefix(value);
149175
+ text = stripCompleteTagPairsGlobally(text);
149176
+ text = stripMalformedTagNotationGlobally(text);
149177
+ text = stripTagSectionCharacters(text);
149178
+ return text.trim();
149179
+ }
149164
149180
  function byteSize(value) {
149165
149181
  return encoder.encode(value).length;
149166
149182
  }
149167
149183
  function stripTagPrefix(value) {
149168
- let stripped = value.replace(MALFORMED_TAG_PREFIX_REGEX, "");
149169
- stripped = stripped.replace(TAG_PREFIX_REGEX, "");
149184
+ let stripped = value;
149185
+ for (let pass = 0;pass < 8; pass++) {
149186
+ const prev = stripped;
149187
+ stripped = stripped.replace(MALFORMED_TAG_PREFIX_REGEX, "");
149188
+ stripped = stripped.replace(TAG_PREFIX_REGEX, "");
149189
+ if (stripped === prev)
149190
+ break;
149191
+ }
149170
149192
  return stripped;
149171
149193
  }
149194
+ function peelLeadingMcTagNotation(value) {
149195
+ const body = stripTagPrefix(value);
149196
+ if (body === value)
149197
+ return { tagPrefix: "", body };
149198
+ return { tagPrefix: value.slice(0, value.length - body.length), body };
149199
+ }
149172
149200
  function prependTag(tagId, value) {
149173
149201
  const stripped = stripTagPrefix(value);
149174
149202
  return `§${tagId}§ ${stripped}`;
@@ -149179,11 +149207,14 @@ function isThinkingPart(part) {
149179
149207
  const candidate = part;
149180
149208
  return candidate.type === "thinking" || candidate.type === "reasoning";
149181
149209
  }
149182
- var encoder, TAG_PREFIX_REGEX, MALFORMED_TAG_PREFIX_REGEX;
149210
+ var encoder, TAG_PREFIX_REGEX, MALFORMED_TAG_PREFIX_REGEX, COMPLETE_TAG_PAIR_GLOBAL_REGEX, MALFORMED_TAG_GLOBAL_REGEX, STRAY_SECTION_CHAR_REGEX;
149183
149211
  var init_tag_content_primitives = __esm(() => {
149184
149212
  encoder = new TextEncoder;
149185
149213
  TAG_PREFIX_REGEX = /^(?:§\d+§\s*)+/;
149186
149214
  MALFORMED_TAG_PREFIX_REGEX = /^(?:§\d+">§(?:\d+§)?\s*)+/;
149215
+ COMPLETE_TAG_PAIR_GLOBAL_REGEX = /\u00a7\d+\u00a7/g;
149216
+ MALFORMED_TAG_GLOBAL_REGEX = /\u00a7\d+">(?:\u00a7(?:\d+\u00a7)?)?/g;
149217
+ STRAY_SECTION_CHAR_REGEX = /\u00a7/g;
149187
149218
  });
149188
149219
 
149189
149220
  // src/hooks/magic-context/tag-part-guards.ts
@@ -149311,7 +149342,9 @@ function hasMeaningfulPart(part) {
149311
149342
  return false;
149312
149343
  const type = part.type;
149313
149344
  if (type === "text") {
149314
- return typeof part.text === "string" && part.text.trim().length > 0;
149345
+ if (typeof part.text !== "string")
149346
+ return false;
149347
+ return stripTagPrefix(part.text).trim().length > 0;
149315
149348
  }
149316
149349
  if (typeof type !== "string")
149317
149350
  return false;
@@ -149427,6 +149460,7 @@ function createToolDropTarget(compositeKey, thinkingParts, index, batch) {
149427
149460
  }
149428
149461
  var DROP_PREFIX = "[dropped", IGNORE_PART_TYPES, TRUNCATION_SENTINEL = "...[truncated]";
149429
149462
  var init_tool_drop_target = __esm(() => {
149463
+ init_tag_content_primitives();
149430
149464
  IGNORE_PART_TYPES = new Set([
149431
149465
  "thinking",
149432
149466
  "reasoning",
@@ -150727,6 +150761,17 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
150727
150761
  updated_at INTEGER NOT NULL DEFAULT 0
150728
150762
  );
150729
150763
 
150764
+ CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
150765
+ project_path TEXT PRIMARY KEY,
150766
+ lease_holder TEXT,
150767
+ lease_expires_at INTEGER,
150768
+ last_swept_at INTEGER
150769
+ );
150770
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
150771
+ ON git_sweep_coordinator(lease_expires_at);
150772
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
150773
+ ON git_sweep_coordinator(last_swept_at);
150774
+
150730
150775
  CREATE TABLE IF NOT EXISTS m0_mutation_log (
150731
150776
  id INTEGER PRIMARY KEY AUTOINCREMENT,
150732
150777
  session_id TEXT NOT NULL,
@@ -151256,7 +151301,7 @@ function getDatabasePersistenceError(db) {
151256
151301
  return null;
151257
151302
  return persistenceErrorByDatabase.get(db) ?? null;
151258
151303
  }
151259
- var databases, persistenceByDatabase, persistenceErrorByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 27, sqlitePragmaConfig;
151304
+ var databases, persistenceByDatabase, persistenceErrorByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 29, sqlitePragmaConfig;
151260
151305
  var init_storage_db = __esm(async () => {
151261
151306
  init_data_path();
151262
151307
  init_logger();
@@ -152047,6 +152092,38 @@ var init_migrations = __esm(async () => {
152047
152092
  ON tags(session_id, entry_fingerprint)
152048
152093
  WHERE type='message' AND entry_fingerprint IS NOT NULL`);
152049
152094
  }
152095
+ },
152096
+ {
152097
+ version: 28,
152098
+ description: "Add git commit sweep coordinator lease/cooldown table",
152099
+ up: (db) => {
152100
+ db.exec(`
152101
+ CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
152102
+ project_path TEXT PRIMARY KEY,
152103
+ lease_holder TEXT,
152104
+ lease_expires_at INTEGER,
152105
+ last_swept_at INTEGER
152106
+ );
152107
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
152108
+ ON git_sweep_coordinator(lease_expires_at);
152109
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
152110
+ ON git_sweep_coordinator(last_swept_at);
152111
+ `);
152112
+ }
152113
+ },
152114
+ {
152115
+ version: 29,
152116
+ description: "Add anchor_ordinal to notes (traceback to the conversation tail)",
152117
+ up: (db) => {
152118
+ const notesExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='notes'").get();
152119
+ if (!notesExists) {
152120
+ return;
152121
+ }
152122
+ const columns = db.prepare("PRAGMA table_info(notes)").all();
152123
+ if (!columns.some((column) => column.name === "anchor_ordinal")) {
152124
+ db.exec("ALTER TABLE notes ADD COLUMN anchor_ordinal INTEGER");
152125
+ }
152126
+ }
152050
152127
  }
152051
152128
  ];
152052
152129
  LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -153004,7 +153081,8 @@ function toNote(row) {
153004
153081
  updatedAt: row.updated_at,
153005
153082
  lastCheckedAt: toNullableNumber(row.last_checked_at),
153006
153083
  readyAt: toNullableNumber(row.ready_at),
153007
- readyReason: toNullableString(row.ready_reason)
153084
+ readyReason: toNullableString(row.ready_reason),
153085
+ anchorOrdinal: toNullableNumber(row.anchor_ordinal)
153008
153086
  };
153009
153087
  }
153010
153088
  function getNoteById(db, noteId) {
@@ -153056,7 +153134,7 @@ function getNotes(db, options = {}) {
153056
153134
  }
153057
153135
  function addNote(db, type, options) {
153058
153136
  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());
153137
+ 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
153138
  if (!isNoteRow(result)) {
153061
153139
  throw new Error("[notes] failed to insert note");
153062
153140
  }
@@ -164319,8 +164397,130 @@ var init_storage_git_commit_embeddings = __esm(() => {
164319
164397
  distinctModelIdStatements = new WeakMap;
164320
164398
  });
164321
164399
 
164400
+ // src/features/magic-context/git-commits/sweep-coordinator.ts
164401
+ function runImmediate2(db, body) {
164402
+ db.exec("BEGIN IMMEDIATE");
164403
+ let committed = false;
164404
+ try {
164405
+ const result = body();
164406
+ db.exec("COMMIT");
164407
+ committed = true;
164408
+ return result;
164409
+ } finally {
164410
+ if (!committed) {
164411
+ try {
164412
+ db.exec("ROLLBACK");
164413
+ } catch {}
164414
+ }
164415
+ }
164416
+ }
164417
+ function rowToState(row) {
164418
+ return {
164419
+ projectPath: row.project_path,
164420
+ leaseHolder: row.lease_holder,
164421
+ leaseExpiresAt: row.lease_expires_at,
164422
+ lastSweptAt: row.last_swept_at
164423
+ };
164424
+ }
164425
+ function getGitSweepCoordinatorState(db, projectPath) {
164426
+ const row = db.prepare(`SELECT project_path, lease_holder, lease_expires_at, last_swept_at
164427
+ FROM git_sweep_coordinator
164428
+ WHERE project_path = ?`).get(projectPath);
164429
+ return row ? rowToState(row) : null;
164430
+ }
164431
+ function acquireGitSweepLease(db, projectPath, holderId, options = {}) {
164432
+ const cooldownMs = options.cooldownMs ?? GIT_SWEEP_COOLDOWN_MS;
164433
+ const leaseTtlMs = options.leaseTtlMs ?? GIT_SWEEP_LEASE_TTL_MS;
164434
+ return runImmediate2(db, () => {
164435
+ const now = Date.now();
164436
+ const row = getGitSweepCoordinatorState(db, projectPath);
164437
+ if (row?.leaseHolder && row.leaseExpiresAt !== null && row.leaseExpiresAt > now) {
164438
+ return {
164439
+ acquired: false,
164440
+ projectPath,
164441
+ reason: "lease_active",
164442
+ leaseHolder: row.leaseHolder,
164443
+ leaseExpiresAt: row.leaseExpiresAt,
164444
+ lastSweptAt: row.lastSweptAt,
164445
+ nextAllowedAt: null
164446
+ };
164447
+ }
164448
+ if (!options.ignoreCooldown && row?.lastSweptAt !== null && row?.lastSweptAt !== undefined) {
164449
+ const nextAllowedAt = row.lastSweptAt + cooldownMs;
164450
+ if (nextAllowedAt > now) {
164451
+ return {
164452
+ acquired: false,
164453
+ projectPath,
164454
+ reason: "cooldown_active",
164455
+ leaseHolder: row.leaseHolder,
164456
+ leaseExpiresAt: row.leaseExpiresAt,
164457
+ lastSweptAt: row.lastSweptAt,
164458
+ nextAllowedAt
164459
+ };
164460
+ }
164461
+ }
164462
+ const leaseExpiresAt = now + leaseTtlMs;
164463
+ db.prepare(`INSERT INTO git_sweep_coordinator (
164464
+ project_path,
164465
+ lease_holder,
164466
+ lease_expires_at,
164467
+ last_swept_at
164468
+ ) VALUES (?, ?, ?, NULL)
164469
+ ON CONFLICT(project_path) DO UPDATE SET
164470
+ lease_holder = excluded.lease_holder,
164471
+ lease_expires_at = excluded.lease_expires_at`).run(projectPath, holderId, leaseExpiresAt);
164472
+ return {
164473
+ acquired: true,
164474
+ projectPath,
164475
+ holderId,
164476
+ acquiredAt: now,
164477
+ leaseExpiresAt
164478
+ };
164479
+ });
164480
+ }
164481
+ function renewGitSweepLease(db, projectPath, holderId, leaseTtlMs = GIT_SWEEP_LEASE_TTL_MS) {
164482
+ return runImmediate2(db, () => {
164483
+ const now = Date.now();
164484
+ const leaseExpiresAt = now + leaseTtlMs;
164485
+ const result = db.prepare(`UPDATE git_sweep_coordinator
164486
+ SET lease_expires_at = ?
164487
+ WHERE project_path = ?
164488
+ AND lease_holder = ?
164489
+ AND lease_expires_at > ?`).run(leaseExpiresAt, projectPath, holderId, now);
164490
+ return result.changes === 1;
164491
+ });
164492
+ }
164493
+ function markGitSweepSuccessAndRelease(db, projectPath, holderId) {
164494
+ return runImmediate2(db, () => {
164495
+ const now = Date.now();
164496
+ const result = db.prepare(`UPDATE git_sweep_coordinator
164497
+ SET lease_holder = NULL,
164498
+ lease_expires_at = NULL,
164499
+ last_swept_at = ?
164500
+ WHERE project_path = ?
164501
+ AND lease_holder = ?
164502
+ AND lease_expires_at > ?`).run(now, projectPath, holderId, now);
164503
+ return result.changes === 1;
164504
+ });
164505
+ }
164506
+ function releaseGitSweepLease(db, projectPath, holderId) {
164507
+ runImmediate2(db, () => {
164508
+ db.prepare(`UPDATE git_sweep_coordinator
164509
+ SET lease_holder = NULL,
164510
+ lease_expires_at = NULL
164511
+ WHERE project_path = ?
164512
+ AND lease_holder = ?`).run(projectPath, holderId);
164513
+ });
164514
+ }
164515
+ var GIT_SWEEP_COOLDOWN_MS, GIT_SWEEP_LEASE_TTL_MS, GIT_SWEEP_LEASE_RENEWAL_MS;
164516
+ var init_sweep_coordinator = __esm(() => {
164517
+ GIT_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
164518
+ GIT_SWEEP_LEASE_TTL_MS = 5 * 60 * 1000;
164519
+ GIT_SWEEP_LEASE_RENEWAL_MS = 60 * 1000;
164520
+ });
164521
+
164322
164522
  // src/features/magic-context/project-embedding-registry.ts
164323
- import { createHash as createHash7 } from "node:crypto";
164523
+ import { createHash as createHash7, randomUUID } from "node:crypto";
164324
164524
  function resolveEmbeddingConfig(config2) {
164325
164525
  if (!config2 || config2.provider === "local") {
164326
164526
  return {
@@ -164584,6 +164784,7 @@ var init_project_embedding_registry = __esm(() => {
164584
164784
  init_magic_context();
164585
164785
  init_logger();
164586
164786
  init_storage_git_commit_embeddings();
164787
+ init_sweep_coordinator();
164587
164788
  init_embedding_cache();
164588
164789
  init_embedding_identity();
164589
164790
  init_embedding_local();
@@ -164682,32 +164883,48 @@ var init_storage_memory_fts = __esm(() => {
164682
164883
  });
164683
164884
 
164684
164885
  // 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";
164886
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync12, renameSync as renameSync3, writeFileSync as writeFileSync4 } from "node:fs";
164688
164887
  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;
164888
+ function isSaneLimit(limit) {
164889
+ return typeof limit === "number" && limit >= MIN_SANE_LIMIT && limit <= MAX_SANE_LIMIT;
164890
+ }
164891
+ function persistFilePath() {
164892
+ return join18(getMagicContextStorageDir(), `model-context-limits-${getHarness()}.json`);
164893
+ }
164894
+ function loadPersistedApiCacheOnce() {
164895
+ if (persistSeedLoaded || apiCache !== null)
164896
+ return;
164897
+ persistSeedLoaded = true;
164898
+ try {
164899
+ const raw = readFileSync12(persistFilePath(), "utf-8");
164900
+ const obj = JSON.parse(raw);
164901
+ const map2 = new Map;
164902
+ for (const [key, limit] of Object.entries(obj)) {
164903
+ if (isSaneLimit(limit))
164904
+ map2.set(key, { limit });
164905
+ }
164906
+ if (map2.size > 0) {
164907
+ apiCache = map2;
164908
+ sessionLog("global", `models-dev-cache: seeded ${map2.size} entries from persisted cache (cold start)`);
164909
+ }
164910
+ } catch {}
164911
+ }
164912
+ function persistApiCache() {
164913
+ if (!apiCache)
164914
+ return;
164915
+ const obj = {};
164916
+ for (const [key, value] of apiCache) {
164917
+ if (isSaneLimit(value.limit))
164918
+ obj[key] = value.limit;
164919
+ }
164920
+ try {
164921
+ const dir = getMagicContextStorageDir();
164922
+ mkdirSync5(dir, { recursive: true });
164923
+ const target = persistFilePath();
164924
+ const tmp = `${target}.${process.pid}.tmp`;
164925
+ writeFileSync4(tmp, JSON.stringify(obj), { encoding: "utf-8", mode: 384 });
164926
+ renameSync3(tmp, target);
164927
+ } catch {}
164711
164928
  }
164712
164929
  function resolveLimit(limit) {
164713
164930
  if (!limit)
@@ -164720,7 +164937,7 @@ function resolveLimit(limit) {
164720
164937
  }
164721
164938
  function setCachedModelMetadata(cache, key, model) {
164722
164939
  const limit = resolveLimit(model?.limit);
164723
- if (limit === undefined) {
164940
+ if (!isSaneLimit(limit)) {
164724
164941
  return;
164725
164942
  }
164726
164943
  const value = { limit };
@@ -164732,54 +164949,26 @@ function setCachedModelMetadata(cache, key, model) {
164732
164949
  }
164733
164950
  }
164734
164951
  }
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
- }
164952
+ async function refreshModelLimitsFromApi(client, options) {
164953
+ const attempts = Math.max(1, (options?.retries ?? 0) + 1);
164954
+ const delayMs = options?.retryDelayMs ?? 1000;
164955
+ for (let attempt = 1;attempt <= attempts; attempt++) {
164956
+ const ok = await refreshModelLimitsOnce(client);
164957
+ if (ok)
164958
+ return;
164959
+ if (attempt < attempts) {
164960
+ await new Promise((resolve6) => setTimeout(resolve6, delayMs));
164768
164961
  }
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
164962
  }
164772
- sessionLog("global", `models-dev-cache: file-layer loaded ${metadata.size} model metadata entries (modelsJsonPath=${modelsJsonPath}, found=${fileFound})`);
164773
- return metadata;
164774
164963
  }
164775
- async function refreshModelLimitsFromApi(client) {
164964
+ async function refreshModelLimitsOnce(client) {
164776
164965
  try {
164777
164966
  const result = await client.config.providers();
164778
164967
  const data = result.data;
164779
164968
  const providers = data?.providers;
164780
- if (!Array.isArray(providers)) {
164781
- sessionLog("global", "models-dev-cache: API refresh returned no providers payload");
164782
- return;
164969
+ if (!Array.isArray(providers) || providers.length === 0) {
164970
+ sessionLog("global", "models-dev-cache: API refresh returned no providers payload (will retry if attempts remain)");
164971
+ return false;
164783
164972
  }
164784
164973
  const map2 = new Map;
164785
164974
  for (const entry of providers) {
@@ -164793,27 +164982,22 @@ async function refreshModelLimitsFromApi(client) {
164793
164982
  const previousSize = apiCache?.size ?? null;
164794
164983
  apiCache = map2;
164795
164984
  apiLoadedAt = Date.now();
164985
+ persistApiCache();
164796
164986
  if (previousSize === null) {
164797
164987
  sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model metadata entries`);
164798
164988
  } else if (previousSize !== map2.size) {
164799
164989
  sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model metadata entries (was ${previousSize})`);
164800
164990
  }
164991
+ return true;
164801
164992
  } catch (error51) {
164802
164993
  sessionLog("global", "models-dev-cache: API refresh failed:", error51 instanceof Error ? error51.message : String(error51));
164994
+ return false;
164803
164995
  }
164804
164996
  }
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
- }
164997
+ function getSdkContextLimit(providerID, modelID) {
164998
+ loadPersistedApiCacheOnce();
164811
164999
  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;
165000
+ return isSaneLimit(fromApi) ? fromApi : undefined;
164817
165001
  }
164818
165002
  function lookupLimitWithTagFallback(cache, providerID, modelID) {
164819
165003
  if (!cache)
@@ -164830,12 +165014,10 @@ function lookupLimitWithTagFallback(cache, providerID, modelID) {
164830
165014
  }
164831
165015
  return;
164832
165016
  }
164833
- var RELOAD_INTERVAL_MS, apiCache = null, apiLoadedAt = 0, fileCache = null, fileLastAttempt = 0;
165017
+ var MIN_SANE_LIMIT = 20000, MAX_SANE_LIMIT = 3000000, apiCache = null, apiLoadedAt = 0, persistSeedLoaded = false;
164834
165018
  var init_models_dev_cache = __esm(() => {
164835
165019
  init_data_path();
164836
- init_jsonc_parser();
164837
165020
  init_logger();
164838
- RELOAD_INTERVAL_MS = 5 * 60 * 1000;
164839
165021
  });
164840
165022
 
164841
165023
  // src/shared/rpc-notifications.ts
@@ -165561,7 +165743,7 @@ var init_compartment_runner_validation = __esm(async () => {
165561
165743
  });
165562
165744
 
165563
165745
  // src/hooks/magic-context/compartment-runner-historian.ts
165564
- import { mkdirSync as mkdirSync5, unlinkSync, writeFileSync as writeFileSync4 } from "node:fs";
165746
+ import { mkdirSync as mkdirSync6, unlinkSync, writeFileSync as writeFileSync5 } from "node:fs";
165565
165747
  import { join as join21 } from "node:path";
165566
165748
  function historianResponseDumpDir(directory) {
165567
165749
  return getProjectMagicContextHistorianDir(directory);
@@ -165864,11 +166046,11 @@ function cleanupHistorianDump(sessionId, dumpPath) {
165864
166046
  function dumpHistorianResponse(sessionId, directory, label, text) {
165865
166047
  try {
165866
166048
  const dumpDir = historianResponseDumpDir(directory);
165867
- mkdirSync5(dumpDir, { recursive: true });
166049
+ mkdirSync6(dumpDir, { recursive: true });
165868
166050
  const safeSessionId = sanitizeDumpName(sessionId);
165869
166051
  const safeLabel = sanitizeDumpName(label);
165870
166052
  const dumpPath = join21(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
165871
- writeFileSync4(dumpPath, text, "utf8");
166053
+ writeFileSync5(dumpPath, text, "utf8");
165872
166054
  sessionLog(sessionId, "compartment agent: historian response dumped", {
165873
166055
  label,
165874
166056
  dumpPath
@@ -165990,7 +166172,7 @@ function insertCompartmentEvents(db, sessionId, events, compartmentIds) {
165990
166172
  var init_compartment_events = () => {};
165991
166173
 
165992
166174
  // src/hooks/magic-context/historian-state-file.ts
165993
- import { mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "node:fs";
166175
+ import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "node:fs";
165994
166176
  function cleanupHistorianStateFile(path6) {
165995
166177
  if (!path6)
165996
166178
  return;
@@ -166545,9 +166727,7 @@ function injectTemporalMarkers(messages) {
166545
166727
  if (prefix && Array.isArray(msg.parts)) {
166546
166728
  const target = findFirstVisibleTextPart(msg.parts);
166547
166729
  if (target && typeof target.text === "string") {
166548
- const tagMatch = target.text.match(/^(?:§\d+§\s*)+/);
166549
- const tagPrefix = tagMatch ? tagMatch[0] : "";
166550
- const body = target.text.slice(tagPrefix.length);
166730
+ const { tagPrefix, body } = peelLeadingMcTagNotation(target.text);
166551
166731
  if (!TEMPORAL_MARKER_PATTERN.test(body)) {
166552
166732
  target.text = tagPrefix + prefix + body;
166553
166733
  injected++;
@@ -166562,6 +166742,7 @@ function injectTemporalMarkers(messages) {
166562
166742
  }
166563
166743
  var TEMPORAL_AWARENESS_THRESHOLD_SECONDS = 300, SECONDS_PER_MINUTE = 60, SECONDS_PER_HOUR, SECONDS_PER_DAY, SECONDS_PER_WEEK, TEMPORAL_MARKER_PATTERN;
166564
166744
  var init_temporal_awareness = __esm(() => {
166745
+ init_tag_content_primitives();
166565
166746
  SECONDS_PER_HOUR = 60 * 60;
166566
166747
  SECONDS_PER_DAY = 24 * 60 * 60;
166567
166748
  SECONDS_PER_WEEK = 7 * 24 * 60 * 60;
@@ -170826,7 +171007,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
170826
171007
  const [providerID, ...rest] = historianModelOverride.split("/");
170827
171008
  const modelID = rest.join("/");
170828
171009
  if (providerID && modelID) {
170829
- const limit = getModelsDevContextLimit(providerID, modelID);
171010
+ const limit = getSdkContextLimit(providerID, modelID);
170830
171011
  if (typeof limit === "number" && limit > 0)
170831
171012
  return limit;
170832
171013
  }
@@ -170845,7 +171026,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
170845
171026
  const modelID = rest.join("/");
170846
171027
  if (!providerID || !modelID)
170847
171028
  continue;
170848
- const limit = getModelsDevContextLimit(providerID, modelID);
171029
+ const limit = getSdkContextLimit(providerID, modelID);
170849
171030
  if (typeof limit !== "number" || limit <= 0)
170850
171031
  continue;
170851
171032
  if (minLimit === undefined || limit < minLimit)
@@ -171212,6 +171393,7 @@ __export(exports_recomp_orchestrator, {
171212
171393
  setRecompNote: () => setRecompNote,
171213
171394
  runManagedUpgrade: () => runManagedUpgrade,
171214
171395
  runManagedRecomp: () => runManagedRecomp,
171396
+ isRecompSkip: () => isRecompSkip,
171215
171397
  isRecompFailure: () => isRecompFailure,
171216
171398
  isRecompComplete: () => isRecompComplete,
171217
171399
  extractRecompReason: () => extractRecompReason,
@@ -171224,6 +171406,9 @@ function resolveLiveModelKey(liveSessionState, sessionId) {
171224
171406
  function isRecompFailure(message) {
171225
171407
  return /—\s*(Failed|Skipped)/.test(message);
171226
171408
  }
171409
+ function isRecompSkip(message) {
171410
+ return /—\s*Skipped|already mutating compartment state|already running/i.test(message);
171411
+ }
171227
171412
  function isRecompComplete(message) {
171228
171413
  return /—\s*Complete/.test(message);
171229
171414
  }
@@ -171239,9 +171424,10 @@ function contextualizeUpgradeReason(reason) {
171239
171424
  }
171240
171425
  return rewritten;
171241
171426
  }
171242
- function setRecompStarting(liveSessionState, sessionId, note) {
171427
+ function setRecompStarting(liveSessionState, sessionId, note, kind = "recomp") {
171243
171428
  liveSessionState.recompProgressBySession.set(sessionId, {
171244
171429
  sessionId,
171430
+ kind,
171245
171431
  phase: "recomp",
171246
171432
  processedMessages: 0,
171247
171433
  totalMessages: 0,
@@ -171266,6 +171452,7 @@ function setRecompTerminal(liveSessionState, sessionId, phase, message) {
171266
171452
  const existing = liveSessionState.recompProgressBySession.get(sessionId);
171267
171453
  liveSessionState.recompProgressBySession.set(sessionId, {
171268
171454
  sessionId,
171455
+ kind: existing?.kind ?? "recomp",
171269
171456
  phase,
171270
171457
  processedMessages: existing?.processedMessages ?? 0,
171271
171458
  totalMessages: existing?.totalMessages ?? 0,
@@ -171275,10 +171462,10 @@ function setRecompTerminal(liveSessionState, sessionId, phase, message) {
171275
171462
  updatedAt: Date.now(),
171276
171463
  message
171277
171464
  });
171278
- if (phase === "done") {
171465
+ if (phase === "done" || phase === "skipped") {
171279
171466
  const t = setTimeout(() => {
171280
171467
  const cur = liveSessionState.recompProgressBySession.get(sessionId);
171281
- if (cur?.phase === "done")
171468
+ if (cur?.phase === phase)
171282
171469
  liveSessionState.recompProgressBySession.delete(sessionId);
171283
171470
  }, RECOMP_DONE_GRACE_MS);
171284
171471
  t.unref?.();
@@ -171307,7 +171494,11 @@ function buildRecompDeps(ctx, sessionId) {
171307
171494
  ctx.liveSessionState.deferredHistoryRefreshSessions.add(sid);
171308
171495
  },
171309
171496
  onRecompProgress: (p) => {
171310
- ctx.liveSessionState.recompProgressBySession.set(sessionId, p);
171497
+ const prevKind = ctx.liveSessionState.recompProgressBySession.get(sessionId)?.kind ?? "recomp";
171498
+ ctx.liveSessionState.recompProgressBySession.set(sessionId, {
171499
+ ...p,
171500
+ kind: p.kind ?? prevKind
171501
+ });
171311
171502
  }
171312
171503
  };
171313
171504
  }
@@ -171326,10 +171517,11 @@ async function resolveSessionDirectory(ctx, sessionId) {
171326
171517
  return ctx.directory;
171327
171518
  }
171328
171519
  async function runManagedRecomp(ctx, sessionId, options) {
171329
- setRecompStarting(ctx.liveSessionState, sessionId, "Starting recomp…");
171520
+ setRecompStarting(ctx.liveSessionState, sessionId, "Starting recomp…", "recomp");
171330
171521
  try {
171331
171522
  const message = await executeContextRecomp(buildRecompDeps(ctx, sessionId), options);
171332
- setRecompTerminal(ctx.liveSessionState, sessionId, isRecompFailure(message) ? "failed" : "done", extractRecompReason(message));
171523
+ const terminalPhase = isRecompSkip(message) ? "skipped" : isRecompFailure(message) ? "failed" : "done";
171524
+ setRecompTerminal(ctx.liveSessionState, sessionId, terminalPhase, extractRecompReason(message));
171333
171525
  return message;
171334
171526
  } catch (error51) {
171335
171527
  setRecompTerminal(ctx.liveSessionState, sessionId, "failed", `Recomp crashed: ${String(error51)}`);
@@ -171339,7 +171531,7 @@ Recomp crashed: ${String(error51)}`;
171339
171531
  }
171340
171532
  }
171341
171533
  async function runManagedUpgrade(ctx, sessionId) {
171342
- setRecompStarting(ctx.liveSessionState, sessionId, "Starting upgrade…");
171534
+ setRecompStarting(ctx.liveSessionState, sessionId, "Starting upgrade…", "upgrade");
171343
171535
  try {
171344
171536
  const compartments = getCompartments(ctx.db, sessionId);
171345
171537
  const legacyCount = compartments.filter((c) => c.legacy === 1 || !c.p1 || c.p1.trim() === "").length;
@@ -171397,6 +171589,7 @@ async function runUpgradeMemoryMigration(ctx, sessionId, migrationDirectory) {
171397
171589
  const prev = ctx.liveSessionState.recompProgressBySession.get(sessionId);
171398
171590
  ctx.liveSessionState.recompProgressBySession.set(sessionId, {
171399
171591
  sessionId,
171592
+ kind: prev?.kind ?? "upgrade",
171400
171593
  phase: "migration",
171401
171594
  processedMessages: prev?.processedMessages ?? 0,
171402
171595
  totalMessages: prev?.totalMessages ?? 0,
@@ -171493,7 +171686,7 @@ var exports_tui_config = {};
171493
171686
  __export(exports_tui_config, {
171494
171687
  ensureTuiPluginEntry: () => ensureTuiPluginEntry
171495
171688
  });
171496
- import { existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync16, writeFileSync as writeFileSync8 } from "node:fs";
171689
+ import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "node:fs";
171497
171690
  import { dirname as dirname9, join as join25 } from "node:path";
171498
171691
  function isMagicContextEntry(entry) {
171499
171692
  if (!entry)
@@ -171510,9 +171703,9 @@ function resolveTuiConfigPath() {
171510
171703
  const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
171511
171704
  const jsoncPath = join25(configDir, "tui.jsonc");
171512
171705
  const jsonPath = join25(configDir, "tui.json");
171513
- if (existsSync16(jsoncPath))
171706
+ if (existsSync15(jsoncPath))
171514
171707
  return jsoncPath;
171515
- if (existsSync16(jsonPath))
171708
+ if (existsSync15(jsonPath))
171516
171709
  return jsonPath;
171517
171710
  return jsonPath;
171518
171711
  }
@@ -171520,7 +171713,7 @@ function ensureTuiPluginEntry() {
171520
171713
  try {
171521
171714
  const configPath = resolveTuiConfigPath();
171522
171715
  let config2 = {};
171523
- if (existsSync16(configPath)) {
171716
+ if (existsSync15(configPath)) {
171524
171717
  const raw = readFileSync16(configPath, "utf-8");
171525
171718
  config2 = import_comment_json4.parse(raw) ?? {};
171526
171719
  }
@@ -171540,8 +171733,8 @@ function ensureTuiPluginEntry() {
171540
171733
  plugins.push(PLUGIN_ENTRY);
171541
171734
  }
171542
171735
  config2.plugin = plugins;
171543
- mkdirSync9(dirname9(configPath), { recursive: true });
171544
- writeFileSync8(configPath, `${import_comment_json4.stringify(config2, null, 2)}
171736
+ mkdirSync10(dirname9(configPath), { recursive: true });
171737
+ writeFileSync9(configPath, `${import_comment_json4.stringify(config2, null, 2)}
171545
171738
  `);
171546
171739
  log(`[magic-context] updated TUI plugin entry in ${configPath}`);
171547
171740
  return true;
@@ -173466,7 +173659,8 @@ function createLiveSessionState() {
173466
173659
  pendingMaterializationSessions: new Set,
173467
173660
  deferredMaterializationSessions: new Set,
173468
173661
  sessionDirectoryBySession: new Map,
173469
- recompProgressBySession: new Map
173662
+ recompProgressBySession: new Map,
173663
+ internalChildSessions: new Set
173470
173664
  };
173471
173665
  }
173472
173666
 
@@ -175083,7 +175277,7 @@ async function processDreamQueue(args) {
175083
175277
  return null;
175084
175278
  }
175085
175279
  const projectDirectory = args.sessionDirectoryOverride ?? resolveDreamSessionDirectory(entry.projectIdentity);
175086
- log(`[dreamer] dequeued project ${entry.projectIdentity} (dir=${projectDirectory}), starting dream run`);
175280
+ log(`[dreamer] dequeued project ${entry.projectIdentity}, starting dream run`);
175087
175281
  let result;
175088
175282
  try {
175089
175283
  result = await runDream({
@@ -175207,6 +175401,7 @@ async function readGitCommits(directory, options = {}) {
175207
175401
  if (revision.startsWith("-")) {
175208
175402
  throw new Error(`readGitCommits: refusing revision that looks like an option: "${revision}"`);
175209
175403
  }
175404
+ const projectLabel = options.projectIdentity ?? "<project>";
175210
175405
  const args = [
175211
175406
  "log",
175212
175407
  revision,
@@ -175229,11 +175424,11 @@ async function readGitCommits(directory, options = {}) {
175229
175424
  stdout = result.stdout;
175230
175425
  } catch (error51) {
175231
175426
  const message = error51 instanceof Error ? error51.message : String(error51);
175232
- log(`[git-commits] readGitCommits failed at cwd=${directory}: ${message.slice(0, 500)}`);
175427
+ log(`[git-commits] readGitCommits failed for ${projectLabel}: ${message.slice(0, 500)}`);
175233
175428
  return [];
175234
175429
  }
175235
175430
  if (stdout.trim().length === 0) {
175236
- log(`[git-commits] readGitCommits returned empty stdout at cwd=${directory} (sinceMs=${options.sinceMs ?? "none"} args=${args.slice(0, 4).join(" ")})`);
175431
+ log(`[git-commits] readGitCommits returned empty stdout for ${projectLabel} (sinceMs=${options.sinceMs ?? "none"} args=${args.slice(0, 4).join(" ")})`);
175237
175432
  }
175238
175433
  return parseGitLogOutput(stdout);
175239
175434
  }
@@ -175288,6 +175483,7 @@ var insertStatements = new WeakMap;
175288
175483
  var existingShasStatements = new WeakMap;
175289
175484
  var projectCountStatements = new WeakMap;
175290
175485
  var evictStatements = new WeakMap;
175486
+ var evictOverflowStatements = new WeakMap;
175291
175487
  var latestCommitTimeStatements = new WeakMap;
175292
175488
  function getInsertStatement(db) {
175293
175489
  let stmt = insertStatements.get(db);
@@ -175330,17 +175526,17 @@ function getLatestCommitTimeStatement(db) {
175330
175526
  }
175331
175527
  return stmt;
175332
175528
  }
175333
- function getEvictStatement(db) {
175334
- let stmt = evictStatements.get(db);
175529
+ function getEvictOverflowStatement(db) {
175530
+ let stmt = evictOverflowStatements.get(db);
175335
175531
  if (!stmt) {
175336
175532
  stmt = db.prepare(`DELETE FROM git_commits
175337
- WHERE sha IN (
175338
- SELECT sha FROM git_commits
175533
+ WHERE rowid IN (
175534
+ SELECT rowid FROM git_commits
175339
175535
  WHERE project_path = ?
175340
- ORDER BY committed_at ASC
175341
- LIMIT ?
175536
+ ORDER BY committed_at DESC, sha DESC
175537
+ LIMIT -1 OFFSET ?
175342
175538
  )`);
175343
- evictStatements.set(db, stmt);
175539
+ evictOverflowStatements.set(db, stmt);
175344
175540
  }
175345
175541
  return stmt;
175346
175542
  }
@@ -175378,22 +175574,15 @@ function getLatestIndexedCommitTimeMs(db, projectPath) {
175378
175574
  const row = getLatestCommitTimeStatement(db).get(projectPath);
175379
175575
  return row?.latest ?? null;
175380
175576
  }
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
175577
  function enforceProjectCap(db, projectPath, maxCommits) {
175390
175578
  if (maxCommits <= 0)
175391
175579
  return 0;
175392
175580
  const count = getCommitCount(db, projectPath);
175393
175581
  if (count <= maxCommits)
175394
175582
  return 0;
175395
- const excess = count - maxCommits;
175396
- const evicted = evictOldestCommits(db, projectPath, excess);
175583
+ getEvictOverflowStatement(db).run(projectPath, maxCommits);
175584
+ const after = getCommitCount(db, projectPath);
175585
+ const evicted = Math.max(0, count - after);
175397
175586
  if (evicted > 0) {
175398
175587
  log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
175399
175588
  }
@@ -175425,7 +175614,8 @@ async function indexCommitsForProject(db, projectPath, directory, options) {
175425
175614
  const sinceMs = latestIndexed !== null ? Math.max(latestIndexed - 60000, Date.now() - options.sinceDays * MS_PER_DAY) : Date.now() - options.sinceDays * MS_PER_DAY;
175426
175615
  const commits = await readGitCommits(directory, {
175427
175616
  sinceMs,
175428
- maxCommits: options.maxCommits
175617
+ maxCommits: options.maxCommits,
175618
+ projectIdentity: projectPath
175429
175619
  });
175430
175620
  result.scanned = commits.length;
175431
175621
  if (commits.length === 0) {
@@ -175637,6 +175827,7 @@ function searchGitCommitsSync(db, projectPath, query, options) {
175637
175827
 
175638
175828
  // src/features/magic-context/git-commits/index.ts
175639
175829
  init_storage_git_commit_embeddings();
175830
+ init_sweep_coordinator();
175640
175831
 
175641
175832
  // src/plugin/dream-timer.ts
175642
175833
  init_embedding();
@@ -175669,7 +175860,7 @@ async function startDreamScheduleTimer(args) {
175669
175860
  const isNewRegistration = !registeredProjects.has(args.directory);
175670
175861
  registeredProjects.set(args.directory, args);
175671
175862
  if (isNewRegistration) {
175672
- log(`[dreamer] registered project ${args.directory} (dreaming=${dreamingEnabled} embeddings=${embeddingSweepEnabled} commits=${commitIndexingEnabled}; total=${registeredProjects.size})`);
175863
+ log(`[dreamer] registered project ${args.projectIdentity} (dreaming=${dreamingEnabled} embeddings=${embeddingSweepEnabled} commits=${commitIndexingEnabled}; total=${registeredProjects.size})`);
175673
175864
  }
175674
175865
  if (!activeTimer) {
175675
175866
  log(`[dreamer] started independent schedule timer (every ${DREAM_TIMER_INTERVAL_MS / 60000}m)`);
@@ -175684,7 +175875,7 @@ async function startDreamScheduleTimer(args) {
175684
175875
  }
175685
175876
  return () => {
175686
175877
  registeredProjects.delete(args.directory);
175687
- log(`[dreamer] unregistered project ${args.directory} (remaining=${registeredProjects.size})`);
175878
+ log(`[dreamer] unregistered project ${args.projectIdentity} (remaining=${registeredProjects.size})`);
175688
175879
  if (registeredProjects.size === 0 && activeTimer) {
175689
175880
  clearInterval(activeTimer);
175690
175881
  activeTimer = null;
@@ -175732,7 +175923,7 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
175732
175923
  return;
175733
175924
  }
175734
175925
  try {
175735
- log(`[dreamer] timer tick (${origin}) ${reg.directory} — checking schedule window "${reg.dreamerConfig.schedule}"`);
175926
+ log(`[dreamer] timer tick (${origin}) ${reg.projectIdentity} — checking schedule window "${reg.dreamerConfig.schedule}"`);
175736
175927
  checkScheduleAndEnqueue(db, reg.dreamerConfig.schedule, reg.projectIdentity);
175737
175928
  await processDreamQueue({
175738
175929
  db,
@@ -175747,13 +175938,34 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
175747
175938
  fallbackModels: resolveFallbackChain(DREAMER_AGENT, reg.dreamerConfig.fallback_models)
175748
175939
  });
175749
175940
  } catch (error51) {
175750
- log(`[dreamer] timer-triggered queue processing failed for ${reg.directory}:`, error51);
175941
+ log(`[dreamer] timer-triggered queue processing failed for ${reg.projectIdentity}:`, error51);
175751
175942
  }
175752
175943
  }
175944
+ function startGitSweepLeaseRenewal(db, projectIdentity, holderId) {
175945
+ const timer = setInterval(() => {
175946
+ try {
175947
+ if (!renewGitSweepLease(db, projectIdentity, holderId)) {
175948
+ log(`[git-commits] sweep lease renewal failed for ${projectIdentity}`);
175949
+ }
175950
+ } catch (error51) {
175951
+ log(`[git-commits] sweep lease renewal errored for ${projectIdentity}: ${error51 instanceof Error ? error51.message : String(error51)}`);
175952
+ }
175953
+ }, GIT_SWEEP_LEASE_RENEWAL_MS);
175954
+ timer.unref?.();
175955
+ return () => clearInterval(timer);
175956
+ }
175753
175957
  async function sweepGitCommits(args) {
175754
175958
  const { directory, projectIdentity, db, gitCommitIndexing } = args;
175959
+ const holderId = crypto.randomUUID();
175960
+ const lease2 = acquireGitSweepLease(db, projectIdentity, holderId);
175961
+ if (!lease2.acquired) {
175962
+ const reason = lease2.reason === "cooldown_active" ? `cooldown active until ${lease2.nextAllowedAt}` : `lease held by ${lease2.leaseHolder ?? "another holder"} until ${lease2.leaseExpiresAt ?? "unknown"}`;
175963
+ log(`[git-commits] sweep skipped for ${projectIdentity}: ${reason}`);
175964
+ return;
175965
+ }
175755
175966
  const startedAt = Date.now();
175756
- log(`[git-commits] sweep starting for ${directory} (sinceDays=${gitCommitIndexing.since_days} maxCommits=${gitCommitIndexing.max_commits})`);
175967
+ const stopRenewal = startGitSweepLeaseRenewal(db, projectIdentity, holderId);
175968
+ log(`[git-commits] sweep starting for ${projectIdentity} (sinceDays=${gitCommitIndexing.since_days} maxCommits=${gitCommitIndexing.max_commits})`);
175757
175969
  try {
175758
175970
  const result = await indexCommitsForProject(db, projectIdentity, directory, {
175759
175971
  sinceDays: gitCommitIndexing.since_days,
@@ -175763,11 +175975,19 @@ async function sweepGitCommits(args) {
175763
175975
  if (result.embedded > 0) {
175764
175976
  drainedEmbeddings = await embedUnembeddedCommits(db, projectIdentity);
175765
175977
  }
175978
+ const cooldownMarked = markGitSweepSuccessAndRelease(db, projectIdentity, holderId);
175979
+ if (!cooldownMarked) {
175980
+ releaseGitSweepLease(db, projectIdentity, holderId);
175981
+ log(`[git-commits] sweep finished for ${projectIdentity}, but lease was no longer active; cooldown not advanced`);
175982
+ }
175766
175983
  const elapsedMs = Date.now() - startedAt;
175767
175984
  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
175985
  } catch (error51) {
175986
+ releaseGitSweepLease(db, projectIdentity, holderId);
175769
175987
  const elapsedMs = Date.now() - startedAt;
175770
- log(`[git-commits] sweep failed for ${directory} after ${elapsedMs}ms: ${error51 instanceof Error ? error51.message : String(error51)}`);
175988
+ log(`[git-commits] sweep failed for ${projectIdentity} after ${elapsedMs}ms: ${error51 instanceof Error ? error51.message : String(error51)}`);
175989
+ } finally {
175990
+ stopRenewal();
175771
175991
  }
175772
175992
  }
175773
175993
 
@@ -175908,7 +176128,7 @@ init_models_dev_cache();
175908
176128
  var DEFAULT_CONTEXT_LIMIT = 128000;
175909
176129
  var MAX_EXECUTE_THRESHOLD = 80;
175910
176130
  function resolveContextLimit(providerID, modelID, ctx) {
175911
- const fromModelsDev = providerID && modelID ? getModelsDevContextLimit(providerID, modelID) : undefined;
176131
+ const fromModelsDev = providerID && modelID ? getSdkContextLimit(providerID, modelID) : undefined;
175912
176132
  const baseline = fromModelsDev ?? DEFAULT_CONTEXT_LIMIT;
175913
176133
  if (ctx?.db && ctx.sessionID) {
175914
176134
  try {
@@ -175921,7 +176141,7 @@ function resolveContextLimit(providerID, modelID, ctx) {
175921
176141
  return baseline;
175922
176142
  }
175923
176143
  function resolveTrustedContextLimit(providerID, modelID, ctx) {
175924
- const fromModelsDev = providerID && modelID ? getModelsDevContextLimit(providerID, modelID) : undefined;
176144
+ const fromModelsDev = providerID && modelID ? getSdkContextLimit(providerID, modelID) : undefined;
175925
176145
  let detected;
175926
176146
  if (ctx?.db && ctx.sessionID) {
175927
176147
  try {
@@ -177279,7 +177499,8 @@ function getSessionCreatedInfo(properties) {
177279
177499
  id: info.id,
177280
177500
  parentID: info.parentID,
177281
177501
  providerID: typeof info.providerID === "string" ? info.providerID : undefined,
177282
- modelID: typeof info.modelID === "string" ? info.modelID : undefined
177502
+ modelID: typeof info.modelID === "string" ? info.modelID : undefined,
177503
+ title: typeof info.title === "string" ? info.title : undefined
177283
177504
  };
177284
177505
  }
177285
177506
  function getMessageUpdatedAssistantInfo(properties) {
@@ -179346,6 +179567,64 @@ init_embedding();
179346
179567
 
179347
179568
  // src/features/magic-context/search.ts
179348
179569
  init_logger();
179570
+
179571
+ // src/features/magic-context/literal-probes.ts
179572
+ var MAX_PROBES = 5;
179573
+ var MIN_PROBE_LENGTH = 3;
179574
+ var SLASH_COMMAND_RE = /\/[a-z][a-z0-9]*(?:-[a-z0-9]+)+/gi;
179575
+ var KEBAB_SNAKE_RE = /[a-z][a-z0-9]*(?:[-_][a-z0-9]+)+/gi;
179576
+ var DOTTED_RE = /[a-z0-9][a-z0-9_-]*(?:\.[a-z0-9_-]+)+/gi;
179577
+ var CAMEL_RE = /\b[a-zA-Z][a-z0-9]*(?:[A-Z][a-z0-9]*)+\b/g;
179578
+ var SHA_RE = /\b[0-9a-f]{7,40}\b/gi;
179579
+ var ERROR_CODE_RE = /\b(?:TS\d{4,}|ERR_[A-Z][A-Z0-9_]*)\b/g;
179580
+ var QUOTED_RE = /["`]([^"`]{3,80})["`]/g;
179581
+ function looksLikeSha(token) {
179582
+ return /[0-9]/.test(token) && /^[0-9a-f]{7,40}$/i.test(token);
179583
+ }
179584
+ function extractLiteralProbes(query) {
179585
+ const trimmed = query.trim();
179586
+ if (trimmed.length === 0)
179587
+ return [];
179588
+ const ordered = [];
179589
+ const seen = new Set;
179590
+ const add = (raw) => {
179591
+ if (!raw)
179592
+ return;
179593
+ const probe = raw.trim();
179594
+ if (probe.length < MIN_PROBE_LENGTH)
179595
+ return;
179596
+ const key = probe.toLowerCase();
179597
+ if (seen.has(key))
179598
+ return;
179599
+ seen.add(key);
179600
+ ordered.push(probe);
179601
+ };
179602
+ for (const m of trimmed.matchAll(QUOTED_RE))
179603
+ add(m[1]);
179604
+ for (const m of trimmed.matchAll(SLASH_COMMAND_RE))
179605
+ add(m[0]);
179606
+ for (const m of trimmed.matchAll(ERROR_CODE_RE))
179607
+ add(m[0]);
179608
+ for (const m of trimmed.matchAll(DOTTED_RE))
179609
+ add(m[0]);
179610
+ for (const m of trimmed.matchAll(KEBAB_SNAKE_RE))
179611
+ add(m[0]);
179612
+ for (const m of trimmed.matchAll(CAMEL_RE))
179613
+ add(m[0]);
179614
+ for (const m of trimmed.matchAll(SHA_RE)) {
179615
+ if (looksLikeSha(m[0]))
179616
+ add(m[0]);
179617
+ }
179618
+ return ordered.slice(0, MAX_PROBES);
179619
+ }
179620
+ function containsProbeVerbatim(text, probes) {
179621
+ if (probes.length === 0)
179622
+ return false;
179623
+ const haystack = text.toLowerCase();
179624
+ return probes.some((probe) => haystack.includes(probe.toLowerCase()));
179625
+ }
179626
+
179627
+ // src/features/magic-context/search.ts
179349
179628
  init_memory();
179350
179629
  init_embedding();
179351
179630
  init_storage_memory_fts();
@@ -179525,36 +179804,82 @@ function linearDecayScore(rank, total) {
179525
179804
  return 0;
179526
179805
  return Math.max(0, 1 - rank / total);
179527
179806
  }
179528
- function searchMessages(args) {
179529
- const sanitizedQuery = sanitizeFtsQuery(args.query.trim());
179530
- if (sanitizedQuery.length === 0) {
179807
+ function runMessageFtsQuery(db, sessionId, ftsQuery, fetchLimit, cutoff) {
179808
+ if (ftsQuery.length === 0)
179531
179809
  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) => {
179810
+ const rows = getMessageSearchStatement(db).all(sessionId, ftsQuery, fetchLimit).map((row) => row);
179811
+ const result = [];
179812
+ for (const row of rows) {
179537
179813
  const messageOrdinal = getMessageOrdinal(row.messageOrdinal);
179538
179814
  if (messageOrdinal === null || typeof row.messageId !== "string" || typeof row.role !== "string" || typeof row.content !== "string") {
179539
- return null;
179815
+ continue;
179540
179816
  }
179541
179817
  if (cutoff !== null && messageOrdinal > cutoff) {
179542
- return null;
179818
+ continue;
179543
179819
  }
179544
- return {
179820
+ result.push({
179545
179821
  messageOrdinal,
179546
179822
  messageId: row.messageId,
179547
179823
  role: row.role,
179548
179824
  content: row.content
179549
- };
179550
- }).filter((result) => result !== null).slice(0, args.limit);
179551
- return filtered.map((row, rank) => ({
179825
+ });
179826
+ }
179827
+ return result;
179828
+ }
179829
+ var RRF_K = 60;
179830
+ var VERBATIM_PROBE_BONUS = 0.5;
179831
+ function searchMessages(args) {
179832
+ const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
179833
+ const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
179834
+ const baseQuery = sanitizeFtsQuery(args.query.trim());
179835
+ const probes = args.probes ?? [];
179836
+ if (probes.length === 0) {
179837
+ const filtered = runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff).slice(0, args.limit);
179838
+ return filtered.map((row, rank) => ({
179839
+ source: "message",
179840
+ content: previewText(row.content),
179841
+ score: linearDecayScore(rank, filtered.length),
179842
+ messageOrdinal: row.messageOrdinal,
179843
+ messageId: row.messageId,
179844
+ role: row.role
179845
+ }));
179846
+ }
179847
+ const queryLists = [];
179848
+ if (baseQuery.length > 0) {
179849
+ queryLists.push(runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff));
179850
+ }
179851
+ for (const probe of probes) {
179852
+ const probeQuery = sanitizeFtsQuery(probe);
179853
+ if (probeQuery.length === 0)
179854
+ continue;
179855
+ queryLists.push(runMessageFtsQuery(args.db, args.sessionId, probeQuery, fetchLimit, cutoff));
179856
+ }
179857
+ const fused = new Map;
179858
+ for (const list of queryLists) {
179859
+ list.forEach((row, rank) => {
179860
+ const rrf = 1 / (RRF_K + rank);
179861
+ const existing = fused.get(row.messageId);
179862
+ if (existing) {
179863
+ existing.score += rrf;
179864
+ } else {
179865
+ fused.set(row.messageId, { row, score: rrf });
179866
+ }
179867
+ });
179868
+ }
179869
+ for (const entry of fused.values()) {
179870
+ if (containsProbeVerbatim(entry.row.content, probes)) {
179871
+ entry.score += VERBATIM_PROBE_BONUS;
179872
+ }
179873
+ }
179874
+ 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);
179875
+ const maxScore = ranked.length > 0 ? ranked[0].score : 1;
179876
+ return ranked.map((entry) => ({
179552
179877
  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
179878
+ content: previewText(entry.row.content),
179879
+ score: maxScore > 0 ? entry.score / maxScore : 0,
179880
+ messageOrdinal: entry.row.messageOrdinal,
179881
+ messageId: entry.row.messageId,
179882
+ role: entry.row.role
179558
179883
  }));
179559
179884
  }
179560
179885
  function getSourceBoost(result) {
@@ -179638,12 +179963,14 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
179638
179963
  return null;
179639
179964
  }) : Promise.resolve(null);
179640
179965
  await Promise.resolve();
179966
+ const messageProbes = options.explicitSearch ? extractLiteralProbes(trimmedQuery) : [];
179641
179967
  const messageResults = runMessages ? searchMessages({
179642
179968
  db,
179643
179969
  sessionId,
179644
179970
  query: trimmedQuery,
179645
179971
  limit: tierLimit,
179646
- maxOrdinal: options.maxMessageOrdinal
179972
+ maxOrdinal: options.maxMessageOrdinal,
179973
+ probes: messageProbes
179647
179974
  }) : [];
179648
179975
  const queryEmbedding = await queryEmbeddingPromise;
179649
179976
  const [memoryResults, gitCommitResults] = await Promise.all([
@@ -180226,7 +180553,7 @@ function isVisibleNoteReadPart(part) {
180226
180553
  }
180227
180554
 
180228
180555
  // src/hooks/magic-context/todo-view.ts
180229
- import { createHash as createHash10 } from "node:crypto";
180556
+ import { createHash as createHash9 } from "node:crypto";
180230
180557
  var TERMINAL_STATUSES = new Set(["completed", "cancelled"]);
180231
180558
  var TITLE_DONE_STATUSES = new Set(["completed"]);
180232
180559
  var SYNTHETIC_CALL_ID_PREFIX = "mc_synthetic_todo_";
@@ -180271,7 +180598,7 @@ function buildSyntheticTodoPart(stateJson) {
180271
180598
  };
180272
180599
  }
180273
180600
  function computeSyntheticCallId(stateJson) {
180274
- const hash2 = createHash10("sha256").update(stateJson).digest("hex").slice(0, 16);
180601
+ const hash2 = createHash9("sha256").update(stateJson).digest("hex").slice(0, 16);
180275
180602
  return `${SYNTHETIC_CALL_ID_PREFIX}${hash2}`;
180276
180603
  }
180277
180604
  function parseTodoState(stateJson) {
@@ -180837,6 +181164,10 @@ function createTransform(deps) {
180837
181164
  return;
180838
181165
  }
180839
181166
  logTransformTiming(sessionId, "getOrCreateSessionMeta", tMeta);
181167
+ if (deps.internalChildSessions?.has(sessionId)) {
181168
+ sessionLog(sessionId, "transform skipped (internal magic-context child session)");
181169
+ return;
181170
+ }
180840
181171
  const reducedMode = sessionMeta.isSubagent;
180841
181172
  const fullFeatureMode = !reducedMode;
180842
181173
  let sessionDirectory = deps.directory ?? "";
@@ -181432,6 +181763,7 @@ function truncateHistorianEmergencyError(error51) {
181432
181763
 
181433
181764
  // src/hooks/magic-context/event-handler.ts
181434
181765
  var CONTEXT_USAGE_TTL_MS = 60 * 60 * 1000;
181766
+ var INTERNAL_CHILD_TITLE_PREFIX = "magic-context-";
181435
181767
  function formatTokens(value) {
181436
181768
  return value.toLocaleString();
181437
181769
  }
@@ -181496,6 +181828,10 @@ function createEventHandler2(deps) {
181496
181828
  if (!info) {
181497
181829
  return;
181498
181830
  }
181831
+ if (deps.internalChildSessions && info.parentID.length > 0 && typeof info.title === "string" && info.title.startsWith(INTERNAL_CHILD_TITLE_PREFIX)) {
181832
+ deps.internalChildSessions.add(info.id);
181833
+ sessionLog(info.id, `marked internal magic-context child (title="${info.title}") — exempt from transform + injection`);
181834
+ }
181499
181835
  try {
181500
181836
  const modelKey = resolveModelKey(info.providerID, info.modelID);
181501
181837
  updateSessionMeta(deps.db, info.id, {
@@ -181912,11 +182248,10 @@ await __promiseAll([
181912
182248
  ]);
181913
182249
 
181914
182250
  // src/hooks/magic-context/text-complete.ts
181915
- var LEADING_TAG_PREFIX_REGEX = /^(\u00a7\d+\u00a7\s*)+/;
181916
- var SECTION_CHAR_REGEX = /\u00a7/g;
182251
+ init_tag_content_primitives();
181917
182252
  function createTextCompleteHandler() {
181918
182253
  return async (_input, output) => {
181919
- output.text = output.text.replace(LEADING_TAG_PREFIX_REGEX, "").replace(SECTION_CHAR_REGEX, "");
182254
+ output.text = stripPersistedAssistantText(output.text);
181920
182255
  };
181921
182256
  }
181922
182257
 
@@ -182138,7 +182473,7 @@ function createToolExecuteAfterHook(args) {
182138
182473
  init_send_session_notification();
182139
182474
 
182140
182475
  // src/hooks/magic-context/system-prompt-hash.ts
182141
- import { createHash as createHash11 } from "node:crypto";
182476
+ import { createHash as createHash10 } from "node:crypto";
182142
182477
 
182143
182478
  // src/agents/magic-context-prompt.ts
182144
182479
  var LONG_TERM_PARTNER_FRAME = `### You are the user's long-term partner on this project — not a one-off hire
@@ -182260,6 +182595,9 @@ function clearSystemPromptHashSession(sessionId, handleMaps) {
182260
182595
  function isInternalOpenCodeAgent(systemPromptContent) {
182261
182596
  return systemPromptContent.includes("You are a title generator. You output ONLY a thread title.") || systemPromptContent.includes("Summarize what was done in this conversation. Write like a pull request description.") || systemPromptContent.includes("You are an anchored context summarization assistant for coding sessions.");
182262
182597
  }
182598
+ function isMagicContextInternalAgent(systemPromptContent) {
182599
+ return systemPromptContent.includes("You are Historian — the hippocampus of a long-running coding agent.") || systemPromptContent.includes("You are a memory maintenance agent for the magic-context system.") || systemPromptContent.includes("You are Sidekick, a focused memory-retrieval subagent for an AI coding assistant.") || systemPromptContent.includes("You are a file importance evaluator. Given read statistics about files");
182600
+ }
182263
182601
  function createSystemPromptHashHandler(deps) {
182264
182602
  const stickyDateBySession = new Map;
182265
182603
  const handler = async (input, output) => {
@@ -182272,6 +182610,10 @@ function createSystemPromptHashHandler(deps) {
182272
182610
  sessionLog(sessionId, "system-prompt-hash skipped (OpenCode internal agent: title/summary/compaction)");
182273
182611
  return;
182274
182612
  }
182613
+ if (deps.internalChildSessions?.has(sessionId) || isMagicContextInternalAgent(fullPromptForDetection)) {
182614
+ sessionLog(sessionId, "system-prompt-hash skipped (Magic Context internal child: historian/dreamer/sidekick/migration)");
182615
+ return;
182616
+ }
182275
182617
  const injectionEnabled = deps.injectionEnabled !== false;
182276
182618
  const skipSignatures = deps.injectionSkipSignatures ?? [];
182277
182619
  if (!injectionEnabled) {
@@ -182322,7 +182664,7 @@ function createSystemPromptHashHandler(deps) {
182322
182664
  `);
182323
182665
  if (systemContent.length === 0)
182324
182666
  return;
182325
- const currentHash = createHash11("md5").update(systemContent).digest("hex");
182667
+ const currentHash = createHash10("md5").update(systemContent).digest("hex");
182326
182668
  if (!sessionMetaEarly) {
182327
182669
  return;
182328
182670
  }
@@ -182530,6 +182872,7 @@ function createMagicContextHook(deps) {
182530
182872
  const liveModelBySession = deps.liveSessionState?.liveModelBySession ?? new Map;
182531
182873
  const agentBySession = deps.liveSessionState?.agentBySession ?? new Map;
182532
182874
  const sessionDirectoryBySession = deps.liveSessionState?.sessionDirectoryBySession ?? new Map;
182875
+ const internalChildSessions = deps.liveSessionState?.internalChildSessions ?? new Set;
182533
182876
  const recompProgressBySession = deps.liveSessionState?.recompProgressBySession ?? new Map;
182534
182877
  const recentReduceBySession = new Map;
182535
182878
  const toolUsageSinceUserTurn = new Map;
@@ -182561,7 +182904,8 @@ function createMagicContextHook(deps) {
182561
182904
  pendingMaterializationSessions,
182562
182905
  deferredMaterializationSessions,
182563
182906
  sessionDirectoryBySession,
182564
- recompProgressBySession
182907
+ recompProgressBySession,
182908
+ internalChildSessions
182565
182909
  },
182566
182910
  directory: deps.directory,
182567
182911
  historianChunkTokens: getHistorianChunkTokens(),
@@ -182606,6 +182950,7 @@ function createMagicContextHook(deps) {
182606
182950
  deferredMaterializationSessions,
182607
182951
  lastHeuristicsTurnId,
182608
182952
  commitSeenLastPass,
182953
+ internalChildSessions,
182609
182954
  client: deps.client,
182610
182955
  directory: deps.directory,
182611
182956
  memoryConfig: deps.config.memory ? {
@@ -182657,6 +183002,7 @@ function createMagicContextHook(deps) {
182657
183002
  tagger: deps.tagger,
182658
183003
  db,
182659
183004
  client: deps.client,
183005
+ internalChildSessions,
182660
183006
  getNotificationParams: (sessionId) => getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
182661
183007
  nudgePlacements,
182662
183008
  onSessionCacheInvalidated: (sessionId) => {
@@ -182674,6 +183020,7 @@ function createMagicContextHook(deps) {
182674
183020
  recompProgressBySession.delete(sessionId);
182675
183021
  recentReduceBySession.delete(sessionId);
182676
183022
  toolUsageSinceUserTurn.delete(sessionId);
183023
+ internalChildSessions.delete(sessionId);
182677
183024
  }
182678
183025
  });
182679
183026
  const runDreamQueueInBackground = () => {
@@ -182784,6 +183131,7 @@ function createMagicContextHook(deps) {
182784
183131
  injectionSkipSignatures: deps.config.system_prompt_injection?.skip_signatures ?? [
182785
183132
  "<!-- magic-context: skip -->"
182786
183133
  ],
183134
+ internalChildSessions,
182787
183135
  experimentalUserMemories: deps.config.dreamer?.user_memories?.enabled,
182788
183136
  experimentalPinKeyFiles: deps.config.dreamer?.pin_key_files?.enabled ?? false,
182789
183137
  experimentalPinKeyFilesTokenBudget: deps.config.dreamer?.pin_key_files?.token_budget,
@@ -183551,6 +183899,7 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
183551
183899
  if (!p)
183552
183900
  return null;
183553
183901
  return {
183902
+ kind: p.kind ?? "recomp",
183554
183903
  phase: p.phase,
183555
183904
  processedMessages: p.processedMessages,
183556
183905
  totalMessages: p.totalMessages,
@@ -183570,6 +183919,7 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
183570
183919
  return {
183571
183920
  ...empty,
183572
183921
  recompProgress: {
183922
+ kind: p.kind ?? "recomp",
183573
183923
  phase: p.phase,
183574
183924
  processedMessages: p.processedMessages,
183575
183925
  totalMessages: p.totalMessages,
@@ -184286,16 +184636,30 @@ Example: \`ctx_note(action="write", content="Implement X because Y", surface_con
184286
184636
 
184287
184637
  Historian reads these notes, deduplicates them, and rewrites the remaining useful notes over time.`;
184288
184638
  // src/tools/ctx-note/tools.ts
184289
- await init_storage();
184639
+ await __promiseAll([
184640
+ init_message_index(),
184641
+ init_storage()
184642
+ ]);
184290
184643
  import { tool as tool3 } from "@opencode-ai/plugin";
184644
+ function captureAnchorOrdinal(db, sessionId) {
184645
+ try {
184646
+ const ordinal = getLastIndexedOrdinal(db, sessionId);
184647
+ return ordinal > 0 ? ordinal : null;
184648
+ } catch {
184649
+ return null;
184650
+ }
184651
+ }
184652
+ function anchorSuffix(note) {
184653
+ return note.anchorOrdinal !== null ? ` ↳ @msg ${note.anchorOrdinal}` : "";
184654
+ }
184291
184655
  function formatNoteLine(note) {
184292
184656
  const statusSuffix = note.status === "active" ? "" : ` (${note.status})`;
184293
184657
  if (note.type === "session") {
184294
- return `- **#${note.id}**${statusSuffix}: ${note.content}`;
184658
+ return `- **#${note.id}**${statusSuffix}: ${note.content}${anchorSuffix(note)}`;
184295
184659
  }
184296
184660
  const conditionText = note.status === "ready" ? note.readyReason ?? note.surfaceCondition ?? "Condition satisfied" : note.surfaceCondition ?? "No condition recorded";
184297
184661
  const conditionLabel = note.status === "ready" ? "Condition met" : "Condition";
184298
- return `- **#${note.id}**${statusSuffix}: ${note.content}
184662
+ return `- **#${note.id}**${statusSuffix}: ${note.content}${anchorSuffix(note)}
184299
184663
  ${conditionLabel}: ${conditionText}`;
184300
184664
  }
184301
184665
  var DISMISS_FOOTER = `
@@ -184373,6 +184737,7 @@ function createCtxNoteTool(deps) {
184373
184737
  if (!content) {
184374
184738
  return "Error: 'content' is required when action is 'write'.";
184375
184739
  }
184740
+ const anchorOrdinal = captureAnchorOrdinal(deps.db, sessionId);
184376
184741
  if (args.surface_condition?.trim()) {
184377
184742
  if (!deps.dreamerEnabled) {
184378
184743
  return "Error: Smart notes require dreamer to be enabled. Enable dreamer in magic-context.jsonc to use surface_condition.";
@@ -184384,13 +184749,14 @@ function createCtxNoteTool(deps) {
184384
184749
  content,
184385
184750
  projectPath: projectIdentity,
184386
184751
  sessionId,
184387
- surfaceCondition: args.surface_condition.trim()
184752
+ surfaceCondition: args.surface_condition.trim(),
184753
+ anchorOrdinal
184388
184754
  });
184389
184755
  return `Created smart note #${note2.id}. Dreamer will evaluate the condition during nightly runs:
184390
184756
  - Content: ${content}
184391
184757
  - Condition: ${args.surface_condition.trim()}`;
184392
184758
  }
184393
- const note = addNote(deps.db, "session", { sessionId, content });
184759
+ const note = addNote(deps.db, "session", { sessionId, content, anchorOrdinal });
184394
184760
  return `Saved session note #${note.id}. Historian will rewrite or deduplicate notes as needed.`;
184395
184761
  }
184396
184762
  if (action === "dismiss") {
@@ -184453,9 +184819,13 @@ ${parts.join(`
184453
184819
 
184454
184820
  No session notes or smart notes.`;
184455
184821
  }
184456
- return sections.join(`
184822
+ const body = sections.join(`
184457
184823
 
184458
- `) + DISMISS_FOOTER;
184824
+ `);
184825
+ const anchorHint = body.includes("↳ @msg ") ? `
184826
+
184827
+ ↳ @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).` : "";
184828
+ return body + anchorHint + DISMISS_FOOTER;
184459
184829
  }
184460
184830
  });
184461
184831
  }
@@ -184746,7 +185116,8 @@ function createCtxSearchTool(deps) {
184746
185116
  maxMessageOrdinal: lastCompartmentEnd >= 0 ? lastCompartmentEnd : undefined,
184747
185117
  gitCommitsEnabled,
184748
185118
  sources: normalizeSources(args.sources),
184749
- visibleMemoryIds
185119
+ visibleMemoryIds,
185120
+ explicitSearch: true
184750
185121
  });
184751
185122
  return formatSearchResults(query, results);
184752
185123
  }
@@ -184847,22 +185218,22 @@ init_models_dev_cache();
184847
185218
  init_logger();
184848
185219
  import { randomBytes } from "node:crypto";
184849
185220
  import {
184850
- mkdirSync as mkdirSync8,
185221
+ mkdirSync as mkdirSync9,
184851
185222
  readdirSync,
184852
185223
  readFileSync as readFileSync15,
184853
- renameSync as renameSync3,
185224
+ renameSync as renameSync4,
184854
185225
  unlinkSync as unlinkSync3,
184855
- writeFileSync as writeFileSync7
185226
+ writeFileSync as writeFileSync8
184856
185227
  } from "node:fs";
184857
185228
  import { createServer } from "node:http";
184858
185229
  import { dirname as dirname8 } from "node:path";
184859
185230
 
184860
185231
  // src/shared/rpc-utils.ts
184861
- import { createHash as createHash12 } from "node:crypto";
185232
+ import { createHash as createHash11 } from "node:crypto";
184862
185233
  import { join as join24 } from "node:path";
184863
185234
  function projectHash(directory) {
184864
185235
  const normalized = directory.replace(/\/+$/, "");
184865
- return createHash12("sha256").update(normalized).digest("hex").slice(0, 16);
185236
+ return createHash11("sha256").update(normalized).digest("hex").slice(0, 16);
184866
185237
  }
184867
185238
  function rpcPortDir(storageDir, directory) {
184868
185239
  return join24(storageDir, "rpc", projectHash(directory));
@@ -184945,15 +185316,15 @@ class MagicContextRpcServer {
184945
185316
  try {
184946
185317
  this.warnIfOtherLiveInstance();
184947
185318
  const dir = dirname8(this.portFilePath);
184948
- mkdirSync8(dir, { recursive: true, mode: 448 });
185319
+ mkdirSync9(dir, { recursive: true, mode: 448 });
184949
185320
  const tmpPath = `${this.portFilePath}.tmp`;
184950
- writeFileSync7(tmpPath, JSON.stringify({
185321
+ writeFileSync8(tmpPath, JSON.stringify({
184951
185322
  port: this.port,
184952
185323
  pid: process.pid,
184953
185324
  started_at: this.startedAt,
184954
185325
  token: this.token
184955
185326
  }), { encoding: "utf-8", mode: 384 });
184956
- renameSync3(tmpPath, this.portFilePath);
185327
+ renameSync4(tmpPath, this.portFilePath);
184957
185328
  log(`[rpc] server listening on 127.0.0.1:${this.port}`);
184958
185329
  } catch (err) {
184959
185330
  log(`[rpc] failed to write port file: ${err}`);
@@ -185153,7 +185524,7 @@ var plugin = async (ctx) => {
185153
185524
  rpcServer.start().catch((err) => {
185154
185525
  log(`[magic-context] RPC server failed to start: ${err}`);
185155
185526
  });
185156
- refreshModelLimitsFromApi(ctx.client);
185527
+ refreshModelLimitsFromApi(ctx.client, { retries: 3, retryDelayMs: 1000 });
185157
185528
  }
185158
185529
  {
185159
185530
  const fence = getSchemaFenceRejection();