@wolfx/pi-magic-context 0.24.1 → 0.26.0-patch.1
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 +16239 -15569
- package/dist/subagent-entry.js +828 -603
- package/package.json +4 -6
package/dist/subagent-entry.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
1
|
var __create = Object.create;
|
|
3
2
|
var __getProtoOf = Object.getPrototypeOf;
|
|
4
3
|
var __defProp = Object.defineProperty;
|
|
@@ -44,7 +43,6 @@ var __export = (target, all) => {
|
|
|
44
43
|
});
|
|
45
44
|
};
|
|
46
45
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
47
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
48
46
|
|
|
49
47
|
// ../plugin/src/shared/harness.ts
|
|
50
48
|
function setHarness(value) {
|
|
@@ -8010,6 +8008,14 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
|
|
|
8010
8008
|
}
|
|
8011
8009
|
super(typeof filename === "string" ? filename : ":memory:", translated);
|
|
8012
8010
|
}
|
|
8011
|
+
prepare(sql) {
|
|
8012
|
+
const stmt = super.prepare(sql);
|
|
8013
|
+
for (const method of ["run", "get", "all"]) {
|
|
8014
|
+
const original = stmt[method].bind(stmt);
|
|
8015
|
+
stmt[method] = (...args) => args.length === 1 && Array.isArray(args[0]) ? original(...args[0]) : original(...args);
|
|
8016
|
+
}
|
|
8017
|
+
return stmt;
|
|
8018
|
+
}
|
|
8013
8019
|
transaction(fn) {
|
|
8014
8020
|
const self = this;
|
|
8015
8021
|
const wrapped = function(...args) {
|
|
@@ -9574,6 +9580,41 @@ var MIGRATIONS = [
|
|
|
9574
9580
|
`);
|
|
9575
9581
|
}
|
|
9576
9582
|
}
|
|
9583
|
+
},
|
|
9584
|
+
{
|
|
9585
|
+
version: 37,
|
|
9586
|
+
description: "emergency drain catch-up latch + historian drain failure backoff",
|
|
9587
|
+
up: (db) => {
|
|
9588
|
+
const hasSessionMeta = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='session_meta'").get();
|
|
9589
|
+
if (!hasSessionMeta)
|
|
9590
|
+
return;
|
|
9591
|
+
ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
|
|
9592
|
+
ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
|
|
9593
|
+
}
|
|
9594
|
+
},
|
|
9595
|
+
{
|
|
9596
|
+
version: 38,
|
|
9597
|
+
description: "durable transform decisions for cache-event cause attribution",
|
|
9598
|
+
up: (db) => {
|
|
9599
|
+
db.exec(`
|
|
9600
|
+
CREATE TABLE IF NOT EXISTS transform_decisions (
|
|
9601
|
+
session_id TEXT NOT NULL,
|
|
9602
|
+
harness TEXT NOT NULL DEFAULT 'opencode',
|
|
9603
|
+
message_id TEXT NOT NULL,
|
|
9604
|
+
ts_ms INTEGER NOT NULL,
|
|
9605
|
+
decision TEXT NOT NULL,
|
|
9606
|
+
materialized INTEGER NOT NULL DEFAULT 0,
|
|
9607
|
+
materialize_reason TEXT,
|
|
9608
|
+
emergency INTEGER NOT NULL DEFAULT 0,
|
|
9609
|
+
dropped_tokens INTEGER NOT NULL DEFAULT 0,
|
|
9610
|
+
dropped_count INTEGER NOT NULL DEFAULT 0,
|
|
9611
|
+
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
9612
|
+
PRIMARY KEY (session_id, harness, message_id)
|
|
9613
|
+
);
|
|
9614
|
+
CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
|
|
9615
|
+
ON transform_decisions(session_id, harness);
|
|
9616
|
+
`);
|
|
9617
|
+
}
|
|
9577
9618
|
}
|
|
9578
9619
|
];
|
|
9579
9620
|
var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
|
|
@@ -142550,8 +142591,9 @@ function backfillToolOwnersInChunks(db, result) {
|
|
|
142550
142591
|
var databases = new Map;
|
|
142551
142592
|
var persistenceByDatabase = new WeakMap;
|
|
142552
142593
|
var persistenceErrorByDatabase = new WeakMap;
|
|
142594
|
+
var pathByDatabase = new WeakMap;
|
|
142553
142595
|
var lastSchemaFenceRejection = null;
|
|
142554
|
-
var LATEST_SUPPORTED_VERSION =
|
|
142596
|
+
var LATEST_SUPPORTED_VERSION = 38;
|
|
142555
142597
|
function resolveDatabasePath(dbPathOverride) {
|
|
142556
142598
|
if (dbPathOverride) {
|
|
142557
142599
|
return { dbDir: dirname2(dbPathOverride), dbPath: dbPathOverride };
|
|
@@ -142564,6 +142606,9 @@ function resolveDatabasePath(dbPathOverride) {
|
|
|
142564
142606
|
const dbDir = getMagicContextStorageDir();
|
|
142565
142607
|
return { dbDir, dbPath: join4(dbDir, "context.db") };
|
|
142566
142608
|
}
|
|
142609
|
+
function getDatabasePath(db) {
|
|
142610
|
+
return pathByDatabase.get(db) ?? null;
|
|
142611
|
+
}
|
|
142567
142612
|
function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
142568
142613
|
if (existsSync3(targetDbPath))
|
|
142569
142614
|
return;
|
|
@@ -143078,6 +143123,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
143078
143123
|
recovery_no_eligible_head_count INTEGER NOT NULL DEFAULT 0,
|
|
143079
143124
|
force_emergency_bypass_window_start INTEGER NOT NULL DEFAULT 0,
|
|
143080
143125
|
force_emergency_bypass_used INTEGER NOT NULL DEFAULT 0,
|
|
143126
|
+
emergency_drain_active INTEGER NOT NULL DEFAULT 0,
|
|
143127
|
+
historian_drain_failure_at INTEGER NOT NULL DEFAULT 0,
|
|
143081
143128
|
cached_m0_materialized_at INTEGER,
|
|
143082
143129
|
cached_m0_session_facts_version INTEGER,
|
|
143083
143130
|
cached_m0_upgrade_state TEXT,
|
|
@@ -143141,6 +143188,23 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
143141
143188
|
CREATE INDEX IF NOT EXISTS idx_historian_runs_status
|
|
143142
143189
|
ON historian_runs(status, created_at DESC);
|
|
143143
143190
|
|
|
143191
|
+
CREATE TABLE IF NOT EXISTS transform_decisions (
|
|
143192
|
+
session_id TEXT NOT NULL,
|
|
143193
|
+
harness TEXT NOT NULL DEFAULT 'opencode',
|
|
143194
|
+
message_id TEXT NOT NULL,
|
|
143195
|
+
ts_ms INTEGER NOT NULL,
|
|
143196
|
+
decision TEXT NOT NULL,
|
|
143197
|
+
materialized INTEGER NOT NULL DEFAULT 0,
|
|
143198
|
+
materialize_reason TEXT,
|
|
143199
|
+
emergency INTEGER NOT NULL DEFAULT 0,
|
|
143200
|
+
dropped_tokens INTEGER NOT NULL DEFAULT 0,
|
|
143201
|
+
dropped_count INTEGER NOT NULL DEFAULT 0,
|
|
143202
|
+
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
143203
|
+
PRIMARY KEY (session_id, harness, message_id)
|
|
143204
|
+
);
|
|
143205
|
+
CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
|
|
143206
|
+
ON transform_decisions(session_id, harness);
|
|
143207
|
+
|
|
143144
143208
|
CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
|
|
143145
143209
|
CREATE INDEX IF NOT EXISTS idx_tags_session_message_id ON tags(session_id, message_id);
|
|
143146
143210
|
CREATE INDEX IF NOT EXISTS idx_pending_ops_session ON pending_ops(session_id);
|
|
@@ -143218,6 +143282,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
143218
143282
|
ensureColumn(db, "session_meta", "historian_last_failure_at", "INTEGER DEFAULT NULL");
|
|
143219
143283
|
ensureColumn(db, "session_meta", "system_prompt_hash", "TEXT DEFAULT ''");
|
|
143220
143284
|
ensureColumn(db, "session_meta", "cleared_reasoning_through_tag", "INTEGER DEFAULT 0");
|
|
143285
|
+
ensureColumn(db, "session_meta", "tool_reclaim_watermark", "INTEGER DEFAULT 0");
|
|
143221
143286
|
ensureColumn(db, "session_meta", "stripped_placeholder_ids", "TEXT DEFAULT ''");
|
|
143222
143287
|
ensureColumn(db, "session_meta", "stale_reduce_stripped_ids", "TEXT DEFAULT ''");
|
|
143223
143288
|
ensureColumn(db, "session_meta", "processed_image_stripped_ids", "TEXT DEFAULT ''");
|
|
@@ -143291,6 +143356,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
143291
143356
|
ensureColumn(db, "session_meta", "recovery_no_eligible_head_count", "INTEGER NOT NULL DEFAULT 0");
|
|
143292
143357
|
ensureColumn(db, "session_meta", "force_emergency_bypass_window_start", "INTEGER NOT NULL DEFAULT 0");
|
|
143293
143358
|
ensureColumn(db, "session_meta", "force_emergency_bypass_used", "INTEGER NOT NULL DEFAULT 0");
|
|
143359
|
+
ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
|
|
143360
|
+
ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
|
|
143294
143361
|
ensureColumn(db, "session_meta", "cached_m0_materialized_at", "INTEGER");
|
|
143295
143362
|
ensureColumn(db, "session_meta", "cached_m0_session_facts_version", "INTEGER");
|
|
143296
143363
|
ensureColumn(db, "session_meta", "cached_m0_upgrade_state", "TEXT");
|
|
@@ -143369,6 +143436,22 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
143369
143436
|
failed_at INTEGER NOT NULL,
|
|
143370
143437
|
UNIQUE(table_name, row_id)
|
|
143371
143438
|
);
|
|
143439
|
+
CREATE TABLE IF NOT EXISTS transform_decisions (
|
|
143440
|
+
session_id TEXT NOT NULL,
|
|
143441
|
+
harness TEXT NOT NULL DEFAULT 'opencode',
|
|
143442
|
+
message_id TEXT NOT NULL,
|
|
143443
|
+
ts_ms INTEGER NOT NULL,
|
|
143444
|
+
decision TEXT NOT NULL,
|
|
143445
|
+
materialized INTEGER NOT NULL DEFAULT 0,
|
|
143446
|
+
materialize_reason TEXT,
|
|
143447
|
+
emergency INTEGER NOT NULL DEFAULT 0,
|
|
143448
|
+
dropped_tokens INTEGER NOT NULL DEFAULT 0,
|
|
143449
|
+
dropped_count INTEGER NOT NULL DEFAULT 0,
|
|
143450
|
+
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
143451
|
+
PRIMARY KEY (session_id, harness, message_id)
|
|
143452
|
+
);
|
|
143453
|
+
CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
|
|
143454
|
+
ON transform_decisions(session_id, harness);
|
|
143372
143455
|
`);
|
|
143373
143456
|
ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
143374
143457
|
ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
@@ -143455,7 +143538,9 @@ function healNullIntegerColumns(db) {
|
|
|
143455
143538
|
["protected_tail_drain_tokens", 0],
|
|
143456
143539
|
["recovery_no_eligible_head_count", 0],
|
|
143457
143540
|
["force_emergency_bypass_window_start", 0],
|
|
143458
|
-
["force_emergency_bypass_used", 0]
|
|
143541
|
+
["force_emergency_bypass_used", 0],
|
|
143542
|
+
["emergency_drain_active", 0],
|
|
143543
|
+
["historian_drain_failure_at", 0]
|
|
143459
143544
|
];
|
|
143460
143545
|
for (const [column, fallback] of columns) {
|
|
143461
143546
|
try {
|
|
@@ -143531,6 +143616,7 @@ function openDatabase(dbPathOrOptions) {
|
|
|
143531
143616
|
setDatabase(db);
|
|
143532
143617
|
loadToolDefinitionMeasurements(db);
|
|
143533
143618
|
databases.set(dbPath, db);
|
|
143619
|
+
pathByDatabase.set(db, dbPath);
|
|
143534
143620
|
persistenceByDatabase.set(db, true);
|
|
143535
143621
|
persistenceErrorByDatabase.delete(db);
|
|
143536
143622
|
return db;
|
|
@@ -158092,7 +158178,6 @@ var DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65;
|
|
|
158092
158178
|
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.";
|
|
158093
158179
|
var DEFAULT_HISTORIAN_TIMEOUT_MS = 300000;
|
|
158094
158180
|
var DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15;
|
|
158095
|
-
var DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2";
|
|
158096
158181
|
var DREAMER_TASKS = [
|
|
158097
158182
|
"consolidate",
|
|
158098
158183
|
"verify",
|
|
@@ -158132,14 +158217,16 @@ var SidekickConfigSchema = AgentOverrideConfigSchema.extend({
|
|
|
158132
158217
|
}).optional();
|
|
158133
158218
|
var HistorianConfigSchema = AgentOverrideConfigSchema.extend({
|
|
158134
158219
|
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)"),
|
|
158135
|
-
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")
|
|
158220
|
+
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"),
|
|
158221
|
+
disallowed_tools: exports_external.array(exports_external.enum(["*", "read", "aft_outline", "aft_zoom", "aft_search"])).default([]).describe(`OpenCode only. Tools to REMOVE from the historian's default allow-list [read, aft_outline, aft_zoom, aft_search]. Applies to both historian and historian-editor agents. Use ["*"] to strip all tool definitions from the model request — this prevents weak instruction-following models (e.g. mistral-small-latest) from entering tool-calling loops. Individual tool names remove just that tool. Note: a user-supplied historian.permission override can re-allow a tool that disallowed_tools removed — disallowed_tools sets the baseline, permission overrides take precedence. (default: [])`)
|
|
158136
158222
|
}).optional();
|
|
158137
158223
|
var BaseEmbeddingConfigSchema = exports_external.object({
|
|
158138
|
-
provider: exports_external.enum(["
|
|
158139
|
-
model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible
|
|
158224
|
+
provider: exports_external.enum(["openai-compatible", "off"]).default("off").describe("Embedding provider. 'openai-compatible' requires endpoint and model, 'off' disables embeddings (default)."),
|
|
158225
|
+
model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible."),
|
|
158140
158226
|
endpoint: exports_external.string().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
|
|
158141
158227
|
api_key: exports_external.string().optional().describe("API key for remote embedding provider (optional)"),
|
|
158142
|
-
input_type: exports_external.string().optional().describe("
|
|
158228
|
+
input_type: exports_external.string().optional().describe("Default input_type for stored/indexed (passage) embeddings in the request body. Required by some openai-compatible providers (e.g. NVIDIA NIM). Omitted from the request when unset."),
|
|
158229
|
+
query_input_type: exports_external.string().optional().describe("Optional input_type for query (search) embeddings on asymmetric models (e.g. NVIDIA NIM 'query'). When unset, query embeddings use embedding.input_type. Passage/stored content always uses embedding.input_type."),
|
|
158143
158230
|
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."),
|
|
158144
158231
|
max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted.")
|
|
158145
158232
|
}).superRefine((data, ctx) => {
|
|
@@ -158159,16 +158246,10 @@ var BaseEmbeddingConfigSchema = exports_external.object({
|
|
|
158159
158246
|
}
|
|
158160
158247
|
});
|
|
158161
158248
|
var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
158162
|
-
if (data.provider === "local") {
|
|
158163
|
-
return {
|
|
158164
|
-
provider: "local",
|
|
158165
|
-
model: data.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
|
|
158166
|
-
...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
|
|
158167
|
-
};
|
|
158168
|
-
}
|
|
158169
158249
|
if (data.provider === "openai-compatible") {
|
|
158170
158250
|
const apiKey = data.api_key?.trim();
|
|
158171
158251
|
const inputType = data.input_type?.trim();
|
|
158252
|
+
const queryInputType = data.query_input_type?.trim();
|
|
158172
158253
|
const truncate = data.truncate?.trim();
|
|
158173
158254
|
return {
|
|
158174
158255
|
provider: "openai-compatible",
|
|
@@ -158176,6 +158257,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
|
158176
158257
|
endpoint: data.endpoint?.trim() ?? "",
|
|
158177
158258
|
...apiKey ? { api_key: apiKey } : {},
|
|
158178
158259
|
...inputType ? { input_type: inputType } : {},
|
|
158260
|
+
...queryInputType ? { query_input_type: queryInputType } : {},
|
|
158179
158261
|
...truncate ? { truncate } : {},
|
|
158180
158262
|
...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
|
|
158181
158263
|
};
|
|
@@ -158215,8 +158297,7 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
158215
158297
|
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)")
|
|
158216
158298
|
}).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."),
|
|
158217
158299
|
embedding: EmbeddingConfigSchema.default({
|
|
158218
|
-
provider: "
|
|
158219
|
-
model: DEFAULT_LOCAL_EMBEDDING_MODEL
|
|
158300
|
+
provider: "off"
|
|
158220
158301
|
}).describe("Embedding provider configuration"),
|
|
158221
158302
|
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).'),
|
|
158222
158303
|
keep_subagents: exports_external.boolean().default(false).describe("Debug: keep the child sessions Magic Context spawns for its own subagents (historian, dreamer, sidekick, memory-migration) instead of deleting them on success. Useful for short-term inspection/data collection — their full transcript (prompt, tool calls, token usage, output) stays in the host session store. Kept sessions accumulate until manually cleared; leave false for normal use. Requires a restart to take effect."),
|
|
@@ -158899,6 +158980,16 @@ function buildCanonicalChunkTextFromFts(db, sessionId, startOrdinal, endOrdinal)
|
|
|
158899
158980
|
return lines.join(`
|
|
158900
158981
|
`);
|
|
158901
158982
|
}
|
|
158983
|
+
function buildCompartmentSummaryFallbackText(db, compartmentId) {
|
|
158984
|
+
const row = db.prepare("SELECT title, p1, content FROM compartments WHERE id = ?").get(compartmentId);
|
|
158985
|
+
if (!row)
|
|
158986
|
+
return "";
|
|
158987
|
+
const title = typeof row.title === "string" ? row.title.trim() : "";
|
|
158988
|
+
const p1 = typeof row.p1 === "string" ? row.p1.trim() : "";
|
|
158989
|
+
const body = p1.length > 0 ? p1 : typeof row.content === "string" ? row.content.trim() : "";
|
|
158990
|
+
return [title, body].filter((s) => s.length > 0).join(`
|
|
158991
|
+
`);
|
|
158992
|
+
}
|
|
158902
158993
|
function canonicalizeInMemoryChunkTextForEmbedding(chunkText, startOrdinal, endOrdinal) {
|
|
158903
158994
|
const lines = [];
|
|
158904
158995
|
for (const rawLine of chunkText.split(/\r?\n/)) {
|
|
@@ -159142,6 +159233,28 @@ function countUnembeddedSessionCompartments(db, projectPath, sessionId, modelId)
|
|
|
159142
159233
|
)`).get(projectPath, sessionId, projectPath, modelId);
|
|
159143
159234
|
return typeof row?.n === "number" ? row.n : 0;
|
|
159144
159235
|
}
|
|
159236
|
+
function countSessionCompartmentEmbedCoverage(db, projectPath, sessionId, modelId) {
|
|
159237
|
+
const row = db.prepare(`SELECT
|
|
159238
|
+
COUNT(*) AS total,
|
|
159239
|
+
SUM(CASE WHEN EXISTS (
|
|
159240
|
+
SELECT 1 FROM compartment_chunk_embeddings e
|
|
159241
|
+
WHERE e.compartment_id = c.id
|
|
159242
|
+
AND e.project_path = ?
|
|
159243
|
+
AND e.model_id = ?
|
|
159244
|
+
) THEN 1 ELSE 0 END) AS embedded
|
|
159245
|
+
FROM compartments c
|
|
159246
|
+
JOIN session_projects sp
|
|
159247
|
+
ON sp.session_id = c.session_id
|
|
159248
|
+
AND sp.harness = c.harness
|
|
159249
|
+
AND sp.project_path = ?
|
|
159250
|
+
WHERE c.session_id = ?
|
|
159251
|
+
AND c.start_message IS NOT NULL
|
|
159252
|
+
AND c.end_message IS NOT NULL`).get(projectPath, modelId, projectPath, sessionId);
|
|
159253
|
+
return {
|
|
159254
|
+
total: typeof row?.total === "number" ? row.total : 0,
|
|
159255
|
+
embedded: typeof row?.embedded === "number" ? row.embedded : 0
|
|
159256
|
+
};
|
|
159257
|
+
}
|
|
159145
159258
|
|
|
159146
159259
|
// ../plugin/src/features/magic-context/memory/cosine-similarity.ts
|
|
159147
159260
|
function cosineSimilarity(a, b) {
|
|
@@ -159184,356 +159297,11 @@ function getEmbeddingProviderIdentity(config2) {
|
|
|
159184
159297
|
endpoint: normalizeEndpoint2(config2.endpoint),
|
|
159185
159298
|
apiKeyPresent: Boolean(config2.api_key?.trim()),
|
|
159186
159299
|
inputType: config2.input_type?.trim() || ""
|
|
159187
|
-
} :
|
|
159188
|
-
|
|
159189
|
-
|
|
159190
|
-
endpoint: "",
|
|
159191
|
-
apiKeyPresent: false
|
|
159192
|
-
};
|
|
159193
|
-
return `embedding-provider:${computeNormalizedHash(JSON.stringify(identityInput))}`;
|
|
159194
|
-
}
|
|
159195
|
-
|
|
159196
|
-
// ../plugin/src/features/magic-context/memory/embedding-local.ts
|
|
159197
|
-
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
159198
|
-
import { open, stat, unlink, writeFile } from "node:fs/promises";
|
|
159199
|
-
import { dirname as dirname4, join as join6 } from "node:path";
|
|
159200
|
-
import { pathToFileURL } from "node:url";
|
|
159201
|
-
init_data_path();
|
|
159202
|
-
init_logger();
|
|
159203
|
-
var LOCK_POLL_MS = 150;
|
|
159204
|
-
var STALE_LOCK_MS = 3 * 60000;
|
|
159205
|
-
var MAX_LOCK_WAIT_MS = 5 * 60000;
|
|
159206
|
-
async function acquireModelLoadLock(lockPath) {
|
|
159207
|
-
const waitStart = Date.now();
|
|
159208
|
-
while (true) {
|
|
159209
|
-
try {
|
|
159210
|
-
const handle = await open(lockPath, "wx");
|
|
159211
|
-
try {
|
|
159212
|
-
await handle.writeFile(`pid=${process.pid} started=${Date.now()}
|
|
159213
|
-
`);
|
|
159214
|
-
} catch {}
|
|
159215
|
-
await handle.close();
|
|
159216
|
-
return async () => {
|
|
159217
|
-
try {
|
|
159218
|
-
await unlink(lockPath);
|
|
159219
|
-
} catch {}
|
|
159220
|
-
};
|
|
159221
|
-
} catch (error51) {
|
|
159222
|
-
const code = error51.code;
|
|
159223
|
-
if (code !== "EEXIST" && code !== "EPERM") {
|
|
159224
|
-
throw error51;
|
|
159225
|
-
}
|
|
159226
|
-
try {
|
|
159227
|
-
const info = await stat(lockPath);
|
|
159228
|
-
if (Date.now() - info.mtimeMs > STALE_LOCK_MS) {
|
|
159229
|
-
log(`[magic-context] embedding-load lock stale (>${STALE_LOCK_MS}ms), taking over`);
|
|
159230
|
-
try {
|
|
159231
|
-
await unlink(lockPath);
|
|
159232
|
-
} catch {}
|
|
159233
|
-
continue;
|
|
159234
|
-
}
|
|
159235
|
-
} catch {
|
|
159236
|
-
continue;
|
|
159237
|
-
}
|
|
159238
|
-
if (Date.now() - waitStart > MAX_LOCK_WAIT_MS) {
|
|
159239
|
-
throw new Error(`[magic-context] embedding-load lock wait exceeded ${MAX_LOCK_WAIT_MS}ms; another process is still loading the model. Skipping this init attempt to avoid an unsynchronized native load.`);
|
|
159240
|
-
}
|
|
159241
|
-
await new Promise((resolve3) => setTimeout(resolve3, LOCK_POLL_MS));
|
|
159242
|
-
}
|
|
159243
|
-
}
|
|
159244
|
-
}
|
|
159245
|
-
function startLockHeartbeat(lockPath) {
|
|
159246
|
-
const HEARTBEAT_MS = Math.floor(STALE_LOCK_MS / 3);
|
|
159247
|
-
const timer = setInterval(() => {
|
|
159248
|
-
writeFile(lockPath, `pid=${process.pid} alive=${Date.now()}
|
|
159249
|
-
`).catch(() => {});
|
|
159250
|
-
}, HEARTBEAT_MS);
|
|
159251
|
-
timer.unref?.();
|
|
159252
|
-
return () => clearInterval(timer);
|
|
159253
|
-
}
|
|
159254
|
-
async function injectWasmOrtForElectron() {
|
|
159255
|
-
if (typeof process === "undefined" || !process.versions?.electron) {
|
|
159256
|
-
return false;
|
|
159257
|
-
}
|
|
159258
|
-
try {
|
|
159259
|
-
const ortWebSpec = `onnxruntime-${"web"}`;
|
|
159260
|
-
const ortWeb = await import(ortWebSpec);
|
|
159261
|
-
try {
|
|
159262
|
-
const { createRequire: createRequireFn } = await import("node:module");
|
|
159263
|
-
const requireFn = createRequireFn(import.meta.url);
|
|
159264
|
-
const pkgPath = requireFn.resolve("onnxruntime-web/package.json");
|
|
159265
|
-
const distDir = join6(dirname4(pkgPath), "dist");
|
|
159266
|
-
const wasmPathsPrefix = `${pathToFileURL(distDir).href}/`;
|
|
159267
|
-
if (ortWeb.env?.wasm) {
|
|
159268
|
-
ortWeb.env.wasm.wasmPaths = wasmPathsPrefix;
|
|
159269
|
-
}
|
|
159270
|
-
} catch (pathError) {
|
|
159271
|
-
log("[magic-context] could not resolve local onnxruntime-web/dist, falling back to default WASM paths:", pathError instanceof Error ? pathError.message : String(pathError));
|
|
159272
|
-
}
|
|
159273
|
-
globalThis[Symbol.for("onnxruntime")] = ortWeb;
|
|
159274
|
-
log("[magic-context] Electron detected — using onnxruntime-web (WASM) for embeddings (bypasses onnxruntime-node native load)");
|
|
159275
|
-
return true;
|
|
159276
|
-
} catch (error51) {
|
|
159277
|
-
log("[magic-context] failed to inject onnxruntime-web for Electron — letting transformers fall back to native:", error51 instanceof Error ? error51.message : String(error51));
|
|
159278
|
-
return false;
|
|
159279
|
-
}
|
|
159280
|
-
}
|
|
159281
|
-
async function withQuietConsole(fn) {
|
|
159282
|
-
const origWarn = console.warn;
|
|
159283
|
-
const origError = console.error;
|
|
159284
|
-
const redirect = (...args) => {
|
|
159285
|
-
const message = args.map((a) => typeof a === "string" ? a : String(a)).join(" ");
|
|
159286
|
-
log(`[transformers] ${message}`);
|
|
159287
|
-
};
|
|
159288
|
-
console.warn = redirect;
|
|
159289
|
-
console.error = redirect;
|
|
159290
|
-
try {
|
|
159291
|
-
return await fn();
|
|
159292
|
-
} finally {
|
|
159293
|
-
console.warn = origWarn;
|
|
159294
|
-
console.error = origError;
|
|
159295
|
-
}
|
|
159296
|
-
}
|
|
159297
|
-
function isTransientLoadError(error51) {
|
|
159298
|
-
const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
|
|
159299
|
-
if (!message)
|
|
159300
|
-
return false;
|
|
159301
|
-
const lower = message.toLowerCase();
|
|
159302
|
-
return lower.includes("protobuf parsing failed") || lower.includes("unable to get model file path or buffer") || lower.includes("ebusy") || lower.includes("resource busy") || lower.includes("resource temporarily unavailable");
|
|
159303
|
-
}
|
|
159304
|
-
function isArrayLikeNumber(value) {
|
|
159305
|
-
if (typeof value !== "object" || value === null || !("length" in value)) {
|
|
159306
|
-
return false;
|
|
159307
|
-
}
|
|
159308
|
-
const arr = value;
|
|
159309
|
-
if (typeof arr.length !== "number") {
|
|
159310
|
-
return false;
|
|
159311
|
-
}
|
|
159312
|
-
return arr.length === 0 || typeof arr[0] === "number";
|
|
159313
|
-
}
|
|
159314
|
-
function toFloat32Array2(values) {
|
|
159315
|
-
return values instanceof Float32Array ? new Float32Array(values) : Float32Array.from(Array.from(values));
|
|
159316
|
-
}
|
|
159317
|
-
function extractBatchEmbeddings(result, expectedCount) {
|
|
159318
|
-
const { data } = result;
|
|
159319
|
-
if (Array.isArray(data) && data.length === expectedCount && data.every((entry) => typeof entry !== "number" && isArrayLikeNumber(entry))) {
|
|
159320
|
-
return data.map((entry) => toFloat32Array2(entry));
|
|
159321
|
-
}
|
|
159322
|
-
if (!isArrayLikeNumber(data)) {
|
|
159323
|
-
log("[magic-context] embedding batch returned unexpected data shape");
|
|
159324
|
-
return Array.from({ length: expectedCount }, () => null);
|
|
159325
|
-
}
|
|
159326
|
-
const flatData = toFloat32Array2(data);
|
|
159327
|
-
const dimension = result.dims?.at(-1) ?? flatData.length / expectedCount;
|
|
159328
|
-
if (!Number.isInteger(dimension) || dimension <= 0 || flatData.length !== expectedCount * dimension) {
|
|
159329
|
-
log("[magic-context] embedding batch returned invalid dimensions");
|
|
159330
|
-
return Array.from({ length: expectedCount }, () => null);
|
|
159331
|
-
}
|
|
159332
|
-
const embeddings = [];
|
|
159333
|
-
for (let index = 0;index < expectedCount; index++) {
|
|
159334
|
-
embeddings.push(flatData.slice(index * dimension, (index + 1) * dimension));
|
|
159335
|
-
}
|
|
159336
|
-
return embeddings;
|
|
159337
|
-
}
|
|
159338
|
-
|
|
159339
|
-
class LocalEmbeddingProvider {
|
|
159340
|
-
modelId;
|
|
159341
|
-
maxInputTokens;
|
|
159342
|
-
model;
|
|
159343
|
-
pipeline = null;
|
|
159344
|
-
initPromise = null;
|
|
159345
|
-
inFlight = 0;
|
|
159346
|
-
disposing = false;
|
|
159347
|
-
disposePromise = null;
|
|
159348
|
-
inFlightWaiters = [];
|
|
159349
|
-
constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL, maxInputTokens = 512) {
|
|
159350
|
-
this.model = model;
|
|
159351
|
-
this.maxInputTokens = maxInputTokens;
|
|
159352
|
-
this.modelId = getEmbeddingProviderIdentity({ provider: "local", model });
|
|
159353
|
-
}
|
|
159354
|
-
async initialize() {
|
|
159355
|
-
if (this.disposing) {
|
|
159356
|
-
return false;
|
|
159357
|
-
}
|
|
159358
|
-
if (this.pipeline) {
|
|
159359
|
-
return true;
|
|
159360
|
-
}
|
|
159361
|
-
if (this.initPromise) {
|
|
159362
|
-
await this.initPromise;
|
|
159363
|
-
return this.pipeline !== null;
|
|
159364
|
-
}
|
|
159365
|
-
this.initPromise = (async () => {
|
|
159366
|
-
try {
|
|
159367
|
-
if (this.disposing) {
|
|
159368
|
-
return;
|
|
159369
|
-
}
|
|
159370
|
-
await injectWasmOrtForElectron();
|
|
159371
|
-
const transformersSpec = "@huggingface/transformers";
|
|
159372
|
-
const transformersModule = await import(transformersSpec);
|
|
159373
|
-
const env = transformersModule.env;
|
|
159374
|
-
const LogLevel = transformersModule.LogLevel;
|
|
159375
|
-
if (LogLevel && "ERROR" in LogLevel) {
|
|
159376
|
-
env.logLevel = LogLevel.ERROR;
|
|
159377
|
-
}
|
|
159378
|
-
const modelCacheDir = join6(getMagicContextStorageDir(), "models");
|
|
159379
|
-
try {
|
|
159380
|
-
mkdirSync3(modelCacheDir, { recursive: true });
|
|
159381
|
-
env.cacheDir = modelCacheDir;
|
|
159382
|
-
} catch {
|
|
159383
|
-
log("[magic-context] could not create model cache dir, using library default");
|
|
159384
|
-
}
|
|
159385
|
-
const createPipeline = transformersModule.pipeline;
|
|
159386
|
-
const lockPath = join6(modelCacheDir, ".load.lock");
|
|
159387
|
-
const releaseLock = await acquireModelLoadLock(lockPath);
|
|
159388
|
-
const stopHeartbeat = startLockHeartbeat(lockPath);
|
|
159389
|
-
try {
|
|
159390
|
-
const MAX_ATTEMPTS = 3;
|
|
159391
|
-
let lastError;
|
|
159392
|
-
for (let attempt = 1;attempt <= MAX_ATTEMPTS; attempt++) {
|
|
159393
|
-
try {
|
|
159394
|
-
const pipeline = await withQuietConsole(() => createPipeline("feature-extraction", this.model, {
|
|
159395
|
-
dtype: "fp32"
|
|
159396
|
-
}));
|
|
159397
|
-
if (this.disposing) {
|
|
159398
|
-
await pipeline.dispose?.();
|
|
159399
|
-
this.pipeline = null;
|
|
159400
|
-
} else {
|
|
159401
|
-
this.pipeline = pipeline;
|
|
159402
|
-
}
|
|
159403
|
-
lastError = undefined;
|
|
159404
|
-
break;
|
|
159405
|
-
} catch (error51) {
|
|
159406
|
-
lastError = error51;
|
|
159407
|
-
if (!isTransientLoadError(error51) || attempt === MAX_ATTEMPTS) {
|
|
159408
|
-
break;
|
|
159409
|
-
}
|
|
159410
|
-
const delayMs = 300 * attempt + Math.floor(Math.random() * 200);
|
|
159411
|
-
log(`[magic-context] embedding model load attempt ${attempt}/${MAX_ATTEMPTS} failed transiently, retrying in ${delayMs}ms`);
|
|
159412
|
-
await new Promise((resolve3) => setTimeout(resolve3, delayMs));
|
|
159413
|
-
}
|
|
159414
|
-
}
|
|
159415
|
-
if (this.pipeline) {
|
|
159416
|
-
log(`[magic-context] embedding model loaded: ${this.model}`);
|
|
159417
|
-
} else if (this.disposing) {
|
|
159418
|
-
return;
|
|
159419
|
-
} else {
|
|
159420
|
-
throw lastError ?? new Error("unknown embedding load failure");
|
|
159421
|
-
}
|
|
159422
|
-
} finally {
|
|
159423
|
-
stopHeartbeat();
|
|
159424
|
-
await releaseLock();
|
|
159425
|
-
}
|
|
159426
|
-
} catch (error51) {
|
|
159427
|
-
log("[magic-context] embedding model failed to load:", error51);
|
|
159428
|
-
this.pipeline = null;
|
|
159429
|
-
} finally {
|
|
159430
|
-
this.initPromise = null;
|
|
159431
|
-
}
|
|
159432
|
-
})();
|
|
159433
|
-
await this.initPromise;
|
|
159434
|
-
return this.pipeline !== null;
|
|
159435
|
-
}
|
|
159436
|
-
waitForInFlightToDrain() {
|
|
159437
|
-
if (this.inFlight === 0) {
|
|
159438
|
-
return Promise.resolve();
|
|
159439
|
-
}
|
|
159440
|
-
return new Promise((resolve3) => {
|
|
159441
|
-
this.inFlightWaiters.push(resolve3);
|
|
159442
|
-
});
|
|
159443
|
-
}
|
|
159444
|
-
finishInFlight() {
|
|
159445
|
-
this.inFlight = Math.max(0, this.inFlight - 1);
|
|
159446
|
-
if (this.inFlight !== 0)
|
|
159447
|
-
return;
|
|
159448
|
-
const waiters = this.inFlightWaiters.splice(0);
|
|
159449
|
-
for (const waiter of waiters) {
|
|
159450
|
-
waiter();
|
|
159451
|
-
}
|
|
159452
|
-
}
|
|
159453
|
-
async embed(text, signal) {
|
|
159454
|
-
if (signal?.aborted)
|
|
159455
|
-
return null;
|
|
159456
|
-
if (this.disposing)
|
|
159457
|
-
return null;
|
|
159458
|
-
this.inFlight += 1;
|
|
159459
|
-
try {
|
|
159460
|
-
if (!await this.initialize()) {
|
|
159461
|
-
return null;
|
|
159462
|
-
}
|
|
159463
|
-
const pipeline = this.pipeline;
|
|
159464
|
-
if (!pipeline) {
|
|
159465
|
-
return null;
|
|
159466
|
-
}
|
|
159467
|
-
const result = await withQuietConsole(() => pipeline(text, {
|
|
159468
|
-
pooling: "mean",
|
|
159469
|
-
normalize: true
|
|
159470
|
-
}));
|
|
159471
|
-
return extractBatchEmbeddings(result, 1)[0] ?? null;
|
|
159472
|
-
} catch (error51) {
|
|
159473
|
-
log("[magic-context] embedding failed:", error51);
|
|
159474
|
-
return null;
|
|
159475
|
-
} finally {
|
|
159476
|
-
this.finishInFlight();
|
|
159477
|
-
}
|
|
159478
|
-
}
|
|
159479
|
-
async embedBatch(texts, signal) {
|
|
159480
|
-
if (texts.length === 0) {
|
|
159481
|
-
return [];
|
|
159482
|
-
}
|
|
159483
|
-
if (signal?.aborted) {
|
|
159484
|
-
return Array.from({ length: texts.length }, () => null);
|
|
159485
|
-
}
|
|
159486
|
-
if (this.disposing) {
|
|
159487
|
-
return Array.from({ length: texts.length }, () => null);
|
|
159488
|
-
}
|
|
159489
|
-
this.inFlight += 1;
|
|
159490
|
-
try {
|
|
159491
|
-
if (!await this.initialize()) {
|
|
159492
|
-
return Array.from({ length: texts.length }, () => null);
|
|
159493
|
-
}
|
|
159494
|
-
const pipeline = this.pipeline;
|
|
159495
|
-
if (!pipeline) {
|
|
159496
|
-
return Array.from({ length: texts.length }, () => null);
|
|
159497
|
-
}
|
|
159498
|
-
const result = await withQuietConsole(() => pipeline(texts, {
|
|
159499
|
-
pooling: "mean",
|
|
159500
|
-
normalize: true
|
|
159501
|
-
}));
|
|
159502
|
-
return extractBatchEmbeddings(result, texts.length);
|
|
159503
|
-
} catch (error51) {
|
|
159504
|
-
log("[magic-context] embedding batch failed:", error51);
|
|
159505
|
-
return Array.from({ length: texts.length }, () => null);
|
|
159506
|
-
} finally {
|
|
159507
|
-
this.finishInFlight();
|
|
159508
|
-
}
|
|
159509
|
-
}
|
|
159510
|
-
async dispose() {
|
|
159511
|
-
if (this.disposePromise) {
|
|
159512
|
-
return this.disposePromise;
|
|
159513
|
-
}
|
|
159514
|
-
this.disposing = true;
|
|
159515
|
-
this.disposePromise = (async () => {
|
|
159516
|
-
if (this.initPromise) {
|
|
159517
|
-
await this.initPromise;
|
|
159518
|
-
}
|
|
159519
|
-
await this.waitForInFlightToDrain();
|
|
159520
|
-
const pipelineToDispose = this.pipeline;
|
|
159521
|
-
this.pipeline = null;
|
|
159522
|
-
this.initPromise = null;
|
|
159523
|
-
if (!pipelineToDispose) {
|
|
159524
|
-
return;
|
|
159525
|
-
}
|
|
159526
|
-
try {
|
|
159527
|
-
await pipelineToDispose.dispose?.();
|
|
159528
|
-
} catch (error51) {
|
|
159529
|
-
log("[magic-context] embedding model dispose failed:", error51);
|
|
159530
|
-
}
|
|
159531
|
-
})();
|
|
159532
|
-
return this.disposePromise;
|
|
159533
|
-
}
|
|
159534
|
-
isLoaded() {
|
|
159535
|
-
return this.pipeline !== null;
|
|
159300
|
+
} : null;
|
|
159301
|
+
if (!identityInput) {
|
|
159302
|
+
return "embedding-provider:off";
|
|
159536
159303
|
}
|
|
159304
|
+
return `embedding-provider:${computeNormalizedHash(JSON.stringify(identityInput))}`;
|
|
159537
159305
|
}
|
|
159538
159306
|
|
|
159539
159307
|
// ../plugin/src/features/magic-context/memory/embedding-openai.ts
|
|
@@ -159615,6 +159383,7 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
159615
159383
|
model;
|
|
159616
159384
|
apiKey;
|
|
159617
159385
|
inputType;
|
|
159386
|
+
queryInputType;
|
|
159618
159387
|
truncate;
|
|
159619
159388
|
initialized = false;
|
|
159620
159389
|
failureTimes = [];
|
|
@@ -159627,6 +159396,7 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
159627
159396
|
this.model = options.model?.trim() ?? "";
|
|
159628
159397
|
this.apiKey = options.apiKey?.trim() ?? "";
|
|
159629
159398
|
this.inputType = options.inputType?.trim() ?? "";
|
|
159399
|
+
this.queryInputType = options.queryInputType?.trim() ?? "";
|
|
159630
159400
|
this.truncate = options.truncate?.trim() ?? "";
|
|
159631
159401
|
this.maxInputTokens = typeof options.maxInputTokens === "number" && Number.isFinite(options.maxInputTokens) ? Math.max(1, Math.floor(options.maxInputTokens)) : 512;
|
|
159632
159402
|
this.modelId = getEmbeddingProviderIdentity({
|
|
@@ -159654,11 +159424,17 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
159654
159424
|
this.initialized = true;
|
|
159655
159425
|
return true;
|
|
159656
159426
|
}
|
|
159657
|
-
|
|
159658
|
-
|
|
159427
|
+
resolveInputTypeForPurpose(purpose = "passage") {
|
|
159428
|
+
if (purpose === "query") {
|
|
159429
|
+
return this.queryInputType || this.inputType;
|
|
159430
|
+
}
|
|
159431
|
+
return this.inputType;
|
|
159432
|
+
}
|
|
159433
|
+
async embed(text, signal, purpose) {
|
|
159434
|
+
const [embedding] = await this.embedBatch([text], signal, purpose);
|
|
159659
159435
|
return embedding ?? null;
|
|
159660
159436
|
}
|
|
159661
|
-
async embedBatch(texts, signal) {
|
|
159437
|
+
async embedBatch(texts, signal, purpose) {
|
|
159662
159438
|
if (texts.length === 0) {
|
|
159663
159439
|
return [];
|
|
159664
159440
|
}
|
|
@@ -159684,6 +159460,7 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
159684
159460
|
if (signal) {
|
|
159685
159461
|
signal.addEventListener("abort", onOuterAbort, { once: true });
|
|
159686
159462
|
}
|
|
159463
|
+
const inputTypeForRequest = this.resolveInputTypeForPurpose(purpose);
|
|
159687
159464
|
const response = await fetch(`${this.endpoint}/embeddings`, {
|
|
159688
159465
|
method: "POST",
|
|
159689
159466
|
headers: {
|
|
@@ -159693,7 +159470,7 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
159693
159470
|
body: JSON.stringify({
|
|
159694
159471
|
model: this.model,
|
|
159695
159472
|
input: texts,
|
|
159696
|
-
...
|
|
159473
|
+
...inputTypeForRequest ? { input_type: inputTypeForRequest } : {},
|
|
159697
159474
|
...this.truncate ? { truncate: this.truncate } : {}
|
|
159698
159475
|
}),
|
|
159699
159476
|
redirect: "error",
|
|
@@ -159849,7 +159626,7 @@ function isEmbeddingRow(row) {
|
|
|
159849
159626
|
const candidate = row;
|
|
159850
159627
|
return typeof candidate.memoryId === "number" && isEmbeddingBlob(candidate.embedding) && (candidate.modelId === null || typeof candidate.modelId === "string");
|
|
159851
159628
|
}
|
|
159852
|
-
function
|
|
159629
|
+
function toFloat32Array2(blob) {
|
|
159853
159630
|
if (blob instanceof Uint8Array) {
|
|
159854
159631
|
const buffer2 = blob.buffer.slice(blob.byteOffset, blob.byteOffset + blob.byteLength);
|
|
159855
159632
|
return new Float32Array(buffer2);
|
|
@@ -159897,7 +159674,7 @@ function loadAllEmbeddings(db, projectPath) {
|
|
|
159897
159674
|
const embeddings = new Map;
|
|
159898
159675
|
for (const row of rows) {
|
|
159899
159676
|
embeddings.set(row.memoryId, {
|
|
159900
|
-
embedding:
|
|
159677
|
+
embedding: toFloat32Array2(row.embedding),
|
|
159901
159678
|
modelId: row.modelId
|
|
159902
159679
|
});
|
|
159903
159680
|
}
|
|
@@ -159910,10 +159687,24 @@ function getDistinctStoredModelIds(db, projectPath) {
|
|
|
159910
159687
|
const rows = getDistinctStoredModelIdsStatement(db).all(projectPath);
|
|
159911
159688
|
return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
|
|
159912
159689
|
}
|
|
159690
|
+
function getMemoryEmbedCoverage(db, projectPath, modelId) {
|
|
159691
|
+
const row = db.prepare(`SELECT
|
|
159692
|
+
COUNT(*) AS total,
|
|
159693
|
+
SUM(CASE WHEN EXISTS (
|
|
159694
|
+
SELECT 1 FROM memory_embeddings e
|
|
159695
|
+
WHERE e.memory_id = m.id AND e.model_id = ?
|
|
159696
|
+
) THEN 1 ELSE 0 END) AS embedded
|
|
159697
|
+
FROM memories m
|
|
159698
|
+
WHERE m.project_path = ? AND m.status = 'active'`).get(modelId, projectPath);
|
|
159699
|
+
return {
|
|
159700
|
+
total: typeof row?.total === "number" ? row.total : 0,
|
|
159701
|
+
embedded: typeof row?.embedded === "number" ? row.embedded : 0
|
|
159702
|
+
};
|
|
159703
|
+
}
|
|
159913
159704
|
|
|
159914
159705
|
// ../plugin/src/features/magic-context/project-embedding-registry.ts
|
|
159915
|
-
import { createHash as createHash6, randomUUID } from "node:crypto";
|
|
159916
159706
|
init_logger();
|
|
159707
|
+
import { createHash as createHash6, randomUUID } from "node:crypto";
|
|
159917
159708
|
|
|
159918
159709
|
// ../plugin/src/features/magic-context/git-commits/storage-git-commit-embeddings.ts
|
|
159919
159710
|
var saveStatements = new WeakMap;
|
|
@@ -160016,6 +159807,118 @@ function getDistinctCommitEmbeddingModelIds(db, projectPath) {
|
|
|
160016
159807
|
return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
|
|
160017
159808
|
}
|
|
160018
159809
|
|
|
159810
|
+
// ../plugin/src/features/magic-context/git-commits/storage-git-commits.ts
|
|
159811
|
+
init_logger();
|
|
159812
|
+
var insertStatements = new WeakMap;
|
|
159813
|
+
var existingShasStatements = new WeakMap;
|
|
159814
|
+
var projectCountStatements = new WeakMap;
|
|
159815
|
+
var evictStatements = new WeakMap;
|
|
159816
|
+
var evictOverflowStatements = new WeakMap;
|
|
159817
|
+
var latestCommitTimeStatements = new WeakMap;
|
|
159818
|
+
function getInsertStatement(db) {
|
|
159819
|
+
let stmt = insertStatements.get(db);
|
|
159820
|
+
if (!stmt) {
|
|
159821
|
+
stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
|
|
159822
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
159823
|
+
ON CONFLICT(sha) DO UPDATE SET
|
|
159824
|
+
project_path = excluded.project_path,
|
|
159825
|
+
short_sha = excluded.short_sha,
|
|
159826
|
+
message = excluded.message,
|
|
159827
|
+
author = excluded.author,
|
|
159828
|
+
committed_at = excluded.committed_at,
|
|
159829
|
+
indexed_at = excluded.indexed_at
|
|
159830
|
+
WHERE git_commits.message != excluded.message`);
|
|
159831
|
+
insertStatements.set(db, stmt);
|
|
159832
|
+
}
|
|
159833
|
+
return stmt;
|
|
159834
|
+
}
|
|
159835
|
+
function getExistingShasStatement(db) {
|
|
159836
|
+
let stmt = existingShasStatements.get(db);
|
|
159837
|
+
if (!stmt) {
|
|
159838
|
+
stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
|
|
159839
|
+
existingShasStatements.set(db, stmt);
|
|
159840
|
+
}
|
|
159841
|
+
return stmt;
|
|
159842
|
+
}
|
|
159843
|
+
function getProjectCountStatement(db) {
|
|
159844
|
+
let stmt = projectCountStatements.get(db);
|
|
159845
|
+
if (!stmt) {
|
|
159846
|
+
stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
|
|
159847
|
+
projectCountStatements.set(db, stmt);
|
|
159848
|
+
}
|
|
159849
|
+
return stmt;
|
|
159850
|
+
}
|
|
159851
|
+
function getLatestCommitTimeStatement(db) {
|
|
159852
|
+
let stmt = latestCommitTimeStatements.get(db);
|
|
159853
|
+
if (!stmt) {
|
|
159854
|
+
stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
|
|
159855
|
+
latestCommitTimeStatements.set(db, stmt);
|
|
159856
|
+
}
|
|
159857
|
+
return stmt;
|
|
159858
|
+
}
|
|
159859
|
+
function getEvictOverflowStatement(db) {
|
|
159860
|
+
let stmt = evictOverflowStatements.get(db);
|
|
159861
|
+
if (!stmt) {
|
|
159862
|
+
stmt = db.prepare(`DELETE FROM git_commits
|
|
159863
|
+
WHERE rowid IN (
|
|
159864
|
+
SELECT rowid FROM git_commits
|
|
159865
|
+
WHERE project_path = ?
|
|
159866
|
+
ORDER BY committed_at DESC, sha DESC
|
|
159867
|
+
LIMIT -1 OFFSET ?
|
|
159868
|
+
)`);
|
|
159869
|
+
evictOverflowStatements.set(db, stmt);
|
|
159870
|
+
}
|
|
159871
|
+
return stmt;
|
|
159872
|
+
}
|
|
159873
|
+
function upsertCommits(db, projectPath, commits) {
|
|
159874
|
+
if (commits.length === 0)
|
|
159875
|
+
return { inserted: 0, updated: 0 };
|
|
159876
|
+
const existing = new Set;
|
|
159877
|
+
for (const row of getExistingShasStatement(db).all(projectPath)) {
|
|
159878
|
+
existing.add(row.sha);
|
|
159879
|
+
}
|
|
159880
|
+
let inserted = 0;
|
|
159881
|
+
let updated = 0;
|
|
159882
|
+
const now = Date.now();
|
|
159883
|
+
const insertStmt = getInsertStatement(db);
|
|
159884
|
+
db.transaction(() => {
|
|
159885
|
+
for (const commit of commits) {
|
|
159886
|
+
const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
|
|
159887
|
+
if (result.changes > 0) {
|
|
159888
|
+
if (existing.has(commit.sha)) {
|
|
159889
|
+
updated++;
|
|
159890
|
+
} else {
|
|
159891
|
+
inserted++;
|
|
159892
|
+
existing.add(commit.sha);
|
|
159893
|
+
}
|
|
159894
|
+
}
|
|
159895
|
+
}
|
|
159896
|
+
})();
|
|
159897
|
+
return { inserted, updated };
|
|
159898
|
+
}
|
|
159899
|
+
function getCommitCount(db, projectPath) {
|
|
159900
|
+
const row = getProjectCountStatement(db).get(projectPath);
|
|
159901
|
+
return row?.count ?? 0;
|
|
159902
|
+
}
|
|
159903
|
+
function getLatestIndexedCommitTimeMs(db, projectPath) {
|
|
159904
|
+
const row = getLatestCommitTimeStatement(db).get(projectPath);
|
|
159905
|
+
return row?.latest ?? null;
|
|
159906
|
+
}
|
|
159907
|
+
function enforceProjectCap(db, projectPath, maxCommits) {
|
|
159908
|
+
if (maxCommits <= 0)
|
|
159909
|
+
return 0;
|
|
159910
|
+
const count = getCommitCount(db, projectPath);
|
|
159911
|
+
if (count <= maxCommits)
|
|
159912
|
+
return 0;
|
|
159913
|
+
getEvictOverflowStatement(db).run(projectPath, maxCommits);
|
|
159914
|
+
const after = getCommitCount(db, projectPath);
|
|
159915
|
+
const evicted = Math.max(0, count - after);
|
|
159916
|
+
if (evicted > 0) {
|
|
159917
|
+
log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
|
|
159918
|
+
}
|
|
159919
|
+
return evicted;
|
|
159920
|
+
}
|
|
159921
|
+
|
|
160019
159922
|
// ../plugin/src/features/magic-context/git-commits/sweep-coordinator.ts
|
|
160020
159923
|
var GIT_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
|
|
160021
159924
|
var GIT_SWEEP_LEASE_TTL_MS = 5 * 60 * 1000;
|
|
@@ -160248,25 +160151,24 @@ function repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectPath) {
|
|
|
160248
160151
|
var OFF_PROVIDER_IDENTITY = "embedding-provider:off";
|
|
160249
160152
|
var SWEEP_MAX_WALL_CLOCK_MS = 10 * 60 * 1000;
|
|
160250
160153
|
var CHUNK_DRAIN_BATCH_SIZE = 8;
|
|
160251
|
-
var MAX_WINDOWS_PER_EMBED_CALL =
|
|
160154
|
+
var MAX_WINDOWS_PER_EMBED_CALL = 2;
|
|
160252
160155
|
var SESSION_EMBED_LEASE_RENEWAL_MS = 60 * 1000;
|
|
160156
|
+
var EMBED_SLICE_RETRY_ATTEMPTS = 3;
|
|
160157
|
+
var EMBED_SLICE_RETRY_BASE_MS = 250;
|
|
160158
|
+
var EMBED_SLOW_FAILURE_NO_RETRY_MS = 1e4;
|
|
160159
|
+
var MAX_CONSECUTIVE_FAILED_BATCHES = 3;
|
|
160253
160160
|
var projectRegistrations = new Map;
|
|
160254
160161
|
var loadUnembeddedMemoriesStatements = new WeakMap;
|
|
160255
160162
|
var globalRegistrationGeneration = 0;
|
|
160256
160163
|
var testProviderFactory = null;
|
|
160257
160164
|
function resolveEmbeddingConfig(config2) {
|
|
160258
|
-
if (!config2 || config2.provider === "
|
|
160259
|
-
return {
|
|
160260
|
-
provider: "local",
|
|
160261
|
-
model: config2?.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
|
|
160262
|
-
...config2?.max_input_tokens ? {
|
|
160263
|
-
max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
|
|
160264
|
-
} : {}
|
|
160265
|
-
};
|
|
160165
|
+
if (!config2 || config2.provider === "off") {
|
|
160166
|
+
return { provider: "off" };
|
|
160266
160167
|
}
|
|
160267
160168
|
if (config2.provider === "openai-compatible") {
|
|
160268
160169
|
const apiKey = config2.api_key?.trim();
|
|
160269
160170
|
const inputType = config2.input_type?.trim();
|
|
160171
|
+
const queryInputType = config2.query_input_type?.trim();
|
|
160270
160172
|
const truncate = config2.truncate?.trim();
|
|
160271
160173
|
return {
|
|
160272
160174
|
provider: "openai-compatible",
|
|
@@ -160274,6 +160176,7 @@ function resolveEmbeddingConfig(config2) {
|
|
|
160274
160176
|
endpoint: config2.endpoint.trim(),
|
|
160275
160177
|
...apiKey ? { api_key: apiKey } : {},
|
|
160276
160178
|
...inputType ? { input_type: inputType } : {},
|
|
160179
|
+
...queryInputType ? { query_input_type: queryInputType } : {},
|
|
160277
160180
|
...truncate ? { truncate } : {},
|
|
160278
160181
|
...config2.max_input_tokens ? {
|
|
160279
160182
|
max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
|
|
@@ -160295,11 +160198,12 @@ function createProvider(config2) {
|
|
|
160295
160198
|
model: config2.model,
|
|
160296
160199
|
apiKey: config2.api_key,
|
|
160297
160200
|
inputType: config2.input_type,
|
|
160201
|
+
queryInputType: config2.query_input_type,
|
|
160298
160202
|
truncate: config2.truncate,
|
|
160299
160203
|
maxInputTokens: config2.max_input_tokens
|
|
160300
160204
|
});
|
|
160301
160205
|
}
|
|
160302
|
-
return
|
|
160206
|
+
return null;
|
|
160303
160207
|
}
|
|
160304
160208
|
function stableStringify2(value) {
|
|
160305
160209
|
if (Array.isArray(value)) {
|
|
@@ -160349,7 +160253,9 @@ function snapshotFor(registration) {
|
|
|
160349
160253
|
enabled,
|
|
160350
160254
|
gitCommitEnabled,
|
|
160351
160255
|
modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
|
|
160352
|
-
chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId
|
|
160256
|
+
chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId,
|
|
160257
|
+
model: registration.observationMode || !providerIsOn ? "off" : ("model" in registration.config) && registration.config.model.trim() ? registration.config.model.trim() : registration.modelId,
|
|
160258
|
+
provider: registration.observationMode || !providerIsOn ? "off" : registration.config.provider ?? "off"
|
|
160353
160259
|
};
|
|
160354
160260
|
}
|
|
160355
160261
|
function disposeProvider(provider) {
|
|
@@ -160475,7 +160381,7 @@ function getOrCreateProjectProvider(registration) {
|
|
|
160475
160381
|
registration.provider = provider;
|
|
160476
160382
|
return provider;
|
|
160477
160383
|
}
|
|
160478
|
-
async function embedTextForProject(projectIdentity, text, signal) {
|
|
160384
|
+
async function embedTextForProject(projectIdentity, text, signal, purpose = "passage") {
|
|
160479
160385
|
const registration = projectRegistrations.get(projectIdentity);
|
|
160480
160386
|
if (!registration)
|
|
160481
160387
|
return null;
|
|
@@ -160484,7 +160390,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
|
|
|
160484
160390
|
const provider = getOrCreateProjectProvider(registration);
|
|
160485
160391
|
if (!provider)
|
|
160486
160392
|
return null;
|
|
160487
|
-
const vector = await provider.embed(text, signal);
|
|
160393
|
+
const vector = await provider.embed(text, signal, purpose);
|
|
160488
160394
|
if (!vector)
|
|
160489
160395
|
return null;
|
|
160490
160396
|
const current = projectRegistrations.get(projectIdentity);
|
|
@@ -160493,7 +160399,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
|
|
|
160493
160399
|
}
|
|
160494
160400
|
return { vector, modelId, generation };
|
|
160495
160401
|
}
|
|
160496
|
-
async function embedBatchForProject(projectIdentity, texts, signal) {
|
|
160402
|
+
async function embedBatchForProject(projectIdentity, texts, signal, purpose = "passage") {
|
|
160497
160403
|
if (texts.length === 0) {
|
|
160498
160404
|
const registration2 = projectRegistrations.get(projectIdentity);
|
|
160499
160405
|
if (!registration2 || registration2.observationMode)
|
|
@@ -160509,7 +160415,7 @@ async function embedBatchForProject(projectIdentity, texts, signal) {
|
|
|
160509
160415
|
const provider = getOrCreateProjectProvider(registration);
|
|
160510
160416
|
if (!provider)
|
|
160511
160417
|
return null;
|
|
160512
|
-
const vectors = await provider.embedBatch(texts, signal);
|
|
160418
|
+
const vectors = await provider.embedBatch(texts, signal, purpose);
|
|
160513
160419
|
const current = projectRegistrations.get(projectIdentity);
|
|
160514
160420
|
if (!current || current.generation !== generation || current.runtimeFingerprint !== runtimeFingerprint) {
|
|
160515
160421
|
return null;
|
|
@@ -160560,12 +160466,13 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
160560
160466
|
}
|
|
160561
160467
|
async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates, signal) {
|
|
160562
160468
|
const noWork = [];
|
|
160469
|
+
const failed = [];
|
|
160563
160470
|
if (candidates.length === 0)
|
|
160564
|
-
return { embedded: 0, noWork };
|
|
160471
|
+
return { embedded: 0, noWork, failed };
|
|
160565
160472
|
const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectIdentity);
|
|
160566
160473
|
const prepared = [];
|
|
160567
160474
|
for (const candidate of candidates) {
|
|
160568
|
-
const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage);
|
|
160475
|
+
const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage) || buildCompartmentSummaryFallbackText(db, candidate.id);
|
|
160569
160476
|
if (canonicalText.length === 0) {
|
|
160570
160477
|
noWork.push(candidate.id);
|
|
160571
160478
|
continue;
|
|
@@ -160578,7 +160485,7 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
160578
160485
|
prepared.push({ candidate, windows });
|
|
160579
160486
|
}
|
|
160580
160487
|
if (prepared.length === 0)
|
|
160581
|
-
return { embedded: 0, noWork };
|
|
160488
|
+
return { embedded: 0, noWork, failed };
|
|
160582
160489
|
let embedded = 0;
|
|
160583
160490
|
let i = 0;
|
|
160584
160491
|
while (i < prepared.length) {
|
|
@@ -160595,35 +160502,60 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
160595
160502
|
const texts = [];
|
|
160596
160503
|
for (const item of slice)
|
|
160597
160504
|
texts.push(...item.windows.map((w) => w.text));
|
|
160598
|
-
|
|
160599
|
-
|
|
160600
|
-
if (!result)
|
|
160601
|
-
continue;
|
|
160505
|
+
const persistedIds = new Set;
|
|
160506
|
+
for (let attempt = 0;attempt < EMBED_SLICE_RETRY_ATTEMPTS; attempt++) {
|
|
160602
160507
|
if (signal?.aborted)
|
|
160603
160508
|
break;
|
|
160604
|
-
let
|
|
160605
|
-
|
|
160606
|
-
|
|
160607
|
-
|
|
160608
|
-
|
|
160609
|
-
|
|
160509
|
+
let result = null;
|
|
160510
|
+
const attemptStart = Date.now();
|
|
160511
|
+
try {
|
|
160512
|
+
result = await embedBatchForProject(projectIdentity, texts, signal);
|
|
160513
|
+
} catch (error51) {
|
|
160514
|
+
log("[magic-context] failed to proactively embed compartment chunks:", error51);
|
|
160515
|
+
}
|
|
160516
|
+
if (signal?.aborted)
|
|
160517
|
+
break;
|
|
160518
|
+
if (result) {
|
|
160519
|
+
let offset = 0;
|
|
160520
|
+
for (const item of slice) {
|
|
160521
|
+
const vectors = result.vectors.slice(offset, offset + item.windows.length);
|
|
160522
|
+
offset += item.windows.length;
|
|
160523
|
+
if (persistedIds.has(item.candidate.id))
|
|
160524
|
+
continue;
|
|
160525
|
+
if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
|
|
160526
|
+
continue;
|
|
160527
|
+
}
|
|
160528
|
+
const rows = item.windows.map((window, index) => ({
|
|
160529
|
+
compartmentId: item.candidate.id,
|
|
160530
|
+
sessionId: item.candidate.sessionId,
|
|
160531
|
+
projectPath: projectIdentity,
|
|
160532
|
+
window,
|
|
160533
|
+
modelId,
|
|
160534
|
+
vector: vectors[index]
|
|
160535
|
+
}));
|
|
160536
|
+
replaceCompartmentChunkEmbeddings(db, rows);
|
|
160537
|
+
persistedIds.add(item.candidate.id);
|
|
160610
160538
|
}
|
|
160611
|
-
const rows = item.windows.map((window, index) => ({
|
|
160612
|
-
compartmentId: item.candidate.id,
|
|
160613
|
-
sessionId: item.candidate.sessionId,
|
|
160614
|
-
projectPath: projectIdentity,
|
|
160615
|
-
window,
|
|
160616
|
-
modelId,
|
|
160617
|
-
vector: vectors[index]
|
|
160618
|
-
}));
|
|
160619
|
-
replaceCompartmentChunkEmbeddings(db, rows);
|
|
160620
|
-
embedded += 1;
|
|
160621
160539
|
}
|
|
160622
|
-
|
|
160623
|
-
|
|
160540
|
+
if (persistedIds.size === slice.length)
|
|
160541
|
+
break;
|
|
160542
|
+
if (persistedIds.size > 0)
|
|
160543
|
+
break;
|
|
160544
|
+
if (Date.now() - attemptStart >= EMBED_SLOW_FAILURE_NO_RETRY_MS)
|
|
160545
|
+
break;
|
|
160546
|
+
if (attempt < EMBED_SLICE_RETRY_ATTEMPTS - 1) {
|
|
160547
|
+
await new Promise((resolve3) => setTimeout(resolve3, EMBED_SLICE_RETRY_BASE_MS * 2 ** attempt));
|
|
160548
|
+
}
|
|
160549
|
+
}
|
|
160550
|
+
embedded += persistedIds.size;
|
|
160551
|
+
if (!signal?.aborted) {
|
|
160552
|
+
for (const item of slice) {
|
|
160553
|
+
if (!persistedIds.has(item.candidate.id))
|
|
160554
|
+
failed.push(item.candidate.id);
|
|
160555
|
+
}
|
|
160624
160556
|
}
|
|
160625
160557
|
}
|
|
160626
|
-
return { embedded, noWork };
|
|
160558
|
+
return { embedded, noWork, failed };
|
|
160627
160559
|
}
|
|
160628
160560
|
async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, options) {
|
|
160629
160561
|
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
@@ -160646,9 +160578,11 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
160646
160578
|
renewal.unref?.();
|
|
160647
160579
|
const batchSize = Math.max(1, options?.batchSize ?? CHUNK_DRAIN_BATCH_SIZE);
|
|
160648
160580
|
const skipIds = [];
|
|
160581
|
+
const failedIds = [];
|
|
160649
160582
|
let embedded = 0;
|
|
160650
160583
|
let aborted2 = false;
|
|
160651
|
-
let
|
|
160584
|
+
let providerDown = false;
|
|
160585
|
+
let consecutiveFailedBatches = 0;
|
|
160652
160586
|
try {
|
|
160653
160587
|
options?.onProgress?.({ embedded, total });
|
|
160654
160588
|
for (;; ) {
|
|
@@ -160656,15 +160590,26 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
160656
160590
|
aborted2 = true;
|
|
160657
160591
|
break;
|
|
160658
160592
|
}
|
|
160659
|
-
const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, skipIds);
|
|
160593
|
+
const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, [...skipIds, ...failedIds]);
|
|
160660
160594
|
if (candidates.length === 0)
|
|
160661
160595
|
break;
|
|
160662
|
-
const {
|
|
160596
|
+
const {
|
|
160597
|
+
embedded: n,
|
|
160598
|
+
noWork,
|
|
160599
|
+
failed
|
|
160600
|
+
} = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
|
|
160663
160601
|
for (const id of noWork)
|
|
160664
160602
|
skipIds.push(id);
|
|
160603
|
+
for (const id of failed)
|
|
160604
|
+
failedIds.push(id);
|
|
160665
160605
|
if (n === 0 && noWork.length === 0) {
|
|
160666
|
-
|
|
160667
|
-
|
|
160606
|
+
consecutiveFailedBatches += 1;
|
|
160607
|
+
if (consecutiveFailedBatches >= MAX_CONSECUTIVE_FAILED_BATCHES) {
|
|
160608
|
+
providerDown = true;
|
|
160609
|
+
break;
|
|
160610
|
+
}
|
|
160611
|
+
} else {
|
|
160612
|
+
consecutiveFailedBatches = 0;
|
|
160668
160613
|
}
|
|
160669
160614
|
embedded += n;
|
|
160670
160615
|
options?.onProgress?.({ embedded: Math.min(embedded, total), total });
|
|
@@ -160672,22 +160617,55 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
160672
160617
|
}
|
|
160673
160618
|
} finally {
|
|
160674
160619
|
clearInterval(renewal);
|
|
160675
|
-
|
|
160620
|
+
try {
|
|
160621
|
+
releaseGitSweepLease(db, projectIdentity, holderId);
|
|
160622
|
+
} catch (error51) {
|
|
160623
|
+
log("[magic-context] embed drain: lease release failed (will TTL-expire):", error51);
|
|
160624
|
+
}
|
|
160676
160625
|
}
|
|
160677
160626
|
if (aborted2)
|
|
160678
|
-
return { status: "aborted", embedded, total };
|
|
160679
|
-
if (
|
|
160627
|
+
return { status: "aborted", embedded, total, failed: failedIds.length };
|
|
160628
|
+
if (providerDown || failedIds.length > 0) {
|
|
160680
160629
|
const remaining = Math.max(0, countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId) - skipIds.length);
|
|
160681
|
-
if (remaining > 0)
|
|
160682
|
-
return { status: "stalled", embedded, total, remaining };
|
|
160630
|
+
if (remaining > 0) {
|
|
160631
|
+
return { status: "stalled", embedded, total, remaining, failed: failedIds.length };
|
|
160632
|
+
}
|
|
160633
|
+
}
|
|
160634
|
+
return { status: "done", embedded, total, failed: failedIds.length };
|
|
160635
|
+
}
|
|
160636
|
+
function getEmbeddingCoverageStatus(db, projectIdentity, sessionId) {
|
|
160637
|
+
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
160638
|
+
if (!snapshot?.enabled || snapshot.chunkModelId === "off") {
|
|
160639
|
+
return {
|
|
160640
|
+
enabled: false,
|
|
160641
|
+
model: snapshot?.model ?? "off",
|
|
160642
|
+
provider: snapshot?.provider ?? "off",
|
|
160643
|
+
session: { embedded: 0, total: 0 },
|
|
160644
|
+
memories: { embedded: 0, total: 0 },
|
|
160645
|
+
commits: { embedded: 0, total: 0, gitEnabled: false }
|
|
160646
|
+
};
|
|
160683
160647
|
}
|
|
160684
|
-
|
|
160648
|
+
const session = countSessionCompartmentEmbedCoverage(db, projectIdentity, sessionId, snapshot.chunkModelId);
|
|
160649
|
+
const memories = getMemoryEmbedCoverage(db, projectIdentity, snapshot.modelId);
|
|
160650
|
+
const gitEnabled = snapshot.gitCommitEnabled;
|
|
160651
|
+
const commits = gitEnabled ? {
|
|
160652
|
+
embedded: countEmbeddedCommits(db, projectIdentity),
|
|
160653
|
+
total: getCommitCount(db, projectIdentity),
|
|
160654
|
+
gitEnabled: true
|
|
160655
|
+
} : { embedded: 0, total: 0, gitEnabled: false };
|
|
160656
|
+
return {
|
|
160657
|
+
enabled: true,
|
|
160658
|
+
model: snapshot.model,
|
|
160659
|
+
provider: snapshot.provider,
|
|
160660
|
+
session,
|
|
160661
|
+
memories,
|
|
160662
|
+
commits
|
|
160663
|
+
};
|
|
160685
160664
|
}
|
|
160686
160665
|
|
|
160687
160666
|
// ../plugin/src/features/magic-context/memory/embedding.ts
|
|
160688
160667
|
var DEFAULT_EMBEDDING_CONFIG = {
|
|
160689
|
-
provider: "
|
|
160690
|
-
model: DEFAULT_LOCAL_EMBEDDING_MODEL
|
|
160668
|
+
provider: "off"
|
|
160691
160669
|
};
|
|
160692
160670
|
var embeddingConfig = DEFAULT_EMBEDDING_CONFIG;
|
|
160693
160671
|
var provider = null;
|
|
@@ -160702,11 +160680,12 @@ function createProvider2(config2) {
|
|
|
160702
160680
|
model: config2.model,
|
|
160703
160681
|
apiKey: config2.api_key,
|
|
160704
160682
|
inputType: config2.input_type,
|
|
160683
|
+
queryInputType: config2.query_input_type,
|
|
160705
160684
|
truncate: config2.truncate,
|
|
160706
160685
|
maxInputTokens: config2.max_input_tokens
|
|
160707
160686
|
});
|
|
160708
160687
|
}
|
|
160709
|
-
return
|
|
160688
|
+
return null;
|
|
160710
160689
|
}
|
|
160711
160690
|
function getOrCreateProvider() {
|
|
160712
160691
|
if (provider) {
|
|
@@ -160910,6 +160889,7 @@ var SESSION_META_SELECT_COLUMNS = [
|
|
|
160910
160889
|
"conversation_tokens",
|
|
160911
160890
|
"tool_call_tokens",
|
|
160912
160891
|
"cleared_reasoning_through_tag",
|
|
160892
|
+
"tool_reclaim_watermark",
|
|
160913
160893
|
"last_todo_state",
|
|
160914
160894
|
"cached_m0_bytes",
|
|
160915
160895
|
"cached_m1_bytes",
|
|
@@ -160936,6 +160916,8 @@ var SESSION_META_SELECT_COLUMNS = [
|
|
|
160936
160916
|
"recovery_no_eligible_head_count",
|
|
160937
160917
|
"force_emergency_bypass_window_start",
|
|
160938
160918
|
"force_emergency_bypass_used",
|
|
160919
|
+
"emergency_drain_active",
|
|
160920
|
+
"historian_drain_failure_at",
|
|
160939
160921
|
"upgrade_reminded_at",
|
|
160940
160922
|
"pi_stable_id_scheme"
|
|
160941
160923
|
];
|
|
@@ -160958,6 +160940,7 @@ var META_COLUMNS = {
|
|
|
160958
160940
|
conversationTokens: "conversation_tokens",
|
|
160959
160941
|
toolCallTokens: "tool_call_tokens",
|
|
160960
160942
|
clearedReasoningThroughTag: "cleared_reasoning_through_tag",
|
|
160943
|
+
toolReclaimWatermark: "tool_reclaim_watermark",
|
|
160961
160944
|
lastTodoState: "last_todo_state",
|
|
160962
160945
|
cachedM0Bytes: "cached_m0_bytes",
|
|
160963
160946
|
cachedM1Bytes: "cached_m1_bytes",
|
|
@@ -160984,6 +160967,8 @@ var META_COLUMNS = {
|
|
|
160984
160967
|
recoveryNoEligibleHeadCount: "recovery_no_eligible_head_count",
|
|
160985
160968
|
forceEmergencyBypassWindowStart: "force_emergency_bypass_window_start",
|
|
160986
160969
|
forceEmergencyBypassUsed: "force_emergency_bypass_used",
|
|
160970
|
+
emergencyDrainActive: "emergency_drain_active",
|
|
160971
|
+
historianDrainFailureAt: "historian_drain_failure_at",
|
|
160987
160972
|
upgradeRemindedAt: "upgrade_reminded_at",
|
|
160988
160973
|
piStableIdScheme: "pi_stable_id_scheme"
|
|
160989
160974
|
};
|
|
@@ -161026,7 +161011,7 @@ function isSessionMetaRow(row) {
|
|
|
161026
161011
|
if (row === null || typeof row !== "object")
|
|
161027
161012
|
return false;
|
|
161028
161013
|
const r = row;
|
|
161029
|
-
return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.observed_safe_input_tokens) && isNumberOrNull(r.cache_alert_sent) && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state) && isBlobOrNull(r.cached_m0_bytes) && isBlobOrNull(r.cached_m1_bytes) && isNumberOrNull(r.cached_m0_project_memory_epoch) && isStringOrNull(r.cached_m0_workspace_fingerprint) && isNumberOrNull(r.cached_m0_project_user_profile_version) && isNumberOrNull(r.cached_m0_max_compartment_seq) && isNumberOrNull(r.cached_m0_max_memory_id) && isNumberOrNull(r.cached_m0_max_mutation_id) && isNumberOrNull(r.cached_m0_max_memory_mutation_id) && isStringOrNull(r.cached_m0_project_docs_hash) && isNumberOrNull(r.cached_m0_materialized_at) && isNumberOrNull(r.cached_m0_session_facts_version) && isStringOrNull(r.cached_m0_upgrade_state) && isStringOrNull(r.cached_m0_system_hash) && isStringOrNull(r.cached_m0_tool_set_hash) && isStringOrNull(r.cached_m0_model_key) && isStringOrNull(r.last_observed_model_key) && isNumberOrNull(r.last_usage_context_limit) && isNumberOrNull(r.prior_boundary_ordinal) && isNumberOrNull(r.protected_tail_policy_version) && isNumberOrNull(r.protected_tail_drain_window_started_at) && isNumberOrNull(r.protected_tail_drain_tokens) && isNumberOrNull(r.recovery_no_eligible_head_count) && isNumberOrNull(r.force_emergency_bypass_window_start) && isNumberOrNull(r.force_emergency_bypass_used) && isNumberOrNull(r.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme);
|
|
161014
|
+
return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.observed_safe_input_tokens) && isNumberOrNull(r.cache_alert_sent) && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state) && isBlobOrNull(r.cached_m0_bytes) && isBlobOrNull(r.cached_m1_bytes) && isNumberOrNull(r.cached_m0_project_memory_epoch) && isStringOrNull(r.cached_m0_workspace_fingerprint) && isNumberOrNull(r.cached_m0_project_user_profile_version) && isNumberOrNull(r.cached_m0_max_compartment_seq) && isNumberOrNull(r.cached_m0_max_memory_id) && isNumberOrNull(r.cached_m0_max_mutation_id) && isNumberOrNull(r.cached_m0_max_memory_mutation_id) && isStringOrNull(r.cached_m0_project_docs_hash) && isNumberOrNull(r.cached_m0_materialized_at) && isNumberOrNull(r.cached_m0_session_facts_version) && isStringOrNull(r.cached_m0_upgrade_state) && isStringOrNull(r.cached_m0_system_hash) && isStringOrNull(r.cached_m0_tool_set_hash) && isStringOrNull(r.cached_m0_model_key) && isStringOrNull(r.last_observed_model_key) && isNumberOrNull(r.last_usage_context_limit) && isNumberOrNull(r.prior_boundary_ordinal) && isNumberOrNull(r.protected_tail_policy_version) && isNumberOrNull(r.protected_tail_drain_window_started_at) && isNumberOrNull(r.protected_tail_drain_tokens) && isNumberOrNull(r.recovery_no_eligible_head_count) && isNumberOrNull(r.force_emergency_bypass_window_start) && isNumberOrNull(r.force_emergency_bypass_used) && isNumberOrNull(r.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme) && isNumberOrNull(r.tool_reclaim_watermark);
|
|
161030
161015
|
}
|
|
161031
161016
|
function getDefaultSessionMeta(sessionId) {
|
|
161032
161017
|
return {
|
|
@@ -161049,6 +161034,7 @@ function getDefaultSessionMeta(sessionId) {
|
|
|
161049
161034
|
conversationTokens: 0,
|
|
161050
161035
|
toolCallTokens: 0,
|
|
161051
161036
|
clearedReasoningThroughTag: 0,
|
|
161037
|
+
toolReclaimWatermark: 0,
|
|
161052
161038
|
lastTodoState: "",
|
|
161053
161039
|
cachedM0Bytes: null,
|
|
161054
161040
|
cachedM1Bytes: null,
|
|
@@ -161112,6 +161098,7 @@ function toSessionMeta(row) {
|
|
|
161112
161098
|
conversationTokens: numOrZero(row.conversation_tokens),
|
|
161113
161099
|
toolCallTokens: numOrZero(row.tool_call_tokens),
|
|
161114
161100
|
clearedReasoningThroughTag: numOrZero(row.cleared_reasoning_through_tag),
|
|
161101
|
+
toolReclaimWatermark: numOrZero(row.tool_reclaim_watermark),
|
|
161115
161102
|
lastTodoState: lastTodoStateRaw,
|
|
161116
161103
|
cachedM0Bytes: toBufferOrNull(row.cached_m0_bytes),
|
|
161117
161104
|
cachedM1Bytes: toBufferOrNull(row.cached_m1_bytes),
|
|
@@ -161369,9 +161356,9 @@ function escapeXmlContent(s) {
|
|
|
161369
161356
|
init_data_path();
|
|
161370
161357
|
init_logger();
|
|
161371
161358
|
import { existsSync as existsSync6 } from "node:fs";
|
|
161372
|
-
import { join as
|
|
161359
|
+
import { join as join6 } from "node:path";
|
|
161373
161360
|
function getOpenCodeDbPath() {
|
|
161374
|
-
return
|
|
161361
|
+
return join6(getDataDir(), "opencode", "opencode.db");
|
|
161375
161362
|
}
|
|
161376
161363
|
function openCodeDbExists() {
|
|
161377
161364
|
return existsSync6(getOpenCodeDbPath());
|
|
@@ -161538,6 +161525,8 @@ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
|
|
|
161538
161525
|
var encoder = new TextEncoder;
|
|
161539
161526
|
var TAG_PREFIX_REGEX = /^(?:§\d+§\s*)+/;
|
|
161540
161527
|
var MALFORMED_TAG_PREFIX_REGEX = /^(?:§\d+">§(?:\d+§)?\s*)+/;
|
|
161528
|
+
var DANGLING_TAG_GLOBAL_REGEX = /\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?/g;
|
|
161529
|
+
var DANGLING_TAG_PREFIX_REGEX = /^(?:\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?\s*)+/;
|
|
161541
161530
|
var COMPLETE_TAG_PAIR_GLOBAL_REGEX = /\u00a7\d+\u00a7/g;
|
|
161542
161531
|
var MALFORMED_TAG_GLOBAL_REGEX = /\u00a7\d+">(?:\u00a7(?:\d+\u00a7)?)?/g;
|
|
161543
161532
|
var STRAY_SECTION_CHAR_REGEX = /\u00a7/g;
|
|
@@ -161550,6 +161539,9 @@ function stripCompleteTagPairsGlobally(value) {
|
|
|
161550
161539
|
function stripMalformedTagNotationGlobally(value) {
|
|
161551
161540
|
return value.replace(MALFORMED_TAG_GLOBAL_REGEX, "");
|
|
161552
161541
|
}
|
|
161542
|
+
function stripDanglingTagNotationGlobally(value) {
|
|
161543
|
+
return value.replace(DANGLING_TAG_GLOBAL_REGEX, "");
|
|
161544
|
+
}
|
|
161553
161545
|
function stripTagSectionCharacters(value) {
|
|
161554
161546
|
return value.replace(STRAY_SECTION_CHAR_REGEX, "");
|
|
161555
161547
|
}
|
|
@@ -161557,6 +161549,7 @@ function stripPersistedAssistantText(value) {
|
|
|
161557
161549
|
let text = stripWellFormedLeadingTagPrefix(value);
|
|
161558
161550
|
text = stripCompleteTagPairsGlobally(text);
|
|
161559
161551
|
text = stripMalformedTagNotationGlobally(text);
|
|
161552
|
+
text = stripDanglingTagNotationGlobally(text);
|
|
161560
161553
|
text = stripTagSectionCharacters(text);
|
|
161561
161554
|
return text.trim();
|
|
161562
161555
|
}
|
|
@@ -161569,6 +161562,7 @@ function stripTagPrefix(value) {
|
|
|
161569
161562
|
const prev = stripped;
|
|
161570
161563
|
stripped = stripped.replace(MALFORMED_TAG_PREFIX_REGEX, "");
|
|
161571
161564
|
stripped = stripped.replace(TAG_PREFIX_REGEX, "");
|
|
161565
|
+
stripped = stripped.replace(DANGLING_TAG_PREFIX_REGEX, "");
|
|
161572
161566
|
if (stripped === prev)
|
|
161573
161567
|
break;
|
|
161574
161568
|
}
|
|
@@ -161676,6 +161670,24 @@ class ToolMutationBatch {
|
|
|
161676
161670
|
}
|
|
161677
161671
|
|
|
161678
161672
|
// ../plugin/src/hooks/magic-context/read-session-chunk.ts
|
|
161673
|
+
var BLOCK_TOKEN_MEMO_MAX = 2048;
|
|
161674
|
+
var blockTokenMemo = new Map;
|
|
161675
|
+
function estimateBlockTokens(blockText) {
|
|
161676
|
+
const cached2 = blockTokenMemo.get(blockText);
|
|
161677
|
+
if (cached2 !== undefined) {
|
|
161678
|
+
blockTokenMemo.delete(blockText);
|
|
161679
|
+
blockTokenMemo.set(blockText, cached2);
|
|
161680
|
+
return cached2;
|
|
161681
|
+
}
|
|
161682
|
+
const count = estimateTokens(blockText);
|
|
161683
|
+
if (blockTokenMemo.size >= BLOCK_TOKEN_MEMO_MAX) {
|
|
161684
|
+
const oldest = blockTokenMemo.keys().next().value;
|
|
161685
|
+
if (oldest !== undefined)
|
|
161686
|
+
blockTokenMemo.delete(oldest);
|
|
161687
|
+
}
|
|
161688
|
+
blockTokenMemo.set(blockText, count);
|
|
161689
|
+
return count;
|
|
161690
|
+
}
|
|
161679
161691
|
var activeRawMessageCache = null;
|
|
161680
161692
|
var activeAbsoluteCountCache = null;
|
|
161681
161693
|
var sessionProviders = new Map;
|
|
@@ -161858,7 +161870,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
161858
161870
|
if (!currentBlock)
|
|
161859
161871
|
return true;
|
|
161860
161872
|
const blockText = formatBlock(currentBlock);
|
|
161861
|
-
const blockTokens =
|
|
161873
|
+
const blockTokens = estimateBlockTokens(blockText);
|
|
161862
161874
|
if (totalTokens + blockTokens > tokenBudget && totalTokens > 0) {
|
|
161863
161875
|
return false;
|
|
161864
161876
|
}
|
|
@@ -161984,9 +161996,184 @@ Older parts of this session are summarized into <compartment> blocks inside <ses
|
|
|
161984
161996
|
|
|
161985
161997
|
ctx_expand(start=120, end=245) ← the compartment's own start/end attributes
|
|
161986
161998
|
|
|
161987
|
-
Returns the raw transcript as [N] U:/A: lines, capped at ~15K tokens; an oversized range returns the head and tells you where to continue. Also works with ordinals from ctx_search message results — expand a window around a hit (e.g. start=N-10, end=N+5). Ranges after the last compartment are your live tail — already visible in context, not expandable
|
|
161999
|
+
Returns the raw transcript as [N] U:/A: lines, capped at ~15K tokens; an oversized range returns the head and tells you where to continue. Also works with ordinals from ctx_search message results — expand a window around a hit (e.g. start=N-10, end=N+5). Ranges after the last compartment are your live tail — already visible in context, not expandable.
|
|
162000
|
+
|
|
162001
|
+
Two recovery modes for finer detail:
|
|
162002
|
+
- ctx_expand(start=120, end=245, verbose=true) — lists each message SEPARATELY with its ordinal [N] and a per-part preview (each tool call shown with its output size). Use this to find the exact message or tool call you want, then recover it in full by ordinal.
|
|
162003
|
+
- ctx_expand(message=138) — returns the FULL untruncated content of the message at that ordinal: every text part, and every tool call's complete input + output, read from stored history. This is the cheap way to get back a tool output you dropped with ctx_reduce — the original is still in storage even though the wire shows [dropped §N§]. If the message was deleted from history (session prune/revert), it says so.`;
|
|
161988
162004
|
var CTX_EXPAND_TOKEN_BUDGET = 15000;
|
|
161989
162005
|
|
|
162006
|
+
// ../plugin/src/tools/ctx-expand/render.ts
|
|
162007
|
+
function isRecord2(value) {
|
|
162008
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
162009
|
+
}
|
|
162010
|
+
function roleLabel(role) {
|
|
162011
|
+
if (role === "assistant")
|
|
162012
|
+
return "A (assistant)";
|
|
162013
|
+
if (role === "user")
|
|
162014
|
+
return "U (user)";
|
|
162015
|
+
return role;
|
|
162016
|
+
}
|
|
162017
|
+
function truncate(value, max) {
|
|
162018
|
+
const t = value.trim();
|
|
162019
|
+
return t.length <= max ? t : `${t.slice(0, max)}…`;
|
|
162020
|
+
}
|
|
162021
|
+
function keyArg(input) {
|
|
162022
|
+
if (!input)
|
|
162023
|
+
return "";
|
|
162024
|
+
for (const k of ["filePath", "path", "pattern", "query", "symbol", "module", "action"]) {
|
|
162025
|
+
const v = input[k];
|
|
162026
|
+
if (typeof v === "string" && v.length > 0)
|
|
162027
|
+
return truncate(v, 60);
|
|
162028
|
+
}
|
|
162029
|
+
if (typeof input.description === "string")
|
|
162030
|
+
return truncate(input.description, 60);
|
|
162031
|
+
return "";
|
|
162032
|
+
}
|
|
162033
|
+
function asToolPart(part) {
|
|
162034
|
+
const type = typeof part.type === "string" ? part.type : "";
|
|
162035
|
+
if (type === "tool") {
|
|
162036
|
+
const state = isRecord2(part.state) ? part.state : null;
|
|
162037
|
+
const output = state && typeof state.output === "string" ? state.output : state && state.output != null ? JSON.stringify(state.output) : null;
|
|
162038
|
+
const metadata = state && isRecord2(state.metadata) ? state.metadata : null;
|
|
162039
|
+
const title = state && typeof state.title === "string" && state.title || metadata && typeof metadata.title === "string" && metadata.title || null;
|
|
162040
|
+
return {
|
|
162041
|
+
name: typeof part.tool === "string" ? part.tool : "tool",
|
|
162042
|
+
callId: typeof part.callID === "string" ? part.callID : "",
|
|
162043
|
+
title,
|
|
162044
|
+
input: state && isRecord2(state.input) ? state.input : null,
|
|
162045
|
+
output
|
|
162046
|
+
};
|
|
162047
|
+
}
|
|
162048
|
+
if (type === "tool_use") {
|
|
162049
|
+
return {
|
|
162050
|
+
name: typeof part.name === "string" ? part.name : "tool",
|
|
162051
|
+
callId: typeof part.id === "string" ? part.id : "",
|
|
162052
|
+
title: null,
|
|
162053
|
+
input: isRecord2(part.input) ? part.input : null,
|
|
162054
|
+
output: null
|
|
162055
|
+
};
|
|
162056
|
+
}
|
|
162057
|
+
if (type === "tool_result") {
|
|
162058
|
+
const content = part.content;
|
|
162059
|
+
const output = typeof content === "string" ? content : content != null ? JSON.stringify(content) : null;
|
|
162060
|
+
return {
|
|
162061
|
+
name: "tool_result",
|
|
162062
|
+
callId: typeof part.tool_use_id === "string" ? part.tool_use_id : "",
|
|
162063
|
+
title: null,
|
|
162064
|
+
input: null,
|
|
162065
|
+
output
|
|
162066
|
+
};
|
|
162067
|
+
}
|
|
162068
|
+
return null;
|
|
162069
|
+
}
|
|
162070
|
+
function textOf(part) {
|
|
162071
|
+
if (part.type === "text" && typeof part.text === "string")
|
|
162072
|
+
return part.text;
|
|
162073
|
+
return null;
|
|
162074
|
+
}
|
|
162075
|
+
function reasoningOf(part) {
|
|
162076
|
+
if ((part.type === "reasoning" || part.type === "thinking") && typeof part.text === "string") {
|
|
162077
|
+
return part.text;
|
|
162078
|
+
}
|
|
162079
|
+
return null;
|
|
162080
|
+
}
|
|
162081
|
+
function renderPartPreview(part) {
|
|
162082
|
+
if (!isRecord2(part))
|
|
162083
|
+
return null;
|
|
162084
|
+
const text = textOf(part);
|
|
162085
|
+
if (text !== null) {
|
|
162086
|
+
const t = truncate(text, 200);
|
|
162087
|
+
return t.length > 0 ? ` • ${t}` : null;
|
|
162088
|
+
}
|
|
162089
|
+
const tool = asToolPart(part);
|
|
162090
|
+
if (tool) {
|
|
162091
|
+
const arg = keyArg(tool.input);
|
|
162092
|
+
const head = arg ? `${tool.name}(${arg})` : tool.name;
|
|
162093
|
+
return tool.output !== null ? ` • tool ${head} → output ~${estimateTokens(tool.output)} tok` : ` • tool ${head}`;
|
|
162094
|
+
}
|
|
162095
|
+
const reasoning = reasoningOf(part);
|
|
162096
|
+
if (reasoning !== null)
|
|
162097
|
+
return ` • [reasoning] ${truncate(reasoning, 120)}`;
|
|
162098
|
+
const type = typeof part.type === "string" ? part.type : "part";
|
|
162099
|
+
if (type === "file")
|
|
162100
|
+
return " • [file]";
|
|
162101
|
+
if (type === "step-start" || type === "step-finish")
|
|
162102
|
+
return null;
|
|
162103
|
+
return ` • [${type}]`;
|
|
162104
|
+
}
|
|
162105
|
+
function renderPartFull(part) {
|
|
162106
|
+
if (!isRecord2(part))
|
|
162107
|
+
return null;
|
|
162108
|
+
const text = textOf(part);
|
|
162109
|
+
if (text !== null) {
|
|
162110
|
+
return text.trim().length > 0 ? ` [text]
|
|
162111
|
+
${text}` : null;
|
|
162112
|
+
}
|
|
162113
|
+
const tool = asToolPart(part);
|
|
162114
|
+
if (tool) {
|
|
162115
|
+
const lines = [];
|
|
162116
|
+
const idSuffix = tool.callId ? ` #${tool.callId}` : "";
|
|
162117
|
+
lines.push(` [tool: ${tool.name}${idSuffix}]`);
|
|
162118
|
+
if (tool.title && tool.title.trim().length > 0) {
|
|
162119
|
+
lines.push(` description: ${tool.title.trim()}`);
|
|
162120
|
+
}
|
|
162121
|
+
if (tool.input)
|
|
162122
|
+
lines.push(` input: ${JSON.stringify(tool.input)}`);
|
|
162123
|
+
if (tool.output !== null)
|
|
162124
|
+
lines.push(` output:
|
|
162125
|
+
${tool.output}`);
|
|
162126
|
+
return lines.join(`
|
|
162127
|
+
`);
|
|
162128
|
+
}
|
|
162129
|
+
const type = typeof part.type === "string" ? part.type : "part";
|
|
162130
|
+
if (type === "file") {
|
|
162131
|
+
const name2 = typeof part.filename === "string" && part.filename || typeof part.url === "string" && part.url || "";
|
|
162132
|
+
return ` [file]${name2 ? ` ${name2}` : ""}`;
|
|
162133
|
+
}
|
|
162134
|
+
return null;
|
|
162135
|
+
}
|
|
162136
|
+
function renderMessageByOrdinal(sessionId, ordinal) {
|
|
162137
|
+
const msg = readRawSessionMessages(sessionId).find((m) => m.ordinal === ordinal);
|
|
162138
|
+
if (!msg) {
|
|
162139
|
+
return `No message at ordinal ${ordinal} in this session's stored history — it was deleted ` + `(session prune/revert) or the ordinal is wrong, so it can't be recovered. ` + `Re-run the tool if you still need the data.`;
|
|
162140
|
+
}
|
|
162141
|
+
const rendered = msg.parts.map(renderPartFull).filter((l) => l !== null);
|
|
162142
|
+
const lines = [`[${msg.ordinal}] ${roleLabel(msg.role)} — full recovery:`, ""];
|
|
162143
|
+
if (rendered.length === 0) {
|
|
162144
|
+
lines.push(" (no recoverable content — message had only structural/reasoning parts)");
|
|
162145
|
+
} else {
|
|
162146
|
+
lines.push(...rendered);
|
|
162147
|
+
}
|
|
162148
|
+
return lines.join(`
|
|
162149
|
+
`);
|
|
162150
|
+
}
|
|
162151
|
+
function renderVerboseRange(sessionId, start, end, tokenBudget) {
|
|
162152
|
+
const messages = readRawSessionMessages(sessionId).filter((m) => m.ordinal >= start && m.ordinal <= end);
|
|
162153
|
+
const out = [];
|
|
162154
|
+
let usedTokens = 0;
|
|
162155
|
+
let lastOrdinal = start - 1;
|
|
162156
|
+
let truncated = false;
|
|
162157
|
+
for (const msg of messages) {
|
|
162158
|
+
const header = `[${msg.ordinal}] ${roleLabel(msg.role)}`;
|
|
162159
|
+
const partLines = msg.parts.map(renderPartPreview).filter((l) => l !== null);
|
|
162160
|
+
const block = partLines.length > 0 ? `${header}
|
|
162161
|
+
${partLines.join(`
|
|
162162
|
+
`)}` : header;
|
|
162163
|
+
const blockTokens = estimateTokens(block);
|
|
162164
|
+
if (usedTokens + blockTokens > tokenBudget && out.length > 0) {
|
|
162165
|
+
truncated = true;
|
|
162166
|
+
break;
|
|
162167
|
+
}
|
|
162168
|
+
out.push(block);
|
|
162169
|
+
usedTokens += blockTokens;
|
|
162170
|
+
lastOrdinal = msg.ordinal;
|
|
162171
|
+
}
|
|
162172
|
+
return { text: out.join(`
|
|
162173
|
+
|
|
162174
|
+
`), lastOrdinal, truncated };
|
|
162175
|
+
}
|
|
162176
|
+
|
|
161990
162177
|
// ../../node_modules/.bun/typebox@1.1.38/node_modules/typebox/build/system/memory/memory.mjs
|
|
161991
162178
|
var exports_memory = {};
|
|
161992
162179
|
__export(exports_memory, {
|
|
@@ -166343,12 +166530,18 @@ function synthesizeToolResultParts(msg) {
|
|
|
166343
166530
|
|
|
166344
166531
|
// src/tools/ctx-expand.ts
|
|
166345
166532
|
var ParamsSchema = exports_typebox.Object({
|
|
166346
|
-
start: exports_typebox.Number({
|
|
166533
|
+
start: exports_typebox.Optional(exports_typebox.Number({
|
|
166347
166534
|
description: "Start message ordinal (from compartment start attribute)"
|
|
166348
|
-
}),
|
|
166349
|
-
end: exports_typebox.Number({
|
|
166535
|
+
})),
|
|
166536
|
+
end: exports_typebox.Optional(exports_typebox.Number({
|
|
166350
166537
|
description: "End message ordinal (from compartment end attribute)"
|
|
166351
|
-
})
|
|
166538
|
+
})),
|
|
166539
|
+
verbose: exports_typebox.Optional(exports_typebox.Boolean({
|
|
166540
|
+
description: "With start/end: list each message separately with its ordinal [N] and per-part preview, so you can recover one in full by ordinal."
|
|
166541
|
+
})),
|
|
166542
|
+
message: exports_typebox.Optional(exports_typebox.Number({
|
|
166543
|
+
description: "Full untruncated recovery of ONE message by its ordinal (text + every tool call's full input/output). Recovers a tool output you dropped with ctx_reduce."
|
|
166544
|
+
}))
|
|
166352
166545
|
});
|
|
166353
166546
|
function ok(text) {
|
|
166354
166547
|
return { content: [{ type: "text", text }], details: undefined };
|
|
@@ -166367,22 +166560,41 @@ function createCtxExpandTool(deps) {
|
|
|
166367
166560
|
description: CTX_EXPAND_DESCRIPTION,
|
|
166368
166561
|
parameters: ParamsSchema,
|
|
166369
166562
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
166370
|
-
if (typeof params.start !== "number" || typeof params.end !== "number" || params.start < 1 || params.end < params.start) {
|
|
166371
|
-
return err("Error: start and end must be positive integers with start <= end.");
|
|
166372
|
-
}
|
|
166373
166563
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
166374
166564
|
if (!sessionId) {
|
|
166375
166565
|
return err("Error: no active Pi session.");
|
|
166376
166566
|
}
|
|
166377
|
-
const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
|
|
166378
|
-
if (lastCompartmentEnd >= 0 && params.start > lastCompartmentEnd) {
|
|
166379
|
-
return ok(`Range ${params.start}-${params.end} is entirely within the live tail (after the last compacted message ${lastCompartmentEnd}); those messages are already visible in context.`);
|
|
166380
|
-
}
|
|
166381
|
-
const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(params.end, lastCompartmentEnd) : params.end;
|
|
166382
166567
|
const unregister = setRawMessageProvider(sessionId, {
|
|
166383
166568
|
readMessages: () => readPiSessionMessages(ctx)
|
|
166384
166569
|
});
|
|
166385
166570
|
try {
|
|
166571
|
+
if (typeof params.message === "number" && params.message >= 1) {
|
|
166572
|
+
return ok(renderMessageByOrdinal(sessionId, params.message));
|
|
166573
|
+
}
|
|
166574
|
+
if (typeof params.start !== "number" || typeof params.end !== "number" || params.start < 1 || params.end < params.start) {
|
|
166575
|
+
return err("Error: provide either message=<ordinal>, or start and end (positive integers, start <= end).");
|
|
166576
|
+
}
|
|
166577
|
+
const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
|
|
166578
|
+
if (lastCompartmentEnd >= 0 && params.start > lastCompartmentEnd) {
|
|
166579
|
+
return ok(`Range ${params.start}-${params.end} is entirely within the live tail (after the last compacted message ${lastCompartmentEnd}); those messages are already visible in context.`);
|
|
166580
|
+
}
|
|
166581
|
+
const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(params.end, lastCompartmentEnd) : params.end;
|
|
166582
|
+
if (params.verbose === true) {
|
|
166583
|
+
const v = renderVerboseRange(sessionId, params.start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET);
|
|
166584
|
+
if (!v.text) {
|
|
166585
|
+
return ok(`No messages found in range ${params.start}-${effectiveEnd}. The range may be outside this session's history.`);
|
|
166586
|
+
}
|
|
166587
|
+
const out = [
|
|
166588
|
+
`Messages ${params.start}-${v.lastOrdinal} (verbose). Recover any one in full with ctx_expand(message=<ordinal>):`,
|
|
166589
|
+
"",
|
|
166590
|
+
v.text
|
|
166591
|
+
];
|
|
166592
|
+
if (v.truncated) {
|
|
166593
|
+
out.push("", `Truncated at message ${v.lastOrdinal} (budget: ~${CTX_EXPAND_TOKEN_BUDGET} tokens). Call again with start=${v.lastOrdinal + 1} end=${effectiveEnd} verbose=true for more.`);
|
|
166594
|
+
}
|
|
166595
|
+
return ok(out.join(`
|
|
166596
|
+
`));
|
|
166597
|
+
}
|
|
166386
166598
|
const chunk = readSessionChunk(sessionId, CTX_EXPAND_TOKEN_BUDGET, params.start, effectiveEnd + 1);
|
|
166387
166599
|
if (!chunk.text || chunk.messageCount === 0) {
|
|
166388
166600
|
return ok(`No messages found in range ${params.start}-${params.end}. The range may be outside this session's history.`);
|
|
@@ -167265,12 +167477,12 @@ function canonicalizeDocContent(raw) {
|
|
|
167265
167477
|
}
|
|
167266
167478
|
function fingerprintFile(filePath) {
|
|
167267
167479
|
try {
|
|
167268
|
-
const
|
|
167269
|
-
const isReadableDoc =
|
|
167480
|
+
const stat = lstatSync(filePath);
|
|
167481
|
+
const isReadableDoc = stat.isFile() && stat.size <= MAX_PROJECT_DOC_BYTES;
|
|
167270
167482
|
return {
|
|
167271
167483
|
exists: isReadableDoc,
|
|
167272
|
-
mtimeMs:
|
|
167273
|
-
size:
|
|
167484
|
+
mtimeMs: stat.mtimeMs,
|
|
167485
|
+
size: stat.size
|
|
167274
167486
|
};
|
|
167275
167487
|
} catch {
|
|
167276
167488
|
return { exists: false, mtimeMs: 0, size: 0 };
|
|
@@ -167608,7 +167820,9 @@ var DEFAULT_PROTECTED_TAIL_META = {
|
|
|
167608
167820
|
protectedTailDrainTokens: 0,
|
|
167609
167821
|
recoveryNoEligibleHeadCount: 0,
|
|
167610
167822
|
forceEmergencyBypassWindowStart: 0,
|
|
167611
|
-
forceEmergencyBypassUsed: 0
|
|
167823
|
+
forceEmergencyBypassUsed: 0,
|
|
167824
|
+
emergencyDrainActive: 0,
|
|
167825
|
+
historianDrainFailureAt: 0
|
|
167612
167826
|
};
|
|
167613
167827
|
function toProtectedTailMeta(row) {
|
|
167614
167828
|
if (row === null || typeof row !== "object")
|
|
@@ -167622,7 +167836,9 @@ function toProtectedTailMeta(row) {
|
|
|
167622
167836
|
protectedTailDrainTokens: numberOr(r.protected_tail_drain_tokens, 0),
|
|
167623
167837
|
recoveryNoEligibleHeadCount: numberOr(r.recovery_no_eligible_head_count, 0),
|
|
167624
167838
|
forceEmergencyBypassWindowStart: numberOr(r.force_emergency_bypass_window_start, 0),
|
|
167625
|
-
forceEmergencyBypassUsed: numberOr(r.force_emergency_bypass_used, 0)
|
|
167839
|
+
forceEmergencyBypassUsed: numberOr(r.force_emergency_bypass_used, 0),
|
|
167840
|
+
emergencyDrainActive: numberOr(r.emergency_drain_active, 0),
|
|
167841
|
+
historianDrainFailureAt: numberOr(r.historian_drain_failure_at, 0)
|
|
167626
167842
|
};
|
|
167627
167843
|
}
|
|
167628
167844
|
function loadProtectedTailMeta(db, sessionId) {
|
|
@@ -167630,7 +167846,7 @@ function loadProtectedTailMeta(db, sessionId) {
|
|
|
167630
167846
|
const row = db.prepare(`SELECT prior_boundary_ordinal, protected_tail_policy_version,
|
|
167631
167847
|
protected_tail_drain_window_started_at, protected_tail_drain_tokens,
|
|
167632
167848
|
recovery_no_eligible_head_count, force_emergency_bypass_window_start,
|
|
167633
|
-
force_emergency_bypass_used
|
|
167849
|
+
force_emergency_bypass_used, emergency_drain_active, historian_drain_failure_at
|
|
167634
167850
|
FROM session_meta WHERE session_id = ?`).get(sessionId);
|
|
167635
167851
|
return toProtectedTailMeta(row);
|
|
167636
167852
|
}
|
|
@@ -167674,6 +167890,17 @@ function protectedTailWindowBudget(usagePercentage, usable, perRunCap) {
|
|
|
167674
167890
|
return Math.min(750000, Math.max(3 * perRunCap, Math.round(0.35 * usable)));
|
|
167675
167891
|
return Math.min(500000, Math.max(perRunCap, Math.round(0.2 * usable)));
|
|
167676
167892
|
}
|
|
167893
|
+
var EMERGENCY_DRAIN_ENTER_PERCENTAGE = 95;
|
|
167894
|
+
var EMERGENCY_DRAIN_EXIT_MARGIN = 10;
|
|
167895
|
+
var EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE = 55;
|
|
167896
|
+
var EMERGENCY_DRAIN_FAILURE_BACKOFF_MS = 60000;
|
|
167897
|
+
var EMERGENCY_DRAIN_MAX_LATCH_MS = 30 * 60 * 1000;
|
|
167898
|
+
function emergencyDrainExitThreshold(executeThresholdPercentage) {
|
|
167899
|
+
if (!Number.isFinite(executeThresholdPercentage) || executeThresholdPercentage <= 0) {
|
|
167900
|
+
return EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE;
|
|
167901
|
+
}
|
|
167902
|
+
return Math.max(0, executeThresholdPercentage - EMERGENCY_DRAIN_EXIT_MARGIN);
|
|
167903
|
+
}
|
|
167677
167904
|
function reserveProtectedTailDrainTokens(args) {
|
|
167678
167905
|
const now = args.now ?? Date.now();
|
|
167679
167906
|
const requested = Math.max(0, Math.floor(args.trueRawTokens));
|
|
@@ -167692,18 +167919,30 @@ function reserveProtectedTailDrainTokens(args) {
|
|
|
167692
167919
|
let meta3 = loadProtectedTailMeta(args.db, args.sessionId);
|
|
167693
167920
|
if (now - meta3.protectedTailDrainWindowStartedAt > DRAIN_WINDOW_MS) {
|
|
167694
167921
|
args.db.prepare(`UPDATE session_meta
|
|
167695
|
-
SET protected_tail_drain_window_started_at = ?, protected_tail_drain_tokens = 0
|
|
167696
|
-
|
|
167697
|
-
WHERE session_id = ?`).run(now, now, args.sessionId);
|
|
167922
|
+
SET protected_tail_drain_window_started_at = ?, protected_tail_drain_tokens = 0
|
|
167923
|
+
WHERE session_id = ?`).run(now, args.sessionId);
|
|
167698
167924
|
meta3 = loadProtectedTailMeta(args.db, args.sessionId);
|
|
167699
167925
|
}
|
|
167926
|
+
const exitThreshold = emergencyDrainExitThreshold(args.executeThresholdPercentage);
|
|
167927
|
+
let latchActiveSince = meta3.emergencyDrainActive;
|
|
167928
|
+
if (args.usagePercentage >= EMERGENCY_DRAIN_ENTER_PERCENTAGE) {
|
|
167929
|
+
if (latchActiveSince <= 0)
|
|
167930
|
+
latchActiveSince = now;
|
|
167931
|
+
} else if (latchActiveSince > 0) {
|
|
167932
|
+
const expired = now - latchActiveSince > EMERGENCY_DRAIN_MAX_LATCH_MS;
|
|
167933
|
+
if (args.usagePercentage < exitThreshold || expired)
|
|
167934
|
+
latchActiveSince = 0;
|
|
167935
|
+
}
|
|
167936
|
+
if (latchActiveSince !== meta3.emergencyDrainActive) {
|
|
167937
|
+
args.db.prepare("UPDATE session_meta SET emergency_drain_active = ? WHERE session_id = ?").run(latchActiveSince, args.sessionId);
|
|
167938
|
+
}
|
|
167939
|
+
const latchActive = latchActiveSince > 0;
|
|
167700
167940
|
const budget = protectedTailWindowBudget(args.usagePercentage, args.usable, args.perRunCap);
|
|
167701
167941
|
const remaining = Math.max(0, budget - meta3.protectedTailDrainTokens);
|
|
167702
167942
|
let reserved = Math.min(requested, args.perRunCap, remaining);
|
|
167703
167943
|
let bypass = false;
|
|
167704
|
-
const
|
|
167705
|
-
|
|
167706
|
-
if (reserved <= 0 && args.usagePercentage >= 95 && bypassUsed === 0) {
|
|
167944
|
+
const inFailureBackoff = meta3.historianDrainFailureAt > 0 && now - meta3.historianDrainFailureAt < EMERGENCY_DRAIN_FAILURE_BACKOFF_MS;
|
|
167945
|
+
if (reserved <= 0 && latchActive && !inFailureBackoff) {
|
|
167707
167946
|
reserved = Math.min(requested, args.perRunCap);
|
|
167708
167947
|
bypass = true;
|
|
167709
167948
|
}
|
|
@@ -167711,10 +167950,8 @@ function reserveProtectedTailDrainTokens(args) {
|
|
|
167711
167950
|
return;
|
|
167712
167951
|
args.db.prepare(`UPDATE session_meta
|
|
167713
167952
|
SET protected_tail_drain_window_started_at = CASE WHEN protected_tail_drain_window_started_at = 0 THEN ? ELSE protected_tail_drain_window_started_at END,
|
|
167714
|
-
protected_tail_drain_tokens = COALESCE(protected_tail_drain_tokens, 0) +
|
|
167715
|
-
|
|
167716
|
-
force_emergency_bypass_used = CASE WHEN ? THEN 1 ELSE force_emergency_bypass_used END
|
|
167717
|
-
WHERE session_id = ?`).run(now, reserved, bypass ? 1 : 0, now, bypass ? 1 : 0, args.sessionId);
|
|
167953
|
+
protected_tail_drain_tokens = COALESCE(protected_tail_drain_tokens, 0) + ?
|
|
167954
|
+
WHERE session_id = ?`).run(now, reserved, args.sessionId);
|
|
167718
167955
|
result = {
|
|
167719
167956
|
ok: true,
|
|
167720
167957
|
reservedTokens: reserved,
|
|
@@ -167724,6 +167961,25 @@ function reserveProtectedTailDrainTokens(args) {
|
|
|
167724
167961
|
})();
|
|
167725
167962
|
return result;
|
|
167726
167963
|
}
|
|
167964
|
+
function clearEmergencyDrainLatch(db, sessionId) {
|
|
167965
|
+
db.transaction(() => {
|
|
167966
|
+
ensureSessionMetaRow(db, sessionId);
|
|
167967
|
+
db.prepare("UPDATE session_meta SET emergency_drain_active = 0 WHERE session_id = ?").run(sessionId);
|
|
167968
|
+
})();
|
|
167969
|
+
}
|
|
167970
|
+
function recordHistorianDrainFailure(db, sessionId, now) {
|
|
167971
|
+
const ts = now ?? Date.now();
|
|
167972
|
+
db.transaction(() => {
|
|
167973
|
+
ensureSessionMetaRow(db, sessionId);
|
|
167974
|
+
db.prepare("UPDATE session_meta SET historian_drain_failure_at = ? WHERE session_id = ?").run(ts, sessionId);
|
|
167975
|
+
})();
|
|
167976
|
+
}
|
|
167977
|
+
function clearHistorianDrainFailure(db, sessionId) {
|
|
167978
|
+
db.transaction(() => {
|
|
167979
|
+
ensureSessionMetaRow(db, sessionId);
|
|
167980
|
+
db.prepare("UPDATE session_meta SET historian_drain_failure_at = 0 WHERE session_id = ?").run(sessionId);
|
|
167981
|
+
})();
|
|
167982
|
+
}
|
|
167727
167983
|
function rollbackProtectedTailDrainReservation(db, reservation) {
|
|
167728
167984
|
if (!reservation || reservation.tokens <= 0)
|
|
167729
167985
|
return;
|
|
@@ -168242,6 +168498,7 @@ var SESSION_META_FALLBACK_SELECTS = {
|
|
|
168242
168498
|
last_transform_error: "'' AS last_transform_error",
|
|
168243
168499
|
system_prompt_hash: "'' AS system_prompt_hash",
|
|
168244
168500
|
last_todo_state: "'' AS last_todo_state",
|
|
168501
|
+
tool_reclaim_watermark: "0 AS tool_reclaim_watermark",
|
|
168245
168502
|
cached_m0_bytes: "NULL AS cached_m0_bytes",
|
|
168246
168503
|
cached_m1_bytes: "NULL AS cached_m1_bytes",
|
|
168247
168504
|
cached_m0_project_memory_epoch: "NULL AS cached_m0_project_memory_epoch",
|
|
@@ -168319,6 +168576,14 @@ function updateSessionMeta(db, sessionId, updates) {
|
|
|
168319
168576
|
db.prepare(`UPDATE session_meta SET ${setClauses.join(", ")} WHERE session_id = ?`).run(...values, sessionId);
|
|
168320
168577
|
})();
|
|
168321
168578
|
}
|
|
168579
|
+
function advanceToolReclaimWatermark(db, sessionId, maxTagNumber) {
|
|
168580
|
+
if (maxTagNumber <= 0)
|
|
168581
|
+
return;
|
|
168582
|
+
db.transaction(() => {
|
|
168583
|
+
ensureSessionMetaRow(db, sessionId);
|
|
168584
|
+
db.prepare("UPDATE session_meta SET tool_reclaim_watermark = MAX(COALESCE(tool_reclaim_watermark, 0), ?) WHERE session_id = ?").run(maxTagNumber, sessionId);
|
|
168585
|
+
})();
|
|
168586
|
+
}
|
|
168322
168587
|
// ../plugin/src/features/magic-context/storage-notes.ts
|
|
168323
168588
|
var NOTE_TYPES = new Set(["session", "smart"]);
|
|
168324
168589
|
var NOTE_STATUSES = new Set(["active", "pending", "ready", "dismissed"]);
|
|
@@ -168716,12 +168981,37 @@ function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
|
|
|
168716
168981
|
nullCount: row?.null_count ?? 0
|
|
168717
168982
|
};
|
|
168718
168983
|
}
|
|
168719
|
-
function
|
|
168720
|
-
|
|
168984
|
+
function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, limit = 4) {
|
|
168985
|
+
if (limit <= 0)
|
|
168986
|
+
return [];
|
|
168987
|
+
const boundedLimit = Math.max(1, Math.min(10, Math.floor(limit)));
|
|
168988
|
+
const whereProtected = protectedTags > 0 ? `AND tag_number < (
|
|
168989
|
+
SELECT tag_number FROM tags
|
|
168990
|
+
WHERE session_id = ? AND status = 'active'
|
|
168991
|
+
ORDER BY tag_number DESC LIMIT 1 OFFSET ?
|
|
168992
|
+
)` : "";
|
|
168993
|
+
const params = protectedTags > 0 ? [sessionId, sessionId, protectedTags - 1, boundedLimit] : [sessionId, boundedLimit];
|
|
168994
|
+
const rows = db.prepare(`SELECT tag_number, tool_name
|
|
168995
|
+
FROM tags
|
|
168996
|
+
WHERE session_id = ? AND status = 'active' AND type = 'tool' ${whereProtected}
|
|
168997
|
+
ORDER BY tag_number ASC, id ASC
|
|
168998
|
+
LIMIT ?`).all(...params);
|
|
168999
|
+
return rows.filter((row) => typeof row.tag_number === "number").map((row) => ({
|
|
169000
|
+
tagNumber: row.tag_number,
|
|
169001
|
+
toolName: typeof row.tool_name === "string" ? row.tool_name : null
|
|
169002
|
+
}));
|
|
169003
|
+
}
|
|
169004
|
+
function getTriggerTagTokenUpperBound(db, sessionId, floor = 0) {
|
|
169005
|
+
const sql = floor > 0 ? `SELECT
|
|
168721
169006
|
COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
|
|
168722
169007
|
COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
|
|
168723
169008
|
FROM tags
|
|
168724
|
-
WHERE session_id = ? AND status IN ('active', 'dropped')`
|
|
169009
|
+
WHERE session_id = ? AND status IN ('active', 'dropped') AND tag_number >= ?` : `SELECT
|
|
169010
|
+
COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
|
|
169011
|
+
COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
|
|
169012
|
+
FROM tags
|
|
169013
|
+
WHERE session_id = ? AND status IN ('active', 'dropped')`;
|
|
169014
|
+
const row = floor > 0 ? db.prepare(sql).get(sessionId, floor) : db.prepare(sql).get(sessionId);
|
|
168725
169015
|
return { bound: row?.bound ?? 0, nullCount: row?.null_count ?? 0 };
|
|
168726
169016
|
}
|
|
168727
169017
|
function updateTagInputByteSize(db, sessionId, tagNumber, newInputByteSize) {
|
|
@@ -168748,10 +169038,12 @@ function getUpdateTagInputTokenCountStatement(db) {
|
|
|
168748
169038
|
function updateTagTokenCount(db, sessionId, tagNumber, newTokenCount) {
|
|
168749
169039
|
getUpdateTagTokenCountStatement(db).run(newTokenCount, sessionId, tagNumber);
|
|
168750
169040
|
}
|
|
168751
|
-
function getAllStatusTagTokenTotalsFlat(db, sessionId) {
|
|
168752
|
-
const rows = db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
|
|
168753
|
-
|
|
168754
|
-
|
|
169041
|
+
function getAllStatusTagTokenTotalsFlat(db, sessionId, floor = 0) {
|
|
169042
|
+
const rows = floor > 0 ? db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
|
|
169043
|
+
FROM tags
|
|
169044
|
+
WHERE session_id = ? AND tag_number >= ?`).all(sessionId, floor) : db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
|
|
169045
|
+
FROM tags
|
|
169046
|
+
WHERE session_id = ?`).all(sessionId);
|
|
168755
169047
|
const totals = new Map;
|
|
168756
169048
|
const nullMessageIds = new Set;
|
|
168757
169049
|
for (const row of rows) {
|
|
@@ -168870,6 +169162,52 @@ function getTagNumberByMessageId(db, sessionId, messageId) {
|
|
|
168870
169162
|
const row = getTagNumberByMessageIdStatement(db).get(sessionId, messageId);
|
|
168871
169163
|
return isTagNumberRow(row) ? row.tag_number : null;
|
|
168872
169164
|
}
|
|
169165
|
+
var getMinMessageTagNumberForRawIdStatements = new WeakMap;
|
|
169166
|
+
function isMinTagNumberRow(row) {
|
|
169167
|
+
return row !== null && typeof row === "object" && "m" in row;
|
|
169168
|
+
}
|
|
169169
|
+
function getMinMessageTagNumberForRawId(db, sessionId, rawId) {
|
|
169170
|
+
if (rawId.includes(":"))
|
|
169171
|
+
return null;
|
|
169172
|
+
let stmt = getMinMessageTagNumberForRawIdStatements.get(db);
|
|
169173
|
+
if (!stmt) {
|
|
169174
|
+
stmt = db.prepare("SELECT MIN(tag_number) AS m FROM tags WHERE session_id = ? AND message_id >= ? AND message_id < ?");
|
|
169175
|
+
getMinMessageTagNumberForRawIdStatements.set(db, stmt);
|
|
169176
|
+
}
|
|
169177
|
+
const row = stmt.get(sessionId, `${rawId}:`, `${rawId};`);
|
|
169178
|
+
return isMinTagNumberRow(row) && typeof row.m === "number" ? row.m : null;
|
|
169179
|
+
}
|
|
169180
|
+
var TAGGER_FLOOR_SCAN_MESSAGES = 8;
|
|
169181
|
+
var TAGGER_FLOOR_MAX_PROBES = 64;
|
|
169182
|
+
var TAGGER_FLOOR_SAFETY_MARGIN = 256;
|
|
169183
|
+
var TAGGER_FLOOR_PER_SKIP_MARGIN = 64;
|
|
169184
|
+
function deriveTagLoadFloor(db, sessionId, rawIds) {
|
|
169185
|
+
let min = Number.POSITIVE_INFINITY;
|
|
169186
|
+
let probes = 0;
|
|
169187
|
+
let hits = 0;
|
|
169188
|
+
let skippedBeforeFirstHit = 0;
|
|
169189
|
+
for (const rawId of rawIds) {
|
|
169190
|
+
if (typeof rawId !== "string" || rawId.length === 0)
|
|
169191
|
+
continue;
|
|
169192
|
+
if (probes >= TAGGER_FLOOR_MAX_PROBES)
|
|
169193
|
+
break;
|
|
169194
|
+
probes++;
|
|
169195
|
+
const m = getMinMessageTagNumberForRawId(db, sessionId, rawId);
|
|
169196
|
+
if (m === null) {
|
|
169197
|
+
if (hits === 0)
|
|
169198
|
+
skippedBeforeFirstHit++;
|
|
169199
|
+
continue;
|
|
169200
|
+
}
|
|
169201
|
+
if (m < min)
|
|
169202
|
+
min = m;
|
|
169203
|
+
if (++hits >= TAGGER_FLOOR_SCAN_MESSAGES)
|
|
169204
|
+
break;
|
|
169205
|
+
}
|
|
169206
|
+
if (!Number.isFinite(min))
|
|
169207
|
+
return 0;
|
|
169208
|
+
const margin = TAGGER_FLOOR_SAFETY_MARGIN + skippedBeforeFirstHit * TAGGER_FLOOR_PER_SKIP_MARGIN;
|
|
169209
|
+
return Math.max(0, min - margin);
|
|
169210
|
+
}
|
|
168873
169211
|
var TAG_SELECT_COLUMNS = "id, message_id, type, status, drop_mode, tool_name, input_byte_size, byte_size, reasoning_byte_size, session_id, tag_number, caveman_depth, tool_owner_message_id";
|
|
168874
169212
|
function getTagsBySession(db, sessionId) {
|
|
168875
169213
|
const rows = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? ORDER BY tag_number ASC, id ASC`).all(sessionId).filter(isTagRow);
|
|
@@ -169665,15 +170003,16 @@ function parseInteger(str) {
|
|
|
169665
170003
|
}
|
|
169666
170004
|
|
|
169667
170005
|
// ../plugin/src/tools/ctx-reduce/constants.ts
|
|
169668
|
-
var CTX_REDUCE_DESCRIPTION = `
|
|
169669
|
-
|
|
170006
|
+
var CTX_REDUCE_DESCRIPTION = `Mark spent tagged content as discardable to reclaim context space. This is NOT an immediate delete. Use §N§ identifiers visible in the conversation. The \`drop\` param accepts ranges: "3-5", "1,2,9", "1-5,8".
|
|
170007
|
+
|
|
170008
|
+
How it works:
|
|
170009
|
+
- Marking QUEUES content for release. It stays fully visible to you until context space is actually needed — which may be as soon as the next turn if you are already under pressure, or many turns later if not. So mark spent outputs as soon as you finish with them; don't hoard the call for the end of the turn.
|
|
170010
|
+
- The newest tags are protected: marking one just queues it until it ages out of the recent window, so marking recent output is harmless.
|
|
170011
|
+
- When content is finally released it becomes a short placeholder, and re-running the tool is the only way to get it back. So mark only what you are genuinely DONE with — the test is "have I extracted what I need from this?", not "is it safe / do I have time before it drops?".
|
|
169670
170012
|
|
|
169671
|
-
|
|
169672
|
-
|
|
169673
|
-
-
|
|
169674
|
-
- Protected tags are accepted but deferred until they leave the last protected range.
|
|
169675
|
-
- Keep recent context — only reduce OLD content that is no longer relevant to current work.
|
|
169676
|
-
- Dropped content is gone forever.`;
|
|
170013
|
+
Mark discardable once processed: large outputs you've summarized, repeated or redundant dumps, data written to disk, status/log output that only confirmed an expected state.
|
|
170014
|
+
Keep: user messages, unresolved errors, raw evidence you haven't extracted yet, and outputs whose exact wording may matter later.
|
|
170015
|
+
Never blanket-mark large ranges (e.g. "1-50") — review what each tag holds first.`;
|
|
169677
170016
|
|
|
169678
170017
|
// src/tools/ctx-reduce.ts
|
|
169679
170018
|
var ParamsSchema4 = exports_typebox.Object({
|
|
@@ -169858,120 +170197,6 @@ ${body}` : subject;
|
|
|
169858
170197
|
}
|
|
169859
170198
|
// ../plugin/src/features/magic-context/git-commits/indexer.ts
|
|
169860
170199
|
init_logger();
|
|
169861
|
-
|
|
169862
|
-
// ../plugin/src/features/magic-context/git-commits/storage-git-commits.ts
|
|
169863
|
-
init_logger();
|
|
169864
|
-
var insertStatements = new WeakMap;
|
|
169865
|
-
var existingShasStatements = new WeakMap;
|
|
169866
|
-
var projectCountStatements = new WeakMap;
|
|
169867
|
-
var evictStatements = new WeakMap;
|
|
169868
|
-
var evictOverflowStatements = new WeakMap;
|
|
169869
|
-
var latestCommitTimeStatements = new WeakMap;
|
|
169870
|
-
function getInsertStatement(db) {
|
|
169871
|
-
let stmt = insertStatements.get(db);
|
|
169872
|
-
if (!stmt) {
|
|
169873
|
-
stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
|
|
169874
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
169875
|
-
ON CONFLICT(sha) DO UPDATE SET
|
|
169876
|
-
project_path = excluded.project_path,
|
|
169877
|
-
short_sha = excluded.short_sha,
|
|
169878
|
-
message = excluded.message,
|
|
169879
|
-
author = excluded.author,
|
|
169880
|
-
committed_at = excluded.committed_at,
|
|
169881
|
-
indexed_at = excluded.indexed_at
|
|
169882
|
-
WHERE git_commits.message != excluded.message`);
|
|
169883
|
-
insertStatements.set(db, stmt);
|
|
169884
|
-
}
|
|
169885
|
-
return stmt;
|
|
169886
|
-
}
|
|
169887
|
-
function getExistingShasStatement(db) {
|
|
169888
|
-
let stmt = existingShasStatements.get(db);
|
|
169889
|
-
if (!stmt) {
|
|
169890
|
-
stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
|
|
169891
|
-
existingShasStatements.set(db, stmt);
|
|
169892
|
-
}
|
|
169893
|
-
return stmt;
|
|
169894
|
-
}
|
|
169895
|
-
function getProjectCountStatement(db) {
|
|
169896
|
-
let stmt = projectCountStatements.get(db);
|
|
169897
|
-
if (!stmt) {
|
|
169898
|
-
stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
|
|
169899
|
-
projectCountStatements.set(db, stmt);
|
|
169900
|
-
}
|
|
169901
|
-
return stmt;
|
|
169902
|
-
}
|
|
169903
|
-
function getLatestCommitTimeStatement(db) {
|
|
169904
|
-
let stmt = latestCommitTimeStatements.get(db);
|
|
169905
|
-
if (!stmt) {
|
|
169906
|
-
stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
|
|
169907
|
-
latestCommitTimeStatements.set(db, stmt);
|
|
169908
|
-
}
|
|
169909
|
-
return stmt;
|
|
169910
|
-
}
|
|
169911
|
-
function getEvictOverflowStatement(db) {
|
|
169912
|
-
let stmt = evictOverflowStatements.get(db);
|
|
169913
|
-
if (!stmt) {
|
|
169914
|
-
stmt = db.prepare(`DELETE FROM git_commits
|
|
169915
|
-
WHERE rowid IN (
|
|
169916
|
-
SELECT rowid FROM git_commits
|
|
169917
|
-
WHERE project_path = ?
|
|
169918
|
-
ORDER BY committed_at DESC, sha DESC
|
|
169919
|
-
LIMIT -1 OFFSET ?
|
|
169920
|
-
)`);
|
|
169921
|
-
evictOverflowStatements.set(db, stmt);
|
|
169922
|
-
}
|
|
169923
|
-
return stmt;
|
|
169924
|
-
}
|
|
169925
|
-
function upsertCommits(db, projectPath, commits) {
|
|
169926
|
-
if (commits.length === 0)
|
|
169927
|
-
return { inserted: 0, updated: 0 };
|
|
169928
|
-
const existing = new Set;
|
|
169929
|
-
for (const row of getExistingShasStatement(db).all(projectPath)) {
|
|
169930
|
-
existing.add(row.sha);
|
|
169931
|
-
}
|
|
169932
|
-
let inserted = 0;
|
|
169933
|
-
let updated = 0;
|
|
169934
|
-
const now = Date.now();
|
|
169935
|
-
const insertStmt = getInsertStatement(db);
|
|
169936
|
-
db.transaction(() => {
|
|
169937
|
-
for (const commit of commits) {
|
|
169938
|
-
const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
|
|
169939
|
-
if (result.changes > 0) {
|
|
169940
|
-
if (existing.has(commit.sha)) {
|
|
169941
|
-
updated++;
|
|
169942
|
-
} else {
|
|
169943
|
-
inserted++;
|
|
169944
|
-
existing.add(commit.sha);
|
|
169945
|
-
}
|
|
169946
|
-
}
|
|
169947
|
-
}
|
|
169948
|
-
})();
|
|
169949
|
-
return { inserted, updated };
|
|
169950
|
-
}
|
|
169951
|
-
function getCommitCount(db, projectPath) {
|
|
169952
|
-
const row = getProjectCountStatement(db).get(projectPath);
|
|
169953
|
-
return row?.count ?? 0;
|
|
169954
|
-
}
|
|
169955
|
-
function getLatestIndexedCommitTimeMs(db, projectPath) {
|
|
169956
|
-
const row = getLatestCommitTimeStatement(db).get(projectPath);
|
|
169957
|
-
return row?.latest ?? null;
|
|
169958
|
-
}
|
|
169959
|
-
function enforceProjectCap(db, projectPath, maxCommits) {
|
|
169960
|
-
if (maxCommits <= 0)
|
|
169961
|
-
return 0;
|
|
169962
|
-
const count = getCommitCount(db, projectPath);
|
|
169963
|
-
if (count <= maxCommits)
|
|
169964
|
-
return 0;
|
|
169965
|
-
getEvictOverflowStatement(db).run(projectPath, maxCommits);
|
|
169966
|
-
const after = getCommitCount(db, projectPath);
|
|
169967
|
-
const evicted = Math.max(0, count - after);
|
|
169968
|
-
if (evicted > 0) {
|
|
169969
|
-
log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
|
|
169970
|
-
}
|
|
169971
|
-
return evicted;
|
|
169972
|
-
}
|
|
169973
|
-
|
|
169974
|
-
// ../plugin/src/features/magic-context/git-commits/indexer.ts
|
|
169975
170200
|
var MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
169976
170201
|
var EMBED_BATCH_SIZE = 16;
|
|
169977
170202
|
var EMBED_MAX_PER_SWEEP = 500;
|
|
@@ -171156,13 +171381,13 @@ function extractM0Block(m0Text, tag) {
|
|
|
171156
171381
|
|
|
171157
171382
|
// ../plugin/src/hooks/magic-context/key-files-block.ts
|
|
171158
171383
|
import { readFileSync as readFileSync6, realpathSync as realpathSync2 } from "node:fs";
|
|
171159
|
-
import { join as
|
|
171384
|
+
import { join as join8, sep as sep2 } from "node:path";
|
|
171160
171385
|
|
|
171161
171386
|
// ../plugin/src/features/magic-context/key-files/aft-availability.ts
|
|
171162
171387
|
var import_comment_json2 = __toESM(require_src2(), 1);
|
|
171163
171388
|
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
|
|
171164
171389
|
import { homedir as homedir4 } from "node:os";
|
|
171165
|
-
import { isAbsolute as isAbsolute2, join as
|
|
171390
|
+
import { isAbsolute as isAbsolute2, join as join7, resolve as resolve3 } from "node:path";
|
|
171166
171391
|
import { fileURLToPath } from "node:url";
|
|
171167
171392
|
var overrideAvailability = null;
|
|
171168
171393
|
function parseConfig(path5) {
|
|
@@ -171183,14 +171408,14 @@ function resolveLocalEntryPackageName(value, configDir) {
|
|
|
171183
171408
|
return null;
|
|
171184
171409
|
}
|
|
171185
171410
|
} else if (value.startsWith("~/")) {
|
|
171186
|
-
dir =
|
|
171411
|
+
dir = join7(homedir4(), value.slice(2));
|
|
171187
171412
|
} else if (value.startsWith("/") || value.startsWith("./") || value.startsWith("../")) {
|
|
171188
171413
|
dir = isAbsolute2(value) ? value : resolve3(configDir, value);
|
|
171189
171414
|
} else {
|
|
171190
171415
|
return null;
|
|
171191
171416
|
}
|
|
171192
171417
|
try {
|
|
171193
|
-
const pkg = JSON.parse(readFileSync5(
|
|
171418
|
+
const pkg = JSON.parse(readFileSync5(join7(dir, "package.json"), "utf-8"));
|
|
171194
171419
|
return typeof pkg.name === "string" ? pkg.name : null;
|
|
171195
171420
|
} catch {
|
|
171196
171421
|
return null;
|
|
@@ -171221,16 +171446,16 @@ function hasAftAtKeys(value, keys2, configDir) {
|
|
|
171221
171446
|
function getAftAvailability() {
|
|
171222
171447
|
const home = process.env.HOME || homedir4();
|
|
171223
171448
|
const opencodePaths = [
|
|
171224
|
-
|
|
171225
|
-
|
|
171449
|
+
join7(home, ".config", "opencode", "opencode.jsonc"),
|
|
171450
|
+
join7(home, ".config", "opencode", "opencode.json")
|
|
171226
171451
|
];
|
|
171227
|
-
const piPaths = [
|
|
171452
|
+
const piPaths = [join7(home, ".pi", "agent", "settings.json")];
|
|
171228
171453
|
const checkedPaths = [...opencodePaths, ...piPaths];
|
|
171229
171454
|
let opencode = false;
|
|
171230
171455
|
for (const path5 of opencodePaths) {
|
|
171231
171456
|
try {
|
|
171232
171457
|
const config2 = parseConfig(path5);
|
|
171233
|
-
const configDir =
|
|
171458
|
+
const configDir = join7(path5, "..");
|
|
171234
171459
|
if (hasAftAtKeys(config2, ["plugin", "plugins", "mcp", "mcp_servers"], configDir)) {
|
|
171235
171460
|
opencode = true;
|
|
171236
171461
|
break;
|
|
@@ -171241,7 +171466,7 @@ function getAftAvailability() {
|
|
|
171241
171466
|
for (const path5 of piPaths) {
|
|
171242
171467
|
try {
|
|
171243
171468
|
const config2 = parseConfig(path5);
|
|
171244
|
-
const configDir =
|
|
171469
|
+
const configDir = join7(path5, "..");
|
|
171245
171470
|
if (hasAftAtKeys(config2, ["packages", "extensions"], configDir)) {
|
|
171246
171471
|
pi = true;
|
|
171247
171472
|
break;
|
|
@@ -171320,7 +171545,7 @@ function buildKeyFilesBlock(db, projectPath, config2 = { enabled: true, tokenBud
|
|
|
171320
171545
|
let nextStale = null;
|
|
171321
171546
|
let observed = false;
|
|
171322
171547
|
try {
|
|
171323
|
-
const absPath =
|
|
171548
|
+
const absPath = join8(projectPath, row.path);
|
|
171324
171549
|
const real = realpathSync2(absPath);
|
|
171325
171550
|
if (!isUnderResolvedRoot(projectRoot, real)) {
|
|
171326
171551
|
nextStale = "missing";
|
|
@@ -171721,7 +171946,7 @@ function createCtxSearchTool(deps) {
|
|
|
171721
171946
|
memoryEnabled,
|
|
171722
171947
|
embeddingEnabled,
|
|
171723
171948
|
embedQuery: async (text, signal) => {
|
|
171724
|
-
const result = await embedTextForProject(projectIdentity, text, signal);
|
|
171949
|
+
const result = await embedTextForProject(projectIdentity, text, signal, "query");
|
|
171725
171950
|
return result?.vector ?? null;
|
|
171726
171951
|
},
|
|
171727
171952
|
isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
|