@cortexkit/pi-magic-context 0.21.4 → 0.21.5

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.
@@ -8489,8 +8489,10 @@ function deleteOrphanProjectKeyFiles(db, now = Date.now()) {
8489
8489
  if (existsSync2(row.projectPath))
8490
8490
  continue;
8491
8491
  try {
8492
- db.prepare("DELETE FROM project_key_files WHERE project_path = ?").run(row.projectPath);
8493
- db.prepare("DELETE FROM project_key_files_version WHERE project_path = ?").run(row.projectPath);
8492
+ db.transaction(() => {
8493
+ db.prepare("DELETE FROM project_key_files WHERE project_path = ?").run(row.projectPath);
8494
+ db.prepare("DELETE FROM project_key_files_version WHERE project_path = ?").run(row.projectPath);
8495
+ })();
8494
8496
  deletedProjects++;
8495
8497
  } catch (error) {
8496
8498
  log(`[key-files] orphan GC failed for ${row.projectPath}:`, error);
@@ -8897,6 +8899,16 @@ var MIGRATIONS = [
8897
8899
  WHERE auto_search_hint_decisions IS NULL
8898
8900
  `);
8899
8901
  }
8902
+ },
8903
+ {
8904
+ version: 18,
8905
+ description: "Add pending_pi_compaction_marker_state column for Pi deferred marker drain",
8906
+ up: (db) => {
8907
+ const cols = db.prepare("PRAGMA table_info(session_meta)").all();
8908
+ if (!cols.some((c) => c.name === "pending_pi_compaction_marker_state")) {
8909
+ db.exec("ALTER TABLE session_meta ADD COLUMN pending_pi_compaction_marker_state TEXT");
8910
+ }
8911
+ }
8900
8912
  }
8901
8913
  ];
8902
8914
  function ensureMigrationsTable(db) {
@@ -8912,7 +8924,7 @@ function getCurrentVersion(db) {
8912
8924
  const row = db.prepare("SELECT MAX(version) as version FROM schema_migrations").get();
8913
8925
  return row?.version ?? 0;
8914
8926
  }
8915
- function isSiblingMigrationConflict(error, version) {
8927
+ function isSiblingMigrationConflict(db, error, version) {
8916
8928
  if (!(error instanceof Error))
8917
8929
  return false;
8918
8930
  const code = error.code;
@@ -8924,7 +8936,8 @@ function isSiblingMigrationConflict(error, version) {
8924
8936
  return false;
8925
8937
  if (!msg.toLowerCase().includes("version"))
8926
8938
  return false;
8927
- return true;
8939
+ const confirmed = db.prepare("SELECT 1 FROM schema_migrations WHERE version = ?").get(version);
8940
+ return confirmed != null;
8928
8941
  }
8929
8942
  function runMigrations(db) {
8930
8943
  ensureMigrationsTable(db);
@@ -8945,7 +8958,7 @@ function runMigrations(db) {
8945
8958
  log(`[migrations] applied v${migration.version}: ${migration.description}`);
8946
8959
  migrationIndex += 1;
8947
8960
  } catch (error) {
8948
- if (isSiblingMigrationConflict(error, migration.version)) {
8961
+ if (isSiblingMigrationConflict(db, error, migration.version)) {
8949
8962
  log(`[migrations] v${migration.version} already applied by sibling instance — resuming with re-read version`);
8950
8963
  const reReadVersion = getCurrentVersion(db);
8951
8964
  if (reReadVersion <= currentVersion) {
@@ -142134,6 +142147,11 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
142134
142147
  -- Excluded from healNullTextColumns. Readers filter IS NOT NULL AND
142135
142148
  -- != empty-string defensively. Plan v6 section 3.
142136
142149
  pending_compaction_marker_state TEXT,
142150
+ -- pending_pi_compaction_marker_state: intentionally NULLABLE without a
142151
+ -- default. Absence of a deferred Pi-native marker is SQL NULL; presence
142152
+ -- is a valid JSON blob written via setPendingPiCompactionMarkerState.
142153
+ -- Excluded from healNullTextColumns.
142154
+ pending_pi_compaction_marker_state TEXT,
142137
142155
  -- deferred_execute_state: intentionally NULLABLE without a default.
142138
142156
  -- Absence is SQL NULL; presence is a JSON blob written via
142139
142157
  -- setDeferredExecutePendingIfAbsent. Excluded from healNullTextColumns.
@@ -142230,6 +142248,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
142230
142248
  ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
142231
142249
  ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
142232
142250
  ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
142251
+ ensureColumn(db, "session_meta", "pending_pi_compaction_marker_state", "TEXT");
142233
142252
  ensureColumn(db, "session_meta", "deferred_execute_state", "TEXT");
142234
142253
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
142235
142254
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -156867,11 +156886,124 @@ var MagicContextConfigSchema = exports_external.object({
156867
156886
  import { existsSync as existsSync5, readFileSync as readFileSync2 } from "node:fs";
156868
156887
  import { homedir as homedir2 } from "node:os";
156869
156888
  import { dirname as dirname3, isAbsolute, resolve as resolve2 } from "node:path";
156889
+
156890
+ // ../plugin/src/shared/jsonc-parser.ts
156891
+ function stripJsonComments(content) {
156892
+ let result = "";
156893
+ let inString = false;
156894
+ let escaped = false;
156895
+ let inLineComment = false;
156896
+ let inBlockComment = false;
156897
+ for (let index = 0;index < content.length; index += 1) {
156898
+ const char = content[index];
156899
+ const next = content[index + 1];
156900
+ if (inLineComment) {
156901
+ if (char === `
156902
+ `) {
156903
+ inLineComment = false;
156904
+ result += char;
156905
+ }
156906
+ continue;
156907
+ }
156908
+ if (inBlockComment) {
156909
+ if (char === "*" && next === "/") {
156910
+ inBlockComment = false;
156911
+ index += 1;
156912
+ }
156913
+ continue;
156914
+ }
156915
+ if (inString) {
156916
+ result += char;
156917
+ if (escaped) {
156918
+ escaped = false;
156919
+ } else if (char === "\\") {
156920
+ escaped = true;
156921
+ } else if (char === '"') {
156922
+ inString = false;
156923
+ }
156924
+ continue;
156925
+ }
156926
+ if (char === '"') {
156927
+ inString = true;
156928
+ result += char;
156929
+ continue;
156930
+ }
156931
+ if (char === "/" && next === "/") {
156932
+ inLineComment = true;
156933
+ index += 1;
156934
+ continue;
156935
+ }
156936
+ if (char === "/" && next === "*") {
156937
+ inBlockComment = true;
156938
+ index += 1;
156939
+ continue;
156940
+ }
156941
+ result += char;
156942
+ }
156943
+ return result;
156944
+ }
156945
+ function stripTrailingCommas(content) {
156946
+ let result = "";
156947
+ let inString = false;
156948
+ let escaped = false;
156949
+ for (let index = 0;index < content.length; index += 1) {
156950
+ const char = content[index];
156951
+ if (inString) {
156952
+ result += char;
156953
+ if (escaped) {
156954
+ escaped = false;
156955
+ } else if (char === "\\") {
156956
+ escaped = true;
156957
+ } else if (char === '"') {
156958
+ inString = false;
156959
+ }
156960
+ continue;
156961
+ }
156962
+ if (char === '"') {
156963
+ inString = true;
156964
+ result += char;
156965
+ continue;
156966
+ }
156967
+ if (char === ",") {
156968
+ let lookahead = index + 1;
156969
+ while (lookahead < content.length && /\s/.test(content[lookahead] ?? "")) {
156970
+ lookahead += 1;
156971
+ }
156972
+ const next = content[lookahead];
156973
+ if (next === "}" || next === "]") {
156974
+ continue;
156975
+ }
156976
+ }
156977
+ result += char;
156978
+ }
156979
+ return result;
156980
+ }
156981
+ function parseJsonc(content) {
156982
+ const normalized = stripTrailingCommas(stripJsonComments(content));
156983
+ return JSON.parse(normalized);
156984
+ }
156985
+
156986
+ // ../plugin/src/config/variable.ts
156870
156987
  var ENV_PATTERN = /\{env:([^}]+)\}/g;
156871
156988
  var FILE_PATTERN = /\{file:([^}]+)\}/g;
156872
156989
  function substituteConfigVariables(input) {
156873
156990
  const warnings = [];
156874
156991
  let text = input.text;
156992
+ if (input.isProjectConfig) {
156993
+ const hasEnvTokens = ENV_PATTERN.test(text);
156994
+ const hasFileTokens = FILE_PATTERN.test(text);
156995
+ ENV_PATTERN.lastIndex = 0;
156996
+ FILE_PATTERN.lastIndex = 0;
156997
+ if (hasEnvTokens || hasFileTokens) {
156998
+ const tokenTypes = [
156999
+ hasEnvTokens ? "{env:}" : undefined,
157000
+ hasFileTokens ? "{file:}" : undefined
157001
+ ].filter(Boolean).join(" and ");
157002
+ warnings.push(`Project-level config no longer supports ${tokenTypes} tokens for security reasons; leaving tokens literal. Move secret expansion to user-level config.`);
157003
+ }
157004
+ return { text, warnings };
157005
+ }
157006
+ text = stripJsonComments(text);
156875
157007
  text = text.replace(ENV_PATTERN, (_, rawName) => {
156876
157008
  const varName = rawName.trim();
156877
157009
  const value = varName ? process.env[varName] : undefined;
@@ -156879,7 +157011,7 @@ function substituteConfigVariables(input) {
156879
157011
  warnings.push(`Environment variable ${varName} is not set (referenced via {env:${varName}}); using empty string`);
156880
157012
  return "";
156881
157013
  }
156882
- return value;
157014
+ return JSON.stringify(value).slice(1, -1);
156883
157015
  });
156884
157016
  const fileMatches = Array.from(text.matchAll(FILE_PATTERN));
156885
157017
  if (fileMatches.length === 0) {
@@ -163269,6 +163401,10 @@ function getLastCompartmentEndMessage(db, sessionId) {
163269
163401
  const row = db.prepare("SELECT MAX(end_message) as max_end FROM compartments WHERE session_id = ?").get(sessionId);
163270
163402
  return row?.max_end ?? -1;
163271
163403
  }
163404
+ function getCompartmentsByEndMessageId(db, sessionId, endMessageId) {
163405
+ const rows = db.prepare("SELECT * FROM compartments WHERE session_id = ? AND end_message_id = ? ORDER BY sequence ASC").all(sessionId, endMessageId).filter(isCompartmentRow);
163406
+ return rows.map(toCompartment);
163407
+ }
163272
163408
  function appendCompartments(db, sessionId, compartments) {
163273
163409
  if (compartments.length === 0)
163274
163410
  return;
@@ -163378,7 +163514,7 @@ function promoteRecompStaging(db, sessionId) {
163378
163514
  insertFactRows(db, sessionId, staging.facts, now);
163379
163515
  db.prepare("DELETE FROM recomp_compartments WHERE session_id = ?").run(sessionId);
163380
163516
  db.prepare("DELETE FROM recomp_facts WHERE session_id = ?").run(sessionId);
163381
- db.prepare("UPDATE session_meta SET memory_block_cache = '' WHERE session_id = ?").run(sessionId);
163517
+ db.prepare("UPDATE session_meta SET memory_block_cache = '', memory_block_ids = '' WHERE session_id = ?").run(sessionId);
163382
163518
  return { compartments: staging.compartments, facts: staging.facts };
163383
163519
  })();
163384
163520
  }
@@ -164927,6 +165063,39 @@ function clearPendingCompactionMarkerStateIf(db, sessionId, expected) {
164927
165063
  WHERE session_id = ? AND pending_compaction_marker_state = ?`).run(sessionId, expectedBlob);
164928
165064
  return result.changes > 0;
164929
165065
  }
165066
+ function isPendingPiCompactionMarker(value) {
165067
+ return typeof value === "object" && value !== null && typeof value.firstKeptEntryId === "string" && typeof value.endMessageId === "string" && typeof value.ordinal === "number" && typeof value.tokensBefore === "number" && typeof value.summary === "string" && typeof value.publishedAt === "number";
165068
+ }
165069
+ function getPendingPiCompactionMarkerState(db, sessionId) {
165070
+ const row = db.prepare("SELECT pending_pi_compaction_marker_state FROM session_meta WHERE session_id = ?").get(sessionId);
165071
+ const raw = row?.pending_pi_compaction_marker_state;
165072
+ if (raw === null || raw === undefined || raw === "")
165073
+ return null;
165074
+ try {
165075
+ const parsed = JSON.parse(raw);
165076
+ if (isPendingPiCompactionMarker(parsed)) {
165077
+ return parsed;
165078
+ }
165079
+ } catch {}
165080
+ return null;
165081
+ }
165082
+ function setPendingPiCompactionMarkerState(db, sessionId, state) {
165083
+ ensureSessionMetaRow(db, sessionId);
165084
+ const blob = state ? stableStringify(state) : null;
165085
+ db.prepare("UPDATE session_meta SET pending_pi_compaction_marker_state = ? WHERE session_id = ?").run(blob, sessionId);
165086
+ }
165087
+ function clearPendingPiCompactionMarkerStateIf(db, sessionId, expected) {
165088
+ const expectedBlob = stableStringify(expected);
165089
+ const result = db.prepare(`UPDATE session_meta SET pending_pi_compaction_marker_state = NULL
165090
+ WHERE session_id = ? AND pending_pi_compaction_marker_state = ?`).run(sessionId, expectedBlob);
165091
+ return result.changes > 0;
165092
+ }
165093
+ function getSessionsWithPendingPiMarker(db) {
165094
+ const rows = db.prepare(`SELECT session_id FROM session_meta
165095
+ WHERE pending_pi_compaction_marker_state IS NOT NULL
165096
+ AND pending_pi_compaction_marker_state != ''`).all();
165097
+ return rows.map((r) => r.session_id);
165098
+ }
164930
165099
  function peekDeferredExecutePending(db, sessionId) {
164931
165100
  const row = db.prepare("SELECT deferred_execute_state FROM session_meta WHERE session_id = ?").get(sessionId);
164932
165101
  const raw = row?.deferred_execute_state;
@@ -165047,6 +165216,12 @@ function getNoteById(db, noteId) {
165047
165216
  const row = db.prepare("SELECT * FROM notes WHERE id = ?").get(noteId);
165048
165217
  return isNoteRow(row) ? toNote(row) : null;
165049
165218
  }
165219
+ function noteBelongsToScope(note, scope) {
165220
+ if (note.type === "session") {
165221
+ return note.sessionId === scope.sessionId;
165222
+ }
165223
+ return note.projectPath === scope.projectPath;
165224
+ }
165050
165225
  function buildStatusClause(status) {
165051
165226
  if (status === undefined) {
165052
165227
  return null;
@@ -165108,9 +165283,9 @@ function getPendingSmartNotes(db, projectPath) {
165108
165283
  function getReadySmartNotes(db, projectPath) {
165109
165284
  return getSmartNotes(db, projectPath, "ready");
165110
165285
  }
165111
- function updateNote(db, noteId, updates) {
165286
+ function updateNote(db, noteId, updates, scope) {
165112
165287
  const existing = getNoteById(db, noteId);
165113
- if (!existing) {
165288
+ if (!existing || !noteBelongsToScope(existing, scope)) {
165114
165289
  return null;
165115
165290
  }
165116
165291
  const now = Date.now();
@@ -165157,7 +165332,11 @@ function updateNote(db, noteId, updates) {
165157
165332
  const result = db.prepare(`UPDATE notes SET ${sets.join(", ")} WHERE id = ? RETURNING *`).get(...params);
165158
165333
  return isNoteRow(result) ? toNote(result) : null;
165159
165334
  }
165160
- function dismissNote(db, noteId) {
165335
+ function dismissNote(db, noteId, scope) {
165336
+ const existing = getNoteById(db, noteId);
165337
+ if (!existing || !noteBelongsToScope(existing, scope)) {
165338
+ return false;
165339
+ }
165161
165340
  const result = db.prepare("UPDATE notes SET status = 'dismissed', updated_at = ? WHERE id = ? AND status != 'dismissed'").run(Date.now(), noteId);
165162
165341
  return result.changes > 0;
165163
165342
  }
@@ -165577,8 +165756,15 @@ function createCtxNoteTool(deps) {
165577
165756
  if (typeof params.note_id !== "number") {
165578
165757
  return err3("Error: 'note_id' is required when action is 'dismiss'.");
165579
165758
  }
165580
- const dismissed = dismissNote(deps.db, params.note_id);
165581
- return ok3(dismissed ? `Note #${params.note_id} dismissed.` : `Note #${params.note_id} not found or already dismissed.`);
165759
+ const projectIdentity = resolveProjectIdentity(ctx.cwd);
165760
+ if (!projectIdentity) {
165761
+ return err3("Error: Could not resolve project identity for note dismiss.");
165762
+ }
165763
+ const dismissed = dismissNote(deps.db, params.note_id, {
165764
+ projectPath: projectIdentity,
165765
+ sessionId
165766
+ });
165767
+ return dismissed ? ok3(`Note #${params.note_id} dismissed.`) : err3(`Error: Note #${params.note_id} not found in your session/project or already dismissed.`);
165582
165768
  }
165583
165769
  if (action2 === "update") {
165584
165770
  if (typeof params.note_id !== "number") {
@@ -165592,9 +165778,16 @@ function createCtxNoteTool(deps) {
165592
165778
  if (!updates.content && !updates.surfaceCondition) {
165593
165779
  return err3("Error: Provide 'content' and/or 'surface_condition' to update.");
165594
165780
  }
165595
- const updated = updateNote(deps.db, params.note_id, updates);
165781
+ const projectIdentity = resolveProjectIdentity(ctx.cwd);
165782
+ if (!projectIdentity) {
165783
+ return err3("Error: Could not resolve project identity for note update.");
165784
+ }
165785
+ const updated = updateNote(deps.db, params.note_id, updates, {
165786
+ projectPath: projectIdentity,
165787
+ sessionId
165788
+ });
165596
165789
  if (!updated) {
165597
- return err3(`Note #${params.note_id} not found.`);
165790
+ return err3(`Error: Note #${params.note_id} not found in your session/project.`);
165598
165791
  }
165599
165792
  const parts = [];
165600
165793
  if (updates.content)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/pi-magic-context",
3
- "version": "0.21.4",
3
+ "version": "0.21.5",
4
4
  "type": "module",
5
5
  "description": "Pi coding agent extension for Magic Context — cross-session memory and context management",
6
6
  "main": "dist/index.js",