@cortexkit/pi-magic-context 0.33.1 → 0.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-e3kftg52.js → index-ekzg3w5y.js} +846 -194
- package/dist/{rpc-notifications-td0amw6w.js → index-hw34xmzk.js} +2 -10
- package/dist/index.js +1720 -903
- package/dist/rpc-notifications-s2rw51c6.js +17 -0
- package/dist/subagent-entry.js +8 -4
- package/package.json +69 -69
- /package/dist/{index-v7fmc1s2.js → index-3pnd4qh9.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AGE_RECLAIM_MIN_TOKENS,
|
|
3
|
+
BoundedSessionMap,
|
|
4
|
+
COMPACTION_ENABLED_PATH,
|
|
3
5
|
COMPARTMENT_LEASE_RENEWAL_MS,
|
|
4
6
|
COMPARTMENT_RENDER_EPOCH,
|
|
5
7
|
DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE,
|
|
@@ -10,6 +12,8 @@ import {
|
|
|
10
12
|
Database,
|
|
11
13
|
GIT_SWEEP_LEASE_RENEWAL_MS,
|
|
12
14
|
GLOBAL_USER_PROFILE_PROJECT_PATH,
|
|
15
|
+
LATEST_SUPPORTED_VERSION,
|
|
16
|
+
MAX_EXECUTE_THRESHOLD,
|
|
13
17
|
PRIMER_CANDIDATE_MAX_AGE_MS,
|
|
14
18
|
PRIMER_CANDIDATE_TTL_MS,
|
|
15
19
|
ProjectIdentityError,
|
|
@@ -57,6 +61,7 @@ import {
|
|
|
57
61
|
clearInjectionCache,
|
|
58
62
|
clearMemoryVerifications,
|
|
59
63
|
clearPendingCompactionMarkerStateIf,
|
|
64
|
+
clearPendingOps,
|
|
60
65
|
clearPendingPiCompactionMarkerStateIf,
|
|
61
66
|
clearPersistedReasoningWatermark,
|
|
62
67
|
clearPersistedTodoSyntheticAnchor,
|
|
@@ -89,6 +94,7 @@ import {
|
|
|
89
94
|
encodeCachedM0UpgradeIdentity,
|
|
90
95
|
enqueueShadowEmbeddingItems,
|
|
91
96
|
ensureProjectRegisteredFromPiDirectory,
|
|
97
|
+
escalationBands,
|
|
92
98
|
escapeXmlAttr,
|
|
93
99
|
escapeXmlContent,
|
|
94
100
|
estimateTokens,
|
|
@@ -109,6 +115,7 @@ import {
|
|
|
109
115
|
getAutoSearchHintDecisions,
|
|
110
116
|
getCachedAbsoluteMessageCount,
|
|
111
117
|
getChannel2NudgeState,
|
|
118
|
+
getCompactionModeRecord,
|
|
112
119
|
getCompartments,
|
|
113
120
|
getCompartmentsByEndMessageId,
|
|
114
121
|
getContextStoreUuid,
|
|
@@ -141,6 +148,7 @@ import {
|
|
|
141
148
|
getMemoryVerifications,
|
|
142
149
|
getMessageIndexReconciliationStartOrdinal,
|
|
143
150
|
getMessageIndexSourceIdentity,
|
|
151
|
+
getMigrationOnOpenRefusal,
|
|
144
152
|
getModuleNoteEvaluationBridge,
|
|
145
153
|
getMuralCueState,
|
|
146
154
|
getNoteLastReadAt,
|
|
@@ -156,6 +164,7 @@ import {
|
|
|
156
164
|
getPendingPiCompactionMarkerState,
|
|
157
165
|
getPendingSmartNotes,
|
|
158
166
|
getPersistedNoteNudge,
|
|
167
|
+
getPersistedSchemaVersion,
|
|
159
168
|
getPersistedTodoSyntheticAnchor,
|
|
160
169
|
getPersistedToolTagAccounting,
|
|
161
170
|
getPrimerCandidatesByIds,
|
|
@@ -191,6 +200,7 @@ import {
|
|
|
191
200
|
hasMeaningfulUserText,
|
|
192
201
|
hasMemoryClassifiedAtColumn,
|
|
193
202
|
hasMemoryShareableColumn,
|
|
203
|
+
hasMuralCueColumns,
|
|
194
204
|
hasPiFallbackMessageTags,
|
|
195
205
|
hasPiFallbackToolOwnerTags,
|
|
196
206
|
incrementHistorianFailure,
|
|
@@ -203,11 +213,13 @@ import {
|
|
|
203
213
|
insertUserMemory,
|
|
204
214
|
insertUserMemoryCandidates,
|
|
205
215
|
invalidateMemory,
|
|
216
|
+
isCompactionEnabled,
|
|
206
217
|
isCompartmentLeaseHeld,
|
|
207
218
|
isDreamerRunnable,
|
|
208
219
|
isEditTool,
|
|
209
220
|
isMessageIndexReconciledThrough,
|
|
210
221
|
isMessageIndexSourceCurrent,
|
|
222
|
+
isMidTurn,
|
|
211
223
|
isMidTurnPi,
|
|
212
224
|
isRecord,
|
|
213
225
|
isSaneLimit,
|
|
@@ -282,7 +294,9 @@ import {
|
|
|
282
294
|
reserveProtectedTailDrainTokens,
|
|
283
295
|
resetLastNudgeCycle,
|
|
284
296
|
resetLastNudgeCycleIfTailShrank,
|
|
297
|
+
resolveCompactionModeRecord,
|
|
285
298
|
resolveGitTopLevel,
|
|
299
|
+
resolveLimit,
|
|
286
300
|
resolveModelRefForPi,
|
|
287
301
|
resolveMuralWire,
|
|
288
302
|
resolvePiStableId,
|
|
@@ -298,6 +312,7 @@ import {
|
|
|
298
312
|
saveSourceContent,
|
|
299
313
|
scheduleAfterBootQuiet,
|
|
300
314
|
setChannel2NudgeState,
|
|
315
|
+
setCompactionModeRecord,
|
|
301
316
|
setDeferredExecutePendingIfAbsent,
|
|
302
317
|
setEmergencyDropSample,
|
|
303
318
|
setLastNudgeLevel,
|
|
@@ -312,6 +327,7 @@ import {
|
|
|
312
327
|
setRecompPartialRange,
|
|
313
328
|
setSessionWorkMetrics,
|
|
314
329
|
setSqlitePragmaConfig,
|
|
330
|
+
setStoragePrivatePermissionEnforcement,
|
|
315
331
|
setTodoSnapshot,
|
|
316
332
|
sourceNameForMemory,
|
|
317
333
|
stableStringify,
|
|
@@ -346,7 +362,10 @@ import {
|
|
|
346
362
|
withMigrationLanguageDirective,
|
|
347
363
|
withRawMessageProvider,
|
|
348
364
|
withRawSessionMessageCache
|
|
349
|
-
} from "./index-
|
|
365
|
+
} from "./index-ekzg3w5y.js";
|
|
366
|
+
import {
|
|
367
|
+
pushNotification
|
|
368
|
+
} from "./index-hw34xmzk.js";
|
|
350
369
|
import {
|
|
351
370
|
ensureCortexKitArtifactGitignore,
|
|
352
371
|
getHarness,
|
|
@@ -425,6 +444,16 @@ var CANONICAL_DREAM_TASKS = [
|
|
|
425
444
|
"promote-primers",
|
|
426
445
|
"refresh-primers"
|
|
427
446
|
];
|
|
447
|
+
function formatDreamTaskBacklogs(backlogs, tasks = CANONICAL_DREAM_TASKS) {
|
|
448
|
+
return tasks.filter((task) => backlogs[task] !== undefined).map((task) => {
|
|
449
|
+
const backlog = backlogs[task];
|
|
450
|
+
return `- ${task}: ${backlog?.pending ?? 0} pending / ${backlog?.total ?? 0} total`;
|
|
451
|
+
}).join(`
|
|
452
|
+
`);
|
|
453
|
+
}
|
|
454
|
+
function processedDreamTaskItems(startPending, endPending) {
|
|
455
|
+
return Math.max(0, startPending - endPending);
|
|
456
|
+
}
|
|
428
457
|
var MEMORY_DOMAIN_TASKS = [
|
|
429
458
|
"map-memories",
|
|
430
459
|
"verify",
|
|
@@ -528,14 +557,41 @@ function isMagicContextHiddenAgentName(agent) {
|
|
|
528
557
|
return true;
|
|
529
558
|
return false;
|
|
530
559
|
}
|
|
560
|
+
var MAX_FORMATTED_BLOCKING_PROCESSES = 8;
|
|
561
|
+
function formatFailClosedBlockingProcesses(processes) {
|
|
562
|
+
const uniqueProcesses = new Map;
|
|
563
|
+
for (const process2 of processes) {
|
|
564
|
+
if (!process2.harness.trim() || !Number.isInteger(process2.pid) || process2.pid <= 0)
|
|
565
|
+
continue;
|
|
566
|
+
uniqueProcesses.set(`${process2.harness}\x00${process2.pid}`, {
|
|
567
|
+
harness: process2.harness.trim(),
|
|
568
|
+
pid: process2.pid
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
const entries = [...uniqueProcesses.values()];
|
|
572
|
+
const visible = entries.slice(0, MAX_FORMATTED_BLOCKING_PROCESSES);
|
|
573
|
+
const rendered = visible.map(({ harness, pid }) => `${harness} (PID ${pid})`);
|
|
574
|
+
const omitted = entries.length - visible.length;
|
|
575
|
+
if (omitted > 0)
|
|
576
|
+
rendered.push(`${omitted} more blocking process(es)`);
|
|
577
|
+
if (rendered.length === 0)
|
|
578
|
+
return "a live harness process";
|
|
579
|
+
if (rendered.length === 1)
|
|
580
|
+
return rendered[0];
|
|
581
|
+
const last = rendered.pop();
|
|
582
|
+
return `${rendered.join(", ")}, and ${last}`;
|
|
583
|
+
}
|
|
531
584
|
function formatFailClosedBlockingMessage(reason) {
|
|
585
|
+
if (reason.kind === "migration_guard") {
|
|
586
|
+
return [
|
|
587
|
+
`Magic Context cannot migrate the shared database because ${formatFailClosedBlockingProcesses(reason.blockingProcesses)} may be running an older Magic Context build that would fail against the migrated database.`,
|
|
588
|
+
"Restart the blocking process (it will pick up the new build and migrate on start), or shut it down and retry.",
|
|
589
|
+
`Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
|
|
590
|
+
].join(" ");
|
|
591
|
+
}
|
|
532
592
|
if (reason.kind === "schema_fence") {
|
|
533
593
|
return [
|
|
534
|
-
`Magic Context cannot operate:
|
|
535
|
-
`is newer than this build supports (max v${reason.supportedVersion}).`,
|
|
536
|
-
"A newer OpenCode/Pi instance upgraded the database; this build fail-closed",
|
|
537
|
-
"so it cannot corrupt the cache or silently fall back to native compaction.",
|
|
538
|
-
`Update or unpin Magic Context on this harness, then restart.`,
|
|
594
|
+
`Magic Context cannot operate: this Magic Context build is older than the database; upgrade/restart this harness (upstream migration lane v${reason.persistedVersion}, build supports through v${reason.supportedVersion}).`,
|
|
539
595
|
`Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
|
|
540
596
|
].join(" ");
|
|
541
597
|
}
|
|
@@ -596,7 +652,7 @@ function createFailClosedController(options) {
|
|
|
596
652
|
const shouldReprobe = typeof input.tryReopen === "function" && (blockedPassCount === 1 || blockedPassCount % reprobeEveryN === 0);
|
|
597
653
|
if (shouldReprobe) {
|
|
598
654
|
try {
|
|
599
|
-
const healed = await input.tryReopen();
|
|
655
|
+
const healed = await input.tryReopen?.();
|
|
600
656
|
if (healed) {
|
|
601
657
|
reason = null;
|
|
602
658
|
blockedPassCount = 0;
|
|
@@ -779,6 +835,7 @@ var OVERFLOW_PATTERNS = [
|
|
|
779
835
|
/maximum prompt length is \d+/i,
|
|
780
836
|
/reduce the length of the messages/i,
|
|
781
837
|
/maximum context length is \d+ tokens/i,
|
|
838
|
+
/maximum model length is \d+/i,
|
|
782
839
|
/exceeds the limit of \d+/i,
|
|
783
840
|
/exceeds the available context size/i,
|
|
784
841
|
/greater than the context length/i,
|
|
@@ -794,15 +851,25 @@ var OVERFLOW_PATTERNS = [
|
|
|
794
851
|
/context size has been exceeded/i
|
|
795
852
|
];
|
|
796
853
|
var LIMIT_EXTRACTION_PATTERNS = [
|
|
797
|
-
/maximum prompt length is (\d+)/i,
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
/
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
854
|
+
{ pattern: /maximum prompt length is (\d+)/i, provenance: "prompt_only" },
|
|
855
|
+
{
|
|
856
|
+
pattern: /maximum context length is (\d+) tokens?/i,
|
|
857
|
+
provenance: "combined"
|
|
858
|
+
},
|
|
859
|
+
{ pattern: /maximum model length is (\d+)/i, provenance: "combined" },
|
|
860
|
+
{ pattern: /context length is only (\d+) tokens?/i, provenance: "combined" },
|
|
861
|
+
{ pattern: /exceeds the limit of (\d+)/i, provenance: "unknown" },
|
|
862
|
+
{
|
|
863
|
+
pattern: /too large for model with (\d+) maximum context length/i,
|
|
864
|
+
provenance: "combined"
|
|
865
|
+
},
|
|
866
|
+
{ pattern: /context size.*(\d+) tokens?/i, provenance: "combined" },
|
|
867
|
+
{ pattern: /exceeds? the context length of (\d+)/i, provenance: "combined" },
|
|
868
|
+
{
|
|
869
|
+
pattern: />\s*(\d+)\s*(?:tokens?\s*)?(?:maximum|max|limit)\b/i,
|
|
870
|
+
provenance: "prompt_only"
|
|
871
|
+
},
|
|
872
|
+
{ pattern: /max(?:imum)?.*context.*?(\d+)/i, provenance: "unknown" }
|
|
806
873
|
];
|
|
807
874
|
var MIN_PLAUSIBLE_LIMIT = 1024;
|
|
808
875
|
var MAX_PLAUSIBLE_LIMIT = 1e7;
|
|
@@ -853,14 +920,15 @@ function detectOverflow(error) {
|
|
|
853
920
|
const reportedLimit = parseReportedLimit(message);
|
|
854
921
|
return {
|
|
855
922
|
isOverflow: true,
|
|
856
|
-
reportedLimit,
|
|
923
|
+
reportedLimit: reportedLimit?.value,
|
|
924
|
+
reportedLimitProvenance: reportedLimit?.provenance,
|
|
857
925
|
matchedPattern: matched?.source
|
|
858
926
|
};
|
|
859
927
|
}
|
|
860
928
|
function parseReportedLimit(message) {
|
|
861
929
|
if (!message)
|
|
862
930
|
return;
|
|
863
|
-
for (const pattern of LIMIT_EXTRACTION_PATTERNS) {
|
|
931
|
+
for (const { pattern, provenance } of LIMIT_EXTRACTION_PATTERNS) {
|
|
864
932
|
const match = message.match(pattern);
|
|
865
933
|
if (!match)
|
|
866
934
|
continue;
|
|
@@ -872,7 +940,7 @@ function parseReportedLimit(message) {
|
|
|
872
940
|
continue;
|
|
873
941
|
if (value < MIN_PLAUSIBLE_LIMIT || value > MAX_PLAUSIBLE_LIMIT)
|
|
874
942
|
continue;
|
|
875
|
-
return value;
|
|
943
|
+
return { value, provenance };
|
|
876
944
|
}
|
|
877
945
|
return;
|
|
878
946
|
}
|
|
@@ -1354,6 +1422,15 @@ async function runDeferredV22Backfill(db, options = {}) {
|
|
|
1354
1422
|
};
|
|
1355
1423
|
}
|
|
1356
1424
|
|
|
1425
|
+
// ../plugin/src/hooks/magic-context/ctx-reduce-availability.ts
|
|
1426
|
+
var ctxReduceRegisteredGlobally = true;
|
|
1427
|
+
function setCtxReduceRegisteredGlobally(registered) {
|
|
1428
|
+
ctxReduceRegisteredGlobally = registered;
|
|
1429
|
+
}
|
|
1430
|
+
var availabilityBySession = new BoundedSessionMap(1000);
|
|
1431
|
+
var permissionDeniedBySession = new BoundedSessionMap(2000);
|
|
1432
|
+
var ctxReducePermissionDenyLogged = new BoundedSessionMap(1000);
|
|
1433
|
+
|
|
1357
1434
|
// ../plugin/src/hooks/magic-context/derive-budgets.ts
|
|
1358
1435
|
var TRIGGER_BUDGET_PERCENTAGE = 0.05;
|
|
1359
1436
|
var TRIGGER_BUDGET_MIN = 5000;
|
|
@@ -1397,7 +1474,6 @@ function resolveHistorianContextLimit(historianModelOverride) {
|
|
|
1397
1474
|
|
|
1398
1475
|
// ../plugin/src/hooks/magic-context/event-resolvers.ts
|
|
1399
1476
|
var DEFAULT_CONTEXT_LIMIT = 128000;
|
|
1400
|
-
var MAX_EXECUTE_THRESHOLD = 80;
|
|
1401
1477
|
function resolveCacheTtl(cacheTtl, modelKey) {
|
|
1402
1478
|
if (typeof cacheTtl === "string") {
|
|
1403
1479
|
return cacheTtl;
|
|
@@ -1442,7 +1518,7 @@ function resolveExecuteThresholdDetail(config, modelKey, fallback, options) {
|
|
|
1442
1518
|
const dedupeKey = `${options.sessionId ?? "__global__"}|${modelKey ?? "__default__"}|${tokenMatch.value}|${cap}`;
|
|
1443
1519
|
if (!clampWarnSeen.has(dedupeKey)) {
|
|
1444
1520
|
clampWarnSeen.add(dedupeKey);
|
|
1445
|
-
const msg = `execute_threshold_tokens clamped: ${tokenMatch.value} → ${effectiveTokens} (
|
|
1521
|
+
const msg = `execute_threshold_tokens clamped: ${tokenMatch.value} → ${effectiveTokens} (${MAX_EXECUTE_THRESHOLD}% of ${contextLimit}) for ${modelKey ?? "default"}`;
|
|
1446
1522
|
if (options.sessionId) {
|
|
1447
1523
|
sessionLog(options.sessionId, `WARN: ${msg}`);
|
|
1448
1524
|
} else {
|
|
@@ -1498,7 +1574,7 @@ function resolveExecuteThresholdDetail(config, modelKey, fallback, options) {
|
|
|
1498
1574
|
const dedupeKey = `pct|${options?.sessionId ?? "__global__"}|${modelKey ?? "__default__"}|${resolved}`;
|
|
1499
1575
|
if (!clampWarnSeen.has(dedupeKey)) {
|
|
1500
1576
|
clampWarnSeen.add(dedupeKey);
|
|
1501
|
-
const msg = `execute_threshold clamped ${resolved}% → ${MAX_EXECUTE_THRESHOLD}% for ${modelKey ?? "default"} (capped
|
|
1577
|
+
const msg = `execute_threshold clamped ${resolved}% → ${MAX_EXECUTE_THRESHOLD}% for ${modelKey ?? "default"} (capped against the output-reserved safe window; 10% remains for mid-turn growth before the absolute 95% wall)`;
|
|
1502
1578
|
if (options?.sessionId) {
|
|
1503
1579
|
sessionLog(options.sessionId, `WARN: ${msg}`);
|
|
1504
1580
|
} else {
|
|
@@ -1990,6 +2066,57 @@ import {
|
|
|
1990
2066
|
import { createInterface } from "node:readline";
|
|
1991
2067
|
import { fileURLToPath } from "node:url";
|
|
1992
2068
|
|
|
2069
|
+
// ../plugin/src/features/magic-context/schema-fence-probe.ts
|
|
2070
|
+
var STALE_CHILD_SPAWN_FAILURE = "stale_schema_fence";
|
|
2071
|
+
var STALE_CHILD_SPAWN_LATCH_THRESHOLD = 2;
|
|
2072
|
+
var state = {
|
|
2073
|
+
consecutiveFailures: 0,
|
|
2074
|
+
totalFailures: 0,
|
|
2075
|
+
latched: false,
|
|
2076
|
+
noticeEmitted: false,
|
|
2077
|
+
failure: null
|
|
2078
|
+
};
|
|
2079
|
+
function recordStaleFence(persistedVersion, supportedVersion) {
|
|
2080
|
+
state.consecutiveFailures += 1;
|
|
2081
|
+
state.totalFailures += 1;
|
|
2082
|
+
const latched = state.consecutiveFailures >= STALE_CHILD_SPAWN_LATCH_THRESHOLD;
|
|
2083
|
+
state.latched ||= latched;
|
|
2084
|
+
const failure = {
|
|
2085
|
+
failureClass: STALE_CHILD_SPAWN_FAILURE,
|
|
2086
|
+
persistedVersion,
|
|
2087
|
+
supportedVersion,
|
|
2088
|
+
consecutiveFailures: state.consecutiveFailures,
|
|
2089
|
+
totalFailures: state.totalFailures,
|
|
2090
|
+
latched: state.latched
|
|
2091
|
+
};
|
|
2092
|
+
state.failure = failure;
|
|
2093
|
+
const shouldSurface = state.latched && !state.noticeEmitted;
|
|
2094
|
+
if (shouldSurface)
|
|
2095
|
+
state.noticeEmitted = true;
|
|
2096
|
+
return { allowSpawn: false, failure, shouldSurface };
|
|
2097
|
+
}
|
|
2098
|
+
function probeChildSpawnFence(db) {
|
|
2099
|
+
if (!db) {
|
|
2100
|
+
const knownRejection = getSchemaFenceRejection();
|
|
2101
|
+
if (knownRejection) {
|
|
2102
|
+
return recordStaleFence(knownRejection.persistedVersion, knownRejection.supportedVersion);
|
|
2103
|
+
}
|
|
2104
|
+
return { allowSpawn: true };
|
|
2105
|
+
}
|
|
2106
|
+
try {
|
|
2107
|
+
const persistedVersion = getPersistedSchemaVersion(db);
|
|
2108
|
+
if (persistedVersion > LATEST_SUPPORTED_VERSION) {
|
|
2109
|
+
return recordStaleFence(persistedVersion, LATEST_SUPPORTED_VERSION);
|
|
2110
|
+
}
|
|
2111
|
+
} catch {
|
|
2112
|
+
return { allowSpawn: true };
|
|
2113
|
+
}
|
|
2114
|
+
state.consecutiveFailures = 0;
|
|
2115
|
+
state.latched = false;
|
|
2116
|
+
state.noticeEmitted = false;
|
|
2117
|
+
return { allowSpawn: true };
|
|
2118
|
+
}
|
|
2119
|
+
|
|
1993
2120
|
// ../plugin/src/features/magic-context/subagent-token-capture.ts
|
|
1994
2121
|
function asNumber(value) {
|
|
1995
2122
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
@@ -2338,6 +2465,10 @@ class PiSubagentRunner {
|
|
|
2338
2465
|
if (ZERO_TOOL_PROMPT_REQUIRED_AGENTS.has(options.agent) && options.systemPrompt.trim().length === 0) {
|
|
2339
2466
|
return failBeforeSpawn("invalid_prompt", `zero-tool Pi subagent "${options.agent}" requires a non-empty system prompt`, true);
|
|
2340
2467
|
}
|
|
2468
|
+
const fence = probeChildSpawnFence(openDatabase());
|
|
2469
|
+
if (!fence.allowSpawn) {
|
|
2470
|
+
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.`);
|
|
2471
|
+
}
|
|
2341
2472
|
const promptBytes = Buffer.byteLength(options.userMessage, "utf8");
|
|
2342
2473
|
const deliverViaStdin = promptBytes > PROMPT_ARGV_MAX_BYTES || this.platform === "win32";
|
|
2343
2474
|
let systemPromptTempDir;
|
|
@@ -2909,7 +3040,7 @@ function registerCtxAugCommand(pi, config) {
|
|
|
2909
3040
|
sessionLog(sessionLabel, "/ctx-aug: spawning sidekick", {
|
|
2910
3041
|
model: currentConfig.model
|
|
2911
3042
|
});
|
|
2912
|
-
const projectIdentity = resolveProjectIdentityForSession(ctx.cwd);
|
|
3043
|
+
const projectIdentity = resolveProjectIdentityForSession(ctx.cwd, currentConfig.allowHomeProject);
|
|
2913
3044
|
if (!projectIdentity) {
|
|
2914
3045
|
sessionLog(sessionLabel, "Error: Could not resolve project identity for sidekick.");
|
|
2915
3046
|
return;
|
|
@@ -2952,6 +3083,394 @@ ${sidekickText}
|
|
|
2952
3083
|
});
|
|
2953
3084
|
}
|
|
2954
3085
|
|
|
3086
|
+
// ../plugin/src/features/magic-context/smart-notes/types.ts
|
|
3087
|
+
var SMART_NOTE_CHECK_POLICY_VERSION = 1;
|
|
3088
|
+
var SMART_NOTE_CHECK_FLOOR_MS = 5 * 60 * 1000;
|
|
3089
|
+
var SMART_NOTE_CHECK_CEILING_MS = 24 * 60 * 60 * 1000;
|
|
3090
|
+
var SMART_NOTE_CHECK_DEFAULT_INTERVAL_MS = 60 * 60 * 1000;
|
|
3091
|
+
var SMART_NOTE_CHECK_MAX_STALENESS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
3092
|
+
var SMART_NOTE_CHECK_LIVENESS_RECHECK_MS = 24 * 60 * 60 * 1000;
|
|
3093
|
+
|
|
3094
|
+
class SmartNoteNetworkError extends Error {
|
|
3095
|
+
isSmartNoteNetworkError = true;
|
|
3096
|
+
terminal;
|
|
3097
|
+
constructor(message, options = {}) {
|
|
3098
|
+
super(message);
|
|
3099
|
+
this.name = "SmartNoteNetworkError";
|
|
3100
|
+
this.terminal = options.terminal ?? false;
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3104
|
+
class SmartNoteSecurityError extends Error {
|
|
3105
|
+
isSmartNoteSecurityError = true;
|
|
3106
|
+
constructor(message) {
|
|
3107
|
+
super(message);
|
|
3108
|
+
this.name = "SmartNoteSecurityError";
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
function isSmartNoteNetworkError(error) {
|
|
3112
|
+
return error instanceof SmartNoteNetworkError || error instanceof Error && (error.name === "SmartNoteNetworkError" || error.message.includes("SmartNoteNetworkError") || error.message.includes("SMART_NOTE_NETWORK"));
|
|
3113
|
+
}
|
|
3114
|
+
function isTerminalSmartNoteNetworkError(error) {
|
|
3115
|
+
return error instanceof SmartNoteNetworkError && error.terminal;
|
|
3116
|
+
}
|
|
3117
|
+
function parseSmartNoteManifest(json) {
|
|
3118
|
+
if (!json)
|
|
3119
|
+
return { capabilities: [] };
|
|
3120
|
+
try {
|
|
3121
|
+
const parsed = JSON.parse(json);
|
|
3122
|
+
const capabilities = Array.isArray(parsed.capabilities) ? parsed.capabilities.filter((c) => ["readFile", "gitHeadSha", "gitTag", "gitLog", "httpGet"].includes(String(c))) : [];
|
|
3123
|
+
return {
|
|
3124
|
+
capabilities,
|
|
3125
|
+
readFiles: stringArray(parsed.readFiles),
|
|
3126
|
+
hosts: stringArray(parsed.hosts),
|
|
3127
|
+
urls: stringArray(parsed.urls),
|
|
3128
|
+
signals: stringArray(parsed.signals),
|
|
3129
|
+
summary: typeof parsed.summary === "string" ? parsed.summary : undefined
|
|
3130
|
+
};
|
|
3131
|
+
} catch {
|
|
3132
|
+
return { capabilities: [] };
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
function stringArray(value) {
|
|
3136
|
+
if (!Array.isArray(value))
|
|
3137
|
+
return;
|
|
3138
|
+
const arr = value.filter((item) => typeof item === "string");
|
|
3139
|
+
return arr.length > 0 ? arr : undefined;
|
|
3140
|
+
}
|
|
3141
|
+
|
|
3142
|
+
// ../plugin/src/features/magic-context/smart-notes/storage.ts
|
|
3143
|
+
function toSmartNote(note) {
|
|
3144
|
+
return {
|
|
3145
|
+
...note,
|
|
3146
|
+
checkStatus: note.checkStatus ?? "uncompiled",
|
|
3147
|
+
checkFailureCount: note.checkFailureCount ?? 0,
|
|
3148
|
+
checkNetworkFailureCount: note.checkNetworkFailureCount ?? 0,
|
|
3149
|
+
policyVersion: note.policyVersion ?? 0
|
|
3150
|
+
};
|
|
3151
|
+
}
|
|
3152
|
+
function commitSmartNoteState(db, args) {
|
|
3153
|
+
db.exec("BEGIN IMMEDIATE");
|
|
3154
|
+
let leaseLost = false;
|
|
3155
|
+
let committed = false;
|
|
3156
|
+
try {
|
|
3157
|
+
if (args.leaseHeld && !args.leaseHeld()) {
|
|
3158
|
+
leaseLost = true;
|
|
3159
|
+
} else if (claimExpectedState(db, args.expected)) {
|
|
3160
|
+
args.write();
|
|
3161
|
+
committed = true;
|
|
3162
|
+
}
|
|
3163
|
+
db.exec("COMMIT");
|
|
3164
|
+
} catch (error) {
|
|
3165
|
+
try {
|
|
3166
|
+
db.exec("ROLLBACK");
|
|
3167
|
+
} catch {}
|
|
3168
|
+
throw error;
|
|
3169
|
+
}
|
|
3170
|
+
if (leaseLost) {
|
|
3171
|
+
throw new Error(`Dream lease lost during smart-note ${args.phase} commit`);
|
|
3172
|
+
}
|
|
3173
|
+
if (!committed) {
|
|
3174
|
+
log(`[debug] smart note #${args.expected.noteId}: discarded stale ${args.phase} result`);
|
|
3175
|
+
}
|
|
3176
|
+
return committed;
|
|
3177
|
+
}
|
|
3178
|
+
function claimExpectedState(db, expected) {
|
|
3179
|
+
if (expected.kind === "compiled-check") {
|
|
3180
|
+
return db.prepare(`UPDATE notes SET id = id
|
|
3181
|
+
WHERE id = ? AND type = 'smart' AND status = 'pending'
|
|
3182
|
+
AND check_status = 'compiled' AND compiled_check = ?
|
|
3183
|
+
AND check_hash IS ? AND check_compiled_at IS ?`).run(expected.noteId, expected.compiledCheck, expected.checkHash, expected.checkCompiledAt).changes > 0;
|
|
3184
|
+
}
|
|
3185
|
+
const statusClause = expected.checkStatus ? " AND check_status = ?" : "";
|
|
3186
|
+
const params = [
|
|
3187
|
+
expected.noteId,
|
|
3188
|
+
expected.content,
|
|
3189
|
+
expected.surfaceCondition,
|
|
3190
|
+
expected.updatedAt
|
|
3191
|
+
];
|
|
3192
|
+
if (expected.checkStatus)
|
|
3193
|
+
params.push(expected.checkStatus);
|
|
3194
|
+
return db.prepare(`UPDATE notes SET id = id
|
|
3195
|
+
WHERE id = ? AND type = 'smart' AND status = 'pending'
|
|
3196
|
+
AND content = ? AND surface_condition IS ? AND updated_at = ?${statusClause}`).run(...params).changes > 0;
|
|
3197
|
+
}
|
|
3198
|
+
function getDueCompiledSmartNoteChecks(db, projectPath, now, limit) {
|
|
3199
|
+
return getPendingSmartNotes(db, projectPath).map(toSmartNote).filter((note) => note.checkStatus === "compiled" && note.compiledCheck !== null && note.policyVersion === SMART_NOTE_CHECK_POLICY_VERSION && (note.checkQuarantinedUntil === null || note.checkQuarantinedUntil <= now) && (note.checkNextDueAt === null || note.checkNextDueAt <= now)).sort((a, b) => (a.checkNextDueAt ?? 0) - (b.checkNextDueAt ?? 0) || a.id - b.id).slice(0, Math.max(1, limit));
|
|
3200
|
+
}
|
|
3201
|
+
function getSmartNotesNeedingCompilation(db, projectPath, now, limit) {
|
|
3202
|
+
return getPendingSmartNotes(db, projectPath).map(toSmartNote).filter((note) => (note.checkNextDueAt === null || note.checkNextDueAt <= now) && (note.checkStatus === "uncompiled" || note.checkStatus === "failing" || note.compiledCheck === null || note.policyVersion !== SMART_NOTE_CHECK_POLICY_VERSION)).sort((a, b) => a.createdAt - b.createdAt || a.id - b.id).slice(0, Math.max(1, limit));
|
|
3203
|
+
}
|
|
3204
|
+
function getStaleCompiledSmartNotes(db, projectPath, now, limit) {
|
|
3205
|
+
const staleBefore = now - SMART_NOTE_CHECK_MAX_STALENESS_MS;
|
|
3206
|
+
const livenessBefore = now - SMART_NOTE_CHECK_LIVENESS_RECHECK_MS;
|
|
3207
|
+
return getPendingSmartNotes(db, projectPath).map(toSmartNote).filter((note) => note.checkStatus === "compiled" && note.compiledCheck !== null && note.policyVersion === SMART_NOTE_CHECK_POLICY_VERSION && note.checkFalseSinceAt !== null && note.checkFalseSinceAt <= staleBefore && (note.checkLastLivenessAt === null || note.checkLastLivenessAt <= livenessBefore)).sort((a, b) => (a.checkFalseSinceAt ?? 0) - (b.checkFalseSinceAt ?? 0) || a.id - b.id).slice(0, Math.max(1, limit));
|
|
3208
|
+
}
|
|
3209
|
+
function storeCompiledSmartNoteCheck(db, args) {
|
|
3210
|
+
db.prepare(`UPDATE notes
|
|
3211
|
+
SET compiled_check = ?,
|
|
3212
|
+
manifest_json = ?,
|
|
3213
|
+
check_hash = ?,
|
|
3214
|
+
check_cron = ?,
|
|
3215
|
+
check_version = 1,
|
|
3216
|
+
check_status = 'compiled',
|
|
3217
|
+
check_failure_count = 0,
|
|
3218
|
+
check_network_failure_count = 0,
|
|
3219
|
+
check_quarantined_until = NULL,
|
|
3220
|
+
check_next_due_at = ?,
|
|
3221
|
+
check_compiled_at = ?,
|
|
3222
|
+
check_false_since_at = COALESCE(check_false_since_at, ?),
|
|
3223
|
+
check_last_liveness_at = NULL,
|
|
3224
|
+
policy_version = ?,
|
|
3225
|
+
updated_at = ?
|
|
3226
|
+
WHERE id = ? AND type = 'smart'`).run(args.compiledCheck, JSON.stringify(args.manifest), args.checkHash, args.checkCron, args.nextDueAt, args.now, args.now, SMART_NOTE_CHECK_POLICY_VERSION, args.now, args.noteId);
|
|
3227
|
+
}
|
|
3228
|
+
function markCompiledCheckFalse(db, noteId, nextDueAt, now) {
|
|
3229
|
+
db.prepare(`UPDATE notes
|
|
3230
|
+
SET last_checked_at = ?,
|
|
3231
|
+
updated_at = ?,
|
|
3232
|
+
check_next_due_at = ?,
|
|
3233
|
+
check_failure_count = 0,
|
|
3234
|
+
check_network_failure_count = 0,
|
|
3235
|
+
check_false_since_at = COALESCE(check_false_since_at, ?)
|
|
3236
|
+
WHERE id = ? AND type = 'smart'`).run(now, now, nextDueAt, now, noteId);
|
|
3237
|
+
}
|
|
3238
|
+
function markCompiledCheckLogicFailure(db, noteId, now, maxFailures) {
|
|
3239
|
+
const failureCount = readFailureCount(db, noteId, "check_failure_count") + 1;
|
|
3240
|
+
const status = failureCount >= maxFailures ? "failing" : "compiled";
|
|
3241
|
+
db.prepare(`UPDATE notes
|
|
3242
|
+
SET check_failure_count = ?,
|
|
3243
|
+
check_status = ?,
|
|
3244
|
+
check_next_due_at = ?,
|
|
3245
|
+
updated_at = ?
|
|
3246
|
+
WHERE id = ? AND type = 'smart'`).run(failureCount, status, now + backoffMs(failureCount), now, noteId);
|
|
3247
|
+
}
|
|
3248
|
+
function markCompiledCheckNetworkFailure(db, noteId, now, maxFailures) {
|
|
3249
|
+
const failureCount = readFailureCount(db, noteId, "check_network_failure_count") + 1;
|
|
3250
|
+
const quarantinedUntil = now + backoffMs(failureCount);
|
|
3251
|
+
const status = failureCount >= maxFailures ? "failing" : "compiled";
|
|
3252
|
+
db.prepare(`UPDATE notes
|
|
3253
|
+
SET check_network_failure_count = ?,
|
|
3254
|
+
check_status = ?,
|
|
3255
|
+
check_next_due_at = ?,
|
|
3256
|
+
check_quarantined_until = ?,
|
|
3257
|
+
updated_at = ?
|
|
3258
|
+
WHERE id = ? AND type = 'smart'`).run(failureCount, status, quarantinedUntil, quarantinedUntil, now, noteId);
|
|
3259
|
+
}
|
|
3260
|
+
function markSmartNoteLivenessChecked(db, noteId, now) {
|
|
3261
|
+
db.prepare(`UPDATE notes
|
|
3262
|
+
SET check_last_liveness_at = ?, updated_at = ?
|
|
3263
|
+
WHERE id = ? AND type = 'smart'`).run(now, now, noteId);
|
|
3264
|
+
}
|
|
3265
|
+
function markSmartNoteCheckStatus(db, noteId, status, now) {
|
|
3266
|
+
db.prepare(`UPDATE notes SET check_status = ?, updated_at = ? WHERE id = ? AND type = 'smart'`).run(status, now, noteId);
|
|
3267
|
+
}
|
|
3268
|
+
function markSmartNoteCompilationFailure(db, noteId, now, maxFailures) {
|
|
3269
|
+
const failureCount = readFailureCount(db, noteId, "check_failure_count") + 1;
|
|
3270
|
+
const status = failureCount >= maxFailures ? "fallback" : "uncompiled";
|
|
3271
|
+
db.prepare(`UPDATE notes
|
|
3272
|
+
SET check_failure_count = ?,
|
|
3273
|
+
check_status = ?,
|
|
3274
|
+
check_next_due_at = ?,
|
|
3275
|
+
updated_at = ?
|
|
3276
|
+
WHERE id = ? AND type = 'smart'`).run(failureCount, status, now + backoffMs(failureCount), now, noteId);
|
|
3277
|
+
}
|
|
3278
|
+
function readFailureCount(db, noteId, column) {
|
|
3279
|
+
if (column !== "check_failure_count" && column !== "check_network_failure_count")
|
|
3280
|
+
return 0;
|
|
3281
|
+
const row = db.prepare(`SELECT ${column} AS count FROM notes WHERE id = ?`).get(noteId);
|
|
3282
|
+
return row?.count ?? 0;
|
|
3283
|
+
}
|
|
3284
|
+
function backoffMs(failureCount) {
|
|
3285
|
+
const minutes = Math.min(24 * 60, 5 * 2 ** Math.max(0, failureCount - 1));
|
|
3286
|
+
return minutes * 60 * 1000;
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3289
|
+
// ../plugin/src/features/magic-context/dreamer/task-gates.ts
|
|
3290
|
+
function countActiveMemories(db, projectPath) {
|
|
3291
|
+
const row = db.prepare("SELECT COUNT(*) AS cnt FROM memories WHERE project_path = ? AND status IN ('active','permanent')").get(projectPath);
|
|
3292
|
+
return row?.cnt ?? 0;
|
|
3293
|
+
}
|
|
3294
|
+
function countUnmappedActiveMemories(db, projectPath) {
|
|
3295
|
+
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
3296
|
+
FROM memories m
|
|
3297
|
+
WHERE m.project_path = ?
|
|
3298
|
+
AND m.status IN ('active','permanent')
|
|
3299
|
+
AND NOT EXISTS (
|
|
3300
|
+
SELECT 1 FROM memory_verifications v WHERE v.memory_id = m.id
|
|
3301
|
+
)`).get(projectPath);
|
|
3302
|
+
return row?.cnt ?? 0;
|
|
3303
|
+
}
|
|
3304
|
+
function countCompartmentsSince(db, projectPath, since) {
|
|
3305
|
+
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
3306
|
+
FROM compartments c
|
|
3307
|
+
JOIN session_projects sp ON sp.session_id = c.session_id
|
|
3308
|
+
WHERE sp.project_path = ? AND c.created_at > ?`).get(projectPath, since);
|
|
3309
|
+
return row?.cnt ?? 0;
|
|
3310
|
+
}
|
|
3311
|
+
function countProjectSessionsSince(db, projectPath, since) {
|
|
3312
|
+
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 WHERE project_path = ? AND updated_at > ?").get(projectPath, since);
|
|
3313
|
+
return row?.cnt ?? 0;
|
|
3314
|
+
}
|
|
3315
|
+
function countMappedMemories(db, projectPath) {
|
|
3316
|
+
const row = db.prepare(`SELECT COUNT(DISTINCT m.id) AS cnt
|
|
3317
|
+
FROM memories m
|
|
3318
|
+
JOIN memory_verifications v ON v.memory_id = m.id
|
|
3319
|
+
WHERE m.project_path = ?
|
|
3320
|
+
AND m.status IN ('active','permanent')
|
|
3321
|
+
AND v.file_path <> ''`).get(projectPath);
|
|
3322
|
+
return row?.cnt ?? 0;
|
|
3323
|
+
}
|
|
3324
|
+
function countUnverifiedMappedMemories(db, projectPath) {
|
|
3325
|
+
const row = db.prepare(`SELECT COUNT(DISTINCT m.id) AS cnt
|
|
3326
|
+
FROM memories m
|
|
3327
|
+
JOIN memory_verifications v ON v.memory_id = m.id
|
|
3328
|
+
WHERE m.project_path = ?
|
|
3329
|
+
AND m.status IN ('active','permanent')
|
|
3330
|
+
AND v.file_path <> ''
|
|
3331
|
+
AND v.verified_at = 0`).get(projectPath);
|
|
3332
|
+
return row?.cnt ?? 0;
|
|
3333
|
+
}
|
|
3334
|
+
function countCueCandidates(db, projectPath) {
|
|
3335
|
+
if (!hasMuralCueColumns(db))
|
|
3336
|
+
return countActiveMemories(db, projectPath);
|
|
3337
|
+
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
3338
|
+
FROM memories
|
|
3339
|
+
WHERE project_path = ?
|
|
3340
|
+
AND status IN ('active','permanent')
|
|
3341
|
+
AND (mural_cue IS NULL OR mural_cue_hash IS NULL OR updated_at > mural_cue_at)`).get(projectPath);
|
|
3342
|
+
return row?.cnt ?? 0;
|
|
3343
|
+
}
|
|
3344
|
+
function countStalePrimers(db, projectPath) {
|
|
3345
|
+
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
3346
|
+
FROM primers
|
|
3347
|
+
WHERE project_path = ?
|
|
3348
|
+
AND status = 'active'
|
|
3349
|
+
AND (answer IS NULL OR TRIM(answer) = '' OR answer_refreshed_at IS NULL
|
|
3350
|
+
OR last_observed_at > answer_refreshed_at)`).get(projectPath);
|
|
3351
|
+
return row?.cnt ?? 0;
|
|
3352
|
+
}
|
|
3353
|
+
function countUnclassifiedActiveMemories(db, projectPath) {
|
|
3354
|
+
if (!hasMemoryClassifiedAtColumn(db))
|
|
3355
|
+
return countActiveMemories(db, projectPath);
|
|
3356
|
+
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
3357
|
+
FROM memories
|
|
3358
|
+
WHERE project_path = ?
|
|
3359
|
+
AND status IN ('active','permanent')
|
|
3360
|
+
AND classified_at IS NULL`).get(projectPath);
|
|
3361
|
+
return row?.cnt ?? 0;
|
|
3362
|
+
}
|
|
3363
|
+
function countPendingSmartNotes(db, projectPath) {
|
|
3364
|
+
const row = db.prepare("SELECT COUNT(*) AS cnt FROM notes WHERE project_path = ? AND type = 'smart' AND status = 'pending'").get(projectPath);
|
|
3365
|
+
return row?.cnt ?? 0;
|
|
3366
|
+
}
|
|
3367
|
+
function countUserMemoryCandidates(db) {
|
|
3368
|
+
const row = db.prepare("SELECT COUNT(*) AS cnt FROM user_memory_candidates").get();
|
|
3369
|
+
return row?.cnt ?? 0;
|
|
3370
|
+
}
|
|
3371
|
+
function countActivePrimers(db, projectPath) {
|
|
3372
|
+
const row = db.prepare("SELECT COUNT(*) AS cnt FROM primers WHERE project_path = ? AND status = 'active'").get(projectPath);
|
|
3373
|
+
return row?.cnt ?? 0;
|
|
3374
|
+
}
|
|
3375
|
+
function getDreamTaskBacklog(db, projectPath, task, options = {}) {
|
|
3376
|
+
switch (task) {
|
|
3377
|
+
case "map-memories": {
|
|
3378
|
+
const total = countActiveMemories(db, projectPath);
|
|
3379
|
+
return { pending: countUnmappedActiveMemories(db, projectPath), total };
|
|
3380
|
+
}
|
|
3381
|
+
case "verify": {
|
|
3382
|
+
return {
|
|
3383
|
+
pending: countUnverifiedMappedMemories(db, projectPath),
|
|
3384
|
+
total: countMappedMemories(db, projectPath)
|
|
3385
|
+
};
|
|
3386
|
+
}
|
|
3387
|
+
case "verify-broad": {
|
|
3388
|
+
const total = countMappedMemories(db, projectPath);
|
|
3389
|
+
return { pending: total, total };
|
|
3390
|
+
}
|
|
3391
|
+
case "curate": {
|
|
3392
|
+
const total = countActiveMemories(db, projectPath);
|
|
3393
|
+
return { pending: total, total };
|
|
3394
|
+
}
|
|
3395
|
+
case "compress-cues": {
|
|
3396
|
+
const total = countActiveMemories(db, projectPath);
|
|
3397
|
+
return { pending: countCueCandidates(db, projectPath), total };
|
|
3398
|
+
}
|
|
3399
|
+
case "classify-memories": {
|
|
3400
|
+
const total = countActiveMemories(db, projectPath);
|
|
3401
|
+
return { pending: countUnclassifiedActiveMemories(db, projectPath), total };
|
|
3402
|
+
}
|
|
3403
|
+
case "retrospective": {
|
|
3404
|
+
const pending = countProjectSessionsSince(db, projectPath, options.retrospectiveWatermarkMs ?? null);
|
|
3405
|
+
return { pending, total: pending };
|
|
3406
|
+
}
|
|
3407
|
+
case "maintain-docs": {
|
|
3408
|
+
const total = countCompartmentsSince(db, projectPath, 0);
|
|
3409
|
+
const pending = countCompartmentsSince(db, projectPath, options.lastRunAt ?? 0);
|
|
3410
|
+
return { pending, total };
|
|
3411
|
+
}
|
|
3412
|
+
case "evaluate-smart-notes": {
|
|
3413
|
+
const pending = countPendingSmartNotes(db, projectPath);
|
|
3414
|
+
return { pending, total: pending };
|
|
3415
|
+
}
|
|
3416
|
+
case "review-user-memories": {
|
|
3417
|
+
const pending = countUserMemoryCandidates(db);
|
|
3418
|
+
return { pending, total: pending };
|
|
3419
|
+
}
|
|
3420
|
+
case "promote-primers": {
|
|
3421
|
+
const pending = countPrimerCandidatesForProject(db, projectPath);
|
|
3422
|
+
return { pending, total: pending };
|
|
3423
|
+
}
|
|
3424
|
+
case "refresh-primers": {
|
|
3425
|
+
const total = countActivePrimers(db, projectPath);
|
|
3426
|
+
return { pending: countStalePrimers(db, projectPath), total };
|
|
3427
|
+
}
|
|
3428
|
+
default: {
|
|
3429
|
+
const _exhaustive = task;
|
|
3430
|
+
return _exhaustive;
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
function getDreamTaskBacklogs(db, projectPath, tasks = CANONICAL_DREAM_TASKS, options = {}) {
|
|
3435
|
+
const result = {};
|
|
3436
|
+
for (const task of tasks)
|
|
3437
|
+
result[task] = getDreamTaskBacklog(db, projectPath, task, options);
|
|
3438
|
+
return result;
|
|
3439
|
+
}
|
|
3440
|
+
function evaluateTaskGate(task, ctx) {
|
|
3441
|
+
const { db, projectIdentity: project, lastRunAt } = ctx;
|
|
3442
|
+
switch (task) {
|
|
3443
|
+
case "map-memories":
|
|
3444
|
+
return countUnmappedActiveMemories(db, project) > 0;
|
|
3445
|
+
case "verify":
|
|
3446
|
+
return countActiveMemories(db, project) > 0;
|
|
3447
|
+
case "verify-broad":
|
|
3448
|
+
return countActiveMemories(db, project) > 0;
|
|
3449
|
+
case "curate":
|
|
3450
|
+
return countActiveMemories(db, project) > 0;
|
|
3451
|
+
case "compress-cues":
|
|
3452
|
+
return countActiveMemories(db, project) > 0;
|
|
3453
|
+
case "classify-memories":
|
|
3454
|
+
return countActiveMemories(db, project) > 0;
|
|
3455
|
+
case "retrospective":
|
|
3456
|
+
return countProjectSessionsSince(db, project, ctx.retrospectiveWatermarkMs ?? null) > 0;
|
|
3457
|
+
case "maintain-docs":
|
|
3458
|
+
return countCompartmentsSince(db, project, lastRunAt ?? 0) > 0;
|
|
3459
|
+
case "evaluate-smart-notes":
|
|
3460
|
+
return getSmartNotesNeedingCompilation(db, project, Date.now(), 1).length > 0 || getStaleCompiledSmartNotes(db, project, Date.now(), 1).length > 0 || getPendingSmartNotes(db, project).some((note) => note.checkStatus === "fallback");
|
|
3461
|
+
case "review-user-memories":
|
|
3462
|
+
return getUserMemoryCandidates(db).length >= ctx.promotionThreshold;
|
|
3463
|
+
case "promote-primers":
|
|
3464
|
+
return countPrimerCandidatesForProject(db, project) >= (ctx.promotionThreshold ?? 2);
|
|
3465
|
+
case "refresh-primers":
|
|
3466
|
+
return getActivePrimers(db, project).some((primer) => !primer.answer.trim() || primer.answerRefreshedAt == null || (primer.lastObservedAt ?? 0) > primer.answerRefreshedAt);
|
|
3467
|
+
default: {
|
|
3468
|
+
const _exhaustive = task;
|
|
3469
|
+
return Boolean(_exhaustive);
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
|
|
2955
3474
|
// ../plugin/src/features/magic-context/dreamer/open-opencode-db.ts
|
|
2956
3475
|
import { existsSync as existsSync4 } from "node:fs";
|
|
2957
3476
|
function openOpenCodeDb() {
|
|
@@ -2969,19 +3488,214 @@ function openOpenCodeDb() {
|
|
|
2969
3488
|
return null;
|
|
2970
3489
|
}
|
|
2971
3490
|
}
|
|
2972
|
-
|
|
2973
|
-
// ../plugin/src/features/magic-context/dreamer/task-executor.ts
|
|
2974
|
-
import { createHash as createHash6 } from "node:crypto";
|
|
2975
|
-
import { existsSync as existsSync7 } from "node:fs";
|
|
2976
|
-
|
|
2977
|
-
// ../plugin/src/agents/dreamer.ts
|
|
2978
|
-
var DREAMER_AGENT = "dreamer";
|
|
2979
|
-
var DREAMER_RETROSPECTIVE_AGENT = "dreamer-retrospective";
|
|
2980
|
-
var DREAMER_PRIMER_INVESTIGATOR_AGENT = "dreamer-primer-investigator";
|
|
2981
|
-
var DREAMER_MEMORY_MAPPER_AGENT = "dreamer-memory-mapper";
|
|
2982
|
-
var DREAMER_CLASSIFIER_AGENT = "dreamer-classifier";
|
|
2983
|
-
var DREAMER_DOCS_AGENT = "dreamer-docs";
|
|
2984
|
-
var DREAMER_REVIEWER_AGENT = "dreamer-reviewer";
|
|
3491
|
+
|
|
3492
|
+
// ../plugin/src/features/magic-context/dreamer/task-executor.ts
|
|
3493
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
3494
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
3495
|
+
|
|
3496
|
+
// ../plugin/src/agents/dreamer.ts
|
|
3497
|
+
var DREAMER_AGENT = "dreamer";
|
|
3498
|
+
var DREAMER_RETROSPECTIVE_AGENT = "dreamer-retrospective";
|
|
3499
|
+
var DREAMER_PRIMER_INVESTIGATOR_AGENT = "dreamer-primer-investigator";
|
|
3500
|
+
var DREAMER_MEMORY_MAPPER_AGENT = "dreamer-memory-mapper";
|
|
3501
|
+
var DREAMER_CLASSIFIER_AGENT = "dreamer-classifier";
|
|
3502
|
+
var DREAMER_DOCS_AGENT = "dreamer-docs";
|
|
3503
|
+
var DREAMER_REVIEWER_AGENT = "dreamer-reviewer";
|
|
3504
|
+
|
|
3505
|
+
// ../plugin/src/hooks/magic-context/send-session-notification.ts
|
|
3506
|
+
var MAX_QUEUED_IGNORED_NOTIFICATIONS = 16;
|
|
3507
|
+
var queuedIgnoredNotifications = new Map;
|
|
3508
|
+
var flushingIgnoredNotifications = new Set;
|
|
3509
|
+
var midTurnDetector = (sessionId) => isMidTurn(undefined, sessionId);
|
|
3510
|
+
function queueIgnoredNotification(notification) {
|
|
3511
|
+
const queued = queuedIgnoredNotifications.get(notification.sessionId) ?? [];
|
|
3512
|
+
queued.push(notification);
|
|
3513
|
+
if (queued.length > MAX_QUEUED_IGNORED_NOTIFICATIONS) {
|
|
3514
|
+
queued.splice(0, queued.length - MAX_QUEUED_IGNORED_NOTIFICATIONS);
|
|
3515
|
+
sessionLog(notification.sessionId, `ignored notification queue full; dropped oldest entries (kept newest ${MAX_QUEUED_IGNORED_NOTIFICATIONS})`);
|
|
3516
|
+
}
|
|
3517
|
+
queuedIgnoredNotifications.set(notification.sessionId, queued);
|
|
3518
|
+
}
|
|
3519
|
+
async function trySendTuiToast(sessionId, text, params, forcePersist) {
|
|
3520
|
+
if (forcePersist)
|
|
3521
|
+
return false;
|
|
3522
|
+
const title = extractToastTitle(text);
|
|
3523
|
+
const message = text.length > 200 ? `${text.slice(0, 200)}…` : text;
|
|
3524
|
+
const toastVariant = inferToastVariant(text);
|
|
3525
|
+
const duration = params.toastDurationMs ?? 5000;
|
|
3526
|
+
const { isTuiConnected: checkTui } = await import("./rpc-notifications-s2rw51c6.js");
|
|
3527
|
+
if (!checkTui(sessionId))
|
|
3528
|
+
return false;
|
|
3529
|
+
try {
|
|
3530
|
+
const { pushNotification: pushNotification2 } = await import("./rpc-notifications-s2rw51c6.js");
|
|
3531
|
+
pushNotification2("toast", {
|
|
3532
|
+
title,
|
|
3533
|
+
message,
|
|
3534
|
+
variant: toastVariant,
|
|
3535
|
+
duration
|
|
3536
|
+
}, sessionId);
|
|
3537
|
+
return true;
|
|
3538
|
+
} catch {
|
|
3539
|
+
sessionLog(sessionId, "TUI RPC toast enqueue failed, falling back to ignored message");
|
|
3540
|
+
return false;
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
function hasNotificationSessionClient(client) {
|
|
3544
|
+
if (client === null || typeof client !== "object")
|
|
3545
|
+
return false;
|
|
3546
|
+
const candidate = client;
|
|
3547
|
+
if (candidate.session === undefined)
|
|
3548
|
+
return true;
|
|
3549
|
+
if (candidate.session === null || typeof candidate.session !== "object")
|
|
3550
|
+
return false;
|
|
3551
|
+
const session = candidate.session;
|
|
3552
|
+
return (session.prompt === undefined || typeof session.prompt === "function") && (session.promptAsync === undefined || typeof session.promptAsync === "function");
|
|
3553
|
+
}
|
|
3554
|
+
function inferToastVariant(text) {
|
|
3555
|
+
const lower = text.toLowerCase();
|
|
3556
|
+
if (lower.includes("error") || lower.includes("failed") || lower.includes("alert"))
|
|
3557
|
+
return "error";
|
|
3558
|
+
if (lower.includes("warning") || lower.includes("⚠"))
|
|
3559
|
+
return "warning";
|
|
3560
|
+
if (lower.includes("complete") || lower.includes("success") || lower.includes("✓") || lower.includes("finished"))
|
|
3561
|
+
return "success";
|
|
3562
|
+
return "info";
|
|
3563
|
+
}
|
|
3564
|
+
function extractToastTitle(text) {
|
|
3565
|
+
const headingMatch = text.match(/^#+\s+(.+)/m);
|
|
3566
|
+
if (headingMatch)
|
|
3567
|
+
return headingMatch[1].trim();
|
|
3568
|
+
const firstLine = text.split(`
|
|
3569
|
+
`)[0].trim();
|
|
3570
|
+
if (firstLine.length <= 80)
|
|
3571
|
+
return firstLine;
|
|
3572
|
+
return "Magic Context";
|
|
3573
|
+
}
|
|
3574
|
+
async function sendIgnoredMessageNow(client, sessionId, text, params, forcePersist) {
|
|
3575
|
+
if (midTurnDetector(sessionId)) {
|
|
3576
|
+
queueIgnoredNotification({ client, sessionId, text, params, forcePersist });
|
|
3577
|
+
return "queued";
|
|
3578
|
+
}
|
|
3579
|
+
const { waitForSafeNotificationTarget } = await import("./safe-notification-target-ng8ygena.js");
|
|
3580
|
+
if (await waitForSafeNotificationTarget(client, sessionId) === "skip") {
|
|
3581
|
+
sessionLog(sessionId, "notification skipped (session not titled yet)");
|
|
3582
|
+
return "skipped";
|
|
3583
|
+
}
|
|
3584
|
+
if (midTurnDetector(sessionId)) {
|
|
3585
|
+
queueIgnoredNotification({ client, sessionId, text, params, forcePersist });
|
|
3586
|
+
return "queued";
|
|
3587
|
+
}
|
|
3588
|
+
if (!hasNotificationSessionClient(client)) {
|
|
3589
|
+
sessionLog(sessionId, "session prompt API unavailable for notification");
|
|
3590
|
+
return "failed";
|
|
3591
|
+
}
|
|
3592
|
+
const c = client;
|
|
3593
|
+
let agent = params.agent || undefined;
|
|
3594
|
+
let variant = params.variant || undefined;
|
|
3595
|
+
let model = params.providerId && params.modelId ? { providerID: params.providerId, modelID: params.modelId } : undefined;
|
|
3596
|
+
if (!agent || !model || !variant) {
|
|
3597
|
+
try {
|
|
3598
|
+
const { resolvePromptContext } = await import("./prompt-context-602yq94t.js");
|
|
3599
|
+
const resolved = await resolvePromptContext(client, sessionId);
|
|
3600
|
+
if (resolved) {
|
|
3601
|
+
agent = agent ?? resolved.agent;
|
|
3602
|
+
model = model ?? resolved.model;
|
|
3603
|
+
variant = variant ?? resolved.variant;
|
|
3604
|
+
}
|
|
3605
|
+
} catch {}
|
|
3606
|
+
}
|
|
3607
|
+
if (midTurnDetector(sessionId)) {
|
|
3608
|
+
queueIgnoredNotification({ client, sessionId, text, params, forcePersist });
|
|
3609
|
+
return "queued";
|
|
3610
|
+
}
|
|
3611
|
+
const input = {
|
|
3612
|
+
path: { id: sessionId },
|
|
3613
|
+
body: {
|
|
3614
|
+
noReply: true,
|
|
3615
|
+
agent,
|
|
3616
|
+
model,
|
|
3617
|
+
variant,
|
|
3618
|
+
parts: [
|
|
3619
|
+
{
|
|
3620
|
+
type: "text",
|
|
3621
|
+
text,
|
|
3622
|
+
ignored: true
|
|
3623
|
+
}
|
|
3624
|
+
]
|
|
3625
|
+
}
|
|
3626
|
+
};
|
|
3627
|
+
try {
|
|
3628
|
+
if (typeof c.session?.prompt === "function") {
|
|
3629
|
+
await Promise.resolve(c.session.prompt(input));
|
|
3630
|
+
return "sent";
|
|
3631
|
+
}
|
|
3632
|
+
if (typeof c.session?.promptAsync === "function") {
|
|
3633
|
+
await c.session.promptAsync(input);
|
|
3634
|
+
return "sent";
|
|
3635
|
+
}
|
|
3636
|
+
sessionLog(sessionId, "session prompt API unavailable for notification");
|
|
3637
|
+
return "failed";
|
|
3638
|
+
} catch (error) {
|
|
3639
|
+
const msg = getErrorMessage(error);
|
|
3640
|
+
sessionLog(sessionId, "failed to send notification:", msg);
|
|
3641
|
+
return "failed";
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
async function sendIgnoredMessage(client, sessionId, text, params, forcePersist = false) {
|
|
3645
|
+
if (await trySendTuiToast(sessionId, text, params, forcePersist))
|
|
3646
|
+
return "sent";
|
|
3647
|
+
if (midTurnDetector(sessionId)) {
|
|
3648
|
+
queueIgnoredNotification({ client, sessionId, text, params, forcePersist });
|
|
3649
|
+
return "queued";
|
|
3650
|
+
}
|
|
3651
|
+
return sendIgnoredMessageNow(client, sessionId, text, params, forcePersist);
|
|
3652
|
+
}
|
|
3653
|
+
|
|
3654
|
+
// ../plugin/src/hooks/magic-context/child-session-spawn.ts
|
|
3655
|
+
var STALE_PLUGIN_RESTART_NOTICE = "Magic Context: plugin build is older than its database — restart OpenCode";
|
|
3656
|
+
async function surfaceStalePluginBuild(args) {
|
|
3657
|
+
if (!args.parentSessionId)
|
|
3658
|
+
return;
|
|
3659
|
+
try {
|
|
3660
|
+
if (args.db) {
|
|
3661
|
+
updateSessionMeta(args.db, args.parentSessionId, {
|
|
3662
|
+
lastTransformError: STALE_PLUGIN_RESTART_NOTICE
|
|
3663
|
+
});
|
|
3664
|
+
}
|
|
3665
|
+
pushNotification("toast", {
|
|
3666
|
+
title: "Magic Context",
|
|
3667
|
+
message: STALE_PLUGIN_RESTART_NOTICE,
|
|
3668
|
+
variant: "error",
|
|
3669
|
+
duration: 1e4
|
|
3670
|
+
}, args.parentSessionId);
|
|
3671
|
+
pushNotification("action", { action: "refresh-sidebar" }, args.parentSessionId);
|
|
3672
|
+
await sendIgnoredMessage(args.client, args.parentSessionId, STALE_PLUGIN_RESTART_NOTICE, args.notificationParams ?? {}, true);
|
|
3673
|
+
} catch (error) {
|
|
3674
|
+
sessionLog(args.parentSessionId, `stale plugin-build warning delivery failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
async function createChildSessionWithFence(args) {
|
|
3678
|
+
const verdict = probeChildSpawnFence(args.db);
|
|
3679
|
+
if (!verdict.allowSpawn) {
|
|
3680
|
+
if (args.parentSessionId) {
|
|
3681
|
+
sessionLog(args.parentSessionId, `child session skipped (${verdict.failure.failureClass}): database=v${verdict.failure.persistedVersion}, supported_fence=v${verdict.failure.supportedVersion}, consecutive=${verdict.failure.consecutiveFailures}, total=${verdict.failure.totalFailures}`);
|
|
3682
|
+
}
|
|
3683
|
+
if (verdict.shouldSurface) {
|
|
3684
|
+
if (args.onFenceLatched)
|
|
3685
|
+
await args.onFenceLatched(verdict.failure);
|
|
3686
|
+
else
|
|
3687
|
+
await surfaceStalePluginBuild(args);
|
|
3688
|
+
}
|
|
3689
|
+
return null;
|
|
3690
|
+
}
|
|
3691
|
+
return args.client.session.create({
|
|
3692
|
+
body: {
|
|
3693
|
+
...args.parentSessionId ? { parentID: args.parentSessionId } : {},
|
|
3694
|
+
title: args.title
|
|
3695
|
+
},
|
|
3696
|
+
query: { directory: args.directory }
|
|
3697
|
+
});
|
|
3698
|
+
}
|
|
2985
3699
|
// ../plugin/src/shared/model-suggestion-retry.ts
|
|
2986
3700
|
var ABORT_CALL_TIMEOUT_MS = 3000;
|
|
2987
3701
|
function copyPromptArgs(args, body) {
|
|
@@ -3635,7 +4349,7 @@ function parseCuesManifest(text) {
|
|
|
3635
4349
|
const body = extractCompleteManifestBody(text, "cues");
|
|
3636
4350
|
const out = [];
|
|
3637
4351
|
for (const match of body.matchAll(/<cue\s+id="(\d+)"\s*>([\s\S]*?)<\/cue>/g)) {
|
|
3638
|
-
const id = Number.parseInt(match[1], 10);
|
|
4352
|
+
const id = Number.parseInt(match[1] ?? "", 10);
|
|
3639
4353
|
if (!Number.isInteger(id))
|
|
3640
4354
|
continue;
|
|
3641
4355
|
out.push({ id, cue: unescapeXml(match[2] ?? "").trim() });
|
|
@@ -3736,11 +4450,15 @@ async function runCompressCues(args) {
|
|
|
3736
4450
|
break;
|
|
3737
4451
|
}
|
|
3738
4452
|
const sliceMs = computeChunkSliceMs(remainingMs, chunks.length - i);
|
|
3739
|
-
const
|
|
4453
|
+
const chunk = chunks[i];
|
|
4454
|
+
if (!chunk)
|
|
4455
|
+
break;
|
|
4456
|
+
const outcome = await compressOneChunk(args, chunk, sliceMs, abortController.signal);
|
|
3740
4457
|
result.compressed += outcome.compressed;
|
|
3741
4458
|
result.skipped += outcome.skipped;
|
|
3742
4459
|
result.remaining -= outcome.compressed;
|
|
3743
4460
|
result.chunks += 1;
|
|
4461
|
+
args.onProgress?.(result.compressed + result.skipped);
|
|
3744
4462
|
if (outcome.failure?.class === "timeout") {
|
|
3745
4463
|
consecutiveTimeouts += 1;
|
|
3746
4464
|
timeoutStreakElapsedMs.push(outcome.failure.elapsedMs);
|
|
@@ -3771,12 +4489,12 @@ async function compressOneChunk(args, chunk, sliceMs, signal) {
|
|
|
3771
4489
|
projectPath: args.projectIdentity,
|
|
3772
4490
|
memories: chunk.map(toPromptMemory)
|
|
3773
4491
|
});
|
|
3774
|
-
const createResponse = await
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
4492
|
+
const createResponse = await createChildSessionWithFence({
|
|
4493
|
+
client: args.client,
|
|
4494
|
+
db: args.db,
|
|
4495
|
+
parentSessionId: args.parentSessionId,
|
|
4496
|
+
title: "magic-context-dream-compress-cues",
|
|
4497
|
+
directory: args.sessionDirectory
|
|
3780
4498
|
});
|
|
3781
4499
|
const created = normalizeSDKResponse(createResponse, null, {
|
|
3782
4500
|
preferResponseOnMissingData: true
|
|
@@ -4260,12 +4978,12 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
|
|
|
4260
4978
|
abortController.abort();
|
|
4261
4979
|
});
|
|
4262
4980
|
try {
|
|
4263
|
-
const createResponse = await
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4981
|
+
const createResponse = await createChildSessionWithFence({
|
|
4982
|
+
client: args.client,
|
|
4983
|
+
db: args.db,
|
|
4984
|
+
parentSessionId: args.parentSessionId,
|
|
4985
|
+
title: "magic-context-dream-user-memories",
|
|
4986
|
+
directory: args.sessionDirectory
|
|
4269
4987
|
});
|
|
4270
4988
|
const created = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
|
|
4271
4989
|
agentSessionId = typeof created?.id === "string" ? created.id : null;
|
|
@@ -4836,6 +5554,7 @@ async function runClassify(args) {
|
|
|
4836
5554
|
result.changed += counts.changed;
|
|
4837
5555
|
result.remaining -= counts.classified;
|
|
4838
5556
|
result.chunks += 1;
|
|
5557
|
+
args.onProgress?.(result.classified);
|
|
4839
5558
|
}
|
|
4840
5559
|
result.complete = result.remaining === 0;
|
|
4841
5560
|
log(`[dreamer] classify: stage=${stage} classified=${result.classified} changed=${result.changed} chunks=${result.chunks} remaining=${result.remaining} complete=${result.complete}`);
|
|
@@ -4859,12 +5578,12 @@ async function classifyOneChunk(args, chunk, anchors, sliceMs, signal) {
|
|
|
4859
5578
|
recordInvocation(args, startedAt, { status: "completed" });
|
|
4860
5579
|
return run2;
|
|
4861
5580
|
}
|
|
4862
|
-
const createResponse = await
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
5581
|
+
const createResponse = await createChildSessionWithFence({
|
|
5582
|
+
client: args.client,
|
|
5583
|
+
db: args.db,
|
|
5584
|
+
parentSessionId: args.parentSessionId,
|
|
5585
|
+
title: "magic-context-dream-classify",
|
|
5586
|
+
directory: args.sessionDirectory
|
|
4868
5587
|
});
|
|
4869
5588
|
const created = normalizeSDKResponse(createResponse, null, {
|
|
4870
5589
|
preferResponseOnMissingData: true
|
|
@@ -5096,64 +5815,6 @@ import { lookup as dnsLookup } from "node:dns/promises";
|
|
|
5096
5815
|
import * as https from "node:https";
|
|
5097
5816
|
import { isIP } from "node:net";
|
|
5098
5817
|
import { domainToASCII } from "node:url";
|
|
5099
|
-
|
|
5100
|
-
// ../plugin/src/features/magic-context/smart-notes/types.ts
|
|
5101
|
-
var SMART_NOTE_CHECK_POLICY_VERSION = 1;
|
|
5102
|
-
var SMART_NOTE_CHECK_FLOOR_MS = 5 * 60 * 1000;
|
|
5103
|
-
var SMART_NOTE_CHECK_CEILING_MS = 24 * 60 * 60 * 1000;
|
|
5104
|
-
var SMART_NOTE_CHECK_DEFAULT_INTERVAL_MS = 60 * 60 * 1000;
|
|
5105
|
-
var SMART_NOTE_CHECK_MAX_STALENESS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
5106
|
-
var SMART_NOTE_CHECK_LIVENESS_RECHECK_MS = 24 * 60 * 60 * 1000;
|
|
5107
|
-
|
|
5108
|
-
class SmartNoteNetworkError extends Error {
|
|
5109
|
-
isSmartNoteNetworkError = true;
|
|
5110
|
-
terminal;
|
|
5111
|
-
constructor(message, options = {}) {
|
|
5112
|
-
super(message);
|
|
5113
|
-
this.name = "SmartNoteNetworkError";
|
|
5114
|
-
this.terminal = options.terminal ?? false;
|
|
5115
|
-
}
|
|
5116
|
-
}
|
|
5117
|
-
|
|
5118
|
-
class SmartNoteSecurityError extends Error {
|
|
5119
|
-
isSmartNoteSecurityError = true;
|
|
5120
|
-
constructor(message) {
|
|
5121
|
-
super(message);
|
|
5122
|
-
this.name = "SmartNoteSecurityError";
|
|
5123
|
-
}
|
|
5124
|
-
}
|
|
5125
|
-
function isSmartNoteNetworkError(error) {
|
|
5126
|
-
return error instanceof SmartNoteNetworkError || error instanceof Error && (error.name === "SmartNoteNetworkError" || error.message.includes("SmartNoteNetworkError") || error.message.includes("SMART_NOTE_NETWORK"));
|
|
5127
|
-
}
|
|
5128
|
-
function isTerminalSmartNoteNetworkError(error) {
|
|
5129
|
-
return error instanceof SmartNoteNetworkError && error.terminal;
|
|
5130
|
-
}
|
|
5131
|
-
function parseSmartNoteManifest(json) {
|
|
5132
|
-
if (!json)
|
|
5133
|
-
return { capabilities: [] };
|
|
5134
|
-
try {
|
|
5135
|
-
const parsed = JSON.parse(json);
|
|
5136
|
-
const capabilities = Array.isArray(parsed.capabilities) ? parsed.capabilities.filter((c) => ["readFile", "gitHeadSha", "gitTag", "gitLog", "httpGet"].includes(String(c))) : [];
|
|
5137
|
-
return {
|
|
5138
|
-
capabilities,
|
|
5139
|
-
readFiles: stringArray(parsed.readFiles),
|
|
5140
|
-
hosts: stringArray(parsed.hosts),
|
|
5141
|
-
urls: stringArray(parsed.urls),
|
|
5142
|
-
signals: stringArray(parsed.signals),
|
|
5143
|
-
summary: typeof parsed.summary === "string" ? parsed.summary : undefined
|
|
5144
|
-
};
|
|
5145
|
-
} catch {
|
|
5146
|
-
return { capabilities: [] };
|
|
5147
|
-
}
|
|
5148
|
-
}
|
|
5149
|
-
function stringArray(value) {
|
|
5150
|
-
if (!Array.isArray(value))
|
|
5151
|
-
return;
|
|
5152
|
-
const arr = value.filter((item) => typeof item === "string");
|
|
5153
|
-
return arr.length > 0 ? arr : undefined;
|
|
5154
|
-
}
|
|
5155
|
-
|
|
5156
|
-
// ../plugin/src/features/magic-context/smart-notes/ssrf-guard.ts
|
|
5157
5818
|
var DNS_TIMEOUT_MS = 3000;
|
|
5158
5819
|
var DEFAULT_HTTP_TIMEOUT_MS = 5000;
|
|
5159
5820
|
var DEFAULT_HTTP_BODY_LIMIT_BYTES = 64 * 1024;
|
|
@@ -5676,7 +6337,7 @@ function getAsyncModule() {
|
|
|
5676
6337
|
asyncModulePromise ??= (async () => {
|
|
5677
6338
|
const [{ default: singlefileAsyncifyVariant }, { newQuickJSAsyncWASMModuleFromVariant }] = await Promise.all([
|
|
5678
6339
|
import("./index-ayd60xnw.js").then((m)=>__toESM(m.default,1)),
|
|
5679
|
-
import("./index-
|
|
6340
|
+
import("./index-3pnd4qh9.js")
|
|
5680
6341
|
]);
|
|
5681
6342
|
return newQuickJSAsyncWASMModuleFromVariant(singlefileAsyncifyVariant);
|
|
5682
6343
|
})();
|
|
@@ -5895,12 +6556,12 @@ Remember: output only the JSON object described by the system prompt.`;
|
|
|
5895
6556
|
});
|
|
5896
6557
|
};
|
|
5897
6558
|
try {
|
|
5898
|
-
const createResponse = await
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
}
|
|
5903
|
-
|
|
6559
|
+
const createResponse = await createChildSessionWithFence({
|
|
6560
|
+
client: args.client,
|
|
6561
|
+
db: args.db ?? null,
|
|
6562
|
+
parentSessionId: args.parentSessionId,
|
|
6563
|
+
title: `magic-context-smart-note-compile-${args.note.id}`,
|
|
6564
|
+
directory: args.sessionDirectory ?? args.projectIdentity
|
|
5904
6565
|
});
|
|
5905
6566
|
const created = normalizeSDKResponse(createResponse, null, {
|
|
5906
6567
|
preferResponseOnMissingData: true
|
|
@@ -6077,213 +6738,66 @@ function capabilityUses(code) {
|
|
|
6077
6738
|
const uses = new Set;
|
|
6078
6739
|
const regex = /\bcap\s*\.\s*(readFile|gitHeadSha|gitTag|gitLog|httpGet)\s*\(/g;
|
|
6079
6740
|
for (const match of code.matchAll(regex))
|
|
6080
|
-
uses.add(match[1]);
|
|
6081
|
-
return uses;
|
|
6082
|
-
}
|
|
6083
|
-
function literalCalls(code, method) {
|
|
6084
|
-
const regex = new RegExp(`\\bcap\\s*\\.\\s*${method}\\s*\\(\\s*(["'])((?:\\\\.|(?!\\1)[^\\\\])*)\\1`, "g");
|
|
6085
|
-
const values = [];
|
|
6086
|
-
for (const match of code.matchAll(regex)) {
|
|
6087
|
-
values.push(match[2].replace(/\\([\\"'])/g, "$1"));
|
|
6088
|
-
}
|
|
6089
|
-
return values;
|
|
6090
|
-
}
|
|
6091
|
-
function normalizeCron(cron) {
|
|
6092
|
-
if (cron.length > MAX_CRON_CHARS)
|
|
6093
|
-
throw new Error("check_cron exceeds 256 characters");
|
|
6094
|
-
const normalized = cron.trim() || "0 * * * *";
|
|
6095
|
-
const parsed = parseCron(normalized);
|
|
6096
|
-
if (!parsed.ok)
|
|
6097
|
-
throw new Error(`invalid check_cron: ${parsed.error}`);
|
|
6098
|
-
const next = nextOccurrence(parsed.cron, new Date, undefined, SMART_NOTE_CHECK_CEILING_MS);
|
|
6099
|
-
if (!next)
|
|
6100
|
-
throw new Error("check_cron has no occurrence within the scheduling ceiling");
|
|
6101
|
-
return normalized;
|
|
6102
|
-
}
|
|
6103
|
-
function unique(items) {
|
|
6104
|
-
return [...new Set(items)];
|
|
6105
|
-
}
|
|
6106
|
-
function uniqueStrings(items, normalize = (value) => value) {
|
|
6107
|
-
if (!Array.isArray(items))
|
|
6108
|
-
return;
|
|
6109
|
-
const values = unique(items.slice(0, MAX_MANIFEST_ENTRIES).filter((item) => typeof item === "string" && item.length > 0).map(normalize));
|
|
6110
|
-
return values.length > 0 ? values : undefined;
|
|
6111
|
-
}
|
|
6112
|
-
function boundedError(error) {
|
|
6113
|
-
return error.slice(0, MAX_COMPILER_ERROR_CHARS);
|
|
6114
|
-
}
|
|
6115
|
-
|
|
6116
|
-
// ../plugin/src/features/magic-context/smart-notes/schedule.ts
|
|
6117
|
-
function nextSmartNoteCheckDueAt(cron, options = {}) {
|
|
6118
|
-
const now = options.now ?? Date.now();
|
|
6119
|
-
const floorMs = options.floorMs ?? SMART_NOTE_CHECK_FLOOR_MS;
|
|
6120
|
-
const ceilingMs = options.ceilingMs ?? SMART_NOTE_CHECK_CEILING_MS;
|
|
6121
|
-
const rawNext = cron?.trim() ? nextDueAtMs(cron, now, undefined, ceilingMs) : null;
|
|
6122
|
-
const rawDelta = rawNext ? rawNext - now : SMART_NOTE_CHECK_DEFAULT_INTERVAL_MS;
|
|
6123
|
-
const clamped = Math.min(ceilingMs, Math.max(floorMs, rawDelta));
|
|
6124
|
-
const jittered = clamped + deterministicJitterMs(clamped, options.noteId, options.hash);
|
|
6125
|
-
const bounded = Math.min(ceilingMs, Math.max(floorMs, jittered));
|
|
6126
|
-
return now + bounded;
|
|
6127
|
-
}
|
|
6128
|
-
function deterministicJitterMs(intervalMs, noteId, hash) {
|
|
6129
|
-
const max = Math.min(60000, Math.floor(intervalMs * 0.1));
|
|
6130
|
-
if (max <= 0)
|
|
6131
|
-
return 0;
|
|
6132
|
-
const seed = `${noteId ?? 0}:${hash ?? ""}`;
|
|
6133
|
-
let h = 2166136261;
|
|
6134
|
-
for (let i = 0;i < seed.length; i++) {
|
|
6135
|
-
h ^= seed.charCodeAt(i);
|
|
6136
|
-
h = Math.imul(h, 16777619);
|
|
6137
|
-
}
|
|
6138
|
-
const unsigned = h >>> 0;
|
|
6139
|
-
return unsigned % (max * 2 + 1) - max;
|
|
6140
|
-
}
|
|
6141
|
-
|
|
6142
|
-
// ../plugin/src/features/magic-context/smart-notes/storage.ts
|
|
6143
|
-
function toSmartNote(note) {
|
|
6144
|
-
return {
|
|
6145
|
-
...note,
|
|
6146
|
-
checkStatus: note.checkStatus ?? "uncompiled",
|
|
6147
|
-
checkFailureCount: note.checkFailureCount ?? 0,
|
|
6148
|
-
checkNetworkFailureCount: note.checkNetworkFailureCount ?? 0,
|
|
6149
|
-
policyVersion: note.policyVersion ?? 0
|
|
6150
|
-
};
|
|
6151
|
-
}
|
|
6152
|
-
function commitSmartNoteState(db, args) {
|
|
6153
|
-
db.exec("BEGIN IMMEDIATE");
|
|
6154
|
-
let leaseLost = false;
|
|
6155
|
-
let committed = false;
|
|
6156
|
-
try {
|
|
6157
|
-
if (args.leaseHeld && !args.leaseHeld()) {
|
|
6158
|
-
leaseLost = true;
|
|
6159
|
-
} else if (claimExpectedState(db, args.expected)) {
|
|
6160
|
-
args.write();
|
|
6161
|
-
committed = true;
|
|
6162
|
-
}
|
|
6163
|
-
db.exec("COMMIT");
|
|
6164
|
-
} catch (error) {
|
|
6165
|
-
try {
|
|
6166
|
-
db.exec("ROLLBACK");
|
|
6167
|
-
} catch {}
|
|
6168
|
-
throw error;
|
|
6169
|
-
}
|
|
6170
|
-
if (leaseLost) {
|
|
6171
|
-
throw new Error(`Dream lease lost during smart-note ${args.phase} commit`);
|
|
6172
|
-
}
|
|
6173
|
-
if (!committed) {
|
|
6174
|
-
log(`[debug] smart note #${args.expected.noteId}: discarded stale ${args.phase} result`);
|
|
6175
|
-
}
|
|
6176
|
-
return committed;
|
|
6177
|
-
}
|
|
6178
|
-
function claimExpectedState(db, expected) {
|
|
6179
|
-
if (expected.kind === "compiled-check") {
|
|
6180
|
-
return db.prepare(`UPDATE notes SET id = id
|
|
6181
|
-
WHERE id = ? AND type = 'smart' AND status = 'pending'
|
|
6182
|
-
AND check_status = 'compiled' AND compiled_check = ?
|
|
6183
|
-
AND check_hash IS ? AND check_compiled_at IS ?`).run(expected.noteId, expected.compiledCheck, expected.checkHash, expected.checkCompiledAt).changes > 0;
|
|
6184
|
-
}
|
|
6185
|
-
const statusClause = expected.checkStatus ? " AND check_status = ?" : "";
|
|
6186
|
-
const params = [
|
|
6187
|
-
expected.noteId,
|
|
6188
|
-
expected.content,
|
|
6189
|
-
expected.surfaceCondition,
|
|
6190
|
-
expected.updatedAt
|
|
6191
|
-
];
|
|
6192
|
-
if (expected.checkStatus)
|
|
6193
|
-
params.push(expected.checkStatus);
|
|
6194
|
-
return db.prepare(`UPDATE notes SET id = id
|
|
6195
|
-
WHERE id = ? AND type = 'smart' AND status = 'pending'
|
|
6196
|
-
AND content = ? AND surface_condition IS ? AND updated_at = ?${statusClause}`).run(...params).changes > 0;
|
|
6197
|
-
}
|
|
6198
|
-
function getDueCompiledSmartNoteChecks(db, projectPath, now, limit) {
|
|
6199
|
-
return getPendingSmartNotes(db, projectPath).map(toSmartNote).filter((note) => note.checkStatus === "compiled" && note.compiledCheck !== null && note.policyVersion === SMART_NOTE_CHECK_POLICY_VERSION && (note.checkQuarantinedUntil === null || note.checkQuarantinedUntil <= now) && (note.checkNextDueAt === null || note.checkNextDueAt <= now)).sort((a, b) => (a.checkNextDueAt ?? 0) - (b.checkNextDueAt ?? 0) || a.id - b.id).slice(0, Math.max(1, limit));
|
|
6200
|
-
}
|
|
6201
|
-
function getSmartNotesNeedingCompilation(db, projectPath, now, limit) {
|
|
6202
|
-
return getPendingSmartNotes(db, projectPath).map(toSmartNote).filter((note) => (note.checkNextDueAt === null || note.checkNextDueAt <= now) && (note.checkStatus === "uncompiled" || note.checkStatus === "failing" || note.compiledCheck === null || note.policyVersion !== SMART_NOTE_CHECK_POLICY_VERSION)).sort((a, b) => a.createdAt - b.createdAt || a.id - b.id).slice(0, Math.max(1, limit));
|
|
6203
|
-
}
|
|
6204
|
-
function getStaleCompiledSmartNotes(db, projectPath, now, limit) {
|
|
6205
|
-
const staleBefore = now - SMART_NOTE_CHECK_MAX_STALENESS_MS;
|
|
6206
|
-
const livenessBefore = now - SMART_NOTE_CHECK_LIVENESS_RECHECK_MS;
|
|
6207
|
-
return getPendingSmartNotes(db, projectPath).map(toSmartNote).filter((note) => note.checkStatus === "compiled" && note.compiledCheck !== null && note.policyVersion === SMART_NOTE_CHECK_POLICY_VERSION && note.checkFalseSinceAt !== null && note.checkFalseSinceAt <= staleBefore && (note.checkLastLivenessAt === null || note.checkLastLivenessAt <= livenessBefore)).sort((a, b) => (a.checkFalseSinceAt ?? 0) - (b.checkFalseSinceAt ?? 0) || a.id - b.id).slice(0, Math.max(1, limit));
|
|
6208
|
-
}
|
|
6209
|
-
function storeCompiledSmartNoteCheck(db, args) {
|
|
6210
|
-
db.prepare(`UPDATE notes
|
|
6211
|
-
SET compiled_check = ?,
|
|
6212
|
-
manifest_json = ?,
|
|
6213
|
-
check_hash = ?,
|
|
6214
|
-
check_cron = ?,
|
|
6215
|
-
check_version = 1,
|
|
6216
|
-
check_status = 'compiled',
|
|
6217
|
-
check_failure_count = 0,
|
|
6218
|
-
check_network_failure_count = 0,
|
|
6219
|
-
check_quarantined_until = NULL,
|
|
6220
|
-
check_next_due_at = ?,
|
|
6221
|
-
check_compiled_at = ?,
|
|
6222
|
-
check_false_since_at = COALESCE(check_false_since_at, ?),
|
|
6223
|
-
check_last_liveness_at = NULL,
|
|
6224
|
-
policy_version = ?,
|
|
6225
|
-
updated_at = ?
|
|
6226
|
-
WHERE id = ? AND type = 'smart'`).run(args.compiledCheck, JSON.stringify(args.manifest), args.checkHash, args.checkCron, args.nextDueAt, args.now, args.now, SMART_NOTE_CHECK_POLICY_VERSION, args.now, args.noteId);
|
|
6227
|
-
}
|
|
6228
|
-
function markCompiledCheckFalse(db, noteId, nextDueAt, now) {
|
|
6229
|
-
db.prepare(`UPDATE notes
|
|
6230
|
-
SET last_checked_at = ?,
|
|
6231
|
-
updated_at = ?,
|
|
6232
|
-
check_next_due_at = ?,
|
|
6233
|
-
check_failure_count = 0,
|
|
6234
|
-
check_network_failure_count = 0,
|
|
6235
|
-
check_false_since_at = COALESCE(check_false_since_at, ?)
|
|
6236
|
-
WHERE id = ? AND type = 'smart'`).run(now, now, nextDueAt, now, noteId);
|
|
6741
|
+
uses.add(match[1]);
|
|
6742
|
+
return uses;
|
|
6237
6743
|
}
|
|
6238
|
-
function
|
|
6239
|
-
const
|
|
6240
|
-
const
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
updated_at = ?
|
|
6246
|
-
WHERE id = ? AND type = 'smart'`).run(failureCount, status, now + backoffMs(failureCount), now, noteId);
|
|
6744
|
+
function literalCalls(code, method) {
|
|
6745
|
+
const regex = new RegExp(`\\bcap\\s*\\.\\s*${method}\\s*\\(\\s*(["'])((?:\\\\.|(?!\\1)[^\\\\])*)\\1`, "g");
|
|
6746
|
+
const values = [];
|
|
6747
|
+
for (const match of code.matchAll(regex)) {
|
|
6748
|
+
values.push(match[2].replace(/\\([\\"'])/g, "$1"));
|
|
6749
|
+
}
|
|
6750
|
+
return values;
|
|
6247
6751
|
}
|
|
6248
|
-
function
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
const
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6752
|
+
function normalizeCron(cron) {
|
|
6753
|
+
if (cron.length > MAX_CRON_CHARS)
|
|
6754
|
+
throw new Error("check_cron exceeds 256 characters");
|
|
6755
|
+
const normalized = cron.trim() || "0 * * * *";
|
|
6756
|
+
const parsed = parseCron(normalized);
|
|
6757
|
+
if (!parsed.ok)
|
|
6758
|
+
throw new Error(`invalid check_cron: ${parsed.error}`);
|
|
6759
|
+
const next = nextOccurrence(parsed.cron, new Date, undefined, SMART_NOTE_CHECK_CEILING_MS);
|
|
6760
|
+
if (!next)
|
|
6761
|
+
throw new Error("check_cron has no occurrence within the scheduling ceiling");
|
|
6762
|
+
return normalized;
|
|
6259
6763
|
}
|
|
6260
|
-
function
|
|
6261
|
-
|
|
6262
|
-
SET check_last_liveness_at = ?, updated_at = ?
|
|
6263
|
-
WHERE id = ? AND type = 'smart'`).run(now, now, noteId);
|
|
6764
|
+
function unique(items) {
|
|
6765
|
+
return [...new Set(items)];
|
|
6264
6766
|
}
|
|
6265
|
-
function
|
|
6266
|
-
|
|
6767
|
+
function uniqueStrings(items, normalize = (value) => value) {
|
|
6768
|
+
if (!Array.isArray(items))
|
|
6769
|
+
return;
|
|
6770
|
+
const values = unique(items.slice(0, MAX_MANIFEST_ENTRIES).filter((item) => typeof item === "string" && item.length > 0).map(normalize));
|
|
6771
|
+
return values.length > 0 ? values : undefined;
|
|
6267
6772
|
}
|
|
6268
|
-
function
|
|
6269
|
-
|
|
6270
|
-
const status = failureCount >= maxFailures ? "fallback" : "uncompiled";
|
|
6271
|
-
db.prepare(`UPDATE notes
|
|
6272
|
-
SET check_failure_count = ?,
|
|
6273
|
-
check_status = ?,
|
|
6274
|
-
check_next_due_at = ?,
|
|
6275
|
-
updated_at = ?
|
|
6276
|
-
WHERE id = ? AND type = 'smart'`).run(failureCount, status, now + backoffMs(failureCount), now, noteId);
|
|
6773
|
+
function boundedError(error) {
|
|
6774
|
+
return error.slice(0, MAX_COMPILER_ERROR_CHARS);
|
|
6277
6775
|
}
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
const
|
|
6282
|
-
|
|
6776
|
+
|
|
6777
|
+
// ../plugin/src/features/magic-context/smart-notes/schedule.ts
|
|
6778
|
+
function nextSmartNoteCheckDueAt(cron, options = {}) {
|
|
6779
|
+
const now = options.now ?? Date.now();
|
|
6780
|
+
const floorMs = options.floorMs ?? SMART_NOTE_CHECK_FLOOR_MS;
|
|
6781
|
+
const ceilingMs = options.ceilingMs ?? SMART_NOTE_CHECK_CEILING_MS;
|
|
6782
|
+
const rawNext = cron?.trim() ? nextDueAtMs(cron, now, undefined, ceilingMs) : null;
|
|
6783
|
+
const rawDelta = rawNext ? rawNext - now : SMART_NOTE_CHECK_DEFAULT_INTERVAL_MS;
|
|
6784
|
+
const clamped = Math.min(ceilingMs, Math.max(floorMs, rawDelta));
|
|
6785
|
+
const jittered = clamped + deterministicJitterMs(clamped, options.noteId, options.hash);
|
|
6786
|
+
const bounded = Math.min(ceilingMs, Math.max(floorMs, jittered));
|
|
6787
|
+
return now + bounded;
|
|
6283
6788
|
}
|
|
6284
|
-
function
|
|
6285
|
-
const
|
|
6286
|
-
|
|
6789
|
+
function deterministicJitterMs(intervalMs, noteId, hash) {
|
|
6790
|
+
const max = Math.min(60000, Math.floor(intervalMs * 0.1));
|
|
6791
|
+
if (max <= 0)
|
|
6792
|
+
return 0;
|
|
6793
|
+
const seed = `${noteId ?? 0}:${hash ?? ""}`;
|
|
6794
|
+
let h = 2166136261;
|
|
6795
|
+
for (let i = 0;i < seed.length; i++) {
|
|
6796
|
+
h ^= seed.charCodeAt(i);
|
|
6797
|
+
h = Math.imul(h, 16777619);
|
|
6798
|
+
}
|
|
6799
|
+
const unsigned = h >>> 0;
|
|
6800
|
+
return unsigned % (max * 2 + 1) - max;
|
|
6287
6801
|
}
|
|
6288
6802
|
|
|
6289
6803
|
// ../plugin/src/features/magic-context/smart-notes/runner.ts
|
|
@@ -6681,12 +7195,12 @@ async function confirmReadOnly(args, noteId, content, surfaceCondition, leaseSig
|
|
|
6681
7195
|
});
|
|
6682
7196
|
};
|
|
6683
7197
|
try {
|
|
6684
|
-
const createResponse = await
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
}
|
|
6689
|
-
|
|
7198
|
+
const createResponse = await createChildSessionWithFence({
|
|
7199
|
+
client: args.client,
|
|
7200
|
+
db: args.db,
|
|
7201
|
+
parentSessionId: args.parentSessionId,
|
|
7202
|
+
title: `magic-context-smart-note-confirm-${noteId}`,
|
|
7203
|
+
directory: args.sessionDirectory ?? args.projectIdentity
|
|
6690
7204
|
});
|
|
6691
7205
|
const created = normalizeSDKResponse(createResponse, null, {
|
|
6692
7206
|
preferResponseOnMissingData: true
|
|
@@ -7019,6 +7533,7 @@ async function mapMemories(args) {
|
|
|
7019
7533
|
result.independent += counts.independent;
|
|
7020
7534
|
result.remaining -= counts.mapped + counts.independent;
|
|
7021
7535
|
result.batches += 1;
|
|
7536
|
+
args.onProgress?.(result.mapped + result.independent);
|
|
7022
7537
|
}
|
|
7023
7538
|
result.complete = result.remaining === 0;
|
|
7024
7539
|
log(`[dreamer] map-memories: mapped=${result.mapped} independent=${result.independent} batches=${result.batches} remaining=${result.remaining} complete=${result.complete}`);
|
|
@@ -7031,12 +7546,12 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
7031
7546
|
let agentSessionId = null;
|
|
7032
7547
|
const startedAt = Date.now();
|
|
7033
7548
|
try {
|
|
7034
|
-
const createResponse = await
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7549
|
+
const createResponse = await createChildSessionWithFence({
|
|
7550
|
+
client: args.client,
|
|
7551
|
+
db: args.db,
|
|
7552
|
+
parentSessionId: args.parentSessionId,
|
|
7553
|
+
title: "magic-context-dream-map-memories",
|
|
7554
|
+
directory: args.sessionDirectory
|
|
7040
7555
|
});
|
|
7041
7556
|
const created = normalizeSDKResponse(createResponse, null, {
|
|
7042
7557
|
preferResponseOnMissingData: true
|
|
@@ -7620,6 +8135,7 @@ async function refreshPrimers(args) {
|
|
|
7620
8135
|
result.refreshed += 1;
|
|
7621
8136
|
else
|
|
7622
8137
|
result.skipped += 1;
|
|
8138
|
+
args.onProgress?.(result.refreshed + result.skipped);
|
|
7623
8139
|
}
|
|
7624
8140
|
log(`[dreamer] refresh-primers: refreshed=${result.refreshed} skipped=${result.skipped}`);
|
|
7625
8141
|
return result;
|
|
@@ -7648,12 +8164,12 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
|
|
|
7648
8164
|
let agentSessionId = null;
|
|
7649
8165
|
const startedAt = Date.now();
|
|
7650
8166
|
try {
|
|
7651
|
-
const createResponse = await
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
8167
|
+
const createResponse = await createChildSessionWithFence({
|
|
8168
|
+
client: args.client,
|
|
8169
|
+
db: args.db,
|
|
8170
|
+
parentSessionId: args.parentSessionId,
|
|
8171
|
+
title: "magic-context-dream-refresh-primers",
|
|
8172
|
+
directory: args.sessionDirectory
|
|
7657
8173
|
});
|
|
7658
8174
|
const created = normalizeSDKResponse(createResponse, null, {
|
|
7659
8175
|
preferResponseOnMissingData: true
|
|
@@ -8151,8 +8667,8 @@ function extractToolRows(parts) {
|
|
|
8151
8667
|
const record = part;
|
|
8152
8668
|
if (record.type !== "tool" || typeof record.tool !== "string")
|
|
8153
8669
|
continue;
|
|
8154
|
-
const
|
|
8155
|
-
const stateRecord =
|
|
8670
|
+
const state2 = record.state;
|
|
8671
|
+
const stateRecord = state2 && typeof state2 === "object" ? state2 : {};
|
|
8156
8672
|
const output = stringifyToolOutput(stateRecord.output);
|
|
8157
8673
|
const errorText = stringifyToolOutput(stateRecord.error);
|
|
8158
8674
|
const status = typeof stateRecord.status === "string" ? stateRecord.status : "";
|
|
@@ -8467,6 +8983,7 @@ async function runVerify(args) {
|
|
|
8467
8983
|
result.archived += counts.archived;
|
|
8468
8984
|
result.remaining -= counts.verified + counts.updated + counts.archived;
|
|
8469
8985
|
result.batches += 1;
|
|
8986
|
+
args.onProgress?.(result.verified + result.updated + result.archived);
|
|
8470
8987
|
}
|
|
8471
8988
|
result.complete = result.remaining === 0;
|
|
8472
8989
|
log(`[dreamer] ${args.forceBroad ? "verify-broad" : "verify"}: verified=${result.verified} updated=${result.updated} archived=${result.archived} batches=${result.batches} remaining=${result.remaining} complete=${result.complete}`);
|
|
@@ -8479,12 +8996,12 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
8479
8996
|
let agentSessionId = null;
|
|
8480
8997
|
const startedAt = Date.now();
|
|
8481
8998
|
try {
|
|
8482
|
-
const createResponse = await
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8999
|
+
const createResponse = await createChildSessionWithFence({
|
|
9000
|
+
client: args.client,
|
|
9001
|
+
db: args.db,
|
|
9002
|
+
parentSessionId: args.parentSessionId,
|
|
9003
|
+
title: "magic-context-dream-verify",
|
|
9004
|
+
directory: args.sessionDirectory
|
|
8488
9005
|
});
|
|
8489
9006
|
const created = normalizeSDKResponse(createResponse, null, {
|
|
8490
9007
|
preferResponseOnMissingData: true
|
|
@@ -8758,6 +9275,20 @@ function createDreamTaskExecutor(deps) {
|
|
|
8758
9275
|
const { db, projectIdentity, holderId, leaseKey } = ctx;
|
|
8759
9276
|
const startedAt = Date.now();
|
|
8760
9277
|
const deadline = startedAt + config.timeoutMinutes * 60 * 1000;
|
|
9278
|
+
const backlogAtStart = getDreamTaskBacklog(db, projectIdentity, config.task);
|
|
9279
|
+
const reportProgress = (processed) => {
|
|
9280
|
+
deps.onProgress?.({
|
|
9281
|
+
task: config.task,
|
|
9282
|
+
processed: Math.max(0, processed),
|
|
9283
|
+
total: backlogAtStart.pending,
|
|
9284
|
+
startedAt
|
|
9285
|
+
});
|
|
9286
|
+
};
|
|
9287
|
+
reportProgress(0);
|
|
9288
|
+
const incompleteMessage = (remaining) => {
|
|
9289
|
+
const processed = processedDreamTaskItems(backlogAtStart.pending, remaining);
|
|
9290
|
+
return `${config.task} incomplete: ${remaining} remain (was ${backlogAtStart.pending} at run start; processed ${processed} this run)`;
|
|
9291
|
+
};
|
|
8761
9292
|
const parent = await resolveParentSessionId();
|
|
8762
9293
|
let moduleRoute;
|
|
8763
9294
|
if (config.task === "map-memories" || config.task === "compress-cues" || config.task === "verify" || config.task === "verify-broad" || config.task === "retrospective") {
|
|
@@ -8786,7 +9317,19 @@ function createDreamTaskExecutor(deps) {
|
|
|
8786
9317
|
name: config.task,
|
|
8787
9318
|
durationMs: Date.now() - startedAt,
|
|
8788
9319
|
resultChars: 0,
|
|
8789
|
-
...error ? { error } : {}
|
|
9320
|
+
...error ? { error } : {},
|
|
9321
|
+
backlog: (() => {
|
|
9322
|
+
const end = getDreamTaskBacklog(db, projectIdentity, config.task);
|
|
9323
|
+
const processed = processedDreamTaskItems(backlogAtStart.pending, end.pending);
|
|
9324
|
+
const value = {
|
|
9325
|
+
pendingAtStart: backlogAtStart.pending,
|
|
9326
|
+
totalAtStart: backlogAtStart.total,
|
|
9327
|
+
pendingAtEnd: end.pending,
|
|
9328
|
+
totalAtEnd: end.total,
|
|
9329
|
+
processed
|
|
9330
|
+
};
|
|
9331
|
+
return value;
|
|
9332
|
+
})()
|
|
8790
9333
|
}
|
|
8791
9334
|
],
|
|
8792
9335
|
tasksSucceeded: status === "completed" ? 1 : 0,
|
|
@@ -8820,8 +9363,8 @@ function createDreamTaskExecutor(deps) {
|
|
|
8820
9363
|
}
|
|
8821
9364
|
try {
|
|
8822
9365
|
if (config.task === "compress-cues") {
|
|
8823
|
-
if (deps.
|
|
8824
|
-
log("[dreamer] compress-cues: skipped (
|
|
9366
|
+
if (deps.mural?.enabled !== true) {
|
|
9367
|
+
log("[dreamer] compress-cues: skipped (mural is not enabled)");
|
|
8825
9368
|
recordRun("completed", null);
|
|
8826
9369
|
return { status: "completed" };
|
|
8827
9370
|
}
|
|
@@ -8840,12 +9383,13 @@ function createDreamTaskExecutor(deps) {
|
|
|
8840
9383
|
holderId,
|
|
8841
9384
|
leaseKey,
|
|
8842
9385
|
deadline,
|
|
8843
|
-
model: config.model ?? deps.
|
|
8844
|
-
fallbackModels: config.fallbackModels
|
|
9386
|
+
model: config.model ?? deps.mural.model ?? deps.dreamerModel,
|
|
9387
|
+
fallbackModels: config.fallbackModels,
|
|
9388
|
+
onProgress: (processed) => reportProgress(processed)
|
|
8845
9389
|
});
|
|
8846
9390
|
log(`[dreamer] compress-cues: compressed=${result.compressed} skipped=${result.skipped} chunks=${result.chunks} remaining=${result.remaining}`);
|
|
8847
9391
|
if (!result.complete) {
|
|
8848
|
-
const error =
|
|
9392
|
+
const error = incompleteMessage(result.remaining);
|
|
8849
9393
|
recordRun("failed", error);
|
|
8850
9394
|
return { status: "failed", transient: true, error };
|
|
8851
9395
|
}
|
|
@@ -8882,11 +9426,12 @@ function createDreamTaskExecutor(deps) {
|
|
|
8882
9426
|
deadline,
|
|
8883
9427
|
model: config.model,
|
|
8884
9428
|
fallbackModels: config.fallbackModels,
|
|
8885
|
-
moduleRoute
|
|
9429
|
+
moduleRoute,
|
|
9430
|
+
onProgress: (processed) => reportProgress(processed)
|
|
8886
9431
|
});
|
|
8887
9432
|
log(`[dreamer] map-memories: mapped=${result.mapped} independent=${result.independent} batches=${result.batches} remaining=${result.remaining}`);
|
|
8888
9433
|
if (!result.complete) {
|
|
8889
|
-
const error =
|
|
9434
|
+
const error = incompleteMessage(result.remaining);
|
|
8890
9435
|
recordRun("failed", error);
|
|
8891
9436
|
return { status: "failed", transient: true, error };
|
|
8892
9437
|
}
|
|
@@ -8908,10 +9453,11 @@ function createDreamTaskExecutor(deps) {
|
|
|
8908
9453
|
model: config.model,
|
|
8909
9454
|
fallbackModels: config.fallbackModels,
|
|
8910
9455
|
language: config.language ?? deps.language,
|
|
8911
|
-
moduleRoute
|
|
9456
|
+
moduleRoute,
|
|
9457
|
+
onProgress: (processed) => reportProgress(processed)
|
|
8912
9458
|
});
|
|
8913
9459
|
if (!result.complete) {
|
|
8914
|
-
const error =
|
|
9460
|
+
const error = incompleteMessage(result.remaining);
|
|
8915
9461
|
recordRun("failed", error, {
|
|
8916
9462
|
memoryChanges: computeMemoryDelta(memoryBefore)
|
|
8917
9463
|
});
|
|
@@ -8970,11 +9516,12 @@ function createDreamTaskExecutor(deps) {
|
|
|
8970
9516
|
deadline,
|
|
8971
9517
|
model: config.model,
|
|
8972
9518
|
fallbackModels: config.fallbackModels,
|
|
8973
|
-
...moduleArgs
|
|
9519
|
+
...moduleArgs,
|
|
9520
|
+
onProgress: (processed) => reportProgress(processed)
|
|
8974
9521
|
});
|
|
8975
9522
|
log(`[dreamer] classify-memories: stage=${result.stage} classified=${result.classified} changed=${result.changed} chunks=${result.chunks} remaining=${result.remaining}`);
|
|
8976
9523
|
if (!result.complete) {
|
|
8977
|
-
const error =
|
|
9524
|
+
const error = incompleteMessage(result.remaining);
|
|
8978
9525
|
recordRun("failed", error);
|
|
8979
9526
|
return { status: "failed", transient: true, error };
|
|
8980
9527
|
}
|
|
@@ -9010,7 +9557,8 @@ function createDreamTaskExecutor(deps) {
|
|
|
9010
9557
|
model: config.model,
|
|
9011
9558
|
fallbackModels: config.fallbackModels,
|
|
9012
9559
|
language: config.language ?? deps.language,
|
|
9013
|
-
rawProviderFactory: deps.primerRawProviderFactory
|
|
9560
|
+
rawProviderFactory: deps.primerRawProviderFactory,
|
|
9561
|
+
onProgress: (processed) => reportProgress(processed)
|
|
9014
9562
|
});
|
|
9015
9563
|
recordRun("completed", null);
|
|
9016
9564
|
log(`[dreamer] refresh-primers: refreshed=${result.refreshed} skipped=${result.skipped}`);
|
|
@@ -9064,6 +9612,8 @@ function createDreamTaskExecutor(deps) {
|
|
|
9064
9612
|
recordRun("failed", brief);
|
|
9065
9613
|
log(`[dreamer] task ${config.task} failed (transient=${transient}): ${brief}`);
|
|
9066
9614
|
return { status: "failed", transient, error: brief };
|
|
9615
|
+
} finally {
|
|
9616
|
+
deps.onProgress?.(null, config.task);
|
|
9067
9617
|
}
|
|
9068
9618
|
};
|
|
9069
9619
|
}
|
|
@@ -9171,12 +9721,12 @@ async function runRetrospectiveTask(config, ctx, helpers) {
|
|
|
9171
9721
|
});
|
|
9172
9722
|
let childSessionId = null;
|
|
9173
9723
|
try {
|
|
9174
|
-
const createResponse = await
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9724
|
+
const createResponse = await createChildSessionWithFence({
|
|
9725
|
+
client: deps.client,
|
|
9726
|
+
db,
|
|
9727
|
+
parentSessionId: parent ?? undefined,
|
|
9728
|
+
title: "magic-context-dream-retrospective",
|
|
9729
|
+
directory: deps.sessionDirectory
|
|
9180
9730
|
});
|
|
9181
9731
|
const created = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
|
|
9182
9732
|
childSessionId = typeof created?.id === "string" ? created.id : null;
|
|
@@ -9361,12 +9911,12 @@ async function runAgenticTask(config, ctx, helpers) {
|
|
|
9361
9911
|
});
|
|
9362
9912
|
let childSessionId = null;
|
|
9363
9913
|
try {
|
|
9364
|
-
const createResponse = await
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
}
|
|
9369
|
-
|
|
9914
|
+
const createResponse = await createChildSessionWithFence({
|
|
9915
|
+
client: deps.client,
|
|
9916
|
+
db,
|
|
9917
|
+
parentSessionId: parent ?? undefined,
|
|
9918
|
+
title: `magic-context-dream-${task}`,
|
|
9919
|
+
directory: docsDir
|
|
9370
9920
|
});
|
|
9371
9921
|
const created = normalizeSDKResponse(createResponse, null, {
|
|
9372
9922
|
preferResponseOnMissingData: true
|
|
@@ -9439,66 +9989,6 @@ async function runAgenticTask(config, ctx, helpers) {
|
|
|
9439
9989
|
}
|
|
9440
9990
|
}
|
|
9441
9991
|
|
|
9442
|
-
// ../plugin/src/features/magic-context/dreamer/task-gates.ts
|
|
9443
|
-
function countActiveMemories(db, projectPath) {
|
|
9444
|
-
const row = db.prepare("SELECT COUNT(*) AS cnt FROM memories WHERE project_path = ? AND status IN ('active','permanent')").get(projectPath);
|
|
9445
|
-
return row?.cnt ?? 0;
|
|
9446
|
-
}
|
|
9447
|
-
function countUnmappedActiveMemories(db, projectPath) {
|
|
9448
|
-
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
9449
|
-
FROM memories m
|
|
9450
|
-
WHERE m.project_path = ?
|
|
9451
|
-
AND m.status IN ('active','permanent')
|
|
9452
|
-
AND NOT EXISTS (
|
|
9453
|
-
SELECT 1 FROM memory_verifications v WHERE v.memory_id = m.id
|
|
9454
|
-
)`).get(projectPath);
|
|
9455
|
-
return row?.cnt ?? 0;
|
|
9456
|
-
}
|
|
9457
|
-
function countCompartmentsSince(db, projectPath, since) {
|
|
9458
|
-
const row = db.prepare(`SELECT COUNT(*) AS cnt
|
|
9459
|
-
FROM compartments c
|
|
9460
|
-
JOIN session_projects sp ON sp.session_id = c.session_id
|
|
9461
|
-
WHERE sp.project_path = ? AND c.created_at > ?`).get(projectPath, since);
|
|
9462
|
-
return row?.cnt ?? 0;
|
|
9463
|
-
}
|
|
9464
|
-
function countProjectSessionsSince(db, projectPath, since) {
|
|
9465
|
-
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 WHERE project_path = ? AND updated_at > ?").get(projectPath, since);
|
|
9466
|
-
return row?.cnt ?? 0;
|
|
9467
|
-
}
|
|
9468
|
-
function evaluateTaskGate(task, ctx) {
|
|
9469
|
-
const { db, projectIdentity: project, lastRunAt } = ctx;
|
|
9470
|
-
switch (task) {
|
|
9471
|
-
case "map-memories":
|
|
9472
|
-
return countUnmappedActiveMemories(db, project) > 0;
|
|
9473
|
-
case "verify":
|
|
9474
|
-
return countActiveMemories(db, project) > 0;
|
|
9475
|
-
case "verify-broad":
|
|
9476
|
-
return countActiveMemories(db, project) > 0;
|
|
9477
|
-
case "curate":
|
|
9478
|
-
return countActiveMemories(db, project) > 0;
|
|
9479
|
-
case "compress-cues":
|
|
9480
|
-
return countActiveMemories(db, project) > 0;
|
|
9481
|
-
case "classify-memories":
|
|
9482
|
-
return countActiveMemories(db, project) > 0;
|
|
9483
|
-
case "retrospective":
|
|
9484
|
-
return countProjectSessionsSince(db, project, ctx.retrospectiveWatermarkMs ?? null) > 0;
|
|
9485
|
-
case "maintain-docs":
|
|
9486
|
-
return countCompartmentsSince(db, project, lastRunAt ?? 0) > 0;
|
|
9487
|
-
case "evaluate-smart-notes":
|
|
9488
|
-
return getSmartNotesNeedingCompilation(db, project, Date.now(), 1).length > 0 || getStaleCompiledSmartNotes(db, project, Date.now(), 1).length > 0 || getPendingSmartNotes(db, project).some((note) => note.checkStatus === "fallback");
|
|
9489
|
-
case "review-user-memories":
|
|
9490
|
-
return getUserMemoryCandidates(db).length >= ctx.promotionThreshold;
|
|
9491
|
-
case "promote-primers":
|
|
9492
|
-
return countPrimerCandidatesForProject(db, project) >= (ctx.promotionThreshold ?? 2);
|
|
9493
|
-
case "refresh-primers":
|
|
9494
|
-
return getActivePrimers(db, project).some((primer) => !primer.answer.trim() || primer.answerRefreshedAt == null || (primer.lastObservedAt ?? 0) > primer.answerRefreshedAt);
|
|
9495
|
-
default: {
|
|
9496
|
-
const _exhaustive = task;
|
|
9497
|
-
return Boolean(_exhaustive);
|
|
9498
|
-
}
|
|
9499
|
-
}
|
|
9500
|
-
}
|
|
9501
|
-
|
|
9502
9992
|
// ../plugin/src/features/magic-context/dreamer/task-scheduler.ts
|
|
9503
9993
|
var MAX_TASK_RETRIES = 3;
|
|
9504
9994
|
function ensureSeeded(db, projectIdentity, config, now) {
|
|
@@ -9538,11 +10028,11 @@ function planDueTasks(db, projectIdentity, tasks, now) {
|
|
|
9538
10028
|
const due = [];
|
|
9539
10029
|
for (const config of tasks) {
|
|
9540
10030
|
reconcileSchedule(db, projectIdentity, config, now);
|
|
9541
|
-
const
|
|
9542
|
-
if (!
|
|
10031
|
+
const state2 = getTaskScheduleState(db, projectIdentity, config.task);
|
|
10032
|
+
if (!state2 || state2.nextDueAt === null)
|
|
9543
10033
|
continue;
|
|
9544
|
-
if (now >=
|
|
9545
|
-
due.push({ config, scheduledAt:
|
|
10034
|
+
if (now >= state2.nextDueAt) {
|
|
10035
|
+
due.push({ config, scheduledAt: state2.nextDueAt });
|
|
9546
10036
|
}
|
|
9547
10037
|
}
|
|
9548
10038
|
return due;
|
|
@@ -9646,10 +10136,10 @@ async function runDomainGroup(deps, group, cb) {
|
|
|
9646
10136
|
cb?.onRan?.(due.config.task);
|
|
9647
10137
|
} else if (outcome.transient) {
|
|
9648
10138
|
recordTransientFailure(db, projectIdentity, due, finishedAt, outcome.error ?? null);
|
|
9649
|
-
cb?.onFailed?.(due.config.task);
|
|
10139
|
+
cb?.onFailed?.(due.config.task, outcome.error);
|
|
9650
10140
|
} else {
|
|
9651
10141
|
advanceAfterRun(db, projectIdentity, due, finishedAt, "failed", outcome.error ?? null);
|
|
9652
|
-
cb?.onFailed?.(due.config.task);
|
|
10142
|
+
cb?.onFailed?.(due.config.task, outcome.error);
|
|
9653
10143
|
}
|
|
9654
10144
|
}
|
|
9655
10145
|
} finally {
|
|
@@ -9658,7 +10148,15 @@ async function runDomainGroup(deps, group, cb) {
|
|
|
9658
10148
|
}
|
|
9659
10149
|
async function runManualDream(deps) {
|
|
9660
10150
|
const now = Date.now();
|
|
9661
|
-
const result = {
|
|
10151
|
+
const result = {
|
|
10152
|
+
ran: [],
|
|
10153
|
+
skippedNoWork: [],
|
|
10154
|
+
deferredBusy: [],
|
|
10155
|
+
failed: [],
|
|
10156
|
+
failureDetails: [],
|
|
10157
|
+
backlogBefore: {},
|
|
10158
|
+
backlogAfter: {}
|
|
10159
|
+
};
|
|
9662
10160
|
let selected;
|
|
9663
10161
|
let forceGate = false;
|
|
9664
10162
|
if (deps.task) {
|
|
@@ -9672,6 +10170,9 @@ async function runManualDream(deps) {
|
|
|
9672
10170
|
}
|
|
9673
10171
|
if (selected.length === 0)
|
|
9674
10172
|
return result;
|
|
10173
|
+
const selectedTaskNames = selected.map((config) => config.task);
|
|
10174
|
+
result.backlogBefore = getDreamTaskBacklogs(deps.db, deps.projectIdentity, selectedTaskNames);
|
|
10175
|
+
result.backlogAfter = { ...result.backlogBefore };
|
|
9675
10176
|
for (const cfg of selected)
|
|
9676
10177
|
ensureSeeded(deps.db, deps.projectIdentity, cfg, now);
|
|
9677
10178
|
const dueAll = selected.map((config) => ({ config, scheduledAt: now }));
|
|
@@ -9693,8 +10194,10 @@ async function runManualDream(deps) {
|
|
|
9693
10194
|
else
|
|
9694
10195
|
result.skippedNoWork.push(d.config.task);
|
|
9695
10196
|
}
|
|
9696
|
-
if (gated.length === 0)
|
|
10197
|
+
if (gated.length === 0) {
|
|
10198
|
+
result.backlogAfter = getDreamTaskBacklogs(deps.db, deps.projectIdentity, selectedTaskNames);
|
|
9697
10199
|
return result;
|
|
10200
|
+
}
|
|
9698
10201
|
const groups = new Map;
|
|
9699
10202
|
for (const d of gated) {
|
|
9700
10203
|
const kind = leaseKindFor(d.config.task);
|
|
@@ -9706,9 +10209,14 @@ async function runManualDream(deps) {
|
|
|
9706
10209
|
forceGate,
|
|
9707
10210
|
leaseWaitMs: MANUAL_RUN_LEASE_WAIT_MS,
|
|
9708
10211
|
onRan: (t) => result.ran.push(t),
|
|
9709
|
-
onFailed: (
|
|
10212
|
+
onFailed: (task, error) => {
|
|
10213
|
+
result.failed.push(task);
|
|
10214
|
+
if (error)
|
|
10215
|
+
result.failureDetails?.push(`${task}: ${error}`);
|
|
10216
|
+
},
|
|
9710
10217
|
onBusy: (t) => result.deferredBusy.push(t)
|
|
9711
10218
|
})));
|
|
10219
|
+
result.backlogAfter = getDreamTaskBacklogs(deps.db, deps.projectIdentity, selectedTaskNames);
|
|
9712
10220
|
return result;
|
|
9713
10221
|
}
|
|
9714
10222
|
async function runDueTasksForProject(deps) {
|
|
@@ -10023,10 +10531,11 @@ async function sweepProject(reg, origin, db, gitCommitEnabled) {
|
|
|
10023
10531
|
ensureProjectRegistered: reg.ensureRegistered,
|
|
10024
10532
|
language: reg.language,
|
|
10025
10533
|
dreamerModel: dreamerConfig.model,
|
|
10026
|
-
|
|
10534
|
+
mural: reg.mural,
|
|
10027
10535
|
memoryInjectionBudgetTokens: reg.memoryInjectionBudgetTokens,
|
|
10028
10536
|
transformMode: reg.transformMode,
|
|
10029
|
-
moduleClient: reg.moduleClient
|
|
10537
|
+
moduleClient: reg.moduleClient,
|
|
10538
|
+
onProgress: (progress, completedTask) => reg.onDreamerProgress?.(progress, completedTask)
|
|
10030
10539
|
});
|
|
10031
10540
|
const ran = await runDueTasksForProject({
|
|
10032
10541
|
db,
|
|
@@ -10696,13 +11205,18 @@ Unknown task "${requested}".`,
|
|
|
10696
11205
|
});
|
|
10697
11206
|
return;
|
|
10698
11207
|
}
|
|
11208
|
+
const backlogTasks = task ? [task] : CANONICAL_DREAM_TASKS;
|
|
11209
|
+
const backlogBefore = getDreamTaskBacklogs(deps.db, project.projectIdentity, backlogTasks);
|
|
10699
11210
|
sendCtxStatusMessage(pi, {
|
|
10700
11211
|
title: "/ctx-dream",
|
|
10701
11212
|
text: [
|
|
10702
11213
|
"## /ctx-dream",
|
|
10703
11214
|
"",
|
|
10704
11215
|
task ? `Running dream task "${task}" for ${project.projectIdentity}…` : `Starting dream run for ${project.projectIdentity}…`,
|
|
10705
|
-
`Project directory: ${project.projectDir}
|
|
11216
|
+
`Project directory: ${project.projectDir}`,
|
|
11217
|
+
"",
|
|
11218
|
+
"Backlog before starting:",
|
|
11219
|
+
formatDreamTaskBacklogs(backlogBefore, backlogTasks)
|
|
10706
11220
|
].join(`
|
|
10707
11221
|
`),
|
|
10708
11222
|
level: "info"
|
|
@@ -10717,10 +11231,16 @@ Unknown task "${requested}".`,
|
|
|
10717
11231
|
lines.push(`Ran: ${result.ran.join(", ")}`);
|
|
10718
11232
|
if (result.failed.length > 0)
|
|
10719
11233
|
lines.push(`Failed: ${result.failed.join(", ")}`);
|
|
11234
|
+
if ((result.failureDetails?.length ?? 0) > 0) {
|
|
11235
|
+
lines.push("Failure details:", ...(result.failureDetails ?? []).map((detail) => `- ${detail}`));
|
|
11236
|
+
}
|
|
10720
11237
|
if (result.skippedNoWork.length > 0)
|
|
10721
11238
|
lines.push(`Skipped (no work): ${result.skippedNoWork.join(", ")}`);
|
|
10722
11239
|
if (result.deferredBusy.length > 0)
|
|
10723
11240
|
lines.push(`Busy: ${result.deferredBusy.join(", ")} — another dream task holds this domain's lease; retry in a minute`);
|
|
11241
|
+
if (Object.keys(result.backlogAfter ?? {}).length > 0) {
|
|
11242
|
+
lines.push("", "Backlog at run end:", formatDreamTaskBacklogs(result.backlogAfter));
|
|
11243
|
+
}
|
|
10724
11244
|
if (lines.length === 0)
|
|
10725
11245
|
lines.push("No enabled dream tasks to run.");
|
|
10726
11246
|
sendCtxStatusMessage(pi, {
|
|
@@ -11047,6 +11567,101 @@ function executeFlush(db, sessionId) {
|
|
|
11047
11567
|
}
|
|
11048
11568
|
}
|
|
11049
11569
|
|
|
11570
|
+
// src/compaction-off-pi.ts
|
|
11571
|
+
var COMPACTION_OFF_COMMAND_UNAVAILABLE = `Unavailable: magic-context is in compaction-off mode (${COMPACTION_ENABLED_PATH}=false).`;
|
|
11572
|
+
var NO_TRANSITION = {
|
|
11573
|
+
recordToWrite: null,
|
|
11574
|
+
invalidatedBaseline: false,
|
|
11575
|
+
clearDeferredMarkerState: false,
|
|
11576
|
+
historianCatchUpSignaled: false,
|
|
11577
|
+
notice: null
|
|
11578
|
+
};
|
|
11579
|
+
var PI_COMPACTION_OFF_NOTICE = "Magic Context is in compaction-off mode. Native Pi compaction now owns this session's context window; the first turn after disabling may trigger one native compaction cycle on long sessions.";
|
|
11580
|
+
var PI_COMPACTION_ON_NOTICE = "Magic Context context-window management resumed. Run /ctx-wrapup to digest history accumulated while compaction was off.";
|
|
11581
|
+
function reconcilePiCompactionMode(args) {
|
|
11582
|
+
const stored = getCompactionModeRecord(args.db, args.sessionId);
|
|
11583
|
+
if (stored === "on_notice_pending") {
|
|
11584
|
+
return {
|
|
11585
|
+
...NO_TRANSITION,
|
|
11586
|
+
recordToWrite: "on",
|
|
11587
|
+
notice: PI_COMPACTION_ON_NOTICE
|
|
11588
|
+
};
|
|
11589
|
+
}
|
|
11590
|
+
if (stored === "off_notice_pending") {
|
|
11591
|
+
return {
|
|
11592
|
+
...NO_TRANSITION,
|
|
11593
|
+
recordToWrite: "off",
|
|
11594
|
+
notice: PI_COMPACTION_OFF_NOTICE
|
|
11595
|
+
};
|
|
11596
|
+
}
|
|
11597
|
+
if (!args.compactionOff) {
|
|
11598
|
+
if (stored === null || resolveCompactionModeRecord(stored) === "on") {
|
|
11599
|
+
return stored === null ? { ...NO_TRANSITION, recordToWrite: "on" } : NO_TRANSITION;
|
|
11600
|
+
}
|
|
11601
|
+
clearCachedM0M1(args.db, args.sessionId);
|
|
11602
|
+
if (!args.historianRunnable) {
|
|
11603
|
+
return {
|
|
11604
|
+
...NO_TRANSITION,
|
|
11605
|
+
recordToWrite: "on",
|
|
11606
|
+
invalidatedBaseline: true
|
|
11607
|
+
};
|
|
11608
|
+
}
|
|
11609
|
+
updateSessionMeta(args.db, args.sessionId, {
|
|
11610
|
+
compartmentInProgress: true
|
|
11611
|
+
});
|
|
11612
|
+
setCompactionModeRecord(args.db, args.sessionId, "on_notice_pending");
|
|
11613
|
+
return {
|
|
11614
|
+
recordToWrite: "on",
|
|
11615
|
+
invalidatedBaseline: true,
|
|
11616
|
+
clearDeferredMarkerState: false,
|
|
11617
|
+
historianCatchUpSignaled: true,
|
|
11618
|
+
notice: PI_COMPACTION_ON_NOTICE
|
|
11619
|
+
};
|
|
11620
|
+
}
|
|
11621
|
+
if (resolveCompactionModeRecord(stored) === "off")
|
|
11622
|
+
return NO_TRANSITION;
|
|
11623
|
+
let clearedSomething = false;
|
|
11624
|
+
if (getPendingPiCompactionMarkerState(args.db, args.sessionId) !== null) {
|
|
11625
|
+
setPendingPiCompactionMarkerState(args.db, args.sessionId, null);
|
|
11626
|
+
clearedSomething = true;
|
|
11627
|
+
}
|
|
11628
|
+
if (getOverflowState(args.db, args.sessionId).needsEmergencyRecovery) {
|
|
11629
|
+
clearEmergencyRecovery(args.db, args.sessionId);
|
|
11630
|
+
clearedSomething = true;
|
|
11631
|
+
}
|
|
11632
|
+
const channel2 = getChannel2NudgeState(args.db, args.sessionId);
|
|
11633
|
+
if (channel2 === "pending" || channel2 === "claimed") {
|
|
11634
|
+
setChannel2NudgeState(args.db, args.sessionId, "");
|
|
11635
|
+
clearedSomething = true;
|
|
11636
|
+
}
|
|
11637
|
+
if (getPendingOps(args.db, args.sessionId).length > 0) {
|
|
11638
|
+
clearPendingOps(args.db, args.sessionId);
|
|
11639
|
+
clearedSomething = true;
|
|
11640
|
+
}
|
|
11641
|
+
const meta = getOrCreateSessionMeta(args.db, args.sessionId);
|
|
11642
|
+
if (meta.compartmentInProgress) {
|
|
11643
|
+
updateSessionMeta(args.db, args.sessionId, {
|
|
11644
|
+
compartmentInProgress: false
|
|
11645
|
+
});
|
|
11646
|
+
}
|
|
11647
|
+
clearCachedM0M1(args.db, args.sessionId);
|
|
11648
|
+
sessionLog(args.sessionId, `Pi compaction-off transition: clearedPendingMarker=${clearedSomething}`);
|
|
11649
|
+
const notice = clearedSomething ? PI_COMPACTION_OFF_NOTICE : null;
|
|
11650
|
+
if (notice) {
|
|
11651
|
+
setCompactionModeRecord(args.db, args.sessionId, "off_notice_pending");
|
|
11652
|
+
}
|
|
11653
|
+
return {
|
|
11654
|
+
recordToWrite: "off",
|
|
11655
|
+
invalidatedBaseline: true,
|
|
11656
|
+
clearDeferredMarkerState: true,
|
|
11657
|
+
historianCatchUpSignaled: false,
|
|
11658
|
+
notice
|
|
11659
|
+
};
|
|
11660
|
+
}
|
|
11661
|
+
function commitPiCompactionModeRecord(db, sessionId, record) {
|
|
11662
|
+
setCompactionModeRecord(db, sessionId, record);
|
|
11663
|
+
}
|
|
11664
|
+
|
|
11050
11665
|
// src/context-handler.ts
|
|
11051
11666
|
import * as crypto3 from "node:crypto";
|
|
11052
11667
|
|
|
@@ -11060,6 +11675,9 @@ var UNIT_TO_MS = {
|
|
|
11060
11675
|
};
|
|
11061
11676
|
function parseCacheTtl(ttl) {
|
|
11062
11677
|
const normalizedTtl = ttl.trim();
|
|
11678
|
+
if (normalizedTtl.toLowerCase() === "never") {
|
|
11679
|
+
return Number.POSITIVE_INFINITY;
|
|
11680
|
+
}
|
|
11063
11681
|
if (NUMERIC_PATTERN.test(normalizedTtl)) {
|
|
11064
11682
|
return Number(normalizedTtl);
|
|
11065
11683
|
}
|
|
@@ -11775,9 +12393,9 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
|
|
|
11775
12393
|
}
|
|
11776
12394
|
|
|
11777
12395
|
// ../plugin/src/hooks/magic-context/boundary-execution.ts
|
|
11778
|
-
var FORCE_MATERIALIZE_PERCENTAGE = 85;
|
|
11779
12396
|
function detectMidTurnBypassReason(input) {
|
|
11780
|
-
|
|
12397
|
+
const { forceMaterializationPercentage } = escalationBands(input.effectiveExecuteThresholdPercentage);
|
|
12398
|
+
if (input.contextUsage.percentage >= forceMaterializationPercentage)
|
|
11781
12399
|
return "force-materialize";
|
|
11782
12400
|
if (input.historyRefreshSessions.has(input.sessionId))
|
|
11783
12401
|
return "explicit-bust";
|
|
@@ -12270,29 +12888,29 @@ function callIdFromPart(part) {
|
|
|
12270
12888
|
const direct = firstStringField(part, ["callID", "callId", "toolCallId", "tool_call_id", "id"]);
|
|
12271
12889
|
if (direct)
|
|
12272
12890
|
return direct;
|
|
12273
|
-
const
|
|
12274
|
-
return
|
|
12891
|
+
const state2 = isRecord(part.state) ? part.state : null;
|
|
12892
|
+
return state2 ? firstStringField(state2, ["callID", "callId", "toolCallId", "tool_call_id", "id"]) ?? "" : "";
|
|
12275
12893
|
}
|
|
12276
12894
|
function toolSignalFromPart(part) {
|
|
12277
12895
|
if (!isRecord(part))
|
|
12278
12896
|
return null;
|
|
12279
12897
|
const type = partType(part);
|
|
12280
|
-
const
|
|
12898
|
+
const state2 = isRecord(part.state) ? part.state : null;
|
|
12281
12899
|
const callId = callIdFromPart(part);
|
|
12282
12900
|
if (!callId && type !== "tool")
|
|
12283
12901
|
return null;
|
|
12284
12902
|
if (type === "tool") {
|
|
12285
|
-
const hasInput =
|
|
12286
|
-
const outputKey =
|
|
12903
|
+
const hasInput = state2 !== null && hasOwn(state2, "input");
|
|
12904
|
+
const outputKey = state2 ? hasOwn(state2, "output") ? "output" : hasOwn(state2, "error") ? "error" : hasOwn(state2, "result") ? "result" : null : null;
|
|
12287
12905
|
const hasOutput = outputKey !== null;
|
|
12288
|
-
const outputValue = outputKey &&
|
|
12906
|
+
const outputValue = outputKey && state2 ? state2[outputKey] : undefined;
|
|
12289
12907
|
const providerExecuted = part.providerExecuted === true;
|
|
12290
12908
|
const openInvocation = !providerExecuted && !hasOutput;
|
|
12291
12909
|
return {
|
|
12292
12910
|
callId,
|
|
12293
12911
|
hasInput: hasInput || openInvocation,
|
|
12294
12912
|
hasOutput,
|
|
12295
|
-
inputText: hasInput &&
|
|
12913
|
+
inputText: hasInput && state2 ? stringValue(state2.input) : "",
|
|
12296
12914
|
outputText: hasOutput ? stringValue(outputValue) : ""
|
|
12297
12915
|
};
|
|
12298
12916
|
}
|
|
@@ -12520,24 +13138,27 @@ function tokenForMessage(message, options) {
|
|
|
12520
13138
|
function buildTrueRawTokenIndex(sessionId, messages, options) {
|
|
12521
13139
|
const ordered = [...messages].sort((a, b) => a.ordinal - b.ordinal);
|
|
12522
13140
|
const sliceCount = ordered.length;
|
|
12523
|
-
const
|
|
12524
|
-
const
|
|
13141
|
+
const firstOrdinal = ordered.length > 0 ? ordered[0].ordinal : 1;
|
|
13142
|
+
const terminalOrdinal = ordered.length > 0 ? ordered[ordered.length - 1].ordinal : 0;
|
|
13143
|
+
const rawMessageCount = Math.max(sliceCount, terminalOrdinal, options.absoluteMessageCount ?? sliceCount);
|
|
13144
|
+
const ordinalSpan = terminalOrdinal >= firstOrdinal ? terminalOrdinal - firstOrdinal + 1 : 0;
|
|
12525
13145
|
const tokensByOrdinal = new Map;
|
|
12526
13146
|
const idsByOrdinal = new Map;
|
|
12527
|
-
const prefix = new Array(
|
|
13147
|
+
const prefix = new Array(ordinalSpan + 1).fill(0);
|
|
12528
13148
|
for (const message of ordered) {
|
|
12529
13149
|
const stored = options.storedTotalForMessage?.(message);
|
|
12530
13150
|
const total = stored !== undefined && stored !== null ? stored : tokenForMessage(message, options).total;
|
|
12531
13151
|
tokensByOrdinal.set(message.ordinal, total);
|
|
12532
13152
|
idsByOrdinal.set(message.ordinal, message.id);
|
|
12533
|
-
|
|
12534
|
-
|
|
13153
|
+
const relative = message.ordinal - firstOrdinal + 1;
|
|
13154
|
+
if (relative >= 1 && relative <= ordinalSpan) {
|
|
13155
|
+
prefix[relative] = total;
|
|
12535
13156
|
}
|
|
12536
13157
|
}
|
|
12537
|
-
for (let k = 1;k <=
|
|
13158
|
+
for (let k = 1;k <= ordinalSpan; k += 1) {
|
|
12538
13159
|
prefix[k] += prefix[k - 1];
|
|
12539
13160
|
}
|
|
12540
|
-
const ordinalToIndex = (ordinal) => Math.max(0, Math.min(
|
|
13161
|
+
const ordinalToIndex = (ordinal) => Math.max(0, Math.min(ordinalSpan, ordinal - firstOrdinal));
|
|
12541
13162
|
return {
|
|
12542
13163
|
sessionId,
|
|
12543
13164
|
providerShapeVersion: options.providerShapeVersion,
|
|
@@ -12549,27 +13170,27 @@ function buildTrueRawTokenIndex(sessionId, messages, options) {
|
|
|
12549
13170
|
return idsByOrdinal.get(ordinal) ?? null;
|
|
12550
13171
|
},
|
|
12551
13172
|
suffixTokensFromOrdinal(ordinal) {
|
|
12552
|
-
if (ordinal <=
|
|
12553
|
-
return prefix[
|
|
12554
|
-
if (ordinal >
|
|
13173
|
+
if (ordinal <= firstOrdinal)
|
|
13174
|
+
return prefix[ordinalSpan];
|
|
13175
|
+
if (ordinal > terminalOrdinal)
|
|
12555
13176
|
return 0;
|
|
12556
|
-
return prefix[
|
|
13177
|
+
return prefix[ordinalSpan] - prefix[ordinalToIndex(ordinal)];
|
|
12557
13178
|
},
|
|
12558
13179
|
rangeTokens(startInclusive, endExclusive) {
|
|
12559
|
-
const start = Math.max(
|
|
12560
|
-
const end = Math.max(start, Math.min(
|
|
12561
|
-
return prefix[end -
|
|
13180
|
+
const start = Math.max(firstOrdinal, startInclusive);
|
|
13181
|
+
const end = Math.max(start, Math.min(terminalOrdinal + 1, endExclusive));
|
|
13182
|
+
return prefix[end - firstOrdinal] - prefix[start - firstOrdinal];
|
|
12562
13183
|
},
|
|
12563
13184
|
findSuffixStartForTokens(tokens) {
|
|
12564
13185
|
if (!Number.isFinite(tokens) || tokens <= 0)
|
|
12565
|
-
return
|
|
13186
|
+
return terminalOrdinal + 1;
|
|
12566
13187
|
const target = Math.max(0, Math.floor(tokens));
|
|
12567
|
-
const total = prefix[
|
|
13188
|
+
const total = prefix[ordinalSpan];
|
|
12568
13189
|
if (total < target)
|
|
12569
|
-
return
|
|
13190
|
+
return firstOrdinal;
|
|
12570
13191
|
const cut = total - target;
|
|
12571
13192
|
let lo = 0;
|
|
12572
|
-
let hi =
|
|
13193
|
+
let hi = ordinalSpan;
|
|
12573
13194
|
let best = 0;
|
|
12574
13195
|
while (lo <= hi) {
|
|
12575
13196
|
const mid = lo + hi >> 1;
|
|
@@ -12580,29 +13201,31 @@ function buildTrueRawTokenIndex(sessionId, messages, options) {
|
|
|
12580
13201
|
hi = mid - 1;
|
|
12581
13202
|
}
|
|
12582
13203
|
}
|
|
12583
|
-
return
|
|
13204
|
+
return firstOrdinal + best;
|
|
12584
13205
|
},
|
|
12585
13206
|
findHeadEndForCap(startInclusive, endExclusive, capTokens) {
|
|
12586
|
-
const start = Math.max(
|
|
12587
|
-
const end = Math.max(start, Math.min(
|
|
13207
|
+
const start = Math.max(firstOrdinal, Math.min(terminalOrdinal + 1, startInclusive));
|
|
13208
|
+
const end = Math.max(start, Math.min(terminalOrdinal + 1, endExclusive));
|
|
12588
13209
|
if (!Number.isFinite(capTokens) || capTokens <= 0)
|
|
12589
13210
|
return start;
|
|
12590
|
-
const
|
|
12591
|
-
const
|
|
12592
|
-
|
|
12593
|
-
let
|
|
12594
|
-
let
|
|
13211
|
+
const startIndex = start - firstOrdinal;
|
|
13212
|
+
const endIndex = end - firstOrdinal;
|
|
13213
|
+
const cut = prefix[startIndex] + Math.floor(capTokens);
|
|
13214
|
+
let lo = startIndex + 1;
|
|
13215
|
+
let hi = endIndex;
|
|
13216
|
+
let bestEndIndex = startIndex;
|
|
12595
13217
|
while (lo <= hi) {
|
|
12596
13218
|
const mid = lo + hi >> 1;
|
|
12597
13219
|
if (prefix[mid] <= cut) {
|
|
12598
|
-
|
|
13220
|
+
bestEndIndex = mid;
|
|
12599
13221
|
lo = mid + 1;
|
|
12600
13222
|
} else {
|
|
12601
13223
|
hi = mid - 1;
|
|
12602
13224
|
}
|
|
12603
13225
|
}
|
|
13226
|
+
let bestEnd = firstOrdinal + bestEndIndex;
|
|
12604
13227
|
if (bestEnd === start && start < end)
|
|
12605
|
-
|
|
13228
|
+
bestEnd = start + 1;
|
|
12606
13229
|
return Math.min(bestEnd, end);
|
|
12607
13230
|
}
|
|
12608
13231
|
};
|
|
@@ -12918,7 +13541,8 @@ function resolveProtectedTailBoundary(ctx) {
|
|
|
12918
13541
|
if (ctx.migrationFloorActive)
|
|
12919
13542
|
runtimeFloor = Math.max(runtimeFloor, ctx.priorBoundaryOrdinal);
|
|
12920
13543
|
let protectedTailStart = Math.max(boundary, runtimeFloor);
|
|
12921
|
-
|
|
13544
|
+
const forceMaterializationPercentage = escalationBands(ctx.executeThresholdPercentage).forceMaterializationPercentage;
|
|
13545
|
+
if (!ctx.emergencyTailScale && usagePercentage < forceMaterializationPercentage) {
|
|
12922
13546
|
let lastMeaningfulUserOrdinal = 0;
|
|
12923
13547
|
for (let i = messages.length - 1;i >= 0; i--) {
|
|
12924
13548
|
const message = messages[i];
|
|
@@ -13159,7 +13783,8 @@ function getRawHistoryEligibility(db, sessionId) {
|
|
|
13159
13783
|
function hasRunnableCompartmentWindow(snapshot) {
|
|
13160
13784
|
if (snapshot.offset >= snapshot.protectedTailStart)
|
|
13161
13785
|
return false;
|
|
13162
|
-
|
|
13786
|
+
const forceMaterializationPercentage = escalationBands(snapshot.executeThresholdPercentage).forceMaterializationPercentage;
|
|
13787
|
+
if (snapshot.usagePercentage >= forceMaterializationPercentage || snapshot.emergencyTailScale) {
|
|
13163
13788
|
return snapshot.trueRawEligibleTokens >= deriveMinForceEligibleTokens(snapshot.N) || snapshot.eligibleEndOrdinal > snapshot.offset;
|
|
13164
13789
|
}
|
|
13165
13790
|
return snapshot.eligibleEndOrdinal > snapshot.offset;
|
|
@@ -13222,11 +13847,18 @@ function validateBoundarySnapshot(args) {
|
|
|
13222
13847
|
return { ok: true };
|
|
13223
13848
|
}
|
|
13224
13849
|
function recordHighPressureNoEligibleHead(db, snapshot) {
|
|
13225
|
-
|
|
13850
|
+
const forceMaterializationPercentage = escalationBands(snapshot.executeThresholdPercentage).forceMaterializationPercentage;
|
|
13851
|
+
if (snapshot.usagePercentage < forceMaterializationPercentage && !snapshot.emergencyTailScale) {
|
|
13226
13852
|
return 0;
|
|
13853
|
+
}
|
|
13227
13854
|
return recordProtectedTailNoEligibleHead(db, snapshot.sessionId);
|
|
13228
13855
|
}
|
|
13229
13856
|
|
|
13857
|
+
// ../plugin/src/hooks/magic-context/sentinel.ts
|
|
13858
|
+
function modelAcceptsEmptyContent(providerID) {
|
|
13859
|
+
return providerID === "anthropic";
|
|
13860
|
+
}
|
|
13861
|
+
|
|
13230
13862
|
// ../plugin/src/hooks/magic-context/compartment-trigger.ts
|
|
13231
13863
|
var PROACTIVE_TRIGGER_OFFSET_PERCENTAGE = 2;
|
|
13232
13864
|
var POST_DROP_TARGET_RATIO = 0.75;
|
|
@@ -13234,7 +13866,6 @@ var MIN_PROACTIVE_TAIL_TOKEN_ESTIMATE = 6000;
|
|
|
13234
13866
|
var MIN_PROACTIVE_TAIL_MESSAGE_COUNT = 12;
|
|
13235
13867
|
var DEFAULT_MIN_COMMIT_CLUSTERS_FOR_TRIGGER = 3;
|
|
13236
13868
|
var TAIL_SIZE_TRIGGER_MULTIPLIER = 3;
|
|
13237
|
-
var FORCE_COMPARTMENT_PERCENTAGE = 80;
|
|
13238
13869
|
var BLOCK_UNTIL_DONE_PERCENTAGE = 95;
|
|
13239
13870
|
var CONTENT_TAG_OWNER_SUFFIX = /:(?:p|file)\d+$/;
|
|
13240
13871
|
function tagOwnerMessageId(row) {
|
|
@@ -13271,7 +13902,7 @@ function estimateUntaggedInMemoryTailUpperBound(db, sessionId, inMemoryTail, tag
|
|
|
13271
13902
|
function getProactiveCompartmentTriggerPercentage(executeThresholdPercentage) {
|
|
13272
13903
|
return Math.max(0, executeThresholdPercentage - PROACTIVE_TRIGGER_OFFSET_PERCENTAGE);
|
|
13273
13904
|
}
|
|
13274
|
-
function estimateProjectedPostDropPercentage(db, sessionId, usage, activeTags, clearReasoningAge, clearedReasoningThroughTag) {
|
|
13905
|
+
function estimateProjectedPostDropPercentage(db, sessionId, usage, activeTags, clearReasoningAge, clearedReasoningThroughTag, canClearReasoning) {
|
|
13275
13906
|
const totalActiveBytes = activeTags.reduce((sum, tag) => sum + tag.byteSize + tag.reasoningByteSize, 0);
|
|
13276
13907
|
if (totalActiveBytes === 0)
|
|
13277
13908
|
return null;
|
|
@@ -13282,7 +13913,7 @@ function estimateProjectedPostDropPercentage(db, sessionId, usage, activeTags, c
|
|
|
13282
13913
|
droppableBytes += activeTags.filter((tag) => pendingDropTagIds.has(tag.tagNumber)).reduce((sum, tag) => sum + tag.byteSize + tag.reasoningByteSize, 0);
|
|
13283
13914
|
}
|
|
13284
13915
|
const maxTag = activeTags.reduce((max, t) => Math.max(max, t.tagNumber), 0);
|
|
13285
|
-
if (clearReasoningAge !== undefined && clearedReasoningThroughTag !== undefined) {
|
|
13916
|
+
if (canClearReasoning && clearReasoningAge !== undefined && clearedReasoningThroughTag !== undefined) {
|
|
13286
13917
|
const reasoningAgeCutoff = maxTag - clearReasoningAge;
|
|
13287
13918
|
for (const tag of activeTags) {
|
|
13288
13919
|
if (tag.type !== "message")
|
|
@@ -13382,7 +14013,7 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
|
|
|
13382
14013
|
}
|
|
13383
14014
|
});
|
|
13384
14015
|
}
|
|
13385
|
-
function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, clearReasoningAge, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail, taggerFloorOverride) {
|
|
14016
|
+
function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, clearReasoningAge, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail, taggerFloorOverride, reasoningProjection) {
|
|
13386
14017
|
if (sessionMeta.compartmentInProgress) {
|
|
13387
14018
|
sessionLog(sessionId, `compartment trigger: skipped — historian already in progress (usage=${usage.percentage.toFixed(1)}%)`);
|
|
13388
14019
|
return { shouldFire: false };
|
|
@@ -13436,18 +14067,20 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
|
|
|
13436
14067
|
}
|
|
13437
14068
|
return { shouldFire: false };
|
|
13438
14069
|
}
|
|
13439
|
-
const
|
|
14070
|
+
const canClearReasoning = reasoningProjection?.canClearReasoning ?? modelAcceptsEmptyContent(reasoningProjection?.providerID);
|
|
14071
|
+
const projectedPostDropPercentage = estimateProjectedPostDropPercentage(db, sessionId, usage, preloadedActiveTags ?? getActiveTagsBySession(db, sessionId), clearReasoningAge, sessionMeta.clearedReasoningThroughTag, canClearReasoning);
|
|
13440
14072
|
const relativePostDropTarget = executeThresholdPercentage * POST_DROP_TARGET_RATIO;
|
|
13441
|
-
|
|
14073
|
+
const forceMaterializationPercentage = escalationBands(executeThresholdPercentage).forceMaterializationPercentage;
|
|
14074
|
+
if (usage.percentage >= forceMaterializationPercentage) {
|
|
13442
14075
|
if (projectedPostDropPercentage !== null && projectedPostDropPercentage <= relativePostDropTarget) {
|
|
13443
|
-
sessionLog(sessionId, `compartment trigger: skipping force
|
|
14076
|
+
sessionLog(sessionId, `compartment trigger: skipping force band ${forceMaterializationPercentage}% because projected post-drop usage is ${projectedPostDropPercentage.toFixed(1)}% (target ${relativePostDropTarget.toFixed(1)}%)`);
|
|
13444
14077
|
return { shouldFire: false };
|
|
13445
14078
|
}
|
|
13446
14079
|
sessionLog(sessionId, `compartment trigger: force-firing at ${usage.percentage.toFixed(1)}% (projected post-drop ${projectedPostDropPercentage?.toFixed(1) ?? "none"}%)`);
|
|
13447
14080
|
if (tailInfo.boundarySnapshot && hasRunnableCompartmentWindow(tailInfo.boundarySnapshot)) {
|
|
13448
14081
|
return {
|
|
13449
14082
|
shouldFire: true,
|
|
13450
|
-
reason: "
|
|
14083
|
+
reason: "force_band",
|
|
13451
14084
|
boundarySnapshot: tailInfo.boundarySnapshot
|
|
13452
14085
|
};
|
|
13453
14086
|
}
|
|
@@ -13472,9 +14105,9 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
|
|
|
13472
14105
|
});
|
|
13473
14106
|
});
|
|
13474
14107
|
if (hasRunnableCompartmentWindow(scaledBoundary)) {
|
|
13475
|
-
return { shouldFire: true, reason: "
|
|
14108
|
+
return { shouldFire: true, reason: "force_band", boundarySnapshot: scaledBoundary };
|
|
13476
14109
|
}
|
|
13477
|
-
sessionLog(sessionId, "compartment trigger:
|
|
14110
|
+
sessionLog(sessionId, "compartment trigger: force_band skipped — raw exists but protected head genuinely empty after emergency tail scale");
|
|
13478
14111
|
return { shouldFire: false };
|
|
13479
14112
|
}
|
|
13480
14113
|
const clusterEnabled = commitClusterTrigger?.enabled ?? true;
|
|
@@ -13639,11 +14272,6 @@ ${body}${hintText}
|
|
|
13639
14272
|
</system-reminder>`;
|
|
13640
14273
|
}
|
|
13641
14274
|
|
|
13642
|
-
// ../plugin/src/hooks/magic-context/sentinel.ts
|
|
13643
|
-
function modelAcceptsEmptyContent(providerID) {
|
|
13644
|
-
return providerID === "anthropic";
|
|
13645
|
-
}
|
|
13646
|
-
|
|
13647
14275
|
// ../plugin/src/hooks/magic-context/supersession-reclaim.ts
|
|
13648
14276
|
var TODOWRITE_KEEP = 1;
|
|
13649
14277
|
var CTX_REDUCE_KEEP = 5;
|
|
@@ -14958,16 +15586,16 @@ function sealDeliveredAfterUnconfirmedSend(db, sessionId) {
|
|
|
14958
15586
|
}
|
|
14959
15587
|
}
|
|
14960
15588
|
var channel1StateBySession = new Map;
|
|
14961
|
-
function setPiChannel1Baseline(sessionId,
|
|
14962
|
-
channel1StateBySession.set(sessionId,
|
|
15589
|
+
function setPiChannel1Baseline(sessionId, state2) {
|
|
15590
|
+
channel1StateBySession.set(sessionId, state2);
|
|
14963
15591
|
}
|
|
14964
15592
|
function clearPiChannel1State(sessionId) {
|
|
14965
15593
|
channel1StateBySession.delete(sessionId);
|
|
14966
15594
|
}
|
|
14967
15595
|
function markPiChannel1Reduced(sessionId, db) {
|
|
14968
|
-
const
|
|
14969
|
-
if (
|
|
14970
|
-
|
|
15596
|
+
const state2 = channel1StateBySession.get(sessionId);
|
|
15597
|
+
if (state2)
|
|
15598
|
+
state2.reducedSinceRefresh = true;
|
|
14971
15599
|
if (db)
|
|
14972
15600
|
resetLastNudgeCycle(db, sessionId);
|
|
14973
15601
|
}
|
|
@@ -15042,11 +15670,11 @@ function computeTailTokenEstimatePi(messages) {
|
|
|
15042
15670
|
}
|
|
15043
15671
|
function maybeChannel1ReminderForToolResult(args) {
|
|
15044
15672
|
const { db, sessionId, toolName } = args;
|
|
15045
|
-
const
|
|
15046
|
-
if (!
|
|
15673
|
+
const state2 = channel1StateBySession.get(sessionId);
|
|
15674
|
+
if (!state2)
|
|
15047
15675
|
return null;
|
|
15048
15676
|
if (toolName === "ctx_reduce") {
|
|
15049
|
-
|
|
15677
|
+
state2.reducedSinceRefresh = true;
|
|
15050
15678
|
resetLastNudgeCycle(db, sessionId);
|
|
15051
15679
|
return null;
|
|
15052
15680
|
}
|
|
@@ -15055,21 +15683,21 @@ function maybeChannel1ReminderForToolResult(args) {
|
|
|
15055
15683
|
return null;
|
|
15056
15684
|
if (text.includes(CHANNEL1_SENTINEL))
|
|
15057
15685
|
return null;
|
|
15058
|
-
|
|
15059
|
-
if (
|
|
15686
|
+
state2.turnToolTokens += toolOutputTokens(text);
|
|
15687
|
+
if (state2.reducedSinceRefresh)
|
|
15060
15688
|
return null;
|
|
15061
|
-
const undroppedTokens =
|
|
15689
|
+
const undroppedTokens = state2.tailToolTokens + state2.turnToolTokens;
|
|
15062
15690
|
const pressure = computePressure({
|
|
15063
|
-
lastInputTokens:
|
|
15064
|
-
turnToolTokens:
|
|
15065
|
-
contextLimit:
|
|
15066
|
-
executeThresholdPercentage:
|
|
15691
|
+
lastInputTokens: state2.lastInputTokens,
|
|
15692
|
+
turnToolTokens: state2.turnToolTokens,
|
|
15693
|
+
contextLimit: state2.contextLimit,
|
|
15694
|
+
executeThresholdPercentage: state2.executeThresholdPercentage
|
|
15067
15695
|
});
|
|
15068
|
-
const workingWindowTokens = Math.round(
|
|
15696
|
+
const workingWindowTokens = Math.round(state2.contextLimit * state2.executeThresholdPercentage / 100);
|
|
15069
15697
|
const decision = decideChannel1({
|
|
15070
15698
|
undroppedTokens,
|
|
15071
15699
|
pressure,
|
|
15072
|
-
estimatedInputTokens:
|
|
15700
|
+
estimatedInputTokens: state2.lastInputTokens + state2.turnToolTokens,
|
|
15073
15701
|
workingWindowTokens,
|
|
15074
15702
|
lastNudgeUndropped: getLastNudgeUndropped(db, sessionId),
|
|
15075
15703
|
lastNudgeLevel: getLastNudgeLevel(db, sessionId),
|
|
@@ -15081,18 +15709,18 @@ function maybeChannel1ReminderForToolResult(args) {
|
|
|
15081
15709
|
return null;
|
|
15082
15710
|
return {
|
|
15083
15711
|
type: "text",
|
|
15084
|
-
text: buildChannel1Reminder(decision.level, decision.undroppedTokens,
|
|
15712
|
+
text: buildChannel1Reminder(decision.level, decision.undroppedTokens, state2.oldestReclaimableToolTags)
|
|
15085
15713
|
};
|
|
15086
15714
|
}
|
|
15087
15715
|
var CHANNEL2_NUDGE_CUSTOM_TYPE = "magic-context:ceiling-nudge";
|
|
15088
15716
|
function maybeDeliverChannel2Pi(pi, db, sessionId, deliverAs = "followUp") {
|
|
15089
|
-
let
|
|
15717
|
+
let state2;
|
|
15090
15718
|
try {
|
|
15091
|
-
|
|
15719
|
+
state2 = getChannel2NudgeState(db, sessionId);
|
|
15092
15720
|
} catch {
|
|
15093
15721
|
return false;
|
|
15094
15722
|
}
|
|
15095
|
-
if (
|
|
15723
|
+
if (state2 !== "pending")
|
|
15096
15724
|
return false;
|
|
15097
15725
|
const baseline = channel1StateBySession.get(sessionId);
|
|
15098
15726
|
if (!baseline)
|
|
@@ -15764,14 +16392,17 @@ var EMPTY_PI_PROJECT_DOCS = {
|
|
|
15764
16392
|
renderedBlock: "",
|
|
15765
16393
|
canonicalHash: ""
|
|
15766
16394
|
};
|
|
15767
|
-
function
|
|
15768
|
-
return
|
|
16395
|
+
function getRenderableCompartmentsPi(db, state2) {
|
|
16396
|
+
return state2.compactionOff ? [] : getCompartments(db, state2.sessionId);
|
|
16397
|
+
}
|
|
16398
|
+
function readProjectDocsForPiM0(state2) {
|
|
16399
|
+
return state2.injectDocs !== false ? readProjectDocsCanonical(state2.projectDirectory) : EMPTY_PI_PROJECT_DOCS;
|
|
15769
16400
|
}
|
|
15770
|
-
function memoryProjectPath(
|
|
15771
|
-
return
|
|
16401
|
+
function memoryProjectPath(state2) {
|
|
16402
|
+
return state2.memoryEnabled === false ? undefined : state2.projectIdentity;
|
|
15772
16403
|
}
|
|
15773
|
-
function resolveWorkspaceRenderContextPi(
|
|
15774
|
-
const memPath = memoryProjectPath(
|
|
16404
|
+
function resolveWorkspaceRenderContextPi(state2, db) {
|
|
16405
|
+
const memPath = memoryProjectPath(state2);
|
|
15775
16406
|
if (!memPath) {
|
|
15776
16407
|
return {
|
|
15777
16408
|
identities: [],
|
|
@@ -15847,12 +16478,12 @@ function piImageFromDataUrl(dataUrl) {
|
|
|
15847
16478
|
return null;
|
|
15848
16479
|
return { type: "image", mimeType: match[1], data: match[2] };
|
|
15849
16480
|
}
|
|
15850
|
-
function resolveMuralForM0Pi(
|
|
15851
|
-
if (
|
|
15852
|
-
return
|
|
15853
|
-
if (!
|
|
16481
|
+
function resolveMuralForM0Pi(state2, db, modelKey, budgetTokens) {
|
|
16482
|
+
if (state2.mural)
|
|
16483
|
+
return state2.mural;
|
|
16484
|
+
if (!state2.muralEnabled)
|
|
15854
16485
|
return;
|
|
15855
|
-
return resolveMuralWire(db, memoryProjectPath(
|
|
16486
|
+
return resolveMuralWire(db, memoryProjectPath(state2), modelKey, true, budgetTokens);
|
|
15856
16487
|
}
|
|
15857
16488
|
function cachedInjectionTokenCounts(sessionId, m0, m1) {
|
|
15858
16489
|
const cached = injectionTokenCountsBySession.get(sessionId);
|
|
@@ -15905,17 +16536,17 @@ function trimPiMessagesToCachedBoundary(db, sessionId, piMessages, entryIds) {
|
|
|
15905
16536
|
function setCachedBoundary(db, sessionId, boundary) {
|
|
15906
16537
|
db.prepare("UPDATE session_meta SET cached_m0_last_baseline_end_message_id = ? WHERE session_id = ?").run(boundary, sessionId);
|
|
15907
16538
|
}
|
|
15908
|
-
function getCachedMarkers(db,
|
|
15909
|
-
const meta = getOrCreateSessionMeta(db,
|
|
16539
|
+
function getCachedMarkers(db, state2, compartmentsForNormalization) {
|
|
16540
|
+
const meta = getOrCreateSessionMeta(db, state2.sessionId);
|
|
15910
16541
|
if (!meta.cachedM0Bytes)
|
|
15911
16542
|
return null;
|
|
15912
16543
|
if (meta.cachedM0MaxCompartmentSeq === null || meta.cachedM0MaxMemoryId === null || meta.cachedM0MaxMutationId === null || meta.cachedM0MaxMemoryMutationId === null || meta.cachedM0ProjectMemoryEpoch === null || meta.cachedM0ProjectUserProfileVersion === null || meta.cachedM0ProjectDocsHash === null || meta.cachedM0SessionFactsVersion === null || meta.cachedM0MaterializedAt === null || meta.cachedM0UpgradeState === null) {
|
|
15913
16544
|
return null;
|
|
15914
16545
|
}
|
|
15915
|
-
const compartments = compartmentsForNormalization ??
|
|
16546
|
+
const compartments = compartmentsForNormalization ?? getRenderableCompartmentsPi(db, state2);
|
|
15916
16547
|
const cachedUpgradeIdentity = decodeCachedM0UpgradeIdentity(meta.cachedM0UpgradeState);
|
|
15917
16548
|
const maxCompartmentSeq = normalizeCachedMaxCompartmentSeq(meta.cachedM0MaxCompartmentSeq, compartments);
|
|
15918
|
-
const cachedBoundary = getCachedBoundary(db,
|
|
16549
|
+
const cachedBoundary = getCachedBoundary(db, state2.sessionId);
|
|
15919
16550
|
const liveBoundary = lastBaselineEndMessageId(compartments);
|
|
15920
16551
|
if (maxCompartmentSeq >= 0 && cachedBoundary === null && liveBoundary !== null) {
|
|
15921
16552
|
return null;
|
|
@@ -15943,38 +16574,38 @@ function lastBaselineEndMessageId(compartments) {
|
|
|
15943
16574
|
const last = compartments.at(-1);
|
|
15944
16575
|
return last?.endMessageId && last.endMessageId.length > 0 ? last.endMessageId : null;
|
|
15945
16576
|
}
|
|
15946
|
-
function readCurrentMarkers(db,
|
|
15947
|
-
return readCurrentMarkersFromCompartments(db,
|
|
16577
|
+
function readCurrentMarkers(db, state2, projectDocsHash) {
|
|
16578
|
+
return readCurrentMarkersFromCompartments(db, state2, getRenderableCompartmentsPi(db, state2), projectDocsHash);
|
|
15948
16579
|
}
|
|
15949
|
-
function readCurrentMarkersFromCompartments(db,
|
|
15950
|
-
const memPath = memoryProjectPath(
|
|
15951
|
-
const workspace = resolveWorkspaceRenderContextPi(
|
|
16580
|
+
function readCurrentMarkersFromCompartments(db, state2, compartments, projectDocsHash) {
|
|
16581
|
+
const memPath = memoryProjectPath(state2);
|
|
16582
|
+
const workspace = resolveWorkspaceRenderContextPi(state2, db);
|
|
15952
16583
|
const maxMemoryId = memPath ? workspace.isWorkspaced ? getMaxMemoryIdForProjects(db, workspace.expandedIdentities, workspace.ownIdentities, workspace.shareCategories) : getMaxMemoryIdForProjects(db, [memPath]) : 0;
|
|
15953
16584
|
const projectState = memPath ? getProjectState(db, memPath) : undefined;
|
|
15954
16585
|
const globalState = getProjectState(db, GLOBAL_USER_PROFILE_PROJECT_PATH);
|
|
15955
16586
|
return {
|
|
15956
16587
|
maxCompartmentSeq: compartments.length > 0 ? compartments.reduce((max, compartment) => compartment.sequence > max ? compartment.sequence : max, EMPTY_MAX_COMPARTMENT_SEQ) : EMPTY_MAX_COMPARTMENT_SEQ,
|
|
15957
16588
|
maxMemoryId,
|
|
15958
|
-
maxMutationId: getMaxM0MutationId(db,
|
|
16589
|
+
maxMutationId: getMaxM0MutationId(db, state2.sessionId) ?? 0,
|
|
15959
16590
|
maxMemoryMutationId: memPath ? workspace.isWorkspaced ? getMaxMemoryMutationIdForProjects(db, workspace.expandedIdentities) ?? 0 : getMaxMemoryMutationId(db, memPath) ?? 0 : 0,
|
|
15960
16591
|
projectMemoryEpoch: projectState?.projectMemoryEpoch ?? 0,
|
|
15961
16592
|
workspaceFingerprint: workspace.isWorkspaced ? computeWorkspaceEpochFingerprint(db, workspace.identities) : null,
|
|
15962
16593
|
projectUserProfileVersion: globalState?.projectUserProfileVersion ?? 0,
|
|
15963
|
-
projectDocsHash: projectDocsHash ?? readProjectDocsForPiM0(
|
|
15964
|
-
sessionFactsVersion: getSessionFactsVersion(db,
|
|
16594
|
+
projectDocsHash: projectDocsHash ?? readProjectDocsForPiM0(state2).canonicalHash,
|
|
16595
|
+
sessionFactsVersion: getSessionFactsVersion(db, state2.sessionId),
|
|
15965
16596
|
materializedAt: Date.now(),
|
|
15966
16597
|
upgradeState: `${PI_M0_UPGRADE_STATE}:${compartments.some((c) => c.legacy === 1) ? "legacy" : "ready"}`,
|
|
15967
16598
|
compartmentRenderEpoch: COMPARTMENT_RENDER_EPOCH,
|
|
15968
16599
|
lastBaselineEndMessageId: lastBaselineEndMessageId(compartments),
|
|
15969
|
-
systemHash: (
|
|
15970
|
-
modelKey: (
|
|
15971
|
-
projectIdentity:
|
|
16600
|
+
systemHash: (state2.hardSignals ?? EMPTY_PI_HARD_SIGNALS).systemHash,
|
|
16601
|
+
modelKey: (state2.hardSignals ?? EMPTY_PI_HARD_SIGNALS).modelKey,
|
|
16602
|
+
projectIdentity: state2.projectIdentity
|
|
15972
16603
|
};
|
|
15973
16604
|
}
|
|
15974
|
-
function mustMaterializePi(
|
|
15975
|
-
const meta = getOrCreateSessionMeta(db,
|
|
15976
|
-
const currentCompartments = currentCompartmentsOverride ??
|
|
15977
|
-
const current = readCurrentMarkersFromCompartments(db,
|
|
16605
|
+
function mustMaterializePi(state2, db, currentCompartmentsOverride) {
|
|
16606
|
+
const meta = getOrCreateSessionMeta(db, state2.sessionId);
|
|
16607
|
+
const currentCompartments = currentCompartmentsOverride ?? getRenderableCompartmentsPi(db, state2);
|
|
16608
|
+
const current = readCurrentMarkersFromCompartments(db, state2, currentCompartments, meta.cachedM0ProjectDocsHash ?? undefined);
|
|
15978
16609
|
if (!meta.cachedM0Bytes)
|
|
15979
16610
|
return { value: true, reason: "first_render" };
|
|
15980
16611
|
if (!meta.cachedM1Bytes)
|
|
@@ -15982,21 +16613,21 @@ function mustMaterializePi(state, db, currentCompartmentsOverride) {
|
|
|
15982
16613
|
if (!decodeCachedM0(meta.cachedM0Bytes)) {
|
|
15983
16614
|
return { value: true, reason: "cache_invalid" };
|
|
15984
16615
|
}
|
|
15985
|
-
const cached = getCachedMarkers(db,
|
|
16616
|
+
const cached = getCachedMarkers(db, state2, currentCompartments);
|
|
15986
16617
|
if (cached === null) {
|
|
15987
16618
|
return { value: true, reason: "cache_invalid" };
|
|
15988
16619
|
}
|
|
15989
16620
|
if (cached.compartmentRenderEpoch !== current.compartmentRenderEpoch) {
|
|
15990
16621
|
return { value: true, reason: "compartment_render_epoch" };
|
|
15991
16622
|
}
|
|
15992
|
-
const hard =
|
|
16623
|
+
const hard = state2.hardSignals ?? EMPTY_PI_HARD_SIGNALS;
|
|
15993
16624
|
if (hard.modelKey !== "" && hard.modelKey !== (meta.cachedM0ModelKey ?? "")) {
|
|
15994
16625
|
return { value: true, reason: "model_change" };
|
|
15995
16626
|
}
|
|
15996
16627
|
if (hard.systemHash !== "" && hard.systemHash !== (meta.cachedM0SystemHash ?? "")) {
|
|
15997
16628
|
return { value: true, reason: "system_hash" };
|
|
15998
16629
|
}
|
|
15999
|
-
if (meta.cachedM0ProjectIdentity !== null && meta.cachedM0ProjectIdentity !==
|
|
16630
|
+
if (meta.cachedM0ProjectIdentity !== null && meta.cachedM0ProjectIdentity !== state2.projectIdentity) {
|
|
16000
16631
|
return { value: true, reason: "project_change" };
|
|
16001
16632
|
}
|
|
16002
16633
|
if (hard.cacheExpired && hard.lastResponseTime > 0 && hard.lastResponseTime > (meta.cachedM0MaterializedAt ?? 0)) {
|
|
@@ -16026,9 +16657,9 @@ ${memories.map((memory) => `- ${escapeXmlContent(memory.content)}`).join(`
|
|
|
16026
16657
|
`)}
|
|
16027
16658
|
</${wrapper}>`;
|
|
16028
16659
|
}
|
|
16029
|
-
function renderM0Pi(
|
|
16030
|
-
const memPath = memoryProjectPath(
|
|
16031
|
-
const workspace = workspaceOverride ?? resolveWorkspaceRenderContextPi(
|
|
16660
|
+
function renderM0Pi(state2, db, projectDocs = readProjectDocsForPiM0(state2).renderedBlock, decayPressureMultiplier = 1, memoriesOverride, compartmentsOverride, userProfileOverride, workspaceOverride, mural) {
|
|
16661
|
+
const memPath = memoryProjectPath(state2);
|
|
16662
|
+
const workspace = workspaceOverride ?? resolveWorkspaceRenderContextPi(state2, db);
|
|
16032
16663
|
const allMemories = memoriesOverride ?? (memPath ? workspace.isWorkspaced ? getMemoriesByProjects(db, workspace.expandedIdentities, ["active", "permanent"], Date.now(), workspace.ownIdentities, workspace.shareCategories) : getMemoriesByProject(db, memPath, ["active", "permanent"]) : []);
|
|
16033
16664
|
const memoryRenderOptions = {
|
|
16034
16665
|
sourceNameByMemoryId: sourceNamesForPiMemories({
|
|
@@ -16037,23 +16668,25 @@ function renderM0Pi(state, db, projectDocs = readProjectDocsForPiM0(state).rende
|
|
|
16037
16668
|
workspace
|
|
16038
16669
|
})
|
|
16039
16670
|
};
|
|
16040
|
-
const memories = allMemories.length > 0 ? workspace.isWorkspaced ? trimWorkspaceMemoriesToBudgetV2(
|
|
16671
|
+
const memories = allMemories.length > 0 ? workspace.isWorkspaced ? trimWorkspaceMemoriesToBudgetV2(state2.sessionId, allMemories, state2.injectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS, workspace, memoryRenderOptions).renderOrder : trimMemoriesToBudgetV2(state2.sessionId, allMemories, state2.injectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS).renderOrder : allMemories;
|
|
16041
16672
|
const memoryBlock = memories.length > 0 ? renderMemoryBlockV2(memories, "project-memory", memoryRenderOptions) : undefined;
|
|
16042
|
-
const baseHistoryBudget =
|
|
16673
|
+
const baseHistoryBudget = state2.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS;
|
|
16043
16674
|
const decayed = renderDecayedCompartments({
|
|
16044
|
-
compartments: compartmentsOverride ??
|
|
16675
|
+
compartments: compartmentsOverride ?? getRenderableCompartmentsPi(db, state2),
|
|
16045
16676
|
historyBudgetTokens: baseHistoryBudget / Math.max(1, decayPressureMultiplier)
|
|
16046
16677
|
});
|
|
16047
16678
|
const sections = [];
|
|
16048
16679
|
if (projectDocs.length > 0)
|
|
16049
16680
|
sections.push(projectDocs);
|
|
16050
|
-
const trimmedProfile = trimUserMemoriesToBudget(userProfileOverride ?? safeGetActiveUserMemoriesPi(db),
|
|
16681
|
+
const trimmedProfile = trimUserMemoriesToBudget(userProfileOverride ?? safeGetActiveUserMemoriesPi(db), state2.userProfileBudgetTokens ?? DEFAULT_USER_PROFILE_BUDGET_TOKENS);
|
|
16051
16682
|
const userProfile = renderUserProfileBlock(db, "user-profile", trimmedProfile);
|
|
16052
16683
|
if (userProfile.length > 0)
|
|
16053
16684
|
sections.push(userProfile);
|
|
16054
|
-
|
|
16685
|
+
if (!state2.compactionOff) {
|
|
16686
|
+
sections.push(decayed.length > 0 ? `<session-history>
|
|
16055
16687
|
${decayed}
|
|
16056
16688
|
</session-history>` : "<session-history></session-history>");
|
|
16689
|
+
}
|
|
16057
16690
|
if (memoryBlock)
|
|
16058
16691
|
sections.push(memoryBlock);
|
|
16059
16692
|
if (mural?.enabled && mural.supportsVision && mural.dataUrl) {
|
|
@@ -16065,18 +16698,18 @@ The project memory mural image follows.
|
|
|
16065
16698
|
|
|
16066
16699
|
`).trim();
|
|
16067
16700
|
}
|
|
16068
|
-
function renderedMemoryIdsForPi(
|
|
16701
|
+
function renderedMemoryIdsForPi(state2, memories, workspace, db) {
|
|
16069
16702
|
if (memories.length === 0)
|
|
16070
16703
|
return [];
|
|
16071
|
-
const resolvedWorkspace = workspace ?? (db ? resolveWorkspaceRenderContextPi(
|
|
16704
|
+
const resolvedWorkspace = workspace ?? (db ? resolveWorkspaceRenderContextPi(state2, db) : undefined);
|
|
16072
16705
|
const renderOptions = resolvedWorkspace ? {
|
|
16073
16706
|
sourceNameByMemoryId: sourceNamesForPiMemories({
|
|
16074
16707
|
memories,
|
|
16075
|
-
projectPath: memoryProjectPath(
|
|
16708
|
+
projectPath: memoryProjectPath(state2),
|
|
16076
16709
|
workspace: resolvedWorkspace
|
|
16077
16710
|
})
|
|
16078
16711
|
} : {};
|
|
16079
|
-
const trimmed = resolvedWorkspace?.isWorkspaced ? trimWorkspaceMemoriesToBudgetV2(
|
|
16712
|
+
const trimmed = resolvedWorkspace?.isWorkspaced ? trimWorkspaceMemoriesToBudgetV2(state2.sessionId, [...memories], state2.injectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS, resolvedWorkspace, renderOptions) : trimMemoriesToBudgetV2(state2.sessionId, [...memories], state2.injectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS);
|
|
16080
16713
|
return trimmed.renderOrder.map((memory) => memory.id);
|
|
16081
16714
|
}
|
|
16082
16715
|
function isTransientSqliteLockError(error) {
|
|
@@ -16099,11 +16732,11 @@ class PiMaterializeContentionError extends Error {
|
|
|
16099
16732
|
this.name = "PiMaterializeContentionError";
|
|
16100
16733
|
}
|
|
16101
16734
|
}
|
|
16102
|
-
function readFrozenM0InputsPi(
|
|
16103
|
-
const memPath = memoryProjectPath(
|
|
16735
|
+
function readFrozenM0InputsPi(state2, db, docs = readProjectDocsForPiM0(state2), memoryCutoff) {
|
|
16736
|
+
const memPath = memoryProjectPath(state2);
|
|
16104
16737
|
const read = db.transaction(() => {
|
|
16105
|
-
const workspace = resolveWorkspaceRenderContextPi(
|
|
16106
|
-
const compartments =
|
|
16738
|
+
const workspace = resolveWorkspaceRenderContextPi(state2, db);
|
|
16739
|
+
const compartments = getRenderableCompartmentsPi(db, state2);
|
|
16107
16740
|
const memories = memPath ? workspace.isWorkspaced ? getMemoriesByProjects(db, workspace.expandedIdentities, ["active", "permanent"], memoryCutoff, workspace.ownIdentities, workspace.shareCategories) : getMemoriesByProject(db, memPath, ["active", "permanent"], memoryCutoff) : [];
|
|
16108
16741
|
const userProfile = safeGetActiveUserMemoriesPi(db);
|
|
16109
16742
|
const projectState = memPath ? getProjectState(db, memPath) : undefined;
|
|
@@ -16111,72 +16744,72 @@ function readFrozenM0InputsPi(state, db, docs = readProjectDocsForPiM0(state), m
|
|
|
16111
16744
|
const markers = {
|
|
16112
16745
|
maxCompartmentSeq: compartments.reduce((max, compartment) => compartment.sequence > max ? compartment.sequence : max, EMPTY_MAX_COMPARTMENT_SEQ),
|
|
16113
16746
|
maxMemoryId: memPath ? workspace.isWorkspaced ? getMaxMemoryIdForProjects(db, workspace.expandedIdentities, workspace.ownIdentities, workspace.shareCategories) : getMaxMemoryIdForProjects(db, [memPath]) : 0,
|
|
16114
|
-
maxMutationId: getMaxM0MutationId(db,
|
|
16747
|
+
maxMutationId: getMaxM0MutationId(db, state2.sessionId) ?? 0,
|
|
16115
16748
|
maxMemoryMutationId: memPath ? workspace.isWorkspaced ? getMaxMemoryMutationIdForProjects(db, workspace.expandedIdentities) ?? 0 : getMaxMemoryMutationId(db, memPath) ?? 0 : 0,
|
|
16116
16749
|
projectMemoryEpoch: projectState?.projectMemoryEpoch ?? 0,
|
|
16117
16750
|
workspaceFingerprint: workspace.isWorkspaced ? computeWorkspaceEpochFingerprint(db, workspace.identities) : null,
|
|
16118
16751
|
projectUserProfileVersion: globalState?.projectUserProfileVersion ?? 0,
|
|
16119
16752
|
projectDocsHash: docs.canonicalHash,
|
|
16120
|
-
sessionFactsVersion: getSessionFactsVersion(db,
|
|
16753
|
+
sessionFactsVersion: getSessionFactsVersion(db, state2.sessionId),
|
|
16121
16754
|
materializedAt: memoryCutoff ?? Date.now(),
|
|
16122
16755
|
upgradeState: `${PI_M0_UPGRADE_STATE}:${compartments.some((c) => c.legacy === 1) ? "legacy" : "ready"}`,
|
|
16123
16756
|
compartmentRenderEpoch: COMPARTMENT_RENDER_EPOCH,
|
|
16124
16757
|
lastBaselineEndMessageId: lastBaselineEndMessageId(compartments),
|
|
16125
|
-
systemHash: (
|
|
16126
|
-
modelKey: (
|
|
16127
|
-
projectIdentity:
|
|
16758
|
+
systemHash: (state2.hardSignals ?? EMPTY_PI_HARD_SIGNALS).systemHash,
|
|
16759
|
+
modelKey: (state2.hardSignals ?? EMPTY_PI_HARD_SIGNALS).modelKey,
|
|
16760
|
+
projectIdentity: state2.projectIdentity
|
|
16128
16761
|
};
|
|
16129
16762
|
return { docs, markers, compartments, memories, userProfile, workspace };
|
|
16130
16763
|
});
|
|
16131
16764
|
return read();
|
|
16132
16765
|
}
|
|
16133
|
-
function renderFreshM0PiNonPersisted(
|
|
16134
|
-
const docs = readProjectDocsForPiM0(
|
|
16135
|
-
const cachedMaterializedAt = getOrCreateSessionMeta(db,
|
|
16136
|
-
const frozen = readFrozenM0InputsPi(
|
|
16766
|
+
function renderFreshM0PiNonPersisted(state2, db) {
|
|
16767
|
+
const docs = readProjectDocsForPiM0(state2);
|
|
16768
|
+
const cachedMaterializedAt = getOrCreateSessionMeta(db, state2.sessionId).cachedM0MaterializedAt ?? 0;
|
|
16769
|
+
const frozen = readFrozenM0InputsPi(state2, db, docs, cachedMaterializedAt);
|
|
16137
16770
|
frozen.markers.materializedAt = cachedMaterializedAt;
|
|
16138
|
-
const historyBudget =
|
|
16139
|
-
const memoryBudget =
|
|
16140
|
-
const mural = resolveMuralForM0Pi(
|
|
16141
|
-
rememberPiMural(
|
|
16771
|
+
const historyBudget = state2.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS;
|
|
16772
|
+
const memoryBudget = state2.injectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS;
|
|
16773
|
+
const mural = resolveMuralForM0Pi(state2, db, frozen.markers.modelKey, memoryBudget);
|
|
16774
|
+
rememberPiMural(state2.sessionId, mural);
|
|
16142
16775
|
let dpm = 1;
|
|
16143
|
-
let m0 = renderM0Pi(
|
|
16776
|
+
let m0 = renderM0Pi(state2, db, docs.renderedBlock, dpm, frozen.memories, frozen.compartments, frozen.userProfile, frozen.workspace, mural);
|
|
16144
16777
|
let attempts = 0;
|
|
16145
16778
|
while (historyBudget > 0 && historySliceTokensPi(m0) > historyBudget * 1.05 && attempts < 3) {
|
|
16146
16779
|
dpm *= 1.15;
|
|
16147
|
-
m0 = renderM0Pi(
|
|
16780
|
+
m0 = renderM0Pi(state2, db, docs.renderedBlock, dpm, frozen.memories, frozen.compartments, frozen.userProfile, frozen.workspace, mural);
|
|
16148
16781
|
attempts += 1;
|
|
16149
16782
|
}
|
|
16150
16783
|
return {
|
|
16151
16784
|
m0,
|
|
16152
16785
|
snapshotMarkers: frozen.markers,
|
|
16153
|
-
renderedMemoryIds: renderedMemoryIdsForPi(
|
|
16786
|
+
renderedMemoryIds: renderedMemoryIdsForPi(state2, frozen.memories, frozen.workspace, db)
|
|
16154
16787
|
};
|
|
16155
16788
|
}
|
|
16156
|
-
function materializeM0Pi(
|
|
16157
|
-
const docs = readProjectDocsForPiM0(
|
|
16789
|
+
function materializeM0Pi(state2, db) {
|
|
16790
|
+
const docs = readProjectDocsForPiM0(state2);
|
|
16158
16791
|
const foldMaterializedAt = Date.now();
|
|
16159
|
-
const frozen = readFrozenM0InputsPi(
|
|
16792
|
+
const frozen = readFrozenM0InputsPi(state2, db, docs, foldMaterializedAt);
|
|
16160
16793
|
const snapshotMarkers = frozen.markers;
|
|
16161
16794
|
const snapshotMemories = frozen.memories;
|
|
16162
16795
|
const snapshotCompartments = frozen.compartments;
|
|
16163
16796
|
const snapshotUserProfile = frozen.userProfile;
|
|
16164
|
-
const renderedMemoryIds = renderedMemoryIdsForPi(
|
|
16165
|
-
const memoryBudget =
|
|
16166
|
-
const mural = resolveMuralForM0Pi(
|
|
16797
|
+
const renderedMemoryIds = renderedMemoryIdsForPi(state2, snapshotMemories, frozen.workspace, db);
|
|
16798
|
+
const memoryBudget = state2.injectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS;
|
|
16799
|
+
const mural = resolveMuralForM0Pi(state2, db, snapshotMarkers.modelKey, memoryBudget);
|
|
16167
16800
|
const frozenMuralDataUrl = mural?.enabled && mural.supportsVision ? mural.dataUrl ?? null : null;
|
|
16168
16801
|
const frozenMuralHash = mural?.enabled && mural.supportsVision ? mural.contentHash ?? null : null;
|
|
16169
16802
|
let decayPressureMultiplier = 1;
|
|
16170
|
-
let m0 = renderM0Pi(
|
|
16171
|
-
const historyBudget =
|
|
16803
|
+
let m0 = renderM0Pi(state2, db, docs.renderedBlock, decayPressureMultiplier, snapshotMemories, snapshotCompartments, snapshotUserProfile, frozen.workspace, mural);
|
|
16804
|
+
const historyBudget = state2.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS;
|
|
16172
16805
|
let attempts = 0;
|
|
16173
16806
|
while (historyBudget > 0 && historySliceTokensPi(m0) > historyBudget * 1.05 && attempts < 3) {
|
|
16174
16807
|
decayPressureMultiplier *= 1.15;
|
|
16175
|
-
m0 = renderM0Pi(
|
|
16808
|
+
m0 = renderM0Pi(state2, db, docs.renderedBlock, decayPressureMultiplier, snapshotMemories, snapshotCompartments, snapshotUserProfile, frozen.workspace, mural);
|
|
16176
16809
|
attempts += 1;
|
|
16177
16810
|
}
|
|
16178
16811
|
const m0Bytes = Buffer.from(m0, "utf8");
|
|
16179
|
-
const phase3ProjectDocsHash = readProjectDocsForPiM0(
|
|
16812
|
+
const phase3ProjectDocsHash = readProjectDocsForPiM0(state2).canonicalHash;
|
|
16180
16813
|
try {
|
|
16181
16814
|
db.exec("BEGIN IMMEDIATE");
|
|
16182
16815
|
} catch (error) {
|
|
@@ -16186,7 +16819,7 @@ function materializeM0Pi(state, db) {
|
|
|
16186
16819
|
throw error;
|
|
16187
16820
|
}
|
|
16188
16821
|
try {
|
|
16189
|
-
const current = readCurrentMarkers(db,
|
|
16822
|
+
const current = readCurrentMarkers(db, state2, phase3ProjectDocsHash);
|
|
16190
16823
|
const memoryEpochStale = current.workspaceFingerprint !== null || snapshotMarkers.workspaceFingerprint !== null ? current.workspaceFingerprint !== snapshotMarkers.workspaceFingerprint : current.projectMemoryEpoch !== snapshotMarkers.projectMemoryEpoch;
|
|
16191
16824
|
const stale = memoryEpochStale || current.projectUserProfileVersion !== snapshotMarkers.projectUserProfileVersion || current.maxCompartmentSeq !== snapshotMarkers.maxCompartmentSeq || current.maxMutationId !== snapshotMarkers.maxMutationId || current.maxMemoryMutationId !== snapshotMarkers.maxMemoryMutationId || current.projectIdentity !== snapshotMarkers.projectIdentity || current.sessionFactsVersion !== snapshotMarkers.sessionFactsVersion || current.upgradeState !== snapshotMarkers.upgradeState;
|
|
16192
16825
|
if (stale) {
|
|
@@ -16194,9 +16827,9 @@ function materializeM0Pi(state, db) {
|
|
|
16194
16827
|
throw new PiMaterializeContentionError("snapshot changed before persist");
|
|
16195
16828
|
}
|
|
16196
16829
|
snapshotMarkers.materializedAt = foldMaterializedAt;
|
|
16197
|
-
const m1Render = renderM1PiWithMetadata(
|
|
16830
|
+
const m1Render = renderM1PiWithMetadata(state2, db, snapshotMarkers, renderedMemoryIds);
|
|
16198
16831
|
const m1Bytes = Buffer.from(m1Render.text, "utf8");
|
|
16199
|
-
persistCachedM0(db,
|
|
16832
|
+
persistCachedM0(db, state2.sessionId, {
|
|
16200
16833
|
m0Bytes,
|
|
16201
16834
|
muralDataUrl: frozenMuralDataUrl,
|
|
16202
16835
|
muralHash: frozenMuralHash,
|
|
@@ -16216,10 +16849,10 @@ function materializeM0Pi(state, db) {
|
|
|
16216
16849
|
modelKey: snapshotMarkers.modelKey,
|
|
16217
16850
|
projectIdentity: snapshotMarkers.projectIdentity
|
|
16218
16851
|
});
|
|
16219
|
-
db.prepare("UPDATE session_meta SET memory_block_count = ?, memory_block_ids = ? WHERE session_id = ?").run(renderedMemoryIds.length, JSON.stringify(renderedMemoryIds),
|
|
16220
|
-
setCachedBoundary(db,
|
|
16852
|
+
db.prepare("UPDATE session_meta SET memory_block_count = ?, memory_block_ids = ? WHERE session_id = ?").run(renderedMemoryIds.length, JSON.stringify(renderedMemoryIds), state2.sessionId);
|
|
16853
|
+
setCachedBoundary(db, state2.sessionId, snapshotMarkers.lastBaselineEndMessageId);
|
|
16221
16854
|
db.exec("COMMIT");
|
|
16222
|
-
rememberPiMuralPayload(
|
|
16855
|
+
rememberPiMuralPayload(state2.sessionId, frozenMuralDataUrl, frozenMuralHash);
|
|
16223
16856
|
return {
|
|
16224
16857
|
m0,
|
|
16225
16858
|
m1: m1Render.text,
|
|
@@ -16233,11 +16866,11 @@ function materializeM0Pi(state, db) {
|
|
|
16233
16866
|
throw error;
|
|
16234
16867
|
}
|
|
16235
16868
|
}
|
|
16236
|
-
function materializeM0PiWithRetry(
|
|
16869
|
+
function materializeM0PiWithRetry(state2, db, maxRetries = 3) {
|
|
16237
16870
|
let lastError = null;
|
|
16238
16871
|
for (let attempt = 0;attempt < maxRetries; attempt++) {
|
|
16239
16872
|
try {
|
|
16240
|
-
return materializeM0Pi(
|
|
16873
|
+
return materializeM0Pi(state2, db);
|
|
16241
16874
|
} catch (error) {
|
|
16242
16875
|
if (!(error instanceof PiMaterializeContentionError))
|
|
16243
16876
|
throw error;
|
|
@@ -16279,10 +16912,10 @@ ${lines.join(`
|
|
|
16279
16912
|
count: mutations.length
|
|
16280
16913
|
};
|
|
16281
16914
|
}
|
|
16282
|
-
function renderM1PiWithMetadata(
|
|
16915
|
+
function renderM1PiWithMetadata(state2, db, markers, renderedMemoryIds, compartmentsOverride) {
|
|
16283
16916
|
const sections = [];
|
|
16284
|
-
const workspace = resolveWorkspaceRenderContextPi(
|
|
16285
|
-
const memPath = memoryProjectPath(
|
|
16917
|
+
const workspace = resolveWorkspaceRenderContextPi(state2, db);
|
|
16918
|
+
const memPath = memoryProjectPath(state2);
|
|
16286
16919
|
const memoryUpdates = memPath ? renderMemoryUpdatesBlockPi({
|
|
16287
16920
|
db,
|
|
16288
16921
|
projectPath: memPath,
|
|
@@ -16292,7 +16925,7 @@ function renderM1PiWithMetadata(state, db, markers, renderedMemoryIds, compartme
|
|
|
16292
16925
|
}) : { block: undefined, count: 0 };
|
|
16293
16926
|
if (memoryUpdates.block)
|
|
16294
16927
|
sections.push(memoryUpdates.block);
|
|
16295
|
-
const newCompartments = (compartmentsOverride ??
|
|
16928
|
+
const newCompartments = (compartmentsOverride ?? getRenderableCompartmentsPi(db, state2)).filter((compartment) => compartment.sequence > markers.maxCompartmentSeq);
|
|
16296
16929
|
if (newCompartments.length > 0) {
|
|
16297
16930
|
const body = newCompartments.map((compartment) => renderCompartmentAtTier(compartment, 1)).join(`
|
|
16298
16931
|
|
|
@@ -16303,7 +16936,7 @@ ${body}
|
|
|
16303
16936
|
}
|
|
16304
16937
|
const newMemories = memPath ? workspace.isWorkspaced ? readNewMemoriesForM1Union(db, workspace.expandedIdentities, markers.maxMemoryId, markers.materializedAt, workspace.ownIdentities, workspace.shareCategories) : getMemoriesByProject(db, memPath, ["active", "permanent"], markers.materializedAt).filter((memory) => memory.id > markers.maxMemoryId) : [];
|
|
16305
16938
|
if (newMemories.length > 0) {
|
|
16306
|
-
const memoryBudget =
|
|
16939
|
+
const memoryBudget = state2.injectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS;
|
|
16307
16940
|
const memoryRenderOptions = {
|
|
16308
16941
|
sourceNameByMemoryId: sourceNamesForPiMemories({
|
|
16309
16942
|
memories: newMemories,
|
|
@@ -16311,14 +16944,14 @@ ${body}
|
|
|
16311
16944
|
workspace
|
|
16312
16945
|
})
|
|
16313
16946
|
};
|
|
16314
|
-
const trimmedNewMemories = trimMemoriesToBudgetV2(
|
|
16947
|
+
const trimmedNewMemories = trimMemoriesToBudgetV2(state2.sessionId, newMemories, Math.max(1, Math.floor(memoryBudget * 0.25)), memoryRenderOptions).renderOrder;
|
|
16315
16948
|
const newMemoriesBlock = renderMemoryBlockV2(trimmedNewMemories, "new-memories", memoryRenderOptions);
|
|
16316
16949
|
if (newMemoriesBlock)
|
|
16317
16950
|
sections.push(newMemoriesBlock);
|
|
16318
16951
|
}
|
|
16319
16952
|
const currentUserProfileVersion = getProjectState(db, GLOBAL_USER_PROFILE_PROJECT_PATH)?.projectUserProfileVersion ?? 0;
|
|
16320
16953
|
if (currentUserProfileVersion !== markers.projectUserProfileVersion) {
|
|
16321
|
-
const profileBudget =
|
|
16954
|
+
const profileBudget = state2.userProfileBudgetTokens ?? DEFAULT_USER_PROFILE_BUDGET_TOKENS;
|
|
16322
16955
|
const trimmedProfile = trimUserMemoriesToBudget(safeGetActiveUserMemoriesPi(db), Math.max(1, Math.floor(profileBudget * 0.25)));
|
|
16323
16956
|
const profileBlock = renderUserProfileBlock(db, "new-user-profile", trimmedProfile);
|
|
16324
16957
|
if (profileBlock)
|
|
@@ -16331,7 +16964,10 @@ ${body}
|
|
|
16331
16964
|
};
|
|
16332
16965
|
}
|
|
16333
16966
|
return {
|
|
16334
|
-
text: `<
|
|
16967
|
+
text: state2.compactionOff ? `<knowledge-updates>
|
|
16968
|
+
${sections.join(`
|
|
16969
|
+
`)}
|
|
16970
|
+
</knowledge-updates>` : `<session-history-since>
|
|
16335
16971
|
${sections.join(`
|
|
16336
16972
|
`)}
|
|
16337
16973
|
</session-history-since>`,
|
|
@@ -16427,8 +17063,8 @@ function cachedPiRowMatchesSnapshot(args) {
|
|
|
16427
17063
|
return false;
|
|
16428
17064
|
return bufferEqualsNullable(args.row.cached_m0_bytes, args.m0Bytes) && rowMarkers.projectMemoryEpoch === args.markers.projectMemoryEpoch && rowMarkers.projectUserProfileVersion === args.markers.projectUserProfileVersion && rowMarkers.maxCompartmentSeq === args.markers.maxCompartmentSeq && rowMarkers.maxMemoryId === args.markers.maxMemoryId && rowMarkers.maxMutationId === args.markers.maxMutationId && rowMarkers.maxMemoryMutationId === args.markers.maxMemoryMutationId && rowMarkers.materializedAt === args.markers.materializedAt && rowMarkers.sessionFactsVersion === args.markers.sessionFactsVersion && (rowMarkers.upgradeState ?? null) === (args.markers.upgradeState ?? null) && rowMarkers.compartmentRenderEpoch === args.markers.compartmentRenderEpoch && (rowMarkers.systemHash ?? "") === (args.markers.systemHash ?? "") && (rowMarkers.modelKey ?? "") === (args.markers.modelKey ?? "") && (rowMarkers.projectIdentity ?? null) === (args.markers.projectIdentity ?? null) && (rowMarkers.workspaceFingerprint ?? null) === (args.markers.workspaceFingerprint ?? null);
|
|
16429
17065
|
}
|
|
16430
|
-
function adoptCachedPiProjectIdentity(db,
|
|
16431
|
-
db.prepare("UPDATE session_meta SET cached_m0_project_identity = ? WHERE session_id = ? AND cached_m0_project_identity IS NULL").run(
|
|
17066
|
+
function adoptCachedPiProjectIdentity(db, state2) {
|
|
17067
|
+
db.prepare("UPDATE session_meta SET cached_m0_project_identity = ? WHERE session_id = ? AND cached_m0_project_identity IS NULL").run(state2.projectIdentity, state2.sessionId);
|
|
16432
17068
|
}
|
|
16433
17069
|
function decodeCachedM1(row, sessionId) {
|
|
16434
17070
|
if (!row.cached_m1_bytes) {
|
|
@@ -16449,12 +17085,12 @@ function applyCachedPiRow(args) {
|
|
|
16449
17085
|
markers
|
|
16450
17086
|
};
|
|
16451
17087
|
}
|
|
16452
|
-
function replayCachedM1Pi(db,
|
|
16453
|
-
const row = readCachedPiM0M1Row(db,
|
|
17088
|
+
function replayCachedM1Pi(db, state2, compartmentsForNormalization) {
|
|
17089
|
+
const row = readCachedPiM0M1Row(db, state2.sessionId);
|
|
16454
17090
|
if (!row) {
|
|
16455
|
-
throw new PiMaterializeContentionError(`missing cached m[0]/m[1] for ${
|
|
17091
|
+
throw new PiMaterializeContentionError(`missing cached m[0]/m[1] for ${state2.sessionId}`);
|
|
16456
17092
|
}
|
|
16457
|
-
return applyCachedPiRow({ row, state, compartmentsForNormalization });
|
|
17093
|
+
return applyCachedPiRow({ row, state: state2, compartmentsForNormalization });
|
|
16458
17094
|
}
|
|
16459
17095
|
function softRefreshCachedM1Pi(args) {
|
|
16460
17096
|
args.db.exec("BEGIN IMMEDIATE");
|
|
@@ -16471,7 +17107,7 @@ function softRefreshCachedM1Pi(args) {
|
|
|
16471
17107
|
if (!sibling) {
|
|
16472
17108
|
throw new PiMaterializeContentionError(`missing sibling cached m[0]/m[1] for ${args.state.sessionId}`);
|
|
16473
17109
|
}
|
|
16474
|
-
const siblingCompartments =
|
|
17110
|
+
const siblingCompartments = getRenderableCompartmentsPi(args.db, args.state);
|
|
16475
17111
|
return {
|
|
16476
17112
|
...applyCachedPiRow({
|
|
16477
17113
|
row: sibling,
|
|
@@ -16538,9 +17174,9 @@ function prependM0M1Messages(piMessages, m0, m1, mural) {
|
|
|
16538
17174
|
timestamp: baseTimestamp - 1
|
|
16539
17175
|
});
|
|
16540
17176
|
}
|
|
16541
|
-
function injectM0M1Pi(
|
|
16542
|
-
const currentCompartments =
|
|
16543
|
-
let decision = mustMaterializePi(
|
|
17177
|
+
function injectM0M1Pi(state2, db, piMessages, entryIds, recomputeM1ThisPass = false) {
|
|
17178
|
+
const currentCompartments = getRenderableCompartmentsPi(db, state2);
|
|
17179
|
+
let decision = mustMaterializePi(state2, db, currentCompartments);
|
|
16544
17180
|
let m0 = "";
|
|
16545
17181
|
let m1 = PI_M1_PLACEHOLDER;
|
|
16546
17182
|
let markers = null;
|
|
@@ -16551,7 +17187,7 @@ function injectM0M1Pi(state, db, piMessages, entryIds, recomputeM1ThisPass = fal
|
|
|
16551
17187
|
let freshFallbackRenderedMemoryIds = null;
|
|
16552
17188
|
if (decision.value) {
|
|
16553
17189
|
try {
|
|
16554
|
-
const result = materializeM0PiWithRetry(
|
|
17190
|
+
const result = materializeM0PiWithRetry(state2, db);
|
|
16555
17191
|
m0 = result.m0;
|
|
16556
17192
|
m1 = result.m1;
|
|
16557
17193
|
markers = result.snapshotMarkers;
|
|
@@ -16561,30 +17197,30 @@ function injectM0M1Pi(state, db, piMessages, entryIds, recomputeM1ThisPass = fal
|
|
|
16561
17197
|
if (!(error instanceof PiMaterializeContentionError))
|
|
16562
17198
|
throw error;
|
|
16563
17199
|
try {
|
|
16564
|
-
const cached = replayCachedM1Pi(db,
|
|
17200
|
+
const cached = replayCachedM1Pi(db, state2, currentCompartments);
|
|
16565
17201
|
contentionExhausted = true;
|
|
16566
17202
|
m0 = cached.m0;
|
|
16567
17203
|
m1 = cached.m1;
|
|
16568
17204
|
markers = cached.markers;
|
|
16569
|
-
sessionLog(
|
|
17205
|
+
sessionLog(state2.sessionId, "pi m[0] materialization contention exhausted; reusing cached m[0]/m[1]");
|
|
16570
17206
|
} catch {
|
|
16571
|
-
const fresh = renderFreshM0PiNonPersisted(
|
|
17207
|
+
const fresh = renderFreshM0PiNonPersisted(state2, db);
|
|
16572
17208
|
m0 = fresh.m0;
|
|
16573
17209
|
markers = fresh.snapshotMarkers;
|
|
16574
17210
|
freshFallbackRenderedMemoryIds = fresh.renderedMemoryIds;
|
|
16575
17211
|
contentionExhausted = true;
|
|
16576
|
-
sessionLog(
|
|
17212
|
+
sessionLog(state2.sessionId, "pi m[0] materialization contention exhausted with no cached fallback; rendered fresh non-persisted m[0]/m[1]");
|
|
16577
17213
|
}
|
|
16578
17214
|
}
|
|
16579
17215
|
} else {
|
|
16580
|
-
const meta = getOrCreateSessionMeta(db,
|
|
17216
|
+
const meta = getOrCreateSessionMeta(db, state2.sessionId);
|
|
16581
17217
|
m0 = decodeCachedM0(meta.cachedM0Bytes) ?? "";
|
|
16582
|
-
rememberPiMuralPayload(
|
|
16583
|
-
markers = getCachedMarkers(db,
|
|
17218
|
+
rememberPiMuralPayload(state2.sessionId, meta.cachedM0MuralDataUrl, meta.cachedM0MuralHash);
|
|
17219
|
+
markers = getCachedMarkers(db, state2, currentCompartments);
|
|
16584
17220
|
if (!m0 || !markers) {
|
|
16585
17221
|
decision = { value: true, reason: "cache_invalid" };
|
|
16586
17222
|
try {
|
|
16587
|
-
const result = materializeM0PiWithRetry(
|
|
17223
|
+
const result = materializeM0PiWithRetry(state2, db);
|
|
16588
17224
|
m0 = result.m0;
|
|
16589
17225
|
m1 = result.m1;
|
|
16590
17226
|
markers = result.snapshotMarkers;
|
|
@@ -16593,30 +17229,30 @@ function injectM0M1Pi(state, db, piMessages, entryIds, recomputeM1ThisPass = fal
|
|
|
16593
17229
|
} catch (error) {
|
|
16594
17230
|
if (!(error instanceof PiMaterializeContentionError))
|
|
16595
17231
|
throw error;
|
|
16596
|
-
const fresh = renderFreshM0PiNonPersisted(
|
|
17232
|
+
const fresh = renderFreshM0PiNonPersisted(state2, db);
|
|
16597
17233
|
m0 = fresh.m0;
|
|
16598
17234
|
markers = fresh.snapshotMarkers;
|
|
16599
17235
|
freshFallbackRenderedMemoryIds = fresh.renderedMemoryIds;
|
|
16600
17236
|
contentionExhausted = true;
|
|
16601
|
-
sessionLog(
|
|
17237
|
+
sessionLog(state2.sessionId, "pi m[0] cache_invalid materialization contention exhausted; rendered fresh non-persisted m[0]/m[1]");
|
|
16602
17238
|
}
|
|
16603
17239
|
}
|
|
16604
17240
|
}
|
|
16605
17241
|
if (!markers) {
|
|
16606
|
-
throw new PiMaterializeContentionError(`missing m[0] markers for ${
|
|
17242
|
+
throw new PiMaterializeContentionError(`missing m[0] markers for ${state2.sessionId}`);
|
|
16607
17243
|
}
|
|
16608
17244
|
if (!materialized && markers.projectIdentity === null) {
|
|
16609
|
-
adoptCachedPiProjectIdentity(db,
|
|
16610
|
-
markers = { ...markers, projectIdentity:
|
|
17245
|
+
adoptCachedPiProjectIdentity(db, state2);
|
|
17246
|
+
markers = { ...markers, projectIdentity: state2.projectIdentity };
|
|
16611
17247
|
}
|
|
16612
17248
|
if (materialized) {} else if (contentionExhausted && freshFallbackRenderedMemoryIds) {
|
|
16613
|
-
const freshM1 = renderM1PiWithMetadata(
|
|
17249
|
+
const freshM1 = renderM1PiWithMetadata(state2, db, markers, freshFallbackRenderedMemoryIds);
|
|
16614
17250
|
m1 = freshM1.text;
|
|
16615
17251
|
memoryUpdateCount = freshM1.memoryUpdateCount;
|
|
16616
17252
|
m1Recomputed = true;
|
|
16617
17253
|
} else if (contentionExhausted) {} else if (recomputeM1ThisPass) {
|
|
16618
17254
|
const refreshed = softRefreshCachedM1Pi({
|
|
16619
|
-
state,
|
|
17255
|
+
state: state2,
|
|
16620
17256
|
db,
|
|
16621
17257
|
m0Bytes: Buffer.from(m0, "utf8"),
|
|
16622
17258
|
markers,
|
|
@@ -16628,7 +17264,7 @@ function injectM0M1Pi(state, db, piMessages, entryIds, recomputeM1ThisPass = fal
|
|
|
16628
17264
|
memoryUpdateCount = refreshed.memoryUpdateCount;
|
|
16629
17265
|
m1Recomputed = refreshed.recomputed;
|
|
16630
17266
|
} else {
|
|
16631
|
-
const replayed = replayCachedM1Pi(db,
|
|
17267
|
+
const replayed = replayCachedM1Pi(db, state2, currentCompartments);
|
|
16632
17268
|
m0 = replayed.m0;
|
|
16633
17269
|
m1 = replayed.m1;
|
|
16634
17270
|
markers = replayed.markers;
|
|
@@ -16636,14 +17272,14 @@ function injectM0M1Pi(state, db, piMessages, entryIds, recomputeM1ThisPass = fal
|
|
|
16636
17272
|
const M0_DRIFT_RATIO_FLOOR_TOKENS = 500;
|
|
16637
17273
|
const M1_DRIFT_RATIO = 0.15;
|
|
16638
17274
|
const M1_ABSOLUTE_CAP_RATIO = 0.2;
|
|
16639
|
-
const m1AbsoluteBudget = (
|
|
17275
|
+
const m1AbsoluteBudget = (state2.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS) * M1_ABSOLUTE_CAP_RATIO;
|
|
16640
17276
|
const m1HasContent = m1 !== PI_M1_PLACEHOLDER;
|
|
16641
|
-
const { m0Tokens, m1Tokens } = cachedInjectionTokenCounts(
|
|
17277
|
+
const { m0Tokens, m1Tokens } = cachedInjectionTokenCounts(state2.sessionId, m0, m1);
|
|
16642
17278
|
const m1OverAbsoluteCap = m1HasContent && m1Tokens > m1AbsoluteBudget;
|
|
16643
17279
|
if (!materialized && !contentionExhausted && m1Recomputed && recomputeM1ThisPass && (memoryUpdateCount > 40 || m1OverAbsoluteCap || m1HasContent && m0Tokens >= M0_DRIFT_RATIO_FLOOR_TOKENS && m1Tokens > m0Tokens * M1_DRIFT_RATIO)) {
|
|
16644
17280
|
decision = { value: true, reason: "drift" };
|
|
16645
17281
|
try {
|
|
16646
|
-
const result = materializeM0PiWithRetry(
|
|
17282
|
+
const result = materializeM0PiWithRetry(state2, db);
|
|
16647
17283
|
m0 = result.m0;
|
|
16648
17284
|
m1 = result.m1;
|
|
16649
17285
|
markers = result.snapshotMarkers;
|
|
@@ -16666,17 +17302,17 @@ function injectM0M1Pi(state, db, piMessages, entryIds, recomputeM1ThisPass = fal
|
|
|
16666
17302
|
}
|
|
16667
17303
|
}
|
|
16668
17304
|
const skippedVisibleMessages = boundaryId ? trimPiMessagesToBoundary(piMessages, entryIds, boundaryId) : 0;
|
|
16669
|
-
const muralWire = m0.includes("<memory-mural>") ? muralForWire(
|
|
17305
|
+
const muralWire = m0.includes("<memory-mural>") ? muralForWire(state2.sessionId) : undefined;
|
|
16670
17306
|
if (!muralWire)
|
|
16671
17307
|
m0 = stripMemoryMuralBlock(m0);
|
|
16672
17308
|
prependM0M1Messages(piMessages, m0, m1, muralWire);
|
|
16673
|
-
sessionLog(
|
|
16674
|
-
const memPath = memoryProjectPath(
|
|
16675
|
-
const workspace = resolveWorkspaceRenderContextPi(
|
|
17309
|
+
sessionLog(state2.sessionId, `injected m[0]/m[1] into Pi messages (${m0.length} + ${m1.length} bytes, materialized=${materialized}${decision.reason ? ` reason=${decision.reason}` : ""})`);
|
|
17310
|
+
const memPath = memoryProjectPath(state2);
|
|
17311
|
+
const workspace = resolveWorkspaceRenderContextPi(state2, db);
|
|
16676
17312
|
const memoryCount = memPath ? workspace.isWorkspaced ? getMemoriesByProjects(db, workspace.expandedIdentities, ["active", "permanent"], Date.now(), workspace.ownIdentities, workspace.shareCategories).length : getMemoriesByProject(db, memPath, ["active", "permanent"]).length : 0;
|
|
16677
17313
|
return {
|
|
16678
17314
|
injected: true,
|
|
16679
|
-
compartmentCount:
|
|
17315
|
+
compartmentCount: currentCompartments.length,
|
|
16680
17316
|
factCount: 0,
|
|
16681
17317
|
memoryCount,
|
|
16682
17318
|
skippedVisibleMessages,
|
|
@@ -16738,6 +17374,16 @@ function isSentinelPart(part) {
|
|
|
16738
17374
|
return p.type === "text" && typeof p.text === "string" && (p.text === "" || p.text === WHOLE_MESSAGE_PLACEHOLDER_TEXT);
|
|
16739
17375
|
}
|
|
16740
17376
|
|
|
17377
|
+
// src/pi-context-limit.ts
|
|
17378
|
+
function resolvePiUsableContextLimit(args) {
|
|
17379
|
+
const rawContext = isSaneLimit(args.rawContextWindow) ? args.rawContextWindow : undefined;
|
|
17380
|
+
const detected = typeof args.detectedContextLimit === "number" && Number.isFinite(args.detectedContextLimit) && args.detectedContextLimit >= 1024 ? args.detectedContextLimit : undefined;
|
|
17381
|
+
const context = rawContext !== undefined && detected !== undefined ? Math.min(rawContext, detected) : rawContext ?? detected;
|
|
17382
|
+
if (context === undefined)
|
|
17383
|
+
return;
|
|
17384
|
+
return resolveLimit({ context, output: args.model?.maxTokens }, args.model?.provider ?? "unknown", args.model?.id ?? "unknown");
|
|
17385
|
+
}
|
|
17386
|
+
|
|
16741
17387
|
// src/pi-historian-runner.ts
|
|
16742
17388
|
import * as crypto2 from "node:crypto";
|
|
16743
17389
|
|
|
@@ -19928,6 +20574,7 @@ async function runPiHistorian(deps) {
|
|
|
19928
20574
|
twoPass,
|
|
19929
20575
|
thinkingLevel,
|
|
19930
20576
|
memoryEnabled,
|
|
20577
|
+
allowHomeProject,
|
|
19931
20578
|
autoPromote,
|
|
19932
20579
|
userMemoriesEnabled,
|
|
19933
20580
|
onPublished,
|
|
@@ -20066,7 +20713,7 @@ async function runPiHistorian(deps) {
|
|
|
20066
20713
|
rollbackDrainReservation();
|
|
20067
20714
|
return;
|
|
20068
20715
|
}
|
|
20069
|
-
const projectPath = resolveProjectIdentityForSession(directory);
|
|
20716
|
+
const projectPath = resolveProjectIdentityForSession(directory, allowHomeProject);
|
|
20070
20717
|
if (!projectPath) {
|
|
20071
20718
|
rollbackDrainReservation();
|
|
20072
20719
|
return;
|
|
@@ -22021,7 +22668,6 @@ function extractStableId(msg, index, entryIds) {
|
|
|
22021
22668
|
}
|
|
22022
22669
|
|
|
22023
22670
|
// src/context-handler.ts
|
|
22024
|
-
var FORCE_MATERIALIZATION_PERCENTAGE = 85;
|
|
22025
22671
|
var EMERGENCY_BLOCK_PERCENTAGE = 95;
|
|
22026
22672
|
var FORWARD_PRESSURE_LIMIT_FACTOR = 0.85;
|
|
22027
22673
|
function applyForwardPressureFloor(trailingPercentage, trailingInputTokens, piUsageTokens, correctedLimit) {
|
|
@@ -22643,8 +23289,8 @@ function parsePiFallbackToolOwnerId(ownerMsgId) {
|
|
|
22643
23289
|
return { timestamp, role: match[2] ?? "" };
|
|
22644
23290
|
}
|
|
22645
23291
|
function databaseIsInTransaction(db) {
|
|
22646
|
-
const
|
|
22647
|
-
return
|
|
23292
|
+
const state2 = db;
|
|
23293
|
+
return state2.inTransaction === true || state2.isTransaction === true;
|
|
22648
23294
|
}
|
|
22649
23295
|
function runImmediateTransaction(db, fn) {
|
|
22650
23296
|
if (databaseIsInTransaction(db)) {
|
|
@@ -22770,7 +23416,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
22770
23416
|
const options = baseOptions.resolveForProject?.(projectDirectory) ?? baseOptions;
|
|
22771
23417
|
const schedulerConfig = options.scheduler ?? DEFAULT_SCHEDULER_CONFIG;
|
|
22772
23418
|
const scheduler = schedulerFor(options);
|
|
22773
|
-
const projectIdentity = resolveProjectIdentityForSession(projectDirectory) ?? "";
|
|
23419
|
+
const projectIdentity = resolveProjectIdentityForSession(projectDirectory, options.allowHomeProject) ?? "";
|
|
22774
23420
|
updateSessionProjectTracking(sessionId, projectIdentity, options.db);
|
|
22775
23421
|
logTransformTiming(sessionId, "findSessionId", tFindSession, `messages=${event.messages.length}`);
|
|
22776
23422
|
const tEntryBranch = performance.now();
|
|
@@ -22792,7 +23438,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
22792
23438
|
const alignedEntryIds = branchLookup?.alignedEntryIds ?? null;
|
|
22793
23439
|
const resolvedEntryIds = alignedEntryIds?.length === event.messages.length ? alignedEntryIds : branchEntries === null ? null : collectMessageEntryIdsByRef(ctx, event.messages, sessionId, branchEntries);
|
|
22794
23440
|
const strictEntryIds = resolvedEntryIds ? [...resolvedEntryIds] : null;
|
|
22795
|
-
if (strictEntryIds && options.injection) {
|
|
23441
|
+
if (strictEntryIds && options.injection && !options.compactionOff) {
|
|
22796
23442
|
const removed = trimPiMessagesToCachedBoundary(options.db, sessionId, event.messages, strictEntryIds);
|
|
22797
23443
|
if (removed > 0) {
|
|
22798
23444
|
logTransformTiming(sessionId, "cachedBoundaryEarlyTrim", tEntryBranch, `removed=${removed}`);
|
|
@@ -22840,6 +23486,30 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
22840
23486
|
const tMeta = performance.now();
|
|
22841
23487
|
const sessionMetaForUsage = getOrCreateSessionMeta(options.db, sessionId);
|
|
22842
23488
|
logTransformTiming(sessionId, "getOrCreateSessionMeta", tMeta);
|
|
23489
|
+
const compactionTransition = reconcilePiCompactionMode({
|
|
23490
|
+
db: options.db,
|
|
23491
|
+
sessionId,
|
|
23492
|
+
compactionOff: options.compactionOff === true,
|
|
23493
|
+
historianRunnable: options.historian !== undefined
|
|
23494
|
+
});
|
|
23495
|
+
if (compactionTransition.recordToWrite) {
|
|
23496
|
+
if (compactionTransition.clearDeferredMarkerState) {
|
|
23497
|
+
clearPiCompactionOffInMemoryState(sessionId);
|
|
23498
|
+
}
|
|
23499
|
+
if (compactionTransition.invalidatedBaseline) {
|
|
23500
|
+
clearPiInjectionTokenCountCache(sessionId);
|
|
23501
|
+
sessionMetaForUsage.cachedM0Bytes = null;
|
|
23502
|
+
sessionMetaForUsage.cachedM1Bytes = null;
|
|
23503
|
+
}
|
|
23504
|
+
let noticeDelivered = compactionTransition.notice === null;
|
|
23505
|
+
if (compactionTransition.notice && ctx.ui?.notify) {
|
|
23506
|
+
ctx.ui.notify(compactionTransition.notice, "info");
|
|
23507
|
+
noticeDelivered = true;
|
|
23508
|
+
}
|
|
23509
|
+
if (noticeDelivered) {
|
|
23510
|
+
commitPiCompactionModeRecord(options.db, sessionId, compactionTransition.recordToWrite);
|
|
23511
|
+
}
|
|
23512
|
+
}
|
|
22843
23513
|
const usageReset = {
|
|
22844
23514
|
lastContextPercentage: 0,
|
|
22845
23515
|
lastInputTokens: 0,
|
|
@@ -22883,12 +23553,14 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
22883
23553
|
usageInputTokens = typeof piUsage?.tokens === "number" ? piUsage.tokens : 0;
|
|
22884
23554
|
}
|
|
22885
23555
|
let usageContextLimit = isSaneLimit(piUsage?.contextWindow) ? piUsage.contextWindow : undefined;
|
|
23556
|
+
let detectedContextLimit;
|
|
22886
23557
|
const tEmergencyRecovery = performance.now();
|
|
22887
23558
|
let needsEmergencyBump = false;
|
|
22888
23559
|
let emergencyRecoveryArmed = false;
|
|
22889
23560
|
try {
|
|
22890
|
-
const overflowState = getOverflowState(options.db, sessionId);
|
|
23561
|
+
const overflowState = options.compactionOff ? { detectedContextLimit: 0, needsEmergencyRecovery: false } : getOverflowState(options.db, sessionId);
|
|
22891
23562
|
if (overflowState.detectedContextLimit > 0) {
|
|
23563
|
+
detectedContextLimit = overflowState.detectedContextLimit;
|
|
22892
23564
|
usageContextLimit = Math.min(usageContextLimit ?? overflowState.detectedContextLimit, overflowState.detectedContextLimit);
|
|
22893
23565
|
}
|
|
22894
23566
|
emergencyRecoveryArmed = overflowState.needsEmergencyRecovery;
|
|
@@ -22906,6 +23578,17 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
22906
23578
|
usageContextLimit = modelWindow;
|
|
22907
23579
|
}
|
|
22908
23580
|
}
|
|
23581
|
+
usageContextLimit = resolvePiUsableContextLimit({
|
|
23582
|
+
rawContextWindow: usageContextLimit,
|
|
23583
|
+
model: ctx.model,
|
|
23584
|
+
detectedContextLimit
|
|
23585
|
+
});
|
|
23586
|
+
const effectiveExecuteThresholdPercentage = resolveExecuteThreshold(schedulerConfig.executeThresholdPercentage, modelKey, 65, {
|
|
23587
|
+
tokensConfig: schedulerConfig.executeThresholdTokens,
|
|
23588
|
+
contextLimit: usageContextLimit,
|
|
23589
|
+
sessionId
|
|
23590
|
+
});
|
|
23591
|
+
const { forceMaterializationPercentage } = escalationBands(effectiveExecuteThresholdPercentage);
|
|
22909
23592
|
if (!usedPersistedUsage && isSaneLimit(usageContextLimit) && usageInputTokens > 0) {
|
|
22910
23593
|
usagePercentage = usageInputTokens / usageContextLimit * 100;
|
|
22911
23594
|
}
|
|
@@ -22926,7 +23609,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
22926
23609
|
logTransformTiming(sessionId, "schedulerAndUsage", tScheduler);
|
|
22927
23610
|
const piMessageCount = fullWireMessageCount;
|
|
22928
23611
|
const looksLikeImportedSession = schedulerDecision === "defer" && usagePercentage === 0 && sessionMeta.lastResponseTime === 0 && piMessageCount >= 50;
|
|
22929
|
-
if (looksLikeImportedSession) {
|
|
23612
|
+
if (looksLikeImportedSession && !options.compactionOff) {
|
|
22930
23613
|
schedulerDecision = "execute";
|
|
22931
23614
|
sessionLog(sessionId, `transform: large imported session detected (${piMessageCount} messages, no usage baseline) — forcing execute on first pass`);
|
|
22932
23615
|
}
|
|
@@ -22937,7 +23620,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
22937
23620
|
if (storedStableIdScheme < PI_STABLE_ID_SCHEME && !realEntryIdsAvailable) {
|
|
22938
23621
|
sessionLog(sessionId, `stable-id scheme cutover deferred: real SessionEntry ids unavailable this pass (branch resolution failed) — will retry when getBranch() succeeds`);
|
|
22939
23622
|
}
|
|
22940
|
-
if (stableIdSchemeCutover) {
|
|
23623
|
+
if (stableIdSchemeCutover && !options.compactionOff) {
|
|
22941
23624
|
schedulerDecision = "execute";
|
|
22942
23625
|
signalPiPendingMaterialization(sessionId);
|
|
22943
23626
|
sessionLog(sessionId, `stable-id scheme cutover: stored=${storedStableIdScheme} < current=${PI_STABLE_ID_SCHEME} — forcing execute+materialize this pass`);
|
|
@@ -22949,14 +23632,18 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
22949
23632
|
contextUsage: { percentage: usagePercentage },
|
|
22950
23633
|
sessionMeta,
|
|
22951
23634
|
historyRefreshSessions,
|
|
22952
|
-
sessionId
|
|
23635
|
+
sessionId,
|
|
23636
|
+
effectiveExecuteThresholdPercentage
|
|
22953
23637
|
});
|
|
22954
|
-
const { midTurnAdjustedSchedulerDecision, sideEffect } =
|
|
23638
|
+
const { midTurnAdjustedSchedulerDecision, sideEffect } = options.compactionOff ? {
|
|
23639
|
+
midTurnAdjustedSchedulerDecision: "defer",
|
|
23640
|
+
sideEffect: "none"
|
|
23641
|
+
} : applyMidTurnDeferral({
|
|
22955
23642
|
base: schedulerDecisionEarly,
|
|
22956
23643
|
bypassReason,
|
|
22957
23644
|
midTurn
|
|
22958
23645
|
});
|
|
22959
|
-
if (sideEffect === "set-flag") {
|
|
23646
|
+
if (sideEffect === "set-flag" && !options.compactionOff) {
|
|
22960
23647
|
const flagPayload = {
|
|
22961
23648
|
id: crypto3.randomUUID(),
|
|
22962
23649
|
reason: `${schedulerDecisionEarly}-${bypassReason}`,
|
|
@@ -22966,8 +23653,8 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
22966
23653
|
}
|
|
22967
23654
|
schedulerDecision = midTurnAdjustedSchedulerDecision;
|
|
22968
23655
|
sessionLog(sessionId, `[boundary-exec] base=${schedulerDecisionEarly} bypass=${bypassReason} midTurn=${midTurn} effective=${midTurnAdjustedSchedulerDecision} sideEffect=${sideEffect}`);
|
|
22969
|
-
const forceMaterialization = usagePercentage >=
|
|
22970
|
-
const isEmergency = usagePercentage >= EMERGENCY_BLOCK_PERCENTAGE;
|
|
23656
|
+
const forceMaterialization = !options.compactionOff && usagePercentage >= forceMaterializationPercentage;
|
|
23657
|
+
const isEmergency = !options.compactionOff && usagePercentage >= EMERGENCY_BLOCK_PERCENTAGE;
|
|
22971
23658
|
if (isEmergency) {
|
|
22972
23659
|
const lastNotifiedAt = lastEmergencyNotificationAtMs.get(sessionId) ?? 0;
|
|
22973
23660
|
const now = Date.now();
|
|
@@ -22983,7 +23670,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
22983
23670
|
sessionLog(sessionId, "EMERGENCY: historian wait completed (or timed out)");
|
|
22984
23671
|
} catch {}
|
|
22985
23672
|
}
|
|
22986
|
-
if (emergencyRecoveryArmed && realUsagePercentageBeforeEmergencyBump <
|
|
23673
|
+
if (emergencyRecoveryArmed && realUsagePercentageBeforeEmergencyBump < forceMaterializationPercentage && !inFlightHistorian.has(sessionId) && !hasEligiblePiCompartmentHistory(options.db, sessionId)) {
|
|
22987
23674
|
try {
|
|
22988
23675
|
clearEmergencyRecovery(options.db, sessionId);
|
|
22989
23676
|
sessionLog(sessionId, "EMERGENCY: disarming recovery — no eligible pre-tail history to compact (would otherwise loop at 95%)");
|
|
@@ -23035,6 +23722,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
23035
23722
|
stableIdSchemeCutover,
|
|
23036
23723
|
schedulerDecision,
|
|
23037
23724
|
forceMaterialization: forceMaterialization || isEmergency,
|
|
23725
|
+
forceMaterializationPercentage,
|
|
23038
23726
|
contextUsage: {
|
|
23039
23727
|
percentage: usagePercentage,
|
|
23040
23728
|
inputTokens: usageInputTokens
|
|
@@ -23047,7 +23735,8 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
23047
23735
|
temporalAwareness: options.injection?.temporalAwareness === true,
|
|
23048
23736
|
appendCompaction: resolvePiAppendCompaction(ctx),
|
|
23049
23737
|
readBranchEntries: resolvePiReadBranchEntries(ctx),
|
|
23050
|
-
isSubagent: sessionMeta.isSubagent
|
|
23738
|
+
isSubagent: sessionMeta.isSubagent,
|
|
23739
|
+
compactionOff: options.compactionOff === true
|
|
23051
23740
|
});
|
|
23052
23741
|
logTransformTiming(sessionId, "runPipeline", tRunPipeline);
|
|
23053
23742
|
const postPipelineStart = performance.now();
|
|
@@ -23071,7 +23760,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
23071
23760
|
}
|
|
23072
23761
|
logTransformTiming(sessionId, "transformDecisionAndReuseState", tTransformDecision);
|
|
23073
23762
|
const tHistorianScheduling = performance.now();
|
|
23074
|
-
if (options.historian) {
|
|
23763
|
+
if (options.historian && !options.compactionOff) {
|
|
23075
23764
|
maybeFireHistorian({
|
|
23076
23765
|
pi,
|
|
23077
23766
|
ctx,
|
|
@@ -23089,22 +23778,24 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
23089
23778
|
let outputMessages = result.messages;
|
|
23090
23779
|
const tNoteNudges = performance.now();
|
|
23091
23780
|
try {
|
|
23092
|
-
|
|
23093
|
-
|
|
23094
|
-
|
|
23095
|
-
|
|
23096
|
-
|
|
23097
|
-
|
|
23098
|
-
|
|
23099
|
-
|
|
23100
|
-
|
|
23101
|
-
|
|
23781
|
+
if (!options.compactionOff) {
|
|
23782
|
+
outputMessages = applyNoteNudges({
|
|
23783
|
+
sessionId,
|
|
23784
|
+
db: options.db,
|
|
23785
|
+
messages: outputMessages,
|
|
23786
|
+
projectIdentity,
|
|
23787
|
+
entryIds: strictEntryIds,
|
|
23788
|
+
entryIdByRef: result.postCommitEntryIdByRef,
|
|
23789
|
+
isCacheBusting: isCacheBusting || result.executedWorkThisPass,
|
|
23790
|
+
syntheticLeadingCount: result.syntheticLeadingCount
|
|
23791
|
+
});
|
|
23792
|
+
}
|
|
23102
23793
|
} catch (err) {
|
|
23103
23794
|
sessionLog(sessionId, `note nudges failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
23104
23795
|
}
|
|
23105
23796
|
logTransformTiming(sessionId, "noteNudges", tNoteNudges);
|
|
23106
23797
|
const tAutoSearch = performance.now();
|
|
23107
|
-
if (options.autoSearch?.enabled) {
|
|
23798
|
+
if (options.autoSearch?.enabled && !options.compactionOff) {
|
|
23108
23799
|
try {
|
|
23109
23800
|
outputMessages = await runAutoSearchHintForPi({
|
|
23110
23801
|
sessionId,
|
|
@@ -23129,7 +23820,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
23129
23820
|
const tTodoCapture = performance.now();
|
|
23130
23821
|
try {
|
|
23131
23822
|
const sessionMetaForTodo = getOrCreateSessionMeta(options.db, sessionId);
|
|
23132
|
-
if (!sessionMetaForTodo.isSubagent && sessionMetaForTodo.lastTodoState !== "") {
|
|
23823
|
+
if (!options.compactionOff && !sessionMetaForTodo.isSubagent && sessionMetaForTodo.lastTodoState !== "") {
|
|
23133
23824
|
const isCacheBustingForTodo = isCacheBusting || result.executedWorkThisPass;
|
|
23134
23825
|
outputMessages = injectSyntheticTodowriteForPi({
|
|
23135
23826
|
db: options.db,
|
|
@@ -23147,7 +23838,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
23147
23838
|
const tChannelAccounting = performance.now();
|
|
23148
23839
|
try {
|
|
23149
23840
|
const sessionMetaForCh1 = getOrCreateSessionMeta(options.db, sessionId);
|
|
23150
|
-
if (!sessionMetaForCh1.isSubagent) {
|
|
23841
|
+
if (!options.compactionOff && !sessionMetaForCh1.isSubagent) {
|
|
23151
23842
|
const resolvedExecuteThresholdPct = resolveExecuteThreshold(schedulerConfig.executeThresholdPercentage ?? 65, liveModelBySession.get(sessionId), 65, {
|
|
23152
23843
|
tokensConfig: schedulerConfig.executeThresholdTokens,
|
|
23153
23844
|
contextLimit: usageContextLimit ?? 0
|
|
@@ -23214,7 +23905,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
23214
23905
|
}
|
|
23215
23906
|
logTransformTiming(sessionId, "workMetrics", tWorkMetrics);
|
|
23216
23907
|
const tStableIdSchemePersist = performance.now();
|
|
23217
|
-
if (stableIdSchemeCutover) {
|
|
23908
|
+
if (stableIdSchemeCutover && !options.compactionOff) {
|
|
23218
23909
|
persistStableIdSchemeForRun(options.db, sessionId, {
|
|
23219
23910
|
piStableIdScheme: PI_STABLE_ID_SCHEME
|
|
23220
23911
|
});
|
|
@@ -23239,7 +23930,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
23239
23930
|
sessionLog(sessionId, `transform completed in ${transformElapsedMs.toFixed(1)}ms (${outputMessages.length} messages, ${result.targetCount} targets, watermark: ${result.reasoningWatermark})`);
|
|
23240
23931
|
return { messages: outputMessages };
|
|
23241
23932
|
} catch (err) {
|
|
23242
|
-
if (isFailClosedBlockingError(err))
|
|
23933
|
+
if (isFailClosedBlockingError(err) && !baseOptions.compactionOff)
|
|
23243
23934
|
throw err;
|
|
23244
23935
|
const message = err instanceof Error ? err.message : String(err);
|
|
23245
23936
|
const stack = err instanceof Error ? err.stack : undefined;
|
|
@@ -23396,6 +24087,7 @@ function spawnPiHistorianRun(args) {
|
|
|
23396
24087
|
twoPass: historian.twoPass,
|
|
23397
24088
|
thinkingLevel: historian.thinkingLevel,
|
|
23398
24089
|
memoryEnabled: historian.memoryEnabled,
|
|
24090
|
+
allowHomeProject: historian.allowHomeProject,
|
|
23399
24091
|
autoPromote: historian.autoPromote,
|
|
23400
24092
|
userMemoriesEnabled: historian.userMemoriesEnabled,
|
|
23401
24093
|
language: historian.language,
|
|
@@ -23475,15 +24167,22 @@ function maybeFireHistorian(args) {
|
|
|
23475
24167
|
const piUsage = ctx.getContextUsage?.();
|
|
23476
24168
|
let usageSource;
|
|
23477
24169
|
usageContextLimit = isSaneLimit(piUsage?.contextWindow) ? piUsage.contextWindow : undefined;
|
|
24170
|
+
let detectedContextLimit;
|
|
23478
24171
|
if (usageContextLimit === undefined && isSaneLimit(ctx.model?.contextWindow)) {
|
|
23479
24172
|
usageContextLimit = ctx.model.contextWindow;
|
|
23480
24173
|
}
|
|
23481
24174
|
try {
|
|
23482
24175
|
const overflowState = getOverflowState(db, sessionId);
|
|
23483
24176
|
if (overflowState.detectedContextLimit > 0) {
|
|
24177
|
+
detectedContextLimit = overflowState.detectedContextLimit;
|
|
23484
24178
|
usageContextLimit = Math.min(usageContextLimit ?? overflowState.detectedContextLimit, overflowState.detectedContextLimit);
|
|
23485
24179
|
}
|
|
23486
24180
|
} catch {}
|
|
24181
|
+
usageContextLimit = resolvePiUsableContextLimit({
|
|
24182
|
+
rawContextWindow: usageContextLimit,
|
|
24183
|
+
model: ctx.model,
|
|
24184
|
+
detectedContextLimit
|
|
24185
|
+
});
|
|
23487
24186
|
const sessionMetaForUsage = getOrCreateSessionMeta(db, sessionId);
|
|
23488
24187
|
if (sessionMetaForUsage.lastContextPercentage > 0 && sessionMetaForUsage.lastInputTokens > 0) {
|
|
23489
24188
|
usage = {
|
|
@@ -23522,6 +24221,7 @@ function maybeFireHistorian(args) {
|
|
|
23522
24221
|
modelKey,
|
|
23523
24222
|
usageContextLimit
|
|
23524
24223
|
});
|
|
24224
|
+
const historianForceMaterializationPercentage = escalationBands(triggerInputs.executeThresholdPercentage).forceMaterializationPercentage;
|
|
23525
24225
|
const boundaryContextLimit = triggerInputs.contextLimit;
|
|
23526
24226
|
const resolvePiBoundarySnapshot = (emergencyTailScale) => resolveProtectedTailBoundary(resolveBoundaryContext({
|
|
23527
24227
|
db,
|
|
@@ -23537,7 +24237,7 @@ function maybeFireHistorian(args) {
|
|
|
23537
24237
|
}));
|
|
23538
24238
|
const resolveRunnablePiBoundarySnapshot = () => {
|
|
23539
24239
|
let snapshot = ensureRunnablePiBoundaryForTests(resolvePiBoundarySnapshot());
|
|
23540
|
-
if (!hasRunnableCompartmentWindow(snapshot) && usage.percentage >=
|
|
24240
|
+
if (!hasRunnableCompartmentWindow(snapshot) && usage.percentage >= historianForceMaterializationPercentage) {
|
|
23541
24241
|
snapshot = ensureRunnablePiBoundaryForTests(resolvePiBoundarySnapshot(usage.percentage >= 95 ? 0.25 : 0.5));
|
|
23542
24242
|
}
|
|
23543
24243
|
return snapshot;
|
|
@@ -23581,15 +24281,15 @@ Historian previously failed ${failureState.failureCount} time(s), so Magic Conte
|
|
|
23581
24281
|
const trigger = checkCompartmentTrigger(db, sessionId, sessionMeta, usage, 0, triggerInputs.executeThresholdPercentage, triggerInputs.triggerBudget, triggerInputs.clearReasoningAge, triggerInputs.commitClusterTrigger, args.activeTags, boundaryContextLimit, () => {
|
|
23582
24282
|
const messages = provider.readMessages();
|
|
23583
24283
|
return { messages, absoluteMessageCount: messages.length };
|
|
23584
|
-
}, args.taggerFloor);
|
|
24284
|
+
}, args.taggerFloor, { canClearReasoning: true });
|
|
23585
24285
|
if (!trigger.shouldFire) {
|
|
23586
24286
|
sessionLog(sessionId, `historian trigger eval: shouldFire=false (no trigger condition met)`);
|
|
23587
24287
|
try {
|
|
23588
24288
|
const overflowState = getOverflowState(db, sessionId);
|
|
23589
|
-
if (overflowState.needsEmergencyRecovery && usage.percentage <
|
|
24289
|
+
if (overflowState.needsEmergencyRecovery && usage.percentage < historianForceMaterializationPercentage && !inFlightHistorian.has(sessionId)) {
|
|
23590
24290
|
boundarySnapshot ??= resolveRunnablePiBoundarySnapshot();
|
|
23591
24291
|
}
|
|
23592
|
-
if (overflowState.needsEmergencyRecovery && usage.percentage <
|
|
24292
|
+
if (overflowState.needsEmergencyRecovery && usage.percentage < historianForceMaterializationPercentage && !inFlightHistorian.has(sessionId) && boundarySnapshot !== undefined && !hasRunnableCompartmentWindow(boundarySnapshot)) {
|
|
23593
24293
|
clearEmergencyRecovery(db, sessionId);
|
|
23594
24294
|
sessionLog(sessionId, `historian: disarming stale emergency recovery — real pressure ${usage.percentage.toFixed(1)}% with no runnable compartment window (would otherwise bump to 95% every pass)`);
|
|
23595
24295
|
}
|
|
@@ -23677,7 +24377,45 @@ function captureReasoningMutationRollback(messages) {
|
|
|
23677
24377
|
}
|
|
23678
24378
|
};
|
|
23679
24379
|
}
|
|
24380
|
+
async function runCompactionOffPipeline(args) {
|
|
24381
|
+
let injectionResult = null;
|
|
24382
|
+
if (args.injection) {
|
|
24383
|
+
injectionResult = injectM0M1Pi({
|
|
24384
|
+
sessionId: args.sessionId,
|
|
24385
|
+
projectIdentity: args.projectIdentity,
|
|
24386
|
+
projectDirectory: args.projectDirectory,
|
|
24387
|
+
memoryEnabled: args.injection.memoryEnabled,
|
|
24388
|
+
injectDocs: args.injection.injectDocs,
|
|
24389
|
+
injectionBudgetTokens: args.injection.injectionBudgetTokens,
|
|
24390
|
+
historyBudgetTokens: args.injection.historyBudgetTokens,
|
|
24391
|
+
muralEnabled: args.injection.muralEnabled === true,
|
|
24392
|
+
compactionOff: true
|
|
24393
|
+
}, args.db, args.messages, args.entryIds, false);
|
|
24394
|
+
}
|
|
24395
|
+
return {
|
|
24396
|
+
messages: args.messages,
|
|
24397
|
+
heuristicsExecuted: false,
|
|
24398
|
+
executedWorkThisPass: false,
|
|
24399
|
+
historyInjected: false,
|
|
24400
|
+
syntheticLeadingCount: injectionResult?.syntheticLeadingCount ?? 0,
|
|
24401
|
+
heuristicsResult: null,
|
|
24402
|
+
injectionResult,
|
|
24403
|
+
materialized: injectionResult?.m0Materialized === true,
|
|
24404
|
+
materializeReason: injectionResult?.m0Reason ?? null,
|
|
24405
|
+
droppedTokens: 0,
|
|
24406
|
+
droppedCount: 0,
|
|
24407
|
+
emergency: false,
|
|
24408
|
+
bustedThisPass: injectionResult?.m0Materialized === true,
|
|
24409
|
+
targetCount: 0,
|
|
24410
|
+
reasoningWatermark: args.sessionMeta.clearedReasoningThroughTag ?? 0,
|
|
24411
|
+
activeTags: [],
|
|
24412
|
+
postCommitEntryIdByRef: new Map
|
|
24413
|
+
};
|
|
24414
|
+
}
|
|
23680
24415
|
async function runPipeline(args) {
|
|
24416
|
+
if (args.compactionOff)
|
|
24417
|
+
return runCompactionOffPipeline(args);
|
|
24418
|
+
const forceMaterializationPercentage = args.forceMaterializationPercentage ?? escalationBands(65).forceMaterializationPercentage;
|
|
23681
24419
|
let executedWorkThisPass = false;
|
|
23682
24420
|
let historyWasConsumedThisPass = false;
|
|
23683
24421
|
let materializationSatisfiedThisPass = false;
|
|
@@ -23716,7 +24454,7 @@ async function runPipeline(args) {
|
|
|
23716
24454
|
})();
|
|
23717
24455
|
const alreadyRanHeuristicsThisTurn = currentTurnId !== null && lastHeuristicsTurnIdBySession.get(args.sessionId) === currentTurnId;
|
|
23718
24456
|
const ctxReduceCallable = !args.sessionMeta.isSubagent;
|
|
23719
|
-
const canConsumeDeferredLate = args.schedulerDecision === "execute" || args.forceMaterialization === true || args.contextUsage.percentage >=
|
|
24457
|
+
const canConsumeDeferredLate = args.schedulerDecision === "execute" || args.forceMaterialization === true || args.contextUsage.percentage >= forceMaterializationPercentage;
|
|
23720
24458
|
const deferredMaterializeEligible = canConsumeDeferredLate && deferredMaterializationSessions.has(args.sessionId);
|
|
23721
24459
|
const piHardSignals = args.injection ? (() => {
|
|
23722
24460
|
const hardMeta = args.sessionMeta;
|
|
@@ -23966,7 +24704,7 @@ async function runPipeline(args) {
|
|
|
23966
24704
|
}
|
|
23967
24705
|
const toolReclaimExecutePass = args.schedulerDecision === "execute";
|
|
23968
24706
|
const alreadyMutatingThisPass = pendingOpsDidMutate || heuristicOrReasoningDidMutate;
|
|
23969
|
-
const emergencyDropEligible = args.forceMaterialization === true || args.contextUsage.percentage >=
|
|
24707
|
+
const emergencyDropEligible = args.forceMaterialization === true || args.contextUsage.percentage >= forceMaterializationPercentage;
|
|
23970
24708
|
let autoReclaimTargetCount = 0;
|
|
23971
24709
|
let autoReclaimDidMutate = false;
|
|
23972
24710
|
if (toolReclaimExecutePass && alreadyMutatingThisPass && !emergencyDropEligible) {
|
|
@@ -24353,6 +25091,13 @@ function appendReminderToPiUserMessage(message, reminder) {
|
|
|
24353
25091
|
}
|
|
24354
25092
|
contentArr.push({ type: "text", text: reminder.trimStart() });
|
|
24355
25093
|
}
|
|
25094
|
+
function clearPiCompactionOffInMemoryState(sessionId) {
|
|
25095
|
+
historyRefreshSessions.delete(sessionId);
|
|
25096
|
+
deferredHistoryRefreshSessions.delete(sessionId);
|
|
25097
|
+
pendingMaterializationSessions.delete(sessionId);
|
|
25098
|
+
deferredMaterializationSessions.delete(sessionId);
|
|
25099
|
+
clearPiChannel1State(sessionId);
|
|
25100
|
+
}
|
|
24356
25101
|
function clearContextHandlerSession(sessionId) {
|
|
24357
25102
|
invalidateTrueRawTokenCache({ sessionId, reason: "pi.branch.changed" });
|
|
24358
25103
|
activeContextHandlerSessions.delete(sessionId);
|
|
@@ -24411,6 +25156,14 @@ No active Pi session is available.`,
|
|
|
24411
25156
|
});
|
|
24412
25157
|
return;
|
|
24413
25158
|
}
|
|
25159
|
+
if (deps.compactionOff) {
|
|
25160
|
+
sendCtxStatusMessage(pi, {
|
|
25161
|
+
title: "/ctx-flush",
|
|
25162
|
+
text: COMPACTION_OFF_COMMAND_UNAVAILABLE,
|
|
25163
|
+
level: "warning"
|
|
25164
|
+
});
|
|
25165
|
+
return;
|
|
25166
|
+
}
|
|
24414
25167
|
const pendingBefore = getPendingOps(deps.db, sessionId).length;
|
|
24415
25168
|
const result = executeFlush(deps.db, sessionId);
|
|
24416
25169
|
signalPiHistoryRefresh(sessionId);
|
|
@@ -24455,6 +25208,24 @@ function historianResponseDumpDir(directory) {
|
|
|
24455
25208
|
return getProjectMagicContextHistorianDir(directory);
|
|
24456
25209
|
}
|
|
24457
25210
|
var MAX_HISTORIAN_RETRIES2 = 2;
|
|
25211
|
+
var HISTORIAN_REASONING_PART_TYPES = new Set(["reasoning", "thinking", "redacted_thinking"]);
|
|
25212
|
+
function extractLatestHistorianReasoning(messages) {
|
|
25213
|
+
if (!Array.isArray(messages))
|
|
25214
|
+
return null;
|
|
25215
|
+
const latest = messages.filter((message) => isRecord(message) && isRecord(message.info) && message.info.role === "assistant").sort((left, right) => historianMessageCreatedAt(right) - historianMessageCreatedAt(left))[0];
|
|
25216
|
+
if (!latest || !Array.isArray(latest.parts))
|
|
25217
|
+
return null;
|
|
25218
|
+
return latest.parts.filter(isHistorianReasoningPart).map((part) => part.text).join(`
|
|
25219
|
+
`) || null;
|
|
25220
|
+
}
|
|
25221
|
+
function isHistorianReasoningPart(part) {
|
|
25222
|
+
return isRecord(part) && typeof part.type === "string" && HISTORIAN_REASONING_PART_TYPES.has(part.type) && typeof part.text === "string" && part.text.length > 0;
|
|
25223
|
+
}
|
|
25224
|
+
function historianMessageCreatedAt(message) {
|
|
25225
|
+
if (!isRecord(message.info) || !isRecord(message.info.time))
|
|
25226
|
+
return 0;
|
|
25227
|
+
return typeof message.info.time.created === "number" ? message.info.time.created : 0;
|
|
25228
|
+
}
|
|
24458
25229
|
async function runValidatedHistorianPass(args) {
|
|
24459
25230
|
const firstRun = await runHistorianPrompt({
|
|
24460
25231
|
...args,
|
|
@@ -24520,6 +25291,7 @@ async function runEditorPassOrFallback(args) {
|
|
|
24520
25291
|
sessionLog(args.parentSessionId, "historian two-pass: running editor on draft");
|
|
24521
25292
|
const editorRun = await runHistorianPrompt({
|
|
24522
25293
|
client: args.client,
|
|
25294
|
+
db: args.db,
|
|
24523
25295
|
parentSessionId: args.parentSessionId,
|
|
24524
25296
|
sessionDirectory: args.sessionDirectory,
|
|
24525
25297
|
prompt: buildHistorianEditorPrompt(args.draftXml),
|
|
@@ -24546,6 +25318,7 @@ async function runEditorPassOrFallback(args) {
|
|
|
24546
25318
|
async function runHistorianPrompt(args) {
|
|
24547
25319
|
const {
|
|
24548
25320
|
client,
|
|
25321
|
+
db,
|
|
24549
25322
|
parentSessionId,
|
|
24550
25323
|
sessionDirectory,
|
|
24551
25324
|
prompt,
|
|
@@ -24579,12 +25352,12 @@ async function runHistorianPrompt(args) {
|
|
|
24579
25352
|
};
|
|
24580
25353
|
try {
|
|
24581
25354
|
sessionLog(parentSessionId, `historian: creating child session (agent=${agentId}, model=${modelOverride ? `${modelOverride.providerID}/${modelOverride.modelID}` : `agent:${agentId}`})`);
|
|
24582
|
-
const createResponse = await
|
|
24583
|
-
|
|
24584
|
-
|
|
24585
|
-
|
|
24586
|
-
|
|
24587
|
-
|
|
25355
|
+
const createResponse = await createChildSessionWithFence({
|
|
25356
|
+
client,
|
|
25357
|
+
db,
|
|
25358
|
+
parentSessionId,
|
|
25359
|
+
title: "magic-context-compartment",
|
|
25360
|
+
directory: sessionDirectory
|
|
24588
25361
|
});
|
|
24589
25362
|
const createdSession = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
|
|
24590
25363
|
agentSessionId = typeof createdSession?.id === "string" ? createdSession.id : null;
|
|
@@ -24632,7 +25405,18 @@ async function runHistorianPrompt(args) {
|
|
|
24632
25405
|
preferResponseOnMissingData: true
|
|
24633
25406
|
});
|
|
24634
25407
|
const invocationId = recordInvocation5({ status: "completed", messages });
|
|
24635
|
-
const
|
|
25408
|
+
const lengthCapped = hasLengthCappedOutput(messages);
|
|
25409
|
+
const textResult = extractLatestAssistantText(messages);
|
|
25410
|
+
const reasoningResult = textResult ? null : extractLatestHistorianReasoning(messages);
|
|
25411
|
+
if (!textResult && reasoningResult && lengthCapped) {
|
|
25412
|
+
const outputTokens = sumTokensFromChildMessages(messages).output;
|
|
25413
|
+
return {
|
|
25414
|
+
ok: false,
|
|
25415
|
+
error: `historian output length-capped at ${outputTokens} tokens (all reasoning, no text) — set historian.maxTokens or route historian.model to a low-reasoning lane/variant`,
|
|
25416
|
+
invocationId: invocationId ?? undefined
|
|
25417
|
+
};
|
|
25418
|
+
}
|
|
25419
|
+
const result = textResult ?? reasoningResult;
|
|
24636
25420
|
if (!result) {
|
|
24637
25421
|
return {
|
|
24638
25422
|
ok: false,
|
|
@@ -24684,6 +25468,7 @@ async function runFallbackHistorianPass(args) {
|
|
|
24684
25468
|
args.callbacks?.onModelFallback?.(modelId, i + 1, chain.length);
|
|
24685
25469
|
const fallbackRun = await runHistorianPrompt({
|
|
24686
25470
|
client: args.client,
|
|
25471
|
+
db: args.db,
|
|
24687
25472
|
parentSessionId: args.parentSessionId,
|
|
24688
25473
|
sessionDirectory: args.sessionDirectory,
|
|
24689
25474
|
prompt: args.prompt,
|
|
@@ -24779,116 +25564,6 @@ function sanitizeDumpName(value) {
|
|
|
24779
25564
|
return value.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
24780
25565
|
}
|
|
24781
25566
|
|
|
24782
|
-
// ../plugin/src/hooks/magic-context/send-session-notification.ts
|
|
24783
|
-
function hasNotificationSessionClient(client) {
|
|
24784
|
-
if (client === null || typeof client !== "object")
|
|
24785
|
-
return false;
|
|
24786
|
-
const candidate = client;
|
|
24787
|
-
if (candidate.session === undefined)
|
|
24788
|
-
return true;
|
|
24789
|
-
if (candidate.session === null || typeof candidate.session !== "object")
|
|
24790
|
-
return false;
|
|
24791
|
-
const session = candidate.session;
|
|
24792
|
-
return (session.prompt === undefined || typeof session.prompt === "function") && (session.promptAsync === undefined || typeof session.promptAsync === "function");
|
|
24793
|
-
}
|
|
24794
|
-
function inferToastVariant(text) {
|
|
24795
|
-
const lower = text.toLowerCase();
|
|
24796
|
-
if (lower.includes("error") || lower.includes("failed") || lower.includes("alert"))
|
|
24797
|
-
return "error";
|
|
24798
|
-
if (lower.includes("warning") || lower.includes("⚠"))
|
|
24799
|
-
return "warning";
|
|
24800
|
-
if (lower.includes("complete") || lower.includes("success") || lower.includes("✓") || lower.includes("finished"))
|
|
24801
|
-
return "success";
|
|
24802
|
-
return "info";
|
|
24803
|
-
}
|
|
24804
|
-
function extractToastTitle(text) {
|
|
24805
|
-
const headingMatch = text.match(/^#+\s+(.+)/m);
|
|
24806
|
-
if (headingMatch)
|
|
24807
|
-
return headingMatch[1].trim();
|
|
24808
|
-
const firstLine = text.split(`
|
|
24809
|
-
`)[0].trim();
|
|
24810
|
-
if (firstLine.length <= 80)
|
|
24811
|
-
return firstLine;
|
|
24812
|
-
return "Magic Context";
|
|
24813
|
-
}
|
|
24814
|
-
async function sendIgnoredMessage(client, sessionId, text, params, forcePersist = false) {
|
|
24815
|
-
const title = extractToastTitle(text);
|
|
24816
|
-
const message = text.length > 200 ? `${text.slice(0, 200)}…` : text;
|
|
24817
|
-
const toastVariant = inferToastVariant(text);
|
|
24818
|
-
const duration = params.toastDurationMs ?? 5000;
|
|
24819
|
-
const { isTuiConnected: checkTui } = await import("./rpc-notifications-td0amw6w.js");
|
|
24820
|
-
if (!forcePersist && checkTui(sessionId)) {
|
|
24821
|
-
try {
|
|
24822
|
-
const { pushNotification } = await import("./rpc-notifications-td0amw6w.js");
|
|
24823
|
-
pushNotification("toast", {
|
|
24824
|
-
title,
|
|
24825
|
-
message,
|
|
24826
|
-
variant: toastVariant,
|
|
24827
|
-
duration
|
|
24828
|
-
}, sessionId);
|
|
24829
|
-
return "sent";
|
|
24830
|
-
} catch {
|
|
24831
|
-
sessionLog(sessionId, "TUI RPC toast enqueue failed, falling back to ignored message");
|
|
24832
|
-
}
|
|
24833
|
-
}
|
|
24834
|
-
const { waitForSafeNotificationTarget } = await import("./safe-notification-target-ng8ygena.js");
|
|
24835
|
-
if (await waitForSafeNotificationTarget(client, sessionId) === "skip") {
|
|
24836
|
-
sessionLog(sessionId, "notification skipped (session not titled yet)");
|
|
24837
|
-
return "skipped";
|
|
24838
|
-
}
|
|
24839
|
-
if (!hasNotificationSessionClient(client)) {
|
|
24840
|
-
sessionLog(sessionId, "session prompt API unavailable for notification");
|
|
24841
|
-
return "failed";
|
|
24842
|
-
}
|
|
24843
|
-
const c = client;
|
|
24844
|
-
let agent = params.agent || undefined;
|
|
24845
|
-
let variant = params.variant || undefined;
|
|
24846
|
-
let model = params.providerId && params.modelId ? { providerID: params.providerId, modelID: params.modelId } : undefined;
|
|
24847
|
-
if (!agent || !model || !variant) {
|
|
24848
|
-
try {
|
|
24849
|
-
const { resolvePromptContext } = await import("./prompt-context-602yq94t.js");
|
|
24850
|
-
const resolved = await resolvePromptContext(client, sessionId);
|
|
24851
|
-
if (resolved) {
|
|
24852
|
-
agent = agent ?? resolved.agent;
|
|
24853
|
-
model = model ?? resolved.model;
|
|
24854
|
-
variant = variant ?? resolved.variant;
|
|
24855
|
-
}
|
|
24856
|
-
} catch {}
|
|
24857
|
-
}
|
|
24858
|
-
const input = {
|
|
24859
|
-
path: { id: sessionId },
|
|
24860
|
-
body: {
|
|
24861
|
-
noReply: true,
|
|
24862
|
-
agent,
|
|
24863
|
-
model,
|
|
24864
|
-
variant,
|
|
24865
|
-
parts: [
|
|
24866
|
-
{
|
|
24867
|
-
type: "text",
|
|
24868
|
-
text,
|
|
24869
|
-
ignored: true
|
|
24870
|
-
}
|
|
24871
|
-
]
|
|
24872
|
-
}
|
|
24873
|
-
};
|
|
24874
|
-
try {
|
|
24875
|
-
if (typeof c.session?.prompt === "function") {
|
|
24876
|
-
await Promise.resolve(c.session.prompt(input));
|
|
24877
|
-
return "sent";
|
|
24878
|
-
}
|
|
24879
|
-
if (typeof c.session?.promptAsync === "function") {
|
|
24880
|
-
await c.session.promptAsync(input);
|
|
24881
|
-
return "sent";
|
|
24882
|
-
}
|
|
24883
|
-
sessionLog(sessionId, "session prompt API unavailable for notification");
|
|
24884
|
-
return "failed";
|
|
24885
|
-
} catch (error) {
|
|
24886
|
-
const msg = getErrorMessage(error);
|
|
24887
|
-
sessionLog(sessionId, "failed to send notification:", msg);
|
|
24888
|
-
return "failed";
|
|
24889
|
-
}
|
|
24890
|
-
}
|
|
24891
|
-
|
|
24892
25567
|
// ../plugin/src/hooks/magic-context/compartment-runner-incremental.ts
|
|
24893
25568
|
var HISTORIAN_ALERT_COOLDOWN_MS2 = 60 * 1000;
|
|
24894
25569
|
var lastHistorianAlertBySession2 = new Map;
|
|
@@ -25106,6 +25781,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
|
|
|
25106
25781
|
emitProgress(`Running historian (pass ${passCount + 1})…`);
|
|
25107
25782
|
const validatedPass = await runValidatedHistorianPass({
|
|
25108
25783
|
client,
|
|
25784
|
+
db,
|
|
25109
25785
|
parentSessionId: sessionId,
|
|
25110
25786
|
sessionDirectory,
|
|
25111
25787
|
prompt,
|
|
@@ -25525,6 +26201,7 @@ ${chunk.text}`,
|
|
|
25525
26201
|
Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${chunk.startIndex}-${chunk.endIndex}.`, notifParams());
|
|
25526
26202
|
const validatedPass = await runValidatedHistorianPass({
|
|
25527
26203
|
client,
|
|
26204
|
+
db,
|
|
25528
26205
|
parentSessionId: sessionId,
|
|
25529
26206
|
sessionDirectory,
|
|
25530
26207
|
prompt,
|
|
@@ -25880,8 +26557,8 @@ function renderStatusText(ctx, db, sessionId) {
|
|
|
25880
26557
|
const inputTokens = typeof usage?.tokens === "number" ? usage.tokens : undefined;
|
|
25881
26558
|
const pct = typeof usage?.percent === "number" ? usage.percent : undefined;
|
|
25882
26559
|
const meta = readSessionMetaStatus(db, sessionId);
|
|
25883
|
-
const
|
|
25884
|
-
return `mc: ${inputTokens === undefined ? "--" : fmt(inputTokens)} (${pct === undefined ? "--" : `${Math.round(pct)}%`}) · ${
|
|
26560
|
+
const state2 = renderHistorianState(meta, recompSessions.has(sessionId));
|
|
26561
|
+
return `mc: ${inputTokens === undefined ? "--" : fmt(inputTokens)} (${pct === undefined ? "--" : `${Math.round(pct)}%`}) · ${state2}`;
|
|
25885
26562
|
}
|
|
25886
26563
|
function renderHistorianState(meta, recompActive) {
|
|
25887
26564
|
const failureCount = meta?.historian_failure_count ?? 0;
|
|
@@ -25984,6 +26661,14 @@ No active Pi session is available.`,
|
|
|
25984
26661
|
return;
|
|
25985
26662
|
}
|
|
25986
26663
|
const currentDeps = deps.resolveRuntimeDeps?.(ctx) ?? deps;
|
|
26664
|
+
if (currentDeps.compactionOff) {
|
|
26665
|
+
sendCtxStatusMessage(pi, {
|
|
26666
|
+
title: "/ctx-recomp",
|
|
26667
|
+
text: COMPACTION_OFF_COMMAND_UNAVAILABLE,
|
|
26668
|
+
level: "warning"
|
|
26669
|
+
});
|
|
26670
|
+
return;
|
|
26671
|
+
}
|
|
25987
26672
|
const parsed = parseRecompArgs(args);
|
|
25988
26673
|
if (parsed.kind === "error") {
|
|
25989
26674
|
sendCtxStatusMessage(pi, {
|
|
@@ -26336,7 +27021,7 @@ function contextualizeUpgradeReason(reason) {
|
|
|
26336
27021
|
// src/pi-memory-migration.ts
|
|
26337
27022
|
var MIGRATION_SYSTEM_PROMPT2 = "You re-organize a software project's long-term memory into a stricter taxonomy. " + "Follow the user instructions exactly. Output ONLY the requested XML blocks, nothing else.";
|
|
26338
27023
|
async function runPiMemoryMigration(deps) {
|
|
26339
|
-
const projectPath = resolveProjectIdentityForSession(deps.directory);
|
|
27024
|
+
const projectPath = resolveProjectIdentityForSession(deps.directory, deps.allowHomeProject);
|
|
26340
27025
|
if (!projectPath) {
|
|
26341
27026
|
return {
|
|
26342
27027
|
ran: false,
|
|
@@ -26451,6 +27136,14 @@ No active Pi session is available.`,
|
|
|
26451
27136
|
return;
|
|
26452
27137
|
}
|
|
26453
27138
|
const currentDeps = deps.resolveRuntimeDeps?.(ctx) ?? deps;
|
|
27139
|
+
if (currentDeps.compactionOff) {
|
|
27140
|
+
sendCtxStatusMessage(pi, {
|
|
27141
|
+
title: "/ctx-session-upgrade",
|
|
27142
|
+
text: COMPACTION_OFF_COMMAND_UNAVAILABLE,
|
|
27143
|
+
level: "warning"
|
|
27144
|
+
});
|
|
27145
|
+
return;
|
|
27146
|
+
}
|
|
26454
27147
|
if (!currentDeps.historianModel) {
|
|
26455
27148
|
sendCtxStatusMessage(pi, {
|
|
26456
27149
|
title: "/ctx-session-upgrade",
|
|
@@ -26497,6 +27190,7 @@ An upgrade or recomp is already running for this session in the background. Wait
|
|
|
26497
27190
|
timeoutMs: currentDeps.historianTimeoutMs,
|
|
26498
27191
|
thinkingLevel: currentDeps.historianThinkingLevel,
|
|
26499
27192
|
directory: ctx.cwd,
|
|
27193
|
+
allowHomeProject: currentDeps.allowHomeProject,
|
|
26500
27194
|
sessionId,
|
|
26501
27195
|
userMemoriesEnabled: currentDeps.userMemoriesEnabled,
|
|
26502
27196
|
language: currentDeps.language
|
|
@@ -26507,7 +27201,7 @@ An upgrade or recomp is already running for this session in the background. Wait
|
|
|
26507
27201
|
}
|
|
26508
27202
|
};
|
|
26509
27203
|
if (upgradableCount === 0) {
|
|
26510
|
-
const projectPath = resolveProjectIdentityForSession(ctx.cwd);
|
|
27204
|
+
const projectPath = resolveProjectIdentityForSession(ctx.cwd, currentDeps.allowHomeProject);
|
|
26511
27205
|
if (!projectPath)
|
|
26512
27206
|
return;
|
|
26513
27207
|
const migrationPending = migrationEnabled && !isMemoryMigrationDone(currentDeps.db, projectPath);
|
|
@@ -26683,7 +27377,7 @@ function formatExecuteThreshold(detail, contextLimit) {
|
|
|
26683
27377
|
}
|
|
26684
27378
|
return `${percentage}%${clampNote}`;
|
|
26685
27379
|
}
|
|
26686
|
-
function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit) {
|
|
27380
|
+
function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit, dreamer) {
|
|
26687
27381
|
const thresholdDetail = resolveExecuteThresholdDetail(executeThresholdPercentageConfig, liveModelKey, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, {
|
|
26688
27382
|
tokensConfig: executeThresholdTokens,
|
|
26689
27383
|
contextLimit,
|
|
@@ -26728,8 +27422,8 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
|
|
|
26728
27422
|
"### Cache TTL",
|
|
26729
27423
|
`- Configured: ${meta.cacheTtl}`,
|
|
26730
27424
|
`- Last response: ${meta.lastResponseTime > 0 ? `${Math.round(elapsed / 1000)}s ago` : "never"}`,
|
|
26731
|
-
`- Remaining: ${cacheExpired ? "expired" : `${Math.round(remainingMs / 1000)}s`}`,
|
|
26732
|
-
`- Queue will auto-execute: ${cacheExpired ? "yes (cache expired)" : `when TTL expires or context >= ${executeThresholdPercentage}%`}`,
|
|
27425
|
+
`- Remaining: ${cacheExpired ? "expired" : ttlMs === Number.POSITIVE_INFINITY ? "never expires (always-warm lane)" : `${Math.round(remainingMs / 1000)}s`}`,
|
|
27426
|
+
`- Queue will auto-execute: ${cacheExpired ? "yes (cache expired)" : ttlMs === Number.POSITIVE_INFINITY ? `when context >= ${executeThresholdPercentage}%` : `when TTL expires or context >= ${executeThresholdPercentage}%`}`,
|
|
26733
27427
|
"",
|
|
26734
27428
|
"### Execute Threshold",
|
|
26735
27429
|
`- Execute threshold: ${formatExecuteThreshold(thresholdDetail, displayContextLimit)}`,
|
|
@@ -26738,6 +27432,11 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
|
|
|
26738
27432
|
`**Protected tags:** ${protectedTags}`,
|
|
26739
27433
|
`**Subagent session:** ${meta.isSubagent}`
|
|
26740
27434
|
];
|
|
27435
|
+
if (dreamer?.backlog && Object.keys(dreamer.backlog).length > 0) {
|
|
27436
|
+
lines.push("", "### Dreamer", ...dreamer.progress ? [
|
|
27437
|
+
`- Running: ${dreamer.progress.task} — ${dreamer.progress.processed}/${dreamer.progress.total} processed`
|
|
27438
|
+
] : [], ...formatDreamTaskBacklogs(dreamer.backlog).split("\\n"));
|
|
27439
|
+
}
|
|
26741
27440
|
if (meta.lastContextPercentage > 0 || meta.lastInputTokens > 0) {
|
|
26742
27441
|
lines.push("", "### Context Usage", `- Last percentage: ${meta.lastContextPercentage.toFixed(1)}%`, `- Last input tokens: ${meta.lastInputTokens.toLocaleString()}`, `- Resolved context limit: ${displayContextLimit > 0 ? displayContextLimit.toLocaleString() : "unknown"}`, `- Proactive compartment evaluation: ${proactiveCompartmentTrigger}%`, `- Post-drop target for historian: ${(executeThresholdPercentage * POST_DROP_TARGET_RATIO).toFixed(0)}% (${executeThresholdPercentage}% * ${POST_DROP_TARGET_RATIO})`, `- Commit cluster trigger: ${commitClusterTrigger?.enabled !== false ? `enabled (min ${commitClusterTrigger?.min_clusters ?? 3} clusters)` : "disabled"}, tail-size trigger: > 3x compartment budget`);
|
|
26743
27442
|
}
|
|
@@ -26760,6 +27459,12 @@ ${c.content}
|
|
|
26760
27459
|
lines.push(`- §${op.tagId}§ → ${op.operation}`);
|
|
26761
27460
|
}
|
|
26762
27461
|
}
|
|
27462
|
+
if (dreamer?.backlog && Object.keys(dreamer.backlog).length > 0) {
|
|
27463
|
+
lines.push("", "### Dreamer Backlog", formatDreamTaskBacklogs(dreamer.backlog));
|
|
27464
|
+
}
|
|
27465
|
+
if (dreamer?.progress) {
|
|
27466
|
+
lines.push("", "### Dreamer Progress", `- ${dreamer.progress.task}: ${dreamer.progress.processed}/${dreamer.progress.total} processed this run`);
|
|
27467
|
+
}
|
|
26763
27468
|
return lines.join(`
|
|
26764
27469
|
`);
|
|
26765
27470
|
} catch (error) {
|
|
@@ -26834,7 +27539,7 @@ ${c.content}
|
|
|
26834
27539
|
// package.json
|
|
26835
27540
|
var package_default = {
|
|
26836
27541
|
name: "@cortexkit/pi-magic-context",
|
|
26837
|
-
version: "0.
|
|
27542
|
+
version: "0.34.1",
|
|
26838
27543
|
type: "module",
|
|
26839
27544
|
description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
|
|
26840
27545
|
main: "dist/index.js",
|
|
@@ -27004,7 +27709,7 @@ function renderInner(s, theme, innerWidth) {
|
|
|
27004
27709
|
lines.push(`Upgrade: ${theme.fg("accent", "up to date")}`);
|
|
27005
27710
|
}
|
|
27006
27711
|
lines.push(`Pending drops: ${s.pendingOpsCount}`);
|
|
27007
|
-
lines.push(`Cache TTL: ${s.cacheTtl} · last response ${s.lastResponseTime > 0 ? `${Math.round((Date.now() - s.lastResponseTime) / 1000)}s ago` : "never"} · ${s.cacheExpired ? theme.fg("warning", "expired") : `${Math.round(s.cacheRemainingMs / 1000)}s remaining`}`);
|
|
27712
|
+
lines.push(`Cache TTL: ${s.cacheTtl} · last response ${s.lastResponseTime > 0 ? `${Math.round((Date.now() - s.lastResponseTime) / 1000)}s ago` : "never"} · ${s.cacheExpired ? theme.fg("warning", "expired") : s.cacheRemainingMs === Number.POSITIVE_INFINITY ? "never expires (always-warm lane)" : `${Math.round(s.cacheRemainingMs / 1000)}s remaining`}`);
|
|
27008
27713
|
lines.push("");
|
|
27009
27714
|
lines.push(theme.fg("muted", "Tags"));
|
|
27010
27715
|
lines.push(`Active ${s.activeTags} (~${formatBytes(s.activeBytes)}) · Dropped ${s.droppedTags} · Total ${s.totalTags}`);
|
|
@@ -27046,8 +27751,18 @@ function buildPiStatusDetail(pi, ctx, deps, sessionId) {
|
|
|
27046
27751
|
const usage = ctx.getContextUsage?.();
|
|
27047
27752
|
const meta = getOrCreateSessionMeta(deps.db, sessionId);
|
|
27048
27753
|
const inputTokens = typeof usage?.tokens === "number" ? usage.tokens : meta.lastInputTokens;
|
|
27049
|
-
|
|
27050
|
-
|
|
27754
|
+
let detectedContextLimit;
|
|
27755
|
+
try {
|
|
27756
|
+
const detected = getOverflowState(deps.db, sessionId).detectedContextLimit;
|
|
27757
|
+
if (detected > 0)
|
|
27758
|
+
detectedContextLimit = detected;
|
|
27759
|
+
} catch {}
|
|
27760
|
+
const contextLimit = resolvePiUsableContextLimit({
|
|
27761
|
+
rawContextWindow: usage?.contextWindow ?? ctx.model?.contextWindow,
|
|
27762
|
+
model: ctx.model,
|
|
27763
|
+
detectedContextLimit
|
|
27764
|
+
}) ?? (meta.lastContextPercentage > 0 ? Math.round(inputTokens / (meta.lastContextPercentage / 100)) : 0);
|
|
27765
|
+
const usagePercentage = contextLimit > 0 && inputTokens > 0 ? inputTokens / contextLimit * 100 : meta.lastContextPercentage;
|
|
27051
27766
|
const compartments = getCompartments(deps.db, sessionId);
|
|
27052
27767
|
const metaRow = readSessionMetaRow(deps.db, sessionId);
|
|
27053
27768
|
const memoryBlockCount = Number(metaRow?.memory_block_count ?? 0);
|
|
@@ -27095,9 +27810,15 @@ ${safeStringify(tool.parameters)}`);
|
|
|
27095
27810
|
sessionId
|
|
27096
27811
|
});
|
|
27097
27812
|
const cacheTtl = meta.cacheTtl || "5m";
|
|
27098
|
-
|
|
27813
|
+
let cacheTtlMs;
|
|
27814
|
+
try {
|
|
27815
|
+
cacheTtlMs = parseCacheTtl(cacheTtl);
|
|
27816
|
+
} catch {
|
|
27817
|
+
cacheTtlMs = 5 * 60 * 1000;
|
|
27818
|
+
}
|
|
27819
|
+
const neverExpires = cacheTtlMs === Number.POSITIVE_INFINITY;
|
|
27099
27820
|
const elapsed = meta.lastResponseTime > 0 ? Date.now() - meta.lastResponseTime : 0;
|
|
27100
|
-
const cacheRemainingMs = meta.lastResponseTime > 0 ? Math.max(0, cacheTtlMs - elapsed) : cacheTtlMs;
|
|
27821
|
+
const cacheRemainingMs = neverExpires ? Number.POSITIVE_INFINITY : meta.lastResponseTime > 0 ? Math.max(0, cacheTtlMs - elapsed) : cacheTtlMs;
|
|
27101
27822
|
const cacheExpired = meta.lastResponseTime > 0 && cacheRemainingMs === 0;
|
|
27102
27823
|
const historyBlockTokens = compartmentTokens + factTokens;
|
|
27103
27824
|
const historyBudgetPercentage = deps.historyBudgetPercentage ?? 0.15;
|
|
@@ -27252,22 +27973,6 @@ function readPendingOpsCount(db, sessionId) {
|
|
|
27252
27973
|
return 0;
|
|
27253
27974
|
}
|
|
27254
27975
|
}
|
|
27255
|
-
function parseTtlString(ttl) {
|
|
27256
|
-
const match = ttl.match(/^(\d+)(s|m|h)$/);
|
|
27257
|
-
if (!match)
|
|
27258
|
-
return 5 * 60 * 1000;
|
|
27259
|
-
const val = Number.parseInt(match[1] ?? "5", 10);
|
|
27260
|
-
switch (match[2]) {
|
|
27261
|
-
case "s":
|
|
27262
|
-
return val * 1000;
|
|
27263
|
-
case "m":
|
|
27264
|
-
return val * 60 * 1000;
|
|
27265
|
-
case "h":
|
|
27266
|
-
return val * 3600 * 1000;
|
|
27267
|
-
default:
|
|
27268
|
-
return 5 * 60 * 1000;
|
|
27269
|
-
}
|
|
27270
|
-
}
|
|
27271
27976
|
function safeRead(fn, fallback) {
|
|
27272
27977
|
try {
|
|
27273
27978
|
return fn();
|
|
@@ -27331,7 +28036,20 @@ No active Pi session is available.`,
|
|
|
27331
28036
|
}
|
|
27332
28037
|
const usage = ctx.getContextUsage?.();
|
|
27333
28038
|
const modelKey = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
|
|
27334
|
-
|
|
28039
|
+
let detectedContextLimit;
|
|
28040
|
+
try {
|
|
28041
|
+
const detected = getOverflowState(currentDeps.db, sessionId).detectedContextLimit;
|
|
28042
|
+
if (detected > 0)
|
|
28043
|
+
detectedContextLimit = detected;
|
|
28044
|
+
} catch {}
|
|
28045
|
+
const usableContextLimit = resolvePiUsableContextLimit({
|
|
28046
|
+
rawContextWindow: usage?.contextWindow ?? ctx.model?.contextWindow,
|
|
28047
|
+
model: ctx.model,
|
|
28048
|
+
detectedContextLimit
|
|
28049
|
+
});
|
|
28050
|
+
const statusText = executeStatus(currentDeps.db, sessionId, currentDeps.protectedTags ?? 20, currentDeps.executeThresholdPercentage, modelKey, currentDeps.historyBudgetPercentage, currentDeps.commitClusterTrigger, currentDeps.executeThresholdTokens, usableContextLimit, {
|
|
28051
|
+
backlog: getDreamTaskBacklogs(currentDeps.db, currentDeps.projectIdentity, CANONICAL_DREAM_TASKS)
|
|
28052
|
+
});
|
|
27335
28053
|
const details = buildStatusDetails(currentDeps, sessionId);
|
|
27336
28054
|
sendCtxStatusMessage(pi, { title: "/ctx-status", text: statusText, level: "info" }, details);
|
|
27337
28055
|
} catch (error) {
|
|
@@ -27373,6 +28091,7 @@ function buildStatusDetails(deps, sessionId) {
|
|
|
27373
28091
|
dreamer: {
|
|
27374
28092
|
enabled: deps.dreamer?.runnable === true,
|
|
27375
28093
|
scheduleSummary: deps.dreamer?.scheduleSummary ?? null,
|
|
28094
|
+
backlog: getDreamTaskBacklogs(deps.db, deps.projectIdentity, CANONICAL_DREAM_TASKS),
|
|
27376
28095
|
lastRunAt: getMostRecentTaskRunAt(deps.db, deps.projectIdentity)
|
|
27377
28096
|
},
|
|
27378
28097
|
historian: readHistorianState(deps.db, sessionId, meta)
|
|
@@ -27433,6 +28152,14 @@ No active Pi session is available.`,
|
|
|
27433
28152
|
return;
|
|
27434
28153
|
}
|
|
27435
28154
|
const currentDeps = deps.resolveRuntimeDeps?.(ctx) ?? deps;
|
|
28155
|
+
if (currentDeps.compactionOff) {
|
|
28156
|
+
sendCtxStatusMessage(pi, {
|
|
28157
|
+
title: "/ctx-wrapup",
|
|
28158
|
+
text: COMPACTION_OFF_COMMAND_UNAVAILABLE,
|
|
28159
|
+
level: "warning"
|
|
28160
|
+
});
|
|
28161
|
+
return;
|
|
28162
|
+
}
|
|
27436
28163
|
const sessionMeta = getOrCreateSessionMeta(currentDeps.db, sessionId);
|
|
27437
28164
|
if (sessionMeta.isSubagent) {
|
|
27438
28165
|
sendCtxStatusMessage(pi, {
|
|
@@ -27485,7 +28212,7 @@ async function runPiWrapup(pi, deps, ctx, sessionId, messagesToKeep) {
|
|
|
27485
28212
|
const unregister = setRawMessageProvider(sessionId, provider);
|
|
27486
28213
|
let holderId = "";
|
|
27487
28214
|
try {
|
|
27488
|
-
const contextLimit = resolvePiContextLimit(ctx);
|
|
28215
|
+
const contextLimit = resolvePiContextLimit(ctx, deps.db, sessionId);
|
|
27489
28216
|
const modelKey = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
|
|
27490
28217
|
const executeThresholdPercentage = resolveExecuteThreshold(deps.executeThresholdPercentage ?? 65, modelKey, 65, {
|
|
27491
28218
|
tokensConfig: deps.executeThresholdTokens,
|
|
@@ -27714,15 +28441,19 @@ Wrapped up ${messagesWrapped} messages into ${compartmentsCreated} compartments.
|
|
|
27714
28441
|
unregister();
|
|
27715
28442
|
}
|
|
27716
28443
|
}
|
|
27717
|
-
function resolvePiContextLimit(ctx) {
|
|
28444
|
+
function resolvePiContextLimit(ctx, db, sessionId) {
|
|
27718
28445
|
const usage = ctx.getContextUsage?.();
|
|
27719
|
-
|
|
27720
|
-
|
|
27721
|
-
|
|
27722
|
-
|
|
27723
|
-
|
|
27724
|
-
}
|
|
27725
|
-
return
|
|
28446
|
+
let detectedContextLimit;
|
|
28447
|
+
try {
|
|
28448
|
+
const detected = getOverflowState(db, sessionId).detectedContextLimit;
|
|
28449
|
+
if (detected > 0)
|
|
28450
|
+
detectedContextLimit = detected;
|
|
28451
|
+
} catch {}
|
|
28452
|
+
return resolvePiUsableContextLimit({
|
|
28453
|
+
rawContextWindow: usage?.contextWindow ?? ctx.model?.contextWindow,
|
|
28454
|
+
model: ctx.model,
|
|
28455
|
+
detectedContextLimit
|
|
28456
|
+
}) ?? 128000;
|
|
27726
28457
|
}
|
|
27727
28458
|
async function acquireCompartmentLeaseEventually(db, sessionId, renewWrapupMarker, maxWaitMs) {
|
|
27728
28459
|
const waitStartedAt = Date.now();
|
|
@@ -27742,15 +28473,15 @@ async function acquireCompartmentLeaseEventually(db, sessionId, renewWrapupMarke
|
|
|
27742
28473
|
function estimateChunks(tokens, chunkTokens) {
|
|
27743
28474
|
return Math.max(1, Math.ceil(Math.max(0, tokens) / Math.max(1, chunkTokens)));
|
|
27744
28475
|
}
|
|
27745
|
-
function formatExistingWrapup(
|
|
27746
|
-
if (!
|
|
28476
|
+
function formatExistingWrapup(state2) {
|
|
28477
|
+
if (!state2) {
|
|
27747
28478
|
return `## Magic Wrapup — Skipped
|
|
27748
28479
|
|
|
27749
28480
|
Another /ctx-wrapup is already compacting this session. Wait for it to finish, then try again.`;
|
|
27750
28481
|
}
|
|
27751
28482
|
return `## Magic Wrapup — Skipped
|
|
27752
28483
|
|
|
27753
|
-
Another /ctx-wrapup is already compacting this session (chunk ${
|
|
28484
|
+
Another /ctx-wrapup is already compacting this session (chunk ${state2.chunkIndex}/${Math.max(state2.chunkIndex, state2.expectedChunks)}, wrapped through message ${state2.lastCompartmentEnd}). Wait for it to finish, then try again.`;
|
|
27754
28485
|
}
|
|
27755
28486
|
function readBranchEntries(ctx) {
|
|
27756
28487
|
const getBranch = ctx.sessionManager.getBranch;
|
|
@@ -27874,15 +28605,43 @@ function stripTagPrefixFromAssistantMessage(message) {
|
|
|
27874
28605
|
|
|
27875
28606
|
// src/index.ts
|
|
27876
28607
|
var PREFIX2 = "[magic-context][pi]";
|
|
27877
|
-
|
|
28608
|
+
var PI_ACTIVE_LATCH = Symbol.for("magic-context.pi.active");
|
|
28609
|
+
function isPiMagicContextActiveInProcess() {
|
|
28610
|
+
return globalThis[PI_ACTIVE_LATCH] === true;
|
|
28611
|
+
}
|
|
28612
|
+
function markPiMagicContextActive() {
|
|
28613
|
+
globalThis[PI_ACTIVE_LATCH] = true;
|
|
28614
|
+
}
|
|
28615
|
+
function clearPiMagicContextActive() {
|
|
28616
|
+
try {
|
|
28617
|
+
delete globalThis[PI_ACTIVE_LATCH];
|
|
28618
|
+
} catch {
|
|
28619
|
+
globalThis[PI_ACTIVE_LATCH] = undefined;
|
|
28620
|
+
}
|
|
28621
|
+
}
|
|
28622
|
+
function resolveCurrentProject(ctx, allowHomeProject = false) {
|
|
27878
28623
|
const projectDir = ctx.cwd;
|
|
27879
|
-
const projectIdentity = resolveProjectIdentityForSession(projectDir) ?? "";
|
|
28624
|
+
const projectIdentity = resolveProjectIdentityForSession(projectDir, allowHomeProject) ?? "";
|
|
27880
28625
|
return { projectDir, projectIdentity };
|
|
27881
28626
|
}
|
|
27882
28627
|
function signalPiDeferredCompactionMarkerDrain(sessionId) {
|
|
27883
28628
|
signalPiDeferredHistoryRefresh(sessionId);
|
|
27884
28629
|
signalPiDeferredMaterialization(sessionId);
|
|
27885
28630
|
}
|
|
28631
|
+
async function handlePiSessionBeforeCompact(args) {
|
|
28632
|
+
try {
|
|
28633
|
+
const sessionId = args.ctx.sessionManager?.getSessionId?.();
|
|
28634
|
+
if (typeof sessionId === "string" && sessionId.length > 0) {
|
|
28635
|
+
clearPiM0Cache(args.db, sessionId, "session_before_compact");
|
|
28636
|
+
}
|
|
28637
|
+
} catch {}
|
|
28638
|
+
if (args.compactionOff) {
|
|
28639
|
+
info("session_before_compact: native Pi compaction proceeds (compaction-off mode)");
|
|
28640
|
+
return;
|
|
28641
|
+
}
|
|
28642
|
+
info("session_before_compact: cancelling — magic-context owns compaction");
|
|
28643
|
+
return { cancel: true };
|
|
28644
|
+
}
|
|
27886
28645
|
function persistPiMessageEndModelMeta(args) {
|
|
27887
28646
|
if (!args.message || typeof args.message !== "object")
|
|
27888
28647
|
return;
|
|
@@ -27988,7 +28747,10 @@ var __test = {
|
|
|
27988
28747
|
logPiConfigLoad,
|
|
27989
28748
|
resetLoggedPiConfigDirs() {
|
|
27990
28749
|
loggedPiConfigDirs.clear();
|
|
27991
|
-
}
|
|
28750
|
+
},
|
|
28751
|
+
isPiMagicContextActiveInProcess,
|
|
28752
|
+
markPiMagicContextActive,
|
|
28753
|
+
clearPiMagicContextActive
|
|
27992
28754
|
};
|
|
27993
28755
|
function formatTokens(value) {
|
|
27994
28756
|
return value.toLocaleString();
|
|
@@ -28004,23 +28766,28 @@ function getPiMessageModel(message) {
|
|
|
28004
28766
|
};
|
|
28005
28767
|
}
|
|
28006
28768
|
function resolvePiPressureContextLimit(args) {
|
|
28007
|
-
let
|
|
28769
|
+
let detectedContextLimit;
|
|
28008
28770
|
try {
|
|
28009
28771
|
const overflowState = getOverflowState(args.db, args.sessionId);
|
|
28010
28772
|
if (overflowState.detectedContextLimit > 0) {
|
|
28011
|
-
|
|
28773
|
+
detectedContextLimit = overflowState.detectedContextLimit;
|
|
28012
28774
|
}
|
|
28013
28775
|
} catch (err) {
|
|
28014
28776
|
warn("message_end: getOverflowState failed:", err);
|
|
28015
28777
|
}
|
|
28016
|
-
return
|
|
28778
|
+
return resolvePiUsableContextLimit({
|
|
28779
|
+
rawContextWindow: args.piContextWindow,
|
|
28780
|
+
model: args.model,
|
|
28781
|
+
detectedContextLimit
|
|
28782
|
+
}) ?? 0;
|
|
28017
28783
|
}
|
|
28018
28784
|
async function persistPiPressureFromMessageEnd(args) {
|
|
28019
28785
|
const { provider, model } = getPiMessageModel(args.message);
|
|
28020
28786
|
const effectiveContextLimit = resolvePiPressureContextLimit({
|
|
28021
28787
|
db: args.db,
|
|
28022
28788
|
sessionId: args.sessionId,
|
|
28023
|
-
piContextWindow: args.piContextWindow
|
|
28789
|
+
piContextWindow: args.piContextWindow,
|
|
28790
|
+
model: args.piModel ?? { provider, id: model }
|
|
28024
28791
|
});
|
|
28025
28792
|
const usage = extractAssistantUsage(args.message);
|
|
28026
28793
|
const pressure = computePiPressure(usage, effectiveContextLimit);
|
|
@@ -28047,8 +28814,8 @@ async function persistPiPressureFromMessageEnd(args) {
|
|
|
28047
28814
|
}
|
|
28048
28815
|
} else if (typeof args.piTokens === "number") {
|
|
28049
28816
|
updates.lastInputTokens = args.piTokens;
|
|
28050
|
-
if (
|
|
28051
|
-
updates.lastContextPercentage = args.piTokens /
|
|
28817
|
+
if (effectiveContextLimit > 0) {
|
|
28818
|
+
updates.lastContextPercentage = args.piTokens / effectiveContextLimit * 100;
|
|
28052
28819
|
}
|
|
28053
28820
|
}
|
|
28054
28821
|
updateSessionMeta(args.db, args.sessionId, updates);
|
|
@@ -28075,7 +28842,8 @@ function resolveSidekickFromConfig(config) {
|
|
|
28075
28842
|
timeoutMs: sidekick.timeout_ms,
|
|
28076
28843
|
thinking_level: sidekick.thinking_level,
|
|
28077
28844
|
fallbackModels: resolveFallbackChain(sidekick.fallback_models),
|
|
28078
|
-
language: config.language
|
|
28845
|
+
language: config.language,
|
|
28846
|
+
allowHomeProject: config.allow_home_project
|
|
28079
28847
|
};
|
|
28080
28848
|
}
|
|
28081
28849
|
function resolveHistorianFromConfig(config) {
|
|
@@ -28105,7 +28873,8 @@ function resolveHistorianFromConfig(config) {
|
|
|
28105
28873
|
memoryEnabled: config.memory.enabled,
|
|
28106
28874
|
autoPromote: config.memory.auto_promote,
|
|
28107
28875
|
userMemoriesEnabled: userMemoryCollectionEnabled(config.dreamer),
|
|
28108
|
-
language: config.language
|
|
28876
|
+
language: config.language,
|
|
28877
|
+
allowHomeProject: config.allow_home_project
|
|
28109
28878
|
};
|
|
28110
28879
|
}
|
|
28111
28880
|
function resolveAutoSearchFromConfig(config) {
|
|
@@ -28125,7 +28894,20 @@ async function src_default(pi) {
|
|
|
28125
28894
|
log(`${PREFIX2} subagent child detected (${MAGIC_CONTEXT_PI_SUBAGENT_ENV}=1); skipping full extension registration`);
|
|
28126
28895
|
return;
|
|
28127
28896
|
}
|
|
28897
|
+
if (isPiMagicContextActiveInProcess()) {
|
|
28898
|
+
log(`${PREFIX2} in-process re-init detected (Magic Context already active in this process); skipping full extension registration`);
|
|
28899
|
+
return;
|
|
28900
|
+
}
|
|
28901
|
+
markPiMagicContextActive();
|
|
28128
28902
|
beginBootQuietPeriod();
|
|
28903
|
+
const bootProjectDir = process.cwd();
|
|
28904
|
+
ensureConfigLocationsMigrated(bootProjectDir);
|
|
28905
|
+
const bootConfig = loadPiConfig({ cwd: bootProjectDir });
|
|
28906
|
+
setStoragePrivatePermissionEnforcement(bootConfig.config.storage.enforce_private_permissions);
|
|
28907
|
+
setSqlitePragmaConfig({
|
|
28908
|
+
cacheSizeMb: bootConfig.config.sqlite.cache_size_mb,
|
|
28909
|
+
mmapSizeMb: bootConfig.config.sqlite.mmap_size_mb
|
|
28910
|
+
});
|
|
28129
28911
|
const storageDir = getMagicContextStorageDir();
|
|
28130
28912
|
const dbPath = join5(storageDir, "context.db");
|
|
28131
28913
|
let db;
|
|
@@ -28144,16 +28926,26 @@ async function src_default(pi) {
|
|
|
28144
28926
|
info("plugin DISABLED via config (enabled: false) — skipping registration");
|
|
28145
28927
|
return;
|
|
28146
28928
|
}
|
|
28929
|
+
const migration = getMigrationOnOpenRefusal();
|
|
28930
|
+
const blockingProcesses = migration?.serverPids.map((pid) => ({
|
|
28931
|
+
harness: "OpenCode server",
|
|
28932
|
+
pid
|
|
28933
|
+
})) ?? [];
|
|
28147
28934
|
const fence = getSchemaFenceRejection();
|
|
28148
|
-
const reason =
|
|
28935
|
+
const reason = migration && blockingProcesses.length > 0 ? {
|
|
28936
|
+
kind: "migration_guard",
|
|
28937
|
+
persistedVersion: migration.persistedVersion,
|
|
28938
|
+
supportedVersion: migration.supportedVersion,
|
|
28939
|
+
blockingProcesses
|
|
28940
|
+
} : fence ? {
|
|
28149
28941
|
kind: "schema_fence",
|
|
28150
28942
|
persistedVersion: fence.persistedVersion,
|
|
28151
28943
|
supportedVersion: fence.supportedVersion
|
|
28152
28944
|
} : {
|
|
28153
28945
|
kind: "storage_failure",
|
|
28154
|
-
cause: openFailureCause ?? `storage unavailable at ${dbPath} (cache schema newer than this binary, or open failed)`
|
|
28946
|
+
cause: migration?.unreadableFile ? `migration guard could not read RPC discovery file ${migration.unreadableFile}` : openFailureCause ?? `storage unavailable at ${dbPath} (cache schema newer than this binary, or open failed)`
|
|
28155
28947
|
};
|
|
28156
|
-
if (early.config.fail_closed_blocking === false) {
|
|
28948
|
+
if (early.config.fail_closed_blocking === false || !isCompactionEnabled(early.config)) {
|
|
28157
28949
|
warn(`Magic Context (pi) storage unavailable at ${dbPath}: ${formatFailClosedBlockingMessage(reason)}. ` + "fail_closed_blocking=false — degrading silently (hooks not registered).");
|
|
28158
28950
|
return;
|
|
28159
28951
|
}
|
|
@@ -28201,26 +28993,30 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28201
28993
|
})();
|
|
28202
28994
|
}, 0);
|
|
28203
28995
|
const projectDir = process.cwd();
|
|
28204
|
-
const projectIdentity = resolveProjectIdentityForSession(projectDir) ?? "";
|
|
28205
28996
|
const seenDreamerProjectIdentities = new Set;
|
|
28206
|
-
if (projectIdentity)
|
|
28207
|
-
seenDreamerProjectIdentities.add(projectIdentity);
|
|
28208
|
-
try {
|
|
28209
|
-
const pendingPiMarkerSessions = getSessionsWithPendingPiMarker(db);
|
|
28210
|
-
for (const sid of pendingPiMarkerSessions) {
|
|
28211
|
-
signalPiDeferredCompactionMarkerDrain(sid);
|
|
28212
|
-
}
|
|
28213
|
-
if (pendingPiMarkerSessions.length > 0) {
|
|
28214
|
-
log(`${PREFIX2} rehydrated ${pendingPiMarkerSessions.length} Pi deferred compaction marker session(s)`);
|
|
28215
|
-
}
|
|
28216
|
-
} catch (err) {
|
|
28217
|
-
warn(`Magic Context (pi) failed to rehydrate deferred Pi compaction markers: ${err instanceof Error ? err.message : String(err)}`);
|
|
28218
|
-
}
|
|
28219
|
-
info(`loaded v${PLUGIN_VERSION} | harness=pi | db=${dbPath} | ` + `project=${projectIdentity} | dir=${projectDir}`);
|
|
28220
28997
|
ensureConfigLocationsMigrated(projectDir);
|
|
28221
28998
|
const { config, warnings, loadedFromPaths } = loadPiConfig({
|
|
28222
28999
|
cwd: projectDir
|
|
28223
29000
|
});
|
|
29001
|
+
const projectIdentity = resolveProjectIdentityForSession(projectDir, config.allow_home_project) ?? "";
|
|
29002
|
+
if (projectIdentity)
|
|
29003
|
+
seenDreamerProjectIdentities.add(projectIdentity);
|
|
29004
|
+
info(`loaded v${PLUGIN_VERSION} | harness=pi | db=${dbPath} | ` + `project=${projectIdentity} | dir=${projectDir}`);
|
|
29005
|
+
const compactionOff = !isCompactionEnabled(config);
|
|
29006
|
+
setCtxReduceRegisteredGlobally(!compactionOff);
|
|
29007
|
+
if (!compactionOff) {
|
|
29008
|
+
try {
|
|
29009
|
+
const pendingPiMarkerSessions = getSessionsWithPendingPiMarker(db);
|
|
29010
|
+
for (const sid of pendingPiMarkerSessions) {
|
|
29011
|
+
signalPiDeferredCompactionMarkerDrain(sid);
|
|
29012
|
+
}
|
|
29013
|
+
if (pendingPiMarkerSessions.length > 0) {
|
|
29014
|
+
log(`${PREFIX2} rehydrated ${pendingPiMarkerSessions.length} Pi deferred compaction marker session(s)`);
|
|
29015
|
+
}
|
|
29016
|
+
} catch (err) {
|
|
29017
|
+
warn(`Magic Context (pi) failed to rehydrate deferred Pi compaction markers: ${err instanceof Error ? err.message : String(err)}`);
|
|
29018
|
+
}
|
|
29019
|
+
}
|
|
28224
29020
|
configurePiSubagentExtensions(config.pi?.subagent_extensions);
|
|
28225
29021
|
logPiConfigLoad({
|
|
28226
29022
|
dir: projectDir,
|
|
@@ -28228,6 +29024,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28228
29024
|
warnings,
|
|
28229
29025
|
dedupe: true
|
|
28230
29026
|
});
|
|
29027
|
+
setStoragePrivatePermissionEnforcement(config.storage.enforce_private_permissions);
|
|
28231
29028
|
setSqlitePragmaConfig({
|
|
28232
29029
|
cacheSizeMb: config.sqlite.cache_size_mb,
|
|
28233
29030
|
mmapSizeMb: config.sqlite.mmap_size_mb
|
|
@@ -28257,7 +29054,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28257
29054
|
injectDocs: cfg.dreamer?.inject_docs !== false,
|
|
28258
29055
|
injectionBudgetTokens: cfg.memory.injection_budget_tokens,
|
|
28259
29056
|
temporalAwareness: cfg.temporal_awareness === true,
|
|
28260
|
-
muralEnabled: cfg.
|
|
29057
|
+
muralEnabled: cfg.mural.enabled === true
|
|
28261
29058
|
},
|
|
28262
29059
|
scheduler: {
|
|
28263
29060
|
executeThresholdPercentage: cfg.execute_threshold_percentage,
|
|
@@ -28267,6 +29064,8 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28267
29064
|
language: cfg.language,
|
|
28268
29065
|
autoSearch: auto,
|
|
28269
29066
|
resolveForProject: resolveContextOptionsForProject,
|
|
29067
|
+
compactionOff,
|
|
29068
|
+
allowHomeProject: cfg.allow_home_project,
|
|
28270
29069
|
maybeAutoEmbedSession: (sessionId, dir, identity) => {
|
|
28271
29070
|
maybeAutoEmbedPiSession({
|
|
28272
29071
|
db: database,
|
|
@@ -28288,7 +29087,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28288
29087
|
hist.onStatusChange = (ctx) => {
|
|
28289
29088
|
updateStatusLine(ctx, {
|
|
28290
29089
|
db: database,
|
|
28291
|
-
projectIdentity: resolveCurrentProject(ctx).projectIdentity ?? ""
|
|
29090
|
+
projectIdentity: resolveCurrentProject(ctx, cfg.allow_home_project).projectIdentity ?? ""
|
|
28292
29091
|
});
|
|
28293
29092
|
};
|
|
28294
29093
|
}
|
|
@@ -28318,14 +29117,13 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28318
29117
|
dedupe: true
|
|
28319
29118
|
});
|
|
28320
29119
|
const switchedConfig = switchedLoad.config;
|
|
28321
|
-
const switchedIdentity = identityOverride ?? resolveProjectIdentityForSession(dir) ?? "";
|
|
29120
|
+
const switchedIdentity = identityOverride ?? resolveProjectIdentityForSession(dir, switchedConfig.allow_home_project) ?? "";
|
|
28322
29121
|
const built = buildProjectDeps(dir, switchedIdentity, switchedConfig);
|
|
28323
29122
|
projectDepsByDir.set(dir, built);
|
|
28324
29123
|
return built;
|
|
28325
29124
|
}
|
|
28326
29125
|
function resolveCurrentProjectDeps(ctx) {
|
|
28327
|
-
|
|
28328
|
-
return resolveProjectDepsForDir(currentProject.projectDir, currentProject.projectIdentity);
|
|
29126
|
+
return resolveProjectDepsForDir(ctx.cwd);
|
|
28329
29127
|
}
|
|
28330
29128
|
function resolveContextOptionsForProject(dir) {
|
|
28331
29129
|
return resolveProjectDepsForDir(dir).contextOptions;
|
|
@@ -28341,11 +29139,13 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28341
29139
|
memoryToolEnabled: true,
|
|
28342
29140
|
protectedTags: config.protected_tags ?? 20,
|
|
28343
29141
|
resolveProtectedTags: (ctx) => resolveCurrentProjectDeps(ctx).config.protected_tags ?? 20,
|
|
29142
|
+
resolveProjectIdentity: (ctx) => resolveCurrentProjectDeps(ctx).projectIdentity,
|
|
28344
29143
|
dreamerEnabled: isDreamerRunnable(config),
|
|
28345
29144
|
resolveDreamerEnabled: (ctx) => resolveCurrentProjectDeps(ctx).dreamerEnabled,
|
|
28346
|
-
todowriteEnabled
|
|
29145
|
+
todowriteEnabled,
|
|
29146
|
+
compactionOff
|
|
28347
29147
|
});
|
|
28348
|
-
info(todowriteEnabled ? "registered tools: ctx_search, ctx_memory, ctx_note, ctx_expand, todowrite, ctx_reduce; registered /todos" : "registered tools: ctx_search, ctx_memory, ctx_note, ctx_expand, ctx_reduce (todowrite disabled)");
|
|
29148
|
+
info(compactionOff ? todowriteEnabled ? "registered tools: ctx_search, ctx_memory, ctx_note, ctx_expand, todowrite; registered /todos (ctx_reduce unavailable in compaction-off mode)" : "registered tools: ctx_search, ctx_memory, ctx_note, ctx_expand (ctx_reduce unavailable in compaction-off mode; todowrite disabled)" : todowriteEnabled ? "registered tools: ctx_search, ctx_memory, ctx_note, ctx_expand, todowrite, ctx_reduce; registered /todos" : "registered tools: ctx_search, ctx_memory, ctx_note, ctx_expand, ctx_reduce (todowrite disabled)");
|
|
28349
29149
|
pi.on("session_start", async (event, ctx) => {
|
|
28350
29150
|
await handlePiCloneSessionStart(event, ctx, {
|
|
28351
29151
|
db,
|
|
@@ -28389,7 +29189,6 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28389
29189
|
return {
|
|
28390
29190
|
db,
|
|
28391
29191
|
projectIdentity: current.projectIdentity,
|
|
28392
|
-
resolveProject: resolveCurrentProject,
|
|
28393
29192
|
protectedTags: current.config.protected_tags,
|
|
28394
29193
|
executeThresholdPercentage: current.config.execute_threshold_percentage,
|
|
28395
29194
|
historyBudgetPercentage: current.config.history_budget_percentage,
|
|
@@ -28406,7 +29205,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28406
29205
|
info("registered /ctx-status");
|
|
28407
29206
|
registerStatusLine(pi, { db, projectIdentity });
|
|
28408
29207
|
info("registered magic-context status line");
|
|
28409
|
-
registerCtxFlushCommand(pi, { db });
|
|
29208
|
+
registerCtxFlushCommand(pi, { db, compactionOff });
|
|
28410
29209
|
info("registered /ctx-flush");
|
|
28411
29210
|
registerCtxRecompCommand(pi, {
|
|
28412
29211
|
db,
|
|
@@ -28419,6 +29218,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28419
29218
|
language: bootProjectDeps.config.language,
|
|
28420
29219
|
memoryEnabled: bootProjectDeps.config.memory.enabled,
|
|
28421
29220
|
autoPromote: bootProjectDeps.config.memory.auto_promote,
|
|
29221
|
+
compactionOff,
|
|
28422
29222
|
resolveRuntimeDeps: (ctx) => {
|
|
28423
29223
|
const current = resolveCurrentProjectDeps(ctx);
|
|
28424
29224
|
return {
|
|
@@ -28431,7 +29231,8 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28431
29231
|
historianThinkingLevel: current.historianConfig?.thinkingLevel,
|
|
28432
29232
|
language: current.config.language,
|
|
28433
29233
|
memoryEnabled: current.config.memory.enabled,
|
|
28434
|
-
autoPromote: current.config.memory.auto_promote
|
|
29234
|
+
autoPromote: current.config.memory.auto_promote,
|
|
29235
|
+
compactionOff
|
|
28435
29236
|
};
|
|
28436
29237
|
}
|
|
28437
29238
|
});
|
|
@@ -28447,6 +29248,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28447
29248
|
language: bootProjectDeps.config.language,
|
|
28448
29249
|
memoryEnabled: bootProjectDeps.config.memory.enabled,
|
|
28449
29250
|
autoPromote: bootProjectDeps.config.memory.auto_promote,
|
|
29251
|
+
compactionOff,
|
|
28450
29252
|
userMemoriesEnabled: userMemoryCollectionEnabled(bootProjectDeps.config.dreamer),
|
|
28451
29253
|
executeThresholdPercentage: bootProjectDeps.config.execute_threshold_percentage,
|
|
28452
29254
|
executeThresholdTokens: bootProjectDeps.config.execute_threshold_tokens,
|
|
@@ -28463,6 +29265,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28463
29265
|
language: current.config.language,
|
|
28464
29266
|
memoryEnabled: current.config.memory.enabled,
|
|
28465
29267
|
autoPromote: current.config.memory.auto_promote,
|
|
29268
|
+
compactionOff,
|
|
28466
29269
|
userMemoriesEnabled: userMemoryCollectionEnabled(current.config.dreamer),
|
|
28467
29270
|
executeThresholdPercentage: current.config.execute_threshold_percentage,
|
|
28468
29271
|
executeThresholdTokens: current.config.execute_threshold_tokens
|
|
@@ -28480,7 +29283,9 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28480
29283
|
historianThinkingLevel: bootProjectDeps.historianConfig?.thinkingLevel,
|
|
28481
29284
|
language: bootProjectDeps.config.language,
|
|
28482
29285
|
memoryEnabled: bootProjectDeps.config.memory.enabled,
|
|
29286
|
+
allowHomeProject: bootProjectDeps.config.allow_home_project,
|
|
28483
29287
|
autoPromote: bootProjectDeps.config.memory.auto_promote,
|
|
29288
|
+
compactionOff,
|
|
28484
29289
|
userMemoriesEnabled: userMemoryCollectionEnabled(bootProjectDeps.config.dreamer),
|
|
28485
29290
|
resolveRuntimeDeps: (ctx) => {
|
|
28486
29291
|
const current = resolveCurrentProjectDeps(ctx);
|
|
@@ -28494,7 +29299,9 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28494
29299
|
historianThinkingLevel: current.historianConfig?.thinkingLevel,
|
|
28495
29300
|
language: current.config.language,
|
|
28496
29301
|
memoryEnabled: current.config.memory.enabled,
|
|
29302
|
+
allowHomeProject: current.config.allow_home_project,
|
|
28497
29303
|
autoPromote: current.config.memory.auto_promote,
|
|
29304
|
+
compactionOff,
|
|
28498
29305
|
userMemoriesEnabled: userMemoryCollectionEnabled(current.config.dreamer)
|
|
28499
29306
|
};
|
|
28500
29307
|
}
|
|
@@ -28504,7 +29311,13 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28504
29311
|
db,
|
|
28505
29312
|
projectDir,
|
|
28506
29313
|
projectIdentity,
|
|
28507
|
-
resolveProject:
|
|
29314
|
+
resolveProject: (ctx) => {
|
|
29315
|
+
const current = resolveCurrentProjectDeps(ctx);
|
|
29316
|
+
return {
|
|
29317
|
+
projectDir: current.projectDir,
|
|
29318
|
+
projectIdentity: current.projectIdentity
|
|
29319
|
+
};
|
|
29320
|
+
},
|
|
28508
29321
|
dreamerEnabled: bootProjectDeps.dreamerEnabled,
|
|
28509
29322
|
resolveDreamerEnabled: (ctx) => resolveCurrentProjectDeps(ctx).dreamerEnabled,
|
|
28510
29323
|
onProjectSeen: (identity) => seenDreamerProjectIdentities.add(identity)
|
|
@@ -28516,7 +29329,13 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28516
29329
|
projectIdentity,
|
|
28517
29330
|
memoryEnabled: bootProjectDeps.config.memory.enabled,
|
|
28518
29331
|
resolveMemoryEnabled: (ctx) => resolveCurrentProjectDeps(ctx).config.memory.enabled,
|
|
28519
|
-
resolveProject:
|
|
29332
|
+
resolveProject: (ctx) => {
|
|
29333
|
+
const current = resolveCurrentProjectDeps(ctx);
|
|
29334
|
+
return {
|
|
29335
|
+
projectDir: current.projectDir,
|
|
29336
|
+
projectIdentity: current.projectIdentity
|
|
29337
|
+
};
|
|
29338
|
+
}
|
|
28520
29339
|
});
|
|
28521
29340
|
info("registered /ctx-embed");
|
|
28522
29341
|
const dreamerConfig = bootProjectDeps.dreamerConfig;
|
|
@@ -28605,11 +29424,11 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28605
29424
|
try {
|
|
28606
29425
|
const smForDrain = sm;
|
|
28607
29426
|
const canDrain = typeof smForDrain.appendCompaction === "function" && typeof smForDrain.getBranch === "function";
|
|
28608
|
-
if (canDrain && getPendingPiCompactionMarkerState(db, sessionId)) {
|
|
29427
|
+
if (!compactionOff && canDrain && getPendingPiCompactionMarkerState(db, sessionId)) {
|
|
28609
29428
|
signalPiDeferredCompactionMarkerDrain(sessionId);
|
|
28610
29429
|
}
|
|
28611
29430
|
} catch {}
|
|
28612
|
-
if (ctx.hasUI && effectiveProjectDeps.historianConfig?.model) {
|
|
29431
|
+
if (!compactionOff && ctx.hasUI && effectiveProjectDeps.historianConfig?.model) {
|
|
28613
29432
|
maybeSendUpgradeReminder({
|
|
28614
29433
|
client: null,
|
|
28615
29434
|
db,
|
|
@@ -28637,7 +29456,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
28637
29456
|
memoryEnabled: effectiveConfig.memory.enabled,
|
|
28638
29457
|
includeGuidance: true,
|
|
28639
29458
|
protectedTags: effectiveConfig.protected_tags,
|
|
28640
|
-
ctxReduceCallable:
|
|
29459
|
+
ctxReduceCallable: !compactionOff,
|
|
28641
29460
|
dreamerEnabled: effectiveProjectDeps.dreamerEnabled,
|
|
28642
29461
|
temporalAwarenessEnabled: effectiveConfig.temporal_awareness ?? false,
|
|
28643
29462
|
cavemanTextCompressionEnabled: effectiveConfig.caveman_text_compression?.enabled === true,
|
|
@@ -28682,7 +29501,7 @@ ${block}` : event.systemPrompt;
|
|
|
28682
29501
|
const lastAssistant = Array.isArray(msgs) ? [...msgs].reverse().find((m) => m?.role === "assistant") : undefined;
|
|
28683
29502
|
if (lastAssistant?.stopReason === "stop") {
|
|
28684
29503
|
const sessionId = ctx.sessionManager?.getSessionId?.();
|
|
28685
|
-
if (sessionId && db)
|
|
29504
|
+
if (sessionId && db && !compactionOff)
|
|
28686
29505
|
maybeDeliverChannel2Pi(pi, db, sessionId);
|
|
28687
29506
|
}
|
|
28688
29507
|
} catch (err) {
|
|
@@ -28707,7 +29526,7 @@ ${block}` : event.systemPrompt;
|
|
|
28707
29526
|
toolCallId
|
|
28708
29527
|
});
|
|
28709
29528
|
if (Array.isArray(todos) && todos.length > 0 && todos.every((t) => t.status === "completed" || t.status === "cancelled")) {
|
|
28710
|
-
if (sessionMeta && !sessionMeta.isSubagent) {
|
|
29529
|
+
if (!compactionOff && sessionMeta && !sessionMeta.isSubagent) {
|
|
28711
29530
|
onNoteTrigger(db, sessionId, "todos_complete");
|
|
28712
29531
|
}
|
|
28713
29532
|
}
|
|
@@ -28723,7 +29542,7 @@ ${block}` : event.systemPrompt;
|
|
|
28723
29542
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
28724
29543
|
if (typeof sessionId !== "string" || sessionId.length === 0)
|
|
28725
29544
|
return;
|
|
28726
|
-
if (event.toolName === "ctx_reduce") {
|
|
29545
|
+
if (!compactionOff && event.toolName === "ctx_reduce") {
|
|
28727
29546
|
markPiChannel1Reduced(sessionId, db);
|
|
28728
29547
|
}
|
|
28729
29548
|
} catch (err) {
|
|
@@ -28735,6 +29554,8 @@ ${block}` : event.systemPrompt;
|
|
|
28735
29554
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
28736
29555
|
if (typeof sessionId !== "string" || sessionId.length === 0)
|
|
28737
29556
|
return;
|
|
29557
|
+
if (compactionOff)
|
|
29558
|
+
return;
|
|
28738
29559
|
if (db)
|
|
28739
29560
|
maybeDeliverChannel2Pi(pi, db, sessionId, "steer");
|
|
28740
29561
|
const block = maybeChannel1ReminderForToolResult({
|
|
@@ -28750,20 +29571,11 @@ ${block}` : event.systemPrompt;
|
|
|
28750
29571
|
log(`tool_result hook failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
|
|
28751
29572
|
}
|
|
28752
29573
|
});
|
|
28753
|
-
pi.on("session_before_compact", async (_event, ctx) => {
|
|
28754
|
-
try {
|
|
28755
|
-
const sessionId = ctx.sessionManager?.getSessionId?.();
|
|
28756
|
-
if (typeof sessionId === "string" && sessionId.length > 0) {
|
|
28757
|
-
clearPiM0Cache(db, sessionId, "session_before_compact");
|
|
28758
|
-
}
|
|
28759
|
-
} catch {}
|
|
28760
|
-
info("session_before_compact: cancelling — magic-context owns compaction");
|
|
28761
|
-
return { cancel: true };
|
|
28762
|
-
});
|
|
29574
|
+
pi.on("session_before_compact", async (_event, ctx) => handlePiSessionBeforeCompact({ db, compactionOff, ctx }));
|
|
28763
29575
|
pi.on("message_end", async (event, ctx) => {
|
|
28764
29576
|
try {
|
|
28765
29577
|
const msg = event.message;
|
|
28766
|
-
if (msg !== null && typeof msg === "object") {
|
|
29578
|
+
if (!compactionOff && msg !== null && typeof msg === "object") {
|
|
28767
29579
|
stripTagPrefixFromAssistantMessage(msg);
|
|
28768
29580
|
}
|
|
28769
29581
|
} catch (err) {
|
|
@@ -28795,6 +29607,7 @@ ${block}` : event.systemPrompt;
|
|
|
28795
29607
|
sessionId,
|
|
28796
29608
|
message: event.message,
|
|
28797
29609
|
piContextWindow,
|
|
29610
|
+
piModel: ctx.model,
|
|
28798
29611
|
piTokens: piUsage && typeof piUsage.tokens === "number" ? piUsage.tokens : undefined,
|
|
28799
29612
|
notifyIssue: async (message) => {
|
|
28800
29613
|
const uiNotify = ctx.ui?.notify;
|
|
@@ -28824,6 +29637,8 @@ ${block}` : event.systemPrompt;
|
|
|
28824
29637
|
warn("message_end: persist session_meta usage failed:", err);
|
|
28825
29638
|
}
|
|
28826
29639
|
try {
|
|
29640
|
+
if (compactionOff)
|
|
29641
|
+
return;
|
|
28827
29642
|
const sm = ctx.sessionManager;
|
|
28828
29643
|
const sessionId = sm?.getSessionId?.();
|
|
28829
29644
|
if (typeof sessionId !== "string" || sessionId.length === 0)
|
|
@@ -28841,8 +29656,8 @@ ${block}` : event.systemPrompt;
|
|
|
28841
29656
|
if (!detection.isOverflow)
|
|
28842
29657
|
return;
|
|
28843
29658
|
const modelKey = typeof msg.provider === "string" && typeof msg.model === "string" && msg.provider.length > 0 && msg.model.length > 0 ? `${msg.provider}/${msg.model}` : undefined;
|
|
28844
|
-
recordOverflowDetected(db, sessionId, detection.reportedLimit, modelKey);
|
|
28845
|
-
log(`[magic-context][${sessionId}] overflow detected: reportedLimit=${detection.reportedLimit ?? "?"} pattern=${detection.matchedPattern ?? "?"}`);
|
|
29659
|
+
recordOverflowDetected(db, sessionId, detection.reportedLimit, modelKey, "provider_overflow", detection.reportedLimitProvenance);
|
|
29660
|
+
log(`[magic-context][${sessionId}] overflow detected: reportedLimit=${detection.reportedLimit ?? "?"} provenance=${detection.reportedLimitProvenance ?? "?"} pattern=${detection.matchedPattern ?? "?"}`);
|
|
28846
29661
|
} catch (err) {
|
|
28847
29662
|
warn("message_end: overflow detection failed:", err);
|
|
28848
29663
|
}
|
|
@@ -28879,6 +29694,7 @@ ${block}` : event.systemPrompt;
|
|
|
28879
29694
|
clearContextHandlerSession(sessionId);
|
|
28880
29695
|
}
|
|
28881
29696
|
} catch {}
|
|
29697
|
+
clearPiMagicContextActive();
|
|
28882
29698
|
});
|
|
28883
29699
|
pi.on("session_before_switch", (_event, ctx) => {
|
|
28884
29700
|
try {
|
|
@@ -28907,6 +29723,7 @@ export {
|
|
|
28907
29723
|
resolveDreamerFromConfig,
|
|
28908
29724
|
persistPiPressureFromMessageEnd,
|
|
28909
29725
|
persistPiMessageEndModelMeta,
|
|
29726
|
+
handlePiSessionBeforeCompact,
|
|
28910
29727
|
src_default as default,
|
|
28911
29728
|
capturePiTodowriteMessageIfCompatible,
|
|
28912
29729
|
capturePiTodowriteArgsIfCompatible,
|