@cortexkit/pi-magic-context 0.22.0 → 0.22.2

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.
@@ -86,9 +86,6 @@ function getMagicContextStorageDir() {
86
86
  function getLegacyOpenCodeMagicContextStorageDir() {
87
87
  return path.join(getOpenCodeStorageDir(), "plugin", "magic-context");
88
88
  }
89
- function getCacheDir() {
90
- return process.env.XDG_CACHE_HOME ?? path.join(os.homedir(), ".cache");
91
- }
92
89
  var init_data_path = () => {};
93
90
 
94
91
  // ../plugin/src/shared/logger.ts
@@ -8919,6 +8916,38 @@ var MIGRATIONS = [
8919
8916
  ON tags(session_id, entry_fingerprint)
8920
8917
  WHERE type='message' AND entry_fingerprint IS NOT NULL`);
8921
8918
  }
8919
+ },
8920
+ {
8921
+ version: 28,
8922
+ description: "Add git commit sweep coordinator lease/cooldown table",
8923
+ up: (db) => {
8924
+ db.exec(`
8925
+ CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
8926
+ project_path TEXT PRIMARY KEY,
8927
+ lease_holder TEXT,
8928
+ lease_expires_at INTEGER,
8929
+ last_swept_at INTEGER
8930
+ );
8931
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
8932
+ ON git_sweep_coordinator(lease_expires_at);
8933
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
8934
+ ON git_sweep_coordinator(last_swept_at);
8935
+ `);
8936
+ }
8937
+ },
8938
+ {
8939
+ version: 29,
8940
+ description: "Add anchor_ordinal to notes (traceback to the conversation tail)",
8941
+ up: (db) => {
8942
+ const notesExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='notes'").get();
8943
+ if (!notesExists) {
8944
+ return;
8945
+ }
8946
+ const columns = db.prepare("PRAGMA table_info(notes)").all();
8947
+ if (!columns.some((column) => column.name === "anchor_ordinal")) {
8948
+ db.exec("ALTER TABLE notes ADD COLUMN anchor_ordinal INTEGER");
8949
+ }
8950
+ }
8922
8951
  }
8923
8952
  ];
8924
8953
  var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -141883,7 +141912,7 @@ var databases = new Map;
141883
141912
  var persistenceByDatabase = new WeakMap;
141884
141913
  var persistenceErrorByDatabase = new WeakMap;
141885
141914
  var lastSchemaFenceRejection = null;
141886
- var LATEST_SUPPORTED_VERSION = 27;
141915
+ var LATEST_SUPPORTED_VERSION = 29;
141887
141916
  function resolveDatabasePath(dbPathOverride) {
141888
141917
  if (dbPathOverride) {
141889
141918
  return { dbDir: dirname2(dbPathOverride), dbPath: dbPathOverride };
@@ -142203,6 +142232,17 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
142203
142232
  updated_at INTEGER NOT NULL DEFAULT 0
142204
142233
  );
142205
142234
 
142235
+ CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
142236
+ project_path TEXT PRIMARY KEY,
142237
+ lease_holder TEXT,
142238
+ lease_expires_at INTEGER,
142239
+ last_swept_at INTEGER
142240
+ );
142241
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
142242
+ ON git_sweep_coordinator(lease_expires_at);
142243
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
142244
+ ON git_sweep_coordinator(last_swept_at);
142245
+
142206
142246
  CREATE TABLE IF NOT EXISTS m0_mutation_log (
142207
142247
  id INTEGER PRIMARY KEY AUTOINCREMENT,
142208
142248
  session_id TEXT NOT NULL,
@@ -157156,25 +157196,26 @@ var PermissionSchema = exports_external.object({
157156
157196
  external_directory: PermissionValueSchema.optional()
157157
157197
  }).optional();
157158
157198
  var AgentOverrideConfigSchema = exports_external.object({
157159
- model: exports_external.string().optional(),
157160
- temperature: exports_external.number().min(0).max(2).optional(),
157161
- top_p: exports_external.number().min(0).max(1).optional(),
157162
- prompt: exports_external.string().optional(),
157163
- tools: exports_external.record(exports_external.string(), exports_external.boolean()).optional(),
157164
- disable: exports_external.boolean().optional(),
157165
- description: exports_external.string().optional(),
157166
- mode: exports_external.enum(["subagent", "primary", "all"]).optional(),
157167
- color: exports_external.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
157168
- maxSteps: exports_external.number().optional(),
157169
- permission: PermissionSchema,
157170
- maxTokens: exports_external.number().optional(),
157171
- variant: exports_external.string().optional(),
157172
- fallback_models: exports_external.union([exports_external.string(), exports_external.array(exports_external.string())]).optional()
157199
+ model: exports_external.string().optional().describe("Primary model ID (e.g. 'claude-sonnet-4-6')"),
157200
+ temperature: exports_external.number().min(0).max(2).optional().describe("Sampling temperature (0-2)"),
157201
+ top_p: exports_external.number().min(0).max(1).optional().describe("Nucleus sampling top_p (0-1)"),
157202
+ prompt: exports_external.string().optional().describe("Additional system prompt text"),
157203
+ tools: exports_external.record(exports_external.string(), exports_external.boolean()).optional().describe("Tool enable/disable overrides"),
157204
+ disable: exports_external.boolean().optional().describe("Disable this agent"),
157205
+ description: exports_external.string().optional().describe("Agent description"),
157206
+ mode: exports_external.enum(["subagent", "primary", "all"]).optional().describe("Agent mode (subagent, primary, or all)"),
157207
+ color: exports_external.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe("Hex color for the agent (e.g. '#a1b2c3')"),
157208
+ maxSteps: exports_external.number().optional().describe("Maximum tool-call steps per invocation"),
157209
+ permission: PermissionSchema.describe("Per-tool permission overrides"),
157210
+ maxTokens: exports_external.number().optional().describe("Maximum output tokens"),
157211
+ variant: exports_external.string().optional().describe("OpenCode reasoning variant (e.g. for extended thinking)"),
157212
+ fallback_models: exports_external.union([exports_external.string(), exports_external.array(exports_external.string())]).optional().describe("Fallback model IDs if primary is unavailable")
157173
157213
  });
157174
157214
 
157175
157215
  // ../plugin/src/config/schema/magic-context.ts
157176
157216
  var DEFAULT_NUDGE_INTERVAL_TOKENS = 1e4;
157177
157217
  var DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65;
157218
+ var EXECUTE_THRESHOLD_CAP_MESSAGE = "execute_threshold is capped at 80% for cache safety: a single large agent step can overflow the context window before Magic Context can compact between turns, forcing OpenCode's native compaction (hard to recover from). 80% also leaves headroom below the 85%/95% emergency bands. Use a value between 20 and 80.";
157178
157219
  var DEFAULT_HISTORIAN_TIMEOUT_MS = 300000;
157179
157220
  var DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15;
157180
157221
  var DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2";
@@ -157194,36 +157235,38 @@ var DEFAULT_DREAMER_TASKS = [
157194
157235
  ];
157195
157236
  var PiThinkingLevelSchema = exports_external.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional();
157196
157237
  var DreamerConfigSchema = AgentOverrideConfigSchema.merge(exports_external.object({
157197
- schedule: exports_external.string().default("02:00-06:00"),
157198
- max_runtime_minutes: exports_external.number().min(10).default(120),
157199
- tasks: exports_external.array(DreamingTaskSchema).default(DEFAULT_DREAMER_TASKS),
157200
- task_timeout_minutes: exports_external.number().min(5).default(20),
157201
- inject_docs: exports_external.boolean().default(true),
157238
+ schedule: exports_external.string().default("02:00-06:00").describe("Scheduled window for overnight dreaming (e.g. '02:00-06:00')"),
157239
+ max_runtime_minutes: exports_external.number().min(10).default(120).describe("Maximum runtime per dream session in minutes"),
157240
+ tasks: exports_external.array(DreamingTaskSchema).default(DEFAULT_DREAMER_TASKS).describe("Tasks to run during dreaming, in order"),
157241
+ task_timeout_minutes: exports_external.number().min(5).default(20).describe("Minutes allocated per task before moving to next"),
157242
+ inject_docs: exports_external.boolean().default(true).describe("Inject ARCHITECTURE.md and STRUCTURE.md into system prompt"),
157202
157243
  user_memories: exports_external.object({
157203
- enabled: exports_external.boolean().default(true),
157204
- promotion_threshold: exports_external.number().min(2).max(20).default(3)
157205
- }).default({ enabled: true, promotion_threshold: 3 }),
157244
+ enabled: exports_external.boolean().default(true).describe("Enable user memory extraction and promotion (default: true)"),
157245
+ promotion_threshold: exports_external.number().min(2).max(20).default(3).describe("Minimum candidate observations before dreamer considers promotion (default: 3)")
157246
+ }).default({ enabled: true, promotion_threshold: 3 }).describe("User memory pipeline: historian extracts behavior observations from each compartment run; dreamer reviews recurring patterns and promotes them to stable user memories injected into all sessions as <user-profile>. Requires dreamer to not be disabled for promotion to actually happen. Graduated from experimental in v0.14. Default: enabled."),
157206
157247
  pin_key_files: exports_external.object({
157207
- enabled: exports_external.boolean().default(false),
157208
- token_budget: exports_external.number().min(2000).max(30000).default(1e4),
157209
- min_reads: exports_external.number().min(2).max(20).default(4)
157210
- }).default({ enabled: false, token_budget: 1e4, min_reads: 4 }),
157211
- thinking_level: PiThinkingLevelSchema
157248
+ enabled: exports_external.boolean().default(false).describe("Enable key file pinning (default: false)"),
157249
+ token_budget: exports_external.number().min(2000).max(30000).default(1e4).describe("Total token budget for all pinned key files (min: 2000, max: 30000, default: 10000)"),
157250
+ min_reads: exports_external.number().min(2).max(20).default(4).describe("Minimum full-read count before a file is considered for pinning (min: 2, default: 4)")
157251
+ }).default({ enabled: false, token_budget: 1e4, min_reads: 4 }).describe("Pin frequently-read key files into the system prompt so the agent doesn't need to re-read them after context drops. Dreamer identifies key files per session based on read patterns. Requires dreamer to not be disabled for selection to happen. Graduated from experimental in v0.14. Default: disabled."),
157252
+ thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level for dreamer subagent invocations. See historian.thinking_level.")
157212
157253
  }));
157213
157254
  var SidekickConfigSchema = AgentOverrideConfigSchema.extend({
157214
- timeout_ms: exports_external.number().default(30000),
157215
- system_prompt: exports_external.string().optional(),
157216
- thinking_level: PiThinkingLevelSchema
157255
+ timeout_ms: exports_external.number().default(30000).describe("Timeout for sidekick calls in milliseconds"),
157256
+ system_prompt: exports_external.string().optional().describe("Custom system prompt for sidekick"),
157257
+ thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level for sidekick subagent invocations. See historian.thinking_level.")
157217
157258
  }).optional();
157218
157259
  var HistorianConfigSchema = AgentOverrideConfigSchema.extend({
157219
- two_pass: exports_external.boolean().default(false),
157220
- thinking_level: PiThinkingLevelSchema
157260
+ two_pass: exports_external.boolean().default(false).describe("Run a second editor pass over historian output to clean low-signal U: lines and cross-compartment duplicates. Adds ~1 extra API call and ~1.3x cost per historian run. Useful for models without extended thinking support. (default: false)"),
157261
+ thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level passed as --thinking <level> to Pi historian subagent invocations. Required when using reasoning models (e.g. github-copilot/gpt-5.4) because Pi's default thinking-level resolution can pick a value the provider rejects. OpenCode users set variant instead. Valid: off | minimal | low | medium | high | xhigh")
157221
157262
  }).optional();
157222
157263
  var BaseEmbeddingConfigSchema = exports_external.object({
157223
- provider: exports_external.enum(["local", "openai-compatible", "off"]).default("local"),
157224
- model: exports_external.string().optional(),
157225
- endpoint: exports_external.string().optional(),
157226
- api_key: exports_external.string().optional()
157264
+ provider: exports_external.enum(["local", "openai-compatible", "off"]).default("local").describe("Embedding provider. 'local' uses Xenova/all-MiniLM-L6-v2, 'openai-compatible' requires endpoint and model, 'off' disables embeddings."),
157265
+ model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible, ignored for local."),
157266
+ endpoint: exports_external.string().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
157267
+ api_key: exports_external.string().optional().describe("API key for remote embedding provider (optional)"),
157268
+ input_type: exports_external.string().optional().describe("Optional input_type sent in the embedding request body. Required by some openai-compatible providers (e.g. NVIDIA NIM expects 'query' or 'passage'). Omitted from the request when unset."),
157269
+ truncate: exports_external.string().optional().describe("Optional truncate mode sent in the embedding request body (e.g. NVIDIA NIM accepts 'NONE' | 'START' | 'END'). Omitted from the request when unset.")
157227
157270
  }).superRefine((data, ctx) => {
157228
157271
  if (data.provider === "openai-compatible" && !data.endpoint?.trim()) {
157229
157272
  ctx.addIssue({
@@ -157249,76 +157292,80 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
157249
157292
  }
157250
157293
  if (data.provider === "openai-compatible") {
157251
157294
  const apiKey = data.api_key?.trim();
157295
+ const inputType = data.input_type?.trim();
157296
+ const truncate = data.truncate?.trim();
157252
157297
  return {
157253
157298
  provider: "openai-compatible",
157254
157299
  model: data.model?.trim() ?? "",
157255
157300
  endpoint: data.endpoint?.trim() ?? "",
157256
- ...apiKey ? { api_key: apiKey } : {}
157301
+ ...apiKey ? { api_key: apiKey } : {},
157302
+ ...inputType ? { input_type: inputType } : {},
157303
+ ...truncate ? { truncate } : {}
157257
157304
  };
157258
157305
  }
157259
157306
  return { provider: "off" };
157260
157307
  });
157261
157308
  var MagicContextConfigSchema = exports_external.object({
157262
- enabled: exports_external.boolean().default(true),
157263
- auto_update: exports_external.boolean().optional(),
157264
- ctx_reduce_enabled: exports_external.boolean().default(true),
157265
- historian: HistorianConfigSchema,
157266
- dreamer: DreamerConfigSchema.optional(),
157267
- cache_ttl: exports_external.union([exports_external.string(), exports_external.object({ default: exports_external.string() }).catchall(exports_external.string())]).default("5m"),
157268
- nudge_interval_tokens: exports_external.number().min(1000).default(DEFAULT_NUDGE_INTERVAL_TOKENS),
157309
+ enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
157310
+ auto_update: exports_external.boolean().optional().describe("Enable automatic npm self-update checks for the OpenCode plugin. Security: USER-only in config loader, so hostile project configs cannot suppress updates."),
157311
+ ctx_reduce_enabled: exports_external.boolean().default(true).describe("When false, ctx_reduce tool is hidden, all nudges disabled, and prompt guidance about ctx_reduce stripped. Heuristic cleanup, compartments, memory, and other features still work. (default: true)"),
157312
+ historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
157313
+ dreamer: DreamerConfigSchema.optional().describe("Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)"),
157314
+ 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") or per-model object ({ default: "5m", "model-id": "10m" })'),
157315
+ nudge_interval_tokens: exports_external.number().min(1000).default(DEFAULT_NUDGE_INTERVAL_TOKENS).describe("Minimum token growth between low-priority rolling nudges (default: DEFAULT_NUDGE_INTERVAL_TOKENS)"),
157269
157316
  execute_threshold_percentage: exports_external.union([
157270
- exports_external.number().min(20).max(80),
157271
- exports_external.object({ default: exports_external.number().min(20).max(80) }).catchall(exports_external.number().min(20).max(80))
157272
- ]).default(DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE),
157317
+ exports_external.number().min(20).max(80, EXECUTE_THRESHOLD_CAP_MESSAGE),
157318
+ exports_external.object({ default: exports_external.number().min(20).max(80, EXECUTE_THRESHOLD_CAP_MESSAGE) }).catchall(exports_external.number().min(20).max(80, EXECUTE_THRESHOLD_CAP_MESSAGE))
157319
+ ]).default(DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE).describe('Context percentage that forces queued operations to execute. Number or per-model object ({ default: 65, "provider/model": 45 }). Values above 80 are rejected because the runtime caps at 80% for cache safety (MAX_EXECUTE_THRESHOLD). Default: DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE'),
157273
157320
  execute_threshold_tokens: exports_external.object({
157274
157321
  default: exports_external.number().min(5000).max(2000000).optional()
157275
- }).catchall(exports_external.number().min(5000).max(2000000)).optional(),
157276
- protected_tags: exports_external.number().min(1).max(100).optional(),
157277
- auto_drop_tool_age: exports_external.number().min(10).default(100),
157278
- drop_tool_structure: exports_external.boolean().default(true),
157279
- clear_reasoning_age: exports_external.number().min(10).default(50),
157280
- iteration_nudge_threshold: exports_external.number().min(5).default(15),
157281
- history_budget_percentage: exports_external.number().min(0.05).max(0.5).default(DEFAULT_HISTORY_BUDGET_PERCENTAGE),
157282
- historian_timeout_ms: exports_external.number().min(60000).default(DEFAULT_HISTORIAN_TIMEOUT_MS),
157322
+ }).catchall(exports_external.number().min(5000).max(2000000)).optional().describe("Absolute token thresholds per model. When matched, overrides execute_threshold_percentage for that model. Accepts `default` for all models or per-model keys. Values above 80% × context_limit are clamped with a warning log. Min 5_000, max 2_000_000."),
157323
+ protected_tags: exports_external.number().min(1).max(100).optional().describe("Number of recent tags to protect from dropping (min: 1, max: 100, default: 20)"),
157324
+ auto_drop_tool_age: exports_external.number().min(10).default(100).describe("Auto-drop tool outputs older than N tags during queue execution (default: 100)"),
157325
+ drop_tool_structure: exports_external.boolean().default(true).describe("When true, dropped tool parts are fully removed instead of truncated in place (default: true)"),
157326
+ clear_reasoning_age: exports_external.number().min(10).default(50).describe("Clear reasoning/thinking blocks older than N tags (default: 50)"),
157327
+ iteration_nudge_threshold: exports_external.number().min(5).default(15).describe("Number of consecutive assistant messages without user input to trigger iteration nudge (default: 15)"),
157328
+ history_budget_percentage: exports_external.number().min(0.05).max(0.5).default(DEFAULT_HISTORY_BUDGET_PERCENTAGE).describe("Fraction of usable context (context_limit × execute_threshold) reserved for the session history block (default: 0.15)"),
157329
+ historian_timeout_ms: exports_external.number().min(60000).default(DEFAULT_HISTORIAN_TIMEOUT_MS).describe("Timeout for each historian prompt call in milliseconds (default: 300000)"),
157283
157330
  commit_cluster_trigger: exports_external.object({
157284
- enabled: exports_external.boolean().default(true),
157285
- min_clusters: exports_external.number().min(1).default(3)
157286
- }).default({ enabled: true, min_clusters: 3 }),
157331
+ enabled: exports_external.boolean().default(true).describe("Enable commit-cluster based historian triggering (default: true)"),
157332
+ min_clusters: exports_external.number().min(1).default(3).describe("Minimum commit clusters required to trigger historian (min: 1, default: 3)")
157333
+ }).default({ enabled: true, min_clusters: 3 }).describe("Commit-cluster trigger: fire historian when enough commit clusters accumulate in the unsummarized tail"),
157287
157334
  system_prompt_injection: exports_external.object({
157288
- enabled: exports_external.boolean().default(true),
157289
- skip_signatures: exports_external.array(exports_external.string()).default(["<!-- magic-context: skip -->"])
157335
+ enabled: exports_external.boolean().default(true).describe("When false, NO injection happens for ANY agent — global escape hatch. (default: true)"),
157336
+ skip_signatures: exports_external.array(exports_external.string()).default(["<!-- magic-context: skip -->"]).describe(`Substring opt-out list. If the agent's system prompt contains any of these strings, skip ALL Magic Context injection for that call. Default "<!-- magic-context: skip -->" is meant to be added inside a user's custom agent prompt to opt that agent out.`)
157290
157337
  }).default({
157291
157338
  enabled: true,
157292
157339
  skip_signatures: ["<!-- magic-context: skip -->"]
157293
- }),
157340
+ }).describe("Controls whether and where Magic Context augments the system prompt. Lets users opt specific agents out of the Magic Context guidance and the surrounding project-docs / user-profile / key-files blocks. OpenCode's internal hidden agents — title, summary, and compaction — are always skipped automatically."),
157294
157341
  sqlite: exports_external.object({
157295
- cache_size_mb: exports_external.number().min(2).max(2048).default(64),
157296
- mmap_size_mb: exports_external.number().min(0).max(8192).default(0)
157297
- }).default({ cache_size_mb: 64, mmap_size_mb: 0 }),
157342
+ cache_size_mb: exports_external.number().min(2).max(2048).default(64).describe("Page-cache size in MiB per connection (PRAGMA cache_size). Larger keeps more hot pages resident, cutting re-reads on repeated full-table scans. (min 2, max 2048, default 64)"),
157343
+ mmap_size_mb: exports_external.number().min(0).max(8192).default(0).describe("Memory-mapped I/O size in MiB (PRAGMA mmap_size). 0 disables mmap (SQLite default). Raising it can cut read overhead on large DBs at the cost of address space. (min 0, max 8192, default 0)")
157344
+ }).default({ cache_size_mb: 64, mmap_size_mb: 0 }).describe("SQLite connection tuning for Magic Context's own context.db. These are per-connection PRAGMAs applied at open; they do not change the schema or what is stored."),
157298
157345
  embedding: EmbeddingConfigSchema.default({
157299
157346
  provider: "local",
157300
157347
  model: DEFAULT_LOCAL_EMBEDDING_MODEL
157301
- }),
157302
- temporal_awareness: exports_external.boolean().default(true),
157348
+ }).describe("Embedding provider configuration"),
157349
+ temporal_awareness: exports_external.boolean().default(true).describe('Inject wall-clock gap markers (<!-- +Xm -->) between user messages where > 5 min elapsed since the previous message, and add start/end date attributes on compartments. Gives the agent a sense of session pacing and "how long ago" across multi-day sessions. Graduated from experimental.temporal_awareness; default: true (set false to opt out).'),
157303
157350
  caveman_text_compression: exports_external.object({
157304
- enabled: exports_external.boolean().default(false),
157305
- min_chars: exports_external.number().min(100).max(1e4).default(500)
157306
- }).default({ enabled: false, min_chars: 500 }),
157351
+ enabled: exports_external.boolean().default(false).describe("Apply deterministic caveman-style text compression to old conversation text. Only active when ctx_reduce_enabled=false. Compresses user/assistant text in oldest-first tiers: ultra (oldest 20%), full, lite, untouched (newest 40%)."),
157352
+ min_chars: exports_external.number().min(100).max(1e4).default(500).describe("Text parts shorter than this (characters) stay untouched. Min 100, max 10000. Default: 500.")
157353
+ }).default({ enabled: false, min_chars: 500 }).describe("Age-tier caveman compression for long user/assistant text parts. Only active when ctx_reduce_enabled is false. Oldest 20% of eligible tags (outside protected tail) go to ultra, next 20% to full, next 20% to lite, newest 40% untouched. Graduated from experimental.caveman_text_compression; opt-in, default off (lossy)."),
157307
157354
  memory: exports_external.object({
157308
- enabled: exports_external.boolean().default(true),
157309
- injection_budget_tokens: exports_external.number().min(500).max(20000).default(4000),
157310
- auto_promote: exports_external.boolean().default(true),
157311
- retrieval_count_promotion_threshold: exports_external.number().min(1).default(3),
157355
+ enabled: exports_external.boolean().default(true).describe("Enable cross-session memory (default: true)"),
157356
+ injection_budget_tokens: exports_external.number().min(500).max(20000).default(4000).describe("Token budget for memory injection on session start (min: 500, max: 20000, default: 4000)"),
157357
+ auto_promote: exports_external.boolean().default(true).describe("Automatically promote eligible session facts into memory (default: true)"),
157358
+ retrieval_count_promotion_threshold: exports_external.number().min(1).default(3).describe("retrieval_count threshold for promoting memory to permanent status (min: 1, default: 3)"),
157312
157359
  auto_search: exports_external.object({
157313
- enabled: exports_external.boolean().default(true),
157314
- score_threshold: exports_external.number().min(0.3).max(0.95).default(0.6),
157315
- min_prompt_chars: exports_external.number().min(5).max(500).default(20)
157316
- }).default({ enabled: true, score_threshold: 0.6, min_prompt_chars: 20 }),
157360
+ enabled: exports_external.boolean().default(true).describe("Automatically append a compact <ctx-search-hint> to eligible user messages when relevant memories, conversation, or commits are found. Graduated from experimental.auto_search; on by default (set false to opt out). Independent of memory.enabled."),
157361
+ score_threshold: exports_external.number().min(0.3).max(0.95).default(0.6).describe("Top hit score must exceed this threshold for the hint to fire (min: 0.3, max: 0.95, default: 0.60)"),
157362
+ min_prompt_chars: exports_external.number().min(5).max(500).default(20).describe("Skip hint when user message is shorter than this (min: 5, max: 500, default: 20)")
157363
+ }).default({ enabled: true, score_threshold: 0.6, min_prompt_chars: 20 }).describe("Auto-search hint: transform-time ctx_search on each new user message; when the top hit clears the threshold, append a compact <ctx-search-hint> block of vague fragments to that user message. Does NOT inject full content. Graduated from experimental.auto_search; enabled by default (set enabled: false to opt out). Independent of memory.enabled."),
157317
157364
  git_commit_indexing: exports_external.object({
157318
- enabled: exports_external.boolean().default(false),
157319
- since_days: exports_external.number().min(7).max(3650).default(365),
157320
- max_commits: exports_external.number().min(100).max(20000).default(2000)
157321
- }).default({ enabled: false, since_days: 365, max_commits: 2000 })
157365
+ enabled: exports_external.boolean().default(false).describe("Index HEAD git commits for ctx_search (git_commit source). Graduated from experimental.git_commit_indexing; opt-in, default off. Independent of memory.enabled."),
157366
+ since_days: exports_external.number().min(7).max(3650).default(365).describe("Days of HEAD history to index (min: 7, max: 3650, default: 365)"),
157367
+ max_commits: exports_external.number().min(100).max(20000).default(2000).describe("Max commits kept per project; oldest evicted (min: 100, max: 20000, default: 2000)")
157368
+ }).default({ enabled: false, since_days: 365, max_commits: 2000 }).describe("Index git commit messages from HEAD into ctx_search. Commits become a 4th searchable source alongside memories and session history. Graduated from experimental.git_commit_indexing; opt-in, default off (per-project embedding cost). Independent of memory.enabled.")
157322
157369
  }).default({
157323
157370
  enabled: true,
157324
157371
  injection_budget_tokens: 4000,
@@ -157326,8 +157373,8 @@ var MagicContextConfigSchema = exports_external.object({
157326
157373
  retrieval_count_promotion_threshold: 3,
157327
157374
  auto_search: { enabled: true, score_threshold: 0.6, min_prompt_chars: 20 },
157328
157375
  git_commit_indexing: { enabled: false, since_days: 365, max_commits: 2000 }
157329
- }),
157330
- sidekick: SidekickConfigSchema
157376
+ }).describe("Cross-session memory configuration"),
157377
+ sidekick: SidekickConfigSchema.describe("Optional sidekick agent configuration for session-start memory retrieval")
157331
157378
  }).transform((data) => {
157332
157379
  return {
157333
157380
  ...data,
@@ -157395,46 +157442,6 @@ function stripJsonComments(content) {
157395
157442
  }
157396
157443
  return result;
157397
157444
  }
157398
- function stripTrailingCommas(content) {
157399
- let result = "";
157400
- let inString = false;
157401
- let escaped = false;
157402
- for (let index = 0;index < content.length; index += 1) {
157403
- const char = content[index];
157404
- if (inString) {
157405
- result += char;
157406
- if (escaped) {
157407
- escaped = false;
157408
- } else if (char === "\\") {
157409
- escaped = true;
157410
- } else if (char === '"') {
157411
- inString = false;
157412
- }
157413
- continue;
157414
- }
157415
- if (char === '"') {
157416
- inString = true;
157417
- result += char;
157418
- continue;
157419
- }
157420
- if (char === ",") {
157421
- let lookahead = index + 1;
157422
- while (lookahead < content.length && /\s/.test(content[lookahead] ?? "")) {
157423
- lookahead += 1;
157424
- }
157425
- const next = content[lookahead];
157426
- if (next === "}" || next === "]") {
157427
- continue;
157428
- }
157429
- }
157430
- result += char;
157431
- }
157432
- return result;
157433
- }
157434
- function parseJsonc(content) {
157435
- const normalized = stripTrailingCommas(stripJsonComments(content));
157436
- return JSON.parse(normalized);
157437
- }
157438
157445
 
157439
157446
  // ../plugin/src/config/variable.ts
157440
157447
  var ENV_PATTERN = /\{env:([^}]+)\}/g;
@@ -157791,7 +157798,8 @@ function getEmbeddingProviderIdentity(config2) {
157791
157798
  provider: "openai-compatible",
157792
157799
  model: config2.model.trim(),
157793
157800
  endpoint: normalizeEndpoint(config2.endpoint),
157794
- apiKeyPresent: Boolean(config2.api_key?.trim())
157801
+ apiKeyPresent: Boolean(config2.api_key?.trim()),
157802
+ inputType: config2.input_type?.trim() || ""
157795
157803
  } : {
157796
157804
  provider: "local",
157797
157805
  model: config2.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
@@ -158157,6 +158165,8 @@ class OpenAICompatibleEmbeddingProvider {
158157
158165
  endpoint;
158158
158166
  model;
158159
158167
  apiKey;
158168
+ inputType;
158169
+ truncate;
158160
158170
  initialized = false;
158161
158171
  failureTimes = [];
158162
158172
  circuitOpenUntil = 0;
@@ -158166,6 +158176,8 @@ class OpenAICompatibleEmbeddingProvider {
158166
158176
  this.endpoint = normalizeEndpoint2(options.endpoint);
158167
158177
  this.model = options.model?.trim() ?? "";
158168
158178
  this.apiKey = options.apiKey?.trim() ?? "";
158179
+ this.inputType = options.inputType?.trim() ?? "";
158180
+ this.truncate = options.truncate?.trim() ?? "";
158169
158181
  this.modelId = getEmbeddingProviderIdentity({
158170
158182
  provider: "openai-compatible",
158171
158183
  endpoint: this.endpoint,
@@ -158222,7 +158234,9 @@ class OpenAICompatibleEmbeddingProvider {
158222
158234
  },
158223
158235
  body: JSON.stringify({
158224
158236
  model: this.model,
158225
- input: texts
158237
+ input: texts,
158238
+ ...this.inputType ? { input_type: this.inputType } : {},
158239
+ ...this.truncate ? { truncate: this.truncate } : {}
158226
158240
  }),
158227
158241
  signal: internalController.signal
158228
158242
  });
@@ -158427,7 +158441,7 @@ function getDistinctStoredModelIds(db, projectPath) {
158427
158441
  }
158428
158442
 
158429
158443
  // ../plugin/src/features/magic-context/project-embedding-registry.ts
158430
- import { createHash as createHash3 } from "node:crypto";
158444
+ import { createHash as createHash3, randomUUID } from "node:crypto";
158431
158445
  init_logger();
158432
158446
 
158433
158447
  // ../plugin/src/features/magic-context/git-commits/storage-git-commit-embeddings.ts
@@ -158531,6 +158545,125 @@ function getDistinctCommitEmbeddingModelIds(db, projectPath) {
158531
158545
  return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
158532
158546
  }
158533
158547
 
158548
+ // ../plugin/src/features/magic-context/git-commits/sweep-coordinator.ts
158549
+ var GIT_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
158550
+ var GIT_SWEEP_LEASE_TTL_MS = 5 * 60 * 1000;
158551
+ var GIT_SWEEP_LEASE_RENEWAL_MS = 60 * 1000;
158552
+ function runImmediate(db, body) {
158553
+ db.exec("BEGIN IMMEDIATE");
158554
+ let committed = false;
158555
+ try {
158556
+ const result = body();
158557
+ db.exec("COMMIT");
158558
+ committed = true;
158559
+ return result;
158560
+ } finally {
158561
+ if (!committed) {
158562
+ try {
158563
+ db.exec("ROLLBACK");
158564
+ } catch {}
158565
+ }
158566
+ }
158567
+ }
158568
+ function rowToState(row) {
158569
+ return {
158570
+ projectPath: row.project_path,
158571
+ leaseHolder: row.lease_holder,
158572
+ leaseExpiresAt: row.lease_expires_at,
158573
+ lastSweptAt: row.last_swept_at
158574
+ };
158575
+ }
158576
+ function getGitSweepCoordinatorState(db, projectPath) {
158577
+ const row = db.prepare(`SELECT project_path, lease_holder, lease_expires_at, last_swept_at
158578
+ FROM git_sweep_coordinator
158579
+ WHERE project_path = ?`).get(projectPath);
158580
+ return row ? rowToState(row) : null;
158581
+ }
158582
+ function acquireGitSweepLease(db, projectPath, holderId, options = {}) {
158583
+ const cooldownMs = options.cooldownMs ?? GIT_SWEEP_COOLDOWN_MS;
158584
+ const leaseTtlMs = options.leaseTtlMs ?? GIT_SWEEP_LEASE_TTL_MS;
158585
+ return runImmediate(db, () => {
158586
+ const now = Date.now();
158587
+ const row = getGitSweepCoordinatorState(db, projectPath);
158588
+ if (row?.leaseHolder && row.leaseExpiresAt !== null && row.leaseExpiresAt > now) {
158589
+ return {
158590
+ acquired: false,
158591
+ projectPath,
158592
+ reason: "lease_active",
158593
+ leaseHolder: row.leaseHolder,
158594
+ leaseExpiresAt: row.leaseExpiresAt,
158595
+ lastSweptAt: row.lastSweptAt,
158596
+ nextAllowedAt: null
158597
+ };
158598
+ }
158599
+ if (!options.ignoreCooldown && row?.lastSweptAt !== null && row?.lastSweptAt !== undefined) {
158600
+ const nextAllowedAt = row.lastSweptAt + cooldownMs;
158601
+ if (nextAllowedAt > now) {
158602
+ return {
158603
+ acquired: false,
158604
+ projectPath,
158605
+ reason: "cooldown_active",
158606
+ leaseHolder: row.leaseHolder,
158607
+ leaseExpiresAt: row.leaseExpiresAt,
158608
+ lastSweptAt: row.lastSweptAt,
158609
+ nextAllowedAt
158610
+ };
158611
+ }
158612
+ }
158613
+ const leaseExpiresAt = now + leaseTtlMs;
158614
+ db.prepare(`INSERT INTO git_sweep_coordinator (
158615
+ project_path,
158616
+ lease_holder,
158617
+ lease_expires_at,
158618
+ last_swept_at
158619
+ ) VALUES (?, ?, ?, NULL)
158620
+ ON CONFLICT(project_path) DO UPDATE SET
158621
+ lease_holder = excluded.lease_holder,
158622
+ lease_expires_at = excluded.lease_expires_at`).run(projectPath, holderId, leaseExpiresAt);
158623
+ return {
158624
+ acquired: true,
158625
+ projectPath,
158626
+ holderId,
158627
+ acquiredAt: now,
158628
+ leaseExpiresAt
158629
+ };
158630
+ });
158631
+ }
158632
+ function renewGitSweepLease(db, projectPath, holderId, leaseTtlMs = GIT_SWEEP_LEASE_TTL_MS) {
158633
+ return runImmediate(db, () => {
158634
+ const now = Date.now();
158635
+ const leaseExpiresAt = now + leaseTtlMs;
158636
+ const result = db.prepare(`UPDATE git_sweep_coordinator
158637
+ SET lease_expires_at = ?
158638
+ WHERE project_path = ?
158639
+ AND lease_holder = ?
158640
+ AND lease_expires_at > ?`).run(leaseExpiresAt, projectPath, holderId, now);
158641
+ return result.changes === 1;
158642
+ });
158643
+ }
158644
+ function markGitSweepSuccessAndRelease(db, projectPath, holderId) {
158645
+ return runImmediate(db, () => {
158646
+ const now = Date.now();
158647
+ const result = db.prepare(`UPDATE git_sweep_coordinator
158648
+ SET lease_holder = NULL,
158649
+ lease_expires_at = NULL,
158650
+ last_swept_at = ?
158651
+ WHERE project_path = ?
158652
+ AND lease_holder = ?
158653
+ AND lease_expires_at > ?`).run(now, projectPath, holderId, now);
158654
+ return result.changes === 1;
158655
+ });
158656
+ }
158657
+ function releaseGitSweepLease(db, projectPath, holderId) {
158658
+ runImmediate(db, () => {
158659
+ db.prepare(`UPDATE git_sweep_coordinator
158660
+ SET lease_holder = NULL,
158661
+ lease_expires_at = NULL
158662
+ WHERE project_path = ?
158663
+ AND lease_holder = ?`).run(projectPath, holderId);
158664
+ });
158665
+ }
158666
+
158534
158667
  // ../plugin/src/features/magic-context/memory/embedding-cache.ts
158535
158668
  var DEFAULT_EMBEDDING_CACHE_TTL_MS = 60000;
158536
158669
  var projectEmbeddingCache = new Map;
@@ -158851,7 +158984,9 @@ function createProvider2(config2) {
158851
158984
  return new OpenAICompatibleEmbeddingProvider({
158852
158985
  endpoint: config2.endpoint,
158853
158986
  model: config2.model,
158854
- apiKey: config2.api_key
158987
+ apiKey: config2.api_key,
158988
+ inputType: config2.input_type,
158989
+ truncate: config2.truncate
158855
158990
  });
158856
158991
  }
158857
158992
  return new LocalEmbeddingProvider(config2.model);
@@ -166024,7 +166159,8 @@ function toNote(row) {
166024
166159
  updatedAt: row.updated_at,
166025
166160
  lastCheckedAt: toNullableNumber(row.last_checked_at),
166026
166161
  readyAt: toNullableNumber(row.ready_at),
166027
- readyReason: toNullableString(row.ready_reason)
166162
+ readyReason: toNullableString(row.ready_reason),
166163
+ anchorOrdinal: toNullableNumber(row.anchor_ordinal)
166028
166164
  };
166029
166165
  }
166030
166166
  function getNoteById(db, noteId) {
@@ -166076,7 +166212,7 @@ function getNotes(db, options3 = {}) {
166076
166212
  }
166077
166213
  function addNote(db, type, options3) {
166078
166214
  const now = Date.now();
166079
- const result = type === "session" ? db.prepare("INSERT INTO notes (type, status, content, session_id, created_at, updated_at, harness) VALUES ('session', 'active', ?, ?, ?, ?, ?) RETURNING *").get(options3.content, options3.sessionId, now, now, getHarness()) : db.prepare("INSERT INTO notes (type, status, content, session_id, project_path, surface_condition, created_at, updated_at, harness) VALUES ('smart', 'pending', ?, ?, ?, ?, ?, ?, ?) RETURNING *").get(options3.content, options3.sessionId ?? null, options3.projectPath, options3.surfaceCondition, now, now, getHarness());
166215
+ const result = type === "session" ? db.prepare("INSERT INTO notes (type, status, content, session_id, created_at, updated_at, harness, anchor_ordinal) VALUES ('session', 'active', ?, ?, ?, ?, ?, ?) RETURNING *").get(options3.content, options3.sessionId, now, now, getHarness(), options3.anchorOrdinal ?? null) : db.prepare("INSERT INTO notes (type, status, content, session_id, project_path, surface_condition, created_at, updated_at, harness, anchor_ordinal) VALUES ('smart', 'pending', ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *").get(options3.content, options3.sessionId ?? null, options3.projectPath, options3.surfaceCondition, now, now, getHarness(), options3.anchorOrdinal ?? null);
166080
166216
  if (!isNoteRow(result)) {
166081
166217
  throw new Error("[notes] failed to insert note");
166082
166218
  }
@@ -166930,15 +167066,26 @@ function err3(text) {
166930
167066
  isError: true
166931
167067
  };
166932
167068
  }
167069
+ function captureAnchorOrdinal(db, sessionId) {
167070
+ try {
167071
+ const ordinal = getLastIndexedOrdinal(db, sessionId);
167072
+ return ordinal > 0 ? ordinal : null;
167073
+ } catch {
167074
+ return null;
167075
+ }
167076
+ }
167077
+ function anchorSuffix(note) {
167078
+ return note.anchorOrdinal !== null ? ` ↳ @msg ${note.anchorOrdinal}` : "";
167079
+ }
166933
167080
  function formatNoteLine(note) {
166934
167081
  if (note.type === "smart") {
166935
167082
  const conditionLine = note.status === "ready" ? note.readyReason ?? note.surfaceCondition ?? "Condition satisfied" : note.surfaceCondition ?? "No condition recorded";
166936
167083
  const statusSuffix2 = note.status === "active" ? "" : ` (${note.status})`;
166937
- return `- **#${note.id}**${statusSuffix2}: ${note.content}
167084
+ return `- **#${note.id}**${statusSuffix2}: ${note.content}${anchorSuffix(note)}
166938
167085
  *Condition*: ${conditionLine}`;
166939
167086
  }
