@cortexkit/pi-magic-context 0.49.12 → 0.49.13

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
@@ -55,12 +55,6 @@ import {
55
55
  advanceToolReclaimWatermark,
56
56
  retryPendingSessionCleanups,
57
57
  retryPendingRustSessionCleanupsForProject,
58
- embedPauseBySession,
59
- embedRunStateBySession,
60
- autoEmbedAttemptedBySession,
61
- autoEmbedIdentityBySession,
62
- invalidateAutoEmbedSession,
63
- getEmbedDrainUiStatus,
64
58
  COMPARTMENT_LEASE_RENEWAL_MS,
65
59
  acquireCompartmentLease,
66
60
  renewCompartmentLease,
@@ -253,7 +247,7 @@ import {
253
247
  registerTodoStateLifecycle,
254
248
  syncCtxMemoryToolEnabled,
255
249
  registerMagicContextTools
256
- } from "./index-zapgkmp7.js";
250
+ } from "./index-aypx5h57.js";
257
251
  import {
258
252
  canonicalModelIdentity,
259
253
  piModelRefToCanonical,
@@ -293,7 +287,7 @@ import {
293
287
  openCurrentDatabase,
294
288
  openDatabaseAsync,
295
289
  closeDatabase
296
- } from "./index-z5ftst3e.js";
290
+ } from "./index-t27xk4r7.js";
297
291
  import {
298
292
  setHomeProjectPermission,
299
293
  ProjectIdentityError,
@@ -626,7 +620,7 @@ import {
626
620
  indexSingleMessage2,
627
621
  indexMessagesAfterOrdinal2,
628
622
  sweepOrphanedOpenCodeMessageIndexes2
629
- } from "./index-6hex51s9.js";
623
+ } from "./index-vv1j5cak.js";
630
624
  import {
631
625
  formatSynapseLaneDescriptor
632
626
  } from "./index-6dz2ed1v.js";
@@ -642,7 +636,7 @@ import {
642
636
  replaceCompartmentChunkEmbeddings,
643
637
  deleteChunkEmbedBackoffForSession,
644
638
  chunkWindowSourceKey
645
- } from "./index-af9nb5db.js";
639
+ } from "./index-7qc73yk5.js";
646
640
  import {
647
641
  recordSessionProjectIdentity,
648
642
  GIT_SWEEP_LEASE_RENEWAL_MS,
@@ -650,7 +644,17 @@ import {
650
644
  renewGitSweepLease,
651
645
  markGitSweepSuccessAndRelease,
652
646
  parkGitSweepNonIndexable,
653
- releaseGitSweepLease,
647
+ releaseGitSweepLease
648
+ } from "./index-1mrvw9en.js";
649
+ import {
650
+ embedPauseBySession,
651
+ embedRunStateBySession,
652
+ activeEmbeddingSessions,
653
+ autoEmbedAttemptedBySession,
654
+ autoEmbedIdentityBySession,
655
+ autoEmbedRetryAfterBySession,
656
+ invalidateAutoEmbedSession,
657
+ getEmbedDrainUiStatus,
654
658
  describeShadowBackfillWriteRefusal2,
655
659
  contentSha2562,
656
660
  drainProjectEmbeddingIdentityMaintenance2,
@@ -667,7 +671,7 @@ import {
667
671
  embedSessionCompartmentChunks2,
668
672
  getEmbeddingCoverageStatus2,
669
673
  getAutoEmbeddingSessionCoverage2
670
- } from "./index-f6e8jsrh.js";
674
+ } from "./index-nwbhe6qv.js";
671
675
  import {
672
676
  rememberV1HiddenRun
673
677
  } from "./index-6x556c2q.js";
@@ -732,7 +736,7 @@ import {
732
736
  normalizeVerificationFiles,
733
737
  indexCommitsForProject,
734
738
  embedUnembeddedCommits
735
- } from "./index-gykw2zhn.js";
739
+ } from "./index-wp9q91kn.js";
736
740
  import {
737
741
  setEmbeddingSessionBusy2,
738
742
  isEmbeddingHostBusy2
@@ -763,18 +767,19 @@ import {
763
767
  import {
764
768
  pushNotification2
765
769
  } from "./index-q1yc6bs0.js";
766
- import"./index-j24916mq.js";
770
+ import"./index-9jvktm7k.js";
767
771
  import {
768
772
  cosineSimilarity
769
- } from "./index-xpsht877.js";
773
+ } from "./index-7wagp5jp.js";
770
774
  import"./index-46q4e104.js";
771
775
  import {
776
+ DREAMER_PRIMER_INVESTIGATOR_ALLOWED_TOOLS,
772
777
  buildHiddenAgentRegistrations,
773
778
  logTemperatureDecision,
774
779
  subagentStepCap,
775
780
  SubagentTelemetry,
776
781
  readHostSystemPrompt
777
- } from "./index-xmdkde4v.js";
782
+ } from "./index-y29y2j2w.js";
778
783
  import"./index-f18ec69t.js";
