@wolfx/opencode-magic-context 0.27.2 → 0.28.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 (66) hide show
  1. package/dist/agents/language-directive.d.ts +27 -0
  2. package/dist/agents/language-directive.d.ts.map +1 -0
  3. package/dist/agents/magic-context-prompt.d.ts +1 -1
  4. package/dist/agents/magic-context-prompt.d.ts.map +1 -1
  5. package/dist/config/project-security.d.ts +1 -0
  6. package/dist/config/project-security.d.ts.map +1 -1
  7. package/dist/config/schema/magic-context.d.ts +4 -0
  8. package/dist/config/schema/magic-context.d.ts.map +1 -1
  9. package/dist/features/magic-context/dreamer/refresh-primers.d.ts +1 -0
  10. package/dist/features/magic-context/dreamer/refresh-primers.d.ts.map +1 -1
  11. package/dist/features/magic-context/dreamer/task-config.d.ts +1 -1
  12. package/dist/features/magic-context/dreamer/task-config.d.ts.map +1 -1
  13. package/dist/features/magic-context/dreamer/task-executor.d.ts +1 -0
  14. package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
  15. package/dist/features/magic-context/dreamer/task-scheduler.d.ts +1 -0
  16. package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
  17. package/dist/features/magic-context/dreamer/verify.d.ts +1 -0
  18. package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
  19. package/dist/features/magic-context/memory/memory-migration.d.ts +1 -0
  20. package/dist/features/magic-context/memory/memory-migration.d.ts.map +1 -1
  21. package/dist/features/magic-context/sidekick/agent.d.ts +1 -0
  22. package/dist/features/magic-context/sidekick/agent.d.ts.map +1 -1
  23. package/dist/features/magic-context/storage-tags.d.ts +0 -5
  24. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  25. package/dist/features/magic-context/transform-decision-log.d.ts +1 -0
  26. package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -1
  27. package/dist/features/magic-context/user-memory/review-user-memories.d.ts +1 -0
  28. package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
  29. package/dist/hooks/magic-context/command-handler.d.ts +1 -0
  30. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  31. package/dist/hooks/magic-context/compartment-runner-historian.d.ts +1 -0
  32. package/dist/hooks/magic-context/compartment-runner-historian.d.ts.map +1 -1
  33. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  34. package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
  35. package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
  36. package/dist/hooks/magic-context/compartment-runner-types.d.ts +1 -0
  37. package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
  38. package/dist/hooks/magic-context/compartment-runner-validation.d.ts +1 -1
  39. package/dist/hooks/magic-context/compartment-runner-validation.d.ts.map +1 -1
  40. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts +13 -3
  41. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts.map +1 -1
  42. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  43. package/dist/hooks/magic-context/hook.d.ts +1 -0
  44. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  45. package/dist/hooks/magic-context/recomp-orchestrator.d.ts +1 -0
  46. package/dist/hooks/magic-context/recomp-orchestrator.d.ts.map +1 -1
  47. package/dist/hooks/magic-context/system-prompt-hash.d.ts +2 -0
  48. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  49. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +235 -57
  52. package/dist/plugin/conflict-warning-hook.d.ts.map +1 -1
  53. package/dist/plugin/dream-timer.d.ts +1 -0
  54. package/dist/plugin/dream-timer.d.ts.map +1 -1
  55. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  56. package/dist/shared/announcement.d.ts +1 -1
  57. package/dist/shared/announcement.d.ts.map +1 -1
  58. package/dist/shared/tui-preferences.d.ts +1 -1
  59. package/dist/tui/badge-contrast.d.ts +31 -0
  60. package/dist/tui/badge-contrast.d.ts.map +1 -0
  61. package/package.json +1 -1
  62. package/src/shared/announcement.ts +3 -6
  63. package/src/shared/tui-preferences.ts +2 -2
  64. package/src/tui/badge-contrast.test.ts +45 -0
  65. package/src/tui/badge-contrast.ts +46 -0
  66. package/src/tui/slots/sidebar-content.tsx +2 -1
package/dist/index.js CHANGED
@@ -169,6 +169,98 @@ var init_logger = __esm(() => {
169
169
  }
170
170
  });
171
171
 
172
+ // src/agents/language-directive.ts
173
+ function resolveLanguageName(language) {
174
+ const code = typeof language === "string" ? language.trim().toLowerCase() : "";
175
+ if (!/^[a-z]{2}$/.test(code))
176
+ return "";
177
+ let english;
178
+ try {
179
+ english = ENGLISH_LANGUAGE_NAMES.of(code) ?? undefined;
180
+ } catch {
181
+ return "";
182
+ }
183
+ if (!english)
184
+ return "";
185
+ let endonym;
186
+ try {
187
+ endonym = new Intl.DisplayNames([code], { type: "language", fallback: "none" }).of(code) ?? undefined;
188
+ } catch {
189
+ endonym = undefined;
190
+ }
191
+ return endonym && endonym !== english ? `${english} (${endonym})` : english;
192
+ }
193
+ function isValidLanguageCode(language) {
194
+ return resolveLanguageName(language) !== "";
195
+ }
196
+ function buildContentLanguageDirective(language, options = {}) {
197
+ const target = resolveLanguageName(language);
198
+ if (!target)
199
+ return "";
200
+ const lines = [
201
+ "## Output language",
202
+ "",
203
+ `Write human-readable prose you author in: ${target}.`,
204
+ "",
205
+ "Do not translate or rename structural tokens. Copy required output schemas exactly:",
206
+ "- XML tag names, XML attribute names, JSON keys, tool names, tool-call argument keys, enum values, booleans/null, and required sentinel strings stay in English exactly as shown.",
207
+ "- Keep code identifiers, file paths, commands, config keys, CLI flags, URLs, commit hashes, model/provider IDs, stack traces, diagnostics, and transcript role markers such as U:, A:, and TC: verbatim.",
208
+ "- Localize only free-text prose values/content: summaries, memory text, explanations, titles, observations, and answers — unless the prompt says to preserve original wording.",
209
+ "",
210
+ "These literal values must remain English when used:",
211
+ "PROJECT_RULES, ARCHITECTURE, CONSTRAINTS, CONFIG_VALUES, NAMING;",
212
+ "causal_incident, trajectory_correction;",
213
+ "feature, design, docs, release, investigation, bug, refactor, infra;",
214
+ "memory, observation; true, false; No relevant memories found.",
215
+ "",
216
+ "Preserve the required output shape. Do not add commentary outside the requested XML/JSON/tool output."
217
+ ];
218
+ if (options.preserveUserQuotes) {
219
+ lines.push("", `Preserve U: lines and directly quoted user text in their original source language; write the surrounding summary prose in ${target}.`);
220
+ }
221
+ if (options.retrospective) {
222
+ lines.push("", `Write the lesson text in ${target}; paraphrase source text and never quote the user.`);
223
+ }
224
+ return lines.join(`
225
+ `);
226
+ }
227
+ function withContentLanguageDirective(systemPrompt, language, options = {}) {
228
+ const directive = buildContentLanguageDirective(language, options);
229
+ return directive ? `${systemPrompt}
230
+
231
+ ${directive}` : systemPrompt;
232
+ }
233
+ function buildMigrationLanguageDirective(language) {
234
+ const target = resolveLanguageName(language);
235
+ if (!target)
236
+ return "";
237
+ return [
238
+ "## Output language",
239
+ "",
240
+ "Preserve each migrated memory's existing language — do NOT translate a memory just because an output language is set. When merging memories written in different languages, use the language of the clearest / source-majority memory; otherwise keep the source phrasing. Only the category re-mapping changes."
241
+ ].join(`
242
+ `);
243
+ }
244
+ function withMigrationLanguageDirective(systemPrompt, language) {
245
+ const directive = buildMigrationLanguageDirective(language);
246
+ return directive ? `${systemPrompt}
247
+
248
+ ${directive}` : systemPrompt;
249
+ }
250
+ function buildPrimaryLanguageDirective(language) {
251
+ const target = resolveLanguageName(language);
252
+ if (!target)
253
+ return "";
254
+ return `Use ${target} for your natural-language replies to the user unless the user explicitly asks for another language. Keep code, identifiers, file paths, commands, logs, and quoted text verbatim.`;
255
+ }
256
+ var ENGLISH_LANGUAGE_NAMES;
257
+ var init_language_directive = __esm(() => {
258
+ ENGLISH_LANGUAGE_NAMES = new Intl.DisplayNames(["en"], {
259
+ type: "language",
260
+ fallback: "none"
261
+ });
262
+ });
263
+
172
264
  // src/shared/jsonc-parser.ts
