@cortexkit/pi-magic-context 0.30.7 → 0.31.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/README.md +1 -0
- package/dist/index.js +1246 -144
- package/dist/subagent-entry.js +511 -51
- package/package.json +1 -1
package/dist/subagent-entry.js
CHANGED
|
@@ -8017,7 +8017,7 @@ var require_src2 = __commonJS((exports, module) => {
|
|
|
8017
8017
|
});
|
|
8018
8018
|
|
|
8019
8019
|
// ../plugin/src/features/magic-context/storage-db.ts
|
|
8020
|
-
import { chmodSync, copyFileSync, cpSync, existsSync as
|
|
8020
|
+
import { chmodSync, copyFileSync, cpSync, existsSync as existsSync4, mkdirSync as mkdirSync3, mkdtempSync } from "node:fs";
|
|
8021
8021
|
init_data_path();
|
|
8022
8022
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
8023
8023
|
|
|
@@ -8311,13 +8311,23 @@ var CATEGORY_DEFAULT_TTL = {
|
|
|
8311
8311
|
};
|
|
8312
8312
|
|
|
8313
8313
|
// ../plugin/src/features/magic-context/memory/project-identity.ts
|
|
8314
|
+
init_logger();
|
|
8314
8315
|
import { execFileSync } from "node:child_process";
|
|
8315
8316
|
import { createHash } from "node:crypto";
|
|
8316
|
-
import { statSync } from "node:fs";
|
|
8317
|
+
import { existsSync as existsSync2, realpathSync, statSync } from "node:fs";
|
|
8317
8318
|
import path3 from "node:path";
|
|
8318
8319
|
var GIT_TIMEOUT_MS = 5000;
|
|
8320
|
+
var TRANSIENT_FAILURE_COOLDOWN_MS = 5 * 60 * 1000;
|
|
8319
8321
|
var identityCache = new Map;
|
|
8322
|
+
var lastKnownGitIdentityCache = new Map;
|
|
8320
8323
|
var directoryFallbackCache = new Map;
|
|
8324
|
+
var transientFailureCooldown = new Map;
|
|
8325
|
+
var dubiousOwnershipFallbackDirectories = new Set;
|
|
8326
|
+
var dubiousOwnershipLoggedDirectories = new Set;
|
|
8327
|
+
var dubiousOwnershipWarnedDirectories = new Set;
|
|
8328
|
+
var transientGitIdentityReuseLoggedDirectories = new Set;
|
|
8329
|
+
var execFileSyncForIdentity = execFileSync;
|
|
8330
|
+
var nowMs = () => Date.now();
|
|
8321
8331
|
|
|
8322
8332
|
class ProjectIdentityError extends Error {
|
|
8323
8333
|
errorClass;
|
|
@@ -8407,6 +8417,9 @@ function classifyGitError(error, rawDirectory) {
|
|
|
8407
8417
|
return new ProjectIdentityError("permission_denied", rawDirectory, "Permission denied while spawning git", asError(error));
|
|
8408
8418
|
}
|
|
8409
8419
|
const stderr = getErrorStderr(error).toLowerCase();
|
|
8420
|
+
if (stderr.includes("detected dubious ownership")) {
|
|
8421
|
+
return new ProjectIdentityError("dubious_ownership", rawDirectory, "git refused to read the repository because it detected dubious ownership", asError(error));
|
|
8422
|
+
}
|
|
8410
8423
|
if (stderr.includes("not a git repository") || stderr.includes("does not have any commits yet") || stderr.includes("ambiguous argument 'head'") || stderr.includes("unknown revision or path")) {
|
|
8411
8424
|
return new ProjectIdentityError("not_git_repo", rawDirectory, "Directory has no git root commit; caller may use directory fallback", asError(error));
|
|
8412
8425
|
}
|
|
@@ -8419,9 +8432,12 @@ function resolveProjectIdentityStrict(directory) {
|
|
|
8419
8432
|
return cached;
|
|
8420
8433
|
}
|
|
8421
8434
|
assertDirectoryUsable(canonical, directory);
|
|
8435
|
+
if (!hasGitDir(canonical)) {
|
|
8436
|
+
throw new ProjectIdentityError("not_git_repo", directory, "Directory has no git metadata; caller may use directory fallback");
|
|
8437
|
+
}
|
|
8422
8438
|
let output;
|
|
8423
8439
|
try {
|
|
8424
|
-
output =
|
|
8440
|
+
output = execFileSyncForIdentity("git", ["rev-list", "--max-parents=0", "HEAD"], {
|
|
8425
8441
|
cwd: canonical,
|
|
8426
8442
|
encoding: "utf8",
|
|
8427
8443
|
env: { ...process.env, LC_ALL: "C", LANG: "C" },
|
|
@@ -8439,10 +8455,73 @@ function resolveProjectIdentityStrict(directory) {
|
|
|
8439
8455
|
}
|
|
8440
8456
|
const identity = `git:${rootCommit}`;
|
|
8441
8457
|
identityCache.set(canonical, identity);
|
|
8458
|
+
lastKnownGitIdentityCache.set(canonical, identity);
|
|
8459
|
+
transientFailureCooldown.delete(canonical);
|
|
8460
|
+
dubiousOwnershipFallbackDirectories.delete(canonical);
|
|
8461
|
+
transientGitIdentityReuseLoggedDirectories.delete(canonical);
|
|
8442
8462
|
return identity;
|
|
8443
8463
|
}
|
|
8444
8464
|
function shouldUseDirectoryFallback(error) {
|
|
8445
|
-
return error.errorClass
|
|
8465
|
+
return error.errorClass !== "permission_denied";
|
|
8466
|
+
}
|
|
8467
|
+
function getActiveCooldown(canonical) {
|
|
8468
|
+
const until = transientFailureCooldown.get(canonical);
|
|
8469
|
+
if (until === undefined)
|
|
8470
|
+
return;
|
|
8471
|
+
if (nowMs() < until)
|
|
8472
|
+
return until;
|
|
8473
|
+
transientFailureCooldown.delete(canonical);
|
|
8474
|
+
return;
|
|
8475
|
+
}
|
|
8476
|
+
function lastKnownGitIdentity(canonical) {
|
|
8477
|
+
return lastKnownGitIdentityCache.get(canonical) ?? identityCache.get(canonical);
|
|
8478
|
+
}
|
|
8479
|
+
function nearestLastKnownGitIdentity(canonical) {
|
|
8480
|
+
const visited = new Set;
|
|
8481
|
+
const walk = (start) => {
|
|
8482
|
+
let current = start;
|
|
8483
|
+
while (!visited.has(current)) {
|
|
8484
|
+
visited.add(current);
|
|
8485
|
+
const cached = lastKnownGitIdentity(current);
|
|
8486
|
+
if (cached !== undefined)
|
|
8487
|
+
return { identity: cached, source: current };
|
|
8488
|
+
const parent = path3.dirname(current);
|
|
8489
|
+
if (parent === current)
|
|
8490
|
+
break;
|
|
8491
|
+
current = parent;
|
|
8492
|
+
}
|
|
8493
|
+
return;
|
|
8494
|
+
};
|
|
8495
|
+
const exactOrAncestor = walk(canonical);
|
|
8496
|
+
if (exactOrAncestor)
|
|
8497
|
+
return exactOrAncestor;
|
|
8498
|
+
try {
|
|
8499
|
+
const realCanonical = realpathSync.native(canonical);
|
|
8500
|
+
if (realCanonical !== canonical)
|
|
8501
|
+
return walk(realCanonical);
|
|
8502
|
+
} catch {}
|
|
8503
|
+
return;
|
|
8504
|
+
}
|
|
8505
|
+
function reuseLastKnownGitIdentity(canonical) {
|
|
8506
|
+
const cached = nearestLastKnownGitIdentity(canonical);
|
|
8507
|
+
if (cached === undefined)
|
|
8508
|
+
return;
|
|
8509
|
+
if (!transientGitIdentityReuseLoggedDirectories.has(canonical)) {
|
|
8510
|
+
transientGitIdentityReuseLoggedDirectories.add(canonical);
|
|
8511
|
+
const sourceNote = cached.source === canonical ? "" : ` from ancestor ${cached.source}`;
|
|
8512
|
+
log(`[magic-context] git identity resolution is temporarily unavailable for ${canonical}; reusing the last successful project identity${sourceNote} to avoid splitting project-scoped memory`);
|
|
8513
|
+
}
|
|
8514
|
+
return cached.identity;
|
|
8515
|
+
}
|
|
8516
|
+
function formatDubiousOwnershipWarning(canonical) {
|
|
8517
|
+
return `Magic Context: git refused to read ${canonical} (dubious ownership — the repo is owned by a different user). Using a directory-based project identity for now, which keeps memory separate from this repo's normal identity. Fix: git config --global --add safe.directory ${canonical}`;
|
|
8518
|
+
}
|
|
8519
|
+
function recordDubiousOwnershipFallback(canonical) {
|
|
8520
|
+
dubiousOwnershipFallbackDirectories.add(canonical);
|
|
8521
|
+
if (dubiousOwnershipLoggedDirectories.has(canonical))
|
|
8522
|
+
return;
|
|
8523
|
+
dubiousOwnershipLoggedDirectories.add(canonical);
|
|
8524
|
+
log(`[magic-context] ${formatDubiousOwnershipWarning(canonical)}`);
|
|
8446
8525
|
}
|
|
8447
8526
|
function resolveProjectIdentity(directory) {
|
|
8448
8527
|
const canonical = path3.resolve(directory);
|
|
@@ -8453,27 +8532,74 @@ function resolveProjectIdentity(directory) {
|
|
|
8453
8532
|
}
|
|
8454
8533
|
directoryFallbackCache.delete(canonical);
|
|
8455
8534
|
}
|
|
8535
|
+
if (getActiveCooldown(canonical) !== undefined) {
|
|
8536
|
+
if (hasGitDir(canonical)) {
|
|
8537
|
+
const cachedGitIdentity = reuseLastKnownGitIdentity(canonical);
|
|
8538
|
+
if (cachedGitIdentity !== undefined) {
|
|
8539
|
+
return cachedGitIdentity;
|
|
8540
|
+
}
|
|
8541
|
+
}
|
|
8542
|
+
return directoryFallback(canonical);
|
|
8543
|
+
}
|
|
8456
8544
|
try {
|
|
8457
8545
|
return resolveProjectIdentityStrict(directory);
|
|
8458
8546
|
} catch (error) {
|
|
8459
8547
|
if (error instanceof ProjectIdentityError && shouldUseDirectoryFallback(error)) {
|
|
8460
8548
|
const fallback = directoryFallback(canonical);
|
|
8461
|
-
|
|
8549
|
+
const hasGitMetadata = hasGitDir(canonical);
|
|
8550
|
+
if (!hasGitMetadata) {
|
|
8462
8551
|
directoryFallbackCache.set(canonical, fallback);
|
|
8552
|
+
transientFailureCooldown.delete(canonical);
|
|
8553
|
+
} else {
|
|
8554
|
+
transientFailureCooldown.set(canonical, nowMs() + TRANSIENT_FAILURE_COOLDOWN_MS);
|
|
8555
|
+
const cachedGitIdentity = reuseLastKnownGitIdentity(canonical);
|
|
8556
|
+
if (cachedGitIdentity !== undefined) {
|
|
8557
|
+
return cachedGitIdentity;
|
|
8558
|
+
}
|
|
8559
|
+
}
|
|
8560
|
+
if (error.errorClass === "dubious_ownership") {
|
|
8561
|
+
recordDubiousOwnershipFallback(canonical);
|
|
8463
8562
|
}
|
|
8464
8563
|
return fallback;
|
|
8465
8564
|
}
|
|
8466
8565
|
throw error;
|
|
8467
8566
|
}
|
|
8468
8567
|
}
|
|
8469
|
-
function
|
|
8568
|
+
function resolveProjectIdentityOrFallback(directory) {
|
|
8470
8569
|
try {
|
|
8471
|
-
|
|
8570
|
+
return resolveProjectIdentity(directory);
|
|
8571
|
+
} catch (error) {
|
|
8572
|
+
const canonical = path3.resolve(directory);
|
|
8573
|
+
const fallback = directoryFallback(canonical);
|
|
8574
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8575
|
+
log(`[magic-context] project identity resolution failed for ${canonical}; using directory fallback ${fallback}: ${message}`);
|
|
8576
|
+
return fallback;
|
|
8577
|
+
}
|
|
8578
|
+
}
|
|
8579
|
+
function hasGitDir(canonical) {
|
|
8580
|
+
if (hasGitDirInAncestorChain(canonical)) {
|
|
8472
8581
|
return true;
|
|
8582
|
+
}
|
|
8583
|
+
try {
|
|
8584
|
+
const realCanonical = realpathSync.native(canonical);
|
|
8585
|
+
return realCanonical !== canonical && hasGitDirInAncestorChain(realCanonical);
|
|
8473
8586
|
} catch {
|
|
8474
8587
|
return false;
|
|
8475
8588
|
}
|
|
8476
8589
|
}
|
|
8590
|
+
function hasGitDirInAncestorChain(startDirectory) {
|
|
8591
|
+
let current = startDirectory;
|
|
8592
|
+
while (true) {
|
|
8593
|
+
if (existsSync2(path3.join(current, ".git"))) {
|
|
8594
|
+
return true;
|
|
8595
|
+
}
|
|
8596
|
+
const parent = path3.dirname(current);
|
|
8597
|
+
if (parent === current) {
|
|
8598
|
+
return false;
|
|
8599
|
+
}
|
|
8600
|
+
current = parent;
|
|
8601
|
+
}
|
|
8602
|
+
}
|
|
8477
8603
|
function normalizeStoredProjectPath(rawOrStored) {
|
|
8478
8604
|
if (rawOrStored.startsWith("git:") || rawOrStored.startsWith("dir:")) {
|
|
8479
8605
|
return rawOrStored;
|
|
@@ -10038,6 +10164,15 @@ var MIGRATIONS = [
|
|
|
10038
10164
|
);
|
|
10039
10165
|
`);
|
|
10040
10166
|
}
|
|
10167
|
+
},
|
|
10168
|
+
{
|
|
10169
|
+
version: 50,
|
|
10170
|
+
description: "add durable ctx-wrapup session marker",
|
|
10171
|
+
up(db) {
|
|
10172
|
+
if (tableExists2(db, "session_meta")) {
|
|
10173
|
+
ensureColumn(db, "session_meta", "wrapup_in_progress_state", "TEXT");
|
|
10174
|
+
}
|
|
10175
|
+
}
|
|
10041
10176
|
}
|
|
10042
10177
|
];
|
|
10043
10178
|
var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
|
|
@@ -142760,7 +142895,7 @@ function loadToolDefinitionMeasurements(db) {
|
|
|
142760
142895
|
// ../plugin/src/features/magic-context/tool-owner-backfill.ts
|
|
142761
142896
|
init_data_path();
|
|
142762
142897
|
init_logger();
|
|
142763
|
-
import { existsSync as
|
|
142898
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
142764
142899
|
import { join as join2 } from "node:path";
|
|
142765
142900
|
var LEASE_DURATION_MS = 5 * 60 * 1000;
|
|
142766
142901
|
var LEASE_RENEWAL_MS = 60 * 1000;
|
|
@@ -142800,7 +142935,7 @@ function runToolOwnerBackfill(db) {
|
|
|
142800
142935
|
return result;
|
|
142801
142936
|
}
|
|
142802
142937
|
const opencodeDbPath = resolveOpencodeDbPath();
|
|
142803
|
-
if (!
|
|
142938
|
+
if (!existsSync3(opencodeDbPath)) {
|
|
142804
142939
|
log(`[backfill] OpenCode DB not found at ${opencodeDbPath} — marking all unbackfilled sessions as skipped. Lazy adoption (defense-in-depth) handles legacy rows at runtime.`);
|
|
142805
142940
|
markAllUnbackfilledSessionsSkipped(db);
|
|
142806
142941
|
result.sessionsSkippedNoOcDb = countSessionsByStatus(db, "skipped");
|
|
@@ -143022,7 +143157,7 @@ var persistenceByDatabase = new WeakMap;
|
|
|
143022
143157
|
var persistenceErrorByDatabase = new WeakMap;
|
|
143023
143158
|
var pathByDatabase = new WeakMap;
|
|
143024
143159
|
var lastSchemaFenceRejection = null;
|
|
143025
|
-
var LATEST_SUPPORTED_VERSION =
|
|
143160
|
+
var LATEST_SUPPORTED_VERSION = 50;
|
|
143026
143161
|
var PERMISSIONS_ENFORCEABLE = process.platform !== "win32";
|
|
143027
143162
|
function ensureSecureStorageDir(dir) {
|
|
143028
143163
|
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
@@ -143039,7 +143174,7 @@ function restrictDatabaseFilePermissions(dbPath) {
|
|
|
143039
143174
|
return;
|
|
143040
143175
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
143041
143176
|
const file = `${dbPath}${suffix}`;
|
|
143042
|
-
if (!
|
|
143177
|
+
if (!existsSync4(file))
|
|
143043
143178
|
continue;
|
|
143044
143179
|
try {
|
|
143045
143180
|
chmodSync(file, 384);
|
|
@@ -143065,11 +143200,11 @@ function getDatabasePath(db) {
|
|
|
143065
143200
|
return pathByDatabase.get(db) ?? null;
|
|
143066
143201
|
}
|
|
143067
143202
|
function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
143068
|
-
if (
|
|
143203
|
+
if (existsSync4(targetDbPath))
|
|
143069
143204
|
return;
|
|
143070
143205
|
const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
|
|
143071
143206
|
const legacyDbPath = join3(legacyDir, "context.db");
|
|
143072
|
-
if (!
|
|
143207
|
+
if (!existsSync4(legacyDbPath))
|
|
143073
143208
|
return;
|
|
143074
143209
|
log(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
|
|
143075
143210
|
ensureSecureStorageDir(targetDbDir);
|
|
@@ -143086,7 +143221,7 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
|
143086
143221
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
143087
143222
|
const src = `${legacyDbPath}${suffix}`;
|
|
143088
143223
|
const dst = join3(targetDbDir, `context.db${suffix}`);
|
|
143089
|
-
if (
|
|
143224
|
+
if (existsSync4(src)) {
|
|
143090
143225
|
try {
|
|
143091
143226
|
copyFileSync(src, dst);
|
|
143092
143227
|
} catch (error) {
|
|
@@ -143096,7 +143231,7 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
|
143096
143231
|
}
|
|
143097
143232
|
const legacyModelsDir = join3(legacyDir, "models");
|
|
143098
143233
|
const targetModelsDir = join3(targetDbDir, "models");
|
|
143099
|
-
if (
|
|
143234
|
+
if (existsSync4(legacyModelsDir) && !existsSync4(targetModelsDir)) {
|
|
143100
143235
|
try {
|
|
143101
143236
|
cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
|
|
143102
143237
|
} catch (error) {
|
|
@@ -143700,6 +143835,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
143700
143835
|
force_emergency_bypass_used INTEGER NOT NULL DEFAULT 0,
|
|
143701
143836
|
emergency_drain_active INTEGER NOT NULL DEFAULT 0,
|
|
143702
143837
|
historian_drain_failure_at INTEGER NOT NULL DEFAULT 0,
|
|
143838
|
+
wrapup_in_progress_state TEXT,
|
|
143703
143839
|
cached_m0_materialized_at INTEGER,
|
|
143704
143840
|
cached_m0_session_facts_version INTEGER,
|
|
143705
143841
|
cached_m0_upgrade_state TEXT,
|
|
@@ -143988,6 +144124,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
143988
144124
|
ensureColumn(db, "session_meta", "force_emergency_bypass_used", "INTEGER NOT NULL DEFAULT 0");
|
|
143989
144125
|
ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
|
|
143990
144126
|
ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
|
|
144127
|
+
ensureColumn(db, "session_meta", "wrapup_in_progress_state", "TEXT");
|
|
143991
144128
|
ensureColumn(db, "session_meta", "cached_m0_materialized_at", "INTEGER");
|
|
143992
144129
|
ensureColumn(db, "session_meta", "cached_m0_session_facts_version", "INTEGER");
|
|
143993
144130
|
ensureColumn(db, "session_meta", "cached_m0_upgrade_state", "TEXT");
|
|
@@ -144164,7 +144301,7 @@ init_logger();
|
|
|
144164
144301
|
// ../plugin/src/config/migrate-config-location.ts
|
|
144165
144302
|
import {
|
|
144166
144303
|
closeSync,
|
|
144167
|
-
existsSync as
|
|
144304
|
+
existsSync as existsSync5,
|
|
144168
144305
|
mkdirSync as mkdirSync4,
|
|
144169
144306
|
openSync,
|
|
144170
144307
|
readFileSync as readFileSync2,
|
|
@@ -144459,7 +144596,7 @@ function visibleConfigMigrationWarning(scope, targetPath, paths, reason) {
|
|
|
144459
144596
|
}
|
|
144460
144597
|
function migrateConfigFile(opts) {
|
|
144461
144598
|
const warnings = [];
|
|
144462
|
-
const existingSources = opts.legacySources.filter((source) =>
|
|
144599
|
+
const existingSources = opts.legacySources.filter((source) => existsSync5(source.path));
|
|
144463
144600
|
const info = opts.logger?.info ?? opts.logger?.log;
|
|
144464
144601
|
if (existingSources.length === 0) {
|
|
144465
144602
|
return { migrated: false, conflict: false, targetPath: opts.targetPath, warnings };
|
|
@@ -144475,7 +144612,7 @@ function migrateConfigFile(opts) {
|
|
|
144475
144612
|
...source,
|
|
144476
144613
|
content: readFileSync2(source.path, "utf-8")
|
|
144477
144614
|
}));
|
|
144478
|
-
if (
|
|
144615
|
+
if (existsSync5(opts.targetPath)) {
|
|
144479
144616
|
const targetContent = readFileSync2(opts.targetPath, "utf-8");
|
|
144480
144617
|
const differing2 = sources.filter((source) => !fileSemanticsMatch(source.content, targetContent));
|
|
144481
144618
|
if (differing2.length > 0) {
|
|
@@ -144572,7 +144709,7 @@ function pruneNestedConfigLeaf(block, relativePath) {
|
|
|
144572
144709
|
}
|
|
144573
144710
|
|
|
144574
144711
|
// src/config/index.ts
|
|
144575
|
-
import { existsSync as
|
|
144712
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4 } from "node:fs";
|
|
144576
144713
|
|
|
144577
144714
|
// ../plugin/src/config/agent-disable.ts
|
|
144578
144715
|
function isDreamerRunnable(config) {
|
|
@@ -159724,7 +159861,6 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
159724
159861
|
enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
|
|
159725
159862
|
auto_update: exports_external.boolean().optional().describe("Enable automatic npm self-update checks for the OpenCode plugin. Security: USER-only in config loader, so hostile project configs cannot suppress updates."),
|
|
159726
159863
|
language: exports_external.string().trim().toLowerCase().refine((s) => isValidLanguageCode(s), 'language must be a 2-letter ISO 639-1 code (e.g. "tr", "es", "de")').optional().describe("Output language for Magic Context's generated content and guidance, as a " + '2-letter ISO 639-1 code (e.g. "tr", "es", "de", "ja", "pt"). When set, the ' + "historian, dreamer, sidekick, and the agent-guidance block instruct the model to " + "write its PROSE in this language while keeping all structural tokens (XML tags, " + "the five memory category names, code identifiers, file paths) in English. " + "USER-LEVEL ONLY (ignored in project config for security). Unset = today's " + "behavior (model mirrors the conversation; English scaffolding). Changing it " + "triggers one cache re-materialization; existing compartments/memories keep their " + "original language until naturally rewritten."),
|
|
159727
|
-
ctx_reduce_enabled: exports_external.boolean().default(true).describe("When false, ctx_reduce tool is hidden, all nudges disabled, and prompt guidance about ctx_reduce stripped. Heuristic cleanup, compartments, memory, and other features still work. (default: true)"),
|
|
159728
159864
|
historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
|
|
159729
159865
|
dreamer: DreamerConfigSchema.optional().describe("Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)"),
|
|
159730
159866
|
cache_ttl: exports_external.union([exports_external.string(), exports_external.object({ default: exports_external.string() }).catchall(exports_external.string())]).default("5m").describe('Cache TTL: string (e.g. "5m") or per-model object ({ default: "5m", "model-id": "10m" })'),
|
|
@@ -159763,9 +159899,9 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
159763
159899
|
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."),
|
|
159764
159900
|
smart_drops: exports_external.boolean().default(false).describe("Content-aware reclaim of provably-superseded tool output, layered on the existing execute-pass auto-drop. When on: superseded todowrite (keep newest 1), spent ctx_reduce (keep newest 5), and zero-value meta (bash_status, bash_kill, ctx_note read/dismiss) outputs are dropped; older edits to a file are compressed to a filePath-preserving marker while the newest edit per file stays full. Only acts on passes already busting the cache, so it never originates a cache bust. Honors the protected-tag reserve. Experimental: opt-in, default off until cache stability is proven; when off the wire is byte-identical to the positional-only reclaim. Requires a restart."),
|
|
159765
159901
|
caveman_text_compression: exports_external.object({
|
|
159766
|
-
enabled: exports_external.boolean().default(false).describe("Apply deterministic caveman-style text compression to old conversation text.
|
|
159902
|
+
enabled: exports_external.boolean().default(false).describe("Apply deterministic caveman-style text compression to old conversation text. Active for primary sessions when enabled; never for subagents. Compresses user/assistant text in oldest-first tiers: ultra (oldest 20%), full, lite, untouched (newest 40%)."),
|
|
159767
159903
|
min_chars: exports_external.number().min(100).max(1e4).default(500).describe("Text parts shorter than this (characters) stay untouched. Min 100, max 10000. Default: 500.")
|
|
159768
|
-
}).default({ enabled: false, min_chars: 500 }).describe("Age-tier caveman compression for long user/assistant text parts.
|
|
159904
|
+
}).default({ enabled: false, min_chars: 500 }).describe("Age-tier caveman compression for long user/assistant text parts. Active for primary sessions when enabled; never for subagents. Oldest 20% of eligible tags (outside protected tail) go to ultra, next 20% to full, next 20% to lite, newest 40% untouched. Graduated from experimental.caveman_text_compression; opt-in, default off (lossy)."),
|
|
159769
159905
|
memory: exports_external.object({
|
|
159770
159906
|
enabled: exports_external.boolean().default(true).describe("Enable cross-session memory (default: true)"),
|
|
159771
159907
|
injection_budget_tokens: exports_external.number().min(500).max(20000).default(4000).describe("Token budget for memory injection on session start (min: 500, max: 20000, default: 4000)"),
|
|
@@ -159798,7 +159934,7 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
159798
159934
|
});
|
|
159799
159935
|
|
|
159800
159936
|
// ../plugin/src/config/variable.ts
|
|
159801
|
-
import { existsSync as
|
|
159937
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "node:fs";
|
|
159802
159938
|
import { homedir as homedir3 } from "node:os";
|
|
159803
159939
|
import { dirname as dirname4, isAbsolute as isAbsolute2, resolve } from "node:path";
|
|
159804
159940
|
|
|
@@ -159933,7 +160069,7 @@ function substituteConfigVariables(input) {
|
|
|
159933
160069
|
if (sensitiveReason) {
|
|
159934
160070
|
warnings.push(`${token} resolves to a sensitive path (${sensitiveReason}: ${filePath}); ` + "inlining its contents into config — make sure this is intentional.");
|
|
159935
160071
|
}
|
|
159936
|
-
if (!
|
|
160072
|
+
if (!existsSync6(filePath)) {
|
|
159937
160073
|
warnings.push(`File not found for ${token} (resolved to ${filePath}); using empty string`);
|
|
159938
160074
|
continue;
|
|
159939
160075
|
}
|
|
@@ -159962,10 +160098,10 @@ function getUserConfigPaths() {
|
|
|
159962
160098
|
return [`${basePath}.jsonc`, `${basePath}.json`];
|
|
159963
160099
|
}
|
|
159964
160100
|
function resolveFirstExisting(paths) {
|
|
159965
|
-
return paths.find((path4) =>
|
|
160101
|
+
return paths.find((path4) => existsSync7(path4));
|
|
159966
160102
|
}
|
|
159967
160103
|
function resolvePiLegacyFallback(sources) {
|
|
159968
|
-
return sources.find((source) =>
|
|
160104
|
+
return sources.find((source) => existsSync7(source.path)) ?? null;
|
|
159969
160105
|
}
|
|
159970
160106
|
function loadConfigFile(path4, scope) {
|
|
159971
160107
|
try {
|
|
@@ -160102,7 +160238,7 @@ function loadPiConfig(opts = {}) {
|
|
|
160102
160238
|
if (loaded)
|
|
160103
160239
|
loadedFiles.push(loaded);
|
|
160104
160240
|
}
|
|
160105
|
-
const legacyProjectUnmigrated = !projectPath && !projectLegacyFallback && legacySources.project.some((source) =>
|
|
160241
|
+
const legacyProjectUnmigrated = !projectPath && !projectLegacyFallback && legacySources.project.some((source) => existsSync7(source.path));
|
|
160106
160242
|
const userPath = resolveFirstExisting(getUserConfigPaths());
|
|
160107
160243
|
const userLegacyFallback = userPath ? null : resolvePiLegacyFallback(harnessLegacy.user);
|
|
160108
160244
|
const userReadPath = userPath ?? userLegacyFallback?.path;
|
|
@@ -160111,7 +160247,7 @@ function loadPiConfig(opts = {}) {
|
|
|
160111
160247
|
if (loaded)
|
|
160112
160248
|
loadedFiles.push(loaded);
|
|
160113
160249
|
}
|
|
160114
|
-
const legacyUserUnmigrated = !userPath && !userLegacyFallback && legacySources.user.some((source) =>
|
|
160250
|
+
const legacyUserUnmigrated = !userPath && !userLegacyFallback && legacySources.user.some((source) => existsSync7(source.path));
|
|
160115
160251
|
if (userLegacyFallback) {
|
|
160116
160252
|
warnings.push(`[user config] reading legacy config from ${userLegacyFallback.path} until migration completes; run \`npx @cortexkit/magic-context doctor\` to consolidate into the shared CortexKit location.`);
|
|
160117
160253
|
} else if (legacyUserUnmigrated) {
|
|
@@ -160212,7 +160348,7 @@ function loadPiConfigDetailed(opts = {}) {
|
|
|
160212
160348
|
if (loaded)
|
|
160213
160349
|
loadedFiles.push(loaded);
|
|
160214
160350
|
}
|
|
160215
|
-
const legacyProjectUnmigrated = !projectPath && !projectLegacyFallback && legacySources.project.some((source) =>
|
|
160351
|
+
const legacyProjectUnmigrated = !projectPath && !projectLegacyFallback && legacySources.project.some((source) => existsSync7(source.path));
|
|
160216
160352
|
const userPath = resolveFirstExisting(getUserConfigPaths());
|
|
160217
160353
|
const userLegacyFallback = userPath ? null : resolvePiLegacyFallback(harnessLegacy.user);
|
|
160218
160354
|
const userReadPath = userPath ?? userLegacyFallback?.path;
|
|
@@ -160221,7 +160357,7 @@ function loadPiConfigDetailed(opts = {}) {
|
|
|
160221
160357
|
if (loaded)
|
|
160222
160358
|
loadedFiles.push(loaded);
|
|
160223
160359
|
}
|
|
160224
|
-
const legacyUserUnmigrated = !userPath && !userLegacyFallback && legacySources.user.some((source) =>
|
|
160360
|
+
const legacyUserUnmigrated = !userPath && !userLegacyFallback && legacySources.user.some((source) => existsSync7(source.path));
|
|
160225
160361
|
if (userLegacyFallback) {
|
|
160226
160362
|
warnings.push(`[user config] reading legacy config from ${userLegacyFallback.path} until migration completes; run \`npx @cortexkit/magic-context doctor\` to consolidate into the shared CortexKit location.`);
|
|
160227
160363
|
} else if (legacyUserUnmigrated) {
|
|
@@ -161055,7 +161191,8 @@ function isNativeRuntimeMissingError(error51) {
|
|
|
161055
161191
|
if (code === "ERR_DLOPEN_FAILED" && lower.includes("onnxruntime")) {
|
|
161056
161192
|
return true;
|
|
161057
161193
|
}
|
|
161058
|
-
|
|
161194
|
+
const mentionsNativeRuntime = lower.includes("onnxruntime-node") || lower.includes("onnxruntime_binding");
|
|
161195
|
+
if (!mentionsNativeRuntime)
|
|
161059
161196
|
return false;
|
|
161060
161197
|
return code === "ERR_MODULE_NOT_FOUND" || name2 === "ResolveMessage" || lower.includes("cannot find package") || lower.includes("cannot find module") || lower.includes("err_module_not_found");
|
|
161061
161198
|
}
|
|
@@ -161200,7 +161337,7 @@ class LocalEmbeddingProvider {
|
|
|
161200
161337
|
} catch (error51) {
|
|
161201
161338
|
if (isNativeRuntimeMissingError(error51)) {
|
|
161202
161339
|
nativeRuntimeMissing = true;
|
|
161203
|
-
log("[magic-context] local
|
|
161340
|
+
log("[magic-context] local embeddings are disabled because the onnxruntime-node native binding is missing or failed to load. Run `npx @cortexkit/magic-context@latest doctor` for repair guidance (use `doctor --force` to reinstall cached plugin packages), or configure an `openai-compatible` embedding HTTP endpoint. Existing memories are unaffected.");
|
|
161204
161341
|
} else {
|
|
161205
161342
|
log("[magic-context] embedding model failed to load:", error51);
|
|
161206
161343
|
}
|
|
@@ -163623,13 +163760,13 @@ function escapeXmlContent(s) {
|
|
|
163623
163760
|
// ../plugin/src/hooks/magic-context/read-session-db.ts
|
|
163624
163761
|
init_data_path();
|
|
163625
163762
|
init_logger();
|
|
163626
|
-
import { existsSync as
|
|
163763
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
163627
163764
|
import { join as join6 } from "node:path";
|
|
163628
163765
|
function getOpenCodeDbPath() {
|
|
163629
163766
|
return join6(getDataDir(), "opencode", "opencode.db");
|
|
163630
163767
|
}
|
|
163631
163768
|
function openCodeDbExists() {
|
|
163632
|
-
return
|
|
163769
|
+
return existsSync8(getOpenCodeDbPath());
|
|
163633
163770
|
}
|
|
163634
163771
|
var cachedReadOnlyDb = null;
|
|
163635
163772
|
function closeCachedReadOnlyDb() {
|
|
@@ -169963,7 +170100,7 @@ function getMemoryVerifications(db, memoryIds) {
|
|
|
169963
170100
|
}
|
|
169964
170101
|
// ../plugin/src/features/magic-context/memory/verification-paths.ts
|
|
169965
170102
|
import { execFile } from "node:child_process";
|
|
169966
|
-
import { existsSync as
|
|
170103
|
+
import { existsSync as existsSync9, realpathSync as realpathSync2, statSync as statSync3 } from "node:fs";
|
|
169967
170104
|
import path4 from "node:path";
|
|
169968
170105
|
import { promisify } from "node:util";
|
|
169969
170106
|
var execFileAsync = promisify(execFile);
|
|
@@ -169990,7 +170127,7 @@ function isWithin(root, candidate) {
|
|
|
169990
170127
|
}
|
|
169991
170128
|
function safeRealpath(value) {
|
|
169992
170129
|
try {
|
|
169993
|
-
return
|
|
170130
|
+
return realpathSync2.native(value);
|
|
169994
170131
|
} catch {
|
|
169995
170132
|
return null;
|
|
169996
170133
|
}
|
|
@@ -170068,7 +170205,7 @@ function verificationFileExists(baseRoot, filePath) {
|
|
|
170068
170205
|
return false;
|
|
170069
170206
|
const root = path4.resolve(baseRoot);
|
|
170070
170207
|
const candidate = path4.resolve(root, filePath);
|
|
170071
|
-
return isWithin(root, candidate) &&
|
|
170208
|
+
return isWithin(root, candidate) && existsSync9(candidate);
|
|
170072
170209
|
}
|
|
170073
170210
|
async function normalizeVerificationFiles(args) {
|
|
170074
170211
|
const cwd = path4.resolve(args.cwd);
|
|
@@ -170101,7 +170238,7 @@ async function normalizeVerificationFiles(args) {
|
|
|
170101
170238
|
warnings.push(`Skipped verification path "${value}" because it is the repo/project root.`);
|
|
170102
170239
|
continue;
|
|
170103
170240
|
}
|
|
170104
|
-
if (
|
|
170241
|
+
if (existsSync9(candidate)) {
|
|
170105
170242
|
try {
|
|
170106
170243
|
if (statSync3(candidate).isDirectory()) {
|
|
170107
170244
|
warnings.push(`Skipped verification path "${value}" because it is a directory.`);
|
|
@@ -170132,7 +170269,7 @@ async function normalizeVerificationFiles(args) {
|
|
|
170132
170269
|
}
|
|
170133
170270
|
normalized.push(tracked);
|
|
170134
170271
|
} else {
|
|
170135
|
-
if (!
|
|
170272
|
+
if (!existsSync9(candidate)) {
|
|
170136
170273
|
warnings.push(`Skipped verification path "${value}" because it does not exist.`);
|
|
170137
170274
|
continue;
|
|
170138
170275
|
}
|
|
@@ -170734,6 +170871,148 @@ function recordProtectedTailNoEligibleHead(db, sessionId) {
|
|
|
170734
170871
|
return loadProtectedTailMeta(db, sessionId).recoveryNoEligibleHeadCount;
|
|
170735
170872
|
}
|
|
170736
170873
|
var DRAIN_WINDOW_MS = 10 * 60 * 1000;
|
|
170874
|
+
var WRAPUP_IN_PROGRESS_TTL_MS = 5 * 60 * 1000;
|
|
170875
|
+
function parseWrapupState(value) {
|
|
170876
|
+
if (typeof value !== "string" || value.trim().length === 0)
|
|
170877
|
+
return null;
|
|
170878
|
+
try {
|
|
170879
|
+
const parsed = JSON.parse(value);
|
|
170880
|
+
if (!parsed || typeof parsed !== "object")
|
|
170881
|
+
return null;
|
|
170882
|
+
if (typeof parsed.holderId !== "string" || parsed.holderId.length === 0)
|
|
170883
|
+
return null;
|
|
170884
|
+
const numberFields = [
|
|
170885
|
+
"acquiredAt",
|
|
170886
|
+
"expiresAt",
|
|
170887
|
+
"messagesToKeep",
|
|
170888
|
+
"anchorRawMessageCount",
|
|
170889
|
+
"targetEligibleEndOrdinal",
|
|
170890
|
+
"lastCompartmentEnd",
|
|
170891
|
+
"chunkIndex",
|
|
170892
|
+
"expectedChunks",
|
|
170893
|
+
"updatedAt"
|
|
170894
|
+
];
|
|
170895
|
+
for (const field of numberFields) {
|
|
170896
|
+
if (typeof parsed[field] !== "number" || !Number.isFinite(parsed[field]))
|
|
170897
|
+
return null;
|
|
170898
|
+
}
|
|
170899
|
+
return parsed;
|
|
170900
|
+
} catch {
|
|
170901
|
+
return null;
|
|
170902
|
+
}
|
|
170903
|
+
}
|
|
170904
|
+
function readRawWrapupState(db, sessionId) {
|
|
170905
|
+
const row = db.prepare("SELECT wrapup_in_progress_state FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
170906
|
+
return parseWrapupState(row?.wrapup_in_progress_state);
|
|
170907
|
+
}
|
|
170908
|
+
function getWrapupInProgressState(db, sessionId, now = Date.now()) {
|
|
170909
|
+
const state = readRawWrapupState(db, sessionId);
|
|
170910
|
+
if (!state)
|
|
170911
|
+
return null;
|
|
170912
|
+
if (state.expiresAt > now)
|
|
170913
|
+
return state;
|
|
170914
|
+
try {
|
|
170915
|
+
db.exec("BEGIN IMMEDIATE");
|
|
170916
|
+
} catch {
|
|
170917
|
+
return null;
|
|
170918
|
+
}
|
|
170919
|
+
let finished = false;
|
|
170920
|
+
try {
|
|
170921
|
+
const current = readRawWrapupState(db, sessionId);
|
|
170922
|
+
if (current && current.expiresAt <= now) {
|
|
170923
|
+
db.prepare("UPDATE session_meta SET wrapup_in_progress_state = NULL WHERE session_id = ?").run(sessionId);
|
|
170924
|
+
}
|
|
170925
|
+
db.exec("COMMIT");
|
|
170926
|
+
finished = true;
|
|
170927
|
+
} finally {
|
|
170928
|
+
if (!finished) {
|
|
170929
|
+
try {
|
|
170930
|
+
db.exec("ROLLBACK");
|
|
170931
|
+
} catch {}
|
|
170932
|
+
}
|
|
170933
|
+
}
|
|
170934
|
+
return null;
|
|
170935
|
+
}
|
|
170936
|
+
function isWrapupInProgress(db, sessionId, now = Date.now()) {
|
|
170937
|
+
return getWrapupInProgressState(db, sessionId, now) !== null;
|
|
170938
|
+
}
|
|
170939
|
+
function acquireWrapupInProgress(db, sessionId, state, now = Date.now()) {
|
|
170940
|
+
const acquiredAt = now;
|
|
170941
|
+
const next = {
|
|
170942
|
+
...state,
|
|
170943
|
+
acquiredAt,
|
|
170944
|
+
expiresAt: acquiredAt + WRAPUP_IN_PROGRESS_TTL_MS,
|
|
170945
|
+
updatedAt: acquiredAt
|
|
170946
|
+
};
|
|
170947
|
+
db.exec("BEGIN IMMEDIATE");
|
|
170948
|
+
let finished = false;
|
|
170949
|
+
try {
|
|
170950
|
+
ensureSessionMetaRow(db, sessionId);
|
|
170951
|
+
const current = readRawWrapupState(db, sessionId);
|
|
170952
|
+
if (current && current.expiresAt > now && current.holderId !== state.holderId) {
|
|
170953
|
+
db.exec("COMMIT");
|
|
170954
|
+
finished = true;
|
|
170955
|
+
return { ok: false, state: current };
|
|
170956
|
+
}
|
|
170957
|
+
db.prepare("UPDATE session_meta SET wrapup_in_progress_state = ? WHERE session_id = ?").run(stableStringify(next), sessionId);
|
|
170958
|
+
db.exec("COMMIT");
|
|
170959
|
+
finished = true;
|
|
170960
|
+
return { ok: true, state: next };
|
|
170961
|
+
} finally {
|
|
170962
|
+
if (!finished) {
|
|
170963
|
+
try {
|
|
170964
|
+
db.exec("ROLLBACK");
|
|
170965
|
+
} catch {}
|
|
170966
|
+
}
|
|
170967
|
+
}
|
|
170968
|
+
}
|
|
170969
|
+
function updateWrapupInProgress(db, sessionId, holderId, updates, now = Date.now()) {
|
|
170970
|
+
db.exec("BEGIN IMMEDIATE");
|
|
170971
|
+
let finished = false;
|
|
170972
|
+
try {
|
|
170973
|
+
const current = readRawWrapupState(db, sessionId);
|
|
170974
|
+
if (!current || current.holderId !== holderId || current.expiresAt <= now) {
|
|
170975
|
+
db.exec("ROLLBACK");
|
|
170976
|
+
finished = true;
|
|
170977
|
+
return null;
|
|
170978
|
+
}
|
|
170979
|
+
const next = {
|
|
170980
|
+
...current,
|
|
170981
|
+
...updates,
|
|
170982
|
+
holderId,
|
|
170983
|
+
expiresAt: now + WRAPUP_IN_PROGRESS_TTL_MS,
|
|
170984
|
+
updatedAt: now
|
|
170985
|
+
};
|
|
170986
|
+
db.prepare("UPDATE session_meta SET wrapup_in_progress_state = ? WHERE session_id = ?").run(stableStringify(next), sessionId);
|
|
170987
|
+
db.exec("COMMIT");
|
|
170988
|
+
finished = true;
|
|
170989
|
+
return next;
|
|
170990
|
+
} finally {
|
|
170991
|
+
if (!finished) {
|
|
170992
|
+
try {
|
|
170993
|
+
db.exec("ROLLBACK");
|
|
170994
|
+
} catch {}
|
|
170995
|
+
}
|
|
170996
|
+
}
|
|
170997
|
+
}
|
|
170998
|
+
function releaseWrapupInProgress(db, sessionId, holderId) {
|
|
170999
|
+
db.exec("BEGIN IMMEDIATE");
|
|
171000
|
+
let finished = false;
|
|
171001
|
+
try {
|
|
171002
|
+
const current = readRawWrapupState(db, sessionId);
|
|
171003
|
+
if (current?.holderId === holderId) {
|
|
171004
|
+
db.prepare("UPDATE session_meta SET wrapup_in_progress_state = NULL WHERE session_id = ?").run(sessionId);
|
|
171005
|
+
}
|
|
171006
|
+
db.exec("COMMIT");
|
|
171007
|
+
finished = true;
|
|
171008
|
+
} finally {
|
|
171009
|
+
if (!finished) {
|
|
171010
|
+
try {
|
|
171011
|
+
db.exec("ROLLBACK");
|
|
171012
|
+
} catch {}
|
|
171013
|
+
}
|
|
171014
|
+
}
|
|
171015
|
+
}
|
|
170737
171016
|
function protectedTailWindowBudget(usagePercentage, usable, perRunCap) {
|
|
170738
171017
|
if (usagePercentage >= 95)
|
|
170739
171018
|
return Math.min(1e6, Math.max(4 * perRunCap, Math.round(0.5 * usable)));
|
|
@@ -174219,6 +174498,147 @@ function searchMessages(args) {
|
|
|
174219
174498
|
role: entry.row.role
|
|
174220
174499
|
}));
|
|
174221
174500
|
}
|
|
174501
|
+
var NOTE_SEARCHABLE_STATUSES = ["active", "pending", "ready", "dismissed"];
|
|
174502
|
+
function noteSearchText(note) {
|
|
174503
|
+
const reason = note.readyReason?.trim();
|
|
174504
|
+
return reason ? `${note.content}
|
|
174505
|
+
Reason: ${reason}` : note.content;
|
|
174506
|
+
}
|
|
174507
|
+
function tokenizeKeywordNeedle(text) {
|
|
174508
|
+
const matches = text.toLowerCase().match(/[a-z0-9/._:-]+/g) ?? [];
|
|
174509
|
+
const seen = new Set;
|
|
174510
|
+
const tokens = [];
|
|
174511
|
+
for (const match of matches) {
|
|
174512
|
+
if (match.length <= 1 || !/[a-z0-9]/.test(match) || seen.has(match)) {
|
|
174513
|
+
continue;
|
|
174514
|
+
}
|
|
174515
|
+
seen.add(match);
|
|
174516
|
+
tokens.push(match);
|
|
174517
|
+
}
|
|
174518
|
+
return tokens;
|
|
174519
|
+
}
|
|
174520
|
+
function rankNotesForNeedle(notes, needle) {
|
|
174521
|
+
const normalizedNeedle = needle.trim().toLowerCase();
|
|
174522
|
+
if (normalizedNeedle.length === 0) {
|
|
174523
|
+
return [];
|
|
174524
|
+
}
|
|
174525
|
+
const needleTokens = tokenizeKeywordNeedle(normalizedNeedle);
|
|
174526
|
+
const ranked = [];
|
|
174527
|
+
for (const note of notes) {
|
|
174528
|
+
const text = noteSearchText(note);
|
|
174529
|
+
const normalizedText = text.toLowerCase();
|
|
174530
|
+
const noteTokens = new Set(tokenizeKeywordNeedle(normalizedText));
|
|
174531
|
+
const exact = normalizedText.includes(normalizedNeedle);
|
|
174532
|
+
const matchedTokens = needleTokens.filter((token) => noteTokens.has(token)).length;
|
|
174533
|
+
if (!exact && matchedTokens === 0) {
|
|
174534
|
+
continue;
|
|
174535
|
+
}
|
|
174536
|
+
const coverage = needleTokens.length > 0 ? matchedTokens / needleTokens.length : 0;
|
|
174537
|
+
const score = (exact ? 2 : 0) + coverage + (needleTokens.length > 1 && matchedTokens === needleTokens.length ? 0.5 : 0);
|
|
174538
|
+
ranked.push({ note, score, text });
|
|
174539
|
+
}
|
|
174540
|
+
return ranked.sort((left, right) => {
|
|
174541
|
+
if (right.score !== left.score) {
|
|
174542
|
+
return right.score - left.score;
|
|
174543
|
+
}
|
|
174544
|
+
if (right.note.createdAt !== left.note.createdAt) {
|
|
174545
|
+
return right.note.createdAt - left.note.createdAt;
|
|
174546
|
+
}
|
|
174547
|
+
return left.note.id - right.note.id;
|
|
174548
|
+
});
|
|
174549
|
+
}
|
|
174550
|
+
function searchNotes(args) {
|
|
174551
|
+
if (args.limit <= 0) {
|
|
174552
|
+
return [];
|
|
174553
|
+
}
|
|
174554
|
+
const notes = [
|
|
174555
|
+
...getNotes(args.db, {
|
|
174556
|
+
sessionId: args.sessionId,
|
|
174557
|
+
type: "session",
|
|
174558
|
+
status: NOTE_SEARCHABLE_STATUSES
|
|
174559
|
+
}),
|
|
174560
|
+
...getNotes(args.db, {
|
|
174561
|
+
projectPath: args.projectPath,
|
|
174562
|
+
type: "smart",
|
|
174563
|
+
status: NOTE_SEARCHABLE_STATUSES
|
|
174564
|
+
})
|
|
174565
|
+
];
|
|
174566
|
+
if (notes.length === 0) {
|
|
174567
|
+
return [];
|
|
174568
|
+
}
|
|
174569
|
+
const baseList = rankNotesForNeedle(notes, args.query);
|
|
174570
|
+
const probes = args.probes ?? [];
|
|
174571
|
+
if (probes.length === 0) {
|
|
174572
|
+
const ranked2 = baseList.slice(0, args.limit);
|
|
174573
|
+
return ranked2.map((entry, rank) => ({
|
|
174574
|
+
source: "note",
|
|
174575
|
+
content: previewText(entry.text),
|
|
174576
|
+
score: linearDecayScore(rank, ranked2.length),
|
|
174577
|
+
noteId: entry.note.id,
|
|
174578
|
+
status: entry.note.status,
|
|
174579
|
+
createdAt: entry.note.createdAt,
|
|
174580
|
+
anchorOrdinal: entry.note.anchorOrdinal,
|
|
174581
|
+
sourceSessionId: entry.note.sessionId
|
|
174582
|
+
}));
|
|
174583
|
+
}
|
|
174584
|
+
const queryLists = [];
|
|
174585
|
+
if (baseList.length > 0) {
|
|
174586
|
+
queryLists.push({ rows: baseList, weight: 1 });
|
|
174587
|
+
}
|
|
174588
|
+
const probeWeights = new Map;
|
|
174589
|
+
for (const probe of probes) {
|
|
174590
|
+
const rows = rankNotesForNeedle(notes, probe);
|
|
174591
|
+
if (rows.length === 0) {
|
|
174592
|
+
continue;
|
|
174593
|
+
}
|
|
174594
|
+
const weight = probeDiscriminationWeight(rows.length, notes.length);
|
|
174595
|
+
probeWeights.set(probe, weight);
|
|
174596
|
+
queryLists.push({ rows, weight });
|
|
174597
|
+
}
|
|
174598
|
+
const fused = new Map;
|
|
174599
|
+
for (const list of queryLists) {
|
|
174600
|
+
list.rows.forEach((row, rank) => {
|
|
174601
|
+
const rrf = list.weight / (RRF_K + rank);
|
|
174602
|
+
const existing = fused.get(row.note.id);
|
|
174603
|
+
if (existing) {
|
|
174604
|
+
existing.score += rrf;
|
|
174605
|
+
} else {
|
|
174606
|
+
fused.set(row.note.id, { entry: row, score: rrf });
|
|
174607
|
+
}
|
|
174608
|
+
});
|
|
174609
|
+
}
|
|
174610
|
+
for (const match of fused.values()) {
|
|
174611
|
+
let best = 0;
|
|
174612
|
+
for (const probe of probes) {
|
|
174613
|
+
const weight = probeWeights.get(probe) ?? 0;
|
|
174614
|
+
if (weight > best && containsProbeVerbatim(match.entry.text, [probe])) {
|
|
174615
|
+
best = weight;
|
|
174616
|
+
}
|
|
174617
|
+
}
|
|
174618
|
+
if (best > 0) {
|
|
174619
|
+
match.score += best * VERBATIM_RANK_BONUS;
|
|
174620
|
+
}
|
|
174621
|
+
}
|
|
174622
|
+
const ranked = [...fused.values()].sort((left, right) => {
|
|
174623
|
+
if (right.score !== left.score) {
|
|
174624
|
+
return right.score - left.score;
|
|
174625
|
+
}
|
|
174626
|
+
if (right.entry.note.createdAt !== left.entry.note.createdAt) {
|
|
174627
|
+
return right.entry.note.createdAt - left.entry.note.createdAt;
|
|
174628
|
+
}
|
|
174629
|
+
return left.entry.note.id - right.entry.note.id;
|
|
174630
|
+
}).slice(0, args.limit);
|
|
174631
|
+
return ranked.map((entry, rank) => ({
|
|
174632
|
+
source: "note",
|
|
174633
|
+
content: previewText(entry.entry.text),
|
|
174634
|
+
score: linearDecayScore(rank, ranked.length),
|
|
174635
|
+
noteId: entry.entry.note.id,
|
|
174636
|
+
status: entry.entry.note.status,
|
|
174637
|
+
createdAt: entry.entry.note.createdAt,
|
|
174638
|
+
anchorOrdinal: entry.entry.note.anchorOrdinal,
|
|
174639
|
+
sourceSessionId: entry.entry.note.sessionId
|
|
174640
|
+
}));
|
|
174641
|
+
}
|
|
174222
174642
|
function searchCompartmentChunks(args) {
|
|
174223
174643
|
if (!args.queryEmbedding || args.limit <= 0 || !args.modelId || args.modelId === "off")
|
|
174224
174644
|
return [];
|
|
@@ -174304,6 +174724,8 @@ function getSourceBoost(result) {
|
|
|
174304
174724
|
return GIT_COMMIT_SOURCE_BOOST;
|
|
174305
174725
|
case "primer":
|
|
174306
174726
|
return PRIMER_SOURCE_BOOST;
|
|
174727
|
+
case "note":
|
|
174728
|
+
return 1;
|
|
174307
174729
|
}
|
|
174308
174730
|
}
|
|
174309
174731
|
function compareUnifiedResults(left, right) {
|
|
@@ -174327,6 +174749,9 @@ function compareUnifiedResults(left, right) {
|
|
|
174327
174749
|
if (left.source === "primer" && right.source === "primer") {
|
|
174328
174750
|
return right.support - left.support || left.primerId - right.primerId;
|
|
174329
174751
|
}
|
|
174752
|
+
if (left.source === "note" && right.source === "note") {
|
|
174753
|
+
return right.createdAt - left.createdAt || left.noteId - right.noteId;
|
|
174754
|
+
}
|
|
174330
174755
|
return 0;
|
|
174331
174756
|
}
|
|
174332
174757
|
function toGitCommitResult(hit) {
|
|
@@ -174394,11 +174819,11 @@ function searchPrimers(args) {
|
|
|
174394
174819
|
}
|
|
174395
174820
|
function resolveSources(sources) {
|
|
174396
174821
|
if (sources === undefined) {
|
|
174397
|
-
return new Set(["memory", "message", "git_commit", "primer"]);
|
|
174822
|
+
return new Set(["memory", "message", "git_commit", "primer", "note"]);
|
|
174398
174823
|
}
|
|
174399
174824
|
const set2 = new Set;
|
|
174400
174825
|
for (const source of sources) {
|
|
174401
|
-
if (source === "memory" || source === "message" || source === "git_commit" || source === "primer") {
|
|
174826
|
+
if (source === "memory" || source === "message" || source === "git_commit" || source === "primer" || source === "note") {
|
|
174402
174827
|
set2.add(source);
|
|
174403
174828
|
}
|
|
174404
174829
|
}
|
|
@@ -174421,6 +174846,7 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
|
174421
174846
|
const runMessages = activeSources.has("message");
|
|
174422
174847
|
const runGitCommits = activeSources.has("git_commit") && gitCommitsEnabled;
|
|
174423
174848
|
const runPrimers = activeSources.has("primer") && memoryFeatureEnabled;
|
|
174849
|
+
const runNotes = activeSources.has("note");
|
|
174424
174850
|
const runCompartmentChunks = runMessages && memoryFeatureEnabled && embeddingEnabled;
|
|
174425
174851
|
const needsEmbedding = (runMemory || runGitCommits || runCompartmentChunks || runPrimers) && embeddingEnabled && isEmbeddingRuntimeEnabled();
|
|
174426
174852
|
const queryEmbeddingPromise = needsEmbedding ? embedQuery(trimmedQuery, options.signal).catch((error51) => {
|
|
@@ -174456,7 +174882,7 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
|
174456
174882
|
compartments: compartmentResults,
|
|
174457
174883
|
limit: tierLimit
|
|
174458
174884
|
});
|
|
174459
|
-
const [memoryResults, gitCommitResults, primerResults] = await Promise.all([
|
|
174885
|
+
const [memoryResults, gitCommitResults, primerResults, noteResults] = await Promise.all([
|
|
174460
174886
|
runMemory ? searchMemories({
|
|
174461
174887
|
db,
|
|
174462
174888
|
projectPath,
|
|
@@ -174483,9 +174909,23 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
|
174483
174909
|
limit: tierLimit,
|
|
174484
174910
|
queryEmbedding,
|
|
174485
174911
|
queryModelId: embeddingModelId && embeddingModelId !== "off" ? embeddingModelId : null
|
|
174912
|
+
})) : Promise.resolve([]),
|
|
174913
|
+
runNotes ? Promise.resolve(searchNotes({
|
|
174914
|
+
db,
|
|
174915
|
+
sessionId,
|
|
174916
|
+
projectPath,
|
|
174917
|
+
query: trimmedQuery,
|
|
174918
|
+
limit: tierLimit,
|
|
174919
|
+
probes: messageProbes
|
|
174486
174920
|
})) : Promise.resolve([])
|
|
174487
174921
|
]);
|
|
174488
|
-
const results = [
|
|
174922
|
+
const results = [
|
|
174923
|
+
...memoryResults,
|
|
174924
|
+
...primerResults,
|
|
174925
|
+
...messageLikeResults,
|
|
174926
|
+
...gitCommitResults,
|
|
174927
|
+
...noteResults
|
|
174928
|
+
].sort(compareUnifiedResults).slice(0, limit);
|
|
174489
174929
|
const countRetrievals = options.countRetrievals ?? true;
|
|
174490
174930
|
if (countRetrievals) {
|
|
174491
174931
|
const memoryIds = results.filter((result) => result.source === "memory").map((result) => result.memoryId);
|
|
@@ -175018,14 +175458,17 @@ Sources (omit for a broad search across all):
|
|
|
175018
175458
|
- memory: curated cross-session project knowledge — rules, constraints, conventions.
|
|
175019
175459
|
- message: the raw conversation behind your compacted history. Hits include message ordinals — expand the surrounding exchange with ctx_expand(start=N-10, end=N+5).
|
|
175020
175460
|
- git_commit: this repository's commit history.
|
|
175461
|
+
- note: parked decisions, follow-ups, and dismissed notes with their recorded text.
|
|
175021
175462
|
|
|
175022
175463
|
Picking sources:
|
|
175023
175464
|
- "when did this change / was this working before" → ["git_commit", "message"]
|
|
175024
175465
|
- "did we discuss this earlier" → ["message"]
|
|
175466
|
+
- "did we decide something about this / leave a follow-up" → ["note"]
|
|
175025
175467
|
- "what's our convention / rule for X" → ["memory"]`;
|
|
175026
175468
|
|
|
175027
175469
|
// src/tools/ctx-search.ts
|
|
175028
175470
|
var DEFAULT_LIMIT = 10;
|
|
175471
|
+
var NOTE_EXPAND_HINT = "Use ctx_expand(start=N-10, end=N) around any note @msg anchor above to read the surrounding conversation context.";
|
|
175029
175472
|
var ParamsSchema5 = exports_typebox.Object({
|
|
175030
175473
|
query: exports_typebox.String({
|
|
175031
175474
|
description: "Search query. Matches against memory content, Primers, git commit messages, and raw user/assistant message text."
|
|
@@ -175037,9 +175480,10 @@ var ParamsSchema5 = exports_typebox.Object({
|
|
|
175037
175480
|
exports_typebox.Literal("memory"),
|
|
175038
175481
|
exports_typebox.Literal("message"),
|
|
175039
175482
|
exports_typebox.Literal("git_commit"),
|
|
175040
|
-
exports_typebox.Literal("primer")
|
|
175483
|
+
exports_typebox.Literal("primer"),
|
|
175484
|
+
exports_typebox.Literal("note")
|
|
175041
175485
|
]), {
|
|
175042
|
-
description: 'Optional. Restrict to specific sources. Examples: ["primer"] for standing project explanations, ["git_commit"] for "when did we change X", ["memory"] for naming conventions, ["message"] for "did we discuss this earlier", ["git_commit","message"] for regression hunts. Omit for a broad search across all enabled sources.'
|
|
175486
|
+
description: 'Optional. Restrict to specific sources. Examples: ["primer"] for standing project explanations, ["git_commit"] for "when did we change X", ["memory"] for naming conventions, ["message"] for "did we discuss this earlier", ["note"] for parked decisions or follow-ups, ["git_commit","message"] for regression hunts. Omit for a broad search across all enabled sources.'
|
|
175043
175487
|
}))
|
|
175044
175488
|
});
|
|
175045
175489
|
function normalizeLimit3(limit) {
|
|
@@ -175066,7 +175510,7 @@ function formatAge(committedAtMs) {
|
|
|
175066
175510
|
const years = Math.floor(days / 365);
|
|
175067
175511
|
return years === 1 ? "1y ago" : `${years}y ago`;
|
|
175068
175512
|
}
|
|
175069
|
-
function formatResult(result, index) {
|
|
175513
|
+
function formatResult(result, index, currentSessionId) {
|
|
175070
175514
|
if (result.source === "memory") {
|
|
175071
175515
|
const source = result.sourceName ? ` source=${result.sourceName}` : "";
|
|
175072
175516
|
return [
|
|
@@ -175087,6 +175531,14 @@ function formatResult(result, index) {
|
|
|
175087
175531
|
`[${index}] [primer] score=${result.score.toFixed(2)} id=${result.primerId} support=${result.support} match=${result.matchType}`,
|
|
175088
175532
|
result.content
|
|
175089
175533
|
].join(`
|
|
175534
|
+
`);
|
|
175535
|
+
}
|
|
175536
|
+
if (result.source === "note") {
|
|
175537
|
+
const anchor = result.anchorOrdinal !== null && result.sourceSessionId === currentSessionId ? ` @msg ${result.anchorOrdinal}` : "";
|
|
175538
|
+
return [
|
|
175539
|
+
`[${index}] [note] score=${result.score.toFixed(2)} id=#${result.noteId} status=${result.status} ${formatAge(result.createdAt)}${anchor}`,
|
|
175540
|
+
result.content
|
|
175541
|
+
].join(`
|
|
175090
175542
|
`);
|
|
175091
175543
|
}
|
|
175092
175544
|
if (result.source === "compartment") {
|
|
@@ -175104,14 +175556,17 @@ function formatResult(result, index) {
|
|
|
175104
175556
|
].join(`
|
|
175105
175557
|
`);
|
|
175106
175558
|
}
|
|
175107
|
-
function formatSearchResults(query, results) {
|
|
175559
|
+
function formatSearchResults(query, results, currentSessionId) {
|
|
175108
175560
|
if (results.length === 0) {
|
|
175109
|
-
return `No results found for "${query}" across memories, primers, git commits, or message history.`;
|
|
175561
|
+
return `No results found for "${query}" across notes, memories, primers, git commits, or message history.`;
|
|
175110
175562
|
}
|
|
175111
|
-
const bodyParts = results.map((result, index) => formatResult(result, index + 1));
|
|
175563
|
+
const bodyParts = results.map((result, index) => formatResult(result, index + 1, currentSessionId));
|
|
175112
175564
|
if (results.some((result) => result.source === "message" || result.source === "compartment")) {
|
|
175113
175565
|
bodyParts.push("Use ctx_expand(start, end) with the range from any message result above to read the full conversation context.");
|
|
175114
175566
|
}
|
|
175567
|
+
if (results.some((result) => result.source === "note" && result.anchorOrdinal !== null && result.sourceSessionId === currentSessionId)) {
|
|
175568
|
+
bodyParts.push(NOTE_EXPAND_HINT);
|
|
175569
|
+
}
|
|
175115
175570
|
const body = bodyParts.join(`
|
|
175116
175571
|
|
|
175117
175572
|
`);
|
|
@@ -175160,7 +175615,12 @@ function createCtxSearchTool(deps) {
|
|
|
175160
175615
|
explicitSearch: true
|
|
175161
175616
|
});
|
|
175162
175617
|
return {
|
|
175163
|
-
content: [
|
|
175618
|
+
content: [
|
|
175619
|
+
{
|
|
175620
|
+
type: "text",
|
|
175621
|
+
text: formatSearchResults(query, results, sessionId)
|
|
175622
|
+
}
|
|
175623
|
+
],
|
|
175164
175624
|
details: undefined
|
|
175165
175625
|
};
|
|
175166
175626
|
}
|
|
@@ -175257,7 +175717,7 @@ function registerMagicContextTools(pi, opts) {
|
|
|
175257
175717
|
pi.registerTool(createCtxExpandTool({ db: opts.db }));
|
|
175258
175718
|
}
|
|
175259
175719
|
pi.registerTool(createTodowriteTool());
|
|
175260
|
-
if (opts.
|
|
175720
|
+
if (!opts.sessionScopedToolsDisabled) {
|
|
175261
175721
|
pi.registerTool(createCtxReduceTool({
|
|
175262
175722
|
db: opts.db,
|
|
175263
175723
|
protectedTags: opts.protectedTags ?? 20
|