779
784
  import {
780
785
  __toESM
@@ -3494,7 +3499,7 @@ function startHostStallProfilerSwitch() {
3494
3499
  // package.json
3495
3500
  var package_default = {
3496
3501
  name: "@cortexkit/pi-magic-context",
3497
- version: "0.49.12",
3502
+ version: "0.49.13",
3498
3503
  type: "module",
3499
3504
  description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
3500
3505
  main: "dist/index.js",
@@ -3523,7 +3528,7 @@ var package_default = {
3523
3528
  ],
3524
3529
  "//": "The build intentionally bundles typebox (no --external typebox) and keeps it a regular dependency. OMP's extension loader rewrites every bare `typebox` import to its own omptype-based shim, whose schemas are callable objects rather than JSON Schema; they fail structuredClone during tool registration, so an external typebox import stops Magic Context loading on OMP. src/typebox-bundling.test.ts guards this.",
3525
3530
  scripts: {
3526
- build: "bun ../../scripts/check-bun.mjs && tsc -p ../retina-local-fs/tsconfig.build.json && bun ../../scripts/clean-dist-chunks.mjs dist index.js subagent-entry.js historian-calibration-extension.js embedding-worker-process.js raw-ordinal-warmup-worker.js transformers-web.js transformers-node-wasm.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 ../plugin/scripts/build-transformers-node-wasm.ts dist && bun build src/index.ts src/subagent-entry.ts src/historian-calibration-extension.ts ../plugin/src/features/magic-context/memory/embedding-worker.ts ../plugin/src/features/magic-context/memory/embedding-worker-process.ts ../plugin/src/features/magic-context/migration-worker.ts ../plugin/src/hooks/magic-context/auto-search-worker.ts ../plugin/src/hooks/magic-context/raw-ordinal-warmup-worker.ts ../plugin/src/features/magic-context/compaction-marker-candidates-worker.ts --outdir dist --entry-naming '[name].[ext]' --target node --format esm --splitting --external @earendil-works/pi-coding-agent --external @oh-my-pi/pi-coding-agent --external @earendil-works/pi-tui --external onnxruntime-node --external onnxruntime-web --external sharp --external bun:ffi --external node:sqlite",
3531
+ build: "bun ../../scripts/check-bun.mjs && tsc -p ../retina-local-fs/tsconfig.build.json && bun ../../scripts/clean-dist-chunks.mjs dist index.js subagent-entry.js historian-calibration-extension.js embedding-worker-process.js raw-ordinal-warmup-worker.js transformers-web.js transformers-node-wasm.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 ../plugin/scripts/build-transformers-node-wasm.ts dist && bun build src/index.ts src/subagent-entry.ts src/historian-calibration-extension.ts ../plugin/src/features/magic-context/memory/embedding-worker.ts ../plugin/src/features/magic-context/memory/embedding-worker-process.ts ../plugin/src/features/magic-context/migration-worker.ts ../plugin/src/hooks/magic-context/auto-search-worker.ts ../plugin/src/hooks/magic-context/raw-ordinal-warmup-worker.ts ../plugin/src/features/magic-context/compaction-marker-candidates-worker.ts ../plugin/src/features/magic-context/chunk-embedding-write-worker.ts --outdir dist --entry-naming '[name].[ext]' --target node --format esm --splitting --external @earendil-works/pi-coding-agent --external @oh-my-pi/pi-coding-agent --external @earendil-works/pi-tui --external onnxruntime-node --external onnxruntime-web --external sharp --external bun:ffi --external node:sqlite",
3527
3532
  "build:e2e-argv": `bun ../../scripts/clean-dist-chunks.mjs ../../tests/docker/.generated subagent-runner-e2e.mjs && bun build src/subagent-runner.ts --outdir ../../tests/docker/.generated --entry-naming 'subagent-runner-e2e.mjs' --splitting --target node --format esm --external node:sqlite && node --input-type=module -e 'const { __test } = await import("../../tests/docker/.generated/subagent-runner-e2e.mjs"); if (typeof __test.buildArgs !== "function" || typeof __test.resolvePiInvocation !== "function") throw new Error("argv renderer exports missing"); console.log("Node argv renderer: 2 exports loaded")'`,
3528
3533
  typecheck: "tsc -p ../retina-local-fs/tsconfig.build.json && tsc --noEmit",
3529
3534
  test: "bun ../../scripts/check-bun.mjs && bun install --frozen-lockfile && BUN_JSC_useOMGJIT=0 bun test --parallel=4 --timeout 30000",
@@ -4425,6 +4430,14 @@ var DREAMER_AGENT = "dreamer";
4425
4430
  var DREAMER_RETROSPECTIVE_AGENT = "dreamer-retrospective";
4426
4431
  var DREAMER_PRIMER_INVESTIGATOR_AGENT = "dreamer-primer-investigator";
4427
4432
  var DREAMER_MEMORY_MAPPER_AGENT = "dreamer-memory-mapper";
4433
+ var DREAMER_MEMORY_MAPPER_ALLOWED_TOOLS = [
4434
+ "read",
4435
+ "grep",
4436
+ "glob",
4437
+ "aft_outline",
4438
+ "aft_zoom",
4439
+ "aft_search"
4440
+ ];
4428
4441
  var DREAMER_CLASSIFIER_AGENT = "dreamer-classifier";
4429
4442
  var DREAMER_DOCS_AGENT = "dreamer-docs";
4430
4443
  var DREAMER_REVIEWER_AGENT = "dreamer-reviewer";
@@ -4654,13 +4667,12 @@ function providerOutputFailureFromInvalidManifest(messages, responseText, now =
4654
4667
  var HIDDEN_HISTORIAN_AGENT = "historian";
4655
4668
  var HIDDEN_DREAMER_AGENT = "dreamer-classifier";
4656
4669
  var HIDDEN_CURATE_AGENT = DREAMER_AGENT;
4657
- var READ_TOOLS = ["read", "grep", "glob"];
4658
4670
  var AGENT_TOOLS = {
4659
4671
  [HIDDEN_HISTORIAN_AGENT]: [],
4660
4672
  [HIDDEN_DREAMER_AGENT]: [],
4661
4673
  [HIDDEN_CURATE_AGENT]: ["ctx_memory"],
4662
- [DREAMER_MEMORY_MAPPER_AGENT]: READ_TOOLS,
4663
- [DREAMER_PRIMER_INVESTIGATOR_AGENT]: [...READ_TOOLS, "ctx_search"],
4674
+ [DREAMER_MEMORY_MAPPER_AGENT]: DREAMER_MEMORY_MAPPER_ALLOWED_TOOLS,
4675
+ [DREAMER_PRIMER_INVESTIGATOR_AGENT]: DREAMER_PRIMER_INVESTIGATOR_ALLOWED_TOOLS,
4664
4676
  [DREAMER_RETROSPECTIVE_AGENT]: ["ctx_search"]
4665
4677
  };
4666
4678
  var AGENT_STEPS = {
@@ -4867,6 +4879,21 @@ function externalAbortMessage(signal) {
4867
4879
  const reason = signal.reason;
4868
4880
  return `prompt aborted by external signal${reason instanceof Error && /^lease_(?:lost|expired):/.test(reason.message) ? `: ${reason.message}` : ""}`;
4869
4881
  }
4882
+ function raceWithSignal(work, signal) {
4883
+ work = Promise.resolve(work);
4884
+ if (!signal)
4885
+ return work;
4886
+ return new Promise((resolve, reject) => {
4887
+ const abort = () => reject(new Error(externalAbortMessage(signal)));
4888
+ if (signal.aborted) {
4889
+ work.catch(() => {});
4890
+ abort();
4891
+ return;
4892
+ }
4893
+ signal.addEventListener("abort", abort, { once: true });
4894
+ work.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
4895
+ });
4896
+ }
4870
4897
  async function promptWithTimeout(client, args, timeoutMs, signal, transport) {
4871
4898
  if (signal?.aborted) {
4872
4899
  throw new Error(externalAbortMessage(signal));
@@ -4878,11 +4905,11 @@ async function promptWithTimeout(client, args, timeoutMs, signal, transport) {
4878
4905
  try {
4879
4906
  const request = { ...args, signal: controller.signal };
4880
4907
  if (transport)
4881
- await transport(request);
4908
+ await raceWithSignal(transport(request), controller.signal);
4882
4909
  else {
4883
4910
  if (!client)
4884
4911
  throw new Error("Hidden completion transport is unavailable");
4885
- await client.session.prompt(request);
4912
+ await raceWithSignal(client.session.prompt(request), controller.signal);
4886
4913
  }
4887
4914
  } catch (error) {
4888
4915
  if (signal?.aborted) {
@@ -5084,7 +5111,7 @@ async function attemptAndValidate(client, args, timeoutMs, signal, callContext,
5084
5111
  }
5085
5112
  let output;
5086
5113
  try {
5087
- output = await options.fetchOutput(args, attempt);
5114
+ output = await raceWithSignal(options.fetchOutput(args, attempt), options.signal);
5088
5115
  } catch (error) {
5089
5116
  throw {
5090
5117
  error,
@@ -5382,10 +5409,20 @@ function createPromptAsyncTransport(client, childSessionId, options = {}) {
5382
5409
  if (!readChildTokenBudget || !session?.prompt)
5383
5410
  return;
5384
5411
  return Object.assign(async (request) => {
5385
- await session.prompt?.(request);
5386
- const state = readChildTokenBudget(childSessionId);
5387
- if (state)
5388
- options.onBudgetUpdate?.({ ...state, sessionId: childSessionId });
5412
+ const previous = readChildTokenBudget(childSessionId);
5413
+ const remaining = options.tokenBudget === undefined ? undefined : options.tokenBudget - (previous?.spent ?? 0);
5414
+ if (remaining !== undefined && remaining <= 0 || previous?.finalizeFired)
5415
+ throw new DreamTokenBudgetExceeded(childSessionId, previous?.spent ?? 0);
5416
+ try {
5417
+ await session.prompt?.({
5418
+ ...request,
5419
+ ...remaining === undefined ? {} : { tokenBudget: remaining }
5420
+ });
5421
+ } finally {
5422
+ const state = readChildTokenBudget(childSessionId);
5423
+ if (state)
5424
+ options.onBudgetUpdate?.({ ...state, sessionId: childSessionId });
5425
+ }
5389
5426
  }, { childSessionId });
5390
5427
  }
5391
5428
  const budgetGuard = options.tokenBudget ? createDreamTokenBudget(options.tokenBudget) : undefined;
@@ -10117,9 +10154,12 @@ async function runCompressCues(args) {
10117
10154
  let timeoutStreakElapsedMs = [];
10118
10155
  for (let i = 0;i < chunks.length; i += 1) {
10119
10156
  const remainingMs = Math.max(0, args.deadline - Date.now());
10120
- if (remainingMs <= 0)
10157
+ if (remainingMs <= 0) {
10158
+ result.stopReason = "deadline";
10121
10159
  break;
10160
+ }
10122
10161
  if (remainingMs < CHUNK_TIMEOUT_FLOOR_MS) {
10162
+ result.stopReason = "deadline";
10123
10163
  log2(`[dreamer] compress-cues: stopping before chunk ${i + 1}/${chunks.length} — remaining budget ${remainingMs}ms is below the ${CHUNK_TIMEOUT_FLOOR_MS}ms chunk floor; banking ${result.compressed} compressed cue(s)`);
10124
10164
  break;
10125
10165
  }
@@ -10128,6 +10168,10 @@ async function runCompressCues(args) {
10128
10168
  if (!chunk)
10129
10169
  break;
10130
10170
  const outcome = await compressOneChunk(args, chunk, sliceMs, abortController.signal);
10171
+ if (outcome.failure) {
10172
+ result.failure = outcome.failure.error;
10173
+ result.failureStage = outcome.failure.stage;
10174
+ }
10131
10175
  result.compressed += outcome.compressed;
10132
10176
  result.skipped += outcome.skipped;
10133
10177
  result.remaining -= outcome.compressed;
@@ -10137,6 +10181,7 @@ async function runCompressCues(args) {
10137
10181
  consecutiveTimeouts += 1;
10138
10182
  timeoutStreakElapsedMs.push(outcome.failure.elapsedMs);
10139
10183
  if (consecutiveTimeouts >= CONSECUTIVE_TIMEOUT_LIMIT) {
10184
+ result.stopReason = "timeout-circuit-breaker";
10140
10185
  log2(`[dreamer] compress-cues: circuit breaker tripped — ${consecutiveTimeouts} consecutive chunk timeouts (model too slow for its time slice); per-chunk elapsed [${timeoutStreakElapsedMs.join("ms, ")}ms] vs ${sliceMs}ms slice; stopping run incomplete with ${chunks.length - i - 1} chunk(s) unattempted`);
10141
10186
  break;
10142
10187
  }
@@ -10153,7 +10198,7 @@ async function runCompressCues(args) {
10153
10198
  }
10154
10199
  }
10155
10200
  function isTimeoutClassError(error) {
10156
- return error instanceof Error && /^prompt timed out after \d+ms$/.test(error.message);
10201
+ return isPromptTimeoutError(error) || getPromptFailureDetail(error)?.failureClass === "provider_timeout";
10157
10202
  }
10158
10203
  async function compressOneChunk(args, chunk, sliceMs, signal) {
10159
10204
  let agentSessionId = null;
@@ -10161,6 +10206,7 @@ async function compressOneChunk(args, chunk, sliceMs, signal) {
10161
10206
  const executor = resolveHiddenCompletionExecutor(args.hiddenCompletionExecutor, args.client, args.db, args.sessionDirectory, "compress-cues");
10162
10207
  let promptSettled = false;
10163
10208
  const startedAt = Date.now();
10209
+ let stage = "child-start";
10164
10210
  try {
10165
10211
  const prompt = buildCompressCuesPrompt({
10166
10212
  projectPath: args.projectIdentity,
@@ -10181,6 +10227,7 @@ async function compressOneChunk(args, chunk, sliceMs, signal) {
10181
10227
  agentSessionId = handle.id || null;
10182
10228
  if (!agentSessionId)
10183
10229
  throw new Error("Could not create compress-cues session.");
10230
+ stage = "prompt";
10184
10231
  const opened = handle;
10185
10232
  const run = await promptSyncWithValidatedOutputRetry(args.client, {
10186
10233
  path: { id: agentSessionId },
@@ -10232,16 +10279,19 @@ async function compressOneChunk(args, chunk, sliceMs, signal) {
10232
10279
  modelId: run.output.modelId
10233
10280
  });
10234
10281
  }
10282
+ stage = "apply";
10235
10283
  return args.moduleRoute ? await applyCuesThroughModule(args, chunk, run.validated, signal) : applyCues(args, chunk, run.validated);
10236
10284
  } catch (error) {
10237
10285
  const desc = describeError(error);
10238
10286
  log2(`[dreamer] compress-cues chunk failed: ${desc.brief}`, desc.stackHead ? { stackHead: desc.stackHead } : undefined);
10239
- if (signal.aborted || error instanceof HiddenCompletionRefusal || error instanceof DreamerProviderOutputFailureError)
10287
+ if (signal.aborted || error instanceof DreamerModuleFailureError || error instanceof HiddenCompletionRefusal || error instanceof DreamerProviderOutputFailureError)
10240
10288
  throw error;
10241
10289
  return {
10242
10290
  compressed: 0,
10243
10291
  skipped: 0,
10244
10292
  failure: {
10293
+ error,
10294
+ stage,
10245
10295
  class: isTimeoutClassError(error) ? "timeout" : "other",
10246
10296
  brief: desc.brief,
10247
10297
  elapsedMs: Date.now() - startedAt
@@ -10395,7 +10445,7 @@ async function applyCuesThroughModule(args, chunk, manifestText, signal) {
10395
10445
  // ../plugin/src/features/magic-context/dreamer/hidden-single-shot.ts
10396
10446
  async function runHiddenSingleShotPrompt(args) {
10397
10447
  const system = withContentLanguageDirective(args.system, args.language);
10398
- const handle = await args.executor.open({
10448
+ const opening = args.executor.open({
10399
10449
  parentSessionId: args.parentSessionId,
10400
10450
  agent: args.agent,
10401
10451
  kind: "dreamer-task",
@@ -10407,6 +10457,17 @@ async function runHiddenSingleShotPrompt(args) {
10407
10457
  directory: args.sessionDirectory,
10408
10458
  ...args.metadata ? { metadata: args.metadata } : {}
10409
10459
  });
10460
+ opening.then((handle) => {
10461
+ if (args.signal?.aborted)
10462
+ return args.executor.close(handle, {
10463
+ promptSettled: false,
10464
+ privacySensitive: args.privacySensitive ?? true,
10465
+ context: `[dreamer] ${args.callContext}`,
10466
+ log: log2
10467
+ });
10468
+ }).catch(() => {});
10469
+ const handle = await raceWithSignal(opening, args.signal);
10470
+ args.onOpened?.(handle.id);
10410
10471
  let promptSettled = false;
10411
10472
  try {
10412
10473
  if (!handle.id)
@@ -10461,12 +10522,16 @@ async function runHiddenSingleShotPrompt(args) {
10461
10522
  }
10462
10523
  throw error;
10463
10524
  } finally {
10464
- await args.executor.close(handle, {
10525
+ const close = args.executor.close(handle, {
10465
10526
  promptSettled,
10466
10527
  privacySensitive: args.privacySensitive ?? true,
10467
10528
  context: `[dreamer] ${args.callContext}`,
10468
10529
  log: log2
10469
10530
  });
10531
+ if (args.signal?.aborted)
10532
+ close.catch(() => {});
10533
+ else
10534
+ await raceWithSignal(close, args.signal);
10470
10535
  }
10471
10536
  }
10472
10537
 
@@ -12628,7 +12693,7 @@ function getAsyncModule() {
12628
12693
  await beforeModuleLoad();
12629
12694
  const [{ default: singlefileAsyncifyVariant }, { newQuickJSAsyncWASMModuleFromVariant }] = await importPluginModule(() => Promise.all([
12630
12695
  import("./index-e7vx3byk.js").then((m)=>__toESM(m.default,1)),
12631
- import("./index-69vas84q.js")
12696
+ import("./index-e6xnpach.js")
12632
12697
  ]));
12633
12698
  const module = await newQuickJSAsyncWASMModuleFromVariant(singlefileAsyncifyVariant);
12634
12699
  return module;
@@ -14294,14 +14359,28 @@ function isDeferred(memory, retries) {
14294
14359
  return !!retry && retry.content === contentKey(memory) && retry.deferUntil > Date.now();
14295
14360
  }
14296
14361
  function assistantText(messages) {
14297
- return messages.filter((message) => isRecord(message) && (message.role === "assistant" || isRecord(message.info) && message.info.role === "assistant")).map((message) => extractLatestAssistantText([message]) ?? (isRecord(message) && Array.isArray(message.content) ? message.content.filter(isRecord).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join(`
14362
+ const joined = messages.filter((message) => isRecord(message) && (message.role === "assistant" || isRecord(message.info) && message.info.role === "assistant")).map((message) => extractLatestAssistantText([message]) ?? (isRecord(message) && Array.isArray(message.content) ? message.content.filter(isRecord).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join(`
14298
14363
  `) : "")).join(`
14299
14364
  `);
14365
+ return joined.trim() ? joined : null;
14300
14366
  }
14301
14367
  function currentManifest(text) {
14302
14368
  const start = text.lastIndexOf("<mappings>");
14303
14369
  return start < 0 ? text : text.slice(start);
14304
14370
  }
14371
+ function parseMapMemoriesCompletion(text, completion, expectedIds) {
14372
+ return validateMapMemoriesManifest(currentManifest(assistantText(completion.messages ?? []) || text), expectedIds);
14373
+ }
14374
+ function validateClientMapMemories(messages, expectedIds) {
14375
+ if (hasLengthCappedOutput(messages)) {
14376
+ throw new Error("map-memories returned length-capped output");
14377
+ }
14378
+ const raw = assistantText(messages);
14379
+ if (raw == null) {
14380
+ throw new Error("map-memories returned no output");
14381
+ }
14382
+ return validateMapMemoriesManifest(currentManifest(raw), expectedIds);
14383
+ }
14305
14384
  function logBatchUsage(messages, committed, startedAt) {
14306
14385
  const steps = messages.filter((message) => isRecord(message) && (message.role === "assistant" || isRecord(message.info) && message.info.role === "assistant")).length;
14307
14386
  log2(`[dreamer] map-memories batch: steps=${steps} committed=${committed} steps_per_memory=${committed ? (steps / committed).toFixed(2) : "n/a"} elapsed_ms=${Date.now() - startedAt}`);
@@ -14366,8 +14445,11 @@ async function mapMemories(args) {
14366
14445
  const inputs = allInputs.filter((memory) => !isDeferred(memory, retries));
14367
14446
  result.remaining = allInputs.length;
14368
14447
  result.complete = result.remaining === 0;
14369
- if (inputs.length === 0)
14448
+ if (inputs.length === 0) {
14449
+ if (allInputs.length > 0)
14450
+ result.deferred = true;
14370
14451
  return result;
14452
+ }
14371
14453
  const batches = [];
14372
14454
  for (let i = 0;i < inputs.length; i += MAP_BATCH_SIZE) {
14373
14455
  batches.push({ inputs: inputs.slice(i, i + MAP_BATCH_SIZE), isOmissionRetry: false });
@@ -14393,6 +14475,10 @@ async function mapMemories(args) {
14393
14475
  if (!batch)
14394
14476
  break;
14395
14477
  const outcome = await mapOneBatch(args, batch.inputs, sliceMs, abortController.signal);
14478
+ if (outcome.failure) {
14479
+ result.failure = outcome.failure.error;
14480
+ result.failureStage = outcome.failure.stage;
14481
+ }
14396
14482
  const committed = outcome.mapped + outcome.independent;
14397
14483
  runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
14398
14484
  const committedIds = new Set(outcome.committedIds);
@@ -14456,6 +14542,7 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
14456
14542
  const startedAt = Date.now();
14457
14543
  let budgetFinalized = false;
14458
14544
  let failedCompletion;
14545
+ let stage = "child-start";
14459
14546
  const onBudgetUpdate = (state) => {
14460
14547
  budgetFinalized ||= state.finalizeFired;
14461
14548
  args.onBudgetUpdate?.(state);
@@ -14477,15 +14564,19 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
14477
14564
  fallbackModels: args.fallbackModels,
14478
14565
  timeoutMs: sliceMs,
14479
14566
  signal,
14567
+ onOpened: () => {
14568
+ stage = "prompt";
14569
+ },
14480
14570
  onFailureOutput: (completion) => {
14481
14571
  failedCompletion = completion;
14482
14572
  },
14483
- parse: (text, completion) => validateMapMemoriesManifest(currentManifest(assistantText(completion.messages ?? []) || text), new Set(batch.map((memory) => memory.id)))
14573
+ parse: (text, completion) => parseMapMemoriesCompletion(text, completion, new Set(batch.map((memory) => memory.id)))
14484
14574
  });
14485
14575
  recordInvocation2(args, startedAt, {
14486
14576
  status: "completed",
14487
14577
  messages: run.completion.messages ?? []
14488
14578
  });
14579
+ stage = "apply";
14489
14580
  const outcome = await applyParsedBatchMappings(args, batch, run.validated, budgetFinalized, true);
14490
14581
  logBatchUsage(run.completion.messages ?? [], outcome.mapped + outcome.independent, startedAt);
14491
14582
  const returnedIds = new Set(outcome.committedIds);
@@ -14510,6 +14601,7 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
14510
14601
  agentSessionId = typeof created?.id === "string" ? created.id : null;
14511
14602
  if (!agentSessionId)
14512
14603
  throw new Error("Could not create map-memories session.");
14604
+ stage = "prompt";
14513
14605
  const run = await promptSyncWithValidatedOutputRetry(client, {
14514
14606
  path: { id: agentSessionId },
14515
14607
  query: { directory: args.sessionDirectory },
@@ -14537,18 +14629,11 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
14537
14629
  preferResponseOnMissingData: true
14538
14630
  });
14539
14631
  },
14540
- validateOutput: (messages) => {
14541
- if (hasLengthCappedOutput(messages)) {
14542
- throw new Error("map-memories returned length-capped output");
14543
- }
14544
- const text = currentManifest(assistantText(messages));
14545
- if (!text)
14546
- throw new Error("map-memories returned no output");
14547
- return validateMapMemoriesManifest(text, new Set(batch.map((memory) => memory.id)));
14548
- }
14632
+ validateOutput: (messages) => validateClientMapMemories(messages, new Set(batch.map((memory) => memory.id)))
14549
14633
  });
14550
14634
  promptSettled = true;
14551
14635
  recordInvocation2(args, startedAt, { status: "completed", messages: run.output });
14636
+ stage = "apply";
14552
14637
  const outcome = await applyParsedBatchMappings(args, batch, run.validated, budgetFinalized, true);
14553
14638
  logBatchUsage(run.output, outcome.mapped + outcome.independent, startedAt);
14554
14639
  const returnedIds = new Set(outcome.committedIds);
@@ -14577,6 +14662,8 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
14577
14662
  return {
14578
14663
  ...outcome,
14579
14664
  failure: {
14665
+ error,
14666
+ stage,
14580
14667
  class: isTimeoutClassError2(error) ? "timeout" : "other",
14581
14668
  elapsedMs: Date.now() - startedAt
14582
14669
  }
@@ -15701,10 +15788,28 @@ async function readRetrospectiveScanWindow(provider, projectIdentity, watermarkM
15701
15788
  }
15702
15789
  function readOpenCodeMessagesSince(db, sessionId, sinceMs, capPerSession) {
15703
15790
  const limit = Math.max(1, Math.floor(capPerSession));
15704
- const rows = db.prepare(`SELECT id, data, time_created
15705
- FROM message
15706
- WHERE session_id = ? AND time_created > ?
15707
- ORDER BY time_created ASC, id ASC
15791
+ const rows = db.prepare(`SELECT current.id, current.data, current.time_created,
15792
+ EXISTS (
15793
+ SELECT 1 FROM message assistant_reply
15794
+ WHERE assistant_reply.session_id = current.session_id
15795
+ AND json_valid(assistant_reply.data)
15796
+ AND json_extract(assistant_reply.data, '$.role') = 'assistant'
15797
+ AND (assistant_reply.time_created < current.time_created
15798
+ OR (assistant_reply.time_created = current.time_created
15799
+ AND assistant_reply.id < current.id))
15800
+ AND EXISTS (
15801
+ SELECT 1 FROM message prior_user
15802
+ WHERE prior_user.session_id = current.session_id
15803
+ AND json_valid(prior_user.data)
15804
+ AND json_extract(prior_user.data, '$.role') = 'user'
15805
+ AND (prior_user.time_created < assistant_reply.time_created
15806
+ OR (prior_user.time_created = assistant_reply.time_created
15807
+ AND prior_user.id < assistant_reply.id))
15808
+ )
15809
+ ) AS has_prior_assistant_reply
15810
+ FROM message current
15811
+ WHERE current.session_id = ? AND current.time_created > ?
15812
+ ORDER BY current.time_created ASC, current.id ASC
15708
15813
  LIMIT ?`).all(sessionId, sinceMs, limit + 1);
15709
15814
  const truncated = rows.length > limit;
15710
15815
  const kept = truncated ? rows.slice(0, limit) : rows;
@@ -15808,7 +15913,8 @@ function normalizeOpenCodeRows(db, sessionId, rows) {
15808
15913
  ordinal,
15809
15914
  role,
15810
15915
  parts,
15811
- ts: row.time_created
15916
+ ts: row.time_created,
15917
+ followsAssistantReply: row.has_prior_assistant_reply === 1
15812
15918
  });
15813
15919
  });
15814
15920
  }
@@ -15822,7 +15928,8 @@ function normalizeOpenCodeMessage(args) {
15822
15928
  ordinal: args.ordinal,
15823
15929
  role: "user",
15824
15930
  text,
15825
- ts: args.ts
15931
+ ts: args.ts,
15932
+ ...args.followsAssistantReply ? { followsAssistantReply: true } : {}
15826
15933
  });
15827
15934
  }
15828
15935
  }
@@ -16193,7 +16300,7 @@ var VERIFY_SYSTEM_PROMPT = `You are a memory verifier for the magic-context syst
16193
16300
 
16194
16301
  Each memory below comes with its backing file(s) — the code it makes a claim about. For EACH memory: use the provided git diff as evidence when present. If no diff is supplied, read its backing files (you may read more if needed) and decide whether the memory is still accurate.
16195
16302
 
16196
- Tools (read-only): read, grep, glob. Request line ranges and keep reads short. You read code only when the evidence requires it; you change nothing.
16303
+ Tools (read-only): read, grep, glob, plus any available AFT navigation tools. Use at most 3 tool calls per memory, including searches and reads. Start with the backing files and likely identifier matches. Never read the same file twice in a batch; reuse evidence. Request short useful line ranges unless the claim concerns the file as a whole. If evidence is insufficient when the call budget runs out, SKIP that memory; uncertainty never justifies archival. You change nothing.
16197
16304
 
16198
16305
  Decide ONE of four outcomes per memory:
16199
16306
  - VERIFIED — still accurate. Keep it as-is.
@@ -16205,7 +16312,7 @@ UPDATE and ARCHIVE are ONLY for claims a repository file can falsify, such as an
16205
16312
 
16206
16313
  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.
16207
16314
 
16208
- Output ONE XML manifest at the very end and NOTHING else — no narration, no per-memory commentary, no reasoning:
16315
+ Output ONE XML manifest and NOTHING else — no narration or reasoning. Open <verify> before investigating and emit each complete verdict immediately before investigating the next memory, so interruption preserves finished work. Close </verify> when done:
16209
16316
  <verify>
16210
16317
  <verified id="N" files="path/a.ts,path/b.ts"/>
16211
16318
  <update id="M" files="path/c.ts">corrected present-tense content</update>
@@ -16218,17 +16325,24 @@ Rules:
16218
16325
  - Every input memory id MUST appear exactly once, in exactly one of verified/update/archive/skip.
16219
16326
  - 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.
16220
16327
  - Default to VERIFIED. skip when code cannot decide the claim. update and archive are the exceptions, not the norm.`;
16328
+ async function seedVerifyCandidates(memories, directory) {
16329
+ const seeded = await seedIdentifierCandidates(memories.map((m) => ({ ...m, candidates: [] })), directory);
16330
+ return memories.map((m, i) => ({ ...m, candidates: seeded[i].candidates }));
16331
+ }
16221
16332
  function buildVerifyPrompt(projectPath, memories, evidence) {
16222
16333
  const list = memories.map((m) => `[${m.id}] ${m.category}
16223
16334
  Content: ${m.content}
16224
- Backing files: ${m.mappedFiles.join(", ")}`).join(`
16335
+ Backing files: ${m.mappedFiles.join(", ")}${m.candidates?.length ? `
16336
+ Likely files (identifier matches, confirm before using): ${m.candidates.join(", ")}` : ""}`).join(`
16225
16337
 
16226
16338
  `);
16227
16339
  return `## Verify these memories against the code
16228
16340
 
16229
16341
  Project: ${projectPath}
16230
16342
 
16231
- ${evidence ? "The git diff below is the evidence. If changed hunks do not touch a claim, mark it verified without opening files. Open files only when a diff suggests the claim may have changed. A rename or deletion requires an update or archive when positively supported; do not archive on uncertainty." : "Read each memory's backing files to check its claims."} Decide verified / update / archive / skip (default verified; behavioral directives only permit verified or skip), then output ONE <verify> manifest covering every id.
16343
+ When available in this session, use aft_outline to outline the backing file and aft_zoom on the claimed symbol (or aft_search to locate it), instead of paging through whole files. Read a whole file only when the claim is about the file as a whole. Otherwise use the available read-only tools.
16344
+
16345
+ ${evidence ? "The git diff below is the evidence. If changed hunks do not touch a claim, mark it verified without opening files. Open files only when a diff suggests the claim may have changed. A rename or deletion requires an update or archive when positively supported; do not archive on uncertainty." : "Check each memory's claim against its backing files."} Decide verified / update / archive / skip (default verified; behavioral directives only permit verified or skip), then output ONE <verify> manifest covering every id.
16232
16346
 
16233
16347
  <memories>
16234
16348
  ${list}
@@ -16305,13 +16419,57 @@ function validateVerifyManifest(text, expectedIds) {
16305
16419
  assertNoDuplicateManifestIds(ids.filter((id) => expectedIds.has(id)), "verify");
16306
16420
  return parsed;
16307
16421
  }
16422
+ function parsePartialVerifyManifest(text, expectedIds) {
16423
+ const out = { verified: [], updated: [], archived: [], skipped: [] };
16424
+ const start = text.lastIndexOf("<verify>");
16425
+ if (start < 0)
16426
+ return out;
16427
+ const entries = new Map;
16428
+ const conflicts = new Set;
16429
+ for (const match of text.slice(start).matchAll(/<(?:verified|archive|skip)\b[^<>]*\/>|<update\b[^<>]*>[\s\S]*?<\/update>/g)) {
16430
+ try {
16431
+ const parsed = parseVerifyManifest(`<verify>${match[0]}</verify>`);
16432
+ const id = verifyIds(parsed)[0];
16433
+ if (!expectedIds.has(id) || conflicts.has(id))
16434
+ continue;
16435
+ const previous = entries.get(id);
16436
+ if (previous && JSON.stringify(previous) !== JSON.stringify(parsed)) {
16437
+ entries.delete(id);
16438
+ conflicts.add(id);
16439
+ } else
16440
+ entries.set(id, parsed);
16441
+ } catch {}
16442
+ }
16443
+ for (const parsed of entries.values()) {
16444
+ out.verified.push(...parsed.verified);
16445
+ out.updated.push(...parsed.updated);
16446
+ out.archived.push(...parsed.archived);
16447
+ out.skipped.push(...parsed.skipped);
16448
+ }
16449
+ return out;
16450
+ }
16308
16451
 
16309
16452
  // ../plugin/src/features/magic-context/dreamer/verify.ts
16310
- var VERIFY_BATCH_SIZE = 20;
16453
+ var VERIFY_BATCH_SIZE = 5;
16454
+ var VERIFY_TOKENS_PER_MEMORY = 1e5;
16455
+ var VERIFY_RUN_TOKEN_BUDGET = 2500000;
16456
+ var VERIFY_RUN_SLICE_MS = 240000;
16457
+ function verifyBatchTokenBudget(size, configured) {
16458
+ return Math.min(configured ?? Number.MAX_SAFE_INTEGER, size * VERIFY_TOKENS_PER_MEMORY);
16459
+ }
16460
+ function verifyAssistantText(messages) {
16461
+ const text = messages.filter((m) => isRecord(m) && (m.role === "assistant" || isRecord(m.info) && m.info.role === "assistant")).map((m) => extractLatestAssistantText([m]) || (isRecord(m) && Array.isArray(m.content) ? m.content.filter(isRecord).filter((p) => p.type === "text").map((p) => p.text).join(`
16462
+ `) : "")).join(`
16463
+ `);
16464
+ if (!text.trim())
16465
+ return null;
16466
+ const start = text.lastIndexOf("<verify>");
16467
+ return start >= 0 ? text.slice(start) : text;
16468
+ }
16311
16469
  var IDENTICAL_PROVIDER_FAILURE_BATCH_LIMIT = 2;
16312
- var VERIFY_BATCH_FLOOR_MS = 240000;
16313
- function computeVerifyBatchSliceMs(remainingMs, batchesRemaining) {
16314
- return Math.min(remainingMs, Math.max(VERIFY_BATCH_FLOOR_MS, Math.floor(remainingMs / batchesRemaining)));
16470
+ var VERIFY_BATCH_FLOOR_MS = 30000;
16471
+ function computeVerifyBatchSliceMs(remainingMs, _batchesRemaining) {
16472
+ return Math.min(remainingMs, VERIFY_RUN_SLICE_MS);
16315
16473
  }
16316
16474
  function closeBroadCycle(args, cycleStartAt) {
16317
16475
  if (!args.forceBroad || cycleStartAt === undefined)
@@ -16335,6 +16493,7 @@ async function runVerify(args) {
16335
16493
  }
16336
16494
  async function runVerifyWithCooldown(args) {
16337
16495
  const runStartedAt = Date.now();
16496
+ const sliceDeadline = runStartedAt + VERIFY_RUN_SLICE_MS;
16338
16497
  const result = {
16339
16498
  verified: 0,
16340
16499
  updated: 0,
@@ -16377,16 +16536,31 @@ async function runVerifyWithCooldown(args) {
16377
16536
  let consecutiveProviderFailures = 0;
16378
16537
  let priorProviderFailureFingerprint = null;
16379
16538
  let lastProviderFailure = null;
16539
+ let runSpent = 0;
16540
+ const runTokenBudget = Math.min(args.tokenBudget ?? VERIFY_RUN_TOKEN_BUDGET, VERIFY_RUN_TOKEN_BUDGET);
16380
16541
  try {
16381
16542
  for (let i = 0;i < batches.length; i += 1) {
16382
- const remainingMs = Math.max(0, args.deadline - Date.now());
16543
+ const remainingMs = Math.max(0, Math.min(args.deadline, sliceDeadline) - Date.now());
16383
16544
  if (remainingMs < VERIFY_BATCH_FLOOR_MS) {
16384
16545
  result.stopReason = "deadline";
16385
16546
  log2(`[dreamer] ${args.forceBroad ? "verify-broad" : "verify"}: stopping before batch ${i + 1}/${batches.length} — remaining budget ${remainingMs}ms is below the ${VERIFY_BATCH_FLOOR_MS}ms batch floor`);
16386
16547
  break;
16387
16548
  }
16388
16549
  const sliceMs = computeVerifyBatchSliceMs(remainingMs, batches.length - i);
16389
- const counts = await verifyOneBatch(args, batches[i], sliceMs, abortController.signal);
16550
+ if (runSpent >= runTokenBudget) {
16551
+ result.stopReason = "token-budget";
16552
+ break;
16553
+ }
16554
+ let batchSpent = 0;
16555
+ const counts = await verifyOneBatch({
16556
+ ...args,
16557
+ tokenBudget: runTokenBudget - runSpent,
16558
+ onBudgetUpdate: (state) => {
16559
+ runSpent += Math.max(0, state.spent - batchSpent);
16560
+ batchSpent = Math.max(batchSpent, state.spent);
16561
+ args.onBudgetUpdate?.(state);
16562
+ }
16563
+ }, batches[i], sliceMs, abortController.signal);
16390
16564
  result.verified += counts.verified;
16391
16565
  result.updated += counts.updated;
16392
16566
  result.archived += counts.archived;
@@ -16396,6 +16570,10 @@ async function runVerifyWithCooldown(args) {
16396
16570
  result.remaining -= batchProcessed;
16397
16571
  result.batches += 1;
16398
16572
  args.onProgress?.(result.verified + result.updated + result.archived + result.skipped + result.refused, result.refused);
16573
+ if (counts.tokenLimited) {
16574
+ result.stopReason = "token-budget";
16575
+ break;
16576
+ }
16399
16577
  if (counts.timedOut) {
16400
16578
  result.stopReason = "batch-timeout";
16401
16579
  log2(`[dreamer] ${args.forceBroad ? "verify-broad" : "verify"}: batch ${i + 1}/${batches.length} timed out after its ${sliceMs}ms slice; stopping with ${result.remaining} remaining`);
@@ -16430,18 +16608,41 @@ async function runVerifyWithCooldown(args) {
16430
16608
  }
16431
16609
  }
16432
16610
  async function verifyOneBatch(args, batch, sliceMs, signal) {
16611
+ const batchController = new AbortController;
16612
+ const abort = () => batchController.abort(signal.reason);
16613
+ signal.addEventListener("abort", abort, { once: true });
16614
+ if (signal.aborted)
16615
+ abort();
16616
+ const timer = setTimeout(() => batchController.abort(new Error(`verify slice timed out after ${sliceMs}ms`)), sliceMs);
16617
+ const batchSignal = batchController.signal;
16618
+ const tokenBudget = verifyBatchTokenBudget(batch.length, args.tokenBudget);
16433
16619
  let agentSessionId = null;
16620
+ let hiddenSessionId = null;
16434
16621
  let promptSettled = false;
16435
16622
  const startedAt = Date.now();
16436
16623
  let budgetFinalized = false;
16624
+ let failedCompletion;
16625
+ let headAtPrompt = null;
16437
16626
  const onBudgetUpdate = (state) => {
16438
16627
  budgetFinalized ||= state.finalizeFired;
16439
- args.onBudgetUpdate?.(state);
16628
+ args.onBudgetUpdate?.({
16629
+ ...state,
16630
+ ...hiddenSessionId ? { sessionId: hiddenSessionId } : {}
16631
+ });
16632
+ };
16633
+ const reportUsage = (messages, sessionId, fallbackSpent = 0) => {
16634
+ const usage = sumTokensFromChildMessages(messages);
16635
+ onBudgetUpdate({
16636
+ spent: Math.max(fallbackSpent, usage.input + usage.cacheRead + usage.cacheWrite),
16637
+ finalizeFired: budgetFinalized,
16638
+ ...sessionId ? { sessionId } : {}
16639
+ });
16440
16640
  };
16441
16641
  try {
16442
- const headAtPrompt = await readGitHead(args.sessionDirectory);
16443
- const evidence = !args.forceBroad && batch.every((m) => m.verifiedAt) ? await buildVerifyDiffEvidence(args.sessionDirectory, batch) : null;
16444
- const prompt = buildVerifyPrompt(args.projectIdentity, batch, evidence);
16642
+ headAtPrompt = await raceWithSignal(readGitHead(args.sessionDirectory), batchSignal);
16643
+ const evidence = !args.forceBroad && batch.every((m) => m.verifiedAt) ? await raceWithSignal(buildVerifyDiffEvidence(args.sessionDirectory, batch), batchSignal) : null;
16644
+ const prompt = buildVerifyPrompt(args.projectIdentity, await raceWithSignal(seedVerifyCandidates(batch, args.sessionDirectory), batchSignal), evidence);
16645
+ batchSignal.throwIfAborted();
16445
16646
  if (args.hiddenCompletionExecutor) {
16446
16647
  const run = await runHiddenSingleShotPrompt({
16447
16648
  executor: args.hiddenCompletionExecutor,
@@ -16452,41 +16653,53 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
16452
16653
  prompt,
16453
16654
  title: "magic-context-dream-verify",
16454
16655
  callContext: "dreamer:verify",
16455
- metadata: { tokenBudget: args.tokenBudget, onBudgetUpdate },
16656
+ metadata: { tokenBudget, onBudgetUpdate },
16456
16657
  model: args.model,
16457
16658
  fallbackModels: args.fallbackModels,
16458
16659
  language: args.language,
16459
16660
  timeoutMs: sliceMs,
16460
- signal,
16661
+ signal: batchSignal,
16662
+ onFailureOutput: (completion) => {
16663
+ failedCompletion = completion;
16664
+ },
16665
+ onOpened: (sessionId) => {
16666
+ hiddenSessionId = sessionId;
16667
+ onBudgetUpdate({ spent: 0, finalizeFired: false, sessionId });
16668
+ },
16461
16669
  parse: (text, completion) => {
16462
16670
  const providerFailure = providerOutputFailureFromInvalidManifest(completion.messages ?? [], text);
16463
16671
  if (providerFailure)
16464
16672
  throw providerFailure;
16465
- return validateVerifyManifest(text, new Set(batch.map((memory) => memory.id)));
16673
+ const manifest = verifyAssistantText(completion.messages ?? []) ?? (text.trim() ? text : null);
16674
+ if (!manifest)
16675
+ throw new Error("verify returned no output");
16676
+ return validateVerifyManifest(manifest, new Set(batch.map((memory) => memory.id)));
16466
16677
  }
16467
16678
  });
16468
16679
  recordInvocation4(args, startedAt, {
16469
16680
  status: "completed",
16470
16681
  messages: run.completion.messages ?? []
16471
16682
  });
16472
- return applyParsedVerifyManifest(args, batch, run.validated, evidence?.head ?? headAtPrompt, budgetFinalized);
16683
+ reportUsage(run.completion.messages ?? [], run.childSessionId, run.completion.usage.input + (run.completion.usage.cacheRead ?? 0) + (run.completion.usage.cacheWrite ?? 0));
16684
+ return await applyParsedVerifyManifest(args, batch, run.validated, evidence?.head ?? headAtPrompt, budgetFinalized);
16473
16685
  }
16474
16686
  const client = args.client;
16475
16687
  if (!client)
16476
16688
  throw new Error("verify requires a client or hidden completion executor");
16477
- const createResponse = await createChildSessionWithFence({
16689
+ const createResponse = await raceWithSignal(createChildSessionWithFence({
16478
16690
  client,
16479
16691
  db: args.db,
16480
16692
  parentSessionId: args.parentSessionId,
16481
16693
  title: args.forceBroad ? "magic-context-dream-verify-broad" : "magic-context-dream-verify",
16482
16694
  directory: args.sessionDirectory
16483
- });
16695
+ }), batchSignal);
16484
16696
  const created = normalizeSDKResponse(createResponse, null, {
16485
16697
  preferResponseOnMissingData: true
16486
16698
  });
16487
16699
  agentSessionId = typeof created?.id === "string" ? created.id : null;
16488
16700
  if (!agentSessionId)
16489
16701
  throw new Error("Could not create verify session.");
16702
+ onBudgetUpdate({ spent: 0, finalizeFired: false, sessionId: agentSessionId });
16490
16703
  const run = await promptSyncWithValidatedOutputRetry(client, {
16491
16704
  path: { id: agentSessionId },
16492
16705
  query: { directory: args.sessionDirectory },
@@ -16498,11 +16711,11 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
16498
16711
  }
16499
16712
  }, {
16500
16713
  transport: createPromptAsyncTransport(client, agentSessionId, {
16501
- tokenBudget: args.tokenBudget,
16714
+ tokenBudget,
16502
16715
  onBudgetUpdate
16503
16716
  }),
16504
16717
  timeoutMs: sliceMs,
16505
- signal,
16718
+ signal: batchSignal,
16506
16719
  fallbackModels: args.fallbackModels,
16507
16720
  callContext: "dreamer:verify",
16508
16721
  fetchOutput: async () => {
@@ -16518,7 +16731,7 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
16518
16731
  if (hasLengthCappedOutput(messages)) {
16519
16732
  throw new Error("verify returned length-capped output");
16520
16733
  }
16521
- const text = extractLatestAssistantText(messages);
16734
+ const text = verifyAssistantText(messages);
16522
16735
  if (!text)
16523
16736
  throw new Error("verify returned no output");
16524
16737
  const providerFailure = providerOutputFailureFromInvalidManifest(messages, text);
@@ -16529,27 +16742,31 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
16529
16742
  });
16530
16743
  promptSettled = true;
16531
16744
  recordInvocation4(args, startedAt, { status: "completed", messages: run.output });
16745
+ reportUsage(run.output, agentSessionId);
16532
16746
  return await applyParsedVerifyManifest(args, batch, run.validated, evidence?.head ?? headAtPrompt, budgetFinalized);
16533
16747
  } catch (error) {
16534
16748
  const desc = describeError(error);
16535
16749
  const providerFailure = error instanceof DreamerProviderOutputFailureError ? error : undefined;
16536
16750
  const promptFailure = getPromptFailureDetail(error);
16537
16751
  log2(`[dreamer] verify batch ${providerFailure ? "provider failure" : "failed"}: ${desc.brief}`, desc.stackHead ? { stackHead: desc.stackHead } : undefined);
16752
+ const messages = failedCompletion?.messages ?? await readFailedChildMessages(args.client, agentSessionId, args.sessionDirectory) ?? [];
16753
+ reportUsage(messages, hiddenSessionId ?? agentSessionId, failedCompletion?.usage.input ?? 0);
16538
16754
  recordInvocation4(args, startedAt, {
16539
16755
  status: failedInvocationStatus(error),
16540
16756
  error,
16541
- messages: await readFailedChildMessages(args.client, agentSessionId, args.sessionDirectory)
16757
+ messages
16542
16758
  });
16543
- if (error instanceof DreamerModuleFailureError || error instanceof DreamTokenBudgetExceeded || signal.aborted)
16759
+ if (error instanceof DreamerModuleFailureError || signal.aborted)
16544
16760
  throw error;
16545
- if (promptFailure?.failureClass === "provider_timeout" || isPromptTimeoutError(error)) {
16761
+ const interrupted = batchSignal.aborted || error instanceof DreamTokenBudgetExceeded || promptFailure?.failureClass === "provider_timeout" || isPromptTimeoutError(error);
16762
+ if (interrupted) {
16763
+ const text = verifyAssistantText(messages) ?? failedCompletion?.text ?? "";
16764
+ const recovered = parsePartialVerifyManifest(text, new Set(batch.map((m) => m.id)));
16765
+ const counts = await applyParsedVerifyManifest(args, batch, recovered, headAtPrompt, true);
16546
16766
  return {
16547
- verified: 0,
16548
- updated: 0,
16549
- archived: 0,
16550
- skipped: 0,
16551
- refused: 0,
16552
- timedOut: true
16767
+ ...counts,
16768
+ timedOut: !(error instanceof DreamTokenBudgetExceeded),
16769
+ tokenLimited: error instanceof DreamTokenBudgetExceeded
16553
16770
  };
16554
16771
  }
16555
16772
  if (promptFailure !== null && promptFailure.failureClass !== "parse_failed" && !providerFailure)
@@ -16564,7 +16781,7 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
16564
16781
  };
16565
16782
  } finally {
16566
16783
  if (agentSessionId && args.client)
16567
- await teardownChildSession({
16784
+ await raceWithSignal(teardownChildSession({
16568
16785
  client: args.client,
16569
16786
  sessionId: agentSessionId,
16570
16787
  sessionDirectory: args.sessionDirectory,
@@ -16572,7 +16789,9 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
16572
16789
  privacySensitive: true,
16573
16790
  context: "[dreamer] verify",
16574
16791
  log: log2
16575
- });
16792
+ }), batchSignal).catch(() => {});
16793
+ clearTimeout(timer);
16794
+ signal.removeEventListener("abort", abort);
16576
16795
  }
16577
16796
  }
16578
16797
  async function applyParsedVerifyManifest(args, batch, parsed, headAtPrompt, budgetFinalized = false) {
@@ -16599,7 +16818,7 @@ async function applyParsedVerifyManifest(args, batch, parsed, headAtPrompt, budg
16599
16818
  throw new Error(`verify manifest covers ${validIds.length}/${batch.length} batch ids after filtering unknown entries; rejecting mostly-wrong manifest`);
16600
16819
  }
16601
16820
  if (budgetFinalized && validIds.length < batch.length) {
16602
- log2(`[dreamer] verify: accepted partial manifest after token budget: ${validIds.length}/${batch.length}`);
16821
+ log2(`[dreamer] verify: accepted partial manifest after investigation stop: ${validIds.length}/${batch.length}`);
16603
16822
  }
16604
16823
  if (validIds.length === 0) {
16605
16824
  return { verified: 0, updated: 0, archived: 0, skipped: 0, refused: 0 };
@@ -16834,10 +17053,10 @@ function dreamRunFailureDetail(error) {
16834
17053
  child_session_id: null
16835
17054
  };
16836
17055
  }
16837
- const localRefusal = error instanceof HiddenCompletionRefusal;
17056
+ const localRefusal = error instanceof HiddenCompletionRefusal || error instanceof DreamerModuleFailureError;
16838
17057
  const providerFailure = error instanceof Error && error.name === "DreamerProviderOutputFailureError";
16839
17058
  return {
16840
- failure_class: localRefusal ? "local_refusal" : providerFailure ? "provider_error" : /no models?|model chain is empty/i.test(message) ? "no_models" : "unknown",
17059
+ failure_class: localRefusal ? "local_refusal" : providerFailure ? "provider_error" : /lease_(?:lost|expired)|Dream lease (?:lost|unavailable)/i.test(message) ? "child_aborted" : /no models?|model chain is empty/i.test(message) ? "no_models" : "unknown",
16841
17060
  model_attempted: null,
16842
17061
  models_tried: [],
16843
17062
  provider_error: providerFailure ? sanitizeDiagnosticText(message).slice(0, 500) : null,
@@ -16848,6 +17067,10 @@ function dreamRunFailureDetail(error) {
16848
17067
  } : {}
16849
17068
  };
16850
17069
  }
17070
+ function incompleteFailure(error, fallback = "parse_failed") {
17071
+ const detail = dreamRunFailureDetail(error);
17072
+ return detail.failure_class === "unknown" ? { ...detail, failure_class: fallback } : detail;
17073
+ }
16851
17074
  function classifyFailure(error) {
16852
17075
  const described = describeError(error);
16853
17076
  const brief = described.brief;
@@ -17007,6 +17230,7 @@ function createDreamTaskExecutor(deps) {
17007
17230
  const { db, projectIdentity, holderId, leaseKey } = ctx;
17008
17231
  deps.client?.resetDreamTokenBudget?.(config.task);
17009
17232
  const startedAt = Date.now();
17233
+ const effectiveTokenBudget = config.task === "verify" || config.task === "verify-broad" ? Math.min(config.tokenBudget ?? VERIFY_RUN_TOKEN_BUDGET, VERIFY_RUN_TOKEN_BUDGET) : config.tokenBudget;
17010
17234
  const leaseAcquisition = ctx.leaseAcquisition ?? acquireLeaseWithAcquisition(db, holderId, leaseKey) ?? (() => {
17011
17235
  throw new Error("Dream lease unavailable during executor setup");
17012
17236
  })();
@@ -17054,7 +17278,7 @@ function createDreamTaskExecutor(deps) {
17054
17278
  if (state.sessionId) {
17055
17279
  const previous = childSpending.get(state.sessionId) ?? 0;
17056
17280
  budgetSpent += Math.max(0, state.spent - previous);
17057
- childSpending.set(state.sessionId, state.spent);
17281
+ childSpending.set(state.sessionId, Math.max(previous, state.spent));
17058
17282
  } else
17059
17283
  budgetSpent = Math.max(budgetSpent, state.spent);
17060
17284
  budgetFinalized ||= state.finalizeFired;
@@ -17081,9 +17305,15 @@ function createDreamTaskExecutor(deps) {
17081
17305
  failure: extra?.failure ?? dreamRunFailureDetail(error ?? "unknown dreamer failure")
17082
17306
  } : {},
17083
17307
  ...extra?.progress ? { progress: extra.progress } : {},
17084
- ...config.tokenBudget ? {
17308
+ ...childSpending.size ? {
17309
+ children: [...childSpending].map(([sessionId, promptTokens]) => ({
17310
+ sessionId,
17311
+ promptTokens
17312
+ }))
17313
+ } : {},
17314
+ ...effectiveTokenBudget ? {
17085
17315
  tokenBudget: {
17086
- budget: config.tokenBudget,
17316
+ budget: effectiveTokenBudget,
17087
17317
  spent: budgetSpent,
17088
17318
  finalizeFired: budgetFinalized,
17089
17319
  banked: extra?.banked ?? processedDreamTaskItems(backlogAtStart.pending, (extra?.backlogAfter ?? getDreamTaskBacklog(db, projectIdentity, config.task)).pending)
@@ -17172,8 +17402,13 @@ function createDreamTaskExecutor(deps) {
17172
17402
  });
17173
17403
  log2(`[dreamer] compress-cues: compressed=${result.compressed} skipped=${result.skipped} chunks=${result.chunks} remaining=${result.remaining}`);
17174
17404
  if (!result.complete) {
17175
- const error = incompleteMessage(result.remaining);
17176
- recordRun("failed", error);
17405
+ if (result.stopReason === "deadline" && result.chunks === 0)
17406
+ return skip("compress-cues: insufficient time to start a chunk");
17407
+ const error = `${incompleteMessage(result.remaining)}; ${result.failureStage ? `${result.failureStage}: ` : ""}${result.failure ? describeError(result.failure).brief : `${result.skipped} cues rejected by validation`}`;
17408
+ recordRun("failed", error, {
17409
+ banked: result.compressed,
17410
+ failure: incompleteFailure(result.failure, result.failureStage === "child-start" ? "local_refusal" : result.stopReason === "timeout-circuit-breaker" ? "provider_timeout" : "parse_failed")
17411
+ });
17177
17412
  return { status: "failed", transient: true, error };
17178
17413
  }
17179
17414
  recordRun("completed", null);
@@ -17219,9 +17454,15 @@ function createDreamTaskExecutor(deps) {
17219
17454
  });
17220
17455
  log2(`[dreamer] map-memories: committed=${result.mapped + result.independent} mapped=${result.mapped} independent=${result.independent} batches=${result.batches} remaining=${result.remaining} complete=${result.complete}${result.stopReason ? ` stop_reason=${result.stopReason}` : ""}`);
17221
17456
  if (!result.complete) {
17457
+ if (result.deferred)
17458
+ return skip("map-memories: all pending mappings are in content-keyed retry backoff; no child started");
17459
+ if (result.stopReason === "deadline" && result.batches === 0 && !result.failure)
17460
+ return skip("map-memories: insufficient time to start a batch");
17222
17461
  if (result.stopReason === "timeout-circuit-breaker") {
17223
17462
  const error = `map-memories starvation: timeout circuit breaker stopped the run with ${result.remaining} remain`;
17224
- recordRun("failed", error);
17463
+ recordRun("failed", error, {
17464
+ failure: incompleteFailure(result.failure, "provider_timeout")
17465
+ });
17225
17466
  return { status: "failed", transient: true, error };
17226
17467
  }
17227
17468
  const processed = result.mapped + result.independent;
@@ -17230,8 +17471,10 @@ function createDreamTaskExecutor(deps) {
17230
17471
  recordRun("completed", null, { progress });
17231
17472
  return { status: "completed" };
17232
17473
  }
17233
- const error = incompleteMessage(result.remaining);
17234
- recordRun("failed", error);
17474
+ const error = `${incompleteMessage(result.remaining)}; ${result.failureStage ? `${result.failureStage}: ` : ""}${result.failure ? describeError(result.failure).brief : "manifest returned no accepted mapping decisions"}`;
17475
+ recordRun("failed", error, {
17476
+ failure: incompleteFailure(result.failure, result.failureStage === "child-start" ? "local_refusal" : "parse_failed")
17477
+ });
17235
17478
  return { status: "failed", transient: true, error };
17236
17479
  }
17237
17480
  recordRun("completed", null);
@@ -17261,25 +17504,27 @@ function createDreamTaskExecutor(deps) {
17261
17504
  });
17262
17505
  const processed = result.verified + result.updated + result.archived + result.skipped + result.refused;
17263
17506
  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`;
17264
- const broadProgress = config.task === "verify-broad" ? verificationProgress : null;
17265
17507
  const backlogAfter = config.task === "verify-broad" ? { pending: result.remaining, total: backlogAtStart.total } : undefined;
17266
17508
  if (!result.complete) {
17267
- if (broadProgress && processed > 0) {
17509
+ if (processed > 0) {
17268
17510
  recordRun("completed", null, {
17269
- progress: broadProgress,
17511
+ progress: verificationProgress,
17270
17512
  memoryChanges: computeMemoryDelta(memoryBefore),
17271
17513
  backlogAfter,
17272
17514
  banked: result.verified + result.updated + result.archived
17273
17515
  });
17274
17516
  return { status: "completed" };
17275
17517
  }
17518
+ if (result.stopReason === "deadline")
17519
+ return skip(`${config.task}: insufficient time to start a batch`);
17276
17520
  const batchTimedOut = result.stopReason === "batch-timeout";
17277
17521
  const error = batchTimedOut ? `${config.task}: a batch timed out within its time slice; ${result.remaining} remain` : incompleteMessage(result.remaining);
17278
17522
  recordRun("failed", error, {
17279
17523
  progress: verificationProgress,
17280
17524
  memoryChanges: computeMemoryDelta(memoryBefore),
17281
17525
  backlogAfter,
17282
- banked: result.verified + result.updated + result.archived
17526
+ banked: result.verified + result.updated + result.archived,
17527
+ failure: incompleteFailure(error, batchTimedOut ? "provider_timeout" : result.stopReason === "token-budget" ? "token_budget" : "parse_failed")
17283
17528
  });
17284
17529
  return { status: "failed", transient: !batchTimedOut, error };
17285
17530
  }
@@ -17403,6 +17648,7 @@ function createDreamTaskExecutor(deps) {
17403
17648
  if (retro.skipReason)
17404
17649
  return skip(retro.skipReason);
17405
17650
  recordRun("completed", null, {
17651
+ progress: retro.noOpReason,
17406
17652
  memoryChanges: computeMemoryDelta(memoryBefore),
17407
17653
  backlogAfter: retro.retrospectiveWatermarkMs == null ? undefined : getDreamTaskBacklog(db, projectIdentity, "retrospective", {
17408
17654
  retrospectiveWatermarkMs: retro.retrospectiveWatermarkMs
@@ -17410,6 +17656,7 @@ function createDreamTaskExecutor(deps) {
17410
17656
  });
17411
17657
  return {
17412
17658
  status: "completed",
17659
+ ...retro.noOpReason ? { detail: retro.noOpReason } : {},
17413
17660
  schedulePatch: retro.retrospectiveWatermarkMs != null || retro.taskStateJson !== undefined ? {
17414
17661
  ...retro.retrospectiveWatermarkMs != null ? {
17415
17662
  retrospectiveWatermarkMs: retro.retrospectiveWatermarkMs
@@ -17609,20 +17856,28 @@ async function runRetrospectiveTask(config, ctx, helpers) {
17609
17856
  const recencyCutoffMs = helpers.invocationStartedAt - recencyDays * RETROSPECTIVE_DAY_MS;
17610
17857
  const scan = await readRetrospectiveScanWindow(provider, projectIdentity, watermarkMs, RETROSPECTIVE_OVERLAP_USER_LINES, { usableInputTokens, recencyCutoffMs });
17611
17858
  const clearedTaskStateJson = activeOverflow ? writeRetrospectiveOverflowState(taskState.root, null) : undefined;
17612
- const completedWindow = (retrospectiveWatermarkMs) => ({
17859
+ const completedWindow = (retrospectiveWatermarkMs, noOpReason) => ({
17613
17860
  retrospectiveWatermarkMs,
17614
- ...clearedTaskStateJson ? { taskStateJson: clearedTaskStateJson } : {}
17861
+ ...clearedTaskStateJson ? { taskStateJson: clearedTaskStateJson } : {},
17862
+ ...noOpReason ? { noOpReason } : {}
17615
17863
  });
17616
17864
  const messages = withGlobalOrdinals(scan.messages);
17617
17865
  const userMessages = messages.filter((message) => message.role === "user" && message.text.trim().length > 0);
17618
17866
  if (userMessages.length === 0) {
17619
- log2("[dreamer] retrospective: no user messages in window");
17620
- return completedWindow(scan.maxScannedTs);
17867
+ const noOpReason = "no reply-following user lines";
17868
+ log2(`[dreamer] retrospective: ${noOpReason}`);
17869
+ return completedWindow(scan.maxScannedTs, noOpReason);
17621
17870
  }
17622
- const postWatermarkOrdinals = new Set(userMessages.filter((message) => message.ts > watermarkMs).map((message) => message.ordinal));
17623
- if (postWatermarkOrdinals.size === 0) {
17871
+ const newUserOrdinals = new Set(userMessages.filter((message) => message.ts > watermarkMs).map((message) => message.ordinal));
17872
+ if (newUserOrdinals.size === 0) {
17624
17873
  log2("[dreamer] retrospective: only overlap lines, nothing new");
17625
- return completedWindow(scan.maxScannedTs);
17874
+ return completedWindow(scan.maxScannedTs, "only overlap lines, nothing new");
17875
+ }
17876
+ const postWatermarkOrdinals = new Set(userMessages.filter((message) => message.ts > watermarkMs && message.followsAssistantReply === true).map((message) => message.ordinal));
17877
+ if (postWatermarkOrdinals.size === 0) {
17878
+ const noOpReason = "no reply-following user lines";
17879
+ log2(`[dreamer] retrospective: ${noOpReason}`);
17880
+ return completedWindow(scan.maxScannedTs, noOpReason);
17626
17881
  }
17627
17882
  const abortController = new AbortController;
17628
17883
  let leaseLost = false;
@@ -19574,31 +19829,6 @@ function emptyPiModelChainsKey(chains) {
19574
19829
  return chains.map((chain) => `${chain.owner}=${chain.dropped.map((dropped) => dropped.model).join("|")}`).join(";");
19575
19830
  }
19576
19831
 
19577
- // src/subagent-runner.ts
19578
- import * as childProcess from "node:child_process";
19579
- import {
19580
- existsSync as existsSync10,
19581
- mkdtempSync as mkdtempSync2,
19582
- readFileSync as readFileSync9,
19583
- realpathSync as realpathSync3,
19584
- rmSync as rmSync2,
19585
- statSync as statSync4,
19586
- writeFileSync as writeFileSync4
19587
- } from "node:fs";
19588
- import { createRequire } from "node:module";
19589
- import { homedir, tmpdir as tmpdir2 } from "node:os";
19590
- import {
19591
- basename,
19592
- dirname as dirname2,
19593
- isAbsolute,
19594
- join as join10,
19595
- resolve as resolvePath,
19596
- sep
19597
- } from "node:path";
19598
- import { createInterface } from "node:readline";
19599
- import { StringDecoder } from "node:string_decoder";
19600
- import { fileURLToPath } from "node:url";
19601
-
19602
19832
  // src/pi-zen-free-tier.ts
19603
19833
  var ZEN_FREE_TIER_FAILURE = "opencode_zen_free_tier";
19604
19834
  function isZenModel(model) {
@@ -19631,6 +19861,29 @@ function classifyZenFreeTierFailure(result, model, agent, harness) {
19631
19861
  }
19632
19862
 
19633
19863
  // src/subagent-runner.ts
19864
+ import * as childProcess from "node:child_process";
19865
+ import {
19866
+ existsSync as existsSync10,
19867
+ mkdtempSync as mkdtempSync2,
19868
+ readFileSync as readFileSync9,
19869
+ realpathSync as realpathSync3,
19870
+ rmSync as rmSync2,
19871
+ statSync as statSync4,
19872
+ writeFileSync as writeFileSync4
19873
+ } from "node:fs";
19874
+ import { createRequire } from "node:module";
19875
+ import { homedir, tmpdir as tmpdir2 } from "node:os";
19876
+ import {
19877
+ basename,
19878
+ dirname as dirname2,
19879
+ isAbsolute,
19880
+ join as join10,
19881
+ resolve as resolvePath,
19882
+ sep
19883
+ } from "node:path";
19884
+ import { createInterface } from "node:readline";
19885
+ import { StringDecoder } from "node:string_decoder";
19886
+ import { fileURLToPath } from "node:url";
19634
19887
  var PI_CODING_AGENT_MODULE = "@earendil-works/pi-coding-agent";
19635
19888
  var PI_CODING_AGENT_PACKAGE_NAMES = new Set([
19636
19889
  PI_CODING_AGENT_MODULE,
@@ -21765,8 +22018,17 @@ class PiRetrospectiveRawProvider {
21765
22018
  }
21766
22019
  const entries = this.deps.loadEntriesFromFile ? await this.deps.loadEntriesFromFile(filePath) : asyncSessionEntries(filePath);
21767
22020
  let ordinal = 0;
22021
+ let sawUserMessage = false;
22022
+ let sawAssistantReply = false;
21768
22023
  for await (const entry of entries) {
21769
- const row = normalizePiUserEntry(entry, sessionId, ++ordinal);
22024
+ ordinal++;
22025
+ let row = null;
22026
+ if (isPiUserEntry(entry)) {
22027
+ row = normalizePiUserEntry(entry, sessionId, ordinal, sawUserMessage && sawAssistantReply);
22028
+ sawUserMessage = true;
22029
+ } else if (sawUserMessage && isPiAssistantEntry(entry)) {
22030
+ sawAssistantReply = true;
22031
+ }
21770
22032
  if (!row || !eligible(row.ts))
21771
22033
  continue;
21772
22034
  kept.push(row);
@@ -21784,7 +22046,7 @@ class PiRetrospectiveRawProvider {
21784
22046
  return kept;
21785
22047
  }
21786
22048
  }
21787
- function normalizePiUserEntry(entry, sessionId, ordinal) {
22049
+ function normalizePiUserEntry(entry, sessionId, ordinal, followsAssistantReply) {
21788
22050
  const e = entry;
21789
22051
  if (!e || typeof e !== "object" || e.type !== "message")
21790
22052
  return null;
@@ -21801,9 +22063,23 @@ function normalizePiUserEntry(entry, sessionId, ordinal) {
21801
22063
  ordinal,
21802
22064
  role: "user",
21803
22065
  text,
21804
- ts: message.timestamp
22066
+ ts: message.timestamp,
22067
+ ...followsAssistantReply ? { followsAssistantReply: true } : {}
21805
22068
  };
21806
22069
  }
22070
+ function isPiUserEntry(entry) {
22071
+ return piMessageRole(entry) === "user";
22072
+ }
22073
+ function isPiAssistantEntry(entry) {
22074
+ return piMessageRole(entry) === "assistant";
22075
+ }
22076
+ function piMessageRole(entry) {
22077
+ const e = entry;
22078
+ if (!e || typeof e !== "object" || e.type !== "message")
22079
+ return null;
22080
+ const message = e.message;
22081
+ return message && typeof message === "object" ? message.role : null;
22082
+ }
21807
22083
  function extractPiTextContent(content) {
21808
22084
  if (typeof content === "string")
21809
22085
  return content;
@@ -22097,6 +22373,8 @@ function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRe
22097
22373
  accountingSubagent: "dreamer",
22098
22374
  accountingTask: accountingTaskFromTitle(dreamSession.title),
22099
22375
  tokenBudget: (() => {
22376
+ if (args.tokenBudget !== undefined)
22377
+ return args.tokenBudget;
22100
22378
  const task = accountingTaskFromTitle(dreamSession.title);
22101
22379
  if (!task)
22102
22380
  return;
@@ -22107,8 +22385,15 @@ function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRe
22107
22385
  try {
22108
22386
  const result = await runPromise;
22109
22387
  const budget = result.meta?.tokenBudget;
22110
- if (budget)
22111
- childBudgetStates.set(sessionId, budget);
22388
+ if (budget) {
22389
+ const prior = childBudgetStates.get(sessionId);
22390
+ childBudgetStates.set(sessionId, {
22391
+ ...budget,
22392
+ budget: prior?.budget ?? budget.budget,
22393
+ spent: (prior?.spent ?? 0) + budget.spent,
22394
+ finalizeFired: (prior?.finalizeFired ?? false) || budget.finalizeFired
22395
+ });
22396
+ }
22112
22397
  const budgetTask = accountingTaskFromTitle(dreamSession.title);
22113
22398
  if (budgetTask && budget && typeof budget.spent === "number") {
22114
22399
  const prior = budgetStates.get(budgetTask);
@@ -22119,8 +22404,15 @@ function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRe
22119
22404
  }
22120
22405
  assertRegistrationOwnerActive();
22121
22406
  const mappingMessages = result.meta?.mappingMessages;
22122
- if (budgetTask === "map-memories" && Array.isArray(mappingMessages)) {
22407
+ const isVerification = budgetTask === "verify" || budgetTask === "verify-broad";
22408
+ const hasMemoryDecisions = (budgetTask === "map-memories" || isVerification) && result.meta?.failureClass !== ZEN_FREE_TIER_FAILURE && Array.isArray(mappingMessages);
22409
+ if (hasMemoryDecisions) {
22123
22410
  dreamSession.messages = mappingMessages.filter(isRecord).filter((message) => message.role === "assistant").map((message) => makeMessage("assistant", Array.isArray(message.content) ? message.content.filter(isRecord).flatMap((part) => part.type === "text" && typeof part.text === "string" ? [{ type: "text", text: part.text }] : part.type === "toolCall" ? syntheticToolParts(1) : []) : []));
22411
+ if (result.ok && result.assistantText?.trim() && !dreamSession.messages.some((message) => extractLatestAssistantText([message])?.trim())) {
22412
+ dreamSession.messages.push(makeMessage("assistant", [
22413
+ { type: "text", text: result.assistantText }
22414
+ ]));
22415
+ }
22124
22416
  }
22125
22417
  if (!result.ok) {
22126
22418
  if (result.reason === "token_budget") {
@@ -22135,7 +22427,7 @@ function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRe
22135
22427
  }
22136
22428
  throw error;
22137
22429
  }
22138
- if (!(budgetTask === "map-memories" && Array.isArray(mappingMessages)))
22430
+ if (!hasMemoryDecisions)
22139
22431
  dreamSession.messages = [
22140
22432
  makeMessage("user", [{ type: "text", text: userMessage }]),
22141
22433
  makeMessage("assistant", [
@@ -22534,6 +22826,85 @@ Unknown task "${requested}".`,
22534
22826
  });
22535
22827
  }
22536
22828
 
22829
+ // ../plugin/src/hooks/magic-context/auto-embed-session.ts
22830
+ function embeddingIdentity(projectIdentity) {
22831
+ const snapshot = getProjectEmbeddingSnapshot2(projectIdentity);
22832
+ return JSON.stringify([
22833
+ projectIdentity,
22834
+ snapshot?.providerIdentity,
22835
+ snapshot?.chunkModelId,
22836
+ snapshot?.runtimeFingerprint
22837
+ ]);
22838
+ }
22839
+ function maybeAutoEmbedSessionBackfill(deps, sessionId) {
22840
+ const markActive = () => activeEmbeddingSessions.set(sessionId, {
22841
+ project: deps.projectIdentity,
22842
+ model: getProjectEmbeddingSnapshot2(deps.projectIdentity)?.chunkModelId ?? "off"
22843
+ });
22844
+ markActive();
22845
+ const identity = embeddingIdentity(deps.projectIdentity);
22846
+ if (autoEmbedIdentityBySession.get(sessionId) !== identity) {
22847
+ invalidateAutoEmbedSession(sessionId);
22848
+ autoEmbedIdentityBySession.set(sessionId, identity);
22849
+ }
22850
+ if (autoEmbedAttemptedBySession.has(sessionId) || embedPauseBySession.has(sessionId) || embedRunStateBySession.has(sessionId) || Date.now() < (autoEmbedRetryAfterBySession.get(sessionId) ?? 0))
22851
+ return;
22852
+ const controller = new AbortController;
22853
+ embedRunStateBySession.set(sessionId, controller);
22854
+ (async () => {
22855
+ let complete = false;
22856
+ let retryDelay = 1000;
22857
+ let started = false;
22858
+ let reason = "error";
22859
+ let registeredIdentity = identity;
22860
+ try {
22861
+ await new Promise((resolve) => setTimeout(resolve, 0));
22862
+ if (controller.signal.aborted)
22863
+ return;
22864
+ await deps.ensureRegistered();
22865
+ if (controller.signal.aborted)
22866
+ return;
22867
+ markActive();
22868
+ registeredIdentity = embeddingIdentity(deps.projectIdentity);
22869
+ autoEmbedIdentityBySession.set(sessionId, registeredIdentity);
22870
+ const coverage = await getAutoEmbeddingSessionCoverage2(deps.db, deps.projectIdentity, sessionId);
22871
+ if (controller.signal.aborted || !coverage.enabled)
22872
+ return;
22873
+ if (coverage.embedded === coverage.total) {
22874
+ complete = true;
22875
+ return;
22876
+ }
22877
+ const model = getProjectEmbeddingSnapshot2(deps.projectIdentity)?.model;
22878
+ log2(`[magic-context] session backfill start session=${sessionId} missing=${coverage.total - coverage.embedded} model=${model}`);
22879
+ started = true;
22880
+ const outcome = await embedSessionCompartmentChunks2(deps.db, deps.projectIdentity, sessionId, { signal: controller.signal, automatic: true });
22881
+ reason = outcome.status;
22882
+ const after = await getAutoEmbeddingSessionCoverage2(deps.db, deps.projectIdentity, sessionId);
22883
+ complete = after.enabled && after.embedded === after.total;
22884
+ if (!complete && outcome.status !== "busy" && outcome.status !== "aborted") {
22885
+ retryDelay = 60000;
22886
+ if (outcome.status === "nothing" || outcome.status === "done")
22887
+ reason = "deferred";
22888
+ }
22889
+ } catch (error) {
22890
+ log2("[magic-context] auto-embed drain failed:", error);
22891
+ retryDelay = 60000;
22892
+ } finally {
22893
+ if (started)
22894
+ log2(`[magic-context] session backfill ${complete ? "complete" : "stop"} session=${sessionId} reason=${reason}`);
22895
+ if (embedRunStateBySession.get(sessionId) === controller) {
22896
+ embedRunStateBySession.delete(sessionId);
22897
+ }
22898
+ if (!controller.signal.aborted && autoEmbedIdentityBySession.get(sessionId) === registeredIdentity && embeddingIdentity(deps.projectIdentity) === registeredIdentity) {
22899
+ if (complete)
22900
+ autoEmbedAttemptedBySession.add(sessionId);
22901
+ else
22902
+ autoEmbedRetryAfterBySession.set(sessionId, Date.now() + retryDelay);
22903
+ }
22904
+ }
22905
+ })();
22906
+ }
22907
+
22537
22908
  // ../plugin/src/hooks/magic-context/format-embed-failure.ts
22538
22909
  function formatEmbedFailureSummary(embedded, remaining, failure, style = "markdown") {
22539
22910
  const historyBlocks = `history block${embedded === 1 ? "" : "s"}`;
@@ -22591,6 +22962,7 @@ function formatEmbedStatusText(coverage, drain) {
22591
22962
  var EMBED_PROGRESS_COMPARTMENT_STEP = 8;
22592
22963
  var EMBED_PROGRESS_MIN_INTERVAL_MS = 1e4;
22593
22964
  function clearPiEmbedSessionState(sessionId) {
22965
+ activeEmbeddingSessions.delete(sessionId);
22594
22966
  embedPauseBySession.delete(sessionId);
22595
22967
  const ctrl = embedRunStateBySession.get(sessionId);
22596
22968
  if (ctrl) {
@@ -22779,35 +23151,11 @@ ${statusText}`,
22779
23151
  function maybeAutoEmbedPiSession(deps, sessionId, projectDir, projectIdentity) {
22780
23152
  if (!isUsableProjectIdentity(projectIdentity))
22781
23153
  return;
22782
- const snapshot = getProjectEmbeddingSnapshot2(projectIdentity);
22783
- const embedIdentity = snapshot ? JSON.stringify([
22784
- snapshot.providerIdentity,
22785
- snapshot.chunkModelId,
22786
- snapshot.runtimeFingerprint
22787
- ]) : "off";
22788
- if (autoEmbedIdentityBySession.get(sessionId) !== embedIdentity)
22789
- invalidateAutoEmbedSession(sessionId);
22790
- if (autoEmbedAttemptedBySession.has(sessionId))
22791
- return;
22792
- if (embedPauseBySession.has(sessionId))
22793
- return;
22794
- autoEmbedAttemptedBySession.add(sessionId);
22795
- autoEmbedIdentityBySession.set(sessionId, embedIdentity);
22796
- (async () => {
22797
- try {
22798
- await new Promise((resolve) => setTimeout(resolve, 0));
22799
- await ensureProjectRegisteredFromPiDirectory(projectDir, deps.db);
22800
- const coverage = await getAutoEmbeddingSessionCoverage2(deps.db, projectIdentity, sessionId);
22801
- if (!coverage.enabled) {
22802
- invalidateAutoEmbedSession(sessionId);
22803
- return;
22804
- }
22805
- const remaining = coverage.total - coverage.embedded;
22806
- if (remaining <= 0)
22807
- return;
22808
- await runEmbedDrain(deps.db, projectIdentity, sessionId);
22809
- } catch {}
22810
- })();
23154
+ maybeAutoEmbedSessionBackfill({
23155
+ db: deps.db,
23156
+ projectIdentity,
23157
+ ensureRegistered: () => ensureProjectRegisteredFromPiDirectory(projectDir, deps.db)
23158
+ }, sessionId);
22811
23159
  }
22812
23160
 
22813
23161
  // ../plugin/src/hooks/magic-context/execute-flush.ts
@@ -38802,7 +39150,7 @@ class PiThinkingBindingRecoveryRefusal extends EmergencyFailClosedError {
38802
39150
  function applyPiThinkingBindingRecovery(args) {
38803
39151
  const protectedMessages = latestAssistantTurnMessages(args.messages);
38804
39152
  const frozenEntryIds = frozenBindingEntryIds(args.db, args.sessionId);
38805
- const flagTargetCandidate = isPrefixBoundThinkingModel(args.provider, args.model) ? getThinkingBindingRecoveryTarget(args.db, args.sessionId) : null;
39153
+ const flagTargetCandidate = !args.replayOnly && isPrefixBoundThinkingModel(args.provider, args.model) ? getThinkingBindingRecoveryTarget(args.db, args.sessionId) : null;
38806
39154
  const flagTarget = flagTargetCandidate?.startsWith(LATEST_THINKING_RESTORE) ? null : flagTargetCandidate;
38807
39155
  let applied = null;
38808
39156
  if (flagTarget) {
@@ -42239,6 +42587,7 @@ function registerPiContextHandler(pi, baseOptions) {
42239
42587
  const scheduler = schedulerFor(options);
42240
42588
  lkgCompactionOff = options.compactionOff === true;
42241
42589
  const projectIdentity = resolveProjectIdentityForSession(projectDirectory, options.allowHomeProject) ?? "";
42590
+ withoutSqliteTransformPass2(() => options.maybeAutoEmbedSession?.(sessionId, projectDirectory, projectIdentity));
42242
42591
  logTransformTiming(sessionId, "findSessionId", tFindSession, `messages=${event.messages.length}`);
42243
42592
  budget.stage = "branch projection";
42244
42593
  const tEntryBranch = performance.now();
@@ -42973,14 +43322,15 @@ function registerPiContextHandler(pi, baseOptions) {
42973
43322
  hostEditBeforeNewestThinking = stickyBefore === null || [...stickyAfter].some((key) => !stickyBefore.has(key)) || JSON.stringify(getPersistedTodoSyntheticAnchor(options.db, sessionId)) !== todoAnchorBefore;
42974
43323
  } catch {}
42975
43324
  const outputEntryIds = resolvePiLkgOutputEntryIds(outputMessages, result.syntheticLeadingCount, (message) => result.lkgEntryIdByRef.get(message) ?? result.postCommitEntryIdByRef.get(message) ?? bindingEntryIdByRef.get(message)).map((entryId) => entryId ?? undefined);
42976
- const thinkingBindingRecoveryApplied = bindingStripOrder === "end" && (!result.signedPrefixRewritten || !result.prefixBustPermitted) ? applyPiThinkingBindingRecovery({
43325
+ const thinkingBindingRecoveryApplied = bindingStripOrder === "end" ? applyPiThinkingBindingRecovery({
42977
43326
  db: options.db,
42978
43327
  sessionId,
42979
43328
  messages: outputMessages,
42980
43329
  entryIds: outputEntryIds,
42981
43330
  provider: lkgProviderKey ?? undefined,
42982
43331
  model: ctx.model?.id,
42983
- endOfPassOrder: true
43332
+ endOfPassOrder: true,
43333
+ replayOnly: result.signedPrefixRewritten && result.prefixBustPermitted
42984
43334
  }) : startOfPassBindingRecovery;
42985
43335
  if (shouldRunPiProactiveThinkingStrip({
42986
43336
  compactionOff: options.compactionOff === true,
@@ -43135,8 +43485,6 @@ function registerPiContextHandler(pi, baseOptions) {
43135
43485
  logTransformTiming(sessionId, "stableIdSchemePersist", tStableIdSchemePersist);
43136
43486
  logTransformTiming(sessionId, "postTransformPhase", tPostTransform);
43137
43487
  clearLastTransformErrorIfSet(options.db, sessionId, sessionMeta.lastTransformError);
43138
- if (budget.remainingWork() > PI_CONTEXT_BUDGET.completionReserveMs)
43139
- withoutSqliteTransformPass2(() => options.maybeAutoEmbedSession?.(sessionId, projectDirectory, projectIdentity));
43140
43488
  logTransformTiming(sessionId, "postPipelineTotal", postPipelineStart);
43141
43489
  const transformElapsedMs = performance.now() - transformStartTime;
43142
43490
  recordPiTransformTiming({
@@ -44566,7 +44914,7 @@ async function runPipeline(args) {
44566
44914
  ...message,
44567
44915
  parts: message.parts
44568
44916
  }));
44569
- const recoveryNormalized = !!args.recoveryPrefix && (foldBustsServedPrefixThisPass || firstRenderBust || identityRebuild);
44917
+ const recoveryNormalized = !!args.recoveryPrefix && (isCacheBustingPass || identityRebuild);
44570
44918
  const taggingView = args.recoveryPrefix && !recoveryNormalized ? mutationView.filter((message) => !args.recoveryPrefix?.untaggedInputIds.has(message.info.id ?? "")) : mutationView;
44571
44919
  let tagTextTokenCache = piTagTextTokenCacheBySession.get(args.sessionId);
44572
44920
  if (!tagTextTokenCache) {