@wolfx/pi-magic-context 0.27.2-patch.1 → 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.
package/dist/index.js CHANGED
@@ -15568,7 +15568,7 @@ function compareTaskOrder(a, b) {
15568
15568
  }
15569
15569
 
15570
15570
  // ../plugin/src/features/magic-context/dreamer/task-config.ts
15571
- function buildDreamTaskRuntimeConfigs(dreamer) {
15571
+ function buildDreamTaskRuntimeConfigs(dreamer, language) {
15572
15572
  const tasks = dreamer.tasks ?? {};
15573
15573
  return CANONICAL_DREAM_TASKS.map((task) => {
15574
15574
  const t = tasks[task] ?? {
@@ -15584,6 +15584,7 @@ function buildDreamTaskRuntimeConfigs(dreamer) {
15584
15584
  model,
15585
15585
  fallbackModels,
15586
15586
  thinkingLevel,
15587
+ language,
15587
15588
  timeoutMinutes: t.timeout_minutes ?? 20,
15588
15589
  promotionThreshold: t.promotion_threshold
15589
15590
  };
@@ -150534,7 +150535,7 @@ function enforceSchemaFence(db, dbPath, latestSupportedVersion) {
150534
150535
  return true;
150535
150536
  }
150536
150537
  lastSchemaFenceRejection = { persistedVersion, supportedVersion: latestSupportedVersion };
150537
- 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.`);
150538
+ 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.`);
150538
150539
  return false;
150539
150540
  }
150540
150541
  var sqlitePragmaConfig = {
@@ -155056,6 +155057,9 @@ function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
155056
155057
  nullCount: row?.null_count ?? 0
155057
155058
  };
155058
155059
  }
155060
+ var RECLAIM_HINT_EXCLUDED_TOOLS = ["todowrite"];
155061
+ var RECLAIM_HINT_MIN_TOKENS = 250;
155062
+ var RECLAIM_HINT_EXCLUDED_LIST = RECLAIM_HINT_EXCLUDED_TOOLS.map((name2) => `'${name2.replace(/'/g, "''")}'`).join(", ");
155059
155063
  function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, limit = 4) {
155060
155064
  if (limit <= 0)
155061
155065
  return [];
@@ -155065,10 +155069,18 @@ function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, li
155065
155069
  WHERE session_id = ? AND status = 'active'
155066
155070
  ORDER BY tag_number DESC LIMIT 1 OFFSET ?
155067
155071
  )` : "";
155068
- const params = protectedTags > 0 ? [sessionId, sessionId, protectedTags - 1, boundedLimit] : [sessionId, boundedLimit];
155072
+ const excludeStateTools = RECLAIM_HINT_EXCLUDED_LIST ? `AND (tool_name IS NULL OR tool_name NOT IN (${RECLAIM_HINT_EXCLUDED_LIST}))` : "";
155073
+ const valueFloor = `AND (
155074
+ (token_count IS NULL AND input_token_count IS NULL)
155075
+ OR (COALESCE(token_count, 0) + COALESCE(input_token_count, 0)) >= ?
155076
+ )`;
155077
+ const params = protectedTags > 0 ? [sessionId, RECLAIM_HINT_MIN_TOKENS, sessionId, protectedTags - 1, boundedLimit] : [sessionId, RECLAIM_HINT_MIN_TOKENS, boundedLimit];
155069
155078
  const rows = db.prepare(`SELECT tag_number, tool_name
155070
155079
  FROM tags
155071
- WHERE session_id = ? AND status = 'active' AND type = 'tool' ${whereProtected}
155080
+ WHERE session_id = ? AND status = 'active' AND type = 'tool'
155081
+ ${excludeStateTools}
155082
+ ${valueFloor}
155083
+ ${whereProtected}
155072
155084
  ORDER BY tag_number ASC, id ASC
155073
155085
  LIMIT ?`).all(...params);
155074
155086
  return rows.filter((row) => typeof row.tag_number === "number").map((row) => ({
@@ -156329,13 +156341,10 @@ function compareSemverCore(a, b) {
156329
156341
  const [b0, b1, b2] = core(b);
156330
156342
  return a0 - b0 || a1 - b1 || a2 - b2;
156331
156343
  }
156332
- var ANNOUNCEMENT_VERSION = "0.27.0";
156344
+ var ANNOUNCEMENT_VERSION = "0.28.0";
156333
156345
  var ANNOUNCEMENT_FEATURES = [
156334
- "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.",
156335
- "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.",
156336
- "New Primers: durable answers to the questions that keep coming up about your project, kept current by the dreamer investigating the actual code.",
156337
- "Embedding storage no longer wipes your vectors when you change model or endpoint. Different models now coexist, so switching providers keeps your existing vectors.",
156338
- "Config moved to a shared CortexKit location (~/.config/cortexkit/ and <project>/.cortexkit/). This happens automatically on first run; your old file is preserved."
156346
+ `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.`,
156347
+ "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."
156339
156348
  ];
156340
156349
  var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU";
156341
156350
  var STATE_FILENAME = "last_announced_version";
@@ -156397,6 +156406,95 @@ function shouldKeepSubagents() {
156397
156406
  // src/index.ts
156398
156407
  init_logger();
156399
156408
 
156409
+ // ../plugin/src/agents/language-directive.ts
156410
+ var ENGLISH_LANGUAGE_NAMES = new Intl.DisplayNames(["en"], {
156411
+ type: "language",
156412
+ fallback: "none"
156413
+ });
156414
+ function resolveLanguageName(language) {
156415
+ const code = typeof language === "string" ? language.trim().toLowerCase() : "";
156416
+ if (!/^[a-z]{2}$/.test(code))
156417
+ return "";
156418
+ let english;
156419
+ try {
156420
+ english = ENGLISH_LANGUAGE_NAMES.of(code) ?? undefined;
156421
+ } catch {
156422
+ return "";
156423
+ }
156424
+ if (!english)
156425
+ return "";
156426
+ let endonym;
156427
+ try {
156428
+ endonym = new Intl.DisplayNames([code], { type: "language", fallback: "none" }).of(code) ?? undefined;
156429
+ } catch {
156430
+ endonym = undefined;
156431
+ }
156432
+ return endonym && endonym !== english ? `${english} (${endonym})` : english;
156433
+ }
156434
+ function isValidLanguageCode(language) {
156435
+ return resolveLanguageName(language) !== "";
156436
+ }
156437
+ function buildContentLanguageDirective(language, options = {}) {
156438
+ const target = resolveLanguageName(language);
156439
+ if (!target)
156440
+ return "";
156441
+ const lines = [
156442
+ "## Output language",
156443
+ "",
156444
+ `Write human-readable prose you author in: ${target}.`,
156445
+ "",
156446
+ "Do not translate or rename structural tokens. Copy required output schemas exactly:",
156447
+ "- 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.",
156448
+ "- 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.",
156449
+ "- Localize only free-text prose values/content: summaries, memory text, explanations, titles, observations, and answers — unless the prompt says to preserve original wording.",
156450
+ "",
156451
+ "These literal values must remain English when used:",
156452
+ "PROJECT_RULES, ARCHITECTURE, CONSTRAINTS, CONFIG_VALUES, NAMING;",
156453
+ "causal_incident, trajectory_correction;",
156454
+ "feature, design, docs, release, investigation, bug, refactor, infra;",
156455
+ "memory, observation; true, false; No relevant memories found.",
156456
+ "",
156457
+ "Preserve the required output shape. Do not add commentary outside the requested XML/JSON/tool output."
156458
+ ];
156459
+ if (options.preserveUserQuotes) {
156460
+ lines.push("", `Preserve U: lines and directly quoted user text in their original source language; write the surrounding summary prose in ${target}.`);
156461
+ }
156462
+ if (options.retrospective) {
156463
+ lines.push("", `Write the lesson text in ${target}; paraphrase source text and never quote the user.`);
156464
+ }
156465
+ return lines.join(`
156466
+ `);
156467
+ }
156468
+ function withContentLanguageDirective(systemPrompt, language, options = {}) {
156469
+ const directive = buildContentLanguageDirective(language, options);
156470
+ return directive ? `${systemPrompt}
156471
+
156472
+ ${directive}` : systemPrompt;
156473
+ }
156474
+ function buildMigrationLanguageDirective(language) {
156475
+ const target = resolveLanguageName(language);
156476
+ if (!target)
156477
+ return "";
156478
+ return [
156479
+ "## Output language",
156480
+ "",
156481
+ "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."
156482
+ ].join(`
156483
+ `);
156484
+ }
156485
+ function withMigrationLanguageDirective(systemPrompt, language) {
156486
+ const directive = buildMigrationLanguageDirective(language);
156487
+ return directive ? `${systemPrompt}
156488
+
156489
+ ${directive}` : systemPrompt;
156490
+ }
156491
+ function buildPrimaryLanguageDirective(language) {
156492
+ const target = resolveLanguageName(language);
156493
+ if (!target)
156494
+ return "";
156495
+ 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.`;
156496
+ }
156497
+
156400
156498
  // ../plugin/src/features/magic-context/sidekick/core.ts