166940
167087
  const statusSuffix = note.status === "active" ? "" : ` (${note.status})`;
166941
- return `- **#${note.id}**${statusSuffix}: ${note.content}`;
167088
+ return `- **#${note.id}**${statusSuffix}: ${note.content}${anchorSuffix(note)}`;
166942
167089
  }
166943
167090
  var DISMISS_FOOTER = `
166944
167091
 
@@ -166960,6 +167107,7 @@ function createCtxNoteTool(deps) {
166960
167107
  const content = params.content?.trim();
166961
167108
  if (!content)
166962
167109
  return err3("Error: 'content' is required when action is 'write'.");
167110
+ const anchorOrdinal = captureAnchorOrdinal(deps.db, sessionId);
166963
167111
  const surfaceCondition = params.surface_condition?.trim();
166964
167112
  if (surfaceCondition) {
166965
167113
  if (deps.dreamerEnabled !== true) {
@@ -166972,13 +167120,18 @@ function createCtxNoteTool(deps) {
166972
167120
  const note2 = addNote(deps.db, "smart", {
166973
167121
  content,
166974
167122
  projectPath: projectIdentity,
166975
- surfaceCondition
167123
+ surfaceCondition,
167124
+ anchorOrdinal
166976
167125
  });
166977
167126
  return ok3(`Created smart note #${note2.id}. Dreamer will evaluate the condition during nightly runs:
