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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +11 -26
  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 +566 -194
  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
@@ -15216,9 +15216,6 @@ function getMagicContextStorageDir() {
15216
15216
  function getLegacyOpenCodeMagicContextStorageDir() {
15217
15217
  return path2.join(getOpenCodeStorageDir(), "plugin", "magic-context");
15218
15218
  }
15219
- function getCacheDir() {
15220
- return process.env.XDG_CACHE_HOME ?? path2.join(os.homedir(), ".cache");
15221
- }
15222
15219
  var init_data_path = () => {};
15223
15220
 
15224
15221
  // src/shared/logger.ts
@@ -149164,14 +149161,45 @@ function readRawSessionMessageByIdFromDb(db, sessionId, messageId) {
149164
149161
  }
149165
149162
 
149166
149163
  // src/hooks/magic-context/tag-content-primitives.ts
149164
+ function stripWellFormedLeadingTagPrefix(value) {
149165
+ return value.replace(/^(\u00a7\d+\u00a7\s*)+/, "");
149166
+ }
149167
+ function stripCompleteTagPairsGlobally(value) {
149168
+ return value.replace(COMPLETE_TAG_PAIR_GLOBAL_REGEX, "");
149169
+ }
149170
+ function stripMalformedTagNotationGlobally(value) {
149171
+ return value.replace(MALFORMED_TAG_GLOBAL_REGEX, "");
149172
+ }
149173
+ function stripTagSectionCharacters(value) {
149174
+ return value.replace(STRAY_SECTION_CHAR_REGEX, "");
149175
+ }
149176
+ function stripPersistedAssistantText(value) {
149177
+ let text = stripWellFormedLeadingTagPrefix(value);
149178
+ text = stripCompleteTagPairsGlobally(text);
149179
+ text = stripMalformedTagNotationGlobally(text);
149180
+ text = stripTagSectionCharacters(text);
149181
+ return text.trim();
149182
+ }
149167
149183
  function byteSize(value) {
149168
149184
  return encoder.encode(value).length;
149169
149185
  }
149170
149186
  function stripTagPrefix(value) {
149171
- let stripped = value.replace(MALFORMED_TAG_PREFIX_REGEX, "");
149172
- stripped = stripped.replace(TAG_PREFIX_REGEX, "");
149187
+ let stripped = value;
149188
+ for (let pass = 0;pass < 8; pass++) {
149189
+ const prev = stripped;
149190
+ stripped = stripped.replace(MALFORMED_TAG_PREFIX_REGEX, "");
149191
+ stripped = stripped.replace(TAG_PREFIX_REGEX, "");
149192
+ if (stripped === prev)
149193
+ break;
149194
+ }
149173
149195
  return stripped;
149174
149196
  }
149197
+ function peelLeadingMcTagNotation(value) {
149198
+ const body = stripTagPrefix(value);
149199
+ if (body === value)
149200
+ return { tagPrefix: "", body };
149201
+ return { tagPrefix: value.slice(0, value.length - body.length), body };
149202
+ }
149175
149203
  function prependTag(tagId, value) {
149176
149204
  const stripped = stripTagPrefix(value);
149177
149205
  return `§${tagId}§ ${stripped}`;
@@ -149182,11 +149210,14 @@ function isThinkingPart(part) {
149182
149210
  const candidate = part;
149183
149211
  return candidate.type === "thinking" || candidate.type === "reasoning";
149184
149212
  }
149185
- var encoder, TAG_PREFIX_REGEX, MALFORMED_TAG_PREFIX_REGEX;
149213
+ var encoder, TAG_PREFIX_REGEX, MALFORMED_TAG_PREFIX_REGEX, COMPLETE_TAG_PAIR_GLOBAL_REGEX, MALFORMED_TAG_GLOBAL_REGEX, STRAY_SECTION_CHAR_REGEX;
149186
149214
  var init_tag_content_primitives = __esm(() => {
149187
149215
  encoder = new TextEncoder;
149188
149216
  TAG_PREFIX_REGEX = /^(?:§\d+§\s*)+/;
149189
149217
  MALFORMED_TAG_PREFIX_REGEX = /^(?:§\d+">§(?:\d+§)?\s*)+/;
149218
+ COMPLETE_TAG_PAIR_GLOBAL_REGEX = /\u00a7\d+\u00a7/g;
149219
+ MALFORMED_TAG_GLOBAL_REGEX = /\u00a7\d+">(?:\u00a7(?:\d+\u00a7)?)?/g;
149220
+ STRAY_SECTION_CHAR_REGEX = /\u00a7/g;
149190
149221
  });
149191
149222
 
149192
149223
  // src/hooks/magic-context/tag-part-guards.ts
@@ -149314,7 +149345,9 @@ function hasMeaningfulPart(part) {
149314
149345
  return false;
149315
149346
  const type = part.type;
149316
149347
  if (type === "text") {
149317
- return typeof part.text === "string" && part.text.trim().length > 0;
149348
+ if (typeof part.text !== "string")
149349
+ return false;
149350
+ return stripTagPrefix(part.text).trim().length > 0;
149318
149351
  }
149319
149352
  if (typeof type !== "string")
149320
149353
  return false;
@@ -149430,6 +149463,7 @@ function createToolDropTarget(compositeKey, thinkingParts, index, batch) {
149430
149463
  }
149431
149464
  var DROP_PREFIX = "[dropped", IGNORE_PART_TYPES, TRUNCATION_SENTINEL = "...[truncated]";
149432
149465
  var init_tool_drop_target = __esm(() => {
149466
+ init_tag_content_primitives();
149433
149467
  IGNORE_PART_TYPES = new Set([
149434
149468
  "thinking",
149435
149469
  "reasoning",
@@ -150730,6 +150764,17 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
150730
150764
  updated_at INTEGER NOT NULL DEFAULT 0
150731
150765
  );
150732
150766
 
150767
+ CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
150768
+ project_path TEXT PRIMARY KEY,
150769
+ lease_holder TEXT,
150770
+ lease_expires_at INTEGER,
150771
+ last_swept_at INTEGER
150772
+ );
150773
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
150774
+ ON git_sweep_coordinator(lease_expires_at);
150775
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
150776
+ ON git_sweep_coordinator(last_swept_at);
150777
+
150733
150778
  CREATE TABLE IF NOT EXISTS m0_mutation_log (
150734
150779
  id INTEGER PRIMARY KEY AUTOINCREMENT,
150735
150780
  session_id TEXT NOT NULL,
@@ -151259,7 +151304,7 @@ function getDatabasePersistenceError(db) {
151259
151304
  return null;
151260
151305
  return persistenceErrorByDatabase.get(db) ?? null;
151261
151306
  }
151262
- var databases, persistenceByDatabase, persistenceErrorByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 27, sqlitePragmaConfig;
151307
+ var databases, persistenceByDatabase, persistenceErrorByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 29, sqlitePragmaConfig;
151263
151308
  var init_storage_db = __esm(async () => {
151264
151309
  init_data_path();
151265
151310
  init_logger();
@@ -152050,6 +152095,38 @@ var init_migrations = __esm(async () => {
152050
152095
  ON tags(session_id, entry_fingerprint)
152051
152096
  WHERE type='message' AND entry_fingerprint IS NOT NULL`);
152052
152097
  }
152098
+ },
152099
+ {
152100
+ version: 28,
152101
+ description: "Add git commit sweep coordinator lease/cooldown table",
152102
+ up: (db) => {
152103
+ db.exec(`
152104
+ CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
152105
+ project_path TEXT PRIMARY KEY,
152106
+ lease_holder TEXT,
152107
+ lease_expires_at INTEGER,
152108
+ last_swept_at INTEGER
152109
+ );
152110
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
152111
+ ON git_sweep_coordinator(lease_expires_at);
152112
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
152113
+ ON git_sweep_coordinator(last_swept_at);
152114
+ `);
152115
+ }
152116
+ },
152117
+ {
152118
+ version: 29,
152119
+ description: "Add anchor_ordinal to notes (traceback to the conversation tail)",
152120
+ up: (db) => {
152121
+ const notesExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='notes'").get();
152122
+ if (!notesExists) {
152123
+ return;
152124
+ }
152125
+ const columns = db.prepare("PRAGMA table_info(notes)").all();
152126
+ if (!columns.some((column) => column.name === "anchor_ordinal")) {
152127
+ db.exec("ALTER TABLE notes ADD COLUMN anchor_ordinal INTEGER");
152128
+ }
152129
+ }
152053
152130
  }
152054
152131
  ];
152055
152132
  LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -153007,7 +153084,8 @@ function toNote(row) {
153007
153084
  updatedAt: row.updated_at,
153008
153085
  lastCheckedAt: toNullableNumber(row.last_checked_at),
153009
153086
  readyAt: toNullableNumber(row.ready_at),
153010
- readyReason: toNullableString(row.ready_reason)
153087
+ readyReason: toNullableString(row.ready_reason),
153088
+ anchorOrdinal: toNullableNumber(row.anchor_ordinal)
153011
153089
  };
153012
153090
  }
153013
153091
  function getNoteById(db, noteId) {
@@ -153059,7 +153137,7 @@ function getNotes(db, options = {}) {
153059
153137
  }
153060
153138
  function addNote(db, type, options) {
153061
153139
  const now = Date.now();
153062
- 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());
153140
+ 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);
153063
153141
  if (!isNoteRow(result)) {
153064
153142
  throw new Error("[notes] failed to insert note");
153065
153143
  }
@@ -164322,8 +164400,130 @@ var init_storage_git_commit_embeddings = __esm(() => {
164322
164400
  distinctModelIdStatements = new WeakMap;
164323
164401
  });
164324
164402
 
164403
+ // src/features/magic-context/git-commits/sweep-coordinator.ts
164404
+ function runImmediate2(db, body) {
164405
+ db.exec("BEGIN IMMEDIATE");
164406
+ let committed = false;
164407
+ try {
164408
+ const result = body();
164409
+ db.exec("COMMIT");
164410
+ committed = true;
164411
+ return result;
164412
+ } finally {
164413
+ if (!committed) {
164414
+ try {
164415
+ db.exec("ROLLBACK");
164416
+ } catch {}
164417
+ }
164418
+ }
164419
+ }
164420
+ function rowToState(row) {
164421
+ return {
164422
+ projectPath: row.project_path,
164423
+ leaseHolder: row.lease_holder,
164424
+ leaseExpiresAt: row.lease_expires_at,
164425
+ lastSweptAt: row.last_swept_at
164426
+ };
164427
+ }
164428
+ function getGitSweepCoordinatorState(db, projectPath) {
164429
+ const row = db.prepare(`SELECT project_path, lease_holder, lease_expires_at, last_swept_at
164430
+ FROM git_sweep_coordinator
164431
+ WHERE project_path = ?`).get(projectPath);
164432
+ return row ? rowToState(row) : null;
164433
+ }
164434
+ function acquireGitSweepLease(db, projectPath, holderId, options = {}) {
164435
+ const cooldownMs = options.cooldownMs ?? GIT_SWEEP_COOLDOWN_MS;
164436
+ const leaseTtlMs = options.leaseTtlMs ?? GIT_SWEEP_LEASE_TTL_MS;
164437
+ return runImmediate2(db, () => {
164438
+ const now = Date.now();
164439
+ const row = getGitSweepCoordinatorState(db, projectPath);
164440
+ if (row?.leaseHolder && row.leaseExpiresAt !== null && row.leaseExpiresAt > now) {
164441
+ return {
164442
+ acquired: false,
164443
+ projectPath,
164444
+ reason: "lease_active",
164445
+ leaseHolder: row.leaseHolder,
164446
+ leaseExpiresAt: row.leaseExpiresAt,
164447
+ lastSweptAt: row.lastSweptAt,
164448
+ nextAllowedAt: null
164449
+ };
164450
+ }
164451
+ if (!options.ignoreCooldown && row?.lastSweptAt !== null && row?.lastSweptAt !== undefined) {
164452
+ const nextAllowedAt = row.lastSweptAt + cooldownMs;
164453
+ if (nextAllowedAt > now) {
164454
+ return {
164455
+ acquired: false,
164456
+ projectPath,
164457
+ reason: "cooldown_active",
164458
+ leaseHolder: row.leaseHolder,
164459
+ leaseExpiresAt: row.leaseExpiresAt,
164460
+ lastSweptAt: row.lastSweptAt,
164461
+ nextAllowedAt
164462
+ };
164463
+ }
164464
+ }
164465
+ const leaseExpiresAt = now + leaseTtlMs;
164466
+ db.prepare(`INSERT INTO git_sweep_coordinator (
164467
+ project_path,
164468
+ lease_holder,
164469
+ lease_expires_at,
164470
+ last_swept_at
164471
+ ) VALUES (?, ?, ?, NULL)
164472
+ ON CONFLICT(project_path) DO UPDATE SET
164473
+ lease_holder = excluded.lease_holder,
164474
+ lease_expires_at = excluded.lease_expires_at`).run(projectPath, holderId, leaseExpiresAt);
164475
+ return {
164476
+ acquired: true,
164477
+ projectPath,
164478
+ holderId,
164479
+ acquiredAt: now,
164480
+ leaseExpiresAt
164481
+ };
164482
+ });
164483
+ }
164484
+ function renewGitSweepLease(db, projectPath, holderId, leaseTtlMs = GIT_SWEEP_LEASE_TTL_MS) {
164485
+ return runImmediate2(db, () => {
164486
+ const now = Date.now();
164487
+ const leaseExpiresAt = now + leaseTtlMs;
164488
+ const result = db.prepare(`UPDATE git_sweep_coordinator
164489
+ SET lease_expires_at = ?
164490
+ WHERE project_path = ?
164491
+ AND lease_holder = ?
164492
+ AND lease_expires_at > ?`).run(leaseExpiresAt, projectPath, holderId, now);
164493
+ return result.changes === 1;
164494
+ });
164495
+ }
164496
+ function markGitSweepSuccessAndRelease(db, projectPath, holderId) {
164497
+ return runImmediate2(db, () => {
164498
+ const now = Date.now();
164499
+ const result = db.prepare(`UPDATE git_sweep_coordinator
164500
+ SET lease_holder = NULL,
164501
+ lease_expires_at = NULL,
164502
+ last_swept_at = ?
164503
+ WHERE project_path = ?
164504
+ AND lease_holder = ?
164505
+ AND lease_expires_at > ?`).run(now, projectPath, holderId, now);
164506
+ return result.changes === 1;
164507
+ });
164508
+ }
164509
+ function releaseGitSweepLease(db, projectPath, holderId) {
164510
+ runImmediate2(db, () => {
164511
+ db.prepare(`UPDATE git_sweep_coordinator
164512
+ SET lease_holder = NULL,
164513
+ lease_expires_at = NULL
164514
+ WHERE project_path = ?
164515
+ AND lease_holder = ?`).run(projectPath, holderId);
164516
+ });
164517
+ }
164518
+ var GIT_SWEEP_COOLDOWN_MS, GIT_SWEEP_LEASE_TTL_MS, GIT_SWEEP_LEASE_RENEWAL_MS;
164519
+ var init_sweep_coordinator = __esm(() => {
164520
+ GIT_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
164521
+ GIT_SWEEP_LEASE_TTL_MS = 5 * 60 * 1000;
164522
+ GIT_SWEEP_LEASE_RENEWAL_MS = 60 * 1000;
164523
+ });
164524
+
164325
164525
  // src/features/magic-context/project-embedding-registry.ts
164326
- import { createHash as createHash7 } from "node:crypto";
164526
+ import { createHash as createHash7, randomUUID } from "node:crypto";
164327
164527
  function resolveEmbeddingConfig(config2) {
164328
164528
  if (!config2 || config2.provider === "local") {
164329
164529
  return {
@@ -164587,6 +164787,7 @@ var init_project_embedding_registry = __esm(() => {
164587
164787
  init_magic_context();
164588
164788
  init_logger();
164589
164789
  init_storage_git_commit_embeddings();
164790
+ init_sweep_coordinator();
164590
164791
  init_embedding_cache();
164591
164792
  init_embedding_identity();
164592
164793
  init_embedding_local();
@@ -164685,32 +164886,48 @@ var init_storage_memory_fts = __esm(() => {
164685
164886
  });
164686
164887
 
164687
164888
  // src/shared/models-dev-cache.ts
164688
- import { createHash as createHash9 } from "node:crypto";
164689
- import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
164690
- import { homedir as homedir7, platform as platform2 } from "node:os";
164889
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync9, renameSync, writeFileSync } from "node:fs";
164691
164890
  import { join as join14 } from "node:path";
164692
- function hashFast(input) {
164693
- return createHash9("sha1").update(input).digest("hex");
164694
- }
164695
- function getModelsJsonPath() {
164696
- const explicit = process.env.OPENCODE_MODELS_PATH?.trim();
164697
- if (explicit)
164698
- return explicit;
164699
- const cacheBase = getCacheDir();
164700
- const source = process.env.OPENCODE_MODELS_URL?.trim();
164701
- const filename = source && source !== "https://models.dev" ? `models-${hashFast(source)}.json` : "models.json";
164702
- return join14(cacheBase, "opencode", filename);
164703
- }
164704
- function getOpencodeConfigPath() {
164705
- const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
164706
- const configDir = envDir ? envDir : platform2() === "win32" ? join14(homedir7(), ".config", "opencode") : join14(process.env.XDG_CONFIG_HOME || join14(homedir7(), ".config"), "opencode");
164707
- const jsonc = join14(configDir, "opencode.jsonc");
164708
- if (existsSync11(jsonc))
164709
- return jsonc;
164710
- const json2 = join14(configDir, "opencode.json");
164711
- if (existsSync11(json2))
164712
- return json2;
164713
- return null;
164891
+ function isSaneLimit(limit) {
164892
+ return typeof limit === "number" && limit >= MIN_SANE_LIMIT && limit <= MAX_SANE_LIMIT;
164893
+ }
164894
+ function persistFilePath() {
164895
+ return join14(getMagicContextStorageDir(), `model-context-limits-${getHarness()}.json`);
164896
+ }
164897
+ function loadPersistedApiCacheOnce() {
164898
+ if (persistSeedLoaded || apiCache !== null)
164899
+ return;
164900
+ persistSeedLoaded = true;
164901
+ try {
164902
+ const raw = readFileSync9(persistFilePath(), "utf-8");
164903
+ const obj = JSON.parse(raw);
164904
+ const map2 = new Map;
164905
+ for (const [key, limit] of Object.entries(obj)) {
164906
+ if (isSaneLimit(limit))
164907
+ map2.set(key, { limit });
164908
+ }
164909
+ if (map2.size > 0) {
164910
+ apiCache = map2;
164911
+ sessionLog("global", `models-dev-cache: seeded ${map2.size} entries from persisted cache (cold start)`);
164912
+ }
164913
+ } catch {}
164914
+ }
164915
+ function persistApiCache() {
164916
+ if (!apiCache)
164917
+ return;
164918
+ const obj = {};
164919
+ for (const [key, value] of apiCache) {
164920
+ if (isSaneLimit(value.limit))
164921
+ obj[key] = value.limit;
164922
+ }
164923
+ try {
164924
+ const dir = getMagicContextStorageDir();
164925
+ mkdirSync4(dir, { recursive: true });
164926
+ const target = persistFilePath();
164927
+ const tmp = `${target}.${process.pid}.tmp`;
164928
+ writeFileSync(tmp, JSON.stringify(obj), { encoding: "utf-8", mode: 384 });
164929
+ renameSync(tmp, target);
164930
+ } catch {}
164714
164931
  }
164715
164932
  function resolveLimit(limit) {
164716
164933
  if (!limit)
@@ -164723,7 +164940,7 @@ function resolveLimit(limit) {
164723
164940
  }
164724
164941
  function setCachedModelMetadata(cache, key, model) {
164725
164942
  const limit = resolveLimit(model?.limit);
164726
- if (limit === undefined) {
164943
+ if (!isSaneLimit(limit)) {
164727
164944
  return;
164728
164945
  }
164729
164946
  const value = { limit };
@@ -164735,54 +164952,26 @@ function setCachedModelMetadata(cache, key, model) {
164735
164952
  }
164736
164953
  }
164737
164954
  }
164738
- function loadModelsDevMetadataFromFile() {
164739
- const metadata = new Map;
164740
- const modelsJsonPath = getModelsJsonPath();
164741
- let fileFound = false;
164742
- try {
164743
- if (existsSync11(modelsJsonPath)) {
164744
- fileFound = true;
164745
- const raw = readFileSync9(modelsJsonPath, "utf-8");
164746
- const data = JSON.parse(raw);
164747
- for (const [providerId, provider2] of Object.entries(data)) {
164748
- if (!provider2?.models || typeof provider2.models !== "object")
164749
- continue;
164750
- for (const [modelId, model] of Object.entries(provider2.models)) {
164751
- setCachedModelMetadata(metadata, `${providerId}/${modelId}`, model);
164752
- }
164753
- }
164754
- }
164755
- } catch (error51) {
164756
- sessionLog("global", `models-dev-cache: failed to read models.json at ${modelsJsonPath}:`, error51 instanceof Error ? error51.message : String(error51));
164757
- }
164758
- try {
164759
- const configPath = getOpencodeConfigPath();
164760
- if (configPath && existsSync11(configPath)) {
164761
- const config2 = parseJsonc(readFileSync9(configPath, "utf-8"));
164762
- if (config2.provider && typeof config2.provider === "object") {
164763
- for (const [providerId, provider2] of Object.entries(config2.provider)) {
164764
- if (!provider2?.models || typeof provider2.models !== "object")
164765
- continue;
164766
- for (const [modelId, model] of Object.entries(provider2.models)) {
164767
- setCachedModelMetadata(metadata, `${providerId}/${modelId}`, model);
164768
- }
164769
- }
164770
- }
164955
+ async function refreshModelLimitsFromApi(client, options) {
164956
+ const attempts = Math.max(1, (options?.retries ?? 0) + 1);
164957
+ const delayMs = options?.retryDelayMs ?? 1000;
164958
+ for (let attempt = 1;attempt <= attempts; attempt++) {
164959
+ const ok = await refreshModelLimitsOnce(client);
164960
+ if (ok)
164961
+ return;
164962
+ if (attempt < attempts) {
164963
+ await new Promise((resolve5) => setTimeout(resolve5, delayMs));
164771
164964
  }
164772
- } catch (error51) {
164773
- sessionLog("global", "models-dev-cache: failed to read opencode config for custom models:", error51 instanceof Error ? error51.message : String(error51));
164774
164965
  }
164775
- sessionLog("global", `models-dev-cache: file-layer loaded ${metadata.size} model metadata entries (modelsJsonPath=${modelsJsonPath}, found=${fileFound})`);
164776
- return metadata;
164777
164966
  }
164778
- async function refreshModelLimitsFromApi(client) {
164967
+ async function refreshModelLimitsOnce(client) {
164779
164968
  try {
164780
164969
  const result = await client.config.providers();
164781
164970
  const data = result.data;
164782
164971
  const providers = data?.providers;
164783
- if (!Array.isArray(providers)) {
164784
- sessionLog("global", "models-dev-cache: API refresh returned no providers payload");
164785
- return;
164972
+ if (!Array.isArray(providers) || providers.length === 0) {
164973
+ sessionLog("global", "models-dev-cache: API refresh returned no providers payload (will retry if attempts remain)");
164974
+ return false;
164786
164975
  }
164787
164976
  const map2 = new Map;
164788
164977
  for (const entry of providers) {
@@ -164796,27 +164985,22 @@ async function refreshModelLimitsFromApi(client) {
164796
164985
  const previousSize = apiCache?.size ?? null;
164797
164986
  apiCache = map2;
164798
164987
  apiLoadedAt = Date.now();
164988
+ persistApiCache();
164799
164989
  if (previousSize === null) {
164800
164990
  sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model metadata entries`);
164801
164991
  } else if (previousSize !== map2.size) {
164802
164992
  sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model metadata entries (was ${previousSize})`);
164803
164993
  }
164994
+ return true;
164804
164995
  } catch (error51) {
164805
164996
  sessionLog("global", "models-dev-cache: API refresh failed:", error51 instanceof Error ? error51.message : String(error51));
164997
+ return false;
164806
164998
  }
164807
164999
  }
164808
- function getModelsDevContextLimit(providerID, modelID) {
164809
- const now = Date.now();
164810
- if (!fileCache || now - fileLastAttempt > RELOAD_INTERVAL_MS) {
164811
- fileLastAttempt = now;
164812
- fileCache = loadModelsDevMetadataFromFile();
164813
- }
165000
+ function getSdkContextLimit(providerID, modelID) {
165001
+ loadPersistedApiCacheOnce();
164814
165002
  const fromApi = lookupLimitWithTagFallback(apiCache, providerID, modelID);
164815
- const fromFile = lookupLimitWithTagFallback(fileCache, providerID, modelID);
164816
- if (typeof fromApi === "number" && typeof fromFile === "number") {
164817
- return Math.max(fromApi, fromFile);
164818
- }
164819
- return fromApi ?? fromFile;
165003
+ return isSaneLimit(fromApi) ? fromApi : undefined;
164820
165004
  }
164821
165005
  function lookupLimitWithTagFallback(cache, providerID, modelID) {
164822
165006
  if (!cache)
@@ -164833,12 +165017,10 @@ function lookupLimitWithTagFallback(cache, providerID, modelID) {
164833
165017
  }
164834
165018
  return;
164835
165019
  }
164836
- var RELOAD_INTERVAL_MS, apiCache = null, apiLoadedAt = 0, fileCache = null, fileLastAttempt = 0;
165020
+ var MIN_SANE_LIMIT = 20000, MAX_SANE_LIMIT = 3000000, apiCache = null, apiLoadedAt = 0, persistSeedLoaded = false;
164837
165021
  var init_models_dev_cache = __esm(() => {
164838
165022
  init_data_path();
164839
- init_jsonc_parser();
164840
165023
  init_logger();
164841
- RELOAD_INTERVAL_MS = 5 * 60 * 1000;
164842
165024
  });
164843
165025
 
164844
165026
  // src/shared/rpc-notifications.ts
@@ -165564,7 +165746,7 @@ var init_compartment_runner_validation = __esm(async () => {
165564
165746
  });
165565
165747
 
165566
165748
  // src/hooks/magic-context/compartment-runner-historian.ts
165567
- import { mkdirSync as mkdirSync4, unlinkSync, writeFileSync } from "node:fs";
165749
+ import { mkdirSync as mkdirSync5, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
165568
165750
  import { join as join17 } from "node:path";
165569
165751
  function historianResponseDumpDir(directory) {
165570
165752
  return getProjectMagicContextHistorianDir(directory);
@@ -165867,11 +166049,11 @@ function cleanupHistorianDump(sessionId, dumpPath) {
165867
166049
  function dumpHistorianResponse(sessionId, directory, label, text) {
165868
166050
  try {
165869
166051
  const dumpDir = historianResponseDumpDir(directory);
165870
- mkdirSync4(dumpDir, { recursive: true });
166052
+ mkdirSync5(dumpDir, { recursive: true });
165871
166053
  const safeSessionId = sanitizeDumpName(sessionId);
165872
166054
  const safeLabel = sanitizeDumpName(label);
165873
166055
  const dumpPath = join17(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
165874
- writeFileSync(dumpPath, text, "utf8");
166056
+ writeFileSync2(dumpPath, text, "utf8");
165875
166057
  sessionLog(sessionId, "compartment agent: historian response dumped", {
165876
166058
  label,
165877
166059
  dumpPath
@@ -165993,7 +166175,7 @@ function insertCompartmentEvents(db, sessionId, events, compartmentIds) {
165993
166175
  var init_compartment_events = () => {};
165994
166176
 
165995
166177
  // src/hooks/magic-context/historian-state-file.ts
165996
- import { mkdirSync as mkdirSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
166178
+ import { mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
165997
166179
  function cleanupHistorianStateFile(path6) {
165998
166180
  if (!path6)
165999
166181
  return;
@@ -166548,9 +166730,7 @@ function injectTemporalMarkers(messages) {
166548
166730
  if (prefix && Array.isArray(msg.parts)) {
166549
166731
  const target = findFirstVisibleTextPart(msg.parts);
166550
166732
  if (target && typeof target.text === "string") {
166551
- const tagMatch = target.text.match(/^(?:§\d+§\s*)+/);
166552
- const tagPrefix = tagMatch ? tagMatch[0] : "";
166553
- const body = target.text.slice(tagPrefix.length);
166733
+ const { tagPrefix, body } = peelLeadingMcTagNotation(target.text);
166554
166734
  if (!TEMPORAL_MARKER_PATTERN.test(body)) {
166555
166735
  target.text = tagPrefix + prefix + body;
166556
166736
  injected++;
@@ -166565,6 +166745,7 @@ function injectTemporalMarkers(messages) {
166565
166745
  }
166566
166746
  var TEMPORAL_AWARENESS_THRESHOLD_SECONDS = 300, SECONDS_PER_MINUTE = 60, SECONDS_PER_HOUR, SECONDS_PER_DAY, SECONDS_PER_WEEK, TEMPORAL_MARKER_PATTERN;
166567
166747
  var init_temporal_awareness = __esm(() => {
166748
+ init_tag_content_primitives();
166568
166749
  SECONDS_PER_HOUR = 60 * 60;
166569
166750
  SECONDS_PER_DAY = 24 * 60 * 60;
166570
166751
  SECONDS_PER_WEEK = 7 * 24 * 60 * 60;
@@ -170829,7 +171010,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
170829
171010
  const [providerID, ...rest] = historianModelOverride.split("/");
170830
171011
  const modelID = rest.join("/");
170831
171012
  if (providerID && modelID) {
170832
- const limit = getModelsDevContextLimit(providerID, modelID);
171013
+ const limit = getSdkContextLimit(providerID, modelID);
170833
171014
  if (typeof limit === "number" && limit > 0)
170834
171015
  return limit;
170835
171016
  }
@@ -170848,7 +171029,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
170848
171029
  const modelID = rest.join("/");
170849
171030
  if (!providerID || !modelID)
170850
171031
  continue;
170851
- const limit = getModelsDevContextLimit(providerID, modelID);
171032
+ const limit = getSdkContextLimit(providerID, modelID);
170852
171033
  if (typeof limit !== "number" || limit <= 0)
170853
171034
  continue;
170854
171035
  if (minLimit === undefined || limit < minLimit)
@@ -171215,6 +171396,7 @@ __export(exports_recomp_orchestrator, {
171215
171396
  setRecompNote: () => setRecompNote,
171216
171397
  runManagedUpgrade: () => runManagedUpgrade,
171217
171398
  runManagedRecomp: () => runManagedRecomp,
171399
+ isRecompSkip: () => isRecompSkip,
171218
171400
  isRecompFailure: () => isRecompFailure,
171219
171401
  isRecompComplete: () => isRecompComplete,
171220
171402
  extractRecompReason: () => extractRecompReason,
@@ -171227,6 +171409,9 @@ function resolveLiveModelKey(liveSessionState, sessionId) {
171227
171409
  function isRecompFailure(message) {
171228
171410
  return /—\s*(Failed|Skipped)/.test(message);
171229
171411
  }
171412
+ function isRecompSkip(message) {
171413
+ return /—\s*Skipped|already mutating compartment state|already running/i.test(message);
171414
+ }
171230
171415
  function isRecompComplete(message) {
171231
171416
  return /—\s*Complete/.test(message);
171232
171417
  }
@@ -171242,9 +171427,10 @@ function contextualizeUpgradeReason(reason) {
171242
171427
  }
171243
171428
  return rewritten;
171244
171429
  }
171245
- function setRecompStarting(liveSessionState, sessionId, note) {
171430
+ function setRecompStarting(liveSessionState, sessionId, note, kind = "recomp") {
171246
171431
  liveSessionState.recompProgressBySession.set(sessionId, {
171247
171432
  sessionId,
171433
+ kind,
171248
171434
  phase: "recomp",
171249
171435
  processedMessages: 0,
171250
171436
  totalMessages: 0,
@@ -171269,6 +171455,7 @@ function setRecompTerminal(liveSessionState, sessionId, phase, message) {
171269
171455
  const existing = liveSessionState.recompProgressBySession.get(sessionId);
171270
171456
  liveSessionState.recompProgressBySession.set(sessionId, {
171271
171457
  sessionId,
171458
+ kind: existing?.kind ?? "recomp",
171272
171459
  phase,
171273
171460
  processedMessages: existing?.processedMessages ?? 0,
171274
171461
  totalMessages: existing?.totalMessages ?? 0,
@@ -171278,10 +171465,10 @@ function setRecompTerminal(liveSessionState, sessionId, phase, message) {
171278
171465
  updatedAt: Date.now(),
171279
171466
  message
171280
171467
  });
171281
- if (phase === "done") {
171468
+ if (phase === "done" || phase === "skipped") {
171282
171469
  const t = setTimeout(() => {
171283
171470
  const cur = liveSessionState.recompProgressBySession.get(sessionId);
171284
- if (cur?.phase === "done")
171471
+ if (cur?.phase === phase)
171285
171472
  liveSessionState.recompProgressBySession.delete(sessionId);
171286
171473
  }, RECOMP_DONE_GRACE_MS);
171287
171474
  t.unref?.();
@@ -171310,7 +171497,11 @@ function buildRecompDeps(ctx, sessionId) {
171310
171497
  ctx.liveSessionState.deferredHistoryRefreshSessions.add(sid);
171311
171498
  },
171312
171499
  onRecompProgress: (p) => {
171313
- ctx.liveSessionState.recompProgressBySession.set(sessionId, p);
171500
+ const prevKind = ctx.liveSessionState.recompProgressBySession.get(sessionId)?.kind ?? "recomp";
171501
+ ctx.liveSessionState.recompProgressBySession.set(sessionId, {
171502
+ ...p,
171503
+ kind: p.kind ?? prevKind
171504
+ });
171314
171505
  }
171315
171506
  };
171316
171507
  }
@@ -171329,10 +171520,11 @@ async function resolveSessionDirectory(ctx, sessionId) {
171329
171520
  return ctx.directory;
171330
171521
  }
171331
171522
  async function runManagedRecomp(ctx, sessionId, options) {
171332
- setRecompStarting(ctx.liveSessionState, sessionId, "Starting recomp…");
171523
+ setRecompStarting(ctx.liveSessionState, sessionId, "Starting recomp…", "recomp");
171333
171524
  try {
171334
171525
  const message = await executeContextRecomp(buildRecompDeps(ctx, sessionId), options);
171335
- setRecompTerminal(ctx.liveSessionState, sessionId, isRecompFailure(message) ? "failed" : "done", extractRecompReason(message));
171526
+ const terminalPhase = isRecompSkip(message) ? "skipped" : isRecompFailure(message) ? "failed" : "done";
171527
+ setRecompTerminal(ctx.liveSessionState, sessionId, terminalPhase, extractRecompReason(message));
171336
171528
  return message;
171337
171529
  } catch (error51) {
171338
171530
  setRecompTerminal(ctx.liveSessionState, sessionId, "failed", `Recomp crashed: ${String(error51)}`);
@@ -171342,7 +171534,7 @@ Recomp crashed: ${String(error51)}`;
171342
171534
  }
171343
171535
  }
171344
171536
  async function runManagedUpgrade(ctx, sessionId) {
171345
- setRecompStarting(ctx.liveSessionState, sessionId, "Starting upgrade…");
171537
+ setRecompStarting(ctx.liveSessionState, sessionId, "Starting upgrade…", "upgrade");
171346
171538
  try {
171347
171539
  const compartments = getCompartments(ctx.db, sessionId);
171348
171540
  const legacyCount = compartments.filter((c) => c.legacy === 1 || !c.p1 || c.p1.trim() === "").length;
@@ -171400,6 +171592,7 @@ async function runUpgradeMemoryMigration(ctx, sessionId, migrationDirectory) {
171400
171592
  const prev = ctx.liveSessionState.recompProgressBySession.get(sessionId);
171401
171593
  ctx.liveSessionState.recompProgressBySession.set(sessionId, {
171402
171594
  sessionId,
171595
+ kind: prev?.kind ?? "upgrade",
171403
171596
  phase: "migration",
171404
171597
  processedMessages: prev?.processedMessages ?? 0,
171405
171598
  totalMessages: prev?.totalMessages ?? 0,
@@ -172796,7 +172989,8 @@ function createLiveSessionState() {
172796
172989
  pendingMaterializationSessions: new Set,
172797
172990
  deferredMaterializationSessions: new Set,
172798
172991
  sessionDirectoryBySession: new Map,
172799
- recompProgressBySession: new Map
172992
+ recompProgressBySession: new Map,
172993
+ internalChildSessions: new Set
172800
172994
  };
172801
172995
  }
172802
172996
 
@@ -174413,7 +174607,7 @@ async function processDreamQueue(args) {
174413
174607
  return null;
174414
174608
  }
174415
174609
  const projectDirectory = args.sessionDirectoryOverride ?? resolveDreamSessionDirectory(entry.projectIdentity);
174416
- log(`[dreamer] dequeued project ${entry.projectIdentity} (dir=${projectDirectory}), starting dream run`);
174610
+ log(`[dreamer] dequeued project ${entry.projectIdentity}, starting dream run`);
174417
174611
  let result;
174418
174612
  try {
174419
174613
  result = await runDream({
@@ -174537,6 +174731,7 @@ async function readGitCommits(directory, options = {}) {
174537
174731
  if (revision.startsWith("-")) {
174538
174732
  throw new Error(`readGitCommits: refusing revision that looks like an option: "${revision}"`);
174539
174733
  }
174734
+ const projectLabel = options.projectIdentity ?? "<project>";
174540
174735
  const args = [
174541
174736
  "log",
174542
174737
  revision,
@@ -174559,11 +174754,11 @@ async function readGitCommits(directory, options = {}) {
174559
174754
  stdout = result.stdout;
174560
174755
  } catch (error51) {
174561
174756
  const message = error51 instanceof Error ? error51.message : String(error51);
174562
- log(`[git-commits] readGitCommits failed at cwd=${directory}: ${message.slice(0, 500)}`);
174757
+ log(`[git-commits] readGitCommits failed for ${projectLabel}: ${message.slice(0, 500)}`);
174563
174758
  return [];
174564
174759
  }
174565
174760
  if (stdout.trim().length === 0) {
174566
- log(`[git-commits] readGitCommits returned empty stdout at cwd=${directory} (sinceMs=${options.sinceMs ?? "none"} args=${args.slice(0, 4).join(" ")})`);
174761
+ log(`[git-commits] readGitCommits returned empty stdout for ${projectLabel} (sinceMs=${options.sinceMs ?? "none"} args=${args.slice(0, 4).join(" ")})`);
174567
174762
  }
174568
174763
  return parseGitLogOutput(stdout);
174569
174764
  }
@@ -174618,6 +174813,7 @@ var insertStatements = new WeakMap;
174618
174813
  var existingShasStatements = new WeakMap;
174619
174814
  var projectCountStatements = new WeakMap;
174620
174815
  var evictStatements = new WeakMap;
174816
+ var evictOverflowStatements = new WeakMap;
174621
174817
  var latestCommitTimeStatements = new WeakMap;
174622
174818
  function getInsertStatement(db) {
174623
174819
  let stmt = insertStatements.get(db);
@@ -174660,17 +174856,17 @@ function getLatestCommitTimeStatement(db) {
174660
174856
  }
174661
174857
  return stmt;
174662
174858
  }
174663
- function getEvictStatement(db) {
174664
- let stmt = evictStatements.get(db);
174859
+ function getEvictOverflowStatement(db) {
174860
+ let stmt = evictOverflowStatements.get(db);
174665
174861
  if (!stmt) {
174666
174862
  stmt = db.prepare(`DELETE FROM git_commits
174667
- WHERE sha IN (
174668
- SELECT sha FROM git_commits
174863
+ WHERE rowid IN (
174864
+ SELECT rowid FROM git_commits
174669
174865
  WHERE project_path = ?
174670
- ORDER BY committed_at ASC
174671
- LIMIT ?
174866
+ ORDER BY committed_at DESC, sha DESC
174867
+ LIMIT -1 OFFSET ?
174672
174868
  )`);
174673
- evictStatements.set(db, stmt);
174869
+ evictOverflowStatements.set(db, stmt);
174674
174870
  }
174675
174871
  return stmt;
174676
174872
  }
@@ -174708,22 +174904,15 @@ function getLatestIndexedCommitTimeMs(db, projectPath) {
174708
174904
  const row = getLatestCommitTimeStatement(db).get(projectPath);
174709
174905
  return row?.latest ?? null;
174710
174906
  }
174711
- function evictOldestCommits(db, projectPath, excess) {
174712
- if (excess <= 0)
174713
- return 0;
174714
- const before = getCommitCount(db, projectPath);
174715
- getEvictStatement(db).run(projectPath, excess);
174716
- const after = getCommitCount(db, projectPath);
174717
- return Math.max(0, before - after);
174718
- }
174719
174907
  function enforceProjectCap(db, projectPath, maxCommits) {
174720
174908
  if (maxCommits <= 0)
174721
174909
  return 0;
174722
174910
  const count = getCommitCount(db, projectPath);
174723
174911
  if (count <= maxCommits)
174724
174912
  return 0;
174725
- const excess = count - maxCommits;
174726
- const evicted = evictOldestCommits(db, projectPath, excess);
174913
+ getEvictOverflowStatement(db).run(projectPath, maxCommits);
174914
+ const after = getCommitCount(db, projectPath);
174915
+ const evicted = Math.max(0, count - after);
174727
174916
  if (evicted > 0) {
174728
174917
  log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
174729
174918
  }
@@ -174755,7 +174944,8 @@ async function indexCommitsForProject(db, projectPath, directory, options) {
174755
174944
  const sinceMs = latestIndexed !== null ? Math.max(latestIndexed - 60000, Date.now() - options.sinceDays * MS_PER_DAY) : Date.now() - options.sinceDays * MS_PER_DAY;
174756
174945
  const commits = await readGitCommits(directory, {
174757
174946
  sinceMs,
174758
- maxCommits: options.maxCommits
174947
+ maxCommits: options.maxCommits,
174948
+ projectIdentity: projectPath
174759
174949
  });
174760
174950
  result.scanned = commits.length;
174761
174951
  if (commits.length === 0) {
@@ -174967,6 +175157,7 @@ function searchGitCommitsSync(db, projectPath, query, options) {
174967
175157
 
174968
175158
  // src/features/magic-context/git-commits/index.ts
174969
175159
  init_storage_git_commit_embeddings();
175160
+ init_sweep_coordinator();
174970
175161
 
174971
175162
  // src/plugin/dream-timer.ts
174972
175163
  init_embedding();
@@ -174999,7 +175190,7 @@ async function startDreamScheduleTimer(args) {
174999
175190
  const isNewRegistration = !registeredProjects.has(args.directory);
175000
175191
  registeredProjects.set(args.directory, args);
175001
175192
  if (isNewRegistration) {
175002
- log(`[dreamer] registered project ${args.directory} (dreaming=${dreamingEnabled} embeddings=${embeddingSweepEnabled} commits=${commitIndexingEnabled}; total=${registeredProjects.size})`);
175193
+ log(`[dreamer] registered project ${args.projectIdentity} (dreaming=${dreamingEnabled} embeddings=${embeddingSweepEnabled} commits=${commitIndexingEnabled}; total=${registeredProjects.size})`);
175003
175194
  }
175004
175195
  if (!activeTimer) {
175005
175196
  log(`[dreamer] started independent schedule timer (every ${DREAM_TIMER_INTERVAL_MS / 60000}m)`);
@@ -175014,7 +175205,7 @@ async function startDreamScheduleTimer(args) {
175014
175205
  }
175015
175206
  return () => {
175016
175207
  registeredProjects.delete(args.directory);
175017
- log(`[dreamer] unregistered project ${args.directory} (remaining=${registeredProjects.size})`);
175208
+ log(`[dreamer] unregistered project ${args.projectIdentity} (remaining=${registeredProjects.size})`);
175018
175209
  if (registeredProjects.size === 0 && activeTimer) {
175019
175210
  clearInterval(activeTimer);
175020
175211
  activeTimer = null;
@@ -175062,7 +175253,7 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
175062
175253
  return;
175063
175254
  }
175064
175255
  try {
175065
- log(`[dreamer] timer tick (${origin}) ${reg.directory} — checking schedule window "${reg.dreamerConfig.schedule}"`);
175256
+ log(`[dreamer] timer tick (${origin}) ${reg.projectIdentity} — checking schedule window "${reg.dreamerConfig.schedule}"`);
175066
175257
  checkScheduleAndEnqueue(db, reg.dreamerConfig.schedule, reg.projectIdentity);
175067
175258
  await processDreamQueue({
175068
175259
  db,
@@ -175077,13 +175268,34 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
175077
175268
  fallbackModels: resolveFallbackChain(DREAMER_AGENT, reg.dreamerConfig.fallback_models)
175078
175269
  });
175079
175270
  } catch (error51) {
175080
- log(`[dreamer] timer-triggered queue processing failed for ${reg.directory}:`, error51);
175271
+ log(`[dreamer] timer-triggered queue processing failed for ${reg.projectIdentity}:`, error51);
175081
175272
  }
175082
175273
  }
175274
+ function startGitSweepLeaseRenewal(db, projectIdentity, holderId) {
175275
+ const timer = setInterval(() => {
175276
+ try {
175277
+ if (!renewGitSweepLease(db, projectIdentity, holderId)) {
175278
+ log(`[git-commits] sweep lease renewal failed for ${projectIdentity}`);
175279
+ }
175280
+ } catch (error51) {
175281
+ log(`[git-commits] sweep lease renewal errored for ${projectIdentity}: ${error51 instanceof Error ? error51.message : String(error51)}`);
175282
+ }
175283
+ }, GIT_SWEEP_LEASE_RENEWAL_MS);
175284
+ timer.unref?.();
175285
+ return () => clearInterval(timer);
175286
+ }
175083
175287
  async function sweepGitCommits(args) {
175084
175288
  const { directory, projectIdentity, db, gitCommitIndexing } = args;
175289
+ const holderId = crypto.randomUUID();
175290
+ const lease2 = acquireGitSweepLease(db, projectIdentity, holderId);
175291
+ if (!lease2.acquired) {
175292
+ const reason = lease2.reason === "cooldown_active" ? `cooldown active until ${lease2.nextAllowedAt}` : `lease held by ${lease2.leaseHolder ?? "another holder"} until ${lease2.leaseExpiresAt ?? "unknown"}`;
175293
+ log(`[git-commits] sweep skipped for ${projectIdentity}: ${reason}`);
175294
+ return;
175295
+ }
175085
175296
  const startedAt = Date.now();
175086
- log(`[git-commits] sweep starting for ${directory} (sinceDays=${gitCommitIndexing.since_days} maxCommits=${gitCommitIndexing.max_commits})`);
175297
+ const stopRenewal = startGitSweepLeaseRenewal(db, projectIdentity, holderId);
175298
+ log(`[git-commits] sweep starting for ${projectIdentity} (sinceDays=${gitCommitIndexing.since_days} maxCommits=${gitCommitIndexing.max_commits})`);
175087
175299
  try {
175088
175300
  const result = await indexCommitsForProject(db, projectIdentity, directory, {
175089
175301
  sinceDays: gitCommitIndexing.since_days,
@@ -175093,11 +175305,19 @@ async function sweepGitCommits(args) {
175093
175305
  if (result.embedded > 0) {
175094
175306
  drainedEmbeddings = await embedUnembeddedCommits(db, projectIdentity);
175095
175307
  }
175308
+ const cooldownMarked = markGitSweepSuccessAndRelease(db, projectIdentity, holderId);
175309
+ if (!cooldownMarked) {
175310
+ releaseGitSweepLease(db, projectIdentity, holderId);
175311
+ log(`[git-commits] sweep finished for ${projectIdentity}, but lease was no longer active; cooldown not advanced`);
175312
+ }
175096
175313
  const elapsedMs = Date.now() - startedAt;
175097
175314
  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}`);
175098
175315
  } catch (error51) {
175316
+ releaseGitSweepLease(db, projectIdentity, holderId);
175099
175317
  const elapsedMs = Date.now() - startedAt;
175100
- log(`[git-commits] sweep failed for ${directory} after ${elapsedMs}ms: ${error51 instanceof Error ? error51.message : String(error51)}`);
175318
+ log(`[git-commits] sweep failed for ${projectIdentity} after ${elapsedMs}ms: ${error51 instanceof Error ? error51.message : String(error51)}`);
175319
+ } finally {
175320
+ stopRenewal();
175101
175321
  }
175102
175322
  }
175103
175323
 
@@ -175237,7 +175457,7 @@ init_models_dev_cache();
175237
175457
  var DEFAULT_CONTEXT_LIMIT = 128000;
175238
175458
  var MAX_EXECUTE_THRESHOLD = 80;
175239
175459
  function resolveContextLimit(providerID, modelID, ctx) {
175240
- const fromModelsDev = providerID && modelID ? getModelsDevContextLimit(providerID, modelID) : undefined;
175460
+ const fromModelsDev = providerID && modelID ? getSdkContextLimit(providerID, modelID) : undefined;
175241
175461
  const baseline = fromModelsDev ?? DEFAULT_CONTEXT_LIMIT;
175242
175462
  if (ctx?.db && ctx.sessionID) {
175243
175463
  try {
@@ -175250,7 +175470,7 @@ function resolveContextLimit(providerID, modelID, ctx) {
175250
175470
  return baseline;
175251
175471
  }
175252
175472
  function resolveTrustedContextLimit(providerID, modelID, ctx) {
175253
- const fromModelsDev = providerID && modelID ? getModelsDevContextLimit(providerID, modelID) : undefined;
175473
+ const fromModelsDev = providerID && modelID ? getSdkContextLimit(providerID, modelID) : undefined;
175254
175474
  let detected;
175255
175475
  if (ctx?.db && ctx.sessionID) {
175256
175476
  try {
@@ -176608,7 +176828,8 @@ function getSessionCreatedInfo(properties) {
176608
176828
  id: info.id,
176609
176829
  parentID: info.parentID,
176610
176830
  providerID: typeof info.providerID === "string" ? info.providerID : undefined,
176611
- modelID: typeof info.modelID === "string" ? info.modelID : undefined
176831
+ modelID: typeof info.modelID === "string" ? info.modelID : undefined,
176832
+ title: typeof info.title === "string" ? info.title : undefined
176612
176833
  };
176613
176834
  }
176614
176835
  function getMessageUpdatedAssistantInfo(properties) {
@@ -178675,6 +178896,64 @@ init_embedding();
178675
178896
 
178676
178897
  // src/features/magic-context/search.ts
178677
178898
  init_logger();
178899
+
178900
+ // src/features/magic-context/literal-probes.ts
178901
+ var MAX_PROBES = 5;
178902
+ var MIN_PROBE_LENGTH = 3;
178903
+ var SLASH_COMMAND_RE = /\/[a-z][a-z0-9]*(?:-[a-z0-9]+)+/gi;
178904
+ var KEBAB_SNAKE_RE = /[a-z][a-z0-9]*(?:[-_][a-z0-9]+)+/gi;
178905
+ var DOTTED_RE = /[a-z0-9][a-z0-9_-]*(?:\.[a-z0-9_-]+)+/gi;
178906
+ var CAMEL_RE = /\b[a-zA-Z][a-z0-9]*(?:[A-Z][a-z0-9]*)+\b/g;
178907
+ var SHA_RE = /\b[0-9a-f]{7,40}\b/gi;
178908
+ var ERROR_CODE_RE = /\b(?:TS\d{4,}|ERR_[A-Z][A-Z0-9_]*)\b/g;
178909
+ var QUOTED_RE = /["`]([^"`]{3,80})["`]/g;
178910
+ function looksLikeSha(token) {
178911
+ return /[0-9]/.test(token) && /^[0-9a-f]{7,40}$/i.test(token);
178912
+ }
178913
+ function extractLiteralProbes(query) {
178914
+ const trimmed = query.trim();
178915
+ if (trimmed.length === 0)
178916
+ return [];
178917
+ const ordered = [];
178918
+ const seen = new Set;
178919
+ const add = (raw) => {
178920
+ if (!raw)
178921
+ return;
178922
+ const probe = raw.trim();
178923
+ if (probe.length < MIN_PROBE_LENGTH)
178924
+ return;
178925
+ const key = probe.toLowerCase();
178926
+ if (seen.has(key))
178927
+ return;
178928
+ seen.add(key);
178929
+ ordered.push(probe);
178930
+ };
178931
+ for (const m of trimmed.matchAll(QUOTED_RE))
178932
+ add(m[1]);
178933
+ for (const m of trimmed.matchAll(SLASH_COMMAND_RE))
178934
+ add(m[0]);
178935
+ for (const m of trimmed.matchAll(ERROR_CODE_RE))
178936
+ add(m[0]);
178937
+ for (const m of trimmed.matchAll(DOTTED_RE))
178938
+ add(m[0]);
178939
+ for (const m of trimmed.matchAll(KEBAB_SNAKE_RE))
178940
+ add(m[0]);
178941
+ for (const m of trimmed.matchAll(CAMEL_RE))
178942
+ add(m[0]);
178943
+ for (const m of trimmed.matchAll(SHA_RE)) {
178944
+ if (looksLikeSha(m[0]))
178945
+ add(m[0]);
178946
+ }
178947
+ return ordered.slice(0, MAX_PROBES);
178948
+ }
178949
+ function containsProbeVerbatim(text, probes) {
178950
+ if (probes.length === 0)
178951
+ return false;
178952
+ const haystack = text.toLowerCase();
178953
+ return probes.some((probe) => haystack.includes(probe.toLowerCase()));
178954
+ }
178955
+
178956
+ // src/features/magic-context/search.ts
178678
178957
  init_memory();
178679
178958
  init_embedding();
178680
178959
  init_storage_memory_fts();
@@ -178854,36 +179133,82 @@ function linearDecayScore(rank, total) {
178854
179133
  return 0;
178855
179134
  return Math.max(0, 1 - rank / total);
178856
179135
  }
178857
- function searchMessages(args) {
178858
- const sanitizedQuery = sanitizeFtsQuery(args.query.trim());
178859
- if (sanitizedQuery.length === 0) {
179136
+ function runMessageFtsQuery(db, sessionId, ftsQuery, fetchLimit, cutoff) {
179137
+ if (ftsQuery.length === 0)
178860
179138
  return [];
178861
- }
178862
- const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
178863
- const rows = getMessageSearchStatement(args.db).all(args.sessionId, sanitizedQuery, fetchLimit).map((row) => row);
178864
- const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
178865
- const filtered = rows.map((row) => {
179139
+ const rows = getMessageSearchStatement(db).all(sessionId, ftsQuery, fetchLimit).map((row) => row);
179140
+ const result = [];
179141
+ for (const row of rows) {
178866
179142
  const messageOrdinal = getMessageOrdinal(row.messageOrdinal);
178867
179143
  if (messageOrdinal === null || typeof row.messageId !== "string" || typeof row.role !== "string" || typeof row.content !== "string") {
178868
- return null;
179144
+ continue;
178869
179145
  }
178870
179146
  if (cutoff !== null && messageOrdinal > cutoff) {
178871
- return null;
179147
+ continue;
178872
179148
  }
178873
- return {
179149
+ result.push({
178874
179150
  messageOrdinal,
178875
179151
  messageId: row.messageId,
178876
179152
  role: row.role,
178877
179153
  content: row.content
178878
- };
178879
- }).filter((result) => result !== null).slice(0, args.limit);
178880
- return filtered.map((row, rank) => ({
179154
+ });
179155
+ }
179156
+ return result;
179157
+ }
179158
+ var RRF_K = 60;
179159
+ var VERBATIM_PROBE_BONUS = 0.5;
179160
+ function searchMessages(args) {
179161
+ const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
179162
+ const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
179163
+ const baseQuery = sanitizeFtsQuery(args.query.trim());
179164
+ const probes = args.probes ?? [];
179165
+ if (probes.length === 0) {
179166
+ const filtered = runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff).slice(0, args.limit);
179167
+ return filtered.map((row, rank) => ({
179168
+ source: "message",
179169
+ content: previewText(row.content),
179170
+ score: linearDecayScore(rank, filtered.length),
179171
+ messageOrdinal: row.messageOrdinal,
179172
+ messageId: row.messageId,
179173
+ role: row.role
179174
+ }));
179175
+ }
179176
+ const queryLists = [];
179177
+ if (baseQuery.length > 0) {
179178
+ queryLists.push(runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff));
179179
+ }
179180
+ for (const probe of probes) {
179181
+ const probeQuery = sanitizeFtsQuery(probe);
179182
+ if (probeQuery.length === 0)
179183
+ continue;
179184
+ queryLists.push(runMessageFtsQuery(args.db, args.sessionId, probeQuery, fetchLimit, cutoff));
179185
+ }
179186
+ const fused = new Map;
179187
+ for (const list of queryLists) {
179188
+ list.forEach((row, rank) => {
179189
+ const rrf = 1 / (RRF_K + rank);
179190
+ const existing = fused.get(row.messageId);
179191
+ if (existing) {
179192
+ existing.score += rrf;
179193
+ } else {
179194
+ fused.set(row.messageId, { row, score: rrf });
179195
+ }
179196
+ });
179197
+ }
179198
+ for (const entry of fused.values()) {
179199
+ if (containsProbeVerbatim(entry.row.content, probes)) {
179200
+ entry.score += VERBATIM_PROBE_BONUS;
179201
+ }
179202
+ }
179203
+ 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);
179204
+ const maxScore = ranked.length > 0 ? ranked[0].score : 1;
179205
+ return ranked.map((entry) => ({
178881
179206
  source: "message",
178882
- content: previewText(row.content),
178883
- score: linearDecayScore(rank, filtered.length),
178884
- messageOrdinal: row.messageOrdinal,
178885
- messageId: row.messageId,
178886
- role: row.role
179207
+ content: previewText(entry.row.content),
179208
+ score: maxScore > 0 ? entry.score / maxScore : 0,
179209
+ messageOrdinal: entry.row.messageOrdinal,
179210
+ messageId: entry.row.messageId,
179211
+ role: entry.row.role
178887
179212
  }));
178888
179213
  }
178889
179214
  function getSourceBoost(result) {
@@ -178967,12 +179292,14 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
178967
179292
  return null;
178968
179293
  }) : Promise.resolve(null);
178969
179294
  await Promise.resolve();
179295
+ const messageProbes = options.explicitSearch ? extractLiteralProbes(trimmedQuery) : [];
178970
179296
  const messageResults = runMessages ? searchMessages({
178971
179297
  db,
178972
179298
  sessionId,
178973
179299
  query: trimmedQuery,
178974
179300
  limit: tierLimit,
178975
- maxOrdinal: options.maxMessageOrdinal
179301
+ maxOrdinal: options.maxMessageOrdinal,
179302
+ probes: messageProbes
178976
179303
  }) : [];
178977
179304
  const queryEmbedding = await queryEmbeddingPromise;
178978
179305
  const [memoryResults, gitCommitResults] = await Promise.all([
@@ -179555,7 +179882,7 @@ function isVisibleNoteReadPart(part) {
179555
179882
  }
179556
179883
 
179557
179884
  // src/hooks/magic-context/todo-view.ts
179558
- import { createHash as createHash10 } from "node:crypto";
179885
+ import { createHash as createHash9 } from "node:crypto";
179559
179886
  var TERMINAL_STATUSES = new Set(["completed", "cancelled"]);
179560
179887
  var TITLE_DONE_STATUSES = new Set(["completed"]);
179561
179888
  var SYNTHETIC_CALL_ID_PREFIX = "mc_synthetic_todo_";
@@ -179600,7 +179927,7 @@ function buildSyntheticTodoPart(stateJson) {
179600
179927
  };
179601
179928
  }
179602
179929
  function computeSyntheticCallId(stateJson) {
179603
- const hash2 = createHash10("sha256").update(stateJson).digest("hex").slice(0, 16);
179930
+ const hash2 = createHash9("sha256").update(stateJson).digest("hex").slice(0, 16);
179604
179931
  return `${SYNTHETIC_CALL_ID_PREFIX}${hash2}`;
179605
179932
  }
179606
179933
  function parseTodoState(stateJson) {
@@ -180166,6 +180493,10 @@ function createTransform(deps) {
180166
180493
  return;
180167
180494
  }
180168
180495
  logTransformTiming(sessionId, "getOrCreateSessionMeta", tMeta);
180496
+ if (deps.internalChildSessions?.has(sessionId)) {
180497
+ sessionLog(sessionId, "transform skipped (internal magic-context child session)");
180498
+ return;
180499
+ }
180169
180500
  const reducedMode = sessionMeta.isSubagent;
180170
180501
  const fullFeatureMode = !reducedMode;
180171
180502
  let sessionDirectory = deps.directory ?? "";
@@ -180761,6 +181092,7 @@ function truncateHistorianEmergencyError(error51) {
180761
181092
 
180762
181093
  // src/hooks/magic-context/event-handler.ts
180763
181094
  var CONTEXT_USAGE_TTL_MS = 60 * 60 * 1000;
181095
+ var INTERNAL_CHILD_TITLE_PREFIX = "magic-context-";
180764
181096
  function formatTokens(value) {
180765
181097
  return value.toLocaleString();
180766
181098
  }
@@ -180825,6 +181157,10 @@ function createEventHandler2(deps) {
180825
181157
  if (!info) {
180826
181158
  return;
180827
181159
  }
181160
+ if (deps.internalChildSessions && info.parentID.length > 0 && typeof info.title === "string" && info.title.startsWith(INTERNAL_CHILD_TITLE_PREFIX)) {
181161
+ deps.internalChildSessions.add(info.id);
181162
+ sessionLog(info.id, `marked internal magic-context child (title="${info.title}") — exempt from transform + injection`);
181163
+ }
180828
181164
  try {
180829
181165
  const modelKey = resolveModelKey(info.providerID, info.modelID);
180830
181166
  updateSessionMeta(deps.db, info.id, {
@@ -181241,10 +181577,7 @@ await __promiseAll([
181241
181577
  ]);
181242
181578
 
181243
181579
  // src/hooks/magic-context/text-complete.ts
181244
- var DEFAULT_PATTERNS = [
181245
- /^(\u00a7\d+\u00a7\s*)+/,
181246
- /\u00a7/g
181247
- ];
181580
+ init_tag_content_primitives();
181248
181581
  function parseRegex(src) {
181249
181582
  if (src.startsWith("/")) {
181250
181583
  const lastSlash = src.lastIndexOf("/");
@@ -181258,17 +181591,19 @@ function parseRegex(src) {
181258
181591
  }
181259
181592
  function createTextCompleteHandler(stripConfig) {
181260
181593
  const isEnabled = stripConfig?.enabled ?? true;
181261
- let patterns;
181262
- if (!isEnabled) {
181263
- patterns = [];
181264
- } else if (stripConfig?.patterns && stripConfig.patterns.length > 0) {
181594
+ let patterns = null;
181595
+ if (!isEnabled) {} else if (stripConfig?.patterns && stripConfig.patterns.length > 0) {
181265
181596
  patterns = stripConfig.patterns.map(parseRegex);
181266
- } else {
181267
- patterns = DEFAULT_PATTERNS;
181268
181597
  }
181269
181598
  return async (_input, output) => {
181270
- for (const regex of patterns) {
181271
- output.text = output.text.replace(regex, "");
181599
+ if (!isEnabled)
181600
+ return;
181601
+ if (patterns) {
181602
+ for (const regex of patterns) {
181603
+ output.text = output.text.replace(regex, "");
181604
+ }
181605
+ } else {
181606
+ output.text = stripPersistedAssistantText(output.text);
181272
181607
  }
181273
181608
  };
181274
181609
  }
@@ -181491,7 +181826,7 @@ function createToolExecuteAfterHook(args) {
181491
181826
  init_send_session_notification();
181492
181827
 
181493
181828
  // src/hooks/magic-context/system-prompt-hash.ts
181494
- import { createHash as createHash11 } from "node:crypto";
181829
+ import { createHash as createHash10 } from "node:crypto";
181495
181830
 
181496
181831
  // src/agents/magic-context-prompt.ts
181497
181832
  var LONG_TERM_PARTNER_FRAME = `### You are the user's long-term partner on this project — not a one-off hire
@@ -181613,6 +181948,9 @@ function clearSystemPromptHashSession(sessionId, handleMaps) {
181613
181948
  function isInternalOpenCodeAgent(systemPromptContent) {
181614
181949
  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.");
181615
181950
  }
181951
+ function isMagicContextInternalAgent(systemPromptContent) {
181952
+ 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");
181953
+ }
181616
181954
  function createSystemPromptHashHandler(deps) {
181617
181955
  const stickyDateBySession = new Map;
181618
181956
  const handler = async (input, output) => {
@@ -181625,6 +181963,10 @@ function createSystemPromptHashHandler(deps) {
181625
181963
  sessionLog(sessionId, "system-prompt-hash skipped (OpenCode internal agent: title/summary/compaction)");
181626
181964
  return;
181627
181965
  }
181966
+ if (deps.internalChildSessions?.has(sessionId) || isMagicContextInternalAgent(fullPromptForDetection)) {
181967
+ sessionLog(sessionId, "system-prompt-hash skipped (Magic Context internal child: historian/dreamer/sidekick/migration)");
181968
+ return;
181969
+ }
181628
181970
  const injectionEnabled = deps.injectionEnabled !== false;
181629
181971
  const skipSignatures = deps.injectionSkipSignatures ?? [];
181630
181972
  if (!injectionEnabled) {
@@ -181675,7 +182017,7 @@ function createSystemPromptHashHandler(deps) {
181675
182017
  `);
181676
182018
  if (systemContent.length === 0)
181677
182019
  return;
181678
- const currentHash = createHash11("md5").update(systemContent).digest("hex");
182020
+ const currentHash = createHash10("md5").update(systemContent).digest("hex");
181679
182021
  if (!sessionMetaEarly) {
181680
182022
  return;
181681
182023
  }
@@ -181883,6 +182225,7 @@ function createMagicContextHook(deps) {
181883
182225
  const liveModelBySession = deps.liveSessionState?.liveModelBySession ?? new Map;
181884
182226
  const agentBySession = deps.liveSessionState?.agentBySession ?? new Map;
181885
182227
  const sessionDirectoryBySession = deps.liveSessionState?.sessionDirectoryBySession ?? new Map;
182228
+ const internalChildSessions = deps.liveSessionState?.internalChildSessions ?? new Set;
181886
182229
  const recompProgressBySession = deps.liveSessionState?.recompProgressBySession ?? new Map;
181887
182230
  const recentReduceBySession = new Map;
181888
182231
  const toolUsageSinceUserTurn = new Map;
@@ -181914,7 +182257,8 @@ function createMagicContextHook(deps) {
181914
182257
  pendingMaterializationSessions,
181915
182258
  deferredMaterializationSessions,
181916
182259
  sessionDirectoryBySession,
181917
- recompProgressBySession
182260
+ recompProgressBySession,
182261
+ internalChildSessions
181918
182262
  },
181919
182263
  directory: deps.directory,
181920
182264
  historianChunkTokens: getHistorianChunkTokens(),
@@ -181959,6 +182303,7 @@ function createMagicContextHook(deps) {
181959
182303
  deferredMaterializationSessions,
181960
182304
  lastHeuristicsTurnId,
181961
182305
  commitSeenLastPass,
182306
+ internalChildSessions,
181962
182307
  client: deps.client,
181963
182308
  directory: deps.directory,
181964
182309
  memoryConfig: deps.config.memory ? {
@@ -182010,6 +182355,7 @@ function createMagicContextHook(deps) {
182010
182355
  tagger: deps.tagger,
182011
182356
  db,
182012
182357
  client: deps.client,
182358
+ internalChildSessions,
182013
182359
  getNotificationParams: (sessionId) => getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
182014
182360
  nudgePlacements,
182015
182361
  onSessionCacheInvalidated: (sessionId) => {
@@ -182027,6 +182373,7 @@ function createMagicContextHook(deps) {
182027
182373
  recompProgressBySession.delete(sessionId);
182028
182374
  recentReduceBySession.delete(sessionId);
182029
182375
  toolUsageSinceUserTurn.delete(sessionId);
182376
+ internalChildSessions.delete(sessionId);
182030
182377
  }
182031
182378
  });
182032
182379
  const runDreamQueueInBackground = () => {
@@ -182137,6 +182484,7 @@ function createMagicContextHook(deps) {
182137
182484
  injectionSkipSignatures: deps.config.system_prompt_injection?.skip_signatures ?? [
182138
182485
  "<!-- magic-context: skip -->"
182139
182486
  ],
182487
+ internalChildSessions,
182140
182488
  experimentalUserMemories: deps.config.dreamer?.user_memories?.enabled,
182141
182489
  experimentalPinKeyFiles: deps.config.dreamer?.pin_key_files?.enabled ?? false,
182142
182490
  experimentalPinKeyFilesTokenBudget: deps.config.dreamer?.pin_key_files?.token_budget,
@@ -182249,7 +182597,8 @@ function createSessionHooks(args) {
182249
182597
  commit_cluster_trigger: pluginConfig.commit_cluster_trigger,
182250
182598
  system_prompt_injection: pluginConfig.system_prompt_injection,
182251
182599
  temporal_awareness: pluginConfig.temporal_awareness,
182252
- caveman_text_compression: pluginConfig.caveman_text_compression
182600
+ caveman_text_compression: pluginConfig.caveman_text_compression,
182601
+ text_complete_strip: pluginConfig.text_complete_strip
182253
182602
  }
182254
182603
  })
182255
182604
  };
@@ -182904,6 +183253,7 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
182904
183253
  if (!p)
182905
183254
  return null;
182906
183255
  return {
183256
+ kind: p.kind ?? "recomp",
182907
183257
  phase: p.phase,
182908
183258
  processedMessages: p.processedMessages,
182909
183259
  totalMessages: p.totalMessages,
@@ -182923,6 +183273,7 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
182923
183273
  return {
182924
183274
  ...empty,
182925
183275
  recompProgress: {
183276
+ kind: p.kind ?? "recomp",
182926
183277
  phase: p.phase,
182927
183278
  processedMessages: p.processedMessages,
182928
183279
  totalMessages: p.totalMessages,
@@ -183639,16 +183990,30 @@ Example: \`ctx_note(action="write", content="Implement X because Y", surface_con
183639
183990
 
183640
183991
  Historian reads these notes, deduplicates them, and rewrites the remaining useful notes over time.`;
183641
183992
  // src/tools/ctx-note/tools.ts
183642
- await init_storage();
183993
+ await __promiseAll([
183994
+ init_message_index(),
183995
+ init_storage()
183996
+ ]);
183643
183997
  import { tool as tool3 } from "@opencode-ai/plugin";
183998
+ function captureAnchorOrdinal(db, sessionId) {
183999
+ try {
184000
+ const ordinal = getLastIndexedOrdinal(db, sessionId);
184001
+ return ordinal > 0 ? ordinal : null;
184002
+ } catch {
184003
+ return null;
184004
+ }
184005
+ }
184006
+ function anchorSuffix(note) {
184007
+ return note.anchorOrdinal !== null ? ` ↳ @msg ${note.anchorOrdinal}` : "";
184008
+ }
183644
184009
  function formatNoteLine(note) {
183645
184010
  const statusSuffix = note.status === "active" ? "" : ` (${note.status})`;
183646
184011
  if (note.type === "session") {
183647
- return `- **#${note.id}**${statusSuffix}: ${note.content}`;
184012
+ return `- **#${note.id}**${statusSuffix}: ${note.content}${anchorSuffix(note)}`;
183648
184013
  }
183649
184014
  const conditionText = note.status === "ready" ? note.readyReason ?? note.surfaceCondition ?? "Condition satisfied" : note.surfaceCondition ?? "No condition recorded";
183650
184015
  const conditionLabel = note.status === "ready" ? "Condition met" : "Condition";
183651
- return `- **#${note.id}**${statusSuffix}: ${note.content}
184016
+ return `- **#${note.id}**${statusSuffix}: ${note.content}${anchorSuffix(note)}
183652
184017
  ${conditionLabel}: ${conditionText}`;
183653
184018
  }
183654
184019
  var DISMISS_FOOTER = `
@@ -183726,6 +184091,7 @@ function createCtxNoteTool(deps) {
183726
184091
  if (!content) {
183727
184092
  return "Error: 'content' is required when action is 'write'.";
183728
184093
  }
184094
+ const anchorOrdinal = captureAnchorOrdinal(deps.db, sessionId);
183729
184095
  if (args.surface_condition?.trim()) {
183730
184096
  if (!deps.dreamerEnabled) {
183731
184097
  return "Error: Smart notes require dreamer to be enabled. Enable dreamer in magic-context.jsonc to use surface_condition.";
@@ -183737,13 +184103,14 @@ function createCtxNoteTool(deps) {
183737
184103
  content,
183738
184104
  projectPath: projectIdentity,
183739
184105
  sessionId,
183740
- surfaceCondition: args.surface_condition.trim()
184106
+ surfaceCondition: args.surface_condition.trim(),
184107
+ anchorOrdinal
183741
184108
  });
183742
184109
  return `Created smart note #${note2.id}. Dreamer will evaluate the condition during nightly runs:
183743
184110
  - Content: ${content}
183744
184111
  - Condition: ${args.surface_condition.trim()}`;
183745
184112
  }
183746
- const note = addNote(deps.db, "session", { sessionId, content });
184113
+ const note = addNote(deps.db, "session", { sessionId, content, anchorOrdinal });
183747
184114
  return `Saved session note #${note.id}. Historian will rewrite or deduplicate notes as needed.`;
183748
184115
  }
183749
184116
  if (action === "dismiss") {
@@ -183806,9 +184173,13 @@ ${parts.join(`
183806
184173
 
183807
184174
  No session notes or smart notes.`;
183808
184175
  }
183809
- return sections.join(`
184176
+ const body = sections.join(`
183810
184177
 
183811
- `) + DISMISS_FOOTER;
184178
+ `);
184179
+ const anchorHint = body.includes("↳ @msg ") ? `
184180
+
184181
+ ↳ @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).` : "";
184182
+ return body + anchorHint + DISMISS_FOOTER;
183812
184183
  }
183813
184184
  });
183814
184185
  }
@@ -184099,7 +184470,8 @@ function createCtxSearchTool(deps) {
184099
184470
  maxMessageOrdinal: lastCompartmentEnd >= 0 ? lastCompartmentEnd : undefined,
184100
184471
  gitCommitsEnabled,
184101
184472
  sources: normalizeSources(args.sources),
184102
- visibleMemoryIds
184473
+ visibleMemoryIds,
184474
+ explicitSearch: true
184103
184475
  });
184104
184476
  return formatSearchResults(query, results);
184105
184477
  }
@@ -184200,22 +184572,22 @@ init_models_dev_cache();
184200
184572
  init_logger();
184201
184573
  import { randomBytes } from "node:crypto";
184202
184574
  import {
184203
- mkdirSync as mkdirSync7,
184575
+ mkdirSync as mkdirSync8,
184204
184576
  readdirSync,
184205
184577
  readFileSync as readFileSync12,
184206
- renameSync,
184578
+ renameSync as renameSync2,
184207
184579
  unlinkSync as unlinkSync3,
184208
- writeFileSync as writeFileSync4
184580
+ writeFileSync as writeFileSync5
184209
184581
  } from "node:fs";
184210
184582
  import { createServer } from "node:http";
184211
184583
  import { dirname as dirname5 } from "node:path";
184212
184584
 
184213
184585
  // src/shared/rpc-utils.ts
184214
- import { createHash as createHash12 } from "node:crypto";
184586
+ import { createHash as createHash11 } from "node:crypto";
184215
184587
  import { join as join20 } from "node:path";
184216
184588
  function projectHash(directory) {
184217
184589
  const normalized = directory.replace(/\/+$/, "");
184218
- return createHash12("sha256").update(normalized).digest("hex").slice(0, 16);
184590
+ return createHash11("sha256").update(normalized).digest("hex").slice(0, 16);
184219
184591
  }
184220
184592
  function rpcPortDir(storageDir, directory) {
184221
184593
  return join20(storageDir, "rpc", projectHash(directory));
@@ -184298,15 +184670,15 @@ class MagicContextRpcServer {
184298
184670
  try {
184299
184671
  this.warnIfOtherLiveInstance();
184300
184672
  const dir = dirname5(this.portFilePath);
184301
- mkdirSync7(dir, { recursive: true, mode: 448 });
184673
+ mkdirSync8(dir, { recursive: true, mode: 448 });
184302
184674
  const tmpPath = `${this.portFilePath}.tmp`;
184303
- writeFileSync4(tmpPath, JSON.stringify({
184675
+ writeFileSync5(tmpPath, JSON.stringify({
184304
184676
  port: this.port,
184305
184677
  pid: process.pid,
184306
184678
  started_at: this.startedAt,
184307
184679
  token: this.token
184308
184680
  }), { encoding: "utf-8", mode: 384 });
184309
- renameSync(tmpPath, this.portFilePath);
184681
+ renameSync2(tmpPath, this.portFilePath);
184310
184682
  log(`[rpc] server listening on 127.0.0.1:${this.port}`);
184311
184683
  } catch (err) {
184312
184684
  log(`[rpc] failed to write port file: ${err}`);
@@ -184502,7 +184874,7 @@ var plugin = async (ctx) => {
184502
184874
  rpcServer.start().catch((err) => {
184503
184875
  log(`[magic-context] RPC server failed to start: ${err}`);
184504
184876
  });
184505
- refreshModelLimitsFromApi(ctx.client);
184877
+ refreshModelLimitsFromApi(ctx.client, { retries: 3, retryDelayMs: 1000 });
184506
184878
  }
184507
184879
  {
184508
184880
  const fence = getSchemaFenceRejection();