@cortexkit/magic-context 0.46.1 → 0.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +2059 -488
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2973,7 +2973,7 @@ function sortJson(value) {
2973
2973
  if (value && typeof value === "object") {
2974
2974
  const sorted = {};
2975
2975
  for (const key of Object.keys(value).sort()) {
2976
- if (key === "protected_tags")
2976
+ if (key === "protected_tags" || key === "clear_reasoning_age")
2977
2977
  continue;
2978
2978
  sorted[key] = sortJson(value[key]);
2979
2979
  }
@@ -3108,7 +3108,9 @@ function migrateConfigFile(opts) {
3108
3108
  let migratedContent = first.content;
3109
3109
  let strippedProtectedTags = false;
3110
3110
  try {
3111
- const stripped = removeJsoncValue(first.content, ["protected_tags"]);
3111
+ const stripped = removeJsoncValue(removeJsoncValue(first.content, ["protected_tags"]), [
3112
+ "clear_reasoning_age"
3113
+ ]);
3112
3114
  if (stripped !== first.content) {
3113
3115
  migratedContent = stripped;
3114
3116
  strippedProtectedTags = true;
@@ -3116,7 +3118,10 @@ function migrateConfigFile(opts) {
3116
3118
  } catch {}
3117
3119
  atomicWriteConfigFile(opts.targetPath, migratedContent);
3118
3120
  if (strippedProtectedTags) {
3119
- info?.(`Stripped deprecated "protected_tags" key during config location migration`);
3121
+ if (first.content.includes('"protected_tags"'))
3122
+ info?.(`Stripped deprecated "protected_tags" key during config location migration`);
3123
+ if (first.content.includes('"clear_reasoning_age"'))
3124
+ info?.(`Stripped deprecated "clear_reasoning_age" key during config location migration`);
3120
3125
  }
3121
3126
  info?.(`Migrated Magic Context ${opts.scope} config from ${first.path} to ${opts.targetPath}`);
3122
3127
  warnings.push(...markLegacySourcesMovedAside(sources, opts.targetPath, opts.logger));
@@ -3471,6 +3476,15 @@ function migrateLegacyExperimental(rawConfig, warnings) {
3471
3476
  return patched;
3472
3477
  }
3473
3478
 
3479
+ // ../plugin/src/config/mural-model-deprecation.ts
3480
+ function warnDeprecatedMuralModel(config, warnings) {
3481
+ const mural = config.mural;
3482
+ if (typeof mural === "object" && mural !== null && Object.hasOwn(mural, "model")) {
3483
+ warnings.push(MURAL_MODEL_DEPRECATION_WARNING);
3484
+ }
3485
+ }
3486
+ var MURAL_MODEL_DEPRECATION_WARNING = "mural.model is deprecated and ignored; compress-cues now uses the dreamer model for each harness, like every other dreamer task. To override it, set dreamer.opencode.tasks.compress-cues.model (or the pi/omp equivalent). This key no longer does anything; remove it.";
3487
+
3474
3488
  // ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/core/core.js
3475
3489
  function $constructor(name, initializer, params) {
3476
3490
  function init(inst, def) {
@@ -8694,7 +8708,7 @@ class Parser {
8694
8708
  } else
8695
8709
  break;
8696
8710
  }
8697
- const expression = { steps, cap: 300 };
8711
+ const expression = { steps };
8698
8712
  if (this.take(".each(")) {
8699
8713
  if (relative)
8700
8714
  return this.error();
@@ -8760,9 +8774,23 @@ function compiled(source) {
8760
8774
  function oneLine(value) {
8761
8775
  return value.replace(/[\r\n\u2028\u2029]+/g, " ");
8762
8776
  }
8763
- function truncate(value, cap) {
8777
+ function truncate(value, cap, legacyPreview = false) {
8764
8778
  const chars = Array.from(oneLine(value));
8765
- return chars.length > cap ? `${chars.slice(0, cap).join("")}…` : chars.join("");
8779
+ if (cap === undefined || chars.length <= cap)
8780
+ return chars.join("");
8781
+ if (legacyPreview)
8782
+ return `${chars.slice(0, cap).join("")}…`;
8783
+ let end = 0;
8784
+ for (let i = 0;i < Math.min(cap, chars.length); i++) {
8785
+ if (!".!?。!?".includes(chars[i]))
8786
+ continue;
8787
+ let next = i + 1;
8788
+ while (next < chars.length && `"'”’)]`.includes(chars[next]))
8789
+ next++;
8790
+ if (next <= cap && ("。!?".includes(chars[i]) || next === chars.length || /[ \t]/.test(chars[next])))
8791
+ end = next;
8792
+ }
8793
+ return `${end ? `${chars.slice(0, end).join("")} ` : ""}[… ${chars.length - end} more characters]`;
8766
8794
  }
8767
8795
  function scalar(value) {
8768
8796
  if (value === undefined)
@@ -8779,10 +8807,11 @@ function compactJson(value) {
8779
8807
  function field(value, key) {
8780
8808
  return value !== null && typeof value === "object" && !Array.isArray(value) && Object.hasOwn(value, key) ? value[key] : undefined;
8781
8809
  }
8782
- function render(nodes, root) {
8810
+ function render(nodes, root, legacyPreview) {
8783
8811
  return nodes.map((node) => {
8784
8812
  if (typeof node === "string")
8785
8813
  return node;
8814
+ const cap = node.cap ?? (legacyPreview ? 300 : undefined);
8786
8815
  let value = root;
8787
8816
  let list = false;
8788
8817
  for (const step of node.steps) {
@@ -8796,19 +8825,19 @@ function render(nodes, root) {
8796
8825
  }
8797
8826
  }
8798
8827
  if (node.count)
8799
- return truncate(Array.isArray(value) ? String(value.length) : "", node.cap);
8828
+ return truncate(Array.isArray(value) ? String(value.length) : "", cap, legacyPreview);
8800
8829
  if (node.each || list || node.join !== undefined) {
8801
8830
  if (!Array.isArray(value))
8802
8831
  return "";
8803
- const elements = value.slice(0, 10).map((item) => truncate(node.each ? render(node.each, item) : scalar(item), 300));
8804
- if (value.length > 10)
8832
+ const elements = value.slice(0, legacyPreview ? 10 : undefined).map((item) => truncate(node.each ? render(node.each, item, legacyPreview) : scalar(item), legacyPreview ? 300 : undefined, legacyPreview));
8833
+ if (legacyPreview && value.length > 10)
8805
8834
  elements.push(`… +${value.length - 10} more`);
8806
- return truncate(elements.join(node.join ?? (node.each ? "; " : ", ")), node.cap);
8835
+ return truncate(elements.join(node.join ?? (node.each ? "; " : ", ")), cap, legacyPreview);
8807
8836
  }
8808
- return truncate(scalar(value), node.cap);
8837
+ return truncate(scalar(value), cap, legacyPreview);
8809
8838
  }).join("");
8810
8839
  }
8811
- function renderToolTemplate(source, input, output) {
8840
+ function renderToolTemplate(source, input, output, legacyPreview = false) {
8812
8841
  const nodes = compiled(source);
8813
8842
  if (!nodes)
8814
8843
  return null;
@@ -8823,8 +8852,8 @@ function renderToolTemplate(source, input, output) {
8823
8852
  }
8824
8853
  }
8825
8854
  const root = { input, output: structured };
8826
- const bareOutputNodes = nodes.map((node) => typeof node !== "string" && node.steps.length === 1 && ("field" in node.steps[0]) && node.steps[0].field === "output" && !node.each && !node.count && node.join === undefined ? truncate(text ?? "", node.cap) : node);
8827
- return truncate(render(bareOutputNodes, root), 1000);
8855
+ const bareOutputNodes = nodes.map((node) => typeof node !== "string" && node.steps.length === 1 && ("field" in node.steps[0]) && node.steps[0].field === "output" && !node.each && !node.count && node.join === undefined ? truncate(text ?? "", node.cap ?? (legacyPreview ? 300 : undefined), legacyPreview) : node);
8856
+ return truncate(render(bareOutputNodes, root, legacyPreview), legacyPreview ? 1000 : undefined, legacyPreview);
8828
8857
  }
8829
8858
  var templates;
8830
8859
  var init_historian_tool_template = __esm(() => {
@@ -9182,7 +9211,7 @@ var init_magic_context = __esm(() => {
9182
9211
  ctx.addIssue({ code: "custom", message: error });
9183
9212
  }),
9184
9213
  literal(false)
9185
- ])).optional().describe('Readable historian tool expansions, keyed by exact host tool name. Templates override built-in defaults; false disables an expansion. Supports ${input.path}, ${output.path}, bare ${output}, array [N], [*].field, .each("${field}"), .join("separator"), .count and final .truncate(N). Missing fields are empty; placeholders default to 300 characters, lists to 10 elements, expansions to 1000 characters. Valid in user and project config; affects historian/recomp and verbose ctx_expand only, never the wire or default ctx_expand transcript.'),
9214
+ ])).optional().describe('Readable historian tool expansions, keyed by exact host tool name. Templates override built-in defaults; false disables an expansion. Supports ${input.path}, ${output.path}, bare ${output}, array [N], [*].field, .each("${field}"), .join("separator"), .count and final .truncate(N). Missing fields are empty. Historian text and lists have no implicit caps; explicit .truncate(N) keeps complete sentences and marks omitted characters, or emits only the marker if no sentence fits. When the ctx_expand history-recovery tool is called in verbose mode, its tool-call previews retain their original built-in templates and limits (300 characters per placeholder, 10 list elements, 1000 characters per expansion). Valid in user and project config; never changes the wire or default ctx_expand transcript.'),
9186
9215
  opencode: OpenCodeHarnessBlockSchema.optional(),
9187
9216
  pi: PiHarnessBlockSchema.optional(),
9188
9217
  omp: OmpHarnessBlockSchema.optional(),
@@ -9295,7 +9324,7 @@ var init_magic_context = __esm(() => {
9295
9324
  allow_home_project: boolean2().default(false).describe("Allow Magic Context sessions launched from the exact canonical home directory. A non-git home uses its deterministic dir: identity; a home repository uses its git: identity. USER-LEVEL ONLY: project config is ignored. The home identity is excluded from registry seed exports, never resolves descendants by containment, and cannot join a workspace."),
9296
9325
  mural: object({
9297
9326
  enabled: boolean2().default(false),
9298
- model: string2().trim().min(1).optional().describe("Model for the compress-cues task that compresses each memory into a mural cue. The mural image itself is rendered deterministically (no author model).")
9327
+ model: unknown().optional().describe("Deprecated: ignored. compress-cues now uses the dreamer model for each harness, like every other dreamer task; override it with dreamer.opencode.tasks.compress-cues.model (or the pi/omp equivalent). Remove this key; it no longer does anything.").meta({ deprecated: true })
9299
9328
  }).default({ enabled: false }).describe("Experimental mural: a single deterministically-rendered image of project memories that did not fit the context budget. Cues are compressed per-memory by the compress-cues dreamer task."),
9300
9329
  transform_mode: _enum(["ts", "rust"]).default("ts").describe('Experimental: routes the entire Magic Context runtime for the project through the ck-mc Rust module over subc (requires user-level `subc` config); "ts" is the current TypeScript pipeline.'),
9301
9330
  auto_update: boolean2().optional().describe("Enable automatic npm self-update checks for the OpenCode plugin. Security: USER-only in config loader, so hostile project configs cannot suppress updates."),
@@ -9305,8 +9334,9 @@ var init_magic_context = __esm(() => {
9305
9334
  historian: HistorianConfigSchema.describe("Historian metadata plus independent strict OpenCode, Pi, and OMP execution blocks. Retained metadata stays at historian; model, fallback_models, variant, and thinking_level belong only in historian.opencode, historian.pi, or historian.omp."),
9306
9335
  dreamer: DreamerConfigSchema.optional().describe("Dreamer metadata and scheduling plus independent strict OpenCode, Pi, and OMP execution blocks. schedule and promotion_threshold stay at dreamer.tasks; model, fallback_models, variant, thinking_level, and timeout_minutes belong only in the matching harness block."),
9307
9336
  smart_notes: object({
9337
+ enabled: boolean2().default(true).describe("Enable smart-note authoring and condition evaluation. User-level only: tool definitions are process-wide. Changes require a host restart and rebuild each session's cached prompt once. Pending notes remain stored and unchecked; already-ready notes still surface."),
9308
9338
  retina_handoff: boolean2().default(false).describe("When true, dreamer skips smart notes whose surface conditions compiled to retina provider configs at authoring time. Default false keeps both paths active until the retina consumer is deployed.")
9309
- }).default({ retina_handoff: false }).describe("Smart-note ownership transition controls."),
9339
+ }).default({ enabled: true, retina_handoff: false }).describe("Smart-note ownership transition controls."),
9310
9340
  cache_ttl: union([string2(), object({ default: string2() }).catchall(string2())]).default("5m").describe(`How long Magic Context assumes the provider's cached prefix stays valid. This is MC's own deferral gate — it does not change the provider's actual cache lifetime. String (e.g. "5m", "1h", "30s") or per-model object ({ default: "5m", "provider/model": "1h", "provider/*": "never" }); keys resolve most-specific first (exact provider/model, bare model ID, shorter dash-prefixes, then the provider/* wildcard, then default). User and project settings, including an explicit "5m" or object default, apply on the next pass. Only built-in defaults are frozen per session, including across restarts: when unset, GPT-5.6 and later (including gpt-6*, through any provider prefix) use 30m, other models use 5m. /ctx-status shows the effective TTL and whether it comes from your config or a frozen built-in default. Editing the TTL does not itself rewrite prompt bytes; a lowered TTL rebuilds only after normal idle expiry. OpenAI documents at least 30 minutes since the latest write or reuse: https://developers.openai.com/api/docs/guides/prompt-caching (Cache lifetime and Summary of model differences). Set to "never" to mean MC never assumes expiry (for lanes kept warm externally by a cache-keep tool) — disables the idle-TTL heuristic so MC never initiates a rebuild based on elapsed time. Provider-side extended TTL is a separate request-level concern (cache_control: { ttl } in the request body).`),
9311
9341
  prompt_surface: PromptSurfaceConfigSchema.default({ default: "full" }).describe("Prompt-surface presets: default is full; models use bare model IDs, provider/model, or provider/* routing keys. Guidance and tool-description overrides are user-level only. OpenCode 1.x, Pi, and OMP register tool descriptions once per process (they follow the default preset). OpenCode 2 rewrites the five ctx_* descriptions per request from the draft model."),
9312
9342
  output_reserve: union([
@@ -9326,7 +9356,11 @@ var init_magic_context = __esm(() => {
9326
9356
  }).catchall(number2().min(5000).max(2000000)).optional().describe("Absolute token thresholds per model. When matched, overrides execute_threshold_percentage for that model. Accepts `default` for all models or per-model keys. Values above 90% × context_limit are clamped with a warning log. Min 5_000, max 2_000_000."),
9327
9357
  protected_tokens: number2().int().min(PROTECTED_TOKENS_MIN).max(1e6).optional().describe("Positive integer token floor to protect from automatic reclaim (min: 4_000, max: 1_000_000). When omitted, the derived default is clamp(round(0.05 × usableSoft), min(16_000, round(0.08 × usableSoft)), 64_000)."),
9328
9358
  protected_tags: unknown().optional().describe("Deprecated: number of recent tags to protect. Ignored for behaviour; use protected_tokens instead.").meta({ deprecated: true }),
9329
- clear_reasoning_age: number2().min(10).default(50).describe("Clear reasoning/thinking blocks older than N tags (default: 50)"),
9359
+ clear_reasoning_age: unknown().optional().describe("Deprecated and ignored. Use keep_reasoning_tokens instead.").meta({ deprecated: true }),
9360
+ keep_reasoning_tokens: union([
9361
+ number2().int().min(0).max(1e6),
9362
+ object({ default: number2().int().min(0).max(1e6).optional() }).catchall(number2().int().min(0).max(1e6))
9363
+ ]).optional().describe("Reasoning tokens to keep on rebuilding passes. Number or per-model object; exact, shorter model keys, provider/*, then default. Omitted: fixed 10,000. 0 removes all eligible historical reasoning; newest and exempt steps always stay."),
9330
9364
  history_budget_percentage: number2().min(0.05).max(0.5).default(DEFAULT_HISTORY_BUDGET_PERCENTAGE).describe("Fraction of usable context (context_limit × execute_threshold) reserved for the session history block (default: 0.15)"),
9331
9365
  historian_timeout_ms: number2().min(60000).default(DEFAULT_HISTORIAN_TIMEOUT_MS).describe("Timeout for each historian prompt call in both TypeScript and Rust transform modes, in milliseconds (default: 600000)"),
9332
9366
  commit_cluster_trigger: object({
@@ -9407,7 +9441,6 @@ var init_magic_context = __esm(() => {
9407
9441
  });
9408
9442
  LIVE_RELOAD_CONFIG_PATHS = [
9409
9443
  "cache_ttl",
9410
- "mural.model",
9411
9444
  "toast_duration_ms",
9412
9445
  "historian.opencode.model",
9413
9446
  "historian.opencode.fallback_models",
@@ -9588,16 +9621,6 @@ function stripEscalationAtExecutableSite(block, path, removed) {
9588
9621
  }
9589
9622
  }
9590
9623
  }
9591
- function stripNestedMuralModels(node, path, removed) {
9592
- for (const [key, value] of Object.entries(node)) {
9593
- if (key === "mural" && isPlainObject2(value) && "model" in value) {
9594
- delete value.model;
9595
- removed.push(`${path}.${key}.model`);
9596
- } else if (isPlainObject2(value)) {
9597
- stripNestedMuralModels(value, `${path}.${key}`, removed);
9598
- }
9599
- }
9600
- }
9601
9624
  function isValidPercentageThreshold(value) {
9602
9625
  return typeof value === "number" && Number.isFinite(value) && value >= 20 && value <= 90;
9603
9626
  }
@@ -9738,6 +9761,11 @@ function stripUnsafeProjectConfigFields(projectRaw) {
9738
9761
  delete storage.enforce_private_permissions;
9739
9762
  warnings.push("Ignoring storage.enforce_private_permissions from project config (security: only user-level config may opt into externally managed shared storage permissions).");
9740
9763
  }
9764
+ const smartNotes = projectRaw.smart_notes;
9765
+ if (isPlainObject2(smartNotes) && "enabled" in smartNotes) {
9766
+ delete smartNotes.enabled;
9767
+ warnings.push("Ignoring smart_notes.enabled from project config (only user-level config may change process-wide tool definitions; restart required).");
9768
+ }
9741
9769
  const promptSurface = projectRaw.prompt_surface;
9742
9770
  if (isPlainObject2(promptSurface)) {
9743
9771
  const removed = [];
@@ -9836,27 +9864,6 @@ function stripUnsafeProjectConfigFields(projectRaw) {
9836
9864
  delete dreamer.runner;
9837
9865
  warnings.push("Ignoring dreamer.runner from project config (security: which process and provider account run dreamer completions is a user-level setting).");
9838
9866
  }
9839
- const mural = projectRaw.mural;
9840
- if (isPlainObject2(mural) && "model" in mural) {
9841
- delete mural.model;
9842
- warnings.push("Ignoring mural.model from project config (security: the mural cue-compressor model is a user-level setting; a repository cannot choose where project memory is sent).");
9843
- }
9844
- const experimental = projectRaw.experimental;
9845
- const legacyMural = isPlainObject2(experimental) ? experimental.mural : undefined;
9846
- if (isPlainObject2(legacyMural) && "model" in legacyMural) {
9847
- delete legacyMural.model;
9848
- warnings.push("Ignoring experimental.mural.model from project config (security: the mural cue-compressor model is a user-level setting; use user-level mural.model).");
9849
- }
9850
- const nestedMuralRemoved = [];
9851
- for (const agentKey of HIDDEN_AGENT_KEYS) {
9852
- const block = projectRaw[agentKey];
9853
- if (!isPlainObject2(block))
9854
- continue;
9855
- stripNestedMuralModels(block, agentKey, nestedMuralRemoved);
9856
- }
9857
- if (nestedMuralRemoved.length > 0) {
9858
- warnings.push(`Ignoring ${nestedMuralRemoved.join(", ")} from project config (security: the mural cue-compressor model is a user-level setting; a repository cannot choose where project memory is sent).`);
9859
- }
9860
9867
  return warnings;
9861
9868
  }
9862
9869
  function constrainProjectThresholdOverrides(args) {
@@ -10891,6 +10898,11 @@ function warnProtectedTagsDeprecationOnce() {
10891
10898
  function parsePluginConfig(rawConfig, recoveredTopLevelKeys = []) {
10892
10899
  const preMigrationWarnings = [];
10893
10900
  const configWithoutRemovedAgent = stripRemovedAgentConfig(rawConfig, preMigrationWarnings);
10901
+ if (Object.hasOwn(rawConfig, "clear_reasoning_age")) {
10902
+ const warning = "clear_reasoning_age is deprecated and ignored. Magic Context now keeps reasoning up to a token budget, keep_reasoning_tokens (default 10,000). Remove the key, or set keep_reasoning_tokens to a token count.";
10903
+ preMigrationWarnings.push(warning);
10904
+ log(`[magic-context] ${warning}`);
10905
+ }
10894
10906
  if (Object.hasOwn(rawConfig, "protected_tags")) {
10895
10907
  warnProtectedTagsDeprecationOnce();
10896
10908
  preMigrationWarnings.push("protected_tags is deprecated and ignored; use protected_tokens instead.");
@@ -10900,6 +10912,7 @@ function parsePluginConfig(rawConfig, recoveredTopLevelKeys = []) {
10900
10912
  }
10901
10913
  preMigrationWarnings.push(...misplacedAgentModelWarnings(rawConfig));
10902
10914
  const migratedExperimental = migrateLegacyExperimental(configWithoutRemovedAgent, preMigrationWarnings);
10915
+ warnDeprecatedMuralModel(migratedExperimental, preMigrationWarnings);
10903
10916
  const migratedDreamer = migrateDreamerV2(migratedExperimental, preMigrationWarnings);
10904
10917
  const migrated = migrateLegacyAgentEnabledInMemory(migratedDreamer, preMigrationWarnings);
10905
10918
  const parsed = MagicContextConfigSchema.safeParse(migrated);
@@ -12822,19 +12835,28 @@ __export(exports_sqlite, {
12822
12835
  Database: () => Database,
12823
12836
  SqliteAcquisitionBusyError: () => SqliteAcquisitionBusyError,
12824
12837
  SqliteRuntimeUnavailableError: () => SqliteRuntimeUnavailableError,
12838
+ assertTransformWrite: () => assertTransformWrite,
12825
12839
  beginSqliteWriterAsync: () => beginSqliteWriterAsync,
12826
12840
  detectSqliteRuntime: () => detectSqliteRuntime,
12841
+ getSqliteDatabasePath: () => getSqliteDatabasePath,
12827
12842
  getSqliteMemoryStats: () => getSqliteMemoryStats,
12843
+ getTagIdentityWriteGeneration: () => getTagIdentityWriteGeneration,
12844
+ guardSqliteTransformPass: () => guardSqliteTransformPass,
12845
+ indexCoordinationInput: () => indexCoordinationInput,
12828
12846
  isTransientSqliteError: () => isTransientSqliteError,
12829
12847
  loadSqliteModule: () => loadSqliteModule,
12848
+ mayChangeTagIdentity: () => mayChangeTagIdentity,
12830
12849
  prepareCachedStatement: () => prepareCachedStatement,
12831
12850
  registerSlowWriteReporter: () => registerSlowWriteReporter,
12832
12851
  registerSqliteDiagnosticSink: () => registerSqliteDiagnosticSink,
12852
+ releaseIndexOwner: () => releaseIndexOwner,
12833
12853
  withAsyncPrivilegedWriter: () => withAsyncPrivilegedWriter,
12854
+ withIndexChunk: () => withIndexChunk,
12834
12855
  withPrivilegedWriter: () => withPrivilegedWriter,
12835
12856
  withSqliteBackgroundWriter: () => withSqliteBackgroundWriter,
12836
12857
  withSqliteTransformPass: () => withSqliteTransformPass,
12837
- withoutSqliteTransformPass: () => withoutSqliteTransformPass
12858
+ withoutSqliteTransformPass: () => withoutSqliteTransformPass,
12859
+ yieldIndexChunk: () => yieldIndexChunk
12838
12860
  });
12839
12861
  import { AsyncLocalStorage } from "node:async_hooks";
12840
12862
  import { statSync as statSync4 } from "node:fs";
@@ -12868,10 +12890,31 @@ function isModuleNotFoundError(error, specifier) {
12868
12890
  return false;
12869
12891
  return code === "ERR_MODULE_NOT_FOUND" || code === "ERR_UNKNOWN_BUILTIN_MODULE" || code === "MODULE_NOT_FOUND" || name === "ResolveMessage" || details.includes("module not found") || details.includes("cannot find module") || details.includes("cannot find package") || details.includes("no such built-in module");
12870
12892
  }
12893
+ function isNodeSqliteExperimentalWarning(args) {
12894
+ const [warning, options] = args;
12895
+ const message = warning instanceof Error ? warning.message : warning;
12896
+ const type = typeof options === "string" ? options : options && typeof options === "object" ? options.type : undefined;
12897
+ return type === "ExperimentalWarning" && typeof message === "string" && message.startsWith("SQLite is an experimental feature");
12898
+ }
12899
+ async function importSqliteModuleForRuntime(runtime, specifier, importer) {
12900
+ if (runtime !== "Node.js")
12901
+ return importer(specifier);
12902
+ const originalEmitWarning = process.emitWarning;
12903
+ process.emitWarning = function(...args) {
12904
+ if (isNodeSqliteExperimentalWarning(args))
12905
+ return;
12906
+ Reflect.apply(originalEmitWarning, this, args);
12907
+ };
12908
+ try {
12909
+ return await importer(specifier);
12910
+ } finally {
12911
+ process.emitWarning = originalEmitWarning;
12912
+ }
12913
+ }
12871
12914
  async function loadSqliteModule(runtime = detectSqliteRuntime(), importer = importSqliteModule) {
12872
12915
  const specifier = runtime === "Bun" ? bunSpec : nodeSpec;
12873
12916
  try {
12874
- return await importer(specifier);
12917
+ return await importSqliteModuleForRuntime(runtime, specifier, importer);
12875
12918
  } catch (error) {
12876
12919
  if (isModuleNotFoundError(error, specifier)) {
12877
12920
  throw new SqliteRuntimeUnavailableError(runtime, specifier, error);
@@ -12879,12 +12922,75 @@ async function loadSqliteModule(runtime = detectSqliteRuntime(), importer = impo
12879
12922
  throw error;
12880
12923
  }
12881
12924
  }
12925
+ function mayChangeTagIdentity(sql) {
12926
+ if (!/\btags\b/i.test(sql) || /^\s*(?:SELECT|EXPLAIN|PRAGMA)\b/i.test(sql))
12927
+ return false;
12928
+ if (/^\s*WITH\b/i.test(sql) && !/\b(?:INSERT|UPDATE|DELETE|REPLACE)\b/i.test(sql))
12929
+ return false;
12930
+ if (/;\s*\S/.test(sql) || /\bREPLACE\b/i.test(sql) || /\bON\s+CONFLICT\b/i.test(sql))
12931
+ return true;
12932
+ const insert = /^\s*INSERT\s+INTO\s+(?:main\.)?["`]?tags["`]?\s*\(([^)]*)\)/i.exec(sql);
12933
+ if (insert) {
12934
+ const columns = (insert[1] ?? "").split(",").map((column) => column.trim().replace(/["`[\]]/g, "").toLowerCase());
12935
+ return columns.includes("id") || columns.includes("rowid");
12936
+ }
12937
+ const update = /^\s*UPDATE\s+(?:main\.)?["`]?tags["`]?\s+SET\s+([\s\S]*?)\bWHERE\b/i.exec(sql);
12938
+ if (update && !/\bSELECT\b/i.test(sql)) {
12939
+ const assignments = topLevelCommaSegments(update[1] ?? "");
12940
+ if (!assignments)
12941
+ return true;
12942
+ return !assignments.every((assignment) => {
12943
+ const column = /^\s*["`]?(\w+)["`]?\s*=(?!=)/.exec(assignment)?.[1];
12944
+ return column !== undefined && TAG_COLUMNS_OUTSIDE_IDENTITY.has(column.toLowerCase());
12945
+ });
12946
+ }
12947
+ return true;
12948
+ }
12949
+ function topLevelCommaSegments(text) {
12950
+ const segments = [];
12951
+ let depth = 0;
12952
+ let quote = null;
12953
+ let start = 0;
12954
+ for (let index = 0;index < text.length; index += 1) {
12955
+ const char = text[index];
12956
+ if (quote) {
12957
+ if (char === quote) {
12958
+ if (text[index + 1] === quote)
12959
+ index += 1;
12960
+ else
12961
+ quote = null;
12962
+ }
12963
+ continue;
12964
+ }
12965
+ if (char === "'" || char === '"' || char === "`")
12966
+ quote = char;
12967
+ else if (char === "(")
12968
+ depth += 1;
12969
+ else if (char === ")") {
12970
+ depth -= 1;
12971
+ if (depth < 0)
12972
+ return null;
12973
+ } else if (char === "," && depth === 0) {
12974
+ segments.push(text.slice(start, index));
12975
+ start = index + 1;
12976
+ }
12977
+ }
12978
+ if (quote || depth !== 0)
12979
+ return null;
12980
+ segments.push(text.slice(start));
12981
+ return segments;
12982
+ }
12983
+ function getTagIdentityWriteGeneration() {
12984
+ return tagIdentityWriteGeneration;
12985
+ }
12882
12986
  function installTransactionRouting(db, readonly) {
12883
12987
  const nativeExec = db.exec.bind(db);
12884
12988
  Object.defineProperty(db, "exec", {
12885
12989
  configurable: true,
12886
12990
  writable: true,
12887
12991
  value: (sql) => {
12992
+ if (/^\s*(?:COMMIT|END)\b/i.test(sql))
12993
+ assertTransformWrite();
12888
12994
  if (/^\s*BEGIN\s+(?:IMMEDIATE|EXCLUSIVE)(?:\s+TRANSACTION)?\s*;?\s*$/i.test(sql)) {
12889
12995
  openWriterTransactions.delete(db);
12890
12996
  if (transformPassScope.getStore()?.active || backgroundWriterScope.getStore())
@@ -12895,8 +13001,14 @@ function installTransactionRouting(db, readonly) {
12895
13001
  }
12896
13002
  if (WRITER_TRANSACTION_END.test(sql) && openWriterTransactions.has(db))
12897
13003
  return endWriterTransaction(db, () => nativeExec(sql), sql);
12898
- if (!/^\s*(?:SELECT|EXPLAIN|PRAGMA|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\b/i.test(sql))
12899
- return withAutocommitTimeout(db, () => nativeExec(sql));
13004
+ if (!/^\s*(?:SELECT|EXPLAIN|PRAGMA|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\b/i.test(sql)) {
13005
+ try {
13006
+ return withAutocommitTimeout(db, () => nativeExec(sql));
13007
+ } finally {
13008
+ if (mayChangeTagIdentity(sql))
13009
+ tagIdentityWriteGeneration += 1;
13010
+ }
13011
+ }
12900
13012
  return nativeExec(sql);
12901
13013
  }
12902
13014
  });
@@ -12907,13 +13019,23 @@ function installTransactionRouting(db, readonly) {
12907
13019
  value: (sql) => {
12908
13020
  const statement = nativePrepare(sql);
12909
13021
  if (!readonly && !/^\s*(?:SELECT|EXPLAIN|PRAGMA)\b/i.test(sql)) {
13022
+ const changesTagIdentity = mayChangeTagIdentity(sql);
12910
13023
  for (const method of ["run", "get", "all"]) {
12911
13024
  const execute = statement[method].bind(statement);
12912
13025
  Object.defineProperty(statement, method, {
12913
13026
  configurable: true,
12914
- value: (...args) => withAutocommitTimeout(db, () => execute(...args))
13027
+ value: (...args) => {
13028
+ try {
13029
+ return withAutocommitTimeout(db, () => execute(...args));
13030
+ } finally {
13031
+ if (changesTagIdentity)
13032
+ tagIdentityWriteGeneration += 1;
13033
+ }
13034
+ }
12915
13035
  });
12916
13036
  }
13037
+ if (changesTagIdentity)
13038
+ wrapRemainingTagWriteForms(statement);
12917
13039
  }
12918
13040
  return statement;
12919
13041
  }
@@ -12961,6 +13083,35 @@ function installTransactionRouting(db, readonly) {
12961
13083
  }
12962
13084
  });
12963
13085
  }
13086
+ function wrapRemainingTagWriteForms(statement) {
13087
+ if (typeof statement.iterate === "function") {
13088
+ const iterate = statement.iterate.bind(statement);
13089
+ Object.defineProperty(statement, "iterate", {
13090
+ configurable: true,
13091
+ value: function* (...args) {
13092
+ tagIdentityWriteGeneration += 1;
13093
+ try {
13094
+ yield* iterate(...args);
13095
+ } finally {
13096
+ tagIdentityWriteGeneration += 1;
13097
+ }
13098
+ }
13099
+ });
13100
+ }
13101
+ if (typeof statement.values === "function") {
13102
+ const values = statement.values.bind(statement);
13103
+ Object.defineProperty(statement, "values", {
13104
+ configurable: true,
13105
+ value: (...args) => {
13106
+ try {
13107
+ return values(...args);
13108
+ } finally {
13109
+ tagIdentityWriteGeneration += 1;
13110
+ }
13111
+ }
13112
+ });
13113
+ }
13114
+ }
12964
13115
  function trackSqliteConnection(db, filename, options) {
12965
13116
  const originalClose = db.close.bind(db);
12966
13117
  const sequence = nextSqliteConnectionSequence++;
@@ -13014,6 +13165,18 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
13014
13165
  }
13015
13166
  return NodeSqliteDatabase;
13016
13167
  }
13168
+ function getSqliteDatabasePath(db) {
13169
+ for (const connection of trackedSqliteConnections.values()) {
13170
+ if (connection.reference.deref() === db) {
13171
+ return connection.filename === ":memory:" || connection.filename === "<buffer>" ? null : connection.filename;
13172
+ }
13173
+ }
13174
+ const native = db;
13175
+ const path = native.filename ?? native.name;
13176
+ if (typeof path === "string" && path !== ":memory:" && path !== "")
13177
+ return path;
13178
+ return null;
13179
+ }
13017
13180
  function pragmaValue(db, name) {
13018
13181
  const row = db.prepare(`PRAGMA ${name}`).get();
13019
13182
  if (!row)
@@ -13093,6 +13256,15 @@ function getSqliteMemoryStats() {
13093
13256
  connections
13094
13257
  };
13095
13258
  }
13259
+ function closeReleasingStatements(close, args) {
13260
+ if (args[0] === true)
13261
+ return close(true);
13262
+ try {
13263
+ return close(true);
13264
+ } catch {
13265
+ return close(...args);
13266
+ }
13267
+ }
13096
13268
  function ownPreparedStatement(db, statement) {
13097
13269
  const finalizable = statement;
13098
13270
  if (typeof finalizable.finalize !== "function")
@@ -13116,7 +13288,7 @@ function ownPreparedStatement(db, statement) {
13116
13288
  }
13117
13289
  }
13118
13290
  references.clear();
13119
- const result = close(...args);
13291
+ const result = closeReleasingStatements(close, args);
13120
13292
  if (failure)
13121
13293
  throw failure;
13122
13294
  return result;
@@ -13146,26 +13318,42 @@ function withSqliteTransformPass(operation) {
13146
13318
  const existing = transformPassScope.getStore();
13147
13319
  if (existing?.active)
13148
13320
  return operation();
13149
- const lease = { active: true, remainingWaitMs: FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS };
13150
- return transformPassScope.run(lease, () => {
13151
- try {
13152
- const result = operation();
13153
- if (result && typeof result.then === "function") {
13154
- return Promise.resolve(result).finally(() => {
13155
- lease.active = false;
13156
- });
13321
+ const reservation = reserveForegroundSqliteTurn();
13322
+ const run = (release) => {
13323
+ const lease = { active: true, remainingWaitMs: FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS };
13324
+ return transformPassScope.run(lease, () => {
13325
+ try {
13326
+ const result = operation();
13327
+ if (result && typeof result.then === "function") {
13328
+ return Promise.resolve(result).finally(() => {
13329
+ lease.active = false;
13330
+ release();
13331
+ });
13332
+ }
13333
+ lease.active = false;
13334
+ release();
13335
+ return result;
13336
+ } catch (error) {
13337
+ lease.active = false;
13338
+ release();
13339
+ throw error;
13157
13340
  }
13158
- lease.active = false;
13159
- return result;
13160
- } catch (error) {
13161
- lease.active = false;
13162
- throw error;
13163
- }
13164
- });
13341
+ });
13342
+ };
13343
+ return typeof reservation === "function" ? run(reservation) : reservation.then(run);
13165
13344
  }
13166
13345
  function withoutSqliteTransformPass(operation) {
13167
13346
  return transformPassScope.run(undefined, operation);
13168
13347
  }
13348
+ function guardSqliteTransformPass(guard) {
13349
+ const lease = transformPassScope.getStore();
13350
+ if (lease)
13351
+ lease.guard = guard;
13352
+ }
13353
+ function assertTransformWrite() {
13354
+ if (!backgroundWriterScope.getStore())
13355
+ transformPassScope.getStore()?.guard?.assert();
13356
+ }
13169
13357
  function isTransientSqliteError(error) {
13170
13358
  if (!error || typeof error !== "object")
13171
13359
  return false;
@@ -13177,6 +13365,7 @@ function foregroundWaitLease() {
13177
13365
  return lease?.active && !admissionScope.getStore() && !backgroundWriterScope.getStore() ? lease : undefined;
13178
13366
  }
13179
13367
  function withAutocommitTimeout(db, operation) {
13368
+ assertTransformWrite();
13180
13369
  const lease = foregroundWaitLease();
13181
13370
  if (isInTransaction(db) || !lease && !backgroundWriterScope.getStore())
13182
13371
  return operation();
@@ -13197,6 +13386,7 @@ function withAutocommitTimeout(db, operation) {
13197
13386
  }
13198
13387
  }
13199
13388
  function acquireShort(db, acquire, site) {
13389
+ assertTransformWrite();
13200
13390
  const started = performance.now();
13201
13391
  const previous = pragmaNumber(db, "busy_timeout");
13202
13392
  const outer = writerAcquisitionScope.getStore();
@@ -13208,7 +13398,7 @@ function acquireShort(db, acquire, site) {
13208
13398
  let acquiredAt;
13209
13399
  try {
13210
13400
  const lease = foregroundWaitLease();
13211
- const timeout = lease ? Math.floor(lease.remainingWaitMs) : SHORT_BUSY_TIMEOUT_MS;
13401
+ const timeout = Math.floor(Math.min(lease ? lease.remainingWaitMs : SHORT_BUSY_TIMEOUT_MS, owner?.remainingMs?.() ?? lease?.guard?.remainingMs() ?? Infinity));
13212
13402
  db.exec(`PRAGMA busy_timeout=${timeout}`);
13213
13403
  acquire();
13214
13404
  acquiredAt = performance.now();
@@ -13238,18 +13428,20 @@ function acquireShort(db, acquire, site) {
13238
13428
  }
13239
13429
  function endWriterTransaction(db, end, sql) {
13240
13430
  let ended = false;
13431
+ const endStarted = performance.now();
13241
13432
  try {
13242
13433
  const result = end();
13243
13434
  ended = true;
13244
13435
  return result;
13245
13436
  } finally {
13246
13437
  const open = openWriterTransactions.get(db);
13438
+ const endMs = performance.now() - endStarted;
13247
13439
  if (open && !isInTransaction(db)) {
13248
13440
  openWriterTransactions.delete(db);
13249
13441
  const holdMs = performance.now() - open.acquiredAt;
13250
13442
  if (open.acquireMs >= SLOW_WRITER_LOG_MS || holdMs >= SLOW_WRITER_LOG_MS) {
13251
13443
  const outcome = /^\s*ROLLBACK/i.test(sql) ? "rolled_back" : ended ? "committed" : "failed";
13252
- reportSqliteDiagnostic(`sqlite writer site=${open.site} lane=${open.lane} acquire_ms=${Math.round(open.acquireMs)} hold_ms=${Math.round(holdMs)} attempts=${open.attempts} outcome=${outcome}`);
13444
+ reportSqliteDiagnostic(`sqlite writer site=${open.site} lane=${open.lane} acquire_ms=${Math.round(open.acquireMs)} hold_ms=${Math.round(holdMs)} end_ms=${Math.round(endMs)} attempts=${open.attempts} outcome=${outcome}`);
13253
13445
  }
13254
13446
  }
13255
13447
  }
@@ -13281,29 +13473,53 @@ async function beginSqliteWriterAsync(db, site) {
13281
13473
  }
13282
13474
  }
13283
13475
  }
13284
- async function withAsyncPrivilegedWriter(db, operation) {
13476
+ async function withAsyncPrivilegedWriter(db, operation, options = {}) {
13285
13477
  const started = performance.now();
13478
+ const budgetMs = Math.max(0, Math.min(FOREGROUND_ACQUISITION_BUDGET_MS, options.budgetMs ?? Infinity));
13286
13479
  let attempts = 0;
13287
13480
  for (;; ) {
13481
+ options.signal?.throwIfAborted();
13482
+ options.assertCurrentPass?.();
13288
13483
  attempts++;
13484
+ let entered = false;
13289
13485
  try {
13290
13486
  const acquisition = {
13291
13487
  site: "privileged_writer",
13292
13488
  lane: "foreground",
13293
13489
  startedAt: started,
13294
13490
  attempts,
13295
- claimed: false
13491
+ claimed: false,
13492
+ remainingMs: () => Math.max(0, budgetMs - (performance.now() - started))
13296
13493
  };
13297
- return admissionScope.run(true, () => withSqliteTransformPass(() => writerAcquisitionScope.run(acquisition, () => withPrivilegedWriter(db, operation))));
13494
+ return admissionScope.run(true, () => withSqliteTransformPass(() => writerAcquisitionScope.run(acquisition, () => withPrivilegedWriter(db, () => {
13495
+ entered = true;
13496
+ return operation();
13497
+ }))));
13298
13498
  } catch (error) {
13299
- if (!(error instanceof SqliteAcquisitionBusyError))
13499
+ if (entered || !(error instanceof SqliteAcquisitionBusyError))
13300
13500
  throw error;
13501
+ options.beforeRetry?.(error);
13301
13502
  const elapsed = performance.now() - started;
13302
- if (elapsed >= FOREGROUND_ACQUISITION_BUDGET_MS) {
13503
+ if (elapsed >= budgetMs) {
13303
13504
  reportSqliteDiagnostic(`sqlite writer site=privileged_writer lane=foreground acquire_ms=${Math.round(elapsed)} attempts=${attempts} outcome=busy`);
13304
13505
  throw error;
13305
13506
  }
13306
- await new Promise((resolve) => setTimeout(resolve, Math.min(attempts === 1 ? 500 : 1000, FOREGROUND_ACQUISITION_BUDGET_MS - elapsed)));
13507
+ const backoff = attempts === 1 ? 500 : 1000;
13508
+ const delay = Math.min(options.jitter ? backoff * (0.8 + Math.random() * 0.4) : backoff, budgetMs - elapsed);
13509
+ await new Promise((resolve, reject) => {
13510
+ const abort = () => {
13511
+ clearTimeout(timer);
13512
+ options.signal?.removeEventListener("abort", abort);
13513
+ reject(options.signal?.reason ?? new Error("Writer wait aborted"));
13514
+ };
13515
+ const timer = setTimeout(() => {
13516
+ options.signal?.removeEventListener("abort", abort);
13517
+ resolve();
13518
+ }, delay);
13519
+ options.signal?.addEventListener("abort", abort, { once: true });
13520
+ if (options.signal?.aborted)
13521
+ abort();
13522
+ });
13307
13523
  }
13308
13524
  }
13309
13525
  }
@@ -13365,7 +13581,55 @@ function withPrivilegedWriter(db, operation) {
13365
13581
  throw error;
13366
13582
  }
13367
13583
  }
13368
- var reportSlowPrivilegedWrite, sqliteDiagnosticSink, bunSpec, nodeSpec, SqliteRuntimeUnavailableError, detectedRuntime, isBun, sqliteModule, DatabaseImpl, trackedSqliteConnections, nextSqliteConnectionSequence = 1, TrackedDatabase, Database, statementOwners, FIRST_PRUNE_AT = 256, privilegeDepth, transformPassScope, admissionScope, backgroundWriterScope, SqliteAcquisitionBusyError, SHORT_BUSY_TIMEOUT_MS = 25, FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS = 250, FOREGROUND_ACQUISITION_BUDGET_MS = 16500, SLOW_WRITER_LOG_MS = 250, WRITER_TRANSACTION_END, writerAcquisitionScope, openWriterTransactions;
13584
+ function indexCoordinationInput() {
13585
+ return {
13586
+ indexCoordination: indexCoordinationBuffer,
13587
+ indexOwner: Atomics.add(indexCoordinationState, 2, 1) + 1
13588
+ };
13589
+ }
13590
+ function reserveForegroundSqliteTurn() {
13591
+ Atomics.add(indexCoordinationState, 0, 1);
13592
+ const release = () => {
13593
+ Atomics.sub(indexCoordinationState, 0, 1);
13594
+ Atomics.notify(indexCoordinationState, 0);
13595
+ };
13596
+ if (Atomics.load(indexCoordinationState, 1) === 0)
13597
+ return release;
13598
+ return (async () => {
13599
+ while (Atomics.load(indexCoordinationState, 1) !== 0)
13600
+ await yieldIndexTurn();
13601
+ return release;
13602
+ })();
13603
+ }
13604
+ function releaseIndexOwner(input) {
13605
+ if (!input.indexCoordination || !input.indexOwner)
13606
+ return;
13607
+ const shared = new Int32Array(input.indexCoordination);
13608
+ Atomics.compareExchange(shared, 1, input.indexOwner, 0);
13609
+ Atomics.notify(shared, 1);
13610
+ }
13611
+ async function withIndexChunk(input, operation) {
13612
+ if (!input.indexCoordination || !input.indexOwner)
13613
+ return operation();
13614
+ const shared = new Int32Array(input.indexCoordination);
13615
+ for (;; ) {
13616
+ if (Atomics.load(shared, 0) === 0 && Atomics.compareExchange(shared, 1, 0, input.indexOwner) === 0) {
13617
+ if (Atomics.load(shared, 0) === 0)
13618
+ break;
13619
+ releaseIndexOwner(input);
13620
+ }
13621
+ await yieldIndexTurn();
13622
+ }
13623
+ try {
13624
+ return await operation();
13625
+ } finally {
13626
+ releaseIndexOwner(input);
13627
+ }
13628
+ }
13629
+ async function yieldIndexChunk() {
13630
+ await yieldIndexTurn();
13631
+ }
13632
+ var reportSlowPrivilegedWrite, sqliteDiagnosticSink, bunSpec, nodeSpec, SqliteRuntimeUnavailableError, detectedRuntime, isBun, sqliteModule, DatabaseImpl, trackedSqliteConnections, nextSqliteConnectionSequence = 1, TAG_COLUMNS_OUTSIDE_IDENTITY, tagIdentityWriteGeneration = 0, TrackedDatabase, Database, statementOwners, FIRST_PRUNE_AT = 256, privilegeDepth, transformPassScope, admissionScope, backgroundWriterScope, SqliteAcquisitionBusyError, SHORT_BUSY_TIMEOUT_MS = 25, FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS = 250, FOREGROUND_ACQUISITION_BUDGET_MS = 16500, SLOW_WRITER_LOG_MS = 250, WRITER_TRANSACTION_END, writerAcquisitionScope, openWriterTransactions, indexCoordinationKey, indexCoordinationHeap, indexCoordinationBuffer, indexCoordinationState, yieldIndexTurn = () => new Promise((resolve) => setTimeout(resolve, 1));
13369
13633
  var init_sqlite = __esm(async () => {
13370
13634
  bunSpec = "bun:" + "sqlite";
13371
13635
  nodeSpec = "node:" + "sqlite";
@@ -13385,6 +13649,19 @@ var init_sqlite = __esm(async () => {
13385
13649
  sqliteModule = await loadSqliteModule(detectedRuntime);
13386
13650
  DatabaseImpl = isBun ? sqliteModule.Database : buildNodeSqliteDatabaseClass(sqliteModule.DatabaseSync);
13387
13651
  trackedSqliteConnections = new Map;
13652
+ TAG_COLUMNS_OUTSIDE_IDENTITY = new Set([
13653
+ "status",
13654
+ "drop_mode",
13655
+ "byte_size",
13656
+ "input_byte_size",
13657
+ "reasoning_byte_size",
13658
+ "token_count",
13659
+ "input_token_count",
13660
+ "caveman_depth",
13661
+ "entry_fingerprint",
13662
+ "tool_name",
13663
+ "harness"
13664
+ ]);
13388
13665
  TrackedDatabase = new Proxy(DatabaseImpl, {
13389
13666
  construct(target, args) {
13390
13667
  const db = Reflect.construct(target, args, target);
@@ -13409,6 +13686,11 @@ var init_sqlite = __esm(async () => {
13409
13686
  WRITER_TRANSACTION_END = /^\s*(?:COMMIT|END|ROLLBACK)(?:\s+TRANSACTION)?\s*;?\s*$/i;
13410
13687
  writerAcquisitionScope = new AsyncLocalStorage;
13411
13688
  openWriterTransactions = new WeakMap;
13689
+ indexCoordinationKey = Symbol.for("magic-context.index-foreground-coordination.v1");
13690
+ indexCoordinationHeap = globalThis;
13691
+ indexCoordinationBuffer = indexCoordinationHeap[indexCoordinationKey] ?? new SharedArrayBuffer(3 * Int32Array.BYTES_PER_ELEMENT);
13692
+ indexCoordinationHeap[indexCoordinationKey] = indexCoordinationBuffer;
13693
+ indexCoordinationState = new Int32Array(indexCoordinationBuffer);
13412
13694
  });
13413
13695
 
13414
13696
  // src/lib/docs-proposal-gitignore.ts
@@ -22856,6 +23138,22 @@ function readConfiguredOpenCodePluginSpec(config) {
22856
23138
  }
22857
23139
  return "latest";
22858
23140
  }
23141
+ function readReferencedOpenCodePluginSpecs(config, host) {
23142
+ const specs = new Set;
23143
+ for (const { key, entry } of readPluginEntries(config)) {
23144
+ if (host === "opencode1" && key !== "plugin")
23145
+ continue;
23146
+ const specifier = pluginEntrySpecifier(entry);
23147
+ const spec = specifier === undefined ? undefined : openCodeV2PluginSpecOf(specifier);
23148
+ if (spec !== undefined)
23149
+ specs.add(spec);
23150
+ }
23151
+ return specs;
23152
+ }
23153
+ function describeUnusedOpenCodePluginCache(input) {
23154
+ const why = input.loadedFrom ? `the config loads Magic Context from ${input.loadedFrom}` : `no ${input.host} plugin entry in the config references it`;
23155
+ return `${input.host} plugin cache: unused cached copy of ${OPENCODE_PLUGIN_NAME}@${input.spec} (cached: ${input.cached ?? "unreadable"}); ${input.host} never loads it because ${why}. Informational, nothing breaks; safe to delete: ${input.paths.join(", ")}`;
23156
+ }
22859
23157
  function pluginEntrySpecifier(entry) {
22860
23158
  if (typeof entry === "string")
22861
23159
  return entry;
@@ -23081,6 +23379,9 @@ function checkOpenCodeV2PluginCache(options, deps = {}) {
23081
23379
  const distTag = options.distTag && options.distTag !== "latest" ? options.distTag : undefined;
23082
23380
  const slot = deps.slot ?? getOpenCodeV2PluginCacheSlot(undefined, distTag ?? "latest");
23083
23381
  const tagged = distTag ? { distTag } : {};
23382
+ if (options.referencedSpecs !== undefined && !options.referencedSpecs.has(distTag ?? "latest")) {
23383
+ return existsSync13(slot) ? { action: "unused", slot, cached: readOpenCodeV2CachedPluginVersion(slot), ...tagged } : { action: "not_found", slot, unused: true, ...tagged };
23384
+ }
23084
23385
  if (!existsSync13(slot))
23085
23386
  return { action: "not_found", slot, ...tagged };
23086
23387
  const cached = readOpenCodeV2CachedPluginVersion(slot);
@@ -23120,7 +23421,7 @@ function reportOpenCodeV2PluginCache(result, report, options) {
23120
23421
  const slotName = result.distTag ? ` @${result.distTag}` : "";
23121
23422
  switch (result.action) {
23122
23423
  case "not_found":
23123
- if (options.reportMissing) {
23424
+ if (options.reportMissing && !result.unused) {
23124
23425
  report.pass(`OpenCode 2 plugin cache has no Magic Context${slotName} install yet (OpenCode installs it on next start)`);
23125
23426
  }
23126
23427
  return { fixed: false, issue: false };
@@ -23148,6 +23449,15 @@ function reportOpenCodeV2PluginCache(result, report, options) {
23148
23449
  report.pass(`Cleared ${result.forced ? "" : "outdated "}Magic Context${slotName} from the OpenCode 2 plugin cache (${versions}${result.forced ? ", --force" : ""}) — OpenCode installs the ${result.distTag ? `current ${tag} release` : "latest"} on next start`);
23149
23450
  report.info(` ${result.slot}`);
23150
23451
  return { fixed: true, issue: false };
23452
+ case "unused":
23453
+ report.info(describeUnusedOpenCodePluginCache({
23454
+ host: "OpenCode 2",
23455
+ spec: tag,
23456
+ cached: result.cached,
23457
+ paths: [result.slot],
23458
+ loadedFrom: options.loadedFrom
23459
+ }));
23460
+ return { fixed: false, issue: false };
23151
23461
  case "error":
23152
23462
  report.warn(`Could not clear the OpenCode 2 plugin cache: ${result.error}`);
23153
23463
  report.info(` Manually delete: ${result.slot}`);
@@ -28683,6 +28993,26 @@ var init_migrations = __esm(async () => {
28683
28993
  LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
28684
28994
  });
28685
28995
 
28996
+ // ../plugin/src/features/magic-context/storage-tag-identity-revision.ts
28997
+ function installTagIdentityRevisionTrigger(db) {
28998
+ db.exec(TRIGGER_SQL);
28999
+ }
29000
+ var TAG_IDENTITY_REVISION_PREFIX = "tag_identity_revision:", BUMP = (session) => `INSERT INTO schema_migrations_meta (key, value)
29001
+ VALUES ('${TAG_IDENTITY_REVISION_PREFIX}' || ${session}, '1')
29002
+ ON CONFLICT(key) DO UPDATE SET value = CAST(CAST(value AS INTEGER) + 1 AS TEXT)`, TRIGGER_SQL;
29003
+ var init_storage_tag_identity_revision = __esm(() => {
29004
+ TRIGGER_SQL = `CREATE TEMP TRIGGER IF NOT EXISTS mc_tag_identity_revision_au
29005
+ AFTER UPDATE OF id, session_id, message_id, tag_number, type, tool_owner_message_id, reasoning_token_count
29006
+ ON main.tags
29007
+ BEGIN
29008
+ ${BUMP("OLD.session_id")};
29009
+ INSERT INTO schema_migrations_meta (key, value)
29010
+ SELECT '${TAG_IDENTITY_REVISION_PREFIX}' || NEW.session_id, '1'
29011
+ WHERE NEW.session_id IS NOT OLD.session_id
29012
+ ON CONFLICT(key) DO UPDATE SET value = CAST(CAST(value AS INTEGER) + 1 AS TEXT);
29013
+ END`;
29014
+ });
29015
+
28686
29016
  // ../plugin/src/shared/commit-detection.ts
28687
29017
  function createCommitHashExtractPattern() {
28688
29018
  return new RegExp(`\`?\\b(${HASH_HEX})\\b\`?`, "gi");
@@ -28705,6 +29035,271 @@ function removeSystemReminders(text) {
28705
29035
  }
28706
29036
  var SYSTEM_DIRECTIVE_PREFIX = "[SYSTEM DIRECTIVE: MAGIC-CONTEXT";
28707
29037
 
29038
+ // ../plugin/src/hooks/magic-context/token-count-exact.ts
29039
+ function hasInternals(tokenizer) {
29040
+ const value = tokenizer;
29041
+ return !!value && value.patternRegex instanceof RegExp && value.patternRegex.global && typeof value.stringRankEncoder === "object" && value.stringRankEncoder !== null && Array.isArray(value.binaryFirstByteIndex);
29042
+ }
29043
+ function isValidUtf8(bytes) {
29044
+ let index = 0;
29045
+ while (index < bytes.length) {
29046
+ const first = bytes[index];
29047
+ let size = 0;
29048
+ let codePoint = 0;
29049
+ if (first <= 127) {
29050
+ size = 1;
29051
+ codePoint = first;
29052
+ } else if ((first & 224) === 192) {
29053
+ size = 2;
29054
+ codePoint = first & 31;
29055
+ if (first <= 193)
29056
+ return false;
29057
+ } else if ((first & 240) === 224) {
29058
+ size = 3;
29059
+ codePoint = first & 15;
29060
+ } else if ((first & 248) === 240) {
29061
+ size = 4;
29062
+ codePoint = first & 7;
29063
+ if (first > 244)
29064
+ return false;
29065
+ } else
29066
+ return false;
29067
+ if (index + size > bytes.length)
29068
+ return false;
29069
+ for (let offset = 1;offset < size; offset += 1) {
29070
+ const byte = bytes[index + offset];
29071
+ if ((byte & 192) !== 128)
29072
+ return false;
29073
+ codePoint = codePoint << 6 | byte & 63;
29074
+ }
29075
+ if (size === 2 && codePoint < 128)
29076
+ return false;
29077
+ if (size === 3 && codePoint < 2048)
29078
+ return false;
29079
+ if (size === 4 && codePoint < 65536)
29080
+ return false;
29081
+ if (codePoint >= 55296 && codePoint <= 57343)
29082
+ return false;
29083
+ if (codePoint > 1114111)
29084
+ return false;
29085
+ index += size;
29086
+ }
29087
+ return true;
29088
+ }
29089
+ function binarySearchBytes(sorted, key) {
29090
+ let low = 0;
29091
+ let high = sorted.length - 1;
29092
+ while (low <= high) {
29093
+ const mid = low + high >>> 1;
29094
+ const midKey = sorted[mid][0];
29095
+ let compare = 0;
29096
+ const shared = Math.min(midKey.length, key.length);
29097
+ for (let index = 0;index < shared; index += 1) {
29098
+ compare = midKey[index] - key[index];
29099
+ if (compare !== 0)
29100
+ break;
29101
+ }
29102
+ if (compare === 0)
29103
+ compare = midKey.length - key.length;
29104
+ if (compare === 0)
29105
+ return sorted[mid][1];
29106
+ if (compare < 0)
29107
+ low = mid + 1;
29108
+ else
29109
+ high = mid - 1;
29110
+ }
29111
+ return -1;
29112
+ }
29113
+ function rankOf(tokenizer, slice) {
29114
+ if (isValidUtf8(slice)) {
29115
+ const rank = tokenizer.stringRankEncoder[textDecoder.decode(slice)];
29116
+ if (rank !== undefined)
29117
+ return rank;
29118
+ }
29119
+ const bucket = tokenizer.binaryFirstByteIndex[slice[0]];
29120
+ if (bucket !== null && bucket !== undefined) {
29121
+ const rank = binarySearchBytes(bucket, slice);
29122
+ if (rank !== -1)
29123
+ return rank;
29124
+ }
29125
+ return NO_RANK;
29126
+ }
29127
+ function mergeByScanning(tokenizer, bytes) {
29128
+ const starts = [];
29129
+ const ranks = [];
29130
+ const pairRank = (index) => {
29131
+ const end = starts[index + 2];
29132
+ return end === undefined ? NO_RANK : rankOf(tokenizer, bytes.subarray(starts[index], end));
29133
+ };
29134
+ for (let index = 0;index <= bytes.length; index += 1)
29135
+ starts.push(index);
29136
+ for (let index = 0;index < starts.length; index += 1)
29137
+ ranks.push(pairRank(index));
29138
+ while (starts.length > 1) {
29139
+ let minRank = NO_RANK;
29140
+ let minIndex = -1;
29141
+ for (let index = 0;index < ranks.length - 1; index += 1) {
29142
+ if (ranks[index] < minRank) {
29143
+ minRank = ranks[index];
29144
+ minIndex = index;
29145
+ }
29146
+ }
29147
+ if (minIndex === -1)
29148
+ break;
29149
+ starts.splice(minIndex + 1, 1);
29150
+ ranks.splice(minIndex, 1);
29151
+ ranks[minIndex] = pairRank(minIndex);
29152
+ if (minIndex > 0)
29153
+ ranks[minIndex - 1] = pairRank(minIndex - 1);
29154
+ }
29155
+ const tokens = [];
29156
+ for (let index = 0;index < starts.length - 1; index += 1) {
29157
+ const rank = rankOf(tokenizer, bytes.subarray(starts[index], starts[index + 1]));
29158
+ if (rank !== NO_RANK)
29159
+ tokens.push(rank);
29160
+ }
29161
+ return tokens;
29162
+ }
29163
+ function mergeWithHeap(tokenizer, bytes) {
29164
+ const length = bytes.length;
29165
+ const next = new Int32Array(length + 1);
29166
+ const prev = new Int32Array(length + 1);
29167
+ const alive = new Uint8Array(length + 1).fill(1);
29168
+ const rank = new Float64Array(length + 1).fill(NO_RANK);
29169
+ for (let index = 0;index <= length; index += 1) {
29170
+ next[index] = index + 1;
29171
+ prev[index] = index - 1;
29172
+ }
29173
+ const heapRank = [];
29174
+ const heapAt = [];
29175
+ const less = (a, b) => heapRank[a] < heapRank[b] || heapRank[a] === heapRank[b] && heapAt[a] < heapAt[b];
29176
+ const swap = (a, b) => {
29177
+ [heapRank[a], heapRank[b]] = [heapRank[b], heapRank[a]];
29178
+ [heapAt[a], heapAt[b]] = [heapAt[b], heapAt[a]];
29179
+ };
29180
+ const push = (value, at) => {
29181
+ heapRank.push(value);
29182
+ heapAt.push(at);
29183
+ let child = heapRank.length - 1;
29184
+ while (child > 0) {
29185
+ const parent = child - 1 >> 1;
29186
+ if (!less(child, parent))
29187
+ break;
29188
+ swap(child, parent);
29189
+ child = parent;
29190
+ }
29191
+ };
29192
+ const pop = () => {
29193
+ const last = heapRank.length - 1;
29194
+ swap(0, last);
29195
+ heapRank.pop();
29196
+ heapAt.pop();
29197
+ let parent = 0;
29198
+ for (;; ) {
29199
+ const left = parent * 2 + 1;
29200
+ const right = left + 1;
29201
+ let smallest = parent;
29202
+ if (left < heapRank.length && less(left, smallest))
29203
+ smallest = left;
29204
+ if (right < heapRank.length && less(right, smallest))
29205
+ smallest = right;
29206
+ if (smallest === parent)
29207
+ break;
29208
+ swap(parent, smallest);
29209
+ parent = smallest;
29210
+ }
29211
+ };
29212
+ const pairRank = (at) => {
29213
+ const middle = next[at];
29214
+ if (middle >= length)
29215
+ return NO_RANK;
29216
+ return rankOf(tokenizer, bytes.subarray(at, next[middle]));
29217
+ };
29218
+ const update = (at) => {
29219
+ rank[at] = pairRank(at);
29220
+ if (rank[at] !== NO_RANK)
29221
+ push(rank[at], at);
29222
+ };
29223
+ for (let at = 0;at < length; at += 1)
29224
+ update(at);
29225
+ while (heapRank.length > 0) {
29226
+ const value = heapRank[0];
29227
+ const at = heapAt[0];
29228
+ pop();
29229
+ if (!alive[at] || rank[at] !== value)
29230
+ continue;
29231
+ const removed = next[at];
29232
+ alive[removed] = 0;
29233
+ next[at] = next[removed];
29234
+ prev[next[removed]] = at;
29235
+ update(at);
29236
+ if (at > 0)
29237
+ update(prev[at]);
29238
+ }
29239
+ const tokens = [];
29240
+ for (let at = 0;at < length; at = next[at]) {
29241
+ const value = rankOf(tokenizer, bytes.subarray(at, next[at]));
29242
+ if (value !== NO_RANK)
29243
+ tokens.push(value);
29244
+ }
29245
+ return tokens;
29246
+ }
29247
+ function mergePiece(tokenizer, piece) {
29248
+ let cache = mergeCaches.get(tokenizer);
29249
+ if (!cache) {
29250
+ cache = { current: new Map, previous: new Map };
29251
+ mergeCaches.set(tokenizer, cache);
29252
+ }
29253
+ let tokens = cache.current.get(piece);
29254
+ if (tokens)
29255
+ return tokens;
29256
+ tokens = cache.previous.get(piece);
29257
+ if (!tokens) {
29258
+ const bytes = textEncoder.encode(piece);
29259
+ mergedPieces += 1;
29260
+ tokens = bytes.length > LONG_PIECE_BYTES ? mergeWithHeap(tokenizer, bytes) : mergeByScanning(tokenizer, bytes);
29261
+ }
29262
+ if (cache.current.size >= MERGE_CACHE_GENERATION) {
29263
+ cache.previous = cache.current;
29264
+ cache.current = new Map;
29265
+ }
29266
+ cache.current.set(piece, tokens);
29267
+ return tokens;
29268
+ }
29269
+ function encodeTokensExactly(tokenizer, text) {
29270
+ if (!hasInternals(tokenizer)) {
29271
+ return tokenizer.encode(text, "all");
29272
+ }
29273
+ if (text.length < 10) {
29274
+ const direct = tokenizer.stringRankEncoder[text];
29275
+ if (direct !== undefined)
29276
+ return [direct];
29277
+ }
29278
+ const tokens = [];
29279
+ const regex = tokenizer.patternRegex;
29280
+ regex.lastIndex = 0;
29281
+ for (let match = regex.exec(text);match !== null; match = regex.exec(text)) {
29282
+ const piece = match[0];
29283
+ const direct = tokenizer.stringRankEncoder[piece];
29284
+ if (direct !== undefined) {
29285
+ tokens.push(direct);
29286
+ continue;
29287
+ }
29288
+ for (const token of mergePiece(tokenizer, piece))
29289
+ tokens.push(token);
29290
+ }
29291
+ return tokens;
29292
+ }
29293
+ function countTokensExactly(tokenizer, text) {
29294
+ return encodeTokensExactly(tokenizer, text).length;
29295
+ }
29296
+ var NO_RANK = 4294967295, LONG_PIECE_BYTES = 256, MERGE_CACHE_GENERATION = 50000, textEncoder, textDecoder, mergeCaches, mergedPieces = 0;
29297
+ var init_token_count_exact = __esm(() => {
29298
+ textEncoder = new TextEncoder;
29299
+ textDecoder = new TextDecoder("utf-8");
29300
+ mergeCaches = new WeakMap;
29301
+ });
29302
+
28708
29303
  // ../plugin/src/hooks/magic-context/read-session-formatting.ts
28709
29304
  import { createRequire as createRequire2 } from "node:module";
28710
29305
  function hasMeaningfulUserText(parts) {
@@ -28847,7 +29442,7 @@ function estimateTokens(text) {
28847
29442
  if (!activeTokenizer)
28848
29443
  return estimateTokensHeuristically(text);
28849
29444
  try {
28850
- return activeTokenizer.encode(text, "all").length;
29445
+ return countTokensExactly(activeTokenizer, text);
28851
29446
  } catch (error) {
28852
29447
  tokenizer = undefined;
28853
29448
  tokenizerLoadAttempted = true;
@@ -28938,21 +29533,41 @@ var MAX_COMMITS_PER_BLOCK = 5, tokenizer, tokenizerLoadAttempted = false, tokeni
28938
29533
  var init_read_session_formatting = __esm(() => {
28939
29534
  init_commit_detection();
28940
29535
  init_logger();
29536
+ init_token_count_exact();
28941
29537
  promptTokenCounts = new Map;
28942
29538
  PROMPT_TOKEN_COUNT_BYTES = 8 * 1024 * 1024;
28943
29539
  });
28944
29540
 
28945
29541
  // ../plugin/src/shared/stable-json.ts
28946
29542
  function stableStringify(value, seen = new WeakSet) {
28947
- if (value === undefined)
28948
- return "undefined";
28949
- if (value === null || typeof value !== "object")
28950
- return JSON.stringify(value) ?? String(value);
28951
- if (seen.has(value))
28952
- return '"[Circular]"';
29543
+ const pieces = [];
29544
+ writeStable(value, seen, pieces);
29545
+ return pieces.join("");
29546
+ }
29547
+ function writeStable(value, seen, pieces) {
29548
+ if (value === undefined) {
29549
+ pieces.push("undefined");
29550
+ return;
29551
+ }
29552
+ if (value === null || typeof value !== "object") {
29553
+ pieces.push(JSON.stringify(value) ?? String(value));
29554
+ return;
29555
+ }
29556
+ if (seen.has(value)) {
29557
+ pieces.push('"[Circular]"');
29558
+ return;
29559
+ }
28953
29560
  seen.add(value);
28954
29561
  if (Array.isArray(value)) {
28955
- return `[${value.map((item) => stableStringify(item, seen)).join(",")}]`;
29562
+ pieces.push("[");
29563
+ for (let index = 0;index < value.length; index += 1) {
29564
+ if (index > 0)
29565
+ pieces.push(",");
29566
+ if (index in value)
29567
+ writeStable(value[index], seen, pieces);
29568
+ }
29569
+ pieces.push("]");
29570
+ return;
28956
29571
  }
28957
29572
  const entries = Object.entries(value).sort(([a], [b]) => {
28958
29573
  if (a < b)
@@ -28961,7 +29576,14 @@ function stableStringify(value, seen = new WeakSet) {
28961
29576
  return 1;
28962
29577
  return 0;
28963
29578
  });
28964
- return `{${entries.map(([key, child]) => `${JSON.stringify(key)}:${stableStringify(child, seen)}`).join(",")}}`;
29579
+ pieces.push("{");
29580
+ entries.forEach(([key, child], index) => {
29581
+ if (index > 0)
29582
+ pieces.push(",");
29583
+ pieces.push(JSON.stringify(key), ":");
29584
+ writeStable(child, seen, pieces);
29585
+ });
29586
+ pieces.push("}");
28965
29587
  }
28966
29588
 
28967
29589
  // ../plugin/src/features/magic-context/tool-definition-tokens.ts
@@ -29647,6 +30269,19 @@ var init_image_token_estimate = __esm(() => {
29647
30269
  JPEG_SCAN_LIMIT_BYTES = 256 * 1024;
29648
30270
  });
29649
30271
 
30272
+ // ../plugin/src/hooks/magic-context/provider-visible-parts.ts
30273
+ var KEPT_TOOL_STATE_FIELDS;
30274
+ var init_provider_visible_parts = __esm(() => {
30275
+ KEPT_TOOL_STATE_FIELDS = new Set([
30276
+ "status",
30277
+ "input",
30278
+ "output",
30279
+ "error",
30280
+ "attachments",
30281
+ "content"
30282
+ ]);
30283
+ });
30284
+
29650
30285
  // ../plugin/src/hooks/magic-context/todo-view.ts
29651
30286
  var TODO_STATUS_PENDING = "pending", TODO_STATUS_IN_PROGRESS = "in_progress", TODO_STATUS_COMPLETED = "completed", TODO_STATUS_CANCELLED = "cancelled", TODO_PRIORITY_HIGH = "high", TODO_PRIORITY_MEDIUM = "medium", TODO_PRIORITY_LOW = "low", TODO_STATUSES, TODO_PRIORITIES, TODO_STATUS_SET, TODO_PRIORITY_SET, TERMINAL_STATUSES, TITLE_DONE_STATUSES;
29652
30287
  var init_todo_view = __esm(() => {
@@ -29676,10 +30311,11 @@ var init_tail_hygiene_walk = __esm(() => {
29676
30311
  init_reclaim_protection();
29677
30312
  init_tag_owner_id();
29678
30313
  init_image_token_estimate();
30314
+ init_provider_visible_parts();
29679
30315
  init_read_session_formatting();
29680
30316
  init_todo_view();
29681
30317
  init_reclaim_protection();
29682
- MAX_CONTENT_MEMO_BYTES = 64 * 1024 * 1024;
30318
+ MAX_CONTENT_MEMO_BYTES = 256 * 1024 * 1024;
29683
30319
  contentMemo = new Map;
29684
30320
  contentMemoOrder = new Set;
29685
30321
  baselineMeasurementMemo = new Map;
@@ -29745,12 +30381,14 @@ function pickNearestPriorOwner(candidates, currentMessageId, times) {
29745
30381
  }
29746
30382
  return best?.id ?? null;
29747
30383
  }
29748
- var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, getAssignableTagNumberByMessageIdStatements, hasPiFallbackMessageTagStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements, recentTagOwnerStatements, RECLAIM_HINT_EXCLUDED_TOOLS, getActiveToolTagsForAgeReclaimStatements, scopedActiveTokenTotalStatements, updateTagTokenCountStatements, updateTagInputTokenCountStatements, inertWhitespaceStatements, foldStatements, getOwnerScopedToolTagNumbersStatements, getMinMessageTagNumberForRawIdStatements, getActiveTagsBySessionStatements, getNullOwnerToolTagsBySessionStatements, getDroppedTagsBySessionStatements, getMaxDroppedTagNumberStatements, newestToolTagNumberStatements, droppedNumberStatements, tagByIdStatements, getToolTagNumberByOwnerStatements, getNullOwnerToolTagStatements, adoptNullOwnerToolTagStatements, getToolOwnerByTagIdStatements, deleteToolTagsByOwnerStatements;
29749
- var init_storage_tags = __esm(() => {
30384
+ var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, getAssignableTagNumberByMessageIdStatements, hasPiFallbackMessageTagStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements, recentTagOwnerStatements, RECLAIM_HINT_EXCLUDED_TOOLS, getActiveToolTagsForAgeReclaimStatements, scopedActiveTokenTotalStatements, updateTagTokenCountStatements, updateTagInputTokenCountStatements, tagOwnerSummaries, TAG_OWNER_COLUMNS = "id, type, message_id, tool_owner_message_id, tag_number, reasoning_token_count", TAG_OWNER_APPENDED_WITH_SHAPE_SQL, inertWhitespaceStatements, foldStatements, getOwnerScopedToolTagNumbersStatements, getMinMessageTagNumberForRawIdStatements, getActiveTagsBySessionStatements, getNullOwnerToolTagsBySessionStatements, getDroppedTagsBySessionStatements, getMaxDroppedTagNumberStatements, newestToolTagNumberStatements, droppedNumberStatements, tagByIdStatements, getToolTagNumberByOwnerStatements, getNullOwnerToolTagStatements, adoptNullOwnerToolTagStatements, getToolOwnerByTagIdStatements, deleteToolTagsByOwnerStatements;
30385
+ var init_storage_tags = __esm(async () => {
29750
30386
  init_emergency_drop();
29751
30387
  init_tag_owner_id();
29752
30388
  init_migration_v95_perf_indexes();
29753
30389
  init_storage_ops();
30390
+ init_storage_tag_identity_revision();
30391
+ await init_sqlite();
29754
30392
  insertTagStatements = new WeakMap;
29755
30393
  updateTagStatusStatements = new WeakMap;
29756
30394
  updateTagDropModeStatements = new WeakMap;
@@ -29778,6 +30416,12 @@ var init_storage_tags = __esm(() => {
29778
30416
  scopedActiveTokenTotalStatements = new WeakMap;
29779
30417
  updateTagTokenCountStatements = new WeakMap;
29780
30418
  updateTagInputTokenCountStatements = new WeakMap;
30419
+ tagOwnerSummaries = new WeakMap;
30420
+ TAG_OWNER_APPENDED_WITH_SHAPE_SQL = `SELECT ${TAG_OWNER_COLUMNS}, 0 AS shape, NULL AS tag_count, NULL AS max_id, NULL AS revision
30421
+ FROM tags WHERE id > ? AND +session_id = ?
30422
+ UNION ALL
30423
+ SELECT NULL, NULL, NULL, NULL, NULL, NULL, 1, COUNT(*), MAX(id), (SELECT value FROM schema_migrations_meta WHERE key = ?)
30424
+ FROM tags WHERE session_id = ?`;
29781
30425
  inertWhitespaceStatements = new WeakMap;
29782
30426
  foldStatements = new WeakMap;
29783
30427
  getOwnerScopedToolTagNumbersStatements = new WeakMap;
@@ -29800,6 +30444,24 @@ var init_storage_tags = __esm(() => {
29800
30444
  var historian_tool_defaults_default;
29801
30445
  var init_historian_tool_defaults = __esm(() => {
29802
30446
  historian_tool_defaults_default = {
30447
+ ctx_note: "Note ${input.action} ${input.note_ids}: ${input.content} → ${output.truncate(160)}",
30448
+ ctx_memory: "Memory ${input.action} ${input.category} ${input.ids}: ${input.content} → ${output.truncate(160)}",
30449
+ todowrite: 'Todos (${input.todos.count}): ${input.todos.each("${status}: ${content}")}',
30450
+ question: 'Asked user: ${input.questions[*].question.join(" / ")} → ${output}',
30451
+ task: "Task ${input.subagent_type}: ${input.prompt.truncate(600)} → ${output.truncate(200)}",
30452
+ ask: 'Asked: ${input.question} [options: ${input.options.join(" / ")}] ${input.text}${input.resolution} → ${output.truncate(300)}',
30453
+ peer_send: "PM ${input.recipient}: ${input.message}",
30454
+ board: 'Board ${input.verb}${input.board_lane}: ${input.ops.each("${op} ${lane_label} ${item}${props.text}${lane.items} ${state}${props.state}${lane.status}${id}${title}")}',
30455
+ room: "Room ${input.action} ${input.room_label}: ${input.room_detail}",
30456
+ work: "Work ${input.action} ${input.title}${input.id}: ${input.prompt.truncate(600)}${input.notes.truncate(600)} → ${output.truncate(160)}",
30457
+ knowhow: "Looked up how-to: ${input.query}${input.id}"
30458
+ };
30459
+ });
30460
+
30461
+ // ../plugin/src/shared/historian-tool-preview-defaults.json
30462
+ var historian_tool_preview_defaults_default;
30463
+ var init_historian_tool_preview_defaults = __esm(() => {
30464
+ historian_tool_preview_defaults_default = {
29803
30465
  ctx_note: "Note ${input.action} ${input.note_ids}: ${input.content} → ${output.truncate(160)}",
29804
30466
  ctx_memory: "Memory ${input.action} ${input.category} ${input.ids}: ${input.content} → ${output.truncate(160)}",
29805
30467
  todowrite: 'Todos (${input.todos.count}): ${input.todos.each("${status}: ${content}")}',
@@ -29815,20 +30477,52 @@ var init_historian_tool_defaults = __esm(() => {
29815
30477
  });
29816
30478
 
29817
30479
  // ../plugin/src/shared/historian-tool-expansions.ts
29818
- function expandToolPart(part, overrides) {
30480
+ function builtinInput(tool, input) {
30481
+ if (!input || typeof input !== "object")
30482
+ return input;
30483
+ const i = input;
30484
+ if (tool === "peer_send") {
30485
+ const external = i.external;
30486
+ const recipient = i.reply_to_pmid ? `reply to ${i.reply_to_pmid}` : i.agent || i.agent_id ? `to ${i.agent || i.agent_id}` : external?.session_id || i.session_id ? `to session ${external?.session_id || i.session_id}` : "to unknown recipient";
30487
+ return { ...i, recipient };
30488
+ }
30489
+ if (tool === "room") {
30490
+ return {
30491
+ ...i,
30492
+ room_label: i.room_id || i.title,
30493
+ room_detail: i.action === "create" || i.action === "invite" ? `invitees: ${renderToolTemplate("${input.invitees}", i)}` : i.text
30494
+ };
30495
+ }
30496
+ if (tool === "board") {
30497
+ return {
30498
+ ...i,
30499
+ board_lane: i.lane ? ` ${i.lane}` : "",
30500
+ ops: Array.isArray(i.ops) ? i.ops.map((op) => op && typeof op === "object" ? {
30501
+ ...op,
30502
+ lane_label: typeof op.lane === "string" ? op.lane : op.lane?.title || op.lane?.id
30503
+ } : op) : i.ops
30504
+ };
30505
+ }
30506
+ return input;
30507
+ }
30508
+ function expandToolPart(part, overrides, legacyPreview = false) {
29819
30509
  if (!part || typeof part !== "object")
29820
30510
  return null;
29821
30511
  const p = part;
29822
30512
  if (p.type !== "tool" || typeof p.tool !== "string")
29823
30513
  return null;
29824
- const template = overrides && Object.hasOwn(overrides, p.tool) ? overrides[p.tool] : Object.hasOwn(historian_tool_defaults_default, p.tool) ? historian_tool_defaults_default[p.tool] : undefined;
30514
+ const builtinDefaults = legacyPreview ? historian_tool_preview_defaults_default : historian_tool_defaults_default;
30515
+ const overridden = overrides && Object.hasOwn(overrides, p.tool);
30516
+ const template = overridden ? overrides[p.tool] : Object.hasOwn(builtinDefaults, p.tool) ? builtinDefaults[p.tool] : undefined;
29825
30517
  if (typeof template !== "string")
29826
30518
  return null;
29827
30519
  const state = p.state;
29828
- return renderToolTemplate(template, state?.input, state?.output ?? state?.error);
30520
+ const input = overridden || legacyPreview ? state?.input : builtinInput(p.tool, state?.input);
30521
+ return renderToolTemplate(template, input, state?.output ?? state?.error, legacyPreview);
29829
30522
  }
29830
30523
  var init_historian_tool_expansions = __esm(() => {
29831
30524
  init_historian_tool_defaults();
30525
+ init_historian_tool_preview_defaults();
29832
30526
  init_historian_tool_template();
29833
30527
  });
29834
30528
 
@@ -29846,92 +30540,28 @@ function isHostUnservedRow(value) {
29846
30540
  }
29847
30541
  var HOST_UNSERVED_ROW = "hostUnservedRow";
29848
30542
 
29849
- // ../plugin/src/hooks/magic-context/read-session-db.ts
29850
- function openCodeDbExists() {
29851
- return harnessOwnsOpenCodeStore() && openCodeDbPathExists(resolveOpenCodeDbPath());
29852
- }
29853
- function closeCachedReadOnlyDb() {
29854
- if (!cachedReadOnlyDb) {
29855
- return;
29856
- }
29857
- try {
29858
- closeQuietly(cachedReadOnlyDb.db);
29859
- } catch (error) {
29860
- log("[magic-context] failed to close cached OpenCode read-only DB:", error);
29861
- } finally {
29862
- cachedReadOnlyDb = null;
29863
- }
29864
- }
29865
- function getReadOnlySessionDb() {
29866
- if (!harnessOwnsOpenCodeStore()) {
29867
- throw new Error("OpenCode session database is not readable from a Pi-compatible process; its history lives in Pi sessions");
29868
- }
29869
- const resolution = resolveOpenCodeDbPath();
29870
- const dbPath = resolution.path;
29871
- if (!openCodeDbPathExists(resolution)) {
29872
- throw new Error(`OpenCode session database is unavailable at ${dbPath} (source=${resolution.source})`);
29873
- }
29874
- if (cachedReadOnlyDb?.path === dbPath) {
29875
- return cachedReadOnlyDb.db;
29876
- }
29877
- closeCachedReadOnlyDb();
29878
- const db = new Database(dbPath, { readonly: true });
29879
- try {
29880
- assertOpenCodeStoreGeneration(db, "v1", dbPath);
29881
- } catch (error) {
29882
- closeQuietly(db);
29883
- throw error;
30543
+ // ../plugin/src/shared/exit-abort-registry.ts
30544
+ function abortAll() {
30545
+ for (const controller of controllers) {
30546
+ try {
30547
+ controller.abort();
30548
+ } catch {}
29884
30549
  }
29885
- cachedReadOnlyDb = { path: dbPath, db };
29886
- clearOpenCodeDbReadFailure();
29887
- return db;
29888
30550
  }
29889
- function withReadOnlySessionDb(fn) {
29890
- return fn(getReadOnlySessionDb());
29891
- }
29892
- function getRawSessionMessageCountFromDb(db, sessionId) {
29893
- const row = db.prepare(`SELECT COUNT(*) as count FROM message WHERE session_id = ?
29894
- AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
29895
- AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')`).get(sessionId);
29896
- return typeof row?.count === "number" ? row.count : 0;
29897
- }
29898
- function logProbeFailureOnce(resolution, error) {
29899
- const failure = recordOpenCodeDbReadFailure(resolution, error);
29900
- if (!claimOpenCodeDbDiagnosticOnce("session-state-probe", resolution))
30551
+ function registerExitAbort(controller) {
30552
+ controllers.add(controller);
30553
+ if (listenerRegistered)
29901
30554
  return;
29902
- const message = `[magic-context] OpenCode DB probe failed: path=${resolution.path} source=${resolution.source} cause=${failure.message}`;
29903
- probeLogObserverForTests?.(message);
29904
- log(message);
30555
+ listenerRegistered = true;
30556
+ process.once("exit", abortAll);
29905
30557
  }
29906
- function getMessageTimesFromOpenCodeDb(sessionId, messageIds) {
29907
- const result = new Map;
29908
- if (messageIds.length === 0 || !harnessOwnsOpenCodeStore())
29909
- return result;
29910
- try {
29911
- withReadOnlySessionDb((db) => {
29912
- const placeholders = messageIds.map(() => "?").join(",");
29913
- const rows = db.prepare(`SELECT id, time_created FROM message WHERE session_id = ? AND id IN (${placeholders})`).all(sessionId, ...messageIds);
29914
- for (const row of rows) {
29915
- if (typeof row.id === "string" && typeof row.time_created === "number") {
29916
- result.set(row.id, row.time_created);
29917
- }
29918
- }
29919
- });
29920
- } catch (error) {
29921
- logProbeFailureOnce(resolveOpenCodeDbPath(), error);
29922
- }
29923
- return result;
29924
- }
29925
- var cachedReadOnlyDb = null, trackedSessions, pendingParts, probeLogObserverForTests;
29926
- var init_read_session_db = __esm(async () => {
29927
- init_logger();
29928
- init_opencode_db_path();
29929
- await init_sqlite();
29930
- trackedSessions = new Map;
29931
- pendingParts = new Map;
30558
+ var controllers, listenerRegistered = false;
30559
+ var init_exit_abort_registry = __esm(() => {
30560
+ controllers = new Set;
29932
30561
  });
29933
30562
 
29934
30563
  // ../plugin/src/hooks/magic-context/read-session-raw.ts
30564
+ import { isMainThread as isMainThread2 } from "node:worker_threads";
29935
30565
  function isRawMessageRow(row) {
29936
30566
  if (row === null || typeof row !== "object")
29937
30567
  return false;
@@ -29991,7 +30621,7 @@ function readRawSessionMessagesFromDb(db, sessionId) {
29991
30621
  if (messageIds.length === 0)
29992
30622
  continue;
29993
30623
  const placeholders = messageIds.map(() => "?").join(", ");
29994
- const partRows = db.prepare(`SELECT message_id, data, time_updated
30624
+ const partRows = db.prepare(`SELECT message_id, ${RAW_PART_DATA_SQL} AS data, time_updated
29995
30625
  FROM part
29996
30626
  WHERE +session_id = ?
29997
30627
  AND likelihood(message_id IN (${placeholders}), 0.000001)
@@ -30023,7 +30653,7 @@ function readRawSessionMessagePageFromDb(db, sessionId, afterOrdinal, limit, fin
30023
30653
  if (messageRows.length === 0)
30024
30654
  return [];
30025
30655
  const placeholders = messageRows.map(() => "?").join(", ");
30026
- const partRows = db.prepare(`SELECT message_id, data, time_updated
30656
+ const partRows = db.prepare(`SELECT message_id, ${RAW_PART_DATA_SQL} AS data, time_updated
30027
30657
  FROM part
30028
30658
  WHERE +session_id = ?
30029
30659
  AND likelihood(message_id IN (${placeholders}), 0.000001)
@@ -30096,6 +30726,9 @@ function readRawSessionMessageSummaryPageFromDb(db, sessionId, afterOrdinal, lim
30096
30726
  return assembleRawMessagePage(messageRows, partRows);
30097
30727
  }
30098
30728
  function countRawSessionMessageOrdinalsFromDb(db, sessionId) {
30729
+ const indexed = countCanonicalOrdinalsIndexed(db, sessionId, null, null);
30730
+ if (indexed !== null)
30731
+ return indexed;
30099
30732
  const row = db.prepare(`SELECT COUNT(*) AS count
30100
30733
  FROM message
30101
30734
  WHERE session_id = ?
@@ -30182,7 +30815,7 @@ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
30182
30815
  for (let i = 0;i < ids.length; i += CHUNK) {
30183
30816
  const slice = ids.slice(i, i + CHUNK);
30184
30817
  const placeholders = slice.map(() => "?").join(",");
30185
- const partRows = db.prepare(`SELECT message_id, data, time_updated FROM part WHERE +session_id = ? AND likelihood(message_id IN (${placeholders}), 0.000001) ORDER BY time_created ASC, id ASC`).all(sessionId, ...slice).filter(isRawPartRow);
30818
+ const partRows = db.prepare(`SELECT message_id, ${RAW_PART_DATA_SQL} AS data, time_updated FROM part WHERE +session_id = ? AND likelihood(message_id IN (${placeholders}), 0.000001) ORDER BY time_created ASC, id ASC`).all(sessionId, ...slice).filter(isRawPartRow);
30186
30819
  for (const part of partRows) {
30187
30820
  const list = partsByMessageId.get(part.message_id) ?? [];
30188
30821
  list.push(attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated));
@@ -30228,49 +30861,431 @@ function readRawSessionMessagePartsByIdFromDb(db, sessionId, messageId, onQuery)
30228
30861
  version: row.time_updated ?? null
30229
30862
  };
30230
30863
  }
30231
- function readRawSessionMessageOrdinalByIdFromDb(db, sessionId, messageId) {
30232
- const row = db.prepare(`SELECT COUNT(candidate.id) AS ordinal
30233
- FROM message AS target
30234
- JOIN message AS candidate
30235
- ON candidate.session_id = target.session_id
30236
- AND NOT (
30237
- CASE WHEN json_valid(candidate.data) = 1
30238
- THEN COALESCE(json_extract(candidate.data, '$.summary'), 0)
30239
- ELSE 0 END = 1
30240
- AND CASE WHEN json_valid(candidate.data) = 1
30241
- THEN COALESCE(json_extract(candidate.data, '$.finish'), '')
30242
- ELSE '' END = 'stop'
30243
- )
30244
- AND (candidate.time_created < target.time_created
30245
- OR (candidate.time_created = target.time_created AND candidate.id <= target.id))
30246
- WHERE target.session_id = ?
30247
- AND target.id = ?
30248
- AND NOT (
30249
- CASE WHEN json_valid(target.data) = 1
30250
- THEN COALESCE(json_extract(target.data, '$.summary'), 0)
30251
- ELSE 0 END = 1
30252
- AND CASE WHEN json_valid(target.data) = 1
30253
- THEN COALESCE(json_extract(target.data, '$.finish'), '')
30254
- ELSE '' END = 'stop'
30255
- )`).get(sessionId, messageId);
30256
- const ordinal = row?.ordinal;
30257
- return typeof ordinal === "number" && ordinal > 0 ? ordinal : null;
30864
+ function ordinalRangeSql(lowerBound) {
30865
+ return `SELECT COUNT(*) AS visited,
30866
+ COALESCE(SUM(CASE WHEN ${ORDINAL_SUMMARY_FLAG_SQL} AND ${ORDINAL_STOP_FLAG_SQL}
30867
+ THEN 0 ELSE 1 END), 0) AS eligible
30868
+ FROM message
30869
+ WHERE session_id = ?
30870
+ ${lowerBound ? "AND (time_created, id) > (?, ?)" : ""}
30871
+ AND (time_created, id) <= (?, ?)`;
30258
30872
  }
30259
- function readRawSessionMessageByIdFromDb(db, sessionId, messageId) {
30260
- const row = db.prepare("SELECT id, data, time_created, time_updated FROM message WHERE session_id = ? AND id = ?").get(sessionId, messageId);
30261
- if (!row || !isRawMessageRow(row) || typeof row.time_created !== "number") {
30873
+ function readStoreStamp(db) {
30874
+ try {
30875
+ const row = db.prepare(`SELECT data_version AS dataVersion, total_changes() AS totalChanges,
30876
+ (SELECT schema_version FROM pragma_schema_version) AS schemaVersion
30877
+ FROM pragma_data_version`).get();
30878
+ if (typeof row?.dataVersion !== "number" || typeof row.totalChanges !== "number" || typeof row.schemaVersion !== "number")
30879
+ return null;
30880
+ return {
30881
+ dataVersion: row.dataVersion,
30882
+ totalChanges: row.totalChanges,
30883
+ schemaVersion: row.schemaVersion
30884
+ };
30885
+ } catch {
30262
30886
  return null;
30263
30887
  }
30264
- const info = parseJsonRecord(row.data);
30265
- if (!info || isRawCompactionSummaryInfo(info)) {
30888
+ }
30889
+ function sameStoreStamp(left, right) {
30890
+ return left !== null && right !== null && left.dataVersion === right.dataVersion && left.totalChanges === right.totalChanges && left.schemaVersion === right.schemaVersion;
30891
+ }
30892
+ function compareOrdinalKeys(left, right) {
30893
+ if (left.timeCreated !== right.timeCreated)
30894
+ return left.timeCreated < right.timeCreated ? -1 : 1;
30895
+ if (left.id === right.id)
30896
+ return 0;
30897
+ return Buffer.compare(Buffer.from(left.id, "utf8"), Buffer.from(right.id, "utf8"));
30898
+ }
30899
+ function countEligibleOrdinals(db, sessionId, after, through) {
30900
+ const indexed = countCanonicalOrdinalsIndexed(db, sessionId, after, through);
30901
+ if (indexed !== null)
30902
+ return indexed;
30903
+ const row = after ? db.prepare(ORDINAL_RANGE_BETWEEN_SQL).get(sessionId, after.timeCreated, after.id, through.timeCreated, through.id) : db.prepare(ORDINAL_RANGE_FROM_START_SQL).get(sessionId, through.timeCreated, through.id);
30904
+ const visited = typeof row?.visited === "number" ? row.visited : 0;
30905
+ ordinalRowsVisited += visited;
30906
+ ordinalJsonRowsRead += visited;
30907
+ return typeof row?.eligible === "number" ? row.eligible : 0;
30908
+ }
30909
+ function survivingAnchor(db, set) {
30910
+ if (set.anchors.length === 0)
30266
30911
  return null;
30912
+ const current = new Map(db.prepare(ANCHOR_CHECK_SQL).all(JSON.stringify(set.anchors.map((anchor) => anchor.rowid))).map((row) => [row.rid, row.id]));
30913
+ return set.anchors.find((anchor) => current.get(anchor.rowid) === anchor.id);
30914
+ }
30915
+ function currentOrdinalCheckpoints(db, sessionId) {
30916
+ const cached = ordinalCheckpoints.get(db)?.get(sessionId);
30917
+ if (!cached)
30918
+ return;
30919
+ return withReadSnapshot(db, () => {
30920
+ if (!extendOrdinalCheckpoints(db, sessionId, cached.scan, ORDINAL_LOOKUP_LIMIT))
30921
+ return;
30922
+ cached.epoch = getRawSessionSummaryEpoch(sessionId);
30923
+ return cached;
30924
+ });
30925
+ }
30926
+ function extendOrdinalCheckpoints(db, sessionId, scan, limit) {
30927
+ const points = scan.checkpoints ?? [];
30928
+ const exists = (point) => Boolean(db.prepare("SELECT 1 FROM message WHERE id=? AND session_id=? AND time_created=?").get(point.id, sessionId, point.timeCreated));
30929
+ if (points.length && !exists(points[points.length - 1])) {
30930
+ let low = 0;
30931
+ let high = points.length;
30932
+ while (low < high) {
30933
+ const middle = Math.floor((low + high) / 2);
30934
+ if (exists(points[middle]))
30935
+ low = middle + 1;
30936
+ else
30937
+ high = middle;
30938
+ }
30939
+ points.length = low;
30940
+ scan.total = points.at(-1)?.ordinal ?? 0;
30941
+ scan.sessionRows = points.at(-1)?.storedCount ?? 0;
30942
+ }
30943
+ const tail = points.at(-1);
30944
+ const rows = db.prepare(`SELECT id, time_created AS timeCreated, ${EXCLUDED_ROW_SQL} AS excluded FROM message
30945
+ WHERE session_id=?${tail ? " AND (time_created,id) > (?,?)" : ""}
30946
+ ORDER BY time_created,id${limit === undefined ? "" : ` LIMIT ${limit + 1}`}`).all(sessionId, ...tail ? [tail.timeCreated, tail.id] : []);
30947
+ ordinalJsonRowsRead += rows.length;
30948
+ if (limit !== undefined && rows.length > limit)
30949
+ return false;
30950
+ let total = scan.total ?? 0;
30951
+ if (rows.length && tail && (tail.storedCount ?? 0) % 256 !== 0)
30952
+ points.pop();
30953
+ for (let index = 0;index < rows.length; index++) {
30954
+ const row = rows[index];
30955
+ if (row.excluded !== 1)
30956
+ total++;
30957
+ const storedCount = scan.sessionRows + index + 1;
30958
+ if (storedCount % 256 === 0 || index === rows.length - 1)
30959
+ points.push({ id: row.id, timeCreated: row.timeCreated, ordinal: total, storedCount });
30960
+ }
30961
+ scan.checkpoints = points;
30962
+ scan.sessionRows += rows.length;
30963
+ scan.total = total;
30964
+ return true;
30965
+ }
30966
+ function rawOrdinalSummarySeed(db, sessionId) {
30967
+ const retained = ordinalCheckpoints.get(db)?.get(sessionId);
30968
+ if (retained)
30969
+ return retained.scan;
30970
+ const seed = ordinalSummarySeeds.get(db)?.get(sessionId);
30971
+ if (!seed || seed.epoch !== getRawSessionSummaryEpoch(sessionId))
30972
+ return;
30973
+ return {
30974
+ ...seed.scan,
30975
+ ids: [...new Set([...seed.scan.ids, ...writtenSummaryIds.get(sessionId) ?? []])]
30976
+ };
30977
+ }
30978
+ function boundedOrdinalPrefix(db, sessionId, through) {
30979
+ const cached = currentOrdinalCheckpoints(db, sessionId);
30980
+ if (cached && through === null)
30981
+ return cached.scan.total ?? 0;
30982
+ let floor;
30983
+ if (cached && through) {
30984
+ const points = cached.scan.checkpoints ?? [];
30985
+ let low = 0;
30986
+ let high = points.length;
30987
+ while (low < high) {
30988
+ const middle = Math.floor((low + high) / 2);
30989
+ if (compareOrdinalKeys(points[middle], through) <= 0)
30990
+ low = middle + 1;
30991
+ else
30992
+ high = middle;
30993
+ }
30994
+ floor = points[low - 1];
30995
+ if (floor && compareOrdinalKeys(floor, through) === 0)
30996
+ return floor.ordinal;
30997
+ }
30998
+ const bindings = [sessionId];
30999
+ if (floor)
31000
+ bindings.push(floor.timeCreated, floor.id);
31001
+ if (through)
31002
+ bindings.push(through.timeCreated, through.id);
31003
+ const rows = db.prepare(`SELECT ${EXCLUDED_ROW_SQL} AS excluded FROM message WHERE id IN (
31004
+ SELECT id FROM message WHERE session_id = ?${floor ? " AND (time_created, id) > (?, ?)" : ""}${through ? " AND (time_created, id) <= (?, ?)" : ""}
31005
+ ORDER BY time_created, id LIMIT ${ORDINAL_LOOKUP_LIMIT + 1})`).all(...bindings);
31006
+ ordinalRowsVisited += rows.length;
31007
+ ordinalJsonRowsRead += rows.length;
31008
+ if (rows.length > ORDINAL_LOOKUP_LIMIT) {
31009
+ prewarmRawSessionOrdinalsForDb(db, sessionId);
31010
+ throw new RawOrdinalsNotReady;
31011
+ }
31012
+ return (floor?.ordinal ?? 0) + rows.reduce((sum, row) => sum + (row.excluded === 1 ? 0 : 1), 0);
31013
+ }
31014
+ function scanRawSessionSummaryRows(db, sessionId, previous) {
31015
+ return withReadSnapshot(db, () => {
31016
+ if (previous?.checkpoints) {
31017
+ extendOrdinalCheckpoints(db, sessionId, previous);
31018
+ return previous;
31019
+ }
31020
+ if (previous) {
31021
+ const sessions = summaryCandidateSets.get(db) ?? new Map;
31022
+ sessions.set(sessionId, {
31023
+ epoch: getRawSessionSummaryEpoch(sessionId),
31024
+ anchors: previous.anchors,
31025
+ ids: new Set(previous.ids)
31026
+ });
31027
+ summaryCandidateSets.set(db, sessions);
31028
+ }
31029
+ const incremental = previous && isRawSessionSummaryWarm(db, sessionId) ? summaryCandidatesOf(db, sessionId) : undefined;
31030
+ const rows = incremental ? [] : db.prepare(SUMMARY_FULL_SCAN_SQL).all(sessionId, sessionId);
31031
+ const anchors = [];
31032
+ const ids = [];
31033
+ let sessionRows = 0;
31034
+ for (const row of rows) {
31035
+ if (row.kind === 1 && typeof row.rid === "number" && typeof row.id === "string")
31036
+ anchors.push({ rowid: row.rid, id: row.id });
31037
+ else if (row.kind === 2 && typeof row.id === "string")
31038
+ ids.push(row.id);
31039
+ else if (row.kind === 4 && typeof row.rid === "number")
31040
+ sessionRows = row.rid;
31041
+ }
31042
+ if (incremental) {
31043
+ ids.push(...incremental.ids);
31044
+ anchors.push(...summaryCandidateSets.get(db)?.get(sessionId)?.anchors ?? []);
31045
+ sessionRows = countStoredRawSessionRowsIndexed(db, sessionId);
31046
+ }
31047
+ anchors.sort((left, right) => right.rowid - left.rowid);
31048
+ anchors.length = Math.min(anchors.length, SUMMARY_ANCHOR_LIMIT);
31049
+ const keys = db.prepare(`SELECT id, time_created AS timeCreated, ${EXCLUDED_ROW_SQL} AS excluded FROM message WHERE session_id = ?
31050
+ ORDER BY time_created, id`).all(sessionId);
31051
+ const checkpoints = [];
31052
+ let total = 0;
31053
+ for (let index = 0;index < keys.length; index++) {
31054
+ const key = keys[index];
31055
+ if (key.excluded !== 1)
31056
+ total++;
31057
+ if (index % 256 === 255 || index === keys.length - 1)
31058
+ checkpoints.push({
31059
+ id: key.id,
31060
+ timeCreated: key.timeCreated,
31061
+ ordinal: total,
31062
+ storedCount: index + 1
31063
+ });
31064
+ }
31065
+ return { anchors, ids, sessionRows, checkpoints, total };
31066
+ });
31067
+ }
31068
+ function getRawSessionSummaryEpoch(sessionId) {
31069
+ return summaryCandidateEpochs.get(sessionId) ?? 0;
31070
+ }
31071
+ function installRawSessionSummaryScan(db, sessionId, scan, epoch, workerPrepared = false) {
31072
+ if (epoch !== getRawSessionSummaryEpoch(sessionId))
31073
+ return false;
31074
+ const seeds = ordinalSummarySeeds.get(db) ?? new Map;
31075
+ seeds.set(sessionId, { epoch, scan });
31076
+ ordinalSummarySeeds.set(db, seeds);
31077
+ if (scan.checkpoints && workerPrepared) {
31078
+ const counts = ordinalCheckpoints.get(db) ?? new Map;
31079
+ counts.set(sessionId, { epoch, scan });
31080
+ ordinalCheckpoints.set(db, counts);
31081
+ } else if (getSqliteDatabasePath(db) && isMainThread2)
31082
+ return false;
31083
+ if (isRawSessionSummaryWarm(db, sessionId))
31084
+ return true;
31085
+ let sessions = summaryCandidateSets.get(db);
31086
+ if (!sessions) {
31087
+ sessions = new Map;
31088
+ summaryCandidateSets.set(db, sessions);
30267
31089
  }
30268
- const ordinalRow = db.prepare(`SELECT COUNT(*) AS ordinal FROM message
30269
- WHERE session_id = ?
30270
- AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
30271
- AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')
30272
- AND (time_created < ? OR (time_created = ? AND id <= ?))`).get(sessionId, row.time_created, row.time_created, messageId);
30273
- const ordinal = typeof ordinalRow?.ordinal === "number" ? ordinalRow.ordinal : 0;
31090
+ sessions.set(sessionId, { epoch, anchors: [...scan.anchors], ids: new Set(scan.ids) });
31091
+ return isRawSessionSummaryWarm(db, sessionId);
31092
+ }
31093
+ function isRawSessionSummaryWarm(db, sessionId) {
31094
+ if (isMainThread2 && getSqliteDatabasePath(db)) {
31095
+ return Boolean(currentOrdinalCheckpoints(db, sessionId));
31096
+ }
31097
+ const set = summaryCandidateSets.get(db)?.get(sessionId);
31098
+ if (!set || set.epoch !== getRawSessionSummaryEpoch(sessionId))
31099
+ return false;
31100
+ try {
31101
+ return survivingAnchor(db, set) !== undefined;
31102
+ } catch {
31103
+ return false;
31104
+ }
31105
+ }
31106
+ function countStoredRawSessionRowsIndexed(db, sessionId) {
31107
+ const row = db.prepare("SELECT COUNT(*) AS count FROM message WHERE session_id = ?").get(sessionId);
31108
+ return typeof row?.count === "number" ? row.count : 0;
31109
+ }
31110
+ function summaryCandidatesOf(db, sessionId) {
31111
+ let sessions = summaryCandidateSets.get(db);
31112
+ if (!sessions) {
31113
+ sessions = new Map;
31114
+ summaryCandidateSets.set(db, sessions);
31115
+ }
31116
+ const epoch = getRawSessionSummaryEpoch(sessionId);
31117
+ const cached = sessions.get(sessionId);
31118
+ const floor = cached && cached.epoch === epoch ? survivingAnchor(db, cached) : undefined;
31119
+ let ids;
31120
+ let anchors;
31121
+ if (floor === undefined) {
31122
+ const scan = scanRawSessionSummaryRows(db, sessionId);
31123
+ summaryFullScans += 1;
31124
+ ordinalJsonRowsRead += scan.sessionRows;
31125
+ ids = new Set(scan.ids);
31126
+ anchors = scan.anchors;
31127
+ } else {
31128
+ const rows = db.prepare(SUMMARY_SCAN_AFTER_SQL).all(floor?.rowid ?? 0, sessionId);
31129
+ ids = cached?.ids ?? new Set;
31130
+ anchors = [];
31131
+ for (const row of rows) {
31132
+ if (row.kind === 1 && typeof row.rid === "number" && typeof row.id === "string")
31133
+ anchors.push({ rowid: row.rid, id: row.id });
31134
+ else if (row.kind === 2 && typeof row.id === "string")
31135
+ ids.add(row.id);
31136
+ if (row.kind === 2 || row.kind === 3)
31137
+ ordinalJsonRowsRead += 1;
31138
+ }
31139
+ anchors.sort((left, right) => right.rowid - left.rowid);
31140
+ anchors.length = Math.min(anchors.length, SUMMARY_ANCHOR_LIMIT);
31141
+ }
31142
+ for (const id of writtenSummaryIds.get(sessionId) ?? [])
31143
+ ids.add(id);
31144
+ sessions.set(sessionId, { epoch, anchors, ids });
31145
+ const top = anchors[0];
31146
+ return { ids: [...ids], floorRowid: top?.rowid ?? 0, floorId: top?.id ?? null };
31147
+ }
31148
+ function canonicalCountSql(lower, upper) {
31149
+ const range = `${lower ? " AND (time_created, id) > (?, ?)" : ""}${upper ? " AND (time_created, id) <= (?, ?)" : ""}`;
31150
+ return `SELECT
31151
+ (SELECT COUNT(*) FROM message WHERE session_id = ?${range}) AS stored,
31152
+ (SELECT COUNT(*) FROM message
31153
+ WHERE id IN (SELECT value FROM json_each(?)) AND +session_id = ?${range}
31154
+ AND ${EXCLUDED_ROW_SQL}) AS knownExcluded,
31155
+ (SELECT COUNT(*) FROM message
31156
+ WHERE rowid > ? AND +session_id = ? AND id NOT IN (SELECT value FROM json_each(?))${range}
31157
+ AND ${EXCLUDED_ROW_SQL}) AS newExcluded,
31158
+ (SELECT id FROM message WHERE rowid = ?) AS floorId`;
31159
+ }
31160
+ function canonicalCountStatementText(lower, upper) {
31161
+ const key = `${lower}:${upper}`;
31162
+ let sql = CANONICAL_COUNT_SQL.get(key);
31163
+ if (!sql) {
31164
+ sql = canonicalCountSql(lower, upper);
31165
+ CANONICAL_COUNT_SQL.set(key, sql);
31166
+ }
31167
+ return sql;
31168
+ }
31169
+ function countCanonicalOrdinalsIndexed(db, sessionId, after, through) {
31170
+ if (isMainThread2 && getSqliteDatabasePath(db)) {
31171
+ return withReadSnapshot(db, () => boundedOrdinalPrefix(db, sessionId, through) - (after ? boundedOrdinalPrefix(db, sessionId, after) : 0));
31172
+ }
31173
+ if (indexedCountUnsupported.has(db))
31174
+ return null;
31175
+ const range = [];
31176
+ if (after)
31177
+ range.push(after.timeCreated, after.id);
31178
+ if (through)
31179
+ range.push(through.timeCreated, through.id);
31180
+ try {
31181
+ return withReadSnapshot(db, () => {
31182
+ for (let attempt = 0;attempt < STABLE_STORE_ATTEMPTS; attempt += 1) {
31183
+ const view = summaryCandidatesOf(db, sessionId);
31184
+ const ids = JSON.stringify(view.ids);
31185
+ const row = db.prepare(canonicalCountStatementText(after !== null, through !== null)).get(sessionId, ...range, ids, sessionId, ...range, view.floorRowid, sessionId, ids, ...range, view.floorRowid);
31186
+ if (view.floorId !== null && row?.floorId !== view.floorId)
31187
+ continue;
31188
+ const stored = typeof row?.stored === "number" ? row.stored : 0;
31189
+ const excluded = (typeof row?.knownExcluded === "number" ? row.knownExcluded : 0) + (typeof row?.newExcluded === "number" ? row.newExcluded : 0);
31190
+ ordinalRowsVisited += stored;
31191
+ ordinalJsonRowsRead += view.ids.length;
31192
+ return stored - excluded;
31193
+ }
31194
+ return null;
31195
+ });
31196
+ } catch (error) {
31197
+ if (/no such (column|table|function)|json_each/i.test(String(error))) {
31198
+ indexedCountUnsupported.add(db);
31199
+ return null;
31200
+ }
31201
+ throw error;
31202
+ }
31203
+ }
31204
+ function connectionHasOpenTransaction(db) {
31205
+ const state = db;
31206
+ return state.inTransaction === true || state.isTransaction === true;
31207
+ }
31208
+ function withReadSnapshot(db, read) {
31209
+ if (connectionHasOpenTransaction(db))
31210
+ return read();
31211
+ db.exec("BEGIN");
31212
+ try {
31213
+ return read();
31214
+ } finally {
31215
+ if (connectionHasOpenTransaction(db))
31216
+ db.exec("ROLLBACK");
31217
+ }
31218
+ }
31219
+ function reusableWatermark(db, sessionId, stamp) {
31220
+ const watermark = ordinalWatermarks.get(db)?.get(sessionId);
31221
+ if (!watermark || watermark.epoch !== (ordinalWatermarkEpochs.get(sessionId) ?? 0) || !sameStoreStamp(watermark.stamp, stamp))
31222
+ return null;
31223
+ return watermark;
31224
+ }
31225
+ function rememberOrdinalWatermark(db, sessionId, key, ordinal, stamp) {
31226
+ let sessions = ordinalWatermarks.get(db);
31227
+ if (!sessions) {
31228
+ sessions = new Map;
31229
+ ordinalWatermarks.set(db, sessions);
31230
+ }
31231
+ const current = sessions.get(sessionId);
31232
+ if (current && sameStoreStamp(current.stamp, stamp) && compareOrdinalKeys(current, key) >= 0)
31233
+ return;
31234
+ sessions.set(sessionId, {
31235
+ timeCreated: key.timeCreated,
31236
+ id: key.id,
31237
+ ordinal,
31238
+ stamp,
31239
+ epoch: ordinalWatermarkEpochs.get(sessionId) ?? 0
31240
+ });
31241
+ }
31242
+ function canonicalOrdinalOf(db, sessionId, readTarget) {
31243
+ for (let attempt = 1;; attempt += 1) {
31244
+ const before = readStoreStamp(db);
31245
+ const target = readTarget();
31246
+ if (!target)
31247
+ return null;
31248
+ const watermark = attempt < STABLE_STORE_ATTEMPTS ? reusableWatermark(db, sessionId, before) : null;
31249
+ if (!watermark) {
31250
+ const ordinal = countEligibleOrdinals(db, sessionId, null, target);
31251
+ if (before && sameStoreStamp(before, readStoreStamp(db)))
31252
+ rememberOrdinalWatermark(db, sessionId, target, ordinal, before);
31253
+ return { target, ordinal };
31254
+ }
31255
+ const order = compareOrdinalKeys(target, watermark);
31256
+ const ordinal = order === 0 ? watermark.ordinal : order < 0 ? watermark.ordinal - countEligibleOrdinals(db, sessionId, target, watermark) : watermark.ordinal + countEligibleOrdinals(db, sessionId, watermark, target);
31257
+ if (sameStoreStamp(before, readStoreStamp(db))) {
31258
+ rememberOrdinalWatermark(db, sessionId, target, ordinal, before);
31259
+ return { target, ordinal };
31260
+ }
31261
+ }
31262
+ }
31263
+ function readRawSessionMessageOrdinalByIdFromDb(db, sessionId, messageId) {
31264
+ const counted = canonicalOrdinalOf(db, sessionId, () => {
31265
+ const row = db.prepare(`SELECT id, time_created, ${ORDINAL_SUMMARY_FLAG_SQL} AND ${ORDINAL_STOP_FLAG_SQL} AS excluded
31266
+ FROM message WHERE session_id = ? AND id = ?`).get(sessionId, messageId);
31267
+ if (typeof row?.id !== "string" || typeof row.time_created !== "number")
31268
+ return null;
31269
+ return { id: row.id, timeCreated: row.time_created, excluded: row.excluded === 1 };
31270
+ });
31271
+ if (!counted || counted.target.excluded)
31272
+ return null;
31273
+ return counted.ordinal > 0 ? counted.ordinal : null;
31274
+ }
31275
+ function readRawSessionMessageByIdFromDb(db, sessionId, messageId) {
31276
+ const counted = canonicalOrdinalOf(db, sessionId, () => {
31277
+ const row = db.prepare("SELECT id, data, time_created, time_updated FROM message WHERE session_id = ? AND id = ?").get(sessionId, messageId);
31278
+ if (!row || !isRawMessageRow(row) || typeof row.time_created !== "number")
31279
+ return null;
31280
+ const parsed = parseJsonRecord(row.data);
31281
+ if (!parsed || isRawCompactionSummaryInfo(parsed))
31282
+ return null;
31283
+ return { ...row, timeCreated: row.time_created, info: parsed };
31284
+ });
31285
+ if (!counted)
31286
+ return null;
31287
+ const { target: row, ordinal } = counted;
31288
+ const info = row.info;
30274
31289
  if (ordinal <= 0) {
30275
31290
  return null;
30276
31291
  }
@@ -30334,8 +31349,34 @@ function readRawSeedTailFromDb(db, sessionId, boundaryId) {
30334
31349
  throw new Error("state_sync materialized boundary is missing from raw storage");
30335
31350
  return messages;
30336
31351
  }
30337
- var RAW_MESSAGE_PARTS_BY_ID_SQL = "SELECT message_id, data, time_updated FROM part WHERE +session_id = ? AND likelihood(message_id = ?, 0.000001) ORDER BY time_created ASC, id ASC", RAW_SUMMARY_TEXT_MAX_CHARS = 8192, RAW_SUMMARY_ARG_MAX_CHARS = 512, RAW_SUMMARY_TOOL_INPUT_KEYS, RAW_SUMMARY_PART_DATA_SQL;
30338
- var init_read_session_raw = __esm(() => {
31352
+ var RAW_MESSAGE_PARTS_BY_ID_SQL = "SELECT message_id, data, time_updated FROM part WHERE +session_id = ? AND likelihood(message_id = ?, 0.000001) ORDER BY time_created ASC, id ASC", RAW_METADATA_KEEP_CHARS = 16384, RAW_METADATA_KEPT_KEYS, RAW_PART_DATA_SQL, RAW_SUMMARY_TEXT_MAX_CHARS = 8192, RAW_SUMMARY_ARG_MAX_CHARS = 512, RAW_SUMMARY_TOOL_INPUT_KEYS, RAW_SUMMARY_PART_DATA_SQL, ORDINAL_SUMMARY_FLAG_SQL = `(CASE WHEN json_valid(data) = 1
31353
+ THEN COALESCE(json_extract(data, '$.summary'), 0)
31354
+ ELSE 0 END = 1)`, ORDINAL_STOP_FLAG_SQL = `(CASE WHEN json_valid(data) = 1
31355
+ THEN COALESCE(json_extract(data, '$.finish'), '')
31356
+ ELSE '' END = 'stop')`, ORDINAL_RANGE_FROM_START_SQL, ORDINAL_RANGE_BETWEEN_SQL, ordinalWatermarks, ordinalWatermarkEpochs, ordinalRowsVisited = 0, STABLE_STORE_ATTEMPTS = 3, EXCLUDED_ROW_SQL, SUMMARY_ANCHOR_SPAN = 256, SUMMARY_ANCHOR_LIMIT = 64, summaryCandidateSets, summaryCandidateEpochs, writtenSummaryIds, indexedCountUnsupported, ordinalJsonRowsRead = 0, summaryFullScans = 0, ANCHOR_ROWS_SQL, SUMMARY_FULL_SCAN_SQL, SUMMARY_SCAN_AFTER_SQL, ANCHOR_CHECK_SQL = "SELECT rowid AS rid, id FROM message WHERE rowid IN (SELECT value FROM json_each(?))", ordinalCheckpoints, ordinalSummarySeeds, ORDINAL_LOOKUP_LIMIT = 512, RawOrdinalsNotReady, CANONICAL_COUNT_SQL;
31357
+ var init_read_session_raw = __esm(async () => {
31358
+ await __promiseAll([
31359
+ init_sqlite(),
31360
+ init_raw_ordinal_warmup()
31361
+ ]);
31362
+ RAW_METADATA_KEPT_KEYS = [
31363
+ "description",
31364
+ "title",
31365
+ "userAnswer",
31366
+ "answers",
31367
+ "answer",
31368
+ "selectedOptions",
31369
+ "customInput",
31370
+ "results"
31371
+ ];
31372
+ RAW_PART_DATA_SQL = `CASE
31373
+ WHEN json_valid(data) = 1
31374
+ AND json_type(data, '$.state.metadata') = 'object'
31375
+ AND COALESCE(json_extract(data, '$.state.status'), '') <> 'error'
31376
+ AND length(json_extract(data, '$.state.metadata')) > ${RAW_METADATA_KEEP_CHARS}
31377
+ THEN json_set(data, '$.state.metadata', json_patch('{}', json_object(${RAW_METADATA_KEPT_KEYS.map((key) => `'${key}', json_extract(data, '$.state.metadata.${key}')`).join(", ")})))
31378
+ ELSE data
31379
+ END`;
30339
31380
  RAW_SUMMARY_TOOL_INPUT_KEYS = [
30340
31381
  "description",
30341
31382
  "filePath",
@@ -30360,6 +31401,268 @@ var init_read_session_raw = __esm(() => {
30360
31401
  )
30361
31402
  )
30362
31403
  END`;
31404
+ ORDINAL_RANGE_FROM_START_SQL = ordinalRangeSql(false);
31405
+ ORDINAL_RANGE_BETWEEN_SQL = ordinalRangeSql(true);
31406
+ ordinalWatermarks = new WeakMap;
31407
+ ordinalWatermarkEpochs = new Map;
31408
+ EXCLUDED_ROW_SQL = `${ORDINAL_SUMMARY_FLAG_SQL} AND ${ORDINAL_STOP_FLAG_SQL}`;
31409
+ summaryCandidateSets = new WeakMap;
31410
+ summaryCandidateEpochs = new Map;
31411
+ writtenSummaryIds = new Map;
31412
+ indexedCountUnsupported = new WeakSet;
31413
+ ANCHOR_ROWS_SQL = `SELECT rowid AS rid, id, 1 AS kind FROM message
31414
+ WHERE rowid > (SELECT MAX(rowid) FROM message) - ${SUMMARY_ANCHOR_SPAN}`;
31415
+ SUMMARY_FULL_SCAN_SQL = `${ANCHOR_ROWS_SQL}
31416
+ UNION ALL
31417
+ SELECT rowid, id, 2 FROM message WHERE session_id = ? AND ${ORDINAL_SUMMARY_FLAG_SQL}
31418
+ UNION ALL
31419
+ SELECT COUNT(*), NULL, 4 FROM message WHERE session_id = ?`;
31420
+ SUMMARY_SCAN_AFTER_SQL = `${ANCHOR_ROWS_SQL}
31421
+ UNION ALL
31422
+ SELECT rowid, id, CASE WHEN ${ORDINAL_SUMMARY_FLAG_SQL} THEN 2 ELSE 3 END
31423
+ FROM message WHERE rowid > ? AND +session_id = ?`;
31424
+ ordinalCheckpoints = new WeakMap;
31425
+ ordinalSummarySeeds = new WeakMap;
31426
+ RawOrdinalsNotReady = class RawOrdinalsNotReady extends Error {
31427
+ constructor() {
31428
+ super("canonical ordinals are warming off-thread; retry after preparation");
31429
+ }
31430
+ };
31431
+ CANONICAL_COUNT_SQL = new Map;
31432
+ });
31433
+
31434
+ // ../plugin/src/hooks/magic-context/raw-ordinal-warmup.ts
31435
+ import { Worker } from "node:worker_threads";
31436
+ function stopRawSessionOrdinalWarmups() {
31437
+ for (const stop of [...activeWorkers.values()])
31438
+ stop("stopped");
31439
+ }
31440
+ function defaultEntry() {
31441
+ return new URL(new URL(import.meta.url).pathname.endsWith(".ts") ? "./raw-ordinal-warmup-worker.ts" : "./raw-ordinal-warmup-worker.js", import.meta.url);
31442
+ }
31443
+ function ensureExitHook() {
31444
+ if (exitHookRegistered)
31445
+ return;
31446
+ exitHookRegistered = true;
31447
+ const controller = new AbortController;
31448
+ controller.signal.addEventListener("abort", stopRawSessionOrdinalWarmups, { once: true });
31449
+ registerExitAbort(controller);
31450
+ }
31451
+ function runSessionWarmupWorker(input, options) {
31452
+ ensureExitHook();
31453
+ return new Promise((resolve) => {
31454
+ let worker;
31455
+ let deadline;
31456
+ let settled = false;
31457
+ const finish = (result) => {
31458
+ if (settled)
31459
+ return;
31460
+ settled = true;
31461
+ clearTimeout(deadline);
31462
+ if (worker) {
31463
+ activeWorkers.delete(worker);
31464
+ const stopped = worker.terminate();
31465
+ worker.unref();
31466
+ stopped.finally(() => {
31467
+ releaseIndexOwner(input);
31468
+ resolve(result);
31469
+ });
31470
+ return;
31471
+ }
31472
+ releaseIndexOwner(input);
31473
+ resolve(result);
31474
+ };
31475
+ const arm = (failure, ms) => {
31476
+ clearTimeout(deadline);
31477
+ deadline = setTimeout(() => finish({ failure, detail: `no reply within ${ms} ms` }), ms);
31478
+ deadline.unref?.();
31479
+ };
31480
+ try {
31481
+ worker = new Worker(options.entry ?? defaultEntry(), { workerData: input });
31482
+ } catch (error) {
31483
+ finish({ failure: "start-failed", detail: String(error) });
31484
+ return;
31485
+ }
31486
+ worker.unref();
31487
+ activeWorkers.set(worker, (failure) => finish({ failure, detail: "stopped" }));
31488
+ arm("not-accepted", options.acceptMs ?? ACCEPT_TIMEOUT_MS);
31489
+ worker.on("message", (reply) => {
31490
+ if (reply.kind === "accepted")
31491
+ arm("no-result", options.resultMs ?? RESULT_TIMEOUT_MS);
31492
+ else if (reply.kind === "scan")
31493
+ finish({ scan: reply.scan });
31494
+ else if (reply.kind === "tag-summary")
31495
+ finish({ summary: reply.summary });
31496
+ else if (reply.kind === "tag-delta")
31497
+ finish({ delta: reply.delta });
31498
+ else if (reply.kind === "indexed")
31499
+ finish({ inserted: reply.inserted, measurements: reply.measurements });
31500
+ else
31501
+ finish({ failure: "worker-error", detail: reply.error });
31502
+ });
31503
+ worker.on("error", (error) => finish({ failure: "worker-error", detail: String(error) }));
31504
+ worker.on("exit", (code) => finish({ failure: "exited", detail: `exit code ${code}` }));
31505
+ });
31506
+ }
31507
+ function prewarmRawSessionOrdinalsForDb(db, sessionId, options = {}) {
31508
+ if (isRawSessionSummaryWarm(db, sessionId))
31509
+ return Promise.resolve("warm");
31510
+ if (!db.prepare("SELECT 1 FROM sqlite_master WHERE name = 'message' AND type IN ('table', 'view')").get())
31511
+ return Promise.resolve("warm");
31512
+ const path = getSqliteDatabasePath(db);
31513
+ if (!path) {
31514
+ countRawSessionMessageOrdinalsFromDb(db, sessionId);
31515
+ return Promise.resolve("warm");
31516
+ }
31517
+ let connectionId = connectionIds.get(db);
31518
+ if (connectionId === undefined) {
31519
+ connectionId = ++nextConnectionId;
31520
+ connectionIds.set(db, connectionId);
31521
+ }
31522
+ const key = `${path}\x00${sessionId}\x00${connectionId}`;
31523
+ let shared = inFlight.get(key);
31524
+ if (!shared) {
31525
+ const failed = failedAt.get(key);
31526
+ if (failed !== undefined && Date.now() - failed < FAILURE_BACKOFF_MS)
31527
+ return Promise.resolve("failed");
31528
+ const epoch = getRawSessionSummaryEpoch(sessionId);
31529
+ const started = performance.now();
31530
+ const result = runSessionWarmupWorker({ path, sessionId, previous: rawOrdinalSummarySeed(db, sessionId) }, options).then((outcome) => {
31531
+ if ("failure" in outcome)
31532
+ recordFailure(key, sessionId, outcome.failure, outcome.detail);
31533
+ return outcome;
31534
+ }).finally(() => {
31535
+ if (inFlight.get(key) === scan)
31536
+ inFlight.delete(key);
31537
+ });
31538
+ const scan = { epoch, started, result };
31539
+ inFlight.set(key, scan);
31540
+ shared = scan;
31541
+ }
31542
+ const awaited = shared;
31543
+ return awaited.result.then((result) => {
31544
+ if (!("scan" in result))
31545
+ return "failed";
31546
+ try {
31547
+ if (installRawSessionSummaryScan(db, sessionId, result.scan, awaited.epoch, true)) {
31548
+ failedAt.delete(key);
31549
+ log(`[magic-context] canonical ordinals of ${sessionId} warmed off-thread (${result.scan.sessionRows} messages, ${Math.round(performance.now() - awaited.started)} ms)`);
31550
+ return "warm";
31551
+ }
31552
+ } catch {}
31553
+ recordFailure(key, sessionId, "superseded", `session removed during scan: epoch=${awaited.epoch}/${getRawSessionSummaryEpoch(sessionId)}`);
31554
+ return "failed";
31555
+ });
31556
+ }
31557
+ function recordFailure(key, sessionId, failure, detail) {
31558
+ failures.set(failure, (failures.get(failure) ?? 0) + 1);
31559
+ if (failure !== "superseded")
31560
+ failedAt.set(key, Date.now());
31561
+ log(`[magic-context] WARN canonical ordinal warm-up failed for ${sessionId} (${failure}: ${detail}); counts remain bounded and callers must defer or retry`);
31562
+ }
31563
+ var ACCEPT_TIMEOUT_MS = 2000, RESULT_TIMEOUT_MS, FAILURE_BACKOFF_MS, inFlight, connectionIds, nextConnectionId = 0, failedAt, activeWorkers, failures, exitHookRegistered = false;
31564
+ var init_raw_ordinal_warmup = __esm(async () => {
31565
+ init_exit_abort_registry();
31566
+ init_logger();
31567
+ await __promiseAll([
31568
+ init_sqlite(),
31569
+ init_read_session_raw()
31570
+ ]);
31571
+ RESULT_TIMEOUT_MS = 10 * 60000;
31572
+ FAILURE_BACKOFF_MS = 5 * 60000;
31573
+ inFlight = new Map;
31574
+ connectionIds = new WeakMap;
31575
+ failedAt = new Map;
31576
+ activeWorkers = new Map;
31577
+ failures = new Map;
31578
+ });
31579
+
31580
+ // ../plugin/src/hooks/magic-context/read-session-db.ts
31581
+ function openCodeDbExists() {
31582
+ return harnessOwnsOpenCodeStore() && openCodeDbPathExists(resolveOpenCodeDbPath());
31583
+ }
31584
+ function closeCachedReadOnlyDb() {
31585
+ if (!cachedReadOnlyDb) {
31586
+ return;
31587
+ }
31588
+ try {
31589
+ closeQuietly(cachedReadOnlyDb.db);
31590
+ } catch (error) {
31591
+ log("[magic-context] failed to close cached OpenCode read-only DB:", error);
31592
+ } finally {
31593
+ cachedReadOnlyDb = null;
31594
+ }
31595
+ }
31596
+ function getReadOnlySessionDb() {
31597
+ if (!harnessOwnsOpenCodeStore()) {
31598
+ throw new Error("OpenCode session database is not readable from a Pi-compatible process; its history lives in Pi sessions");
31599
+ }
31600
+ const resolution = resolveOpenCodeDbPath();
31601
+ const dbPath = resolution.path;
31602
+ if (!openCodeDbPathExists(resolution)) {
31603
+ throw new Error(`OpenCode session database is unavailable at ${dbPath} (source=${resolution.source})`);
31604
+ }
31605
+ if (cachedReadOnlyDb?.path === dbPath) {
31606
+ return cachedReadOnlyDb.db;
31607
+ }
31608
+ closeCachedReadOnlyDb();
31609
+ const db = new Database(dbPath, { readonly: true });
31610
+ try {
31611
+ assertOpenCodeStoreGeneration(db, "v1", dbPath);
31612
+ } catch (error) {
31613
+ closeQuietly(db);
31614
+ throw error;
31615
+ }
31616
+ cachedReadOnlyDb = { path: dbPath, db };
31617
+ clearOpenCodeDbReadFailure();
31618
+ return db;
31619
+ }
31620
+ function withReadOnlySessionDb(fn) {
31621
+ return fn(getReadOnlySessionDb());
31622
+ }
31623
+ function getRawSessionMessageCountFromDb(db, sessionId) {
31624
+ const row = db.prepare(`SELECT COUNT(*) as count FROM message WHERE session_id = ?
31625
+ AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
31626
+ AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')`).get(sessionId);
31627
+ return typeof row?.count === "number" ? row.count : 0;
31628
+ }
31629
+ function logProbeFailureOnce(resolution, error) {
31630
+ const failure = recordOpenCodeDbReadFailure(resolution, error);
31631
+ if (!claimOpenCodeDbDiagnosticOnce("session-state-probe", resolution))
31632
+ return;
31633
+ const message = `[magic-context] OpenCode DB probe failed: path=${resolution.path} source=${resolution.source} cause=${failure.message}`;
31634
+ probeLogObserverForTests?.(message);
31635
+ log(message);
31636
+ }
31637
+ function getMessageTimesFromOpenCodeDb(sessionId, messageIds) {
31638
+ const result = new Map;
31639
+ if (messageIds.length === 0 || !harnessOwnsOpenCodeStore())
31640
+ return result;
31641
+ try {
31642
+ withReadOnlySessionDb((db) => {
31643
+ const placeholders = messageIds.map(() => "?").join(",");
31644
+ const rows = db.prepare(`SELECT id, time_created FROM message WHERE session_id = ? AND id IN (${placeholders})`).all(sessionId, ...messageIds);
31645
+ for (const row of rows) {
31646
+ if (typeof row.id === "string" && typeof row.time_created === "number") {
31647
+ result.set(row.id, row.time_created);
31648
+ }
31649
+ }
31650
+ });
31651
+ } catch (error) {
31652
+ logProbeFailureOnce(resolveOpenCodeDbPath(), error);
31653
+ }
31654
+ return result;
31655
+ }
31656
+ var cachedReadOnlyDb = null, trackedSessions, pendingParts, probeLogObserverForTests;
31657
+ var init_read_session_db = __esm(async () => {
31658
+ init_logger();
31659
+ init_opencode_db_path();
31660
+ await __promiseAll([
31661
+ init_sqlite(),
31662
+ init_read_session_raw()
31663
+ ]);
31664
+ trackedSessions = new Map;
31665
+ pendingParts = new Map;
30363
31666
  });
30364
31667
 
30365
31668
  // ../plugin/src/hooks/magic-context/read-session-true-raw-tokens.ts
@@ -30411,7 +31714,7 @@ function callIdFromPart(part) {
30411
31714
  const state = isRecord2(part.state) ? part.state : null;
30412
31715
  return state ? firstStringField(state, ["callID", "callId", "toolCallId", "tool_call_id", "id"]) ?? "" : "";
30413
31716
  }
30414
- function toolSignalFromPart(part) {
31717
+ function toolSignalFromPart(part, withText = true) {
30415
31718
  if (!isRecord2(part))
30416
31719
  return null;
30417
31720
  const type = partType(part);
@@ -30430,8 +31733,8 @@ function toolSignalFromPart(part) {
30430
31733
  callId,
30431
31734
  hasInput: hasInput || openInvocation,
30432
31735
  hasOutput,
30433
- inputText: hasInput && state ? stringValue(state.input) : "",
30434
- outputText: hasOutput ? stringValue(outputValue) : ""
31736
+ inputText: withText && hasInput && state ? stringValue(state.input) : "",
31737
+ outputText: withText && hasOutput ? stringValue(outputValue) : ""
30435
31738
  };
30436
31739
  }
30437
31740
  if (type === "tool-invocation") {
@@ -30440,7 +31743,7 @@ function toolSignalFromPart(part) {
30440
31743
  callId,
30441
31744
  hasInput: args !== undefined,
30442
31745
  hasOutput: false,
30443
- inputText: args !== undefined ? stringValue(args) : "",
31746
+ inputText: withText && args !== undefined ? stringValue(args) : "",
30444
31747
  outputText: ""
30445
31748
  };
30446
31749
  }
@@ -30450,7 +31753,7 @@ function toolSignalFromPart(part) {
30450
31753
  callId,
30451
31754
  hasInput: input !== undefined,
30452
31755
  hasOutput: false,
30453
- inputText: input !== undefined ? stringValue(input) : "",
31756
+ inputText: withText && input !== undefined ? stringValue(input) : "",
30454
31757
  outputText: ""
30455
31758
  };
30456
31759
  }
@@ -30461,7 +31764,7 @@ function toolSignalFromPart(part) {
30461
31764
  hasInput: false,
30462
31765
  hasOutput: content !== undefined,
30463
31766
  inputText: "",
30464
- outputText: content !== undefined ? textFromToolResultContent(content) : ""
31767
+ outputText: withText && content !== undefined ? textFromToolResultContent(content) : ""
30465
31768
  };
30466
31769
  }
30467
31770
  return null;
@@ -30471,7 +31774,7 @@ function buildToolArcs(messages) {
30471
31774
  const arcs = [];
30472
31775
  for (const message of messages) {
30473
31776
  for (const part of message.parts) {
30474
- const signal = toolSignalFromPart(part);
31777
+ const signal = toolSignalFromPart(part, false);
30475
31778
  if (!signal || signal.callId.length === 0)
30476
31779
  continue;
30477
31780
  if (signal.hasInput && signal.hasOutput) {
@@ -30510,6 +31813,7 @@ function buildToolArcs(messages) {
30510
31813
  }
30511
31814
  var MAX_MESSAGE_CACHE_KEY_BYTES, messageEstimateCache, messageEstimateKeysByField;
30512
31815
  var init_read_session_true_raw_tokens = __esm(() => {
31816
+ init_provider_visible_parts();
30513
31817
  init_read_session_formatting();
30514
31818
  MAX_MESSAGE_CACHE_KEY_BYTES = 64 * 1024 * 1024;
30515
31819
  messageEstimateCache = new Map;
@@ -30591,7 +31895,8 @@ var init_overflow_detection = __esm(() => {
30591
31895
  PREFIX_BOUND_THINKING_MODELS = [
30592
31896
  ["fable", 5, 1],
30593
31897
  ["opus", 5, 5],
30594
- ["sonnet", 5, 5]
31898
+ ["sonnet", 5, 5],
31899
+ ["haiku", 5, 5]
30595
31900
  ];
30596
31901
  PREFIX_BOUND_THINKING_PATTERN = new RegExp(`(?:^|[-_.:/])(?:${PREFIX_BOUND_THINKING_MODELS.map(([family, major, minor]) => `${family}[-_.]?${major}[-_.]${minor}`).join("|")})(?:$|[-_.:/@])`, "i");
30597
31902
  });
@@ -30670,6 +31975,7 @@ __export(exports_read_session_chunk, {
30670
31975
  hasMeaningfulUserText: () => hasMeaningfulUserText,
30671
31976
  hasRawMessageProvider: () => hasRawMessageProvider,
30672
31977
  hasRawSessionMessageById: () => hasRawSessionMessageById,
31978
+ prepareRawSessionOrdinals: () => prepareRawSessionOrdinals,
30673
31979
  primeInMemoryTailRawMessageCache: () => primeInMemoryTailRawMessageCache,
30674
31980
  primeTailRawMessageCache: () => primeTailRawMessageCache,
30675
31981
  readRawSessionMessageById: () => readRawSessionMessageById,
@@ -30940,6 +32246,36 @@ function readRawSessionMessageRange(sessionId, fromOrdinal, toOrdinal) {
30940
32246
  }
30941
32247
  return messages;
30942
32248
  }
32249
+ async function prepareRawSessionOrdinals(sessionId) {
32250
+ if (sessionProviders.get(sessionId)?.provider)
32251
+ return true;
32252
+ if (!openCodeDbExists())
32253
+ return true;
32254
+ const db = withReadOnlySessionDb((connection) => connection);
32255
+ let timer;
32256
+ const deadline = Date.now() + 250;
32257
+ const prepare = async () => {
32258
+ for (let attempt = 0;attempt < 3 && Date.now() < deadline; attempt++) {
32259
+ if (await prewarmRawSessionOrdinalsForDb(db, sessionId) === "warm")
32260
+ return true;
32261
+ }
32262
+ return false;
32263
+ };
32264
+ try {
32265
+ const ready = await Promise.race([
32266
+ prepare(),
32267
+ new Promise((resolve) => {
32268
+ timer = setTimeout(() => resolve(false), 250);
32269
+ })
32270
+ ]);
32271
+ if (!ready) {
32272
+ sessionLog(sessionId, "WARN canonical ordinal preparation unavailable within 250ms; exact reads must defer or retry");
32273
+ }
32274
+ return ready;
32275
+ } finally {
32276
+ clearTimeout(timer);
32277
+ }
32278
+ }
30943
32279
  function primeTailRawMessageCache(args) {
30944
32280
  const { sessionId, lastCompartmentEnd, anchorMessageId } = args;
30945
32281
  if (!activeRawMessageCache)
@@ -31574,22 +32910,28 @@ function readRawSessionSeedTail(sessionId, boundaryId, onQuery) {
31574
32910
  }
31575
32911
  var BLOCK_TOKEN_MEMO_MAX = 2048, blockTokenMemo, activeRawMessageCache = null, activeAbsoluteCountCache = null, sessionProviders, boundedProviderWrappers, SYSTEM_REMINDER_BLOCK_REGEX, RAW_MESSAGE_RANGE_PAGE_SIZE = 100, RAW_MESSAGE_VISIT_PAGE_SIZE = 50, RAW_SESSION_TAG_KEY_PAGE_SIZE = 32, PROTECTED_TAIL_USER_TURNS = 5;
31576
32912
  var init_read_session_chunk = __esm(async () => {
31577
- init_storage_tags();
31578
32913
  init_historian_tool_expansions();
32914
+ init_logger();
31579
32915
  init_read_session_formatting();
31580
- init_read_session_raw();
31581
32916
  init_read_session_true_raw_tokens();
31582
32917
  init_system_injection_stripper();
31583
32918
  init_tag_part_guards();
31584
32919
  init_tool_drop_target();
31585
32920
  init_read_session_formatting();
31586
- await init_read_session_db();
32921
+ await __promiseAll([
32922
+ init_storage_tags(),
32923
+ init_raw_ordinal_warmup(),
32924
+ init_read_session_db(),
32925
+ init_read_session_raw()
32926
+ ]);
31587
32927
  blockTokenMemo = new Map;
31588
32928
  sessionProviders = new Map;
31589
32929
  boundedProviderWrappers = new WeakMap;
31590
32930
  SYSTEM_REMINDER_BLOCK_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
31591
32931
  readRawSessionMessages.readPage = readRawSessionMessagePage;
31592
32932
  readRawSessionMessages.getCount = getRawSessionMessageOrdinalCount;
32933
+ readRawSessionMessages.prepare = prepareRawSessionOrdinals;
32934
+ readRawSessionMessageById.prepare = prepareRawSessionOrdinals;
31593
32935
  });
31594
32936
 
31595
32937
  // ../plugin/src/features/magic-context/message-time-backfill.ts
@@ -33450,6 +34792,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
33450
34792
  const version = getPersistedSchemaVersion(db);
33451
34793
  if (version === 0 || version >= 95)
33452
34794
  installV95PerfSchema(db, false, version === 0);
34795
+ installTagIdentityRevisionTrigger(db);
33453
34796
  }
33454
34797
  function healWedgedChannel2Claims(db) {
33455
34798
  try {
@@ -33529,6 +34872,7 @@ function openDatabase(dbPathOrOptions) {
33529
34872
  } else
33530
34873
  runMigrations(db);
33531
34874
  ensureContextStoreUuid(db);
34875
+ installTagIdentityRevisionTrigger(db);
33532
34876
  return finishDatabaseOpen(db, dbPath, explicitDbPath, latestSupportedVersion);
33533
34877
  } catch (error) {
33534
34878
  if (db)
@@ -33563,6 +34907,7 @@ var init_storage_db = __esm(async () => {
33563
34907
  init_migration_v95_perf_indexes();
33564
34908
  init_migration_worker_client();
33565
34909
  init_storage_schema_helpers();
34910
+ init_storage_tag_identity_revision();
33566
34911
  init_tool_definition_tokens();
33567
34912
  init_tool_owner_backfill();
33568
34913
  await __promiseAll([
@@ -33768,6 +35113,9 @@ var init_config_location_migration = __esm(() => {
33768
35113
  });
33769
35114
 
33770
35115
  // ../plugin/src/features/magic-context/dreamer/task-registry.ts
35116
+ function availableDreamTasks(smartNotesEnabled = true) {
35117
+ return CANONICAL_DREAM_TASKS.filter((task) => smartNotesEnabled || task !== "evaluate-smart-notes");
35118
+ }
33771
35119
  var CANONICAL_DREAM_TASKS, DREAM_TOOL_LOOP_TOKEN_BUDGETS, MEMORY_DOMAIN_TASKS, MEMORY_DOMAIN_SET;
33772
35120
  var init_task_registry = __esm(() => {
33773
35121
  CANONICAL_DREAM_TASKS = [
@@ -34518,8 +35866,10 @@ function openCodeConfigFileChain(directory, hostGeneration) {
34518
35866
  const globalSupplementary = getOpenCodeConfigDirs().filter((dir) => dir !== globalDir).flatMap((dir) => [join17(dir, "opencode.json"), join17(dir, "opencode.jsonc")]);
34519
35867
  const explicitConfig = process.env.OPENCODE_CONFIG?.trim();
34520
35868
  const explicit = explicitConfig ? [explicitConfig] : [];
34521
- const direct = [join17(directory, "opencode.json"), join17(directory, "opencode.jsonc")];
34522
- const projectSupplementary = [
35869
+ const disableProjectConfig = process.env.OPENCODE_DISABLE_PROJECT_CONFIG?.toLowerCase();
35870
+ const skipProject = hostGeneration === "v1" && (disableProjectConfig === "true" || disableProjectConfig === "1");
35871
+ const direct = skipProject ? [] : [join17(directory, "opencode.json"), join17(directory, "opencode.jsonc")];
35872
+ const projectSupplementary = skipProject ? [] : [
34523
35873
  join17(directory, ".opencode", "opencode.json"),
34524
35874
  join17(directory, ".opencode", "opencode.jsonc")
34525
35875
  ];
@@ -35749,6 +37099,24 @@ function readCachedPluginVersion(pluginCacheDir) {
35749
37099
  return;
35750
37100
  }
35751
37101
  }
37102
+ function readOpenCodePluginConfigUse(config, configDir) {
37103
+ const use = {
37104
+ referencedSpecs: {
37105
+ opencode1: readReferencedOpenCodePluginSpecs(config, "opencode1"),
37106
+ opencode2: readReferencedOpenCodePluginSpecs(config, "opencode2")
37107
+ },
37108
+ loadedFrom: {}
37109
+ };
37110
+ for (const { key, entry } of readPluginEntries(config)) {
37111
+ if (!isDevPathPluginEntry(entry, configDir))
37112
+ continue;
37113
+ const specifier = pluginEntryPackage(entry) ?? undefined;
37114
+ if (key === "plugin")
37115
+ use.loadedFrom.opencode1 ??= specifier;
37116
+ use.loadedFrom.opencode2 ??= specifier;
37117
+ }
37118
+ return use;
37119
+ }
35752
37120
  async function clearPluginCache(options = {}, deps = {}) {
35753
37121
  const remove = deps.remove ?? ((path) => rmSync6(path, { recursive: true, force: true }));
35754
37122
  const pluginCacheRoots = getOpenCodePluginCacheRoots();
@@ -35761,6 +37129,15 @@ async function clearPluginCache(options = {}, deps = {}) {
35761
37129
  path,
35762
37130
  cached: readCachedPluginVersion(path)
35763
37131
  }));
37132
+ if (options.referencedSpecs !== undefined && !options.referencedSpecs.has("latest")) {
37133
+ const firstEntry = cacheEntries[0];
37134
+ return {
37135
+ action: "unused",
37136
+ path: firstEntry?.path ?? pluginCacheRoots[0] ?? "",
37137
+ paths: cacheEntries.map((entry) => entry.path),
37138
+ cached: firstEntry?.cached
37139
+ };
37140
+ }
35764
37141
  if (options.force !== true && latestVersion === undefined) {
35765
37142
  const firstEntry = cacheEntries[0];
35766
37143
  return {
@@ -35832,15 +37209,24 @@ async function clearPluginCache(options = {}, deps = {}) {
35832
37209
  }
35833
37210
  var init_doctor_opencode_cache = __esm(async () => {
35834
37211
  init_opencode_plugin_cache();
35835
- await init_doctor_opencode2_cache();
37212
+ init_opencode_plugin_registration();
37213
+ await __promiseAll([
37214
+ init_opencode(),
37215
+ init_doctor_opencode2_cache()
37216
+ ]);
35836
37217
  });
35837
37218
 
35838
37219
  // src/commands/doctor-cached-plugin-fence.ts
35839
37220
  import { existsSync as existsSync29 } from "node:fs";
35840
37221
  import { dirname as dirname15 } from "node:path";
37222
+ function isReferenced(sources, host, spec) {
37223
+ const specs = sources.referencedSpecs?.[host];
37224
+ return specs === undefined || specs.has(spec);
37225
+ }
35841
37226
  function listCachedOpenCodePluginFences(sources = {}) {
35842
37227
  const fences = [];
35843
- for (const root of sources.openCodeV1Roots ?? getOpenCodePluginCacheRoots()) {
37228
+ const v1Referenced = isReferenced(sources, "opencode1", "latest");
37229
+ for (const root of v1Referenced ? sources.openCodeV1Roots ?? getOpenCodePluginCacheRoots() : []) {
35844
37230
  const packageJson = getOpenCodePluginPackageJsonPath(root);
35845
37231
  if (!existsSync29(packageJson))
35846
37232
  continue;
@@ -35854,6 +37240,8 @@ function listCachedOpenCodePluginFences(sources = {}) {
35854
37240
  for (const slot of listOpenCodeV2PluginCacheSlots(sources.openCodeV2NpmCacheDir)) {
35855
37241
  if (!isOpenCodePluginDistTag(slot.spec))
35856
37242
  continue;
37243
+ if (!isReferenced(sources, "opencode2", slot.spec))
37244
+ continue;
35857
37245
  const generation = getOpenCodeV2ActiveGeneration(slot.slot);
35858
37246
  if (!generation)
35859
37247
  continue;
@@ -41042,11 +42430,6 @@ var init_user_facing_codes = __esm(() => {
41042
42430
  sentence: "Magic Context has no context.db.",
41043
42431
  action: "Run `npx @cortexkit/magic-context doctor store init`, then restart ck-mc."
41044
42432
  },
41045
- checkout_claim_held_elsewhere: {
41046
- code: "MC-C16",
41047
- sentence: "This request was not sent: this agent is checked out on another machine, so Magic Context will not work on its sessions here.",
41048
- action: "Move the agent back to this machine, then send your message again."
41049
- },
41050
42433
  single_store_migration_required: {
41051
42434
  code: "MC-C14",
41052
42435
  sentence: "Magic Context's Rust mode needs a one-time migration of its store.",
@@ -42624,6 +44007,9 @@ function mergeRawConfigs(base, override) {
42624
44007
  }
42625
44008
  function parsePiConfig(rawConfig, recoveredTopLevelKeys = []) {
42626
44009
  const preMigrationWarnings = [];
44010
+ if (Object.hasOwn(rawConfig, "clear_reasoning_age")) {
44011
+ preMigrationWarnings.push("clear_reasoning_age is deprecated and ignored. Magic Context now keeps reasoning up to a token budget, keep_reasoning_tokens (default 10,000). Remove the key, or set keep_reasoning_tokens to a token count.");
44012
+ }
42627
44013
  const configWithoutRemovedAgent = stripRemovedAgentConfig(rawConfig, preMigrationWarnings);
42628
44014
  const hasDeprecatedProtectedTags = Object.hasOwn(rawConfig, "protected_tags");
42629
44015
  if (hasDeprecatedProtectedTags) {
@@ -42634,7 +44020,9 @@ function parsePiConfig(rawConfig, recoveredTopLevelKeys = []) {
42634
44020
  delete configToMigrate.protected_tags;
42635
44021
  }
42636
44022
  const agentMigrated = migrateLegacyAgentEnabledInMemory(configToMigrate, preMigrationWarnings);
42637
- const migrated = migrateDreamerV2(migrateLegacyExperimental(agentMigrated, preMigrationWarnings), preMigrationWarnings);
44023
+ const migratedExperimental = migrateLegacyExperimental(agentMigrated, preMigrationWarnings);
44024
+ warnDeprecatedMuralModel(migratedExperimental, preMigrationWarnings);
44025
+ const migrated = migrateDreamerV2(migratedExperimental, preMigrationWarnings);
42638
44026
  const parsed = MagicContextConfigSchema.safeParse(migrated);
42639
44027
  if (parsed.success) {
42640
44028
  return { config: parsed.data, warnings: preMigrationWarnings };
@@ -43476,64 +44864,66 @@ async function runHealthChecks(options) {
43476
44864
  add(results, "fail", "OMP binary not found on PATH or in standard user bin directories");
43477
44865
  } else {
43478
44866
  const version = options.deps.getOmpVersion(omp.path);
43479
- if (!version)
43480
- add(results, "fail", `OMP at ${omp.path} could not report its version`);
43481
- else if (isOlderThan(version, MIN_OMP_VERSION)) {
43482
- add(results, "fail", `OMP ${version} is older than tested minimum ${MIN_OMP_VERSION}`);
43483
- } else
43484
- add(results, "pass", `OMP ${version} detected at ${omp.path}`);
43485
44867
  const plugins = options.deps.listOmpPlugins(omp.path);
43486
44868
  if (!plugins) {
43487
44869
  add(results, "fail", "`omp plugin list --json` failed or returned invalid JSON");
43488
44870
  } else {
43489
44871
  const plugin = plugins.find((entry) => entry.name === OMP_PLUGIN_PACKAGE);
43490
44872
  if (!plugin) {
43491
- add(results, "fail", `${OMP_PLUGIN_PACKAGE} is not installed in OMP`);
43492
- repairPlan.installPlugin = true;
43493
- } else if (!plugin.enabled) {
43494
- add(results, "fail", `${OMP_PLUGIN_PACKAGE} is installed but disabled in OMP`);
44873
+ add(results, "info", `${version ? `Oh My Pi ${version}` : "Oh My Pi"} found; Magic Context is not installed there (run setup to add it)`);
43495
44874
  repairPlan.installPlugin = true;
43496
44875
  } else {
43497
- add(results, "pass", `${OMP_PLUGIN_PACKAGE} ${plugin.version} is enabled`);
43498
- const manifest = pluginDeclaresOmp(plugin.path);
43499
- if (manifest === true)
43500
- add(results, "pass", "Plugin exposes an OMP/Pi extension manifest");
43501
- else if (manifest === false)
43502
- add(results, "fail", "Installed plugin has no OMP/Pi extension manifest");
43503
- else
43504
- add(results, "warn", "Could not inspect the installed plugin manifest");
43505
- }
43506
- }
43507
- const compaction = options.deps.getOmpSetting(omp.path, "compaction.enabled");
43508
- if (compaction === false)
43509
- add(results, "pass", "OMP native compaction is disabled");
43510
- else if (compaction === true) {
43511
- add(results, "fail", "OMP native compaction is enabled and conflicts with Magic Context");
43512
- repairPlan.disableCompaction = true;
43513
- } else
43514
- add(results, "fail", "Could not read OMP compaction.enabled");
43515
- const memory = options.deps.getOmpSetting(omp.path, "memory.backend");
43516
- if (memory === "off")
43517
- add(results, "pass", "OMP automatic memory backend is disabled");
43518
- else if (typeof memory === "string") {
43519
- add(results, "fail", `OMP memory.backend=${memory} duplicates Magic Context memory injection`);
43520
- repairPlan.disableMemory = true;
43521
- } else
43522
- add(results, "fail", "Could not read OMP memory.backend");
43523
- const nonGlobalSources = getOmpNonGlobalConfigSources(options.cwd);
43524
- if (nonGlobalSources.length > 0 && (repairPlan.disableCompaction || repairPlan.disableMemory)) {
43525
- add(results, "warn", "OMP project/overlay config owns effective conflicting settings; automatic global repair is disabled: " + nonGlobalSources.join(", "));
43526
- }
43527
- const reportedAgentDir = options.deps.runOmpCommand(omp.path, ["config", "path"], 1e4);
43528
- if (!reportedAgentDir.ok) {
43529
- add(results, "warn", "Could not verify OMP active agent directory");
43530
- } else {
43531
- const reportedPath = resolve15(reportedAgentDir.stdout);
43532
- const expectedPath = resolve15(getOmpAgentDir());
43533
- if (reportedPath === expectedPath) {
43534
- add(results, "pass", `OMP agent directory resolved to ${getOmpAgentDir()}`);
43535
- } else {
43536
- add(results, "fail", `OMP reports agent directory ${reportedAgentDir.stdout}, but Magic Context resolved ${getOmpAgentDir()}`);
44876
+ if (!version) {
44877
+ add(results, "fail", `OMP at ${omp.path} could not report its version`);
44878
+ } else if (isOlderThan(version, MIN_OMP_VERSION)) {
44879
+ add(results, "fail", `OMP ${version} is older than tested minimum ${MIN_OMP_VERSION}`);
44880
+ } else
44881
+ add(results, "pass", `OMP ${version} detected at ${omp.path}`);
44882
+ if (!plugin.enabled) {
44883
+ add(results, "fail", `${OMP_PLUGIN_PACKAGE} is installed but disabled in OMP`);
44884
+ repairPlan.installPlugin = true;
44885
+ } else {
44886
+ add(results, "pass", `${OMP_PLUGIN_PACKAGE} ${plugin.version} is enabled`);
44887
+ const manifest = pluginDeclaresOmp(plugin.path);
44888
+ if (manifest === true)
44889
+ add(results, "pass", "Plugin exposes an OMP/Pi extension manifest");
44890
+ else if (manifest === false)
44891
+ add(results, "fail", "Installed plugin has no OMP/Pi extension manifest");
44892
+ else
44893
+ add(results, "warn", "Could not inspect the installed plugin manifest");
44894
+ }
44895
+ const compaction = options.deps.getOmpSetting(omp.path, "compaction.enabled");
44896
+ if (compaction === false)
44897
+ add(results, "pass", "OMP native compaction is disabled");
44898
+ else if (compaction === true) {
44899
+ add(results, "fail", "OMP native compaction is enabled and conflicts with Magic Context");
44900
+ repairPlan.disableCompaction = true;
44901
+ } else
44902
+ add(results, "fail", "Could not read OMP compaction.enabled");
44903
+ const memory = options.deps.getOmpSetting(omp.path, "memory.backend");
44904
+ if (memory === "off")
44905
+ add(results, "pass", "OMP automatic memory backend is disabled");
44906
+ else if (typeof memory === "string") {
44907
+ add(results, "fail", `OMP memory.backend=${memory} duplicates Magic Context memory injection`);
44908
+ repairPlan.disableMemory = true;
44909
+ } else
44910
+ add(results, "fail", "Could not read OMP memory.backend");
44911
+ const nonGlobalSources = getOmpNonGlobalConfigSources(options.cwd);
44912
+ if (nonGlobalSources.length > 0 && (repairPlan.disableCompaction || repairPlan.disableMemory)) {
44913
+ add(results, "warn", "OMP project/overlay config owns effective conflicting settings; automatic global repair is disabled: " + nonGlobalSources.join(", "));
44914
+ }
44915
+ const reportedAgentDir = options.deps.runOmpCommand(omp.path, ["config", "path"], 1e4);
44916
+ if (!reportedAgentDir.ok) {
44917
+ add(results, "warn", "Could not verify OMP active agent directory");
44918
+ } else {
44919
+ const reportedPath = resolve15(reportedAgentDir.stdout);
44920
+ const expectedPath = resolve15(getOmpAgentDir());
44921
+ if (reportedPath === expectedPath) {
44922
+ add(results, "pass", `OMP agent directory resolved to ${getOmpAgentDir()}`);
44923
+ } else {
44924
+ add(results, "fail", `OMP reports agent directory ${reportedAgentDir.stdout}, but Magic Context resolved ${getOmpAgentDir()}`);
44925
+ }
44926
+ }
43537
44927
  }
43538
44928
  }
43539
44929
  }
@@ -46534,6 +47924,16 @@ function listDanglingCompartmentBoundaries(contextDb, openCodeDb, hostGeneration
46534
47924
  ORDER BY c.session_id ASC, c.sequence ASC`).all();
46535
47925
  const statement = openCodeDb.prepare(`SELECT 1 AS found FROM ${selection.table} WHERE session_id = ? AND id = ? LIMIT 1`);
46536
47926
  const exists = (sessionId, messageId) => statement.get(sessionId, messageId) != null;
47927
+ const sessionStatement = openCodeDb.prepare(`SELECT 1 AS found FROM ${selection.table} WHERE session_id = ? LIMIT 1`);
47928
+ const sessionPresence = new Map;
47929
+ const sessionInStore = (sessionId) => {
47930
+ let present = sessionPresence.get(sessionId);
47931
+ if (present === undefined) {
47932
+ present = sessionStatement.get(sessionId) != null;
47933
+ sessionPresence.set(sessionId, present);
47934
+ }
47935
+ return present;
47936
+ };
46537
47937
  return rows.flatMap((row) => {
46538
47938
  const missingStart = !exists(row.session_id, row.start_message_id);
46539
47939
  const missingEnd = !exists(row.session_id, row.end_message_id);
@@ -46544,7 +47944,8 @@ function listDanglingCompartmentBoundaries(contextDb, openCodeDb, hostGeneration
46544
47944
  sessionId: row.session_id,
46545
47945
  sequence: row.sequence,
46546
47946
  missingStartMessageId: missingStart ? row.start_message_id : null,
46547
- missingEndMessageId: missingEnd ? row.end_message_id : null
47947
+ missingEndMessageId: missingEnd ? row.end_message_id : null,
47948
+ sessionInStore: sessionInStore(row.session_id)
46548
47949
  }
46549
47950
  ];
46550
47951
  });
@@ -46556,6 +47957,52 @@ function formatDanglingCompartmentBoundary(boundary) {
46556
47957
  ].filter((value) => value !== null);
46557
47958
  return `session=${boundary.sessionId} sequence=${boundary.sequence} missing ${missing.join(" ")}`;
46558
47959
  }
47960
+ function countDanglingCompartmentBoundariesBySession(boundaries) {
47961
+ const bySession = new Map;
47962
+ for (const boundary of boundaries) {
47963
+ const entry = bySession.get(boundary.sessionId);
47964
+ if (entry)
47965
+ entry.count++;
47966
+ else
47967
+ bySession.set(boundary.sessionId, {
47968
+ sessionId: boundary.sessionId,
47969
+ count: 1,
47970
+ sessionInStore: boundary.sessionInStore
47971
+ });
47972
+ }
47973
+ return [...bySession.values()].sort((a, b) => b.count - a.count || a.sessionId.localeCompare(b.sessionId));
47974
+ }
47975
+ function formatDanglingCompartmentBoundaryHeadline(boundaries, store) {
47976
+ const sessions = countDanglingCompartmentBoundariesBySession(boundaries);
47977
+ const absent = sessions.filter((session) => !session.sessionInStore).length;
47978
+ const scope = `${boundaries.length} compartment(s) in ${sessions.length} session(s)`;
47979
+ const absentNote = absent > 0 ? ` ${absent} of those session(s) have no messages in this store at all, so OpenCode cannot open them from it and their compartments are not used.` : "";
47980
+ if (store === "opencode2") {
47981
+ return `Informational, no action needed: ${scope} point at OpenCode message ids that are not in the OpenCode 2 store.${absentNote} Magic Context places these from the neighbouring compartments; any it cannot place are listed as excluded from range recovery below.`;
47982
+ }
47983
+ return `${scope} have dangling OpenCode boundary ids.${absentNote}`;
47984
+ }
47985
+ function formatDanglingCompartmentBoundaryDetails(boundaries, options = {}) {
47986
+ const limit = options.verbose ? Number.POSITIVE_INFINITY : options.limit ?? DANGLING_BOUNDARY_EXAMPLE_LIMIT;
47987
+ const sessions = countDanglingCompartmentBoundariesBySession(boundaries);
47988
+ const lines = [];
47989
+ lines.push(options.verbose || sessions.length <= limit ? "By session:" : `By session (largest ${limit} of ${sessions.length}):`);
47990
+ for (const session of sessions.slice(0, limit)) {
47991
+ lines.push(` session=${session.sessionId}: ${session.count} compartment(s)${session.sessionInStore ? "" : " (session has no messages in this store)"}`);
47992
+ }
47993
+ if (sessions.length > limit) {
47994
+ lines.push(` … and ${sessions.length - limit} more session(s)`);
47995
+ }
47996
+ lines.push(options.verbose || boundaries.length <= limit ? "Compartments:" : `Examples (${limit} of ${boundaries.length}):`);
47997
+ for (const boundary of boundaries.slice(0, limit)) {
47998
+ lines.push(` ${formatDanglingCompartmentBoundary(boundary)}`);
47999
+ }
48000
+ if (sessions.length > limit || boundaries.length > limit) {
48001
+ lines.push(`Run \`magic-context doctor --verbose\` for the full list of ${boundaries.length} compartment(s).`);
48002
+ }
48003
+ return lines;
48004
+ }
48005
+ var DANGLING_BOUNDARY_EXAMPLE_LIMIT = 10;
46559
48006
 
46560
48007
  // src/commands/doctor-harness-relabel.ts
46561
48008
  function reportUnresolvedHarnessRelabel(args) {
@@ -46709,6 +48156,9 @@ function withPluginEntrySpecifier(entry, specifier) {
46709
48156
  }
46710
48157
  return specifier;
46711
48158
  }
48159
+ function isOpenCodePluginRegistered(config, configDir) {
48160
+ return readPluginEntries(config).some(({ entry }) => matchesPluginEntry(entry, OPENCODE_PLUGIN_NAME) || isDevPathPluginEntry(entry, configDir));
48161
+ }
46712
48162
  function checkOpenCodePluginEntry(config, configName, options, report) {
46713
48163
  const allEntries = readPluginEntries(config);
46714
48164
  const found = allEntries.find(({ entry }) => matchesPluginEntry(entry, OPENCODE_PLUGIN_NAME) || isDevPathPluginEntry(entry, options.configDir));
@@ -46799,6 +48249,20 @@ function formatUnresolvedCompartmentSession(session) {
46799
48249
  return `session=${session.sessionId} unresolved=${session.count}`;
46800
48250
  }
46801
48251
 
48252
+ // src/commands/reasoning-config-migration.ts
48253
+ function removeDeprecatedReasoningAge(config) {
48254
+ if (!Object.hasOwn(config, "clear_reasoning_age"))
48255
+ return [];
48256
+ const age = config.clear_reasoning_age;
48257
+ delete config.clear_reasoning_age;
48258
+ const messages = [
48259
+ "Removed deprecated clear_reasoning_age (reasoning is now kept up to a token budget, keep_reasoning_tokens, default 10,000)."
48260
+ ];
48261
+ if (typeof age === "number" && age < 50)
48262
+ messages.push("You had set a lower age to keep less reasoning; set keep_reasoning_tokens (for example 8000) to keep less than the default.");
48263
+ return messages;
48264
+ }
48265
+
46802
48266
  // src/commands/doctor-opencode.ts
46803
48267
  import { spawnSync as spawnSync10 } from "node:child_process";
46804
48268
  import { existsSync as existsSync45, mkdtempSync as mkdtempSync3, readFileSync as readFileSync28, rmSync as rmSync8 } from "node:fs";
@@ -47352,6 +48816,21 @@ function logOpenCodeInstallationTable(installations) {
47352
48816
  function describeOpenCode2SessionAPIRequirement(hostVersion) {
47353
48817
  return `OpenCode host ${hostVersion}; Magic Context requires OpenCode 2.0.22 or newer with session.remove and session.compact.`;
47354
48818
  }
48819
+ function getOpenCodePluginHostStatus(hostLabel, hostVersion, installed) {
48820
+ if (!installed) {
48821
+ return {
48822
+ status: "info",
48823
+ message: `${hostLabel} found; Magic Context is not installed there (run setup to add it)`
48824
+ };
48825
+ }
48826
+ if (openCodeHostGenerationFromVersion(hostVersion) === "v2" && compareVersions(hostVersion, "2.0.22") === -1) {
48827
+ return {
48828
+ status: "fail",
48829
+ message: describeOpenCode2SessionAPIRequirement(hostVersion)
48830
+ };
48831
+ }
48832
+ return null;
48833
+ }
47355
48834
  async function runDoctor2(options = {}) {
47356
48835
  migrateConfigLocationsForCli(process.cwd(), log3);
47357
48836
  if (options.issue) {
@@ -47427,8 +48906,15 @@ async function runDoctor2(options = {}) {
47427
48906
  pass(installationReports.length > 1 ? `OpenCode ${activeInstallation.version} installed (active install marked above)` : `OpenCode ${activeInstallation.version} installed`);
47428
48907
  }
47429
48908
  const hostGeneration = openCodeHostGenerationFromVersion(activeInstallation.version);
47430
- if (hostGeneration === "v2") {
47431
- log3.info(describeOpenCode2SessionAPIRequirement(activeInstallation.version));
48909
+ const paths = detectConfigPaths();
48910
+ let magicContextInstalled = false;
48911
+ let magicContextStatusKnown = paths.opencodeConfigFormat === "none";
48912
+ if (!magicContextStatusKnown) {
48913
+ try {
48914
+ const config = import_comment_json11.parse(readFileSync28(paths.opencodeConfig, "utf-8"));
48915
+ magicContextInstalled = isOpenCodePluginRegistered(config, dirname25(paths.opencodeConfig));
48916
+ magicContextStatusKnown = true;
48917
+ } catch {}
47432
48918
  }
47433
48919
  const storeHostSelection = selectOpenCodeStoreHost(installationReports, openCodeHostGenerationFromVersion);
47434
48920
  const storeHost = storeHostSelection?.host ?? activeInstallation;
@@ -47446,7 +48932,9 @@ async function runDoctor2(options = {}) {
47446
48932
  const openCodeDbCheck = describeOpenCodeDatabaseDoctorCheck(openCodeDbResolution);
47447
48933
  if (openCodeDbCheck.ok)
47448
48934
  pass(openCodeDbCheck.message);
47449
- else
48935
+ else if (magicContextStatusKnown && !magicContextInstalled) {
48936
+ log3.info("OpenCode session database not yet created (will be created on first OpenCode session)");
48937
+ } else
47450
48938
  fail(openCodeDbCheck.message);
47451
48939
  if (openCodeDbCheck.ok) {
47452
48940
  let markerDb = null;
@@ -47467,7 +48955,7 @@ async function runDoctor2(options = {}) {
47467
48955
  } else if (options.fix) {
47468
48956
  warn(`${summary}; repaired=${report.repaired}, but some rows remain unconvertible`);
47469
48957
  } else if (report.migrationCompleted) {
47470
- log3.info(`${summary}; OpenCode 2 already converted this store`);
48958
+ log3.info(`${summary}; OpenCode 2 already converted this store, so these markers no longer matter (informational, no action needed)`);
47471
48959
  } else {
47472
48960
  warn(`${summary}; run \`magic-context doctor --fix\` before upgrading OpenCode`);
47473
48961
  }
@@ -47495,14 +48983,18 @@ async function runDoctor2(options = {}) {
47495
48983
  if (dangling.length === 0) {
47496
48984
  pass("Compartment boundary ids resolve in the OpenCode session store");
47497
48985
  } else if (storeGeneration === "v2" && /\d/.test(storeHost.version)) {
47498
- log3.info(`${dangling.length} compartment(s) point at OpenCode message ids that are not in the OpenCode 2 store. Magic Context places these from the neighbouring compartments; any it cannot place are listed as excluded from range recovery below.`);
47499
- for (const boundary of dangling) {
47500
- log3.info(` ${formatDanglingCompartmentBoundary(boundary)}`);
48986
+ log3.info(formatDanglingCompartmentBoundaryHeadline(dangling, "opencode2"));
48987
+ for (const line of formatDanglingCompartmentBoundaryDetails(dangling, {
48988
+ verbose: options.verbose
48989
+ })) {
48990
+ log3.info(` ${line}`);
47501
48991
  }
47502
48992
  } else {
47503
- warn(`${dangling.length} compartment(s) have dangling OpenCode boundary ids`);
47504
- for (const boundary of dangling) {
47505
- log3.warn(` ${formatDanglingCompartmentBoundary(boundary)}`);
48993
+ warn(formatDanglingCompartmentBoundaryHeadline(dangling, "other"));
48994
+ for (const line of formatDanglingCompartmentBoundaryDetails(dangling, {
48995
+ verbose: options.verbose
48996
+ })) {
48997
+ log3.warn(` ${line}`);
47506
48998
  }
47507
48999
  }
47508
49000
  if (!supportsCoordinateGenerationReporting(contextDb)) {
@@ -47538,10 +49030,7 @@ async function runDoctor2(options = {}) {
47538
49030
  }
47539
49031
  }
47540
49032
  const selfVersion = getSelfVersion2();
47541
- const [npmLatest, pluginNpmLatest] = await Promise.all([
47542
- fetchNpmLatest(CLI_PACKAGE_NAME),
47543
- fetchNpmLatest(OPENCODE_PLUGIN_NAME)
47544
- ]);
49033
+ const npmLatest = await fetchNpmLatest(CLI_PACKAGE_NAME);
47545
49034
  const cliComparison = npmLatest ? compareVersions(selfVersion, npmLatest) : null;
47546
49035
  if (!npmLatest) {
47547
49036
  log3.info(`Magic Context CLI v${selfVersion}; npm latest check unavailable`);
@@ -47552,9 +49041,8 @@ async function runDoctor2(options = {}) {
47552
49041
  } else {
47553
49042
  pass(`Magic Context CLI v${selfVersion} is current (npm latest v${npmLatest})`);
47554
49043
  }
47555
- const paths = detectConfigPaths();
47556
49044
  if (paths.opencodeConfigFormat === "none") {
47557
- fail(`No opencode.json found at ${paths.opencodeConfig}`);
49045
+ magicContextStatusKnown = true;
47558
49046
  } else {
47559
49047
  pass(`OpenCode config: ${paths.opencodeConfig}`);
47560
49048
  }
@@ -47615,6 +49103,13 @@ async function runDoctor2(options = {}) {
47615
49103
  log3.success("Removed deprecated compaction_markers (always-on since v0.21.4)");
47616
49104
  fixed++;
47617
49105
  }
49106
+ const reasoningMessages = removeDeprecatedReasoningAge(mcConfig);
49107
+ if (reasoningMessages.length > 0) {
49108
+ mcChanged = true;
49109
+ fixed++;
49110
+ for (const message of reasoningMessages)
49111
+ log3.success(message);
49112
+ }
47618
49113
  for (const deadKey of ["auto_drop_tool_age", "drop_tool_structure"]) {
47619
49114
  if (deadKey in mcConfig) {
47620
49115
  delete mcConfig[deadKey];
@@ -47717,6 +49212,7 @@ async function runDoctor2(options = {}) {
47717
49212
  log3.warn("Could not migrate deprecated config keys in magic-context.jsonc");
47718
49213
  }
47719
49214
  }
49215
+ const openCodeHostLabel = activeInstallation.kind === "desktop" ? "OpenCode Desktop" : activeInstallation.version === "unknown" ? "OpenCode" : `OpenCode ${activeInstallation.version}`;
47720
49216
  const reportedAutoUpdateStalls = new Set;
47721
49217
  const reportAutoUpdateStall = (specifier) => {
47722
49218
  const message = describeAutoUpdateStall(specifier, autoUpdateEnabled);
@@ -47731,71 +49227,90 @@ async function runDoctor2(options = {}) {
47731
49227
  const config = import_comment_json11.parse(raw);
47732
49228
  const configName = paths.opencodeConfigFormat === "jsonc" ? "opencode.jsonc" : "opencode.json";
47733
49229
  const configDir = dirname25(paths.opencodeConfig);
47734
- if (checkPluginDuplicates(config, configName, { fix: options.fix, configDir }, {
47735
- warn,
47736
- pass: (message) => {
47737
- pass(message);
47738
- fixed++;
47739
- },
47740
- info: (message) => log3.info(message)
47741
- })) {
47742
- writeFileAtomic(paths.opencodeConfig, `${import_comment_json11.stringify(config, null, 2)}
49230
+ magicContextInstalled = isOpenCodePluginRegistered(config, configDir);
49231
+ magicContextStatusKnown = true;
49232
+ if (magicContextInstalled) {
49233
+ if (checkPluginDuplicates(config, configName, { fix: options.fix, configDir }, {
49234
+ warn,
49235
+ pass: (message) => {
49236
+ pass(message);
49237
+ fixed++;
49238
+ },
49239
+ info: (message) => log3.info(message)
49240
+ })) {
49241
+ writeFileAtomic(paths.opencodeConfig, `${import_comment_json11.stringify(config, null, 2)}
47743
49242
  `);
47744
- }
47745
- const allEntries = readPluginEntries(config);
47746
- if (allEntries.some(({ entry }) => isLocalPathPluginEntry(entry) && String(entry).includes("magic-context") && !isDevPathPluginEntry(entry, configDir))) {
47747
- warn("An unverifiable local OpenCode plugin path was ignored because its package name is not Magic Context");
47748
- }
47749
- if (checkOpenCodePluginEntry(config, configName, {
47750
- force: options.force,
47751
- registrationKey: pluginConfigKeyFor(hostGeneration),
47752
- configDir
47753
- }, {
47754
- pass,
47755
- warn,
47756
- fixed: (message) => {
47757
- pass(message);
47758
- fixed++;
47759
- },
47760
- autoUpdateStall: reportAutoUpdateStall
47761
- })) {
47762
- writeFileAtomic(paths.opencodeConfig, `${import_comment_json11.stringify(config, null, 2)}
49243
+ }
49244
+ const allEntries = readPluginEntries(config);
49245
+ if (allEntries.some(({ entry }) => isLocalPathPluginEntry(entry) && String(entry).includes("magic-context") && !isDevPathPluginEntry(entry, configDir))) {
49246
+ warn("An unverifiable local OpenCode plugin path was ignored because its package name is not Magic Context");
49247
+ }
49248
+ if (checkOpenCodePluginEntry(config, configName, {
49249
+ force: options.force,
49250
+ registrationKey: pluginConfigKeyFor(hostGeneration),
49251
+ configDir
49252
+ }, {
49253
+ pass,
49254
+ warn,
49255
+ fixed: (message) => {
49256
+ pass(message);
49257
+ fixed++;
49258
+ },
49259
+ autoUpdateStall: reportAutoUpdateStall
49260
+ })) {
49261
+ writeFileAtomic(paths.opencodeConfig, `${import_comment_json11.stringify(config, null, 2)}
47763
49262
  `);
49263
+ }
47764
49264
  }
47765
49265
  } catch {
47766
49266
  warn("Could not parse opencode config to verify plugin entry");
47767
49267
  }
47768
- }
47769
- const cwd = process.cwd();
47770
- const compactionEnabled = resolveCompactionEnabledForDoctor();
47771
- const conflictResult = detectConflicts(cwd, { compactionEnabled });
47772
- log3.info("Compaction check: file-based; the running server's resolved config may differ — `opencode debug config` is authoritative");
47773
- if (conflictResult.hasConflict) {
47774
- for (const reason of conflictResult.reasons) {
47775
- fail(`Conflict: ${reason}`);
47776
- }
47777
- const actions = fixConflicts(cwd, conflictResult.conflicts, { compactionEnabled });
47778
- for (const action of actions) {
47779
- pass(`Fixed: ${action}`);
47780
- fixed++;
47781
- }
47782
- if (actions.length > 0) {
47783
- warn("Restart OpenCode for conflict fixes to take effect");
47784
- }
47785
49268
  } else {
47786
- if (!compactionEnabled) {
47787
- if (conflictResult.nativeCompaction.auto || conflictResult.nativeCompaction.prune) {
47788
- pass("No conflicts detected (compaction, DCP, OMO hooks) — native compaction active (compaction-off mode)");
47789
- } else {
47790
- warn("No compaction manager is active: Magic Context compaction is off and OpenCode auto-compaction is disabled");
49269
+ magicContextStatusKnown = true;
49270
+ }
49271
+ if (!magicContextStatusKnown)
49272
+ magicContextInstalled = true;
49273
+ const hostPluginStatus = magicContextStatusKnown ? getOpenCodePluginHostStatus(openCodeHostLabel, activeInstallation.version, magicContextInstalled) : null;
49274
+ if (hostPluginStatus?.status === "info")
49275
+ log3.info(hostPluginStatus.message);
49276
+ else if (hostPluginStatus?.status === "fail")
49277
+ fail(hostPluginStatus.message);
49278
+ else if (magicContextInstalled && hostGeneration === "v2") {
49279
+ log3.info(describeOpenCode2SessionAPIRequirement(activeInstallation.version));
49280
+ }
49281
+ const pluginNpmLatest = magicContextInstalled ? await fetchNpmLatest(OPENCODE_PLUGIN_NAME) : null;
49282
+ if (magicContextInstalled) {
49283
+ const cwd = process.cwd();
49284
+ const compactionEnabled = resolveCompactionEnabledForDoctor();
49285
+ const conflictResult = detectConflicts(cwd, { compactionEnabled });
49286
+ log3.info("Compaction check: file-based; the running server's resolved config may differ — `opencode debug config` is authoritative");
49287
+ if (conflictResult.hasConflict) {
49288
+ for (const reason of conflictResult.reasons) {
49289
+ fail(`Conflict: ${reason}`);
49290
+ }
49291
+ const actions = fixConflicts(cwd, conflictResult.conflicts, { compactionEnabled });
49292
+ for (const action of actions) {
49293
+ pass(`Fixed: ${action}`);
49294
+ fixed++;
49295
+ }
49296
+ if (actions.length > 0) {
49297
+ warn("Restart OpenCode for conflict fixes to take effect");
47791
49298
  }
47792
49299
  } else {
47793
- pass("No conflicts detected (compaction, DCP, OMO hooks)");
49300
+ if (!compactionEnabled) {
49301
+ if (conflictResult.nativeCompaction.auto || conflictResult.nativeCompaction.prune) {
49302
+ pass("No conflicts detected (compaction, DCP, OMO hooks) — native compaction active (compaction-off mode)");
49303
+ } else {
49304
+ warn("No compaction manager is active: Magic Context compaction is off and OpenCode auto-compaction is disabled");
49305
+ }
49306
+ } else {
49307
+ pass("No conflicts detected (compaction, DCP, OMO hooks)");
49308
+ }
47794
49309
  }
47795
49310
  }
47796
- if (hostGeneration === "v2") {
49311
+ if (magicContextInstalled && hostGeneration === "v2") {
47797
49312
  pass("TUI sidebar loads from the plugin entry on OpenCode 2 (tui.json not used)");
47798
- } else {
49313
+ } else if (magicContextInstalled) {
47799
49314
  const tuiAdded = ensureTuiPluginEntry();
47800
49315
  if (tuiAdded) {
47801
49316
  pass("Added TUI sidebar plugin to tui.json");
@@ -47945,65 +49460,88 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
47945
49460
  warn(`RPC discovery check unavailable: ${error instanceof Error ? error.message : String(error)}`);
47946
49461
  issues++;
47947
49462
  }
47948
- const cacheResult = await clearPluginCache({
47949
- force: options.force,
47950
- latestVersion: pluginNpmLatest,
47951
- hostFiles: openCodeHostDatabaseFiles([openCodeDbResolution.path])
47952
- });
47953
- if (cacheResult.action === "cleared") {
47954
- const versionInfo = cacheResult.cached ? ` (cached: ${cacheResult.cached}${cacheResult.latest ? `, latest: ${cacheResult.latest}` : ""})` : "";
47955
- const reason = cacheResult.latest ? "outdated plugin cache" : "plugin cache (latest version check unavailable)";
47956
- pass(`Cleared ${reason}${versionInfo} — latest will download on restart`);
47957
- log3.info(` ${cacheResult.path}`);
47958
- fixed++;
47959
- } else if (cacheResult.action === "up_to_date") {
47960
- pass(`Plugin cache up to date (v${cacheResult.cached})`);
47961
- } else if (cacheResult.action === "check_unavailable") {
47962
- warn(`Plugin cache version check unavailable; preserving cached plugin${cacheResult.cached ? ` (cached: ${cacheResult.cached})` : ""}. Use doctor --force to reinstall it.`);
47963
- } else if (cacheResult.action === "in_use" || cacheResult.action === "in_use_unknown") {
47964
- const versionInfo = cacheResult.cached ? ` (cached: ${cacheResult.cached}${cacheResult.latest ? `, latest: ${cacheResult.latest}` : ""})` : "";
47965
- const why = cacheResult.action === "in_use" ? `OpenCode is running (pid ${cacheResult.pids?.join(", ")})` : cacheResult.reason ?? "could not tell whether OpenCode is running";
47966
- warn(`Plugin cache${versionInfo} was not cleared: ${why}`);
47967
- log3.info(" Quit OpenCode and rerun doctor to clear it.");
47968
- for (const path of cacheResult.paths ?? [cacheResult.path])
47969
- log3.info(` ${path}`);
47970
- issues++;
47971
- } else if (cacheResult.action === "error") {
47972
- warn(`Could not clear plugin cache: ${cacheResult.error}`);
47973
- if (cacheResult.clearedPaths && cacheResult.clearedPaths.length > 0) {
47974
- log3.info(` Cleared roots: ${cacheResult.clearedPaths.join(", ")}`);
49463
+ if (magicContextInstalled) {
49464
+ let pluginConfigUse;
49465
+ if (paths.opencodeConfigFormat !== "none") {
49466
+ try {
49467
+ pluginConfigUse = readOpenCodePluginConfigUse(import_comment_json11.parse(readFileSync28(paths.opencodeConfig, "utf-8")), dirname25(paths.opencodeConfig));
49468
+ } catch {}
47975
49469
  }
47976
- if (cacheResult.failedPaths && cacheResult.failedPaths.length > 0) {
47977
- log3.info(` Failed roots: ${cacheResult.failedPaths.join(", ")}`);
47978
- } else {
47979
- log3.info(` Manually delete: ${cacheResult.path}`);
49470
+ const cacheResult = await clearPluginCache({
49471
+ force: options.force,
49472
+ latestVersion: pluginNpmLatest,
49473
+ hostFiles: openCodeHostDatabaseFiles([openCodeDbResolution.path]),
49474
+ referencedSpecs: pluginConfigUse?.referencedSpecs.opencode1
49475
+ });
49476
+ if (cacheResult.action === "unused") {
49477
+ log3.info(describeUnusedOpenCodePluginCache({
49478
+ host: "OpenCode 1",
49479
+ spec: "latest",
49480
+ cached: cacheResult.cached,
49481
+ paths: cacheResult.paths ?? [cacheResult.path],
49482
+ loadedFrom: pluginConfigUse?.loadedFrom.opencode1
49483
+ }));
49484
+ } else if (cacheResult.action === "cleared") {
49485
+ const versionInfo = cacheResult.cached ? ` (cached: ${cacheResult.cached}${cacheResult.latest ? `, latest: ${cacheResult.latest}` : ""})` : "";
49486
+ const reason = cacheResult.latest ? "outdated plugin cache" : "plugin cache (latest version check unavailable)";
49487
+ pass(`Cleared ${reason}${versionInfo} — latest will download on restart`);
49488
+ log3.info(` ${cacheResult.path}`);
49489
+ fixed++;
49490
+ } else if (cacheResult.action === "up_to_date") {
49491
+ pass(`Plugin cache up to date (v${cacheResult.cached})`);
49492
+ } else if (cacheResult.action === "check_unavailable") {
49493
+ warn(`Plugin cache version check unavailable; preserving cached plugin${cacheResult.cached ? ` (cached: ${cacheResult.cached})` : ""}. Use doctor --force to reinstall it.`);
49494
+ } else if (cacheResult.action === "in_use" || cacheResult.action === "in_use_unknown") {
49495
+ const versionInfo = cacheResult.cached ? ` (cached: ${cacheResult.cached}${cacheResult.latest ? `, latest: ${cacheResult.latest}` : ""})` : "";
49496
+ const why = cacheResult.action === "in_use" ? `OpenCode is running (pid ${cacheResult.pids?.join(", ")})` : cacheResult.reason ?? "could not tell whether OpenCode is running";
49497
+ warn(`Plugin cache${versionInfo} was not cleared: ${why}`);
49498
+ log3.info(" Quit OpenCode and rerun doctor to clear it.");
49499
+ for (const path of cacheResult.paths ?? [cacheResult.path])
49500
+ log3.info(` ${path}`);
49501
+ issues++;
49502
+ } else if (cacheResult.action === "error") {
49503
+ warn(`Could not clear plugin cache: ${cacheResult.error}`);
49504
+ if (cacheResult.clearedPaths && cacheResult.clearedPaths.length > 0) {
49505
+ log3.info(` Cleared roots: ${cacheResult.clearedPaths.join(", ")}`);
49506
+ }
49507
+ if (cacheResult.failedPaths && cacheResult.failedPaths.length > 0) {
49508
+ log3.info(` Failed roots: ${cacheResult.failedPaths.join(", ")}`);
49509
+ } else {
49510
+ log3.info(` Manually delete: ${cacheResult.path}`);
49511
+ }
49512
+ issues++;
49513
+ } else if (hostGeneration !== "v2") {
49514
+ pass("Plugin cache clean (no cached version found)");
49515
+ }
49516
+ let v2DistTag;
49517
+ if (paths.opencodeConfigFormat !== "none") {
49518
+ try {
49519
+ v2DistTag = configuredOpenCodeV2DistTag(import_comment_json11.parse(readFileSync28(paths.opencodeConfig, "utf-8")));
49520
+ } catch {}
49521
+ }
49522
+ const v2Cache = reportOpenCodeV2PluginCache(checkOpenCodeV2PluginCache({
49523
+ fix: options.fix,
49524
+ force: options.force,
49525
+ latestVersion: v2DistTag ? await fetchNpmLatest(OPENCODE_PLUGIN_NAME, v2DistTag) : pluginNpmLatest,
49526
+ distTag: v2DistTag,
49527
+ hostFiles: openCodeHostDatabaseFiles([openCodeDbResolution.path]),
49528
+ referencedSpecs: pluginConfigUse?.referencedSpecs.opencode2
49529
+ }), { pass, warn, info: (message) => log3.info(message) }, {
49530
+ reportMissing: hostGeneration === "v2",
49531
+ loadedFrom: pluginConfigUse?.loadedFrom.opencode2
49532
+ });
49533
+ if (v2Cache.fixed)
49534
+ fixed++;
49535
+ if (v2Cache.issue)
49536
+ issues++;
49537
+ const sharedDbVersion = readContextDbSchemaVersion(dbPath);
49538
+ if (sharedDbVersion !== null) {
49539
+ reportCachedPluginFences(compareCachedPluginFences(listCachedOpenCodePluginFences({
49540
+ referencedSpecs: pluginConfigUse?.referencedSpecs
49541
+ }), sharedDbVersion), { pass, fail, info: (message) => log3.info(message) });
47980
49542
  }
47981
- issues++;
47982
- } else if (hostGeneration !== "v2") {
47983
- pass("Plugin cache clean (no cached version found)");
47984
- }
47985
- let v2DistTag;
47986
- if (paths.opencodeConfigFormat !== "none") {
47987
- try {
47988
- v2DistTag = configuredOpenCodeV2DistTag(import_comment_json11.parse(readFileSync28(paths.opencodeConfig, "utf-8")));
47989
- } catch {}
47990
- }
47991
- const v2Cache = reportOpenCodeV2PluginCache(checkOpenCodeV2PluginCache({
47992
- fix: options.fix,
47993
- force: options.force,
47994
- latestVersion: v2DistTag ? await fetchNpmLatest(OPENCODE_PLUGIN_NAME, v2DistTag) : pluginNpmLatest,
47995
- distTag: v2DistTag,
47996
- hostFiles: openCodeHostDatabaseFiles([openCodeDbResolution.path])
47997
- }), { pass, warn, info: (message) => log3.info(message) }, { reportMissing: hostGeneration === "v2" });
47998
- if (v2Cache.fixed)
47999
- fixed++;
48000
- if (v2Cache.issue)
48001
- issues++;
48002
- const sharedDbVersion = readContextDbSchemaVersion(dbPath);
48003
- if (sharedDbVersion !== null) {
48004
- reportCachedPluginFences(compareCachedPluginFences(listCachedOpenCodePluginFences(), sharedDbVersion), { pass, fail, info: (message) => log3.info(message) });
48005
49543
  }
48006
- {
49544
+ if (magicContextInstalled) {
48007
49545
  const ageWarnings = collectNpmReleaseAgeWarnings();
48008
49546
  if (ageWarnings.length > 0) {
48009
49547
  log3.warn("npm min-release-age restriction detected — this can prevent OpenCode from installing the latest plugin version");
@@ -48015,17 +49553,19 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
48015
49553
  issues++;
48016
49554
  }
48017
49555
  }
48018
- const logHarnesses = [
48019
- ...hostGeneration === "v1" ? ["opencode"] : [],
48020
- ...hostGeneration === "v2" || storeGeneration === "v2" ? ["opencode2"] : []
48021
- ];
48022
- const logFiles = logHarnesses.flatMap((harness) => inspectMagicContextLogs(harness)).filter((file, index, all) => all.findIndex((other) => other.path === file.path) === index);
48023
- const existingLogFiles = logFiles.filter((file) => file.exists);
48024
- if (existingLogFiles.length === 0) {
48025
- log3.info(`No plugin log file yet; checked: ${logFiles.map((file) => file.path).join(", ")}`);
48026
- } else {
48027
- for (const file of existingLogFiles) {
48028
- log3.info(`Log file read: ${formatLogFileInspection(file)}`);
49556
+ if (magicContextInstalled) {
49557
+ const logHarnesses = [
49558
+ ...hostGeneration === "v1" ? ["opencode"] : [],
49559
+ ...hostGeneration === "v2" || storeGeneration === "v2" ? ["opencode2"] : []
49560
+ ];
49561
+ const logFiles = logHarnesses.flatMap((harness) => inspectMagicContextLogs(harness)).filter((file, index, all) => all.findIndex((other) => other.path === file.path) === index);
49562
+ const existingLogFiles = logFiles.filter((file) => file.exists);
49563
+ if (existingLogFiles.length === 0) {
49564
+ log3.info(`No plugin log file yet; checked: ${logFiles.map((file) => file.path).join(", ")}`);
49565
+ } else {
49566
+ for (const file of existingLogFiles) {
49567
+ log3.info(`Log file read: ${formatLogFileInspection(file)}`);
49568
+ }
48029
49569
  }
48030
49570
  }
48031
49571
  const diagnostics = await collectDiagnostics();
@@ -48206,13 +49746,12 @@ function resolveDreamerTaskModel(args) {
48206
49746
  const taskQualifier = taskBlock?.[args.harness === "opencode" ? "variant" : "thinking_level"];
48207
49747
  const harnessQualifier = harnessBlock[args.harness === "opencode" ? "variant" : "thinking_level"] ?? normalizeModelEntry(harnessBlock.model, args.harness)?.qualifier;
48208
49748
  const taskHasModel = taskBlock !== undefined && hasOwn2(taskBlock, "model");
48209
- const usesMuralModel = !taskHasModel && args.task === "compress-cues" && Boolean(readString(args.muralModel));
48210
- const primarySource = taskHasModel ? taskBlock?.model : usesMuralModel ? args.muralModel : harnessBlock.model;
49749
+ const primarySource = taskHasModel ? taskBlock?.model : harnessBlock.model;
48211
49750
  const fallbackSource = taskBlock !== undefined && hasOwn2(taskBlock, "fallback_models") ? taskBlock.fallback_models : harnessBlock.fallback_models;
48212
49751
  return {
48213
49752
  primary: resolvePrimaryEntry({
48214
49753
  entry: primarySource,
48215
- defaultQualifier: usesMuralModel ? harnessQualifier : taskQualifier ?? harnessQualifier,
49754
+ defaultQualifier: taskQualifier ?? harnessQualifier,
48216
49755
  harness: args.harness
48217
49756
  }),
48218
49757
  fallbacks: resolveFallbackEntries(fallbackSource, args.harness),
@@ -48223,13 +49762,12 @@ function resolveDreamerTaskModel(args) {
48223
49762
  }
48224
49763
 
48225
49764
  // ../plugin/src/features/magic-context/dreamer/task-config.ts
48226
- function buildDreamTaskRuntimeConfigs(dreamer, harness, language, muralModel) {
48227
- return CANONICAL_DREAM_TASKS.map((task) => {
49765
+ function buildDreamTaskRuntimeConfigs(dreamer, harness, language, smartNotesEnabled = true) {
49766
+ return availableDreamTasks(smartNotesEnabled).map((task) => {
48228
49767
  const resolved = resolveDreamerTaskModel({
48229
49768
  config: { dreamer },
48230
49769
  harness,
48231
- task,
48232
- muralModel
49770
+ task
48233
49771
  });
48234
49772
  return {
48235
49773
  task,
@@ -48337,7 +49875,7 @@ function runnablePiModelChains(config, harness) {
48337
49875
  });
48338
49876
  }
48339
49877
  if (isDreamerRunnable(config)) {
48340
- for (const task of buildDreamTaskRuntimeConfigs(config.dreamer, harness, config.language, config.mural?.model)) {
49878
+ for (const task of buildDreamTaskRuntimeConfigs(config.dreamer, harness, config.language, config.smart_notes?.enabled)) {
48341
49879
  if (task.schedule.trim() === "")
48342
49880
  continue;
48343
49881
  const chain = [task.model, ...task.fallbackModels ?? []].filter((entry) => entry !== undefined);
@@ -49126,26 +50664,14 @@ async function runHealthChecks2(options) {
49126
50664
  const repairPlan = {
49127
50665
  addPackageEntry: false,
49128
50666
  writeUserConfig: false,
49129
- clearCachePaths: []
50667
+ clearCachePaths: [],
50668
+ reasoningAgeConfigPaths: []
49130
50669
  };
49131
50670
  const self = options.deps.selfVersion();
49132
50671
  const pi = options.deps.detectPiBinary();
49133
- if (!pi) {
50672
+ const piVersion = pi ? options.deps.getPiVersion(pi.path) : null;
50673
+ if (!pi)
49134
50674
  add2(results, "fail", "Pi binary not found on PATH or at ~/.pi/bin/pi");
49135
- } else {
49136
- const version = options.deps.getPiVersion(pi.path);
49137
- if (version === null) {
49138
- add2(results, "fail", `Pi CLI was found at ${pi.path} but could not be executed`);
49139
- } else {
49140
- add2(results, "pass", `Pi ${version} detected at ${pi.path}`);
49141
- }
49142
- const compare = compareSemver(version, MIN_PI_VERSION);
49143
- if (compare !== null && compare < 0) {
49144
- add2(results, "fail", `Pi ${version} is older than required ${MIN_PI_VERSION}. Subagents (historian/dreamer) use the long-form \`--extension\` flag introduced in Pi 0.71.0; older versions hard-fail with "Unknown option". Run \`pi update\` (or \`npm install -g @earendil-works/pi-coding-agent@latest\`).`);
49145
- } else if (version) {
49146
- add2(results, "pass", `Pi version meets minimum ${MIN_PI_VERSION} requirement`);
49147
- }
49148
- }
49149
50675
  const latest = options.deps.getLatestNpmVersion();
49150
50676
  const latestCompare = latest ? compareSemver(self, latest) : null;
49151
50677
  if (latest && latestCompare === -1) {
@@ -49159,8 +50685,11 @@ async function runHealthChecks2(options) {
49159
50685
  }
49160
50686
  const settingsPath = getPiUserExtensionsPath();
49161
50687
  let packages = [];
50688
+ let piPluginInstalled = false;
49162
50689
  if (!existsSync47(settingsPath)) {
49163
- add2(results, "fail", `Pi settings not found at ${settingsPath}`);
50690
+ if (pi) {
50691
+ add2(results, "info", `${piVersion ? `Pi ${piVersion}` : "Pi"} found; Magic Context is not installed there (run setup to add it)`);
50692
+ }
49164
50693
  repairPlan.addPackageEntry = true;
49165
50694
  } else {
49166
50695
  const parsed = readJsonc2(settingsPath);
@@ -49168,15 +50697,31 @@ async function runHealthChecks2(options) {
49168
50697
  add2(results, "fail", `Could not parse Pi settings ${settingsPath}: ${parsed.error}`);
49169
50698
  } else {
49170
50699
  packages = packagesFrom(parsed.value);
49171
- add2(results, "pass", `Pi settings found at ${settingsPath}`);
49172
50700
  if (hasPiMagicContextPackage(packages)) {
50701
+ piPluginInstalled = true;
50702
+ add2(results, "pass", `Pi settings found at ${settingsPath}`);
49173
50703
  add2(results, "pass", `${PI_PACKAGE_SOURCE} is registered in packages[]`);
49174
50704
  } else {
49175
- add2(results, "fail", `${PI_PACKAGE_SOURCE} is missing from packages[]`);
50705
+ if (pi) {
50706
+ add2(results, "info", `${piVersion ? `Pi ${piVersion}` : "Pi"} found; Magic Context is not installed there (run setup to add it)`);
50707
+ }
49176
50708
  repairPlan.addPackageEntry = true;
49177
50709
  }
49178
50710
  }
49179
50711
  }
50712
+ if (pi && piPluginInstalled) {
50713
+ if (piVersion === null) {
50714
+ add2(results, "fail", `Pi CLI was found at ${pi.path} but could not be executed`);
50715
+ } else {
50716
+ add2(results, "pass", `Pi ${piVersion} detected at ${pi.path}`);
50717
+ }
50718
+ const compare = compareSemver(piVersion, MIN_PI_VERSION);
50719
+ if (compare !== null && compare < 0) {
50720
+ add2(results, "fail", `Pi ${piVersion} is older than required ${MIN_PI_VERSION}. Subagents (historian/dreamer) use the long-form \`--extension\` flag introduced in Pi 0.71.0; older versions hard-fail with "Unknown option". Run \`pi update\` (or \`npm install -g @earendil-works/pi-coding-agent@latest\`).`);
50721
+ } else if (piVersion) {
50722
+ add2(results, "pass", `Pi version meets minimum ${MIN_PI_VERSION} requirement`);
50723
+ }
50724
+ }
49180
50725
  const userConfigPath = getPiUserConfigPath();
49181
50726
  const projectPath = projectConfigPath(options.cwd);
49182
50727
  for (const [label, path, required] of [
@@ -49197,6 +50742,10 @@ async function runHealthChecks2(options) {
49197
50742
  continue;
49198
50743
  }
49199
50744
  const parsed = readMagicContextJsonc(path, label);
50745
+ if (!parsed.error && Object.hasOwn(parsed.value, "clear_reasoning_age")) {
50746
+ repairPlan.reasoningAgeConfigPaths.push(path);
50747
+ add2(results, "warn", `${label} clear_reasoning_age is deprecated and ignored; doctor removes it`);
50748
+ }
49200
50749
  if (parsed.error)
49201
50750
  add2(results, "fail", `${label} magic-context.jsonc is invalid JSONC: ${parsed.error}`);
49202
50751
  else
@@ -49221,7 +50770,8 @@ async function runHealthChecks2(options) {
49221
50770
  } else if (historianModel.startsWith("github-copilot/") && historianThinkingLevel) {
49222
50771
  add2(results, "pass", `historian.model "${historianModel}" has thinking_level "${historianThinkingLevel}" configured`);
49223
50772
  }
49224
- checkPiModelChains(results, pi, loadedConfig.config, options.deps);
50773
+ if (piPluginInstalled)
50774
+ checkPiModelChains(results, pi, loadedConfig.config, options.deps);
49225
50775
  const storage = getMagicContextStorageResolution();
49226
50776
  const storageDir = storage.path;
49227
50777
  const dbPath = join41(storageDir, "context.db");
@@ -49344,7 +50894,7 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
49344
50894
  }
49345
50895
  } else if (loadedConfig.config.embedding.provider === "off") {
49346
50896
  add2(results, "info", "Embedding provider disabled");
49347
- } else {
50897
+ } else if (piPluginInstalled) {
49348
50898
  let runtimeReported = false;
49349
50899
  let firstFallback = null;
49350
50900
  const brokenWarnings = [];
@@ -49383,38 +50933,40 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
49383
50933
  }
49384
50934
  }
49385
50935
  }
49386
- const piEntries = packages.filter(isConfiguredPiMagicContextEntry2).map(describePiPackageEntry);
49387
- if (piEntries.length > 1) {
49388
- add2(results, "fail", `Multiple magic-context entries in Pi packages[] — this loads the plugin twice: ${piEntries.join(", ")}`);
49389
- } else {
49390
- add2(results, "pass", "No conflicting magic-context entries in Pi packages[]");
49391
- }
49392
- const otherExtensions = packages.filter((entry) => !isConfiguredPiMagicContextEntry2(entry)).map(describePiPackageEntry);
49393
- if (otherExtensions.length > 0) {
49394
- add2(results, "info", `Other Pi extensions registered: ${otherExtensions.join(", ")}`);
49395
- } else {
49396
- add2(results, "info", "No other Pi extensions listed in settings.json");
49397
- }
49398
- const configuredEntry = packages.find(isPiMagicContextPackageEntry);
49399
- const configuredSpecifier = getPiMagicContextPackageSpecifier(configuredEntry);
49400
- const expectedPluginVersion = pinnedVersionFromPackageSpecifier(configuredSpecifier) ?? latest ?? null;
49401
- const staleCaches = findPiMagicContextCacheDirs(options.cwd, expectedPluginVersion, options.force === true);
49402
- if (staleCaches.length > 0) {
49403
- repairPlan.clearCachePaths = staleCaches.map((entry) => entry.path);
49404
- add2(results, "warn", `Stale Pi extension cache found: ${staleCaches.map((entry) => `${entry.path}${entry.version ? ` (v${entry.version})` : ""}`).join(", ")}`);
49405
- } else {
49406
- add2(results, "pass", "Pi extension cache clean (no stale cached package found)");
49407
- }
49408
- const logFiles = inspectMagicContextLogs("pi");
49409
- const existingLogFiles = logFiles.filter((file) => file.exists);
49410
- if (existingLogFiles.length === 0) {
49411
- add2(results, "info", `No plugin log file yet; checked: ${logFiles.map((file) => file.path).join(", ")}`);
49412
- } else {
49413
- for (const file of existingLogFiles) {
49414
- add2(results, "info", `Log file read: ${formatLogFileInspection(file)}`);
50936
+ if (piPluginInstalled) {
50937
+ const piEntries = packages.filter(isConfiguredPiMagicContextEntry2).map(describePiPackageEntry);
50938
+ if (piEntries.length > 1) {
50939
+ add2(results, "fail", `Multiple magic-context entries in Pi packages[] — this loads the plugin twice: ${piEntries.join(", ")}`);
50940
+ } else {
50941
+ add2(results, "pass", "No conflicting magic-context entries in Pi packages[]");
50942
+ }
50943
+ const otherExtensions = packages.filter((entry) => !isConfiguredPiMagicContextEntry2(entry)).map(describePiPackageEntry);
50944
+ if (otherExtensions.length > 0) {
50945
+ add2(results, "info", `Other Pi extensions registered: ${otherExtensions.join(", ")}`);
50946
+ } else {
50947
+ add2(results, "info", "No other Pi extensions listed in settings.json");
50948
+ }
50949
+ const configuredEntry = packages.find(isPiMagicContextPackageEntry);
50950
+ const configuredSpecifier = getPiMagicContextPackageSpecifier(configuredEntry);
50951
+ const expectedPluginVersion = pinnedVersionFromPackageSpecifier(configuredSpecifier) ?? latest ?? null;
50952
+ const staleCaches = findPiMagicContextCacheDirs(options.cwd, expectedPluginVersion, options.force === true);
50953
+ if (staleCaches.length > 0) {
50954
+ repairPlan.clearCachePaths = staleCaches.map((entry) => entry.path);
50955
+ add2(results, "warn", `Stale Pi extension cache found: ${staleCaches.map((entry) => `${entry.path}${entry.version ? ` (v${entry.version})` : ""}`).join(", ")}`);
50956
+ } else {
50957
+ add2(results, "pass", "Pi extension cache clean (no stale cached package found)");
50958
+ }
50959
+ const logFiles = inspectMagicContextLogs("pi");
50960
+ const existingLogFiles = logFiles.filter((file) => file.exists);
50961
+ if (existingLogFiles.length === 0) {
50962
+ add2(results, "info", `No plugin log file yet; checked: ${logFiles.map((file) => file.path).join(", ")}`);
50963
+ } else {
50964
+ for (const file of existingLogFiles) {
50965
+ add2(results, "info", `Log file read: ${formatLogFileInspection(file)}`);
50966
+ }
50967
+ const lastLine = readLogLines(existingLogFiles).at(-1);
50968
+ add2(results, "info", `Last plugin log line: ${lastLine ? sanitizeDiagnosticText(lastLine) : "<empty log>"}`);
49415
50969
  }
49416
- const lastLine = readLogLines(existingLogFiles).at(-1);
49417
- add2(results, "info", `Last plugin log line: ${lastLine ? sanitizeDiagnosticText(lastLine) : "<empty log>"}`);
49418
50970
  }
49419
50971
  const diagnosticsForDumps = await collectDiagnostics2(options.cwd);
49420
50972
  const dumpBuckets = diagnosticsForDumps.historianDumps.byProject;
@@ -49452,6 +51004,21 @@ function writeDefaultMagicContextConfig(path) {
49452
51004
  }
49453
51005
  function repair2(plan, prompts) {
49454
51006
  let fixed = 0;
51007
+ for (const path of plan.reasoningAgeConfigPaths) {
51008
+ const content = readFileSync30(path, "utf8");
51009
+ const parsed = readJsonc2(path);
51010
+ if (parsed.error)
51011
+ continue;
51012
+ const messages = removeDeprecatedReasoningAge(parsed.value);
51013
+ if (messages.length === 0)
51014
+ continue;
51015
+ writeFileAtomic(path, removeJsoncValue(content, ["clear_reasoning_age"]), {
51016
+ ownerOnly: true
51017
+ });
51018
+ for (const message of messages)
51019
+ prompts.log.success(message);
51020
+ fixed++;
51021
+ }
49455
51022
  if (plan.addPackageEntry) {
49456
51023
  const settingsPath = getPiUserExtensionsPath();
49457
51024
  try {
@@ -49678,6 +51245,7 @@ var init_doctor_pi = __esm(async () => {
49678
51245
  init_tick_failure();
49679
51246
  init_embedding_synapse();
49680
51247
  init_data_path();
51248
+ init_jsonc_edit();
49681
51249
  init_jsonc_parser();
49682
51250
  init_config2();
49683
51251
  init_model_chain_health();
@@ -49794,7 +51362,8 @@ async function dispatchDoctor(adapter, options) {
49794
51362
  force: options.force,
49795
51363
  fix: options.fix,
49796
51364
  issue: options.issue,
49797
- report: options.report
51365
+ report: options.report,
51366
+ verbose: options.verbose
49798
51367
  });
49799
51368
  }
49800
51369
  case "pi": {
@@ -49958,6 +51527,7 @@ var DOCTOR_HELP = [
49958
51527
  " --fix Repair safe, Magic Context-owned store rows and clear an outdated OpenCode 2 plugin cache",
49959
51528
  " --force Force-clear the plugin cache",
49960
51529
  " --clear Interactive cache cleanup picker",
51530
+ " --verbose Print long listings in full instead of a capped summary",
49961
51531
  " --prune-discovery [--yes] List RPC discovery records that block offline maintenance, then remove them after confirmation",
49962
51532
  " --issue Collect diagnostics and open a GitHub issue",
49963
51533
  " --report <path> Write the issue diagnostics to <path> without prompting",
@@ -50216,6 +51786,7 @@ async function runCommand(command, rest) {
50216
51786
  issue: rest.includes("--issue") || report !== null,
50217
51787
  ...report !== null ? { report } : {},
50218
51788
  clear: rest.includes("--clear"),
51789
+ verbose: rest.includes("--verbose"),
50219
51790
  checkV22Backfill: rest.includes("--check-v22-backfill"),
50220
51791
  retryV22Backfill: rest.includes("--retry-v22-backfill"),
50221
51792
  ...rekeyV22DirIdentity !== null ? { rekeyV22DirIdentity } : {},