@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.
@@ -9923,6 +9923,17 @@ function runMigrations(db) {
9923
9923
  log(`[migrations] schema version now: ${MIGRATIONS[MIGRATIONS.length - 1].version}`);
9924
9924
  }
9925
9925
 
9926
+ // ../plugin/src/shared/commit-detection.ts
9927
+ var HASH_HEX = "[0-9a-f]{7,12}";
9928
+ var COMMIT_HASH_TEST_PATTERN = new RegExp(`\\b${HASH_HEX}\\b`, "i");
9929
+ var COMMIT_VERB_PATTERN = /\b(?:commit(?:ted|ting|s)?|cherry-?pick(?:ed|ing|s)?|merge[ds]?|merging|rebas(?:e|ed|es|ing))\b/i;
9930
+ function textMentionsRecentCommit(text) {
9931
+ return COMMIT_HASH_TEST_PATTERN.test(text) && COMMIT_VERB_PATTERN.test(text);
9932
+ }
9933
+ function createCommitHashExtractPattern() {
9934
+ return new RegExp(`\`?\\b(${HASH_HEX})\\b\`?`, "gi");
9935
+ }
9936
+
9926
9937
  // ../plugin/src/shared/internal-initiator-marker.ts
9927
9938
  var OMO_INTERNAL_INITIATOR_MARKER = "<!-- OMO_INTERNAL_INITIATOR -->";
9928
9939
 
@@ -142374,8 +142385,6 @@ var decoder = {
142374
142385
  };
142375
142386
 
142376
142387
  // ../plugin/src/hooks/magic-context/read-session-formatting.ts
142377
- var COMMIT_HASH_PATTERN = /`?\b([0-9a-f]{6,12})\b`?/gi;
142378
- var COMMIT_HINT_PATTERN = /\b(commit(?:ted)?|cherry-?pick(?:ed)?|hash(?:es)?|sha)\b/i;
142379
142388
  var MAX_COMMITS_PER_BLOCK = 5;
