@cortexkit/pi-magic-context 0.49.8 → 0.49.10

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
@@ -250,7 +250,7 @@ import {
250
250
  registerTodoStateLifecycle,
251
251
  syncCtxMemoryToolEnabled,
252
252
  registerMagicContextTools
253
- } from "./index-jsyjxgfy.js";
253
+ } from "./index-rkwh9kce.js";
254
254
  import {
255
255
  canonicalModelIdentity,
256
256
  piModelRefToCanonical,
@@ -290,7 +290,7 @@ import {
290
290
  openCurrentDatabase,
291
291
  openDatabaseAsync,
292
292
  closeDatabase
293
- } from "./index-rggtvnbg.js";
293
+ } from "./index-243wh86p.js";
294
294
  import {
295
295
  setHomeProjectPermission,
296
296
  ProjectIdentityError,
@@ -438,6 +438,7 @@ import {
438
438
  reasoningBudgetCutoff,
439
439
  reasoningTextAndOpaque,
440
440
  opaqueThinkingCost,
441
+ estimatePartImageTokens,
441
442
  SKELETON_REAL_INPUT_MAX_BYTES,
442
443
  toolInputStringBytes,
443
444
  hasRawMessageProvider2,
@@ -459,7 +460,7 @@ import {
459
460
  getRawSessionTagKeysThrough2,
460
461
  getLegacyProtectedTailStartOrdinal2,
461
462
  readSessionChunk2
462
- } from "./index-18qe4gs1.js";
463
+ } from "./index-9vzkrnrf.js";
463
464
  import {
464
465
  UNKNOWN_FIT_RATIO,
465
466
  hasMeasuredDecisionCalibration,
@@ -476,10 +477,14 @@ import {
476
477
  stripTagPrefix,
477
478
  peelLeadingMcTagNotation,
478
479
  prependTag,
480
+ rememberCurrentToolOutputCount,
481
+ currentReclaimableToolOutputCount,
479
482
  isRecord,
480
483
  TEXT_TAG_IDENTITY_MARKER,
481
484
  PI_FALLBACK_PREFIXES_JSON,
482
485
  contentTagOwnerMessageId,
486
+ TailHygieneJsonMemo,
487
+ piJsonHash,
483
488
  estimateImageTokensFromDataUrl,
484
489
  normalizeTodoStateJson,
485
490
  buildSyntheticTodoPart,
@@ -492,7 +497,6 @@ import {
492
497
  CHANNEL1_FLOOR_TOKENS,
493
498
  decideChannel1,
494
499
  evaluateChannel2,
495
- reclaimableToolOutputCount,
496
500
  buildChannel2Reminder,
497
501
  buildChannel1Reminder,
498
502
  planEmergencyDrop,
@@ -542,7 +546,7 @@ import {
542
546
  getToolTagNumberByOwner2,
543
547
  getNullOwnerToolTag2,
544
548
  adoptNullOwnerToolTag2
545
- } from "./index-54r5vs9w.js";
549
+ } from "./index-x7426vy4.js";
546
550
  import {
547
551
  textMentionsRecentCommit,
548
552
  hasMeaningfulUserText2,
@@ -579,7 +583,7 @@ import {
579
583
  tagRevisionKey,
580
584
  tagIdentityRevisionKey,
581
585
  TAG_INSERT_CONFLICT_ROWS
582
- } from "./index-zzn7qbps.js";
586
+ } from "./index-ttxsmaj5.js";
583
587
  import {
584
588
  resolveOpenCodeDbPath,
585
589
  assertOpenCodeStoreGeneration,
@@ -618,7 +622,7 @@ import {
618
622
  indexSingleMessage2,
619
623
  indexMessagesAfterOrdinal2,
620
624
  sweepOrphanedOpenCodeMessageIndexes2
621
- } from "./index-tdhppxwd.js";
625
+ } from "./index-h160xxaq.js";
622
626
  import {
623
627
  formatSynapseLaneDescriptor
624
628
  } from "./index-szh001qb.js";
@@ -660,6 +664,9 @@ import {
660
664
  getEmbeddingCoverageStatus2,
661
665
  getAutoEmbeddingSessionCoverage2
662
666
  } from "./index-qc8e13wc.js";
667
+ import {
668
+ rememberV1HiddenRun
669
+ } from "./index-6x556c2q.js";
663
670
  import {
664
671
  V2_MEMORY_CATEGORIES
665
672
  } from "./index-135e8hvg.js";
@@ -671,7 +678,7 @@ import {
671
678
  expandWorkspaceIdentitySetWithAliases,
672
679
  sourceNameForMemory,
673
680
  computeWorkspaceEpochFingerprint
674
- } from "./index-r8cy8hmq.js";
681
+ } from "./index-g13fb2sj.js";
675
682
  import {
676
683
  configureContextDatabasePragmas2
677
684
  } from "./index-fapenpch.js";
@@ -721,7 +728,7 @@ import {
721
728
  normalizeVerificationFiles,
722
729
  indexCommitsForProject,
723
730
  embedUnembeddedCommits
724
- } from "./index-pdhbj18g.js";
731
+ } from "./index-ns319t8e.js";
725
732
  import {
726
733
  setEmbeddingSessionBusy2,
727
734
  isEmbeddingHostBusy2
@@ -763,7 +770,7 @@ import {
763
770
  subagentStepCap,
764
771
  SubagentTelemetry,
765
772
  readHostSystemPrompt
766
- } from "./index-zh1jw8qp.js";
773
+ } from "./index-xmdkde4v.js";
767
774
  import"./index-kh3jt7t9.js";
768
775
  import {
769
776
  __toESM
@@ -3425,7 +3432,7 @@ function startHostStallProfilerSwitch() {
3425
3432
  // package.json
3426
3433
  var package_default = {
3427
3434
  name: "@cortexkit/pi-magic-context",
3428
- version: "0.49.8",
3435
+ version: "0.49.10",
3429
3436
  type: "module",
3430
3437
  description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
3431
3438
  main: "dist/index.js",
@@ -8968,6 +8975,8 @@ function createV1HiddenCompletionExecutor(client, db, directory) {
8968
8975
  preferResponseOnMissingData: true
8969
8976
  });
8970
8977
  const id = typeof created?.id === "string" ? created.id : "";
8978
+ if (id)
8979
+ rememberV1HiddenRun(id, run);
8971
8980
  if (id && run.kind !== "dreamer-task") {
8972
8981
  historianChildren.add(id);
8973
8982
  rememberHistorianOutputCap(id, run.maxOutputTokens);
@@ -9018,6 +9027,7 @@ function createV1HiddenCompletionExecutor(client, db, directory) {
9018
9027
  },
9019
9028
  async close(handle, settlement) {
9020
9029
  if (handle?.id) {
9030
+ rememberV1HiddenRun(handle.id, undefined);
9021
9031
  historianChildren.delete(handle.id);
9022
9032
  forgetHistorianOutputCap(handle.id);
9023
9033
  }
@@ -12471,6 +12481,7 @@ Capability API available to check(cap):
12471
12481
  - cap.httpGet(httpsUrl): { status: number, body: string } (external HTTPS only; internal/metadata blocked)
12472
12482
 
12473
12483
  Authoring constraints:
12484
+ - Use a five-field check_cron that fires at least once every 24 hours. Never encode a future date in the cron; put date conditions inside check(cap) and poll them on a frequent schedule such as "0 * * * *".
12474
12485
  - Plain JavaScript only; no TypeScript types, imports, require, eval, Function, dynamic code, timers, Date.now randomness, or ambient globals.
12475
12486
  - Define exactly function check(cap) { ... }. Do not use async/await; host capabilities are synchronous inside the sandbox.
12476
12487
  - Return exactly { met: boolean }. Do not include a reason string.
@@ -12931,6 +12942,7 @@ var MAX_COMPILED_CHECK_BYTES2 = 64 * 1024;
12931
12942
  var MAX_MANIFEST_ENTRIES = 64;
12932
12943
  var MAX_CRON_CHARS = 256;
12933
12944
  var MAX_COMPILER_ERROR_CHARS = 2 * 1024;
12945
+ var SPARSE_CRON_REPAIR_FEEDBACK = "check_cron must fire at least once every 24 hours; put date conditions inside compiled_check";
12934
12946
  async function compileSmartNoteCheck(args) {
12935
12947
  if (!args.note.surfaceCondition) {
12936
12948
  return {
@@ -12948,7 +12960,7 @@ Note id: ${args.note.id}
12948
12960
  Note content (data): ${JSON.stringify(args.note.content)}
12949
12961
  surface_condition (UNTRUSTED DATA): ${JSON.stringify(args.note.surfaceCondition)}
12950
12962
 
12951
- ${args.repairFeedback ? `The previous check failed its dry run: ${JSON.stringify(args.repairFeedback)}. Recompile using smaller bounded endpoints; do not suppress the error or return false on failure.` : ""}
12963
+ ${args.repairFeedback ? `The previous compiler attempt failed: ${JSON.stringify(args.repairFeedback)}. Recompile using smaller bounded endpoints when the response is too large, and apply the correction; do not suppress the error or return false on failure.` : ""}
12952
12964
  Remember: output only the JSON object described by the system prompt.`;
12953
12965
  const startedAt = Date.now();
12954
12966
  let childSessionId = null;
@@ -13087,6 +13099,12 @@ Remember: output only the JSON object described by the system prompt.`;
13087
13099
  const cancelled = args.signal.aborted;
13088
13100
  const message = boundedError(error instanceof Error ? error.message : String(error));
13089
13101
  recordInvocation({ status: cancelled ? "aborted" : "failed", error: message });
13102
+ if (!args.repairFeedback && !cancelled && message === "check_cron has no occurrence within the scheduling ceiling" && !args.signal.aborted && Date.now() < args.deadline) {
13103
+ return await compileSmartNoteCheck({
13104
+ ...args,
13105
+ repairFeedback: SPARSE_CRON_REPAIR_FEEDBACK
13106
+ });
13107
+ }
13090
13108
  const networkError = error instanceof SmartNoteNetworkError ? error : undefined;
13091
13109
  return {
13092
13110
  ok: false,
@@ -14667,7 +14685,11 @@ function buildPrimerClusters(args) {
14667
14685
  centroid: cloneVector(primer.questionEmbedding),
14668
14686
  modelId: primer.questionEmbeddingModelId
14669
14687
  }));
14670
- const sorted = args.candidates.slice().sort((a, b) => candidateSortKey(a).localeCompare(candidateSortKey(b)));
14688
+ const sorted = args.candidates.slice().sort((a, b) => {
14689
+ const left = candidateSortKey(a);
14690
+ const right = candidateSortKey(b);
14691
+ return left < right ? -1 : left > right ? 1 : 0;
14692
+ });
14671
14693
  for (const candidate of sorted) {
14672
14694
  let best = null;
14673
14695
  for (const cluster of clusters) {
@@ -19460,6 +19482,39 @@ import {
19460
19482
  import { createInterface } from "node:readline";
19461
19483
  import { StringDecoder } from "node:string_decoder";
19462
19484
  import { fileURLToPath } from "node:url";
19485
+
19486
+ // src/pi-zen-free-tier.ts
19487
+ var ZEN_FREE_TIER_FAILURE = "opencode_zen_free_tier";
19488
+ function isZenModel(model) {
19489
+ return model !== undefined && canonicalModelIdentity(model).startsWith("opencode/");
19490
+ }
19491
+ function isZenFreeTierRefusal(text) {
19492
+ return /\bFreeTierError\b/.test(text) || /\b403\b/.test(text) && /OpenCode's free tier can only be used from within OpenCode/i.test(text);
19493
+ }
19494
+ function zenFreeTierMessage(agent, harness) {
19495
+ const task = agent.includes("historian") ? "historian" : "dreamer";
19496
+ const host = harness === "omp" ? "Oh My Pi" : "Pi";
19497
+ return `${ZEN_FREE_TIER_FAILURE}: OpenCode Zen free models only run inside OpenCode; choose another ${task} model for ${host} (FreeTierError).`;
19498
+ }
19499
+ function classifyZenFreeTierFailure(result, model, agent, harness) {
19500
+ if (result.ok || !isZenModel(model))
19501
+ return result;
19502
+ const stderr = typeof result.meta?.stderr === "string" ? result.meta.stderr : "";
19503
+ if (!isZenFreeTierRefusal(result.error) && !isZenFreeTierRefusal(stderr))
19504
+ return result;
19505
+ return {
19506
+ ...result,
19507
+ error: zenFreeTierMessage(agent, harness),
19508
+ meta: {
19509
+ failureClass: ZEN_FREE_TIER_FAILURE,
19510
+ errorClass: "FreeTierError",
19511
+ ...typeof result.meta?.exitCode === "number" ? { exitCode: result.meta.exitCode } : {},
19512
+ ...typeof result.meta?.sawProtocolOutput === "boolean" ? { sawProtocolOutput: result.meta.sawProtocolOutput } : {}
19513
+ }
19514
+ };
19515
+ }
19516
+
19517
+ // src/subagent-runner.ts
19463
19518
  var PI_CODING_AGENT_MODULE = "@earendil-works/pi-coding-agent";
19464
19519
  var PI_CODING_AGENT_PACKAGE_NAMES = new Set([
19465
19520
  PI_CODING_AGENT_MODULE,
@@ -20011,6 +20066,9 @@ class PiSubagentRunner {
20011
20066
  }
20012
20067
  async runOnce(options, runMode, modelRefOverride) {
20013
20068
  const startTime = Date.now();
20069
+ const zenChild = STRICT_TOOL_ALLOWLIST.has(options.agent) && isZenModel(options.model);
20070
+ let deferredZenStderr = "";
20071
+ let reportedZenStderr = false;
20014
20072
  let recordedAccounting = false;
20015
20073
  const cap = subagentStepCap(options.agent);
20016
20074
  const budget = options.tokenBudget ? createDreamTokenBudget(options.tokenBudget) : undefined;
@@ -20034,7 +20092,7 @@ class PiSubagentRunner {
20034
20092
  subagent: options.accountingSubagent ?? inferAccountingSubagent(options.agent),
20035
20093
  task: options.accountingTask ?? null,
20036
20094
  startedAt: startTime,
20037
- status: result.ok ? "completed" : result.reason === "abort" ? "aborted" : result.reason === "timeout" ? "timed_out" : result.reason === "no_assistant" || result.reason === "truncated" ? "empty" : "failed",
20095
+ status: result.ok ? "completed" : result.meta?.failureClass === ZEN_FREE_TIER_FAILURE ? "failed" : result.reason === "abort" ? "aborted" : result.reason === "timeout" ? "timed_out" : result.reason === "no_assistant" || result.reason === "truncated" ? "empty" : "failed",
20038
20096
  messages,
20039
20097
  providerId: hasProvider ? modelRef?.slice(0, providerSeparator) : undefined,
20040
20098
  modelId: hasProvider ? modelRef?.slice(providerSeparator + 1) : modelRef,
@@ -20139,7 +20197,20 @@ class PiSubagentRunner {
20139
20197
  if (settled)
20140
20198
  return;
20141
20199
  settled = true;
20142
- if (isMemoryMapper) {
20200
+ if (zenChild) {
20201
+ const classified = classifyZenFreeTierFailure(result, options.model, options.agent, this.invocation.targetHarness);
20202
+ if (classified !== result && !classified.ok) {
20203
+ sessionLog2(options.accountingSessionId ?? "subagent", classified.error);
20204
+ }
20205
+ result = classified;
20206
+ if (deferredZenStderr && !reportedZenStderr) {
20207
+ emitProgress({
20208
+ type: "stderr",
20209
+ chunk: result.meta?.failureClass === ZEN_FREE_TIER_FAILURE ? zenFreeTierMessage(options.agent, this.invocation.targetHarness) : deferredZenStderr
20210
+ });
20211
+ }
20212
+ }
20213
+ if (isMemoryMapper && result.meta?.failureClass !== ZEN_FREE_TIER_FAILURE) {
20143
20214
  result = {
20144
20215
  ...result,
20145
20216
  meta: { ...result.meta, mappingMessages: [...mappingMessages] }
@@ -20169,6 +20240,12 @@ class PiSubagentRunner {
20169
20240
  const emitProgress = (event) => {
20170
20241
  if (!options.onProgress)
20171
20242
  return;
20243
+ if (zenChild && event.type === "raw_event" && isZenFreeTierRefusal(JSON.stringify(event.event))) {
20244
+ event = {
20245
+ ...event,
20246
+ event: { type: event.eventType, error: { type: "FreeTierError" } }
20247
+ };
20248
+ }
20172
20249
  try {
20173
20250
  options.onProgress(event);
20174
20251
  } catch {}
@@ -20282,7 +20359,17 @@ class PiSubagentRunner {
20282
20359
  if (stderr.length > 16000) {
20283
20360
  stderr = `…[truncated]${stderr.slice(-16000)}`;
20284
20361
  }
20285
- emitProgress({ type: "stderr", chunk: text });
20362
+ if (zenChild) {
20363
+ deferredZenStderr = stderr;
20364
+ if (!reportedZenStderr && isZenFreeTierRefusal(stderr)) {
20365
+ reportedZenStderr = true;
20366
+ emitProgress({
20367
+ type: "stderr",
20368
+ chunk: zenFreeTierMessage(options.agent, this.invocation.targetHarness)
20369
+ });
20370
+ }
20371
+ } else
20372
+ emitProgress({ type: "stderr", chunk: text });
20286
20373
  });
20287
20374
  child.stderr?.on("error", () => {});
20288
20375
  let finalAssistantText = null;
@@ -25711,7 +25798,7 @@ function buildEditSupersessionReclaim(input) {
25711
25798
  seenFile.add(filePath);
25712
25799
  continue;
25713
25800
  }
25714
- if (input.recentMessageIds && (tag.toolOwnerMessageId === null || input.recentMessageIds.has(tag.toolOwnerMessageId))) {
25801
+ if (tag.tagNumber > input.watermark || input.recentMessageIds && (tag.toolOwnerMessageId === null || input.recentMessageIds.has(tag.toolOwnerMessageId))) {
25715
25802
  continue;
25716
25803
  }
25717
25804
  if (realPendingTagIds.has(tag.tagNumber))
@@ -25809,6 +25896,51 @@ function readServedTemporalDecisions(db, sessionId, runtime, messageIds) {
25809
25896
  return result;
25810
25897
  }
25811
25898
 
25899
+ // ../plugin/src/shared/reclaim-distance.ts
25900
+ var RECLAIM_DISTANCE_USER_TURNS = 3;
25901
+ var RECLAIM_DISTANCE_WINDOW_FRACTION = 0.25;
25902
+ function isReclaimUserTurn(args) {
25903
+ return args.role === "user" && !args.syntheticHead && !args.synthetic && !args.ignored && args.parts.some((part) => {
25904
+ if (part.synthetic || part.ignored)
25905
+ return false;
25906
+ if (part.kind !== "text" && part.kind !== "image" && part.kind !== "file")
25907
+ return false;
25908
+ if (part.kind !== "text")
25909
+ return true;
25910
+ return stripReclaimMcText(part.text ?? "").trim().length > 0;
25911
+ });
25912
+ }
25913
+ function stripReclaimMcText(text) {
25914
+ return text.replace(/^§\d+§\s*/, "").replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, (reminder) => /^(?:Your next step: call ctx_reduce on the outputs you've already used|Still unstamped:|Housekeeping: .* are reclaimable\. Stamp each output with ctx_reduce|.* are reclaimable\. Make a ctx_reduce pass now|.* are still unstamped\. Call ctx_reduce now)/.test(reminder.slice("<system-reminder>".length).trimStart()) ? "" : reminder).replace(/<instruction name="deferred_notes">[\s\S]*?<\/instruction>/g, "");
25915
+ }
25916
+ function farBehindToolBookmark(entries, usableWindow) {
25917
+ let newerTurns = 0;
25918
+ let newerTokens = 0;
25919
+ const farTags = [];
25920
+ let firstYoungTag = Infinity;
25921
+ const seenOutputs = new Set;
25922
+ const tokenDistance = usableWindow > 0 && Number.isFinite(usableWindow) ? usableWindow * RECLAIM_DISTANCE_WINDOW_FRACTION : Infinity;
25923
+ for (let index = entries.length - 1;index >= 0; index--) {
25924
+ const entry = entries[index];
25925
+ if (!entry)
25926
+ continue;
25927
+ const tag = entry.toolOutputTag;
25928
+ if (tag !== undefined && !seenOutputs.has(tag)) {
25929
+ seenOutputs.add(tag);
25930
+ if (newerTurns >= RECLAIM_DISTANCE_USER_TURNS || newerTokens >= tokenDistance) {
25931
+ farTags.push(tag);
25932
+ } else {
25933
+ firstYoungTag = Math.min(firstYoungTag, tag);
25934
+ }
25935
+ }
25936
+ if (entry.userTurn)
25937
+ newerTurns++;
25938
+ if (Number.isFinite(entry.tokens) && entry.tokens > 0)
25939
+ newerTokens += entry.tokens;
25940
+ }
25941
+ return farTags.reduce((bookmark, tag) => tag < firstYoungTag ? Math.max(bookmark, tag) : bookmark, 0);
25942
+ }
25943
+
25812
25944
  // ../plugin/src/hooks/magic-context/tool-reclaim.ts
25813
25945
  function buildSyntheticToolReclaimOps(input) {
25814
25946
  const watermark = Math.max(0, input.watermark);
@@ -25840,10 +25972,74 @@ function buildSyntheticToolReclaimOps(input) {
25840
25972
  }
25841
25973
  return synthetic;
25842
25974
  }
25843
- function advanceToolReclaimWatermarkToCurrentMax(db, sessionId) {
25844
- const maxTagNumber = getMaxTagNumberBySession2(db, sessionId);
25845
- advanceToolReclaimWatermark(db, sessionId, maxTagNumber);
25846
- return maxTagNumber;
25975
+ function advanceToolReclaimWatermarkByDistance(db, sessionId, entries, usableWindow) {
25976
+ const bookmark = farBehindToolBookmark(entries, usableWindow);
25977
+ advanceToolReclaimWatermark(db, sessionId, bookmark);
25978
+ return bookmark;
25979
+ }
25980
+ function toolReclaimDistanceEntries(messages, targets) {
25981
+ const outputTags = new Map;
25982
+ for (const [tag, target] of targets) {
25983
+ for (const location of target.mutationParts ?? []) {
25984
+ const part = distancePart(location.part);
25985
+ if (part.kind === "tool_result" || part.kind === "tool")
25986
+ outputTags.set(location.part, tag);
25987
+ }
25988
+ }
25989
+ const entries = [];
25990
+ for (const message of messages) {
25991
+ const parts = message.parts.map(distancePart);
25992
+ let userTurn = isReclaimUserTurn({ ...message.info, parts });
25993
+ for (const [index, part] of parts.entries()) {
25994
+ const authored = isReclaimUserTurn({ role: "user", parts: [part] });
25995
+ entries.push({
25996
+ tokens: message.info.syntheticHead ? 0 : estimateTokens(stripReclaimMcText(part.text ?? "")) + part.imageTokens,
25997
+ userTurn: userTurn && authored,
25998
+ toolOutputTag: outputTags.get(message.parts[index])
25999
+ });
26000
+ if (authored)
26001
+ userTurn = false;
26002
+ }
26003
+ }
26004
+ return entries;
26005
+ }
26006
+ function distancePart(raw) {
26007
+ if (!raw || typeof raw !== "object")
26008
+ return { kind: "structural", imageTokens: 0 };
26009
+ const part = raw;
26010
+ const provenance = { synthetic: part.synthetic === true, ignored: part.ignored === true };
26011
+ if (typeof part.getText === "function") {
26012
+ const text = part.getText();
26013
+ return {
26014
+ ...provenance,
26015
+ kind: String(part.kind),
26016
+ text: typeof text === "string" ? text : undefined,
26017
+ imageTokens: typeof part.imageTokens === "function" ? Number(part.imageTokens()) : 0
26018
+ };
26019
+ }
26020
+ const metadata = { ...provenance, imageTokens: estimatePartImageTokens(raw) };
26021
+ if (part.type === "tool") {
26022
+ const state = part.state;
26023
+ return {
26024
+ ...metadata,
26025
+ kind: "tool",
26026
+ text: `${JSON.stringify(state?.input ?? {})}
26027
+ ${typeof state?.output === "string" ? state.output : JSON.stringify(state?.output ?? "")}`
26028
+ };
26029
+ }
26030
+ if (part.type === "tool_use")
26031
+ return { ...metadata, kind: "tool_use", text: JSON.stringify(part.input ?? {}) };
26032
+ if (part.type === "tool_result")
26033
+ return {
26034
+ ...metadata,
26035
+ kind: "tool_result",
26036
+ text: typeof part.content === "string" ? part.content : JSON.stringify(part.content ?? "")
26037
+ };
26038
+ return {
26039
+ ...metadata,
26040
+ kind: String(part.type),
26041
+ text: typeof part.text === "string" ? part.text : typeof part.thinking === "string" ? part.thinking : undefined
26042
+ };
25847
26043
  }
25848
26044
 
25849
26045
  // ../plugin/src/shared/recovery-reasoning-prefix.ts
@@ -28606,6 +28802,8 @@ ${chunkText}`,
28606
28802
  }
28607
28803
  }
28608
28804
  function piSubagentFailureReason(failure) {
28805
+ if (failure.meta?.failureClass === ZEN_FREE_TIER_FAILURE)
28806
+ return ZEN_FREE_TIER_FAILURE;
28609
28807
  if (PRODUCER_PROMPT_FIT_FAILURE_PATTERN.test(failure.error))
28610
28808
  return failure.error;
28611
28809
  if (["step_limit", "token_budget", "invalid_prompt"].includes(failure.reason)) {
@@ -28914,6 +29112,59 @@ function recordPiTransformTiming(sample) {
28914
29112
  // src/ctx-reduce-nudge-pi.ts
28915
29113
  import { randomUUID as randomUUID3 } from "node:crypto";
28916
29114
 
29115
+ // src/provider-visible-parts-pi.ts
29116
+ function isRecord4(value) {
29117
+ return value !== null && typeof value === "object" && !Array.isArray(value);
29118
+ }
29119
+ var PART_FIELDS = {
29120
+ text: ["type", "text", "textSignature"],
29121
+ thinking: ["type", "thinking", "thinkingSignature", "redacted"],
29122
+ image: ["type", "data", "mimeType"],
29123
+ toolCall: [
29124
+ "type",
29125
+ "id",
29126
+ "name",
29127
+ "arguments",
29128
+ "thoughtSignature",
29129
+ "namespace"
29130
+ ]
29131
+ };
29132
+ var MESSAGE_FIELDS = [
29133
+ "role",
29134
+ "provider",
29135
+ "api",
29136
+ "model",
29137
+ "stopReason",
29138
+ "toolCallId",
29139
+ "toolName",
29140
+ "isError"
29141
+ ];
29142
+ function pick(value, fields) {
29143
+ const view = {};
29144
+ for (const field of fields) {
29145
+ if (Object.hasOwn(value, field))
29146
+ view[field] = value[field];
29147
+ }
29148
+ return view;
29149
+ }
29150
+ function providerVisiblePiPart(value, providerApi) {
29151
+ if (providerApi === "pi-messages")
29152
+ return value;
29153
+ if (Array.isArray(value))
29154
+ return value.map((part) => providerVisiblePiPart(part, providerApi));
29155
+ if (!isRecord4(value))
29156
+ return value;
29157
+ if (typeof value.role === "string") {
29158
+ const view = pick(value, MESSAGE_FIELDS);
29159
+ if (Object.hasOwn(value, "content")) {
29160
+ view.content = Array.isArray(value.content) ? value.content.map((part) => providerVisiblePiPart(part, providerApi)) : typeof value.content === "string" ? value.content : undefined;
29161
+ }
29162
+ return view;
29163
+ }
29164
+ const type = typeof value.type === "string" ? value.type : "";
29165
+ return pick(value, Object.hasOwn(PART_FIELDS, type) ? PART_FIELDS[type] : ["type"]);
29166
+ }
29167
+
28917
29168
  // src/tail-hygiene-walk-pi.ts
28918
29169
  var TAG_PREFIX = /^§(\d+)§\s*/;
28919
29170
  var SYNTHETIC_TODO_PREFIX = "mc_synthetic_todo_";
@@ -28927,7 +29178,7 @@ function clearPiTailHygieneContentMemo() {
28927
29178
  }
28928
29179
  var FNV1A_32_OFFSET = 2166136261;
28929
29180
  var FNV1A_32_PRIME = 16777619;
28930
- function isRecord4(value) {
29181
+ function isRecord5(value) {
28931
29182
  return value !== null && typeof value === "object";
28932
29183
  }
28933
29184
  function fnv1a32(value) {
@@ -28942,7 +29193,7 @@ function safeStableStringify(value) {
28942
29193
  const seen = new Set;
28943
29194
  try {
28944
29195
  return JSON.stringify(value, (_key, current) => {
28945
- if (!isRecord4(current) || Array.isArray(current))
29196
+ if (!isRecord5(current) || Array.isArray(current))
28946
29197
  return current;
28947
29198
  if (seen.has(current))
28948
29199
  return "[Circular]";
@@ -29001,7 +29252,7 @@ function messageIdentity(message, index, stableId) {
29001
29252
  const resolved = stableId?.(message, index);
29002
29253
  if (resolved)
29003
29254
  return resolved;
29004
- if (isRecord4(message)) {
29255
+ if (isRecord5(message)) {
29005
29256
  if (typeof message.responseId === "string" && message.responseId.length > 0) {
29006
29257
  return `response:${message.responseId}`;
29007
29258
  }
@@ -29061,13 +29312,13 @@ function collectToolArcs(input) {
29061
29312
  const messageIds = input.messages.map((message, index) => messageIdentity(message, index, input.stableId));
29062
29313
  for (let messageIndex = 0;messageIndex < input.messages.length; messageIndex += 1) {
29063
29314
  const raw = input.messages[messageIndex];
29064
- if (!isRecord4(raw))
29315
+ if (!isRecord5(raw))
29065
29316
  continue;
29066
29317
  const role = raw.role;
29067
29318
  if (role === "assistant" && Array.isArray(raw.content)) {
29068
29319
  for (let partIndex = 0;partIndex < raw.content.length; partIndex += 1) {
29069
29320
  const part = raw.content[partIndex];
29070
- if (!isRecord4(part) || part.type !== "toolCall")
29321
+ if (!isRecord5(part) || part.type !== "toolCall")
29071
29322
  continue;
29072
29323
  if (typeof part.id !== "string" || part.id.length === 0)
29073
29324
  continue;
@@ -29078,7 +29329,7 @@ function collectToolArcs(input) {
29078
29329
  ownerId,
29079
29330
  ownerIndex: messageIndex,
29080
29331
  toolName: typeof part.name === "string" ? part.name : null,
29081
- sentinel: isRecord4(part.arguments) && typeof part.arguments.__magic_context_dropped__ === "string",
29332
+ sentinel: isRecord5(part.arguments) && typeof part.arguments.__magic_context_dropped__ === "string",
29082
29333
  parts: [part]
29083
29334
  };
29084
29335
  arcs.push(arc);
@@ -29110,7 +29361,7 @@ function collectToolArcs(input) {
29110
29361
  if (!Array.isArray(raw.content))
29111
29362
  continue;
29112
29363
  for (const part of raw.content) {
29113
- if (!isRecord4(part))
29364
+ if (!isRecord5(part))
29114
29365
  continue;
29115
29366
  arc.parts.push(part);
29116
29367
  arcByPart.set(part, arc);
@@ -29311,11 +29562,20 @@ function attributeTags(input, arcs, messageIds) {
29311
29562
  }
29312
29563
  return { messageTags, tagsByNumber };
29313
29564
  }
29314
- function excludedDraft(key, value) {
29565
+ var partJsonMemo = new TailHygieneJsonMemo;
29566
+ var excludedViews = new WeakMap;
29567
+ function excludedDraftForApi(key, value, providerApi) {
29568
+ let view = value !== null && typeof value === "object" ? excludedViews.get(value) : undefined;
29569
+ if (view === undefined) {
29570
+ view = providerVisiblePiPart(value, providerApi);
29571
+ if (value !== null && typeof value === "object")
29572
+ excludedViews.set(value, view);
29573
+ }
29315
29574
  return {
29316
29575
  key,
29317
29576
  kind: "excluded",
29318
- content: typeof value === "string" ? value : safeStableStringify(value),
29577
+ content: "",
29578
+ contentHash: partJsonMemo.get(key, view, () => typeof view === "string" ? partHash("excluded", view) : piJsonHash(view, () => safeStableStringify(view)), true),
29319
29579
  tokens: 0
29320
29580
  };
29321
29581
  }
@@ -29340,7 +29600,7 @@ function finalizeParts(drafts, pendingDropTagNumbers, protectedTagNumbers, newes
29340
29600
  u += uTokens;
29341
29601
  return {
29342
29602
  key: draft.key,
29343
- contentHash: partHash(draft.kind, draft.content),
29603
+ contentHash: draft.contentHash ?? partHash(draft.kind, draft.content),
29344
29604
  kind: draft.kind,
29345
29605
  tokens,
29346
29606
  uTokens,
@@ -29360,6 +29620,19 @@ function finalizeParts(drafts, pendingDropTagNumbers, protectedTagNumbers, newes
29360
29620
  };
29361
29621
  }
29362
29622
  function measurePiTailHygiene(input) {
29623
+ partJsonMemo.beginPass();
29624
+ excludedViews = new WeakMap;
29625
+ let providerApi = input.providerApi;
29626
+ if (providerApi === undefined) {
29627
+ for (let index = input.messages.length - 1;index >= 0; index--) {
29628
+ const message = input.messages[index];
29629
+ if (isRecord5(message) && message.role === "assistant" && typeof message.api === "string") {
29630
+ providerApi = message.api;
29631
+ break;
29632
+ }
29633
+ }
29634
+ }
29635
+ const excludedDraft = (key, value) => excludedDraftForApi(key, value, providerApi);
29363
29636
  const { arcs, arcByPart, messageIds } = collectToolArcs(input);
29364
29637
  const { messageTags, tagsByNumber } = attributeTags(input, arcs, messageIds);
29365
29638
  const drafts = [];
@@ -29370,7 +29643,7 @@ function measurePiTailHygiene(input) {
29370
29643
  if (messageIndex === input.messages.length - 1)
29371
29644
  newestMessagePartStart = drafts.length;
29372
29645
  const messageKey = messageIds[messageIndex] ?? `pi-message:${messageIndex}`;
29373
- if (!isRecord4(raw)) {
29646
+ if (!isRecord5(raw)) {
29374
29647
  drafts.push(excludedDraft(`${messageKey}\x00excluded`, raw));
29375
29648
  continue;
29376
29649
  }
@@ -29409,7 +29682,7 @@ function measurePiTailHygiene(input) {
29409
29682
  for (let partIndex = 0;partIndex < raw.content.length; partIndex += 1) {
29410
29683
  const part = raw.content[partIndex];
29411
29684
  const key = `${messageKey}\x00${partIndex}`;
29412
- if (!isRecord4(part)) {
29685
+ if (!isRecord5(part)) {
29413
29686
  drafts.push(excludedDraft(`${key}\x00excluded`, part));
29414
29687
  continue;
29415
29688
  }
@@ -29479,7 +29752,7 @@ function measurePiTailHygiene(input) {
29479
29752
  drafts.push(excludedDraft(`${key}\x00toolInput`, part));
29480
29753
  continue;
29481
29754
  }
29482
- const content = safeStableStringify(part.arguments);
29755
+ const content = partJsonMemo.get(`${key}\x00input`, part.arguments, () => safeStableStringify(part.arguments));
29483
29756
  if (!content) {
29484
29757
  drafts.push(excludedDraft(`${key}\x00toolInput`, part));
29485
29758
  } else {
@@ -29529,7 +29802,7 @@ function refreshPiTailHygieneBaseline(input) {
29529
29802
  })
29530
29803
  };
29531
29804
  const now = input.now ?? Date.now();
29532
- const refrozen = (mismatch) => ({
29805
+ const refrozen = (mismatch) => rememberCurrentToolOutputCount({
29533
29806
  ...freezeTailHygieneMeasurement(measured),
29534
29807
  protectedToolsPolicy,
29535
29808
  hygieneUnitsVersion: frozenCalibration.hygieneUnitsVersion,
@@ -29543,7 +29816,7 @@ function refreshPiTailHygieneBaseline(input) {
29543
29816
  channel1PostReduceGrace: input.previous?.channel1PostReduceGrace,
29544
29817
  lastPrefixMismatch: mismatch,
29545
29818
  realUserTurnCount: rawMeasured.realUserTurnCount
29546
- });
29819
+ }, measured);
29547
29820
  if (input.cacheBusting || !input.previous)
29548
29821
  return refrozen();
29549
29822
  const prefix = compareMeasuredTailPrefix(input.previous.baselineParts, measured.parts);
@@ -29557,7 +29830,7 @@ function refreshPiTailHygieneBaseline(input) {
29557
29830
  if (part.kind !== "toolOutput" || !part.protected)
29558
29831
  turnDeltaU += part.uTokens;
29559
29832
  }
29560
- return {
29833
+ return rememberCurrentToolOutputCount({
29561
29834
  ...input.previous,
29562
29835
  protectedToolsPolicy,
29563
29836
  turnDeltaU,
@@ -29567,7 +29840,7 @@ function refreshPiTailHygieneBaseline(input) {
29567
29840
  contentSignature: measured.contentSignature,
29568
29841
  lastPrefixMismatch: undefined,
29569
29842
  realUserTurnCount: rawMeasured.realUserTurnCount
29570
- };
29843
+ }, measured);
29571
29844
  }
29572
29845
  function effectivePiTailHygiene(baseline) {
29573
29846
  const t = Math.ceil(Math.max(0, baseline.baselineT + baseline.turnDeltaT));
@@ -29577,7 +29850,7 @@ function effectivePiTailHygiene(baseline) {
29577
29850
  function measurePiToolResultDelta(content) {
29578
29851
  let tokens = 0;
29579
29852
  for (const part of content) {
29580
- if (!isRecord4(part))
29853
+ if (!isRecord5(part))
29581
29854
  continue;
29582
29855
  if (part.type === "text" && typeof part.text === "string") {
29583
29856
  const text = stripChannel1ReminderSpans(part.text);
@@ -29680,7 +29953,7 @@ function maybeChannel1ReminderForToolResult(args) {
29680
29953
  }
29681
29954
  const block = {
29682
29955
  type: "text",
29683
- text: buildChannel1Reminder(decision.level, decision.undroppedTokens, reclaimableToolOutputCount(state.baselineParts), currentReclaimHints(db, sessionId, state.oldestReclaimableToolTags), decision.sticky)
29956
+ text: buildChannel1Reminder(decision.level, decision.undroppedTokens, currentReclaimableToolOutputCount(state), currentReclaimHints(db, sessionId, state.oldestReclaimableToolTags), decision.sticky)
29684
29957
  };
29685
29958
  setChannel1NudgeState(db, sessionId, {
29686
29959
  ...nextNudgeState,
@@ -29718,7 +29991,7 @@ function maybeDeliverChannel2Pi(pi, db, sessionId) {
29718
29991
  try {
29719
29992
  const message = {
29720
29993
  customType: CHANNEL2_NUDGE_CUSTOM_TYPE,
29721
- content: buildChannel2Reminder(undropped, reclaimableToolOutputCount(baseline.baselineParts), currentReclaimHints(db, sessionId, baseline.oldestReclaimableToolTags)),
29994
+ content: buildChannel2Reminder(undropped, currentReclaimableToolOutputCount(baseline), currentReclaimHints(db, sessionId, baseline.oldestReclaimableToolTags)),
29722
29995
  display: false,
29723
29996
  details: { kind: "channel-2-ceiling-nudge" }
29724
29997
  };
@@ -34303,16 +34576,25 @@ function piResponsesAssistantTexts(value, model) {
34303
34576
  blocks.push(block);
34304
34577
  }
34305
34578
  const payload = object4(message.providerPayload);
34306
- if (message.api !== model?.api || typeof model?.id !== "string" || typeof model?.provider !== "string" || message.model !== model?.id || (payload?.provider ?? message.provider) !== model?.provider || payload?.type !== "openaiResponsesHistory" || payload.dt !== true || !Array.isArray(payload.items) || message.content.length !== blocks.length)
34579
+ if (message.api !== model?.api || typeof model?.id !== "string" || typeof model?.provider !== "string" || message.model !== model?.id || (payload?.provider ?? message.provider) !== model?.provider || payload?.type !== "openaiResponsesHistory" || payload.dt !== true || !Array.isArray(payload.items))
34307
34580
  return texts;
34308
34581
  const replay = [];
34582
+ let bound = 0;
34583
+ let output = false;
34309
34584
  for (const value of payload.items) {
34310
34585
  const item = object4(value);
34586
+ if (item?.type === "reasoning")
34587
+ continue;
34588
+ if (item?.type === "function_call" || item?.type === "custom_tool_call") {
34589
+ output = true;
34590
+ continue;
34591
+ }
34311
34592
  if (item?.type !== "message" || item.role !== "assistant" || !Array.isArray(item.content))
34312
34593
  return texts;
34594
+ const itemTexts = [];
34313
34595
  for (const value of item.content) {
34314
34596
  const part = object4(value);
34315
- const block = blocks[replay.length];
34597
+ const block = blocks[bound];
34316
34598
  if (!block || part?.type !== "output_text" || typeof part.text !== "string")
34317
34599
  return texts;
34318
34600
  let signature;
@@ -34324,10 +34606,16 @@ function piResponsesAssistantTexts(value, model) {
34324
34606
  }
34325
34607
  if (signature?.v !== 1 || typeof signature.id !== "string" || item.id !== signature.id || item.phase !== signature.phase || part.text !== block.text.replace(/^§\d+§ /u, ""))
34326
34608
  return texts;
34327
- replay.push(textIdentity2(wellFormed(part.text), item.phase));
34609
+ bound++;
34610
+ itemTexts.push(part.text);
34328
34611
  }
34612
+ if (!itemTexts.some((text) => /\S/u.test(text)))
34613
+ continue;
34614
+ output = true;
34615
+ for (const text of itemTexts)
34616
+ replay.push(textIdentity2(wellFormed(text), item.phase));
34329
34617
  }
34330
- return replay.length === texts.length ? replay : texts;
34618
+ return output && bound === blocks.length ? replay : texts;
34331
34619
  }
34332
34620
  function piResponsesWireTexts(value, phase) {
34333
34621
  if (phase !== undefined && phase !== null && phase !== "commentary" && phase !== "final_answer")
@@ -35040,6 +35328,10 @@ function trimPreparedImageRows(rows, budget, drops) {
35040
35328
  return projected;
35041
35329
  }
35042
35330
  function piPreparedStructureMismatch(payload, prepared, onImageTrim) {
35331
+ let stage = "image count";
35332
+ let row = 0;
35333
+ let expectedRows;
35334
+ let rows;
35043
35335
  try {
35044
35336
  if (prepared?.structuralSkipReason)
35045
35337
  return;
@@ -35049,21 +35341,25 @@ function piPreparedStructureMismatch(payload, prepared, onImageTrim) {
35049
35341
  if (trimmed < 0)
35050
35342
  return "comparison=image count";
35051
35343
  const imageProjection = trimmed > 0;
35344
+ stage = "wire rows";
35052
35345
  const body = object5(payload);
35053
35346
  const ollama = prepared?.api === "ollama-chat" && !!body && Array.isArray(body.messages) && body.input === undefined && body.system === undefined && body.inferenceConfig === undefined;
35054
35347
  const responses = body?.messages === undefined && Array.isArray(body?.input);
35055
- const rows = payloadStructure(payload, ollama, responses && !!prepared?.responsesMessages, imageProjection);
35348
+ rows = payloadStructure(payload, ollama, responses && !!prepared?.responsesMessages, imageProjection);
35056
35349
  const chat = !!body && Array.isArray(body.messages) && (prepared?.api === "openai-completions" || body.system === undefined) && body.input === undefined && body.inferenceConfig === undefined;
35057
- let expectedRows = responses && prepared?.responsesMessages ? prepared.responsesMessages : chat && prepared?.chatMessages ? prepared.chatMessages : prepared?.messages;
35350
+ stage = "prepared rows";
35351
+ expectedRows = responses && prepared?.responsesMessages ? prepared.responsesMessages : chat && prepared?.chatMessages ? prepared.chatMessages : prepared?.messages;
35058
35352
  if (expectedRows && imageProjection && imageBudget)
35059
35353
  expectedRows = trimPreparedImageRows(expectedRows, imageBudget, trimmed);
35060
35354
  if (!rows || !expectedRows)
35061
35355
  return structureDetail(0, expectedRows, rows, "count");
35062
35356
  if (!body)
35063
35357
  return structureDetail(0, expectedRows, rows, "count");
35358
+ stage = "row comparison";
35064
35359
  const google = !!piGoogleConversation(payload);
35065
35360
  const anthropic = google || body.inferenceConfig !== undefined || prepared?.api === "anthropic-messages" || !prepared?.api && body.max_tokens !== undefined && Array.isArray(body.system);
35066
35361
  for (let i = 0;i < Math.min(rows.length, expectedRows.length); i++) {
35362
+ row = i;
35067
35363
  const expected = expectedRows[i];
35068
35364
  const actual = rows[i];
35069
35365
  if (actual.role !== expected.role)
@@ -35098,8 +35394,15 @@ function piPreparedStructureMismatch(payload, prepared, onImageTrim) {
35098
35394
  trimmed
35099
35395
  });
35100
35396
  return;
35101
- } catch {
35102
- return structureDetail(0, undefined, undefined, "count");
35397
+ } catch (error) {
35398
+ const name = error instanceof Error && [
35399
+ "Error",
35400
+ "TypeError",
35401
+ "RangeError",
35402
+ "SyntaxError",
35403
+ "ReferenceError"
35404
+ ].includes(error.constructor.name) ? error.constructor.name : "unknown";
35405
+ return `${structureDetail(row, expectedRows, rows, "exception")} stage=${stage} error=${name}`;
35103
35406
  }
35104
35407
  }
35105
35408
 
@@ -35649,6 +35952,7 @@ function createPiFallbackIndex() {
35649
35952
  import { createHash as createHash12, randomUUID as randomUUID4 } from "node:crypto";
35650
35953
 
35651
35954
  // src/tokenize-pi-messages.ts
35955
+ var PI_IMAGE_TOKENS = 1200;
35652
35956
  function tokenizePiMessages(messages, options) {
35653
35957
  let conversation = 0;
35654
35958
  let toolCall = 0;
@@ -35706,7 +36010,7 @@ function tokenizePiMessages(messages, options) {
35706
36010
  }
35707
36011
  break;
35708
36012
  case "image":
35709
- conversation += 1200;
36013
+ conversation += PI_IMAGE_TOKENS;
35710
36014
  break;
35711
36015
  case "toolCall":
35712
36016
  if (typeof p.name === "string")
@@ -35735,7 +36039,7 @@ function tokenizePiMessages(messages, options) {
35735
36039
  if (p.type === "text" && typeof p.text === "string") {
35736
36040
  toolCall += estimateTokens(p.text);
35737
36041
  } else if (p.type === "image") {
35738
- toolCall += 1200;
36042
+ toolCall += PI_IMAGE_TOKENS;
35739
36043
  }
35740
36044
  }
35741
36045
  }
@@ -38309,6 +38613,13 @@ function stripThinkingParts(message) {
38309
38613
  assistant.content = strippedContent;
38310
38614
  return before - strippedContent.length;
38311
38615
  }
38616
+
38617
+ class PiThinkingBindingRecoveryRefusal extends EmergencyFailClosedError {
38618
+ constructor() {
38619
+ super("Pi thinking-binding recovery has no valid new strip shape; this turn was not sent. Send a new message or start a new session.", { code: "pi_thinking_binding_no_valid_shape" });
38620
+ this.name = "PiThinkingBindingRecoveryRefusal";
38621
+ }
38622
+ }
38312
38623
  function applyPiThinkingBindingRecovery(args) {
38313
38624
  const protectedMessages = latestAssistantTurnMessages(args.messages);
38314
38625
  const frozenEntryIds = frozenBindingEntryIds(args.db, args.sessionId);
@@ -38318,14 +38629,17 @@ function applyPiThinkingBindingRecovery(args) {
38318
38629
  if (flagTarget) {
38319
38630
  const entryIds = new Set;
38320
38631
  for (let index = 0;index < args.messages.length; index += 1) {
38321
- if (protectedMessages.has(args.messages[index]))
38632
+ if (!hasThinkingPart(args.messages[index]))
38322
38633
  continue;
38323
38634
  const entryId = args.entryIds[index];
38324
- if (entryId && hasThinkingPart(args.messages[index]))
38325
- entryIds.add(entryId);
38635
+ if (!entryId)
38636
+ throw new PiThinkingBindingRecoveryRefusal;
38637
+ entryIds.add(entryId);
38326
38638
  }
38327
38639
  const newEntryIds = [...entryIds].filter((id) => !frozenEntryIds.has(id));
38328
- if (newEntryIds.length === 0 || addMergedReasoningStrippedIds(args.db, args.sessionId, [
38640
+ if (newEntryIds.length === 0)
38641
+ throw new PiThinkingBindingRecoveryRefusal;
38642
+ if (addMergedReasoningStrippedIds(args.db, args.sessionId, [
38329
38643
  ...newEntryIds.map(thinkingBindingRecoveryFrozenId),
38330
38644
  ...args.endOfPassOrder ? [THINKING_BINDING_STRIP_ORDER_END_MARKER] : []
38331
38645
  ])) {
@@ -38333,7 +38647,8 @@ function applyPiThinkingBindingRecovery(args) {
38333
38647
  frozenEntryIds.add(id);
38334
38648
  applied = { flagTarget, entryIds: [...entryIds] };
38335
38649
  reportBindingRecovery(args.sessionId, args.report, `thinking-binding recovery consumed on context pass target=${flagTarget} entries=${applied.entryIds.length} [${applied.entryIds.join(",")}]`);
38336
- }
38650
+ } else
38651
+ throw new PiThinkingBindingRecoveryRefusal;
38337
38652
  }
38338
38653
  const restoringActiveTurn = latestThinkingRecoveryCoversActiveTurn({
38339
38654
  db: args.db,
@@ -38350,6 +38665,12 @@ function applyPiThinkingBindingRecovery(args) {
38350
38665
  }
38351
38666
  return applied;
38352
38667
  }
38668
+ function hasUnfrozenPiThinking(args) {
38669
+ return isPrefixBoundThinkingModel(args.provider, args.model) && args.messages.some((message, index) => {
38670
+ const entryId = args.entryIds[index];
38671
+ return hasThinkingPart(message) && (!entryId || !args.frozen.has(entryId));
38672
+ });
38673
+ }
38353
38674
  function applyPiProactiveThinkingStrip(args) {
38354
38675
  if (!args.cacheBustingPass)
38355
38676
  return null;
@@ -39606,6 +39927,12 @@ function createOpaqueTranscriptMessage(working, index, sessionId, _markDirty, en
39606
39927
  function createPiUserStringPart(working, messageIndex, markDirty) {
39607
39928
  return {
39608
39929
  kind: "text",
39930
+ get synthetic() {
39931
+ return working[messageIndex].synthetic;
39932
+ },
39933
+ get ignored() {
39934
+ return working[messageIndex].ignored;
39935
+ },
39609
39936
  id: undefined,
39610
39937
  getText() {
39611
39938
  const msg = working[messageIndex];
@@ -39645,6 +39972,12 @@ function createPiUserArrayPart(working, messageIndex, partIndex, markDirty) {
39645
39972
  const kind = classifyContent(part);
39646
39973
  return {
39647
39974
  kind,
39975
+ synthetic: msg.synthetic || part?.synthetic,
39976
+ ignored: msg.ignored || part?.ignored,
39977
+ imageTokens() {
39978
+ const current = working[messageIndex].content;
39979
+ return Array.isArray(current) && current[partIndex]?.type === "image" ? PI_IMAGE_TOKENS : 0;
39980
+ },
39648
39981
  id: undefined,
39649
39982
  getText() {
39650
39983
  const current = working[messageIndex].content;
@@ -39851,6 +40184,9 @@ function createPiToolResultPart(working, messageIndex, partIndex, markDirty) {
39851
40184
  return {
39852
40185
  kind,
39853
40186
  id: msg.toolCallId,
40187
+ imageTokens() {
40188
+ return working[messageIndex].content[partIndex]?.type === "image" ? PI_IMAGE_TOKENS : 0;
40189
+ },
39854
40190
  remove() {
39855
40191
  return markToolRemoval(working, messageIndex, partIndex);
39856
40192
  },
@@ -39995,6 +40331,15 @@ var afterFallbackAdoptionForTests;
39995
40331
  var pendingDecisionLogObserverForTests;
39996
40332
  var mutationGateObserverForTests;
39997
40333
  var lkgRecoveryLogObserverForTests;
40334
+ var pipelineResultObserverForTests;
40335
+ function observePipelineResultForTests(sessionId, result) {
40336
+ pipelineResultObserverForTests?.(sessionId, {
40337
+ prefixBustPermitted: result.prefixBustPermitted,
40338
+ bustedThisPass: result.bustedThisPass,
40339
+ identityRebuilt: result.identityRebuilt,
40340
+ signedPrefixRewritten: result.signedPrefixRewritten
40341
+ });
40342
+ }
39998
40343
  var variantChangeLogObserverForTests;
39999
40344
  var beforePipelineForTests;
40000
40345
  function logPiLkgRecovery(sessionId, message) {
@@ -42186,6 +42531,7 @@ function registerPiContextHandler(pi, baseOptions) {
42186
42531
  protectedTokens: options.protectedTokens,
42187
42532
  protectedTokenTierOverrides: options.protectedTokenTierOverrides,
42188
42533
  usableSoft: windowGeometry?.usableSoft ?? usageContextLimit ?? 200000,
42534
+ reclaimUsableWindow: windowGeometry?.usableSoft ?? usageContextLimit ?? 0,
42189
42535
  heuristics: options.heuristics,
42190
42536
  emergencyCeilingTokens,
42191
42537
  injection: options.injection ? {
@@ -42257,6 +42603,7 @@ function registerPiContextHandler(pi, baseOptions) {
42257
42603
  }
42258
42604
  };
42259
42605
  let result = await budget.waitMandatory(runPipeline(pipelineArgs));
42606
+ observePipelineResultForTests(sessionId, result);
42260
42607
  logTransformTiming(sessionId, "runPipeline", tRunPipeline);
42261
42608
  if (result.identityRebuilt || result.lkgFrozenReleaseReason)
42262
42609
  schedulerDecision = "execute";
@@ -42432,7 +42779,7 @@ function registerPiContextHandler(pi, baseOptions) {
42432
42779
  hostEditBeforeNewestThinking = stickyBefore === null || [...stickyAfter].some((key) => !stickyBefore.has(key)) || JSON.stringify(getPersistedTodoSyntheticAnchor(options.db, sessionId)) !== todoAnchorBefore;
42433
42780
  } catch {}
42434
42781
  const outputEntryIds = resolvePiLkgOutputEntryIds(outputMessages, result.syntheticLeadingCount, (message) => result.lkgEntryIdByRef.get(message) ?? result.postCommitEntryIdByRef.get(message) ?? bindingEntryIdByRef.get(message)).map((entryId) => entryId ?? undefined);
42435
- const thinkingBindingRecoveryApplied = bindingStripOrder === "end" && !result.signedPrefixRewritten ? applyPiThinkingBindingRecovery({
42782
+ const thinkingBindingRecoveryApplied = bindingStripOrder === "end" && (!result.signedPrefixRewritten || !result.prefixBustPermitted) ? applyPiThinkingBindingRecovery({
42436
42783
  db: options.db,
42437
42784
  sessionId,
42438
42785
  messages: outputMessages,
@@ -42454,7 +42801,7 @@ function registerPiContextHandler(pi, baseOptions) {
42454
42801
  entryIds: outputEntryIds,
42455
42802
  provider: lkgProviderKey ?? undefined,
42456
42803
  model: ctx.model?.id,
42457
- cacheBustingPass: !!recovery.rebuildReason || result.signedPrefixRewritten || result.prefixBustPermitted && !result.strictThinkingKept && (isCacheBusting || result.prefixRebuildAlreadyPriced || result.executedWorkThisPass && (result.prefixEditBesidesReasoningTrim || hostEditBeforeNewestThinking)),
42804
+ cacheBustingPass: result.prefixBustPermitted && !result.strictThinkingKept && (result.signedPrefixRewritten || isCacheBusting || result.prefixRebuildAlreadyPriced || result.executedWorkThisPass && (result.prefixEditBesidesReasoningTrim || hostEditBeforeNewestThinking)),
42458
42805
  stripActiveTurn: result.signedPrefixRewritten,
42459
42806
  requirePersistence: !!recovery.rebuildReason,
42460
42807
  report: (line) => sessionLog2(sessionId, line)
@@ -42497,6 +42844,7 @@ function registerPiContextHandler(pi, baseOptions) {
42497
42844
  const previousBaseline = getPiChannel1Baseline(sessionId);
42498
42845
  const baseline = refreshPiTailHygieneBaseline({
42499
42846
  messages: outputMessages,
42847
+ providerApi: ctx.model?.api,
42500
42848
  tags,
42501
42849
  tagCacheKey: result.channelBaselineSnapshot.tagCacheKey,
42502
42850
  protectedTagNumbers,
@@ -42636,6 +42984,7 @@ function registerPiContextHandler(pi, baseOptions) {
42636
42984
  percentage: Math.max(95, pipelineArgs.contextUsage.percentage)
42637
42985
  }
42638
42986
  }));
42987
+ observePipelineResultForTests(sessionId, result);
42639
42988
  fitReclaimedThisPass = true;
42640
42989
  inputForFitReclaim = undefined;
42641
42990
  outputMessages = result.messages;
@@ -42647,7 +42996,7 @@ function registerPiContextHandler(pi, baseOptions) {
42647
42996
  entryIds: retryIds.map((id) => id ?? undefined),
42648
42997
  provider: ctx.model?.provider,
42649
42998
  model: ctx.model?.id,
42650
- cacheBustingPass: true,
42999
+ cacheBustingPass: result.prefixBustPermitted,
42651
43000
  stripActiveTurn: true
42652
43001
  });
42653
43002
  estimate = estimatePiOutgoingInputTokens(outputMessages, envelope, undefined, rawFallbackLimit);
@@ -42748,6 +43097,7 @@ function registerPiContextHandler(pi, baseOptions) {
42748
43097
  }
42749
43098
  }));
42750
43099
  outputMessages = result.messages;
43100
+ observePipelineResultForTests(sessionId, result);
42751
43101
  injectTodo();
42752
43102
  if (upperCursor)
42753
43103
  recordPiUpperEstimateReclaim(options.db, currentUpperCursor() ?? upperCursor, reservation);
@@ -42759,7 +43109,7 @@ function registerPiContextHandler(pi, baseOptions) {
42759
43109
  entryIds: resolvePiLkgOutputEntryIds(outputMessages, result.syntheticLeadingCount, (message) => result.lkgEntryIdByRef.get(message) ?? result.postCommitEntryIdByRef.get(message)).map((id) => id ?? undefined),
42760
43110
  provider: ctx.model?.provider,
42761
43111
  model: ctx.model?.id,
42762
- cacheBustingPass: result.bustedThisPass,
43112
+ cacheBustingPass: result.prefixBustPermitted,
42763
43113
  stripActiveTurn: result.signedPrefixRewritten
42764
43114
  });
42765
43115
  estimate = estimatePiOutgoingInputTokens(outputMessages, envelope, undefined, outgoingFitLimit);
@@ -43631,29 +43981,37 @@ async function runPipeline(args) {
43631
43981
  let activeThinkingTurn = hasActiveAnthropicThinkingTurn(args.messages, args.resolvedProviderID, args.resolvedModelID);
43632
43982
  let protectedSignedPrefix = activeThinkingTurn && args.reasoningClearing?.prefixBound === true;
43633
43983
  let rewritingSignedPrefix = false;
43984
+ let isCacheBustingPass = args.recoveryRebuildReason === "lkg_recovery_input_rebuild" || (!!args.recoveryRebuildReason || args.forceSignedRebuildForFit === true) && hasUnfrozenPiThinking({
43985
+ messages: args.messages,
43986
+ entryIds: args.messages.map((message, index) => args.lkgEntryIds?.[index] ?? resolvePiRealStableId(message, index, args.entryIds, args.entryIdByRef ?? undefined)),
43987
+ frozen: frozenBindingEntryIds(args.db, args.sessionId),
43988
+ provider: args.resolvedProviderID,
43989
+ model: args.resolvedModelID
43990
+ });
43634
43991
  let workingForRewrite;
43635
- const stripForPrefixRewrite = () => {
43992
+ const stripForPrefixRewrite = (permitRewrite = true) => {
43993
+ isCacheBustingPass ||= permitRewrite;
43636
43994
  const messages = workingForRewrite ?? args.messages;
43637
43995
  const result = applyPiProactiveThinkingStrip({
43638
43996
  db: args.db,
43639
43997
  sessionId: args.sessionId,
43640
43998
  messages,
43641
- entryIds: messages.map((message, index) => resolvePiStableId(message, index, args.entryIds, args.entryIdByRef ?? undefined)),
43999
+ entryIds: messages.map((message, index) => args.lkgEntryIds?.[index] ?? resolvePiRealStableId(message, index, args.entryIds, args.entryIdByRef ?? undefined)),
43642
44000
  provider: args.resolvedProviderID,
43643
44001
  model: args.resolvedModelID,
43644
- cacheBustingPass: true,
44002
+ cacheBustingPass: isCacheBustingPass,
43645
44003
  stripActiveTurn: true,
43646
44004
  persistOnly: true,
43647
44005
  requirePersistence: !!args.recoveryRebuildReason
43648
44006
  });
43649
- if (activeThinkingTurn && !result)
44007
+ if (activeThinkingTurn && isCacheBustingPass && !result)
43650
44008
  throw new Error("Cannot persist signed thinking omission before prefix rewrite");
43651
44009
  rewritingSignedPrefix = true;
43652
44010
  activeThinkingTurn = false;
43653
44011
  protectedSignedPrefix = false;
43654
44012
  };
43655
44013
  if (args.recoveryRebuildReason || args.forceSignedRebuildForFit && protectedSignedPrefix)
43656
- stripForPrefixRewrite();
44014
+ stripForPrefixRewrite(isCacheBustingPass);
43657
44015
  const realStableId = (msg, index) => resolvePiRealStableId(msg, index, args.entryIds, args.entryIdByRef ?? undefined);
43658
44016
  const passFallbackIds = settlePiFallbackOccurrences(args.db, args.sessionId, piContentFallbackIds(args.messages, realStableId));
43659
44017
  const stableIdResolver = (msg, index) => realStableId(msg, index) ?? passFallbackIds[index] ?? resolvePiStableId(msg, index);
@@ -43888,13 +44246,13 @@ async function runPipeline(args) {
43888
44246
  const heldIdleExpiryRebuild = protectedSignedPrefix && piHardSignals?.cacheExpired === true;
43889
44247
  const rideSignals = {
43890
44248
  subagentExecute: args.sessionMeta.isSubagent && args.schedulerDecision === "execute",
43891
- hardFold: foldBustsServedPrefixThisPass || firstRenderBust || heldIdleExpiryRebuild,
44249
+ hardFold: foldBustsServedPrefixThisPass || firstRenderBust || heldIdleExpiryRebuild || isCacheBustingPass,
43892
44250
  force: (args.forceMaterialization === true || emergencyDropEligible) && (args.contextUsage.percentage >= 95 || getEmergencyInputSample(args.db, args.sessionId) === 0),
43893
44251
  explicitFlush: hasPendingMaterializeSignal || deferredMaterializeEligible && !prefixPreflightContended,
43894
44252
  publishedHistory: !prefixPreflightContended && (args.isCacheBusting || publishedM1RefreshedThisPass || canConsumeDeferredLate && deferredHistoryWasPendingAtPassStart)
43895
44253
  };
43896
44254
  let prefixMutationAllowed = !protectedSignedPrefix || firstRenderBust || heldIdleExpiryRebuild || emergencyDropEligible;
43897
- let isCacheBustingPass = prefixMutationAllowed && hasReclaimRide(rideSignals);
44255
+ isCacheBustingPass = prefixMutationAllowed && hasReclaimRide(rideSignals);
43898
44256
  let publishedWorkDrainAllowed = isCacheBustingPass;
43899
44257
  const usesTokenProtection = args.protectedTokenTierOverrides !== undefined || args.protectedTokens !== undefined;
43900
44258
  const resolveProtectionFloor = () => resolveEpochFloorForPass(args.db, args.sessionId, {
@@ -43905,7 +44263,7 @@ async function runPipeline(args) {
43905
44263
  onRejectedProjectOverride: (warning) => sessionLog2(args.sessionId, warning)
43906
44264
  });
43907
44265
  let protectionFloorResolution = resolveProtectionFloor();
43908
- let shouldRunHeuristics = args.heuristics !== undefined && isCacheBustingPass && (rideSignals.publishedHistory || args.forceMaterialization === true || hasPendingMaterializeSignal || deferredMaterializeEligible || foldBustsServedPrefixThisPass || firstRenderBust || heldIdleExpiryRebuild || rideSignals.subagentExecute || args.schedulerDecision === "execute" && !alreadyRanHeuristicsThisTurn);
44266
+ let shouldRunHeuristics = args.heuristics !== undefined && isCacheBustingPass && (rideSignals.publishedHistory || args.forceMaterialization === true || hasPendingMaterializeSignal || deferredMaterializeEligible || foldBustsServedPrefixThisPass || firstRenderBust || heldIdleExpiryRebuild || args.recoveryRebuildReason !== undefined && isCacheBustingPass || rideSignals.subagentExecute || args.schedulerDecision === "execute" && !alreadyRanHeuristicsThisTurn);
43909
44267
  let unavoidableCarriedIdentityRebuild = false;
43910
44268
  if (protectedSignedPrefix && readPiIdentityRebuilds(args.db, args.sessionId).length > 0) {
43911
44269
  args.tryReplayHeldIdentityRebuild?.();
@@ -44138,6 +44496,7 @@ async function runPipeline(args) {
44138
44496
  const deferredHistoryRefreshWasPending = deferredHistoryWasPendingAtPassStart;
44139
44497
  const shouldReadPendingOps = !args.compactionOff && (publishedWorkDrainAllowed || args.schedulerDecision === "execute" || args.forceMaterialization || hasPendingMaterializeSignal || foldExecutedThisPass || firstRenderBust);
44140
44498
  const pendingOps = shouldReadPendingOps ? getPendingOps(args.db, args.sessionId) : [];
44499
+ const reclaimDistance = isCacheBustingPass ? toolReclaimDistanceEntries(mutationView, targets) : [];
44141
44500
  const protectedToolTags = protectedToolTagNumbers(getActiveTagsBySession2(args.db, args.sessionId), args.protectedTools);
44142
44501
  for (const number of identityAdoption.protectedTagNumbers)
44143
44502
  protectedToolTags.add(number);
@@ -44452,6 +44811,7 @@ async function runPipeline(args) {
44452
44811
  }
44453
44812
  }
44454
44813
  const editReclaim = buildEditSupersessionReclaim({
44814
+ watermark: reclaimMeta.toolReclaimWatermark ?? 0,
44455
44815
  db: args.db,
44456
44816
  sessionId: args.sessionId,
44457
44817
  targets: newTargets,
@@ -44579,7 +44939,7 @@ async function runPipeline(args) {
44579
44939
  setEmergencyDropSample(args.db, args.sessionId, args.contextUsage.inputTokens);
44580
44940
  }
44581
44941
  if (toolReclaimApplicationOpportunity) {
44582
- advanceToolReclaimWatermarkToCurrentMax(args.db, args.sessionId);
44942
+ advanceToolReclaimWatermarkByDistance(args.db, args.sessionId, reclaimDistance, args.reclaimUsableWindow ?? 0);
44583
44943
  }
44584
44944
  if (autoReclaimTargetCount > 0) {
44585
44945
  sessionLog2(args.sessionId, `tool reclaim auto-drop: targets=${autoReclaimTargetCount} mutated=${autoReclaimDidMutate}`);
@@ -44879,7 +45239,7 @@ async function runPipeline(args) {
44879
45239
  prefixMutationAllowed,
44880
45240
  strictThinkingKept: protectedSignedPrefix,
44881
45241
  signedPrefixRewritten: rewritingSignedPrefix,
44882
- prefixRebuildAlreadyPriced: firstRenderBust || heldIdleExpiryRebuild,
45242
+ prefixRebuildAlreadyPriced: firstRenderBust || heldIdleExpiryRebuild || !!args.recoveryRebuildReason && isCacheBustingPass,
44883
45243
  identityRebuilt: identityRebuild,
44884
45244
  servedTagNumbers,
44885
45245
  heuristicsExecuted,
@@ -46099,7 +46459,7 @@ function formatThresholdPercent(value) {
46099
46459
  }
46100
46460
 
46101
46461
  // ../plugin/src/shared/status-view-check.ts
46102
- function isRecord5(value) {
46462
+ function isRecord6(value) {
46103
46463
  return typeof value === "object" && value !== null && !Array.isArray(value);
46104
46464
  }
46105
46465
  function isNumber(value) {
@@ -46177,28 +46537,28 @@ class OptionalFields {
46177
46537
  }
46178
46538
  }
46179
46539
  function isWindowGeometry(value) {
46180
- if (!isRecord5(value) || !isNumber(value.usableSoft) || !isRecord5(value.derivation))
46540
+ if (!isRecord6(value) || !isNumber(value.usableSoft) || !isRecord6(value.derivation))
46181
46541
  return false;
46182
46542
  const derivation = value.derivation;
46183
46543
  return isNumber(derivation.window) && isNumber(derivation.reserve) && isString(derivation.reserveSource);
46184
46544
  }
46185
46545
  function isTailHygiene(value) {
46186
- return isRecord5(value) && isNumber(value.u) && isNumber(value.t) && isNumber(value.severity) && isBoolean(value.evaluable);
46546
+ return isRecord6(value) && isNumber(value.u) && isNumber(value.t) && isNumber(value.severity) && isBoolean(value.evaluable);
46187
46547
  }
46188
46548
  function isCompactionMarker(value) {
46189
- return isRecord5(value) && (value.code === null || value.code === "MC-C11") && isNumber(value.attempts) && (value.lastError === null || isString(value.lastError));
46549
+ return isRecord6(value) && (value.code === null || value.code === "MC-C11") && isNumber(value.attempts) && (value.lastError === null || isString(value.lastError));
46190
46550
  }
46191
46551
  function isTickFailure(value) {
46192
- return isRecord5(value) && isString(value.stage) && isNumber(value.at);
46552
+ return isRecord6(value) && isString(value.stage) && isNumber(value.at);
46193
46553
  }
46194
46554
  function isConfigParseFailure(value) {
46195
- return isRecord5(value);
46555
+ return isRecord6(value);
46196
46556
  }
46197
46557
  function isConfigReloadFailure(value) {
46198
- return isRecord5(value) && isString(value.path) && isString(value.message);
46558
+ return isRecord6(value) && isString(value.path) && isString(value.message);
46199
46559
  }
46200
46560
  function isUnconfirmedMigrationHolders(value) {
46201
- return isRecord5(value) && Array.isArray(value.pids) && value.pids.every(isNumber) && isNumber(value.fromVersion) && isNumber(value.toVersion);
46561
+ return isRecord6(value) && Array.isArray(value.pids) && value.pids.every(isNumber) && isNumber(value.fromVersion) && isNumber(value.toVersion);
46202
46562
  }
46203
46563
  function isNullableNumber(value) {
46204
46564
  return value === null || isNumber(value);
@@ -46210,7 +46570,7 @@ function isCacheTtlSource(value) {
46210
46570
  return isString(value);
46211
46571
  }
46212
46572
  function checkStatusViewSource(candidate) {
46213
- if (!isRecord5(candidate))
46573
+ if (!isRecord6(candidate))
46214
46574
  return { ok: false, fields: ["(the payload is not an object)"] };
46215
46575
  const missing = [];
46216
46576
  const numbers = {};
@@ -46249,7 +46609,7 @@ function checkStatusViewSource(candidate) {
46249
46609
  lastDreamerRunAt: optional.read("lastDreamerRunAt", isNullableNumber),
46250
46610
  dreamerUnsupportedTasks: optional.readArray("dreamerUnsupportedTasks", isString),
46251
46611
  dreamerSkipped: optional.readArray("dreamerSkipped", isString),
46252
- dreamerFailures: optional.readArray("dreamerFailures", (value) => isRecord5(value) && isString(value.task) && isString(value.error)),
46612
+ dreamerFailures: optional.readArray("dreamerFailures", (value) => isRecord6(value) && isString(value.task) && isString(value.error)),
46253
46613
  dreamerTickFailure: optional.read("dreamerTickFailure", (value) => value === null || isTickFailure(value)),
46254
46614
  sessionNoteCount: optional.read("sessionNoteCount", isNumber),
46255
46615
  readySmartNoteCount: optional.read("readySmartNoteCount", isNumber),