@cortexkit/pi-magic-context 0.44.1 → 0.44.3
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/historian-calibration-extension.js +17 -0
- package/dist/{index-mwpk0wn9.js → index-atb1ecv9.js} +14766 -14284
- package/dist/index-n8kg008j.js +245 -0
- package/dist/{index-nm3c0sn8.js → index-tckvezry.js} +191 -39
- package/dist/index.js +1752 -750
- package/dist/{read-session-chunk-ka7n4kah.js → read-session-chunk-z2weg25w.js} +1 -1
- package/dist/subagent-entry.js +18 -2
- package/package.json +8 -4
package/dist/index.js
CHANGED
|
@@ -38,8 +38,10 @@ import {
|
|
|
38
38
|
formatWindowDerivationLine,
|
|
39
39
|
isSaneLimit,
|
|
40
40
|
resolveOutputReserve,
|
|
41
|
+
getSdkWindowGeometry,
|
|
41
42
|
getSdkContextLimit,
|
|
42
43
|
getSdkOutputLimit,
|
|
44
|
+
getSdkInputLimit,
|
|
43
45
|
cortexKitUserConfigBasePath,
|
|
44
46
|
cortexKitProjectConfigBasePath,
|
|
45
47
|
migrateMagicContextConfigLocations,
|
|
@@ -49,6 +51,7 @@ import {
|
|
|
49
51
|
nextDueAtMs,
|
|
50
52
|
resolveModelConfigValue,
|
|
51
53
|
DEFAULT_HISTORIAN_TIMEOUT_MS,
|
|
54
|
+
DREAM_TASK_PROMOTION_DEFAULTS,
|
|
52
55
|
LIVE_RELOAD_CONFIG_PATHS,
|
|
53
56
|
getProtectedTokensTierOverrides,
|
|
54
57
|
FAIL_CLOSED_DOCTOR_COMMAND,
|
|
@@ -57,8 +60,10 @@ import {
|
|
|
57
60
|
isFailClosedBlockingError,
|
|
58
61
|
shouldBypassFailClosedBlock,
|
|
59
62
|
createFailClosedController,
|
|
63
|
+
setHomeProjectPermission,
|
|
60
64
|
ProjectIdentityError,
|
|
61
65
|
resolveProjectIdentityStrict,
|
|
66
|
+
isUserHomeDirectory,
|
|
62
67
|
resolveProjectIdentity,
|
|
63
68
|
isUsableProjectIdentity,
|
|
64
69
|
resolveProjectIdentityForSession,
|
|
@@ -66,6 +71,17 @@ import {
|
|
|
66
71
|
scheduleAfterBootQuiet,
|
|
67
72
|
clearCompressionDepth,
|
|
68
73
|
clearCompressionDepthRange,
|
|
74
|
+
formatSynapseLaneDescriptor,
|
|
75
|
+
buildCanonicalChunkTextFromFts,
|
|
76
|
+
buildCompartmentSummaryFallbackText,
|
|
77
|
+
canonicalizeInMemoryChunkTextForEmbedding,
|
|
78
|
+
chunkCanonicalText,
|
|
79
|
+
chunkEmbeddingWindowsAreCurrent,
|
|
80
|
+
replaceCompartmentChunkEmbeddings,
|
|
81
|
+
deleteChunkEmbedBackoffForSession,
|
|
82
|
+
readSessionActivity,
|
|
83
|
+
observeSessionActivity,
|
|
84
|
+
backfillSessionActivity,
|
|
69
85
|
getMessageIndexSourceIdentity,
|
|
70
86
|
isMessageIndexSourceCurrent,
|
|
71
87
|
getLastIndexedOrdinal,
|
|
@@ -168,6 +184,12 @@ import {
|
|
|
168
184
|
retryPendingSessionCleanups,
|
|
169
185
|
retryPendingRustSessionCleanupsForProject,
|
|
170
186
|
recordSessionProjectIdentity,
|
|
187
|
+
embedPauseBySession,
|
|
188
|
+
embedRunStateBySession,
|
|
189
|
+
autoEmbedAttemptedBySession,
|
|
190
|
+
autoEmbedIdentityBySession,
|
|
191
|
+
invalidateAutoEmbedSession,
|
|
192
|
+
getEmbedDrainUiStatus,
|
|
171
193
|
piHarnessKindFromExecutable,
|
|
172
194
|
COMPARTMENT_LEASE_RENEWAL_MS,
|
|
173
195
|
acquireCompartmentLease,
|
|
@@ -231,6 +253,7 @@ import {
|
|
|
231
253
|
runSqliteOptimize,
|
|
232
254
|
openDatabase,
|
|
233
255
|
openDatabaseAsync,
|
|
256
|
+
closeDatabase,
|
|
234
257
|
queueM0Mutation,
|
|
235
258
|
getMaxM0MutationId,
|
|
236
259
|
queueMemoryMutation,
|
|
@@ -325,13 +348,6 @@ import {
|
|
|
325
348
|
peekCurateCategoryScope,
|
|
326
349
|
beginCurateCategoryRun,
|
|
327
350
|
curateTaskStateAfterSuccess,
|
|
328
|
-
formatSynapseLaneDescriptor,
|
|
329
|
-
buildCanonicalChunkTextFromFts,
|
|
330
|
-
buildCompartmentSummaryFallbackText,
|
|
331
|
-
canonicalizeInMemoryChunkTextForEmbedding,
|
|
332
|
-
chunkCanonicalText,
|
|
333
|
-
chunkEmbeddingWindowsAreCurrent,
|
|
334
|
-
replaceCompartmentChunkEmbeddings,
|
|
335
351
|
cosineSimilarity,
|
|
336
352
|
GIT_SWEEP_LEASE_RENEWAL_MS,
|
|
337
353
|
acquireGitSweepLease,
|
|
@@ -353,6 +369,7 @@ import {
|
|
|
353
369
|
drainCommitBacklogForProject,
|
|
354
370
|
embedSessionCompartmentChunks,
|
|
355
371
|
getEmbeddingCoverageStatus,
|
|
372
|
+
getAutoEmbeddingSessionCoverage,
|
|
356
373
|
promoteSessionFactsDurable,
|
|
357
374
|
embedPromotedFacts,
|
|
358
375
|
recordMemoryMapping,
|
|
@@ -373,6 +390,7 @@ import {
|
|
|
373
390
|
embedUnembeddedCommits,
|
|
374
391
|
loadPiConfig,
|
|
375
392
|
loadPiConfigDetailed,
|
|
393
|
+
unregisterPiProjectEmbeddings,
|
|
376
394
|
ensureProjectRegisteredFromPiDirectory,
|
|
377
395
|
resolvePiHarnessDetection,
|
|
378
396
|
resolvePiHarnessKind,
|
|
@@ -408,8 +426,10 @@ import {
|
|
|
408
426
|
registerTodoStateLifecycle,
|
|
409
427
|
syncCtxMemoryToolEnabled,
|
|
410
428
|
registerMagicContextTools
|
|
411
|
-
} from "./index-
|
|
429
|
+
} from "./index-atb1ecv9.js";
|
|
412
430
|
import {
|
|
431
|
+
Database,
|
|
432
|
+
withSqliteBackgroundWriter,
|
|
413
433
|
CTX_REDUCE_KEEP,
|
|
414
434
|
newestCtxReduceTagNumbers,
|
|
415
435
|
textMentionsRecentCommit,
|
|
@@ -485,7 +505,6 @@ import {
|
|
|
485
505
|
recordOpenCodeDbReadFailure,
|
|
486
506
|
clearOpenCodeDbReadFailure,
|
|
487
507
|
claimOpenCodeDbDiagnosticOnce,
|
|
488
|
-
Database,
|
|
489
508
|
closeQuietly,
|
|
490
509
|
RAW_SUMMARY_TEXT_MAX_CHARS,
|
|
491
510
|
completedToolArcCrossesBoundary,
|
|
@@ -496,6 +515,12 @@ import {
|
|
|
496
515
|
buildTrueRawTokenIndex,
|
|
497
516
|
computeRawRangeFingerprint,
|
|
498
517
|
invalidateTrueRawTokenCache,
|
|
518
|
+
DreamTokenBudgetExceeded,
|
|
519
|
+
TOKEN_BUDGET_FINALIZE_MESSAGE,
|
|
520
|
+
TOKEN_BUDGET_TOOL_REFUSAL,
|
|
521
|
+
registerBudgetFinalizeChild,
|
|
522
|
+
releaseBudgetFinalizeChild,
|
|
523
|
+
createDreamTokenBudget,
|
|
499
524
|
droppedInputMarker,
|
|
500
525
|
createDroppedInputGuard,
|
|
501
526
|
isEditTool,
|
|
@@ -522,10 +547,14 @@ import {
|
|
|
522
547
|
getRawSessionTagKeysThrough2,
|
|
523
548
|
getLegacyProtectedTailStartOrdinal2,
|
|
524
549
|
readSessionChunk2
|
|
525
|
-
} from "./index-
|
|
550
|
+
} from "./index-tckvezry.js";
|
|
526
551
|
import {
|
|
527
552
|
pushNotification2
|
|
528
553
|
} from "./index-b3eqj1g6.js";
|
|
554
|
+
import {
|
|
555
|
+
subagentStepCap,
|
|
556
|
+
SubagentTelemetry
|
|
557
|
+
} from "./index-n8kg008j.js";
|
|
529
558
|
import"./index-5fw91enm.js";
|
|
530
559
|
import {
|
|
531
560
|
logSlowWriteTransaction
|
|
@@ -822,6 +851,15 @@ var CANONICAL_DREAM_TASKS = [
|
|
|
822
851
|
"promote-primers",
|
|
823
852
|
"refresh-primers"
|
|
824
853
|
];
|
|
854
|
+
var DREAM_TOOL_LOOP_TOKEN_BUDGETS = {
|
|
855
|
+
"map-memories": 1500000,
|
|
856
|
+
verify: 1700000,
|
|
857
|
+
"verify-broad": 1700000,
|
|
858
|
+
curate: 1500000,
|
|
859
|
+
retrospective: 300000,
|
|
860
|
+
"maintain-docs": 1600000,
|
|
861
|
+
"refresh-primers": 350000
|
|
862
|
+
};
|
|
825
863
|
var DREAM_TASK_CAPABILITIES = {
|
|
826
864
|
"map-memories": {
|
|
827
865
|
requiresTools: true,
|
|
@@ -934,6 +972,7 @@ function buildDreamTaskRuntimeConfigs(dreamer, harness, language, muralModel) {
|
|
|
934
972
|
thinkingLevel: harness === "pi" ? resolved.primary?.qualifier : undefined,
|
|
935
973
|
language,
|
|
936
974
|
timeoutMinutes: resolved.timeoutMinutes ?? 20,
|
|
975
|
+
tokenBudget: task in DREAM_TOOL_LOOP_TOKEN_BUDGETS ? dreamer?.tasks?.[task]?.token_budget ?? DREAM_TOOL_LOOP_TOKEN_BUDGETS[task] : undefined,
|
|
937
976
|
promotionThreshold: resolved.promotionThreshold,
|
|
938
977
|
docsMaxTokens: task === "maintain-docs" ? dreamer?.tasks?.["maintain-docs"]?.max_tokens ?? 12000 : undefined,
|
|
939
978
|
retrospectiveRecencyDays: task === "retrospective" ? dreamer?.tasks?.retrospective?.recency_days ?? 30 : undefined
|
|
@@ -1036,7 +1075,7 @@ async function reconcileSessionIndex(db, sessionId, readMessages) {
|
|
|
1036
1075
|
const messages = readMessages.readPage ? readMessages.readPage(sessionId, cursor, RECONCILIATION_BATCH_SIZE, finalWatermark) : (fallbackSnapshot ?? []).filter((message) => message.ordinal > cursor && message.ordinal <= pageEnd);
|
|
1037
1076
|
const retainedMessages = fallbackSnapshot ?? messages;
|
|
1038
1077
|
heapHolder.activeReconcilerBuffers.set(sessionId, retainedMessages);
|
|
1039
|
-
indexMessagesAfterOrdinal(db, sessionId, messages, cursor, pageEnd);
|
|
1078
|
+
withSqliteBackgroundWriter(() => indexMessagesAfterOrdinal(db, sessionId, messages, cursor, pageEnd));
|
|
1040
1079
|
const nextCursor = getMessageIndexReconciliationStartOrdinal(db, sessionId);
|
|
1041
1080
|
if (nextCursor <= cursor)
|
|
1042
1081
|
break;
|
|
@@ -1062,6 +1101,8 @@ function scheduleReconciliation(db, sessionId, readMessages) {
|
|
|
1062
1101
|
defer(() => {
|
|
1063
1102
|
reconcileSessionIndex(db, sessionId, readMessages).catch((error) => {
|
|
1064
1103
|
logIndexingError(sessionId, "reconciliation", error);
|
|
1104
|
+
if (isDatabaseLockedError(error))
|
|
1105
|
+
setTimeout(() => scheduleReconciliation(db, sessionId, readMessages), 1000).unref();
|
|
1065
1106
|
}).finally(() => {
|
|
1066
1107
|
heapHolder.reconciliationScheduledSessions.delete(sessionId);
|
|
1067
1108
|
});
|
|
@@ -1091,7 +1132,7 @@ function scheduleIncrementalIndex(db, sessionId, messageId, messageSource) {
|
|
|
1091
1132
|
return;
|
|
1092
1133
|
}
|
|
1093
1134
|
const wasReconciled = heapHolder.reconciledSessions.delete(sessionId);
|
|
1094
|
-
indexSingleMessage(db, sessionId, message);
|
|
1135
|
+
withSqliteBackgroundWriter(() => indexSingleMessage(db, sessionId, message));
|
|
1095
1136
|
const finalWatermark = getLastIndexedOrdinal(db, sessionId);
|
|
1096
1137
|
if (wasReconciled && isMessageIndexReconciledThrough(db, sessionId, finalWatermark)) {
|
|
1097
1138
|
heapHolder.reconciledSessions.add(sessionId);
|
|
@@ -1100,6 +1141,8 @@ function scheduleIncrementalIndex(db, sessionId, messageId, messageSource) {
|
|
|
1100
1141
|
}).catch((error) => {
|
|
1101
1142
|
heapHolder.reconciledSessions.delete(sessionId);
|
|
1102
1143
|
logIndexingError(sessionId, `incremental index for ${messageId}`, error);
|
|
1144
|
+
if (isDatabaseLockedError(error))
|
|
1145
|
+
setTimeout(() => scheduleIncrementalIndex(db, sessionId, messageId, messageSource), 1000).unref();
|
|
1103
1146
|
}).finally(() => {
|
|
1104
1147
|
heapHolder.pendingIncrementalKeys.delete(schedulingKey);
|
|
1105
1148
|
});
|
|
@@ -1527,7 +1570,7 @@ async function runSessionProjectBackfill(db, source, options = {}) {
|
|
|
1527
1570
|
const startedAt = performance.now();
|
|
1528
1571
|
const harness = getHarness();
|
|
1529
1572
|
const now = options.now ?? Date.now;
|
|
1530
|
-
const resolveIdentity = options.resolveIdentity ??
|
|
1573
|
+
const resolveIdentity = options.resolveIdentity ?? ((directory) => resolveProjectIdentityForSession(directory, options.allowHomeProject));
|
|
1531
1574
|
const yieldFn = options.yieldFn ?? defaultYieldFn;
|
|
1532
1575
|
const holderId = options.holderId ?? randomUUID();
|
|
1533
1576
|
const readPage = createPageReader(source);
|
|
@@ -1541,7 +1584,8 @@ async function runSessionProjectBackfill(db, source, options = {}) {
|
|
|
1541
1584
|
skippedEmptyDirectories: 0,
|
|
1542
1585
|
durationMs: 0
|
|
1543
1586
|
};
|
|
1544
|
-
const
|
|
1587
|
+
const leaseKey = options.leaseKey ?? harness;
|
|
1588
|
+
const leaseStatus = acquireBackfillLease(db, leaseKey, holderId, now());
|
|
1545
1589
|
if (leaseStatus !== "acquired") {
|
|
1546
1590
|
result.status = leaseStatus;
|
|
1547
1591
|
result.durationMs = performance.now() - startedAt;
|
|
@@ -1553,10 +1597,10 @@ async function runSessionProjectBackfill(db, source, options = {}) {
|
|
|
1553
1597
|
return result;
|
|
1554
1598
|
};
|
|
1555
1599
|
const yieldAndRenew = async () => {
|
|
1556
|
-
if (!renewBackfillLease(db,
|
|
1600
|
+
if (!renewBackfillLease(db, leaseKey, holderId, now()))
|
|
1557
1601
|
return false;
|
|
1558
1602
|
await yieldFn();
|
|
1559
|
-
return renewBackfillLease(db,
|
|
1603
|
+
return renewBackfillLease(db, leaseKey, holderId, now());
|
|
1560
1604
|
};
|
|
1561
1605
|
const existenceCache = new Map;
|
|
1562
1606
|
const identityCache = new Map;
|
|
@@ -1570,7 +1614,7 @@ async function runSessionProjectBackfill(db, source, options = {}) {
|
|
|
1570
1614
|
sourcePage = await readPage(afterSessionId, SESSION_PAGE_SIZE);
|
|
1571
1615
|
} catch (error) {
|
|
1572
1616
|
try {
|
|
1573
|
-
if (!markBackfillRetryPending(db,
|
|
1617
|
+
if (!markBackfillRetryPending(db, leaseKey, holderId, now())) {
|
|
1574
1618
|
log("[session-projects] backfill lease changed before failure cleanup");
|
|
1575
1619
|
}
|
|
1576
1620
|
} catch (releaseError) {
|
|
@@ -1595,7 +1639,7 @@ async function runSessionProjectBackfill(db, source, options = {}) {
|
|
|
1595
1639
|
result.alreadyMappedSessions += 1;
|
|
1596
1640
|
} else {
|
|
1597
1641
|
result.unmappedSessions += 1;
|
|
1598
|
-
if (!session.directory) {
|
|
1642
|
+
if (!session.directory || !options.allowHomeProject && isUserHomeDirectory(session.directory)) {
|
|
1599
1643
|
result.skippedEmptyDirectories += 1;
|
|
1600
1644
|
} else {
|
|
1601
1645
|
const stillExists = existenceCache.get(session.directory) ?? existsSync(session.directory);
|
|
@@ -1613,13 +1657,18 @@ async function runSessionProjectBackfill(db, source, options = {}) {
|
|
|
1613
1657
|
}
|
|
1614
1658
|
identity = await resolveIdentity(session.directory);
|
|
1615
1659
|
identityResolutionsSinceYield += 1;
|
|
1616
|
-
if (!renewBackfillLease(db,
|
|
1660
|
+
if (!renewBackfillLease(db, leaseKey, holderId, now())) {
|
|
1617
1661
|
return finishWithLostLease();
|
|
1618
1662
|
}
|
|
1619
|
-
|
|
1663
|
+
if (identity)
|
|
1664
|
+
identityCache.set(session.directory, identity);
|
|
1665
|
+
}
|
|
1666
|
+
if (identity) {
|
|
1667
|
+
recordSessionProjectIdentity(db, session.sessionId, identity);
|
|
1668
|
+
result.backfilledSessions += 1;
|
|
1669
|
+
} else {
|
|
1670
|
+
result.skippedEmptyDirectories += 1;
|
|
1620
1671
|
}
|
|
1621
|
-
recordSessionProjectIdentity(db, session.sessionId, identity);
|
|
1622
|
-
result.backfilledSessions += 1;
|
|
1623
1672
|
}
|
|
1624
1673
|
}
|
|
1625
1674
|
}
|
|
@@ -1635,11 +1684,11 @@ async function runSessionProjectBackfill(db, source, options = {}) {
|
|
|
1635
1684
|
}
|
|
1636
1685
|
const hasSkippedSessions = result.skippedDeadDirectories > 0 || result.skippedEmptyDirectories > 0;
|
|
1637
1686
|
if (hasSkippedSessions) {
|
|
1638
|
-
if (!markBackfillRetryPending(db,
|
|
1687
|
+
if (!markBackfillRetryPending(db, leaseKey, holderId, now())) {
|
|
1639
1688
|
return finishWithLostLease();
|
|
1640
1689
|
}
|
|
1641
1690
|
result.status = "retry_pending";
|
|
1642
|
-
} else if (!markBackfillCompleted(db,
|
|
1691
|
+
} else if (!markBackfillCompleted(db, leaseKey, holderId, now())) {
|
|
1643
1692
|
return finishWithLostLease();
|
|
1644
1693
|
}
|
|
1645
1694
|
result.durationMs = performance.now() - startedAt;
|
|
@@ -1709,7 +1758,7 @@ function countFailures(db) {
|
|
|
1709
1758
|
function classifyBackfillError(error) {
|
|
1710
1759
|
if (error instanceof ProjectIdentityError) {
|
|
1711
1760
|
return {
|
|
1712
|
-
errorClass: error.errorClass === "dubious_ownership" ? "unknown" : error.errorClass,
|
|
1761
|
+
errorClass: error.errorClass === "dubious_ownership" || error.errorClass === "home_project_disabled" || error.errorClass === "git_identity_unavailable" ? "unknown" : error.errorClass,
|
|
1713
1762
|
errorMessage: error.message
|
|
1714
1763
|
};
|
|
1715
1764
|
}
|
|
@@ -1948,6 +1997,21 @@ function resolveKnownHistorianContextLimit(historianModelOverride) {
|
|
|
1948
1997
|
const limit = getSdkContextLimit(providerID, modelID, undefined, { reservation: "none" });
|
|
1949
1998
|
return typeof limit === "number" && limit > 0 ? limit : undefined;
|
|
1950
1999
|
}
|
|
2000
|
+
function resolveHistorianProducerLimits(modelKey) {
|
|
2001
|
+
if (!modelKey?.includes("/"))
|
|
2002
|
+
return {};
|
|
2003
|
+
const [provider, ...parts] = modelKey.split("/");
|
|
2004
|
+
const model = parts.join("/");
|
|
2005
|
+
if (!provider || !model)
|
|
2006
|
+
return {};
|
|
2007
|
+
const input = getSdkInputLimit(provider, model);
|
|
2008
|
+
const window = getSdkWindowGeometry(provider, model)?.derivation.window;
|
|
2009
|
+
const context = window ?? (input === undefined ? resolveKnownHistorianContextLimit(modelKey) : undefined);
|
|
2010
|
+
return {
|
|
2011
|
+
...context !== undefined ? { context } : {},
|
|
2012
|
+
...input !== undefined ? { input } : {}
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
1951
2015
|
function resolveHistorianContextLimit(historianModelOverride) {
|
|
1952
2016
|
if (typeof historianModelOverride === "string" && historianModelOverride.includes("/")) {
|
|
1953
2017
|
return resolveKnownHistorianContextLimit(historianModelOverride) ?? DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
|
|
@@ -2313,10 +2377,12 @@ var SMART_NOTE_CHECK_LIVENESS_RECHECK_MS = 24 * 60 * 60 * 1000;
|
|
|
2313
2377
|
class SmartNoteNetworkError extends Error {
|
|
2314
2378
|
isSmartNoteNetworkError = true;
|
|
2315
2379
|
terminal;
|
|
2380
|
+
persistent;
|
|
2316
2381
|
constructor(message, options = {}) {
|
|
2317
2382
|
super(message);
|
|
2318
2383
|
this.name = "SmartNoteNetworkError";
|
|
2319
2384
|
this.terminal = options.terminal ?? false;
|
|
2385
|
+
this.persistent = options.persistent ?? false;
|
|
2320
2386
|
}
|
|
2321
2387
|
}
|
|
2322
2388
|
|
|
@@ -2438,6 +2504,7 @@ function storeCompiledSmartNoteCheck(db, args) {
|
|
|
2438
2504
|
check_failure_count = 0,
|
|
2439
2505
|
check_network_failure_count = 0,
|
|
2440
2506
|
check_quarantined_until = NULL,
|
|
2507
|
+
ready_reason = NULL,
|
|
2441
2508
|
check_next_due_at = ?,
|
|
2442
2509
|
check_compiled_at = ?,
|
|
2443
2510
|
check_false_since_at = COALESCE(check_false_since_at, ?),
|
|
@@ -2486,15 +2553,17 @@ function markSmartNoteLivenessChecked(db, noteId, now) {
|
|
|
2486
2553
|
function markSmartNoteCheckStatus(db, noteId, status, now) {
|
|
2487
2554
|
db.prepare(`UPDATE notes SET check_status = ?, updated_at = ? WHERE id = ? AND type = 'smart'`).run(status, now, noteId);
|
|
2488
2555
|
}
|
|
2489
|
-
function markSmartNoteCompilationFailure(db, noteId, now, maxFailures) {
|
|
2556
|
+
function markSmartNoteCompilationFailure(db, noteId, now, maxFailures, error, persistent) {
|
|
2490
2557
|
const failureCount = readFailureCount(db, noteId, "check_failure_count") + 1;
|
|
2491
|
-
const status = failureCount >= maxFailures ? "fallback" : "uncompiled";
|
|
2558
|
+
const status = persistent ? "uncompiled" : failureCount >= maxFailures ? "fallback" : "uncompiled";
|
|
2559
|
+
const nextDueAt = now + (persistent ? 7 * 24 * 60 * 60 * 1000 : backoffMs(failureCount));
|
|
2492
2560
|
db.prepare(`UPDATE notes
|
|
2493
2561
|
SET check_failure_count = ?,
|
|
2494
2562
|
check_status = ?,
|
|
2495
2563
|
check_next_due_at = ?,
|
|
2564
|
+
ready_reason = ?,
|
|
2496
2565
|
updated_at = ?
|
|
2497
|
-
WHERE id = ? AND type = 'smart'`).run(failureCount, status,
|
|
2566
|
+
WHERE id = ? AND type = 'smart'`).run(failureCount, status, nextDueAt, persistent ? `Condition can't be checked: ${error}; rewrite it` : null, now, noteId);
|
|
2498
2567
|
}
|
|
2499
2568
|
function readFailureCount(db, noteId, column) {
|
|
2500
2569
|
if (column !== "check_failure_count" && column !== "check_network_failure_count")
|
|
@@ -2542,7 +2611,10 @@ function countCompartmentsSince(db, projectPath, since) {
|
|
|
2542
2611
|
return row?.cnt ?? 0;
|
|
2543
2612
|
}
|
|
2544
2613
|
function countProjectSessionsSince(db, projectPath, since) {
|
|
2545
|
-
const row = since === null ? db.prepare("SELECT COUNT(*) AS cnt FROM session_projects WHERE project_path = ?").get(projectPath) : db.prepare(
|
|
2614
|
+
const row = since === null ? db.prepare("SELECT COUNT(*) AS cnt FROM session_projects WHERE project_path = ?").get(projectPath) : db.prepare(`SELECT COUNT(*) AS cnt FROM session_projects sp
|
|
2615
|
+
JOIN schema_migrations_meta activity
|
|
2616
|
+
ON activity.key = 'retrospective_activity:' || sp.session_id
|
|
2617
|
+
WHERE sp.project_path = ? AND CAST(activity.value AS INTEGER) > ?`).get(projectPath, since);
|
|
2546
2618
|
return row?.cnt ?? 0;
|
|
2547
2619
|
}
|
|
2548
2620
|
function countMappedMemories(db, projectPath) {
|
|
@@ -2651,12 +2723,14 @@ function getDreamTaskBacklog(db, projectPath, task, options = {}) {
|
|
|
2651
2723
|
};
|
|
2652
2724
|
}
|
|
2653
2725
|
case "retrospective": {
|
|
2654
|
-
const
|
|
2726
|
+
const watermark = options.retrospectiveWatermarkMs !== undefined ? options.retrospectiveWatermarkMs : getTaskScheduleState(db, projectPath, task)?.retrospectiveWatermarkMs;
|
|
2727
|
+
const pending = countProjectSessionsSince(db, projectPath, watermark ?? null);
|
|
2655
2728
|
return { pending, total: pending };
|
|
2656
2729
|
}
|
|
2657
2730
|
case "maintain-docs": {
|
|
2658
2731
|
const total = countCompartmentsSince(db, projectPath, 0);
|
|
2659
|
-
const
|
|
2732
|
+
const lastRunAt = options.lastRunAt !== undefined ? options.lastRunAt : getTaskScheduleState(db, projectPath, task)?.lastRunAt;
|
|
2733
|
+
const pending = countCompartmentsSince(db, projectPath, lastRunAt ?? 0);
|
|
2660
2734
|
return { pending, total };
|
|
2661
2735
|
}
|
|
2662
2736
|
case "evaluate-smart-notes": {
|
|
@@ -2711,7 +2785,7 @@ function evaluateTaskGate(task, ctx) {
|
|
|
2711
2785
|
case "review-user-memories":
|
|
2712
2786
|
return getUserMemoryCandidates(db).length >= ctx.promotionThreshold;
|
|
2713
2787
|
case "promote-primers":
|
|
2714
|
-
return countPrimerCandidatesForProject(db, project) >= (ctx.promotionThreshold ??
|
|
2788
|
+
return countPrimerCandidatesForProject(db, project) >= (ctx.promotionThreshold ?? DREAM_TASK_PROMOTION_DEFAULTS["promote-primers"]);
|
|
2715
2789
|
case "refresh-primers":
|
|
2716
2790
|
return getActivePrimers(db, project).some((primer) => !primer.answer.trim() || primer.answerRefreshedAt == null || (primer.lastObservedAt ?? 0) > primer.answerRefreshedAt);
|
|
2717
2791
|
default: {
|
|
@@ -2758,6 +2832,11 @@ var USER_FACING_FAILURES = {
|
|
|
2758
2832
|
sentence: "Memory maintenance stopped at its hidden agent step limit.",
|
|
2759
2833
|
action: "This task needs less work per run; changing the model connection will not help."
|
|
2760
2834
|
},
|
|
2835
|
+
dream_token_budget: {
|
|
2836
|
+
code: "MC-D11",
|
|
2837
|
+
sentence: "Memory maintenance reached its prompt-token budget.",
|
|
2838
|
+
action: "The unfinished items will be retried on the next run."
|
|
2839
|
+
},
|
|
2761
2840
|
dream_empty_completion: {
|
|
2762
2841
|
code: "MC-D03",
|
|
2763
2842
|
sentence: "Memory maintenance received no usable response.",
|
|
@@ -2975,6 +3054,7 @@ var DREAM_FAILURE_KEYS = {
|
|
|
2975
3054
|
provider_timeout: "dream_provider_timeout",
|
|
2976
3055
|
provider_error: "dream_provider_error",
|
|
2977
3056
|
step_limit: "dream_step_limit",
|
|
3057
|
+
token_budget: "dream_token_budget",
|
|
2978
3058
|
empty_completion: "dream_empty_completion",
|
|
2979
3059
|
no_models: "dream_no_models",
|
|
2980
3060
|
child_aborted: "dream_child_aborted",
|
|
@@ -3042,7 +3122,7 @@ function openOpenCodeDb() {
|
|
|
3042
3122
|
|
|
3043
3123
|
// ../plugin/src/features/magic-context/dreamer/task-executor.ts
|
|
3044
3124
|
import { createHash as createHash10 } from "node:crypto";
|
|
3045
|
-
import { existsSync as existsSync6 } from "node:fs";
|
|
3125
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
|
|
3046
3126
|
|
|
3047
3127
|
// ../plugin/src/agents/dreamer.ts
|
|
3048
3128
|
var DREAMER_AGENT = "dreamer";
|
|
@@ -3175,7 +3255,7 @@ var READ_TOOLS = ["read", "grep", "glob"];
|
|
|
3175
3255
|
var AGENT_TOOLS = {
|
|
3176
3256
|
[HIDDEN_HISTORIAN_AGENT]: [],
|
|
3177
3257
|
[HIDDEN_DREAMER_AGENT]: [],
|
|
3178
|
-
[HIDDEN_CURATE_AGENT]: ["ctx_memory"
|
|
3258
|
+
[HIDDEN_CURATE_AGENT]: ["ctx_memory"],
|
|
3179
3259
|
[DREAMER_MEMORY_MAPPER_AGENT]: READ_TOOLS,
|
|
3180
3260
|
[DREAMER_PRIMER_INVESTIGATOR_AGENT]: [...READ_TOOLS, "ctx_search"],
|
|
3181
3261
|
[DREAMER_RETROSPECTIVE_AGENT]: ["ctx_search"]
|
|
@@ -3424,7 +3504,7 @@ async function abortChildRun(client, sessionId) {
|
|
|
3424
3504
|
}
|
|
3425
3505
|
}
|
|
3426
3506
|
function isNonRetryable(error, externalSignal) {
|
|
3427
|
-
if (externalSignal?.aborted || error instanceof HiddenAgentStepLimit)
|
|
3507
|
+
if (externalSignal?.aborted || error instanceof HiddenAgentStepLimit || error instanceof DreamTokenBudgetExceeded)
|
|
3428
3508
|
return true;
|
|
3429
3509
|
if (error instanceof Error) {
|
|
3430
3510
|
if (error.name === "AbortError")
|
|
@@ -3450,6 +3530,8 @@ function classifyPromptFailure(error, phase, externalSignal) {
|
|
|
3450
3530
|
const message = extractMessage(error);
|
|
3451
3531
|
if (error instanceof HiddenAgentStepLimit)
|
|
3452
3532
|
return "step_limit";
|
|
3533
|
+
if (error instanceof DreamTokenBudgetExceeded)
|
|
3534
|
+
return "token_budget";
|
|
3453
3535
|
if (externalSignal?.aborted || message === "prompt aborted by external signal") {
|
|
3454
3536
|
return "child_aborted";
|
|
3455
3537
|
}
|
|
@@ -3700,256 +3782,6 @@ function normalizeSDKResponse(response, fallback, options) {
|
|
|
3700
3782
|
}
|
|
3701
3783
|
return fallback;
|
|
3702
3784
|
}
|
|
3703
|
-
// ../plugin/src/shared/prompt-async-transport.ts
|
|
3704
|
-
var DEFAULT_POLL_INTERVAL_MS = 1000;
|
|
3705
|
-
var DEFAULT_START_GRACE_MS = 30000;
|
|
3706
|
-
var MESSAGE_WINDOW = 20;
|
|
3707
|
-
function sessionApi(client) {
|
|
3708
|
-
return client?.session;
|
|
3709
|
-
}
|
|
3710
|
-
function supportsPromptAsync(client) {
|
|
3711
|
-
const session = sessionApi(client);
|
|
3712
|
-
return typeof session?.promptAsync === "function" && typeof session?.status === "function" && typeof session?.messages === "function";
|
|
3713
|
-
}
|
|
3714
|
-
function createPromptAsyncTransport(client, childSessionId, options = {}) {
|
|
3715
|
-
if (!supportsPromptAsync(client))
|
|
3716
|
-
return;
|
|
3717
|
-
return Object.assign((request) => promptAsyncAndWaitForIdle(client, request, options), { childSessionId });
|
|
3718
|
-
}
|
|
3719
|
-
function infoOf(message) {
|
|
3720
|
-
const info = message?.info;
|
|
3721
|
-
return info && typeof info === "object" ? info : {};
|
|
3722
|
-
}
|
|
3723
|
-
function messageId(message) {
|
|
3724
|
-
const id = infoOf(message).id;
|
|
3725
|
-
return typeof id === "string" ? id : null;
|
|
3726
|
-
}
|
|
3727
|
-
function isSettledAssistant(message) {
|
|
3728
|
-
const info = infoOf(message);
|
|
3729
|
-
if (info.role !== "assistant")
|
|
3730
|
-
return false;
|
|
3731
|
-
return info.time?.completed != null || info.error != null || info.finish != null;
|
|
3732
|
-
}
|
|
3733
|
-
function unwrapData(response) {
|
|
3734
|
-
if (response && typeof response === "object" && "data" in response) {
|
|
3735
|
-
return response.data;
|
|
3736
|
-
}
|
|
3737
|
-
return response;
|
|
3738
|
-
}
|
|
3739
|
-
function describeRejection(error) {
|
|
3740
|
-
if (error instanceof Error)
|
|
3741
|
-
return error.message;
|
|
3742
|
-
try {
|
|
3743
|
-
return JSON.stringify(error);
|
|
3744
|
-
} catch {
|
|
3745
|
-
return String(error);
|
|
3746
|
-
}
|
|
3747
|
-
}
|
|
3748
|
-
function abortError(signal) {
|
|
3749
|
-
return signal.reason ?? new Error("prompt_async wait aborted");
|
|
3750
|
-
}
|
|
3751
|
-
function sleep(ms, signal) {
|
|
3752
|
-
return new Promise((resolve, reject) => {
|
|
3753
|
-
if (signal?.aborted) {
|
|
3754
|
-
reject(abortError(signal));
|
|
3755
|
-
return;
|
|
3756
|
-
}
|
|
3757
|
-
const onAbort = () => {
|
|
3758
|
-
clearTimeout(timer);
|
|
3759
|
-
reject(abortError(signal));
|
|
3760
|
-
};
|
|
3761
|
-
const timer = setTimeout(() => {
|
|
3762
|
-
signal?.removeEventListener("abort", onAbort);
|
|
3763
|
-
resolve();
|
|
3764
|
-
}, ms);
|
|
3765
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
3766
|
-
});
|
|
3767
|
-
}
|
|
3768
|
-
async function readMessages(session, sessionId, directory, signal) {
|
|
3769
|
-
const response = await session.messages?.({
|
|
3770
|
-
path: { id: sessionId },
|
|
3771
|
-
query: { ...directory ? { directory } : {}, limit: MESSAGE_WINDOW },
|
|
3772
|
-
...signal ? { signal } : {}
|
|
3773
|
-
});
|
|
3774
|
-
const data = unwrapData(response);
|
|
3775
|
-
return Array.isArray(data) ? data : [];
|
|
3776
|
-
}
|
|
3777
|
-
async function readStatus(session, sessionId, directory, signal) {
|
|
3778
|
-
try {
|
|
3779
|
-
const response = await session.status?.({
|
|
3780
|
-
...directory ? { query: { directory } } : {},
|
|
3781
|
-
...signal ? { signal } : {}
|
|
3782
|
-
});
|
|
3783
|
-
if (response && typeof response === "object" && "error" in response) {
|
|
3784
|
-
if (response.error)
|
|
3785
|
-
return null;
|
|
3786
|
-
}
|
|
3787
|
-
const data = unwrapData(response);
|
|
3788
|
-
if (!data || typeof data !== "object")
|
|
3789
|
-
return null;
|
|
3790
|
-
const entry = data[sessionId];
|
|
3791
|
-
const type = entry?.type;
|
|
3792
|
-
return typeof type === "string" ? type : "idle";
|
|
3793
|
-
} catch (error) {
|
|
3794
|
-
if (signal?.aborted)
|
|
3795
|
-
throw error;
|
|
3796
|
-
return null;
|
|
3797
|
-
}
|
|
3798
|
-
}
|
|
3799
|
-
async function promptAsyncAndWaitForIdle(client, request, options = {}) {
|
|
3800
|
-
const session = sessionApi(client);
|
|
3801
|
-
if (!session || !supportsPromptAsync(client)) {
|
|
3802
|
-
throw new Error("prompt_async transport is unavailable on this client");
|
|
3803
|
-
}
|
|
3804
|
-
const sessionId = request.path.id;
|
|
3805
|
-
const directory = request.query?.directory;
|
|
3806
|
-
const dir = typeof directory === "string" ? directory : undefined;
|
|
3807
|
-
const signal = request.signal;
|
|
3808
|
-
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
3809
|
-
const startGraceMs = options.startGraceMs ?? DEFAULT_START_GRACE_MS;
|
|
3810
|
-
const baseline = new Set((await readMessages(session, sessionId, dir, signal)).map(messageId).filter((id) => id !== null));
|
|
3811
|
-
const sent = await session.promptAsync?.(request);
|
|
3812
|
-
if (sent && typeof sent === "object" && "error" in sent) {
|
|
3813
|
-
const rejection = sent.error;
|
|
3814
|
-
if (rejection)
|
|
3815
|
-
throw new Error(`prompt_async was rejected: ${describeRejection(rejection)}`);
|
|
3816
|
-
}
|
|
3817
|
-
const sentAt = Date.now();
|
|
3818
|
-
let sawBusy = false;
|
|
3819
|
-
for (;; ) {
|
|
3820
|
-
await sleep(pollIntervalMs, signal);
|
|
3821
|
-
const status = await readStatus(session, sessionId, dir, signal);
|
|
3822
|
-
if (status === "busy" || status === "retry") {
|
|
3823
|
-
sawBusy = true;
|
|
3824
|
-
continue;
|
|
3825
|
-
}
|
|
3826
|
-
if (status === null)
|
|
3827
|
-
continue;
|
|
3828
|
-
const messages = await readMessages(session, sessionId, dir, signal);
|
|
3829
|
-
const last = messages.at(-1);
|
|
3830
|
-
const lastId = messageId(last);
|
|
3831
|
-
if (last && lastId !== null && !baseline.has(lastId) && isSettledAssistant(last))
|
|
3832
|
-
return;
|
|
3833
|
-
const fresh = messages.some((message) => {
|
|
3834
|
-
const id = messageId(message);
|
|
3835
|
-
return id !== null && !baseline.has(id);
|
|
3836
|
-
});
|
|
3837
|
-
if (sawBusy && fresh)
|
|
3838
|
-
return;
|
|
3839
|
-
if (Date.now() - sentAt >= startGraceMs) {
|
|
3840
|
-
if (fresh)
|
|
3841
|
-
return;
|
|
3842
|
-
throw new Error(`prompt_async did not start a run in child session ${sessionId} within ${startGraceMs}ms`);
|
|
3843
|
-
}
|
|
3844
|
-
}
|
|
3845
|
-
}
|
|
3846
|
-
// ../plugin/src/shared/summarize-child-stderr.ts
|
|
3847
|
-
var MAX_CHILD_STDERR_SUMMARY_LENGTH = 500;
|
|
3848
|
-
var CHILD_ERROR_LINE_PATTERN = /^(?:[A-Za-z]*Error|SqliteError|Error)\b[^\n]*/;
|
|
3849
|
-
var STACK_FRAME_PATTERN = /^\s*at\b/;
|
|
3850
|
-
function summarizeChildStderr(stderr) {
|
|
3851
|
-
const lines = stderr.split(/\r?\n/);
|
|
3852
|
-
const errorLineIndex = lines.findIndex((line) => CHILD_ERROR_LINE_PATTERN.test(line));
|
|
3853
|
-
if (errorLineIndex < 0) {
|
|
3854
|
-
return stderr.length > MAX_CHILD_STDERR_SUMMARY_LENGTH ? stderr.slice(-MAX_CHILD_STDERR_SUMMARY_LENGTH) : stderr;
|
|
3855
|
-
}
|
|
3856
|
-
const excerptLines = [lines[errorLineIndex]];
|
|
3857
|
-
let stackFrames = 0;
|
|
3858
|
-
for (let index = errorLineIndex + 1;index < lines.length && stackFrames < 2; index += 1) {
|
|
3859
|
-
if (!STACK_FRAME_PATTERN.test(lines[index]))
|
|
3860
|
-
continue;
|
|
3861
|
-
excerptLines.push(lines[index]);
|
|
3862
|
-
stackFrames += 1;
|
|
3863
|
-
}
|
|
3864
|
-
return excerptLines.join(`
|
|
3865
|
-
`).slice(0, MAX_CHILD_STDERR_SUMMARY_LENGTH);
|
|
3866
|
-
}
|
|
3867
|
-
// ../plugin/src/shared/child-session-teardown.ts
|
|
3868
|
-
async function teardownChildSession(args) {
|
|
3869
|
-
const { client, sessionId, sessionDirectory, promptSettled, context, log } = args;
|
|
3870
|
-
if (!sessionId)
|
|
3871
|
-
return;
|
|
3872
|
-
const request = {
|
|
3873
|
-
path: { id: sessionId },
|
|
3874
|
-
...sessionDirectory ? { query: { directory: sessionDirectory } } : {}
|
|
3875
|
-
};
|
|
3876
|
-
if (promptSettled && !shouldKeepSubagents()) {
|
|
3877
|
-
await client.session.delete(request).catch((error) => {
|
|
3878
|
-
log(`${context}: session cleanup failed: ${String(error)}`);
|
|
3879
|
-
});
|
|
3880
|
-
return;
|
|
3881
|
-
}
|
|
3882
|
-
if (!promptSettled) {
|
|
3883
|
-
const archiveSession = client.session;
|
|
3884
|
-
await archiveSession.update?.({
|
|
3885
|
-
...request,
|
|
3886
|
-
body: { time: { archived: Date.now() } }
|
|
3887
|
-
}).catch(() => {});
|
|
3888
|
-
log(`${context}: prompt unsettled — session ${sessionId} left to the age-gated sweep`);
|
|
3889
|
-
}
|
|
3890
|
-
}
|
|
3891
|
-
|
|
3892
|
-
// ../plugin/src/features/magic-context/compartment-events.ts
|
|
3893
|
-
function insertCompartmentEvents(db, sessionId, events, compartmentIds) {
|
|
3894
|
-
if (events.length === 0)
|
|
3895
|
-
return;
|
|
3896
|
-
const now = Date.now();
|
|
3897
|
-
const harness = getHarness();
|
|
3898
|
-
const stmt = db.prepare("INSERT INTO compartment_events (session_id, compartment_id, kind, at_compartment, fields_json, created_at, harness) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
|
3899
|
-
for (const ev of events) {
|
|
3900
|
-
const idx = ev.atCompartment != null && ev.atCompartment >= 1 ? ev.atCompartment - 1 : -1;
|
|
3901
|
-
const compartmentId = idx >= 0 && idx < compartmentIds.length ? compartmentIds[idx] : null;
|
|
3902
|
-
stmt.run(sessionId, compartmentId, ev.kind, ev.atCompartment, JSON.stringify(ev.fields ?? {}), now, harness);
|
|
3903
|
-
}
|
|
3904
|
-
}
|
|
3905
|
-
function getCompartmentEvents(db, sessionId) {
|
|
3906
|
-
const rows = db.prepare("SELECT id, session_id, compartment_id, kind, at_compartment, fields_json, created_at FROM compartment_events WHERE session_id = ? ORDER BY id DESC").all(sessionId);
|
|
3907
|
-
return rows.map((r) => ({
|
|
3908
|
-
id: r.id,
|
|
3909
|
-
sessionId: r.session_id,
|
|
3910
|
-
compartmentId: r.compartment_id,
|
|
3911
|
-
kind: r.kind,
|
|
3912
|
-
atCompartment: r.at_compartment,
|
|
3913
|
-
fields: parseFields(r.fields_json),
|
|
3914
|
-
createdAt: r.created_at
|
|
3915
|
-
}));
|
|
3916
|
-
}
|
|
3917
|
-
function parseFields(json) {
|
|
3918
|
-
try {
|
|
3919
|
-
const parsed = JSON.parse(json);
|
|
3920
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
3921
|
-
const out = {};
|
|
3922
|
-
for (const [k, v] of Object.entries(parsed)) {
|
|
3923
|
-
if (typeof v === "string")
|
|
3924
|
-
out[k] = v;
|
|
3925
|
-
}
|
|
3926
|
-
return out;
|
|
3927
|
-
}
|
|
3928
|
-
} catch {}
|
|
3929
|
-
return {};
|
|
3930
|
-
}
|
|
3931
|
-
|
|
3932
|
-
// ../plugin/src/features/magic-context/mural/compress-cues.ts
|
|
3933
|
-
import { createHash as createHash4 } from "node:crypto";
|
|
3934
|
-
|
|
3935
|
-
// ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
|
|
3936
|
-
import { mkdirSync as mkdirSync2, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
3937
|
-
import { join as join2 } from "node:path";
|
|
3938
|
-
|
|
3939
|
-
// ../plugin/src/agents/historian.ts
|
|
3940
|
-
var HISTORIAN_AGENT = "historian";
|
|
3941
|
-
var HISTORIAN_RECOMP_AGENT = "historian-recomp";
|
|
3942
|
-
var HISTORIAN_EDITOR_AGENT = "historian-editor";
|
|
3943
|
-
|
|
3944
|
-
// ../plugin/src/config/live-child-output-cap.ts
|
|
3945
|
-
var historianCaps = new BoundedSessionMap(4096);
|
|
3946
|
-
function rememberHistorianOutputCap(sessionId, cap) {
|
|
3947
|
-
historianCaps.set(sessionId, { value: cap });
|
|
3948
|
-
}
|
|
3949
|
-
function forgetHistorianOutputCap(sessionId) {
|
|
3950
|
-
historianCaps.delete(sessionId);
|
|
3951
|
-
}
|
|
3952
|
-
|
|
3953
3785
|
// ../plugin/src/features/magic-context/subagent-token-capture.ts
|
|
3954
3786
|
function asNumber(value) {
|
|
3955
3787
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
@@ -4055,6 +3887,348 @@ function recordChildInvocation(input) {
|
|
|
4055
3887
|
}
|
|
4056
3888
|
}
|
|
4057
3889
|
|
|
3890
|
+
// ../plugin/src/shared/prompt-async-transport.ts
|
|
3891
|
+
var DEFAULT_POLL_INTERVAL_MS = 1000;
|
|
3892
|
+
var DEFAULT_START_GRACE_MS = 30000;
|
|
3893
|
+
var MESSAGE_WINDOW = 100;
|
|
3894
|
+
function sessionApi(client) {
|
|
3895
|
+
return client?.session;
|
|
3896
|
+
}
|
|
3897
|
+
function supportsPromptAsync(client) {
|
|
3898
|
+
const session = sessionApi(client);
|
|
3899
|
+
return typeof session?.promptAsync === "function" && typeof session?.status === "function" && typeof session?.messages === "function";
|
|
3900
|
+
}
|
|
3901
|
+
function createPromptAsyncTransport(client, childSessionId, options = {}) {
|
|
3902
|
+
if (!supportsPromptAsync(client))
|
|
3903
|
+
return;
|
|
3904
|
+
const budgetGuard = options.tokenBudget ? createDreamTokenBudget(options.tokenBudget) : undefined;
|
|
3905
|
+
return Object.assign((request) => promptAsyncAndWaitForIdle(client, request, { ...options, budgetGuard }), { childSessionId });
|
|
3906
|
+
}
|
|
3907
|
+
function infoOf(message) {
|
|
3908
|
+
const info = message?.info;
|
|
3909
|
+
return info && typeof info === "object" ? info : {};
|
|
3910
|
+
}
|
|
3911
|
+
function messageId(message) {
|
|
3912
|
+
const id = infoOf(message).id;
|
|
3913
|
+
return typeof id === "string" ? id : null;
|
|
3914
|
+
}
|
|
3915
|
+
function isSettledAssistant(message) {
|
|
3916
|
+
const info = infoOf(message);
|
|
3917
|
+
if (info.role !== "assistant")
|
|
3918
|
+
return false;
|
|
3919
|
+
return info.time?.completed != null || info.error != null || info.finish != null;
|
|
3920
|
+
}
|
|
3921
|
+
function isTerminalAssistant(message) {
|
|
3922
|
+
const info = infoOf(message);
|
|
3923
|
+
return isSettledAssistant(message) && info.finish !== "tool-calls" && info.finish !== "tool_use" && !(message && typeof message === "object" && Array.isArray(message.parts) && message.parts.some((part) => part.type === "tool"));
|
|
3924
|
+
}
|
|
3925
|
+
function unwrapData(response) {
|
|
3926
|
+
if (response && typeof response === "object" && "data" in response) {
|
|
3927
|
+
return response.data;
|
|
3928
|
+
}
|
|
3929
|
+
return response;
|
|
3930
|
+
}
|
|
3931
|
+
function describeRejection(error) {
|
|
3932
|
+
if (error instanceof Error)
|
|
3933
|
+
return error.message;
|
|
3934
|
+
try {
|
|
3935
|
+
return JSON.stringify(error);
|
|
3936
|
+
} catch {
|
|
3937
|
+
return String(error);
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
function abortError(signal) {
|
|
3941
|
+
return signal.reason ?? new Error("prompt_async wait aborted");
|
|
3942
|
+
}
|
|
3943
|
+
function sleep(ms, signal) {
|
|
3944
|
+
return new Promise((resolve, reject) => {
|
|
3945
|
+
if (signal?.aborted) {
|
|
3946
|
+
reject(abortError(signal));
|
|
3947
|
+
return;
|
|
3948
|
+
}
|
|
3949
|
+
const onAbort = () => {
|
|
3950
|
+
clearTimeout(timer);
|
|
3951
|
+
reject(abortError(signal));
|
|
3952
|
+
};
|
|
3953
|
+
const timer = setTimeout(() => {
|
|
3954
|
+
signal?.removeEventListener("abort", onAbort);
|
|
3955
|
+
resolve();
|
|
3956
|
+
}, ms);
|
|
3957
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
3958
|
+
});
|
|
3959
|
+
}
|
|
3960
|
+
async function readMessages(session, sessionId, directory, signal) {
|
|
3961
|
+
const response = await session.messages?.({
|
|
3962
|
+
path: { id: sessionId },
|
|
3963
|
+
query: { ...directory ? { directory } : {}, limit: MESSAGE_WINDOW },
|
|
3964
|
+
...signal ? { signal } : {}
|
|
3965
|
+
});
|
|
3966
|
+
const data = unwrapData(response);
|
|
3967
|
+
return Array.isArray(data) ? data : [];
|
|
3968
|
+
}
|
|
3969
|
+
async function readStatus(session, sessionId, directory, signal) {
|
|
3970
|
+
try {
|
|
3971
|
+
const response = await session.status?.({
|
|
3972
|
+
...directory ? { query: { directory } } : {},
|
|
3973
|
+
...signal ? { signal } : {}
|
|
3974
|
+
});
|
|
3975
|
+
if (response && typeof response === "object" && "error" in response) {
|
|
3976
|
+
if (response.error)
|
|
3977
|
+
return null;
|
|
3978
|
+
}
|
|
3979
|
+
const data = unwrapData(response);
|
|
3980
|
+
if (!data || typeof data !== "object")
|
|
3981
|
+
return null;
|
|
3982
|
+
const entry = data[sessionId];
|
|
3983
|
+
const type = entry?.type;
|
|
3984
|
+
return typeof type === "string" ? type : "idle";
|
|
3985
|
+
} catch (error) {
|
|
3986
|
+
if (signal?.aborted)
|
|
3987
|
+
throw error;
|
|
3988
|
+
return null;
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
async function promptAsyncAndWaitForIdle(client, request, options = {}) {
|
|
3992
|
+
const session = sessionApi(client);
|
|
3993
|
+
if (!session || !supportsPromptAsync(client)) {
|
|
3994
|
+
throw new Error("prompt_async transport is unavailable on this client");
|
|
3995
|
+
}
|
|
3996
|
+
const sessionId = request.path.id;
|
|
3997
|
+
const directory = request.query?.directory;
|
|
3998
|
+
const dir = typeof directory === "string" ? directory : undefined;
|
|
3999
|
+
const signal = request.signal;
|
|
4000
|
+
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
4001
|
+
const startGraceMs = options.startGraceMs ?? DEFAULT_START_GRACE_MS;
|
|
4002
|
+
const budget = options.budgetGuard ?? (options.tokenBudget ? createDreamTokenBudget(options.tokenBudget) : undefined);
|
|
4003
|
+
if (budget?.snapshot().finalizeFired)
|
|
4004
|
+
throw new DreamTokenBudgetExceeded(sessionId, budget.snapshot().spent);
|
|
4005
|
+
const seenUsage = new Set;
|
|
4006
|
+
let finalizing = false;
|
|
4007
|
+
let completionBaseline = new Set((await readMessages(session, sessionId, dir, signal)).map(messageId).filter((id) => id !== null));
|
|
4008
|
+
const initialBaseline = new Set(completionBaseline);
|
|
4009
|
+
const sent = await session.promptAsync?.(request);
|
|
4010
|
+
if (sent && typeof sent === "object" && "error" in sent) {
|
|
4011
|
+
const rejection = sent.error;
|
|
4012
|
+
if (rejection)
|
|
4013
|
+
throw new Error(`prompt_async was rejected: ${describeRejection(rejection)}`);
|
|
4014
|
+
}
|
|
4015
|
+
let sentAt = Date.now();
|
|
4016
|
+
let sawBusy = false;
|
|
4017
|
+
try {
|
|
4018
|
+
for (;; ) {
|
|
4019
|
+
await sleep(pollIntervalMs, signal);
|
|
4020
|
+
const status = await readStatus(session, sessionId, dir, signal);
|
|
4021
|
+
const messages = await readMessages(session, sessionId, dir, signal);
|
|
4022
|
+
if (budget) {
|
|
4023
|
+
for (const message of messages) {
|
|
4024
|
+
const id = messageId(message);
|
|
4025
|
+
if (!id || seenUsage.has(id) || infoOf(message).role !== "assistant")
|
|
4026
|
+
continue;
|
|
4027
|
+
if (initialBaseline.has(id))
|
|
4028
|
+
continue;
|
|
4029
|
+
const tokens = sumTokensFromChildMessages([message]);
|
|
4030
|
+
if (tokens.input + tokens.cacheRead + tokens.cacheWrite === 0)
|
|
4031
|
+
continue;
|
|
4032
|
+
seenUsage.add(id);
|
|
4033
|
+
const decision = budget.charge(tokens.input, tokens.cacheRead, tokens.cacheWrite, isTerminalAssistant(message));
|
|
4034
|
+
options.onBudgetUpdate?.({ ...budget.snapshot(), sessionId });
|
|
4035
|
+
if (decision === "stop") {
|
|
4036
|
+
await session.abort?.({ path: { id: sessionId } });
|
|
4037
|
+
throw new DreamTokenBudgetExceeded(sessionId, budget.snapshot().spent);
|
|
4038
|
+
}
|
|
4039
|
+
if (decision === "finalize") {
|
|
4040
|
+
finalizing = true;
|
|
4041
|
+
registerBudgetFinalizeChild(sessionId, budget);
|
|
4042
|
+
if (!session.abort)
|
|
4043
|
+
throw new DreamTokenBudgetExceeded(sessionId, budget.snapshot().spent);
|
|
4044
|
+
await session.abort({ path: { id: sessionId } });
|
|
4045
|
+
const abortDeadline = Date.now() + startGraceMs;
|
|
4046
|
+
while (Date.now() < abortDeadline) {
|
|
4047
|
+
const state = await readStatus(session, sessionId, dir, signal);
|
|
4048
|
+
if (state === "idle")
|
|
4049
|
+
break;
|
|
4050
|
+
await sleep(pollIntervalMs, signal);
|
|
4051
|
+
}
|
|
4052
|
+
if (await readStatus(session, sessionId, dir, signal) !== "idle") {
|
|
4053
|
+
throw new DreamTokenBudgetExceeded(sessionId, budget.snapshot().spent);
|
|
4054
|
+
}
|
|
4055
|
+
completionBaseline = new Set((await readMessages(session, sessionId, dir, signal)).map(messageId).filter((value) => value !== null));
|
|
4056
|
+
const finalizeResponse = await session.promptAsync?.({
|
|
4057
|
+
...request,
|
|
4058
|
+
body: {
|
|
4059
|
+
...request.body,
|
|
4060
|
+
parts: [{ type: "text", text: TOKEN_BUDGET_FINALIZE_MESSAGE }]
|
|
4061
|
+
}
|
|
4062
|
+
});
|
|
4063
|
+
if (finalizeResponse && typeof finalizeResponse === "object" && "error" in finalizeResponse && finalizeResponse.error) {
|
|
4064
|
+
throw new DreamTokenBudgetExceeded(sessionId, budget.snapshot().spent);
|
|
4065
|
+
}
|
|
4066
|
+
sentAt = Date.now();
|
|
4067
|
+
sawBusy = false;
|
|
4068
|
+
break;
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
if (budget.snapshot().hardStopped) {
|
|
4072
|
+
await session.abort?.({ path: { id: sessionId } });
|
|
4073
|
+
throw new DreamTokenBudgetExceeded(sessionId, budget.snapshot().spent);
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
4076
|
+
if (status === "busy" || status === "retry") {
|
|
4077
|
+
sawBusy = true;
|
|
4078
|
+
continue;
|
|
4079
|
+
}
|
|
4080
|
+
if (status === null)
|
|
4081
|
+
continue;
|
|
4082
|
+
const current = finalizing ? await readMessages(session, sessionId, dir, signal) : messages;
|
|
4083
|
+
const last = current.at(-1);
|
|
4084
|
+
const lastId = messageId(last);
|
|
4085
|
+
if (last && lastId !== null && !completionBaseline.has(lastId) && isSettledAssistant(last))
|
|
4086
|
+
return;
|
|
4087
|
+
const fresh = current.some((message) => {
|
|
4088
|
+
const id = messageId(message);
|
|
4089
|
+
return id !== null && !completionBaseline.has(id);
|
|
4090
|
+
});
|
|
4091
|
+
if (sawBusy && fresh)
|
|
4092
|
+
return;
|
|
4093
|
+
if (Date.now() - sentAt >= startGraceMs) {
|
|
4094
|
+
if (fresh)
|
|
4095
|
+
return;
|
|
4096
|
+
throw new Error(`prompt_async did not start a run in child session ${sessionId} within ${startGraceMs}ms`);
|
|
4097
|
+
}
|
|
4098
|
+
}
|
|
4099
|
+
} finally {
|
|
4100
|
+
if (budget)
|
|
4101
|
+
releaseBudgetFinalizeChild(sessionId);
|
|
4102
|
+
}
|
|
4103
|
+
}
|
|
4104
|
+
// ../plugin/src/shared/summarize-child-stderr.ts
|
|
4105
|
+
var MAX_CHILD_STDERR_SUMMARY_LENGTH = 500;
|
|
4106
|
+
var CHILD_ERROR_LINE_PATTERN = /^(?:[A-Za-z]*Error|SqliteError|Error)\b[^\n]*/;
|
|
4107
|
+
var STACK_FRAME_PATTERN = /^\s*at\b/;
|
|
4108
|
+
function summarizeChildStderr(stderr) {
|
|
4109
|
+
const lines = stderr.split(/\r?\n/);
|
|
4110
|
+
const errorLineIndex = lines.findIndex((line) => CHILD_ERROR_LINE_PATTERN.test(line));
|
|
4111
|
+
if (errorLineIndex < 0) {
|
|
4112
|
+
return stderr.length > MAX_CHILD_STDERR_SUMMARY_LENGTH ? stderr.slice(-MAX_CHILD_STDERR_SUMMARY_LENGTH) : stderr;
|
|
4113
|
+
}
|
|
4114
|
+
const excerptLines = [lines[errorLineIndex]];
|
|
4115
|
+
let stackFrames = 0;
|
|
4116
|
+
for (let index = errorLineIndex + 1;index < lines.length && stackFrames < 2; index += 1) {
|
|
4117
|
+
if (!STACK_FRAME_PATTERN.test(lines[index]))
|
|
4118
|
+
continue;
|
|
4119
|
+
excerptLines.push(lines[index]);
|
|
4120
|
+
stackFrames += 1;
|
|
4121
|
+
}
|
|
4122
|
+
return excerptLines.join(`
|
|
4123
|
+
`).slice(0, MAX_CHILD_STDERR_SUMMARY_LENGTH);
|
|
4124
|
+
}
|
|
4125
|
+
// ../plugin/src/shared/child-session-teardown.ts
|
|
4126
|
+
async function teardownChildSession(args) {
|
|
4127
|
+
const { client, sessionId, sessionDirectory, promptSettled, context, log } = args;
|
|
4128
|
+
if (!sessionId)
|
|
4129
|
+
return;
|
|
4130
|
+
const request = {
|
|
4131
|
+
path: { id: sessionId },
|
|
4132
|
+
...sessionDirectory ? { query: { directory: sessionDirectory } } : {}
|
|
4133
|
+
};
|
|
4134
|
+
if (promptSettled && !shouldKeepSubagents()) {
|
|
4135
|
+
await client.session.delete(request).catch((error) => {
|
|
4136
|
+
log(`${context}: session cleanup failed: ${String(error)}`);
|
|
4137
|
+
});
|
|
4138
|
+
return;
|
|
4139
|
+
}
|
|
4140
|
+
if (!promptSettled) {
|
|
4141
|
+
const archiveSession = client.session;
|
|
4142
|
+
await archiveSession.update?.({
|
|
4143
|
+
...request,
|
|
4144
|
+
body: { time: { archived: Date.now() } }
|
|
4145
|
+
}).catch(() => {});
|
|
4146
|
+
log(`${context}: prompt unsettled — session ${sessionId} left to the age-gated sweep`);
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
|
|
4150
|
+
// ../plugin/src/features/magic-context/compartment-events.ts
|
|
4151
|
+
function insertCompartmentEvents(db, sessionId, events, compartmentIds) {
|
|
4152
|
+
if (events.length === 0)
|
|
4153
|
+
return;
|
|
4154
|
+
const now = Date.now();
|
|
4155
|
+
const harness = getHarness();
|
|
4156
|
+
const stmt = db.prepare("INSERT INTO compartment_events (session_id, compartment_id, kind, at_compartment, fields_json, created_at, harness) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
|
4157
|
+
for (const ev of events) {
|
|
4158
|
+
const idx = ev.atCompartment != null && ev.atCompartment >= 1 ? ev.atCompartment - 1 : -1;
|
|
4159
|
+
const compartmentId = idx >= 0 && idx < compartmentIds.length ? compartmentIds[idx] : null;
|
|
4160
|
+
stmt.run(sessionId, compartmentId, ev.kind, ev.atCompartment, JSON.stringify(ev.fields ?? {}), now, harness);
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
4163
|
+
function getCompartmentEvents(db, sessionId) {
|
|
4164
|
+
const rows = db.prepare("SELECT id, session_id, compartment_id, kind, at_compartment, fields_json, created_at FROM compartment_events WHERE session_id = ? ORDER BY id DESC").all(sessionId);
|
|
4165
|
+
return rows.map((r) => ({
|
|
4166
|
+
id: r.id,
|
|
4167
|
+
sessionId: r.session_id,
|
|
4168
|
+
compartmentId: r.compartment_id,
|
|
4169
|
+
kind: r.kind,
|
|
4170
|
+
atCompartment: r.at_compartment,
|
|
4171
|
+
fields: parseFields(r.fields_json),
|
|
4172
|
+
createdAt: r.created_at
|
|
4173
|
+
}));
|
|
4174
|
+
}
|
|
4175
|
+
function parseFields(json) {
|
|
4176
|
+
try {
|
|
4177
|
+
const parsed = JSON.parse(json);
|
|
4178
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
4179
|
+
const out = {};
|
|
4180
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
4181
|
+
if (typeof v === "string")
|
|
4182
|
+
out[k] = v;
|
|
4183
|
+
}
|
|
4184
|
+
return out;
|
|
4185
|
+
}
|
|
4186
|
+
} catch {}
|
|
4187
|
+
return {};
|
|
4188
|
+
}
|
|
4189
|
+
|
|
4190
|
+
// ../plugin/src/features/magic-context/mural/compress-cues.ts
|
|
4191
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
4192
|
+
|
|
4193
|
+
// ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
|
|
4194
|
+
import { mkdirSync as mkdirSync2, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
4195
|
+
import { join as join2 } from "node:path";
|
|
4196
|
+
|
|
4197
|
+
// ../plugin/src/agents/historian.ts
|
|
4198
|
+
var HISTORIAN_AGENT = "historian";
|
|
4199
|
+
var HISTORIAN_RECOMP_AGENT = "historian-recomp";
|
|
4200
|
+
var HISTORIAN_EDITOR_AGENT = "historian-editor";
|
|
4201
|
+
|
|
4202
|
+
// ../plugin/src/config/live-child-output-cap.ts
|
|
4203
|
+
var historianCaps = new BoundedSessionMap(4096);
|
|
4204
|
+
function rememberHistorianOutputCap(sessionId, cap) {
|
|
4205
|
+
historianCaps.set(sessionId, { value: cap });
|
|
4206
|
+
}
|
|
4207
|
+
function forgetHistorianOutputCap(sessionId) {
|
|
4208
|
+
historianCaps.delete(sessionId);
|
|
4209
|
+
}
|
|
4210
|
+
|
|
4211
|
+
// ../plugin/src/shared/run-token-log.ts
|
|
4212
|
+
function count(value) {
|
|
4213
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
4214
|
+
}
|
|
4215
|
+
function runTokenLog(tokens, maxTokens, finish) {
|
|
4216
|
+
const usage = tokens && typeof tokens === "object" ? tokens : {};
|
|
4217
|
+
const cache = usage.cache && typeof usage.cache === "object" ? usage.cache : {};
|
|
4218
|
+
return {
|
|
4219
|
+
input: count(usage.input ?? usage.input_tokens),
|
|
4220
|
+
output: count(usage.output ?? usage.output_tokens),
|
|
4221
|
+
reasoning: count(usage.reasoning ?? usage.reasoning_tokens),
|
|
4222
|
+
cache_read: count(usage.cacheRead ?? usage.cache_read ?? usage.cached_input_tokens ?? cache.read),
|
|
4223
|
+
cache_write: count(usage.cacheWrite ?? usage.cache_write ?? usage.cache_write_tokens ?? cache.write),
|
|
4224
|
+
max_tokens: count(maxTokens),
|
|
4225
|
+
finish_reason: typeof finish === "string" ? finish : null
|
|
4226
|
+
};
|
|
4227
|
+
}
|
|
4228
|
+
function formatRunTokenLog(tokens) {
|
|
4229
|
+
return `tokens=${JSON.stringify(tokens)}`;
|
|
4230
|
+
}
|
|
4231
|
+
|
|
4058
4232
|
// ../plugin/src/hooks/magic-context/historian-prompt.generated.ts
|
|
4059
4233
|
var COMPARTMENT_AGENT_SYSTEM_PROMPT = `# Historian
|
|
4060
4234
|
|
|
@@ -5217,11 +5391,14 @@ function clampProducerAtomChars(text, maxChars, marker) {
|
|
|
5217
5391
|
const tailChars = Math.floor(keptChars / 2);
|
|
5218
5392
|
return `${text.slice(0, headChars)}${marker}${text.slice(text.length - tailChars)}`;
|
|
5219
5393
|
}
|
|
5220
|
-
function producerInputTokenLimit(contextLimitTokens, maxOutputTokens) {
|
|
5221
|
-
if (
|
|
5394
|
+
function producerInputTokenLimit(contextLimitTokens, maxOutputTokens, inputLimitTokens) {
|
|
5395
|
+
if (!Number.isFinite(maxOutputTokens) || maxOutputTokens < 0)
|
|
5222
5396
|
return;
|
|
5223
|
-
|
|
5224
|
-
const
|
|
5397
|
+
const shared = typeof contextLimitTokens === "number" && Number.isFinite(contextLimitTokens) && contextLimitTokens > 0 ? contextLimitTokens - maxOutputTokens : undefined;
|
|
5398
|
+
const input = typeof inputLimitTokens === "number" && Number.isFinite(inputLimitTokens) && inputLimitTokens > 0 ? inputLimitTokens : undefined;
|
|
5399
|
+
if (shared === undefined && input === undefined)
|
|
5400
|
+
return;
|
|
5401
|
+
const usableInputTokens = Math.floor(Math.min(shared ?? Infinity, input ?? Infinity));
|
|
5225
5402
|
if (usableInputTokens <= 0)
|
|
5226
5403
|
return;
|
|
5227
5404
|
const limit = Math.floor(usableInputTokens * (1 - PRODUCER_WINDOW_REFUSAL_MARGIN));
|
|
@@ -5233,15 +5410,15 @@ function historianProducerReserve(window, configuredOutput, catalogOutput) {
|
|
|
5233
5410
|
return window === undefined ? 0 : catalogOutputReserve(window, catalogOutput);
|
|
5234
5411
|
}
|
|
5235
5412
|
function producerWindowFailureReason(input) {
|
|
5236
|
-
const { producerSourceTokens, contextLimitTokens, maxOutputTokens } = input;
|
|
5237
|
-
const producerInputLimitTokens = producerInputTokenLimit(contextLimitTokens, maxOutputTokens);
|
|
5238
|
-
if (producerInputLimitTokens === undefined ||
|
|
5413
|
+
const { producerSourceTokens, contextLimitTokens, inputLimitTokens, maxOutputTokens } = input;
|
|
5414
|
+
const producerInputLimitTokens = producerInputTokenLimit(contextLimitTokens, maxOutputTokens, inputLimitTokens);
|
|
5415
|
+
if (producerInputLimitTokens === undefined || !Number.isFinite(producerSourceTokens) || producerSourceTokens <= 0) {
|
|
5239
5416
|
return null;
|
|
5240
5417
|
}
|
|
5241
|
-
const usableInputTokens = Math.max(0,
|
|
5418
|
+
const usableInputTokens = Math.floor(Math.min(contextLimitTokens === undefined ? Infinity : Math.max(0, contextLimitTokens - maxOutputTokens), inputLimitTokens ?? Infinity));
|
|
5242
5419
|
if (producerSourceTokens <= producerInputLimitTokens)
|
|
5243
5420
|
return null;
|
|
5244
|
-
return `producer_source_exceeds_window producer_source_tokens=${Math.round(producerSourceTokens)} usable_input_tokens=${usableInputTokens} producer_input_limit_tokens=${producerInputLimitTokens} context_limit_tokens=${Math.round(contextLimitTokens)} max_output_tokens=${Math.round(maxOutputTokens)} estimator_margin=${PRODUCER_WINDOW_REFUSAL_MARGIN}`;
|
|
5421
|
+
return `producer_source_exceeds_window producer_source_tokens=${Math.round(producerSourceTokens)} usable_input_tokens=${usableInputTokens} producer_input_limit_tokens=${producerInputLimitTokens} context_limit_tokens=${contextLimitTokens === undefined ? "unknown" : Math.round(contextLimitTokens)} max_output_tokens=${Math.round(maxOutputTokens)} estimator_margin=${PRODUCER_WINDOW_REFUSAL_MARGIN}`;
|
|
5245
5422
|
}
|
|
5246
5423
|
function splitMarkerPair() {
|
|
5247
5424
|
return `
|
|
@@ -5250,7 +5427,7 @@ ${HISTORIAN_TRUNCATION_MARKER}
|
|
|
5250
5427
|
`;
|
|
5251
5428
|
}
|
|
5252
5429
|
function fitAtomicHistorianSourceToProducerWindow(args) {
|
|
5253
|
-
const producerInputLimitTokens = producerInputTokenLimit(args.contextLimitTokens, args.maxOutputTokens);
|
|
5430
|
+
const producerInputLimitTokens = producerInputTokenLimit(args.contextLimitTokens, args.maxOutputTokens, args.inputLimitTokens);
|
|
5254
5431
|
const originalTokens = estimateTokens(args.text);
|
|
5255
5432
|
if (producerInputLimitTokens === undefined || originalTokens < producerInputLimitTokens || producerInputLimitTokens <= 0) {
|
|
5256
5433
|
return { text: args.text, producerInputLimitTokens, removedTokens: 0 };
|
|
@@ -5284,7 +5461,7 @@ function fitAtomicHistorianSourceToProducerWindow(args) {
|
|
|
5284
5461
|
};
|
|
5285
5462
|
}
|
|
5286
5463
|
function producerPromptFailureReason(input) {
|
|
5287
|
-
const limit = producerInputTokenLimit(input.contextLimitTokens, input.maxOutputTokens);
|
|
5464
|
+
const limit = producerInputTokenLimit(input.contextLimitTokens, input.maxOutputTokens, input.inputLimitTokens);
|
|
5288
5465
|
const tokens = providerMass({ prose: input.sourceLocal, system: input.systemLocal, tools: input.toolsLocal }, calibrationForModelKey(input.modelKey), true);
|
|
5289
5466
|
if (limit === undefined)
|
|
5290
5467
|
return null;
|
|
@@ -5395,12 +5572,15 @@ function createV1HiddenCompletionExecutor(client, db, directory) {
|
|
|
5395
5572
|
throw new Error(`Historian host recorded an assistant error${finish}: ${describeAssistantFailure(assistantFailure.error)}`);
|
|
5396
5573
|
}
|
|
5397
5574
|
const text = extractLatestAssistantText(messages);
|
|
5575
|
+
const latest = Array.isArray(messages) ? messages.filter((message) => isRecord(message) && isRecord(message.info) && message.info.role === "assistant").sort((left, right) => historianMessageCreatedAt(right) - historianMessageCreatedAt(left))[0] : undefined;
|
|
5576
|
+
const info = latest && isRecord(latest.info) ? latest.info : {};
|
|
5398
5577
|
return {
|
|
5399
5578
|
messages,
|
|
5400
5579
|
text,
|
|
5401
5580
|
reasoning: text ? null : extractLatestHistorianReasoning(messages),
|
|
5402
5581
|
lengthCapped: hasLengthCappedOutput(messages),
|
|
5403
|
-
usage: sumTokensFromChildMessages(messages)
|
|
5582
|
+
usage: sumTokensFromChildMessages(messages),
|
|
5583
|
+
tokenLog: runTokenLog(info.tokens, undefined, info.finish ?? info.finish_reason ?? info.finishReason)
|
|
5404
5584
|
};
|
|
5405
5585
|
},
|
|
5406
5586
|
async close(handle, settlement) {
|
|
@@ -5419,8 +5599,8 @@ function createV1HiddenCompletionExecutor(client, db, directory) {
|
|
|
5419
5599
|
}
|
|
5420
5600
|
};
|
|
5421
5601
|
}
|
|
5422
|
-
function historianReasoningBudgetDiagnostic(outputTokens) {
|
|
5423
|
-
return `historian ran out of output budget while reasoning (length-capped at ${outputTokens} tokens, no text) — set historian.maxTokens or route historian.model to a low-reasoning lane/variant`;
|
|
5602
|
+
function historianReasoningBudgetDiagnostic(outputTokens, tokens) {
|
|
5603
|
+
return `historian ran out of output budget while reasoning (length-capped at ${outputTokens} tokens, no text${tokens ? `; ${formatRunTokenLog(tokens)}` : ""}) — set historian.maxTokens or route historian.model to a low-reasoning lane/variant`;
|
|
5424
5604
|
}
|
|
5425
5605
|
async function runValidatedHistorianPass(args) {
|
|
5426
5606
|
const firstRun = await runHistorianPrompt({
|
|
@@ -5609,8 +5789,10 @@ async function runHistorianPrompt(args) {
|
|
|
5609
5789
|
const modelKey = selected ? `${selected.providerID}/${selected.modelID}` : undefined;
|
|
5610
5790
|
const system = withContentLanguageDirective(agentId === HISTORIAN_EDITOR_AGENT ? HISTORIAN_EDITOR_SYSTEM_PROMPT : COMPARTMENT_AGENT_SYSTEM_PROMPT, args.language);
|
|
5611
5791
|
const contextLimitTokens = selected ? getSdkContextLimit(selected.providerID, selected.modelID, undefined, { reservation: "none" }) : undefined;
|
|
5612
|
-
const
|
|
5613
|
-
|
|
5792
|
+
const producerLimits = selected ? resolveHistorianProducerLimits(modelKey) : {};
|
|
5793
|
+
const producerContext = producerLimits.context ?? (producerLimits.input === undefined ? contextLimitTokens : undefined);
|
|
5794
|
+
const reserve = historianProducerReserve(producerContext, args.maxOutputTokens, selected ? getSdkOutputLimit(selected.providerID, selected.modelID) : undefined);
|
|
5795
|
+
if (contextLimitTokens !== undefined && producerInputTokenLimit(producerContext, reserve, producerLimits.input) === undefined && modelKey && !unknownProducerWindows.has(modelKey)) {
|
|
5614
5796
|
unknownProducerWindows.add(modelKey);
|
|
5615
5797
|
sessionLog(parentSessionId, `producer window inconsistent for ${modelKey}: window=${contextLimitTokens} reserve=${reserve}; sending unguarded`);
|
|
5616
5798
|
}
|
|
@@ -5619,7 +5801,8 @@ async function runHistorianPrompt(args) {
|
|
|
5619
5801
|
systemLocal: estimateTokens(system),
|
|
5620
5802
|
toolsLocal: 0,
|
|
5621
5803
|
modelKey,
|
|
5622
|
-
contextLimitTokens,
|
|
5804
|
+
contextLimitTokens: producerContext,
|
|
5805
|
+
inputLimitTokens: producerLimits.input,
|
|
5623
5806
|
maxOutputTokens: reserve
|
|
5624
5807
|
});
|
|
5625
5808
|
if (failure)
|
|
@@ -5653,9 +5836,15 @@ async function runHistorianPrompt(args) {
|
|
|
5653
5836
|
}
|
|
5654
5837
|
completion = await executor.collect(handle, 50);
|
|
5655
5838
|
const lengthCapped = completion.lengthCapped;
|
|
5839
|
+
const tokens = {
|
|
5840
|
+
...runTokenLog(undefined, args.maxOutputTokens),
|
|
5841
|
+
...completion.tokenLog,
|
|
5842
|
+
max_tokens: args.maxOutputTokens ?? null
|
|
5843
|
+
};
|
|
5844
|
+
sessionLog(parentSessionId, `historian response_chars=${(completion.text ?? completion.reasoning ?? "").length} ${formatRunTokenLog(tokens)}`);
|
|
5656
5845
|
const textResult = completion.text;
|
|
5657
5846
|
const reasoningResult = textResult ? null : completion.reasoning;
|
|
5658
|
-
const emptyError = !textResult && reasoningResult && lengthCapped ? historianReasoningBudgetDiagnostic(completion.usage.output) : !textResult && !reasoningResult ? "Historian returned no assistant output." : !textResult ? "Historian returned reasoning but no assistant text." : lengthCapped ?
|
|
5847
|
+
const emptyError = !textResult && reasoningResult && lengthCapped ? historianReasoningBudgetDiagnostic(completion.usage.output, tokens) : !textResult && !reasoningResult ? "Historian returned no assistant output." : !textResult ? "Historian returned reasoning but no assistant text." : lengthCapped ? `Historian returned length-capped output. ${formatRunTokenLog(tokens)}` : null;
|
|
5659
5848
|
const invocationId = recordInvocation({
|
|
5660
5849
|
status: emptyError ? "empty" : "completed",
|
|
5661
5850
|
messages: completion.messages,
|
|
@@ -6029,7 +6218,7 @@ function startLeaseHeartbeat(db, holderId, leaseKey, onLost, intervalOrAcquisiti
|
|
|
6029
6218
|
}
|
|
6030
6219
|
};
|
|
6031
6220
|
beat();
|
|
6032
|
-
const timer = lost ? undefined : setInterval(beat, intervalMs);
|
|
6221
|
+
const timer = lost ? undefined : setInterval(() => withSqliteBackgroundWriter(beat), intervalMs);
|
|
6033
6222
|
return {
|
|
6034
6223
|
stop: () => {
|
|
6035
6224
|
if (timer)
|
|
@@ -6748,8 +6937,10 @@ async function runHiddenSingleShotPrompt(args) {
|
|
|
6748
6937
|
callContext: args.callContext,
|
|
6749
6938
|
fetchOutput: () => args.executor.collect(handle, 50),
|
|
6750
6939
|
validateOutput: (completion) => {
|
|
6940
|
+
const tokens = completion.tokenLog ?? runTokenLog(undefined);
|
|
6941
|
+
sessionLog(args.parentSessionId ?? "dreamer", `${args.callContext} response_chars=${(completion.text ?? completion.reasoning ?? "").length} ${formatRunTokenLog(tokens)}`);
|
|
6751
6942
|
if (completion.lengthCapped) {
|
|
6752
|
-
throw new Error(completion.reasoning && !completion.text ? `${args.callContext} ran out of output budget while reasoning (length-capped at ${completion.usage.output} tokens, no text) — set dreamer.maxTokens or use a low-reasoning model` : `${args.callContext} returned length-capped output`);
|
|
6943
|
+
throw new Error(completion.reasoning && !completion.text ? `${args.callContext} ran out of output budget while reasoning (length-capped at ${completion.usage.output} tokens, no text; ${formatRunTokenLog(tokens)}) — set dreamer.maxTokens or use a low-reasoning model` : `${args.callContext} returned length-capped output; ${formatRunTokenLog(tokens)}`);
|
|
6753
6944
|
}
|
|
6754
6945
|
const text = completion.text;
|
|
6755
6946
|
if (!text && !args.allowEmpty)
|
|
@@ -6783,7 +6974,7 @@ Project memory uses exactly 5 categories. Every memory belongs to one:
|
|
|
6783
6974
|
var CURATE_SYSTEM_PROMPT = `You are a memory-pool curator for the magic-context system. You run during a scheduled dream window to keep a project's cross-session memory store lean and well-formed.
|
|
6784
6975
|
|
|
6785
6976
|
## Memory operations
|
|
6786
|
-
-
|
|
6977
|
+
- The scoped category snapshot arrives in the first message. Do not list or get it again; track mutation deltas.
|
|
6787
6978
|
- \`action="merge", ids=[N,M,...], content="...", category="..."\` — consolidate duplicates into one canonical memory
|
|
6788
6979
|
- \`action="update", ids=[N], content="...", superseded_by=M\` — rewrite content; name where removed detail survives when cutting more than half
|
|
6789
6980
|
- \`action="write", category="...", content="..."\` — create a memory (SPLITS ONLY — never mint new facts)
|
|
@@ -6813,7 +7004,7 @@ var PRIMER_INVESTIGATOR_SYSTEM_PROMPT = `You are a read-only code investigator f
|
|
|
6813
7004
|
function renderMemoryList(memories) {
|
|
6814
7005
|
return memories.map((memory) => {
|
|
6815
7006
|
const files = memory.mappedFiles.length ? memory.mappedFiles.join(", ") : "(none mapped yet)";
|
|
6816
|
-
return `[${memory.id}] ${memory.category}
|
|
7007
|
+
return `[${memory.id}] ${memory.category} importance=${memory.importance} retrieval_count=${memory.retrievalCount} seen_count=${memory.seenCount}
|
|
6817
7008
|
Content: ${memory.content}
|
|
6818
7009
|
Mapped files: ${files}${memory.hasNoFileSentinel ? " (file-independent)" : ""}`;
|
|
6819
7010
|
}).join(`
|
|
@@ -6841,9 +7032,41 @@ Rewrite narrative/historical → operational present tense ("X uses Y because Z"
|
|
|
6841
7032
|
Archive a redundant memory only when a better ACTIVE memory in the same project and category preserves its information; name that survivor with \`superseded_by\`. A bare "redundant" verdict is deletion and will be refused. Leave standalone low-value or stale entries unchanged for a human to review. The global user profile describes the operator and is never a substitute for project knowledge, so it cannot justify an archive.
|
|
6842
7033
|
KEEP (overrides archive): constraint/rule language (must/never/always) · explains WHY (because/so that/to prevent) · EXTERNAL-system limit (CONSTRAINTS: archive only if word-for-word duplicated) · path/config WITH context · retrieval_count>0 · priority/philosophy.
|
|
6843
7034
|
|
|
6844
|
-
###
|
|
7035
|
+
### Cross-chunk duplicate candidates (normalized content matches)
|
|
7036
|
+
${args.crossChunkCandidates?.join(`
|
|
7037
|
+
`) || "(none)"}
|
|
7038
|
+
|
|
7039
|
+
### Category snapshot (do not re-enumerate)
|
|
6845
7040
|
${renderMemoryList(args.memories)}`;
|
|
6846
7041
|
}
|
|
7042
|
+
function chunkCurateMemories(memories, maxCharacters) {
|
|
7043
|
+
const chunks = [];
|
|
7044
|
+
let current = [];
|
|
7045
|
+
let size = 0;
|
|
7046
|
+
for (const memory of memories) {
|
|
7047
|
+
const length = renderMemoryList([memory]).length + 2;
|
|
7048
|
+
if (current.length && size + length > maxCharacters) {
|
|
7049
|
+
chunks.push(current);
|
|
7050
|
+
current = [];
|
|
7051
|
+
size = 0;
|
|
7052
|
+
}
|
|
7053
|
+
current.push(memory);
|
|
7054
|
+
size += length;
|
|
7055
|
+
}
|
|
7056
|
+
if (current.length)
|
|
7057
|
+
chunks.push(current);
|
|
7058
|
+
const keys = new Map;
|
|
7059
|
+
chunks.forEach((chunk, index) => {
|
|
7060
|
+
chunk.forEach((memory) => {
|
|
7061
|
+
const key = memory.content.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
7062
|
+
keys.set(key, [...keys.get(key) ?? [], { id: memory.id, chunk: index }]);
|
|
7063
|
+
});
|
|
7064
|
+
});
|
|
7065
|
+
return chunks.map((chunk, index) => ({
|
|
7066
|
+
memories: chunk,
|
|
7067
|
+
crossChunkCandidates: [...keys.values()].filter((matches) => matches.some((match) => match.chunk === index) && matches.some((match) => match.chunk !== index)).map((matches) => `IDs ${matches.map((match) => match.id).join(", ")}`)
|
|
7068
|
+
}));
|
|
7069
|
+
}
|
|
6847
7070
|
var RETROSPECTIVE_SYSTEM_PROMPT = `You are a retrospective learning agent for Magic Context.
|
|
6848
7071
|
|
|
6849
7072
|
You learn only from recurring user-friction moments where the user had to correct, re-explain, or recover from the assistant's repeated behavior. You receive a pre-rendered friction window from the host and may use ctx_search to look for corroborating prior patterns.
|
|
@@ -6902,17 +7125,23 @@ Return only XML in this exact shape:
|
|
|
6902
7125
|
<learning route="observation">one recurring user preference</learning>
|
|
6903
7126
|
</learnings>`;
|
|
6904
7127
|
}
|
|
6905
|
-
function buildMaintainDocsPrompt(projectPath, changeSet, existingDocs, budget = 12000, currentTokens = 0) {
|
|
7128
|
+
function buildMaintainDocsPrompt(projectPath, changeSet, existingDocs, budget = 12000, currentTokens = 0, currentDocs) {
|
|
6906
7129
|
return `## Task: Propose documentation corrections
|
|
6907
7130
|
|
|
6908
7131
|
Project: ${projectPath}
|
|
6909
7132
|
Existing docs: ARCHITECTURE.md ${existingDocs.architecture ? "exists" : "missing"}; STRUCTURE.md ${existingDocs.structure ? "exists" : "missing"}.
|
|
6910
7133
|
Current combined token count: ${currentTokens}. Combined budget: ${budget} tokens.
|
|
6911
7134
|
|
|
6912
|
-
Host-collected code changes (
|
|
7135
|
+
Host-collected code changes (diff stat and hunks, or file/line ranges when oversized):
|
|
6913
7136
|
${changeSet}
|
|
6914
7137
|
|
|
6915
|
-
|
|
7138
|
+
Current ARCHITECTURE.md:
|
|
7139
|
+
${currentDocs?.architecture ?? "(read from project)"}
|
|
7140
|
+
|
|
7141
|
+
Current STRUCTURE.md:
|
|
7142
|
+
${currentDocs?.structure ?? "(read from project)"}
|
|
7143
|
+
|
|
7144
|
+
Find claims in these sections that the diff makes wrong or incomplete. Propose one focused change per claim or section. Do not investigate unrelated areas. Read the docs and relevant source with read-only tools. These files are short maps read by every agent in every session. Describe how the system works now, never what changed or when: no change log, dates, or commit lists. One short paragraph per subsystem at most. Mechanism detail belongs in docs/architecture/; you may name a docs page for it but never move that detail into these two files. Propose a change only when code contradicts the docs or a major piece is missing. Prefer rewriting a stale sentence to adding one. Propose nothing when nothing is wrong. Keep both files within the combined budget. Never touch protected regions (<!-- mc:protected START ... --> through <!-- mc:protected END -->); preserve their bytes.
|
|
6916
7145
|
|
|
6917
7146
|
Return ONLY a JSON array (or []), with each entry {"file":"ARCHITECTURE.md"|"STRUCTURE.md","action":"replace"|"add"|"remove","heading":"## Exact section heading","text":"## Full replacement section including heading and body (empty for remove)","reason":"one-line reason"}. A replacement includes the entire section, including its heading. Additions are appended at the end of the file. Do not include any unmodified sections. No file writes.`;
|
|
6918
7147
|
}
|
|
@@ -6922,10 +7151,11 @@ function buildDreamTaskPrompt(task, args) {
|
|
|
6922
7151
|
return buildCuratePrompt({
|
|
6923
7152
|
projectPath: args.projectPath,
|
|
6924
7153
|
category: args.curate?.category ?? "PROJECT_RULES",
|
|
6925
|
-
memories: args.curate?.memories ?? []
|
|
7154
|
+
memories: args.curate?.memories ?? [],
|
|
7155
|
+
crossChunkCandidates: args.curate?.crossChunkCandidates
|
|
6926
7156
|
});
|
|
6927
7157
|
case "maintain-docs":
|
|
6928
|
-
return buildMaintainDocsPrompt(args.projectPath, args.docsChangeSet ?? "", args.existingDocs ?? { architecture: false, structure: false }, args.docsBudget, args.docsCurrentTokens);
|
|
7158
|
+
return buildMaintainDocsPrompt(args.projectPath, args.docsChangeSet ?? "", args.existingDocs ?? { architecture: false, structure: false }, args.docsBudget, args.docsCurrentTokens, args.docsContents);
|
|
6929
7159
|
}
|
|
6930
7160
|
}
|
|
6931
7161
|
|
|
@@ -7545,8 +7775,10 @@ async function classifyOneChunk(args, chunk, anchors, sliceMs, signal) {
|
|
|
7545
7775
|
fetchOutput: () => executor.collect(opened, 50),
|
|
7546
7776
|
validateOutput: (completion) => {
|
|
7547
7777
|
const messages = completion.messages ?? [];
|
|
7778
|
+
const tokens = completion.tokenLog ?? runTokenLog(undefined);
|
|
7779
|
+
sessionLog(args.parentSessionId ?? "dreamer", `dreamer:classify response_chars=${(completion.text ?? completion.reasoning ?? "").length} ${formatRunTokenLog(tokens)}`);
|
|
7548
7780
|
if (completion.lengthCapped) {
|
|
7549
|
-
throw new Error(completion.reasoning && !completion.text ? `classify ran out of output budget while reasoning (length-capped at ${completion.usage.output} tokens, no text) — set dreamer.maxTokens or use a low-reasoning model` :
|
|
7781
|
+
throw new Error(completion.reasoning && !completion.text ? `classify ran out of output budget while reasoning (length-capped at ${completion.usage.output} tokens, no text; ${formatRunTokenLog(tokens)}) — set dreamer.maxTokens or use a low-reasoning model` : `classify returned length-capped output; ${formatRunTokenLog(tokens)}`);
|
|
7550
7782
|
}
|
|
7551
7783
|
const text = completion.text;
|
|
7552
7784
|
if (!text)
|
|
@@ -7858,6 +8090,7 @@ function hasCurrentDocsProposal(projectDir) {
|
|
|
7858
8090
|
}
|
|
7859
8091
|
function git(projectDir, args) {
|
|
7860
8092
|
return execFileSync("git", args, {
|
|
8093
|
+
windowsHide: true,
|
|
7861
8094
|
cwd: projectDir,
|
|
7862
8095
|
encoding: "utf8",
|
|
7863
8096
|
timeout: 1e4,
|
|
@@ -7869,11 +8102,80 @@ function docsChangeSet(projectDir, storedAnchor) {
|
|
|
7869
8102
|
if (realpathSync2(git(projectDir, ["rev-parse", "--show-toplevel"])) !== realpathSync2(projectDir))
|
|
7870
8103
|
return null;
|
|
7871
8104
|
const head = git(projectDir, ["rev-parse", "HEAD"]);
|
|
8105
|
+
const recorded = Boolean(storedAnchor);
|
|
7872
8106
|
let anchor = storedAnchor;
|
|
7873
|
-
if (
|
|
8107
|
+
if (anchor && (!/^[a-f0-9]{40}$/.test(anchor) || git(projectDir, ["cat-file", "-t", anchor]) !== "commit"))
|
|
8108
|
+
throw new Error("maintain-docs checkpoint is not a git commit");
|
|
8109
|
+
if (!anchor)
|
|
7874
8110
|
anchor = git(projectDir, ["log", "-1", "--format=%H", "--", ...FILES]);
|
|
7875
8111
|
if (!anchor)
|
|
7876
8112
|
return null;
|
|
8113
|
+
if (recorded && anchor === head)
|
|
8114
|
+
return { head, text: "", unchanged: true, relevant: false };
|
|
8115
|
+
if (recorded) {
|
|
8116
|
+
const changed = git(projectDir, [
|
|
8117
|
+
"diff",
|
|
8118
|
+
"-M",
|
|
8119
|
+
"--name-status",
|
|
8120
|
+
`${anchor}..HEAD`,
|
|
8121
|
+
"--",
|
|
8122
|
+
"."
|
|
8123
|
+
]).split(`
|
|
8124
|
+
`).filter(Boolean).map((line) => line.split("\t").slice(1));
|
|
8125
|
+
const files = [
|
|
8126
|
+
...new Set(changed.flat().filter((file) => file !== "ARCHITECTURE.md" && file !== "STRUCTURE.md" && !/(?:^|[./-])(test|spec)(?:[./-]|$)/.test(file) && !file.endsWith(".lock")))
|
|
8127
|
+
];
|
|
8128
|
+
const docs = FILES.map((file) => existsSync3(join3(projectDir, file)) ? readFileSync3(join3(projectDir, file), "utf8") : "").join(`
|
|
8129
|
+
`);
|
|
8130
|
+
const relevant = files.some((file) => {
|
|
8131
|
+
const directory = file.split("/").slice(0, -1).join("/");
|
|
8132
|
+
return docs.includes(file) || directory.length > 2 && docs.includes(directory);
|
|
8133
|
+
});
|
|
8134
|
+
if (!relevant)
|
|
8135
|
+
return { head, text: "", unchanged: false, relevant: false };
|
|
8136
|
+
const stat = git(projectDir, [
|
|
8137
|
+
"diff",
|
|
8138
|
+
"-M",
|
|
8139
|
+
"--stat",
|
|
8140
|
+
`${anchor}..HEAD`,
|
|
8141
|
+
"--",
|
|
8142
|
+
...files
|
|
8143
|
+
]);
|
|
8144
|
+
const patch = git(projectDir, [
|
|
8145
|
+
"diff",
|
|
8146
|
+
"-M",
|
|
8147
|
+
"--unified=3",
|
|
8148
|
+
`${anchor}..HEAD`,
|
|
8149
|
+
"--",
|
|
8150
|
+
...files
|
|
8151
|
+
]);
|
|
8152
|
+
const budget = 32000;
|
|
8153
|
+
const text = `Changed files:
|
|
8154
|
+
${files.join(`
|
|
8155
|
+
`)}
|
|
8156
|
+
|
|
8157
|
+
Stat:
|
|
8158
|
+
${stat}
|
|
8159
|
+
|
|
8160
|
+
Hunks:
|
|
8161
|
+
${patch}`;
|
|
8162
|
+
if (text.length <= budget)
|
|
8163
|
+
return { head, text, unchanged: false, relevant: true };
|
|
8164
|
+
const ranges = [...patch.matchAll(/^\+\+\+ b\/(.+)$|^@@ .* \+(\d+)(?:,(\d+))? @@/gm)];
|
|
8165
|
+
return {
|
|
8166
|
+
head,
|
|
8167
|
+
unchanged: false,
|
|
8168
|
+
relevant: true,
|
|
8169
|
+
text: `Changed files and ranges (diff exceeds prompt budget):
|
|
8170
|
+
${files.join(`
|
|
8171
|
+
`)}
|
|
8172
|
+
${ranges.map((match) => match[1] ? `file: ${match[1]}` : `line: ${match[2]} +${match[3] ?? 1}`).join(`
|
|
8173
|
+
`).slice(0, budget)}
|
|
8174
|
+
|
|
8175
|
+
Stat:
|
|
8176
|
+
${stat}`
|
|
8177
|
+
};
|
|
8178
|
+
}
|
|
7877
8179
|
const excluded = [
|
|
7878
8180
|
"ARCHITECTURE.md",
|
|
7879
8181
|
"STRUCTURE.md",
|
|
@@ -7903,11 +8205,15 @@ function docsChangeSet(projectDir, storedAnchor) {
|
|
|
7903
8205
|
const text = raw.slice(0, 24000);
|
|
7904
8206
|
return {
|
|
7905
8207
|
head,
|
|
8208
|
+
unchanged: false,
|
|
8209
|
+
relevant: true,
|
|
7906
8210
|
text: `${text}${raw.length > text.length ? `
|
|
7907
8211
|
[cut ${raw.length - text.length} bytes]` : ""}${commits.length > 201 ? `
|
|
7908
8212
|
[cut commits beyond newest 200]` : ""}`
|
|
7909
8213
|
};
|
|
7910
|
-
} catch {
|
|
8214
|
+
} catch (error) {
|
|
8215
|
+
if (storedAnchor)
|
|
8216
|
+
throw error;
|
|
7911
8217
|
return null;
|
|
7912
8218
|
}
|
|
7913
8219
|
}
|
|
@@ -7941,7 +8247,7 @@ function diff(name, before, after) {
|
|
|
7941
8247
|
writeFileSync3(join3(dir, "before"), before);
|
|
7942
8248
|
writeFileSync3(join3(dir, "after"), after);
|
|
7943
8249
|
try {
|
|
7944
|
-
return execFileSync("git", ["diff", "--no-index", "--", join3(dir, "before"), join3(dir, "after")], { encoding: "utf8" });
|
|
8250
|
+
return execFileSync("git", ["diff", "--no-index", "--", join3(dir, "before"), join3(dir, "after")], { windowsHide: true, encoding: "utf8" });
|
|
7945
8251
|
} catch (error) {
|
|
7946
8252
|
return error.stdout?.replaceAll(join3(dir, "before"), `a/${name}`).replaceAll(join3(dir, "after"), `b/${name}`) ?? "";
|
|
7947
8253
|
}
|
|
@@ -8192,7 +8498,7 @@ function requestValidatedAddress(validation, candidate, options) {
|
|
|
8192
8498
|
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
8193
8499
|
bytes += buf.byteLength;
|
|
8194
8500
|
if (bytes > options.bodyLimitBytes) {
|
|
8195
|
-
reject(new SmartNoteNetworkError(
|
|
8501
|
+
reject(new SmartNoteNetworkError(`SMART_NOTE_NETWORK: response body too large at ${url.href} (received at least ${bytes} bytes; limit ${options.bodyLimitBytes})`, { terminal: true, persistent: true }));
|
|
8196
8502
|
response.destroy();
|
|
8197
8503
|
request2.destroy();
|
|
8198
8504
|
return;
|
|
@@ -8529,6 +8835,7 @@ async function runGit(projectRoot, args, signal) {
|
|
|
8529
8835
|
throwIfAborted2(signal);
|
|
8530
8836
|
try {
|
|
8531
8837
|
const result = await execFileAsync("git", ["-C", projectRoot, ...args], {
|
|
8838
|
+
windowsHide: true,
|
|
8532
8839
|
timeout: DEFAULT_GIT_TIMEOUT_MS,
|
|
8533
8840
|
maxBuffer: 128 * 1024,
|
|
8534
8841
|
signal
|
|
@@ -8691,6 +8998,7 @@ async function runCompiledSmartNoteCheckLocked(options, quickjs) {
|
|
|
8691
8998
|
const controller = new AbortController;
|
|
8692
8999
|
let externallyCancelled = false;
|
|
8693
9000
|
let executionTimedOut = false;
|
|
9001
|
+
let persistentNetworkFailure = false;
|
|
8694
9002
|
const externalAbort = () => {
|
|
8695
9003
|
externallyCancelled = true;
|
|
8696
9004
|
controller.abort(options.signal?.reason);
|
|
@@ -8709,7 +9017,19 @@ async function runCompiledSmartNoteCheckLocked(options, quickjs) {
|
|
|
8709
9017
|
context.runtime.setMemoryLimit(options.heapLimitBytes ?? DEFAULT_HEAP_LIMIT_BYTES);
|
|
8710
9018
|
context.runtime.setMaxStackSize(options.stackLimitBytes ?? DEFAULT_STACK_LIMIT_BYTES);
|
|
8711
9019
|
context.runtime.setInterruptHandler(() => controller.signal.aborted || Date.now() > deadline);
|
|
8712
|
-
installCapabilityObject(context,
|
|
9020
|
+
installCapabilityObject(context, {
|
|
9021
|
+
...capabilities,
|
|
9022
|
+
httpGet: async (url) => {
|
|
9023
|
+
try {
|
|
9024
|
+
return await capabilities.httpGet(url);
|
|
9025
|
+
} catch (error) {
|
|
9026
|
+
if (error instanceof SmartNoteNetworkError && error.persistent) {
|
|
9027
|
+
persistentNetworkFailure = true;
|
|
9028
|
+
}
|
|
9029
|
+
throw error;
|
|
9030
|
+
}
|
|
9031
|
+
}
|
|
9032
|
+
});
|
|
8713
9033
|
disableAmbientDynamicCode(context);
|
|
8714
9034
|
const result = await evalCheck(context, options.compiledCheck);
|
|
8715
9035
|
const checkResult = result;
|
|
@@ -8723,14 +9043,14 @@ async function runCompiledSmartNoteCheckLocked(options, quickjs) {
|
|
|
8723
9043
|
} catch (error) {
|
|
8724
9044
|
if (externallyCancelled && !executionTimedOut)
|
|
8725
9045
|
return cancelledResult(error);
|
|
8726
|
-
return failureResult(formatSandboxError(error), isSmartNoteNetworkError(error));
|
|
9046
|
+
return failureResult(formatSandboxError(error), isSmartNoteNetworkError(error), persistentNetworkFailure);
|
|
8727
9047
|
} finally {
|
|
8728
9048
|
clearTimeout(timer);
|
|
8729
9049
|
options.signal?.removeEventListener("abort", externalAbort);
|
|
8730
9050
|
}
|
|
8731
9051
|
}
|
|
8732
|
-
function failureResult(error, network) {
|
|
8733
|
-
return { ok: false, cancelled: false, error: truncate(error), network };
|
|
9052
|
+
function failureResult(error, network, persistent = false) {
|
|
9053
|
+
return { ok: false, cancelled: false, error: truncate(error), network, persistent };
|
|
8734
9054
|
}
|
|
8735
9055
|
function cancelledResult(reason) {
|
|
8736
9056
|
return {
|
|
@@ -8828,7 +9148,12 @@ var MAX_CRON_CHARS = 256;
|
|
|
8828
9148
|
var MAX_COMPILER_ERROR_CHARS = 2 * 1024;
|
|
8829
9149
|
async function compileSmartNoteCheck(args) {
|
|
8830
9150
|
if (!args.note.surfaceCondition) {
|
|
8831
|
-
return {
|
|
9151
|
+
return {
|
|
9152
|
+
ok: false,
|
|
9153
|
+
cancelled: false,
|
|
9154
|
+
error: "note has no surface condition",
|
|
9155
|
+
persistent: false
|
|
9156
|
+
};
|
|
8832
9157
|
}
|
|
8833
9158
|
const prompt = `Compile this smart note condition into a sandbox check.
|
|
8834
9159
|
|
|
@@ -8935,15 +9260,13 @@ Remember: output only the JSON object described by the system prompt.`;
|
|
|
8935
9260
|
const compiledCheck = normalizeCompiledCheck(response.compiled_check);
|
|
8936
9261
|
const manifest = normalizeManifest(response.manifest);
|
|
8937
9262
|
const checkCron = normalizeCron(response.check_cron);
|
|
8938
|
-
|
|
9263
|
+
const dryRun = await dryRunSmartNoteCheck(compiledCheck, args.capabilityFactory, args.signal);
|
|
9264
|
+
for (const warning of [
|
|
9265
|
+
...manifestAdvisoryWarnings(compiledCheck, manifest),
|
|
9266
|
+
...dryRun.advisories
|
|
9267
|
+
]) {
|
|
8939
9268
|
log(`[dreamer] smart note #${args.note.id}: manifest advisory — ${warning}`);
|
|
8940
9269
|
}
|
|
8941
|
-
const dryRun = await runCompiledSmartNoteCheck({
|
|
8942
|
-
compiledCheck,
|
|
8943
|
-
capabilityFactory: args.capabilityFactory,
|
|
8944
|
-
signal: args.signal,
|
|
8945
|
-
timeoutMs: 2000
|
|
8946
|
-
});
|
|
8947
9270
|
if (!dryRun.ok) {
|
|
8948
9271
|
const error = boundedError(`dry-run failed: ${dryRun.error}`);
|
|
8949
9272
|
recordInvocation({
|
|
@@ -8951,7 +9274,12 @@ Remember: output only the JSON object described by the system prompt.`;
|
|
|
8951
9274
|
messages: outputMessages,
|
|
8952
9275
|
error
|
|
8953
9276
|
});
|
|
8954
|
-
return {
|
|
9277
|
+
return {
|
|
9278
|
+
ok: false,
|
|
9279
|
+
cancelled: dryRun.cancelled,
|
|
9280
|
+
error,
|
|
9281
|
+
persistent: !dryRun.cancelled && dryRun.persistent
|
|
9282
|
+
};
|
|
8955
9283
|
}
|
|
8956
9284
|
recordInvocation({ status: "completed", messages: outputMessages });
|
|
8957
9285
|
return {
|
|
@@ -8966,7 +9294,7 @@ Remember: output only the JSON object described by the system prompt.`;
|
|
|
8966
9294
|
const cancelled = args.signal.aborted;
|
|
8967
9295
|
const message = boundedError(error instanceof Error ? error.message : String(error));
|
|
8968
9296
|
recordInvocation({ status: cancelled ? "aborted" : "failed", error: message });
|
|
8969
|
-
return { ok: false, cancelled, error: message };
|
|
9297
|
+
return { ok: false, cancelled, error: message, persistent: false };
|
|
8970
9298
|
} finally {
|
|
8971
9299
|
if (args.client) {
|
|
8972
9300
|
await teardownChildSession({
|
|
@@ -8981,6 +9309,29 @@ Remember: output only the JSON object described by the system prompt.`;
|
|
|
8981
9309
|
}
|
|
8982
9310
|
}
|
|
8983
9311
|
}
|
|
9312
|
+
async function dryRunSmartNoteCheck(compiledCheck, capabilityFactory, signal) {
|
|
9313
|
+
const responses = [];
|
|
9314
|
+
const dryRun = await runCompiledSmartNoteCheck({
|
|
9315
|
+
compiledCheck,
|
|
9316
|
+
capabilityFactory: (runSignal) => {
|
|
9317
|
+
const capabilities = capabilityFactory(runSignal);
|
|
9318
|
+
return {
|
|
9319
|
+
...capabilities,
|
|
9320
|
+
httpGet: async (url) => {
|
|
9321
|
+
const response = await capabilities.httpGet(url);
|
|
9322
|
+
responses.push({ url, status: response.status });
|
|
9323
|
+
return response;
|
|
9324
|
+
}
|
|
9325
|
+
};
|
|
9326
|
+
},
|
|
9327
|
+
signal,
|
|
9328
|
+
timeoutMs: 2000
|
|
9329
|
+
});
|
|
9330
|
+
const advisories = dryRun.ok && responses.length > 0 && responses.every(({ status }) => status === 401 || status === 403 || status === 404) ? [
|
|
9331
|
+
`all HTTP sources currently return 401/403/404: ${responses.map(({ url, status }) => `${url} (HTTP ${status})`).join(", ")}`
|
|
9332
|
+
] : [];
|
|
9333
|
+
return { ...dryRun, advisories };
|
|
9334
|
+
}
|
|
8984
9335
|
function parseCompilerOutput(output) {
|
|
8985
9336
|
if (!output)
|
|
8986
9337
|
throw new Error("smart-note compiler returned no output");
|
|
@@ -9435,7 +9786,7 @@ async function compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld,
|
|
|
9435
9786
|
expected: sourceRevisionExpectation(note),
|
|
9436
9787
|
leaseHeld,
|
|
9437
9788
|
write: () => {
|
|
9438
|
-
markSmartNoteCompilationFailure(args.db, note.id, now, MAX_COMPILATION_FAILURES);
|
|
9789
|
+
markSmartNoteCompilationFailure(args.db, note.id, now, MAX_COMPILATION_FAILURES, result.error, result.persistent);
|
|
9439
9790
|
}
|
|
9440
9791
|
});
|
|
9441
9792
|
return false;
|
|
@@ -9806,7 +10157,7 @@ var MAP_MEMORIES_SYSTEM_PROMPT = `You are a memory mapper for the magic-context
|
|
|
9806
10157
|
|
|
9807
10158
|
A memory's BACKING FILES are the file(s) whose code the memory makes a claim about — the files you would open to check whether the memory is accurate. You do NOT judge accuracy, rewrite, or remove anything. You only LOCATE backing files.
|
|
9808
10159
|
|
|
9809
|
-
Tools (read-only): read, grep, glob
|
|
10160
|
+
Tools (read-only): read, grep, glob. Request line ranges rather than full files; use offset/limit for longer files. Each memory may come with "Likely files" already named in it and confirmed to exist — confirm those FIRST (cheap) instead of searching. Use search/grep to FIND code only when no likely files are given. Do not guess — confirm a file exists and genuinely backs the memory before listing it. Keep reads minimal: you do not need to read a whole file to confirm it backs a one-line claim.
|
|
9810
10161
|
|
|
9811
10162
|
For each memory decide ONE of:
|
|
9812
10163
|
- Backing files found → the COMPLETE set of repo-relative paths whose code the memory is about.
|
|
@@ -10063,6 +10414,7 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
10063
10414
|
prompt,
|
|
10064
10415
|
title: "magic-context-dream-map-memories",
|
|
10065
10416
|
callContext: "dreamer:map-memories",
|
|
10417
|
+
metadata: { tokenBudget: args.tokenBudget, onBudgetUpdate: args.onBudgetUpdate },
|
|
10066
10418
|
model: args.model,
|
|
10067
10419
|
fallbackModels: args.fallbackModels,
|
|
10068
10420
|
timeoutMs: sliceMs,
|
|
@@ -10103,7 +10455,10 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
10103
10455
|
parts: [{ type: "text", text: prompt, synthetic: true }]
|
|
10104
10456
|
}
|
|
10105
10457
|
}, {
|
|
10106
|
-
transport: createPromptAsyncTransport(client, agentSessionId
|
|
10458
|
+
transport: createPromptAsyncTransport(client, agentSessionId, {
|
|
10459
|
+
tokenBudget: args.tokenBudget,
|
|
10460
|
+
onBudgetUpdate: args.onBudgetUpdate
|
|
10461
|
+
}),
|
|
10107
10462
|
timeoutMs: sliceMs,
|
|
10108
10463
|
signal,
|
|
10109
10464
|
fallbackModels: args.fallbackModels,
|
|
@@ -10143,7 +10498,7 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
10143
10498
|
error,
|
|
10144
10499
|
messages: await readFailedChildMessages(args.client, agentSessionId, args.sessionDirectory)
|
|
10145
10500
|
});
|
|
10146
|
-
if (error instanceof DreamerModuleFailureError)
|
|
10501
|
+
if (error instanceof DreamerModuleFailureError || error instanceof DreamTokenBudgetExceeded)
|
|
10147
10502
|
throw error;
|
|
10148
10503
|
if (signal.aborted)
|
|
10149
10504
|
throw error;
|
|
@@ -10773,6 +11128,7 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
|
|
|
10773
11128
|
prompt,
|
|
10774
11129
|
title: "magic-context-dream-refresh-primers",
|
|
10775
11130
|
callContext: "dreamer:refresh-primers",
|
|
11131
|
+
metadata: { tokenBudget: args.tokenBudget, onBudgetUpdate: args.onBudgetUpdate },
|
|
10776
11132
|
model: args.model,
|
|
10777
11133
|
fallbackModels: args.fallbackModels,
|
|
10778
11134
|
language: args.language,
|
|
@@ -10822,6 +11178,10 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
|
|
|
10822
11178
|
signal,
|
|
10823
11179
|
fallbackModels: args.fallbackModels,
|
|
10824
11180
|
callContext: "dreamer:refresh-primers",
|
|
11181
|
+
transport: createPromptAsyncTransport(client, agentSessionId, {
|
|
11182
|
+
tokenBudget: args.tokenBudget,
|
|
11183
|
+
onBudgetUpdate: args.onBudgetUpdate
|
|
11184
|
+
}),
|
|
10825
11185
|
fetchOutput: async () => {
|
|
10826
11186
|
const messagesResponse = await client.session.messages({
|
|
10827
11187
|
path: { id: agentSessionId },
|
|
@@ -11034,12 +11394,14 @@ class OpenCodeRetrospectiveRawProvider {
|
|
|
11034
11394
|
this.openDb = deps.openOpenCodeDb ?? openOpenCodeDb;
|
|
11035
11395
|
}
|
|
11036
11396
|
listProjectSessions(projectIdentity) {
|
|
11037
|
-
const rows = this.deps.contextDb.prepare(`SELECT sp.session_id,
|
|
11397
|
+
const rows = this.deps.contextDb.prepare(`SELECT sp.session_id, CAST(activity.value AS INTEGER) AS updated_at
|
|
11038
11398
|
FROM session_projects sp
|
|
11399
|
+
JOIN schema_migrations_meta activity
|
|
11400
|
+
ON activity.key = 'retrospective_activity:' || sp.session_id
|
|
11039
11401
|
LEFT JOIN session_meta m ON m.session_id = sp.session_id
|
|
11040
11402
|
WHERE sp.project_path = ? AND sp.harness = 'opencode'
|
|
11041
11403
|
AND COALESCE(m.is_subagent, 0) = 0
|
|
11042
|
-
ORDER BY
|
|
11404
|
+
ORDER BY updated_at ASC, sp.session_id ASC`).all(projectIdentity);
|
|
11043
11405
|
return rows.map((row) => ({
|
|
11044
11406
|
sessionId: row.session_id,
|
|
11045
11407
|
updatedAt: typeof row.updated_at === "number" ? row.updated_at : undefined
|
|
@@ -11058,15 +11420,13 @@ class OpenCodeRetrospectiveRawProvider {
|
|
|
11058
11420
|
const db = this.resolveDb();
|
|
11059
11421
|
if (!db)
|
|
11060
11422
|
return { messages: [], truncated: false };
|
|
11061
|
-
|
|
11062
|
-
return readOpenCodeMessagesSince(db, sessionId, sinceMs, capPerSession);
|
|
11063
|
-
} catch {
|
|
11064
|
-
return { messages: [], truncated: false };
|
|
11065
|
-
}
|
|
11423
|
+
return readOpenCodeMessagesSince(db, sessionId, sinceMs, capPerSession);
|
|
11066
11424
|
}
|
|
11067
11425
|
readOldestMessageTimesSince(sessionIds, sinceMs) {
|
|
11068
11426
|
const db = this.resolveDb();
|
|
11069
|
-
if (!db
|
|
11427
|
+
if (!db)
|
|
11428
|
+
return null;
|
|
11429
|
+
if (sessionIds.length === 0)
|
|
11070
11430
|
return new Map;
|
|
11071
11431
|
return readOpenCodeOldestMessageTimesSince(db, sessionIds, sinceMs);
|
|
11072
11432
|
}
|
|
@@ -11163,6 +11523,11 @@ async function readRetrospectiveScanWindow(provider, projectIdentity, watermarkM
|
|
|
11163
11523
|
}
|
|
11164
11524
|
const droppedSince = [...budgetDropped, ...countDropped];
|
|
11165
11525
|
let maxScannedTs = scanSinceMs;
|
|
11526
|
+
if (oldestBySession && sinceReads.every((read) => read.messages.length === 0)) {
|
|
11527
|
+
for (const { session } of eligibleSessions) {
|
|
11528
|
+
maxScannedTs = Math.max(maxScannedTs, session.updatedAt ?? scanSinceMs);
|
|
11529
|
+
}
|
|
11530
|
+
}
|
|
11166
11531
|
for (const row of keptSince) {
|
|
11167
11532
|
if (row.ts > maxScannedTs)
|
|
11168
11533
|
maxScannedTs = row.ts;
|
|
@@ -11454,6 +11819,127 @@ function insertDreamRun(db, run) {
|
|
|
11454
11819
|
// ../plugin/src/features/magic-context/dreamer/verify.ts
|
|
11455
11820
|
import { createHash as createHash9 } from "node:crypto";
|
|
11456
11821
|
|
|
11822
|
+
// ../plugin/src/features/magic-context/dreamer/verify-diff.ts
|
|
11823
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
11824
|
+
import { promisify as promisify2 } from "node:util";
|
|
11825
|
+
var exec = promisify2(execFile2);
|
|
11826
|
+
var VERIFY_DIFF_BUDGET_TOKENS = 12000;
|
|
11827
|
+
var MAX_DIFF_BYTES = 4 * 1024 * 1024;
|
|
11828
|
+
async function git2(root, args) {
|
|
11829
|
+
const { stdout } = await exec("git", args, {
|
|
11830
|
+
cwd: root,
|
|
11831
|
+
timeout: 1e4,
|
|
11832
|
+
maxBuffer: MAX_DIFF_BYTES,
|
|
11833
|
+
encoding: "utf8"
|
|
11834
|
+
});
|
|
11835
|
+
return stdout;
|
|
11836
|
+
}
|
|
11837
|
+
async function buildVerifyDiffEvidence(directory, memories) {
|
|
11838
|
+
const root = await resolveGitTopLevel(directory);
|
|
11839
|
+
const head = root && await readGitHead(root);
|
|
11840
|
+
if (!root || !head || memories.some((m) => !m.verifiedAt))
|
|
11841
|
+
return null;
|
|
11842
|
+
try {
|
|
11843
|
+
const bases = new Map;
|
|
11844
|
+
for (const memory of memories) {
|
|
11845
|
+
const at = memory.verifiedAt;
|
|
11846
|
+
if (!at)
|
|
11847
|
+
return null;
|
|
11848
|
+
const base = memory.verifiedCommit ?? (await git2(root, [
|
|
11849
|
+
"rev-list",
|
|
11850
|
+
"-1",
|
|
11851
|
+
`--before=${new Date(at).toISOString()}`,
|
|
11852
|
+
head
|
|
11853
|
+
])).trim();
|
|
11854
|
+
if (!/^[0-9a-f]{40}$/i.test(base))
|
|
11855
|
+
return null;
|
|
11856
|
+
bases.set(memory.id, base);
|
|
11857
|
+
}
|
|
11858
|
+
const grouped = new Map;
|
|
11859
|
+
for (const memory of memories) {
|
|
11860
|
+
const at = memory.verifiedAt;
|
|
11861
|
+
const base = bases.get(memory.id);
|
|
11862
|
+
if (!at || !base)
|
|
11863
|
+
return null;
|
|
11864
|
+
for (const file of memory.mappedFiles) {
|
|
11865
|
+
if (!file || file.startsWith("/") || file.split("/").includes(".."))
|
|
11866
|
+
return null;
|
|
11867
|
+
const previous = grouped.get(file);
|
|
11868
|
+
if (!previous)
|
|
11869
|
+
grouped.set(file, {
|
|
11870
|
+
base,
|
|
11871
|
+
time: at,
|
|
11872
|
+
ids: [memory.id]
|
|
11873
|
+
});
|
|
11874
|
+
else {
|
|
11875
|
+
previous.ids.push(memory.id);
|
|
11876
|
+
if (at < previous.time) {
|
|
11877
|
+
previous.time = at;
|
|
11878
|
+
previous.base = base;
|
|
11879
|
+
}
|
|
11880
|
+
}
|
|
11881
|
+
}
|
|
11882
|
+
}
|
|
11883
|
+
const files = [];
|
|
11884
|
+
const namesByBase = new Map;
|
|
11885
|
+
for (const [file, group] of grouped) {
|
|
11886
|
+
let names = namesByBase.get(group.base);
|
|
11887
|
+
if (names === undefined) {
|
|
11888
|
+
names = await git2(root, ["diff", "-M", "--name-status", group.base, head]);
|
|
11889
|
+
namesByBase.set(group.base, names);
|
|
11890
|
+
}
|
|
11891
|
+
const rename = names.split(`
|
|
11892
|
+
`).map((line) => line.split("\t")).find((parts) => parts[0]?.startsWith("R") && parts[1] === file);
|
|
11893
|
+
const paths = rename ? [file, rename[2]] : [file];
|
|
11894
|
+
const committed = await git2(root, [
|
|
11895
|
+
"diff",
|
|
11896
|
+
"-M",
|
|
11897
|
+
"--no-ext-diff",
|
|
11898
|
+
group.base,
|
|
11899
|
+
head,
|
|
11900
|
+
"--",
|
|
11901
|
+
...paths
|
|
11902
|
+
]);
|
|
11903
|
+
const pending = await git2(root, ["diff", "-M", "--no-ext-diff", head, "--", ...paths]);
|
|
11904
|
+
const status = rename ? `RENAMED ${file} -> ${rename[2]}
|
|
11905
|
+
` : names.split(`
|
|
11906
|
+
`).some((line) => line === `D ${file}`) ? `DELETED ${file}
|
|
11907
|
+
` : "";
|
|
11908
|
+
files.push({
|
|
11909
|
+
path: file,
|
|
11910
|
+
memoryIds: group.ids,
|
|
11911
|
+
text: `${status}${committed}${pending ? `
|
|
11912
|
+
Uncommitted changes:
|
|
11913
|
+
${pending}` : ""}` || "No changed hunks in mapped file."
|
|
11914
|
+
});
|
|
11915
|
+
}
|
|
11916
|
+
const size = files.reduce((sum, file) => sum + file.text.length, 0);
|
|
11917
|
+
if (size <= VERIFY_DIFF_BUDGET_TOKENS * 3)
|
|
11918
|
+
return { head, mode: "diff", files };
|
|
11919
|
+
return {
|
|
11920
|
+
head,
|
|
11921
|
+
mode: "ranges",
|
|
11922
|
+
files: files.map((file) => {
|
|
11923
|
+
const headers = file.text.split(`
|
|
11924
|
+
`).filter((line) => /^(?:@@|diff --git|rename |deleted file mode|new file mode|RENAMED|DELETED|Uncommitted changes:)/.test(line));
|
|
11925
|
+
const lines = file.text.split(`
|
|
11926
|
+
`);
|
|
11927
|
+
const added = lines.filter((line) => line.startsWith("+") && !line.startsWith("+++")).length;
|
|
11928
|
+
const removed = lines.filter((line) => line.startsWith("-") && !line.startsWith("---")).length;
|
|
11929
|
+
return {
|
|
11930
|
+
...file,
|
|
11931
|
+
text: `Changed lines: +${added}/-${removed}.
|
|
11932
|
+
${headers.join(`
|
|
11933
|
+
`)}
|
|
11934
|
+
Read only these changed line ranges if they may affect the claim.`
|
|
11935
|
+
};
|
|
11936
|
+
})
|
|
11937
|
+
};
|
|
11938
|
+
} catch {
|
|
11939
|
+
return null;
|
|
11940
|
+
}
|
|
11941
|
+
}
|
|
11942
|
+
|
|
11457
11943
|
// ../plugin/src/features/magic-context/dreamer/verify-gate.ts
|
|
11458
11944
|
import path5 from "node:path";
|
|
11459
11945
|
function minOf(values) {
|
|
@@ -11488,12 +11974,24 @@ async function partitionVerifyScope(args) {
|
|
|
11488
11974
|
const active = getMemoriesByProject(args.db, args.projectIdentity);
|
|
11489
11975
|
const verById = getMemoryVerifications(args.db, active.map((m) => m.id));
|
|
11490
11976
|
const candidates = active.filter((m) => (verById.get(m.id)?.files.length ?? 0) > 0);
|
|
11491
|
-
const toPrompt = (m) =>
|
|
11492
|
-
|
|
11493
|
-
|
|
11494
|
-
|
|
11495
|
-
|
|
11496
|
-
|
|
11977
|
+
const toPrompt = (m) => {
|
|
11978
|
+
const verifiedAt = verById.get(m.id)?.verifiedAt ?? 0;
|
|
11979
|
+
let verifiedCommit = null;
|
|
11980
|
+
try {
|
|
11981
|
+
const metadata = JSON.parse(m.metadataJson ?? "{}");
|
|
11982
|
+
if (metadata.dreamerVerifiedAt === verifiedAt && typeof metadata.dreamerVerifiedCommit === "string" && /^[0-9a-f]{40}$/i.test(metadata.dreamerVerifiedCommit)) {
|
|
11983
|
+
verifiedCommit = metadata.dreamerVerifiedCommit;
|
|
11984
|
+
}
|
|
11985
|
+
} catch {}
|
|
11986
|
+
return {
|
|
11987
|
+
id: m.id,
|
|
11988
|
+
category: m.category,
|
|
11989
|
+
content: m.content,
|
|
11990
|
+
mappedFiles: verById.get(m.id)?.files ?? [],
|
|
11991
|
+
verifiedAt,
|
|
11992
|
+
verifiedCommit
|
|
11993
|
+
};
|
|
11994
|
+
};
|
|
11497
11995
|
if (args.forceBroad) {
|
|
11498
11996
|
const broadCycleStartAt = ensureBroadCycleStart({
|
|
11499
11997
|
db: args.db,
|
|
@@ -11573,9 +12071,9 @@ async function partitionVerifyScope(args) {
|
|
|
11573
12071
|
// ../plugin/src/features/magic-context/dreamer/verify-prompt.ts
|
|
11574
12072
|
var VERIFY_SYSTEM_PROMPT = `You are a memory verifier for the magic-context system. You verify project memories against the CURRENT code.
|
|
11575
12073
|
|
|
11576
|
-
Each memory below comes with its backing file(s) — the code it makes a claim about. For EACH memory: read its backing files (you may read more if needed) and decide whether the memory is still accurate.
|
|
12074
|
+
Each memory below comes with its backing file(s) — the code it makes a claim about. For EACH memory: use the provided git diff as evidence when present. If no diff is supplied, read its backing files (you may read more if needed) and decide whether the memory is still accurate.
|
|
11577
12075
|
|
|
11578
|
-
Tools (read-only): read, grep, glob
|
|
12076
|
+
Tools (read-only): read, grep, glob. Request line ranges and keep reads short. You read code only when the evidence requires it; you change nothing.
|
|
11579
12077
|
|
|
11580
12078
|
Decide ONE of four outcomes per memory:
|
|
11581
12079
|
- VERIFIED — still accurate. Keep it as-is.
|
|
@@ -11600,7 +12098,7 @@ Rules:
|
|
|
11600
12098
|
- Every input memory id MUST appear exactly once, in exactly one of verified/update/archive/skip.
|
|
11601
12099
|
- files = the COMPLETE current backing set (repo-relative, comma-separated). It may differ from the given mapping if a file moved — record what you actually verified against.
|
|
11602
12100
|
- Default to VERIFIED. skip when code cannot decide the claim. update and archive are the exceptions, not the norm.`;
|
|
11603
|
-
function buildVerifyPrompt(projectPath, memories) {
|
|
12101
|
+
function buildVerifyPrompt(projectPath, memories, evidence) {
|
|
11604
12102
|
const list = memories.map((m) => `[${m.id}] ${m.category}
|
|
11605
12103
|
Content: ${m.content}
|
|
11606
12104
|
Backing files: ${m.mappedFiles.join(", ")}`).join(`
|
|
@@ -11610,11 +12108,18 @@ Backing files: ${m.mappedFiles.join(", ")}`).join(`
|
|
|
11610
12108
|
|
|
11611
12109
|
Project: ${projectPath}
|
|
11612
12110
|
|
|
11613
|
-
Read each memory's backing files
|
|
12111
|
+
${evidence ? "The git diff below is the evidence. If changed hunks do not touch a claim, mark it verified without opening files. Open files only when a diff suggests the claim may have changed. A rename or deletion requires an update or archive when positively supported; do not archive on uncertainty." : "Read each memory's backing files to check its claims."} Decide verified / update / archive / skip (default verified; behavioral directives only permit verified or skip), then output ONE <verify> manifest covering every id.
|
|
11614
12112
|
|
|
11615
12113
|
<memories>
|
|
11616
12114
|
${list}
|
|
11617
|
-
</memories
|
|
12115
|
+
</memories>${evidence ? `
|
|
12116
|
+
|
|
12117
|
+
<changed-files mode="${evidence.mode}">
|
|
12118
|
+
${evidence.files.map((file) => `File: ${file.path} (memories: ${file.memoryIds.join(", ")})
|
|
12119
|
+
${file.text}`).join(`
|
|
12120
|
+
|
|
12121
|
+
`)}
|
|
12122
|
+
</changed-files>` : ""}`;
|
|
11618
12123
|
}
|
|
11619
12124
|
function attrOf(s, name) {
|
|
11620
12125
|
const m = s.match(new RegExp(`\\b${name}\\s*=\\s*"([^"]*)"`));
|
|
@@ -11738,8 +12243,11 @@ async function runVerify(args) {
|
|
|
11738
12243
|
return result;
|
|
11739
12244
|
}
|
|
11740
12245
|
const batches = [];
|
|
11741
|
-
|
|
11742
|
-
|
|
12246
|
+
const groups = !args.forceBroad && gate.mode === "incremental" ? [gate.inScope.filter((m) => m.verifiedAt), gate.inScope.filter((m) => !m.verifiedAt)] : [gate.inScope];
|
|
12247
|
+
for (const group of groups) {
|
|
12248
|
+
for (let i = 0;i < group.length; i += VERIFY_BATCH_SIZE) {
|
|
12249
|
+
batches.push(group.slice(i, i + VERIFY_BATCH_SIZE));
|
|
12250
|
+
}
|
|
11743
12251
|
}
|
|
11744
12252
|
const abortController = new AbortController;
|
|
11745
12253
|
const heartbeat = startLeaseHeartbeat(args.db, args.holderId, args.leaseKey, (reason) => abortController.abort(new Error(reason)), args.leaseAcquisition);
|
|
@@ -11803,7 +12311,9 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
11803
12311
|
let promptSettled = false;
|
|
11804
12312
|
const startedAt = Date.now();
|
|
11805
12313
|
try {
|
|
11806
|
-
const
|
|
12314
|
+
const headAtPrompt = await readGitHead(args.sessionDirectory);
|
|
12315
|
+
const evidence = !args.forceBroad && batch.every((m) => m.verifiedAt) ? await buildVerifyDiffEvidence(args.sessionDirectory, batch) : null;
|
|
12316
|
+
const prompt = buildVerifyPrompt(args.projectIdentity, batch, evidence);
|
|
11807
12317
|
if (args.hiddenCompletionExecutor) {
|
|
11808
12318
|
const run = await runHiddenSingleShotPrompt({
|
|
11809
12319
|
executor: args.hiddenCompletionExecutor,
|
|
@@ -11814,6 +12324,7 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
11814
12324
|
prompt,
|
|
11815
12325
|
title: "magic-context-dream-verify",
|
|
11816
12326
|
callContext: "dreamer:verify",
|
|
12327
|
+
metadata: { tokenBudget: args.tokenBudget, onBudgetUpdate: args.onBudgetUpdate },
|
|
11817
12328
|
model: args.model,
|
|
11818
12329
|
fallbackModels: args.fallbackModels,
|
|
11819
12330
|
language: args.language,
|
|
@@ -11830,7 +12341,7 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
11830
12341
|
status: "completed",
|
|
11831
12342
|
messages: run.completion.messages ?? []
|
|
11832
12343
|
});
|
|
11833
|
-
return applyParsedVerifyManifest(args, batch, run.validated);
|
|
12344
|
+
return applyParsedVerifyManifest(args, batch, run.validated, evidence?.head ?? headAtPrompt);
|
|
11834
12345
|
}
|
|
11835
12346
|
const client = args.client;
|
|
11836
12347
|
if (!client)
|
|
@@ -11858,7 +12369,10 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
11858
12369
|
parts: [{ type: "text", text: prompt, synthetic: true }]
|
|
11859
12370
|
}
|
|
11860
12371
|
}, {
|
|
11861
|
-
transport: createPromptAsyncTransport(client, agentSessionId
|
|
12372
|
+
transport: createPromptAsyncTransport(client, agentSessionId, {
|
|
12373
|
+
tokenBudget: args.tokenBudget,
|
|
12374
|
+
onBudgetUpdate: args.onBudgetUpdate
|
|
12375
|
+
}),
|
|
11862
12376
|
timeoutMs: sliceMs,
|
|
11863
12377
|
signal,
|
|
11864
12378
|
fallbackModels: args.fallbackModels,
|
|
@@ -11887,7 +12401,7 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
11887
12401
|
});
|
|
11888
12402
|
promptSettled = true;
|
|
11889
12403
|
recordInvocation4(args, startedAt, { status: "completed", messages: run.output });
|
|
11890
|
-
return await applyParsedVerifyManifest(args, batch, run.validated);
|
|
12404
|
+
return await applyParsedVerifyManifest(args, batch, run.validated, evidence?.head ?? headAtPrompt);
|
|
11891
12405
|
} catch (error) {
|
|
11892
12406
|
const desc = describeError(error);
|
|
11893
12407
|
const providerFailure = error instanceof DreamerProviderOutputFailureError ? error : undefined;
|
|
@@ -11898,7 +12412,7 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
11898
12412
|
error,
|
|
11899
12413
|
messages: await readFailedChildMessages(args.client, agentSessionId, args.sessionDirectory)
|
|
11900
12414
|
});
|
|
11901
|
-
if (error instanceof DreamerModuleFailureError || signal.aborted)
|
|
12415
|
+
if (error instanceof DreamerModuleFailureError || error instanceof DreamTokenBudgetExceeded || signal.aborted)
|
|
11902
12416
|
throw error;
|
|
11903
12417
|
if (promptFailure?.failureClass === "provider_timeout" || isPromptTimeoutError(error)) {
|
|
11904
12418
|
return {
|
|
@@ -11933,7 +12447,7 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
11933
12447
|
});
|
|
11934
12448
|
}
|
|
11935
12449
|
}
|
|
11936
|
-
async function applyParsedVerifyManifest(args, batch, parsed) {
|
|
12450
|
+
async function applyParsedVerifyManifest(args, batch, parsed, headAtPrompt) {
|
|
11937
12451
|
const batchIds = new Set(batch.map((m) => m.id));
|
|
11938
12452
|
const batchById = new Map(batch.map((memory) => [memory.id, memory]));
|
|
11939
12453
|
const valid = {
|
|
@@ -11960,6 +12474,7 @@ async function applyParsedVerifyManifest(args, batch, parsed) {
|
|
|
11960
12474
|
return { verified: 0, updated: 0, archived: 0, skipped: 0, refused: 0 };
|
|
11961
12475
|
}
|
|
11962
12476
|
const now = Date.now();
|
|
12477
|
+
const verifiedHead = headAtPrompt === undefined ? await readGitHead(args.sessionDirectory) : headAtPrompt;
|
|
11963
12478
|
const writes = [];
|
|
11964
12479
|
let refused = 0;
|
|
11965
12480
|
const skipped = valid.skipped.length;
|
|
@@ -12071,6 +12586,8 @@ async function applyParsedVerifyManifest(args, batch, parsed) {
|
|
|
12071
12586
|
continue;
|
|
12072
12587
|
if (w.kind === "verify") {
|
|
12073
12588
|
recordMemoryVerifications(args.db, w.id, w.files, now);
|
|
12589
|
+
if (verifiedHead)
|
|
12590
|
+
recordVerifiedCommit(args.db, memory, now, verifiedHead);
|
|
12074
12591
|
verified += 1;
|
|
12075
12592
|
} else if (w.kind === "update") {
|
|
12076
12593
|
rewriteMemoryContent(args.db, memory, w.content, w.hash);
|
|
@@ -12095,6 +12612,15 @@ async function applyParsedVerifyManifest(args, batch, parsed) {
|
|
|
12095
12612
|
});
|
|
12096
12613
|
return { verified, updated, archived, skipped, refused };
|
|
12097
12614
|
}
|
|
12615
|
+
function recordVerifiedCommit(db, memory, at, head) {
|
|
12616
|
+
let metadata = {};
|
|
12617
|
+
try {
|
|
12618
|
+
const parsed = JSON.parse(memory.metadataJson ?? "{}");
|
|
12619
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
12620
|
+
metadata = parsed;
|
|
12621
|
+
} catch {}
|
|
12622
|
+
db.prepare("UPDATE memories SET metadata_json = ? WHERE id = ?").run(JSON.stringify({ ...metadata, dreamerVerifiedAt: at, dreamerVerifiedCommit: head }), memory.id);
|
|
12623
|
+
}
|
|
12098
12624
|
async function normalizeFiles(args, rawFiles) {
|
|
12099
12625
|
if (rawFiles.length === 0)
|
|
12100
12626
|
return [];
|
|
@@ -12152,6 +12678,16 @@ function dreamRunFailureDetail(error) {
|
|
|
12152
12678
|
}
|
|
12153
12679
|
const described = describeError(error);
|
|
12154
12680
|
const message = described.brief;
|
|
12681
|
+
if (error instanceof DreamTokenBudgetExceeded) {
|
|
12682
|
+
return {
|
|
12683
|
+
failure_class: "token_budget",
|
|
12684
|
+
model_attempted: null,
|
|
12685
|
+
models_tried: [],
|
|
12686
|
+
provider_error: null,
|
|
12687
|
+
timeout_ms: null,
|
|
12688
|
+
child_session_id: error.sessionId
|
|
12689
|
+
};
|
|
12690
|
+
}
|
|
12155
12691
|
if (error instanceof Error && error.name === "HiddenAgentStepLimit") {
|
|
12156
12692
|
return {
|
|
12157
12693
|
failure_class: "step_limit",
|
|
@@ -12196,7 +12732,10 @@ function toCuratePromptMemory(memory, verificationById) {
|
|
|
12196
12732
|
category: memory.category,
|
|
12197
12733
|
content: memory.content,
|
|
12198
12734
|
mappedFiles: verification?.files ?? [],
|
|
12199
|
-
hasNoFileSentinel: verification?.hasSentinel ?? false
|
|
12735
|
+
hasNoFileSentinel: verification?.hasSentinel ?? false,
|
|
12736
|
+
importance: memory.importance,
|
|
12737
|
+
retrievalCount: memory.retrievalCount,
|
|
12738
|
+
seenCount: memory.seenCount
|
|
12200
12739
|
};
|
|
12201
12740
|
}
|
|
12202
12741
|
function loadActiveMemoryPromptMemories(db, projectIdentity) {
|
|
@@ -12236,7 +12775,10 @@ function inspectCurateMemoryOperations(messages) {
|
|
|
12236
12775
|
if (!isRecord(part.state) || part.state.status !== "completed")
|
|
12237
12776
|
continue;
|
|
12238
12777
|
const input = isRecord(part.state.input) ? part.state.input : null;
|
|
12239
|
-
|
|
12778
|
+
const action = input?.action;
|
|
12779
|
+
const output = typeof part.state.output === "string" ? part.state.output : "";
|
|
12780
|
+
if (output === "completed" && (action === "merge" || action === "archive" || action === "update") || action === "merge" && output.startsWith("Merged memories") || action === "archive" && output.startsWith("Archived memor") || action === "update" && output.startsWith("Updated memory"))
|
|
12781
|
+
summary.completedActions.push(action);
|
|
12240
12782
|
}
|
|
12241
12783
|
}
|
|
12242
12784
|
return summary;
|
|
@@ -12284,6 +12826,7 @@ function createDreamTaskExecutor(deps) {
|
|
|
12284
12826
|
};
|
|
12285
12827
|
return async (config, ctx) => {
|
|
12286
12828
|
const { db, projectIdentity, holderId, leaseKey } = ctx;
|
|
12829
|
+
deps.client?.resetDreamTokenBudget?.(config.task);
|
|
12287
12830
|
const startedAt = Date.now();
|
|
12288
12831
|
const leaseAcquisition = ctx.leaseAcquisition ?? acquireLeaseWithAcquisition(db, holderId, leaseKey) ?? (() => {
|
|
12289
12832
|
throw new Error("Dream lease unavailable during executor setup");
|
|
@@ -12342,8 +12885,23 @@ function createDreamTaskExecutor(deps) {
|
|
|
12342
12885
|
if (!leaseOwnershipMatches(db, holderId, leaseAcquisition.generation, leaseKey)) {
|
|
12343
12886
|
throw new Error("Dream lease lost during executor setup");
|
|
12344
12887
|
}
|
|
12888
|
+
let budgetSpent = 0;
|
|
12889
|
+
let budgetFinalized = false;
|
|
12890
|
+
const childSpending = new Map;
|
|
12891
|
+
const onBudgetUpdate = (state) => {
|
|
12892
|
+
if (state.sessionId) {
|
|
12893
|
+
const previous = childSpending.get(state.sessionId) ?? 0;
|
|
12894
|
+
budgetSpent += Math.max(0, state.spent - previous);
|
|
12895
|
+
childSpending.set(state.sessionId, state.spent);
|
|
12896
|
+
} else
|
|
12897
|
+
budgetSpent = Math.max(budgetSpent, state.spent);
|
|
12898
|
+
budgetFinalized ||= state.finalizeFired;
|
|
12899
|
+
};
|
|
12345
12900
|
const recordRun = (status, error, extra) => {
|
|
12346
12901
|
try {
|
|
12902
|
+
const hostBudget = deps.readTokenBudget?.(config.task) ?? deps.client?.readTokenBudget?.(config.task);
|
|
12903
|
+
if (hostBudget)
|
|
12904
|
+
onBudgetUpdate(hostBudget);
|
|
12347
12905
|
insertDreamRun(db, {
|
|
12348
12906
|
projectPath: projectIdentity,
|
|
12349
12907
|
startedAt,
|
|
@@ -12359,6 +12917,14 @@ function createDreamTaskExecutor(deps) {
|
|
|
12359
12917
|
failure: extra?.failure ?? dreamRunFailureDetail(error ?? "unknown dreamer failure")
|
|
12360
12918
|
} : {},
|
|
12361
12919
|
...extra?.progress ? { progress: extra.progress } : {},
|
|
12920
|
+
...config.tokenBudget ? {
|
|
12921
|
+
tokenBudget: {
|
|
12922
|
+
budget: config.tokenBudget,
|
|
12923
|
+
spent: budgetSpent,
|
|
12924
|
+
finalizeFired: budgetFinalized,
|
|
12925
|
+
banked: extra?.banked ?? processedDreamTaskItems(backlogAtStart.pending, (extra?.backlogAfter ?? getDreamTaskBacklog(db, projectIdentity, config.task)).pending)
|
|
12926
|
+
}
|
|
12927
|
+
} : {},
|
|
12362
12928
|
backlog: (() => {
|
|
12363
12929
|
const end = extra?.backlogAfter ?? getDreamTaskBacklog(db, projectIdentity, config.task);
|
|
12364
12930
|
const processed = processedDreamTaskItems(backlogAtStart.pending, end.pending);
|
|
@@ -12449,7 +13015,7 @@ function createDreamTaskExecutor(deps) {
|
|
|
12449
13015
|
leaseKey,
|
|
12450
13016
|
deadline,
|
|
12451
13017
|
leaseAcquisition,
|
|
12452
|
-
promotionThreshold: config.promotionThreshold ??
|
|
13018
|
+
promotionThreshold: config.promotionThreshold ?? DREAM_TASK_PROMOTION_DEFAULTS["review-user-memories"],
|
|
12453
13019
|
model: config.model,
|
|
12454
13020
|
fallbackModels: config.fallbackModels,
|
|
12455
13021
|
language: config.language ?? deps.language
|
|
@@ -12473,6 +13039,8 @@ function createDreamTaskExecutor(deps) {
|
|
|
12473
13039
|
model: config.model,
|
|
12474
13040
|
fallbackModels: config.fallbackModels,
|
|
12475
13041
|
moduleRoute,
|
|
13042
|
+
tokenBudget: config.tokenBudget,
|
|
13043
|
+
onBudgetUpdate,
|
|
12476
13044
|
onProgress: (processed) => reportProgress(processed)
|
|
12477
13045
|
});
|
|
12478
13046
|
log(`[dreamer] map-memories: committed=${result.mapped + result.independent} mapped=${result.mapped} independent=${result.independent} batches=${result.batches} remaining=${result.remaining} complete=${result.complete}${result.stopReason ? ` stop_reason=${result.stopReason}` : ""}`);
|
|
@@ -12513,6 +13081,8 @@ function createDreamTaskExecutor(deps) {
|
|
|
12513
13081
|
fallbackModels: config.fallbackModels,
|
|
12514
13082
|
language: config.language ?? deps.language,
|
|
12515
13083
|
moduleRoute,
|
|
13084
|
+
tokenBudget: config.tokenBudget,
|
|
13085
|
+
onBudgetUpdate,
|
|
12516
13086
|
onProgress: (processed, refused) => reportProgress(processed, refused)
|
|
12517
13087
|
});
|
|
12518
13088
|
const processed = result.verified + result.updated + result.archived + result.skipped + result.refused;
|
|
@@ -12524,7 +13094,8 @@ function createDreamTaskExecutor(deps) {
|
|
|
12524
13094
|
recordRun("completed", null, {
|
|
12525
13095
|
progress: broadProgress,
|
|
12526
13096
|
memoryChanges: computeMemoryDelta(memoryBefore),
|
|
12527
|
-
backlogAfter
|
|
13097
|
+
backlogAfter,
|
|
13098
|
+
banked: result.verified + result.updated + result.archived
|
|
12528
13099
|
});
|
|
12529
13100
|
return { status: "completed" };
|
|
12530
13101
|
}
|
|
@@ -12533,14 +13104,16 @@ function createDreamTaskExecutor(deps) {
|
|
|
12533
13104
|
recordRun("failed", error, {
|
|
12534
13105
|
progress: verificationProgress,
|
|
12535
13106
|
memoryChanges: computeMemoryDelta(memoryBefore),
|
|
12536
|
-
backlogAfter
|
|
13107
|
+
backlogAfter,
|
|
13108
|
+
banked: result.verified + result.updated + result.archived
|
|
12537
13109
|
});
|
|
12538
13110
|
return { status: "failed", transient: !batchTimedOut, error };
|
|
12539
13111
|
}
|
|
12540
13112
|
recordRun("completed", null, {
|
|
12541
13113
|
progress: verificationProgress,
|
|
12542
13114
|
memoryChanges: computeMemoryDelta(memoryBefore),
|
|
12543
|
-
backlogAfter
|
|
13115
|
+
backlogAfter,
|
|
13116
|
+
banked: result.verified + result.updated + result.archived
|
|
12544
13117
|
});
|
|
12545
13118
|
return { status: "completed" };
|
|
12546
13119
|
}
|
|
@@ -12592,7 +13165,7 @@ function createDreamTaskExecutor(deps) {
|
|
|
12592
13165
|
leaseKey,
|
|
12593
13166
|
deadline,
|
|
12594
13167
|
leaseAcquisition,
|
|
12595
|
-
promotionThreshold: config.promotionThreshold ??
|
|
13168
|
+
promotionThreshold: config.promotionThreshold ?? DREAM_TASK_PROMOTION_DEFAULTS["promote-primers"],
|
|
12596
13169
|
ensureProjectRegistered: deps.ensureProjectRegistered
|
|
12597
13170
|
});
|
|
12598
13171
|
recordRun("completed", null);
|
|
@@ -12615,6 +13188,8 @@ function createDreamTaskExecutor(deps) {
|
|
|
12615
13188
|
fallbackModels: config.fallbackModels,
|
|
12616
13189
|
language: config.language ?? deps.language,
|
|
12617
13190
|
rawProviderFactory: deps.primerRawProviderFactory,
|
|
13191
|
+
tokenBudget: config.tokenBudget,
|
|
13192
|
+
onBudgetUpdate,
|
|
12618
13193
|
onProgress: (processed) => reportProgress(processed)
|
|
12619
13194
|
});
|
|
12620
13195
|
recordRun("completed", null);
|
|
@@ -12645,6 +13220,7 @@ function createDreamTaskExecutor(deps) {
|
|
|
12645
13220
|
if (config.task === "retrospective") {
|
|
12646
13221
|
const memoryBefore = getMemoryCountsByStatus(db, projectIdentity);
|
|
12647
13222
|
const retro = await runRetrospectiveTask(config, ctx, {
|
|
13223
|
+
onBudgetUpdate,
|
|
12648
13224
|
deps,
|
|
12649
13225
|
deadline,
|
|
12650
13226
|
parent,
|
|
@@ -12652,7 +13228,10 @@ function createDreamTaskExecutor(deps) {
|
|
|
12652
13228
|
moduleRoute
|
|
12653
13229
|
});
|
|
12654
13230
|
recordRun("completed", null, {
|
|
12655
|
-
memoryChanges: computeMemoryDelta(memoryBefore)
|
|
13231
|
+
memoryChanges: computeMemoryDelta(memoryBefore),
|
|
13232
|
+
backlogAfter: retro.retrospectiveWatermarkMs == null ? undefined : getDreamTaskBacklog(db, projectIdentity, "retrospective", {
|
|
13233
|
+
retrospectiveWatermarkMs: retro.retrospectiveWatermarkMs
|
|
13234
|
+
})
|
|
12656
13235
|
});
|
|
12657
13236
|
return {
|
|
12658
13237
|
status: "completed",
|
|
@@ -12665,6 +13244,7 @@ function createDreamTaskExecutor(deps) {
|
|
|
12665
13244
|
};
|
|
12666
13245
|
}
|
|
12667
13246
|
return await runAgenticTask(config, ctx, {
|
|
13247
|
+
onBudgetUpdate,
|
|
12668
13248
|
deps,
|
|
12669
13249
|
deadline,
|
|
12670
13250
|
parent,
|
|
@@ -12675,6 +13255,9 @@ function createDreamTaskExecutor(deps) {
|
|
|
12675
13255
|
moduleRoute
|
|
12676
13256
|
});
|
|
12677
13257
|
} catch (error) {
|
|
13258
|
+
if (error instanceof DreamTokenBudgetExceeded) {
|
|
13259
|
+
budgetSpent = Math.max(budgetSpent, error.spent);
|
|
13260
|
+
}
|
|
12678
13261
|
const classified = classifyFailure(error);
|
|
12679
13262
|
const retrospectiveOverflow = error instanceof RetrospectivePromptOverflowError ? error : null;
|
|
12680
13263
|
const transient = retrospectiveOverflow ? true : classified.transient;
|
|
@@ -12833,7 +13416,7 @@ function resolveRetrospectiveUsableInputTokens(config, deps) {
|
|
|
12833
13416
|
}
|
|
12834
13417
|
async function runRetrospectiveTask(config, ctx, helpers) {
|
|
12835
13418
|
const { db, projectIdentity, holderId, leaseKey } = ctx;
|
|
12836
|
-
const { deps, deadline, parent } = helpers;
|
|
13419
|
+
const { deps, deadline, parent, onBudgetUpdate } = helpers;
|
|
12837
13420
|
const provider = resolveRetrospectiveProvider(deps, db, projectIdentity);
|
|
12838
13421
|
if (!provider) {
|
|
12839
13422
|
log("[dreamer] retrospective: no raw provider available — clean no-op");
|
|
@@ -12887,7 +13470,8 @@ async function runRetrospectiveTask(config, ctx, helpers) {
|
|
|
12887
13470
|
],
|
|
12888
13471
|
timeoutMs: Math.max(1, deadline - Date.now()),
|
|
12889
13472
|
title: "magic-context-dream-retrospective",
|
|
12890
|
-
directory: deps.sessionDirectory
|
|
13473
|
+
directory: deps.sessionDirectory,
|
|
13474
|
+
metadata: { tokenBudget: config.tokenBudget, onBudgetUpdate }
|
|
12891
13475
|
});
|
|
12892
13476
|
childSessionId = hiddenHandle.id;
|
|
12893
13477
|
} else {
|
|
@@ -12923,6 +13507,12 @@ async function runRetrospectiveTask(config, ctx, helpers) {
|
|
|
12923
13507
|
signal: abortController.signal,
|
|
12924
13508
|
fallbackModels: config.fallbackModels,
|
|
12925
13509
|
callContext: "dreamer:retrospective",
|
|
13510
|
+
...!hiddenHandle && deps.client ? {
|
|
13511
|
+
transport: createPromptAsyncTransport(deps.client, sessionId, {
|
|
13512
|
+
tokenBudget: config.tokenBudget,
|
|
13513
|
+
onBudgetUpdate
|
|
13514
|
+
})
|
|
13515
|
+
} : {},
|
|
12926
13516
|
...hiddenHandle && deps.hiddenCompletionExecutor ? {
|
|
12927
13517
|
transport: Object.assign((request) => deps.hiddenCompletionExecutor?.attempt(hiddenHandle, request) ?? Promise.reject(new Error("Hidden transport unavailable")), { childSessionId: hiddenHandle.childSessionId })
|
|
12928
13518
|
} : {},
|
|
@@ -13098,7 +13688,7 @@ async function runRetrospectiveTask(config, ctx, helpers) {
|
|
|
13098
13688
|
}
|
|
13099
13689
|
async function runAgenticTask(config, ctx, helpers) {
|
|
13100
13690
|
const { db, projectIdentity, holderId, leaseKey } = ctx;
|
|
13101
|
-
const { deps, deadline, parent } = helpers;
|
|
13691
|
+
const { deps, deadline, parent, onBudgetUpdate } = helpers;
|
|
13102
13692
|
const task = config.task;
|
|
13103
13693
|
const docsDir = deps.sessionDirectory;
|
|
13104
13694
|
const invocationStartedAt = Date.now();
|
|
@@ -13152,122 +13742,178 @@ async function runAgenticTask(config, ctx, helpers) {
|
|
|
13152
13742
|
return { status: "completed", detail: progress };
|
|
13153
13743
|
}
|
|
13154
13744
|
}
|
|
13155
|
-
if (task === "maintain-docs" &&
|
|
13156
|
-
const reason =
|
|
13157
|
-
log(`[dreamer] maintain-docs skipped: ${reason}`);
|
|
13745
|
+
if (task === "maintain-docs" && !changes) {
|
|
13746
|
+
const reason = "no relevant commits or no git repository";
|
|
13158
13747
|
helpers.recordRun("completed", null, { progress: reason });
|
|
13159
13748
|
return { status: "completed", detail: reason };
|
|
13160
13749
|
}
|
|
13161
|
-
|
|
13162
|
-
|
|
13163
|
-
|
|
13164
|
-
|
|
13165
|
-
|
|
13166
|
-
|
|
13167
|
-
|
|
13168
|
-
|
|
13169
|
-
}
|
|
13170
|
-
|
|
13171
|
-
|
|
13172
|
-
|
|
13173
|
-
|
|
13174
|
-
|
|
13175
|
-
|
|
13176
|
-
|
|
13177
|
-
|
|
13178
|
-
|
|
13179
|
-
|
|
13180
|
-
|
|
13181
|
-
|
|
13182
|
-
|
|
13183
|
-
|
|
13184
|
-
|
|
13185
|
-
|
|
13186
|
-
|
|
13187
|
-
|
|
13188
|
-
|
|
13189
|
-
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
|
|
13193
|
-
|
|
13194
|
-
|
|
13195
|
-
|
|
13196
|
-
|
|
13197
|
-
|
|
13198
|
-
|
|
13199
|
-
|
|
13200
|
-
|
|
13201
|
-
|
|
13202
|
-
|
|
13203
|
-
|
|
13204
|
-
const createResponse = await createChildSessionWithFence({
|
|
13205
|
-
client: requireDreamClient(deps.client),
|
|
13206
|
-
db,
|
|
13207
|
-
parentSessionId: parent ?? undefined,
|
|
13208
|
-
title: `magic-context-dream-${task}`,
|
|
13209
|
-
directory: docsDir
|
|
13210
|
-
});
|
|
13211
|
-
const created = normalizeSDKResponse(createResponse, null, {
|
|
13212
|
-
preferResponseOnMissingData: true
|
|
13750
|
+
if (task === "maintain-docs" && changes && (changes.unchanged || !changes.relevant)) {
|
|
13751
|
+
const reason = changes.unchanged ? "no commits since last run" : "diff does not describe a documented area";
|
|
13752
|
+
helpers.recordRun("completed", null, { progress: reason });
|
|
13753
|
+
return {
|
|
13754
|
+
status: "completed",
|
|
13755
|
+
detail: reason,
|
|
13756
|
+
schedulePatch: { taskStateJson: JSON.stringify({ head: changes.head }) }
|
|
13757
|
+
};
|
|
13758
|
+
}
|
|
13759
|
+
if (task === "maintain-docs" && hasCurrentDocsProposal(docsDir)) {
|
|
13760
|
+
const reason = "pending proposal matches current docs";
|
|
13761
|
+
helpers.recordRun("completed", null, { progress: reason });
|
|
13762
|
+
return {
|
|
13763
|
+
status: "completed",
|
|
13764
|
+
detail: reason,
|
|
13765
|
+
...changes ? { schedulePatch: { taskStateJson: JSON.stringify({ head: changes.head }) } } : {}
|
|
13766
|
+
};
|
|
13767
|
+
}
|
|
13768
|
+
const curateChunks = curateMemories ? chunkCurateMemories(curateMemories, Math.max(4000, Math.floor(resolveRetrospectiveUsableInputTokens(config, deps) * 2))) : [];
|
|
13769
|
+
const batches = task === "curate" ? curateChunks : [{ memories: [], crossChunkCandidates: [] }];
|
|
13770
|
+
const completedActions = [];
|
|
13771
|
+
let curateRefused = 0;
|
|
13772
|
+
let run = {
|
|
13773
|
+
output: [],
|
|
13774
|
+
validated: ""
|
|
13775
|
+
};
|
|
13776
|
+
for (const batch of batches) {
|
|
13777
|
+
const taskPrompt = buildDreamTaskPrompt(task, {
|
|
13778
|
+
projectPath: projectIdentity,
|
|
13779
|
+
lastDreamAt: lastRunAt ? String(lastRunAt) : null,
|
|
13780
|
+
existingDocs,
|
|
13781
|
+
docsChangeSet: changes?.text,
|
|
13782
|
+
docsContents: task === "maintain-docs" ? {
|
|
13783
|
+
architecture: existingDocs?.architecture ? readFileSync5(`${docsDir}/ARCHITECTURE.md`, "utf8") : "(missing)",
|
|
13784
|
+
structure: existingDocs?.structure ? readFileSync5(`${docsDir}/STRUCTURE.md`, "utf8") : "(missing)"
|
|
13785
|
+
} : undefined,
|
|
13786
|
+
docsBudget: config.docsMaxTokens ?? 12000,
|
|
13787
|
+
docsCurrentTokens: maintainDocsSnapshot ? Math.ceil([...maintainDocsSnapshot.values()].join("").length / 3.5) : 0,
|
|
13788
|
+
curate: curateMemories && curateCategory ? {
|
|
13789
|
+
category: curateCategory,
|
|
13790
|
+
memories: batch.memories,
|
|
13791
|
+
crossChunkCandidates: batch.crossChunkCandidates
|
|
13792
|
+
} : undefined
|
|
13213
13793
|
});
|
|
13214
|
-
|
|
13215
|
-
|
|
13216
|
-
|
|
13217
|
-
|
|
13218
|
-
|
|
13219
|
-
run = await promptSyncWithValidatedOutputRetry(requireDreamClient(deps.client), {
|
|
13220
|
-
path: { id: sessionId },
|
|
13221
|
-
query: { directory: docsDir },
|
|
13222
|
-
body: {
|
|
13794
|
+
if (deps.hiddenCompletionExecutor) {
|
|
13795
|
+
const hidden = await runHiddenSingleShotPrompt({
|
|
13796
|
+
executor: deps.hiddenCompletionExecutor,
|
|
13797
|
+
parentSessionId: parent ?? undefined,
|
|
13798
|
+
sessionDirectory: docsDir,
|
|
13223
13799
|
agent: task === "maintain-docs" ? DREAMER_DOCS_AGENT : DREAMER_AGENT,
|
|
13224
|
-
system: task === "maintain-docs" ? MAINTAIN_DOCS_SYSTEM_PROMPT :
|
|
13225
|
-
|
|
13226
|
-
|
|
13227
|
-
|
|
13228
|
-
|
|
13229
|
-
|
|
13230
|
-
|
|
13231
|
-
|
|
13232
|
-
|
|
13233
|
-
|
|
13234
|
-
|
|
13235
|
-
|
|
13236
|
-
|
|
13237
|
-
|
|
13238
|
-
|
|
13800
|
+
system: task === "maintain-docs" ? MAINTAIN_DOCS_SYSTEM_PROMPT : CURATE_SYSTEM_PROMPT,
|
|
13801
|
+
prompt: taskPrompt,
|
|
13802
|
+
title: `magic-context-dream-${task}`,
|
|
13803
|
+
callContext: `dreamer:${task}`,
|
|
13804
|
+
metadata: { tokenBudget: config.tokenBudget, onBudgetUpdate },
|
|
13805
|
+
allowEmpty: task === "curate",
|
|
13806
|
+
model: config.model,
|
|
13807
|
+
fallbackModels: config.fallbackModels,
|
|
13808
|
+
language: task === "curate" ? config.language ?? deps.language : undefined,
|
|
13809
|
+
timeoutMs: Math.min(Math.max(0, deadline - Date.now()), config.timeoutMinutes * 60 * 1000),
|
|
13810
|
+
signal: abortController.signal,
|
|
13811
|
+
parse: (text, completion) => {
|
|
13812
|
+
if (task !== "curate")
|
|
13813
|
+
return text;
|
|
13814
|
+
const memoryOperations = inspectCurateMemoryOperations(completion.messages);
|
|
13815
|
+
if (text)
|
|
13816
|
+
validateCurateAssistantText(text);
|
|
13817
|
+
if (memoryOperations.totalCalls > 0 && memoryOperations.completedActions.length === 0)
|
|
13818
|
+
throw new Error("Curate returned no completed ctx_memory tool result.");
|
|
13819
|
+
if (!text && memoryOperations.completedActions.length === 0)
|
|
13820
|
+
throw new Error("Dreamer returned no assistant output.");
|
|
13821
|
+
return { text: text || null, memoryOperations };
|
|
13822
|
+
}
|
|
13823
|
+
});
|
|
13824
|
+
childSessionId = hidden.childSessionId;
|
|
13825
|
+
run = { output: hidden.completion.messages ?? [], validated: hidden.validated };
|
|
13826
|
+
promptSettled = true;
|
|
13827
|
+
} else {
|
|
13828
|
+
const createResponse = await createChildSessionWithFence({
|
|
13829
|
+
client: requireDreamClient(deps.client),
|
|
13830
|
+
db,
|
|
13831
|
+
parentSessionId: parent ?? undefined,
|
|
13832
|
+
title: `magic-context-dream-${task}`,
|
|
13833
|
+
directory: docsDir
|
|
13834
|
+
});
|
|
13835
|
+
const created = normalizeSDKResponse(createResponse, null, {
|
|
13836
|
+
preferResponseOnMissingData: true
|
|
13837
|
+
});
|
|
13838
|
+
childSessionId = typeof created?.id === "string" ? created.id : null;
|
|
13839
|
+
if (!childSessionId)
|
|
13840
|
+
throw new Error("Dreamer could not create its child session.");
|
|
13841
|
+
const sessionId = childSessionId;
|
|
13842
|
+
const remainingMs = Math.max(0, deadline - Date.now());
|
|
13843
|
+
run = await promptSyncWithValidatedOutputRetry(requireDreamClient(deps.client), {
|
|
13844
|
+
path: { id: sessionId },
|
|
13845
|
+
query: { directory: docsDir },
|
|
13846
|
+
body: {
|
|
13847
|
+
agent: task === "maintain-docs" ? DREAMER_DOCS_AGENT : DREAMER_AGENT,
|
|
13848
|
+
system: task === "maintain-docs" ? MAINTAIN_DOCS_SYSTEM_PROMPT : withContentLanguageDirective(CURATE_SYSTEM_PROMPT, config.language ?? deps.language),
|
|
13849
|
+
...modelBodyField(config.model),
|
|
13850
|
+
parts: [{ type: "text", text: taskPrompt, synthetic: true }]
|
|
13851
|
+
}
|
|
13852
|
+
}, {
|
|
13853
|
+
timeoutMs: Math.min(remainingMs, config.timeoutMinutes * 60 * 1000),
|
|
13854
|
+
signal: abortController.signal,
|
|
13855
|
+
fallbackModels: config.fallbackModels,
|
|
13856
|
+
callContext: `dreamer:${task}`,
|
|
13857
|
+
transport: createPromptAsyncTransport(requireDreamClient(deps.client), sessionId, { tokenBudget: config.tokenBudget, onBudgetUpdate }),
|
|
13858
|
+
fetchOutput: async () => {
|
|
13859
|
+
const messagesResponse = await requireDreamClient(deps.client).session.messages({
|
|
13860
|
+
path: { id: sessionId },
|
|
13861
|
+
query: {
|
|
13862
|
+
directory: docsDir,
|
|
13863
|
+
...task === "curate" ? {} : { limit: 50 }
|
|
13864
|
+
}
|
|
13865
|
+
});
|
|
13866
|
+
return normalizeSDKResponse(messagesResponse, [], {
|
|
13867
|
+
preferResponseOnMissingData: true
|
|
13868
|
+
});
|
|
13869
|
+
},
|
|
13870
|
+
validateOutput: (messages) => {
|
|
13871
|
+
const text = extractLatestAssistantText(messages);
|
|
13872
|
+
if (task !== "curate") {
|
|
13873
|
+
if (!text)
|
|
13874
|
+
throw new Error("Dreamer returned no assistant output.");
|
|
13875
|
+
return text;
|
|
13876
|
+
}
|
|
13877
|
+
const memoryOperations = inspectCurateMemoryOperations(messages);
|
|
13878
|
+
if (text)
|
|
13879
|
+
validateCurateAssistantText(text);
|
|
13880
|
+
if (memoryOperations.completedActions.length > 0) {
|
|
13881
|
+
return { text, memoryOperations };
|
|
13239
13882
|
}
|
|
13240
|
-
});
|
|
13241
|
-
return normalizeSDKResponse(messagesResponse, [], {
|
|
13242
|
-
preferResponseOnMissingData: true
|
|
13243
|
-
});
|
|
13244
|
-
},
|
|
13245
|
-
validateOutput: (messages) => {
|
|
13246
|
-
const text = extractLatestAssistantText(messages);
|
|
13247
|
-
if (task !== "curate") {
|
|
13248
13883
|
if (!text)
|
|
13249
13884
|
throw new Error("Dreamer returned no assistant output.");
|
|
13250
|
-
|
|
13251
|
-
|
|
13252
|
-
|
|
13253
|
-
if (text)
|
|
13254
|
-
validateCurateAssistantText(text);
|
|
13255
|
-
if (memoryOperations.completedActions.length > 0) {
|
|
13885
|
+
if (memoryOperations.totalCalls > 0) {
|
|
13886
|
+
throw new Error("Curate returned no completed ctx_memory tool result.");
|
|
13887
|
+
}
|
|
13256
13888
|
return { text, memoryOperations };
|
|
13257
13889
|
}
|
|
13258
|
-
|
|
13259
|
-
|
|
13260
|
-
|
|
13261
|
-
|
|
13262
|
-
|
|
13263
|
-
|
|
13264
|
-
|
|
13265
|
-
|
|
13266
|
-
|
|
13890
|
+
});
|
|
13891
|
+
promptSettled = true;
|
|
13892
|
+
}
|
|
13893
|
+
if (task === "curate") {
|
|
13894
|
+
completedActions.push(...run.validated.memoryOperations.completedActions);
|
|
13895
|
+
curateRefused += takeCurateSafetyRefusalCount(childSessionId ?? "");
|
|
13896
|
+
}
|
|
13897
|
+
if (childSessionId && !deps.hiddenCompletionExecutor) {
|
|
13898
|
+
await teardownChildSession({
|
|
13899
|
+
client: requireDreamClient(deps.client),
|
|
13900
|
+
sessionId: childSessionId,
|
|
13901
|
+
sessionDirectory: docsDir,
|
|
13902
|
+
promptSettled,
|
|
13903
|
+
privacySensitive: true,
|
|
13904
|
+
context: `[dreamer] ${task}`,
|
|
13905
|
+
log
|
|
13906
|
+
});
|
|
13907
|
+
childSessionId = null;
|
|
13908
|
+
}
|
|
13267
13909
|
}
|
|
13910
|
+
if (task === "curate")
|
|
13911
|
+
run.validated = {
|
|
13912
|
+
text: null,
|
|
13913
|
+
memoryOperations: { totalCalls: completedActions.length, completedActions }
|
|
13914
|
+
};
|
|
13268
13915
|
if (leaseLost)
|
|
13269
13916
|
throw new Error("Dream lease lost during task");
|
|
13270
|
-
const curateRefused = task === "curate" ? takeCurateSafetyRefusalCount(childSessionId ?? "") : 0;
|
|
13271
13917
|
if (curateRefused > 0) {
|
|
13272
13918
|
helpers.reportProgress(curateRefused, curateRefused);
|
|
13273
13919
|
log(`[dreamer] curate safety summary: refused=${curateRefused}`);
|
|
@@ -13288,7 +13934,11 @@ async function runAgenticTask(config, ctx, helpers) {
|
|
|
13288
13934
|
try {
|
|
13289
13935
|
if (run.validated === "[]") {
|
|
13290
13936
|
helpers.recordRun("completed", null, { progress: "no corrections proposed" });
|
|
13291
|
-
return {
|
|
13937
|
+
return {
|
|
13938
|
+
status: "completed",
|
|
13939
|
+
detail: "no corrections proposed",
|
|
13940
|
+
schedulePatch: { taskStateJson: JSON.stringify({ head: changes.head }) }
|
|
13941
|
+
};
|
|
13292
13942
|
}
|
|
13293
13943
|
const path = writeDocsProposal(docsDir, String(run.validated), config.docsMaxTokens ?? 12000, docsHashes, changes.head);
|
|
13294
13944
|
helpers.recordRun("completed", null, { progress: `proposal: ${path}` });
|
|
@@ -13506,7 +14156,7 @@ async function runDomainGroup(deps, group, cb) {
|
|
|
13506
14156
|
projectIdentity,
|
|
13507
14157
|
lastRunAt: readLastRunAt(db, projectIdentity, due.config.task),
|
|
13508
14158
|
retrospectiveWatermarkMs: readRetrospectiveWatermark(db, projectIdentity, due.config.task),
|
|
13509
|
-
promotionThreshold: due.config.promotionThreshold ??
|
|
14159
|
+
promotionThreshold: due.config.promotionThreshold ?? (due.config.task === "promote-primers" ? DREAM_TASK_PROMOTION_DEFAULTS["promote-primers"] : DREAM_TASK_PROMOTION_DEFAULTS["review-user-memories"])
|
|
13510
14160
|
});
|
|
13511
14161
|
if (!gatePass) {
|
|
13512
14162
|
advanceAfterRun(db, projectIdentity, due, Date.now(), "skipped", null);
|
|
@@ -13586,7 +14236,7 @@ async function runManualDream(deps) {
|
|
|
13586
14236
|
projectIdentity: deps.projectIdentity,
|
|
13587
14237
|
lastRunAt: readLastRunAt(deps.db, deps.projectIdentity, d.config.task),
|
|
13588
14238
|
retrospectiveWatermarkMs: readRetrospectiveWatermark(deps.db, deps.projectIdentity, d.config.task),
|
|
13589
|
-
promotionThreshold: d.config.promotionThreshold ??
|
|
14239
|
+
promotionThreshold: d.config.promotionThreshold ?? (d.config.task === "promote-primers" ? DREAM_TASK_PROMOTION_DEFAULTS["promote-primers"] : DREAM_TASK_PROMOTION_DEFAULTS["review-user-memories"])
|
|
13590
14240
|
});
|
|
13591
14241
|
if (pass)
|
|
13592
14242
|
gated.push(d);
|
|
@@ -13642,7 +14292,7 @@ async function runDueTasksForProject(deps) {
|
|
|
13642
14292
|
projectIdentity: deps.projectIdentity,
|
|
13643
14293
|
lastRunAt: readLastRunAt(deps.db, deps.projectIdentity, d.config.task),
|
|
13644
14294
|
retrospectiveWatermarkMs: readRetrospectiveWatermark(deps.db, deps.projectIdentity, d.config.task),
|
|
13645
|
-
promotionThreshold: d.config.promotionThreshold ??
|
|
14295
|
+
promotionThreshold: d.config.promotionThreshold ?? (d.config.task === "promote-primers" ? DREAM_TASK_PROMOTION_DEFAULTS["promote-primers"] : DREAM_TASK_PROMOTION_DEFAULTS["review-user-memories"])
|
|
13646
14296
|
});
|
|
13647
14297
|
if (pass) {
|
|
13648
14298
|
gated.push(d);
|
|
@@ -13846,6 +14496,205 @@ function clearDreamerTickFailure(db) {
|
|
|
13846
14496
|
db.prepare("DELETE FROM schema_migrations_meta WHERE key = ?").run(DREAMER_TICK_FAILURE_KEY);
|
|
13847
14497
|
}
|
|
13848
14498
|
|
|
14499
|
+
// ../plugin/src/hooks/magic-context/lkg-persist.ts
|
|
14500
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
14501
|
+
function parseStringArray(value) {
|
|
14502
|
+
if (typeof value !== "string")
|
|
14503
|
+
return null;
|
|
14504
|
+
let parsed;
|
|
14505
|
+
try {
|
|
14506
|
+
parsed = JSON.parse(value);
|
|
14507
|
+
} catch {
|
|
14508
|
+
return null;
|
|
14509
|
+
}
|
|
14510
|
+
if (!Array.isArray(parsed))
|
|
14511
|
+
return null;
|
|
14512
|
+
const result = [];
|
|
14513
|
+
for (const entry of parsed) {
|
|
14514
|
+
if (typeof entry !== "string" || entry.length === 0)
|
|
14515
|
+
return null;
|
|
14516
|
+
result.push(entry);
|
|
14517
|
+
}
|
|
14518
|
+
return result;
|
|
14519
|
+
}
|
|
14520
|
+
function parseNullableString(value) {
|
|
14521
|
+
if (value === null || value === undefined)
|
|
14522
|
+
return null;
|
|
14523
|
+
return typeof value === "string" ? value : undefined;
|
|
14524
|
+
}
|
|
14525
|
+
function parseNullableInteger(value) {
|
|
14526
|
+
if (value === null || value === undefined)
|
|
14527
|
+
return;
|
|
14528
|
+
return typeof value === "number" && Number.isSafeInteger(value) ? value : undefined;
|
|
14529
|
+
}
|
|
14530
|
+
function parsePersistedLkgSlot(row) {
|
|
14531
|
+
if (!row || typeof row !== "object")
|
|
14532
|
+
return;
|
|
14533
|
+
const record = row;
|
|
14534
|
+
const jsonPrefix = record.json_prefix;
|
|
14535
|
+
const lastInputMessageId = record.last_input_message_id;
|
|
14536
|
+
const capturedAt = record.captured_at;
|
|
14537
|
+
let inputIdsRaw = record.input_id_seq;
|
|
14538
|
+
let piOutputEntryIds;
|
|
14539
|
+
if (typeof inputIdsRaw === "string") {
|
|
14540
|
+
try {
|
|
14541
|
+
const metadata = JSON.parse(inputIdsRaw);
|
|
14542
|
+
if (metadata && !Array.isArray(metadata) && metadata.version === 1 && Array.isArray(metadata.piOutputEntryIds)) {
|
|
14543
|
+
if (!metadata.piOutputEntryIds.every((id) => id === null || typeof id === "string" && id.length > 0))
|
|
14544
|
+
return;
|
|
14545
|
+
piOutputEntryIds = metadata.piOutputEntryIds;
|
|
14546
|
+
inputIdsRaw = JSON.stringify(metadata.inputIds);
|
|
14547
|
+
}
|
|
14548
|
+
} catch {
|
|
14549
|
+
return;
|
|
14550
|
+
}
|
|
14551
|
+
}
|
|
14552
|
+
const inputIdSeq = parseStringArray(inputIdsRaw);
|
|
14553
|
+
const inputContentDigests = parseStringArray(record.input_content_digests);
|
|
14554
|
+
const modelKey = parseNullableString(record.model_key);
|
|
14555
|
+
const providerKey = parseNullableString(record.provider_key);
|
|
14556
|
+
if (typeof jsonPrefix !== "string" || typeof lastInputMessageId !== "string" || lastInputMessageId.length === 0 || typeof capturedAt !== "number" || !Number.isFinite(capturedAt) || inputIdSeq === null || inputContentDigests === null || inputContentDigests.length !== inputIdSeq.length || modelKey === undefined || providerKey === undefined) {
|
|
14557
|
+
return;
|
|
14558
|
+
}
|
|
14559
|
+
if (piOutputEntryIds) {
|
|
14560
|
+
try {
|
|
14561
|
+
const output = JSON.parse(jsonPrefix);
|
|
14562
|
+
const inputs = new Set(inputIdSeq);
|
|
14563
|
+
if (!Array.isArray(output) || output.length !== piOutputEntryIds.length || piOutputEntryIds.some((id) => id !== null && !inputs.has(id)))
|
|
14564
|
+
return;
|
|
14565
|
+
} catch {
|
|
14566
|
+
return;
|
|
14567
|
+
}
|
|
14568
|
+
}
|
|
14569
|
+
let inputContentSignatures;
|
|
14570
|
+
if (record.input_content_signatures !== null && record.input_content_signatures !== undefined) {
|
|
14571
|
+
const parsed = parseStringArray(record.input_content_signatures);
|
|
14572
|
+
if (parsed === null || parsed.length !== inputIdSeq.length)
|
|
14573
|
+
return;
|
|
14574
|
+
inputContentSignatures = parsed;
|
|
14575
|
+
}
|
|
14576
|
+
const slot = {
|
|
14577
|
+
jsonPrefix,
|
|
14578
|
+
inputIdSeq,
|
|
14579
|
+
inputContentDigests,
|
|
14580
|
+
lastInputMessageId,
|
|
14581
|
+
modelKey,
|
|
14582
|
+
providerKey,
|
|
14583
|
+
capturedAt
|
|
14584
|
+
};
|
|
14585
|
+
if (inputContentSignatures)
|
|
14586
|
+
slot.inputContentSignatures = inputContentSignatures;
|
|
14587
|
+
if (piOutputEntryIds)
|
|
14588
|
+
slot.piOutputEntryIds = piOutputEntryIds;
|
|
14589
|
+
const rowVersion = parseNullableInteger(record.row_version);
|
|
14590
|
+
if (rowVersion !== undefined)
|
|
14591
|
+
slot.rowVersion = rowVersion;
|
|
14592
|
+
const captureSequence = parseNullableInteger(record.capture_sequence);
|
|
14593
|
+
if (captureSequence !== undefined)
|
|
14594
|
+
slot.captureSequence = captureSequence;
|
|
14595
|
+
return slot;
|
|
14596
|
+
}
|
|
14597
|
+
var persistedFingerprints = new WeakMap;
|
|
14598
|
+
function saveLkgSlotToDb(db, sessionId, slot) {
|
|
14599
|
+
const fingerprint = createHash11("sha256").update(JSON.stringify([
|
|
14600
|
+
slot.jsonPrefix,
|
|
14601
|
+
slot.inputIdSeq,
|
|
14602
|
+
slot.inputContentDigests,
|
|
14603
|
+
slot.inputContentSignatures,
|
|
14604
|
+
slot.piOutputEntryIds,
|
|
14605
|
+
slot.lastInputMessageId,
|
|
14606
|
+
slot.modelKey,
|
|
14607
|
+
slot.providerKey,
|
|
14608
|
+
slot.rowVersion,
|
|
14609
|
+
slot.captureSequence
|
|
14610
|
+
])).digest("hex");
|
|
14611
|
+
let saved = persistedFingerprints.get(db);
|
|
14612
|
+
if (saved?.get(sessionId) === fingerprint)
|
|
14613
|
+
return true;
|
|
14614
|
+
try {
|
|
14615
|
+
db.prepare(`INSERT INTO lkg_slots (
|
|
14616
|
+
session_id, json_prefix, input_id_seq, input_content_digests,
|
|
14617
|
+
input_content_signatures, last_input_message_id, model_key, provider_key,
|
|
14618
|
+
captured_at, row_version, capture_sequence
|
|
14619
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
14620
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
14621
|
+
json_prefix = excluded.json_prefix,
|
|
14622
|
+
input_id_seq = excluded.input_id_seq,
|
|
14623
|
+
input_content_digests = excluded.input_content_digests,
|
|
14624
|
+
input_content_signatures = excluded.input_content_signatures,
|
|
14625
|
+
last_input_message_id = excluded.last_input_message_id,
|
|
14626
|
+
model_key = excluded.model_key,
|
|
14627
|
+
provider_key = excluded.provider_key,
|
|
14628
|
+
captured_at = excluded.captured_at,
|
|
14629
|
+
row_version = excluded.row_version,
|
|
14630
|
+
capture_sequence = excluded.capture_sequence`).run(sessionId, slot.jsonPrefix, JSON.stringify(slot.piOutputEntryIds ? {
|
|
14631
|
+
version: 1,
|
|
14632
|
+
inputIds: slot.inputIdSeq,
|
|
14633
|
+
piOutputEntryIds: slot.piOutputEntryIds
|
|
14634
|
+
} : slot.inputIdSeq), JSON.stringify(slot.inputContentDigests), slot.inputContentSignatures ? JSON.stringify(slot.inputContentSignatures) : null, slot.lastInputMessageId, slot.modelKey, slot.providerKey, slot.capturedAt, slot.rowVersion ?? null, slot.captureSequence ?? null);
|
|
14635
|
+
if (!saved) {
|
|
14636
|
+
saved = new Map;
|
|
14637
|
+
persistedFingerprints.set(db, saved);
|
|
14638
|
+
}
|
|
14639
|
+
if (saved.size >= 1000) {
|
|
14640
|
+
const oldest = saved.keys().next().value;
|
|
14641
|
+
if (oldest !== undefined)
|
|
14642
|
+
saved.delete(oldest);
|
|
14643
|
+
}
|
|
14644
|
+
saved.set(sessionId, fingerprint);
|
|
14645
|
+
return true;
|
|
14646
|
+
} catch (error) {
|
|
14647
|
+
sessionLog(sessionId, "LKG snapshot persistence failed (in-memory slot retained):", error);
|
|
14648
|
+
return false;
|
|
14649
|
+
}
|
|
14650
|
+
}
|
|
14651
|
+
function clearPersistedLkgSlot(db, sessionId) {
|
|
14652
|
+
try {
|
|
14653
|
+
db.prepare("DELETE FROM lkg_slots WHERE session_id = ?").run(sessionId);
|
|
14654
|
+
persistedFingerprints.get(db)?.delete(sessionId);
|
|
14655
|
+
} catch (error) {
|
|
14656
|
+
sessionLog(sessionId, "LKG snapshot durable clear failed:", error);
|
|
14657
|
+
}
|
|
14658
|
+
}
|
|
14659
|
+
function loadPersistedLkgSlot(db, sessionId) {
|
|
14660
|
+
let row;
|
|
14661
|
+
try {
|
|
14662
|
+
row = db.prepare("SELECT * FROM lkg_slots WHERE session_id = ?").get(sessionId);
|
|
14663
|
+
} catch (error) {
|
|
14664
|
+
sessionLog(sessionId, "LKG snapshot durable load failed:", error);
|
|
14665
|
+
return;
|
|
14666
|
+
}
|
|
14667
|
+
if (!row)
|
|
14668
|
+
return;
|
|
14669
|
+
const slot = parsePersistedLkgSlot(row);
|
|
14670
|
+
if (!slot) {
|
|
14671
|
+
clearPersistedLkgSlot(db, sessionId);
|
|
14672
|
+
return;
|
|
14673
|
+
}
|
|
14674
|
+
return slot;
|
|
14675
|
+
}
|
|
14676
|
+
function pruneStaleLkgSlots(db, now = Date.now()) {
|
|
14677
|
+
const previousTimeout = db.prepare("PRAGMA busy_timeout").get();
|
|
14678
|
+
try {
|
|
14679
|
+
db.exec("PRAGMA busy_timeout = 0");
|
|
14680
|
+
const result = db.prepare(`DELETE FROM lkg_slots WHERE captured_at < ? AND NOT EXISTS (
|
|
14681
|
+
SELECT 1 FROM session_projects sp
|
|
14682
|
+
WHERE sp.session_id = lkg_slots.session_id AND sp.updated_at >= ?
|
|
14683
|
+
)`).run(now - 7 * 24 * 60 * 60 * 1000, now - 7 * 24 * 60 * 60 * 1000);
|
|
14684
|
+
if (result.changes)
|
|
14685
|
+
persistedFingerprints.delete(db);
|
|
14686
|
+
return result.changes;
|
|
14687
|
+
} finally {
|
|
14688
|
+
db.exec(`PRAGMA busy_timeout = ${Number(previousTimeout.timeout) || 0}`);
|
|
14689
|
+
}
|
|
14690
|
+
}
|
|
14691
|
+
function createDbLkgPersistence(db) {
|
|
14692
|
+
return {
|
|
14693
|
+
load: (sessionId) => loadPersistedLkgSlot(db, sessionId),
|
|
14694
|
+
clear: (sessionId) => clearPersistedLkgSlot(db, sessionId)
|
|
14695
|
+
};
|
|
14696
|
+
}
|
|
14697
|
+
|
|
13849
14698
|
// ../plugin/src/plugin/dream-timer.ts
|
|
13850
14699
|
var DREAM_TIMER_INTERVAL_MS = 15 * 60 * 1000;
|
|
13851
14700
|
var GIT_COMMIT_BACKLOG_DRAIN_MAX_MS = 5 * 60 * 1000;
|
|
@@ -14008,6 +14857,11 @@ function persistTickOutcome(db, failure) {
|
|
|
14008
14857
|
}
|
|
14009
14858
|
}
|
|
14010
14859
|
async function runMessageHistoryMaintenance(db) {
|
|
14860
|
+
try {
|
|
14861
|
+
pruneStaleLkgSlots(db);
|
|
14862
|
+
} catch (error) {
|
|
14863
|
+
log("[magic-context] LKG pruning deferred:", error);
|
|
14864
|
+
}
|
|
14011
14865
|
const cleanup = retryPendingSessionCleanups(db);
|
|
14012
14866
|
if (cleanup.cleared > 0 || cleanup.failedSessionIds.length > 0) {
|
|
14013
14867
|
log(`[message-index] pending session cleanup: cleared=${cleanup.cleared} failed=${cleanup.failedSessionIds.length}`);
|
|
@@ -14269,7 +15123,7 @@ import * as childProcess from "node:child_process";
|
|
|
14269
15123
|
import {
|
|
14270
15124
|
existsSync as existsSync7,
|
|
14271
15125
|
mkdtempSync as mkdtempSync2,
|
|
14272
|
-
readFileSync as
|
|
15126
|
+
readFileSync as readFileSync6,
|
|
14273
15127
|
realpathSync as realpathSync3,
|
|
14274
15128
|
rmSync as rmSync2,
|
|
14275
15129
|
statSync as statSync4,
|
|
@@ -14298,7 +15152,7 @@ var DEFAULT_PI_RESOLUTION_FS = {
|
|
|
14298
15152
|
existsSync: existsSync7,
|
|
14299
15153
|
statSync: statSync4,
|
|
14300
15154
|
realpathSync: realpathSync3,
|
|
14301
|
-
readFileSync:
|
|
15155
|
+
readFileSync: readFileSync6
|
|
14302
15156
|
};
|
|
14303
15157
|
function packageHarnessKind(packageName) {
|
|
14304
15158
|
if (packageName === "@oh-my-pi/pi-coding-agent")
|
|
@@ -14595,12 +15449,6 @@ var DREAMER_ACTION_AGENTS = new Set([
|
|
|
14595
15449
|
"dreamer",
|
|
14596
15450
|
"magic-context-dreamer"
|
|
14597
15451
|
]);
|
|
14598
|
-
var HISTORIAN_AGENTS = new Set([
|
|
14599
|
-
"magic-context-historian",
|
|
14600
|
-
"historian",
|
|
14601
|
-
"historian-recomp",
|
|
14602
|
-
"historian-editor"
|
|
14603
|
-
]);
|
|
14604
15452
|
var SEARCH_ONLY_SUBAGENT_TOOL_AGENTS = new Set([
|
|
14605
15453
|
"dreamer-retrospective",
|
|
14606
15454
|
"dreamer-primer-investigator"
|
|
@@ -14790,7 +15638,15 @@ class PiSubagentRunner {
|
|
|
14790
15638
|
async runOnce(options, runMode, modelRefOverride) {
|
|
14791
15639
|
const startTime = Date.now();
|
|
14792
15640
|
let recordedAccounting = false;
|
|
15641
|
+
const cap = subagentStepCap(options.agent);
|
|
15642
|
+
const budget = options.tokenBudget ? createDreamTokenBudget(options.tokenBudget) : undefined;
|
|
15643
|
+
const telemetry = new SubagentTelemetry(options.systemPrompt, (line) => sessionLog(options.accountingSessionId ?? "subagent", line));
|
|
15644
|
+
let loggedTelemetry = false;
|
|
14793
15645
|
const recordAccounting = (result, messages = []) => {
|
|
15646
|
+
if (!loggedTelemetry) {
|
|
15647
|
+
loggedTelemetry = true;
|
|
15648
|
+
telemetry.finish(options.agent, result.ok ? "completed" : result.reason, cap);
|
|
15649
|
+
}
|
|
14794
15650
|
if (!options.accountingSessionId || recordedAccounting)
|
|
14795
15651
|
return;
|
|
14796
15652
|
recordedAccounting = true;
|
|
@@ -14846,7 +15702,9 @@ class PiSubagentRunner {
|
|
|
14846
15702
|
return failBeforeSpawn("spawn_failed", `Magic Context: plugin build is older than its database (database=v${fence.failure.persistedVersion}, supported_fence=v${fence.failure.supportedVersion}) — restart Pi.`);
|
|
14847
15703
|
}
|
|
14848
15704
|
const promptBytes = Buffer.byteLength(options.userMessage, "utf8");
|
|
14849
|
-
const
|
|
15705
|
+
const budgetToolGuardAvailable = SUBAGENT_ENTRY_PATH !== undefined || this.subagentExtensions?.some((extension) => basename(extension) === "subagent-entry.js") === true;
|
|
15706
|
+
const rpcBudget = options.tokenBudget !== undefined && this.invocation.targetHarness === "pi" && budgetToolGuardAvailable;
|
|
15707
|
+
const deliverViaStdin = !rpcBudget && (promptBytes > PROMPT_ARGV_MAX_BYTES || this.platform === "win32");
|
|
14850
15708
|
let systemPromptTempDir;
|
|
14851
15709
|
let systemPromptPath;
|
|
14852
15710
|
const cleanupSystemPromptFile = () => {
|
|
@@ -14869,13 +15727,30 @@ class PiSubagentRunner {
|
|
|
14869
15727
|
return failBeforeSpawn("spawn_failed", `failed to prepare pi system prompt file: ${error instanceof Error ? error.message : String(error)}`);
|
|
14870
15728
|
}
|
|
14871
15729
|
}
|
|
15730
|
+
if (rpcBudget && !systemPromptTempDir) {
|
|
15731
|
+
try {
|
|
15732
|
+
systemPromptTempDir = mkdtempSync2(join5(tmpdir2(), "mc-pi-prompt-"));
|
|
15733
|
+
} catch (error) {
|
|
15734
|
+
return failBeforeSpawn("spawn_failed", `failed to prepare pi budget guard: ${error instanceof Error ? error.message : String(error)}`);
|
|
15735
|
+
}
|
|
15736
|
+
}
|
|
15737
|
+
const budgetGuardFile = rpcBudget && systemPromptTempDir ? join5(systemPromptTempDir, "budget-guard") : undefined;
|
|
15738
|
+
if (budgetGuardFile) {
|
|
15739
|
+
try {
|
|
15740
|
+
writeFileSync5(budgetGuardFile, "", "utf8");
|
|
15741
|
+
} catch (error) {
|
|
15742
|
+
cleanupSystemPromptFile();
|
|
15743
|
+
return failBeforeSpawn("spawn_failed", `failed to write pi budget guard: ${error instanceof Error ? error.message : String(error)}`);
|
|
15744
|
+
}
|
|
15745
|
+
}
|
|
14872
15746
|
const args = buildArgs(options, {
|
|
14873
15747
|
targetHarness: this.invocation.targetHarness,
|
|
14874
15748
|
disableDiscoveredExtensions: runMode.disableDiscoveredExtensions,
|
|
14875
15749
|
subagentExtensions: this.subagentExtensions,
|
|
14876
|
-
omitPositionalMessage: deliverViaStdin,
|
|
15750
|
+
omitPositionalMessage: deliverViaStdin || rpcBudget,
|
|
14877
15751
|
systemPromptPath,
|
|
14878
|
-
modelRef: modelRefOverride
|
|
15752
|
+
modelRef: modelRefOverride,
|
|
15753
|
+
rpc: rpcBudget
|
|
14879
15754
|
});
|
|
14880
15755
|
return new Promise((resolve) => {
|
|
14881
15756
|
let accountingMessages = [];
|
|
@@ -14884,13 +15759,26 @@ class PiSubagentRunner {
|
|
|
14884
15759
|
if (settled)
|
|
14885
15760
|
return;
|
|
14886
15761
|
settled = true;
|
|
15762
|
+
if (accountingMessages.length > 0 && /historian|dreamer|classify/i.test(options.agent)) {
|
|
15763
|
+
const last = [...accountingMessages].reverse().find((message) => message !== null && typeof message === "object" && message.role === "assistant");
|
|
15764
|
+
const responseChars = result.ok ? result.assistantText.length : last?.content?.filter((part) => typeof part === "object" && part !== null && part.type === "text" && typeof part.text === "string").reduce((sum, part) => sum + part.text.length, 0) ?? 0;
|
|
15765
|
+
const tokenDetails = formatRunTokenLog(runTokenLog(last?.usage, options.maxOutputTokens, last?.stopReason));
|
|
15766
|
+
if (!result.ok && result.reason === "truncated") {
|
|
15767
|
+
result = { ...result, error: `${result.error}; ${tokenDetails}` };
|
|
15768
|
+
}
|
|
15769
|
+
sessionLog(options.accountingSessionId ?? "subagent", `${options.agent} response_chars=${responseChars} ${tokenDetails}`);
|
|
15770
|
+
}
|
|
15771
|
+
const outcome = budget ? {
|
|
15772
|
+
...result,
|
|
15773
|
+
meta: { ...result.meta, tokenBudget: budget.snapshot() }
|
|
15774
|
+
} : result;
|
|
14887
15775
|
cleanupSystemPromptFile();
|
|
14888
15776
|
try {
|
|
14889
|
-
recordAccounting(
|
|
15777
|
+
recordAccounting(outcome, accountingMessages);
|
|
14890
15778
|
} catch (err) {
|
|
14891
15779
|
sessionLog(options.accountingSessionId ?? "subagent", `subagent accounting failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
|
|
14892
15780
|
}
|
|
14893
|
-
resolve(
|
|
15781
|
+
resolve(outcome);
|
|
14894
15782
|
};
|
|
14895
15783
|
const emitProgress = (event) => {
|
|
14896
15784
|
if (!options.onProgress)
|
|
@@ -14902,10 +15790,14 @@ class PiSubagentRunner {
|
|
|
14902
15790
|
let child;
|
|
14903
15791
|
try {
|
|
14904
15792
|
child = this.spawnImpl(this.invocation.command, [...this.invocation.prefixArgs, ...this.extraArgs, ...args], {
|
|
15793
|
+
windowsHide: true,
|
|
14905
15794
|
cwd: options.cwd,
|
|
14906
15795
|
env: {
|
|
14907
15796
|
...process.env,
|
|
14908
15797
|
[MAGIC_CONTEXT_PI_SUBAGENT_ENV]: "1",
|
|
15798
|
+
MAGIC_CONTEXT_SUBAGENT_PROMPT_FILE: systemPromptPath,
|
|
15799
|
+
MAGIC_CONTEXT_SUBAGENT_BUDGET_GUARD_FILE: budgetGuardFile,
|
|
15800
|
+
MAGIC_CONTEXT_SUBAGENT_PROVENANCE_ONLY: /^(magic-context-)?historian(?:-recomp|-editor)?$/.test(options.agent) ? undefined : "1",
|
|
14909
15801
|
...options.temperature !== undefined ? {
|
|
14910
15802
|
MAGIC_CONTEXT_HISTORIAN_TEMPERATURE: String(options.temperature)
|
|
14911
15803
|
} : {},
|
|
@@ -14913,7 +15805,11 @@ class PiSubagentRunner {
|
|
|
14913
15805
|
MAGIC_CONTEXT_HISTORIAN_MAX_OUTPUT_TOKENS: String(options.maxOutputTokens)
|
|
14914
15806
|
} : {}
|
|
14915
15807
|
},
|
|
14916
|
-
stdio: [
|
|
15808
|
+
stdio: [
|
|
15809
|
+
deliverViaStdin || rpcBudget ? "pipe" : "ignore",
|
|
15810
|
+
"pipe",
|
|
15811
|
+
"pipe"
|
|
15812
|
+
]
|
|
14917
15813
|
});
|
|
14918
15814
|
} catch (error) {
|
|
14919
15815
|
cleanupSystemPromptFile();
|
|
@@ -14936,15 +15832,43 @@ class PiSubagentRunner {
|
|
|
14936
15832
|
return;
|
|
14937
15833
|
}
|
|
14938
15834
|
emitProgress({ type: "spawned", argv: args, pid: child.pid });
|
|
14939
|
-
if (
|
|
15835
|
+
if (rpcBudget && child.stdin) {
|
|
15836
|
+
child.stdin.on("error", (error) => {
|
|
15837
|
+
terminateChild(child);
|
|
15838
|
+
settle({
|
|
15839
|
+
ok: false,
|
|
15840
|
+
reason: budget?.snapshot().finalizeFired ? "token_budget" : "spawn_failed",
|
|
15841
|
+
error: `Pi RPC input failed: ${error.message}`,
|
|
15842
|
+
durationMs: Date.now() - startTime
|
|
15843
|
+
});
|
|
15844
|
+
});
|
|
15845
|
+
child.stdin.write(`${JSON.stringify({
|
|
15846
|
+
type: "prompt",
|
|
15847
|
+
message: options.userMessage,
|
|
15848
|
+
id: "dreamer-initial"
|
|
15849
|
+
})}
|
|
15850
|
+
`);
|
|
15851
|
+
} else if (deliverViaStdin && child.stdin) {
|
|
14940
15852
|
child.stdin.on("error", () => {});
|
|
14941
15853
|
try {
|
|
14942
15854
|
child.stdin.end(options.userMessage, "utf8");
|
|
14943
15855
|
} catch {}
|
|
14944
15856
|
}
|
|
14945
15857
|
let stderr = "";
|
|
15858
|
+
let stderrLine = "";
|
|
14946
15859
|
child.stderr?.on("data", (chunk) => {
|
|
14947
15860
|
const text = chunk.toString("utf8");
|
|
15861
|
+
stderrLine += text;
|
|
15862
|
+
const lines = stderrLine.split(`
|
|
15863
|
+
`);
|
|
15864
|
+
stderrLine = (lines.pop() ?? "").slice(-4096);
|
|
15865
|
+
for (const line of lines) {
|
|
15866
|
+
if (settled || !line.startsWith('{"type":"mc_system_prompt"'))
|
|
15867
|
+
continue;
|
|
15868
|
+
const parsed = parsePiEventLine(line);
|
|
15869
|
+
if (parsed.ok)
|
|
15870
|
+
telemetry.observe(parsed.event);
|
|
15871
|
+
}
|
|
14948
15872
|
stderr += text;
|
|
14949
15873
|
if (stderr.length > 16000) {
|
|
14950
15874
|
stderr = `…[truncated]${stderr.slice(-16000)}`;
|
|
@@ -14980,7 +15904,7 @@ class PiSubagentRunner {
|
|
|
14980
15904
|
const accumulatedMessages = [];
|
|
14981
15905
|
accountingMessages = accumulatedMessages;
|
|
14982
15906
|
rl.on("line", (line) => {
|
|
14983
|
-
if (line.length === 0)
|
|
15907
|
+
if (settled || line.length === 0)
|
|
14984
15908
|
return;
|
|
14985
15909
|
const parsed = parsePiEventLine(line);
|
|
14986
15910
|
if (!parsed.ok) {
|
|
@@ -14993,6 +15917,95 @@ class PiSubagentRunner {
|
|
|
14993
15917
|
if (typeof event !== "object" || event === null)
|
|
14994
15918
|
return;
|
|
14995
15919
|
const e = event;
|
|
15920
|
+
if (rpcBudget && e.type === "response" && event.success === false) {
|
|
15921
|
+
const reply = event;
|
|
15922
|
+
if (reply.command === "prompt" || reply.command === "steer" && !sawAgentEnd) {
|
|
15923
|
+
terminateChild(child);
|
|
15924
|
+
settle({
|
|
15925
|
+
ok: false,
|
|
15926
|
+
reason: reply.command === "steer" ? "token_budget" : "model_failed",
|
|
15927
|
+
error: reply.error ?? `Pi RPC ${reply.command} failed`,
|
|
15928
|
+
durationMs: Date.now() - startTime
|
|
15929
|
+
});
|
|
15930
|
+
return;
|
|
15931
|
+
}
|
|
15932
|
+
}
|
|
15933
|
+
telemetry.observe(event);
|
|
15934
|
+
if (budget && e.type === "message_end") {
|
|
15935
|
+
const message = e.message;
|
|
15936
|
+
if (message?.role === "assistant") {
|
|
15937
|
+
const usage = message.usage;
|
|
15938
|
+
const finished = message.stopReason === "stop" && !(Array.isArray(message.content) && message.content.some((part) => typeof part === "object" && part !== null && part.type === "toolCall"));
|
|
15939
|
+
const decision = budget.charge(usage?.input ?? 0, usage?.cacheRead ?? 0, usage?.cacheWrite ?? 0, finished, rpcBudget);
|
|
15940
|
+
if (decision === "stop" || decision === "finalize" && !rpcBudget) {
|
|
15941
|
+
terminateChild(child);
|
|
15942
|
+
settle({
|
|
15943
|
+
ok: false,
|
|
15944
|
+
reason: "token_budget",
|
|
15945
|
+
error: "MC-D11: Pi dreamer exceeded its prompt-token budget",
|
|
15946
|
+
durationMs: Date.now() - startTime,
|
|
15947
|
+
meta: { tokenBudget: budget.snapshot() }
|
|
15948
|
+
});
|
|
15949
|
+
return;
|
|
15950
|
+
}
|
|
15951
|
+
if (decision === "finalize" && rpcBudget) {
|
|
15952
|
+
try {
|
|
15953
|
+
if (!budgetGuardFile)
|
|
15954
|
+
throw new Error("missing budget guard file");
|
|
15955
|
+
writeFileSync5(budgetGuardFile, "finalize", "utf8");
|
|
15956
|
+
} catch {
|
|
15957
|
+
terminateChild(child);
|
|
15958
|
+
settle({
|
|
15959
|
+
ok: false,
|
|
15960
|
+
reason: "token_budget",
|
|
15961
|
+
error: "MC-D11: Pi could not activate its tool guard",
|
|
15962
|
+
durationMs: Date.now() - startTime
|
|
15963
|
+
});
|
|
15964
|
+
return;
|
|
15965
|
+
}
|
|
15966
|
+
child.stdin?.write(`${JSON.stringify({
|
|
15967
|
+
type: "steer",
|
|
15968
|
+
message: TOKEN_BUDGET_FINALIZE_MESSAGE,
|
|
15969
|
+
id: "dreamer-finalize"
|
|
15970
|
+
})}
|
|
15971
|
+
`);
|
|
15972
|
+
}
|
|
15973
|
+
}
|
|
15974
|
+
}
|
|
15975
|
+
const resultParts = event.result?.content;
|
|
15976
|
+
if (budget?.snapshot().finalizeFired && e.type === "tool_execution_end" && resultParts?.some((part) => part.text === TOKEN_BUDGET_TOOL_REFUSAL)) {
|
|
15977
|
+
const refusal = budget.refuseTool();
|
|
15978
|
+
if (refusal?.hardStopped) {
|
|
15979
|
+
terminateChild(child);
|
|
15980
|
+
settle({
|
|
15981
|
+
ok: false,
|
|
15982
|
+
reason: "token_budget",
|
|
15983
|
+
error: "MC-D11: Pi dreamer made two calls after finalize",
|
|
15984
|
+
durationMs: Date.now() - startTime,
|
|
15985
|
+
meta: { tokenBudget: budget.snapshot() }
|
|
15986
|
+
});
|
|
15987
|
+
return;
|
|
15988
|
+
}
|
|
15989
|
+
}
|
|
15990
|
+
if (cap !== undefined && telemetry.steps > cap) {
|
|
15991
|
+
if (e.type === "message_end")
|
|
15992
|
+
accumulatedMessages.push(e.message);
|
|
15993
|
+
if (e.type === "agent_end" && Array.isArray(e.messages))
|
|
15994
|
+
accountingMessages = e.messages;
|
|
15995
|
+
if (timeoutHandle)
|
|
15996
|
+
clearTimeout(timeoutHandle);
|
|
15997
|
+
if (drainTimerHandle)
|
|
15998
|
+
clearTimeout(drainTimerHandle);
|
|
15999
|
+
terminateChild(child);
|
|
16000
|
+
settle({
|
|
16001
|
+
ok: false,
|
|
16002
|
+
reason: "step_limit",
|
|
16003
|
+
error: `MC-D10: Pi subagent ${options.agent} exceeded its ${cap}-step limit`,
|
|
16004
|
+
durationMs: Date.now() - startTime,
|
|
16005
|
+
meta: { cap, steps: telemetry.steps }
|
|
16006
|
+
});
|
|
16007
|
+
return;
|
|
16008
|
+
}
|
|
14996
16009
|
const isFirstEvent = eventCount === 0;
|
|
14997
16010
|
eventCount += 1;
|
|
14998
16011
|
lastEventTimestamp = Date.now();
|
|
@@ -15306,9 +16319,9 @@ function buildArgs(options, opts) {
|
|
|
15306
16319
|
const targetHarness = opts?.targetHarness ?? resolvePiHarnessKind();
|
|
15307
16320
|
const ompTarget = targetHarness === "omp";
|
|
15308
16321
|
const args = [
|
|
15309
|
-
"--print",
|
|
16322
|
+
...opts?.rpc ? [] : ["--print"],
|
|
15310
16323
|
"--mode",
|
|
15311
|
-
"json",
|
|
16324
|
+
opts?.rpc ? "rpc" : "json",
|
|
15312
16325
|
"--no-session",
|
|
15313
16326
|
"--no-skills",
|
|
15314
16327
|
...ompTarget ? ["--no-rules"] : ["--no-prompt-templates", "--no-context-files"]
|
|
@@ -15322,7 +16335,7 @@ function buildArgs(options, opts) {
|
|
|
15322
16335
|
}
|
|
15323
16336
|
}
|
|
15324
16337
|
const subagentEntryPath = opts?.subagentEntryPath ?? SUBAGENT_ENTRY_PATH;
|
|
15325
|
-
const shouldLoadSubagentExtension = subagentEntryPath && (SEARCH_ONLY_SUBAGENT_TOOL_AGENTS.has(options.agent) || DREAMER_ACTION_AGENTS.has(options.agent));
|
|
16338
|
+
const shouldLoadSubagentExtension = subagentEntryPath && (opts?.rpc || SEARCH_ONLY_SUBAGENT_TOOL_AGENTS.has(options.agent) || DREAMER_ACTION_AGENTS.has(options.agent));
|
|
15326
16339
|
if (shouldLoadSubagentExtension) {
|
|
15327
16340
|
args.push("--extension", subagentEntryPath);
|
|
15328
16341
|
if (DREAMER_ACTION_AGENTS.has(options.agent)) {
|
|
@@ -15330,7 +16343,7 @@ function buildArgs(options, opts) {
|
|
|
15330
16343
|
}
|
|
15331
16344
|
}
|
|
15332
16345
|
const historianCalibrationEntryPath = opts?.historianCalibrationEntryPath === undefined ? HISTORIAN_CALIBRATION_ENTRY_PATH : opts.historianCalibrationEntryPath;
|
|
15333
|
-
if (
|
|
16346
|
+
if (subagentStepCap(options.agent) !== undefined && historianCalibrationEntryPath) {
|
|
15334
16347
|
args.push("--extension", historianCalibrationEntryPath);
|
|
15335
16348
|
}
|
|
15336
16349
|
const strictTools = STRICT_TOOL_ALLOWLIST.get(options.agent);
|
|
@@ -15559,7 +16572,7 @@ function* sessionEntries(path) {
|
|
|
15559
16572
|
}
|
|
15560
16573
|
|
|
15561
16574
|
// src/dreamer/pi-session-api.ts
|
|
15562
|
-
import { existsSync as existsSync8, readFileSync as
|
|
16575
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, realpathSync as realpathSync4, statSync as statSync5 } from "node:fs";
|
|
15563
16576
|
import {
|
|
15564
16577
|
basename as basename2,
|
|
15565
16578
|
dirname as dirname2,
|
|
@@ -15588,7 +16601,7 @@ function isScriptEntry(filePath) {
|
|
|
15588
16601
|
}
|
|
15589
16602
|
function readManifest(pkgJsonPath) {
|
|
15590
16603
|
try {
|
|
15591
|
-
const pkg = JSON.parse(
|
|
16604
|
+
const pkg = JSON.parse(readFileSync7(pkgJsonPath, "utf8"));
|
|
15592
16605
|
return pkg && typeof pkg === "object" ? pkg : null;
|
|
15593
16606
|
} catch {
|
|
15594
16607
|
return null;
|
|
@@ -15752,7 +16765,7 @@ async function loadDefaultPiSessionApi(loaders) {
|
|
|
15752
16765
|
throw new Error("Pi session APIs unavailable: expected SessionManager.listAll on pi-coding-agent");
|
|
15753
16766
|
}
|
|
15754
16767
|
const loadEntriesFromFile = mod.loadEntriesFromFile ?? ((filePath) => {
|
|
15755
|
-
const content =
|
|
16768
|
+
const content = readFileSync7(filePath, "utf8");
|
|
15756
16769
|
return mod.parseSessionEntries?.(content) ?? [];
|
|
15757
16770
|
});
|
|
15758
16771
|
return {
|
|
@@ -15900,11 +16913,14 @@ class PiRetrospectiveRawProvider {
|
|
|
15900
16913
|
continue;
|
|
15901
16914
|
if (typeof info.cwd !== "string" || resolve2(info.cwd) !== projectCwd)
|
|
15902
16915
|
continue;
|
|
16916
|
+
const activity = this.deps.contextDb ? readSessionActivity(this.deps.contextDb, info.id) : typeof info.modified === "number" ? info.modified : undefined;
|
|
16917
|
+
if (this.deps.contextDb && activity === undefined)
|
|
16918
|
+
continue;
|
|
15903
16919
|
this.sessionPathById.set(info.id, info.path);
|
|
15904
16920
|
result.push({
|
|
15905
16921
|
sessionId: info.id,
|
|
15906
16922
|
path: info.path,
|
|
15907
|
-
updatedAt:
|
|
16923
|
+
updatedAt: activity
|
|
15908
16924
|
});
|
|
15909
16925
|
}
|
|
15910
16926
|
return result.sort((a, b) => (a.updatedAt ?? 0) - (b.updatedAt ?? 0));
|
|
@@ -16051,6 +17067,12 @@ var inFlightDreams = (() => {
|
|
|
16051
17067
|
globals[PI_DREAMER_IN_FLIGHT] = dreams;
|
|
16052
17068
|
return dreams;
|
|
16053
17069
|
})();
|
|
17070
|
+
var dreamAbortControllers = new Map;
|
|
17071
|
+
function abortInFlightDreamers(registrationOwner) {
|
|
17072
|
+
for (const controller of dreamAbortControllers.get(registrationOwner) ?? []) {
|
|
17073
|
+
controller.abort();
|
|
17074
|
+
}
|
|
17075
|
+
}
|
|
16054
17076
|
var sessionCounter = 0;
|
|
16055
17077
|
var piSubagentRunnerFactory = () => new PiSubagentRunner;
|
|
16056
17078
|
var startDreamScheduleTimerFn = startDreamScheduleTimer;
|
|
@@ -16079,7 +17101,7 @@ function registerPiDreamerProject(opts) {
|
|
|
16079
17101
|
const client = createPiDreamerClient(opts, notifyOwnersOfAdjunctRefresh, () => {
|
|
16080
17102
|
const current = registeredProjects2.get(opts.projectIdentity);
|
|
16081
17103
|
return current?.generation === generation && current.owners.get(opts.registrationOwner)?.projectDir === opts.projectDir;
|
|
16082
|
-
}, () => opts.sampleDreamRun ? opts.sampleDreamRun().dreamerConfig?.maxTokens : opts.config.maxTokens);
|
|
17104
|
+
}, () => opts.sampleDreamRun ? opts.sampleDreamRun().dreamerConfig?.maxTokens : opts.config.maxTokens, () => opts.sampleDreamRun?.().dreamerConfig ?? opts.config);
|
|
16083
17105
|
let cleanup;
|
|
16084
17106
|
let cancelled = false;
|
|
16085
17107
|
const modelRegistry = opts.modelRegistry;
|
|
@@ -16098,7 +17120,10 @@ function registerPiDreamerProject(opts) {
|
|
|
16098
17120
|
retinaHandoff: opts.retinaHandoff,
|
|
16099
17121
|
mural: opts.mural,
|
|
16100
17122
|
ensureRegistered: ensureProjectRegisteredFromPiDirectory,
|
|
16101
|
-
retrospectiveRawProvider: () => new PiRetrospectiveRawProvider({
|
|
17123
|
+
retrospectiveRawProvider: () => new PiRetrospectiveRawProvider({
|
|
17124
|
+
projectCwd: opts.projectDir,
|
|
17125
|
+
contextDb: opts.db
|
|
17126
|
+
}),
|
|
16102
17127
|
primerRawProviderFactory: createPiPrimerRawProviderFactory()
|
|
16103
17128
|
}).then((timerCleanup) => {
|
|
16104
17129
|
if (cancelled) {
|
|
@@ -16117,7 +17142,7 @@ function registerPiDreamerProject(opts) {
|
|
|
16117
17142
|
const sampled = manualOpts.sampleDreamRun?.();
|
|
16118
17143
|
const dreamerConfig = sampled?.dreamerConfig ?? manualOpts.config;
|
|
16119
17144
|
const mural = sampled?.mural ?? manualOpts.mural;
|
|
16120
|
-
const manualClient = createPiDreamerClient(manualOpts, notifyOwnersOfAdjunctRefresh, () => owners.get(manualOpts.registrationOwner)?.projectDir === manualOpts.projectDir, () => dreamerConfig.maxTokens);
|
|
17145
|
+
const manualClient = createPiDreamerClient(manualOpts, notifyOwnersOfAdjunctRefresh, () => owners.get(manualOpts.registrationOwner)?.projectDir === manualOpts.projectDir, () => dreamerConfig.maxTokens, () => dreamerConfig);
|
|
16121
17146
|
const manualRun = runManualDream({
|
|
16122
17147
|
db: manualOpts.db,
|
|
16123
17148
|
projectIdentity: manualOpts.projectIdentity,
|
|
@@ -16127,7 +17152,8 @@ function registerPiDreamerProject(opts) {
|
|
|
16127
17152
|
sessionDirectory: manualOpts.projectDir,
|
|
16128
17153
|
openOpenCodeDb,
|
|
16129
17154
|
retrospectiveRawProvider: new PiRetrospectiveRawProvider({
|
|
16130
|
-
projectCwd: manualOpts.projectDir
|
|
17155
|
+
projectCwd: manualOpts.projectDir,
|
|
17156
|
+
contextDb: manualOpts.db
|
|
16131
17157
|
}),
|
|
16132
17158
|
primerRawProviderFactory: createPiPrimerRawProviderFactory(),
|
|
16133
17159
|
userMemoryCollectionEnabled: userMemoryCollectionEnabled(dreamerConfig),
|
|
@@ -16197,8 +17223,9 @@ async function awaitInFlightDreamers(registrationOwner) {
|
|
|
16197
17223
|
return;
|
|
16198
17224
|
await Promise.allSettled(runs);
|
|
16199
17225
|
}
|
|
16200
|
-
function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRefreshNeeded, isRegistrationOwnerActive = () => true, getMaxOutputTokens = () => opts.config.maxTokens) {
|
|
17226
|
+
function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRefreshNeeded, isRegistrationOwnerActive = () => true, getMaxOutputTokens = () => opts.config.maxTokens, getDreamerConfig = () => opts.config) {
|
|
16201
17227
|
const runner = piSubagentRunnerFactory();
|
|
17228
|
+
const budgetStates = new Map;
|
|
16202
17229
|
const assertRegistrationOwnerActive = () => {
|
|
16203
17230
|
if (!isRegistrationOwnerActive()) {
|
|
16204
17231
|
throw new Error(`Pi dreamer registration is no longer active for project ${opts.projectIdentity}`);
|
|
@@ -16228,6 +17255,13 @@ function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRe
|
|
|
16228
17255
|
const systemPrompt = extractSystemPrompt(args);
|
|
16229
17256
|
const perTaskModel = extractBodyModel(args);
|
|
16230
17257
|
const requestedAgent = extractBodyAgent(args) ?? "magic-context-dreamer";
|
|
17258
|
+
const controller = new AbortController;
|
|
17259
|
+
let controllers = dreamAbortControllers.get(opts.registrationOwner);
|
|
17260
|
+
if (!controllers) {
|
|
17261
|
+
controllers = new Set;
|
|
17262
|
+
dreamAbortControllers.set(opts.registrationOwner, controllers);
|
|
17263
|
+
}
|
|
17264
|
+
controllers.add(controller);
|
|
16231
17265
|
const runPromise = runner.run({
|
|
16232
17266
|
agent: requestedAgent,
|
|
16233
17267
|
systemPrompt,
|
|
@@ -16236,18 +17270,39 @@ function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRe
|
|
|
16236
17270
|
fallbackModels: undefined,
|
|
16237
17271
|
timeoutMs: 30 * 60 * 1000,
|
|
16238
17272
|
cwd: dreamSession.directory,
|
|
16239
|
-
signal: args.signal
|
|
17273
|
+
signal: args.signal ? AbortSignal.any([args.signal, controller.signal]) : controller.signal,
|
|
16240
17274
|
thinkingLevel: extractBodyVariant(args),
|
|
16241
17275
|
maxOutputTokens: getMaxOutputTokens(),
|
|
16242
17276
|
accountingSessionId: opts.projectIdentity,
|
|
16243
17277
|
accountingSubagent: "dreamer",
|
|
16244
|
-
accountingTask: accountingTaskFromTitle(dreamSession.title)
|
|
17278
|
+
accountingTask: accountingTaskFromTitle(dreamSession.title),
|
|
17279
|
+
tokenBudget: (() => {
|
|
17280
|
+
const task = accountingTaskFromTitle(dreamSession.title);
|
|
17281
|
+
if (!task)
|
|
17282
|
+
return;
|
|
17283
|
+
return buildDreamTaskRuntimeConfigs(getDreamerConfig(), opts.harness).find((entry) => entry.task === task)?.tokenBudget;
|
|
17284
|
+
})()
|
|
16245
17285
|
});
|
|
16246
17286
|
inFlightDreams.set(runPromise, opts.registrationOwner);
|
|
16247
17287
|
try {
|
|
16248
17288
|
const result = await runPromise;
|
|
17289
|
+
const budget = result.meta?.tokenBudget;
|
|
17290
|
+
const budgetTask = accountingTaskFromTitle(dreamSession.title);
|
|
17291
|
+
if (budgetTask && budget && typeof budget.spent === "number") {
|
|
17292
|
+
const prior = budgetStates.get(budgetTask);
|
|
17293
|
+
budgetStates.set(budgetTask, {
|
|
17294
|
+
spent: (prior?.spent ?? 0) + budget.spent,
|
|
17295
|
+
finalizeFired: (prior?.finalizeFired ?? false) || budget.finalizeFired === true
|
|
17296
|
+
});
|
|
17297
|
+
}
|
|
16249
17298
|
assertRegistrationOwnerActive();
|
|
16250
17299
|
if (!result.ok) {
|
|
17300
|
+
if (result.reason === "token_budget") {
|
|
17301
|
+
throw new DreamTokenBudgetExceeded(dreamSession.id, Number(result.meta?.tokenBudget?.spent ?? 0));
|
|
17302
|
+
}
|
|
17303
|
+
if (result.reason === "step_limit") {
|
|
17304
|
+
throw new HiddenAgentStepLimit("pi-dreamer", Number(result.meta?.cap));
|
|
17305
|
+
}
|
|
16251
17306
|
const error = new Error(`Pi dreamer subagent failed (${result.reason}): ${result.error}`);
|
|
16252
17307
|
if (result.transient) {
|
|
16253
17308
|
error.transient = true;
|
|
@@ -16264,6 +17319,9 @@ function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRe
|
|
|
16264
17319
|
onAdjunctsRefreshNeeded?.(opts.projectIdentity);
|
|
16265
17320
|
} finally {
|
|
16266
17321
|
inFlightDreams.delete(runPromise);
|
|
17322
|
+
controllers.delete(controller);
|
|
17323
|
+
if (controllers.size === 0)
|
|
17324
|
+
dreamAbortControllers.delete(opts.registrationOwner);
|
|
16267
17325
|
}
|
|
16268
17326
|
},
|
|
16269
17327
|
messages: async (args) => {
|
|
@@ -16276,7 +17334,11 @@ function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRe
|
|
|
16276
17334
|
return {};
|
|
16277
17335
|
}
|
|
16278
17336
|
};
|
|
16279
|
-
return {
|
|
17337
|
+
return {
|
|
17338
|
+
session,
|
|
17339
|
+
readTokenBudget: (task) => budgetStates.get(task),
|
|
17340
|
+
resetDreamTokenBudget: (task) => budgetStates.delete(task)
|
|
17341
|
+
};
|
|
16280
17342
|
}
|
|
16281
17343
|
function readDirectory(args) {
|
|
16282
17344
|
const query = args.query;
|
|
@@ -16585,6 +17647,7 @@ Unknown task "${requested}".`,
|
|
|
16585
17647
|
try {
|
|
16586
17648
|
await deps.ensureRegistered?.(ctx);
|
|
16587
17649
|
const result = await runPiDreamForProject(project.projectIdentity, task, deps.registrationOwner);
|
|
17650
|
+
const backlogAfter = getDreamTaskBacklogs(deps.db, project.projectIdentity, backlogTasks);
|
|
16588
17651
|
const lines = [];
|
|
16589
17652
|
if (result.ran.length > 0)
|
|
16590
17653
|
lines.push(`Ran: ${result.ran.join(", ")}`);
|
|
@@ -16600,8 +17663,8 @@ Unknown task "${requested}".`,
|
|
|
16600
17663
|
lines.push(`Skipped (no work): ${result.skippedNoWork.join(", ")}`);
|
|
16601
17664
|
if (result.deferredBusy.length > 0)
|
|
16602
17665
|
lines.push(`Busy: ${result.deferredBusy.join(", ")} — another dream task holds this domain's lease; retry in a minute`);
|
|
16603
|
-
if (Object.keys(
|
|
16604
|
-
lines.push("", "Backlog at run end:", formatDreamTaskBacklogs(
|
|
17666
|
+
if (Object.keys(backlogAfter).length > 0) {
|
|
17667
|
+
lines.push("", "Backlog at run end:", formatDreamTaskBacklogs(backlogAfter, backlogTasks));
|
|
16605
17668
|
}
|
|
16606
17669
|
if (lines.length === 0)
|
|
16607
17670
|
lines.push("No enabled dream tasks to run.");
|
|
@@ -16630,26 +17693,6 @@ Unknown task "${requested}".`,
|
|
|
16630
17693
|
});
|
|
16631
17694
|
}
|
|
16632
17695
|
|
|
16633
|
-
// ../plugin/src/hooks/magic-context/embed-session-state.ts
|
|
16634
|
-
var embedPauseBySession = new Set;
|
|
16635
|
-
var embedRunStateBySession = new Map;
|
|
16636
|
-
var autoEmbedAttemptedBySession = new Set;
|
|
16637
|
-
function getEmbedDrainUiStatus(sessionId, progress) {
|
|
16638
|
-
if (embedPauseBySession.has(sessionId)) {
|
|
16639
|
-
return { status: "paused" };
|
|
16640
|
-
}
|
|
16641
|
-
const activeRun = embedRunStateBySession.get(sessionId);
|
|
16642
|
-
if (activeRun && !activeRun.signal.aborted || progress?.kind === "embed" && progress.phase === "recomp") {
|
|
16643
|
-
return { status: "running" };
|
|
16644
|
-
}
|
|
16645
|
-
if (progress?.kind === "embed" && (progress.phase === "failed" || progress.phase === "skipped") && progress.message) {
|
|
16646
|
-
if (/provider/i.test(progress.message)) {
|
|
16647
|
-
return { status: "stopped", detail: progress.message };
|
|
16648
|
-
}
|
|
16649
|
-
}
|
|
16650
|
-
return { status: "idle" };
|
|
16651
|
-
}
|
|
16652
|
-
|
|
16653
17696
|
// ../plugin/src/hooks/magic-context/format-embed-failure.ts
|
|
16654
17697
|
function formatEmbedFailureSummary(embedded, remaining, failure, style = "markdown") {
|
|
16655
17698
|
const historyBlocks = `history block${embedded === 1 ? "" : "s"}`;
|
|
@@ -16713,7 +17756,7 @@ function clearPiEmbedSessionState(sessionId) {
|
|
|
16713
17756
|
ctrl.abort();
|
|
16714
17757
|
embedRunStateBySession.delete(sessionId);
|
|
16715
17758
|
}
|
|
16716
|
-
|
|
17759
|
+
invalidateAutoEmbedSession(sessionId);
|
|
16717
17760
|
}
|
|
16718
17761
|
async function runEmbedDrain(db, projectIdentity, sessionId, options = {}) {
|
|
16719
17762
|
const activeCtrl = embedRunStateBySession.get(sessionId);
|
|
@@ -16895,28 +17938,34 @@ ${statusText}`,
|
|
|
16895
17938
|
function maybeAutoEmbedPiSession(deps, sessionId, projectDir, projectIdentity) {
|
|
16896
17939
|
if (!isUsableProjectIdentity(projectIdentity))
|
|
16897
17940
|
return;
|
|
17941
|
+
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
17942
|
+
const embedIdentity = snapshot ? JSON.stringify([
|
|
17943
|
+
snapshot.providerIdentity,
|
|
17944
|
+
snapshot.chunkModelId,
|
|
17945
|
+
snapshot.runtimeFingerprint
|
|
17946
|
+
]) : "off";
|
|
17947
|
+
if (autoEmbedIdentityBySession.get(sessionId) !== embedIdentity)
|
|
17948
|
+
invalidateAutoEmbedSession(sessionId);
|
|
16898
17949
|
if (autoEmbedAttemptedBySession.has(sessionId))
|
|
16899
17950
|
return;
|
|
16900
17951
|
if (embedPauseBySession.has(sessionId))
|
|
16901
17952
|
return;
|
|
16902
17953
|
autoEmbedAttemptedBySession.add(sessionId);
|
|
17954
|
+
autoEmbedIdentityBySession.set(sessionId, embedIdentity);
|
|
16903
17955
|
(async () => {
|
|
16904
|
-
let drainReachedTerminal = false;
|
|
16905
17956
|
try {
|
|
16906
17957
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
16907
17958
|
await ensureProjectRegisteredFromPiDirectory(projectDir, deps.db);
|
|
16908
|
-
const coverage =
|
|
16909
|
-
if (!coverage.enabled)
|
|
17959
|
+
const coverage = await getAutoEmbeddingSessionCoverage(deps.db, projectIdentity, sessionId);
|
|
17960
|
+
if (!coverage.enabled) {
|
|
17961
|
+
invalidateAutoEmbedSession(sessionId);
|
|
16910
17962
|
return;
|
|
16911
|
-
|
|
17963
|
+
}
|
|
17964
|
+
const remaining = coverage.total - coverage.embedded;
|
|
16912
17965
|
if (remaining <= 0)
|
|
16913
17966
|
return;
|
|
16914
17967
|
await runEmbedDrain(deps.db, projectIdentity, sessionId);
|
|
16915
|
-
|
|
16916
|
-
} catch {} finally {
|
|
16917
|
-
if (!drainReachedTerminal)
|
|
16918
|
-
autoEmbedAttemptedBySession.delete(sessionId);
|
|
16919
|
-
}
|
|
17968
|
+
} catch {}
|
|
16920
17969
|
})();
|
|
16921
17970
|
}
|
|
16922
17971
|
|
|
@@ -19772,10 +20821,10 @@ function advanceToolReclaimWatermarkToCurrentMax(db, sessionId) {
|
|
|
19772
20821
|
}
|
|
19773
20822
|
|
|
19774
20823
|
// ../plugin/src/shared/tag-transcript.ts
|
|
19775
|
-
import { createHash as
|
|
20824
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
19776
20825
|
var TEXT_TAG_IDENTITY_MARKER = ":mc-text-v1:";
|
|
19777
20826
|
function textIdentityDigest(value) {
|
|
19778
|
-
return
|
|
20827
|
+
return createHash12("sha256").update(value).digest("hex");
|
|
19779
20828
|
}
|
|
19780
20829
|
function buildContentDerivedTextIds(messageId, parts) {
|
|
19781
20830
|
const sources = parts.filter((part) => part.kind === "text").map((part) => stripTagPrefix(part.getText() ?? ""));
|
|
@@ -23039,8 +24088,10 @@ async function runHistorianSubagentWithTransientRetriesGuarded(args) {
|
|
|
23039
24088
|
try {
|
|
23040
24089
|
const key = piModelRefToCanonical(args.options.model ?? "");
|
|
23041
24090
|
const window = args.resolveContextLimit?.(key) ?? resolveKnownHistorianContextLimit(key);
|
|
23042
|
-
const
|
|
23043
|
-
|
|
24091
|
+
const limits = args.resolveContextLimit ? { context: window, input: undefined } : resolveHistorianProducerLimits(key);
|
|
24092
|
+
const context = limits.context ?? (limits.input === undefined ? window : undefined);
|
|
24093
|
+
const reserve = historianProducerReserve(context, args.options.maxOutputTokens, args.resolveOutputLimit?.(key));
|
|
24094
|
+
if (window !== undefined && producerInputTokenLimit(context, reserve, limits.input) === undefined && !loggedInconsistentWindows.has(key)) {
|
|
23044
24095
|
loggedInconsistentWindows.add(key);
|
|
23045
24096
|
sessionLog(args.sessionId, `producer window inconsistent for ${key}: window=${window} reserve=${reserve}; sending unguarded`);
|
|
23046
24097
|
}
|
|
@@ -23049,7 +24100,8 @@ async function runHistorianSubagentWithTransientRetriesGuarded(args) {
|
|
|
23049
24100
|
systemLocal: estimateTokens(args.options.systemPrompt),
|
|
23050
24101
|
toolsLocal: 0,
|
|
23051
24102
|
modelKey: key,
|
|
23052
|
-
contextLimitTokens:
|
|
24103
|
+
contextLimitTokens: context,
|
|
24104
|
+
inputLimitTokens: limits.input,
|
|
23053
24105
|
maxOutputTokens: reserve
|
|
23054
24106
|
});
|
|
23055
24107
|
if (failure)
|
|
@@ -25659,7 +26711,7 @@ function readFrozenM0InputsPi(state, db, docs = readProjectDocsForPiM0(state), m
|
|
|
25659
26711
|
};
|
|
25660
26712
|
return { docs, markers, compartments, memories, userProfile, workspace };
|
|
25661
26713
|
});
|
|
25662
|
-
return read();
|
|
26714
|
+
return read.deferred();
|
|
25663
26715
|
}
|
|
25664
26716
|
function renderFreshM0PiNonPersisted(state, db) {
|
|
25665
26717
|
const docs = readProjectDocsForPiM0(state);
|
|
@@ -26786,162 +27838,6 @@ function registerPiGuardedContext(pi, handler) {
|
|
|
26786
27838
|
});
|
|
26787
27839
|
}
|
|
26788
27840
|
|
|
26789
|
-
// ../plugin/src/hooks/magic-context/lkg-persist.ts
|
|
26790
|
-
function parseStringArray(value) {
|
|
26791
|
-
if (typeof value !== "string")
|
|
26792
|
-
return null;
|
|
26793
|
-
let parsed;
|
|
26794
|
-
try {
|
|
26795
|
-
parsed = JSON.parse(value);
|
|
26796
|
-
} catch {
|
|
26797
|
-
return null;
|
|
26798
|
-
}
|
|
26799
|
-
if (!Array.isArray(parsed))
|
|
26800
|
-
return null;
|
|
26801
|
-
const result = [];
|
|
26802
|
-
for (const entry of parsed) {
|
|
26803
|
-
if (typeof entry !== "string" || entry.length === 0)
|
|
26804
|
-
return null;
|
|
26805
|
-
result.push(entry);
|
|
26806
|
-
}
|
|
26807
|
-
return result;
|
|
26808
|
-
}
|
|
26809
|
-
function parseNullableString(value) {
|
|
26810
|
-
if (value === null || value === undefined)
|
|
26811
|
-
return null;
|
|
26812
|
-
return typeof value === "string" ? value : undefined;
|
|
26813
|
-
}
|
|
26814
|
-
function parseNullableInteger(value) {
|
|
26815
|
-
if (value === null || value === undefined)
|
|
26816
|
-
return;
|
|
26817
|
-
return typeof value === "number" && Number.isSafeInteger(value) ? value : undefined;
|
|
26818
|
-
}
|
|
26819
|
-
function parsePersistedLkgSlot(row) {
|
|
26820
|
-
if (!row || typeof row !== "object")
|
|
26821
|
-
return;
|
|
26822
|
-
const record = row;
|
|
26823
|
-
const jsonPrefix = record.json_prefix;
|
|
26824
|
-
const lastInputMessageId = record.last_input_message_id;
|
|
26825
|
-
const capturedAt = record.captured_at;
|
|
26826
|
-
let inputIdsRaw = record.input_id_seq;
|
|
26827
|
-
let piOutputEntryIds;
|
|
26828
|
-
if (typeof inputIdsRaw === "string") {
|
|
26829
|
-
try {
|
|
26830
|
-
const metadata = JSON.parse(inputIdsRaw);
|
|
26831
|
-
if (metadata && !Array.isArray(metadata) && metadata.version === 1 && Array.isArray(metadata.piOutputEntryIds)) {
|
|
26832
|
-
if (!metadata.piOutputEntryIds.every((id) => id === null || typeof id === "string" && id.length > 0))
|
|
26833
|
-
return;
|
|
26834
|
-
piOutputEntryIds = metadata.piOutputEntryIds;
|
|
26835
|
-
inputIdsRaw = JSON.stringify(metadata.inputIds);
|
|
26836
|
-
}
|
|
26837
|
-
} catch {
|
|
26838
|
-
return;
|
|
26839
|
-
}
|
|
26840
|
-
}
|
|
26841
|
-
const inputIdSeq = parseStringArray(inputIdsRaw);
|
|
26842
|
-
const inputContentDigests = parseStringArray(record.input_content_digests);
|
|
26843
|
-
const modelKey = parseNullableString(record.model_key);
|
|
26844
|
-
const providerKey = parseNullableString(record.provider_key);
|
|
26845
|
-
if (typeof jsonPrefix !== "string" || typeof lastInputMessageId !== "string" || lastInputMessageId.length === 0 || typeof capturedAt !== "number" || !Number.isFinite(capturedAt) || inputIdSeq === null || inputContentDigests === null || inputContentDigests.length !== inputIdSeq.length || modelKey === undefined || providerKey === undefined) {
|
|
26846
|
-
return;
|
|
26847
|
-
}
|
|
26848
|
-
if (piOutputEntryIds) {
|
|
26849
|
-
try {
|
|
26850
|
-
const output = JSON.parse(jsonPrefix);
|
|
26851
|
-
const inputs = new Set(inputIdSeq);
|
|
26852
|
-
if (!Array.isArray(output) || output.length !== piOutputEntryIds.length || piOutputEntryIds.some((id) => id !== null && !inputs.has(id)))
|
|
26853
|
-
return;
|
|
26854
|
-
} catch {
|
|
26855
|
-
return;
|
|
26856
|
-
}
|
|
26857
|
-
}
|
|
26858
|
-
let inputContentSignatures;
|
|
26859
|
-
if (record.input_content_signatures !== null && record.input_content_signatures !== undefined) {
|
|
26860
|
-
const parsed = parseStringArray(record.input_content_signatures);
|
|
26861
|
-
if (parsed === null || parsed.length !== inputIdSeq.length)
|
|
26862
|
-
return;
|
|
26863
|
-
inputContentSignatures = parsed;
|
|
26864
|
-
}
|
|
26865
|
-
const slot = {
|
|
26866
|
-
jsonPrefix,
|
|
26867
|
-
inputIdSeq,
|
|
26868
|
-
inputContentDigests,
|
|
26869
|
-
lastInputMessageId,
|
|
26870
|
-
modelKey,
|
|
26871
|
-
providerKey,
|
|
26872
|
-
capturedAt
|
|
26873
|
-
};
|
|
26874
|
-
if (inputContentSignatures)
|
|
26875
|
-
slot.inputContentSignatures = inputContentSignatures;
|
|
26876
|
-
if (piOutputEntryIds)
|
|
26877
|
-
slot.piOutputEntryIds = piOutputEntryIds;
|
|
26878
|
-
const rowVersion = parseNullableInteger(record.row_version);
|
|
26879
|
-
if (rowVersion !== undefined)
|
|
26880
|
-
slot.rowVersion = rowVersion;
|
|
26881
|
-
const captureSequence = parseNullableInteger(record.capture_sequence);
|
|
26882
|
-
if (captureSequence !== undefined)
|
|
26883
|
-
slot.captureSequence = captureSequence;
|
|
26884
|
-
return slot;
|
|
26885
|
-
}
|
|
26886
|
-
function saveLkgSlotToDb(db, sessionId, slot) {
|
|
26887
|
-
try {
|
|
26888
|
-
db.prepare(`INSERT INTO lkg_slots (
|
|
26889
|
-
session_id, json_prefix, input_id_seq, input_content_digests,
|
|
26890
|
-
input_content_signatures, last_input_message_id, model_key, provider_key,
|
|
26891
|
-
captured_at, row_version, capture_sequence
|
|
26892
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
26893
|
-
ON CONFLICT(session_id) DO UPDATE SET
|
|
26894
|
-
json_prefix = excluded.json_prefix,
|
|
26895
|
-
input_id_seq = excluded.input_id_seq,
|
|
26896
|
-
input_content_digests = excluded.input_content_digests,
|
|
26897
|
-
input_content_signatures = excluded.input_content_signatures,
|
|
26898
|
-
last_input_message_id = excluded.last_input_message_id,
|
|
26899
|
-
model_key = excluded.model_key,
|
|
26900
|
-
provider_key = excluded.provider_key,
|
|
26901
|
-
captured_at = excluded.captured_at,
|
|
26902
|
-
row_version = excluded.row_version,
|
|
26903
|
-
capture_sequence = excluded.capture_sequence`).run(sessionId, slot.jsonPrefix, JSON.stringify(slot.piOutputEntryIds ? {
|
|
26904
|
-
version: 1,
|
|
26905
|
-
inputIds: slot.inputIdSeq,
|
|
26906
|
-
piOutputEntryIds: slot.piOutputEntryIds
|
|
26907
|
-
} : slot.inputIdSeq), JSON.stringify(slot.inputContentDigests), slot.inputContentSignatures ? JSON.stringify(slot.inputContentSignatures) : null, slot.lastInputMessageId, slot.modelKey, slot.providerKey, slot.capturedAt, slot.rowVersion ?? null, slot.captureSequence ?? null);
|
|
26908
|
-
return true;
|
|
26909
|
-
} catch (error) {
|
|
26910
|
-
sessionLog(sessionId, "LKG snapshot persistence failed (in-memory slot retained):", error);
|
|
26911
|
-
return false;
|
|
26912
|
-
}
|
|
26913
|
-
}
|
|
26914
|
-
function clearPersistedLkgSlot(db, sessionId) {
|
|
26915
|
-
try {
|
|
26916
|
-
db.prepare("DELETE FROM lkg_slots WHERE session_id = ?").run(sessionId);
|
|
26917
|
-
} catch (error) {
|
|
26918
|
-
sessionLog(sessionId, "LKG snapshot durable clear failed:", error);
|
|
26919
|
-
}
|
|
26920
|
-
}
|
|
26921
|
-
function loadPersistedLkgSlot(db, sessionId) {
|
|
26922
|
-
let row;
|
|
26923
|
-
try {
|
|
26924
|
-
row = db.prepare("SELECT * FROM lkg_slots WHERE session_id = ?").get(sessionId);
|
|
26925
|
-
} catch (error) {
|
|
26926
|
-
sessionLog(sessionId, "LKG snapshot durable load failed:", error);
|
|
26927
|
-
return;
|
|
26928
|
-
}
|
|
26929
|
-
if (!row)
|
|
26930
|
-
return;
|
|
26931
|
-
const slot = parsePersistedLkgSlot(row);
|
|
26932
|
-
if (!slot) {
|
|
26933
|
-
clearPersistedLkgSlot(db, sessionId);
|
|
26934
|
-
return;
|
|
26935
|
-
}
|
|
26936
|
-
return slot;
|
|
26937
|
-
}
|
|
26938
|
-
function createDbLkgPersistence(db) {
|
|
26939
|
-
return {
|
|
26940
|
-
load: (sessionId) => loadPersistedLkgSlot(db, sessionId),
|
|
26941
|
-
clear: (sessionId) => clearPersistedLkgSlot(db, sessionId)
|
|
26942
|
-
};
|
|
26943
|
-
}
|
|
26944
|
-
|
|
26945
27841
|
// ../plugin/src/hooks/magic-context/openai-compat-adjacency.ts
|
|
26946
27842
|
function assertOpenAiCompatAdjacency(messages) {
|
|
26947
27843
|
const violations = [];
|
|
@@ -27136,19 +28032,22 @@ function validateAnthropicReasoningRuns(messages) {
|
|
|
27136
28032
|
index += 1;
|
|
27137
28033
|
continue;
|
|
27138
28034
|
}
|
|
27139
|
-
let
|
|
28035
|
+
let firstMessageInRun = null;
|
|
27140
28036
|
let sawOtherContent = false;
|
|
27141
28037
|
while (index < messages.length && messageRole(messages[index]) === "assistant") {
|
|
27142
28038
|
for (const part of messageParts(messages[index])) {
|
|
27143
|
-
if (
|
|
27144
|
-
|
|
27145
|
-
if (thinkingBlocks > 1 || sawOtherContent)
|
|
27146
|
-
return false;
|
|
27147
|
-
} else if (partEndsAnthropicAssistantRun(part)) {
|
|
27148
|
-
thinkingBlocks = 0;
|
|
28039
|
+
if (partEndsAnthropicAssistantRun(part)) {
|
|
28040
|
+
firstMessageInRun = null;
|
|
27149
28041
|
sawOtherContent = false;
|
|
27150
28042
|
} else if (!partIsOpenCodeStepMetadata(part)) {
|
|
27151
|
-
|
|
28043
|
+
if (firstMessageInRun === null)
|
|
28044
|
+
firstMessageInRun = index;
|
|
28045
|
+
if (partIsAnthropicThinking(part)) {
|
|
28046
|
+
if (sawOtherContent || index !== firstMessageInRun)
|
|
28047
|
+
return false;
|
|
28048
|
+
} else {
|
|
28049
|
+
sawOtherContent = true;
|
|
28050
|
+
}
|
|
27152
28051
|
}
|
|
27153
28052
|
}
|
|
27154
28053
|
index += 1;
|
|
@@ -27256,6 +28155,8 @@ function replayLkg(args) {
|
|
|
27256
28155
|
dropSlot(args.sessionId, "lkg_seam_invalid");
|
|
27257
28156
|
return { ok: false, reason: "lkg_seam_invalid" };
|
|
27258
28157
|
}
|
|
28158
|
+
const replayed = [...prefix, ...entry.pristineTail];
|
|
28159
|
+
args.prepareReplay?.(replayed);
|
|
27259
28160
|
if (!args.skipSeamValidation) {
|
|
27260
28161
|
if (!validateLkgSeamBoundary(prefix, entry.pristineTail)) {
|
|
27261
28162
|
dropSlot(args.sessionId, "lkg_unsafe_seam");
|
|
@@ -27266,7 +28167,6 @@ function replayLkg(args) {
|
|
|
27266
28167
|
return { ok: false, reason: "lkg_seam_invalid" };
|
|
27267
28168
|
}
|
|
27268
28169
|
}
|
|
27269
|
-
const replayed = [...prefix, ...entry.pristineTail];
|
|
27270
28170
|
if (requestedModelKeys.providerKey === "anthropic" && !validateAnthropicReasoningRuns(replayed)) {
|
|
27271
28171
|
dropSlot(args.sessionId, "lkg_anthropic_reasoning_run_invalid");
|
|
27272
28172
|
return { ok: false, reason: "lkg_anthropic_reasoning_run_invalid" };
|
|
@@ -28573,7 +29473,7 @@ function replayStrippedInlineThinkingPi(args) {
|
|
|
28573
29473
|
}
|
|
28574
29474
|
|
|
28575
29475
|
// src/served-array-ledger.ts
|
|
28576
|
-
import { createHash as
|
|
29476
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
28577
29477
|
import * as fs2 from "node:fs";
|
|
28578
29478
|
import * as path6 from "node:path";
|
|
28579
29479
|
var PI_SERVED_ARRAY_TAIL_MESSAGES = 40;
|
|
@@ -28588,7 +29488,7 @@ var flushTimer = null;
|
|
|
28588
29488
|
var swallowedWriteCount = 0;
|
|
28589
29489
|
var lastWriteError = null;
|
|
28590
29490
|
function sha256(value) {
|
|
28591
|
-
return
|
|
29491
|
+
return createHash13("sha256").update(value).digest("hex");
|
|
28592
29492
|
}
|
|
28593
29493
|
function safeSessionFileStem(sessionId) {
|
|
28594
29494
|
const readable = sessionId.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 96);
|
|
@@ -28731,13 +29631,10 @@ function capturePiServedArray(sessionId, messages, options = {}) {
|
|
|
28731
29631
|
process.once("beforeExit", flushPiServedArrayLedger);
|
|
28732
29632
|
|
|
28733
29633
|
// src/strip-placeholders-pi.ts
|
|
28734
|
-
var
|
|
28735
|
-
function
|
|
29634
|
+
var MARKER_ONLY_PATTERN = /^(?:(?:§\d+§|\[dropped(?: §\d+§)?\]|\[cleared\])\s*)+$/;
|
|
29635
|
+
function isPiMarkerOnlyText(text) {
|
|
28736
29636
|
const trimmed = text.trim();
|
|
28737
|
-
|
|
28738
|
-
return true;
|
|
28739
|
-
const segments = trimmed.split(/(?=\[dropped(?: §[^§]+§)?\])/).map((segment) => segment.trim()).filter((segment) => segment.length > 0);
|
|
28740
|
-
return segments.length > 0 && segments.every((s) => DROPPED_SEGMENT_PATTERN.test(s));
|
|
29637
|
+
return trimmed.length > 0 && MARKER_ONLY_PATTERN.test(trimmed);
|
|
28741
29638
|
}
|
|
28742
29639
|
function messageIsPlaceholderOnly(message) {
|
|
28743
29640
|
if (!message || typeof message !== "object")
|
|
@@ -28746,7 +29643,7 @@ function messageIsPlaceholderOnly(message) {
|
|
|
28746
29643
|
if (msg.role !== "assistant")
|
|
28747
29644
|
return false;
|
|
28748
29645
|
if (typeof msg.content === "string")
|
|
28749
|
-
return
|
|
29646
|
+
return msg.content.trim().length === 0 || isPiMarkerOnlyText(msg.content);
|
|
28750
29647
|
if (!Array.isArray(msg.content))
|
|
28751
29648
|
return false;
|
|
28752
29649
|
if (msg.content.length === 0)
|
|
@@ -28756,12 +29653,11 @@ function messageIsPlaceholderOnly(message) {
|
|
|
28756
29653
|
if (!part || typeof part !== "object")
|
|
28757
29654
|
return false;
|
|
28758
29655
|
const p = part;
|
|
28759
|
-
|
|
28760
|
-
|
|
28761
|
-
if (typeof p.text !== "string")
|
|
29656
|
+
const text = p.type === "text" ? p.text : p.type === "thinking" ? p.thinking : null;
|
|
29657
|
+
if (typeof text !== "string")
|
|
28762
29658
|
return false;
|
|
28763
29659
|
sawVisibleContent = true;
|
|
28764
|
-
if (!
|
|
29660
|
+
if (text.trim().length > 0 && !isPiMarkerOnlyText(text))
|
|
28765
29661
|
return false;
|
|
28766
29662
|
}
|
|
28767
29663
|
return sawVisibleContent;
|
|
@@ -28900,7 +29796,7 @@ function stripPiProcessedImages(args) {
|
|
|
28900
29796
|
}
|
|
28901
29797
|
|
|
28902
29798
|
// src/system-prompt.ts
|
|
28903
|
-
import { createHash as
|
|
29799
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
28904
29800
|
var MAGIC_CONTEXT_MARKER = "## Magic Context";
|
|
28905
29801
|
var stickyDateBySession = new Map;
|
|
28906
29802
|
function buildMagicContextBlock(opts) {
|
|
@@ -28931,7 +29827,7 @@ function processSystemPromptForCache(args) {
|
|
|
28931
29827
|
const liveDate = dateMatch ? dateMatch[0] : null;
|
|
28932
29828
|
const stickyDate = stickyDateBySession.get(sessionId);
|
|
28933
29829
|
const stableCandidate = liveDate && stickyDate && liveDate !== stickyDate ? systemPrompt.replace(DATE_PATTERN, stickyDate) : systemPrompt;
|
|
28934
|
-
const stableCandidateHash =
|
|
29830
|
+
const stableCandidateHash = createHash14("md5").update(promptSurfaceHashMaterial(stableCandidate, args.promptSurfacePreset)).digest("hex");
|
|
28935
29831
|
const contentOrPresetChanged = !isFirstHash && stableCandidateHash !== previousHash;
|
|
28936
29832
|
const dateMayAdvance = isCacheBusting || contentOrPresetChanged;
|
|
28937
29833
|
if (liveDate && !stickyDate) {
|
|
@@ -28945,7 +29841,7 @@ function processSystemPromptForCache(args) {
|
|
|
28945
29841
|
sessionLog(sessionId, `system prompt date frozen: real=${liveDate}, using=${stickyDate} (cache-stable pass)`);
|
|
28946
29842
|
}
|
|
28947
29843
|
}
|
|
28948
|
-
const currentHash =
|
|
29844
|
+
const currentHash = createHash14("md5").update(promptSurfaceHashMaterial(frozenPrompt, args.promptSurfacePreset)).digest("hex");
|
|
28949
29845
|
const hashChanged = !isFirstHash && currentHash !== previousHash;
|
|
28950
29846
|
if (hashChanged) {
|
|
28951
29847
|
sessionLog(sessionId, `system prompt hash changed: ${previousHash} → ${currentHash} (len=${frozenPrompt.length})`);
|
|
@@ -29651,6 +30547,7 @@ var deferredHistoryRefreshSessions = new Set;
|
|
|
29651
30547
|
var deferredMaterializationSessions = new Set;
|
|
29652
30548
|
var sessionsByProject = new Map;
|
|
29653
30549
|
var lastSeenProjectIdentityBySession = new Map;
|
|
30550
|
+
var persistedProjectIdentityBySession = new Map;
|
|
29654
30551
|
var rawMessageProviderUnregistersBySession = new Map;
|
|
29655
30552
|
var activeContextHandlerSessions = new Set;
|
|
29656
30553
|
var lastHeuristicsTurnIdBySession = new Map;
|
|
@@ -29872,9 +30769,10 @@ function updateSessionProjectTracking(sessionId, projectIdentity, db) {
|
|
|
29872
30769
|
sessionsByProject.delete(prev);
|
|
29873
30770
|
clearPiSystemPromptSession(sessionId);
|
|
29874
30771
|
}
|
|
29875
|
-
if (db &&
|
|
30772
|
+
if (db && persistedProjectIdentityBySession.get(sessionId) !== projectIdentity) {
|
|
29876
30773
|
try {
|
|
29877
30774
|
recordSessionProjectIdentity(db, sessionId, projectIdentity);
|
|
30775
|
+
persistedProjectIdentityBySession.set(sessionId, projectIdentity);
|
|
29878
30776
|
} catch {}
|
|
29879
30777
|
}
|
|
29880
30778
|
trackSessionForProject(projectIdentity, sessionId);
|
|
@@ -31233,6 +32131,10 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
31233
32131
|
log("[magic-context][pi] registered context handler (tagging + drops + nudges)");
|
|
31234
32132
|
}
|
|
31235
32133
|
var inFlightHistorian = new Map;
|
|
32134
|
+
var historianAbortControllers = new Map;
|
|
32135
|
+
function abortInFlightHistorians(sessionId) {
|
|
32136
|
+
historianAbortControllers.get(sessionId)?.abort();
|
|
32137
|
+
}
|
|
31236
32138
|
async function awaitInFlightHistorians(sessionId) {
|
|
31237
32139
|
const runs = sessionId ? [inFlightHistorian.get(sessionId)].filter((run) => run !== undefined) : [...inFlightHistorian.values()];
|
|
31238
32140
|
if (runs.length === 0)
|
|
@@ -31345,7 +32247,11 @@ function spawnPiHistorianRun(args) {
|
|
|
31345
32247
|
fallbackModelId
|
|
31346
32248
|
} = args;
|
|
31347
32249
|
const holderId = crypto3.randomUUID();
|
|
32250
|
+
const controller = new AbortController;
|
|
32251
|
+
historianAbortControllers.set(sessionId, controller);
|
|
31348
32252
|
const runPromise = (async () => {
|
|
32253
|
+
if (controller.signal.aborted)
|
|
32254
|
+
return;
|
|
31349
32255
|
const lease = acquireCompartmentLease(db, sessionId, holderId);
|
|
31350
32256
|
if (!lease) {
|
|
31351
32257
|
sessionLog(sessionId, "historian skipped: compartment lease held by another process");
|
|
@@ -31366,6 +32272,7 @@ function spawnPiHistorianRun(args) {
|
|
|
31366
32272
|
appendCompaction: resolvePiAppendCompaction(ctx),
|
|
31367
32273
|
readBranchEntries: resolvePiReadBranchEntries(ctx),
|
|
31368
32274
|
runner: historian.runner,
|
|
32275
|
+
signal: controller.signal,
|
|
31369
32276
|
historianModel: historian.model,
|
|
31370
32277
|
fallbackModels: historian.fallbackModels,
|
|
31371
32278
|
fallbackModelId,
|
|
@@ -31434,6 +32341,8 @@ function spawnPiHistorianRun(args) {
|
|
|
31434
32341
|
}).finally(() => {
|
|
31435
32342
|
try {
|
|
31436
32343
|
inFlightHistorian.delete(sessionId);
|
|
32344
|
+
if (historianAbortControllers.get(sessionId) === controller)
|
|
32345
|
+
historianAbortControllers.delete(sessionId);
|
|
31437
32346
|
unregister();
|
|
31438
32347
|
if (isContextHandlerSessionActive(sessionId)) {
|
|
31439
32348
|
historian.onStatusChange?.(ctx, sessionId);
|
|
@@ -31636,6 +32545,8 @@ Historian previously failed ${failureCount} time(s), so Magic Context is retryin
|
|
|
31636
32545
|
}
|
|
31637
32546
|
return;
|
|
31638
32547
|
}
|
|
32548
|
+
if (!ctx.hasUI)
|
|
32549
|
+
return;
|
|
31639
32550
|
triggered = true;
|
|
31640
32551
|
sessionLog(sessionId, `historian trigger fired (reason=${trigger.reason ?? "unknown"}) usage=${usage.percentage.toFixed(1)}% — spawning subagent`);
|
|
31641
32552
|
spawnPiHistorianRun({
|
|
@@ -32831,6 +33742,7 @@ function clearContextHandlerSession(sessionId) {
|
|
|
32831
33742
|
lastHeuristicsTurnIdBySession.delete(sessionId);
|
|
32832
33743
|
routinePressureAppliedBySession.delete(sessionId);
|
|
32833
33744
|
lastSeenProjectIdentityBySession.delete(sessionId);
|
|
33745
|
+
persistedProjectIdentityBySession.delete(sessionId);
|
|
32834
33746
|
for (const [projectIdentity, sessions] of sessionsByProject) {
|
|
32835
33747
|
sessions.delete(sessionId);
|
|
32836
33748
|
if (sessions.size === 0)
|
|
@@ -32985,6 +33897,7 @@ function promoteRecompStagingWithM0Mutation(db, sessionId, holderId) {
|
|
|
32985
33897
|
finished = true;
|
|
32986
33898
|
return null;
|
|
32987
33899
|
}
|
|
33900
|
+
deleteChunkEmbedBackoffForSession(db, sessionId);
|
|
32988
33901
|
db.prepare("DELETE FROM compartments WHERE session_id = ?").run(sessionId);
|
|
32989
33902
|
db.prepare("DELETE FROM session_facts WHERE session_id = ?").run(sessionId);
|
|
32990
33903
|
insertRecompCompartmentRows(db, sessionId, staging.compartments, now);
|
|
@@ -32999,6 +33912,7 @@ function promoteRecompStagingWithM0Mutation(db, sessionId, holderId) {
|
|
|
32999
33912
|
clearCachedM0M1(db, sessionId);
|
|
33000
33913
|
db.exec("COMMIT");
|
|
33001
33914
|
finished = true;
|
|
33915
|
+
invalidateAutoEmbedSession(sessionId);
|
|
33002
33916
|
logSlowWriteTransaction("historian-publish:recomp", transactionStartedAt);
|
|
33003
33917
|
return { compartments: staging.compartments, facts: staging.facts };
|
|
33004
33918
|
} finally {
|
|
@@ -35025,7 +35939,7 @@ function buildStatusView(source, options) {
|
|
|
35025
35939
|
// package.json
|
|
35026
35940
|
var package_default = {
|
|
35027
35941
|
name: "@cortexkit/pi-magic-context",
|
|
35028
|
-
version: "0.44.
|
|
35942
|
+
version: "0.44.3",
|
|
35029
35943
|
type: "module",
|
|
35030
35944
|
description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
|
|
35031
35945
|
main: "dist/index.js",
|
|
@@ -35073,7 +35987,6 @@ var package_default = {
|
|
|
35073
35987
|
"comment-json": "^5.0.0",
|
|
35074
35988
|
"onnxruntime-web": "1.26.0-dev.20260416-b7804b056c",
|
|
35075
35989
|
"quickjs-emscripten": "^0.32.0",
|
|
35076
|
-
typebox: "^1.3.1",
|
|
35077
35990
|
zod: "^4.1.8"
|
|
35078
35991
|
},
|
|
35079
35992
|
optionalDependencies: {
|
|
@@ -35093,11 +36006,13 @@ var package_default = {
|
|
|
35093
36006
|
"pi-ai-086": "npm:@earendil-works/pi-ai@0.86.0",
|
|
35094
36007
|
"pi-coding-agent-086": "npm:@earendil-works/pi-coding-agent@0.86.0",
|
|
35095
36008
|
"pi-coding-agent-087": "npm:@earendil-works/pi-coding-agent@0.87.1",
|
|
36009
|
+
typebox: "^1.3.1",
|
|
35096
36010
|
typescript: "^5.8.0"
|
|
35097
36011
|
},
|
|
35098
36012
|
peerDependencies: {
|
|
35099
|
-
"@earendil-works/pi-coding-agent": "
|
|
35100
|
-
"@earendil-works/pi-tui": "
|
|
36013
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
36014
|
+
"@earendil-works/pi-tui": "*",
|
|
36015
|
+
typebox: "*"
|
|
35101
36016
|
},
|
|
35102
36017
|
peerDependenciesMeta: {
|
|
35103
36018
|
"@earendil-works/pi-coding-agent": {
|
|
@@ -35105,6 +36020,9 @@ var package_default = {
|
|
|
35105
36020
|
},
|
|
35106
36021
|
"@earendil-works/pi-tui": {
|
|
35107
36022
|
optional: true
|
|
36023
|
+
},
|
|
36024
|
+
typebox: {
|
|
36025
|
+
optional: true
|
|
35108
36026
|
}
|
|
35109
36027
|
},
|
|
35110
36028
|
exports: {
|
|
@@ -35143,6 +36061,11 @@ async function showStatusDialog(pi, ctx, deps) {
|
|
|
35143
36061
|
overlayOptions: { anchor: "center", width: 78 }
|
|
35144
36062
|
});
|
|
35145
36063
|
}
|
|
36064
|
+
var openStatusDialogs = new Set;
|
|
36065
|
+
function stopStatusDialogRefresh() {
|
|
36066
|
+
for (const dialog of openStatusDialogs)
|
|
36067
|
+
dialog.close();
|
|
36068
|
+
}
|
|
35146
36069
|
|
|
35147
36070
|
class StatusDialogComponent {
|
|
35148
36071
|
props;
|
|
@@ -35152,6 +36075,7 @@ class StatusDialogComponent {
|
|
|
35152
36075
|
constructor(props) {
|
|
35153
36076
|
this.props = props;
|
|
35154
36077
|
this.detail = buildPiStatusDetail(props.pi, props.ctx, props.deps, props.sessionId);
|
|
36078
|
+
openStatusDialogs.add(this);
|
|
35155
36079
|
this.refreshTimer = setInterval(() => {
|
|
35156
36080
|
if (this.closed)
|
|
35157
36081
|
return;
|
|
@@ -35169,11 +36093,7 @@ class StatusDialogComponent {
|
|
|
35169
36093
|
close() {
|
|
35170
36094
|
if (this.closed)
|
|
35171
36095
|
return;
|
|
35172
|
-
this.
|
|
35173
|
-
if (this.refreshTimer) {
|
|
35174
|
-
clearInterval(this.refreshTimer);
|
|
35175
|
-
this.refreshTimer = null;
|
|
35176
|
-
}
|
|
36096
|
+
this.dispose();
|
|
35177
36097
|
this.props.done(undefined);
|
|
35178
36098
|
}
|
|
35179
36099
|
invalidate() {}
|
|
@@ -35183,6 +36103,8 @@ class StatusDialogComponent {
|
|
|
35183
36103
|
return drawBorder(inner, width, this.props.theme);
|
|
35184
36104
|
}
|
|
35185
36105
|
dispose() {
|
|
36106
|
+
this.closed = true;
|
|
36107
|
+
openStatusDialogs.delete(this);
|
|
35186
36108
|
if (this.refreshTimer) {
|
|
35187
36109
|
clearInterval(this.refreshTimer);
|
|
35188
36110
|
this.refreshTimer = null;
|
|
@@ -35726,7 +36648,10 @@ No active Pi session is available.`,
|
|
|
35726
36648
|
});
|
|
35727
36649
|
return;
|
|
35728
36650
|
}
|
|
35729
|
-
const currentDeps =
|
|
36651
|
+
const currentDeps = {
|
|
36652
|
+
...deps.resolveRuntimeDeps?.(ctx) ?? deps,
|
|
36653
|
+
shutdownSignal: deps.shutdownSignal
|
|
36654
|
+
};
|
|
35730
36655
|
if (currentDeps.compactionOff) {
|
|
35731
36656
|
sendStatus({
|
|
35732
36657
|
title: "/ctx-wrapup",
|
|
@@ -35859,6 +36784,8 @@ No runnable wrapup boundary is available yet; wrapped up 0 messages into 0 compa
|
|
|
35859
36784
|
console.warn(`[magic-context][pi] /ctx-wrapup marker renewal failed for ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
35860
36785
|
}
|
|
35861
36786
|
}, 60000);
|
|
36787
|
+
const stopRenewal = () => clearInterval(renewal);
|
|
36788
|
+
deps.shutdownSignal?.addEventListener("abort", stopRenewal, { once: true });
|
|
35862
36789
|
try {
|
|
35863
36790
|
sendStatus({
|
|
35864
36791
|
title: "/ctx-wrapup",
|
|
@@ -35925,7 +36852,7 @@ Eligible history is about ${initialPlan.snapshot.trueRawEligibleTokens.toLocaleS
|
|
|
35925
36852
|
Chunk ${chunkIndex}: wrapping messages ${plan.snapshot.offset}-${plan.snapshot.eligibleEndOrdinal - 1} (~${plan.snapshot.trueRawEligibleTokens.toLocaleString()} eligible tokens remain).`,
|
|
35926
36853
|
level: "info"
|
|
35927
36854
|
});
|
|
35928
|
-
const leaseResult = await acquireCompartmentLeaseEventually(deps.db, sessionId, renewWrapupMarker, resolveWrapupLeaseWaitTimeout(deps));
|
|
36855
|
+
const leaseResult = await acquireCompartmentLeaseEventually(deps.db, sessionId, renewWrapupMarker, resolveWrapupLeaseWaitTimeout(deps), deps.shutdownSignal);
|
|
35929
36856
|
if (!leaseResult.ok) {
|
|
35930
36857
|
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.";
|
|
35931
36858
|
break;
|
|
@@ -35938,6 +36865,10 @@ Chunk ${chunkIndex}: wrapping messages ${plan.snapshot.offset}-${plan.snapshot.e
|
|
|
35938
36865
|
console.warn(`[magic-context][pi] /ctx-wrapup compartment lease renewal failed for ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
35939
36866
|
}
|
|
35940
36867
|
}, COMPARTMENT_LEASE_RENEWAL_MS);
|
|
36868
|
+
const stopLeaseRenewal = () => clearInterval(leaseRenewal);
|
|
36869
|
+
deps.shutdownSignal?.addEventListener("abort", stopLeaseRenewal, {
|
|
36870
|
+
once: true
|
|
36871
|
+
});
|
|
35941
36872
|
try {
|
|
35942
36873
|
const runHistorian = deps.runPiHistorianForWrapup ?? runPiHistorian;
|
|
35943
36874
|
await runHistorian({
|
|
@@ -35946,6 +36877,7 @@ Chunk ${chunkIndex}: wrapping messages ${plan.snapshot.offset}-${plan.snapshot.e
|
|
|
35946
36877
|
directory: ctx.cwd,
|
|
35947
36878
|
provider,
|
|
35948
36879
|
runner: deps.runner,
|
|
36880
|
+
signal: deps.shutdownSignal,
|
|
35949
36881
|
historianModel: deps.historianModel,
|
|
35950
36882
|
fallbackModels: deps.historianFallbacks,
|
|
35951
36883
|
fallbackModelId: modelKey,
|
|
@@ -35989,6 +36921,7 @@ Chunk ${chunkIndex}: wrapping messages ${plan.snapshot.offset}-${plan.snapshot.e
|
|
|
35989
36921
|
});
|
|
35990
36922
|
} finally {
|
|
35991
36923
|
clearInterval(leaseRenewal);
|
|
36924
|
+
deps.shutdownSignal?.removeEventListener("abort", stopLeaseRenewal);
|
|
35992
36925
|
releaseCompartmentLeaseBestEffort(deps.db, sessionId, leaseHolder, sessionLog);
|
|
35993
36926
|
}
|
|
35994
36927
|
const afterEnd = getLastCompartmentEndMessage(deps.db, sessionId);
|
|
@@ -36018,6 +36951,7 @@ Wrapped up ${messagesWrapped} messages into ${compartmentsCreated} compartments;
|
|
|
36018
36951
|
Wrapped up ${messagesWrapped} messages into ${compartmentsCreated} compartments. The compacted history is queued and materializes on your next message.`;
|
|
36019
36952
|
} finally {
|
|
36020
36953
|
clearInterval(renewal);
|
|
36954
|
+
deps.shutdownSignal?.removeEventListener("abort", stopRenewal);
|
|
36021
36955
|
releaseWrapupInProgress(deps.db, sessionId, holderId);
|
|
36022
36956
|
}
|
|
36023
36957
|
} finally {
|
|
@@ -36039,10 +36973,12 @@ function resolvePiContextLimit(ctx, db, sessionId) {
|
|
|
36039
36973
|
detectedContextLimit
|
|
36040
36974
|
}) ?? 128000;
|
|
36041
36975
|
}
|
|
36042
|
-
async function acquireCompartmentLeaseEventually(db, sessionId, renewWrapupMarker, maxWaitMs) {
|
|
36976
|
+
async function acquireCompartmentLeaseEventually(db, sessionId, renewWrapupMarker, maxWaitMs, signal) {
|
|
36043
36977
|
const waitStartedAt = Date.now();
|
|
36044
36978
|
const remainingMs = () => Math.max(0, waitStartedAt + maxWaitMs - Date.now());
|
|
36045
36979
|
for (;; ) {
|
|
36980
|
+
if (signal?.aborted)
|
|
36981
|
+
return { ok: false, reason: "ownership_lost" };
|
|
36046
36982
|
if (remainingMs() <= 0)
|
|
36047
36983
|
return { ok: false, reason: "timeout" };
|
|
36048
36984
|
const holderId = crypto4.randomUUID();
|
|
@@ -36051,7 +36987,17 @@ async function acquireCompartmentLeaseEventually(db, sessionId, renewWrapupMarke
|
|
|
36051
36987
|
return { ok: true, holderId };
|
|
36052
36988
|
if (!renewWrapupMarker({}))
|
|
36053
36989
|
return { ok: false, reason: "ownership_lost" };
|
|
36054
|
-
await new Promise((resolve) =>
|
|
36990
|
+
await new Promise((resolve) => {
|
|
36991
|
+
const finish = () => {
|
|
36992
|
+
clearTimeout(timer);
|
|
36993
|
+
signal?.removeEventListener("abort", finish);
|
|
36994
|
+
resolve();
|
|
36995
|
+
};
|
|
36996
|
+
const timer = setTimeout(finish, Math.min(LEASE_WAIT_MS, remainingMs()));
|
|
36997
|
+
signal?.addEventListener("abort", finish, { once: true });
|
|
36998
|
+
if (signal?.aborted)
|
|
36999
|
+
finish();
|
|
37000
|
+
});
|
|
36055
37001
|
}
|
|
36056
37002
|
}
|
|
36057
37003
|
function estimateChunks(tokens, chunkTokens) {
|
|
@@ -36075,6 +37021,32 @@ function readBranchEntries(ctx) {
|
|
|
36075
37021
|
return Array.isArray(branch?.entries) ? branch.entries : [];
|
|
36076
37022
|
}
|
|
36077
37023
|
|
|
37024
|
+
// src/dreamer/session-activity-pi.ts
|
|
37025
|
+
function observePiMessageActivity(db, sessionId, timestamp) {
|
|
37026
|
+
observeSessionActivity(db, sessionId, timestamp);
|
|
37027
|
+
}
|
|
37028
|
+
function latestPiMessageTime(path) {
|
|
37029
|
+
let latest = 0;
|
|
37030
|
+
for (const entry of sessionEntries(path)) {
|
|
37031
|
+
const row = entry;
|
|
37032
|
+
if (row.type === "message" && typeof row.message?.timestamp === "number")
|
|
37033
|
+
latest = Math.max(latest, row.message.timestamp);
|
|
37034
|
+
}
|
|
37035
|
+
return latest || undefined;
|
|
37036
|
+
}
|
|
37037
|
+
async function backfillPiSessionActivity(db, harness, paths) {
|
|
37038
|
+
await backfillSessionActivity(db, harness, (sessionId) => {
|
|
37039
|
+
const path = paths.get(sessionId);
|
|
37040
|
+
if (!path)
|
|
37041
|
+
return;
|
|
37042
|
+
try {
|
|
37043
|
+
return latestPiMessageTime(path);
|
|
37044
|
+
} catch {
|
|
37045
|
+
return;
|
|
37046
|
+
}
|
|
37047
|
+
});
|
|
37048
|
+
}
|
|
37049
|
+
|
|
36078
37050
|
// src/dropped-input-guard-pi.ts
|
|
36079
37051
|
function createPiDroppedInputGuard(options = {}) {
|
|
36080
37052
|
const guard = createDroppedInputGuard({
|
|
@@ -36273,6 +37245,8 @@ function stripTagPrefixFromAssistantMessage(message) {
|
|
|
36273
37245
|
var PI_HARNESS_DETECTION = await resolvePiHarnessDetection();
|
|
36274
37246
|
var PI_HARNESS_KIND = PI_HARNESS_DETECTION.kind;
|
|
36275
37247
|
var PREFIX2 = `[magic-context][${PI_HARNESS_KIND}]`;
|
|
37248
|
+
var databaseExitHookRegistered = false;
|
|
37249
|
+
var activePiRuntimes = new Set;
|
|
36276
37250
|
var PI_BOOT_DEADLINE_MS = 15000;
|
|
36277
37251
|
var PI_CHILD_INIT_CONTEXT = Symbol.for("magic-context.pi.child-init-context");
|
|
36278
37252
|
var SUBAGENT_CHILD_SESSION_CREATED = "subagents:child:session-created";
|
|
@@ -36710,6 +37684,7 @@ async function src_default(pi) {
|
|
|
36710
37684
|
const bootProjectDir = process.cwd();
|
|
36711
37685
|
ensureConfigLocationsMigrated(bootProjectDir);
|
|
36712
37686
|
const bootConfig = loadPiConfig({ cwd: bootProjectDir });
|
|
37687
|
+
setHomeProjectPermission(bootConfig.config.allow_home_project);
|
|
36713
37688
|
if (!bootConfig.config.enabled) {
|
|
36714
37689
|
info("plugin DISABLED via config (enabled: false) — skipping registration");
|
|
36715
37690
|
return;
|
|
@@ -36720,6 +37695,10 @@ async function src_default(pi) {
|
|
|
36720
37695
|
cacheSizeMb: bootConfig.config.sqlite.cache_size_mb,
|
|
36721
37696
|
mmapSizeMb: bootConfig.config.sqlite.mmap_size_mb
|
|
36722
37697
|
});
|
|
37698
|
+
if (!databaseExitHookRegistered) {
|
|
37699
|
+
process.once("exit", closeDatabase);
|
|
37700
|
+
databaseExitHookRegistered = true;
|
|
37701
|
+
}
|
|
36723
37702
|
const storageDir = getMagicContextStorageDir();
|
|
36724
37703
|
const dbPath = join11(storageDir, "context.db");
|
|
36725
37704
|
let openFailureCause = null;
|
|
@@ -36793,6 +37772,13 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
36793
37772
|
const offset = afterSessionId === null ? 0 : sessions.findIndex((session) => session.sessionId === afterSessionId) + 1;
|
|
36794
37773
|
return sessions.slice(offset, offset + limit);
|
|
36795
37774
|
});
|
|
37775
|
+
const api = await loadDefaultPiSessionApi();
|
|
37776
|
+
const paths = new Map;
|
|
37777
|
+
for (const session of await api.listSessions()) {
|
|
37778
|
+
if (typeof session.id === "string" && typeof session.path === "string")
|
|
37779
|
+
paths.set(session.id, session.path);
|
|
37780
|
+
}
|
|
37781
|
+
await backfillPiSessionActivity(database, PI_HARNESS_KIND, paths);
|
|
36796
37782
|
} catch (err) {
|
|
36797
37783
|
warn(`[session-projects] background runner failed: ${err}`);
|
|
36798
37784
|
}
|
|
@@ -36802,6 +37788,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
36802
37788
|
const projectDir = process.cwd();
|
|
36803
37789
|
const seenDreamerProjectIdentities = new Set;
|
|
36804
37790
|
const dreamerRegistrationOwner = {};
|
|
37791
|
+
activePiRuntimes.add(dreamerRegistrationOwner);
|
|
36805
37792
|
const commandLifecycleController = new AbortController;
|
|
36806
37793
|
registerCtxStatusLifecycleSignal(pi, commandLifecycleController.signal);
|
|
36807
37794
|
let sessionShuttingDown = false;
|
|
@@ -37055,7 +38042,8 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
37055
38042
|
projectDepsByDir.delete(ctx.cwd);
|
|
37056
38043
|
const current = resolveCurrentProjectDeps(ctx);
|
|
37057
38044
|
activeModelRegistry = ctx.modelRegistry;
|
|
37058
|
-
|
|
38045
|
+
if (ctx.hasUI)
|
|
38046
|
+
syncDreamerProjectRegistration(current, ctx.modelRegistry);
|
|
37059
38047
|
syncCtxMemoryToolEnabled(pi, current.config.memory.enabled);
|
|
37060
38048
|
const failuresToShow = claimConfigParseFailuresOnce("pi", current.configParseFailures);
|
|
37061
38049
|
if (ctx.hasUI && failuresToShow.length > 0) {
|
|
@@ -37190,6 +38178,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
37190
38178
|
});
|
|
37191
38179
|
info("registered /ctx-recomp");
|
|
37192
38180
|
registerCtxWrapupCommand(pi, {
|
|
38181
|
+
shutdownSignal: commandLifecycleController.signal,
|
|
37193
38182
|
db,
|
|
37194
38183
|
runner: wrapupRunner,
|
|
37195
38184
|
historianModel: bootProjectDeps.historianConfig?.model,
|
|
@@ -37241,7 +38230,10 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
37241
38230
|
dreamerEnabled: bootProjectDeps.dreamerEnabled,
|
|
37242
38231
|
resolveDreamerEnabled: (ctx) => resolveCurrentProjectDeps(ctx).dreamerEnabled,
|
|
37243
38232
|
onProjectSeen: (identity) => seenDreamerProjectIdentities.add(identity),
|
|
37244
|
-
ensureRegistered: (ctx) =>
|
|
38233
|
+
ensureRegistered: (ctx) => {
|
|
38234
|
+
if (ctx.hasUI)
|
|
38235
|
+
syncDreamerProjectRegistration(resolveCurrentProjectDeps(ctx), ctx.modelRegistry);
|
|
38236
|
+
},
|
|
37245
38237
|
registrationOwner: dreamerRegistrationOwner
|
|
37246
38238
|
});
|
|
37247
38239
|
info("registered /ctx-dream");
|
|
@@ -37260,7 +38252,6 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
37260
38252
|
info("registered /ctx-embed");
|
|
37261
38253
|
const dreamerConfig = bootProjectDeps.dreamerConfig;
|
|
37262
38254
|
if (dreamerConfig) {
|
|
37263
|
-
syncDreamerProjectRegistration(bootProjectDeps);
|
|
37264
38255
|
info(`registered dreamer (${summarizeDreamSchedule(dreamerConfig)})`);
|
|
37265
38256
|
} else {
|
|
37266
38257
|
info(bootProjectDeps.dreamerEnabled ? "registered dreamer: DISABLED (no dreamer config)" : "registered dreamer: DISABLED (dreamer.disable=true or no dreamer config)");
|
|
@@ -37293,7 +38284,8 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
37293
38284
|
const effectiveConfig = effectiveProjectDeps.config;
|
|
37294
38285
|
try {
|
|
37295
38286
|
activeModelRegistry = ctx.modelRegistry;
|
|
37296
|
-
|
|
38287
|
+
if (ctx.hasUI)
|
|
38288
|
+
syncDreamerProjectRegistration(effectiveProjectDeps, ctx.modelRegistry);
|
|
37297
38289
|
} catch (err) {
|
|
37298
38290
|
warn("before_agent_start: dreamer registration sync threw:", err);
|
|
37299
38291
|
}
|
|
@@ -37389,6 +38381,8 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
37389
38381
|
} catch (err) {
|
|
37390
38382
|
log(`agent_end: channel2 delivery skipped: ${String(err)}`);
|
|
37391
38383
|
}
|
|
38384
|
+
if (!ctx.hasUI)
|
|
38385
|
+
flushLogger();
|
|
37392
38386
|
});
|
|
37393
38387
|
pi.on("tool_execution_start", async (event, ctx) => {
|
|
37394
38388
|
try {
|
|
@@ -37469,6 +38463,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
37469
38463
|
if (typeof sessionId !== "string" || sessionId.length === 0)
|
|
37470
38464
|
return;
|
|
37471
38465
|
const endedMsg = event.message;
|
|
38466
|
+
observePiMessageActivity(db, sessionId, endedMsg?.timestamp);
|
|
37472
38467
|
if (endedMsg?.role === "assistant" && typeof endedMsg.id === "string" && endedMsg.id.length > 0) {
|
|
37473
38468
|
const messageId = endedMsg.id;
|
|
37474
38469
|
scheduleIncrementalIndex(db, sessionId, messageId, () => {
|
|
@@ -37549,6 +38544,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
37549
38544
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
37550
38545
|
sessionShuttingDown = true;
|
|
37551
38546
|
commandLifecycleController.abort();
|
|
38547
|
+
stopStatusDialogRefresh();
|
|
37552
38548
|
const SHUTDOWN_DRAIN_MS = 5000;
|
|
37553
38549
|
const sessionId = resolveSessionId(ctx);
|
|
37554
38550
|
try {
|
|
@@ -37562,6 +38558,8 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
37562
38558
|
warn("shutdown: unregisterPiDreamerProject threw:", err);
|
|
37563
38559
|
}
|
|
37564
38560
|
if (sessionId) {
|
|
38561
|
+
abortInFlightHistorians(sessionId);
|
|
38562
|
+
abortInFlightRecomps(sessionId);
|
|
37565
38563
|
try {
|
|
37566
38564
|
await withTimeout(awaitInFlightHistorians(sessionId), SHUTDOWN_DRAIN_MS);
|
|
37567
38565
|
} catch (err) {
|
|
@@ -37574,6 +38572,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
37574
38572
|
}
|
|
37575
38573
|
abortInFlightRecomps(sessionId);
|
|
37576
38574
|
}
|
|
38575
|
+
abortInFlightDreamers(dreamerRegistrationOwner);
|
|
37577
38576
|
try {
|
|
37578
38577
|
await withTimeout(awaitInFlightDreamers(dreamerRegistrationOwner), SHUTDOWN_DRAIN_MS);
|
|
37579
38578
|
} catch (err) {
|
|
@@ -37588,6 +38587,9 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
37588
38587
|
clearContextHandlerSession(sessionId);
|
|
37589
38588
|
}
|
|
37590
38589
|
} catch {}
|
|
38590
|
+
activePiRuntimes.delete(dreamerRegistrationOwner);
|
|
38591
|
+
if (activePiRuntimes.size === 0)
|
|
38592
|
+
unregisterPiProjectEmbeddings(db);
|
|
37591
38593
|
});
|
|
37592
38594
|
pi.on("session_before_switch", (_event, ctx) => {
|
|
37593
38595
|
try {
|