@cortexkit/pi-magic-context 0.41.3 → 0.41.4

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
@@ -1,4 +1,5 @@
1
1
  import {
2
+ sanitizeDiagnosticText,
2
3
  hasShareabilitySensitiveText,
3
4
  setHarness,
4
5
  getHarness,
@@ -83,6 +84,7 @@ import {
83
84
  releaseCompartmentLease,
84
85
  releaseCompartmentLeaseBestEffort,
85
86
  isCompartmentLeaseHeld,
87
+ canonicalModelIdentity,
86
88
  piModelRefToCanonical,
87
89
  resolveModelRefForPi,
88
90
  modelRefLookupOrder,
@@ -116,6 +118,7 @@ import {
116
118
  copySessionStateForClone,
117
119
  getErrorMessage,
118
120
  describeError,
121
+ piHarnessKindFromExecutable,
119
122
  setStoragePrivatePermissionEnforcement,
120
123
  getSchemaFenceRejection,
121
124
  getMigrationOnOpenRefusal,
@@ -309,7 +312,11 @@ import {
309
312
  isSaneLimit,
310
313
  resolveOutputReserve,
311
314
  getSdkContextLimit,
315
+ resolveModelConfigValue,
312
316
  resolveModelConfigOrDefault,
317
+ formatConfigParseStatusLine,
318
+ formatConfigParseNotice,
319
+ claimConfigParseFailuresOnce,
313
320
  promptSurfaceHashMaterial,
314
321
  createPromptSurfaceRuntime,
315
322
  createPromptSurfaceGuidanceEpochCache,
@@ -323,6 +330,7 @@ import {
323
330
  withContentLanguageDirective,
324
331
  withMigrationLanguageDirective,
325
332
  buildPrimaryLanguageDirective,
333
+ resolvePiHarnessDetection,
326
334
  resolvePiHarnessKind,
327
335
  computeCueContentHash,
328
336
  hasMuralCueColumns,
@@ -437,7 +445,7 @@ import {
437
445
  registerTodoStateLifecycle,
438
446
  syncCtxMemoryToolEnabled,
439
447
  registerMagicContextTools
440
- } from "./index-s0zw9ddk.js";
448
+ } from "./index-jc59f2q7.js";
441
449
  import {
442
450
  pushNotification2
443
451
  } from "./index-b3eqj1g6.js";
@@ -1508,16 +1516,20 @@ function deriveHistorianChunkTokens(historianContextLimit) {
1508
1516
  const derived = Math.round(historianContextLimit * HISTORIAN_CHUNK_PERCENTAGE);
1509
1517
  return Math.max(HISTORIAN_CHUNK_MIN, Math.min(HISTORIAN_CHUNK_MAX, derived));
1510
1518
  }
1519
+ function resolveKnownHistorianContextLimit(historianModelOverride) {
1520
+ if (typeof historianModelOverride !== "string" || !historianModelOverride.includes("/")) {
1521
+ return;
1522
+ }
1523
+ const [providerID, ...rest] = historianModelOverride.split("/");
1524
+ const modelID = rest.join("/");
1525
+ if (!providerID || !modelID)
1526
+ return;
1527
+ const limit = getSdkContextLimit(providerID, modelID, undefined, { reservation: "none" });
1528
+ return typeof limit === "number" && limit > 0 ? limit : undefined;
1529
+ }
1511
1530
  function resolveHistorianContextLimit(historianModelOverride) {
1512
1531
  if (typeof historianModelOverride === "string" && historianModelOverride.includes("/")) {
1513
- const [providerID, ...rest] = historianModelOverride.split("/");
1514
- const modelID = rest.join("/");
1515
- if (providerID && modelID) {
1516
- const limit = getSdkContextLimit(providerID, modelID);
1517
- if (typeof limit === "number" && limit > 0)
1518
- return limit;
1519
- }
1520
- return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
1532
+ return resolveKnownHistorianContextLimit(historianModelOverride) ?? DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
1521
1533
  }
1522
1534
  if (typeof historianModelOverride === "string" && historianModelOverride.trim() !== "") {
1523
1535
  console.warn(`[magic-context] historian.model "${historianModelOverride}" lacks provider prefix ("provider/model-id"); using the default context limit for chunk-budget derivation.`);
@@ -1933,6 +1945,16 @@ function shouldShowAnnouncement() {
1933
1945
  return ordering > 0;
1934
1946
  }
1935
1947
 
1948
+ // ../plugin/src/shared/cache-ttl-seed.ts
1949
+ function seedSessionCacheTtlIfUnsynced(args) {
1950
+ const meta = getOrCreateSessionMeta(args.db, args.sessionId);
1951
+ if (meta.lastResponseTime !== 0 || meta.lastObservedModelKey !== null)
1952
+ return false;
1953
+ const cacheTtl = typeof args.configured === "string" ? args.configured : resolveModelConfigValue(args.configured, args.modelKey)?.value ?? args.configured.default ?? "5m";
1954
+ updateSessionMeta(args.db, args.sessionId, { cacheTtl });
1955
+ return true;
1956
+ }
1957
+
1936
1958
  // ../plugin/src/shared/keep-subagents.ts
1937
1959
  var keepSubagents = false;
1938
1960
  function setKeepSubagents(value) {
@@ -2357,6 +2379,11 @@ var DEFAULT_PI_RESOLUTION_FS = {
2357
2379
  realpathSync: realpathSync2,
2358
2380
  readFileSync: readFileSync2
2359
2381
  };
2382
+ function packageHarnessKind(packageName) {
2383
+ if (packageName === "@oh-my-pi/pi-coding-agent")
2384
+ return "omp";
2385
+ return typeof packageName === "string" && PI_CODING_AGENT_PACKAGE_NAMES.has(packageName) ? "pi" : undefined;
2386
+ }
2360
2387
  function readPiManifest(packageJsonPath, fs) {
2361
2388
  try {
2362
2389
  const manifest = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
@@ -2422,32 +2449,38 @@ function resolvePiBin(found, fs, checkedPaths) {
2422
2449
  function resolvePiCliFromPackageJson(packageJsonPath, fs, checkedPaths) {
2423
2450
  checkedPaths.push(packageJsonPath);
2424
2451
  const manifest = readPiManifest(packageJsonPath, fs);
2425
- if (typeof manifest?.name !== "string" || !PI_CODING_AGENT_PACKAGE_NAMES.has(manifest.name)) {
2426
- return null;
2452
+ const targetHarness = packageHarnessKind(manifest?.name);
2453
+ if (!manifest || targetHarness === undefined) {
2454
+ return { cliPath: null, targetHarness: null, checkedPaths };
2427
2455
  }
2428
- return resolvePiBin({ dir: dirname(packageJsonPath), manifest }, fs, checkedPaths);
2456
+ return {
2457
+ cliPath: resolvePiBin({ dir: dirname(packageJsonPath), manifest }, fs, checkedPaths),
2458
+ targetHarness,
2459
+ checkedPaths
2460
+ };
2429
2461
  }
2430
2462
  function resolvePiCliFromRunningEntry(entry, fs) {
2431
2463
  const checkedPaths = [entry ?? "process.argv[1] (missing)"];
2432
2464
  if (!entry || entry.startsWith(BUN_VIRTUAL_SCRIPT_PREFIX)) {
2433
- return { cliPath: null, checkedPaths };
2465
+ return { cliPath: null, targetHarness: null, checkedPaths };
2434
2466
  }
2435
2467
  try {
2436
2468
  if (!fs.existsSync(entry) || !fs.statSync(entry).isFile()) {
2437
- return { cliPath: null, checkedPaths };
2469
+ return { cliPath: null, targetHarness: null, checkedPaths };
2438
2470
  }
2439
2471
  const realEntry = fs.realpathSync(entry);
2440
2472
  if (realEntry !== entry)
2441
2473
  checkedPaths.push(realEntry);
2442
2474
  const found = findPiPackageRoot(dirname(realEntry), fs);
2443
2475
  if (!found)
2444
- return { cliPath: null, checkedPaths };
2476
+ return { cliPath: null, targetHarness: null, checkedPaths };
2445
2477
  return {
2446
2478
  cliPath: resolvePiBin(found, fs, checkedPaths),
2479
+ targetHarness: packageHarnessKind(found.manifest.name) ?? null,
2447
2480
  checkedPaths
2448
2481
  };
2449
2482
  } catch {
2450
- return { cliPath: null, checkedPaths };
2483
+ return { cliPath: null, targetHarness: null, checkedPaths };
2451
2484
  }
2452
2485
  }
2453
2486
  function defaultResolvePiPackageJson(from) {
@@ -2464,7 +2497,7 @@ function resolveBundledPiCli(from, fs, resolvePackageJson, checkedPaths) {
2464
2497
  const packageJsonPath = resolvePackageJson(from);
2465
2498
  if (!packageJsonPath) {
2466
2499
  checkedPaths.push(`${PI_CODING_AGENT_MODULE}/package.json (unresolvable from running module paths)`);
2467
- return null;
2500
+ return { cliPath: null, targetHarness: null, checkedPaths };
2468
2501
  }
2469
2502
  return resolvePiCliFromPackageJson(packageJsonPath, fs, checkedPaths);
2470
2503
  }
@@ -2519,14 +2552,26 @@ function resolvePiInvocation(options = {}) {
2519
2552
  const running = resolvePiCliFromRunningEntry(currentScript, fs);
2520
2553
  const checkedPaths = [...running.checkedPaths];
2521
2554
  if (running.cliPath) {
2522
- return { command: execPath, prefixArgs: [running.cliPath] };
2555
+ return {
2556
+ command: execPath,
2557
+ prefixArgs: [running.cliPath],
2558
+ targetHarness: running.targetHarness ?? piHarnessKindFromExecutable(running.cliPath) ?? resolvePiHarnessKind()
2559
+ };
2523
2560
  }
2524
2561
  if (!isGenericRuntimeExecutable(execPath)) {
2525
- return { command: execPath, prefixArgs: [] };
2562
+ return {
2563
+ command: execPath,
2564
+ prefixArgs: [],
2565
+ targetHarness: piHarnessKindFromExecutable(execPath) ?? resolvePiHarnessKind()
2566
+ };
2526
2567
  }
2527
2568
  const bundled = resolveBundledPiCli(currentScript, fs, resolvePackageJson, checkedPaths);
2528
- if (bundled) {
2529
- return { command: execPath, prefixArgs: [bundled] };
2569
+ if (bundled.cliPath) {
2570
+ return {
2571
+ command: execPath,
2572
+ prefixArgs: [bundled.cliPath],
2573
+ targetHarness: bundled.targetHarness ?? piHarnessKindFromExecutable(bundled.cliPath) ?? resolvePiHarnessKind()
2574
+ };
2530
2575
  }
2531
2576
  const detectionMiss = `script-detection miss on ${formatCheckedPaths(checkedPaths)}`;
2532
2577
  if (platform === "win32") {
@@ -2538,6 +2583,7 @@ function resolvePiInvocation(options = {}) {
2538
2583
  return {
2539
2584
  command,
2540
2585
  prefixArgs: ["/d", "/s", "/c", resolved.path],
2586
+ targetHarness: piHarnessKindFromExecutable(resolved.path) ?? "pi",
2541
2587
  fallbackDiagnostic: diagnostic
2542
2588
  };
2543
2589
  }
@@ -2552,12 +2598,14 @@ function resolvePiInvocation(options = {}) {
2552
2598
  "-File",
2553
2599
  resolved.path
2554
2600
  ],
2601
+ targetHarness: piHarnessKindFromExecutable(resolved.path) ?? "pi",
2555
2602
  fallbackDiagnostic: diagnostic
2556
2603
  };
2557
2604
  }
2558
2605
  return {
2559
2606
  command: resolved.path,
2560
2607
  prefixArgs: [],
2608
+ targetHarness: piHarnessKindFromExecutable(resolved.path) ?? "pi",
2561
2609
  fallbackDiagnostic: diagnostic
2562
2610
  };
2563
2611
  }
@@ -2566,6 +2614,7 @@ function resolvePiInvocation(options = {}) {
2566
2614
  return {
2567
2615
  command: "pi",
2568
2616
  prefixArgs: [],
2617
+ targetHarness: "pi",
2569
2618
  fallbackDiagnostic: `script-detection miss on ${formatCheckedPaths(checkedPaths)}`
2570
2619
  };
2571
2620
  }
@@ -2593,8 +2642,8 @@ function normalizedOmpProfile() {
2593
2642
  const raw = (process.env.OMP_PROFILE ?? process.env.PI_PROFILE)?.trim();
2594
2643
  return raw && raw !== "default" && /^[a-z0-9][a-z0-9._-]{0,63}$/.test(raw) ? raw : undefined;
2595
2644
  }
2596
- function getHostAgentSettingsDir() {
2597
- if (!isOmpHostProcess())
2645
+ function getHostAgentSettingsDir(targetHarness) {
2646
+ if (targetHarness !== "omp")
2598
2647
  return join2(homedir(), ".pi", "agent");
2599
2648
  const configRoot = join2(homedir(), process.env.PI_CONFIG_DIR?.trim() || ".omp");
2600
2649
  const profile = normalizedOmpProfile();
@@ -2603,20 +2652,20 @@ function getHostAgentSettingsDir() {
2603
2652
  const configured = process.env.PI_CODING_AGENT_DIR?.trim();
2604
2653
  return configured ? resolvePath(configured) : join2(configRoot, "agent");
2605
2654
  }
2606
- function modelRefToCanonicalForHost(ref) {
2607
- return isOmpHostProcess() ? ompModelRefToCanonical(ref) : piModelRefToCanonical(ref);
2655
+ function modelRefToCanonicalForTarget(ref, targetHarness) {
2656
+ return targetHarness === "omp" ? ompModelRefToCanonical(ref) : piModelRefToCanonical(ref);
2608
2657
  }
2609
- function resolveModelRefForHost(ref) {
2610
- return isOmpHostProcess() ? resolveModelRefForOmp(ref) : resolveModelRefForPi(ref);
2658
+ function resolveModelRefForTarget(ref, targetHarness) {
2659
+ return targetHarness === "omp" ? resolveModelRefForOmp(ref) : resolveModelRefForPi(ref);
2611
2660
  }
2612
2661
  var configuredSubagentExtensions;
2613
2662
  function configurePiSubagentExtensions(extensions) {
2614
2663
  configuredSubagentExtensions = extensions?.slice();
2615
2664
  }
2616
- function resolveSubagentExtensionEntry(entry) {
2665
+ function resolveSubagentExtensionEntry(entry, targetHarness) {
2617
2666
  const trimmed = entry.trim();
2618
2667
  const isNpmSource = trimmed.startsWith("npm:");
2619
- return !isNpmSource && !isAbsolute(trimmed) ? resolvePath(getHostAgentSettingsDir(), trimmed) : trimmed;
2668
+ return !isNpmSource && !isAbsolute(trimmed) ? resolvePath(getHostAgentSettingsDir(targetHarness), trimmed) : trimmed;
2620
2669
  }
2621
2670
  var PI_READ_ONLY_BUILTINS = ["read", "grep", "find", "ls"];
2622
2671
  var PI_AFT_READ_TOOLS = ["aft_outline", "aft_zoom", "aft_search"];
@@ -2722,14 +2771,18 @@ class PiSubagentRunner {
2722
2771
  extraArgs;
2723
2772
  subagentExtensions;
2724
2773
  constructor(options = {}) {
2725
- this.invocation = options.invocation ?? (options.piBinary ? { command: options.piBinary, prefixArgs: [] } : resolvePiInvocation({ platform: options.platform }));
2774
+ this.invocation = options.invocation ?? (options.piBinary ? {
2775
+ command: options.piBinary,
2776
+ prefixArgs: [],
2777
+ targetHarness: piHarnessKindFromExecutable(options.piBinary) ?? resolvePiHarnessKind()
2778
+ } : resolvePiInvocation({ platform: options.platform }));
2726
2779
  this.spawnImpl = options.spawnImpl ?? childProcess.spawn;
2727
2780
  this.platform = options.platform ?? process.platform;
2728
2781
  this.extraArgs = options.extraArgs ?? [];
2729
2782
  this.subagentExtensions = options.subagentExtensions ?? configuredSubagentExtensions;
2730
2783
  }
2731
2784
  async run(options) {
2732
- const providerAttempt = resolveProviderModelAttempt(options.model);
2785
+ const providerAttempt = resolveProviderModelAttempt(options.model, this.invocation.targetHarness);
2733
2786
  const firstOptions = providerAttempt ? { ...options, model: providerAttempt.canonicalRef } : options;
2734
2787
  const firstRun = await this.runWithExtensionRetry(firstOptions, providerAttempt?.modelRef);
2735
2788
  if (!providerAttempt)
@@ -2903,6 +2956,7 @@ class PiSubagentRunner {
2903
2956
  }
2904
2957
  }
2905
2958
  const args = buildArgs(options, {
2959
+ targetHarness: this.invocation.targetHarness,
2906
2960
  disableDiscoveredExtensions: runMode.disableDiscoveredExtensions,
2907
2961
  subagentExtensions: this.subagentExtensions,
2908
2962
  omitPositionalMessage: deliverViaStdin,
@@ -3284,14 +3338,14 @@ function replaceProviderPrefix(ref, provider) {
3284
3338
  const slash = ref.indexOf("/");
3285
3339
  return slash > 0 ? `${provider}${ref.slice(slash)}` : ref;
3286
3340
  }
3287
- function resolveProviderModelAttempt(model) {
3341
+ function resolveProviderModelAttempt(model, targetHarness) {
3288
3342
  if (typeof model !== "string" || model.length === 0)
3289
3343
  return;
3290
- const canonicalRef = modelRefToCanonicalForHost(model);
3344
+ const canonicalRef = modelRefToCanonicalForTarget(model, targetHarness);
3291
3345
  const canonicalProvider = providerPrefix(canonicalRef);
3292
3346
  if (!canonicalProvider)
3293
3347
  return;
3294
- const translatedRef = resolveModelRefForHost(canonicalRef);
3348
+ const translatedRef = resolveModelRefForTarget(canonicalRef, targetHarness);
3295
3349
  const translatedProvider = providerPrefix(translatedRef);
3296
3350
  const cachedProvider = PI_PROVIDER_FORM_CACHE.get(canonicalProvider);
3297
3351
  if (!translatedProvider || translatedProvider === canonicalProvider && cachedProvider === undefined) {
@@ -3311,21 +3365,22 @@ function isProviderCredentialFailure(result, attempt) {
3311
3365
  }
3312
3366
  var PROMPT_ARGV_MAX_BYTES = 96 * 1024;
3313
3367
  function buildArgs(options, opts) {
3314
- const ompHost = isOmpHostProcess();
3368
+ const targetHarness = opts?.targetHarness ?? resolvePiHarnessKind();
3369
+ const ompTarget = targetHarness === "omp";
3315
3370
  const args = [
3316
3371
  "--print",
3317
3372
  "--mode",
3318
3373
  "json",
3319
3374
  "--no-session",
3320
3375
  "--no-skills",
3321
- ...ompHost ? ["--no-rules"] : ["--no-prompt-templates", "--no-context-files"]
3376
+ ...ompTarget ? ["--no-rules"] : ["--no-prompt-templates", "--no-context-files"]
3322
3377
  ];
3323
3378
  if (opts?.disableDiscoveredExtensions || opts?.subagentExtensions !== undefined) {
3324
3379
  args.push("--no-extensions");
3325
3380
  }
3326
3381
  if (opts?.subagentExtensions !== undefined) {
3327
3382
  for (const extension of opts.subagentExtensions) {
3328
- args.push("--extension", resolveSubagentExtensionEntry(extension));
3383
+ args.push("--extension", resolveSubagentExtensionEntry(extension, targetHarness));
3329
3384
  }
3330
3385
  }
3331
3386
  const subagentEntryPath = opts?.subagentEntryPath ?? SUBAGENT_ENTRY_PATH;
@@ -3345,7 +3400,7 @@ function buildArgs(options, opts) {
3345
3400
  sessionLog(options.accountingSessionId ?? "pi-subagent", `Pi subagent agent "${options.agent}" has no strict tool allow-list; forcing --no-tools`);
3346
3401
  args.push("--no-tools");
3347
3402
  } else {
3348
- const hostTools = resolveHostToolAllowlist(strictTools, ompHost);
3403
+ const hostTools = resolveHostToolAllowlist(strictTools, ompTarget);
3349
3404
  if (hostTools.length > 0) {
3350
3405
  args.push("--tools", hostTools.join(","));
3351
3406
  } else {
@@ -3356,7 +3411,7 @@ function buildArgs(options, opts) {
3356
3411
  args.push("--system-prompt", opts.systemPromptPath);
3357
3412
  }
3358
3413
  if (typeof options.model === "string" && options.model.length > 0) {
3359
- args.push("--model", opts?.modelRef ?? resolveModelRefForHost(options.model));
3414
+ args.push("--model", opts?.modelRef ?? resolveModelRefForTarget(options.model, targetHarness));
3360
3415
  }
3361
3416
  if (options.thinkingLevel) {
3362
3417
  args.push("--thinking", options.thinkingLevel);
@@ -4301,6 +4356,10 @@ var ABORT_CALL_TIMEOUT_MS = 3000;
4301
4356
  function copyPromptArgs(args, body) {
4302
4357
  return { ...args, body: { ...body } };
4303
4358
  }
4359
+ var promptFailureDetails = new WeakMap;
4360
+ function getPromptFailureDetail(error) {
4361
+ return error !== null && typeof error === "object" ? promptFailureDetails.get(error) ?? null : null;
4362
+ }
4304
4363
  function extractMessage(error) {
4305
4364
  if (typeof error === "string")
4306
4365
  return error;
@@ -4413,6 +4472,39 @@ function shortErr(error) {
4413
4472
  }
4414
4473
  return extractMessage(error);
4415
4474
  }
4475
+ function classifyPromptFailure(error, phase, externalSignal) {
4476
+ const message = extractMessage(error);
4477
+ if (externalSignal?.aborted || message === "prompt aborted by external signal") {
4478
+ return "child_aborted";
4479
+ }
4480
+ if (/^prompt timed out after \d+ms$/.test(message))
4481
+ return "provider_timeout";
4482
+ if (phase === "validation") {
4483
+ if (/returned no (?:assistant )?output|no assistant output/i.test(message)) {
4484
+ return "empty_completion";
4485
+ }
4486
+ if (error instanceof Error && error.name === "DreamerProviderOutputFailureError") {
4487
+ return "provider_error";
4488
+ }
4489
+ return "parse_failed";
4490
+ }
4491
+ return phase === "prompt" || phase === "output" ? "provider_error" : "unknown";
4492
+ }
4493
+ function throwWithPromptFailure(legacyError, failedAttempts, args, timeoutMs) {
4494
+ const error = legacyError instanceof Error ? legacyError : new Error(extractMessage(legacyError));
4495
+ const last = failedAttempts.at(-1);
4496
+ const failureClass = last?.failureClass ?? "unknown";
4497
+ const providerError = failureClass === "provider_error" ? sanitizeDiagnosticText(shortErr(last?.error ?? legacyError)).slice(0, 500) : null;
4498
+ promptFailureDetails.set(error, {
4499
+ failureClass,
4500
+ modelAttempted: last?.attempt.label ?? null,
4501
+ modelsTried: failedAttempts.map((failure) => failure.attempt.label),
4502
+ providerError,
4503
+ timeoutMs: failureClass === "provider_timeout" ? timeoutMs : null,
4504
+ childSessionId: args.path.id || null
4505
+ });
4506
+ throw error;
4507
+ }
4416
4508
  async function attemptOnce(client, args, timeoutMs, signal, callContext, label) {
4417
4509
  const originalBody = { ...args.body };
4418
4510
  const attemptArgs = copyPromptArgs(args, originalBody);
@@ -4491,10 +4583,35 @@ async function promptSyncWithModelSuggestionRetry(client, args, options = {}) {
4491
4583
  throw lastError ?? new Error("All fallback models failed");
4492
4584
  }
4493
4585
  async function attemptAndValidate(client, args, timeoutMs, signal, callContext, attempt, options) {
4494
- await attemptOnce(client, args, timeoutMs, signal, callContext, attempt.label);
4495
- const output = await options.fetchOutput(args, attempt);
4496
- const validated = await options.validateOutput(output, attempt);
4497
- return { output, validated, attempt };
4586
+ try {
4587
+ await attemptOnce(client, args, timeoutMs, signal, callContext, attempt.label);
4588
+ } catch (error) {
4589
+ throw {
4590
+ error,
4591
+ failureClass: classifyPromptFailure(error, "prompt", signal),
4592
+ attempt
4593
+ };
4594
+ }
4595
+ let output;
4596
+ try {
4597
+ output = await options.fetchOutput(args, attempt);
4598
+ } catch (error) {
4599
+ throw {
4600
+ error,
4601
+ failureClass: classifyPromptFailure(error, "output", signal),
4602
+ attempt
4603
+ };
4604
+ }
4605
+ try {
4606
+ const validated = await options.validateOutput(output, attempt);
4607
+ return { output, validated, attempt };
4608
+ } catch (error) {
4609
+ throw {
4610
+ error,
4611
+ failureClass: classifyPromptFailure(error, "validation", signal),
4612
+ attempt
4613
+ };
4614
+ }
4498
4615
  }
4499
4616
  async function promptSyncWithValidatedOutputRetry(client, args, options) {
4500
4617
  const timeoutMs = options.timeoutMs ?? 300000;
@@ -4504,6 +4621,7 @@ async function promptSyncWithValidatedOutputRetry(client, args, options) {
4504
4621
  const baseArgs = copyPromptArgs(args, baseBody);
4505
4622
  const explicitPrimaryLabel = baseBody.model?.providerID && baseBody.model.modelID ? `${baseBody.model.providerID}/${baseBody.model.modelID}` : "primary";
4506
4623
  const totalAttempts = fallbacks.length + 1;
4624
+ const failedAttempts = [];
4507
4625
  let firstError = null;
4508
4626
  let lastError = null;
4509
4627
  try {
@@ -4514,15 +4632,18 @@ async function promptSyncWithValidatedOutputRetry(client, args, options) {
4514
4632
  totalAttempts,
4515
4633
  model: baseBody.model
4516
4634
  }, options);
4517
- } catch (error) {
4518
- firstError = error;
4519
- lastError = error;
4520
- if (isNonRetryable(error, options.signal))
4521
- throw error;
4635
+ } catch (caught) {
4636
+ const failure = caught;
4637
+ failedAttempts.push(failure);
4638
+ firstError = failure.error;
4639
+ lastError = failure.error;
4640
+ if (isNonRetryable(failure.error, options.signal)) {
4641
+ throwWithPromptFailure(failure.error, failedAttempts, args, timeoutMs);
4642
+ }
4522
4643
  if (fallbacks.length === 0) {
4523
- throw error;
4644
+ throwWithPromptFailure(failure.error, failedAttempts, args, timeoutMs);
4524
4645
  }
4525
- log(`[${callContext}] primary (${explicitPrimaryLabel}) failed validation/prompt: ${shortErr(error)}; trying ${fallbacks.length} fallback(s)`);
4646
+ log(`[${callContext}] primary (${explicitPrimaryLabel}) failed validation/prompt: ${shortErr(failure.error)}; trying ${fallbacks.length} fallback(s)`);
4526
4647
  }
4527
4648
  for (let i = 0;i < fallbacks.length; i += 1) {
4528
4649
  const fallback = toModelEntry(fallbacks[i]);
@@ -4549,20 +4670,21 @@ async function promptSyncWithValidatedOutputRetry(client, args, options) {
4549
4670
  const result = await attemptAndValidate(client, attemptArgs, timeoutMs, options.signal, callContext, attempt, options);
4550
4671
  log(`[${callContext}] fallback succeeded with ${label} (attempt ${i + 2}/${fallbacks.length + 1})`);
4551
4672
  return result;
4552
- } catch (error) {
4553
- if (firstError === null)
4554
- firstError = error;
4555
- lastError = error;
4556
- if (isNonRetryable(error, options.signal))
4557
- throw error;
4673
+ } catch (caught) {
4674
+ const failure = caught;
4675
+ failedAttempts.push(failure);
4676
+ lastError = failure.error;
4677
+ if (isNonRetryable(failure.error, options.signal)) {
4678
+ throwWithPromptFailure(failure.error, failedAttempts, args, timeoutMs);
4679
+ }
4558
4680
  const remaining = fallbacks.length - i - 1;
4559
4681
  if (remaining > 0) {
4560
- log(`[${callContext}] ${label} failed validation/prompt: ${shortErr(error)}; ${remaining} fallback(s) left`);
4682
+ log(`[${callContext}] ${label} failed validation/prompt: ${shortErr(failure.error)}; ${remaining} fallback(s) left`);
4561
4683
  }
4562
4684
  }
4563
4685
  }
4564
- log(`[${callContext}] all models exhausted; tried: ${[explicitPrimaryLabel, ...fallbacks.map((fallback) => toModelEntry(fallback)?.model ?? String(fallback))].join(", ")}; original error: ${shortErr(firstError)}; last error: ${shortErr(lastError)}`);
4565
- throw firstError ?? lastError ?? new Error("All fallback models failed validation");
4686
+ log(`[${callContext}] all models exhausted; tried: ${failedAttempts.map((failure) => failure.attempt.label).join(", ")}; original error: ${shortErr(firstError)}; last error: ${shortErr(lastError)}`);
4687
+ throwWithPromptFailure(firstError ?? lastError ?? new Error("All fallback models failed validation"), failedAttempts, args, timeoutMs);
4566
4688
  }
4567
4689
  // ../plugin/src/shared/normalize-sdk-response.ts
4568
4690
  function normalizeSDKResponse(response, fallback, options) {
@@ -6401,7 +6523,7 @@ async function classifyOneChunk(args, chunk, anchors, sliceMs, signal) {
6401
6523
  const desc = describeError(failure);
6402
6524
  log(`[dreamer] classify chunk failed: ${desc.brief}`, desc.stackHead ? { stackHead: desc.stackHead } : undefined);
6403
6525
  recordInvocation(args, startedAt, { status: "failed", error: failure });
6404
- if (moduleRoute || signal.aborted || failure instanceof DreamerProviderOutputFailureError)
6526
+ if (moduleRoute || signal.aborted || failure instanceof DreamerProviderOutputFailureError || getPromptFailureDetail(failure)?.failureClass !== "parse_failed" && getPromptFailureDetail(failure) !== null)
6405
6527
  throw failure;
6406
6528
  return { classified: 0, changed: 0 };
6407
6529
  } finally {
@@ -9702,6 +9824,16 @@ function parseJsonRecord(value) {
9702
9824
  }
9703
9825
 
9704
9826
  // ../plugin/src/features/magic-context/dreamer/storage-dream-runs.ts
9827
+ function formatDreamRunFailure(failure) {
9828
+ const parts = [failure.failure_class];
9829
+ if (failure.model_attempted)
9830
+ parts.push(`model: ${failure.model_attempted}`);
9831
+ if (failure.provider_error)
9832
+ parts.push(failure.provider_error.split(/\r?\n/, 1)[0].trim());
9833
+ else if (failure.timeout_ms !== null)
9834
+ parts.push(`timeout: ${failure.timeout_ms}ms`);
9835
+ return parts.join(" · ");
9836
+ }
9705
9837
  var insertDreamRunStatements = new WeakMap;
9706
9838
  var getDreamRunsByProjectStatements = new Map;
9707
9839
  function getInsertDreamRunStatement(db) {
@@ -10111,9 +10243,10 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
10111
10243
  } catch (error) {
10112
10244
  const desc = describeError(error);
10113
10245
  const providerFailure = error instanceof DreamerProviderOutputFailureError ? error : undefined;
10246
+ const promptFailure = getPromptFailureDetail(error);
10114
10247
  log(`[dreamer] verify batch ${providerFailure ? "provider failure" : "failed"}: ${desc.brief}`, desc.stackHead ? { stackHead: desc.stackHead } : undefined);
10115
10248
  recordInvocation4(args, startedAt, { status: "failed", error });
10116
- if (error instanceof DreamerModuleFailureError || signal.aborted)
10249
+ if (error instanceof DreamerModuleFailureError || signal.aborted || promptFailure !== null && promptFailure.failureClass !== "parse_failed" && !providerFailure)
10117
10250
  throw error;
10118
10251
  return {
10119
10252
  verified: 0,
@@ -10337,6 +10470,30 @@ function recordInvocation4(args, startedAt, params) {
10337
10470
  }
10338
10471
 
10339
10472
  // ../plugin/src/features/magic-context/dreamer/task-executor.ts
10473
+ function dreamRunFailureDetail(error) {
10474
+ const prompt = getPromptFailureDetail(error);
10475
+ if (prompt) {
10476
+ return {
10477
+ failure_class: prompt.failureClass,
10478
+ model_attempted: prompt.modelAttempted,
10479
+ models_tried: prompt.modelsTried,
10480
+ provider_error: prompt.providerError,
10481
+ timeout_ms: prompt.timeoutMs,
10482
+ child_session_id: prompt.childSessionId
10483
+ };
10484
+ }
10485
+ const described = describeError(error);
10486
+ const message = described.brief;
10487
+ const providerFailure = error instanceof Error && error.name === "DreamerProviderOutputFailureError";
10488
+ return {
10489
+ failure_class: providerFailure ? "provider_error" : /no models?|model chain is empty/i.test(message) ? "no_models" : "unknown",
10490
+ model_attempted: null,
10491
+ models_tried: [],
10492
+ provider_error: providerFailure ? sanitizeDiagnosticText(message).slice(0, 500) : null,
10493
+ timeout_ms: null,
10494
+ child_session_id: null
10495
+ };
10496
+ }
10340
10497
  function classifyFailure(error) {
10341
10498
  const described = describeError(error);
10342
10499
  const brief = described.brief;
@@ -10453,6 +10610,9 @@ function createDreamTaskExecutor(deps) {
10453
10610
  durationMs: Date.now() - startedAt,
10454
10611
  resultChars: 0,
10455
10612
  ...status === "failed" && error ? { error } : {},
10613
+ ...status === "failed" ? {
10614
+ failure: extra?.failure ?? dreamRunFailureDetail(error ?? "unknown dreamer failure")
10615
+ } : {},
10456
10616
  ...extra?.progress ? { progress: extra.progress } : {},
10457
10617
  backlog: (() => {
10458
10618
  const end = extra?.backlogAfter ?? getDreamTaskBacklog(db, projectIdentity, config.task);
@@ -10753,9 +10913,15 @@ function createDreamTaskExecutor(deps) {
10753
10913
  });
10754
10914
  } catch (error) {
10755
10915
  const { transient, brief } = classifyFailure(error);
10756
- recordRun("failed", brief);
10916
+ const failure = dreamRunFailureDetail(error);
10917
+ recordRun("failed", brief, { failure });
10757
10918
  log(`[dreamer] task ${config.task} failed (transient=${transient}): ${brief}`);
10758
- return { status: "failed", transient, error: brief };
10919
+ return {
10920
+ status: "failed",
10921
+ transient,
10922
+ error: brief,
10923
+ failureDetail: formatDreamRunFailure(failure)
10924
+ };
10759
10925
  } finally {
10760
10926
  deps.onProgress?.(null, config.task);
10761
10927
  }
@@ -11292,10 +11458,10 @@ async function runDomainGroup(deps, group, cb) {
11292
11458
  cb?.onRan?.(due.config.task);
11293
11459
  } else if (outcome.transient) {
11294
11460
  recordTransientFailure(db, projectIdentity, due, finishedAt, outcome.error ?? null);
11295
- cb?.onFailed?.(due.config.task, outcome.error);
11461
+ cb?.onFailed?.(due.config.task, outcome.failureDetail ?? outcome.error);
11296
11462
  } else {
11297
11463
  advanceAfterRun(db, projectIdentity, due, finishedAt, "failed", outcome.error ?? null);
11298
- cb?.onFailed?.(due.config.task, outcome.error);
11464
+ cb?.onFailed?.(due.config.task, outcome.failureDetail ?? outcome.error);
11299
11465
  }
11300
11466
  }
11301
11467
  } finally {
@@ -14371,6 +14537,16 @@ function rearmChannel2AfterMeasuredCollapse(input) {
14371
14537
  }
14372
14538
 
14373
14539
  // ../plugin/src/hooks/magic-context/protected-tail-boundary.ts
14540
+ function describeBoundaryDiagnostics(snapshot) {
14541
+ return `boundaryDiagnostics=${JSON.stringify(snapshot.diagnostics ?? { unavailable: true, boundaryReason: snapshot.boundaryReason })}`;
14542
+ }
14543
+ function boundaryDiagnostics(args) {
14544
+ return {
14545
+ ...args,
14546
+ capTier: args.usagePercentage >= 95 ? "95" : args.usagePercentage >= 80 ? "80" : "normal",
14547
+ drainBudget: "not-reserved; does-not-size-cap"
14548
+ };
14549
+ }
14374
14550
  var ALPHA = 0.3;
14375
14551
  var FLOOR_RATIO = 0.08;
14376
14552
  var FLOOR_MIN = 2000;
@@ -14510,11 +14686,40 @@ function applyHeadCap(args) {
14510
14686
  capTokens,
14511
14687
  recentOpenArcCutoff
14512
14688
  } = args;
14513
- if (offset >= protectedTailStart)
14689
+ const diagnostics = {
14690
+ offset,
14691
+ capEnd: offset,
14692
+ capTokenSum: 0,
14693
+ completedFence: { from: offset, to: offset, arcs: [], tokenMass: 0 },
14694
+ oversizeAdmission: { from: offset, to: offset },
14695
+ openArcClamp: { from: offset, to: offset },
14696
+ completedRefence: { from: offset, to: offset, arcs: [], tokenMass: 0 },
14697
+ eligibleEnd: offset
14698
+ };
14699
+ if (offset >= protectedTailStart) {
14700
+ args.observeDiagnostics?.(diagnostics);
14514
14701
  return { eligibleEndOrdinal: offset, oversizeAtomicUnit: false };
14702
+ }
14515
14703
  let end = index.findHeadEndForCap(offset, protectedTailStart, capTokens);
14704
+ diagnostics.capEnd = end;
14705
+ diagnostics.capTokenSum = index.rangeTokens(offset, end);
14516
14706
  let oversizeAtomicUnit = end === offset + 1 && index.tokenForOrdinal(offset) > capTokens;
14517
- const completedFence = fenceBoundaryForCompletedToolArcs(end, arcs, lastCompartmentEndOrdinal + 1);
14707
+ let completedFence = fenceBoundaryForCompletedToolArcs(end, arcs, lastCompartmentEndOrdinal + 1, (component) => {
14708
+ diagnostics.completedFence.arcs = component.map((arc) => ({
14709
+ assistant: arc.invocation,
14710
+ result: arc.result
14711
+ }));
14712
+ diagnostics.completedFence.tokenMass = index.rangeTokens(Math.min(...component.map((arc) => arc.invocation)), Math.max(...component.map((arc) => arc.result)) + 1);
14713
+ });
14714
+ diagnostics.completedFence.from = end;
14715
+ diagnostics.completedFence.to = completedFence;
14716
+ diagnostics.oversizeAdmission.from = completedFence;
14717
+ if (completedFence <= offset && end > offset) {
14718
+ const afterFirstArc = fenceBoundaryForCompletedToolArcs(end, arcs, offset + 1);
14719
+ if (afterFirstArc <= protectedTailStart)
14720
+ completedFence = afterFirstArc;
14721
+ }
14722
+ diagnostics.oversizeAdmission.to = completedFence;
14518
14723
  if (completedFence > end) {
14519
14724
  end = Math.min(protectedTailStart, completedFence);
14520
14725
  if (index.rangeTokens(offset, end) > capTokens)
@@ -14522,6 +14727,7 @@ function applyHeadCap(args) {
14522
14727
  } else {
14523
14728
  end = completedFence;
14524
14729
  }
14730
+ diagnostics.openArcClamp.from = end;
14525
14731
  for (const arc of arcs) {
14526
14732
  const resOrdinal = arc.resOrdinal;
14527
14733
  if (resOrdinal === null) {
@@ -14530,6 +14736,18 @@ function applyHeadCap(args) {
14530
14736
  }
14531
14737
  }
14532
14738
  }
14739
+ diagnostics.openArcClamp.to = end;
14740
+ diagnostics.completedRefence.from = end;
14741
+ end = fenceBoundaryForCompletedToolArcs(end, arcs, lastCompartmentEndOrdinal + 1, (component) => {
14742
+ diagnostics.completedRefence.arcs = component.map((arc) => ({
14743
+ assistant: arc.invocation,
14744
+ result: arc.result
14745
+ }));
14746
+ diagnostics.completedRefence.tokenMass = index.rangeTokens(Math.min(...component.map((arc) => arc.invocation)), Math.max(...component.map((arc) => arc.result)) + 1);
14747
+ });
14748
+ diagnostics.completedRefence.to = end;
14749
+ diagnostics.eligibleEnd = end <= offset ? offset : Math.min(end, protectedTailStart);
14750
+ args.observeDiagnostics?.(diagnostics);
14533
14751
  if (end <= offset && offset < protectedTailStart) {
14534
14752
  return { eligibleEndOrdinal: offset, oversizeAtomicUnit };
14535
14753
  }
@@ -14658,6 +14876,11 @@ function resolveProtectedTailBoundary(ctx) {
14658
14876
  runtimeFloor = Math.max(runtimeFloor, ctx.priorBoundaryOrdinal);
14659
14877
  let protectedTailStart = Math.max(boundary, runtimeFloor);
14660
14878
  const forceMaterializationPercentage = escalationBands(ctx.executeThresholdPercentage).forceMaterializationPercentage;
14879
+ const livePromptFloor = {
14880
+ ordinal: null,
14881
+ from: protectedTailStart,
14882
+ to: protectedTailStart
14883
+ };
14661
14884
  if (!ctx.emergencyTailScale && usagePercentage < forceMaterializationPercentage) {
14662
14885
  let lastMeaningfulUserOrdinal = 0;
14663
14886
  for (let i = messages.length - 1;i >= 0; i--) {
@@ -14669,10 +14892,12 @@ function resolveProtectedTailBoundary(ctx) {
14669
14892
  lastMeaningfulUserOrdinal = message.ordinal;
14670
14893
  break;
14671
14894
  }
14895
+ livePromptFloor.ordinal = lastMeaningfulUserOrdinal || null;
14672
14896
  if (lastMeaningfulUserOrdinal >= offset) {
14673
14897
  protectedTailStart = Math.min(protectedTailStart, lastMeaningfulUserOrdinal);
14674
14898
  }
14675
14899
  }
14900
+ livePromptFloor.to = protectedTailStart;
14676
14901
  if (protectedTailStart > offset && index.rangeTokens(offset, protectedTailStart) <= NORMAL_HYSTERESIS_TOKENS) {
14677
14902
  protectedTailStart = offset;
14678
14903
  }
@@ -14683,7 +14908,11 @@ function resolveProtectedTailBoundary(ctx) {
14683
14908
  contextLimit: ctx.contextLimit,
14684
14909
  executeThresholdPercentage: ctx.executeThresholdPercentage
14685
14910
  });
14911
+ let headDiagnostics;
14686
14912
  const head = applyHeadCap({
14913
+ observeDiagnostics: (value) => {
14914
+ headDiagnostics = value;
14915
+ },
14687
14916
  index,
14688
14917
  protectedTailStart,
14689
14918
  offset,
@@ -14720,7 +14949,19 @@ function resolveProtectedTailBoundary(ctx) {
14720
14949
  rawRangeFingerprint,
14721
14950
  trueRawEligibleTokens: index.rangeTokens(offset, protectedTailStart),
14722
14951
  oversizeAtomicUnit: head.oversizeAtomicUnit,
14723
- boundaryReason
14952
+ boundaryReason,
14953
+ diagnostics: boundaryDiagnostics({
14954
+ tailTarget: target,
14955
+ usable: target.usable,
14956
+ usagePercentage,
14957
+ N: scaledN,
14958
+ triggerBudget: ctx.triggerBudget,
14959
+ capTokens: perRunCap,
14960
+ sizeWalkStart: recentOpenArcCutoff,
14961
+ protectedTailStart,
14962
+ livePromptFloor,
14963
+ head: headDiagnostics
14964
+ })
14724
14965
  };
14725
14966
  }
14726
14967
  function resolveBoundaryContext(args) {
@@ -14836,7 +15077,11 @@ function resolveWrapupProtectedTailBoundary(args) {
14836
15077
  contextLimit: ctx.contextLimit,
14837
15078
  executeThresholdPercentage: ctx.executeThresholdPercentage
14838
15079
  });
15080
+ let headDiagnostics;
14839
15081
  const head = applyHeadCap({
15082
+ observeDiagnostics: (value) => {
15083
+ headDiagnostics = value;
15084
+ },
14840
15085
  index,
14841
15086
  protectedTailStart: targetProtectedTailStart,
14842
15087
  offset,
@@ -14874,7 +15119,23 @@ function resolveWrapupProtectedTailBoundary(args) {
14874
15119
  rawRangeFingerprint,
14875
15120
  trueRawEligibleTokens: index.rangeTokens(offset, targetProtectedTailStart),
14876
15121
  oversizeAtomicUnit: head.oversizeAtomicUnit,
14877
- boundaryReason
15122
+ boundaryReason,
15123
+ diagnostics: boundaryDiagnostics({
15124
+ tailTarget: target,
15125
+ usable: target.usable,
15126
+ usagePercentage,
15127
+ N: target.N,
15128
+ triggerBudget: ctx.triggerBudget,
15129
+ capTokens: perRunCap,
15130
+ sizeWalkStart: Math.max(offset, anchorRawMessageCount - keep + 1),
15131
+ protectedTailStart: targetProtectedTailStart,
15132
+ livePromptFloor: {
15133
+ ordinal: null,
15134
+ from: targetProtectedTailStart,
15135
+ to: targetProtectedTailStart
15136
+ },
15137
+ head: headDiagnostics
15138
+ })
14878
15139
  };
14879
15140
  return {
14880
15141
  snapshot,
@@ -14974,6 +15235,27 @@ function recordHighPressureNoEligibleHead(db, snapshot) {
14974
15235
  function modelAcceptsEmptyContent(providerID) {
14975
15236
  return providerID === "anthropic";
14976
15237
  }
15238
+ var VARIANT_CACHE_PRESERVING_MODELS = {
15239
+ "anthropic/claude-fable-5-1": "2026-09-02",
15240
+ "openai/gpt-6-astra": "2026-09-05"
15241
+ };
15242
+ function canonicalVariantModelIdentity(providerID, modelID) {
15243
+ const normalizedProviderID = providerID.toLowerCase();
15244
+ const isAnthropicFamily = normalizedProviderID === "anthropic" || normalizedProviderID === "google-vertex-anthropic" || normalizedProviderID.includes("bedrock");
15245
+ if (isAnthropicFamily && isFable51ThinkingBindingModel("anthropic", modelID)) {
15246
+ return "anthropic/claude-fable-5-1";
15247
+ }
15248
+ return canonicalModelIdentity(`${providerID}/${modelID}`).toLowerCase();
15249
+ }
15250
+ function variantChangeBustsProviderCache(providerID, modelID) {
15251
+ if (!providerID || !modelID)
15252
+ return false;
15253
+ const identity = canonicalVariantModelIdentity(providerID, modelID);
15254
+ if (Object.hasOwn(VARIANT_CACHE_PRESERVING_MODELS, identity))
15255
+ return false;
15256
+ const normalizedProviderID = providerID.toLowerCase();
15257
+ return normalizedProviderID === "anthropic" || normalizedProviderID === "google-vertex-anthropic" || normalizedProviderID.includes("bedrock");
15258
+ }
14977
15259
 
14978
15260
  // ../plugin/src/hooks/magic-context/compartment-trigger.ts
14979
15261
  var PROACTIVE_TRIGGER_OFFSET_PERCENTAGE = 2;
@@ -15451,6 +15733,7 @@ function tagTranscript(sessionId, transcript, tagger, db, options = {}) {
15451
15733
  let textOrdinal = 0;
15452
15734
  let toolResultOrdinal = 0;
15453
15735
  const parts = message.parts;
15736
+ const messageHasNativeReasoning = message.info.role === "assistant" && parts.some((part) => part.kind === "thinking");
15454
15737
  const contentDerivedTextIds = messageId !== undefined && options.textIdentityDriftMessageIds?.has(messageId) === true ? buildContentDerivedTextIds(messageId, parts) : undefined;
15455
15738
  for (let partIndex = 0;partIndex < parts.length; partIndex += 1) {
15456
15739
  const part = parts[partIndex];
@@ -15525,6 +15808,7 @@ function tagTranscript(sessionId, transcript, tagger, db, options = {}) {
15525
15808
  const existing = toolAggregates.get(aggregateKey);
15526
15809
  if (existing) {
15527
15810
  existing.occurrences.push({ message, part, kind: part.kind });
15811
+ existing.requiresToolArcSkeleton ||= messageHasNativeReasoning;
15528
15812
  const canReuseIdentity = reuseIdentity && existing.identityReusable;
15529
15813
  let text = "";
15530
15814
  if (canReuseIdentity) {
@@ -15606,7 +15890,8 @@ function tagTranscript(sessionId, transcript, tagger, db, options = {}) {
15606
15890
  maxTokenCount: reusableAccounting.tokenCount ?? 0,
15607
15891
  toolName: null,
15608
15892
  inputByteSize: reusableAccounting.inputByteSize,
15609
- inputTokenCount: reusableAccounting.inputTokenCount
15893
+ inputTokenCount: reusableAccounting.inputTokenCount,
15894
+ requiresToolArcSkeleton: messageHasNativeReasoning
15610
15895
  };
15611
15896
  if (part.kind === "tool_result") {
15612
15897
  text = part.getText() ?? "";
@@ -15646,7 +15931,8 @@ function tagTranscript(sessionId, transcript, tagger, db, options = {}) {
15646
15931
  maxTokenCount: persistedAccounting?.tokenCount ?? outputTokenCount,
15647
15932
  toolName: accounting.toolName,
15648
15933
  inputByteSize: persistedAccounting?.inputByteSize ?? (part.kind === "tool_use" ? accounting.inputByteSize : 0),
15649
- inputTokenCount: persistedAccounting?.inputTokenCount ?? (part.kind === "tool_use" ? firstInputTokenCount : null)
15934
+ inputTokenCount: persistedAccounting?.inputTokenCount ?? (part.kind === "tool_use" ? firstInputTokenCount : null),
15935
+ requiresToolArcSkeleton: messageHasNativeReasoning
15650
15936
  };
15651
15937
  if (part.kind === "tool_result") {
15652
15938
  applyGrownToolResultAccounting({
@@ -15748,7 +16034,7 @@ function applyToolPrefixAndTarget(args) {
15748
16034
  args.timing.prefix += performance.now() - prefixStart;
15749
16035
  }
15750
16036
  const targetStart = args.timing ? performance.now() : 0;
15751
- args.targets.set(args.tagId, buildAggregateTarget(args.tagId, args.aggregate.occurrences));
16037
+ args.targets.set(args.tagId, buildAggregateTarget(args.tagId, args.aggregate.occurrences, args.aggregate.requiresToolArcSkeleton));
15752
16038
  if (args.timing)
15753
16039
  args.timing.targets += performance.now() - targetStart;
15754
16040
  }
@@ -15936,7 +16222,7 @@ function setToolContentOrText(part, content) {
15936
16222
  } catch {}
15937
16223
  return part.setText(content);
15938
16224
  }
15939
- function buildAggregateTarget(tagId, occurrences) {
16225
+ function buildAggregateTarget(tagId, occurrences, requiresToolArcSkeleton) {
15940
16226
  const role = occurrences[0]?.message.info.role ?? "user";
15941
16227
  const messageId = occurrences[0]?.message.info.id;
15942
16228
  return {
@@ -15997,6 +16283,7 @@ function buildAggregateTarget(tagId, occurrences) {
15997
16283
  canDrop() {
15998
16284
  return occurrences.length > 0;
15999
16285
  },
16286
+ requiresToolArcSkeleton,
16000
16287
  readInput() {
16001
16288
  for (const occ of occurrences) {
16002
16289
  const input = occ.part.getToolInput?.();
@@ -17635,6 +17922,20 @@ function getReducedRecompTokenBudget(currentBudget) {
17635
17922
  return reducedBudget < currentBudget ? reducedBudget : null;
17636
17923
  }
17637
17924
 
17925
+ // ../plugin/src/hooks/magic-context/producer-window-guard.ts
17926
+ var PRODUCER_WINDOW_REFUSAL_MARGIN = 0.15;
17927
+ function producerWindowFailureReason(input) {
17928
+ const { producerSourceTokens, contextLimitTokens, maxOutputTokens } = input;
17929
+ if (typeof contextLimitTokens !== "number" || !Number.isFinite(contextLimitTokens) || contextLimitTokens <= 0 || !Number.isFinite(producerSourceTokens) || producerSourceTokens <= 0 || !Number.isFinite(maxOutputTokens) || maxOutputTokens < 0) {
17930
+ return null;
17931
+ }
17932
+ const usableInputTokens = Math.max(0, Math.floor(contextLimitTokens - maxOutputTokens));
17933
+ const refusalThreshold = usableInputTokens * (1 + PRODUCER_WINDOW_REFUSAL_MARGIN);
17934
+ if (producerSourceTokens < refusalThreshold)
17935
+ return null;
17936
+ return `producer_source_exceeds_window producer_source_tokens=${Math.round(producerSourceTokens)} usable_input_tokens=${usableInputTokens} context_limit_tokens=${Math.round(contextLimitTokens)} max_output_tokens=${Math.round(maxOutputTokens)} refusal_margin=${PRODUCER_WINDOW_REFUSAL_MARGIN}`;
17937
+ }
17938
+
17638
17939
  // ../plugin/src/hooks/magic-context/reference-seeds.generated.ts
17639
17940
  var REFERENCE_SEEDS = [
17640
17941
  {
@@ -19601,6 +19902,7 @@ async function runPiHistorian(deps) {
19601
19902
  fallbackModels,
19602
19903
  fallbackModelId,
19603
19904
  historianChunkTokens,
19905
+ historianContextLimit,
19604
19906
  boundarySnapshot: providedBoundarySnapshot,
19605
19907
  refreshBoundarySnapshot,
19606
19908
  currentContextLimit,
@@ -19696,7 +19998,7 @@ async function runPiHistorian(deps) {
19696
19998
  const protectedTailStart = boundarySnapshot.protectedTailStart;
19697
19999
  const eligibleEndOrdinal = Math.min(boundarySnapshot.eligibleEndOrdinal, protectedTailStart);
19698
20000
  if (protectedTailStart <= offset || eligibleEndOrdinal <= offset) {
19699
- sessionLog(sessionId, `historian no-op: protectedTailStart=${protectedTailStart} eligibleEnd=${eligibleEndOrdinal} <= offset=${offset} — nothing to compact`);
20001
+ sessionLog(sessionId, `historian no-op: protectedTailStart=${protectedTailStart} eligibleEnd=${eligibleEndOrdinal} <= offset=${offset} — nothing to compact; ${describeBoundaryDiagnostics(boundarySnapshot)}`);
19700
20002
  if (boundarySnapshot.usagePercentage < 80) {
19701
20003
  if (!isWrapupInProgress(db, sessionId))
19702
20004
  clearEmergencyRecovery(db, sessionId);
@@ -19767,7 +20069,23 @@ async function runPiHistorian(deps) {
19767
20069
  chunkStart: chunk.startIndex,
19768
20070
  sessionCompartments: priorCompartments
19769
20071
  });
19770
- const chunkText = truncateHistorianInputIfNeeded(chunk.text, historianChunkTokens);
20072
+ const chunkText = chunk.oversizeAtomicUnit ? chunk.text : truncateHistorianInputIfNeeded(chunk.text, historianChunkTokens);
20073
+ const producerSourceTokens = estimateTokens(chunkText);
20074
+ if (boundarySnapshot.oversizeAtomicUnit || chunk.oversizeAtomicUnit) {
20075
+ sessionLog(sessionId, `historian oversize admission: range=${chunk.startIndex}-${chunk.endIndex} rawComponentTokens=${boundarySnapshot.diagnostics?.head.completedFence.tokenMass ?? "unknown"} perRunCap=${perRunCap} producerSourceTokens=${producerSourceTokens} historianChunkTokens=${historianChunkTokens}; ${describeBoundaryDiagnostics(boundarySnapshot)}`);
20076
+ }
20077
+ const producerWindowFailure = producerWindowFailureReason({
20078
+ producerSourceTokens,
20079
+ contextLimitTokens: historianContextLimit,
20080
+ maxOutputTokens
20081
+ });
20082
+ if (producerWindowFailure) {
20083
+ telemetry.failureReason = producerWindowFailure;
20084
+ retainDrainReservationForRetryThrottle = true;
20085
+ incrementHistorianFailure(db, sessionId, producerWindowFailure);
20086
+ sessionLog(sessionId, `historian oversize admission refused before spawn: ${producerWindowFailure}`);
20087
+ return;
20088
+ }
19771
20089
  if (chunkText !== chunk.text) {
19772
20090
  sessionLog(sessionId, `historian pre-flight: truncated formatted input for ${chunk.startIndex}-${chunk.endIndex} to fit ${historianChunkTokens} tokens`);
19773
20091
  }
@@ -20530,6 +20848,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
20530
20848
  protectedTags: config.protectedTags,
20531
20849
  currentTotalInputTokens: emergency.currentTotalInputTokens,
20532
20850
  ceilingTokens: emergency.ceilingTokens,
20851
+ usagePercentage: emergency.usagePercentage,
20533
20852
  priorInputSample,
20534
20853
  hasPriorDrop: priorInputSample > 0
20535
20854
  });
@@ -20543,11 +20862,13 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
20543
20862
  if (tag.status !== "active" || tag.type !== "tool")
20544
20863
  continue;
20545
20864
  const target = targets.get(tag.tagNumber);
20546
- const recent = newestEmergencyTags.has(tag.tagNumber);
20547
- const result = recent ? target?.truncate?.() ?? target?.drop?.() ?? "absent" : target?.drop?.() ?? "absent";
20865
+ const recent = (emergency.usagePercentage ?? 0) < 95 && newestEmergencyTags.has(tag.tagNumber);
20866
+ const reasoningSafeSkeleton = target?.requiresToolArcSkeleton === true;
20867
+ const skeleton = recent || reasoningSafeSkeleton;
20868
+ const result = reasoningSafeSkeleton ? target?.truncate?.() ?? "absent" : recent ? target?.truncate?.() ?? target?.drop?.() ?? "absent" : target?.drop?.() ?? "absent";
20548
20869
  if (result === "removed" || result === "truncated") {
20549
20870
  updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
20550
- updateTagDropMode(db, sessionId, tag.tagNumber, recent ? "truncated" : "full");
20871
+ updateTagDropMode(db, sessionId, tag.tagNumber, skeleton ? "truncated" : "full");
20551
20872
  droppedTools++;
20552
20873
  emergencyDroppedTools++;
20553
20874
  }
@@ -20557,7 +20878,9 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
20557
20878
  } else {
20558
20879
  sessionLog(sessionId, `emergency tiered drop skipped: ${plan.reason}`);
20559
20880
  }
20560
- setEmergencyDropSample(db, sessionId, emergency.currentTotalInputTokens);
20881
+ if (emergencyDroppedTools > 0) {
20882
+ setEmergencyDropSample(db, sessionId, emergency.currentTotalInputTokens);
20883
+ }
20561
20884
  }
20562
20885
  const staleReduce = routine && config.staleReduceStripEnabled ? collectStaleReduceCallIds(piMessages, buildMessageIdToMaxTagFromTargets(targets), buildCtxReduceTagNumbers(tags), toolAgeCutoff, resolveStableId) : { composite: new Set, bareCallIds: new Set };
20563
20886
  if (routine && config.staleReduceStripEnabled && (staleReduce.composite.size > 0 || staleReduce.bareCallIds.size > 0)) {
@@ -25535,6 +25858,7 @@ var afterFallbackAdoptionForTests;
25535
25858
  var pendingDecisionLogObserverForTests;
25536
25859
  var mutationGateObserverForTests;
25537
25860
  var lkgRecoveryLogObserverForTests;
25861
+ var variantChangeLogObserverForTests;
25538
25862
  function logPiLkgRecovery(sessionId, message) {
25539
25863
  lkgRecoveryLogObserverForTests?.(message);
25540
25864
  sessionLog(sessionId, message);
@@ -25644,6 +25968,9 @@ function logTransformTiming(sessionId, stage, start, extra) {
25644
25968
  recordPiTransformTiming({ sessionId, stage, elapsedMs, extra });
25645
25969
  sessionLog(sessionId, `transform stage: stage=${stage} elapsed=${elapsed}ms${suffix}`);
25646
25970
  }
25971
+ function piVariantChangeBustsProviderCache(providerID, modelID) {
25972
+ return variantChangeBustsProviderCache(providerID, modelID);
25973
+ }
25647
25974
  function resolvePiContextModelKey(ctx) {
25648
25975
  const model = ctx.model;
25649
25976
  if (!model)
@@ -26287,6 +26614,26 @@ function registerPiContextHandler(pi, baseOptions) {
26287
26614
  }
26288
26615
  return s;
26289
26616
  };
26617
+ pi.on("thinking_level_select", async (event, ctx) => {
26618
+ if (event.previousLevel === event.level)
26619
+ return;
26620
+ const sessionId = resolveSessionId2(ctx);
26621
+ if (!sessionId)
26622
+ return;
26623
+ const providerID = ctx.model?.provider;
26624
+ const modelID = ctx.model?.id;
26625
+ if (piVariantChangeBustsProviderCache(providerID, modelID)) {
26626
+ sessionLog(sessionId, `variant changed (${event.previousLevel} -> ${event.level}), triggering flush`);
26627
+ historyRefreshSessions.add(sessionId);
26628
+ systemPromptRefreshSessions.add(sessionId);
26629
+ pendingMaterializationSessions.add(sessionId);
26630
+ lastHeuristicsTurnIdBySession.delete(sessionId);
26631
+ return;
26632
+ }
26633
+ const message = `variant changed (${event.previousLevel} -> ${event.level}) on ${providerID ?? "unknown"}/${modelID ?? "unknown"} without a proven natural cache bust; deferring flush to next natural bust`;
26634
+ variantChangeLogObserverForTests?.(message);
26635
+ sessionLog(sessionId, message);
26636
+ });
26290
26637
  pi.on("context", async (event, ctx) => {
26291
26638
  const transformStartTime = performance.now();
26292
26639
  let rawMessageCount = 0;
@@ -26593,7 +26940,7 @@ function registerPiContextHandler(pi, baseOptions) {
26593
26940
  if (now - lastNotifiedAt >= EMERGENCY_NOTIFICATION_COOLDOWN_MS) {
26594
26941
  lastEmergencyNotificationAtMs.set(sessionId, now);
26595
26942
  sendPiIgnoredNotification(ctx, "Context full — /ctx-flush or /clear to continue.");
26596
- sessionLog(sessionId, `EMERGENCY: usage=${usagePercentage.toFixed(1)}% — notified user, awaiting in-flight historian + applying drop-all-tools`);
26943
+ sessionLog(sessionId, `EMERGENCY: usage=${usagePercentage.toFixed(1)}% — notified user, awaiting in-flight historian + evaluating tiered emergency reclaim`);
26597
26944
  }
26598
26945
  const histPromise = inFlightHistorian.get(sessionId);
26599
26946
  if (histPromise) {
@@ -27077,6 +27424,7 @@ function spawnPiHistorianRun(args) {
27077
27424
  fallbackModels: historian.fallbackModels,
27078
27425
  fallbackModelId,
27079
27426
  historianChunkTokens: historian.historianChunkTokens,
27427
+ historianContextLimit: historian.historianContextLimit,
27080
27428
  boundarySnapshot,
27081
27429
  refreshBoundarySnapshot,
27082
27430
  currentContextLimit,
@@ -27710,7 +28058,8 @@ async function runPipeline(args) {
27710
28058
  staleReduceStripEnabled: args.canUseEmptySentinels,
27711
28059
  emergency: args.forceMaterialization === true && args.emergencyCeilingTokens !== undefined && args.emergencyCeilingTokens > 0 ? {
27712
28060
  currentTotalInputTokens: args.contextUsage.inputTokens,
27713
- ceilingTokens: args.emergencyCeilingTokens
28061
+ ceilingTokens: args.emergencyCeilingTokens,
28062
+ usagePercentage: args.contextUsage.percentage
27714
28063
  } : undefined,
27715
28064
  caveman: args.isSubagent ? undefined : args.heuristics.caveman
27716
28065
  }, activeTags, stableIdResolver);
@@ -30530,6 +30879,41 @@ ${migrationSummary}` : "",
30530
30879
  });
30531
30880
  }
30532
30881
 
30882
+ // ../plugin/src/shared/cache-ttl-display.ts
30883
+ function resolveCacheTtlDisplay(args) {
30884
+ if (args.sessionModelKey && (!args.modelKey || args.sessionModelKey === args.modelKey) || !args.modelKey && !args.sessionModelKey && args.sessionValue !== "5m") {
30885
+ return {
30886
+ value: args.sessionValue || "5m",
30887
+ source: "session",
30888
+ modelKey: args.modelKey ?? args.sessionModelKey ?? undefined
30889
+ };
30890
+ }
30891
+ if (typeof args.configured === "string") {
30892
+ return {
30893
+ value: args.configured,
30894
+ source: args.configuredExplicitly ? "config" : "default",
30895
+ modelKey: args.modelKey
30896
+ };
30897
+ }
30898
+ const matched = resolveModelConfigValue(args.configured, args.modelKey);
30899
+ if (matched) {
30900
+ return { value: matched.value, source: "config", modelKey: args.modelKey };
30901
+ }
30902
+ return {
30903
+ value: args.configured.default ?? "5m",
30904
+ source: "default",
30905
+ modelKey: args.modelKey
30906
+ };
30907
+ }
30908
+ function formatCacheTtlDisplay(display) {
30909
+ if (display.source === "session")
30910
+ return `Cache TTL: ${display.value} (session)`;
30911
+ if (display.source === "config") {
30912
+ return `Cache TTL: ${display.value} (config for ${display.modelKey ?? "current model"})`;
30913
+ }
30914
+ return `Cache TTL: ${display.value} (default — no cache_ttl for ${display.modelKey ?? "unknown model"})`;
30915
+ }
30916
+
30533
30917
  // ../plugin/src/shared/format-threshold.ts
30534
30918
  function formatThresholdPercent(value) {
30535
30919
  if (typeof value !== "number" || !Number.isFinite(value))
@@ -30617,7 +31001,7 @@ function formatExecuteThreshold(detail, contextLimit) {
30617
31001
  }
30618
31002
  return `${percentage}%${clampNote}`;
30619
31003
  }
30620
- function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit, dreamer, windowGeometry, tailHygiene, contextUsage, rustMode = false) {
31004
+ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit, dreamer, windowGeometry, tailHygiene, contextUsage, rustMode = false, display) {
30621
31005
  const thresholdDetail = resolveExecuteThresholdDetail(executeThresholdPercentageConfig, liveModelKey, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, {
30622
31006
  tokensConfig: executeThresholdTokens,
30623
31007
  contextLimit,
@@ -30631,11 +31015,18 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
30631
31015
  const activeTags = tags.filter((t) => t.status === "active");
30632
31016
  const droppedTags = tags.filter((t) => t.status === "dropped");
30633
31017
  const totalBytes = activeTags.reduce((sum, t) => sum + t.byteSize, 0);
31018
+ const ttlDisplay = resolveCacheTtlDisplay({
31019
+ configured: display?.cacheTtlConfig ?? "5m",
31020
+ configuredExplicitly: display?.cacheTtlConfigured === true,
31021
+ modelKey: liveModelKey,
31022
+ sessionValue: meta.cacheTtl,
31023
+ sessionModelKey: meta.lastObservedModelKey
31024
+ });
30634
31025
  let ttlMs;
30635
31026
  try {
30636
- ttlMs = parseCacheTtl(meta.cacheTtl);
31027
+ ttlMs = parseCacheTtl(ttlDisplay.value);
30637
31028
  } catch (error) {
30638
- sessionLog(sessionId, `invalid cache_ttl "${meta.cacheTtl}" in ctx-status; falling back to default 5m`, error);
31029
+ sessionLog(sessionId, `invalid cache_ttl "${ttlDisplay.value}" in ctx-status; falling back to default 5m`, error);
30639
31030
  ttlMs = parseCacheTtl("5m");
30640
31031
  }
30641
31032
  const elapsed = Date.now() - meta.lastResponseTime;
@@ -30645,7 +31036,10 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
30645
31036
  const displayInputTokens = contextUsage?.inputTokens ?? meta.lastInputTokens;
30646
31037
  const displayPercentage = contextUsage?.percentage ?? meta.lastContextPercentage;
30647
31038
  const displayContextLimit = contextLimit && contextLimit > 0 ? contextLimit : displayPercentage > 0 ? Math.round(displayInputTokens / (displayPercentage / 100)) : 0;
31039
+ const parseFailureLines = (display?.configParseFailures ?? []).map(formatConfigParseStatusLine);
30648
31040
  const lines = [
31041
+ ...parseFailureLines,
31042
+ ...parseFailureLines.length > 0 ? [""] : [],
30649
31043
  "## Magic Status",
30650
31044
  "",
30651
31045
  `**Session:** ${sessionId}`,
@@ -30662,7 +31056,7 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
30662
31056
  "",
30663
31057
  ...meta.lastTransformError ? ["### Last Transform Error", `- ${meta.lastTransformError}`, ""] : [],
30664
31058
  "### Cache TTL",
30665
- `- Configured: ${meta.cacheTtl}`,
31059
+ `- ${formatCacheTtlDisplay(ttlDisplay)}`,
30666
31060
  `- Last response: ${meta.lastResponseTime > 0 ? `${Math.round(elapsed / 1000)}s ago` : "never"}`,
30667
31061
  `- Remaining: ${cacheExpired ? "expired" : ttlMs === Number.POSITIVE_INFINITY ? "never (MC never assumes expiry — external cache-keep)" : `${Math.round(remainingMs / 1000)}s`}`,
30668
31062
  `- Queue will auto-execute: ${cacheExpired ? "yes (cache expired)" : ttlMs === Number.POSITIVE_INFINITY ? `when context >= ${executeThresholdPercentage}%` : `when TTL expires or context >= ${executeThresholdPercentage}%`}`,
@@ -30788,7 +31182,7 @@ ${c.content}
30788
31182
  // package.json
30789
31183
  var package_default = {
30790
31184
  name: "@cortexkit/pi-magic-context",
30791
- version: "0.41.3",
31185
+ version: "0.41.4",
30792
31186
  type: "module",
30793
31187
  description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
30794
31188
  main: "dist/index.js",
@@ -30961,6 +31355,11 @@ function renderInner(s, theme, innerWidth) {
30961
31355
  const lines = [];
30962
31356
  lines.push(`${theme.fg("accent", theme.bold("⚡ Magic Context Status"))} ${theme.fg("muted", `v${package_default.version}`)}`);
30963
31357
  lines.push("");
31358
+ for (const failure of s.configParseFailures) {
31359
+ lines.push(theme.fg("error", formatConfigParseStatusLine(failure)));
31360
+ }
31361
+ if (s.configParseFailures.length > 0)
31362
+ lines.push("");
30964
31363
  lines.push(`Context ${theme.fg(pctColor, theme.bold(`${s.usagePercentage.toFixed(1)}%`))} · ${fmt2(s.inputTokens)} / ${s.contextLimit > 0 ? fmt2(s.contextLimit) : "?"} tokens`);
30965
31364
  if (s.windowGeometry) {
30966
31365
  lines.push(formatWindowDerivationLine(s.inputTokens, s.windowGeometry).replace(/^Context:.* — window /, "Window "));
@@ -30989,7 +31388,7 @@ function renderInner(s, theme, innerWidth) {
30989
31388
  lines.push(`Upgrade: ${theme.fg("accent", "up to date")}`);
30990
31389
  }
30991
31390
  lines.push(`Pending drops: ${s.pendingOpsCount}`);
30992
- lines.push(`Cache TTL: ${s.cacheTtl} · last response ${s.lastResponseTime > 0 ? `${Math.round((Date.now() - s.lastResponseTime) / 1000)}s ago` : "never"} · ${s.cacheExpired ? theme.fg("warning", "expired") : s.cacheRemainingMs === Number.POSITIVE_INFINITY ? "never (MC never assumes expiry — external cache-keep)" : `${Math.round(s.cacheRemainingMs / 1000)}s remaining`}`);
31391
+ lines.push(`${formatCacheTtlDisplay({ value: s.cacheTtl, source: s.cacheTtlSource, modelKey: s.cacheTtlModelKey })} · last response ${s.lastResponseTime > 0 ? `${Math.round((Date.now() - s.lastResponseTime) / 1000)}s ago` : "never"} · ${s.cacheExpired ? theme.fg("warning", "expired") : s.cacheRemainingMs === Number.POSITIVE_INFINITY ? "never (MC never assumes expiry — external cache-keep)" : `${Math.round(s.cacheRemainingMs / 1000)}s remaining`}`);
30993
31392
  lines.push("");
30994
31393
  lines.push(theme.fg("muted", "Tags"));
30995
31394
  lines.push(`Active ${s.activeTags} (~${formatBytes(s.activeBytes)}) · Dropped ${s.droppedTags} · Total ${s.totalTags}`);
@@ -31099,7 +31498,14 @@ ${safeStringify(tool.parameters)}`);
31099
31498
  contextLimit: contextLimit || undefined,
31100
31499
  sessionId
31101
31500
  });
31102
- const cacheTtl = meta.cacheTtl || "5m";
31501
+ const cacheTtlDisplay = resolveCacheTtlDisplay({
31502
+ configured: deps.cacheTtlConfig ?? "5m",
31503
+ configuredExplicitly: deps.cacheTtlConfigured === true,
31504
+ modelKey,
31505
+ sessionValue: meta.cacheTtl,
31506
+ sessionModelKey: meta.lastObservedModelKey
31507
+ });
31508
+ const cacheTtl = cacheTtlDisplay.value;
31103
31509
  let cacheTtlMs;
31104
31510
  try {
31105
31511
  cacheTtlMs = parseCacheTtl(cacheTtl);
@@ -31138,6 +31544,9 @@ ${safeStringify(tool.parameters)}`);
31138
31544
  historianLastFailureAt: typeof metaRow?.historian_last_failure_at === "number" ? metaRow.historian_last_failure_at : null,
31139
31545
  historianLastError: metaRow?.historian_last_error ?? null,
31140
31546
  cacheTtl,
31547
+ cacheTtlSource: cacheTtlDisplay.source,
31548
+ cacheTtlModelKey: cacheTtlDisplay.modelKey,
31549
+ configParseFailures: deps.configParseFailures ?? [],
31141
31550
  lastResponseTime: meta.lastResponseTime,
31142
31551
  cacheRemainingMs,
31143
31552
  cacheExpired,
@@ -31353,7 +31762,11 @@ No active Pi session is available.`,
31353
31762
  });
31354
31763
  const statusText = executeStatus(currentDeps.db, sessionId, currentDeps.protectedTags ?? 20, currentDeps.executeThresholdPercentage, modelKey, currentDeps.historyBudgetPercentage, currentDeps.commitClusterTrigger, currentDeps.executeThresholdTokens, usableContextLimit, {
31355
31764
  backlog: getDreamTaskBacklogs(currentDeps.db, currentDeps.projectIdentity, CANONICAL_DREAM_TASKS)
31356
- }, windowGeometry, resolveTailHygieneStatus(getPiChannel1Baseline(sessionId)), pressure);
31765
+ }, windowGeometry, resolveTailHygieneStatus(getPiChannel1Baseline(sessionId)), pressure, false, {
31766
+ cacheTtlConfig: currentDeps.cacheTtlConfig ?? "5m",
31767
+ cacheTtlConfigured: currentDeps.cacheTtlConfigured === true,
31768
+ configParseFailures: currentDeps.configParseFailures ?? []
31769
+ });
31357
31770
  const details = buildStatusDetails(currentDeps, sessionId);
31358
31771
  const profileStatus = currentDeps.activeProfile ?? "none";
31359
31772
  const storage = getMagicContextStorageResolution();
@@ -31642,6 +32055,7 @@ Eligible history is about ${initialPlan.snapshot.trueRawEligibleTokens.toLocaleS
31642
32055
  if (lastEnd + 1 >= targetEligibleEndOrdinal)
31643
32056
  break;
31644
32057
  if (!hasRunnableCompartmentWindow(plan.snapshot)) {
32058
+ sessionLog(sessionId, `wrapup no-op: ${describeBoundaryDiagnostics(plan.snapshot)}`);
31645
32059
  failure = `No runnable wrapup boundary is available yet; wrapped up through message ${lastEnd}. Run /ctx-wrapup again to continue.`;
31646
32060
  break;
31647
32061
  }
@@ -31659,7 +32073,7 @@ Eligible history is about ${initialPlan.snapshot.trueRawEligibleTokens.toLocaleS
31659
32073
  title: "/ctx-wrapup",
31660
32074
  text: `## Magic Wrapup
31661
32075
 
31662
- Chunk ${chunkIndex}: wrapping messages ${plan.snapshot.offset}-${plan.snapshot.eligibleEndOrdinal - 1} (~${plan.snapshot.trueRawEligibleTokens.toLocaleString()} eligible tokens remain).`,
32076
+ Chunk ${chunkIndex}: wrapping messages ${plan.snapshot.offset}-${plan.snapshot.eligibleEndOrdinal - 1} (~${plan.snapshot.trueRawEligibleTokens.toLocaleString()} eligible tokens remain). ${describeBoundaryDiagnostics(plan.snapshot)}`,
31663
32077
  level: "info"
31664
32078
  });
31665
32079
  const leaseResult = await acquireCompartmentLeaseEventually(deps.db, sessionId, renewWrapupMarker, resolveWrapupLeaseWaitTimeout(deps));
@@ -31978,7 +32392,8 @@ function stripTagPrefixFromAssistantMessage(message) {
31978
32392
  }
31979
32393
 
31980
32394
  // src/index.ts
31981
- var PI_HARNESS_KIND = resolvePiHarnessKind();
32395
+ var PI_HARNESS_DETECTION = await resolvePiHarnessDetection();
32396
+ var PI_HARNESS_KIND = PI_HARNESS_DETECTION.kind;
31982
32397
  var PREFIX2 = `[magic-context][${PI_HARNESS_KIND}]`;
31983
32398
  var PI_BOOT_DEADLINE_MS = 15000;
31984
32399
  var PI_CHILD_INIT_CONTEXT = Symbol.for("magic-context.pi.child-init-context");
@@ -32057,9 +32472,10 @@ function persistPiMessageEndModelMeta(args) {
32057
32472
  return;
32058
32473
  const cacheTtl = resolveCacheTtl(args.cacheTtlConfig, modelKey);
32059
32474
  const currentMeta = getOrCreateSessionMeta(args.db, args.sessionId);
32060
- if (currentMeta.cacheTtl !== cacheTtl) {
32061
- updateSessionMeta(args.db, args.sessionId, { cacheTtl });
32062
- }
32475
+ updateSessionMeta(args.db, args.sessionId, {
32476
+ ...currentMeta.cacheTtl === cacheTtl ? {} : { cacheTtl },
32477
+ lastObservedModelKey: modelKey
32478
+ });
32063
32479
  }
32064
32480
  function getCompatiblePiTodoCapture(todos) {
32065
32481
  if (!Array.isArray(todos))
@@ -32266,6 +32682,7 @@ function resolveHistorianFromConfig(config, harness = PI_HARNESS_KIND) {
32266
32682
  model,
32267
32683
  fallbackModels,
32268
32684
  historianChunkTokens,
32685
+ historianContextLimit: resolveKnownHistorianContextLimit(model),
32269
32686
  timeoutMs: config.historian_timeout_ms,
32270
32687
  temperature: historian?.temperature,
32271
32688
  maxOutputTokens: historian?.maxTokens ?? 32000,
@@ -32432,7 +32849,14 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
32432
32849
  registerCtxStatusLifecycleSignal(pi, commandLifecycleController.signal);
32433
32850
  let sessionShuttingDown = false;
32434
32851
  ensureConfigLocationsMigrated(projectDir);
32435
- const { config, warnings, loadedFromPaths, registrationPromptSurface } = loadPiConfig({
32852
+ const {
32853
+ config,
32854
+ warnings,
32855
+ loadedFromPaths,
32856
+ registrationPromptSurface,
32857
+ configParseFailures,
32858
+ cacheTtlConfigured
32859
+ } = loadPiConfig({
32436
32860
  cwd: projectDir
32437
32861
  });
32438
32862
  const promptSurfaceRuntime = createPromptSurfaceRuntime({
@@ -32444,7 +32868,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
32444
32868
  const projectIdentity = resolveProjectIdentityForSession(projectDir, config.allow_home_project) ?? "";
32445
32869
  if (projectIdentity)
32446
32870
  seenDreamerProjectIdentities.add(projectIdentity);
32447
- info(`loaded v${PLUGIN_VERSION} | harness=${PI_HARNESS_KIND} | db=${dbPath} | ` + `project=${projectIdentity} | dir=${projectDir}`);
32871
+ info(`loaded v${PLUGIN_VERSION} | harness=${PI_HARNESS_KIND} (via ${PI_HARNESS_DETECTION.via}) | db=${dbPath} | ` + `project=${projectIdentity} | dir=${projectDir}`);
32448
32872
  const compactionOff = !isCompactionEnabled(config);
32449
32873
  setCtxReduceRegisteredGlobally(!compactionOff);
32450
32874
  if (!compactionOff) {
@@ -32524,7 +32948,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
32524
32948
  });
32525
32949
  }
32526
32950
  });
32527
- function buildProjectDeps(dir, identity, cfg) {
32951
+ function buildProjectDeps(dir, identity, cfg, loadMetadata) {
32528
32952
  const hist = resolveHistorianFromConfig(cfg);
32529
32953
  if (hist) {
32530
32954
  hist.onStatusChange = (ctx) => {
@@ -32544,7 +32968,9 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
32544
32968
  contextOptions: buildContextOptions(cfg, hist, auto),
32545
32969
  sidekickConfig: resolveSidekickFromConfig(cfg),
32546
32970
  dreamerConfig: resolveDreamerFromConfig(cfg),
32547
- dreamerEnabled: isDreamerRunnable(cfg)
32971
+ dreamerEnabled: isDreamerRunnable(cfg),
32972
+ configParseFailures: loadMetadata.configParseFailures,
32973
+ cacheTtlConfigured: loadMetadata.cacheTtlConfigured
32548
32974
  };
32549
32975
  }
32550
32976
  function resolveProjectDepsForDir(dir, identityOverride) {
@@ -32561,7 +32987,10 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
32561
32987
  });
32562
32988
  const switchedConfig = switchedLoad.config;
32563
32989
  const switchedIdentity = identityOverride ?? resolveProjectIdentityForSession(dir, switchedConfig.allow_home_project) ?? "";
32564
- const built = buildProjectDeps(dir, switchedIdentity, switchedConfig);
32990
+ const built = buildProjectDeps(dir, switchedIdentity, switchedConfig, {
32991
+ configParseFailures: switchedLoad.configParseFailures,
32992
+ cacheTtlConfigured: switchedLoad.cacheTtlConfigured
32993
+ });
32565
32994
  projectDepsByDir.set(dir, built);
32566
32995
  return built;
32567
32996
  }
@@ -32571,7 +33000,10 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
32571
33000
  function resolveContextOptionsForProject(dir) {
32572
33001
  return resolveProjectDepsForDir(dir).contextOptions;
32573
33002
  }
32574
- const bootProjectDeps = buildProjectDeps(projectDir, projectIdentity, config);
33003
+ const bootProjectDeps = buildProjectDeps(projectDir, projectIdentity, config, {
33004
+ configParseFailures,
33005
+ cacheTtlConfigured
33006
+ });
32575
33007
  projectDepsByDir.set(projectDir, bootProjectDeps);
32576
33008
  function syncDreamerProjectRegistration(current) {
32577
33009
  if (sessionShuttingDown)
@@ -32622,6 +33054,20 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
32622
33054
  projectDepsByDir.delete(ctx.cwd);
32623
33055
  const current = resolveCurrentProjectDeps(ctx);
32624
33056
  syncCtxMemoryToolEnabled(pi, current.config.memory.enabled);
33057
+ const failuresToShow = claimConfigParseFailuresOnce("pi", current.configParseFailures);
33058
+ if (ctx.hasUI && failuresToShow.length > 0) {
33059
+ ctx.ui.notify(formatConfigParseNotice(failuresToShow), "error");
33060
+ }
33061
+ const sessionId = resolveSessionId(ctx);
33062
+ const model = ctx.model;
33063
+ if (sessionId && model?.provider && model.id) {
33064
+ seedSessionCacheTtlIfUnsynced({
33065
+ db,
33066
+ sessionId,
33067
+ configured: current.config.cache_ttl,
33068
+ modelKey: canonicalPiModelKey(model.provider, model.id)
33069
+ });
33070
+ }
32625
33071
  await handlePiCloneSessionStart(event, ctx, {
32626
33072
  db,
32627
33073
  signalPendingMarker: signalPiDeferredCompactionMarkerDrain
@@ -32660,6 +33106,9 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
32660
33106
  scheduleSummary: summarizeDreamSchedule(bootProjectDeps.config.dreamer)
32661
33107
  },
32662
33108
  activeProfile: bootProjectDeps.config.profile,
33109
+ cacheTtlConfig: bootProjectDeps.config.cache_ttl,
33110
+ cacheTtlConfigured: bootProjectDeps.cacheTtlConfigured,
33111
+ configParseFailures: bootProjectDeps.configParseFailures,
32663
33112
  resolveStatusDeps: (ctx) => {
32664
33113
  const current = resolveCurrentProjectDeps(ctx);
32665
33114
  return {
@@ -32675,7 +33124,10 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
32675
33124
  runnable: current.dreamerEnabled,
32676
33125
  scheduleSummary: summarizeDreamSchedule(current.config.dreamer)
32677
33126
  },
32678
- activeProfile: current.config.profile
33127
+ activeProfile: current.config.profile,
33128
+ cacheTtlConfig: current.config.cache_ttl,
33129
+ cacheTtlConfigured: current.cacheTtlConfigured,
33130
+ configParseFailures: current.configParseFailures
32679
33131
  };
32680
33132
  }
32681
33133
  });