@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/index.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 path4 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(path4.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)`);
|
|
@@ -16143,17 +16202,9 @@ function closeQuietly(db) {
|
|
|
16143
16202
|
}
|
|
16144
16203
|
|
|
16145
16204
|
// ../plugin/src/hooks/magic-context/read-session-db.ts
|
|
16146
|
-
|
|
16147
|
-
__export(exports_read_session_db, {
|
|
16148
|
-
withReadOnlySessionDb: () => withReadOnlySessionDb,
|
|
16149
|
-
getRawSessionMessageCountFromDb: () => getRawSessionMessageCountFromDb,
|
|
16150
|
-
getMessageTimesFromOpenCodeDb: () => getMessageTimesFromOpenCodeDb,
|
|
16151
|
-
findLastAssistantModelFromOpenCodeDb: () => findLastAssistantModelFromOpenCodeDb,
|
|
16152
|
-
closeReadOnlySessionDb: () => closeReadOnlySessionDb
|
|
16153
|
-
});
|
|
16154
|
-
import { join as join5 } from "node:path";
|
|
16205
|
+
import { join as join4 } from "node:path";
|
|
16155
16206
|
function getOpenCodeDbPath() {
|
|
16156
|
-
return
|
|
16207
|
+
return join4(getDataDir(), "opencode", "opencode.db");
|
|
16157
16208
|
}
|
|
16158
16209
|
function closeCachedReadOnlyDb() {
|
|
16159
16210
|
if (!cachedReadOnlyDb) {
|
|
@@ -16180,9 +16231,6 @@ function getReadOnlySessionDb() {
|
|
|
16180
16231
|
function withReadOnlySessionDb(fn) {
|
|
16181
16232
|
return fn(getReadOnlySessionDb());
|
|
16182
16233
|
}
|
|
16183
|
-
function closeReadOnlySessionDb() {
|
|
16184
|
-
closeCachedReadOnlyDb();
|
|
16185
|
-
}
|
|
16186
16234
|
function getRawSessionMessageCountFromDb(db, sessionId) {
|
|
16187
16235
|
const row = db.prepare(`SELECT COUNT(*) as count FROM message WHERE session_id = ?
|
|
16188
16236
|
AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
|
|
@@ -16208,34 +16256,6 @@ function getMessageTimesFromOpenCodeDb(sessionId, messageIds) {
|
|
|
16208
16256
|
}
|
|
16209
16257
|
return result;
|
|
16210
16258
|
}
|
|
16211
|
-
function findLastAssistantModelFromOpenCodeDb(sessionId) {
|
|
16212
|
-
try {
|
|
16213
|
-
return withReadOnlySessionDb((db) => {
|
|
16214
|
-
const row = db.prepare(`SELECT json_extract(data, '$.providerID') as providerID,
|
|
16215
|
-
json_extract(data, '$.modelID') as modelID,
|
|
16216
|
-
json_extract(data, '$.agent') as agent
|
|
16217
|
-
FROM message
|
|
16218
|
-
WHERE session_id = ?
|
|
16219
|
-
AND json_extract(data, '$.role') = 'assistant'
|
|
16220
|
-
AND json_extract(data, '$.providerID') IS NOT NULL
|
|
16221
|
-
AND json_extract(data, '$.modelID') IS NOT NULL
|
|
16222
|
-
ORDER BY time_created DESC
|
|
16223
|
-
LIMIT 1`).get(sessionId);
|
|
16224
|
-
if (!row || typeof row.providerID !== "string" || typeof row.modelID !== "string") {
|
|
16225
|
-
return null;
|
|
16226
|
-
}
|
|
16227
|
-
const agent = typeof row.agent === "string" && row.agent.length > 0 ? row.agent : undefined;
|
|
16228
|
-
return {
|
|
16229
|
-
providerID: row.providerID,
|
|
16230
|
-
modelID: row.modelID,
|
|
16231
|
-
...agent ? { agent } : {}
|
|
16232
|
-
};
|
|
16233
|
-
});
|
|
16234
|
-
} catch (error51) {
|
|
16235
|
-
log("[magic-context] failed to recover live model from OpenCode DB:", error51);
|
|
16236
|
-
return null;
|
|
16237
|
-
}
|
|
16238
|
-
}
|
|
16239
16259
|
var cachedReadOnlyDb = null;
|
|
16240
16260
|
var init_read_session_db = __esm(async () => {
|
|
16241
16261
|
init_data_path();
|
|
@@ -16243,33 +16263,6 @@ var init_read_session_db = __esm(async () => {
|
|
|
16243
16263
|
await init_sqlite();
|
|
16244
16264
|
});
|
|
16245
16265
|
|
|
16246
|
-
// ../plugin/src/shared/rpc-notifications.ts
|
|
16247
|
-
var exports_rpc_notifications = {};
|
|
16248
|
-
__export(exports_rpc_notifications, {
|
|
16249
|
-
pushNotification: () => pushNotification,
|
|
16250
|
-
isTuiConnected: () => isTuiConnected,
|
|
16251
|
-
drainNotifications: () => drainNotifications
|
|
16252
|
-
});
|
|
16253
|
-
function pushNotification(type, payload, sessionId) {
|
|
16254
|
-
queue2.push({ type, payload, sessionId });
|
|
16255
|
-
if (queue2.length > 100) {
|
|
16256
|
-
queue2 = queue2.slice(-50);
|
|
16257
|
-
}
|
|
16258
|
-
}
|
|
16259
|
-
function drainNotifications() {
|
|
16260
|
-
lastDrainAt = Date.now();
|
|
16261
|
-
const result = queue2;
|
|
16262
|
-
queue2 = [];
|
|
16263
|
-
return result;
|
|
16264
|
-
}
|
|
16265
|
-
function isTuiConnected() {
|
|
16266
|
-
return lastDrainAt > 0 && Date.now() - lastDrainAt < TUI_CONNECTED_WINDOW_MS;
|
|
16267
|
-
}
|
|
16268
|
-
var queue2, lastDrainAt = 0, TUI_CONNECTED_WINDOW_MS = 3000;
|
|
16269
|
-
var init_rpc_notifications = __esm(() => {
|
|
16270
|
-
queue2 = [];
|
|
16271
|
-
});
|
|
16272
|
-
|
|
16273
16266
|
// ../../node_modules/.bun/esprima@4.0.1/node_modules/esprima/dist/esprima.js
|
|
16274
16267
|
var require_esprima = __commonJS((exports, module) => {
|
|
16275
16268
|
(function webpackUniversalModuleDefinition(root, factory) {
|
|
@@ -23877,10 +23870,10 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
23877
23870
|
replacer = null;
|
|
23878
23871
|
indent = EMPTY;
|
|
23879
23872
|
};
|
|
23880
|
-
var
|
|
23873
|
+
var join9 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
|
|
23881
23874
|
var join_content = (inside, value, gap) => {
|
|
23882
23875
|
const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
|
|
23883
|
-
return
|
|
23876
|
+
return join9(comment, inside, gap);
|
|
23884
23877
|
};
|
|
23885
23878
|
var stringify_string = (holder, key, value) => {
|
|
23886
23879
|
const raw = get_raw_string_literal(holder, key);
|
|
@@ -23902,13 +23895,13 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
23902
23895
|
if (i !== 0) {
|
|
23903
23896
|
inside += COMMA;
|
|
23904
23897
|
}
|
|
23905
|
-
const before =
|
|
23898
|
+
const before = join9(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
|
|
23906
23899
|
inside += before || LF + deeper_gap;
|
|
23907
23900
|
inside += stringify(i, value, deeper_gap) || STR_NULL;
|
|
23908
23901
|
inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
|
|
23909
23902
|
after_comma = process_comments(value, AFTER(i), deeper_gap);
|
|
23910
23903
|
}
|
|
23911
|
-
inside +=
|
|
23904
|
+
inside += join9(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
|
|
23912
23905
|
return BRACKET_OPEN + join_content(inside, value, gap) + BRACKET_CLOSE;
|
|
23913
23906
|
};
|
|
23914
23907
|
var object_stringify = (value, gap) => {
|
|
@@ -23929,13 +23922,13 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
23929
23922
|
inside += COMMA;
|
|
23930
23923
|
}
|
|
23931
23924
|
first = false;
|
|
23932
|
-
const before =
|
|
23925
|
+
const before = join9(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
|
|
23933
23926
|
inside += before || LF + deeper_gap;
|
|
23934
23927
|
inside += quote(key) + process_comments(value, AFTER_PROP(key), deeper_gap) + COLON + process_comments(value, AFTER_COLON(key), deeper_gap) + SPACE + sv + process_comments(value, AFTER_VALUE(key), deeper_gap);
|
|
23935
23928
|
after_comma = process_comments(value, AFTER(key), deeper_gap);
|
|
23936
23929
|
};
|
|
23937
23930
|
keys.forEach(iteratee);
|
|
23938
|
-
inside +=
|
|
23931
|
+
inside += join9(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
|
|
23939
23932
|
return CURLY_BRACKET_OPEN + join_content(inside, value, gap) + CURLY_BRACKET_CLOSE;
|
|
23940
23933
|
};
|
|
23941
23934
|
function stringify(key, holder, gap) {
|
|
@@ -24027,10 +24020,38 @@ var require_src2 = __commonJS((exports, module) => {
|
|
|
24027
24020
|
};
|
|
24028
24021
|
});
|
|
24029
24022
|
|
|
24023
|
+
// ../plugin/src/shared/rpc-notifications.ts
|
|
24024
|
+
var exports_rpc_notifications = {};
|
|
24025
|
+
__export(exports_rpc_notifications, {
|
|
24026
|
+
pushNotification: () => pushNotification,
|
|
24027
|
+
isTuiConnected: () => isTuiConnected,
|
|
24028
|
+
drainNotifications: () => drainNotifications
|
|
24029
|
+
});
|
|
24030
|
+
function pushNotification(type, payload, sessionId) {
|
|
24031
|
+
queue2.push({ id: nextNotificationId++, type, payload, sessionId });
|
|
24032
|
+
if (queue2.length > 100) {
|
|
24033
|
+
queue2 = queue2.slice(-50);
|
|
24034
|
+
}
|
|
24035
|
+
}
|
|
24036
|
+
function drainNotifications(lastReceivedId = 0) {
|
|
24037
|
+
lastDrainAt = Date.now();
|
|
24038
|
+
if (lastReceivedId > 0) {
|
|
24039
|
+
queue2 = queue2.filter((notification) => notification.id > lastReceivedId);
|
|
24040
|
+
}
|
|
24041
|
+
return [...queue2];
|
|
24042
|
+
}
|
|
24043
|
+
function isTuiConnected() {
|
|
24044
|
+
return lastDrainAt > 0 && Date.now() - lastDrainAt < TUI_CONNECTED_WINDOW_MS;
|
|
24045
|
+
}
|
|
24046
|
+
var queue2, nextNotificationId = 1, lastDrainAt = 0, TUI_CONNECTED_WINDOW_MS = 3000;
|
|
24047
|
+
var init_rpc_notifications = __esm(() => {
|
|
24048
|
+
queue2 = [];
|
|
24049
|
+
});
|
|
24050
|
+
|
|
24030
24051
|
// src/index.ts
|
|
24031
24052
|
init_embedding();
|
|
24032
24053
|
import { createRequire as createRequire2 } from "node:module";
|
|
24033
|
-
import { join as
|
|
24054
|
+
import { join as join18 } from "node:path";
|
|
24034
24055
|
|
|
24035
24056
|
// ../plugin/src/features/magic-context/memory/project-identity.ts
|
|
24036
24057
|
import { execSync } from "node:child_process";
|
|
@@ -157083,21 +157104,6 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
157083
157104
|
toolOnlyRanges
|
|
157084
157105
|
};
|
|
157085
157106
|
}
|
|
157086
|
-
|
|
157087
|
-
// ../plugin/src/shared/harness.ts
|
|
157088
|
-
var currentHarness = "opencode";
|
|
157089
|
-
var harnessLocked = false;
|
|
157090
|
-
function setHarness(value) {
|
|
157091
|
-
if (harnessLocked && currentHarness !== value) {
|
|
157092
|
-
throw new Error(`Magic Context: harness already locked to "${currentHarness}"; cannot change to "${value}"`);
|
|
157093
|
-
}
|
|
157094
|
-
currentHarness = value;
|
|
157095
|
-
harnessLocked = true;
|
|
157096
|
-
}
|
|
157097
|
-
function getHarness() {
|
|
157098
|
-
return currentHarness;
|
|
157099
|
-
}
|
|
157100
|
-
|
|
157101
157107
|
// ../plugin/src/features/magic-context/compression-depth-storage.ts
|
|
157102
157108
|
var incrementDepthStatements = new WeakMap;
|
|
157103
157109
|
var totalDepthStatements = new WeakMap;
|
|
@@ -157773,7 +157779,7 @@ function escapeXmlContent(s) {
|
|
|
157773
157779
|
}
|
|
157774
157780
|
// ../plugin/src/features/magic-context/storage-db.ts
|
|
157775
157781
|
init_data_path();
|
|
157776
|
-
import { copyFileSync, cpSync, existsSync as
|
|
157782
|
+
import { copyFileSync, cpSync, existsSync as existsSync4, mkdirSync as mkdirSync4 } from "node:fs";
|
|
157777
157783
|
import { join as join7 } from "node:path";
|
|
157778
157784
|
|
|
157779
157785
|
// ../plugin/src/shared/error-message.ts
|
|
@@ -157856,6 +157862,119 @@ function safeString(value) {
|
|
|
157856
157862
|
init_logger();
|
|
157857
157863
|
await init_sqlite();
|
|
157858
157864
|
|
|
157865
|
+
// ../plugin/src/features/magic-context/key-files/project-key-files.ts
|
|
157866
|
+
init_logger();
|
|
157867
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
157868
|
+
import { existsSync as existsSync2, readFileSync, realpathSync } from "node:fs";
|
|
157869
|
+
import { join as join5, resolve, sep } from "node:path";
|
|
157870
|
+
var MISSING_CONTENT_HASH = "<missing>";
|
|
157871
|
+
var ORPHAN_GRACE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
157872
|
+
function sha256(input) {
|
|
157873
|
+
return createHash3("sha256").update(input).digest("hex");
|
|
157874
|
+
}
|
|
157875
|
+
function resolveProjectPath(directory) {
|
|
157876
|
+
if (!directory?.trim())
|
|
157877
|
+
return null;
|
|
157878
|
+
try {
|
|
157879
|
+
return realpathSync(directory);
|
|
157880
|
+
} catch {
|
|
157881
|
+
return resolve(directory);
|
|
157882
|
+
}
|
|
157883
|
+
}
|
|
157884
|
+
function rowToProjectKeyFile(row) {
|
|
157885
|
+
return {
|
|
157886
|
+
projectPath: String(row.project_path),
|
|
157887
|
+
path: String(row.path),
|
|
157888
|
+
content: String(row.content),
|
|
157889
|
+
contentHash: String(row.content_hash),
|
|
157890
|
+
localTokenEstimate: Number(row.local_token_estimate),
|
|
157891
|
+
generatedAt: Number(row.generated_at),
|
|
157892
|
+
generatedByModel: row.generated_by_model == null ? null : String(row.generated_by_model),
|
|
157893
|
+
generationConfigHash: String(row.generation_config_hash),
|
|
157894
|
+
staleReason: row.stale_reason == null ? null : String(row.stale_reason)
|
|
157895
|
+
};
|
|
157896
|
+
}
|
|
157897
|
+
function readCurrentKeyFiles(db, projectPath) {
|
|
157898
|
+
const resolvedProjectPath = resolveProjectPath(projectPath) ?? projectPath;
|
|
157899
|
+
const rows = db.prepare(`SELECT project_path, path, content, content_hash, local_token_estimate,
|
|
157900
|
+
generated_at, generated_by_model, generation_config_hash, stale_reason
|
|
157901
|
+
FROM project_key_files
|
|
157902
|
+
WHERE project_path = ?
|
|
157903
|
+
ORDER BY generated_at DESC, path ASC`).all(resolvedProjectPath);
|
|
157904
|
+
return rows.map(rowToProjectKeyFile);
|
|
157905
|
+
}
|
|
157906
|
+
function getKeyFilesVersion(db, projectPath) {
|
|
157907
|
+
const resolvedProjectPath = resolveProjectPath(projectPath) ?? projectPath;
|
|
157908
|
+
const row = db.prepare("SELECT version FROM project_key_files_version WHERE project_path = ?").get(resolvedProjectPath);
|
|
157909
|
+
return Number(row?.version ?? 0);
|
|
157910
|
+
}
|
|
157911
|
+
function bumpKeyFilesVersion(db, projectPath) {
|
|
157912
|
+
const resolvedProjectPath = resolveProjectPath(projectPath) ?? projectPath;
|
|
157913
|
+
db.prepare(`INSERT INTO project_key_files_version (project_path, version)
|
|
157914
|
+
VALUES (?, 1)
|
|
157915
|
+
ON CONFLICT(project_path) DO UPDATE SET version = version + 1`).run(resolvedProjectPath);
|
|
157916
|
+
return getKeyFilesVersion(db, resolvedProjectPath);
|
|
157917
|
+
}
|
|
157918
|
+
function resolveCommitFiles(projectPath, files) {
|
|
157919
|
+
return files.map((file2) => {
|
|
157920
|
+
try {
|
|
157921
|
+
const disk = readFileSync(join5(projectPath, file2.path));
|
|
157922
|
+
return {
|
|
157923
|
+
...file2,
|
|
157924
|
+
contentHash: sha256(disk),
|
|
157925
|
+
staleReason: null
|
|
157926
|
+
};
|
|
157927
|
+
} catch {
|
|
157928
|
+
return {
|
|
157929
|
+
...file2,
|
|
157930
|
+
contentHash: MISSING_CONTENT_HASH,
|
|
157931
|
+
staleReason: "missing"
|
|
157932
|
+
};
|
|
157933
|
+
}
|
|
157934
|
+
});
|
|
157935
|
+
}
|
|
157936
|
+
function insertResolvedKeyFiles(db, projectPath, files, generatedAt, generatedByModel, generationConfigHash) {
|
|
157937
|
+
const insert = db.prepare(`INSERT INTO project_key_files
|
|
157938
|
+
(project_path, path, content, content_hash, local_token_estimate,
|
|
157939
|
+
generated_at, generated_by_model, generation_config_hash, stale_reason)
|
|
157940
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
157941
|
+
for (const file2 of files) {
|
|
157942
|
+
insert.run(projectPath, file2.path, file2.content, file2.contentHash, file2.localTokenEstimate, generatedAt, generatedByModel, generationConfigHash, file2.staleReason);
|
|
157943
|
+
}
|
|
157944
|
+
}
|
|
157945
|
+
function deleteOrphanProjectKeyFiles(db, now = Date.now()) {
|
|
157946
|
+
const cutoff = now - ORPHAN_GRACE_MS;
|
|
157947
|
+
const rows = db.prepare(`SELECT project_path AS projectPath, MAX(generated_at) AS lastGen
|
|
157948
|
+
FROM project_key_files
|
|
157949
|
+
GROUP BY project_path
|
|
157950
|
+
HAVING lastGen < ?`).all(cutoff);
|
|
157951
|
+
let deletedProjects = 0;
|
|
157952
|
+
for (const row of rows) {
|
|
157953
|
+
if (existsSync2(row.projectPath))
|
|
157954
|
+
continue;
|
|
157955
|
+
try {
|
|
157956
|
+
db.prepare("DELETE FROM project_key_files WHERE project_path = ?").run(row.projectPath);
|
|
157957
|
+
db.prepare("DELETE FROM project_key_files_version WHERE project_path = ?").run(row.projectPath);
|
|
157958
|
+
deletedProjects++;
|
|
157959
|
+
} catch (error51) {
|
|
157960
|
+
log(`[key-files] orphan GC failed for ${row.projectPath}:`, error51);
|
|
157961
|
+
}
|
|
157962
|
+
}
|
|
157963
|
+
return deletedProjects;
|
|
157964
|
+
}
|
|
157965
|
+
function isRelativeProjectFile(projectPath, relativePath) {
|
|
157966
|
+
if (!relativePath || relativePath.startsWith("/") || relativePath.includes(".."))
|
|
157967
|
+
return false;
|
|
157968
|
+
try {
|
|
157969
|
+
const root = realpathSync(projectPath);
|
|
157970
|
+
const absPath = resolve(projectPath, relativePath);
|
|
157971
|
+
const real = realpathSync(absPath);
|
|
157972
|
+
return real.startsWith(root + sep) || real === root;
|
|
157973
|
+
} catch {
|
|
157974
|
+
return false;
|
|
157975
|
+
}
|
|
157976
|
+
}
|
|
157977
|
+
|
|
157859
157978
|
// ../plugin/src/features/magic-context/migrations.ts
|
|
157860
157979
|
init_logger();
|
|
157861
157980
|
var MIGRATIONS = [
|
|
@@ -157991,6 +158110,9 @@ var MIGRATIONS = [
|
|
|
157991
158110
|
embedding BLOB NOT NULL,
|
|
157992
158111
|
model_id TEXT NOT NULL,
|
|
157993
158112
|
created_at INTEGER NOT NULL,
|
|
158113
|
+
-- FK-cascade audit (v12): git_commit_embeddings.sha -> git_commits.sha
|
|
158114
|
+
-- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
|
|
158115
|
+
-- every connection and v12 cleans historical orphan rows.
|
|
157994
158116
|
FOREIGN KEY(sha) REFERENCES git_commits(sha) ON DELETE CASCADE
|
|
157995
158117
|
);
|
|
157996
158118
|
|
|
@@ -158130,6 +158252,69 @@ var MIGRATIONS = [
|
|
|
158130
158252
|
db.exec("ALTER TABLE session_meta ADD COLUMN todo_synthetic_state_json TEXT DEFAULT ''");
|
|
158131
158253
|
}
|
|
158132
158254
|
}
|
|
158255
|
+
},
|
|
158256
|
+
{
|
|
158257
|
+
version: 12,
|
|
158258
|
+
description: "Clean orphan rows from FK-cascade embedding tables",
|
|
158259
|
+
up: (db) => {
|
|
158260
|
+
const hasTable = (name2) => Boolean(db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(name2));
|
|
158261
|
+
const memoryEmbeddings = hasTable("memory_embeddings") ? db.prepare(`DELETE FROM memory_embeddings
|
|
158262
|
+
WHERE memory_id NOT IN (SELECT id FROM memories)`).run().changes : 0;
|
|
158263
|
+
log(`[migrations] v12 cleaned ${memoryEmbeddings} orphan memory_embeddings row(s)`);
|
|
158264
|
+
const gitCommitEmbeddings = hasTable("git_commit_embeddings") ? db.prepare(`DELETE FROM git_commit_embeddings
|
|
158265
|
+
WHERE sha NOT IN (SELECT sha FROM git_commits)`).run().changes : 0;
|
|
158266
|
+
log(`[migrations] v12 cleaned ${gitCommitEmbeddings} orphan git_commit_embeddings row(s)`);
|
|
158267
|
+
}
|
|
158268
|
+
},
|
|
158269
|
+
{
|
|
158270
|
+
version: 13,
|
|
158271
|
+
description: "Add pending_compaction_marker_state column for deferred marker drain",
|
|
158272
|
+
up: (db) => {
|
|
158273
|
+
const cols = db.prepare("PRAGMA table_info(session_meta)").all();
|
|
158274
|
+
if (!cols.some((c) => c.name === "pending_compaction_marker_state")) {
|
|
158275
|
+
db.exec("ALTER TABLE session_meta ADD COLUMN pending_compaction_marker_state TEXT");
|
|
158276
|
+
}
|
|
158277
|
+
}
|
|
158278
|
+
},
|
|
158279
|
+
{
|
|
158280
|
+
version: 14,
|
|
158281
|
+
description: "Add project-scoped key files and version counter",
|
|
158282
|
+
up: (db) => {
|
|
158283
|
+
db.exec(`
|
|
158284
|
+
CREATE TABLE IF NOT EXISTS project_key_files (
|
|
158285
|
+
project_path TEXT NOT NULL,
|
|
158286
|
+
path TEXT NOT NULL,
|
|
158287
|
+
content TEXT NOT NULL,
|
|
158288
|
+
content_hash TEXT NOT NULL,
|
|
158289
|
+
local_token_estimate INTEGER NOT NULL,
|
|
158290
|
+
generated_at INTEGER NOT NULL,
|
|
158291
|
+
generated_by_model TEXT,
|
|
158292
|
+
generation_config_hash TEXT NOT NULL,
|
|
158293
|
+
stale_reason TEXT,
|
|
158294
|
+
PRIMARY KEY (project_path, path)
|
|
158295
|
+
);
|
|
158296
|
+
|
|
158297
|
+
CREATE INDEX IF NOT EXISTS idx_project_key_files_project
|
|
158298
|
+
ON project_key_files(project_path);
|
|
158299
|
+
CREATE INDEX IF NOT EXISTS idx_project_key_files_generated_at
|
|
158300
|
+
ON project_key_files(project_path, generated_at);
|
|
158301
|
+
|
|
158302
|
+
CREATE TABLE IF NOT EXISTS project_key_files_version (
|
|
158303
|
+
project_path TEXT PRIMARY KEY,
|
|
158304
|
+
version INTEGER NOT NULL DEFAULT 0
|
|
158305
|
+
);
|
|
158306
|
+
`);
|
|
158307
|
+
}
|
|
158308
|
+
},
|
|
158309
|
+
{
|
|
158310
|
+
version: 15,
|
|
158311
|
+
description: "Add deferred_execute_state column for boundary execution drain",
|
|
158312
|
+
up: (db) => {
|
|
158313
|
+
const cols = db.prepare("PRAGMA table_info(session_meta)").all();
|
|
158314
|
+
if (!cols.some((c) => c.name === "deferred_execute_state")) {
|
|
158315
|
+
db.exec("ALTER TABLE session_meta ADD COLUMN deferred_execute_state TEXT");
|
|
158316
|
+
}
|
|
158317
|
+
}
|
|
158133
158318
|
}
|
|
158134
158319
|
];
|
|
158135
158320
|
function ensureMigrationsTable(db) {
|
|
@@ -158197,6 +158382,28 @@ function runMigrations(db) {
|
|
|
158197
158382
|
log(`[migrations] schema version now: ${MIGRATIONS[MIGRATIONS.length - 1].version}`);
|
|
158198
158383
|
}
|
|
158199
158384
|
|
|
158385
|
+
// ../plugin/src/shared/stable-json.ts
|
|
158386
|
+
function stableStringify(value, seen = new WeakSet) {
|
|
158387
|
+
if (value === undefined)
|
|
158388
|
+
return "undefined";
|
|
158389
|
+
if (value === null || typeof value !== "object")
|
|
158390
|
+
return JSON.stringify(value) ?? String(value);
|
|
158391
|
+
if (seen.has(value))
|
|
158392
|
+
return '"[Circular]"';
|
|
158393
|
+
seen.add(value);
|
|
158394
|
+
if (Array.isArray(value)) {
|
|
158395
|
+
return `[${value.map((item) => stableStringify(item, seen)).join(",")}]`;
|
|
158396
|
+
}
|
|
158397
|
+
const entries = Object.entries(value).sort(([a], [b]) => {
|
|
158398
|
+
if (a < b)
|
|
158399
|
+
return -1;
|
|
158400
|
+
if (a > b)
|
|
158401
|
+
return 1;
|
|
158402
|
+
return 0;
|
|
158403
|
+
});
|
|
158404
|
+
return `{${entries.map(([key, child]) => `${JSON.stringify(key)}:${stableStringify(child, seen)}`).join(",")}}`;
|
|
158405
|
+
}
|
|
158406
|
+
|
|
158200
158407
|
// ../plugin/src/features/magic-context/tool-definition-tokens.ts
|
|
158201
158408
|
var measurements = new Map;
|
|
158202
158409
|
var fingerprints = new Map;
|
|
@@ -158231,7 +158438,7 @@ function loadToolDefinitionMeasurements(db) {
|
|
|
158231
158438
|
// ../plugin/src/features/magic-context/tool-owner-backfill.ts
|
|
158232
158439
|
init_data_path();
|
|
158233
158440
|
init_logger();
|
|
158234
|
-
import { existsSync as
|
|
158441
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
158235
158442
|
import { join as join6 } from "node:path";
|
|
158236
158443
|
var LEASE_DURATION_MS = 5 * 60 * 1000;
|
|
158237
158444
|
var LEASE_RENEWAL_MS = 60 * 1000;
|
|
@@ -158271,7 +158478,7 @@ function runToolOwnerBackfill(db) {
|
|
|
158271
158478
|
return result;
|
|
158272
158479
|
}
|
|
158273
158480
|
const opencodeDbPath = resolveOpencodeDbPath();
|
|
158274
|
-
if (!
|
|
158481
|
+
if (!existsSync3(opencodeDbPath)) {
|
|
158275
158482
|
log(`[backfill] OpenCode DB not found at ${opencodeDbPath} — marking all unbackfilled sessions as skipped. Lazy adoption (defense-in-depth) handles legacy rows at runtime.`);
|
|
158276
158483
|
markAllUnbackfilledSessionsSkipped(db);
|
|
158277
158484
|
result.sessionsSkippedNoOcDb = countSessionsByStatus(db, "skipped");
|
|
@@ -158482,18 +158689,18 @@ function resolveDatabasePath() {
|
|
|
158482
158689
|
return { dbDir, dbPath: join7(dbDir, "context.db") };
|
|
158483
158690
|
}
|
|
158484
158691
|
function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
158485
|
-
if (
|
|
158692
|
+
if (existsSync4(targetDbPath))
|
|
158486
158693
|
return;
|
|
158487
158694
|
const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
|
|
158488
158695
|
const legacyDbPath = join7(legacyDir, "context.db");
|
|
158489
|
-
if (!
|
|
158696
|
+
if (!existsSync4(legacyDbPath))
|
|
158490
158697
|
return;
|
|
158491
158698
|
log(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
|
|
158492
|
-
|
|
158699
|
+
mkdirSync4(targetDbDir, { recursive: true });
|
|
158493
158700
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
158494
158701
|
const src = `${legacyDbPath}${suffix}`;
|
|
158495
158702
|
const dst = join7(targetDbDir, `context.db${suffix}`);
|
|
158496
|
-
if (
|
|
158703
|
+
if (existsSync4(src)) {
|
|
158497
158704
|
try {
|
|
158498
158705
|
copyFileSync(src, dst);
|
|
158499
158706
|
} catch (error51) {
|
|
@@ -158503,7 +158710,7 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
|
158503
158710
|
}
|
|
158504
158711
|
const legacyModelsDir = join7(legacyDir, "models");
|
|
158505
158712
|
const targetModelsDir = join7(targetDbDir, "models");
|
|
158506
|
-
if (
|
|
158713
|
+
if (existsSync4(legacyModelsDir) && !existsSync4(targetModelsDir)) {
|
|
158507
158714
|
try {
|
|
158508
158715
|
cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
|
|
158509
158716
|
} catch (error51) {
|
|
@@ -158512,9 +158719,9 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
|
158512
158719
|
}
|
|
158513
158720
|
}
|
|
158514
158721
|
function initializeDatabase(db) {
|
|
158722
|
+
db.exec("PRAGMA foreign_keys=ON");
|
|
158515
158723
|
db.exec("PRAGMA journal_mode=WAL");
|
|
158516
158724
|
db.exec("PRAGMA busy_timeout=5000");
|
|
158517
|
-
db.exec("PRAGMA foreign_keys=ON");
|
|
158518
158725
|
db.exec(`
|
|
158519
158726
|
CREATE TABLE IF NOT EXISTS tags (
|
|
158520
158727
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -158612,6 +158819,9 @@ function initializeDatabase(db) {
|
|
|
158612
158819
|
);
|
|
158613
158820
|
|
|
158614
158821
|
CREATE TABLE IF NOT EXISTS memory_embeddings (
|
|
158822
|
+
-- FK-cascade audit (v12): memory_embeddings.memory_id -> memories.id
|
|
158823
|
+
-- uses ON DELETE CASCADE, so SQLite PRAGMA foreign_keys must be ON on
|
|
158824
|
+
-- every connection and v12 cleans historical orphan rows.
|
|
158615
158825
|
memory_id INTEGER PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
|
|
158616
158826
|
embedding BLOB NOT NULL,
|
|
158617
158827
|
model_id TEXT
|
|
@@ -158648,6 +158858,26 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
158648
158858
|
);
|
|
158649
158859
|
CREATE INDEX IF NOT EXISTS idx_dream_runs_project ON dream_runs(project_path, finished_at DESC);
|
|
158650
158860
|
|
|
158861
|
+
CREATE TABLE IF NOT EXISTS project_key_files (
|
|
158862
|
+
project_path TEXT NOT NULL,
|
|
158863
|
+
path TEXT NOT NULL,
|
|
158864
|
+
content TEXT NOT NULL,
|
|
158865
|
+
content_hash TEXT NOT NULL,
|
|
158866
|
+
local_token_estimate INTEGER NOT NULL,
|
|
158867
|
+
generated_at INTEGER NOT NULL,
|
|
158868
|
+
generated_by_model TEXT,
|
|
158869
|
+
generation_config_hash TEXT NOT NULL,
|
|
158870
|
+
stale_reason TEXT,
|
|
158871
|
+
PRIMARY KEY (project_path, path)
|
|
158872
|
+
);
|
|
158873
|
+
CREATE INDEX IF NOT EXISTS idx_project_key_files_project ON project_key_files(project_path);
|
|
158874
|
+
CREATE INDEX IF NOT EXISTS idx_project_key_files_generated_at ON project_key_files(project_path, generated_at);
|
|
158875
|
+
|
|
158876
|
+
CREATE TABLE IF NOT EXISTS project_key_files_version (
|
|
158877
|
+
project_path TEXT PRIMARY KEY,
|
|
158878
|
+
version INTEGER NOT NULL DEFAULT 0
|
|
158879
|
+
);
|
|
158880
|
+
|
|
158651
158881
|
-- (smart_notes: see note above; merged into unified notes table by migration v1)
|
|
158652
158882
|
|
|
158653
158883
|
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
@@ -158719,7 +158949,17 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
158719
158949
|
system_prompt_hash TEXT DEFAULT '',
|
|
158720
158950
|
memory_block_cache TEXT DEFAULT '',
|
|
158721
158951
|
memory_block_count INTEGER DEFAULT 0,
|
|
158722
|
-
memory_block_ids TEXT DEFAULT ''
|
|
158952
|
+
memory_block_ids TEXT DEFAULT '',
|
|
158953
|
+
-- pending_compaction_marker_state: intentionally NULLABLE without a
|
|
158954
|
+
-- default. Absence of a deferred marker is SQL NULL; presence is a
|
|
158955
|
+
-- valid JSON blob written via setPendingCompactionMarkerState.
|
|
158956
|
+
-- Excluded from healNullTextColumns. Readers filter IS NOT NULL AND
|
|
158957
|
+
-- != empty-string defensively. Plan v6 section 3.
|
|
158958
|
+
pending_compaction_marker_state TEXT,
|
|
158959
|
+
-- deferred_execute_state: intentionally NULLABLE without a default.
|
|
158960
|
+
-- Absence is SQL NULL; presence is a JSON blob written via
|
|
158961
|
+
-- setDeferredExecutePendingIfAbsent. Excluded from healNullTextColumns.
|
|
158962
|
+
deferred_execute_state TEXT
|
|
158723
158963
|
);
|
|
158724
158964
|
|
|
158725
158965
|
CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
|
|
@@ -158807,6 +159047,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
158807
159047
|
ensureColumn(db, "session_meta", "recomp_partial_range_end", "INTEGER DEFAULT 0");
|
|
158808
159048
|
ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
|
|
158809
159049
|
ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
|
|
159050
|
+
ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
|
|
159051
|
+
ensureColumn(db, "session_meta", "deferred_execute_state", "TEXT");
|
|
158810
159052
|
ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
158811
159053
|
ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
158812
159054
|
ensureColumn(db, "source_contents", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
@@ -158883,7 +159125,15 @@ function ensureColumn(db, table, column, definition) {
|
|
|
158883
159125
|
if (rows.some((row) => row.name === column)) {
|
|
158884
159126
|
return;
|
|
158885
159127
|
}
|
|
158886
|
-
|
|
159128
|
+
try {
|
|
159129
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
|
|
159130
|
+
} catch (err) {
|
|
159131
|
+
const recheck = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
159132
|
+
if (recheck.some((row) => row.name === column)) {
|
|
159133
|
+
return;
|
|
159134
|
+
}
|
|
159135
|
+
throw err;
|
|
159136
|
+
}
|
|
158887
159137
|
}
|
|
158888
159138
|
function openDatabase() {
|
|
158889
159139
|
const { dbDir, dbPath } = resolveDatabasePath();
|
|
@@ -158896,10 +159146,15 @@ function openDatabase() {
|
|
|
158896
159146
|
}
|
|
158897
159147
|
try {
|
|
158898
159148
|
migrateLegacyStorageIfNeeded(dbPath, dbDir);
|
|
158899
|
-
|
|
159149
|
+
mkdirSync4(dbDir, { recursive: true });
|
|
158900
159150
|
const db = new Database(dbPath);
|
|
158901
159151
|
initializeDatabase(db);
|
|
158902
159152
|
runMigrations(db);
|
|
159153
|
+
try {
|
|
159154
|
+
deleteOrphanProjectKeyFiles(db);
|
|
159155
|
+
} catch (error51) {
|
|
159156
|
+
log(`[magic-context] key-files orphan GC failed: ${getErrorMessage(error51)}`);
|
|
159157
|
+
}
|
|
158903
159158
|
try {
|
|
158904
159159
|
runToolOwnerBackfill(db);
|
|
158905
159160
|
} catch (error51) {
|
|
@@ -159021,6 +159276,12 @@ function isPersistedNoteNudgeRow(row) {
|
|
|
159021
159276
|
const r = row;
|
|
159022
159277
|
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";
|
|
159023
159278
|
}
|
|
159279
|
+
function isPersistedTodoSyntheticAnchorRow(row) {
|
|
159280
|
+
if (row === null || typeof row !== "object")
|
|
159281
|
+
return false;
|
|
159282
|
+
const r = row;
|
|
159283
|
+
return typeof r.todo_synthetic_call_id === "string" && typeof r.todo_synthetic_anchor_message_id === "string" && typeof r.todo_synthetic_state_json === "string";
|
|
159284
|
+
}
|
|
159024
159285
|
function isPersistedHistorianFailureRow(row) {
|
|
159025
159286
|
if (row === null || typeof row !== "object")
|
|
159026
159287
|
return false;
|
|
@@ -159097,6 +159358,29 @@ function setPersistedDeliveredNoteNudge(db, sessionId, text, messageId = "") {
|
|
|
159097
159358
|
function clearPersistedNoteNudge(db, sessionId) {
|
|
159098
159359
|
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);
|
|
159099
159360
|
}
|
|
159361
|
+
function getPersistedTodoSyntheticAnchor(db, sessionId) {
|
|
159362
|
+
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);
|
|
159363
|
+
if (!isPersistedTodoSyntheticAnchorRow(result)) {
|
|
159364
|
+
return null;
|
|
159365
|
+
}
|
|
159366
|
+
if (result.todo_synthetic_call_id.length === 0 || result.todo_synthetic_anchor_message_id.length === 0) {
|
|
159367
|
+
return null;
|
|
159368
|
+
}
|
|
159369
|
+
return {
|
|
159370
|
+
callId: result.todo_synthetic_call_id,
|
|
159371
|
+
messageId: result.todo_synthetic_anchor_message_id,
|
|
159372
|
+
stateJson: result.todo_synthetic_state_json
|
|
159373
|
+
};
|
|
159374
|
+
}
|
|
159375
|
+
function setPersistedTodoSyntheticAnchor(db, sessionId, callId, messageId, stateJson) {
|
|
159376
|
+
db.transaction(() => {
|
|
159377
|
+
ensureSessionMetaRow(db, sessionId);
|
|
159378
|
+
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);
|
|
159379
|
+
})();
|
|
159380
|
+
}
|
|
159381
|
+
function clearPersistedTodoSyntheticAnchor(db, sessionId) {
|
|
159382
|
+
db.prepare("UPDATE session_meta SET todo_synthetic_call_id = '', todo_synthetic_anchor_message_id = '', todo_synthetic_state_json = '' WHERE session_id = ?").run(sessionId);
|
|
159383
|
+
}
|
|
159100
159384
|
function getNoteLastReadAt(db, sessionId) {
|
|
159101
159385
|
try {
|
|
159102
159386
|
const result = db.prepare("SELECT note_last_read_at FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
@@ -159201,6 +159485,52 @@ function setStrippedPlaceholderIds(db, sessionId, ids) {
|
|
|
159201
159485
|
const json2 = ids.size > 0 ? JSON.stringify([...ids]) : "";
|
|
159202
159486
|
db.prepare("UPDATE session_meta SET stripped_placeholder_ids = ? WHERE session_id = ?").run(json2, sessionId);
|
|
159203
159487
|
}
|
|
159488
|
+
function isPendingCompactionMarker(value) {
|
|
159489
|
+
return typeof value === "object" && value !== null && typeof value.ordinal === "number" && typeof value.endMessageId === "string" && typeof value.publishedAt === "number";
|
|
159490
|
+
}
|
|
159491
|
+
function getPendingCompactionMarkerState(db, sessionId) {
|
|
159492
|
+
const row = db.prepare("SELECT pending_compaction_marker_state FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
159493
|
+
const raw = row?.pending_compaction_marker_state;
|
|
159494
|
+
if (raw === null || raw === undefined || raw === "")
|
|
159495
|
+
return null;
|
|
159496
|
+
try {
|
|
159497
|
+
const parsed = JSON.parse(raw);
|
|
159498
|
+
if (isPendingCompactionMarker(parsed)) {
|
|
159499
|
+
return parsed;
|
|
159500
|
+
}
|
|
159501
|
+
} catch {}
|
|
159502
|
+
return null;
|
|
159503
|
+
}
|
|
159504
|
+
function clearPendingCompactionMarkerStateIf(db, sessionId, expected) {
|
|
159505
|
+
const expectedBlob = stableStringify(expected);
|
|
159506
|
+
const result = db.prepare(`UPDATE session_meta SET pending_compaction_marker_state = NULL
|
|
159507
|
+
WHERE session_id = ? AND pending_compaction_marker_state = ?`).run(sessionId, expectedBlob);
|
|
159508
|
+
return result.changes > 0;
|
|
159509
|
+
}
|
|
159510
|
+
function peekDeferredExecutePending(db, sessionId) {
|
|
159511
|
+
const row = db.prepare("SELECT deferred_execute_state FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
159512
|
+
const raw = row?.deferred_execute_state;
|
|
159513
|
+
if (raw === null || raw === undefined || raw === "")
|
|
159514
|
+
return null;
|
|
159515
|
+
try {
|
|
159516
|
+
return JSON.parse(raw);
|
|
159517
|
+
} catch {
|
|
159518
|
+
return null;
|
|
159519
|
+
}
|
|
159520
|
+
}
|
|
159521
|
+
function setDeferredExecutePendingIfAbsent(db, sessionId, payload) {
|
|
159522
|
+
ensureSessionMetaRow(db, sessionId);
|
|
159523
|
+
const payloadBlob = stableStringify(payload);
|
|
159524
|
+
const result = db.prepare(`UPDATE session_meta SET deferred_execute_state = ?
|
|
159525
|
+
WHERE session_id = ? AND deferred_execute_state IS NULL`).run(payloadBlob, sessionId);
|
|
159526
|
+
return result.changes > 0;
|
|
159527
|
+
}
|
|
159528
|
+
function clearDeferredExecutePendingIfMatches(db, sessionId, expected) {
|
|
159529
|
+
const expectedBlob = stableStringify(expected);
|
|
159530
|
+
const result = db.prepare(`UPDATE session_meta SET deferred_execute_state = NULL
|
|
159531
|
+
WHERE session_id = ? AND deferred_execute_state = ?`).run(sessionId, expectedBlob);
|
|
159532
|
+
return result.changes > 0;
|
|
159533
|
+
}
|
|
159204
159534
|
// ../plugin/src/features/magic-context/resolve-subagent-fallback.ts
|
|
159205
159535
|
init_logger();
|
|
159206
159536
|
await init_read_session_db();
|
|
@@ -159795,8 +160125,8 @@ function getAgentFallbackModels(agent) {
|
|
|
159795
160125
|
|
|
159796
160126
|
// ../plugin/src/shared/models-dev-cache.ts
|
|
159797
160127
|
init_data_path();
|
|
159798
|
-
import { createHash as
|
|
159799
|
-
import { existsSync as
|
|
160128
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
160129
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "node:fs";
|
|
159800
160130
|
import { homedir as homedir2, platform } from "node:os";
|
|
159801
160131
|
import { join as join8 } from "node:path";
|
|
159802
160132
|
|
|
@@ -159904,7 +160234,7 @@ var recentlySeenApiSizes = new Set;
|
|
|
159904
160234
|
var fileCache = null;
|
|
159905
160235
|
var fileLastAttempt = 0;
|
|
159906
160236
|
function hashFast(input) {
|
|
159907
|
-
return
|
|
160237
|
+
return createHash4("sha1").update(input).digest("hex");
|
|
159908
160238
|
}
|
|
159909
160239
|
function getModelsJsonPath() {
|
|
159910
160240
|
const explicit = process.env.OPENCODE_MODELS_PATH?.trim();
|
|
@@ -159919,10 +160249,10 @@ function getOpencodeConfigPath() {
|
|
|
159919
160249
|
const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
159920
160250
|
const configDir = envDir ? envDir : platform() === "win32" ? join8(homedir2(), ".config", "opencode") : join8(process.env.XDG_CONFIG_HOME || join8(homedir2(), ".config"), "opencode");
|
|
159921
160251
|
const jsonc = join8(configDir, "opencode.jsonc");
|
|
159922
|
-
if (
|
|
160252
|
+
if (existsSync5(jsonc))
|
|
159923
160253
|
return jsonc;
|
|
159924
160254
|
const json2 = join8(configDir, "opencode.json");
|
|
159925
|
-
if (
|
|
160255
|
+
if (existsSync5(json2))
|
|
159926
160256
|
return json2;
|
|
159927
160257
|
return null;
|
|
159928
160258
|
}
|
|
@@ -159954,9 +160284,9 @@ function loadModelsDevMetadataFromFile() {
|
|
|
159954
160284
|
const modelsJsonPath = getModelsJsonPath();
|
|
159955
160285
|
let fileFound = false;
|
|
159956
160286
|
try {
|
|
159957
|
-
if (
|
|
160287
|
+
if (existsSync5(modelsJsonPath)) {
|
|
159958
160288
|
fileFound = true;
|
|
159959
|
-
const raw =
|
|
160289
|
+
const raw = readFileSync2(modelsJsonPath, "utf-8");
|
|
159960
160290
|
const data = JSON.parse(raw);
|
|
159961
160291
|
for (const [providerId, provider2] of Object.entries(data)) {
|
|
159962
160292
|
if (!provider2?.models || typeof provider2.models !== "object")
|
|
@@ -159971,8 +160301,8 @@ function loadModelsDevMetadataFromFile() {
|
|
|
159971
160301
|
}
|
|
159972
160302
|
try {
|
|
159973
160303
|
const configPath = getOpencodeConfigPath();
|
|
159974
|
-
if (configPath &&
|
|
159975
|
-
const config2 = parseJsonc(
|
|
160304
|
+
if (configPath && existsSync5(configPath)) {
|
|
160305
|
+
const config2 = parseJsonc(readFileSync2(configPath, "utf-8"));
|
|
159976
160306
|
if (config2.provider && typeof config2.provider === "object") {
|
|
159977
160307
|
for (const [providerId, provider2] of Object.entries(config2.provider)) {
|
|
159978
160308
|
if (!provider2?.models || typeof provider2.models !== "object")
|
|
@@ -160267,6 +160597,84 @@ function clearNoteNudgeState(db, sessionId, options) {
|
|
|
160267
160597
|
lastDeliveredAt.delete(sessionId);
|
|
160268
160598
|
}
|
|
160269
160599
|
|
|
160600
|
+
// ../plugin/src/hooks/magic-context/todo-view.ts
|
|
160601
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
160602
|
+
var TERMINAL_STATUSES = new Set(["completed", "cancelled"]);
|
|
160603
|
+
var TITLE_DONE_STATUSES = new Set(["completed"]);
|
|
160604
|
+
var SYNTHETIC_CALL_ID_PREFIX = "mc_synthetic_todo_";
|
|
160605
|
+
function normalizeTodoStateJson(todos) {
|
|
160606
|
+
if (!Array.isArray(todos))
|
|
160607
|
+
return null;
|
|
160608
|
+
const normalized = [];
|
|
160609
|
+
for (const todo of todos) {
|
|
160610
|
+
if (!isTodoItem(todo))
|
|
160611
|
+
return null;
|
|
160612
|
+
normalized.push({
|
|
160613
|
+
content: todo.content,
|
|
160614
|
+
status: todo.status,
|
|
160615
|
+
priority: todo.priority ?? "medium"
|
|
160616
|
+
});
|
|
160617
|
+
}
|
|
160618
|
+
return JSON.stringify(normalized);
|
|
160619
|
+
}
|
|
160620
|
+
function buildSyntheticTodoPart(stateJson) {
|
|
160621
|
+
const todos = parseTodoState(stateJson);
|
|
160622
|
+
if (todos === null || todos.length === 0)
|
|
160623
|
+
return null;
|
|
160624
|
+
if (todos.every((t) => TERMINAL_STATUSES.has(t.status)))
|
|
160625
|
+
return null;
|
|
160626
|
+
const callID = computeSyntheticCallId(stateJson);
|
|
160627
|
+
const activeCount = todos.filter((t) => !TITLE_DONE_STATUSES.has(t.status)).length;
|
|
160628
|
+
const output = JSON.stringify(todos, null, 2);
|
|
160629
|
+
const ts = 0;
|
|
160630
|
+
return {
|
|
160631
|
+
type: "tool",
|
|
160632
|
+
callID,
|
|
160633
|
+
tool: "todowrite",
|
|
160634
|
+
state: {
|
|
160635
|
+
status: "completed",
|
|
160636
|
+
input: { todos },
|
|
160637
|
+
output,
|
|
160638
|
+
title: `${activeCount} todos`,
|
|
160639
|
+
metadata: { todos, truncated: false },
|
|
160640
|
+
time: { start: ts, end: ts }
|
|
160641
|
+
},
|
|
160642
|
+
syntheticTodoMarker: true
|
|
160643
|
+
};
|
|
160644
|
+
}
|
|
160645
|
+
function computeSyntheticCallId(stateJson) {
|
|
160646
|
+
const hash2 = createHash5("sha256").update(stateJson).digest("hex").slice(0, 16);
|
|
160647
|
+
return `${SYNTHETIC_CALL_ID_PREFIX}${hash2}`;
|
|
160648
|
+
}
|
|
160649
|
+
function parseTodoState(stateJson) {
|
|
160650
|
+
if (stateJson.length === 0)
|
|
160651
|
+
return null;
|
|
160652
|
+
try {
|
|
160653
|
+
const parsed = JSON.parse(stateJson);
|
|
160654
|
+
if (!Array.isArray(parsed))
|
|
160655
|
+
return null;
|
|
160656
|
+
const result = [];
|
|
160657
|
+
for (const item of parsed) {
|
|
160658
|
+
if (!isTodoItem(item))
|
|
160659
|
+
continue;
|
|
160660
|
+
result.push({
|
|
160661
|
+
content: item.content,
|
|
160662
|
+
status: item.status,
|
|
160663
|
+
priority: item.priority ?? "medium"
|
|
160664
|
+
});
|
|
160665
|
+
}
|
|
160666
|
+
return result;
|
|
160667
|
+
} catch {
|
|
160668
|
+
return null;
|
|
160669
|
+
}
|
|
160670
|
+
}
|
|
160671
|
+
function isTodoItem(value) {
|
|
160672
|
+
if (value === null || typeof value !== "object")
|
|
160673
|
+
return false;
|
|
160674
|
+
const todo = value;
|
|
160675
|
+
return typeof todo.content === "string" && typeof todo.status === "string" && (todo.priority === undefined || typeof todo.priority === "string");
|
|
160676
|
+
}
|
|
160677
|
+
|
|
160270
160678
|
// src/index.ts
|
|
160271
160679
|
init_data_path();
|
|
160272
160680
|
init_logger();
|
|
@@ -160296,15 +160704,15 @@ init_logger();
|
|
|
160296
160704
|
|
|
160297
160705
|
// src/subagent-runner.ts
|
|
160298
160706
|
import * as childProcess from "node:child_process";
|
|
160299
|
-
import { existsSync as
|
|
160300
|
-
import { dirname as
|
|
160707
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
160708
|
+
import { dirname as dirname3, resolve as resolvePath } from "node:path";
|
|
160301
160709
|
import { createInterface } from "node:readline";
|
|
160302
160710
|
import { fileURLToPath } from "node:url";
|
|
160303
160711
|
function resolveSubagentEntryPath() {
|
|
160304
160712
|
try {
|
|
160305
|
-
const here =
|
|
160713
|
+
const here = dirname3(fileURLToPath(import.meta.url));
|
|
160306
160714
|
const candidate = resolvePath(here, "subagent-entry.js");
|
|
160307
|
-
if (
|
|
160715
|
+
if (existsSync6(candidate))
|
|
160308
160716
|
return candidate;
|
|
160309
160717
|
return;
|
|
160310
160718
|
} catch {
|
|
@@ -160329,13 +160737,13 @@ class PiSubagentRunner {
|
|
|
160329
160737
|
async run(options) {
|
|
160330
160738
|
const startTime = Date.now();
|
|
160331
160739
|
const args = buildArgs(options);
|
|
160332
|
-
return new Promise((
|
|
160740
|
+
return new Promise((resolve2) => {
|
|
160333
160741
|
let settled = false;
|
|
160334
160742
|
const settle = (result) => {
|
|
160335
160743
|
if (settled)
|
|
160336
160744
|
return;
|
|
160337
160745
|
settled = true;
|
|
160338
|
-
|
|
160746
|
+
resolve2(result);
|
|
160339
160747
|
};
|
|
160340
160748
|
const emitProgress = (event) => {
|
|
160341
160749
|
if (!options.onProgress)
|
|
@@ -160744,69 +161152,6 @@ ${sidekickText}
|
|
|
160744
161152
|
});
|
|
160745
161153
|
}
|
|
160746
161154
|
|
|
160747
|
-
// ../plugin/src/features/magic-context/dreamer/queue.ts
|
|
160748
|
-
function enqueueDream(db, projectIdentity, reason, force = false) {
|
|
160749
|
-
const now = Date.now();
|
|
160750
|
-
return db.transaction(() => {
|
|
160751
|
-
const staleThresholdMs = force ? 2 * 60 * 1000 : 120 * 60 * 1000;
|
|
160752
|
-
db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run([projectIdentity, now - staleThresholdMs]);
|
|
160753
|
-
const existing = db.prepare("SELECT id FROM dream_queue WHERE project_path = ?").get(projectIdentity);
|
|
160754
|
-
if (existing) {
|
|
160755
|
-
return null;
|
|
160756
|
-
}
|
|
160757
|
-
const result = db.prepare("INSERT INTO dream_queue (project_path, reason, enqueued_at) VALUES (?, ?, ?)").run(projectIdentity, reason, now);
|
|
160758
|
-
return {
|
|
160759
|
-
id: Number(result.lastInsertRowid),
|
|
160760
|
-
projectIdentity,
|
|
160761
|
-
reason,
|
|
160762
|
-
enqueuedAt: now,
|
|
160763
|
-
startedAt: null
|
|
160764
|
-
};
|
|
160765
|
-
})();
|
|
160766
|
-
}
|
|
160767
|
-
function peekQueue(db, projectIdentity) {
|
|
160768
|
-
const row = projectIdentity ? db.prepare("SELECT id, project_path, reason, enqueued_at FROM dream_queue WHERE started_at IS NULL AND project_path = ? ORDER BY enqueued_at ASC LIMIT 1").get(projectIdentity) : db.prepare("SELECT id, project_path, reason, enqueued_at FROM dream_queue WHERE started_at IS NULL ORDER BY enqueued_at ASC LIMIT 1").get();
|
|
160769
|
-
if (!row)
|
|
160770
|
-
return null;
|
|
160771
|
-
return {
|
|
160772
|
-
id: row.id,
|
|
160773
|
-
projectIdentity: row.project_path,
|
|
160774
|
-
reason: row.reason,
|
|
160775
|
-
enqueuedAt: row.enqueued_at,
|
|
160776
|
-
startedAt: null
|
|
160777
|
-
};
|
|
160778
|
-
}
|
|
160779
|
-
function dequeueNext(db, projectIdentity) {
|
|
160780
|
-
const now = Date.now();
|
|
160781
|
-
return db.transaction(() => {
|
|
160782
|
-
const entry = peekQueue(db, projectIdentity);
|
|
160783
|
-
if (!entry)
|
|
160784
|
-
return null;
|
|
160785
|
-
const result = db.prepare("UPDATE dream_queue SET started_at = ? WHERE id = ? AND started_at IS NULL").run(now, entry.id);
|
|
160786
|
-
if (result.changes === 0)
|
|
160787
|
-
return null;
|
|
160788
|
-
return { ...entry, startedAt: now };
|
|
160789
|
-
})();
|
|
160790
|
-
}
|
|
160791
|
-
function removeDreamEntry(db, id) {
|
|
160792
|
-
db.prepare("DELETE FROM dream_queue WHERE id = ?").run(id);
|
|
160793
|
-
}
|
|
160794
|
-
function resetDreamEntry(db, id) {
|
|
160795
|
-
db.prepare("UPDATE dream_queue SET started_at = NULL, retry_count = COALESCE(retry_count, 0) + 1 WHERE id = ?").run(id);
|
|
160796
|
-
}
|
|
160797
|
-
function getEntryRetryCount(db, id) {
|
|
160798
|
-
const row = db.prepare("SELECT retry_count FROM dream_queue WHERE id = ?").get(id);
|
|
160799
|
-
return row?.retry_count ?? 0;
|
|
160800
|
-
}
|
|
160801
|
-
function clearStaleEntries(db, maxAgeMs) {
|
|
160802
|
-
const cutoff = Date.now() - maxAgeMs;
|
|
160803
|
-
const result = db.prepare("DELETE FROM dream_queue WHERE started_at IS NOT NULL AND started_at < ?").run(cutoff);
|
|
160804
|
-
return result.changes;
|
|
160805
|
-
}
|
|
160806
|
-
|
|
160807
|
-
// src/commands/ctx-dream.ts
|
|
160808
|
-
init_logger();
|
|
160809
|
-
|
|
160810
161155
|
// ../plugin/src/features/magic-context/dreamer/storage-dream-state.ts
|
|
160811
161156
|
var getDreamStateStatements = new WeakMap;
|
|
160812
161157
|
var setDreamStateStatements = new WeakMap;
|
|
@@ -160866,6 +161211,16 @@ function isLeaseActive(db) {
|
|
|
160866
161211
|
function getLeaseHolder(db) {
|
|
160867
161212
|
return getDreamState(db, LEASE_HOLDER_KEY);
|
|
160868
161213
|
}
|
|
161214
|
+
function peekLeaseHolderAndExpiry(db, expectedHolder) {
|
|
161215
|
+
const holder = getDreamState(db, LEASE_HOLDER_KEY);
|
|
161216
|
+
if (holder !== expectedHolder)
|
|
161217
|
+
return false;
|
|
161218
|
+
const expiryStr = getDreamState(db, LEASE_EXPIRY_KEY);
|
|
161219
|
+
if (!expiryStr)
|
|
161220
|
+
return false;
|
|
161221
|
+
const expiry = Number(expiryStr);
|
|
161222
|
+
return Number.isFinite(expiry) && expiry >= Date.now();
|
|
161223
|
+
}
|
|
160869
161224
|
function acquireLease(db, holderId) {
|
|
160870
161225
|
return db.transaction(() => {
|
|
160871
161226
|
if (isLeaseActive(db)) {
|
|
@@ -160902,9 +161257,84 @@ function releaseLease(db, holderId) {
|
|
|
160902
161257
|
deleteDreamState(db, LEASE_EXPIRY_KEY);
|
|
160903
161258
|
})();
|
|
160904
161259
|
}
|
|
161260
|
+
|
|
161261
|
+
// ../plugin/src/features/magic-context/dreamer/queue.ts
|
|
161262
|
+
function hasActiveDreamLease(db) {
|
|
161263
|
+
try {
|
|
161264
|
+
return isLeaseActive(db);
|
|
161265
|
+
} catch (error51) {
|
|
161266
|
+
if (String(error51).includes("no such table: dream_state")) {
|
|
161267
|
+
return false;
|
|
161268
|
+
}
|
|
161269
|
+
throw error51;
|
|
161270
|
+
}
|
|
161271
|
+
}
|
|
161272
|
+
function enqueueDream(db, projectIdentity, reason, force = false) {
|
|
161273
|
+
const now = Date.now();
|
|
161274
|
+
return db.transaction(() => {
|
|
161275
|
+
if (!hasActiveDreamLease(db)) {
|
|
161276
|
+
const staleThresholdMs = force ? 2 * 60 * 1000 : 120 * 60 * 1000;
|
|
161277
|
+
db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run([projectIdentity, now - staleThresholdMs]);
|
|
161278
|
+
}
|
|
161279
|
+
const existing = db.prepare("SELECT id FROM dream_queue WHERE project_path = ?").get(projectIdentity);
|
|
161280
|
+
if (existing) {
|
|
161281
|
+
return null;
|
|
161282
|
+
}
|
|
161283
|
+
const result = db.prepare("INSERT INTO dream_queue (project_path, reason, enqueued_at) VALUES (?, ?, ?)").run(projectIdentity, reason, now);
|
|
161284
|
+
return {
|
|
161285
|
+
id: Number(result.lastInsertRowid),
|
|
161286
|
+
projectIdentity,
|
|
161287
|
+
reason,
|
|
161288
|
+
enqueuedAt: now,
|
|
161289
|
+
startedAt: null
|
|
161290
|
+
};
|
|
161291
|
+
})();
|
|
161292
|
+
}
|
|
161293
|
+
function peekQueue(db, projectIdentity) {
|
|
161294
|
+
const row = projectIdentity ? db.prepare("SELECT id, project_path, reason, enqueued_at FROM dream_queue WHERE started_at IS NULL AND project_path = ? ORDER BY enqueued_at ASC LIMIT 1").get(projectIdentity) : db.prepare("SELECT id, project_path, reason, enqueued_at FROM dream_queue WHERE started_at IS NULL ORDER BY enqueued_at ASC LIMIT 1").get();
|
|
161295
|
+
if (!row)
|
|
161296
|
+
return null;
|
|
161297
|
+
return {
|
|
161298
|
+
id: row.id,
|
|
161299
|
+
projectIdentity: row.project_path,
|
|
161300
|
+
reason: row.reason,
|
|
161301
|
+
enqueuedAt: row.enqueued_at,
|
|
161302
|
+
startedAt: null
|
|
161303
|
+
};
|
|
161304
|
+
}
|
|
161305
|
+
function dequeueNext(db, projectIdentity) {
|
|
161306
|
+
const now = Date.now();
|
|
161307
|
+
return db.transaction(() => {
|
|
161308
|
+
const entry = peekQueue(db, projectIdentity);
|
|
161309
|
+
if (!entry)
|
|
161310
|
+
return null;
|
|
161311
|
+
const result = db.prepare("UPDATE dream_queue SET started_at = ? WHERE id = ? AND started_at IS NULL").run(now, entry.id);
|
|
161312
|
+
if (result.changes === 0)
|
|
161313
|
+
return null;
|
|
161314
|
+
return { ...entry, startedAt: now };
|
|
161315
|
+
})();
|
|
161316
|
+
}
|
|
161317
|
+
function removeDreamEntry(db, id) {
|
|
161318
|
+
db.prepare("DELETE FROM dream_queue WHERE id = ?").run(id);
|
|
161319
|
+
}
|
|
161320
|
+
function resetDreamEntry(db, id) {
|
|
161321
|
+
db.prepare("UPDATE dream_queue SET started_at = NULL, retry_count = COALESCE(retry_count, 0) + 1 WHERE id = ?").run(id);
|
|
161322
|
+
}
|
|
161323
|
+
function getEntryRetryCount(db, id) {
|
|
161324
|
+
const row = db.prepare("SELECT retry_count FROM dream_queue WHERE id = ?").get(id);
|
|
161325
|
+
return row?.retry_count ?? 0;
|
|
161326
|
+
}
|
|
161327
|
+
function clearStaleEntries(db, maxAgeMs) {
|
|
161328
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
161329
|
+
const result = db.prepare("DELETE FROM dream_queue WHERE started_at IS NOT NULL AND started_at < ?").run(cutoff);
|
|
161330
|
+
return result.changes;
|
|
161331
|
+
}
|
|
161332
|
+
|
|
161333
|
+
// src/commands/ctx-dream.ts
|
|
161334
|
+
init_logger();
|
|
160905
161335
|
// ../plugin/src/features/magic-context/dreamer/runner.ts
|
|
160906
|
-
import { existsSync as
|
|
160907
|
-
import { join as
|
|
161336
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
161337
|
+
import { join as join11 } from "node:path";
|
|
160908
161338
|
|
|
160909
161339
|
// ../plugin/src/shared/index.ts
|
|
160910
161340
|
init_logger();
|
|
@@ -161198,187 +161628,519 @@ init_logger();
|
|
|
161198
161628
|
await init_sqlite();
|
|
161199
161629
|
|
|
161200
161630
|
// ../plugin/src/features/magic-context/key-files/identify-key-files.ts
|
|
161631
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
161632
|
+
import { join as join10 } from "node:path";
|
|
161201
161633
|
init_logger();
|
|
161202
161634
|
|
|
161203
|
-
// ../plugin/src/features/magic-context/key-files/
|
|
161204
|
-
|
|
161205
|
-
|
|
161206
|
-
|
|
161207
|
-
|
|
161208
|
-
|
|
161209
|
-
|
|
161210
|
-
|
|
161211
|
-
|
|
161212
|
-
|
|
161213
|
-
|
|
161214
|
-
|
|
161215
|
-
|
|
161216
|
-
|
|
161217
|
-
|
|
161218
|
-
|
|
161219
|
-
|
|
161220
|
-
|
|
161221
|
-
|
|
161222
|
-
|
|
161223
|
-
|
|
161224
|
-
|
|
161225
|
-
|
|
161226
|
-
|
|
161227
|
-
|
|
161228
|
-
|
|
161229
|
-
|
|
161230
|
-
|
|
161231
|
-
|
|
161232
|
-
|
|
161233
|
-
|
|
161234
|
-
|
|
161235
|
-
|
|
161236
|
-
|
|
161237
|
-
|
|
161238
|
-
|
|
161239
|
-
|
|
161240
|
-
|
|
161241
|
-
|
|
161242
|
-
|
|
161243
|
-
|
|
161244
|
-
|
|
161245
|
-
|
|
161246
|
-
|
|
161247
|
-
|
|
161248
|
-
|
|
161249
|
-
|
|
161250
|
-
|
|
161251
|
-
|
|
161252
|
-
|
|
161253
|
-
|
|
161254
|
-
|
|
161255
|
-
|
|
161256
|
-
|
|
161257
|
-
|
|
161258
|
-
|
|
161259
|
-
|
|
161260
|
-
|
|
161261
|
-
|
|
161262
|
-
|
|
161263
|
-
|
|
161264
|
-
|
|
161265
|
-
|
|
161266
|
-
|
|
161635
|
+
// ../plugin/src/features/magic-context/key-files/aft-availability.ts
|
|
161636
|
+
var import_comment_json = __toESM(require_src2(), 1);
|
|
161637
|
+
import { existsSync as existsSync7, readFileSync as readFileSync3 } from "node:fs";
|
|
161638
|
+
import { homedir as homedir3 } from "node:os";
|
|
161639
|
+
import { join as join9 } from "node:path";
|
|
161640
|
+
var overrideAvailability = null;
|
|
161641
|
+
function parseConfig(path5) {
|
|
161642
|
+
if (!existsSync7(path5))
|
|
161643
|
+
return null;
|
|
161644
|
+
return import_comment_json.parse(readFileSync3(path5, "utf-8"));
|
|
161645
|
+
}
|
|
161646
|
+
function entryMatchesAft(entry) {
|
|
161647
|
+
const value = Array.isArray(entry) ? entry[0] : entry;
|
|
161648
|
+
return typeof value === "string" && (value.includes("@cortexkit/aft") || value.includes("aft-opencode") || value.includes("aft-pi"));
|
|
161649
|
+
}
|
|
161650
|
+
function hasAftInArray(value) {
|
|
161651
|
+
return Array.isArray(value) && value.some(entryMatchesAft);
|
|
161652
|
+
}
|
|
161653
|
+
function hasAftAtKeys(value, keys) {
|
|
161654
|
+
if (!value || typeof value !== "object")
|
|
161655
|
+
return false;
|
|
161656
|
+
const record2 = value;
|
|
161657
|
+
for (const key of keys) {
|
|
161658
|
+
if (hasAftInArray(record2[key]))
|
|
161659
|
+
return true;
|
|
161660
|
+
}
|
|
161661
|
+
return false;
|
|
161662
|
+
}
|
|
161663
|
+
function getAftAvailability() {
|
|
161664
|
+
const home = process.env.HOME || homedir3();
|
|
161665
|
+
const opencodePaths = [
|
|
161666
|
+
join9(home, ".config", "opencode", "opencode.jsonc"),
|
|
161667
|
+
join9(home, ".config", "opencode", "opencode.json")
|
|
161668
|
+
];
|
|
161669
|
+
const piPaths = [join9(home, ".pi", "agent", "settings.json")];
|
|
161670
|
+
const checkedPaths = [...opencodePaths, ...piPaths];
|
|
161671
|
+
let opencode = false;
|
|
161672
|
+
for (const path5 of opencodePaths) {
|
|
161673
|
+
try {
|
|
161674
|
+
const config2 = parseConfig(path5);
|
|
161675
|
+
if (hasAftAtKeys(config2, ["plugin", "plugins", "mcp", "mcp_servers"])) {
|
|
161676
|
+
opencode = true;
|
|
161677
|
+
break;
|
|
161678
|
+
}
|
|
161679
|
+
} catch {}
|
|
161680
|
+
}
|
|
161681
|
+
let pi = false;
|
|
161682
|
+
for (const path5 of piPaths) {
|
|
161683
|
+
try {
|
|
161684
|
+
const config2 = parseConfig(path5);
|
|
161685
|
+
if (hasAftAtKeys(config2, ["packages", "extensions"])) {
|
|
161686
|
+
pi = true;
|
|
161687
|
+
break;
|
|
161688
|
+
}
|
|
161689
|
+
const agent = config2?.agent;
|
|
161690
|
+
if (hasAftAtKeys(agent, ["packages", "extensions"])) {
|
|
161691
|
+
pi = true;
|
|
161692
|
+
break;
|
|
161693
|
+
}
|
|
161694
|
+
} catch {}
|
|
161695
|
+
}
|
|
161696
|
+
const detected = opencode || pi;
|
|
161697
|
+
return {
|
|
161698
|
+
available: overrideAvailability ?? detected,
|
|
161699
|
+
opencode,
|
|
161700
|
+
pi,
|
|
161701
|
+
checkedPaths
|
|
161702
|
+
};
|
|
161703
|
+
}
|
|
161704
|
+
function isAftAvailable() {
|
|
161705
|
+
return getAftAvailability().available;
|
|
161267
161706
|
}
|
|
161268
161707
|
|
|
161269
|
-
// ../plugin/src/features/magic-context/key-files/
|
|
161270
|
-
|
|
161271
|
-
|
|
161272
|
-
|
|
161273
|
-
|
|
161274
|
-
|
|
161275
|
-
|
|
161276
|
-
const
|
|
161277
|
-
if (
|
|
161278
|
-
return
|
|
161279
|
-
|
|
161280
|
-
|
|
161708
|
+
// ../plugin/src/features/magic-context/key-files/read-history.ts
|
|
161709
|
+
import { realpathSync as realpathSync2 } from "node:fs";
|
|
161710
|
+
import { relative, resolve as resolve2 } from "node:path";
|
|
161711
|
+
function toMs(value) {
|
|
161712
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
161713
|
+
return value;
|
|
161714
|
+
if (typeof value === "string") {
|
|
161715
|
+
const n = Number(value);
|
|
161716
|
+
if (Number.isFinite(n))
|
|
161717
|
+
return n;
|
|
161718
|
+
const parsed = Date.parse(value);
|
|
161719
|
+
if (Number.isFinite(parsed))
|
|
161720
|
+
return parsed;
|
|
161721
|
+
}
|
|
161722
|
+
return 0;
|
|
161723
|
+
}
|
|
161724
|
+
function toPositiveInt(value) {
|
|
161725
|
+
const n = Number(value);
|
|
161726
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : null;
|
|
161727
|
+
}
|
|
161728
|
+
function coalesceRanges(ranges) {
|
|
161729
|
+
if (ranges.length === 0)
|
|
161281
161730
|
return [];
|
|
161731
|
+
const sorted = [...ranges].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
161732
|
+
const merged = [];
|
|
161733
|
+
for (const range of sorted) {
|
|
161734
|
+
const last = merged.at(-1);
|
|
161735
|
+
if (last && range.start <= last.end + 10) {
|
|
161736
|
+
last.end = Math.max(last.end, range.end);
|
|
161737
|
+
last.count += range.count;
|
|
161738
|
+
last.lastReadAt = Math.max(last.lastReadAt, range.lastReadAt);
|
|
161739
|
+
} else {
|
|
161740
|
+
merged.push({ ...range });
|
|
161741
|
+
}
|
|
161282
161742
|
}
|
|
161743
|
+
return merged.sort((a, b) => b.lastReadAt - a.lastReadAt || b.count - a.count).slice(0, 3);
|
|
161283
161744
|
}
|
|
161284
|
-
function
|
|
161745
|
+
function normalizeProjectRelativePath(projectPath, filePath) {
|
|
161746
|
+
const root = realpathSync2(projectPath);
|
|
161747
|
+
const abs = filePath.startsWith("/") ? resolve2(filePath) : resolve2(root, filePath);
|
|
161748
|
+
let real = abs;
|
|
161285
161749
|
try {
|
|
161286
|
-
|
|
161287
|
-
|
|
161288
|
-
}
|
|
161289
|
-
|
|
161750
|
+
real = realpathSync2(abs);
|
|
161751
|
+
} catch {}
|
|
161752
|
+
if (!real.startsWith(`${root}/`) && real !== root)
|
|
161753
|
+
return null;
|
|
161754
|
+
const rel = relative(root, real).replaceAll("\\", "/");
|
|
161755
|
+
if (!rel || rel.startsWith(".."))
|
|
161756
|
+
return null;
|
|
161757
|
+
return rel;
|
|
161758
|
+
}
|
|
161759
|
+
function primarySessionIds(db) {
|
|
161760
|
+
try {
|
|
161761
|
+
const rows = db.prepare("SELECT session_id AS sessionId FROM session_meta WHERE is_subagent = 0").all();
|
|
161762
|
+
return new Set(rows.map((row) => row.sessionId));
|
|
161763
|
+
} catch {
|
|
161764
|
+
return new Set;
|
|
161290
161765
|
}
|
|
161291
161766
|
}
|
|
161292
|
-
function
|
|
161293
|
-
const
|
|
161294
|
-
|
|
161295
|
-
|
|
161296
|
-
|
|
161767
|
+
function collectKeyFileCandidates(args) {
|
|
161768
|
+
const primaryIds = primarySessionIds(args.magicDb);
|
|
161769
|
+
if (primaryIds.size === 0)
|
|
161770
|
+
return [];
|
|
161771
|
+
const reads = args.openCodeDb.prepare(`SELECT p.session_id,
|
|
161772
|
+
json_extract(json_extract(p.data, '$.state'), '$.input.filePath') AS file_path,
|
|
161773
|
+
json_extract(json_extract(p.data, '$.state'), '$.input.startLine') AS start_line,
|
|
161774
|
+
json_extract(json_extract(p.data, '$.state'), '$.input.endLine') AS end_line,
|
|
161775
|
+
json_extract(json_extract(p.data, '$.state'), '$.input.offset') AS offset_value,
|
|
161776
|
+
json_extract(json_extract(p.data, '$.state'), '$.input.limit') AS limit_value,
|
|
161777
|
+
LENGTH(json_extract(json_extract(p.data, '$.state'), '$.output')) AS output_bytes,
|
|
161778
|
+
p.time_created
|
|
161779
|
+
FROM part p
|
|
161780
|
+
WHERE json_extract(p.data, '$.type') = 'tool'
|
|
161781
|
+
AND json_extract(p.data, '$.tool') = 'read'
|
|
161782
|
+
AND json_extract(json_extract(p.data, '$.state'), '$.input.filePath') IS NOT NULL`).all();
|
|
161783
|
+
const byPath = new Map;
|
|
161784
|
+
for (const row of reads) {
|
|
161785
|
+
if (!primaryIds.has(row.session_id) || !row.file_path)
|
|
161297
161786
|
continue;
|
|
161298
|
-
|
|
161787
|
+
const rel = normalizeProjectRelativePath(args.projectPath, row.file_path);
|
|
161788
|
+
if (!rel)
|
|
161299
161789
|
continue;
|
|
161300
|
-
|
|
161301
|
-
|
|
161302
|
-
|
|
161303
|
-
|
|
161790
|
+
const timestamp = toMs(row.time_created);
|
|
161791
|
+
const candidate = byPath.get(rel) ?? {
|
|
161792
|
+
path: rel,
|
|
161793
|
+
totalReads: 0,
|
|
161794
|
+
rangedReads: 0,
|
|
161795
|
+
fullReads: 0,
|
|
161796
|
+
editCount: 0,
|
|
161797
|
+
latestReadBytes: 0,
|
|
161798
|
+
firstReadAt: timestamp || Date.now(),
|
|
161799
|
+
lastReadAt: 0,
|
|
161800
|
+
ranges: [],
|
|
161801
|
+
rangeMap: new Map
|
|
161802
|
+
};
|
|
161803
|
+
candidate.totalReads++;
|
|
161804
|
+
candidate.firstReadAt = Math.min(candidate.firstReadAt, timestamp || candidate.firstReadAt);
|
|
161805
|
+
candidate.lastReadAt = Math.max(candidate.lastReadAt, timestamp);
|
|
161806
|
+
if (timestamp >= candidate.lastReadAt)
|
|
161807
|
+
candidate.latestReadBytes = Number(row.output_bytes ?? 0);
|
|
161808
|
+
const start = toPositiveInt(row.start_line) ?? toPositiveInt(row.offset_value);
|
|
161809
|
+
const explicitEnd = toPositiveInt(row.end_line);
|
|
161810
|
+
const limit = toPositiveInt(row.limit_value);
|
|
161811
|
+
const end = explicitEnd ?? (start && limit ? start + limit - 1 : null);
|
|
161812
|
+
if (start && end) {
|
|
161813
|
+
candidate.rangedReads++;
|
|
161814
|
+
const key = `${start}:${end}`;
|
|
161815
|
+
const existing = candidate.rangeMap.get(key);
|
|
161816
|
+
if (existing) {
|
|
161817
|
+
existing.count++;
|
|
161818
|
+
existing.lastReadAt = Math.max(existing.lastReadAt, timestamp);
|
|
161819
|
+
} else {
|
|
161820
|
+
candidate.rangeMap.set(key, { start, end, count: 1, lastReadAt: timestamp });
|
|
161821
|
+
}
|
|
161822
|
+
} else {
|
|
161823
|
+
candidate.fullReads++;
|
|
161824
|
+
}
|
|
161825
|
+
byPath.set(rel, candidate);
|
|
161826
|
+
}
|
|
161827
|
+
const edits = args.openCodeDb.prepare(`SELECT p.session_id,
|
|
161828
|
+
json_extract(json_extract(p.data, '$.state'), '$.input.filePath') AS file_path,
|
|
161829
|
+
COUNT(*) AS edit_count
|
|
161830
|
+
FROM part p
|
|
161831
|
+
WHERE json_extract(p.data, '$.type') = 'tool'
|
|
161832
|
+
AND json_extract(p.data, '$.tool') IN ('edit', 'write', 'mcp_edit', 'mcp_write')
|
|
161833
|
+
AND json_extract(json_extract(p.data, '$.state'), '$.input.filePath') IS NOT NULL
|
|
161834
|
+
GROUP BY p.session_id, file_path`).all();
|
|
161835
|
+
for (const row of edits) {
|
|
161836
|
+
if (!primaryIds.has(row.session_id) || !row.file_path)
|
|
161837
|
+
continue;
|
|
161838
|
+
const rel = normalizeProjectRelativePath(args.projectPath, row.file_path);
|
|
161839
|
+
if (!rel)
|
|
161840
|
+
continue;
|
|
161841
|
+
const candidate = byPath.get(rel);
|
|
161842
|
+
if (candidate)
|
|
161843
|
+
candidate.editCount += Number(row.edit_count ?? 0);
|
|
161304
161844
|
}
|
|
161305
|
-
return
|
|
161845
|
+
return [...byPath.values()].filter((candidate) => candidate.totalReads >= args.minReads).map(({ rangeMap, ...candidate }) => ({
|
|
161846
|
+
...candidate,
|
|
161847
|
+
ranges: coalesceRanges([...rangeMap.values()])
|
|
161848
|
+
})).sort((a, b) => b.totalReads - a.totalReads || b.lastReadAt - a.lastReadAt).slice(0, 200);
|
|
161306
161849
|
}
|
|
161307
161850
|
|
|
161851
|
+
// ../plugin/src/features/magic-context/key-files/storage-key-files.ts
|
|
161852
|
+
init_logger();
|
|
161853
|
+
|
|
161308
161854
|
// ../plugin/src/features/magic-context/key-files/identify-key-files.ts
|
|
161309
161855
|
var KEY_FILES_SYSTEM_PROMPT = "You are a file importance evaluator. Given read statistics about files in a coding session, identify which are core orientation files worth pinning in context. Return a JSON array.";
|
|
161310
|
-
|
|
161311
|
-
|
|
161856
|
+
class KeyFilesValidationError extends Error {
|
|
161857
|
+
constructor(message) {
|
|
161858
|
+
super(message);
|
|
161859
|
+
this.name = "KeyFilesValidationError";
|
|
161860
|
+
}
|
|
161861
|
+
}
|
|
161862
|
+
function computeGenerationConfigHash(config2) {
|
|
161863
|
+
return sha256(JSON.stringify({ budget: config2.token_budget, min_reads: config2.min_reads }));
|
|
161864
|
+
}
|
|
161865
|
+
function extractJsonObject(text) {
|
|
161866
|
+
const fenced = text.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
161867
|
+
if (fenced?.[1])
|
|
161868
|
+
return fenced[1];
|
|
161869
|
+
const object2 = text.match(/\{[\s\S]*\}/);
|
|
161870
|
+
if (object2?.[0])
|
|
161871
|
+
return object2[0];
|
|
161872
|
+
throw new KeyFilesValidationError("missing JSON object");
|
|
161873
|
+
}
|
|
161874
|
+
function formatCandidates(candidates) {
|
|
161875
|
+
return candidates.map((candidate) => {
|
|
161876
|
+
const ranges = candidate.ranges.map((range) => `${range.start}-${range.end} (${range.count}x)`).join(", ");
|
|
161877
|
+
return `| ${candidate.path} | ${candidate.totalReads} | ${new Date(candidate.lastReadAt).toISOString()} | ${ranges || "full reads"} |`;
|
|
161878
|
+
}).join(`
|
|
161879
|
+
`);
|
|
161880
|
+
}
|
|
161881
|
+
function buildV6KeyFilesPrompt(args) {
|
|
161882
|
+
const current = args.currentRows.map((row) => `- ${row.path}: ${row.localTokenEstimate} tokens, stale=${row.staleReason ?? "fresh"}`).join(`
|
|
161312
161883
|
`);
|
|
161313
|
-
return `## Identify
|
|
161884
|
+
return `## Task: Identify project key files
|
|
161314
161885
|
|
|
161315
|
-
|
|
161316
|
-
Identify which ones are **core orientation files** worth keeping permanently in context.
|
|
161886
|
+
You are deciding which files this project's primary agent should always see as orientation context. The injection budget is **${args.config.token_budget} tokens** for all files combined.
|
|
161317
161887
|
|
|
161318
|
-
|
|
161319
|
-
- Read many times across different phases of work (not clustered in one task)
|
|
161320
|
-
- Read without editing — consulted for understanding, not modification
|
|
161321
|
-
- Contains architecture, configuration, types, or key abstractions
|
|
161888
|
+
## Read history (primary sessions only, project-scoped)
|
|
161322
161889
|
|
|
161323
|
-
|
|
161324
|
-
|
|
161325
|
-
|
|
161326
|
-
- Test files, scripts, or generated files
|
|
161890
|
+
| path | total reads | last read | common line ranges |
|
|
161891
|
+
| --- | ---: | --- | --- |
|
|
161892
|
+
${formatCandidates(args.candidates)}
|
|
161327
161893
|
|
|
161328
|
-
|
|
161894
|
+
## Current key files (if any)
|
|
161329
161895
|
|
|
161330
|
-
|
|
161331
|
-
|
|
161896
|
+
${current || "(none)"}
|
|
161897
|
+
|
|
161898
|
+
## Tools available
|
|
161899
|
+
|
|
161900
|
+
- \`aft_outline(target=<path>)\` — get symbol outline for a file
|
|
161901
|
+
- \`aft_zoom(filePath=<path>, symbol=<name>)\` — get full source of one symbol
|
|
161902
|
+
|
|
161903
|
+
## Output (strict JSON)
|
|
161332
161904
|
|
|
161333
|
-
### Output Format
|
|
161334
|
-
Return a JSON array ranked by importance (most important first):
|
|
161335
161905
|
\`\`\`json
|
|
161336
|
-
|
|
161337
|
-
|
|
161338
|
-
|
|
161906
|
+
{
|
|
161907
|
+
"no_change": false,
|
|
161908
|
+
"files": [
|
|
161909
|
+
{
|
|
161910
|
+
"path": "src/example.ts",
|
|
161911
|
+
"content": "...outline + symbol bodies stitched from src/example.ts only...",
|
|
161912
|
+
"approx_token_estimate": 1200
|
|
161913
|
+
}
|
|
161914
|
+
]
|
|
161915
|
+
}
|
|
161339
161916
|
\`\`\`
|
|
161340
161917
|
|
|
161341
|
-
|
|
161918
|
+
If current key files are still optimal, return \`{ "no_change": true, "files": [] }\`.
|
|
161919
|
+
|
|
161920
|
+
Rules:
|
|
161921
|
+
- Each row's content MUST come from ONLY the one file at path. If two files matter, emit two rows.
|
|
161922
|
+
- Do NOT emit source_files. One row = one file = one hash.
|
|
161923
|
+
- Total approx_token_estimate must be < ${args.config.token_budget}.
|
|
161924
|
+
- path must be relative, no .., no absolute paths, no symlinks escaping project root.
|
|
161925
|
+
- path must be unique case-sensitively and case-insensitively.
|
|
161926
|
+
- content must be plain text; no XML tags inside.`;
|
|
161927
|
+
}
|
|
161928
|
+
function validateLlmOutput(raw, config2, projectPath) {
|
|
161929
|
+
let obj;
|
|
161930
|
+
try {
|
|
161931
|
+
obj = JSON.parse(extractJsonObject(raw));
|
|
161932
|
+
} catch (error51) {
|
|
161933
|
+
if (error51 instanceof KeyFilesValidationError)
|
|
161934
|
+
throw error51;
|
|
161935
|
+
throw new KeyFilesValidationError(`invalid JSON: ${getErrorMessage(error51)}`);
|
|
161936
|
+
}
|
|
161937
|
+
if (!obj || typeof obj !== "object")
|
|
161938
|
+
throw new KeyFilesValidationError("output must be object");
|
|
161939
|
+
const record2 = obj;
|
|
161940
|
+
if (typeof record2.no_change !== "boolean")
|
|
161941
|
+
throw new KeyFilesValidationError("missing no_change");
|
|
161942
|
+
if (!Array.isArray(record2.files))
|
|
161943
|
+
throw new KeyFilesValidationError("missing files array");
|
|
161944
|
+
if (record2.no_change && record2.files.length > 0)
|
|
161945
|
+
throw new KeyFilesValidationError("no_change=true with files");
|
|
161946
|
+
if (!record2.no_change && record2.files.length === 0)
|
|
161947
|
+
throw new KeyFilesValidationError("no_change=false with empty files");
|
|
161948
|
+
const seen = new Set;
|
|
161949
|
+
const seenLower = new Set;
|
|
161950
|
+
const files = [];
|
|
161951
|
+
for (const item of record2.files) {
|
|
161952
|
+
if (!item || typeof item !== "object")
|
|
161953
|
+
throw new KeyFilesValidationError("bad file entry");
|
|
161954
|
+
const file2 = item;
|
|
161955
|
+
if ("source_files" in file2) {
|
|
161956
|
+
throw new KeyFilesValidationError(`source_files field not allowed (one file per row): ${String(file2.path)}`);
|
|
161957
|
+
}
|
|
161958
|
+
if (typeof file2.path !== "string" || file2.path.length === 0)
|
|
161959
|
+
throw new KeyFilesValidationError("bad path");
|
|
161960
|
+
if (file2.path.startsWith("/") || file2.path.includes(".."))
|
|
161961
|
+
throw new KeyFilesValidationError(`escape: ${file2.path}`);
|
|
161962
|
+
if (seen.has(file2.path))
|
|
161963
|
+
throw new KeyFilesValidationError(`dup path: ${file2.path}`);
|
|
161964
|
+
if (seenLower.has(file2.path.toLowerCase()))
|
|
161965
|
+
throw new KeyFilesValidationError(`case-dup: ${file2.path}`);
|
|
161966
|
+
seen.add(file2.path);
|
|
161967
|
+
seenLower.add(file2.path.toLowerCase());
|
|
161968
|
+
if (!isRelativeProjectFile(projectPath, file2.path))
|
|
161969
|
+
throw new KeyFilesValidationError(`unreadable: ${file2.path}`);
|
|
161970
|
+
if (typeof file2.content !== "string")
|
|
161971
|
+
throw new KeyFilesValidationError(`bad content: ${file2.path}`);
|
|
161972
|
+
if (file2.content.length > 1e5)
|
|
161973
|
+
throw new KeyFilesValidationError(`content >100KB: ${file2.path}`);
|
|
161974
|
+
if (typeof file2.approx_token_estimate !== "number" || file2.approx_token_estimate < 0) {
|
|
161975
|
+
throw new KeyFilesValidationError(`bad token estimate: ${file2.path}`);
|
|
161976
|
+
}
|
|
161977
|
+
const local = estimateTokens(file2.content);
|
|
161978
|
+
if (file2.approx_token_estimate > 0 && (local / file2.approx_token_estimate > 1.5 || local / file2.approx_token_estimate < 0.5)) {
|
|
161979
|
+
log(`key-files: token estimate divergence for ${file2.path}: claimed=${file2.approx_token_estimate}, plugin=${local}`);
|
|
161980
|
+
}
|
|
161981
|
+
files.push({
|
|
161982
|
+
path: file2.path,
|
|
161983
|
+
content: file2.content,
|
|
161984
|
+
approx_token_estimate: file2.approx_token_estimate,
|
|
161985
|
+
local_token_estimate: local
|
|
161986
|
+
});
|
|
161987
|
+
}
|
|
161988
|
+
const total = files.reduce((sum, file2) => sum + file2.local_token_estimate, 0);
|
|
161989
|
+
if (total > config2.token_budget)
|
|
161990
|
+
throw new KeyFilesValidationError(`total ${total} > budget ${config2.token_budget}`);
|
|
161991
|
+
return { no_change: record2.no_change, files };
|
|
161342
161992
|
}
|
|
161343
|
-
function
|
|
161344
|
-
|
|
161345
|
-
|
|
161346
|
-
|
|
161993
|
+
function commitKeyFiles(args) {
|
|
161994
|
+
if (args.validated.no_change)
|
|
161995
|
+
return null;
|
|
161996
|
+
const projectPath = resolveProjectPath(args.projectPath) ?? args.projectPath;
|
|
161997
|
+
const resolved = resolveCommitFiles(projectPath, args.validated.files.map((file2) => ({
|
|
161998
|
+
path: file2.path,
|
|
161999
|
+
content: file2.content,
|
|
162000
|
+
localTokenEstimate: file2.local_token_estimate
|
|
162001
|
+
})));
|
|
162002
|
+
const generatedAt = Date.now();
|
|
162003
|
+
const bump = args.bumpVersion ?? bumpKeyFilesVersion;
|
|
162004
|
+
args.db.exec("BEGIN IMMEDIATE");
|
|
162005
|
+
let committed = false;
|
|
161347
162006
|
try {
|
|
161348
|
-
|
|
161349
|
-
|
|
161350
|
-
|
|
161351
|
-
|
|
161352
|
-
|
|
161353
|
-
|
|
161354
|
-
|
|
162007
|
+
if (!peekLeaseHolderAndExpiry(args.db, args.leaseHolderId)) {
|
|
162008
|
+
log(`key-files commit aborted: lease lost (holder ${args.leaseHolderId})`);
|
|
162009
|
+
return null;
|
|
162010
|
+
}
|
|
162011
|
+
args.db.prepare("DELETE FROM project_key_files WHERE project_path = ?").run(projectPath);
|
|
162012
|
+
insertResolvedKeyFiles(args.db, projectPath, resolved, generatedAt, args.modelId, args.configHash);
|
|
162013
|
+
const version2 = bump(args.db, projectPath);
|
|
162014
|
+
args.db.exec("COMMIT");
|
|
162015
|
+
committed = true;
|
|
162016
|
+
log(`key-files committed: ${resolved.length} files, version=${version2}, ${resolved.filter((r) => r.staleReason).length} pre-stale`);
|
|
162017
|
+
return version2;
|
|
162018
|
+
} finally {
|
|
162019
|
+
if (!committed) {
|
|
162020
|
+
try {
|
|
162021
|
+
args.db.exec("ROLLBACK");
|
|
162022
|
+
} catch {}
|
|
162023
|
+
}
|
|
162024
|
+
}
|
|
162025
|
+
}
|
|
162026
|
+
async function runKeyFilesLlm(args) {
|
|
162027
|
+
const createResponse = await args.client.session.create({
|
|
162028
|
+
body: {
|
|
162029
|
+
...args.parentSessionId ? { parentID: args.parentSessionId } : {},
|
|
162030
|
+
title: "magic-context-dream-key-files-v6"
|
|
162031
|
+
},
|
|
162032
|
+
query: { directory: args.projectPath }
|
|
162033
|
+
});
|
|
162034
|
+
const created = normalizeSDKResponse(createResponse, null, {
|
|
162035
|
+
preferResponseOnMissingData: true
|
|
162036
|
+
});
|
|
162037
|
+
const agentSessionId = typeof created?.id === "string" ? created.id : null;
|
|
162038
|
+
if (!agentSessionId)
|
|
162039
|
+
throw new Error("Could not create key-file identification session.");
|
|
162040
|
+
try {
|
|
162041
|
+
await promptSyncWithModelSuggestionRetry(args.client, {
|
|
162042
|
+
path: { id: agentSessionId },
|
|
162043
|
+
query: { directory: args.projectPath },
|
|
162044
|
+
body: {
|
|
162045
|
+
agent: DREAMER_AGENT,
|
|
162046
|
+
system: KEY_FILES_SYSTEM_PROMPT,
|
|
162047
|
+
parts: [{ type: "text", text: args.prompt, synthetic: true }]
|
|
162048
|
+
}
|
|
162049
|
+
}, {
|
|
162050
|
+
timeoutMs: Math.min(Math.max(0, args.deadline - Date.now()), 5 * 60 * 1000),
|
|
162051
|
+
fallbackModels: args.fallbackModels,
|
|
162052
|
+
callContext: "dreamer:key-files-v6"
|
|
162053
|
+
});
|
|
162054
|
+
const messagesResponse = await args.client.session.messages({
|
|
162055
|
+
path: { id: agentSessionId },
|
|
162056
|
+
query: { directory: args.projectPath }
|
|
162057
|
+
});
|
|
162058
|
+
const messages = normalizeSDKResponse(messagesResponse, [], {
|
|
162059
|
+
preferResponseOnMissingData: true
|
|
162060
|
+
});
|
|
162061
|
+
const text = extractLatestAssistantText(messages);
|
|
162062
|
+
if (!text)
|
|
162063
|
+
throw new Error("Dreamer returned no key-files output.");
|
|
162064
|
+
return text;
|
|
162065
|
+
} finally {
|
|
162066
|
+
await args.client.session.delete({ path: { id: agentSessionId } }).catch(() => {
|
|
162067
|
+
return;
|
|
162068
|
+
});
|
|
161355
162069
|
}
|
|
161356
162070
|
}
|
|
161357
|
-
function
|
|
161358
|
-
|
|
161359
|
-
|
|
161360
|
-
|
|
161361
|
-
|
|
161362
|
-
}
|
|
161363
|
-
|
|
161364
|
-
const
|
|
161365
|
-
const
|
|
161366
|
-
|
|
161367
|
-
|
|
161368
|
-
|
|
161369
|
-
|
|
161370
|
-
}
|
|
161371
|
-
|
|
161372
|
-
|
|
161373
|
-
|
|
161374
|
-
|
|
161375
|
-
|
|
161376
|
-
|
|
161377
|
-
|
|
161378
|
-
|
|
161379
|
-
|
|
161380
|
-
|
|
161381
|
-
|
|
162071
|
+
async function runKeyFilesTask(args) {
|
|
162072
|
+
if (!args.config.enabled)
|
|
162073
|
+
return { committedVersion: null, candidates: 0, noChange: false };
|
|
162074
|
+
if (!isAftAvailable()) {
|
|
162075
|
+
log("[key-files] AFT not available, skipping key-files task");
|
|
162076
|
+
return { committedVersion: null, candidates: 0, noChange: false };
|
|
162077
|
+
}
|
|
162078
|
+
const projectPath = resolveProjectPath(args.projectPath) ?? args.projectPath;
|
|
162079
|
+
const candidates = collectKeyFileCandidates({
|
|
162080
|
+
openCodeDb: args.openCodeDb,
|
|
162081
|
+
magicDb: args.db,
|
|
162082
|
+
projectPath,
|
|
162083
|
+
minReads: args.config.min_reads
|
|
162084
|
+
});
|
|
162085
|
+
if (candidates.length === 0)
|
|
162086
|
+
return { committedVersion: null, candidates: 0, noChange: false };
|
|
162087
|
+
const currentRows = readCurrentKeyFiles(args.db, projectPath);
|
|
162088
|
+
const configHash = computeGenerationConfigHash(args.config);
|
|
162089
|
+
const allRowsFreshAndCurrent = currentRows.length > 0 && currentRows.every((row) => {
|
|
162090
|
+
if (row.staleReason !== null || row.generationConfigHash !== configHash)
|
|
162091
|
+
return false;
|
|
162092
|
+
try {
|
|
162093
|
+
return sha256(readFileSync4(join10(projectPath, row.path))) === row.contentHash;
|
|
162094
|
+
} catch {
|
|
162095
|
+
return false;
|
|
162096
|
+
}
|
|
162097
|
+
});
|
|
162098
|
+
const currentPaths = new Set(currentRows.map((row) => row.path));
|
|
162099
|
+
if (allRowsFreshAndCurrent && candidates.every((candidate) => currentPaths.has(candidate.path))) {
|
|
162100
|
+
log(`key-files: no_change short-circuit (${currentRows.length} rows fresh)`);
|
|
162101
|
+
return { committedVersion: null, candidates: candidates.length, noChange: true };
|
|
162102
|
+
}
|
|
162103
|
+
const prompt = buildV6KeyFilesPrompt({ candidates, currentRows, config: args.config });
|
|
162104
|
+
let validated;
|
|
162105
|
+
const leaseInterval = setInterval(() => {
|
|
162106
|
+
try {
|
|
162107
|
+
if (!renewLease(args.db, args.holderId)) {
|
|
162108
|
+
log("[key-files] lease renewal failed during LLM call");
|
|
162109
|
+
}
|
|
162110
|
+
} catch (error51) {
|
|
162111
|
+
log(`[key-files] lease renewal threw: ${getErrorMessage(error51)}`);
|
|
162112
|
+
}
|
|
162113
|
+
}, 60000);
|
|
162114
|
+
try {
|
|
162115
|
+
try {
|
|
162116
|
+
const raw = await runKeyFilesLlm({
|
|
162117
|
+
client: args.client,
|
|
162118
|
+
parentSessionId: args.parentSessionId,
|
|
162119
|
+
projectPath,
|
|
162120
|
+
prompt,
|
|
162121
|
+
deadline: args.deadline,
|
|
162122
|
+
fallbackModels: args.fallbackModels
|
|
162123
|
+
});
|
|
162124
|
+
validated = validateLlmOutput(raw, args.config, projectPath);
|
|
162125
|
+
} catch (error51) {
|
|
162126
|
+
log(`[key-files] LLM validation failed: ${getErrorMessage(error51)}`);
|
|
162127
|
+
throw error51;
|
|
162128
|
+
}
|
|
162129
|
+
if (validated.no_change)
|
|
162130
|
+
return { committedVersion: null, candidates: candidates.length, noChange: true };
|
|
162131
|
+
const committedVersion = commitKeyFiles({
|
|
162132
|
+
db: args.db,
|
|
162133
|
+
projectPath,
|
|
162134
|
+
validated,
|
|
162135
|
+
configHash,
|
|
162136
|
+
modelId: args.fallbackModels?.[0] ?? "dreamer",
|
|
162137
|
+
leaseHolderId: args.holderId
|
|
162138
|
+
});
|
|
162139
|
+
renewLease(args.db, args.holderId);
|
|
162140
|
+
return { committedVersion, candidates: candidates.length, noChange: false };
|
|
162141
|
+
} finally {
|
|
162142
|
+
clearInterval(leaseInterval);
|
|
162143
|
+
}
|
|
161382
162144
|
}
|
|
161383
162145
|
|
|
161384
162146
|
// ../plugin/src/features/magic-context/memory/embedding-cache.ts
|
|
@@ -162442,11 +163204,11 @@ function logWithStackHead(message, stackHead) {
|
|
|
162442
163204
|
log(message, stackHead ? { stackHead } : undefined);
|
|
162443
163205
|
}
|
|
162444
163206
|
function getOpenCodeDbPath2() {
|
|
162445
|
-
return
|
|
163207
|
+
return join11(getDataDir(), "opencode", "opencode.db");
|
|
162446
163208
|
}
|
|
162447
163209
|
function openOpenCodeDb() {
|
|
162448
163210
|
const dbPath = getOpenCodeDbPath2();
|
|
162449
|
-
if (!
|
|
163211
|
+
if (!existsSync8(dbPath)) {
|
|
162450
163212
|
log(`[key-files] OpenCode DB not found at ${dbPath} — skipping`);
|
|
162451
163213
|
return null;
|
|
162452
163214
|
}
|
|
@@ -162459,178 +163221,6 @@ function openOpenCodeDb() {
|
|
|
162459
163221
|
return null;
|
|
162460
163222
|
}
|
|
162461
163223
|
}
|
|
162462
|
-
function isSessionIdRow(row) {
|
|
162463
|
-
if (row === null || typeof row !== "object") {
|
|
162464
|
-
return false;
|
|
162465
|
-
}
|
|
162466
|
-
return typeof row.sessionId === "string";
|
|
162467
|
-
}
|
|
162468
|
-
function hasExplicitEmptyKeyFilesOutput(text) {
|
|
162469
|
-
return /```(?:json)?\s*\[\s*\]\s*```/s.test(text) || /^\s*\[\s*\]\s*$/s.test(text);
|
|
162470
|
-
}
|
|
162471
|
-
async function getActiveProjectSessionIds(args) {
|
|
162472
|
-
try {
|
|
162473
|
-
const { withReadOnlySessionDb: withReadOnlySessionDb2 } = await init_read_session_db().then(() => exports_read_session_db);
|
|
162474
|
-
const projectSessionIds = withReadOnlySessionDb2((openCodeDb) => {
|
|
162475
|
-
const bareIdentity = args.projectIdentity.replace(/^git:/, "");
|
|
162476
|
-
const rows = openCodeDb.prepare("SELECT id FROM session WHERE project_id = ? AND parent_id IS NULL ORDER BY time_updated DESC").all(bareIdentity);
|
|
162477
|
-
return new Set(rows.map((r) => r.id));
|
|
162478
|
-
});
|
|
162479
|
-
if (projectSessionIds.size === 0) {
|
|
162480
|
-
return [];
|
|
162481
|
-
}
|
|
162482
|
-
return args.db.prepare("SELECT session_id AS sessionId FROM session_meta WHERE is_subagent = 0 ORDER BY session_id ASC").all().filter(isSessionIdRow).map((row) => row.sessionId).filter((sessionId) => projectSessionIds.has(sessionId));
|
|
162483
|
-
} catch (error51) {
|
|
162484
|
-
sessionLog(args.projectIdentity, `key-files: OpenCode DB lookup failed, falling back to SDK list: ${getErrorMessage(error51)}`);
|
|
162485
|
-
const listResponse = await args.client.session.list({
|
|
162486
|
-
query: { directory: args.sessionDirectory ?? args.projectIdentity }
|
|
162487
|
-
});
|
|
162488
|
-
const sessions = normalizeSDKResponse(listResponse, [], {
|
|
162489
|
-
preferResponseOnMissingData: true
|
|
162490
|
-
});
|
|
162491
|
-
const projectSessionIds = new Set(sessions.map((session) => typeof session?.id === "string" ? session.id : null).filter((sessionId) => Boolean(sessionId)));
|
|
162492
|
-
if (projectSessionIds.size === 0) {
|
|
162493
|
-
return [];
|
|
162494
|
-
}
|
|
162495
|
-
return args.db.prepare("SELECT session_id AS sessionId FROM session_meta WHERE is_subagent = 0 ORDER BY session_id ASC").all().filter(isSessionIdRow).map((row) => row.sessionId).filter((sessionId) => projectSessionIds.has(sessionId));
|
|
162496
|
-
}
|
|
162497
|
-
}
|
|
162498
|
-
async function identifyKeyFilesForSession(args) {
|
|
162499
|
-
let openCodeDb = null;
|
|
162500
|
-
try {
|
|
162501
|
-
openCodeDb = openOpenCodeDb();
|
|
162502
|
-
if (!openCodeDb) {
|
|
162503
|
-
return;
|
|
162504
|
-
}
|
|
162505
|
-
const candidates = getKeyFileCandidates(openCodeDb, args.sessionId, args.config.min_reads, args.config.token_budget, args.sessionDirectory);
|
|
162506
|
-
if (candidates.length === 0) {
|
|
162507
|
-
log(`[key-files][${args.sessionId}] no candidates found — skipping`);
|
|
162508
|
-
return;
|
|
162509
|
-
}
|
|
162510
|
-
const prompt = buildKeyFilesPrompt(candidates, args.config.token_budget, args.config.min_reads);
|
|
162511
|
-
const applyHeuristicFallback = () => {
|
|
162512
|
-
heuristicKeyFileSelection(args.db, args.sessionId, candidates, args.config.token_budget);
|
|
162513
|
-
};
|
|
162514
|
-
let agentSessionId = null;
|
|
162515
|
-
const abortController = new AbortController;
|
|
162516
|
-
const leaseInterval = setInterval(() => {
|
|
162517
|
-
try {
|
|
162518
|
-
if (!renewLease(args.db, args.holderId)) {
|
|
162519
|
-
log(`[key-files][${args.sessionId}] lease renewal failed — aborting`);
|
|
162520
|
-
abortController.abort();
|
|
162521
|
-
}
|
|
162522
|
-
} catch {
|
|
162523
|
-
abortController.abort();
|
|
162524
|
-
}
|
|
162525
|
-
}, 60000);
|
|
162526
|
-
try {
|
|
162527
|
-
const createResponse = await args.client.session.create({
|
|
162528
|
-
body: {
|
|
162529
|
-
...args.parentSessionId ? { parentID: args.parentSessionId } : {},
|
|
162530
|
-
title: `magic-context-dream-key-files-${args.sessionId.slice(0, 12)}`
|
|
162531
|
-
},
|
|
162532
|
-
query: { directory: args.sessionDirectory }
|
|
162533
|
-
});
|
|
162534
|
-
const created = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
|
|
162535
|
-
agentSessionId = typeof created?.id === "string" ? created.id : null;
|
|
162536
|
-
if (!agentSessionId) {
|
|
162537
|
-
throw new Error("Could not create key-file identification session.");
|
|
162538
|
-
}
|
|
162539
|
-
log(`[key-files][${args.sessionId}] child session created ${agentSessionId}`);
|
|
162540
|
-
const remainingMs = Math.max(0, args.deadline - Date.now());
|
|
162541
|
-
await promptSyncWithModelSuggestionRetry(args.client, {
|
|
162542
|
-
path: { id: agentSessionId },
|
|
162543
|
-
query: { directory: args.sessionDirectory },
|
|
162544
|
-
body: {
|
|
162545
|
-
agent: DREAMER_AGENT,
|
|
162546
|
-
system: KEY_FILES_SYSTEM_PROMPT,
|
|
162547
|
-
parts: [{ type: "text", text: prompt, synthetic: true }]
|
|
162548
|
-
}
|
|
162549
|
-
}, {
|
|
162550
|
-
timeoutMs: Math.min(remainingMs, 300000),
|
|
162551
|
-
signal: abortController.signal,
|
|
162552
|
-
fallbackModels: args.fallbackModels,
|
|
162553
|
-
callContext: `dreamer:key-files:${args.sessionId.slice(0, 12)}`
|
|
162554
|
-
});
|
|
162555
|
-
const messagesResponse = await args.client.session.messages({
|
|
162556
|
-
path: { id: agentSessionId },
|
|
162557
|
-
query: { directory: args.sessionDirectory }
|
|
162558
|
-
});
|
|
162559
|
-
const messages = normalizeSDKResponse(messagesResponse, [], {
|
|
162560
|
-
preferResponseOnMissingData: true
|
|
162561
|
-
});
|
|
162562
|
-
const responseText = extractLatestAssistantText(messages);
|
|
162563
|
-
if (!responseText) {
|
|
162564
|
-
log(`[key-files][${args.sessionId}] no response from agent — using heuristic fallback`);
|
|
162565
|
-
applyHeuristicFallback();
|
|
162566
|
-
return;
|
|
162567
|
-
}
|
|
162568
|
-
const parsed = parseKeyFilesOutput(responseText);
|
|
162569
|
-
if (parsed.length > 0 || hasExplicitEmptyKeyFilesOutput(responseText)) {
|
|
162570
|
-
const candidatePaths = new Set(candidates.map((c) => c.filePath));
|
|
162571
|
-
applyKeyFileResults(args.db, args.sessionId, parsed, args.config.token_budget, candidatePaths);
|
|
162572
|
-
return;
|
|
162573
|
-
}
|
|
162574
|
-
log(`[key-files][${args.sessionId}] could not parse agent output — using heuristic fallback`);
|
|
162575
|
-
applyHeuristicFallback();
|
|
162576
|
-
} catch (error51) {
|
|
162577
|
-
log(`[key-files][${args.sessionId}] identification failed: ${getErrorMessage(error51)} — using heuristic fallback`);
|
|
162578
|
-
try {
|
|
162579
|
-
applyHeuristicFallback();
|
|
162580
|
-
} catch (fallbackError) {
|
|
162581
|
-
log(`[key-files][${args.sessionId}] heuristic fallback failed: ${getErrorMessage(fallbackError)}`);
|
|
162582
|
-
}
|
|
162583
|
-
} finally {
|
|
162584
|
-
clearInterval(leaseInterval);
|
|
162585
|
-
if (agentSessionId) {
|
|
162586
|
-
await args.client.session.delete({
|
|
162587
|
-
path: { id: agentSessionId },
|
|
162588
|
-
query: { directory: args.sessionDirectory }
|
|
162589
|
-
}).catch((error51) => {
|
|
162590
|
-
log(`[key-files][${args.sessionId}] session cleanup failed: ${getErrorMessage(error51)}`);
|
|
162591
|
-
});
|
|
162592
|
-
}
|
|
162593
|
-
}
|
|
162594
|
-
} finally {
|
|
162595
|
-
if (openCodeDb) {
|
|
162596
|
-
try {
|
|
162597
|
-
closeQuietly(openCodeDb);
|
|
162598
|
-
} catch (error51) {
|
|
162599
|
-
log(`[key-files][${args.sessionId}] failed to close OpenCode DB: ${getErrorMessage(error51)}`);
|
|
162600
|
-
}
|
|
162601
|
-
}
|
|
162602
|
-
}
|
|
162603
|
-
}
|
|
162604
|
-
async function identifyKeyFiles(args) {
|
|
162605
|
-
const sessionIds = await getActiveProjectSessionIds({
|
|
162606
|
-
db: args.db,
|
|
162607
|
-
client: args.client,
|
|
162608
|
-
projectIdentity: args.projectIdentity,
|
|
162609
|
-
sessionDirectory: args.sessionDirectory
|
|
162610
|
-
});
|
|
162611
|
-
if (sessionIds.length === 0) {
|
|
162612
|
-
log(`[key-files] no active sessions found for ${args.projectIdentity}`);
|
|
162613
|
-
return;
|
|
162614
|
-
}
|
|
162615
|
-
log(`[key-files] evaluating ${sessionIds.length} active session(s) for ${args.projectIdentity}`);
|
|
162616
|
-
for (const sessionId of sessionIds) {
|
|
162617
|
-
if (Date.now() > args.deadline) {
|
|
162618
|
-
log("[key-files] deadline reached — stopping key-file identification");
|
|
162619
|
-
break;
|
|
162620
|
-
}
|
|
162621
|
-
await identifyKeyFilesForSession({
|
|
162622
|
-
db: args.db,
|
|
162623
|
-
client: args.client,
|
|
162624
|
-
parentSessionId: args.parentSessionId,
|
|
162625
|
-
sessionDirectory: args.sessionDirectory,
|
|
162626
|
-
holderId: args.holderId,
|
|
162627
|
-
fallbackModels: args.fallbackModels,
|
|
162628
|
-
deadline: args.deadline,
|
|
162629
|
-
sessionId,
|
|
162630
|
-
config: args.config
|
|
162631
|
-
});
|
|
162632
|
-
}
|
|
162633
|
-
}
|
|
162634
163224
|
async function runDream(args) {
|
|
162635
163225
|
const holderId = crypto.randomUUID();
|
|
162636
163226
|
const startedAt = Date.now();
|
|
@@ -162681,8 +163271,50 @@ async function runDream(args) {
|
|
|
162681
163271
|
let lastErrorSignature = null;
|
|
162682
163272
|
let consecutiveSameErrorFailures = 0;
|
|
162683
163273
|
let circuitBreakerTripped = false;
|
|
163274
|
+
let lostLease = false;
|
|
163275
|
+
let lostLeaseReason = null;
|
|
163276
|
+
let lostLeaseRecorded = false;
|
|
163277
|
+
const markLeaseLost = (phase, error51) => {
|
|
163278
|
+
const detail = error51 ? `: ${getErrorMessage(error51)}` : "";
|
|
163279
|
+
lostLeaseReason = `Dream lease lost during ${phase}${detail}`;
|
|
163280
|
+
if (!lostLease) {
|
|
163281
|
+
log(`[dreamer] FATAL: ${lostLeaseReason}; aborting all remaining dream work`);
|
|
163282
|
+
} else {
|
|
163283
|
+
log(`[dreamer] FATAL: ${lostLeaseReason}; dream work is already aborting`);
|
|
163284
|
+
}
|
|
163285
|
+
lostLease = true;
|
|
163286
|
+
};
|
|
163287
|
+
const recordLeaseLostTask = (phase) => {
|
|
163288
|
+
if (lostLeaseRecorded)
|
|
163289
|
+
return;
|
|
163290
|
+
lostLeaseRecorded = true;
|
|
163291
|
+
result.tasks.push({
|
|
163292
|
+
name: "lease-lost",
|
|
163293
|
+
durationMs: 0,
|
|
163294
|
+
result: "",
|
|
163295
|
+
error: lostLeaseReason ?? `Dream lease lost during ${phase}; aborted remaining work`
|
|
163296
|
+
});
|
|
163297
|
+
};
|
|
163298
|
+
const verifyLeaseStillHeld = (phase) => {
|
|
163299
|
+
if (lostLease)
|
|
163300
|
+
return false;
|
|
163301
|
+
try {
|
|
163302
|
+
if (!renewLease(args.db, holderId)) {
|
|
163303
|
+
markLeaseLost(phase);
|
|
163304
|
+
return false;
|
|
163305
|
+
}
|
|
163306
|
+
return true;
|
|
163307
|
+
} catch (error51) {
|
|
163308
|
+
markLeaseLost(phase, error51);
|
|
163309
|
+
return false;
|
|
163310
|
+
}
|
|
163311
|
+
};
|
|
162684
163312
|
try {
|
|
162685
163313
|
for (const taskName of args.tasks) {
|
|
163314
|
+
if (!verifyLeaseStillHeld(`before task ${taskName}`)) {
|
|
163315
|
+
recordLeaseLostTask(`before task ${taskName}`);
|
|
163316
|
+
break;
|
|
163317
|
+
}
|
|
162686
163318
|
if (Date.now() > deadline) {
|
|
162687
163319
|
log(`[dreamer] deadline reached, stopping after ${result.tasks.length} tasks`);
|
|
162688
163320
|
break;
|
|
@@ -162695,18 +163327,20 @@ async function runDream(args) {
|
|
|
162695
163327
|
try {
|
|
162696
163328
|
if (!renewLease(args.db, holderId)) {
|
|
162697
163329
|
log(`[dreamer] task ${taskName}: lease renewal failed — aborting LLM call`);
|
|
163330
|
+
markLeaseLost(`task ${taskName} lease renewal`);
|
|
162698
163331
|
taskAbortController.abort();
|
|
162699
163332
|
}
|
|
162700
163333
|
} catch (err) {
|
|
162701
163334
|
log(`[dreamer] task ${taskName}: lease renewal threw — aborting LLM call: ${err}`);
|
|
163335
|
+
markLeaseLost(`task ${taskName} lease renewal`, err);
|
|
162702
163336
|
taskAbortController.abort();
|
|
162703
163337
|
}
|
|
162704
163338
|
}, 60000);
|
|
162705
163339
|
try {
|
|
162706
163340
|
const docsDir = args.sessionDirectory ?? args.projectIdentity;
|
|
162707
163341
|
const existingDocs = taskName === "maintain-docs" ? {
|
|
162708
|
-
architecture:
|
|
162709
|
-
structure:
|
|
163342
|
+
architecture: existsSync8(join11(docsDir, "ARCHITECTURE.md")),
|
|
163343
|
+
structure: existsSync8(join11(docsDir, "STRUCTURE.md"))
|
|
162710
163344
|
} : undefined;
|
|
162711
163345
|
const userMemories = taskName === "archive-stale" ? getActiveUserMemories(args.db).map((um) => ({
|
|
162712
163346
|
id: um.id,
|
|
@@ -162745,6 +163379,9 @@ async function runDream(args) {
|
|
|
162745
163379
|
fallbackModels: args.fallbackModels,
|
|
162746
163380
|
callContext: `dreamer:${taskName}`
|
|
162747
163381
|
});
|
|
163382
|
+
if (lostLease) {
|
|
163383
|
+
throw new Error(lostLeaseReason ?? `Dream lease lost during ${taskName}`);
|
|
163384
|
+
}
|
|
162748
163385
|
const messagesResponse = await args.client.session.messages({
|
|
162749
163386
|
path: { id: agentSessionId },
|
|
162750
163387
|
query: { directory: args.sessionDirectory ?? args.projectIdentity }
|
|
@@ -162775,7 +163412,10 @@ async function runDream(args) {
|
|
|
162775
163412
|
result: null,
|
|
162776
163413
|
error: errorDescription.brief
|
|
162777
163414
|
});
|
|
162778
|
-
if (
|
|
163415
|
+
if (lostLease) {
|
|
163416
|
+
lastErrorSignature = null;
|
|
163417
|
+
consecutiveSameErrorFailures = 0;
|
|
163418
|
+
} else if (shouldSkipCircuitBreaker(error51, errorDescription.brief)) {
|
|
162779
163419
|
lastErrorSignature = null;
|
|
162780
163420
|
consecutiveSameErrorFailures = 0;
|
|
162781
163421
|
} else {
|
|
@@ -162801,18 +163441,24 @@ async function runDream(args) {
|
|
|
162801
163441
|
clearInterval(leaseRenewalInterval);
|
|
162802
163442
|
if (agentSessionId) {
|
|
162803
163443
|
await args.client.session.delete({
|
|
162804
|
-
path: { id: agentSessionId }
|
|
162805
|
-
query: { directory: args.sessionDirectory ?? args.projectIdentity }
|
|
163444
|
+
path: { id: agentSessionId }
|
|
162806
163445
|
}).catch((error51) => {
|
|
162807
163446
|
log("[dreamer] failed to delete child session:", error51);
|
|
162808
163447
|
});
|
|
162809
163448
|
}
|
|
162810
163449
|
}
|
|
163450
|
+
if (lostLease) {
|
|
163451
|
+
recordLeaseLostTask(`task ${taskName}`);
|
|
163452
|
+
break;
|
|
163453
|
+
}
|
|
162811
163454
|
if (circuitBreakerTripped) {
|
|
162812
163455
|
break;
|
|
162813
163456
|
}
|
|
162814
163457
|
}
|
|
162815
|
-
if (
|
|
163458
|
+
if (lostLease) {
|
|
163459
|
+
log("[dreamer] lease lost: skipping all post-task phases");
|
|
163460
|
+
recordLeaseLostTask("post-task phases");
|
|
163461
|
+
} else if (circuitBreakerTripped) {
|
|
162816
163462
|
log("[dreamer] circuit breaker: skipping post-task phases");
|
|
162817
163463
|
result.tasks.push({
|
|
162818
163464
|
name: "post-task-phases",
|
|
@@ -162821,9 +163467,12 @@ async function runDream(args) {
|
|
|
162821
163467
|
error: "Skipped post-task phases after circuit breaker tripped; configure dreamer model/fallback_models in magic-context.jsonc."
|
|
162822
163468
|
});
|
|
162823
163469
|
}
|
|
162824
|
-
if (!circuitBreakerTripped && args.experimentalUserMemories?.enabled && Date.now() <= deadline) {
|
|
163470
|
+
if (!circuitBreakerTripped && !lostLease && args.experimentalUserMemories?.enabled && Date.now() <= deadline) {
|
|
162825
163471
|
const umStart = Date.now();
|
|
162826
163472
|
try {
|
|
163473
|
+
if (!verifyLeaseStillHeld("before user-memory review")) {
|
|
163474
|
+
throw new Error(lostLeaseReason ?? "Dream lease lost before user-memory review");
|
|
163475
|
+
}
|
|
162827
163476
|
const reviewResult = await reviewUserMemories({
|
|
162828
163477
|
db: args.db,
|
|
162829
163478
|
client: args.client,
|
|
@@ -162834,6 +163483,9 @@ async function runDream(args) {
|
|
|
162834
163483
|
promotionThreshold: args.experimentalUserMemories.promotionThreshold,
|
|
162835
163484
|
fallbackModels: args.fallbackModels
|
|
162836
163485
|
});
|
|
163486
|
+
if (!verifyLeaseStillHeld("after user-memory review")) {
|
|
163487
|
+
throw new Error(lostLeaseReason ?? "Dream lease lost after user-memory review");
|
|
163488
|
+
}
|
|
162837
163489
|
const umOutput = `promoted=${reviewResult.promoted} merged=${reviewResult.merged} dismissed=${reviewResult.dismissed} consumed=${reviewResult.candidatesConsumed}`;
|
|
162838
163490
|
if (reviewResult.promoted > 0 || reviewResult.merged > 0 || reviewResult.dismissed > 0) {
|
|
162839
163491
|
log(`[dreamer] user-memories: ${umOutput}`);
|
|
@@ -162853,9 +163505,14 @@ async function runDream(args) {
|
|
|
162853
163505
|
error: errorDescription.brief
|
|
162854
163506
|
});
|
|
162855
163507
|
}
|
|
163508
|
+
if (lostLease)
|
|
163509
|
+
recordLeaseLostTask("user-memory review");
|
|
162856
163510
|
}
|
|
162857
|
-
if (!circuitBreakerTripped && Date.now() <= deadline) {
|
|
163511
|
+
if (!circuitBreakerTripped && !lostLease && Date.now() <= deadline) {
|
|
162858
163512
|
try {
|
|
163513
|
+
if (!verifyLeaseStillHeld("before smart-note evaluation")) {
|
|
163514
|
+
throw new Error(lostLeaseReason ?? "Dream lease lost before smart-note evaluation");
|
|
163515
|
+
}
|
|
162859
163516
|
await evaluateSmartNotes({
|
|
162860
163517
|
db: args.db,
|
|
162861
163518
|
client: args.client,
|
|
@@ -162865,27 +163522,47 @@ async function runDream(args) {
|
|
|
162865
163522
|
holderId,
|
|
162866
163523
|
deadline,
|
|
162867
163524
|
result,
|
|
162868
|
-
fallbackModels: args.fallbackModels
|
|
163525
|
+
fallbackModels: args.fallbackModels,
|
|
163526
|
+
onLeaseLost: markLeaseLost,
|
|
163527
|
+
isLeaseLost: () => lostLease
|
|
162869
163528
|
});
|
|
163529
|
+
if (!verifyLeaseStillHeld("after smart-note evaluation")) {
|
|
163530
|
+
throw new Error(lostLeaseReason ?? "Dream lease lost after smart-note evaluation");
|
|
163531
|
+
}
|
|
162870
163532
|
} catch (error51) {
|
|
162871
163533
|
const errorDescription = describeError(error51);
|
|
162872
163534
|
logWithStackHead(`[dreamer] smart note evaluation failed: ${errorDescription.brief}`, errorDescription.stackHead);
|
|
162873
163535
|
}
|
|
163536
|
+
if (lostLease)
|
|
163537
|
+
recordLeaseLostTask("smart-note evaluation");
|
|
162874
163538
|
}
|
|
162875
|
-
if (!circuitBreakerTripped && args.experimentalPinKeyFiles?.enabled && Date.now() <= deadline) {
|
|
163539
|
+
if (!circuitBreakerTripped && !lostLease && args.experimentalPinKeyFiles?.enabled && Date.now() <= deadline) {
|
|
162876
163540
|
const kfStart = Date.now();
|
|
162877
163541
|
try {
|
|
162878
|
-
|
|
162879
|
-
|
|
162880
|
-
|
|
162881
|
-
|
|
162882
|
-
|
|
162883
|
-
|
|
162884
|
-
|
|
162885
|
-
|
|
162886
|
-
|
|
162887
|
-
|
|
162888
|
-
|
|
163542
|
+
if (!verifyLeaseStillHeld("before key-file identification")) {
|
|
163543
|
+
throw new Error(lostLeaseReason ?? "Dream lease lost before key-file identification");
|
|
163544
|
+
}
|
|
163545
|
+
const openCodeDb = openOpenCodeDb();
|
|
163546
|
+
if (openCodeDb) {
|
|
163547
|
+
try {
|
|
163548
|
+
await runKeyFilesTask({
|
|
163549
|
+
db: args.db,
|
|
163550
|
+
openCodeDb,
|
|
163551
|
+
client: args.client,
|
|
163552
|
+
projectPath: args.sessionDirectory ?? args.projectIdentity,
|
|
163553
|
+
holderId,
|
|
163554
|
+
deadline,
|
|
163555
|
+
parentSessionId,
|
|
163556
|
+
config: args.experimentalPinKeyFiles,
|
|
163557
|
+
fallbackModels: args.fallbackModels
|
|
163558
|
+
});
|
|
163559
|
+
} finally {
|
|
163560
|
+
closeQuietly(openCodeDb);
|
|
163561
|
+
}
|
|
163562
|
+
}
|
|
163563
|
+
if (!verifyLeaseStillHeld("after key-file identification")) {
|
|
163564
|
+
throw new Error(lostLeaseReason ?? "Dream lease lost after key-file identification");
|
|
163565
|
+
}
|
|
162889
163566
|
result.tasks.push({
|
|
162890
163567
|
name: "key files",
|
|
162891
163568
|
durationMs: Date.now() - kfStart,
|
|
@@ -162901,6 +163578,8 @@ async function runDream(args) {
|
|
|
162901
163578
|
error: errorDescription.brief
|
|
162902
163579
|
});
|
|
162903
163580
|
}
|
|
163581
|
+
if (lostLease)
|
|
163582
|
+
recordLeaseLostTask("key-file identification");
|
|
162904
163583
|
}
|
|
162905
163584
|
} finally {
|
|
162906
163585
|
releaseLease(args.db, holderId);
|
|
@@ -162941,7 +163620,7 @@ async function runDream(args) {
|
|
|
162941
163620
|
"circuit-breaker"
|
|
162942
163621
|
]);
|
|
162943
163622
|
const hasSuccessfulTask = result.tasks.some((t) => !t.error && !POST_TASK_NAMES.has(t.name));
|
|
162944
|
-
if (hasSuccessfulTask) {
|
|
163623
|
+
if (hasSuccessfulTask && !lostLease) {
|
|
162945
163624
|
setDreamState(args.db, `last_dream_at:${args.projectIdentity}`, String(result.finishedAt));
|
|
162946
163625
|
setDreamState(args.db, "last_dream_at", String(result.finishedAt));
|
|
162947
163626
|
}
|
|
@@ -162990,9 +163669,11 @@ Only include notes whose conditions you could definitively evaluate. Skip notes
|
|
|
162990
163669
|
try {
|
|
162991
163670
|
if (!renewLease(args.db, args.holderId)) {
|
|
162992
163671
|
log("[dreamer] smart notes: lease renewal failed — aborting");
|
|
163672
|
+
args.onLeaseLost?.("smart notes");
|
|
162993
163673
|
abortController.abort();
|
|
162994
163674
|
}
|
|
162995
|
-
} catch {
|
|
163675
|
+
} catch (error51) {
|
|
163676
|
+
args.onLeaseLost?.("smart notes", error51);
|
|
162996
163677
|
abortController.abort();
|
|
162997
163678
|
}
|
|
162998
163679
|
}, 60000);
|
|
@@ -163096,8 +163777,7 @@ Only include notes whose conditions you could definitively evaluate. Skip notes
|
|
|
163096
163777
|
clearInterval(leaseInterval);
|
|
163097
163778
|
if (agentSessionId) {
|
|
163098
163779
|
await args.client.session.delete({
|
|
163099
|
-
path: { id: agentSessionId }
|
|
163100
|
-
query: { directory: args.sessionDirectory ?? args.projectIdentity }
|
|
163780
|
+
path: { id: agentSessionId }
|
|
163101
163781
|
}).catch(() => {});
|
|
163102
163782
|
}
|
|
163103
163783
|
}
|
|
@@ -164215,6 +164895,9 @@ function executeFlush(db, sessionId) {
|
|
|
164215
164895
|
}
|
|
164216
164896
|
}
|
|
164217
164897
|
|
|
164898
|
+
// src/context-handler.ts
|
|
164899
|
+
import * as crypto2 from "node:crypto";
|
|
164900
|
+
|
|
164218
164901
|
// ../plugin/src/features/magic-context/scheduler.ts
|
|
164219
164902
|
init_logger();
|
|
164220
164903
|
var TTL_PATTERN = /^(\d+)([smh])$/;
|
|
@@ -164271,7 +164954,6 @@ function createScheduler(config2) {
|
|
|
164271
164954
|
}
|
|
164272
164955
|
};
|
|
164273
164956
|
}
|
|
164274
|
-
|
|
164275
164957
|
// ../plugin/src/features/magic-context/tagger.ts
|
|
164276
164958
|
var TOOL_COMPOSITE_KEY_SEP = "\x00";
|
|
164277
164959
|
function makeToolCompositeKey(ownerMsgId, callId) {
|
|
@@ -164613,6 +165295,30 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
|
|
|
164613
165295
|
return didMutateMessage;
|
|
164614
165296
|
}
|
|
164615
165297
|
|
|
165298
|
+
// ../plugin/src/hooks/magic-context/boundary-execution.ts
|
|
165299
|
+
var FORCE_MATERIALIZE_PERCENTAGE = 85;
|
|
165300
|
+
function detectMidTurnBypassReason(input) {
|
|
165301
|
+
if (input.contextUsage.percentage >= FORCE_MATERIALIZE_PERCENTAGE)
|
|
165302
|
+
return "force-materialize";
|
|
165303
|
+
if (input.historyRefreshSessions.has(input.sessionId))
|
|
165304
|
+
return "explicit-bust";
|
|
165305
|
+
if (input.sessionMeta.isSubagent)
|
|
165306
|
+
return "subagent";
|
|
165307
|
+
return "none";
|
|
165308
|
+
}
|
|
165309
|
+
function applyMidTurnDeferral(input) {
|
|
165310
|
+
if (input.base === "defer") {
|
|
165311
|
+
return { midTurnAdjustedSchedulerDecision: "defer", sideEffect: "none" };
|
|
165312
|
+
}
|
|
165313
|
+
if (input.bypassReason !== "none") {
|
|
165314
|
+
return { midTurnAdjustedSchedulerDecision: "execute", sideEffect: "none" };
|
|
165315
|
+
}
|
|
165316
|
+
if (input.midTurn) {
|
|
165317
|
+
return { midTurnAdjustedSchedulerDecision: "defer", sideEffect: "set-flag" };
|
|
165318
|
+
}
|
|
165319
|
+
return { midTurnAdjustedSchedulerDecision: "execute", sideEffect: "none" };
|
|
165320
|
+
}
|
|
165321
|
+
|
|
164616
165322
|
// ../plugin/src/hooks/magic-context/caveman.ts
|
|
164617
165323
|
var PRESERVATION_PATTERNS = [
|
|
164618
165324
|
/```[\s\S]*?```/g,
|
|
@@ -165310,16 +166016,24 @@ function trimMemoriesToBudget(sessionId, memories, budgetTokens) {
|
|
|
165310
166016
|
function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectPath, injectionBudgetTokens, temporalAwareness) {
|
|
165311
166017
|
const cached2 = injectionCache.get(sessionId);
|
|
165312
166018
|
if (!isCacheBusting && cached2) {
|
|
165313
|
-
if (cached2.
|
|
165314
|
-
|
|
165315
|
-
|
|
165316
|
-
|
|
165317
|
-
|
|
165318
|
-
|
|
165319
|
-
|
|
166019
|
+
if (cached2.kind === "empty") {
|
|
166020
|
+
return null;
|
|
166021
|
+
}
|
|
166022
|
+
const prepared = cached2.injection;
|
|
166023
|
+
if (prepared.compartmentEndMessageId === null) {
|
|
166024
|
+
sessionLog(sessionId, "compartment injection cache in degraded mode (null boundary), forcing rebuild");
|
|
166025
|
+
} else {
|
|
166026
|
+
if (prepared.compartmentEndMessageId.length > 0) {
|
|
166027
|
+
const cutoffIndex2 = messages.findIndex((message) => message.info.id === prepared.compartmentEndMessageId);
|
|
166028
|
+
if (cutoffIndex2 >= 0) {
|
|
166029
|
+
const remaining = messages.slice(cutoffIndex2 + 1);
|
|
166030
|
+
messages.splice(0, messages.length, ...remaining);
|
|
166031
|
+
} else {
|
|
166032
|
+
sessionLog(sessionId, `compartment injection: cached boundary ${prepared.compartmentEndMessageId} not in messages (already trimmed), reusing cache`);
|
|
166033
|
+
}
|
|
165320
166034
|
}
|
|
166035
|
+
return { ...prepared, rebuiltFromDb: false };
|
|
165321
166036
|
}
|
|
165322
|
-
return cached2;
|
|
165323
166037
|
}
|
|
165324
166038
|
const compartments = getCompartments(db, sessionId);
|
|
165325
166039
|
const facts = getSessionFacts(db, sessionId);
|
|
@@ -165351,7 +166065,11 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
|
|
|
165351
166065
|
}
|
|
165352
166066
|
}
|
|
165353
166067
|
if (compartments.length === 0 && facts.length === 0 && !memoryBlock) {
|
|
165354
|
-
injectionCache.
|
|
166068
|
+
injectionCache.set(sessionId, {
|
|
166069
|
+
kind: "empty",
|
|
166070
|
+
compartmentEndMessageId: "",
|
|
166071
|
+
renderedBytes: 0
|
|
166072
|
+
});
|
|
165355
166073
|
return null;
|
|
165356
166074
|
}
|
|
165357
166075
|
let dateRanges;
|
|
@@ -165384,9 +166102,10 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
|
|
|
165384
166102
|
compartmentCount: 0,
|
|
165385
166103
|
skippedVisibleMessages: 0,
|
|
165386
166104
|
factCount: facts.length,
|
|
165387
|
-
memoryCount
|
|
166105
|
+
memoryCount,
|
|
166106
|
+
rebuiltFromDb: true
|
|
165388
166107
|
};
|
|
165389
|
-
injectionCache.set(sessionId, result2);
|
|
166108
|
+
injectionCache.set(sessionId, { kind: "populated", injection: result2 });
|
|
165390
166109
|
return result2;
|
|
165391
166110
|
}
|
|
165392
166111
|
const lastCompartment = compartments[compartments.length - 1];
|
|
@@ -165404,9 +166123,10 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
|
|
|
165404
166123
|
compartmentCount: compartments.length,
|
|
165405
166124
|
skippedVisibleMessages: 0,
|
|
165406
166125
|
factCount: facts.length,
|
|
165407
|
-
memoryCount
|
|
166126
|
+
memoryCount,
|
|
166127
|
+
rebuiltFromDb: true
|
|
165408
166128
|
};
|
|
165409
|
-
injectionCache.set(sessionId, result2);
|
|
166129
|
+
injectionCache.set(sessionId, { kind: "populated", injection: result2 });
|
|
165410
166130
|
return result2;
|
|
165411
166131
|
}
|
|
165412
166132
|
let skippedVisibleMessages = 0;
|
|
@@ -165415,17 +166135,20 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
|
|
|
165415
166135
|
skippedVisibleMessages = cutoffIndex + 1;
|
|
165416
166136
|
const remaining = messages.slice(cutoffIndex + 1);
|
|
165417
166137
|
messages.splice(0, messages.length, ...remaining);
|
|
166138
|
+
} else {
|
|
166139
|
+
sessionLog(sessionId, `compartment injection entering degraded mode: boundary ${lastEndMessageId} not in visible messages`);
|
|
165418
166140
|
}
|
|
165419
166141
|
const result = {
|
|
165420
166142
|
block,
|
|
165421
166143
|
compartmentEndMessage: lastEnd,
|
|
165422
|
-
compartmentEndMessageId: lastEndMessageId,
|
|
166144
|
+
compartmentEndMessageId: cutoffIndex >= 0 ? lastEndMessageId : null,
|
|
165423
166145
|
compartmentCount: compartments.length,
|
|
165424
166146
|
skippedVisibleMessages,
|
|
165425
166147
|
factCount: facts.length,
|
|
165426
|
-
memoryCount
|
|
166148
|
+
memoryCount,
|
|
166149
|
+
rebuiltFromDb: true
|
|
165427
166150
|
};
|
|
165428
|
-
injectionCache.set(sessionId, result);
|
|
166151
|
+
injectionCache.set(sessionId, { kind: "populated", injection: result });
|
|
165429
166152
|
return result;
|
|
165430
166153
|
}
|
|
165431
166154
|
|
|
@@ -166367,10 +167090,10 @@ var DEFAULT_MIN_PROMPT_CHARS = 20;
|
|
|
166367
167090
|
async function unifiedSearchWithTimeout(db, sessionId, projectPath, prompt, options, timeoutMs) {
|
|
166368
167091
|
const controller = new AbortController;
|
|
166369
167092
|
let timer;
|
|
166370
|
-
const timeoutPromise = new Promise((
|
|
167093
|
+
const timeoutPromise = new Promise((resolve3) => {
|
|
166371
167094
|
timer = setTimeout(() => {
|
|
166372
167095
|
controller.abort();
|
|
166373
|
-
|
|
167096
|
+
resolve3(null);
|
|
166374
167097
|
}, timeoutMs);
|
|
166375
167098
|
});
|
|
166376
167099
|
try {
|
|
@@ -166968,7 +167691,7 @@ function injectSessionHistoryIntoPi(db, sessionId, piMessages, isCacheBusting, p
|
|
|
166968
167691
|
};
|
|
166969
167692
|
}
|
|
166970
167693
|
const boundaryId = prepared.compartmentEndMessageId;
|
|
166971
|
-
const skippedVisible = boundaryId.length > 0 ? trimPiMessagesToBoundary(piMessages, entryIds, boundaryId) : prepared.skippedVisibleMessages;
|
|
167694
|
+
const skippedVisible = boundaryId != null && boundaryId.length > 0 ? trimPiMessagesToBoundary(piMessages, entryIds, boundaryId) : prepared.skippedVisibleMessages;
|
|
166972
167695
|
const historyBlock = `<session-history>
|
|
166973
167696
|
${prepared.block}
|
|
166974
167697
|
</session-history>`;
|
|
@@ -168018,17 +168741,17 @@ function getReducedRecompTokenBudget(currentBudget) {
|
|
|
168018
168741
|
}
|
|
168019
168742
|
|
|
168020
168743
|
// ../plugin/src/hooks/magic-context/historian-state-file.ts
|
|
168021
|
-
|
|
168022
|
-
import {
|
|
168023
|
-
import { join as
|
|
168744
|
+
init_data_path();
|
|
168745
|
+
import { mkdirSync as mkdirSync5, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
168746
|
+
import { join as join12 } from "node:path";
|
|
168024
168747
|
var HISTORIAN_STATE_INLINE_THRESHOLD = 30000;
|
|
168025
|
-
|
|
168026
|
-
function maybeWriteHistorianStateFile(sessionId, existingState) {
|
|
168748
|
+
function maybeWriteHistorianStateFile(sessionId, existingState, directory) {
|
|
168027
168749
|
if (existingState.length <= HISTORIAN_STATE_INLINE_THRESHOLD)
|
|
168028
168750
|
return;
|
|
168029
168751
|
try {
|
|
168030
|
-
|
|
168031
|
-
|
|
168752
|
+
const dir = getProjectMagicContextHistorianDir(directory);
|
|
168753
|
+
mkdirSync5(dir, { recursive: true });
|
|
168754
|
+
const path5 = join12(dir, `state-${sessionId}-${Date.now()}.xml`);
|
|
168032
168755
|
writeFileSync2(path5, existingState, "utf8");
|
|
168033
168756
|
return path5;
|
|
168034
168757
|
} catch {
|
|
@@ -168045,6 +168768,238 @@ function cleanupHistorianStateFile(path5) {
|
|
|
168045
168768
|
|
|
168046
168769
|
// src/pi-historian-runner.ts
|
|
168047
168770
|
init_logger();
|
|
168771
|
+
|
|
168772
|
+
// src/read-session-pi.ts
|
|
168773
|
+
function isMidTurnPi(event, _sessionId) {
|
|
168774
|
+
const messages = event?.messages;
|
|
168775
|
+
if (!Array.isArray(messages))
|
|
168776
|
+
return false;
|
|
168777
|
+
let latestAssistantIndex = -1;
|
|
168778
|
+
let latestAssistant = null;
|
|
168779
|
+
for (let i = messages.length - 1;i >= 0; i--) {
|
|
168780
|
+
const msg = messages[i];
|
|
168781
|
+
if (msg !== null && typeof msg === "object") {
|
|
168782
|
+
const record2 = msg;
|
|
168783
|
+
if (record2.role === "assistant") {
|
|
168784
|
+
latestAssistantIndex = i;
|
|
168785
|
+
latestAssistant = record2;
|
|
168786
|
+
break;
|
|
168787
|
+
}
|
|
168788
|
+
}
|
|
168789
|
+
}
|
|
168790
|
+
if (latestAssistant === null)
|
|
168791
|
+
return false;
|
|
168792
|
+
if (latestAssistant.stopReason === "toolUse")
|
|
168793
|
+
return true;
|
|
168794
|
+
const toolCallIds = getToolCallIds(latestAssistant.content);
|
|
168795
|
+
if (toolCallIds.size === 0)
|
|
168796
|
+
return false;
|
|
168797
|
+
const pairedToolResultIds = new Set;
|
|
168798
|
+
for (const msg of messages.slice(latestAssistantIndex + 1)) {
|
|
168799
|
+
if (msg === null || typeof msg !== "object")
|
|
168800
|
+
continue;
|
|
168801
|
+
const record2 = msg;
|
|
168802
|
+
if (record2.role !== "toolResult")
|
|
168803
|
+
continue;
|
|
168804
|
+
if (typeof record2.toolCallId === "string") {
|
|
168805
|
+
pairedToolResultIds.add(record2.toolCallId);
|
|
168806
|
+
}
|
|
168807
|
+
}
|
|
168808
|
+
for (const id of toolCallIds) {
|
|
168809
|
+
if (!pairedToolResultIds.has(id))
|
|
168810
|
+
return true;
|
|
168811
|
+
}
|
|
168812
|
+
return false;
|
|
168813
|
+
}
|
|
168814
|
+
function getToolCallIds(content) {
|
|
168815
|
+
const ids = new Set;
|
|
168816
|
+
if (!Array.isArray(content))
|
|
168817
|
+
return ids;
|
|
168818
|
+
for (const item of content) {
|
|
168819
|
+
if (item === null || typeof item !== "object")
|
|
168820
|
+
continue;
|
|
168821
|
+
const record2 = item;
|
|
168822
|
+
if (record2.type === "toolCall" && typeof record2.id === "string") {
|
|
168823
|
+
ids.add(record2.id);
|
|
168824
|
+
}
|
|
168825
|
+
}
|
|
168826
|
+
return ids;
|
|
168827
|
+
}
|
|
168828
|
+
function readPiSessionMessages(ctx) {
|
|
168829
|
+
const sm = ctx.sessionManager;
|
|
168830
|
+
if (sm === undefined)
|
|
168831
|
+
return [];
|
|
168832
|
+
const getBranch = sm.getBranch;
|
|
168833
|
+
if (typeof getBranch !== "function")
|
|
168834
|
+
return [];
|
|
168835
|
+
let entries;
|
|
168836
|
+
try {
|
|
168837
|
+
entries = getBranch.call(sm);
|
|
168838
|
+
} catch {
|
|
168839
|
+
return [];
|
|
168840
|
+
}
|
|
168841
|
+
if (!Array.isArray(entries))
|
|
168842
|
+
return [];
|
|
168843
|
+
return convertEntriesToRawMessages(entries);
|
|
168844
|
+
}
|
|
168845
|
+
function convertEntriesToRawMessages(entries) {
|
|
168846
|
+
const result = [];
|
|
168847
|
+
let nextOrdinal = 1;
|
|
168848
|
+
let pendingToolParts = [];
|
|
168849
|
+
let pendingFirstRealId = "";
|
|
168850
|
+
for (const entry of entries) {
|
|
168851
|
+
if (!isMessageEntry(entry)) {
|
|
168852
|
+
continue;
|
|
168853
|
+
}
|
|
168854
|
+
const msg = entry.message;
|
|
168855
|
+
const role = msg.role;
|
|
168856
|
+
if (role === "toolResult") {
|
|
168857
|
+
pendingToolParts.push(...synthesizeToolResultParts(msg));
|
|
168858
|
+
if (pendingFirstRealId === "") {
|
|
168859
|
+
pendingFirstRealId = entry.id;
|
|
168860
|
+
}
|
|
168861
|
+
continue;
|
|
168862
|
+
}
|
|
168863
|
+
if (role === "user") {
|
|
168864
|
+
const parts = [
|
|
168865
|
+
...pendingToolParts,
|
|
168866
|
+
...synthesizeUserParts(msg)
|
|
168867
|
+
];
|
|
168868
|
+
pendingToolParts = [];
|
|
168869
|
+
pendingFirstRealId = "";
|
|
168870
|
+
result.push({
|
|
168871
|
+
ordinal: nextOrdinal++,
|
|
168872
|
+
id: entry.id,
|
|
168873
|
+
role: "user",
|
|
168874
|
+
parts
|
|
168875
|
+
});
|
|
168876
|
+
continue;
|
|
168877
|
+
}
|
|
168878
|
+
if (role === "assistant") {
|
|
168879
|
+
if (pendingToolParts.length > 0) {
|
|
168880
|
+
result.push({
|
|
168881
|
+
ordinal: nextOrdinal++,
|
|
168882
|
+
id: pendingFirstRealId,
|
|
168883
|
+
role: "user",
|
|
168884
|
+
parts: pendingToolParts
|
|
168885
|
+
});
|
|
168886
|
+
pendingToolParts = [];
|
|
168887
|
+
pendingFirstRealId = "";
|
|
168888
|
+
}
|
|
168889
|
+
result.push({
|
|
168890
|
+
ordinal: nextOrdinal++,
|
|
168891
|
+
id: entry.id,
|
|
168892
|
+
role: "assistant",
|
|
168893
|
+
parts: synthesizeAssistantParts(msg)
|
|
168894
|
+
});
|
|
168895
|
+
continue;
|
|
168896
|
+
}
|
|
168897
|
+
result.push({
|
|
168898
|
+
ordinal: nextOrdinal++,
|
|
168899
|
+
id: entry.id,
|
|
168900
|
+
role: typeof role === "string" ? role : "unknown",
|
|
168901
|
+
parts: []
|
|
168902
|
+
});
|
|
168903
|
+
}
|
|
168904
|
+
if (pendingToolParts.length > 0) {
|
|
168905
|
+
result.push({
|
|
168906
|
+
ordinal: nextOrdinal,
|
|
168907
|
+
id: pendingFirstRealId,
|
|
168908
|
+
role: "user",
|
|
168909
|
+
parts: pendingToolParts
|
|
168910
|
+
});
|
|
168911
|
+
}
|
|
168912
|
+
return result;
|
|
168913
|
+
}
|
|
168914
|
+
function isMessageEntry(value) {
|
|
168915
|
+
if (value === null || typeof value !== "object")
|
|
168916
|
+
return false;
|
|
168917
|
+
const v = value;
|
|
168918
|
+
if (v.type !== "message")
|
|
168919
|
+
return false;
|
|
168920
|
+
if (typeof v.id !== "string")
|
|
168921
|
+
return false;
|
|
168922
|
+
if (v.message === null || typeof v.message !== "object")
|
|
168923
|
+
return false;
|
|
168924
|
+
return true;
|
|
168925
|
+
}
|
|
168926
|
+
function synthesizeUserParts(msg) {
|
|
168927
|
+
const m = msg;
|
|
168928
|
+
if (typeof m.content === "string") {
|
|
168929
|
+
if (m.content.trim().length === 0)
|
|
168930
|
+
return [];
|
|
168931
|
+
return [{ type: "text", text: m.content }];
|
|
168932
|
+
}
|
|
168933
|
+
if (!Array.isArray(m.content))
|
|
168934
|
+
return [];
|
|
168935
|
+
const parts = [];
|
|
168936
|
+
for (const c of m.content) {
|
|
168937
|
+
if (c === null || typeof c !== "object")
|
|
168938
|
+
continue;
|
|
168939
|
+
const cc = c;
|
|
168940
|
+
if (cc.type === "text" && typeof cc.text === "string") {
|
|
168941
|
+
parts.push({ type: "text", text: cc.text });
|
|
168942
|
+
}
|
|
168943
|
+
}
|
|
168944
|
+
return parts;
|
|
168945
|
+
}
|
|
168946
|
+
function synthesizeAssistantParts(msg) {
|
|
168947
|
+
const m = msg;
|
|
168948
|
+
if (!Array.isArray(m.content))
|
|
168949
|
+
return [];
|
|
168950
|
+
const parts = [];
|
|
168951
|
+
for (const c of m.content) {
|
|
168952
|
+
if (c === null || typeof c !== "object")
|
|
168953
|
+
continue;
|
|
168954
|
+
const cc = c;
|
|
168955
|
+
if (cc.type === "text" && typeof cc.text === "string") {
|
|
168956
|
+
parts.push({ type: "text", text: cc.text });
|
|
168957
|
+
} else if (cc.type === "toolCall" && typeof cc.id === "string") {
|
|
168958
|
+
parts.push({
|
|
168959
|
+
type: "tool",
|
|
168960
|
+
tool: typeof cc.name === "string" ? cc.name : "unknown",
|
|
168961
|
+
callID: cc.id,
|
|
168962
|
+
state: {
|
|
168963
|
+
input: cc.arguments ?? {}
|
|
168964
|
+
}
|
|
168965
|
+
});
|
|
168966
|
+
}
|
|
168967
|
+
}
|
|
168968
|
+
return parts;
|
|
168969
|
+
}
|
|
168970
|
+
function synthesizeToolResultParts(msg) {
|
|
168971
|
+
const m = msg;
|
|
168972
|
+
const callID = typeof m.toolCallId === "string" ? m.toolCallId : "";
|
|
168973
|
+
const tool = typeof m.toolName === "string" ? m.toolName : "unknown";
|
|
168974
|
+
if (!callID)
|
|
168975
|
+
return [];
|
|
168976
|
+
let output = "";
|
|
168977
|
+
if (Array.isArray(m.content)) {
|
|
168978
|
+
const fragments = [];
|
|
168979
|
+
for (const c of m.content) {
|
|
168980
|
+
if (c === null || typeof c !== "object")
|
|
168981
|
+
continue;
|
|
168982
|
+
const cc = c;
|
|
168983
|
+
if (cc.type === "text" && typeof cc.text === "string") {
|
|
168984
|
+
fragments.push(cc.text);
|
|
168985
|
+
}
|
|
168986
|
+
}
|
|
168987
|
+
output = fragments.join(`
|
|
168988
|
+
`);
|
|
168989
|
+
}
|
|
168990
|
+
return [
|
|
168991
|
+
{
|
|
168992
|
+
type: "tool",
|
|
168993
|
+
tool,
|
|
168994
|
+
callID,
|
|
168995
|
+
state: {
|
|
168996
|
+
output
|
|
168997
|
+
}
|
|
168998
|
+
}
|
|
168999
|
+
];
|
|
169000
|
+
}
|
|
169001
|
+
|
|
169002
|
+
// src/pi-historian-runner.ts
|
|
168048
169003
|
var HISTORIAN_AGENT_NAME = "magic-context-historian";
|
|
168049
169004
|
var DEFAULT_HISTORIAN_TIMEOUT_MS2 = 120000;
|
|
168050
169005
|
var HISTORIAN_ALERT_COOLDOWN_MS = 60 * 1000;
|
|
@@ -168128,7 +169083,7 @@ async function runPiHistorian(deps) {
|
|
|
168128
169083
|
const existingState = priorCompartments.length > 0 || priorFacts.length > 0 ? buildExistingStateXml(priorCompartments, priorFacts, memoryBlock) : memoryBlock ? `${memoryBlock}
|
|
168129
169084
|
|
|
168130
169085
|
This is your first run. No existing compartments or facts.` : "This is your first run. No existing state.";
|
|
168131
|
-
stateFilePath = maybeWriteHistorianStateFile(sessionId, existingState);
|
|
169086
|
+
stateFilePath = maybeWriteHistorianStateFile(sessionId, existingState, directory);
|
|
168132
169087
|
if (stateFilePath) {
|
|
168133
169088
|
sessionLog(sessionId, `pi-historian: existing state offloaded to file (${existingState.length} chars) → ${stateFilePath}`);
|
|
168134
169089
|
}
|
|
@@ -168339,191 +169294,160 @@ function appendPiNativeCompaction(args) {
|
|
|
168339
169294
|
}
|
|
168340
169295
|
}
|
|
168341
169296
|
function findFirstKeptEntryId(entries, lastCompactedOrdinal) {
|
|
168342
|
-
|
|
168343
|
-
|
|
168344
|
-
|
|
168345
|
-
|
|
168346
|
-
|
|
168347
|
-
|
|
168348
|
-
continue;
|
|
168349
|
-
const role = e.message?.role;
|
|
168350
|
-
if (role !== "user" && role !== "assistant")
|
|
168351
|
-
continue;
|
|
168352
|
-
ordinal++;
|
|
168353
|
-
if (ordinal === lastCompactedOrdinal + 1) {
|
|
168354
|
-
return typeof e.id === "string" && e.id.length > 0 ? e.id : null;
|
|
168355
|
-
}
|
|
168356
|
-
}
|
|
168357
|
-
return null;
|
|
169297
|
+
const rawMessages = convertEntriesToRawMessages(entries);
|
|
169298
|
+
const target = lastCompactedOrdinal + 1;
|
|
169299
|
+
const match = rawMessages.find((m) => m.ordinal === target);
|
|
169300
|
+
if (!match)
|
|
169301
|
+
return null;
|
|
169302
|
+
return match.id.length > 0 ? match.id : null;
|
|
168358
169303
|
}
|
|
168359
169304
|
|
|
168360
|
-
// src/
|
|
168361
|
-
function
|
|
168362
|
-
|
|
168363
|
-
|
|
168364
|
-
return [];
|
|
168365
|
-
const getBranch = sm.getBranch;
|
|
168366
|
-
if (typeof getBranch !== "function")
|
|
168367
|
-
return [];
|
|
168368
|
-
let entries;
|
|
168369
|
-
try {
|
|
168370
|
-
entries = getBranch.call(sm);
|
|
168371
|
-
} catch {
|
|
168372
|
-
return [];
|
|
169305
|
+
// src/pi-todo-inject.ts
|
|
169306
|
+
function getMessageId(message) {
|
|
169307
|
+
if (typeof message.responseId === "string" && message.responseId.length > 0) {
|
|
169308
|
+
return message.responseId;
|
|
168373
169309
|
}
|
|
168374
|
-
if (
|
|
168375
|
-
return
|
|
168376
|
-
|
|
169310
|
+
if (typeof message.timestamp === "number") {
|
|
169311
|
+
return `pi-ts-${message.timestamp}`;
|
|
169312
|
+
}
|
|
169313
|
+
return "";
|
|
168377
169314
|
}
|
|
168378
|
-
function
|
|
168379
|
-
|
|
168380
|
-
|
|
168381
|
-
|
|
168382
|
-
|
|
168383
|
-
if (!isMessageEntry(entry)) {
|
|
168384
|
-
continue;
|
|
168385
|
-
}
|
|
168386
|
-
const msg = entry.message;
|
|
168387
|
-
const role = msg.role;
|
|
168388
|
-
if (role === "toolResult") {
|
|
168389
|
-
pendingToolParts.push(...synthesizeToolResultParts(msg));
|
|
168390
|
-
continue;
|
|
168391
|
-
}
|
|
168392
|
-
if (role === "user") {
|
|
168393
|
-
const parts = [
|
|
168394
|
-
...pendingToolParts,
|
|
168395
|
-
...synthesizeUserParts(msg)
|
|
168396
|
-
];
|
|
168397
|
-
pendingToolParts = [];
|
|
168398
|
-
result.push({
|
|
168399
|
-
ordinal: nextOrdinal++,
|
|
168400
|
-
id: entry.id,
|
|
168401
|
-
role: "user",
|
|
168402
|
-
parts
|
|
168403
|
-
});
|
|
168404
|
-
continue;
|
|
168405
|
-
}
|
|
168406
|
-
if (role === "assistant") {
|
|
168407
|
-
if (pendingToolParts.length > 0) {
|
|
168408
|
-
result.push({
|
|
168409
|
-
ordinal: nextOrdinal++,
|
|
168410
|
-
id: "",
|
|
168411
|
-
role: "user",
|
|
168412
|
-
parts: pendingToolParts
|
|
168413
|
-
});
|
|
168414
|
-
pendingToolParts = [];
|
|
168415
|
-
}
|
|
168416
|
-
result.push({
|
|
168417
|
-
ordinal: nextOrdinal++,
|
|
168418
|
-
id: entry.id,
|
|
168419
|
-
role: "assistant",
|
|
168420
|
-
parts: synthesizeAssistantParts(msg)
|
|
168421
|
-
});
|
|
169315
|
+
function hasToolCallWithId(message, callId) {
|
|
169316
|
+
if (!Array.isArray(message.content))
|
|
169317
|
+
return false;
|
|
169318
|
+
for (const block of message.content) {
|
|
169319
|
+
if (!block || typeof block !== "object")
|
|
168422
169320
|
continue;
|
|
168423
|
-
|
|
168424
|
-
|
|
168425
|
-
|
|
168426
|
-
|
|
168427
|
-
role: typeof role === "string" ? role : "unknown",
|
|
168428
|
-
parts: []
|
|
168429
|
-
});
|
|
169321
|
+
const t = block.type;
|
|
169322
|
+
const id = block.id;
|
|
169323
|
+
if (t === "toolCall" && id === callId)
|
|
169324
|
+
return true;
|
|
168430
169325
|
}
|
|
168431
|
-
|
|
168432
|
-
|
|
168433
|
-
|
|
168434
|
-
|
|
168435
|
-
|
|
168436
|
-
|
|
168437
|
-
|
|
169326
|
+
return false;
|
|
169327
|
+
}
|
|
169328
|
+
function findToolResultAfter(messages, assistantIndex, callId) {
|
|
169329
|
+
for (let i = assistantIndex + 1;i < messages.length; i += 1) {
|
|
169330
|
+
const m = messages[i];
|
|
169331
|
+
if (!m || typeof m !== "object")
|
|
169332
|
+
continue;
|
|
169333
|
+
const role = m.role;
|
|
169334
|
+
const tcId = m.toolCallId;
|
|
169335
|
+
if (role === "toolResult" && tcId === callId)
|
|
169336
|
+
return i;
|
|
169337
|
+
if (role === "assistant")
|
|
169338
|
+
return -1;
|
|
168438
169339
|
}
|
|
168439
|
-
return
|
|
169340
|
+
return -1;
|
|
168440
169341
|
}
|
|
168441
|
-
function
|
|
168442
|
-
|
|
168443
|
-
|
|
168444
|
-
|
|
168445
|
-
|
|
168446
|
-
|
|
168447
|
-
|
|
168448
|
-
|
|
168449
|
-
|
|
168450
|
-
|
|
168451
|
-
|
|
169342
|
+
function piBlocksFromSynthetic(part) {
|
|
169343
|
+
return {
|
|
169344
|
+
call: {
|
|
169345
|
+
type: "toolCall",
|
|
169346
|
+
id: part.callID,
|
|
169347
|
+
name: part.tool,
|
|
169348
|
+
arguments: { todos: part.state.input.todos },
|
|
169349
|
+
syntheticTodoMarker: true
|
|
169350
|
+
},
|
|
169351
|
+
result: {
|
|
169352
|
+
role: "toolResult",
|
|
169353
|
+
toolCallId: part.callID,
|
|
169354
|
+
toolName: part.tool,
|
|
169355
|
+
content: [{ type: "text", text: part.state.output }],
|
|
169356
|
+
timestamp: 0,
|
|
169357
|
+
syntheticTodoMarker: true
|
|
169358
|
+
}
|
|
169359
|
+
};
|
|
168452
169360
|
}
|
|
168453
|
-
function
|
|
168454
|
-
|
|
168455
|
-
|
|
168456
|
-
if (m
|
|
168457
|
-
return [];
|
|
168458
|
-
return [{ type: "text", text: m.content }];
|
|
168459
|
-
}
|
|
168460
|
-
if (!Array.isArray(m.content))
|
|
168461
|
-
return [];
|
|
168462
|
-
const parts = [];
|
|
168463
|
-
for (const c of m.content) {
|
|
168464
|
-
if (c === null || typeof c !== "object")
|
|
169361
|
+
function injectByAssistantId(messages, messageId, part) {
|
|
169362
|
+
for (let i = 0;i < messages.length; i += 1) {
|
|
169363
|
+
const m = messages[i];
|
|
169364
|
+
if (!m || typeof m !== "object")
|
|
168465
169365
|
continue;
|
|
168466
|
-
|
|
168467
|
-
|
|
168468
|
-
|
|
169366
|
+
if (m.role !== "assistant")
|
|
169367
|
+
continue;
|
|
169368
|
+
const assistant = m;
|
|
169369
|
+
if (getMessageId(assistant) !== messageId)
|
|
169370
|
+
continue;
|
|
169371
|
+
if (hasToolCallWithId(assistant, part.callID)) {
|
|
169372
|
+
if (findToolResultAfter(messages, i, part.callID) >= 0)
|
|
169373
|
+
return true;
|
|
168469
169374
|
}
|
|
169375
|
+
const { call, result } = piBlocksFromSynthetic(part);
|
|
169376
|
+
if (!Array.isArray(assistant.content))
|
|
169377
|
+
assistant.content = [];
|
|
169378
|
+
if (!hasToolCallWithId(assistant, part.callID)) {
|
|
169379
|
+
assistant.content.push(call);
|
|
169380
|
+
}
|
|
169381
|
+
if (findToolResultAfter(messages, i, part.callID) < 0) {
|
|
169382
|
+
messages.splice(i + 1, 0, result);
|
|
169383
|
+
}
|
|
169384
|
+
return true;
|
|
168470
169385
|
}
|
|
168471
|
-
return
|
|
169386
|
+
return false;
|
|
168472
169387
|
}
|
|
168473
|
-
function
|
|
168474
|
-
|
|
168475
|
-
|
|
168476
|
-
|
|
168477
|
-
const parts = [];
|
|
168478
|
-
for (const c of m.content) {
|
|
168479
|
-
if (c === null || typeof c !== "object")
|
|
169388
|
+
function injectIntoLatestAssistant(messages, part) {
|
|
169389
|
+
for (let i = messages.length - 1;i >= 0; i -= 1) {
|
|
169390
|
+
const m = messages[i];
|
|
169391
|
+
if (!m || typeof m !== "object")
|
|
168480
169392
|
continue;
|
|
168481
|
-
|
|
168482
|
-
|
|
168483
|
-
|
|
168484
|
-
|
|
168485
|
-
|
|
168486
|
-
|
|
168487
|
-
|
|
168488
|
-
|
|
168489
|
-
|
|
168490
|
-
|
|
168491
|
-
|
|
168492
|
-
|
|
169393
|
+
if (m.role !== "assistant")
|
|
169394
|
+
continue;
|
|
169395
|
+
const assistant = m;
|
|
169396
|
+
const id = getMessageId(assistant);
|
|
169397
|
+
if (id.length === 0)
|
|
169398
|
+
continue;
|
|
169399
|
+
if (hasToolCallWithId(assistant, part.callID)) {
|
|
169400
|
+
if (findToolResultAfter(messages, i, part.callID) >= 0)
|
|
169401
|
+
return id;
|
|
169402
|
+
}
|
|
169403
|
+
const { call, result } = piBlocksFromSynthetic(part);
|
|
169404
|
+
if (!Array.isArray(assistant.content))
|
|
169405
|
+
assistant.content = [];
|
|
169406
|
+
if (!hasToolCallWithId(assistant, part.callID)) {
|
|
169407
|
+
assistant.content.push(call);
|
|
169408
|
+
}
|
|
169409
|
+
if (findToolResultAfter(messages, i, part.callID) < 0) {
|
|
169410
|
+
messages.splice(i + 1, 0, result);
|
|
168493
169411
|
}
|
|
169412
|
+
return id;
|
|
168494
169413
|
}
|
|
168495
|
-
return
|
|
169414
|
+
return null;
|
|
168496
169415
|
}
|
|
168497
|
-
function
|
|
168498
|
-
|
|
168499
|
-
|
|
168500
|
-
const
|
|
168501
|
-
if (
|
|
168502
|
-
|
|
168503
|
-
|
|
168504
|
-
|
|
168505
|
-
|
|
168506
|
-
for (const c of m.content) {
|
|
168507
|
-
if (c === null || typeof c !== "object")
|
|
168508
|
-
continue;
|
|
168509
|
-
const cc = c;
|
|
168510
|
-
if (cc.type === "text" && typeof cc.text === "string") {
|
|
168511
|
-
fragments.push(cc.text);
|
|
169416
|
+
function injectSyntheticTodowriteForPi(args) {
|
|
169417
|
+
if (args.isSubagent)
|
|
169418
|
+
return args.messages;
|
|
169419
|
+
const persistedAnchor = getPersistedTodoSyntheticAnchor(args.db, args.sessionId);
|
|
169420
|
+
if (args.isCacheBusting) {
|
|
169421
|
+
const part2 = buildSyntheticTodoPart(args.lastTodoState);
|
|
169422
|
+
if (part2 === null) {
|
|
169423
|
+
if (persistedAnchor) {
|
|
169424
|
+
clearPersistedTodoSyntheticAnchor(args.db, args.sessionId);
|
|
168512
169425
|
}
|
|
169426
|
+
return args.messages;
|
|
168513
169427
|
}
|
|
168514
|
-
|
|
168515
|
-
|
|
168516
|
-
|
|
168517
|
-
return [
|
|
168518
|
-
{
|
|
168519
|
-
type: "tool",
|
|
168520
|
-
tool,
|
|
168521
|
-
callID,
|
|
168522
|
-
state: {
|
|
168523
|
-
output
|
|
169428
|
+
if (persistedAnchor && persistedAnchor.callId === part2.callID && injectByAssistantId(args.messages, persistedAnchor.messageId, part2)) {
|
|
169429
|
+
if (persistedAnchor.stateJson.length === 0) {
|
|
169430
|
+
setPersistedTodoSyntheticAnchor(args.db, args.sessionId, persistedAnchor.callId, persistedAnchor.messageId, args.lastTodoState);
|
|
168524
169431
|
}
|
|
169432
|
+
return args.messages;
|
|
168525
169433
|
}
|
|
168526
|
-
|
|
169434
|
+
const anchoredMessageId = injectIntoLatestAssistant(args.messages, part2);
|
|
169435
|
+
if (anchoredMessageId) {
|
|
169436
|
+
setPersistedTodoSyntheticAnchor(args.db, args.sessionId, part2.callID, anchoredMessageId, args.lastTodoState);
|
|
169437
|
+
} else if (persistedAnchor) {
|
|
169438
|
+
clearPersistedTodoSyntheticAnchor(args.db, args.sessionId);
|
|
169439
|
+
}
|
|
169440
|
+
return args.messages;
|
|
169441
|
+
}
|
|
169442
|
+
if (!persistedAnchor || persistedAnchor.stateJson.length === 0) {
|
|
169443
|
+
return args.messages;
|
|
169444
|
+
}
|
|
169445
|
+
const part = buildSyntheticTodoPart(persistedAnchor.stateJson);
|
|
169446
|
+
if (part === null || part.callID !== persistedAnchor.callId) {
|
|
169447
|
+
return args.messages;
|
|
169448
|
+
}
|
|
169449
|
+
injectByAssistantId(args.messages, persistedAnchor.messageId, part);
|
|
169450
|
+
return args.messages;
|
|
168527
169451
|
}
|
|
168528
169452
|
|
|
168529
169453
|
// src/reasoning-replay-pi.ts
|
|
@@ -169294,6 +170218,17 @@ var recentReduceBySession = new Map;
|
|
|
169294
170218
|
var liveModelBySession = new Map;
|
|
169295
170219
|
var toolUsageSinceUserTurn = new Map;
|
|
169296
170220
|
var latestUserMessageBySession = new Map;
|
|
170221
|
+
function resolvePiContextModelKey(ctx) {
|
|
170222
|
+
const model = ctx.model;
|
|
170223
|
+
if (!model)
|
|
170224
|
+
return;
|
|
170225
|
+
if (typeof model.provider !== "string" || model.provider.length === 0) {
|
|
170226
|
+
return;
|
|
170227
|
+
}
|
|
170228
|
+
if (typeof model.id !== "string" || model.id.length === 0)
|
|
170229
|
+
return;
|
|
170230
|
+
return `${model.provider}/${model.id}`;
|
|
170231
|
+
}
|
|
169297
170232
|
function readPiSessionMessageById(ctx, messageId) {
|
|
169298
170233
|
return readPiSessionMessages(ctx).find((message) => message.id === messageId) ?? null;
|
|
169299
170234
|
}
|
|
@@ -169516,8 +170451,32 @@ function registerPiContextHandler(pi, options) {
|
|
|
169516
170451
|
const isFirstContextPassForSession = !firstContextPassSeenBySession.has(sessionId);
|
|
169517
170452
|
firstContextPassSeenBySession.add(sessionId);
|
|
169518
170453
|
const piUsage = ctx.getContextUsage?.();
|
|
169519
|
-
|
|
169520
|
-
const
|
|
170454
|
+
const previousModelKey = liveModelBySession.get(sessionId);
|
|
170455
|
+
const currentModelKey = resolvePiContextModelKey(ctx);
|
|
170456
|
+
const modelChanged = previousModelKey !== undefined && currentModelKey !== undefined && previousModelKey !== currentModelKey;
|
|
170457
|
+
if (currentModelKey !== undefined) {
|
|
170458
|
+
liveModelBySession.set(sessionId, currentModelKey);
|
|
170459
|
+
}
|
|
170460
|
+
const sessionMetaForUsage = getOrCreateSessionMeta(options.db, sessionId);
|
|
170461
|
+
if ((isFirstContextPassForSession || modelChanged) && (sessionMetaForUsage.lastContextPercentage > 0 || sessionMetaForUsage.lastInputTokens > 0)) {
|
|
170462
|
+
const reason = isFirstContextPassForSession ? "first pass" : `model switch ${previousModelKey} -> ${currentModelKey}`;
|
|
170463
|
+
sessionLog(sessionId, `pi transform: ${reason} reset — percentage=${sessionMetaForUsage.lastContextPercentage.toFixed(1)}% tokens=${sessionMetaForUsage.lastInputTokens} — clearing stale usage state`);
|
|
170464
|
+
updateSessionMeta(options.db, sessionId, {
|
|
170465
|
+
lastContextPercentage: 0,
|
|
170466
|
+
lastInputTokens: 0
|
|
170467
|
+
});
|
|
170468
|
+
sessionMetaForUsage.lastContextPercentage = 0;
|
|
170469
|
+
sessionMetaForUsage.lastInputTokens = 0;
|
|
170470
|
+
}
|
|
170471
|
+
let usagePercentage = 0;
|
|
170472
|
+
let usageInputTokens = 0;
|
|
170473
|
+
if (sessionMetaForUsage.lastContextPercentage > 0 && sessionMetaForUsage.lastInputTokens > 0) {
|
|
170474
|
+
usagePercentage = sessionMetaForUsage.lastContextPercentage;
|
|
170475
|
+
usageInputTokens = sessionMetaForUsage.lastInputTokens;
|
|
170476
|
+
} else {
|
|
170477
|
+
usagePercentage = typeof piUsage?.percent === "number" ? piUsage.percent : 0;
|
|
170478
|
+
usageInputTokens = typeof piUsage?.tokens === "number" ? piUsage.tokens : 0;
|
|
170479
|
+
}
|
|
169521
170480
|
let usageContextLimit = typeof piUsage?.contextWindow === "number" && piUsage.contextWindow > 0 ? piUsage.contextWindow : undefined;
|
|
169522
170481
|
try {
|
|
169523
170482
|
const overflowState = getOverflowState(options.db, sessionId);
|
|
@@ -169531,7 +170490,7 @@ function registerPiContextHandler(pi, options) {
|
|
|
169531
170490
|
} catch (err) {
|
|
169532
170491
|
sessionLog(sessionId, `pi transform: overflow state read failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
169533
170492
|
}
|
|
169534
|
-
const sessionMeta =
|
|
170493
|
+
const sessionMeta = sessionMetaForUsage;
|
|
169535
170494
|
const modelKey = liveModelBySession.get(sessionId);
|
|
169536
170495
|
let schedulerDecision;
|
|
169537
170496
|
try {
|
|
@@ -169546,6 +170505,29 @@ function registerPiContextHandler(pi, options) {
|
|
|
169546
170505
|
schedulerDecision = "execute";
|
|
169547
170506
|
sessionLog(sessionId, `pi transform: large imported session detected (${piMessageCount} messages, no usage baseline) — forcing execute on first pass`);
|
|
169548
170507
|
}
|
|
170508
|
+
const schedulerDecisionEarly = schedulerDecision;
|
|
170509
|
+
const midTurn = isMidTurnPi(event, sessionId);
|
|
170510
|
+
const bypassReason = detectMidTurnBypassReason({
|
|
170511
|
+
contextUsage: { percentage: usagePercentage },
|
|
170512
|
+
sessionMeta,
|
|
170513
|
+
historyRefreshSessions,
|
|
170514
|
+
sessionId
|
|
170515
|
+
});
|
|
170516
|
+
const { midTurnAdjustedSchedulerDecision, sideEffect } = applyMidTurnDeferral({
|
|
170517
|
+
base: schedulerDecisionEarly,
|
|
170518
|
+
bypassReason,
|
|
170519
|
+
midTurn
|
|
170520
|
+
});
|
|
170521
|
+
if (sideEffect === "set-flag") {
|
|
170522
|
+
const flagPayload = {
|
|
170523
|
+
id: crypto2.randomUUID(),
|
|
170524
|
+
reason: `${schedulerDecisionEarly}-${bypassReason}`,
|
|
170525
|
+
recordedAt: Date.now()
|
|
170526
|
+
};
|
|
170527
|
+
setDeferredExecutePendingIfAbsent(options.db, sessionId, flagPayload);
|
|
170528
|
+
}
|
|
170529
|
+
schedulerDecision = midTurnAdjustedSchedulerDecision;
|
|
170530
|
+
sessionLog(sessionId, `pi [boundary-exec] base=${schedulerDecisionEarly} bypass=${bypassReason} midTurn=${midTurn} effective=${midTurnAdjustedSchedulerDecision} sideEffect=${sideEffect}`);
|
|
169549
170531
|
const forceMaterialization = usagePercentage >= FORCE_MATERIALIZATION_PERCENTAGE;
|
|
169550
170532
|
const isEmergency = usagePercentage >= EMERGENCY_BLOCK_PERCENTAGE;
|
|
169551
170533
|
if (isEmergency) {
|
|
@@ -169560,7 +170542,7 @@ function registerPiContextHandler(pi, options) {
|
|
|
169560
170542
|
try {
|
|
169561
170543
|
await Promise.race([
|
|
169562
170544
|
histPromise,
|
|
169563
|
-
new Promise((
|
|
170545
|
+
new Promise((resolve3) => setTimeout(resolve3, 30000))
|
|
169564
170546
|
]);
|
|
169565
170547
|
sessionLog(sessionId, "pi EMERGENCY: historian wait completed (or timed out)");
|
|
169566
170548
|
} catch {}
|
|
@@ -169652,6 +170634,22 @@ function registerPiContextHandler(pi, options) {
|
|
|
169652
170634
|
sessionLog(sessionId, `pi auto-search failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
169653
170635
|
}
|
|
169654
170636
|
}
|
|
170637
|
+
try {
|
|
170638
|
+
const sessionMetaForTodo = getOrCreateSessionMeta(options.db, sessionId);
|
|
170639
|
+
if (!sessionMetaForTodo.isSubagent && sessionMetaForTodo.lastTodoState !== "") {
|
|
170640
|
+
const isCacheBustingForTodo = isCacheBusting || result.executedWorkThisPass;
|
|
170641
|
+
outputMessages = injectSyntheticTodowriteForPi({
|
|
170642
|
+
db: options.db,
|
|
170643
|
+
sessionId,
|
|
170644
|
+
isSubagent: sessionMetaForTodo.isSubagent,
|
|
170645
|
+
isCacheBusting: isCacheBustingForTodo,
|
|
170646
|
+
lastTodoState: sessionMetaForTodo.lastTodoState,
|
|
170647
|
+
messages: outputMessages
|
|
170648
|
+
});
|
|
170649
|
+
}
|
|
170650
|
+
} catch (err) {
|
|
170651
|
+
sessionLog(sessionId, `pi synthetic todowrite injection failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
170652
|
+
}
|
|
169655
170653
|
clearLastTransformErrorIfSet(options.db, sessionId);
|
|
169656
170654
|
return { messages: outputMessages };
|
|
169657
170655
|
} catch (err) {
|
|
@@ -169833,16 +170831,25 @@ function maybeFireHistorian(args) {
|
|
|
169833
170831
|
}
|
|
169834
170832
|
let usage;
|
|
169835
170833
|
try {
|
|
169836
|
-
const
|
|
169837
|
-
if (
|
|
169838
|
-
|
|
169839
|
-
|
|
170834
|
+
const sessionMetaForUsage = getOrCreateSessionMeta(db, sessionId);
|
|
170835
|
+
if (sessionMetaForUsage.lastContextPercentage > 0 && sessionMetaForUsage.lastInputTokens > 0) {
|
|
170836
|
+
usage = {
|
|
170837
|
+
percentage: sessionMetaForUsage.lastContextPercentage,
|
|
170838
|
+
inputTokens: sessionMetaForUsage.lastInputTokens
|
|
170839
|
+
};
|
|
170840
|
+
sessionLog(sessionId, `pi-historian trigger eval: usage=${usage.percentage.toFixed(1)}% (${usage.inputTokens} tokens) [from session_meta], checking trigger...`);
|
|
170841
|
+
} else {
|
|
170842
|
+
const piUsage = ctx.getContextUsage?.();
|
|
170843
|
+
if (!piUsage || piUsage.tokens === null || piUsage.percent === null || piUsage.contextWindow === 0) {
|
|
170844
|
+
sessionLog(sessionId, `pi-historian trigger eval: no usage info yet (tokens=${piUsage?.tokens ?? "<no piUsage>"}, percent=${piUsage?.percent ?? "<no piUsage>"}, contextWindow=${piUsage?.contextWindow ?? "<no piUsage>"})`);
|
|
170845
|
+
return;
|
|
170846
|
+
}
|
|
170847
|
+
usage = {
|
|
170848
|
+
percentage: piUsage.percent,
|
|
170849
|
+
inputTokens: piUsage.tokens
|
|
170850
|
+
};
|
|
170851
|
+
sessionLog(sessionId, `pi-historian trigger eval: usage=${usage.percentage.toFixed(1)}% (${usage.inputTokens} tokens) [piUsage fallback], checking trigger...`);
|
|
169840
170852
|
}
|
|
169841
|
-
usage = {
|
|
169842
|
-
percentage: piUsage.percent,
|
|
169843
|
-
inputTokens: piUsage.tokens
|
|
169844
|
-
};
|
|
169845
|
-
sessionLog(sessionId, `pi-historian trigger eval: usage=${usage.percentage.toFixed(1)}% (${usage.inputTokens} tokens), checking trigger...`);
|
|
169846
170853
|
} catch (err) {
|
|
169847
170854
|
sessionLog(sessionId, `pi-historian trigger eval: getContextUsage threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
169848
170855
|
return;
|
|
@@ -169854,6 +170861,11 @@ function maybeFireHistorian(args) {
|
|
|
169854
170861
|
let triggered = false;
|
|
169855
170862
|
try {
|
|
169856
170863
|
if (isFirstContextPassForSession) {
|
|
170864
|
+
const sessionMeta2 = getOrCreateSessionMeta(db, sessionId);
|
|
170865
|
+
if (sessionMeta2.compartmentInProgress && !inFlightHistorian.has(sessionId)) {
|
|
170866
|
+
updateSessionMeta(db, sessionId, { compartmentInProgress: false });
|
|
170867
|
+
sessionLog(sessionId, "pi-historian: cleared stale compartmentInProgress flag on first context pass after restart");
|
|
170868
|
+
}
|
|
169857
170869
|
const failureState = getHistorianFailureState(db, sessionId);
|
|
169858
170870
|
const shouldRecoverOnFirstPass = failureState.failureCount > 0 && hasEligiblePiCompartmentHistory(db, sessionId);
|
|
169859
170871
|
if (shouldRecoverOnFirstPass) {
|
|
@@ -169897,6 +170909,7 @@ Historian previously failed ${failureState.failureCount} time(s), so magic-conte
|
|
|
169897
170909
|
}
|
|
169898
170910
|
}
|
|
169899
170911
|
async function runPipeline(args) {
|
|
170912
|
+
let executedWorkThisPass = false;
|
|
169900
170913
|
if (args.temporalAwareness) {
|
|
169901
170914
|
try {
|
|
169902
170915
|
const injected = injectPiTemporalMarkers(args.messages);
|
|
@@ -169927,9 +170940,15 @@ async function runPipeline(args) {
|
|
|
169927
170940
|
}
|
|
169928
170941
|
const hasPendingMaterializeSignal = hasPendingMaterialization(args.sessionId);
|
|
169929
170942
|
if (args.schedulerDecision === "execute" || args.forceMaterialization || hasPendingMaterializeSignal) {
|
|
169930
|
-
|
|
169931
|
-
|
|
169932
|
-
|
|
170943
|
+
try {
|
|
170944
|
+
applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags);
|
|
170945
|
+
executedWorkThisPass = true;
|
|
170946
|
+
if (hasPendingMaterializeSignal) {
|
|
170947
|
+
consumePendingMaterialization(args.sessionId);
|
|
170948
|
+
}
|
|
170949
|
+
} catch (err) {
|
|
170950
|
+
sessionLog(args.sessionId, `pi pending operations failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
170951
|
+
throw err;
|
|
169933
170952
|
}
|
|
169934
170953
|
}
|
|
169935
170954
|
const flushedSliceTags = getTagsByNumbers(args.db, args.sessionId, [
|
|
@@ -169983,6 +171002,7 @@ async function runPipeline(args) {
|
|
|
169983
171002
|
caveman: args.heuristics.caveman
|
|
169984
171003
|
});
|
|
169985
171004
|
heuristicsExecuted = true;
|
|
171005
|
+
executedWorkThisPass = true;
|
|
169986
171006
|
} catch (err) {
|
|
169987
171007
|
sessionLog(args.sessionId, `pi heuristic cleanup failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
|
|
169988
171008
|
}
|
|
@@ -170005,6 +171025,7 @@ async function runPipeline(args) {
|
|
|
170005
171025
|
sessionLog(args.sessionId, `pi reasoning cleared on execute: cleared=${clearOutcome.cleared} watermark=${prevWatermark}->${clearOutcome.newWatermark}`);
|
|
170006
171026
|
}
|
|
170007
171027
|
}
|
|
171028
|
+
executedWorkThisPass = true;
|
|
170008
171029
|
} catch (err) {
|
|
170009
171030
|
sessionLog(args.sessionId, `pi reasoning clearing failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
|
|
170010
171031
|
}
|
|
@@ -170027,6 +171048,16 @@ async function runPipeline(args) {
|
|
|
170027
171048
|
messages: args.messages,
|
|
170028
171049
|
isCacheBusting: args.isCacheBusting
|
|
170029
171050
|
});
|
|
171051
|
+
if (executedWorkThisPass) {
|
|
171052
|
+
try {
|
|
171053
|
+
const currentFlag = peekDeferredExecutePending(args.db, args.sessionId);
|
|
171054
|
+
if (currentFlag !== null) {
|
|
171055
|
+
clearDeferredExecutePendingIfMatches(args.db, args.sessionId, currentFlag);
|
|
171056
|
+
}
|
|
171057
|
+
} catch (err) {
|
|
171058
|
+
sessionLog(args.sessionId, `pi [boundary-exec] drain failed (continuing): ${err}`);
|
|
171059
|
+
}
|
|
171060
|
+
}
|
|
170030
171061
|
const outputMessages = transcript.getOutputMessages();
|
|
170031
171062
|
try {
|
|
170032
171063
|
const counts = tokenizePiMessages(outputMessages);
|
|
@@ -170040,6 +171071,7 @@ async function runPipeline(args) {
|
|
|
170040
171071
|
return {
|
|
170041
171072
|
messages: outputMessages,
|
|
170042
171073
|
heuristicsExecuted,
|
|
171074
|
+
executedWorkThisPass,
|
|
170043
171075
|
historyInjected: injectionResult?.injected ?? false,
|
|
170044
171076
|
heuristicsResult,
|
|
170045
171077
|
injectionResult
|
|
@@ -170236,7 +171268,7 @@ init_logger();
|
|
|
170236
171268
|
init_data_path();
|
|
170237
171269
|
init_logger();
|
|
170238
171270
|
await init_sqlite();
|
|
170239
|
-
import { join as
|
|
171271
|
+
import { join as join13 } from "node:path";
|
|
170240
171272
|
var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
170241
171273
|
function randomBase62(length) {
|
|
170242
171274
|
const chars = [];
|
|
@@ -170257,7 +171289,7 @@ function generatePartId(timestampMs, counter = 0n) {
|
|
|
170257
171289
|
return generateId("prt", timestampMs, counter);
|
|
170258
171290
|
}
|
|
170259
171291
|
function getOpenCodeDbPath3() {
|
|
170260
|
-
return
|
|
171292
|
+
return join13(getDataDir(), "opencode", "opencode.db");
|
|
170261
171293
|
}
|
|
170262
171294
|
var cachedWriteDb = null;
|
|
170263
171295
|
var REQUIRED_MESSAGE_COLUMNS = ["id", "session_id", "time_created", "time_updated", "data"];
|
|
@@ -170807,7 +171839,7 @@ async function runCompressorPass2(args) {
|
|
|
170807
171839
|
return null;
|
|
170808
171840
|
} finally {
|
|
170809
171841
|
if (agentSessionId) {
|
|
170810
|
-
await client.session.delete({ path: { id: agentSessionId }
|
|
171842
|
+
await client.session.delete({ path: { id: agentSessionId } }).catch((e) => {
|
|
170811
171843
|
sessionLog(sessionId, "compressor: session cleanup failed:", getErrorMessage(e));
|
|
170812
171844
|
});
|
|
170813
171845
|
}
|
|
@@ -170815,11 +171847,13 @@ async function runCompressorPass2(args) {
|
|
|
170815
171847
|
}
|
|
170816
171848
|
|
|
170817
171849
|
// ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
|
|
170818
|
-
import { mkdirSync as
|
|
170819
|
-
import {
|
|
170820
|
-
import { join as join12 } from "node:path";
|
|
171850
|
+
import { mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
171851
|
+
import { join as join14 } from "node:path";
|
|
170821
171852
|
init_magic_context();
|
|
170822
|
-
|
|
171853
|
+
init_data_path();
|
|
171854
|
+
function historianResponseDumpDir(directory) {
|
|
171855
|
+
return getProjectMagicContextHistorianDir(directory);
|
|
171856
|
+
}
|
|
170823
171857
|
var MAX_HISTORIAN_RETRIES = 2;
|
|
170824
171858
|
async function runValidatedHistorianPass(args) {
|
|
170825
171859
|
const firstRun = await runHistorianPrompt({
|
|
@@ -170971,7 +172005,7 @@ async function runHistorianPrompt(args) {
|
|
|
170971
172005
|
if (!result) {
|
|
170972
172006
|
return { ok: false, error: "Historian returned no assistant output." };
|
|
170973
172007
|
}
|
|
170974
|
-
const dumpPath = dumpHistorianResponse(parentSessionId, dumpLabel ?? "historian-response", result);
|
|
172008
|
+
const dumpPath = dumpHistorianResponse(parentSessionId, sessionDirectory, dumpLabel ?? "historian-response", result);
|
|
170975
172009
|
return { ok: true, result, dumpPath };
|
|
170976
172010
|
} catch (modelError) {
|
|
170977
172011
|
const desc = describeError(modelError);
|
|
@@ -170982,7 +172016,7 @@ async function runHistorianPrompt(args) {
|
|
|
170982
172016
|
};
|
|
170983
172017
|
} finally {
|
|
170984
172018
|
if (agentSessionId) {
|
|
170985
|
-
await client.session.delete({ path: { id: agentSessionId }
|
|
172019
|
+
await client.session.delete({ path: { id: agentSessionId } }).catch((e) => {
|
|
170986
172020
|
sessionLog(parentSessionId, "compartment agent: session cleanup failed", getErrorMessage(e));
|
|
170987
172021
|
});
|
|
170988
172022
|
}
|
|
@@ -171047,8 +172081,8 @@ function isTransientHistorianPromptError(message) {
|
|
|
171047
172081
|
].some((token) => normalized.includes(token));
|
|
171048
172082
|
}
|
|
171049
172083
|
function sleep(ms) {
|
|
171050
|
-
return new Promise((
|
|
171051
|
-
setTimeout(
|
|
172084
|
+
return new Promise((resolve3) => {
|
|
172085
|
+
setTimeout(resolve3, ms);
|
|
171052
172086
|
});
|
|
171053
172087
|
}
|
|
171054
172088
|
function cleanupHistorianDump(sessionId, dumpPath) {
|
|
@@ -171063,12 +172097,13 @@ function cleanupHistorianDump(sessionId, dumpPath) {
|
|
|
171063
172097
|
});
|
|
171064
172098
|
}
|
|
171065
172099
|
}
|
|
171066
|
-
function dumpHistorianResponse(sessionId, label, text) {
|
|
172100
|
+
function dumpHistorianResponse(sessionId, directory, label, text) {
|
|
171067
172101
|
try {
|
|
171068
|
-
|
|
172102
|
+
const dumpDir = historianResponseDumpDir(directory);
|
|
172103
|
+
mkdirSync6(dumpDir, { recursive: true });
|
|
171069
172104
|
const safeSessionId = sanitizeDumpName(sessionId);
|
|
171070
172105
|
const safeLabel = sanitizeDumpName(label);
|
|
171071
|
-
const dumpPath =
|
|
172106
|
+
const dumpPath = join14(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
|
|
171072
172107
|
writeFileSync3(dumpPath, text, "utf8");
|
|
171073
172108
|
sessionLog(sessionId, "compartment agent: historian response dumped", {
|
|
171074
172109
|
label,
|
|
@@ -171293,9 +172328,18 @@ async function executePartialRecompInternal(deps, range) {
|
|
|
171293
172328
|
}
|
|
171294
172329
|
setRecompPartialRange(db, sessionId, null);
|
|
171295
172330
|
clearCompressionDepthRange(db, sessionId, snapStart, snapEnd);
|
|
171296
|
-
|
|
171297
|
-
|
|
172331
|
+
if (deps.preserveInjectionCacheUntilConsumed !== true) {
|
|
172332
|
+
clearInjectionCache(sessionId);
|
|
172333
|
+
}
|
|
172334
|
+
deps.onCompartmentStatePublished?.(sessionId);
|
|
171298
172335
|
const lastEnd = merged[merged.length - 1]?.endMessage ?? snapEnd;
|
|
172336
|
+
if (deps.experimentalCompactionMarkers && lastEnd > 0) {
|
|
172337
|
+
updateCompactionMarkerAfterPublication(db, sessionId, lastEnd, deps.directory);
|
|
172338
|
+
const stalePending = getPendingCompactionMarkerState(db, sessionId);
|
|
172339
|
+
if (stalePending) {
|
|
172340
|
+
clearPendingCompactionMarkerStateIf(db, sessionId, stalePending);
|
|
172341
|
+
}
|
|
172342
|
+
}
|
|
171299
172343
|
return { compartmentCount: merged.length, lastEndMessage: lastEnd };
|
|
171300
172344
|
};
|
|
171301
172345
|
const protectedTailStart = getProtectedTailStartOrdinal(sessionId);
|
|
@@ -171510,8 +172554,10 @@ async function executeContextRecompInternal(deps) {
|
|
|
171510
172554
|
if (!promoted2)
|
|
171511
172555
|
return null;
|
|
171512
172556
|
clearCompressionDepth(db, sessionId);
|
|
171513
|
-
|
|
171514
|
-
|
|
172557
|
+
if (deps.preserveInjectionCacheUntilConsumed !== true) {
|
|
172558
|
+
clearInjectionCache(sessionId);
|
|
172559
|
+
}
|
|
172560
|
+
deps.onCompartmentStatePublished?.(sessionId);
|
|
171515
172561
|
if (deps.directory && deps.memoryEnabled !== false && deps.autoPromote !== false) {
|
|
171516
172562
|
promoteSessionFactsToMemory(db, sessionId, resolveProjectIdentity(deps.directory), promoted2.facts);
|
|
171517
172563
|
}
|
|
@@ -171521,6 +172567,10 @@ async function executeContextRecompInternal(deps) {
|
|
|
171521
172567
|
}
|
|
171522
172568
|
if (deps.experimentalCompactionMarkers && lastCompartmentEnd2 > 0) {
|
|
171523
172569
|
updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd2, deps.directory);
|
|
172570
|
+
const stalePending = getPendingCompactionMarkerState(db, sessionId);
|
|
172571
|
+
if (stalePending) {
|
|
172572
|
+
clearPendingCompactionMarkerStateIf(db, sessionId, stalePending);
|
|
172573
|
+
}
|
|
171524
172574
|
}
|
|
171525
172575
|
return [
|
|
171526
172576
|
`Persisted ${promoted2.compartments.length} compartment${promoted2.compartments.length === 1 ? "" : "s"} from ${passCount} successful pass${passCount === 1 ? "" : "es"}.`,
|
|
@@ -171586,7 +172636,7 @@ Nothing was written.`;
|
|
|
171586
172636
|
|
|
171587
172637
|
This is your first run. No existing compartments or facts.` : "This is your first run. No existing state.";
|
|
171588
172638
|
cleanupHistorianStateFile(currentStateFilePath);
|
|
171589
|
-
currentStateFilePath = maybeWriteHistorianStateFile(sessionId, existingState);
|
|
172639
|
+
currentStateFilePath = maybeWriteHistorianStateFile(sessionId, existingState, sessionDirectory);
|
|
171590
172640
|
const prompt = buildCompartmentAgentPrompt(existingState, `Messages ${chunk.startIndex}-${chunk.endIndex}:
|
|
171591
172641
|
|
|
171592
172642
|
${chunk.text}`, { stateFilePath: currentStateFilePath });
|
|
@@ -171682,8 +172732,10 @@ Nothing was written.`;
|
|
|
171682
172732
|
clearRecompStaging(db, sessionId);
|
|
171683
172733
|
}
|
|
171684
172734
|
clearCompressionDepth(db, sessionId);
|
|
171685
|
-
|
|
171686
|
-
|
|
172735
|
+
if (deps.preserveInjectionCacheUntilConsumed !== true) {
|
|
172736
|
+
clearInjectionCache(sessionId);
|
|
172737
|
+
}
|
|
172738
|
+
deps.onCompartmentStatePublished?.(sessionId);
|
|
171687
172739
|
const finalCompartments = promoted?.compartments ?? candidateCompartments;
|
|
171688
172740
|
const finalFacts = promoted?.facts ?? candidateFacts;
|
|
171689
172741
|
if (deps.directory && deps.memoryEnabled !== false && deps.autoPromote !== false) {
|
|
@@ -171732,21 +172784,39 @@ Staging data preserved for resume on next attempt.`;
|
|
|
171732
172784
|
|
|
171733
172785
|
// ../plugin/src/hooks/magic-context/compartment-runner.ts
|
|
171734
172786
|
var activeRuns = new Map;
|
|
172787
|
+
function markActiveCompartmentRunPublished(sessionId) {
|
|
172788
|
+
const activeRun = activeRuns.get(sessionId);
|
|
172789
|
+
if (activeRun)
|
|
172790
|
+
activeRun.published = true;
|
|
172791
|
+
}
|
|
172792
|
+
function withPublishedCallback(deps) {
|
|
172793
|
+
return {
|
|
172794
|
+
...deps,
|
|
172795
|
+
onCompartmentStatePublished: (sid) => {
|
|
172796
|
+
markActiveCompartmentRunPublished(sid);
|
|
172797
|
+
deps.onCompartmentStatePublished?.(sid);
|
|
172798
|
+
}
|
|
172799
|
+
};
|
|
172800
|
+
}
|
|
171735
172801
|
async function executeContextRecomp(deps, options = {}) {
|
|
171736
172802
|
const { sessionId } = deps;
|
|
171737
172803
|
if (activeRuns.has(sessionId)) {
|
|
171738
172804
|
return "## Magic Recomp\n\nHistorian is already running for this session. Wait for it to finish, then try `/ctx-recomp` again.";
|
|
171739
172805
|
}
|
|
171740
|
-
const
|
|
171741
|
-
|
|
172806
|
+
const runnerDeps = withPublishedCallback(deps);
|
|
172807
|
+
const promise2 = options.range ? executePartialRecompInternal(runnerDeps, options.range) : executeContextRecompInternal(runnerDeps);
|
|
172808
|
+
const wrappedPromise = promise2.then(() => {
|
|
171742
172809
|
return;
|
|
171743
172810
|
}).catch((err) => {
|
|
171744
172811
|
sessionLog(sessionId, "compartment agent: recomp unhandled rejection:", err);
|
|
171745
|
-
})
|
|
172812
|
+
});
|
|
172813
|
+
activeRuns.set(sessionId, { promise: wrappedPromise, published: false });
|
|
171746
172814
|
try {
|
|
171747
172815
|
return await promise2;
|
|
171748
172816
|
} finally {
|
|
171749
|
-
activeRuns.
|
|
172817
|
+
if (activeRuns.get(sessionId)?.promise === wrappedPromise) {
|
|
172818
|
+
activeRuns.delete(sessionId);
|
|
172819
|
+
}
|
|
171750
172820
|
}
|
|
171751
172821
|
}
|
|
171752
172822
|
|
|
@@ -172235,11 +173305,11 @@ import {
|
|
|
172235
173305
|
matchesKey,
|
|
172236
173306
|
truncateToWidth,
|
|
172237
173307
|
visibleWidth
|
|
172238
|
-
} from "@
|
|
173308
|
+
} from "@earendil-works/pi-tui";
|
|
172239
173309
|
// package.json
|
|
172240
173310
|
var package_default = {
|
|
172241
173311
|
name: "@cortexkit/pi-magic-context",
|
|
172242
|
-
version: "0.
|
|
173312
|
+
version: "0.20.0",
|
|
172243
173313
|
type: "module",
|
|
172244
173314
|
description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
|
|
172245
173315
|
main: "dist/index.js",
|
|
@@ -172265,7 +173335,7 @@ var package_default = {
|
|
|
172265
173335
|
"README.md"
|
|
172266
173336
|
],
|
|
172267
173337
|
scripts: {
|
|
172268
|
-
build: "bun build src/index.ts src/subagent-entry.ts --outdir dist --target node --format esm --external @
|
|
173338
|
+
build: "bun build src/index.ts src/subagent-entry.ts --outdir dist --target node --format esm --external @earendil-works/pi-coding-agent --external @earendil-works/pi-tui --external @huggingface/transformers --external better-sqlite3",
|
|
172269
173339
|
typecheck: "tsc --noEmit",
|
|
172270
173340
|
test: "bun test",
|
|
172271
173341
|
lint: "biome check src",
|
|
@@ -172285,15 +173355,15 @@ var package_default = {
|
|
|
172285
173355
|
},
|
|
172286
173356
|
devDependencies: {
|
|
172287
173357
|
"@biomejs/biome": "^2.4.7",
|
|
172288
|
-
"@
|
|
172289
|
-
"@
|
|
173358
|
+
"@earendil-works/pi-coding-agent": "^0.74.0",
|
|
173359
|
+
"@earendil-works/pi-tui": "^0.74.0",
|
|
172290
173360
|
"@types/better-sqlite3": "^7.6.13",
|
|
172291
173361
|
"@types/node": "^22.0.0",
|
|
172292
173362
|
typescript: "^5.8.0"
|
|
172293
173363
|
},
|
|
172294
173364
|
peerDependencies: {
|
|
172295
|
-
"@
|
|
172296
|
-
"@
|
|
173365
|
+
"@earendil-works/pi-coding-agent": "^0.74.0",
|
|
173366
|
+
"@earendil-works/pi-tui": "^0.74.0"
|
|
172297
173367
|
},
|
|
172298
173368
|
exports: {
|
|
172299
173369
|
".": {
|
|
@@ -172776,14 +173846,14 @@ function readHistorianState(db, sessionId, meta3) {
|
|
|
172776
173846
|
|
|
172777
173847
|
// src/config/index.ts
|
|
172778
173848
|
init_magic_context();
|
|
172779
|
-
import { existsSync as
|
|
172780
|
-
import { homedir as
|
|
172781
|
-
import { join as
|
|
173849
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6 } from "node:fs";
|
|
173850
|
+
import { homedir as homedir5 } from "node:os";
|
|
173851
|
+
import { join as join15 } from "node:path";
|
|
172782
173852
|
|
|
172783
173853
|
// ../plugin/src/config/variable.ts
|
|
172784
|
-
import { existsSync as
|
|
172785
|
-
import { homedir as
|
|
172786
|
-
import { dirname as
|
|
173854
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "node:fs";
|
|
173855
|
+
import { homedir as homedir4 } from "node:os";
|
|
173856
|
+
import { dirname as dirname4, isAbsolute, resolve as resolve3 } from "node:path";
|
|
172787
173857
|
var ENV_PATTERN = /\{env:([^}]+)\}/g;
|
|
172788
173858
|
var FILE_PATTERN = /\{file:([^}]+)\}/g;
|
|
172789
173859
|
function substituteConfigVariables(input) {
|
|
@@ -172802,7 +173872,7 @@ function substituteConfigVariables(input) {
|
|
|
172802
173872
|
if (fileMatches.length === 0) {
|
|
172803
173873
|
return { text, warnings };
|
|
172804
173874
|
}
|
|
172805
|
-
const configDir = input.configPath ?
|
|
173875
|
+
const configDir = input.configPath ? dirname4(input.configPath) : process.cwd();
|
|
172806
173876
|
let output = "";
|
|
172807
173877
|
let cursor = 0;
|
|
172808
173878
|
for (const match of fileMatches) {
|
|
@@ -172820,17 +173890,17 @@ function substituteConfigVariables(input) {
|
|
|
172820
173890
|
}
|
|
172821
173891
|
let filePath = rawPath.trim();
|
|
172822
173892
|
if (filePath.startsWith("~/")) {
|
|
172823
|
-
filePath =
|
|
173893
|
+
filePath = resolve3(homedir4(), filePath.slice(2));
|
|
172824
173894
|
} else if (!isAbsolute(filePath)) {
|
|
172825
|
-
filePath =
|
|
173895
|
+
filePath = resolve3(configDir, filePath);
|
|
172826
173896
|
}
|
|
172827
|
-
if (!
|
|
173897
|
+
if (!existsSync9(filePath)) {
|
|
172828
173898
|
warnings.push(`File not found for ${token} (resolved to ${filePath}); using empty string`);
|
|
172829
173899
|
continue;
|
|
172830
173900
|
}
|
|
172831
173901
|
let contents;
|
|
172832
173902
|
try {
|
|
172833
|
-
contents =
|
|
173903
|
+
contents = readFileSync5(filePath, "utf-8").trim();
|
|
172834
173904
|
} catch (error51) {
|
|
172835
173905
|
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
172836
173906
|
warnings.push(`Failed to read file for ${token} (${filePath}): ${message}; using empty string`);
|
|
@@ -172843,23 +173913,23 @@ function substituteConfigVariables(input) {
|
|
|
172843
173913
|
}
|
|
172844
173914
|
|
|
172845
173915
|
// src/config/index.ts
|
|
172846
|
-
var
|
|
173916
|
+
var import_comment_json2 = __toESM(require_src2(), 1);
|
|
172847
173917
|
var CONFIG_FILE_NAME = "magic-context";
|
|
172848
173918
|
function getProjectConfigPaths(cwd) {
|
|
172849
|
-
const basePath =
|
|
173919
|
+
const basePath = join15(cwd, ".pi", CONFIG_FILE_NAME);
|
|
172850
173920
|
return [`${basePath}.jsonc`, `${basePath}.json`];
|
|
172851
173921
|
}
|
|
172852
173922
|
function getUserConfigPaths() {
|
|
172853
|
-
const home = process.env.HOME ??
|
|
172854
|
-
const basePath =
|
|
173923
|
+
const home = process.env.HOME ?? homedir5();
|
|
173924
|
+
const basePath = join15(home, ".pi", "agent", CONFIG_FILE_NAME);
|
|
172855
173925
|
return [`${basePath}.jsonc`, `${basePath}.json`];
|
|
172856
173926
|
}
|
|
172857
173927
|
function resolveFirstExisting(paths) {
|
|
172858
|
-
return paths.find((path5) =>
|
|
173928
|
+
return paths.find((path5) => existsSync10(path5));
|
|
172859
173929
|
}
|
|
172860
173930
|
function loadConfigFile(path5, scope) {
|
|
172861
173931
|
try {
|
|
172862
|
-
const rawText =
|
|
173932
|
+
const rawText = readFileSync6(path5, "utf-8");
|
|
172863
173933
|
const substituted = substituteConfigVariables({
|
|
172864
173934
|
text: rawText,
|
|
172865
173935
|
configPath: path5
|
|
@@ -172867,7 +173937,7 @@ function loadConfigFile(path5, scope) {
|
|
|
172867
173937
|
return {
|
|
172868
173938
|
path: path5,
|
|
172869
173939
|
scope,
|
|
172870
|
-
config:
|
|
173940
|
+
config: import_comment_json2.parse(substituted.text),
|
|
172871
173941
|
warnings: substituted.warnings.map((warning) => `${path5}: ${warning}`)
|
|
172872
173942
|
};
|
|
172873
173943
|
} catch (error51) {
|
|
@@ -172981,6 +174051,42 @@ function loadPiConfig(opts = {}) {
|
|
|
172981
174051
|
};
|
|
172982
174052
|
}
|
|
172983
174053
|
|
|
174054
|
+
// src/pi-pressure.ts
|
|
174055
|
+
function extractAssistantUsage(message) {
|
|
174056
|
+
if (!message || typeof message !== "object")
|
|
174057
|
+
return null;
|
|
174058
|
+
const m = message;
|
|
174059
|
+
if (m.role !== "assistant")
|
|
174060
|
+
return null;
|
|
174061
|
+
if (!m.usage || typeof m.usage !== "object")
|
|
174062
|
+
return null;
|
|
174063
|
+
const u = m.usage;
|
|
174064
|
+
const result = {};
|
|
174065
|
+
if (typeof u.input === "number")
|
|
174066
|
+
result.input = u.input;
|
|
174067
|
+
if (typeof u.output === "number")
|
|
174068
|
+
result.output = u.output;
|
|
174069
|
+
if (typeof u.cacheRead === "number")
|
|
174070
|
+
result.cacheRead = u.cacheRead;
|
|
174071
|
+
if (typeof u.cacheWrite === "number")
|
|
174072
|
+
result.cacheWrite = u.cacheWrite;
|
|
174073
|
+
if (typeof u.totalTokens === "number")
|
|
174074
|
+
result.totalTokens = u.totalTokens;
|
|
174075
|
+
return result;
|
|
174076
|
+
}
|
|
174077
|
+
function computePiPressure(usage, contextLimit) {
|
|
174078
|
+
if (!usage)
|
|
174079
|
+
return null;
|
|
174080
|
+
const input = usage.input ?? 0;
|
|
174081
|
+
const cacheRead = usage.cacheRead ?? 0;
|
|
174082
|
+
const cacheWrite = usage.cacheWrite ?? 0;
|
|
174083
|
+
const inputTokens = input + cacheRead + cacheWrite;
|
|
174084
|
+
if (inputTokens === 0)
|
|
174085
|
+
return null;
|
|
174086
|
+
const percentage = contextLimit > 0 ? inputTokens / contextLimit * 100 : 0;
|
|
174087
|
+
return { inputTokens, percentage };
|
|
174088
|
+
}
|
|
174089
|
+
|
|
172984
174090
|
// src/strip-tag-prefix.ts
|
|
172985
174091
|
var TAG_PREFIX_REGEX2 = /^(\u00a7\d+\u00a7\s*)+/;
|
|
172986
174092
|
function stripTagPrefixFromAssistantMessage(message) {
|
|
@@ -173006,9 +174112,9 @@ function stripTagPrefixFromAssistantMessage(message) {
|
|
|
173006
174112
|
}
|
|
173007
174113
|
|
|
173008
174114
|
// src/system-prompt.ts
|
|
173009
|
-
import { createHash as
|
|
173010
|
-
import { existsSync as
|
|
173011
|
-
import { join as
|
|
174115
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
174116
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "node:fs";
|
|
174117
|
+
import { join as join17 } from "node:path";
|
|
173012
174118
|
|
|
173013
174119
|
// ../plugin/src/agents/magic-context-prompt.ts
|
|
173014
174120
|
function getToolHistoryGuidance(dropToolStructure) {
|
|
@@ -173097,23 +174203,146 @@ ${GENERIC_SECTION}
|
|
|
173097
174203
|
Prefer many small targeted operations over one large blanket operation. Compress early and often — don't wait for warnings.`;
|
|
173098
174204
|
}
|
|
173099
174205
|
|
|
174206
|
+
// ../plugin/src/hooks/magic-context/key-files-block.ts
|
|
174207
|
+
import { readFileSync as readFileSync7, realpathSync as realpathSync3 } from "node:fs";
|
|
174208
|
+
import { join as join16, sep as sep2 } from "node:path";
|
|
174209
|
+
init_logger();
|
|
174210
|
+
var cachedKeyFilesBySession = new Map;
|
|
174211
|
+
var staleUpdates = new Map;
|
|
174212
|
+
function staleKey(update) {
|
|
174213
|
+
return `${update.projectPath}\x00${update.path}\x00${update.generatedAtWitness}\x00${update.staleReason}`;
|
|
174214
|
+
}
|
|
174215
|
+
function queueStaleUpdate(projectPath, path5, generatedAtWitness, staleReason) {
|
|
174216
|
+
const update = { projectPath, path: path5, generatedAtWitness, staleReason };
|
|
174217
|
+
staleUpdates.set(staleKey(update), update);
|
|
174218
|
+
}
|
|
174219
|
+
function flushStaleUpdates(db) {
|
|
174220
|
+
if (staleUpdates.size === 0)
|
|
174221
|
+
return 0;
|
|
174222
|
+
const updates = [...staleUpdates.values()];
|
|
174223
|
+
staleUpdates.clear();
|
|
174224
|
+
const stmt = db.prepare(`UPDATE project_key_files
|
|
174225
|
+
SET stale_reason = ?1
|
|
174226
|
+
WHERE project_path = ?2
|
|
174227
|
+
AND path = ?3
|
|
174228
|
+
AND generated_at = ?4
|
|
174229
|
+
AND (stale_reason IS NULL OR stale_reason != ?1)`);
|
|
174230
|
+
let changed = 0;
|
|
174231
|
+
for (const update of updates) {
|
|
174232
|
+
try {
|
|
174233
|
+
changed += stmt.run(update.staleReason, update.projectPath, update.path, update.generatedAtWitness).changes;
|
|
174234
|
+
} catch (error51) {
|
|
174235
|
+
log("[key-files] flushStaleUpdates failed:", error51);
|
|
174236
|
+
}
|
|
174237
|
+
}
|
|
174238
|
+
return changed;
|
|
174239
|
+
}
|
|
174240
|
+
function clearKeyFilesCacheForSession(sessionId) {
|
|
174241
|
+
cachedKeyFilesBySession.delete(sessionId);
|
|
174242
|
+
}
|
|
174243
|
+
function isUnderProject(projectPath, absPath) {
|
|
174244
|
+
const root = realpathSync3(projectPath);
|
|
174245
|
+
return absPath.startsWith(root + sep2) || absPath === root;
|
|
174246
|
+
}
|
|
174247
|
+
function buildKeyFilesBlock(db, projectPath, config2 = { enabled: true, tokenBudget: 1e4 }) {
|
|
174248
|
+
if (!config2.enabled)
|
|
174249
|
+
return null;
|
|
174250
|
+
if (!isAftAvailable())
|
|
174251
|
+
return null;
|
|
174252
|
+
const rows = readCurrentKeyFiles(db, projectPath);
|
|
174253
|
+
if (rows.length === 0)
|
|
174254
|
+
return null;
|
|
174255
|
+
for (const row of rows) {
|
|
174256
|
+
if (row.staleReason !== null)
|
|
174257
|
+
continue;
|
|
174258
|
+
let nextStale = null;
|
|
174259
|
+
let observed = false;
|
|
174260
|
+
try {
|
|
174261
|
+
const absPath = join16(projectPath, row.path);
|
|
174262
|
+
const real = realpathSync3(absPath);
|
|
174263
|
+
if (!isUnderProject(projectPath, real)) {
|
|
174264
|
+
nextStale = "missing";
|
|
174265
|
+
observed = true;
|
|
174266
|
+
} else {
|
|
174267
|
+
const diskHash = sha256(readFileSync7(real));
|
|
174268
|
+
if (diskHash !== row.contentHash)
|
|
174269
|
+
nextStale = "content_drift";
|
|
174270
|
+
observed = true;
|
|
174271
|
+
}
|
|
174272
|
+
} catch (error51) {
|
|
174273
|
+
const code = error51?.code;
|
|
174274
|
+
if (code === "ENOENT" || code === "ELOOP") {
|
|
174275
|
+
nextStale = "missing";
|
|
174276
|
+
observed = true;
|
|
174277
|
+
} else {
|
|
174278
|
+
log(`[key-files] freshness check transient failure: ${row.path}: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
174279
|
+
}
|
|
174280
|
+
}
|
|
174281
|
+
if (observed && nextStale !== null) {
|
|
174282
|
+
queueStaleUpdate(row.projectPath, row.path, row.generatedAt, nextStale);
|
|
174283
|
+
}
|
|
174284
|
+
}
|
|
174285
|
+
const blocks = [];
|
|
174286
|
+
let used = 0;
|
|
174287
|
+
for (const row of rows) {
|
|
174288
|
+
if (used + row.localTokenEstimate > config2.tokenBudget)
|
|
174289
|
+
break;
|
|
174290
|
+
blocks.push(` <key-file path="${escapeXmlAttr(row.path)}">
|
|
174291
|
+
${escapeXmlContent(row.content)}
|
|
174292
|
+
</key-file>`);
|
|
174293
|
+
used += row.localTokenEstimate;
|
|
174294
|
+
}
|
|
174295
|
+
if (blocks.length === 0)
|
|
174296
|
+
return null;
|
|
174297
|
+
const rendered = `<key-files>
|
|
174298
|
+
${blocks.join(`
|
|
174299
|
+
`)}
|
|
174300
|
+
</key-files>`;
|
|
174301
|
+
flushStaleUpdates(db);
|
|
174302
|
+
return rendered;
|
|
174303
|
+
}
|
|
174304
|
+
function readVersionedKeyFiles(args) {
|
|
174305
|
+
const config2 = args.config ?? { enabled: true, tokenBudget: 1e4 };
|
|
174306
|
+
if (args.sessionMeta.isSubagent)
|
|
174307
|
+
return null;
|
|
174308
|
+
if (!config2.enabled)
|
|
174309
|
+
return null;
|
|
174310
|
+
if (!isAftAvailable())
|
|
174311
|
+
return null;
|
|
174312
|
+
const projectPath = resolveProjectPath(args.directory ?? args.sessionMeta.sessionId);
|
|
174313
|
+
if (!projectPath)
|
|
174314
|
+
return null;
|
|
174315
|
+
const currentVersion = getKeyFilesVersion(args.db, projectPath);
|
|
174316
|
+
if (args.sessionId) {
|
|
174317
|
+
const cached2 = cachedKeyFilesBySession.get(args.sessionId);
|
|
174318
|
+
if (cached2 && !args.isCacheBusting && cached2.version === currentVersion) {
|
|
174319
|
+
return cached2.value;
|
|
174320
|
+
}
|
|
174321
|
+
}
|
|
174322
|
+
const value = buildKeyFilesBlock(args.db, projectPath, config2);
|
|
174323
|
+
if (args.sessionId) {
|
|
174324
|
+
cachedKeyFilesBySession.set(args.sessionId, { value, version: currentVersion });
|
|
174325
|
+
if (value)
|
|
174326
|
+
sessionLog(args.sessionId, `loaded key-files block (v${currentVersion}, ${value.length} chars)`);
|
|
174327
|
+
}
|
|
174328
|
+
return value;
|
|
174329
|
+
}
|
|
174330
|
+
|
|
173100
174331
|
// src/system-prompt.ts
|
|
173101
174332
|
init_logger();
|
|
173102
174333
|
var PROJECT_DOCS_MARKER = "<project-docs>";
|
|
173103
174334
|
var USER_PROFILE_MARKER = "<user-profile>";
|
|
173104
|
-
var KEY_FILES_MARKER = "<key-files>";
|
|
173105
174335
|
var stickyDateBySession = new Map;
|
|
173106
174336
|
var cachedDocsBySession = new Map;
|
|
173107
174337
|
var cachedUserProfileBySession = new Map;
|
|
173108
|
-
var cachedKeyFilesBySession = new Map;
|
|
173109
174338
|
var DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
|
|
173110
174339
|
function readProjectDocs(directory) {
|
|
173111
174340
|
const sections = [];
|
|
173112
174341
|
for (const filename of DOC_FILES) {
|
|
173113
|
-
const filePath =
|
|
174342
|
+
const filePath = join17(directory, filename);
|
|
173114
174343
|
try {
|
|
173115
|
-
if (
|
|
173116
|
-
const content =
|
|
174344
|
+
if (existsSync11(filePath)) {
|
|
174345
|
+
const content = readFileSync8(filePath, "utf-8").trim();
|
|
173117
174346
|
if (content.length > 0) {
|
|
173118
174347
|
sections.push(`<${filename}>
|
|
173119
174348
|
${content}
|
|
@@ -173142,56 +174371,6 @@ function buildUserProfileBlock(db) {
|
|
|
173142
174371
|
${items}
|
|
173143
174372
|
</user-profile>`;
|
|
173144
174373
|
}
|
|
173145
|
-
function buildKeyFilesBlock(db, sessionId, directory, tokenBudget) {
|
|
173146
|
-
const entries = getKeyFiles(db, sessionId);
|
|
173147
|
-
if (entries.length === 0)
|
|
173148
|
-
return null;
|
|
173149
|
-
const sections = [];
|
|
173150
|
-
const projectRoot = resolve2(directory);
|
|
173151
|
-
let remainingBudgetTokens = tokenBudget;
|
|
173152
|
-
for (const entry of entries) {
|
|
173153
|
-
try {
|
|
173154
|
-
const absPath = resolve2(directory, entry.filePath);
|
|
173155
|
-
if (!absPath.startsWith(projectRoot + sep) && absPath !== projectRoot) {
|
|
173156
|
-
log(`[magic-context-pi] key file path escapes project root, skipping: ${entry.filePath}`);
|
|
173157
|
-
continue;
|
|
173158
|
-
}
|
|
173159
|
-
if (!existsSync9(absPath))
|
|
173160
|
-
continue;
|
|
173161
|
-
let realPath;
|
|
173162
|
-
try {
|
|
173163
|
-
realPath = realpathSync(absPath);
|
|
173164
|
-
} catch {
|
|
173165
|
-
continue;
|
|
173166
|
-
}
|
|
173167
|
-
if (!realPath.startsWith(projectRoot + sep) && realPath !== projectRoot) {
|
|
173168
|
-
log(`[magic-context-pi] key file symlink escapes project root, skipping: ${entry.filePath} → ${realPath}`);
|
|
173169
|
-
continue;
|
|
173170
|
-
}
|
|
173171
|
-
const content = readFileSync4(realPath, "utf-8").trim();
|
|
173172
|
-
if (content.length === 0)
|
|
173173
|
-
continue;
|
|
173174
|
-
const fileTokens = estimateTokens(content);
|
|
173175
|
-
if (fileTokens > remainingBudgetTokens) {
|
|
173176
|
-
log(`[magic-context-pi] key file ${entry.filePath} exceeds remaining budget (${fileTokens} > ${remainingBudgetTokens}), skipping`);
|
|
173177
|
-
continue;
|
|
173178
|
-
}
|
|
173179
|
-
remainingBudgetTokens -= fileTokens;
|
|
173180
|
-
sections.push(`<file path="${escapeXmlAttr(entry.filePath)}">
|
|
173181
|
-
${escapeXmlContent(content)}
|
|
173182
|
-
</file>`);
|
|
173183
|
-
} catch (error51) {
|
|
173184
|
-
log(`[magic-context-pi] failed to read key file ${entry.filePath}:`, error51);
|
|
173185
|
-
}
|
|
173186
|
-
}
|
|
173187
|
-
if (sections.length === 0)
|
|
173188
|
-
return null;
|
|
173189
|
-
return `${KEY_FILES_MARKER}
|
|
173190
|
-
${sections.join(`
|
|
173191
|
-
|
|
173192
|
-
`)}
|
|
173193
|
-
</key-files>`;
|
|
173194
|
-
}
|
|
173195
174374
|
function buildMagicContextBlock(opts) {
|
|
173196
174375
|
const sections = [];
|
|
173197
174376
|
const sessionId = opts.sessionId;
|
|
@@ -173219,13 +174398,23 @@ function buildMagicContextBlock(opts) {
|
|
|
173219
174398
|
sections.push(profileBlock);
|
|
173220
174399
|
}
|
|
173221
174400
|
if (opts.pinKeyFilesEnabled && sessionId) {
|
|
173222
|
-
|
|
173223
|
-
|
|
174401
|
+
let sessionMeta = null;
|
|
174402
|
+
try {
|
|
174403
|
+
sessionMeta = getOrCreateSessionMeta(opts.db, sessionId);
|
|
174404
|
+
} catch (error51) {
|
|
174405
|
+
sessionLog(sessionId, "Pi key-files session meta load failed:", error51);
|
|
174406
|
+
}
|
|
174407
|
+
const keyFilesBlock = sessionMeta ? readVersionedKeyFiles({
|
|
174408
|
+
db: opts.db,
|
|
173224
174409
|
sessionId,
|
|
174410
|
+
sessionMeta,
|
|
174411
|
+
directory: opts.cwd,
|
|
173225
174412
|
isCacheBusting,
|
|
173226
|
-
|
|
173227
|
-
|
|
173228
|
-
|
|
174413
|
+
config: {
|
|
174414
|
+
enabled: opts.pinKeyFilesEnabled,
|
|
174415
|
+
tokenBudget: opts.pinKeyFilesTokenBudget ?? 1e4
|
|
174416
|
+
}
|
|
174417
|
+
}) : null;
|
|
173229
174418
|
if (keyFilesBlock)
|
|
173230
174419
|
sections.push(keyFilesBlock);
|
|
173231
174420
|
}
|
|
@@ -173290,7 +174479,7 @@ function processSystemPromptForCache(args) {
|
|
|
173290
174479
|
sessionLog(sessionId, `system prompt date frozen: real=${liveDate}, using=${stickyDate} (cache-stable pass)`);
|
|
173291
174480
|
}
|
|
173292
174481
|
}
|
|
173293
|
-
const currentHash =
|
|
174482
|
+
const currentHash = createHash6("md5").update(frozenPrompt).digest("hex");
|
|
173294
174483
|
const hashChanged = !isFirstHash && currentHash !== previousHash;
|
|
173295
174484
|
if (hashChanged) {
|
|
173296
174485
|
sessionLog(sessionId, `system prompt hash changed: ${previousHash} → ${currentHash} (len=${frozenPrompt.length})`);
|
|
@@ -173318,7 +174507,7 @@ function clearPiSystemPromptSession(sessionId) {
|
|
|
173318
174507
|
stickyDateBySession.delete(sessionId);
|
|
173319
174508
|
cachedDocsBySession.delete(sessionId);
|
|
173320
174509
|
cachedUserProfileBySession.delete(sessionId);
|
|
173321
|
-
|
|
174510
|
+
clearKeyFilesCacheForSession(sessionId);
|
|
173322
174511
|
}
|
|
173323
174512
|
|
|
173324
174513
|
// ../plugin/src/tools/ctx-expand/constants.ts
|
|
@@ -178208,6 +179397,70 @@ function createCtxSearchTool(deps) {
|
|
|
178208
179397
|
};
|
|
178209
179398
|
}
|
|
178210
179399
|
|
|
179400
|
+
// src/tools/todowrite.ts
|
|
179401
|
+
var STATUS_VALUES = [
|
|
179402
|
+
"pending",
|
|
179403
|
+
"in_progress",
|
|
179404
|
+
"completed",
|
|
179405
|
+
"cancelled"
|
|
179406
|
+
];
|
|
179407
|
+
var PRIORITY_VALUES = ["high", "medium", "low"];
|
|
179408
|
+
var TodoItem = exports_typebox.Object({
|
|
179409
|
+
content: exports_typebox.String({ description: "Brief description of the task" }),
|
|
179410
|
+
status: exports_typebox.Union(STATUS_VALUES.map((v) => exports_typebox.Literal(v))),
|
|
179411
|
+
priority: exports_typebox.Optional(exports_typebox.Union(PRIORITY_VALUES.map((v) => exports_typebox.Literal(v)))),
|
|
179412
|
+
id: exports_typebox.Optional(exports_typebox.String({ description: "Optional stable id for the todo" }))
|
|
179413
|
+
});
|
|
179414
|
+
var TodowriteParams = exports_typebox.Object({
|
|
179415
|
+
todos: exports_typebox.Array(TodoItem, {
|
|
179416
|
+
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."
|
|
179417
|
+
})
|
|
179418
|
+
});
|
|
179419
|
+
var TOOL_DESCRIPTION = [
|
|
179420
|
+
"Manage your task list for this session.",
|
|
179421
|
+
"",
|
|
179422
|
+
"Use this tool to plan multi-step work, track in-flight tasks, and",
|
|
179423
|
+
"mark progress as you complete steps. Pass the COMPLETE updated list",
|
|
179424
|
+
"of todos every time — this tool replaces the prior list rather than",
|
|
179425
|
+
"appending to it.",
|
|
179426
|
+
"",
|
|
179427
|
+
"Task states:",
|
|
179428
|
+
" - pending: not started yet",
|
|
179429
|
+
" - in_progress: currently working on (limit to ONE task at a time)",
|
|
179430
|
+
" - completed: finished successfully",
|
|
179431
|
+
" - cancelled: no longer needed",
|
|
179432
|
+
"",
|
|
179433
|
+
"Use this tool proactively for non-trivial work spanning 3+ steps.",
|
|
179434
|
+
"Skip it for single-shot answers or trivial 1-2 step tasks."
|
|
179435
|
+
].join(`
|
|
179436
|
+
`);
|
|
179437
|
+
function createTodowriteTool() {
|
|
179438
|
+
return {
|
|
179439
|
+
name: "todowrite",
|
|
179440
|
+
label: "Todos",
|
|
179441
|
+
description: TOOL_DESCRIPTION,
|
|
179442
|
+
parameters: TodowriteParams,
|
|
179443
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
179444
|
+
const todos = params.todos ?? [];
|
|
179445
|
+
const completed = todos.filter((t) => t.status === "completed").length;
|
|
179446
|
+
const active = todos.length - completed;
|
|
179447
|
+
return {
|
|
179448
|
+
content: [
|
|
179449
|
+
{
|
|
179450
|
+
type: "text",
|
|
179451
|
+
text: JSON.stringify(todos, null, 2)
|
|
179452
|
+
}
|
|
179453
|
+
],
|
|
179454
|
+
details: {
|
|
179455
|
+
todos,
|
|
179456
|
+
title: `${active} todos`,
|
|
179457
|
+
truncated: false
|
|
179458
|
+
}
|
|
179459
|
+
};
|
|
179460
|
+
}
|
|
179461
|
+
};
|
|
179462
|
+
}
|
|
179463
|
+
|
|
178211
179464
|
// src/tools/index.ts
|
|
178212
179465
|
function registerMagicContextTools(pi, opts) {
|
|
178213
179466
|
pi.registerTool(createCtxSearchTool({
|
|
@@ -178227,6 +179480,7 @@ function registerMagicContextTools(pi, opts) {
|
|
|
178227
179480
|
dreamerEnabled: opts.dreamerEnabled ?? false
|
|
178228
179481
|
}));
|
|
178229
179482
|
pi.registerTool(createCtxExpandTool({ db: opts.db }));
|
|
179483
|
+
pi.registerTool(createTodowriteTool());
|
|
178230
179484
|
if (opts.ctxReduceEnabled === true) {
|
|
178231
179485
|
pi.registerTool(createCtxReduceTool({
|
|
178232
179486
|
db: opts.db,
|
|
@@ -178341,7 +179595,7 @@ function resolveDreamerFromConfig(config2) {
|
|
|
178341
179595
|
}
|
|
178342
179596
|
async function src_default2(pi) {
|
|
178343
179597
|
const storageDir = getMagicContextStorageDir();
|
|
178344
|
-
const dbPath =
|
|
179598
|
+
const dbPath = join18(storageDir, "context.db");
|
|
178345
179599
|
let db;
|
|
178346
179600
|
try {
|
|
178347
179601
|
db = openDatabase();
|
|
@@ -178551,9 +179805,17 @@ ${block}` : event.systemPrompt;
|
|
|
178551
179805
|
if (event.toolName === "todowrite") {
|
|
178552
179806
|
const todoArgs = event.args;
|
|
178553
179807
|
const todos = todoArgs?.todos;
|
|
179808
|
+
const sessionMeta = Array.isArray(todos) ? getOrCreateSessionMeta(db, sessionId) : null;
|
|
179809
|
+
if (sessionMeta && !sessionMeta.isSubagent) {
|
|
179810
|
+
const normalizedTodos = normalizeTodoStateJson(todos);
|
|
179811
|
+
if (normalizedTodos !== null) {
|
|
179812
|
+
updateSessionMeta(db, sessionId, {
|
|
179813
|
+
lastTodoState: normalizedTodos
|
|
179814
|
+
});
|
|
179815
|
+
}
|
|
179816
|
+
}
|
|
178554
179817
|
if (Array.isArray(todos) && todos.length > 0 && todos.every((t) => t.status === "completed" || t.status === "cancelled")) {
|
|
178555
|
-
|
|
178556
|
-
if (!sessionMeta.isSubagent) {
|
|
179818
|
+
if (sessionMeta && !sessionMeta.isSubagent) {
|
|
178557
179819
|
onNoteTrigger(db, sessionId, "todos_complete");
|
|
178558
179820
|
}
|
|
178559
179821
|
}
|
|
@@ -178607,14 +179869,62 @@ ${block}` : event.systemPrompt;
|
|
|
178607
179869
|
cacheTtlConfig: config2.cache_ttl
|
|
178608
179870
|
});
|
|
178609
179871
|
const piUsage = ctx.getContextUsage?.();
|
|
178610
|
-
const
|
|
178611
|
-
|
|
178612
|
-
|
|
179872
|
+
const piContextWindow = piUsage && typeof piUsage.contextWindow === "number" ? piUsage.contextWindow : 0;
|
|
179873
|
+
let effectiveContextLimit = piContextWindow;
|
|
179874
|
+
try {
|
|
179875
|
+
const overflowState = getOverflowState(db, sessionId);
|
|
179876
|
+
if (overflowState.detectedContextLimit > 0) {
|
|
179877
|
+
effectiveContextLimit = effectiveContextLimit > 0 ? Math.min(effectiveContextLimit, overflowState.detectedContextLimit) : overflowState.detectedContextLimit;
|
|
179878
|
+
}
|
|
179879
|
+
} catch (err5) {
|
|
179880
|
+
warn("message_end: getOverflowState failed:", err5);
|
|
178613
179881
|
}
|
|
178614
|
-
|
|
179882
|
+
const usage = extractAssistantUsage(event.message);
|
|
179883
|
+
const pressure = computePiPressure(usage, effectiveContextLimit);
|
|
179884
|
+
const updates = { lastResponseTime: Date.now() };
|
|
179885
|
+
if (pressure) {
|
|
179886
|
+
updates.lastContextPercentage = pressure.percentage;
|
|
179887
|
+
updates.lastInputTokens = pressure.inputTokens;
|
|
179888
|
+
} else if (piUsage && typeof piUsage.tokens === "number") {
|
|
178615
179889
|
updates.lastInputTokens = piUsage.tokens;
|
|
179890
|
+
if (piUsage.contextWindow > 0) {
|
|
179891
|
+
updates.lastContextPercentage = piUsage.tokens / piUsage.contextWindow * 100;
|
|
179892
|
+
}
|
|
178616
179893
|
}
|
|
178617
179894
|
updateSessionMeta(db, sessionId, updates);
|
|
179895
|
+
try {
|
|
179896
|
+
const sessionMetaForTodo = getOrCreateSessionMeta(db, sessionId);
|
|
179897
|
+
if (!sessionMetaForTodo.isSubagent) {
|
|
179898
|
+
const msg = event.message;
|
|
179899
|
+
if (msg && msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
179900
|
+
for (const block of msg.content) {
|
|
179901
|
+
if (!block || typeof block !== "object")
|
|
179902
|
+
continue;
|
|
179903
|
+
const b = block;
|
|
179904
|
+
if (b.type !== "toolCall")
|
|
179905
|
+
continue;
|
|
179906
|
+
if (typeof b.name !== "string")
|
|
179907
|
+
continue;
|
|
179908
|
+
if (!/^todo.*write$|^write.*todo$|^todowrite$/i.test(b.name)) {
|
|
179909
|
+
continue;
|
|
179910
|
+
}
|
|
179911
|
+
const args = b.arguments;
|
|
179912
|
+
const todos = args?.todos;
|
|
179913
|
+
if (!Array.isArray(todos))
|
|
179914
|
+
continue;
|
|
179915
|
+
const normalized = normalizeTodoStateJson(todos);
|
|
179916
|
+
if (normalized === null)
|
|
179917
|
+
continue;
|
|
179918
|
+
updateSessionMeta(db, sessionId, {
|
|
179919
|
+
lastTodoState: normalized
|
|
179920
|
+
});
|
|
179921
|
+
break;
|
|
179922
|
+
}
|
|
179923
|
+
}
|
|
179924
|
+
}
|
|
179925
|
+
} catch (err5) {
|
|
179926
|
+
warn("message_end: synthetic todowrite capture failed:", err5);
|
|
179927
|
+
}
|
|
178618
179928
|
} catch (err5) {
|
|
178619
179929
|
warn("message_end: persist session_meta usage failed:", err5);
|
|
178620
179930
|
}
|
|
@@ -178646,7 +179956,7 @@ ${block}` : event.systemPrompt;
|
|
|
178646
179956
|
try {
|
|
178647
179957
|
await Promise.race([
|
|
178648
179958
|
awaitInFlightHistorians(),
|
|
178649
|
-
new Promise((
|
|
179959
|
+
new Promise((resolve4) => setTimeout(resolve4, SHUTDOWN_DRAIN_MS))
|
|
178650
179960
|
]);
|
|
178651
179961
|
} catch (err5) {
|
|
178652
179962
|
warn("shutdown: historian drain threw:", err5);
|
|
@@ -178654,7 +179964,7 @@ ${block}` : event.systemPrompt;
|
|
|
178654
179964
|
try {
|
|
178655
179965
|
await Promise.race([
|
|
178656
179966
|
awaitInFlightDreamers(),
|
|
178657
|
-
new Promise((
|
|
179967
|
+
new Promise((resolve4) => setTimeout(resolve4, SHUTDOWN_DRAIN_MS))
|
|
178658
179968
|
]);
|
|
178659
179969
|
} catch (err5) {
|
|
178660
179970
|
warn("shutdown: dreamer drain threw:", err5);
|