@cortexkit/magic-context 0.16.3 → 0.17.0

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 (2) hide show
  1. package/dist/index.js +498 -61
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -160419,18 +160419,77 @@ var init_tag_part_guards = __esm(() => {
160419
160419
  init_tag_content_primitives();
160420
160420
  });
160421
160421
 
160422
+ // ../plugin/src/shared/record-type-guard.ts
160423
+ function isRecord2(value) {
160424
+ return typeof value === "object" && value !== null && !Array.isArray(value);
160425
+ }
160426
+
160427
+ // ../plugin/src/hooks/magic-context/tool-drop-target.ts
160428
+ function hasMeaningfulPart(part) {
160429
+ if (!isRecord2(part))
160430
+ return false;
160431
+ const type = part.type;
160432
+ if (type === "text") {
160433
+ return typeof part.text === "string" && part.text.trim().length > 0;
160434
+ }
160435
+ if (typeof type !== "string")
160436
+ return false;
160437
+ if (IGNORE_PART_TYPES.has(type))
160438
+ return false;
160439
+ return true;
160440
+ }
160441
+
160442
+ class ToolMutationBatch {
160443
+ partsToRemove = new Set;
160444
+ affectedMessages = new Set;
160445
+ messages;
160446
+ constructor(messages) {
160447
+ this.messages = messages;
160448
+ }
160449
+ markForRemoval(occurrence) {
160450
+ this.partsToRemove.add(occurrence.part);
160451
+ this.affectedMessages.add(occurrence.message);
160452
+ }
160453
+ finalize() {
160454
+ if (this.partsToRemove.size === 0)
160455
+ return;
160456
+ for (const message of this.affectedMessages) {
160457
+ message.parts = message.parts.filter((p) => !this.partsToRemove.has(p));
160458
+ }
160459
+ for (let i = this.messages.length - 1;i >= 0; i -= 1) {
160460
+ if (!this.messages[i].parts.some(hasMeaningfulPart)) {
160461
+ this.messages.splice(i, 1);
160462
+ }
160463
+ }
160464
+ this.partsToRemove.clear();
160465
+ this.affectedMessages.clear();
160466
+ }
160467
+ }
160468
+ var IGNORE_PART_TYPES;
160469
+ var init_tool_drop_target = __esm(() => {
160470
+ IGNORE_PART_TYPES = new Set([
160471
+ "thinking",
160472
+ "reasoning",
160473
+ "redacted_thinking",
160474
+ "meta",
160475
+ "step-start",
160476
+ "step-finish"
160477
+ ]);
160478
+ });
160479
+
160422
160480
  // ../plugin/src/hooks/magic-context/read-session-chunk.ts
160423
160481
  var sessionProviders;
160424
160482
  var init_read_session_chunk = __esm(async () => {
160425
160483
  init_read_session_formatting();
160426
160484
  init_tag_part_guards();
160485
+ init_tool_drop_target();
160427
160486
  init_read_session_formatting();
160428
160487
  await init_read_session_db();
160429
160488
  sessionProviders = new Map;
160430
160489
  });
160431
160490
 
160432
160491
  // ../plugin/src/features/magic-context/message-index.ts
