@cortexkit/pi-magic-context 0.27.3 → 0.29.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
  };
@@ -15905,6 +15906,17 @@ function getRawSessionMessageCountFromDb(db, sessionId) {
15905
15906
  return typeof row?.count === "number" ? row.count : 0;
15906
15907
  }
15907
15908
 
15909
+ // ../plugin/src/shared/commit-detection.ts
15910
+ var HASH_HEX = "[0-9a-f]{7,12}";
15911
+ var COMMIT_HASH_TEST_PATTERN = new RegExp(`\\b${HASH_HEX}\\b`, "i");
15912
+ var COMMIT_VERB_PATTERN = /\b(?:commit(?:ted|ting|s)?|cherry-?pick(?:ed|ing|s)?|merge[ds]?|merging|rebas(?:e|ed|es|ing))\b/i;
15913
+ function textMentionsRecentCommit(text) {
15914
+ return COMMIT_HASH_TEST_PATTERN.test(text) && COMMIT_VERB_PATTERN.test(text);
15915
+ }
15916
+ function createCommitHashExtractPattern() {
15917
+ return new RegExp(`\`?\\b(${HASH_HEX})\\b\`?`, "gi");
15918
+ }
15919
+
15908
15920
  // ../../node_modules/.bun/ai-tokenizer@1.0.6/node_modules/ai-tokenizer/dist/index.js
