@cortexkit/pi-magic-context 0.27.3 → 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
  };
@@ -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) => ({
@@ -156335,13 +156347,10 @@ function compareSemverCore(a, b) {
156335
156347
 
156336
156348
  // ../plugin/src/shared/announcement.ts
156337
156349
  init_data_path();
156338
- var ANNOUNCEMENT_VERSION = "0.27.0";
156350
+ var ANNOUNCEMENT_VERSION = "0.28.0";
156339
156351
  var ANNOUNCEMENT_FEATURES = [
156340
- "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.",
156341
- "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.",
156342
- "New Primers: durable answers to the questions that keep coming up about your project, kept current by the dreamer investigating the actual code.",
156343
- "Embedding storage no longer wipes your vectors when you change model or endpoint. Different models now coexist, so switching providers keeps your existing vectors.",
156344
- "Config moved to a shared CortexKit location (~/.config/cortexkit/ and <project>/.cortexkit/). This happens automatically on first run; your old file is preserved."
156352
+ `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.`,
156353
+ "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."
156345
156354
  ];
156346
156355
  var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU";
156347
156356
  var STATE_FILENAME = "last_announced_version";
@@ -156403,6 +156412,95 @@ function shouldKeepSubagents() {
156403
156412
  // src/index.ts
156404
156413
  init_logger();
156405
156414
 
156415
+ // ../plugin/src/agents/language-directive.ts
156416
+ var ENGLISH_LANGUAGE_NAMES = new Intl.DisplayNames(["en"], {
156417
+ type: "language",
156418
+ fallback: "none"
156419
+ });
156420
+ function resolveLanguageName(language) {
156421
+ const code = typeof language === "string" ? language.trim().toLowerCase() : "";
156422
+ if (!/^[a-z]{2}$/.test(code))
156423
+ return "";
156424
+ let english;
156425
+ try {
156426
+ english = ENGLISH_LANGUAGE_NAMES.of(code) ?? undefined;
156427
+ } catch {
156428
+ return "";
156429
+ }
156430
+ if (!english)
156431
+ return "";
156432
+ let endonym;
156433
+ try {
156434
+ endonym = new Intl.DisplayNames([code], { type: "language", fallback: "none" }).of(code) ?? undefined;
156435
+ } catch {
156436
+ endonym = undefined;
156437
+ }
156438
+ return endonym && endonym !== english ? `${english} (${endonym})` : english;
156439
+ }
156440
+ function isValidLanguageCode(language) {
156441
+ return resolveLanguageName(language) !== "";
156442
+ }
156443
+ function buildContentLanguageDirective(language, options = {}) {
156444
+ const target = resolveLanguageName(language);
156445
+ if (!target)
156446
+ return "";
156447
+ const lines = [
156448
+ "## Output language",
156449
+ "",
156450
+ `Write human-readable prose you author in: ${target}.`,
156451
+ "",
156452
+ "Do not translate or rename structural tokens. Copy required output schemas exactly:",
156453
+ "- 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.",
156454
+ "- 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.",
156455
+ "- Localize only free-text prose values/content: summaries, memory text, explanations, titles, observations, and answers — unless the prompt says to preserve original wording.",
156456
+ "",
156457
+ "These literal values must remain English when used:",
156458
+ "PROJECT_RULES, ARCHITECTURE, CONSTRAINTS, CONFIG_VALUES, NAMING;",
156459
+ "causal_incident, trajectory_correction;",
156460
+ "feature, design, docs, release, investigation, bug, refactor, infra;",
156461
+ "memory, observation; true, false; No relevant memories found.",
156462
+ "",
156463
+ "Preserve the required output shape. Do not add commentary outside the requested XML/JSON/tool output."
156464
+ ];
156465
+ if (options.preserveUserQuotes) {
156466
+ lines.push("", `Preserve U: lines and directly quoted user text in their original source language; write the surrounding summary prose in ${target}.`);
156467
+ }
156468
+ if (options.retrospective) {
156469
+ lines.push("", `Write the lesson text in ${target}; paraphrase source text and never quote the user.`);
156470
+ }
156471
+ return lines.join(`
156472
+ `);
156473
+ }
156474
+ function withContentLanguageDirective(systemPrompt, language, options = {}) {
156475
+ const directive = buildContentLanguageDirective(language, options);
156476
+ return directive ? `${systemPrompt}
156477
+
156478
+ ${directive}` : systemPrompt;
156479
+ }
156480
+ function buildMigrationLanguageDirective(language) {
156481
+ const target = resolveLanguageName(language);
156482
+ if (!target)
156483
+ return "";
156484
+ return [
156485
+ "## Output language",
156486
+ "",
156487
+ "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."
156488
+ ].join(`
156489
+ `);
156490
+ }
156491
+ function withMigrationLanguageDirective(systemPrompt, language) {
156492
+ const directive = buildMigrationLanguageDirective(language);
156493
+ return directive ? `${systemPrompt}
156494
+
156495
+ ${directive}` : systemPrompt;
156496
+ }
156497
+ function buildPrimaryLanguageDirective(language) {
156498
+ const target = resolveLanguageName(language);
156499
+ if (!target)
156500
+ return "";
156501
+ 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.`;
156502
+ }
156503
+
156406
156504
  // ../plugin/src/features/magic-context/sidekick/core.ts
156407
156505
  var SIDEKICK_SYSTEM_PROMPT = `You are Sidekick, a focused memory-retrieval subagent for an AI coding assistant.
156408
156506
 
@@ -157123,7 +157221,7 @@ function registerCtxAugCommand(pi, config) {
157123
157221
  sessionLog(sessionLabel, "/ctx-aug: project identity", projectIdentity);
157124
157222
  const result = await runner.run({
157125
157223
  agent: "sidekick",
157126
- systemPrompt: config.systemPrompt ?? SIDEKICK_SYSTEM_PROMPT,
157224
+ systemPrompt: withContentLanguageDirective(config.systemPrompt ?? SIDEKICK_SYSTEM_PROMPT, config.language),
157127
157225
  userMessage: prompt,
157128
157226
  model: config.model,
157129
157227
  fallbackModels: config.fallbackModels,
@@ -172319,6 +172417,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
172319
172417
  var MagicContextConfigSchema = exports_external.object({
172320
172418
  enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
172321
172419
  auto_update: exports_external.boolean().optional().describe("Enable automatic npm self-update checks for the OpenCode plugin. Security: USER-only in config loader, so hostile project configs cannot suppress updates."),
172420
+ 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."),
172322
172421
  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)"),
172323
172422
  historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
172324
172423
  dreamer: DreamerConfigSchema.optional().describe("Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)"),
@@ -176616,7 +176715,7 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
176616
176715
  query: { directory: args.sessionDirectory },
176617
176716
  body: {
176618
176717
  agent: DREAMER_REVIEWER_AGENT,
176619
- system: REVIEW_USER_MEMORIES_SYSTEM_PROMPT,
176718
+ system: withContentLanguageDirective(REVIEW_USER_MEMORIES_SYSTEM_PROMPT, args.language),
176620
176719
  ...modelBodyField(args.model),
176621
176720
  parts: [{ type: "text", text: prompt, synthetic: true }]
176622
176721
  }
@@ -179430,7 +179529,7 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
179430
179529
  query: { directory: args.sessionDirectory },
179431
179530
  body: {
179432
179531
  agent: DREAMER_PRIMER_INVESTIGATOR_AGENT,
179433
- system: PRIMER_INVESTIGATOR_SYSTEM_PROMPT,
179532
+ system: withContentLanguageDirective(PRIMER_INVESTIGATOR_SYSTEM_PROMPT, args.language),
179434
179533
  ...modelBodyField(args.model),
179435
179534
  parts: [{ type: "text", text: prompt, synthetic: true }]
179436
179535
  }
@@ -180211,7 +180310,7 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
180211
180310
  query: { directory: args.sessionDirectory },
180212
180311
  body: {
180213
180312
  agent: DREAMER_MEMORY_MAPPER_AGENT,
180214
- system: VERIFY_SYSTEM_PROMPT,
180313
+ system: withContentLanguageDirective(VERIFY_SYSTEM_PROMPT, args.language),
180215
180314
  ...modelBodyField(args.model),
180216
180315
  parts: [{ type: "text", text: prompt, synthetic: true }]
180217
180316
  }
@@ -180491,7 +180590,8 @@ function createDreamTaskExecutor(deps) {
180491
180590
  deadline,
180492
180591
  promotionThreshold: config2.promotionThreshold ?? 3,
180493
180592
  model: config2.model,
180494
- fallbackModels: config2.fallbackModels
180593
+ fallbackModels: config2.fallbackModels,
180594
+ language: config2.language ?? deps.language
180495
180595
  });
180496
180596
  recordRun("completed", null);
180497
180597
  log(`[dreamer] review-user-memories: promoted=${result.promoted} merged=${result.merged} dismissed=${result.dismissed}`);
@@ -180527,7 +180627,8 @@ function createDreamTaskExecutor(deps) {
180527
180627
  deadline,
180528
180628
  forceBroad: config2.task === "verify-broad",
180529
180629
  model: config2.model,
180530
- fallbackModels: config2.fallbackModels
180630
+ fallbackModels: config2.fallbackModels,
180631
+ language: config2.language ?? deps.language
180531
180632
  });
180532
180633
  recordRun("completed", null, {
180533
180634
  memoryChanges: computeMemoryDelta(memoryBefore)
@@ -180579,6 +180680,7 @@ function createDreamTaskExecutor(deps) {
180579
180680
  deadline,
180580
180681
  model: config2.model,
180581
180682
  fallbackModels: config2.fallbackModels,
180683
+ language: config2.language ?? deps.language,
180582
180684
  rawProviderFactory: deps.primerRawProviderFactory
180583
180685
  });
180584
180686
  recordRun("completed", null);
@@ -180696,6 +180798,7 @@ function retrospectiveEventsForSessions(db, sessionIds) {
180696
180798
  try {
180697
180799
  for (const event of getCompartmentEvents(db, sessionId)) {
180698
180800
  if (event.kind !== "causal_incident" && event.kind !== "trajectory_correction") {
180801
+ log(`[dreamer] dropping event: unknown kind="${event.kind}"`);
180699
180802
  continue;
180700
180803
  }
180701
180804
  events.push({
@@ -180820,7 +180923,9 @@ async function runRetrospectiveTask(config2, ctx, helpers) {
180820
180923
  const frictionWindow = renderFrictionWindow(messages, flagged.map((message) => message.ordinal));
180821
180924
  const eventSessionIds = new Set(messages.map((message) => message.sessionId));
180822
180925
  const events = retrospectiveEventsForSessions(db, eventSessionIds);
180823
- const deepenRun = await runChildTurn(RETROSPECTIVE_SYSTEM_PROMPT, buildRetrospectivePrompt({ projectPath: projectIdentity, frictionWindow, events }));
180926
+ const deepenRun = await runChildTurn(withContentLanguageDirective(RETROSPECTIVE_SYSTEM_PROMPT, config2.language ?? deps.language, {
180927
+ retrospective: true
180928
+ }), buildRetrospectivePrompt({ projectPath: projectIdentity, frictionWindow, events }));
180824
180929
  if (leaseLost)
180825
180930
  throw new Error("Dream lease lost during retrospective");
180826
180931
  const sourceSessionId = flagged[0]?.sessionId ?? userMessages[0]?.sessionId ?? "retrospective";
@@ -180906,7 +181011,7 @@ async function runAgenticTask(config2, ctx, helpers) {
180906
181011
  query: { directory: docsDir },
180907
181012
  body: {
180908
181013
  agent: task === "maintain-docs" ? DREAMER_DOCS_AGENT : DREAMER_AGENT,
180909
- system: task === "maintain-docs" ? MAINTAIN_DOCS_SYSTEM_PROMPT : CURATE_SYSTEM_PROMPT,
181014
+ system: task === "maintain-docs" ? MAINTAIN_DOCS_SYSTEM_PROMPT : withContentLanguageDirective(CURATE_SYSTEM_PROMPT, config2.language ?? deps.language),
180910
181015
  ...modelBodyField(config2.model),
180911
181016
  parts: [{ type: "text", text: taskPrompt, synthetic: true }]
180912
181017
  }
@@ -181788,7 +181893,7 @@ async function sweepProject(reg, origin, db, gitCommitEnabled) {
181788
181893
  }
181789
181894
  try {
181790
181895
  await runCompiledSmartNoteSweep(reg, db);
181791
- const runtimeConfigs = buildDreamTaskRuntimeConfigs(dreamerConfig);
181896
+ const runtimeConfigs = buildDreamTaskRuntimeConfigs(dreamerConfig, reg.language);
181792
181897
  const executor = createDreamTaskExecutor({
181793
181898
  client: reg.client,
181794
181899
  sessionDirectory: reg.directory,
@@ -181796,7 +181901,8 @@ async function sweepProject(reg, origin, db, gitCommitEnabled) {
181796
181901
  retrospectiveRawProvider: reg.retrospectiveRawProvider ?? ((db2) => new OpenCodeRetrospectiveRawProvider({ contextDb: db2, openOpenCodeDb })),
181797
181902
  primerRawProviderFactory: reg.primerRawProviderFactory,
181798
181903
  userMemoryCollectionEnabled: userMemoryCollectionEnabled(dreamerConfig),
181799
- ensureProjectRegistered: reg.ensureRegistered
181904
+ ensureProjectRegistered: reg.ensureRegistered,
181905
+ language: reg.language
181800
181906
  });
181801
181907
  const ran = await runDueTasksForProject({
181802
181908
  db,
@@ -182349,6 +182455,10 @@ function stripUnsafeProjectConfigFields(projectRaw) {
182349
182455
  delete projectRaw.auto_update;
182350
182456
  warnings.push("Ignoring auto_update from project config (security: this setting only honors user-level config).");
182351
182457
  }
182458
+ if ("language" in projectRaw) {
182459
+ delete projectRaw.language;
182460
+ warnings.push("Ignoring language from project config (security: output language is a user-level setting).");
182461
+ }
182352
182462
  if ("sqlite" in projectRaw) {
182353
182463
  delete projectRaw.sqlite;
182354
182464
  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).");
@@ -183404,6 +183514,7 @@ function registerPiDreamerProject(opts) {
183404
183514
  projectIdentity: opts.projectIdentity,
183405
183515
  client,
183406
183516
  dreamerConfig: opts.config,
183517
+ language: opts.language,
183407
183518
  gitCommitIndexing: opts.gitCommitIndexing,
183408
183519
  ensureRegistered: ensureProjectRegisteredFromPiDirectory,
183409
183520
  retrospectiveRawProvider: () => new PiRetrospectiveRawProvider({ projectCwd: opts.projectDir }),
@@ -183418,7 +183529,7 @@ function registerPiDreamerProject(opts) {
183418
183529
  const runManual = async (task) => runManualDream({
183419
183530
  db: opts.db,
183420
183531
  projectIdentity: opts.projectIdentity,
183421
- tasks: buildDreamTaskRuntimeConfigs(opts.config),
183532
+ tasks: buildDreamTaskRuntimeConfigs(opts.config, opts.language),
183422
183533
  executor: createDreamTaskExecutor({
183423
183534
  client,
183424
183535
  sessionDirectory: opts.projectDir,
@@ -183428,7 +183539,8 @@ function registerPiDreamerProject(opts) {
183428
183539
  }),
183429
183540
  primerRawProviderFactory: createPiPrimerRawProviderFactory(),
183430
183541
  userMemoryCollectionEnabled: userMemoryCollectionEnabled(opts.config),
183431
- ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory
183542
+ ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
183543
+ language: opts.language
183432
183544
  }),
183433
183545
  task
183434
183546
  });
@@ -186213,6 +186325,7 @@ function channel1RefireTokens(workingWindowTokens) {
186213
186325
  var S_GENTLE = 0.2;
186214
186326
  var S_FIRM = 0.4;
186215
186327
  var S_URGENT = 0.65;
186328
+ var CHANNEL1_PRESSURE_FLOOR = 0.8;
186216
186329
  var LEVEL_RANK = { gentle: 1, firm: 2, urgent: 3 };
186217
186330
  var DROP_SENTINELS = ["[dropped", "[truncated"];
186218
186331
  function isDroppedToolOutput(output) {
@@ -186223,7 +186336,8 @@ function toolOutputTokens(output) {
186223
186336
  return Math.round(byteSize(output) * TOKENS_PER_BYTE);
186224
186337
  }
186225
186338
  function decideChannel1(input) {
186226
- const { undroppedTokens, pressure, workingWindowTokens, hasRecentReduce } = input;
186339
+ const { undroppedTokens, workingWindowTokens, hasRecentReduce } = input;
186340
+ const pressure = Math.min(1, Math.max(0, input.pressure));
186227
186341
  const resetCycle = hasRecentReduce || undroppedTokens < input.lastNudgeUndropped;
186228
186342
  const lastNudge = resetCycle ? 0 : input.lastNudgeUndropped;
186229
186343
  const lastLevel = resetCycle ? "" : input.lastNudgeLevel;
@@ -186238,8 +186352,10 @@ function decideChannel1(input) {
186238
186352
  return quiet();
186239
186353
  if (undroppedTokens < CHANNEL1_FLOOR_TOKENS)
186240
186354
  return quiet();
186241
- const budget = workingWindowTokens > 0 ? workingWindowTokens : undroppedTokens || 1;
186242
- const severity = undroppedTokens / budget * pressure;
186355
+ if (pressure < CHANNEL1_PRESSURE_FLOOR)
186356
+ return quiet();
186357
+ const denom = Math.max(input.estimatedInputTokens, 1);
186358
+ const severity = Math.min(1, undroppedTokens / denom);
186243
186359
  if (severity < S_GENTLE)
186244
186360
  return quiet();
186245
186361
  let level;
@@ -188555,6 +188671,7 @@ function maybeChannel1ReminderForToolResult(args) {
188555
188671
  const decision = decideChannel1({
188556
188672
  undroppedTokens,
188557
188673
  pressure,
188674
+ estimatedInputTokens: state.lastInputTokens + state.turnToolTokens,
188558
188675
  workingWindowTokens,
188559
188676
  lastNudgeUndropped: getLastNudgeUndropped(db, sessionId),
188560
188677
  lastNudgeLevel: getLastNudgeLevel(db, sessionId),
@@ -191362,8 +191479,8 @@ function buildHistorianFailureNotice(failureCount, lastError) {
191362
191479
  ].join(`
191363
191480
  `);
191364
191481
  }
191365
- function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationError) {
191366
- return [
191482
+ function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationError, language) {
191483
+ const prompt = [
191367
191484
  originalPrompt,
191368
191485
  "",
191369
191486
  "Your previous XML response was invalid and cannot be persisted.",
@@ -191376,6 +191493,7 @@ function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationEr
191376
191493
  previousOutput
191377
191494
  ].join(`
191378
191495
  `);
191496
+ return withContentLanguageDirective(prompt, language, { preserveUserQuotes: true });
191379
191497
  }
191380
191498
  function validateStoredCompartments(compartments) {
191381
191499
  if (compartments.length === 0) {
@@ -193499,9 +193617,11 @@ ${chunkText}`,
193499
193617
  };
193500
193618
  };
193501
193619
  retainDrainReservationForRetryThrottle = true;
193620
+ const historianSystemPrompt = withContentLanguageDirective(COMPARTMENT_AGENT_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true });
193621
+ const historianEditorSystemPrompt = withContentLanguageDirective(HISTORIAN_EDITOR_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true });
193502
193622
  const firstResult = await runner.run({
193503
193623
  agent: HISTORIAN_AGENT_NAME,
193504
- systemPrompt: COMPARTMENT_AGENT_SYSTEM_PROMPT,
193624
+ systemPrompt: historianSystemPrompt,
193505
193625
  userMessage: prompt,
193506
193626
  model: historianModel,
193507
193627
  fallbackModels,
@@ -193516,10 +193636,10 @@ ${chunkText}`,
193516
193636
  let validatedDraftText = firstResult.ok ? firstResult.assistantText : null;
193517
193637
  if (validatedPass.kind === "validation-failed") {
193518
193638
  sessionLog(sessionId, `historian: first pass validation failed, retrying with repair prompt: ${validatedPass.error}`);
193519
- const repairPrompt = buildHistorianRepairPrompt(prompt, validatedPass.rawText, validatedPass.error);
193639
+ const repairPrompt = buildHistorianRepairPrompt(prompt, validatedPass.rawText, validatedPass.error, deps.language);
193520
193640
  const repairResult = await runner.run({
193521
193641
  agent: HISTORIAN_AGENT_NAME,
193522
- systemPrompt: COMPARTMENT_AGENT_SYSTEM_PROMPT,
193642
+ systemPrompt: historianSystemPrompt,
193523
193643
  userMessage: repairPrompt,
193524
193644
  model: historianModel,
193525
193645
  fallbackModels,
@@ -193544,7 +193664,7 @@ ${chunkText}`,
193544
193664
  sessionLog(sessionId, `historian: escalating to configured fallback model ${candidate}`);
193545
193665
  const fbResult = await runner.run({
193546
193666
  agent: HISTORIAN_AGENT_NAME,
193547
- systemPrompt: COMPARTMENT_AGENT_SYSTEM_PROMPT,
193667
+ systemPrompt: historianSystemPrompt,
193548
193668
  userMessage: prompt,
193549
193669
  model: candidate,
193550
193670
  fallbackModels: undefined,
@@ -193580,7 +193700,7 @@ ${chunkText}`,
193580
193700
  sessionLog(sessionId, "historian two-pass: running editor on draft");
193581
193701
  const editorResult = await runner.run({
193582
193702
  agent: HISTORIAN_AGENT_NAME,
193583
- systemPrompt: HISTORIAN_EDITOR_SYSTEM_PROMPT,
193703
+ systemPrompt: historianEditorSystemPrompt,
193584
193704
  userMessage: buildHistorianEditorPrompt(draftAssistantText),
193585
193705
  model: historianModel,
193586
193706
  timeoutMs: historianTimeoutMs,
@@ -194401,7 +194521,7 @@ Drop silently — do not narrate it. NEVER drop large ranges blindly (e.g., "1-5
194401
194521
  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.`;
194402
194522
  var CAVEMAN_COMPRESSION_WARNING = `
194403
194523
  **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.`;
194404
- function buildMagicContextSection(_agent, protectedTags, ctxReduceEnabled = true, dreamerEnabled = false, temporalAwarenessEnabled = false, cavemanTextCompressionEnabled = false, subagentMode = false) {
194524
+ function buildMagicContextSection(_agent, protectedTags, ctxReduceEnabled = true, dreamerEnabled = false, temporalAwarenessEnabled = false, cavemanTextCompressionEnabled = false, subagentMode = false, language) {
194405
194525
  if (subagentMode) {
194406
194526
  return `## Magic Context
194407
194527
 
@@ -194413,13 +194533,17 @@ The dreamer evaluates smart note conditions during nightly runs and surfaces the
194413
194533
  Example: \`ctx_note(action="write", content="Implement X because Y", surface_condition="When PR #42 is merged in this repo")\`` : "";
194414
194534
  const temporalGuidance = temporalAwarenessEnabled ? TEMPORAL_AWARENESS_GUIDANCE : "";
194415
194535
  const cavemanWarning = cavemanTextCompressionEnabled && !ctxReduceEnabled ? CAVEMAN_COMPRESSION_WARNING : "";
194536
+ const languageDirective = buildPrimaryLanguageDirective(language);
194537
+ const languageGuidance = languageDirective ? `
194538
+
194539
+ ${languageDirective}` : "";
194416
194540
  if (!ctxReduceEnabled) {
194417
194541
  return `## Magic Context
194418
194542
 
194419
194543
  ${LONG_TERM_PARTNER_FRAME}
194420
194544
  ${PARTNER_FRAME_CLOSER_NO_REDUCE}
194421
194545
 
194422
- ${BASE_INTRO_NO_REDUCE()}${smartNoteGuidance}${temporalGuidance}${cavemanWarning}`;
194546
+ ${BASE_INTRO_NO_REDUCE()}${smartNoteGuidance}${temporalGuidance}${cavemanWarning}${languageGuidance}`;
194423
194547
  }
194424
194548
  return `## Magic Context
194425
194549
 
@@ -194429,7 +194553,7 @@ ${PARTNER_FRAME_CLOSER_REDUCE}
194429
194553
  ${BASE_INTRO(protectedTags)}${smartNoteGuidance}${temporalGuidance}
194430
194554
  ${GENERIC_SECTION}
194431
194555
 
194432
- Prefer many small targeted operations over one large blanket operation, and keep the working set tidy as routine maintenance.`;
194556
+ Prefer many small targeted operations over one large blanket operation, and keep the working set tidy as routine maintenance.${languageGuidance}`;
194433
194557
  }
194434
194558
 
194435
194559
  // src/system-prompt.ts
@@ -194441,7 +194565,7 @@ function buildMagicContextBlock(opts) {
194441
194565
  const includeGuidance = (opts.includeGuidance ?? true) && !existing.includes(MAGIC_CONTEXT_MARKER);
194442
194566
  if (!includeGuidance)
194443
194567
  return null;
194444
- return buildMagicContextSection(null, opts.protectedTags ?? 20, opts.ctxReduceEnabled ?? true, opts.dreamerEnabled ?? false, opts.temporalAwarenessEnabled ?? false, opts.cavemanTextCompressionEnabled ?? false);
194568
+ return buildMagicContextSection(null, opts.protectedTags ?? 20, opts.ctxReduceEnabled ?? true, opts.dreamerEnabled ?? false, opts.temporalAwarenessEnabled ?? false, opts.cavemanTextCompressionEnabled ?? false, false, opts.language);
194445
194569
  }
194446
194570
  var DATE_PATTERN = /Today's date: .+/;
194447
194571
  function processSystemPromptForCache(args) {
@@ -196133,6 +196257,7 @@ function spawnPiHistorianRun(args) {
196133
196257
  memoryEnabled: historian.memoryEnabled,
196134
196258
  autoPromote: historian.autoPromote,
196135
196259
  userMemoriesEnabled: historian.userMemoriesEnabled,
196260
+ language: historian.language,
196136
196261
  compartmentLeaseHolderId: holderId,
196137
196262
  onPublished: () => {
196138
196263
  const sessionStillActive = isContextHandlerSessionActive(sessionId);
@@ -197082,7 +197207,7 @@ async function runValidatedHistorianPass(args) {
197082
197207
  return finalResult;
197083
197208
  }
197084
197209
  await args.callbacks?.onRepairRetry?.(firstValidation.error ?? "invalid compartment output");
197085
- const repairPrompt = buildHistorianRepairPrompt(args.prompt, firstRun.result, firstValidation.error ?? "invalid compartment output");
197210
+ const repairPrompt = buildHistorianRepairPrompt(args.prompt, firstRun.result, firstValidation.error ?? "invalid compartment output", args.language);
197086
197211
  const repairRun = await runHistorianPrompt({
197087
197212
  ...args,
197088
197213
  prompt: repairPrompt,
@@ -197724,6 +197849,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
197724
197849
  twoPass: deps.historianTwoPass,
197725
197850
  subagentKind: "recomp",
197726
197851
  agentId: HISTORIAN_RECOMP_AGENT,
197852
+ language: deps.language,
197727
197853
  callbacks: {
197728
197854
  onRepairRetry: async (error51) => {
197729
197855
  emitProgress(`Repair retry (pass ${passCount + 1})…`);
@@ -198142,6 +198268,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
198142
198268
  twoPass: deps.historianTwoPass,
198143
198269
  subagentKind: "recomp",
198144
198270
  agentId: HISTORIAN_RECOMP_AGENT,
198271
+ language: deps.language,
198145
198272
  callbacks: {
198146
198273
  onRepairRetry: async (error51) => {
198147
198274
  await sendIgnoredMessage(client, sessionId, `## Magic Recomp — Partial
@@ -198634,7 +198761,7 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
198634
198761
  client: createPiHistorianClient({
198635
198762
  runner: deps.runner,
198636
198763
  model: deps.historianModel,
198637
- systemPrompt: COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT,
198764
+ systemPrompt: withContentLanguageDirective(COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true }),
198638
198765
  fallbackModels: deps.historianFallbacks,
198639
198766
  timeoutMs: deps.historianTimeoutMs,
198640
198767
  thinkingLevel: deps.historianThinkingLevel,
@@ -198657,6 +198784,7 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
198657
198784
  autoPromote: deps.autoPromote,
198658
198785
  ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
198659
198786
  fallbackModels: deps.historianFallbacks,
198787
+ language: deps.language,
198660
198788
  fallbackModelId: ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined
198661
198789
  }, parsed.kind === "partial" ? { range: parsed.range } : {});
198662
198790
  if (result.published) {
@@ -198909,7 +199037,7 @@ async function runPiMemoryMigration(deps) {
198909
199037
  }
198910
199038
  const result = await deps.runner.run({
198911
199039
  agent: "magic-context-historian",
198912
- systemPrompt: MIGRATION_SYSTEM_PROMPT2,
199040
+ systemPrompt: withMigrationLanguageDirective(MIGRATION_SYSTEM_PROMPT2, deps.language),
198913
199041
  userMessage: prompt,
198914
199042
  model,
198915
199043
  fallbackModels: undefined,
@@ -199021,7 +199149,8 @@ An upgrade or recomp is already running for this session in the background. Wait
199021
199149
  thinkingLevel: deps.historianThinkingLevel,
199022
199150
  directory: ctx.cwd,
199023
199151
  sessionId,
199024
- userMemoriesEnabled: deps.userMemoriesEnabled
199152
+ userMemoriesEnabled: deps.userMemoriesEnabled,
199153
+ language: deps.language
199025
199154
  });
199026
199155
  return outcome.summary;
199027
199156
  } catch (error51) {
@@ -199093,7 +199222,7 @@ Rebuilding compartments into the v2 format and re-organizing project memories. T
199093
199222
  thinkingLevel: deps.historianThinkingLevel,
199094
199223
  directory: ctx.cwd,
199095
199224
  accountingSessionId: sessionId,
199096
- systemPrompt: COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT,
199225
+ systemPrompt: withContentLanguageDirective(COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true }),
199097
199226
  notify: (text) => sendCtxStatusMessage(pi, {
199098
199227
  title: "/ctx-session-upgrade",
199099
199228
  text,
@@ -199109,7 +199238,8 @@ Rebuilding compartments into the v2 format and re-organizing project memories. T
199109
199238
  autoPromote: deps.autoPromote,
199110
199239
  ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
199111
199240
  fallbackModels: deps.historianFallbacks,
199112
- fallbackModelId: sessionMainModel
199241
+ fallbackModelId: sessionMainModel,
199242
+ language: deps.language
199113
199243
  }, {});
199114
199244
  if (!recompResult.published || !isRecompComplete(recompResult.message)) {
199115
199245
  const reason = contextualizeUpgradeReason(isRecompFailure(recompResult.message) ? extractRecompReason(recompResult.message) : `Compartments were not fully rebuilt: ${extractRecompReason(recompResult.message)}`);
@@ -199321,7 +199451,7 @@ function formatThresholdPercent(value) {
199321
199451
  // package.json
199322
199452
  var package_default = {
199323
199453
  name: "@cortexkit/pi-magic-context",
199324
- version: "0.27.3",
199454
+ version: "0.28.0",
199325
199455
  type: "module",
199326
199456
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
199327
199457
  main: "dist/index.js",
@@ -205476,7 +205606,8 @@ function resolveSidekickFromConfig(config2) {
205476
205606
  systemPrompt: sidekick.system_prompt,
205477
205607
  timeoutMs: sidekick.timeout_ms,
205478
205608
  thinking_level: sidekick.thinking_level,
205479
- fallbackModels: resolveFallbackChain(sidekick.fallback_models)
205609
+ fallbackModels: resolveFallbackChain(sidekick.fallback_models),
205610
+ language: config2.language
205480
205611
  };
205481
205612
  }
205482
205613
  function resolveHistorianFromConfig(config2) {
@@ -205505,7 +205636,8 @@ function resolveHistorianFromConfig(config2) {
205505
205636
  historyBudgetPercentage: config2.history_budget_percentage,
205506
205637
  memoryEnabled: config2.memory.enabled,
205507
205638
  autoPromote: config2.memory.auto_promote,
205508
- userMemoriesEnabled: userMemoryCollectionEnabled(config2.dreamer)
205639
+ userMemoriesEnabled: userMemoryCollectionEnabled(config2.dreamer),
205640
+ language: config2.language
205509
205641
  };
205510
205642
  }
205511
205643
  function resolveAutoSearchFromConfig(config2) {
@@ -205620,6 +205752,7 @@ async function src_default2(pi) {
205620
205752
  executeThresholdTokens: cfg.execute_threshold_tokens
205621
205753
  },
205622
205754
  historian: hist,
205755
+ language: cfg.language,
205623
205756
  autoSearch: auto,
205624
205757
  resolveForProject: resolveContextOptionsForProject,
205625
205758
  maybeAutoEmbedSession: (sessionId, dir, identity) => {
@@ -205692,6 +205825,7 @@ async function src_default2(pi) {
205692
205825
  historianFallbacks: historianConfig?.fallbackModels,
205693
205826
  historianTimeoutMs: config2.historian_timeout_ms,
205694
205827
  historianThinkingLevel: historianConfig?.thinkingLevel,
205828
+ language: config2.language,
205695
205829
  memoryEnabled: config2.memory.enabled,
205696
205830
  autoPromote: config2.memory.auto_promote
205697
205831
  });
@@ -205704,6 +205838,7 @@ async function src_default2(pi) {
205704
205838
  historianFallbacks: historianConfig?.fallbackModels,
205705
205839
  historianTimeoutMs: config2.historian_timeout_ms,
205706
205840
  historianThinkingLevel: historianConfig?.thinkingLevel,
205841
+ language: config2.language,
205707
205842
  memoryEnabled: config2.memory.enabled,
205708
205843
  autoPromote: config2.memory.auto_promote,
205709
205844
  userMemoriesEnabled: userMemoryCollectionEnabled(config2.dreamer)
@@ -205735,6 +205870,7 @@ async function src_default2(pi) {
205735
205870
  config: dreamerConfig,
205736
205871
  embeddingConfig: config2.embedding,
205737
205872
  memoryEnabled: config2.memory.enabled,
205873
+ language: config2.language,
205738
205874
  gitCommitIndexing: config2.memory.git_commit_indexing,
205739
205875
  onAdjunctsRefreshNeeded: signalPiSystemPromptRefreshForProject
205740
205876
  });
@@ -205849,6 +205985,7 @@ async function src_default2(pi) {
205849
205985
  dreamerEnabled: effectiveDreamerRunnable,
205850
205986
  temporalAwarenessEnabled: effectiveConfig.temporal_awareness ?? false,
205851
205987
  cavemanTextCompressionEnabled: effectiveConfig.ctx_reduce_enabled === false && effectiveConfig.caveman_text_compression?.enabled === true,
205988
+ language: effectiveConfig.language,
205852
205989
  userMemoriesEnabled: userMemoryCollectionEnabled(effectiveConfig.dreamer),
205853
205990
  isCacheBusting,
205854
205991
  existingSystemPrompt: event.systemPrompt
@@ -144835,6 +144835,10 @@ function stripUnsafeProjectConfigFields(projectRaw) {
144835
144835
  delete projectRaw.auto_update;
144836
144836
  warnings.push("Ignoring auto_update from project config (security: this setting only honors user-level config).");
144837
144837
  }
144838
+ if ("language" in projectRaw) {
144839
+ delete projectRaw.language;
144840
+ warnings.push("Ignoring language from project config (security: output language is a user-level setting).");
144841
+ }
144838
144842
  if ("sqlite" in projectRaw) {
144839
144843
  delete projectRaw.sqlite;
144840
144844
  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).");
@@ -159180,6 +159184,95 @@ function date4(params) {
159180
159184
 
159181
159185
  // ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
159182
159186
  config(en_default());
159187
+ // ../plugin/src/agents/language-directive.ts
159188
+ var ENGLISH_LANGUAGE_NAMES = new Intl.DisplayNames(["en"], {
159189
+ type: "language",
159190
+ fallback: "none"
159191
+ });
159192
+ function resolveLanguageName(language) {
159193
+ const code = typeof language === "string" ? language.trim().toLowerCase() : "";
159194
+ if (!/^[a-z]{2}$/.test(code))
159195
+ return "";
159196
+ let english;
159197
+ try {
159198
+ english = ENGLISH_LANGUAGE_NAMES.of(code) ?? undefined;
159199
+ } catch {
159200
+ return "";
159201
+ }
159202
+ if (!english)
159203
+ return "";
159204
+ let endonym;
159205
+ try {
159206
+ endonym = new Intl.DisplayNames([code], { type: "language", fallback: "none" }).of(code) ?? undefined;
159207
+ } catch {
159208
+ endonym = undefined;
159209
+ }
159210
+ return endonym && endonym !== english ? `${english} (${endonym})` : english;
159211
+ }
159212
+ function isValidLanguageCode(language) {
159213
+ return resolveLanguageName(language) !== "";
159214
+ }
159215
+ function buildContentLanguageDirective(language, options = {}) {
159216
+ const target = resolveLanguageName(language);
159217
+ if (!target)
159218
+ return "";
159219
+ const lines = [
159220
+ "## Output language",
159221
+ "",
159222
+ `Write human-readable prose you author in: ${target}.`,
159223
+ "",
159224
+ "Do not translate or rename structural tokens. Copy required output schemas exactly:",
159225
+ "- 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.",
159226
+ "- 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.",
159227
+ "- Localize only free-text prose values/content: summaries, memory text, explanations, titles, observations, and answers — unless the prompt says to preserve original wording.",
159228
+ "",
159229
+ "These literal values must remain English when used:",
159230
+ "PROJECT_RULES, ARCHITECTURE, CONSTRAINTS, CONFIG_VALUES, NAMING;",
159231
+ "causal_incident, trajectory_correction;",
159232
+ "feature, design, docs, release, investigation, bug, refactor, infra;",
159233
+ "memory, observation; true, false; No relevant memories found.",
159234
+ "",
159235
+ "Preserve the required output shape. Do not add commentary outside the requested XML/JSON/tool output."
159236
+ ];
159237
+ if (options.preserveUserQuotes) {
159238
+ lines.push("", `Preserve U: lines and directly quoted user text in their original source language; write the surrounding summary prose in ${target}.`);
159239
+ }
159240
+ if (options.retrospective) {
159241
+ lines.push("", `Write the lesson text in ${target}; paraphrase source text and never quote the user.`);
159242
+ }
159243
+ return lines.join(`
159244
+ `);
159245
+ }
159246
+ function withContentLanguageDirective(systemPrompt, language, options = {}) {
159247
+ const directive = buildContentLanguageDirective(language, options);
159248
+ return directive ? `${systemPrompt}
159249
+
159250
+ ${directive}` : systemPrompt;
159251
+ }
159252
+ function buildMigrationLanguageDirective(language) {
159253
+ const target = resolveLanguageName(language);
159254
+ if (!target)
159255
+ return "";
159256
+ return [
159257
+ "## Output language",
159258
+ "",
159259
+ "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."
159260
+ ].join(`
159261
+ `);
159262
+ }
159263
+ function withMigrationLanguageDirective(systemPrompt, language) {
159264
+ const directive = buildMigrationLanguageDirective(language);
159265
+ return directive ? `${systemPrompt}
159266
+
159267
+ ${directive}` : systemPrompt;
159268
+ }
159269
+ function buildPrimaryLanguageDirective(language) {
159270
+ const target = resolveLanguageName(language);
159271
+ if (!target)
159272
+ return "";
159273
+ 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.`;
159274
+ }
159275
+
159183
159276
  // ../plugin/src/features/magic-context/defaults.ts
159184
159277
  var DEFAULT_PROTECTED_TAGS = 20;
159185
159278
 
@@ -159536,6 +159629,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
159536
159629
  var MagicContextConfigSchema = exports_external.object({
159537
159630
  enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
159538
159631
  auto_update: exports_external.boolean().optional().describe("Enable automatic npm self-update checks for the OpenCode plugin. Security: USER-only in config loader, so hostile project configs cannot suppress updates."),
159632
+ 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."),
159539
159633
  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)"),
159540
159634
  historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
159541
159635
  dreamer: DreamerConfigSchema.optional().describe("Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)"),
@@ -171478,6 +171572,9 @@ function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
171478
171572
  nullCount: row?.null_count ?? 0
171479
171573
  };
171480
171574
  }
171575
+ var RECLAIM_HINT_EXCLUDED_TOOLS = ["todowrite"];
171576
+ var RECLAIM_HINT_MIN_TOKENS = 250;
171577
+ var RECLAIM_HINT_EXCLUDED_LIST = RECLAIM_HINT_EXCLUDED_TOOLS.map((name2) => `'${name2.replace(/'/g, "''")}'`).join(", ");
171481
171578
  function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, limit = 4) {
171482
171579
  if (limit <= 0)
171483
171580
  return [];
@@ -171487,10 +171584,18 @@ function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, li
171487
171584
  WHERE session_id = ? AND status = 'active'
171488
171585
  ORDER BY tag_number DESC LIMIT 1 OFFSET ?
171489
171586
  )` : "";
171490
- const params = protectedTags > 0 ? [sessionId, sessionId, protectedTags - 1, boundedLimit] : [sessionId, boundedLimit];
171587
+ const excludeStateTools = RECLAIM_HINT_EXCLUDED_LIST ? `AND (tool_name IS NULL OR tool_name NOT IN (${RECLAIM_HINT_EXCLUDED_LIST}))` : "";
171588
+ const valueFloor = `AND (
171589
+ (token_count IS NULL AND input_token_count IS NULL)
171590
+ OR (COALESCE(token_count, 0) + COALESCE(input_token_count, 0)) >= ?
171591
+ )`;
171592
+ const params = protectedTags > 0 ? [sessionId, RECLAIM_HINT_MIN_TOKENS, sessionId, protectedTags - 1, boundedLimit] : [sessionId, RECLAIM_HINT_MIN_TOKENS, boundedLimit];
171491
171593
  const rows = db.prepare(`SELECT tag_number, tool_name
171492
171594
  FROM tags
171493
- WHERE session_id = ? AND status = 'active' AND type = 'tool' ${whereProtected}
171595
+ WHERE session_id = ? AND status = 'active' AND type = 'tool'
171596
+ ${excludeStateTools}
171597
+ ${valueFloor}
171598
+ ${whereProtected}
171494
171599
  ORDER BY tag_number ASC, id ASC
171495
171600
  LIMIT ?`).all(...params);
171496
171601
  return rows.filter((row) => typeof row.tag_number === "number").map((row) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/pi-magic-context",
3
- "version": "0.27.3",
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",