@cortexkit/pi-magic-context 0.31.3 → 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 +991 -311
- package/dist/subagent-entry.js +657 -178
- 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,16 +150838,33 @@ 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")) {
|
|
150736
150842
|
return null;
|
|
150737
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)
|
|
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();
|
|
150856
|
+
}
|
|
150738
150857
|
const row = db.prepare(`SELECT workspace.share_categories AS shareCategories
|
|
150739
150858
|
FROM workspace_members AS member
|
|
150740
150859
|
JOIN workspaces AS workspace ON workspace.id = member.workspace_id
|
|
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;
|
|
@@ -156833,8 +156960,31 @@ function clearNoteNudgeTriggerOnly(db, sessionId) {
|
|
|
156833
156960
|
|
|
156834
156961
|
// ../plugin/src/hooks/magic-context/todo-view.ts
|
|
156835
156962
|
import { createHash as createHash5 } from "node:crypto";
|
|
156836
|
-
var
|
|
156837
|
-
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]);
|
|
156838
156988
|
var SYNTHETIC_CALL_ID_PREFIX = "mc_synthetic_todo_";
|
|
156839
156989
|
function normalizeTodoStateJson(todos) {
|
|
156840
156990
|
if (!Array.isArray(todos))
|
|
@@ -156846,7 +156996,7 @@ function normalizeTodoStateJson(todos) {
|
|
|
156846
156996
|
normalized.push({
|
|
156847
156997
|
content: todo.content,
|
|
156848
156998
|
status: todo.status,
|
|
156849
|
-
priority: todo.priority ??
|
|
156999
|
+
priority: todo.priority ?? TODO_PRIORITY_MEDIUM
|
|
156850
157000
|
});
|
|
156851
157001
|
}
|
|
156852
157002
|
return JSON.stringify(normalized);
|
|
@@ -156890,11 +157040,11 @@ function parseTodoState(stateJson) {
|
|
|
156890
157040
|
const result = [];
|
|
156891
157041
|
for (const item of parsed) {
|
|
156892
157042
|
if (!isTodoItem(item))
|
|
156893
|
-
|
|
157043
|
+
return null;
|
|
156894
157044
|
result.push({
|
|
156895
157045
|
content: item.content,
|
|
156896
157046
|
status: item.status,
|
|
156897
|
-
priority: item.priority ??
|
|
157047
|
+
priority: item.priority ?? TODO_PRIORITY_MEDIUM
|
|
156898
157048
|
});
|
|
156899
157049
|
}
|
|
156900
157050
|
return result;
|
|
@@ -156902,11 +157052,17 @@ function parseTodoState(stateJson) {
|
|
|
156902
157052
|
return null;
|
|
156903
157053
|
}
|
|
156904
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
|
+
}
|
|
156905
157061
|
function isTodoItem(value) {
|
|
156906
157062
|
if (value === null || typeof value !== "object")
|
|
156907
157063
|
return false;
|
|
156908
157064
|
const todo = value;
|
|
156909
|
-
return typeof todo.content === "string" &&
|
|
157065
|
+
return typeof todo.content === "string" && isTodoStatus(todo.status) && (todo.priority === undefined || isTodoPriority(todo.priority));
|
|
156910
157066
|
}
|
|
156911
157067
|
|
|
156912
157068
|
// ../plugin/src/hooks/magic-context/upgrade-reminder.ts
|
|
@@ -158585,6 +158741,22 @@ function extractLatestAssistantText(messages) {
|
|
|
158585
158741
|
return getTextParts(latest).map((part) => part.text).join(`
|
|
158586
158742
|
`) || null;
|
|
158587
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
|
+
}
|
|
158588
158760
|
|
|
158589
158761
|
// ../plugin/src/features/magic-context/dreamer/task-executor.ts
|
|
158590
158762
|
init_logger();
|
|
@@ -174637,6 +174809,7 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
174637
174809
|
|
|
174638
174810
|
// ../plugin/src/features/magic-context/memory/storage-memory-embeddings.ts
|
|
174639
174811
|
var saveEmbeddingStatements = new WeakMap;
|
|
174812
|
+
var saveEmbeddingIfHashMatchesStatements = new WeakMap;
|
|
174640
174813
|
var loadAllEmbeddingsStatements = new WeakMap;
|
|
174641
174814
|
var deleteEmbeddingStatements = new WeakMap;
|
|
174642
174815
|
var getStoredModelIdStatements = new WeakMap;
|
|
@@ -174667,6 +174840,14 @@ function getSaveEmbeddingStatement(db) {
|
|
|
174667
174840
|
}
|
|
174668
174841
|
return stmt;
|
|
174669
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
|
+
}
|
|
174670
174851
|
function getLoadAllEmbeddingsStatement(db) {
|
|
174671
174852
|
let stmt = loadAllEmbeddingsStatements.get(db);
|
|
174672
174853
|
if (!stmt) {
|
|
@@ -174695,6 +174876,10 @@ function saveEmbedding(db, memoryId, embedding, modelId) {
|
|
|
174695
174876
|
const blob = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
|
|
174696
174877
|
getSaveEmbeddingStatement(db).run(memoryId, blob, modelId);
|
|
174697
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
|
+
}
|
|
174698
174883
|
function loadAllEmbeddings(db, projectPath, modelId) {
|
|
174699
174884
|
const rows = getLoadAllEmbeddingsStatement(db).all(projectPath, modelId).filter(isEmbeddingRow);
|
|
174700
174885
|
const embeddings = new Map;
|
|
@@ -175555,12 +175740,12 @@ function isUnembeddedMemoryRow(row) {
|
|
|
175555
175740
|
if (row === null || typeof row !== "object")
|
|
175556
175741
|
return false;
|
|
175557
175742
|
const candidate = row;
|
|
175558
|
-
return typeof candidate.id === "number" && typeof candidate.content === "string";
|
|
175743
|
+
return typeof candidate.id === "number" && typeof candidate.content === "string" && typeof candidate.normalized_hash === "string";
|
|
175559
175744
|
}
|
|
175560
175745
|
function getLoadUnembeddedMemoriesStatement(db) {
|
|
175561
175746
|
let stmt = loadUnembeddedMemoriesStatements.get(db);
|
|
175562
175747
|
if (!stmt) {
|
|
175563
|
-
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 ?");
|
|
175564
175749
|
loadUnembeddedMemoriesStatements.set(db, stmt);
|
|
175565
175750
|
}
|
|
175566
175751
|
return stmt;
|
|
@@ -175583,8 +175768,9 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
175583
175768
|
const embedding = result.vectors[index];
|
|
175584
175769
|
if (!embedding)
|
|
175585
175770
|
continue;
|
|
175586
|
-
|
|
175587
|
-
|
|
175771
|
+
if (saveEmbeddingIfHashMatches(db, memory.id, embedding, result.modelId, memory.normalized_hash)) {
|
|
175772
|
+
embeddedCount += 1;
|
|
175773
|
+
}
|
|
175588
175774
|
}
|
|
175589
175775
|
})();
|
|
175590
175776
|
return embeddedCount;
|
|
@@ -176286,25 +176472,33 @@ function getMemoryCountsByStatusStatement(db) {
|
|
|
176286
176472
|
}
|
|
176287
176473
|
return stmt;
|
|
176288
176474
|
}
|
|
176289
|
-
function
|
|
176290
|
-
const now = Date.now();
|
|
176291
|
-
const normalizedHash = computeNormalizedHash(input.content);
|
|
176475
|
+
function buildInsertMemoryValues(input, normalizedHash, now, includeImportance) {
|
|
176292
176476
|
const insertValues = [
|
|
176293
176477
|
input.projectPath,
|
|
176294
176478
|
input.category,
|
|
176295
176479
|
input.content,
|
|
176296
176480
|
normalizedHash
|
|
176297
176481
|
];
|
|
176298
|
-
if (
|
|
176482
|
+
if (includeImportance) {
|
|
176299
176483
|
insertValues.push(input.importance ?? 50);
|
|
176300
176484
|
}
|
|
176301
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);
|
|
176302
|
-
|
|
176303
|
-
|
|
176304
|
-
|
|
176486
|
+
return insertValues;
|
|
176487
|
+
}
|
|
176488
|
+
function loadInsertedMemory(db, rowid) {
|
|
176489
|
+
const inserted = getMemoryById(db, Number(rowid));
|
|
176305
176490
|
if (!inserted) {
|
|
176306
176491
|
throw new Error("Failed to load inserted memory row");
|
|
176307
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);
|
|
176308
176502
|
invalidateProject(input.projectPath);
|
|
176309
176503
|
return inserted;
|
|
176310
176504
|
}
|
|
@@ -177098,6 +177292,14 @@ function renewLease(db, holderId, leaseKey = DREAMING_LEASE_KEY) {
|
|
|
177098
177292
|
return true;
|
|
177099
177293
|
});
|
|
177100
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
|
+
}
|
|
177101
177303
|
var LEASE_HEARTBEAT_INTERVAL_MS = 60 * 1000;
|
|
177102
177304
|
function startLeaseHeartbeat(db, holderId, leaseKey, onLost, intervalMs = LEASE_HEARTBEAT_INTERVAL_MS) {
|
|
177103
177305
|
let lost = false;
|
|
@@ -177892,6 +178094,39 @@ function hasShareabilitySensitiveText(text) {
|
|
|
177892
178094
|
}
|
|
177893
178095
|
}
|
|
177894
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
|
+
|
|
177895
178130
|
// ../plugin/src/features/magic-context/dreamer/classify-prompt.ts
|
|
177896
178131
|
var SCORING_GUIDANCE = `### How to score importance (1-100)
|
|
177897
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.
|
|
@@ -177958,14 +178193,15 @@ ${renderPool(args.memories)}`;
|
|
|
177958
178193
|
var SCOPES = new Set(["project", "ecosystem", "universe"]);
|
|
177959
178194
|
function parseClassifyManifest(text) {
|
|
177960
178195
|
const out = [];
|
|
177961
|
-
|
|
178196
|
+
const body = extractCompleteManifestBody(text, "classify");
|
|
178197
|
+
for (const m of body.matchAll(/<memory\b([^>]*)\/?>/g)) {
|
|
177962
178198
|
const attrs = m[1];
|
|
177963
178199
|
const idMatch = attrs.match(/\bid\s*=\s*"(\d+)"/);
|
|
177964
178200
|
if (!idMatch)
|
|
177965
|
-
|
|
178201
|
+
throw new Error("classify manifest entry missing numeric id");
|
|
177966
178202
|
const id = Number.parseInt(idMatch[1], 10);
|
|
177967
178203
|
if (!Number.isInteger(id))
|
|
177968
|
-
|
|
178204
|
+
throw new Error("classify manifest entry missing numeric id");
|
|
177969
178205
|
const entry = { id };
|
|
177970
178206
|
const impMatch = attrs.match(/\bimportance\s*=\s*"(\d+)"/);
|
|
177971
178207
|
if (impMatch) {
|
|
@@ -177974,18 +178210,23 @@ function parseClassifyManifest(text) {
|
|
|
177974
178210
|
entry.importance = Math.max(1, Math.min(100, imp));
|
|
177975
178211
|
}
|
|
177976
178212
|
const scopeMatch = attrs.match(/\bscope\s*=\s*"([a-z]+)"/i);
|
|
177977
|
-
if (scopeMatch
|
|
177978
|
-
|
|
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;
|
|
177979
178218
|
}
|
|
177980
178219
|
const shareMatch = attrs.match(/\bshareable\s*=\s*"(true|false|1|0)"/i);
|
|
177981
178220
|
if (shareMatch) {
|
|
177982
178221
|
const v = shareMatch[1].toLowerCase();
|
|
177983
178222
|
entry.shareable = v === "true" || v === "1";
|
|
177984
178223
|
}
|
|
177985
|
-
if (entry.importance
|
|
177986
|
-
|
|
178224
|
+
if (entry.importance === undefined && !entry.scope && entry.shareable === undefined) {
|
|
178225
|
+
throw new Error(`classify manifest entry ${id} missing classification fields`);
|
|
177987
178226
|
}
|
|
178227
|
+
out.push(entry);
|
|
177988
178228
|
}
|
|
178229
|
+
assertNoDuplicateManifestIds(out.map((entry) => entry.id), "classify");
|
|
177989
178230
|
return out;
|
|
177990
178231
|
}
|
|
177991
178232
|
|
|
@@ -178120,9 +178361,13 @@ async function classifyOneChunk(args, chunk, anchors, sliceMs, signal) {
|
|
|
178120
178361
|
});
|
|
178121
178362
|
},
|
|
178122
178363
|
validateOutput: (messages) => {
|
|
178364
|
+
if (hasLengthCappedOutput(messages)) {
|
|
178365
|
+
throw new Error("classify returned length-capped output");
|
|
178366
|
+
}
|
|
178123
178367
|
const text = extractLatestAssistantText(messages);
|
|
178124
178368
|
if (!text)
|
|
178125
178369
|
throw new Error("classify returned no output");
|
|
178370
|
+
parseClassifyManifest(text);
|
|
178126
178371
|
return text;
|
|
178127
178372
|
}
|
|
178128
178373
|
});
|
|
@@ -178148,17 +178393,13 @@ async function classifyOneChunk(args, chunk, anchors, sliceMs, signal) {
|
|
|
178148
178393
|
}
|
|
178149
178394
|
function applyClassifications(args, chunk, manifestText) {
|
|
178150
178395
|
const byId = new Map(chunk.map((m) => [m.id, m]));
|
|
178151
|
-
const parsed = parseClassifyManifest(manifestText)
|
|
178396
|
+
const parsed = parseClassifyManifest(manifestText);
|
|
178397
|
+
assertManifestCoversExactly(parsed.map((entry) => entry.id), new Set(byId.keys()), "classify");
|
|
178152
178398
|
if (parsed.length === 0)
|
|
178153
178399
|
return { classified: 0, changed: 0 };
|
|
178154
178400
|
let classified = 0;
|
|
178155
178401
|
let changed = 0;
|
|
178156
|
-
|
|
178157
|
-
args.db.transaction(() => {
|
|
178158
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
178159
|
-
leaseLost = true;
|
|
178160
|
-
return;
|
|
178161
|
-
}
|
|
178402
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
178162
178403
|
for (const p of parsed) {
|
|
178163
178404
|
const memory = byId.get(p.id);
|
|
178164
178405
|
if (!memory)
|
|
@@ -178173,9 +178414,7 @@ function applyClassifications(args, chunk, manifestText) {
|
|
|
178173
178414
|
if (didChange)
|
|
178174
178415
|
changed += 1;
|
|
178175
178416
|
}
|
|
178176
|
-
})
|
|
178177
|
-
if (leaseLost)
|
|
178178
|
-
throw new Error("Dream lease lost during classify commit");
|
|
178417
|
+
});
|
|
178179
178418
|
return { classified, changed };
|
|
178180
178419
|
}
|
|
178181
178420
|
function recordInvocation(args, startedAt, params) {
|
|
@@ -178223,9 +178462,11 @@ var SMART_NOTE_CHECK_LIVENESS_RECHECK_MS = 24 * 60 * 60 * 1000;
|
|
|
178223
178462
|
|
|
178224
178463
|
class SmartNoteNetworkError extends Error {
|
|
178225
178464
|
isSmartNoteNetworkError = true;
|
|
178226
|
-
|
|
178465
|
+
terminal;
|
|
178466
|
+
constructor(message, options = {}) {
|
|
178227
178467
|
super(message);
|
|
178228
178468
|
this.name = "SmartNoteNetworkError";
|
|
178469
|
+
this.terminal = options.terminal ?? false;
|
|
178229
178470
|
}
|
|
178230
178471
|
}
|
|
178231
178472
|
|
|
@@ -178239,6 +178480,9 @@ class SmartNoteSecurityError extends Error {
|
|
|
178239
178480
|
function isSmartNoteNetworkError(error51) {
|
|
178240
178481
|
return error51 instanceof SmartNoteNetworkError || error51 instanceof Error && (error51.name === "SmartNoteNetworkError" || error51.message.includes("SmartNoteNetworkError") || error51.message.includes("SMART_NOTE_NETWORK"));
|
|
178241
178482
|
}
|
|
178483
|
+
function isTerminalSmartNoteNetworkError(error51) {
|
|
178484
|
+
return error51 instanceof SmartNoteNetworkError && error51.terminal;
|
|
178485
|
+
}
|
|
178242
178486
|
function parseSmartNoteManifest(json2) {
|
|
178243
178487
|
if (!json2)
|
|
178244
178488
|
return { capabilities: [] };
|
|
@@ -178268,6 +178512,7 @@ function stringArray(value) {
|
|
|
178268
178512
|
var DNS_TIMEOUT_MS = 3000;
|
|
178269
178513
|
var DEFAULT_HTTP_TIMEOUT_MS = 5000;
|
|
178270
178514
|
var DEFAULT_HTTP_BODY_LIMIT_BYTES = 64 * 1024;
|
|
178515
|
+
var MAX_HTTP_ADDRESS_CANDIDATES = 4;
|
|
178271
178516
|
var defaultResolver = {
|
|
178272
178517
|
async lookup(hostname3, signal) {
|
|
178273
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 })));
|
|
@@ -178304,18 +178549,21 @@ async function guardedSmartNoteHttpGet(input, options) {
|
|
|
178304
178549
|
});
|
|
178305
178550
|
const timeoutMs = options.timeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;
|
|
178306
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);
|
|
178307
178554
|
let lastError;
|
|
178308
|
-
for (const candidate of
|
|
178555
|
+
for (const candidate of candidates) {
|
|
178309
178556
|
try {
|
|
178310
|
-
return await
|
|
178557
|
+
return await requestAddress(validation, candidate, {
|
|
178311
178558
|
signal: options.signal,
|
|
178312
178559
|
timeoutMs,
|
|
178313
178560
|
bodyLimitBytes
|
|
178314
178561
|
});
|
|
178315
178562
|
} catch (error51) {
|
|
178316
178563
|
lastError = error51;
|
|
178317
|
-
if (error51 instanceof SmartNoteSecurityError || options.signal.aborted)
|
|
178564
|
+
if (error51 instanceof SmartNoteSecurityError || options.signal.aborted || isTerminalSmartNoteNetworkError(error51)) {
|
|
178318
178565
|
throw error51;
|
|
178566
|
+
}
|
|
178319
178567
|
}
|
|
178320
178568
|
}
|
|
178321
178569
|
throw toNetworkError(lastError, "all validated addresses failed");
|
|
@@ -178385,7 +178633,9 @@ function requestValidatedAddress(validation, candidate, options) {
|
|
|
178385
178633
|
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
178386
178634
|
bytes += buf.byteLength;
|
|
178387
178635
|
if (bytes > options.bodyLimitBytes) {
|
|
178388
|
-
|
|
178636
|
+
reject(new SmartNoteNetworkError("SMART_NOTE_NETWORK: response body too large", { terminal: true }));
|
|
178637
|
+
response.destroy();
|
|
178638
|
+
request2.destroy();
|
|
178389
178639
|
return;
|
|
178390
178640
|
}
|
|
178391
178641
|
chunks.push(buf);
|
|
@@ -178402,10 +178652,16 @@ function requestValidatedAddress(validation, candidate, options) {
|
|
|
178402
178652
|
resolve({ status, body: Buffer.concat(chunks).toString("utf8") });
|
|
178403
178653
|
});
|
|
178404
178654
|
});
|
|
178405
|
-
const onAbort = () =>
|
|
178655
|
+
const onAbort = () => {
|
|
178656
|
+
reject(new SmartNoteNetworkError("SMART_NOTE_NETWORK: aborted"));
|
|
178657
|
+
request2.destroy();
|
|
178658
|
+
};
|
|
178406
178659
|
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
178407
178660
|
request2.on("timeout", () => {
|
|
178408
|
-
|
|
178661
|
+
reject(new SmartNoteNetworkError("SMART_NOTE_NETWORK: request timed out", {
|
|
178662
|
+
terminal: true
|
|
178663
|
+
}));
|
|
178664
|
+
request2.destroy();
|
|
178409
178665
|
});
|
|
178410
178666
|
request2.on("error", (error51) => {
|
|
178411
178667
|
options.signal.removeEventListener("abort", onAbort);
|
|
@@ -178753,6 +179009,20 @@ function withSandboxLock(fn) {
|
|
|
178753
179009
|
var DEFAULT_TIMEOUT_MS = 2000;
|
|
178754
179010
|
var DEFAULT_HEAP_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
178755
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
|
+
}
|
|
178756
179026
|
async function runCompiledSmartNoteCheck(options) {
|
|
178757
179027
|
return withSandboxLock(() => runCompiledSmartNoteCheckLocked(options));
|
|
178758
179028
|
}
|
|
@@ -178760,17 +179030,24 @@ async function runCompiledSmartNoteCheckLocked(options) {
|
|
|
178760
179030
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
178761
179031
|
const controller = new AbortController;
|
|
178762
179032
|
const externalAbort = () => controller.abort(options.signal?.reason);
|
|
178763
|
-
options.signal?.
|
|
179033
|
+
if (options.signal?.aborted) {
|
|
179034
|
+
externalAbort();
|
|
179035
|
+
} else {
|
|
179036
|
+
options.signal?.addEventListener("abort", externalAbort, { once: true });
|
|
179037
|
+
}
|
|
178764
179038
|
const timer = setTimeout(() => controller.abort(new Error("smart-note check timed out")), timeoutMs);
|
|
178765
179039
|
try {
|
|
179040
|
+
throwIfRunAborted(controller.signal);
|
|
179041
|
+
const capabilities = resolveCapabilitiesForRun(options, controller.signal);
|
|
178766
179042
|
const deadline = Date.now() + timeoutMs;
|
|
178767
179043
|
const quickjs = await getAsyncModule();
|
|
179044
|
+
throwIfRunAborted(controller.signal);
|
|
178768
179045
|
const context = quickjs.newContext();
|
|
178769
179046
|
try {
|
|
178770
179047
|
context.runtime.setMemoryLimit(options.heapLimitBytes ?? DEFAULT_HEAP_LIMIT_BYTES);
|
|
178771
179048
|
context.runtime.setMaxStackSize(options.stackLimitBytes ?? DEFAULT_STACK_LIMIT_BYTES);
|
|
178772
179049
|
context.runtime.setInterruptHandler(() => controller.signal.aborted || Date.now() > deadline);
|
|
178773
|
-
installCapabilityObject(context,
|
|
179050
|
+
installCapabilityObject(context, capabilities);
|
|
178774
179051
|
disableAmbientDynamicCode(context);
|
|
178775
179052
|
const result = await evalCheck(context, options.compiledCheck);
|
|
178776
179053
|
const checkResult = result;
|
|
@@ -178923,6 +179200,7 @@ Remember: output only the JSON object described by the system prompt.`;
|
|
|
178923
179200
|
}
|
|
178924
179201
|
}, {
|
|
178925
179202
|
timeoutMs: remainingMs,
|
|
179203
|
+
signal: args.signal,
|
|
178926
179204
|
fallbackModels: args.fallbackModels,
|
|
178927
179205
|
callContext: "dreamer:smart-note-compiler",
|
|
178928
179206
|
fetchOutput: async () => {
|
|
@@ -178947,7 +179225,8 @@ Remember: output only the JSON object described by the system prompt.`;
|
|
|
178947
179225
|
}
|
|
178948
179226
|
const dryRun = await runCompiledSmartNoteCheck({
|
|
178949
179227
|
compiledCheck,
|
|
178950
|
-
|
|
179228
|
+
capabilityFactory: args.capabilityFactory,
|
|
179229
|
+
signal: args.signal,
|
|
178951
179230
|
timeoutMs: 2000
|
|
178952
179231
|
});
|
|
178953
179232
|
if (!dryRun.ok) {
|
|
@@ -179095,7 +179374,9 @@ function nextSmartNoteCheckDueAt(cron, options = {}) {
|
|
|
179095
179374
|
const rawNext = cron?.trim() ? nextDueAtMs(cron, now) : null;
|
|
179096
179375
|
const rawDelta = rawNext ? rawNext - now : SMART_NOTE_CHECK_DEFAULT_INTERVAL_MS;
|
|
179097
179376
|
const clamped = Math.min(ceilingMs, Math.max(floorMs, rawDelta));
|
|
179098
|
-
|
|
179377
|
+
const jittered = clamped + deterministicJitterMs(clamped, options.noteId, options.hash);
|
|
179378
|
+
const bounded = Math.min(ceilingMs, Math.max(floorMs, jittered));
|
|
179379
|
+
return now + bounded;
|
|
179099
179380
|
}
|
|
179100
179381
|
function deterministicJitterMs(intervalMs, noteId, hash2) {
|
|
179101
179382
|
const max = Math.min(60000, Math.floor(intervalMs * 0.1));
|
|
@@ -179264,9 +179545,9 @@ async function runDueCompiledSmartNoteChecks(args) {
|
|
|
179264
179545
|
try {
|
|
179265
179546
|
const result = await runCompiledSmartNoteCheck({
|
|
179266
179547
|
compiledCheck: note.compiledCheck,
|
|
179267
|
-
|
|
179548
|
+
capabilityFactory: (signal) => createSmartNoteCapabilities({
|
|
179268
179549
|
projectRoot: args.projectRoot,
|
|
179269
|
-
signal
|
|
179550
|
+
signal
|
|
179270
179551
|
}),
|
|
179271
179552
|
signal: controller.signal,
|
|
179272
179553
|
timeoutMs: Math.min(2000, remaining)
|
|
@@ -179329,6 +179610,24 @@ var MAX_COMPILE_PER_RUN = 5;
|
|
|
179329
179610
|
var MAX_FALLBACK_PER_RUN = 3;
|
|
179330
179611
|
var MAX_COMPILATION_FAILURES = 3;
|
|
179331
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
|
+
}
|
|
179332
179631
|
async function evaluateSmartNotes(args) {
|
|
179333
179632
|
const projectRoot = args.sessionDirectory ?? args.projectIdentity;
|
|
179334
179633
|
const pendingAtStart = getPendingSmartNotes(args.db, args.projectIdentity).length;
|
|
@@ -179337,6 +179636,7 @@ async function evaluateSmartNotes(args) {
|
|
|
179337
179636
|
return { surfaced: 0, pending: 0, ran: false };
|
|
179338
179637
|
}
|
|
179339
179638
|
let leaseLost = false;
|
|
179639
|
+
const leaseAbortController = new AbortController;
|
|
179340
179640
|
const leaseHeld = () => !leaseLost && peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey);
|
|
179341
179641
|
const assertLeaseHeld = (phase) => {
|
|
179342
179642
|
if (!leaseHeld()) {
|
|
@@ -179346,6 +179646,7 @@ async function evaluateSmartNotes(args) {
|
|
|
179346
179646
|
};
|
|
179347
179647
|
const heartbeat = startLeaseHeartbeat(args.db, args.holderId, args.leaseKey, () => {
|
|
179348
179648
|
leaseLost = true;
|
|
179649
|
+
leaseAbortController.abort(new Error("Dream lease lost during smart notes"));
|
|
179349
179650
|
log("[dreamer] smart notes: lease lost — aborting");
|
|
179350
179651
|
args.onLeaseLost?.("smart notes");
|
|
179351
179652
|
});
|
|
@@ -179368,7 +179669,7 @@ async function evaluateSmartNotes(args) {
|
|
|
179368
179669
|
break;
|
|
179369
179670
|
assertLeaseHeld("compile start");
|
|
179370
179671
|
didWork = true;
|
|
179371
|
-
const compiled = await compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld);
|
|
179672
|
+
const compiled = await compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld, leaseAbortController.signal);
|
|
179372
179673
|
if (compiled)
|
|
179373
179674
|
surfaced += 1;
|
|
179374
179675
|
}
|
|
@@ -179388,7 +179689,7 @@ async function evaluateSmartNotes(args) {
|
|
|
179388
179689
|
break;
|
|
179389
179690
|
assertLeaseHeld("fallback start");
|
|
179390
179691
|
didWork = true;
|
|
179391
|
-
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);
|
|
179392
179693
|
const now = Date.now();
|
|
179393
179694
|
assertLeaseHeld("fallback commit");
|
|
179394
179695
|
commitSmartNoteState(args.db, {
|
|
@@ -179414,9 +179715,8 @@ async function evaluateSmartNotes(args) {
|
|
|
179414
179715
|
heartbeat.stop();
|
|
179415
179716
|
}
|
|
179416
179717
|
}
|
|
179417
|
-
async function compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld) {
|
|
179418
|
-
const
|
|
179419
|
-
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");
|
|
179420
179720
|
try {
|
|
179421
179721
|
const result = await compileSmartNoteCheck({
|
|
179422
179722
|
client: args.client,
|
|
@@ -179425,7 +179725,8 @@ async function compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld)
|
|
|
179425
179725
|
sessionDirectory: args.sessionDirectory,
|
|
179426
179726
|
projectIdentity: args.projectIdentity,
|
|
179427
179727
|
note,
|
|
179428
|
-
|
|
179728
|
+
capabilityFactory: (signal) => createSmartNoteCapabilities({ projectRoot, signal }),
|
|
179729
|
+
signal: promptSignal.signal,
|
|
179429
179730
|
deadline: args.deadline,
|
|
179430
179731
|
model: args.model,
|
|
179431
179732
|
fallbackModels: args.fallbackModels
|
|
@@ -179470,7 +179771,7 @@ async function compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld)
|
|
|
179470
179771
|
});
|
|
179471
179772
|
return result.dryRun.met;
|
|
179472
179773
|
} finally {
|
|
179473
|
-
|
|
179774
|
+
promptSignal.cleanup();
|
|
179474
179775
|
}
|
|
179475
179776
|
}
|
|
179476
179777
|
async function runLivenessCheck(args, note, projectRoot, assertLeaseHeld, leaseHeld) {
|
|
@@ -179481,7 +179782,7 @@ async function runLivenessCheck(args, note, projectRoot, assertLeaseHeld, leaseH
|
|
|
179481
179782
|
try {
|
|
179482
179783
|
const result = await runCompiledSmartNoteCheck({
|
|
179483
179784
|
compiledCheck: note.compiledCheck,
|
|
179484
|
-
|
|
179785
|
+
capabilityFactory: (signal) => createSmartNoteCapabilities({ projectRoot, signal }),
|
|
179485
179786
|
signal: controller.signal,
|
|
179486
179787
|
timeoutMs: 2000
|
|
179487
179788
|
});
|
|
@@ -179510,7 +179811,7 @@ async function runLivenessCheck(args, note, projectRoot, assertLeaseHeld, leaseH
|
|
|
179510
179811
|
clearTimeout(timer);
|
|
179511
179812
|
}
|
|
179512
179813
|
}
|
|
179513
|
-
async function confirmReadOnly(args, noteId, content, surfaceCondition) {
|
|
179814
|
+
async function confirmReadOnly(args, noteId, content, surfaceCondition, leaseSignal) {
|
|
179514
179815
|
let childSessionId = null;
|
|
179515
179816
|
const startedAt = Date.now();
|
|
179516
179817
|
let invocationRecorded = false;
|
|
@@ -179553,42 +179854,49 @@ Note content: ${JSON.stringify(content)}
|
|
|
179553
179854
|
Surface condition: ${JSON.stringify(surfaceCondition ?? "")}
|
|
179554
179855
|
|
|
179555
179856
|
Output exactly JSON: {"met": false}`;
|
|
179556
|
-
const
|
|
179557
|
-
|
|
179558
|
-
|
|
179559
|
-
|
|
179560
|
-
|
|
179561
|
-
|
|
179562
|
-
|
|
179563
|
-
|
|
179564
|
-
|
|
179565
|
-
|
|
179566
|
-
|
|
179567
|
-
|
|
179568
|
-
|
|
179569
|
-
|
|
179570
|
-
|
|
179571
|
-
|
|
179572
|
-
|
|
179573
|
-
|
|
179574
|
-
|
|
179575
|
-
|
|
179576
|
-
|
|
179577
|
-
|
|
179578
|
-
|
|
179579
|
-
|
|
179580
|
-
|
|
179581
|
-
|
|
179582
|
-
|
|
179583
|
-
|
|
179584
|
-
|
|
179585
|
-
|
|
179586
|
-
|
|
179587
|
-
|
|
179588
|
-
|
|
179589
|
-
|
|
179590
|
-
|
|
179591
|
-
|
|
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
|
+
}
|
|
179592
179900
|
recordInvocation2({ status: "completed", messages: run.output });
|
|
179593
179901
|
return run.validated;
|
|
179594
179902
|
} catch (error51) {
|
|
@@ -179803,19 +180111,21 @@ ${list}
|
|
|
179803
180111
|
}
|
|
179804
180112
|
function parseMapMemoriesManifest(text) {
|
|
179805
180113
|
const out = [];
|
|
179806
|
-
|
|
180114
|
+
const body = extractCompleteManifestBody(text, "mappings");
|
|
180115
|
+
for (const m of body.matchAll(/<memory\b([^>]*)\/?>/g)) {
|
|
179807
180116
|
const attrs = m[1];
|
|
179808
180117
|
const idMatch = attrs.match(/\bid\s*=\s*"(\d+)"/);
|
|
179809
180118
|
if (!idMatch)
|
|
179810
|
-
|
|
180119
|
+
throw new Error("mappings manifest entry missing numeric id");
|
|
179811
180120
|
const id = Number.parseInt(idMatch[1], 10);
|
|
179812
180121
|
if (!Number.isInteger(id))
|
|
179813
|
-
|
|
180122
|
+
throw new Error("mappings manifest entry missing numeric id");
|
|
179814
180123
|
const independent = /\bindependent\s*=\s*"(?:true|1)"/i.test(attrs);
|
|
179815
180124
|
const filesMatch = attrs.match(/\bfiles\s*=\s*"([^"]*)"/);
|
|
179816
180125
|
const files = filesMatch ? filesMatch[1].split(",").map((f) => f.trim()).filter(Boolean) : [];
|
|
179817
180126
|
out.push({ id, files, independent: independent || files.length === 0 });
|
|
179818
180127
|
}
|
|
180128
|
+
assertNoDuplicateManifestIds(out.map((entry) => entry.id), "mappings");
|
|
179819
180129
|
return out;
|
|
179820
180130
|
}
|
|
179821
180131
|
|
|
@@ -179904,9 +180214,13 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
179904
180214
|
});
|
|
179905
180215
|
},
|
|
179906
180216
|
validateOutput: (messages) => {
|
|
180217
|
+
if (hasLengthCappedOutput(messages)) {
|
|
180218
|
+
throw new Error("map-memories returned length-capped output");
|
|
180219
|
+
}
|
|
179907
180220
|
const text = extractLatestAssistantText(messages);
|
|
179908
180221
|
if (!text)
|
|
179909
180222
|
throw new Error("map-memories returned no output");
|
|
180223
|
+
parseMapMemoriesManifest(text);
|
|
179910
180224
|
return text;
|
|
179911
180225
|
}
|
|
179912
180226
|
});
|
|
@@ -179932,7 +180246,8 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
179932
180246
|
}
|
|
179933
180247
|
async function applyBatchMappings(args, batch, manifestText) {
|
|
179934
180248
|
const batchIds = new Set(batch.map((m) => m.id));
|
|
179935
|
-
const parsed = parseMapMemoriesManifest(manifestText)
|
|
180249
|
+
const parsed = parseMapMemoriesManifest(manifestText);
|
|
180250
|
+
assertManifestCoversExactly(parsed.map((entry) => entry.id), batchIds, "mappings");
|
|
179936
180251
|
if (parsed.length === 0)
|
|
179937
180252
|
return { mapped: 0, independent: 0 };
|
|
179938
180253
|
const planned = [];
|
|
@@ -179954,12 +180269,7 @@ async function applyBatchMappings(args, batch, manifestText) {
|
|
|
179954
180269
|
const now = Date.now();
|
|
179955
180270
|
let mapped = 0;
|
|
179956
180271
|
let independent = 0;
|
|
179957
|
-
|
|
179958
|
-
args.db.transaction(() => {
|
|
179959
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
179960
|
-
leaseLost = true;
|
|
179961
|
-
return;
|
|
179962
|
-
}
|
|
180272
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
179963
180273
|
for (const item of planned) {
|
|
179964
180274
|
recordMemoryMapping(args.db, item.id, item.files, now);
|
|
179965
180275
|
if (item.independent)
|
|
@@ -179967,9 +180277,7 @@ async function applyBatchMappings(args, batch, manifestText) {
|
|
|
179967
180277
|
else
|
|
179968
180278
|
mapped += 1;
|
|
179969
180279
|
}
|
|
179970
|
-
})
|
|
179971
|
-
if (leaseLost)
|
|
179972
|
-
throw new Error("Dream lease lost during map-memories commit");
|
|
180280
|
+
});
|
|
179973
180281
|
return { mapped, independent };
|
|
179974
180282
|
}
|
|
179975
180283
|
function recordInvocation2(args, startedAt, params) {
|
|
@@ -180197,11 +180505,7 @@ async function promotePrimers(args) {
|
|
|
180197
180505
|
activePrimers: primers,
|
|
180198
180506
|
threshold: PRIMER_CLUSTER_THRESHOLD
|
|
180199
180507
|
});
|
|
180200
|
-
args.db.
|
|
180201
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
180202
|
-
leaseLost = true;
|
|
180203
|
-
return;
|
|
180204
|
-
}
|
|
180508
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
180205
180509
|
for (const cluster of clusters) {
|
|
180206
180510
|
if (cluster.candidates.length === 0)
|
|
180207
180511
|
continue;
|
|
@@ -180232,7 +180536,7 @@ async function promotePrimers(args) {
|
|
|
180232
180536
|
});
|
|
180233
180537
|
result.promoted += 1;
|
|
180234
180538
|
}
|
|
180235
|
-
})
|
|
180539
|
+
});
|
|
180236
180540
|
if (leaseLost)
|
|
180237
180541
|
throw new Error("Dream lease lost during promote-primers commit");
|
|
180238
180542
|
log(`[dreamer] primers: candidates=${result.candidates} promoted=${result.promoted} updated=${result.updated}`);
|
|
@@ -180500,16 +180804,9 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
|
|
|
180500
180804
|
log(`[dreamer] refresh-primers: primer #${primer.id} answer not committed (no investigation tool calls)`);
|
|
180501
180805
|
return false;
|
|
180502
180806
|
}
|
|
180503
|
-
|
|
180504
|
-
args.db.transaction(() => {
|
|
180505
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
180506
|
-
leaseLost = true;
|
|
180507
|
-
return;
|
|
180508
|
-
}
|
|
180807
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
180509
180808
|
updatePrimerAnswer(args.db, primer.id, answer);
|
|
180510
|
-
})
|
|
180511
|
-
if (leaseLost)
|
|
180512
|
-
throw new Error("Dream lease lost during refresh-primers commit");
|
|
180809
|
+
});
|
|
180513
180810
|
return true;
|
|
180514
180811
|
} catch (error51) {
|
|
180515
180812
|
const desc = describeError(error51);
|
|
@@ -180706,8 +181003,7 @@ class OpenCodeRetrospectiveRawProvider {
|
|
|
180706
181003
|
LEFT JOIN session_meta m ON m.session_id = sp.session_id
|
|
180707
181004
|
WHERE sp.project_path = ? AND sp.harness = 'opencode'
|
|
180708
181005
|
AND COALESCE(m.is_subagent, 0) = 0
|
|
180709
|
-
ORDER BY sp.updated_at
|
|
180710
|
-
LIMIT ?`).all(projectIdentity, RETROSPECTIVE_MAX_SESSIONS_PER_RUN);
|
|
181006
|
+
ORDER BY sp.updated_at ASC, sp.session_id ASC`).all(projectIdentity);
|
|
180711
181007
|
return rows.map((row) => ({
|
|
180712
181008
|
sessionId: row.session_id,
|
|
180713
181009
|
updatedAt: typeof row.updated_at === "number" ? row.updated_at : undefined
|
|
@@ -180732,6 +181028,12 @@ class OpenCodeRetrospectiveRawProvider {
|
|
|
180732
181028
|
return { messages: [], truncated: false };
|
|
180733
181029
|
}
|
|
180734
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
|
+
}
|
|
180735
181037
|
readUserMessagesBefore(sessionId, beforeMs, count) {
|
|
180736
181038
|
const db = this.resolveDb();
|
|
180737
181039
|
if (!db)
|
|
@@ -180753,13 +181055,28 @@ class OpenCodeRetrospectiveRawProvider {
|
|
|
180753
181055
|
async function readRetrospectiveScanWindow(provider2, projectIdentity, watermarkMs, overlapUserCount, options) {
|
|
180754
181056
|
const maxMessages = options?.maxMessagesPerRun ?? RETROSPECTIVE_MAX_MESSAGES_PER_RUN;
|
|
180755
181057
|
const capPerSession = options?.capPerSession ?? RETROSPECTIVE_MAX_MESSAGES_PER_SESSION;
|
|
180756
|
-
const
|
|
181058
|
+
const sessionLimit = Math.max(1, Math.floor(options?.maxSessionsPerRun ?? RETROSPECTIVE_MAX_SESSIONS_PER_RUN));
|
|
180757
181059
|
try {
|
|
180758
|
-
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;
|
|
180759
181077
|
const sinceReads = [];
|
|
180760
|
-
|
|
180761
|
-
|
|
180762
|
-
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))));
|
|
180763
181080
|
}
|
|
180764
181081
|
let saturatedFrontier = Number.POSITIVE_INFINITY;
|
|
180765
181082
|
for (const read of sinceReads) {
|
|
@@ -180781,9 +181098,14 @@ async function readRetrospectiveScanWindow(provider2, projectIdentity, watermark
|
|
|
180781
181098
|
if (firstDropped) {
|
|
180782
181099
|
frontier = Math.min(frontier, firstDropped.ts - 1);
|
|
180783
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
|
+
}
|
|
180784
181106
|
maxScannedTs = Math.max(watermarkMs, Math.min(maxScannedTs, frontier));
|
|
180785
181107
|
const keptSessionIds = new Set(keptSince.map((message) => message.sessionId));
|
|
180786
|
-
const overlapSessions =
|
|
181108
|
+
const overlapSessions = sessionsToRead.filter((session) => keptSessionIds.has(session.sessionId));
|
|
180787
181109
|
const overlapBatches = overlapUserCount > 0 && watermarkMs > 0 ? await Promise.all(overlapSessions.map((session) => provider2.readUserMessagesBefore(session.sessionId, watermarkMs, overlapUserCount))) : [];
|
|
180788
181110
|
const seen = new Set;
|
|
180789
181111
|
const merged = [];
|
|
@@ -180811,6 +181133,24 @@ function readOpenCodeMessagesSince(db, sessionId, sinceMs, capPerSession) {
|
|
|
180811
181133
|
const kept = truncated ? rows.slice(0, limit) : rows;
|
|
180812
181134
|
return { messages: normalizeOpenCodeRows(db, sessionId, kept), truncated };
|
|
180813
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
|
+
}
|
|
180814
181154
|
function readOpenCodeUserMessagesBefore(db, sessionId, beforeMs, count) {
|
|
180815
181155
|
const want = Math.max(1, Math.floor(count));
|
|
180816
181156
|
const window = Math.max(want * 5, 32);
|
|
@@ -181103,7 +181443,7 @@ async function partitionVerifyScope(args) {
|
|
|
181103
181443
|
continue;
|
|
181104
181444
|
}
|
|
181105
181445
|
const files = v?.files ?? [];
|
|
181106
|
-
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);
|
|
181107
181447
|
if (needs)
|
|
181108
181448
|
inScope.push(toPrompt(m));
|
|
181109
181449
|
else
|
|
@@ -181169,22 +181509,26 @@ function filesOf(s) {
|
|
|
181169
181509
|
}
|
|
181170
181510
|
function parseVerifyManifest(text) {
|
|
181171
181511
|
const out = { verified: [], updated: [], archived: [] };
|
|
181172
|
-
|
|
181512
|
+
const body = extractCompleteManifestBody(text, "verify");
|
|
181513
|
+
for (const m of body.matchAll(/<verified\b([^>]*)\/?>/g)) {
|
|
181173
181514
|
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
181174
|
-
if (Number.isInteger(id))
|
|
181175
|
-
|
|
181515
|
+
if (!Number.isInteger(id))
|
|
181516
|
+
throw new Error("verify manifest entry missing numeric id");
|
|
181517
|
+
out.verified.push({ id, files: filesOf(m[1]) });
|
|
181176
181518
|
}
|
|
181177
|
-
for (const m of
|
|
181519
|
+
for (const m of body.matchAll(/<update\b([^>]*?)(?:\/>|>([\s\S]*?)<\/update>)/g)) {
|
|
181178
181520
|
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
181179
|
-
if (Number.isInteger(id))
|
|
181180
|
-
|
|
181181
|
-
}
|
|
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() });
|
|
181182
181524
|
}
|
|
181183
|
-
for (const m of
|
|
181525
|
+
for (const m of body.matchAll(/<archive\b([^>]*)\/?>/g)) {
|
|
181184
181526
|
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
181185
|
-
if (Number.isInteger(id))
|
|
181186
|
-
|
|
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") ?? "" });
|
|
181187
181530
|
}
|
|
181531
|
+
assertNoDuplicateManifestIds([...out.verified, ...out.updated, ...out.archived].map((entry) => entry.id), "verify");
|
|
181188
181532
|
return out;
|
|
181189
181533
|
}
|
|
181190
181534
|
|
|
@@ -181277,9 +181621,13 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
181277
181621
|
});
|
|
181278
181622
|
},
|
|
181279
181623
|
validateOutput: (messages) => {
|
|
181624
|
+
if (hasLengthCappedOutput(messages)) {
|
|
181625
|
+
throw new Error("verify returned length-capped output");
|
|
181626
|
+
}
|
|
181280
181627
|
const text = extractLatestAssistantText(messages);
|
|
181281
181628
|
if (!text)
|
|
181282
181629
|
throw new Error("verify returned no output");
|
|
181630
|
+
parseVerifyManifest(text);
|
|
181283
181631
|
return text;
|
|
181284
181632
|
}
|
|
181285
181633
|
});
|
|
@@ -181306,24 +181654,14 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
181306
181654
|
async function applyVerifyManifest(args, batch, manifestText) {
|
|
181307
181655
|
const batchIds = new Set(batch.map((m) => m.id));
|
|
181308
181656
|
const parsed = parseVerifyManifest(manifestText);
|
|
181657
|
+
assertManifestCoversExactly([...parsed.verified, ...parsed.updated, ...parsed.archived].map((entry) => entry.id), batchIds, "verify");
|
|
181309
181658
|
const now = Date.now();
|
|
181310
181659
|
const writes = [];
|
|
181311
|
-
const verdictCounts = new Map;
|
|
181312
|
-
for (const verdict of [...parsed.verified, ...parsed.updated, ...parsed.archived]) {
|
|
181313
|
-
if (!batchIds.has(verdict.id))
|
|
181314
|
-
continue;
|
|
181315
|
-
verdictCounts.set(verdict.id, (verdictCounts.get(verdict.id) ?? 0) + 1);
|
|
181316
|
-
}
|
|
181317
|
-
const conflictingIds = new Set(Array.from(verdictCounts.entries()).filter(([, count]) => count !== 1).map(([id]) => id));
|
|
181318
181660
|
for (const v of parsed.verified) {
|
|
181319
|
-
if (!batchIds.has(v.id) || conflictingIds.has(v.id))
|
|
181320
|
-
continue;
|
|
181321
181661
|
const files = await normalizeFiles(args, v.files);
|
|
181322
181662
|
writes.push({ kind: "verify", id: v.id, files });
|
|
181323
181663
|
}
|
|
181324
181664
|
for (const u of parsed.updated) {
|
|
181325
|
-
if (!batchIds.has(u.id) || conflictingIds.has(u.id))
|
|
181326
|
-
continue;
|
|
181327
181665
|
const content = u.content.trim();
|
|
181328
181666
|
if (!content || content.length > 20000) {
|
|
181329
181667
|
const files2 = await normalizeFiles(args, u.files);
|
|
@@ -181340,8 +181678,6 @@ async function applyVerifyManifest(args, batch, manifestText) {
|
|
|
181340
181678
|
});
|
|
181341
181679
|
}
|
|
181342
181680
|
for (const a of parsed.archived) {
|
|
181343
|
-
if (!batchIds.has(a.id) || conflictingIds.has(a.id))
|
|
181344
|
-
continue;
|
|
181345
181681
|
writes.push({ kind: "archive", id: a.id, reason: a.reason });
|
|
181346
181682
|
}
|
|
181347
181683
|
if (writes.length === 0)
|
|
@@ -181349,12 +181685,7 @@ async function applyVerifyManifest(args, batch, manifestText) {
|
|
|
181349
181685
|
let verified = 0;
|
|
181350
181686
|
let updated = 0;
|
|
181351
181687
|
let archived = 0;
|
|
181352
|
-
|
|
181353
|
-
args.db.transaction(() => {
|
|
181354
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
181355
|
-
leaseLost = true;
|
|
181356
|
-
return;
|
|
181357
|
-
}
|
|
181688
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
181358
181689
|
for (const w of writes) {
|
|
181359
181690
|
const memory = getMemoryById(args.db, w.id);
|
|
181360
181691
|
if (!isPrimaryMutable(memory))
|
|
@@ -181382,9 +181713,7 @@ async function applyVerifyManifest(args, batch, manifestText) {
|
|
|
181382
181713
|
archived += 1;
|
|
181383
181714
|
}
|
|
181384
181715
|
}
|
|
181385
|
-
})
|
|
181386
|
-
if (leaseLost)
|
|
181387
|
-
throw new Error("Dream lease lost during verify commit");
|
|
181716
|
+
});
|
|
181388
181717
|
return { verified, updated, archived };
|
|
181389
181718
|
}
|
|
181390
181719
|
async function normalizeFiles(args, rawFiles) {
|
|
@@ -181878,11 +182207,7 @@ async function runRetrospectiveTask(config2, ctx, helpers) {
|
|
|
181878
182207
|
throw new Error("Dream lease lost during retrospective");
|
|
181879
182208
|
const sourceSessionId = flagged[0]?.sessionId ?? userMessages[0]?.sessionId ?? "retrospective";
|
|
181880
182209
|
const learnings = parseRetrospectiveLearnings(deepenRun.validated);
|
|
181881
|
-
const applied = db
|
|
181882
|
-
if (!peekLeaseHolderAndExpiry(db, holderId, leaseKey)) {
|
|
181883
|
-
leaseLost = true;
|
|
181884
|
-
return null;
|
|
181885
|
-
}
|
|
182210
|
+
const applied = runLeaseGuardedWrite(db, holderId, leaseKey, () => {
|
|
181886
182211
|
const result = applyRetrospectiveLearnings({
|
|
181887
182212
|
db,
|
|
181888
182213
|
projectIdentity,
|
|
@@ -181893,7 +182218,7 @@ async function runRetrospectiveTask(config2, ctx, helpers) {
|
|
|
181893
182218
|
});
|
|
181894
182219
|
recordRetrospectiveWindowProcessed(db, projectIdentity, windowKey);
|
|
181895
182220
|
return result;
|
|
181896
|
-
})
|
|
182221
|
+
});
|
|
181897
182222
|
if (leaseLost || !applied)
|
|
181898
182223
|
throw new Error("Dream lease lost during retrospective commit");
|
|
181899
182224
|
log(`[dreamer] retrospective: flagged=${flagged.length} learnings=${learnings.length} memory=${applied.memoryWritten} observations=${applied.observationsInserted} dropped=${applied.observationsDropped} rejected=${applied.rejected.length}`);
|
|
@@ -183428,11 +183753,106 @@ function migrateLegacyExperimental(rawConfig, warnings) {
|
|
|
183428
183753
|
|
|
183429
183754
|
// ../plugin/src/config/project-security.ts
|
|
183430
183755
|
var HIDDEN_AGENT_KEYS = ["historian", "dreamer", "sidekick"];
|
|
183756
|
+
var HISTORIAN_USER_ONLY_FIELDS = ["model", "fallback_models"];
|
|
183431
183757
|
var AGENT_ESCALATION_FIELDS = ["prompt", "permission", "tools", "system_prompt"];
|
|
183432
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.";
|
|
183433
183762
|
function isPlainObject3(value) {
|
|
183434
183763
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
183435
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
|
+
}
|
|
183436
183856
|
function stripUnsafeProjectConfigFields(projectRaw) {
|
|
183437
183857
|
const warnings = [];
|
|
183438
183858
|
if ("auto_update" in projectRaw) {
|
|
@@ -183460,6 +183880,19 @@ function stripUnsafeProjectConfigFields(projectRaw) {
|
|
|
183460
183880
|
warnings.push(`Ignoring embedding.${removed.join("/")} from project config ` + "(security: a repository cannot choose where private text is embedded).");
|
|
183461
183881
|
}
|
|
183462
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
|
+
}
|
|
183463
183896
|
for (const agentKey of HIDDEN_AGENT_KEYS) {
|
|
183464
183897
|
const block = projectRaw[agentKey];
|
|
183465
183898
|
if (!isPlainObject3(block))
|
|
@@ -183477,6 +183910,101 @@ function stripUnsafeProjectConfigFields(projectRaw) {
|
|
|
183477
183910
|
}
|
|
183478
183911
|
return warnings;
|
|
183479
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
|
+
}
|
|
183480
184008
|
function normalizeEndpoint3(value) {
|
|
183481
184009
|
if (typeof value !== "string")
|
|
183482
184010
|
return;
|
|
@@ -183844,6 +184372,7 @@ function loadPiConfig(opts = {}) {
|
|
|
183844
184372
|
return a.scope === "user" ? -1 : 1;
|
|
183845
184373
|
});
|
|
183846
184374
|
const userRaw = mergeFiles.find((f) => f.scope === "user")?.config;
|
|
184375
|
+
const trustedBaseConfig = parsePiConfig(userRaw ?? {}).config;
|
|
183847
184376
|
for (const loaded of mergeFiles) {
|
|
183848
184377
|
const prefix = loaded.scope === "user" ? "[user config]" : "[project config]";
|
|
183849
184378
|
warnings.push(...loaded.warnings.map((warning) => `${prefix} ${warning}`));
|
|
@@ -183856,6 +184385,13 @@ function loadPiConfig(opts = {}) {
|
|
|
183856
184385
|
for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, rawConfig, userRaw)) {
|
|
183857
184386
|
warnings.push(`${prefix} ${warning}`);
|
|
183858
184387
|
}
|
|
184388
|
+
for (const warning of constrainProjectThresholdOverrides({
|
|
184389
|
+
mergedRaw: rawConfig,
|
|
184390
|
+
projectRaw,
|
|
184391
|
+
trustedBaseConfig
|
|
184392
|
+
})) {
|
|
184393
|
+
warnings.push(`${prefix} ${warning}`);
|
|
184394
|
+
}
|
|
183859
184395
|
} else {
|
|
183860
184396
|
rawConfig = mergeRawConfigs(rawConfig, loaded.config);
|
|
183861
184397
|
}
|
|
@@ -183954,6 +184490,7 @@ function loadPiConfigDetailed(opts = {}) {
|
|
|
183954
184490
|
return a.scope === "user" ? -1 : 1;
|
|
183955
184491
|
});
|
|
183956
184492
|
const userRaw = mergeFiles.find((f) => f.scope === "user")?.config;
|
|
184493
|
+
const trustedBaseConfig = parsePiConfig(userRaw ?? {}).config;
|
|
183957
184494
|
for (const loaded of mergeFiles) {
|
|
183958
184495
|
const prefix = loaded.scope === "user" ? "[user config]" : "[project config]";
|
|
183959
184496
|
warnings.push(...loaded.warnings.map((warning) => `${prefix} ${warning}`));
|
|
@@ -183966,6 +184503,13 @@ function loadPiConfigDetailed(opts = {}) {
|
|
|
183966
184503
|
for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, rawConfig, userRaw)) {
|
|
183967
184504
|
warnings.push(`${prefix} ${warning}`);
|
|
183968
184505
|
}
|
|
184506
|
+
for (const warning of constrainProjectThresholdOverrides({
|
|
184507
|
+
mergedRaw: rawConfig,
|
|
184508
|
+
projectRaw,
|
|
184509
|
+
trustedBaseConfig
|
|
184510
|
+
})) {
|
|
184511
|
+
warnings.push(`${prefix} ${warning}`);
|
|
184512
|
+
}
|
|
183969
184513
|
} else {
|
|
183970
184514
|
rawConfig = mergeRawConfigs(rawConfig, loaded.config);
|
|
183971
184515
|
}
|
|
@@ -184044,6 +184588,8 @@ function isMidTurnPi(event, _sessionId) {
|
|
|
184044
184588
|
}
|
|
184045
184589
|
if (latestAssistant === null)
|
|
184046
184590
|
return false;
|
|
184591
|
+
if (hasRealUserAfter(messages, latestAssistantIndex))
|
|
184592
|
+
return false;
|
|
184047
184593
|
if (latestAssistant.stopReason === "toolUse")
|
|
184048
184594
|
return true;
|
|
184049
184595
|
const toolCallIds = getToolCallIds(latestAssistant.content);
|
|
@@ -184066,6 +184612,15 @@ function isMidTurnPi(event, _sessionId) {
|
|
|
184066
184612
|
}
|
|
184067
184613
|
return false;
|
|
184068
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
|
+
}
|
|
184069
184624
|
function getToolCallIds(content) {
|
|
184070
184625
|
const ids = new Set;
|
|
184071
184626
|
if (!Array.isArray(content))
|
|
@@ -184388,7 +184943,7 @@ class PiRetrospectiveRawProvider {
|
|
|
184388
184943
|
updatedAt: typeof info.modified === "number" ? info.modified : undefined
|
|
184389
184944
|
});
|
|
184390
184945
|
}
|
|
184391
|
-
return result.sort((a, b) => (
|
|
184946
|
+
return result.sort((a, b) => (a.updatedAt ?? 0) - (b.updatedAt ?? 0));
|
|
184392
184947
|
}
|
|
184393
184948
|
async readUserMessagesSince(sessionId, sinceMs, capPerSession) {
|
|
184394
184949
|
const all = await this.loadUserEntries(sessionId);
|
|
@@ -184399,6 +184954,15 @@ class PiRetrospectiveRawProvider {
|
|
|
184399
184954
|
truncated: eligible.length > limit
|
|
184400
184955
|
};
|
|
184401
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
|
+
}
|
|
184402
184966
|
async readUserMessagesBefore(sessionId, beforeMs, count) {
|
|
184403
184967
|
const all = await this.loadUserEntries(sessionId);
|
|
184404
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)));
|
|
@@ -186158,6 +186722,8 @@ init_logger();
|
|
|
186158
186722
|
// ../plugin/src/hooks/magic-context/read-session-true-raw-tokens.ts
|
|
186159
186723
|
var MAX_MESSAGE_CACHE_ENTRIES = 1e5;
|
|
186160
186724
|
var MAX_MESSAGE_CACHE_KEY_BYTES = 64 * 1024 * 1024;
|
|
186725
|
+
var FNV1A_32_OFFSET = 2166136261;
|
|
186726
|
+
var FNV1A_32_PRIME = 16777619;
|
|
186161
186727
|
var messageEstimateCache = new Map;
|
|
186162
186728
|
var messageEstimateCacheBytes = 0;
|
|
186163
186729
|
var EMPTY_BREAKDOWN = {
|
|
@@ -186260,6 +186826,23 @@ function recursiveByteLength(value) {
|
|
|
186260
186826
|
}
|
|
186261
186827
|
return String(value).length;
|
|
186262
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
|
+
}
|
|
186263
186846
|
function rawPartVersion(part) {
|
|
186264
186847
|
return part.__magicContextPartUpdatedAt ?? part.updated_at ?? part.updatedAt ?? part.version ?? part.revision ?? "";
|
|
186265
186848
|
}
|
|
@@ -186607,13 +187190,12 @@ function buildTrueRawTokenIndex(sessionId, messages, options) {
|
|
|
186607
187190
|
function partContentFingerprint(part) {
|
|
186608
187191
|
if (!isRecord(part))
|
|
186609
187192
|
return `${typeof part}:${recursiveByteLength(part)}`;
|
|
186610
|
-
const type = partType(part);
|
|
186611
187193
|
const tool = toolSignalFromPart(part);
|
|
186612
187194
|
if (tool) {
|
|
186613
|
-
return
|
|
187195
|
+
return contentStringsHash([tool.inputText, tool.outputText]);
|
|
186614
187196
|
}
|
|
186615
187197
|
const text = firstStringField(part, ["text", "thinking", "reasoning", "content", "url"]) ?? "";
|
|
186616
|
-
return
|
|
187198
|
+
return contentStringsHash([text]);
|
|
186617
187199
|
}
|
|
186618
187200
|
function computeRawRangeFingerprint(messages, startInclusive, endExclusive) {
|
|
186619
187201
|
const pieces = [];
|
|
@@ -188870,24 +189452,46 @@ function getMessageSearchStatementWithCutoff(db) {
|
|
|
188870
189452
|
return stmt;
|
|
188871
189453
|
}
|
|
188872
189454
|
var ftsRowCountStatements = new WeakMap;
|
|
189455
|
+
var ftsRowCountStatementsWithCutoff = new WeakMap;
|
|
188873
189456
|
var ftsMatchCountStatements = new WeakMap;
|
|
188874
|
-
|
|
188875
|
-
|
|
188876
|
-
|
|
188877
|
-
|
|
188878
|
-
|
|
188879
|
-
|
|
188880
|
-
|
|
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);
|
|
188881
189475
|
return typeof row?.n === "number" ? row.n : 0;
|
|
188882
189476
|
}
|
|
188883
|
-
function countSessionFtsMatches(db, sessionId, ftsQuery) {
|
|
188884
|
-
|
|
188885
|
-
|
|
188886
|
-
|
|
188887
|
-
|
|
188888
|
-
|
|
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
|
+
})();
|
|
188889
189493
|
try {
|
|
188890
|
-
const row = stmt.get(sessionId, ftsQuery);
|
|
189494
|
+
const row = cutoff === null ? stmt.get(sessionId, ftsQuery) : stmt.get(sessionId, ftsQuery, cutoff);
|
|
188891
189495
|
return typeof row?.n === "number" ? row.n : 0;
|
|
188892
189496
|
} catch {
|
|
188893
189497
|
return 0;
|
|
@@ -189137,7 +189741,7 @@ function searchMessages(args) {
|
|
|
189137
189741
|
role: row.role
|
|
189138
189742
|
}));
|
|
189139
189743
|
}
|
|
189140
|
-
const corpusSize = getSessionFtsRowCount(args.db, args.sessionId);
|
|
189744
|
+
const corpusSize = getSessionFtsRowCount(args.db, args.sessionId, cutoff);
|
|
189141
189745
|
const queryLists = [];
|
|
189142
189746
|
if (baseQuery.length > 0) {
|
|
189143
189747
|
queryLists.push({
|
|
@@ -189150,7 +189754,7 @@ function searchMessages(args) {
|
|
|
189150
189754
|
const probeQuery = sanitizeFtsQuery(probe);
|
|
189151
189755
|
if (probeQuery.length === 0)
|
|
189152
189756
|
continue;
|
|
189153
|
-
const df = countSessionFtsMatches(args.db, args.sessionId, probeQuery);
|
|
189757
|
+
const df = countSessionFtsMatches(args.db, args.sessionId, probeQuery, cutoff);
|
|
189154
189758
|
const weight = probeDiscriminationWeight(df, corpusSize);
|
|
189155
189759
|
probeWeights.set(probe, weight);
|
|
189156
189760
|
queryLists.push({
|
|
@@ -200185,6 +200789,15 @@ async function executeContextRecompWithResult(deps, options = {}) {
|
|
|
200185
200789
|
published: false
|
|
200186
200790
|
};
|
|
200187
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
|
+
}
|
|
200188
200801
|
const renewal = startLeaseRenewal(deps, holderId);
|
|
200189
200802
|
const runnerDeps = withPublishedCallback({ ...deps, compartmentLeaseHolderId: holderId });
|
|
200190
200803
|
const promise2 = options.range ? executePartialRecompInternal(runnerDeps, options.range) : executeContextRecompInternal(runnerDeps);
|
|
@@ -201310,7 +201923,7 @@ function formatThresholdPercent(value) {
|
|
|
201310
201923
|
// package.json
|
|
201311
201924
|
var package_default = {
|
|
201312
201925
|
name: "@cortexkit/pi-magic-context",
|
|
201313
|
-
version: "0.31.
|
|
201926
|
+
version: "0.31.4",
|
|
201314
201927
|
type: "module",
|
|
201315
201928
|
description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
|
|
201316
201929
|
main: "dist/index.js",
|
|
@@ -201860,6 +202473,11 @@ function readHistorianState(db, sessionId, meta3) {
|
|
|
201860
202473
|
import * as crypto4 from "node:crypto";
|
|
201861
202474
|
var DEFAULT_MESSAGES_TO_KEEP = 20;
|
|
201862
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
|
+
}
|
|
201863
202481
|
function parseWrapupArgs(raw) {
|
|
201864
202482
|
const trimmed = raw.trim();
|
|
201865
202483
|
if (trimmed === "")
|
|
@@ -202077,11 +202695,12 @@ Eligible history is about ${initialPlan.snapshot.trueRawEligibleTokens.toLocaleS
|
|
|
202077
202695
|
Chunk ${chunkIndex}: wrapping messages ${plan.snapshot.offset}-${plan.snapshot.eligibleEndOrdinal - 1} (~${plan.snapshot.trueRawEligibleTokens.toLocaleString()} eligible tokens remain).`,
|
|
202078
202696
|
level: "info"
|
|
202079
202697
|
});
|
|
202080
|
-
const
|
|
202081
|
-
if (!
|
|
202082
|
-
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.";
|
|
202083
202701
|
break;
|
|
202084
202702
|
}
|
|
202703
|
+
const leaseHolder = leaseResult.holderId;
|
|
202085
202704
|
const leaseRenewal = setInterval(() => {
|
|
202086
202705
|
try {
|
|
202087
202706
|
renewCompartmentLease(deps.db, sessionId, leaseHolder);
|
|
@@ -202185,15 +202804,19 @@ function resolvePiContextLimit(ctx) {
|
|
|
202185
202804
|
}
|
|
202186
202805
|
return 128000;
|
|
202187
202806
|
}
|
|
202188
|
-
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());
|
|
202189
202810
|
for (;; ) {
|
|
202811
|
+
if (remainingMs() <= 0)
|
|
202812
|
+
return { ok: false, reason: "timeout" };
|
|
202190
202813
|
const holderId = crypto4.randomUUID();
|
|
202191
202814
|
const lease = acquireCompartmentLease(db, sessionId, holderId);
|
|
202192
202815
|
if (lease)
|
|
202193
|
-
return holderId;
|
|
202816
|
+
return { ok: true, holderId };
|
|
202194
202817
|
if (!renewWrapupMarker({}))
|
|
202195
|
-
return
|
|
202196
|
-
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())));
|
|
202197
202820
|
}
|
|
202198
202821
|
}
|
|
202199
202822
|
function estimateChunks(tokens, chunkTokens) {
|
|
@@ -206949,8 +207572,9 @@ function createCtxMemoryTool(deps) {
|
|
|
206949
207572
|
const isOwn = targetIdentityForStoredPath(memory2.projectPath) === projectIdentity;
|
|
206950
207573
|
if (isOwn)
|
|
206951
207574
|
return true;
|
|
206952
|
-
return toolShareCategories
|
|
207575
|
+
return toolShareCategories?.includes(memory2.category) ?? false;
|
|
206953
207576
|
};
|
|
207577
|
+
const memoryOwnedByTool = (memory2) => workspaceIdentitySet.identities.length > 1 ? targetIdentityForStoredPath(memory2.projectPath) === projectIdentity : storedPathBelongsToIdentity(memory2.projectPath, projectIdentity);
|
|
206954
207578
|
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
206955
207579
|
if (snapshot ? !snapshot.features.memoryEnabled : deps.memoryEnabled === false) {
|
|
206956
207580
|
return err2("Cross-session memory is disabled for this project.");
|
|
@@ -206997,7 +207621,8 @@ function createCtxMemoryTool(deps) {
|
|
|
206997
207621
|
return err2("Error: 'content' is required when action is 'update'.");
|
|
206998
207622
|
}
|
|
206999
207623
|
const memory2 = getMemoryById(deps.db, updateId);
|
|
207000
|
-
|
|
207624
|
+
const updateAllowed = memory2 ? dreamerAllowed ? memoryVisibleToTool(memory2) : memoryOwnedByTool(memory2) : false;
|
|
207625
|
+
if (!memory2 || !updateAllowed) {
|
|
207001
207626
|
return err2(`Error: Memory with ID ${updateId} was not found.`);
|
|
207002
207627
|
}
|
|
207003
207628
|
if (!dreamerAllowed && !isPrimaryMutableMemory(memory2)) {
|
|
@@ -207044,7 +207669,7 @@ function createCtxMemoryTool(deps) {
|
|
|
207044
207669
|
return err2("Error: One or more source memories were not found.");
|
|
207045
207670
|
}
|
|
207046
207671
|
if (!dreamerAllowed) {
|
|
207047
|
-
const foreign = sourceMemories.find((memory2) => !
|
|
207672
|
+
const foreign = sourceMemories.find((memory2) => !memoryOwnedByTool(memory2));
|
|
207048
207673
|
if (foreign) {
|
|
207049
207674
|
return err2(`Error: Memory with ID ${foreign.id} was not found.`);
|
|
207050
207675
|
}
|
|
@@ -207146,7 +207771,8 @@ function createCtxMemoryTool(deps) {
|
|
|
207146
207771
|
const archiveIds = [...new Set(rawArchiveIds)];
|
|
207147
207772
|
for (const memoryId of archiveIds) {
|
|
207148
207773
|
const memory2 = getMemoryById(deps.db, memoryId);
|
|
207149
|
-
|
|
207774
|
+
const archiveAllowed = memory2 ? dreamerAllowed ? memoryVisibleToTool(memory2) : memoryOwnedByTool(memory2) : false;
|
|
207775
|
+
if (!memory2 || !archiveAllowed) {
|
|
207150
207776
|
return err2(`Error: Memory with ID ${memoryId} was not found.`);
|
|
207151
207777
|
}
|
|
207152
207778
|
if (!dreamerAllowed && !isPrimaryMutableMemory(memory2)) {
|
|
@@ -207807,14 +208433,7 @@ import {
|
|
|
207807
208433
|
var TODO_TOOL_NAME = "todowrite";
|
|
207808
208434
|
var TODOS_COMMAND_NAME = "todos";
|
|
207809
208435
|
var WIDGET_KEY = "magic-context-todos";
|
|
207810
|
-
var
|
|
207811
|
-
var TODO_STATUSES = [
|
|
207812
|
-
"pending",
|
|
207813
|
-
"in_progress",
|
|
207814
|
-
"completed",
|
|
207815
|
-
"cancelled"
|
|
207816
|
-
];
|
|
207817
|
-
var TODO_PRIORITIES = ["high", "medium", "low"];
|
|
208436
|
+
var MAX_TODO_CONTENT_ROWS = 12;
|
|
207818
208437
|
var STATUS_GLYPH = {
|
|
207819
208438
|
pending: "○",
|
|
207820
208439
|
in_progress: "◐",
|
|
@@ -207828,10 +208447,10 @@ var STATUS_COLOR = {
|
|
|
207828
208447
|
cancelled: "error"
|
|
207829
208448
|
};
|
|
207830
208449
|
var snapshotsBySession = new Map;
|
|
207831
|
-
function
|
|
208450
|
+
function isTodoStatus2(value) {
|
|
207832
208451
|
return TODO_STATUSES.includes(value);
|
|
207833
208452
|
}
|
|
207834
|
-
function
|
|
208453
|
+
function isTodoPriority2(value) {
|
|
207835
208454
|
return TODO_PRIORITIES.includes(value);
|
|
207836
208455
|
}
|
|
207837
208456
|
function cloneTodos(todos) {
|
|
@@ -207845,14 +208464,14 @@ function parseTodos(input) {
|
|
|
207845
208464
|
if (item === null || typeof item !== "object")
|
|
207846
208465
|
return null;
|
|
207847
208466
|
const raw = item;
|
|
207848
|
-
if (typeof raw.content !== "string" || !
|
|
208467
|
+
if (typeof raw.content !== "string" || !isTodoStatus2(raw.status)) {
|
|
207849
208468
|
return null;
|
|
207850
208469
|
}
|
|
207851
208470
|
const todo = {
|
|
207852
208471
|
content: raw.content,
|
|
207853
208472
|
status: raw.status
|
|
207854
208473
|
};
|
|
207855
|
-
if (
|
|
208474
|
+
if (isTodoPriority2(raw.priority))
|
|
207856
208475
|
todo.priority = raw.priority;
|
|
207857
208476
|
if (typeof raw.id === "string" && raw.id.length > 0)
|
|
207858
208477
|
todo.id = raw.id;
|
|
@@ -207929,7 +208548,7 @@ function countTodos(todos) {
|
|
|
207929
208548
|
return counts;
|
|
207930
208549
|
}
|
|
207931
208550
|
function activeTitleCount(todos) {
|
|
207932
|
-
return todos.filter((todo) => todo.status
|
|
208551
|
+
return todos.filter((todo) => !TITLE_DONE_STATUSES.has(todo.status)).length;
|
|
207933
208552
|
}
|
|
207934
208553
|
function formatCounts(counts) {
|
|
207935
208554
|
if (counts.total === 0)
|
|
@@ -207963,6 +208582,16 @@ function lineComponent(renderLines) {
|
|
|
207963
208582
|
invalidate() {}
|
|
207964
208583
|
};
|
|
207965
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
|
+
}
|
|
207966
208595
|
function renderTodowriteCall(args, theme) {
|
|
207967
208596
|
const todos = parseTodos(args.todos) ?? [];
|
|
207968
208597
|
const active = activeTitleCount(todos);
|
|
@@ -207985,7 +208614,12 @@ function renderTodowriteResult(result, theme) {
|
|
|
207985
208614
|
return lineComponent((width) => {
|
|
207986
208615
|
if (renderedTodos.length === 0)
|
|
207987
208616
|
return [theme.fg("dim", "No todos")];
|
|
207988
|
-
|
|
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;
|
|
207989
208623
|
});
|
|
207990
208624
|
}
|
|
207991
208625
|
function registerTodosCommand(pi) {
|
|
@@ -208015,8 +208649,11 @@ function registerTodosCommand(pi) {
|
|
|
208015
208649
|
if (group.length === 0)
|
|
208016
208650
|
continue;
|
|
208017
208651
|
lines.push(heading);
|
|
208018
|
-
|
|
208652
|
+
const { visible, hiddenCount } = capTodoRows(group);
|
|
208653
|
+
for (const todo of visible)
|
|
208019
208654
|
lines.push(formatCommandLine(todo));
|
|
208655
|
+
if (hiddenCount > 0)
|
|
208656
|
+
lines.push(` +${hiddenCount} more`);
|
|
208020
208657
|
}
|
|
208021
208658
|
ctx.ui.notify(lines.join(`
|
|
208022
208659
|
`), "info");
|
|
@@ -208144,19 +208781,16 @@ class TodoOverlay {
|
|
|
208144
208781
|
const heading = `${theme.fg(headingColor, headingIcon)} ${theme.fg(headingColor, "Todos")} ${theme.fg("muted", "—")} ${theme.fg("dim", formatCounts(counts))}`;
|
|
208145
208782
|
const truncate3 = (line) => truncateToWidth2(line, width, "…");
|
|
208146
208783
|
const lines = [truncate3(heading)];
|
|
208147
|
-
const
|
|
208148
|
-
const visibleRows = hasTruncatedTail ? MAX_OVERLAY_CONTENT_ROWS - 1 : MAX_OVERLAY_CONTENT_ROWS;
|
|
208149
|
-
const truncatedTail = Math.max(0, overlayTodos.length - visibleRows);
|
|
208150
|
-
const visible = overlayTodos.slice(0, visibleRows);
|
|
208784
|
+
const { visible, hiddenCount } = capTodoRows(overlayTodos);
|
|
208151
208785
|
for (const [index, { todo, key }] of visible.entries()) {
|
|
208152
|
-
const isLast = index === visible.length - 1 &&
|
|
208786
|
+
const isLast = index === visible.length - 1 && hiddenCount === 0;
|
|
208153
208787
|
const branch = theme.fg("dim", isLast ? "└─" : "├─");
|
|
208154
208788
|
lines.push(truncate3(`${branch} ${formatTodoLine(todo, theme, { showId: true })}`));
|
|
208155
208789
|
if (todo.status === "completed")
|
|
208156
208790
|
this.completedTaskIdsPendingHide.add(key);
|
|
208157
208791
|
}
|
|
208158
|
-
if (
|
|
208159
|
-
lines.push(truncate3(`${theme.fg("dim", "└─")} ${theme.fg("dim", `+${
|
|
208792
|
+
if (hiddenCount > 0) {
|
|
208793
|
+
lines.push(truncate3(`${theme.fg("dim", "└─")} ${theme.fg("dim", `+${hiddenCount} more`)}`));
|
|
208160
208794
|
}
|
|
208161
208795
|
return lines;
|
|
208162
208796
|
}
|
|
@@ -208212,17 +208846,10 @@ function registerTodoStateLifecycle(pi, deps) {
|
|
|
208212
208846
|
}
|
|
208213
208847
|
|
|
208214
208848
|
// src/tools/todowrite.ts
|
|
208215
|
-
var STATUS_VALUES = [
|
|
208216
|
-
"pending",
|
|
208217
|
-
"in_progress",
|
|
208218
|
-
"completed",
|
|
208219
|
-
"cancelled"
|
|
208220
|
-
];
|
|
208221
|
-
var PRIORITY_VALUES = ["high", "medium", "low"];
|
|
208222
208849
|
var TodoItem = exports_typebox.Object({
|
|
208223
208850
|
content: exports_typebox.String({ description: "Brief description of the task" }),
|
|
208224
|
-
status: exports_typebox.Union(
|
|
208225
|
-
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)))),
|
|
208226
208853
|
id: exports_typebox.Optional(exports_typebox.String({ description: "Optional stable id for the todo" }))
|
|
208227
208854
|
});
|
|
208228
208855
|
var TodowriteParams = exports_typebox.Object({
|
|
@@ -208247,8 +208874,7 @@ function createTodowriteTool() {
|
|
|
208247
208874
|
parameters: TodowriteParams,
|
|
208248
208875
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
208249
208876
|
const todos = params.todos ?? [];
|
|
208250
|
-
const
|
|
208251
|
-
const active = todos.length - completed;
|
|
208877
|
+
const active = todos.filter((todo) => !TITLE_DONE_STATUSES.has(todo.status)).length;
|
|
208252
208878
|
return {
|
|
208253
208879
|
content: [
|
|
208254
208880
|
{
|
|
@@ -208339,6 +208965,55 @@ function persistPiMessageEndModelMeta(args) {
|
|
|
208339
208965
|
updateSessionMeta(args.db, args.sessionId, { cacheTtl });
|
|
208340
208966
|
}
|
|
208341
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
|
+
}
|
|
208342
209017
|
function info(message, data) {
|
|
208343
209018
|
log(`${PREFIX} ${message}`, data);
|
|
208344
209019
|
}
|
|
@@ -208346,6 +209021,7 @@ function warn(message, data) {
|
|
|
208346
209021
|
log(`${PREFIX} WARN ${message}`, data);
|
|
208347
209022
|
}
|
|
208348
209023
|
var migratedConfigDirs = new Set;
|
|
209024
|
+
var loggedPiConfigDirs = new Set;
|
|
208349
209025
|
function ensureConfigLocationsMigrated(dir) {
|
|
208350
209026
|
if (migratedConfigDirs.has(dir))
|
|
208351
209027
|
return;
|
|
@@ -208355,6 +209031,28 @@ function ensureConfigLocationsMigrated(dir) {
|
|
|
208355
209031
|
info: (m) => info(m)
|
|
208356
209032
|
});
|
|
208357
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
|
+
};
|
|
208358
209056
|
function formatTokens(value) {
|
|
208359
209057
|
return value.toLocaleString();
|
|
208360
209058
|
}
|
|
@@ -208541,14 +209239,12 @@ async function src_default2(pi) {
|
|
|
208541
209239
|
const { config: config2, warnings, loadedFromPaths } = loadPiConfig({
|
|
208542
209240
|
cwd: projectDir
|
|
208543
209241
|
});
|
|
208544
|
-
|
|
208545
|
-
|
|
208546
|
-
|
|
208547
|
-
|
|
208548
|
-
|
|
208549
|
-
|
|
208550
|
-
warn(`config: ${w}`);
|
|
208551
|
-
}
|
|
209242
|
+
logPiConfigLoad({
|
|
209243
|
+
dir: projectDir,
|
|
209244
|
+
loadedFromPaths,
|
|
209245
|
+
warnings,
|
|
209246
|
+
dedupe: true
|
|
209247
|
+
});
|
|
208552
209248
|
setSqlitePragmaConfig({
|
|
208553
209249
|
cacheSizeMb: config2.sqlite.cache_size_mb,
|
|
208554
209250
|
mmapSizeMb: config2.sqlite.mmap_size_mb
|
|
@@ -208630,7 +209326,14 @@ async function src_default2(pi) {
|
|
|
208630
209326
|
if (cached2)
|
|
208631
209327
|
return cached2;
|
|
208632
209328
|
ensureConfigLocationsMigrated(dir);
|
|
208633
|
-
const
|
|
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;
|
|
208634
209337
|
const switchedIdentity = identityOverride ?? resolveProjectIdentityOrFallback(dir);
|
|
208635
209338
|
const built = buildProjectDeps(dir, switchedIdentity, switchedConfig);
|
|
208636
209339
|
projectDepsByDir.set(dir, built);
|
|
@@ -209001,18 +209704,14 @@ ${block}` : event.systemPrompt;
|
|
|
209001
209704
|
const todoArgs = event.args;
|
|
209002
209705
|
const todos = todoArgs?.todos;
|
|
209003
209706
|
const sessionMeta = Array.isArray(todos) ? getOrCreateSessionMeta(db, sessionId) : null;
|
|
209004
|
-
|
|
209005
|
-
|
|
209006
|
-
|
|
209007
|
-
|
|
209008
|
-
|
|
209009
|
-
|
|
209010
|
-
|
|
209011
|
-
|
|
209012
|
-
lastTodoState: normalizedTodos
|
|
209013
|
-
});
|
|
209014
|
-
}
|
|
209015
|
-
}
|
|
209707
|
+
capturePiTodowriteArgsIfCompatible({
|
|
209708
|
+
db,
|
|
209709
|
+
sessionId,
|
|
209710
|
+
todos,
|
|
209711
|
+
todowriteEnabled,
|
|
209712
|
+
todoOverlay,
|
|
209713
|
+
persist: Boolean(sessionMeta && !sessionMeta.isSubagent)
|
|
209714
|
+
});
|
|
209016
209715
|
if (Array.isArray(todos) && todos.length > 0 && todos.every((t) => t.status === "completed" || t.status === "cancelled")) {
|
|
209017
209716
|
if (sessionMeta && !sessionMeta.isSubagent) {
|
|
209018
209717
|
onNoteTrigger(db, sessionId, "todos_complete");
|
|
@@ -209115,36 +209814,14 @@ ${block}` : event.systemPrompt;
|
|
|
209115
209814
|
try {
|
|
209116
209815
|
const sessionMetaForTodo = getOrCreateSessionMeta(db, sessionId);
|
|
209117
209816
|
if (!sessionMetaForTodo.isSubagent) {
|
|
209118
|
-
|
|
209119
|
-
|
|
209120
|
-
|
|
209121
|
-
|
|
209122
|
-
|
|
209123
|
-
|
|
209124
|
-
|
|
209125
|
-
|
|
209126
|
-
if (typeof b.name !== "string")
|
|
209127
|
-
continue;
|
|
209128
|
-
if (b.name !== "todowrite") {
|
|
209129
|
-
continue;
|
|
209130
|
-
}
|
|
209131
|
-
const args = b.arguments;
|
|
209132
|
-
const todos = args?.todos;
|
|
209133
|
-
if (!Array.isArray(todos))
|
|
209134
|
-
continue;
|
|
209135
|
-
const normalized = normalizeTodoStateJson(todos);
|
|
209136
|
-
if (normalized === null)
|
|
209137
|
-
continue;
|
|
209138
|
-
if (todowriteEnabled) {
|
|
209139
|
-
setTodoSnapshot(sessionId, todos);
|
|
209140
|
-
todoOverlay?.update(sessionId);
|
|
209141
|
-
}
|
|
209142
|
-
updateSessionMeta(db, sessionId, {
|
|
209143
|
-
lastTodoState: normalized
|
|
209144
|
-
});
|
|
209145
|
-
break;
|
|
209146
|
-
}
|
|
209147
|
-
}
|
|
209817
|
+
capturePiTodowriteMessageIfCompatible({
|
|
209818
|
+
db,
|
|
209819
|
+
sessionId,
|
|
209820
|
+
message: event.message,
|
|
209821
|
+
todowriteEnabled,
|
|
209822
|
+
todoOverlay,
|
|
209823
|
+
persist: true
|
|
209824
|
+
});
|
|
209148
209825
|
}
|
|
209149
209826
|
} catch (err5) {
|
|
209150
209827
|
warn("message_end: synthetic todowrite capture failed:", err5);
|
|
@@ -209236,5 +209913,8 @@ export {
|
|
|
209236
209913
|
resolveDreamerFromConfig,
|
|
209237
209914
|
persistPiPressureFromMessageEnd,
|
|
209238
209915
|
persistPiMessageEndModelMeta,
|
|
209239
|
-
src_default2 as default
|
|
209916
|
+
src_default2 as default,
|
|
209917
|
+
capturePiTodowriteMessageIfCompatible,
|
|
209918
|
+
capturePiTodowriteArgsIfCompatible,
|
|
209919
|
+
__test
|
|
209240
209920
|
};
|