160433
- var lastIndexedStatements, insertMessageStatements, upsertIndexStatements, deleteFtsStatements, deleteIndexStatements, countIndexedMessageStatements;
160492
+ var lastIndexedStatements, insertMessageStatements, upsertIndexStatements, deleteFtsStatements, deleteIndexStatements, countIndexedMessageStatements, indexedMessageIdStatements;
160434
160493
  var init_message_index = __esm(async () => {
160435
160494
  init_compression_depth_storage();
160436
160495
  await init_read_session_chunk();
@@ -160440,6 +160499,7 @@ var init_message_index = __esm(async () => {
160440
160499
  deleteFtsStatements = new WeakMap;
160441
160500
  deleteIndexStatements = new WeakMap;
160442
160501
  countIndexedMessageStatements = new WeakMap;
160502
+ indexedMessageIdStatements = new WeakMap;
160443
160503
  });
160444
160504
 
160445
160505
  // ../plugin/src/shared/error-message.ts
@@ -160461,22 +160521,51 @@ function getCurrentVersion(db) {
160461
160521
  const row = db.prepare("SELECT MAX(version) as version FROM schema_migrations").get();
160462
160522
  return row?.version ?? 0;
160463
160523
  }
160524
+ function isSiblingMigrationConflict(error48, version2) {
160525
+ if (!(error48 instanceof Error))
160526
+ return false;
160527
+ const code = error48.code;
160528
+ if (code !== "SQLITE_CONSTRAINT_PRIMARYKEY" && code !== "SQLITE_CONSTRAINT_UNIQUE") {
160529
+ return false;
160530
+ }
160531
+ const msg = error48.message;
160532
+ if (!msg.includes("schema_migrations"))
160533
+ return false;
160534
+ if (!msg.toLowerCase().includes("version"))
160535
+ return false;
160536
+ return true;
160537
+ }
160464
160538
  function runMigrations(db) {
160465
160539
  ensureMigrationsTable(db);
160466
- const currentVersion = getCurrentVersion(db);
160467
- const pendingMigrations = MIGRATIONS.filter((m) => m.version > currentVersion);
160540
+ let currentVersion = getCurrentVersion(db);
160541
+ let pendingMigrations = MIGRATIONS.filter((m) => m.version > currentVersion);
160468
160542
  if (pendingMigrations.length === 0) {
160469
160543
  return;
160470
160544
  }
160471
160545
  log2(`[migrations] current schema version: ${currentVersion}, applying ${pendingMigrations.length} migration(s)`);
160472
- for (const migration of pendingMigrations) {
160546
+ let migrationIndex = 0;
160547
+ while (migrationIndex < pendingMigrations.length) {
160548
+ const migration = pendingMigrations[migrationIndex];
160473
160549
  try {
160474
160550
  db.transaction(() => {
160475
160551
  migration.up(db);
160476
160552
  db.prepare("INSERT INTO schema_migrations (version, description, applied_at) VALUES (?, ?, ?)").run(migration.version, migration.description, Date.now());
160477
160553
  })();
160478
160554
  log2(`[migrations] applied v${migration.version}: ${migration.description}`);
160555
+ migrationIndex += 1;
160479
160556
  } catch (error48) {
160557
+ if (isSiblingMigrationConflict(error48, migration.version)) {
160558
+ log2(`[migrations] v${migration.version} already applied by sibling instance — resuming with re-read version`);
160559
+ const reReadVersion = getCurrentVersion(db);
160560
+ if (reReadVersion <= currentVersion) {
160561
+ log2(`[migrations] FAILED v${migration.version}: sibling-conflict shape but version not advanced (${reReadVersion} <= ${currentVersion}) — failing closed`);
160562
+ throw new Error(`Migration v${migration.version} failed: sibling conflict reported but version did not advance. Database may need manual repair.`);
160563
+ }
160564
+ currentVersion = reReadVersion;
160565
+ pendingMigrations = MIGRATIONS.filter((m) => m.version > currentVersion);
160566
+ migrationIndex = 0;
160567
+ continue;
160568
+ }
160480
160569
  log2(`[migrations] FAILED v${migration.version}: ${migration.description} — ${error48 instanceof Error ? error48.message : String(error48)}`);
160481
160570
  throw new Error(`Migration v${migration.version} failed: ${error48 instanceof Error ? error48.message : String(error48)}. Database may need manual repair.`);
160482
160571
  }
@@ -160688,30 +160777,361 @@ var init_migrations = __esm(async () => {
160688
160777
  db.exec("ALTER TABLE notes ADD COLUMN harness TEXT NOT NULL DEFAULT 'opencode'");
160689
160778
  }
160690
160779
  }
160780
+ },
160781
+ {
160782
+ version: 8,
160783
+ description: "Add partial indexes on tags(session_id, tag_number) for active and dropped",
160784
+ up: (db) => {
160785
+ db.exec(`
160786
+ CREATE INDEX IF NOT EXISTS idx_tags_active_session_tag_number
160787
+ ON tags(session_id, tag_number)
160788
+ WHERE status = 'active';
160789
+
160790
+ CREATE INDEX IF NOT EXISTS idx_tags_dropped_session_tag_number
160791
+ ON tags(session_id, tag_number)
160792
+ WHERE status = 'dropped';
160793
+ `);
160794
+ db.exec("ANALYZE tags;");
160795
+ }
160796
+ },
160797
+ {
160798
+ version: 9,
160799
+ description: "Persist tool_definition_measurements across plugin restarts",
160800
+ up: (db) => {
160801
+ db.exec(`
160802
+ CREATE TABLE IF NOT EXISTS tool_definition_measurements (
160803
+ provider_id TEXT NOT NULL,
160804
+ model_id TEXT NOT NULL,
160805
+ agent_name TEXT NOT NULL,
160806
+ tool_id TEXT NOT NULL,
160807
+ token_count INTEGER NOT NULL,
160808
+ recorded_at INTEGER NOT NULL,
160809
+ PRIMARY KEY (provider_id, model_id, agent_name, tool_id)
160810
+ );
160811
+ `);
160812
+ }
160813
+ },
160814
+ {
160815
+ version: 10,
160816
+ description: "Add tool_owner_message_id column to tags + composite identity indexes",
160817
+ up: (db) => {
160818
+ const cols = db.prepare("PRAGMA table_info(tags)").all();
160819
+ if (!cols.some((c) => c.name === "tool_owner_message_id")) {
160820
+ db.exec("ALTER TABLE tags ADD COLUMN tool_owner_message_id TEXT DEFAULT NULL");
160821
+ }
160822
+ db.exec(`
160823
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_tags_tool_composite
160824
+ ON tags(session_id, message_id, tool_owner_message_id)
160825
+ WHERE type = 'tool' AND tool_owner_message_id IS NOT NULL;
160826
+
160827
+ CREATE INDEX IF NOT EXISTS idx_tags_tool_null_owner
160828
+ ON tags(session_id, message_id)
160829
+ WHERE type = 'tool' AND tool_owner_message_id IS NULL;
160830
+ `);
160831
+ }
160691
160832
  }
160692
160833
  ];
160693
160834
  });
160694
160835
 
160695
- // ../plugin/src/features/magic-context/storage-db.ts
160696
- import { copyFileSync, cpSync, existsSync as existsSync16, mkdirSync as mkdirSync7 } from "node:fs";
160836
+ // ../plugin/src/features/magic-context/tool-definition-tokens.ts
160837
+ function keyFor(providerID, modelID, agentName) {
160838
+ const agent = agentName && agentName.length > 0 ? agentName : "default";
160839
+ return `${providerID}/${modelID}/${agent}`;
160840
+ }
160841
+ function setDatabase(db) {
160842
+ persistenceDb = db;
160843
+ }
160844
+ function loadToolDefinitionMeasurements(db) {
160845
+ let rows = [];
160846
+ try {
160847
+ rows = db.prepare("SELECT provider_id, model_id, agent_name, tool_id, token_count FROM tool_definition_measurements").all();
160848
+ } catch {
160849
+ return;
160850
+ }
160851
+ for (const row of rows) {
160852
+ const key = keyFor(row.provider_id, row.model_id, row.agent_name);
160853
+ let inner = measurements.get(key);
160854
+ if (!inner) {
160855
+ inner = new Map;
160856
+ measurements.set(key, inner);
160857
+ }
160858
+ inner.set(row.tool_id, row.token_count);
160859
+ }
160860
+ }
160861
+ var measurements, persistenceDb = null;
160862
+ var init_tool_definition_tokens = __esm(() => {
160863
+ init_read_session_formatting();
160864
+ measurements = new Map;
160865
+ });
160866
+
160867
+ // ../plugin/src/features/magic-context/tool-owner-backfill.ts
160868
+ import { existsSync as existsSync16 } from "node:fs";
160697
160869
  import { join as join15 } from "node:path";
160870
+ function resolveOpencodeDbPath() {
160871
+ return join15(getDataDir(), "opencode", "opencode.db");
160872
+ }
160873
+ function ensureBackfillStateTable(db) {
160874
+ db.exec(`
160875
+ CREATE TABLE IF NOT EXISTS tool_owner_backfill_state (
160876
+ session_id TEXT PRIMARY KEY,
160877
+ status TEXT NOT NULL CHECK (status IN ('pending', 'running', 'completed', 'skipped')),
160878
+ started_at INTEGER,
160879
+ lease_expires_at INTEGER,
160880
+ completed_at INTEGER,
160881
+ last_error TEXT
160882
+ );
160883
+ CREATE INDEX IF NOT EXISTS idx_tool_owner_backfill_state_status
160884
+ ON tool_owner_backfill_state(status);
160885
+ `);
160886
+ }
160887
+ function runToolOwnerBackfill(db) {
160888
+ const startedAt = performance.now();
160889
+ ensureBackfillStateTable(db);
160890
+ const result = {
160891
+ sessionsProcessed: 0,
160892
+ sessionsSkippedNoOcDb: 0,
160893
+ sessionsSkippedNoMatches: 0,
160894
+ sessionsCompleted: 0,
160895
+ sessionsBlockedByLease: 0,
160896
+ sessionsErrored: 0,
160897
+ rowsUpdated: 0,
160898
+ rowsLeftNull: 0,
160899
+ durationMs: 0
160900
+ };
160901
+ if (!isToolOwnerBackfillNeeded(db)) {
160902
+ result.durationMs = performance.now() - startedAt;
160903
+ return result;
160904
+ }
160905
+ const opencodeDbPath = resolveOpencodeDbPath();
160906
+ if (!existsSync16(opencodeDbPath)) {
160907
+ log2(`[backfill] OpenCode DB not found at ${opencodeDbPath} — marking all unbackfilled sessions as skipped. Lazy adoption (defense-in-depth) handles legacy rows at runtime.`);
160908
+ markAllUnbackfilledSessionsSkipped(db);
160909
+ result.sessionsSkippedNoOcDb = countSessionsByStatus(db, "skipped");
160910
+ result.durationMs = performance.now() - startedAt;
160911
+ return result;
160912
+ }
160913
+ db.exec(`ATTACH '${opencodeDbPath}' AS oc_backfill`);
160914
+ try {
160915
+ backfillToolOwnersInChunks(db, result);
160916
+ } finally {
160917
+ try {
160918
+ db.exec("DETACH DATABASE oc_backfill");
160919
+ } catch (error48) {
160920
+ log2(`[backfill] failed to detach oc_backfill database: ${error48 instanceof Error ? error48.message : String(error48)}`);
160921
+ }
160922
+ }
160923
+ result.durationMs = performance.now() - startedAt;
160924
+ log2(`[backfill] sessions=${result.sessionsProcessed} completed=${result.sessionsCompleted} skipped_no_oc=${result.sessionsSkippedNoOcDb} skipped_no_matches=${result.sessionsSkippedNoMatches} blocked_by_lease=${result.sessionsBlockedByLease} errored=${result.sessionsErrored} rows_updated=${result.rowsUpdated} rows_left_null=${result.rowsLeftNull} duration_ms=${Math.round(result.durationMs)}`);
160925
+ return result;
160926
+ }
160927
+ function isToolOwnerBackfillNeeded(db) {
160928
+ ensureBackfillStateTable(db);
160929
+ const row = db.prepare(`SELECT 1 AS hit
160930
+ FROM tags
160931
+ WHERE type = 'tool' AND tool_owner_message_id IS NULL
160932
+ AND NOT EXISTS (
160933
+ SELECT 1 FROM tool_owner_backfill_state s
160934
+ WHERE s.session_id = tags.session_id
160935
+ AND s.status IN ('completed', 'skipped')
160936
+ )
160937
+ LIMIT 1`).get();
160938
+ return row !== null && row !== undefined;
160939
+ }
160940
+ function markAllUnbackfilledSessionsSkipped(db) {
160941
+ const now = Date.now();
160942
+ db.prepare(`INSERT INTO tool_owner_backfill_state(session_id, status, started_at, completed_at, last_error)
160943
+ SELECT DISTINCT session_id, 'skipped', NULL, ?, NULL
160944
+ FROM tags
160945
+ WHERE type = 'tool' AND tool_owner_message_id IS NULL
160946
+ ON CONFLICT(session_id) DO UPDATE SET
160947
+ status = 'skipped',
160948
+ completed_at = excluded.completed_at,
160949
+ last_error = NULL
160950
+ WHERE tool_owner_backfill_state.status NOT IN ('completed', 'running')`).run(now);
160951
+ }
160952
+ function countSessionsByStatus(db, status) {
160953
+ const row = db.prepare("SELECT COUNT(*) AS c FROM tool_owner_backfill_state WHERE status = ?").get(status);
160954
+ return row.c;
160955
+ }
160956
+ function acquireSessionLease(db, sessionId, now) {
160957
+ const expiresAt = now + LEASE_DURATION_MS;
160958
+ const result = db.prepare(`INSERT INTO tool_owner_backfill_state(session_id, status, started_at, lease_expires_at)
160959
+ VALUES (?, 'running', ?, ?)
160960
+ ON CONFLICT(session_id) DO UPDATE SET
160961
+ status = 'running',
160962
+ started_at = excluded.started_at,
160963
+ lease_expires_at = excluded.lease_expires_at,
160964
+ last_error = NULL
160965
+ WHERE tool_owner_backfill_state.status IN ('pending', 'skipped')
160966
+ OR (tool_owner_backfill_state.status = 'running'
160967
+ AND tool_owner_backfill_state.lease_expires_at < ?)`).run(sessionId, now, expiresAt, now);
160968
+ return (result.changes ?? 0) === 1;
160969
+ }
160970
+ function renewSessionLease(db, sessionId, now) {
160971
+ const expiresAt = now + LEASE_DURATION_MS;
160972
+ db.prepare(`UPDATE tool_owner_backfill_state
160973
+ SET lease_expires_at = ?
160974
+ WHERE session_id = ? AND status = 'running'`).run(expiresAt, sessionId);
160975
+ }
160976
+ function markSessionCompleted(db, sessionId, now) {
160977
+ db.prepare(`UPDATE tool_owner_backfill_state
160978
+ SET status = 'completed', completed_at = ?, lease_expires_at = NULL, last_error = NULL
160979
+ WHERE session_id = ?`).run(now, sessionId);
160980
+ }
160981
+ function markSessionSkipped(db, sessionId, now, reason) {
160982
+ db.prepare(`INSERT INTO tool_owner_backfill_state(session_id, status, completed_at, last_error)
160983
+ VALUES (?, 'skipped', ?, ?)
160984
+ ON CONFLICT(session_id) DO UPDATE SET
160985
+ status = 'skipped',
160986
+ completed_at = excluded.completed_at,
160987
+ last_error = excluded.last_error,
160988
+ lease_expires_at = NULL`).run(sessionId, now, reason);
160989
+ }
160990
+ function markSessionErrored(db, sessionId, error48) {
160991
+ const message = error48 instanceof Error ? error48.message : String(error48);
160992
+ db.prepare(`UPDATE tool_owner_backfill_state
160993
+ SET last_error = ?, lease_expires_at = NULL
160994
+ WHERE session_id = ?`).run(message, sessionId);
160995
+ }
160996
+ function getSessionsNeedingBackfill(db) {
160997
+ const rows = db.prepare(`SELECT DISTINCT t.session_id
160998
+ FROM tags t
160999
+ LEFT JOIN tool_owner_backfill_state s ON s.session_id = t.session_id
161000
+ WHERE t.type = 'tool' AND t.tool_owner_message_id IS NULL
161001
+ AND (s.status IS NULL OR s.status NOT IN ('completed', 'skipped'))
161002
+ ORDER BY t.session_id ASC`).all();
161003
+ return rows.map((r) => r.session_id);
161004
+ }
161005
+ function buildSessionOwnerMap(db, sessionId) {
161006
+ const rows = db.prepare(`SELECT
161007
+ COALESCE(
161008
+ CASE WHEN json_extract(p.data, '$.type') = 'tool_use'
161009
+ THEN json_extract(p.data, '$.id')
161010
+ END,
161011
+ json_extract(p.data, '$.callID')
161012
+ ) AS callid,
161013
+ m.id AS owner_id,
161014
+ m.time_created AS owner_t_created,
161015
+ p.id AS part_id,
161016
+ p.time_created AS part_t_created
161017
+ FROM oc_backfill.message m
161018
+ INNER JOIN oc_backfill.part p ON p.message_id = m.id
161019
+ WHERE m.session_id = ?
161020
+ AND json_extract(m.data, '$.role') = 'assistant'
161021
+ AND (
161022
+ (json_extract(p.data, '$.type') IN ('tool', 'tool-invocation')
161023
+ AND json_extract(p.data, '$.callID') IS NOT NULL)
161024
+ OR (json_extract(p.data, '$.type') = 'tool_use'
161025
+ AND json_extract(p.data, '$.id') IS NOT NULL)
161026
+ )
161027
+ ORDER BY
161028
+ m.time_created ASC,
161029
+ m.id ASC,
161030
+ p.time_created ASC,
161031
+ p.id ASC`).all(sessionId);
161032
+ const oldestByCallId = new Map;
161033
+ for (const r of rows) {
161034
+ if (typeof r.callid !== "string" || r.callid.length === 0)
161035
+ continue;
161036
+ if (!oldestByCallId.has(r.callid)) {
161037
+ oldestByCallId.set(r.callid, r.owner_id);
161038
+ }
161039
+ }
161040
+ return oldestByCallId;
161041
+ }
161042
+ function applyOwnersForSession(db, sessionId, ownersByCallId) {
161043
+ if (ownersByCallId.size === 0) {
161044
+ const leftNull = db.prepare(`SELECT COUNT(*) AS c FROM tags
161045
+ WHERE session_id = ? AND type = 'tool'
161046
+ AND tool_owner_message_id IS NULL`).get(sessionId).c;
161047
+ return { rowsUpdated: 0, rowsLeftNull: leftNull };
161048
+ }
161049
+ const findOrphanStmt = db.prepare(`SELECT id FROM tags
161050
+ WHERE session_id = ? AND message_id = ? AND type = 'tool'
161051
+ AND tool_owner_message_id IS NULL
161052
+ ORDER BY tag_number ASC
161053
+ LIMIT 1`);
161054
+ const updateRowStmt = db.prepare(`UPDATE tags
161055
+ SET tool_owner_message_id = ?
161056
+ WHERE id = ? AND tool_owner_message_id IS NULL`);
161057
+ let rowsUpdated = 0;
161058
+ db.transaction(() => {
161059
+ for (const [callId, ownerId] of ownersByCallId) {
161060
+ const orphan = findOrphanStmt.get(sessionId, callId);
161061
+ if (!orphan)
161062
+ continue;
161063
+ const result = updateRowStmt.run(ownerId, orphan.id);
161064
+ rowsUpdated += result.changes ?? 0;
161065
+ }
161066
+ })();
161067
+ const rowsLeftNull = db.prepare(`SELECT COUNT(*) AS c FROM tags
161068
+ WHERE session_id = ? AND type = 'tool'
161069
+ AND tool_owner_message_id IS NULL`).get(sessionId).c;
161070
+ return { rowsUpdated, rowsLeftNull };
161071
+ }
161072
+ function backfillToolOwnersInChunks(db, result) {
161073
+ const sessionIds = getSessionsNeedingBackfill(db);
161074
+ let lastRenewedAt = Date.now();
161075
+ for (const sessionId of sessionIds) {
161076
+ const now = Date.now();
161077
+ result.sessionsProcessed += 1;
161078
+ const acquired = acquireSessionLease(db, sessionId, now);
161079
+ if (!acquired) {
161080
+ result.sessionsBlockedByLease += 1;
161081
+ continue;
161082
+ }
161083
+ try {
161084
+ const owners = buildSessionOwnerMap(db, sessionId);
161085
+ const { rowsUpdated, rowsLeftNull } = applyOwnersForSession(db, sessionId, owners);
161086
+ result.rowsUpdated += rowsUpdated;
161087
+ result.rowsLeftNull += rowsLeftNull;
161088
+ if (owners.size === 0) {
161089
+ markSessionSkipped(db, sessionId, Date.now(), "no_oc_matches");
161090
+ result.sessionsSkippedNoMatches += 1;
161091
+ } else {
161092
+ markSessionCompleted(db, sessionId, Date.now());
161093
+ result.sessionsCompleted += 1;
161094
+ }
161095
+ } catch (error48) {
161096
+ log2(`[backfill] session=${sessionId} errored: ${error48 instanceof Error ? error48.message : String(error48)}`);
161097
+ markSessionErrored(db, sessionId, error48);
161098
+ result.sessionsErrored += 1;
161099
+ }
161100
+ const sinceRenew = Date.now() - lastRenewedAt;
161101
+ if (sinceRenew > LEASE_RENEWAL_MS) {
161102
+ renewSessionLease(db, sessionId, Date.now());
161103
+ lastRenewedAt = Date.now();
161104
+ }
161105
+ }
161106
+ }
161107
+ var LEASE_DURATION_MS, LEASE_RENEWAL_MS;
161108
+ var init_tool_owner_backfill = __esm(() => {
161109
+ init_data_path();
161110
+ init_logger();
161111
+ LEASE_DURATION_MS = 5 * 60 * 1000;
161112
+ LEASE_RENEWAL_MS = 60 * 1000;
161113
+ });
161114
+
161115
+ // ../plugin/src/features/magic-context/storage-db.ts
161116
+ import { copyFileSync, cpSync, existsSync as existsSync17, mkdirSync as mkdirSync7 } from "node:fs";
161117
+ import { join as join16 } from "node:path";
160698
161118
  function resolveDatabasePath() {
160699
161119
  const dbDir = getMagicContextStorageDir();
160700
- return { dbDir, dbPath: join15(dbDir, "context.db") };
161120
+ return { dbDir, dbPath: join16(dbDir, "context.db") };
160701
161121
  }
160702
161122
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
160703
- if (existsSync16(targetDbPath))
161123
+ if (existsSync17(targetDbPath))
160704
161124
  return;
160705
161125
  const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
160706
- const legacyDbPath = join15(legacyDir, "context.db");
160707
- if (!existsSync16(legacyDbPath))
161126
+ const legacyDbPath = join16(legacyDir, "context.db");
161127
+ if (!existsSync17(legacyDbPath))
160708
161128
  return;
160709
161129
  log2(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
160710
161130
  mkdirSync7(targetDbDir, { recursive: true });
160711
161131
  for (const suffix of ["", "-wal", "-shm"]) {
160712
161132
  const src = `${legacyDbPath}${suffix}`;
160713
- const dst = join15(targetDbDir, `context.db${suffix}`);
160714
- if (existsSync16(src)) {
161133
+ const dst = join16(targetDbDir, `context.db${suffix}`);
161134
+ if (existsSync17(src)) {
160715
161135
  try {
160716
161136
  copyFileSync(src, dst);
160717
161137
  } catch (error48) {
@@ -160719,9 +161139,9 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
160719
161139
  }
160720
161140
  }
160721
161141
  }
160722
- const legacyModelsDir = join15(legacyDir, "models");
160723
- const targetModelsDir = join15(targetDbDir, "models");
160724
- if (existsSync16(legacyModelsDir) && !existsSync16(targetModelsDir)) {
161142
+ const legacyModelsDir = join16(legacyDir, "models");
161143
+ const targetModelsDir = join16(targetDbDir, "models");
161144
+ if (existsSync17(legacyModelsDir) && !existsSync17(targetModelsDir)) {
160725
161145
  try {
160726
161146
  cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
160727
161147
  } catch (error48) {
@@ -161007,6 +161427,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
161007
161427
  ensureColumn(db, "tags", "tool_name", "TEXT");
161008
161428
  ensureColumn(db, "tags", "input_byte_size", "INTEGER DEFAULT 0");
161009
161429
  ensureColumn(db, "tags", "caveman_depth", "INTEGER DEFAULT 0");
161430
+ ensureColumn(db, "tags", "tool_owner_message_id", "TEXT DEFAULT NULL");
161010
161431
  ensureColumn(db, "session_meta", "system_prompt_tokens", "INTEGER DEFAULT 0");
161011
161432
  ensureColumn(db, "session_meta", "compaction_marker_state", "TEXT DEFAULT ''");
161012
161433
  ensureColumn(db, "session_meta", "key_files", "TEXT DEFAULT ''");
@@ -161105,6 +161526,13 @@ function openDatabase() {
161105
161526
  const db = new Database(dbPath);
161106
161527
  initializeDatabase(db);
161107
161528
  runMigrations(db);
161529
+ try {
161530
+ runToolOwnerBackfill(db);
161531
+ } catch (error48) {
161532
+ log2(`[magic-context] tool-owner backfill failed (continuing with lazy adoption fallback): ${getErrorMessage(error48)}`);
161533
+ }
161534
+ setDatabase(db);
161535
+ loadToolDefinitionMeasurements(db);
161108
161536
  databases.set(dbPath, db);
161109
161537
  persistenceByDatabase.set(db, true);
161110
161538
  persistenceErrorByDatabase.delete(db);
@@ -161133,6 +161561,8 @@ var databases, persistenceByDatabase, persistenceErrorByDatabase;
161133
161561
  var init_storage_db = __esm(async () => {
161134
161562
  init_data_path();
161135
161563
  init_logger();
161564
+ init_tool_definition_tokens();
161565
+ init_tool_owner_backfill();
161136
161566
  await __promiseAll([
161137
161567
  init_sqlite(),
161138
161568
  init_migrations()
@@ -161197,7 +161627,7 @@ var init_storage_ops = __esm(() => {
161197
161627
  var init_storage_source = () => {};
161198
161628
 
161199
161629
  // ../plugin/src/features/magic-context/storage-tags.ts
161200
- var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements;
161630
+ var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements, getOwnerScopedToolTagNumbersStatements, getActiveTagsBySessionStatements, getMaxDroppedTagNumberStatements, getToolTagNumberByOwnerStatements, getNullOwnerToolTagStatements, adoptNullOwnerToolTagStatements, deleteToolTagsByOwnerStatements;
161201
161631
  var init_storage_tags = __esm(() => {
161202
161632
  insertTagStatements = new WeakMap;
161203
161633
  updateTagStatusStatements = new WeakMap;
@@ -161209,6 +161639,13 @@ var init_storage_tags = __esm(() => {
161209
161639
  getTagNumberByMessageIdStatements = new WeakMap;
161210
161640
  updateTagByteSizeStatements = new WeakMap;
161211
161641
  updateTagInputByteSizeStatements = new WeakMap;
161642
+ getOwnerScopedToolTagNumbersStatements = new WeakMap;
161643
+ getActiveTagsBySessionStatements = new WeakMap;
161644
+ getMaxDroppedTagNumberStatements = new WeakMap;
161645
+ getToolTagNumberByOwnerStatements = new WeakMap;
161646
+ getNullOwnerToolTagStatements = new WeakMap;
161647
+ adoptNullOwnerToolTagStatements = new WeakMap;
161648
+ deleteToolTagsByOwnerStatements = new WeakMap;
161212
161649
  });
161213
161650
 
161214
161651
  // ../plugin/src/features/magic-context/storage.ts
@@ -161227,20 +161664,20 @@ var init_storage = __esm(async () => {
161227
161664
  });
161228
161665
 
161229
161666
  // ../pi-plugin/src/config/index.ts
161230
- import { existsSync as existsSync17, readFileSync as readFileSync12 } from "node:fs";
161667
+ import { existsSync as existsSync18, readFileSync as readFileSync12 } from "node:fs";
161231
161668
  import { homedir as homedir11 } from "node:os";
161232
- import { join as join16 } from "node:path";
161669
+ import { join as join17 } from "node:path";
161233
161670
  function getProjectConfigPaths(cwd) {
161234
- const basePath = join16(cwd, ".pi", CONFIG_FILE_NAME);
161671
+ const basePath = join17(cwd, ".pi", CONFIG_FILE_NAME);
161235
161672
  return [`${basePath}.jsonc`, `${basePath}.json`];
161236
161673
  }
161237
161674
  function getUserConfigPaths() {
161238
161675
  const home = process.env.HOME ?? homedir11();
161239
- const basePath = join16(home, ".pi", "agent", CONFIG_FILE_NAME);
161676
+ const basePath = join17(home, ".pi", "agent", CONFIG_FILE_NAME);
161240
161677
  return [`${basePath}.jsonc`, `${basePath}.json`];
161241
161678
  }
161242
161679
  function resolveFirstExisting(paths) {
161243
- return paths.find((path4) => existsSync17(path4));
161680
+ return paths.find((path4) => existsSync18(path4));
161244
161681
  }
161245
161682
  function loadConfigFile2(path4, scope) {
161246
161683
  try {
@@ -161374,10 +161811,10 @@ var init_config2 = __esm(() => {
161374
161811
 
161375
161812
  // src/lib/diagnostics-pi.ts
161376
161813
  import { createHash } from "node:crypto";
161377
- import { existsSync as existsSync18, readFileSync as readFileSync13, statSync as statSync4 } from "node:fs";
161814
+ import { existsSync as existsSync19, readFileSync as readFileSync13, statSync as statSync4 } from "node:fs";
161378
161815
  import { createRequire as createRequire5 } from "node:module";
161379
161816
  import { homedir as homedir12, tmpdir as tmpdir5, userInfo as userInfo3 } from "node:os";
161380
- import { join as join17 } from "node:path";
161817
+ import { join as join18 } from "node:path";
161381
161818
  function getSelfVersion3() {
161382
161819
  const req = createRequire5(import.meta.url);
161383
161820
  for (const relPath of ["../../package.json", "../package.json"]) {
@@ -161392,7 +161829,7 @@ function getSelfVersion3() {
161392
161829
  }
161393
161830
  function fileSize2(path4) {
161394
161831
  try {
161395
- return existsSync18(path4) ? statSync4(path4).size : 0;
161832
+ return existsSync19(path4) ? statSync4(path4).size : 0;
161396
161833
  } catch {
161397
161834
  return 0;
161398
161835
  }
@@ -161442,7 +161879,7 @@ function sanitizeValue2(value, key = "") {
161442
161879
  return value;
161443
161880
  }
161444
161881
  function readJsonc3(path4) {
161445
- if (!existsSync18(path4))
161882
+ if (!existsSync19(path4))
161446
161883
  return { value: {} };
161447
161884
  try {
161448
161885
  return {
@@ -161456,13 +161893,13 @@ function readJsonc3(path4) {
161456
161893
  }
161457
161894
  }
161458
161895
  function getProjectConfigPath(cwd) {
161459
- return join17(cwd, ".pi", "magic-context.jsonc");
161896
+ return join18(cwd, ".pi", "magic-context.jsonc");
161460
161897
  }
161461
161898
  function readConfigDiagnostic(path4) {
161462
161899
  const parsed = readJsonc3(path4);
161463
161900
  return {
161464
161901
  path: path4,
161465
- exists: existsSync18(path4),
161902
+ exists: existsSync19(path4),
161466
161903
  ...parsed.parseError ? { parseError: parsed.parseError } : {},
161467
161904
  flags: sanitizeValue2(parsed.value)
161468
161905
  };
@@ -161482,9 +161919,9 @@ async function collectDiagnostics2(cwd = process.cwd()) {
161482
161919
  const projectConfigPath = getProjectConfigPath(cwd);
161483
161920
  const loaded = loadPiConfig({ cwd });
161484
161921
  const storageDirPath = getMagicContextStorageDir();
161485
- const dbPath = join17(storageDirPath, "context.db");
161486
- const logPath = join17(tmpdir5(), "magic-context.log");
161487
- const logFileSize = existsSync18(logPath) ? statSync4(logPath).size : 0;
161922
+ const dbPath = join18(storageDirPath, "context.db");
161923
+ const logPath = join18(tmpdir5(), "magic-context.log");
161924
+ const logFileSize = existsSync19(logPath) ? statSync4(logPath).size : 0;
161488
161925
  const otherPiExtensions = packages.filter((entry) => !hasMagicContextPackage([entry]));
161489
161926
  return {
161490
161927
  timestamp: new Date().toISOString(),
@@ -161497,7 +161934,7 @@ async function collectDiagnostics2(cwd = process.cwd()) {
161497
161934
  piVersion: pi ? getPiVersion(pi.path) : null,
161498
161935
  settings: {
161499
161936
  path: settingsPath,
161500
- exists: existsSync18(settingsPath),
161937
+ exists: existsSync19(settingsPath),
161501
161938
  ...settingsParsed.parseError ? { parseError: settingsParsed.parseError } : {},
161502
161939
  hasMagicContextPackage: hasMagicContextPackage(packages),
161503
161940
  packages: packages.map(sanitizeString2)
@@ -161513,7 +161950,7 @@ async function collectDiagnostics2(cwd = process.cwd()) {
161513
161950
  loadWarnings: loaded.warnings.map(sanitizeString2),
161514
161951
  storageDir: {
161515
161952
  path: storageDirPath,
161516
- exists: existsSync18(storageDirPath),
161953
+ exists: existsSync19(storageDirPath),
161517
161954
  contextDbSizeBytes: fileSize2(dbPath)
161518
161955
  },
161519
161956
  conflicts: {
@@ -161522,7 +161959,7 @@ async function collectDiagnostics2(cwd = process.cwd()) {
161522
161959
  },
161523
161960
  logFile: {
161524
161961
  path: logPath,
161525
- exists: existsSync18(logPath),
161962
+ exists: existsSync19(logPath),
161526
161963
  sizeKb: Math.round(logFileSize / 1024)
161527
161964
  }
161528
161965
  };
@@ -161612,7 +162049,7 @@ var init_diagnostics_pi = __esm(() => {
161612
162049
 
161613
162050
  // src/lib/logs-pi.ts
161614
162051
  import { readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "node:fs";
161615
- import { join as join18 } from "node:path";
162052
+ import { join as join19 } from "node:path";
161616
162053
  function sanitizeLogContent2(content) {
161617
162054
  return sanitizeString2(content);
161618
162055
  }
@@ -161656,7 +162093,7 @@ async function bundleIssueReport2(report, description, title, options = {}) {
161656
162093
  ].join(`
161657
162094
  `);
161658
162095
  const cwd = options.cwd ?? process.cwd();
161659
- const path4 = join18(cwd, `magic-context-pi-issue-${formatTimestamp2(options.now ?? new Date)}.md`);
162096
+ const path4 = join19(cwd, `magic-context-pi-issue-${formatTimestamp2(options.now ?? new Date)}.md`);
161660
162097
  writeFileSync11(path4, `${bodyMarkdown}
161661
162098
  `);
161662
162099
  return { path: path4, bodyMarkdown };
@@ -161668,7 +162105,7 @@ var init_logs_pi = __esm(() => {
161668
162105
  // src/commands/doctor-pi.ts
161669
162106
  import { execFileSync as execFileSync3, spawnSync as spawnSync3 } from "node:child_process";
161670
162107
  import {
161671
- existsSync as existsSync19,
162108
+ existsSync as existsSync20,
161672
162109
  mkdirSync as mkdirSync8,
161673
162110
  readdirSync as readdirSync4,
161674
162111
  readFileSync as readFileSync15,
@@ -161678,7 +162115,7 @@ import {
161678
162115
  } from "node:fs";
161679
162116
  import { createRequire as createRequire6 } from "node:module";
161680
162117
  import { homedir as homedir13, tmpdir as tmpdir6 } from "node:os";
161681
- import { dirname as dirname9, join as join19 } from "node:path";
162118
+ import { dirname as dirname9, join as join20 } from "node:path";
161682
162119
  function depsFrom(options) {
161683
162120
  return {
161684
162121
  ...DEFAULT_DEPS,
@@ -161781,10 +162218,10 @@ function hasPiPackage(packages) {
161781
162218
  return packages.some((entry) => entry === PI_PACKAGE_SOURCE || entry === PACKAGE_NAME2 || entry.includes("pi-magic-context"));
161782
162219
  }
161783
162220
  function projectConfigPath(cwd) {
161784
- return join19(cwd, ".pi", "magic-context.jsonc");
162221
+ return join20(cwd, ".pi", "magic-context.jsonc");
161785
162222
  }
161786
162223
  function readConfigForEmbedding(path4) {
161787
- if (!existsSync19(path4))
162224
+ if (!existsSync20(path4))
161788
162225
  return null;
161789
162226
  try {
161790
162227
  const rawText = readFileSync15(path4, "utf-8");
@@ -161846,19 +162283,19 @@ function countTable(db, table) {
161846
162283
  }
161847
162284
  function cacheRoots() {
161848
162285
  const home = homedir13();
161849
- const cacheHome = process.env.XDG_CACHE_HOME || join19(home, ".cache");
162286
+ const cacheHome = process.env.XDG_CACHE_HOME || join20(home, ".cache");
161850
162287
  return [
161851
- join19(home, ".pi", "cache", "extensions"),
161852
- join19(home, ".pi", "cache", "packages"),
161853
- join19(cacheHome, "pi", "extensions"),
161854
- join19(cacheHome, "pi", "packages"),
161855
- join19(getPiAgentConfigDir(), "cache", "extensions")
162288
+ join20(home, ".pi", "cache", "extensions"),
162289
+ join20(home, ".pi", "cache", "packages"),
162290
+ join20(cacheHome, "pi", "extensions"),
162291
+ join20(cacheHome, "pi", "packages"),
162292
+ join20(getPiAgentConfigDir(), "cache", "extensions")
161856
162293
  ];
161857
162294
  }
161858
162295
  function findPiMagicContextCacheDirs(self, force = false) {
161859
162296
  const found = new Map;
161860
162297
  const visit = (path4, depth) => {
161861
- if (depth < 0 || !existsSync19(path4))
162298
+ if (depth < 0 || !existsSync20(path4))
161862
162299
  return;
161863
162300
  let stat;
161864
162301
  try {
@@ -161868,8 +162305,8 @@ function findPiMagicContextCacheDirs(self, force = false) {
161868
162305
  }
161869
162306
  if (!stat.isDirectory())
161870
162307
  return;
161871
- const packageJson = join19(path4, "package.json");
161872
- if (existsSync19(packageJson)) {
162308
+ const packageJson = join20(path4, "package.json");
162309
+ if (existsSync20(packageJson)) {
161873
162310
  try {
161874
162311
  const pkg = JSON.parse(readFileSync15(packageJson, "utf-8"));
161875
162312
  const name2 = typeof pkg.name === "string" ? pkg.name : "";
@@ -161890,7 +162327,7 @@ function findPiMagicContextCacheDirs(self, force = false) {
161890
162327
  return;
161891
162328
  }
161892
162329
  for (const entry of entries)
161893
- visit(join19(path4, entry), depth - 1);
162330
+ visit(join20(path4, entry), depth - 1);
161894
162331
  };
161895
162332
  for (const root of cacheRoots())
161896
162333
  visit(root, 5);
@@ -161927,7 +162364,7 @@ async function runHealthChecks(options) {
161927
162364
  }
161928
162365
  const settingsPath = getPiUserExtensionsPath();
161929
162366
  let packages = [];
161930
- if (!existsSync19(settingsPath)) {
162367
+ if (!existsSync20(settingsPath)) {
161931
162368
  add(results, "warn", `Pi settings not found at ${settingsPath}`);
161932
162369
  repairPlan.addPackageEntry = true;
161933
162370
  } else {
@@ -161951,7 +162388,7 @@ async function runHealthChecks(options) {
161951
162388
  ["user", userConfigPath, true],
161952
162389
  ["project", projectPath, false]
161953
162390
  ]) {
161954
- if (!existsSync19(path4)) {
162391
+ if (!existsSync20(path4)) {
161955
162392
  if (required2) {
161956
162393
  add(results, "warn", `No ${label} magic-context.jsonc found at ${path4}`);
161957
162394
  repairPlan.writeUserConfig = true;
@@ -161984,8 +162421,8 @@ async function runHealthChecks(options) {
161984
162421
  add(results, "pass", `historian.model "${historianModel}" has thinking_level "${historianThinkingLevel}" configured`);
161985
162422
  }
161986
162423
  const storageDir = getMagicContextStorageDir();
161987
- const dbPath = join19(storageDir, "context.db");
161988
- const existedBeforeOpen = existsSync19(dbPath);
162424
+ const dbPath = join20(storageDir, "context.db");
162425
+ const existedBeforeOpen = existsSync20(dbPath);
161989
162426
  if (existedBeforeOpen)
161990
162427
  add(results, "pass", `Shared context DB exists at ${dbPath}`);
161991
162428
  else
@@ -162061,8 +162498,8 @@ async function runHealthChecks(options) {
162061
162498
  } else {
162062
162499
  add(results, "pass", "Pi extension cache clean (no stale cached package found)");
162063
162500
  }
162064
- const logPath = join19(tmpdir6(), "magic-context.log");
162065
- if (existsSync19(logPath)) {
162501
+ const logPath = join20(tmpdir6(), "magic-context.log");
162502
+ if (existsSync20(logPath)) {
162066
162503
  const stat = statSync5(logPath);
162067
162504
  const sizeKb = (stat.size / 1024).toFixed(0);
162068
162505
  const lines = readFileSync15(logPath, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
@@ -162071,10 +162508,10 @@ async function runHealthChecks(options) {
162071
162508
  } else {
162072
162509
  add(results, "info", `No plugin log file yet at ${logPath}`);
162073
162510
  }
162074
- const historianDumpDir = join19(tmpdir6(), "magic-context-historian");
162075
- if (existsSync19(historianDumpDir)) {
162511
+ const historianDumpDir = join20(tmpdir6(), "magic-context-historian");
162512
+ if (existsSync20(historianDumpDir)) {
162076
162513
  try {
162077
- const dumps = readdirSync4(historianDumpDir).filter((f) => f.endsWith(".xml")).map((f) => ({ name: f, mtime: statSync5(join19(historianDumpDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
162514
+ const dumps = readdirSync4(historianDumpDir).filter((f) => f.endsWith(".xml")).map((f) => ({ name: f, mtime: statSync5(join20(historianDumpDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
162078
162515
  if (dumps.length > 0) {
162079
162516
  add(results, "warn", `Historian debug dumps: ${dumps.length} file(s) in ${historianDumpDir}`);
162080
162517
  for (const dump of dumps.slice(0, 3)) {
@@ -162117,7 +162554,7 @@ function repair(plan, prompts) {
162117
162554
  }
162118
162555
  if (plan.writeUserConfig) {
162119
162556
  const configPath = getPiUserConfigPath();
162120
- if (!existsSync19(configPath)) {
162557
+ if (!existsSync20(configPath)) {
162121
162558
  try {
162122
162559
  writeDefaultMagicContextConfig(configPath);
162123
162560
  prompts.log.success(`Wrote default Magic Context config to ${configPath}`);
@@ -162278,7 +162715,7 @@ var exports_doctor = {};
162278
162715
  __export(exports_doctor, {
162279
162716
  runDoctor: () => runDoctor3
162280
162717
  });
162281
- import { existsSync as existsSync20, rmSync as rmSync3 } from "node:fs";
162718
+ import { existsSync as existsSync21, rmSync as rmSync3 } from "node:fs";
162282
162719
  async function runDoctor3(options) {
162283
162720
  if (options.clear)
162284
162721
  return runClear();
@@ -162362,7 +162799,7 @@ async function runClear() {
162362
162799
  const s = spinner();
162363
162800
  s.start(`Clearing ${item.path}`);
162364
162801
  try {
162365
- if (existsSync20(item.path)) {
162802
+ if (existsSync21(item.path)) {
162366
162803
  rmSync3(item.path, { recursive: true, force: true });
162367
162804
  }
162368
162805
  s.stop(`Cleared ${item.path}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/magic-context",
3
- "version": "0.16.3",
3
+ "version": "0.17.0",
4
4
  "description": "Unified CLI for Magic Context — setup, doctor, and migration across OpenCode and Pi",
5
5
  "keywords": [
6
6
  "opencode",