173
265
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
174
266
  function stripJsonComments(content) {
@@ -15155,6 +15247,7 @@ function defaultTaskConfig(task) {
15155
15247
  var DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65, EXECUTE_THRESHOLD_CAP_MESSAGE = "execute_threshold is capped at 80% for cache safety: a single large agent step can overflow the context window before Magic Context can compact between turns, forcing OpenCode's native compaction (hard to recover from). 80% also leaves headroom below the 85%/95% emergency bands. Use a value between 20 and 80.", DEFAULT_HISTORIAN_TIMEOUT_MS = 300000, DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15, DreamingTaskSchema, PiThinkingLevelSchema, CronScheduleSchema, DreamTaskBaseConfigSchema, PromotionThresholdSchema, PrimerPromotionThresholdSchema, DreamTaskConfigSchema, ReviewUserMemoriesTaskConfigSchema, PromotePrimersTaskConfigSchema, DEFAULT_TASK_SCHEDULES, DreamTasksSchema, DreamerConfigSchema, SidekickConfigSchema, HistorianConfigSchema, BaseEmbeddingConfigSchema, EmbeddingConfigSchema, MagicContextConfigSchema;
15156
15248
  var init_magic_context = __esm(() => {
15157
15249
  init_zod();
15250
+ init_language_directive();
15158
15251
  init_cron();
15159
15252
  init_task_registry();
15160
15253
  init_agent_overrides();
@@ -15268,6 +15361,7 @@ var init_magic_context = __esm(() => {
15268
15361
  });
15269
15362
  MagicContextConfigSchema = exports_external.object({
15270
15363
  enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
15364
+ language: exports_external.string().trim().toLowerCase().refine((s) => isValidLanguageCode(s), 'language must be a 2-letter ISO 639-1 code (e.g. "tr", "es", "de")').optional().describe("Output language for Magic Context's generated content and guidance, as a " + '2-letter ISO 639-1 code (e.g. "tr", "es", "de", "ja", "pt"). When set, the ' + "historian, dreamer, sidekick, and the agent-guidance block instruct the model to " + "write its PROSE in this language while keeping all structural tokens (XML tags, " + "the five memory category names, code identifiers, file paths) in English. " + "USER-LEVEL ONLY (ignored in project config for security). Unset = today's " + "behavior (model mirrors the conversation; English scaffolding). Changing it " + "triggers one cache re-materialization; existing compartments/memories keep their " + "original language until naturally rewritten."),
15271
15365
  ctx_reduce_enabled: exports_external.boolean().default(true).describe("When false, ctx_reduce tool is hidden, all nudges disabled, and prompt guidance about ctx_reduce stripped. Heuristic cleanup, compartments, memory, and other features still work. (default: true)"),
15272
15366
  historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
15273
15367
  dreamer: DreamerConfigSchema.optional().describe("Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)"),
@@ -150998,7 +151092,7 @@ function enforceSchemaFence(db, dbPath, latestSupportedVersion) {
150998
151092
  return true;
150999
151093
  }
151000
151094
  lastSchemaFenceRejection = { persistedVersion, supportedVersion: latestSupportedVersion };
151001
- log(`[magic-context] storage fatal: refusing to open ${dbPath}; database schema v${persistedVersion} is newer than this binary supports (max v${latestSupportedVersion}). Upgrade Magic Context/OpenCode/Pi before writing to this cache.`);
151095
+ log(`[magic-context] storage fatal: refusing to open ${dbPath}; database schema v${persistedVersion} is newer than this binary supports (max v${latestSupportedVersion}). A pinned or stale plugin is likely sharing this database with a newer instance; update or unpin Magic Context with 'npx @cortexkit/magic-context@latest doctor --force', then restart.`);
151002
151096
  return false;
151003
151097
  }
151004
151098
  function setSqlitePragmaConfig(config2) {
@@ -155731,10 +155825,18 @@ function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, li
155731
155825
  WHERE session_id = ? AND status = 'active'
155732
155826
  ORDER BY tag_number DESC LIMIT 1 OFFSET ?
155733
155827
  )` : "";
155734
- const params = protectedTags > 0 ? [sessionId, sessionId, protectedTags - 1, boundedLimit] : [sessionId, boundedLimit];
155828
+ const excludeStateTools = RECLAIM_HINT_EXCLUDED_LIST ? `AND (tool_name IS NULL OR tool_name NOT IN (${RECLAIM_HINT_EXCLUDED_LIST}))` : "";
155829
+ const valueFloor = `AND (
155830
+ (token_count IS NULL AND input_token_count IS NULL)
155831
+ OR (COALESCE(token_count, 0) + COALESCE(input_token_count, 0)) >= ?
155832
+ )`;
155833
+ const params = protectedTags > 0 ? [sessionId, RECLAIM_HINT_MIN_TOKENS, sessionId, protectedTags - 1, boundedLimit] : [sessionId, RECLAIM_HINT_MIN_TOKENS, boundedLimit];
155735
155834
  const rows = db.prepare(`SELECT tag_number, tool_name
155736
155835
  FROM tags
155737
- WHERE session_id = ? AND status = 'active' AND type = 'tool' ${whereProtected}
155836
+ WHERE session_id = ? AND status = 'active' AND type = 'tool'
155837
+ ${excludeStateTools}
155838
+ ${valueFloor}
155839
+ ${whereProtected}
155738
155840
  ORDER BY tag_number ASC, id ASC
155739
155841
  LIMIT ?`).all(...params);
155740
155842
  return rows.filter((row) => typeof row.tag_number === "number").map((row) => ({
@@ -156124,7 +156226,7 @@ function deleteToolTagsByOwner(db, sessionId, ownerMsgId) {
156124
156226
  const result = getDeleteToolTagsByOwnerStatement(db).run(sessionId, ownerMsgId);
156125
156227
  return result.changes ?? 0;
156126
156228
  }
156127
- var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements, CONTENT_ID_SUFFIX, updateTagTokenCountStatements, updateTagInputTokenCountStatements, getOwnerScopedToolTagNumbersStatements, getMinMessageTagNumberForRawIdStatements, TAGGER_FLOOR_SCAN_MESSAGES = 8, TAGGER_FLOOR_MAX_PROBES = 64, TAGGER_FLOOR_SAFETY_MARGIN = 256, TAGGER_FLOOR_PER_SKIP_MARGIN = 64, TAG_SELECT_COLUMNS = "id, message_id, type, status, drop_mode, tool_name, input_byte_size, byte_size, reasoning_byte_size, session_id, tag_number, caveman_depth, tool_owner_message_id", getActiveTagsBySessionStatements, getMaxDroppedTagNumberStatements, getToolTagNumberByOwnerStatements, getNullOwnerToolTagStatements, adoptNullOwnerToolTagStatements, deleteToolTagsByOwnerStatements;
156229
+ var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements, CONTENT_ID_SUFFIX, RECLAIM_HINT_EXCLUDED_TOOLS, RECLAIM_HINT_MIN_TOKENS = 250, RECLAIM_HINT_EXCLUDED_LIST, updateTagTokenCountStatements, updateTagInputTokenCountStatements, getOwnerScopedToolTagNumbersStatements, getMinMessageTagNumberForRawIdStatements, TAGGER_FLOOR_SCAN_MESSAGES = 8, TAGGER_FLOOR_MAX_PROBES = 64, TAGGER_FLOOR_SAFETY_MARGIN = 256, TAGGER_FLOOR_PER_SKIP_MARGIN = 64, TAG_SELECT_COLUMNS = "id, message_id, type, status, drop_mode, tool_name, input_byte_size, byte_size, reasoning_byte_size, session_id, tag_number, caveman_depth, tool_owner_message_id", getActiveTagsBySessionStatements, getMaxDroppedTagNumberStatements, getToolTagNumberByOwnerStatements, getNullOwnerToolTagStatements, adoptNullOwnerToolTagStatements, deleteToolTagsByOwnerStatements;
156128
156230
  var init_storage_tags = __esm(() => {
156129
156231
  insertTagStatements = new WeakMap;
156130
156232
  updateTagStatusStatements = new WeakMap;
@@ -156137,6 +156239,8 @@ var init_storage_tags = __esm(() => {
156137
156239
  updateTagByteSizeStatements = new WeakMap;
156138
156240
  updateTagInputByteSizeStatements = new WeakMap;
156139
156241
  CONTENT_ID_SUFFIX = /:(?:p|file)\d+$/;
156242
+ RECLAIM_HINT_EXCLUDED_TOOLS = ["todowrite"];
156243
+ RECLAIM_HINT_EXCLUDED_LIST = RECLAIM_HINT_EXCLUDED_TOOLS.map((name2) => `'${name2.replace(/'/g, "''")}'`).join(", ");
156140
156244
  updateTagTokenCountStatements = new WeakMap;
156141
156245
  updateTagInputTokenCountStatements = new WeakMap;
156142
156246
  getOwnerScopedToolTagNumbersStatements = new WeakMap;
@@ -157804,8 +157908,13 @@ async function sendSchemaFenceWarning(client, directory, detail) {
157804
157908
  `newer build (OpenCode and Pi share one database). This build only supports`,
157805
157909
  `up to v${detail.supportedVersion}, so it has fail-closed to avoid corrupting the cache.`,
157806
157910
  "",
157807
- "Update Magic Context on this harness (or update OpenCode/Pi) to the latest",
157808
- "version, then restart. Your data is safe nothing is disabled permanently."
157911
+ "This usually means a pinned or stale plugin is sharing the database with a",
157912
+ "newer instance. Update or unpin Magic Context on this harness (or update",
157913
+ "OpenCode/Pi) to the latest version, then restart. The fastest fix is:",
157914
+ "",
157915
+ " npx @cortexkit/magic-context@latest doctor --force",
157916
+ "",
157917
+ "Your data is safe; nothing is disabled permanently."
157809
157918
  ].join(`
157810
157919
  `);
157811
157920
  try {
@@ -158075,7 +158184,7 @@ __export(exports_task_config, {
158075
158184
  dreamTaskScheduled: () => dreamTaskScheduled,
158076
158185
  buildDreamTaskRuntimeConfigs: () => buildDreamTaskRuntimeConfigs
158077
158186
  });
158078
- function buildDreamTaskRuntimeConfigs(dreamer) {
158187
+ function buildDreamTaskRuntimeConfigs(dreamer, language) {
158079
158188
  const tasks = dreamer.tasks ?? {};
158080
158189
  return CANONICAL_DREAM_TASKS.map((task) => {
158081
158190
  const t = tasks[task] ?? {
@@ -158091,6 +158200,7 @@ function buildDreamTaskRuntimeConfigs(dreamer) {
158091
158200
  model,
158092
158201
  fallbackModels,
158093
158202
  thinkingLevel,
158203
+ language,
158094
158204
  timeoutMinutes: t.timeout_minutes ?? 20,
158095
158205
  promotionThreshold: t.promotion_threshold
158096
158206
  };
@@ -169057,8 +169167,8 @@ function buildHistorianFailureNotice(failureCount, lastError) {
169057
169167
  ].join(`
169058
169168
  `);
169059
169169
  }
169060
- function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationError) {
169061
- return [
169170
+ function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationError, language) {
169171
+ const prompt = [
169062
169172
  originalPrompt,
169063
169173
  "",
169064
169174
  "Your previous XML response was invalid and cannot be persisted.",
@@ -169071,6 +169181,7 @@ function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationEr
169071
169181
  previousOutput
169072
169182
  ].join(`
169073
169183
  `);
169184
+ return withContentLanguageDirective(prompt, language, { preserveUserQuotes: true });
169074
169185
  }
169075
169186
  function validateStoredCompartments(compartments) {
169076
169187
  if (compartments.length === 0) {
@@ -169147,6 +169258,7 @@ function getReducedRecompTokenBudget(currentBudget) {
169147
169258
  }
169148
169259
  var MIN_RECOMP_CHUNK_TOKEN_BUDGET = 20, HISTORIAN_PERSISTENT_FAILURE_THRESHOLD = 3;
169149
169260
  var init_compartment_runner_validation = __esm(async () => {
169261
+ init_language_directive();
169150
169262
  init_compartment_parser();
169151
169263
  await init_compartment_runner_mapping();
169152
169264
  });
@@ -169184,7 +169296,7 @@ async function runValidatedHistorianPass(args) {
169184
169296
  return finalResult;
169185
169297
  }
169186
169298
  await args.callbacks?.onRepairRetry?.(firstValidation.error ?? "invalid compartment output");
169187
- const repairPrompt = buildHistorianRepairPrompt(args.prompt, firstRun.result, firstValidation.error ?? "invalid compartment output");
169299
+ const repairPrompt = buildHistorianRepairPrompt(args.prompt, firstRun.result, firstValidation.error ?? "invalid compartment output", args.language);
169188
169300
  const repairRun = await runHistorianPrompt({
169189
169301
  ...args,
169190
169302
  prompt: repairPrompt,
@@ -174569,7 +174681,8 @@ ${chunkText}`,
174569
174681
  timeoutMs: historianTimeoutMs,
174570
174682
  fallbackModelId: deps.fallbackModelId,
174571
174683
  fallbackModels: deps.fallbackModels,
174572
- twoPass: deps.historianTwoPass
174684
+ twoPass: deps.historianTwoPass,
174685
+ language: deps.language
174573
174686
  });
174574
174687
  if (!validatedPass.ok) {
174575
174688
  sessionLog(sessionId, `historian failure: source=validation reason="${validatedPass.error}" chunkRange=${chunk.startIndex}-${chunk.endIndex} fallbackModel=${deps.fallbackModelId ?? "<none>"} twoPass=${deps.historianTwoPass ? "true" : "false"}`);
@@ -175045,6 +175158,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
175045
175158
  twoPass: deps.historianTwoPass,
175046
175159
  subagentKind: "recomp",
175047
175160
  agentId: HISTORIAN_RECOMP_AGENT,
175161
+ language: deps.language,
175048
175162
  callbacks: {
175049
175163
  onRepairRetry: async (error51) => {
175050
175164
  emitProgress(`Repair retry (pass ${passCount + 1})…`);
@@ -175487,6 +175601,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
175487
175601
  twoPass: deps.historianTwoPass,
175488
175602
  subagentKind: "recomp",
175489
175603
  agentId: HISTORIAN_RECOMP_AGENT,
175604
+ language: deps.language,
175490
175605
  callbacks: {
175491
175606
  onRepairRetry: async (error51) => {
175492
175607
  await sendIgnoredMessage(client, sessionId, `## Magic Recomp — Partial
@@ -177847,7 +177962,7 @@ async function runMemoryMigration(deps) {
177847
177962
  query: { directory },
177848
177963
  body: {
177849
177964
  agent: HISTORIAN_AGENT,
177850
- system: MIGRATION_SYSTEM_PROMPT,
177965
+ system: withMigrationLanguageDirective(MIGRATION_SYSTEM_PROMPT, deps.language),
177851
177966
  ...modelOverride ? { model: modelOverride } : {},
177852
177967
  parts: [{ type: "text", text: prompt, synthetic: true }]
177853
177968
  }
@@ -177926,6 +178041,7 @@ async function runMemoryMigration(deps) {
177926
178041
  }
177927
178042
  var V2_CATEGORIES, MIGRATED_BLOCK_RE, USER_OBS_BLOCK_RE, CATEGORY_BLOCK_RE = (cat) => new RegExp(`<${cat}>([\\s\\S]*?)</${cat}>`), MIGRATION_SYSTEM_PROMPT;
177928
178043
  var init_memory_migration = __esm(async () => {
178044
+ init_language_directive();
177929
178045
  init_shared();
177930
178046
  init_assistant_message_extractor();
177931
178047
  init_logger();
@@ -178042,6 +178158,7 @@ function buildRecompDeps(ctx, sessionId) {
178042
178158
  memoryEnabled: ctx.memoryEnabled,
178043
178159
  autoPromote: ctx.autoPromote,
178044
178160
  fallbackModels: ctx.fallbackModels,
178161
+ language: ctx.language,
178045
178162
  fallbackModelId: ctx.fallbackModelId ?? resolveLiveModelKey(ctx.liveSessionState, sessionId),
178046
178163
  historianTwoPass: ctx.historianTwoPass,
178047
178164
  ensureProjectRegistered: ctx.ensureProjectRegistered,
@@ -178173,7 +178290,8 @@ async function runUpgradeMemoryMigration(ctx, sessionId, migrationDirectory) {
178173
178290
  primaryModelId: ctx.fallbackModelId ?? resolveLiveModelKey(ctx.liveSessionState, sessionId),
178174
178291
  fallbackModels: ctx.fallbackModels,
178175
178292
  timeoutMs: ctx.historianTimeoutMs,
178176
- userMemoriesEnabled: ctx.userMemoriesEnabled
178293
+ userMemoriesEnabled: ctx.userMemoriesEnabled,
178294
+ language: ctx.language
178177
178295
  });
178178
178296
  return outcome.summary;
178179
178297
  } catch (error51) {
@@ -178258,15 +178376,12 @@ function shouldShowAnnouncement() {
178258
178376
  }
178259
178377
  return ordering > 0;
178260
178378
  }
178261
- var ANNOUNCEMENT_VERSION = "0.27.0", ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU", STATE_FILENAME = "last_announced_version";
178379
+ var ANNOUNCEMENT_VERSION = "0.28.0", ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU", STATE_FILENAME = "last_announced_version";
178262
178380
  var init_announcement = __esm(() => {
178263
178381
  init_data_path();
178264
178382
  ANNOUNCEMENT_FEATURES = [
178265
- "Dreamer V2: each maintenance task now runs on its own schedule (cron), with its own model. Configure them in setup, the dashboard, or magic-context.jsonc.",
178266
- "New 'classify' task scores each memory's importance so the most relevant stay in context as your work shifts; new 'retrospective' learns from moments you had to correct or re-explain. Both cache-safe, on by default, off anytime.",
178267
- "New Primers: durable answers to the questions that keep coming up about your project, kept current by the dreamer investigating the actual code.",
178268
- "Embedding storage no longer wipes your vectors when you change model or endpoint. Different models now coexist, so switching providers keeps your existing vectors.",
178269
- "Config moved to a shared CortexKit location (~/.config/cortexkit/ and <project>/.cortexkit/). This happens automatically on first run; your old file is preserved."
178383
+ `New 'language' option: set a top-level 2-letter language code (e.g. "tr" or "es") and Magic Context writes its summaries, memories, and guidance in that language, while keeping all structure (tags, categories, code, paths) in English. User-level only, off by default.`,
178384
+ "The ctx_reduce reminder now reflects how much tool output is actually reclaimable, instead of escalating to 'urgent' just because you're near compaction. It also no longer suggests dropping the agent's task list or tiny status outputs."
178270
178385
  ];
178271
178386
  });
178272
178387
 
@@ -178441,6 +178556,9 @@ function buildHiddenAgentConfig(prompt, allowedTools, maxSteps, overrides, agent
178441
178556
  };
178442
178557
  }
178443
178558
 
178559
+ // src/index.ts
178560
+ init_language_directive();
178561
+
178444
178562
  // src/config/index.ts
178445
178563
  init_jsonc_parser();
178446
178564
  import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
@@ -179189,6 +179307,10 @@ function stripUnsafeProjectConfigFields(projectRaw) {
179189
179307
  delete projectRaw.auto_update;
179190
179308
  warnings.push("Ignoring auto_update from project config (security: this setting only honors user-level config).");
179191
179309
  }
179310
+ if ("language" in projectRaw) {
179311
+ delete projectRaw.language;
179312
+ warnings.push("Ignoring language from project config (security: output language is a user-level setting).");
179313
+ }
179192
179314
  if ("sqlite" in projectRaw) {
179193
179315
  delete projectRaw.sqlite;
179194
179316
  warnings.push("Ignoring sqlite.* from project config (security: SQLite cache/mmap PRAGMAs apply to the " + "process-global shared database handle; only user-level config may set them).");
@@ -180021,6 +180143,9 @@ function buildDreamTaskPrompt(task, args) {
180021
180143
  // src/index.ts
180022
180144
  init_project_identity();
180023
180145
 
180146
+ // src/features/magic-context/sidekick/agent.ts
180147
+ init_language_directive();
180148
+
180024
180149
  // src/agents/sidekick.ts
180025
180150
  var SIDEKICK_AGENT = "sidekick";
180026
180151
 
@@ -180088,12 +180213,13 @@ async function runSidekick(deps) {
180088
180213
  throw error51;
180089
180214
  }
180090
180215
  const childSessionId = agentSessionId;
180216
+ const systemPrompt = withContentLanguageDirective(deps.config.system_prompt?.trim() || deps.config.prompt?.trim() || SIDEKICK_SYSTEM_PROMPT, deps.language);
180091
180217
  const sidekickRun = await promptSyncWithValidatedOutputRetry(deps.client, {
180092
180218
  path: { id: childSessionId },
180093
180219
  query: { directory: deps.sessionDirectory ?? deps.projectPath },
180094
180220
  body: {
180095
180221
  agent: SIDEKICK_AGENT,
180096
- system: deps.config.system_prompt?.trim() || deps.config.prompt?.trim() || SIDEKICK_SYSTEM_PROMPT,
180222
+ system: systemPrompt,
180097
180223
  parts: [{ type: "text", text: deps.userMessage, synthetic: true }]
180098
180224
  }
180099
180225
  }, {
@@ -181066,6 +181192,7 @@ var DREAMER_DOCS_AGENT = "dreamer-docs";
181066
181192
  var DREAMER_REVIEWER_AGENT = "dreamer-reviewer";
181067
181193
 
181068
181194
  // src/features/magic-context/dreamer/task-executor.ts
181195
+ init_language_directive();
181069
181196
  init_shared();
181070
181197
  init_assistant_message_extractor();
181071
181198
  init_logger();
@@ -181074,6 +181201,7 @@ init_memory();
181074
181201
  init_subagent_token_capture();
181075
181202
 
181076
181203
  // src/features/magic-context/user-memory/review-user-memories.ts
181204
+ init_language_directive();
181077
181205
  init_shared();
181078
181206
  init_assistant_message_extractor();
181079
181207
  init_logger();
@@ -181188,7 +181316,7 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
181188
181316
  query: { directory: args.sessionDirectory },
181189
181317
  body: {
181190
181318
  agent: DREAMER_REVIEWER_AGENT,
181191
- system: REVIEW_USER_MEMORIES_SYSTEM_PROMPT,
181319
+ system: withContentLanguageDirective(REVIEW_USER_MEMORIES_SYSTEM_PROMPT, args.language),
181192
181320
  ...modelBodyField(args.model),
181193
181321
  parts: [{ type: "text", text: prompt, synthetic: true }]
181194
181322
  }
@@ -183772,6 +183900,7 @@ async function promotePrimers(args) {
183772
183900
  }
183773
183901
 
183774
183902
  // src/features/magic-context/dreamer/refresh-primers.ts
183903
+ init_language_directive();
183775
183904
  init_read_session_formatting();
183776
183905
  init_shared();
183777
183906
  init_assistant_message_extractor();
@@ -184010,7 +184139,7 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
184010
184139
  query: { directory: args.sessionDirectory },
184011
184140
  body: {
184012
184141
  agent: DREAMER_PRIMER_INVESTIGATOR_AGENT,
184013
- system: PRIMER_INVESTIGATOR_SYSTEM_PROMPT,
184142
+ system: withContentLanguageDirective(PRIMER_INVESTIGATOR_SYSTEM_PROMPT, args.language),
184014
184143
  ...modelBodyField(args.model),
184015
184144
  parts: [{ type: "text", text: prompt, synthetic: true }]
184016
184145
  }
@@ -184246,6 +184375,7 @@ function insertDreamRun(db, run) {
184246
184375
  init_task_registry();
184247
184376
 
184248
184377
  // src/features/magic-context/dreamer/verify.ts
184378
+ init_language_directive();
184249
184379
  init_shared();
184250
184380
  init_assistant_message_extractor();
184251
184381
  init_logger();
@@ -184473,7 +184603,7 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
184473
184603
  query: { directory: args.sessionDirectory },
184474
184604
  body: {
184475
184605
  agent: DREAMER_MEMORY_MAPPER_AGENT,
184476
- system: VERIFY_SYSTEM_PROMPT,
184606
+ system: withContentLanguageDirective(VERIFY_SYSTEM_PROMPT, args.language),
184477
184607
  ...modelBodyField(args.model),
184478
184608
  parts: [{ type: "text", text: prompt, synthetic: true }]
184479
184609
  }
@@ -184753,7 +184883,8 @@ function createDreamTaskExecutor(deps) {
184753
184883
  deadline,
184754
184884
  promotionThreshold: config2.promotionThreshold ?? 3,
184755
184885
  model: config2.model,
184756
- fallbackModels: config2.fallbackModels
184886
+ fallbackModels: config2.fallbackModels,
184887
+ language: config2.language ?? deps.language
184757
184888
  });
184758
184889
  recordRun("completed", null);
184759
184890
  log(`[dreamer] review-user-memories: promoted=${result.promoted} merged=${result.merged} dismissed=${result.dismissed}`);
@@ -184789,7 +184920,8 @@ function createDreamTaskExecutor(deps) {
184789
184920
  deadline,
184790
184921
  forceBroad: config2.task === "verify-broad",
184791
184922
  model: config2.model,
184792
- fallbackModels: config2.fallbackModels
184923
+ fallbackModels: config2.fallbackModels,
184924
+ language: config2.language ?? deps.language
184793
184925
  });
184794
184926
  recordRun("completed", null, {
184795
184927
  memoryChanges: computeMemoryDelta(memoryBefore)
@@ -184841,6 +184973,7 @@ function createDreamTaskExecutor(deps) {
184841
184973
  deadline,
184842
184974
  model: config2.model,
184843
184975
  fallbackModels: config2.fallbackModels,
184976
+ language: config2.language ?? deps.language,
184844
184977
  rawProviderFactory: deps.primerRawProviderFactory
184845
184978
  });
184846
184979
  recordRun("completed", null);
@@ -184958,6 +185091,7 @@ function retrospectiveEventsForSessions(db, sessionIds) {
184958
185091
  try {
184959
185092
  for (const event of getCompartmentEvents(db, sessionId)) {
184960
185093
  if (event.kind !== "causal_incident" && event.kind !== "trajectory_correction") {
185094
+ log(`[dreamer] dropping event: unknown kind="${event.kind}"`);
184961
185095
  continue;
184962
185096
  }
184963
185097
  events.push({
@@ -185082,7 +185216,9 @@ async function runRetrospectiveTask(config2, ctx, helpers) {
185082
185216
  const frictionWindow = renderFrictionWindow(messages, flagged.map((message) => message.ordinal));
185083
185217
  const eventSessionIds = new Set(messages.map((message) => message.sessionId));
185084
185218
  const events = retrospectiveEventsForSessions(db, eventSessionIds);
185085
- const deepenRun = await runChildTurn(RETROSPECTIVE_SYSTEM_PROMPT, buildRetrospectivePrompt({ projectPath: projectIdentity, frictionWindow, events }));
185219
+ const deepenRun = await runChildTurn(withContentLanguageDirective(RETROSPECTIVE_SYSTEM_PROMPT, config2.language ?? deps.language, {
185220
+ retrospective: true
185221
+ }), buildRetrospectivePrompt({ projectPath: projectIdentity, frictionWindow, events }));
185086
185222
  if (leaseLost)
185087
185223
  throw new Error("Dream lease lost during retrospective");
185088
185224
  const sourceSessionId = flagged[0]?.sessionId ?? userMessages[0]?.sessionId ?? "retrospective";
@@ -185168,7 +185304,7 @@ async function runAgenticTask(config2, ctx, helpers) {
185168
185304
  query: { directory: docsDir },
185169
185305
  body: {
185170
185306
  agent: task === "maintain-docs" ? DREAMER_DOCS_AGENT : DREAMER_AGENT,
185171
- system: task === "maintain-docs" ? MAINTAIN_DOCS_SYSTEM_PROMPT : CURATE_SYSTEM_PROMPT,
185307
+ system: task === "maintain-docs" ? MAINTAIN_DOCS_SYSTEM_PROMPT : withContentLanguageDirective(CURATE_SYSTEM_PROMPT, config2.language ?? deps.language),
185172
185308
  ...modelBodyField(config2.model),
185173
185309
  parts: [{ type: "text", text: taskPrompt, synthetic: true }]
185174
185310
  }
@@ -185990,7 +186126,7 @@ async function sweepProject(reg, origin, db, gitCommitEnabled) {
185990
186126
  }
185991
186127
  try {
185992
186128
  await runCompiledSmartNoteSweep(reg, db);
185993
- const runtimeConfigs = buildDreamTaskRuntimeConfigs(dreamerConfig);
186129
+ const runtimeConfigs = buildDreamTaskRuntimeConfigs(dreamerConfig, reg.language);
185994
186130
  const executor = createDreamTaskExecutor({
185995
186131
  client: reg.client,
185996
186132
  sessionDirectory: reg.directory,
@@ -185998,7 +186134,8 @@ async function sweepProject(reg, origin, db, gitCommitEnabled) {
185998
186134
  retrospectiveRawProvider: reg.retrospectiveRawProvider ?? ((db2) => new OpenCodeRetrospectiveRawProvider({ contextDb: db2, openOpenCodeDb })),
185999
186135
  primerRawProviderFactory: reg.primerRawProviderFactory,
186000
186136
  userMemoryCollectionEnabled: userMemoryCollectionEnabled(dreamerConfig),
186001
- ensureProjectRegistered: reg.ensureRegistered
186137
+ ensureProjectRegistered: reg.ensureRegistered,
186138
+ language: reg.language
186002
186139
  });
186003
186140
  const ran = await runDueTasksForProject({
186004
186141
  db,
@@ -187361,7 +187498,8 @@ Provide a prompt to augment with project memory context.`, {});
187361
187498
  projectPath: deps.sidekick.projectPath,
187362
187499
  sessionDirectory: deps.sidekick.sessionDirectory,
187363
187500
  userMessage: prompt,
187364
- config: deps.sidekick.config
187501
+ config: deps.sidekick.config,
187502
+ language: deps.sidekick.language
187365
187503
  });
187366
187504
  let augmentedPrompt;
187367
187505
  if (sidekickResult) {
@@ -187839,6 +187977,7 @@ var pendingPiDecisionBySession = new Map;
187839
187977
  var lastBoundMessageIdBySession = new Map;
187840
187978
  var scheduledWriteTokensBySession = new Map;
187841
187979
  var writerOverrideForTests = null;
187980
+ var retentionOverrideForTests = null;
187842
187981
  function normalizeMaterializeReason(harness, reason, rematerialized) {
187843
187982
  const raw = typeof reason === "string" ? reason.trim() : "";
187844
187983
  if (raw.length > 0) {
@@ -187939,7 +188078,7 @@ function writeTransformDecisionRow(dbPath, row) {
187939
188078
  WHERE session_id = ? AND harness = ?
187940
188079
  ORDER BY ts_ms DESC, rowid DESC
187941
188080
  LIMIT ?
187942
- )`).run(row.sessionId, row.harness, row.sessionId, row.harness, TRANSFORM_DECISIONS_RETENTION);
188081
+ )`).run(row.sessionId, row.harness, row.sessionId, row.harness, retentionOverrideForTests ?? TRANSFORM_DECISIONS_RETENTION);
187943
188082
  } finally {
187944
188083
  closeQuietly(db);
187945
188084
  }
@@ -187967,6 +188106,7 @@ function channel1RefireTokens(workingWindowTokens) {
187967
188106
  var S_GENTLE = 0.2;
187968
188107
  var S_FIRM = 0.4;
187969
188108
  var S_URGENT = 0.65;
188109
+ var CHANNEL1_PRESSURE_FLOOR = 0.8;
187970
188110
  var LEVEL_RANK = { gentle: 1, firm: 2, urgent: 3 };
187971
188111
  var DROP_SENTINELS = ["[dropped", "[truncated"];
187972
188112
  function isDroppedToolOutput(output) {
@@ -188031,7 +188171,8 @@ function computeTailTokenEstimate(messages) {
188031
188171
  };
188032
188172
  }
188033
188173
  function decideChannel1(input) {
188034
- const { undroppedTokens, pressure, workingWindowTokens, hasRecentReduce } = input;
188174
+ const { undroppedTokens, workingWindowTokens, hasRecentReduce } = input;
188175
+ const pressure = Math.min(1, Math.max(0, input.pressure));
188035
188176
  const resetCycle = hasRecentReduce || undroppedTokens < input.lastNudgeUndropped;
188036
188177
  const lastNudge = resetCycle ? 0 : input.lastNudgeUndropped;
188037
188178
  const lastLevel = resetCycle ? "" : input.lastNudgeLevel;
@@ -188046,8 +188187,10 @@ function decideChannel1(input) {
188046
188187
  return quiet();
188047
188188
  if (undroppedTokens < CHANNEL1_FLOOR_TOKENS)
188048
188189
  return quiet();
188049
- const budget = workingWindowTokens > 0 ? workingWindowTokens : undroppedTokens || 1;
188050
- const severity = undroppedTokens / budget * pressure;
188190
+ if (pressure < CHANNEL1_PRESSURE_FLOOR)
188191
+ return quiet();
188192
+ const denom = Math.max(input.estimatedInputTokens, 1);
188193
+ const severity = Math.min(1, undroppedTokens / denom);
188051
188194
  if (severity < S_GENTLE)
188052
188195
  return quiet();
188053
188196
  let level;
@@ -188348,6 +188491,7 @@ init_session_project_storage();
188348
188491
  init_storage_meta_persisted();
188349
188492
  await init_storage();
188350
188493
  init_logger();
188494
+ init_models_dev_cache();
188351
188495
 
188352
188496
  // src/hooks/magic-context/boundary-execution.ts
188353
188497
  var FORCE_MATERIALIZE_PERCENTAGE2 = 85;
@@ -192487,6 +192631,18 @@ function findLastAssistantModel2(messages) {
192487
192631
  }
192488
192632
  return null;
192489
192633
  }
192634
+ function findNewestUserModel(messages) {
192635
+ for (let i = messages.length - 1;i >= 0; i--) {
192636
+ const info = messages[i].info;
192637
+ if (info.role !== "user")
192638
+ continue;
192639
+ if (info.model?.providerID && info.model.modelID) {
192640
+ return { providerID: info.model.providerID, modelID: info.model.modelID };
192641
+ }
192642
+ return null;
192643
+ }
192644
+ return null;
192645
+ }
192490
192646
  function createTransform(deps) {
192491
192647
  const loadedSessions = new Set;
192492
192648
  const lastEmergencyNotificationCount = new Map;
@@ -192547,15 +192703,15 @@ function createTransform(deps) {
192547
192703
  const canRunCompartments = fullFeatureMode && historianRunnable && deps.client !== undefined && compartmentDirectory.length > 0;
192548
192704
  const fallbackModelId = deps.getFallbackModelId?.(sessionId);
192549
192705
  const tModelDetect = performance.now();
192706
+ const persistedUsageBeforeResets = loadPersistedUsage(db, sessionId);
192550
192707
  if (deps.liveModelBySession) {
192551
- const lastAssistantModel = findLastAssistantModel2(messages);
192552
- if (lastAssistantModel) {
192553
- const knownModel = deps.liveModelBySession.get(sessionId);
192554
- if (!knownModel) {
192555
- deps.liveModelBySession.set(sessionId, lastAssistantModel);
192556
- } else if (knownModel.providerID !== lastAssistantModel.providerID || knownModel.modelID !== lastAssistantModel.modelID) {
192557
- sessionLog(sessionId, `transform: model change detected (${knownModel.providerID}/${knownModel.modelID} -> ${lastAssistantModel.providerID}/${lastAssistantModel.modelID}), clearing stale context state`);
192558
- deps.liveModelBySession.set(sessionId, lastAssistantModel);
192708
+ const currentOutgoingModel = findNewestUserModel(messages) ?? deps.liveModelBySession.get(sessionId) ?? findLastAssistantModel2(messages);
192709
+ if (currentOutgoingModel) {
192710
+ deps.liveModelBySession.set(sessionId, currentOutgoingModel);
192711
+ const outgoingModelKey = resolveModelKey(currentOutgoingModel.providerID, currentOutgoingModel.modelID);
192712
+ const lastUsageModelKey = persistedUsageBeforeResets?.lastObservedModelKey ?? null;
192713
+ if (lastUsageModelKey != null && outgoingModelKey != null && lastUsageModelKey !== outgoingModelKey) {
192714
+ sessionLog(sessionId, `transform: model change since last usage (${lastUsageModelKey} -> ${outgoingModelKey}), clearing stale per-model state`);
192559
192715
  updateSessionMeta(db, sessionId, {
192560
192716
  lastContextPercentage: 0,
192561
192717
  lastInputTokens: 0,
@@ -192585,7 +192741,6 @@ function createTransform(deps) {
192585
192741
  const tFirstPass = performance.now();
192586
192742
  const isFirstTransformPassForSession = !loadedSessions.has(sessionId);
192587
192743
  loadedSessions.add(sessionId);
192588
- const persistedUsageBeforeFirstPassReset = loadPersistedUsage(db, sessionId);
192589
192744
  const historianFailureState = getHistorianFailureState(db, sessionId);
192590
192745
  if (isFirstTransformPassForSession && sessionMeta) {
192591
192746
  const persistedPct = sessionMeta.lastContextPercentage ?? 0;
@@ -192604,6 +192759,17 @@ function createTransform(deps) {
192604
192759
  let emergencyRecoveryArmed = false;
192605
192760
  if (fullFeatureMode) {
192606
192761
  try {
192762
+ const armModel = deps.liveModelBySession?.get(sessionId);
192763
+ const armModelKey = deps.getModelKey?.(sessionId);
192764
+ const armSnapshot = persistedUsageBeforeResets;
192765
+ const lastMeasuredInput = armSnapshot?.usage.inputTokens ?? sessionMeta?.lastInputTokens ?? 0;
192766
+ const lastMeasuredModelKey = armSnapshot?.lastObservedModelKey ?? null;
192767
+ const armCatalogLimit = armModel ? getSdkContextLimit(armModel.providerID, armModel.modelID) : undefined;
192768
+ if (!sessionMeta?.isSubagent && armModel && typeof armCatalogLimit === "number" && armCatalogLimit > 0 && lastMeasuredInput > armCatalogLimit && lastMeasuredModelKey != null && armModelKey != null && lastMeasuredModelKey !== armModelKey && !getOverflowState(db, sessionId).needsEmergencyRecovery) {
192769
+ sessionLog(sessionId, `transform: last input ${lastMeasuredInput} (model ${lastMeasuredModelKey}) exceeds new model ${armModelKey} catalog limit ${armCatalogLimit}; arming overflow recovery proactively for the shrinking switch`);
192770
+ recordOverflowDetected(db, sessionId, undefined, armModelKey);
192771
+ resetProtectedTailNoEligibleHead(db, sessionId);
192772
+ }
192607
192773
  const overflowState = getOverflowState(db, sessionId);
192608
192774
  emergencyRecoveryArmed = overflowState.needsEmergencyRecovery;
192609
192775
  if (contextUsageEarly.percentage < 80 && !overflowState.needsEmergencyRecovery) {
@@ -192640,7 +192806,7 @@ function createTransform(deps) {
192640
192806
  sessionID: sessionId
192641
192807
  }) : undefined;
192642
192808
  const currentModelKeyForBoundary = deps.getModelKey?.(sessionId);
192643
- const persistedUsageFreshForBoundary = persistedUsageBeforeFirstPassReset && Date.now() - persistedUsageBeforeFirstPassReset.updatedAt <= 10 * 60 * 1000 && (persistedUsageBeforeFirstPassReset.lastObservedModelKey === null || currentModelKeyForBoundary === undefined || persistedUsageBeforeFirstPassReset.lastObservedModelKey === currentModelKeyForBoundary) && (resolvedContextLimit === undefined || persistedUsageBeforeFirstPassReset.lastUsageContextLimit === 0 || persistedUsageBeforeFirstPassReset.lastUsageContextLimit === resolvedContextLimit) ? persistedUsageBeforeFirstPassReset.usage : null;
192809
+ const persistedUsageFreshForBoundary = persistedUsageBeforeResets && Date.now() - persistedUsageBeforeResets.updatedAt <= 10 * 60 * 1000 && (persistedUsageBeforeResets.lastObservedModelKey === null || currentModelKeyForBoundary === undefined || persistedUsageBeforeResets.lastObservedModelKey === currentModelKeyForBoundary) && (resolvedContextLimit === undefined || persistedUsageBeforeResets.lastUsageContextLimit === 0 || persistedUsageBeforeResets.lastUsageContextLimit === resolvedContextLimit) ? persistedUsageBeforeResets.usage : null;
192644
192810
  const boundaryUsageForProtectedTail = persistedUsageFreshForBoundary ?? contextUsageEarly;
192645
192811
  const boundaryUsageSource = persistedUsageFreshForBoundary ? "persisted" : "live";
192646
192812
  const historyBudgetTokens = resolveHistoryBudgetTokens(deps.historyBudgetPercentage, contextUsageEarly, deps.executeThresholdPercentage, deps.getModelKey?.(sessionId), deps.executeThresholdTokens, resolvedContextLimit);
@@ -193901,6 +194067,7 @@ function maybeInjectChannel1Nudge(args, sessionId, tool, output) {
193901
194067
  const decision = decideChannel1({
193902
194068
  undroppedTokens,
193903
194069
  pressure,
194070
+ estimatedInputTokens: state.lastInputTokens + state.turnToolTokens,
193904
194071
  workingWindowTokens,
193905
194072
  lastNudgeUndropped: getLastNudgeUndropped(args.db, sessionId),
193906
194073
  lastNudgeLevel: getLastNudgeLevel(args.db, sessionId),
@@ -193966,6 +194133,7 @@ init_send_session_notification();
193966
194133
  import { createHash as createHash13 } from "node:crypto";
193967
194134
 
193968
194135
  // src/agents/magic-context-prompt.ts
194136
+ init_language_directive();
193969
194137
  var LONG_TERM_PARTNER_FRAME = `### You are the user's long-term partner on this project — not a one-off hire
193970
194138
 
193971
194139
  Most AI sessions are disposable: one session per task, discarded when it's done — like hiring a developer for a single bug fix and letting them go the moment they finish. Magic Context changes this completely. This session is a durable working relationship: you carry the full history and accumulated knowledge of this project, and you continue across many tasks, bugs, and features — with memory that persists across restarts. This session may continue for weeks, months, or even years.
@@ -194044,7 +194212,7 @@ Drop silently — do not narrate it. NEVER drop large ranges blindly (e.g., "1-5
194044
194212
  Older tool calls may show \`[dropped §N§]\` sentinels; that is normal context management, not a pattern to copy. ALWAYS make fresh real tool calls when you need data again; never fabricate or inline tool output.`;
194045
194213
  var CAVEMAN_COMPRESSION_WARNING = `
194046
194214
  **BEWARE**: History compression is on; older user AND assistant text — including your own earlier responses — has been deterministically rewritten in a terse caveman style (dropped articles, missing auxiliaries, \`//\` instead of connectives like \`because\`). This is automatic context compression that runs after the fact, not your actual prior wording or the user's. **DO NOT mimic this style in new turns.** Write fresh responses in normal prose. If you notice your output drifting into caveman cadence, that drift is in-context-learning bleeding from the compressed history — consciously revert to full sentences.`;
194047
- function buildMagicContextSection(_agent, protectedTags, ctxReduceEnabled = true, dreamerEnabled = false, temporalAwarenessEnabled = false, cavemanTextCompressionEnabled = false, subagentMode = false) {
194215
+ function buildMagicContextSection(_agent, protectedTags, ctxReduceEnabled = true, dreamerEnabled = false, temporalAwarenessEnabled = false, cavemanTextCompressionEnabled = false, subagentMode = false, language) {
194048
194216
  if (subagentMode) {
194049
194217
  return `## Magic Context
194050
194218
 
@@ -194056,13 +194224,17 @@ The dreamer evaluates smart note conditions during nightly runs and surfaces the
194056
194224
  Example: \`ctx_note(action="write", content="Implement X because Y", surface_condition="When PR #42 is merged in this repo")\`` : "";
194057
194225
  const temporalGuidance = temporalAwarenessEnabled ? TEMPORAL_AWARENESS_GUIDANCE : "";
194058
194226
  const cavemanWarning = cavemanTextCompressionEnabled && !ctxReduceEnabled ? CAVEMAN_COMPRESSION_WARNING : "";
194227
+ const languageDirective = buildPrimaryLanguageDirective(language);
194228
+ const languageGuidance = languageDirective ? `
194229
+
194230
+ ${languageDirective}` : "";
194059
194231
  if (!ctxReduceEnabled) {
194060
194232
  return `## Magic Context
194061
194233
 
194062
194234
  ${LONG_TERM_PARTNER_FRAME}
194063
194235
  ${PARTNER_FRAME_CLOSER_NO_REDUCE}
194064
194236
 
194065
- ${BASE_INTRO_NO_REDUCE()}${smartNoteGuidance}${temporalGuidance}${cavemanWarning}`;
194237
+ ${BASE_INTRO_NO_REDUCE()}${smartNoteGuidance}${temporalGuidance}${cavemanWarning}${languageGuidance}`;
194066
194238
  }
194067
194239
  return `## Magic Context
194068
194240
 
@@ -194072,7 +194244,7 @@ ${PARTNER_FRAME_CLOSER_REDUCE}
194072
194244
  ${BASE_INTRO(protectedTags)}${smartNoteGuidance}${temporalGuidance}
194073
194245
  ${GENERIC_SECTION}
194074
194246
 
194075
- Prefer many small targeted operations over one large blanket operation, and keep the working set tidy as routine maintenance.`;
194247
+ Prefer many small targeted operations over one large blanket operation, and keep the working set tidy as routine maintenance.${languageGuidance}`;
194076
194248
  }
194077
194249
 
194078
194250
  // src/hooks/magic-context/system-prompt-hash.ts
@@ -194130,7 +194302,7 @@ function createSystemPromptHashHandler(deps) {
194130
194302
  const fullPrompt = output.system.join(`
194131
194303
  `);
194132
194304
  if (fullPrompt.length > 0 && !fullPrompt.includes(MAGIC_CONTEXT_MARKER) && !skipGuidanceForDisabledSubagent) {
194133
- const guidance = buildMagicContextSection(null, deps.protectedTags, effectiveCtxReduceEnabled, deps.dreamerEnabled, deps.experimentalTemporalAwareness, deps.experimentalCavemanTextCompression, subagentReduceMode);
194305
+ const guidance = buildMagicContextSection(null, deps.protectedTags, effectiveCtxReduceEnabled, deps.dreamerEnabled, deps.experimentalTemporalAwareness, deps.experimentalCavemanTextCompression, subagentReduceMode, deps.language);
194134
194306
  output.system.push(guidance);
194135
194307
  sessionLog(sessionId, `injected generic guidance into system prompt (ctxReduce=${effectiveCtxReduceEnabled}, subagent=${isSubagentSession}, subagentReduceMode=${subagentReduceMode})`);
194136
194308
  }
@@ -194418,6 +194590,7 @@ function createMagicContextHook(deps) {
194418
194590
  memoryEnabled: deps.config.memory?.enabled ?? true,
194419
194591
  autoPromote: deps.config.memory?.auto_promote ?? true,
194420
194592
  fallbackModels: historianFallbackModels,
194593
+ language: deps.config.language,
194421
194594
  fallbackModelId: (() => {
194422
194595
  const model = resolveLiveModel(sessionId);
194423
194596
  return model ? `${model.providerID}/${model.modelID}` : undefined;
@@ -194456,7 +194629,7 @@ function createMagicContextHook(deps) {
194456
194629
  signal,
194457
194630
  onProgress: ({ embedded, total }) => {
194458
194631
  const cur = recompProgressBySession.get(sessionId);
194459
- if (!cur || cur.phase !== "recomp")
194632
+ if (cur?.phase !== "recomp")
194460
194633
  return;
194461
194634
  recompProgressBySession.set(sessionId, {
194462
194635
  ...cur,
@@ -194659,7 +194832,7 @@ function createMagicContextHook(deps) {
194659
194832
  return;
194660
194833
  }
194661
194834
  lastScheduleCheckMs = now;
194662
- const runtimeConfigs = buildDreamTaskRuntimeConfigs(dreaming);
194835
+ const runtimeConfigs = buildDreamTaskRuntimeConfigs(dreaming, deps.config.language);
194663
194836
  const executor = createDreamTaskExecutor({
194664
194837
  client: deps.client,
194665
194838
  sessionDirectory: deps.directory,
@@ -194668,7 +194841,8 @@ function createMagicContextHook(deps) {
194668
194841
  contextDb: providerDb,
194669
194842
  openOpenCodeDb
194670
194843
  }),
194671
- userMemoryCollectionEnabled: userMemoryCollectionEnabled(dreaming)
194844
+ userMemoryCollectionEnabled: userMemoryCollectionEnabled(dreaming),
194845
+ language: deps.config.language
194672
194846
  });
194673
194847
  runDueTasksForProject({
194674
194848
  db,
@@ -194717,7 +194891,8 @@ function createMagicContextHook(deps) {
194717
194891
  config: sidekickConfig,
194718
194892
  projectPath,
194719
194893
  sessionDirectory: deps.directory,
194720
- client: deps.client
194894
+ client: deps.client,
194895
+ language: deps.config.language
194721
194896
  } : undefined,
194722
194897
  dreamer: dreamerConfig ? {
194723
194898
  config: dreamerConfig,
@@ -194725,7 +194900,7 @@ function createMagicContextHook(deps) {
194725
194900
  runManual: (task) => runManualDream({
194726
194901
  db,
194727
194902
  projectIdentity: projectPath,
194728
- tasks: buildDreamTaskRuntimeConfigs(dreamerConfig),
194903
+ tasks: buildDreamTaskRuntimeConfigs(dreamerConfig, deps.config.language),
194729
194904
  executor: createDreamTaskExecutor({
194730
194905
  client: deps.client,
194731
194906
  sessionDirectory: deps.directory,
@@ -194734,7 +194909,8 @@ function createMagicContextHook(deps) {
194734
194909
  contextDb: providerDb,
194735
194910
  openOpenCodeDb
194736
194911
  }),
194737
- userMemoryCollectionEnabled: userMemoryCollectionEnabled(dreamerConfig)
194912
+ userMemoryCollectionEnabled: userMemoryCollectionEnabled(dreamerConfig),
194913
+ language: deps.config.language
194738
194914
  }),
194739
194915
  task
194740
194916
  })
@@ -194745,6 +194921,7 @@ function createMagicContextHook(deps) {
194745
194921
  protectedTags: deps.config.protected_tags,
194746
194922
  ctxReduceEnabled,
194747
194923
  dreamerEnabled: dreamerRunnable,
194924
+ language: deps.config.language,
194748
194925
  injectDocs: deps.config.dreamer?.inject_docs !== false,
194749
194926
  directory: deps.directory,
194750
194927
  historyRefreshSessions,
@@ -197472,6 +197649,7 @@ var server2 = async (ctx) => {
197472
197649
  projectIdentity: resolveProjectIdentity(ctx.directory),
197473
197650
  client: ctx.client,
197474
197651
  dreamerConfig: dreamerRunnable ? pluginConfig.dreamer : undefined,
197652
+ language: pluginConfig.language,
197475
197653
  embeddingConfig: pluginConfig.embedding,
197476
197654
  memoryEnabled: pluginConfig.memory?.enabled === true,
197477
197655
  gitCommitIndexing: pluginConfig.memory.git_commit_indexing?.enabled ? {
@@ -197623,9 +197801,9 @@ var server2 = async (ctx) => {
197623
197801
  const registrations = buildHiddenAgentRegistrations({
197624
197802
  dreamerPrompt: DREAMER_SYSTEM_PROMPT,
197625
197803
  smartNoteCompilerPrompt: SMART_NOTE_COMPILER_SYSTEM_PROMPT,
197626
- historianPrompt: COMPARTMENT_AGENT_SYSTEM_PROMPT,
197627
- historianRecompPrompt: COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT,
197628
- historianEditorPrompt: HISTORIAN_EDITOR_SYSTEM_PROMPT,
197804
+ historianPrompt: withContentLanguageDirective(COMPARTMENT_AGENT_SYSTEM_PROMPT, pluginConfig.language, { preserveUserQuotes: true }),
197805
+ historianRecompPrompt: withContentLanguageDirective(COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT, pluginConfig.language, { preserveUserQuotes: true }),
197806
+ historianEditorPrompt: withContentLanguageDirective(HISTORIAN_EDITOR_SYSTEM_PROMPT, pluginConfig.language, { preserveUserQuotes: true }),
197629
197807
  sidekickPrompt: SIDEKICK_SYSTEM_PROMPT,
197630
197808
  dreamerOverrides: dreamerAgentOverrides,
197631
197809
  historianOverrides: historianAgentOverrides,