@cortexkit/pi-magic-context 0.21.2 → 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'");
@@ -156796,7 +156815,6 @@ var MagicContextConfigSchema = exports_external.object({
156796
156815
  enabled: exports_external.boolean().default(true),
156797
156816
  min_clusters: exports_external.number().min(1).default(3)
156798
156817
  }).default({ enabled: true, min_clusters: 3 }),
156799
- compaction_markers: exports_external.boolean().default(true),
156800
156818
  system_prompt_injection: exports_external.object({
156801
156819
  enabled: exports_external.boolean().default(true),
156802
156820
  skip_signatures: exports_external.array(exports_external.string()).default(["<!-- magic-context: skip -->"])
@@ -156868,11 +156886,124 @@ var MagicContextConfigSchema = exports_external.object({
156868
156886
  import { existsSync as existsSync5, readFileSync as readFileSync2 } from "node:fs";
156869
156887
  import { homedir as homedir2 } from "node:os";
156870
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
156871
156987
  var ENV_PATTERN = /\{env:([^}]+)\}/g;
156872
156988
  var FILE_PATTERN = /\{file:([^}]+)\}/g;
156873
156989
  function substituteConfigVariables(input) {
156874
156990
  const warnings = [];
156875
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);
156876
157007
  text = text.replace(ENV_PATTERN, (_, rawName) => {
156877
157008
  const varName = rawName.trim();
156878
157009
  const value = varName ? process.env[varName] : undefined;
@@ -156880,7 +157011,7 @@ function substituteConfigVariables(input) {
156880
157011
  warnings.push(`Environment variable ${varName} is not set (referenced via {env:${varName}}); using empty string`);
156881
157012
  return "";
156882
157013
  }
156883
- return value;
157014
+ return JSON.stringify(value).slice(1, -1);
156884
157015
  });
156885
157016
  const fileMatches = Array.from(text.matchAll(FILE_PATTERN));
156886
157017
  if (fileMatches.length === 0) {
@@ -163270,6 +163401,10 @@ function getLastCompartmentEndMessage(db, sessionId) {
163270
163401
  const row = db.prepare("SELECT MAX(end_message) as max_end FROM compartments WHERE session_id = ?").get(sessionId);
163271
163402
  return row?.max_end ?? -1;
163272
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
+ }
163273
163408
  function appendCompartments(db, sessionId, compartments) {
163274
163409
  if (compartments.length === 0)
163275
163410
  return;
@@ -163379,7 +163514,7 @@ function promoteRecompStaging(db, sessionId) {
163379
163514
  insertFactRows(db, sessionId, staging.facts, now);
163380
163515
  db.prepare("DELETE FROM recomp_compartments WHERE session_id = ?").run(sessionId);
163381
163516
  db.prepare("DELETE FROM recomp_facts WHERE session_id = ?").run(sessionId);
163382
- 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);
163383
163518
  return { compartments: staging.compartments, facts: staging.facts };
163384
163519
  })();
163385
163520
  }
@@ -164928,6 +165063,39 @@ function clearPendingCompactionMarkerStateIf(db, sessionId, expected) {
164928
165063
  WHERE session_id = ? AND pending_compaction_marker_state = ?`).run(sessionId, expectedBlob);
164929
165064
  return result.changes > 0;
164930
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
+ }
164931
165099
  function peekDeferredExecutePending(db, sessionId) {
164932
165100
  const row = db.prepare("SELECT deferred_execute_state FROM session_meta WHERE session_id = ?").get(sessionId);
164933
165101
  const raw = row?.deferred_execute_state;
@@ -165048,6 +165216,12 @@ function getNoteById(db, noteId) {
165048
165216
  const row = db.prepare("SELECT * FROM notes WHERE id = ?").get(noteId);
165049
165217
  return isNoteRow(row) ? toNote(row) : null;
165050
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
+ }
165051
165225
  function buildStatusClause(status) {
165052
165226
  if (status === undefined) {
165053
165227
  return null;
@@ -165109,9 +165283,9 @@ function getPendingSmartNotes(db, projectPath) {
165109
165283
  function getReadySmartNotes(db, projectPath) {
165110
165284
  return getSmartNotes(db, projectPath, "ready");
165111
165285
  }
165112
- function updateNote(db, noteId, updates) {
165286
+ function updateNote(db, noteId, updates, scope) {
165113
165287
  const existing = getNoteById(db, noteId);
165114
- if (!existing) {
165288
+ if (!existing || !noteBelongsToScope(existing, scope)) {
165115
165289
  return null;
165116
165290
  }
165117
165291
  const now = Date.now();
@@ -165158,7 +165332,11 @@ function updateNote(db, noteId, updates) {
165158
165332
  const result = db.prepare(`UPDATE notes SET ${sets.join(", ")} WHERE id = ? RETURNING *`).get(...params);
165159
165333
  return isNoteRow(result) ? toNote(result) : null;
165160
165334
  }
165161
- 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
+ }
165162
165340
  const result = db.prepare("UPDATE notes SET status = 'dismissed', updated_at = ? WHERE id = ? AND status != 'dismissed'").run(Date.now(), noteId);
165163
165341
  return result.changes > 0;
165164
165342
  }
@@ -165578,8 +165756,15 @@ function createCtxNoteTool(deps) {
165578
165756
  if (typeof params.note_id !== "number") {
165579
165757
  return err3("Error: 'note_id' is required when action is 'dismiss'.");
165580
165758
  }
165581
- const dismissed = dismissNote(deps.db, params.note_id);
165582
- 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.`);
165583
165768
  }
165584
165769
  if (action2 === "update") {
165585
165770
  if (typeof params.note_id !== "number") {
@@ -165593,9 +165778,16 @@ function createCtxNoteTool(deps) {
165593
165778
  if (!updates.content && !updates.surfaceCondition) {
165594
165779
  return err3("Error: Provide 'content' and/or 'surface_condition' to update.");
165595
165780
  }
165596
- 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
+ });
165597
165789
  if (!updated) {
165598
- return err3(`Note #${params.note_id} not found.`);
165790
+ return err3(`Error: Note #${params.note_id} not found in your session/project.`);
165599
165791
  }
165600
165792
  const parts = [];
165601
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.2",
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",