@cortexkit/magic-context 0.47.0 → 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 +1857 -459
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3476,6 +3476,15 @@ function migrateLegacyExperimental(rawConfig, warnings) {
3476
3476
  return patched;
3477
3477
  }
3478
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
+
3479
3488
  // ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/core/core.js
3480
3489
  function $constructor(name, initializer, params) {
3481
3490
  function init(inst, def) {
@@ -9315,7 +9324,7 @@ var init_magic_context = __esm(() => {
9315
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."),
9316
9325
  mural: object({
9317
9326
  enabled: boolean2().default(false),
9318
- 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 })
9319
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."),
9320
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.'),
9321
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."),
@@ -9325,8 +9334,9 @@ var init_magic_context = __esm(() => {
9325
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."),
9326
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."),
9327
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."),
9328
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.")
9329
- }).default({ retina_handoff: false }).describe("Smart-note ownership transition controls."),
9339
+ }).default({ enabled: true, retina_handoff: false }).describe("Smart-note ownership transition controls."),
9330
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).`),
9331
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."),
9332
9342
  output_reserve: union([
@@ -9431,7 +9441,6 @@ var init_magic_context = __esm(() => {
9431
9441
  });
9432
9442
  LIVE_RELOAD_CONFIG_PATHS = [
9433
9443
  "cache_ttl",
9434
- "mural.model",
9435
9444
  "toast_duration_ms",
9436
9445
  "historian.opencode.model",
9437
9446
  "historian.opencode.fallback_models",
@@ -9612,16 +9621,6 @@ function stripEscalationAtExecutableSite(block, path, removed) {
9612
9621
  }
9613
9622
  }
9614
9623
  }
9615
- function stripNestedMuralModels(node, path, removed) {
9616
- for (const [key, value] of Object.entries(node)) {
9617
- if (key === "mural" && isPlainObject2(value) && "model" in value) {
9618
- delete value.model;
9619
- removed.push(`${path}.${key}.model`);
9620
- } else if (isPlainObject2(value)) {
9621
- stripNestedMuralModels(value, `${path}.${key}`, removed);
9622
- }
9623
- }
9624
- }
9625
9624
  function isValidPercentageThreshold(value) {
9626
9625
  return typeof value === "number" && Number.isFinite(value) && value >= 20 && value <= 90;
9627
9626
  }
@@ -9762,6 +9761,11 @@ function stripUnsafeProjectConfigFields(projectRaw) {
9762
9761
  delete storage.enforce_private_permissions;
9763
9762
  warnings.push("Ignoring storage.enforce_private_permissions from project config (security: only user-level config may opt into externally managed shared storage permissions).");
9764
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
+ }
9765
9769
  const promptSurface = projectRaw.prompt_surface;
9766
9770
  if (isPlainObject2(promptSurface)) {
9767
9771
  const removed = [];
@@ -9860,27 +9864,6 @@ function stripUnsafeProjectConfigFields(projectRaw) {
9860
9864
  delete dreamer.runner;
9861
9865
  warnings.push("Ignoring dreamer.runner from project config (security: which process and provider account run dreamer completions is a user-level setting).");
9862
9866
  }
9863
- const mural = projectRaw.mural;
9864
- if (isPlainObject2(mural) && "model" in mural) {
9865
- delete mural.model;
9866
- 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).");
9867
- }
9868
- const experimental = projectRaw.experimental;
9869
- const legacyMural = isPlainObject2(experimental) ? experimental.mural : undefined;
9870
- if (isPlainObject2(legacyMural) && "model" in legacyMural) {
9871
- delete legacyMural.model;
9872
- 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).");
9873
- }
9874
- const nestedMuralRemoved = [];
9875
- for (const agentKey of HIDDEN_AGENT_KEYS) {
9876
- const block = projectRaw[agentKey];
9877
- if (!isPlainObject2(block))
9878
- continue;
9879
- stripNestedMuralModels(block, agentKey, nestedMuralRemoved);
9880
- }
9881
- if (nestedMuralRemoved.length > 0) {
9882
- 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).`);
9883
- }
9884
9867
  return warnings;
9885
9868
  }
9886
9869
  function constrainProjectThresholdOverrides(args) {
@@ -10929,6 +10912,7 @@ function parsePluginConfig(rawConfig, recoveredTopLevelKeys = []) {
10929
10912
  }
10930
10913
  preMigrationWarnings.push(...misplacedAgentModelWarnings(rawConfig));
10931
10914
  const migratedExperimental = migrateLegacyExperimental(configWithoutRemovedAgent, preMigrationWarnings);
10915
+ warnDeprecatedMuralModel(migratedExperimental, preMigrationWarnings);
10932
10916
  const migratedDreamer = migrateDreamerV2(migratedExperimental, preMigrationWarnings);
10933
10917
  const migrated = migrateLegacyAgentEnabledInMemory(migratedDreamer, preMigrationWarnings);
10934
10918
  const parsed = MagicContextConfigSchema.safeParse(migrated);
@@ -12856,17 +12840,23 @@ __export(exports_sqlite, {
12856
12840
  detectSqliteRuntime: () => detectSqliteRuntime,
12857
12841
  getSqliteDatabasePath: () => getSqliteDatabasePath,
12858
12842
  getSqliteMemoryStats: () => getSqliteMemoryStats,
12843
+ getTagIdentityWriteGeneration: () => getTagIdentityWriteGeneration,
12859
12844
  guardSqliteTransformPass: () => guardSqliteTransformPass,
12845
+ indexCoordinationInput: () => indexCoordinationInput,
12860
12846
  isTransientSqliteError: () => isTransientSqliteError,
12861
12847
  loadSqliteModule: () => loadSqliteModule,
12848
+ mayChangeTagIdentity: () => mayChangeTagIdentity,
12862
12849
  prepareCachedStatement: () => prepareCachedStatement,
12863
12850
  registerSlowWriteReporter: () => registerSlowWriteReporter,
12864
12851
  registerSqliteDiagnosticSink: () => registerSqliteDiagnosticSink,
12852
+ releaseIndexOwner: () => releaseIndexOwner,
12865
12853
  withAsyncPrivilegedWriter: () => withAsyncPrivilegedWriter,
12854
+ withIndexChunk: () => withIndexChunk,
12866
12855
  withPrivilegedWriter: () => withPrivilegedWriter,
12867
12856
  withSqliteBackgroundWriter: () => withSqliteBackgroundWriter,
12868
12857
  withSqliteTransformPass: () => withSqliteTransformPass,
12869
- withoutSqliteTransformPass: () => withoutSqliteTransformPass
12858
+ withoutSqliteTransformPass: () => withoutSqliteTransformPass,
12859
+ yieldIndexChunk: () => yieldIndexChunk
12870
12860
  });
12871
12861
  import { AsyncLocalStorage } from "node:async_hooks";
12872
12862
  import { statSync as statSync4 } from "node:fs";
@@ -12900,10 +12890,31 @@ function isModuleNotFoundError(error, specifier) {
12900
12890
  return false;
12901
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");
12902
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
+ }
12903
12914
  async function loadSqliteModule(runtime = detectSqliteRuntime(), importer = importSqliteModule) {
12904
12915
  const specifier = runtime === "Bun" ? bunSpec : nodeSpec;
12905
12916
  try {
12906
- return await importer(specifier);
12917
+ return await importSqliteModuleForRuntime(runtime, specifier, importer);
12907
12918
  } catch (error) {
12908
12919
  if (isModuleNotFoundError(error, specifier)) {
12909
12920
  throw new SqliteRuntimeUnavailableError(runtime, specifier, error);
@@ -12911,6 +12922,67 @@ async function loadSqliteModule(runtime = detectSqliteRuntime(), importer = impo
12911
12922
  throw error;
12912
12923
  }
12913
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
+ }
12914
12986
  function installTransactionRouting(db, readonly) {
12915
12987
  const nativeExec = db.exec.bind(db);
12916
12988
  Object.defineProperty(db, "exec", {
@@ -12929,8 +13001,14 @@ function installTransactionRouting(db, readonly) {
12929
13001
  }
12930
13002
  if (WRITER_TRANSACTION_END.test(sql) && openWriterTransactions.has(db))
12931
13003
  return endWriterTransaction(db, () => nativeExec(sql), sql);
12932
- if (!/^\s*(?:SELECT|EXPLAIN|PRAGMA|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\b/i.test(sql))
12933
- 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
+ }
12934
13012
  return nativeExec(sql);
12935
13013
  }
12936
13014
  });
@@ -12941,13 +13019,23 @@ function installTransactionRouting(db, readonly) {
12941
13019
  value: (sql) => {
12942
13020
  const statement = nativePrepare(sql);
12943
13021
  if (!readonly && !/^\s*(?:SELECT|EXPLAIN|PRAGMA)\b/i.test(sql)) {
13022
+ const changesTagIdentity = mayChangeTagIdentity(sql);
12944
13023
  for (const method of ["run", "get", "all"]) {
12945
13024
  const execute = statement[method].bind(statement);
12946
13025
  Object.defineProperty(statement, method, {
12947
13026
  configurable: true,
12948
- 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
+ }
12949
13035
  });
12950
13036
  }
13037
+ if (changesTagIdentity)
13038
+ wrapRemainingTagWriteForms(statement);
12951
13039
  }
12952
13040
  return statement;
12953
13041
  }
@@ -12995,6 +13083,35 @@ function installTransactionRouting(db, readonly) {
12995
13083
  }
12996
13084
  });
12997
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
+ }
12998
13115
  function trackSqliteConnection(db, filename, options) {
12999
13116
  const originalClose = db.close.bind(db);
13000
13117
  const sequence = nextSqliteConnectionSequence++;
@@ -13054,6 +13171,10 @@ function getSqliteDatabasePath(db) {
13054
13171
  return connection.filename === ":memory:" || connection.filename === "<buffer>" ? null : connection.filename;
13055
13172
  }
13056
13173
  }