15909
15921
  function _typeof(o) {
15910
15922
  "@babel/helpers - typeof";
@@ -148344,8 +148356,6 @@ var decoder = {
148344
148356
  };
148345
148357
 
148346
148358
  // ../plugin/src/hooks/magic-context/read-session-formatting.ts
148347
- var COMMIT_HASH_PATTERN = /`?\b([0-9a-f]{6,12})\b`?/gi;
148348
- var COMMIT_HINT_PATTERN = /\b(commit(?:ted)?|cherry-?pick(?:ed)?|hash(?:es)?|sha)\b/i;
148349
148359
  var MAX_COMMITS_PER_BLOCK = 5;
148350
148360
  function hasMeaningfulUserText(parts) {
148351
148361
  for (const part of parts) {
@@ -148449,7 +148459,7 @@ function formatBlock(block) {
148449
148459
  function extractCommitHashes(text) {
148450
148460
  const hashes = [];
148451
148461
  const seen = new Set;
148452
- for (const match of text.matchAll(COMMIT_HASH_PATTERN)) {
148462
+ for (const match of text.matchAll(createCommitHashExtractPattern())) {
148453
148463
  const hash = match[1]?.toLowerCase();
148454
148464
  if (!hash || seen.has(hash))
148455
148465
  continue;
@@ -148462,10 +148472,10 @@ function extractCommitHashes(text) {
148462
148472
  }
148463
148473
  function compactTextForSummary(text, role) {
148464
148474
  const commitHashes = role === "assistant" ? extractCommitHashes(text) : [];
148465
- if (commitHashes.length === 0 || !COMMIT_HINT_PATTERN.test(text)) {
148475
+ if (commitHashes.length === 0 || !COMMIT_VERB_PATTERN.test(text)) {
148466
148476
  return { text, commitHashes };
148467
148477
  }
148468
- const withoutHashes = text.replace(COMMIT_HASH_PATTERN, "").replace(/\(\s*\)/g, "").replace(/\s+,/g, ",").replace(/,\s*,+/g, ", ").replace(/\s{2,}/g, " ").replace(/\s+([,.;:])/g, "$1").trim();
148478
+ const withoutHashes = text.replace(createCommitHashExtractPattern(), "").replace(/\(\s*\)/g, "").replace(/\s+,/g, ",").replace(/,\s*,+/g, ", ").replace(/\s{2,}/g, " ").replace(/\s+([,.;:])/g, "$1").trim();
148469
148479
  return {
148470
148480
  text: withoutHashes.length > 0 ? withoutHashes : text,
148471
148481
  commitHashes
@@ -148687,6 +148697,36 @@ function isRecord(value) {
148687
148697
  return typeof value === "object" && value !== null && !Array.isArray(value);
148688
148698
  }
148689
148699
 
148700
+ // ../plugin/src/hooks/magic-context/edit-marker.ts
148701
+ var TRUNCATION_SENTINEL = "...[truncated]";
148702
+ var EDIT_REGION_HINT_LEN = 40;
148703
+ var PATH_KEYS = new Set(["filePath", "file_path", "path"]);
148704
+ var DIFF_KEYS = new Set(["oldString", "newString", "content", "old_string", "new_string"]);
148705
+ function safeSlice(str, maxLen) {
148706
+ if (str.length <= maxLen)
148707
+ return str;
148708
+ const lastCharCode = str.charCodeAt(maxLen - 1);
148709
+ if (lastCharCode >= 55296 && lastCharCode <= 56319) {
148710
+ return str.slice(0, maxLen - 1);
148711
+ }
148712
+ return str.slice(0, maxLen);
148713
+ }
148714
+ function isEditTool(name2) {
148715
+ return name2 === "edit" || name2 === "write";
148716
+ }
148717
+ function applyEditMarkerToInput(input) {
148718
+ for (const key of Object.keys(input)) {
148719
+ if (PATH_KEYS.has(key))
148720
+ continue;
148721
+ const value = input[key];
148722
+ if (typeof value !== "string" || !DIFF_KEYS.has(key))
148723
+ continue;
148724
+ if (value.endsWith(TRUNCATION_SENTINEL))
148725
+ continue;
148726
+ input[key] = value.length > EDIT_REGION_HINT_LEN ? `${safeSlice(value, EDIT_REGION_HINT_LEN)}${TRUNCATION_SENTINEL}` : value;
148727
+ }
148728
+ }
148729
+
148690
148730
  // ../plugin/src/hooks/magic-context/tool-drop-target.ts
148691
148731
  var IGNORE_PART_TYPES = new Set([
148692
148732
  "thinking",
@@ -155056,6 +155096,9 @@ function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
155056
155096
  nullCount: row?.null_count ?? 0
155057
155097
  };
155058
155098
  }
155099
+ var RECLAIM_HINT_EXCLUDED_TOOLS = ["todowrite"];
155100
+ var RECLAIM_HINT_MIN_TOKENS = 250;
155101
+ var RECLAIM_HINT_EXCLUDED_LIST = RECLAIM_HINT_EXCLUDED_TOOLS.map((name2) => `'${name2.replace(/'/g, "''")}'`).join(", ");
155059
155102
  function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, limit = 4) {
155060
155103
  if (limit <= 0)
155061
155104
  return [];
@@ -155065,10 +155108,18 @@ function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, li
155065
155108
  WHERE session_id = ? AND status = 'active'
155066
155109
  ORDER BY tag_number DESC LIMIT 1 OFFSET ?
155067
155110
  )` : "";
155068
- const params = protectedTags > 0 ? [sessionId, sessionId, protectedTags - 1, boundedLimit] : [sessionId, boundedLimit];
155111
+ const excludeStateTools = RECLAIM_HINT_EXCLUDED_LIST ? `AND (tool_name IS NULL OR tool_name NOT IN (${RECLAIM_HINT_EXCLUDED_LIST}))` : "";
155112
+ const valueFloor = `AND (
155113
+ (token_count IS NULL AND input_token_count IS NULL)
155114
+ OR (COALESCE(token_count, 0) + COALESCE(input_token_count, 0)) >= ?
155115
+ )`;
155116
+ const params = protectedTags > 0 ? [sessionId, RECLAIM_HINT_MIN_TOKENS, sessionId, protectedTags - 1, boundedLimit] : [sessionId, RECLAIM_HINT_MIN_TOKENS, boundedLimit];
155069
155117
  const rows = db.prepare(`SELECT tag_number, tool_name
155070
155118
  FROM tags
155071
- WHERE session_id = ? AND status = 'active' AND type = 'tool' ${whereProtected}
155119
+ WHERE session_id = ? AND status = 'active' AND type = 'tool'
155120
+ ${excludeStateTools}
155121
+ ${valueFloor}
155122
+ ${whereProtected}
155072
155123
  ORDER BY tag_number ASC, id ASC
155073
155124
  LIMIT ?`).all(...params);
155074
155125
  return rows.filter((row) => typeof row.tag_number === "number").map((row) => ({
@@ -155179,7 +155230,7 @@ function toTagEntry(row) {
155179
155230
  messageId: row.message_id,
155180
155231
  type,
155181
155232
  status,
155182
- dropMode: row.drop_mode === "truncated" ? "truncated" : "full",
155233
+ dropMode: row.drop_mode === "truncated" ? "truncated" : row.drop_mode === "edit_marker" ? "edit_marker" : "full",
155183
155234
  toolName: row.tool_name ?? null,
155184
155235
  inputByteSize: row.input_byte_size ?? 0,
155185
155236
  byteSize: row.byte_size,
@@ -156335,13 +156386,10 @@ function compareSemverCore(a, b) {
156335
156386
 
156336
156387
  // ../plugin/src/shared/announcement.ts
156337
156388
  init_data_path();
156338
- var ANNOUNCEMENT_VERSION = "0.27.0";
156389
+ var ANNOUNCEMENT_VERSION = "0.29.0";
156339
156390
  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."
156391
+ `New 'smart_drops' option (opt-in, off by default): on long, edit-heavy sessions it reclaims more context by dropping tool output a later call made obsolete (superseded edits, old todowrite/ctx_reduce, spent status calls) instead of only the oldest. When off, the prompt is byte-identical to before. Enable with "smart_drops": true.`,
156392
+ "When memory is disabled, the ctx_memory tool and its guidance are no longer shown (they had nothing to write to). ctx_search stays, searching conversation history and git commits."
156345
156393
  ];
156346
156394
  var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU";
156347
156395
  var STATE_FILENAME = "last_announced_version";
@@ -156403,6 +156451,95 @@ function shouldKeepSubagents() {
156403
156451
  // src/index.ts
156404
156452
  init_logger();
156405
156453
 
156454
+ // ../plugin/src/agents/language-directive.ts
156455
+ var ENGLISH_LANGUAGE_NAMES = new Intl.DisplayNames(["en"], {
156456
+ type: "language",
156457
+ fallback: "none"
156458
+ });
156459
+ function resolveLanguageName(language) {
156460
+ const code = typeof language === "string" ? language.trim().toLowerCase() : "";
156461
+ if (!/^[a-z]{2}$/.test(code))
156462
+ return "";
156463
+ let english;
156464
+ try {
156465
+ english = ENGLISH_LANGUAGE_NAMES.of(code) ?? undefined;
156466
+ } catch {
156467
+ return "";
156468
+ }
156469
+ if (!english)
156470
+ return "";
156471
+ let endonym;
156472
+ try {
156473
+ endonym = new Intl.DisplayNames([code], { type: "language", fallback: "none" }).of(code) ?? undefined;
156474
+ } catch {
156475
+ endonym = undefined;
156476
+ }
156477
+ return endonym && endonym !== english ? `${english} (${endonym})` : english;
156478
+ }
156479
+ function isValidLanguageCode(language) {
156480
+ return resolveLanguageName(language) !== "";
156481
+ }
156482
+ function buildContentLanguageDirective(language, options = {}) {
156483
+ const target = resolveLanguageName(language);
156484
+ if (!target)
156485
+ return "";
156486
+ const lines = [
156487
+ "## Output language",
156488
+ "",
156489
+ `Write human-readable prose you author in: ${target}.`,
156490
+ "",
156491
+ "Do not translate or rename structural tokens. Copy required output schemas exactly:",
156492
+ "- 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.",
156493
+ "- 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.",
156494
+ "- Localize only free-text prose values/content: summaries, memory text, explanations, titles, observations, and answers — unless the prompt says to preserve original wording.",
156495
+ "",
156496
+ "These literal values must remain English when used:",
156497
+ "PROJECT_RULES, ARCHITECTURE, CONSTRAINTS, CONFIG_VALUES, NAMING;",
156498
+ "causal_incident, trajectory_correction;",
156499
+ "feature, design, docs, release, investigation, bug, refactor, infra;",
156500
+ "memory, observation; true, false; No relevant memories found.",
156501
+ "",
156502
+ "Preserve the required output shape. Do not add commentary outside the requested XML/JSON/tool output."
156503
+ ];
156504
+ if (options.preserveUserQuotes) {
156505
+ lines.push("", `Preserve U: lines and directly quoted user text in their original source language; write the surrounding summary prose in ${target}.`);
156506
+ }
156507
+ if (options.retrospective) {
156508
+ lines.push("", `Write the lesson text in ${target}; paraphrase source text and never quote the user.`);
156509
+ }
156510
+ return lines.join(`
156511
+ `);
156512
+ }
156513
+ function withContentLanguageDirective(systemPrompt, language, options = {}) {
156514
+ const directive = buildContentLanguageDirective(language, options);
156515
+ return directive ? `${systemPrompt}
156516
+
156517
+ ${directive}` : systemPrompt;
156518
+ }
156519
+ function buildMigrationLanguageDirective(language) {
156520
+ const target = resolveLanguageName(language);
156521
+ if (!target)
156522
+ return "";
156523
+ return [
156524
+ "## Output language",
156525
+ "",
156526
+ "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."
156527
+ ].join(`
156528
+ `);
156529
+ }
156530
+ function withMigrationLanguageDirective(systemPrompt, language) {
156531
+ const directive = buildMigrationLanguageDirective(language);
156532
+ return directive ? `${systemPrompt}
156533
+
156534
+ ${directive}` : systemPrompt;
156535
+ }
156536
+ function buildPrimaryLanguageDirective(language) {
156537
+ const target = resolveLanguageName(language);
156538
+ if (!target)
156539
+ return "";
156540
+ 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.`;
156541
+ }
156542
+
156406
156543
  // ../plugin/src/features/magic-context/sidekick/core.ts
156407
156544
  var SIDEKICK_SYSTEM_PROMPT = `You are Sidekick, a focused memory-retrieval subagent for an AI coding assistant.
156408
156545
 
@@ -157123,7 +157260,7 @@ function registerCtxAugCommand(pi, config) {
157123
157260
  sessionLog(sessionLabel, "/ctx-aug: project identity", projectIdentity);
157124
157261
  const result = await runner.run({
157125
157262
  agent: "sidekick",
157126
- systemPrompt: config.systemPrompt ?? SIDEKICK_SYSTEM_PROMPT,
157263
+ systemPrompt: withContentLanguageDirective(config.systemPrompt ?? SIDEKICK_SYSTEM_PROMPT, config.language),
157127
157264
  userMessage: prompt,
157128
157265
  model: config.model,
157129
157266
  fallbackModels: config.fallbackModels,
@@ -172319,6 +172456,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
172319
172456
  var MagicContextConfigSchema = exports_external.object({
172320
172457
  enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
172321
172458
  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."),
172459
+ 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
172460
  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
172461
  historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
172324
172462
  dreamer: DreamerConfigSchema.optional().describe("Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)"),
@@ -172356,6 +172494,7 @@ var MagicContextConfigSchema = exports_external.object({
172356
172494
  }).describe("Embedding provider configuration"),
172357
172495
  temporal_awareness: exports_external.boolean().default(true).describe('Inject wall-clock gap markers (<!-- +Xm -->) between user messages where > 5 min elapsed since the previous message, and add start/end date attributes on compartments. Gives the agent a sense of session pacing and "how long ago" across multi-day sessions. Graduated from experimental.temporal_awareness; default: true (set false to opt out).'),
172358
172496
  keep_subagents: exports_external.boolean().default(false).describe("Debug: keep the child sessions Magic Context spawns for its own subagents (historian, dreamer, sidekick, memory-migration) instead of deleting them on success. Useful for short-term inspection/data collection — their full transcript (prompt, tool calls, token usage, output) stays in the host session store. Kept sessions accumulate until manually cleared; leave false for normal use. Requires a restart to take effect."),
172497
+ smart_drops: exports_external.boolean().default(false).describe("Content-aware reclaim of provably-superseded tool output, layered on the existing execute-pass auto-drop. When on: superseded todowrite (keep newest 1), spent ctx_reduce (keep newest 5), and zero-value meta (bash_status, bash_kill, ctx_note read/dismiss) outputs are dropped; older edits to a file are compressed to a filePath-preserving marker while the newest edit per file stays full. Only acts on passes already busting the cache, so it never originates a cache bust. Honors the protected-tag reserve. Experimental: opt-in, default off until cache stability is proven; when off the wire is byte-identical to the positional-only reclaim. Requires a restart."),
172359
172498
  caveman_text_compression: exports_external.object({
172360
172499
  enabled: exports_external.boolean().default(false).describe("Apply deterministic caveman-style text compression to old conversation text. Only active when ctx_reduce_enabled=false. Compresses user/assistant text in oldest-first tiers: ultra (oldest 20%), full, lite, untouched (newest 40%)."),
172361
172500
  min_chars: exports_external.number().min(100).max(1e4).default(500).describe("Text parts shorter than this (characters) stay untouched. Min 100, max 10000. Default: 500.")
@@ -176175,6 +176314,7 @@ var MAINTAIN_DOCS_SYSTEM_PROMPT = `You are a documentation maintainer for the ma
176175
176314
 
176176
176315
  ## Rules
176177
176316
  - **NEVER touch protected regions.** Any content between \`<!-- mc:protected START ... -->\` and \`<!-- mc:protected END -->\` is hand-authored and cache-critical. Reproduce it BYTE-FOR-BYTE — do not edit, reword, reorder, summarize, trim, or drop a single line, and keep the marker comments. Only a human edits that region.
176317
+ - **Preserve an existing doc's structure, voice, and density.** When a doc already exists, it is the source of truth for shape: keep its headings, ordering, level of detail, and writing style. Make the SMALLEST edits that bring it back in sync with the code. NEVER reshape hand-written prose into a generic template, collapse a dense section into bullet stubs, or drop hard-won detail (specific invariants, edge cases, mechanism descriptions) because it does not fit a standard layout. A doc denser and more specific than a template is BETTER, not worse: leave it that way.
176178
176318
  - **Be prescriptive** ("Use X pattern", not "X pattern is used"). **Current state only** — no temporal language, no history.
176179
176319
  - **Verify before writing** — read the actual files, never guess. All file paths in the docs must point to files that exist.`;
176180
176320
  var REVIEW_USER_MEMORIES_SYSTEM_PROMPT = `You are a user-profile reviewer for the magic-context system. You run during a scheduled dream window to decide which recurring behavioral observations about the human user are real, persistent patterns worth keeping in their global user profile.
@@ -176290,7 +176430,7 @@ Return only XML in this exact shape:
176290
176430
  function buildMaintainDocsPrompt(projectPath, lastDreamAt, existingDocs) {
176291
176431
  const hasAny = existingDocs.architecture || existingDocs.structure;
176292
176432
  const gitSinceClause = lastDreamAt ? `Run \`git log --oneline --since="${new Date(Number(lastDreamAt)).toISOString()}"\` to see what changed since the last dream.` : "No previous dream timestamp — treat this as a full analysis.";
176293
- const modeIntro = hasAny ? `Some docs already exist. Update only the sections affected by recent changes. Do NOT rewrite unchanged sections.` : `No docs exist yet. Create both ARCHITECTURE.md and STRUCTURE.md from scratch using the templates below.`;
176433
+ const modeIntro = hasAny ? `Some docs already exist and are the source of truth for shape. Make SURGICAL \`edit\` changes to only the sections affected by recent code changes; preserve every other section, the existing structure, and the existing density verbatim. Do NOT regenerate a whole file, do NOT reshape prose into a template, and do NOT use the templates below (they are for creation only). If nothing material changed, change nothing.` : `No docs exist yet. Create both ARCHITECTURE.md and STRUCTURE.md from scratch using the templates below as a STARTING shape, then go deeper than the template wherever the code warrants it.`;
176294
176434
  return `## Task: Maintain Codebase Documentation
176295
176435
 
176296
176436
  **Project:** ${projectPath}
@@ -176305,15 +176445,16 @@ ${modeIntro}
176305
176445
  ### Process
176306
176446
 
176307
176447
  1. **Check what changed.** ${gitSinceClause}
176308
- 2. **Read existing docs** (if they exist) to understand current state.
176448
+ 2. **Read existing docs** (if they exist) IN FULL to understand their current structure, depth, and voice; you will preserve all of it except what code changes force you to touch.
176309
176449
  3. **Explore the codebase** to verify and update:
176310
176450
  - Directory structure: \`find . -type d -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' | head -60\`
176311
176451
  - Entry points: \`ls src/index.* src/main.* 2>/dev/null\`
176312
176452
  - Key imports: \`grep -r "^import\\|^export" src/ --include="*.ts" | head -80\`
176313
- 4. **Write or update** using the Write tool. Always write to project root, NOT to .planning/.
176453
+ 4. **Apply the change.** If the doc EXISTS: use \`edit\` for the specific sections that drifted, never rewrite the whole file with \`write\`. If the doc is MISSING: create it with \`write\`. Always at project root, NOT \`.planning/\`.
176314
176454
 
176315
176455
  ### Rules
176316
176456
  - **NEVER touch protected regions**: any content between \`<!-- mc:protected START ... -->\` and \`<!-- mc:protected END -->\` is hand-authored and cache-critical. Reproduce it BYTE-FOR-BYTE in your rewrite — do not edit, reword, reorder, summarize, trim, or drop a single line of it, and keep the marker comments themselves. Only a human edits that region.
176457
+ - **Preserve existing structure and density**: when a doc exists, keep its headings, ordering, level of detail, and voice. Make the smallest edits that re-sync it with the code. NEVER flatten dense hand-written prose into the generic template, collapse a detailed section into bullet stubs, or drop specific invariants/edge-cases/mechanism detail because it does not match a standard layout. Denser and more specific than the template is BETTER.
176317
176458
  - **Be prescriptive**: "Use X pattern" not "X pattern is used"
176318
176459
  - **Always include file paths** in backticks
176319
176460
  - **Write current state only**: no temporal language, no history
@@ -176616,7 +176757,7 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
176616
176757
  query: { directory: args.sessionDirectory },
176617
176758
  body: {
176618
176759
  agent: DREAMER_REVIEWER_AGENT,
176619
- system: REVIEW_USER_MEMORIES_SYSTEM_PROMPT,
176760
+ system: withContentLanguageDirective(REVIEW_USER_MEMORIES_SYSTEM_PROMPT, args.language),
176620
176761
  ...modelBodyField(args.model),
176621
176762
  parts: [{ type: "text", text: prompt, synthetic: true }]
176622
176763
  }
@@ -177718,10 +177859,23 @@ function getAsyncModule() {
177718
177859
  asyncModulePromise ??= newQuickJSAsyncWASMModuleFromVariant(import_quickjs_singlefile_cjs_release_asyncify.default);
177719
177860
  return asyncModulePromise;
177720
177861
  }
177862
+ var sandboxRunChain = Promise.resolve();
177863
+ function withSandboxLock(fn) {
177864
+ const run = sandboxRunChain.then(fn, fn);
177865
+ sandboxRunChain = run.then(() => {
177866
+ return;
177867
+ }, () => {
177868
+ return;
177869
+ });
177870
+ return run;
177871
+ }
177721
177872
  var DEFAULT_TIMEOUT_MS = 2000;
177722
177873
  var DEFAULT_HEAP_LIMIT_BYTES = 8 * 1024 * 1024;
177723
177874
  var DEFAULT_STACK_LIMIT_BYTES = 512 * 1024;
177724
177875
  async function runCompiledSmartNoteCheck(options) {
177876
+ return withSandboxLock(() => runCompiledSmartNoteCheckLocked(options));
177877
+ }
177878
+ async function runCompiledSmartNoteCheckLocked(options) {
177725
177879
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
177726
177880
  const controller = new AbortController;
177727
177881
  const externalAbort = () => controller.abort(options.signal?.reason);
@@ -179430,7 +179584,7 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
179430
179584
  query: { directory: args.sessionDirectory },
179431
179585
  body: {
179432
179586
  agent: DREAMER_PRIMER_INVESTIGATOR_AGENT,
179433
- system: PRIMER_INVESTIGATOR_SYSTEM_PROMPT,
179587
+ system: withContentLanguageDirective(PRIMER_INVESTIGATOR_SYSTEM_PROMPT, args.language),
179434
179588
  ...modelBodyField(args.model),
179435
179589
  parts: [{ type: "text", text: prompt, synthetic: true }]
179436
179590
  }
@@ -180211,7 +180365,7 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
180211
180365
  query: { directory: args.sessionDirectory },
180212
180366
  body: {
180213
180367
  agent: DREAMER_MEMORY_MAPPER_AGENT,
180214
- system: VERIFY_SYSTEM_PROMPT,
180368
+ system: withContentLanguageDirective(VERIFY_SYSTEM_PROMPT, args.language),
180215
180369
  ...modelBodyField(args.model),
180216
180370
  parts: [{ type: "text", text: prompt, synthetic: true }]
180217
180371
  }
@@ -180491,7 +180645,8 @@ function createDreamTaskExecutor(deps) {
180491
180645
  deadline,
180492
180646
  promotionThreshold: config2.promotionThreshold ?? 3,
180493
180647
  model: config2.model,
180494
- fallbackModels: config2.fallbackModels
180648
+ fallbackModels: config2.fallbackModels,
180649
+ language: config2.language ?? deps.language
180495
180650
  });
180496
180651
  recordRun("completed", null);
180497
180652
  log(`[dreamer] review-user-memories: promoted=${result.promoted} merged=${result.merged} dismissed=${result.dismissed}`);
@@ -180527,7 +180682,8 @@ function createDreamTaskExecutor(deps) {
180527
180682
  deadline,
180528
180683
  forceBroad: config2.task === "verify-broad",
180529
180684
  model: config2.model,
180530
- fallbackModels: config2.fallbackModels
180685
+ fallbackModels: config2.fallbackModels,
180686
+ language: config2.language ?? deps.language
180531
180687
  });
180532
180688
  recordRun("completed", null, {
180533
180689
  memoryChanges: computeMemoryDelta(memoryBefore)
@@ -180579,6 +180735,7 @@ function createDreamTaskExecutor(deps) {
180579
180735
  deadline,
180580
180736
  model: config2.model,
180581
180737
  fallbackModels: config2.fallbackModels,
180738
+ language: config2.language ?? deps.language,
180582
180739
  rawProviderFactory: deps.primerRawProviderFactory
180583
180740
  });
180584
180741
  recordRun("completed", null);
@@ -180696,6 +180853,7 @@ function retrospectiveEventsForSessions(db, sessionIds) {
180696
180853
  try {
180697
180854
  for (const event of getCompartmentEvents(db, sessionId)) {
180698
180855
  if (event.kind !== "causal_incident" && event.kind !== "trajectory_correction") {
180856
+ log(`[dreamer] dropping event: unknown kind="${event.kind}"`);
180699
180857
  continue;
180700
180858
  }
180701
180859
  events.push({
@@ -180820,7 +180978,9 @@ async function runRetrospectiveTask(config2, ctx, helpers) {
180820
180978
  const frictionWindow = renderFrictionWindow(messages, flagged.map((message) => message.ordinal));
180821
180979
  const eventSessionIds = new Set(messages.map((message) => message.sessionId));
180822
180980
  const events = retrospectiveEventsForSessions(db, eventSessionIds);
180823
- const deepenRun = await runChildTurn(RETROSPECTIVE_SYSTEM_PROMPT, buildRetrospectivePrompt({ projectPath: projectIdentity, frictionWindow, events }));
180981
+ const deepenRun = await runChildTurn(withContentLanguageDirective(RETROSPECTIVE_SYSTEM_PROMPT, config2.language ?? deps.language, {
180982
+ retrospective: true
180983
+ }), buildRetrospectivePrompt({ projectPath: projectIdentity, frictionWindow, events }));
180824
180984
  if (leaseLost)
180825
180985
  throw new Error("Dream lease lost during retrospective");
180826
180986
  const sourceSessionId = flagged[0]?.sessionId ?? userMessages[0]?.sessionId ?? "retrospective";
@@ -180906,7 +181066,7 @@ async function runAgenticTask(config2, ctx, helpers) {
180906
181066
  query: { directory: docsDir },
180907
181067
  body: {
180908
181068
  agent: task === "maintain-docs" ? DREAMER_DOCS_AGENT : DREAMER_AGENT,
180909
- system: task === "maintain-docs" ? MAINTAIN_DOCS_SYSTEM_PROMPT : CURATE_SYSTEM_PROMPT,
181069
+ system: task === "maintain-docs" ? MAINTAIN_DOCS_SYSTEM_PROMPT : withContentLanguageDirective(CURATE_SYSTEM_PROMPT, config2.language ?? deps.language),
180910
181070
  ...modelBodyField(config2.model),
180911
181071
  parts: [{ type: "text", text: taskPrompt, synthetic: true }]
180912
181072
  }
@@ -181788,7 +181948,7 @@ async function sweepProject(reg, origin, db, gitCommitEnabled) {
181788
181948
  }
181789
181949
  try {
181790
181950
  await runCompiledSmartNoteSweep(reg, db);
181791
- const runtimeConfigs = buildDreamTaskRuntimeConfigs(dreamerConfig);
181951
+ const runtimeConfigs = buildDreamTaskRuntimeConfigs(dreamerConfig, reg.language);
181792
181952
  const executor = createDreamTaskExecutor({
181793
181953
  client: reg.client,
181794
181954
  sessionDirectory: reg.directory,
@@ -181796,7 +181956,8 @@ async function sweepProject(reg, origin, db, gitCommitEnabled) {
181796
181956
  retrospectiveRawProvider: reg.retrospectiveRawProvider ?? ((db2) => new OpenCodeRetrospectiveRawProvider({ contextDb: db2, openOpenCodeDb })),
181797
181957
  primerRawProviderFactory: reg.primerRawProviderFactory,
181798
181958
  userMemoryCollectionEnabled: userMemoryCollectionEnabled(dreamerConfig),
181799
- ensureProjectRegistered: reg.ensureRegistered
181959
+ ensureProjectRegistered: reg.ensureRegistered,
181960
+ language: reg.language
181800
181961
  });
181801
181962
  const ran = await runDueTasksForProject({
181802
181963
  db,
@@ -182349,6 +182510,10 @@ function stripUnsafeProjectConfigFields(projectRaw) {
182349
182510
  delete projectRaw.auto_update;
182350
182511
  warnings.push("Ignoring auto_update from project config (security: this setting only honors user-level config).");
182351
182512
  }
182513
+ if ("language" in projectRaw) {
182514
+ delete projectRaw.language;
182515
+ warnings.push("Ignoring language from project config (security: output language is a user-level setting).");
182516
+ }
182352
182517
  if ("sqlite" in projectRaw) {
182353
182518
  delete projectRaw.sqlite;
182354
182519
  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 +183569,7 @@ function registerPiDreamerProject(opts) {
183404
183569
  projectIdentity: opts.projectIdentity,
183405
183570
  client,
183406
183571
  dreamerConfig: opts.config,
183572
+ language: opts.language,
183407
183573
  gitCommitIndexing: opts.gitCommitIndexing,
183408
183574
  ensureRegistered: ensureProjectRegisteredFromPiDirectory,
183409
183575
  retrospectiveRawProvider: () => new PiRetrospectiveRawProvider({ projectCwd: opts.projectDir }),
@@ -183418,7 +183584,7 @@ function registerPiDreamerProject(opts) {
183418
183584
  const runManual = async (task) => runManualDream({
183419
183585
  db: opts.db,
183420
183586
  projectIdentity: opts.projectIdentity,
183421
- tasks: buildDreamTaskRuntimeConfigs(opts.config),
183587
+ tasks: buildDreamTaskRuntimeConfigs(opts.config, opts.language),
183422
183588
  executor: createDreamTaskExecutor({
183423
183589
  client,
183424
183590
  sessionDirectory: opts.projectDir,
@@ -183428,7 +183594,8 @@ function registerPiDreamerProject(opts) {
183428
183594
  }),
183429
183595
  primerRawProviderFactory: createPiPrimerRawProviderFactory(),
183430
183596
  userMemoryCollectionEnabled: userMemoryCollectionEnabled(opts.config),
183431
- ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory
183597
+ ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
183598
+ language: opts.language
183432
183599
  }),
183433
183600
  task
183434
183601
  });
@@ -184540,7 +184707,7 @@ var RECENT_TOOL_SKELETON_WINDOW = 20;
184540
184707
  function buildReplacementContent(tagId) {
184541
184708
  return `[dropped §${tagId}§]`;
184542
184709
  }
184543
- function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps, syntheticPendingOps = []) {
184710
+ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps, syntheticPendingOps = [], editMarkerTagIds = new Set) {
184544
184711
  let didMutateMessage = false;
184545
184712
  db.transaction(() => {
184546
184713
  const tags = preloadedTags ?? getTagsBySession(db, sessionId);
@@ -184571,7 +184738,15 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
184571
184738
  }
184572
184739
  let shouldPersistDrop = false;
184573
184740
  if (isToolTag) {
184574
- if (skeletonWindow.has(pendingOp.tagId)) {
184741
+ if (editMarkerTagIds.has(pendingOp.tagId)) {
184742
+ const markResult = target?.editMarker?.() ?? "absent";
184743
+ if (markResult === "incomplete" || markResult === "absent") {
184744
+ continue;
184745
+ }
184746
+ didMutateMessage = true;
184747
+ updateTagDropMode(db, sessionId, pendingOp.tagId, "edit_marker");
184748
+ shouldPersistDrop = true;
184749
+ } else if (skeletonWindow.has(pendingOp.tagId)) {
184575
184750
  const truncResult = target?.truncate?.() ?? "absent";
184576
184751
  if (truncResult === "incomplete" || synthetic && truncResult !== "truncated") {
184577
184752
  continue;
@@ -184616,7 +184791,12 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
184616
184791
  if (tag.status === "dropped") {
184617
184792
  const target = targets.get(tag.tagNumber);
184618
184793
  if (tag.type === "tool") {
184619
- if (tag.dropMode === "truncated") {
184794
+ if (tag.dropMode === "edit_marker") {
184795
+ const markResult = target?.editMarker?.() ?? "absent";
184796
+ if (markResult === "truncated") {
184797
+ didMutateMessage = true;
184798
+ }
184799
+ } else if (tag.dropMode === "truncated") {
184620
184800
  const truncResult = target?.truncate?.() ?? "absent";
184621
184801
  if (truncResult === "truncated") {
184622
184802
  didMutateMessage = true;
@@ -186213,6 +186393,7 @@ function channel1RefireTokens(workingWindowTokens) {
186213
186393
  var S_GENTLE = 0.2;
186214
186394
  var S_FIRM = 0.4;
186215
186395
  var S_URGENT = 0.65;
186396
+ var CHANNEL1_PRESSURE_FLOOR = 0.8;
186216
186397
  var LEVEL_RANK = { gentle: 1, firm: 2, urgent: 3 };
186217
186398
  var DROP_SENTINELS = ["[dropped", "[truncated"];
186218
186399
  function isDroppedToolOutput(output) {
@@ -186223,7 +186404,8 @@ function toolOutputTokens(output) {
186223
186404
  return Math.round(byteSize(output) * TOKENS_PER_BYTE);
186224
186405
  }
186225
186406
  function decideChannel1(input) {
186226
- const { undroppedTokens, pressure, workingWindowTokens, hasRecentReduce } = input;
186407
+ const { undroppedTokens, workingWindowTokens, hasRecentReduce } = input;
186408
+ const pressure = Math.min(1, Math.max(0, input.pressure));
186227
186409
  const resetCycle = hasRecentReduce || undroppedTokens < input.lastNudgeUndropped;
186228
186410
  const lastNudge = resetCycle ? 0 : input.lastNudgeUndropped;
186229
186411
  const lastLevel = resetCycle ? "" : input.lastNudgeLevel;
@@ -186238,8 +186420,10 @@ function decideChannel1(input) {
186238
186420
  return quiet();
186239
186421
  if (undroppedTokens < CHANNEL1_FLOOR_TOKENS)
186240
186422
  return quiet();
186241
- const budget = workingWindowTokens > 0 ? workingWindowTokens : undroppedTokens || 1;
186242
- const severity = undroppedTokens / budget * pressure;
186423
+ if (pressure < CHANNEL1_PRESSURE_FLOOR)
186424
+ return quiet();
186425
+ const denom = Math.max(input.estimatedInputTokens, 1);
186426
+ const severity = Math.min(1, undroppedTokens / denom);
186243
186427
  if (severity < S_GENTLE)
186244
186428
  return quiet();
186245
186429
  let level;
@@ -186763,6 +186947,97 @@ function renderMemoryBlockV2(memories, wrapper = "project-memory", renderOptions
186763
186947
  `);
186764
186948
  }
186765
186949
 
186950
+ // ../plugin/src/hooks/magic-context/supersession-reclaim.ts
186951
+ var TODOWRITE_KEEP = 1;
186952
+ var CTX_REDUCE_KEEP = 5;
186953
+ var ZERO_VALUE_META_TOOLS = new Set(["bash_status", "bash_kill"]);
186954
+ var CTX_NOTE_ZERO_VALUE_ACTIONS = new Set(["read", "dismiss"]);
186955
+ function buildSupersessionReclaimOps(input) {
186956
+ const realPendingTagIds = new Set((input.pendingOps ?? []).map((op) => op.tagId));
186957
+ const tags = getActiveTagsBySession(input.db, input.sessionId);
186958
+ const toolTags = tags.filter((tag) => tag.type === "tool" && tag.status === "active").sort((left, right) => right.tagNumber - left.tagNumber);
186959
+ const dropTagIds = [];
186960
+ let todowriteSeen = 0;
186961
+ let ctxReduceSeen = 0;
186962
+ for (const tag of toolTags) {
186963
+ const name2 = tag.toolName;
186964
+ if (!name2)
186965
+ continue;
186966
+ let isTarget = false;
186967
+ if (name2 === "todowrite") {
186968
+ todowriteSeen += 1;
186969
+ isTarget = todowriteSeen > TODOWRITE_KEEP;
186970
+ } else if (name2 === "ctx_reduce") {
186971
+ ctxReduceSeen += 1;
186972
+ isTarget = ctxReduceSeen > CTX_REDUCE_KEEP;
186973
+ } else if (ZERO_VALUE_META_TOOLS.has(name2)) {
186974
+ isTarget = true;
186975
+ } else if (name2 === "ctx_note") {
186976
+ const action = input.targets.get(tag.tagNumber)?.readInput?.()?.action;
186977
+ isTarget = typeof action === "string" && CTX_NOTE_ZERO_VALUE_ACTIONS.has(action);
186978
+ }
186979
+ if (isTarget)
186980
+ dropTagIds.push(tag.tagNumber);
186981
+ }
186982
+ const synthetic = [];
186983
+ for (const tagId of dropTagIds) {
186984
+ if (realPendingTagIds.has(tagId))
186985
+ continue;
186986
+ if (input.targets.get(tagId)?.canDrop?.() !== true)
186987
+ continue;
186988
+ synthetic.push({
186989
+ id: 0,
186990
+ sessionId: input.sessionId,
186991
+ tagId,
186992
+ operation: "drop",
186993
+ queuedAt: 0
186994
+ });
186995
+ }
186996
+ return synthetic;
186997
+ }
186998
+ function buildEditSupersessionReclaim(input) {
186999
+ const realPendingTagIds = new Set((input.pendingOps ?? []).map((op) => op.tagId));
187000
+ const tags = getActiveTagsBySession(input.db, input.sessionId);
187001
+ const editTags = tags.filter((tag) => tag.type === "tool" && tag.status === "active" && isEditTool(tag.toolName)).sort((left, right) => right.tagNumber - left.tagNumber);
187002
+ const seenFile = new Set;
187003
+ const ops = [];
187004
+ const editMarkerTagIds = new Set;
187005
+ for (const tag of editTags) {
187006
+ const filePath = readFilePath(input.targets.get(tag.tagNumber));
187007
+ if (!filePath)
187008
+ continue;
187009
+ if (!seenFile.has(filePath)) {
187010
+ seenFile.add(filePath);
187011
+ continue;
187012
+ }
187013
+ if (realPendingTagIds.has(tag.tagNumber))
187014
+ continue;
187015
+ if (input.targets.get(tag.tagNumber)?.canDrop?.() !== true)
187016
+ continue;
187017
+ editMarkerTagIds.add(tag.tagNumber);
187018
+ ops.push({
187019
+ id: 0,
187020
+ sessionId: input.sessionId,
187021
+ tagId: tag.tagNumber,
187022
+ operation: "drop",
187023
+ queuedAt: 0
187024
+ });
187025
+ }
187026
+ return { ops, editMarkerTagIds };
187027
+ }
187028
+ var FILE_PATH_KEYS = ["filePath", "file_path", "path"];
187029
+ function readFilePath(target) {
187030
+ const input = target?.readInput?.();
187031
+ if (!input)
187032
+ return null;
187033
+ for (const key of FILE_PATH_KEYS) {
187034
+ const value = input[key];
187035
+ if (typeof value === "string" && value.length > 0)
187036
+ return value;
187037
+ }
187038
+ return null;
187039
+ }
187040
+
186766
187041
  // ../plugin/src/hooks/magic-context/tool-reclaim.ts
186767
187042
  function buildSyntheticToolReclaimOps(input) {
186768
187043
  const watermark = Math.max(0, input.watermark);
@@ -187257,9 +187532,35 @@ function buildAggregateTarget(tagId, occurrences) {
187257
187532
  }
187258
187533
  return any2 ? "truncated" : "absent";
187259
187534
  },
187535
+ editMarker() {
187536
+ const sentinel = `[dropped §${tagId}§]`;
187537
+ let any2 = false;
187538
+ for (const occ of occurrences) {
187539
+ if (occ.kind === "tool_use") {
187540
+ const input = occ.part.getToolInput?.();
187541
+ if (input) {
187542
+ const next = { ...input };
187543
+ applyEditMarkerToInput(next);
187544
+ if (occ.part.setToolInput?.(next))
187545
+ any2 = true;
187546
+ }
187547
+ } else if (setToolContentOrText(occ.part, sentinel)) {
187548
+ any2 = true;
187549
+ }
187550
+ }
187551
+ return any2 ? "truncated" : "absent";
187552
+ },
187260
187553
  canDrop() {
187261
187554
  return occurrences.length > 0;
187262
187555
  },
187556
+ readInput() {
187557
+ for (const occ of occurrences) {
187558
+ const input = occ.part.getToolInput?.();
187559
+ if (input)
187560
+ return input;
187561
+ }
187562
+ return null;
187563
+ },
187263
187564
  message: {
187264
187565
  info: { id: messageId, role },
187265
187566
  parts: []
@@ -188555,6 +188856,7 @@ function maybeChannel1ReminderForToolResult(args) {
188555
188856
  const decision = decideChannel1({
188556
188857
  undroppedTokens,
188557
188858
  pressure,
188859
+ estimatedInputTokens: state.lastInputTokens + state.turnToolTokens,
188558
188860
  workingWindowTokens,
188559
188861
  lastNudgeUndropped: getLastNudgeUndropped(db, sessionId),
188560
188862
  lastNudgeLevel: getLastNudgeLevel(db, sessionId),
@@ -188643,8 +188945,6 @@ function maybeDeliverChannel2Pi(pi, db, sessionId, deliverAs = "followUp") {
188643
188945
 
188644
188946
  // src/detect-recent-commit.ts
188645
188947
  var COMMIT_LOOKBACK = 5;
188646
- var COMMIT_HASH_PATTERN2 = /\b[0-9a-f]{7,12}\b/;
188647
- var COMMIT_VERB_PATTERN = /\b(commit|committed|cherry-pick|merge|rebas)/i;
188648
188948
  function detectRecentCommit(messages) {
188649
188949
  let assistantsScanned = 0;
188650
188950
  for (let i = messages.length - 1;i >= 0; i--) {
@@ -188662,8 +188962,7 @@ function detectRecentCommit(messages) {
188662
188962
  continue;
188663
188963
  for (const part of content) {
188664
188964
  if (part && typeof part === "object" && "type" in part && part.type === "text" && "text" in part && typeof part.text === "string") {
188665
- const text = part.text;
188666
- if (COMMIT_HASH_PATTERN2.test(text) && COMMIT_VERB_PATTERN.test(text)) {
188965
+ if (textMentionsRecentCommit(part.text)) {
188667
188966
  return true;
188668
188967
  }
188669
188968
  }
@@ -191362,8 +191661,8 @@ function buildHistorianFailureNotice(failureCount, lastError) {
191362
191661
  ].join(`
191363
191662
  `);
191364
191663
  }
191365
- function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationError) {
191366
- return [
191664
+ function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationError, language) {
191665
+ const prompt = [
191367
191666
  originalPrompt,
191368
191667
  "",
191369
191668
  "Your previous XML response was invalid and cannot be persisted.",
@@ -191376,6 +191675,7 @@ function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationEr
191376
191675
  previousOutput
191377
191676
  ].join(`
191378
191677
  `);
191678
+ return withContentLanguageDirective(prompt, language, { preserveUserQuotes: true });
191379
191679
  }
191380
191680
  function validateStoredCompartments(compartments) {
191381
191681
  if (compartments.length === 0) {
@@ -193499,9 +193799,11 @@ ${chunkText}`,
193499
193799
  };
193500
193800
  };
193501
193801
  retainDrainReservationForRetryThrottle = true;
193802
+ const historianSystemPrompt = withContentLanguageDirective(COMPARTMENT_AGENT_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true });
193803
+ const historianEditorSystemPrompt = withContentLanguageDirective(HISTORIAN_EDITOR_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true });
193502
193804
  const firstResult = await runner.run({
193503
193805
  agent: HISTORIAN_AGENT_NAME,
193504
- systemPrompt: COMPARTMENT_AGENT_SYSTEM_PROMPT,
193806
+ systemPrompt: historianSystemPrompt,
193505
193807
  userMessage: prompt,
193506
193808
  model: historianModel,
193507
193809
  fallbackModels,
@@ -193516,10 +193818,10 @@ ${chunkText}`,
193516
193818
  let validatedDraftText = firstResult.ok ? firstResult.assistantText : null;
193517
193819
  if (validatedPass.kind === "validation-failed") {
193518
193820
  sessionLog(sessionId, `historian: first pass validation failed, retrying with repair prompt: ${validatedPass.error}`);
193519
- const repairPrompt = buildHistorianRepairPrompt(prompt, validatedPass.rawText, validatedPass.error);
193821
+ const repairPrompt = buildHistorianRepairPrompt(prompt, validatedPass.rawText, validatedPass.error, deps.language);
193520
193822
  const repairResult = await runner.run({
193521
193823
  agent: HISTORIAN_AGENT_NAME,
193522
- systemPrompt: COMPARTMENT_AGENT_SYSTEM_PROMPT,
193824
+ systemPrompt: historianSystemPrompt,
193523
193825
  userMessage: repairPrompt,
193524
193826
  model: historianModel,
193525
193827
  fallbackModels,
@@ -193544,7 +193846,7 @@ ${chunkText}`,
193544
193846
  sessionLog(sessionId, `historian: escalating to configured fallback model ${candidate}`);
193545
193847
  const fbResult = await runner.run({
193546
193848
  agent: HISTORIAN_AGENT_NAME,
193547
- systemPrompt: COMPARTMENT_AGENT_SYSTEM_PROMPT,
193849
+ systemPrompt: historianSystemPrompt,
193548
193850
  userMessage: prompt,
193549
193851
  model: candidate,
193550
193852
  fallbackModels: undefined,
@@ -193580,7 +193882,7 @@ ${chunkText}`,
193580
193882
  sessionLog(sessionId, "historian two-pass: running editor on draft");
193581
193883
  const editorResult = await runner.run({
193582
193884
  agent: HISTORIAN_AGENT_NAME,
193583
- systemPrompt: HISTORIAN_EDITOR_SYSTEM_PROMPT,
193885
+ systemPrompt: historianEditorSystemPrompt,
193584
193886
  userMessage: buildHistorianEditorPrompt(draftAssistantText),
193585
193887
  model: historianModel,
193586
193888
  timeoutMs: historianTimeoutMs,
@@ -194338,16 +194640,20 @@ var PARTNER_FRAME_CLOSER_NO_REDUCE = `
194338
194640
  Context is managed for you entirely automatically — there's nothing to prune and no warnings to act on. Stay reasonably concise per operation, and never let context size change *what* work you take on or *how thoroughly* you do it.`;
194339
194641
  var CTX_NOTE_GUIDANCE = `Use \`ctx_note\` ONLY for genuinely future concerns — something to revisit much later, not work coming up in the next few turns (that's already in your active context) and not active multi-step work (use todos for that). Magic Context preserves your full context across both compaction and restarts, so an upcoming restart or "let's come back to this later" is never a reason to take a note — nothing is lost either way. Notes you do take survive compression and resurface at natural work boundaries (after commits, historian runs, todo completion).`;
194340
194642
  var TOOL_HISTORY_GUIDANCE = `Compressed history intentionally omits tool calls and their outputs — summaries like "I edited file X" are historian records, not patterns to replicate. In the live conversation, older tool calls and their results are cleaned up to save context — you may see your own past messages referencing actions without the corresponding tool call or result visible. This is normal context management. ALWAYS use real tool calls; never simulate, fabricate, or inline tool outputs in your text. If there is no tool result message, the action did not happen. NEVER simulate, hallucinate or claim tool calls, command output, search results, file edits, or diffs in plain text as if they actually occurred.`;
194341
- var BASE_INTRO = (protectedTags) => `Messages and tool outputs are tagged with §N§ identifiers (e.g., §1§, §42§).
194342
- Use \`ctx_reduce\` to mark spent tagged content as discardable and reclaim space. Marking is NOT an immediate delete — it queues the content, which stays fully visible until space is actually needed (as soon as the next turn if you're already under pressure, much later if not), so mark a tool output as soon as you're done with it rather than hoarding the call for the end of the turn. The last ${protectedTags} tags are protected (marking one just queues it until it ages out). Syntax: "3-5", "1,2,9", or "1-5,8,12-15".
194343
- Do not announce or narrate \`ctx_reduce\` drops — just call the tool silently. Saying "I'll drop these outputs" wastes tokens the user does not care about.
194344
- ${CTX_NOTE_GUIDANCE}
194345
- Use \`ctx_memory\` for durable project knowledge: write what future sessions must know, update/archive/merge the memories you see in \`<project-memory>\` when they drift. Memories persist across sessions and every new session starts with them.
194643
+ var MEMORY_GUIDANCE = `Use \`ctx_memory\` for durable project knowledge: write what future sessions must know, update/archive/merge the memories you see in \`<project-memory>\` when they drift. Memories persist across sessions and every new session starts with them.
194346
194644
  **Save to memory proactively**: If you spent multiple turns finding something (a file path, a DB location, a config pattern, a workaround), save it with \`ctx_memory\` so future sessions don't repeat the search. Examples:
194347
194645
  - Found a project's source code path after searching → \`ctx_memory(action="write", category="CONFIG_VALUES", content="OpenCode source is at ~/Work/OSS/opencode")\`
194348
194646
  - Discovered a non-obvious build/test command → \`ctx_memory(action="write", category="PROJECT_RULES", content="Always use scripts/release.sh for releases")\`
194349
- - Learned a constraint the hard way → \`ctx_memory(action="write", category="CONSTRAINTS", content="Dashboard Tauri build needs RGBA PNGs, not grayscale")\`
194350
- Use \`ctx_search\` to search across project memories, indexed git commits, and this session's full conversation history (including compacted parts) from one query.
194647
+ - Learned a constraint the hard way → \`ctx_memory(action="write", category="CONSTRAINTS", content="Dashboard Tauri build needs RGBA PNGs, not grayscale")\``;
194648
+ function memoryGuidanceBlock(memoryEnabled) {
194649
+ return memoryEnabled ? `${MEMORY_GUIDANCE}
194650
+ ` : "";
194651
+ }
194652
+ var BASE_INTRO = (protectedTags, memoryEnabled) => `Messages and tool outputs are tagged with §N§ identifiers (e.g., §1§, §42§).
194653
+ Use \`ctx_reduce\` to mark spent tagged content as discardable and reclaim space. Marking is NOT an immediate delete — it queues the content, which stays fully visible until space is actually needed (as soon as the next turn if you're already under pressure, much later if not), so mark a tool output as soon as you're done with it rather than hoarding the call for the end of the turn. The last ${protectedTags} tags are protected (marking one just queues it until it ages out). Syntax: "3-5", "1,2,9", or "1-5,8,12-15".
194654
+ Do not announce or narrate \`ctx_reduce\` drops — just call the tool silently. Saying "I'll drop these outputs" wastes tokens the user does not care about.
194655
+ ${CTX_NOTE_GUIDANCE}
194656
+ ${memoryGuidanceBlock(memoryEnabled)}Use \`ctx_search\` to search across project memories, indexed git commits, and this session's full conversation history (including compacted parts) from one query.
194351
194657
  Use \`ctx_expand\` to recover the raw conversation behind a \`<compartment>\` summary in \`<session-history>\` — pass its \`start\`/\`end\` attributes when the summary is not enough (exact wording, values, error text).
194352
194658
  **Search before asking the user**: If you can't remember or don't know something that might have been discussed before or stored in project memory, use \`ctx_search\` before asking the user. Examples:
194353
194659
  - Can't remember where a related codebase or dependency lives → \`ctx_search(query="opencode source code path")\`
@@ -194361,13 +194667,8 @@ NEVER drop large ranges blindly (e.g., "1-50"). Review each tag before deciding.
194361
194667
  Keep your user's instructions and intent — never drop a user message for its directive, even an old one. But a large block of pasted content inside a user message (logs, data dumps, long code, attachments) is fair to mark discardable once you've extracted what you need — it stays searchable via \`ctx_search\`.
194362
194668
  NEVER drop assistant text messages unless they are exceptionally large. Your conversation messages are lightweight; only large tool outputs are worth dropping.
194363
194669
  Before your turn finishes, consider using \`ctx_reduce\` to drop large tool outputs you no longer need.`;
194364
- var BASE_INTRO_NO_REDUCE = () => `${CTX_NOTE_GUIDANCE}
194365
- Use \`ctx_memory\` for durable project knowledge: write what future sessions must know, update/archive/merge the memories you see in \`<project-memory>\` when they drift. Memories persist across sessions and every new session starts with them.
194366
- **Save to memory proactively**: If you spent multiple turns finding something (a file path, a DB location, a config pattern, a workaround), save it with \`ctx_memory\` so future sessions don't repeat the search. Examples:
194367
- - Found a project's source code path after searching → \`ctx_memory(action="write", category="CONFIG_VALUES", content="OpenCode source is at ~/Work/OSS/opencode")\`
194368
- - Discovered a non-obvious build/test command → \`ctx_memory(action="write", category="PROJECT_RULES", content="Always use scripts/release.sh for releases")\`
194369
- - Learned a constraint the hard way → \`ctx_memory(action="write", category="CONSTRAINTS", content="Dashboard Tauri build needs RGBA PNGs, not grayscale")\`
194370
- Use \`ctx_search\` to search across project memories, indexed git commits, and this session's full conversation history (including compacted parts) from one query.
194670
+ var BASE_INTRO_NO_REDUCE = (memoryEnabled) => `${CTX_NOTE_GUIDANCE}
194671
+ ${memoryGuidanceBlock(memoryEnabled)}Use \`ctx_search\` to search across project memories, indexed git commits, and this session's full conversation history (including compacted parts) from one query.
194371
194672
  Use \`ctx_expand\` to recover the raw conversation behind a \`<compartment>\` summary in \`<session-history>\` — pass its \`start\`/\`end\` attributes when the summary is not enough (exact wording, values, error text).
194372
194673
  **Search before asking the user**: If you can't remember or don't know something that might have been discussed before or stored in project memory, use \`ctx_search\` before asking the user. Examples:
194373
194674
  - Can't remember where a related codebase or dependency lives → \`ctx_search(query="opencode source code path")\`
@@ -194401,7 +194702,7 @@ Drop silently — do not narrate it. NEVER drop large ranges blindly (e.g., "1-5
194401
194702
  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
194703
  var CAVEMAN_COMPRESSION_WARNING = `
194403
194704
  **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) {
194705
+ function buildMagicContextSection(_agent, protectedTags, ctxReduceEnabled = true, dreamerEnabled = false, temporalAwarenessEnabled = false, cavemanTextCompressionEnabled = false, subagentMode = false, language, memoryEnabled = true) {
194405
194706
  if (subagentMode) {
194406
194707
  return `## Magic Context
194407
194708
 
@@ -194413,23 +194714,27 @@ The dreamer evaluates smart note conditions during nightly runs and surfaces the
194413
194714
  Example: \`ctx_note(action="write", content="Implement X because Y", surface_condition="When PR #42 is merged in this repo")\`` : "";
194414
194715
  const temporalGuidance = temporalAwarenessEnabled ? TEMPORAL_AWARENESS_GUIDANCE : "";
194415
194716
  const cavemanWarning = cavemanTextCompressionEnabled && !ctxReduceEnabled ? CAVEMAN_COMPRESSION_WARNING : "";
194717
+ const languageDirective = buildPrimaryLanguageDirective(language);
194718
+ const languageGuidance = languageDirective ? `
194719
+
194720
+ ${languageDirective}` : "";
194416
194721
  if (!ctxReduceEnabled) {
194417
194722
  return `## Magic Context
194418
194723
 
194419
194724
  ${LONG_TERM_PARTNER_FRAME}
194420
194725
  ${PARTNER_FRAME_CLOSER_NO_REDUCE}
194421
194726
 
194422
- ${BASE_INTRO_NO_REDUCE()}${smartNoteGuidance}${temporalGuidance}${cavemanWarning}`;
194727
+ ${BASE_INTRO_NO_REDUCE(memoryEnabled)}${smartNoteGuidance}${temporalGuidance}${cavemanWarning}${languageGuidance}`;
194423
194728
  }
194424
194729
  return `## Magic Context
194425
194730
 
194426
194731
  ${LONG_TERM_PARTNER_FRAME}
194427
194732
  ${PARTNER_FRAME_CLOSER_REDUCE}
194428
194733
 
194429
- ${BASE_INTRO(protectedTags)}${smartNoteGuidance}${temporalGuidance}
194734
+ ${BASE_INTRO(protectedTags, memoryEnabled)}${smartNoteGuidance}${temporalGuidance}
194430
194735
  ${GENERIC_SECTION}
194431
194736
 
194432
- Prefer many small targeted operations over one large blanket operation, and keep the working set tidy as routine maintenance.`;
194737
+ Prefer many small targeted operations over one large blanket operation, and keep the working set tidy as routine maintenance.${languageGuidance}`;
194433
194738
  }
194434
194739
 
194435
194740
  // src/system-prompt.ts
@@ -194441,7 +194746,7 @@ function buildMagicContextBlock(opts) {
194441
194746
  const includeGuidance = (opts.includeGuidance ?? true) && !existing.includes(MAGIC_CONTEXT_MARKER);
194442
194747
  if (!includeGuidance)
194443
194748
  return null;
194444
- return buildMagicContextSection(null, opts.protectedTags ?? 20, opts.ctxReduceEnabled ?? true, opts.dreamerEnabled ?? false, opts.temporalAwarenessEnabled ?? false, opts.cavemanTextCompressionEnabled ?? false);
194749
+ return buildMagicContextSection(null, opts.protectedTags ?? 20, opts.ctxReduceEnabled ?? true, opts.dreamerEnabled ?? false, opts.temporalAwarenessEnabled ?? false, opts.cavemanTextCompressionEnabled ?? false, false, opts.language, opts.memoryEnabled !== false);
194445
194750
  }
194446
194751
  var DATE_PATTERN = /Today's date: .+/;
194447
194752
  function processSystemPromptForCache(args) {
@@ -194976,6 +195281,31 @@ function createPiAssistantPart(working, messageIndex, partIndex, markDirty) {
194976
195281
  }
194977
195282
  return { toolName: p.name, inputByteSize, inputTokenCount };
194978
195283
  },
195284
+ getToolInput() {
195285
+ const current = working[messageIndex].content;
195286
+ const p = current[partIndex];
195287
+ if (p?.type !== "toolCall")
195288
+ return null;
195289
+ return p.arguments && typeof p.arguments === "object" ? p.arguments : null;
195290
+ },
195291
+ setToolInput(input) {
195292
+ const current = working[messageIndex].content;
195293
+ const p = current[partIndex];
195294
+ if (p?.type !== "toolCall")
195295
+ return false;
195296
+ try {
195297
+ if (JSON.stringify(p.arguments) === JSON.stringify(input))
195298
+ return false;
195299
+ } catch {}
195300
+ const newContent = current.slice();
195301
+ newContent[partIndex] = { ...p, arguments: input };
195302
+ working[messageIndex] = {
195303
+ ...working[messageIndex],
195304
+ content: newContent
195305
+ };
195306
+ markDirty(messageIndex);
195307
+ return true;
195308
+ },
194979
195309
  replaceWithSentinel(sentinelText) {
194980
195310
  const current = working[messageIndex].content;
194981
195311
  const existing = current[partIndex];
@@ -195809,6 +196139,7 @@ function registerPiContextHandler(pi, baseOptions) {
195809
196139
  projectDirectory,
195810
196140
  messages: event.messages,
195811
196141
  ctxReduceEnabled: options.ctxReduceEnabled,
196142
+ smartDrops: options.smartDrops === true,
195812
196143
  protectedTags: options.protectedTags ?? 20,
195813
196144
  heuristics: options.heuristics,
195814
196145
  emergencyCeilingTokens,
@@ -196133,6 +196464,7 @@ function spawnPiHistorianRun(args) {
196133
196464
  memoryEnabled: historian.memoryEnabled,
196134
196465
  autoPromote: historian.autoPromote,
196135
196466
  userMemoriesEnabled: historian.userMemoriesEnabled,
196467
+ language: historian.language,
196136
196468
  compartmentLeaseHolderId: holderId,
196137
196469
  onPublished: () => {
196138
196470
  const sessionStillActive = isContextHandlerSessionActive(sessionId);
@@ -196598,9 +196930,38 @@ async function runPipeline(args) {
196598
196930
  watermark: reclaimMeta.toolReclaimWatermark ?? 0,
196599
196931
  pendingOps
196600
196932
  });
196933
+ const editMarkerTagIds = new Set;
196934
+ if (args.smartDrops) {
196935
+ const selectedIds = new Set(syntheticPendingOps.map((op) => op.tagId));
196936
+ const supersessionOps = buildSupersessionReclaimOps({
196937
+ db: args.db,
196938
+ sessionId: args.sessionId,
196939
+ targets,
196940
+ pendingOps
196941
+ });
196942
+ for (const op of supersessionOps) {
196943
+ if (!selectedIds.has(op.tagId)) {
196944
+ syntheticPendingOps.push(op);
196945
+ selectedIds.add(op.tagId);
196946
+ }
196947
+ }
196948
+ const editReclaim = buildEditSupersessionReclaim({
196949
+ db: args.db,
196950
+ sessionId: args.sessionId,
196951
+ targets,
196952
+ pendingOps
196953
+ });
196954
+ for (const op of editReclaim.ops) {
196955
+ if (!selectedIds.has(op.tagId)) {
196956
+ syntheticPendingOps.push(op);
196957
+ selectedIds.add(op.tagId);
196958
+ editMarkerTagIds.add(op.tagId);
196959
+ }
196960
+ }
196961
+ }
196601
196962
  autoReclaimTargetCount = syntheticPendingOps.length;
196602
196963
  if (syntheticPendingOps.length > 0) {
196603
- autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags, undefined, [], syntheticPendingOps);
196964
+ autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags, undefined, [], syntheticPendingOps, editMarkerTagIds);
196604
196965
  if (autoReclaimDidMutate) {
196605
196966
  droppedCount += syntheticPendingOps.length;
196606
196967
  autoReclaimDidMutateThisPass = true;
@@ -197082,7 +197443,7 @@ async function runValidatedHistorianPass(args) {
197082
197443
  return finalResult;
197083
197444
  }
197084
197445
  await args.callbacks?.onRepairRetry?.(firstValidation.error ?? "invalid compartment output");
197085
- const repairPrompt = buildHistorianRepairPrompt(args.prompt, firstRun.result, firstValidation.error ?? "invalid compartment output");
197446
+ const repairPrompt = buildHistorianRepairPrompt(args.prompt, firstRun.result, firstValidation.error ?? "invalid compartment output", args.language);
197086
197447
  const repairRun = await runHistorianPrompt({
197087
197448
  ...args,
197088
197449
  prompt: repairPrompt,
@@ -197724,6 +198085,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
197724
198085
  twoPass: deps.historianTwoPass,
197725
198086
  subagentKind: "recomp",
197726
198087
  agentId: HISTORIAN_RECOMP_AGENT,
198088
+ language: deps.language,
197727
198089
  callbacks: {
197728
198090
  onRepairRetry: async (error51) => {
197729
198091
  emitProgress(`Repair retry (pass ${passCount + 1})…`);
@@ -198142,6 +198504,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
198142
198504
  twoPass: deps.historianTwoPass,
198143
198505
  subagentKind: "recomp",
198144
198506
  agentId: HISTORIAN_RECOMP_AGENT,
198507
+ language: deps.language,
198145
198508
  callbacks: {
198146
198509
  onRepairRetry: async (error51) => {
198147
198510
  await sendIgnoredMessage(client, sessionId, `## Magic Recomp — Partial
@@ -198634,7 +198997,7 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
198634
198997
  client: createPiHistorianClient({
198635
198998
  runner: deps.runner,
198636
198999
  model: deps.historianModel,
198637
- systemPrompt: COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT,
199000
+ systemPrompt: withContentLanguageDirective(COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true }),
198638
199001
  fallbackModels: deps.historianFallbacks,
198639
199002
  timeoutMs: deps.historianTimeoutMs,
198640
199003
  thinkingLevel: deps.historianThinkingLevel,
@@ -198657,6 +199020,7 @@ Historian recomp started. Rebuilding compartments and facts from raw Pi session
198657
199020
  autoPromote: deps.autoPromote,
198658
199021
  ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
198659
199022
  fallbackModels: deps.historianFallbacks,
199023
+ language: deps.language,
198660
199024
  fallbackModelId: ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined
198661
199025
  }, parsed.kind === "partial" ? { range: parsed.range } : {});
198662
199026
  if (result.published) {
@@ -198909,7 +199273,7 @@ async function runPiMemoryMigration(deps) {
198909
199273
  }
198910
199274
  const result = await deps.runner.run({
198911
199275
  agent: "magic-context-historian",
198912
- systemPrompt: MIGRATION_SYSTEM_PROMPT2,
199276
+ systemPrompt: withMigrationLanguageDirective(MIGRATION_SYSTEM_PROMPT2, deps.language),
198913
199277
  userMessage: prompt,
198914
199278
  model,
198915
199279
  fallbackModels: undefined,
@@ -199021,7 +199385,8 @@ An upgrade or recomp is already running for this session in the background. Wait
199021
199385
  thinkingLevel: deps.historianThinkingLevel,
199022
199386
  directory: ctx.cwd,
199023
199387
  sessionId,
199024
- userMemoriesEnabled: deps.userMemoriesEnabled
199388
+ userMemoriesEnabled: deps.userMemoriesEnabled,
199389
+ language: deps.language
199025
199390
  });
199026
199391
  return outcome.summary;
199027
199392
  } catch (error51) {
@@ -199093,7 +199458,7 @@ Rebuilding compartments into the v2 format and re-organizing project memories. T
199093
199458
  thinkingLevel: deps.historianThinkingLevel,
199094
199459
  directory: ctx.cwd,
199095
199460
  accountingSessionId: sessionId,
199096
- systemPrompt: COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT,
199461
+ systemPrompt: withContentLanguageDirective(COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true }),
199097
199462
  notify: (text) => sendCtxStatusMessage(pi, {
199098
199463
  title: "/ctx-session-upgrade",
199099
199464
  text,
@@ -199109,7 +199474,8 @@ Rebuilding compartments into the v2 format and re-organizing project memories. T
199109
199474
  autoPromote: deps.autoPromote,
199110
199475
  ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
199111
199476
  fallbackModels: deps.historianFallbacks,
199112
- fallbackModelId: sessionMainModel
199477
+ fallbackModelId: sessionMainModel,
199478
+ language: deps.language
199113
199479
  }, {});
199114
199480
  if (!recompResult.published || !isRecompComplete(recompResult.message)) {
199115
199481
  const reason = contextualizeUpgradeReason(isRecompFailure(recompResult.message) ? extractRecompReason(recompResult.message) : `Compartments were not fully rebuilt: ${extractRecompReason(recompResult.message)}`);
@@ -199321,7 +199687,7 @@ function formatThresholdPercent(value) {
199321
199687
  // package.json
199322
199688
  var package_default = {
199323
199689
  name: "@cortexkit/pi-magic-context",
199324
- version: "0.27.3",
199690
+ version: "0.29.0",
199325
199691
  type: "module",
199326
199692
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
199327
199693
  main: "dist/index.js",
@@ -205476,7 +205842,8 @@ function resolveSidekickFromConfig(config2) {
205476
205842
  systemPrompt: sidekick.system_prompt,
205477
205843
  timeoutMs: sidekick.timeout_ms,
205478
205844
  thinking_level: sidekick.thinking_level,
205479
- fallbackModels: resolveFallbackChain(sidekick.fallback_models)
205845
+ fallbackModels: resolveFallbackChain(sidekick.fallback_models),
205846
+ language: config2.language
205480
205847
  };
205481
205848
  }
205482
205849
  function resolveHistorianFromConfig(config2) {
@@ -205505,7 +205872,8 @@ function resolveHistorianFromConfig(config2) {
205505
205872
  historyBudgetPercentage: config2.history_budget_percentage,
205506
205873
  memoryEnabled: config2.memory.enabled,
205507
205874
  autoPromote: config2.memory.auto_promote,
205508
- userMemoriesEnabled: userMemoryCollectionEnabled(config2.dreamer)
205875
+ userMemoriesEnabled: userMemoryCollectionEnabled(config2.dreamer),
205876
+ language: config2.language
205509
205877
  };
205510
205878
  }
205511
205879
  function resolveAutoSearchFromConfig(config2) {
@@ -205583,6 +205951,7 @@ async function src_default2(pi) {
205583
205951
  db,
205584
205952
  ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,
205585
205953
  allowDreamerActions: false,
205954
+ memoryToolEnabled: true,
205586
205955
  ctxReduceEnabled: config2.ctx_reduce_enabled === true,
205587
205956
  protectedTags: config2.protected_tags ?? 20,
205588
205957
  dreamerEnabled: isDreamerRunnable(config2)
@@ -205602,6 +205971,7 @@ async function src_default2(pi) {
205602
205971
  const buildContextOptions = (cfg, hist, auto) => ({
205603
205972
  db: database,
205604
205973
  ctxReduceEnabled: cfg.ctx_reduce_enabled,
205974
+ smartDrops: cfg.smart_drops === true,
205605
205975
  protectedTags: cfg.protected_tags ?? 20,
205606
205976
  heuristics: {
205607
205977
  caveman: cfg.ctx_reduce_enabled === false && cfg.caveman_text_compression ? {
@@ -205620,6 +205990,7 @@ async function src_default2(pi) {
205620
205990
  executeThresholdTokens: cfg.execute_threshold_tokens
205621
205991
  },
205622
205992
  historian: hist,
205993
+ language: cfg.language,
205623
205994
  autoSearch: auto,
205624
205995
  resolveForProject: resolveContextOptionsForProject,
205625
205996
  maybeAutoEmbedSession: (sessionId, dir, identity) => {
@@ -205692,6 +206063,7 @@ async function src_default2(pi) {
205692
206063
  historianFallbacks: historianConfig?.fallbackModels,
205693
206064
  historianTimeoutMs: config2.historian_timeout_ms,
205694
206065
  historianThinkingLevel: historianConfig?.thinkingLevel,
206066
+ language: config2.language,
205695
206067
  memoryEnabled: config2.memory.enabled,
205696
206068
  autoPromote: config2.memory.auto_promote
205697
206069
  });
@@ -205704,6 +206076,7 @@ async function src_default2(pi) {
205704
206076
  historianFallbacks: historianConfig?.fallbackModels,
205705
206077
  historianTimeoutMs: config2.historian_timeout_ms,
205706
206078
  historianThinkingLevel: historianConfig?.thinkingLevel,
206079
+ language: config2.language,
205707
206080
  memoryEnabled: config2.memory.enabled,
205708
206081
  autoPromote: config2.memory.auto_promote,
205709
206082
  userMemoriesEnabled: userMemoryCollectionEnabled(config2.dreamer)
@@ -205735,6 +206108,7 @@ async function src_default2(pi) {
205735
206108
  config: dreamerConfig,
205736
206109
  embeddingConfig: config2.embedding,
205737
206110
  memoryEnabled: config2.memory.enabled,
206111
+ language: config2.language,
205738
206112
  gitCommitIndexing: config2.memory.git_commit_indexing,
205739
206113
  onAdjunctsRefreshNeeded: signalPiSystemPromptRefreshForProject
205740
206114
  });
@@ -205849,6 +206223,7 @@ async function src_default2(pi) {
205849
206223
  dreamerEnabled: effectiveDreamerRunnable,
205850
206224
  temporalAwarenessEnabled: effectiveConfig.temporal_awareness ?? false,
205851
206225
  cavemanTextCompressionEnabled: effectiveConfig.ctx_reduce_enabled === false && effectiveConfig.caveman_text_compression?.enabled === true,
206226
+ language: effectiveConfig.language,
205852
206227
  userMemoriesEnabled: userMemoryCollectionEnabled(effectiveConfig.dreamer),
205853
206228
  isCacheBusting,
205854
206229
  existingSystemPrompt: event.systemPrompt