@cortexkit/pi-magic-context 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.js +2231 -921
- package/dist/subagent-entry.js +626 -153
- package/package.json +6 -6
package/dist/subagent-entry.js
CHANGED
|
@@ -11277,7 +11277,7 @@ function finalize(ctx, schema) {
|
|
|
11277
11277
|
result.$schema = "http://json-schema.org/draft-07/schema#";
|
|
11278
11278
|
} else if (ctx.target === "draft-04") {
|
|
11279
11279
|
result.$schema = "http://json-schema.org/draft-04/schema#";
|
|
11280
|
-
} else if (ctx.target === "openapi-3.0") {}
|
|
11280
|
+
} else if (ctx.target === "openapi-3.0") {}
|
|
11281
11281
|
if (ctx.external?.uri) {
|
|
11282
11282
|
const id = ctx.external.registry.get(schema)?.id;
|
|
11283
11283
|
if (!id)
|
|
@@ -11538,7 +11538,7 @@ var formatMap, stringProcessor = (schema, ctx, _json, _params) => {
|
|
|
11538
11538
|
if (val === undefined) {
|
|
11539
11539
|
if (ctx.unrepresentable === "throw") {
|
|
11540
11540
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
11541
|
-
}
|
|
11541
|
+
}
|
|
11542
11542
|
} else if (typeof val === "bigint") {
|
|
11543
11543
|
if (ctx.unrepresentable === "throw") {
|
|
11544
11544
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -14891,10 +14891,63 @@ var init_magic_context = __esm(() => {
|
|
|
14891
14891
|
});
|
|
14892
14892
|
});
|
|
14893
14893
|
|
|
14894
|
-
// ../plugin/src/shared/
|
|
14895
|
-
|
|
14894
|
+
// ../plugin/src/shared/harness.ts
|
|
14895
|
+
function setHarness(value) {
|
|
14896
|
+
if (harnessLocked && currentHarness !== value) {
|
|
14897
|
+
throw new Error(`Magic Context: harness already locked to "${currentHarness}"; cannot change to "${value}"`);
|
|
14898
|
+
}
|
|
14899
|
+
currentHarness = value;
|
|
14900
|
+
harnessLocked = true;
|
|
14901
|
+
}
|
|
14902
|
+
function getHarness() {
|
|
14903
|
+
return currentHarness;
|
|
14904
|
+
}
|
|
14905
|
+
var currentHarness = "opencode", harnessLocked = false;
|
|
14906
|
+
|
|
14907
|
+
// ../plugin/src/shared/data-path.ts
|
|
14896
14908
|
import * as os from "node:os";
|
|
14897
14909
|
import * as path from "node:path";
|
|
14910
|
+
function getDataDir() {
|
|
14911
|
+
return process.env.XDG_DATA_HOME ?? path.join(os.homedir(), ".local", "share");
|
|
14912
|
+
}
|
|
14913
|
+
function getMagicContextTempDir(harness = getHarness()) {
|
|
14914
|
+
return path.join(os.tmpdir(), harness, "magic-context");
|
|
14915
|
+
}
|
|
14916
|
+
function getMagicContextLogPath(harness = getHarness()) {
|
|
14917
|
+
return path.join(getMagicContextTempDir(harness), "magic-context.log");
|
|
14918
|
+
}
|
|
14919
|
+
function getProjectMagicContextDir(directory) {
|
|
14920
|
+
return path.join(directory, ".opencode", "magic-context");
|
|
14921
|
+
}
|
|
14922
|
+
function getProjectMagicContextHistorianDir(directory) {
|
|
14923
|
+
return path.join(getProjectMagicContextDir(directory), "historian");
|
|
14924
|
+
}
|
|
14925
|
+
function getOpenCodeStorageDir() {
|
|
14926
|
+
return path.join(getDataDir(), "opencode", "storage");
|
|
14927
|
+
}
|
|
14928
|
+
function getMagicContextStorageDir() {
|
|
14929
|
+
return path.join(getDataDir(), "cortexkit", "magic-context");
|
|
14930
|
+
}
|
|
14931
|
+
function getLegacyOpenCodeMagicContextStorageDir() {
|
|
14932
|
+
return path.join(getOpenCodeStorageDir(), "plugin", "magic-context");
|
|
14933
|
+
}
|
|
14934
|
+
function getCacheDir() {
|
|
14935
|
+
return process.env.XDG_CACHE_HOME ?? path.join(os.homedir(), ".cache");
|
|
14936
|
+
}
|
|
14937
|
+
var init_data_path = () => {};
|
|
14938
|
+
|
|
14939
|
+
// ../plugin/src/shared/logger.ts
|
|
14940
|
+
import * as fs from "node:fs";
|
|
14941
|
+
import * as path2 from "node:path";
|
|
14942
|
+
function ensureDir(filePath) {
|
|
14943
|
+
const dir = path2.dirname(filePath);
|
|
14944
|
+
if (dir === lastEnsuredDir)
|
|
14945
|
+
return;
|
|
14946
|
+
try {
|
|
14947
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
14948
|
+
lastEnsuredDir = dir;
|
|
14949
|
+
} catch {}
|
|
14950
|
+
}
|
|
14898
14951
|
function flush() {
|
|
14899
14952
|
if (flushTimer) {
|
|
14900
14953
|
clearTimeout(flushTimer);
|
|
@@ -14905,6 +14958,8 @@ function flush() {
|
|
|
14905
14958
|
const data = buffer.join("");
|
|
14906
14959
|
buffer = [];
|
|
14907
14960
|
try {
|
|
14961
|
+
const logFile = getMagicContextLogPath();
|
|
14962
|
+
ensureDir(logFile);
|
|
14908
14963
|
fs.appendFileSync(logFile, data);
|
|
14909
14964
|
} catch {}
|
|
14910
14965
|
}
|
|
@@ -14935,9 +14990,9 @@ ${data.stack}` : ""}` : ` ${JSON.stringify(data)}`;
|
|
|
14935
14990
|
function sessionLog(sessionId, message, data) {
|
|
14936
14991
|
log(`[magic-context][${sessionId}] ${message}`, data);
|
|
14937
14992
|
}
|
|
14938
|
-
var
|
|
14993
|
+
var isTestEnv = false, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50, lastEnsuredDir = null;
|
|
14939
14994
|
var init_logger = __esm(() => {
|
|
14940
|
-
|
|
14995
|
+
init_data_path();
|
|
14941
14996
|
buffer = [];
|
|
14942
14997
|
if (!isTestEnv) {
|
|
14943
14998
|
process.on("exit", flush);
|
|
@@ -14961,30 +15016,47 @@ function cosineSimilarity(a, b) {
|
|
|
14961
15016
|
return denominator === 0 ? 0 : dotProduct / denominator;
|
|
14962
15017
|
}
|
|
14963
15018
|
|
|
14964
|
-
// ../plugin/src/
|
|
14965
|
-
import
|
|
14966
|
-
|
|
14967
|
-
|
|
14968
|
-
return process.env.XDG_DATA_HOME ?? path2.join(os2.homedir(), ".local", "share");
|
|
14969
|
-
}
|
|
14970
|
-
function getOpenCodeStorageDir() {
|
|
14971
|
-
return path2.join(getDataDir(), "opencode", "storage");
|
|
15019
|
+
// ../plugin/src/features/magic-context/memory/normalize-hash.ts
|
|
15020
|
+
import { createHash } from "node:crypto";
|
|
15021
|
+
function normalizeMemoryContent(content) {
|
|
15022
|
+
return content.toLowerCase().replace(/\s+/g, " ").trim();
|
|
14972
15023
|
}
|
|
14973
|
-
function
|
|
14974
|
-
|
|
15024
|
+
function computeNormalizedHash(content) {
|
|
15025
|
+
const normalized = normalizeMemoryContent(content);
|
|
15026
|
+
return createHash("md5").update(normalized).digest("hex");
|
|
14975
15027
|
}
|
|
14976
|
-
|
|
14977
|
-
|
|
15028
|
+
var init_normalize_hash = () => {};
|
|
15029
|
+
|
|
15030
|
+
// ../plugin/src/features/magic-context/memory/embedding-identity.ts
|
|
15031
|
+
function normalizeEndpoint(endpoint) {
|
|
15032
|
+
return endpoint?.trim().replace(/\/+$/, "") ?? "";
|
|
14978
15033
|
}
|
|
14979
|
-
function
|
|
14980
|
-
|
|
15034
|
+
function getEmbeddingProviderIdentity(config2) {
|
|
15035
|
+
if (config2.provider === "off") {
|
|
15036
|
+
return "embedding-provider:off";
|
|
15037
|
+
}
|
|
15038
|
+
const identityInput = config2.provider === "openai-compatible" ? {
|
|
15039
|
+
provider: "openai-compatible",
|
|
15040
|
+
model: config2.model.trim(),
|
|
15041
|
+
endpoint: normalizeEndpoint(config2.endpoint),
|
|
15042
|
+
apiKeyPresent: Boolean(config2.api_key?.trim())
|
|
15043
|
+
} : {
|
|
15044
|
+
provider: "local",
|
|
15045
|
+
model: config2.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
|
|
15046
|
+
endpoint: "",
|
|
15047
|
+
apiKeyPresent: false
|
|
15048
|
+
};
|
|
15049
|
+
return `embedding-provider:${computeNormalizedHash(JSON.stringify(identityInput))}`;
|
|
14981
15050
|
}
|
|
14982
|
-
var
|
|
15051
|
+
var init_embedding_identity = __esm(() => {
|
|
15052
|
+
init_magic_context();
|
|
15053
|
+
init_normalize_hash();
|
|
15054
|
+
});
|
|
14983
15055
|
|
|
14984
15056
|
// ../plugin/src/features/magic-context/memory/embedding-local.ts
|
|
14985
|
-
import { mkdirSync } from "node:fs";
|
|
15057
|
+
import { mkdirSync as mkdirSync2 } from "node:fs";
|
|
14986
15058
|
import { open, stat, unlink, writeFile } from "node:fs/promises";
|
|
14987
|
-
import { join as
|
|
15059
|
+
import { join as join2 } from "node:path";
|
|
14988
15060
|
async function acquireModelLoadLock(lockPath) {
|
|
14989
15061
|
const waitStart = Date.now();
|
|
14990
15062
|
while (true) {
|
|
@@ -15099,7 +15171,7 @@ class LocalEmbeddingProvider {
|
|
|
15099
15171
|
initPromise = null;
|
|
15100
15172
|
constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL) {
|
|
15101
15173
|
this.model = model;
|
|
15102
|
-
this.modelId =
|
|
15174
|
+
this.modelId = getEmbeddingProviderIdentity({ provider: "local", model });
|
|
15103
15175
|
}
|
|
15104
15176
|
async initialize() {
|
|
15105
15177
|
if (this.pipeline) {
|
|
@@ -15118,15 +15190,15 @@ class LocalEmbeddingProvider {
|
|
|
15118
15190
|
if (LogLevel && "ERROR" in LogLevel) {
|
|
15119
15191
|
env.logLevel = LogLevel.ERROR;
|
|
15120
15192
|
}
|
|
15121
|
-
const modelCacheDir =
|
|
15193
|
+
const modelCacheDir = join2(getMagicContextStorageDir(), "models");
|
|
15122
15194
|
try {
|
|
15123
|
-
|
|
15195
|
+
mkdirSync2(modelCacheDir, { recursive: true });
|
|
15124
15196
|
env.cacheDir = modelCacheDir;
|
|
15125
15197
|
} catch {
|
|
15126
15198
|
log("[magic-context] could not create model cache dir, using library default");
|
|
15127
15199
|
}
|
|
15128
15200
|
const createPipeline = transformersModule.pipeline;
|
|
15129
|
-
const lockPath =
|
|
15201
|
+
const lockPath = join2(modelCacheDir, ".load.lock");
|
|
15130
15202
|
const releaseLock = await acquireModelLoadLock(lockPath);
|
|
15131
15203
|
const stopHeartbeat = startLockHeartbeat(lockPath);
|
|
15132
15204
|
try {
|
|
@@ -15241,12 +15313,13 @@ var init_embedding_local = __esm(() => {
|
|
|
15241
15313
|
init_magic_context();
|
|
15242
15314
|
init_data_path();
|
|
15243
15315
|
init_logger();
|
|
15316
|
+
init_embedding_identity();
|
|
15244
15317
|
STALE_LOCK_MS = 3 * 60000;
|
|
15245
15318
|
MAX_LOCK_WAIT_MS = 5 * 60000;
|
|
15246
15319
|
});
|
|
15247
15320
|
|
|
15248
15321
|
// ../plugin/src/features/magic-context/memory/embedding-openai.ts
|
|
15249
|
-
function
|
|
15322
|
+
function normalizeEndpoint2(endpoint) {
|
|
15250
15323
|
return endpoint?.trim().replace(/\/+$/, "") ?? "";
|
|
15251
15324
|
}
|
|
15252
15325
|
|
|
@@ -15261,10 +15334,15 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
15261
15334
|
openLogged = false;
|
|
15262
15335
|
halfOpenProbeInFlight = false;
|
|
15263
15336
|
constructor(options) {
|
|
15264
|
-
this.endpoint =
|
|
15337
|
+
this.endpoint = normalizeEndpoint2(options.endpoint);
|
|
15265
15338
|
this.model = options.model?.trim() ?? "";
|
|
15266
15339
|
this.apiKey = options.apiKey?.trim() ?? "";
|
|
15267
|
-
this.modelId =
|
|
15340
|
+
this.modelId = getEmbeddingProviderIdentity({
|
|
15341
|
+
provider: "openai-compatible",
|
|
15342
|
+
endpoint: this.endpoint,
|
|
15343
|
+
model: this.model,
|
|
15344
|
+
...this.apiKey ? { api_key: this.apiKey } : {}
|
|
15345
|
+
});
|
|
15268
15346
|
}
|
|
15269
15347
|
async initialize() {
|
|
15270
15348
|
if (this.initialized)
|
|
@@ -15446,20 +15524,10 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
15446
15524
|
var FAILURE_THRESHOLD = 3, FAILURE_WINDOW_MS = 60000, OPEN_DURATION_MS, FETCH_TIMEOUT_MS = 30000;
|
|
15447
15525
|
var init_embedding_openai = __esm(() => {
|
|
15448
15526
|
init_logger();
|
|
15527
|
+
init_embedding_identity();
|
|
15449
15528
|
OPEN_DURATION_MS = 5 * 60000;
|
|
15450
15529
|
});
|
|
15451
15530
|
|
|
15452
|
-
// ../plugin/src/features/magic-context/memory/normalize-hash.ts
|
|
15453
|
-
import { createHash } from "node:crypto";
|
|
15454
|
-
function normalizeMemoryContent(content) {
|
|
15455
|
-
return content.toLowerCase().replace(/\s+/g, " ").trim();
|
|
15456
|
-
}
|
|
15457
|
-
function computeNormalizedHash(content) {
|
|
15458
|
-
const normalized = normalizeMemoryContent(content);
|
|
15459
|
-
return createHash("md5").update(normalized).digest("hex");
|
|
15460
|
-
}
|
|
15461
|
-
var init_normalize_hash = () => {};
|
|
15462
|
-
|
|
15463
15531
|
// ../plugin/src/features/magic-context/memory/storage-memory-embeddings.ts
|
|
15464
15532
|
var exports_storage_memory_embeddings = {};
|
|
15465
15533
|
__export(exports_storage_memory_embeddings, {
|
|
@@ -15604,17 +15672,8 @@ function resolveEmbeddingConfig(config2) {
|
|
|
15604
15672
|
}
|
|
15605
15673
|
return { provider: "off" };
|
|
15606
15674
|
}
|
|
15607
|
-
function
|
|
15608
|
-
|
|
15609
|
-
return "off";
|
|
15610
|
-
}
|
|
15611
|
-
if (config2.provider === "openai-compatible") {
|
|
15612
|
-
const endpoint = config2.endpoint.trim();
|
|
15613
|
-
const model = config2.model.trim();
|
|
15614
|
-
const keyHash = config2.api_key ? computeNormalizedHash(config2.api_key) : "nokey";
|
|
15615
|
-
return `openai-compat:${endpoint}:${model}:${keyHash}`;
|
|
15616
|
-
}
|
|
15617
|
-
return config2.model.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL;
|
|
15675
|
+
function resolveProviderIdentity(config2) {
|
|
15676
|
+
return getEmbeddingProviderIdentity(config2);
|
|
15618
15677
|
}
|
|
15619
15678
|
function createProvider(config2) {
|
|
15620
15679
|
if (config2.provider === "off") {
|
|
@@ -15638,10 +15697,10 @@ function getOrCreateProvider() {
|
|
|
15638
15697
|
}
|
|
15639
15698
|
function initializeEmbedding(config2) {
|
|
15640
15699
|
const nextConfig = resolveEmbeddingConfig(config2);
|
|
15641
|
-
const
|
|
15700
|
+
const nextProviderIdentity = resolveProviderIdentity(nextConfig);
|
|
15642
15701
|
const previousProvider = provider;
|
|
15643
|
-
const
|
|
15644
|
-
if (
|
|
15702
|
+
const previousProviderIdentity = previousProvider?.modelId ?? resolveProviderIdentity(embeddingConfig);
|
|
15703
|
+
if (previousProviderIdentity === nextProviderIdentity) {
|
|
15645
15704
|
embeddingConfig = nextConfig;
|
|
15646
15705
|
return;
|
|
15647
15706
|
}
|
|
@@ -15789,9 +15848,9 @@ var DEFAULT_EMBEDDING_CONFIG, embeddingConfig, provider = null, loadUnembeddedMe
|
|
|
15789
15848
|
var init_embedding = __esm(() => {
|
|
15790
15849
|
init_magic_context();
|
|
15791
15850
|
init_logger();
|
|
15851
|
+
init_embedding_identity();
|
|
15792
15852
|
init_embedding_local();
|
|
15793
15853
|
init_embedding_openai();
|
|
15794
|
-
init_normalize_hash();
|
|
15795
15854
|
init_storage_memory_embeddings();
|
|
15796
15855
|
DEFAULT_EMBEDDING_CONFIG = {
|
|
15797
15856
|
provider: "local",
|
|
@@ -15983,7 +16042,7 @@ var exports_native_binding = {};
|
|
|
15983
16042
|
__export(exports_native_binding, {
|
|
15984
16043
|
resolveBetterSqliteNativeBinding: () => resolveBetterSqliteNativeBinding
|
|
15985
16044
|
});
|
|
15986
|
-
import { existsSync, mkdirSync as
|
|
16045
|
+
import { existsSync, mkdirSync as mkdirSync3, writeFileSync } from "node:fs";
|
|
15987
16046
|
import { createRequire } from "node:module";
|
|
15988
16047
|
import * as path3 from "node:path";
|
|
15989
16048
|
function logInfo(message) {
|
|
@@ -16083,7 +16142,7 @@ async function resolveBetterSqliteNativeBinding() {
|
|
|
16083
16142
|
}
|
|
16084
16143
|
logWarn(`cached binary at ${cachedPath} has wrong ABI (${cachedProbe.actual ?? "unknown"} != ${expected}); refetching`);
|
|
16085
16144
|
}
|
|
16086
|
-
|
|
16145
|
+
mkdirSync3(path3.dirname(cachedPath), { recursive: true });
|
|
16087
16146
|
const nodeFileBytes = await downloadElectronPrebuild(pkgVersion, expected);
|
|
16088
16147
|
writeFileSync(cachedPath, nodeFileBytes);
|
|
16089
16148
|
logInfo(`cached Electron prebuild at ${cachedPath} (${(nodeFileBytes.length / 1024).toFixed(1)} KB)`);
|
|
@@ -23900,14 +23959,6 @@ var require_src2 = __commonJS((exports, module) => {
|
|
|
23900
23959
|
});
|
|
23901
23960
|
|
|
23902
23961
|
// ../plugin/src/hooks/magic-context/read-session-db.ts
|
|
23903
|
-
var exports_read_session_db = {};
|
|
23904
|
-
__export(exports_read_session_db, {
|
|
23905
|
-
withReadOnlySessionDb: () => withReadOnlySessionDb,
|
|
23906
|
-
getRawSessionMessageCountFromDb: () => getRawSessionMessageCountFromDb,
|
|
23907
|
-
getMessageTimesFromOpenCodeDb: () => getMessageTimesFromOpenCodeDb,
|
|
23908
|
-
findLastAssistantModelFromOpenCodeDb: () => findLastAssistantModelFromOpenCodeDb,
|
|
23909
|
-
closeReadOnlySessionDb: () => closeReadOnlySessionDb
|
|
23910
|
-
});
|
|
23911
23962
|
import { join as join8 } from "node:path";
|
|
23912
23963
|
function getOpenCodeDbPath() {
|
|
23913
23964
|
return join8(getDataDir(), "opencode", "opencode.db");
|
|
@@ -23937,9 +23988,6 @@ function getReadOnlySessionDb() {
|
|
|
23937
23988
|
function withReadOnlySessionDb(fn) {
|
|
23938
23989
|
return fn(getReadOnlySessionDb());
|
|
23939
23990
|
}
|
|
23940
|
-
function closeReadOnlySessionDb() {
|
|
23941
|
-
closeCachedReadOnlyDb();
|
|
23942
|
-
}
|
|
23943
23991
|
function getRawSessionMessageCountFromDb(db, sessionId) {
|
|
23944
23992
|
const row = db.prepare(`SELECT COUNT(*) as count FROM message WHERE session_id = ?
|
|
23945
23993
|
AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
|
|
@@ -23965,34 +24013,6 @@ function getMessageTimesFromOpenCodeDb(sessionId, messageIds) {
|
|
|
23965
24013
|
}
|
|
23966
24014
|
return result;
|
|
23967
24015
|
}
|
|
23968
|
-
function findLastAssistantModelFromOpenCodeDb(sessionId) {
|
|
23969
|
-
try {
|
|
23970
|
-
return withReadOnlySessionDb((db) => {
|
|
23971
|
-
const row = db.prepare(`SELECT json_extract(data, '$.providerID') as providerID,
|
|
23972
|
-
json_extract(data, '$.modelID') as modelID,
|
|
23973
|
-
json_extract(data, '$.agent') as agent
|
|
23974
|
-
FROM message
|
|
23975
|
-
WHERE session_id = ?
|
|
23976
|
-
AND json_extract(data, '$.role') = 'assistant'
|
|
23977
|
-
AND json_extract(data, '$.providerID') IS NOT NULL
|
|
23978
|
-
AND json_extract(data, '$.modelID') IS NOT NULL
|
|
23979
|
-
ORDER BY time_created DESC
|
|
23980
|
-
LIMIT 1`).get(sessionId);
|
|
23981
|
-
if (!row || typeof row.providerID !== "string" || typeof row.modelID !== "string") {
|
|
23982
|
-
return null;
|
|
23983
|
-
}
|
|
23984
|
-
const agent = typeof row.agent === "string" && row.agent.length > 0 ? row.agent : undefined;
|
|
23985
|
-
return {
|
|
23986
|
-
providerID: row.providerID,
|
|
23987
|
-
modelID: row.modelID,
|
|
23988
|
-
...agent ? { agent } : {}
|
|
23989
|
-
};
|
|
23990
|
-
});
|
|
23991
|
-
} catch (error51) {
|
|
23992
|
-
log("[magic-context] failed to recover live model from OpenCode DB:", error51);
|
|
23993
|
-
return null;
|
|
23994
|
-
}
|
|
23995
|
-
}
|
|
23996
24016
|
var cachedReadOnlyDb = null;
|
|
23997
24017
|
var init_read_session_db = __esm(async () => {
|
|
23998
24018
|
init_data_path();
|
|
@@ -24005,7 +24025,7 @@ init_embedding();
|
|
|
24005
24025
|
|
|
24006
24026
|
// ../plugin/src/features/magic-context/storage-db.ts
|
|
24007
24027
|
init_data_path();
|
|
24008
|
-
import { copyFileSync, cpSync, existsSync as
|
|
24028
|
+
import { copyFileSync, cpSync, existsSync as existsSync4, mkdirSync as mkdirSync4 } from "node:fs";
|
|
24009
24029
|
import { join as join6 } from "node:path";
|
|
24010
24030
|
|
|
24011
24031
|
// ../plugin/src/shared/error-message.ts
|
|
@@ -24088,6 +24108,119 @@ function safeString(value) {
|
|
|
24088
24108
|
init_logger();
|
|
24089
24109
|
await init_sqlite();
|
|
24090
24110
|
|
|
24111
|
+
// ../plugin/src/features/magic-context/key-files/project-key-files.ts
|
|
24112
|
+
init_logger();
|
|
24113
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
24114
|
+
import { existsSync as existsSync2, readFileSync, realpathSync } from "node:fs";
|
|
24115
|
+
import { join as join4, resolve, sep } from "node:path";
|
|
24116
|
+
var MISSING_CONTENT_HASH = "<missing>";
|
|
24117
|
+
var ORPHAN_GRACE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
24118
|
+
function sha256(input) {
|
|
24119
|
+
return createHash2("sha256").update(input).digest("hex");
|
|
24120
|
+
}
|
|
24121
|
+
function resolveProjectPath(directory) {
|
|
24122
|
+
if (!directory?.trim())
|
|
24123
|
+
return null;
|
|
24124
|
+
try {
|
|
24125
|
+
return realpathSync(directory);
|
|
24126
|
+
} catch {
|
|
24127
|
+
return resolve(directory);
|
|
24128
|
+
}
|
|
24129
|
+
}
|
|
24130
|
+
function rowToProjectKeyFile(row) {
|
|
24131
|
+
return {
|
|
24132
|
+
projectPath: String(row.project_path),
|
|
24133
|
+
path: String(row.path),
|
|
24134
|
+
content: String(row.content),
|
|
24135
|
+
contentHash: String(row.content_hash),
|
|
24136
|
+
localTokenEstimate: Number(row.local_token_estimate),
|
|
24137
|
+
generatedAt: Number(row.generated_at),
|
|
24138
|
+
generatedByModel: row.generated_by_model == null ? null : String(row.generated_by_model),
|
|
24139
|
+
generationConfigHash: String(row.generation_config_hash),
|
|
24140
|
+
staleReason: row.stale_reason == null ? null : String(row.stale_reason)
|
|
24141
|
+
};
|
|
24142
|
+
}
|
|
24143
|
+
function readCurrentKeyFiles(db, projectPath) {
|
|
24144
|
+
const resolvedProjectPath = resolveProjectPath(projectPath) ?? projectPath;
|
|
24145
|
+
const rows = db.prepare(`SELECT project_path, path, content, content_hash, local_token_estimate,
|
|
24146
|
+
generated_at, generated_by_model, generation_config_hash, stale_reason
|
|
24147
|
+
FROM project_key_files
|
|
24148
|
+
WHERE project_path = ?
|
|
24149
|
+
ORDER BY generated_at DESC, path ASC`).all(resolvedProjectPath);
|
|
24150
|
+
return rows.map(rowToProjectKeyFile);
|
|
24151
|
+
}
|
|
24152
|
+
function getKeyFilesVersion(db, projectPath) {
|
|
24153
|
+
const resolvedProjectPath = resolveProjectPath(projectPath) ?? projectPath;
|
|
24154
|
+
const row = db.prepare("SELECT version FROM project_key_files_version WHERE project_path = ?").get(resolvedProjectPath);
|
|
24155
|
+
return Number(row?.version ?? 0);
|
|
24156
|
+
}
|
|
24157
|
+
function bumpKeyFilesVersion(db, projectPath) {
|
|
24158
|
+
const resolvedProjectPath = resolveProjectPath(projectPath) ?? projectPath;
|
|
24159
|
+
db.prepare(`INSERT INTO project_key_files_version (project_path, version)
|
|
24160
|
+
VALUES (?, 1)
|
|
24161
|
+
ON CONFLICT(project_path) DO UPDATE SET version = version + 1`).run(resolvedProjectPath);
|
|
24162
|
+
return getKeyFilesVersion(db, resolvedProjectPath);
|
|
24163
|
+
}
|
|
24164
|
+
function resolveCommitFiles(projectPath, files) {
|
|
24165
|
+
return files.map((file2) => {
|
|
24166
|
+
try {
|
|
24167
|
+
const disk = readFileSync(join4(projectPath, file2.path));
|
|
24168
|
+
return {
|
|
24169
|
+
...file2,
|
|
24170
|
+
contentHash: sha256(disk),
|
|
24171
|
+
staleReason: null
|
|
24172
|
+
};
|
|
24173
|
+
} catch {
|
|
24174
|
+
return {
|
|
24175
|
+
...file2,
|
|
24176
|
+
contentHash: MISSING_CONTENT_HASH,
|
|
24177
|
+
staleReason: "missing"
|
|
24178
|
+
};
|
|
24179
|
+
}
|
|
24180
|
+
});
|
|
24181
|
+
}
|
|
24182
|
+
function insertResolvedKeyFiles(db, projectPath, files, generatedAt, generatedByModel, generationConfigHash) {
|
|
24183
|
+
const insert = db.prepare(`INSERT INTO project_key_files
|
|
24184
|
+
(project_path, path, content, content_hash, local_token_estimate,
|
|
24185
|
+
generated_at, generated_by_model, generation_config_hash, stale_reason)
|
|
24186
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
24187
|
+
for (const file2 of files) {
|
|
24188
|
+
insert.run(projectPath, file2.path, file2.content, file2.contentHash, file2.localTokenEstimate, generatedAt, generatedByModel, generationConfigHash, file2.staleReason);
|
|
24189
|
+
}
|
|
24190
|
+
}
|
|
24191
|
+
function deleteOrphanProjectKeyFiles(db, now = Date.now()) {
|
|
24192
|
+
const cutoff = now - ORPHAN_GRACE_MS;
|
|
24193
|
+
const rows = db.prepare(`SELECT project_path AS projectPath, MAX(generated_at) AS lastGen
|
|
24194
|
+
FROM project_key_files
|
|
24195
|
+
GROUP BY project_path
|
|
24196
|
+
HAVING lastGen < ?`).all(cutoff);
|
|
24197
|
+
let deletedProjects = 0;
|
|
24198
|
+
for (const row of rows) {
|
|
24199
|
+
if (existsSync2(row.projectPath))
|
|
24200
|
+
continue;
|
|
24201
|
+
try {
|
|
24202
|
+
db.prepare("DELETE FROM project_key_files WHERE project_path = ?").run(row.projectPath);
|
|
24203
|
+
db.prepare("DELETE FROM project_key_files_version WHERE project_path = ?").run(row.projectPath);
|
|
24204
|
+
deletedProjects++;
|
|
24205
|
+
} catch (error51) {
|
|
24206
|
+
log(`[key-files] orphan GC failed for ${row.projectPath}:`, error51);
|
|
24207
|
+
}
|
|
24208
|
+
}
|
|
24209
|
+
return deletedProjects;
|
|
24210
|
+
}
|
|
24211
|
+
function isRelativeProjectFile(projectPath, relativePath) {
|
|
24212
|
+
if (!relativePath || relativePath.startsWith("/") || relativePath.includes(".."))
|
|
24213
|
+
return false;
|
|
24214
|
+
try {
|
|
24215
|
+
const root = realpathSync(projectPath);
|
|
24216
|
+
const absPath = resolve(projectPath, relativePath);
|
|
24217
|
+
const real = realpathSync(absPath);
|
|
24218
|
+
return real.startsWith(root + sep) || real === root;
|
|
24219
|
+
} catch {
|
|
24220
|
+
return false;
|
|
24221
|
+
}
|
|
24222
|
+
}
|
|
24223
|
+
|
|
24091
24224
|
// ../plugin/src/features/magic-context/migrations.ts
|
|
24092
24225
|
init_logger();
|
|
24093
24226
|
var MIGRATIONS = [
|
|
@@ -24223,6 +24356,9 @@ var MIGRATIONS = [
|
|
|
24223
24356
|
embedding BLOB NOT NULL,
|
|
24224
24357
|
model_id TEXT NOT NULL,
|
|
24225
24358
|
created_at INTEGER NOT NULL,
|
|
24359
|
+
-- FK-cascade audit (v12): git_commit_embeddings.sha -> git_commits.sha
|
|
24360
|
+
-- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
|
|
24361
|
+
-- every connection and v12 cleans historical orphan rows.
|
|
24226
24362
|
FOREIGN KEY(sha) REFERENCES git_commits(sha) ON DELETE CASCADE
|
|
24227
24363
|
);
|
|
24228
24364
|
|
|
@@ -24362,6 +24498,69 @@ var MIGRATIONS = [
|
|
|
24362
24498
|
db.exec("ALTER TABLE session_meta ADD COLUMN todo_synthetic_state_json TEXT DEFAULT ''");
|
|
24363
24499
|
}
|
|
24364
24500
|
}
|
|
24501
|
+
},
|
|
24502
|
+
{
|
|
24503
|
+
version: 12,
|
|
24504
|
+
description: "Clean orphan rows from FK-cascade embedding tables",
|
|
24505
|
+
up: (db) => {
|
|
24506
|
+
const hasTable = (name) => Boolean(db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(name));
|
|
24507
|
+
const memoryEmbeddings = hasTable("memory_embeddings") ? db.prepare(`DELETE FROM memory_embeddings
|
|
24508
|
+
WHERE memory_id NOT IN (SELECT id FROM memories)`).run().changes : 0;
|
|
24509
|
+
log(`[migrations] v12 cleaned ${memoryEmbeddings} orphan memory_embeddings row(s)`);
|
|
24510
|
+
const gitCommitEmbeddings = hasTable("git_commit_embeddings") ? db.prepare(`DELETE FROM git_commit_embeddings
|
|
24511
|
+
WHERE sha NOT IN (SELECT sha FROM git_commits)`).run().changes : 0;
|
|
24512
|
+
log(`[migrations] v12 cleaned ${gitCommitEmbeddings} orphan git_commit_embeddings row(s)`);
|
|
24513
|
+
}
|
|
24514
|
+
},
|
|
24515
|
+
{
|
|
24516
|
+
version: 13,
|
|
24517
|
+
description: "Add pending_compaction_marker_state column for deferred marker drain",
|
|
24518
|
+
up: (db) => {
|
|
24519
|
+
const cols = db.prepare("PRAGMA table_info(session_meta)").all();
|
|
24520
|
+
if (!cols.some((c) => c.name === "pending_compaction_marker_state")) {
|
|
24521
|
+
db.exec("ALTER TABLE session_meta ADD COLUMN pending_compaction_marker_state TEXT");
|
|
24522
|
+
}
|
|
24523
|
+
}
|
|
24524
|
+
},
|
|
24525
|
+
{
|
|
24526
|
+
version: 14,
|
|
24527
|
+
description: "Add project-scoped key files and version counter",
|
|
24528
|
+
up: (db) => {
|
|
24529
|
+
db.exec(`
|
|
24530
|
+
CREATE TABLE IF NOT EXISTS project_key_files (
|
|
24531
|
+
project_path TEXT NOT NULL,
|
|
24532
|
+
path TEXT NOT NULL,
|
|
24533
|
+
content TEXT NOT NULL,
|
|
24534
|
+
content_hash TEXT NOT NULL,
|
|
24535
|
+
local_token_estimate INTEGER NOT NULL,
|
|
24536
|
+
generated_at INTEGER NOT NULL,
|
|
24537
|
+
generated_by_model TEXT,
|
|
24538
|
+
generation_config_hash TEXT NOT NULL,
|
|
24539
|
+
stale_reason TEXT,
|
|
24540
|
+
PRIMARY KEY (project_path, path)
|
|
24541
|
+
);
|
|
24542
|
+
|
|
24543
|
+
CREATE INDEX IF NOT EXISTS idx_project_key_files_project
|
|
24544
|
+
ON project_key_files(project_path);
|
|
24545
|
+
CREATE INDEX IF NOT EXISTS idx_project_key_files_generated_at
|
|
24546
|
+
ON project_key_files(project_path, generated_at);
|
|
24547
|
+
|
|
24548
|
+
CREATE TABLE IF NOT EXISTS project_key_files_version (
|
|
24549
|
+
project_path TEXT PRIMARY KEY,
|
|
24550
|
+
version INTEGER NOT NULL DEFAULT 0
|
|
24551
|
+
);
|
|
24552
|
+
`);
|
|
24553
|
+
}
|
|
24554
|
+
},
|
|
24555
|
+
{
|
|
24556
|
+
version: 15,
|
|
24557
|
+
description: "Add deferred_execute_state column for boundary execution drain",
|
|
24558
|
+
up: (db) => {
|
|
24559
|
+
const cols = db.prepare("PRAGMA table_info(session_meta)").all();
|
|
24560
|
+
if (!cols.some((c) => c.name === "deferred_execute_state")) {
|
|
24561
|
+
db.exec("ALTER TABLE session_meta ADD COLUMN deferred_execute_state TEXT");
|
|
24562
|
+
}
|
|
24563
|
+
}
|
|
24365
24564
|
}
|
|
24366
24565
|
];
|
|
24367
24566
|
function ensureMigrationsTable(db) {
|
|
@@ -157021,6 +157220,28 @@ function mergeCommitHashes(existing, next) {
|
|
|
157021
157220
|
return merged;
|
|
157022
157221
|
}
|
|
157023
157222
|
|
|
157223
|
+
// ../plugin/src/shared/stable-json.ts
|
|
157224
|
+
function stableStringify(value, seen = new WeakSet) {
|
|
157225
|
+
if (value === undefined)
|
|
157226
|
+
return "undefined";
|
|
157227
|
+
if (value === null || typeof value !== "object")
|
|
157228
|
+
return JSON.stringify(value) ?? String(value);
|
|
157229
|
+
if (seen.has(value))
|
|
157230
|
+
return '"[Circular]"';
|
|
157231
|
+
seen.add(value);
|
|
157232
|
+
if (Array.isArray(value)) {
|
|
157233
|
+
return `[${value.map((item) => stableStringify(item, seen)).join(",")}]`;
|
|
157234
|
+
}
|
|
157235
|
+
const entries = Object.entries(value).sort(([a], [b]) => {
|
|
157236
|
+
if (a < b)
|
|
157237
|
+
return -1;
|
|
157238
|
+
if (a > b)
|
|
157239
|
+
return 1;
|
|
157240
|
+
return 0;
|
|
157241
|
+
});
|
|
157242
|
+
return `{${entries.map(([key, child]) => `${JSON.stringify(key)}:${stableStringify(child, seen)}`).join(",")}}`;
|
|
157243
|
+
}
|
|
157244
|
+
|
|
157024
157245
|
// ../plugin/src/features/magic-context/tool-definition-tokens.ts
|
|
157025
157246
|
var measurements = new Map;
|
|
157026
157247
|
var fingerprints = new Map;
|
|
@@ -157055,7 +157276,7 @@ function loadToolDefinitionMeasurements(db) {
|
|
|
157055
157276
|
// ../plugin/src/features/magic-context/tool-owner-backfill.ts
|
|
157056
157277
|
init_data_path();
|
|
157057
157278
|
init_logger();
|
|
157058
|
-
import { existsSync as
|
|
157279
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
157059
157280
|
import { join as join5 } from "node:path";
|
|
157060
157281
|
var LEASE_DURATION_MS = 5 * 60 * 1000;
|
|
157061
157282
|
var LEASE_RENEWAL_MS = 60 * 1000;
|
|
@@ -157095,7 +157316,7 @@ function runToolOwnerBackfill(db) {
|
|
|
157095
157316
|
return result;
|
|
157096
157317
|
}
|
|
157097
157318
|
const opencodeDbPath = resolveOpencodeDbPath();
|
|
157098
|
-
if (!
|
|
157319
|
+
if (!existsSync3(opencodeDbPath)) {
|
|
157099
157320
|
log(`[backfill] OpenCode DB not found at ${opencodeDbPath} — marking all unbackfilled sessions as skipped. Lazy adoption (defense-in-depth) handles legacy rows at runtime.`);
|
|
157100
157321
|
markAllUnbackfilledSessionsSkipped(db);
|
|
157101
157322
|
result.sessionsSkippedNoOcDb = countSessionsByStatus(db, "skipped");
|
|
@@ -157306,18 +157527,18 @@ function resolveDatabasePath() {
|
|
|
157306
157527
|
return { dbDir, dbPath: join6(dbDir, "context.db") };
|
|
157307
157528
|
}
|
|
157308
157529
|
function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
157309
|
-
if (
|
|
157530
|
+
if (existsSync4(targetDbPath))
|
|
157310
157531
|
return;
|
|
157311
157532
|
const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
|
|
157312
157533
|
const legacyDbPath = join6(legacyDir, "context.db");
|
|
157313
|
-
if (!
|
|
157534
|
+
if (!existsSync4(legacyDbPath))
|
|
157314
157535
|
return;
|
|
157315
157536
|
log(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
|
|
157316
|
-
|
|
157537
|
+
mkdirSync4(targetDbDir, { recursive: true });
|
|
157317
157538
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
157318
157539
|
const src = `${legacyDbPath}${suffix}`;
|
|
157319
157540
|
const dst = join6(targetDbDir, `context.db${suffix}`);
|
|
157320
|
-
if (
|
|
157541
|
+
if (existsSync4(src)) {
|
|
157321
157542
|
try {
|
|
157322
157543
|
copyFileSync(src, dst);
|
|
157323
157544
|
} catch (error51) {
|
|
@@ -157327,7 +157548,7 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
|
157327
157548
|
}
|
|
157328
157549
|
const legacyModelsDir = join6(legacyDir, "models");
|
|
157329
157550
|
const targetModelsDir = join6(targetDbDir, "models");
|
|
157330
|
-
if (
|
|
157551
|
+
if (existsSync4(legacyModelsDir) && !existsSync4(targetModelsDir)) {
|
|
157331
157552
|
try {
|
|
157332
157553
|
cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
|
|
157333
157554
|
} catch (error51) {
|
|
@@ -157336,9 +157557,9 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
|
157336
157557
|
}
|
|
157337
157558
|
}
|
|
157338
157559
|
function initializeDatabase(db) {
|
|
157560
|
+
db.exec("PRAGMA foreign_keys=ON");
|
|
157339
157561
|
db.exec("PRAGMA journal_mode=WAL");
|
|
157340
157562
|
db.exec("PRAGMA busy_timeout=5000");
|
|
157341
|
-
db.exec("PRAGMA foreign_keys=ON");
|
|
157342
157563
|
db.exec(`
|
|
157343
157564
|
CREATE TABLE IF NOT EXISTS tags (
|
|
157344
157565
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -157436,6 +157657,9 @@ function initializeDatabase(db) {
|
|
|
157436
157657
|
);
|
|
157437
157658
|
|
|
157438
157659
|
CREATE TABLE IF NOT EXISTS memory_embeddings (
|
|
157660
|
+
-- FK-cascade audit (v12): memory_embeddings.memory_id -> memories.id
|
|
157661
|
+
-- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
|
|
157662
|
+
-- every connection and v12 cleans historical orphan rows.
|
|
157439
157663
|
memory_id INTEGER PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
|
|
157440
157664
|
embedding BLOB NOT NULL,
|
|
157441
157665
|
model_id TEXT
|
|
@@ -157472,6 +157696,26 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
157472
157696
|
);
|
|
157473
157697
|
CREATE INDEX IF NOT EXISTS idx_dream_runs_project ON dream_runs(project_path, finished_at DESC);
|
|
157474
157698
|
|
|
157699
|
+
CREATE TABLE IF NOT EXISTS project_key_files (
|
|
157700
|
+
project_path TEXT NOT NULL,
|
|
157701
|
+
path TEXT NOT NULL,
|
|
157702
|
+
content TEXT NOT NULL,
|
|
157703
|
+
content_hash TEXT NOT NULL,
|
|
157704
|
+
local_token_estimate INTEGER NOT NULL,
|
|
157705
|
+
generated_at INTEGER NOT NULL,
|
|
157706
|
+
generated_by_model TEXT,
|
|
157707
|
+
generation_config_hash TEXT NOT NULL,
|
|
157708
|
+
stale_reason TEXT,
|
|
157709
|
+
PRIMARY KEY (project_path, path)
|
|
157710
|
+
);
|
|
157711
|
+
CREATE INDEX IF NOT EXISTS idx_project_key_files_project ON project_key_files(project_path);
|
|
157712
|
+
CREATE INDEX IF NOT EXISTS idx_project_key_files_generated_at ON project_key_files(project_path, generated_at);
|
|
157713
|
+
|
|
157714
|
+
CREATE TABLE IF NOT EXISTS project_key_files_version (
|
|
157715
|
+
project_path TEXT PRIMARY KEY,
|
|
157716
|
+
version INTEGER NOT NULL DEFAULT 0
|
|
157717
|
+
);
|
|
157718
|
+
|
|
157475
157719
|
-- (smart_notes: see note above; merged into unified notes table by migration v1)
|
|
157476
157720
|
|
|
157477
157721
|
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
@@ -157543,7 +157787,17 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
157543
157787
|
system_prompt_hash TEXT DEFAULT '',
|
|
157544
157788
|
memory_block_cache TEXT DEFAULT '',
|
|
157545
157789
|
memory_block_count INTEGER DEFAULT 0,
|
|
157546
|
-
memory_block_ids TEXT DEFAULT ''
|
|
157790
|
+
memory_block_ids TEXT DEFAULT '',
|
|
157791
|
+
-- pending_compaction_marker_state: intentionally NULLABLE without a
|
|
157792
|
+
-- default. Absence of a deferred marker is SQL NULL; presence is a
|
|
157793
|
+
-- valid JSON blob written via setPendingCompactionMarkerState.
|
|
157794
|
+
-- Excluded from healNullTextColumns. Readers filter IS NOT NULL AND
|
|
157795
|
+
-- != empty-string defensively. Plan v6 section 3.
|
|
157796
|
+
pending_compaction_marker_state TEXT,
|
|
157797
|
+
-- deferred_execute_state: intentionally NULLABLE without a default.
|
|
157798
|
+
-- Absence is SQL NULL; presence is a JSON blob written via
|
|
157799
|
+
-- setDeferredExecutePendingIfAbsent. Excluded from healNullTextColumns.
|
|
157800
|
+
deferred_execute_state TEXT
|
|
157547
157801
|
);
|
|
157548
157802
|
|
|
157549
157803
|
CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
|
|
@@ -157631,6 +157885,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
157631
157885
|
ensureColumn(db, "session_meta", "recomp_partial_range_end", "INTEGER DEFAULT 0");
|
|
157632
157886
|
ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
|
|
157633
157887
|
ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
|
|
157888
|
+
ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
|
|
157889
|
+
ensureColumn(db, "session_meta", "deferred_execute_state", "TEXT");
|
|
157634
157890
|
ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
157635
157891
|
ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
157636
157892
|
ensureColumn(db, "source_contents", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
@@ -157707,7 +157963,15 @@ function ensureColumn(db, table, column, definition) {
|
|
|
157707
157963
|
if (rows.some((row) => row.name === column)) {
|
|
157708
157964
|
return;
|
|
157709
157965
|
}
|
|
157710
|
-
|
|
157966
|
+
try {
|
|
157967
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
|
|
157968
|
+
} catch (err) {
|
|
157969
|
+
const recheck = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
157970
|
+
if (recheck.some((row) => row.name === column)) {
|
|
157971
|
+
return;
|
|
157972
|
+
}
|
|
157973
|
+
throw err;
|
|
157974
|
+
}
|
|
157711
157975
|
}
|
|
157712
157976
|
function openDatabase() {
|
|
157713
157977
|
const { dbDir, dbPath } = resolveDatabasePath();
|
|
@@ -157720,10 +157984,15 @@ function openDatabase() {
|
|
|
157720
157984
|
}
|
|
157721
157985
|
try {
|
|
157722
157986
|
migrateLegacyStorageIfNeeded(dbPath, dbDir);
|
|
157723
|
-
|
|
157987
|
+
mkdirSync4(dbDir, { recursive: true });
|
|
157724
157988
|
const db = new Database(dbPath);
|
|
157725
157989
|
initializeDatabase(db);
|
|
157726
157990
|
runMigrations(db);
|
|
157991
|
+
try {
|
|
157992
|
+
deleteOrphanProjectKeyFiles(db);
|
|
157993
|
+
} catch (error51) {
|
|
157994
|
+
log(`[magic-context] key-files orphan GC failed: ${getErrorMessage(error51)}`);
|
|
157995
|
+
}
|
|
157727
157996
|
try {
|
|
157728
157997
|
runToolOwnerBackfill(db);
|
|
157729
157998
|
} catch (error51) {
|
|
@@ -157742,33 +158011,19 @@ function openDatabase() {
|
|
|
157742
158011
|
}
|
|
157743
158012
|
}
|
|
157744
158013
|
|
|
157745
|
-
// ../plugin/src/shared/harness.ts
|
|
157746
|
-
var currentHarness = "opencode";
|
|
157747
|
-
var harnessLocked = false;
|
|
157748
|
-
function setHarness(value) {
|
|
157749
|
-
if (harnessLocked && currentHarness !== value) {
|
|
157750
|
-
throw new Error(`Magic Context: harness already locked to "${currentHarness}"; cannot change to "${value}"`);
|
|
157751
|
-
}
|
|
157752
|
-
currentHarness = value;
|
|
157753
|
-
harnessLocked = true;
|
|
157754
|
-
}
|
|
157755
|
-
function getHarness() {
|
|
157756
|
-
return currentHarness;
|
|
157757
|
-
}
|
|
157758
|
-
|
|
157759
158014
|
// src/subagent-entry.ts
|
|
157760
158015
|
init_logger();
|
|
157761
158016
|
|
|
157762
158017
|
// src/config/index.ts
|
|
157763
158018
|
init_magic_context();
|
|
157764
|
-
import { existsSync as
|
|
158019
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "node:fs";
|
|
157765
158020
|
import { homedir as homedir3 } from "node:os";
|
|
157766
158021
|
import { join as join7 } from "node:path";
|
|
157767
158022
|
|
|
157768
158023
|
// ../plugin/src/config/variable.ts
|
|
157769
|
-
import { existsSync as
|
|
158024
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "node:fs";
|
|
157770
158025
|
import { homedir as homedir2 } from "node:os";
|
|
157771
|
-
import { dirname as
|
|
158026
|
+
import { dirname as dirname3, isAbsolute, resolve as resolve2 } from "node:path";
|
|
157772
158027
|
var ENV_PATTERN = /\{env:([^}]+)\}/g;
|
|
157773
158028
|
var FILE_PATTERN = /\{file:([^}]+)\}/g;
|
|
157774
158029
|
function substituteConfigVariables(input) {
|
|
@@ -157787,7 +158042,7 @@ function substituteConfigVariables(input) {
|
|
|
157787
158042
|
if (fileMatches.length === 0) {
|
|
157788
158043
|
return { text, warnings };
|
|
157789
158044
|
}
|
|
157790
|
-
const configDir = input.configPath ?
|
|
158045
|
+
const configDir = input.configPath ? dirname3(input.configPath) : process.cwd();
|
|
157791
158046
|
let output = "";
|
|
157792
158047
|
let cursor = 0;
|
|
157793
158048
|
for (const match of fileMatches) {
|
|
@@ -157805,17 +158060,17 @@ function substituteConfigVariables(input) {
|
|
|
157805
158060
|
}
|
|
157806
158061
|
let filePath = rawPath.trim();
|
|
157807
158062
|
if (filePath.startsWith("~/")) {
|
|
157808
|
-
filePath =
|
|
158063
|
+
filePath = resolve2(homedir2(), filePath.slice(2));
|
|
157809
158064
|
} else if (!isAbsolute(filePath)) {
|
|
157810
|
-
filePath =
|
|
158065
|
+
filePath = resolve2(configDir, filePath);
|
|
157811
158066
|
}
|
|
157812
|
-
if (!
|
|
158067
|
+
if (!existsSync5(filePath)) {
|
|
157813
158068
|
warnings.push(`File not found for ${token} (resolved to ${filePath}); using empty string`);
|
|
157814
158069
|
continue;
|
|
157815
158070
|
}
|
|
157816
158071
|
let contents;
|
|
157817
158072
|
try {
|
|
157818
|
-
contents =
|
|
158073
|
+
contents = readFileSync2(filePath, "utf-8").trim();
|
|
157819
158074
|
} catch (error51) {
|
|
157820
158075
|
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
157821
158076
|
warnings.push(`Failed to read file for ${token} (${filePath}): ${message}; using empty string`);
|
|
@@ -157840,11 +158095,11 @@ function getUserConfigPaths() {
|
|
|
157840
158095
|
return [`${basePath}.jsonc`, `${basePath}.json`];
|
|
157841
158096
|
}
|
|
157842
158097
|
function resolveFirstExisting(paths) {
|
|
157843
|
-
return paths.find((path4) =>
|
|
158098
|
+
return paths.find((path4) => existsSync6(path4));
|
|
157844
158099
|
}
|
|
157845
158100
|
function loadConfigFile(path4, scope) {
|
|
157846
158101
|
try {
|
|
157847
|
-
const rawText =
|
|
158102
|
+
const rawText = readFileSync3(path4, "utf-8");
|
|
157848
158103
|
const substituted = substituteConfigVariables({
|
|
157849
158104
|
text: rawText,
|
|
157850
158105
|
configPath: path4
|
|
@@ -162453,6 +162708,61 @@ __export(exports_typebox, {
|
|
|
162453
162708
|
Any: () => Any
|
|
162454
162709
|
});
|
|
162455
162710
|
// src/read-session-pi.ts
|
|
162711
|
+
function isMidTurnPi(event, _sessionId) {
|
|
162712
|
+
const messages = event?.messages;
|
|
162713
|
+
if (!Array.isArray(messages))
|
|
162714
|
+
return false;
|
|
162715
|
+
let latestAssistantIndex = -1;
|
|
162716
|
+
let latestAssistant = null;
|
|
162717
|
+
for (let i = messages.length - 1;i >= 0; i--) {
|
|
162718
|
+
const msg = messages[i];
|
|
162719
|
+
if (msg !== null && typeof msg === "object") {
|
|
162720
|
+
const record4 = msg;
|
|
162721
|
+
if (record4.role === "assistant") {
|
|
162722
|
+
latestAssistantIndex = i;
|
|
162723
|
+
latestAssistant = record4;
|
|
162724
|
+
break;
|
|
162725
|
+
}
|
|
162726
|
+
}
|
|
162727
|
+
}
|
|
162728
|
+
if (latestAssistant === null)
|
|
162729
|
+
return false;
|
|
162730
|
+
if (latestAssistant.stopReason === "toolUse")
|
|
162731
|
+
return true;
|
|
162732
|
+
const toolCallIds = getToolCallIds(latestAssistant.content);
|
|
162733
|
+
if (toolCallIds.size === 0)
|
|
162734
|
+
return false;
|
|
162735
|
+
const pairedToolResultIds = new Set;
|
|
162736
|
+
for (const msg of messages.slice(latestAssistantIndex + 1)) {
|
|
162737
|
+
if (msg === null || typeof msg !== "object")
|
|
162738
|
+
continue;
|
|
162739
|
+
const record4 = msg;
|
|
162740
|
+
if (record4.role !== "toolResult")
|
|
162741
|
+
continue;
|
|
162742
|
+
if (typeof record4.toolCallId === "string") {
|
|
162743
|
+
pairedToolResultIds.add(record4.toolCallId);
|
|
162744
|
+
}
|
|
162745
|
+
}
|
|
162746
|
+
for (const id of toolCallIds) {
|
|
162747
|
+
if (!pairedToolResultIds.has(id))
|
|
162748
|
+
return true;
|
|
162749
|
+
}
|
|
162750
|
+
return false;
|
|
162751
|
+
}
|
|
162752
|
+
function getToolCallIds(content) {
|
|
162753
|
+
const ids = new Set;
|
|
162754
|
+
if (!Array.isArray(content))
|
|
162755
|
+
return ids;
|
|
162756
|
+
for (const item of content) {
|
|
162757
|
+
if (item === null || typeof item !== "object")
|
|
162758
|
+
continue;
|
|
162759
|
+
const record4 = item;
|
|
162760
|
+
if (record4.type === "toolCall" && typeof record4.id === "string") {
|
|
162761
|
+
ids.add(record4.id);
|
|
162762
|
+
}
|
|
162763
|
+
}
|
|
162764
|
+
return ids;
|
|
162765
|
+
}
|
|
162456
162766
|
function readPiSessionMessages(ctx) {
|
|
162457
162767
|
const sm = ctx.sessionManager;
|
|
162458
162768
|
if (sm === undefined)
|
|
@@ -162474,6 +162784,7 @@ function convertEntriesToRawMessages(entries) {
|
|
|
162474
162784
|
const result = [];
|
|
162475
162785
|
let nextOrdinal = 1;
|
|
162476
162786
|
let pendingToolParts = [];
|
|
162787
|
+
let pendingFirstRealId = "";
|
|
162477
162788
|
for (const entry of entries) {
|
|
162478
162789
|
if (!isMessageEntry(entry)) {
|
|
162479
162790
|
continue;
|
|
@@ -162482,6 +162793,9 @@ function convertEntriesToRawMessages(entries) {
|
|
|
162482
162793
|
const role = msg.role;
|
|
162483
162794
|
if (role === "toolResult") {
|
|
162484
162795
|
pendingToolParts.push(...synthesizeToolResultParts(msg));
|
|
162796
|
+
if (pendingFirstRealId === "") {
|
|
162797
|
+
pendingFirstRealId = entry.id;
|
|
162798
|
+
}
|
|
162485
162799
|
continue;
|
|
162486
162800
|
}
|
|
162487
162801
|
if (role === "user") {
|
|
@@ -162490,6 +162804,7 @@ function convertEntriesToRawMessages(entries) {
|
|
|
162490
162804
|
...synthesizeUserParts(msg)
|
|
162491
162805
|
];
|
|
162492
162806
|
pendingToolParts = [];
|
|
162807
|
+
pendingFirstRealId = "";
|
|
162493
162808
|
result.push({
|
|
162494
162809
|
ordinal: nextOrdinal++,
|
|
162495
162810
|
id: entry.id,
|
|
@@ -162502,11 +162817,12 @@ function convertEntriesToRawMessages(entries) {
|
|
|
162502
162817
|
if (pendingToolParts.length > 0) {
|
|
162503
162818
|
result.push({
|
|
162504
162819
|
ordinal: nextOrdinal++,
|
|
162505
|
-
id:
|
|
162820
|
+
id: pendingFirstRealId,
|
|
162506
162821
|
role: "user",
|
|
162507
162822
|
parts: pendingToolParts
|
|
162508
162823
|
});
|
|
162509
162824
|
pendingToolParts = [];
|
|
162825
|
+
pendingFirstRealId = "";
|
|
162510
162826
|
}
|
|
162511
162827
|
result.push({
|
|
162512
162828
|
ordinal: nextOrdinal++,
|
|
@@ -162526,7 +162842,7 @@ function convertEntriesToRawMessages(entries) {
|
|
|
162526
162842
|
if (pendingToolParts.length > 0) {
|
|
162527
162843
|
result.push({
|
|
162528
162844
|
ordinal: nextOrdinal,
|
|
162529
|
-
id:
|
|
162845
|
+
id: pendingFirstRealId,
|
|
162530
162846
|
role: "user",
|
|
162531
162847
|
parts: pendingToolParts
|
|
162532
162848
|
});
|
|
@@ -163027,7 +163343,7 @@ init_normalize_hash();
|
|
|
163027
163343
|
|
|
163028
163344
|
// ../plugin/src/features/magic-context/memory/project-identity.ts
|
|
163029
163345
|
import { execSync } from "node:child_process";
|
|
163030
|
-
import { createHash as
|
|
163346
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
163031
163347
|
import path4 from "node:path";
|
|
163032
163348
|
var GIT_TIMEOUT_MS = 5000;
|
|
163033
163349
|
var resolvedCache = new Map;
|
|
@@ -163048,7 +163364,7 @@ function getRootCommitHash(directory) {
|
|
|
163048
163364
|
}
|
|
163049
163365
|
function directoryFallback(directory) {
|
|
163050
163366
|
const canonical = path4.resolve(directory);
|
|
163051
|
-
const hash2 =
|
|
163367
|
+
const hash2 = createHash3("md5").update(canonical).digest("hex").slice(0, 12);
|
|
163052
163368
|
return `dir:${hash2}`;
|
|
163053
163369
|
}
|
|
163054
163370
|
function resolveProjectIdentity(directory) {
|
|
@@ -164113,6 +164429,12 @@ function isPersistedNoteNudgeRow(row) {
|
|
|
164113
164429
|
const r = row;
|
|
164114
164430
|
return typeof r.note_nudge_trigger_pending === "number" && typeof r.note_nudge_trigger_message_id === "string" && typeof r.note_nudge_sticky_text === "string" && typeof r.note_nudge_sticky_message_id === "string";
|
|
164115
164431
|
}
|
|
164432
|
+
function isPersistedTodoSyntheticAnchorRow(row) {
|
|
164433
|
+
if (row === null || typeof row !== "object")
|
|
164434
|
+
return false;
|
|
164435
|
+
const r = row;
|
|
164436
|
+
return typeof r.todo_synthetic_call_id === "string" && typeof r.todo_synthetic_anchor_message_id === "string" && typeof r.todo_synthetic_state_json === "string";
|
|
164437
|
+
}
|
|
164116
164438
|
function isPersistedHistorianFailureRow(row) {
|
|
164117
164439
|
if (row === null || typeof row !== "object")
|
|
164118
164440
|
return false;
|
|
@@ -164189,6 +164511,29 @@ function setPersistedDeliveredNoteNudge(db, sessionId, text, messageId = "") {
|
|
|
164189
164511
|
function clearPersistedNoteNudge(db, sessionId) {
|
|
164190
164512
|
db.prepare("UPDATE session_meta SET note_nudge_trigger_pending = 0, note_nudge_trigger_message_id = '', note_nudge_sticky_text = '', note_nudge_sticky_message_id = '' WHERE session_id = ?").run(sessionId);
|
|
164191
164513
|
}
|
|
164514
|
+
function getPersistedTodoSyntheticAnchor(db, sessionId) {
|
|
164515
|
+
const result = db.prepare("SELECT todo_synthetic_call_id, todo_synthetic_anchor_message_id, todo_synthetic_state_json FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
164516
|
+
if (!isPersistedTodoSyntheticAnchorRow(result)) {
|
|
164517
|
+
return null;
|
|
164518
|
+
}
|
|
164519
|
+
if (result.todo_synthetic_call_id.length === 0 || result.todo_synthetic_anchor_message_id.length === 0) {
|
|
164520
|
+
return null;
|
|
164521
|
+
}
|
|
164522
|
+
return {
|
|
164523
|
+
callId: result.todo_synthetic_call_id,
|
|
164524
|
+
messageId: result.todo_synthetic_anchor_message_id,
|
|
164525
|
+
stateJson: result.todo_synthetic_state_json
|
|
164526
|
+
};
|
|
164527
|
+
}
|
|
164528
|
+
function setPersistedTodoSyntheticAnchor(db, sessionId, callId, messageId, stateJson) {
|
|
164529
|
+
db.transaction(() => {
|
|
164530
|
+
ensureSessionMetaRow(db, sessionId);
|
|
164531
|
+
db.prepare("UPDATE session_meta SET todo_synthetic_call_id = ?, todo_synthetic_anchor_message_id = ?, todo_synthetic_state_json = ? WHERE session_id = ?").run(callId, messageId, stateJson, sessionId);
|
|
164532
|
+
})();
|
|
164533
|
+
}
|
|
164534
|
+
function clearPersistedTodoSyntheticAnchor(db, sessionId) {
|
|
164535
|
+
db.prepare("UPDATE session_meta SET todo_synthetic_call_id = '', todo_synthetic_anchor_message_id = '', todo_synthetic_state_json = '' WHERE session_id = ?").run(sessionId);
|
|
164536
|
+
}
|
|
164192
164537
|
function getNoteLastReadAt(db, sessionId) {
|
|
164193
164538
|
try {
|
|
164194
164539
|
const result = db.prepare("SELECT note_last_read_at FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
@@ -164293,6 +164638,52 @@ function setStrippedPlaceholderIds(db, sessionId, ids) {
|
|
|
164293
164638
|
const json2 = ids.size > 0 ? JSON.stringify([...ids]) : "";
|
|
164294
164639
|
db.prepare("UPDATE session_meta SET stripped_placeholder_ids = ? WHERE session_id = ?").run(json2, sessionId);
|
|
164295
164640
|
}
|
|
164641
|
+
function isPendingCompactionMarker(value) {
|
|
164642
|
+
return typeof value === "object" && value !== null && typeof value.ordinal === "number" && typeof value.endMessageId === "string" && typeof value.publishedAt === "number";
|
|
164643
|
+
}
|
|
164644
|
+
function getPendingCompactionMarkerState(db, sessionId) {
|
|
164645
|
+
const row = db.prepare("SELECT pending_compaction_marker_state FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
164646
|
+
const raw = row?.pending_compaction_marker_state;
|
|
164647
|
+
if (raw === null || raw === undefined || raw === "")
|
|
164648
|
+
return null;
|
|
164649
|
+
try {
|
|
164650
|
+
const parsed = JSON.parse(raw);
|
|
164651
|
+
if (isPendingCompactionMarker(parsed)) {
|
|
164652
|
+
return parsed;
|
|
164653
|
+
}
|
|
164654
|
+
} catch {}
|
|
164655
|
+
return null;
|
|
164656
|
+
}
|
|
164657
|
+
function clearPendingCompactionMarkerStateIf(db, sessionId, expected) {
|
|
164658
|
+
const expectedBlob = stableStringify(expected);
|
|
164659
|
+
const result = db.prepare(`UPDATE session_meta SET pending_compaction_marker_state = NULL
|
|
164660
|
+
WHERE session_id = ? AND pending_compaction_marker_state = ?`).run(sessionId, expectedBlob);
|
|
164661
|
+
return result.changes > 0;
|
|
164662
|
+
}
|
|
164663
|
+
function peekDeferredExecutePending(db, sessionId) {
|
|
164664
|
+
const row = db.prepare("SELECT deferred_execute_state FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
164665
|
+
const raw = row?.deferred_execute_state;
|
|
164666
|
+
if (raw === null || raw === undefined || raw === "")
|
|
164667
|
+
return null;
|
|
164668
|
+
try {
|
|
164669
|
+
return JSON.parse(raw);
|
|
164670
|
+
} catch {
|
|
164671
|
+
return null;
|
|
164672
|
+
}
|
|
164673
|
+
}
|
|
164674
|
+
function setDeferredExecutePendingIfAbsent(db, sessionId, payload) {
|
|
164675
|
+
ensureSessionMetaRow(db, sessionId);
|
|
164676
|
+
const payloadBlob = stableStringify(payload);
|
|
164677
|
+
const result = db.prepare(`UPDATE session_meta SET deferred_execute_state = ?
|
|
164678
|
+
WHERE session_id = ? AND deferred_execute_state IS NULL`).run(payloadBlob, sessionId);
|
|
164679
|
+
return result.changes > 0;
|
|
164680
|
+
}
|
|
164681
|
+
function clearDeferredExecutePendingIfMatches(db, sessionId, expected) {
|
|
164682
|
+
const expectedBlob = stableStringify(expected);
|
|
164683
|
+
const result = db.prepare(`UPDATE session_meta SET deferred_execute_state = NULL
|
|
164684
|
+
WHERE session_id = ? AND deferred_execute_state = ?`).run(sessionId, expectedBlob);
|
|
164685
|
+
return result.changes > 0;
|
|
164686
|
+
}
|
|
164296
164687
|
// ../plugin/src/features/magic-context/resolve-subagent-fallback.ts
|
|
164297
164688
|
init_logger();
|
|
164298
164689
|
await init_read_session_db();
|
|
@@ -166209,16 +166600,24 @@ function trimMemoriesToBudget(sessionId, memories, budgetTokens) {
|
|
|
166209
166600
|
function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectPath, injectionBudgetTokens, temporalAwareness) {
|
|
166210
166601
|
const cached2 = injectionCache.get(sessionId);
|
|
166211
166602
|
if (!isCacheBusting && cached2) {
|
|
166212
|
-
if (cached2.
|
|
166213
|
-
|
|
166214
|
-
|
|
166215
|
-
|
|
166216
|
-
|
|
166217
|
-
|
|
166218
|
-
|
|
166603
|
+
if (cached2.kind === "empty") {
|
|
166604
|
+
return null;
|
|
166605
|
+
}
|
|
166606
|
+
const prepared = cached2.injection;
|
|
166607
|
+
if (prepared.compartmentEndMessageId === null) {
|
|
166608
|
+
sessionLog(sessionId, "compartment injection cache in degraded mode (null boundary), forcing rebuild");
|
|
166609
|
+
} else {
|
|
166610
|
+
if (prepared.compartmentEndMessageId.length > 0) {
|
|
166611
|
+
const cutoffIndex2 = messages.findIndex((message) => message.info.id === prepared.compartmentEndMessageId);
|
|
166612
|
+
if (cutoffIndex2 >= 0) {
|
|
166613
|
+
const remaining = messages.slice(cutoffIndex2 + 1);
|
|
166614
|
+
messages.splice(0, messages.length, ...remaining);
|
|
166615
|
+
} else {
|
|
166616
|
+
sessionLog(sessionId, `compartment injection: cached boundary ${prepared.compartmentEndMessageId} not in messages (already trimmed), reusing cache`);
|
|
166617
|
+
}
|
|
166219
166618
|
}
|
|
166619
|
+
return { ...prepared, rebuiltFromDb: false };
|
|
166220
166620
|
}
|
|
166221
|
-
return cached2;
|
|
166222
166621
|
}
|
|
166223
166622
|
const compartments = getCompartments(db, sessionId);
|
|
166224
166623
|
const facts = getSessionFacts(db, sessionId);
|
|
@@ -166250,7 +166649,11 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
|
|
|
166250
166649
|
}
|
|
166251
166650
|
}
|
|
166252
166651
|
if (compartments.length === 0 && facts.length === 0 && !memoryBlock) {
|
|
166253
|
-
injectionCache.
|
|
166652
|
+
injectionCache.set(sessionId, {
|
|
166653
|
+
kind: "empty",
|
|
166654
|
+
compartmentEndMessageId: "",
|
|
166655
|
+
renderedBytes: 0
|
|
166656
|
+
});
|
|
166254
166657
|
return null;
|
|
166255
166658
|
}
|
|
166256
166659
|
let dateRanges;
|
|
@@ -166283,9 +166686,10 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
|
|
|
166283
166686
|
compartmentCount: 0,
|
|
166284
166687
|
skippedVisibleMessages: 0,
|
|
166285
166688
|
factCount: facts.length,
|
|
166286
|
-
memoryCount
|
|
166689
|
+
memoryCount,
|
|
166690
|
+
rebuiltFromDb: true
|
|
166287
166691
|
};
|
|
166288
|
-
injectionCache.set(sessionId, result2);
|
|
166692
|
+
injectionCache.set(sessionId, { kind: "populated", injection: result2 });
|
|
166289
166693
|
return result2;
|
|
166290
166694
|
}
|
|
166291
166695
|
const lastCompartment = compartments[compartments.length - 1];
|
|
@@ -166303,9 +166707,10 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
|
|
|
166303
166707
|
compartmentCount: compartments.length,
|
|
166304
166708
|
skippedVisibleMessages: 0,
|
|
166305
166709
|
factCount: facts.length,
|
|
166306
|
-
memoryCount
|
|
166710
|
+
memoryCount,
|
|
166711
|
+
rebuiltFromDb: true
|
|
166307
166712
|
};
|
|
166308
|
-
injectionCache.set(sessionId, result2);
|
|
166713
|
+
injectionCache.set(sessionId, { kind: "populated", injection: result2 });
|
|
166309
166714
|
return result2;
|
|
166310
166715
|
}
|
|
166311
166716
|
let skippedVisibleMessages = 0;
|
|
@@ -166314,17 +166719,20 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
|
|
|
166314
166719
|
skippedVisibleMessages = cutoffIndex + 1;
|
|
166315
166720
|
const remaining = messages.slice(cutoffIndex + 1);
|
|
166316
166721
|
messages.splice(0, messages.length, ...remaining);
|
|
166722
|
+
} else {
|
|
166723
|
+
sessionLog(sessionId, `compartment injection entering degraded mode: boundary ${lastEndMessageId} not in visible messages`);
|
|
166317
166724
|
}
|
|
166318
166725
|
const result = {
|
|
166319
166726
|
block,
|
|
166320
166727
|
compartmentEndMessage: lastEnd,
|
|
166321
|
-
compartmentEndMessageId: lastEndMessageId,
|
|
166728
|
+
compartmentEndMessageId: cutoffIndex >= 0 ? lastEndMessageId : null,
|
|
166322
166729
|
compartmentCount: compartments.length,
|
|
166323
166730
|
skippedVisibleMessages,
|
|
166324
166731
|
factCount: facts.length,
|
|
166325
|
-
memoryCount
|
|
166732
|
+
memoryCount,
|
|
166733
|
+
rebuiltFromDb: true
|
|
166326
166734
|
};
|
|
166327
|
-
injectionCache.set(sessionId, result);
|
|
166735
|
+
injectionCache.set(sessionId, { kind: "populated", injection: result });
|
|
166328
166736
|
return result;
|
|
166329
166737
|
}
|
|
166330
166738
|
|
|
@@ -166448,6 +166856,70 @@ function createCtxSearchTool(deps) {
|
|
|
166448
166856
|
};
|
|
166449
166857
|
}
|
|
166450
166858
|
|
|
166859
|
+
// src/tools/todowrite.ts
|
|
166860
|
+
var STATUS_VALUES = [
|
|
166861
|
+
"pending",
|
|
166862
|
+
"in_progress",
|
|
166863
|
+
"completed",
|
|
166864
|
+
"cancelled"
|
|
166865
|
+
];
|
|
166866
|
+
var PRIORITY_VALUES = ["high", "medium", "low"];
|
|
166867
|
+
var TodoItem = exports_typebox.Object({
|
|
166868
|
+
content: exports_typebox.String({ description: "Brief description of the task" }),
|
|
166869
|
+
status: exports_typebox.Union(STATUS_VALUES.map((v) => exports_typebox.Literal(v))),
|
|
166870
|
+
priority: exports_typebox.Optional(exports_typebox.Union(PRIORITY_VALUES.map((v) => exports_typebox.Literal(v)))),
|
|
166871
|
+
id: exports_typebox.Optional(exports_typebox.String({ description: "Optional stable id for the todo" }))
|
|
166872
|
+
});
|
|
166873
|
+
var TodowriteParams = exports_typebox.Object({
|
|
166874
|
+
todos: exports_typebox.Array(TodoItem, {
|
|
166875
|
+
description: "Replace the current task list with this complete set of todos. Include every task you intend to track this turn — pending, in_progress, completed, or cancelled — because the list overwrites previous state."
|
|
166876
|
+
})
|
|
166877
|
+
});
|
|
166878
|
+
var TOOL_DESCRIPTION = [
|
|
166879
|
+
"Manage your task list for this session.",
|
|
166880
|
+
"",
|
|
166881
|
+
"Use this tool to plan multi-step work, track in-flight tasks, and",
|
|
166882
|
+
"mark progress as you complete steps. Pass the COMPLETE updated list",
|
|
166883
|
+
"of todos every time — this tool replaces the prior list rather than",
|
|
166884
|
+
"appending to it.",
|
|
166885
|
+
"",
|
|
166886
|
+
"Task states:",
|
|
166887
|
+
" - pending: not started yet",
|
|
166888
|
+
" - in_progress: currently working on (limit to ONE task at a time)",
|
|
166889
|
+
" - completed: finished successfully",
|
|
166890
|
+
" - cancelled: no longer needed",
|
|
166891
|
+
"",
|
|
166892
|
+
"Use this tool proactively for non-trivial work spanning 3+ steps.",
|
|
166893
|
+
"Skip it for single-shot answers or trivial 1-2 step tasks."
|
|
166894
|
+
].join(`
|
|
166895
|
+
`);
|
|
166896
|
+
function createTodowriteTool() {
|
|
166897
|
+
return {
|
|
166898
|
+
name: "todowrite",
|
|
166899
|
+
label: "Todos",
|
|
166900
|
+
description: TOOL_DESCRIPTION,
|
|
166901
|
+
parameters: TodowriteParams,
|
|
166902
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
166903
|
+
const todos = params.todos ?? [];
|
|
166904
|
+
const completed = todos.filter((t) => t.status === "completed").length;
|
|
166905
|
+
const active = todos.length - completed;
|
|
166906
|
+
return {
|
|
166907
|
+
content: [
|
|
166908
|
+
{
|
|
166909
|
+
type: "text",
|
|
166910
|
+
text: JSON.stringify(todos, null, 2)
|
|
166911
|
+
}
|
|
166912
|
+
],
|
|
166913
|
+
details: {
|
|
166914
|
+
todos,
|
|
166915
|
+
title: `${active} todos`,
|
|
166916
|
+
truncated: false
|
|
166917
|
+
}
|
|
166918
|
+
};
|
|
166919
|
+
}
|
|
166920
|
+
};
|
|
166921
|
+
}
|
|
166922
|
+
|
|
166451
166923
|
// src/tools/index.ts
|
|
166452
166924
|
function registerMagicContextTools(pi, opts) {
|
|
166453
166925
|
pi.registerTool(createCtxSearchTool({
|
|
@@ -166467,6 +166939,7 @@ function registerMagicContextTools(pi, opts) {
|
|
|
166467
166939
|
dreamerEnabled: opts.dreamerEnabled ?? false
|
|
166468
166940
|
}));
|
|
166469
166941
|
pi.registerTool(createCtxExpandTool({ db: opts.db }));
|
|
166942
|
+
pi.registerTool(createTodowriteTool());
|
|
166470
166943
|
if (opts.ctxReduceEnabled === true) {
|
|
166471
166944
|
pi.registerTool(createCtxReduceTool({
|
|
166472
166945
|
db: opts.db,
|