156401
156499
  var SIDEKICK_SYSTEM_PROMPT = `You are Sidekick, a focused memory-retrieval subagent for an AI coding assistant.
156402
156500
 
@@ -157117,7 +157215,7 @@ function registerCtxAugCommand(pi, config) {
157117
157215
  sessionLog(sessionLabel, "/ctx-aug: project identity", projectIdentity);
157118
157216
  const result = await runner.run({
157119
157217
  agent: "sidekick",
157120
- systemPrompt: config.systemPrompt ?? SIDEKICK_SYSTEM_PROMPT,
157218
+ systemPrompt: withContentLanguageDirective(config.systemPrompt ?? SIDEKICK_SYSTEM_PROMPT, config.language),
157121
157219
  userMessage: prompt,
157122
157220
  model: config.model,
157123
157221
  fallbackModels: config.fallbackModels,
@@ -161583,7 +161681,7 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
161583
161681
  query: { directory: args.sessionDirectory },
161584
161682
  body: {
161585
161683
  agent: DREAMER_REVIEWER_AGENT,
161586
- system: REVIEW_USER_MEMORIES_SYSTEM_PROMPT,
161684
+ system: withContentLanguageDirective(REVIEW_USER_MEMORIES_SYSTEM_PROMPT, args.language),
161587
161685
  ...modelBodyField(args.model),
161588
161686
  parts: [{ type: "text", text: prompt, synthetic: true }]
161589
161687
  }
@@ -164537,7 +164635,7 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
164537
164635
  query: { directory: args.sessionDirectory },
164538
164636
  body: {
164539
164637
  agent: DREAMER_PRIMER_INVESTIGATOR_AGENT,
164540
- system: PRIMER_INVESTIGATOR_SYSTEM_PROMPT,
164638
+ system: withContentLanguageDirective(PRIMER_INVESTIGATOR_SYSTEM_PROMPT, args.language),
164541
164639
  ...modelBodyField(args.model),
164542
164640
  parts: [{ type: "text", text: prompt, synthetic: true }]
164543
164641
  }
@@ -165318,7 +165416,7 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
165318
165416
  query: { directory: args.sessionDirectory },
165319
165417
  body: {
165320
165418
  agent: DREAMER_MEMORY_MAPPER_AGENT,
165321
- system: VERIFY_SYSTEM_PROMPT,
165419
+ system: withContentLanguageDirective(VERIFY_SYSTEM_PROMPT, args.language),
165322
165420
  ...modelBodyField(args.model),
165323
165421
  parts: [{ type: "text", text: prompt, synthetic: true }]
165324
165422
  }
@@ -165598,7 +165696,8 @@ function createDreamTaskExecutor(deps) {
165598
165696
  deadline,
165599
165697
  promotionThreshold: config.promotionThreshold ?? 3,
165600
165698
  model: config.model,
165601
- fallbackModels: config.fallbackModels
165699
+ fallbackModels: config.fallbackModels,
165700
+ language: config.language ?? deps.language
165602
165701
  });
165603
165702
  recordRun("completed", null);
165604
165703
  log(`[dreamer] review-user-memories: promoted=${result.promoted} merged=${result.merged} dismissed=${result.dismissed}`);
@@ -165634,7 +165733,8 @@ function createDreamTaskExecutor(deps) {
165634
165733
  deadline,
165635
165734
  forceBroad: config.task === "verify-broad",
165636
165735
  model: config.model,
165637
- fallbackModels: config.fallbackModels
165736
+ fallbackModels: config.fallbackModels,
165737
+ language: config.language ?? deps.language
165638
165738
  });
165639
165739
  recordRun("completed", null, {
165640
165740
  memoryChanges: computeMemoryDelta(memoryBefore)
@@ -165686,6 +165786,7 @@ function createDreamTaskExecutor(deps) {
165686
165786
  deadline,
165687
165787
  model: config.model,
165688
165788
  fallbackModels: config.fallbackModels,
165789
+ language: config.language ?? deps.language,
165689
165790
  rawProviderFactory: deps.primerRawProviderFactory
165690
165791
  });
165691
165792
  recordRun("completed", null);
@@ -165803,6 +165904,7 @@ function retrospectiveEventsForSessions(db, sessionIds) {
165803
165904
  try {
165804
165905
  for (const event of getCompartmentEvents(db, sessionId)) {
165805
165906
  if (event.kind !== "causal_incident" && event.kind !== "trajectory_correction") {
165907
+ log(`[dreamer] dropping event: unknown kind="${event.kind}"`);
165806
165908
  continue;
165807
165909
  }
165808
165910
  events.push({
@@ -165927,7 +166029,9 @@ async function runRetrospectiveTask(config, ctx, helpers) {
165927
166029
  const frictionWindow = renderFrictionWindow(messages, flagged.map((message) => message.ordinal));
165928
166030
  const eventSessionIds = new Set(messages.map((message) => message.sessionId));
165929
166031
  const events = retrospectiveEventsForSessions(db, eventSessionIds);
165930
- const deepenRun = await runChildTurn(RETROSPECTIVE_SYSTEM_PROMPT, buildRetrospectivePrompt({ projectPath: projectIdentity, frictionWindow, events }));
166032
+ const deepenRun = await runChildTurn(withContentLanguageDirective(RETROSPECTIVE_SYSTEM_PROMPT, config.language ?? deps.language, {
166033
+ retrospective: true
166034
+ }), buildRetrospectivePrompt({ projectPath: projectIdentity, frictionWindow, events }));
165931
166035
  if (leaseLost)
165932
166036
  throw new Error("Dream lease lost during retrospective");
165933
166037
  const sourceSessionId = flagged[0]?.sessionId ?? userMessages[0]?.sessionId ?? "retrospective";
@@ -166013,7 +166117,7 @@ async function runAgenticTask(config, ctx, helpers) {
166013
166117
  query: { directory: docsDir },
166014
166118
  body: {
166015
166119
  agent: task === "maintain-docs" ? DREAMER_DOCS_AGENT : DREAMER_AGENT,
166016
- system: task === "maintain-docs" ? MAINTAIN_DOCS_SYSTEM_PROMPT : CURATE_SYSTEM_PROMPT,
166120
+ system: task === "maintain-docs" ? MAINTAIN_DOCS_SYSTEM_PROMPT : withContentLanguageDirective(CURATE_SYSTEM_PROMPT, config.language ?? deps.language),
166017
166121
  ...modelBodyField(config.model),
166018
166122
  parts: [{ type: "text", text: taskPrompt, synthetic: true }]
166019
166123
  }
@@ -166895,7 +166999,7 @@ async function sweepProject(reg, origin, db, gitCommitEnabled) {
166895
166999
  }
166896
167000
  try {
166897
167001
  await runCompiledSmartNoteSweep(reg, db);
166898
- const runtimeConfigs = buildDreamTaskRuntimeConfigs(dreamerConfig);
167002
+ const runtimeConfigs = buildDreamTaskRuntimeConfigs(dreamerConfig, reg.language);
166899
167003
  const executor = createDreamTaskExecutor({
166900
167004
  client: reg.client,
166901
167005
  sessionDirectory: reg.directory,
@@ -166903,7 +167007,8 @@ async function sweepProject(reg, origin, db, gitCommitEnabled) {
166903
167007
  retrospectiveRawProvider: reg.retrospectiveRawProvider ?? ((db2) => new OpenCodeRetrospectiveRawProvider({ contextDb: db2, openOpenCodeDb })),
166904
167008
  primerRawProviderFactory: reg.primerRawProviderFactory,
166905
167009
  userMemoryCollectionEnabled: userMemoryCollectionEnabled(dreamerConfig),
166906
- ensureProjectRegistered: reg.ensureRegistered
167010
+ ensureProjectRegistered: reg.ensureRegistered,
167011
+ language: reg.language
166907
167012
  });
166908
167013
  const ran = await runDueTasksForProject({
166909
167014
  db,
@@ -167460,6 +167565,10 @@ function stripUnsafeProjectConfigFields(projectRaw) {
167460
167565
  delete projectRaw.auto_update;
167461
167566
  warnings.push("Ignoring auto_update from project config (security: this setting only honors user-level config).");
167462
167567
  }
167568
+ if ("language" in projectRaw) {
167569
+ delete projectRaw.language;
167570
+ warnings.push("Ignoring language from project config (security: output language is a user-level setting).");
167571
+ }
167463
167572
  if ("sqlite" in projectRaw) {
167464
167573
  delete projectRaw.sqlite;
167465
167574
  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).");
@@ -181957,6 +182066,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
181957
182066
  });
181958
182067
  var MagicContextConfigSchema = exports_external.object({
181959
182068
  enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
182069
+ 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."),
181960
182070
  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)"),
181961
182071
  historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
181962
182072
  dreamer: DreamerConfigSchema.optional().describe("Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)"),
@@ -183019,6 +183129,7 @@ function registerPiDreamerProject(opts) {
183019
183129
  projectIdentity: opts.projectIdentity,
183020
183130
  client,
183021
183131
  dreamerConfig: opts.config,
183132
+ language: opts.language,
183022
183133
  gitCommitIndexing: opts.gitCommitIndexing,
183023
183134
  ensureRegistered: ensureProjectRegisteredFromPiDirectory,
183024
183135
  retrospectiveRawProvider: () => new PiRetrospectiveRawProvider({ projectCwd: opts.projectDir }),
@@ -183033,7 +183144,7 @@ function registerPiDreamerProject(opts) {
183033
183144
  const runManual = async (task) => runManualDream({
183034
183145
  db: opts.db,
183035
183146
  projectIdentity: opts.projectIdentity,
183036
- tasks: buildDreamTaskRuntimeConfigs(opts.config),
183147
+ tasks: buildDreamTaskRuntimeConfigs(opts.config, opts.language),
183037
183148
  executor: createDreamTaskExecutor({
183038
183149
  client,
183039
183150
  sessionDirectory: opts.projectDir,
@@ -183043,7 +183154,8 @@ function registerPiDreamerProject(opts) {
183043
183154
  }),
183044
183155
  primerRawProviderFactory: createPiPrimerRawProviderFactory(),
183045
183156
  userMemoryCollectionEnabled: userMemoryCollectionEnabled(opts.config),
183046
- ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory
183157
+ ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
183158
+ language: opts.language
183047
183159
  }),
183048
183160
  task
183049
183161
  });
@@ -183952,6 +184064,7 @@ var pendingPiDecisionBySession = new Map;
183952
184064
  var lastBoundMessageIdBySession = new Map;
183953
184065
  var scheduledWriteTokensBySession = new Map;
183954
184066
  var writerOverrideForTests = null;
184067
+ var retentionOverrideForTests = null;
183955
184068
  function normalizeMaterializeReason(harness, reason, rematerialized) {
183956
184069
  const raw = typeof reason === "string" ? reason.trim() : "";
183957
184070
  if (raw.length > 0) {
@@ -184092,7 +184205,7 @@ function writeTransformDecisionRow(dbPath, row) {
184092
184205
  WHERE session_id = ? AND harness = ?
184093
184206
  ORDER BY ts_ms DESC, rowid DESC
184094
184207
  LIMIT ?
184095
- )`).run(row.sessionId, row.harness, row.sessionId, row.harness, TRANSFORM_DECISIONS_RETENTION);
184208
+ )`).run(row.sessionId, row.harness, row.sessionId, row.harness, retentionOverrideForTests ?? TRANSFORM_DECISIONS_RETENTION);
184096
184209
  } finally {
184097
184210
  closeQuietly(db);
184098
184211
  }
@@ -185827,6 +185940,7 @@ function channel1RefireTokens(workingWindowTokens) {
185827
185940
  var S_GENTLE = 0.2;
185828
185941
  var S_FIRM = 0.4;
185829
185942
  var S_URGENT = 0.65;
185943
+ var CHANNEL1_PRESSURE_FLOOR = 0.8;
185830
185944
  var LEVEL_RANK = { gentle: 1, firm: 2, urgent: 3 };
185831
185945
  var DROP_SENTINELS = ["[dropped", "[truncated"];
185832
185946
  function isDroppedToolOutput(output) {
@@ -185837,7 +185951,8 @@ function toolOutputTokens(output) {
185837
185951
  return Math.round(byteSize(output) * TOKENS_PER_BYTE);
185838
185952
  }
185839
185953
  function decideChannel1(input) {
185840
- const { undroppedTokens, pressure, workingWindowTokens, hasRecentReduce } = input;
185954
+ const { undroppedTokens, workingWindowTokens, hasRecentReduce } = input;
185955
+ const pressure = Math.min(1, Math.max(0, input.pressure));
185841
185956
  const resetCycle = hasRecentReduce || undroppedTokens < input.lastNudgeUndropped;
185842
185957
  const lastNudge = resetCycle ? 0 : input.lastNudgeUndropped;
185843
185958
  const lastLevel = resetCycle ? "" : input.lastNudgeLevel;
@@ -185852,8 +185967,10 @@ function decideChannel1(input) {
185852
185967
  return quiet();
185853
185968
  if (undroppedTokens < CHANNEL1_FLOOR_TOKENS)
185854
185969
  return quiet();
185855
- const budget = workingWindowTokens > 0 ? workingWindowTokens : undroppedTokens || 1;
185856
- const severity = undroppedTokens / budget * pressure;
185970
+ if (pressure < CHANNEL1_PRESSURE_FLOOR)
185971
+ return quiet();
185972
+ const denom = Math.max(input.estimatedInputTokens, 1);
185973
+ const severity = Math.min(1, undroppedTokens / denom);
185857
185974
  if (severity < S_GENTLE)
185858
185975
  return quiet();
185859
185976
  let level;
@@ -188169,6 +188286,7 @@ function maybeChannel1ReminderForToolResult(args) {
188169
188286
  const decision = decideChannel1({
188170
188287
  undroppedTokens,
188171
188288
  pressure,
188289
+ estimatedInputTokens: state.lastInputTokens + state.turnToolTokens,
188172
188290
  workingWindowTokens,
188173
188291
  lastNudgeUndropped: getLastNudgeUndropped(db, sessionId),
188174
188292
  lastNudgeLevel: getLastNudgeLevel(db, sessionId),
@@ -190976,8 +191094,8 @@ function buildHistorianFailureNotice(failureCount, lastError) {
190976
191094
  ].join(`