166978
167127
  - Content: ${content}
166979
167128
  - Condition: ${surfaceCondition}`);
166980
167129
  }
166981
- const note = addNote(deps.db, "session", { sessionId, content });
167130
+ const note = addNote(deps.db, "session", {
167131
+ sessionId,
167132
+ content,
167133
+ anchorOrdinal
167134
+ });
166982
167135
  return ok3(`Saved session note #${note.id}.`);
166983
167136
  }
166984
167137
  if (action2 === "dismiss") {
@@ -167041,9 +167194,13 @@ function createCtxNoteTool(deps) {
167041
167194
 
167042
167195
  No notes for the current filter.`);
167043
167196
  }
167044
- return ok3(`${sections.join(`
167197
+ const body = sections.join(`
167045
167198
 
167046
- `)}${DISMISS_FOOTER}`);
167199
+ `);
167200
+ const anchorHint = body.includes("↳ @msg ") ? `
167201
+
167202
+ ↳ @msg N marks the conversation tail when a note was written. To see what led to it: ctx_expand(start=N-x, end=N) (pick x for how far back to look).` : "";
167203
+ return ok3(`${body}${anchorHint}${DISMISS_FOOTER}`);
167047
167204
  }
167048
167205
  };
167049
167206
  }
@@ -167358,6 +167515,7 @@ var insertStatements = new WeakMap;
167358
167515
  var existingShasStatements = new WeakMap;
167359
167516
  var projectCountStatements = new WeakMap;
167360
167517
  var evictStatements = new WeakMap;
167518
+ var evictOverflowStatements = new WeakMap;
167361
167519
  var latestCommitTimeStatements = new WeakMap;
167362
167520
  function getInsertStatement(db) {
167363
167521
  let stmt = insertStatements.get(db);
@@ -167400,17 +167558,17 @@ function getLatestCommitTimeStatement(db) {
167400
167558
  }
167401
167559
  return stmt;
167402
167560
  }
167403
- function getEvictStatement(db) {
167404
- let stmt = evictStatements.get(db);
167561
+ function getEvictOverflowStatement(db) {
167562
+ let stmt = evictOverflowStatements.get(db);
167405
167563
  if (!stmt) {
167406
167564
  stmt = db.prepare(`DELETE FROM git_commits
167407
- WHERE sha IN (
167408
- SELECT sha FROM git_commits
167565
+ WHERE rowid IN (
167566
+ SELECT rowid FROM git_commits
167409
167567
  WHERE project_path = ?
167410
- ORDER BY committed_at ASC
167411
- LIMIT ?
167568
+ ORDER BY committed_at DESC, sha DESC
167569
+ LIMIT -1 OFFSET ?
167412
167570
  )`);
167413
- evictStatements.set(db, stmt);
167571
+ evictOverflowStatements.set(db, stmt);
167414
167572
  }
167415
167573
  return stmt;
167416
167574
  }
@@ -167448,22 +167606,15 @@ function getLatestIndexedCommitTimeMs(db, projectPath) {
167448
167606
  const row = getLatestCommitTimeStatement(db).get(projectPath);
167449
167607
  return row?.latest ?? null;
167450
167608
  }
167451
- function evictOldestCommits(db, projectPath, excess) {
167452
- if (excess <= 0)
167453
- return 0;
167454
- const before = getCommitCount(db, projectPath);
167455
- getEvictStatement(db).run(projectPath, excess);
167456
- const after = getCommitCount(db, projectPath);
167457
- return Math.max(0, before - after);
167458
- }
167459
167609
  function enforceProjectCap(db, projectPath, maxCommits) {
167460
167610
  if (maxCommits <= 0)
167461
167611
  return 0;
167462
167612
  const count = getCommitCount(db, projectPath);
167463
167613
  if (count <= maxCommits)
167464
167614
  return 0;
167465
- const excess = count - maxCommits;
167466
- const evicted = evictOldestCommits(db, projectPath, excess);
167615
+ getEvictOverflowStatement(db).run(projectPath, maxCommits);
167616
+ const after = getCommitCount(db, projectPath);
167617
+ const evicted = Math.max(0, count - after);
167467
167618
  if (evicted > 0) {
167468
167619
  log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
167469
167620
  }
@@ -167702,6 +167853,62 @@ function searchGitCommitsSync(db, projectPath, query, options3) {
167702
167853
  });
167703
167854
  return results.slice(0, options3.limit);
167704
167855
  }
167856
+ // ../plugin/src/features/magic-context/literal-probes.ts
167857
+ var MAX_PROBES = 5;
167858
+ var MIN_PROBE_LENGTH = 3;
167859
+ var SLASH_COMMAND_RE = /\/[a-z][a-z0-9]*(?:-[a-z0-9]+)+/gi;
167860
+ var KEBAB_SNAKE_RE = /[a-z][a-z0-9]*(?:[-_][a-z0-9]+)+/gi;
167861
+ var DOTTED_RE = /[a-z0-9][a-z0-9_-]*(?:\.[a-z0-9_-]+)+/gi;
167862
+ var CAMEL_RE = /\b[a-zA-Z][a-z0-9]*(?:[A-Z][a-z0-9]*)+\b/g;
167863
+ var SHA_RE = /\b[0-9a-f]{7,40}\b/gi;
167864
+ var ERROR_CODE_RE = /\b(?:TS\d{4,}|ERR_[A-Z][A-Z0-9_]*)\b/g;
167865
+ var QUOTED_RE = /["`]([^"`]{3,80})["`]/g;
167866
+ function looksLikeSha(token) {
167867
+ return /[0-9]/.test(token) && /^[0-9a-f]{7,40}$/i.test(token);
167868
+ }
167869
+ function extractLiteralProbes(query) {
167870
+ const trimmed = query.trim();
167871
+ if (trimmed.length === 0)
167872
+ return [];
167873
+ const ordered = [];
167874
+ const seen = new Set;
167875
+ const add = (raw) => {
167876
+ if (!raw)
167877
+ return;
167878
+ const probe = raw.trim();
167879
+ if (probe.length < MIN_PROBE_LENGTH)
167880
+ return;
167881
+ const key = probe.toLowerCase();
167882
+ if (seen.has(key))
167883
+ return;
167884
+ seen.add(key);
167885
+ ordered.push(probe);
167886
+ };
167887
+ for (const m of trimmed.matchAll(QUOTED_RE))
167888
+ add(m[1]);
167889
+ for (const m of trimmed.matchAll(SLASH_COMMAND_RE))
167890
+ add(m[0]);
167891
+ for (const m of trimmed.matchAll(ERROR_CODE_RE))
167892
+ add(m[0]);
167893
+ for (const m of trimmed.matchAll(DOTTED_RE))
167894
+ add(m[0]);
167895
+ for (const m of trimmed.matchAll(KEBAB_SNAKE_RE))
167896
+ add(m[0]);
167897
+ for (const m of trimmed.matchAll(CAMEL_RE))
167898
+ add(m[0]);
167899
+ for (const m of trimmed.matchAll(SHA_RE)) {
167900
+ if (looksLikeSha(m[0]))
167901
+ add(m[0]);
167902
+ }
167903
+ return ordered.slice(0, MAX_PROBES);
167904
+ }
167905
+ function containsProbeVerbatim(text, probes) {
167906
+ if (probes.length === 0)
167907
+ return false;
167908
+ const haystack = text.toLowerCase();
167909
+ return probes.some((probe) => haystack.includes(probe.toLowerCase()));
167910
+ }
167911
+
167705
167912
  // ../plugin/src/features/magic-context/search.ts
167706
167913
  var DEFAULT_UNIFIED_SEARCH_LIMIT = 10;
167707
167914
  var FTS_SEMANTIC_CANDIDATE_LIMIT = 50;
@@ -167879,36 +168086,82 @@ function linearDecayScore(rank, total) {
167879
168086
  return 0;
167880
168087
  return Math.max(0, 1 - rank / total);
167881
168088
  }
167882
- function searchMessages(args) {
167883
- const sanitizedQuery = sanitizeFtsQuery(args.query.trim());
167884
- if (sanitizedQuery.length === 0) {
168089
+ function runMessageFtsQuery(db, sessionId, ftsQuery, fetchLimit, cutoff) {
168090
+ if (ftsQuery.length === 0)
167885
168091
  return [];
167886
- }
167887
- const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
167888
- const rows = getMessageSearchStatement(args.db).all(args.sessionId, sanitizedQuery, fetchLimit).map((row) => row);
167889
- const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
167890
- const filtered = rows.map((row) => {
168092
+ const rows = getMessageSearchStatement(db).all(sessionId, ftsQuery, fetchLimit).map((row) => row);
168093
+ const result = [];
168094
+ for (const row of rows) {
167891
168095
  const messageOrdinal = getMessageOrdinal(row.messageOrdinal);
167892
168096
  if (messageOrdinal === null || typeof row.messageId !== "string" || typeof row.role !== "string" || typeof row.content !== "string") {
167893
- return null;
168097
+ continue;
167894
168098
  }
167895
168099
  if (cutoff !== null && messageOrdinal > cutoff) {
167896
- return null;
168100
+ continue;
167897
168101
  }
167898
- return {
168102
+ result.push({
167899
168103
  messageOrdinal,
167900
168104
  messageId: row.messageId,
167901
168105
  role: row.role,
167902
168106
  content: row.content
167903
- };
167904
- }).filter((result) => result !== null).slice(0, args.limit);
167905
- return filtered.map((row, rank) => ({
168107
+ });
168108
+ }
168109
+ return result;
168110
+ }
168111
+ var RRF_K = 60;
168112
+ var VERBATIM_PROBE_BONUS = 0.5;
168113
+ function searchMessages(args) {
168114
+ const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
168115
+ const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
168116
+ const baseQuery = sanitizeFtsQuery(args.query.trim());
168117
+ const probes = args.probes ?? [];
168118
+ if (probes.length === 0) {
168119
+ const filtered = runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff).slice(0, args.limit);
168120
+ return filtered.map((row, rank) => ({
168121
+ source: "message",
168122
+ content: previewText(row.content),
168123
+ score: linearDecayScore(rank, filtered.length),
168124
+ messageOrdinal: row.messageOrdinal,
168125
+ messageId: row.messageId,
168126
+ role: row.role
168127
+ }));
168128
+ }
168129
+ const queryLists = [];
168130
+ if (baseQuery.length > 0) {
168131
+ queryLists.push(runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff));
168132
+ }
168133
+ for (const probe of probes) {
168134
+ const probeQuery = sanitizeFtsQuery(probe);
168135
+ if (probeQuery.length === 0)
168136
+ continue;
168137
+ queryLists.push(runMessageFtsQuery(args.db, args.sessionId, probeQuery, fetchLimit, cutoff));
168138
+ }
168139
+ const fused = new Map;
168140
+ for (const list of queryLists) {
168141
+ list.forEach((row, rank) => {
168142
+ const rrf = 1 / (RRF_K + rank);
168143
+ const existing = fused.get(row.messageId);
168144
+ if (existing) {
168145
+ existing.score += rrf;
168146
+ } else {
168147
+ fused.set(row.messageId, { row, score: rrf });
168148
+ }
168149
+ });
168150
+ }
168151
+ for (const entry of fused.values()) {
168152
+ if (containsProbeVerbatim(entry.row.content, probes)) {
168153
+ entry.score += VERBATIM_PROBE_BONUS;
168154
+ }
168155
+ }
168156
+ const ranked = [...fused.values()].sort((a, b) => b.score !== a.score ? b.score - a.score : a.row.messageOrdinal - b.row.messageOrdinal).slice(0, args.limit);
168157
+ const maxScore = ranked.length > 0 ? ranked[0].score : 1;
168158
+ return ranked.map((entry) => ({
167906
168159
  source: "message",
167907
- content: previewText(row.content),
167908
- score: linearDecayScore(rank, filtered.length),
167909
- messageOrdinal: row.messageOrdinal,
167910
- messageId: row.messageId,
167911
- role: row.role
168160
+ content: previewText(entry.row.content),
168161
+ score: maxScore > 0 ? entry.score / maxScore : 0,
168162
+ messageOrdinal: entry.row.messageOrdinal,
168163
+ messageId: entry.row.messageId,
168164
+ role: entry.row.role
167912
168165
  }));
167913
168166
  }
167914
168167
  function getSourceBoost(result) {
@@ -167992,12 +168245,14 @@ async function unifiedSearch(db, sessionId, projectPath, query, options3 = {}) {
167992
168245
  return null;
167993
168246
  }) : Promise.resolve(null);
167994
168247
  await Promise.resolve();
168248
+ const messageProbes = options3.explicitSearch ? extractLiteralProbes(trimmedQuery) : [];
167995
168249
  const messageResults = runMessages ? searchMessages({
167996
168250
  db,
167997
168251
  sessionId,
167998
168252
  query: trimmedQuery,
167999
168253
  limit: tierLimit,
168000
- maxOrdinal: options3.maxMessageOrdinal
168254
+ maxOrdinal: options3.maxMessageOrdinal,
168255
+ probes: messageProbes
168001
168256
  }) : [];
168002
168257
  const queryEmbedding = await queryEmbeddingPromise;
168003
168258
  const [memoryResults, gitCommitResults] = await Promise.all([
@@ -168768,7 +169023,8 @@ function createCtxSearchTool(deps) {
168768
169023
  maxMessageOrdinal: lastCompartmentEnd >= 0 ? lastCompartmentEnd : undefined,
168769
169024
  gitCommitsEnabled,
168770
169025
  sources: params.sources,
168771
- visibleMemoryIds
169026
+ visibleMemoryIds,
169027
+ explicitSearch: true
168772
169028
  });
168773
169029
  return {
168774
169030
  content: [{ type: "text", text: formatSearchResults(query, results) }],