@cortexkit/magic-context 0.46.1 → 0.47.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 +202 -29
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2973,7 +2973,7 @@ function sortJson(value) {
2973
2973
  if (value && typeof value === "object") {
2974
2974
  const sorted = {};
2975
2975
  for (const key of Object.keys(value).sort()) {
2976
- if (key === "protected_tags")
2976
+ if (key === "protected_tags" || key === "clear_reasoning_age")
2977
2977
  continue;
2978
2978
  sorted[key] = sortJson(value[key]);
2979
2979
  }
@@ -3108,7 +3108,9 @@ function migrateConfigFile(opts) {
3108
3108
  let migratedContent = first.content;
3109
3109
  let strippedProtectedTags = false;
3110
3110
  try {
3111
- const stripped = removeJsoncValue(first.content, ["protected_tags"]);
3111
+ const stripped = removeJsoncValue(removeJsoncValue(first.content, ["protected_tags"]), [
3112
+ "clear_reasoning_age"
3113
+ ]);
3112
3114
  if (stripped !== first.content) {
3113
3115
  migratedContent = stripped;
3114
3116
  strippedProtectedTags = true;
@@ -3116,7 +3118,10 @@ function migrateConfigFile(opts) {
3116
3118
  } catch {}
3117
3119
  atomicWriteConfigFile(opts.targetPath, migratedContent);
3118
3120
  if (strippedProtectedTags) {
3119
- info?.(`Stripped deprecated "protected_tags" key during config location migration`);
3121
+ if (first.content.includes('"protected_tags"'))
3122
+ info?.(`Stripped deprecated "protected_tags" key during config location migration`);
3123
+ if (first.content.includes('"clear_reasoning_age"'))
3124
+ info?.(`Stripped deprecated "clear_reasoning_age" key during config location migration`);
3120
3125
  }
3121
3126
  info?.(`Migrated Magic Context ${opts.scope} config from ${first.path} to ${opts.targetPath}`);
3122
3127
  warnings.push(...markLegacySourcesMovedAside(sources, opts.targetPath, opts.logger));
@@ -8694,7 +8699,7 @@ class Parser {
8694
8699
  } else
8695
8700
  break;
8696
8701
  }
8697
- const expression = { steps, cap: 300 };
8702
+ const expression = { steps };
8698
8703
  if (this.take(".each(")) {
8699
8704
  if (relative)
8700
8705
  return this.error();
@@ -8760,9 +8765,23 @@ function compiled(source) {
8760
8765
  function oneLine(value) {
8761
8766
  return value.replace(/[\r\n\u2028\u2029]+/g, " ");
8762
8767
  }
8763
- function truncate(value, cap) {
8768
+ function truncate(value, cap, legacyPreview = false) {
8764
8769
  const chars = Array.from(oneLine(value));
8765
- return chars.length > cap ? `${chars.slice(0, cap).join("")}…` : chars.join("");
8770
+ if (cap === undefined || chars.length <= cap)
8771
+ return chars.join("");
8772
+ if (legacyPreview)
8773
+ return `${chars.slice(0, cap).join("")}…`;
8774
+ let end = 0;
8775
+ for (let i = 0;i < Math.min(cap, chars.length); i++) {
8776
+ if (!".!?。!?".includes(chars[i]))
8777
+ continue;
8778
+ let next = i + 1;
8779
+ while (next < chars.length && `"'”’)]`.includes(chars[next]))
8780
+ next++;
8781
+ if (next <= cap && ("。!?".includes(chars[i]) || next === chars.length || /[ \t]/.test(chars[next])))
8782
+ end = next;
8783
+ }
8784
+ return `${end ? `${chars.slice(0, end).join("")} ` : ""}[… ${chars.length - end} more characters]`;
8766
8785
  }
8767
8786
  function scalar(value) {
8768
8787
  if (value === undefined)
@@ -8779,10 +8798,11 @@ function compactJson(value) {
8779
8798
  function field(value, key) {
8780
8799
  return value !== null && typeof value === "object" && !Array.isArray(value) && Object.hasOwn(value, key) ? value[key] : undefined;
8781
8800
  }
8782
- function render(nodes, root) {
8801
+ function render(nodes, root, legacyPreview) {
8783
8802
  return nodes.map((node) => {
8784
8803
  if (typeof node === "string")
8785
8804
  return node;
8805
+ const cap = node.cap ?? (legacyPreview ? 300 : undefined);
8786
8806
  let value = root;
8787
8807
  let list = false;
8788
8808
  for (const step of node.steps) {
@@ -8796,19 +8816,19 @@ function render(nodes, root) {
8796
8816
  }
8797
8817
  }
8798
8818
  if (node.count)
8799
- return truncate(Array.isArray(value) ? String(value.length) : "", node.cap);
8819
+ return truncate(Array.isArray(value) ? String(value.length) : "", cap, legacyPreview);
8800
8820
  if (node.each || list || node.join !== undefined) {
8801
8821
  if (!Array.isArray(value))
8802
8822
  return "";
8803
- const elements = value.slice(0, 10).map((item) => truncate(node.each ? render(node.each, item) : scalar(item), 300));
8804
- if (value.length > 10)
8823
+ const elements = value.slice(0, legacyPreview ? 10 : undefined).map((item) => truncate(node.each ? render(node.each, item, legacyPreview) : scalar(item), legacyPreview ? 300 : undefined, legacyPreview));
8824
+ if (legacyPreview && value.length > 10)
8805
8825
  elements.push(`… +${value.length - 10} more`);
8806
- return truncate(elements.join(node.join ?? (node.each ? "; " : ", ")), node.cap);
8826
+ return truncate(elements.join(node.join ?? (node.each ? "; " : ", ")), cap, legacyPreview);
8807
8827
  }
8808
- return truncate(scalar(value), node.cap);
8828
+ return truncate(scalar(value), cap, legacyPreview);
8809
8829
  }).join("");
8810
8830
  }
8811
- function renderToolTemplate(source, input, output) {
8831
+ function renderToolTemplate(source, input, output, legacyPreview = false) {
8812
8832
  const nodes = compiled(source);
8813
8833
  if (!nodes)
8814
8834
  return null;
@@ -8823,8 +8843,8 @@ function renderToolTemplate(source, input, output) {
8823
8843
  }
8824
8844
  }
8825
8845
  const root = { input, output: structured };
8826
- const bareOutputNodes = nodes.map((node) => typeof node !== "string" && node.steps.length === 1 && ("field" in node.steps[0]) && node.steps[0].field === "output" && !node.each && !node.count && node.join === undefined ? truncate(text ?? "", node.cap) : node);
8827
- return truncate(render(bareOutputNodes, root), 1000);
8846
+ const bareOutputNodes = nodes.map((node) => typeof node !== "string" && node.steps.length === 1 && ("field" in node.steps[0]) && node.steps[0].field === "output" && !node.each && !node.count && node.join === undefined ? truncate(text ?? "", node.cap ?? (legacyPreview ? 300 : undefined), legacyPreview) : node);
8847
+ return truncate(render(bareOutputNodes, root, legacyPreview), legacyPreview ? 1000 : undefined, legacyPreview);
8828
8848
  }
8829
8849
  var templates;
8830
8850
  var init_historian_tool_template = __esm(() => {
@@ -9182,7 +9202,7 @@ var init_magic_context = __esm(() => {
9182
9202
  ctx.addIssue({ code: "custom", message: error });
9183
9203
  }),
9184
9204
  literal(false)
9185
- ])).optional().describe('Readable historian tool expansions, keyed by exact host tool name. Templates override built-in defaults; false disables an expansion. Supports ${input.path}, ${output.path}, bare ${output}, array [N], [*].field, .each("${field}"), .join("separator"), .count and final .truncate(N). Missing fields are empty; placeholders default to 300 characters, lists to 10 elements, expansions to 1000 characters. Valid in user and project config; affects historian/recomp and verbose ctx_expand only, never the wire or default ctx_expand transcript.'),
9205
+ ])).optional().describe('Readable historian tool expansions, keyed by exact host tool name. Templates override built-in defaults; false disables an expansion. Supports ${input.path}, ${output.path}, bare ${output}, array [N], [*].field, .each("${field}"), .join("separator"), .count and final .truncate(N). Missing fields are empty. Historian text and lists have no implicit caps; explicit .truncate(N) keeps complete sentences and marks omitted characters, or emits only the marker if no sentence fits. When the ctx_expand history-recovery tool is called in verbose mode, its tool-call previews retain their original built-in templates and limits (300 characters per placeholder, 10 list elements, 1000 characters per expansion). Valid in user and project config; never changes the wire or default ctx_expand transcript.'),
9186
9206
  opencode: OpenCodeHarnessBlockSchema.optional(),
9187
9207
  pi: PiHarnessBlockSchema.optional(),
9188
9208
  omp: OmpHarnessBlockSchema.optional(),
@@ -9326,7 +9346,11 @@ var init_magic_context = __esm(() => {
9326
9346
  }).catchall(number2().min(5000).max(2000000)).optional().describe("Absolute token thresholds per model. When matched, overrides execute_threshold_percentage for that model. Accepts `default` for all models or per-model keys. Values above 90% × context_limit are clamped with a warning log. Min 5_000, max 2_000_000."),
9327
9347
  protected_tokens: number2().int().min(PROTECTED_TOKENS_MIN).max(1e6).optional().describe("Positive integer token floor to protect from automatic reclaim (min: 4_000, max: 1_000_000). When omitted, the derived default is clamp(round(0.05 × usableSoft), min(16_000, round(0.08 × usableSoft)), 64_000)."),
9328
9348
  protected_tags: unknown().optional().describe("Deprecated: number of recent tags to protect. Ignored for behaviour; use protected_tokens instead.").meta({ deprecated: true }),
9329
- clear_reasoning_age: number2().min(10).default(50).describe("Clear reasoning/thinking blocks older than N tags (default: 50)"),
9349
+ clear_reasoning_age: unknown().optional().describe("Deprecated and ignored. Use keep_reasoning_tokens instead.").meta({ deprecated: true }),
9350
+ keep_reasoning_tokens: union([
9351
+ number2().int().min(0).max(1e6),
9352
+ object({ default: number2().int().min(0).max(1e6).optional() }).catchall(number2().int().min(0).max(1e6))
9353
+ ]).optional().describe("Reasoning tokens to keep on rebuilding passes. Number or per-model object; exact, shorter model keys, provider/*, then default. Omitted: fixed 10,000. 0 removes all eligible historical reasoning; newest and exempt steps always stay."),
9330
9354
  history_budget_percentage: number2().min(0.05).max(0.5).default(DEFAULT_HISTORY_BUDGET_PERCENTAGE).describe("Fraction of usable context (context_limit × execute_threshold) reserved for the session history block (default: 0.15)"),
9331
9355
  historian_timeout_ms: number2().min(60000).default(DEFAULT_HISTORIAN_TIMEOUT_MS).describe("Timeout for each historian prompt call in both TypeScript and Rust transform modes, in milliseconds (default: 600000)"),
9332
9356
  commit_cluster_trigger: object({
@@ -10891,6 +10915,11 @@ function warnProtectedTagsDeprecationOnce() {
10891
10915
  function parsePluginConfig(rawConfig, recoveredTopLevelKeys = []) {
10892
10916
  const preMigrationWarnings = [];
10893
10917
  const configWithoutRemovedAgent = stripRemovedAgentConfig(rawConfig, preMigrationWarnings);
10918
+ if (Object.hasOwn(rawConfig, "clear_reasoning_age")) {
10919
+ const warning = "clear_reasoning_age is deprecated and ignored. Magic Context now keeps reasoning up to a token budget, keep_reasoning_tokens (default 10,000). Remove the key, or set keep_reasoning_tokens to a token count.";
10920
+ preMigrationWarnings.push(warning);
10921
+ log(`[magic-context] ${warning}`);
10922
+ }
10894
10923
  if (Object.hasOwn(rawConfig, "protected_tags")) {
10895
10924
  warnProtectedTagsDeprecationOnce();
10896
10925
  preMigrationWarnings.push("protected_tags is deprecated and ignored; use protected_tokens instead.");
@@ -12822,9 +12851,12 @@ __export(exports_sqlite, {
12822
12851
  Database: () => Database,
12823
12852
  SqliteAcquisitionBusyError: () => SqliteAcquisitionBusyError,
12824
12853
  SqliteRuntimeUnavailableError: () => SqliteRuntimeUnavailableError,
12854
+ assertTransformWrite: () => assertTransformWrite,
12825
12855
  beginSqliteWriterAsync: () => beginSqliteWriterAsync,
12826
12856
  detectSqliteRuntime: () => detectSqliteRuntime,
12857
+ getSqliteDatabasePath: () => getSqliteDatabasePath,
12827
12858
  getSqliteMemoryStats: () => getSqliteMemoryStats,
12859
+ guardSqliteTransformPass: () => guardSqliteTransformPass,
12828
12860
  isTransientSqliteError: () => isTransientSqliteError,
12829
12861
  loadSqliteModule: () => loadSqliteModule,
12830
12862
  prepareCachedStatement: () => prepareCachedStatement,
@@ -12885,6 +12917,8 @@ function installTransactionRouting(db, readonly) {
12885
12917
  configurable: true,
12886
12918
  writable: true,
12887
12919
  value: (sql) => {
12920
+ if (/^\s*(?:COMMIT|END)\b/i.test(sql))
12921
+ assertTransformWrite();
12888
12922
  if (/^\s*BEGIN\s+(?:IMMEDIATE|EXCLUSIVE)(?:\s+TRANSACTION)?\s*;?\s*$/i.test(sql)) {
12889
12923
  openWriterTransactions.delete(db);
12890
12924
  if (transformPassScope.getStore()?.active || backgroundWriterScope.getStore())
@@ -13014,6 +13048,14 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
13014
13048
  }
13015
13049
  return NodeSqliteDatabase;
13016
13050
  }
13051
+ function getSqliteDatabasePath(db) {
13052
+ for (const connection of trackedSqliteConnections.values()) {
13053
+ if (connection.reference.deref() === db) {
13054
+ return connection.filename === ":memory:" || connection.filename === "<buffer>" ? null : connection.filename;
13055
+ }
13056
+ }
13057
+ return null;
13058
+ }
13017
13059
  function pragmaValue(db, name) {
13018
13060
  const row = db.prepare(`PRAGMA ${name}`).get();
13019
13061
  if (!row)
@@ -13166,6 +13208,15 @@ function withSqliteTransformPass(operation) {
13166
13208
  function withoutSqliteTransformPass(operation) {
13167
13209
  return transformPassScope.run(undefined, operation);
13168
13210
  }
13211
+ function guardSqliteTransformPass(guard) {
13212
+ const lease = transformPassScope.getStore();
13213
+ if (lease)
13214
+ lease.guard = guard;
13215
+ }
13216
+ function assertTransformWrite() {
13217
+ if (!backgroundWriterScope.getStore())
13218
+ transformPassScope.getStore()?.guard?.assert();
13219
+ }
13169
13220
  function isTransientSqliteError(error) {
13170
13221
  if (!error || typeof error !== "object")
13171
13222
  return false;
@@ -13177,6 +13228,7 @@ function foregroundWaitLease() {
13177
13228
  return lease?.active && !admissionScope.getStore() && !backgroundWriterScope.getStore() ? lease : undefined;
13178
13229
  }
13179
13230
  function withAutocommitTimeout(db, operation) {
13231
+ assertTransformWrite();
13180
13232
  const lease = foregroundWaitLease();
13181
13233
  if (isInTransaction(db) || !lease && !backgroundWriterScope.getStore())
13182
13234
  return operation();
@@ -13197,6 +13249,7 @@ function withAutocommitTimeout(db, operation) {
13197
13249
  }
13198
13250
  }
13199
13251
  function acquireShort(db, acquire, site) {
13252
+ assertTransformWrite();
13200
13253
  const started = performance.now();
13201
13254
  const previous = pragmaNumber(db, "busy_timeout");
13202
13255
  const outer = writerAcquisitionScope.getStore();
@@ -13208,7 +13261,7 @@ function acquireShort(db, acquire, site) {
13208
13261
  let acquiredAt;
13209
13262
  try {
13210
13263
  const lease = foregroundWaitLease();
13211
- const timeout = lease ? Math.floor(lease.remainingWaitMs) : SHORT_BUSY_TIMEOUT_MS;
13264
+ const timeout = Math.floor(Math.min(lease ? lease.remainingWaitMs : SHORT_BUSY_TIMEOUT_MS, owner?.remainingMs?.() ?? lease?.guard?.remainingMs() ?? Infinity));
13212
13265
  db.exec(`PRAGMA busy_timeout=${timeout}`);
13213
13266
  acquire();
13214
13267
  acquiredAt = performance.now();
@@ -13281,29 +13334,53 @@ async function beginSqliteWriterAsync(db, site) {
13281
13334
  }
13282
13335
  }
13283
13336
  }
13284
- async function withAsyncPrivilegedWriter(db, operation) {
13337
+ async function withAsyncPrivilegedWriter(db, operation, options = {}) {
13285
13338
  const started = performance.now();
13339
+ const budgetMs = Math.max(0, Math.min(FOREGROUND_ACQUISITION_BUDGET_MS, options.budgetMs ?? Infinity));
13286
13340
  let attempts = 0;
13287
13341
  for (;; ) {
13342
+ options.signal?.throwIfAborted();
13343
+ options.assertCurrentPass?.();
13288
13344
  attempts++;
13345
+ let entered = false;
13289
13346
  try {
13290
13347
  const acquisition = {
13291
13348
  site: "privileged_writer",
13292
13349
  lane: "foreground",
13293
13350
  startedAt: started,
13294
13351
  attempts,
13295
- claimed: false
13352
+ claimed: false,
13353
+ remainingMs: () => Math.max(0, budgetMs - (performance.now() - started))
13296
13354
  };
13297
- return admissionScope.run(true, () => withSqliteTransformPass(() => writerAcquisitionScope.run(acquisition, () => withPrivilegedWriter(db, operation))));
13355
+ return admissionScope.run(true, () => withSqliteTransformPass(() => writerAcquisitionScope.run(acquisition, () => withPrivilegedWriter(db, () => {
13356
+ entered = true;
13357
+ return operation();
13358
+ }))));
13298
13359
  } catch (error) {
13299
- if (!(error instanceof SqliteAcquisitionBusyError))
13360
+ if (entered || !(error instanceof SqliteAcquisitionBusyError))
13300
13361
  throw error;
13362
+ options.beforeRetry?.(error);
13301
13363
  const elapsed = performance.now() - started;
13302
- if (elapsed >= FOREGROUND_ACQUISITION_BUDGET_MS) {
13364
+ if (elapsed >= budgetMs) {
13303
13365
  reportSqliteDiagnostic(`sqlite writer site=privileged_writer lane=foreground acquire_ms=${Math.round(elapsed)} attempts=${attempts} outcome=busy`);
13304
13366
  throw error;
13305
13367
  }
13306
- await new Promise((resolve) => setTimeout(resolve, Math.min(attempts === 1 ? 500 : 1000, FOREGROUND_ACQUISITION_BUDGET_MS - elapsed)));
13368
+ const backoff = attempts === 1 ? 500 : 1000;
13369
+ const delay = Math.min(options.jitter ? backoff * (0.8 + Math.random() * 0.4) : backoff, budgetMs - elapsed);
13370
+ await new Promise((resolve, reject) => {
13371
+ const abort = () => {
13372
+ clearTimeout(timer);
13373
+ options.signal?.removeEventListener("abort", abort);
13374
+ reject(options.signal?.reason ?? new Error("Writer wait aborted"));
13375
+ };
13376
+ const timer = setTimeout(() => {
13377
+ options.signal?.removeEventListener("abort", abort);
13378
+ resolve();
13379
+ }, delay);
13380
+ options.signal?.addEventListener("abort", abort, { once: true });
13381
+ if (options.signal?.aborted)
13382
+ abort();
13383
+ });
13307
13384
  }
13308
13385
  }
13309
13386
  }
@@ -29800,6 +29877,24 @@ var init_storage_tags = __esm(() => {
29800
29877
  var historian_tool_defaults_default;
29801
29878
  var init_historian_tool_defaults = __esm(() => {
29802
29879
  historian_tool_defaults_default = {
29880
+ ctx_note: "Note ${input.action} ${input.note_ids}: ${input.content} → ${output.truncate(160)}",
29881
+ ctx_memory: "Memory ${input.action} ${input.category} ${input.ids}: ${input.content} → ${output.truncate(160)}",
29882
+ todowrite: 'Todos (${input.todos.count}): ${input.todos.each("${status}: ${content}")}',
29883
+ question: 'Asked user: ${input.questions[*].question.join(" / ")} → ${output}',
29884
+ task: "Task ${input.subagent_type}: ${input.prompt.truncate(600)} → ${output.truncate(200)}",
29885
+ ask: 'Asked: ${input.question} [options: ${input.options.join(" / ")}] ${input.text}${input.resolution} → ${output.truncate(300)}',
29886
+ peer_send: "PM ${input.recipient}: ${input.message}",
29887
+ board: 'Board ${input.verb}${input.board_lane}: ${input.ops.each("${op} ${lane_label} ${item}${props.text}${lane.items} ${state}${props.state}${lane.status}${id}${title}")}',
29888
+ room: "Room ${input.action} ${input.room_label}: ${input.room_detail}",
29889
+ work: "Work ${input.action} ${input.title}${input.id}: ${input.prompt.truncate(600)}${input.notes.truncate(600)} → ${output.truncate(160)}",
29890
+ knowhow: "Looked up how-to: ${input.query}${input.id}"
29891
+ };
29892
+ });
29893
+
29894
+ // ../plugin/src/shared/historian-tool-preview-defaults.json
29895
+ var historian_tool_preview_defaults_default;
29896
+ var init_historian_tool_preview_defaults = __esm(() => {
29897
+ historian_tool_preview_defaults_default = {
29803
29898
  ctx_note: "Note ${input.action} ${input.note_ids}: ${input.content} → ${output.truncate(160)}",
29804
29899
  ctx_memory: "Memory ${input.action} ${input.category} ${input.ids}: ${input.content} → ${output.truncate(160)}",
29805
29900
  todowrite: 'Todos (${input.todos.count}): ${input.todos.each("${status}: ${content}")}',
@@ -29815,20 +29910,52 @@ var init_historian_tool_defaults = __esm(() => {
29815
29910
  });
29816
29911
 
29817
29912
  // ../plugin/src/shared/historian-tool-expansions.ts
29818
- function expandToolPart(part, overrides) {
29913
+ function builtinInput(tool, input) {
29914
+ if (!input || typeof input !== "object")
29915
+ return input;
29916
+ const i = input;
29917
+ if (tool === "peer_send") {
29918
+ const external = i.external;
29919
+ const recipient = i.reply_to_pmid ? `reply to ${i.reply_to_pmid}` : i.agent || i.agent_id ? `to ${i.agent || i.agent_id}` : external?.session_id || i.session_id ? `to session ${external?.session_id || i.session_id}` : "to unknown recipient";
29920
+ return { ...i, recipient };
29921
+ }
29922
+ if (tool === "room") {
29923
+ return {
29924
+ ...i,
29925
+ room_label: i.room_id || i.title,
29926
+ room_detail: i.action === "create" || i.action === "invite" ? `invitees: ${renderToolTemplate("${input.invitees}", i)}` : i.text
29927
+ };
29928
+ }
29929
+ if (tool === "board") {
29930
+ return {
29931
+ ...i,
29932
+ board_lane: i.lane ? ` ${i.lane}` : "",
29933
+ ops: Array.isArray(i.ops) ? i.ops.map((op) => op && typeof op === "object" ? {
29934
+ ...op,
29935
+ lane_label: typeof op.lane === "string" ? op.lane : op.lane?.title || op.lane?.id
29936
+ } : op) : i.ops
29937
+ };
29938
+ }
29939
+ return input;
29940
+ }
29941
+ function expandToolPart(part, overrides, legacyPreview = false) {
29819
29942
  if (!part || typeof part !== "object")
29820
29943
  return null;
29821
29944
  const p = part;
29822
29945
  if (p.type !== "tool" || typeof p.tool !== "string")
29823
29946
  return null;
29824
- const template = overrides && Object.hasOwn(overrides, p.tool) ? overrides[p.tool] : Object.hasOwn(historian_tool_defaults_default, p.tool) ? historian_tool_defaults_default[p.tool] : undefined;
29947
+ const builtinDefaults = legacyPreview ? historian_tool_preview_defaults_default : historian_tool_defaults_default;
29948
+ const overridden = overrides && Object.hasOwn(overrides, p.tool);
29949
+ const template = overridden ? overrides[p.tool] : Object.hasOwn(builtinDefaults, p.tool) ? builtinDefaults[p.tool] : undefined;
29825
29950
  if (typeof template !== "string")
29826
29951
  return null;
29827
29952
  const state = p.state;
29828
- return renderToolTemplate(template, state?.input, state?.output ?? state?.error);
29953
+ const input = overridden || legacyPreview ? state?.input : builtinInput(p.tool, state?.input);
29954
+ return renderToolTemplate(template, input, state?.output ?? state?.error, legacyPreview);
29829
29955
  }
29830
29956
  var init_historian_tool_expansions = __esm(() => {
29831
29957
  init_historian_tool_defaults();
29958
+ init_historian_tool_preview_defaults();
29832
29959
  init_historian_tool_template();
29833
29960
  });
29834
29961
 
@@ -30591,7 +30718,8 @@ var init_overflow_detection = __esm(() => {
30591
30718
  PREFIX_BOUND_THINKING_MODELS = [
30592
30719
  ["fable", 5, 1],
30593
30720
  ["opus", 5, 5],
30594
- ["sonnet", 5, 5]
30721
+ ["sonnet", 5, 5],
30722
+ ["haiku", 5, 5]
30595
30723
  ];
30596
30724
  PREFIX_BOUND_THINKING_PATTERN = new RegExp(`(?:^|[-_.:/])(?:${PREFIX_BOUND_THINKING_MODELS.map(([family, major, minor]) => `${family}[-_.]?${major}[-_.]${minor}`).join("|")})(?:$|[-_.:/@])`, "i");
30597
30725
  });
@@ -42624,6 +42752,9 @@ function mergeRawConfigs(base, override) {
42624
42752
  }
42625
42753
  function parsePiConfig(rawConfig, recoveredTopLevelKeys = []) {
42626
42754
  const preMigrationWarnings = [];
42755
+ if (Object.hasOwn(rawConfig, "clear_reasoning_age")) {
42756
+ preMigrationWarnings.push("clear_reasoning_age is deprecated and ignored. Magic Context now keeps reasoning up to a token budget, keep_reasoning_tokens (default 10,000). Remove the key, or set keep_reasoning_tokens to a token count.");
42757
+ }
42627
42758
  const configWithoutRemovedAgent = stripRemovedAgentConfig(rawConfig, preMigrationWarnings);
42628
42759
  const hasDeprecatedProtectedTags = Object.hasOwn(rawConfig, "protected_tags");
42629
42760
  if (hasDeprecatedProtectedTags) {
@@ -46799,6 +46930,20 @@ function formatUnresolvedCompartmentSession(session) {
46799
46930
  return `session=${session.sessionId} unresolved=${session.count}`;
46800
46931
  }
46801
46932
 
46933
+ // src/commands/reasoning-config-migration.ts
46934
+ function removeDeprecatedReasoningAge(config) {
46935
+ if (!Object.hasOwn(config, "clear_reasoning_age"))
46936
+ return [];
46937
+ const age = config.clear_reasoning_age;
46938
+ delete config.clear_reasoning_age;
46939
+ const messages = [
46940
+ "Removed deprecated clear_reasoning_age (reasoning is now kept up to a token budget, keep_reasoning_tokens, default 10,000)."
46941
+ ];
46942
+ if (typeof age === "number" && age < 50)
46943
+ messages.push("You had set a lower age to keep less reasoning; set keep_reasoning_tokens (for example 8000) to keep less than the default.");
46944
+ return messages;
46945
+ }
46946
+
46802
46947
  // src/commands/doctor-opencode.ts
46803
46948
  import { spawnSync as spawnSync10 } from "node:child_process";
46804
46949
  import { existsSync as existsSync45, mkdtempSync as mkdtempSync3, readFileSync as readFileSync28, rmSync as rmSync8 } from "node:fs";
@@ -47615,6 +47760,13 @@ async function runDoctor2(options = {}) {
47615
47760
  log3.success("Removed deprecated compaction_markers (always-on since v0.21.4)");
47616
47761
  fixed++;
47617
47762
  }
47763
+ const reasoningMessages = removeDeprecatedReasoningAge(mcConfig);
47764
+ if (reasoningMessages.length > 0) {
47765
+ mcChanged = true;
47766
+ fixed++;
47767
+ for (const message of reasoningMessages)
47768
+ log3.success(message);
47769
+ }
47618
47770
  for (const deadKey of ["auto_drop_tool_age", "drop_tool_structure"]) {
47619
47771
  if (deadKey in mcConfig) {
47620
47772
  delete mcConfig[deadKey];
@@ -49126,7 +49278,8 @@ async function runHealthChecks2(options) {
49126
49278
  const repairPlan = {
49127
49279
  addPackageEntry: false,
49128
49280
  writeUserConfig: false,
49129
- clearCachePaths: []
49281
+ clearCachePaths: [],
49282
+ reasoningAgeConfigPaths: []
49130
49283
  };
49131
49284
  const self = options.deps.selfVersion();
49132
49285
  const pi = options.deps.detectPiBinary();
@@ -49197,6 +49350,10 @@ async function runHealthChecks2(options) {
49197
49350
  continue;
49198
49351
  }
49199
49352
  const parsed = readMagicContextJsonc(path, label);
49353
+ if (!parsed.error && Object.hasOwn(parsed.value, "clear_reasoning_age")) {
49354
+ repairPlan.reasoningAgeConfigPaths.push(path);
49355
+ add2(results, "warn", `${label} clear_reasoning_age is deprecated and ignored; doctor removes it`);
49356
+ }
49200
49357
  if (parsed.error)
49201
49358
  add2(results, "fail", `${label} magic-context.jsonc is invalid JSONC: ${parsed.error}`);
49202
49359
  else
@@ -49452,6 +49609,21 @@ function writeDefaultMagicContextConfig(path) {
49452
49609
  }
49453
49610
  function repair2(plan, prompts) {
49454
49611
  let fixed = 0;
49612
+ for (const path of plan.reasoningAgeConfigPaths) {
49613
+ const content = readFileSync30(path, "utf8");
49614
+ const parsed = readJsonc2(path);
49615
+ if (parsed.error)
49616
+ continue;
49617
+ const messages = removeDeprecatedReasoningAge(parsed.value);
49618
+ if (messages.length === 0)
49619
+ continue;
49620
+ writeFileAtomic(path, removeJsoncValue(content, ["clear_reasoning_age"]), {
49621
+ ownerOnly: true
49622
+ });
49623
+ for (const message of messages)
49624
+ prompts.log.success(message);
49625
+ fixed++;
49626
+ }
49455
49627
  if (plan.addPackageEntry) {
49456
49628
  const settingsPath = getPiUserExtensionsPath();
49457
49629
  try {
@@ -49678,6 +49850,7 @@ var init_doctor_pi = __esm(async () => {
49678
49850
  init_tick_failure();
49679
49851
  init_embedding_synapse();
49680
49852
  init_data_path();
49853
+ init_jsonc_edit();
49681
49854
  init_jsonc_parser();
49682
49855
  init_config2();
49683
49856
  init_model_chain_health();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/magic-context",
3
- "version": "0.46.1",
3
+ "version": "0.47.0",
4
4
  "description": "Unified CLI for Magic Context — setup, doctor, and migration across OpenCode, Pi, and OMP",
5
5
  "keywords": [
6
6
  "opencode",