142380
142389
  function hasMeaningfulUserText(parts) {
142381
142390
  for (const part of parts) {
@@ -142479,7 +142488,7 @@ function formatBlock(block) {
142479
142488
  function extractCommitHashes(text) {
142480
142489
  const hashes = [];
142481
142490
  const seen = new Set;
142482
- for (const match of text.matchAll(COMMIT_HASH_PATTERN)) {
142491
+ for (const match of text.matchAll(createCommitHashExtractPattern())) {
142483
142492
  const hash = match[1]?.toLowerCase();
142484
142493
  if (!hash || seen.has(hash))
142485
142494
  continue;
@@ -142492,10 +142501,10 @@ function extractCommitHashes(text) {
142492
142501
  }
142493
142502
  function compactTextForSummary(text, role) {
142494
142503
  const commitHashes = role === "assistant" ? extractCommitHashes(text) : [];
142495
- if (commitHashes.length === 0 || !COMMIT_HINT_PATTERN.test(text)) {
142504
+ if (commitHashes.length === 0 || !COMMIT_VERB_PATTERN.test(text)) {
142496
142505
  return { text, commitHashes };
142497
142506
  }
142498
- 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();
142507
+ const withoutHashes = text.replace(createCommitHashExtractPattern(), "").replace(/\(\s*\)/g, "").replace(/\s+,/g, ",").replace(/,\s*,+/g, ", ").replace(/\s{2,}/g, " ").replace(/\s+([,.;:])/g, "$1").trim();
142499
142508
  return {
142500
142509
  text: withoutHashes.length > 0 ? withoutHashes : text,
142501
142510
  commitHashes
@@ -144835,6 +144844,10 @@ function stripUnsafeProjectConfigFields(projectRaw) {
144835
144844
  delete projectRaw.auto_update;
144836
144845
  warnings.push("Ignoring auto_update from project config (security: this setting only honors user-level config).");
144837
144846
  }
144847
+ if ("language" in projectRaw) {
144848
+ delete projectRaw.language;
144849
+ warnings.push("Ignoring language from project config (security: output language is a user-level setting).");
144850
+ }
144838
144851
  if ("sqlite" in projectRaw) {
144839
144852
  delete projectRaw.sqlite;
144840
144853
  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 +159193,95 @@ function date4(params) {
159180
159193
 
159181
159194
  // ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
159182
159195
  config(en_default());
159196
+ // ../plugin/src/agents/language-directive.ts
159197
+ var ENGLISH_LANGUAGE_NAMES = new Intl.DisplayNames(["en"], {
159198
+ type: "language",
159199
+ fallback: "none"
159200
+ });
159201
+ function resolveLanguageName(language) {
159202
+ const code = typeof language === "string" ? language.trim().toLowerCase() : "";
159203
+ if (!/^[a-z]{2}$/.test(code))
159204
+ return "";
159205
+ let english;
159206
+ try {
159207
+ english = ENGLISH_LANGUAGE_NAMES.of(code) ?? undefined;
159208
+ } catch {
159209
+ return "";
159210
+ }
159211
+ if (!english)
159212
+ return "";
159213
+ let endonym;
159214
+ try {
159215
+ endonym = new Intl.DisplayNames([code], { type: "language", fallback: "none" }).of(code) ?? undefined;
159216
+ } catch {
159217
+ endonym = undefined;
159218
+ }
159219
+ return endonym && endonym !== english ? `${english} (${endonym})` : english;
159220
+ }
159221
+ function isValidLanguageCode(language) {
159222
+ return resolveLanguageName(language) !== "";
159223
+ }
159224
+ function buildContentLanguageDirective(language, options = {}) {
159225
+ const target = resolveLanguageName(language);
159226
+ if (!target)
159227
+ return "";
159228
+ const lines = [
159229
+ "## Output language",
159230
+ "",
159231
+ `Write human-readable prose you author in: ${target}.`,
159232
+ "",
159233
+ "Do not translate or rename structural tokens. Copy required output schemas exactly:",
159234
+ "- 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.",
159235
+ "- 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.",
159236
+ "- Localize only free-text prose values/content: summaries, memory text, explanations, titles, observations, and answers — unless the prompt says to preserve original wording.",
159237
+ "",
159238
+ "These literal values must remain English when used:",
159239
+ "PROJECT_RULES, ARCHITECTURE, CONSTRAINTS, CONFIG_VALUES, NAMING;",
159240
+ "causal_incident, trajectory_correction;",
159241
+ "feature, design, docs, release, investigation, bug, refactor, infra;",
159242
+ "memory, observation; true, false; No relevant memories found.",
159243
+ "",
159244
+ "Preserve the required output shape. Do not add commentary outside the requested XML/JSON/tool output."
159245
+ ];
159246
+ if (options.preserveUserQuotes) {
159247
+ lines.push("", `Preserve U: lines and directly quoted user text in their original source language; write the surrounding summary prose in ${target}.`);
159248
+ }
159249
+ if (options.retrospective) {
159250
+ lines.push("", `Write the lesson text in ${target}; paraphrase source text and never quote the user.`);
159251
+ }
159252
+ return lines.join(`
159253
+ `);
159254
+ }
159255
+ function withContentLanguageDirective(systemPrompt, language, options = {}) {
159256
+ const directive = buildContentLanguageDirective(language, options);
159257
+ return directive ? `${systemPrompt}
159258
+
159259
+ ${directive}` : systemPrompt;
159260
+ }
159261
+ function buildMigrationLanguageDirective(language) {
159262
+ const target = resolveLanguageName(language);
159263
+ if (!target)
159264
+ return "";
159265
+ return [
159266
+ "## Output language",
159267
+ "",
159268
+ "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."
159269
+ ].join(`
159270
+ `);
159271
+ }
159272
+ function withMigrationLanguageDirective(systemPrompt, language) {
159273
+ const directive = buildMigrationLanguageDirective(language);
159274
+ return directive ? `${systemPrompt}
159275
+
159276
+ ${directive}` : systemPrompt;
159277
+ }
159278
+ function buildPrimaryLanguageDirective(language) {
159279
+ const target = resolveLanguageName(language);
159280
+ if (!target)
159281
+ return "";
159282
+ 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.`;
159283
+ }
159284
+
159183
159285
  // ../plugin/src/features/magic-context/defaults.ts
159184
159286
  var DEFAULT_PROTECTED_TAGS = 20;
159185
159287
 
@@ -159536,6 +159638,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
159536
159638
  var MagicContextConfigSchema = exports_external.object({
159537
159639
  enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
159538
159640
  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."),
159641
+ 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
159642
  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
159643
  historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
159541
159644
  dreamer: DreamerConfigSchema.optional().describe("Dreamer agent + scheduling configuration (model, fallback_models, disable, schedule, tasks, etc.)"),
@@ -159573,6 +159676,7 @@ var MagicContextConfigSchema = exports_external.object({
159573
159676
  }).describe("Embedding provider configuration"),
159574
159677
  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).'),
159575
159678
  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."),
159679
+ 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."),
159576
159680
  caveman_text_compression: exports_external.object({
159577
159681
  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%)."),
159578
159682
  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.")
@@ -163483,6 +163587,36 @@ function isRecord(value) {
163483
163587
  return typeof value === "object" && value !== null && !Array.isArray(value);
163484
163588
  }
163485
163589
 
163590
+ // ../plugin/src/hooks/magic-context/edit-marker.ts
163591
+ var TRUNCATION_SENTINEL = "...[truncated]";
163592
+ var EDIT_REGION_HINT_LEN = 40;
163593
+ var PATH_KEYS = new Set(["filePath", "file_path", "path"]);
163594
+ var DIFF_KEYS = new Set(["oldString", "newString", "content", "old_string", "new_string"]);
163595
+ function safeSlice(str, maxLen) {
163596
+ if (str.length <= maxLen)
163597
+ return str;
163598
+ const lastCharCode = str.charCodeAt(maxLen - 1);
163599
+ if (lastCharCode >= 55296 && lastCharCode <= 56319) {
163600
+ return str.slice(0, maxLen - 1);
163601
+ }
163602
+ return str.slice(0, maxLen);
163603
+ }
163604
+ function isEditTool(name2) {
163605
+ return name2 === "edit" || name2 === "write";
163606
+ }
163607
+ function applyEditMarkerToInput(input) {
163608
+ for (const key of Object.keys(input)) {
163609
+ if (PATH_KEYS.has(key))
163610
+ continue;
163611
+ const value = input[key];
163612
+ if (typeof value !== "string" || !DIFF_KEYS.has(key))
163613
+ continue;
163614
+ if (value.endsWith(TRUNCATION_SENTINEL))
163615
+ continue;
163616
+ input[key] = value.length > EDIT_REGION_HINT_LEN ? `${safeSlice(value, EDIT_REGION_HINT_LEN)}${TRUNCATION_SENTINEL}` : value;
163617
+ }
163618
+ }
163619
+
163486
163620
  // ../plugin/src/hooks/magic-context/tool-drop-target.ts
163487
163621
  var IGNORE_PART_TYPES = new Set([
163488
163622
  "thinking",
@@ -171478,6 +171612,9 @@ function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
171478
171612
  nullCount: row?.null_count ?? 0
171479
171613
  };
171480
171614
  }
171615
+ var RECLAIM_HINT_EXCLUDED_TOOLS = ["todowrite"];
171616
+ var RECLAIM_HINT_MIN_TOKENS = 250;
171617
+ var RECLAIM_HINT_EXCLUDED_LIST = RECLAIM_HINT_EXCLUDED_TOOLS.map((name2) => `'${name2.replace(/'/g, "''")}'`).join(", ");
171481
171618
  function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, limit = 4) {
171482
171619
  if (limit <= 0)
171483
171620
  return [];
@@ -171487,10 +171624,18 @@ function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, li
171487
171624
  WHERE session_id = ? AND status = 'active'
171488
171625
  ORDER BY tag_number DESC LIMIT 1 OFFSET ?
171489
171626
  )` : "";
171490
- const params = protectedTags > 0 ? [sessionId, sessionId, protectedTags - 1, boundedLimit] : [sessionId, boundedLimit];
171627
+ const excludeStateTools = RECLAIM_HINT_EXCLUDED_LIST ? `AND (tool_name IS NULL OR tool_name NOT IN (${RECLAIM_HINT_EXCLUDED_LIST}))` : "";
171628
+ const valueFloor = `AND (
171629
+ (token_count IS NULL AND input_token_count IS NULL)
171630
+ OR (COALESCE(token_count, 0) + COALESCE(input_token_count, 0)) >= ?
171631
+ )`;
171632
+ const params = protectedTags > 0 ? [sessionId, RECLAIM_HINT_MIN_TOKENS, sessionId, protectedTags - 1, boundedLimit] : [sessionId, RECLAIM_HINT_MIN_TOKENS, boundedLimit];
171491
171633
  const rows = db.prepare(`SELECT tag_number, tool_name
171492
171634
  FROM tags
171493
- WHERE session_id = ? AND status = 'active' AND type = 'tool' ${whereProtected}
171635
+ WHERE session_id = ? AND status = 'active' AND type = 'tool'
171636
+ ${excludeStateTools}
171637
+ ${valueFloor}
171638
+ ${whereProtected}
171494
171639
  ORDER BY tag_number ASC, id ASC
171495
171640
  LIMIT ?`).all(...params);
171496
171641
  return rows.filter((row) => typeof row.tag_number === "number").map((row) => ({
@@ -171601,7 +171746,7 @@ function toTagEntry(row) {
171601
171746
  messageId: row.message_id,
171602
171747
  type,
171603
171748
  status,
171604
- dropMode: row.drop_mode === "truncated" ? "truncated" : "full",
171749
+ dropMode: row.drop_mode === "truncated" ? "truncated" : row.drop_mode === "edit_marker" ? "edit_marker" : "full",
171605
171750
  toolName: row.tool_name ?? null,
171606
171751
  inputByteSize: row.input_byte_size ?? 0,
171607
171752
  byteSize: row.byte_size,
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.29.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",