@cortexkit/pi-magic-context 0.19.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 +1776 -756
- package/dist/subagent-entry.js +411 -80
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -16202,14 +16202,6 @@ function closeQuietly(db) {
|
|
|
16202
16202
|
}
|
|
16203
16203
|
|
|
16204
16204
|
// ../plugin/src/hooks/magic-context/read-session-db.ts
|
|
16205
|
-
var exports_read_session_db = {};
|
|
16206
|
-
__export(exports_read_session_db, {
|
|
16207
|
-
withReadOnlySessionDb: () => withReadOnlySessionDb,
|
|
16208
|
-
getRawSessionMessageCountFromDb: () => getRawSessionMessageCountFromDb,
|
|
16209
|
-
getMessageTimesFromOpenCodeDb: () => getMessageTimesFromOpenCodeDb,
|
|
16210
|
-
findLastAssistantModelFromOpenCodeDb: () => findLastAssistantModelFromOpenCodeDb,
|
|
16211
|
-
closeReadOnlySessionDb: () => closeReadOnlySessionDb
|
|
16212
|
-
});
|
|
16213
16205
|
import { join as join4 } from "node:path";
|
|
16214
16206
|
function getOpenCodeDbPath() {
|
|
16215
16207
|
return join4(getDataDir(), "opencode", "opencode.db");
|
|
@@ -16239,9 +16231,6 @@ function getReadOnlySessionDb() {
|
|
|
16239
16231
|
function withReadOnlySessionDb(fn) {
|
|
16240
16232
|
return fn(getReadOnlySessionDb());
|
|
16241
16233
|
}
|
|
16242
|
-
function closeReadOnlySessionDb() {
|
|
16243
|
-
closeCachedReadOnlyDb();
|
|
16244
|
-
}
|
|
16245
16234
|
function getRawSessionMessageCountFromDb(db, sessionId) {
|
|
16246
16235
|
const row = db.prepare(`SELECT COUNT(*) as count FROM message WHERE session_id = ?
|
|
16247
16236
|
AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
|
|
@@ -16267,34 +16256,6 @@ function getMessageTimesFromOpenCodeDb(sessionId, messageIds) {
|
|
|
16267
16256
|
}
|
|
16268
16257
|
return result;
|
|
16269
16258
|
}
|
|
16270
|
-
function findLastAssistantModelFromOpenCodeDb(sessionId) {
|
|
16271
|
-
try {
|
|
16272
|
-
return withReadOnlySessionDb((db) => {
|
|
16273
|
-
const row = db.prepare(`SELECT json_extract(data, '$.providerID') as providerID,
|
|
16274
|
-
json_extract(data, '$.modelID') as modelID,
|
|
16275
|
-
json_extract(data, '$.agent') as agent
|
|
16276
|
-
FROM message
|
|
16277
|
-
WHERE session_id = ?
|
|
16278
|
-
AND json_extract(data, '$.role') = 'assistant'
|
|
16279
|
-
AND json_extract(data, '$.providerID') IS NOT NULL
|
|
16280
|
-
AND json_extract(data, '$.modelID') IS NOT NULL
|
|
16281
|
-
ORDER BY time_created DESC
|
|
16282
|
-
LIMIT 1`).get(sessionId);
|
|
16283
|
-
if (!row || typeof row.providerID !== "string" || typeof row.modelID !== "string") {
|
|
16284
|
-
return null;
|
|
16285
|
-
}
|
|
16286
|
-
const agent = typeof row.agent === "string" && row.agent.length > 0 ? row.agent : undefined;
|
|
16287
|
-
return {
|
|
16288
|
-
providerID: row.providerID,
|
|
16289
|
-
modelID: row.modelID,
|
|
16290
|
-
...agent ? { agent } : {}
|
|
16291
|
-
};
|
|
16292
|
-
});
|
|
16293
|
-
} catch (error51) {
|
|
16294
|
-
log("[magic-context] failed to recover live model from OpenCode DB:", error51);
|
|
16295
|
-
return null;
|
|
16296
|
-
}
|
|
16297
|
-
}
|
|
16298
16259
|
var cachedReadOnlyDb = null;
|
|
16299
16260
|
var init_read_session_db = __esm(async () => {
|
|
16300
16261
|
init_data_path();
|
|
@@ -16302,34 +16263,6 @@ var init_read_session_db = __esm(async () => {
|
|
|
16302
16263
|
await init_sqlite();
|
|
16303
16264
|
});
|
|
16304
16265
|
|
|
16305
|
-
// ../plugin/src/shared/rpc-notifications.ts
|
|
16306
|
-
var exports_rpc_notifications = {};
|
|
16307
|
-
__export(exports_rpc_notifications, {
|
|
16308
|
-
pushNotification: () => pushNotification,
|
|
16309
|
-
isTuiConnected: () => isTuiConnected,
|
|
16310
|
-
drainNotifications: () => drainNotifications
|
|
16311
|
-
});
|
|
16312
|
-
function pushNotification(type, payload, sessionId) {
|
|
16313
|
-
queue2.push({ id: nextNotificationId++, type, payload, sessionId });
|
|
16314
|
-
if (queue2.length > 100) {
|
|
16315
|
-
queue2 = queue2.slice(-50);
|
|
16316
|
-
}
|
|
16317
|
-
}
|
|
16318
|
-
function drainNotifications(lastReceivedId = 0) {
|
|
16319
|
-
lastDrainAt = Date.now();
|
|
16320
|
-
if (lastReceivedId > 0) {
|
|
16321
|
-
queue2 = queue2.filter((notification) => notification.id > lastReceivedId);
|
|
16322
|
-
}
|
|
16323
|
-
return [...queue2];
|
|
16324
|
-
}
|
|
16325
|
-
function isTuiConnected() {
|
|
16326
|
-
return lastDrainAt > 0 && Date.now() - lastDrainAt < TUI_CONNECTED_WINDOW_MS;
|
|
16327
|
-
}
|
|
16328
|
-
var queue2, nextNotificationId = 1, lastDrainAt = 0, TUI_CONNECTED_WINDOW_MS = 3000;
|
|
16329
|
-
var init_rpc_notifications = __esm(() => {
|
|
16330
|
-
queue2 = [];
|
|
16331
|
-
});
|
|
16332
|
-
|
|
16333
16266
|
// ../../node_modules/.bun/esprima@4.0.1/node_modules/esprima/dist/esprima.js
|
|
16334
16267
|
var require_esprima = __commonJS((exports, module) => {
|
|
16335
16268
|
(function webpackUniversalModuleDefinition(root, factory) {
|
|
@@ -23937,10 +23870,10 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
23937
23870
|
replacer = null;
|
|
23938
23871
|
indent = EMPTY;
|
|
23939
23872
|
};
|
|
23940
|
-
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;
|
|
23941
23874
|
var join_content = (inside, value, gap) => {
|
|
23942
23875
|
const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
|
|
23943
|
-
return
|
|
23876
|
+
return join9(comment, inside, gap);
|
|
23944
23877
|
};
|
|
23945
23878
|
var stringify_string = (holder, key, value) => {
|
|
23946
23879
|
const raw = get_raw_string_literal(holder, key);
|
|
@@ -23962,13 +23895,13 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
23962
23895
|
if (i !== 0) {
|
|
23963
23896
|
inside += COMMA;
|
|
23964
23897
|
}
|
|
23965
|
-
const before =
|
|
23898
|
+
const before = join9(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
|
|
23966
23899
|
inside += before || LF + deeper_gap;
|
|
23967
23900
|
inside += stringify(i, value, deeper_gap) || STR_NULL;
|
|
23968
23901
|
inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
|
|
23969
23902
|
after_comma = process_comments(value, AFTER(i), deeper_gap);
|
|
23970
23903
|
}
|
|
23971
|
-
inside +=
|
|
23904
|
+
inside += join9(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
|
|
23972
23905
|
return BRACKET_OPEN + join_content(inside, value, gap) + BRACKET_CLOSE;
|
|
23973
23906
|
};
|
|
23974
23907
|
var object_stringify = (value, gap) => {
|
|
@@ -23989,13 +23922,13 @@ var require_stringify = __commonJS((exports, module) => {
|
|
|
23989
23922
|
inside += COMMA;
|
|
23990
23923
|
}
|
|
23991
23924
|
first = false;
|
|
23992
|
-
const before =
|
|
23925
|
+
const before = join9(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
|
|
23993
23926
|
inside += before || LF + deeper_gap;
|
|
23994
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);
|
|
23995
23928
|
after_comma = process_comments(value, AFTER(key), deeper_gap);
|
|
23996
23929
|
};
|
|
23997
23930
|
keys.forEach(iteratee);
|
|
23998
|
-
inside +=
|
|
23931
|
+
inside += join9(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
|
|
23999
23932
|
return CURLY_BRACKET_OPEN + join_content(inside, value, gap) + CURLY_BRACKET_CLOSE;
|
|
24000
23933
|
};
|
|
24001
23934
|
function stringify(key, holder, gap) {
|
|
@@ -24087,10 +24020,38 @@ var require_src2 = __commonJS((exports, module) => {
|
|
|
24087
24020
|
};
|
|
24088
24021
|
});
|
|
24089
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
|
+
|
|
24090
24051
|
// src/index.ts
|
|
24091
24052
|
init_embedding();
|
|
24092
24053
|
import { createRequire as createRequire2 } from "node:module";
|
|
24093
|
-
import { join as
|
|
24054
|
+
import { join as join18 } from "node:path";
|
|
24094
24055
|
|
|
24095
24056
|
// ../plugin/src/features/magic-context/memory/project-identity.ts
|
|
24096
24057
|
import { execSync } from "node:child_process";
|
|
@@ -157818,8 +157779,8 @@ function escapeXmlContent(s) {
|
|
|
157818
157779
|
}
|
|
157819
157780
|
// ../plugin/src/features/magic-context/storage-db.ts
|
|
157820
157781
|
init_data_path();
|
|
157821
|
-
import { copyFileSync, cpSync, existsSync as
|
|
157822
|
-
import { join as
|
|
157782
|
+
import { copyFileSync, cpSync, existsSync as existsSync4, mkdirSync as mkdirSync4 } from "node:fs";
|
|
157783
|
+
import { join as join7 } from "node:path";
|
|
157823
157784
|
|
|
157824
157785
|
// ../plugin/src/shared/error-message.ts
|
|
157825
157786
|
function getErrorMessage(error51) {
|
|
@@ -157901,6 +157862,119 @@ function safeString(value) {
|
|
|
157901
157862
|
init_logger();
|
|
157902
157863
|
await init_sqlite();
|
|
157903
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
|
+
|
|
157904
157978
|
// ../plugin/src/features/magic-context/migrations.ts
|
|
157905
157979
|
init_logger();
|
|
157906
157980
|
var MIGRATIONS = [
|
|
@@ -158201,6 +158275,46 @@ var MIGRATIONS = [
|
|
|
158201
158275
|
db.exec("ALTER TABLE session_meta ADD COLUMN pending_compaction_marker_state TEXT");
|
|
158202
158276
|
}
|
|
158203
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
|
+
}
|
|
158204
158318
|
}
|
|
158205
158319
|
];
|
|
158206
158320
|
function ensureMigrationsTable(db) {
|
|
@@ -158324,12 +158438,12 @@ function loadToolDefinitionMeasurements(db) {
|
|
|
158324
158438
|
// ../plugin/src/features/magic-context/tool-owner-backfill.ts
|
|
158325
158439
|
init_data_path();
|
|
158326
158440
|
init_logger();
|
|
158327
|
-
import { existsSync as
|
|
158328
|
-
import { join as
|
|
158441
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
158442
|
+
import { join as join6 } from "node:path";
|
|
158329
158443
|
var LEASE_DURATION_MS = 5 * 60 * 1000;
|
|
158330
158444
|
var LEASE_RENEWAL_MS = 60 * 1000;
|
|
158331
158445
|
function resolveOpencodeDbPath() {
|
|
158332
|
-
return
|
|
158446
|
+
return join6(getDataDir(), "opencode", "opencode.db");
|
|
158333
158447
|
}
|
|
158334
158448
|
function ensureBackfillStateTable(db) {
|
|
158335
158449
|
db.exec(`
|
|
@@ -158364,7 +158478,7 @@ function runToolOwnerBackfill(db) {
|
|
|
158364
158478
|
return result;
|
|
158365
158479
|
}
|
|
158366
158480
|
const opencodeDbPath = resolveOpencodeDbPath();
|
|
158367
|
-
if (!
|
|
158481
|
+
if (!existsSync3(opencodeDbPath)) {
|
|
158368
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.`);
|
|
158369
158483
|
markAllUnbackfilledSessionsSkipped(db);
|
|
158370
158484
|
result.sessionsSkippedNoOcDb = countSessionsByStatus(db, "skipped");
|
|
@@ -158572,21 +158686,21 @@ var persistenceByDatabase = new WeakMap;
|
|
|
158572
158686
|
var persistenceErrorByDatabase = new WeakMap;
|
|
158573
158687
|
function resolveDatabasePath() {
|
|
158574
158688
|
const dbDir = getMagicContextStorageDir();
|
|
158575
|
-
return { dbDir, dbPath:
|
|
158689
|
+
return { dbDir, dbPath: join7(dbDir, "context.db") };
|
|
158576
158690
|
}
|
|
158577
158691
|
function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
158578
|
-
if (
|
|
158692
|
+
if (existsSync4(targetDbPath))
|
|
158579
158693
|
return;
|
|
158580
158694
|
const legacyDir = getLegacyOpenCodeMagicContextStorageDir();
|
|
158581
|
-
const legacyDbPath =
|
|
158582
|
-
if (!
|
|
158695
|
+
const legacyDbPath = join7(legacyDir, "context.db");
|
|
158696
|
+
if (!existsSync4(legacyDbPath))
|
|
158583
158697
|
return;
|
|
158584
158698
|
log(`[magic-context] migrating legacy plugin storage: ${legacyDir} -> ${targetDbDir} (legacy left in place as backup)`);
|
|
158585
158699
|
mkdirSync4(targetDbDir, { recursive: true });
|
|
158586
158700
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
158587
158701
|
const src = `${legacyDbPath}${suffix}`;
|
|
158588
|
-
const dst =
|
|
158589
|
-
if (
|
|
158702
|
+
const dst = join7(targetDbDir, `context.db${suffix}`);
|
|
158703
|
+
if (existsSync4(src)) {
|
|
158590
158704
|
try {
|
|
158591
158705
|
copyFileSync(src, dst);
|
|
158592
158706
|
} catch (error51) {
|
|
@@ -158594,9 +158708,9 @@ function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
|
|
|
158594
158708
|
}
|
|
158595
158709
|
}
|
|
158596
158710
|
}
|
|
158597
|
-
const legacyModelsDir =
|
|
158598
|
-
const targetModelsDir =
|
|
158599
|
-
if (
|
|
158711
|
+
const legacyModelsDir = join7(legacyDir, "models");
|
|
158712
|
+
const targetModelsDir = join7(targetDbDir, "models");
|
|
158713
|
+
if (existsSync4(legacyModelsDir) && !existsSync4(targetModelsDir)) {
|
|
158600
158714
|
try {
|
|
158601
158715
|
cpSync(legacyModelsDir, targetModelsDir, { recursive: true });
|
|
158602
158716
|
} catch (error51) {
|
|
@@ -158744,6 +158858,26 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
158744
158858
|
);
|
|
158745
158859
|
CREATE INDEX IF NOT EXISTS idx_dream_runs_project ON dream_runs(project_path, finished_at DESC);
|
|
158746
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
|
+
|
|
158747
158881
|
-- (smart_notes: see note above; merged into unified notes table by migration v1)
|
|
158748
158882
|
|
|
158749
158883
|
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
@@ -158821,7 +158955,11 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
158821
158955
|
-- valid JSON blob written via setPendingCompactionMarkerState.
|
|
158822
158956
|
-- Excluded from healNullTextColumns. Readers filter IS NOT NULL AND
|
|
158823
158957
|
-- != empty-string defensively. Plan v6 section 3.
|
|
158824
|
-
pending_compaction_marker_state TEXT
|
|
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
|
|
158825
158963
|
);
|
|
158826
158964
|
|
|
158827
158965
|
CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
|
|
@@ -158910,6 +159048,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
158910
159048
|
ensureColumn(db, "session_meta", "detected_context_limit", "INTEGER DEFAULT 0");
|
|
158911
159049
|
ensureColumn(db, "session_meta", "needs_emergency_recovery", "INTEGER DEFAULT 0");
|
|
158912
159050
|
ensureColumn(db, "session_meta", "pending_compaction_marker_state", "TEXT");
|
|
159051
|
+
ensureColumn(db, "session_meta", "deferred_execute_state", "TEXT");
|
|
158913
159052
|
ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
158914
159053
|
ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
158915
159054
|
ensureColumn(db, "source_contents", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
@@ -158986,7 +159125,15 @@ function ensureColumn(db, table, column, definition) {
|
|
|
158986
159125
|
if (rows.some((row) => row.name === column)) {
|
|
158987
159126
|
return;
|
|
158988
159127
|
}
|
|
158989
|
-
|
|
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
|
+
}
|
|
158990
159137
|
}
|
|
158991
159138
|
function openDatabase() {
|
|
158992
159139
|
const { dbDir, dbPath } = resolveDatabasePath();
|
|
@@ -159003,6 +159150,11 @@ function openDatabase() {
|
|
|
159003
159150
|
const db = new Database(dbPath);
|
|
159004
159151
|
initializeDatabase(db);
|
|
159005
159152
|
runMigrations(db);
|
|
159153
|
+
try {
|
|
159154
|
+
deleteOrphanProjectKeyFiles(db);
|
|
159155
|
+
} catch (error51) {
|
|
159156
|
+
log(`[magic-context] key-files orphan GC failed: ${getErrorMessage(error51)}`);
|
|
159157
|
+
}
|
|
159006
159158
|
try {
|
|
159007
159159
|
runToolOwnerBackfill(db);
|
|
159008
159160
|
} catch (error51) {
|
|
@@ -159124,6 +159276,12 @@ function isPersistedNoteNudgeRow(row) {
|
|
|
159124
159276
|
const r = row;
|
|
159125
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";
|
|
159126
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
|
+
}
|
|
159127
159285
|
function isPersistedHistorianFailureRow(row) {
|
|
159128
159286
|
if (row === null || typeof row !== "object")
|
|
159129
159287
|
return false;
|
|
@@ -159200,6 +159358,29 @@ function setPersistedDeliveredNoteNudge(db, sessionId, text, messageId = "") {
|
|
|
159200
159358
|
function clearPersistedNoteNudge(db, sessionId) {
|
|
159201
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);
|
|
159202
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
|
+
}
|
|
159203
159384
|
function getNoteLastReadAt(db, sessionId) {
|
|
159204
159385
|
try {
|
|
159205
159386
|
const result = db.prepare("SELECT note_last_read_at FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
@@ -159326,6 +159507,30 @@ function clearPendingCompactionMarkerStateIf(db, sessionId, expected) {
|
|
|
159326
159507
|
WHERE session_id = ? AND pending_compaction_marker_state = ?`).run(sessionId, expectedBlob);
|
|
159327
159508
|
return result.changes > 0;
|
|
159328
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
|
+
}
|
|
159329
159534
|
// ../plugin/src/features/magic-context/resolve-subagent-fallback.ts
|
|
159330
159535
|
init_logger();
|
|
159331
159536
|
await init_read_session_db();
|
|
@@ -159920,10 +160125,10 @@ function getAgentFallbackModels(agent) {
|
|
|
159920
160125
|
|
|
159921
160126
|
// ../plugin/src/shared/models-dev-cache.ts
|
|
159922
160127
|
init_data_path();
|
|
159923
|
-
import { createHash as
|
|
159924
|
-
import { existsSync as
|
|
160128
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
160129
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "node:fs";
|
|
159925
160130
|
import { homedir as homedir2, platform } from "node:os";
|
|
159926
|
-
import { join as
|
|
160131
|
+
import { join as join8 } from "node:path";
|
|
159927
160132
|
|
|
159928
160133
|
// ../plugin/src/shared/jsonc-parser.ts
|
|
159929
160134
|
function stripJsonComments(content) {
|
|
@@ -160029,7 +160234,7 @@ var recentlySeenApiSizes = new Set;
|
|
|
160029
160234
|
var fileCache = null;
|
|
160030
160235
|
var fileLastAttempt = 0;
|
|
160031
160236
|
function hashFast(input) {
|
|
160032
|
-
return
|
|
160237
|
+
return createHash4("sha1").update(input).digest("hex");
|
|
160033
160238
|
}
|
|
160034
160239
|
function getModelsJsonPath() {
|
|
160035
160240
|
const explicit = process.env.OPENCODE_MODELS_PATH?.trim();
|
|
@@ -160038,16 +160243,16 @@ function getModelsJsonPath() {
|
|
|
160038
160243
|
const cacheBase = getCacheDir();
|
|
160039
160244
|
const source = process.env.OPENCODE_MODELS_URL?.trim();
|
|
160040
160245
|
const filename = source && source !== "https://models.dev" ? `models-${hashFast(source)}.json` : "models.json";
|
|
160041
|
-
return
|
|
160246
|
+
return join8(cacheBase, "opencode", filename);
|
|
160042
160247
|
}
|
|
160043
160248
|
function getOpencodeConfigPath() {
|
|
160044
160249
|
const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
160045
|
-
const configDir = envDir ? envDir : platform() === "win32" ?
|
|
160046
|
-
const jsonc =
|
|
160047
|
-
if (
|
|
160250
|
+
const configDir = envDir ? envDir : platform() === "win32" ? join8(homedir2(), ".config", "opencode") : join8(process.env.XDG_CONFIG_HOME || join8(homedir2(), ".config"), "opencode");
|
|
160251
|
+
const jsonc = join8(configDir, "opencode.jsonc");
|
|
160252
|
+
if (existsSync5(jsonc))
|
|
160048
160253
|
return jsonc;
|
|
160049
|
-
const json2 =
|
|
160050
|
-
if (
|
|
160254
|
+
const json2 = join8(configDir, "opencode.json");
|
|
160255
|
+
if (existsSync5(json2))
|
|
160051
160256
|
return json2;
|
|
160052
160257
|
return null;
|
|
160053
160258
|
}
|
|
@@ -160079,9 +160284,9 @@ function loadModelsDevMetadataFromFile() {
|
|
|
160079
160284
|
const modelsJsonPath = getModelsJsonPath();
|
|
160080
160285
|
let fileFound = false;
|
|
160081
160286
|
try {
|
|
160082
|
-
if (
|
|
160287
|
+
if (existsSync5(modelsJsonPath)) {
|
|
160083
160288
|
fileFound = true;
|
|
160084
|
-
const raw =
|
|
160289
|
+
const raw = readFileSync2(modelsJsonPath, "utf-8");
|
|
160085
160290
|
const data = JSON.parse(raw);
|
|
160086
160291
|
for (const [providerId, provider2] of Object.entries(data)) {
|
|
160087
160292
|
if (!provider2?.models || typeof provider2.models !== "object")
|
|
@@ -160096,8 +160301,8 @@ function loadModelsDevMetadataFromFile() {
|
|
|
160096
160301
|
}
|
|
160097
160302
|
try {
|
|
160098
160303
|
const configPath = getOpencodeConfigPath();
|
|
160099
|
-
if (configPath &&
|
|
160100
|
-
const config2 = parseJsonc(
|
|
160304
|
+
if (configPath && existsSync5(configPath)) {
|
|
160305
|
+
const config2 = parseJsonc(readFileSync2(configPath, "utf-8"));
|
|
160101
160306
|
if (config2.provider && typeof config2.provider === "object") {
|
|
160102
160307
|
for (const [providerId, provider2] of Object.entries(config2.provider)) {
|
|
160103
160308
|
if (!provider2?.models || typeof provider2.models !== "object")
|
|
@@ -160392,6 +160597,84 @@ function clearNoteNudgeState(db, sessionId, options) {
|
|
|
160392
160597
|
lastDeliveredAt.delete(sessionId);
|
|
160393
160598
|
}
|
|
160394
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
|
+
|
|
160395
160678
|
// src/index.ts
|
|
160396
160679
|
init_data_path();
|
|
160397
160680
|
init_logger();
|
|
@@ -160421,7 +160704,7 @@ init_logger();
|
|
|
160421
160704
|
|
|
160422
160705
|
// src/subagent-runner.ts
|
|
160423
160706
|
import * as childProcess from "node:child_process";
|
|
160424
|
-
import { existsSync as
|
|
160707
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
160425
160708
|
import { dirname as dirname3, resolve as resolvePath } from "node:path";
|
|
160426
160709
|
import { createInterface } from "node:readline";
|
|
160427
160710
|
import { fileURLToPath } from "node:url";
|
|
@@ -160429,7 +160712,7 @@ function resolveSubagentEntryPath() {
|
|
|
160429
160712
|
try {
|
|
160430
160713
|
const here = dirname3(fileURLToPath(import.meta.url));
|
|
160431
160714
|
const candidate = resolvePath(here, "subagent-entry.js");
|
|
160432
|
-
if (
|
|
160715
|
+
if (existsSync6(candidate))
|
|
160433
160716
|
return candidate;
|
|
160434
160717
|
return;
|
|
160435
160718
|
} catch {
|
|
@@ -160454,13 +160737,13 @@ class PiSubagentRunner {
|
|
|
160454
160737
|
async run(options) {
|
|
160455
160738
|
const startTime = Date.now();
|
|
160456
160739
|
const args = buildArgs(options);
|
|
160457
|
-
return new Promise((
|
|
160740
|
+
return new Promise((resolve2) => {
|
|
160458
160741
|
let settled = false;
|
|
160459
160742
|
const settle = (result) => {
|
|
160460
160743
|
if (settled)
|
|
160461
160744
|
return;
|
|
160462
160745
|
settled = true;
|
|
160463
|
-
|
|
160746
|
+
resolve2(result);
|
|
160464
160747
|
};
|
|
160465
160748
|
const emitProgress = (event) => {
|
|
160466
160749
|
if (!options.onProgress)
|
|
@@ -160928,6 +161211,16 @@ function isLeaseActive(db) {
|
|
|
160928
161211
|
function getLeaseHolder(db) {
|
|
160929
161212
|
return getDreamState(db, LEASE_HOLDER_KEY);
|
|
160930
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
|
+
}
|
|
160931
161224
|
function acquireLease(db, holderId) {
|
|
160932
161225
|
return db.transaction(() => {
|
|
160933
161226
|
if (isLeaseActive(db)) {
|
|
@@ -161040,8 +161333,8 @@ function clearStaleEntries(db, maxAgeMs) {
|
|
|
161040
161333
|
// src/commands/ctx-dream.ts
|
|
161041
161334
|
init_logger();
|
|
161042
161335
|
// ../plugin/src/features/magic-context/dreamer/runner.ts
|
|
161043
|
-
import { existsSync as
|
|
161044
|
-
import { join as
|
|
161336
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
161337
|
+
import { join as join11 } from "node:path";
|
|
161045
161338
|
|
|
161046
161339
|
// ../plugin/src/shared/index.ts
|
|
161047
161340
|
init_logger();
|
|
@@ -161335,187 +161628,519 @@ init_logger();
|
|
|
161335
161628
|
await init_sqlite();
|
|
161336
161629
|
|
|
161337
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";
|
|
161338
161633
|
init_logger();
|
|
161339
161634
|
|
|
161340
|
-
// ../plugin/src/features/magic-context/key-files/
|
|
161341
|
-
|
|
161342
|
-
|
|
161343
|
-
|
|
161344
|
-
|
|
161345
|
-
|
|
161346
|
-
|
|
161347
|
-
|
|
161348
|
-
|
|
161349
|
-
|
|
161350
|
-
|
|
161351
|
-
|
|
161352
|
-
|
|
161353
|
-
|
|
161354
|
-
|
|
161355
|
-
|
|
161356
|
-
|
|
161357
|
-
|
|
161358
|
-
|
|
161359
|
-
|
|
161360
|
-
|
|
161361
|
-
|
|
161362
|
-
|
|
161363
|
-
|
|
161364
|
-
|
|
161365
|
-
|
|
161366
|
-
|
|
161367
|
-
|
|
161368
|
-
|
|
161369
|
-
|
|
161370
|
-
|
|
161371
|
-
|
|
161372
|
-
|
|
161373
|
-
|
|
161374
|
-
|
|
161375
|
-
|
|
161376
|
-
|
|
161377
|
-
|
|
161378
|
-
|
|
161379
|
-
|
|
161380
|
-
|
|
161381
|
-
|
|
161382
|
-
|
|
161383
|
-
|
|
161384
|
-
|
|
161385
|
-
|
|
161386
|
-
|
|
161387
|
-
|
|
161388
|
-
|
|
161389
|
-
|
|
161390
|
-
|
|
161391
|
-
|
|
161392
|
-
|
|
161393
|
-
|
|
161394
|
-
|
|
161395
|
-
|
|
161396
|
-
|
|
161397
|
-
|
|
161398
|
-
|
|
161399
|
-
|
|
161400
|
-
|
|
161401
|
-
|
|
161402
|
-
|
|
161403
|
-
|
|
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;
|
|
161404
161706
|
}
|
|
161405
161707
|
|
|
161406
|
-
// ../plugin/src/features/magic-context/key-files/
|
|
161407
|
-
|
|
161408
|
-
|
|
161409
|
-
|
|
161410
|
-
|
|
161411
|
-
|
|
161412
|
-
|
|
161413
|
-
const
|
|
161414
|
-
if (
|
|
161415
|
-
return
|
|
161416
|
-
|
|
161417
|
-
|
|
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)
|
|
161418
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
|
+
}
|
|
161419
161742
|
}
|
|
161743
|
+
return merged.sort((a, b) => b.lastReadAt - a.lastReadAt || b.count - a.count).slice(0, 3);
|
|
161420
161744
|
}
|
|
161421
|
-
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;
|
|
161422
161749
|
try {
|
|
161423
|
-
|
|
161424
|
-
|
|
161425
|
-
}
|
|
161426
|
-
|
|
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;
|
|
161427
161765
|
}
|
|
161428
161766
|
}
|
|
161429
|
-
function
|
|
161430
|
-
const
|
|
161431
|
-
|
|
161432
|
-
|
|
161433
|
-
|
|
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)
|
|
161434
161786
|
continue;
|
|
161435
|
-
|
|
161787
|
+
const rel = normalizeProjectRelativePath(args.projectPath, row.file_path);
|
|
161788
|
+
if (!rel)
|
|
161436
161789
|
continue;
|
|
161437
|
-
|
|
161438
|
-
|
|
161439
|
-
|
|
161440
|
-
|
|
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);
|
|
161441
161844
|
}
|
|
161442
|
-
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);
|
|
161443
161849
|
}
|
|
161444
161850
|
|
|
161851
|
+
// ../plugin/src/features/magic-context/key-files/storage-key-files.ts
|
|
161852
|
+
init_logger();
|
|
161853
|
+
|
|
161445
161854
|
// ../plugin/src/features/magic-context/key-files/identify-key-files.ts
|
|
161446
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.";
|
|
161447
|
-
|
|
161448
|
-
|
|
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(`
|
|
161449
161883
|
`);
|
|
161450
|
-
return `## Identify
|
|
161884
|
+
return `## Task: Identify project key files
|
|
161451
161885
|
|
|
161452
|
-
|
|
161453
|
-
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.
|
|
161454
161887
|
|
|
161455
|
-
|
|
161456
|
-
- Read many times across different phases of work (not clustered in one task)
|
|
161457
|
-
- Read without editing — consulted for understanding, not modification
|
|
161458
|
-
- Contains architecture, configuration, types, or key abstractions
|
|
161888
|
+
## Read history (primary sessions only, project-scoped)
|
|
161459
161889
|
|
|
161460
|
-
|
|
161461
|
-
|
|
161462
|
-
|
|
161463
|
-
- Test files, scripts, or generated files
|
|
161890
|
+
| path | total reads | last read | common line ranges |
|
|
161891
|
+
| --- | ---: | --- | --- |
|
|
161892
|
+
${formatCandidates(args.candidates)}
|
|
161464
161893
|
|
|
161465
|
-
|
|
161894
|
+
## Current key files (if any)
|
|
161466
161895
|
|
|
161467
|
-
|
|
161468
|
-
|
|
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)
|
|
161469
161904
|
|
|
161470
|
-
### Output Format
|
|
161471
|
-
Return a JSON array ranked by importance (most important first):
|
|
161472
161905
|
\`\`\`json
|
|
161473
|
-
|
|
161474
|
-
|
|
161475
|
-
|
|
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
|
+
}
|
|
161476
161916
|
\`\`\`
|
|
161477
161917
|
|
|
161478
|
-
|
|
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 };
|
|
161479
161992
|
}
|
|
161480
|
-
function
|
|
161481
|
-
|
|
161482
|
-
|
|
161483
|
-
|
|
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;
|
|
161484
162006
|
try {
|
|
161485
|
-
|
|
161486
|
-
|
|
161487
|
-
|
|
161488
|
-
|
|
161489
|
-
|
|
161490
|
-
|
|
161491
|
-
|
|
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
|
+
}
|
|
161492
162024
|
}
|
|
161493
162025
|
}
|
|
161494
|
-
function
|
|
161495
|
-
const
|
|
161496
|
-
|
|
161497
|
-
|
|
161498
|
-
|
|
161499
|
-
}
|
|
161500
|
-
|
|
161501
|
-
|
|
161502
|
-
const
|
|
161503
|
-
|
|
161504
|
-
|
|
161505
|
-
|
|
161506
|
-
|
|
161507
|
-
|
|
161508
|
-
|
|
161509
|
-
|
|
161510
|
-
|
|
161511
|
-
|
|
161512
|
-
|
|
161513
|
-
|
|
161514
|
-
|
|
161515
|
-
|
|
161516
|
-
|
|
161517
|
-
|
|
161518
|
-
|
|
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
|
+
});
|
|
162069
|
+
}
|
|
162070
|
+
}
|
|
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
|
+
}
|
|
161519
162144
|
}
|
|
161520
162145
|
|
|
161521
162146
|
// ../plugin/src/features/magic-context/memory/embedding-cache.ts
|
|
@@ -162579,11 +163204,11 @@ function logWithStackHead(message, stackHead) {
|
|
|
162579
163204
|
log(message, stackHead ? { stackHead } : undefined);
|
|
162580
163205
|
}
|
|
162581
163206
|
function getOpenCodeDbPath2() {
|
|
162582
|
-
return
|
|
163207
|
+
return join11(getDataDir(), "opencode", "opencode.db");
|
|
162583
163208
|
}
|
|
162584
163209
|
function openOpenCodeDb() {
|
|
162585
163210
|
const dbPath = getOpenCodeDbPath2();
|
|
162586
|
-
if (!
|
|
163211
|
+
if (!existsSync8(dbPath)) {
|
|
162587
163212
|
log(`[key-files] OpenCode DB not found at ${dbPath} — skipping`);
|
|
162588
163213
|
return null;
|
|
162589
163214
|
}
|
|
@@ -162596,190 +163221,6 @@ function openOpenCodeDb() {
|
|
|
162596
163221
|
return null;
|
|
162597
163222
|
}
|
|
162598
163223
|
}
|
|
162599
|
-
function isSessionIdRow(row) {
|
|
162600
|
-
if (row === null || typeof row !== "object") {
|
|
162601
|
-
return false;
|
|
162602
|
-
}
|
|
162603
|
-
return typeof row.sessionId === "string";
|
|
162604
|
-
}
|
|
162605
|
-
function hasExplicitEmptyKeyFilesOutput(text) {
|
|
162606
|
-
return /```(?:json)?\s*\[\s*\]\s*```/s.test(text) || /^\s*\[\s*\]\s*$/s.test(text);
|
|
162607
|
-
}
|
|
162608
|
-
async function getActiveProjectSessionIds(args) {
|
|
162609
|
-
try {
|
|
162610
|
-
const { withReadOnlySessionDb: withReadOnlySessionDb2 } = await init_read_session_db().then(() => exports_read_session_db);
|
|
162611
|
-
const projectSessionIds = withReadOnlySessionDb2((openCodeDb) => {
|
|
162612
|
-
const bareIdentity = args.projectIdentity.replace(/^git:/, "");
|
|
162613
|
-
const rows = openCodeDb.prepare("SELECT id FROM session WHERE project_id = ? AND parent_id IS NULL ORDER BY time_updated DESC").all(bareIdentity);
|
|
162614
|
-
return new Set(rows.map((r) => r.id));
|
|
162615
|
-
});
|
|
162616
|
-
if (projectSessionIds.size === 0) {
|
|
162617
|
-
return [];
|
|
162618
|
-
}
|
|
162619
|
-
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));
|
|
162620
|
-
} catch (error51) {
|
|
162621
|
-
sessionLog(args.projectIdentity, `key-files: OpenCode DB lookup failed, falling back to SDK list: ${getErrorMessage(error51)}`);
|
|
162622
|
-
const listResponse = await args.client.session.list({
|
|
162623
|
-
query: { directory: args.sessionDirectory ?? args.projectIdentity }
|
|
162624
|
-
});
|
|
162625
|
-
const sessions = normalizeSDKResponse(listResponse, [], {
|
|
162626
|
-
preferResponseOnMissingData: true
|
|
162627
|
-
});
|
|
162628
|
-
const projectSessionIds = new Set(sessions.map((session) => typeof session?.id === "string" ? session.id : null).filter((sessionId) => Boolean(sessionId)));
|
|
162629
|
-
if (projectSessionIds.size === 0) {
|
|
162630
|
-
return [];
|
|
162631
|
-
}
|
|
162632
|
-
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));
|
|
162633
|
-
}
|
|
162634
|
-
}
|
|
162635
|
-
async function identifyKeyFilesForSession(args) {
|
|
162636
|
-
let openCodeDb = null;
|
|
162637
|
-
try {
|
|
162638
|
-
openCodeDb = openOpenCodeDb();
|
|
162639
|
-
if (!openCodeDb) {
|
|
162640
|
-
return;
|
|
162641
|
-
}
|
|
162642
|
-
const candidates = getKeyFileCandidates(openCodeDb, args.sessionId, args.config.min_reads, args.config.token_budget, args.sessionDirectory);
|
|
162643
|
-
if (candidates.length === 0) {
|
|
162644
|
-
log(`[key-files][${args.sessionId}] no candidates found — skipping`);
|
|
162645
|
-
return;
|
|
162646
|
-
}
|
|
162647
|
-
const prompt = buildKeyFilesPrompt(candidates, args.config.token_budget, args.config.min_reads);
|
|
162648
|
-
const applyHeuristicFallback = () => {
|
|
162649
|
-
heuristicKeyFileSelection(args.db, args.sessionId, candidates, args.config.token_budget);
|
|
162650
|
-
};
|
|
162651
|
-
let agentSessionId = null;
|
|
162652
|
-
const abortController = new AbortController;
|
|
162653
|
-
const leaseInterval = setInterval(() => {
|
|
162654
|
-
try {
|
|
162655
|
-
if (!renewLease(args.db, args.holderId)) {
|
|
162656
|
-
log(`[key-files][${args.sessionId}] lease renewal failed — aborting`);
|
|
162657
|
-
args.onLeaseLost?.(`key-files:${args.sessionId}`);
|
|
162658
|
-
abortController.abort();
|
|
162659
|
-
}
|
|
162660
|
-
} catch (error51) {
|
|
162661
|
-
args.onLeaseLost?.(`key-files:${args.sessionId}`, error51);
|
|
162662
|
-
abortController.abort();
|
|
162663
|
-
}
|
|
162664
|
-
}, 60000);
|
|
162665
|
-
try {
|
|
162666
|
-
const createResponse = await args.client.session.create({
|
|
162667
|
-
body: {
|
|
162668
|
-
...args.parentSessionId ? { parentID: args.parentSessionId } : {},
|
|
162669
|
-
title: `magic-context-dream-key-files-${args.sessionId.slice(0, 12)}`
|
|
162670
|
-
},
|
|
162671
|
-
query: { directory: args.sessionDirectory }
|
|
162672
|
-
});
|
|
162673
|
-
const created = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
|
|
162674
|
-
agentSessionId = typeof created?.id === "string" ? created.id : null;
|
|
162675
|
-
if (!agentSessionId) {
|
|
162676
|
-
throw new Error("Could not create key-file identification session.");
|
|
162677
|
-
}
|
|
162678
|
-
log(`[key-files][${args.sessionId}] child session created ${agentSessionId}`);
|
|
162679
|
-
const remainingMs = Math.max(0, args.deadline - Date.now());
|
|
162680
|
-
await promptSyncWithModelSuggestionRetry(args.client, {
|
|
162681
|
-
path: { id: agentSessionId },
|
|
162682
|
-
query: { directory: args.sessionDirectory },
|
|
162683
|
-
body: {
|
|
162684
|
-
agent: DREAMER_AGENT,
|
|
162685
|
-
system: KEY_FILES_SYSTEM_PROMPT,
|
|
162686
|
-
parts: [{ type: "text", text: prompt, synthetic: true }]
|
|
162687
|
-
}
|
|
162688
|
-
}, {
|
|
162689
|
-
timeoutMs: Math.min(remainingMs, 300000),
|
|
162690
|
-
signal: abortController.signal,
|
|
162691
|
-
fallbackModels: args.fallbackModels,
|
|
162692
|
-
callContext: `dreamer:key-files:${args.sessionId.slice(0, 12)}`
|
|
162693
|
-
});
|
|
162694
|
-
const messagesResponse = await args.client.session.messages({
|
|
162695
|
-
path: { id: agentSessionId },
|
|
162696
|
-
query: { directory: args.sessionDirectory }
|
|
162697
|
-
});
|
|
162698
|
-
const messages = normalizeSDKResponse(messagesResponse, [], {
|
|
162699
|
-
preferResponseOnMissingData: true
|
|
162700
|
-
});
|
|
162701
|
-
const responseText = extractLatestAssistantText(messages);
|
|
162702
|
-
if (!responseText) {
|
|
162703
|
-
log(`[key-files][${args.sessionId}] no response from agent — using heuristic fallback`);
|
|
162704
|
-
applyHeuristicFallback();
|
|
162705
|
-
return;
|
|
162706
|
-
}
|
|
162707
|
-
const parsed = parseKeyFilesOutput(responseText);
|
|
162708
|
-
if (parsed.length > 0 || hasExplicitEmptyKeyFilesOutput(responseText)) {
|
|
162709
|
-
const candidatePaths = new Set(candidates.map((c) => c.filePath));
|
|
162710
|
-
applyKeyFileResults(args.db, args.sessionId, parsed, args.config.token_budget, candidatePaths);
|
|
162711
|
-
return;
|
|
162712
|
-
}
|
|
162713
|
-
log(`[key-files][${args.sessionId}] could not parse agent output — using heuristic fallback`);
|
|
162714
|
-
applyHeuristicFallback();
|
|
162715
|
-
} catch (error51) {
|
|
162716
|
-
if (args.isLeaseLost?.() || abortController.signal.aborted) {
|
|
162717
|
-
log(`[key-files][${args.sessionId}] lease lost during identification — skipping heuristic fallback`);
|
|
162718
|
-
throw error51;
|
|
162719
|
-
}
|
|
162720
|
-
log(`[key-files][${args.sessionId}] identification failed: ${getErrorMessage(error51)} — using heuristic fallback`);
|
|
162721
|
-
try {
|
|
162722
|
-
applyHeuristicFallback();
|
|
162723
|
-
} catch (fallbackError) {
|
|
162724
|
-
log(`[key-files][${args.sessionId}] heuristic fallback failed: ${getErrorMessage(fallbackError)}`);
|
|
162725
|
-
}
|
|
162726
|
-
} finally {
|
|
162727
|
-
clearInterval(leaseInterval);
|
|
162728
|
-
if (agentSessionId) {
|
|
162729
|
-
await args.client.session.delete({
|
|
162730
|
-
path: { id: agentSessionId },
|
|
162731
|
-
query: { directory: args.sessionDirectory }
|
|
162732
|
-
}).catch((error51) => {
|
|
162733
|
-
log(`[key-files][${args.sessionId}] session cleanup failed: ${getErrorMessage(error51)}`);
|
|
162734
|
-
});
|
|
162735
|
-
}
|
|
162736
|
-
}
|
|
162737
|
-
} finally {
|
|
162738
|
-
if (openCodeDb) {
|
|
162739
|
-
try {
|
|
162740
|
-
closeQuietly(openCodeDb);
|
|
162741
|
-
} catch (error51) {
|
|
162742
|
-
log(`[key-files][${args.sessionId}] failed to close OpenCode DB: ${getErrorMessage(error51)}`);
|
|
162743
|
-
}
|
|
162744
|
-
}
|
|
162745
|
-
}
|
|
162746
|
-
}
|
|
162747
|
-
async function identifyKeyFiles(args) {
|
|
162748
|
-
const sessionIds = await getActiveProjectSessionIds({
|
|
162749
|
-
db: args.db,
|
|
162750
|
-
client: args.client,
|
|
162751
|
-
projectIdentity: args.projectIdentity,
|
|
162752
|
-
sessionDirectory: args.sessionDirectory
|
|
162753
|
-
});
|
|
162754
|
-
if (sessionIds.length === 0) {
|
|
162755
|
-
log(`[key-files] no active sessions found for ${args.projectIdentity}`);
|
|
162756
|
-
return;
|
|
162757
|
-
}
|
|
162758
|
-
log(`[key-files] evaluating ${sessionIds.length} active session(s) for ${args.projectIdentity}`);
|
|
162759
|
-
for (const sessionId of sessionIds) {
|
|
162760
|
-
if (args.isLeaseLost?.()) {
|
|
162761
|
-
log("[key-files] lease lost — stopping key-file identification");
|
|
162762
|
-
break;
|
|
162763
|
-
}
|
|
162764
|
-
if (Date.now() > args.deadline) {
|
|
162765
|
-
log("[key-files] deadline reached — stopping key-file identification");
|
|
162766
|
-
break;
|
|
162767
|
-
}
|
|
162768
|
-
await identifyKeyFilesForSession({
|
|
162769
|
-
db: args.db,
|
|
162770
|
-
client: args.client,
|
|
162771
|
-
parentSessionId: args.parentSessionId,
|
|
162772
|
-
sessionDirectory: args.sessionDirectory,
|
|
162773
|
-
holderId: args.holderId,
|
|
162774
|
-
fallbackModels: args.fallbackModels,
|
|
162775
|
-
onLeaseLost: args.onLeaseLost,
|
|
162776
|
-
isLeaseLost: args.isLeaseLost,
|
|
162777
|
-
deadline: args.deadline,
|
|
162778
|
-
sessionId,
|
|
162779
|
-
config: args.config
|
|
162780
|
-
});
|
|
162781
|
-
}
|
|
162782
|
-
}
|
|
162783
163224
|
async function runDream(args) {
|
|
162784
163225
|
const holderId = crypto.randomUUID();
|
|
162785
163226
|
const startedAt = Date.now();
|
|
@@ -162898,8 +163339,8 @@ async function runDream(args) {
|
|
|
162898
163339
|
try {
|
|
162899
163340
|
const docsDir = args.sessionDirectory ?? args.projectIdentity;
|
|
162900
163341
|
const existingDocs = taskName === "maintain-docs" ? {
|
|
162901
|
-
architecture:
|
|
162902
|
-
structure:
|
|
163342
|
+
architecture: existsSync8(join11(docsDir, "ARCHITECTURE.md")),
|
|
163343
|
+
structure: existsSync8(join11(docsDir, "STRUCTURE.md"))
|
|
162903
163344
|
} : undefined;
|
|
162904
163345
|
const userMemories = taskName === "archive-stale" ? getActiveUserMemories(args.db).map((um) => ({
|
|
162905
163346
|
id: um.id,
|
|
@@ -163000,8 +163441,7 @@ async function runDream(args) {
|
|
|
163000
163441
|
clearInterval(leaseRenewalInterval);
|
|
163001
163442
|
if (agentSessionId) {
|
|
163002
163443
|
await args.client.session.delete({
|
|
163003
|
-
path: { id: agentSessionId }
|
|
163004
|
-
query: { directory: args.sessionDirectory ?? args.projectIdentity }
|
|
163444
|
+
path: { id: agentSessionId }
|
|
163005
163445
|
}).catch((error51) => {
|
|
163006
163446
|
log("[dreamer] failed to delete child session:", error51);
|
|
163007
163447
|
});
|
|
@@ -163102,19 +163542,24 @@ async function runDream(args) {
|
|
|
163102
163542
|
if (!verifyLeaseStillHeld("before key-file identification")) {
|
|
163103
163543
|
throw new Error(lostLeaseReason ?? "Dream lease lost before key-file identification");
|
|
163104
163544
|
}
|
|
163105
|
-
|
|
163106
|
-
|
|
163107
|
-
|
|
163108
|
-
|
|
163109
|
-
|
|
163110
|
-
|
|
163111
|
-
|
|
163112
|
-
|
|
163113
|
-
|
|
163114
|
-
|
|
163115
|
-
|
|
163116
|
-
|
|
163117
|
-
|
|
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
|
+
}
|
|
163118
163563
|
if (!verifyLeaseStillHeld("after key-file identification")) {
|
|
163119
163564
|
throw new Error(lostLeaseReason ?? "Dream lease lost after key-file identification");
|
|
163120
163565
|
}
|
|
@@ -163332,8 +163777,7 @@ Only include notes whose conditions you could definitively evaluate. Skip notes
|
|
|
163332
163777
|
clearInterval(leaseInterval);
|
|
163333
163778
|
if (agentSessionId) {
|
|
163334
163779
|
await args.client.session.delete({
|
|
163335
|
-
path: { id: agentSessionId }
|
|
163336
|
-
query: { directory: args.sessionDirectory ?? args.projectIdentity }
|
|
163780
|
+
path: { id: agentSessionId }
|
|
163337
163781
|
}).catch(() => {});
|
|
163338
163782
|
}
|
|
163339
163783
|
}
|
|
@@ -164451,6 +164895,9 @@ function executeFlush(db, sessionId) {
|
|
|
164451
164895
|
}
|
|
164452
164896
|
}
|
|
164453
164897
|
|
|
164898
|
+
// src/context-handler.ts
|
|
164899
|
+
import * as crypto2 from "node:crypto";
|
|
164900
|
+
|
|
164454
164901
|
// ../plugin/src/features/magic-context/scheduler.ts
|
|
164455
164902
|
init_logger();
|
|
164456
164903
|
var TTL_PATTERN = /^(\d+)([smh])$/;
|
|
@@ -164848,6 +165295,30 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
|
|
|
164848
165295
|
return didMutateMessage;
|
|
164849
165296
|
}
|
|
164850
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
|
+
|
|
164851
165322
|
// ../plugin/src/hooks/magic-context/caveman.ts
|
|
164852
165323
|
var PRESERVATION_PATTERNS = [
|
|
164853
165324
|
/```[\s\S]*?```/g,
|
|
@@ -166619,10 +167090,10 @@ var DEFAULT_MIN_PROMPT_CHARS = 20;
|
|
|
166619
167090
|
async function unifiedSearchWithTimeout(db, sessionId, projectPath, prompt, options, timeoutMs) {
|
|
166620
167091
|
const controller = new AbortController;
|
|
166621
167092
|
let timer;
|
|
166622
|
-
const timeoutPromise = new Promise((
|
|
167093
|
+
const timeoutPromise = new Promise((resolve3) => {
|
|
166623
167094
|
timer = setTimeout(() => {
|
|
166624
167095
|
controller.abort();
|
|
166625
|
-
|
|
167096
|
+
resolve3(null);
|
|
166626
167097
|
}, timeoutMs);
|
|
166627
167098
|
});
|
|
166628
167099
|
try {
|
|
@@ -168272,7 +168743,7 @@ function getReducedRecompTokenBudget(currentBudget) {
|
|
|
168272
168743
|
// ../plugin/src/hooks/magic-context/historian-state-file.ts
|
|
168273
168744
|
init_data_path();
|
|
168274
168745
|
import { mkdirSync as mkdirSync5, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
168275
|
-
import { join as
|
|
168746
|
+
import { join as join12 } from "node:path";
|
|
168276
168747
|
var HISTORIAN_STATE_INLINE_THRESHOLD = 30000;
|
|
168277
168748
|
function maybeWriteHistorianStateFile(sessionId, existingState, directory) {
|
|
168278
168749
|
if (existingState.length <= HISTORIAN_STATE_INLINE_THRESHOLD)
|
|
@@ -168280,7 +168751,7 @@ function maybeWriteHistorianStateFile(sessionId, existingState, directory) {
|
|
|
168280
168751
|
try {
|
|
168281
168752
|
const dir = getProjectMagicContextHistorianDir(directory);
|
|
168282
168753
|
mkdirSync5(dir, { recursive: true });
|
|
168283
|
-
const path5 =
|
|
168754
|
+
const path5 = join12(dir, `state-${sessionId}-${Date.now()}.xml`);
|
|
168284
168755
|
writeFileSync2(path5, existingState, "utf8");
|
|
168285
168756
|
return path5;
|
|
168286
168757
|
} catch {
|
|
@@ -168297,6 +168768,238 @@ function cleanupHistorianStateFile(path5) {
|
|
|
168297
168768
|
|
|
168298
168769
|
// src/pi-historian-runner.ts
|
|
168299
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
|
|
168300
169003
|
var HISTORIAN_AGENT_NAME = "magic-context-historian";
|
|
168301
169004
|
var DEFAULT_HISTORIAN_TIMEOUT_MS2 = 120000;
|
|
168302
169005
|
var HISTORIAN_ALERT_COOLDOWN_MS = 60 * 1000;
|
|
@@ -168591,191 +169294,160 @@ function appendPiNativeCompaction(args) {
|
|
|
168591
169294
|
}
|
|
168592
169295
|
}
|
|
168593
169296
|
function findFirstKeptEntryId(entries, lastCompactedOrdinal) {
|
|
168594
|
-
|
|
168595
|
-
|
|
168596
|
-
|
|
168597
|
-
|
|
168598
|
-
|
|
168599
|
-
|
|
168600
|
-
continue;
|
|
168601
|
-
const role = e.message?.role;
|
|
168602
|
-
if (role !== "user" && role !== "assistant")
|
|
168603
|
-
continue;
|
|
168604
|
-
ordinal++;
|
|
168605
|
-
if (ordinal === lastCompactedOrdinal + 1) {
|
|
168606
|
-
return typeof e.id === "string" && e.id.length > 0 ? e.id : null;
|
|
168607
|
-
}
|
|
168608
|
-
}
|
|
168609
|
-
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;
|
|
168610
169303
|
}
|
|
168611
169304
|
|
|
168612
|
-
// src/
|
|
168613
|
-
function
|
|
168614
|
-
|
|
168615
|
-
|
|
168616
|
-
return [];
|
|
168617
|
-
const getBranch = sm.getBranch;
|
|
168618
|
-
if (typeof getBranch !== "function")
|
|
168619
|
-
return [];
|
|
168620
|
-
let entries;
|
|
168621
|
-
try {
|
|
168622
|
-
entries = getBranch.call(sm);
|
|
168623
|
-
} catch {
|
|
168624
|
-
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;
|
|
168625
169309
|
}
|
|
168626
|
-
if (
|
|
168627
|
-
return
|
|
168628
|
-
|
|
169310
|
+
if (typeof message.timestamp === "number") {
|
|
169311
|
+
return `pi-ts-${message.timestamp}`;
|
|
169312
|
+
}
|
|
169313
|
+
return "";
|
|
168629
169314
|
}
|
|
168630
|
-
function
|
|
168631
|
-
|
|
168632
|
-
|
|
168633
|
-
|
|
168634
|
-
|
|
168635
|
-
if (!isMessageEntry(entry)) {
|
|
168636
|
-
continue;
|
|
168637
|
-
}
|
|
168638
|
-
const msg = entry.message;
|
|
168639
|
-
const role = msg.role;
|
|
168640
|
-
if (role === "toolResult") {
|
|
168641
|
-
pendingToolParts.push(...synthesizeToolResultParts(msg));
|
|
168642
|
-
continue;
|
|
168643
|
-
}
|
|
168644
|
-
if (role === "user") {
|
|
168645
|
-
const parts = [
|
|
168646
|
-
...pendingToolParts,
|
|
168647
|
-
...synthesizeUserParts(msg)
|
|
168648
|
-
];
|
|
168649
|
-
pendingToolParts = [];
|
|
168650
|
-
result.push({
|
|
168651
|
-
ordinal: nextOrdinal++,
|
|
168652
|
-
id: entry.id,
|
|
168653
|
-
role: "user",
|
|
168654
|
-
parts
|
|
168655
|
-
});
|
|
168656
|
-
continue;
|
|
168657
|
-
}
|
|
168658
|
-
if (role === "assistant") {
|
|
168659
|
-
if (pendingToolParts.length > 0) {
|
|
168660
|
-
result.push({
|
|
168661
|
-
ordinal: nextOrdinal++,
|
|
168662
|
-
id: "",
|
|
168663
|
-
role: "user",
|
|
168664
|
-
parts: pendingToolParts
|
|
168665
|
-
});
|
|
168666
|
-
pendingToolParts = [];
|
|
168667
|
-
}
|
|
168668
|
-
result.push({
|
|
168669
|
-
ordinal: nextOrdinal++,
|
|
168670
|
-
id: entry.id,
|
|
168671
|
-
role: "assistant",
|
|
168672
|
-
parts: synthesizeAssistantParts(msg)
|
|
168673
|
-
});
|
|
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")
|
|
168674
169320
|
continue;
|
|
168675
|
-
|
|
168676
|
-
|
|
168677
|
-
|
|
168678
|
-
|
|
168679
|
-
role: typeof role === "string" ? role : "unknown",
|
|
168680
|
-
parts: []
|
|
168681
|
-
});
|
|
169321
|
+
const t = block.type;
|
|
169322
|
+
const id = block.id;
|
|
169323
|
+
if (t === "toolCall" && id === callId)
|
|
169324
|
+
return true;
|
|
168682
169325
|
}
|
|
168683
|
-
|
|
168684
|
-
|
|
168685
|
-
|
|
168686
|
-
|
|
168687
|
-
|
|
168688
|
-
|
|
168689
|
-
|
|
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;
|
|
168690
169339
|
}
|
|
168691
|
-
return
|
|
169340
|
+
return -1;
|
|
168692
169341
|
}
|
|
168693
|
-
function
|
|
168694
|
-
|
|
168695
|
-
|
|
168696
|
-
|
|
168697
|
-
|
|
168698
|
-
|
|
168699
|
-
|
|
168700
|
-
|
|
168701
|
-
|
|
168702
|
-
|
|
168703
|
-
|
|
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
|
+
};
|
|
168704
169360
|
}
|
|
168705
|
-
function
|
|
168706
|
-
|
|
168707
|
-
|
|
168708
|
-
if (m
|
|
168709
|
-
return [];
|
|
168710
|
-
return [{ type: "text", text: m.content }];
|
|
168711
|
-
}
|
|
168712
|
-
if (!Array.isArray(m.content))
|
|
168713
|
-
return [];
|
|
168714
|
-
const parts = [];
|
|
168715
|
-
for (const c of m.content) {
|
|
168716
|
-
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")
|
|
168717
169365
|
continue;
|
|
168718
|
-
|
|
168719
|
-
|
|
168720
|
-
|
|
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;
|
|
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);
|
|
168721
169383
|
}
|
|
169384
|
+
return true;
|
|
168722
169385
|
}
|
|
168723
|
-
return
|
|
169386
|
+
return false;
|
|
168724
169387
|
}
|
|
168725
|
-
function
|
|
168726
|
-
|
|
168727
|
-
|
|
168728
|
-
|
|
168729
|
-
const parts = [];
|
|
168730
|
-
for (const c of m.content) {
|
|
168731
|
-
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")
|
|
168732
169392
|
continue;
|
|
168733
|
-
|
|
168734
|
-
|
|
168735
|
-
|
|
168736
|
-
|
|
168737
|
-
|
|
168738
|
-
|
|
168739
|
-
|
|
168740
|
-
|
|
168741
|
-
|
|
168742
|
-
|
|
168743
|
-
|
|
168744
|
-
|
|
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);
|
|
168745
169411
|
}
|
|
169412
|
+
return id;
|
|
168746
169413
|
}
|
|
168747
|
-
return
|
|
169414
|
+
return null;
|
|
168748
169415
|
}
|
|
168749
|
-
function
|
|
168750
|
-
|
|
168751
|
-
|
|
168752
|
-
const
|
|
168753
|
-
if (
|
|
168754
|
-
|
|
168755
|
-
|
|
168756
|
-
|
|
168757
|
-
|
|
168758
|
-
for (const c of m.content) {
|
|
168759
|
-
if (c === null || typeof c !== "object")
|
|
168760
|
-
continue;
|
|
168761
|
-
const cc = c;
|
|
168762
|
-
if (cc.type === "text" && typeof cc.text === "string") {
|
|
168763
|
-
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);
|
|
168764
169425
|
}
|
|
169426
|
+
return args.messages;
|
|
168765
169427
|
}
|
|
168766
|
-
|
|
168767
|
-
|
|
168768
|
-
|
|
168769
|
-
return [
|
|
168770
|
-
{
|
|
168771
|
-
type: "tool",
|
|
168772
|
-
tool,
|
|
168773
|
-
callID,
|
|
168774
|
-
state: {
|
|
168775
|
-
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);
|
|
168776
169431
|
}
|
|
169432
|
+
return args.messages;
|
|
168777
169433
|
}
|
|
168778
|
-
|
|
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;
|
|
168779
169451
|
}
|
|
168780
169452
|
|
|
168781
169453
|
// src/reasoning-replay-pi.ts
|
|
@@ -169546,6 +170218,17 @@ var recentReduceBySession = new Map;
|
|
|
169546
170218
|
var liveModelBySession = new Map;
|
|
169547
170219
|
var toolUsageSinceUserTurn = new Map;
|
|
169548
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
|
+
}
|
|
169549
170232
|
function readPiSessionMessageById(ctx, messageId) {
|
|
169550
170233
|
return readPiSessionMessages(ctx).find((message) => message.id === messageId) ?? null;
|
|
169551
170234
|
}
|
|
@@ -169768,8 +170451,32 @@ function registerPiContextHandler(pi, options) {
|
|
|
169768
170451
|
const isFirstContextPassForSession = !firstContextPassSeenBySession.has(sessionId);
|
|
169769
170452
|
firstContextPassSeenBySession.add(sessionId);
|
|
169770
170453
|
const piUsage = ctx.getContextUsage?.();
|
|
169771
|
-
|
|
169772
|
-
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
|
+
}
|
|
169773
170480
|
let usageContextLimit = typeof piUsage?.contextWindow === "number" && piUsage.contextWindow > 0 ? piUsage.contextWindow : undefined;
|
|
169774
170481
|
try {
|
|
169775
170482
|
const overflowState = getOverflowState(options.db, sessionId);
|
|
@@ -169783,7 +170490,7 @@ function registerPiContextHandler(pi, options) {
|
|
|
169783
170490
|
} catch (err) {
|
|
169784
170491
|
sessionLog(sessionId, `pi transform: overflow state read failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
169785
170492
|
}
|
|
169786
|
-
const sessionMeta =
|
|
170493
|
+
const sessionMeta = sessionMetaForUsage;
|
|
169787
170494
|
const modelKey = liveModelBySession.get(sessionId);
|
|
169788
170495
|
let schedulerDecision;
|
|
169789
170496
|
try {
|
|
@@ -169798,6 +170505,29 @@ function registerPiContextHandler(pi, options) {
|
|
|
169798
170505
|
schedulerDecision = "execute";
|
|
169799
170506
|
sessionLog(sessionId, `pi transform: large imported session detected (${piMessageCount} messages, no usage baseline) — forcing execute on first pass`);
|
|
169800
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}`);
|
|
169801
170531
|
const forceMaterialization = usagePercentage >= FORCE_MATERIALIZATION_PERCENTAGE;
|
|
169802
170532
|
const isEmergency = usagePercentage >= EMERGENCY_BLOCK_PERCENTAGE;
|
|
169803
170533
|
if (isEmergency) {
|
|
@@ -169812,7 +170542,7 @@ function registerPiContextHandler(pi, options) {
|
|
|
169812
170542
|
try {
|
|
169813
170543
|
await Promise.race([
|
|
169814
170544
|
histPromise,
|
|
169815
|
-
new Promise((
|
|
170545
|
+
new Promise((resolve3) => setTimeout(resolve3, 30000))
|
|
169816
170546
|
]);
|
|
169817
170547
|
sessionLog(sessionId, "pi EMERGENCY: historian wait completed (or timed out)");
|
|
169818
170548
|
} catch {}
|
|
@@ -169904,6 +170634,22 @@ function registerPiContextHandler(pi, options) {
|
|
|
169904
170634
|
sessionLog(sessionId, `pi auto-search failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
169905
170635
|
}
|
|
169906
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
|
+
}
|
|
169907
170653
|
clearLastTransformErrorIfSet(options.db, sessionId);
|
|
169908
170654
|
return { messages: outputMessages };
|
|
169909
170655
|
} catch (err) {
|
|
@@ -170085,16 +170831,25 @@ function maybeFireHistorian(args) {
|
|
|
170085
170831
|
}
|
|
170086
170832
|
let usage;
|
|
170087
170833
|
try {
|
|
170088
|
-
const
|
|
170089
|
-
if (
|
|
170090
|
-
|
|
170091
|
-
|
|
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...`);
|
|
170092
170852
|
}
|
|
170093
|
-
usage = {
|
|
170094
|
-
percentage: piUsage.percent,
|
|
170095
|
-
inputTokens: piUsage.tokens
|
|
170096
|
-
};
|
|
170097
|
-
sessionLog(sessionId, `pi-historian trigger eval: usage=${usage.percentage.toFixed(1)}% (${usage.inputTokens} tokens), checking trigger...`);
|
|
170098
170853
|
} catch (err) {
|
|
170099
170854
|
sessionLog(sessionId, `pi-historian trigger eval: getContextUsage threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
170100
170855
|
return;
|
|
@@ -170106,6 +170861,11 @@ function maybeFireHistorian(args) {
|
|
|
170106
170861
|
let triggered = false;
|
|
170107
170862
|
try {
|
|
170108
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
|
+
}
|
|
170109
170869
|
const failureState = getHistorianFailureState(db, sessionId);
|
|
170110
170870
|
const shouldRecoverOnFirstPass = failureState.failureCount > 0 && hasEligiblePiCompartmentHistory(db, sessionId);
|
|
170111
170871
|
if (shouldRecoverOnFirstPass) {
|
|
@@ -170149,6 +170909,7 @@ Historian previously failed ${failureState.failureCount} time(s), so magic-conte
|
|
|
170149
170909
|
}
|
|
170150
170910
|
}
|
|
170151
170911
|
async function runPipeline(args) {
|
|
170912
|
+
let executedWorkThisPass = false;
|
|
170152
170913
|
if (args.temporalAwareness) {
|
|
170153
170914
|
try {
|
|
170154
170915
|
const injected = injectPiTemporalMarkers(args.messages);
|
|
@@ -170179,9 +170940,15 @@ async function runPipeline(args) {
|
|
|
170179
170940
|
}
|
|
170180
170941
|
const hasPendingMaterializeSignal = hasPendingMaterialization(args.sessionId);
|
|
170181
170942
|
if (args.schedulerDecision === "execute" || args.forceMaterialization || hasPendingMaterializeSignal) {
|
|
170182
|
-
|
|
170183
|
-
|
|
170184
|
-
|
|
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;
|
|
170185
170952
|
}
|
|
170186
170953
|
}
|
|
170187
170954
|
const flushedSliceTags = getTagsByNumbers(args.db, args.sessionId, [
|
|
@@ -170235,6 +171002,7 @@ async function runPipeline(args) {
|
|
|
170235
171002
|
caveman: args.heuristics.caveman
|
|
170236
171003
|
});
|
|
170237
171004
|
heuristicsExecuted = true;
|
|
171005
|
+
executedWorkThisPass = true;
|
|
170238
171006
|
} catch (err) {
|
|
170239
171007
|
sessionLog(args.sessionId, `pi heuristic cleanup failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
|
|
170240
171008
|
}
|
|
@@ -170257,6 +171025,7 @@ async function runPipeline(args) {
|
|
|
170257
171025
|
sessionLog(args.sessionId, `pi reasoning cleared on execute: cleared=${clearOutcome.cleared} watermark=${prevWatermark}->${clearOutcome.newWatermark}`);
|
|
170258
171026
|
}
|
|
170259
171027
|
}
|
|
171028
|
+
executedWorkThisPass = true;
|
|
170260
171029
|
} catch (err) {
|
|
170261
171030
|
sessionLog(args.sessionId, `pi reasoning clearing failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
|
|
170262
171031
|
}
|
|
@@ -170279,6 +171048,16 @@ async function runPipeline(args) {
|
|
|
170279
171048
|
messages: args.messages,
|
|
170280
171049
|
isCacheBusting: args.isCacheBusting
|
|
170281
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
|
+
}
|
|
170282
171061
|
const outputMessages = transcript.getOutputMessages();
|
|
170283
171062
|
try {
|
|
170284
171063
|
const counts = tokenizePiMessages(outputMessages);
|
|
@@ -170292,6 +171071,7 @@ async function runPipeline(args) {
|
|
|
170292
171071
|
return {
|
|
170293
171072
|
messages: outputMessages,
|
|
170294
171073
|
heuristicsExecuted,
|
|
171074
|
+
executedWorkThisPass,
|
|
170295
171075
|
historyInjected: injectionResult?.injected ?? false,
|
|
170296
171076
|
heuristicsResult,
|
|
170297
171077
|
injectionResult
|
|
@@ -170488,7 +171268,7 @@ init_logger();
|
|
|
170488
171268
|
init_data_path();
|
|
170489
171269
|
init_logger();
|
|
170490
171270
|
await init_sqlite();
|
|
170491
|
-
import { join as
|
|
171271
|
+
import { join as join13 } from "node:path";
|
|
170492
171272
|
var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
170493
171273
|
function randomBase62(length) {
|
|
170494
171274
|
const chars = [];
|
|
@@ -170509,7 +171289,7 @@ function generatePartId(timestampMs, counter = 0n) {
|
|
|
170509
171289
|
return generateId("prt", timestampMs, counter);
|
|
170510
171290
|
}
|
|
170511
171291
|
function getOpenCodeDbPath3() {
|
|
170512
|
-
return
|
|
171292
|
+
return join13(getDataDir(), "opencode", "opencode.db");
|
|
170513
171293
|
}
|
|
170514
171294
|
var cachedWriteDb = null;
|
|
170515
171295
|
var REQUIRED_MESSAGE_COLUMNS = ["id", "session_id", "time_created", "time_updated", "data"];
|
|
@@ -171059,7 +171839,7 @@ async function runCompressorPass2(args) {
|
|
|
171059
171839
|
return null;
|
|
171060
171840
|
} finally {
|
|
171061
171841
|
if (agentSessionId) {
|
|
171062
|
-
await client.session.delete({ path: { id: agentSessionId }
|
|
171842
|
+
await client.session.delete({ path: { id: agentSessionId } }).catch((e) => {
|
|
171063
171843
|
sessionLog(sessionId, "compressor: session cleanup failed:", getErrorMessage(e));
|
|
171064
171844
|
});
|
|
171065
171845
|
}
|
|
@@ -171068,7 +171848,7 @@ async function runCompressorPass2(args) {
|
|
|
171068
171848
|
|
|
171069
171849
|
// ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
|
|
171070
171850
|
import { mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
171071
|
-
import { join as
|
|
171851
|
+
import { join as join14 } from "node:path";
|
|
171072
171852
|
init_magic_context();
|
|
171073
171853
|
init_data_path();
|
|
171074
171854
|
function historianResponseDumpDir(directory) {
|
|
@@ -171236,7 +172016,7 @@ async function runHistorianPrompt(args) {
|
|
|
171236
172016
|
};
|
|
171237
172017
|
} finally {
|
|
171238
172018
|
if (agentSessionId) {
|
|
171239
|
-
await client.session.delete({ path: { id: agentSessionId }
|
|
172019
|
+
await client.session.delete({ path: { id: agentSessionId } }).catch((e) => {
|
|
171240
172020
|
sessionLog(parentSessionId, "compartment agent: session cleanup failed", getErrorMessage(e));
|
|
171241
172021
|
});
|
|
171242
172022
|
}
|
|
@@ -171301,8 +172081,8 @@ function isTransientHistorianPromptError(message) {
|
|
|
171301
172081
|
].some((token) => normalized.includes(token));
|
|
171302
172082
|
}
|
|
171303
172083
|
function sleep(ms) {
|
|
171304
|
-
return new Promise((
|
|
171305
|
-
setTimeout(
|
|
172084
|
+
return new Promise((resolve3) => {
|
|
172085
|
+
setTimeout(resolve3, ms);
|
|
171306
172086
|
});
|
|
171307
172087
|
}
|
|
171308
172088
|
function cleanupHistorianDump(sessionId, dumpPath) {
|
|
@@ -171323,7 +172103,7 @@ function dumpHistorianResponse(sessionId, directory, label, text) {
|
|
|
171323
172103
|
mkdirSync6(dumpDir, { recursive: true });
|
|
171324
172104
|
const safeSessionId = sanitizeDumpName(sessionId);
|
|
171325
172105
|
const safeLabel = sanitizeDumpName(label);
|
|
171326
|
-
const dumpPath =
|
|
172106
|
+
const dumpPath = join14(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
|
|
171327
172107
|
writeFileSync3(dumpPath, text, "utf8");
|
|
171328
172108
|
sessionLog(sessionId, "compartment agent: historian response dumped", {
|
|
171329
172109
|
label,
|
|
@@ -172525,11 +173305,11 @@ import {
|
|
|
172525
173305
|
matchesKey,
|
|
172526
173306
|
truncateToWidth,
|
|
172527
173307
|
visibleWidth
|
|
172528
|
-
} from "@
|
|
173308
|
+
} from "@earendil-works/pi-tui";
|
|
172529
173309
|
// package.json
|
|
172530
173310
|
var package_default = {
|
|
172531
173311
|
name: "@cortexkit/pi-magic-context",
|
|
172532
|
-
version: "0.
|
|
173312
|
+
version: "0.20.0",
|
|
172533
173313
|
type: "module",
|
|
172534
173314
|
description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
|
|
172535
173315
|
main: "dist/index.js",
|
|
@@ -172555,7 +173335,7 @@ var package_default = {
|
|
|
172555
173335
|
"README.md"
|
|
172556
173336
|
],
|
|
172557
173337
|
scripts: {
|
|
172558
|
-
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",
|
|
172559
173339
|
typecheck: "tsc --noEmit",
|
|
172560
173340
|
test: "bun test",
|
|
172561
173341
|
lint: "biome check src",
|
|
@@ -172575,15 +173355,15 @@ var package_default = {
|
|
|
172575
173355
|
},
|
|
172576
173356
|
devDependencies: {
|
|
172577
173357
|
"@biomejs/biome": "^2.4.7",
|
|
172578
|
-
"@
|
|
172579
|
-
"@
|
|
173358
|
+
"@earendil-works/pi-coding-agent": "^0.74.0",
|
|
173359
|
+
"@earendil-works/pi-tui": "^0.74.0",
|
|
172580
173360
|
"@types/better-sqlite3": "^7.6.13",
|
|
172581
173361
|
"@types/node": "^22.0.0",
|
|
172582
173362
|
typescript: "^5.8.0"
|
|
172583
173363
|
},
|
|
172584
173364
|
peerDependencies: {
|
|
172585
|
-
"@
|
|
172586
|
-
"@
|
|
173365
|
+
"@earendil-works/pi-coding-agent": "^0.74.0",
|
|
173366
|
+
"@earendil-works/pi-tui": "^0.74.0"
|
|
172587
173367
|
},
|
|
172588
173368
|
exports: {
|
|
172589
173369
|
".": {
|
|
@@ -173066,14 +173846,14 @@ function readHistorianState(db, sessionId, meta3) {
|
|
|
173066
173846
|
|
|
173067
173847
|
// src/config/index.ts
|
|
173068
173848
|
init_magic_context();
|
|
173069
|
-
import { existsSync as
|
|
173070
|
-
import { homedir as
|
|
173071
|
-
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";
|
|
173072
173852
|
|
|
173073
173853
|
// ../plugin/src/config/variable.ts
|
|
173074
|
-
import { existsSync as
|
|
173075
|
-
import { homedir as
|
|
173076
|
-
import { dirname as dirname4, isAbsolute, resolve } from "node:path";
|
|
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";
|
|
173077
173857
|
var ENV_PATTERN = /\{env:([^}]+)\}/g;
|
|
173078
173858
|
var FILE_PATTERN = /\{file:([^}]+)\}/g;
|
|
173079
173859
|
function substituteConfigVariables(input) {
|
|
@@ -173110,17 +173890,17 @@ function substituteConfigVariables(input) {
|
|
|
173110
173890
|
}
|
|
173111
173891
|
let filePath = rawPath.trim();
|
|
173112
173892
|
if (filePath.startsWith("~/")) {
|
|
173113
|
-
filePath =
|
|
173893
|
+
filePath = resolve3(homedir4(), filePath.slice(2));
|
|
173114
173894
|
} else if (!isAbsolute(filePath)) {
|
|
173115
|
-
filePath =
|
|
173895
|
+
filePath = resolve3(configDir, filePath);
|
|
173116
173896
|
}
|
|
173117
|
-
if (!
|
|
173897
|
+
if (!existsSync9(filePath)) {
|
|
173118
173898
|
warnings.push(`File not found for ${token} (resolved to ${filePath}); using empty string`);
|
|
173119
173899
|
continue;
|
|
173120
173900
|
}
|
|
173121
173901
|
let contents;
|
|
173122
173902
|
try {
|
|
173123
|
-
contents =
|
|
173903
|
+
contents = readFileSync5(filePath, "utf-8").trim();
|
|
173124
173904
|
} catch (error51) {
|
|
173125
173905
|
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
173126
173906
|
warnings.push(`Failed to read file for ${token} (${filePath}): ${message}; using empty string`);
|
|
@@ -173133,23 +173913,23 @@ function substituteConfigVariables(input) {
|
|
|
173133
173913
|
}
|
|
173134
173914
|
|
|
173135
173915
|
// src/config/index.ts
|
|
173136
|
-
var
|
|
173916
|
+
var import_comment_json2 = __toESM(require_src2(), 1);
|
|
173137
173917
|
var CONFIG_FILE_NAME = "magic-context";
|
|
173138
173918
|
function getProjectConfigPaths(cwd) {
|
|
173139
|
-
const basePath =
|
|
173919
|
+
const basePath = join15(cwd, ".pi", CONFIG_FILE_NAME);
|
|
173140
173920
|
return [`${basePath}.jsonc`, `${basePath}.json`];
|
|
173141
173921
|
}
|
|
173142
173922
|
function getUserConfigPaths() {
|
|
173143
|
-
const home = process.env.HOME ??
|
|
173144
|
-
const basePath =
|
|
173923
|
+
const home = process.env.HOME ?? homedir5();
|
|
173924
|
+
const basePath = join15(home, ".pi", "agent", CONFIG_FILE_NAME);
|
|
173145
173925
|
return [`${basePath}.jsonc`, `${basePath}.json`];
|
|
173146
173926
|
}
|
|
173147
173927
|
function resolveFirstExisting(paths) {
|
|
173148
|
-
return paths.find((path5) =>
|
|
173928
|
+
return paths.find((path5) => existsSync10(path5));
|
|
173149
173929
|
}
|
|
173150
173930
|
function loadConfigFile(path5, scope) {
|
|
173151
173931
|
try {
|
|
173152
|
-
const rawText =
|
|
173932
|
+
const rawText = readFileSync6(path5, "utf-8");
|
|
173153
173933
|
const substituted = substituteConfigVariables({
|
|
173154
173934
|
text: rawText,
|
|
173155
173935
|
configPath: path5
|
|
@@ -173157,7 +173937,7 @@ function loadConfigFile(path5, scope) {
|
|
|
173157
173937
|
return {
|
|
173158
173938
|
path: path5,
|
|
173159
173939
|
scope,
|
|
173160
|
-
config:
|
|
173940
|
+
config: import_comment_json2.parse(substituted.text),
|
|
173161
173941
|
warnings: substituted.warnings.map((warning) => `${path5}: ${warning}`)
|
|
173162
173942
|
};
|
|
173163
173943
|
} catch (error51) {
|
|
@@ -173271,6 +174051,42 @@ function loadPiConfig(opts = {}) {
|
|
|
173271
174051
|
};
|
|
173272
174052
|
}
|
|
173273
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
|
+
|
|
173274
174090
|
// src/strip-tag-prefix.ts
|
|
173275
174091
|
var TAG_PREFIX_REGEX2 = /^(\u00a7\d+\u00a7\s*)+/;
|
|
173276
174092
|
function stripTagPrefixFromAssistantMessage(message) {
|
|
@@ -173296,9 +174112,9 @@ function stripTagPrefixFromAssistantMessage(message) {
|
|
|
173296
174112
|
}
|
|
173297
174113
|
|
|
173298
174114
|
// src/system-prompt.ts
|
|
173299
|
-
import { createHash as
|
|
173300
|
-
import { existsSync as
|
|
173301
|
-
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";
|
|
173302
174118
|
|
|
173303
174119
|
// ../plugin/src/agents/magic-context-prompt.ts
|
|
173304
174120
|
function getToolHistoryGuidance(dropToolStructure) {
|
|
@@ -173387,23 +174203,146 @@ ${GENERIC_SECTION}
|
|
|
173387
174203
|
Prefer many small targeted operations over one large blanket operation. Compress early and often — don't wait for warnings.`;
|
|
173388
174204
|
}
|
|
173389
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
|
+
|
|
173390
174331
|
// src/system-prompt.ts
|
|
173391
174332
|
init_logger();
|
|
173392
174333
|
var PROJECT_DOCS_MARKER = "<project-docs>";
|
|
173393
174334
|
var USER_PROFILE_MARKER = "<user-profile>";
|
|
173394
|
-
var KEY_FILES_MARKER = "<key-files>";
|
|
173395
174335
|
var stickyDateBySession = new Map;
|
|
173396
174336
|
var cachedDocsBySession = new Map;
|
|
173397
174337
|
var cachedUserProfileBySession = new Map;
|
|
173398
|
-
var cachedKeyFilesBySession = new Map;
|
|
173399
174338
|
var DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
|
|
173400
174339
|
function readProjectDocs(directory) {
|
|
173401
174340
|
const sections = [];
|
|
173402
174341
|
for (const filename of DOC_FILES) {
|
|
173403
|
-
const filePath =
|
|
174342
|
+
const filePath = join17(directory, filename);
|
|
173404
174343
|
try {
|
|
173405
|
-
if (
|
|
173406
|
-
const content =
|
|
174344
|
+
if (existsSync11(filePath)) {
|
|
174345
|
+
const content = readFileSync8(filePath, "utf-8").trim();
|
|
173407
174346
|
if (content.length > 0) {
|
|
173408
174347
|
sections.push(`<${filename}>
|
|
173409
174348
|
${content}
|
|
@@ -173432,56 +174371,6 @@ function buildUserProfileBlock(db) {
|
|
|
173432
174371
|
${items}
|
|
173433
174372
|
</user-profile>`;
|
|
173434
174373
|
}
|
|
173435
|
-
function buildKeyFilesBlock(db, sessionId, directory, tokenBudget) {
|
|
173436
|
-
const entries = getKeyFiles(db, sessionId);
|
|
173437
|
-
if (entries.length === 0)
|
|
173438
|
-
return null;
|
|
173439
|
-
const sections = [];
|
|
173440
|
-
const projectRoot = resolve2(directory);
|
|
173441
|
-
let remainingBudgetTokens = tokenBudget;
|
|
173442
|
-
for (const entry of entries) {
|
|
173443
|
-
try {
|
|
173444
|
-
const absPath = resolve2(directory, entry.filePath);
|
|
173445
|
-
if (!absPath.startsWith(projectRoot + sep) && absPath !== projectRoot) {
|
|
173446
|
-
log(`[magic-context-pi] key file path escapes project root, skipping: ${entry.filePath}`);
|
|
173447
|
-
continue;
|
|
173448
|
-
}
|
|
173449
|
-
if (!existsSync9(absPath))
|
|
173450
|
-
continue;
|
|
173451
|
-
let realPath;
|
|
173452
|
-
try {
|
|
173453
|
-
realPath = realpathSync(absPath);
|
|
173454
|
-
} catch {
|
|
173455
|
-
continue;
|
|
173456
|
-
}
|
|
173457
|
-
if (!realPath.startsWith(projectRoot + sep) && realPath !== projectRoot) {
|
|
173458
|
-
log(`[magic-context-pi] key file symlink escapes project root, skipping: ${entry.filePath} → ${realPath}`);
|
|
173459
|
-
continue;
|
|
173460
|
-
}
|
|
173461
|
-
const content = readFileSync4(realPath, "utf-8").trim();
|
|
173462
|
-
if (content.length === 0)
|
|
173463
|
-
continue;
|
|
173464
|
-
const fileTokens = estimateTokens(content);
|
|
173465
|
-
if (fileTokens > remainingBudgetTokens) {
|
|
173466
|
-
log(`[magic-context-pi] key file ${entry.filePath} exceeds remaining budget (${fileTokens} > ${remainingBudgetTokens}), skipping`);
|
|
173467
|
-
continue;
|
|
173468
|
-
}
|
|
173469
|
-
remainingBudgetTokens -= fileTokens;
|
|
173470
|
-
sections.push(`<file path="${escapeXmlAttr(entry.filePath)}">
|
|
173471
|
-
${escapeXmlContent(content)}
|
|
173472
|
-
</file>`);
|
|
173473
|
-
} catch (error51) {
|
|
173474
|
-
log(`[magic-context-pi] failed to read key file ${entry.filePath}:`, error51);
|
|
173475
|
-
}
|
|
173476
|
-
}
|
|
173477
|
-
if (sections.length === 0)
|
|
173478
|
-
return null;
|
|
173479
|
-
return `${KEY_FILES_MARKER}
|
|
173480
|
-
${sections.join(`
|
|
173481
|
-
|
|
173482
|
-
`)}
|
|
173483
|
-
</key-files>`;
|
|
173484
|
-
}
|
|
173485
174374
|
function buildMagicContextBlock(opts) {
|
|
173486
174375
|
const sections = [];
|
|
173487
174376
|
const sessionId = opts.sessionId;
|
|
@@ -173509,13 +174398,23 @@ function buildMagicContextBlock(opts) {
|
|
|
173509
174398
|
sections.push(profileBlock);
|
|
173510
174399
|
}
|
|
173511
174400
|
if (opts.pinKeyFilesEnabled && sessionId) {
|
|
173512
|
-
|
|
173513
|
-
|
|
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,
|
|
173514
174409
|
sessionId,
|
|
174410
|
+
sessionMeta,
|
|
174411
|
+
directory: opts.cwd,
|
|
173515
174412
|
isCacheBusting,
|
|
173516
|
-
|
|
173517
|
-
|
|
173518
|
-
|
|
174413
|
+
config: {
|
|
174414
|
+
enabled: opts.pinKeyFilesEnabled,
|
|
174415
|
+
tokenBudget: opts.pinKeyFilesTokenBudget ?? 1e4
|
|
174416
|
+
}
|
|
174417
|
+
}) : null;
|
|
173519
174418
|
if (keyFilesBlock)
|
|
173520
174419
|
sections.push(keyFilesBlock);
|
|
173521
174420
|
}
|
|
@@ -173580,7 +174479,7 @@ function processSystemPromptForCache(args) {
|
|
|
173580
174479
|
sessionLog(sessionId, `system prompt date frozen: real=${liveDate}, using=${stickyDate} (cache-stable pass)`);
|
|
173581
174480
|
}
|
|
173582
174481
|
}
|
|
173583
|
-
const currentHash =
|
|
174482
|
+
const currentHash = createHash6("md5").update(frozenPrompt).digest("hex");
|
|
173584
174483
|
const hashChanged = !isFirstHash && currentHash !== previousHash;
|
|
173585
174484
|
if (hashChanged) {
|
|
173586
174485
|
sessionLog(sessionId, `system prompt hash changed: ${previousHash} → ${currentHash} (len=${frozenPrompt.length})`);
|
|
@@ -173608,7 +174507,7 @@ function clearPiSystemPromptSession(sessionId) {
|
|
|
173608
174507
|
stickyDateBySession.delete(sessionId);
|
|
173609
174508
|
cachedDocsBySession.delete(sessionId);
|
|
173610
174509
|
cachedUserProfileBySession.delete(sessionId);
|
|
173611
|
-
|
|
174510
|
+
clearKeyFilesCacheForSession(sessionId);
|
|
173612
174511
|
}
|
|
173613
174512
|
|
|
173614
174513
|
// ../plugin/src/tools/ctx-expand/constants.ts
|
|
@@ -178498,6 +179397,70 @@ function createCtxSearchTool(deps) {
|
|
|
178498
179397
|
};
|
|
178499
179398
|
}
|
|
178500
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
|
+
|
|
178501
179464
|
// src/tools/index.ts
|
|
178502
179465
|
function registerMagicContextTools(pi, opts) {
|
|
178503
179466
|
pi.registerTool(createCtxSearchTool({
|
|
@@ -178517,6 +179480,7 @@ function registerMagicContextTools(pi, opts) {
|
|
|
178517
179480
|
dreamerEnabled: opts.dreamerEnabled ?? false
|
|
178518
179481
|
}));
|
|
178519
179482
|
pi.registerTool(createCtxExpandTool({ db: opts.db }));
|
|
179483
|
+
pi.registerTool(createTodowriteTool());
|
|
178520
179484
|
if (opts.ctxReduceEnabled === true) {
|
|
178521
179485
|
pi.registerTool(createCtxReduceTool({
|
|
178522
179486
|
db: opts.db,
|
|
@@ -178631,7 +179595,7 @@ function resolveDreamerFromConfig(config2) {
|
|
|
178631
179595
|
}
|
|
178632
179596
|
async function src_default2(pi) {
|
|
178633
179597
|
const storageDir = getMagicContextStorageDir();
|
|
178634
|
-
const dbPath =
|
|
179598
|
+
const dbPath = join18(storageDir, "context.db");
|
|
178635
179599
|
let db;
|
|
178636
179600
|
try {
|
|
178637
179601
|
db = openDatabase();
|
|
@@ -178841,9 +179805,17 @@ ${block}` : event.systemPrompt;
|
|
|
178841
179805
|
if (event.toolName === "todowrite") {
|
|
178842
179806
|
const todoArgs = event.args;
|
|
178843
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
|
+
}
|
|
178844
179817
|
if (Array.isArray(todos) && todos.length > 0 && todos.every((t) => t.status === "completed" || t.status === "cancelled")) {
|
|
178845
|
-
|
|
178846
|
-
if (!sessionMeta.isSubagent) {
|
|
179818
|
+
if (sessionMeta && !sessionMeta.isSubagent) {
|
|
178847
179819
|
onNoteTrigger(db, sessionId, "todos_complete");
|
|
178848
179820
|
}
|
|
178849
179821
|
}
|
|
@@ -178897,14 +179869,62 @@ ${block}` : event.systemPrompt;
|
|
|
178897
179869
|
cacheTtlConfig: config2.cache_ttl
|
|
178898
179870
|
});
|
|
178899
179871
|
const piUsage = ctx.getContextUsage?.();
|
|
178900
|
-
const
|
|
178901
|
-
|
|
178902
|
-
|
|
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);
|
|
178903
179881
|
}
|
|
178904
|
-
|
|
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") {
|
|
178905
179889
|
updates.lastInputTokens = piUsage.tokens;
|
|
179890
|
+
if (piUsage.contextWindow > 0) {
|
|
179891
|
+
updates.lastContextPercentage = piUsage.tokens / piUsage.contextWindow * 100;
|
|
179892
|
+
}
|
|
178906
179893
|
}
|
|
178907
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
|
+
}
|
|
178908
179928
|
} catch (err5) {
|
|
178909
179929
|
warn("message_end: persist session_meta usage failed:", err5);
|
|
178910
179930
|
}
|
|
@@ -178936,7 +179956,7 @@ ${block}` : event.systemPrompt;
|
|
|
178936
179956
|
try {
|
|
178937
179957
|
await Promise.race([
|
|
178938
179958
|
awaitInFlightHistorians(),
|
|
178939
|
-
new Promise((
|
|
179959
|
+
new Promise((resolve4) => setTimeout(resolve4, SHUTDOWN_DRAIN_MS))
|
|
178940
179960
|
]);
|
|
178941
179961
|
} catch (err5) {
|
|
178942
179962
|
warn("shutdown: historian drain threw:", err5);
|
|
@@ -178944,7 +179964,7 @@ ${block}` : event.systemPrompt;
|
|
|
178944
179964
|
try {
|
|
178945
179965
|
await Promise.race([
|
|
178946
179966
|
awaitInFlightDreamers(),
|
|
178947
|
-
new Promise((
|
|
179967
|
+
new Promise((resolve4) => setTimeout(resolve4, SHUTDOWN_DRAIN_MS))
|
|
178948
179968
|
]);
|
|
178949
179969
|
} catch (err5) {
|
|
178950
179970
|
warn("shutdown: dreamer drain threw:", err5);
|