@cortexkit/pi-magic-context 0.40.1 → 0.41.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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,
@@ -187,6 +190,7 @@ import {
187
190
  getOverflowState,
188
191
  getPendingCompactionMarkerState,
189
192
  getPendingOps,
193
+ getPendingOpsCount,
190
194
  getPendingPiCompactionMarkerState,
191
195
  getPendingSmartNotes,
192
196
  getPersistedNoteNudge,
@@ -230,7 +234,6 @@ import {
230
234
  hasMuralCueColumns,
231
235
  hasPiFallbackMessageTags,
232
236
  hasPiFallbackToolOwnerTags,
233
- hasShareabilitySensitiveText,
234
237
  incrementHistorianFailure,
235
238
  indexCommitsForProject,
236
239
  indexMessagesAfterOrdinal,
@@ -256,6 +259,8 @@ import {
256
259
  isWrapupInProgress,
257
260
  loadPiConfig,
258
261
  loadProtectedTailMeta,
262
+ logSlowWriteTransaction,
263
+ markChannel1PostReduceGracePending,
259
264
  markGitSweepSuccessAndRelease,
260
265
  markNoteChecked,
261
266
  markNoteReady,
@@ -298,9 +303,11 @@ import {
298
303
  readGitFileChangeTimesSince,
299
304
  readGitHead,
300
305
  readPiSessionMessages,
306
+ readPiSessionSnapshot,
301
307
  readProjectDocsCanonical,
302
308
  readRawSessionMessages,
303
309
  readSessionChunk,
310
+ reclaimableToolOutputCount,
304
311
  recordHistorianDrainFailure,
305
312
  recordMemoryMapping,
306
313
  recordMemoryVerifications,
@@ -327,8 +334,6 @@ import {
327
334
  replaceCompartmentChunkEmbeddings,
328
335
  replaceSourceContent,
329
336
  reserveProtectedTailDrainTokens,
330
- resetLastNudgeCycle,
331
- resetLastNudgeCycleIfTailShrank,
332
337
  resolveCompactionModeRecord,
333
338
  resolveGitTopLevel,
334
339
  resolveModelConfigOrDefault,
@@ -368,7 +373,6 @@ import {
368
373
  setStoragePrivatePermissionEnforcement,
369
374
  setTodoSnapshot,
370
375
  shouldBypassFailClosedBlock,
371
- shouldUseStickyChannel1Reminder,
372
376
  sourceNameForMemory,
373
377
  stripChannel1ReminderSpans,
374
378
  stripMemoryMuralBlock,
@@ -403,7 +407,7 @@ import {
403
407
  withMigrationLanguageDirective,
404
408
  withRawMessageProvider,
405
409
  withRawSessionMessageCache
406
- } from "./index-fre7qcqg.js";
410
+ } from "./index-0jgz8yja.js";
407
411
  import {
408
412
  pushNotification
409
413
  } from "./index-hw34xmzk.js";
@@ -411,19 +415,22 @@ import {
411
415
  ensureCortexKitArtifactGitignore,
412
416
  getHarness,
413
417
  getMagicContextStorageDir,
418
+ getMagicContextStorageResolution,
414
419
  getProjectMagicContextHistorianDir,
420
+ hasShareabilitySensitiveText,
415
421
  log,
416
422
  sessionLog,
417
423
  setHarness
418
- } from "./index-hk6sj7dn.js";
424
+ } from "./index-q2vgz0j1.js";
419
425
  import {
420
426
  __require,
421
427
  __toESM
422
428
  } from "./index-1yh8g550.js";
423
429
 
424
430
  // src/index.ts
425
- import { createRequire as createRequire3 } from "node:module";
426
- import { join as join5, resolve as resolve2 } from "node:path";
431
+ import { AsyncLocalStorage } from "node:async_hooks";
432
+ import { createRequire as createRequire2 } from "node:module";
433
+ import { join as join6, resolve as resolve3 } from "node:path";
427
434
 
428
435
  // ../plugin/src/shared/model-resolution.ts
429
436
  function asRecord(value) {
@@ -1432,7 +1439,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
1432
1439
  }
1433
1440
 
1434
1441
  // ../plugin/src/hooks/magic-context/event-resolvers.ts
1435
- var DEFAULT_CONTEXT_LIMIT = 128000;
1442
+ var DEFAULT_CONTEXT_LIMIT = 200000;
1436
1443
  function resolveCacheTtl(cacheTtl, modelKey) {
1437
1444
  if (typeof cacheTtl === "string") {
1438
1445
  return cacheTtl;
@@ -2065,7 +2072,9 @@ import {
2065
2072
  existsSync as existsSync3,
2066
2073
  mkdtempSync,
2067
2074
  readFileSync as readFileSync2,
2075
+ realpathSync as realpathSync2,
2068
2076
  rmSync,
2077
+ statSync,
2069
2078
  writeFileSync as writeFileSync2
2070
2079
  } from "node:fs";
2071
2080
  import { createRequire } from "node:module";
@@ -2075,7 +2084,8 @@ import {
2075
2084
  dirname,
2076
2085
  isAbsolute,
2077
2086
  join as join2,
2078
- resolve as resolvePath
2087
+ resolve as resolvePath,
2088
+ sep
2079
2089
  } from "node:path";
2080
2090
  import { createInterface } from "node:readline";
2081
2091
  import { fileURLToPath } from "node:url";
@@ -2223,48 +2233,235 @@ function recordChildInvocation(input) {
2223
2233
  }
2224
2234
 
2225
2235
  // src/subagent-runner.ts
2226
- function resolveBundledPiCli() {
2236
+ var PI_CODING_AGENT_MODULE = "@earendil-works/pi-coding-agent";
2237
+ var PI_CODING_AGENT_PACKAGE_NAMES = new Set([
2238
+ PI_CODING_AGENT_MODULE,
2239
+ "@oh-my-pi/pi-coding-agent"
2240
+ ]);
2241
+ var BUN_VIRTUAL_SCRIPT_PREFIX = "/$bunfs/root/";
2242
+ var WINDOWS_PI_EXTENSIONS = [".EXE", ".COM", ".CMD", ".BAT", ".PS1"];
2243
+ var DEFAULT_PI_RESOLUTION_FS = {
2244
+ existsSync: existsSync3,
2245
+ statSync,
2246
+ realpathSync: realpathSync2,
2247
+ readFileSync: readFileSync2
2248
+ };
2249
+ function readPiManifest(packageJsonPath, fs2) {
2227
2250
  try {
2228
- const require_ = createRequire(import.meta.url);
2229
- const pkgJson = require_.resolve("@earendil-works/pi-coding-agent/package.json");
2230
- const cliPath = join2(dirname(pkgJson), "dist/cli.js");
2231
- if (existsSync3(cliPath))
2232
- return cliPath;
2251
+ const manifest = JSON.parse(fs2.readFileSync(packageJsonPath, "utf8"));
2252
+ return manifest && typeof manifest === "object" ? manifest : null;
2253
+ } catch {
2254
+ return null;
2255
+ }
2256
+ }
2257
+ function findPiPackageRoot(startDir, fs2) {
2258
+ let dir = startDir;
2259
+ while (dir !== dirname(dir)) {
2260
+ const manifest = readPiManifest(join2(dir, "package.json"), fs2);
2261
+ if (manifest?.name && typeof manifest.name === "string" && PI_CODING_AGENT_PACKAGE_NAMES.has(manifest.name)) {
2262
+ if (basename(dir) === "dist") {
2263
+ const parentDir = dirname(dir);
2264
+ const parentManifest = readPiManifest(join2(parentDir, "package.json"), fs2);
2265
+ if (typeof parentManifest?.name === "string" && parentManifest.name === manifest.name) {
2266
+ return { dir: parentDir, manifest: parentManifest };
2267
+ }
2268
+ }
2269
+ return { dir, manifest };
2270
+ }
2271
+ dir = dirname(dir);
2272
+ }
2273
+ return null;
2274
+ }
2275
+ function pathIsInside(root, candidate) {
2276
+ return candidate !== root && candidate.startsWith(`${root}${sep}`);
2277
+ }
2278
+ function resolvePiBin(found, fs2, checkedPaths) {
2279
+ const bin = found.manifest.bin;
2280
+ const binEntry = typeof bin === "string" ? bin : bin && typeof bin === "object" && typeof bin.pi === "string" ? bin.pi : undefined;
2281
+ if (!binEntry)
2233
2282
  return null;
2283
+ const packageRoot = resolvePath(found.dir);
2284
+ const candidate = resolvePath(packageRoot, binEntry);
2285
+ checkedPaths.push(candidate);
2286
+ if (isAbsolute(binEntry) || !pathIsInside(packageRoot, candidate)) {
2287
+ return null;
2288
+ }
2289
+ try {
2290
+ if (!fs2.existsSync(candidate) || !fs2.statSync(candidate).isFile()) {
2291
+ return null;
2292
+ }
2293
+ const realRoot = fs2.realpathSync(packageRoot);
2294
+ const realCandidate = fs2.realpathSync(candidate);
2295
+ return pathIsInside(realRoot, realCandidate) ? realCandidate : null;
2234
2296
  } catch {
2235
2297
  return null;
2236
2298
  }
2237
2299
  }
2238
- function resolvePiInvocation() {
2239
- const execPath = process.execPath;
2240
- const currentScript = process.argv[1];
2241
- const isBunVirtualScript = currentScript?.startsWith("/$bunfs/root/") ?? false;
2242
- if (currentScript && !isBunVirtualScript && existsSync3(currentScript)) {
2243
- return { command: execPath, prefixArgs: [currentScript] };
2300
+ function resolvePiCliFromPackageJson(packageJsonPath, fs2, checkedPaths) {
2301
+ checkedPaths.push(packageJsonPath);
2302
+ const manifest = readPiManifest(packageJsonPath, fs2);
2303
+ if (typeof manifest?.name !== "string" || !PI_CODING_AGENT_PACKAGE_NAMES.has(manifest.name)) {
2304
+ return null;
2305
+ }
2306
+ return resolvePiBin({ dir: dirname(packageJsonPath), manifest }, fs2, checkedPaths);
2307
+ }
2308
+ function resolvePiCliFromRunningEntry(entry, fs2) {
2309
+ const checkedPaths = [entry ?? "process.argv[1] (missing)"];
2310
+ if (!entry || entry.startsWith(BUN_VIRTUAL_SCRIPT_PREFIX)) {
2311
+ return { cliPath: null, checkedPaths };
2244
2312
  }
2245
- const execName = basename(execPath).toLowerCase();
2246
- const isGenericRuntime = /^(node|bun)(\.exe)?$/.test(execName);
2247
- if (!isGenericRuntime) {
2313
+ try {
2314
+ if (!fs2.existsSync(entry) || !fs2.statSync(entry).isFile()) {
2315
+ return { cliPath: null, checkedPaths };
2316
+ }
2317
+ const realEntry = fs2.realpathSync(entry);
2318
+ if (realEntry !== entry)
2319
+ checkedPaths.push(realEntry);
2320
+ const found = findPiPackageRoot(dirname(realEntry), fs2);
2321
+ if (!found)
2322
+ return { cliPath: null, checkedPaths };
2323
+ return {
2324
+ cliPath: resolvePiBin(found, fs2, checkedPaths),
2325
+ checkedPaths
2326
+ };
2327
+ } catch {
2328
+ return { cliPath: null, checkedPaths };
2329
+ }
2330
+ }
2331
+ function defaultResolvePiPackageJson(from) {
2332
+ const requesters = [from, import.meta.url];
2333
+ for (const requester of requesters) {
2334
+ try {
2335
+ const require_ = requester ? createRequire(requester.startsWith("file:") ? requester : resolvePath(requester)) : createRequire(import.meta.url);
2336
+ return require_.resolve(`${PI_CODING_AGENT_MODULE}/package.json`);
2337
+ } catch {}
2338
+ }
2339
+ return null;
2340
+ }
2341
+ function resolveBundledPiCli(from, fs2, resolvePackageJson, checkedPaths) {
2342
+ const packageJsonPath = resolvePackageJson(from);
2343
+ if (!packageJsonPath) {
2344
+ checkedPaths.push(`${PI_CODING_AGENT_MODULE}/package.json (unresolvable from running module paths)`);
2345
+ return null;
2346
+ }
2347
+ return resolvePiCliFromPackageJson(packageJsonPath, fs2, checkedPaths);
2348
+ }
2349
+ function resolveWindowsPiCommand(env, fs2) {
2350
+ const pathValue = env.PATH ?? env.Path;
2351
+ if (!pathValue)
2352
+ return { path: null, extension: null, checkedPaths: [] };
2353
+ const pathExt = env.PATHEXT ?? env.PathExt ?? "";
2354
+ const extensions = [];
2355
+ for (const value of pathExt.split(";")) {
2356
+ const extension = value.trim().toUpperCase();
2357
+ if (WINDOWS_PI_EXTENSIONS.includes(extension) && !extensions.includes(extension)) {
2358
+ extensions.push(extension);
2359
+ }
2360
+ }
2361
+ for (const extension of WINDOWS_PI_EXTENSIONS) {
2362
+ if (!extensions.includes(extension))
2363
+ extensions.push(extension);
2364
+ }
2365
+ const checkedPaths = [];
2366
+ for (const rawDir of pathValue.split(";")) {
2367
+ const dir = rawDir.trim().replace(/^"(.*)"$/, "$1");
2368
+ if (!dir)
2369
+ continue;
2370
+ for (const extension of extensions) {
2371
+ const candidate = join2(dir, `pi${extension.toLowerCase()}`);
2372
+ checkedPaths.push(candidate);
2373
+ try {
2374
+ if (fs2.existsSync(candidate) && fs2.statSync(candidate).isFile()) {
2375
+ return { path: candidate, extension, checkedPaths };
2376
+ }
2377
+ } catch {}
2378
+ }
2379
+ }
2380
+ return { path: null, extension: null, checkedPaths };
2381
+ }
2382
+ function isGenericRuntimeExecutable(execPath) {
2383
+ return /^(node(?:js)?\d*|bun)(\.exe)?$/.test(basename(execPath).toLowerCase());
2384
+ }
2385
+ function formatCheckedPaths(checkedPaths) {
2386
+ const unique = [...new Set(checkedPaths)];
2387
+ const visible = unique.slice(0, 12);
2388
+ return unique.length > visible.length ? `${visible.join(", ")} (+${unique.length - visible.length} more)` : visible.join(", ");
2389
+ }
2390
+ function resolvePiInvocation(options = {}) {
2391
+ const fs2 = { ...DEFAULT_PI_RESOLUTION_FS, ...options.fs };
2392
+ const execPath = options.execPath ?? process.execPath;
2393
+ const currentScript = options.argv1 ?? process.argv[1];
2394
+ const platform = options.platform ?? process.platform;
2395
+ const env = options.env ?? process.env;
2396
+ const resolvePackageJson = options.resolvePackageJson ?? defaultResolvePiPackageJson;
2397
+ const running = resolvePiCliFromRunningEntry(currentScript, fs2);
2398
+ const checkedPaths = [...running.checkedPaths];
2399
+ if (running.cliPath) {
2400
+ return { command: execPath, prefixArgs: [running.cliPath] };
2401
+ }
2402
+ if (!isGenericRuntimeExecutable(execPath)) {
2248
2403
  return { command: execPath, prefixArgs: [] };
2249
2404
  }
2250
- const bundled = resolveBundledPiCli();
2405
+ const bundled = resolveBundledPiCli(currentScript, fs2, resolvePackageJson, checkedPaths);
2251
2406
  if (bundled) {
2252
2407
  return { command: execPath, prefixArgs: [bundled] };
2253
2408
  }
2254
- return { command: "pi", prefixArgs: [] };
2409
+ const detectionMiss = `script-detection miss on ${formatCheckedPaths(checkedPaths)}`;
2410
+ if (platform === "win32") {
2411
+ const resolved = resolveWindowsPiCommand(env, fs2);
2412
+ if (resolved.path && resolved.extension) {
2413
+ const diagnostic = `${detectionMiss}; Windows PATHEXT resolved ${resolved.path}`;
2414
+ if (resolved.extension === ".CMD" || resolved.extension === ".BAT") {
2415
+ const command = env.ComSpec?.trim() || env.COMSPEC?.trim() || "cmd.exe";
2416
+ return {
2417
+ command,
2418
+ prefixArgs: ["/d", "/s", "/c", resolved.path],
2419
+ fallbackDiagnostic: diagnostic
2420
+ };
2421
+ }
2422
+ if (resolved.extension === ".PS1") {
2423
+ const command = env.SystemRoot ? join2(env.SystemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe") : "powershell.exe";
2424
+ return {
2425
+ command,
2426
+ prefixArgs: [
2427
+ "-NoLogo",
2428
+ "-NoProfile",
2429
+ "-NonInteractive",
2430
+ "-File",
2431
+ resolved.path
2432
+ ],
2433
+ fallbackDiagnostic: diagnostic
2434
+ };
2435
+ }
2436
+ return {
2437
+ command: resolved.path,
2438
+ prefixArgs: [],
2439
+ fallbackDiagnostic: diagnostic
2440
+ };
2441
+ }
2442
+ checkedPaths.push(...resolved.checkedPaths);
2443
+ }
2444
+ return {
2445
+ command: "pi",
2446
+ prefixArgs: [],
2447
+ fallbackDiagnostic: `script-detection miss on ${formatCheckedPaths(checkedPaths)}`
2448
+ };
2255
2449
  }
2256
- function resolveSubagentEntryPath() {
2450
+ function describeSpawnFailure(error, invocation) {
2451
+ const message = error instanceof Error ? error.message : String(error);
2452
+ return invocation.fallbackDiagnostic ? `${message} after ${invocation.fallbackDiagnostic}` : message;
2453
+ }
2454
+ function resolveSiblingEntryPath(fileName) {
2257
2455
  try {
2258
2456
  const here = dirname(fileURLToPath(import.meta.url));
2259
- const candidate = resolvePath(here, "subagent-entry.js");
2260
- if (existsSync3(candidate))
2261
- return candidate;
2262
- return;
2457
+ const candidate = resolvePath(here, fileName);
2458
+ return existsSync3(candidate) ? candidate : undefined;
2263
2459
  } catch {
2264
2460
  return;
2265
2461
  }
2266
2462
  }
2267
- var SUBAGENT_ENTRY_PATH = resolveSubagentEntryPath();
2463
+ var SUBAGENT_ENTRY_PATH = resolveSiblingEntryPath("subagent-entry.js");
2464
+ var HISTORIAN_CALIBRATION_ENTRY_PATH = resolveSiblingEntryPath("historian-calibration-extension.js");
2268
2465
  var TERMINAL_DRAIN_GRACE_MS = 2000;
2269
2466
  var MAGIC_CONTEXT_PI_SUBAGENT_ENV = "MAGIC_CONTEXT_PI_SUBAGENT";
2270
2467
  function packageRootIsOmp(packageRoot) {
@@ -2338,6 +2535,12 @@ var DREAMER_ACTION_AGENTS = new Set([
2338
2535
  "dreamer",
2339
2536
  "magic-context-dreamer"
2340
2537
  ]);
2538
+ var HISTORIAN_AGENTS = new Set([
2539
+ "magic-context-historian",
2540
+ "historian",
2541
+ "historian-recomp",
2542
+ "historian-editor"
2543
+ ]);
2341
2544
  var SEARCH_ONLY_SUBAGENT_TOOL_AGENTS = new Set([
2342
2545
  "sidekick",
2343
2546
  "dreamer-retrospective",
@@ -2429,7 +2632,7 @@ class PiSubagentRunner {
2429
2632
  extraArgs;
2430
2633
  subagentExtensions;
2431
2634
  constructor(options = {}) {
2432
- this.invocation = options.piBinary ? { command: options.piBinary, prefixArgs: [] } : resolvePiInvocation();
2635
+ this.invocation = options.invocation ?? (options.piBinary ? { command: options.piBinary, prefixArgs: [] } : resolvePiInvocation({ platform: options.platform }));
2433
2636
  this.spawnImpl = options.spawnImpl ?? childProcess.spawn;
2434
2637
  this.platform = options.platform ?? process.platform;
2435
2638
  this.extraArgs = options.extraArgs ?? [];
@@ -2644,7 +2847,13 @@ class PiSubagentRunner {
2644
2847
  cwd: options.cwd,
2645
2848
  env: {
2646
2849
  ...process.env,
2647
- [MAGIC_CONTEXT_PI_SUBAGENT_ENV]: "1"
2850
+ [MAGIC_CONTEXT_PI_SUBAGENT_ENV]: "1",
2851
+ ...options.temperature !== undefined ? {
2852
+ MAGIC_CONTEXT_HISTORIAN_TEMPERATURE: String(options.temperature)
2853
+ } : {},
2854
+ ...options.maxOutputTokens !== undefined ? {
2855
+ MAGIC_CONTEXT_HISTORIAN_MAX_OUTPUT_TOKENS: String(options.maxOutputTokens)
2856
+ } : {}
2648
2857
  },
2649
2858
  stdio: [deliverViaStdin ? "pipe" : "ignore", "pipe", "pipe"]
2650
2859
  });
@@ -2653,7 +2862,7 @@ class PiSubagentRunner {
2653
2862
  settle({
2654
2863
  ok: false,
2655
2864
  reason: "spawn_failed",
2656
- error: error instanceof Error ? error.message : String(error),
2865
+ error: describeSpawnFailure(error, this.invocation),
2657
2866
  durationMs: Date.now() - startTime
2658
2867
  });
2659
2868
  return;
@@ -2842,7 +3051,7 @@ class PiSubagentRunner {
2842
3051
  settle({
2843
3052
  ok: false,
2844
3053
  reason: "spawn_failed",
2845
- error: error instanceof Error ? error.message : String(error),
3054
+ error: describeSpawnFailure(error, this.invocation),
2846
3055
  durationMs: Date.now() - startTime
2847
3056
  });
2848
3057
  });
@@ -2863,10 +3072,11 @@ class PiSubagentRunner {
2863
3072
  if (sawAgentEnd) {
2864
3073
  const trimmedAssistantText = finalAssistantText?.trim() ?? null;
2865
3074
  if (trimmedAssistantText === null || trimmedAssistantText.length === 0) {
3075
+ const emptyAssistantReason = trimmedAssistantText === null ? "pi agent_end did not include an assistant message" : "pi assistant produced empty text";
2866
3076
  settle({
2867
3077
  ok: false,
2868
3078
  reason: "no_assistant",
2869
- error: trimmedAssistantText === null ? "pi agent_end did not include an assistant message" : "pi assistant produced empty text",
3079
+ error: finalErrorMessage ? `${emptyAssistantReason} (provider error: ${finalErrorMessage})` : emptyAssistantReason,
2870
3080
  durationMs: Date.now() - startTime,
2871
3081
  meta: {
2872
3082
  stderr: stderr.length > 0 ? stderr : undefined,
@@ -3036,6 +3246,10 @@ function buildArgs(options, opts) {
3036
3246
  args.push("--magic-context-dreamer-actions");
3037
3247
  }
3038
3248
  }
3249
+ const historianCalibrationEntryPath = opts?.historianCalibrationEntryPath === undefined ? HISTORIAN_CALIBRATION_ENTRY_PATH : opts.historianCalibrationEntryPath;
3250
+ if (HISTORIAN_AGENTS.has(options.agent) && historianCalibrationEntryPath) {
3251
+ args.push("--extension", historianCalibrationEntryPath);
3252
+ }
3039
3253
  const strictTools = STRICT_TOOL_ALLOWLIST.get(options.agent);
3040
3254
  if (strictTools === undefined) {
3041
3255
  sessionLog(options.accountingSessionId ?? "pi-subagent", `Pi subagent agent "${options.agent}" has no strict tool allow-list; forcing --no-tools`);
@@ -3772,7 +3986,7 @@ async function sendIgnoredMessageNow(client, sessionId, text, params, forcePersi
3772
3986
  queueIgnoredNotification({ client, sessionId, text, params, forcePersist });
3773
3987
  return "queued";
3774
3988
  }
3775
- const { waitForSafeNotificationTarget } = await import("./safe-notification-target-q00qgjm6.js");
3989
+ const { waitForSafeNotificationTarget } = await import("./safe-notification-target-hjcb423r.js");
3776
3990
  if (await waitForSafeNotificationTarget(client, sessionId) === "skip") {
3777
3991
  sessionLog(sessionId, "notification skipped (session not titled yet)");
3778
3992
  return "skipped";
@@ -4380,13 +4594,17 @@ function peekLeaseHolderAndExpiry(db, expectedHolder, leaseKey = DREAMING_LEASE_
4380
4594
  function leaseOwnershipMatches(db, expectedHolder, expectedGeneration, leaseKey = DREAMING_LEASE_KEY) {
4381
4595
  return getLeaseGeneration(db, leaseKey) === expectedGeneration && peekLeaseHolderAndExpiry(db, expectedHolder, leaseKey);
4382
4596
  }
4383
- function runImmediate(db, body) {
4597
+ function runImmediate(db, body, site, slowWriteThresholdMs) {
4598
+ const transactionStartedAt = site === undefined ? undefined : performance.now();
4384
4599
  db.exec("BEGIN IMMEDIATE");
4385
4600
  let committed = false;
4386
4601
  try {
4387
4602
  const result = body();
4388
4603
  db.exec("COMMIT");
4389
4604
  committed = true;
4605
+ if (site !== undefined && transactionStartedAt !== undefined) {
4606
+ logSlowWriteTransaction(site, transactionStartedAt, slowWriteThresholdMs);
4607
+ }
4390
4608
  return result;
4391
4609
  } finally {
4392
4610
  if (!committed) {
@@ -4428,13 +4646,13 @@ function renewLease(db, holderId, leaseKey = DREAMING_LEASE_KEY, expectedGenerat
4428
4646
  return true;
4429
4647
  });
4430
4648
  }
4431
- function runLeaseGuardedWrite(db, holderId, leaseKey, fn) {
4649
+ function runLeaseGuardedWrite(db, holderId, leaseKey, fn, slowWriteThresholdMs) {
4432
4650
  return runImmediate(db, () => {
4433
4651
  if (!peekLeaseHolderAndExpiry(db, holderId, leaseKey)) {
4434
4652
  throw new Error("Dream lease lost before guarded write");
4435
4653
  }
4436
4654
  return fn();
4437
- });
4655
+ }, `lease-guarded-write:${leaseKey}`, slowWriteThresholdMs);
4438
4656
  }
4439
4657
  var LEASE_HEARTBEAT_INTERVAL_MS = 60 * 1000;
4440
4658
  function startLeaseHeartbeat(db, holderId, leaseKey, onLost, intervalOrAcquisition = LEASE_HEARTBEAT_INTERVAL_MS) {
@@ -5966,7 +6184,7 @@ async function classifyOneChunk(args, chunk, anchors, sliceMs, signal) {
5966
6184
  query: { directory: args.sessionDirectory },
5967
6185
  body: {
5968
6186
  agent: DREAMER_CLASSIFIER_AGENT,
5969
- system: CLASSIFY_SYSTEM_PROMPT,
6187
+ system: withContentLanguageDirective(CLASSIFY_SYSTEM_PROMPT, args.language),
5970
6188
  ...modelBodyField(args.model),
5971
6189
  parts: [{ type: "text", text: prompt, synthetic: true }]
5972
6190
  }
@@ -6029,6 +6247,8 @@ async function runClassifyThroughModule(args, chunk, anchors, signal) {
6029
6247
  memories: chunk.map(toPromptMemory2),
6030
6248
  anchors
6031
6249
  });
6250
+ const modelChain = [args.model, ...args.fallbackModels ?? []].map(toModelEntry).filter((entry) => entry !== undefined).map((entry) => entry.model);
6251
+ const resolvedModelChain = [...new Set(modelChain)];
6032
6252
  const response = await args.moduleClient?.call({
6033
6253
  sessionId: args.moduleSessionId,
6034
6254
  projectRoot: args.moduleProjectRoot,
@@ -6040,6 +6260,7 @@ async function runClassifyThroughModule(args, chunk, anchors, signal) {
6040
6260
  task: "classify",
6041
6261
  command_id: `classify:${args.moduleCommandId ?? Date.now()}:${createHash4("sha256").update(chunk.map((candidate) => candidate.id).join(",")).digest("hex").slice(0, 24)}`,
6042
6262
  authority_generation: args.moduleAuthorityGeneration,
6263
+ ...resolvedModelChain.length > 0 ? { model_chain: resolvedModelChain } : {},
6043
6264
  payload: {
6044
6265
  prompt_body: prompt,
6045
6266
  items: chunk.map((candidate) => ({
@@ -6713,7 +6934,7 @@ function getAsyncModule() {
6713
6934
  asyncModulePromise ??= (async () => {
6714
6935
  const [{ default: singlefileAsyncifyVariant }, { newQuickJSAsyncWASMModuleFromVariant }] = await Promise.all([
6715
6936
  import("./index-rgdqj8nj.js").then((m)=>__toESM(m.default,1)),
6716
- import("./index-8sseka65.js")
6937
+ import("./index-y63nbvkk.js")
6717
6938
  ]);
6718
6939
  return newQuickJSAsyncWASMModuleFromVariant(singlefileAsyncifyVariant);
6719
6940
  })();
@@ -7843,7 +8064,7 @@ function enforceMaintainDocsProtectedRegions(args) {
7843
8064
  import { createHash as createHash6 } from "node:crypto";
7844
8065
 
7845
8066
  // ../plugin/src/features/magic-context/dreamer/map-memories-prompt.ts
7846
- import { existsSync as existsSync6, statSync } from "node:fs";
8067
+ import { existsSync as existsSync6, statSync as statSync2 } from "node:fs";
7847
8068
  import path4 from "node:path";
7848
8069
  var MAP_MEMORIES_SYSTEM_PROMPT = `You are a memory mapper for the magic-context system. You map project memories to the repository files that back them.
7849
8070
 
@@ -7853,7 +8074,7 @@ Tools (read-only): read, grep, glob, aft_search, aft_outline, aft_zoom. Each mem
7853
8074
 
7854
8075
  For each memory decide ONE of:
7855
8076
  - 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.
8077
+ - 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
8078
 
7858
8079
  Output ONE XML manifest at the very end and NOTHING else — no narration, no per-memory commentary, no reasoning:
7859
8080
  <mappings>
@@ -7864,7 +8085,7 @@ Output ONE XML manifest at the very end and NOTHING else — no narration, no pe
7864
8085
  Rules:
7865
8086
  - Every input memory id MUST appear exactly once.
7866
8087
  - 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".
8088
+ - 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
8089
  - 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
8090
  - Prefer the most specific file(s); do not pad with tangential files. Most memories map to one file; some to a few.
7870
8091
  - 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).`;
@@ -7881,7 +8102,7 @@ function extractMemoryCandidatePaths(content, repoDir) {
7881
8102
  if (!abs.startsWith(`${root}/`))
7882
8103
  continue;
7883
8104
  try {
7884
- if (existsSync6(abs) && statSync(abs).isFile())
8105
+ if (existsSync6(abs) && statSync2(abs).isFile())
7885
8106
  found.add(rel);
7886
8107
  } catch {}
7887
8108
  if (found.size >= MAX_SEED_PATHS_PER_MEMORY)
@@ -7902,7 +8123,7 @@ ${m.content}${seed}`;
7902
8123
 
7903
8124
  Project: ${projectPath}
7904
8125
 
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.
8126
+ 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
8127
 
7907
8128
  <memories>
7908
8129
  ${list}
@@ -7967,6 +8188,21 @@ function validateMapMemoriesManifest(text, expectedIds) {
7967
8188
  return parsed;
7968
8189
  }
7969
8190
 
8191
+ // ../plugin/src/features/magic-context/dreamer/memory-claim-safety.ts
8192
+ 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;
8193
+ 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;
8194
+ 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;
8195
+ 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;
8196
+ var DECISION_AUTHORITY = /\b(?:the\s+)?(?:user|operator|maintainer|owner)\s+(?:decides|chooses|approves|has\s+(?:the\s+)?final\s+say)\b/i;
8197
+ function isDirectiveShapedProjectRule(category, content) {
8198
+ if (category !== "PROJECT_RULES")
8199
+ return false;
8200
+ const text = content.trim();
8201
+ if (!text)
8202
+ return false;
8203
+ return POLICY_SENTENCE_START.test(text) || ACTOR_POLICY.test(text) || BEHAVIORAL_WHEN_CLAUSE.test(text) || WORKFLOW_IMPERATIVE.test(text) || DECISION_AUTHORITY.test(text);
8204
+ }
8205
+
7970
8206
  // ../plugin/src/features/magic-context/dreamer/map-memories.ts
7971
8207
  var MAP_BATCH_SIZE = 80;
7972
8208
  var MAP_BATCH_FLOOR_MS = 240000;
@@ -7981,6 +8217,8 @@ function isTimeoutClassError2(error) {
7981
8217
  function shouldRequeueIndependentMapping(state2, content, repoDir) {
7982
8218
  if (!state2.hasSentinel || state2.files.length > 0)
7983
8219
  return false;
8220
+ if (state2.mappingOrigin === "host_rejected_fallback")
8221
+ return false;
7984
8222
  return extractMemoryCandidatePaths(content, repoDir).length > 0;
7985
8223
  }
7986
8224
  function toMapInput(memory, repoDir) {
@@ -8187,9 +8425,26 @@ async function applyParsedBatchMappings(args, batch, parsed) {
8187
8425
  if (valid.length === 0)
8188
8426
  return { mapped: 0, independent: 0 };
8189
8427
  const planned = [];
8428
+ const batchById = new Map(batch.map((memory) => [memory.id, memory]));
8190
8429
  for (const p of valid) {
8430
+ const memory = batchById.get(p.id);
8431
+ if (!p.independent && p.files.length > 0 && memory && isDirectiveShapedProjectRule(memory.category, memory.content)) {
8432
+ 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`);
8433
+ planned.push({
8434
+ id: p.id,
8435
+ files: [],
8436
+ independent: true,
8437
+ mappingOrigin: "host_rejected_fallback"
8438
+ });
8439
+ continue;
8440
+ }
8191
8441
  if (p.independent) {
8192
- planned.push({ id: p.id, files: [], independent: true });
8442
+ planned.push({
8443
+ id: p.id,
8444
+ files: [],
8445
+ independent: true,
8446
+ mappingOrigin: "mapper"
8447
+ });
8193
8448
  continue;
8194
8449
  }
8195
8450
  if (p.files.length === 0) {
@@ -8199,9 +8454,22 @@ async function applyParsedBatchMappings(args, batch, parsed) {
8199
8454
  cwd: args.sessionDirectory,
8200
8455
  files: p.files
8201
8456
  });
8202
- if (normalized.files.length === 0)
8457
+ if (normalized.files.length === 0) {
8458
+ log(`[dreamer] map-memories: all ${p.files.length} path(s) for memory ${p.id} were rejected; recording host_rejected_fallback`);
8459
+ planned.push({
8460
+ id: p.id,
8461
+ files: [],
8462
+ independent: true,
8463
+ mappingOrigin: "host_rejected_fallback"
8464
+ });
8203
8465
  continue;
8204
- planned.push({ id: p.id, files: normalized.files, independent: false });
8466
+ }
8467
+ planned.push({
8468
+ id: p.id,
8469
+ files: normalized.files,
8470
+ independent: false,
8471
+ mappingOrigin: "mapper"
8472
+ });
8205
8473
  }
8206
8474
  if (planned.length === 0)
8207
8475
  return { mapped: 0, independent: 0 };
@@ -8216,7 +8484,8 @@ async function applyParsedBatchMappings(args, batch, parsed) {
8216
8484
  return {
8217
8485
  memory_id: identity.moduleId,
8218
8486
  content_hash_at_prompt: identity.normalizedHash,
8219
- mapped_files: item.independent ? null : item.files
8487
+ mapped_files: item.independent ? null : item.files,
8488
+ mapping_origin: item.mappingOrigin
8220
8489
  };
8221
8490
  });
8222
8491
  let response;
@@ -8253,7 +8522,7 @@ async function applyParsedBatchMappings(args, batch, parsed) {
8253
8522
  const now = Date.now();
8254
8523
  runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
8255
8524
  for (const item of planned) {
8256
- recordMemoryMapping(args.db, item.id, item.files, now);
8525
+ recordMemoryMapping(args.db, item.id, item.files, now, item.mappingOrigin);
8257
8526
  item.independent ? independent += 1 : mapped += 1;
8258
8527
  }
8259
8528
  });
@@ -9414,24 +9683,29 @@ Each memory below comes with its backing file(s) — the code it makes a claim a
9414
9683
 
9415
9684
  Tools (read-only): read, grep, glob, aft_search, aft_outline, aft_zoom. You read code to check claims; you change nothing.
9416
9685
 
9417
- Decide ONE of three outcomes per memory:
9686
+ Decide ONE of four outcomes per memory:
9418
9687
  - VERIFIED — still accurate. Keep it as-is.
9419
- - UPDATE — the underlying fact is still true but a 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.
9420
- - ARCHIVE — the code CLEARLY contradicts the memory, or the thing it describes no longer exists.
9688
+ - 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.
9689
+ - ARCHIVE — a CODE FACT is positively falsified: the code clearly contradicts it, or the API/symbol/path it describes no longer exists.
9690
+ - SKIP — the claim is not a code fact, or code cannot decide it. Keep it unchanged and do not claim verification.
9691
+
9692
+ 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.
9421
9693
 
9422
- 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, never archived. Archive ONLY when you have positive evidence the code contradicts it.
9694
+ 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
9695
 
9424
9696
  Output ONE XML manifest at the very end and NOTHING else — no narration, no per-memory commentary, no reasoning:
9425
9697
  <verify>
9426
9698
  <verified id="N" files="path/a.ts,path/b.ts"/>
9427
9699
  <update id="M" files="path/c.ts">corrected present-tense content</update>
9700
+ <update id="C" files="path/d.ts" consolidation="true">intentionally consolidated content</update>
9428
9701
  <archive id="K" reason="specific evidence the code contradicts it"/>
9702
+ <skip id="S" reason="behavioral directive; code cannot decide it"/>
9429
9703
  </verify>
9430
9704
 
9431
9705
  Rules:
9432
- - Every input memory id MUST appear exactly once, in exactly one of verified/update/archive.
9706
+ - Every input memory id MUST appear exactly once, in exactly one of verified/update/archive/skip.
9433
9707
  - 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.`;
9708
+ - Default to VERIFIED. skip when code cannot decide the claim. update and archive are the exceptions, not the norm.`;
9435
9709
  function buildVerifyPrompt(projectPath, memories) {
9436
9710
  const list = memories.map((m) => `[${m.id}] ${m.category}
9437
9711
  Content: ${m.content}
@@ -9442,7 +9716,7 @@ Backing files: ${m.mappedFiles.join(", ")}`).join(`
9442
9716
 
9443
9717
  Project: ${projectPath}
9444
9718
 
9445
- Read each memory's backing files, decide verified / update / archive (default verified; be conservative about archiving), then output ONE <verify> manifest covering every id.
9719
+ 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
9720
 
9447
9721
  <memories>
9448
9722
  ${list}
@@ -9456,7 +9730,7 @@ function filesOf(s) {
9456
9730
  return (attrOf(s, "files") ?? "").split(",").map((f) => f.trim()).filter(Boolean);
9457
9731
  }
9458
9732
  function verifyIds(parsed) {
9459
- return [...parsed.verified, ...parsed.updated, ...parsed.archived].map((entry) => entry.id);
9733
+ return [...parsed.verified, ...parsed.updated, ...parsed.archived, ...parsed.skipped].map((entry) => entry.id);
9460
9734
  }
9461
9735
  function verifyBody(text) {
9462
9736
  try {
@@ -9469,7 +9743,7 @@ function verifyBody(text) {
9469
9743
  }
9470
9744
  }
9471
9745
  function parseVerifyManifest(text) {
9472
- const out = { verified: [], updated: [], archived: [] };
9746
+ const out = { verified: [], updated: [], archived: [], skipped: [] };
9473
9747
  const body = verifyBody(text);
9474
9748
  for (const m of body.matchAll(/<verified\b([^>]*)\/?>/g)) {
9475
9749
  const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
@@ -9481,7 +9755,12 @@ function parseVerifyManifest(text) {
9481
9755
  const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
9482
9756
  if (!Number.isInteger(id))
9483
9757
  throw new Error("verify manifest entry missing numeric id");
9484
- out.updated.push({ id, files: filesOf(m[1]), content: (m[2] ?? "").trim() });
9758
+ out.updated.push({
9759
+ id,
9760
+ files: filesOf(m[1]),
9761
+ content: (m[2] ?? "").trim(),
9762
+ consolidation: attrOf(m[1], "consolidation")?.toLowerCase() === "true"
9763
+ });
9485
9764
  }
9486
9765
  for (const m of body.matchAll(/<archive\b([^>]*)\/?>/g)) {
9487
9766
  const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
@@ -9489,6 +9768,12 @@ function parseVerifyManifest(text) {
9489
9768
  throw new Error("verify manifest entry missing numeric id");
9490
9769
  out.archived.push({ id, reason: attrOf(m[1], "reason") ?? "" });
9491
9770
  }
9771
+ for (const m of body.matchAll(/<skip\b([^>]*)\/?>/g)) {
9772
+ const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
9773
+ if (!Number.isInteger(id))
9774
+ throw new Error("verify manifest entry missing numeric id");
9775
+ out.skipped.push({ id, reason: attrOf(m[1], "reason") ?? "" });
9776
+ }
9492
9777
  if (verifyIds(out).length === 0 && body.trim().length > 0) {
9493
9778
  throw new Error(describeUnrecognizedManifestShape(text, "verify", "verified"));
9494
9779
  }
@@ -9528,6 +9813,8 @@ async function runVerify(args) {
9528
9813
  verified: 0,
9529
9814
  updated: 0,
9530
9815
  archived: 0,
9816
+ skipped: 0,
9817
+ refused: 0,
9531
9818
  batches: 0,
9532
9819
  inScope: 0,
9533
9820
  remaining: 0,
@@ -9572,9 +9859,12 @@ async function runVerify(args) {
9572
9859
  result.verified += counts.verified;
9573
9860
  result.updated += counts.updated;
9574
9861
  result.archived += counts.archived;
9575
- result.remaining -= counts.verified + counts.updated + counts.archived;
9862
+ result.skipped += counts.skipped;
9863
+ result.refused += counts.refused;
9864
+ const batchProcessed = counts.verified + counts.updated + counts.archived + counts.skipped + counts.refused;
9865
+ result.remaining -= batchProcessed;
9576
9866
  result.batches += 1;
9577
- args.onProgress?.(result.verified + result.updated + result.archived);
9867
+ args.onProgress?.(result.verified + result.updated + result.archived + result.skipped + result.refused, result.refused);
9578
9868
  if (counts.providerFailure) {
9579
9869
  lastProviderFailure = counts.providerFailure;
9580
9870
  if (counts.providerFailure.fingerprint === priorProviderFailureFingerprint) {
@@ -9597,7 +9887,7 @@ async function runVerify(args) {
9597
9887
  result.complete = result.remaining === 0;
9598
9888
  if (result.complete)
9599
9889
  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}`);
9890
+ 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
9891
  return result;
9602
9892
  } finally {
9603
9893
  heartbeat.stop();
@@ -9666,7 +9956,14 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
9666
9956
  recordInvocation4(args, startedAt, { status: "failed", error });
9667
9957
  if (error instanceof DreamerModuleFailureError || signal.aborted)
9668
9958
  throw error;
9669
- return { verified: 0, updated: 0, archived: 0, providerFailure };
9959
+ return {
9960
+ verified: 0,
9961
+ updated: 0,
9962
+ archived: 0,
9963
+ skipped: 0,
9964
+ refused: 0,
9965
+ providerFailure
9966
+ };
9670
9967
  } finally {
9671
9968
  if (agentSessionId && !shouldKeepSubagents()) {
9672
9969
  await args.client.session.delete({
@@ -9680,35 +9977,59 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
9680
9977
  }
9681
9978
  async function applyParsedVerifyManifest(args, batch, parsed) {
9682
9979
  const batchIds = new Set(batch.map((m) => m.id));
9980
+ const batchById = new Map(batch.map((memory) => [memory.id, memory]));
9683
9981
  const valid = {
9684
9982
  verified: parsed.verified.filter((entry) => batchIds.has(entry.id)),
9685
9983
  updated: parsed.updated.filter((entry) => batchIds.has(entry.id)),
9686
- archived: parsed.archived.filter((entry) => batchIds.has(entry.id))
9984
+ archived: parsed.archived.filter((entry) => batchIds.has(entry.id)),
9985
+ skipped: parsed.skipped.filter((entry) => batchIds.has(entry.id))
9687
9986
  };
9688
- const unknown = [...parsed.verified, ...parsed.updated, ...parsed.archived].filter((entry) => !batchIds.has(entry.id));
9987
+ const unknown = [
9988
+ ...parsed.verified,
9989
+ ...parsed.updated,
9990
+ ...parsed.archived,
9991
+ ...parsed.skipped
9992
+ ].filter((entry) => !batchIds.has(entry.id));
9689
9993
  if (unknown.length > 0) {
9690
9994
  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
9995
  }
9692
- const validIds = [...valid.verified, ...valid.updated, ...valid.archived].map((entry) => entry.id);
9996
+ const validIds = [...valid.verified, ...valid.updated, ...valid.archived, ...valid.skipped].map((entry) => entry.id);
9693
9997
  assertNoDuplicateManifestIds(validIds, "verify");
9694
9998
  if (validIds.length * 2 < batch.length) {
9695
9999
  throw new Error(`verify manifest covers ${validIds.length}/${batch.length} batch ids after filtering unknown entries; rejecting mostly-wrong manifest`);
9696
10000
  }
9697
- if (validIds.length === 0)
9698
- return { verified: 0, updated: 0, archived: 0 };
10001
+ if (validIds.length === 0) {
10002
+ return { verified: 0, updated: 0, archived: 0, skipped: 0, refused: 0 };
10003
+ }
9699
10004
  const now = Date.now();
9700
10005
  const writes = [];
10006
+ let refused = 0;
10007
+ const skipped = valid.skipped.length;
9701
10008
  for (const v of valid.verified) {
9702
10009
  const files = await normalizeFiles(args, v.files);
9703
10010
  writes.push({ kind: "verify", id: v.id, files });
9704
10011
  }
9705
10012
  for (const u of valid.updated) {
10013
+ const original = batchById.get(u.id);
10014
+ if (!original)
10015
+ continue;
10016
+ if (isDirectiveShapedProjectRule(original.category, original.content)) {
10017
+ log(`[dreamer] verify safety refusal: memory_id=${u.id} verdict=update reason=directive-shaped-project-rule`);
10018
+ refused += 1;
10019
+ continue;
10020
+ }
9706
10021
  const content = u.content.trim();
9707
10022
  if (!content || content.length > 20000) {
9708
10023
  const files2 = await normalizeFiles(args, u.files);
9709
10024
  writes.push({ kind: "verify", id: u.id, files: files2 });
9710
10025
  continue;
9711
10026
  }
10027
+ const originalLength = original.content.trim().length;
10028
+ if (!u.consolidation && content.length * 2 < originalLength) {
10029
+ log(`[dreamer] verify safety refusal: memory_id=${u.id} verdict=update reason=content-loss original_chars=${originalLength} replacement_chars=${content.length}`);
10030
+ refused += 1;
10031
+ continue;
10032
+ }
9712
10033
  const files = await normalizeFiles(args, u.files);
9713
10034
  writes.push({
9714
10035
  kind: "update",
@@ -9719,13 +10040,21 @@ async function applyParsedVerifyManifest(args, batch, parsed) {
9719
10040
  });
9720
10041
  }
9721
10042
  for (const a of valid.archived) {
10043
+ const original = batchById.get(a.id);
10044
+ if (!original)
10045
+ continue;
10046
+ if (isDirectiveShapedProjectRule(original.category, original.content)) {
10047
+ log(`[dreamer] verify safety refusal: memory_id=${a.id} verdict=archive reason=directive-shaped-project-rule`);
10048
+ refused += 1;
10049
+ continue;
10050
+ }
9722
10051
  writes.push({ kind: "archive", id: a.id, reason: a.reason });
9723
10052
  }
9724
- if (writes.length === 0)
9725
- return { verified: 0, updated: 0, archived: 0 };
9726
10053
  let verified = 0;
9727
10054
  let updated = 0;
9728
10055
  let archived = 0;
10056
+ if (writes.length === 0)
10057
+ return { verified, updated, archived, skipped, refused };
9729
10058
  if (args.moduleRoute) {
9730
10059
  const identities = getModuleMemoryIdentities(args.db, args.projectIdentity, writes.map((write) => write.id));
9731
10060
  const rows = writes.map((write) => {
@@ -9775,7 +10104,7 @@ async function applyParsedVerifyManifest(args, batch, parsed) {
9775
10104
  else
9776
10105
  archived += 1;
9777
10106
  }
9778
- return { verified, updated, archived };
10107
+ return { verified, updated, archived, skipped, refused };
9779
10108
  }
9780
10109
  runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
9781
10110
  for (const w of writes) {
@@ -9806,7 +10135,7 @@ async function applyParsedVerifyManifest(args, batch, parsed) {
9806
10135
  }
9807
10136
  }
9808
10137
  });
9809
- return { verified, updated, archived };
10138
+ return { verified, updated, archived, skipped, refused };
9810
10139
  }
9811
10140
  async function normalizeFiles(args, rawFiles) {
9812
10141
  if (rawFiles.length === 0)
@@ -9919,12 +10248,13 @@ function createDreamTaskExecutor(deps) {
9919
10248
  })();
9920
10249
  const deadline = startedAt + config.timeoutMinutes * 60 * 1000;
9921
10250
  const backlogAtStart = getDreamTaskBacklog(db, projectIdentity, config.task);
9922
- const reportProgress = (processed) => {
10251
+ const reportProgress = (processed, refused) => {
9923
10252
  deps.onProgress?.({
9924
10253
  task: config.task,
9925
10254
  processed: Math.max(0, processed),
9926
10255
  total: backlogAtStart.pending,
9927
- startedAt
10256
+ startedAt,
10257
+ ...refused === undefined ? {} : { refused: Math.max(0, refused) }
9928
10258
  });
9929
10259
  };
9930
10260
  reportProgress(0);
@@ -10111,10 +10441,11 @@ function createDreamTaskExecutor(deps) {
10111
10441
  fallbackModels: config.fallbackModels,
10112
10442
  language: config.language ?? deps.language,
10113
10443
  moduleRoute,
10114
- onProgress: (processed2) => reportProgress(processed2)
10444
+ onProgress: (processed2, refused) => reportProgress(processed2, refused)
10115
10445
  });
10116
- const processed = result.verified + result.updated + result.archived;
10117
- const broadProgress = config.task === "verify-broad" ? `verify-broad cycle ${result.broadCycleStartAt ?? "open"}: verified ${processed}, ${result.remaining} remain` : null;
10446
+ const processed = result.verified + result.updated + result.archived + result.skipped + result.refused;
10447
+ 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`;
10448
+ const broadProgress = config.task === "verify-broad" ? verificationProgress : null;
10118
10449
  const backlogAfter = config.task === "verify-broad" ? { pending: result.remaining, total: backlogAtStart.total } : undefined;
10119
10450
  if (!result.complete) {
10120
10451
  if (broadProgress && processed > 0) {
@@ -10127,13 +10458,14 @@ function createDreamTaskExecutor(deps) {
10127
10458
  }
10128
10459
  const error = incompleteMessage(result.remaining);
10129
10460
  recordRun("failed", error, {
10461
+ progress: verificationProgress,
10130
10462
  memoryChanges: computeMemoryDelta(memoryBefore),
10131
10463
  backlogAfter
10132
10464
  });
10133
10465
  return { status: "failed", transient: true, error };
10134
10466
  }
10135
10467
  recordRun("completed", null, {
10136
- progress: broadProgress,
10468
+ progress: verificationProgress,
10137
10469
  memoryChanges: computeMemoryDelta(memoryBefore),
10138
10470
  backlogAfter
10139
10471
  });
@@ -10163,6 +10495,7 @@ function createDreamTaskExecutor(deps) {
10163
10495
  leaseAcquisition,
10164
10496
  model: config.model,
10165
10497
  fallbackModels: config.fallbackModels,
10498
+ language: config.language ?? deps.language,
10166
10499
  ...moduleArgs,
10167
10500
  onProgress: (processed) => reportProgress(processed)
10168
10501
  });
@@ -10910,7 +11243,7 @@ async function runDueTasksForProject(deps) {
10910
11243
  }
10911
11244
 
10912
11245
  // ../plugin/src/plugin/dream-timer.ts
10913
- import { statSync as statSync2 } from "node:fs";
11246
+ import { statSync as statSync3 } from "node:fs";
10914
11247
 
10915
11248
  // ../plugin/src/features/magic-context/dreamer/retrospective-orphan-sweep.ts
10916
11249
  var HISTORIAN_CHILD_TITLE = "magic-context-compartment";
@@ -11014,12 +11347,13 @@ var DREAM_TIMER_INTERVAL_MS = 15 * 60 * 1000;
11014
11347
  var GIT_COMMIT_BACKLOG_DRAIN_MAX_MS = 5 * 60 * 1000;
11015
11348
  var BOOT_PROJECT_JITTER_SLOT_MS = 1000;
11016
11349
  var activeTimer = null;
11350
+ var startupTickTimer = null;
11017
11351
  var startupTimers = new Map;
11018
11352
  var startupJitters = new Map;
11019
11353
  var nextStartupJitterSlot = 0;
11020
11354
  function directoryStillExists(directory) {
11021
11355
  try {
11022
- return statSync2(directory).isDirectory();
11356
+ return statSync3(directory).isDirectory();
11023
11357
  } catch {
11024
11358
  return false;
11025
11359
  }
@@ -11039,6 +11373,23 @@ function openTimerDatabaseOrNull(context) {
11039
11373
  return db;
11040
11374
  }
11041
11375
  var registeredProjects = new Map;
11376
+ function stopDreamScheduleTimerIfIdle() {
11377
+ if (registeredProjects.size !== 0)
11378
+ return;
11379
+ const stopped = activeTimer !== null || startupTickTimer !== null;
11380
+ if (activeTimer) {
11381
+ clearInterval(activeTimer);
11382
+ activeTimer = null;
11383
+ }
11384
+ if (startupTickTimer) {
11385
+ clearTimeout(startupTickTimer);
11386
+ startupTickTimer = null;
11387
+ }
11388
+ startupJitters.clear();
11389
+ nextStartupJitterSlot = 0;
11390
+ if (stopped)
11391
+ log("[dreamer] stopped dream schedule timer (no projects left)");
11392
+ }
11042
11393
  async function startDreamScheduleTimer(args) {
11043
11394
  beginBootQuietPeriod();
11044
11395
  const db = openTimerDatabaseOrNull("schedule timer registration");
@@ -11062,7 +11413,10 @@ async function startDreamScheduleTimer(args) {
11062
11413
  }
11063
11414
  if (!activeTimer) {
11064
11415
  log(`[dreamer] started independent schedule timer (every ${DREAM_TIMER_INTERVAL_MS / 60000}m)`);
11065
- scheduleAfterBootQuiet(() => runTick("startup"));
11416
+ startupTickTimer = scheduleAfterBootQuiet(() => {
11417
+ startupTickTimer = null;
11418
+ runTick("startup");
11419
+ });
11066
11420
  const timer = setInterval(() => runTick("interval"), DREAM_TIMER_INTERVAL_MS);
11067
11421
  if (typeof timer === "object" && "unref" in timer) {
11068
11422
  timer.unref();
@@ -11072,6 +11426,10 @@ async function startDreamScheduleTimer(args) {
11072
11426
  scheduleInitialProjectRun(args, db);
11073
11427
  }
11074
11428
  return () => {
11429
+ if (registeredProjects.get(args.directory) !== args) {
11430
+ stopDreamScheduleTimerIfIdle();
11431
+ return;
11432
+ }
11075
11433
  registeredProjects.delete(args.directory);
11076
11434
  const startupTimer = startupTimers.get(args.directory);
11077
11435
  if (startupTimer) {
@@ -11079,13 +11437,7 @@ async function startDreamScheduleTimer(args) {
11079
11437
  startupTimers.delete(args.directory);
11080
11438
  }
11081
11439
  log(`[dreamer] unregistered project ${args.projectIdentity} (remaining=${registeredProjects.size})`);
11082
- if (registeredProjects.size === 0 && activeTimer) {
11083
- clearInterval(activeTimer);
11084
- activeTimer = null;
11085
- startupJitters.clear();
11086
- nextStartupJitterSlot = 0;
11087
- log("[dreamer] stopped dream schedule timer (no projects left)");
11088
- }
11440
+ stopDreamScheduleTimerIfIdle();
11089
11441
  };
11090
11442
  }
11091
11443
  function runTick(origin) {
@@ -11170,7 +11522,15 @@ async function sweepProject(reg, origin, db, gitCommitEnabled) {
11170
11522
  log(`[dreamer] orphan schedule GC failed for ${reg.projectIdentity}:`, error);
11171
11523
  }
11172
11524
  }
11173
- registeredProjects.delete(reg.directory);
11525
+ if (registeredProjects.get(reg.directory) === reg) {
11526
+ registeredProjects.delete(reg.directory);
11527
+ const startupTimer = startupTimers.get(reg.directory);
11528
+ if (startupTimer) {
11529
+ clearTimeout(startupTimer);
11530
+ startupTimers.delete(reg.directory);
11531
+ }
11532
+ }
11533
+ stopDreamScheduleTimerIfIdle();
11174
11534
  return;
11175
11535
  }
11176
11536
  await reg.ensureRegistered(reg.directory, db);
@@ -11350,25 +11710,145 @@ async function sweepGitCommits(args) {
11350
11710
  }
11351
11711
 
11352
11712
  // src/dreamer/pi-session-api.ts
11353
- import { readFileSync as readFileSync4 } from "node:fs";
11354
- import { createRequire as createRequire2 } from "node:module";
11713
+ import { existsSync as existsSync8, readFileSync as readFileSync4, realpathSync as realpathSync3, statSync as statSync4 } from "node:fs";
11714
+ import {
11715
+ basename as basename2,
11716
+ dirname as dirname2,
11717
+ extname,
11718
+ join as join4,
11719
+ relative,
11720
+ resolve,
11721
+ sep as sep2
11722
+ } from "node:path";
11355
11723
  import { pathToFileURL } from "node:url";
11356
- var PI_CODING_AGENT_MODULE = "@earendil-works/pi-coding-agent";
11724
+ var PI_CODING_AGENT_MODULE2 = "@earendil-works/pi-coding-agent";
11725
+ var SCRIPT_ENTRY_PATTERN = /\.(mjs|cjs|mts|cts|js|ts|tsx|jsx)$/i;
11726
+ var TS_ENTRY_PATTERN = /\.(mts|cts|ts|tsx)$/i;
11727
+ function isScriptEntry(filePath) {
11728
+ return SCRIPT_ENTRY_PATTERN.test(filePath) || extname(filePath) === "";
11729
+ }
11730
+ function readManifest(pkgJsonPath) {
11731
+ try {
11732
+ const pkg = JSON.parse(readFileSync4(pkgJsonPath, "utf8"));
11733
+ return pkg && typeof pkg === "object" ? pkg : null;
11734
+ } catch {
11735
+ return null;
11736
+ }
11737
+ }
11738
+ function isCompiledBunBinary() {
11739
+ if (!process.versions.bun) {
11740
+ return false;
11741
+ }
11742
+ const exe = basename2(process.execPath).toLowerCase();
11743
+ return exe !== "bun" && exe !== "bun.exe";
11744
+ }
11745
+ function resolveRunningEntry() {
11746
+ const argv1 = process.argv[1];
11747
+ if (argv1 && existsSync8(argv1) && statSync4(argv1).isFile()) {
11748
+ const real = realpathSync3(argv1);
11749
+ if (isScriptEntry(real)) {
11750
+ return real;
11751
+ }
11752
+ }
11753
+ return null;
11754
+ }
11755
+ function findPackageRoot(startDir) {
11756
+ let dir = startDir;
11757
+ while (dir !== dirname2(dir)) {
11758
+ const pkg = readManifest(join4(dir, "package.json"));
11759
+ if (pkg?.name === PI_CODING_AGENT_MODULE2) {
11760
+ if (basename2(dir) === "dist") {
11761
+ const parentDir = dirname2(dir);
11762
+ const parentPkg = readManifest(join4(parentDir, "package.json"));
11763
+ if (parentPkg?.name === PI_CODING_AGENT_MODULE2) {
11764
+ return { dir: parentDir, pkg: parentPkg };
11765
+ }
11766
+ }
11767
+ return { dir, pkg };
11768
+ }
11769
+ dir = dirname2(dir);
11770
+ }
11771
+ return null;
11772
+ }
11773
+ function resolveExportsTarget(target) {
11774
+ if (typeof target === "string") {
11775
+ return target;
11776
+ }
11777
+ if (Array.isArray(target)) {
11778
+ for (const item of target) {
11779
+ const resolved = resolveExportsTarget(item);
11780
+ if (resolved !== undefined) {
11781
+ return resolved;
11782
+ }
11783
+ }
11784
+ return;
11785
+ }
11786
+ if (target && typeof target === "object") {
11787
+ const conditions = target;
11788
+ for (const condition of ["import", "node", "default"]) {
11789
+ if (condition in conditions) {
11790
+ const resolved = resolveExportsTarget(conditions[condition]);
11791
+ if (resolved !== undefined) {
11792
+ return resolved;
11793
+ }
11794
+ }
11795
+ }
11796
+ }
11797
+ return;
11798
+ }
11799
+ function resolveManifestEntry(found) {
11800
+ const pkg = found.pkg;
11801
+ const entryRel = resolveExportsTarget(pkg.exports?.["."]) ?? (typeof pkg.module === "string" ? pkg.module : undefined) ?? (typeof pkg.main === "string" ? pkg.main : undefined);
11802
+ if (!entryRel) {
11803
+ throw new Error(`No ESM entry found in ${found.dir}/package.json`);
11804
+ }
11805
+ if (!entryRel.startsWith("./")) {
11806
+ throw new Error(`Invalid entry "${entryRel}" in ${found.dir}/package.json: targets must start with "./"`);
11807
+ }
11808
+ const root = resolve(found.dir);
11809
+ const resolved = resolve(root, entryRel);
11810
+ if (resolved !== root && !resolved.startsWith(root + sep2)) {
11811
+ throw new Error(`Invalid entry "${entryRel}" in ${found.dir}/package.json: target escapes the package root`);
11812
+ }
11813
+ return resolved;
11814
+ }
11815
+ function toSourceEntry(entryPath, pkgDir) {
11816
+ const rel = relative(pkgDir, entryPath);
11817
+ const srcRel = rel.replace(/^dist[/\\]/, "src/").replace(/\.mjs$/, ".mts").replace(/\.cjs$/, ".cts").replace(/\.jsx$/, ".tsx").replace(/\.js$/, ".ts");
11818
+ if (srcRel === rel) {
11819
+ return null;
11820
+ }
11821
+ return join4(pkgDir, srcRel);
11822
+ }
11357
11823
  var defaultLoaders = [
11358
- {
11359
- name: "Bare import",
11360
- load: async () => await import(PI_CODING_AGENT_MODULE)
11361
- },
11362
11824
  {
11363
11825
  name: "Resolve from running Pi binary entry",
11364
11826
  load: async () => {
11365
- if (!process.argv[1]) {
11366
- throw new Error("process.argv[1] is undefined");
11827
+ if (isCompiledBunBinary()) {
11828
+ 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");
11829
+ }
11830
+ const entry = resolveRunningEntry() ?? process.execPath;
11831
+ if (!entry) {
11832
+ throw new Error("Neither process.argv[1] nor process.execPath is available");
11833
+ }
11834
+ const found = findPackageRoot(dirname2(entry));
11835
+ if (!found) {
11836
+ throw new Error(`Could not locate ${PI_CODING_AGENT_MODULE2} package.json from ${entry}`);
11837
+ }
11838
+ const entryPath = resolveManifestEntry(found);
11839
+ if (TS_ENTRY_PATTERN.test(entry)) {
11840
+ const srcEntry = toSourceEntry(entryPath, found.dir);
11841
+ if (srcEntry && existsSync8(srcEntry)) {
11842
+ return await import(pathToFileURL(srcEntry).href);
11843
+ }
11844
+ 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
11845
  }
11368
- const require2 = createRequire2(process.argv[1]);
11369
- const resolved = require2.resolve(PI_CODING_AGENT_MODULE);
11370
- return await import(pathToFileURL(resolved).href);
11846
+ return await import(pathToFileURL(entryPath).href);
11371
11847
  }
11848
+ },
11849
+ {
11850
+ name: "Bare import",
11851
+ load: async () => await import(PI_CODING_AGENT_MODULE2)
11372
11852
  }
11373
11853
  ];
11374
11854
  var cachedModulePromise = null;
@@ -11386,7 +11866,7 @@ async function resolvePiCodingAgentModule(loaders) {
11386
11866
  errors.push(err);
11387
11867
  }
11388
11868
  }
11389
- throw new Error(`Failed to resolve ${PI_CODING_AGENT_MODULE} via all strategies:
11869
+ throw new Error(`Failed to resolve ${PI_CODING_AGENT_MODULE2} via all strategies:
11390
11870
  ` + errors.map((e, i) => ` - ${activeLoaders[i].name}: ${e.message || e}`).join(`
11391
11871
  `) + `
11392
11872
  Likely cause: symlinked or nonstandard install layout.`);
@@ -11454,7 +11934,7 @@ function createPiPrimerRawProviderFactory(deps = {}) {
11454
11934
  }
11455
11935
 
11456
11936
  // src/dreamer/retrospective-raw-provider-pi.ts
11457
- import { resolve } from "node:path";
11937
+ import { resolve as resolve2 } from "node:path";
11458
11938
  class PiRetrospectiveRawProvider {
11459
11939
  deps;
11460
11940
  sessionPathById = new Map;
@@ -11465,7 +11945,7 @@ class PiRetrospectiveRawProvider {
11465
11945
  async listProjectSessions(_projectIdentity) {
11466
11946
  const deps = await this.resolveDeps();
11467
11947
  const sessions = await deps.listSessions(this.deps.sessionDir);
11468
- const projectCwd = resolve(this.deps.projectCwd);
11948
+ const projectCwd = resolve2(this.deps.projectCwd);
11469
11949
  const result = [];
11470
11950
  this.sessionPathById.clear();
11471
11951
  for (const raw of sessions) {
@@ -11474,7 +11954,7 @@ class PiRetrospectiveRawProvider {
11474
11954
  continue;
11475
11955
  if (typeof info.id !== "string" || typeof info.path !== "string")
11476
11956
  continue;
11477
- if (typeof info.cwd !== "string" || resolve(info.cwd) !== projectCwd)
11957
+ if (typeof info.cwd !== "string" || resolve2(info.cwd) !== projectCwd)
11478
11958
  continue;
11479
11959
  this.sessionPathById.set(info.id, info.path);
11480
11960
  result.push({
@@ -11568,9 +12048,30 @@ function extractPiTextContent(content) {
11568
12048
  }
11569
12049
 
11570
12050
  // src/dreamer/index.ts
11571
- var registeredProjects2 = new Map;
12051
+ var PI_DREAMER_PROJECTS = Symbol.for("magic-context.pi.dreamer-registered-projects");
12052
+ function getRegisteredProjects() {
12053
+ const globals = globalThis;
12054
+ const existing = globals[PI_DREAMER_PROJECTS];
12055
+ if (existing instanceof Map) {
12056
+ return existing;
12057
+ }
12058
+ const projects = new Map;
12059
+ globals[PI_DREAMER_PROJECTS] = projects;
12060
+ return projects;
12061
+ }
12062
+ var registeredProjects2 = getRegisteredProjects();
11572
12063
  var sessionsById = new Map;
11573
- var inFlightDreams = new Set;
12064
+ var PI_DREAMER_IN_FLIGHT = Symbol.for("magic-context.pi.dreamer-in-flight");
12065
+ var inFlightDreams = (() => {
12066
+ const globals = globalThis;
12067
+ const existing = globals[PI_DREAMER_IN_FLIGHT];
12068
+ if (existing instanceof Map) {
12069
+ return existing;
12070
+ }
12071
+ const dreams = new Map;
12072
+ globals[PI_DREAMER_IN_FLIGHT] = dreams;
12073
+ return dreams;
12074
+ })();
11574
12075
  var sessionCounter = 0;
11575
12076
  var piSubagentRunnerFactory = () => new PiSubagentRunner;
11576
12077
  var startDreamScheduleTimerFn = startDreamScheduleTimer;
@@ -11579,14 +12080,26 @@ function registerPiDreamerProject(opts) {
11579
12080
  return;
11580
12081
  }
11581
12082
  const existing = registeredProjects2.get(opts.projectIdentity);
12083
+ const owners = existing?.owners ?? new Map;
12084
+ owners.delete(opts.registrationOwner);
12085
+ owners.set(opts.registrationOwner, opts);
12086
+ const notifyOwnersOfAdjunctRefresh = (projectIdentity) => {
12087
+ const callbacks = new Set([...owners.values()].map((owner) => owner.onAdjunctsRefreshNeeded).filter((callback) => callback !== undefined));
12088
+ for (const callback of callbacks)
12089
+ callback(projectIdentity);
12090
+ };
11582
12091
  if (existing) {
11583
- if (existing.projectDir === opts.projectDir) {
12092
+ if (existing.generation && existing.projectDir === opts.projectDir) {
11584
12093
  return;
11585
12094
  }
11586
12095
  existing.cleanup();
11587
12096
  registeredProjects2.delete(opts.projectIdentity);
11588
12097
  }
11589
- const client = createPiDreamerClient(opts);
12098
+ const generation = {};
12099
+ const client = createPiDreamerClient(opts, notifyOwnersOfAdjunctRefresh, () => {
12100
+ const current = registeredProjects2.get(opts.projectIdentity);
12101
+ return current?.generation === generation && current.owners.get(opts.registrationOwner)?.projectDir === opts.projectDir;
12102
+ });
11590
12103
  let cleanup;
11591
12104
  let cancelled = false;
11592
12105
  startDreamScheduleTimerFn({
@@ -11597,6 +12110,8 @@ function registerPiDreamerProject(opts) {
11597
12110
  dreamerConfig: opts.config,
11598
12111
  language: opts.language,
11599
12112
  gitCommitIndexing: opts.gitCommitIndexing,
12113
+ memoryEnabled: opts.memoryEnabled,
12114
+ embeddingConfig: opts.embeddingConfig,
11600
12115
  retinaHandoff: opts.retinaHandoff,
11601
12116
  mural: opts.mural,
11602
12117
  ensureRegistered: ensureProjectRegisteredFromPiDirectory,
@@ -11604,32 +12119,50 @@ function registerPiDreamerProject(opts) {
11604
12119
  primerRawProviderFactory: createPiPrimerRawProviderFactory()
11605
12120
  }).then((timerCleanup) => {
11606
12121
  if (cancelled) {
11607
- timerCleanup?.();
12122
+ if (registeredProjects2.get(opts.projectIdentity)?.generation !== generation) {
12123
+ timerCleanup?.();
12124
+ }
11608
12125
  return;
11609
12126
  }
11610
12127
  cleanup = timerCleanup;
11611
12128
  });
11612
- const runManual = async (task) => runManualDream({
11613
- db: opts.db,
11614
- projectIdentity: opts.projectIdentity,
11615
- tasks: buildDreamTaskRuntimeConfigs(opts.config, "pi", opts.language, opts.mural?.model),
11616
- executor: createDreamTaskExecutor({
11617
- client,
11618
- sessionDirectory: opts.projectDir,
11619
- openOpenCodeDb,
11620
- retrospectiveRawProvider: new PiRetrospectiveRawProvider({
11621
- projectCwd: opts.projectDir
12129
+ const runManual = async (task, registrationOwner) => {
12130
+ const manualOpts = owners.get(registrationOwner);
12131
+ if (!manualOpts) {
12132
+ throw new Error(`Pi dreamer registration owner is no longer active for project ${opts.projectIdentity}`);
12133
+ }
12134
+ const manualClient = createPiDreamerClient(manualOpts, notifyOwnersOfAdjunctRefresh, () => owners.get(manualOpts.registrationOwner)?.projectDir === manualOpts.projectDir);
12135
+ const manualRun = runManualDream({
12136
+ db: manualOpts.db,
12137
+ projectIdentity: manualOpts.projectIdentity,
12138
+ tasks: buildDreamTaskRuntimeConfigs(manualOpts.config, "pi", manualOpts.language, manualOpts.mural?.model),
12139
+ executor: createDreamTaskExecutor({
12140
+ client: manualClient,
12141
+ sessionDirectory: manualOpts.projectDir,
12142
+ openOpenCodeDb,
12143
+ retrospectiveRawProvider: new PiRetrospectiveRawProvider({
12144
+ projectCwd: manualOpts.projectDir
12145
+ }),
12146
+ primerRawProviderFactory: createPiPrimerRawProviderFactory(),
12147
+ userMemoryCollectionEnabled: userMemoryCollectionEnabled(manualOpts.config),
12148
+ ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
12149
+ language: manualOpts.language,
12150
+ retinaHandoff: manualOpts.retinaHandoff,
12151
+ mural: manualOpts.mural
11622
12152
  }),
11623
- primerRawProviderFactory: createPiPrimerRawProviderFactory(),
11624
- userMemoryCollectionEnabled: userMemoryCollectionEnabled(opts.config),
11625
- ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
11626
- language: opts.language,
11627
- retinaHandoff: opts.retinaHandoff,
11628
- mural: opts.mural
11629
- }),
11630
- task
11631
- });
12153
+ task
12154
+ });
12155
+ inFlightDreams.set(manualRun, manualOpts.registrationOwner);
12156
+ try {
12157
+ return await manualRun;
12158
+ } finally {
12159
+ inFlightDreams.delete(manualRun);
12160
+ }
12161
+ };
11632
12162
  registeredProjects2.set(opts.projectIdentity, {
12163
+ generation,
12164
+ activeOwner: opts.registrationOwner,
12165
+ owners,
11633
12166
  cleanup: () => {
11634
12167
  cancelled = true;
11635
12168
  cleanup?.();
@@ -11638,31 +12171,55 @@ function registerPiDreamerProject(opts) {
11638
12171
  projectDir: opts.projectDir
11639
12172
  });
11640
12173
  }
11641
- async function runPiDreamForProject(projectIdentity, task) {
12174
+ async function runPiDreamForProject(projectIdentity, task, registrationOwner) {
11642
12175
  const registration = registeredProjects2.get(projectIdentity);
11643
12176
  if (!registration) {
11644
12177
  throw new Error(`Pi dreamer not registered for project ${projectIdentity}; call registerPiDreamerProject() first`);
11645
12178
  }
11646
- return registration.runManual(task);
12179
+ return registration.runManual(task, registrationOwner);
11647
12180
  }
11648
12181
  function unregisterPiDreamerProject(opts) {
11649
12182
  const registration = registeredProjects2.get(opts.projectIdentity);
11650
- if (!registration) {
12183
+ if (!registration?.owners.delete(opts.registrationOwner)) {
12184
+ return;
12185
+ }
12186
+ if (registration.owners.size > 0) {
12187
+ if (registration.activeOwner !== opts.registrationOwner)
12188
+ return;
12189
+ const remaining = [...registration.owners.values()];
12190
+ const replacementOptions = remaining[remaining.length - 1];
12191
+ if (!replacementOptions)
12192
+ return;
12193
+ registration.cleanup();
12194
+ registeredProjects2.delete(opts.projectIdentity);
12195
+ registerPiDreamerProject(replacementOptions);
12196
+ const replacement = registeredProjects2.get(opts.projectIdentity);
12197
+ if (replacement) {
12198
+ for (const remainingOptions of remaining) {
12199
+ replacement.owners.set(remainingOptions.registrationOwner, remainingOptions);
12200
+ }
12201
+ }
11651
12202
  return;
11652
12203
  }
11653
12204
  registration.cleanup();
11654
12205
  registeredProjects2.delete(opts.projectIdentity);
11655
12206
  }
11656
- async function awaitInFlightDreamers() {
11657
- if (inFlightDreams.size === 0) {
12207
+ async function awaitInFlightDreamers(registrationOwner) {
12208
+ const runs = registrationOwner === undefined ? [...inFlightDreams.keys()] : [...inFlightDreams.entries()].filter(([, owner]) => owner === registrationOwner).map(([run]) => run);
12209
+ if (runs.length === 0)
11658
12210
  return;
11659
- }
11660
- await Promise.allSettled(Array.from(inFlightDreams));
12211
+ await Promise.allSettled(runs);
11661
12212
  }
11662
- function createPiDreamerClient(opts) {
12213
+ function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRefreshNeeded, isRegistrationOwnerActive = () => true) {
11663
12214
  const runner = piSubagentRunnerFactory();
12215
+ const assertRegistrationOwnerActive = () => {
12216
+ if (!isRegistrationOwnerActive()) {
12217
+ throw new Error(`Pi dreamer registration is no longer active for project ${opts.projectIdentity}`);
12218
+ }
12219
+ };
11664
12220
  const session = {
11665
12221
  create: async (args) => {
12222
+ assertRegistrationOwnerActive();
11666
12223
  const sessionId = `magic-context-pi-dream-${++sessionCounter}`;
11667
12224
  sessionsById.set(sessionId, {
11668
12225
  id: sessionId,
@@ -11679,6 +12236,7 @@ function createPiDreamerClient(opts) {
11679
12236
  if (!dreamSession) {
11680
12237
  throw new Error(`Pi dreamer session not found: ${sessionId}`);
11681
12238
  }
12239
+ assertRegistrationOwnerActive();
11682
12240
  const userMessage = extractUserMessage(args);
11683
12241
  const systemPrompt = extractSystemPrompt(args);
11684
12242
  const perTaskModel = extractBodyModel(args);
@@ -11694,9 +12252,10 @@ function createPiDreamerClient(opts) {
11694
12252
  signal: args.signal ?? undefined,
11695
12253
  thinkingLevel: extractBodyVariant(args)
11696
12254
  });
11697
- inFlightDreams.add(runPromise);
12255
+ inFlightDreams.set(runPromise, opts.registrationOwner);
11698
12256
  try {
11699
12257
  const result = await runPromise;
12258
+ assertRegistrationOwnerActive();
11700
12259
  if (!result.ok) {
11701
12260
  const error = new Error(`Pi dreamer subagent failed (${result.reason}): ${result.error}`);
11702
12261
  if (result.transient) {
@@ -11711,12 +12270,13 @@ function createPiDreamerClient(opts) {
11711
12270
  { type: "text", text: result.assistantText }
11712
12271
  ])
11713
12272
  ];
11714
- opts.onAdjunctsRefreshNeeded?.(opts.projectIdentity);
12273
+ onAdjunctsRefreshNeeded?.(opts.projectIdentity);
11715
12274
  } finally {
11716
12275
  inFlightDreams.delete(runPromise);
11717
12276
  }
11718
12277
  },
11719
12278
  messages: async (args) => {
12279
+ assertRegistrationOwnerActive();
11720
12280
  const dreamSession = sessionsById.get(args.path.id);
11721
12281
  return { data: dreamSession?.messages ?? [] };
11722
12282
  },
@@ -11816,8 +12376,15 @@ function makeMessage(role, parts) {
11816
12376
  }
11817
12377
 
11818
12378
  // src/commands/pi-command-utils.ts
11819
- import { Box, Text } from "@earendil-works/pi-tui";
12379
+ import { Box, matchesKey, Text } from "@earendil-works/pi-tui";
11820
12380
  var CTX_STATUS_CUSTOM_TYPE = "ctx-status";
12381
+ var statusLifecycleSignals = new WeakMap;
12382
+ function registerCtxStatusLifecycleSignal(pi, signal) {
12383
+ statusLifecycleSignals.set(pi, signal);
12384
+ }
12385
+ function shouldShowCtxStatusDialog(content) {
12386
+ return content.rpcDisplay === "dialog" || content.rpcDisplay !== "notification" && content.level !== undefined && content.level !== "info";
12387
+ }
11821
12388
  function resolveSessionId(ctx) {
11822
12389
  const sm = ctx.sessionManager;
11823
12390
  const getSessionId = sm.getSessionId;
@@ -11830,6 +12397,41 @@ function resolveSessionId(ctx) {
11830
12397
  return;
11831
12398
  }
11832
12399
  }
12400
+ async function showCtxStatusDialog(ctx, content) {
12401
+ let factoryInvoked = false;
12402
+ await ctx.ui.custom((_tui, theme, _keybindings, done) => {
12403
+ factoryInvoked = true;
12404
+ return new CtxStatusDialog(content, theme, done);
12405
+ }, { overlay: true, overlayOptions: { anchor: "center", width: 92 } });
12406
+ return factoryInvoked;
12407
+ }
12408
+
12409
+ class CtxStatusDialog {
12410
+ content;
12411
+ theme;
12412
+ done;
12413
+ constructor(content, theme, done) {
12414
+ this.content = content;
12415
+ this.theme = theme;
12416
+ this.done = done;
12417
+ }
12418
+ handleInput(data) {
12419
+ if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c") || matchesKey(data, "return")) {
12420
+ this.done(undefined);
12421
+ }
12422
+ }
12423
+ invalidate() {}
12424
+ render(_width) {
12425
+ return [
12426
+ this.theme.bold(this.theme.fg(statusTitleColor(this.content.level), `[${this.content.title}]`)),
12427
+ "",
12428
+ ...this.content.text.split(`
12429
+ `),
12430
+ "",
12431
+ this.theme.fg("dim", "Press Enter or Escape to close")
12432
+ ];
12433
+ }
12434
+ }
11833
12435
  function statusTitleColor(level) {
11834
12436
  switch (level) {
11835
12437
  case "success":
@@ -11864,7 +12466,37 @@ function registerCtxStatusEntryRenderer(pi) {
11864
12466
  return false;
11865
12467
  }
11866
12468
  }
11867
- function sendCtxStatusMessage(pi, content, details) {
12469
+ async function presentCtxStatusMessage(ctx, content, signal) {
12470
+ if (ctx.mode !== "rpc" || signal?.aborted)
12471
+ return;
12472
+ const type = content.level === "error" || content.level === "warning" ? content.level : "info";
12473
+ if (!shouldShowCtxStatusDialog(content)) {
12474
+ if (!signal?.aborted)
12475
+ ctx.ui.notify(content.text, type);
12476
+ return;
12477
+ }
12478
+ try {
12479
+ const shown = await showCtxStatusDialog(ctx, content);
12480
+ if (!shown && !signal?.aborted)
12481
+ ctx.ui.notify(content.text, type);
12482
+ } catch (err) {
12483
+ if (signal?.aborted)
12484
+ return;
12485
+ sessionLog("pi-status", `ctx status dialog failed: ${err instanceof Error ? err.message : String(err)}`);
12486
+ ctx.ui.notify(content.text, type);
12487
+ }
12488
+ }
12489
+ function createCtxStatusSender(pi, ctx, signal) {
12490
+ const lifecycleSignal = statusLifecycleSignals.get(pi);
12491
+ const effectiveSignal = signal && lifecycleSignal ? AbortSignal.any([signal, lifecycleSignal]) : signal ?? lifecycleSignal;
12492
+ return (content, details) => {
12493
+ if (!effectiveSignal?.aborted)
12494
+ sendCtxStatusMessage(pi, content, details, ctx, effectiveSignal);
12495
+ };
12496
+ }
12497
+ function sendCtxStatusMessage(pi, content, details, ctx, signal) {
12498
+ if (signal?.aborted)
12499
+ return;
11868
12500
  const data = {
11869
12501
  ...content,
11870
12502
  details: details ?? content.details
@@ -11872,6 +12504,8 @@ function sendCtxStatusMessage(pi, content, details) {
11872
12504
  if (typeof pi.appendEntry === "function") {
11873
12505
  pi.appendEntry(CTX_STATUS_CUSTOM_TYPE, data);
11874
12506
  }
12507
+ if (ctx)
12508
+ presentCtxStatusMessage(ctx, data, signal);
11875
12509
  sessionLog("pi-status", `${content.title}: ${content.text}`);
11876
12510
  }
11877
12511
 
@@ -11880,6 +12514,7 @@ function registerCtxDreamCommand(pi, deps) {
11880
12514
  pi.registerCommand("ctx-dream", {
11881
12515
  description: "Run Magic Context dreamer tasks for this project now",
11882
12516
  handler: async (args, ctx) => {
12517
+ const sendStatus = createCtxStatusSender(pi, ctx);
11883
12518
  const project = deps.resolveProject?.(ctx) ?? {
11884
12519
  projectDir: deps.projectDir,
11885
12520
  projectIdentity: deps.projectIdentity
@@ -11890,7 +12525,7 @@ function registerCtxDreamCommand(pi, deps) {
11890
12525
  let task;
11891
12526
  if (requested) {
11892
12527
  if (!isCanonicalDreamTask(requested)) {
11893
- sendCtxStatusMessage(pi, {
12528
+ sendStatus({
11894
12529
  title: "/ctx-dream",
11895
12530
  text: `## /ctx-dream
11896
12531
 
@@ -11905,7 +12540,7 @@ Unknown task "${requested}".`,
11905
12540
  task = requested;
11906
12541
  }
11907
12542
  if (dreamerEnabled === false) {
11908
- sendCtxStatusMessage(pi, {
12543
+ sendStatus({
11909
12544
  title: "/ctx-dream",
11910
12545
  text: "## /ctx-dream\n\nDreamer is disabled for this project (`dreamer.disable=true`).",
11911
12546
  level: "info"
@@ -11917,7 +12552,7 @@ Unknown task "${requested}".`,
11917
12552
  }
11918
12553
  const backlogTasks = task ? [task] : CANONICAL_DREAM_TASKS;
11919
12554
  const backlogBefore = getDreamTaskBacklogs(deps.db, project.projectIdentity, backlogTasks);
11920
- sendCtxStatusMessage(pi, {
12555
+ sendStatus({
11921
12556
  title: "/ctx-dream",
11922
12557
  text: [
11923
12558
  "## /ctx-dream",
@@ -11935,7 +12570,8 @@ Unknown task "${requested}".`,
11935
12570
  projectIdentity: project.projectIdentity
11936
12571
  });
11937
12572
  try {
11938
- const result = await runPiDreamForProject(project.projectIdentity, task);
12573
+ await deps.ensureRegistered?.(ctx);
12574
+ const result = await runPiDreamForProject(project.projectIdentity, task, deps.registrationOwner);
11939
12575
  const lines = [];
11940
12576
  if (result.ran.length > 0)
11941
12577
  lines.push(`Ran: ${result.ran.join(", ")}`);
@@ -11953,11 +12589,12 @@ Unknown task "${requested}".`,
11953
12589
  }
11954
12590
  if (lines.length === 0)
11955
12591
  lines.push("No enabled dream tasks to run.");
11956
- sendCtxStatusMessage(pi, {
12592
+ sendStatus({
11957
12593
  title: "/ctx-dream",
11958
12594
  text: ["## /ctx-dream", "", ...lines].join(`
11959
12595
  `),
11960
- level: result.ran.length > 0 ? "success" : "info"
12596
+ level: result.ran.length > 0 ? "success" : "info",
12597
+ rpcDisplay: "dialog"
11961
12598
  }, {
11962
12599
  projectDir: project.projectDir,
11963
12600
  projectIdentity: project.projectIdentity
@@ -11965,7 +12602,7 @@ Unknown task "${requested}".`,
11965
12602
  } catch (error) {
11966
12603
  const message = error instanceof Error ? error.message : String(error);
11967
12604
  sessionLog(project.projectIdentity, `/ctx-dream failed: ${message}`);
11968
- sendCtxStatusMessage(pi, {
12605
+ sendStatus({
11969
12606
  title: "/ctx-dream",
11970
12607
  text: [
11971
12608
  "## /ctx-dream",
@@ -12005,6 +12642,10 @@ function formatEmbedFailureSummary(embedded, remaining, failure) {
12005
12642
  }
12006
12643
  case "invalid_envelope":
12007
12644
  return `Embedded ${embedded} ${compartment}; ${remaining} failed: ${failure.reason}. Fix: configure an OpenAI-compatible embedding endpoint that returns data[].embedding.`;
12645
+ case "certification_refusal":
12646
+ return `Embedded ${embedded} ${compartment}; ${remaining} rejected: ${failure.reason}. Fix: recertify SYNAPSE or configure embedding.fallback_provider, then run /ctx-embed start again.`;
12647
+ case "credential_required":
12648
+ return `Embedded ${embedded} ${compartment}; ${remaining} rejected: ${failure.reason}. Fix: reauthenticate SYNAPSE, then run /ctx-embed start again.`;
12008
12649
  case "empty_result":
12009
12650
  case "transport_error":
12010
12651
  return `Embedded ${embedded} ${compartment}; ${remaining} failed: ${failure.reason}.${failure.retryable ? retry : ""}`;
@@ -12176,9 +12817,10 @@ function registerCtxEmbedCommand(pi, deps) {
12176
12817
  pi.registerCommand("ctx-embed", {
12177
12818
  description: "Embedding status, or start/pause history compartment embedding (start | pause)",
12178
12819
  handler: async (args, ctx) => {
12820
+ const sendStatus = createCtxStatusSender(pi, ctx);
12179
12821
  const sessionId = resolveSessionId(ctx);
12180
12822
  if (!sessionId) {
12181
- sendCtxStatusMessage(pi, {
12823
+ sendStatus({
12182
12824
  title: "/ctx-embed",
12183
12825
  text: `## /ctx-embed
12184
12826
 
@@ -12199,7 +12841,7 @@ No active Pi session is available.`,
12199
12841
  if (ctrl)
12200
12842
  ctrl.abort();
12201
12843
  const cov = getEmbeddingCoverageStatus(deps.db, project.projectIdentity, sessionId);
12202
- sendCtxStatusMessage(pi, {
12844
+ sendStatus({
12203
12845
  title: "/ctx-embed",
12204
12846
  text: `## /ctx-embed
12205
12847
 
@@ -12209,7 +12851,7 @@ Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`,
12209
12851
  return;
12210
12852
  }
12211
12853
  if (memoryEnabled === false) {
12212
- sendCtxStatusMessage(pi, {
12854
+ sendStatus({
12213
12855
  title: "/ctx-embed",
12214
12856
  text: `## /ctx-embed
12215
12857
 
@@ -12221,16 +12863,16 @@ Memory is disabled for this project, so there is no semantic embedding to backfi
12221
12863
  await ensureProjectRegisteredFromPiDirectory(project.projectDir, deps.db);
12222
12864
  if (sub === "start") {
12223
12865
  const { text, level } = await runEmbedDrain(deps.db, project.projectIdentity, sessionId, {
12224
- onStatus: (status) => sendCtxStatusMessage(pi, {
12866
+ onStatus: (status) => sendStatus({
12225
12867
  title: "/ctx-embed",
12226
12868
  ...status
12227
12869
  })
12228
12870
  });
12229
- sendCtxStatusMessage(pi, { title: "/ctx-embed", text, level });
12871
+ sendStatus({ title: "/ctx-embed", text, level });
12230
12872
  return;
12231
12873
  }
12232
12874
  if (sub !== "") {
12233
- sendCtxStatusMessage(pi, {
12875
+ sendStatus({
12234
12876
  title: "/ctx-embed",
12235
12877
  text: "## /ctx-embed\n\nUsage: `/ctx-embed` (status), `/ctx-embed start`, or `/ctx-embed pause`.",
12236
12878
  level: "info"
@@ -12239,12 +12881,13 @@ Memory is disabled for this project, so there is no semantic embedding to backfi
12239
12881
  }
12240
12882
  const coverage = getEmbeddingCoverageStatus(deps.db, project.projectIdentity, sessionId);
12241
12883
  const statusText = formatEmbedStatusText(coverage, { status: "idle" });
12242
- sendCtxStatusMessage(pi, {
12884
+ sendStatus({
12243
12885
  title: "/ctx-embed",
12244
12886
  text: `## Embedding Status
12245
12887
 
12246
12888
  ${statusText}`,
12247
- level: "info"
12889
+ level: "info",
12890
+ rpcDisplay: "dialog"
12248
12891
  });
12249
12892
  }
12250
12893
  });
@@ -12260,7 +12903,7 @@ function maybeAutoEmbedPiSession(deps, sessionId, projectDir, projectIdentity, n
12260
12903
  (async () => {
12261
12904
  let completedDrainWithWork = false;
12262
12905
  try {
12263
- await new Promise((resolve2) => setTimeout(resolve2, 0));
12906
+ await new Promise((resolve3) => setTimeout(resolve3, 0));
12264
12907
  await ensureProjectRegisteredFromPiDirectory(projectDir, deps.db);
12265
12908
  const coverage = getEmbeddingCoverageStatus(deps.db, projectIdentity, sessionId);
12266
12909
  if (!coverage.enabled)
@@ -13151,15 +13794,31 @@ function detectMidTurnBypassReason(input) {
13151
13794
  }
13152
13795
  function applyMidTurnDeferral(input) {
13153
13796
  if (input.base === "defer") {
13154
- return { midTurnAdjustedSchedulerDecision: "defer", sideEffect: "none" };
13797
+ return {
13798
+ midTurnAdjustedSchedulerDecision: "defer",
13799
+ sideEffect: "none",
13800
+ deferReason: "scheduler_defer"
13801
+ };
13155
13802
  }
13156
13803
  if (input.bypassReason !== "none") {
13157
- return { midTurnAdjustedSchedulerDecision: "execute", sideEffect: "none" };
13804
+ return {
13805
+ midTurnAdjustedSchedulerDecision: "execute",
13806
+ sideEffect: "none",
13807
+ deferReason: null
13808
+ };
13158
13809
  }
13159
13810
  if (input.midTurn) {
13160
- return { midTurnAdjustedSchedulerDecision: "defer", sideEffect: "set-flag" };
13811
+ return {
13812
+ midTurnAdjustedSchedulerDecision: "defer",
13813
+ sideEffect: "set-flag",
13814
+ deferReason: "mid_turn_boundary"
13815
+ };
13161
13816
  }
13162
- return { midTurnAdjustedSchedulerDecision: "execute", sideEffect: "none" };
13817
+ return {
13818
+ midTurnAdjustedSchedulerDecision: "execute",
13819
+ sideEffect: "none",
13820
+ deferReason: null
13821
+ };
13163
13822
  }
13164
13823
 
13165
13824
  // ../plugin/src/hooks/magic-context/caveman.ts
@@ -13649,23 +14308,33 @@ function fenceWrapupBoundaryForToolArcs(args) {
13649
14308
  return boundary;
13650
14309
  }
13651
14310
  function applyHeadCap(args) {
13652
- const { index, protectedTailStart, offset, arcs, capTokens, recentOpenArcCutoff } = args;
14311
+ const {
14312
+ index,
14313
+ protectedTailStart,
14314
+ offset,
14315
+ arcs,
14316
+ lastCompartmentEndOrdinal,
14317
+ capTokens,
14318
+ recentOpenArcCutoff
14319
+ } = args;
13653
14320
  if (offset >= protectedTailStart)
13654
14321
  return { eligibleEndOrdinal: offset, oversizeAtomicUnit: false };
13655
14322
  let end = index.findHeadEndForCap(offset, protectedTailStart, capTokens);
13656
14323
  let oversizeAtomicUnit = end === offset + 1 && index.tokenForOrdinal(offset) > capTokens;
14324
+ const completedFence = fenceBoundaryForCompletedToolArcs(end, arcs, lastCompartmentEndOrdinal + 1);
14325
+ if (completedFence > end) {
14326
+ end = Math.min(protectedTailStart, completedFence);
14327
+ if (index.rangeTokens(offset, end) > capTokens)
14328
+ oversizeAtomicUnit = true;
14329
+ } else {
14330
+ end = completedFence;
14331
+ }
13657
14332
  for (const arc of arcs) {
13658
14333
  const resOrdinal = arc.resOrdinal;
13659
14334
  if (resOrdinal === null) {
13660
14335
  if (arc.invOrdinal >= recentOpenArcCutoff && arc.invOrdinal >= offset && arc.invOrdinal < end) {
13661
14336
  end = Math.min(end, arc.invOrdinal);
13662
14337
  }
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
14338
  }
13670
14339
  }
13671
14340
  if (end <= offset && offset < protectedTailStart) {
@@ -14267,6 +14936,9 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
14267
14936
  }
14268
14937
  });
14269
14938
  }
14939
+ function formatProjectedPostDropPercentage(value) {
14940
+ return value === null ? "none" : `${value.toFixed(1)}%`;
14941
+ }
14270
14942
  function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, clearReasoningAge, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail, taggerFloorOverride, reasoningProjection) {
14271
14943
  if (sessionMeta.compartmentInProgress) {
14272
14944
  sessionLog(sessionId, `compartment trigger: skipped — historian already in progress (usage=${usage.percentage.toFixed(1)}%)`);
@@ -14327,10 +14999,10 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
14327
14999
  const forceMaterializationPercentage = escalationBands(executeThresholdPercentage).forceMaterializationPercentage;
14328
15000
  if (usage.percentage >= forceMaterializationPercentage) {
14329
15001
  if (projectedPostDropPercentage !== null && projectedPostDropPercentage <= relativePostDropTarget) {
14330
- sessionLog(sessionId, `compartment trigger: skipping force band ${forceMaterializationPercentage}% because projected post-drop usage is ${projectedPostDropPercentage.toFixed(1)}% (target ${relativePostDropTarget.toFixed(1)}%)`);
15002
+ sessionLog(sessionId, `historian redundancy skip: summarizer not needed this pass — force band is ${forceMaterializationPercentage}%; queued/automatic drops are projected to reclaim to ${projectedPostDropPercentage.toFixed(1)}% (target ${relativePostDropTarget.toFixed(1)}%) on the next eligible execute pass`);
14331
15003
  return { shouldFire: false };
14332
15004
  }
14333
- sessionLog(sessionId, `compartment trigger: force-firing at ${usage.percentage.toFixed(1)}% (projected post-drop ${projectedPostDropPercentage?.toFixed(1) ?? "none"}%)`);
15005
+ sessionLog(sessionId, `compartment trigger: force-firing at ${usage.percentage.toFixed(1)}% (projected post-drop ${formatProjectedPostDropPercentage(projectedPostDropPercentage)})`);
14334
15006
  if (tailInfo.boundarySnapshot && hasRunnableCompartmentWindow(tailInfo.boundarySnapshot)) {
14335
15007
  return {
14336
15008
  shouldFire: true,
@@ -14388,14 +15060,14 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
14388
15060
  return { shouldFire: false };
14389
15061
  }
14390
15062
  if (projectedPostDropPercentage !== null && projectedPostDropPercentage <= relativePostDropTarget) {
14391
- sessionLog(sessionId, `compartment trigger: not firing at ${usage.percentage.toFixed(1)}% because projected post-drop usage is ${projectedPostDropPercentage.toFixed(1)}% (target ${relativePostDropTarget.toFixed(1)}%)`);
15063
+ sessionLog(sessionId, `historian redundancy skip: summarizer not needed this pass — usage is ${usage.percentage.toFixed(1)}%; queued/automatic drops are projected to reclaim to ${projectedPostDropPercentage.toFixed(1)}% (target ${relativePostDropTarget.toFixed(1)}%) on the next eligible execute pass`);
14392
15064
  return { shouldFire: false };
14393
15065
  }
14394
15066
  if (!tailInfo.hasProtectedEligibleHead || !tailInfo.isMeaningful) {
14395
15067
  sessionLog(sessionId, `compartment trigger: not firing at ${usage.percentage.toFixed(1)}% because unsummarized tail from ${tailInfo.nextStartOrdinal} is too small`);
14396
15068
  return { shouldFire: false };
14397
15069
  }
14398
- sessionLog(sessionId, `compartment trigger: proactive fire at ${usage.percentage.toFixed(1)}% (floor=${proactiveTriggerPercentage}% projected post-drop=${projectedPostDropPercentage?.toFixed(1) ?? "none"}% target=${relativePostDropTarget.toFixed(1)}%)`);
15070
+ sessionLog(sessionId, `compartment trigger: proactive fire at ${usage.percentage.toFixed(1)}% (floor=${proactiveTriggerPercentage}% projected post-drop=${formatProjectedPostDropPercentage(projectedPostDropPercentage)} target=${relativePostDropTarget.toFixed(1)}%)`);
14399
15071
  return {
14400
15072
  shouldFire: true,
14401
15073
  reason: "projected_headroom",
@@ -14436,8 +15108,9 @@ function buildSupersessionReclaimOps(input) {
14436
15108
  const action = input.targets.get(tag.tagNumber)?.readInput?.()?.action;
14437
15109
  isTarget = typeof action === "string" && CTX_NOTE_ZERO_VALUE_ACTIONS.has(action);
14438
15110
  }
14439
- if (isTarget)
15111
+ if (isTarget && (!input.recentMessageIds || tag.toolOwnerMessageId !== null && !input.recentMessageIds.has(tag.toolOwnerMessageId))) {
14440
15112
  dropTagIds.push(tag.tagNumber);
15113
+ }
14441
15114
  }
14442
15115
  const synthetic = [];
14443
15116
  for (const tagId of dropTagIds) {
@@ -14470,6 +15143,9 @@ function buildEditSupersessionReclaim(input) {
14470
15143
  seenFile.add(filePath);
14471
15144
  continue;
14472
15145
  }
15146
+ if (input.recentMessageIds && (tag.toolOwnerMessageId === null || input.recentMessageIds.has(tag.toolOwnerMessageId))) {
15147
+ continue;
15148
+ }
14473
15149
  if (realPendingTagIds.has(tag.tagNumber))
14474
15150
  continue;
14475
15151
  if (input.targets.get(tag.tagNumber)?.canDrop?.() !== true)
@@ -15258,10 +15934,10 @@ var DEFAULT_MIN_PROMPT_CHARS = 20;
15258
15934
  async function unifiedSearchWithTimeout(db, sessionId, projectPath, prompt, options, timeoutMs) {
15259
15935
  const controller = new AbortController;
15260
15936
  let timer;
15261
- const timeoutPromise = new Promise((resolve2) => {
15937
+ const timeoutPromise = new Promise((resolve3) => {
15262
15938
  timer = setTimeout(() => {
15263
15939
  controller.abort();
15264
- resolve2(null);
15940
+ resolve3(null);
15265
15941
  }, timeoutMs);
15266
15942
  });
15267
15943
  try {
@@ -16334,7 +17010,7 @@ function measurePiTailHygiene(input) {
16334
17010
  }
16335
17011
  continue;
16336
17012
  }
16337
- const content = part.text;
17013
+ const content = stripChannel1ReminderSpans(part.text);
16338
17014
  const tag = parseVisibleTag(content, tagsByNumber) ?? messageTags.get(`${messageKey}:p${textOrdinal}`);
16339
17015
  textOrdinal += 1;
16340
17016
  if (!content || isDropSentinel(content)) {
@@ -16437,7 +17113,8 @@ function refreshPiTailHygieneBaseline(input) {
16437
17113
  evaluable: true,
16438
17114
  generationInvalidated: false,
16439
17115
  baselineParts: measured.parts,
16440
- contentSignature: measured.contentSignature
17116
+ contentSignature: measured.contentSignature,
17117
+ channel1PostReduceGrace: input.previous?.channel1PostReduceGrace
16441
17118
  };
16442
17119
  }
16443
17120
  const prefix = sameMeasuredPrefix(input.previous.baselineParts, measured.parts);
@@ -16511,8 +17188,15 @@ function markPiChannel1Reduced(sessionId, db) {
16511
17188
  state2.evaluable = false;
16512
17189
  state2.generationInvalidated = true;
16513
17190
  }
16514
- if (db)
16515
- resetLastNudgeCycle(db, sessionId);
17191
+ if (!db)
17192
+ return;
17193
+ const grace = markChannel1PostReduceGracePending(db, sessionId);
17194
+ if (state2) {
17195
+ state2.channel1PostReduceGrace = {
17196
+ pending: true,
17197
+ preReduceLevel: grace.postReduceGracePreLevel ?? grace.level
17198
+ };
17199
+ }
16516
17200
  }
16517
17201
  function isPiTextContent(c) {
16518
17202
  return c !== null && typeof c === "object" && c.type === "text" && typeof c.text === "string";
@@ -16531,10 +17215,7 @@ function maybeChannel1ReminderForToolResult(args) {
16531
17215
  if (!state2)
16532
17216
  return null;
16533
17217
  if (toolName === "ctx_reduce") {
16534
- state2.reducedSinceRefresh = true;
16535
- state2.evaluable = false;
16536
- state2.generationInvalidated = true;
16537
- resetLastNudgeCycle(db, sessionId);
17218
+ markPiChannel1Reduced(sessionId, db);
16538
17219
  return null;
16539
17220
  }
16540
17221
  const text = toolResultText(args.content);
@@ -16551,28 +17232,32 @@ function maybeChannel1ReminderForToolResult(args) {
16551
17232
  ...state2,
16552
17233
  lastNudgeUndropped: getLastNudgeUndropped(db, sessionId),
16553
17234
  lastNudgeLevel: nudgeState.level,
16554
- hasRecentReduce: state2.reducedSinceRefresh
17235
+ lastFireOrdinal: nudgeState.ordinal,
17236
+ currentRealUserTurnCount: state2.realUserTurnCount,
17237
+ hasRecentReduce: state2.reducedSinceRefresh,
17238
+ postReduceGracePending: nudgeState.postReduceGracePending,
17239
+ postReduceGraceBaselineU: nudgeState.postReduceGraceBaselineU,
17240
+ postReduceGracePreLevel: nudgeState.postReduceGracePreLevel
16555
17241
  });
16556
17242
  setLastNudgeUndropped(db, sessionId, decision.nextLastNudge);
17243
+ const nextNudgeState = {
17244
+ ...nudgeState,
17245
+ level: decision.nextLastNudgeLevel,
17246
+ ordinal: decision.nextLastNudgeLevel === "" ? 0 : nudgeState.ordinal,
17247
+ postReduceGracePending: decision.clearPostReduceGrace ? undefined : nudgeState.postReduceGracePending,
17248
+ postReduceGraceBaselineU: decision.clearPostReduceGrace ? undefined : nudgeState.postReduceGraceBaselineU,
17249
+ postReduceGracePreLevel: decision.clearPostReduceGrace ? undefined : nudgeState.postReduceGracePreLevel
17250
+ };
16557
17251
  if (!decision.fire) {
16558
- setChannel1NudgeState(db, sessionId, {
16559
- level: decision.nextLastNudgeLevel,
16560
- ordinal: decision.nextLastNudgeLevel === "" ? 0 : nudgeState.ordinal
16561
- });
17252
+ setChannel1NudgeState(db, sessionId, nextNudgeState);
16562
17253
  return null;
16563
17254
  }
16564
- const sticky = shouldUseStickyChannel1Reminder({
16565
- lastLevel: nudgeState.level,
16566
- lastOrdinal: nudgeState.ordinal,
16567
- level: decision.level,
16568
- currentRealUserTurnCount: state2.realUserTurnCount
16569
- });
16570
17255
  const block = {
16571
17256
  type: "text",
16572
- text: buildChannel1Reminder(decision.level, decision.undroppedTokens, state2.usableWindow, state2.oldestReclaimableToolTags, sticky)
17257
+ text: buildChannel1Reminder(decision.level, decision.undroppedTokens, reclaimableToolOutputCount(state2.baselineParts), state2.oldestReclaimableToolTags, decision.sticky)
16573
17258
  };
16574
17259
  setChannel1NudgeState(db, sessionId, {
16575
- level: decision.level,
17260
+ ...nextNudgeState,
16576
17261
  ordinal: state2.realUserTurnCount
16577
17262
  });
16578
17263
  return block;
@@ -16606,7 +17291,7 @@ function maybeDeliverChannel2Pi(pi, db, sessionId) {
16606
17291
  try {
16607
17292
  const message = {
16608
17293
  customType: CHANNEL2_NUDGE_CUSTOM_TYPE,
16609
- content: buildChannel2Reminder(undropped, baseline.usableWindow, baseline.oldestReclaimableToolTags),
17294
+ content: buildChannel2Reminder(undropped, reclaimableToolOutputCount(baseline.baselineParts), baseline.oldestReclaimableToolTags),
16610
17295
  display: false,
16611
17296
  details: { kind: "channel-2-ceiling-nudge" }
16612
17297
  };
@@ -16896,6 +17581,8 @@ function piImageFromDataUrl(dataUrl) {
16896
17581
  return { type: "image", mimeType: match[1], data: match[2] };
16897
17582
  }
16898
17583
  function resolveMuralForM0Pi(state2, db, modelKey, budgetTokens) {
17584
+ if (state2.memoryEnabled === false)
17585
+ return;
16899
17586
  if (state2.mural)
16900
17587
  return state2.mural;
16901
17588
  if (!state2.muralEnabled)
@@ -17107,7 +17794,7 @@ function renderM0Pi(state2, db, projectDocs = readProjectDocsForPiM0(state2).ren
17107
17794
  const sections = [];
17108
17795
  if (projectDocs.length > 0)
17109
17796
  sections.push(projectDocs);
17110
- const trimmedProfile = trimUserMemoriesToBudget(userProfileOverride ?? safeGetActiveUserMemoriesPi(db), state2.userProfileBudgetTokens ?? DEFAULT_USER_PROFILE_BUDGET_TOKENS);
17797
+ const trimmedProfile = trimUserMemoriesToBudget(state2.memoryEnabled === false ? [] : userProfileOverride ?? safeGetActiveUserMemoriesPi(db), state2.userProfileBudgetTokens ?? DEFAULT_USER_PROFILE_BUDGET_TOKENS);
17111
17798
  const userProfile = renderUserProfileBlock(db, "user-profile", trimmedProfile);
17112
17799
  if (userProfile.length > 0)
17113
17800
  sections.push(userProfile);
@@ -17167,7 +17854,7 @@ function readFrozenM0InputsPi(state2, db, docs = readProjectDocsForPiM0(state2),
17167
17854
  const workspace = resolveWorkspaceRenderContextPi(state2, db);
17168
17855
  const compartments = getRenderableCompartmentsPi(db, state2);
17169
17856
  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);
17857
+ const userProfile = state2.memoryEnabled === false ? [] : safeGetActiveUserMemoriesPi(db);
17171
17858
  const projectState = memPath ? getProjectState(db, memPath) : undefined;
17172
17859
  const globalState = getProjectState(db, GLOBAL_USER_PROFILE_PROJECT_PATH);
17173
17860
  const markers = {
@@ -17187,7 +17874,7 @@ function readFrozenM0InputsPi(state2, db, docs = readProjectDocsForPiM0(state2),
17187
17874
  systemHash: (state2.hardSignals ?? EMPTY_PI_HARD_SIGNALS).systemHash,
17188
17875
  modelKey: piModelRefToCanonical((state2.hardSignals ?? EMPTY_PI_HARD_SIGNALS).modelKey),
17189
17876
  projectIdentity: state2.projectIdentity,
17190
- muralEnabled: state2.muralEnabled === true,
17877
+ muralEnabled: state2.memoryEnabled !== false && state2.muralEnabled === true,
17191
17878
  renderBudgetIdentity: renderBudgetIdentityPi(state2)
17192
17879
  };
17193
17880
  return { docs, markers, compartments, memories, userProfile, workspace };
@@ -17409,7 +18096,7 @@ ${body}
17409
18096
  if (newMemoriesBlock)
17410
18097
  sections.push(newMemoriesBlock);
17411
18098
  const currentUserProfileVersion = getProjectState(db, GLOBAL_USER_PROFILE_PROJECT_PATH)?.projectUserProfileVersion ?? 0;
17412
- if (currentUserProfileVersion !== markers.projectUserProfileVersion) {
18099
+ if (state2.memoryEnabled !== false && currentUserProfileVersion !== markers.projectUserProfileVersion) {
17413
18100
  const profileBudget = state2.userProfileBudgetTokens ?? DEFAULT_USER_PROFILE_BUDGET_TOKENS;
17414
18101
  const trimmedProfile = trimUserMemoriesToBudget(safeGetActiveUserMemoriesPi(db), Math.max(1, Math.floor(profileBudget * 0.25)));
17415
18102
  const profileBlock = renderUserProfileBlock(db, "new-user-profile", trimmedProfile);
@@ -20994,7 +21681,7 @@ async function sleepWithAbort(ms, signal) {
20994
21681
  return true;
20995
21682
  if (ms <= 0)
20996
21683
  return signal?.aborted === true;
20997
- return new Promise((resolve2) => {
21684
+ return new Promise((resolve3) => {
20998
21685
  let settled = false;
20999
21686
  let timeout;
21000
21687
  const finish = (aborted) => {
@@ -21004,7 +21691,7 @@ async function sleepWithAbort(ms, signal) {
21004
21691
  if (timeout)
21005
21692
  clearTimeout(timeout);
21006
21693
  signal?.removeEventListener("abort", onAbort);
21007
- resolve2(aborted);
21694
+ resolve3(aborted);
21008
21695
  };
21009
21696
  const onAbort = () => finish(true);
21010
21697
  signal?.addEventListener("abort", onAbort, { once: true });
@@ -21123,6 +21810,8 @@ async function runPiHistorian(deps) {
21123
21810
  refreshBoundarySnapshot,
21124
21811
  currentContextLimit,
21125
21812
  historianTimeoutMs = DEFAULT_HISTORIAN_TIMEOUT_MS2,
21813
+ temperature,
21814
+ maxOutputTokens = 32000,
21126
21815
  signal,
21127
21816
  retryBackoffMs,
21128
21817
  twoPass,
@@ -21235,9 +21924,9 @@ async function runPiHistorian(deps) {
21235
21924
  executeThresholdPercentage: boundarySnapshot.executeThresholdPercentage
21236
21925
  });
21237
21926
  if (!reserve.ok) {
21238
- sessionLog(sessionId, `historian rate-limit skip: ${reserve.skippedReason ?? "quota exhausted"}`);
21927
+ sessionLog(sessionId, describeProtectedTailDrainBudgetSkip(reserve));
21239
21928
  telemetry.status = "noop";
21240
- telemetry.failureReason = "protected-tail drain quota exhausted";
21929
+ telemetry.failureReason = "internal protected-tail drain budget spent";
21241
21930
  return;
21242
21931
  }
21243
21932
  drainReservation = reserve.reservation;
@@ -21350,6 +22039,8 @@ ${chunkText}`,
21350
22039
  cwd: directory,
21351
22040
  signal,
21352
22041
  thinkingLevel,
22042
+ temperature,
22043
+ maxOutputTokens,
21353
22044
  onProgress: buildProgressLogger("first"),
21354
22045
  accountingSessionId: sessionId,
21355
22046
  accountingSubagent: "historian"
@@ -21374,6 +22065,8 @@ ${chunkText}`,
21374
22065
  cwd: directory,
21375
22066
  signal,
21376
22067
  thinkingLevel,
22068
+ temperature,
22069
+ maxOutputTokens,
21377
22070
  onProgress: buildProgressLogger("repair"),
21378
22071
  accountingSessionId: sessionId,
21379
22072
  accountingSubagent: "historian"
@@ -21403,6 +22096,8 @@ ${chunkText}`,
21403
22096
  cwd: directory,
21404
22097
  signal,
21405
22098
  thinkingLevel: candidate.entry.qualifier,
22099
+ temperature,
22100
+ maxOutputTokens,
21406
22101
  onProgress: buildProgressLogger("fallback"),
21407
22102
  accountingSessionId: sessionId,
21408
22103
  accountingSubagent: "historian"
@@ -21449,6 +22144,8 @@ ${chunkText}`,
21449
22144
  cwd: directory,
21450
22145
  signal,
21451
22146
  thinkingLevel,
22147
+ temperature,
22148
+ maxOutputTokens,
21452
22149
  onProgress: buildProgressLogger("editor"),
21453
22150
  accountingSessionId: sessionId,
21454
22151
  accountingSubagent: "historian_editor"
@@ -21759,6 +22456,62 @@ function findFirstKeptEntryId(entries, lastCompactedOrdinal) {
21759
22456
  return boundary.id;
21760
22457
  }
21761
22458
 
22459
+ // src/pi-pressure.ts
22460
+ function extractAssistantUsage(message) {
22461
+ if (!message || typeof message !== "object")
22462
+ return null;
22463
+ const m = message;
22464
+ if (m.role !== "assistant")
22465
+ return null;
22466
+ if (!m.usage || typeof m.usage !== "object")
22467
+ return null;
22468
+ const u = m.usage;
22469
+ const result = {};
22470
+ if (typeof u.input === "number")
22471
+ result.input = u.input;
22472
+ if (typeof u.output === "number")
22473
+ result.output = u.output;
22474
+ if (typeof u.cacheRead === "number")
22475
+ result.cacheRead = u.cacheRead;
22476
+ if (typeof u.cacheWrite === "number")
22477
+ result.cacheWrite = u.cacheWrite;
22478
+ if (typeof u.totalTokens === "number")
22479
+ result.totalTokens = u.totalTokens;
22480
+ return result;
22481
+ }
22482
+ function computePiPressure(usage, contextLimit) {
22483
+ if (!usage)
22484
+ return null;
22485
+ const input = usage.input ?? 0;
22486
+ const cacheRead = usage.cacheRead ?? 0;
22487
+ const cacheWrite = usage.cacheWrite ?? 0;
22488
+ const inputTokens = input + cacheRead + cacheWrite;
22489
+ if (inputTokens === 0)
22490
+ return null;
22491
+ const percentage = contextLimit > 0 ? inputTokens / contextLimit * 100 : 0;
22492
+ return { inputTokens, percentage };
22493
+ }
22494
+ function resolvePiPressureSnapshot(args) {
22495
+ const persistedInputTokens = Number.isFinite(args.persistedInputTokens) && args.persistedInputTokens > 0 ? args.persistedInputTokens : 0;
22496
+ const liveInputTokens = typeof args.liveInputTokens === "number" && Number.isFinite(args.liveInputTokens) && args.liveInputTokens > 0 ? args.liveInputTokens : 0;
22497
+ const inputTokens = Math.max(persistedInputTokens, liveInputTokens);
22498
+ const contextLimit = typeof args.usableContextLimit === "number" && Number.isFinite(args.usableContextLimit) && args.usableContextLimit > 0 ? args.usableContextLimit : undefined;
22499
+ if (contextLimit !== undefined) {
22500
+ return {
22501
+ inputTokens,
22502
+ percentage: inputTokens > 0 ? inputTokens / contextLimit * 100 : 0,
22503
+ contextLimit
22504
+ };
22505
+ }
22506
+ return {
22507
+ inputTokens: persistedInputTokens,
22508
+ percentage: Number.isFinite(args.persistedPercentage) && args.persistedPercentage > 0 ? args.persistedPercentage : 0
22509
+ };
22510
+ }
22511
+ function formatPiPressureForLog(snapshot) {
22512
+ return `usage=${snapshot.percentage.toFixed(1)}% (${snapshot.inputTokens} tokens, limit=${snapshot.contextLimit ?? "?"})`;
22513
+ }
22514
+
21762
22515
  // src/pi-todo-inject.ts
21763
22516
  function getMessageId(message) {
21764
22517
  if (typeof message.responseId === "string" && message.responseId.length > 0) {
@@ -22585,8 +23338,8 @@ function injectPiTemporalMarkers(messages) {
22585
23338
  // src/timeout.ts
22586
23339
  function withTimeout(p, ms) {
22587
23340
  let handle;
22588
- const timeout = new Promise((resolve2) => {
22589
- handle = setTimeout(() => resolve2(undefined), ms);
23341
+ const timeout = new Promise((resolve3) => {
23342
+ handle = setTimeout(() => resolve3(undefined), ms);
22590
23343
  handle.unref?.();
22591
23344
  });
22592
23345
  return Promise.race([p, timeout]).finally(() => {
@@ -23300,26 +24053,14 @@ function extractStableId(msg, index, entryIds) {
23300
24053
 
23301
24054
  // src/context-handler.ts
23302
24055
  var EMERGENCY_BLOCK_PERCENTAGE = 95;
23303
- var FORWARD_PRESSURE_LIMIT_FACTOR = 0.85;
23304
24056
  function isPiHardCacheExpired(lastResponseTime, ttlMs, now) {
23305
24057
  return lastResponseTime > 0 && now - lastResponseTime > ttlMs;
23306
24058
  }
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
24059
  var injectM0M1PiForRun = injectM0M1Pi;
23320
24060
  var persistReasoningWatermarkForRun = updateSessionMeta;
23321
24061
  var persistStableIdSchemeForRun = updateSessionMeta;
23322
24062
  var afterFallbackAdoptionForTests;
24063
+ var pendingDecisionLogObserverForTests;
23323
24064
  var mutationGateObserverForTests;
23324
24065
  var DEFAULT_CLEAR_REASONING_AGE = 50;
23325
24066
  var PI_STABLE_ID_SCHEME = 1;
@@ -24255,7 +24996,12 @@ function registerPiContextHandler(pi, baseOptions) {
24255
24996
  if (!usedPersistedUsage && isSaneLimit(usageContextLimit) && usageInputTokens > 0) {
24256
24997
  usagePercentage = usageInputTokens / usageContextLimit * 100;
24257
24998
  }
24258
- ({ percentage: usagePercentage, inputTokens: usageInputTokens } = applyForwardPressureFloor(usagePercentage, usageInputTokens, piUsage?.tokens, usageContextLimit));
24999
+ ({ percentage: usagePercentage, inputTokens: usageInputTokens } = resolvePiPressureSnapshot({
25000
+ persistedPercentage: usagePercentage,
25001
+ persistedInputTokens: usageInputTokens,
25002
+ liveInputTokens: piUsage?.tokens,
25003
+ usableContextLimit: usageContextLimit
25004
+ }));
24259
25005
  const realUsagePercentageBeforeEmergencyBump = usagePercentage;
24260
25006
  if (needsEmergencyBump) {
24261
25007
  sessionLog(sessionId, `transform: overflow recovery flag set — bumping percentage to 95% (detectedLimit=${usageContextLimit ?? "unknown"})`);
@@ -24298,9 +25044,14 @@ function registerPiContextHandler(pi, baseOptions) {
24298
25044
  sessionId,
24299
25045
  effectiveExecuteThresholdPercentage
24300
25046
  });
24301
- const { midTurnAdjustedSchedulerDecision, sideEffect } = options.compactionOff ? {
25047
+ const {
25048
+ midTurnAdjustedSchedulerDecision,
25049
+ sideEffect,
25050
+ deferReason: schedulerDeferReason
25051
+ } = options.compactionOff ? {
24302
25052
  midTurnAdjustedSchedulerDecision: "defer",
24303
- sideEffect: "none"
25053
+ sideEffect: "none",
25054
+ deferReason: "scheduler_defer"
24304
25055
  } : applyMidTurnDeferral({
24305
25056
  base: schedulerDecisionEarly,
24306
25057
  bypassReason,
@@ -24317,6 +25068,9 @@ function registerPiContextHandler(pi, baseOptions) {
24317
25068
  schedulerDecision = midTurnAdjustedSchedulerDecision;
24318
25069
  sessionLog(sessionId, `[boundary-exec] base=${schedulerDecisionEarly} bypass=${bypassReason} midTurn=${midTurn} effective=${midTurnAdjustedSchedulerDecision} sideEffect=${sideEffect}`);
24319
25070
  const forceMaterialization = !options.compactionOff && usagePercentage >= forceMaterializationPercentage;
25071
+ if (!forceMaterialization && getEmergencyInputSample(options.db, sessionId) > 0) {
25072
+ clearEmergencyDropSample(options.db, sessionId);
25073
+ }
24320
25074
  const hardUsagePercentage = needsEmergencyBump ? Math.max(EMERGENCY_BLOCK_PERCENTAGE, usagePercentage) : windowGeometry?.usableHard && usageInputTokens > 0 ? usageInputTokens / windowGeometry.usableHard * 100 : usagePercentage;
24321
25075
  const emergencyPercentage = ctx.model ? hardUsagePercentage : usagePercentage;
24322
25076
  const isEmergency = !options.compactionOff && emergencyPercentage >= EMERGENCY_BLOCK_PERCENTAGE;
@@ -24346,7 +25100,7 @@ function registerPiContextHandler(pi, baseOptions) {
24346
25100
  }
24347
25101
  const isCacheBusting = historyRefreshSessions.has(sessionId);
24348
25102
  logTransformTiming(sessionId, "boundaryTriggerChecks", tBoundaryChecks);
24349
- sessionLog(sessionId, `transform: usage=${usagePercentage.toFixed(1)}% (${usageInputTokens} tokens, limit=${usageContextLimit ?? "?"}) decision=${schedulerDecision}${forceMaterialization ? " force=true" : ""}${isEmergency ? " EMERGENCY=true" : ""}${isCacheBusting ? " busting=true" : ""}`);
25103
+ sessionLog(sessionId, `transform: ${formatPiPressureForLog({ percentage: usagePercentage, inputTokens: usageInputTokens, contextLimit: usageContextLimit })} decision=${schedulerDecision}${forceMaterialization ? " force=true" : ""}${isEmergency ? " EMERGENCY=true" : ""}${isCacheBusting ? " busting=true" : ""}`);
24350
25104
  logTransformTiming(sessionId, "emergencyRecoveryBlock", tEmergencyRecovery);
24351
25105
  const entryIds = strictEntryIds ?? undefined;
24352
25106
  const emergencyCeilingTokens = usageContextLimit && usageContextLimit > 0 ? Math.floor(usageContextLimit * (resolveExecuteThreshold(schedulerConfig.executeThresholdPercentage ?? 65, liveModelBySession.get(sessionId), 65, {
@@ -24386,6 +25140,7 @@ function registerPiContextHandler(pi, baseOptions) {
24386
25140
  reusableMessageIds,
24387
25141
  stableIdSchemeCutover,
24388
25142
  schedulerDecision,
25143
+ schedulerDeferReason,
24389
25144
  forceMaterialization: forceMaterialization || isEmergency,
24390
25145
  forceMaterializationPercentage,
24391
25146
  contextUsage: {
@@ -24524,7 +25279,12 @@ function registerPiContextHandler(pi, baseOptions) {
24524
25279
  previous: getPiChannel1Baseline(sessionId)
24525
25280
  });
24526
25281
  const effective = effectivePiTailHygiene(baseline);
24527
- resetLastNudgeCycleIfTailShrank(options.db, sessionId, effective.u);
25282
+ const durableGrace = baseline.evaluable && !baseline.generationInvalidated ? captureChannel1PostReduceGraceBaseline(options.db, sessionId, effective.u) : getChannel1NudgeState(options.db, sessionId);
25283
+ baseline.channel1PostReduceGrace = durableGrace.postReduceGracePending || durableGrace.postReduceGraceBaselineU !== undefined ? {
25284
+ pending: durableGrace.postReduceGracePending === true,
25285
+ baselineU: durableGrace.postReduceGraceBaselineU,
25286
+ preReduceLevel: durableGrace.postReduceGracePreLevel ?? durableGrace.level
25287
+ } : undefined;
24528
25288
  const oldestReclaimableToolTags = getOldestActiveUnprotectedToolTags(options.db, sessionId, protectedTags);
24529
25289
  const channelState = {
24530
25290
  ...baseline,
@@ -24631,10 +25391,11 @@ function registerPiContextHandler(pi, baseOptions) {
24631
25391
  log("[magic-context][pi] registered context handler (tagging + drops + nudges)");
24632
25392
  }
24633
25393
  var inFlightHistorian = new Map;
24634
- async function awaitInFlightHistorians() {
24635
- if (inFlightHistorian.size === 0)
25394
+ async function awaitInFlightHistorians(sessionId) {
25395
+ const runs = sessionId ? [inFlightHistorian.get(sessionId)].filter((run) => run !== undefined) : [...inFlightHistorian.values()];
25396
+ if (runs.length === 0)
24636
25397
  return;
24637
- await Promise.allSettled(Array.from(inFlightHistorian.values()));
25398
+ await Promise.allSettled(runs);
24638
25399
  }
24639
25400
  function resolvePiHistorianTriggerInputs(args) {
24640
25401
  const contextLimit = typeof args.usageContextLimit === "number" && Number.isFinite(args.usageContextLimit) && args.usageContextLimit > 0 ? args.usageContextLimit : DEFAULT_CONTEXT_LIMIT;
@@ -24771,6 +25532,8 @@ function spawnPiHistorianRun(args) {
24771
25532
  refreshBoundarySnapshot,
24772
25533
  currentContextLimit,
24773
25534
  historianTimeoutMs: historian.timeoutMs,
25535
+ temperature: historian.temperature,
25536
+ maxOutputTokens: historian.maxOutputTokens,
24774
25537
  twoPass: historian.twoPass,
24775
25538
  thinkingLevel: historian.thinkingLevel,
24776
25539
  memoryEnabled: historian.memoryEnabled,
@@ -24889,7 +25652,12 @@ function maybeFireHistorian(args) {
24889
25652
  };
24890
25653
  usageSource = "piUsage fallback";
24891
25654
  }
24892
- usage = applyForwardPressureFloor(usage.percentage, usage.inputTokens, piUsage?.tokens, usageContextLimit);
25655
+ usage = resolvePiPressureSnapshot({
25656
+ persistedPercentage: usage.percentage,
25657
+ persistedInputTokens: usage.inputTokens,
25658
+ liveInputTokens: piUsage?.tokens,
25659
+ usableContextLimit: usageContextLimit
25660
+ });
24893
25661
  sessionLog(sessionId, `historian trigger eval: usage=${usage.percentage.toFixed(1)}% (${usage.inputTokens} tokens) [${usageSource}], checking trigger...`);
24894
25662
  } catch (err) {
24895
25663
  sessionLog(sessionId, `historian trigger eval: getContextUsage threw: ${err instanceof Error ? err.message : String(err)}`);
@@ -25270,7 +26038,10 @@ async function runPipeline(args) {
25270
26038
  });
25271
26039
  if (shouldApplyPendingOps) {
25272
26040
  const applyReason = hasPendingMaterializeSignal ? "explicit_flush" : deferredMaterialize ? "deferred_publication" : args.forceMaterialization ? "force_materialization" : foldExecutedThisPass && args.schedulerDecision !== "execute" ? `m0_hard_fold (drain folded into executed m[0] bust, scheduler=${args.schedulerDecision})` : `scheduler_execute (scheduler=${args.schedulerDecision})`;
25273
- sessionLog(args.sessionId, `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${pendingOps.length}, context=${args.contextUsage.percentage.toFixed(1)}%`);
26041
+ const pendingOpsDepth = getPendingOpsCount(args.db, args.sessionId);
26042
+ const pendingDecisionLog = `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${pendingOpsDepth === null ? "not loaded (deferred pass)" : pendingOpsDepth} context=${args.contextUsage.percentage.toFixed(1)}%`;
26043
+ sessionLog(args.sessionId, pendingDecisionLog);
26044
+ pendingDecisionLogObserverForTests?.(pendingDecisionLog);
25274
26045
  try {
25275
26046
  const tApplyPending = performance.now();
25276
26047
  pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags, pendingOperationTags, pendingOps);
@@ -25291,7 +26062,11 @@ async function runPipeline(args) {
25291
26062
  throw err;
25292
26063
  }
25293
26064
  } else {
25294
- sessionLog(args.sessionId, `pending ops WILL NOT APPLY — reason=scheduler_defer pendingOps=${pendingOps.length} context=${args.contextUsage.percentage.toFixed(1)}%`);
26065
+ const pendingOpsDepth = getPendingOpsCount(args.db, args.sessionId);
26066
+ const refusalReason = args.schedulerDeferReason ?? (historianRunning ? "historian_in_flight" : "scheduler_defer");
26067
+ const pendingDecisionLog = `pending ops WILL NOT APPLY — reason=${refusalReason} pendingOps=${pendingOpsDepth === null ? "not loaded (deferred pass)" : pendingOpsDepth} context=${args.contextUsage.percentage.toFixed(1)}%`;
26068
+ sessionLog(args.sessionId, pendingDecisionLog);
26069
+ pendingDecisionLogObserverForTests?.(pendingDecisionLog);
25295
26070
  }
25296
26071
  const targetTagNumbers = [...targets.keys()];
25297
26072
  const tGetTags = performance.now();
@@ -25348,14 +26123,22 @@ async function runPipeline(args) {
25348
26123
  logTransformTiming(args.sessionId, "getActiveTagsBySession", tActiveTags, `count=${activeTags.length}`);
25349
26124
  if (shouldRunHeuristics) {
25350
26125
  const reason = args.forceMaterialization ? "force_materialization" : foldExecutedThisPass && args.schedulerDecision !== "execute" ? `m0_hard_fold (drain folded into executed m[0] bust, scheduler=${args.schedulerDecision})` : `scheduler_execute (pendingOps=${pendingOps.length}, scheduler=${args.schedulerDecision})`;
25351
- sessionLog(args.sessionId, `heuristics WILL RUN — reason=${reason}, context=${args.contextUsage.percentage.toFixed(1)}%, turn=n/a`);
26126
+ const heuristicsDecisionLog = `heuristics WILL RUN — reason=${reason}, context=${args.contextUsage.percentage.toFixed(1)}%, turn=n/a`;
26127
+ sessionLog(args.sessionId, heuristicsDecisionLog);
26128
+ pendingDecisionLogObserverForTests?.(heuristicsDecisionLog);
25352
26129
  } else {
25353
- const reason = args.heuristics === undefined ? "disabled" : "scheduler_defer";
25354
- sessionLog(args.sessionId, `heuristics WILL NOT RUN — reason=${reason}`);
26130
+ const reason = args.heuristics === undefined ? "disabled" : args.schedulerDeferReason ?? (historianRunning ? "historian_in_flight" : alreadyRanHeuristicsThisTurn ? "already_ran_this_turn" : "scheduler_defer");
26131
+ const heuristicsDecisionLog = `heuristics WILL NOT RUN — reason=${reason}`;
26132
+ sessionLog(args.sessionId, heuristicsDecisionLog);
26133
+ pendingDecisionLogObserverForTests?.(heuristicsDecisionLog);
25355
26134
  }
25356
26135
  if (shouldRunHeuristics && args.heuristics) {
25357
26136
  try {
25358
26137
  const tHeuristic = performance.now();
26138
+ const independentMutationBeforeHeuristics = pendingOpsDidMutate || foldExecutedThisPass;
26139
+ if (args.forceMaterialization === true && independentMutationBeforeHeuristics && getEmergencyInputSample(args.db, args.sessionId) > 0) {
26140
+ clearEmergencyDropSample(args.db, args.sessionId);
26141
+ }
25359
26142
  heuristicsResult = applyPiHeuristicCleanup(args.sessionId, args.db, targets, args.messages, {
25360
26143
  protectedTags: args.protectedTags,
25361
26144
  staleReduceStripEnabled: args.canUseEmptySentinels,
@@ -25875,9 +26658,10 @@ function registerCtxFlushCommand(pi, deps) {
25875
26658
  pi.registerCommand("ctx-flush", {
25876
26659
  description: "Force pending Magic Context drops to materialize on the next provider call",
25877
26660
  handler: async (_args, ctx) => {
26661
+ const sendStatus = createCtxStatusSender(pi, ctx);
25878
26662
  const sessionId = resolveSessionId(ctx);
25879
26663
  if (!sessionId) {
25880
- sendCtxStatusMessage(pi, {
26664
+ sendStatus({
25881
26665
  title: "/ctx-flush",
25882
26666
  text: `## /ctx-flush
25883
26667
 
@@ -25887,7 +26671,7 @@ No active Pi session is available.`,
25887
26671
  return;
25888
26672
  }
25889
26673
  if (deps.compactionOff) {
25890
- sendCtxStatusMessage(pi, {
26674
+ sendStatus({
25891
26675
  title: "/ctx-flush",
25892
26676
  text: COMPACTION_OFF_COMMAND_UNAVAILABLE,
25893
26677
  level: "warning"
@@ -25906,7 +26690,7 @@ Flushed ${pendingBefore} pending ops; next provider call will materialize.
25906
26690
  ${result}` : `## /ctx-flush
25907
26691
 
25908
26692
  ${result}`;
25909
- sendCtxStatusMessage(pi, {
26693
+ sendStatus({
25910
26694
  title: "/ctx-flush",
25911
26695
  text,
25912
26696
  level: result.startsWith("Error:") ? "error" : "success"
@@ -25926,7 +26710,7 @@ function cleanupHistorianStateFile(path6) {
25926
26710
  }
25927
26711
  // ../plugin/src/hooks/magic-context/compartment-runner-historian.ts
25928
26712
  import { mkdirSync as mkdirSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "node:fs";
25929
- import { join as join4 } from "node:path";
26713
+ import { join as join5 } from "node:path";
25930
26714
 
25931
26715
  // ../plugin/src/agents/historian.ts
25932
26716
  var HISTORIAN_AGENT = "historian";
@@ -26263,8 +27047,8 @@ function isTransientHistorianPromptError2(message) {
26263
27047
  ].some((token) => normalized.includes(token));
26264
27048
  }
26265
27049
  function sleep(ms) {
26266
- return new Promise((resolve2) => {
26267
- setTimeout(resolve2, ms);
27050
+ return new Promise((resolve3) => {
27051
+ setTimeout(resolve3, ms);
26268
27052
  });
26269
27053
  }
26270
27054
  function cleanupHistorianDump(sessionId, dumpPath) {
@@ -26286,7 +27070,7 @@ function dumpHistorianResponse(sessionId, directory, label, text) {
26286
27070
  ensureCortexKitArtifactGitignore(directory);
26287
27071
  const safeSessionId = sanitizeDumpName(sessionId);
26288
27072
  const safeLabel = sanitizeDumpName(label);
26289
- const dumpPath = join4(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
27073
+ const dumpPath = join5(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
26290
27074
  writeFileSync5(dumpPath, text, "utf8");
26291
27075
  sessionLog(sessionId, "compartment agent: historian response dumped", {
26292
27076
  label,
@@ -26319,6 +27103,7 @@ function insertRecompCompartmentRows(db, sessionId, compartments, now) {
26319
27103
  }
26320
27104
  function promoteRecompStagingWithM0Mutation(db, sessionId, holderId) {
26321
27105
  const now = Date.now();
27106
+ const transactionStartedAt = performance.now();
26322
27107
  db.exec("BEGIN IMMEDIATE");
26323
27108
  let finished = false;
26324
27109
  try {
@@ -26347,6 +27132,7 @@ function promoteRecompStagingWithM0Mutation(db, sessionId, holderId) {
26347
27132
  clearCachedM0M1(db, sessionId);
26348
27133
  db.exec("COMMIT");
26349
27134
  finished = true;
27135
+ logSlowWriteTransaction("historian-publish:recomp", transactionStartedAt);
26350
27136
  return { compartments: staging.compartments, facts: staging.facts };
26351
27137
  } finally {
26352
27138
  if (!finished) {
@@ -27130,6 +27916,8 @@ function createPiHistorianClient(args) {
27130
27916
  const sessions = new Map;
27131
27917
  let counter = 0;
27132
27918
  async function prompt(input) {
27919
+ if (args.signal?.aborted)
27920
+ throw new Error("prompt aborted by external signal");
27133
27921
  const body = readBody(input);
27134
27922
  const sessionId = readPathId(input);
27135
27923
  if (body.noReply) {
@@ -27150,8 +27938,11 @@ function createPiHistorianClient(args) {
27150
27938
  cwd: args.directory,
27151
27939
  thinkingLevel: modelOverride ? modelOverride.qualifier : args.thinkingLevel,
27152
27940
  accountingSessionId: args.accountingSessionId,
27153
- accountingSubagent: "recomp"
27941
+ accountingSubagent: "recomp",
27942
+ signal: args.signal
27154
27943
  });
27944
+ if (args.signal?.aborted)
27945
+ throw new Error("prompt aborted by external signal");
27155
27946
  if (!result.ok) {
27156
27947
  throw new Error(`Pi recomp historian failed (${result.reason}): ${result.error}`);
27157
27948
  }
@@ -27162,6 +27953,8 @@ function createPiHistorianClient(args) {
27162
27953
  session: {
27163
27954
  get: async () => ({ directory: args.directory }),
27164
27955
  create: async () => {
27956
+ if (args.signal?.aborted)
27957
+ throw new Error("prompt aborted by external signal");
27165
27958
  const id = `magic-context-pi-recomp-${++counter}`;
27166
27959
  sessions.set(id, []);
27167
27960
  return { id };
@@ -27218,16 +28011,13 @@ function makeMessage2(role, text) {
27218
28011
 
27219
28012
  // src/pi-recomp-marker.ts
27220
28013
  function stagePiRecompMarker(args) {
27221
- const readBranchEntries = resolvePiReadBranchEntries2(args.ctx);
27222
- if (!readBranchEntries)
27223
- return;
27224
28014
  const compartments = getCompartments(args.db, args.sessionId);
27225
28015
  const last = compartments[compartments.length - 1];
27226
28016
  if (!last)
27227
28017
  return;
27228
28018
  let firstKeptEntryId = null;
27229
28019
  try {
27230
- firstKeptEntryId = findFirstKeptEntryId(readBranchEntries(), last.endMessage);
28020
+ firstKeptEntryId = findFirstKeptEntryId(args.branchEntries, last.endMessage);
27231
28021
  } catch {
27232
28022
  firstKeptEntryId = null;
27233
28023
  }
@@ -27243,19 +28033,6 @@ function stagePiRecompMarker(args) {
27243
28033
  });
27244
28034
  signalPiDeferredHistoryRefresh(args.sessionId);
27245
28035
  }
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
28036
 
27260
28037
  // src/status-line.ts
27261
28038
  var STATUS_KEY = "magic-context";
@@ -27301,7 +28078,6 @@ function renderStatusText(ctx, db, sessionId) {
27301
28078
  const meta = readSessionMetaStatus(db, sessionId);
27302
28079
  const liveInputTokens = typeof usage?.tokens === "number" && Number.isFinite(usage.tokens) ? usage.tokens : undefined;
27303
28080
  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
28081
  const windowGeometry = resolvePiWindowGeometry({
27306
28082
  rawContextWindow: usage?.contextWindow ?? ctx.model?.contextWindow,
27307
28083
  model: ctx.model,
@@ -27309,8 +28085,14 @@ function renderStatusText(ctx, db, sessionId) {
27309
28085
  persistedInputTokens,
27310
28086
  persistedPercentage: typeof meta?.last_context_percentage === "number" ? meta.last_context_percentage : undefined
27311
28087
  });
27312
- const usableSoft = windowGeometry?.usableSoft;
27313
- const pct = inputTokens !== undefined && usableSoft !== undefined && usableSoft > 0 ? inputTokens / usableSoft * 100 : typeof meta?.last_context_percentage === "number" ? meta.last_context_percentage : undefined;
28088
+ const pressure = liveInputTokens !== undefined || persistedInputTokens !== undefined ? resolvePiPressureSnapshot({
28089
+ persistedPercentage: meta?.last_context_percentage ?? 0,
28090
+ persistedInputTokens: persistedInputTokens ?? 0,
28091
+ liveInputTokens,
28092
+ usableContextLimit: windowGeometry?.usableSoft
28093
+ }) : undefined;
28094
+ const inputTokens = pressure?.inputTokens;
28095
+ const pct = pressure?.percentage;
27314
28096
  const state2 = renderHistorianState(meta, recompSessions.has(sessionId));
27315
28097
  return `mc: ${inputTokens === undefined ? "--" : fmt(inputTokens)} (${pct === undefined ? "--" : `${Math.round(pct)}%`}) · ${state2}`;
27316
28098
  }
@@ -27367,29 +28149,41 @@ var inFlightRecomp = new Map;
27367
28149
  function isPiRecompInFlight(sessionId) {
27368
28150
  return inFlightRecomp.has(sessionId);
27369
28151
  }
27370
- async function awaitInFlightRecomps() {
27371
- if (inFlightRecomp.size === 0)
28152
+ async function awaitInFlightRecomps(sessionId) {
28153
+ const runs = sessionId ? [inFlightRecomp.get(sessionId)?.promise].filter((run) => run !== undefined) : [...inFlightRecomp.values()].map((run) => run.promise);
28154
+ if (runs.length === 0)
27372
28155
  return;
27373
- await Promise.allSettled(Array.from(inFlightRecomp.values()));
28156
+ await Promise.allSettled(runs);
28157
+ }
28158
+ function abortInFlightRecomps(sessionId) {
28159
+ inFlightRecomp.get(sessionId)?.controller.abort();
27374
28160
  }
27375
28161
  function spawnPiRecompRun(args) {
27376
28162
  const { sessionId, provider, onStatusChange, work } = args;
28163
+ const controller = new AbortController;
27377
28164
  const unregister = setRawMessageProvider(sessionId, provider);
27378
28165
  setMagicContextRecompActive(sessionId, true);
27379
- onStatusChange();
27380
- const runPromise = (async () => {
28166
+ let run;
28167
+ const runPromise = Promise.resolve().then(async () => {
27381
28168
  try {
27382
- await work();
28169
+ await work(controller.signal);
27383
28170
  } catch (err) {
27384
- sessionLog(sessionId, `pi recomp run failed (detached): ${err instanceof Error ? err.message : String(err)}`);
28171
+ if (!controller.signal.aborted) {
28172
+ sessionLog(sessionId, `pi recomp run failed (detached): ${err instanceof Error ? err.message : String(err)}`);
28173
+ }
28174
+ }
28175
+ }).finally(() => {
28176
+ if (inFlightRecomp.get(sessionId) === run) {
28177
+ inFlightRecomp.delete(sessionId);
27385
28178
  }
27386
- })().finally(() => {
27387
- inFlightRecomp.delete(sessionId);
27388
28179
  setMagicContextRecompActive(sessionId, false);
27389
28180
  unregister();
27390
- onStatusChange();
28181
+ if (!controller.signal.aborted)
28182
+ onStatusChange();
27391
28183
  });
27392
- inFlightRecomp.set(sessionId, runPromise);
28184
+ run = { promise: runPromise, controller };
28185
+ inFlightRecomp.set(sessionId, run);
28186
+ onStatusChange();
27393
28187
  }
27394
28188
 
27395
28189
  // src/commands/ctx-recomp.ts
@@ -27406,9 +28200,10 @@ function registerCtxRecompCommand(pi, deps) {
27406
28200
  pi.registerCommand("ctx-recomp", {
27407
28201
  description: "Rebuild Magic Context compartments from raw Pi session history",
27408
28202
  handler: async (args, ctx) => {
28203
+ const sendStatus = createCtxStatusSender(pi, ctx);
27409
28204
  const sessionId = resolveSessionId(ctx);
27410
28205
  if (!sessionId) {
27411
- sendCtxStatusMessage(pi, {
28206
+ sendStatus({
27412
28207
  title: "/ctx-recomp",
27413
28208
  text: `## Magic Recomp
27414
28209
 
@@ -27419,7 +28214,7 @@ No active Pi session is available.`,
27419
28214
  }
27420
28215
  const currentDeps = deps.resolveRuntimeDeps?.(ctx) ?? deps;
27421
28216
  if (currentDeps.compactionOff) {
27422
- sendCtxStatusMessage(pi, {
28217
+ sendStatus({
27423
28218
  title: "/ctx-recomp",
27424
28219
  text: COMPACTION_OFF_COMMAND_UNAVAILABLE,
27425
28220
  level: "warning"
@@ -27428,7 +28223,7 @@ No active Pi session is available.`,
27428
28223
  }
27429
28224
  const parsed = parseRecompArgs(args);
27430
28225
  if (parsed.kind === "error") {
27431
- sendCtxStatusMessage(pi, {
28226
+ sendStatus({
27432
28227
  title: "/ctx-recomp",
27433
28228
  text: `## Magic Recomp — Invalid Arguments
27434
28229
 
@@ -27438,15 +28233,16 @@ ${parsed.message}`,
27438
28233
  return;
27439
28234
  }
27440
28235
  if (parsed.kind === "upgrade") {
27441
- sendCtxStatusMessage(pi, {
28236
+ sendStatus({
27442
28237
  title: "/ctx-recomp",
27443
28238
  text: executeRecompUpgradeStub(currentDeps.db, sessionId),
27444
- level: "info"
28239
+ level: "info",
28240
+ rpcDisplay: "dialog"
27445
28241
  });
27446
28242
  return;
27447
28243
  }
27448
28244
  if (!currentDeps.historianModel) {
27449
- sendCtxStatusMessage(pi, {
28245
+ sendStatus({
27450
28246
  title: "/ctx-recomp",
27451
28247
  text: "## Magic Recomp\n\n/ctx-recomp is unavailable because `historian.model` is not configured.",
27452
28248
  level: "error"
@@ -27463,7 +28259,7 @@ ${parsed.message}`,
27463
28259
  confirmationBySession.delete(sessionId);
27464
28260
  else
27465
28261
  confirmationBySession.set(sessionId, { timestamp: now, argsKey });
27466
- sendCtxStatusMessage(pi, {
28262
+ sendStatus({
27467
28263
  title: "/ctx-recomp",
27468
28264
  text: warning.text,
27469
28265
  level: warning.confirmable ? "warning" : "error"
@@ -27471,7 +28267,7 @@ ${parsed.message}`,
27471
28267
  return;
27472
28268
  }
27473
28269
  if (isWrapupInProgress(currentDeps.db, sessionId)) {
27474
- sendCtxStatusMessage(pi, {
28270
+ sendStatus({
27475
28271
  title: "/ctx-recomp",
27476
28272
  text: "## Magic Recomp\n\n/ctx-wrapup is already compacting this session. Wait for it to finish, then try `/ctx-recomp` again.",
27477
28273
  level: "warning"
@@ -27479,7 +28275,7 @@ ${parsed.message}`,
27479
28275
  return;
27480
28276
  }
27481
28277
  if (isPiRecompInFlight(sessionId)) {
27482
- sendCtxStatusMessage(pi, {
28278
+ sendStatus({
27483
28279
  title: "/ctx-recomp",
27484
28280
  text: `## Magic Recomp
27485
28281
 
@@ -27489,7 +28285,7 @@ A recomp or upgrade is already running for this session in the background. Wait
27489
28285
  return;
27490
28286
  }
27491
28287
  confirmationBySession.delete(sessionId);
27492
- sendCtxStatusMessage(pi, {
28288
+ sendStatus({
27493
28289
  title: "/ctx-recomp",
27494
28290
  text: parsed.kind === "partial" ? `## Magic Recomp
27495
28291
 
@@ -27498,17 +28294,21 @@ Partial recomp started for range ${parsed.range.start}-${parsed.range.end}.` : `
27498
28294
  Historian recomp started. Rebuilding compartments and facts from raw Pi session history now.`,
27499
28295
  level: "info"
27500
28296
  });
28297
+ const cwd = ctx.cwd;
28298
+ const fallbackModelId = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
28299
+ const snapshot = readPiSessionSnapshot(ctx);
27501
28300
  const provider = {
27502
- readMessages: () => readPiSessionMessages(ctx)
28301
+ readMessages: () => snapshot.rawMessages
27503
28302
  };
27504
28303
  spawnPiRecompRun({
27505
28304
  sessionId,
27506
28305
  provider,
27507
28306
  onStatusChange: () => updateStatusLine(ctx, {
27508
28307
  db: currentDeps.db,
27509
- projectIdentity: ctx.cwd
28308
+ projectIdentity: cwd
27510
28309
  }),
27511
- work: async () => {
28310
+ work: async (signal) => {
28311
+ const detachedSendStatus = createCtxStatusSender(pi, ctx, signal);
27512
28312
  const result = await executeContextRecompWithResult({
27513
28313
  client: createPiHistorianClient({
27514
28314
  runner: currentDeps.runner,
@@ -27517,10 +28317,11 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
27517
28317
  fallbackModels: currentDeps.historianFallbacks,
27518
28318
  timeoutMs: currentDeps.historianTimeoutMs,
27519
28319
  thinkingLevel: currentDeps.historianThinkingLevel,
27520
- directory: ctx.cwd,
28320
+ directory: cwd,
27521
28321
  accountingSessionId: sessionId,
28322
+ signal,
27522
28323
  notify: (text) => {
27523
- sendCtxStatusMessage(pi, {
28324
+ detachedSendStatus({
27524
28325
  title: "/ctx-recomp",
27525
28326
  text,
27526
28327
  level: inferLevel(text)
@@ -27530,15 +28331,17 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
27530
28331
  db: currentDeps.db,
27531
28332
  sessionId,
27532
28333
  historianChunkTokens: currentDeps.historianChunkTokens,
27533
- directory: ctx.cwd,
28334
+ directory: cwd,
27534
28335
  historianTimeoutMs: currentDeps.historianTimeoutMs,
27535
28336
  memoryEnabled: currentDeps.memoryEnabled,
27536
28337
  autoPromote: currentDeps.autoPromote,
27537
28338
  ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
27538
28339
  fallbackModels: currentDeps.historianFallbacks,
27539
28340
  language: currentDeps.language,
27540
- fallbackModelId: ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined
28341
+ fallbackModelId
27541
28342
  }, parsed.kind === "partial" ? { range: parsed.range } : {});
28343
+ if (signal.aborted)
28344
+ return;
27542
28345
  if (result.published) {
27543
28346
  try {
27544
28347
  clearEmergencyRecovery(currentDeps.db, sessionId);
@@ -27546,14 +28349,18 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
27546
28349
  sessionLog(sessionId, `/ctx-recomp: clearEmergencyRecovery failed (continuing): ${describeError(recoveryError).brief}`);
27547
28350
  }
27548
28351
  try {
27549
- stagePiRecompMarker({ db: currentDeps.db, sessionId, ctx });
28352
+ stagePiRecompMarker({
28353
+ db: currentDeps.db,
28354
+ sessionId,
28355
+ branchEntries: snapshot.branchEntries
28356
+ });
27550
28357
  } catch (markerError) {
27551
28358
  sessionLog(sessionId, `/ctx-recomp: marker staging failed (recomp already published; continuing): ${describeError(markerError).brief}`);
27552
28359
  }
27553
28360
  signalPiDeferredHistoryRefresh(sessionId);
27554
28361
  signalPiDeferredMaterialization(sessionId);
27555
28362
  }
27556
- sendCtxStatusMessage(pi, {
28363
+ detachedSendStatus({
27557
28364
  title: "/ctx-recomp",
27558
28365
  text: result.message,
27559
28366
  level: inferLevel(result.message)
@@ -27785,7 +28592,13 @@ function contextualizeUpgradeReason(reason) {
27785
28592
 
27786
28593
  // src/pi-memory-migration.ts
27787
28594
  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.";
28595
+ var CANCELLED_OUTCOME = {
28596
+ ran: false,
28597
+ summary: "Memory migration cancelled during session shutdown."
28598
+ };
27788
28599
  async function runPiMemoryMigration(deps) {
28600
+ if (deps.signal?.aborted)
28601
+ return CANCELLED_OUTCOME;
27789
28602
  const projectPath = resolveProjectIdentityForSession(deps.directory, deps.allowHomeProject);
27790
28603
  if (!projectPath) {
27791
28604
  return {
@@ -27823,6 +28636,8 @@ async function runPiMemoryMigration(deps) {
27823
28636
  let parsed = null;
27824
28637
  let lastFailReason = "no output";
27825
28638
  for (let i = 0;i < modelChain.length; i += 1) {
28639
+ if (deps.signal?.aborted)
28640
+ return CANCELLED_OUTCOME;
27826
28641
  const model = modelChain[i];
27827
28642
  if (i > 0) {
27828
28643
  sessionLog(deps.sessionId, `memory-migration: escalating to configured fallback model ${model.model} (${i}/${modelChain.length - 1})`);
@@ -27837,8 +28652,11 @@ async function runPiMemoryMigration(deps) {
27837
28652
  cwd: deps.directory,
27838
28653
  thinkingLevel: model.qualifier,
27839
28654
  accountingSessionId: deps.sessionId,
27840
- accountingSubagent: "recomp"
28655
+ accountingSubagent: "recomp",
28656
+ signal: deps.signal
27841
28657
  });
28658
+ if (deps.signal?.aborted)
28659
+ return CANCELLED_OUTCOME;
27842
28660
  if (!result.ok) {
27843
28661
  lastFailReason = `historian ${result.reason}`;
27844
28662
  continue;
@@ -27869,6 +28687,8 @@ async function runPiMemoryMigration(deps) {
27869
28687
  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
28688
  };
27871
28689
  }
28690
+ if (deps.signal?.aborted)
28691
+ return CANCELLED_OUTCOME;
27872
28692
  let routed = 0;
27873
28693
  if (deps.userMemoriesEnabled && parsed.userObservations.length > 0) {
27874
28694
  insertUserMemoryCandidates(deps.db, parsed.userObservations.map((content) => ({
@@ -27893,9 +28713,10 @@ function registerCtxSessionUpgradeCommand(pi, deps) {
27893
28713
  pi.registerCommand("ctx-session-upgrade", {
27894
28714
  description: "Upgrade this session to the current Magic Context history format and re-organize project memories",
27895
28715
  handler: async (_args, ctx) => {
28716
+ const sendStatus = createCtxStatusSender(pi, ctx);
27896
28717
  const sessionId = resolveSessionId(ctx);
27897
28718
  if (!sessionId) {
27898
- sendCtxStatusMessage(pi, {
28719
+ sendStatus({
27899
28720
  title: "/ctx-session-upgrade",
27900
28721
  text: `## Session Upgrade
27901
28722
 
@@ -27906,7 +28727,7 @@ No active Pi session is available.`,
27906
28727
  }
27907
28728
  const currentDeps = deps.resolveRuntimeDeps?.(ctx) ?? deps;
27908
28729
  if (currentDeps.compactionOff) {
27909
- sendCtxStatusMessage(pi, {
28730
+ sendStatus({
27910
28731
  title: "/ctx-session-upgrade",
27911
28732
  text: COMPACTION_OFF_COMMAND_UNAVAILABLE,
27912
28733
  level: "warning"
@@ -27914,7 +28735,7 @@ No active Pi session is available.`,
27914
28735
  return;
27915
28736
  }
27916
28737
  if (!currentDeps.historianModel) {
27917
- sendCtxStatusMessage(pi, {
28738
+ sendStatus({
27918
28739
  title: "/ctx-session-upgrade",
27919
28740
  text: "## Session Upgrade\n\nUnavailable because `historian.model` is not configured.",
27920
28741
  level: "error"
@@ -27922,7 +28743,7 @@ No active Pi session is available.`,
27922
28743
  return;
27923
28744
  }
27924
28745
  if (isWrapupInProgress(currentDeps.db, sessionId)) {
27925
- sendCtxStatusMessage(pi, {
28746
+ sendStatus({
27926
28747
  title: "/ctx-session-upgrade",
27927
28748
  text: `## Session Upgrade
27928
28749
 
@@ -27932,7 +28753,7 @@ No active Pi session is available.`,
27932
28753
  return;
27933
28754
  }
27934
28755
  if (isPiRecompInFlight(sessionId)) {
27935
- sendCtxStatusMessage(pi, {
28756
+ sendStatus({
27936
28757
  title: "/ctx-session-upgrade",
27937
28758
  text: `## Session Upgrade
27938
28759
 
@@ -27943,9 +28764,10 @@ An upgrade or recomp is already running for this session in the background. Wait
27943
28764
  }
27944
28765
  const compartments = getCompartments(currentDeps.db, sessionId);
27945
28766
  const upgradableCount = compartments.filter((c) => c.legacy === 1 || !c.p1 || c.p1.trim() === "").length;
28767
+ const cwd = ctx.cwd;
27946
28768
  const sessionMainModel = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
27947
28769
  const migrationEnabled = currentDeps.memoryEnabled;
27948
- const runMigration = async () => {
28770
+ const runMigration = async (signal) => {
27949
28771
  if (!migrationEnabled) {
27950
28772
  return "Memory migration skipped (memory disabled).";
27951
28773
  }
@@ -27958,24 +28780,27 @@ An upgrade or recomp is already running for this session in the background. Wait
27958
28780
  fallbackModels: currentDeps.historianFallbacks,
27959
28781
  timeoutMs: currentDeps.historianTimeoutMs,
27960
28782
  thinkingLevel: currentDeps.historianThinkingLevel,
27961
- directory: ctx.cwd,
28783
+ directory: cwd,
27962
28784
  allowHomeProject: currentDeps.allowHomeProject,
27963
28785
  sessionId,
27964
28786
  userMemoriesEnabled: currentDeps.userMemoriesEnabled,
27965
- language: currentDeps.language
28787
+ language: currentDeps.language,
28788
+ signal
27966
28789
  });
27967
28790
  return outcome.summary;
27968
28791
  } catch (error) {
28792
+ if (signal.aborted)
28793
+ return "Memory migration cancelled.";
27969
28794
  return `Memory migration skipped (error): ${describeError(error).brief}`;
27970
28795
  }
27971
28796
  };
27972
28797
  if (upgradableCount === 0) {
27973
- const projectPath = resolveProjectIdentityForSession(ctx.cwd, currentDeps.allowHomeProject);
28798
+ const projectPath = resolveProjectIdentityForSession(cwd, currentDeps.allowHomeProject);
27974
28799
  if (!projectPath)
27975
28800
  return;
27976
28801
  const migrationPending = migrationEnabled && !isMemoryMigrationDone(currentDeps.db, projectPath);
27977
28802
  if (!migrationPending) {
27978
- sendCtxStatusMessage(pi, {
28803
+ sendStatus({
27979
28804
  title: "/ctx-session-upgrade",
27980
28805
  text: [
27981
28806
  "## Session Upgrade — Already Up To Date",
@@ -27983,56 +28808,64 @@ An upgrade or recomp is already running for this session in the background. Wait
27983
28808
  compartments.length === 0 ? "This session has no compartment history to upgrade yet." : "This session's compartments are already in the current format."
27984
28809
  ].join(`
27985
28810
  `),
27986
- level: "info"
28811
+ level: "info",
28812
+ rpcDisplay: "dialog"
27987
28813
  });
27988
28814
  return;
27989
28815
  }
27990
- sendCtxStatusMessage(pi, {
28816
+ sendStatus({
27991
28817
  title: "/ctx-session-upgrade",
27992
28818
  text: `## Session Upgrade
27993
28819
 
27994
28820
  Compartments are already current. Re-organizing project memories. This may take a while.`,
27995
28821
  level: "info"
27996
28822
  });
28823
+ const snapshot2 = readPiSessionSnapshot(ctx);
27997
28824
  spawnPiRecompRun({
27998
28825
  sessionId,
27999
28826
  provider: {
28000
- readMessages: () => readPiSessionMessages(ctx)
28827
+ readMessages: () => snapshot2.rawMessages
28001
28828
  },
28002
28829
  onStatusChange: () => updateStatusLine(ctx, {
28003
28830
  db: currentDeps.db,
28004
- projectIdentity: ctx.cwd
28831
+ projectIdentity: cwd
28005
28832
  }),
28006
- work: async () => {
28007
- const summary = await runMigration();
28008
- sendCtxStatusMessage(pi, {
28833
+ work: async (signal) => {
28834
+ const detachedSendStatus = createCtxStatusSender(pi, ctx, signal);
28835
+ const summary = await runMigration(signal);
28836
+ if (signal.aborted)
28837
+ return;
28838
+ detachedSendStatus({
28009
28839
  title: "/ctx-session-upgrade",
28010
28840
  text: ["## Session Upgrade — Complete", "", summary].join(`
28011
28841
  `),
28012
- level: "info"
28842
+ level: "info",
28843
+ rpcDisplay: "dialog"
28013
28844
  });
28014
28845
  }
28015
28846
  });
28016
28847
  return;
28017
28848
  }
28018
- sendCtxStatusMessage(pi, {
28849
+ sendStatus({
28019
28850
  title: "/ctx-session-upgrade",
28020
28851
  text: `## Session Upgrade
28021
28852
 
28022
28853
  Rebuilding compartments into the v2 format and re-organizing project memories. This may take a while.`,
28023
28854
  level: "info"
28024
28855
  });
28856
+ const snapshot = readPiSessionSnapshot(ctx);
28025
28857
  const provider = {
28026
- readMessages: () => readPiSessionMessages(ctx)
28858
+ readMessages: () => snapshot.rawMessages
28027
28859
  };
28028
28860
  spawnPiRecompRun({
28029
28861
  sessionId,
28030
28862
  provider,
28031
28863
  onStatusChange: () => updateStatusLine(ctx, {
28032
28864
  db: currentDeps.db,
28033
- projectIdentity: ctx.cwd
28865
+ projectIdentity: cwd
28034
28866
  }),
28035
- work: async () => {
28867
+ work: async (signal) => {
28868
+ const detachedSendStatus = createCtxStatusSender(pi, ctx, signal);
28036
28869
  const recompResult = await executeContextRecompWithResult({
28037
28870
  client: createPiHistorianClient({
28038
28871
  runner: currentDeps.runner,
@@ -28040,10 +28873,11 @@ Rebuilding compartments into the v2 format and re-organizing project memories. T
28040
28873
  fallbackModels: currentDeps.historianFallbacks,
28041
28874
  timeoutMs: currentDeps.historianTimeoutMs,
28042
28875
  thinkingLevel: currentDeps.historianThinkingLevel,
28043
- directory: ctx.cwd,
28876
+ directory: cwd,
28044
28877
  accountingSessionId: sessionId,
28878
+ signal,
28045
28879
  systemPrompt: withContentLanguageDirective(COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT, currentDeps.language, { preserveUserQuotes: true }),
28046
- notify: (text) => sendCtxStatusMessage(pi, {
28880
+ notify: (text) => detachedSendStatus({
28047
28881
  title: "/ctx-session-upgrade",
28048
28882
  text,
28049
28883
  level: "info"
@@ -28052,7 +28886,7 @@ Rebuilding compartments into the v2 format and re-organizing project memories. T
28052
28886
  db: currentDeps.db,
28053
28887
  sessionId,
28054
28888
  historianChunkTokens: currentDeps.historianChunkTokens,
28055
- directory: ctx.cwd,
28889
+ directory: cwd,
28056
28890
  historianTimeoutMs: currentDeps.historianTimeoutMs,
28057
28891
  memoryEnabled: currentDeps.memoryEnabled,
28058
28892
  autoPromote: currentDeps.autoPromote,
@@ -28061,9 +28895,11 @@ Rebuilding compartments into the v2 format and re-organizing project memories. T
28061
28895
  fallbackModelId: sessionMainModel,
28062
28896
  language: currentDeps.language
28063
28897
  }, {});
28898
+ if (signal.aborted)
28899
+ return;
28064
28900
  if (!recompResult.published || !isRecompComplete(recompResult.message)) {
28065
28901
  const reason = contextualizeUpgradeReason(isRecompFailure(recompResult.message) ? extractRecompReason(recompResult.message) : `Compartments were not fully rebuilt: ${extractRecompReason(recompResult.message)}`);
28066
- sendCtxStatusMessage(pi, {
28902
+ detachedSendStatus({
28067
28903
  title: "/ctx-session-upgrade",
28068
28904
  text: `## Session Upgrade — Incomplete
28069
28905
 
@@ -28073,14 +28909,20 @@ ${reason}`,
28073
28909
  return;
28074
28910
  }
28075
28911
  try {
28076
- stagePiRecompMarker({ db: currentDeps.db, sessionId, ctx });
28912
+ stagePiRecompMarker({
28913
+ db: currentDeps.db,
28914
+ sessionId,
28915
+ branchEntries: snapshot.branchEntries
28916
+ });
28077
28917
  } catch (markerError) {
28078
28918
  sessionLog(sessionId, `pi /ctx-session-upgrade marker staging failed (non-fatal, recomp already published): ${describeError(markerError).brief}`);
28079
28919
  }
28080
28920
  signalPiDeferredHistoryRefresh(sessionId);
28081
28921
  signalPiDeferredMaterialization(sessionId);
28082
- const migrationSummary = await runMigration();
28083
- sendCtxStatusMessage(pi, {
28922
+ const migrationSummary = await runMigration(signal);
28923
+ if (signal.aborted)
28924
+ return;
28925
+ detachedSendStatus({
28084
28926
  title: "/ctx-session-upgrade",
28085
28927
  text: [
28086
28928
  "## Session Upgrade — Complete",
@@ -28092,7 +28934,8 @@ ${migrationSummary}` : "",
28092
28934
  recompResult.message
28093
28935
  ].join(`
28094
28936
  `),
28095
- level: "info"
28937
+ level: "info",
28938
+ rpcDisplay: "dialog"
28096
28939
  });
28097
28940
  }
28098
28941
  });
@@ -28118,6 +28961,9 @@ function formatThresholdClampNote(opts) {
28118
28961
  return ` [clamped: ${opts.configuredValue}% > ${opts.maxPercentage}%]`;
28119
28962
  }
28120
28963
 
28964
+ // ../plugin/src/shared/rust-mode-status.ts
28965
+ var RUST_MODE_HOST_PATHS_LINE = "Host backends → MODULE: ctx_memory, ctx_note; historian: module-side";
28966
+
28121
28967
  // ../plugin/src/shared/tail-hygiene-status.ts
28122
28968
  function finiteNumber(value, fallback = 0) {
28123
28969
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
@@ -28184,7 +29030,7 @@ function formatExecuteThreshold(detail, contextLimit) {
28184
29030
  }
28185
29031
  return `${percentage}%${clampNote}`;
28186
29032
  }
28187
- function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit, dreamer, windowGeometry, tailHygiene) {
29033
+ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit, dreamer, windowGeometry, tailHygiene, contextUsage, rustMode = false) {
28188
29034
  const thresholdDetail = resolveExecuteThresholdDetail(executeThresholdPercentageConfig, liveModelKey, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, {
28189
29035
  tokensConfig: executeThresholdTokens,
28190
29036
  contextLimit,
@@ -28209,7 +29055,9 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
28209
29055
  const remainingMs = Math.max(0, ttlMs - elapsed);
28210
29056
  const cacheExpired = remainingMs === 0 && meta.lastResponseTime > 0;
28211
29057
  const proactiveCompartmentTrigger = getProactiveCompartmentTriggerPercentage(executeThresholdPercentage);
28212
- const displayContextLimit = contextLimit && contextLimit > 0 ? contextLimit : meta.lastContextPercentage > 0 ? Math.round(meta.lastInputTokens / (meta.lastContextPercentage / 100)) : 0;
29058
+ const displayInputTokens = contextUsage?.inputTokens ?? meta.lastInputTokens;
29059
+ const displayPercentage = contextUsage?.percentage ?? meta.lastContextPercentage;
29060
+ const displayContextLimit = contextLimit && contextLimit > 0 ? contextLimit : displayPercentage > 0 ? Math.round(displayInputTokens / (displayPercentage / 100)) : 0;
28213
29061
  const lines = [
28214
29062
  "## Magic Status",
28215
29063
  "",
@@ -28234,11 +29082,15 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
28234
29082
  "",
28235
29083
  "### Execute Threshold",
28236
29084
  `- Execute threshold: ${formatExecuteThreshold(thresholdDetail, displayContextLimit)}`,
28237
- `- Last input tokens: ${meta.lastInputTokens.toLocaleString()} tokens`,
29085
+ `- Last input tokens: ${displayInputTokens.toLocaleString()} tokens`,
28238
29086
  "",
28239
29087
  `**Protected tags:** ${protectedTags}`,
28240
29088
  `**Subagent session:** ${meta.isSubagent}`
28241
29089
  ];
29090
+ const storage = getMagicContextStorageResolution();
29091
+ lines.push("", `**Storage:** ${storage.path} (${storage.source})`);
29092
+ if (rustMode)
29093
+ lines.push("", "### Rust Mode", `- ${RUST_MODE_HOST_PATHS_LINE}`);
28242
29094
  if (tailHygiene !== undefined) {
28243
29095
  lines.push("", "### Tail Hygiene", `- Reclaimable / eligible: ${formatTailHygiene(tailHygiene)}`, "- Reasoning is excluded from both terms.");
28244
29096
  }
@@ -28247,8 +29099,8 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
28247
29099
  `- Running: ${dreamer.progress.task} — ${dreamer.progress.processed}/${dreamer.progress.total} processed`
28248
29100
  ] : [], ...formatDreamTaskBacklogs(dreamer.backlog).split("\\n"));
28249
29101
  }
28250
- if (meta.lastContextPercentage > 0 || meta.lastInputTokens > 0) {
28251
- lines.push("", "### Context Usage", `- Last percentage: ${meta.lastContextPercentage.toFixed(1)}%`, `- Last input tokens: ${meta.lastInputTokens.toLocaleString()}`, `- Resolved context limit: ${displayContextLimit > 0 ? displayContextLimit.toLocaleString() : "unknown"}`, ...windowGeometry ? [`- ${formatWindowDerivationLine(meta.lastInputTokens, 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`);
29102
+ if (displayPercentage > 0 || displayInputTokens > 0) {
29103
+ 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
29104
  }
28253
29105
  const compartments = getCompartments(db, sessionId);
28254
29106
  let historyBlockTokens = 0;
@@ -28285,7 +29137,7 @@ ${c.content}
28285
29137
 
28286
29138
  // src/dialogs/status-dialog.ts
28287
29139
  import {
28288
- matchesKey,
29140
+ matchesKey as matchesKey2,
28289
29141
  truncateToWidth,
28290
29142
  visibleWidth
28291
29143
  } from "@earendil-works/pi-tui";
@@ -28349,7 +29201,7 @@ ${c.content}
28349
29201
  // package.json
28350
29202
  var package_default = {
28351
29203
  name: "@cortexkit/pi-magic-context",
28352
- version: "0.40.1",
29204
+ version: "0.41.1",
28353
29205
  type: "module",
28354
29206
  description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
28355
29207
  main: "dist/index.js",
@@ -28377,7 +29229,7 @@ var package_default = {
28377
29229
  "README.md"
28378
29230
  ],
28379
29231
  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",
29232
+ build: "tsc -p ../retina-local-fs/tsconfig.build.json && rm -f dist/index.js dist/subagent-entry.js dist/historian-calibration-extension.js dist/transformers-web.js dist/*-*.js && bun build ../plugin/src/features/magic-context/memory/transformers-web-entry.ts --outfile dist/transformers-web.js --target browser --format esm --external onnxruntime-web && 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 onnxruntime-node --external onnxruntime-web --external sharp --external node:sqlite",
28381
29233
  "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
29234
  typecheck: "tsc -p ../retina-local-fs/tsconfig.build.json && tsc --noEmit",
28383
29235
  test: "bun install --frozen-lockfile && bun test",
@@ -28390,16 +29242,21 @@ var package_default = {
28390
29242
  },
28391
29243
  dependencies: {
28392
29244
  "@cortexkit/subc-client": "0.4.1",
28393
- "@huggingface/transformers": "^4.1.0",
28394
29245
  "@jitl/quickjs-singlefile-cjs-release-asyncify": "0.32.0",
28395
29246
  "ai-tokenizer": "^1.0.6",
28396
29247
  "comment-json": "^5.0.0",
29248
+ "onnxruntime-web": "1.26.0-dev.20260416-b7804b056c",
28397
29249
  "quickjs-emscripten": "^0.32.0",
28398
29250
  typebox: "^1.3.1",
28399
29251
  zod: "^4.1.8"
28400
29252
  },
29253
+ optionalDependencies: {
29254
+ "onnxruntime-node": "1.24.3",
29255
+ sharp: "^0.34.5"
29256
+ },
28401
29257
  devDependencies: {
28402
29258
  "@biomejs/biome": "^2.5.1",
29259
+ "@huggingface/transformers": "^4.1.0",
28403
29260
  "@earendil-works/pi-ai": "0.83.0",
28404
29261
  "@earendil-works/pi-coding-agent": "0.83.0",
28405
29262
  "@earendil-works/pi-tui": "0.83.0",
@@ -28485,7 +29342,7 @@ class StatusDialogComponent {
28485
29342
  }, REFRESH_INTERVAL_MS);
28486
29343
  }
28487
29344
  handleInput(data) {
28488
- if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c") || matchesKey(data, "return")) {
29345
+ if (matchesKey2(data, "escape") || matchesKey2(data, "ctrl+c") || matchesKey2(data, "return")) {
28489
29346
  this.close();
28490
29347
  }
28491
29348
  }
@@ -28586,8 +29443,6 @@ function drawBorder(inner, width, theme) {
28586
29443
  function buildPiStatusDetail(pi, ctx, deps, sessionId) {
28587
29444
  const usage = ctx.getContextUsage?.();
28588
29445
  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
29446
  let detectedContextLimit;
28592
29447
  try {
28593
29448
  const detected = getOverflowState(deps.db, sessionId).detectedContextLimit;
@@ -28601,8 +29456,15 @@ function buildPiStatusDetail(pi, ctx, deps, sessionId) {
28601
29456
  persistedInputTokens: meta.lastInputTokens,
28602
29457
  persistedPercentage: meta.lastContextPercentage
28603
29458
  });
28604
- const contextLimit = windowGeometry?.usableSoft ?? 0;
28605
- const usagePercentage = contextLimit > 0 && inputTokens > 0 ? inputTokens / contextLimit * 100 : meta.lastContextPercentage;
29459
+ const pressure = resolvePiPressureSnapshot({
29460
+ persistedPercentage: meta.lastContextPercentage,
29461
+ persistedInputTokens: meta.lastInputTokens,
29462
+ liveInputTokens: usage?.tokens,
29463
+ usableContextLimit: windowGeometry?.usableSoft
29464
+ });
29465
+ const inputTokens = pressure.inputTokens;
29466
+ const contextLimit = pressure.contextLimit ?? 0;
29467
+ const usagePercentage = pressure.percentage;
28606
29468
  const compartments = getCompartments(deps.db, sessionId);
28607
29469
  const metaRow = readSessionMetaRow(deps.db, sessionId);
28608
29470
  const memoryBlockCount = Number(metaRow?.memory_block_count ?? 0);
@@ -28859,12 +29721,13 @@ function registerCtxStatusCommand(pi, deps) {
28859
29721
  pi.registerCommand("ctx-status", {
28860
29722
  description: "Show Magic Context status for the current Pi session",
28861
29723
  handler: async (_args, ctx) => {
29724
+ const sendStatus = createCtxStatusSender(pi, ctx);
28862
29725
  const runtimeDeps = deps.resolveStatusDeps?.(ctx) ?? deps;
28863
29726
  const projectIdentity = runtimeDeps.resolveProject?.(ctx).projectIdentity ?? runtimeDeps.projectIdentity;
28864
29727
  const currentDeps = { ...runtimeDeps, projectIdentity };
28865
29728
  const sessionId = resolveSessionId(ctx);
28866
29729
  if (!sessionId) {
28867
- sendCtxStatusMessage(pi, {
29730
+ sendStatus({
28868
29731
  title: "/ctx-status",
28869
29732
  text: `## Magic Status
28870
29733
 
@@ -28895,20 +29758,30 @@ No active Pi session is available.`,
28895
29758
  persistedPercentage: meta.lastContextPercentage
28896
29759
  });
28897
29760
  const usableContextLimit = windowGeometry?.usableSoft;
29761
+ const pressure = resolvePiPressureSnapshot({
29762
+ persistedPercentage: meta.lastContextPercentage,
29763
+ persistedInputTokens: meta.lastInputTokens,
29764
+ liveInputTokens: usage?.tokens,
29765
+ usableContextLimit
29766
+ });
28898
29767
  const statusText = executeStatus(currentDeps.db, sessionId, currentDeps.protectedTags ?? 20, currentDeps.executeThresholdPercentage, modelKey, currentDeps.historyBudgetPercentage, currentDeps.commitClusterTrigger, currentDeps.executeThresholdTokens, usableContextLimit, {
28899
29768
  backlog: getDreamTaskBacklogs(currentDeps.db, currentDeps.projectIdentity, CANONICAL_DREAM_TASKS)
28900
- }, windowGeometry, resolveTailHygieneStatus(getPiChannel1Baseline(sessionId)));
29769
+ }, windowGeometry, resolveTailHygieneStatus(getPiChannel1Baseline(sessionId)), pressure);
28901
29770
  const details = buildStatusDetails(currentDeps, sessionId);
28902
29771
  const profileStatus = currentDeps.activeProfile ?? "none";
28903
- sendCtxStatusMessage(pi, {
29772
+ const storage = getMagicContextStorageResolution();
29773
+ sendStatus({
28904
29774
  title: "/ctx-status",
28905
29775
  text: `${statusText}
28906
29776
 
28907
- Active profile: ${profileStatus}`,
28908
- level: "info"
29777
+ Active profile: ${profileStatus}
29778
+
29779
+ Storage: ${storage.path} (${storage.source})`,
29780
+ level: "info",
29781
+ rpcDisplay: "dialog"
28909
29782
  }, details);
28910
29783
  } catch (error) {
28911
- sendCtxStatusMessage(pi, {
29784
+ sendStatus({
28912
29785
  title: "/ctx-status",
28913
29786
  text: `## Magic Status — Failed
28914
29787
 
@@ -28996,9 +29869,10 @@ function registerCtxWrapupCommand(pi, deps) {
28996
29869
  pi.registerCommand("ctx-wrapup", {
28997
29870
  description: "Compact older Magic Context history while keeping the newest messages raw",
28998
29871
  handler: async (args, ctx) => {
29872
+ const sendStatus = createCtxStatusSender(pi, ctx);
28999
29873
  const sessionId = resolveSessionId(ctx);
29000
29874
  if (!sessionId) {
29001
- sendCtxStatusMessage(pi, {
29875
+ sendStatus({
29002
29876
  title: "/ctx-wrapup",
29003
29877
  text: `## Magic Wrapup
29004
29878
 
@@ -29009,7 +29883,7 @@ No active Pi session is available.`,
29009
29883
  }
29010
29884
  const currentDeps = deps.resolveRuntimeDeps?.(ctx) ?? deps;
29011
29885
  if (currentDeps.compactionOff) {
29012
- sendCtxStatusMessage(pi, {
29886
+ sendStatus({
29013
29887
  title: "/ctx-wrapup",
29014
29888
  text: COMPACTION_OFF_COMMAND_UNAVAILABLE,
29015
29889
  level: "warning"
@@ -29018,7 +29892,7 @@ No active Pi session is available.`,
29018
29892
  }
29019
29893
  const sessionMeta = getOrCreateSessionMeta(currentDeps.db, sessionId);
29020
29894
  if (sessionMeta.isSubagent) {
29021
- sendCtxStatusMessage(pi, {
29895
+ sendStatus({
29022
29896
  title: "/ctx-wrapup",
29023
29897
  text: `## Magic Wrapup — Skipped
29024
29898
 
@@ -29028,7 +29902,7 @@ No active Pi session is available.`,
29028
29902
  return;
29029
29903
  }
29030
29904
  if (!currentDeps.historianModel) {
29031
- sendCtxStatusMessage(pi, {
29905
+ sendStatus({
29032
29906
  title: "/ctx-wrapup",
29033
29907
  text: "## Magic Wrapup\n\n/ctx-wrapup is unavailable because `historian.model` is not configured.",
29034
29908
  level: "error"
@@ -29037,7 +29911,7 @@ No active Pi session is available.`,
29037
29911
  }
29038
29912
  const parsed = parseWrapupArgs(args);
29039
29913
  if (!parsed.ok) {
29040
- sendCtxStatusMessage(pi, {
29914
+ sendStatus({
29041
29915
  title: "/ctx-wrapup",
29042
29916
  text: `## Magic Wrapup — Invalid Arguments
29043
29917
 
@@ -29047,7 +29921,7 @@ ${parsed.message}`,
29047
29921
  return;
29048
29922
  }
29049
29923
  const result = await runPiWrapup(pi, currentDeps, ctx, sessionId, parsed.messagesToKeep);
29050
- sendCtxStatusMessage(pi, {
29924
+ sendStatus({
29051
29925
  title: "/ctx-wrapup",
29052
29926
  text: result,
29053
29927
  level: result.includes("Failed") || result.includes("Partial") ? "error" : "success"
@@ -29056,6 +29930,7 @@ ${parsed.message}`,
29056
29930
  });
29057
29931
  }
29058
29932
  async function runPiWrapup(pi, deps, ctx, sessionId, messagesToKeep) {
29933
+ const sendStatus = createCtxStatusSender(pi, ctx);
29059
29934
  if (getOrCreateSessionMeta(deps.db, sessionId).isSubagent) {
29060
29935
  return `## Magic Wrapup — Skipped
29061
29936
 
@@ -29135,7 +30010,7 @@ No runnable wrapup boundary is available yet; wrapped up 0 messages into 0 compa
29135
30010
  }
29136
30011
  }, 60000);
29137
30012
  try {
29138
- sendCtxStatusMessage(pi, {
30013
+ sendStatus({
29139
30014
  title: "/ctx-wrapup",
29140
30015
  text: `## Magic Wrapup
29141
30016
 
@@ -29191,7 +30066,7 @@ Eligible history is about ${initialPlan.snapshot.trueRawEligibleTokens.toLocaleS
29191
30066
  failure = `${ownershipLostReason}; wrapped up through message ${lastEnd}. Run /ctx-wrapup again to continue.`;
29192
30067
  break;
29193
30068
  }
29194
- sendCtxStatusMessage(pi, {
30069
+ sendStatus({
29195
30070
  title: "/ctx-wrapup",
29196
30071
  text: `## Magic Wrapup
29197
30072
 
@@ -29246,7 +30121,7 @@ Chunk ${chunkIndex}: wrapping messages ${plan.snapshot.offset}-${plan.snapshot.e
29246
30121
  language: deps.language,
29247
30122
  compartmentLeaseHolderId: leaseHolder,
29248
30123
  readBranchEntries: () => readBranchEntries(ctx),
29249
- notifyIssue: (text) => sendCtxStatusMessage(pi, {
30124
+ notifyIssue: (text) => sendStatus({
29250
30125
  title: "/ctx-wrapup",
29251
30126
  text,
29252
30127
  level: "warning"
@@ -29323,7 +30198,7 @@ async function acquireCompartmentLeaseEventually(db, sessionId, renewWrapupMarke
29323
30198
  return { ok: true, holderId };
29324
30199
  if (!renewWrapupMarker({}))
29325
30200
  return { ok: false, reason: "ownership_lost" };
29326
- await new Promise((resolve2) => setTimeout(resolve2, Math.min(LEASE_WAIT_MS, remainingMs())));
30201
+ await new Promise((resolve3) => setTimeout(resolve3, Math.min(LEASE_WAIT_MS, remainingMs())));
29327
30202
  }
29328
30203
  }
29329
30204
  function estimateChunks(tokens, chunkTokens) {
@@ -29400,42 +30275,6 @@ function registerPiFailClosedSurface(pi, args) {
29400
30275
  log(`${PREFIX} fail-closed blocking surface registered (${args.reason.kind}); primary turns will error until storage recovers or the build is upgraded`);
29401
30276
  }
29402
30277
 
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
30278
  // src/strip-tag-prefix.ts
29440
30279
  function stripTagPrefixFromAssistantMessage(message) {
29441
30280
  if (message.role !== "assistant")
@@ -29461,19 +30300,43 @@ function stripTagPrefixFromAssistantMessage(message) {
29461
30300
 
29462
30301
  // src/index.ts
29463
30302
  var PREFIX2 = "[magic-context][pi]";
29464
- var PI_ACTIVE_LATCH = Symbol.for("magic-context.pi.active");
29465
- function isPiMagicContextActiveInProcess() {
29466
- return globalThis[PI_ACTIVE_LATCH] === true;
30303
+ var PI_CHILD_INIT_CONTEXT = Symbol.for("magic-context.pi.child-init-context");
30304
+ var SUBAGENT_CHILD_SESSION_CREATED = "subagents:child:session-created";
30305
+ var SUBAGENT_CHILD_DISPOSED = "subagents:child:disposed";
30306
+ var PI_STARTUP_MAINTENANCE_SCHEDULED = Symbol.for("magic-context.pi.startup-maintenance-scheduled");
30307
+ function getPiChildInitContext() {
30308
+ const globals = globalThis;
30309
+ const existing = globals[PI_CHILD_INIT_CONTEXT];
30310
+ if (existing instanceof AsyncLocalStorage)
30311
+ return existing;
30312
+ const context = new AsyncLocalStorage;
30313
+ globals[PI_CHILD_INIT_CONTEXT] = context;
30314
+ return context;
29467
30315
  }
29468
- function markPiMagicContextActive() {
29469
- globalThis[PI_ACTIVE_LATCH] = true;
30316
+ function isPiInProcessSubagentInit() {
30317
+ return getPiChildInitContext().getStore() === true;
29470
30318
  }
29471
- function clearPiMagicContextActive() {
29472
- try {
29473
- delete globalThis[PI_ACTIVE_LATCH];
29474
- } catch {
29475
- globalThis[PI_ACTIVE_LATCH] = undefined;
29476
- }
30319
+ function clearPiInProcessSubagentInitContext() {
30320
+ getPiChildInitContext().enterWith(false);
30321
+ }
30322
+ function registerPiSubagentInitContext(pi) {
30323
+ const context = getPiChildInitContext();
30324
+ const unsubscribeCreated = pi.events.on(SUBAGENT_CHILD_SESSION_CREATED, () => context.enterWith(true));
30325
+ const unsubscribeDisposed = pi.events.on(SUBAGENT_CHILD_DISPOSED, () => context.enterWith(false));
30326
+ return () => {
30327
+ unsubscribeCreated();
30328
+ unsubscribeDisposed();
30329
+ };
30330
+ }
30331
+ function claimPiStartupMaintenance() {
30332
+ const globals = globalThis;
30333
+ if (globals[PI_STARTUP_MAINTENANCE_SCHEDULED] === true)
30334
+ return false;
30335
+ globals[PI_STARTUP_MAINTENANCE_SCHEDULED] = true;
30336
+ return true;
30337
+ }
30338
+ function clearPiStartupMaintenanceClaim() {
30339
+ delete globalThis[PI_STARTUP_MAINTENANCE_SCHEDULED];
29477
30340
  }
29478
30341
  function resolveCurrentProject(ctx, allowHomeProject = false) {
29479
30342
  const projectDir = ctx.cwd;
@@ -29587,7 +30450,7 @@ function ensureConfigLocationsMigrated(dir) {
29587
30450
  });
29588
30451
  }
29589
30452
  function logPiConfigLoad(args) {
29590
- const key = resolve2(args.dir);
30453
+ const key = resolve3(args.dir);
29591
30454
  if (args.dedupe && loggedPiConfigDirs.has(key))
29592
30455
  return;
29593
30456
  if (args.dedupe) {
@@ -29607,9 +30470,9 @@ var __test = {
29607
30470
  resetLoggedPiConfigDirs() {
29608
30471
  loggedPiConfigDirs.clear();
29609
30472
  },
29610
- isPiMagicContextActiveInProcess,
29611
- markPiMagicContextActive,
29612
- clearPiMagicContextActive
30473
+ clearPiInProcessSubagentInitContext,
30474
+ claimPiStartupMaintenance,
30475
+ clearPiStartupMaintenanceClaim
29613
30476
  };
29614
30477
  function formatTokens(value) {
29615
30478
  return value.toLocaleString();
@@ -29681,7 +30544,7 @@ async function persistPiPressureFromMessageEnd(args) {
29681
30544
  }
29682
30545
  var PLUGIN_VERSION = (() => {
29683
30546
  try {
29684
- const req = createRequire3(import.meta.url);
30547
+ const req = createRequire2(import.meta.url);
29685
30548
  return req("../package.json").version;
29686
30549
  } catch {
29687
30550
  return "0.0.0";
@@ -29722,6 +30585,8 @@ function resolveHistorianFromConfig(config) {
29722
30585
  fallbackModels,
29723
30586
  historianChunkTokens,
29724
30587
  timeoutMs: config.historian_timeout_ms,
30588
+ temperature: historian?.temperature,
30589
+ maxOutputTokens: historian?.maxTokens ?? 32000,
29725
30590
  twoPass: historian?.two_pass === true,
29726
30591
  thinkingLevel: resolved.primary?.qualifier,
29727
30592
  executeThresholdPercentage: config.execute_threshold_percentage,
@@ -29754,11 +30619,12 @@ async function src_default(pi) {
29754
30619
  log(`${PREFIX2} subagent child detected (${MAGIC_CONTEXT_PI_SUBAGENT_ENV}=1); skipping full extension registration`);
29755
30620
  return;
29756
30621
  }
29757
- if (isPiMagicContextActiveInProcess()) {
29758
- log(`${PREFIX2} in-process re-init detected (Magic Context already active in this process); skipping full extension registration`);
30622
+ if (isPiInProcessSubagentInit()) {
30623
+ log(`${PREFIX2} in-process subagent child detected; skipping full extension registration`);
29759
30624
  return;
29760
30625
  }
29761
- markPiMagicContextActive();
30626
+ const unregisterPiSubagentInitContext = registerPiSubagentInitContext(pi);
30627
+ registerPiSubagentInitContextCleanup(pi, unregisterPiSubagentInitContext);
29762
30628
  beginBootQuietPeriod();
29763
30629
  const bootProjectDir = process.cwd();
29764
30630
  ensureConfigLocationsMigrated(bootProjectDir);
@@ -29769,7 +30635,7 @@ async function src_default(pi) {
29769
30635
  mmapSizeMb: bootConfig.config.sqlite.mmap_size_mb
29770
30636
  });
29771
30637
  const storageDir = getMagicContextStorageDir();
29772
- const dbPath = join5(storageDir, "context.db");
30638
+ const dbPath = join6(storageDir, "context.db");
29773
30639
  let db;
29774
30640
  let openFailureCause = null;
29775
30641
  try {
@@ -29830,27 +30696,44 @@ async function src_default(pi) {
29830
30696
  }
29831
30697
  async function startPiMagicContextRuntime(pi, database, dbPath) {
29832
30698
  const db = database;
29833
- scheduleAfterBootQuiet(() => {
29834
- runDeferredV22Backfill(db).catch((err) => {
29835
- warn(`[v22-backfill] background runner failed: ${err}`);
30699
+ if (claimPiStartupMaintenance()) {
30700
+ scheduleAfterBootQuiet(() => {
30701
+ runDeferredV22Backfill(db).catch((err) => {
30702
+ warn(`[v22-backfill] background runner failed: ${err}`);
30703
+ });
29836
30704
  });
29837
- });
29838
- scheduleAfterBootQuiet(() => {
29839
- (async () => {
29840
- try {
29841
- const api = await loadDefaultPiSessionApi();
29842
- const sessions = await api.listSessions();
29843
- await runSessionProjectBackfill(database, sessions.map((session) => ({
29844
- sessionId: typeof session?.id === "string" ? session.id : "",
29845
- directory: typeof session?.cwd === "string" ? session.cwd : ""
29846
- })));
29847
- } catch (err) {
29848
- warn(`[session-projects] background runner failed: ${err}`);
29849
- }
29850
- })();
29851
- }, 0);
30705
+ scheduleAfterBootQuiet(() => {
30706
+ (async () => {
30707
+ try {
30708
+ let sessions;
30709
+ await runSessionProjectBackfill(database, async (afterSessionId, limit) => {
30710
+ if (!sessions) {
30711
+ const api = await loadDefaultPiSessionApi();
30712
+ const sessionsById2 = new Map;
30713
+ for (const session of await api.listSessions()) {
30714
+ const sessionId = typeof session?.id === "string" ? session.id : "";
30715
+ const directory = typeof session?.cwd === "string" ? session.cwd : "";
30716
+ if (sessionId && (!sessionsById2.has(sessionId) || directory)) {
30717
+ sessionsById2.set(sessionId, { sessionId, directory });
30718
+ }
30719
+ }
30720
+ sessions = [...sessionsById2.values()];
30721
+ }
30722
+ const offset = afterSessionId === null ? 0 : sessions.findIndex((session) => session.sessionId === afterSessionId) + 1;
30723
+ return sessions.slice(offset, offset + limit);
30724
+ });
30725
+ } catch (err) {
30726
+ warn(`[session-projects] background runner failed: ${err}`);
30727
+ }
30728
+ })();
30729
+ }, 0);
30730
+ }
29852
30731
  const projectDir = process.cwd();
29853
30732
  const seenDreamerProjectIdentities = new Set;
30733
+ const dreamerRegistrationOwner = {};
30734
+ const commandLifecycleController = new AbortController;
30735
+ registerCtxStatusLifecycleSignal(pi, commandLifecycleController.signal);
30736
+ let sessionShuttingDown = false;
29854
30737
  ensureConfigLocationsMigrated(projectDir);
29855
30738
  const { config, warnings, loadedFromPaths, registrationPromptSurface } = loadPiConfig({
29856
30739
  cwd: projectDir
@@ -29993,6 +30876,32 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
29993
30876
  }
29994
30877
  const bootProjectDeps = buildProjectDeps(projectDir, projectIdentity, config);
29995
30878
  projectDepsByDir.set(projectDir, bootProjectDeps);
30879
+ function syncDreamerProjectRegistration(current) {
30880
+ if (sessionShuttingDown)
30881
+ return;
30882
+ seenDreamerProjectIdentities.add(current.projectIdentity);
30883
+ if (!current.dreamerConfig) {
30884
+ unregisterPiDreamerProject({
30885
+ projectIdentity: current.projectIdentity,
30886
+ registrationOwner: dreamerRegistrationOwner
30887
+ });
30888
+ return;
30889
+ }
30890
+ registerPiDreamerProject({
30891
+ db,
30892
+ projectDir: current.projectDir,
30893
+ projectIdentity: current.projectIdentity,
30894
+ registrationOwner: dreamerRegistrationOwner,
30895
+ config: current.dreamerConfig,
30896
+ embeddingConfig: current.config.embedding,
30897
+ memoryEnabled: current.config.memory.enabled,
30898
+ retinaHandoff: current.config.smart_notes.retina_handoff,
30899
+ mural: current.config.mural,
30900
+ language: current.config.language,
30901
+ gitCommitIndexing: current.config.memory.git_commit_indexing,
30902
+ onAdjunctsRefreshNeeded: signalPiSystemPromptRefreshForProject
30903
+ });
30904
+ }
29996
30905
  const todowriteEnabled = bootProjectDeps.config.todowrite.enabled !== false;
29997
30906
  const todowriteOverlayEnabled = todowriteEnabled && bootProjectDeps.config.todowrite.overlay !== false;
29998
30907
  registerMagicContextTools(pi, {
@@ -30187,7 +31096,9 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
30187
31096
  },
30188
31097
  dreamerEnabled: bootProjectDeps.dreamerEnabled,
30189
31098
  resolveDreamerEnabled: (ctx) => resolveCurrentProjectDeps(ctx).dreamerEnabled,
30190
- onProjectSeen: (identity) => seenDreamerProjectIdentities.add(identity)
31099
+ onProjectSeen: (identity) => seenDreamerProjectIdentities.add(identity),
31100
+ ensureRegistered: (ctx) => syncDreamerProjectRegistration(resolveCurrentProjectDeps(ctx)),
31101
+ registrationOwner: dreamerRegistrationOwner
30191
31102
  });
30192
31103
  info("registered /ctx-dream");
30193
31104
  registerCtxEmbedCommand(pi, {
@@ -30207,19 +31118,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
30207
31118
  info("registered /ctx-embed");
30208
31119
  const dreamerConfig = bootProjectDeps.dreamerConfig;
30209
31120
  if (dreamerConfig) {
30210
- registerPiDreamerProject({
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
- });
31121
+ syncDreamerProjectRegistration(bootProjectDeps);
30223
31122
  info(`registered dreamer (${summarizeDreamSchedule(dreamerConfig)})`);
30224
31123
  } else {
30225
31124
  info(bootProjectDeps.dreamerEnabled ? "registered dreamer: DISABLED (no dreamer config)" : "registered dreamer: DISABLED (dreamer.disable=true or no dreamer config)");
@@ -30250,33 +31149,10 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
30250
31149
  projectIdentity: effectiveProjectDeps.projectIdentity
30251
31150
  };
30252
31151
  const effectiveConfig = effectiveProjectDeps.config;
30253
- seenDreamerProjectIdentities.add(currentProject.projectIdentity);
30254
- const effectiveDreamerConfig = effectiveProjectDeps.dreamerConfig;
30255
- if (effectiveDreamerConfig) {
30256
- try {
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
- }
31152
+ try {
31153
+ syncDreamerProjectRegistration(effectiveProjectDeps);
31154
+ } catch (err) {
31155
+ warn("before_agent_start: dreamer registration sync threw:", err);
30280
31156
  }
30281
31157
  const sm = ctx.sessionManager;
30282
31158
  let sessionId;
@@ -30538,39 +31414,47 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
30538
31414
  }
30539
31415
  });
30540
31416
  pi.on("session_shutdown", async (_event, ctx) => {
31417
+ sessionShuttingDown = true;
31418
+ commandLifecycleController.abort();
30541
31419
  const SHUTDOWN_DRAIN_MS = 5000;
31420
+ const sessionId = resolveSessionId(ctx);
30542
31421
  try {
30543
- await withTimeout(awaitInFlightHistorians(), SHUTDOWN_DRAIN_MS);
31422
+ for (const identity of seenDreamerProjectIdentities) {
31423
+ unregisterPiDreamerProject({
31424
+ projectIdentity: identity,
31425
+ registrationOwner: dreamerRegistrationOwner
31426
+ });
31427
+ }
30544
31428
  } catch (err) {
30545
- warn("shutdown: historian drain threw:", err);
31429
+ warn("shutdown: unregisterPiDreamerProject threw:", err);
30546
31430
  }
30547
- try {
30548
- await withTimeout(awaitInFlightRecomps(), SHUTDOWN_DRAIN_MS);
30549
- } catch (err) {
30550
- warn("shutdown: recomp drain threw:", err);
31431
+ if (sessionId) {
31432
+ try {
31433
+ await withTimeout(awaitInFlightHistorians(sessionId), SHUTDOWN_DRAIN_MS);
31434
+ } catch (err) {
31435
+ warn("shutdown: historian drain threw:", err);
31436
+ }
31437
+ try {
31438
+ await withTimeout(awaitInFlightRecomps(sessionId), SHUTDOWN_DRAIN_MS);
31439
+ } catch (err) {
31440
+ warn("shutdown: recomp drain threw:", err);
31441
+ }
31442
+ abortInFlightRecomps(sessionId);
30551
31443
  }
30552
31444
  try {
30553
- await withTimeout(awaitInFlightDreamers(), SHUTDOWN_DRAIN_MS);
31445
+ await withTimeout(awaitInFlightDreamers(dreamerRegistrationOwner), SHUTDOWN_DRAIN_MS);
30554
31446
  } catch (err) {
30555
31447
  warn("shutdown: dreamer drain threw:", err);
30556
31448
  }
30557
- try {
30558
- for (const identity of seenDreamerProjectIdentities) {
30559
- unregisterPiDreamerProject({ projectIdentity: identity });
30560
- }
30561
- } catch (err) {
30562
- warn("shutdown: unregisterPiDreamerProject threw:", err);
30563
- }
30564
31449
  try {
30565
31450
  const sm = ctx.sessionManager;
30566
- const sessionId = typeof sm?.getSessionId === "function" ? sm.getSessionId() : undefined;
30567
- if (typeof sessionId === "string" && sessionId.length > 0) {
30568
- clearPiSystemPromptSession(sessionId);
30569
- promptSurfaceGuidanceEpochs.clear(sessionId);
30570
- clearContextHandlerSession(sessionId);
31451
+ const sessionId2 = typeof sm?.getSessionId === "function" ? sm.getSessionId() : undefined;
31452
+ if (typeof sessionId2 === "string" && sessionId2.length > 0) {
31453
+ clearPiSystemPromptSession(sessionId2);
31454
+ promptSurfaceGuidanceEpochs.clear(sessionId2);
31455
+ clearContextHandlerSession(sessionId2);
30571
31456
  }
30572
31457
  } catch {}
30573
- clearPiMagicContextActive();
30574
31458
  });
30575
31459
  pi.on("session_before_switch", (_event, ctx) => {
30576
31460
  try {
@@ -30584,6 +31468,9 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
30584
31468
  } catch {}
30585
31469
  });
30586
31470
  }
31471
+ function registerPiSubagentInitContextCleanup(pi, unsubscribe) {
31472
+ pi.on("session_shutdown", unsubscribe);
31473
+ }
30587
31474
  function formatExecuteThresholdForLog(value) {
30588
31475
  if (value === undefined)
30589
31476
  return "65%";