13174
+ const native = db;
13175
+ const path = native.filename ?? native.name;
13176
+ if (typeof path === "string" && path !== ":memory:" && path !== "")
13177
+ return path;
13057
13178
  return null;
13058
13179
  }
13059
13180
  function pragmaValue(db, name) {
@@ -13135,6 +13256,15 @@ function getSqliteMemoryStats() {
13135
13256
  connections
13136
13257
  };
13137
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
+ }
13138
13268
  function ownPreparedStatement(db, statement) {
13139
13269
  const finalizable = statement;
13140
13270
  if (typeof finalizable.finalize !== "function")
@@ -13158,7 +13288,7 @@ function ownPreparedStatement(db, statement) {
13158
13288
  }
13159
13289
  }
13160
13290
  references.clear();
13161
- const result = close(...args);
13291
+ const result = closeReleasingStatements(close, args);
13162
13292
  if (failure)
13163
13293
  throw failure;
13164
13294
  return result;
@@ -13188,22 +13318,29 @@ function withSqliteTransformPass(operation) {
13188
13318
  const existing = transformPassScope.getStore();
13189
13319
  if (existing?.active)
13190
13320
  return operation();
13191
- const lease = { active: true, remainingWaitMs: FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS };
13192
- return transformPassScope.run(lease, () => {
13193
- try {
13194
- const result = operation();
13195
- if (result && typeof result.then === "function") {
13196
- return Promise.resolve(result).finally(() => {
13197
- lease.active = false;
13198
- });
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;
13199
13340
  }
13200
- lease.active = false;
13201
- return result;
13202
- } catch (error) {
13203
- lease.active = false;
13204
- throw error;
13205
- }
13206
- });
13341
+ });
13342
+ };
13343
+ return typeof reservation === "function" ? run(reservation) : reservation.then(run);
13207
13344
  }
