@cortexkit/magic-context 0.34.1 → 0.35.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 +175 -42
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -26716,6 +26716,23 @@ var init_cron = __esm(() => {
26716
26716
  MAX_SEARCH_MS = 4 * 366 * 24 * 60 * MINUTE_MS;
26717
26717
  });
26718
26718
 
26719
+ // ../plugin/src/shared/prompt-surface.ts
26720
+ function isValidPromptSurfaceModelKey(key) {
26721
+ if (key.length === 0 || key.trim() !== key)
26722
+ return false;
26723
+ const slash = key.indexOf("/");
26724
+ if (slash <= 0 || slash === key.length - 1)
26725
+ return false;
26726
+ const provider = key.slice(0, slash);
26727
+ const modelID = key.slice(slash + 1);
26728
+ if (provider.trim() !== provider || modelID.trim() !== modelID || provider.includes("*") || modelID.includes("*") && modelID !== "*") {
26729
+ return false;
26730
+ }
26731
+ if (modelID === "*")
26732
+ return true;
26733
+ return modelID.length > 0 && !modelID.startsWith("/") && !modelID.endsWith("/") && !modelID.includes("//");
26734
+ }
26735
+
26719
26736
  // ../plugin/src/config/schema/agent-overrides.ts
26720
26737
  var PermissionValueSchema, PermissionSchema, AgentOverrideConfigSchema;
