@cortexkit/pi-magic-context 0.40.1 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/historian-calibration-extension.js +58 -0
- package/dist/index-3k71e196.js +378 -0
- package/dist/{index-fre7qcqg.js → index-ts6nabp0.js} +319 -260
- package/dist/index.js +1109 -455
- package/dist/{safe-notification-target-q00qgjm6.js → safe-notification-target-g8fx80va.js} +1 -1
- package/dist/subagent-entry.js +2 -2
- package/package.json +87 -87
- package/dist/index-hk6sj7dn.js +0 -147
- /package/dist/{index-8sseka65.js → index-wnrygn5h.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -52,6 +52,7 @@ import {
|
|
|
52
52
|
bumpProjectUserProfileVersion,
|
|
53
53
|
byteSize,
|
|
54
54
|
canonicalizeInMemoryChunkTextForEmbedding,
|
|
55
|
+
captureChannel1PostReduceGraceBaseline,
|
|
55
56
|
casChannel2NudgeClaim,
|
|
56
57
|
casChannel2NudgeState,
|
|
57
58
|
chunkCanonicalText,
|
|
@@ -100,6 +101,7 @@ import {
|
|
|
100
101
|
deriveTagLoadFloor,
|
|
101
102
|
deriveWindowGeometry,
|
|
102
103
|
describeError,
|
|
104
|
+
describeProtectedTailDrainBudgetSkip,
|
|
103
105
|
dismissUserMemory,
|
|
104
106
|
drainCommitBacklogForProject,
|
|
105
107
|
effectiveTailHygiene,
|
|
@@ -124,6 +126,7 @@ import {
|
|
|
124
126
|
extractM0Block,
|
|
125
127
|
extractTexts,
|
|
126
128
|
extractToolCallSummaries,
|
|
129
|
+
fenceBoundaryForCompletedToolArcs,
|
|
127
130
|
fenceBoundaryForToolArcs,
|
|
128
131
|
findAdoptableFallbackTags,
|
|
129
132
|
findLastModelKeyFromBranch,
|
|
@@ -230,7 +233,6 @@ import {
|
|
|
230
233
|
hasMuralCueColumns,
|
|
231
234
|
hasPiFallbackMessageTags,
|
|
232
235
|
hasPiFallbackToolOwnerTags,
|
|
233
|
-
hasShareabilitySensitiveText,
|
|
234
236
|
incrementHistorianFailure,
|
|
235
237
|
indexCommitsForProject,
|
|
236
238
|
indexMessagesAfterOrdinal,
|
|
@@ -256,6 +258,8 @@ import {
|
|
|
256
258
|
isWrapupInProgress,
|
|
257
259
|
loadPiConfig,
|
|
258
260
|
loadProtectedTailMeta,
|
|
261
|
+
logSlowWriteTransaction,
|
|
262
|
+
markChannel1PostReduceGracePending,
|
|
259
263
|
markGitSweepSuccessAndRelease,
|
|
260
264
|
markNoteChecked,
|
|
261
265
|
markNoteReady,
|
|
@@ -298,9 +302,11 @@ import {
|
|
|
298
302
|
readGitFileChangeTimesSince,
|
|
299
303
|
readGitHead,
|
|
300
304
|
readPiSessionMessages,
|
|
305
|
+
readPiSessionSnapshot,
|
|
301
306
|
readProjectDocsCanonical,
|
|
302
307
|
readRawSessionMessages,
|
|
303
308
|
readSessionChunk,
|
|
309
|
+
reclaimableToolOutputCount,
|
|
304
310
|
recordHistorianDrainFailure,
|
|
305
311
|
recordMemoryMapping,
|
|
306
312
|
recordMemoryVerifications,
|
|
@@ -327,8 +333,6 @@ import {
|
|
|
327
333
|
replaceCompartmentChunkEmbeddings,
|
|
328
334
|
replaceSourceContent,
|
|
329
335
|
reserveProtectedTailDrainTokens,
|
|
330
|
-
resetLastNudgeCycle,
|
|
331
|
-
resetLastNudgeCycleIfTailShrank,
|
|
332
336
|
resolveCompactionModeRecord,
|
|
333
337
|
resolveGitTopLevel,
|
|
334
338
|
resolveModelConfigOrDefault,
|
|
@@ -368,7 +372,6 @@ import {
|
|
|
368
372
|
setStoragePrivatePermissionEnforcement,
|
|
369
373
|
setTodoSnapshot,
|
|
370
374
|
shouldBypassFailClosedBlock,
|
|
371
|
-
shouldUseStickyChannel1Reminder,
|
|
372
375
|
sourceNameForMemory,
|
|
373
376
|
stripChannel1ReminderSpans,
|
|
374
377
|
stripMemoryMuralBlock,
|
|
@@ -403,7 +406,7 @@ import {
|
|
|
403
406
|
withMigrationLanguageDirective,
|
|
404
407
|
withRawMessageProvider,
|
|
405
408
|
withRawSessionMessageCache
|
|
406
|
-
} from "./index-
|
|
409
|
+
} from "./index-ts6nabp0.js";
|
|
407
410
|
import {
|
|
408
411
|
pushNotification
|
|
409
412
|
} from "./index-hw34xmzk.js";
|
|
@@ -411,19 +414,22 @@ import {
|
|
|
411
414
|
ensureCortexKitArtifactGitignore,
|
|
412
415
|
getHarness,
|
|
413
416
|
getMagicContextStorageDir,
|
|
417
|
+
getMagicContextStorageResolution,
|
|
414
418
|
getProjectMagicContextHistorianDir,
|
|
419
|
+
hasShareabilitySensitiveText,
|
|
415
420
|
log,
|
|
416
421
|
sessionLog,
|
|
417
422
|
setHarness
|
|
418
|
-
} from "./index-
|
|
423
|
+
} from "./index-3k71e196.js";
|
|
419
424
|
import {
|
|
420
425
|
__require,
|
|
421
426
|
__toESM
|
|
422
427
|
} from "./index-1yh8g550.js";
|
|
423
428
|
|
|
424
429
|
// src/index.ts
|
|
425
|
-
import {
|
|
426
|
-
import {
|
|
430
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
431
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
432
|
+
import { join as join6, resolve as resolve3 } from "node:path";
|
|
427
433
|
|
|
428
434
|
// ../plugin/src/shared/model-resolution.ts
|
|
429
435
|
function asRecord(value) {
|
|
@@ -1432,7 +1438,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
|
|
|
1432
1438
|
}
|
|
1433
1439
|
|
|
1434
1440
|
// ../plugin/src/hooks/magic-context/event-resolvers.ts
|
|
1435
|
-
var DEFAULT_CONTEXT_LIMIT =
|
|
1441
|
+
var DEFAULT_CONTEXT_LIMIT = 200000;
|
|
1436
1442
|
function resolveCacheTtl(cacheTtl, modelKey) {
|
|
1437
1443
|
if (typeof cacheTtl === "string") {
|
|
1438
1444
|
return cacheTtl;
|
|
@@ -2235,16 +2241,21 @@ function resolveBundledPiCli() {
|
|
|
2235
2241
|
return null;
|
|
2236
2242
|
}
|
|
2237
2243
|
}
|
|
2244
|
+
function isPiCliScript(scriptPath) {
|
|
2245
|
+
const normalized = scriptPath.replaceAll("\\", "/");
|
|
2246
|
+
return /\/@(?:earendil-works|oh-my-pi)\/pi-coding-agent\/dist\/cli\.js$/.test(normalized);
|
|
2247
|
+
}
|
|
2248
|
+
function isGenericRuntimeExecutable(execPath) {
|
|
2249
|
+
return /^(node(?:js)?\d*|bun)(\.exe)?$/.test(basename(execPath).toLowerCase());
|
|
2250
|
+
}
|
|
2238
2251
|
function resolvePiInvocation() {
|
|
2239
2252
|
const execPath = process.execPath;
|
|
2240
2253
|
const currentScript = process.argv[1];
|
|
2241
2254
|
const isBunVirtualScript = currentScript?.startsWith("/$bunfs/root/") ?? false;
|
|
2242
|
-
if (currentScript && !isBunVirtualScript && existsSync3(currentScript)) {
|
|
2255
|
+
if (currentScript && !isBunVirtualScript && existsSync3(currentScript) && isPiCliScript(currentScript)) {
|
|
2243
2256
|
return { command: execPath, prefixArgs: [currentScript] };
|
|
2244
2257
|
}
|
|
2245
|
-
|
|
2246
|
-
const isGenericRuntime = /^(node|bun)(\.exe)?$/.test(execName);
|
|
2247
|
-
if (!isGenericRuntime) {
|
|
2258
|
+
if (!isGenericRuntimeExecutable(execPath)) {
|
|
2248
2259
|
return { command: execPath, prefixArgs: [] };
|
|
2249
2260
|
}
|
|
2250
2261
|
const bundled = resolveBundledPiCli();
|
|
@@ -2253,18 +2264,17 @@ function resolvePiInvocation() {
|
|
|
2253
2264
|
}
|
|
2254
2265
|
return { command: "pi", prefixArgs: [] };
|
|
2255
2266
|
}
|
|
2256
|
-
function
|
|
2267
|
+
function resolveSiblingEntryPath(fileName) {
|
|
2257
2268
|
try {
|
|
2258
2269
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
2259
|
-
const candidate = resolvePath(here,
|
|
2260
|
-
|
|
2261
|
-
return candidate;
|
|
2262
|
-
return;
|
|
2270
|
+
const candidate = resolvePath(here, fileName);
|
|
2271
|
+
return existsSync3(candidate) ? candidate : undefined;
|
|
2263
2272
|
} catch {
|
|
2264
2273
|
return;
|
|
2265
2274
|
}
|
|
2266
2275
|
}
|
|
2267
|
-
var SUBAGENT_ENTRY_PATH =
|
|
2276
|
+
var SUBAGENT_ENTRY_PATH = resolveSiblingEntryPath("subagent-entry.js");
|
|
2277
|
+
var HISTORIAN_CALIBRATION_ENTRY_PATH = resolveSiblingEntryPath("historian-calibration-extension.js");
|
|
2268
2278
|
var TERMINAL_DRAIN_GRACE_MS = 2000;
|
|
2269
2279
|
var MAGIC_CONTEXT_PI_SUBAGENT_ENV = "MAGIC_CONTEXT_PI_SUBAGENT";
|
|
2270
2280
|
function packageRootIsOmp(packageRoot) {
|
|
@@ -2338,6 +2348,12 @@ var DREAMER_ACTION_AGENTS = new Set([
|
|
|
2338
2348
|
"dreamer",
|
|
2339
2349
|
"magic-context-dreamer"
|
|
2340
2350
|
]);
|
|
2351
|
+
var HISTORIAN_AGENTS = new Set([
|
|
2352
|
+
"magic-context-historian",
|
|
2353
|
+
"historian",
|
|
2354
|
+
"historian-recomp",
|
|
2355
|
+
"historian-editor"
|
|
2356
|
+
]);
|
|
2341
2357
|
var SEARCH_ONLY_SUBAGENT_TOOL_AGENTS = new Set([
|
|
2342
2358
|
"sidekick",
|
|
2343
2359
|
"dreamer-retrospective",
|
|
@@ -2644,7 +2660,13 @@ class PiSubagentRunner {
|
|
|
2644
2660
|
cwd: options.cwd,
|
|
2645
2661
|
env: {
|
|
2646
2662
|
...process.env,
|
|
2647
|
-
[MAGIC_CONTEXT_PI_SUBAGENT_ENV]: "1"
|
|
2663
|
+
[MAGIC_CONTEXT_PI_SUBAGENT_ENV]: "1",
|
|
2664
|
+
...options.temperature !== undefined ? {
|
|
2665
|
+
MAGIC_CONTEXT_HISTORIAN_TEMPERATURE: String(options.temperature)
|
|
2666
|
+
} : {},
|
|
2667
|
+
...options.maxOutputTokens !== undefined ? {
|
|
2668
|
+
MAGIC_CONTEXT_HISTORIAN_MAX_OUTPUT_TOKENS: String(options.maxOutputTokens)
|
|
2669
|
+
} : {}
|
|
2648
2670
|
},
|
|
2649
2671
|
stdio: [deliverViaStdin ? "pipe" : "ignore", "pipe", "pipe"]
|
|
2650
2672
|
});
|
|
@@ -3036,6 +3058,10 @@ function buildArgs(options, opts) {
|
|
|
3036
3058
|
args.push("--magic-context-dreamer-actions");
|
|
3037
3059
|
}
|
|
3038
3060
|
}
|
|
3061
|
+
const historianCalibrationEntryPath = opts?.historianCalibrationEntryPath === undefined ? HISTORIAN_CALIBRATION_ENTRY_PATH : opts.historianCalibrationEntryPath;
|
|
3062
|
+
if (HISTORIAN_AGENTS.has(options.agent) && historianCalibrationEntryPath) {
|
|
3063
|
+
args.push("--extension", historianCalibrationEntryPath);
|
|
3064
|
+
}
|
|
3039
3065
|
const strictTools = STRICT_TOOL_ALLOWLIST.get(options.agent);
|
|
3040
3066
|
if (strictTools === undefined) {
|
|
3041
3067
|
sessionLog(options.accountingSessionId ?? "pi-subagent", `Pi subagent agent "${options.agent}" has no strict tool allow-list; forcing --no-tools`);
|
|
@@ -3772,7 +3798,7 @@ async function sendIgnoredMessageNow(client, sessionId, text, params, forcePersi
|
|
|
3772
3798
|
queueIgnoredNotification({ client, sessionId, text, params, forcePersist });
|
|
3773
3799
|
return "queued";
|
|
3774
3800
|
}
|
|
3775
|
-
const { waitForSafeNotificationTarget } = await import("./safe-notification-target-
|
|
3801
|
+
const { waitForSafeNotificationTarget } = await import("./safe-notification-target-g8fx80va.js");
|
|
3776
3802
|
if (await waitForSafeNotificationTarget(client, sessionId) === "skip") {
|
|
3777
3803
|
sessionLog(sessionId, "notification skipped (session not titled yet)");
|
|
3778
3804
|
return "skipped";
|
|
@@ -4380,13 +4406,17 @@ function peekLeaseHolderAndExpiry(db, expectedHolder, leaseKey = DREAMING_LEASE_
|
|
|
4380
4406
|
function leaseOwnershipMatches(db, expectedHolder, expectedGeneration, leaseKey = DREAMING_LEASE_KEY) {
|
|
4381
4407
|
return getLeaseGeneration(db, leaseKey) === expectedGeneration && peekLeaseHolderAndExpiry(db, expectedHolder, leaseKey);
|
|
4382
4408
|
}
|
|
4383
|
-
function runImmediate(db, body) {
|
|
4409
|
+
function runImmediate(db, body, site, slowWriteThresholdMs) {
|
|
4410
|
+
const transactionStartedAt = site === undefined ? undefined : performance.now();
|
|
4384
4411
|
db.exec("BEGIN IMMEDIATE");
|
|
4385
4412
|
let committed = false;
|
|
4386
4413
|
try {
|
|
4387
4414
|
const result = body();
|
|
4388
4415
|
db.exec("COMMIT");
|
|
4389
4416
|
committed = true;
|
|
4417
|
+
if (site !== undefined && transactionStartedAt !== undefined) {
|
|
4418
|
+
logSlowWriteTransaction(site, transactionStartedAt, slowWriteThresholdMs);
|
|
4419
|
+
}
|
|
4390
4420
|
return result;
|
|
4391
4421
|
} finally {
|
|
4392
4422
|
if (!committed) {
|
|
@@ -4428,13 +4458,13 @@ function renewLease(db, holderId, leaseKey = DREAMING_LEASE_KEY, expectedGenerat
|
|
|
4428
4458
|
return true;
|
|
4429
4459
|
});
|
|
4430
4460
|
}
|
|
4431
|
-
function runLeaseGuardedWrite(db, holderId, leaseKey, fn) {
|
|
4461
|
+
function runLeaseGuardedWrite(db, holderId, leaseKey, fn, slowWriteThresholdMs) {
|
|
4432
4462
|
return runImmediate(db, () => {
|
|
4433
4463
|
if (!peekLeaseHolderAndExpiry(db, holderId, leaseKey)) {
|
|
4434
4464
|
throw new Error("Dream lease lost before guarded write");
|
|
4435
4465
|
}
|
|
4436
4466
|
return fn();
|
|
4437
|
-
});
|
|
4467
|
+
}, `lease-guarded-write:${leaseKey}`, slowWriteThresholdMs);
|
|
4438
4468
|
}
|
|
4439
4469
|
var LEASE_HEARTBEAT_INTERVAL_MS = 60 * 1000;
|
|
4440
4470
|
function startLeaseHeartbeat(db, holderId, leaseKey, onLost, intervalOrAcquisition = LEASE_HEARTBEAT_INTERVAL_MS) {
|
|
@@ -5966,7 +5996,7 @@ async function classifyOneChunk(args, chunk, anchors, sliceMs, signal) {
|
|
|
5966
5996
|
query: { directory: args.sessionDirectory },
|
|
5967
5997
|
body: {
|
|
5968
5998
|
agent: DREAMER_CLASSIFIER_AGENT,
|
|
5969
|
-
system: CLASSIFY_SYSTEM_PROMPT,
|
|
5999
|
+
system: withContentLanguageDirective(CLASSIFY_SYSTEM_PROMPT, args.language),
|
|
5970
6000
|
...modelBodyField(args.model),
|
|
5971
6001
|
parts: [{ type: "text", text: prompt, synthetic: true }]
|
|
5972
6002
|
}
|
|
@@ -6029,6 +6059,8 @@ async function runClassifyThroughModule(args, chunk, anchors, signal) {
|
|
|
6029
6059
|
memories: chunk.map(toPromptMemory2),
|
|
6030
6060
|
anchors
|
|
6031
6061
|
});
|
|
6062
|
+
const modelChain = [args.model, ...args.fallbackModels ?? []].map(toModelEntry).filter((entry) => entry !== undefined).map((entry) => entry.model);
|
|
6063
|
+
const resolvedModelChain = [...new Set(modelChain)];
|
|
6032
6064
|
const response = await args.moduleClient?.call({
|
|
6033
6065
|
sessionId: args.moduleSessionId,
|
|
6034
6066
|
projectRoot: args.moduleProjectRoot,
|
|
@@ -6040,6 +6072,7 @@ async function runClassifyThroughModule(args, chunk, anchors, signal) {
|
|
|
6040
6072
|
task: "classify",
|
|
6041
6073
|
command_id: `classify:${args.moduleCommandId ?? Date.now()}:${createHash4("sha256").update(chunk.map((candidate) => candidate.id).join(",")).digest("hex").slice(0, 24)}`,
|
|
6042
6074
|
authority_generation: args.moduleAuthorityGeneration,
|
|
6075
|
+
...resolvedModelChain.length > 0 ? { model_chain: resolvedModelChain } : {},
|
|
6043
6076
|
payload: {
|
|
6044
6077
|
prompt_body: prompt,
|
|
6045
6078
|
items: chunk.map((candidate) => ({
|
|
@@ -6713,7 +6746,7 @@ function getAsyncModule() {
|
|
|
6713
6746
|
asyncModulePromise ??= (async () => {
|
|
6714
6747
|
const [{ default: singlefileAsyncifyVariant }, { newQuickJSAsyncWASMModuleFromVariant }] = await Promise.all([
|
|
6715
6748
|
import("./index-rgdqj8nj.js").then((m)=>__toESM(m.default,1)),
|
|
6716
|
-
import("./index-
|
|
6749
|
+
import("./index-wnrygn5h.js")
|
|
6717
6750
|
]);
|
|
6718
6751
|
return newQuickJSAsyncWASMModuleFromVariant(singlefileAsyncifyVariant);
|
|
6719
6752
|
})();
|
|
@@ -7853,7 +7886,7 @@ Tools (read-only): read, grep, glob, aft_search, aft_outline, aft_zoom. Each mem
|
|
|
7853
7886
|
|
|
7854
7887
|
For each memory decide ONE of:
|
|
7855
7888
|
- Backing files found → the COMPLETE set of repo-relative paths whose code the memory is about.
|
|
7856
|
-
- File-independent → the memory describes EXTERNAL behavior (a provider / API / platform / protocol limit, e.g. "Anthropic returns 400 on empty content"), or a pure process / workflow / philosophy rule, with NO specific local file that backs it.
|
|
7889
|
+
- File-independent → the memory describes EXTERNAL behavior (a provider / API / platform / protocol limit, e.g. "Anthropic returns 400 on empty content"), or a pure process / workflow / philosophy rule, with NO specific local file that backs it. A BEHAVIORAL claim (when to act, how to work, who decides, or tool-usage discipline) is file-independent even when it cites file paths or commands as examples: a named file is not the file backing the rule.
|
|
7857
7890
|
|
|
7858
7891
|
Output ONE XML manifest at the very end and NOTHING else — no narration, no per-memory commentary, no reasoning:
|
|
7859
7892
|
<mappings>
|
|
@@ -7864,7 +7897,7 @@ Output ONE XML manifest at the very end and NOTHING else — no narration, no pe
|
|
|
7864
7897
|
Rules:
|
|
7865
7898
|
- Every input memory id MUST appear exactly once.
|
|
7866
7899
|
- files: repo-relative, comma-separated, no spaces inside a path. Only files that actually exist and genuinely back the memory.
|
|
7867
|
-
- A BACKING FILE is CODE that implements or handles the claim — not a file that merely mentions it. A markdown doc (.md), a PARITY/notes file, or a test that only DESCRIBES an external fact is NOT a backing file. If the only place a memory's fact appears is prose/docs/a test (no code implements or handles it), mark it independent="true".
|
|
7900
|
+
- A BACKING FILE is CODE that implements or handles the claim — not a file that merely mentions it. A path named inside a process directive is an action target or example, not evidence that the file backs the directive. A markdown doc (.md), a PARITY/notes file, or a test that only DESCRIBES an external fact is NOT a backing file. If the only place a memory's fact appears is prose/docs/a test (no code implements or handles it), mark it independent="true".
|
|
7868
7901
|
- Many CONSTRAINTS are HYBRID: "external system does X, and OUR code handles it here." Map those to the HANDLING code (you can verify the handling, even though you can't verify the external behavior). Only mark independent when there is NO local code that implements or handles the fact.
|
|
7869
7902
|
- Prefer the most specific file(s); do not pad with tangential files. Most memories map to one file; some to a few.
|
|
7870
7903
|
- When you genuinely cannot find any local backing and it is not clearly external, still emit the memory with independent="true" (do not drop it).`;
|
|
@@ -7902,7 +7935,7 @@ ${m.content}${seed}`;
|
|
|
7902
7935
|
|
|
7903
7936
|
Project: ${projectPath}
|
|
7904
7937
|
|
|
7905
|
-
For each memory below, find the repo file(s) it makes a claim about, or mark it file-independent. When "Likely files" are listed, those paths are named in the memory and confirmed to exist — START there: confirm each actually backs the claim (a quick read/outline), drop any that don't, add others only if genuinely needed. Search from scratch only when no likely files are given. Then output ONE <mappings> manifest covering every id.
|
|
7938
|
+
For each memory below, find the repo file(s) it makes a claim about, or mark it file-independent. Behavioral process/workflow directives stay file-independent even when they name files. When "Likely files" are listed, those paths are named in the memory and confirmed to exist — START there only for code claims: confirm each actually backs the claim (a quick read/outline), drop any that don't, add others only if genuinely needed. Search from scratch only when no likely files are given. Then output ONE <mappings> manifest covering every id.
|
|
7906
7939
|
|
|
7907
7940
|
<memories>
|
|
7908
7941
|
${list}
|
|
@@ -7967,6 +8000,21 @@ function validateMapMemoriesManifest(text, expectedIds) {
|
|
|
7967
8000
|
return parsed;
|
|
7968
8001
|
}
|
|
7969
8002
|
|
|
8003
|
+
// ../plugin/src/features/magic-context/dreamer/memory-claim-safety.ts
|
|
8004
|
+
var POLICY_SENTENCE_START = /(?:^|[.!?]\s+|\n\s*)(?:[-*]\s+|\d+[.)]\s+)?(?:you\s+|we\s+)?(?:must(?:\s+not)?|never|always|do\s+not|don't|shall\s+not)\b/i;
|
|
8005
|
+
var ACTOR_POLICY = /\b(?:you|we|agents?|masons?|workers?|operators?|users?|maintainers?)\s+(?:must(?:\s+not)?|should(?:\s+not)?|need\s+to|shall(?:\s+not)?|cannot|can't|may\s+not)\b/i;
|
|
8006
|
+
var BEHAVIORAL_WHEN_CLAUSE = /\bwhen\s+(?:you(?:'re|\s+are)?|we(?:'re|\s+are)?|told|asked|requested|working|debugging|reviewing|checking|verifying|investigating|using|running|editing|changing)\b/i;
|
|
8007
|
+
var WORKFLOW_IMPERATIVE = /(?:^|[.!?]\s+|\n\s*)(?:[-*]\s+|\d+[.)]\s+)?(?:please\s+)?(?:run|use|check|ask|avoid|prefer|ensure|keep|follow|brief|report|inspect|search|open|read|review|validate|confirm|delegate|stop|start|remember)\b/i;
|
|
8008
|
+
var DECISION_AUTHORITY = /\b(?:the\s+)?(?:user|operator|maintainer|owner)\s+(?:decides|chooses|approves|has\s+(?:the\s+)?final\s+say)\b/i;
|
|
8009
|
+
function isDirectiveShapedProjectRule(category, content) {
|
|
8010
|
+
if (category !== "PROJECT_RULES")
|
|
8011
|
+
return false;
|
|
8012
|
+
const text = content.trim();
|
|
8013
|
+
if (!text)
|
|
8014
|
+
return false;
|
|
8015
|
+
return POLICY_SENTENCE_START.test(text) || ACTOR_POLICY.test(text) || BEHAVIORAL_WHEN_CLAUSE.test(text) || WORKFLOW_IMPERATIVE.test(text) || DECISION_AUTHORITY.test(text);
|
|
8016
|
+
}
|
|
8017
|
+
|
|
7970
8018
|
// ../plugin/src/features/magic-context/dreamer/map-memories.ts
|
|
7971
8019
|
var MAP_BATCH_SIZE = 80;
|
|
7972
8020
|
var MAP_BATCH_FLOOR_MS = 240000;
|
|
@@ -7981,6 +8029,8 @@ function isTimeoutClassError2(error) {
|
|
|
7981
8029
|
function shouldRequeueIndependentMapping(state2, content, repoDir) {
|
|
7982
8030
|
if (!state2.hasSentinel || state2.files.length > 0)
|
|
7983
8031
|
return false;
|
|
8032
|
+
if (state2.mappingOrigin === "host_rejected_fallback")
|
|
8033
|
+
return false;
|
|
7984
8034
|
return extractMemoryCandidatePaths(content, repoDir).length > 0;
|
|
7985
8035
|
}
|
|
7986
8036
|
function toMapInput(memory, repoDir) {
|
|
@@ -8187,9 +8237,26 @@ async function applyParsedBatchMappings(args, batch, parsed) {
|
|
|
8187
8237
|
if (valid.length === 0)
|
|
8188
8238
|
return { mapped: 0, independent: 0 };
|
|
8189
8239
|
const planned = [];
|
|
8240
|
+
const batchById = new Map(batch.map((memory) => [memory.id, memory]));
|
|
8190
8241
|
for (const p of valid) {
|
|
8242
|
+
const memory = batchById.get(p.id);
|
|
8243
|
+
if (!p.independent && p.files.length > 0 && memory && isDirectiveShapedProjectRule(memory.category, memory.content)) {
|
|
8244
|
+
log(`[dreamer] map-memories safety override: memory_id=${p.id} verdict=file-mapping replacement=independent mapping_origin=host_rejected_fallback reason=directive-shaped-project-rule`);
|
|
8245
|
+
planned.push({
|
|
8246
|
+
id: p.id,
|
|
8247
|
+
files: [],
|
|
8248
|
+
independent: true,
|
|
8249
|
+
mappingOrigin: "host_rejected_fallback"
|
|
8250
|
+
});
|
|
8251
|
+
continue;
|
|
8252
|
+
}
|
|
8191
8253
|
if (p.independent) {
|
|
8192
|
-
planned.push({
|
|
8254
|
+
planned.push({
|
|
8255
|
+
id: p.id,
|
|
8256
|
+
files: [],
|
|
8257
|
+
independent: true,
|
|
8258
|
+
mappingOrigin: "mapper"
|
|
8259
|
+
});
|
|
8193
8260
|
continue;
|
|
8194
8261
|
}
|
|
8195
8262
|
if (p.files.length === 0) {
|
|
@@ -8199,9 +8266,22 @@ async function applyParsedBatchMappings(args, batch, parsed) {
|
|
|
8199
8266
|
cwd: args.sessionDirectory,
|
|
8200
8267
|
files: p.files
|
|
8201
8268
|
});
|
|
8202
|
-
if (normalized.files.length === 0)
|
|
8269
|
+
if (normalized.files.length === 0) {
|
|
8270
|
+
log(`[dreamer] map-memories: all ${p.files.length} path(s) for memory ${p.id} were rejected; recording host_rejected_fallback`);
|
|
8271
|
+
planned.push({
|
|
8272
|
+
id: p.id,
|
|
8273
|
+
files: [],
|
|
8274
|
+
independent: true,
|
|
8275
|
+
mappingOrigin: "host_rejected_fallback"
|
|
8276
|
+
});
|
|
8203
8277
|
continue;
|
|
8204
|
-
|
|
8278
|
+
}
|
|
8279
|
+
planned.push({
|
|
8280
|
+
id: p.id,
|
|
8281
|
+
files: normalized.files,
|
|
8282
|
+
independent: false,
|
|
8283
|
+
mappingOrigin: "mapper"
|
|
8284
|
+
});
|
|
8205
8285
|
}
|
|
8206
8286
|
if (planned.length === 0)
|
|
8207
8287
|
return { mapped: 0, independent: 0 };
|
|
@@ -8216,7 +8296,8 @@ async function applyParsedBatchMappings(args, batch, parsed) {
|
|
|
8216
8296
|
return {
|
|
8217
8297
|
memory_id: identity.moduleId,
|
|
8218
8298
|
content_hash_at_prompt: identity.normalizedHash,
|
|
8219
|
-
mapped_files: item.independent ? null : item.files
|
|
8299
|
+
mapped_files: item.independent ? null : item.files,
|
|
8300
|
+
mapping_origin: item.mappingOrigin
|
|
8220
8301
|
};
|
|
8221
8302
|
});
|
|
8222
8303
|
let response;
|
|
@@ -8253,7 +8334,7 @@ async function applyParsedBatchMappings(args, batch, parsed) {
|
|
|
8253
8334
|
const now = Date.now();
|
|
8254
8335
|
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
8255
8336
|
for (const item of planned) {
|
|
8256
|
-
recordMemoryMapping(args.db, item.id, item.files, now);
|
|
8337
|
+
recordMemoryMapping(args.db, item.id, item.files, now, item.mappingOrigin);
|
|
8257
8338
|
item.independent ? independent += 1 : mapped += 1;
|
|
8258
8339
|
}
|
|
8259
8340
|
});
|
|
@@ -9414,24 +9495,29 @@ Each memory below comes with its backing file(s) — the code it makes a claim a
|
|
|
9414
9495
|
|
|
9415
9496
|
Tools (read-only): read, grep, glob, aft_search, aft_outline, aft_zoom. You read code to check claims; you change nothing.
|
|
9416
9497
|
|
|
9417
|
-
Decide ONE of
|
|
9498
|
+
Decide ONE of four outcomes per memory:
|
|
9418
9499
|
- VERIFIED — still accurate. Keep it as-is.
|
|
9419
|
-
- UPDATE —
|
|
9420
|
-
- ARCHIVE — the code
|
|
9500
|
+
- UPDATE — a CODE FACT is still true but a file-falsifiable DETAIL drifted (a renamed symbol, moved file, changed number/name). Provide corrected content in terse present tense ("X uses Y", not "X was changed to Y"). Only update for genuine drift, not style. If a legitimate update intentionally consolidates the old claim into much shorter content, set consolidation="true" explicitly.
|
|
9501
|
+
- ARCHIVE — a CODE FACT is positively falsified: the code clearly contradicts it, or the API/symbol/path it describes no longer exists.
|
|
9502
|
+
- SKIP — the claim is not a code fact, or code cannot decide it. Keep it unchanged and do not claim verification.
|
|
9421
9503
|
|
|
9422
|
-
|
|
9504
|
+
UPDATE and ARCHIVE are ONLY for claims a repository file can falsify, such as an API that no longer exists, a path that moved, or a constant that changed. Behavioral directives (when to act, how to work, who decides, tool-usage discipline) can only be VERIFIED or SKIPPED. A named path inside a directive does not make the directive a code fact, and a file's failure to corroborate a behavioral rule is never grounds to update or archive it.
|
|
9505
|
+
|
|
9506
|
+
BE CONSERVATIVE ABOUT ARCHIVING. Wrong archival of a TRUE memory is the worst possible outcome — far worse than leaving a slightly-stale memory. If you cannot find the code, or you are unsure, or it might still be true somewhere you didn't look: mark it VERIFIED or SKIPPED, never archived. Archive ONLY when you have positive evidence the code contradicts a code fact.
|
|
9423
9507
|
|
|
9424
9508
|
Output ONE XML manifest at the very end and NOTHING else — no narration, no per-memory commentary, no reasoning:
|
|
9425
9509
|
<verify>
|
|
9426
9510
|
<verified id="N" files="path/a.ts,path/b.ts"/>
|
|
9427
9511
|
<update id="M" files="path/c.ts">corrected present-tense content</update>
|
|
9512
|
+
<update id="C" files="path/d.ts" consolidation="true">intentionally consolidated content</update>
|
|
9428
9513
|
<archive id="K" reason="specific evidence the code contradicts it"/>
|
|
9514
|
+
<skip id="S" reason="behavioral directive; code cannot decide it"/>
|
|
9429
9515
|
</verify>
|
|
9430
9516
|
|
|
9431
9517
|
Rules:
|
|
9432
|
-
- Every input memory id MUST appear exactly once, in exactly one of verified/update/archive.
|
|
9518
|
+
- Every input memory id MUST appear exactly once, in exactly one of verified/update/archive/skip.
|
|
9433
9519
|
- files = the COMPLETE current backing set (repo-relative, comma-separated). It may differ from the given mapping if a file moved — record what you actually verified against.
|
|
9434
|
-
- Default to VERIFIED. update and archive are the exceptions, not the norm.`;
|
|
9520
|
+
- Default to VERIFIED. skip when code cannot decide the claim. update and archive are the exceptions, not the norm.`;
|
|
9435
9521
|
function buildVerifyPrompt(projectPath, memories) {
|
|
9436
9522
|
const list = memories.map((m) => `[${m.id}] ${m.category}
|
|
9437
9523
|
Content: ${m.content}
|
|
@@ -9442,7 +9528,7 @@ Backing files: ${m.mappedFiles.join(", ")}`).join(`
|
|
|
9442
9528
|
|
|
9443
9529
|
Project: ${projectPath}
|
|
9444
9530
|
|
|
9445
|
-
Read each memory's backing files, decide verified / update / archive (default verified;
|
|
9531
|
+
Read each memory's backing files, decide verified / update / archive / skip (default verified; behavioral directives only permit verified or skip), then output ONE <verify> manifest covering every id.
|
|
9446
9532
|
|
|
9447
9533
|
<memories>
|
|
9448
9534
|
${list}
|
|
@@ -9456,7 +9542,7 @@ function filesOf(s) {
|
|
|
9456
9542
|
return (attrOf(s, "files") ?? "").split(",").map((f) => f.trim()).filter(Boolean);
|
|
9457
9543
|
}
|
|
9458
9544
|
function verifyIds(parsed) {
|
|
9459
|
-
return [...parsed.verified, ...parsed.updated, ...parsed.archived].map((entry) => entry.id);
|
|
9545
|
+
return [...parsed.verified, ...parsed.updated, ...parsed.archived, ...parsed.skipped].map((entry) => entry.id);
|
|
9460
9546
|
}
|
|
9461
9547
|
function verifyBody(text) {
|
|
9462
9548
|
try {
|
|
@@ -9469,7 +9555,7 @@ function verifyBody(text) {
|
|
|
9469
9555
|
}
|
|
9470
9556
|
}
|
|
9471
9557
|
function parseVerifyManifest(text) {
|
|
9472
|
-
const out = { verified: [], updated: [], archived: [] };
|
|
9558
|
+
const out = { verified: [], updated: [], archived: [], skipped: [] };
|
|
9473
9559
|
const body = verifyBody(text);
|
|
9474
9560
|
for (const m of body.matchAll(/<verified\b([^>]*)\/?>/g)) {
|
|
9475
9561
|
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
@@ -9481,7 +9567,12 @@ function parseVerifyManifest(text) {
|
|
|
9481
9567
|
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
9482
9568
|
if (!Number.isInteger(id))
|
|
9483
9569
|
throw new Error("verify manifest entry missing numeric id");
|
|
9484
|
-
out.updated.push({
|
|
9570
|
+
out.updated.push({
|
|
9571
|
+
id,
|
|
9572
|
+
files: filesOf(m[1]),
|
|
9573
|
+
content: (m[2] ?? "").trim(),
|
|
9574
|
+
consolidation: attrOf(m[1], "consolidation")?.toLowerCase() === "true"
|
|
9575
|
+
});
|
|
9485
9576
|
}
|
|
9486
9577
|
for (const m of body.matchAll(/<archive\b([^>]*)\/?>/g)) {
|
|
9487
9578
|
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
@@ -9489,6 +9580,12 @@ function parseVerifyManifest(text) {
|
|
|
9489
9580
|
throw new Error("verify manifest entry missing numeric id");
|
|
9490
9581
|
out.archived.push({ id, reason: attrOf(m[1], "reason") ?? "" });
|
|
9491
9582
|
}
|
|
9583
|
+
for (const m of body.matchAll(/<skip\b([^>]*)\/?>/g)) {
|
|
9584
|
+
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
9585
|
+
if (!Number.isInteger(id))
|
|
9586
|
+
throw new Error("verify manifest entry missing numeric id");
|
|
9587
|
+
out.skipped.push({ id, reason: attrOf(m[1], "reason") ?? "" });
|
|
9588
|
+
}
|
|
9492
9589
|
if (verifyIds(out).length === 0 && body.trim().length > 0) {
|
|
9493
9590
|
throw new Error(describeUnrecognizedManifestShape(text, "verify", "verified"));
|
|
9494
9591
|
}
|
|
@@ -9528,6 +9625,8 @@ async function runVerify(args) {
|
|
|
9528
9625
|
verified: 0,
|
|
9529
9626
|
updated: 0,
|
|
9530
9627
|
archived: 0,
|
|
9628
|
+
skipped: 0,
|
|
9629
|
+
refused: 0,
|
|
9531
9630
|
batches: 0,
|
|
9532
9631
|
inScope: 0,
|
|
9533
9632
|
remaining: 0,
|
|
@@ -9572,9 +9671,12 @@ async function runVerify(args) {
|
|
|
9572
9671
|
result.verified += counts.verified;
|
|
9573
9672
|
result.updated += counts.updated;
|
|
9574
9673
|
result.archived += counts.archived;
|
|
9575
|
-
result.
|
|
9674
|
+
result.skipped += counts.skipped;
|
|
9675
|
+
result.refused += counts.refused;
|
|
9676
|
+
const batchProcessed = counts.verified + counts.updated + counts.archived + counts.skipped + counts.refused;
|
|
9677
|
+
result.remaining -= batchProcessed;
|
|
9576
9678
|
result.batches += 1;
|
|
9577
|
-
args.onProgress?.(result.verified + result.updated + result.archived);
|
|
9679
|
+
args.onProgress?.(result.verified + result.updated + result.archived + result.skipped + result.refused, result.refused);
|
|
9578
9680
|
if (counts.providerFailure) {
|
|
9579
9681
|
lastProviderFailure = counts.providerFailure;
|
|
9580
9682
|
if (counts.providerFailure.fingerprint === priorProviderFailureFingerprint) {
|
|
@@ -9597,7 +9699,7 @@ async function runVerify(args) {
|
|
|
9597
9699
|
result.complete = result.remaining === 0;
|
|
9598
9700
|
if (result.complete)
|
|
9599
9701
|
closeBroadCycle(args, gate.broadCycleStartAt);
|
|
9600
|
-
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}`);
|
|
9702
|
+
log(`[dreamer] ${args.forceBroad ? "verify-broad" : "verify"}: verified=${result.verified} updated=${result.updated} archived=${result.archived} skipped=${result.skipped} refused=${result.refused} batches=${result.batches} remaining=${result.remaining} complete=${result.complete}`);
|
|
9601
9703
|
return result;
|
|
9602
9704
|
} finally {
|
|
9603
9705
|
heartbeat.stop();
|
|
@@ -9666,7 +9768,14 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
9666
9768
|
recordInvocation4(args, startedAt, { status: "failed", error });
|
|
9667
9769
|
if (error instanceof DreamerModuleFailureError || signal.aborted)
|
|
9668
9770
|
throw error;
|
|
9669
|
-
return {
|
|
9771
|
+
return {
|
|
9772
|
+
verified: 0,
|
|
9773
|
+
updated: 0,
|
|
9774
|
+
archived: 0,
|
|
9775
|
+
skipped: 0,
|
|
9776
|
+
refused: 0,
|
|
9777
|
+
providerFailure
|
|
9778
|
+
};
|
|
9670
9779
|
} finally {
|
|
9671
9780
|
if (agentSessionId && !shouldKeepSubagents()) {
|
|
9672
9781
|
await args.client.session.delete({
|
|
@@ -9680,35 +9789,59 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
9680
9789
|
}
|
|
9681
9790
|
async function applyParsedVerifyManifest(args, batch, parsed) {
|
|
9682
9791
|
const batchIds = new Set(batch.map((m) => m.id));
|
|
9792
|
+
const batchById = new Map(batch.map((memory) => [memory.id, memory]));
|
|
9683
9793
|
const valid = {
|
|
9684
9794
|
verified: parsed.verified.filter((entry) => batchIds.has(entry.id)),
|
|
9685
9795
|
updated: parsed.updated.filter((entry) => batchIds.has(entry.id)),
|
|
9686
|
-
archived: parsed.archived.filter((entry) => batchIds.has(entry.id))
|
|
9796
|
+
archived: parsed.archived.filter((entry) => batchIds.has(entry.id)),
|
|
9797
|
+
skipped: parsed.skipped.filter((entry) => batchIds.has(entry.id))
|
|
9687
9798
|
};
|
|
9688
|
-
const unknown = [
|
|
9799
|
+
const unknown = [
|
|
9800
|
+
...parsed.verified,
|
|
9801
|
+
...parsed.updated,
|
|
9802
|
+
...parsed.archived,
|
|
9803
|
+
...parsed.skipped
|
|
9804
|
+
].filter((entry) => !batchIds.has(entry.id));
|
|
9689
9805
|
if (unknown.length > 0) {
|
|
9690
9806
|
log(`[dreamer] verify warning: dropping ${unknown.length} unknown verification entr${unknown.length === 1 ? "y" : "ies"} outside the current batch (${unknown.map((entry) => entry.id).join(", ")})`);
|
|
9691
9807
|
}
|
|
9692
|
-
const validIds = [...valid.verified, ...valid.updated, ...valid.archived].map((entry) => entry.id);
|
|
9808
|
+
const validIds = [...valid.verified, ...valid.updated, ...valid.archived, ...valid.skipped].map((entry) => entry.id);
|
|
9693
9809
|
assertNoDuplicateManifestIds(validIds, "verify");
|
|
9694
9810
|
if (validIds.length * 2 < batch.length) {
|
|
9695
9811
|
throw new Error(`verify manifest covers ${validIds.length}/${batch.length} batch ids after filtering unknown entries; rejecting mostly-wrong manifest`);
|
|
9696
9812
|
}
|
|
9697
|
-
if (validIds.length === 0)
|
|
9698
|
-
return { verified: 0, updated: 0, archived: 0 };
|
|
9813
|
+
if (validIds.length === 0) {
|
|
9814
|
+
return { verified: 0, updated: 0, archived: 0, skipped: 0, refused: 0 };
|
|
9815
|
+
}
|
|
9699
9816
|
const now = Date.now();
|
|
9700
9817
|
const writes = [];
|
|
9818
|
+
let refused = 0;
|
|
9819
|
+
const skipped = valid.skipped.length;
|
|
9701
9820
|
for (const v of valid.verified) {
|
|
9702
9821
|
const files = await normalizeFiles(args, v.files);
|
|
9703
9822
|
writes.push({ kind: "verify", id: v.id, files });
|
|
9704
9823
|
}
|
|
9705
9824
|
for (const u of valid.updated) {
|
|
9825
|
+
const original = batchById.get(u.id);
|
|
9826
|
+
if (!original)
|
|
9827
|
+
continue;
|
|
9828
|
+
if (isDirectiveShapedProjectRule(original.category, original.content)) {
|
|
9829
|
+
log(`[dreamer] verify safety refusal: memory_id=${u.id} verdict=update reason=directive-shaped-project-rule`);
|
|
9830
|
+
refused += 1;
|
|
9831
|
+
continue;
|
|
9832
|
+
}
|
|
9706
9833
|
const content = u.content.trim();
|
|
9707
9834
|
if (!content || content.length > 20000) {
|
|
9708
9835
|
const files2 = await normalizeFiles(args, u.files);
|
|
9709
9836
|
writes.push({ kind: "verify", id: u.id, files: files2 });
|
|
9710
9837
|
continue;
|
|
9711
9838
|
}
|
|
9839
|
+
const originalLength = original.content.trim().length;
|
|
9840
|
+
if (!u.consolidation && content.length * 2 < originalLength) {
|
|
9841
|
+
log(`[dreamer] verify safety refusal: memory_id=${u.id} verdict=update reason=content-loss original_chars=${originalLength} replacement_chars=${content.length}`);
|
|
9842
|
+
refused += 1;
|
|
9843
|
+
continue;
|
|
9844
|
+
}
|
|
9712
9845
|
const files = await normalizeFiles(args, u.files);
|
|
9713
9846
|
writes.push({
|
|
9714
9847
|
kind: "update",
|
|
@@ -9719,13 +9852,21 @@ async function applyParsedVerifyManifest(args, batch, parsed) {
|
|
|
9719
9852
|
});
|
|
9720
9853
|
}
|
|
9721
9854
|
for (const a of valid.archived) {
|
|
9855
|
+
const original = batchById.get(a.id);
|
|
9856
|
+
if (!original)
|
|
9857
|
+
continue;
|
|
9858
|
+
if (isDirectiveShapedProjectRule(original.category, original.content)) {
|
|
9859
|
+
log(`[dreamer] verify safety refusal: memory_id=${a.id} verdict=archive reason=directive-shaped-project-rule`);
|
|
9860
|
+
refused += 1;
|
|
9861
|
+
continue;
|
|
9862
|
+
}
|
|
9722
9863
|
writes.push({ kind: "archive", id: a.id, reason: a.reason });
|
|
9723
9864
|
}
|
|
9724
|
-
if (writes.length === 0)
|
|
9725
|
-
return { verified: 0, updated: 0, archived: 0 };
|
|
9726
9865
|
let verified = 0;
|
|
9727
9866
|
let updated = 0;
|
|
9728
9867
|
let archived = 0;
|
|
9868
|
+
if (writes.length === 0)
|
|
9869
|
+
return { verified, updated, archived, skipped, refused };
|
|
9729
9870
|
if (args.moduleRoute) {
|
|
9730
9871
|
const identities = getModuleMemoryIdentities(args.db, args.projectIdentity, writes.map((write) => write.id));
|
|
9731
9872
|
const rows = writes.map((write) => {
|
|
@@ -9775,7 +9916,7 @@ async function applyParsedVerifyManifest(args, batch, parsed) {
|
|
|
9775
9916
|
else
|
|
9776
9917
|
archived += 1;
|
|
9777
9918
|
}
|
|
9778
|
-
return { verified, updated, archived };
|
|
9919
|
+
return { verified, updated, archived, skipped, refused };
|
|
9779
9920
|
}
|
|
9780
9921
|
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
9781
9922
|
for (const w of writes) {
|
|
@@ -9806,7 +9947,7 @@ async function applyParsedVerifyManifest(args, batch, parsed) {
|
|
|
9806
9947
|
}
|
|
9807
9948
|
}
|
|
9808
9949
|
});
|
|
9809
|
-
return { verified, updated, archived };
|
|
9950
|
+
return { verified, updated, archived, skipped, refused };
|
|
9810
9951
|
}
|
|
9811
9952
|
async function normalizeFiles(args, rawFiles) {
|
|
9812
9953
|
if (rawFiles.length === 0)
|
|
@@ -9919,12 +10060,13 @@ function createDreamTaskExecutor(deps) {
|
|
|
9919
10060
|
})();
|
|
9920
10061
|
const deadline = startedAt + config.timeoutMinutes * 60 * 1000;
|
|
9921
10062
|
const backlogAtStart = getDreamTaskBacklog(db, projectIdentity, config.task);
|
|
9922
|
-
const reportProgress = (processed) => {
|
|
10063
|
+
const reportProgress = (processed, refused) => {
|
|
9923
10064
|
deps.onProgress?.({
|
|
9924
10065
|
task: config.task,
|
|
9925
10066
|
processed: Math.max(0, processed),
|
|
9926
10067
|
total: backlogAtStart.pending,
|
|
9927
|
-
startedAt
|
|
10068
|
+
startedAt,
|
|
10069
|
+
...refused === undefined ? {} : { refused: Math.max(0, refused) }
|
|
9928
10070
|
});
|
|
9929
10071
|
};
|
|
9930
10072
|
reportProgress(0);
|
|
@@ -10111,10 +10253,11 @@ function createDreamTaskExecutor(deps) {
|
|
|
10111
10253
|
fallbackModels: config.fallbackModels,
|
|
10112
10254
|
language: config.language ?? deps.language,
|
|
10113
10255
|
moduleRoute,
|
|
10114
|
-
onProgress: (processed2) => reportProgress(processed2)
|
|
10256
|
+
onProgress: (processed2, refused) => reportProgress(processed2, refused)
|
|
10115
10257
|
});
|
|
10116
|
-
const processed = result.verified + result.updated + result.archived;
|
|
10117
|
-
const
|
|
10258
|
+
const processed = result.verified + result.updated + result.archived + result.skipped + result.refused;
|
|
10259
|
+
const verificationProgress = `${config.task}${config.task === "verify-broad" ? ` cycle ${result.broadCycleStartAt ?? "open"}` : ""}: processed ${processed} (verified ${result.verified}, updated ${result.updated}, archived ${result.archived}, skipped ${result.skipped}, refused ${result.refused}); ${result.remaining} remain`;
|
|
10260
|
+
const broadProgress = config.task === "verify-broad" ? verificationProgress : null;
|
|
10118
10261
|
const backlogAfter = config.task === "verify-broad" ? { pending: result.remaining, total: backlogAtStart.total } : undefined;
|
|
10119
10262
|
if (!result.complete) {
|
|
10120
10263
|
if (broadProgress && processed > 0) {
|
|
@@ -10127,13 +10270,14 @@ function createDreamTaskExecutor(deps) {
|
|
|
10127
10270
|
}
|
|
10128
10271
|
const error = incompleteMessage(result.remaining);
|
|
10129
10272
|
recordRun("failed", error, {
|
|
10273
|
+
progress: verificationProgress,
|
|
10130
10274
|
memoryChanges: computeMemoryDelta(memoryBefore),
|
|
10131
10275
|
backlogAfter
|
|
10132
10276
|
});
|
|
10133
10277
|
return { status: "failed", transient: true, error };
|
|
10134
10278
|
}
|
|
10135
10279
|
recordRun("completed", null, {
|
|
10136
|
-
progress:
|
|
10280
|
+
progress: verificationProgress,
|
|
10137
10281
|
memoryChanges: computeMemoryDelta(memoryBefore),
|
|
10138
10282
|
backlogAfter
|
|
10139
10283
|
});
|
|
@@ -10163,6 +10307,7 @@ function createDreamTaskExecutor(deps) {
|
|
|
10163
10307
|
leaseAcquisition,
|
|
10164
10308
|
model: config.model,
|
|
10165
10309
|
fallbackModels: config.fallbackModels,
|
|
10310
|
+
language: config.language ?? deps.language,
|
|
10166
10311
|
...moduleArgs,
|
|
10167
10312
|
onProgress: (processed) => reportProgress(processed)
|
|
10168
10313
|
});
|
|
@@ -11014,6 +11159,7 @@ var DREAM_TIMER_INTERVAL_MS = 15 * 60 * 1000;
|
|
|
11014
11159
|
var GIT_COMMIT_BACKLOG_DRAIN_MAX_MS = 5 * 60 * 1000;
|
|
11015
11160
|
var BOOT_PROJECT_JITTER_SLOT_MS = 1000;
|
|
11016
11161
|
var activeTimer = null;
|
|
11162
|
+
var startupTickTimer = null;
|
|
11017
11163
|
var startupTimers = new Map;
|
|
11018
11164
|
var startupJitters = new Map;
|
|
11019
11165
|
var nextStartupJitterSlot = 0;
|
|
@@ -11039,6 +11185,23 @@ function openTimerDatabaseOrNull(context) {
|
|
|
11039
11185
|
return db;
|
|
11040
11186
|
}
|
|
11041
11187
|
var registeredProjects = new Map;
|
|
11188
|
+
function stopDreamScheduleTimerIfIdle() {
|
|
11189
|
+
if (registeredProjects.size !== 0)
|
|
11190
|
+
return;
|
|
11191
|
+
const stopped = activeTimer !== null || startupTickTimer !== null;
|
|
11192
|
+
if (activeTimer) {
|
|
11193
|
+
clearInterval(activeTimer);
|
|
11194
|
+
activeTimer = null;
|
|
11195
|
+
}
|
|
11196
|
+
if (startupTickTimer) {
|
|
11197
|
+
clearTimeout(startupTickTimer);
|
|
11198
|
+
startupTickTimer = null;
|
|
11199
|
+
}
|
|
11200
|
+
startupJitters.clear();
|
|
11201
|
+
nextStartupJitterSlot = 0;
|
|
11202
|
+
if (stopped)
|
|
11203
|
+
log("[dreamer] stopped dream schedule timer (no projects left)");
|
|
11204
|
+
}
|
|
11042
11205
|
async function startDreamScheduleTimer(args) {
|
|
11043
11206
|
beginBootQuietPeriod();
|
|
11044
11207
|
const db = openTimerDatabaseOrNull("schedule timer registration");
|
|
@@ -11062,7 +11225,10 @@ async function startDreamScheduleTimer(args) {
|
|
|
11062
11225
|
}
|
|
11063
11226
|
if (!activeTimer) {
|
|
11064
11227
|
log(`[dreamer] started independent schedule timer (every ${DREAM_TIMER_INTERVAL_MS / 60000}m)`);
|
|
11065
|
-
scheduleAfterBootQuiet(() =>
|
|
11228
|
+
startupTickTimer = scheduleAfterBootQuiet(() => {
|
|
11229
|
+
startupTickTimer = null;
|
|
11230
|
+
runTick("startup");
|
|
11231
|
+
});
|
|
11066
11232
|
const timer = setInterval(() => runTick("interval"), DREAM_TIMER_INTERVAL_MS);
|
|
11067
11233
|
if (typeof timer === "object" && "unref" in timer) {
|
|
11068
11234
|
timer.unref();
|
|
@@ -11072,6 +11238,10 @@ async function startDreamScheduleTimer(args) {
|
|
|
11072
11238
|
scheduleInitialProjectRun(args, db);
|
|
11073
11239
|
}
|
|
11074
11240
|
return () => {
|
|
11241
|
+
if (registeredProjects.get(args.directory) !== args) {
|
|
11242
|
+
stopDreamScheduleTimerIfIdle();
|
|
11243
|
+
return;
|
|
11244
|
+
}
|
|
11075
11245
|
registeredProjects.delete(args.directory);
|
|
11076
11246
|
const startupTimer = startupTimers.get(args.directory);
|
|
11077
11247
|
if (startupTimer) {
|
|
@@ -11079,13 +11249,7 @@ async function startDreamScheduleTimer(args) {
|
|
|
11079
11249
|
startupTimers.delete(args.directory);
|
|
11080
11250
|
}
|
|
11081
11251
|
log(`[dreamer] unregistered project ${args.projectIdentity} (remaining=${registeredProjects.size})`);
|
|
11082
|
-
|
|
11083
|
-
clearInterval(activeTimer);
|
|
11084
|
-
activeTimer = null;
|
|
11085
|
-
startupJitters.clear();
|
|
11086
|
-
nextStartupJitterSlot = 0;
|
|
11087
|
-
log("[dreamer] stopped dream schedule timer (no projects left)");
|
|
11088
|
-
}
|
|
11252
|
+
stopDreamScheduleTimerIfIdle();
|
|
11089
11253
|
};
|
|
11090
11254
|
}
|
|
11091
11255
|
function runTick(origin) {
|
|
@@ -11170,7 +11334,15 @@ async function sweepProject(reg, origin, db, gitCommitEnabled) {
|
|
|
11170
11334
|
log(`[dreamer] orphan schedule GC failed for ${reg.projectIdentity}:`, error);
|
|
11171
11335
|
}
|
|
11172
11336
|
}
|
|
11173
|
-
registeredProjects.
|
|
11337
|
+
if (registeredProjects.get(reg.directory) === reg) {
|
|
11338
|
+
registeredProjects.delete(reg.directory);
|
|
11339
|
+
const startupTimer = startupTimers.get(reg.directory);
|
|
11340
|
+
if (startupTimer) {
|
|
11341
|
+
clearTimeout(startupTimer);
|
|
11342
|
+
startupTimers.delete(reg.directory);
|
|
11343
|
+
}
|
|
11344
|
+
}
|
|
11345
|
+
stopDreamScheduleTimerIfIdle();
|
|
11174
11346
|
return;
|
|
11175
11347
|
}
|
|
11176
11348
|
await reg.ensureRegistered(reg.directory, db);
|
|
@@ -11350,25 +11522,145 @@ async function sweepGitCommits(args) {
|
|
|
11350
11522
|
}
|
|
11351
11523
|
|
|
11352
11524
|
// src/dreamer/pi-session-api.ts
|
|
11353
|
-
import { readFileSync as readFileSync4 } from "node:fs";
|
|
11354
|
-
import {
|
|
11525
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4, realpathSync as realpathSync2, statSync as statSync3 } from "node:fs";
|
|
11526
|
+
import {
|
|
11527
|
+
basename as basename2,
|
|
11528
|
+
dirname as dirname2,
|
|
11529
|
+
extname,
|
|
11530
|
+
join as join4,
|
|
11531
|
+
relative,
|
|
11532
|
+
resolve,
|
|
11533
|
+
sep
|
|
11534
|
+
} from "node:path";
|
|
11355
11535
|
import { pathToFileURL } from "node:url";
|
|
11356
11536
|
var PI_CODING_AGENT_MODULE = "@earendil-works/pi-coding-agent";
|
|
11537
|
+
var SCRIPT_ENTRY_PATTERN = /\.(mjs|cjs|mts|cts|js|ts|tsx|jsx)$/i;
|
|
11538
|
+
var TS_ENTRY_PATTERN = /\.(mts|cts|ts|tsx)$/i;
|
|
11539
|
+
function isScriptEntry(filePath) {
|
|
11540
|
+
return SCRIPT_ENTRY_PATTERN.test(filePath) || extname(filePath) === "";
|
|
11541
|
+
}
|
|
11542
|
+
function readManifest(pkgJsonPath) {
|
|
11543
|
+
try {
|
|
11544
|
+
const pkg = JSON.parse(readFileSync4(pkgJsonPath, "utf8"));
|
|
11545
|
+
return pkg && typeof pkg === "object" ? pkg : null;
|
|
11546
|
+
} catch {
|
|
11547
|
+
return null;
|
|
11548
|
+
}
|
|
11549
|
+
}
|
|
11550
|
+
function isCompiledBunBinary() {
|
|
11551
|
+
if (!process.versions.bun) {
|
|
11552
|
+
return false;
|
|
11553
|
+
}
|
|
11554
|
+
const exe = basename2(process.execPath).toLowerCase();
|
|
11555
|
+
return exe !== "bun" && exe !== "bun.exe";
|
|
11556
|
+
}
|
|
11557
|
+
function resolveRunningEntry() {
|
|
11558
|
+
const argv1 = process.argv[1];
|
|
11559
|
+
if (argv1 && existsSync8(argv1) && statSync3(argv1).isFile()) {
|
|
11560
|
+
const real = realpathSync2(argv1);
|
|
11561
|
+
if (isScriptEntry(real)) {
|
|
11562
|
+
return real;
|
|
11563
|
+
}
|
|
11564
|
+
}
|
|
11565
|
+
return null;
|
|
11566
|
+
}
|
|
11567
|
+
function findPackageRoot(startDir) {
|
|
11568
|
+
let dir = startDir;
|
|
11569
|
+
while (dir !== dirname2(dir)) {
|
|
11570
|
+
const pkg = readManifest(join4(dir, "package.json"));
|
|
11571
|
+
if (pkg?.name === PI_CODING_AGENT_MODULE) {
|
|
11572
|
+
if (basename2(dir) === "dist") {
|
|
11573
|
+
const parentDir = dirname2(dir);
|
|
11574
|
+
const parentPkg = readManifest(join4(parentDir, "package.json"));
|
|
11575
|
+
if (parentPkg?.name === PI_CODING_AGENT_MODULE) {
|
|
11576
|
+
return { dir: parentDir, pkg: parentPkg };
|
|
11577
|
+
}
|
|
11578
|
+
}
|
|
11579
|
+
return { dir, pkg };
|
|
11580
|
+
}
|
|
11581
|
+
dir = dirname2(dir);
|
|
11582
|
+
}
|
|
11583
|
+
return null;
|
|
11584
|
+
}
|
|
11585
|
+
function resolveExportsTarget(target) {
|
|
11586
|
+
if (typeof target === "string") {
|
|
11587
|
+
return target;
|
|
11588
|
+
}
|
|
11589
|
+
if (Array.isArray(target)) {
|
|
11590
|
+
for (const item of target) {
|
|
11591
|
+
const resolved = resolveExportsTarget(item);
|
|
11592
|
+
if (resolved !== undefined) {
|
|
11593
|
+
return resolved;
|
|
11594
|
+
}
|
|
11595
|
+
}
|
|
11596
|
+
return;
|
|
11597
|
+
}
|
|
11598
|
+
if (target && typeof target === "object") {
|
|
11599
|
+
const conditions = target;
|
|
11600
|
+
for (const condition of ["import", "node", "default"]) {
|
|
11601
|
+
if (condition in conditions) {
|
|
11602
|
+
const resolved = resolveExportsTarget(conditions[condition]);
|
|
11603
|
+
if (resolved !== undefined) {
|
|
11604
|
+
return resolved;
|
|
11605
|
+
}
|
|
11606
|
+
}
|
|
11607
|
+
}
|
|
11608
|
+
}
|
|
11609
|
+
return;
|
|
11610
|
+
}
|
|
11611
|
+
function resolveManifestEntry(found) {
|
|
11612
|
+
const pkg = found.pkg;
|
|
11613
|
+
const entryRel = resolveExportsTarget(pkg.exports?.["."]) ?? (typeof pkg.module === "string" ? pkg.module : undefined) ?? (typeof pkg.main === "string" ? pkg.main : undefined);
|
|
11614
|
+
if (!entryRel) {
|
|
11615
|
+
throw new Error(`No ESM entry found in ${found.dir}/package.json`);
|
|
11616
|
+
}
|
|
11617
|
+
if (!entryRel.startsWith("./")) {
|
|
11618
|
+
throw new Error(`Invalid entry "${entryRel}" in ${found.dir}/package.json: targets must start with "./"`);
|
|
11619
|
+
}
|
|
11620
|
+
const root = resolve(found.dir);
|
|
11621
|
+
const resolved = resolve(root, entryRel);
|
|
11622
|
+
if (resolved !== root && !resolved.startsWith(root + sep)) {
|
|
11623
|
+
throw new Error(`Invalid entry "${entryRel}" in ${found.dir}/package.json: target escapes the package root`);
|
|
11624
|
+
}
|
|
11625
|
+
return resolved;
|
|
11626
|
+
}
|
|
11627
|
+
function toSourceEntry(entryPath, pkgDir) {
|
|
11628
|
+
const rel = relative(pkgDir, entryPath);
|
|
11629
|
+
const srcRel = rel.replace(/^dist[/\\]/, "src/").replace(/\.mjs$/, ".mts").replace(/\.cjs$/, ".cts").replace(/\.jsx$/, ".tsx").replace(/\.js$/, ".ts");
|
|
11630
|
+
if (srcRel === rel) {
|
|
11631
|
+
return null;
|
|
11632
|
+
}
|
|
11633
|
+
return join4(pkgDir, srcRel);
|
|
11634
|
+
}
|
|
11357
11635
|
var defaultLoaders = [
|
|
11358
|
-
{
|
|
11359
|
-
name: "Bare import",
|
|
11360
|
-
load: async () => await import(PI_CODING_AGENT_MODULE)
|
|
11361
|
-
},
|
|
11362
11636
|
{
|
|
11363
11637
|
name: "Resolve from running Pi binary entry",
|
|
11364
11638
|
load: async () => {
|
|
11365
|
-
if (
|
|
11366
|
-
throw new Error("
|
|
11639
|
+
if (isCompiledBunBinary()) {
|
|
11640
|
+
throw new Error("Running inside a compiled Bun binary: pi-coding-agent is embedded in the executable and argv[1] is a user CLI argument, so there is no on-disk package entry to resolve");
|
|
11641
|
+
}
|
|
11642
|
+
const entry = resolveRunningEntry() ?? process.execPath;
|
|
11643
|
+
if (!entry) {
|
|
11644
|
+
throw new Error("Neither process.argv[1] nor process.execPath is available");
|
|
11645
|
+
}
|
|
11646
|
+
const found = findPackageRoot(dirname2(entry));
|
|
11647
|
+
if (!found) {
|
|
11648
|
+
throw new Error(`Could not locate ${PI_CODING_AGENT_MODULE} package.json from ${entry}`);
|
|
11649
|
+
}
|
|
11650
|
+
const entryPath = resolveManifestEntry(found);
|
|
11651
|
+
if (TS_ENTRY_PATTERN.test(entry)) {
|
|
11652
|
+
const srcEntry = toSourceEntry(entryPath, found.dir);
|
|
11653
|
+
if (srcEntry && existsSync8(srcEntry)) {
|
|
11654
|
+
return await import(pathToFileURL(srcEntry).href);
|
|
11655
|
+
}
|
|
11656
|
+
throw new Error(`Pi is running from TypeScript source (${entry}) but no source counterpart of ${entryPath} exists; refusing to load possibly stale build output`);
|
|
11367
11657
|
}
|
|
11368
|
-
|
|
11369
|
-
const resolved = require2.resolve(PI_CODING_AGENT_MODULE);
|
|
11370
|
-
return await import(pathToFileURL(resolved).href);
|
|
11658
|
+
return await import(pathToFileURL(entryPath).href);
|
|
11371
11659
|
}
|
|
11660
|
+
},
|
|
11661
|
+
{
|
|
11662
|
+
name: "Bare import",
|
|
11663
|
+
load: async () => await import(PI_CODING_AGENT_MODULE)
|
|
11372
11664
|
}
|
|
11373
11665
|
];
|
|
11374
11666
|
var cachedModulePromise = null;
|
|
@@ -11454,7 +11746,7 @@ function createPiPrimerRawProviderFactory(deps = {}) {
|
|
|
11454
11746
|
}
|
|
11455
11747
|
|
|
11456
11748
|
// src/dreamer/retrospective-raw-provider-pi.ts
|
|
11457
|
-
import { resolve } from "node:path";
|
|
11749
|
+
import { resolve as resolve2 } from "node:path";
|
|
11458
11750
|
class PiRetrospectiveRawProvider {
|
|
11459
11751
|
deps;
|
|
11460
11752
|
sessionPathById = new Map;
|
|
@@ -11465,7 +11757,7 @@ class PiRetrospectiveRawProvider {
|
|
|
11465
11757
|
async listProjectSessions(_projectIdentity) {
|
|
11466
11758
|
const deps = await this.resolveDeps();
|
|
11467
11759
|
const sessions = await deps.listSessions(this.deps.sessionDir);
|
|
11468
|
-
const projectCwd =
|
|
11760
|
+
const projectCwd = resolve2(this.deps.projectCwd);
|
|
11469
11761
|
const result = [];
|
|
11470
11762
|
this.sessionPathById.clear();
|
|
11471
11763
|
for (const raw of sessions) {
|
|
@@ -11474,7 +11766,7 @@ class PiRetrospectiveRawProvider {
|
|
|
11474
11766
|
continue;
|
|
11475
11767
|
if (typeof info.id !== "string" || typeof info.path !== "string")
|
|
11476
11768
|
continue;
|
|
11477
|
-
if (typeof info.cwd !== "string" ||
|
|
11769
|
+
if (typeof info.cwd !== "string" || resolve2(info.cwd) !== projectCwd)
|
|
11478
11770
|
continue;
|
|
11479
11771
|
this.sessionPathById.set(info.id, info.path);
|
|
11480
11772
|
result.push({
|
|
@@ -11568,9 +11860,30 @@ function extractPiTextContent(content) {
|
|
|
11568
11860
|
}
|
|
11569
11861
|
|
|
11570
11862
|
// src/dreamer/index.ts
|
|
11571
|
-
var
|
|
11863
|
+
var PI_DREAMER_PROJECTS = Symbol.for("magic-context.pi.dreamer-registered-projects");
|
|
11864
|
+
function getRegisteredProjects() {
|
|
11865
|
+
const globals = globalThis;
|
|
11866
|
+
const existing = globals[PI_DREAMER_PROJECTS];
|
|
11867
|
+
if (existing instanceof Map) {
|
|
11868
|
+
return existing;
|
|
11869
|
+
}
|
|
11870
|
+
const projects = new Map;
|
|
11871
|
+
globals[PI_DREAMER_PROJECTS] = projects;
|
|
11872
|
+
return projects;
|
|
11873
|
+
}
|
|
11874
|
+
var registeredProjects2 = getRegisteredProjects();
|
|
11572
11875
|
var sessionsById = new Map;
|
|
11573
|
-
var
|
|
11876
|
+
var PI_DREAMER_IN_FLIGHT = Symbol.for("magic-context.pi.dreamer-in-flight");
|
|
11877
|
+
var inFlightDreams = (() => {
|
|
11878
|
+
const globals = globalThis;
|
|
11879
|
+
const existing = globals[PI_DREAMER_IN_FLIGHT];
|
|
11880
|
+
if (existing instanceof Map) {
|
|
11881
|
+
return existing;
|
|
11882
|
+
}
|
|
11883
|
+
const dreams = new Map;
|
|
11884
|
+
globals[PI_DREAMER_IN_FLIGHT] = dreams;
|
|
11885
|
+
return dreams;
|
|
11886
|
+
})();
|
|
11574
11887
|
var sessionCounter = 0;
|
|
11575
11888
|
var piSubagentRunnerFactory = () => new PiSubagentRunner;
|
|
11576
11889
|
var startDreamScheduleTimerFn = startDreamScheduleTimer;
|
|
@@ -11579,14 +11892,26 @@ function registerPiDreamerProject(opts) {
|
|
|
11579
11892
|
return;
|
|
11580
11893
|
}
|
|
11581
11894
|
const existing = registeredProjects2.get(opts.projectIdentity);
|
|
11895
|
+
const owners = existing?.owners ?? new Map;
|
|
11896
|
+
owners.delete(opts.registrationOwner);
|
|
11897
|
+
owners.set(opts.registrationOwner, opts);
|
|
11898
|
+
const notifyOwnersOfAdjunctRefresh = (projectIdentity) => {
|
|
11899
|
+
const callbacks = new Set([...owners.values()].map((owner) => owner.onAdjunctsRefreshNeeded).filter((callback) => callback !== undefined));
|
|
11900
|
+
for (const callback of callbacks)
|
|
11901
|
+
callback(projectIdentity);
|
|
11902
|
+
};
|
|
11582
11903
|
if (existing) {
|
|
11583
|
-
if (existing.projectDir === opts.projectDir) {
|
|
11904
|
+
if (existing.generation && existing.projectDir === opts.projectDir) {
|
|
11584
11905
|
return;
|
|
11585
11906
|
}
|
|
11586
11907
|
existing.cleanup();
|
|
11587
11908
|
registeredProjects2.delete(opts.projectIdentity);
|
|
11588
11909
|
}
|
|
11589
|
-
const
|
|
11910
|
+
const generation = {};
|
|
11911
|
+
const client = createPiDreamerClient(opts, notifyOwnersOfAdjunctRefresh, () => {
|
|
11912
|
+
const current = registeredProjects2.get(opts.projectIdentity);
|
|
11913
|
+
return current?.generation === generation && current.owners.get(opts.registrationOwner)?.projectDir === opts.projectDir;
|
|
11914
|
+
});
|
|
11590
11915
|
let cleanup;
|
|
11591
11916
|
let cancelled = false;
|
|
11592
11917
|
startDreamScheduleTimerFn({
|
|
@@ -11604,32 +11929,50 @@ function registerPiDreamerProject(opts) {
|
|
|
11604
11929
|
primerRawProviderFactory: createPiPrimerRawProviderFactory()
|
|
11605
11930
|
}).then((timerCleanup) => {
|
|
11606
11931
|
if (cancelled) {
|
|
11607
|
-
|
|
11932
|
+
if (registeredProjects2.get(opts.projectIdentity)?.generation !== generation) {
|
|
11933
|
+
timerCleanup?.();
|
|
11934
|
+
}
|
|
11608
11935
|
return;
|
|
11609
11936
|
}
|
|
11610
11937
|
cleanup = timerCleanup;
|
|
11611
11938
|
});
|
|
11612
|
-
const runManual = async (task) =>
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
|
|
11939
|
+
const runManual = async (task, registrationOwner) => {
|
|
11940
|
+
const manualOpts = owners.get(registrationOwner);
|
|
11941
|
+
if (!manualOpts) {
|
|
11942
|
+
throw new Error(`Pi dreamer registration owner is no longer active for project ${opts.projectIdentity}`);
|
|
11943
|
+
}
|
|
11944
|
+
const manualClient = createPiDreamerClient(manualOpts, notifyOwnersOfAdjunctRefresh, () => owners.get(manualOpts.registrationOwner)?.projectDir === manualOpts.projectDir);
|
|
11945
|
+
const manualRun = runManualDream({
|
|
11946
|
+
db: manualOpts.db,
|
|
11947
|
+
projectIdentity: manualOpts.projectIdentity,
|
|
11948
|
+
tasks: buildDreamTaskRuntimeConfigs(manualOpts.config, "pi", manualOpts.language, manualOpts.mural?.model),
|
|
11949
|
+
executor: createDreamTaskExecutor({
|
|
11950
|
+
client: manualClient,
|
|
11951
|
+
sessionDirectory: manualOpts.projectDir,
|
|
11952
|
+
openOpenCodeDb,
|
|
11953
|
+
retrospectiveRawProvider: new PiRetrospectiveRawProvider({
|
|
11954
|
+
projectCwd: manualOpts.projectDir
|
|
11955
|
+
}),
|
|
11956
|
+
primerRawProviderFactory: createPiPrimerRawProviderFactory(),
|
|
11957
|
+
userMemoryCollectionEnabled: userMemoryCollectionEnabled(manualOpts.config),
|
|
11958
|
+
ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
|
|
11959
|
+
language: manualOpts.language,
|
|
11960
|
+
retinaHandoff: manualOpts.retinaHandoff,
|
|
11961
|
+
mural: manualOpts.mural
|
|
11622
11962
|
}),
|
|
11623
|
-
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
11627
|
-
|
|
11628
|
-
|
|
11629
|
-
|
|
11630
|
-
|
|
11631
|
-
}
|
|
11963
|
+
task
|
|
11964
|
+
});
|
|
11965
|
+
inFlightDreams.set(manualRun, manualOpts.registrationOwner);
|
|
11966
|
+
try {
|
|
11967
|
+
return await manualRun;
|
|
11968
|
+
} finally {
|
|
11969
|
+
inFlightDreams.delete(manualRun);
|
|
11970
|
+
}
|
|
11971
|
+
};
|
|
11632
11972
|
registeredProjects2.set(opts.projectIdentity, {
|
|
11973
|
+
generation,
|
|
11974
|
+
activeOwner: opts.registrationOwner,
|
|
11975
|
+
owners,
|
|
11633
11976
|
cleanup: () => {
|
|
11634
11977
|
cancelled = true;
|
|
11635
11978
|
cleanup?.();
|
|
@@ -11638,31 +11981,55 @@ function registerPiDreamerProject(opts) {
|
|
|
11638
11981
|
projectDir: opts.projectDir
|
|
11639
11982
|
});
|
|
11640
11983
|
}
|
|
11641
|
-
async function runPiDreamForProject(projectIdentity, task) {
|
|
11984
|
+
async function runPiDreamForProject(projectIdentity, task, registrationOwner) {
|
|
11642
11985
|
const registration = registeredProjects2.get(projectIdentity);
|
|
11643
11986
|
if (!registration) {
|
|
11644
11987
|
throw new Error(`Pi dreamer not registered for project ${projectIdentity}; call registerPiDreamerProject() first`);
|
|
11645
11988
|
}
|
|
11646
|
-
return registration.runManual(task);
|
|
11989
|
+
return registration.runManual(task, registrationOwner);
|
|
11647
11990
|
}
|
|
11648
11991
|
function unregisterPiDreamerProject(opts) {
|
|
11649
11992
|
const registration = registeredProjects2.get(opts.projectIdentity);
|
|
11650
|
-
if (!registration) {
|
|
11993
|
+
if (!registration?.owners.delete(opts.registrationOwner)) {
|
|
11994
|
+
return;
|
|
11995
|
+
}
|
|
11996
|
+
if (registration.owners.size > 0) {
|
|
11997
|
+
if (registration.activeOwner !== opts.registrationOwner)
|
|
11998
|
+
return;
|
|
11999
|
+
const remaining = [...registration.owners.values()];
|
|
12000
|
+
const replacementOptions = remaining[remaining.length - 1];
|
|
12001
|
+
if (!replacementOptions)
|
|
12002
|
+
return;
|
|
12003
|
+
registration.cleanup();
|
|
12004
|
+
registeredProjects2.delete(opts.projectIdentity);
|
|
12005
|
+
registerPiDreamerProject(replacementOptions);
|
|
12006
|
+
const replacement = registeredProjects2.get(opts.projectIdentity);
|
|
12007
|
+
if (replacement) {
|
|
12008
|
+
for (const remainingOptions of remaining) {
|
|
12009
|
+
replacement.owners.set(remainingOptions.registrationOwner, remainingOptions);
|
|
12010
|
+
}
|
|
12011
|
+
}
|
|
11651
12012
|
return;
|
|
11652
12013
|
}
|
|
11653
12014
|
registration.cleanup();
|
|
11654
12015
|
registeredProjects2.delete(opts.projectIdentity);
|
|
11655
12016
|
}
|
|
11656
|
-
async function awaitInFlightDreamers() {
|
|
11657
|
-
|
|
12017
|
+
async function awaitInFlightDreamers(registrationOwner) {
|
|
12018
|
+
const runs = registrationOwner === undefined ? [...inFlightDreams.keys()] : [...inFlightDreams.entries()].filter(([, owner]) => owner === registrationOwner).map(([run]) => run);
|
|
12019
|
+
if (runs.length === 0)
|
|
11658
12020
|
return;
|
|
11659
|
-
|
|
11660
|
-
await Promise.allSettled(Array.from(inFlightDreams));
|
|
12021
|
+
await Promise.allSettled(runs);
|
|
11661
12022
|
}
|
|
11662
|
-
function createPiDreamerClient(opts) {
|
|
12023
|
+
function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRefreshNeeded, isRegistrationOwnerActive = () => true) {
|
|
11663
12024
|
const runner = piSubagentRunnerFactory();
|
|
12025
|
+
const assertRegistrationOwnerActive = () => {
|
|
12026
|
+
if (!isRegistrationOwnerActive()) {
|
|
12027
|
+
throw new Error(`Pi dreamer registration is no longer active for project ${opts.projectIdentity}`);
|
|
12028
|
+
}
|
|
12029
|
+
};
|
|
11664
12030
|
const session = {
|
|
11665
12031
|
create: async (args) => {
|
|
12032
|
+
assertRegistrationOwnerActive();
|
|
11666
12033
|
const sessionId = `magic-context-pi-dream-${++sessionCounter}`;
|
|
11667
12034
|
sessionsById.set(sessionId, {
|
|
11668
12035
|
id: sessionId,
|
|
@@ -11679,6 +12046,7 @@ function createPiDreamerClient(opts) {
|
|
|
11679
12046
|
if (!dreamSession) {
|
|
11680
12047
|
throw new Error(`Pi dreamer session not found: ${sessionId}`);
|
|
11681
12048
|
}
|
|
12049
|
+
assertRegistrationOwnerActive();
|
|
11682
12050
|
const userMessage = extractUserMessage(args);
|
|
11683
12051
|
const systemPrompt = extractSystemPrompt(args);
|
|
11684
12052
|
const perTaskModel = extractBodyModel(args);
|
|
@@ -11694,9 +12062,10 @@ function createPiDreamerClient(opts) {
|
|
|
11694
12062
|
signal: args.signal ?? undefined,
|
|
11695
12063
|
thinkingLevel: extractBodyVariant(args)
|
|
11696
12064
|
});
|
|
11697
|
-
inFlightDreams.
|
|
12065
|
+
inFlightDreams.set(runPromise, opts.registrationOwner);
|
|
11698
12066
|
try {
|
|
11699
12067
|
const result = await runPromise;
|
|
12068
|
+
assertRegistrationOwnerActive();
|
|
11700
12069
|
if (!result.ok) {
|
|
11701
12070
|
const error = new Error(`Pi dreamer subagent failed (${result.reason}): ${result.error}`);
|
|
11702
12071
|
if (result.transient) {
|
|
@@ -11711,12 +12080,13 @@ function createPiDreamerClient(opts) {
|
|
|
11711
12080
|
{ type: "text", text: result.assistantText }
|
|
11712
12081
|
])
|
|
11713
12082
|
];
|
|
11714
|
-
|
|
12083
|
+
onAdjunctsRefreshNeeded?.(opts.projectIdentity);
|
|
11715
12084
|
} finally {
|
|
11716
12085
|
inFlightDreams.delete(runPromise);
|
|
11717
12086
|
}
|
|
11718
12087
|
},
|
|
11719
12088
|
messages: async (args) => {
|
|
12089
|
+
assertRegistrationOwnerActive();
|
|
11720
12090
|
const dreamSession = sessionsById.get(args.path.id);
|
|
11721
12091
|
return { data: dreamSession?.messages ?? [] };
|
|
11722
12092
|
},
|
|
@@ -11816,8 +12186,15 @@ function makeMessage(role, parts) {
|
|
|
11816
12186
|
}
|
|
11817
12187
|
|
|
11818
12188
|
// src/commands/pi-command-utils.ts
|
|
11819
|
-
import { Box, Text } from "@earendil-works/pi-tui";
|
|
12189
|
+
import { Box, matchesKey, Text } from "@earendil-works/pi-tui";
|
|
11820
12190
|
var CTX_STATUS_CUSTOM_TYPE = "ctx-status";
|
|
12191
|
+
var statusLifecycleSignals = new WeakMap;
|
|
12192
|
+
function registerCtxStatusLifecycleSignal(pi, signal) {
|
|
12193
|
+
statusLifecycleSignals.set(pi, signal);
|
|
12194
|
+
}
|
|
12195
|
+
function shouldShowCtxStatusDialog(content) {
|
|
12196
|
+
return content.rpcDisplay === "dialog" || content.rpcDisplay !== "notification" && content.level !== undefined && content.level !== "info";
|
|
12197
|
+
}
|
|
11821
12198
|
function resolveSessionId(ctx) {
|
|
11822
12199
|
const sm = ctx.sessionManager;
|
|
11823
12200
|
const getSessionId = sm.getSessionId;
|
|
@@ -11830,6 +12207,41 @@ function resolveSessionId(ctx) {
|
|
|
11830
12207
|
return;
|
|
11831
12208
|
}
|
|
11832
12209
|
}
|
|
12210
|
+
async function showCtxStatusDialog(ctx, content) {
|
|
12211
|
+
let factoryInvoked = false;
|
|
12212
|
+
await ctx.ui.custom((_tui, theme, _keybindings, done) => {
|
|
12213
|
+
factoryInvoked = true;
|
|
12214
|
+
return new CtxStatusDialog(content, theme, done);
|
|
12215
|
+
}, { overlay: true, overlayOptions: { anchor: "center", width: 92 } });
|
|
12216
|
+
return factoryInvoked;
|
|
12217
|
+
}
|
|
12218
|
+
|
|
12219
|
+
class CtxStatusDialog {
|
|
12220
|
+
content;
|
|
12221
|
+
theme;
|
|
12222
|
+
done;
|
|
12223
|
+
constructor(content, theme, done) {
|
|
12224
|
+
this.content = content;
|
|
12225
|
+
this.theme = theme;
|
|
12226
|
+
this.done = done;
|
|
12227
|
+
}
|
|
12228
|
+
handleInput(data) {
|
|
12229
|
+
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c") || matchesKey(data, "return")) {
|
|
12230
|
+
this.done(undefined);
|
|
12231
|
+
}
|
|
12232
|
+
}
|
|
12233
|
+
invalidate() {}
|
|
12234
|
+
render(_width) {
|
|
12235
|
+
return [
|
|
12236
|
+
this.theme.bold(this.theme.fg(statusTitleColor(this.content.level), `[${this.content.title}]`)),
|
|
12237
|
+
"",
|
|
12238
|
+
...this.content.text.split(`
|
|
12239
|
+
`),
|
|
12240
|
+
"",
|
|
12241
|
+
this.theme.fg("dim", "Press Enter or Escape to close")
|
|
12242
|
+
];
|
|
12243
|
+
}
|
|
12244
|
+
}
|
|
11833
12245
|
function statusTitleColor(level) {
|
|
11834
12246
|
switch (level) {
|
|
11835
12247
|
case "success":
|
|
@@ -11864,7 +12276,37 @@ function registerCtxStatusEntryRenderer(pi) {
|
|
|
11864
12276
|
return false;
|
|
11865
12277
|
}
|
|
11866
12278
|
}
|
|
11867
|
-
function
|
|
12279
|
+
async function presentCtxStatusMessage(ctx, content, signal) {
|
|
12280
|
+
if (ctx.mode !== "rpc" || signal?.aborted)
|
|
12281
|
+
return;
|
|
12282
|
+
const type = content.level === "error" || content.level === "warning" ? content.level : "info";
|
|
12283
|
+
if (!shouldShowCtxStatusDialog(content)) {
|
|
12284
|
+
if (!signal?.aborted)
|
|
12285
|
+
ctx.ui.notify(content.text, type);
|
|
12286
|
+
return;
|
|
12287
|
+
}
|
|
12288
|
+
try {
|
|
12289
|
+
const shown = await showCtxStatusDialog(ctx, content);
|
|
12290
|
+
if (!shown && !signal?.aborted)
|
|
12291
|
+
ctx.ui.notify(content.text, type);
|
|
12292
|
+
} catch (err) {
|
|
12293
|
+
if (signal?.aborted)
|
|
12294
|
+
return;
|
|
12295
|
+
sessionLog("pi-status", `ctx status dialog failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
12296
|
+
ctx.ui.notify(content.text, type);
|
|
12297
|
+
}
|
|
12298
|
+
}
|
|
12299
|
+
function createCtxStatusSender(pi, ctx, signal) {
|
|
12300
|
+
const lifecycleSignal = statusLifecycleSignals.get(pi);
|
|
12301
|
+
const effectiveSignal = signal && lifecycleSignal ? AbortSignal.any([signal, lifecycleSignal]) : signal ?? lifecycleSignal;
|
|
12302
|
+
return (content, details) => {
|
|
12303
|
+
if (!effectiveSignal?.aborted)
|
|
12304
|
+
sendCtxStatusMessage(pi, content, details, ctx, effectiveSignal);
|
|
12305
|
+
};
|
|
12306
|
+
}
|
|
12307
|
+
function sendCtxStatusMessage(pi, content, details, ctx, signal) {
|
|
12308
|
+
if (signal?.aborted)
|
|
12309
|
+
return;
|
|
11868
12310
|
const data = {
|
|
11869
12311
|
...content,
|
|
11870
12312
|
details: details ?? content.details
|
|
@@ -11872,6 +12314,8 @@ function sendCtxStatusMessage(pi, content, details) {
|
|
|
11872
12314
|
if (typeof pi.appendEntry === "function") {
|
|
11873
12315
|
pi.appendEntry(CTX_STATUS_CUSTOM_TYPE, data);
|
|
11874
12316
|
}
|
|
12317
|
+
if (ctx)
|
|
12318
|
+
presentCtxStatusMessage(ctx, data, signal);
|
|
11875
12319
|
sessionLog("pi-status", `${content.title}: ${content.text}`);
|
|
11876
12320
|
}
|
|
11877
12321
|
|
|
@@ -11880,6 +12324,7 @@ function registerCtxDreamCommand(pi, deps) {
|
|
|
11880
12324
|
pi.registerCommand("ctx-dream", {
|
|
11881
12325
|
description: "Run Magic Context dreamer tasks for this project now",
|
|
11882
12326
|
handler: async (args, ctx) => {
|
|
12327
|
+
const sendStatus = createCtxStatusSender(pi, ctx);
|
|
11883
12328
|
const project = deps.resolveProject?.(ctx) ?? {
|
|
11884
12329
|
projectDir: deps.projectDir,
|
|
11885
12330
|
projectIdentity: deps.projectIdentity
|
|
@@ -11890,7 +12335,7 @@ function registerCtxDreamCommand(pi, deps) {
|
|
|
11890
12335
|
let task;
|
|
11891
12336
|
if (requested) {
|
|
11892
12337
|
if (!isCanonicalDreamTask(requested)) {
|
|
11893
|
-
|
|
12338
|
+
sendStatus({
|
|
11894
12339
|
title: "/ctx-dream",
|
|
11895
12340
|
text: `## /ctx-dream
|
|
11896
12341
|
|
|
@@ -11905,7 +12350,7 @@ Unknown task "${requested}".`,
|
|
|
11905
12350
|
task = requested;
|
|
11906
12351
|
}
|
|
11907
12352
|
if (dreamerEnabled === false) {
|
|
11908
|
-
|
|
12353
|
+
sendStatus({
|
|
11909
12354
|
title: "/ctx-dream",
|
|
11910
12355
|
text: "## /ctx-dream\n\nDreamer is disabled for this project (`dreamer.disable=true`).",
|
|
11911
12356
|
level: "info"
|
|
@@ -11917,7 +12362,7 @@ Unknown task "${requested}".`,
|
|
|
11917
12362
|
}
|
|
11918
12363
|
const backlogTasks = task ? [task] : CANONICAL_DREAM_TASKS;
|
|
11919
12364
|
const backlogBefore = getDreamTaskBacklogs(deps.db, project.projectIdentity, backlogTasks);
|
|
11920
|
-
|
|
12365
|
+
sendStatus({
|
|
11921
12366
|
title: "/ctx-dream",
|
|
11922
12367
|
text: [
|
|
11923
12368
|
"## /ctx-dream",
|
|
@@ -11935,7 +12380,8 @@ Unknown task "${requested}".`,
|
|
|
11935
12380
|
projectIdentity: project.projectIdentity
|
|
11936
12381
|
});
|
|
11937
12382
|
try {
|
|
11938
|
-
|
|
12383
|
+
await deps.ensureRegistered?.(ctx);
|
|
12384
|
+
const result = await runPiDreamForProject(project.projectIdentity, task, deps.registrationOwner);
|
|
11939
12385
|
const lines = [];
|
|
11940
12386
|
if (result.ran.length > 0)
|
|
11941
12387
|
lines.push(`Ran: ${result.ran.join(", ")}`);
|
|
@@ -11953,11 +12399,12 @@ Unknown task "${requested}".`,
|
|
|
11953
12399
|
}
|
|
11954
12400
|
if (lines.length === 0)
|
|
11955
12401
|
lines.push("No enabled dream tasks to run.");
|
|
11956
|
-
|
|
12402
|
+
sendStatus({
|
|
11957
12403
|
title: "/ctx-dream",
|
|
11958
12404
|
text: ["## /ctx-dream", "", ...lines].join(`
|
|
11959
12405
|
`),
|
|
11960
|
-
level: result.ran.length > 0 ? "success" : "info"
|
|
12406
|
+
level: result.ran.length > 0 ? "success" : "info",
|
|
12407
|
+
rpcDisplay: "dialog"
|
|
11961
12408
|
}, {
|
|
11962
12409
|
projectDir: project.projectDir,
|
|
11963
12410
|
projectIdentity: project.projectIdentity
|
|
@@ -11965,7 +12412,7 @@ Unknown task "${requested}".`,
|
|
|
11965
12412
|
} catch (error) {
|
|
11966
12413
|
const message = error instanceof Error ? error.message : String(error);
|
|
11967
12414
|
sessionLog(project.projectIdentity, `/ctx-dream failed: ${message}`);
|
|
11968
|
-
|
|
12415
|
+
sendStatus({
|
|
11969
12416
|
title: "/ctx-dream",
|
|
11970
12417
|
text: [
|
|
11971
12418
|
"## /ctx-dream",
|
|
@@ -12176,9 +12623,10 @@ function registerCtxEmbedCommand(pi, deps) {
|
|
|
12176
12623
|
pi.registerCommand("ctx-embed", {
|
|
12177
12624
|
description: "Embedding status, or start/pause history compartment embedding (start | pause)",
|
|
12178
12625
|
handler: async (args, ctx) => {
|
|
12626
|
+
const sendStatus = createCtxStatusSender(pi, ctx);
|
|
12179
12627
|
const sessionId = resolveSessionId(ctx);
|
|
12180
12628
|
if (!sessionId) {
|
|
12181
|
-
|
|
12629
|
+
sendStatus({
|
|
12182
12630
|
title: "/ctx-embed",
|
|
12183
12631
|
text: `## /ctx-embed
|
|
12184
12632
|
|
|
@@ -12199,7 +12647,7 @@ No active Pi session is available.`,
|
|
|
12199
12647
|
if (ctrl)
|
|
12200
12648
|
ctrl.abort();
|
|
12201
12649
|
const cov = getEmbeddingCoverageStatus(deps.db, project.projectIdentity, sessionId);
|
|
12202
|
-
|
|
12650
|
+
sendStatus({
|
|
12203
12651
|
title: "/ctx-embed",
|
|
12204
12652
|
text: `## /ctx-embed
|
|
12205
12653
|
|
|
@@ -12209,7 +12657,7 @@ Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`,
|
|
|
12209
12657
|
return;
|
|
12210
12658
|
}
|
|
12211
12659
|
if (memoryEnabled === false) {
|
|
12212
|
-
|
|
12660
|
+
sendStatus({
|
|
12213
12661
|
title: "/ctx-embed",
|
|
12214
12662
|
text: `## /ctx-embed
|
|
12215
12663
|
|
|
@@ -12221,16 +12669,16 @@ Memory is disabled for this project, so there is no semantic embedding to backfi
|
|
|
12221
12669
|
await ensureProjectRegisteredFromPiDirectory(project.projectDir, deps.db);
|
|
12222
12670
|
if (sub === "start") {
|
|
12223
12671
|
const { text, level } = await runEmbedDrain(deps.db, project.projectIdentity, sessionId, {
|
|
12224
|
-
onStatus: (status) =>
|
|
12672
|
+
onStatus: (status) => sendStatus({
|
|
12225
12673
|
title: "/ctx-embed",
|
|
12226
12674
|
...status
|
|
12227
12675
|
})
|
|
12228
12676
|
});
|
|
12229
|
-
|
|
12677
|
+
sendStatus({ title: "/ctx-embed", text, level });
|
|
12230
12678
|
return;
|
|
12231
12679
|
}
|
|
12232
12680
|
if (sub !== "") {
|
|
12233
|
-
|
|
12681
|
+
sendStatus({
|
|
12234
12682
|
title: "/ctx-embed",
|
|
12235
12683
|
text: "## /ctx-embed\n\nUsage: `/ctx-embed` (status), `/ctx-embed start`, or `/ctx-embed pause`.",
|
|
12236
12684
|
level: "info"
|
|
@@ -12239,12 +12687,13 @@ Memory is disabled for this project, so there is no semantic embedding to backfi
|
|
|
12239
12687
|
}
|
|
12240
12688
|
const coverage = getEmbeddingCoverageStatus(deps.db, project.projectIdentity, sessionId);
|
|
12241
12689
|
const statusText = formatEmbedStatusText(coverage, { status: "idle" });
|
|
12242
|
-
|
|
12690
|
+
sendStatus({
|
|
12243
12691
|
title: "/ctx-embed",
|
|
12244
12692
|
text: `## Embedding Status
|
|
12245
12693
|
|
|
12246
12694
|
${statusText}`,
|
|
12247
|
-
level: "info"
|
|
12695
|
+
level: "info",
|
|
12696
|
+
rpcDisplay: "dialog"
|
|
12248
12697
|
});
|
|
12249
12698
|
}
|
|
12250
12699
|
});
|
|
@@ -12260,7 +12709,7 @@ function maybeAutoEmbedPiSession(deps, sessionId, projectDir, projectIdentity, n
|
|
|
12260
12709
|
(async () => {
|
|
12261
12710
|
let completedDrainWithWork = false;
|
|
12262
12711
|
try {
|
|
12263
|
-
await new Promise((
|
|
12712
|
+
await new Promise((resolve3) => setTimeout(resolve3, 0));
|
|
12264
12713
|
await ensureProjectRegisteredFromPiDirectory(projectDir, deps.db);
|
|
12265
12714
|
const coverage = getEmbeddingCoverageStatus(deps.db, projectIdentity, sessionId);
|
|
12266
12715
|
if (!coverage.enabled)
|
|
@@ -13649,23 +14098,33 @@ function fenceWrapupBoundaryForToolArcs(args) {
|
|
|
13649
14098
|
return boundary;
|
|
13650
14099
|
}
|
|
13651
14100
|
function applyHeadCap(args) {
|
|
13652
|
-
const {
|
|
14101
|
+
const {
|
|
14102
|
+
index,
|
|
14103
|
+
protectedTailStart,
|
|
14104
|
+
offset,
|
|
14105
|
+
arcs,
|
|
14106
|
+
lastCompartmentEndOrdinal,
|
|
14107
|
+
capTokens,
|
|
14108
|
+
recentOpenArcCutoff
|
|
14109
|
+
} = args;
|
|
13653
14110
|
if (offset >= protectedTailStart)
|
|
13654
14111
|
return { eligibleEndOrdinal: offset, oversizeAtomicUnit: false };
|
|
13655
14112
|
let end = index.findHeadEndForCap(offset, protectedTailStart, capTokens);
|
|
13656
14113
|
let oversizeAtomicUnit = end === offset + 1 && index.tokenForOrdinal(offset) > capTokens;
|
|
14114
|
+
const completedFence = fenceBoundaryForCompletedToolArcs(end, arcs, lastCompartmentEndOrdinal + 1);
|
|
14115
|
+
if (completedFence > end) {
|
|
14116
|
+
end = Math.min(protectedTailStart, completedFence);
|
|
14117
|
+
if (index.rangeTokens(offset, end) > capTokens)
|
|
14118
|
+
oversizeAtomicUnit = true;
|
|
14119
|
+
} else {
|
|
14120
|
+
end = completedFence;
|
|
14121
|
+
}
|
|
13657
14122
|
for (const arc of arcs) {
|
|
13658
14123
|
const resOrdinal = arc.resOrdinal;
|
|
13659
14124
|
if (resOrdinal === null) {
|
|
13660
14125
|
if (arc.invOrdinal >= recentOpenArcCutoff && arc.invOrdinal >= offset && arc.invOrdinal < end) {
|
|
13661
14126
|
end = Math.min(end, arc.invOrdinal);
|
|
13662
14127
|
}
|
|
13663
|
-
continue;
|
|
13664
|
-
}
|
|
13665
|
-
if (arc.invOrdinal < end && end <= resOrdinal) {
|
|
13666
|
-
end = Math.min(protectedTailStart, resOrdinal + 1);
|
|
13667
|
-
if (index.rangeTokens(Math.max(offset, arc.invOrdinal), end) > capTokens)
|
|
13668
|
-
oversizeAtomicUnit = true;
|
|
13669
14128
|
}
|
|
13670
14129
|
}
|
|
13671
14130
|
if (end <= offset && offset < protectedTailStart) {
|
|
@@ -14267,6 +14726,9 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
|
|
|
14267
14726
|
}
|
|
14268
14727
|
});
|
|
14269
14728
|
}
|
|
14729
|
+
function formatProjectedPostDropPercentage(value) {
|
|
14730
|
+
return value === null ? "none" : `${value.toFixed(1)}%`;
|
|
14731
|
+
}
|
|
14270
14732
|
function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, clearReasoningAge, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail, taggerFloorOverride, reasoningProjection) {
|
|
14271
14733
|
if (sessionMeta.compartmentInProgress) {
|
|
14272
14734
|
sessionLog(sessionId, `compartment trigger: skipped — historian already in progress (usage=${usage.percentage.toFixed(1)}%)`);
|
|
@@ -14330,7 +14792,7 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
|
|
|
14330
14792
|
sessionLog(sessionId, `compartment trigger: skipping force band ${forceMaterializationPercentage}% because projected post-drop usage is ${projectedPostDropPercentage.toFixed(1)}% (target ${relativePostDropTarget.toFixed(1)}%)`);
|
|
14331
14793
|
return { shouldFire: false };
|
|
14332
14794
|
}
|
|
14333
|
-
sessionLog(sessionId, `compartment trigger: force-firing at ${usage.percentage.toFixed(1)}% (projected post-drop ${projectedPostDropPercentage
|
|
14795
|
+
sessionLog(sessionId, `compartment trigger: force-firing at ${usage.percentage.toFixed(1)}% (projected post-drop ${formatProjectedPostDropPercentage(projectedPostDropPercentage)})`);
|
|
14334
14796
|
if (tailInfo.boundarySnapshot && hasRunnableCompartmentWindow(tailInfo.boundarySnapshot)) {
|
|
14335
14797
|
return {
|
|
14336
14798
|
shouldFire: true,
|
|
@@ -14395,7 +14857,7 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
|
|
|
14395
14857
|
sessionLog(sessionId, `compartment trigger: not firing at ${usage.percentage.toFixed(1)}% because unsummarized tail from ${tailInfo.nextStartOrdinal} is too small`);
|
|
14396
14858
|
return { shouldFire: false };
|
|
14397
14859
|
}
|
|
14398
|
-
sessionLog(sessionId, `compartment trigger: proactive fire at ${usage.percentage.toFixed(1)}% (floor=${proactiveTriggerPercentage}% projected post-drop=${projectedPostDropPercentage
|
|
14860
|
+
sessionLog(sessionId, `compartment trigger: proactive fire at ${usage.percentage.toFixed(1)}% (floor=${proactiveTriggerPercentage}% projected post-drop=${formatProjectedPostDropPercentage(projectedPostDropPercentage)} target=${relativePostDropTarget.toFixed(1)}%)`);
|
|
14399
14861
|
return {
|
|
14400
14862
|
shouldFire: true,
|
|
14401
14863
|
reason: "projected_headroom",
|
|
@@ -14436,8 +14898,9 @@ function buildSupersessionReclaimOps(input) {
|
|
|
14436
14898
|
const action = input.targets.get(tag.tagNumber)?.readInput?.()?.action;
|
|
14437
14899
|
isTarget = typeof action === "string" && CTX_NOTE_ZERO_VALUE_ACTIONS.has(action);
|
|
14438
14900
|
}
|
|
14439
|
-
if (isTarget)
|
|
14901
|
+
if (isTarget && (!input.recentMessageIds || tag.toolOwnerMessageId !== null && !input.recentMessageIds.has(tag.toolOwnerMessageId))) {
|
|
14440
14902
|
dropTagIds.push(tag.tagNumber);
|
|
14903
|
+
}
|
|
14441
14904
|
}
|
|
14442
14905
|
const synthetic = [];
|
|
14443
14906
|
for (const tagId of dropTagIds) {
|
|
@@ -14470,6 +14933,9 @@ function buildEditSupersessionReclaim(input) {
|
|
|
14470
14933
|
seenFile.add(filePath);
|
|
14471
14934
|
continue;
|
|
14472
14935
|
}
|
|
14936
|
+
if (input.recentMessageIds && (tag.toolOwnerMessageId === null || input.recentMessageIds.has(tag.toolOwnerMessageId))) {
|
|
14937
|
+
continue;
|
|
14938
|
+
}
|
|
14473
14939
|
if (realPendingTagIds.has(tag.tagNumber))
|
|
14474
14940
|
continue;
|
|
14475
14941
|
if (input.targets.get(tag.tagNumber)?.canDrop?.() !== true)
|
|
@@ -15258,10 +15724,10 @@ var DEFAULT_MIN_PROMPT_CHARS = 20;
|
|
|
15258
15724
|
async function unifiedSearchWithTimeout(db, sessionId, projectPath, prompt, options, timeoutMs) {
|
|
15259
15725
|
const controller = new AbortController;
|
|
15260
15726
|
let timer;
|
|
15261
|
-
const timeoutPromise = new Promise((
|
|
15727
|
+
const timeoutPromise = new Promise((resolve3) => {
|
|
15262
15728
|
timer = setTimeout(() => {
|
|
15263
15729
|
controller.abort();
|
|
15264
|
-
|
|
15730
|
+
resolve3(null);
|
|
15265
15731
|
}, timeoutMs);
|
|
15266
15732
|
});
|
|
15267
15733
|
try {
|
|
@@ -16334,7 +16800,7 @@ function measurePiTailHygiene(input) {
|
|
|
16334
16800
|
}
|
|
16335
16801
|
continue;
|
|
16336
16802
|
}
|
|
16337
|
-
const content = part.text;
|
|
16803
|
+
const content = stripChannel1ReminderSpans(part.text);
|
|
16338
16804
|
const tag = parseVisibleTag(content, tagsByNumber) ?? messageTags.get(`${messageKey}:p${textOrdinal}`);
|
|
16339
16805
|
textOrdinal += 1;
|
|
16340
16806
|
if (!content || isDropSentinel(content)) {
|
|
@@ -16437,7 +16903,8 @@ function refreshPiTailHygieneBaseline(input) {
|
|
|
16437
16903
|
evaluable: true,
|
|
16438
16904
|
generationInvalidated: false,
|
|
16439
16905
|
baselineParts: measured.parts,
|
|
16440
|
-
contentSignature: measured.contentSignature
|
|
16906
|
+
contentSignature: measured.contentSignature,
|
|
16907
|
+
channel1PostReduceGrace: input.previous?.channel1PostReduceGrace
|
|
16441
16908
|
};
|
|
16442
16909
|
}
|
|
16443
16910
|
const prefix = sameMeasuredPrefix(input.previous.baselineParts, measured.parts);
|
|
@@ -16511,8 +16978,15 @@ function markPiChannel1Reduced(sessionId, db) {
|
|
|
16511
16978
|
state2.evaluable = false;
|
|
16512
16979
|
state2.generationInvalidated = true;
|
|
16513
16980
|
}
|
|
16514
|
-
if (db)
|
|
16515
|
-
|
|
16981
|
+
if (!db)
|
|
16982
|
+
return;
|
|
16983
|
+
const grace = markChannel1PostReduceGracePending(db, sessionId);
|
|
16984
|
+
if (state2) {
|
|
16985
|
+
state2.channel1PostReduceGrace = {
|
|
16986
|
+
pending: true,
|
|
16987
|
+
preReduceLevel: grace.postReduceGracePreLevel ?? grace.level
|
|
16988
|
+
};
|
|
16989
|
+
}
|
|
16516
16990
|
}
|
|
16517
16991
|
function isPiTextContent(c) {
|
|
16518
16992
|
return c !== null && typeof c === "object" && c.type === "text" && typeof c.text === "string";
|
|
@@ -16531,10 +17005,7 @@ function maybeChannel1ReminderForToolResult(args) {
|
|
|
16531
17005
|
if (!state2)
|
|
16532
17006
|
return null;
|
|
16533
17007
|
if (toolName === "ctx_reduce") {
|
|
16534
|
-
|
|
16535
|
-
state2.evaluable = false;
|
|
16536
|
-
state2.generationInvalidated = true;
|
|
16537
|
-
resetLastNudgeCycle(db, sessionId);
|
|
17008
|
+
markPiChannel1Reduced(sessionId, db);
|
|
16538
17009
|
return null;
|
|
16539
17010
|
}
|
|
16540
17011
|
const text = toolResultText(args.content);
|
|
@@ -16551,28 +17022,32 @@ function maybeChannel1ReminderForToolResult(args) {
|
|
|
16551
17022
|
...state2,
|
|
16552
17023
|
lastNudgeUndropped: getLastNudgeUndropped(db, sessionId),
|
|
16553
17024
|
lastNudgeLevel: nudgeState.level,
|
|
16554
|
-
|
|
17025
|
+
lastFireOrdinal: nudgeState.ordinal,
|
|
17026
|
+
currentRealUserTurnCount: state2.realUserTurnCount,
|
|
17027
|
+
hasRecentReduce: state2.reducedSinceRefresh,
|
|
17028
|
+
postReduceGracePending: nudgeState.postReduceGracePending,
|
|
17029
|
+
postReduceGraceBaselineU: nudgeState.postReduceGraceBaselineU,
|
|
17030
|
+
postReduceGracePreLevel: nudgeState.postReduceGracePreLevel
|
|
16555
17031
|
});
|
|
16556
17032
|
setLastNudgeUndropped(db, sessionId, decision.nextLastNudge);
|
|
17033
|
+
const nextNudgeState = {
|
|
17034
|
+
...nudgeState,
|
|
17035
|
+
level: decision.nextLastNudgeLevel,
|
|
17036
|
+
ordinal: decision.nextLastNudgeLevel === "" ? 0 : nudgeState.ordinal,
|
|
17037
|
+
postReduceGracePending: decision.clearPostReduceGrace ? undefined : nudgeState.postReduceGracePending,
|
|
17038
|
+
postReduceGraceBaselineU: decision.clearPostReduceGrace ? undefined : nudgeState.postReduceGraceBaselineU,
|
|
17039
|
+
postReduceGracePreLevel: decision.clearPostReduceGrace ? undefined : nudgeState.postReduceGracePreLevel
|
|
17040
|
+
};
|
|
16557
17041
|
if (!decision.fire) {
|
|
16558
|
-
setChannel1NudgeState(db, sessionId,
|
|
16559
|
-
level: decision.nextLastNudgeLevel,
|
|
16560
|
-
ordinal: decision.nextLastNudgeLevel === "" ? 0 : nudgeState.ordinal
|
|
16561
|
-
});
|
|
17042
|
+
setChannel1NudgeState(db, sessionId, nextNudgeState);
|
|
16562
17043
|
return null;
|
|
16563
17044
|
}
|
|
16564
|
-
const sticky = shouldUseStickyChannel1Reminder({
|
|
16565
|
-
lastLevel: nudgeState.level,
|
|
16566
|
-
lastOrdinal: nudgeState.ordinal,
|
|
16567
|
-
level: decision.level,
|
|
16568
|
-
currentRealUserTurnCount: state2.realUserTurnCount
|
|
16569
|
-
});
|
|
16570
17045
|
const block = {
|
|
16571
17046
|
type: "text",
|
|
16572
|
-
text: buildChannel1Reminder(decision.level, decision.undroppedTokens, state2.
|
|
17047
|
+
text: buildChannel1Reminder(decision.level, decision.undroppedTokens, reclaimableToolOutputCount(state2.baselineParts), state2.oldestReclaimableToolTags, decision.sticky)
|
|
16573
17048
|
};
|
|
16574
17049
|
setChannel1NudgeState(db, sessionId, {
|
|
16575
|
-
|
|
17050
|
+
...nextNudgeState,
|
|
16576
17051
|
ordinal: state2.realUserTurnCount
|
|
16577
17052
|
});
|
|
16578
17053
|
return block;
|
|
@@ -16606,7 +17081,7 @@ function maybeDeliverChannel2Pi(pi, db, sessionId) {
|
|
|
16606
17081
|
try {
|
|
16607
17082
|
const message = {
|
|
16608
17083
|
customType: CHANNEL2_NUDGE_CUSTOM_TYPE,
|
|
16609
|
-
content: buildChannel2Reminder(undropped, baseline.
|
|
17084
|
+
content: buildChannel2Reminder(undropped, reclaimableToolOutputCount(baseline.baselineParts), baseline.oldestReclaimableToolTags),
|
|
16610
17085
|
display: false,
|
|
16611
17086
|
details: { kind: "channel-2-ceiling-nudge" }
|
|
16612
17087
|
};
|
|
@@ -16896,6 +17371,8 @@ function piImageFromDataUrl(dataUrl) {
|
|
|
16896
17371
|
return { type: "image", mimeType: match[1], data: match[2] };
|
|
16897
17372
|
}
|
|
16898
17373
|
function resolveMuralForM0Pi(state2, db, modelKey, budgetTokens) {
|
|
17374
|
+
if (state2.memoryEnabled === false)
|
|
17375
|
+
return;
|
|
16899
17376
|
if (state2.mural)
|
|
16900
17377
|
return state2.mural;
|
|
16901
17378
|
if (!state2.muralEnabled)
|
|
@@ -17107,7 +17584,7 @@ function renderM0Pi(state2, db, projectDocs = readProjectDocsForPiM0(state2).ren
|
|
|
17107
17584
|
const sections = [];
|
|
17108
17585
|
if (projectDocs.length > 0)
|
|
17109
17586
|
sections.push(projectDocs);
|
|
17110
|
-
const trimmedProfile = trimUserMemoriesToBudget(userProfileOverride ?? safeGetActiveUserMemoriesPi(db), state2.userProfileBudgetTokens ?? DEFAULT_USER_PROFILE_BUDGET_TOKENS);
|
|
17587
|
+
const trimmedProfile = trimUserMemoriesToBudget(state2.memoryEnabled === false ? [] : userProfileOverride ?? safeGetActiveUserMemoriesPi(db), state2.userProfileBudgetTokens ?? DEFAULT_USER_PROFILE_BUDGET_TOKENS);
|
|
17111
17588
|
const userProfile = renderUserProfileBlock(db, "user-profile", trimmedProfile);
|
|
17112
17589
|
if (userProfile.length > 0)
|
|
17113
17590
|
sections.push(userProfile);
|
|
@@ -17167,7 +17644,7 @@ function readFrozenM0InputsPi(state2, db, docs = readProjectDocsForPiM0(state2),
|
|
|
17167
17644
|
const workspace = resolveWorkspaceRenderContextPi(state2, db);
|
|
17168
17645
|
const compartments = getRenderableCompartmentsPi(db, state2);
|
|
17169
17646
|
const memories = memPath ? workspace.isWorkspaced ? getMemoriesByProjects(db, workspace.expandedIdentities, ["active", "permanent"], memoryCutoff, workspace.ownIdentities, workspace.shareCategories) : getMemoriesByProject(db, memPath, ["active", "permanent"], memoryCutoff) : [];
|
|
17170
|
-
const userProfile = safeGetActiveUserMemoriesPi(db);
|
|
17647
|
+
const userProfile = state2.memoryEnabled === false ? [] : safeGetActiveUserMemoriesPi(db);
|
|
17171
17648
|
const projectState = memPath ? getProjectState(db, memPath) : undefined;
|
|
17172
17649
|
const globalState = getProjectState(db, GLOBAL_USER_PROFILE_PROJECT_PATH);
|
|
17173
17650
|
const markers = {
|
|
@@ -17187,7 +17664,7 @@ function readFrozenM0InputsPi(state2, db, docs = readProjectDocsForPiM0(state2),
|
|
|
17187
17664
|
systemHash: (state2.hardSignals ?? EMPTY_PI_HARD_SIGNALS).systemHash,
|
|
17188
17665
|
modelKey: piModelRefToCanonical((state2.hardSignals ?? EMPTY_PI_HARD_SIGNALS).modelKey),
|
|
17189
17666
|
projectIdentity: state2.projectIdentity,
|
|
17190
|
-
muralEnabled: state2.muralEnabled === true,
|
|
17667
|
+
muralEnabled: state2.memoryEnabled !== false && state2.muralEnabled === true,
|
|
17191
17668
|
renderBudgetIdentity: renderBudgetIdentityPi(state2)
|
|
17192
17669
|
};
|
|
17193
17670
|
return { docs, markers, compartments, memories, userProfile, workspace };
|
|
@@ -17409,7 +17886,7 @@ ${body}
|
|
|
17409
17886
|
if (newMemoriesBlock)
|
|
17410
17887
|
sections.push(newMemoriesBlock);
|
|
17411
17888
|
const currentUserProfileVersion = getProjectState(db, GLOBAL_USER_PROFILE_PROJECT_PATH)?.projectUserProfileVersion ?? 0;
|
|
17412
|
-
if (currentUserProfileVersion !== markers.projectUserProfileVersion) {
|
|
17889
|
+
if (state2.memoryEnabled !== false && currentUserProfileVersion !== markers.projectUserProfileVersion) {
|
|
17413
17890
|
const profileBudget = state2.userProfileBudgetTokens ?? DEFAULT_USER_PROFILE_BUDGET_TOKENS;
|
|
17414
17891
|
const trimmedProfile = trimUserMemoriesToBudget(safeGetActiveUserMemoriesPi(db), Math.max(1, Math.floor(profileBudget * 0.25)));
|
|
17415
17892
|
const profileBlock = renderUserProfileBlock(db, "new-user-profile", trimmedProfile);
|
|
@@ -20994,7 +21471,7 @@ async function sleepWithAbort(ms, signal) {
|
|
|
20994
21471
|
return true;
|
|
20995
21472
|
if (ms <= 0)
|
|
20996
21473
|
return signal?.aborted === true;
|
|
20997
|
-
return new Promise((
|
|
21474
|
+
return new Promise((resolve3) => {
|
|
20998
21475
|
let settled = false;
|
|
20999
21476
|
let timeout;
|
|
21000
21477
|
const finish = (aborted) => {
|
|
@@ -21004,7 +21481,7 @@ async function sleepWithAbort(ms, signal) {
|
|
|
21004
21481
|
if (timeout)
|
|
21005
21482
|
clearTimeout(timeout);
|
|
21006
21483
|
signal?.removeEventListener("abort", onAbort);
|
|
21007
|
-
|
|
21484
|
+
resolve3(aborted);
|
|
21008
21485
|
};
|
|
21009
21486
|
const onAbort = () => finish(true);
|
|
21010
21487
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
@@ -21123,6 +21600,8 @@ async function runPiHistorian(deps) {
|
|
|
21123
21600
|
refreshBoundarySnapshot,
|
|
21124
21601
|
currentContextLimit,
|
|
21125
21602
|
historianTimeoutMs = DEFAULT_HISTORIAN_TIMEOUT_MS2,
|
|
21603
|
+
temperature = 0.1,
|
|
21604
|
+
maxOutputTokens = 32000,
|
|
21126
21605
|
signal,
|
|
21127
21606
|
retryBackoffMs,
|
|
21128
21607
|
twoPass,
|
|
@@ -21235,9 +21714,9 @@ async function runPiHistorian(deps) {
|
|
|
21235
21714
|
executeThresholdPercentage: boundarySnapshot.executeThresholdPercentage
|
|
21236
21715
|
});
|
|
21237
21716
|
if (!reserve.ok) {
|
|
21238
|
-
sessionLog(sessionId,
|
|
21717
|
+
sessionLog(sessionId, describeProtectedTailDrainBudgetSkip(reserve));
|
|
21239
21718
|
telemetry.status = "noop";
|
|
21240
|
-
telemetry.failureReason = "protected-tail drain
|
|
21719
|
+
telemetry.failureReason = "internal protected-tail drain budget spent";
|
|
21241
21720
|
return;
|
|
21242
21721
|
}
|
|
21243
21722
|
drainReservation = reserve.reservation;
|
|
@@ -21350,6 +21829,8 @@ ${chunkText}`,
|
|
|
21350
21829
|
cwd: directory,
|
|
21351
21830
|
signal,
|
|
21352
21831
|
thinkingLevel,
|
|
21832
|
+
temperature,
|
|
21833
|
+
maxOutputTokens,
|
|
21353
21834
|
onProgress: buildProgressLogger("first"),
|
|
21354
21835
|
accountingSessionId: sessionId,
|
|
21355
21836
|
accountingSubagent: "historian"
|
|
@@ -21374,6 +21855,8 @@ ${chunkText}`,
|
|
|
21374
21855
|
cwd: directory,
|
|
21375
21856
|
signal,
|
|
21376
21857
|
thinkingLevel,
|
|
21858
|
+
temperature,
|
|
21859
|
+
maxOutputTokens,
|
|
21377
21860
|
onProgress: buildProgressLogger("repair"),
|
|
21378
21861
|
accountingSessionId: sessionId,
|
|
21379
21862
|
accountingSubagent: "historian"
|
|
@@ -21403,6 +21886,8 @@ ${chunkText}`,
|
|
|
21403
21886
|
cwd: directory,
|
|
21404
21887
|
signal,
|
|
21405
21888
|
thinkingLevel: candidate.entry.qualifier,
|
|
21889
|
+
temperature,
|
|
21890
|
+
maxOutputTokens,
|
|
21406
21891
|
onProgress: buildProgressLogger("fallback"),
|
|
21407
21892
|
accountingSessionId: sessionId,
|
|
21408
21893
|
accountingSubagent: "historian"
|
|
@@ -21449,6 +21934,8 @@ ${chunkText}`,
|
|
|
21449
21934
|
cwd: directory,
|
|
21450
21935
|
signal,
|
|
21451
21936
|
thinkingLevel,
|
|
21937
|
+
temperature,
|
|
21938
|
+
maxOutputTokens,
|
|
21452
21939
|
onProgress: buildProgressLogger("editor"),
|
|
21453
21940
|
accountingSessionId: sessionId,
|
|
21454
21941
|
accountingSubagent: "historian_editor"
|
|
@@ -21759,6 +22246,62 @@ function findFirstKeptEntryId(entries, lastCompactedOrdinal) {
|
|
|
21759
22246
|
return boundary.id;
|
|
21760
22247
|
}
|
|
21761
22248
|
|
|
22249
|
+
// src/pi-pressure.ts
|
|
22250
|
+
function extractAssistantUsage(message) {
|
|
22251
|
+
if (!message || typeof message !== "object")
|
|
22252
|
+
return null;
|
|
22253
|
+
const m = message;
|
|
22254
|
+
if (m.role !== "assistant")
|
|
22255
|
+
return null;
|
|
22256
|
+
if (!m.usage || typeof m.usage !== "object")
|
|
22257
|
+
return null;
|
|
22258
|
+
const u = m.usage;
|
|
22259
|
+
const result = {};
|
|
22260
|
+
if (typeof u.input === "number")
|
|
22261
|
+
result.input = u.input;
|
|
22262
|
+
if (typeof u.output === "number")
|
|
22263
|
+
result.output = u.output;
|
|
22264
|
+
if (typeof u.cacheRead === "number")
|
|
22265
|
+
result.cacheRead = u.cacheRead;
|
|
22266
|
+
if (typeof u.cacheWrite === "number")
|
|
22267
|
+
result.cacheWrite = u.cacheWrite;
|
|
22268
|
+
if (typeof u.totalTokens === "number")
|
|
22269
|
+
result.totalTokens = u.totalTokens;
|
|
22270
|
+
return result;
|
|
22271
|
+
}
|
|
22272
|
+
function computePiPressure(usage, contextLimit) {
|
|
22273
|
+
if (!usage)
|
|
22274
|
+
return null;
|
|
22275
|
+
const input = usage.input ?? 0;
|
|
22276
|
+
const cacheRead = usage.cacheRead ?? 0;
|
|
22277
|
+
const cacheWrite = usage.cacheWrite ?? 0;
|
|
22278
|
+
const inputTokens = input + cacheRead + cacheWrite;
|
|
22279
|
+
if (inputTokens === 0)
|
|
22280
|
+
return null;
|
|
22281
|
+
const percentage = contextLimit > 0 ? inputTokens / contextLimit * 100 : 0;
|
|
22282
|
+
return { inputTokens, percentage };
|
|
22283
|
+
}
|
|
22284
|
+
function resolvePiPressureSnapshot(args) {
|
|
22285
|
+
const persistedInputTokens = Number.isFinite(args.persistedInputTokens) && args.persistedInputTokens > 0 ? args.persistedInputTokens : 0;
|
|
22286
|
+
const liveInputTokens = typeof args.liveInputTokens === "number" && Number.isFinite(args.liveInputTokens) && args.liveInputTokens > 0 ? args.liveInputTokens : 0;
|
|
22287
|
+
const inputTokens = Math.max(persistedInputTokens, liveInputTokens);
|
|
22288
|
+
const contextLimit = typeof args.usableContextLimit === "number" && Number.isFinite(args.usableContextLimit) && args.usableContextLimit > 0 ? args.usableContextLimit : undefined;
|
|
22289
|
+
if (contextLimit !== undefined) {
|
|
22290
|
+
return {
|
|
22291
|
+
inputTokens,
|
|
22292
|
+
percentage: inputTokens > 0 ? inputTokens / contextLimit * 100 : 0,
|
|
22293
|
+
contextLimit
|
|
22294
|
+
};
|
|
22295
|
+
}
|
|
22296
|
+
return {
|
|
22297
|
+
inputTokens: persistedInputTokens,
|
|
22298
|
+
percentage: Number.isFinite(args.persistedPercentage) && args.persistedPercentage > 0 ? args.persistedPercentage : 0
|
|
22299
|
+
};
|
|
22300
|
+
}
|
|
22301
|
+
function formatPiPressureForLog(snapshot) {
|
|
22302
|
+
return `usage=${snapshot.percentage.toFixed(1)}% (${snapshot.inputTokens} tokens, limit=${snapshot.contextLimit ?? "?"})`;
|
|
22303
|
+
}
|
|
22304
|
+
|
|
21762
22305
|
// src/pi-todo-inject.ts
|
|
21763
22306
|
function getMessageId(message) {
|
|
21764
22307
|
if (typeof message.responseId === "string" && message.responseId.length > 0) {
|
|
@@ -22585,8 +23128,8 @@ function injectPiTemporalMarkers(messages) {
|
|
|
22585
23128
|
// src/timeout.ts
|
|
22586
23129
|
function withTimeout(p, ms) {
|
|
22587
23130
|
let handle;
|
|
22588
|
-
const timeout = new Promise((
|
|
22589
|
-
handle = setTimeout(() =>
|
|
23131
|
+
const timeout = new Promise((resolve3) => {
|
|
23132
|
+
handle = setTimeout(() => resolve3(undefined), ms);
|
|
22590
23133
|
handle.unref?.();
|
|
22591
23134
|
});
|
|
22592
23135
|
return Promise.race([p, timeout]).finally(() => {
|
|
@@ -23300,22 +23843,9 @@ function extractStableId(msg, index, entryIds) {
|
|
|
23300
23843
|
|
|
23301
23844
|
// src/context-handler.ts
|
|
23302
23845
|
var EMERGENCY_BLOCK_PERCENTAGE = 95;
|
|
23303
|
-
var FORWARD_PRESSURE_LIMIT_FACTOR = 0.85;
|
|
23304
23846
|
function isPiHardCacheExpired(lastResponseTime, ttlMs, now) {
|
|
23305
23847
|
return lastResponseTime > 0 && now - lastResponseTime > ttlMs;
|
|
23306
23848
|
}
|
|
23307
|
-
function applyForwardPressureFloor(trailingPercentage, trailingInputTokens, piUsageTokens, correctedLimit) {
|
|
23308
|
-
const forwardTokens = typeof piUsageTokens === "number" && piUsageTokens > 0 ? piUsageTokens : 0;
|
|
23309
|
-
if (forwardTokens === 0 || !isSaneLimit(correctedLimit)) {
|
|
23310
|
-
return { percentage: trailingPercentage, inputTokens: trailingInputTokens };
|
|
23311
|
-
}
|
|
23312
|
-
const forwardPressureLimit = correctedLimit * FORWARD_PRESSURE_LIMIT_FACTOR;
|
|
23313
|
-
const forwardPercentage = forwardTokens / forwardPressureLimit * 100;
|
|
23314
|
-
return forwardPercentage > trailingPercentage ? {
|
|
23315
|
-
percentage: forwardPercentage,
|
|
23316
|
-
inputTokens: Math.max(trailingInputTokens, forwardTokens)
|
|
23317
|
-
} : { percentage: trailingPercentage, inputTokens: trailingInputTokens };
|
|
23318
|
-
}
|
|
23319
23849
|
var injectM0M1PiForRun = injectM0M1Pi;
|
|
23320
23850
|
var persistReasoningWatermarkForRun = updateSessionMeta;
|
|
23321
23851
|
var persistStableIdSchemeForRun = updateSessionMeta;
|
|
@@ -24255,7 +24785,12 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
24255
24785
|
if (!usedPersistedUsage && isSaneLimit(usageContextLimit) && usageInputTokens > 0) {
|
|
24256
24786
|
usagePercentage = usageInputTokens / usageContextLimit * 100;
|
|
24257
24787
|
}
|
|
24258
|
-
({ percentage: usagePercentage, inputTokens: usageInputTokens } =
|
|
24788
|
+
({ percentage: usagePercentage, inputTokens: usageInputTokens } = resolvePiPressureSnapshot({
|
|
24789
|
+
persistedPercentage: usagePercentage,
|
|
24790
|
+
persistedInputTokens: usageInputTokens,
|
|
24791
|
+
liveInputTokens: piUsage?.tokens,
|
|
24792
|
+
usableContextLimit: usageContextLimit
|
|
24793
|
+
}));
|
|
24259
24794
|
const realUsagePercentageBeforeEmergencyBump = usagePercentage;
|
|
24260
24795
|
if (needsEmergencyBump) {
|
|
24261
24796
|
sessionLog(sessionId, `transform: overflow recovery flag set — bumping percentage to 95% (detectedLimit=${usageContextLimit ?? "unknown"})`);
|
|
@@ -24317,6 +24852,9 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
24317
24852
|
schedulerDecision = midTurnAdjustedSchedulerDecision;
|
|
24318
24853
|
sessionLog(sessionId, `[boundary-exec] base=${schedulerDecisionEarly} bypass=${bypassReason} midTurn=${midTurn} effective=${midTurnAdjustedSchedulerDecision} sideEffect=${sideEffect}`);
|
|
24319
24854
|
const forceMaterialization = !options.compactionOff && usagePercentage >= forceMaterializationPercentage;
|
|
24855
|
+
if (!forceMaterialization && getEmergencyInputSample(options.db, sessionId) > 0) {
|
|
24856
|
+
clearEmergencyDropSample(options.db, sessionId);
|
|
24857
|
+
}
|
|
24320
24858
|
const hardUsagePercentage = needsEmergencyBump ? Math.max(EMERGENCY_BLOCK_PERCENTAGE, usagePercentage) : windowGeometry?.usableHard && usageInputTokens > 0 ? usageInputTokens / windowGeometry.usableHard * 100 : usagePercentage;
|
|
24321
24859
|
const emergencyPercentage = ctx.model ? hardUsagePercentage : usagePercentage;
|
|
24322
24860
|
const isEmergency = !options.compactionOff && emergencyPercentage >= EMERGENCY_BLOCK_PERCENTAGE;
|
|
@@ -24346,7 +24884,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
24346
24884
|
}
|
|
24347
24885
|
const isCacheBusting = historyRefreshSessions.has(sessionId);
|
|
24348
24886
|
logTransformTiming(sessionId, "boundaryTriggerChecks", tBoundaryChecks);
|
|
24349
|
-
sessionLog(sessionId, `transform:
|
|
24887
|
+
sessionLog(sessionId, `transform: ${formatPiPressureForLog({ percentage: usagePercentage, inputTokens: usageInputTokens, contextLimit: usageContextLimit })} decision=${schedulerDecision}${forceMaterialization ? " force=true" : ""}${isEmergency ? " EMERGENCY=true" : ""}${isCacheBusting ? " busting=true" : ""}`);
|
|
24350
24888
|
logTransformTiming(sessionId, "emergencyRecoveryBlock", tEmergencyRecovery);
|
|
24351
24889
|
const entryIds = strictEntryIds ?? undefined;
|
|
24352
24890
|
const emergencyCeilingTokens = usageContextLimit && usageContextLimit > 0 ? Math.floor(usageContextLimit * (resolveExecuteThreshold(schedulerConfig.executeThresholdPercentage ?? 65, liveModelBySession.get(sessionId), 65, {
|
|
@@ -24524,7 +25062,12 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
24524
25062
|
previous: getPiChannel1Baseline(sessionId)
|
|
24525
25063
|
});
|
|
24526
25064
|
const effective = effectivePiTailHygiene(baseline);
|
|
24527
|
-
|
|
25065
|
+
const durableGrace = baseline.evaluable && !baseline.generationInvalidated ? captureChannel1PostReduceGraceBaseline(options.db, sessionId, effective.u) : getChannel1NudgeState(options.db, sessionId);
|
|
25066
|
+
baseline.channel1PostReduceGrace = durableGrace.postReduceGracePending || durableGrace.postReduceGraceBaselineU !== undefined ? {
|
|
25067
|
+
pending: durableGrace.postReduceGracePending === true,
|
|
25068
|
+
baselineU: durableGrace.postReduceGraceBaselineU,
|
|
25069
|
+
preReduceLevel: durableGrace.postReduceGracePreLevel ?? durableGrace.level
|
|
25070
|
+
} : undefined;
|
|
24528
25071
|
const oldestReclaimableToolTags = getOldestActiveUnprotectedToolTags(options.db, sessionId, protectedTags);
|
|
24529
25072
|
const channelState = {
|
|
24530
25073
|
...baseline,
|
|
@@ -24631,10 +25174,11 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
24631
25174
|
log("[magic-context][pi] registered context handler (tagging + drops + nudges)");
|
|
24632
25175
|
}
|
|
24633
25176
|
var inFlightHistorian = new Map;
|
|
24634
|
-
async function awaitInFlightHistorians() {
|
|
24635
|
-
|
|
25177
|
+
async function awaitInFlightHistorians(sessionId) {
|
|
25178
|
+
const runs = sessionId ? [inFlightHistorian.get(sessionId)].filter((run) => run !== undefined) : [...inFlightHistorian.values()];
|
|
25179
|
+
if (runs.length === 0)
|
|
24636
25180
|
return;
|
|
24637
|
-
await Promise.allSettled(
|
|
25181
|
+
await Promise.allSettled(runs);
|
|
24638
25182
|
}
|
|
24639
25183
|
function resolvePiHistorianTriggerInputs(args) {
|
|
24640
25184
|
const contextLimit = typeof args.usageContextLimit === "number" && Number.isFinite(args.usageContextLimit) && args.usageContextLimit > 0 ? args.usageContextLimit : DEFAULT_CONTEXT_LIMIT;
|
|
@@ -24771,6 +25315,8 @@ function spawnPiHistorianRun(args) {
|
|
|
24771
25315
|
refreshBoundarySnapshot,
|
|
24772
25316
|
currentContextLimit,
|
|
24773
25317
|
historianTimeoutMs: historian.timeoutMs,
|
|
25318
|
+
temperature: historian.temperature,
|
|
25319
|
+
maxOutputTokens: historian.maxOutputTokens,
|
|
24774
25320
|
twoPass: historian.twoPass,
|
|
24775
25321
|
thinkingLevel: historian.thinkingLevel,
|
|
24776
25322
|
memoryEnabled: historian.memoryEnabled,
|
|
@@ -24889,7 +25435,12 @@ function maybeFireHistorian(args) {
|
|
|
24889
25435
|
};
|
|
24890
25436
|
usageSource = "piUsage fallback";
|
|
24891
25437
|
}
|
|
24892
|
-
usage =
|
|
25438
|
+
usage = resolvePiPressureSnapshot({
|
|
25439
|
+
persistedPercentage: usage.percentage,
|
|
25440
|
+
persistedInputTokens: usage.inputTokens,
|
|
25441
|
+
liveInputTokens: piUsage?.tokens,
|
|
25442
|
+
usableContextLimit: usageContextLimit
|
|
25443
|
+
});
|
|
24893
25444
|
sessionLog(sessionId, `historian trigger eval: usage=${usage.percentage.toFixed(1)}% (${usage.inputTokens} tokens) [${usageSource}], checking trigger...`);
|
|
24894
25445
|
} catch (err) {
|
|
24895
25446
|
sessionLog(sessionId, `historian trigger eval: getContextUsage threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -25356,6 +25907,10 @@ async function runPipeline(args) {
|
|
|
25356
25907
|
if (shouldRunHeuristics && args.heuristics) {
|
|
25357
25908
|
try {
|
|
25358
25909
|
const tHeuristic = performance.now();
|
|
25910
|
+
const independentMutationBeforeHeuristics = pendingOpsDidMutate || foldExecutedThisPass;
|
|
25911
|
+
if (args.forceMaterialization === true && independentMutationBeforeHeuristics && getEmergencyInputSample(args.db, args.sessionId) > 0) {
|
|
25912
|
+
clearEmergencyDropSample(args.db, args.sessionId);
|
|
25913
|
+
}
|
|
25359
25914
|
heuristicsResult = applyPiHeuristicCleanup(args.sessionId, args.db, targets, args.messages, {
|
|
25360
25915
|
protectedTags: args.protectedTags,
|
|
25361
25916
|
staleReduceStripEnabled: args.canUseEmptySentinels,
|
|
@@ -25875,9 +26430,10 @@ function registerCtxFlushCommand(pi, deps) {
|
|
|
25875
26430
|
pi.registerCommand("ctx-flush", {
|
|
25876
26431
|
description: "Force pending Magic Context drops to materialize on the next provider call",
|
|
25877
26432
|
handler: async (_args, ctx) => {
|
|
26433
|
+
const sendStatus = createCtxStatusSender(pi, ctx);
|
|
25878
26434
|
const sessionId = resolveSessionId(ctx);
|
|
25879
26435
|
if (!sessionId) {
|
|
25880
|
-
|
|
26436
|
+
sendStatus({
|
|
25881
26437
|
title: "/ctx-flush",
|
|
25882
26438
|
text: `## /ctx-flush
|
|
25883
26439
|
|
|
@@ -25887,7 +26443,7 @@ No active Pi session is available.`,
|
|
|
25887
26443
|
return;
|
|
25888
26444
|
}
|
|
25889
26445
|
if (deps.compactionOff) {
|
|
25890
|
-
|
|
26446
|
+
sendStatus({
|
|
25891
26447
|
title: "/ctx-flush",
|
|
25892
26448
|
text: COMPACTION_OFF_COMMAND_UNAVAILABLE,
|
|
25893
26449
|
level: "warning"
|
|
@@ -25906,7 +26462,7 @@ Flushed ${pendingBefore} pending ops; next provider call will materialize.
|
|
|
25906
26462
|
${result}` : `## /ctx-flush
|
|
25907
26463
|
|
|
25908
26464
|
${result}`;
|
|
25909
|
-
|
|
26465
|
+
sendStatus({
|
|
25910
26466
|
title: "/ctx-flush",
|
|
25911
26467
|
text,
|
|
25912
26468
|
level: result.startsWith("Error:") ? "error" : "success"
|
|
@@ -25926,7 +26482,7 @@ function cleanupHistorianStateFile(path6) {
|
|
|
25926
26482
|
}
|
|
25927
26483
|
// ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
|
|
25928
26484
|
import { mkdirSync as mkdirSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
25929
|
-
import { join as
|
|
26485
|
+
import { join as join5 } from "node:path";
|
|
25930
26486
|
|
|
25931
26487
|
// ../plugin/src/agents/historian.ts
|
|
25932
26488
|
var HISTORIAN_AGENT = "historian";
|
|
@@ -26263,8 +26819,8 @@ function isTransientHistorianPromptError2(message) {
|
|
|
26263
26819
|
].some((token) => normalized.includes(token));
|
|
26264
26820
|
}
|
|
26265
26821
|
function sleep(ms) {
|
|
26266
|
-
return new Promise((
|
|
26267
|
-
setTimeout(
|
|
26822
|
+
return new Promise((resolve3) => {
|
|
26823
|
+
setTimeout(resolve3, ms);
|
|
26268
26824
|
});
|
|
26269
26825
|
}
|
|
26270
26826
|
function cleanupHistorianDump(sessionId, dumpPath) {
|
|
@@ -26286,7 +26842,7 @@ function dumpHistorianResponse(sessionId, directory, label, text) {
|
|
|
26286
26842
|
ensureCortexKitArtifactGitignore(directory);
|
|
26287
26843
|
const safeSessionId = sanitizeDumpName(sessionId);
|
|
26288
26844
|
const safeLabel = sanitizeDumpName(label);
|
|
26289
|
-
const dumpPath =
|
|
26845
|
+
const dumpPath = join5(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
|
|
26290
26846
|
writeFileSync5(dumpPath, text, "utf8");
|
|
26291
26847
|
sessionLog(sessionId, "compartment agent: historian response dumped", {
|
|
26292
26848
|
label,
|
|
@@ -26319,6 +26875,7 @@ function insertRecompCompartmentRows(db, sessionId, compartments, now) {
|
|
|
26319
26875
|
}
|
|
26320
26876
|
function promoteRecompStagingWithM0Mutation(db, sessionId, holderId) {
|
|
26321
26877
|
const now = Date.now();
|
|
26878
|
+
const transactionStartedAt = performance.now();
|
|
26322
26879
|
db.exec("BEGIN IMMEDIATE");
|
|
26323
26880
|
let finished = false;
|
|
26324
26881
|
try {
|
|
@@ -26347,6 +26904,7 @@ function promoteRecompStagingWithM0Mutation(db, sessionId, holderId) {
|
|
|
26347
26904
|
clearCachedM0M1(db, sessionId);
|
|
26348
26905
|
db.exec("COMMIT");
|
|
26349
26906
|
finished = true;
|
|
26907
|
+
logSlowWriteTransaction("historian-publish:recomp", transactionStartedAt);
|
|
26350
26908
|
return { compartments: staging.compartments, facts: staging.facts };
|
|
26351
26909
|
} finally {
|
|
26352
26910
|
if (!finished) {
|
|
@@ -27130,6 +27688,8 @@ function createPiHistorianClient(args) {
|
|
|
27130
27688
|
const sessions = new Map;
|
|
27131
27689
|
let counter = 0;
|
|
27132
27690
|
async function prompt(input) {
|
|
27691
|
+
if (args.signal?.aborted)
|
|
27692
|
+
throw new Error("prompt aborted by external signal");
|
|
27133
27693
|
const body = readBody(input);
|
|
27134
27694
|
const sessionId = readPathId(input);
|
|
27135
27695
|
if (body.noReply) {
|
|
@@ -27150,8 +27710,11 @@ function createPiHistorianClient(args) {
|
|
|
27150
27710
|
cwd: args.directory,
|
|
27151
27711
|
thinkingLevel: modelOverride ? modelOverride.qualifier : args.thinkingLevel,
|
|
27152
27712
|
accountingSessionId: args.accountingSessionId,
|
|
27153
|
-
accountingSubagent: "recomp"
|
|
27713
|
+
accountingSubagent: "recomp",
|
|
27714
|
+
signal: args.signal
|
|
27154
27715
|
});
|
|
27716
|
+
if (args.signal?.aborted)
|
|
27717
|
+
throw new Error("prompt aborted by external signal");
|
|
27155
27718
|
if (!result.ok) {
|
|
27156
27719
|
throw new Error(`Pi recomp historian failed (${result.reason}): ${result.error}`);
|
|
27157
27720
|
}
|
|
@@ -27162,6 +27725,8 @@ function createPiHistorianClient(args) {
|
|
|
27162
27725
|
session: {
|
|
27163
27726
|
get: async () => ({ directory: args.directory }),
|
|
27164
27727
|
create: async () => {
|
|
27728
|
+
if (args.signal?.aborted)
|
|
27729
|
+
throw new Error("prompt aborted by external signal");
|
|
27165
27730
|
const id = `magic-context-pi-recomp-${++counter}`;
|
|
27166
27731
|
sessions.set(id, []);
|
|
27167
27732
|
return { id };
|
|
@@ -27218,16 +27783,13 @@ function makeMessage2(role, text) {
|
|
|
27218
27783
|
|
|
27219
27784
|
// src/pi-recomp-marker.ts
|
|
27220
27785
|
function stagePiRecompMarker(args) {
|
|
27221
|
-
const readBranchEntries = resolvePiReadBranchEntries2(args.ctx);
|
|
27222
|
-
if (!readBranchEntries)
|
|
27223
|
-
return;
|
|
27224
27786
|
const compartments = getCompartments(args.db, args.sessionId);
|
|
27225
27787
|
const last = compartments[compartments.length - 1];
|
|
27226
27788
|
if (!last)
|
|
27227
27789
|
return;
|
|
27228
27790
|
let firstKeptEntryId = null;
|
|
27229
27791
|
try {
|
|
27230
|
-
firstKeptEntryId = findFirstKeptEntryId(
|
|
27792
|
+
firstKeptEntryId = findFirstKeptEntryId(args.branchEntries, last.endMessage);
|
|
27231
27793
|
} catch {
|
|
27232
27794
|
firstKeptEntryId = null;
|
|
27233
27795
|
}
|
|
@@ -27243,19 +27805,6 @@ function stagePiRecompMarker(args) {
|
|
|
27243
27805
|
});
|
|
27244
27806
|
signalPiDeferredHistoryRefresh(args.sessionId);
|
|
27245
27807
|
}
|
|
27246
|
-
function resolvePiReadBranchEntries2(ctx) {
|
|
27247
|
-
const sm = ctx?.sessionManager;
|
|
27248
|
-
if (typeof sm?.getBranch !== "function")
|
|
27249
|
-
return;
|
|
27250
|
-
return () => {
|
|
27251
|
-
try {
|
|
27252
|
-
const entries = sm.getBranch?.call(sm);
|
|
27253
|
-
return Array.isArray(entries) ? entries : [];
|
|
27254
|
-
} catch {
|
|
27255
|
-
return [];
|
|
27256
|
-
}
|
|
27257
|
-
};
|
|
27258
|
-
}
|
|
27259
27808
|
|
|
27260
27809
|
// src/status-line.ts
|
|
27261
27810
|
var STATUS_KEY = "magic-context";
|
|
@@ -27301,7 +27850,6 @@ function renderStatusText(ctx, db, sessionId) {
|
|
|
27301
27850
|
const meta = readSessionMetaStatus(db, sessionId);
|
|
27302
27851
|
const liveInputTokens = typeof usage?.tokens === "number" && Number.isFinite(usage.tokens) ? usage.tokens : undefined;
|
|
27303
27852
|
const persistedInputTokens = typeof meta?.last_input_tokens === "number" && Number.isFinite(meta.last_input_tokens) && meta.last_input_tokens >= 0 ? meta.last_input_tokens : undefined;
|
|
27304
|
-
const inputTokens = liveInputTokens ?? persistedInputTokens;
|
|
27305
27853
|
const windowGeometry = resolvePiWindowGeometry({
|
|
27306
27854
|
rawContextWindow: usage?.contextWindow ?? ctx.model?.contextWindow,
|
|
27307
27855
|
model: ctx.model,
|
|
@@ -27309,8 +27857,14 @@ function renderStatusText(ctx, db, sessionId) {
|
|
|
27309
27857
|
persistedInputTokens,
|
|
27310
27858
|
persistedPercentage: typeof meta?.last_context_percentage === "number" ? meta.last_context_percentage : undefined
|
|
27311
27859
|
});
|
|
27312
|
-
const
|
|
27313
|
-
|
|
27860
|
+
const pressure = liveInputTokens !== undefined || persistedInputTokens !== undefined ? resolvePiPressureSnapshot({
|
|
27861
|
+
persistedPercentage: meta?.last_context_percentage ?? 0,
|
|
27862
|
+
persistedInputTokens: persistedInputTokens ?? 0,
|
|
27863
|
+
liveInputTokens,
|
|
27864
|
+
usableContextLimit: windowGeometry?.usableSoft
|
|
27865
|
+
}) : undefined;
|
|
27866
|
+
const inputTokens = pressure?.inputTokens;
|
|
27867
|
+
const pct = pressure?.percentage;
|
|
27314
27868
|
const state2 = renderHistorianState(meta, recompSessions.has(sessionId));
|
|
27315
27869
|
return `mc: ${inputTokens === undefined ? "--" : fmt(inputTokens)} (${pct === undefined ? "--" : `${Math.round(pct)}%`}) · ${state2}`;
|
|
27316
27870
|
}
|
|
@@ -27367,29 +27921,41 @@ var inFlightRecomp = new Map;
|
|
|
27367
27921
|
function isPiRecompInFlight(sessionId) {
|
|
27368
27922
|
return inFlightRecomp.has(sessionId);
|
|
27369
27923
|
}
|
|
27370
|
-
async function awaitInFlightRecomps() {
|
|
27371
|
-
|
|
27924
|
+
async function awaitInFlightRecomps(sessionId) {
|
|
27925
|
+
const runs = sessionId ? [inFlightRecomp.get(sessionId)?.promise].filter((run) => run !== undefined) : [...inFlightRecomp.values()].map((run) => run.promise);
|
|
27926
|
+
if (runs.length === 0)
|
|
27372
27927
|
return;
|
|
27373
|
-
await Promise.allSettled(
|
|
27928
|
+
await Promise.allSettled(runs);
|
|
27929
|
+
}
|
|
27930
|
+
function abortInFlightRecomps(sessionId) {
|
|
27931
|
+
inFlightRecomp.get(sessionId)?.controller.abort();
|
|
27374
27932
|
}
|
|
27375
27933
|
function spawnPiRecompRun(args) {
|
|
27376
27934
|
const { sessionId, provider, onStatusChange, work } = args;
|
|
27935
|
+
const controller = new AbortController;
|
|
27377
27936
|
const unregister = setRawMessageProvider(sessionId, provider);
|
|
27378
27937
|
setMagicContextRecompActive(sessionId, true);
|
|
27379
|
-
|
|
27380
|
-
const runPromise = (async () => {
|
|
27938
|
+
let run;
|
|
27939
|
+
const runPromise = Promise.resolve().then(async () => {
|
|
27381
27940
|
try {
|
|
27382
|
-
await work();
|
|
27941
|
+
await work(controller.signal);
|
|
27383
27942
|
} catch (err) {
|
|
27384
|
-
|
|
27943
|
+
if (!controller.signal.aborted) {
|
|
27944
|
+
sessionLog(sessionId, `pi recomp run failed (detached): ${err instanceof Error ? err.message : String(err)}`);
|
|
27945
|
+
}
|
|
27946
|
+
}
|
|
27947
|
+
}).finally(() => {
|
|
27948
|
+
if (inFlightRecomp.get(sessionId) === run) {
|
|
27949
|
+
inFlightRecomp.delete(sessionId);
|
|
27385
27950
|
}
|
|
27386
|
-
})().finally(() => {
|
|
27387
|
-
inFlightRecomp.delete(sessionId);
|
|
27388
27951
|
setMagicContextRecompActive(sessionId, false);
|
|
27389
27952
|
unregister();
|
|
27390
|
-
|
|
27953
|
+
if (!controller.signal.aborted)
|
|
27954
|
+
onStatusChange();
|
|
27391
27955
|
});
|
|
27392
|
-
|
|
27956
|
+
run = { promise: runPromise, controller };
|
|
27957
|
+
inFlightRecomp.set(sessionId, run);
|
|
27958
|
+
onStatusChange();
|
|
27393
27959
|
}
|
|
27394
27960
|
|
|
27395
27961
|
// src/commands/ctx-recomp.ts
|
|
@@ -27406,9 +27972,10 @@ function registerCtxRecompCommand(pi, deps) {
|
|
|
27406
27972
|
pi.registerCommand("ctx-recomp", {
|
|
27407
27973
|
description: "Rebuild Magic Context compartments from raw Pi session history",
|
|
27408
27974
|
handler: async (args, ctx) => {
|
|
27975
|
+
const sendStatus = createCtxStatusSender(pi, ctx);
|
|
27409
27976
|
const sessionId = resolveSessionId(ctx);
|
|
27410
27977
|
if (!sessionId) {
|
|
27411
|
-
|
|
27978
|
+
sendStatus({
|
|
27412
27979
|
title: "/ctx-recomp",
|
|
27413
27980
|
text: `## Magic Recomp
|
|
27414
27981
|
|
|
@@ -27419,7 +27986,7 @@ No active Pi session is available.`,
|
|
|
27419
27986
|
}
|
|
27420
27987
|
const currentDeps = deps.resolveRuntimeDeps?.(ctx) ?? deps;
|
|
27421
27988
|
if (currentDeps.compactionOff) {
|
|
27422
|
-
|
|
27989
|
+
sendStatus({
|
|
27423
27990
|
title: "/ctx-recomp",
|
|
27424
27991
|
text: COMPACTION_OFF_COMMAND_UNAVAILABLE,
|
|
27425
27992
|
level: "warning"
|
|
@@ -27428,7 +27995,7 @@ No active Pi session is available.`,
|
|
|
27428
27995
|
}
|
|
27429
27996
|
const parsed = parseRecompArgs(args);
|
|
27430
27997
|
if (parsed.kind === "error") {
|
|
27431
|
-
|
|
27998
|
+
sendStatus({
|
|
27432
27999
|
title: "/ctx-recomp",
|
|
27433
28000
|
text: `## Magic Recomp — Invalid Arguments
|
|
27434
28001
|
|
|
@@ -27438,15 +28005,16 @@ ${parsed.message}`,
|
|
|
27438
28005
|
return;
|
|
27439
28006
|
}
|
|
27440
28007
|
if (parsed.kind === "upgrade") {
|
|
27441
|
-
|
|
28008
|
+
sendStatus({
|
|
27442
28009
|
title: "/ctx-recomp",
|
|
27443
28010
|
text: executeRecompUpgradeStub(currentDeps.db, sessionId),
|
|
27444
|
-
level: "info"
|
|
28011
|
+
level: "info",
|
|
28012
|
+
rpcDisplay: "dialog"
|
|
27445
28013
|
});
|
|
27446
28014
|
return;
|
|
27447
28015
|
}
|
|
27448
28016
|
if (!currentDeps.historianModel) {
|
|
27449
|
-
|
|
28017
|
+
sendStatus({
|
|
27450
28018
|
title: "/ctx-recomp",
|
|
27451
28019
|
text: "## Magic Recomp\n\n/ctx-recomp is unavailable because `historian.model` is not configured.",
|
|
27452
28020
|
level: "error"
|
|
@@ -27463,7 +28031,7 @@ ${parsed.message}`,
|
|
|
27463
28031
|
confirmationBySession.delete(sessionId);
|
|
27464
28032
|
else
|
|
27465
28033
|
confirmationBySession.set(sessionId, { timestamp: now, argsKey });
|
|
27466
|
-
|
|
28034
|
+
sendStatus({
|
|
27467
28035
|
title: "/ctx-recomp",
|
|
27468
28036
|
text: warning.text,
|
|
27469
28037
|
level: warning.confirmable ? "warning" : "error"
|
|
@@ -27471,7 +28039,7 @@ ${parsed.message}`,
|
|
|
27471
28039
|
return;
|
|
27472
28040
|
}
|
|
27473
28041
|
if (isWrapupInProgress(currentDeps.db, sessionId)) {
|
|
27474
|
-
|
|
28042
|
+
sendStatus({
|
|
27475
28043
|
title: "/ctx-recomp",
|
|
27476
28044
|
text: "## Magic Recomp\n\n/ctx-wrapup is already compacting this session. Wait for it to finish, then try `/ctx-recomp` again.",
|
|
27477
28045
|
level: "warning"
|
|
@@ -27479,7 +28047,7 @@ ${parsed.message}`,
|
|
|
27479
28047
|
return;
|
|
27480
28048
|
}
|
|
27481
28049
|
if (isPiRecompInFlight(sessionId)) {
|
|
27482
|
-
|
|
28050
|
+
sendStatus({
|
|
27483
28051
|
title: "/ctx-recomp",
|
|
27484
28052
|
text: `## Magic Recomp
|
|
27485
28053
|
|
|
@@ -27489,7 +28057,7 @@ A recomp or upgrade is already running for this session in the background. Wait
|
|
|
27489
28057
|
return;
|
|
27490
28058
|
}
|
|
27491
28059
|
confirmationBySession.delete(sessionId);
|
|
27492
|
-
|
|
28060
|
+
sendStatus({
|
|
27493
28061
|
title: "/ctx-recomp",
|
|
27494
28062
|
text: parsed.kind === "partial" ? `## Magic Recomp
|
|
27495
28063
|
|
|
@@ -27498,17 +28066,21 @@ Partial recomp started for range ${parsed.range.start}-${parsed.range.end}.` : `
|
|
|
27498
28066
|
Historian recomp started. Rebuilding compartments and facts from raw Pi session history now.`,
|
|
27499
28067
|
level: "info"
|
|
27500
28068
|
});
|
|
28069
|
+
const cwd = ctx.cwd;
|
|
28070
|
+
const fallbackModelId = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
|
|
28071
|
+
const snapshot = readPiSessionSnapshot(ctx);
|
|
27501
28072
|
const provider = {
|
|
27502
|
-
readMessages: () =>
|
|
28073
|
+
readMessages: () => snapshot.rawMessages
|
|
27503
28074
|
};
|
|
27504
28075
|
spawnPiRecompRun({
|
|
27505
28076
|
sessionId,
|
|
27506
28077
|
provider,
|
|
27507
28078
|
onStatusChange: () => updateStatusLine(ctx, {
|
|
27508
28079
|
db: currentDeps.db,
|
|
27509
|
-
projectIdentity:
|
|
28080
|
+
projectIdentity: cwd
|
|
27510
28081
|
}),
|
|
27511
|
-
work: async () => {
|
|
28082
|
+
work: async (signal) => {
|
|
28083
|
+
const detachedSendStatus = createCtxStatusSender(pi, ctx, signal);
|
|
27512
28084
|
const result = await executeContextRecompWithResult({
|
|
27513
28085
|
client: createPiHistorianClient({
|
|
27514
28086
|
runner: currentDeps.runner,
|
|
@@ -27517,10 +28089,11 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
|
|
|
27517
28089
|
fallbackModels: currentDeps.historianFallbacks,
|
|
27518
28090
|
timeoutMs: currentDeps.historianTimeoutMs,
|
|
27519
28091
|
thinkingLevel: currentDeps.historianThinkingLevel,
|
|
27520
|
-
directory:
|
|
28092
|
+
directory: cwd,
|
|
27521
28093
|
accountingSessionId: sessionId,
|
|
28094
|
+
signal,
|
|
27522
28095
|
notify: (text) => {
|
|
27523
|
-
|
|
28096
|
+
detachedSendStatus({
|
|
27524
28097
|
title: "/ctx-recomp",
|
|
27525
28098
|
text,
|
|
27526
28099
|
level: inferLevel(text)
|
|
@@ -27530,15 +28103,17 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
|
|
|
27530
28103
|
db: currentDeps.db,
|
|
27531
28104
|
sessionId,
|
|
27532
28105
|
historianChunkTokens: currentDeps.historianChunkTokens,
|
|
27533
|
-
directory:
|
|
28106
|
+
directory: cwd,
|
|
27534
28107
|
historianTimeoutMs: currentDeps.historianTimeoutMs,
|
|
27535
28108
|
memoryEnabled: currentDeps.memoryEnabled,
|
|
27536
28109
|
autoPromote: currentDeps.autoPromote,
|
|
27537
28110
|
ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
|
|
27538
28111
|
fallbackModels: currentDeps.historianFallbacks,
|
|
27539
28112
|
language: currentDeps.language,
|
|
27540
|
-
fallbackModelId
|
|
28113
|
+
fallbackModelId
|
|
27541
28114
|
}, parsed.kind === "partial" ? { range: parsed.range } : {});
|
|
28115
|
+
if (signal.aborted)
|
|
28116
|
+
return;
|
|
27542
28117
|
if (result.published) {
|
|
27543
28118
|
try {
|
|
27544
28119
|
clearEmergencyRecovery(currentDeps.db, sessionId);
|
|
@@ -27546,14 +28121,18 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
|
|
|
27546
28121
|
sessionLog(sessionId, `/ctx-recomp: clearEmergencyRecovery failed (continuing): ${describeError(recoveryError).brief}`);
|
|
27547
28122
|
}
|
|
27548
28123
|
try {
|
|
27549
|
-
stagePiRecompMarker({
|
|
28124
|
+
stagePiRecompMarker({
|
|
28125
|
+
db: currentDeps.db,
|
|
28126
|
+
sessionId,
|
|
28127
|
+
branchEntries: snapshot.branchEntries
|
|
28128
|
+
});
|
|
27550
28129
|
} catch (markerError) {
|
|
27551
28130
|
sessionLog(sessionId, `/ctx-recomp: marker staging failed (recomp already published; continuing): ${describeError(markerError).brief}`);
|
|
27552
28131
|
}
|
|
27553
28132
|
signalPiDeferredHistoryRefresh(sessionId);
|
|
27554
28133
|
signalPiDeferredMaterialization(sessionId);
|
|
27555
28134
|
}
|
|
27556
|
-
|
|
28135
|
+
detachedSendStatus({
|
|
27557
28136
|
title: "/ctx-recomp",
|
|
27558
28137
|
text: result.message,
|
|
27559
28138
|
level: inferLevel(result.message)
|
|
@@ -27785,7 +28364,13 @@ function contextualizeUpgradeReason(reason) {
|
|
|
27785
28364
|
|
|
27786
28365
|
// src/pi-memory-migration.ts
|
|
27787
28366
|
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.";
|
|
28367
|
+
var CANCELLED_OUTCOME = {
|
|
28368
|
+
ran: false,
|
|
28369
|
+
summary: "Memory migration cancelled during session shutdown."
|
|
28370
|
+
};
|
|
27788
28371
|
async function runPiMemoryMigration(deps) {
|
|
28372
|
+
if (deps.signal?.aborted)
|
|
28373
|
+
return CANCELLED_OUTCOME;
|
|
27789
28374
|
const projectPath = resolveProjectIdentityForSession(deps.directory, deps.allowHomeProject);
|
|
27790
28375
|
if (!projectPath) {
|
|
27791
28376
|
return {
|
|
@@ -27823,6 +28408,8 @@ async function runPiMemoryMigration(deps) {
|
|
|
27823
28408
|
let parsed = null;
|
|
27824
28409
|
let lastFailReason = "no output";
|
|
27825
28410
|
for (let i = 0;i < modelChain.length; i += 1) {
|
|
28411
|
+
if (deps.signal?.aborted)
|
|
28412
|
+
return CANCELLED_OUTCOME;
|
|
27826
28413
|
const model = modelChain[i];
|
|
27827
28414
|
if (i > 0) {
|
|
27828
28415
|
sessionLog(deps.sessionId, `memory-migration: escalating to configured fallback model ${model.model} (${i}/${modelChain.length - 1})`);
|
|
@@ -27837,8 +28424,11 @@ async function runPiMemoryMigration(deps) {
|
|
|
27837
28424
|
cwd: deps.directory,
|
|
27838
28425
|
thinkingLevel: model.qualifier,
|
|
27839
28426
|
accountingSessionId: deps.sessionId,
|
|
27840
|
-
accountingSubagent: "recomp"
|
|
28427
|
+
accountingSubagent: "recomp",
|
|
28428
|
+
signal: deps.signal
|
|
27841
28429
|
});
|
|
28430
|
+
if (deps.signal?.aborted)
|
|
28431
|
+
return CANCELLED_OUTCOME;
|
|
27842
28432
|
if (!result.ok) {
|
|
27843
28433
|
lastFailReason = `historian ${result.reason}`;
|
|
27844
28434
|
continue;
|
|
@@ -27869,6 +28459,8 @@ async function runPiMemoryMigration(deps) {
|
|
|
27869
28459
|
summary: "Memory migration skipped: the model extracted user traits but user memories are disabled. Enable `dreamer.user_memories` so they can be preserved, then re-run /ctx-session-upgrade."
|
|
27870
28460
|
};
|
|
27871
28461
|
}
|
|
28462
|
+
if (deps.signal?.aborted)
|
|
28463
|
+
return CANCELLED_OUTCOME;
|
|
27872
28464
|
let routed = 0;
|
|
27873
28465
|
if (deps.userMemoriesEnabled && parsed.userObservations.length > 0) {
|
|
27874
28466
|
insertUserMemoryCandidates(deps.db, parsed.userObservations.map((content) => ({
|
|
@@ -27893,9 +28485,10 @@ function registerCtxSessionUpgradeCommand(pi, deps) {
|
|
|
27893
28485
|
pi.registerCommand("ctx-session-upgrade", {
|
|
27894
28486
|
description: "Upgrade this session to the current Magic Context history format and re-organize project memories",
|
|
27895
28487
|
handler: async (_args, ctx) => {
|
|
28488
|
+
const sendStatus = createCtxStatusSender(pi, ctx);
|
|
27896
28489
|
const sessionId = resolveSessionId(ctx);
|
|
27897
28490
|
if (!sessionId) {
|
|
27898
|
-
|
|
28491
|
+
sendStatus({
|
|
27899
28492
|
title: "/ctx-session-upgrade",
|
|
27900
28493
|
text: `## Session Upgrade
|
|
27901
28494
|
|
|
@@ -27906,7 +28499,7 @@ No active Pi session is available.`,
|
|
|
27906
28499
|
}
|
|
27907
28500
|
const currentDeps = deps.resolveRuntimeDeps?.(ctx) ?? deps;
|
|
27908
28501
|
if (currentDeps.compactionOff) {
|
|
27909
|
-
|
|
28502
|
+
sendStatus({
|
|
27910
28503
|
title: "/ctx-session-upgrade",
|
|
27911
28504
|
text: COMPACTION_OFF_COMMAND_UNAVAILABLE,
|
|
27912
28505
|
level: "warning"
|
|
@@ -27914,7 +28507,7 @@ No active Pi session is available.`,
|
|
|
27914
28507
|
return;
|
|
27915
28508
|
}
|
|
27916
28509
|
if (!currentDeps.historianModel) {
|
|
27917
|
-
|
|
28510
|
+
sendStatus({
|
|
27918
28511
|
title: "/ctx-session-upgrade",
|
|
27919
28512
|
text: "## Session Upgrade\n\nUnavailable because `historian.model` is not configured.",
|
|
27920
28513
|
level: "error"
|
|
@@ -27922,7 +28515,7 @@ No active Pi session is available.`,
|
|
|
27922
28515
|
return;
|
|
27923
28516
|
}
|
|
27924
28517
|
if (isWrapupInProgress(currentDeps.db, sessionId)) {
|
|
27925
|
-
|
|
28518
|
+
sendStatus({
|
|
27926
28519
|
title: "/ctx-session-upgrade",
|
|
27927
28520
|
text: `## Session Upgrade
|
|
27928
28521
|
|
|
@@ -27932,7 +28525,7 @@ No active Pi session is available.`,
|
|
|
27932
28525
|
return;
|
|
27933
28526
|
}
|
|
27934
28527
|
if (isPiRecompInFlight(sessionId)) {
|
|
27935
|
-
|
|
28528
|
+
sendStatus({
|
|
27936
28529
|
title: "/ctx-session-upgrade",
|
|
27937
28530
|
text: `## Session Upgrade
|
|
27938
28531
|
|
|
@@ -27943,9 +28536,10 @@ An upgrade or recomp is already running for this session in the background. Wait
|
|
|
27943
28536
|
}
|
|
27944
28537
|
const compartments = getCompartments(currentDeps.db, sessionId);
|
|
27945
28538
|
const upgradableCount = compartments.filter((c) => c.legacy === 1 || !c.p1 || c.p1.trim() === "").length;
|
|
28539
|
+
const cwd = ctx.cwd;
|
|
27946
28540
|
const sessionMainModel = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
|
|
27947
28541
|
const migrationEnabled = currentDeps.memoryEnabled;
|
|
27948
|
-
const runMigration = async () => {
|
|
28542
|
+
const runMigration = async (signal) => {
|
|
27949
28543
|
if (!migrationEnabled) {
|
|
27950
28544
|
return "Memory migration skipped (memory disabled).";
|
|
27951
28545
|
}
|
|
@@ -27958,24 +28552,27 @@ An upgrade or recomp is already running for this session in the background. Wait
|
|
|
27958
28552
|
fallbackModels: currentDeps.historianFallbacks,
|
|
27959
28553
|
timeoutMs: currentDeps.historianTimeoutMs,
|
|
27960
28554
|
thinkingLevel: currentDeps.historianThinkingLevel,
|
|
27961
|
-
directory:
|
|
28555
|
+
directory: cwd,
|
|
27962
28556
|
allowHomeProject: currentDeps.allowHomeProject,
|
|
27963
28557
|
sessionId,
|
|
27964
28558
|
userMemoriesEnabled: currentDeps.userMemoriesEnabled,
|
|
27965
|
-
language: currentDeps.language
|
|
28559
|
+
language: currentDeps.language,
|
|
28560
|
+
signal
|
|
27966
28561
|
});
|
|
27967
28562
|
return outcome.summary;
|
|
27968
28563
|
} catch (error) {
|
|
28564
|
+
if (signal.aborted)
|
|
28565
|
+
return "Memory migration cancelled.";
|
|
27969
28566
|
return `Memory migration skipped (error): ${describeError(error).brief}`;
|
|
27970
28567
|
}
|
|
27971
28568
|
};
|
|
27972
28569
|
if (upgradableCount === 0) {
|
|
27973
|
-
const projectPath = resolveProjectIdentityForSession(
|
|
28570
|
+
const projectPath = resolveProjectIdentityForSession(cwd, currentDeps.allowHomeProject);
|
|
27974
28571
|
if (!projectPath)
|
|
27975
28572
|
return;
|
|
27976
28573
|
const migrationPending = migrationEnabled && !isMemoryMigrationDone(currentDeps.db, projectPath);
|
|
27977
28574
|
if (!migrationPending) {
|
|
27978
|
-
|
|
28575
|
+
sendStatus({
|
|
27979
28576
|
title: "/ctx-session-upgrade",
|
|
27980
28577
|
text: [
|
|
27981
28578
|
"## Session Upgrade — Already Up To Date",
|
|
@@ -27983,56 +28580,64 @@ An upgrade or recomp is already running for this session in the background. Wait
|
|
|
27983
28580
|
compartments.length === 0 ? "This session has no compartment history to upgrade yet." : "This session's compartments are already in the current format."
|
|
27984
28581
|
].join(`
|
|
27985
28582
|
`),
|
|
27986
|
-
level: "info"
|
|
28583
|
+
level: "info",
|
|
28584
|
+
rpcDisplay: "dialog"
|
|
27987
28585
|
});
|
|
27988
28586
|
return;
|
|
27989
28587
|
}
|
|
27990
|
-
|
|
28588
|
+
sendStatus({
|
|
27991
28589
|
title: "/ctx-session-upgrade",
|
|
27992
28590
|
text: `## Session Upgrade
|
|
27993
28591
|
|
|
27994
28592
|
Compartments are already current. Re-organizing project memories. This may take a while.`,
|
|
27995
28593
|
level: "info"
|
|
27996
28594
|
});
|
|
28595
|
+
const snapshot2 = readPiSessionSnapshot(ctx);
|
|
27997
28596
|
spawnPiRecompRun({
|
|
27998
28597
|
sessionId,
|
|
27999
28598
|
provider: {
|
|
28000
|
-
readMessages: () =>
|
|
28599
|
+
readMessages: () => snapshot2.rawMessages
|
|
28001
28600
|
},
|
|
28002
28601
|
onStatusChange: () => updateStatusLine(ctx, {
|
|
28003
28602
|
db: currentDeps.db,
|
|
28004
|
-
projectIdentity:
|
|
28603
|
+
projectIdentity: cwd
|
|
28005
28604
|
}),
|
|
28006
|
-
work: async () => {
|
|
28007
|
-
const
|
|
28008
|
-
|
|
28605
|
+
work: async (signal) => {
|
|
28606
|
+
const detachedSendStatus = createCtxStatusSender(pi, ctx, signal);
|
|
28607
|
+
const summary = await runMigration(signal);
|
|
28608
|
+
if (signal.aborted)
|
|
28609
|
+
return;
|
|
28610
|
+
detachedSendStatus({
|
|
28009
28611
|
title: "/ctx-session-upgrade",
|
|
28010
28612
|
text: ["## Session Upgrade — Complete", "", summary].join(`
|
|
28011
28613
|
`),
|
|
28012
|
-
level: "info"
|
|
28614
|
+
level: "info",
|
|
28615
|
+
rpcDisplay: "dialog"
|
|
28013
28616
|
});
|
|
28014
28617
|
}
|
|
28015
28618
|
});
|
|
28016
28619
|
return;
|
|
28017
28620
|
}
|
|
28018
|
-
|
|
28621
|
+
sendStatus({
|
|
28019
28622
|
title: "/ctx-session-upgrade",
|
|
28020
28623
|
text: `## Session Upgrade
|
|
28021
28624
|
|
|
28022
28625
|
Rebuilding compartments into the v2 format and re-organizing project memories. This may take a while.`,
|
|
28023
28626
|
level: "info"
|
|
28024
28627
|
});
|
|
28628
|
+
const snapshot = readPiSessionSnapshot(ctx);
|
|
28025
28629
|
const provider = {
|
|
28026
|
-
readMessages: () =>
|
|
28630
|
+
readMessages: () => snapshot.rawMessages
|
|
28027
28631
|
};
|
|
28028
28632
|
spawnPiRecompRun({
|
|
28029
28633
|
sessionId,
|
|
28030
28634
|
provider,
|
|
28031
28635
|
onStatusChange: () => updateStatusLine(ctx, {
|
|
28032
28636
|
db: currentDeps.db,
|
|
28033
|
-
projectIdentity:
|
|
28637
|
+
projectIdentity: cwd
|
|
28034
28638
|
}),
|
|
28035
|
-
work: async () => {
|
|
28639
|
+
work: async (signal) => {
|
|
28640
|
+
const detachedSendStatus = createCtxStatusSender(pi, ctx, signal);
|
|
28036
28641
|
const recompResult = await executeContextRecompWithResult({
|
|
28037
28642
|
client: createPiHistorianClient({
|
|
28038
28643
|
runner: currentDeps.runner,
|
|
@@ -28040,10 +28645,11 @@ Rebuilding compartments into the v2 format and re-organizing project memories. T
|
|
|
28040
28645
|
fallbackModels: currentDeps.historianFallbacks,
|
|
28041
28646
|
timeoutMs: currentDeps.historianTimeoutMs,
|
|
28042
28647
|
thinkingLevel: currentDeps.historianThinkingLevel,
|
|
28043
|
-
directory:
|
|
28648
|
+
directory: cwd,
|
|
28044
28649
|
accountingSessionId: sessionId,
|
|
28650
|
+
signal,
|
|
28045
28651
|
systemPrompt: withContentLanguageDirective(COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT, currentDeps.language, { preserveUserQuotes: true }),
|
|
28046
|
-
notify: (text) =>
|
|
28652
|
+
notify: (text) => detachedSendStatus({
|
|
28047
28653
|
title: "/ctx-session-upgrade",
|
|
28048
28654
|
text,
|
|
28049
28655
|
level: "info"
|
|
@@ -28052,7 +28658,7 @@ Rebuilding compartments into the v2 format and re-organizing project memories. T
|
|
|
28052
28658
|
db: currentDeps.db,
|
|
28053
28659
|
sessionId,
|
|
28054
28660
|
historianChunkTokens: currentDeps.historianChunkTokens,
|
|
28055
|
-
directory:
|
|
28661
|
+
directory: cwd,
|
|
28056
28662
|
historianTimeoutMs: currentDeps.historianTimeoutMs,
|
|
28057
28663
|
memoryEnabled: currentDeps.memoryEnabled,
|
|
28058
28664
|
autoPromote: currentDeps.autoPromote,
|
|
@@ -28061,9 +28667,11 @@ Rebuilding compartments into the v2 format and re-organizing project memories. T
|
|
|
28061
28667
|
fallbackModelId: sessionMainModel,
|
|
28062
28668
|
language: currentDeps.language
|
|
28063
28669
|
}, {});
|
|
28670
|
+
if (signal.aborted)
|
|
28671
|
+
return;
|
|
28064
28672
|
if (!recompResult.published || !isRecompComplete(recompResult.message)) {
|
|
28065
28673
|
const reason = contextualizeUpgradeReason(isRecompFailure(recompResult.message) ? extractRecompReason(recompResult.message) : `Compartments were not fully rebuilt: ${extractRecompReason(recompResult.message)}`);
|
|
28066
|
-
|
|
28674
|
+
detachedSendStatus({
|
|
28067
28675
|
title: "/ctx-session-upgrade",
|
|
28068
28676
|
text: `## Session Upgrade — Incomplete
|
|
28069
28677
|
|
|
@@ -28073,14 +28681,20 @@ ${reason}`,
|
|
|
28073
28681
|
return;
|
|
28074
28682
|
}
|
|
28075
28683
|
try {
|
|
28076
|
-
stagePiRecompMarker({
|
|
28684
|
+
stagePiRecompMarker({
|
|
28685
|
+
db: currentDeps.db,
|
|
28686
|
+
sessionId,
|
|
28687
|
+
branchEntries: snapshot.branchEntries
|
|
28688
|
+
});
|
|
28077
28689
|
} catch (markerError) {
|
|
28078
28690
|
sessionLog(sessionId, `pi /ctx-session-upgrade marker staging failed (non-fatal, recomp already published): ${describeError(markerError).brief}`);
|
|
28079
28691
|
}
|
|
28080
28692
|
signalPiDeferredHistoryRefresh(sessionId);
|
|
28081
28693
|
signalPiDeferredMaterialization(sessionId);
|
|
28082
|
-
const migrationSummary = await runMigration();
|
|
28083
|
-
|
|
28694
|
+
const migrationSummary = await runMigration(signal);
|
|
28695
|
+
if (signal.aborted)
|
|
28696
|
+
return;
|
|
28697
|
+
detachedSendStatus({
|
|
28084
28698
|
title: "/ctx-session-upgrade",
|
|
28085
28699
|
text: [
|
|
28086
28700
|
"## Session Upgrade — Complete",
|
|
@@ -28092,7 +28706,8 @@ ${migrationSummary}` : "",
|
|
|
28092
28706
|
recompResult.message
|
|
28093
28707
|
].join(`
|
|
28094
28708
|
`),
|
|
28095
|
-
level: "info"
|
|
28709
|
+
level: "info",
|
|
28710
|
+
rpcDisplay: "dialog"
|
|
28096
28711
|
});
|
|
28097
28712
|
}
|
|
28098
28713
|
});
|
|
@@ -28118,6 +28733,9 @@ function formatThresholdClampNote(opts) {
|
|
|
28118
28733
|
return ` [clamped: ${opts.configuredValue}% > ${opts.maxPercentage}%]`;
|
|
28119
28734
|
}
|
|
28120
28735
|
|
|
28736
|
+
// ../plugin/src/shared/rust-mode-status.ts
|
|
28737
|
+
var RUST_MODE_HOST_PATHS_LINE = "Host backends → MODULE: ctx_memory, ctx_note; historian: module-side";
|
|
28738
|
+
|
|
28121
28739
|
// ../plugin/src/shared/tail-hygiene-status.ts
|
|
28122
28740
|
function finiteNumber(value, fallback = 0) {
|
|
28123
28741
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
@@ -28184,7 +28802,7 @@ function formatExecuteThreshold(detail, contextLimit) {
|
|
|
28184
28802
|
}
|
|
28185
28803
|
return `${percentage}%${clampNote}`;
|
|
28186
28804
|
}
|
|
28187
|
-
function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit, dreamer, windowGeometry, tailHygiene) {
|
|
28805
|
+
function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit, dreamer, windowGeometry, tailHygiene, contextUsage, rustMode = false) {
|
|
28188
28806
|
const thresholdDetail = resolveExecuteThresholdDetail(executeThresholdPercentageConfig, liveModelKey, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, {
|
|
28189
28807
|
tokensConfig: executeThresholdTokens,
|
|
28190
28808
|
contextLimit,
|
|
@@ -28209,7 +28827,9 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
|
|
|
28209
28827
|
const remainingMs = Math.max(0, ttlMs - elapsed);
|
|
28210
28828
|
const cacheExpired = remainingMs === 0 && meta.lastResponseTime > 0;
|
|
28211
28829
|
const proactiveCompartmentTrigger = getProactiveCompartmentTriggerPercentage(executeThresholdPercentage);
|
|
28212
|
-
const
|
|
28830
|
+
const displayInputTokens = contextUsage?.inputTokens ?? meta.lastInputTokens;
|
|
28831
|
+
const displayPercentage = contextUsage?.percentage ?? meta.lastContextPercentage;
|
|
28832
|
+
const displayContextLimit = contextLimit && contextLimit > 0 ? contextLimit : displayPercentage > 0 ? Math.round(displayInputTokens / (displayPercentage / 100)) : 0;
|
|
28213
28833
|
const lines = [
|
|
28214
28834
|
"## Magic Status",
|
|
28215
28835
|
"",
|
|
@@ -28234,11 +28854,15 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
|
|
|
28234
28854
|
"",
|
|
28235
28855
|
"### Execute Threshold",
|
|
28236
28856
|
`- Execute threshold: ${formatExecuteThreshold(thresholdDetail, displayContextLimit)}`,
|
|
28237
|
-
`- Last input tokens: ${
|
|
28857
|
+
`- Last input tokens: ${displayInputTokens.toLocaleString()} tokens`,
|
|
28238
28858
|
"",
|
|
28239
28859
|
`**Protected tags:** ${protectedTags}`,
|
|
28240
28860
|
`**Subagent session:** ${meta.isSubagent}`
|
|
28241
28861
|
];
|
|
28862
|
+
const storage = getMagicContextStorageResolution();
|
|
28863
|
+
lines.push("", `**Storage:** ${storage.path} (${storage.source})`);
|
|
28864
|
+
if (rustMode)
|
|
28865
|
+
lines.push("", "### Rust Mode", `- ${RUST_MODE_HOST_PATHS_LINE}`);
|
|
28242
28866
|
if (tailHygiene !== undefined) {
|
|
28243
28867
|
lines.push("", "### Tail Hygiene", `- Reclaimable / eligible: ${formatTailHygiene(tailHygiene)}`, "- Reasoning is excluded from both terms.");
|
|
28244
28868
|
}
|
|
@@ -28247,8 +28871,8 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
|
|
|
28247
28871
|
`- Running: ${dreamer.progress.task} — ${dreamer.progress.processed}/${dreamer.progress.total} processed`
|
|
28248
28872
|
] : [], ...formatDreamTaskBacklogs(dreamer.backlog).split("\\n"));
|
|
28249
28873
|
}
|
|
28250
|
-
if (
|
|
28251
|
-
lines.push("", "### Context Usage", `- Last percentage: ${
|
|
28874
|
+
if (displayPercentage > 0 || displayInputTokens > 0) {
|
|
28875
|
+
lines.push("", "### Context Usage", `- Last percentage: ${displayPercentage.toFixed(1)}%`, `- Last input tokens: ${displayInputTokens.toLocaleString()}`, `- Resolved context limit: ${displayContextLimit > 0 ? displayContextLimit.toLocaleString() : "unknown"}`, ...windowGeometry ? [`- ${formatWindowDerivationLine(displayInputTokens, windowGeometry)}`] : [], `- 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`);
|
|
28252
28876
|
}
|
|
28253
28877
|
const compartments = getCompartments(db, sessionId);
|
|
28254
28878
|
let historyBlockTokens = 0;
|
|
@@ -28285,7 +28909,7 @@ ${c.content}
|
|
|
28285
28909
|
|
|
28286
28910
|
// src/dialogs/status-dialog.ts
|
|
28287
28911
|
import {
|
|
28288
|
-
matchesKey,
|
|
28912
|
+
matchesKey as matchesKey2,
|
|
28289
28913
|
truncateToWidth,
|
|
28290
28914
|
visibleWidth
|
|
28291
28915
|
} from "@earendil-works/pi-tui";
|
|
@@ -28349,7 +28973,7 @@ ${c.content}
|
|
|
28349
28973
|
// package.json
|
|
28350
28974
|
var package_default = {
|
|
28351
28975
|
name: "@cortexkit/pi-magic-context",
|
|
28352
|
-
version: "0.
|
|
28976
|
+
version: "0.41.0",
|
|
28353
28977
|
type: "module",
|
|
28354
28978
|
description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
|
|
28355
28979
|
main: "dist/index.js",
|
|
@@ -28377,7 +29001,7 @@ var package_default = {
|
|
|
28377
29001
|
"README.md"
|
|
28378
29002
|
],
|
|
28379
29003
|
scripts: {
|
|
28380
|
-
build: "tsc -p ../retina-local-fs/tsconfig.build.json && rm -f dist/index.js dist/subagent-entry.js dist/*-*.js && bun build src/index.ts src/subagent-entry.ts --outdir dist --target node --format esm --splitting --external @earendil-works/pi-coding-agent --external @earendil-works/pi-tui --external @huggingface/transformers --external node:sqlite",
|
|
29004
|
+
build: "tsc -p ../retina-local-fs/tsconfig.build.json && rm -f dist/index.js dist/subagent-entry.js dist/historian-calibration-extension.js dist/*-*.js && bun build src/index.ts src/subagent-entry.ts src/historian-calibration-extension.ts --outdir dist --target node --format esm --splitting --external @earendil-works/pi-coding-agent --external @earendil-works/pi-tui --external @huggingface/transformers --external node:sqlite",
|
|
28381
29005
|
"build:e2e-argv": "bun build src/subagent-runner.ts --outfile ../../tests/docker/.generated/subagent-runner-e2e.mjs --target node --format esm --external node:sqlite",
|
|
28382
29006
|
typecheck: "tsc -p ../retina-local-fs/tsconfig.build.json && tsc --noEmit",
|
|
28383
29007
|
test: "bun install --frozen-lockfile && bun test",
|
|
@@ -28485,7 +29109,7 @@ class StatusDialogComponent {
|
|
|
28485
29109
|
}, REFRESH_INTERVAL_MS);
|
|
28486
29110
|
}
|
|
28487
29111
|
handleInput(data) {
|
|
28488
|
-
if (
|
|
29112
|
+
if (matchesKey2(data, "escape") || matchesKey2(data, "ctrl+c") || matchesKey2(data, "return")) {
|
|
28489
29113
|
this.close();
|
|
28490
29114
|
}
|
|
28491
29115
|
}
|
|
@@ -28586,8 +29210,6 @@ function drawBorder(inner, width, theme) {
|
|
|
28586
29210
|
function buildPiStatusDetail(pi, ctx, deps, sessionId) {
|
|
28587
29211
|
const usage = ctx.getContextUsage?.();
|
|
28588
29212
|
const meta = getOrCreateSessionMeta(deps.db, sessionId);
|
|
28589
|
-
const hasPersistedPressure = meta.lastInputTokens > 0 && meta.lastContextPercentage > 0;
|
|
28590
|
-
const inputTokens = hasPersistedPressure ? meta.lastInputTokens : typeof usage?.tokens === "number" ? usage.tokens : 0;
|
|
28591
29213
|
let detectedContextLimit;
|
|
28592
29214
|
try {
|
|
28593
29215
|
const detected = getOverflowState(deps.db, sessionId).detectedContextLimit;
|
|
@@ -28601,8 +29223,15 @@ function buildPiStatusDetail(pi, ctx, deps, sessionId) {
|
|
|
28601
29223
|
persistedInputTokens: meta.lastInputTokens,
|
|
28602
29224
|
persistedPercentage: meta.lastContextPercentage
|
|
28603
29225
|
});
|
|
28604
|
-
const
|
|
28605
|
-
|
|
29226
|
+
const pressure = resolvePiPressureSnapshot({
|
|
29227
|
+
persistedPercentage: meta.lastContextPercentage,
|
|
29228
|
+
persistedInputTokens: meta.lastInputTokens,
|
|
29229
|
+
liveInputTokens: usage?.tokens,
|
|
29230
|
+
usableContextLimit: windowGeometry?.usableSoft
|
|
29231
|
+
});
|
|
29232
|
+
const inputTokens = pressure.inputTokens;
|
|
29233
|
+
const contextLimit = pressure.contextLimit ?? 0;
|
|
29234
|
+
const usagePercentage = pressure.percentage;
|
|
28606
29235
|
const compartments = getCompartments(deps.db, sessionId);
|
|
28607
29236
|
const metaRow = readSessionMetaRow(deps.db, sessionId);
|
|
28608
29237
|
const memoryBlockCount = Number(metaRow?.memory_block_count ?? 0);
|
|
@@ -28859,12 +29488,13 @@ function registerCtxStatusCommand(pi, deps) {
|
|
|
28859
29488
|
pi.registerCommand("ctx-status", {
|
|
28860
29489
|
description: "Show Magic Context status for the current Pi session",
|
|
28861
29490
|
handler: async (_args, ctx) => {
|
|
29491
|
+
const sendStatus = createCtxStatusSender(pi, ctx);
|
|
28862
29492
|
const runtimeDeps = deps.resolveStatusDeps?.(ctx) ?? deps;
|
|
28863
29493
|
const projectIdentity = runtimeDeps.resolveProject?.(ctx).projectIdentity ?? runtimeDeps.projectIdentity;
|
|
28864
29494
|
const currentDeps = { ...runtimeDeps, projectIdentity };
|
|
28865
29495
|
const sessionId = resolveSessionId(ctx);
|
|
28866
29496
|
if (!sessionId) {
|
|
28867
|
-
|
|
29497
|
+
sendStatus({
|
|
28868
29498
|
title: "/ctx-status",
|
|
28869
29499
|
text: `## Magic Status
|
|
28870
29500
|
|
|
@@ -28895,20 +29525,30 @@ No active Pi session is available.`,
|
|
|
28895
29525
|
persistedPercentage: meta.lastContextPercentage
|
|
28896
29526
|
});
|
|
28897
29527
|
const usableContextLimit = windowGeometry?.usableSoft;
|
|
29528
|
+
const pressure = resolvePiPressureSnapshot({
|
|
29529
|
+
persistedPercentage: meta.lastContextPercentage,
|
|
29530
|
+
persistedInputTokens: meta.lastInputTokens,
|
|
29531
|
+
liveInputTokens: usage?.tokens,
|
|
29532
|
+
usableContextLimit
|
|
29533
|
+
});
|
|
28898
29534
|
const statusText = executeStatus(currentDeps.db, sessionId, currentDeps.protectedTags ?? 20, currentDeps.executeThresholdPercentage, modelKey, currentDeps.historyBudgetPercentage, currentDeps.commitClusterTrigger, currentDeps.executeThresholdTokens, usableContextLimit, {
|
|
28899
29535
|
backlog: getDreamTaskBacklogs(currentDeps.db, currentDeps.projectIdentity, CANONICAL_DREAM_TASKS)
|
|
28900
|
-
}, windowGeometry, resolveTailHygieneStatus(getPiChannel1Baseline(sessionId)));
|
|
29536
|
+
}, windowGeometry, resolveTailHygieneStatus(getPiChannel1Baseline(sessionId)), pressure);
|
|
28901
29537
|
const details = buildStatusDetails(currentDeps, sessionId);
|
|
28902
29538
|
const profileStatus = currentDeps.activeProfile ?? "none";
|
|
28903
|
-
|
|
29539
|
+
const storage = getMagicContextStorageResolution();
|
|
29540
|
+
sendStatus({
|
|
28904
29541
|
title: "/ctx-status",
|
|
28905
29542
|
text: `${statusText}
|
|
28906
29543
|
|
|
28907
|
-
Active profile: ${profileStatus}
|
|
28908
|
-
|
|
29544
|
+
Active profile: ${profileStatus}
|
|
29545
|
+
|
|
29546
|
+
Storage: ${storage.path} (${storage.source})`,
|
|
29547
|
+
level: "info",
|
|
29548
|
+
rpcDisplay: "dialog"
|
|
28909
29549
|
}, details);
|
|
28910
29550
|
} catch (error) {
|
|
28911
|
-
|
|
29551
|
+
sendStatus({
|
|
28912
29552
|
title: "/ctx-status",
|
|
28913
29553
|
text: `## Magic Status — Failed
|
|
28914
29554
|
|
|
@@ -28996,9 +29636,10 @@ function registerCtxWrapupCommand(pi, deps) {
|
|
|
28996
29636
|
pi.registerCommand("ctx-wrapup", {
|
|
28997
29637
|
description: "Compact older Magic Context history while keeping the newest messages raw",
|
|
28998
29638
|
handler: async (args, ctx) => {
|
|
29639
|
+
const sendStatus = createCtxStatusSender(pi, ctx);
|
|
28999
29640
|
const sessionId = resolveSessionId(ctx);
|
|
29000
29641
|
if (!sessionId) {
|
|
29001
|
-
|
|
29642
|
+
sendStatus({
|
|
29002
29643
|
title: "/ctx-wrapup",
|
|
29003
29644
|
text: `## Magic Wrapup
|
|
29004
29645
|
|
|
@@ -29009,7 +29650,7 @@ No active Pi session is available.`,
|
|
|
29009
29650
|
}
|
|
29010
29651
|
const currentDeps = deps.resolveRuntimeDeps?.(ctx) ?? deps;
|
|
29011
29652
|
if (currentDeps.compactionOff) {
|
|
29012
|
-
|
|
29653
|
+
sendStatus({
|
|
29013
29654
|
title: "/ctx-wrapup",
|
|
29014
29655
|
text: COMPACTION_OFF_COMMAND_UNAVAILABLE,
|
|
29015
29656
|
level: "warning"
|
|
@@ -29018,7 +29659,7 @@ No active Pi session is available.`,
|
|
|
29018
29659
|
}
|
|
29019
29660
|
const sessionMeta = getOrCreateSessionMeta(currentDeps.db, sessionId);
|
|
29020
29661
|
if (sessionMeta.isSubagent) {
|
|
29021
|
-
|
|
29662
|
+
sendStatus({
|
|
29022
29663
|
title: "/ctx-wrapup",
|
|
29023
29664
|
text: `## Magic Wrapup — Skipped
|
|
29024
29665
|
|
|
@@ -29028,7 +29669,7 @@ No active Pi session is available.`,
|
|
|
29028
29669
|
return;
|
|
29029
29670
|
}
|
|
29030
29671
|
if (!currentDeps.historianModel) {
|
|
29031
|
-
|
|
29672
|
+
sendStatus({
|
|
29032
29673
|
title: "/ctx-wrapup",
|
|
29033
29674
|
text: "## Magic Wrapup\n\n/ctx-wrapup is unavailable because `historian.model` is not configured.",
|
|
29034
29675
|
level: "error"
|
|
@@ -29037,7 +29678,7 @@ No active Pi session is available.`,
|
|
|
29037
29678
|
}
|
|
29038
29679
|
const parsed = parseWrapupArgs(args);
|
|
29039
29680
|
if (!parsed.ok) {
|
|
29040
|
-
|
|
29681
|
+
sendStatus({
|
|
29041
29682
|
title: "/ctx-wrapup",
|
|
29042
29683
|
text: `## Magic Wrapup — Invalid Arguments
|
|
29043
29684
|
|
|
@@ -29047,7 +29688,7 @@ ${parsed.message}`,
|
|
|
29047
29688
|
return;
|
|
29048
29689
|
}
|
|
29049
29690
|
const result = await runPiWrapup(pi, currentDeps, ctx, sessionId, parsed.messagesToKeep);
|
|
29050
|
-
|
|
29691
|
+
sendStatus({
|
|
29051
29692
|
title: "/ctx-wrapup",
|
|
29052
29693
|
text: result,
|
|
29053
29694
|
level: result.includes("Failed") || result.includes("Partial") ? "error" : "success"
|
|
@@ -29056,6 +29697,7 @@ ${parsed.message}`,
|
|
|
29056
29697
|
});
|
|
29057
29698
|
}
|
|
29058
29699
|
async function runPiWrapup(pi, deps, ctx, sessionId, messagesToKeep) {
|
|
29700
|
+
const sendStatus = createCtxStatusSender(pi, ctx);
|
|
29059
29701
|
if (getOrCreateSessionMeta(deps.db, sessionId).isSubagent) {
|
|
29060
29702
|
return `## Magic Wrapup — Skipped
|
|
29061
29703
|
|
|
@@ -29135,7 +29777,7 @@ No runnable wrapup boundary is available yet; wrapped up 0 messages into 0 compa
|
|
|
29135
29777
|
}
|
|
29136
29778
|
}, 60000);
|
|
29137
29779
|
try {
|
|
29138
|
-
|
|
29780
|
+
sendStatus({
|
|
29139
29781
|
title: "/ctx-wrapup",
|
|
29140
29782
|
text: `## Magic Wrapup
|
|
29141
29783
|
|
|
@@ -29191,7 +29833,7 @@ Eligible history is about ${initialPlan.snapshot.trueRawEligibleTokens.toLocaleS
|
|
|
29191
29833
|
failure = `${ownershipLostReason}; wrapped up through message ${lastEnd}. Run /ctx-wrapup again to continue.`;
|
|
29192
29834
|
break;
|
|
29193
29835
|
}
|
|
29194
|
-
|
|
29836
|
+
sendStatus({
|
|
29195
29837
|
title: "/ctx-wrapup",
|
|
29196
29838
|
text: `## Magic Wrapup
|
|
29197
29839
|
|
|
@@ -29246,7 +29888,7 @@ Chunk ${chunkIndex}: wrapping messages ${plan.snapshot.offset}-${plan.snapshot.e
|
|
|
29246
29888
|
language: deps.language,
|
|
29247
29889
|
compartmentLeaseHolderId: leaseHolder,
|
|
29248
29890
|
readBranchEntries: () => readBranchEntries(ctx),
|
|
29249
|
-
notifyIssue: (text) =>
|
|
29891
|
+
notifyIssue: (text) => sendStatus({
|
|
29250
29892
|
title: "/ctx-wrapup",
|
|
29251
29893
|
text,
|
|
29252
29894
|
level: "warning"
|
|
@@ -29323,7 +29965,7 @@ async function acquireCompartmentLeaseEventually(db, sessionId, renewWrapupMarke
|
|
|
29323
29965
|
return { ok: true, holderId };
|
|
29324
29966
|
if (!renewWrapupMarker({}))
|
|
29325
29967
|
return { ok: false, reason: "ownership_lost" };
|
|
29326
|
-
await new Promise((
|
|
29968
|
+
await new Promise((resolve3) => setTimeout(resolve3, Math.min(LEASE_WAIT_MS, remainingMs())));
|
|
29327
29969
|
}
|
|
29328
29970
|
}
|
|
29329
29971
|
function estimateChunks(tokens, chunkTokens) {
|
|
@@ -29400,42 +30042,6 @@ function registerPiFailClosedSurface(pi, args) {
|
|
|
29400
30042
|
log(`${PREFIX} fail-closed blocking surface registered (${args.reason.kind}); primary turns will error until storage recovers or the build is upgraded`);
|
|
29401
30043
|
}
|
|
29402
30044
|
|
|
29403
|
-
// src/pi-pressure.ts
|
|
29404
|
-
function extractAssistantUsage(message) {
|
|
29405
|
-
if (!message || typeof message !== "object")
|
|
29406
|
-
return null;
|
|
29407
|
-
const m = message;
|
|
29408
|
-
if (m.role !== "assistant")
|
|
29409
|
-
return null;
|
|
29410
|
-
if (!m.usage || typeof m.usage !== "object")
|
|
29411
|
-
return null;
|
|
29412
|
-
const u = m.usage;
|
|
29413
|
-
const result = {};
|
|
29414
|
-
if (typeof u.input === "number")
|
|
29415
|
-
result.input = u.input;
|
|
29416
|
-
if (typeof u.output === "number")
|
|
29417
|
-
result.output = u.output;
|
|
29418
|
-
if (typeof u.cacheRead === "number")
|
|
29419
|
-
result.cacheRead = u.cacheRead;
|
|
29420
|
-
if (typeof u.cacheWrite === "number")
|
|
29421
|
-
result.cacheWrite = u.cacheWrite;
|
|
29422
|
-
if (typeof u.totalTokens === "number")
|
|
29423
|
-
result.totalTokens = u.totalTokens;
|
|
29424
|
-
return result;
|
|
29425
|
-
}
|
|
29426
|
-
function computePiPressure(usage, contextLimit) {
|
|
29427
|
-
if (!usage)
|
|
29428
|
-
return null;
|
|
29429
|
-
const input = usage.input ?? 0;
|
|
29430
|
-
const cacheRead = usage.cacheRead ?? 0;
|
|
29431
|
-
const cacheWrite = usage.cacheWrite ?? 0;
|
|
29432
|
-
const inputTokens = input + cacheRead + cacheWrite;
|
|
29433
|
-
if (inputTokens === 0)
|
|
29434
|
-
return null;
|
|
29435
|
-
const percentage = contextLimit > 0 ? inputTokens / contextLimit * 100 : 0;
|
|
29436
|
-
return { inputTokens, percentage };
|
|
29437
|
-
}
|
|
29438
|
-
|
|
29439
30045
|
// src/strip-tag-prefix.ts
|
|
29440
30046
|
function stripTagPrefixFromAssistantMessage(message) {
|
|
29441
30047
|
if (message.role !== "assistant")
|
|
@@ -29461,19 +30067,43 @@ function stripTagPrefixFromAssistantMessage(message) {
|
|
|
29461
30067
|
|
|
29462
30068
|
// src/index.ts
|
|
29463
30069
|
var PREFIX2 = "[magic-context][pi]";
|
|
29464
|
-
var
|
|
29465
|
-
|
|
29466
|
-
|
|
30070
|
+
var PI_CHILD_INIT_CONTEXT = Symbol.for("magic-context.pi.child-init-context");
|
|
30071
|
+
var SUBAGENT_CHILD_SESSION_CREATED = "subagents:child:session-created";
|
|
30072
|
+
var SUBAGENT_CHILD_DISPOSED = "subagents:child:disposed";
|
|
30073
|
+
var PI_STARTUP_MAINTENANCE_SCHEDULED = Symbol.for("magic-context.pi.startup-maintenance-scheduled");
|
|
30074
|
+
function getPiChildInitContext() {
|
|
30075
|
+
const globals = globalThis;
|
|
30076
|
+
const existing = globals[PI_CHILD_INIT_CONTEXT];
|
|
30077
|
+
if (existing instanceof AsyncLocalStorage)
|
|
30078
|
+
return existing;
|
|
30079
|
+
const context = new AsyncLocalStorage;
|
|
30080
|
+
globals[PI_CHILD_INIT_CONTEXT] = context;
|
|
30081
|
+
return context;
|
|
29467
30082
|
}
|
|
29468
|
-
function
|
|
29469
|
-
|
|
30083
|
+
function isPiInProcessSubagentInit() {
|
|
30084
|
+
return getPiChildInitContext().getStore() === true;
|
|
29470
30085
|
}
|
|
29471
|
-
function
|
|
29472
|
-
|
|
29473
|
-
|
|
29474
|
-
|
|
29475
|
-
|
|
29476
|
-
|
|
30086
|
+
function clearPiInProcessSubagentInitContext() {
|
|
30087
|
+
getPiChildInitContext().enterWith(false);
|
|
30088
|
+
}
|
|
30089
|
+
function registerPiSubagentInitContext(pi) {
|
|
30090
|
+
const context = getPiChildInitContext();
|
|
30091
|
+
const unsubscribeCreated = pi.events.on(SUBAGENT_CHILD_SESSION_CREATED, () => context.enterWith(true));
|
|
30092
|
+
const unsubscribeDisposed = pi.events.on(SUBAGENT_CHILD_DISPOSED, () => context.enterWith(false));
|
|
30093
|
+
return () => {
|
|
30094
|
+
unsubscribeCreated();
|
|
30095
|
+
unsubscribeDisposed();
|
|
30096
|
+
};
|
|
30097
|
+
}
|
|
30098
|
+
function claimPiStartupMaintenance() {
|
|
30099
|
+
const globals = globalThis;
|
|
30100
|
+
if (globals[PI_STARTUP_MAINTENANCE_SCHEDULED] === true)
|
|
30101
|
+
return false;
|
|
30102
|
+
globals[PI_STARTUP_MAINTENANCE_SCHEDULED] = true;
|
|
30103
|
+
return true;
|
|
30104
|
+
}
|
|
30105
|
+
function clearPiStartupMaintenanceClaim() {
|
|
30106
|
+
delete globalThis[PI_STARTUP_MAINTENANCE_SCHEDULED];
|
|
29477
30107
|
}
|
|
29478
30108
|
function resolveCurrentProject(ctx, allowHomeProject = false) {
|
|
29479
30109
|
const projectDir = ctx.cwd;
|
|
@@ -29587,7 +30217,7 @@ function ensureConfigLocationsMigrated(dir) {
|
|
|
29587
30217
|
});
|
|
29588
30218
|
}
|
|
29589
30219
|
function logPiConfigLoad(args) {
|
|
29590
|
-
const key =
|
|
30220
|
+
const key = resolve3(args.dir);
|
|
29591
30221
|
if (args.dedupe && loggedPiConfigDirs.has(key))
|
|
29592
30222
|
return;
|
|
29593
30223
|
if (args.dedupe) {
|
|
@@ -29607,9 +30237,9 @@ var __test = {
|
|
|
29607
30237
|
resetLoggedPiConfigDirs() {
|
|
29608
30238
|
loggedPiConfigDirs.clear();
|
|
29609
30239
|
},
|
|
29610
|
-
|
|
29611
|
-
|
|
29612
|
-
|
|
30240
|
+
clearPiInProcessSubagentInitContext,
|
|
30241
|
+
claimPiStartupMaintenance,
|
|
30242
|
+
clearPiStartupMaintenanceClaim
|
|
29613
30243
|
};
|
|
29614
30244
|
function formatTokens(value) {
|
|
29615
30245
|
return value.toLocaleString();
|
|
@@ -29681,7 +30311,7 @@ async function persistPiPressureFromMessageEnd(args) {
|
|
|
29681
30311
|
}
|
|
29682
30312
|
var PLUGIN_VERSION = (() => {
|
|
29683
30313
|
try {
|
|
29684
|
-
const req =
|
|
30314
|
+
const req = createRequire2(import.meta.url);
|
|
29685
30315
|
return req("../package.json").version;
|
|
29686
30316
|
} catch {
|
|
29687
30317
|
return "0.0.0";
|
|
@@ -29722,6 +30352,8 @@ function resolveHistorianFromConfig(config) {
|
|
|
29722
30352
|
fallbackModels,
|
|
29723
30353
|
historianChunkTokens,
|
|
29724
30354
|
timeoutMs: config.historian_timeout_ms,
|
|
30355
|
+
temperature: historian?.temperature ?? 0.1,
|
|
30356
|
+
maxOutputTokens: historian?.maxTokens ?? 32000,
|
|
29725
30357
|
twoPass: historian?.two_pass === true,
|
|
29726
30358
|
thinkingLevel: resolved.primary?.qualifier,
|
|
29727
30359
|
executeThresholdPercentage: config.execute_threshold_percentage,
|
|
@@ -29754,11 +30386,12 @@ async function src_default(pi) {
|
|
|
29754
30386
|
log(`${PREFIX2} subagent child detected (${MAGIC_CONTEXT_PI_SUBAGENT_ENV}=1); skipping full extension registration`);
|
|
29755
30387
|
return;
|
|
29756
30388
|
}
|
|
29757
|
-
if (
|
|
29758
|
-
log(`${PREFIX2} in-process
|
|
30389
|
+
if (isPiInProcessSubagentInit()) {
|
|
30390
|
+
log(`${PREFIX2} in-process subagent child detected; skipping full extension registration`);
|
|
29759
30391
|
return;
|
|
29760
30392
|
}
|
|
29761
|
-
|
|
30393
|
+
const unregisterPiSubagentInitContext = registerPiSubagentInitContext(pi);
|
|
30394
|
+
registerPiSubagentInitContextCleanup(pi, unregisterPiSubagentInitContext);
|
|
29762
30395
|
beginBootQuietPeriod();
|
|
29763
30396
|
const bootProjectDir = process.cwd();
|
|
29764
30397
|
ensureConfigLocationsMigrated(bootProjectDir);
|
|
@@ -29769,7 +30402,7 @@ async function src_default(pi) {
|
|
|
29769
30402
|
mmapSizeMb: bootConfig.config.sqlite.mmap_size_mb
|
|
29770
30403
|
});
|
|
29771
30404
|
const storageDir = getMagicContextStorageDir();
|
|
29772
|
-
const dbPath =
|
|
30405
|
+
const dbPath = join6(storageDir, "context.db");
|
|
29773
30406
|
let db;
|
|
29774
30407
|
let openFailureCause = null;
|
|
29775
30408
|
try {
|
|
@@ -29830,27 +30463,44 @@ async function src_default(pi) {
|
|
|
29830
30463
|
}
|
|
29831
30464
|
async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
29832
30465
|
const db = database;
|
|
29833
|
-
|
|
29834
|
-
|
|
29835
|
-
|
|
30466
|
+
if (claimPiStartupMaintenance()) {
|
|
30467
|
+
scheduleAfterBootQuiet(() => {
|
|
30468
|
+
runDeferredV22Backfill(db).catch((err) => {
|
|
30469
|
+
warn(`[v22-backfill] background runner failed: ${err}`);
|
|
30470
|
+
});
|
|
29836
30471
|
});
|
|
29837
|
-
|
|
29838
|
-
|
|
29839
|
-
|
|
29840
|
-
|
|
29841
|
-
|
|
29842
|
-
|
|
29843
|
-
|
|
29844
|
-
|
|
29845
|
-
|
|
29846
|
-
|
|
29847
|
-
|
|
29848
|
-
|
|
29849
|
-
|
|
29850
|
-
|
|
29851
|
-
|
|
30472
|
+
scheduleAfterBootQuiet(() => {
|
|
30473
|
+
(async () => {
|
|
30474
|
+
try {
|
|
30475
|
+
let sessions;
|
|
30476
|
+
await runSessionProjectBackfill(database, async (afterSessionId, limit) => {
|
|
30477
|
+
if (!sessions) {
|
|
30478
|
+
const api = await loadDefaultPiSessionApi();
|
|
30479
|
+
const sessionsById2 = new Map;
|
|
30480
|
+
for (const session of await api.listSessions()) {
|
|
30481
|
+
const sessionId = typeof session?.id === "string" ? session.id : "";
|
|
30482
|
+
const directory = typeof session?.cwd === "string" ? session.cwd : "";
|
|
30483
|
+
if (sessionId && (!sessionsById2.has(sessionId) || directory)) {
|
|
30484
|
+
sessionsById2.set(sessionId, { sessionId, directory });
|
|
30485
|
+
}
|
|
30486
|
+
}
|
|
30487
|
+
sessions = [...sessionsById2.values()];
|
|
30488
|
+
}
|
|
30489
|
+
const offset = afterSessionId === null ? 0 : sessions.findIndex((session) => session.sessionId === afterSessionId) + 1;
|
|
30490
|
+
return sessions.slice(offset, offset + limit);
|
|
30491
|
+
});
|
|
30492
|
+
} catch (err) {
|
|
30493
|
+
warn(`[session-projects] background runner failed: ${err}`);
|
|
30494
|
+
}
|
|
30495
|
+
})();
|
|
30496
|
+
}, 0);
|
|
30497
|
+
}
|
|
29852
30498
|
const projectDir = process.cwd();
|
|
29853
30499
|
const seenDreamerProjectIdentities = new Set;
|
|
30500
|
+
const dreamerRegistrationOwner = {};
|
|
30501
|
+
const commandLifecycleController = new AbortController;
|
|
30502
|
+
registerCtxStatusLifecycleSignal(pi, commandLifecycleController.signal);
|
|
30503
|
+
let sessionShuttingDown = false;
|
|
29854
30504
|
ensureConfigLocationsMigrated(projectDir);
|
|
29855
30505
|
const { config, warnings, loadedFromPaths, registrationPromptSurface } = loadPiConfig({
|
|
29856
30506
|
cwd: projectDir
|
|
@@ -29993,6 +30643,32 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
29993
30643
|
}
|
|
29994
30644
|
const bootProjectDeps = buildProjectDeps(projectDir, projectIdentity, config);
|
|
29995
30645
|
projectDepsByDir.set(projectDir, bootProjectDeps);
|
|
30646
|
+
function syncDreamerProjectRegistration(current) {
|
|
30647
|
+
if (sessionShuttingDown)
|
|
30648
|
+
return;
|
|
30649
|
+
seenDreamerProjectIdentities.add(current.projectIdentity);
|
|
30650
|
+
if (!current.dreamerConfig) {
|
|
30651
|
+
unregisterPiDreamerProject({
|
|
30652
|
+
projectIdentity: current.projectIdentity,
|
|
30653
|
+
registrationOwner: dreamerRegistrationOwner
|
|
30654
|
+
});
|
|
30655
|
+
return;
|
|
30656
|
+
}
|
|
30657
|
+
registerPiDreamerProject({
|
|
30658
|
+
db,
|
|
30659
|
+
projectDir: current.projectDir,
|
|
30660
|
+
projectIdentity: current.projectIdentity,
|
|
30661
|
+
registrationOwner: dreamerRegistrationOwner,
|
|
30662
|
+
config: current.dreamerConfig,
|
|
30663
|
+
embeddingConfig: current.config.embedding,
|
|
30664
|
+
memoryEnabled: current.config.memory.enabled,
|
|
30665
|
+
retinaHandoff: current.config.smart_notes.retina_handoff,
|
|
30666
|
+
mural: current.config.mural,
|
|
30667
|
+
language: current.config.language,
|
|
30668
|
+
gitCommitIndexing: current.config.memory.git_commit_indexing,
|
|
30669
|
+
onAdjunctsRefreshNeeded: signalPiSystemPromptRefreshForProject
|
|
30670
|
+
});
|
|
30671
|
+
}
|
|
29996
30672
|
const todowriteEnabled = bootProjectDeps.config.todowrite.enabled !== false;
|
|
29997
30673
|
const todowriteOverlayEnabled = todowriteEnabled && bootProjectDeps.config.todowrite.overlay !== false;
|
|
29998
30674
|
registerMagicContextTools(pi, {
|
|
@@ -30187,7 +30863,9 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
30187
30863
|
},
|
|
30188
30864
|
dreamerEnabled: bootProjectDeps.dreamerEnabled,
|
|
30189
30865
|
resolveDreamerEnabled: (ctx) => resolveCurrentProjectDeps(ctx).dreamerEnabled,
|
|
30190
|
-
onProjectSeen: (identity) => seenDreamerProjectIdentities.add(identity)
|
|
30866
|
+
onProjectSeen: (identity) => seenDreamerProjectIdentities.add(identity),
|
|
30867
|
+
ensureRegistered: (ctx) => syncDreamerProjectRegistration(resolveCurrentProjectDeps(ctx)),
|
|
30868
|
+
registrationOwner: dreamerRegistrationOwner
|
|
30191
30869
|
});
|
|
30192
30870
|
info("registered /ctx-dream");
|
|
30193
30871
|
registerCtxEmbedCommand(pi, {
|
|
@@ -30207,19 +30885,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
30207
30885
|
info("registered /ctx-embed");
|
|
30208
30886
|
const dreamerConfig = bootProjectDeps.dreamerConfig;
|
|
30209
30887
|
if (dreamerConfig) {
|
|
30210
|
-
|
|
30211
|
-
db,
|
|
30212
|
-
projectDir,
|
|
30213
|
-
projectIdentity,
|
|
30214
|
-
config: dreamerConfig,
|
|
30215
|
-
embeddingConfig: bootProjectDeps.config.embedding,
|
|
30216
|
-
memoryEnabled: bootProjectDeps.config.memory.enabled,
|
|
30217
|
-
retinaHandoff: bootProjectDeps.config.smart_notes.retina_handoff,
|
|
30218
|
-
mural: bootProjectDeps.config.mural,
|
|
30219
|
-
language: bootProjectDeps.config.language,
|
|
30220
|
-
gitCommitIndexing: bootProjectDeps.config.memory.git_commit_indexing,
|
|
30221
|
-
onAdjunctsRefreshNeeded: signalPiSystemPromptRefreshForProject
|
|
30222
|
-
});
|
|
30888
|
+
syncDreamerProjectRegistration(bootProjectDeps);
|
|
30223
30889
|
info(`registered dreamer (${summarizeDreamSchedule(dreamerConfig)})`);
|
|
30224
30890
|
} else {
|
|
30225
30891
|
info(bootProjectDeps.dreamerEnabled ? "registered dreamer: DISABLED (no dreamer config)" : "registered dreamer: DISABLED (dreamer.disable=true or no dreamer config)");
|
|
@@ -30250,33 +30916,10 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
30250
30916
|
projectIdentity: effectiveProjectDeps.projectIdentity
|
|
30251
30917
|
};
|
|
30252
30918
|
const effectiveConfig = effectiveProjectDeps.config;
|
|
30253
|
-
|
|
30254
|
-
|
|
30255
|
-
|
|
30256
|
-
|
|
30257
|
-
registerPiDreamerProject({
|
|
30258
|
-
db,
|
|
30259
|
-
projectDir: currentProject.projectDir,
|
|
30260
|
-
projectIdentity: currentProject.projectIdentity,
|
|
30261
|
-
config: effectiveDreamerConfig,
|
|
30262
|
-
embeddingConfig: effectiveConfig.embedding,
|
|
30263
|
-
memoryEnabled: effectiveConfig.memory.enabled,
|
|
30264
|
-
retinaHandoff: effectiveConfig.smart_notes.retina_handoff,
|
|
30265
|
-
language: effectiveConfig.language,
|
|
30266
|
-
gitCommitIndexing: effectiveConfig.memory.git_commit_indexing,
|
|
30267
|
-
onAdjunctsRefreshNeeded: signalPiSystemPromptRefreshForProject
|
|
30268
|
-
});
|
|
30269
|
-
} catch (err) {
|
|
30270
|
-
warn("before_agent_start: registerPiDreamerProject threw:", err);
|
|
30271
|
-
}
|
|
30272
|
-
} else {
|
|
30273
|
-
try {
|
|
30274
|
-
unregisterPiDreamerProject({
|
|
30275
|
-
projectIdentity: currentProject.projectIdentity
|
|
30276
|
-
});
|
|
30277
|
-
} catch (err) {
|
|
30278
|
-
warn("before_agent_start: unregisterPiDreamerProject threw:", err);
|
|
30279
|
-
}
|
|
30919
|
+
try {
|
|
30920
|
+
syncDreamerProjectRegistration(effectiveProjectDeps);
|
|
30921
|
+
} catch (err) {
|
|
30922
|
+
warn("before_agent_start: dreamer registration sync threw:", err);
|
|
30280
30923
|
}
|
|
30281
30924
|
const sm = ctx.sessionManager;
|
|
30282
30925
|
let sessionId;
|
|
@@ -30538,39 +31181,47 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
30538
31181
|
}
|
|
30539
31182
|
});
|
|
30540
31183
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
31184
|
+
sessionShuttingDown = true;
|
|
31185
|
+
commandLifecycleController.abort();
|
|
30541
31186
|
const SHUTDOWN_DRAIN_MS = 5000;
|
|
31187
|
+
const sessionId = resolveSessionId(ctx);
|
|
30542
31188
|
try {
|
|
30543
|
-
|
|
31189
|
+
for (const identity of seenDreamerProjectIdentities) {
|
|
31190
|
+
unregisterPiDreamerProject({
|
|
31191
|
+
projectIdentity: identity,
|
|
31192
|
+
registrationOwner: dreamerRegistrationOwner
|
|
31193
|
+
});
|
|
31194
|
+
}
|
|
30544
31195
|
} catch (err) {
|
|
30545
|
-
warn("shutdown:
|
|
31196
|
+
warn("shutdown: unregisterPiDreamerProject threw:", err);
|
|
30546
31197
|
}
|
|
30547
|
-
|
|
30548
|
-
|
|
30549
|
-
|
|
30550
|
-
|
|
31198
|
+
if (sessionId) {
|
|
31199
|
+
try {
|
|
31200
|
+
await withTimeout(awaitInFlightHistorians(sessionId), SHUTDOWN_DRAIN_MS);
|
|
31201
|
+
} catch (err) {
|
|
31202
|
+
warn("shutdown: historian drain threw:", err);
|
|
31203
|
+
}
|
|
31204
|
+
try {
|
|
31205
|
+
await withTimeout(awaitInFlightRecomps(sessionId), SHUTDOWN_DRAIN_MS);
|
|
31206
|
+
} catch (err) {
|
|
31207
|
+
warn("shutdown: recomp drain threw:", err);
|
|
31208
|
+
}
|
|
31209
|
+
abortInFlightRecomps(sessionId);
|
|
30551
31210
|
}
|
|
30552
31211
|
try {
|
|
30553
|
-
await withTimeout(awaitInFlightDreamers(), SHUTDOWN_DRAIN_MS);
|
|
31212
|
+
await withTimeout(awaitInFlightDreamers(dreamerRegistrationOwner), SHUTDOWN_DRAIN_MS);
|
|
30554
31213
|
} catch (err) {
|
|
30555
31214
|
warn("shutdown: dreamer drain threw:", err);
|
|
30556
31215
|
}
|
|
30557
|
-
try {
|
|
30558
|
-
for (const identity of seenDreamerProjectIdentities) {
|
|
30559
|
-
unregisterPiDreamerProject({ projectIdentity: identity });
|
|
30560
|
-
}
|
|
30561
|
-
} catch (err) {
|
|
30562
|
-
warn("shutdown: unregisterPiDreamerProject threw:", err);
|
|
30563
|
-
}
|
|
30564
31216
|
try {
|
|
30565
31217
|
const sm = ctx.sessionManager;
|
|
30566
|
-
const
|
|
30567
|
-
if (typeof
|
|
30568
|
-
clearPiSystemPromptSession(
|
|
30569
|
-
promptSurfaceGuidanceEpochs.clear(
|
|
30570
|
-
clearContextHandlerSession(
|
|
31218
|
+
const sessionId2 = typeof sm?.getSessionId === "function" ? sm.getSessionId() : undefined;
|
|
31219
|
+
if (typeof sessionId2 === "string" && sessionId2.length > 0) {
|
|
31220
|
+
clearPiSystemPromptSession(sessionId2);
|
|
31221
|
+
promptSurfaceGuidanceEpochs.clear(sessionId2);
|
|
31222
|
+
clearContextHandlerSession(sessionId2);
|
|
30571
31223
|
}
|
|
30572
31224
|
} catch {}
|
|
30573
|
-
clearPiMagicContextActive();
|
|
30574
31225
|
});
|
|
30575
31226
|
pi.on("session_before_switch", (_event, ctx) => {
|
|
30576
31227
|
try {
|
|
@@ -30584,6 +31235,9 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
|
|
|
30584
31235
|
} catch {}
|
|
30585
31236
|
});
|
|
30586
31237
|
}
|
|
31238
|
+
function registerPiSubagentInitContextCleanup(pi, unsubscribe) {
|
|
31239
|
+
pi.on("session_shutdown", unsubscribe);
|
|
31240
|
+
}
|
|
30587
31241
|
function formatExecuteThresholdForLog(value) {
|
|
30588
31242
|
if (value === undefined)
|
|
30589
31243
|
return "65%";
|