13208
13345
  function withoutSqliteTransformPass(operation) {
13209
13346
  return transformPassScope.run(undefined, operation);
@@ -13291,18 +13428,20 @@ function acquireShort(db, acquire, site) {
13291
13428
  }
13292
13429
  function endWriterTransaction(db, end, sql) {
13293
13430
  let ended = false;
13431
+ const endStarted = performance.now();
13294
13432
  try {
13295
13433
  const result = end();
13296
13434
  ended = true;
13297
13435
  return result;
13298
13436
  } finally {
13299
13437
  const open = openWriterTransactions.get(db);
13438
+ const endMs = performance.now() - endStarted;
13300
13439
  if (open && !isInTransaction(db)) {
13301
13440
  openWriterTransactions.delete(db);
13302
13441
  const holdMs = performance.now() - open.acquiredAt;
13303
13442
  if (open.acquireMs >= SLOW_WRITER_LOG_MS || holdMs >= SLOW_WRITER_LOG_MS) {
13304
13443
  const outcome = /^\s*ROLLBACK/i.test(sql) ? "rolled_back" : ended ? "committed" : "failed";
13305
- 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}`);
13306
13445
  }
13307
13446
  }
13308
13447
  }
@@ -13442,7 +13581,55 @@ function withPrivilegedWriter(db, operation) {
13442
13581
  throw error;
13443
13582
  }
13444
13583
  }
13445
- 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));
13446
13633
  var init_sqlite = __esm(async () => {
13447
13634
  bunSpec = "bun:" + "sqlite";
13448
13635
  nodeSpec = "node:" + "sqlite";
@@ -13462,6 +13649,19 @@ var init_sqlite = __esm(async () => {
13462
13649
  sqliteModule = await loadSqliteModule(detectedRuntime);
13463
13650
  DatabaseImpl = isBun ? sqliteModule.Database : buildNodeSqliteDatabaseClass(sqliteModule.DatabaseSync);
13464
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
+ ]);
13465
13665
  TrackedDatabase = new Proxy(DatabaseImpl, {
13466
13666
  construct(target, args) {
13467
13667
  const db = Reflect.construct(target, args, target);
@@ -13486,6 +13686,11 @@ var init_sqlite = __esm(async () => {
13486
13686
  WRITER_TRANSACTION_END = /^\s*(?:COMMIT|END|ROLLBACK)(?:\s+TRANSACTION)?\s*;?\s*$/i;
13487
13687
  writerAcquisitionScope = new AsyncLocalStorage;
13488
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);
13489
13694
  });
13490
13695
 
13491
13696
  // src/lib/docs-proposal-gitignore.ts
@@ -22933,6 +23138,22 @@ function readConfiguredOpenCodePluginSpec(config) {
22933
23138
  }
22934
23139
  return "latest";
22935
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
+ }
22936
23157
  function pluginEntrySpecifier(entry) {
22937
23158
  if (typeof entry === "string")
22938
23159
  return entry;
@@ -23158,6 +23379,9 @@ function checkOpenCodeV2PluginCache(options, deps = {}) {
23158
23379
  const distTag = options.distTag && options.distTag !== "latest" ? options.distTag : undefined;
23159
23380
  const slot = deps.slot ?? getOpenCodeV2PluginCacheSlot(undefined, distTag ?? "latest");
23160
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
+ }
23161
23385
  if (!existsSync13(slot))
23162
23386
  return { action: "not_found", slot, ...tagged };
23163
23387
  const cached = readOpenCodeV2CachedPluginVersion(slot);
@@ -23197,7 +23421,7 @@ function reportOpenCodeV2PluginCache(result, report, options) {
23197
23421
  const slotName = result.distTag ? ` @${result.distTag}` : "";
23198
23422
  switch (result.action) {
23199
23423
  case "not_found":
23200
- if (options.reportMissing) {
23424
+ if (options.reportMissing && !result.unused) {
23201
23425
  report.pass(`OpenCode 2 plugin cache has no Magic Context${slotName} install yet (OpenCode installs it on next start)`);
23202
23426
  }
23203
23427
  return { fixed: false, issue: false };
@@ -23225,6 +23449,15 @@ function reportOpenCodeV2PluginCache(result, report, options) {
23225
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`);
23226
23450
  report.info(` ${result.slot}`);
23227
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 };
23228
23461
  case "error":
23229
23462
  report.warn(`Could not clear the OpenCode 2 plugin cache: ${result.error}`);
23230
23463
  report.info(` Manually delete: ${result.slot}`);
@@ -28760,6 +28993,26 @@ var init_migrations = __esm(async () => {
28760
28993
  LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
28761
28994
  });
28762
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
+
28763
29016
  // ../plugin/src/shared/commit-detection.ts
28764
29017
  function createCommitHashExtractPattern() {
28765
29018
  return new RegExp(`\`?\\b(${HASH_HEX})\\b\`?`, "gi");
@@ -28782,6 +29035,271 @@ function removeSystemReminders(text) {
28782
29035
  }
28783
29036
  var SYSTEM_DIRECTIVE_PREFIX = "[SYSTEM DIRECTIVE: MAGIC-CONTEXT";
28784
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
+
28785
29303
  // ../plugin/src/hooks/magic-context/read-session-formatting.ts
28786
29304
  import { createRequire as createRequire2 } from "node:module";
28787
29305
  function hasMeaningfulUserText(parts) {
@@ -28924,7 +29442,7 @@ function estimateTokens(text) {
28924
29442
  if (!activeTokenizer)
28925
29443
  return estimateTokensHeuristically(text);
28926
29444
  try {
28927
- return activeTokenizer.encode(text, "all").length;
29445
+ return countTokensExactly(activeTokenizer, text);
28928
29446
  } catch (error) {
28929
29447
  tokenizer = undefined;
28930
29448
  tokenizerLoadAttempted = true;
@@ -29015,21 +29533,41 @@ var MAX_COMMITS_PER_BLOCK = 5, tokenizer, tokenizerLoadAttempted = false, tokeni
29015
29533
  var init_read_session_formatting = __esm(() => {
29016
29534
  init_commit_detection();
29017
29535
  init_logger();
29536
+ init_token_count_exact();
29018
29537
  promptTokenCounts = new Map;
29019
29538
  PROMPT_TOKEN_COUNT_BYTES = 8 * 1024 * 1024;
29020
29539
  });
29021
29540
 
29022
29541
  // ../plugin/src/shared/stable-json.ts
29023
29542
  function stableStringify(value, seen = new WeakSet) {
29024
- if (value === undefined)
29025
- return "undefined";
29026
- if (value === null || typeof value !== "object")
29027
- return JSON.stringify(value) ?? String(value);
29028
- if (seen.has(value))
29029
- 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
+ }
29030
29560
  seen.add(value);
29031
29561
  if (Array.isArray(value)) {
29032
- 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;
29033
29571
  }
29034
29572
  const entries = Object.entries(value).sort(([a], [b]) => {
29035
29573
  if (a < b)
@@ -29038,7 +29576,14 @@ function stableStringify(value, seen = new WeakSet) {
29038
29576
  return 1;
29039
29577
  return 0;
29040
29578
  });
29041
- 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("}");
29042
29587
  }
29043
29588
 
29044
29589
  // ../plugin/src/features/magic-context/tool-definition-tokens.ts
@@ -29724,6 +30269,19 @@ var init_image_token_estimate = __esm(() => {
29724
30269
  JPEG_SCAN_LIMIT_BYTES = 256 * 1024;
29725
30270
  });
29726
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
+
29727
30285
  // ../plugin/src/hooks/magic-context/todo-view.ts
29728
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;
29729
30287
  var init_todo_view = __esm(() => {
@@ -29753,10 +30311,11 @@ var init_tail_hygiene_walk = __esm(() => {
29753
30311
  init_reclaim_protection();
29754
30312
  init_tag_owner_id();
29755
30313
  init_image_token_estimate();
30314
+ init_provider_visible_parts();
29756
30315
  init_read_session_formatting();
29757
30316
  init_todo_view();
29758
30317
  init_reclaim_protection();
29759
- MAX_CONTENT_MEMO_BYTES = 64 * 1024 * 1024;
30318
+ MAX_CONTENT_MEMO_BYTES = 256 * 1024 * 1024;
29760
30319
  contentMemo = new Map;
29761
30320
  contentMemoOrder = new Set;
29762
30321
  baselineMeasurementMemo = new Map;
@@ -29822,12 +30381,14 @@ function pickNearestPriorOwner(candidates, currentMessageId, times) {
29822
30381
  }
29823
30382
  return best?.id ?? null;
29824
30383
  }
29825
- 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;
29826
- 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 () => {
29827
30386
  init_emergency_drop();
29828
30387
  init_tag_owner_id();
29829
30388
  init_migration_v95_perf_indexes();
29830
30389
  init_storage_ops();
30390
+ init_storage_tag_identity_revision();
30391
+ await init_sqlite();
29831
30392
  insertTagStatements = new WeakMap;
29832
30393
  updateTagStatusStatements = new WeakMap;
29833
30394
  updateTagDropModeStatements = new WeakMap;
@@ -29855,6 +30416,12 @@ var init_storage_tags = __esm(() => {
29855
30416
  scopedActiveTokenTotalStatements = new WeakMap;
29856
30417
  updateTagTokenCountStatements = new WeakMap;
29857
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 = ?`;
29858
30425
  inertWhitespaceStatements = new WeakMap;
29859
30426
  foldStatements = new WeakMap;
29860
30427
  getOwnerScopedToolTagNumbersStatements = new WeakMap;
@@ -29973,92 +30540,28 @@ function isHostUnservedRow(value) {
29973
30540
  }
29974
30541
  var HOST_UNSERVED_ROW = "hostUnservedRow";
29975
30542
 
29976
- // ../plugin/src/hooks/magic-context/read-session-db.ts
29977
- function openCodeDbExists() {
29978
- return harnessOwnsOpenCodeStore() && openCodeDbPathExists(resolveOpenCodeDbPath());
29979
- }
29980
- function closeCachedReadOnlyDb() {
29981
- if (!cachedReadOnlyDb) {
29982
- return;
29983
- }
29984
- try {
29985
- closeQuietly(cachedReadOnlyDb.db);
29986
- } catch (error) {
29987
- log("[magic-context] failed to close cached OpenCode read-only DB:", error);
29988
- } finally {
29989
- cachedReadOnlyDb = null;
29990
- }
29991
- }
29992
- function getReadOnlySessionDb() {
29993
- if (!harnessOwnsOpenCodeStore()) {
29994
- throw new Error("OpenCode session database is not readable from a Pi-compatible process; its history lives in Pi sessions");
29995
- }
29996
- const resolution = resolveOpenCodeDbPath();
29997
- const dbPath = resolution.path;
29998
- if (!openCodeDbPathExists(resolution)) {
29999
- throw new Error(`OpenCode session database is unavailable at ${dbPath} (source=${resolution.source})`);
30000
- }
30001
- if (cachedReadOnlyDb?.path === dbPath) {
30002
- return cachedReadOnlyDb.db;
30003
- }
30004
- closeCachedReadOnlyDb();
30005
- const db = new Database(dbPath, { readonly: true });
30006
- try {
30007
- assertOpenCodeStoreGeneration(db, "v1", dbPath);
30008
- } catch (error) {
30009
- closeQuietly(db);
30010
- 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 {}
30011
30549
  }
30012
- cachedReadOnlyDb = { path: dbPath, db };
30013
- clearOpenCodeDbReadFailure();
30014
- return db;
30015
- }
30016
- function withReadOnlySessionDb(fn) {
30017
- return fn(getReadOnlySessionDb());
30018
- }
30019
- function getRawSessionMessageCountFromDb(db, sessionId) {
30020
- const row = db.prepare(`SELECT COUNT(*) as count FROM message WHERE session_id = ?
30021
- AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
30022
- AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')`).get(sessionId);
30023
- return typeof row?.count === "number" ? row.count : 0;
30024
30550
  }
30025
- function logProbeFailureOnce(resolution, error) {
30026
- const failure = recordOpenCodeDbReadFailure(resolution, error);
30027
- if (!claimOpenCodeDbDiagnosticOnce("session-state-probe", resolution))
30551
+ function registerExitAbort(controller) {
30552
+ controllers.add(controller);
30553
+ if (listenerRegistered)
30028
30554
  return;
30029
- const message = `[magic-context] OpenCode DB probe failed: path=${resolution.path} source=${resolution.source} cause=${failure.message}`;
30030
- probeLogObserverForTests?.(message);
30031
- log(message);
30555
+ listenerRegistered = true;
30556
+ process.once("exit", abortAll);
30032
30557
  }
30033
- function getMessageTimesFromOpenCodeDb(sessionId, messageIds) {
30034
- const result = new Map;
30035
- if (messageIds.length === 0 || !harnessOwnsOpenCodeStore())
30036
- return result;
30037
- try {
30038
- withReadOnlySessionDb((db) => {
30039
- const placeholders = messageIds.map(() => "?").join(",");
30040
- const rows = db.prepare(`SELECT id, time_created FROM message WHERE session_id = ? AND id IN (${placeholders})`).all(sessionId, ...messageIds);
30041
- for (const row of rows) {
30042
- if (typeof row.id === "string" && typeof row.time_created === "number") {
30043
- result.set(row.id, row.time_created);
30044
- }
30045
- }
30046
- });
30047
- } catch (error) {
30048
- logProbeFailureOnce(resolveOpenCodeDbPath(), error);
30049
- }
30050
- return result;
30051
- }
30052
- var cachedReadOnlyDb = null, trackedSessions, pendingParts, probeLogObserverForTests;
30053
- var init_read_session_db = __esm(async () => {
30054
- init_logger();
30055
- init_opencode_db_path();
30056
- await init_sqlite();
30057
- trackedSessions = new Map;
30058
- pendingParts = new Map;
30558
+ var controllers, listenerRegistered = false;
30559
+ var init_exit_abort_registry = __esm(() => {
30560
+ controllers = new Set;
30059
30561
  });
30060
30562
 
30061
30563
  // ../plugin/src/hooks/magic-context/read-session-raw.ts
30564
+ import { isMainThread as isMainThread2 } from "node:worker_threads";
30062
30565
  function isRawMessageRow(row) {
30063
30566
  if (row === null || typeof row !== "object")
30064
30567
  return false;
@@ -30118,7 +30621,7 @@ function readRawSessionMessagesFromDb(db, sessionId) {
30118
30621
  if (messageIds.length === 0)
30119
30622
  continue;
30120
30623
  const placeholders = messageIds.map(() => "?").join(", ");
30121
- 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
30122
30625
  FROM part
30123
30626
  WHERE +session_id = ?
30124
30627
  AND likelihood(message_id IN (${placeholders}), 0.000001)
@@ -30150,7 +30653,7 @@ function readRawSessionMessagePageFromDb(db, sessionId, afterOrdinal, limit, fin
30150
30653
  if (messageRows.length === 0)
30151
30654
  return [];
30152
30655
  const placeholders = messageRows.map(() => "?").join(", ");
30153
- 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
30154
30657
  FROM part
30155
30658
  WHERE +session_id = ?
30156
30659
  AND likelihood(message_id IN (${placeholders}), 0.000001)
@@ -30223,6 +30726,9 @@ function readRawSessionMessageSummaryPageFromDb(db, sessionId, afterOrdinal, lim
30223
30726
  return assembleRawMessagePage(messageRows, partRows);
30224
30727
  }
30225
30728
  function countRawSessionMessageOrdinalsFromDb(db, sessionId) {
30729
+ const indexed = countCanonicalOrdinalsIndexed(db, sessionId, null, null);
30730
+ if (indexed !== null)
30731
+ return indexed;
30226
30732
  const row = db.prepare(`SELECT COUNT(*) AS count
30227
30733
  FROM message
30228
30734
  WHERE session_id = ?
@@ -30309,7 +30815,7 @@ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
30309
30815
  for (let i = 0;i < ids.length; i += CHUNK) {
30310
30816
  const slice = ids.slice(i, i + CHUNK);
30311
30817
  const placeholders = slice.map(() => "?").join(",");
30312
- 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);
30313
30819
  for (const part of partRows) {
30314
30820
  const list = partsByMessageId.get(part.message_id) ?? [];
30315
30821
  list.push(attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated));
@@ -30355,49 +30861,431 @@ function readRawSessionMessagePartsByIdFromDb(db, sessionId, messageId, onQuery)
30355
30861
  version: row.time_updated ?? null
30356
30862
  };
30357
30863
  }
30358
- function readRawSessionMessageOrdinalByIdFromDb(db, sessionId, messageId) {
30359
- const row = db.prepare(`SELECT COUNT(candidate.id) AS ordinal
30360
- FROM message AS target
30361
- JOIN message AS candidate
30362
- ON candidate.session_id = target.session_id
30363
- AND NOT (
30364
- CASE WHEN json_valid(candidate.data) = 1
30365
- THEN COALESCE(json_extract(candidate.data, '$.summary'), 0)
30366
- ELSE 0 END = 1
30367
- AND CASE WHEN json_valid(candidate.data) = 1
30368
- THEN COALESCE(json_extract(candidate.data, '$.finish'), '')
30369
- ELSE '' END = 'stop'
30370
- )
30371
- AND (candidate.time_created < target.time_created
30372
- OR (candidate.time_created = target.time_created AND candidate.id <= target.id))
30373
- WHERE target.session_id = ?
30374
- AND target.id = ?
30375
- AND NOT (
30376
- CASE WHEN json_valid(target.data) = 1
30377
- THEN COALESCE(json_extract(target.data, '$.summary'), 0)
30378
- ELSE 0 END = 1
30379
- AND CASE WHEN json_valid(target.data) = 1
30380
- THEN COALESCE(json_extract(target.data, '$.finish'), '')
30381
- ELSE '' END = 'stop'
30382
- )`).get(sessionId, messageId);
30383
- const ordinal = row?.ordinal;
30384
- 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) <= (?, ?)`;
30385
30872
  }
30386
- function readRawSessionMessageByIdFromDb(db, sessionId, messageId) {
30387
- const row = db.prepare("SELECT id, data, time_created, time_updated FROM message WHERE session_id = ? AND id = ?").get(sessionId, messageId);
30388
- 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 {
30389
30886
  return null;
30390
30887
  }
30391
- const info = parseJsonRecord(row.data);
30392
- 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)
30393
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);
30394
31089
  }
30395
- const ordinalRow = db.prepare(`SELECT COUNT(*) AS ordinal FROM message
30396
- WHERE session_id = ?
30397
- AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
30398
- AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')
30399
- AND (time_created < ? OR (time_created = ? AND id <= ?))`).get(sessionId, row.time_created, row.time_created, messageId);
30400
- 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;
30401
31289
  if (ordinal <= 0) {
30402
31290
  return null;
30403
31291
  }
@@ -30461,8 +31349,34 @@ function readRawSeedTailFromDb(db, sessionId, boundaryId) {
30461
31349
  throw new Error("state_sync materialized boundary is missing from raw storage");
30462
31350
  return messages;
30463
31351
  }
30464
- 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;
30465
- 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`;
30466
31380
  RAW_SUMMARY_TOOL_INPUT_KEYS = [
30467
31381
  "description",
30468
31382
  "filePath",
@@ -30487,6 +31401,268 @@ var init_read_session_raw = __esm(() => {
30487
31401
  )
30488
31402
  )
30489
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;
30490
31666
  });
30491
31667
 
30492
31668
  // ../plugin/src/hooks/magic-context/read-session-true-raw-tokens.ts
@@ -30538,7 +31714,7 @@ function callIdFromPart(part) {
30538
31714
  const state = isRecord2(part.state) ? part.state : null;
30539
31715
  return state ? firstStringField(state, ["callID", "callId", "toolCallId", "tool_call_id", "id"]) ?? "" : "";
30540
31716
  }
30541
- function toolSignalFromPart(part) {
31717
+ function toolSignalFromPart(part, withText = true) {
30542
31718
  if (!isRecord2(part))
30543
31719
  return null;
30544
31720
  const type = partType(part);
@@ -30557,8 +31733,8 @@ function toolSignalFromPart(part) {
30557
31733
  callId,
30558
31734
  hasInput: hasInput || openInvocation,
30559
31735
  hasOutput,
30560
- inputText: hasInput && state ? stringValue(state.input) : "",
30561
- outputText: hasOutput ? stringValue(outputValue) : ""
31736
+ inputText: withText && hasInput && state ? stringValue(state.input) : "",
31737
+ outputText: withText && hasOutput ? stringValue(outputValue) : ""
30562
31738
  };
30563
31739
  }
30564
31740
  if (type === "tool-invocation") {
@@ -30567,7 +31743,7 @@ function toolSignalFromPart(part) {
30567
31743
  callId,
30568
31744
  hasInput: args !== undefined,
30569
31745
  hasOutput: false,
30570
- inputText: args !== undefined ? stringValue(args) : "",
31746
+ inputText: withText && args !== undefined ? stringValue(args) : "",
30571
31747
  outputText: ""
30572
31748
  };
30573
31749
  }
@@ -30577,7 +31753,7 @@ function toolSignalFromPart(part) {
30577
31753
  callId,
30578
31754
  hasInput: input !== undefined,
30579
31755
  hasOutput: false,
30580
- inputText: input !== undefined ? stringValue(input) : "",
31756
+ inputText: withText && input !== undefined ? stringValue(input) : "",
30581
31757
  outputText: ""
30582
31758
  };
30583
31759
  }
@@ -30588,7 +31764,7 @@ function toolSignalFromPart(part) {
30588
31764
  hasInput: false,
30589
31765
  hasOutput: content !== undefined,
30590
31766
  inputText: "",
30591
- outputText: content !== undefined ? textFromToolResultContent(content) : ""
31767
+ outputText: withText && content !== undefined ? textFromToolResultContent(content) : ""
30592
31768
  };
30593
31769
  }
30594
31770
  return null;
@@ -30598,7 +31774,7 @@ function buildToolArcs(messages) {
30598
31774
  const arcs = [];
30599
31775
  for (const message of messages) {
30600
31776
  for (const part of message.parts) {
30601
- const signal = toolSignalFromPart(part);
31777
+ const signal = toolSignalFromPart(part, false);
30602
31778
  if (!signal || signal.callId.length === 0)
30603
31779
  continue;
30604
31780
  if (signal.hasInput && signal.hasOutput) {
@@ -30637,6 +31813,7 @@ function buildToolArcs(messages) {
30637
31813
  }
30638
31814
  var MAX_MESSAGE_CACHE_KEY_BYTES, messageEstimateCache, messageEstimateKeysByField;
30639
31815
  var init_read_session_true_raw_tokens = __esm(() => {
31816
+ init_provider_visible_parts();
30640
31817
  init_read_session_formatting();
30641
31818
  MAX_MESSAGE_CACHE_KEY_BYTES = 64 * 1024 * 1024;
30642
31819
  messageEstimateCache = new Map;
@@ -30798,6 +31975,7 @@ __export(exports_read_session_chunk, {
30798
31975
  hasMeaningfulUserText: () => hasMeaningfulUserText,
30799
31976
  hasRawMessageProvider: () => hasRawMessageProvider,
30800
31977
  hasRawSessionMessageById: () => hasRawSessionMessageById,
31978
+ prepareRawSessionOrdinals: () => prepareRawSessionOrdinals,
30801
31979
  primeInMemoryTailRawMessageCache: () => primeInMemoryTailRawMessageCache,
30802
31980
  primeTailRawMessageCache: () => primeTailRawMessageCache,
30803
31981
  readRawSessionMessageById: () => readRawSessionMessageById,
@@ -31068,6 +32246,36 @@ function readRawSessionMessageRange(sessionId, fromOrdinal, toOrdinal) {
31068
32246
  }
31069
32247
  return messages;
31070
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
+ }
31071
32279
  function primeTailRawMessageCache(args) {
31072
32280
  const { sessionId, lastCompartmentEnd, anchorMessageId } = args;
31073
32281
  if (!activeRawMessageCache)
@@ -31702,22 +32910,28 @@ function readRawSessionSeedTail(sessionId, boundaryId, onQuery) {
31702
32910
  }
31703
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;
31704
32912
  var init_read_session_chunk = __esm(async () => {
31705
- init_storage_tags();
31706
32913
  init_historian_tool_expansions();
32914
+ init_logger();
31707
32915
  init_read_session_formatting();
31708
- init_read_session_raw();
31709
32916
  init_read_session_true_raw_tokens();
31710
32917
  init_system_injection_stripper();
31711
32918
  init_tag_part_guards();
31712
32919
  init_tool_drop_target();
31713
32920
  init_read_session_formatting();
31714
- 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
+ ]);
31715
32927
  blockTokenMemo = new Map;
31716
32928
  sessionProviders = new Map;
31717
32929
  boundedProviderWrappers = new WeakMap;
31718
32930
  SYSTEM_REMINDER_BLOCK_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
31719
32931
  readRawSessionMessages.readPage = readRawSessionMessagePage;
31720
32932
  readRawSessionMessages.getCount = getRawSessionMessageOrdinalCount;
32933
+ readRawSessionMessages.prepare = prepareRawSessionOrdinals;
32934
+ readRawSessionMessageById.prepare = prepareRawSessionOrdinals;
31721
32935
  });
31722
32936
 
31723
32937
  // ../plugin/src/features/magic-context/message-time-backfill.ts
@@ -33578,6 +34792,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
33578
34792
  const version = getPersistedSchemaVersion(db);
33579
34793
  if (version === 0 || version >= 95)
33580
34794
  installV95PerfSchema(db, false, version === 0);
34795
+ installTagIdentityRevisionTrigger(db);
33581
34796
  }
33582
34797
  function healWedgedChannel2Claims(db) {
33583
34798
  try {
@@ -33657,6 +34872,7 @@ function openDatabase(dbPathOrOptions) {
33657
34872
  } else
33658
34873
  runMigrations(db);
33659
34874
  ensureContextStoreUuid(db);
34875
+ installTagIdentityRevisionTrigger(db);
33660
34876
  return finishDatabaseOpen(db, dbPath, explicitDbPath, latestSupportedVersion);
33661
34877
  } catch (error) {
33662
34878
  if (db)
@@ -33691,6 +34907,7 @@ var init_storage_db = __esm(async () => {
33691
34907
  init_migration_v95_perf_indexes();
33692
34908
  init_migration_worker_client();
33693
34909
  init_storage_schema_helpers();
34910
+ init_storage_tag_identity_revision();
33694
34911
  init_tool_definition_tokens();
33695
34912
  init_tool_owner_backfill();
33696
34913
  await __promiseAll([
@@ -33896,6 +35113,9 @@ var init_config_location_migration = __esm(() => {
33896
35113
  });
33897
35114
 
33898
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
+ }
33899
35119
  var CANONICAL_DREAM_TASKS, DREAM_TOOL_LOOP_TOKEN_BUDGETS, MEMORY_DOMAIN_TASKS, MEMORY_DOMAIN_SET;
33900
35120
  var init_task_registry = __esm(() => {
33901
35121
  CANONICAL_DREAM_TASKS = [
@@ -34646,8 +35866,10 @@ function openCodeConfigFileChain(directory, hostGeneration) {
34646
35866
  const globalSupplementary = getOpenCodeConfigDirs().filter((dir) => dir !== globalDir).flatMap((dir) => [join17(dir, "opencode.json"), join17(dir, "opencode.jsonc")]);
34647
35867
  const explicitConfig = process.env.OPENCODE_CONFIG?.trim();
34648
35868
  const explicit = explicitConfig ? [explicitConfig] : [];
34649
- const direct = [join17(directory, "opencode.json"), join17(directory, "opencode.jsonc")];
34650
- 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 ? [] : [
34651
35873
  join17(directory, ".opencode", "opencode.json"),
34652
35874
  join17(directory, ".opencode", "opencode.jsonc")
34653
35875
  ];
@@ -35877,6 +37099,24 @@ function readCachedPluginVersion(pluginCacheDir) {
35877
37099
  return;
35878
37100
  }
35879
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
+ }
35880
37120
  async function clearPluginCache(options = {}, deps = {}) {
35881
37121
  const remove = deps.remove ?? ((path) => rmSync6(path, { recursive: true, force: true }));
35882
37122
  const pluginCacheRoots = getOpenCodePluginCacheRoots();
@@ -35889,6 +37129,15 @@ async function clearPluginCache(options = {}, deps = {}) {
35889
37129
  path,
35890
37130
  cached: readCachedPluginVersion(path)
35891
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
+ }
35892
37141
  if (options.force !== true && latestVersion === undefined) {
35893
37142
  const firstEntry = cacheEntries[0];
35894
37143
  return {
@@ -35960,15 +37209,24 @@ async function clearPluginCache(options = {}, deps = {}) {
35960
37209
  }
35961
37210
  var init_doctor_opencode_cache = __esm(async () => {
35962
37211
  init_opencode_plugin_cache();
35963
- await init_doctor_opencode2_cache();
37212
+ init_opencode_plugin_registration();
37213
+ await __promiseAll([
37214
+ init_opencode(),
37215
+ init_doctor_opencode2_cache()
37216
+ ]);
35964
37217
  });
35965
37218
 
35966
37219
  // src/commands/doctor-cached-plugin-fence.ts
35967
37220
  import { existsSync as existsSync29 } from "node:fs";
35968
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
+ }
35969
37226
  function listCachedOpenCodePluginFences(sources = {}) {
35970
37227
  const fences = [];
35971
- for (const root of sources.openCodeV1Roots ?? getOpenCodePluginCacheRoots()) {
37228
+ const v1Referenced = isReferenced(sources, "opencode1", "latest");
37229
+ for (const root of v1Referenced ? sources.openCodeV1Roots ?? getOpenCodePluginCacheRoots() : []) {
35972
37230
  const packageJson = getOpenCodePluginPackageJsonPath(root);
35973
37231
  if (!existsSync29(packageJson))
35974
37232
  continue;
@@ -35982,6 +37240,8 @@ function listCachedOpenCodePluginFences(sources = {}) {
35982
37240
  for (const slot of listOpenCodeV2PluginCacheSlots(sources.openCodeV2NpmCacheDir)) {
35983
37241
  if (!isOpenCodePluginDistTag(slot.spec))
35984
37242
  continue;
37243
+ if (!isReferenced(sources, "opencode2", slot.spec))
37244
+ continue;
35985
37245
  const generation = getOpenCodeV2ActiveGeneration(slot.slot);
35986
37246
  if (!generation)
35987
37247
  continue;
@@ -41170,11 +42430,6 @@ var init_user_facing_codes = __esm(() => {
41170
42430
  sentence: "Magic Context has no context.db.",
41171
42431
  action: "Run `npx @cortexkit/magic-context doctor store init`, then restart ck-mc."
41172
42432
  },
41173
- checkout_claim_held_elsewhere: {
41174
- code: "MC-C16",
41175
- sentence: "This request was not sent: this agent is checked out on another machine, so Magic Context will not work on its sessions here.",
41176
- action: "Move the agent back to this machine, then send your message again."
41177
- },
41178
42433
  single_store_migration_required: {
41179
42434
  code: "MC-C14",
41180
42435
  sentence: "Magic Context's Rust mode needs a one-time migration of its store.",
@@ -42765,7 +44020,9 @@ function parsePiConfig(rawConfig, recoveredTopLevelKeys = []) {
42765
44020
  delete configToMigrate.protected_tags;
42766
44021
  }
42767
44022
  const agentMigrated = migrateLegacyAgentEnabledInMemory(configToMigrate, preMigrationWarnings);
42768
- const migrated = migrateDreamerV2(migrateLegacyExperimental(agentMigrated, preMigrationWarnings), preMigrationWarnings);
44023
+ const migratedExperimental = migrateLegacyExperimental(agentMigrated, preMigrationWarnings);
44024
+ warnDeprecatedMuralModel(migratedExperimental, preMigrationWarnings);
44025
+ const migrated = migrateDreamerV2(migratedExperimental, preMigrationWarnings);
42769
44026
  const parsed = MagicContextConfigSchema.safeParse(migrated);
42770
44027
  if (parsed.success) {
42771
44028
  return { config: parsed.data, warnings: preMigrationWarnings };
@@ -43607,64 +44864,66 @@ async function runHealthChecks(options) {
43607
44864
  add(results, "fail", "OMP binary not found on PATH or in standard user bin directories");
43608
44865
  } else {
43609
44866
  const version = options.deps.getOmpVersion(omp.path);
43610
- if (!version)
43611
- add(results, "fail", `OMP at ${omp.path} could not report its version`);
43612
- else if (isOlderThan(version, MIN_OMP_VERSION)) {
43613
- add(results, "fail", `OMP ${version} is older than tested minimum ${MIN_OMP_VERSION}`);
43614
- } else
43615
- add(results, "pass", `OMP ${version} detected at ${omp.path}`);
43616
44867
  const plugins = options.deps.listOmpPlugins(omp.path);
43617
44868
  if (!plugins) {
43618
44869
  add(results, "fail", "`omp plugin list --json` failed or returned invalid JSON");
43619
44870
  } else {
43620
44871
  const plugin = plugins.find((entry) => entry.name === OMP_PLUGIN_PACKAGE);
43621
44872
  if (!plugin) {
43622
- add(results, "fail", `${OMP_PLUGIN_PACKAGE} is not installed in OMP`);
43623
- repairPlan.installPlugin = true;
43624
- } else if (!plugin.enabled) {
43625
- 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)`);
43626
44874
  repairPlan.installPlugin = true;
43627
44875
  } else {
43628
- add(results, "pass", `${OMP_PLUGIN_PACKAGE} ${plugin.version} is enabled`);
43629
- const manifest = pluginDeclaresOmp(plugin.path);
43630
- if (manifest === true)
43631
- add(results, "pass", "Plugin exposes an OMP/Pi extension manifest");
43632
- else if (manifest === false)
43633
- add(results, "fail", "Installed plugin has no OMP/Pi extension manifest");
43634
- else
43635
- add(results, "warn", "Could not inspect the installed plugin manifest");
43636
- }
43637
- }
43638
- const compaction = options.deps.getOmpSetting(omp.path, "compaction.enabled");
43639
- if (compaction === false)
43640
- add(results, "pass", "OMP native compaction is disabled");
43641
- else if (compaction === true) {
43642
- add(results, "fail", "OMP native compaction is enabled and conflicts with Magic Context");
43643
- repairPlan.disableCompaction = true;
43644
- } else
43645
- add(results, "fail", "Could not read OMP compaction.enabled");
43646
- const memory = options.deps.getOmpSetting(omp.path, "memory.backend");
43647
- if (memory === "off")
43648
- add(results, "pass", "OMP automatic memory backend is disabled");
43649
- else if (typeof memory === "string") {
43650
- add(results, "fail", `OMP memory.backend=${memory} duplicates Magic Context memory injection`);
43651
- repairPlan.disableMemory = true;
43652
- } else
43653
- add(results, "fail", "Could not read OMP memory.backend");
43654
- const nonGlobalSources = getOmpNonGlobalConfigSources(options.cwd);
43655
- if (nonGlobalSources.length > 0 && (repairPlan.disableCompaction || repairPlan.disableMemory)) {
43656
- add(results, "warn", "OMP project/overlay config owns effective conflicting settings; automatic global repair is disabled: " + nonGlobalSources.join(", "));
43657
- }
43658
- const reportedAgentDir = options.deps.runOmpCommand(omp.path, ["config", "path"], 1e4);
43659
- if (!reportedAgentDir.ok) {
43660
- add(results, "warn", "Could not verify OMP active agent directory");
43661
- } else {
43662
- const reportedPath = resolve15(reportedAgentDir.stdout);
43663
- const expectedPath = resolve15(getOmpAgentDir());
43664
- if (reportedPath === expectedPath) {
43665
- add(results, "pass", `OMP agent directory resolved to ${getOmpAgentDir()}`);
43666
- } else {
43667
- 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
+ }
43668
44927
  }
43669
44928
  }
43670
44929
  }
@@ -46665,6 +47924,16 @@ function listDanglingCompartmentBoundaries(contextDb, openCodeDb, hostGeneration
46665
47924
  ORDER BY c.session_id ASC, c.sequence ASC`).all();
46666
47925
  const statement = openCodeDb.prepare(`SELECT 1 AS found FROM ${selection.table} WHERE session_id = ? AND id = ? LIMIT 1`);
46667
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
+ };
46668
47937
  return rows.flatMap((row) => {
46669
47938
  const missingStart = !exists(row.session_id, row.start_message_id);
46670
47939
  const missingEnd = !exists(row.session_id, row.end_message_id);
@@ -46675,7 +47944,8 @@ function listDanglingCompartmentBoundaries(contextDb, openCodeDb, hostGeneration
46675
47944
  sessionId: row.session_id,
46676
47945
  sequence: row.sequence,
46677
47946
  missingStartMessageId: missingStart ? row.start_message_id : null,
46678
- missingEndMessageId: missingEnd ? row.end_message_id : null
47947
+ missingEndMessageId: missingEnd ? row.end_message_id : null,
47948
+ sessionInStore: sessionInStore(row.session_id)
46679
47949
  }
46680
47950
  ];
46681
47951
  });
@@ -46687,6 +47957,52 @@ function formatDanglingCompartmentBoundary(boundary) {
46687
47957
  ].filter((value) => value !== null);
46688
47958
  return `session=${boundary.sessionId} sequence=${boundary.sequence} missing ${missing.join(" ")}`;
46689
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;
46690
48006
 
46691
48007
  // src/commands/doctor-harness-relabel.ts
46692
48008
  function reportUnresolvedHarnessRelabel(args) {
@@ -46840,6 +48156,9 @@ function withPluginEntrySpecifier(entry, specifier) {
46840
48156
  }
46841
48157
  return specifier;
46842
48158
  }
48159
+ function isOpenCodePluginRegistered(config, configDir) {
48160
+ return readPluginEntries(config).some(({ entry }) => matchesPluginEntry(entry, OPENCODE_PLUGIN_NAME) || isDevPathPluginEntry(entry, configDir));
48161
+ }
46843
48162
  function checkOpenCodePluginEntry(config, configName, options, report) {
46844
48163
  const allEntries = readPluginEntries(config);
46845
48164
  const found = allEntries.find(({ entry }) => matchesPluginEntry(entry, OPENCODE_PLUGIN_NAME) || isDevPathPluginEntry(entry, options.configDir));
@@ -47497,6 +48816,21 @@ function logOpenCodeInstallationTable(installations) {
47497
48816
  function describeOpenCode2SessionAPIRequirement(hostVersion) {
47498
48817
  return `OpenCode host ${hostVersion}; Magic Context requires OpenCode 2.0.22 or newer with session.remove and session.compact.`;
47499
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
+ }
47500
48834
  async function runDoctor2(options = {}) {
47501
48835
  migrateConfigLocationsForCli(process.cwd(), log3);
47502
48836
  if (options.issue) {
@@ -47572,8 +48906,15 @@ async function runDoctor2(options = {}) {
47572
48906
  pass(installationReports.length > 1 ? `OpenCode ${activeInstallation.version} installed (active install marked above)` : `OpenCode ${activeInstallation.version} installed`);
47573
48907
  }
47574
48908
  const hostGeneration = openCodeHostGenerationFromVersion(activeInstallation.version);
47575
- if (hostGeneration === "v2") {
47576
- 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 {}
47577
48918
  }
47578
48919
  const storeHostSelection = selectOpenCodeStoreHost(installationReports, openCodeHostGenerationFromVersion);
47579
48920
  const storeHost = storeHostSelection?.host ?? activeInstallation;
@@ -47591,7 +48932,9 @@ async function runDoctor2(options = {}) {
47591
48932
  const openCodeDbCheck = describeOpenCodeDatabaseDoctorCheck(openCodeDbResolution);
47592
48933
  if (openCodeDbCheck.ok)
47593
48934
  pass(openCodeDbCheck.message);
47594
- else
48935
+ else if (magicContextStatusKnown && !magicContextInstalled) {
48936
+ log3.info("OpenCode session database not yet created (will be created on first OpenCode session)");
48937
+ } else
47595
48938
  fail(openCodeDbCheck.message);
47596
48939
  if (openCodeDbCheck.ok) {
47597
48940
  let markerDb = null;
@@ -47612,7 +48955,7 @@ async function runDoctor2(options = {}) {
47612
48955
  } else if (options.fix) {
47613
48956
  warn(`${summary}; repaired=${report.repaired}, but some rows remain unconvertible`);
47614
48957
  } else if (report.migrationCompleted) {
47615
- 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)`);
47616
48959
  } else {
47617
48960
  warn(`${summary}; run \`magic-context doctor --fix\` before upgrading OpenCode`);
47618
48961
  }
@@ -47640,14 +48983,18 @@ async function runDoctor2(options = {}) {
47640
48983
  if (dangling.length === 0) {
47641
48984
  pass("Compartment boundary ids resolve in the OpenCode session store");
47642
48985
  } else if (storeGeneration === "v2" && /\d/.test(storeHost.version)) {
47643
- 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.`);
47644
- for (const boundary of dangling) {
47645
- 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}`);
47646
48991
  }
47647
48992
  } else {
47648
- warn(`${dangling.length} compartment(s) have dangling OpenCode boundary ids`);
47649
- for (const boundary of dangling) {
47650
- 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}`);
47651
48998
  }
47652
48999
  }
47653
49000
  if (!supportsCoordinateGenerationReporting(contextDb)) {
@@ -47683,10 +49030,7 @@ async function runDoctor2(options = {}) {
47683
49030
  }
47684
49031
  }
47685
49032
  const selfVersion = getSelfVersion2();
47686
- const [npmLatest, pluginNpmLatest] = await Promise.all([
47687
- fetchNpmLatest(CLI_PACKAGE_NAME),
47688
- fetchNpmLatest(OPENCODE_PLUGIN_NAME)
47689
- ]);
49033
+ const npmLatest = await fetchNpmLatest(CLI_PACKAGE_NAME);
47690
49034
  const cliComparison = npmLatest ? compareVersions(selfVersion, npmLatest) : null;
47691
49035
  if (!npmLatest) {
47692
49036
  log3.info(`Magic Context CLI v${selfVersion}; npm latest check unavailable`);
@@ -47697,9 +49041,8 @@ async function runDoctor2(options = {}) {
47697
49041
  } else {
47698
49042
  pass(`Magic Context CLI v${selfVersion} is current (npm latest v${npmLatest})`);
47699
49043
  }
47700
- const paths = detectConfigPaths();
47701
49044
  if (paths.opencodeConfigFormat === "none") {
47702
- fail(`No opencode.json found at ${paths.opencodeConfig}`);
49045
+ magicContextStatusKnown = true;
47703
49046
  } else {
47704
49047
  pass(`OpenCode config: ${paths.opencodeConfig}`);
47705
49048
  }
@@ -47869,6 +49212,7 @@ async function runDoctor2(options = {}) {
47869
49212
  log3.warn("Could not migrate deprecated config keys in magic-context.jsonc");
47870
49213
  }
47871
49214
  }
49215
+ const openCodeHostLabel = activeInstallation.kind === "desktop" ? "OpenCode Desktop" : activeInstallation.version === "unknown" ? "OpenCode" : `OpenCode ${activeInstallation.version}`;
47872
49216
  const reportedAutoUpdateStalls = new Set;
47873
49217
  const reportAutoUpdateStall = (specifier) => {
47874
49218
  const message = describeAutoUpdateStall(specifier, autoUpdateEnabled);
@@ -47883,71 +49227,90 @@ async function runDoctor2(options = {}) {
47883
49227
  const config = import_comment_json11.parse(raw);
47884
49228
  const configName = paths.opencodeConfigFormat === "jsonc" ? "opencode.jsonc" : "opencode.json";
47885
49229
  const configDir = dirname25(paths.opencodeConfig);
47886
- if (checkPluginDuplicates(config, configName, { fix: options.fix, configDir }, {
47887
- warn,
47888
- pass: (message) => {
47889
- pass(message);
47890
- fixed++;
47891
- },
47892
- info: (message) => log3.info(message)
47893
- })) {
47894
- 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)}
47895
49242
  `);
47896
- }
47897
- const allEntries = readPluginEntries(config);
47898
- if (allEntries.some(({ entry }) => isLocalPathPluginEntry(entry) && String(entry).includes("magic-context") && !isDevPathPluginEntry(entry, configDir))) {
47899
- warn("An unverifiable local OpenCode plugin path was ignored because its package name is not Magic Context");
47900
- }
47901
- if (checkOpenCodePluginEntry(config, configName, {
47902
- force: options.force,
47903
- registrationKey: pluginConfigKeyFor(hostGeneration),
47904
- configDir
47905
- }, {
47906
- pass,
47907
- warn,
47908
- fixed: (message) => {
47909
- pass(message);
47910
- fixed++;
47911
- },
47912
- autoUpdateStall: reportAutoUpdateStall
47913
- })) {
47914
- 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)}
47915
49262
  `);
49263
+ }
47916
49264
  }
47917
49265
  } catch {
47918
49266
  warn("Could not parse opencode config to verify plugin entry");
47919
49267
  }
47920
- }
47921
- const cwd = process.cwd();
47922
- const compactionEnabled = resolveCompactionEnabledForDoctor();
47923
- const conflictResult = detectConflicts(cwd, { compactionEnabled });
47924
- log3.info("Compaction check: file-based; the running server's resolved config may differ — `opencode debug config` is authoritative");
47925
- if (conflictResult.hasConflict) {
47926
- for (const reason of conflictResult.reasons) {
47927
- fail(`Conflict: ${reason}`);
47928
- }
47929
- const actions = fixConflicts(cwd, conflictResult.conflicts, { compactionEnabled });
47930
- for (const action of actions) {
47931
- pass(`Fixed: ${action}`);
47932
- fixed++;
47933
- }
47934
- if (actions.length > 0) {
47935
- warn("Restart OpenCode for conflict fixes to take effect");
47936
- }
47937
49268
  } else {
47938
- if (!compactionEnabled) {
47939
- if (conflictResult.nativeCompaction.auto || conflictResult.nativeCompaction.prune) {
47940
- pass("No conflicts detected (compaction, DCP, OMO hooks) — native compaction active (compaction-off mode)");
47941
- } else {
47942
- 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");
47943
49298
  }
47944
49299
  } else {
47945
- 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
+ }
47946
49309
  }
47947
49310
  }
47948
- if (hostGeneration === "v2") {
49311
+ if (magicContextInstalled && hostGeneration === "v2") {
47949
49312
  pass("TUI sidebar loads from the plugin entry on OpenCode 2 (tui.json not used)");
47950
- } else {
49313
+ } else if (magicContextInstalled) {
47951
49314
  const tuiAdded = ensureTuiPluginEntry();
47952
49315
  if (tuiAdded) {
47953
49316
  pass("Added TUI sidebar plugin to tui.json");
@@ -48097,65 +49460,88 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
48097
49460
  warn(`RPC discovery check unavailable: ${error instanceof Error ? error.message : String(error)}`);
48098
49461
  issues++;
48099
49462
  }
48100
- const cacheResult = await clearPluginCache({
48101
- force: options.force,
48102
- latestVersion: pluginNpmLatest,
48103
- hostFiles: openCodeHostDatabaseFiles([openCodeDbResolution.path])
48104
- });
48105
- if (cacheResult.action === "cleared") {
48106
- const versionInfo = cacheResult.cached ? ` (cached: ${cacheResult.cached}${cacheResult.latest ? `, latest: ${cacheResult.latest}` : ""})` : "";
48107
- const reason = cacheResult.latest ? "outdated plugin cache" : "plugin cache (latest version check unavailable)";
48108
- pass(`Cleared ${reason}${versionInfo} — latest will download on restart`);
48109
- log3.info(` ${cacheResult.path}`);
48110
- fixed++;
48111
- } else if (cacheResult.action === "up_to_date") {
48112
- pass(`Plugin cache up to date (v${cacheResult.cached})`);
48113
- } else if (cacheResult.action === "check_unavailable") {
48114
- warn(`Plugin cache version check unavailable; preserving cached plugin${cacheResult.cached ? ` (cached: ${cacheResult.cached})` : ""}. Use doctor --force to reinstall it.`);
48115
- } else if (cacheResult.action === "in_use" || cacheResult.action === "in_use_unknown") {
48116
- const versionInfo = cacheResult.cached ? ` (cached: ${cacheResult.cached}${cacheResult.latest ? `, latest: ${cacheResult.latest}` : ""})` : "";
48117
- const why = cacheResult.action === "in_use" ? `OpenCode is running (pid ${cacheResult.pids?.join(", ")})` : cacheResult.reason ?? "could not tell whether OpenCode is running";
48118
- warn(`Plugin cache${versionInfo} was not cleared: ${why}`);
48119
- log3.info(" Quit OpenCode and rerun doctor to clear it.");
48120
- for (const path of cacheResult.paths ?? [cacheResult.path])
48121
- log3.info(` ${path}`);
48122
- issues++;
48123
- } else if (cacheResult.action === "error") {
48124
- warn(`Could not clear plugin cache: ${cacheResult.error}`);
48125
- if (cacheResult.clearedPaths && cacheResult.clearedPaths.length > 0) {
48126
- 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 {}
48127
49469
  }
48128
- if (cacheResult.failedPaths && cacheResult.failedPaths.length > 0) {
48129
- log3.info(` Failed roots: ${cacheResult.failedPaths.join(", ")}`);
48130
- } else {
48131
- 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) });
48132
49542
  }
48133
- issues++;
48134
- } else if (hostGeneration !== "v2") {
48135
- pass("Plugin cache clean (no cached version found)");
48136
- }
48137
- let v2DistTag;
48138
- if (paths.opencodeConfigFormat !== "none") {
48139
- try {
48140
- v2DistTag = configuredOpenCodeV2DistTag(import_comment_json11.parse(readFileSync28(paths.opencodeConfig, "utf-8")));
48141
- } catch {}
48142
- }
48143
- const v2Cache = reportOpenCodeV2PluginCache(checkOpenCodeV2PluginCache({
48144
- fix: options.fix,
48145
- force: options.force,
48146
- latestVersion: v2DistTag ? await fetchNpmLatest(OPENCODE_PLUGIN_NAME, v2DistTag) : pluginNpmLatest,
48147
- distTag: v2DistTag,
48148
- hostFiles: openCodeHostDatabaseFiles([openCodeDbResolution.path])
48149
- }), { pass, warn, info: (message) => log3.info(message) }, { reportMissing: hostGeneration === "v2" });
48150
- if (v2Cache.fixed)
48151
- fixed++;
48152
- if (v2Cache.issue)
48153
- issues++;
48154
- const sharedDbVersion = readContextDbSchemaVersion(dbPath);
48155
- if (sharedDbVersion !== null) {
48156
- reportCachedPluginFences(compareCachedPluginFences(listCachedOpenCodePluginFences(), sharedDbVersion), { pass, fail, info: (message) => log3.info(message) });
48157
49543
  }
48158
- {
49544
+ if (magicContextInstalled) {
48159
49545
  const ageWarnings = collectNpmReleaseAgeWarnings();
48160
49546
  if (ageWarnings.length > 0) {
48161
49547
  log3.warn("npm min-release-age restriction detected — this can prevent OpenCode from installing the latest plugin version");
@@ -48167,17 +49553,19 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
48167
49553
  issues++;
48168
49554
  }
48169
49555
  }
48170
- const logHarnesses = [
48171
- ...hostGeneration === "v1" ? ["opencode"] : [],
48172
- ...hostGeneration === "v2" || storeGeneration === "v2" ? ["opencode2"] : []
48173
- ];
48174
- const logFiles = logHarnesses.flatMap((harness) => inspectMagicContextLogs(harness)).filter((file, index, all) => all.findIndex((other) => other.path === file.path) === index);
48175
- const existingLogFiles = logFiles.filter((file) => file.exists);
48176
- if (existingLogFiles.length === 0) {
48177
- log3.info(`No plugin log file yet; checked: ${logFiles.map((file) => file.path).join(", ")}`);
48178
- } else {
48179
- for (const file of existingLogFiles) {
48180
- 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
+ }
48181
49569
  }
48182
49570
  }
48183
49571
  const diagnostics = await collectDiagnostics();
@@ -48358,13 +49746,12 @@ function resolveDreamerTaskModel(args) {
48358
49746
  const taskQualifier = taskBlock?.[args.harness === "opencode" ? "variant" : "thinking_level"];
48359
49747
  const harnessQualifier = harnessBlock[args.harness === "opencode" ? "variant" : "thinking_level"] ?? normalizeModelEntry(harnessBlock.model, args.harness)?.qualifier;
48360
49748
  const taskHasModel = taskBlock !== undefined && hasOwn2(taskBlock, "model");
48361
- const usesMuralModel = !taskHasModel && args.task === "compress-cues" && Boolean(readString(args.muralModel));
48362
- const primarySource = taskHasModel ? taskBlock?.model : usesMuralModel ? args.muralModel : harnessBlock.model;
49749
+ const primarySource = taskHasModel ? taskBlock?.model : harnessBlock.model;
48363
49750
  const fallbackSource = taskBlock !== undefined && hasOwn2(taskBlock, "fallback_models") ? taskBlock.fallback_models : harnessBlock.fallback_models;
48364
49751
  return {
48365
49752
  primary: resolvePrimaryEntry({
48366
49753
  entry: primarySource,
48367
- defaultQualifier: usesMuralModel ? harnessQualifier : taskQualifier ?? harnessQualifier,
49754
+ defaultQualifier: taskQualifier ?? harnessQualifier,
48368
49755
  harness: args.harness
48369
49756
  }),
48370
49757
  fallbacks: resolveFallbackEntries(fallbackSource, args.harness),
@@ -48375,13 +49762,12 @@ function resolveDreamerTaskModel(args) {
48375
49762
  }
48376
49763
 
48377
49764
  // ../plugin/src/features/magic-context/dreamer/task-config.ts
48378
- function buildDreamTaskRuntimeConfigs(dreamer, harness, language, muralModel) {
48379
- return CANONICAL_DREAM_TASKS.map((task) => {
49765
+ function buildDreamTaskRuntimeConfigs(dreamer, harness, language, smartNotesEnabled = true) {
49766
+ return availableDreamTasks(smartNotesEnabled).map((task) => {
48380
49767
  const resolved = resolveDreamerTaskModel({
48381
49768
  config: { dreamer },
48382
49769
  harness,
48383
- task,
48384
- muralModel
49770
+ task
48385
49771
  });
48386
49772
  return {
48387
49773
  task,
@@ -48489,7 +49875,7 @@ function runnablePiModelChains(config, harness) {
48489
49875
  });
48490
49876
  }
48491
49877
  if (isDreamerRunnable(config)) {
48492
- 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)) {
48493
49879
  if (task.schedule.trim() === "")
48494
49880
  continue;
48495
49881
  const chain = [task.model, ...task.fallbackModels ?? []].filter((entry) => entry !== undefined);
@@ -49283,22 +50669,9 @@ async function runHealthChecks2(options) {
49283
50669
  };
49284
50670
  const self = options.deps.selfVersion();
49285
50671
  const pi = options.deps.detectPiBinary();
49286
- if (!pi) {
50672
+ const piVersion = pi ? options.deps.getPiVersion(pi.path) : null;
50673
+ if (!pi)
49287
50674
  add2(results, "fail", "Pi binary not found on PATH or at ~/.pi/bin/pi");
49288
- } else {
49289
- const version = options.deps.getPiVersion(pi.path);
49290
- if (version === null) {
49291
- add2(results, "fail", `Pi CLI was found at ${pi.path} but could not be executed`);
49292
- } else {
49293
- add2(results, "pass", `Pi ${version} detected at ${pi.path}`);
49294
- }
49295
- const compare = compareSemver(version, MIN_PI_VERSION);
49296
- if (compare !== null && compare < 0) {
49297
- 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\`).`);
49298
- } else if (version) {
49299
- add2(results, "pass", `Pi version meets minimum ${MIN_PI_VERSION} requirement`);
49300
- }
49301
- }
49302
50675
  const latest = options.deps.getLatestNpmVersion();
49303
50676
  const latestCompare = latest ? compareSemver(self, latest) : null;
49304
50677
  if (latest && latestCompare === -1) {
@@ -49312,8 +50685,11 @@ async function runHealthChecks2(options) {
49312
50685
  }
49313
50686
  const settingsPath = getPiUserExtensionsPath();
49314
50687
  let packages = [];
50688
+ let piPluginInstalled = false;
49315
50689
  if (!existsSync47(settingsPath)) {
49316
- 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
+ }
49317
50693
  repairPlan.addPackageEntry = true;
49318
50694
  } else {
49319
50695
  const parsed = readJsonc2(settingsPath);
@@ -49321,15 +50697,31 @@ async function runHealthChecks2(options) {
49321
50697
  add2(results, "fail", `Could not parse Pi settings ${settingsPath}: ${parsed.error}`);
49322
50698
  } else {
49323
50699
  packages = packagesFrom(parsed.value);
49324
- add2(results, "pass", `Pi settings found at ${settingsPath}`);
49325
50700
  if (hasPiMagicContextPackage(packages)) {
50701
+ piPluginInstalled = true;
50702
+ add2(results, "pass", `Pi settings found at ${settingsPath}`);
49326
50703
  add2(results, "pass", `${PI_PACKAGE_SOURCE} is registered in packages[]`);
49327
50704
  } else {
49328
- 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
+ }
49329
50708
  repairPlan.addPackageEntry = true;
49330
50709
  }
49331
50710
  }
49332
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
+ }
49333
50725
  const userConfigPath = getPiUserConfigPath();
49334
50726
  const projectPath = projectConfigPath(options.cwd);
49335
50727
  for (const [label, path, required] of [
@@ -49378,7 +50770,8 @@ async function runHealthChecks2(options) {
49378
50770
  } else if (historianModel.startsWith("github-copilot/") && historianThinkingLevel) {
49379
50771
  add2(results, "pass", `historian.model "${historianModel}" has thinking_level "${historianThinkingLevel}" configured`);
49380
50772
  }
49381
- checkPiModelChains(results, pi, loadedConfig.config, options.deps);
50773
+ if (piPluginInstalled)
50774
+ checkPiModelChains(results, pi, loadedConfig.config, options.deps);
49382
50775
  const storage = getMagicContextStorageResolution();
49383
50776
  const storageDir = storage.path;
49384
50777
  const dbPath = join41(storageDir, "context.db");
@@ -49501,7 +50894,7 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
49501
50894
  }
49502
50895
  } else if (loadedConfig.config.embedding.provider === "off") {
49503
50896
  add2(results, "info", "Embedding provider disabled");
49504
- } else {
50897
+ } else if (piPluginInstalled) {
49505
50898
  let runtimeReported = false;
49506
50899
  let firstFallback = null;
49507
50900
  const brokenWarnings = [];
@@ -49540,38 +50933,40 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
49540
50933
  }
49541
50934
  }
49542
50935
  }
49543
- const piEntries = packages.filter(isConfiguredPiMagicContextEntry2).map(describePiPackageEntry);
49544
- if (piEntries.length > 1) {
49545
- add2(results, "fail", `Multiple magic-context entries in Pi packages[] — this loads the plugin twice: ${piEntries.join(", ")}`);
49546
- } else {
49547
- add2(results, "pass", "No conflicting magic-context entries in Pi packages[]");
49548
- }
49549
- const otherExtensions = packages.filter((entry) => !isConfiguredPiMagicContextEntry2(entry)).map(describePiPackageEntry);
49550
- if (otherExtensions.length > 0) {
49551
- add2(results, "info", `Other Pi extensions registered: ${otherExtensions.join(", ")}`);
49552
- } else {
49553
- add2(results, "info", "No other Pi extensions listed in settings.json");
49554
- }
49555
- const configuredEntry = packages.find(isPiMagicContextPackageEntry);
49556
- const configuredSpecifier = getPiMagicContextPackageSpecifier(configuredEntry);
49557
- const expectedPluginVersion = pinnedVersionFromPackageSpecifier(configuredSpecifier) ?? latest ?? null;
49558
- const staleCaches = findPiMagicContextCacheDirs(options.cwd, expectedPluginVersion, options.force === true);
49559
- if (staleCaches.length > 0) {
49560
- repairPlan.clearCachePaths = staleCaches.map((entry) => entry.path);
49561
- add2(results, "warn", `Stale Pi extension cache found: ${staleCaches.map((entry) => `${entry.path}${entry.version ? ` (v${entry.version})` : ""}`).join(", ")}`);
49562
- } else {
49563
- add2(results, "pass", "Pi extension cache clean (no stale cached package found)");
49564
- }
49565
- const logFiles = inspectMagicContextLogs("pi");
49566
- const existingLogFiles = logFiles.filter((file) => file.exists);
49567
- if (existingLogFiles.length === 0) {
49568
- add2(results, "info", `No plugin log file yet; checked: ${logFiles.map((file) => file.path).join(", ")}`);
49569
- } else {
49570
- for (const file of existingLogFiles) {
49571
- 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>"}`);
49572
50969
  }
49573
- const lastLine = readLogLines(existingLogFiles).at(-1);
49574
- add2(results, "info", `Last plugin log line: ${lastLine ? sanitizeDiagnosticText(lastLine) : "<empty log>"}`);
49575
50970
  }
49576
50971
  const diagnosticsForDumps = await collectDiagnostics2(options.cwd);
49577
50972
  const dumpBuckets = diagnosticsForDumps.historianDumps.byProject;
@@ -49967,7 +51362,8 @@ async function dispatchDoctor(adapter, options) {
49967
51362
  force: options.force,
49968
51363
  fix: options.fix,
49969
51364
  issue: options.issue,
49970
- report: options.report
51365
+ report: options.report,
51366
+ verbose: options.verbose
49971
51367
  });