26721
26738
  var init_agent_overrides = __esm(() => {
@@ -26764,7 +26781,7 @@ function expandConfigPath(value) {
26764
26781
  return `${homedir6()}/${trimmed.slice(2)}`;
26765
26782
  return trimmed;
26766
26783
  }
26767
- var DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65, EXECUTE_THRESHOLD_CAP_MESSAGE = "execute_threshold is capped at 90% for cache safety: output capacity is reserved from the usable context window, and the remaining 10% absorbs mid-turn growth before the absolute 95% emergency wall. Use a value between 20 and 90.", DEFAULT_HISTORIAN_TIMEOUT_MS = 300000, DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15, DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2", PiThinkingLevelSchema, PiConfigSchema, CronScheduleSchema, DreamTaskBaseConfigSchema, PromotionThresholdSchema, PrimerPromotionThresholdSchema, DreamTaskConfigSchema, ReviewUserMemoriesTaskConfigSchema, PromotePrimersTaskConfigSchema, DEFAULT_TASK_SCHEDULES, DreamTasksSchema, DreamerConfigSchema, SidekickConfigSchema, HistorianConfigSchema, EmbeddingFallbackProviderSchema, BaseEmbeddingConfigSchema, EmbeddingConfigSchema, MagicContextConfigSchema;
26784
+ var DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65, EXECUTE_THRESHOLD_CAP_MESSAGE = "execute_threshold is capped at 90% for cache safety: output capacity is reserved from the usable context window, and the remaining 10% absorbs mid-turn growth before the absolute 95% emergency wall. Use a value between 20 and 90.", DEFAULT_HISTORIAN_TIMEOUT_MS = 300000, DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15, DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2", PiThinkingLevelSchema, PiConfigSchema, PromptSurfacePresetSchema, PromptSurfaceModelKeySchema, PromptSurfaceToolKeySchema, PromptSurfaceConfigSchema, CronScheduleSchema, DreamTaskBaseConfigSchema, PromotionThresholdSchema, PrimerPromotionThresholdSchema, DreamTaskConfigSchema, ReviewUserMemoriesTaskConfigSchema, PromotePrimersTaskConfigSchema, DEFAULT_TASK_SCHEDULES, DreamTasksSchema, DreamerConfigSchema, SidekickConfigSchema, HistorianConfigSchema, EmbeddingFallbackProviderSchema, BaseEmbeddingConfigSchema, EmbeddingConfigSchema, MagicContextConfigSchema;
26768
26785
  var init_magic_context = __esm(() => {
26769
26786
  init_zod();
26770
26787
  init_language_directive();
@@ -26774,6 +26791,23 @@ var init_magic_context = __esm(() => {
26774
26791
  PiConfigSchema = exports_external.object({
26775
26792
  subagent_extensions: exports_external.array(exports_external.string().trim().min(1)).optional().describe("User-only allowlist of Pi extensions for Magic Context subagent children. When set, children use --no-extensions and load only these entries (plus Magic Context's scoped child extension where applicable). Relative paths resolve from ~/.pi/agent, matching Pi's settings.json package location. Unset preserves normal Pi extension discovery.")
26776
26793
  }).optional();
26794
+ PromptSurfacePresetSchema = exports_external.enum(["full", "light"]);
26795
+ PromptSurfaceModelKeySchema = exports_external.string().refine(isValidPromptSurfaceModelKey, {
26796
+ message: "Use a non-empty provider/model key or the literal provider/* wildcard; model IDs may contain additional slashes and matching is case-sensitive."
26797
+ });
26798
+ PromptSurfaceToolKeySchema = exports_external.string().refine((value) => value.trim().length > 0, {
26799
+ message: "tool description keys must not be empty or whitespace-only"
26800
+ });
26801
+ PromptSurfaceConfigSchema = exports_external.object({
26802
+ default: PromptSurfacePresetSchema.default("full").describe('Fallback prompt-surface preset ("full" or "light").'),
26803
+ models: exports_external.record(PromptSurfaceModelKeySchema, PromptSurfacePresetSchema).optional().describe("Literal per-model routing. Keys are provider/model or provider/*; matching is case-sensitive and preserves additional slashes in model IDs."),
26804
+ guidance_override_path: exports_external.string().refine((value) => value.trim().length > 0, {
26805
+ message: "guidance_override_path must not be empty or whitespace-only"
26806
+ }).optional().describe("USER-LEVEL ONLY path to a complete primary guidance section. Relative paths resolve from the user config file."),
26807
+ tool_descriptions: exports_external.record(PromptSurfaceToolKeySchema, exports_external.string().refine((value) => value.trim().length > 0, {
26808
+ message: "tool description values must not be empty or whitespace-only"
26809
+ })).optional().describe("USER-LEVEL ONLY top-level description overrides keyed by ctx_* tool ID; parameter schemas and descriptions are unchanged.")
26810
+ }).describe("Prompt-surface preset routing. Project config may select default/models, while guidance_override_path and tool_descriptions are user-level only.");
26777
26811
  CronScheduleSchema = exports_external.string().refine((s) => s.trim() === "" || isValidCron(s), {
26778
26812
  message: 'Invalid schedule: use a 5-field cron expression (e.g. "0 3 * * *" for 3am daily, "0 3 * * 0" for Sunday 3am, "0 */6 * * *" every 6h) or "" to disable.'
26779
26813
  }).describe('5-field cron schedule (e.g. "0 3 * * *"), or "" to disable this task.');
@@ -26940,6 +26974,7 @@ var init_magic_context = __esm(() => {
26940
26974
  historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
26941
26975
  dreamer: DreamerConfigSchema.optional().describe("Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)"),
26942
26976
  cache_ttl: exports_external.union([exports_external.string(), exports_external.object({ default: exports_external.string() }).catchall(exports_external.string())]).default("5m").describe('Cache TTL: string (e.g. "5m", "1h", "30s") or per-model object ({ default: "5m", "model-id": "10m" }). Set to "never" for lanes kept warm by an external keepwarm proxy — disables the idle-TTL heuristic so MC never initiates a rebuild based on elapsed time.'),
26977
+ prompt_surface: PromptSurfaceConfigSchema.default({ default: "full" }).describe("Prompt-surface presets: default is full; models use the literal provider/model or provider/* routing grammar. Guidance and tool-description overrides are user-level only. On OpenCode and Pi, per-model routing applies to the guidance block only: tool descriptions are registered once per process, so they follow the default preset (a v1 plugin-surface limitation; per-model tool descriptions are planned for the OpenCode v2 plugin API once the SDK stabilizes)."),
26943
26978
  output_reserve: exports_external.union([
26944
26979
  exports_external.number().min(0),
26945
26980
  exports_external.object({ default: exports_external.number().min(0) }).catchall(exports_external.number().min(0))
@@ -26988,7 +27023,7 @@ var init_magic_context = __esm(() => {
26988
27023
  keep_subagents: exports_external.boolean().default(false).describe("Debug: keep the child sessions Magic Context spawns for its own subagents (historian, dreamer, sidekick, memory-migration) instead of deleting them on success. Useful for short-term inspection/data collection — their full transcript (prompt, tool calls, token usage, output) stays in the host session store. Kept sessions accumulate until manually cleared; leave false for normal use. Requires a restart to take effect."),
26989
27024
  fail_closed_blocking: exports_external.boolean().default(true).describe("When Magic Context cannot operate (schema fence mismatch, storage open/migration failure), block the primary-session prompt with a loud recovery error instead of silently degrading to native compaction. Default true. Set false only to restore the old degrade-silently behavior (not recommended). USER-LEVEL ONLY — ignored in project config for security. Requires a restart."),
26990
27025
  compaction: exports_external.object({
26991
- enabled: exports_external.boolean().default(true).describe("When false, Magic Context stops managing the context window and keeps its knowledge layer: memory and docs/user-profile/key-files injection through additive m[0]/m[1], raw-message FTS indexing, dreamer, notes, ctx_search, ctx_expand, ctx_memory, and /ctx-embed remain available. MC's historian/compartment preparation, tagging, markers, pruning, folding, drops, strips, splicing, synthetic context-management todos, temporal markers, nudges, and fail-closed blocking stop; ctx_expand remains a knowledge-surface tool. fail_closed_blocking is inert: a transform failure passes the input messages through without blocking or cancelling. This setting does not enable native compaction: OpenCode's compaction.auto / compaction.prune or Pi's equivalent owns the window, or nothing does. MC's compaction.enabled in magic-context.jsonc is distinct from OpenCode's compaction.auto / compaction.prune in opencode.jsonc; they are different files and different owners. On the first turn after disabling, a long session may trigger one native compaction cycle; MC removes only its own marker boundary, leaves native boundaries and stored compartments intact, and does no pre-trimming mitigation. Marker cleanup is lazy per session, so an unresumed session is cleaned when it is next resumed. If compaction is enabled again, run /ctx-wrapup when the historian is runnable to catch up. OpenCode peer verification against v1.18.4 confirms native compaction covers child sessions: subagents receive additive memory/docs injection and no MC reclaim in this mode, so keep subagent tasks small or leave compaction.enabled on for long subagent runs. If transform_mode is rust, compaction-off resolves to the TypeScript transform and emits one frozen boot warning because there is no Rust reduced-mode contract. This is boot-resolved and requires a process restart; project-tier compaction.enabled is stripped so a cloned repository cannot disable the user's setting. The sidebar reports raw usage as Context: <pct>% · native compaction or Context: <pct>% · no active compaction and does not show an MC execute-threshold fill. /ctx-wrapup, /ctx-recomp, /ctx-flush, and /ctx-session-upgrade refuse without context-management side effects; /ctx-embed remains functional. Raw content hidden by a native boundary before Magic Context's first pass is not retroactively indexed.")
27026
+ enabled: exports_external.boolean().default(true).describe("When false, Magic Context stops managing the context window and keeps its knowledge layer: memory and docs/user-profile/key-files injection through additive m[0]/m[1], raw-message FTS indexing, dreamer, notes, ctx_search, ctx_expand, ctx_memory, and /ctx-embed remain available. MC's historian/compartment preparation, tagging, markers, pruning, folding, drops, strips, splicing, synthetic context-management todos, temporal markers, nudges, and fail-closed blocking stop; ctx_expand remains a knowledge-surface tool. fail_closed_blocking is inert: a transform failure passes the input messages through without blocking or cancelling. This setting does not enable native compaction: OpenCode's compaction.auto / compaction.prune or Pi's equivalent owns the window, or nothing does. MC's compaction.enabled in magic-context.jsonc is distinct from OpenCode's compaction.auto / compaction.prune in opencode.jsonc; they are different files and different owners. On the first turn after disabling, a long session may trigger one native compaction cycle; MC removes only its own marker boundary, leaves native boundaries and stored compartments intact, and does no pre-trimming mitigation. Marker cleanup is lazy per session, so an unresumed session is cleaned when it is next resumed. If compaction is enabled again, run /ctx-wrapup when the historian is runnable to catch up. OpenCode peer verification against v1.18.4 confirms native compaction covers child sessions: subagents receive additive memory/docs injection and no MC reclaim in this mode, so keep subagent tasks small or leave compaction.enabled on for long subagent runs. This is boot-resolved and requires a process restart; project-tier compaction.enabled is stripped so a cloned repository cannot disable the user's setting. The sidebar reports raw usage as Context: <pct>% · native compaction or Context: <pct>% · no active compaction and does not show an MC execute-threshold fill. /ctx-wrapup, /ctx-recomp, /ctx-flush, and /ctx-session-upgrade refuse without context-management side effects; /ctx-embed remains functional. Raw content hidden by a native boundary before Magic Context's first pass is not retroactively indexed.")
26992
27027
  }).default({ enabled: true }).describe("Compaction-off mode gate. Default true (MC manages the context window as today). Set compaction.enabled=false to keep the knowledge layer while letting native compaction (or nothing) own the window. Boot-resolved; requires a restart to change."),
26993
27028
  todowrite: exports_external.object({
26994
27029
  enabled: exports_external.boolean().default(true).describe("Pi only: register Magic Context's todowrite task-list tool. Disable if you use your own todo extension. OpenCode ships its own built-in todowrite; this setting has no effect there."),
@@ -27163,6 +27198,19 @@ function stripUnsafeProjectConfigFields(projectRaw) {
27163
27198
  delete storage.enforce_private_permissions;
27164
27199
  warnings.push("Ignoring storage.enforce_private_permissions from project config (security: only user-level config may opt into externally managed shared storage permissions).");
27165
27200
  }
27201
+ const promptSurface = projectRaw.prompt_surface;
27202
+ if (isPlainObject2(promptSurface)) {
27203
+ const removed = [];
27204
+ for (const field of PROMPT_SURFACE_USER_ONLY_FIELDS) {
27205
+ if (field in promptSurface) {
27206
+ delete promptSurface[field];
27207
+ removed.push(field);
27208
+ }
27209
+ }
27210
+ if (removed.length > 0) {
27211
+ warnings.push(`Ignoring prompt_surface.${removed.join("/")} from project config (security: repositories may select prompt presets but only user config may provide guidance or tool-description text).`);
27212
+ }
27213
+ }
27166
27214
  const pi = projectRaw.pi;
27167
27215
  if (isPlainObject2(pi) && "subagent_extensions" in pi) {
27168
27216
  delete pi.subagent_extensions;
@@ -27357,11 +27405,12 @@ function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
27357
27405
  "Dropped inherited user embedding api_key because project config redirected " + "embedding.endpoint without supplying its own key (security: prevents key " + "exfiltration to a repository-chosen endpoint)."
27358
27406
  ];
27359
27407
  }
27360
- var HIDDEN_AGENT_KEYS, HISTORIAN_USER_ONLY_FIELDS, AGENT_ESCALATION_FIELDS, EMBEDDING_DESTINATION_FIELDS, PERCENTAGE_THRESHOLD_REASON = "security: a repository may only raise compaction thresholds above the user's effective value; it cannot force earlier historian work or cloned-repo cost escalation.", TOKEN_THRESHOLD_REASON = "security: a repository may only raise execute_threshold_tokens above the user's trusted token threshold; it cannot force earlier historian work or cloned-repo cost escalation.", TOKEN_THRESHOLD_INTRODUCTION_REASON = "security: a repository cannot introduce a new execute_threshold_tokens override when the user has no trusted token threshold for that key; that could force earlier historian work or cloned-repo cost escalation.";
27408
+ var HIDDEN_AGENT_KEYS, HISTORIAN_USER_ONLY_FIELDS, PROMPT_SURFACE_USER_ONLY_FIELDS, AGENT_ESCALATION_FIELDS, EMBEDDING_DESTINATION_FIELDS, PERCENTAGE_THRESHOLD_REASON = "security: a repository may only raise compaction thresholds above the user's effective value; it cannot force earlier historian work or cloned-repo cost escalation.", TOKEN_THRESHOLD_REASON = "security: a repository may only raise execute_threshold_tokens above the user's trusted token threshold; it cannot force earlier historian work or cloned-repo cost escalation.", TOKEN_THRESHOLD_INTRODUCTION_REASON = "security: a repository cannot introduce a new execute_threshold_tokens override when the user has no trusted token threshold for that key; that could force earlier historian work or cloned-repo cost escalation.";
27361
27409
  var init_project_security = __esm(() => {
27362
27410
  init_magic_context();
27363
27411
  HIDDEN_AGENT_KEYS = ["historian", "dreamer", "sidekick"];
27364
27412
  HISTORIAN_USER_ONLY_FIELDS = ["model", "fallback_models"];
27413
+ PROMPT_SURFACE_USER_ONLY_FIELDS = ["guidance_override_path", "tool_descriptions"];
27365
27414
  AGENT_ESCALATION_FIELDS = ["prompt", "permission", "tools", "system_prompt"];
27366
27415
  EMBEDDING_DESTINATION_FIELDS = ["endpoint", "provider", "fallback_provider"];
27367
27416
  });
@@ -27823,6 +27872,7 @@ function loadPluginConfigDetailed(directory) {
27823
27872
  };
27824
27873
  return {
27825
27874
  config: config2,
27875
+ registrationPromptSurface: trustedBaseConfig.prompt_surface,
27826
27876
  loadOutcome: combinedOutcome({ sources, substitutionFailures, recoveredTopLevelKeys }),
27827
27877
  sources,
27828
27878
  substitutionFailures,
@@ -29696,9 +29746,10 @@ function prepareMirrorPageStatements(db) {
29696
29746
  memoryById: db.prepare(`SELECT project_path, category, content, normalized_hash, importance, scope, shareable,
29697
29747
  source_session_id, source_type, seen_count, retrieval_count, first_seen_at,
29698
29748
  created_at, updated_at, last_seen_at, last_retrieved_at, status, expires_at,
29699
- verification_status, verified_at, classified_at, superseded_by_memory_id,
29700
- merged_from, metadata_json
29701
- FROM memories WHERE id = ?`),
29749
+ verification_status, verified_at, classified_at, superseded_by_memory_id,
29750
+ merged_from, metadata_json, mural_cue, mural_cue_hash, mural_cue_at,
29751
+ mural_cue_rejection_count
29752
+ FROM memories WHERE id = ?`),
29702
29753
  memoryIdByStoreId: db.prepare("SELECT id FROM memories WHERE id = ? AND project_path = ?"),
29703
29754
  memoryCandidates: db.prepare("SELECT id FROM memories WHERE project_path = ? AND category = ? AND normalized_hash = ? ORDER BY id"),
29704
29755
  insertMemory: db.prepare("INSERT INTO memories (project_path, category, content, normalized_hash, first_seen_at, created_at, updated_at, last_seen_at) VALUES (?, ?, '', '', 0, 0, 0, 0)"),
@@ -29719,8 +29770,9 @@ function prepareMirrorPageStatements(db) {
29719
29770
  importance = ?, scope = ?, shareable = ?, source_session_id = ?, source_type = ?,
29720
29771
  seen_count = ?, retrieval_count = ?, first_seen_at = ?, created_at = ?, updated_at = ?,
29721
29772
  last_seen_at = ?, last_retrieved_at = ?, status = ?, expires_at = ?,
29722
- verification_status = ?, verified_at = ?, classified_at = ?, superseded_by_memory_id = ?,
29723
- merged_from = ?, metadata_json = ? WHERE id = ?`),
29773
+ verification_status = ?, verified_at = ?, classified_at = ?, superseded_by_memory_id = ?,
29774
+ merged_from = ?, metadata_json = ?, mural_cue = ?, mural_cue_hash = ?, mural_cue_at = ?,
29775
+ mural_cue_rejection_count = ? WHERE id = ?`),
29724
29776
  updateSuperseded: db.prepare("UPDATE memories SET superseded_by_memory_id = ? WHERE id = ?"),
29725
29777
  deletePendingReference: db.prepare("DELETE FROM mirror_pending_references WHERE domain = 'memories' AND module_project = ? AND module_row_id = ?"),
29726
29778
  upsertPendingReference: db.prepare("INSERT INTO mirror_pending_references(domain, module_project, module_row_id, target_module_row_id) VALUES ('memories', ?, ?, ?) ON CONFLICT(domain, module_project, module_row_id) DO UPDATE SET target_module_row_id = excluded.target_module_row_id"),
@@ -29884,7 +29936,7 @@ function applyMemoryRow(db, feed, statements) {
29884
29936
  const nullableString = (key, previous) => has(key) ? rowNullableString(row, key) : previous ?? null;
29885
29937
  const hasSuperseded = has("superseded_by_memory_id");
29886
29938
  const previousHash = existing?.normalized_hash;
29887
- statements.updateMemory.run(has("project_path") ? rowString(row, "project_path") : existing?.project_path ?? moduleProject, has("category") ? rowString(row, "category", "CONSTRAINTS") : existing?.category ?? "CONSTRAINTS", has("content") ? rowString(row, "content") : existing?.content ?? "", has("normalized_hash") ? rowString(row, "normalized_hash") : existing?.normalized_hash ?? "", has("importance") ? typeof row.importance === "number" && Number.isFinite(row.importance) ? row.importance : null : existing?.importance ?? null, has("scope") ? rowString(row, "scope", "project") : existing?.scope ?? "project", has("shareable") ? rowNumber(row, "shareable") : existing?.shareable ?? 0, nullableString("source_session_id", existing?.source_session_id), nullableString("source_type", existing?.source_type), has("seen_count") ? rowNumber(row, "seen_count", 1) : existing?.seen_count ?? 1, has("retrieval_count") ? rowNumber(row, "retrieval_count") : existing?.retrieval_count ?? 0, has("first_seen_at") ? rowNumber(row, "first_seen_at") : existing?.first_seen_at ?? 0, has("created_at") ? rowNumber(row, "created_at") : existing?.created_at ?? 0, has("updated_at") ? rowNumber(row, "updated_at") : existing?.updated_at ?? 0, has("last_seen_at") ? rowNumber(row, "last_seen_at") : existing?.last_seen_at ?? 0, nullableNumber("last_retrieved_at", existing?.last_retrieved_at), has("status") ? rowString(row, "status", "active") : existing?.status ?? "active", nullableNumber("expires_at", existing?.expires_at), has("verification_status") ? rowString(row, "verification_status", "unverified") : existing?.verification_status ?? "unverified", nullableNumber("verified_at", existing?.verified_at), nullableNumber("classified_at", existing?.classified_at), hasSuperseded ? null : existing?.superseded_by_memory_id ?? null, nullableString("merged_from", existing?.merged_from), nullableString("metadata_json", existing?.metadata_json), contextId);
29939
+ statements.updateMemory.run(has("project_path") ? rowString(row, "project_path") : existing?.project_path ?? moduleProject, has("category") ? rowString(row, "category", "CONSTRAINTS") : existing?.category ?? "CONSTRAINTS", has("content") ? rowString(row, "content") : existing?.content ?? "", has("normalized_hash") ? rowString(row, "normalized_hash") : existing?.normalized_hash ?? "", has("importance") ? typeof row.importance === "number" && Number.isFinite(row.importance) ? row.importance : null : existing?.importance ?? null, has("scope") ? rowString(row, "scope", "project") : existing?.scope ?? "project", has("shareable") ? rowNumber(row, "shareable") : existing?.shareable ?? 0, nullableString("source_session_id", existing?.source_session_id), nullableString("source_type", existing?.source_type), has("seen_count") ? rowNumber(row, "seen_count", 1) : existing?.seen_count ?? 1, has("retrieval_count") ? rowNumber(row, "retrieval_count") : existing?.retrieval_count ?? 0, has("first_seen_at") ? rowNumber(row, "first_seen_at") : existing?.first_seen_at ?? 0, has("created_at") ? rowNumber(row, "created_at") : existing?.created_at ?? 0, has("updated_at") ? rowNumber(row, "updated_at") : existing?.updated_at ?? 0, has("last_seen_at") ? rowNumber(row, "last_seen_at") : existing?.last_seen_at ?? 0, nullableNumber("last_retrieved_at", existing?.last_retrieved_at), has("status") ? rowString(row, "status", "active") : existing?.status ?? "active", nullableNumber("expires_at", existing?.expires_at), has("verification_status") ? rowString(row, "verification_status", "unverified") : existing?.verification_status ?? "unverified", nullableNumber("verified_at", existing?.verified_at), nullableNumber("classified_at", existing?.classified_at), hasSuperseded ? null : existing?.superseded_by_memory_id ?? null, nullableString("merged_from", existing?.merged_from), nullableString("metadata_json", existing?.metadata_json), nullableString("mural_cue", existing?.mural_cue), nullableString("mural_cue_hash", existing?.mural_cue_hash), nullableNumber("mural_cue_at", existing?.mural_cue_at), has("mural_cue_rejection_count") ? rowNumber(row, "mural_cue_rejection_count") : existing?.mural_cue_rejection_count ?? 0, contextId);
29888
29940
  if (hasSuperseded && typeof row.superseded_by_memory_id === "number") {
29889
29941
  const translated = mirrorIdentity(db, "memories", moduleProject, row.superseded_by_memory_id, statements);
29890
29942
  if (translated) {
@@ -30175,7 +30227,11 @@ var init_context_authority = __esm(async () => {
30175
30227
  "merged_from",
30176
30228
  "metadata_json",
30177
30229
  "context_store_uuid",
30178
- "context_row_id"
30230
+ "context_row_id",
30231
+ "mural_cue",
30232
+ "mural_cue_hash",
30233
+ "mural_cue_at",
30234
+ "mural_cue_rejection_count"
30179
30235
  ];
30180
30236
  mirrorFlights = new WeakMap;
30181
30237
  });
@@ -32897,6 +32953,14 @@ class SubcModuleTransport {
32897
32953
  throw new Error("mirror.pull omitted page");
32898
32954
  return { page: response.page };
32899
32955
  }
32956
+ async deleteSession(sessionId, projectRoot) {
32957
+ await this.call({
32958
+ sessionId,
32959
+ projectRoot,
32960
+ method: "session.delete",
32961
+ body: { method: "session.delete", v: 1, session_id: sessionId }
32962
+ });
32963
+ }
32900
32964
  closeSession(sessionId) {
32901
32965
  const client = this.client;
32902
32966
  const prefix = `${sessionId}\x00`;
@@ -35523,6 +35587,15 @@ var init_migrations = __esm(() => {
35523
35587
  ensureColumn(db, "session_meta", "detected_context_limit_provenance", "TEXT NOT NULL DEFAULT 'unknown'");
35524
35588
  }
35525
35589
  }
35590
+ },
35591
+ {
35592
+ version: 75,
35593
+ description: "persist mural cue validation rejection latches",
35594
+ up(db) {
35595
+ if (!tableExists(db, "memories"))
35596
+ return;
35597
+ ensureColumn(db, "memories", "mural_cue_rejection_count", "INTEGER NOT NULL DEFAULT 0");
35598
+ }
35526
35599
  }
35527
35600
  ];
35528
35601
  LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m3) => Math.max(max, m3.version), 0);
@@ -168015,6 +168088,7 @@ import {
168015
168088
  mkdtempSync,
168016
168089
  readdirSync as readdirSync2,
168017
168090
  readFileSync as readFileSync10,
168091
+ statSync as statSync7,
168018
168092
  unlinkSync as unlinkSync2
168019
168093
  } from "node:fs";
168020
168094
  import { basename as basename3, dirname as dirname10, join as join12 } from "node:path";
@@ -168026,24 +168100,57 @@ function getPersistedSchemaVersion(db) {
168026
168100
  const row = db.prepare("SELECT COALESCE(MAX(version), 0) AS version FROM schema_migrations WHERE version < ?").get(FORK_MIGRATION_VERSION_FLOOR);
168027
168101
  return row?.version ?? 0;
168028
168102
  }
168029
- function unreadableDiscovery(path4) {
168103
+ function unreadableDiscovery(path4, arm) {
168030
168104
  return {
168031
168105
  state: "unreadable",
168032
168106
  serverPids: [],
168033
168107
  staleFiles: [],
168034
- unreadableFile: path4
168108
+ unreadableFile: path4,
168109
+ unreadableArm: arm
168035
168110
  };
168036
168111
  }
168112
+ function invalidDiscoveryReason(raw) {
168113
+ const trimmed = raw.trim();
168114
+ if (trimmed.startsWith("{")) {
168115
+ try {
168116
+ const parsed = JSON.parse(trimmed);
168117
+ if ("pid" in parsed) {
168118
+ const pid = Number(parsed.pid);
168119
+ if (!Number.isInteger(pid) || pid <= 0)
168120
+ return "invalid-pid";
168121
+ }
168122
+ } catch {}
168123
+ }
168124
+ return "parse-invalid";
168125
+ }
168126
+ function classifyJunkDiscovery(portFile, raw, staleFiles) {
168127
+ let mtimeMs;
168128
+ try {
168129
+ mtimeMs = rpcDiscoveryFs.statSync(portFile).mtimeMs;
168130
+ } catch (error51) {
168131
+ if (error51.code === "ENOENT")
168132
+ return null;
168133
+ return unreadableDiscovery(portFile, "io");
168134
+ }
168135
+ const ageMs = Date.now() - mtimeMs;
168136
+ if (!Number.isFinite(ageMs) || ageMs < RPC_DISCOVERY_PARSE_GRACE_MS) {
168137
+ return unreadableDiscovery(portFile, "parse");
168138
+ }
168139
+ staleFiles.push(portFile);
168140
+ const reason = invalidDiscoveryReason(raw);
168141
+ log3(`[magic-context] removing stale RPC discovery file ${portFile}: ${reason} record older than 10 minutes`);
168142
+ return null;
168143
+ }
168037
168144
  function inspectRpcServerDiscovery(storageDir) {
168038
168145
  const rpcRoot = join12(storageDir, "rpc");
168039
168146
  let projectEntries;
168040
168147
  try {
168041
- projectEntries = readdirSync2(rpcRoot, { withFileTypes: true });
168148
+ projectEntries = rpcDiscoveryFs.readdirSync(rpcRoot, { withFileTypes: true });
168042
168149
  } catch (error51) {
168043
168150
  if (error51.code === "ENOENT") {
168044
168151
  return { state: "absent", serverPids: [], staleFiles: [] };
168045
168152
  }
168046
- return unreadableDiscovery(rpcRoot);
168153
+ return unreadableDiscovery(rpcRoot, "io");
168047
168154
  }
168048
168155
  const portFiles = [];
168049
168156
  for (const projectEntry of projectEntries) {
@@ -168052,9 +168159,11 @@ function inspectRpcServerDiscovery(storageDir) {
168052
168159
  const projectDir = join12(rpcRoot, projectEntry.name);
168053
168160
  let entries;
168054
168161
  try {
168055
- entries = readdirSync2(projectDir);
168056
- } catch {
168057
- return unreadableDiscovery(projectDir);
168162
+ entries = rpcDiscoveryFs.readdirSync(projectDir);
168163
+ } catch (error51) {
168164
+ if (error51.code === "ENOENT")
168165
+ continue;
168166
+ return unreadableDiscovery(projectDir, "io");
168058
168167
  }
168059
168168
  for (const entry of entries) {
168060
168169
  if (entry === "port" || entry.startsWith("port-") && entry.endsWith(".json")) {
@@ -168070,15 +168179,21 @@ function inspectRpcServerDiscovery(storageDir) {
168070
168179
  for (const portFile of portFiles) {
168071
168180
  let raw;
168072
168181
  try {
168073
- raw = readFileSync10(portFile, "utf8");
168074
- } catch {
168075
- return unreadableDiscovery(portFile);
168182
+ raw = rpcDiscoveryFs.readFileSync(portFile, "utf8");
168183
+ } catch (error51) {
168184
+ if (error51.code === "ENOENT")
168185
+ continue;
168186
+ return unreadableDiscovery(portFile, "io");
168076
168187
  }
168077
168188
  const filename = basename3(portFile);
168078
168189
  const pidFromName = /^port-(\d+)/.exec(filename)?.[1];
168079
- const record2 = parseRpcPortFile(raw, pidFromName ? Number(pidFromName) : 0);
168190
+ const fallbackPid = pidFromName ? Number(pidFromName) : 0;
168191
+ const record2 = parseRpcPortFile(raw, fallbackPid);
168080
168192
  if (!record2 || !Number.isInteger(record2.pid) || record2.pid <= 0) {
168081
- return unreadableDiscovery(portFile);
168193
+ const junk = classifyJunkDiscovery(portFile, raw, staleFiles);
168194
+ if (junk)
168195
+ return junk;
168196
+ continue;
168082
168197
  }
168083
168198
  if (isPidAlive(record2.pid) && isPidIdentityPlausible(record2))
168084
168199
  pids.add(record2.pid);
@@ -168087,9 +168202,9 @@ function inspectRpcServerDiscovery(storageDir) {
168087
168202
  }
168088
168203
  for (const staleFile of staleFiles) {
168089
168204
  try {
168090
- unlinkSync2(staleFile);
168205
+ rpcDiscoveryFs.unlinkSync(staleFile);
168091
168206
  } catch {
168092
- return unreadableDiscovery(staleFile);
168207
+ return unreadableDiscovery(staleFile, "io");
168093
168208
  }
168094
168209
  }
168095
168210
  const serverPids = [...pids].sort((a3, b2) => a3 - b2);
@@ -168104,7 +168219,7 @@ function getLiveMigrationBlockingProcesses(storageDir) {
168104
168219
  return [];
168105
168220
  return discovery.serverPids.map((pid) => ({ harness: "OpenCode server", pid }));
168106
168221
  }
168107
- var databases, pendingAsyncOpens, persistenceByDatabase, persistenceErrorByDatabase, pathByDatabase, LATEST_SUPPORTED_VERSION = 74, PERMISSIONS_ENFORCEABLE;
168222
+ var databases, pendingAsyncOpens, persistenceByDatabase, persistenceErrorByDatabase, pathByDatabase, LATEST_SUPPORTED_VERSION = 75, PERMISSIONS_ENFORCEABLE, RPC_DISCOVERY_PARSE_GRACE_MS, defaultRpcDiscoveryFs, rpcDiscoveryFs;
168108
168223
  var init_storage_db = __esm(async () => {
168109
168224
  init_data_path();
168110
168225
  init_logger();
@@ -168122,6 +168237,14 @@ var init_storage_db = __esm(async () => {
168122
168237
  persistenceErrorByDatabase = new WeakMap;
168123
168238
  pathByDatabase = new WeakMap;
168124
168239
  PERMISSIONS_ENFORCEABLE = process.platform !== "win32";
168240
+ RPC_DISCOVERY_PARSE_GRACE_MS = 10 * 60 * 1000;
168241
+ defaultRpcDiscoveryFs = {
168242
+ readdirSync: (path4, options) => options?.withFileTypes ? readdirSync2(path4, { withFileTypes: true }) : readdirSync2(path4),
168243
+ readFileSync: (path4, encoding) => String(readFileSync10(path4, encoding)),
168244
+ statSync: (path4) => ({ mtimeMs: statSync7(path4).mtimeMs }),
168245
+ unlinkSync: (path4) => unlinkSync2(path4)
168246
+ };
168247
+ rpcDiscoveryFs = defaultRpcDiscoveryFs;
168125
168248
  });
168126
168249
 
168127
168250
  // src/lib/database-access.ts
@@ -170141,7 +170264,7 @@ import {
170141
170264
  mkdirSync as mkdirSync8,
170142
170265
  readFileSync as readFileSync11,
170143
170266
  renameSync as renameSync3,
170144
- statSync as statSync7,
170267
+ statSync as statSync8,
170145
170268
  writeFileSync as writeFileSync6
170146
170269
  } from "node:fs";
170147
170270
  import { dirname as dirname11, join as join17 } from "node:path";
@@ -170177,8 +170300,8 @@ function writeTuiConfigAtomic(configPath, config2) {
170177
170300
  const tmpPath = `${configPath}.tmp`;
170178
170301
  writeFileSync6(tmpPath, body);
170179
170302
  try {
170180
- if (statSync7(configPath, { throwIfNoEntry: false })?.isFile()) {
170181
- chmodSync3(tmpPath, statSync7(configPath).mode & 511);
170303
+ if (statSync8(configPath, { throwIfNoEntry: false })?.isFile()) {
170304
+ chmodSync3(tmpPath, statSync8(configPath).mode & 511);
170182
170305
  }
170183
170306
  } catch {}
170184
170307
  renameSync3(tmpPath, configPath);
@@ -170458,7 +170581,7 @@ var init_redaction2 = __esm(() => {
170458
170581
  });
170459
170582
 
170460
170583
  // src/lib/diagnostics-opencode.ts
170461
- import { existsSync as existsSync20, readdirSync as readdirSync3, readFileSync as readFileSync12, statSync as statSync8 } from "node:fs";
170584
+ import { existsSync as existsSync20, readdirSync as readdirSync3, readFileSync as readFileSync12, statSync as statSync9 } from "node:fs";
170462
170585
  import { createRequire as createRequire2 } from "node:module";
170463
170586
  import { homedir as homedir14 } from "node:os";
170464
170587
  import { join as join18 } from "node:path";
@@ -170497,7 +170620,7 @@ function getStorageDir() {
170497
170620
  }
170498
170621
  function fileSize(path6) {
170499
170622
  try {
170500
- return existsSync20(path6) ? statSync8(path6).size : 0;
170623
+ return existsSync20(path6) ? statSync9(path6).size : 0;
170501
170624
  } catch {
170502
170625
  return 0;
170503
170626
  }
@@ -170574,7 +170697,7 @@ function listDumpsInDir(dir, limit) {
170574
170697
  return { count: 0, recent: [] };
170575
170698
  try {
170576
170699
  const entries = readdirSync3(dir).filter((name2) => name2.endsWith(".xml")).map((name2) => {
170577
- const stat = statSync8(join18(dir, name2));
170700
+ const stat = statSync9(join18(dir, name2));
170578
170701
  return {
170579
170702
  name: name2,
170580
170703
  mtime: stat.mtimeMs,
@@ -170780,7 +170903,7 @@ async function collectDiagnostics() {
170780
170903
  const storageDirPath = getStorageDir();
170781
170904
  const contextDbPath = join18(storageDirPath, "context.db");
170782
170905
  const logPath = getMagicContextLogPath2("opencode");
170783
- const logFileSize = existsSync20(logPath) ? statSync8(logPath).size : 0;
170906
+ const logFileSize = existsSync20(logPath) ? statSync9(logPath).size : 0;
170784
170907
  let compactionEnabled = false;
170785
170908
  try {
170786
170909
  compactionEnabled = isCompactionEnabled(loadPluginConfig(process.cwd()));
@@ -171679,6 +171802,15 @@ function formatFailClosedBlockingProcesses(processes) {
171679
171802
  }
171680
171803
  function formatFailClosedBlockingMessage(reason) {
171681
171804
  if (reason.kind === "migration_guard") {
171805
+ if (reason.unreadableFile) {
171806
+ const arm = reason.unreadableArm ?? "io";
171807
+ const recovery = arm === "io" ? `If no OpenCode server is running, it is safe to delete ${reason.unreadableFile} and retry.` : `The file may be a recent incomplete write; retry after the file is older than the ten-minute grace window, or stop OpenCode before deleting it.`;
171808
+ return [
171809
+ `Magic Context cannot migrate the shared database because RPC discovery file ${reason.unreadableFile} is uncertain (${arm} arm), so the absence of a live OpenCode server cannot be proven.`,
171810
+ recovery,
171811
+ `Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
171812
+ ].join(" ");
171813
+ }
171682
171814
  return [
171683
171815
  `Magic Context cannot migrate the shared database because ${formatFailClosedBlockingProcesses(reason.blockingProcesses)} may be running an older Magic Context build that would fail against the migrated database.`,
171684
171816
  "Restart the blocking process (it will pick up the new build and migrate on start), or shut it down and retry.",
@@ -171836,7 +171968,7 @@ var init_doctor_opencode_cache = __esm(() => {
171836
171968
 
171837
171969
  // src/commands/doctor-opencode.ts
171838
171970
  import { execSync as execSync2, spawnSync as spawnSync3 } from "node:child_process";
171839
- import { existsSync as existsSync23, readFileSync as readFileSync15, statSync as statSync9 } from "node:fs";
171971
+ import { existsSync as existsSync23, readFileSync as readFileSync15, statSync as statSync10 } from "node:fs";
171840
171972
  import { createRequire as createRequire4 } from "node:module";
171841
171973
  import { homedir as homedir15 } from "node:os";
171842
171974
  import { join as join21 } from "node:path";
@@ -172704,7 +172836,7 @@ async function runDoctor(options = {}) {
172704
172836
  }
172705
172837
  const logPath = getMagicContextLogPath2("opencode");
172706
172838
  if (existsSync23(logPath)) {
172707
- const logStat = statSync9(logPath);
172839
+ const logStat = statSync10(logPath);
172708
172840
  const sizeKb = (logStat.size / 1024).toFixed(0);
172709
172841
  log2.info(`Log file: ${logPath} (${sizeKb} KB)`);
172710
172842
  } else {
@@ -172989,6 +173121,7 @@ function loadPiConfig(opts = {}) {
172989
173121
  warnings.push(...parsed.warnings.map((warning) => `[merged config] ${warning}`));
172990
173122
  return {
172991
173123
  config: parsed.config,
173124
+ registrationPromptSurface: trustedBaseConfig.prompt_surface,
172992
173125
  warnings,
172993
173126
  loadedFromPaths: loadedFiles.map((loaded) => loaded.path)
172994
173127
  };
@@ -173007,7 +173140,7 @@ var init_config2 = __esm(() => {
173007
173140
 
173008
173141
  // src/lib/diagnostics-pi.ts
173009
173142
  import { createHash as createHash4 } from "node:crypto";
173010
- import { existsSync as existsSync25, readdirSync as readdirSync4, readFileSync as readFileSync17, statSync as statSync10 } from "node:fs";
173143
+ import { existsSync as existsSync25, readdirSync as readdirSync4, readFileSync as readFileSync17, statSync as statSync11 } from "node:fs";
173011
173144
  import { createRequire as createRequire5 } from "node:module";
173012
173145
  import { homedir as homedir16, userInfo as userInfo2 } from "node:os";
173013
173146
  import { join as join22 } from "node:path";
@@ -173025,7 +173158,7 @@ function getSelfVersion3() {
173025
173158
  }
173026
173159
  function fileSize2(path6) {
173027
173160
  try {
173028
- return existsSync25(path6) ? statSync10(path6).size : 0;
173161
+ return existsSync25(path6) ? statSync11(path6).size : 0;
173029
173162
  } catch {
173030
173163
  return 0;
173031
173164
  }
@@ -173133,7 +173266,7 @@ function collectPiRecentSessions() {
173133
173266
  }
173134
173267
  for (const file2 of files) {
173135
173268
  try {
173136
- const mtime = statSync10(join22(slugDir, file2)).mtimeMs;
173269
+ const mtime = statSync11(join22(slugDir, file2)).mtimeMs;
173137
173270
  const sessionId = file2.replace(/\.jsonl$/, "");
173138
173271
  candidates.push({ sessionId, directory, mtime });
173139
173272
  } catch {}
@@ -173188,7 +173321,7 @@ function listDumpsInDir2(dir, limit) {
173188
173321
  return { count: 0, recent: [] };
173189
173322
  try {
173190
173323
  const entries = readdirSync4(dir).filter((name2) => name2.endsWith(".xml")).map((name2) => {
173191
- const stat = statSync10(join22(dir, name2));
173324
+ const stat = statSync11(join22(dir, name2));
173192
173325
  return {
173193
173326
  name: name2,
173194
173327
  mtime: stat.mtimeMs,
@@ -173262,7 +173395,7 @@ async function collectDiagnostics2(cwd = process.cwd()) {
173262
173395
  const storageDirPath = getMagicContextStorageDir();
173263
173396
  const dbPath = join22(storageDirPath, "context.db");
173264
173397
  const logPath = getMagicContextLogPath2("pi");
173265
- const logFileSize = existsSync25(logPath) ? statSync10(logPath).size : 0;
173398
+ const logFileSize = existsSync25(logPath) ? statSync11(logPath).size : 0;
173266
173399
  const otherPiExtensions = packages.filter((entry) => !isPiMagicContextPackageEntry(entry)).map(describePiPackageEntry);
173267
173400
  const recentSessions = collectPiRecentSessions();
173268
173401
  const historianDumps = collectPiHistorianDumps(recentSessions);
@@ -173474,7 +173607,7 @@ var init_logs_pi = __esm(() => {
173474
173607
 
173475
173608
  // src/commands/doctor-pi.ts
173476
173609
  import { execFileSync as execFileSync5, spawnSync as spawnSync4 } from "node:child_process";
173477
- import { existsSync as existsSync26, mkdirSync as mkdirSync9, readdirSync as readdirSync5, readFileSync as readFileSync19, rmSync as rmSync3, statSync as statSync11 } from "node:fs";
173610
+ import { existsSync as existsSync26, mkdirSync as mkdirSync9, readdirSync as readdirSync5, readFileSync as readFileSync19, rmSync as rmSync3, statSync as statSync12 } from "node:fs";
173478
173611
  import { createRequire as createRequire6 } from "node:module";
173479
173612
  import { homedir as homedir17 } from "node:os";
173480
173613
  import { dirname as dirname13, isAbsolute as isAbsolute4, join as join24 } from "node:path";
@@ -173690,7 +173823,7 @@ function findPiMagicContextCacheDirs(cwd, expectedVersion, force = false) {
173690
173823
  return;
173691
173824
  let stat;
173692
173825
  try {
173693
- stat = statSync11(path6);
173826
+ stat = statSync12(path6);
173694
173827
  } catch {
173695
173828
  return;
173696
173829
  }
@@ -173953,7 +174086,7 @@ async function runHealthChecks(options) {
173953
174086
  }
173954
174087
  const logPath = getMagicContextLogPath2("pi");
173955
174088
  if (existsSync26(logPath)) {
173956
- const stat = statSync11(logPath);
174089
+ const stat = statSync12(logPath);
173957
174090
  const sizeKb = (stat.size / 1024).toFixed(0);
173958
174091
  const lines = readFileSync19(logPath, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
173959
174092
  add(results, "info", `Log file: ${logPath} (${sizeKb} KB)`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/magic-context",
3
- "version": "0.34.1",
3
+ "version": "0.35.0",
4
4
  "description": "Unified CLI for Magic Context — setup, doctor, and migration across OpenCode and Pi",
5
5
  "keywords": [
6
6
  "opencode",