@cortexkit/pi-magic-context 0.31.2 → 0.31.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2068 -554
- package/dist/subagent-entry.js +1123 -191
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14990,12 +14990,41 @@ function pushNotification(type, payload, sessionId) {
|
|
|
14990
14990
|
queue = kept.sort((a, b) => a.id - b.id);
|
|
14991
14991
|
}
|
|
14992
14992
|
}
|
|
14993
|
-
function
|
|
14993
|
+
function cursor(value) {
|
|
14994
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
14995
|
+
}
|
|
14996
|
+
function drainNotifications(lastReceivedId = 0, sessionId, options = {}) {
|
|
14997
|
+
const sessionCursor = cursor(lastReceivedId);
|
|
14998
|
+
if (options.globalOnly) {
|
|
14999
|
+
queue = queue.filter((notification) => notification.sessionId !== undefined || notification.id > sessionCursor);
|
|
15000
|
+
return queue.filter((notification) => notification.sessionId === undefined && notification.id > sessionCursor);
|
|
15001
|
+
}
|
|
15002
|
+
if (options.sessionOnly) {
|
|
15003
|
+
if (sessionId === undefined)
|
|
15004
|
+
return [];
|
|
15005
|
+
queue = queue.filter((notification) => notification.sessionId !== sessionId || notification.id > sessionCursor);
|
|
15006
|
+
return queue.filter((notification) => notification.sessionId === sessionId && notification.id > sessionCursor);
|
|
15007
|
+
}
|
|
15008
|
+
if (sessionId !== undefined && options.globalLastReceivedId !== undefined) {
|
|
15009
|
+
const globalCursor = cursor(options.globalLastReceivedId);
|
|
15010
|
+
queue = queue.filter((notification) => {
|
|
15011
|
+
if (notification.sessionId === undefined)
|
|
15012
|
+
return notification.id > globalCursor;
|
|
15013
|
+
if (notification.sessionId === sessionId)
|
|
15014
|
+
return notification.id > sessionCursor;
|
|
15015
|
+
return true;
|
|
15016
|
+
});
|
|
15017
|
+
return queue.filter((notification) => {
|
|
15018
|
+
if (notification.sessionId === undefined)
|
|
15019
|
+
return notification.id > globalCursor;
|
|
15020
|
+
return notification.sessionId === sessionId && notification.id > sessionCursor;
|
|
15021
|
+
});
|
|
15022
|
+
}
|
|
14994
15023
|
const matchesClient = (notification) => sessionId === undefined || notification.sessionId === undefined || notification.sessionId === sessionId;
|
|
14995
|
-
if (
|
|
14996
|
-
queue = queue.filter((notification) => !(notification.id <=
|
|
15024
|
+
if (sessionCursor > 0) {
|
|
15025
|
+
queue = queue.filter((notification) => !(notification.id <= sessionCursor && matchesClient(notification)));
|
|
14997
15026
|
}
|
|
14998
|
-
return queue.filter((notification) => notification.id >
|
|
15027
|
+
return queue.filter((notification) => notification.id > sessionCursor && matchesClient(notification));
|
|
14999
15028
|
}
|
|
15000
15029
|
function isTuiConnected(sessionId) {
|
|
15001
15030
|
if (sinks.size === 0)
|
|
@@ -15138,7 +15167,7 @@ var PROMPT_CONTEXT_MESSAGE_LIMIT = 50;
|
|
|
15138
15167
|
|
|
15139
15168
|
// src/index.ts
|
|
15140
15169
|
import { createRequire as createRequire3 } from "node:module";
|
|
15141
|
-
import { join as join13 } from "node:path";
|
|
15170
|
+
import { join as join13, resolve as resolve3 } from "node:path";
|
|
15142
15171
|
|
|
15143
15172
|
// ../plugin/src/config/agent-disable.ts
|
|
15144
15173
|
function isDreamerRunnable(config) {
|
|
@@ -149215,12 +149244,19 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
149215
149244
|
let totalTokens = 0;
|
|
149216
149245
|
let messagesProcessed = 0;
|
|
149217
149246
|
let lastOrdinal = startOrdinal - 1;
|
|
149247
|
+
let highestScannedOrdinal = startOrdinal - 1;
|
|
149218
149248
|
let lastMessageId = "";
|
|
149219
149249
|
let firstMessageId = "";
|
|
149220
149250
|
let currentBlock = null;
|
|
149221
149251
|
let pendingNoiseMeta = [];
|
|
149222
149252
|
let commitClusters = 0;
|
|
149223
149253
|
let lastFlushedRole = "";
|
|
149254
|
+
function recordFilteredNoise(meta) {
|
|
149255
|
+
pendingNoiseMeta.push(meta);
|
|
149256
|
+
if (!currentBlock) {
|
|
149257
|
+
highestScannedOrdinal = Math.max(highestScannedOrdinal, meta.ordinal);
|
|
149258
|
+
}
|
|
149259
|
+
}
|
|
149224
149260
|
function flushCurrentBlock() {
|
|
149225
149261
|
if (!currentBlock)
|
|
149226
149262
|
return true;
|
|
@@ -149236,6 +149272,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
149236
149272
|
if (!firstMessageId)
|
|
149237
149273
|
firstMessageId = currentBlock.meta[0]?.messageId ?? "";
|
|
149238
149274
|
lastOrdinal = currentBlock.meta[currentBlock.meta.length - 1]?.ordinal ?? currentBlock.endOrdinal;
|
|
149275
|
+
highestScannedOrdinal = Math.max(highestScannedOrdinal, lastOrdinal);
|
|
149239
149276
|
lastMessageId = currentBlock.meta[currentBlock.meta.length - 1]?.messageId ?? "";
|
|
149240
149277
|
messagesProcessed += currentBlock.meta.length;
|
|
149241
149278
|
lines.push(blockText);
|
|
@@ -149259,7 +149296,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
149259
149296
|
if (msg.role === "user" && !hasMeaningfulUserText(msg.parts)) {
|
|
149260
149297
|
const tcSummaries = extractToolCallSummaries(msg.parts);
|
|
149261
149298
|
if (tcSummaries.length === 0) {
|
|
149262
|
-
|
|
149299
|
+
recordFilteredNoise(meta);
|
|
149263
149300
|
continue;
|
|
149264
149301
|
}
|
|
149265
149302
|
const tcText = tcSummaries.join(" / ");
|
|
@@ -149291,7 +149328,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
149291
149328
|
const compacted = compactTextForSummary(allParts.join(" / "), msg.role);
|
|
149292
149329
|
const text = compacted.text;
|
|
149293
149330
|
if (!text) {
|
|
149294
|
-
|
|
149331
|
+
recordFilteredNoise(meta);
|
|
149295
149332
|
continue;
|
|
149296
149333
|
}
|
|
149297
149334
|
const msgHasNarrative = textParts.length > 0;
|
|
@@ -149318,7 +149355,9 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
149318
149355
|
};
|
|
149319
149356
|
pendingNoiseMeta = [];
|
|
149320
149357
|
}
|
|
149321
|
-
flushCurrentBlock()
|
|
149358
|
+
if (flushCurrentBlock() && pendingNoiseMeta.length > 0) {
|
|
149359
|
+
highestScannedOrdinal = Math.max(highestScannedOrdinal, pendingNoiseMeta[pendingNoiseMeta.length - 1]?.ordinal ?? highestScannedOrdinal);
|
|
149360
|
+
}
|
|
149322
149361
|
const toolOnlyRanges = [];
|
|
149323
149362
|
for (const range of flushedToolOnlyBlocks) {
|
|
149324
149363
|
const last = toolOnlyRanges[toolOnlyRanges.length - 1];
|
|
@@ -149335,7 +149374,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
149335
149374
|
endMessageId: lastMessageId,
|
|
149336
149375
|
messageCount: messagesProcessed,
|
|
149337
149376
|
tokenEstimate: totalTokens,
|
|
149338
|
-
hasMore: lastOrdinal < (eligibleEndOrdinal !== undefined ? Math.min(eligibleEndOrdinal - 1, totalMessageCount) : totalMessageCount),
|
|
149377
|
+
hasMore: Math.max(lastOrdinal, highestScannedOrdinal) < (eligibleEndOrdinal !== undefined ? Math.min(eligibleEndOrdinal - 1, totalMessageCount) : totalMessageCount),
|
|
149339
149378
|
text: lines.join(`
|
|
149340
149379
|
`),
|
|
149341
149380
|
lines: lineMeta,
|
|
@@ -149370,7 +149409,10 @@ function clearCompressionDepthRange(db, sessionId, startOrdinal, endOrdinal) {
|
|
|
149370
149409
|
var lastIndexedStatements = new WeakMap;
|
|
149371
149410
|
var insertMessageStatements = new WeakMap;
|
|
149372
149411
|
var upsertIndexStatements = new WeakMap;
|
|
149412
|
+
var upsertCleanIndexStatements = new WeakMap;
|
|
149413
|
+
var upsertDirtyFloorStatements = new WeakMap;
|
|
149373
149414
|
var deleteFtsStatements = new WeakMap;
|
|
149415
|
+
var deleteFtsFromOrdinalStatements = new WeakMap;
|
|
149374
149416
|
var deleteIndexStatements = new WeakMap;
|
|
149375
149417
|
var countIndexedMessageStatements = new WeakMap;
|
|
149376
149418
|
function normalizeIndexText(text) {
|
|
@@ -149379,7 +149421,7 @@ function normalizeIndexText(text) {
|
|
|
149379
149421
|
function getLastIndexedStatement(db) {
|
|
149380
149422
|
let stmt = lastIndexedStatements.get(db);
|
|
149381
149423
|
if (!stmt) {
|
|
149382
|
-
stmt = db.prepare("SELECT last_indexed_ordinal FROM message_history_index WHERE session_id = ?");
|
|
149424
|
+
stmt = db.prepare("SELECT last_indexed_ordinal, dirty_floor_ordinal FROM message_history_index WHERE session_id = ?");
|
|
149383
149425
|
lastIndexedStatements.set(db, stmt);
|
|
149384
149426
|
}
|
|
149385
149427
|
return stmt;
|
|
@@ -149400,6 +149442,22 @@ function getUpsertIndexStatement(db) {
|
|
|
149400
149442
|
}
|
|
149401
149443
|
return stmt;
|
|
149402
149444
|
}
|
|
149445
|
+
function getUpsertCleanIndexStatement(db) {
|
|
149446
|
+
let stmt = upsertCleanIndexStatements.get(db);
|
|
149447
|
+
if (!stmt) {
|
|
149448
|
+
stmt = db.prepare("INSERT INTO message_history_index (session_id, last_indexed_ordinal, dirty_floor_ordinal, updated_at, harness) VALUES (?, ?, 0, ?, ?) ON CONFLICT(session_id) DO UPDATE SET last_indexed_ordinal = excluded.last_indexed_ordinal, dirty_floor_ordinal = 0, updated_at = excluded.updated_at");
|
|
149449
|
+
upsertCleanIndexStatements.set(db, stmt);
|
|
149450
|
+
}
|
|
149451
|
+
return stmt;
|
|
149452
|
+
}
|
|
149453
|
+
function getUpsertDirtyFloorStatement(db) {
|
|
149454
|
+
let stmt = upsertDirtyFloorStatements.get(db);
|
|
149455
|
+
if (!stmt) {
|
|
149456
|
+
stmt = db.prepare("INSERT INTO message_history_index (session_id, last_indexed_ordinal, dirty_floor_ordinal, updated_at, harness) VALUES (?, ?, ?, ?, ?) ON CONFLICT(session_id) DO UPDATE SET last_indexed_ordinal = MAX(message_history_index.last_indexed_ordinal, excluded.last_indexed_ordinal), dirty_floor_ordinal = CASE WHEN message_history_index.dirty_floor_ordinal <= 0 THEN excluded.dirty_floor_ordinal WHEN excluded.dirty_floor_ordinal <= 0 THEN message_history_index.dirty_floor_ordinal ELSE MIN(message_history_index.dirty_floor_ordinal, excluded.dirty_floor_ordinal) END, updated_at = excluded.updated_at");
|
|
149457
|
+
upsertDirtyFloorStatements.set(db, stmt);
|
|
149458
|
+
}
|
|
149459
|
+
return stmt;
|
|
149460
|
+
}
|
|
149403
149461
|
function getDeleteFtsStatement(db) {
|
|
149404
149462
|
let stmt = deleteFtsStatements.get(db);
|
|
149405
149463
|
if (!stmt) {
|
|
@@ -149408,6 +149466,14 @@ function getDeleteFtsStatement(db) {
|
|
|
149408
149466
|
}
|
|
149409
149467
|
return stmt;
|
|
149410
149468
|
}
|
|
149469
|
+
function getDeleteFtsFromOrdinalStatement(db) {
|
|
149470
|
+
let stmt = deleteFtsFromOrdinalStatements.get(db);
|
|
149471
|
+
if (!stmt) {
|
|
149472
|
+
stmt = db.prepare("DELETE FROM message_history_fts WHERE session_id = ? AND CAST(message_ordinal AS INTEGER) >= ?");
|
|
149473
|
+
deleteFtsFromOrdinalStatements.set(db, stmt);
|
|
149474
|
+
}
|
|
149475
|
+
return stmt;
|
|
149476
|
+
}
|
|
149411
149477
|
function getDeleteIndexStatement(db) {
|
|
149412
149478
|
let stmt = deleteIndexStatements.get(db);
|
|
149413
149479
|
if (!stmt) {
|
|
@@ -149428,6 +149494,14 @@ function getLastIndexedOrdinal(db, sessionId) {
|
|
|
149428
149494
|
const row = getLastIndexedStatement(db).get(sessionId);
|
|
149429
149495
|
return typeof row?.last_indexed_ordinal === "number" ? row.last_indexed_ordinal : 0;
|
|
149430
149496
|
}
|
|
149497
|
+
function getDirtyIndexFloor(db, sessionId) {
|
|
149498
|
+
const row = getLastIndexedStatement(db).get(sessionId);
|
|
149499
|
+
return typeof row?.dirty_floor_ordinal === "number" && row.dirty_floor_ordinal > 0 ? row.dirty_floor_ordinal : null;
|
|
149500
|
+
}
|
|
149501
|
+
function markMessageIndexDirty(db, sessionId, floorOrdinal) {
|
|
149502
|
+
const dirtyFloor = Math.max(1, Math.floor(floorOrdinal));
|
|
149503
|
+
getUpsertDirtyFloorStatement(db).run(sessionId, getLastIndexedOrdinal(db, sessionId), dirtyFloor, Date.now(), getHarness());
|
|
149504
|
+
}
|
|
149431
149505
|
function isMessageAlreadyIndexed(db, sessionId, messageId) {
|
|
149432
149506
|
const row = getCountIndexedMessageStatement(db).get(sessionId, messageId);
|
|
149433
149507
|
return (typeof row?.count === "number" ? row.count : 0) > 0;
|
|
@@ -149495,7 +149569,15 @@ function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal,
|
|
|
149495
149569
|
db.exec("BEGIN IMMEDIATE");
|
|
149496
149570
|
let committed = false;
|
|
149497
149571
|
try {
|
|
149498
|
-
|
|
149572
|
+
let effectiveWatermark = Math.max(lastIndexedOrdinal, getLastIndexedOrdinal(db, sessionId));
|
|
149573
|
+
const dirtyFloor = getDirtyIndexFloor(db, sessionId);
|
|
149574
|
+
if (dirtyFloor !== null) {
|
|
149575
|
+
const rewindOrdinal = Math.max(1, Math.min(dirtyFloor, finalWatermark + 1));
|
|
149576
|
+
if (rewindOrdinal <= finalWatermark) {
|
|
149577
|
+
getDeleteFtsFromOrdinalStatement(db).run(sessionId, rewindOrdinal);
|
|
149578
|
+
}
|
|
149579
|
+
effectiveWatermark = Math.min(effectiveWatermark, rewindOrdinal - 1);
|
|
149580
|
+
}
|
|
149499
149581
|
const insertMessage = getInsertMessageStatement(db);
|
|
149500
149582
|
for (const message of messages) {
|
|
149501
149583
|
if (message.ordinal <= effectiveWatermark) {
|
|
@@ -149512,7 +149594,7 @@ function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal,
|
|
|
149512
149594
|
inserted++;
|
|
149513
149595
|
}
|
|
149514
149596
|
const newWatermark = Math.max(effectiveWatermark, finalWatermark);
|
|
149515
|
-
|
|
149597
|
+
getUpsertCleanIndexStatement(db).run(sessionId, newWatermark, now, getHarness());
|
|
149516
149598
|
db.exec("COMMIT");
|
|
149517
149599
|
committed = true;
|
|
149518
149600
|
} finally {
|
|
@@ -149609,8 +149691,9 @@ function scheduleReconciliation(db, sessionId, readMessages) {
|
|
|
149609
149691
|
reconciliationScheduledSessions.add(sessionId);
|
|
149610
149692
|
defer(() => {
|
|
149611
149693
|
reconcileSessionIndex(db, sessionId, readMessages).catch((error) => {
|
|
149612
|
-
reconciliationScheduledSessions.delete(sessionId);
|
|
149613
149694
|
logIndexingError(sessionId, "reconciliation", error);
|
|
149695
|
+
}).finally(() => {
|
|
149696
|
+
reconciliationScheduledSessions.delete(sessionId);
|
|
149614
149697
|
});
|
|
149615
149698
|
});
|
|
149616
149699
|
}
|
|
@@ -149622,13 +149705,17 @@ function scheduleIncrementalIndex(db, sessionId, messageId, readSingleMessage) {
|
|
|
149622
149705
|
const timer = setTimeout(() => {
|
|
149623
149706
|
incrementalTimers.delete(key);
|
|
149624
149707
|
pendingIncrementalKeys.add(key);
|
|
149708
|
+
let attemptedOrdinal = null;
|
|
149625
149709
|
runWithSessionLock(sessionId, () => {
|
|
149626
149710
|
const message = readSingleMessage(sessionId, messageId);
|
|
149627
149711
|
if (!message) {
|
|
149628
149712
|
return;
|
|
149629
149713
|
}
|
|
149714
|
+
attemptedOrdinal = message.ordinal;
|
|
149630
149715
|
indexSingleMessage(db, sessionId, message);
|
|
149631
149716
|
}).catch((error) => {
|
|
149717
|
+
markMessageIndexDirty(db, sessionId, attemptedOrdinal ?? getLastIndexedOrdinal(db, sessionId) + 1);
|
|
149718
|
+
reconciledSessions.delete(sessionId);
|
|
149632
149719
|
logIndexingError(sessionId, `incremental index for ${messageId}`, error);
|
|
149633
149720
|
}).finally(() => {
|
|
149634
149721
|
pendingIncrementalKeys.delete(key);
|
|
@@ -150580,6 +150667,7 @@ function healNullTextColumns(db) {
|
|
|
150580
150667
|
["cache_ttl", ""],
|
|
150581
150668
|
["last_nudge_band", ""],
|
|
150582
150669
|
["last_nudge_level", ""],
|
|
150670
|
+
["channel2_nudge_claim_token", ""],
|
|
150583
150671
|
["last_transform_error", ""],
|
|
150584
150672
|
["nudge_anchor_message_id", ""],
|
|
150585
150673
|
["nudge_anchor_text", ""],
|
|
@@ -150643,6 +150731,7 @@ function healNullIntegerColumns(db) {
|
|
|
150643
150731
|
}
|
|
150644
150732
|
|
|
150645
150733
|
// ../plugin/src/features/magic-context/workspaces.ts
|
|
150734
|
+
init_logger();
|
|
150646
150735
|
import { createHash as createHash2 } from "node:crypto";
|
|
150647
150736
|
|
|
150648
150737
|
// ../plugin/src/features/magic-context/memory/constants.ts
|
|
@@ -150693,6 +150782,7 @@ var CATEGORY_DEFAULT_TTL = {
|
|
|
150693
150782
|
};
|
|
150694
150783
|
// ../plugin/src/features/magic-context/workspaces.ts
|
|
150695
150784
|
var VALID_SHARE_CATEGORIES = new Set(V2_MEMORY_CATEGORIES);
|
|
150785
|
+
var DEFAULT_WORKSPACE_SHARE_CATEGORIES = ["CONSTRAINTS"];
|
|
150696
150786
|
function tableExists(db, tableName) {
|
|
150697
150787
|
const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name = ? LIMIT 1").get(tableName);
|
|
150698
150788
|
return Boolean(row);
|
|
@@ -150707,23 +150797,39 @@ function uniqueSorted(values) {
|
|
|
150707
150797
|
function placeholders(values) {
|
|
150708
150798
|
return values.map(() => "?").join(", ");
|
|
150709
150799
|
}
|
|
150800
|
+
function defaultWorkspaceShareCategories() {
|
|
150801
|
+
return [...DEFAULT_WORKSPACE_SHARE_CATEGORIES];
|
|
150802
|
+
}
|
|
150803
|
+
function warnInvalidShareCategories(reason, raw) {
|
|
150804
|
+
log("[magic-context] WARN: invalid workspace share_categories; sharing no foreign memory categories", {
|
|
150805
|
+
reason,
|
|
150806
|
+
raw
|
|
150807
|
+
});
|
|
150808
|
+
}
|
|
150710
150809
|
function normalizeShareCategories(raw) {
|
|
150711
|
-
if (raw === null || raw === undefined)
|
|
150712
|
-
return
|
|
150713
|
-
|
|
150714
|
-
|
|
150810
|
+
if (raw === null || raw === undefined) {
|
|
150811
|
+
return defaultWorkspaceShareCategories();
|
|
150812
|
+
}
|
|
150813
|
+
if (typeof raw !== "string") {
|
|
150814
|
+
warnInvalidShareCategories("not a string", raw);
|
|
150815
|
+
return [];
|
|
150816
|
+
}
|
|
150715
150817
|
let parsed;
|
|
150716
150818
|
try {
|
|
150717
150819
|
parsed = JSON.parse(raw);
|
|
150718
150820
|
} catch {
|
|
150719
|
-
|
|
150821
|
+
warnInvalidShareCategories("malformed JSON", raw);
|
|
150822
|
+
return [];
|
|
150823
|
+
}
|
|
150824
|
+
if (!Array.isArray(parsed)) {
|
|
150825
|
+
warnInvalidShareCategories("not a JSON array", raw);
|
|
150826
|
+
return [];
|
|
150720
150827
|
}
|
|
150721
|
-
if (!Array.isArray(parsed))
|
|
150722
|
-
return null;
|
|
150723
150828
|
const categories = [];
|
|
150724
150829
|
for (const value of parsed) {
|
|
150725
150830
|
if (typeof value !== "string" || !VALID_SHARE_CATEGORIES.has(value)) {
|
|
150726
|
-
|
|
150831
|
+
warnInvalidShareCategories("unknown category", raw);
|
|
150832
|
+
return [];
|
|
150727
150833
|
}
|
|
150728
150834
|
if (!categories.includes(value))
|
|
150729
150835
|
categories.push(value);
|
|
@@ -150732,8 +150838,21 @@ function normalizeShareCategories(raw) {
|
|
|
150732
150838
|
}
|
|
150733
150839
|
function selectWorkspaceShareCategories(db, identities) {
|
|
150734
150840
|
const candidates = uniqueSorted(identities.filter((identity) => identity.length > 0));
|
|
150735
|
-
if (candidates.length === 0 || !tableExists(db, "workspace_members")
|
|
150841
|
+
if (candidates.length === 0 || !tableExists(db, "workspace_members")) {
|
|
150842
|
+
return null;
|
|
150843
|
+
}
|
|
150844
|
+
const hasMembership = Boolean(db.prepare(`SELECT 1
|
|
150845
|
+
FROM workspace_members
|
|
150846
|
+
WHERE project_path IN (${placeholders(candidates)})
|
|
150847
|
+
LIMIT 1`).get(...candidates));
|
|
150848
|
+
if (!hasMembership)
|
|
150736
150849
|
return null;
|
|
150850
|
+
if (!tableExists(db, "workspaces")) {
|
|
150851
|
+
log("[magic-context] WARN: workspace member has no workspaces table; sharing no foreign memory categories");
|
|
150852
|
+
return [];
|
|
150853
|
+
}
|
|
150854
|
+
if (!columnExists(db, "workspaces", "share_categories")) {
|
|
150855
|
+
return defaultWorkspaceShareCategories();
|
|
150737
150856
|
}
|
|
150738
150857
|
const row = db.prepare(`SELECT workspace.share_categories AS shareCategories
|
|
150739
150858
|
FROM workspace_members AS member
|
|
@@ -150741,7 +150860,11 @@ function selectWorkspaceShareCategories(db, identities) {
|
|
|
150741
150860
|
WHERE member.project_path IN (${placeholders(candidates)})
|
|
150742
150861
|
ORDER BY workspace.id ASC
|
|
150743
150862
|
LIMIT 1`).get(...candidates);
|
|
150744
|
-
|
|
150863
|
+
if (!row) {
|
|
150864
|
+
log("[magic-context] WARN: workspace member has no workspace share_categories row; sharing no foreign memory categories");
|
|
150865
|
+
return [];
|
|
150866
|
+
}
|
|
150867
|
+
return normalizeShareCategories(row.shareCategories);
|
|
150745
150868
|
}
|
|
150746
150869
|
function resolveWorkspaceShareCategories(db, projectIdentity) {
|
|
150747
150870
|
return selectWorkspaceShareCategories(db, [projectIdentity]);
|
|
@@ -150846,7 +150969,7 @@ function computeWorkspaceEpochFingerprint(db, identities) {
|
|
|
150846
150969
|
const hash = createHash2("sha256");
|
|
150847
150970
|
hash.update("share_categories", "utf8");
|
|
150848
150971
|
hash.update("\x00");
|
|
150849
|
-
hash.update(shareCategories === null ? "
|
|
150972
|
+
hash.update(shareCategories === null ? "NO_WORKSPACE" : JSON.stringify(shareCategories), "utf8");
|
|
150850
150973
|
hash.update(`
|
|
150851
150974
|
`);
|
|
150852
150975
|
for (const identity of canonical) {
|
|
@@ -153418,6 +153541,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
153418
153541
|
CREATE TABLE IF NOT EXISTS message_history_index (
|
|
153419
153542
|
session_id TEXT PRIMARY KEY,
|
|
153420
153543
|
last_indexed_ordinal INTEGER NOT NULL DEFAULT 0,
|
|
153544
|
+
dirty_floor_ordinal INTEGER NOT NULL DEFAULT 0,
|
|
153421
153545
|
updated_at INTEGER NOT NULL,
|
|
153422
153546
|
harness TEXT NOT NULL DEFAULT 'opencode'
|
|
153423
153547
|
);
|
|
@@ -153447,6 +153571,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
153447
153571
|
last_nudge_level TEXT DEFAULT '',
|
|
153448
153572
|
channel2_nudge_state TEXT DEFAULT '',
|
|
153449
153573
|
channel2_nudge_claimed_at INTEGER DEFAULT 0,
|
|
153574
|
+
channel2_nudge_claim_token TEXT DEFAULT '',
|
|
153450
153575
|
last_emergency_input_sample INTEGER DEFAULT 0,
|
|
153451
153576
|
last_transform_error TEXT DEFAULT '',
|
|
153452
153577
|
nudge_anchor_message_id TEXT DEFAULT '',
|
|
@@ -153693,6 +153818,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
153693
153818
|
ensureColumn(db, "session_meta", "last_nudge_level", "TEXT DEFAULT ''");
|
|
153694
153819
|
ensureColumn(db, "session_meta", "channel2_nudge_state", "TEXT DEFAULT ''");
|
|
153695
153820
|
ensureColumn(db, "session_meta", "channel2_nudge_claimed_at", "INTEGER DEFAULT 0");
|
|
153821
|
+
ensureColumn(db, "session_meta", "channel2_nudge_claim_token", "TEXT DEFAULT ''");
|
|
153696
153822
|
ensureColumn(db, "session_meta", "last_emergency_input_sample", "INTEGER DEFAULT 0");
|
|
153697
153823
|
ensureColumn(db, "session_meta", "last_transform_error", "TEXT DEFAULT ''");
|
|
153698
153824
|
ensureColumn(db, "session_meta", "nudge_anchor_message_id", "TEXT DEFAULT ''");
|
|
@@ -153905,6 +154031,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
153905
154031
|
ON transform_decisions(session_id, harness);
|
|
153906
154032
|
`);
|
|
153907
154033
|
ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
154034
|
+
ensureColumn(db, "message_history_index", "dirty_floor_ordinal", "INTEGER NOT NULL DEFAULT 0");
|
|
153908
154035
|
ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
153909
154036
|
ensureColumn(db, "source_contents", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
153910
154037
|
ensureColumn(db, "compartments", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
@@ -153920,7 +154047,7 @@ var CHANNEL2_CLAIM_TTL_MS = 120000;
|
|
|
153920
154047
|
function healWedgedChannel2Claims(db) {
|
|
153921
154048
|
try {
|
|
153922
154049
|
const staleBefore = Date.now() - CHANNEL2_CLAIM_TTL_MS;
|
|
153923
|
-
db.prepare("UPDATE session_meta SET channel2_nudge_state = 'pending', channel2_nudge_claimed_at = 0 WHERE channel2_nudge_state = 'claimed' AND (channel2_nudge_claimed_at IS NULL OR channel2_nudge_claimed_at = 0 OR channel2_nudge_claimed_at <= ?)").run(staleBefore);
|
|
154050
|
+
db.prepare("UPDATE session_meta SET channel2_nudge_state = 'pending', channel2_nudge_claimed_at = 0, channel2_nudge_claim_token = '' WHERE channel2_nudge_state = 'claimed' AND (channel2_nudge_claimed_at IS NULL OR channel2_nudge_claimed_at = 0 OR channel2_nudge_claimed_at <= ?)").run(staleBefore);
|
|
153924
154051
|
} catch {}
|
|
153925
154052
|
}
|
|
153926
154053
|
function openDatabase(dbPathOrOptions) {
|
|
@@ -154613,7 +154740,7 @@ function setChannel2NudgeState(db, sessionId, state) {
|
|
|
154613
154740
|
db.transaction(() => {
|
|
154614
154741
|
ensureSessionMetaRow(db, sessionId);
|
|
154615
154742
|
const claimedAt = state === "claimed" ? Date.now() : 0;
|
|
154616
|
-
db.prepare("UPDATE session_meta SET channel2_nudge_state = ?, channel2_nudge_claimed_at =
|
|
154743
|
+
db.prepare("UPDATE session_meta SET channel2_nudge_state = ?, channel2_nudge_claimed_at = ?, channel2_nudge_claim_token = '' WHERE session_id = ?").run(state, claimedAt, sessionId);
|
|
154617
154744
|
})();
|
|
154618
154745
|
}
|
|
154619
154746
|
function casChannel2NudgeState(db, sessionId, from, to) {
|
|
@@ -154621,7 +154748,7 @@ function casChannel2NudgeState(db, sessionId, from, to) {
|
|
|
154621
154748
|
db.transaction(() => {
|
|
154622
154749
|
ensureSessionMetaRow(db, sessionId);
|
|
154623
154750
|
const claimedAt = to === "claimed" ? Date.now() : 0;
|
|
154624
|
-
const result = db.prepare("UPDATE session_meta SET channel2_nudge_state = ?, channel2_nudge_claimed_at =
|
|
154751
|
+
const result = db.prepare("UPDATE session_meta SET channel2_nudge_state = ?, channel2_nudge_claimed_at = ?, channel2_nudge_claim_token = '' WHERE session_id = ? AND channel2_nudge_state = ?").run(to, claimedAt, sessionId, from);
|
|
154625
154752
|
changed = (result.changes ?? 0) > 0;
|
|
154626
154753
|
})();
|
|
154627
154754
|
return changed;
|
|
@@ -155029,6 +155156,13 @@ function setSessionWorkMetrics(db, sessionId, newWorkTokens, totalInputTokens) {
|
|
|
155029
155156
|
SET new_work_tokens = ?, total_input_tokens = ?
|
|
155030
155157
|
WHERE session_id = ?`).run(Math.max(0, Math.floor(newWorkTokens)), Math.max(0, Math.floor(totalInputTokens)), sessionId);
|
|
155031
155158
|
}
|
|
155159
|
+
function getSessionWorkMetrics(db, sessionId) {
|
|
155160
|
+
const row = db.prepare("SELECT new_work_tokens, total_input_tokens FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
155161
|
+
return {
|
|
155162
|
+
newWorkTokens: typeof row?.new_work_tokens === "number" ? row.new_work_tokens : 0,
|
|
155163
|
+
totalInputTokens: typeof row?.total_input_tokens === "number" ? row.total_input_tokens : 0
|
|
155164
|
+
};
|
|
155165
|
+
}
|
|
155032
155166
|
// ../plugin/src/features/magic-context/storage-meta-session.ts
|
|
155033
155167
|
import { Buffer as Buffer3 } from "node:buffer";
|
|
155034
155168
|
|
|
@@ -156826,8 +156960,31 @@ function clearNoteNudgeTriggerOnly(db, sessionId) {
|
|
|
156826
156960
|
|
|
156827
156961
|
// ../plugin/src/hooks/magic-context/todo-view.ts
|
|
156828
156962
|
import { createHash as createHash5 } from "node:crypto";
|
|
156829
|
-
var
|
|
156830
|
-
var
|
|
156963
|
+
var TODO_STATUS_PENDING = "pending";
|
|
156964
|
+
var TODO_STATUS_IN_PROGRESS = "in_progress";
|
|
156965
|
+
var TODO_STATUS_COMPLETED = "completed";
|
|
156966
|
+
var TODO_STATUS_CANCELLED = "cancelled";
|
|
156967
|
+
var TODO_PRIORITY_HIGH = "high";
|
|
156968
|
+
var TODO_PRIORITY_MEDIUM = "medium";
|
|
156969
|
+
var TODO_PRIORITY_LOW = "low";
|
|
156970
|
+
var TODO_STATUSES = [
|
|
156971
|
+
TODO_STATUS_PENDING,
|
|
156972
|
+
TODO_STATUS_IN_PROGRESS,
|
|
156973
|
+
TODO_STATUS_COMPLETED,
|
|
156974
|
+
TODO_STATUS_CANCELLED
|
|
156975
|
+
];
|
|
156976
|
+
var TODO_PRIORITIES = [
|
|
156977
|
+
TODO_PRIORITY_HIGH,
|
|
156978
|
+
TODO_PRIORITY_MEDIUM,
|
|
156979
|
+
TODO_PRIORITY_LOW
|
|
156980
|
+
];
|
|
156981
|
+
var TODO_STATUS_SET = new Set(TODO_STATUSES);
|
|
156982
|
+
var TODO_PRIORITY_SET = new Set(TODO_PRIORITIES);
|
|
156983
|
+
var TERMINAL_STATUSES = new Set([
|
|
156984
|
+
TODO_STATUS_COMPLETED,
|
|
156985
|
+
TODO_STATUS_CANCELLED
|
|
156986
|
+
]);
|
|
156987
|
+
var TITLE_DONE_STATUSES = new Set([TODO_STATUS_COMPLETED]);
|
|
156831
156988
|
var SYNTHETIC_CALL_ID_PREFIX = "mc_synthetic_todo_";
|
|
156832
156989
|
function normalizeTodoStateJson(todos) {
|
|
156833
156990
|
if (!Array.isArray(todos))
|
|
@@ -156839,7 +156996,7 @@ function normalizeTodoStateJson(todos) {
|
|
|
156839
156996
|
normalized.push({
|
|
156840
156997
|
content: todo.content,
|
|
156841
156998
|
status: todo.status,
|
|
156842
|
-
priority: todo.priority ??
|
|
156999
|
+
priority: todo.priority ?? TODO_PRIORITY_MEDIUM
|
|
156843
157000
|
});
|
|
156844
157001
|
}
|
|
156845
157002
|
return JSON.stringify(normalized);
|
|
@@ -156883,11 +157040,11 @@ function parseTodoState(stateJson) {
|
|
|
156883
157040
|
const result = [];
|
|
156884
157041
|
for (const item of parsed) {
|
|
156885
157042
|
if (!isTodoItem(item))
|
|
156886
|
-
|
|
157043
|
+
return null;
|
|
156887
157044
|
result.push({
|
|
156888
157045
|
content: item.content,
|
|
156889
157046
|
status: item.status,
|
|
156890
|
-
priority: item.priority ??
|
|
157047
|
+
priority: item.priority ?? TODO_PRIORITY_MEDIUM
|
|
156891
157048
|
});
|
|
156892
157049
|
}
|
|
156893
157050
|
return result;
|
|
@@ -156895,11 +157052,17 @@ function parseTodoState(stateJson) {
|
|
|
156895
157052
|
return null;
|
|
156896
157053
|
}
|
|
156897
157054
|
}
|
|
157055
|
+
function isTodoStatus(value) {
|
|
157056
|
+
return typeof value === "string" && TODO_STATUS_SET.has(value);
|
|
157057
|
+
}
|
|
157058
|
+
function isTodoPriority(value) {
|
|
157059
|
+
return typeof value === "string" && TODO_PRIORITY_SET.has(value);
|
|
157060
|
+
}
|
|
156898
157061
|
function isTodoItem(value) {
|
|
156899
157062
|
if (value === null || typeof value !== "object")
|
|
156900
157063
|
return false;
|
|
156901
157064
|
const todo = value;
|
|
156902
|
-
return typeof todo.content === "string" &&
|
|
157065
|
+
return typeof todo.content === "string" && isTodoStatus(todo.status) && (todo.priority === undefined || isTodoPriority(todo.priority));
|
|
156903
157066
|
}
|
|
156904
157067
|
|
|
156905
157068
|
// ../plugin/src/hooks/magic-context/upgrade-reminder.ts
|
|
@@ -157979,7 +158142,8 @@ function registerCtxAugCommand(pi, config) {
|
|
|
157979
158142
|
const branch = ctx.sessionManager.getBranch();
|
|
157980
158143
|
const lastEntryId = branch.length > 0 ? branch[branch.length - 1]?.id : "unknown";
|
|
157981
158144
|
const sessionLabel = `pi-session-${lastEntryId}`;
|
|
157982
|
-
|
|
158145
|
+
const currentConfig = typeof config === "function" ? config(ctx) : config;
|
|
158146
|
+
if (!currentConfig) {
|
|
157983
158147
|
ctx.ui.notify("/ctx-aug: Sidekick is not configured. Add `sidekick.model` to your magic-context.jsonc to enable this command.", "warning");
|
|
157984
158148
|
return;
|
|
157985
158149
|
}
|
|
@@ -157991,20 +158155,20 @@ function registerCtxAugCommand(pi, config) {
|
|
|
157991
158155
|
ctx.ui.notify("\uD83D\uDD0D Preparing augmentation… 2-10s depending on your sidekick provider.", "info");
|
|
157992
158156
|
}
|
|
157993
158157
|
sessionLog(sessionLabel, "/ctx-aug: spawning sidekick", {
|
|
157994
|
-
model:
|
|
158158
|
+
model: currentConfig.model
|
|
157995
158159
|
});
|
|
157996
158160
|
const projectIdentity = resolveProjectIdentity(ctx.cwd);
|
|
157997
158161
|
sessionLog(sessionLabel, "/ctx-aug: project identity", projectIdentity);
|
|
157998
158162
|
const result = await runner.run({
|
|
157999
158163
|
agent: "sidekick",
|
|
158000
|
-
systemPrompt: withContentLanguageDirective(
|
|
158164
|
+
systemPrompt: withContentLanguageDirective(currentConfig.systemPrompt ?? SIDEKICK_SYSTEM_PROMPT, currentConfig.language),
|
|
158001
158165
|
userMessage: prompt,
|
|
158002
|
-
model:
|
|
158003
|
-
fallbackModels:
|
|
158004
|
-
timeoutMs:
|
|
158166
|
+
model: currentConfig.model,
|
|
158167
|
+
fallbackModels: currentConfig.fallbackModels,
|
|
158168
|
+
timeoutMs: currentConfig.timeoutMs ?? 30000,
|
|
158005
158169
|
cwd: ctx.cwd,
|
|
158006
158170
|
signal: ctx.signal,
|
|
158007
|
-
thinkingLevel:
|
|
158171
|
+
thinkingLevel: currentConfig.thinking_level,
|
|
158008
158172
|
accountingSessionId: sessionLabel,
|
|
158009
158173
|
accountingSubagent: "sidekick"
|
|
158010
158174
|
});
|
|
@@ -158577,6 +158741,22 @@ function extractLatestAssistantText(messages) {
|
|
|
158577
158741
|
return getTextParts(latest).map((part) => part.text).join(`
|
|
158578
158742
|
`) || null;
|
|
158579
158743
|
}
|
|
158744
|
+
function hasLengthCappedOutput(value) {
|
|
158745
|
+
if (Array.isArray(value))
|
|
158746
|
+
return value.some((item) => hasLengthCappedOutput(item));
|
|
158747
|
+
if (!isRecord(value))
|
|
158748
|
+
return false;
|
|
158749
|
+
if (value.length_capped === true || value.lengthCapped === true)
|
|
158750
|
+
return true;
|
|
158751
|
+
const finishReason = value.finish_reason ?? value.finishReason;
|
|
158752
|
+
if (typeof finishReason === "string") {
|
|
158753
|
+
const normalized = finishReason.toLowerCase();
|
|
158754
|
+
if (normalized === "length" || normalized === "max_tokens" || normalized === "max_output_tokens") {
|
|
158755
|
+
return true;
|
|
158756
|
+
}
|
|
158757
|
+
}
|
|
158758
|
+
return Object.values(value).some((item) => hasLengthCappedOutput(item));
|
|
158759
|
+
}
|
|
158580
158760
|
|
|
158581
158761
|
// ../plugin/src/features/magic-context/dreamer/task-executor.ts
|
|
158582
158762
|
init_logger();
|
|
@@ -173234,6 +173414,10 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
173234
173414
|
}).describe("Embedding provider configuration"),
|
|
173235
173415
|
temporal_awareness: exports_external.boolean().default(true).describe('Inject wall-clock gap markers (<!-- +Xm -->) between user messages where > 5 min elapsed since the previous message, and add start/end date attributes on compartments. Gives the agent a sense of session pacing and "how long ago" across multi-day sessions. Graduated from experimental.temporal_awareness; default: true (set false to opt out).'),
|
|
173236
173416
|
keep_subagents: exports_external.boolean().default(false).describe("Debug: keep the child sessions Magic Context spawns for its own subagents (historian, dreamer, sidekick, memory-migration) instead of deleting them on success. Useful for short-term inspection/data collection — their full transcript (prompt, tool calls, token usage, output) stays in the host session store. Kept sessions accumulate until manually cleared; leave false for normal use. Requires a restart to take effect."),
|
|
173417
|
+
todowrite: exports_external.object({
|
|
173418
|
+
enabled: exports_external.boolean().default(true).describe("Pi only: register Magic Context's todowrite task-list tool. Disable if you use your own todo extension. OpenCode ships its own built-in todowrite; this setting has no effect there."),
|
|
173419
|
+
overlay: exports_external.boolean().default(true).describe("Pi only: show the persistent todo overlay above the editor while tasks are active.")
|
|
173420
|
+
}).default({ enabled: true, overlay: true }).describe("Pi-only todowrite tool and overlay controls. Pi registers tools and widgets at extension boot, so changing this after /cd requires /reload or restart."),
|
|
173237
173421
|
smart_drops: exports_external.boolean().default(false).describe("Content-aware reclaim of provably-superseded tool output, layered on the existing execute-pass auto-drop. When on: superseded todowrite (keep newest 1), spent ctx_reduce (keep newest 5), and zero-value meta (bash_status, bash_kill, ctx_note read/dismiss) outputs are dropped; older edits to a file are compressed to a filePath-preserving marker while the newest edit per file stays full. Only acts on passes already busting the cache, so it never originates a cache bust. Honors the protected-tag reserve. Experimental: opt-in, default off until cache stability is proven; when off the wire is byte-identical to the positional-only reclaim. Requires a restart."),
|
|
173238
173422
|
caveman_text_compression: exports_external.object({
|
|
173239
173423
|
enabled: exports_external.boolean().default(false).describe("Apply deterministic caveman-style text compression to old conversation text. Active for primary sessions when enabled; never for subagents. Compresses user/assistant text in oldest-first tiers: ultra (oldest 20%), full, lite, untouched (newest 40%)."),
|
|
@@ -174625,6 +174809,7 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
174625
174809
|
|
|
174626
174810
|
// ../plugin/src/features/magic-context/memory/storage-memory-embeddings.ts
|
|
174627
174811
|
var saveEmbeddingStatements = new WeakMap;
|
|
174812
|
+
var saveEmbeddingIfHashMatchesStatements = new WeakMap;
|
|
174628
174813
|
var loadAllEmbeddingsStatements = new WeakMap;
|
|
174629
174814
|
var deleteEmbeddingStatements = new WeakMap;
|
|
174630
174815
|
var getStoredModelIdStatements = new WeakMap;
|
|
@@ -174655,6 +174840,14 @@ function getSaveEmbeddingStatement(db) {
|
|
|
174655
174840
|
}
|
|
174656
174841
|
return stmt;
|
|
174657
174842
|
}
|
|
174843
|
+
function getSaveEmbeddingIfHashMatchesStatement(db) {
|
|
174844
|
+
let stmt = saveEmbeddingIfHashMatchesStatements.get(db);
|
|
174845
|
+
if (!stmt) {
|
|
174846
|
+
stmt = db.prepare("INSERT INTO memory_embeddings (memory_id, embedding, model_id) SELECT ?, ?, ? FROM memories WHERE id = ? AND normalized_hash = ? ON CONFLICT(memory_id, model_id) DO UPDATE SET embedding = excluded.embedding");
|
|
174847
|
+
saveEmbeddingIfHashMatchesStatements.set(db, stmt);
|
|
174848
|
+
}
|
|
174849
|
+
return stmt;
|
|
174850
|
+
}
|
|
174658
174851
|
function getLoadAllEmbeddingsStatement(db) {
|
|
174659
174852
|
let stmt = loadAllEmbeddingsStatements.get(db);
|
|
174660
174853
|
if (!stmt) {
|
|
@@ -174683,6 +174876,10 @@ function saveEmbedding(db, memoryId, embedding, modelId) {
|
|
|
174683
174876
|
const blob = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
|
|
174684
174877
|
getSaveEmbeddingStatement(db).run(memoryId, blob, modelId);
|
|
174685
174878
|
}
|
|
174879
|
+
function saveEmbeddingIfHashMatches(db, memoryId, embedding, modelId, normalizedHash) {
|
|
174880
|
+
const blob = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
|
|
174881
|
+
return getSaveEmbeddingIfHashMatchesStatement(db).run(memoryId, blob, modelId, memoryId, normalizedHash).changes > 0;
|
|
174882
|
+
}
|
|
174686
174883
|
function loadAllEmbeddings(db, projectPath, modelId) {
|
|
174687
174884
|
const rows = getLoadAllEmbeddingsStatement(db).all(projectPath, modelId).filter(isEmbeddingRow);
|
|
174688
174885
|
const embeddings = new Map;
|
|
@@ -175543,12 +175740,12 @@ function isUnembeddedMemoryRow(row) {
|
|
|
175543
175740
|
if (row === null || typeof row !== "object")
|
|
175544
175741
|
return false;
|
|
175545
175742
|
const candidate = row;
|
|
175546
|
-
return typeof candidate.id === "number" && typeof candidate.content === "string";
|
|
175743
|
+
return typeof candidate.id === "number" && typeof candidate.content === "string" && typeof candidate.normalized_hash === "string";
|
|
175547
175744
|
}
|
|
175548
175745
|
function getLoadUnembeddedMemoriesStatement(db) {
|
|
175549
175746
|
let stmt = loadUnembeddedMemoriesStatements.get(db);
|
|
175550
175747
|
if (!stmt) {
|
|
175551
|
-
stmt = db.prepare("SELECT m.id AS id, m.content AS content FROM memories m LEFT JOIN memory_embeddings me ON m.id = me.memory_id AND me.model_id = ? WHERE m.project_path = ? AND m.status = 'active' AND me.memory_id IS NULL LIMIT ?");
|
|
175748
|
+
stmt = db.prepare("SELECT m.id AS id, m.content AS content, m.normalized_hash AS normalized_hash FROM memories m LEFT JOIN memory_embeddings me ON m.id = me.memory_id AND me.model_id = ? WHERE m.project_path = ? AND m.status = 'active' AND me.memory_id IS NULL LIMIT ?");
|
|
175552
175749
|
loadUnembeddedMemoriesStatements.set(db, stmt);
|
|
175553
175750
|
}
|
|
175554
175751
|
return stmt;
|
|
@@ -175571,8 +175768,9 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
175571
175768
|
const embedding = result.vectors[index];
|
|
175572
175769
|
if (!embedding)
|
|
175573
175770
|
continue;
|
|
175574
|
-
|
|
175575
|
-
|
|
175771
|
+
if (saveEmbeddingIfHashMatches(db, memory.id, embedding, result.modelId, memory.normalized_hash)) {
|
|
175772
|
+
embeddedCount += 1;
|
|
175773
|
+
}
|
|
175576
175774
|
}
|
|
175577
175775
|
})();
|
|
175578
175776
|
return embeddedCount;
|
|
@@ -176274,25 +176472,33 @@ function getMemoryCountsByStatusStatement(db) {
|
|
|
176274
176472
|
}
|
|
176275
176473
|
return stmt;
|
|
176276
176474
|
}
|
|
176277
|
-
function
|
|
176278
|
-
const now = Date.now();
|
|
176279
|
-
const normalizedHash = computeNormalizedHash(input.content);
|
|
176475
|
+
function buildInsertMemoryValues(input, normalizedHash, now, includeImportance) {
|
|
176280
176476
|
const insertValues = [
|
|
176281
176477
|
input.projectPath,
|
|
176282
176478
|
input.category,
|
|
176283
176479
|
input.content,
|
|
176284
176480
|
normalizedHash
|
|
176285
176481
|
];
|
|
176286
|
-
if (
|
|
176482
|
+
if (includeImportance) {
|
|
176287
176483
|
insertValues.push(input.importance ?? 50);
|
|
176288
176484
|
}
|
|
176289
176485
|
insertValues.push(input.sourceSessionId ?? null, input.sourceType ?? "historian", 1, 0, now, now, now, now, null, "active", input.expiresAt ?? null, "unverified", null, null, null, input.metadataJson ?? null);
|
|
176290
|
-
|
|
176291
|
-
|
|
176292
|
-
|
|
176486
|
+
return insertValues;
|
|
176487
|
+
}
|
|
176488
|
+
function loadInsertedMemory(db, rowid) {
|
|
176489
|
+
const inserted = getMemoryById(db, Number(rowid));
|
|
176293
176490
|
if (!inserted) {
|
|
176294
176491
|
throw new Error("Failed to load inserted memory row");
|
|
176295
176492
|
}
|
|
176493
|
+
return inserted;
|
|
176494
|
+
}
|
|
176495
|
+
function insertMemory(db, input) {
|
|
176496
|
+
const now = Date.now();
|
|
176497
|
+
const normalizedHash = computeNormalizedHash(input.content);
|
|
176498
|
+
const insertValues = buildInsertMemoryValues(input, normalizedHash, now, hasMemoryImportanceColumn(db));
|
|
176499
|
+
const result = getInsertMemoryStatement(db).run(...insertValues);
|
|
176500
|
+
const insertedResult = result;
|
|
176501
|
+
const inserted = loadInsertedMemory(db, insertedResult.lastInsertRowid);
|
|
176296
176502
|
invalidateProject(input.projectPath);
|
|
176297
176503
|
return inserted;
|
|
176298
176504
|
}
|
|
@@ -176773,9 +176979,11 @@ import path7 from "node:path";
|
|
|
176773
176979
|
import { promisify } from "node:util";
|
|
176774
176980
|
var execFileAsync = promisify(execFile);
|
|
176775
176981
|
var GIT_TIMEOUT_MS2 = 1e4;
|
|
176982
|
+
var defaultExecFileForVerificationPaths = async (file2, args, options) => await execFileAsync(file2, [...args], options);
|
|
176983
|
+
var execFileForVerificationPaths = defaultExecFileForVerificationPaths;
|
|
176776
176984
|
async function runGit(cwd, args) {
|
|
176777
176985
|
try {
|
|
176778
|
-
const result = await
|
|
176986
|
+
const result = await execFileForVerificationPaths("git", args, {
|
|
176779
176987
|
cwd,
|
|
176780
176988
|
timeout: GIT_TIMEOUT_MS2,
|
|
176781
176989
|
maxBuffer: 16 * 1024 * 1024,
|
|
@@ -177084,6 +177292,14 @@ function renewLease(db, holderId, leaseKey = DREAMING_LEASE_KEY) {
|
|
|
177084
177292
|
return true;
|
|
177085
177293
|
});
|
|
177086
177294
|
}
|
|
177295
|
+
function runLeaseGuardedWrite(db, holderId, leaseKey, fn) {
|
|
177296
|
+
return runImmediate2(db, () => {
|
|
177297
|
+
if (!peekLeaseHolderAndExpiry(db, holderId, leaseKey)) {
|
|
177298
|
+
throw new Error("Dream lease lost before guarded write");
|
|
177299
|
+
}
|
|
177300
|
+
return fn();
|
|
177301
|
+
});
|
|
177302
|
+
}
|
|
177087
177303
|
var LEASE_HEARTBEAT_INTERVAL_MS = 60 * 1000;
|
|
177088
177304
|
function startLeaseHeartbeat(db, holderId, leaseKey, onLost, intervalMs = LEASE_HEARTBEAT_INTERVAL_MS) {
|
|
177089
177305
|
let lost = false;
|
|
@@ -177878,6 +178094,39 @@ function hasShareabilitySensitiveText(text) {
|
|
|
177878
178094
|
}
|
|
177879
178095
|
}
|
|
177880
178096
|
|
|
178097
|
+
// ../plugin/src/features/magic-context/dreamer/manifest-parser.ts
|
|
178098
|
+
function extractCompleteManifestBody(text, rootName) {
|
|
178099
|
+
const escapedRoot = rootName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
178100
|
+
const rootMatch = new RegExp(`<${escapedRoot}\\b[^>]*>([\\s\\S]*?)<\\/${escapedRoot}>`, "i").exec(text);
|
|
178101
|
+
if (rootMatch)
|
|
178102
|
+
return rootMatch[1];
|
|
178103
|
+
const hasOpenRoot = new RegExp(`<${escapedRoot}\\b`, "i").test(text);
|
|
178104
|
+
const hasCloseRoot = new RegExp(`<\\/${escapedRoot}>`, "i").test(text);
|
|
178105
|
+
if (hasOpenRoot && !hasCloseRoot) {
|
|
178106
|
+
throw new Error(`${rootName} manifest missing closing root tag`);
|
|
178107
|
+
}
|
|
178108
|
+
throw new Error(`${rootName} manifest missing complete root element`);
|
|
178109
|
+
}
|
|
178110
|
+
function assertNoDuplicateManifestIds(ids, rootName) {
|
|
178111
|
+
const seen = new Set;
|
|
178112
|
+
for (const id of ids) {
|
|
178113
|
+
if (seen.has(id))
|
|
178114
|
+
throw new Error(`${rootName} manifest contains duplicate id ${id}`);
|
|
178115
|
+
seen.add(id);
|
|
178116
|
+
}
|
|
178117
|
+
}
|
|
178118
|
+
function assertManifestCoversExactly(ids, expectedIds, rootName) {
|
|
178119
|
+
assertNoDuplicateManifestIds(ids, rootName);
|
|
178120
|
+
for (const id of ids) {
|
|
178121
|
+
if (!expectedIds.has(id))
|
|
178122
|
+
throw new Error(`${rootName} manifest contains unknown id ${id}`);
|
|
178123
|
+
}
|
|
178124
|
+
for (const id of expectedIds) {
|
|
178125
|
+
if (!ids.includes(id))
|
|
178126
|
+
throw new Error(`${rootName} manifest missing id ${id}`);
|
|
178127
|
+
}
|
|
178128
|
+
}
|
|
178129
|
+
|
|
177881
178130
|
// ../plugin/src/features/magic-context/dreamer/classify-prompt.ts
|
|
177882
178131
|
var SCORING_GUIDANCE = `### How to score importance (1-100)
|
|
177883
178132
|
Importance decides which memories survive when the injected memory block is over budget: high scores stay in context, low scores drop first. So the score is only useful if it **discriminates** — if most memories land in the same band, you have not classified them, you have just labelled them.
|
|
@@ -177944,14 +178193,15 @@ ${renderPool(args.memories)}`;
|
|
|
177944
178193
|
var SCOPES = new Set(["project", "ecosystem", "universe"]);
|
|
177945
178194
|
function parseClassifyManifest(text) {
|
|
177946
178195
|
const out = [];
|
|
177947
|
-
|
|
178196
|
+
const body = extractCompleteManifestBody(text, "classify");
|
|
178197
|
+
for (const m of body.matchAll(/<memory\b([^>]*)\/?>/g)) {
|
|
177948
178198
|
const attrs = m[1];
|
|
177949
178199
|
const idMatch = attrs.match(/\bid\s*=\s*"(\d+)"/);
|
|
177950
178200
|
if (!idMatch)
|
|
177951
|
-
|
|
178201
|
+
throw new Error("classify manifest entry missing numeric id");
|
|
177952
178202
|
const id = Number.parseInt(idMatch[1], 10);
|
|
177953
178203
|
if (!Number.isInteger(id))
|
|
177954
|
-
|
|
178204
|
+
throw new Error("classify manifest entry missing numeric id");
|
|
177955
178205
|
const entry = { id };
|
|
177956
178206
|
const impMatch = attrs.match(/\bimportance\s*=\s*"(\d+)"/);
|
|
177957
178207
|
if (impMatch) {
|
|
@@ -177960,18 +178210,23 @@ function parseClassifyManifest(text) {
|
|
|
177960
178210
|
entry.importance = Math.max(1, Math.min(100, imp));
|
|
177961
178211
|
}
|
|
177962
178212
|
const scopeMatch = attrs.match(/\bscope\s*=\s*"([a-z]+)"/i);
|
|
177963
|
-
if (scopeMatch
|
|
177964
|
-
|
|
178213
|
+
if (scopeMatch) {
|
|
178214
|
+
const scope = scopeMatch[1].toLowerCase();
|
|
178215
|
+
if (!SCOPES.has(scope))
|
|
178216
|
+
throw new Error(`classify manifest invalid scope ${scope}`);
|
|
178217
|
+
entry.scope = scope;
|
|
177965
178218
|
}
|
|
177966
178219
|
const shareMatch = attrs.match(/\bshareable\s*=\s*"(true|false|1|0)"/i);
|
|
177967
178220
|
if (shareMatch) {
|
|
177968
178221
|
const v = shareMatch[1].toLowerCase();
|
|
177969
178222
|
entry.shareable = v === "true" || v === "1";
|
|
177970
178223
|
}
|
|
177971
|
-
if (entry.importance
|
|
177972
|
-
|
|
178224
|
+
if (entry.importance === undefined && !entry.scope && entry.shareable === undefined) {
|
|
178225
|
+
throw new Error(`classify manifest entry ${id} missing classification fields`);
|
|
177973
178226
|
}
|
|
178227
|
+
out.push(entry);
|
|
177974
178228
|
}
|
|
178229
|
+
assertNoDuplicateManifestIds(out.map((entry) => entry.id), "classify");
|
|
177975
178230
|
return out;
|
|
177976
178231
|
}
|
|
177977
178232
|
|
|
@@ -178106,9 +178361,13 @@ async function classifyOneChunk(args, chunk, anchors, sliceMs, signal) {
|
|
|
178106
178361
|
});
|
|
178107
178362
|
},
|
|
178108
178363
|
validateOutput: (messages) => {
|
|
178364
|
+
if (hasLengthCappedOutput(messages)) {
|
|
178365
|
+
throw new Error("classify returned length-capped output");
|
|
178366
|
+
}
|
|
178109
178367
|
const text = extractLatestAssistantText(messages);
|
|
178110
178368
|
if (!text)
|
|
178111
178369
|
throw new Error("classify returned no output");
|
|
178370
|
+
parseClassifyManifest(text);
|
|
178112
178371
|
return text;
|
|
178113
178372
|
}
|
|
178114
178373
|
});
|
|
@@ -178134,17 +178393,13 @@ async function classifyOneChunk(args, chunk, anchors, sliceMs, signal) {
|
|
|
178134
178393
|
}
|
|
178135
178394
|
function applyClassifications(args, chunk, manifestText) {
|
|
178136
178395
|
const byId = new Map(chunk.map((m) => [m.id, m]));
|
|
178137
|
-
const parsed = parseClassifyManifest(manifestText)
|
|
178396
|
+
const parsed = parseClassifyManifest(manifestText);
|
|
178397
|
+
assertManifestCoversExactly(parsed.map((entry) => entry.id), new Set(byId.keys()), "classify");
|
|
178138
178398
|
if (parsed.length === 0)
|
|
178139
178399
|
return { classified: 0, changed: 0 };
|
|
178140
178400
|
let classified = 0;
|
|
178141
178401
|
let changed = 0;
|
|
178142
|
-
|
|
178143
|
-
args.db.transaction(() => {
|
|
178144
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
178145
|
-
leaseLost = true;
|
|
178146
|
-
return;
|
|
178147
|
-
}
|
|
178402
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
178148
178403
|
for (const p of parsed) {
|
|
178149
178404
|
const memory = byId.get(p.id);
|
|
178150
178405
|
if (!memory)
|
|
@@ -178159,9 +178414,7 @@ function applyClassifications(args, chunk, manifestText) {
|
|
|
178159
178414
|
if (didChange)
|
|
178160
178415
|
changed += 1;
|
|
178161
178416
|
}
|
|
178162
|
-
})
|
|
178163
|
-
if (leaseLost)
|
|
178164
|
-
throw new Error("Dream lease lost during classify commit");
|
|
178417
|
+
});
|
|
178165
178418
|
return { classified, changed };
|
|
178166
178419
|
}
|
|
178167
178420
|
function recordInvocation(args, startedAt, params) {
|
|
@@ -178209,9 +178462,11 @@ var SMART_NOTE_CHECK_LIVENESS_RECHECK_MS = 24 * 60 * 60 * 1000;
|
|
|
178209
178462
|
|
|
178210
178463
|
class SmartNoteNetworkError extends Error {
|
|
178211
178464
|
isSmartNoteNetworkError = true;
|
|
178212
|
-
|
|
178465
|
+
terminal;
|
|
178466
|
+
constructor(message, options = {}) {
|
|
178213
178467
|
super(message);
|
|
178214
178468
|
this.name = "SmartNoteNetworkError";
|
|
178469
|
+
this.terminal = options.terminal ?? false;
|
|
178215
178470
|
}
|
|
178216
178471
|
}
|
|
178217
178472
|
|
|
@@ -178225,6 +178480,9 @@ class SmartNoteSecurityError extends Error {
|
|
|
178225
178480
|
function isSmartNoteNetworkError(error51) {
|
|
178226
178481
|
return error51 instanceof SmartNoteNetworkError || error51 instanceof Error && (error51.name === "SmartNoteNetworkError" || error51.message.includes("SmartNoteNetworkError") || error51.message.includes("SMART_NOTE_NETWORK"));
|
|
178227
178482
|
}
|
|
178483
|
+
function isTerminalSmartNoteNetworkError(error51) {
|
|
178484
|
+
return error51 instanceof SmartNoteNetworkError && error51.terminal;
|
|
178485
|
+
}
|
|
178228
178486
|
function parseSmartNoteManifest(json2) {
|
|
178229
178487
|
if (!json2)
|
|
178230
178488
|
return { capabilities: [] };
|
|
@@ -178254,6 +178512,7 @@ function stringArray(value) {
|
|
|
178254
178512
|
var DNS_TIMEOUT_MS = 3000;
|
|
178255
178513
|
var DEFAULT_HTTP_TIMEOUT_MS = 5000;
|
|
178256
178514
|
var DEFAULT_HTTP_BODY_LIMIT_BYTES = 64 * 1024;
|
|
178515
|
+
var MAX_HTTP_ADDRESS_CANDIDATES = 4;
|
|
178257
178516
|
var defaultResolver = {
|
|
178258
178517
|
async lookup(hostname3, signal) {
|
|
178259
178518
|
return await withAbortAndTimeout(dnsLookup(hostname3, { all: true, verbatim: true }), signal, DNS_TIMEOUT_MS, "DNS lookup timed out").then((rows) => rows.filter((row) => row.family === 4 || row.family === 6).map((row) => ({ address: row.address, family: row.family })));
|
|
@@ -178290,18 +178549,21 @@ async function guardedSmartNoteHttpGet(input, options) {
|
|
|
178290
178549
|
});
|
|
178291
178550
|
const timeoutMs = options.timeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;
|
|
178292
178551
|
const bodyLimitBytes = options.bodyLimitBytes ?? DEFAULT_HTTP_BODY_LIMIT_BYTES;
|
|
178552
|
+
const requestAddress = options.requestAddress ?? requestValidatedAddress;
|
|
178553
|
+
const candidates = validation.addresses.slice(0, MAX_HTTP_ADDRESS_CANDIDATES);
|
|
178293
178554
|
let lastError;
|
|
178294
|
-
for (const candidate of
|
|
178555
|
+
for (const candidate of candidates) {
|
|
178295
178556
|
try {
|
|
178296
|
-
return await
|
|
178557
|
+
return await requestAddress(validation, candidate, {
|
|
178297
178558
|
signal: options.signal,
|
|
178298
178559
|
timeoutMs,
|
|
178299
178560
|
bodyLimitBytes
|
|
178300
178561
|
});
|
|
178301
178562
|
} catch (error51) {
|
|
178302
178563
|
lastError = error51;
|
|
178303
|
-
if (error51 instanceof SmartNoteSecurityError || options.signal.aborted)
|
|
178564
|
+
if (error51 instanceof SmartNoteSecurityError || options.signal.aborted || isTerminalSmartNoteNetworkError(error51)) {
|
|
178304
178565
|
throw error51;
|
|
178566
|
+
}
|
|
178305
178567
|
}
|
|
178306
178568
|
}
|
|
178307
178569
|
throw toNetworkError(lastError, "all validated addresses failed");
|
|
@@ -178371,7 +178633,9 @@ function requestValidatedAddress(validation, candidate, options) {
|
|
|
178371
178633
|
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
178372
178634
|
bytes += buf.byteLength;
|
|
178373
178635
|
if (bytes > options.bodyLimitBytes) {
|
|
178374
|
-
|
|
178636
|
+
reject(new SmartNoteNetworkError("SMART_NOTE_NETWORK: response body too large", { terminal: true }));
|
|
178637
|
+
response.destroy();
|
|
178638
|
+
request2.destroy();
|
|
178375
178639
|
return;
|
|
178376
178640
|
}
|
|
178377
178641
|
chunks.push(buf);
|
|
@@ -178388,10 +178652,16 @@ function requestValidatedAddress(validation, candidate, options) {
|
|
|
178388
178652
|
resolve({ status, body: Buffer.concat(chunks).toString("utf8") });
|
|
178389
178653
|
});
|
|
178390
178654
|
});
|
|
178391
|
-
const onAbort = () =>
|
|
178655
|
+
const onAbort = () => {
|
|
178656
|
+
reject(new SmartNoteNetworkError("SMART_NOTE_NETWORK: aborted"));
|
|
178657
|
+
request2.destroy();
|
|
178658
|
+
};
|
|
178392
178659
|
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
178393
178660
|
request2.on("timeout", () => {
|
|
178394
|
-
|
|
178661
|
+
reject(new SmartNoteNetworkError("SMART_NOTE_NETWORK: request timed out", {
|
|
178662
|
+
terminal: true
|
|
178663
|
+
}));
|
|
178664
|
+
request2.destroy();
|
|
178395
178665
|
});
|
|
178396
178666
|
request2.on("error", (error51) => {
|
|
178397
178667
|
options.signal.removeEventListener("abort", onAbort);
|
|
@@ -178739,6 +179009,20 @@ function withSandboxLock(fn) {
|
|
|
178739
179009
|
var DEFAULT_TIMEOUT_MS = 2000;
|
|
178740
179010
|
var DEFAULT_HEAP_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
178741
179011
|
var DEFAULT_STACK_LIMIT_BYTES = 512 * 1024;
|
|
179012
|
+
function resolveCapabilitiesForRun(options, signal) {
|
|
179013
|
+
if (options.capabilityFactory) {
|
|
179014
|
+
return options.capabilityFactory(signal);
|
|
179015
|
+
}
|
|
179016
|
+
if (options.capabilities) {
|
|
179017
|
+
return options.capabilities;
|
|
179018
|
+
}
|
|
179019
|
+
throw new Error("smart-note check requires capabilities");
|
|
179020
|
+
}
|
|
179021
|
+
function throwIfRunAborted(signal) {
|
|
179022
|
+
if (signal.aborted) {
|
|
179023
|
+
throw signal.reason ?? new Error("smart-note check aborted");
|
|
179024
|
+
}
|
|
179025
|
+
}
|
|
178742
179026
|
async function runCompiledSmartNoteCheck(options) {
|
|
178743
179027
|
return withSandboxLock(() => runCompiledSmartNoteCheckLocked(options));
|
|
178744
179028
|
}
|
|
@@ -178746,17 +179030,24 @@ async function runCompiledSmartNoteCheckLocked(options) {
|
|
|
178746
179030
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
178747
179031
|
const controller = new AbortController;
|
|
178748
179032
|
const externalAbort = () => controller.abort(options.signal?.reason);
|
|
178749
|
-
options.signal?.
|
|
179033
|
+
if (options.signal?.aborted) {
|
|
179034
|
+
externalAbort();
|
|
179035
|
+
} else {
|
|
179036
|
+
options.signal?.addEventListener("abort", externalAbort, { once: true });
|
|
179037
|
+
}
|
|
178750
179038
|
const timer = setTimeout(() => controller.abort(new Error("smart-note check timed out")), timeoutMs);
|
|
178751
179039
|
try {
|
|
179040
|
+
throwIfRunAborted(controller.signal);
|
|
179041
|
+
const capabilities = resolveCapabilitiesForRun(options, controller.signal);
|
|
178752
179042
|
const deadline = Date.now() + timeoutMs;
|
|
178753
179043
|
const quickjs = await getAsyncModule();
|
|
179044
|
+
throwIfRunAborted(controller.signal);
|
|
178754
179045
|
const context = quickjs.newContext();
|
|
178755
179046
|
try {
|
|
178756
179047
|
context.runtime.setMemoryLimit(options.heapLimitBytes ?? DEFAULT_HEAP_LIMIT_BYTES);
|
|
178757
179048
|
context.runtime.setMaxStackSize(options.stackLimitBytes ?? DEFAULT_STACK_LIMIT_BYTES);
|
|
178758
179049
|
context.runtime.setInterruptHandler(() => controller.signal.aborted || Date.now() > deadline);
|
|
178759
|
-
installCapabilityObject(context,
|
|
179050
|
+
installCapabilityObject(context, capabilities);
|
|
178760
179051
|
disableAmbientDynamicCode(context);
|
|
178761
179052
|
const result = await evalCheck(context, options.compiledCheck);
|
|
178762
179053
|
const checkResult = result;
|
|
@@ -178909,6 +179200,7 @@ Remember: output only the JSON object described by the system prompt.`;
|
|
|
178909
179200
|
}
|
|
178910
179201
|
}, {
|
|
178911
179202
|
timeoutMs: remainingMs,
|
|
179203
|
+
signal: args.signal,
|
|
178912
179204
|
fallbackModels: args.fallbackModels,
|
|
178913
179205
|
callContext: "dreamer:smart-note-compiler",
|
|
178914
179206
|
fetchOutput: async () => {
|
|
@@ -178933,7 +179225,8 @@ Remember: output only the JSON object described by the system prompt.`;
|
|
|
178933
179225
|
}
|
|
178934
179226
|
const dryRun = await runCompiledSmartNoteCheck({
|
|
178935
179227
|
compiledCheck,
|
|
178936
|
-
|
|
179228
|
+
capabilityFactory: args.capabilityFactory,
|
|
179229
|
+
signal: args.signal,
|
|
178937
179230
|
timeoutMs: 2000
|
|
178938
179231
|
});
|
|
178939
179232
|
if (!dryRun.ok) {
|
|
@@ -179081,7 +179374,9 @@ function nextSmartNoteCheckDueAt(cron, options = {}) {
|
|
|
179081
179374
|
const rawNext = cron?.trim() ? nextDueAtMs(cron, now) : null;
|
|
179082
179375
|
const rawDelta = rawNext ? rawNext - now : SMART_NOTE_CHECK_DEFAULT_INTERVAL_MS;
|
|
179083
179376
|
const clamped = Math.min(ceilingMs, Math.max(floorMs, rawDelta));
|
|
179084
|
-
|
|
179377
|
+
const jittered = clamped + deterministicJitterMs(clamped, options.noteId, options.hash);
|
|
179378
|
+
const bounded = Math.min(ceilingMs, Math.max(floorMs, jittered));
|
|
179379
|
+
return now + bounded;
|
|
179085
179380
|
}
|
|
179086
179381
|
function deterministicJitterMs(intervalMs, noteId, hash2) {
|
|
179087
179382
|
const max = Math.min(60000, Math.floor(intervalMs * 0.1));
|
|
@@ -179250,9 +179545,9 @@ async function runDueCompiledSmartNoteChecks(args) {
|
|
|
179250
179545
|
try {
|
|
179251
179546
|
const result = await runCompiledSmartNoteCheck({
|
|
179252
179547
|
compiledCheck: note.compiledCheck,
|
|
179253
|
-
|
|
179548
|
+
capabilityFactory: (signal) => createSmartNoteCapabilities({
|
|
179254
179549
|
projectRoot: args.projectRoot,
|
|
179255
|
-
signal
|
|
179550
|
+
signal
|
|
179256
179551
|
}),
|
|
179257
179552
|
signal: controller.signal,
|
|
179258
179553
|
timeoutMs: Math.min(2000, remaining)
|
|
@@ -179315,6 +179610,24 @@ var MAX_COMPILE_PER_RUN = 5;
|
|
|
179315
179610
|
var MAX_FALLBACK_PER_RUN = 3;
|
|
179316
179611
|
var MAX_COMPILATION_FAILURES = 3;
|
|
179317
179612
|
var SMART_NOTE_CONFIRMATION_SYSTEM_PROMPT = 'You are a no-tool smart-note confirmation evaluator. Output only JSON shaped as {"met": boolean}. Return true only when the supplied text alone proves the condition is satisfied.';
|
|
179613
|
+
function createPromptAbortSignal(parent, timeoutMs, timeoutMessage) {
|
|
179614
|
+
const controller = new AbortController;
|
|
179615
|
+
const abortFromParent = () => {
|
|
179616
|
+
controller.abort(parent.reason ?? new Error("smart-note prompt aborted by lease loss"));
|
|
179617
|
+
};
|
|
179618
|
+
if (parent.aborted)
|
|
179619
|
+
abortFromParent();
|
|
179620
|
+
else
|
|
179621
|
+
parent.addEventListener("abort", abortFromParent, { once: true });
|
|
179622
|
+
const timer = setTimeout(() => controller.abort(new Error(timeoutMessage)), timeoutMs);
|
|
179623
|
+
return {
|
|
179624
|
+
signal: controller.signal,
|
|
179625
|
+
cleanup: () => {
|
|
179626
|
+
clearTimeout(timer);
|
|
179627
|
+
parent.removeEventListener("abort", abortFromParent);
|
|
179628
|
+
}
|
|
179629
|
+
};
|
|
179630
|
+
}
|
|
179318
179631
|
async function evaluateSmartNotes(args) {
|
|
179319
179632
|
const projectRoot = args.sessionDirectory ?? args.projectIdentity;
|
|
179320
179633
|
const pendingAtStart = getPendingSmartNotes(args.db, args.projectIdentity).length;
|
|
@@ -179323,6 +179636,7 @@ async function evaluateSmartNotes(args) {
|
|
|
179323
179636
|
return { surfaced: 0, pending: 0, ran: false };
|
|
179324
179637
|
}
|
|
179325
179638
|
let leaseLost = false;
|
|
179639
|
+
const leaseAbortController = new AbortController;
|
|
179326
179640
|
const leaseHeld = () => !leaseLost && peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey);
|
|
179327
179641
|
const assertLeaseHeld = (phase) => {
|
|
179328
179642
|
if (!leaseHeld()) {
|
|
@@ -179332,6 +179646,7 @@ async function evaluateSmartNotes(args) {
|
|
|
179332
179646
|
};
|
|
179333
179647
|
const heartbeat = startLeaseHeartbeat(args.db, args.holderId, args.leaseKey, () => {
|
|
179334
179648
|
leaseLost = true;
|
|
179649
|
+
leaseAbortController.abort(new Error("Dream lease lost during smart notes"));
|
|
179335
179650
|
log("[dreamer] smart notes: lease lost — aborting");
|
|
179336
179651
|
args.onLeaseLost?.("smart notes");
|
|
179337
179652
|
});
|
|
@@ -179354,7 +179669,7 @@ async function evaluateSmartNotes(args) {
|
|
|
179354
179669
|
break;
|
|
179355
179670
|
assertLeaseHeld("compile start");
|
|
179356
179671
|
didWork = true;
|
|
179357
|
-
const compiled = await compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld);
|
|
179672
|
+
const compiled = await compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld, leaseAbortController.signal);
|
|
179358
179673
|
if (compiled)
|
|
179359
179674
|
surfaced += 1;
|
|
179360
179675
|
}
|
|
@@ -179374,7 +179689,7 @@ async function evaluateSmartNotes(args) {
|
|
|
179374
179689
|
break;
|
|
179375
179690
|
assertLeaseHeld("fallback start");
|
|
179376
179691
|
didWork = true;
|
|
179377
|
-
const met = await confirmReadOnly(args, note.id, note.content, note.surfaceCondition);
|
|
179692
|
+
const met = await confirmReadOnly(args, note.id, note.content, note.surfaceCondition, leaseAbortController.signal);
|
|
179378
179693
|
const now = Date.now();
|
|
179379
179694
|
assertLeaseHeld("fallback commit");
|
|
179380
179695
|
commitSmartNoteState(args.db, {
|
|
@@ -179400,9 +179715,8 @@ async function evaluateSmartNotes(args) {
|
|
|
179400
179715
|
heartbeat.stop();
|
|
179401
179716
|
}
|
|
179402
179717
|
}
|
|
179403
|
-
async function compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld) {
|
|
179404
|
-
const
|
|
179405
|
-
const timer = setTimeout(() => controller.abort(new Error("smart-note compile deadline")), Math.max(1000, args.deadline - Date.now()));
|
|
179718
|
+
async function compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld, leaseSignal) {
|
|
179719
|
+
const promptSignal = createPromptAbortSignal(leaseSignal, Math.max(1000, args.deadline - Date.now()), "smart-note compile deadline");
|
|
179406
179720
|
try {
|
|
179407
179721
|
const result = await compileSmartNoteCheck({
|
|
179408
179722
|
client: args.client,
|
|
@@ -179411,7 +179725,8 @@ async function compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld)
|
|
|
179411
179725
|
sessionDirectory: args.sessionDirectory,
|
|
179412
179726
|
projectIdentity: args.projectIdentity,
|
|
179413
179727
|
note,
|
|
179414
|
-
|
|
179728
|
+
capabilityFactory: (signal) => createSmartNoteCapabilities({ projectRoot, signal }),
|
|
179729
|
+
signal: promptSignal.signal,
|
|
179415
179730
|
deadline: args.deadline,
|
|
179416
179731
|
model: args.model,
|
|
179417
179732
|
fallbackModels: args.fallbackModels
|
|
@@ -179456,7 +179771,7 @@ async function compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld)
|
|
|
179456
179771
|
});
|
|
179457
179772
|
return result.dryRun.met;
|
|
179458
179773
|
} finally {
|
|
179459
|
-
|
|
179774
|
+
promptSignal.cleanup();
|
|
179460
179775
|
}
|
|
179461
179776
|
}
|
|
179462
179777
|
async function runLivenessCheck(args, note, projectRoot, assertLeaseHeld, leaseHeld) {
|
|
@@ -179467,7 +179782,7 @@ async function runLivenessCheck(args, note, projectRoot, assertLeaseHeld, leaseH
|
|
|
179467
179782
|
try {
|
|
179468
179783
|
const result = await runCompiledSmartNoteCheck({
|
|
179469
179784
|
compiledCheck: note.compiledCheck,
|
|
179470
|
-
|
|
179785
|
+
capabilityFactory: (signal) => createSmartNoteCapabilities({ projectRoot, signal }),
|
|
179471
179786
|
signal: controller.signal,
|
|
179472
179787
|
timeoutMs: 2000
|
|
179473
179788
|
});
|
|
@@ -179496,7 +179811,7 @@ async function runLivenessCheck(args, note, projectRoot, assertLeaseHeld, leaseH
|
|
|
179496
179811
|
clearTimeout(timer);
|
|
179497
179812
|
}
|
|
179498
179813
|
}
|
|
179499
|
-
async function confirmReadOnly(args, noteId, content, surfaceCondition) {
|
|
179814
|
+
async function confirmReadOnly(args, noteId, content, surfaceCondition, leaseSignal) {
|
|
179500
179815
|
let childSessionId = null;
|
|
179501
179816
|
const startedAt = Date.now();
|
|
179502
179817
|
let invocationRecorded = false;
|
|
@@ -179539,42 +179854,49 @@ Note content: ${JSON.stringify(content)}
|
|
|
179539
179854
|
Surface condition: ${JSON.stringify(surfaceCondition ?? "")}
|
|
179540
179855
|
|
|
179541
179856
|
Output exactly JSON: {"met": false}`;
|
|
179542
|
-
const
|
|
179543
|
-
|
|
179544
|
-
|
|
179545
|
-
|
|
179546
|
-
|
|
179547
|
-
|
|
179548
|
-
|
|
179549
|
-
|
|
179550
|
-
|
|
179551
|
-
|
|
179552
|
-
|
|
179553
|
-
|
|
179554
|
-
|
|
179555
|
-
|
|
179556
|
-
|
|
179557
|
-
|
|
179558
|
-
|
|
179559
|
-
|
|
179560
|
-
|
|
179561
|
-
|
|
179562
|
-
|
|
179563
|
-
|
|
179564
|
-
|
|
179565
|
-
|
|
179566
|
-
|
|
179567
|
-
|
|
179568
|
-
|
|
179569
|
-
|
|
179570
|
-
|
|
179571
|
-
|
|
179572
|
-
|
|
179573
|
-
|
|
179574
|
-
|
|
179575
|
-
|
|
179576
|
-
|
|
179577
|
-
|
|
179857
|
+
const promptSignal = createPromptAbortSignal(leaseSignal, Math.max(1000, args.deadline - Date.now()), "smart-note confirmation deadline");
|
|
179858
|
+
let run;
|
|
179859
|
+
try {
|
|
179860
|
+
run = await promptSyncWithValidatedOutputRetry(args.client, {
|
|
179861
|
+
path: { id: childSessionId },
|
|
179862
|
+
query: { directory: args.sessionDirectory ?? args.projectIdentity },
|
|
179863
|
+
body: {
|
|
179864
|
+
agent: SMART_NOTE_COMPILER_AGENT,
|
|
179865
|
+
system: SMART_NOTE_CONFIRMATION_SYSTEM_PROMPT,
|
|
179866
|
+
...modelBodyField(args.model),
|
|
179867
|
+
parts: [{ type: "text", text: prompt, synthetic: true }]
|
|
179868
|
+
}
|
|
179869
|
+
}, {
|
|
179870
|
+
timeoutMs: Math.max(1000, args.deadline - Date.now()),
|
|
179871
|
+
signal: promptSignal.signal,
|
|
179872
|
+
fallbackModels: args.fallbackModels,
|
|
179873
|
+
callContext: "dreamer:smart-note-read-only-confirm",
|
|
179874
|
+
fetchOutput: async () => {
|
|
179875
|
+
const messagesResponse = await args.client.session.messages({
|
|
179876
|
+
path: { id: childSessionId },
|
|
179877
|
+
query: {
|
|
179878
|
+
directory: args.sessionDirectory ?? args.projectIdentity,
|
|
179879
|
+
limit: 20
|
|
179880
|
+
}
|
|
179881
|
+
});
|
|
179882
|
+
return normalizeSDKResponse(messagesResponse, [], {
|
|
179883
|
+
preferResponseOnMissingData: true
|
|
179884
|
+
});
|
|
179885
|
+
},
|
|
179886
|
+
validateOutput: (messages) => {
|
|
179887
|
+
const text = extractLatestAssistantText(messages) ?? "";
|
|
179888
|
+
const match = text.match(/\{[\s\S]*\}/);
|
|
179889
|
+
if (!match)
|
|
179890
|
+
throw new Error("confirmation evaluator returned no JSON");
|
|
179891
|
+
const parsed = JSON.parse(match[0]);
|
|
179892
|
+
if (typeof parsed.met !== "boolean")
|
|
179893
|
+
throw new Error("confirmation met missing");
|
|
179894
|
+
return parsed.met;
|
|
179895
|
+
}
|
|
179896
|
+
});
|
|
179897
|
+
} finally {
|
|
179898
|
+
promptSignal.cleanup();
|
|
179899
|
+
}
|
|
179578
179900
|
recordInvocation2({ status: "completed", messages: run.output });
|
|
179579
179901
|
return run.validated;
|
|
179580
179902
|
} catch (error51) {
|
|
@@ -179789,19 +180111,21 @@ ${list}
|
|
|
179789
180111
|
}
|
|
179790
180112
|
function parseMapMemoriesManifest(text) {
|
|
179791
180113
|
const out = [];
|
|
179792
|
-
|
|
180114
|
+
const body = extractCompleteManifestBody(text, "mappings");
|
|
180115
|
+
for (const m of body.matchAll(/<memory\b([^>]*)\/?>/g)) {
|
|
179793
180116
|
const attrs = m[1];
|
|
179794
180117
|
const idMatch = attrs.match(/\bid\s*=\s*"(\d+)"/);
|
|
179795
180118
|
if (!idMatch)
|
|
179796
|
-
|
|
180119
|
+
throw new Error("mappings manifest entry missing numeric id");
|
|
179797
180120
|
const id = Number.parseInt(idMatch[1], 10);
|
|
179798
180121
|
if (!Number.isInteger(id))
|
|
179799
|
-
|
|
180122
|
+
throw new Error("mappings manifest entry missing numeric id");
|
|
179800
180123
|
const independent = /\bindependent\s*=\s*"(?:true|1)"/i.test(attrs);
|
|
179801
180124
|
const filesMatch = attrs.match(/\bfiles\s*=\s*"([^"]*)"/);
|
|
179802
180125
|
const files = filesMatch ? filesMatch[1].split(",").map((f) => f.trim()).filter(Boolean) : [];
|
|
179803
180126
|
out.push({ id, files, independent: independent || files.length === 0 });
|
|
179804
180127
|
}
|
|
180128
|
+
assertNoDuplicateManifestIds(out.map((entry) => entry.id), "mappings");
|
|
179805
180129
|
return out;
|
|
179806
180130
|
}
|
|
179807
180131
|
|
|
@@ -179890,9 +180214,13 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
179890
180214
|
});
|
|
179891
180215
|
},
|
|
179892
180216
|
validateOutput: (messages) => {
|
|
180217
|
+
if (hasLengthCappedOutput(messages)) {
|
|
180218
|
+
throw new Error("map-memories returned length-capped output");
|
|
180219
|
+
}
|
|
179893
180220
|
const text = extractLatestAssistantText(messages);
|
|
179894
180221
|
if (!text)
|
|
179895
180222
|
throw new Error("map-memories returned no output");
|
|
180223
|
+
parseMapMemoriesManifest(text);
|
|
179896
180224
|
return text;
|
|
179897
180225
|
}
|
|
179898
180226
|
});
|
|
@@ -179918,7 +180246,8 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
179918
180246
|
}
|
|
179919
180247
|
async function applyBatchMappings(args, batch, manifestText) {
|
|
179920
180248
|
const batchIds = new Set(batch.map((m) => m.id));
|
|
179921
|
-
const parsed = parseMapMemoriesManifest(manifestText)
|
|
180249
|
+
const parsed = parseMapMemoriesManifest(manifestText);
|
|
180250
|
+
assertManifestCoversExactly(parsed.map((entry) => entry.id), batchIds, "mappings");
|
|
179922
180251
|
if (parsed.length === 0)
|
|
179923
180252
|
return { mapped: 0, independent: 0 };
|
|
179924
180253
|
const planned = [];
|
|
@@ -179940,12 +180269,7 @@ async function applyBatchMappings(args, batch, manifestText) {
|
|
|
179940
180269
|
const now = Date.now();
|
|
179941
180270
|
let mapped = 0;
|
|
179942
180271
|
let independent = 0;
|
|
179943
|
-
|
|
179944
|
-
args.db.transaction(() => {
|
|
179945
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
179946
|
-
leaseLost = true;
|
|
179947
|
-
return;
|
|
179948
|
-
}
|
|
180272
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
179949
180273
|
for (const item of planned) {
|
|
179950
180274
|
recordMemoryMapping(args.db, item.id, item.files, now);
|
|
179951
180275
|
if (item.independent)
|
|
@@ -179953,9 +180277,7 @@ async function applyBatchMappings(args, batch, manifestText) {
|
|
|
179953
180277
|
else
|
|
179954
180278
|
mapped += 1;
|
|
179955
180279
|
}
|
|
179956
|
-
})
|
|
179957
|
-
if (leaseLost)
|
|
179958
|
-
throw new Error("Dream lease lost during map-memories commit");
|
|
180280
|
+
});
|
|
179959
180281
|
return { mapped, independent };
|
|
179960
180282
|
}
|
|
179961
180283
|
function recordInvocation2(args, startedAt, params) {
|
|
@@ -180183,11 +180505,7 @@ async function promotePrimers(args) {
|
|
|
180183
180505
|
activePrimers: primers,
|
|
180184
180506
|
threshold: PRIMER_CLUSTER_THRESHOLD
|
|
180185
180507
|
});
|
|
180186
|
-
args.db.
|
|
180187
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
180188
|
-
leaseLost = true;
|
|
180189
|
-
return;
|
|
180190
|
-
}
|
|
180508
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
180191
180509
|
for (const cluster of clusters) {
|
|
180192
180510
|
if (cluster.candidates.length === 0)
|
|
180193
180511
|
continue;
|
|
@@ -180218,7 +180536,7 @@ async function promotePrimers(args) {
|
|
|
180218
180536
|
});
|
|
180219
180537
|
result.promoted += 1;
|
|
180220
180538
|
}
|
|
180221
|
-
})
|
|
180539
|
+
});
|
|
180222
180540
|
if (leaseLost)
|
|
180223
180541
|
throw new Error("Dream lease lost during promote-primers commit");
|
|
180224
180542
|
log(`[dreamer] primers: candidates=${result.candidates} promoted=${result.promoted} updated=${result.updated}`);
|
|
@@ -180486,16 +180804,9 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
|
|
|
180486
180804
|
log(`[dreamer] refresh-primers: primer #${primer.id} answer not committed (no investigation tool calls)`);
|
|
180487
180805
|
return false;
|
|
180488
180806
|
}
|
|
180489
|
-
|
|
180490
|
-
args.db.transaction(() => {
|
|
180491
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
180492
|
-
leaseLost = true;
|
|
180493
|
-
return;
|
|
180494
|
-
}
|
|
180807
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
180495
180808
|
updatePrimerAnswer(args.db, primer.id, answer);
|
|
180496
|
-
})
|
|
180497
|
-
if (leaseLost)
|
|
180498
|
-
throw new Error("Dream lease lost during refresh-primers commit");
|
|
180809
|
+
});
|
|
180499
180810
|
return true;
|
|
180500
180811
|
} catch (error51) {
|
|
180501
180812
|
const desc = describeError(error51);
|
|
@@ -180692,8 +181003,7 @@ class OpenCodeRetrospectiveRawProvider {
|
|
|
180692
181003
|
LEFT JOIN session_meta m ON m.session_id = sp.session_id
|
|
180693
181004
|
WHERE sp.project_path = ? AND sp.harness = 'opencode'
|
|
180694
181005
|
AND COALESCE(m.is_subagent, 0) = 0
|
|
180695
|
-
ORDER BY sp.updated_at
|
|
180696
|
-
LIMIT ?`).all(projectIdentity, RETROSPECTIVE_MAX_SESSIONS_PER_RUN);
|
|
181006
|
+
ORDER BY sp.updated_at ASC, sp.session_id ASC`).all(projectIdentity);
|
|
180697
181007
|
return rows.map((row) => ({
|
|
180698
181008
|
sessionId: row.session_id,
|
|
180699
181009
|
updatedAt: typeof row.updated_at === "number" ? row.updated_at : undefined
|
|
@@ -180718,6 +181028,12 @@ class OpenCodeRetrospectiveRawProvider {
|
|
|
180718
181028
|
return { messages: [], truncated: false };
|
|
180719
181029
|
}
|
|
180720
181030
|
}
|
|
181031
|
+
readOldestMessageTimesSince(sessionIds, sinceMs) {
|
|
181032
|
+
const db = this.resolveDb();
|
|
181033
|
+
if (!db || sessionIds.length === 0)
|
|
181034
|
+
return new Map;
|
|
181035
|
+
return readOpenCodeOldestMessageTimesSince(db, sessionIds, sinceMs);
|
|
181036
|
+
}
|
|
180721
181037
|
readUserMessagesBefore(sessionId, beforeMs, count) {
|
|
180722
181038
|
const db = this.resolveDb();
|
|
180723
181039
|
if (!db)
|
|
@@ -180739,13 +181055,28 @@ class OpenCodeRetrospectiveRawProvider {
|
|
|
180739
181055
|
async function readRetrospectiveScanWindow(provider2, projectIdentity, watermarkMs, overlapUserCount, options) {
|
|
180740
181056
|
const maxMessages = options?.maxMessagesPerRun ?? RETROSPECTIVE_MAX_MESSAGES_PER_RUN;
|
|
180741
181057
|
const capPerSession = options?.capPerSession ?? RETROSPECTIVE_MAX_MESSAGES_PER_SESSION;
|
|
180742
|
-
const
|
|
181058
|
+
const sessionLimit = Math.max(1, Math.floor(options?.maxSessionsPerRun ?? RETROSPECTIVE_MAX_SESSIONS_PER_RUN));
|
|
180743
181059
|
try {
|
|
180744
|
-
const
|
|
181060
|
+
const allSessions = await provider2.listProjectSessions(projectIdentity);
|
|
181061
|
+
const eligibleSessions = allSessions.map((session, index) => ({ session, index })).filter(({ session }) => (session.updatedAt ?? Number.POSITIVE_INFINITY) > watermarkMs);
|
|
181062
|
+
const oldestBySession = provider2.readOldestMessageTimesSince ? await provider2.readOldestMessageTimesSince(eligibleSessions.map(({ session }) => session.sessionId), watermarkMs) : null;
|
|
181063
|
+
const sessions = (oldestBySession ? eligibleSessions.filter(({ session }) => oldestBySession.has(session.sessionId)) : eligibleSessions).sort((a, b) => {
|
|
181064
|
+
const aFrontier = oldestBySession?.get(a.session.sessionId);
|
|
181065
|
+
const bFrontier = oldestBySession?.get(b.session.sessionId);
|
|
181066
|
+
if (aFrontier !== undefined || bFrontier !== undefined) {
|
|
181067
|
+
return (aFrontier ?? Number.POSITIVE_INFINITY) - (bFrontier ?? Number.POSITIVE_INFINITY) || a.index - b.index;
|
|
181068
|
+
}
|
|
181069
|
+
const aUpdated = a.session.updatedAt ?? Number.POSITIVE_INFINITY;
|
|
181070
|
+
const bUpdated = b.session.updatedAt ?? Number.POSITIVE_INFINITY;
|
|
181071
|
+
const byUpdated = aUpdated - bUpdated;
|
|
181072
|
+
return byUpdated || a.index - b.index;
|
|
181073
|
+
});
|
|
181074
|
+
const sessionsToRead = sessions.slice(0, sessionLimit).map(({ session }) => session);
|
|
181075
|
+
const firstExcludedSession = sessions[sessionLimit]?.session;
|
|
181076
|
+
const firstExcludedPendingTs = firstExcludedSession ? oldestBySession?.get(firstExcludedSession.sessionId) : undefined;
|
|
180745
181077
|
const sinceReads = [];
|
|
180746
|
-
|
|
180747
|
-
|
|
180748
|
-
sinceReads.push(...await Promise.all(page.map((session) => provider2.readUserMessagesSince(session.sessionId, watermarkMs, capPerSession))));
|
|
181078
|
+
if (sessionsToRead.length > 0) {
|
|
181079
|
+
sinceReads.push(...await Promise.all(sessionsToRead.map((session) => provider2.readUserMessagesSince(session.sessionId, watermarkMs, capPerSession))));
|
|
180749
181080
|
}
|
|
180750
181081
|
let saturatedFrontier = Number.POSITIVE_INFINITY;
|
|
180751
181082
|
for (const read of sinceReads) {
|
|
@@ -180767,9 +181098,14 @@ async function readRetrospectiveScanWindow(provider2, projectIdentity, watermark
|
|
|
180767
181098
|
if (firstDropped) {
|
|
180768
181099
|
frontier = Math.min(frontier, firstDropped.ts - 1);
|
|
180769
181100
|
}
|
|
181101
|
+
if (typeof firstExcludedPendingTs === "number") {
|
|
181102
|
+
frontier = Math.min(frontier, firstExcludedPendingTs - 1);
|
|
181103
|
+
} else if (typeof firstExcludedSession?.updatedAt === "number") {
|
|
181104
|
+
frontier = Math.min(frontier, firstExcludedSession.updatedAt - 1);
|
|
181105
|
+
}
|
|
180770
181106
|
maxScannedTs = Math.max(watermarkMs, Math.min(maxScannedTs, frontier));
|
|
180771
181107
|
const keptSessionIds = new Set(keptSince.map((message) => message.sessionId));
|
|
180772
|
-
const overlapSessions =
|
|
181108
|
+
const overlapSessions = sessionsToRead.filter((session) => keptSessionIds.has(session.sessionId));
|
|
180773
181109
|
const overlapBatches = overlapUserCount > 0 && watermarkMs > 0 ? await Promise.all(overlapSessions.map((session) => provider2.readUserMessagesBefore(session.sessionId, watermarkMs, overlapUserCount))) : [];
|
|
180774
181110
|
const seen = new Set;
|
|
180775
181111
|
const merged = [];
|
|
@@ -180797,6 +181133,24 @@ function readOpenCodeMessagesSince(db, sessionId, sinceMs, capPerSession) {
|
|
|
180797
181133
|
const kept = truncated ? rows.slice(0, limit) : rows;
|
|
180798
181134
|
return { messages: normalizeOpenCodeRows(db, sessionId, kept), truncated };
|
|
180799
181135
|
}
|
|
181136
|
+
function readOpenCodeOldestMessageTimesSince(db, sessionIds, sinceMs) {
|
|
181137
|
+
const result = new Map;
|
|
181138
|
+
const uniqueIds = Array.from(new Set(sessionIds.filter((id) => id.length > 0)));
|
|
181139
|
+
const chunkSize = 500;
|
|
181140
|
+
for (let i = 0;i < uniqueIds.length; i += chunkSize) {
|
|
181141
|
+
const chunk = uniqueIds.slice(i, i + chunkSize);
|
|
181142
|
+
const placeholders4 = chunk.map(() => "?").join(", ");
|
|
181143
|
+
const rows = db.prepare(`SELECT session_id, MIN(time_created) AS oldest_ts
|
|
181144
|
+
FROM message
|
|
181145
|
+
WHERE time_created > ? AND session_id IN (${placeholders4})
|
|
181146
|
+
GROUP BY session_id`).all(sinceMs, ...chunk);
|
|
181147
|
+
for (const row of rows) {
|
|
181148
|
+
if (typeof row.oldest_ts === "number")
|
|
181149
|
+
result.set(row.session_id, row.oldest_ts);
|
|
181150
|
+
}
|
|
181151
|
+
}
|
|
181152
|
+
return result;
|
|
181153
|
+
}
|
|
180800
181154
|
function readOpenCodeUserMessagesBefore(db, sessionId, beforeMs, count) {
|
|
180801
181155
|
const want = Math.max(1, Math.floor(count));
|
|
180802
181156
|
const window = Math.max(want * 5, 32);
|
|
@@ -181089,7 +181443,7 @@ async function partitionVerifyScope(args) {
|
|
|
181089
181443
|
continue;
|
|
181090
181444
|
}
|
|
181091
181445
|
const files = v?.files ?? [];
|
|
181092
|
-
const needs = files.some((file2) => !verificationFileExists(gitRoot, file2) || uncommitted.has(file2) || (changeTimes.get(file2) ?? 0)
|
|
181446
|
+
const needs = files.some((file2) => !verificationFileExists(gitRoot, file2) || uncommitted.has(file2) || (changeTimes.get(file2) ?? 0) >= verifiedAt - 1000);
|
|
181093
181447
|
if (needs)
|
|
181094
181448
|
inScope.push(toPrompt(m));
|
|
181095
181449
|
else
|
|
@@ -181155,22 +181509,26 @@ function filesOf(s) {
|
|
|
181155
181509
|
}
|
|
181156
181510
|
function parseVerifyManifest(text) {
|
|
181157
181511
|
const out = { verified: [], updated: [], archived: [] };
|
|
181158
|
-
|
|
181512
|
+
const body = extractCompleteManifestBody(text, "verify");
|
|
181513
|
+
for (const m of body.matchAll(/<verified\b([^>]*)\/?>/g)) {
|
|
181159
181514
|
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
181160
|
-
if (Number.isInteger(id))
|
|
181161
|
-
|
|
181515
|
+
if (!Number.isInteger(id))
|
|
181516
|
+
throw new Error("verify manifest entry missing numeric id");
|
|
181517
|
+
out.verified.push({ id, files: filesOf(m[1]) });
|
|
181162
181518
|
}
|
|
181163
|
-
for (const m of
|
|
181519
|
+
for (const m of body.matchAll(/<update\b([^>]*?)(?:\/>|>([\s\S]*?)<\/update>)/g)) {
|
|
181164
181520
|
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
181165
|
-
if (Number.isInteger(id))
|
|
181166
|
-
|
|
181167
|
-
}
|
|
181521
|
+
if (!Number.isInteger(id))
|
|
181522
|
+
throw new Error("verify manifest entry missing numeric id");
|
|
181523
|
+
out.updated.push({ id, files: filesOf(m[1]), content: (m[2] ?? "").trim() });
|
|
181168
181524
|
}
|
|
181169
|
-
for (const m of
|
|
181525
|
+
for (const m of body.matchAll(/<archive\b([^>]*)\/?>/g)) {
|
|
181170
181526
|
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
181171
|
-
if (Number.isInteger(id))
|
|
181172
|
-
|
|
181527
|
+
if (!Number.isInteger(id))
|
|
181528
|
+
throw new Error("verify manifest entry missing numeric id");
|
|
181529
|
+
out.archived.push({ id, reason: attrOf(m[1], "reason") ?? "" });
|
|
181173
181530
|
}
|
|
181531
|
+
assertNoDuplicateManifestIds([...out.verified, ...out.updated, ...out.archived].map((entry) => entry.id), "verify");
|
|
181174
181532
|
return out;
|
|
181175
181533
|
}
|
|
181176
181534
|
|
|
@@ -181263,9 +181621,13 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
181263
181621
|
});
|
|
181264
181622
|
},
|
|
181265
181623
|
validateOutput: (messages) => {
|
|
181624
|
+
if (hasLengthCappedOutput(messages)) {
|
|
181625
|
+
throw new Error("verify returned length-capped output");
|
|
181626
|
+
}
|
|
181266
181627
|
const text = extractLatestAssistantText(messages);
|
|
181267
181628
|
if (!text)
|
|
181268
181629
|
throw new Error("verify returned no output");
|
|
181630
|
+
parseVerifyManifest(text);
|
|
181269
181631
|
return text;
|
|
181270
181632
|
}
|
|
181271
181633
|
});
|
|
@@ -181292,24 +181654,14 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
181292
181654
|
async function applyVerifyManifest(args, batch, manifestText) {
|
|
181293
181655
|
const batchIds = new Set(batch.map((m) => m.id));
|
|
181294
181656
|
const parsed = parseVerifyManifest(manifestText);
|
|
181657
|
+
assertManifestCoversExactly([...parsed.verified, ...parsed.updated, ...parsed.archived].map((entry) => entry.id), batchIds, "verify");
|
|
181295
181658
|
const now = Date.now();
|
|
181296
181659
|
const writes = [];
|
|
181297
|
-
const verdictCounts = new Map;
|
|
181298
|
-
for (const verdict of [...parsed.verified, ...parsed.updated, ...parsed.archived]) {
|
|
181299
|
-
if (!batchIds.has(verdict.id))
|
|
181300
|
-
continue;
|
|
181301
|
-
verdictCounts.set(verdict.id, (verdictCounts.get(verdict.id) ?? 0) + 1);
|
|
181302
|
-
}
|
|
181303
|
-
const conflictingIds = new Set(Array.from(verdictCounts.entries()).filter(([, count]) => count !== 1).map(([id]) => id));
|
|
181304
181660
|
for (const v of parsed.verified) {
|
|
181305
|
-
if (!batchIds.has(v.id) || conflictingIds.has(v.id))
|
|
181306
|
-
continue;
|
|
181307
181661
|
const files = await normalizeFiles(args, v.files);
|
|
181308
181662
|
writes.push({ kind: "verify", id: v.id, files });
|
|
181309
181663
|
}
|
|
181310
181664
|
for (const u of parsed.updated) {
|
|
181311
|
-
if (!batchIds.has(u.id) || conflictingIds.has(u.id))
|
|
181312
|
-
continue;
|
|
181313
181665
|
const content = u.content.trim();
|
|
181314
181666
|
if (!content || content.length > 20000) {
|
|
181315
181667
|
const files2 = await normalizeFiles(args, u.files);
|
|
@@ -181326,8 +181678,6 @@ async function applyVerifyManifest(args, batch, manifestText) {
|
|
|
181326
181678
|
});
|
|
181327
181679
|
}
|
|
181328
181680
|
for (const a of parsed.archived) {
|
|
181329
|
-
if (!batchIds.has(a.id) || conflictingIds.has(a.id))
|
|
181330
|
-
continue;
|
|
181331
181681
|
writes.push({ kind: "archive", id: a.id, reason: a.reason });
|
|
181332
181682
|
}
|
|
181333
181683
|
if (writes.length === 0)
|
|
@@ -181335,12 +181685,7 @@ async function applyVerifyManifest(args, batch, manifestText) {
|
|
|
181335
181685
|
let verified = 0;
|
|
181336
181686
|
let updated = 0;
|
|
181337
181687
|
let archived = 0;
|
|
181338
|
-
|
|
181339
|
-
args.db.transaction(() => {
|
|
181340
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
181341
|
-
leaseLost = true;
|
|
181342
|
-
return;
|
|
181343
|
-
}
|
|
181688
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
181344
181689
|
for (const w of writes) {
|
|
181345
181690
|
const memory = getMemoryById(args.db, w.id);
|
|
181346
181691
|
if (!isPrimaryMutable(memory))
|
|
@@ -181368,9 +181713,7 @@ async function applyVerifyManifest(args, batch, manifestText) {
|
|
|
181368
181713
|
archived += 1;
|
|
181369
181714
|
}
|
|
181370
181715
|
}
|
|
181371
|
-
})
|
|
181372
|
-
if (leaseLost)
|
|
181373
|
-
throw new Error("Dream lease lost during verify commit");
|
|
181716
|
+
});
|
|
181374
181717
|
return { verified, updated, archived };
|
|
181375
181718
|
}
|
|
181376
181719
|
async function normalizeFiles(args, rawFiles) {
|
|
@@ -181864,11 +182207,7 @@ async function runRetrospectiveTask(config2, ctx, helpers) {
|
|
|
181864
182207
|
throw new Error("Dream lease lost during retrospective");
|
|
181865
182208
|
const sourceSessionId = flagged[0]?.sessionId ?? userMessages[0]?.sessionId ?? "retrospective";
|
|
181866
182209
|
const learnings = parseRetrospectiveLearnings(deepenRun.validated);
|
|
181867
|
-
const applied = db
|
|
181868
|
-
if (!peekLeaseHolderAndExpiry(db, holderId, leaseKey)) {
|
|
181869
|
-
leaseLost = true;
|
|
181870
|
-
return null;
|
|
181871
|
-
}
|
|
182210
|
+
const applied = runLeaseGuardedWrite(db, holderId, leaseKey, () => {
|
|
181872
182211
|
const result = applyRetrospectiveLearnings({
|
|
181873
182212
|
db,
|
|
181874
182213
|
projectIdentity,
|
|
@@ -181879,7 +182218,7 @@ async function runRetrospectiveTask(config2, ctx, helpers) {
|
|
|
181879
182218
|
});
|
|
181880
182219
|
recordRetrospectiveWindowProcessed(db, projectIdentity, windowKey);
|
|
181881
182220
|
return result;
|
|
181882
|
-
})
|
|
182221
|
+
});
|
|
181883
182222
|
if (leaseLost || !applied)
|
|
181884
182223
|
throw new Error("Dream lease lost during retrospective commit");
|
|
181885
182224
|
log(`[dreamer] retrospective: flagged=${flagged.length} learnings=${learnings.length} memory=${applied.memoryWritten} observations=${applied.observationsInserted} dropped=${applied.observationsDropped} rejected=${applied.rejected.length}`);
|
|
@@ -183414,11 +183753,106 @@ function migrateLegacyExperimental(rawConfig, warnings) {
|
|
|
183414
183753
|
|
|
183415
183754
|
// ../plugin/src/config/project-security.ts
|
|
183416
183755
|
var HIDDEN_AGENT_KEYS = ["historian", "dreamer", "sidekick"];
|
|
183756
|
+
var HISTORIAN_USER_ONLY_FIELDS = ["model", "fallback_models"];
|
|
183417
183757
|
var AGENT_ESCALATION_FIELDS = ["prompt", "permission", "tools", "system_prompt"];
|
|
183418
183758
|
var EMBEDDING_DESTINATION_FIELDS = ["endpoint", "provider"];
|
|
183759
|
+
var PERCENTAGE_THRESHOLD_REASON = "security: a repository may only raise compaction thresholds above the user's effective value; it cannot force earlier historian work or cloned-repo cost escalation.";
|
|
183760
|
+
var TOKEN_THRESHOLD_REASON = "security: a repository may only raise execute_threshold_tokens above the user's trusted token threshold; it cannot force earlier historian work or cloned-repo cost escalation.";
|
|
183761
|
+
var TOKEN_THRESHOLD_INTRODUCTION_REASON = "security: a repository cannot introduce a new execute_threshold_tokens override when the user has no trusted token threshold for that key; that could force earlier historian work or cloned-repo cost escalation.";
|
|
183419
183762
|
function isPlainObject3(value) {
|
|
183420
183763
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
183421
183764
|
}
|
|
183765
|
+
function isValidPercentageThreshold(value) {
|
|
183766
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 20 && value <= 80;
|
|
183767
|
+
}
|
|
183768
|
+
function isValidTokenThreshold(value) {
|
|
183769
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 5000 && value <= 2000000;
|
|
183770
|
+
}
|
|
183771
|
+
function normalizeTrustedPercentageThresholds(value) {
|
|
183772
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
183773
|
+
return { defaultValue: value, overrides: new Map };
|
|
183774
|
+
}
|
|
183775
|
+
if (isPlainObject3(value) && typeof value.default === "number" && Number.isFinite(value.default)) {
|
|
183776
|
+
const overrides = new Map;
|
|
183777
|
+
for (const [key, child] of Object.entries(value)) {
|
|
183778
|
+
if (key === "default")
|
|
183779
|
+
continue;
|
|
183780
|
+
if (typeof child === "number" && Number.isFinite(child)) {
|
|
183781
|
+
overrides.set(key, child);
|
|
183782
|
+
}
|
|
183783
|
+
}
|
|
183784
|
+
return { defaultValue: value.default, overrides };
|
|
183785
|
+
}
|
|
183786
|
+
return { defaultValue: DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, overrides: new Map };
|
|
183787
|
+
}
|
|
183788
|
+
function normalizeTrustedTokenThresholds(value) {
|
|
183789
|
+
if (!isPlainObject3(value)) {
|
|
183790
|
+
return { defaultValue: undefined, overrides: new Map };
|
|
183791
|
+
}
|
|
183792
|
+
const overrides = new Map;
|
|
183793
|
+
for (const [key, child] of Object.entries(value)) {
|
|
183794
|
+
if (key === "default")
|
|
183795
|
+
continue;
|
|
183796
|
+
if (typeof child === "number" && Number.isFinite(child)) {
|
|
183797
|
+
overrides.set(key, child);
|
|
183798
|
+
}
|
|
183799
|
+
}
|
|
183800
|
+
return {
|
|
183801
|
+
defaultValue: typeof value.default === "number" && Number.isFinite(value.default) ? value.default : undefined,
|
|
183802
|
+
overrides
|
|
183803
|
+
};
|
|
183804
|
+
}
|
|
183805
|
+
function clonePercentageThresholds(value) {
|
|
183806
|
+
return {
|
|
183807
|
+
defaultValue: value.defaultValue,
|
|
183808
|
+
overrides: new Map(value.overrides)
|
|
183809
|
+
};
|
|
183810
|
+
}
|
|
183811
|
+
function cloneTokenThresholds(value) {
|
|
183812
|
+
return {
|
|
183813
|
+
defaultValue: value.defaultValue,
|
|
183814
|
+
overrides: new Map(value.overrides)
|
|
183815
|
+
};
|
|
183816
|
+
}
|
|
183817
|
+
function percentageThresholdsEqual(left, right) {
|
|
183818
|
+
if (left.defaultValue !== right.defaultValue)
|
|
183819
|
+
return false;
|
|
183820
|
+
if (left.overrides.size !== right.overrides.size)
|
|
183821
|
+
return false;
|
|
183822
|
+
for (const [key, value] of left.overrides) {
|
|
183823
|
+
if (right.overrides.get(key) !== value)
|
|
183824
|
+
return false;
|
|
183825
|
+
}
|
|
183826
|
+
return true;
|
|
183827
|
+
}
|
|
183828
|
+
function setMergedPercentageThreshold(mergedRaw, value) {
|
|
183829
|
+
if (value.overrides.size === 0) {
|
|
183830
|
+
mergedRaw.execute_threshold_percentage = value.defaultValue;
|
|
183831
|
+
return;
|
|
183832
|
+
}
|
|
183833
|
+
const serialized = { default: value.defaultValue };
|
|
183834
|
+
for (const [key, threshold] of value.overrides) {
|
|
183835
|
+
serialized[key] = threshold;
|
|
183836
|
+
}
|
|
183837
|
+
mergedRaw.execute_threshold_percentage = serialized;
|
|
183838
|
+
}
|
|
183839
|
+
function setMergedTokenThreshold(mergedRaw, value) {
|
|
183840
|
+
if (value.defaultValue === undefined && value.overrides.size === 0) {
|
|
183841
|
+
delete mergedRaw.execute_threshold_tokens;
|
|
183842
|
+
return;
|
|
183843
|
+
}
|
|
183844
|
+
const serialized = {};
|
|
183845
|
+
if (value.defaultValue !== undefined) {
|
|
183846
|
+
serialized.default = value.defaultValue;
|
|
183847
|
+
}
|
|
183848
|
+
for (const [key, threshold] of value.overrides) {
|
|
183849
|
+
serialized[key] = threshold;
|
|
183850
|
+
}
|
|
183851
|
+
mergedRaw.execute_threshold_tokens = serialized;
|
|
183852
|
+
}
|
|
183853
|
+
function makeProjectThresholdWarning(field, reason) {
|
|
183854
|
+
return `Ignoring ${field} from project config (${reason})`;
|
|
183855
|
+
}
|
|
183422
183856
|
function stripUnsafeProjectConfigFields(projectRaw) {
|
|
183423
183857
|
const warnings = [];
|
|
183424
183858
|
if ("auto_update" in projectRaw) {
|
|
@@ -183446,6 +183880,19 @@ function stripUnsafeProjectConfigFields(projectRaw) {
|
|
|
183446
183880
|
warnings.push(`Ignoring embedding.${removed.join("/")} from project config ` + "(security: a repository cannot choose where private text is embedded).");
|
|
183447
183881
|
}
|
|
183448
183882
|
}
|
|
183883
|
+
const historian = projectRaw.historian;
|
|
183884
|
+
if (isPlainObject3(historian)) {
|
|
183885
|
+
const removed = [];
|
|
183886
|
+
for (const field of HISTORIAN_USER_ONLY_FIELDS) {
|
|
183887
|
+
if (field in historian) {
|
|
183888
|
+
delete historian[field];
|
|
183889
|
+
removed.push(field);
|
|
183890
|
+
}
|
|
183891
|
+
}
|
|
183892
|
+
if (removed.length > 0) {
|
|
183893
|
+
warnings.push(`Ignoring historian.${removed.join("/")} from project config ` + "(security: historian model selection is user-level only; a repository cannot force extra compaction cost).");
|
|
183894
|
+
}
|
|
183895
|
+
}
|
|
183449
183896
|
for (const agentKey of HIDDEN_AGENT_KEYS) {
|
|
183450
183897
|
const block = projectRaw[agentKey];
|
|
183451
183898
|
if (!isPlainObject3(block))
|
|
@@ -183463,6 +183910,101 @@ function stripUnsafeProjectConfigFields(projectRaw) {
|
|
|
183463
183910
|
}
|
|
183464
183911
|
return warnings;
|
|
183465
183912
|
}
|
|
183913
|
+
function constrainProjectThresholdOverrides(args) {
|
|
183914
|
+
const warnings = [];
|
|
183915
|
+
const basePercentage = normalizeTrustedPercentageThresholds(args.trustedBaseConfig.execute_threshold_percentage);
|
|
183916
|
+
const baseTokens = normalizeTrustedTokenThresholds(args.trustedBaseConfig.execute_threshold_tokens);
|
|
183917
|
+
if ("execute_threshold_percentage" in args.projectRaw) {
|
|
183918
|
+
const projectValue = args.projectRaw.execute_threshold_percentage;
|
|
183919
|
+
if (isValidPercentageThreshold(projectValue)) {
|
|
183920
|
+
const constrained = clonePercentageThresholds(basePercentage);
|
|
183921
|
+
constrained.defaultValue = Math.max(basePercentage.defaultValue, projectValue);
|
|
183922
|
+
for (const [modelKey, threshold] of basePercentage.overrides) {
|
|
183923
|
+
const raisedThreshold = Math.max(threshold, projectValue);
|
|
183924
|
+
if (raisedThreshold === constrained.defaultValue) {
|
|
183925
|
+
constrained.overrides.delete(modelKey);
|
|
183926
|
+
} else {
|
|
183927
|
+
constrained.overrides.set(modelKey, raisedThreshold);
|
|
183928
|
+
}
|
|
183929
|
+
}
|
|
183930
|
+
setMergedPercentageThreshold(args.mergedRaw, constrained);
|
|
183931
|
+
if (percentageThresholdsEqual(constrained, basePercentage)) {
|
|
183932
|
+
warnings.push(makeProjectThresholdWarning("execute_threshold_percentage", PERCENTAGE_THRESHOLD_REASON));
|
|
183933
|
+
}
|
|
183934
|
+
} else if (isPlainObject3(projectValue)) {
|
|
183935
|
+
const constrained = clonePercentageThresholds(basePercentage);
|
|
183936
|
+
let touchedValidEntry = false;
|
|
183937
|
+
if (isValidPercentageThreshold(projectValue.default)) {
|
|
183938
|
+
touchedValidEntry = true;
|
|
183939
|
+
if (projectValue.default > basePercentage.defaultValue) {
|
|
183940
|
+
constrained.defaultValue = projectValue.default;
|
|
183941
|
+
} else {
|
|
183942
|
+
warnings.push(makeProjectThresholdWarning("execute_threshold_percentage.default", PERCENTAGE_THRESHOLD_REASON));
|
|
183943
|
+
}
|
|
183944
|
+
}
|
|
183945
|
+
for (const [modelKey, rawValue] of Object.entries(projectValue)) {
|
|
183946
|
+
if (modelKey === "default")
|
|
183947
|
+
continue;
|
|
183948
|
+
if (!isValidPercentageThreshold(rawValue))
|
|
183949
|
+
continue;
|
|
183950
|
+
touchedValidEntry = true;
|
|
183951
|
+
const baseValue = basePercentage.overrides.get(modelKey) ?? basePercentage.defaultValue;
|
|
183952
|
+
if (rawValue > baseValue) {
|
|
183953
|
+
if (rawValue === constrained.defaultValue) {
|
|
183954
|
+
constrained.overrides.delete(modelKey);
|
|
183955
|
+
} else {
|
|
183956
|
+
constrained.overrides.set(modelKey, rawValue);
|
|
183957
|
+
}
|
|
183958
|
+
} else {
|
|
183959
|
+
warnings.push(makeProjectThresholdWarning(`execute_threshold_percentage.${modelKey}`, PERCENTAGE_THRESHOLD_REASON));
|
|
183960
|
+
}
|
|
183961
|
+
}
|
|
183962
|
+
if (touchedValidEntry) {
|
|
183963
|
+
setMergedPercentageThreshold(args.mergedRaw, constrained);
|
|
183964
|
+
}
|
|
183965
|
+
}
|
|
183966
|
+
}
|
|
183967
|
+
if ("execute_threshold_tokens" in args.projectRaw && isPlainObject3(args.projectRaw.execute_threshold_tokens)) {
|
|
183968
|
+
const projectValue = args.projectRaw.execute_threshold_tokens;
|
|
183969
|
+
const constrained = cloneTokenThresholds(baseTokens);
|
|
183970
|
+
let touchedValidEntry = false;
|
|
183971
|
+
if (isValidTokenThreshold(projectValue.default)) {
|
|
183972
|
+
touchedValidEntry = true;
|
|
183973
|
+
if (baseTokens.defaultValue === undefined) {
|
|
183974
|
+
warnings.push(makeProjectThresholdWarning("execute_threshold_tokens.default", TOKEN_THRESHOLD_INTRODUCTION_REASON));
|
|
183975
|
+
} else if (projectValue.default > baseTokens.defaultValue) {
|
|
183976
|
+
constrained.defaultValue = projectValue.default;
|
|
183977
|
+
} else {
|
|
183978
|
+
warnings.push(makeProjectThresholdWarning("execute_threshold_tokens.default", TOKEN_THRESHOLD_REASON));
|
|
183979
|
+
}
|
|
183980
|
+
}
|
|
183981
|
+
for (const [modelKey, rawValue] of Object.entries(projectValue)) {
|
|
183982
|
+
if (modelKey === "default")
|
|
183983
|
+
continue;
|
|
183984
|
+
if (!isValidTokenThreshold(rawValue))
|
|
183985
|
+
continue;
|
|
183986
|
+
touchedValidEntry = true;
|
|
183987
|
+
const baseValue = baseTokens.overrides.get(modelKey) ?? baseTokens.defaultValue;
|
|
183988
|
+
if (baseValue === undefined) {
|
|
183989
|
+
warnings.push(makeProjectThresholdWarning(`execute_threshold_tokens.${modelKey}`, TOKEN_THRESHOLD_INTRODUCTION_REASON));
|
|
183990
|
+
continue;
|
|
183991
|
+
}
|
|
183992
|
+
if (rawValue > baseValue) {
|
|
183993
|
+
if (rawValue === constrained.defaultValue) {
|
|
183994
|
+
constrained.overrides.delete(modelKey);
|
|
183995
|
+
} else {
|
|
183996
|
+
constrained.overrides.set(modelKey, rawValue);
|
|
183997
|
+
}
|
|
183998
|
+
} else {
|
|
183999
|
+
warnings.push(makeProjectThresholdWarning(`execute_threshold_tokens.${modelKey}`, TOKEN_THRESHOLD_REASON));
|
|
184000
|
+
}
|
|
184001
|
+
}
|
|
184002
|
+
if (touchedValidEntry) {
|
|
184003
|
+
setMergedTokenThreshold(args.mergedRaw, constrained);
|
|
184004
|
+
}
|
|
184005
|
+
}
|
|
184006
|
+
return warnings;
|
|
184007
|
+
}
|
|
183466
184008
|
function normalizeEndpoint3(value) {
|
|
183467
184009
|
if (typeof value !== "string")
|
|
183468
184010
|
return;
|
|
@@ -183830,6 +184372,7 @@ function loadPiConfig(opts = {}) {
|
|
|
183830
184372
|
return a.scope === "user" ? -1 : 1;
|
|
183831
184373
|
});
|
|
183832
184374
|
const userRaw = mergeFiles.find((f) => f.scope === "user")?.config;
|
|
184375
|
+
const trustedBaseConfig = parsePiConfig(userRaw ?? {}).config;
|
|
183833
184376
|
for (const loaded of mergeFiles) {
|
|
183834
184377
|
const prefix = loaded.scope === "user" ? "[user config]" : "[project config]";
|
|
183835
184378
|
warnings.push(...loaded.warnings.map((warning) => `${prefix} ${warning}`));
|
|
@@ -183842,6 +184385,13 @@ function loadPiConfig(opts = {}) {
|
|
|
183842
184385
|
for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, rawConfig, userRaw)) {
|
|
183843
184386
|
warnings.push(`${prefix} ${warning}`);
|
|
183844
184387
|
}
|
|
184388
|
+
for (const warning of constrainProjectThresholdOverrides({
|
|
184389
|
+
mergedRaw: rawConfig,
|
|
184390
|
+
projectRaw,
|
|
184391
|
+
trustedBaseConfig
|
|
184392
|
+
})) {
|
|
184393
|
+
warnings.push(`${prefix} ${warning}`);
|
|
184394
|
+
}
|
|
183845
184395
|
} else {
|
|
183846
184396
|
rawConfig = mergeRawConfigs(rawConfig, loaded.config);
|
|
183847
184397
|
}
|
|
@@ -183940,6 +184490,7 @@ function loadPiConfigDetailed(opts = {}) {
|
|
|
183940
184490
|
return a.scope === "user" ? -1 : 1;
|
|
183941
184491
|
});
|
|
183942
184492
|
const userRaw = mergeFiles.find((f) => f.scope === "user")?.config;
|
|
184493
|
+
const trustedBaseConfig = parsePiConfig(userRaw ?? {}).config;
|
|
183943
184494
|
for (const loaded of mergeFiles) {
|
|
183944
184495
|
const prefix = loaded.scope === "user" ? "[user config]" : "[project config]";
|
|
183945
184496
|
warnings.push(...loaded.warnings.map((warning) => `${prefix} ${warning}`));
|
|
@@ -183952,6 +184503,13 @@ function loadPiConfigDetailed(opts = {}) {
|
|
|
183952
184503
|
for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, rawConfig, userRaw)) {
|
|
183953
184504
|
warnings.push(`${prefix} ${warning}`);
|
|
183954
184505
|
}
|
|
184506
|
+
for (const warning of constrainProjectThresholdOverrides({
|
|
184507
|
+
mergedRaw: rawConfig,
|
|
184508
|
+
projectRaw,
|
|
184509
|
+
trustedBaseConfig
|
|
184510
|
+
})) {
|
|
184511
|
+
warnings.push(`${prefix} ${warning}`);
|
|
184512
|
+
}
|
|
183955
184513
|
} else {
|
|
183956
184514
|
rawConfig = mergeRawConfigs(rawConfig, loaded.config);
|
|
183957
184515
|
}
|
|
@@ -184030,6 +184588,8 @@ function isMidTurnPi(event, _sessionId) {
|
|
|
184030
184588
|
}
|
|
184031
184589
|
if (latestAssistant === null)
|
|
184032
184590
|
return false;
|
|
184591
|
+
if (hasRealUserAfter(messages, latestAssistantIndex))
|
|
184592
|
+
return false;
|
|
184033
184593
|
if (latestAssistant.stopReason === "toolUse")
|
|
184034
184594
|
return true;
|
|
184035
184595
|
const toolCallIds = getToolCallIds(latestAssistant.content);
|
|
@@ -184052,6 +184612,15 @@ function isMidTurnPi(event, _sessionId) {
|
|
|
184052
184612
|
}
|
|
184053
184613
|
return false;
|
|
184054
184614
|
}
|
|
184615
|
+
function hasRealUserAfter(messages, latestAssistantIndex) {
|
|
184616
|
+
for (const msg of messages.slice(latestAssistantIndex + 1)) {
|
|
184617
|
+
if (msg === null || typeof msg !== "object")
|
|
184618
|
+
continue;
|
|
184619
|
+
if (msg.role === "user")
|
|
184620
|
+
return true;
|
|
184621
|
+
}
|
|
184622
|
+
return false;
|
|
184623
|
+
}
|
|
184055
184624
|
function getToolCallIds(content) {
|
|
184056
184625
|
const ids = new Set;
|
|
184057
184626
|
if (!Array.isArray(content))
|
|
@@ -184374,7 +184943,7 @@ class PiRetrospectiveRawProvider {
|
|
|
184374
184943
|
updatedAt: typeof info.modified === "number" ? info.modified : undefined
|
|
184375
184944
|
});
|
|
184376
184945
|
}
|
|
184377
|
-
return result.sort((a, b) => (
|
|
184946
|
+
return result.sort((a, b) => (a.updatedAt ?? 0) - (b.updatedAt ?? 0));
|
|
184378
184947
|
}
|
|
184379
184948
|
async readUserMessagesSince(sessionId, sinceMs, capPerSession) {
|
|
184380
184949
|
const all = await this.loadUserEntries(sessionId);
|
|
@@ -184385,6 +184954,15 @@ class PiRetrospectiveRawProvider {
|
|
|
184385
184954
|
truncated: eligible.length > limit
|
|
184386
184955
|
};
|
|
184387
184956
|
}
|
|
184957
|
+
async readOldestMessageTimesSince(sessionIds, sinceMs) {
|
|
184958
|
+
const out = new Map;
|
|
184959
|
+
for (const sessionId of sessionIds) {
|
|
184960
|
+
const oldest = (await this.loadUserEntries(sessionId)).filter((message) => message.ts > sinceMs).sort((a, b) => a.ts - b.ts || a.ordinal - b.ordinal)[0];
|
|
184961
|
+
if (oldest)
|
|
184962
|
+
out.set(sessionId, oldest.ts);
|
|
184963
|
+
}
|
|
184964
|
+
return out;
|
|
184965
|
+
}
|
|
184388
184966
|
async readUserMessagesBefore(sessionId, beforeMs, count) {
|
|
184389
184967
|
const all = await this.loadUserEntries(sessionId);
|
|
184390
184968
|
return all.filter((entry) => entry.ts <= beforeMs).sort((a, b) => a.ts - b.ts || a.ordinal - b.ordinal).slice(-Math.max(1, Math.floor(count)));
|
|
@@ -184714,6 +185292,7 @@ function registerCtxDreamCommand(pi, deps) {
|
|
|
184714
185292
|
projectDir: deps.projectDir,
|
|
184715
185293
|
projectIdentity: deps.projectIdentity
|
|
184716
185294
|
};
|
|
185295
|
+
const dreamerEnabled = deps.resolveDreamerEnabled?.(ctx) ?? deps.dreamerEnabled;
|
|
184717
185296
|
deps.onProjectSeen?.(project.projectIdentity);
|
|
184718
185297
|
const requested = typeof args === "string" ? args.trim() : String(args ?? "").trim();
|
|
184719
185298
|
let task;
|
|
@@ -184733,7 +185312,7 @@ Unknown task "${requested}".`,
|
|
|
184733
185312
|
}
|
|
184734
185313
|
task = requested;
|
|
184735
185314
|
}
|
|
184736
|
-
if (
|
|
185315
|
+
if (dreamerEnabled === false) {
|
|
184737
185316
|
sendCtxStatusMessage(pi, {
|
|
184738
185317
|
title: "/ctx-dream",
|
|
184739
185318
|
text: "## /ctx-dream\n\nDreamer is disabled for this project (`dreamer.disable=true`).",
|
|
@@ -184848,6 +185427,8 @@ function formatEmbedStatusText(coverage, drain) {
|
|
|
184848
185427
|
}
|
|
184849
185428
|
|
|
184850
185429
|
// src/commands/ctx-embed.ts
|
|
185430
|
+
var EMBED_PROGRESS_COMPARTMENT_STEP = 8;
|
|
185431
|
+
var EMBED_PROGRESS_MIN_INTERVAL_MS = 1e4;
|
|
184851
185432
|
function clearPiEmbedSessionState(sessionId) {
|
|
184852
185433
|
embedPauseBySession.delete(sessionId);
|
|
184853
185434
|
const ctrl = embedRunStateBySession.get(sessionId);
|
|
@@ -184857,7 +185438,7 @@ function clearPiEmbedSessionState(sessionId) {
|
|
|
184857
185438
|
}
|
|
184858
185439
|
autoEmbedAttemptedBySession.delete(sessionId);
|
|
184859
185440
|
}
|
|
184860
|
-
async function runEmbedDrain(db, projectIdentity, sessionId) {
|
|
185441
|
+
async function runEmbedDrain(db, projectIdentity, sessionId, options = {}) {
|
|
184861
185442
|
const activeCtrl = embedRunStateBySession.get(sessionId);
|
|
184862
185443
|
if (activeCtrl && !activeCtrl.signal.aborted) {
|
|
184863
185444
|
return {
|
|
@@ -184873,10 +185454,46 @@ Embedding is already running for this session.`,
|
|
|
184873
185454
|
prior.abort();
|
|
184874
185455
|
const controller = new AbortController;
|
|
184875
185456
|
embedRunStateBySession.set(sessionId, controller);
|
|
185457
|
+
const now = options.now ?? Date.now;
|
|
185458
|
+
let startEmitted = false;
|
|
185459
|
+
let lastProgressEmbedded = 0;
|
|
185460
|
+
let lastProgressAt = 0;
|
|
185461
|
+
const emitProgress = (progress) => {
|
|
185462
|
+
if (progress.total <= 0)
|
|
185463
|
+
return;
|
|
185464
|
+
if (!startEmitted) {
|
|
185465
|
+
startEmitted = true;
|
|
185466
|
+
lastProgressAt = now();
|
|
185467
|
+
options.onStatus?.({
|
|
185468
|
+
text: `## /ctx-embed
|
|
185469
|
+
|
|
185470
|
+
Embedding ${progress.total} compartment${progress.total === 1 ? "" : "s"} of history…`,
|
|
185471
|
+
level: "info"
|
|
185472
|
+
});
|
|
185473
|
+
return;
|
|
185474
|
+
}
|
|
185475
|
+
if (progress.embedded <= 0 || progress.embedded >= progress.total)
|
|
185476
|
+
return;
|
|
185477
|
+
const currentTime = now();
|
|
185478
|
+
const enoughCompartments = progress.embedded - lastProgressEmbedded >= EMBED_PROGRESS_COMPARTMENT_STEP;
|
|
185479
|
+
const enoughTime = currentTime - lastProgressAt >= EMBED_PROGRESS_MIN_INTERVAL_MS && progress.embedded > lastProgressEmbedded;
|
|
185480
|
+
if (!enoughCompartments && !enoughTime)
|
|
185481
|
+
return;
|
|
185482
|
+
lastProgressEmbedded = progress.embedded;
|
|
185483
|
+
lastProgressAt = currentTime;
|
|
185484
|
+
options.onStatus?.({
|
|
185485
|
+
text: `## /ctx-embed
|
|
185486
|
+
|
|
185487
|
+
Embedded ${progress.embedded}/${progress.total} compartments so far…`,
|
|
185488
|
+
level: "info"
|
|
185489
|
+
});
|
|
185490
|
+
};
|
|
184876
185491
|
let outcome;
|
|
184877
185492
|
try {
|
|
184878
185493
|
outcome = await embedSessionCompartmentChunks(db, projectIdentity, sessionId, {
|
|
184879
|
-
signal: controller.signal
|
|
185494
|
+
signal: controller.signal,
|
|
185495
|
+
onProgress: emitProgress,
|
|
185496
|
+
...options.batchSize !== undefined ? { batchSize: options.batchSize } : {}
|
|
184880
185497
|
});
|
|
184881
185498
|
} finally {
|
|
184882
185499
|
if (embedRunStateBySession.get(sessionId) === controller) {
|
|
@@ -184949,6 +185566,7 @@ No active Pi session is available.`,
|
|
|
184949
185566
|
projectDir: deps.projectDir,
|
|
184950
185567
|
projectIdentity: deps.projectIdentity
|
|
184951
185568
|
};
|
|
185569
|
+
const memoryEnabled = deps.resolveMemoryEnabled?.(ctx) ?? deps.memoryEnabled;
|
|
184952
185570
|
const sub = args.trim().toLowerCase();
|
|
184953
185571
|
if (sub === "pause") {
|
|
184954
185572
|
embedPauseBySession.add(sessionId);
|
|
@@ -184965,7 +185583,7 @@ Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`,
|
|
|
184965
185583
|
});
|
|
184966
185584
|
return;
|
|
184967
185585
|
}
|
|
184968
|
-
if (
|
|
185586
|
+
if (memoryEnabled === false) {
|
|
184969
185587
|
sendCtxStatusMessage(pi, {
|
|
184970
185588
|
title: "/ctx-embed",
|
|
184971
185589
|
text: `## /ctx-embed
|
|
@@ -184977,7 +185595,12 @@ Memory is disabled for this project, so there is no semantic embedding to backfi
|
|
|
184977
185595
|
}
|
|
184978
185596
|
await ensureProjectRegisteredFromPiDirectory(project.projectDir, deps.db);
|
|
184979
185597
|
if (sub === "start") {
|
|
184980
|
-
const { text, level } = await runEmbedDrain(deps.db, project.projectIdentity, sessionId
|
|
185598
|
+
const { text, level } = await runEmbedDrain(deps.db, project.projectIdentity, sessionId, {
|
|
185599
|
+
onStatus: (status) => sendCtxStatusMessage(pi, {
|
|
185600
|
+
title: "/ctx-embed",
|
|
185601
|
+
...status
|
|
185602
|
+
})
|
|
185603
|
+
});
|
|
184981
185604
|
sendCtxStatusMessage(pi, { title: "/ctx-embed", text, level });
|
|
184982
185605
|
return;
|
|
184983
185606
|
}
|
|
@@ -186099,6 +186722,8 @@ init_logger();
|
|
|
186099
186722
|
// ../plugin/src/hooks/magic-context/read-session-true-raw-tokens.ts
|
|
186100
186723
|
var MAX_MESSAGE_CACHE_ENTRIES = 1e5;
|
|
186101
186724
|
var MAX_MESSAGE_CACHE_KEY_BYTES = 64 * 1024 * 1024;
|
|
186725
|
+
var FNV1A_32_OFFSET = 2166136261;
|
|
186726
|
+
var FNV1A_32_PRIME = 16777619;
|
|
186102
186727
|
var messageEstimateCache = new Map;
|
|
186103
186728
|
var messageEstimateCacheBytes = 0;
|
|
186104
186729
|
var EMPTY_BREAKDOWN = {
|
|
@@ -186201,6 +186826,23 @@ function recursiveByteLength(value) {
|
|
|
186201
186826
|
}
|
|
186202
186827
|
return String(value).length;
|
|
186203
186828
|
}
|
|
186829
|
+
function updateFnv1a32(hash2, text) {
|
|
186830
|
+
let next = hash2;
|
|
186831
|
+
for (let index = 0;index < text.length; index += 1) {
|
|
186832
|
+
next ^= text.charCodeAt(index);
|
|
186833
|
+
next = Math.imul(next, FNV1A_32_PRIME) >>> 0;
|
|
186834
|
+
}
|
|
186835
|
+
return next;
|
|
186836
|
+
}
|
|
186837
|
+
function contentStringsHash(fields) {
|
|
186838
|
+
let hash2 = FNV1A_32_OFFSET;
|
|
186839
|
+
for (const field of fields) {
|
|
186840
|
+
hash2 = updateFnv1a32(hash2, `${field.length}:`);
|
|
186841
|
+
hash2 = updateFnv1a32(hash2, field);
|
|
186842
|
+
hash2 = updateFnv1a32(hash2, "\x00");
|
|
186843
|
+
}
|
|
186844
|
+
return hash2.toString(16).padStart(8, "0");
|
|
186845
|
+
}
|
|
186204
186846
|
function rawPartVersion(part) {
|
|
186205
186847
|
return part.__magicContextPartUpdatedAt ?? part.updated_at ?? part.updatedAt ?? part.version ?? part.revision ?? "";
|
|
186206
186848
|
}
|
|
@@ -186548,13 +187190,12 @@ function buildTrueRawTokenIndex(sessionId, messages, options) {
|
|
|
186548
187190
|
function partContentFingerprint(part) {
|
|
186549
187191
|
if (!isRecord(part))
|
|
186550
187192
|
return `${typeof part}:${recursiveByteLength(part)}`;
|
|
186551
|
-
const type = partType(part);
|
|
186552
187193
|
const tool = toolSignalFromPart(part);
|
|
186553
187194
|
if (tool) {
|
|
186554
|
-
return
|
|
187195
|
+
return contentStringsHash([tool.inputText, tool.outputText]);
|
|
186555
187196
|
}
|
|
186556
187197
|
const text = firstStringField(part, ["text", "thinking", "reasoning", "content", "url"]) ?? "";
|
|
186557
|
-
return
|
|
187198
|
+
return contentStringsHash([text]);
|
|
186558
187199
|
}
|
|
186559
187200
|
function computeRawRangeFingerprint(messages, startInclusive, endExclusive) {
|
|
186560
187201
|
const pieces = [];
|
|
@@ -188010,6 +188651,11 @@ function renderMemoryBlockV2(memories, wrapper = "project-memory", renderOptions
|
|
|
188010
188651
|
`);
|
|
188011
188652
|
}
|
|
188012
188653
|
|
|
188654
|
+
// ../plugin/src/hooks/magic-context/sentinel.ts
|
|
188655
|
+
function modelAcceptsEmptyContent(providerID) {
|
|
188656
|
+
return providerID === "anthropic";
|
|
188657
|
+
}
|
|
188658
|
+
|
|
188013
188659
|
// ../plugin/src/hooks/magic-context/supersession-reclaim.ts
|
|
188014
188660
|
var TODOWRITE_KEEP = 1;
|
|
188015
188661
|
var CTX_REDUCE_KEEP = 5;
|
|
@@ -188806,24 +189452,46 @@ function getMessageSearchStatementWithCutoff(db) {
|
|
|
188806
189452
|
return stmt;
|
|
188807
189453
|
}
|
|
188808
189454
|
var ftsRowCountStatements = new WeakMap;
|
|
189455
|
+
var ftsRowCountStatementsWithCutoff = new WeakMap;
|
|
188809
189456
|
var ftsMatchCountStatements = new WeakMap;
|
|
188810
|
-
|
|
188811
|
-
|
|
188812
|
-
|
|
188813
|
-
|
|
188814
|
-
|
|
188815
|
-
|
|
188816
|
-
|
|
189457
|
+
var ftsMatchCountStatementsWithCutoff = new WeakMap;
|
|
189458
|
+
function getSessionFtsRowCount(db, sessionId, cutoff) {
|
|
189459
|
+
const stmt = cutoff === null ? (() => {
|
|
189460
|
+
let cached2 = ftsRowCountStatements.get(db);
|
|
189461
|
+
if (!cached2) {
|
|
189462
|
+
cached2 = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ?");
|
|
189463
|
+
ftsRowCountStatements.set(db, cached2);
|
|
189464
|
+
}
|
|
189465
|
+
return cached2;
|
|
189466
|
+
})() : (() => {
|
|
189467
|
+
let cached2 = ftsRowCountStatementsWithCutoff.get(db);
|
|
189468
|
+
if (!cached2) {
|
|
189469
|
+
cached2 = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ? AND CAST(message_ordinal AS INTEGER) <= ?");
|
|
189470
|
+
ftsRowCountStatementsWithCutoff.set(db, cached2);
|
|
189471
|
+
}
|
|
189472
|
+
return cached2;
|
|
189473
|
+
})();
|
|
189474
|
+
const row = cutoff === null ? stmt.get(sessionId) : stmt.get(sessionId, cutoff);
|
|
188817
189475
|
return typeof row?.n === "number" ? row.n : 0;
|
|
188818
189476
|
}
|
|
188819
|
-
function countSessionFtsMatches(db, sessionId, ftsQuery) {
|
|
188820
|
-
|
|
188821
|
-
|
|
188822
|
-
|
|
188823
|
-
|
|
188824
|
-
|
|
189477
|
+
function countSessionFtsMatches(db, sessionId, ftsQuery, cutoff) {
|
|
189478
|
+
const stmt = cutoff === null ? (() => {
|
|
189479
|
+
let cached2 = ftsMatchCountStatements.get(db);
|
|
189480
|
+
if (!cached2) {
|
|
189481
|
+
cached2 = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ? AND message_history_fts MATCH ?");
|
|
189482
|
+
ftsMatchCountStatements.set(db, cached2);
|
|
189483
|
+
}
|
|
189484
|
+
return cached2;
|
|
189485
|
+
})() : (() => {
|
|
189486
|
+
let cached2 = ftsMatchCountStatementsWithCutoff.get(db);
|
|
189487
|
+
if (!cached2) {
|
|
189488
|
+
cached2 = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ? AND message_history_fts MATCH ? AND CAST(message_ordinal AS INTEGER) <= ?");
|
|
189489
|
+
ftsMatchCountStatementsWithCutoff.set(db, cached2);
|
|
189490
|
+
}
|
|
189491
|
+
return cached2;
|
|
189492
|
+
})();
|
|
188825
189493
|
try {
|
|
188826
|
-
const row = stmt.get(sessionId, ftsQuery);
|
|
189494
|
+
const row = cutoff === null ? stmt.get(sessionId, ftsQuery) : stmt.get(sessionId, ftsQuery, cutoff);
|
|
188827
189495
|
return typeof row?.n === "number" ? row.n : 0;
|
|
188828
189496
|
} catch {
|
|
188829
189497
|
return 0;
|
|
@@ -189073,7 +189741,7 @@ function searchMessages(args) {
|
|
|
189073
189741
|
role: row.role
|
|
189074
189742
|
}));
|
|
189075
189743
|
}
|
|
189076
|
-
const corpusSize = getSessionFtsRowCount(args.db, args.sessionId);
|
|
189744
|
+
const corpusSize = getSessionFtsRowCount(args.db, args.sessionId, cutoff);
|
|
189077
189745
|
const queryLists = [];
|
|
189078
189746
|
if (baseQuery.length > 0) {
|
|
189079
189747
|
queryLists.push({
|
|
@@ -189086,7 +189754,7 @@ function searchMessages(args) {
|
|
|
189086
189754
|
const probeQuery = sanitizeFtsQuery(probe);
|
|
189087
189755
|
if (probeQuery.length === 0)
|
|
189088
189756
|
continue;
|
|
189089
|
-
const df = countSessionFtsMatches(args.db, args.sessionId, probeQuery);
|
|
189757
|
+
const df = countSessionFtsMatches(args.db, args.sessionId, probeQuery, cutoff);
|
|
189090
189758
|
const weight = probeDiscriminationWeight(df, corpusSize);
|
|
189091
189759
|
probeWeights.set(probe, weight);
|
|
189092
189760
|
queryLists.push({
|
|
@@ -190510,8 +191178,8 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
|
|
|
190510
191178
|
sessionLog(sessionId, `emergency tiered drop skipped: ${plan.reason}`);
|
|
190511
191179
|
}
|
|
190512
191180
|
}
|
|
190513
|
-
const staleReduce = collectStaleReduceCallIds(piMessages, buildMessageIdToMaxTagFromTargets(targets), toolAgeCutoff, resolveStableId);
|
|
190514
|
-
if (staleReduce.composite.size > 0 || staleReduce.bareCallIds.size > 0) {
|
|
191181
|
+
const staleReduce = config2.staleReduceStripEnabled ? collectStaleReduceCallIds(piMessages, buildMessageIdToMaxTagFromTargets(targets), toolAgeCutoff, resolveStableId) : { composite: new Set, bareCallIds: new Set };
|
|
191182
|
+
if (config2.staleReduceStripEnabled && (staleReduce.composite.size > 0 || staleReduce.bareCallIds.size > 0)) {
|
|
190515
191183
|
db.transaction(() => {
|
|
190516
191184
|
for (const tag of tags) {
|
|
190517
191185
|
if (tag.status !== "active")
|
|
@@ -194792,8 +195460,122 @@ function buildReferenceBlocks(args) {
|
|
|
194792
195460
|
init_logger();
|
|
194793
195461
|
var HISTORIAN_AGENT_NAME = "magic-context-historian";
|
|
194794
195462
|
var DEFAULT_HISTORIAN_TIMEOUT_MS2 = 120000;
|
|
195463
|
+
var MAX_HISTORIAN_RETRIES = 2;
|
|
194795
195464
|
var HISTORIAN_ALERT_COOLDOWN_MS = 60 * 1000;
|
|
194796
195465
|
var lastHistorianAlertBySession = new Map;
|
|
195466
|
+
function getHistorianRetryBackoffMs(retryIndex) {
|
|
195467
|
+
if (retryIndex === 0) {
|
|
195468
|
+
return 2000 + Math.floor(Math.random() * 1001);
|
|
195469
|
+
}
|
|
195470
|
+
return 6000 + Math.floor(Math.random() * 2001);
|
|
195471
|
+
}
|
|
195472
|
+
function isTransientHistorianPromptError(message) {
|
|
195473
|
+
const normalized = message.toLowerCase();
|
|
195474
|
+
if (normalized.includes("invalid request") || normalized.includes("bad request") || normalized.includes("unauthorized") || normalized.includes("forbidden") || normalized.includes("authentication") || normalized.includes("auth") || normalized.includes(" 400") || normalized.startsWith("400")) {
|
|
195475
|
+
return false;
|
|
195476
|
+
}
|
|
195477
|
+
return [
|
|
195478
|
+
"429",
|
|
195479
|
+
"rate limit",
|
|
195480
|
+
"timeout",
|
|
195481
|
+
"econnreset",
|
|
195482
|
+
"etimedout",
|
|
195483
|
+
"503",
|
|
195484
|
+
"502",
|
|
195485
|
+
"500",
|
|
195486
|
+
"overloaded"
|
|
195487
|
+
].some((token) => normalized.includes(token));
|
|
195488
|
+
}
|
|
195489
|
+
function isTransientHistorianRunFailure(result) {
|
|
195490
|
+
if (result.reason === "abort")
|
|
195491
|
+
return false;
|
|
195492
|
+
if (result.reason === "timeout")
|
|
195493
|
+
return true;
|
|
195494
|
+
return isTransientHistorianPromptError(result.error);
|
|
195495
|
+
}
|
|
195496
|
+
function historianAbortResult(startedAt) {
|
|
195497
|
+
return {
|
|
195498
|
+
ok: false,
|
|
195499
|
+
reason: "abort",
|
|
195500
|
+
error: "pi subagent aborted by caller",
|
|
195501
|
+
durationMs: Date.now() - startedAt
|
|
195502
|
+
};
|
|
195503
|
+
}
|
|
195504
|
+
async function sleepWithAbort(ms, signal) {
|
|
195505
|
+
if (signal?.aborted)
|
|
195506
|
+
return true;
|
|
195507
|
+
if (ms <= 0)
|
|
195508
|
+
return signal?.aborted === true;
|
|
195509
|
+
return new Promise((resolve3) => {
|
|
195510
|
+
let settled = false;
|
|
195511
|
+
let timeout;
|
|
195512
|
+
const finish = (aborted2) => {
|
|
195513
|
+
if (settled)
|
|
195514
|
+
return;
|
|
195515
|
+
settled = true;
|
|
195516
|
+
if (timeout)
|
|
195517
|
+
clearTimeout(timeout);
|
|
195518
|
+
signal?.removeEventListener("abort", onAbort);
|
|
195519
|
+
resolve3(aborted2);
|
|
195520
|
+
};
|
|
195521
|
+
const onAbort = () => finish(true);
|
|
195522
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
195523
|
+
timeout = setTimeout(() => finish(false), ms);
|
|
195524
|
+
});
|
|
195525
|
+
}
|
|
195526
|
+
async function runHistorianSubagentWithTransientRetries(args) {
|
|
195527
|
+
const startedAt = Date.now();
|
|
195528
|
+
if (args.options.signal?.aborted)
|
|
195529
|
+
return historianAbortResult(startedAt);
|
|
195530
|
+
for (let retryIndex = 0;retryIndex <= MAX_HISTORIAN_RETRIES; retryIndex += 1) {
|
|
195531
|
+
const attemptStart = Date.now();
|
|
195532
|
+
let result;
|
|
195533
|
+
try {
|
|
195534
|
+
result = await args.runner.run({
|
|
195535
|
+
...args.options,
|
|
195536
|
+
fallbackModels: undefined
|
|
195537
|
+
});
|
|
195538
|
+
} catch (error51) {
|
|
195539
|
+
const desc = describeError(error51);
|
|
195540
|
+
result = {
|
|
195541
|
+
ok: false,
|
|
195542
|
+
reason: "model_failed",
|
|
195543
|
+
error: desc.brief,
|
|
195544
|
+
durationMs: Date.now() - attemptStart
|
|
195545
|
+
};
|
|
195546
|
+
}
|
|
195547
|
+
if (result.ok)
|
|
195548
|
+
return result;
|
|
195549
|
+
if (result.reason === "abort" || args.options.signal?.aborted) {
|
|
195550
|
+
return result.reason === "abort" ? result : historianAbortResult(startedAt);
|
|
195551
|
+
}
|
|
195552
|
+
const shouldRetry = retryIndex < MAX_HISTORIAN_RETRIES && isTransientHistorianRunFailure(result);
|
|
195553
|
+
if (!shouldRetry)
|
|
195554
|
+
return result;
|
|
195555
|
+
const backoffMs2 = args.retryBackoffMs?.(retryIndex) ?? getHistorianRetryBackoffMs(retryIndex);
|
|
195556
|
+
sessionLog(args.sessionId, `historian[${args.passLabel}] transient failure; retry ${retryIndex + 1}/${MAX_HISTORIAN_RETRIES} on same model after ${backoffMs2}ms: ${result.error}`);
|
|
195557
|
+
const aborted2 = await sleepWithAbort(backoffMs2, args.options.signal);
|
|
195558
|
+
if (aborted2)
|
|
195559
|
+
return historianAbortResult(startedAt);
|
|
195560
|
+
}
|
|
195561
|
+
return historianAbortResult(startedAt);
|
|
195562
|
+
}
|
|
195563
|
+
function buildHistorianFallbackChain(primaryModel, fallbackModels, fallbackModelId) {
|
|
195564
|
+
const seen = new Set;
|
|
195565
|
+
if (primaryModel)
|
|
195566
|
+
seen.add(primaryModel);
|
|
195567
|
+
const chain = [];
|
|
195568
|
+
for (const candidate of fallbackModels ?? []) {
|
|
195569
|
+
if (!candidate || seen.has(candidate))
|
|
195570
|
+
continue;
|
|
195571
|
+
seen.add(candidate);
|
|
195572
|
+
chain.push({ modelId: candidate, kind: "configured" });
|
|
195573
|
+
}
|
|
195574
|
+
if (fallbackModelId && !seen.has(fallbackModelId)) {
|
|
195575
|
+
chain.push({ modelId: fallbackModelId, kind: "session" });
|
|
195576
|
+
}
|
|
195577
|
+
return chain;
|
|
195578
|
+
}
|
|
194797
195579
|
function parseSourceMessageTime(value) {
|
|
194798
195580
|
if (typeof value === "number" && Number.isFinite(value))
|
|
194799
195581
|
return value;
|
|
@@ -194842,11 +195624,14 @@ async function runPiHistorian(deps) {
|
|
|
194842
195624
|
runner,
|
|
194843
195625
|
historianModel,
|
|
194844
195626
|
fallbackModels,
|
|
195627
|
+
fallbackModelId,
|
|
194845
195628
|
historianChunkTokens,
|
|
194846
195629
|
boundarySnapshot: providedBoundarySnapshot,
|
|
194847
195630
|
refreshBoundarySnapshot,
|
|
194848
195631
|
currentContextLimit,
|
|
194849
195632
|
historianTimeoutMs = DEFAULT_HISTORIAN_TIMEOUT_MS2,
|
|
195633
|
+
signal,
|
|
195634
|
+
retryBackoffMs,
|
|
194850
195635
|
twoPass,
|
|
194851
195636
|
thinkingLevel,
|
|
194852
195637
|
memoryEnabled,
|
|
@@ -194860,7 +195645,9 @@ async function runPiHistorian(deps) {
|
|
|
194860
195645
|
forceDrainQuota,
|
|
194861
195646
|
forceKeepLastCompartment
|
|
194862
195647
|
} = deps;
|
|
195648
|
+
let issueNotified = false;
|
|
194863
195649
|
const notify = async (message) => {
|
|
195650
|
+
issueNotified = true;
|
|
194864
195651
|
if (shouldSuppressHistorianAlert(sessionId)) {
|
|
194865
195652
|
sessionLog(sessionId, "historian alert suppressed (cooldown)");
|
|
194866
195653
|
return;
|
|
@@ -195051,61 +195838,77 @@ ${chunkText}`,
|
|
|
195051
195838
|
retainDrainReservationForRetryThrottle = true;
|
|
195052
195839
|
const historianSystemPrompt = withContentLanguageDirective(COMPARTMENT_AGENT_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true });
|
|
195053
195840
|
const historianEditorSystemPrompt = withContentLanguageDirective(HISTORIAN_EDITOR_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true });
|
|
195054
|
-
const firstResult = await
|
|
195055
|
-
|
|
195056
|
-
|
|
195057
|
-
|
|
195058
|
-
|
|
195059
|
-
|
|
195060
|
-
timeoutMs: historianTimeoutMs,
|
|
195061
|
-
cwd: directory,
|
|
195062
|
-
thinkingLevel,
|
|
195063
|
-
onProgress: buildProgressLogger("first"),
|
|
195064
|
-
accountingSessionId: sessionId,
|
|
195065
|
-
accountingSubagent: "historian"
|
|
195066
|
-
});
|
|
195067
|
-
let validatedPass = await validateHistorianResult(firstResult, sessionId, chunk, priorCompartments, sequenceOffset);
|
|
195068
|
-
let validatedDraftText = firstResult.ok ? firstResult.assistantText : null;
|
|
195069
|
-
if (validatedPass.kind === "validation-failed") {
|
|
195070
|
-
sessionLog(sessionId, `historian: first pass validation failed, retrying with repair prompt: ${validatedPass.error}`);
|
|
195071
|
-
const repairPrompt = buildHistorianRepairPrompt(prompt, validatedPass.rawText, validatedPass.error, deps.language);
|
|
195072
|
-
const repairResult = await runner.run({
|
|
195841
|
+
const firstResult = await runHistorianSubagentWithTransientRetries({
|
|
195842
|
+
runner,
|
|
195843
|
+
sessionId,
|
|
195844
|
+
passLabel: "first",
|
|
195845
|
+
retryBackoffMs,
|
|
195846
|
+
options: {
|
|
195073
195847
|
agent: HISTORIAN_AGENT_NAME,
|
|
195074
195848
|
systemPrompt: historianSystemPrompt,
|
|
195075
|
-
userMessage:
|
|
195849
|
+
userMessage: prompt,
|
|
195076
195850
|
model: historianModel,
|
|
195077
|
-
fallbackModels,
|
|
195078
195851
|
timeoutMs: historianTimeoutMs,
|
|
195079
195852
|
cwd: directory,
|
|
195853
|
+
signal,
|
|
195080
195854
|
thinkingLevel,
|
|
195081
|
-
onProgress: buildProgressLogger("
|
|
195855
|
+
onProgress: buildProgressLogger("first"),
|
|
195082
195856
|
accountingSessionId: sessionId,
|
|
195083
195857
|
accountingSubagent: "historian"
|
|
195084
|
-
});
|
|
195085
|
-
validatedPass = await validateHistorianResult(repairResult, sessionId, chunk, priorCompartments, sequenceOffset);
|
|
195086
|
-
if (validatedPass.kind === "ok" && repairResult.ok) {
|
|
195087
|
-
validatedDraftText = repairResult.assistantText;
|
|
195088
195858
|
}
|
|
195089
|
-
}
|
|
195090
|
-
|
|
195091
|
-
|
|
195092
|
-
|
|
195093
|
-
|
|
195094
|
-
|
|
195095
|
-
|
|
195096
|
-
|
|
195097
|
-
|
|
195859
|
+
});
|
|
195860
|
+
let validatedPass = await validateHistorianResult(firstResult, sessionId, chunk, priorCompartments, sequenceOffset);
|
|
195861
|
+
let validatedDraftText = firstResult.ok ? firstResult.assistantText : null;
|
|
195862
|
+
if (validatedPass.kind === "validation-failed") {
|
|
195863
|
+
sessionLog(sessionId, `historian: first pass validation failed, retrying with repair prompt: ${validatedPass.error}`);
|
|
195864
|
+
const repairPrompt = buildHistorianRepairPrompt(prompt, validatedPass.rawText, validatedPass.error, deps.language);
|
|
195865
|
+
const repairResult = await runHistorianSubagentWithTransientRetries({
|
|
195866
|
+
runner,
|
|
195867
|
+
sessionId,
|
|
195868
|
+
passLabel: "repair",
|
|
195869
|
+
retryBackoffMs,
|
|
195870
|
+
options: {
|
|
195098
195871
|
agent: HISTORIAN_AGENT_NAME,
|
|
195099
195872
|
systemPrompt: historianSystemPrompt,
|
|
195100
|
-
userMessage:
|
|
195101
|
-
model:
|
|
195102
|
-
fallbackModels: undefined,
|
|
195873
|
+
userMessage: repairPrompt,
|
|
195874
|
+
model: historianModel,
|
|
195103
195875
|
timeoutMs: historianTimeoutMs,
|
|
195104
195876
|
cwd: directory,
|
|
195877
|
+
signal,
|
|
195105
195878
|
thinkingLevel,
|
|
195106
|
-
onProgress: buildProgressLogger("
|
|
195879
|
+
onProgress: buildProgressLogger("repair"),
|
|
195107
195880
|
accountingSessionId: sessionId,
|
|
195108
195881
|
accountingSubagent: "historian"
|
|
195882
|
+
}
|
|
195883
|
+
});
|
|
195884
|
+
validatedPass = await validateHistorianResult(repairResult, sessionId, chunk, priorCompartments, sequenceOffset);
|
|
195885
|
+
if (validatedPass.kind === "ok" && repairResult.ok) {
|
|
195886
|
+
validatedDraftText = repairResult.assistantText;
|
|
195887
|
+
}
|
|
195888
|
+
}
|
|
195889
|
+
const fallbackChain = buildHistorianFallbackChain(historianModel, fallbackModels, fallbackModelId);
|
|
195890
|
+
if (validatedPass.kind !== "ok" && !(validatedPass.kind === "spawn-failed" && validatedPass.reason === "abort") && fallbackChain.length > 0) {
|
|
195891
|
+
for (let i = 0;i < fallbackChain.length; i += 1) {
|
|
195892
|
+
const candidate = fallbackChain[i];
|
|
195893
|
+
sessionLog(sessionId, `historian: escalating to ${candidate.kind === "session" ? "session-model last resort" : "configured fallback model"} ${candidate.modelId}`);
|
|
195894
|
+
const fbResult = await runHistorianSubagentWithTransientRetries({
|
|
195895
|
+
runner,
|
|
195896
|
+
sessionId,
|
|
195897
|
+
passLabel: candidate.kind === "session" ? "fallback-session" : "fallback",
|
|
195898
|
+
retryBackoffMs,
|
|
195899
|
+
options: {
|
|
195900
|
+
agent: HISTORIAN_AGENT_NAME,
|
|
195901
|
+
systemPrompt: historianSystemPrompt,
|
|
195902
|
+
userMessage: prompt,
|
|
195903
|
+
model: candidate.modelId,
|
|
195904
|
+
timeoutMs: historianTimeoutMs,
|
|
195905
|
+
cwd: directory,
|
|
195906
|
+
signal,
|
|
195907
|
+
thinkingLevel,
|
|
195908
|
+
onProgress: buildProgressLogger("fallback"),
|
|
195909
|
+
accountingSessionId: sessionId,
|
|
195910
|
+
accountingSubagent: "historian"
|
|
195911
|
+
}
|
|
195109
195912
|
});
|
|
195110
195913
|
const fbPass = await validateHistorianResult(fbResult, sessionId, chunk, priorCompartments, sequenceOffset);
|
|
195111
195914
|
if (fbPass.kind === "ok") {
|
|
@@ -195114,6 +195917,10 @@ ${chunkText}`,
|
|
|
195114
195917
|
validatedDraftText = fbResult.assistantText;
|
|
195115
195918
|
break;
|
|
195116
195919
|
}
|
|
195920
|
+
if (fbPass.kind === "spawn-failed" && fbPass.reason === "abort") {
|
|
195921
|
+
validatedPass = fbPass;
|
|
195922
|
+
break;
|
|
195923
|
+
}
|
|
195117
195924
|
}
|
|
195118
195925
|
}
|
|
195119
195926
|
if (validatedPass.kind !== "ok") {
|
|
@@ -195130,17 +195937,24 @@ ${chunkText}`,
|
|
|
195130
195937
|
const draftAssistantText = validatedDraftText ?? "";
|
|
195131
195938
|
if (draftAssistantText.trim().length > 0) {
|
|
195132
195939
|
sessionLog(sessionId, "historian two-pass: running editor on draft");
|
|
195133
|
-
const editorResult = await
|
|
195134
|
-
|
|
195135
|
-
|
|
195136
|
-
|
|
195137
|
-
|
|
195138
|
-
|
|
195139
|
-
|
|
195140
|
-
|
|
195141
|
-
|
|
195142
|
-
|
|
195143
|
-
|
|
195940
|
+
const editorResult = await runHistorianSubagentWithTransientRetries({
|
|
195941
|
+
runner,
|
|
195942
|
+
sessionId,
|
|
195943
|
+
passLabel: "editor",
|
|
195944
|
+
retryBackoffMs,
|
|
195945
|
+
options: {
|
|
195946
|
+
agent: HISTORIAN_AGENT_NAME,
|
|
195947
|
+
systemPrompt: historianEditorSystemPrompt,
|
|
195948
|
+
userMessage: buildHistorianEditorPrompt(draftAssistantText),
|
|
195949
|
+
model: historianModel,
|
|
195950
|
+
timeoutMs: historianTimeoutMs,
|
|
195951
|
+
cwd: directory,
|
|
195952
|
+
signal,
|
|
195953
|
+
thinkingLevel,
|
|
195954
|
+
onProgress: buildProgressLogger("editor"),
|
|
195955
|
+
accountingSessionId: sessionId,
|
|
195956
|
+
accountingSubagent: "historian_editor"
|
|
195957
|
+
}
|
|
195144
195958
|
});
|
|
195145
195959
|
const editorPass = await validateHistorianResult(editorResult, sessionId, chunk, priorCompartments, sequenceOffset);
|
|
195146
195960
|
if (editorPass.kind === "ok") {
|
|
@@ -195353,7 +196167,7 @@ ${chunkText}`,
|
|
|
195353
196167
|
const desc = describeError(error51);
|
|
195354
196168
|
telemetry.failureReason = `exception: ${desc.brief}`;
|
|
195355
196169
|
sessionLog(sessionId, `historian failure: source=exception ${desc.brief}${desc.stackHead ? ` stackHead="${desc.stackHead}"` : ""}`);
|
|
195356
|
-
{
|
|
196170
|
+
if (!issueNotified) {
|
|
195357
196171
|
const failCount = incrementHistorianFailure(db, sessionId, desc.brief);
|
|
195358
196172
|
await notify(buildHistorianFailureNotice(failCount, desc.brief));
|
|
195359
196173
|
}
|
|
@@ -197302,6 +198116,8 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
197302
198116
|
}
|
|
197303
198117
|
const sessionMeta = sessionMetaForUsage;
|
|
197304
198118
|
const modelKey = liveModelBySession.get(sessionId);
|
|
198119
|
+
const providerId = typeof ctx.model?.provider === "string" ? ctx.model.provider : undefined;
|
|
198120
|
+
const canUseEmptySentinels = modelAcceptsEmptyContent(providerId);
|
|
197305
198121
|
if (usageContextLimit === undefined) {
|
|
197306
198122
|
const modelWindow = ctx.model?.contextWindow;
|
|
197307
198123
|
if (isSaneLimit(modelWindow)) {
|
|
@@ -197312,6 +198128,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
197312
198128
|
usagePercentage = usageInputTokens / usageContextLimit * 100;
|
|
197313
198129
|
}
|
|
197314
198130
|
({ percentage: usagePercentage, inputTokens: usageInputTokens } = applyForwardPressureFloor(usagePercentage, usageInputTokens, piUsage?.tokens, usageContextLimit));
|
|
198131
|
+
const realUsagePercentageBeforeEmergencyBump = usagePercentage;
|
|
197315
198132
|
if (needsEmergencyBump) {
|
|
197316
198133
|
sessionLog(sessionId, `transform: overflow recovery flag set — bumping percentage to 95% (detectedLimit=${usageContextLimit ?? "unknown"})`);
|
|
197317
198134
|
usagePercentage = Math.max(usagePercentage, 95);
|
|
@@ -197382,7 +198199,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
197382
198199
|
sessionLog(sessionId, "EMERGENCY: historian wait completed (or timed out)");
|
|
197383
198200
|
} catch {}
|
|
197384
198201
|
}
|
|
197385
|
-
if (emergencyRecoveryArmed && !inFlightHistorian.has(sessionId) && !hasEligiblePiCompartmentHistory(options.db, sessionId)) {
|
|
198202
|
+
if (emergencyRecoveryArmed && realUsagePercentageBeforeEmergencyBump < FORCE_MATERIALIZATION_PERCENTAGE && !inFlightHistorian.has(sessionId) && !hasEligiblePiCompartmentHistory(options.db, sessionId)) {
|
|
197386
198203
|
try {
|
|
197387
198204
|
clearEmergencyRecovery(options.db, sessionId);
|
|
197388
198205
|
sessionLog(sessionId, "EMERGENCY: disarming recovery — no eligible pre-tail history to compact (would otherwise loop at 95%)");
|
|
@@ -197437,6 +198254,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
197437
198254
|
reasoningClearing: {
|
|
197438
198255
|
clearReasoningAge: options.heuristics?.clearReasoningAge ?? DEFAULT_CLEAR_REASONING_AGE
|
|
197439
198256
|
},
|
|
198257
|
+
canUseEmptySentinels,
|
|
197440
198258
|
temporalAwareness: options.injection?.temporalAwareness === true,
|
|
197441
198259
|
appendCompaction: resolvePiAppendCompaction(ctx),
|
|
197442
198260
|
readBranchEntries: resolvePiReadBranchEntries(ctx),
|
|
@@ -197458,6 +198276,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
197458
198276
|
}
|
|
197459
198277
|
if (options.historian) {
|
|
197460
198278
|
maybeFireHistorian({
|
|
198279
|
+
pi,
|
|
197461
198280
|
ctx,
|
|
197462
198281
|
sessionId,
|
|
197463
198282
|
db: options.db,
|
|
@@ -197698,6 +198517,7 @@ function sendPiIgnoredNotification(ctx, message) {
|
|
|
197698
198517
|
}
|
|
197699
198518
|
function spawnPiHistorianRun(args) {
|
|
197700
198519
|
const {
|
|
198520
|
+
pi,
|
|
197701
198521
|
ctx,
|
|
197702
198522
|
sessionId,
|
|
197703
198523
|
db,
|
|
@@ -197706,7 +198526,8 @@ function spawnPiHistorianRun(args) {
|
|
|
197706
198526
|
unregister,
|
|
197707
198527
|
boundarySnapshot,
|
|
197708
198528
|
refreshBoundarySnapshot,
|
|
197709
|
-
currentContextLimit
|
|
198529
|
+
currentContextLimit,
|
|
198530
|
+
fallbackModelId
|
|
197710
198531
|
} = args;
|
|
197711
198532
|
const holderId = crypto3.randomUUID();
|
|
197712
198533
|
const runPromise = (async () => {
|
|
@@ -197732,6 +198553,7 @@ function spawnPiHistorianRun(args) {
|
|
|
197732
198553
|
runner: historian.runner,
|
|
197733
198554
|
historianModel: historian.model,
|
|
197734
198555
|
fallbackModels: historian.fallbackModels,
|
|
198556
|
+
fallbackModelId,
|
|
197735
198557
|
historianChunkTokens: historian.historianChunkTokens,
|
|
197736
198558
|
boundarySnapshot,
|
|
197737
198559
|
refreshBoundarySnapshot,
|
|
@@ -197744,6 +198566,17 @@ function spawnPiHistorianRun(args) {
|
|
|
197744
198566
|
userMemoriesEnabled: historian.userMemoriesEnabled,
|
|
197745
198567
|
language: historian.language,
|
|
197746
198568
|
compartmentLeaseHolderId: holderId,
|
|
198569
|
+
notifyIssue: (text) => {
|
|
198570
|
+
if (!isContextHandlerSessionActive(sessionId)) {
|
|
198571
|
+
sessionLog(sessionId, "historian failure notice skipped after session context cleared");
|
|
198572
|
+
return;
|
|
198573
|
+
}
|
|
198574
|
+
sendCtxStatusMessage(pi, {
|
|
198575
|
+
title: "Magic Context",
|
|
198576
|
+
text,
|
|
198577
|
+
level: "warning"
|
|
198578
|
+
});
|
|
198579
|
+
},
|
|
197747
198580
|
onPublished: () => {
|
|
197748
198581
|
const sessionStillActive = isContextHandlerSessionActive(sessionId);
|
|
197749
198582
|
try {
|
|
@@ -197893,6 +198726,7 @@ function maybeFireHistorian(args) {
|
|
|
197893
198726
|
|
|
197894
198727
|
Historian previously failed ${failureState.failureCount} time(s), so Magic Context is retrying history comparting immediately after restart.`);
|
|
197895
198728
|
spawnPiHistorianRun({
|
|
198729
|
+
pi: args.pi,
|
|
197896
198730
|
ctx,
|
|
197897
198731
|
sessionId,
|
|
197898
198732
|
db,
|
|
@@ -197901,7 +198735,8 @@ Historian previously failed ${failureState.failureCount} time(s), so Magic Conte
|
|
|
197901
198735
|
unregister,
|
|
197902
198736
|
boundarySnapshot,
|
|
197903
198737
|
refreshBoundarySnapshot: resolveRunnablePiBoundarySnapshot,
|
|
197904
|
-
currentContextLimit: boundaryContextLimit
|
|
198738
|
+
currentContextLimit: boundaryContextLimit,
|
|
198739
|
+
fallbackModelId: modelKey
|
|
197905
198740
|
});
|
|
197906
198741
|
return;
|
|
197907
198742
|
}
|
|
@@ -197923,6 +198758,7 @@ Historian previously failed ${failureState.failureCount} time(s), so Magic Conte
|
|
|
197923
198758
|
triggered = true;
|
|
197924
198759
|
sessionLog(sessionId, `historian trigger fired (reason=${trigger.reason ?? "unknown"}) usage=${usage.percentage.toFixed(1)}% — spawning subagent`);
|
|
197925
198760
|
spawnPiHistorianRun({
|
|
198761
|
+
pi: args.pi,
|
|
197926
198762
|
ctx,
|
|
197927
198763
|
sessionId,
|
|
197928
198764
|
db,
|
|
@@ -197934,7 +198770,8 @@ Historian previously failed ${failureState.failureCount} time(s), so Magic Conte
|
|
|
197934
198770
|
triggerBoundarySnapshot: trigger.boundarySnapshot
|
|
197935
198771
|
}),
|
|
197936
198772
|
refreshBoundarySnapshot: resolveRunnablePiBoundarySnapshot,
|
|
197937
|
-
currentContextLimit: boundaryContextLimit
|
|
198773
|
+
currentContextLimit: boundaryContextLimit,
|
|
198774
|
+
fallbackModelId: modelKey
|
|
197938
198775
|
});
|
|
197939
198776
|
} catch (err) {
|
|
197940
198777
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -198014,7 +198851,10 @@ async function runPipeline(args) {
|
|
|
198014
198851
|
historyBudgetTokens: args.injection.historyBudgetTokens,
|
|
198015
198852
|
hardSignals: piHardSignals
|
|
198016
198853
|
}, args.db, getCompartments(args.db, args.sessionId)).value : false;
|
|
198017
|
-
const
|
|
198854
|
+
const historianRunning = inFlightHistorian.has(args.sessionId);
|
|
198855
|
+
const bypassHistorianGate = args.forceMaterialization === true || m0HardFoldThisPass;
|
|
198856
|
+
const hasPendingMaterializeSignal = hasPendingMaterialization(args.sessionId);
|
|
198857
|
+
const shouldRunHeuristics = args.heuristics !== undefined && (!historianRunning || bypassHistorianGate) && (args.forceMaterialization === true || hasPendingMaterializeSignal || deferredMaterializeEligible || m0HardFoldThisPass || args.schedulerDecision === "execute" && !alreadyRanHeuristicsThisTurn);
|
|
198018
198858
|
const entryFingerprintByMessageId = buildEntryFingerprintMap(args.messages, stableIdResolver);
|
|
198019
198859
|
adoptPiFallbackTags(args.db, args.sessionId, args.tagger, entryFingerprintByMessageId, {
|
|
198020
198860
|
messages: args.messages,
|
|
@@ -198048,14 +198888,13 @@ async function runPipeline(args) {
|
|
|
198048
198888
|
} catch (err) {
|
|
198049
198889
|
sessionLog(args.sessionId, `commit-detect failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
|
|
198050
198890
|
}
|
|
198051
|
-
const hasPendingMaterializeSignal = hasPendingMaterialization(args.sessionId);
|
|
198052
198891
|
const deferredMaterializationWasPending = deferredMaterializationSessions.has(args.sessionId);
|
|
198053
198892
|
const deferredHistoryRefreshWasPending = deferredHistoryWasPendingAtPassStart;
|
|
198054
198893
|
const pendingOps = getPendingOps(args.db, args.sessionId);
|
|
198055
198894
|
const baseShouldApplyPendingOps = args.schedulerDecision === "execute" || args.forceMaterialization || hasPendingMaterializeSignal || m0HardFoldThisPass;
|
|
198056
198895
|
const deferredMaterialize = canConsumeDeferredLate && deferredMaterializationWasPending;
|
|
198057
198896
|
const deferredHistoryRefresh = canConsumeDeferredLate && deferredHistoryRefreshWasPending;
|
|
198058
|
-
const shouldApplyPendingOps = baseShouldApplyPendingOps || deferredMaterialize;
|
|
198897
|
+
const shouldApplyPendingOps = (baseShouldApplyPendingOps || deferredMaterialize) && (!historianRunning || bypassHistorianGate);
|
|
198059
198898
|
if (shouldApplyPendingOps) {
|
|
198060
198899
|
const applyReason = hasPendingMaterializeSignal ? "explicit_flush" : deferredMaterialize ? "deferred_publication" : args.forceMaterialization ? "force_materialization" : m0HardFoldThisPass && args.schedulerDecision !== "execute" ? `m0_hard_fold (drain folded into known m[0] bust, scheduler=${args.schedulerDecision})` : `scheduler_execute (scheduler=${args.schedulerDecision})`;
|
|
198061
198900
|
sessionLog(args.sessionId, `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${pendingOps.length}, context=${args.contextUsage.percentage.toFixed(1)}%`);
|
|
@@ -198144,6 +198983,7 @@ async function runPipeline(args) {
|
|
|
198144
198983
|
const tHeuristic = performance.now();
|
|
198145
198984
|
heuristicsResult = applyPiHeuristicCleanup(args.sessionId, args.db, targets, args.messages, {
|
|
198146
198985
|
protectedTags: args.protectedTags,
|
|
198986
|
+
staleReduceStripEnabled: args.canUseEmptySentinels,
|
|
198147
198987
|
emergency: args.forceMaterialization === true && args.emergencyCeilingTokens !== undefined && args.emergencyCeilingTokens > 0 ? {
|
|
198148
198988
|
currentTotalInputTokens: args.contextUsage.inputTokens,
|
|
198149
198989
|
ceilingTokens: args.emergencyCeilingTokens
|
|
@@ -198729,7 +199569,7 @@ init_data_path();
|
|
|
198729
199569
|
function historianResponseDumpDir(directory) {
|
|
198730
199570
|
return getProjectMagicContextHistorianDir(directory);
|
|
198731
199571
|
}
|
|
198732
|
-
var
|
|
199572
|
+
var MAX_HISTORIAN_RETRIES2 = 2;
|
|
198733
199573
|
async function runValidatedHistorianPass(args) {
|
|
198734
199574
|
const firstRun = await runHistorianPrompt({
|
|
198735
199575
|
...args,
|
|
@@ -198870,7 +199710,7 @@ async function runHistorianPrompt(args) {
|
|
|
198870
199710
|
});
|
|
198871
199711
|
return { ok: false, error: "Historian could not create its child session." };
|
|
198872
199712
|
}
|
|
198873
|
-
for (let retryIndex = 0;retryIndex <=
|
|
199713
|
+
for (let retryIndex = 0;retryIndex <= MAX_HISTORIAN_RETRIES2; retryIndex += 1) {
|
|
198874
199714
|
try {
|
|
198875
199715
|
await promptSyncWithModelSuggestionRetry(client, {
|
|
198876
199716
|
path: { id: agentSessionId },
|
|
@@ -198885,17 +199725,17 @@ async function runHistorianPrompt(args) {
|
|
|
198885
199725
|
fallbackModels: modelOverride ? undefined : fallbackModels,
|
|
198886
199726
|
callContext: agentId === HISTORIAN_EDITOR_AGENT ? "historian:editor" : "historian"
|
|
198887
199727
|
});
|
|
198888
|
-
sessionLog(parentSessionId, `historian: prompt completed (attempt ${retryIndex + 1}/${
|
|
199728
|
+
sessionLog(parentSessionId, `historian: prompt completed (attempt ${retryIndex + 1}/${MAX_HISTORIAN_RETRIES2 + 1})`);
|
|
198889
199729
|
break;
|
|
198890
199730
|
} catch (error51) {
|
|
198891
199731
|
const errorMsg = getErrorMessage(error51);
|
|
198892
199732
|
sessionLog(parentSessionId, `historian: prompt attempt ${retryIndex + 1} failed: ${errorMsg}`);
|
|
198893
|
-
const shouldRetry = retryIndex <
|
|
199733
|
+
const shouldRetry = retryIndex < MAX_HISTORIAN_RETRIES2 && isTransientHistorianPromptError2(errorMsg);
|
|
198894
199734
|
if (!shouldRetry) {
|
|
198895
199735
|
throw error51;
|
|
198896
199736
|
}
|
|
198897
|
-
const backoffMs2 =
|
|
198898
|
-
sessionLog(parentSessionId, `historian retry ${retryIndex + 1}/${
|
|
199737
|
+
const backoffMs2 = getHistorianRetryBackoffMs2(retryIndex);
|
|
199738
|
+
sessionLog(parentSessionId, `historian retry ${retryIndex + 1}/${MAX_HISTORIAN_RETRIES2} after ${backoffMs2}ms: ${errorMsg}`);
|
|
198899
199739
|
await sleep(backoffMs2);
|
|
198900
199740
|
}
|
|
198901
199741
|
}
|
|
@@ -198988,13 +199828,13 @@ function parseModelOverride(modelId) {
|
|
|
198988
199828
|
}
|
|
198989
199829
|
return { providerID, modelID };
|
|
198990
199830
|
}
|
|
198991
|
-
function
|
|
199831
|
+
function getHistorianRetryBackoffMs2(retryIndex) {
|
|
198992
199832
|
if (retryIndex === 0) {
|
|
198993
199833
|
return 2000 + Math.floor(Math.random() * 1001);
|
|
198994
199834
|
}
|
|
198995
199835
|
return 6000 + Math.floor(Math.random() * 2001);
|
|
198996
199836
|
}
|
|
198997
|
-
function
|
|
199837
|
+
function isTransientHistorianPromptError2(message) {
|
|
198998
199838
|
const normalized = message.toLowerCase();
|
|
198999
199839
|
if (normalized.includes("invalid request") || normalized.includes("bad request") || normalized.includes("unauthorized") || normalized.includes("forbidden") || normalized.includes("authentication") || normalized.includes("auth") || normalized.includes(" 400") || normalized.startsWith("400")) {
|
|
199000
199840
|
return false;
|
|
@@ -199949,6 +200789,15 @@ async function executeContextRecompWithResult(deps, options = {}) {
|
|
|
199949
200789
|
published: false
|
|
199950
200790
|
};
|
|
199951
200791
|
}
|
|
200792
|
+
options.onLeaseAcquired?.();
|
|
200793
|
+
if (isWrapupInProgress(deps.db, sessionId)) {
|
|
200794
|
+
sessionLog(sessionId, "recomp skipped: /ctx-wrapup became active");
|
|
200795
|
+
releaseCompartmentLease(deps.db, sessionId, holderId);
|
|
200796
|
+
return {
|
|
200797
|
+
message: "## Magic Recomp — Skipped\n\n/ctx-wrapup is already compacting this session. Wait for it to finish, then try `/ctx-recomp` again.",
|
|
200798
|
+
published: false
|
|
200799
|
+
};
|
|
200800
|
+
}
|
|
199952
200801
|
const renewal = startLeaseRenewal(deps, holderId);
|
|
199953
200802
|
const runnerDeps = withPublishedCallback({ ...deps, compartmentLeaseHolderId: holderId });
|
|
199954
200803
|
const promise2 = options.range ? executePartialRecompInternal(runnerDeps, options.range) : executeContextRecompInternal(runnerDeps);
|
|
@@ -200238,6 +201087,13 @@ function spawnPiRecompRun(args) {
|
|
|
200238
201087
|
// src/commands/ctx-recomp.ts
|
|
200239
201088
|
var confirmationBySession = new Map;
|
|
200240
201089
|
var RECOMP_CONFIRMATION_WINDOW_MS = 60000;
|
|
201090
|
+
var RECOMP_USAGE = [
|
|
201091
|
+
"Usage:",
|
|
201092
|
+
"- `/ctx-recomp` — full rebuild from message 1 to the protected tail",
|
|
201093
|
+
"- `/ctx-recomp <start>-<end>` — partial rebuild of a message range (e.g. `/ctx-recomp 1-11322`)",
|
|
201094
|
+
"- `/ctx-recomp --upgrade` — upgrade legacy v1 compartments to v2 layout (Wave 3 runner)"
|
|
201095
|
+
].join(`
|
|
201096
|
+
`);
|
|
200241
201097
|
function registerCtxRecompCommand(pi, deps) {
|
|
200242
201098
|
pi.registerCommand("ctx-recomp", {
|
|
200243
201099
|
description: "Rebuild Magic Context compartments from raw Pi session history",
|
|
@@ -200253,21 +201109,30 @@ No active Pi session is available.`,
|
|
|
200253
201109
|
});
|
|
200254
201110
|
return;
|
|
200255
201111
|
}
|
|
200256
|
-
|
|
201112
|
+
const currentDeps = deps.resolveRuntimeDeps?.(ctx) ?? deps;
|
|
201113
|
+
const parsed = parseRecompArgs(args);
|
|
201114
|
+
if (parsed.kind === "error") {
|
|
200257
201115
|
sendCtxStatusMessage(pi, {
|
|
200258
201116
|
title: "/ctx-recomp",
|
|
200259
|
-
text:
|
|
201117
|
+
text: `## Magic Recomp — Invalid Arguments
|
|
201118
|
+
|
|
201119
|
+
${parsed.message}`,
|
|
200260
201120
|
level: "error"
|
|
200261
201121
|
});
|
|
200262
201122
|
return;
|
|
200263
201123
|
}
|
|
200264
|
-
|
|
200265
|
-
if (parsed.kind === "error") {
|
|
201124
|
+
if (parsed.kind === "upgrade") {
|
|
200266
201125
|
sendCtxStatusMessage(pi, {
|
|
200267
201126
|
title: "/ctx-recomp",
|
|
200268
|
-
text:
|
|
200269
|
-
|
|
200270
|
-
|
|
201127
|
+
text: executeRecompUpgradeStub(currentDeps.db, sessionId),
|
|
201128
|
+
level: "info"
|
|
201129
|
+
});
|
|
201130
|
+
return;
|
|
201131
|
+
}
|
|
201132
|
+
if (!currentDeps.historianModel) {
|
|
201133
|
+
sendCtxStatusMessage(pi, {
|
|
201134
|
+
title: "/ctx-recomp",
|
|
201135
|
+
text: "## Magic Recomp\n\n/ctx-recomp is unavailable because `historian.model` is not configured.",
|
|
200271
201136
|
level: "error"
|
|
200272
201137
|
});
|
|
200273
201138
|
return;
|
|
@@ -200277,7 +201142,7 @@ ${parsed.message}`,
|
|
|
200277
201142
|
const confirmation = confirmationBySession.get(sessionId);
|
|
200278
201143
|
const confirmed = confirmation !== undefined && now - confirmation.timestamp < RECOMP_CONFIRMATION_WINDOW_MS && confirmation.argsKey === argsKey;
|
|
200279
201144
|
if (!confirmed) {
|
|
200280
|
-
const warning = buildConfirmationWarning(
|
|
201145
|
+
const warning = buildConfirmationWarning(currentDeps.db, sessionId, parsed);
|
|
200281
201146
|
if (!warning.confirmable)
|
|
200282
201147
|
confirmationBySession.delete(sessionId);
|
|
200283
201148
|
else
|
|
@@ -200289,7 +201154,7 @@ ${parsed.message}`,
|
|
|
200289
201154
|
});
|
|
200290
201155
|
return;
|
|
200291
201156
|
}
|
|
200292
|
-
if (isWrapupInProgress(
|
|
201157
|
+
if (isWrapupInProgress(currentDeps.db, sessionId)) {
|
|
200293
201158
|
sendCtxStatusMessage(pi, {
|
|
200294
201159
|
title: "/ctx-recomp",
|
|
200295
201160
|
text: "## Magic Recomp\n\n/ctx-wrapup is already compacting this session. Wait for it to finish, then try `/ctx-recomp` again.",
|
|
@@ -200323,16 +201188,19 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
|
|
|
200323
201188
|
spawnPiRecompRun({
|
|
200324
201189
|
sessionId,
|
|
200325
201190
|
provider: provider2,
|
|
200326
|
-
onStatusChange: () => updateStatusLine(ctx, {
|
|
201191
|
+
onStatusChange: () => updateStatusLine(ctx, {
|
|
201192
|
+
db: currentDeps.db,
|
|
201193
|
+
projectIdentity: ctx.cwd
|
|
201194
|
+
}),
|
|
200327
201195
|
work: async () => {
|
|
200328
201196
|
const result = await executeContextRecompWithResult({
|
|
200329
201197
|
client: createPiHistorianClient({
|
|
200330
|
-
runner:
|
|
200331
|
-
model:
|
|
200332
|
-
systemPrompt: withContentLanguageDirective(COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT,
|
|
200333
|
-
fallbackModels:
|
|
200334
|
-
timeoutMs:
|
|
200335
|
-
thinkingLevel:
|
|
201198
|
+
runner: currentDeps.runner,
|
|
201199
|
+
model: currentDeps.historianModel,
|
|
201200
|
+
systemPrompt: withContentLanguageDirective(COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT, currentDeps.language, { preserveUserQuotes: true }),
|
|
201201
|
+
fallbackModels: currentDeps.historianFallbacks,
|
|
201202
|
+
timeoutMs: currentDeps.historianTimeoutMs,
|
|
201203
|
+
thinkingLevel: currentDeps.historianThinkingLevel,
|
|
200336
201204
|
directory: ctx.cwd,
|
|
200337
201205
|
accountingSessionId: sessionId,
|
|
200338
201206
|
notify: (text) => {
|
|
@@ -200343,26 +201211,26 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
|
|
|
200343
201211
|
});
|
|
200344
201212
|
}
|
|
200345
201213
|
}),
|
|
200346
|
-
db:
|
|
201214
|
+
db: currentDeps.db,
|
|
200347
201215
|
sessionId,
|
|
200348
|
-
historianChunkTokens:
|
|
201216
|
+
historianChunkTokens: currentDeps.historianChunkTokens,
|
|
200349
201217
|
directory: ctx.cwd,
|
|
200350
|
-
historianTimeoutMs:
|
|
200351
|
-
memoryEnabled:
|
|
200352
|
-
autoPromote:
|
|
201218
|
+
historianTimeoutMs: currentDeps.historianTimeoutMs,
|
|
201219
|
+
memoryEnabled: currentDeps.memoryEnabled,
|
|
201220
|
+
autoPromote: currentDeps.autoPromote,
|
|
200353
201221
|
ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
|
|
200354
|
-
fallbackModels:
|
|
200355
|
-
language:
|
|
201222
|
+
fallbackModels: currentDeps.historianFallbacks,
|
|
201223
|
+
language: currentDeps.language,
|
|
200356
201224
|
fallbackModelId: ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined
|
|
200357
201225
|
}, parsed.kind === "partial" ? { range: parsed.range } : {});
|
|
200358
201226
|
if (result.published) {
|
|
200359
201227
|
try {
|
|
200360
|
-
clearEmergencyRecovery(
|
|
201228
|
+
clearEmergencyRecovery(currentDeps.db, sessionId);
|
|
200361
201229
|
} catch (recoveryError) {
|
|
200362
201230
|
sessionLog(sessionId, `/ctx-recomp: clearEmergencyRecovery failed (continuing): ${describeError(recoveryError).brief}`);
|
|
200363
201231
|
}
|
|
200364
201232
|
try {
|
|
200365
|
-
stagePiRecompMarker({ db:
|
|
201233
|
+
stagePiRecompMarker({ db: currentDeps.db, sessionId, ctx });
|
|
200366
201234
|
} catch (markerError) {
|
|
200367
201235
|
sessionLog(sessionId, `/ctx-recomp: marker staging failed (recomp already published; continuing): ${describeError(markerError).brief}`);
|
|
200368
201236
|
}
|
|
@@ -200383,11 +201251,15 @@ function parseRecompArgs(raw) {
|
|
|
200383
201251
|
const trimmed = raw.trim();
|
|
200384
201252
|
if (trimmed.length === 0)
|
|
200385
201253
|
return { kind: "full" };
|
|
201254
|
+
if (trimmed === "--upgrade")
|
|
201255
|
+
return { kind: "upgrade" };
|
|
200386
201256
|
const match = trimmed.match(/^(\d+)\s*-\s*(\d+)$/);
|
|
200387
201257
|
if (!match) {
|
|
200388
201258
|
return {
|
|
200389
201259
|
kind: "error",
|
|
200390
|
-
message:
|
|
201260
|
+
message: `Invalid /ctx-recomp arguments: \`${trimmed}\`.
|
|
201261
|
+
|
|
201262
|
+
${RECOMP_USAGE}`
|
|
200391
201263
|
};
|
|
200392
201264
|
}
|
|
200393
201265
|
const start = Number.parseInt(match[1], 10);
|
|
@@ -200401,6 +201273,21 @@ function parseRecompArgs(raw) {
|
|
|
200401
201273
|
};
|
|
200402
201274
|
return { kind: "partial", range: { start, end } };
|
|
200403
201275
|
}
|
|
201276
|
+
function executeRecompUpgradeStub(db, sessionId) {
|
|
201277
|
+
const legacyCount = getCompartments(db, sessionId).filter((compartment) => compartment.legacy === 1).length;
|
|
201278
|
+
if (legacyCount === 0) {
|
|
201279
|
+
return `## Magic Recomp Upgrade
|
|
201280
|
+
|
|
201281
|
+
Nothing to upgrade: this session has no legacy compartments.`;
|
|
201282
|
+
}
|
|
201283
|
+
return [
|
|
201284
|
+
"## Magic Recomp Upgrade",
|
|
201285
|
+
"",
|
|
201286
|
+
`Found ${legacyCount} legacy compartment${legacyCount === 1 ? "" : "s"} for this session.`,
|
|
201287
|
+
"The `--upgrade` flag is deprecated. Run `/ctx-session-upgrade` to upgrade this session."
|
|
201288
|
+
].join(`
|
|
201289
|
+
`);
|
|
201290
|
+
}
|
|
200404
201291
|
function buildConfirmationWarning(db, sessionId, parsed) {
|
|
200405
201292
|
const compartments = getCompartments(db, sessionId);
|
|
200406
201293
|
if (parsed.kind === "partial") {
|
|
@@ -200680,7 +201567,8 @@ No active Pi session is available.`,
|
|
|
200680
201567
|
});
|
|
200681
201568
|
return;
|
|
200682
201569
|
}
|
|
200683
|
-
|
|
201570
|
+
const currentDeps = deps.resolveRuntimeDeps?.(ctx) ?? deps;
|
|
201571
|
+
if (!currentDeps.historianModel) {
|
|
200684
201572
|
sendCtxStatusMessage(pi, {
|
|
200685
201573
|
title: "/ctx-session-upgrade",
|
|
200686
201574
|
text: "## Session Upgrade\n\nUnavailable because `historian.model` is not configured.",
|
|
@@ -200688,7 +201576,7 @@ No active Pi session is available.`,
|
|
|
200688
201576
|
});
|
|
200689
201577
|
return;
|
|
200690
201578
|
}
|
|
200691
|
-
if (isWrapupInProgress(
|
|
201579
|
+
if (isWrapupInProgress(currentDeps.db, sessionId)) {
|
|
200692
201580
|
sendCtxStatusMessage(pi, {
|
|
200693
201581
|
title: "/ctx-session-upgrade",
|
|
200694
201582
|
text: `## Session Upgrade
|
|
@@ -200708,27 +201596,27 @@ An upgrade or recomp is already running for this session in the background. Wait
|
|
|
200708
201596
|
});
|
|
200709
201597
|
return;
|
|
200710
201598
|
}
|
|
200711
|
-
const compartments = getCompartments(
|
|
201599
|
+
const compartments = getCompartments(currentDeps.db, sessionId);
|
|
200712
201600
|
const upgradableCount = compartments.filter((c) => c.legacy === 1 || !c.p1 || c.p1.trim() === "").length;
|
|
200713
201601
|
const sessionMainModel = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
|
|
200714
|
-
const migrationEnabled =
|
|
201602
|
+
const migrationEnabled = currentDeps.memoryEnabled;
|
|
200715
201603
|
const runMigration = async () => {
|
|
200716
201604
|
if (!migrationEnabled) {
|
|
200717
201605
|
return "Memory migration skipped (memory disabled).";
|
|
200718
201606
|
}
|
|
200719
201607
|
try {
|
|
200720
201608
|
const outcome = await runPiMemoryMigration({
|
|
200721
|
-
db:
|
|
200722
|
-
runner:
|
|
201609
|
+
db: currentDeps.db,
|
|
201610
|
+
runner: currentDeps.runner,
|
|
200723
201611
|
primaryModel: sessionMainModel,
|
|
200724
|
-
model:
|
|
200725
|
-
fallbackModels:
|
|
200726
|
-
timeoutMs:
|
|
200727
|
-
thinkingLevel:
|
|
201612
|
+
model: currentDeps.historianModel,
|
|
201613
|
+
fallbackModels: currentDeps.historianFallbacks,
|
|
201614
|
+
timeoutMs: currentDeps.historianTimeoutMs,
|
|
201615
|
+
thinkingLevel: currentDeps.historianThinkingLevel,
|
|
200728
201616
|
directory: ctx.cwd,
|
|
200729
201617
|
sessionId,
|
|
200730
|
-
userMemoriesEnabled:
|
|
200731
|
-
language:
|
|
201618
|
+
userMemoriesEnabled: currentDeps.userMemoriesEnabled,
|
|
201619
|
+
language: currentDeps.language
|
|
200732
201620
|
});
|
|
200733
201621
|
return outcome.summary;
|
|
200734
201622
|
} catch (error51) {
|
|
@@ -200737,7 +201625,7 @@ An upgrade or recomp is already running for this session in the background. Wait
|
|
|
200737
201625
|
};
|
|
200738
201626
|
if (upgradableCount === 0) {
|
|
200739
201627
|
const projectPath = resolveProjectIdentity(ctx.cwd);
|
|
200740
|
-
const migrationPending = migrationEnabled && !isMemoryMigrationDone(
|
|
201628
|
+
const migrationPending = migrationEnabled && !isMemoryMigrationDone(currentDeps.db, projectPath);
|
|
200741
201629
|
if (!migrationPending) {
|
|
200742
201630
|
sendCtxStatusMessage(pi, {
|
|
200743
201631
|
title: "/ctx-session-upgrade",
|
|
@@ -200763,7 +201651,10 @@ Compartments are already current. Re-organizing project memories. This may take
|
|
|
200763
201651
|
provider: {
|
|
200764
201652
|
readMessages: () => readPiSessionMessages(ctx)
|
|
200765
201653
|
},
|
|
200766
|
-
onStatusChange: () => updateStatusLine(ctx, {
|
|
201654
|
+
onStatusChange: () => updateStatusLine(ctx, {
|
|
201655
|
+
db: currentDeps.db,
|
|
201656
|
+
projectIdentity: ctx.cwd
|
|
201657
|
+
}),
|
|
200767
201658
|
work: async () => {
|
|
200768
201659
|
const summary = await runMigration();
|
|
200769
201660
|
sendCtxStatusMessage(pi, {
|
|
@@ -200789,35 +201680,38 @@ Rebuilding compartments into the v2 format and re-organizing project memories. T
|
|
|
200789
201680
|
spawnPiRecompRun({
|
|
200790
201681
|
sessionId,
|
|
200791
201682
|
provider: provider2,
|
|
200792
|
-
onStatusChange: () => updateStatusLine(ctx, {
|
|
201683
|
+
onStatusChange: () => updateStatusLine(ctx, {
|
|
201684
|
+
db: currentDeps.db,
|
|
201685
|
+
projectIdentity: ctx.cwd
|
|
201686
|
+
}),
|
|
200793
201687
|
work: async () => {
|
|
200794
201688
|
const recompResult = await executeContextRecompWithResult({
|
|
200795
201689
|
client: createPiHistorianClient({
|
|
200796
|
-
runner:
|
|
200797
|
-
model:
|
|
200798
|
-
fallbackModels:
|
|
200799
|
-
timeoutMs:
|
|
200800
|
-
thinkingLevel:
|
|
201690
|
+
runner: currentDeps.runner,
|
|
201691
|
+
model: currentDeps.historianModel,
|
|
201692
|
+
fallbackModels: currentDeps.historianFallbacks,
|
|
201693
|
+
timeoutMs: currentDeps.historianTimeoutMs,
|
|
201694
|
+
thinkingLevel: currentDeps.historianThinkingLevel,
|
|
200801
201695
|
directory: ctx.cwd,
|
|
200802
201696
|
accountingSessionId: sessionId,
|
|
200803
|
-
systemPrompt: withContentLanguageDirective(COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT,
|
|
201697
|
+
systemPrompt: withContentLanguageDirective(COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT, currentDeps.language, { preserveUserQuotes: true }),
|
|
200804
201698
|
notify: (text) => sendCtxStatusMessage(pi, {
|
|
200805
201699
|
title: "/ctx-session-upgrade",
|
|
200806
201700
|
text,
|
|
200807
201701
|
level: "info"
|
|
200808
201702
|
})
|
|
200809
201703
|
}),
|
|
200810
|
-
db:
|
|
201704
|
+
db: currentDeps.db,
|
|
200811
201705
|
sessionId,
|
|
200812
|
-
historianChunkTokens:
|
|
201706
|
+
historianChunkTokens: currentDeps.historianChunkTokens,
|
|
200813
201707
|
directory: ctx.cwd,
|
|
200814
|
-
historianTimeoutMs:
|
|
200815
|
-
memoryEnabled:
|
|
200816
|
-
autoPromote:
|
|
201708
|
+
historianTimeoutMs: currentDeps.historianTimeoutMs,
|
|
201709
|
+
memoryEnabled: currentDeps.memoryEnabled,
|
|
201710
|
+
autoPromote: currentDeps.autoPromote,
|
|
200817
201711
|
ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
|
|
200818
|
-
fallbackModels:
|
|
201712
|
+
fallbackModels: currentDeps.historianFallbacks,
|
|
200819
201713
|
fallbackModelId: sessionMainModel,
|
|
200820
|
-
language:
|
|
201714
|
+
language: currentDeps.language
|
|
200821
201715
|
}, {});
|
|
200822
201716
|
if (!recompResult.published || !isRecompComplete(recompResult.message)) {
|
|
200823
201717
|
const reason = contextualizeUpgradeReason(isRecompFailure(recompResult.message) ? extractRecompReason(recompResult.message) : `Compartments were not fully rebuilt: ${extractRecompReason(recompResult.message)}`);
|
|
@@ -200831,7 +201725,7 @@ ${reason}`,
|
|
|
200831
201725
|
return;
|
|
200832
201726
|
}
|
|
200833
201727
|
try {
|
|
200834
|
-
stagePiRecompMarker({ db:
|
|
201728
|
+
stagePiRecompMarker({ db: currentDeps.db, sessionId, ctx });
|
|
200835
201729
|
} catch (markerError) {
|
|
200836
201730
|
sessionLog(sessionId, `pi /ctx-session-upgrade marker staging failed (non-fatal, recomp already published): ${describeError(markerError).brief}`);
|
|
200837
201731
|
}
|
|
@@ -201029,7 +201923,7 @@ function formatThresholdPercent(value) {
|
|
|
201029
201923
|
// package.json
|
|
201030
201924
|
var package_default = {
|
|
201031
201925
|
name: "@cortexkit/pi-magic-context",
|
|
201032
|
-
version: "0.31.
|
|
201926
|
+
version: "0.31.4",
|
|
201033
201927
|
type: "module",
|
|
201034
201928
|
description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
|
|
201035
201929
|
main: "dist/index.js",
|
|
@@ -201180,6 +202074,7 @@ function renderInner(s, theme, innerWidth) {
|
|
|
201180
202074
|
lines.push(`${theme.fg("accent", theme.bold("⚡ Magic Context Status"))} ${theme.fg("muted", `v${package_default.version}`)}`);
|
|
201181
202075
|
lines.push("");
|
|
201182
202076
|
lines.push(`Context ${theme.fg(pctColor, theme.bold(`${s.usagePercentage.toFixed(1)}%`))} · ${fmt2(s.inputTokens)} / ${s.contextLimit > 0 ? fmt2(s.contextLimit) : "?"} tokens`);
|
|
202077
|
+
lines.push(`Work tokens ${fmt2(s.newWorkTokens)} new · ${fmt2(s.totalInputTokens)} total input`);
|
|
201183
202078
|
lines.push(renderBar(s, innerWidth));
|
|
201184
202079
|
for (const seg of breakdownSegments(s)) {
|
|
201185
202080
|
const pct = (seg.tokens / (s.inputTokens || 1) * 100).toFixed(1);
|
|
@@ -201275,6 +202170,7 @@ ${safeStringify(tool.parameters)}`);
|
|
|
201275
202170
|
}
|
|
201276
202171
|
} catch {}
|
|
201277
202172
|
const conversationTokens = Math.max(0, inputTokens - systemPromptTokens - compartmentTokens - factTokens - memoryTokens - docsTokens - profileTokens - toolCallTokens - toolDefinitionTokens);
|
|
202173
|
+
const workMetrics = getSessionWorkMetrics(deps.db, sessionId);
|
|
201278
202174
|
const modelKey = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
|
|
201279
202175
|
const threshold = resolveExecuteThresholdDetail(deps.executeThresholdPercentage ?? 65, modelKey, 65, {
|
|
201280
202176
|
tokensConfig: deps.executeThresholdTokens,
|
|
@@ -201338,6 +202234,8 @@ ${safeStringify(tool.parameters)}`);
|
|
|
201338
202234
|
conversationTokens,
|
|
201339
202235
|
toolCallTokens,
|
|
201340
202236
|
toolDefinitionTokens,
|
|
202237
|
+
newWorkTokens: workMetrics.newWorkTokens,
|
|
202238
|
+
totalInputTokens: workMetrics.totalInputTokens,
|
|
201341
202239
|
upgradeNeededCount: safeRead(() => countCompartmentsNeedingUpgrade(deps.db, sessionId), 0),
|
|
201342
202240
|
recompInFlight: isPiRecompInFlight(sessionId)
|
|
201343
202241
|
};
|
|
@@ -201492,8 +202390,9 @@ function registerCtxStatusCommand(pi, deps) {
|
|
|
201492
202390
|
pi.registerCommand("ctx-status", {
|
|
201493
202391
|
description: "Show Magic Context status for the current Pi session",
|
|
201494
202392
|
handler: async (_args, ctx) => {
|
|
201495
|
-
const
|
|
201496
|
-
const
|
|
202393
|
+
const runtimeDeps = deps.resolveStatusDeps?.(ctx) ?? deps;
|
|
202394
|
+
const projectIdentity = runtimeDeps.resolveProject?.(ctx).projectIdentity ?? runtimeDeps.projectIdentity;
|
|
202395
|
+
const currentDeps = { ...runtimeDeps, projectIdentity };
|
|
201497
202396
|
const sessionId = resolveSessionId(ctx);
|
|
201498
202397
|
if (!sessionId) {
|
|
201499
202398
|
sendCtxStatusMessage(pi, {
|
|
@@ -201574,6 +202473,11 @@ function readHistorianState(db, sessionId, meta3) {
|
|
|
201574
202473
|
import * as crypto4 from "node:crypto";
|
|
201575
202474
|
var DEFAULT_MESSAGES_TO_KEEP = 20;
|
|
201576
202475
|
var LEASE_WAIT_MS = 1000;
|
|
202476
|
+
var MAX_WRAPUP_LEASE_WAIT_MS = 10 * 60 * 1000;
|
|
202477
|
+
function resolveWrapupLeaseWaitTimeout(deps) {
|
|
202478
|
+
const configured = deps.wrapupLeaseWaitTimeoutMs ?? MAX_WRAPUP_LEASE_WAIT_MS;
|
|
202479
|
+
return Number.isFinite(configured) && configured >= 0 ? configured : MAX_WRAPUP_LEASE_WAIT_MS;
|
|
202480
|
+
}
|
|
201577
202481
|
function parseWrapupArgs(raw) {
|
|
201578
202482
|
const trimmed = raw.trim();
|
|
201579
202483
|
if (trimmed === "")
|
|
@@ -201608,7 +202512,8 @@ No active Pi session is available.`,
|
|
|
201608
202512
|
});
|
|
201609
202513
|
return;
|
|
201610
202514
|
}
|
|
201611
|
-
const
|
|
202515
|
+
const currentDeps = deps.resolveRuntimeDeps?.(ctx) ?? deps;
|
|
202516
|
+
const sessionMeta = getOrCreateSessionMeta(currentDeps.db, sessionId);
|
|
201612
202517
|
if (sessionMeta.isSubagent) {
|
|
201613
202518
|
sendCtxStatusMessage(pi, {
|
|
201614
202519
|
title: "/ctx-wrapup",
|
|
@@ -201619,7 +202524,7 @@ No active Pi session is available.`,
|
|
|
201619
202524
|
});
|
|
201620
202525
|
return;
|
|
201621
202526
|
}
|
|
201622
|
-
if (!
|
|
202527
|
+
if (!currentDeps.historianModel) {
|
|
201623
202528
|
sendCtxStatusMessage(pi, {
|
|
201624
202529
|
title: "/ctx-wrapup",
|
|
201625
202530
|
text: "## Magic Wrapup\n\n/ctx-wrapup is unavailable because `historian.model` is not configured.",
|
|
@@ -201638,7 +202543,7 @@ ${parsed.message}`,
|
|
|
201638
202543
|
});
|
|
201639
202544
|
return;
|
|
201640
202545
|
}
|
|
201641
|
-
const result = await runPiWrapup(pi,
|
|
202546
|
+
const result = await runPiWrapup(pi, currentDeps, ctx, sessionId, parsed.messagesToKeep);
|
|
201642
202547
|
sendCtxStatusMessage(pi, {
|
|
201643
202548
|
title: "/ctx-wrapup",
|
|
201644
202549
|
text: result,
|
|
@@ -201790,11 +202695,12 @@ Eligible history is about ${initialPlan.snapshot.trueRawEligibleTokens.toLocaleS
|
|
|
201790
202695
|
Chunk ${chunkIndex}: wrapping messages ${plan.snapshot.offset}-${plan.snapshot.eligibleEndOrdinal - 1} (~${plan.snapshot.trueRawEligibleTokens.toLocaleString()} eligible tokens remain).`,
|
|
201791
202696
|
level: "info"
|
|
201792
202697
|
});
|
|
201793
|
-
const
|
|
201794
|
-
if (!
|
|
201795
|
-
failure = ownershipLost ? `${ownershipLostReason}; wrapped up through message ${lastEnd}. Run /ctx-wrapup again to continue.` : "Another Magic Context rebuild started while wrapup was waiting. Run /ctx-wrapup again to continue.";
|
|
202698
|
+
const leaseResult = await acquireCompartmentLeaseEventually(deps.db, sessionId, renewWrapupMarker, resolveWrapupLeaseWaitTimeout(deps));
|
|
202699
|
+
if (!leaseResult.ok) {
|
|
202700
|
+
failure = ownershipLost ? `${ownershipLostReason}; wrapped up through message ${lastEnd}. Run /ctx-wrapup again to continue.` : leaseResult.reason === "timeout" ? `Timed out waiting for another process to release the compartment-state lease; wrapped up through message ${lastEnd}. Run /ctx-wrapup again to continue.` : "Another Magic Context rebuild started while wrapup was waiting. Run /ctx-wrapup again to continue.";
|
|
201796
202701
|
break;
|
|
201797
202702
|
}
|
|
202703
|
+
const leaseHolder = leaseResult.holderId;
|
|
201798
202704
|
const leaseRenewal = setInterval(() => {
|
|
201799
202705
|
try {
|
|
201800
202706
|
renewCompartmentLease(deps.db, sessionId, leaseHolder);
|
|
@@ -201812,6 +202718,7 @@ Chunk ${chunkIndex}: wrapping messages ${plan.snapshot.offset}-${plan.snapshot.e
|
|
|
201812
202718
|
runner: deps.runner,
|
|
201813
202719
|
historianModel: deps.historianModel,
|
|
201814
202720
|
fallbackModels: deps.historianFallbacks,
|
|
202721
|
+
fallbackModelId: modelKey,
|
|
201815
202722
|
historianChunkTokens: deps.historianChunkTokens,
|
|
201816
202723
|
boundarySnapshot: plan.snapshot,
|
|
201817
202724
|
refreshBoundarySnapshot: () => resolveWrapupProtectedTailBoundary({
|
|
@@ -201897,15 +202804,19 @@ function resolvePiContextLimit(ctx) {
|
|
|
201897
202804
|
}
|
|
201898
202805
|
return 128000;
|
|
201899
202806
|
}
|
|
201900
|
-
async function acquireCompartmentLeaseEventually(db, sessionId, renewWrapupMarker) {
|
|
202807
|
+
async function acquireCompartmentLeaseEventually(db, sessionId, renewWrapupMarker, maxWaitMs) {
|
|
202808
|
+
const waitStartedAt = Date.now();
|
|
202809
|
+
const remainingMs = () => Math.max(0, waitStartedAt + maxWaitMs - Date.now());
|
|
201901
202810
|
for (;; ) {
|
|
202811
|
+
if (remainingMs() <= 0)
|
|
202812
|
+
return { ok: false, reason: "timeout" };
|
|
201902
202813
|
const holderId = crypto4.randomUUID();
|
|
201903
202814
|
const lease = acquireCompartmentLease(db, sessionId, holderId);
|
|
201904
202815
|
if (lease)
|
|
201905
|
-
return holderId;
|
|
202816
|
+
return { ok: true, holderId };
|
|
201906
202817
|
if (!renewWrapupMarker({}))
|
|
201907
|
-
return
|
|
201908
|
-
await new Promise((resolve3) => setTimeout(resolve3, LEASE_WAIT_MS));
|
|
202818
|
+
return { ok: false, reason: "ownership_lost" };
|
|
202819
|
+
await new Promise((resolve3) => setTimeout(resolve3, Math.min(LEASE_WAIT_MS, remainingMs())));
|
|
201909
202820
|
}
|
|
201910
202821
|
}
|
|
201911
202822
|
function estimateChunks(tokens, chunkTokens) {
|
|
@@ -206393,16 +207304,16 @@ __export(exports_typebox, {
|
|
|
206393
207304
|
// src/tools/ctx-expand.ts
|
|
206394
207305
|
var ParamsSchema = exports_typebox.Object({
|
|
206395
207306
|
start: exports_typebox.Optional(exports_typebox.Number({
|
|
206396
|
-
description:
|
|
207307
|
+
description: `First message ordinal to expand — a compartment's start="N" attribute, or an ordinal from a ctx_search message hit`
|
|
206397
207308
|
})),
|
|
206398
207309
|
end: exports_typebox.Optional(exports_typebox.Number({
|
|
206399
|
-
description:
|
|
207310
|
+
description: `Last message ordinal to expand (inclusive) — a compartment's end="M" attribute`
|
|
206400
207311
|
})),
|
|
206401
207312
|
verbose: exports_typebox.Optional(exports_typebox.Boolean({
|
|
206402
207313
|
description: "With start/end: list each message separately with its ordinal [N] and per-part preview, so you can recover one in full by ordinal."
|
|
206403
207314
|
})),
|
|
206404
207315
|
message: exports_typebox.Optional(exports_typebox.Number({
|
|
206405
|
-
description: "Full untruncated recovery of ONE message by its ordinal (text + every tool call's
|
|
207316
|
+
description: "Full untruncated recovery of ONE message by its ordinal (every text part + every tool call's complete input/output). Use an ordinal from a compartment, ctx_search hit, or verbose range. Recovers a tool output you dropped with ctx_reduce."
|
|
206406
207317
|
}))
|
|
206407
207318
|
});
|
|
206408
207319
|
function ok(text) {
|
|
@@ -206553,25 +207464,45 @@ function formatMemoryList(memories) {
|
|
|
206553
207464
|
id: String(m.id),
|
|
206554
207465
|
category: m.category,
|
|
206555
207466
|
status: m.status,
|
|
207467
|
+
verification: m.verificationStatus,
|
|
206556
207468
|
updated: new Date(m.updatedAt).toISOString(),
|
|
206557
207469
|
content: m.content.replace(/\s+/g, " ").trim()
|
|
206558
207470
|
}));
|
|
207471
|
+
const headers = {
|
|
207472
|
+
id: "ID",
|
|
207473
|
+
category: "CATEGORY",
|
|
207474
|
+
status: "STATUS",
|
|
207475
|
+
verification: "VERIFY",
|
|
207476
|
+
updated: "UPDATED",
|
|
207477
|
+
content: "CONTENT"
|
|
207478
|
+
};
|
|
206559
207479
|
const widths = {
|
|
206560
|
-
id: Math.max(
|
|
206561
|
-
category: Math.max(
|
|
206562
|
-
status: Math.max(
|
|
206563
|
-
|
|
207480
|
+
id: Math.max(headers.id.length, ...rows.map((r) => r.id.length)),
|
|
207481
|
+
category: Math.max(headers.category.length, ...rows.map((r) => r.category.length)),
|
|
207482
|
+
status: Math.max(headers.status.length, ...rows.map((r) => r.status.length)),
|
|
207483
|
+
verification: Math.max(headers.verification.length, ...rows.map((r) => r.verification.length)),
|
|
207484
|
+
updated: Math.max(headers.updated.length, ...rows.map((r) => r.updated.length))
|
|
206564
207485
|
};
|
|
206565
207486
|
const fmt3 = (r) => [
|
|
206566
207487
|
r.id.padEnd(widths.id),
|
|
206567
207488
|
r.category.padEnd(widths.category),
|
|
206568
207489
|
r.status.padEnd(widths.status),
|
|
207490
|
+
r.verification.padEnd(widths.verification),
|
|
206569
207491
|
r.updated.padEnd(widths.updated),
|
|
206570
207492
|
r.content
|
|
206571
207493
|
].join(" | ");
|
|
206572
207494
|
return [
|
|
206573
207495
|
`Found ${rows.length} active ${rows.length === 1 ? "memory" : "memories"}:`,
|
|
206574
207496
|
"",
|
|
207497
|
+
fmt3(headers),
|
|
207498
|
+
[
|
|
207499
|
+
"-".repeat(widths.id),
|
|
207500
|
+
"-".repeat(widths.category),
|
|
207501
|
+
"-".repeat(widths.status),
|
|
207502
|
+
"-".repeat(widths.verification),
|
|
207503
|
+
"-".repeat(widths.updated),
|
|
207504
|
+
"-------"
|
|
207505
|
+
].join("-+-"),
|
|
206575
207506
|
...rows.map(fmt3)
|
|
206576
207507
|
].join(`
|
|
206577
207508
|
`);
|
|
@@ -206641,8 +207572,9 @@ function createCtxMemoryTool(deps) {
|
|
|
206641
207572
|
const isOwn = targetIdentityForStoredPath(memory2.projectPath) === projectIdentity;
|
|
206642
207573
|
if (isOwn)
|
|
206643
207574
|
return true;
|
|
206644
|
-
return toolShareCategories
|
|
207575
|
+
return toolShareCategories?.includes(memory2.category) ?? false;
|
|
206645
207576
|
};
|
|
207577
|
+
const memoryOwnedByTool = (memory2) => workspaceIdentitySet.identities.length > 1 ? targetIdentityForStoredPath(memory2.projectPath) === projectIdentity : storedPathBelongsToIdentity(memory2.projectPath, projectIdentity);
|
|
206646
207578
|
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
206647
207579
|
if (snapshot ? !snapshot.features.memoryEnabled : deps.memoryEnabled === false) {
|
|
206648
207580
|
return err2("Cross-session memory is disabled for this project.");
|
|
@@ -206689,7 +207621,8 @@ function createCtxMemoryTool(deps) {
|
|
|
206689
207621
|
return err2("Error: 'content' is required when action is 'update'.");
|
|
206690
207622
|
}
|
|
206691
207623
|
const memory2 = getMemoryById(deps.db, updateId);
|
|
206692
|
-
|
|
207624
|
+
const updateAllowed = memory2 ? dreamerAllowed ? memoryVisibleToTool(memory2) : memoryOwnedByTool(memory2) : false;
|
|
207625
|
+
if (!memory2 || !updateAllowed) {
|
|
206693
207626
|
return err2(`Error: Memory with ID ${updateId} was not found.`);
|
|
206694
207627
|
}
|
|
206695
207628
|
if (!dreamerAllowed && !isPrimaryMutableMemory(memory2)) {
|
|
@@ -206736,7 +207669,7 @@ function createCtxMemoryTool(deps) {
|
|
|
206736
207669
|
return err2("Error: One or more source memories were not found.");
|
|
206737
207670
|
}
|
|
206738
207671
|
if (!dreamerAllowed) {
|
|
206739
|
-
const foreign = sourceMemories.find((memory2) => !
|
|
207672
|
+
const foreign = sourceMemories.find((memory2) => !memoryOwnedByTool(memory2));
|
|
206740
207673
|
if (foreign) {
|
|
206741
207674
|
return err2(`Error: Memory with ID ${foreign.id} was not found.`);
|
|
206742
207675
|
}
|
|
@@ -206838,7 +207771,8 @@ function createCtxMemoryTool(deps) {
|
|
|
206838
207771
|
const archiveIds = [...new Set(rawArchiveIds)];
|
|
206839
207772
|
for (const memoryId of archiveIds) {
|
|
206840
207773
|
const memory2 = getMemoryById(deps.db, memoryId);
|
|
206841
|
-
|
|
207774
|
+
const archiveAllowed = memory2 ? dreamerAllowed ? memoryVisibleToTool(memory2) : memoryOwnedByTool(memory2) : false;
|
|
207775
|
+
if (!memory2 || !archiveAllowed) {
|
|
206842
207776
|
return err2(`Error: Memory with ID ${memoryId} was not found.`);
|
|
206843
207777
|
}
|
|
206844
207778
|
if (!dreamerAllowed && !isPrimaryMutableMemory(memory2)) {
|
|
@@ -206977,6 +207911,7 @@ function createCtxNoteTool(deps) {
|
|
|
206977
207911
|
parameters: ParamsSchema3,
|
|
206978
207912
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
206979
207913
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
207914
|
+
const dreamerEnabled = deps.resolveDreamerEnabled?.(ctx) ?? deps.dreamerEnabled;
|
|
206980
207915
|
const action2 = params.action ?? (params.content?.trim() ? "write" : "read");
|
|
206981
207916
|
if (action2 === "write") {
|
|
206982
207917
|
const content = params.content?.trim();
|
|
@@ -206985,7 +207920,7 @@ function createCtxNoteTool(deps) {
|
|
|
206985
207920
|
const anchorOrdinal = captureAnchorOrdinal(deps.db, sessionId);
|
|
206986
207921
|
const surfaceCondition = params.surface_condition?.trim();
|
|
206987
207922
|
if (surfaceCondition) {
|
|
206988
|
-
if (
|
|
207923
|
+
if (dreamerEnabled !== true) {
|
|
206989
207924
|
return err3("Error: Smart notes require dreamer to be enabled. Enable dreamer in magic-context.jsonc to use surface_condition.");
|
|
206990
207925
|
}
|
|
206991
207926
|
const projectIdentity = resolveProjectIdentity(ctx.cwd);
|
|
@@ -206994,6 +207929,7 @@ function createCtxNoteTool(deps) {
|
|
|
206994
207929
|
}
|
|
206995
207930
|
const note2 = addNote(deps.db, "smart", {
|
|
206996
207931
|
content,
|
|
207932
|
+
sessionId,
|
|
206997
207933
|
projectPath: projectIdentity,
|
|
206998
207934
|
surfaceCondition,
|
|
206999
207935
|
anchorOrdinal
|
|
@@ -207245,6 +208181,7 @@ function createCtxReduceTool(deps) {
|
|
|
207245
208181
|
parameters: ParamsSchema4,
|
|
207246
208182
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
207247
208183
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
208184
|
+
const protectedTags = Math.max(0, Math.floor(deps.resolveProtectedTags?.(ctx) ?? deps.protectedTags));
|
|
207248
208185
|
if (!params.drop) {
|
|
207249
208186
|
return err4("Error: 'drop' must be provided.");
|
|
207250
208187
|
}
|
|
@@ -207262,7 +208199,7 @@ function createCtxReduceTool(deps) {
|
|
|
207262
208199
|
return err4(`Error: Unknown tag(s) ${formatIds(unknownIds)}. Check available tags in conversation.`);
|
|
207263
208200
|
}
|
|
207264
208201
|
const activeTags = allTags.filter((tag) => tag.status === "active");
|
|
207265
|
-
const protectedTagIds = activeTags.map((tag) => tag.tagNumber).sort((left, right) => right - left).slice(0,
|
|
208202
|
+
const protectedTagIds = activeTags.map((tag) => tag.tagNumber).sort((left, right) => right - left).slice(0, protectedTags);
|
|
207266
208203
|
const protectedSet = new Set(protectedTagIds);
|
|
207267
208204
|
const tagStatusMap = new Map(allTags.map((tag) => [tag.tagNumber, tag.status]));
|
|
207268
208205
|
const pendingOps = getPendingOps(deps.db, sessionId);
|
|
@@ -207489,18 +208426,430 @@ function createCtxSearchTool(deps) {
|
|
|
207489
208426
|
};
|
|
207490
208427
|
}
|
|
207491
208428
|
|
|
208429
|
+
// src/tools/todo-view-pi.ts
|
|
208430
|
+
import {
|
|
208431
|
+
truncateToWidth as truncateToWidth2
|
|
208432
|
+
} from "@earendil-works/pi-tui";
|
|
208433
|
+
var TODO_TOOL_NAME = "todowrite";
|
|
208434
|
+
var TODOS_COMMAND_NAME = "todos";
|
|
208435
|
+
var WIDGET_KEY = "magic-context-todos";
|
|
208436
|
+
var MAX_TODO_CONTENT_ROWS = 12;
|
|
208437
|
+
var STATUS_GLYPH = {
|
|
208438
|
+
pending: "○",
|
|
208439
|
+
in_progress: "◐",
|
|
208440
|
+
completed: "✓",
|
|
208441
|
+
cancelled: "✗"
|
|
208442
|
+
};
|
|
208443
|
+
var STATUS_COLOR = {
|
|
208444
|
+
pending: "dim",
|
|
208445
|
+
in_progress: "warning",
|
|
208446
|
+
completed: "success",
|
|
208447
|
+
cancelled: "error"
|
|
208448
|
+
};
|
|
208449
|
+
var snapshotsBySession = new Map;
|
|
208450
|
+
function isTodoStatus2(value) {
|
|
208451
|
+
return TODO_STATUSES.includes(value);
|
|
208452
|
+
}
|
|
208453
|
+
function isTodoPriority2(value) {
|
|
208454
|
+
return TODO_PRIORITIES.includes(value);
|
|
208455
|
+
}
|
|
208456
|
+
function cloneTodos(todos) {
|
|
208457
|
+
return todos.map((todo) => ({ ...todo }));
|
|
208458
|
+
}
|
|
208459
|
+
function parseTodos(input) {
|
|
208460
|
+
if (!Array.isArray(input))
|
|
208461
|
+
return null;
|
|
208462
|
+
const todos = [];
|
|
208463
|
+
for (const item of input) {
|
|
208464
|
+
if (item === null || typeof item !== "object")
|
|
208465
|
+
return null;
|
|
208466
|
+
const raw = item;
|
|
208467
|
+
if (typeof raw.content !== "string" || !isTodoStatus2(raw.status)) {
|
|
208468
|
+
return null;
|
|
208469
|
+
}
|
|
208470
|
+
const todo = {
|
|
208471
|
+
content: raw.content,
|
|
208472
|
+
status: raw.status
|
|
208473
|
+
};
|
|
208474
|
+
if (isTodoPriority2(raw.priority))
|
|
208475
|
+
todo.priority = raw.priority;
|
|
208476
|
+
if (typeof raw.id === "string" && raw.id.length > 0)
|
|
208477
|
+
todo.id = raw.id;
|
|
208478
|
+
todos.push(todo);
|
|
208479
|
+
}
|
|
208480
|
+
return todos;
|
|
208481
|
+
}
|
|
208482
|
+
function parseTodoStateJson(stateJson) {
|
|
208483
|
+
if (!stateJson)
|
|
208484
|
+
return null;
|
|
208485
|
+
try {
|
|
208486
|
+
return parseTodos(JSON.parse(stateJson));
|
|
208487
|
+
} catch {
|
|
208488
|
+
return null;
|
|
208489
|
+
}
|
|
208490
|
+
}
|
|
208491
|
+
function setTodoSnapshot(sessionId, todos) {
|
|
208492
|
+
const parsed = parseTodos(todos);
|
|
208493
|
+
if (parsed === null)
|
|
208494
|
+
return false;
|
|
208495
|
+
snapshotsBySession.set(sessionId, { todos: parsed });
|
|
208496
|
+
return true;
|
|
208497
|
+
}
|
|
208498
|
+
function seedTodoSnapshotFromStateJson(sessionId, stateJson) {
|
|
208499
|
+
const parsed = parseTodoStateJson(stateJson);
|
|
208500
|
+
if (parsed === null) {
|
|
208501
|
+
snapshotsBySession.delete(sessionId);
|
|
208502
|
+
return false;
|
|
208503
|
+
}
|
|
208504
|
+
snapshotsBySession.set(sessionId, { todos: parsed });
|
|
208505
|
+
return true;
|
|
208506
|
+
}
|
|
208507
|
+
function getTodoSnapshot(sessionId) {
|
|
208508
|
+
if (!sessionId)
|
|
208509
|
+
return { todos: [] };
|
|
208510
|
+
const snapshot = snapshotsBySession.get(sessionId);
|
|
208511
|
+
return { todos: snapshot ? cloneTodos(snapshot.todos) : [] };
|
|
208512
|
+
}
|
|
208513
|
+
function clearTodoSnapshot(sessionId) {
|
|
208514
|
+
snapshotsBySession.delete(sessionId);
|
|
208515
|
+
}
|
|
208516
|
+
function getSessionId(ctx) {
|
|
208517
|
+
try {
|
|
208518
|
+
const id = ctx.sessionManager?.getSessionId?.();
|
|
208519
|
+
return typeof id === "string" && id.length > 0 ? id : undefined;
|
|
208520
|
+
} catch {
|
|
208521
|
+
return;
|
|
208522
|
+
}
|
|
208523
|
+
}
|
|
208524
|
+
function countTodos(todos) {
|
|
208525
|
+
const counts = {
|
|
208526
|
+
total: todos.length,
|
|
208527
|
+
pending: 0,
|
|
208528
|
+
inProgress: 0,
|
|
208529
|
+
completed: 0,
|
|
208530
|
+
cancelled: 0
|
|
208531
|
+
};
|
|
208532
|
+
for (const todo of todos) {
|
|
208533
|
+
switch (todo.status) {
|
|
208534
|
+
case "pending":
|
|
208535
|
+
counts.pending++;
|
|
208536
|
+
break;
|
|
208537
|
+
case "in_progress":
|
|
208538
|
+
counts.inProgress++;
|
|
208539
|
+
break;
|
|
208540
|
+
case "completed":
|
|
208541
|
+
counts.completed++;
|
|
208542
|
+
break;
|
|
208543
|
+
case "cancelled":
|
|
208544
|
+
counts.cancelled++;
|
|
208545
|
+
break;
|
|
208546
|
+
}
|
|
208547
|
+
}
|
|
208548
|
+
return counts;
|
|
208549
|
+
}
|
|
208550
|
+
function activeTitleCount(todos) {
|
|
208551
|
+
return todos.filter((todo) => !TITLE_DONE_STATUSES.has(todo.status)).length;
|
|
208552
|
+
}
|
|
208553
|
+
function formatCounts(counts) {
|
|
208554
|
+
if (counts.total === 0)
|
|
208555
|
+
return "No todos";
|
|
208556
|
+
const parts = [`${counts.completed}/${counts.total} completed`];
|
|
208557
|
+
if (counts.inProgress > 0)
|
|
208558
|
+
parts.push(`${counts.inProgress} in progress`);
|
|
208559
|
+
if (counts.pending > 0)
|
|
208560
|
+
parts.push(`${counts.pending} pending`);
|
|
208561
|
+
if (counts.cancelled > 0)
|
|
208562
|
+
parts.push(`${counts.cancelled} cancelled`);
|
|
208563
|
+
return parts.join(" · ");
|
|
208564
|
+
}
|
|
208565
|
+
function formatTodoLine(todo, theme, options = {}) {
|
|
208566
|
+
const glyph = theme.fg(STATUS_COLOR[todo.status], STATUS_GLYPH[todo.status]);
|
|
208567
|
+
const id = options.showId && todo.id ? `${theme.fg("accent", `#${todo.id}`)} ` : "";
|
|
208568
|
+
const color = todo.status === "pending" || todo.status === "in_progress" ? "text" : "dim";
|
|
208569
|
+
let content = theme.fg(color, todo.content);
|
|
208570
|
+
if (todo.status === "completed" || todo.status === "cancelled") {
|
|
208571
|
+
content = theme.strikethrough(content);
|
|
208572
|
+
}
|
|
208573
|
+
return `${glyph} ${id}${content}`;
|
|
208574
|
+
}
|
|
208575
|
+
function formatCommandLine(todo) {
|
|
208576
|
+
const id = todo.id ? `#${todo.id} ` : "";
|
|
208577
|
+
return ` ${STATUS_GLYPH[todo.status]} ${id}${todo.content}`;
|
|
208578
|
+
}
|
|
208579
|
+
function lineComponent(renderLines) {
|
|
208580
|
+
return {
|
|
208581
|
+
render: renderLines,
|
|
208582
|
+
invalidate() {}
|
|
208583
|
+
};
|
|
208584
|
+
}
|
|
208585
|
+
function capTodoRows(rows) {
|
|
208586
|
+
if (rows.length <= MAX_TODO_CONTENT_ROWS) {
|
|
208587
|
+
return { visible: [...rows], hiddenCount: 0 };
|
|
208588
|
+
}
|
|
208589
|
+
const visibleCount = Math.max(0, MAX_TODO_CONTENT_ROWS - 1);
|
|
208590
|
+
return {
|
|
208591
|
+
visible: rows.slice(0, visibleCount),
|
|
208592
|
+
hiddenCount: rows.length - visibleCount
|
|
208593
|
+
};
|
|
208594
|
+
}
|
|
208595
|
+
function renderTodowriteCall(args, theme) {
|
|
208596
|
+
const todos = parseTodos(args.todos) ?? [];
|
|
208597
|
+
const active = activeTitleCount(todos);
|
|
208598
|
+
const activeColor = active > 0 ? "warning" : "success";
|
|
208599
|
+
const line = `${theme.fg("toolTitle", theme.bold("Todos"))} ${theme.fg("muted", "—")} ${theme.fg(activeColor, `${active} active`)}`;
|
|
208600
|
+
return lineComponent((width) => [truncateToWidth2(line, width, "…")]);
|
|
208601
|
+
}
|
|
208602
|
+
function renderTodowriteResult(result, theme) {
|
|
208603
|
+
const detailsTodos = result.details?.todos;
|
|
208604
|
+
let todos = parseTodos(detailsTodos);
|
|
208605
|
+
if (todos === null && Array.isArray(result.content)) {
|
|
208606
|
+
const text = result.content.find((part) => part !== null && typeof part === "object" && part.type === "text" && typeof part.text === "string")?.text;
|
|
208607
|
+
if (text) {
|
|
208608
|
+
try {
|
|
208609
|
+
todos = parseTodos(JSON.parse(text));
|
|
208610
|
+
} catch {}
|
|
208611
|
+
}
|
|
208612
|
+
}
|
|
208613
|
+
const renderedTodos = todos ?? [];
|
|
208614
|
+
return lineComponent((width) => {
|
|
208615
|
+
if (renderedTodos.length === 0)
|
|
208616
|
+
return [theme.fg("dim", "No todos")];
|
|
208617
|
+
const { visible, hiddenCount } = capTodoRows(renderedTodos);
|
|
208618
|
+
const lines = visible.map((todo) => truncateToWidth2(formatTodoLine(todo, theme, { showId: true }), width, "…"));
|
|
208619
|
+
if (hiddenCount > 0) {
|
|
208620
|
+
lines.push(truncateToWidth2(theme.fg("dim", `+${hiddenCount} more`), width, "…"));
|
|
208621
|
+
}
|
|
208622
|
+
return lines;
|
|
208623
|
+
});
|
|
208624
|
+
}
|
|
208625
|
+
function registerTodosCommand(pi) {
|
|
208626
|
+
pi.registerCommand(TODOS_COMMAND_NAME, {
|
|
208627
|
+
description: "Show the current Magic Context todo list",
|
|
208628
|
+
handler: async (_args, ctx) => {
|
|
208629
|
+
if (!ctx.hasUI) {
|
|
208630
|
+
ctx.ui.notify("The /todos command requires interactive UI.", "error");
|
|
208631
|
+
return;
|
|
208632
|
+
}
|
|
208633
|
+
const sessionId = getSessionId(ctx);
|
|
208634
|
+
const todos = getTodoSnapshot(sessionId).todos;
|
|
208635
|
+
if (todos.length === 0) {
|
|
208636
|
+
ctx.ui.notify("No todos yet.", "info");
|
|
208637
|
+
return;
|
|
208638
|
+
}
|
|
208639
|
+
const counts = countTodos(todos);
|
|
208640
|
+
const lines = [formatCounts(counts)];
|
|
208641
|
+
const groups = [
|
|
208642
|
+
["pending", "── Pending ──"],
|
|
208643
|
+
["in_progress", "── In Progress ──"],
|
|
208644
|
+
["completed", "── Completed ──"],
|
|
208645
|
+
["cancelled", "── Cancelled ──"]
|
|
208646
|
+
];
|
|
208647
|
+
for (const [status, heading] of groups) {
|
|
208648
|
+
const group = todos.filter((todo) => todo.status === status);
|
|
208649
|
+
if (group.length === 0)
|
|
208650
|
+
continue;
|
|
208651
|
+
lines.push(heading);
|
|
208652
|
+
const { visible, hiddenCount } = capTodoRows(group);
|
|
208653
|
+
for (const todo of visible)
|
|
208654
|
+
lines.push(formatCommandLine(todo));
|
|
208655
|
+
if (hiddenCount > 0)
|
|
208656
|
+
lines.push(` +${hiddenCount} more`);
|
|
208657
|
+
}
|
|
208658
|
+
ctx.ui.notify(lines.join(`
|
|
208659
|
+
`), "info");
|
|
208660
|
+
}
|
|
208661
|
+
});
|
|
208662
|
+
}
|
|
208663
|
+
function todoKey(todo, index) {
|
|
208664
|
+
return todo.id ? `id:${todo.id}` : `pos:${index}:${todo.content}`;
|
|
208665
|
+
}
|
|
208666
|
+
function isOverlayLive(todo) {
|
|
208667
|
+
return todo.status === "pending" || todo.status === "in_progress";
|
|
208668
|
+
}
|
|
208669
|
+
|
|
208670
|
+
class TodoOverlay {
|
|
208671
|
+
uiCtx;
|
|
208672
|
+
sessionId;
|
|
208673
|
+
widgetRegistered = false;
|
|
208674
|
+
tui;
|
|
208675
|
+
completedTaskIdsPendingHide = new Set;
|
|
208676
|
+
hiddenCompletedTaskIds = new Set;
|
|
208677
|
+
lastTodoKeys = new Set;
|
|
208678
|
+
setUICtx(sessionId, ctx) {
|
|
208679
|
+
if (ctx !== this.uiCtx || sessionId !== this.sessionId) {
|
|
208680
|
+
this.uiCtx = ctx;
|
|
208681
|
+
this.sessionId = sessionId;
|
|
208682
|
+
this.widgetRegistered = false;
|
|
208683
|
+
this.tui = undefined;
|
|
208684
|
+
this.resetCompletedDisplayState();
|
|
208685
|
+
}
|
|
208686
|
+
}
|
|
208687
|
+
update(sessionId = this.sessionId) {
|
|
208688
|
+
if (!this.uiCtx || !this.sessionId || sessionId !== this.sessionId)
|
|
208689
|
+
return;
|
|
208690
|
+
const todos = getTodoSnapshot(this.sessionId).todos;
|
|
208691
|
+
this.pruneCompletedDisplayState(todos);
|
|
208692
|
+
const visible = this.selectOverlayTodos(todos);
|
|
208693
|
+
if (visible.length === 0) {
|
|
208694
|
+
if (this.widgetRegistered) {
|
|
208695
|
+
this.uiCtx.setWidget(WIDGET_KEY, undefined);
|
|
208696
|
+
this.widgetRegistered = false;
|
|
208697
|
+
this.tui = undefined;
|
|
208698
|
+
}
|
|
208699
|
+
return;
|
|
208700
|
+
}
|
|
208701
|
+
if (!this.widgetRegistered) {
|
|
208702
|
+
this.uiCtx.setWidget(WIDGET_KEY, (tui, theme) => {
|
|
208703
|
+
this.tui = tui;
|
|
208704
|
+
return {
|
|
208705
|
+
render: (width) => this.renderWidget(theme, width),
|
|
208706
|
+
invalidate: () => {
|
|
208707
|
+
this.widgetRegistered = false;
|
|
208708
|
+
this.tui = undefined;
|
|
208709
|
+
}
|
|
208710
|
+
};
|
|
208711
|
+
}, { placement: "aboveEditor" });
|
|
208712
|
+
this.widgetRegistered = true;
|
|
208713
|
+
} else {
|
|
208714
|
+
this.tui?.requestRender();
|
|
208715
|
+
}
|
|
208716
|
+
}
|
|
208717
|
+
resetCompletedDisplayState() {
|
|
208718
|
+
this.completedTaskIdsPendingHide.clear();
|
|
208719
|
+
this.hiddenCompletedTaskIds.clear();
|
|
208720
|
+
this.lastTodoKeys.clear();
|
|
208721
|
+
}
|
|
208722
|
+
hideCompletedTasksFromPreviousTurn() {
|
|
208723
|
+
if (this.completedTaskIdsPendingHide.size === 0)
|
|
208724
|
+
return;
|
|
208725
|
+
for (const taskId of this.completedTaskIdsPendingHide) {
|
|
208726
|
+
this.hiddenCompletedTaskIds.add(taskId);
|
|
208727
|
+
}
|
|
208728
|
+
this.completedTaskIdsPendingHide.clear();
|
|
208729
|
+
this.update();
|
|
208730
|
+
}
|
|
208731
|
+
disposeSession(sessionId) {
|
|
208732
|
+
if (this.sessionId !== sessionId)
|
|
208733
|
+
return;
|
|
208734
|
+
this.dispose();
|
|
208735
|
+
}
|
|
208736
|
+
dispose() {
|
|
208737
|
+
if (this.uiCtx)
|
|
208738
|
+
this.uiCtx.setWidget(WIDGET_KEY, undefined);
|
|
208739
|
+
this.widgetRegistered = false;
|
|
208740
|
+
this.tui = undefined;
|
|
208741
|
+
this.uiCtx = undefined;
|
|
208742
|
+
this.sessionId = undefined;
|
|
208743
|
+
this.resetCompletedDisplayState();
|
|
208744
|
+
}
|
|
208745
|
+
pruneCompletedDisplayState(todos) {
|
|
208746
|
+
const currentKeys = new Set(todos.map((todo, index) => todoKey(todo, index)));
|
|
208747
|
+
const hasSharedKeys = [...currentKeys].some((key) => this.lastTodoKeys.has(key));
|
|
208748
|
+
if (this.lastTodoKeys.size > 0 && currentKeys.size > 0 && !hasSharedKeys) {
|
|
208749
|
+
this.resetCompletedDisplayState();
|
|
208750
|
+
}
|
|
208751
|
+
this.lastTodoKeys = currentKeys;
|
|
208752
|
+
const completedKeys = new Set(todos.map((todo, index) => ({ todo, key: todoKey(todo, index) })).filter(({ todo }) => todo.status === "completed").map(({ key }) => key));
|
|
208753
|
+
for (const taskId of this.completedTaskIdsPendingHide) {
|
|
208754
|
+
if (!completedKeys.has(taskId))
|
|
208755
|
+
this.completedTaskIdsPendingHide.delete(taskId);
|
|
208756
|
+
}
|
|
208757
|
+
for (const taskId of this.hiddenCompletedTaskIds) {
|
|
208758
|
+
if (!completedKeys.has(taskId))
|
|
208759
|
+
this.hiddenCompletedTaskIds.delete(taskId);
|
|
208760
|
+
}
|
|
208761
|
+
}
|
|
208762
|
+
selectOverlayTodos(todos) {
|
|
208763
|
+
return todos.map((todo, index) => ({ todo, key: todoKey(todo, index) })).filter(({ todo, key }) => {
|
|
208764
|
+
if (isOverlayLive(todo))
|
|
208765
|
+
return true;
|
|
208766
|
+
return todo.status === "completed" && !this.hiddenCompletedTaskIds.has(key);
|
|
208767
|
+
});
|
|
208768
|
+
}
|
|
208769
|
+
renderWidget(theme, width) {
|
|
208770
|
+
if (!this.sessionId)
|
|
208771
|
+
return [];
|
|
208772
|
+
const todos = getTodoSnapshot(this.sessionId).todos;
|
|
208773
|
+
this.pruneCompletedDisplayState(todos);
|
|
208774
|
+
const overlayTodos = this.selectOverlayTodos(todos);
|
|
208775
|
+
if (overlayTodos.length === 0)
|
|
208776
|
+
return [];
|
|
208777
|
+
const counts = countTodos(todos);
|
|
208778
|
+
const hasInProgress = counts.inProgress > 0;
|
|
208779
|
+
const headingColor = hasInProgress ? "accent" : "dim";
|
|
208780
|
+
const headingIcon = hasInProgress ? "●" : "○";
|
|
208781
|
+
const heading = `${theme.fg(headingColor, headingIcon)} ${theme.fg(headingColor, "Todos")} ${theme.fg("muted", "—")} ${theme.fg("dim", formatCounts(counts))}`;
|
|
208782
|
+
const truncate3 = (line) => truncateToWidth2(line, width, "…");
|
|
208783
|
+
const lines = [truncate3(heading)];
|
|
208784
|
+
const { visible, hiddenCount } = capTodoRows(overlayTodos);
|
|
208785
|
+
for (const [index, { todo, key }] of visible.entries()) {
|
|
208786
|
+
const isLast = index === visible.length - 1 && hiddenCount === 0;
|
|
208787
|
+
const branch = theme.fg("dim", isLast ? "└─" : "├─");
|
|
208788
|
+
lines.push(truncate3(`${branch} ${formatTodoLine(todo, theme, { showId: true })}`));
|
|
208789
|
+
if (todo.status === "completed")
|
|
208790
|
+
this.completedTaskIdsPendingHide.add(key);
|
|
208791
|
+
}
|
|
208792
|
+
if (hiddenCount > 0) {
|
|
208793
|
+
lines.push(truncate3(`${theme.fg("dim", "└─")} ${theme.fg("dim", `+${hiddenCount} more`)}`));
|
|
208794
|
+
}
|
|
208795
|
+
return lines;
|
|
208796
|
+
}
|
|
208797
|
+
}
|
|
208798
|
+
function registerTodoOverlay(pi, deps) {
|
|
208799
|
+
const overlay = new TodoOverlay;
|
|
208800
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
208801
|
+
const sessionId = getSessionId(ctx);
|
|
208802
|
+
if (!sessionId)
|
|
208803
|
+
return;
|
|
208804
|
+
seedTodoSnapshotFromStateJson(sessionId, deps.readLastTodoState(sessionId));
|
|
208805
|
+
if (!ctx.hasUI)
|
|
208806
|
+
return;
|
|
208807
|
+
overlay.setUICtx(sessionId, ctx.ui);
|
|
208808
|
+
overlay.update(sessionId);
|
|
208809
|
+
});
|
|
208810
|
+
pi.on("agent_start", async () => {
|
|
208811
|
+
overlay.hideCompletedTasksFromPreviousTurn();
|
|
208812
|
+
});
|
|
208813
|
+
pi.on("session_shutdown", async (_event, ctx) => {
|
|
208814
|
+
const sessionId = getSessionId(ctx);
|
|
208815
|
+
if (!sessionId)
|
|
208816
|
+
return;
|
|
208817
|
+
clearTodoSnapshot(sessionId);
|
|
208818
|
+
overlay.disposeSession(sessionId);
|
|
208819
|
+
});
|
|
208820
|
+
pi.on("session_before_switch", async (_event, ctx) => {
|
|
208821
|
+
const sessionId = getSessionId(ctx);
|
|
208822
|
+
if (!sessionId)
|
|
208823
|
+
return;
|
|
208824
|
+
clearTodoSnapshot(sessionId);
|
|
208825
|
+
overlay.disposeSession(sessionId);
|
|
208826
|
+
});
|
|
208827
|
+
return overlay;
|
|
208828
|
+
}
|
|
208829
|
+
function registerTodoStateLifecycle(pi, deps) {
|
|
208830
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
208831
|
+
const sessionId = getSessionId(ctx);
|
|
208832
|
+
if (!sessionId)
|
|
208833
|
+
return;
|
|
208834
|
+
seedTodoSnapshotFromStateJson(sessionId, deps.readLastTodoState(sessionId));
|
|
208835
|
+
});
|
|
208836
|
+
pi.on("session_shutdown", async (_event, ctx) => {
|
|
208837
|
+
const sessionId = getSessionId(ctx);
|
|
208838
|
+
if (sessionId)
|
|
208839
|
+
clearTodoSnapshot(sessionId);
|
|
208840
|
+
});
|
|
208841
|
+
pi.on("session_before_switch", async (_event, ctx) => {
|
|
208842
|
+
const sessionId = getSessionId(ctx);
|
|
208843
|
+
if (sessionId)
|
|
208844
|
+
clearTodoSnapshot(sessionId);
|
|
208845
|
+
});
|
|
208846
|
+
}
|
|
208847
|
+
|
|
207492
208848
|
// src/tools/todowrite.ts
|
|
207493
|
-
var STATUS_VALUES = [
|
|
207494
|
-
"pending",
|
|
207495
|
-
"in_progress",
|
|
207496
|
-
"completed",
|
|
207497
|
-
"cancelled"
|
|
207498
|
-
];
|
|
207499
|
-
var PRIORITY_VALUES = ["high", "medium", "low"];
|
|
207500
208849
|
var TodoItem = exports_typebox.Object({
|
|
207501
208850
|
content: exports_typebox.String({ description: "Brief description of the task" }),
|
|
207502
|
-
status: exports_typebox.Union(
|
|
207503
|
-
priority: exports_typebox.Optional(exports_typebox.Union(
|
|
208851
|
+
status: exports_typebox.Union(TODO_STATUSES.map((v) => exports_typebox.Literal(v))),
|
|
208852
|
+
priority: exports_typebox.Optional(exports_typebox.Union(TODO_PRIORITIES.map((v) => exports_typebox.Literal(v)))),
|
|
207504
208853
|
id: exports_typebox.Optional(exports_typebox.String({ description: "Optional stable id for the todo" }))
|
|
207505
208854
|
});
|
|
207506
208855
|
var TodowriteParams = exports_typebox.Object({
|
|
@@ -207508,34 +208857,24 @@ var TodowriteParams = exports_typebox.Object({
|
|
|
207508
208857
|
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."
|
|
207509
208858
|
})
|
|
207510
208859
|
});
|
|
207511
|
-
var
|
|
207512
|
-
|
|
207513
|
-
"",
|
|
207514
|
-
"
|
|
207515
|
-
"mark
|
|
207516
|
-
"
|
|
207517
|
-
|
|
207518
|
-
"",
|
|
207519
|
-
"Task states:",
|
|
207520
|
-
" - pending: not started yet",
|
|
207521
|
-
" - in_progress: currently working on (limit to ONE task at a time)",
|
|
207522
|
-
" - completed: finished successfully",
|
|
207523
|
-
" - cancelled: no longer needed",
|
|
207524
|
-
"",
|
|
207525
|
-
"Use this tool proactively for non-trivial work spanning 3+ steps.",
|
|
207526
|
-
"Skip it for single-shot answers or trivial 1-2 step tasks."
|
|
207527
|
-
].join(`
|
|
207528
|
-
`);
|
|
208860
|
+
var PROMPT_SNIPPET = "Manage a task list to track multi-step progress";
|
|
208861
|
+
var PROMPT_GUIDELINES = [
|
|
208862
|
+
"Use `todowrite` for non-trivial work spanning 3+ steps, when the user gives you multiple tasks, or when you need to track progress across a verify/fix loop. Skip it for single-shot answers or trivial one-step work.",
|
|
208863
|
+
"Pass the COMPLETE updated todo list every time. This tool replaces the prior list rather than appending to it, so include pending, in_progress, completed, and cancelled tasks that should remain visible.",
|
|
208864
|
+
"When starting a task, mark exactly one todo `in_progress` before doing the work. Mark items `completed` immediately when done; use `cancelled` only for work that is no longer needed.",
|
|
208865
|
+
"Never mark a todo completed if verification is failing, implementation is partial, or an unresolved blocker remains. Keep it `in_progress` and add or update a todo for the blocker instead."
|
|
208866
|
+
];
|
|
207529
208867
|
function createTodowriteTool() {
|
|
207530
208868
|
return {
|
|
207531
|
-
name:
|
|
208869
|
+
name: TODO_TOOL_NAME,
|
|
207532
208870
|
label: "Todos",
|
|
207533
|
-
description:
|
|
208871
|
+
description: "Manage the session task list.",
|
|
208872
|
+
promptSnippet: PROMPT_SNIPPET,
|
|
208873
|
+
promptGuidelines: PROMPT_GUIDELINES,
|
|
207534
208874
|
parameters: TodowriteParams,
|
|
207535
208875
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
207536
208876
|
const todos = params.todos ?? [];
|
|
207537
|
-
const
|
|
207538
|
-
const active = todos.length - completed;
|
|
208877
|
+
const active = todos.filter((todo) => !TITLE_DONE_STATUSES.has(todo.status)).length;
|
|
207539
208878
|
return {
|
|
207540
208879
|
content: [
|
|
207541
208880
|
{
|
|
@@ -207549,6 +208888,12 @@ function createTodowriteTool() {
|
|
|
207549
208888
|
truncated: false
|
|
207550
208889
|
}
|
|
207551
208890
|
};
|
|
208891
|
+
},
|
|
208892
|
+
renderCall(args, theme) {
|
|
208893
|
+
return renderTodowriteCall(args, theme);
|
|
208894
|
+
},
|
|
208895
|
+
renderResult(result, _opts, theme) {
|
|
208896
|
+
return renderTodowriteResult(result, theme);
|
|
207552
208897
|
}
|
|
207553
208898
|
};
|
|
207554
208899
|
}
|
|
@@ -207574,15 +208919,22 @@ function registerMagicContextTools(pi, opts) {
|
|
|
207574
208919
|
if (!opts.sessionScopedToolsDisabled) {
|
|
207575
208920
|
pi.registerTool(createCtxNoteTool({
|
|
207576
208921
|
db: opts.db,
|
|
207577
|
-
dreamerEnabled: opts.dreamerEnabled ?? false
|
|
208922
|
+
dreamerEnabled: opts.dreamerEnabled ?? false,
|
|
208923
|
+
resolveDreamerEnabled: opts.resolveDreamerEnabled
|
|
207578
208924
|
}));
|
|
207579
208925
|
pi.registerTool(createCtxExpandTool({ db: opts.db }));
|
|
207580
208926
|
}
|
|
207581
|
-
|
|
208927
|
+
if (opts.todowriteEnabled !== false) {
|
|
208928
|
+
pi.registerTool(createTodowriteTool());
|
|
208929
|
+
if (opts.todowriteCommandEnabled !== false) {
|
|
208930
|
+
registerTodosCommand(pi);
|
|
208931
|
+
}
|
|
208932
|
+
}
|
|
207582
208933
|
if (!opts.sessionScopedToolsDisabled) {
|
|
207583
208934
|
pi.registerTool(createCtxReduceTool({
|
|
207584
208935
|
db: opts.db,
|
|
207585
|
-
protectedTags: opts.protectedTags ?? 20
|
|
208936
|
+
protectedTags: opts.protectedTags ?? 20,
|
|
208937
|
+
resolveProtectedTags: opts.resolveProtectedTags
|
|
207586
208938
|
}));
|
|
207587
208939
|
}
|
|
207588
208940
|
}
|
|
@@ -207594,6 +208946,10 @@ function resolveCurrentProject(ctx) {
|
|
|
207594
208946
|
const projectIdentity = resolveProjectIdentityOrFallback(projectDir);
|
|
207595
208947
|
return { projectDir, projectIdentity };
|
|
207596
208948
|
}
|
|
208949
|
+
function signalPiDeferredCompactionMarkerDrain(sessionId) {
|
|
208950
|
+
signalPiDeferredHistoryRefresh(sessionId);
|
|
208951
|
+
signalPiDeferredMaterialization(sessionId);
|
|
208952
|
+
}
|
|
207597
208953
|
function persistPiMessageEndModelMeta(args) {
|
|
207598
208954
|
if (!args.message || typeof args.message !== "object")
|
|
207599
208955
|
return;
|
|
@@ -207609,6 +208965,55 @@ function persistPiMessageEndModelMeta(args) {
|
|
|
207609
208965
|
updateSessionMeta(args.db, args.sessionId, { cacheTtl });
|
|
207610
208966
|
}
|
|
207611
208967
|
}
|
|
208968
|
+
function getCompatiblePiTodoCapture(todos) {
|
|
208969
|
+
if (!Array.isArray(todos))
|
|
208970
|
+
return null;
|
|
208971
|
+
const normalized = normalizeTodoStateJson(todos);
|
|
208972
|
+
if (normalized === null)
|
|
208973
|
+
return null;
|
|
208974
|
+
return { normalized, todos };
|
|
208975
|
+
}
|
|
208976
|
+
function applyCompatiblePiTodoCapture(args) {
|
|
208977
|
+
if (args.todowriteEnabled) {
|
|
208978
|
+
setTodoSnapshot(args.sessionId, args.capture.todos);
|
|
208979
|
+
args.todoOverlay?.update(args.sessionId);
|
|
208980
|
+
}
|
|
208981
|
+
if (args.persist) {
|
|
208982
|
+
updateSessionMeta(args.db, args.sessionId, {
|
|
208983
|
+
lastTodoState: args.capture.normalized
|
|
208984
|
+
});
|
|
208985
|
+
}
|
|
208986
|
+
}
|
|
208987
|
+
function capturePiTodowriteArgsIfCompatible(args) {
|
|
208988
|
+
const capture = getCompatiblePiTodoCapture(args.todos);
|
|
208989
|
+
if (capture === null)
|
|
208990
|
+
return false;
|
|
208991
|
+
applyCompatiblePiTodoCapture({ ...args, capture });
|
|
208992
|
+
return true;
|
|
208993
|
+
}
|
|
208994
|
+
function capturePiTodowriteMessageIfCompatible(args) {
|
|
208995
|
+
const msg = args.message;
|
|
208996
|
+
if (msg?.role !== "assistant" || !Array.isArray(msg.content)) {
|
|
208997
|
+
return false;
|
|
208998
|
+
}
|
|
208999
|
+
for (const block of msg.content) {
|
|
209000
|
+
if (!block || typeof block !== "object")
|
|
209001
|
+
continue;
|
|
209002
|
+
const b = block;
|
|
209003
|
+
if (b.type !== "toolCall")
|
|
209004
|
+
continue;
|
|
209005
|
+
if (typeof b.name !== "string")
|
|
209006
|
+
continue;
|
|
209007
|
+
if (b.name !== "todowrite")
|
|
209008
|
+
continue;
|
|
209009
|
+
const capture = getCompatiblePiTodoCapture(b.arguments?.todos);
|
|
209010
|
+
if (capture === null)
|
|
209011
|
+
continue;
|
|
209012
|
+
applyCompatiblePiTodoCapture({ ...args, capture });
|
|
209013
|
+
return true;
|
|
209014
|
+
}
|
|
209015
|
+
return false;
|
|
209016
|
+
}
|
|
207612
209017
|
function info(message, data) {
|
|
207613
209018
|
log(`${PREFIX} ${message}`, data);
|
|
207614
209019
|
}
|
|
@@ -207616,6 +209021,7 @@ function warn(message, data) {
|
|
|
207616
209021
|
log(`${PREFIX} WARN ${message}`, data);
|
|
207617
209022
|
}
|
|
207618
209023
|
var migratedConfigDirs = new Set;
|
|
209024
|
+
var loggedPiConfigDirs = new Set;
|
|
207619
209025
|
function ensureConfigLocationsMigrated(dir) {
|
|
207620
209026
|
if (migratedConfigDirs.has(dir))
|
|
207621
209027
|
return;
|
|
@@ -207625,6 +209031,28 @@ function ensureConfigLocationsMigrated(dir) {
|
|
|
207625
209031
|
info: (m) => info(m)
|
|
207626
209032
|
});
|
|
207627
209033
|
}
|
|
209034
|
+
function logPiConfigLoad(args) {
|
|
209035
|
+
const key = resolve3(args.dir);
|
|
209036
|
+
if (args.dedupe && loggedPiConfigDirs.has(key))
|
|
209037
|
+
return;
|
|
209038
|
+
if (args.dedupe) {
|
|
209039
|
+
loggedPiConfigDirs.add(key);
|
|
209040
|
+
}
|
|
209041
|
+
if (args.loadedFromPaths.length > 0) {
|
|
209042
|
+
info(`config loaded from: ${args.loadedFromPaths.join(", ")}`);
|
|
209043
|
+
} else {
|
|
209044
|
+
info("config: no magic-context.jsonc found, using schema defaults");
|
|
209045
|
+
}
|
|
209046
|
+
for (const warning of args.warnings) {
|
|
209047
|
+
warn(`config: ${warning}`);
|
|
209048
|
+
}
|
|
209049
|
+
}
|
|
209050
|
+
var __test = {
|
|
209051
|
+
logPiConfigLoad,
|
|
209052
|
+
resetLoggedPiConfigDirs() {
|
|
209053
|
+
loggedPiConfigDirs.clear();
|
|
209054
|
+
}
|
|
209055
|
+
};
|
|
207628
209056
|
function formatTokens(value) {
|
|
207629
209057
|
return value.toLocaleString();
|
|
207630
209058
|
}
|
|
@@ -207798,8 +209226,7 @@ async function src_default2(pi) {
|
|
|
207798
209226
|
try {
|
|
207799
209227
|
const pendingPiMarkerSessions = getSessionsWithPendingPiMarker(db);
|
|
207800
209228
|
for (const sid of pendingPiMarkerSessions) {
|
|
207801
|
-
|
|
207802
|
-
signalPiDeferredMaterialization(sid);
|
|
209229
|
+
signalPiDeferredCompactionMarkerDrain(sid);
|
|
207803
209230
|
}
|
|
207804
209231
|
if (pendingPiMarkerSessions.length > 0) {
|
|
207805
209232
|
log(`${PREFIX} rehydrated ${pendingPiMarkerSessions.length} Pi deferred compaction marker session(s)`);
|
|
@@ -207812,14 +209239,12 @@ async function src_default2(pi) {
|
|
|
207812
209239
|
const { config: config2, warnings, loadedFromPaths } = loadPiConfig({
|
|
207813
209240
|
cwd: projectDir
|
|
207814
209241
|
});
|
|
207815
|
-
|
|
207816
|
-
|
|
207817
|
-
|
|
207818
|
-
|
|
207819
|
-
|
|
207820
|
-
|
|
207821
|
-
warn(`config: ${w}`);
|
|
207822
|
-
}
|
|
209242
|
+
logPiConfigLoad({
|
|
209243
|
+
dir: projectDir,
|
|
209244
|
+
loadedFromPaths,
|
|
209245
|
+
warnings,
|
|
209246
|
+
dedupe: true
|
|
209247
|
+
});
|
|
207823
209248
|
setSqlitePragmaConfig({
|
|
207824
209249
|
cacheSizeMb: config2.sqlite.cache_size_mb,
|
|
207825
209250
|
mmapSizeMb: config2.sqlite.mmap_size_mb
|
|
@@ -207832,26 +209257,7 @@ async function src_default2(pi) {
|
|
|
207832
209257
|
}
|
|
207833
209258
|
await ensureProjectRegisteredFromPiDirectory(projectDir, db);
|
|
207834
209259
|
info(`registered embedding config for project ${projectIdentity}`);
|
|
207835
|
-
|
|
207836
|
-
db,
|
|
207837
|
-
ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
|
|
207838
|
-
allowDreamerActions: false,
|
|
207839
|
-
memoryToolEnabled: true,
|
|
207840
|
-
protectedTags: config2.protected_tags ?? 20,
|
|
207841
|
-
dreamerEnabled: isDreamerRunnable(config2)
|
|
207842
|
-
});
|
|
207843
|
-
info("registered tools: ctx_search, ctx_memory, ctx_note, ctx_expand, ctx_reduce");
|
|
207844
|
-
const historianConfig = resolveHistorianFromConfig(config2);
|
|
207845
|
-
if (historianConfig) {
|
|
207846
|
-
historianConfig.onStatusChange = (ctx) => {
|
|
207847
|
-
updateStatusLine(ctx, {
|
|
207848
|
-
db,
|
|
207849
|
-
projectIdentity: resolveCurrentProject(ctx).projectIdentity
|
|
207850
|
-
});
|
|
207851
|
-
};
|
|
207852
|
-
}
|
|
207853
|
-
const autoSearchConfig = resolveAutoSearchFromConfig(config2);
|
|
207854
|
-
const contextOptionsByDir = new Map;
|
|
209260
|
+
const projectDepsByDir = new Map;
|
|
207855
209261
|
const buildContextOptions = (cfg, hist, auto) => ({
|
|
207856
209262
|
db: database,
|
|
207857
209263
|
smartDrops: cfg.smart_drops === true,
|
|
@@ -207892,46 +209298,117 @@ async function src_default2(pi) {
|
|
|
207892
209298
|
});
|
|
207893
209299
|
}
|
|
207894
209300
|
});
|
|
207895
|
-
function
|
|
207896
|
-
const
|
|
207897
|
-
if (
|
|
207898
|
-
|
|
207899
|
-
ensureConfigLocationsMigrated(dir);
|
|
207900
|
-
const switchedConfig = loadPiConfig({ cwd: dir }).config;
|
|
207901
|
-
const switchedHistorian = resolveHistorianFromConfig(switchedConfig);
|
|
207902
|
-
if (switchedHistorian) {
|
|
207903
|
-
switchedHistorian.onStatusChange = (ctx) => {
|
|
209301
|
+
function buildProjectDeps(dir, identity, cfg) {
|
|
209302
|
+
const hist = resolveHistorianFromConfig(cfg);
|
|
209303
|
+
if (hist) {
|
|
209304
|
+
hist.onStatusChange = (ctx) => {
|
|
207904
209305
|
updateStatusLine(ctx, {
|
|
207905
209306
|
db: database,
|
|
207906
209307
|
projectIdentity: resolveCurrentProject(ctx).projectIdentity
|
|
207907
209308
|
});
|
|
207908
209309
|
};
|
|
207909
209310
|
}
|
|
207910
|
-
const
|
|
207911
|
-
|
|
209311
|
+
const auto = resolveAutoSearchFromConfig(cfg);
|
|
209312
|
+
return {
|
|
209313
|
+
projectDir: dir,
|
|
209314
|
+
projectIdentity: identity,
|
|
209315
|
+
config: cfg,
|
|
209316
|
+
historianConfig: hist,
|
|
209317
|
+
autoSearchConfig: auto,
|
|
209318
|
+
contextOptions: buildContextOptions(cfg, hist, auto),
|
|
209319
|
+
sidekickConfig: resolveSidekickFromConfig(cfg),
|
|
209320
|
+
dreamerConfig: resolveDreamerFromConfig(cfg),
|
|
209321
|
+
dreamerEnabled: isDreamerRunnable(cfg)
|
|
209322
|
+
};
|
|
209323
|
+
}
|
|
209324
|
+
function resolveProjectDepsForDir(dir, identityOverride) {
|
|
209325
|
+
const cached2 = projectDepsByDir.get(dir);
|
|
209326
|
+
if (cached2)
|
|
209327
|
+
return cached2;
|
|
209328
|
+
ensureConfigLocationsMigrated(dir);
|
|
209329
|
+
const switchedLoad = loadPiConfig({ cwd: dir });
|
|
209330
|
+
logPiConfigLoad({
|
|
209331
|
+
dir,
|
|
209332
|
+
loadedFromPaths: switchedLoad.loadedFromPaths,
|
|
209333
|
+
warnings: switchedLoad.warnings,
|
|
209334
|
+
dedupe: true
|
|
209335
|
+
});
|
|
209336
|
+
const switchedConfig = switchedLoad.config;
|
|
209337
|
+
const switchedIdentity = identityOverride ?? resolveProjectIdentityOrFallback(dir);
|
|
209338
|
+
const built = buildProjectDeps(dir, switchedIdentity, switchedConfig);
|
|
209339
|
+
projectDepsByDir.set(dir, built);
|
|
207912
209340
|
return built;
|
|
207913
209341
|
}
|
|
207914
|
-
|
|
207915
|
-
|
|
207916
|
-
|
|
207917
|
-
|
|
207918
|
-
|
|
207919
|
-
|
|
207920
|
-
|
|
207921
|
-
|
|
209342
|
+
function resolveCurrentProjectDeps(ctx) {
|
|
209343
|
+
const currentProject = resolveCurrentProject(ctx);
|
|
209344
|
+
return resolveProjectDepsForDir(currentProject.projectDir, currentProject.projectIdentity);
|
|
209345
|
+
}
|
|
209346
|
+
function resolveContextOptionsForProject(dir) {
|
|
209347
|
+
return resolveProjectDepsForDir(dir).contextOptions;
|
|
209348
|
+
}
|
|
209349
|
+
const bootProjectDeps = buildProjectDeps(projectDir, projectIdentity, config2);
|
|
209350
|
+
projectDepsByDir.set(projectDir, bootProjectDeps);
|
|
209351
|
+
const todowriteEnabled = bootProjectDeps.config.todowrite.enabled !== false;
|
|
209352
|
+
const todowriteOverlayEnabled = todowriteEnabled && bootProjectDeps.config.todowrite.overlay !== false;
|
|
209353
|
+
registerMagicContextTools(pi, {
|
|
209354
|
+
db,
|
|
209355
|
+
ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
|
|
209356
|
+
allowDreamerActions: false,
|
|
209357
|
+
memoryToolEnabled: true,
|
|
209358
|
+
protectedTags: config2.protected_tags ?? 20,
|
|
209359
|
+
resolveProtectedTags: (ctx) => resolveCurrentProjectDeps(ctx).config.protected_tags ?? 20,
|
|
209360
|
+
dreamerEnabled: isDreamerRunnable(config2),
|
|
209361
|
+
resolveDreamerEnabled: (ctx) => resolveCurrentProjectDeps(ctx).dreamerEnabled,
|
|
209362
|
+
todowriteEnabled
|
|
209363
|
+
});
|
|
209364
|
+
info(todowriteEnabled ? "registered tools: ctx_search, ctx_memory, ctx_note, ctx_expand, todowrite, ctx_reduce; registered /todos" : "registered tools: ctx_search, ctx_memory, ctx_note, ctx_expand, ctx_reduce (todowrite disabled)");
|
|
209365
|
+
const readLastTodoState = (sessionId) => getOrCreateSessionMeta(db, sessionId).lastTodoState;
|
|
209366
|
+
if (todowriteEnabled) {
|
|
209367
|
+
registerTodoStateLifecycle(pi, { readLastTodoState });
|
|
209368
|
+
}
|
|
209369
|
+
const todoOverlay = todowriteOverlayEnabled ? registerTodoOverlay(pi, {
|
|
209370
|
+
readLastTodoState
|
|
209371
|
+
}) : undefined;
|
|
209372
|
+
info(todowriteOverlayEnabled ? "registered todowrite overlay" : "registered todowrite overlay: DISABLED (todowrite.enabled=false or todowrite.overlay=false)");
|
|
209373
|
+
registerPiContextHandler(pi, bootProjectDeps.contextOptions);
|
|
209374
|
+
info(bootProjectDeps.historianConfig ? `registered historian trigger (model=${bootProjectDeps.historianConfig.model}, executeThreshold=${formatExecuteThresholdForLog(bootProjectDeps.historianConfig.executeThresholdPercentage)})` : "registered historian trigger: DISABLED (set historian.model in magic-context.jsonc)");
|
|
209375
|
+
info(bootProjectDeps.autoSearchConfig.enabled ? `registered auto-search hint (threshold=${bootProjectDeps.autoSearchConfig.scoreThreshold}, minChars=${bootProjectDeps.autoSearchConfig.minPromptChars})` : "registered auto-search hint: DISABLED (memory.auto_search.enabled=false)");
|
|
209376
|
+
registerCtxAugCommand(pi, (ctx) => resolveCurrentProjectDeps(ctx).sidekickConfig);
|
|
209377
|
+
info(bootProjectDeps.sidekickConfig ? `registered /ctx-aug (sidekick model=${bootProjectDeps.sidekickConfig.model})` : "registered /ctx-aug (sidekick disabled — set sidekick.disable=false and sidekick.model in config)");
|
|
209378
|
+
const recompRunner = new PiSubagentRunner;
|
|
209379
|
+
const wrapupRunner = new PiSubagentRunner;
|
|
209380
|
+
const upgradeRunner = new PiSubagentRunner;
|
|
207922
209381
|
registerCtxStatusCommand(pi, {
|
|
207923
209382
|
db,
|
|
207924
209383
|
projectIdentity,
|
|
207925
209384
|
resolveProject: resolveCurrentProject,
|
|
207926
|
-
protectedTags:
|
|
207927
|
-
executeThresholdPercentage:
|
|
207928
|
-
historyBudgetPercentage:
|
|
207929
|
-
injectionBudgetTokens:
|
|
207930
|
-
commitClusterTrigger:
|
|
207931
|
-
executeThresholdTokens:
|
|
209385
|
+
protectedTags: bootProjectDeps.config.protected_tags,
|
|
209386
|
+
executeThresholdPercentage: bootProjectDeps.config.execute_threshold_percentage,
|
|
209387
|
+
historyBudgetPercentage: bootProjectDeps.config.history_budget_percentage,
|
|
209388
|
+
injectionBudgetTokens: bootProjectDeps.config.memory?.injection_budget_tokens,
|
|
209389
|
+
commitClusterTrigger: bootProjectDeps.config.commit_cluster_trigger,
|
|
209390
|
+
executeThresholdTokens: bootProjectDeps.config.execute_threshold_tokens,
|
|
207932
209391
|
dreamer: {
|
|
207933
|
-
runnable:
|
|
207934
|
-
scheduleSummary: summarizeDreamSchedule(
|
|
209392
|
+
runnable: bootProjectDeps.dreamerEnabled,
|
|
209393
|
+
scheduleSummary: summarizeDreamSchedule(bootProjectDeps.config.dreamer)
|
|
209394
|
+
},
|
|
209395
|
+
resolveStatusDeps: (ctx) => {
|
|
209396
|
+
const current = resolveCurrentProjectDeps(ctx);
|
|
209397
|
+
return {
|
|
209398
|
+
db,
|
|
209399
|
+
projectIdentity: current.projectIdentity,
|
|
209400
|
+
resolveProject: resolveCurrentProject,
|
|
209401
|
+
protectedTags: current.config.protected_tags,
|
|
209402
|
+
executeThresholdPercentage: current.config.execute_threshold_percentage,
|
|
209403
|
+
historyBudgetPercentage: current.config.history_budget_percentage,
|
|
209404
|
+
injectionBudgetTokens: current.config.memory?.injection_budget_tokens,
|
|
209405
|
+
commitClusterTrigger: current.config.commit_cluster_trigger,
|
|
209406
|
+
executeThresholdTokens: current.config.execute_threshold_tokens,
|
|
209407
|
+
dreamer: {
|
|
209408
|
+
runnable: current.dreamerEnabled,
|
|
209409
|
+
scheduleSummary: summarizeDreamSchedule(current.config.dreamer)
|
|
209410
|
+
}
|
|
209411
|
+
};
|
|
207935
209412
|
}
|
|
207936
209413
|
});
|
|
207937
209414
|
info("registered /ctx-status");
|
|
@@ -207941,45 +209418,94 @@ async function src_default2(pi) {
|
|
|
207941
209418
|
info("registered /ctx-flush");
|
|
207942
209419
|
registerCtxRecompCommand(pi, {
|
|
207943
209420
|
db,
|
|
207944
|
-
runner:
|
|
207945
|
-
historianModel: historianConfig?.model,
|
|
207946
|
-
historianChunkTokens: deriveHistorianChunkTokens(resolveHistorianContextLimit(historianConfig?.model)),
|
|
207947
|
-
historianFallbacks: historianConfig?.fallbackModels,
|
|
207948
|
-
historianTimeoutMs:
|
|
207949
|
-
historianThinkingLevel: historianConfig?.thinkingLevel,
|
|
207950
|
-
language:
|
|
207951
|
-
memoryEnabled:
|
|
207952
|
-
autoPromote:
|
|
209421
|
+
runner: recompRunner,
|
|
209422
|
+
historianModel: bootProjectDeps.historianConfig?.model,
|
|
209423
|
+
historianChunkTokens: deriveHistorianChunkTokens(resolveHistorianContextLimit(bootProjectDeps.historianConfig?.model)),
|
|
209424
|
+
historianFallbacks: bootProjectDeps.historianConfig?.fallbackModels,
|
|
209425
|
+
historianTimeoutMs: bootProjectDeps.config.historian_timeout_ms,
|
|
209426
|
+
historianThinkingLevel: bootProjectDeps.historianConfig?.thinkingLevel,
|
|
209427
|
+
language: bootProjectDeps.config.language,
|
|
209428
|
+
memoryEnabled: bootProjectDeps.config.memory.enabled,
|
|
209429
|
+
autoPromote: bootProjectDeps.config.memory.auto_promote,
|
|
209430
|
+
resolveRuntimeDeps: (ctx) => {
|
|
209431
|
+
const current = resolveCurrentProjectDeps(ctx);
|
|
209432
|
+
return {
|
|
209433
|
+
db,
|
|
209434
|
+
runner: recompRunner,
|
|
209435
|
+
historianModel: current.historianConfig?.model,
|
|
209436
|
+
historianChunkTokens: deriveHistorianChunkTokens(resolveHistorianContextLimit(current.historianConfig?.model)),
|
|
209437
|
+
historianFallbacks: current.historianConfig?.fallbackModels,
|
|
209438
|
+
historianTimeoutMs: current.config.historian_timeout_ms,
|
|
209439
|
+
historianThinkingLevel: current.historianConfig?.thinkingLevel,
|
|
209440
|
+
language: current.config.language,
|
|
209441
|
+
memoryEnabled: current.config.memory.enabled,
|
|
209442
|
+
autoPromote: current.config.memory.auto_promote
|
|
209443
|
+
};
|
|
209444
|
+
}
|
|
207953
209445
|
});
|
|
207954
209446
|
info("registered /ctx-recomp");
|
|
207955
209447
|
registerCtxWrapupCommand(pi, {
|
|
207956
209448
|
db,
|
|
207957
|
-
runner:
|
|
207958
|
-
historianModel: historianConfig?.model,
|
|
207959
|
-
historianChunkTokens: deriveHistorianChunkTokens(resolveHistorianContextLimit(historianConfig?.model)),
|
|
207960
|
-
historianFallbacks: historianConfig?.fallbackModels,
|
|
207961
|
-
historianTimeoutMs:
|
|
207962
|
-
historianThinkingLevel: historianConfig?.thinkingLevel,
|
|
207963
|
-
language:
|
|
207964
|
-
memoryEnabled:
|
|
207965
|
-
autoPromote:
|
|
207966
|
-
userMemoriesEnabled: userMemoryCollectionEnabled(
|
|
207967
|
-
executeThresholdPercentage:
|
|
207968
|
-
executeThresholdTokens:
|
|
209449
|
+
runner: wrapupRunner,
|
|
209450
|
+
historianModel: bootProjectDeps.historianConfig?.model,
|
|
209451
|
+
historianChunkTokens: deriveHistorianChunkTokens(resolveHistorianContextLimit(bootProjectDeps.historianConfig?.model)),
|
|
209452
|
+
historianFallbacks: bootProjectDeps.historianConfig?.fallbackModels,
|
|
209453
|
+
historianTimeoutMs: bootProjectDeps.config.historian_timeout_ms,
|
|
209454
|
+
historianThinkingLevel: bootProjectDeps.historianConfig?.thinkingLevel,
|
|
209455
|
+
language: bootProjectDeps.config.language,
|
|
209456
|
+
memoryEnabled: bootProjectDeps.config.memory.enabled,
|
|
209457
|
+
autoPromote: bootProjectDeps.config.memory.auto_promote,
|
|
209458
|
+
userMemoriesEnabled: userMemoryCollectionEnabled(bootProjectDeps.config.dreamer),
|
|
209459
|
+
executeThresholdPercentage: bootProjectDeps.config.execute_threshold_percentage,
|
|
209460
|
+
executeThresholdTokens: bootProjectDeps.config.execute_threshold_tokens,
|
|
209461
|
+
resolveRuntimeDeps: (ctx) => {
|
|
209462
|
+
const current = resolveCurrentProjectDeps(ctx);
|
|
209463
|
+
return {
|
|
209464
|
+
db,
|
|
209465
|
+
runner: wrapupRunner,
|
|
209466
|
+
historianModel: current.historianConfig?.model,
|
|
209467
|
+
historianChunkTokens: deriveHistorianChunkTokens(resolveHistorianContextLimit(current.historianConfig?.model)),
|
|
209468
|
+
historianFallbacks: current.historianConfig?.fallbackModels,
|
|
209469
|
+
historianTimeoutMs: current.config.historian_timeout_ms,
|
|
209470
|
+
historianThinkingLevel: current.historianConfig?.thinkingLevel,
|
|
209471
|
+
language: current.config.language,
|
|
209472
|
+
memoryEnabled: current.config.memory.enabled,
|
|
209473
|
+
autoPromote: current.config.memory.auto_promote,
|
|
209474
|
+
userMemoriesEnabled: userMemoryCollectionEnabled(current.config.dreamer),
|
|
209475
|
+
executeThresholdPercentage: current.config.execute_threshold_percentage,
|
|
209476
|
+
executeThresholdTokens: current.config.execute_threshold_tokens
|
|
209477
|
+
};
|
|
209478
|
+
}
|
|
207969
209479
|
});
|
|
207970
209480
|
info("registered /ctx-wrapup");
|
|
207971
209481
|
registerCtxSessionUpgradeCommand(pi, {
|
|
207972
209482
|
db,
|
|
207973
|
-
runner:
|
|
207974
|
-
historianModel: historianConfig?.model,
|
|
207975
|
-
historianChunkTokens: deriveHistorianChunkTokens(resolveHistorianContextLimit(historianConfig?.model)),
|
|
207976
|
-
historianFallbacks: historianConfig?.fallbackModels,
|
|
207977
|
-
historianTimeoutMs:
|
|
207978
|
-
historianThinkingLevel: historianConfig?.thinkingLevel,
|
|
207979
|
-
language:
|
|
207980
|
-
memoryEnabled:
|
|
207981
|
-
autoPromote:
|
|
207982
|
-
userMemoriesEnabled: userMemoryCollectionEnabled(
|
|
209483
|
+
runner: upgradeRunner,
|
|
209484
|
+
historianModel: bootProjectDeps.historianConfig?.model,
|
|
209485
|
+
historianChunkTokens: deriveHistorianChunkTokens(resolveHistorianContextLimit(bootProjectDeps.historianConfig?.model)),
|
|
209486
|
+
historianFallbacks: bootProjectDeps.historianConfig?.fallbackModels,
|
|
209487
|
+
historianTimeoutMs: bootProjectDeps.config.historian_timeout_ms,
|
|
209488
|
+
historianThinkingLevel: bootProjectDeps.historianConfig?.thinkingLevel,
|
|
209489
|
+
language: bootProjectDeps.config.language,
|
|
209490
|
+
memoryEnabled: bootProjectDeps.config.memory.enabled,
|
|
209491
|
+
autoPromote: bootProjectDeps.config.memory.auto_promote,
|
|
209492
|
+
userMemoriesEnabled: userMemoryCollectionEnabled(bootProjectDeps.config.dreamer),
|
|
209493
|
+
resolveRuntimeDeps: (ctx) => {
|
|
209494
|
+
const current = resolveCurrentProjectDeps(ctx);
|
|
209495
|
+
return {
|
|
209496
|
+
db,
|
|
209497
|
+
runner: upgradeRunner,
|
|
209498
|
+
historianModel: current.historianConfig?.model,
|
|
209499
|
+
historianChunkTokens: deriveHistorianChunkTokens(resolveHistorianContextLimit(current.historianConfig?.model)),
|
|
209500
|
+
historianFallbacks: current.historianConfig?.fallbackModels,
|
|
209501
|
+
historianTimeoutMs: current.config.historian_timeout_ms,
|
|
209502
|
+
historianThinkingLevel: current.historianConfig?.thinkingLevel,
|
|
209503
|
+
language: current.config.language,
|
|
209504
|
+
memoryEnabled: current.config.memory.enabled,
|
|
209505
|
+
autoPromote: current.config.memory.auto_promote,
|
|
209506
|
+
userMemoriesEnabled: userMemoryCollectionEnabled(current.config.dreamer)
|
|
209507
|
+
};
|
|
209508
|
+
}
|
|
207983
209509
|
});
|
|
207984
209510
|
info("registered /ctx-session-upgrade");
|
|
207985
209511
|
registerCtxDreamCommand(pi, {
|
|
@@ -207987,7 +209513,8 @@ async function src_default2(pi) {
|
|
|
207987
209513
|
projectDir,
|
|
207988
209514
|
projectIdentity,
|
|
207989
209515
|
resolveProject: resolveCurrentProject,
|
|
207990
|
-
dreamerEnabled:
|
|
209516
|
+
dreamerEnabled: bootProjectDeps.dreamerEnabled,
|
|
209517
|
+
resolveDreamerEnabled: (ctx) => resolveCurrentProjectDeps(ctx).dreamerEnabled,
|
|
207991
209518
|
onProjectSeen: (identity) => seenDreamerProjectIdentities.add(identity)
|
|
207992
209519
|
});
|
|
207993
209520
|
info("registered /ctx-dream");
|
|
@@ -207995,26 +209522,27 @@ async function src_default2(pi) {
|
|
|
207995
209522
|
db,
|
|
207996
209523
|
projectDir,
|
|
207997
209524
|
projectIdentity,
|
|
207998
|
-
memoryEnabled:
|
|
209525
|
+
memoryEnabled: bootProjectDeps.config.memory.enabled,
|
|
209526
|
+
resolveMemoryEnabled: (ctx) => resolveCurrentProjectDeps(ctx).config.memory.enabled,
|
|
207999
209527
|
resolveProject: resolveCurrentProject
|
|
208000
209528
|
});
|
|
208001
209529
|
info("registered /ctx-embed");
|
|
208002
|
-
const dreamerConfig =
|
|
209530
|
+
const dreamerConfig = bootProjectDeps.dreamerConfig;
|
|
208003
209531
|
if (dreamerConfig) {
|
|
208004
209532
|
registerPiDreamerProject({
|
|
208005
209533
|
db,
|
|
208006
209534
|
projectDir,
|
|
208007
209535
|
projectIdentity,
|
|
208008
209536
|
config: dreamerConfig,
|
|
208009
|
-
embeddingConfig:
|
|
208010
|
-
memoryEnabled:
|
|
208011
|
-
language:
|
|
208012
|
-
gitCommitIndexing:
|
|
209537
|
+
embeddingConfig: bootProjectDeps.config.embedding,
|
|
209538
|
+
memoryEnabled: bootProjectDeps.config.memory.enabled,
|
|
209539
|
+
language: bootProjectDeps.config.language,
|
|
209540
|
+
gitCommitIndexing: bootProjectDeps.config.memory.git_commit_indexing,
|
|
208013
209541
|
onAdjunctsRefreshNeeded: signalPiSystemPromptRefreshForProject
|
|
208014
209542
|
});
|
|
208015
209543
|
info(`registered dreamer (${summarizeDreamSchedule(dreamerConfig)})`);
|
|
208016
209544
|
} else {
|
|
208017
|
-
info(
|
|
209545
|
+
info(bootProjectDeps.dreamerEnabled ? "registered dreamer: DISABLED (no dreamer config)" : "registered dreamer: DISABLED (dreamer.disable=true or no dreamer config)");
|
|
208018
209546
|
}
|
|
208019
209547
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
208020
209548
|
try {
|
|
@@ -208035,13 +209563,14 @@ async function src_default2(pi) {
|
|
|
208035
209563
|
}
|
|
208036
209564
|
} catch {}
|
|
208037
209565
|
try {
|
|
208038
|
-
const
|
|
209566
|
+
const effectiveProjectDeps = resolveCurrentProjectDeps(ctx);
|
|
209567
|
+
const currentProject = {
|
|
209568
|
+
projectDir: effectiveProjectDeps.projectDir,
|
|
209569
|
+
projectIdentity: effectiveProjectDeps.projectIdentity
|
|
209570
|
+
};
|
|
209571
|
+
const effectiveConfig = effectiveProjectDeps.config;
|
|
208039
209572
|
seenDreamerProjectIdentities.add(currentProject.projectIdentity);
|
|
208040
|
-
const
|
|
208041
|
-
if (switchedProject)
|
|
208042
|
-
ensureConfigLocationsMigrated(currentProject.projectDir);
|
|
208043
|
-
const effectiveConfig = switchedProject ? loadPiConfig({ cwd: currentProject.projectDir }).config : config2;
|
|
208044
|
-
const effectiveDreamerConfig = switchedProject ? resolveDreamerFromConfig(effectiveConfig) : dreamerConfig;
|
|
209573
|
+
const effectiveDreamerConfig = effectiveProjectDeps.dreamerConfig;
|
|
208045
209574
|
if (effectiveDreamerConfig) {
|
|
208046
209575
|
try {
|
|
208047
209576
|
registerPiDreamerProject({
|
|
@@ -208051,13 +209580,14 @@ async function src_default2(pi) {
|
|
|
208051
209580
|
config: effectiveDreamerConfig,
|
|
208052
209581
|
embeddingConfig: effectiveConfig.embedding,
|
|
208053
209582
|
memoryEnabled: effectiveConfig.memory.enabled,
|
|
209583
|
+
language: effectiveConfig.language,
|
|
208054
209584
|
gitCommitIndexing: effectiveConfig.memory.git_commit_indexing,
|
|
208055
209585
|
onAdjunctsRefreshNeeded: signalPiSystemPromptRefreshForProject
|
|
208056
209586
|
});
|
|
208057
209587
|
} catch (err5) {
|
|
208058
209588
|
warn("before_agent_start: registerPiDreamerProject threw:", err5);
|
|
208059
209589
|
}
|
|
208060
|
-
} else
|
|
209590
|
+
} else {
|
|
208061
209591
|
try {
|
|
208062
209592
|
unregisterPiDreamerProject({
|
|
208063
209593
|
projectIdentity: currentProject.projectIdentity
|
|
@@ -208084,11 +209614,10 @@ async function src_default2(pi) {
|
|
|
208084
209614
|
const smForDrain = sm;
|
|
208085
209615
|
const canDrain = typeof smForDrain.appendCompaction === "function" && typeof smForDrain.getBranch === "function";
|
|
208086
209616
|
if (canDrain && getPendingPiCompactionMarkerState(db, sessionId)) {
|
|
208087
|
-
|
|
208088
|
-
signalPiPendingMaterialization(sessionId);
|
|
209617
|
+
signalPiDeferredCompactionMarkerDrain(sessionId);
|
|
208089
209618
|
}
|
|
208090
209619
|
} catch {}
|
|
208091
|
-
if (ctx.hasUI && historianConfig?.model) {
|
|
209620
|
+
if (ctx.hasUI && effectiveProjectDeps.historianConfig?.model) {
|
|
208092
209621
|
maybeSendUpgradeReminder({
|
|
208093
209622
|
client: null,
|
|
208094
209623
|
db,
|
|
@@ -208109,7 +209638,6 @@ async function src_default2(pi) {
|
|
|
208109
209638
|
return;
|
|
208110
209639
|
}
|
|
208111
209640
|
const isCacheBusting = sessionId ? hasSystemPromptRefresh(sessionId) : true;
|
|
208112
|
-
const effectiveDreamerRunnable = switchedProject ? isDreamerRunnable(effectiveConfig) : isDreamerRunnable(config2);
|
|
208113
209641
|
const block = buildMagicContextBlock({
|
|
208114
209642
|
db,
|
|
208115
209643
|
cwd: currentProject.projectDir,
|
|
@@ -208118,7 +209646,7 @@ async function src_default2(pi) {
|
|
|
208118
209646
|
includeGuidance: true,
|
|
208119
209647
|
protectedTags: effectiveConfig.protected_tags,
|
|
208120
209648
|
ctxReduceCallable: true,
|
|
208121
|
-
dreamerEnabled:
|
|
209649
|
+
dreamerEnabled: effectiveProjectDeps.dreamerEnabled,
|
|
208122
209650
|
temporalAwarenessEnabled: effectiveConfig.temporal_awareness ?? false,
|
|
208123
209651
|
cavemanTextCompressionEnabled: effectiveConfig.caveman_text_compression?.enabled === true,
|
|
208124
209652
|
language: effectiveConfig.language,
|
|
@@ -208176,14 +209704,14 @@ ${block}` : event.systemPrompt;
|
|
|
208176
209704
|
const todoArgs = event.args;
|
|
208177
209705
|
const todos = todoArgs?.todos;
|
|
208178
209706
|
const sessionMeta = Array.isArray(todos) ? getOrCreateSessionMeta(db, sessionId) : null;
|
|
208179
|
-
|
|
208180
|
-
|
|
208181
|
-
|
|
208182
|
-
|
|
208183
|
-
|
|
208184
|
-
|
|
208185
|
-
|
|
208186
|
-
}
|
|
209707
|
+
capturePiTodowriteArgsIfCompatible({
|
|
209708
|
+
db,
|
|
209709
|
+
sessionId,
|
|
209710
|
+
todos,
|
|
209711
|
+
todowriteEnabled,
|
|
209712
|
+
todoOverlay,
|
|
209713
|
+
persist: Boolean(sessionMeta && !sessionMeta.isSubagent)
|
|
209714
|
+
});
|
|
208187
209715
|
if (Array.isArray(todos) && todos.length > 0 && todos.every((t) => t.status === "completed" || t.status === "cancelled")) {
|
|
208188
209716
|
if (sessionMeta && !sessionMeta.isSubagent) {
|
|
208189
209717
|
onNoteTrigger(db, sessionId, "todos_complete");
|
|
@@ -208264,7 +209792,7 @@ ${block}` : event.systemPrompt;
|
|
|
208264
209792
|
db,
|
|
208265
209793
|
sessionId,
|
|
208266
209794
|
message: event.message,
|
|
208267
|
-
cacheTtlConfig:
|
|
209795
|
+
cacheTtlConfig: resolveCurrentProjectDeps(ctx).config.cache_ttl
|
|
208268
209796
|
});
|
|
208269
209797
|
const piUsage = ctx.getContextUsage?.();
|
|
208270
209798
|
const piContextWindow = piUsage && typeof piUsage.contextWindow === "number" && piUsage.contextWindow > 0 ? piUsage.contextWindow : ctx.model?.contextWindow ?? 0;
|
|
@@ -208286,32 +209814,14 @@ ${block}` : event.systemPrompt;
|
|
|
208286
209814
|
try {
|
|
208287
209815
|
const sessionMetaForTodo = getOrCreateSessionMeta(db, sessionId);
|
|
208288
209816
|
if (!sessionMetaForTodo.isSubagent) {
|
|
208289
|
-
|
|
208290
|
-
|
|
208291
|
-
|
|
208292
|
-
|
|
208293
|
-
|
|
208294
|
-
|
|
208295
|
-
|
|
208296
|
-
|
|
208297
|
-
if (typeof b.name !== "string")
|
|
208298
|
-
continue;
|
|
208299
|
-
if (b.name !== "todowrite") {
|
|
208300
|
-
continue;
|
|
208301
|
-
}
|
|
208302
|
-
const args = b.arguments;
|
|
208303
|
-
const todos = args?.todos;
|
|
208304
|
-
if (!Array.isArray(todos))
|
|
208305
|
-
continue;
|
|
208306
|
-
const normalized = normalizeTodoStateJson(todos);
|
|
208307
|
-
if (normalized === null)
|
|
208308
|
-
continue;
|
|
208309
|
-
updateSessionMeta(db, sessionId, {
|
|
208310
|
-
lastTodoState: normalized
|
|
208311
|
-
});
|
|
208312
|
-
break;
|
|
208313
|
-
}
|
|
208314
|
-
}
|
|
209817
|
+
capturePiTodowriteMessageIfCompatible({
|
|
209818
|
+
db,
|
|
209819
|
+
sessionId,
|
|
209820
|
+
message: event.message,
|
|
209821
|
+
todowriteEnabled,
|
|
209822
|
+
todoOverlay,
|
|
209823
|
+
persist: true
|
|
209824
|
+
});
|
|
208315
209825
|
}
|
|
208316
209826
|
} catch (err5) {
|
|
208317
209827
|
warn("message_end: synthetic todowrite capture failed:", err5);
|
|
@@ -208397,10 +209907,14 @@ function formatExecuteThresholdForLog(value) {
|
|
|
208397
209907
|
return overrides.length > 0 ? `${base} (${overrides.join(", ")})` : base;
|
|
208398
209908
|
}
|
|
208399
209909
|
export {
|
|
209910
|
+
signalPiDeferredCompactionMarkerDrain,
|
|
208400
209911
|
resolveSidekickFromConfig,
|
|
208401
209912
|
resolveHistorianFromConfig,
|
|
208402
209913
|
resolveDreamerFromConfig,
|
|
208403
209914
|
persistPiPressureFromMessageEnd,
|
|
208404
209915
|
persistPiMessageEndModelMeta,
|
|
208405
|
-
src_default2 as default
|
|
209916
|
+
src_default2 as default,
|
|
209917
|
+
capturePiTodowriteMessageIfCompatible,
|
|
209918
|
+
capturePiTodowriteArgsIfCompatible,
|
|
209919
|
+
__test
|
|
208406
209920
|
};
|