@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.
- package/dist/index.js +628 -459
- package/dist/subagent-entry.js +433 -177
- package/package.json +1 -1
package/dist/subagent-entry.js
CHANGED
|
@@ -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 =
|
|
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", {
|
|
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
|
-
|
|
167197
|
+
const body = sections.join(`
|
|
167045
167198
|
|
|
167046
|
-
`)
|
|
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
|
|
167404
|
-
let stmt =
|
|
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
|
|
167408
|
-
SELECT
|
|
167565
|
+
WHERE rowid IN (
|
|
167566
|
+
SELECT rowid FROM git_commits
|
|
167409
167567
|
WHERE project_path = ?
|
|
167410
|
-
ORDER BY committed_at
|
|
167411
|
-
LIMIT ?
|
|
167568
|
+
ORDER BY committed_at DESC, sha DESC
|
|
167569
|
+
LIMIT -1 OFFSET ?
|
|
167412
167570
|
)`);
|
|
167413
|
-
|
|
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
|
-
|
|
167466
|
-
const
|
|
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
|
|
167883
|
-
|
|
167884
|
-
if (sanitizedQuery.length === 0) {
|
|
168089
|
+
function runMessageFtsQuery(db, sessionId, ftsQuery, fetchLimit, cutoff) {
|
|
168090
|
+
if (ftsQuery.length === 0)
|
|
167885
168091
|
return [];
|
|
167886
|
-
|
|
167887
|
-
const
|
|
167888
|
-
|
|
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
|
-
|
|
168097
|
+
continue;
|
|
167894
168098
|
}
|
|
167895
168099
|
if (cutoff !== null && messageOrdinal > cutoff) {
|
|
167896
|
-
|
|
168100
|
+
continue;
|
|
167897
168101
|
}
|
|
167898
|
-
|
|
168102
|
+
result.push({
|
|
167899
168103
|
messageOrdinal,
|
|
167900
168104
|
messageId: row.messageId,
|
|
167901
168105
|
role: row.role,
|
|
167902
168106
|
content: row.content
|
|
167903
|
-
};
|
|
167904
|
-
}
|
|
167905
|
-
return
|
|
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:
|
|
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) }],
|