190977
191095
  `);
190978
191096
  }
190979
- function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationError) {
190980
- return [
191097
+ function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationError, language) {
191098
+ const prompt = [
190981
191099
  originalPrompt,
190982
191100
  "",
190983
191101
  "Your previous XML response was invalid and cannot be persisted.",
@@ -190990,6 +191108,7 @@ function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationEr
190990
191108
  previousOutput
190991
191109
  ].join(`
190992
191110
  `);
191111
+ return withContentLanguageDirective(prompt, language, { preserveUserQuotes: true });
190993
191112
  }
190994
191113
  function validateStoredCompartments(compartments) {
190995
191114
  if (compartments.length === 0) {
@@ -193113,9 +193232,11 @@ ${chunkText}`,
193113
193232
  };
193114
193233
  };
193115
193234
  retainDrainReservationForRetryThrottle = true;
193235
+ const historianSystemPrompt = withContentLanguageDirective(COMPARTMENT_AGENT_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true });
193236
+ const historianEditorSystemPrompt = withContentLanguageDirective(HISTORIAN_EDITOR_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true });
193116
193237
  const firstResult = await runner.run({
193117
193238
  agent: HISTORIAN_AGENT_NAME,
193118
- systemPrompt: COMPARTMENT_AGENT_SYSTEM_PROMPT,
193239
+ systemPrompt: historianSystemPrompt,
193119
193240
  userMessage: prompt,
193120
193241
  model: historianModel,
193121
193242
  fallbackModels,
@@ -193130,10 +193251,10 @@ ${chunkText}`,
193130
193251
  let validatedDraftText = firstResult.ok ? firstResult.assistantText : null;
193131
193252
  if (validatedPass.kind === "validation-failed") {
193132
193253
  sessionLog(sessionId, `historian: first pass validation failed, retrying with repair prompt: ${validatedPass.error}`);
193133
- const repairPrompt = buildHistorianRepairPrompt(prompt, validatedPass.rawText, validatedPass.error);
193254
+ const repairPrompt = buildHistorianRepairPrompt(prompt, validatedPass.rawText, validatedPass.error, deps.language);
193134
193255
  const repairResult = await runner.run({
193135
193256
  agent: HISTORIAN_AGENT_NAME,
193136
- systemPrompt: COMPARTMENT_AGENT_SYSTEM_PROMPT,
193257
+ systemPrompt: historianSystemPrompt,
193137
193258
  userMessage: repairPrompt,
193138
193259
  model: historianModel,
193139
193260
  fallbackModels,
@@ -193158,7 +193279,7 @@ ${chunkText}`,
193158
193279
  sessionLog(sessionId, `historian: escalating to configured fallback model ${candidate}`);
193159
193280
  const fbResult = await runner.run({
193160
193281
  agent: HISTORIAN_AGENT_NAME,
193161
- systemPrompt: COMPARTMENT_AGENT_SYSTEM_PROMPT,
193282
+ systemPrompt: historianSystemPrompt,
193162
193283
  userMessage: prompt,
193163
193284
  model: candidate,
193164
193285
  fallbackModels: undefined,
@@ -193194,7 +193315,7 @@ ${chunkText}`,
193194
193315
  sessionLog(sessionId, "historian two-pass: running editor on draft");
193195
193316
  const editorResult = await runner.run({
193196
193317
  agent: HISTORIAN_AGENT_NAME,
193197
- systemPrompt: HISTORIAN_EDITOR_SYSTEM_PROMPT,
193318
+ systemPrompt: historianEditorSystemPrompt,
193198
193319
  userMessage: buildHistorianEditorPrompt(draftAssistantText),
193199
193320
  model: historianModel,
193200
193321
  timeoutMs: historianTimeoutMs,
@@ -194015,7 +194136,7 @@ Drop silently — do not narrate it. NEVER drop large ranges blindly (e.g., "1-5
194015
194136
  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.`;
194016
194137
  var CAVEMAN_COMPRESSION_WARNING = `
194017
194138
  **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.`;
194018
- function buildMagicContextSection(_agent, protectedTags, ctxReduceEnabled = true, dreamerEnabled = false, temporalAwarenessEnabled = false, cavemanTextCompressionEnabled = false, subagentMode = false) {
194139
+ function buildMagicContextSection(_agent, protectedTags, ctxReduceEnabled = true, dreamerEnabled = false, temporalAwarenessEnabled = false, cavemanTextCompressionEnabled = false, subagentMode = false, language) {
194019
194140
  if (subagentMode) {
194020
194141
  return `## Magic Context
194021
194142
 
@@ -194027,13 +194148,17 @@ The dreamer evaluates smart note conditions during nightly runs and surfaces the
194027
194148
  Example: \`ctx_note(action="write", content="Implement X because Y", surface_condition="When PR #42 is merged in this repo")\`` : "";
194028
194149
  const temporalGuidance = temporalAwarenessEnabled ? TEMPORAL_AWARENESS_GUIDANCE : "";
194029
194150
  const cavemanWarning = cavemanTextCompressionEnabled && !ctxReduceEnabled ? CAVEMAN_COMPRESSION_WARNING : "";
194151
+ const languageDirective = buildPrimaryLanguageDirective(language);
194152
+ const languageGuidance = languageDirective ? `
194153
+
194154
+ ${languageDirective}` : "";
194030
194155
  if (!ctxReduceEnabled) {
194031
194156
  return `## Magic Context
194032
194157
 
194033
194158
  ${LONG_TERM_PARTNER_FRAME}
194034
194159
  ${PARTNER_FRAME_CLOSER_NO_REDUCE}
194035
194160
 
194036
- ${BASE_INTRO_NO_REDUCE()}${smartNoteGuidance}${temporalGuidance}${cavemanWarning}`;
194161
+ ${BASE_INTRO_NO_REDUCE()}${smartNoteGuidance}${temporalGuidance}${cavemanWarning}${languageGuidance}`;
194037
194162
  }
194038
194163
  return `## Magic Context
194039
194164
 
@@ -194043,7 +194168,7 @@ ${PARTNER_FRAME_CLOSER_REDUCE}
194043
194168
  ${BASE_INTRO(protectedTags)}${smartNoteGuidance}${temporalGuidance}
194044
194169
  ${GENERIC_SECTION}
194045
194170
 
194046
- Prefer many small targeted operations over one large blanket operation, and keep the working set tidy as routine maintenance.`;
194171
+ Prefer many small targeted operations over one large blanket operation, and keep the working set tidy as routine maintenance.${languageGuidance}`;
194047
194172
  }
194048
194173
 
194049
194174
  // src/system-prompt.ts
@@ -194055,7 +194180,7 @@ function buildMagicContextBlock(opts) {
194055
194180
  const includeGuidance = (opts.includeGuidance ?? true) && !existing.includes(MAGIC_CONTEXT_MARKER);
194056
194181
  if (!includeGuidance)
194057
194182
  return null;
194058
- return buildMagicContextSection(null, opts.protectedTags ?? 20, opts.ctxReduceEnabled ?? true, opts.dreamerEnabled ?? false, opts.temporalAwarenessEnabled ?? false, opts.cavemanTextCompressionEnabled ?? false);
194183
+ return buildMagicContextSection(null, opts.protectedTags ?? 20, opts.ctxReduceEnabled ?? true, opts.dreamerEnabled ?? false, opts.temporalAwarenessEnabled ?? false, opts.cavemanTextCompressionEnabled ?? false, false, opts.language);
194059
194184
  }
194060
194185
  var DATE_PATTERN = /Today's date: .+/;
194061
194186
  function processSystemPromptForCache(args) {
@@ -195747,6 +195872,7 @@ function spawnPiHistorianRun(args) {
195747
195872
  memoryEnabled: historian.memoryEnabled,
195748
195873
  autoPromote: historian.autoPromote,
195749
195874
  userMemoriesEnabled: historian.userMemoriesEnabled,
195875
+ language: historian.language,
195750
195876
  compartmentLeaseHolderId: holderId,
195751
195877
  onPublished: () => {
195752
195878
  const sessionStillActive = isContextHandlerSessionActive(sessionId);
@@ -196696,7 +196822,7 @@ async function runValidatedHistorianPass(args) {
196696
196822
  return finalResult;
196697
196823
  }
196698
196824
  await args.callbacks?.onRepairRetry?.(firstValidation.error ?? "invalid compartment output");
196699
- const repairPrompt = buildHistorianRepairPrompt(args.prompt, firstRun.result, firstValidation.error ?? "invalid compartment output");
196825
+ const repairPrompt = buildHistorianRepairPrompt(args.prompt, firstRun.result, firstValidation.error ?? "invalid compartment output", args.language);
196700
196826
  const repairRun = await runHistorianPrompt({
196701
196827
  ...args,
196702
196828
  prompt: repairPrompt,
@@ -197338,6 +197464,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
197338
197464
  twoPass: deps.historianTwoPass,
197339
197465
  subagentKind: "recomp",
197340
197466
  agentId: HISTORIAN_RECOMP_AGENT,
197467
+ language: deps.language,
197341
197468
  callbacks: {
197342
197469
  onRepairRetry: async (error51) => {
197343
197470
  emitProgress(`Repair retry (pass ${passCount + 1})…`);
@@ -197756,6 +197883,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
197756
197883
  twoPass: deps.historianTwoPass,
197757
197884
  subagentKind: "recomp",
197758
197885
  agentId: HISTORIAN_RECOMP_AGENT,
197886
+ language: deps.language,
197759
197887
  callbacks: {
197760
197888
  onRepairRetry: async (error51) => {
197761
197889
  await sendIgnoredMessage(client, sessionId, `## Magic Recomp — Partial
@@ -198248,7 +198376,7 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
198248
198376
  client: createPiHistorianClient({
198249
198377
  runner: deps.runner,
198250
198378
  model: deps.historianModel,
198251
- systemPrompt: COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT,
198379
+ systemPrompt: withContentLanguageDirective(COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true }),
198252
198380
  fallbackModels: deps.historianFallbacks,
198253
198381
  timeoutMs: deps.historianTimeoutMs,
198254
198382
  thinkingLevel: deps.historianThinkingLevel,
@@ -198271,6 +198399,7 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
198271
198399
  autoPromote: deps.autoPromote,
198272
198400
  ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
198273
198401
  fallbackModels: deps.historianFallbacks,
198402
+ language: deps.language,
198274
198403
  fallbackModelId: ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined
198275
198404
  }, parsed.kind === "partial" ? { range: parsed.range } : {});
198276
198405
  if (result.published) {
@@ -198523,7 +198652,7 @@ async function runPiMemoryMigration(deps) {
198523
198652
  }
198524
198653
  const result = await deps.runner.run({
198525
198654
  agent: "magic-context-historian",
198526
- systemPrompt: MIGRATION_SYSTEM_PROMPT2,
198655
+ systemPrompt: withMigrationLanguageDirective(MIGRATION_SYSTEM_PROMPT2, deps.language),
198527
198656
  userMessage: prompt,
198528
198657
  model,
198529
198658
  fallbackModels: undefined,
@@ -198635,7 +198764,8 @@ An upgrade or recomp is already running for this session in the background. Wait
198635
198764
  thinkingLevel: deps.historianThinkingLevel,
198636
198765
  directory: ctx.cwd,
198637
198766
  sessionId,
198638
- userMemoriesEnabled: deps.userMemoriesEnabled
198767
+ userMemoriesEnabled: deps.userMemoriesEnabled,
198768
+ language: deps.language
198639
198769
  });
198640
198770
  return outcome.summary;
198641
198771
  } catch (error51) {
@@ -198707,7 +198837,7 @@ Rebuilding compartments into the v2 format and re-organizing project memories. T
198707
198837
  thinkingLevel: deps.historianThinkingLevel,
198708
198838
  directory: ctx.cwd,
198709
198839
  accountingSessionId: sessionId,
198710
- systemPrompt: COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT,
198840
+ systemPrompt: withContentLanguageDirective(COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true }),
198711
198841
  notify: (text) => sendCtxStatusMessage(pi, {
198712
198842
  title: "/ctx-session-upgrade",
198713
198843
  text,
@@ -198723,7 +198853,8 @@ Rebuilding compartments into the v2 format and re-organizing project memories. T
198723
198853
  autoPromote: deps.autoPromote,
198724
198854
  ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
198725
198855
  fallbackModels: deps.historianFallbacks,
198726
- fallbackModelId: sessionMainModel
198856
+ fallbackModelId: sessionMainModel,
198857
+ language: deps.language
198727
198858
  }, {});
198728
198859
  if (!recompResult.published || !isRecompComplete(recompResult.message)) {
198729
198860
  const reason = contextualizeUpgradeReason(isRecompFailure(recompResult.message) ? extractRecompReason(recompResult.message) : `Compartments were not fully rebuilt: ${extractRecompReason(recompResult.message)}`);
@@ -198935,7 +199066,7 @@ function formatThresholdPercent(value) {
198935
199066
  // package.json
198936
199067
  var package_default = {
198937
199068
  name: "@wolfx/pi-magic-context",
198938
- version: "0.27.2-patch.1",
199069
+ version: "0.28.0",
198939
199070
  type: "module",
198940
199071
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
198941
199072
  main: "dist/index.js",
@@ -205114,7 +205245,8 @@ function resolveSidekickFromConfig(config2) {
205114
205245
  systemPrompt: sidekick.system_prompt,
205115
205246
  timeoutMs: sidekick.timeout_ms,
205116
205247
  thinking_level: sidekick.thinking_level,
205117
- fallbackModels: resolveFallbackChain(sidekick.fallback_models)
205248
+ fallbackModels: resolveFallbackChain(sidekick.fallback_models),
205249
+ language: config2.language
205118
205250
  };
205119
205251
  }
205120
205252
  function resolveHistorianFromConfig(config2) {
@@ -205143,7 +205275,8 @@ function resolveHistorianFromConfig(config2) {
205143
205275
  historyBudgetPercentage: config2.history_budget_percentage,
205144
205276
  memoryEnabled: config2.memory.enabled,
205145
205277
  autoPromote: config2.memory.auto_promote,
205146
- userMemoriesEnabled: userMemoryCollectionEnabled(config2.dreamer)
205278
+ userMemoriesEnabled: userMemoryCollectionEnabled(config2.dreamer),
205279
+ language: config2.language
205147
205280
  };
205148
205281
  }
205149
205282
  function resolveAutoSearchFromConfig(config2) {
@@ -205170,7 +205303,7 @@ async function src_default2(pi) {
205170
205303
  return;
205171
205304
  }
205172
205305
  if (!db) {
205173
- warn(`Magic Context (pi) storage unavailable at ${dbPath} (cache schema is newer than this binary supports). ` + "Plugin will not register hooks; upgrade/restart Pi/OpenCode/Magic Context to recover.");
205306
+ warn(`Magic Context (pi) storage unavailable at ${dbPath} (cache schema is newer than this binary supports). ` + "A pinned or stale plugin is likely sharing this database with a newer instance. " + "Plugin will not register hooks; run 'npx @cortexkit/magic-context@latest doctor --force' " + "(or update Pi/OpenCode) and restart to recover.");
205174
205307
  return;
205175
205308
  }
205176
205309
  const database = db;
@@ -205258,6 +205391,7 @@ async function src_default2(pi) {
205258
205391
  executeThresholdTokens: cfg.execute_threshold_tokens
205259
205392
  },
205260
205393
  historian: hist,
205394
+ language: cfg.language,
205261
205395
  autoSearch: auto,
205262
205396
  resolveForProject: resolveContextOptionsForProject,
205263
205397
  maybeAutoEmbedSession: (sessionId, dir, identity) => {
@@ -205330,6 +205464,7 @@ async function src_default2(pi) {
205330
205464
  historianFallbacks: historianConfig?.fallbackModels,
205331
205465
  historianTimeoutMs: config2.historian_timeout_ms,
205332
205466
  historianThinkingLevel: historianConfig?.thinkingLevel,
205467
+ language: config2.language,
205333
205468
  memoryEnabled: config2.memory.enabled,
205334
205469
  autoPromote: config2.memory.auto_promote
205335
205470
  });
@@ -205342,6 +205477,7 @@ async function src_default2(pi) {
205342
205477
  historianFallbacks: historianConfig?.fallbackModels,
205343
205478
  historianTimeoutMs: config2.historian_timeout_ms,
205344
205479
  historianThinkingLevel: historianConfig?.thinkingLevel,
205480
+ language: config2.language,
205345
205481
  memoryEnabled: config2.memory.enabled,
205346
205482
  autoPromote: config2.memory.auto_promote,
205347
205483
  userMemoriesEnabled: userMemoryCollectionEnabled(config2.dreamer)
@@ -205373,6 +205509,7 @@ async function src_default2(pi) {
205373
205509
  config: dreamerConfig,
205374
205510
  embeddingConfig: config2.embedding,
205375
205511
  memoryEnabled: config2.memory.enabled,
205512
+ language: config2.language,
205376
205513
  gitCommitIndexing: config2.memory.git_commit_indexing,
205377
205514
  onAdjunctsRefreshNeeded: signalPiSystemPromptRefreshForProject
205378
205515
  });
@@ -205487,6 +205624,7 @@ async function src_default2(pi) {
205487
205624
  dreamerEnabled: effectiveDreamerRunnable,
205488
205625
  temporalAwarenessEnabled: effectiveConfig.temporal_awareness ?? false,
205489
205626
  cavemanTextCompressionEnabled: effectiveConfig.ctx_reduce_enabled === false && effectiveConfig.caveman_text_compression?.enabled === true,
205627
+ language: effectiveConfig.language,
205490
205628
  userMemoriesEnabled: userMemoryCollectionEnabled(effectiveConfig.dreamer),
205491
205629
  isCacheBusting,
205492
205630
  existingSystemPrompt: event.systemPrompt
@@ -142942,7 +142942,7 @@ function enforceSchemaFence(db, dbPath, latestSupportedVersion) {
142942
142942
  return true;
142943
142943
  }
142944
142944
  lastSchemaFenceRejection = { persistedVersion, supportedVersion: latestSupportedVersion };
142945
- 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.`);
142945
+ 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.`);
142946
142946
  return false;
142947
142947
  }
142948
142948
  var sqlitePragmaConfig = {
@@ -144839,6 +144839,10 @@ function stripUnsafeProjectConfigFields(projectRaw) {
144839
144839
  delete projectRaw.auto_update;
144840
144840
  warnings.push("Ignoring auto_update from project config (security: this setting only honors user-level config).");
144841
144841
  }
144842
+ if ("language" in projectRaw) {
144843
+ delete projectRaw.language;
144844
+ warnings.push("Ignoring language from project config (security: output language is a user-level setting).");
144845
+ }
144842
144846
  if ("sqlite" in projectRaw) {
144843
144847
  delete projectRaw.sqlite;
144844
144848
  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).");
@@ -159184,6 +159188,95 @@ function date4(params) {
159184
159188
 
159185
159189
  // ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
159186
159190
  config(en_default());
159191
+ // ../plugin/src/agents/language-directive.ts
159192
+ var ENGLISH_LANGUAGE_NAMES = new Intl.DisplayNames(["en"], {
159193
+ type: "language",
159194
+ fallback: "none"
159195
+ });
159196
+ function resolveLanguageName(language) {
159197
+ const code = typeof language === "string" ? language.trim().toLowerCase() : "";
159198
+ if (!/^[a-z]{2}$/.test(code))
159199
+ return "";
159200
+ let english;
159201
+ try {
159202
+ english = ENGLISH_LANGUAGE_NAMES.of(code) ?? undefined;
159203
+ } catch {
159204
+ return "";
159205
+ }
159206
+ if (!english)
159207
+ return "";
159208
+ let endonym;
159209
+ try {
159210
+ endonym = new Intl.DisplayNames([code], { type: "language", fallback: "none" }).of(code) ?? undefined;
159211
+ } catch {
159212
+ endonym = undefined;
159213
+ }
159214
+ return endonym && endonym !== english ? `${english} (${endonym})` : english;
159215
+ }
159216
+ function isValidLanguageCode(language) {
159217
+ return resolveLanguageName(language) !== "";
159218
+ }
159219
+ function buildContentLanguageDirective(language, options = {}) {
159220
+ const target = resolveLanguageName(language);
159221
+ if (!target)
159222
+ return "";
159223
+ const lines = [
159224
+ "## Output language",
159225
+ "",
159226
+ `Write human-readable prose you author in: ${target}.`,
159227
+ "",
159228
+ "Do not translate or rename structural tokens. Copy required output schemas exactly:",
159229
+ "- 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.",
159230
+ "- 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.",
159231
+ "- Localize only free-text prose values/content: summaries, memory text, explanations, titles, observations, and answers — unless the prompt says to preserve original wording.",
159232
+ "",
159233
+ "These literal values must remain English when used:",
159234
+ "PROJECT_RULES, ARCHITECTURE, CONSTRAINTS, CONFIG_VALUES, NAMING;",
159235
+ "causal_incident, trajectory_correction;",
159236
+ "feature, design, docs, release, investigation, bug, refactor, infra;",
159237
+ "memory, observation; true, false; No relevant memories found.",
159238
+ "",
159239
+ "Preserve the required output shape. Do not add commentary outside the requested XML/JSON/tool output."
159240
+ ];
159241
+ if (options.preserveUserQuotes) {
159242
+ lines.push("", `Preserve U: lines and directly quoted user text in their original source language; write the surrounding summary prose in ${target}.`);
159243
+ }
159244
+ if (options.retrospective) {
159245
+ lines.push("", `Write the lesson text in ${target}; paraphrase source text and never quote the user.`);
159246
+ }
159247
+ return lines.join(`
159248
+ `);
159249
+ }
159250
+ function withContentLanguageDirective(systemPrompt, language, options = {}) {
159251
+ const directive = buildContentLanguageDirective(language, options);
159252
+ return directive ? `${systemPrompt}
159253
+
159254
+ ${directive}` : systemPrompt;
159255
+ }
159256
+ function buildMigrationLanguageDirective(language) {
159257
+ const target = resolveLanguageName(language);
159258
+ if (!target)
159259
+ return "";
159260
+ return [
159261
+ "## Output language",
159262
+ "",
159263
+ "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."
159264
+ ].join(`
159265
+ `);
159266
+ }
159267
+ function withMigrationLanguageDirective(systemPrompt, language) {
159268
+ const directive = buildMigrationLanguageDirective(language);
159269
+ return directive ? `${systemPrompt}
159270
+
159271
+ ${directive}` : systemPrompt;
159272
+ }
159273
+ function buildPrimaryLanguageDirective(language) {
159274
+ const target = resolveLanguageName(language);
159275
+ if (!target)
159276
+ return "";
159277
+ 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.`;
159278
+ }
159279
+
159187
159280
  // ../plugin/src/features/magic-context/defaults.ts
159188
159281
  var DEFAULT_PROTECTED_TAGS = 20;
159189
159282
 
@@ -159531,6 +159624,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
159531
159624
  });
159532
159625
  var MagicContextConfigSchema = exports_external.object({
159533
159626
  enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
159627
+ 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."),
159534
159628
  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)"),
159535
159629
  historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
159536
159630
  dreamer: DreamerConfigSchema.optional().describe("Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)"),
@@ -171098,6 +171192,9 @@ function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
171098
171192
  nullCount: row?.null_count ?? 0
171099
171193
  };
171100
171194
  }
171195
+ var RECLAIM_HINT_EXCLUDED_TOOLS = ["todowrite"];
171196
+ var RECLAIM_HINT_MIN_TOKENS = 250;
171197
+ var RECLAIM_HINT_EXCLUDED_LIST = RECLAIM_HINT_EXCLUDED_TOOLS.map((name2) => `'${name2.replace(/'/g, "''")}'`).join(", ");
171101
171198
  function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, limit = 4) {
171102
171199
  if (limit <= 0)
171103
171200
  return [];
@@ -171107,10 +171204,18 @@ function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, li
171107
171204
  WHERE session_id = ? AND status = 'active'
171108
171205
  ORDER BY tag_number DESC LIMIT 1 OFFSET ?
171109
171206
  )` : "";
171110
- const params = protectedTags > 0 ? [sessionId, sessionId, protectedTags - 1, boundedLimit] : [sessionId, boundedLimit];
171207
+ const excludeStateTools = RECLAIM_HINT_EXCLUDED_LIST ? `AND (tool_name IS NULL OR tool_name NOT IN (${RECLAIM_HINT_EXCLUDED_LIST}))` : "";
171208
+ const valueFloor = `AND (
171209
+ (token_count IS NULL AND input_token_count IS NULL)
171210
+ OR (COALESCE(token_count, 0) + COALESCE(input_token_count, 0)) >= ?
171211
+ )`;
171212
+ const params = protectedTags > 0 ? [sessionId, RECLAIM_HINT_MIN_TOKENS, sessionId, protectedTags - 1, boundedLimit] : [sessionId, RECLAIM_HINT_MIN_TOKENS, boundedLimit];
171111
171213
  const rows = db.prepare(`SELECT tag_number, tool_name
171112
171214
  FROM tags
171113
- WHERE session_id = ? AND status = 'active' AND type = 'tool' ${whereProtected}
171215
+ WHERE session_id = ? AND status = 'active' AND type = 'tool'
171216
+ ${excludeStateTools}
171217
+ ${valueFloor}
171218
+ ${whereProtected}
171114
171219
  ORDER BY tag_number ASC, id ASC
171115
171220
  LIMIT ?`).all(...params);
171116
171221
  return rows.filter((row) => typeof row.tag_number === "number").map((row) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wolfx/pi-magic-context",
3
- "version": "0.27.2-patch.1",
3
+ "version": "0.28.0",
4
4
  "type": "module",
5
5
  "description": "Pi coding agent extension for Magic Context — cross-session memory and context management",
6
6
  "main": "dist/index.js",