49972
51368
  }
49973
51369
  case "pi": {
@@ -50131,6 +51527,7 @@ var DOCTOR_HELP = [
50131
51527
  " --fix Repair safe, Magic Context-owned store rows and clear an outdated OpenCode 2 plugin cache",
50132
51528
  " --force Force-clear the plugin cache",
50133
51529
  " --clear Interactive cache cleanup picker",
51530
+ " --verbose Print long listings in full instead of a capped summary",
50134
51531
  " --prune-discovery [--yes] List RPC discovery records that block offline maintenance, then remove them after confirmation",
50135
51532
  " --issue Collect diagnostics and open a GitHub issue",
50136
51533
  " --report <path> Write the issue diagnostics to <path> without prompting",
@@ -50389,6 +51786,7 @@ async function runCommand(command, rest) {
50389
51786
  issue: rest.includes("--issue") || report !== null,
50390
51787
  ...report !== null ? { report } : {},
50391
51788
  clear: rest.includes("--clear"),
51789
+ verbose: rest.includes("--verbose"),
50392
51790
  checkV22Backfill: rest.includes("--check-v22-backfill"),
50393
51791
  retryV22Backfill: rest.includes("--retry-v22-backfill"),
50394
51792
  ...rekeyV22DirIdentity !== null ? { rekeyV22DirIdentity } : {},