@cortexkit/pi-magic-context 0.22.4 → 0.22.5

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.
@@ -142476,6 +142476,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
142476
142476
  ensureColumn(db, "session_meta", "system_prompt_hash", "TEXT DEFAULT ''");
142477
142477
  ensureColumn(db, "session_meta", "cleared_reasoning_through_tag", "INTEGER DEFAULT 0");
142478
142478
  ensureColumn(db, "session_meta", "stripped_placeholder_ids", "TEXT DEFAULT ''");
142479
+ ensureColumn(db, "session_meta", "stale_reduce_stripped_ids", "TEXT DEFAULT ''");
142479
142480
  ensureColumn(db, "compartments", "start_message_id", "TEXT DEFAULT ''");
142480
142481
  ensureColumn(db, "compartments", "end_message_id", "TEXT DEFAULT ''");
142481
142482
  ensureColumn(db, "memory_embeddings", "model_id", "TEXT");
@@ -142626,6 +142627,7 @@ function healNullTextColumns(db) {
142626
142627
  ["todo_synthetic_state_json", ""],
142627
142628
  ["system_prompt_hash", ""],
142628
142629
  ["stripped_placeholder_ids", ""],
142630
+ ["stale_reduce_stripped_ids", ""],
142629
142631
  ["memory_block_cache", ""],
142630
142632
  ["memory_block_ids", ""],
142631
142633
  ["compaction_marker_state", ""],
@@ -142735,6 +142737,31 @@ function openDatabase(dbPathOrOptions) {
142735
142737
  }
142736
142738
  }
142737
142739
 
142740
+ // ../plugin/src/config/prune-config-leaf.ts
142741
+ function isPlainObject(value) {
142742
+ return typeof value === "object" && value !== null && !Array.isArray(value);
142743
+ }
142744
+ function pruneNestedConfigLeaf(block, relativePath) {
142745
+ if (relativePath.length === 0)
142746
+ return null;
142747
+ const result = { ...block };
142748
+ let cursor = result;
142749
+ for (let i = 0;i < relativePath.length - 1; i++) {
142750
+ const seg = String(relativePath[i]);
142751
+ const child = cursor[seg];
142752
+ if (!isPlainObject(child))
142753
+ return null;
142754
+ const clonedChild = { ...child };
142755
+ cursor[seg] = clonedChild;
142756
+ cursor = clonedChild;
142757
+ }
142758
+ const leaf = String(relativePath[relativePath.length - 1]);
142759
+ if (!(leaf in cursor))
142760
+ return null;
142761
+ delete cursor[leaf];
142762
+ return { block: result, removed: relativePath.map(String).join(".") };
142763
+ }
142764
+
142738
142765
  // src/config/index.ts
142739
142766
  import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
142740
142767
  import { homedir as homedir3 } from "node:os";
@@ -142876,6 +142903,70 @@ function migrateLegacyExperimental(rawConfig, warnings) {
142876
142903
  return patched;
142877
142904
  }
142878
142905
 
142906
+ // ../plugin/src/config/project-security.ts
142907
+ var HIDDEN_AGENT_KEYS = ["historian", "dreamer", "sidekick"];
142908
+ var AGENT_ESCALATION_FIELDS = ["prompt", "permission", "tools", "system_prompt"];
142909
+ function isPlainObject2(value) {
142910
+ return typeof value === "object" && value !== null && !Array.isArray(value);
142911
+ }
142912
+ function stripUnsafeProjectConfigFields(projectRaw) {
142913
+ const warnings = [];
142914
+ if ("auto_update" in projectRaw) {
142915
+ delete projectRaw.auto_update;
142916
+ warnings.push("Ignoring auto_update from project config (security: this setting only honors user-level config).");
142917
+ }
142918
+ for (const agentKey of HIDDEN_AGENT_KEYS) {
142919
+ const block = projectRaw[agentKey];
142920
+ if (!isPlainObject2(block))
142921
+ continue;
142922
+ const removed = [];
142923
+ for (const field of AGENT_ESCALATION_FIELDS) {
142924
+ if (field in block) {
142925
+ delete block[field];
142926
+ removed.push(field);
142927
+ }
142928
+ }
142929
+ if (removed.length > 0) {
142930
+ warnings.push(`Ignoring ${agentKey}.${removed.join("/")} from project config ` + "(security: a repository cannot reprogram or re-permission hidden agents).");
142931
+ }
142932
+ }
142933
+ return warnings;
142934
+ }
142935
+ function normalizeEndpoint(value) {
142936
+ if (typeof value !== "string")
142937
+ return;
142938
+ const trimmed = value.trim().replace(/\/+$/, "");
142939
+ return trimmed.length > 0 ? trimmed.toLowerCase() : undefined;
142940
+ }
142941
+ function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
142942
+ const projectEmbedding = projectRaw.embedding;
142943
+ if (!isPlainObject2(projectEmbedding))
142944
+ return [];
142945
+ const redirectsEndpoint = "endpoint" in projectEmbedding;
142946
+ if (!redirectsEndpoint)
142947
+ return [];
142948
+ const userEmbedding = userRaw?.embedding;
142949
+ if (isPlainObject2(userEmbedding)) {
142950
+ const projectEndpoint = normalizeEndpoint(projectEmbedding.endpoint);
142951
+ const userEndpoint = normalizeEndpoint(userEmbedding.endpoint);
142952
+ if (projectEndpoint !== undefined && projectEndpoint === userEndpoint) {
142953
+ return [];
142954
+ }
142955
+ }
142956
+ const providesOwnKey = typeof projectEmbedding.api_key === "string" && projectEmbedding.api_key.length > 0;
142957
+ if (providesOwnKey)
142958
+ return [];
142959
+ const mergedEmbedding = mergedRaw.embedding;
142960
+ if (!isPlainObject2(mergedEmbedding))
142961
+ return [];
142962
+ if (!("api_key" in mergedEmbedding))
142963
+ return [];
142964
+ delete mergedEmbedding.api_key;
142965
+ return [
142966
+ "Dropped inherited user embedding api_key because project config redirected " + "embedding.endpoint without supplying its own key (security: prevents key " + "exfiltration to a repository-chosen endpoint)."
142967
+ ];
142968
+ }
142969
+
142879
142970
  // ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
142880
142971
  var exports_external = {};
142881
142972
  __export(exports_external, {
@@ -143507,7 +143598,7 @@ __export(exports_util, {
143507
143598
  jsonStringifyReplacer: () => jsonStringifyReplacer,
143508
143599
  joinValues: () => joinValues,
143509
143600
  issue: () => issue,
143510
- isPlainObject: () => isPlainObject,
143601
+ isPlainObject: () => isPlainObject3,
143511
143602
  isObject: () => isObject,
143512
143603
  hexToUint8Array: () => hexToUint8Array,
143513
143604
  getSizableOrigin: () => getSizableOrigin,
@@ -143689,7 +143780,7 @@ var allowsEval = /* @__PURE__ */ cached(() => {
143689
143780
  return false;
143690
143781
  }
143691
143782
  });
143692
- function isPlainObject(o) {
143783
+ function isPlainObject3(o) {
143693
143784
  if (isObject(o) === false)
143694
143785
  return false;
143695
143786
  const ctor = o.constructor;
@@ -143706,7 +143797,7 @@ function isPlainObject(o) {
143706
143797
  return true;
143707
143798
  }
143708
143799
  function shallowClone(o) {
143709
- if (isPlainObject(o))
143800
+ if (isPlainObject3(o))
143710
143801
  return { ...o };
143711
143802
  if (Array.isArray(o))
143712
143803
  return [...o];
@@ -143910,7 +144001,7 @@ function omit(schema, mask) {
143910
144001
  return clone(schema, def);
143911
144002
  }
143912
144003
  function extend(schema, shape) {
143913
- if (!isPlainObject(shape)) {
144004
+ if (!isPlainObject3(shape)) {
143914
144005
  throw new Error("Invalid input to extend: expected a plain object");
143915
144006
  }
143916
144007
  const checks = schema._zod.def.checks;
@@ -143933,7 +144024,7 @@ function extend(schema, shape) {
143933
144024
  return clone(schema, def);
143934
144025
  }
143935
144026
  function safeExtend(schema, shape) {
143936
- if (!isPlainObject(shape)) {
144027
+ if (!isPlainObject3(shape)) {
143937
144028
  throw new Error("Invalid input to safeExtend: expected a plain object");
143938
144029
  }
143939
144030
  const def = mergeDefs(schema._zod.def, {
@@ -146276,7 +146367,7 @@ function mergeValues(a, b) {
146276
146367
  if (a instanceof Date && b instanceof Date && +a === +b) {
146277
146368
  return { valid: true, data: a };
146278
146369
  }
146279
- if (isPlainObject(a) && isPlainObject(b)) {
146370
+ if (isPlainObject3(a) && isPlainObject3(b)) {
146280
146371
  const bKeys = Object.keys(b);
146281
146372
  const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
146282
146373
  const newObj = { ...a, ...b };
@@ -146462,7 +146553,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
146462
146553
  $ZodType.init(inst, def);
146463
146554
  inst._zod.parse = (payload, ctx) => {
146464
146555
  const input = payload.value;
146465
- if (!isPlainObject(input)) {
146556
+ if (!isPlainObject3(input)) {
146466
146557
  payload.issues.push({
146467
146558
  expected: "record",
146468
146559
  code: "invalid_type",
@@ -157416,6 +157507,21 @@ function stripJsonComments(content) {
157416
157507
  // ../plugin/src/config/variable.ts
157417
157508
  var ENV_PATTERN = /\{env:([^}]+)\}/g;
157418
157509
  var FILE_PATTERN = /\{file:([^}]+)\}/g;
157510
+ function sensitiveFilePathReason(resolvedPath) {
157511
+ const home = homedir2();
157512
+ const sensitiveDirs = [
157513
+ { dir: resolve2(home, ".ssh"), label: "SSH keys" },
157514
+ { dir: resolve2(home, ".aws"), label: "AWS credentials" },
157515
+ { dir: resolve2(home, ".gnupg"), label: "GnuPG keyring" },
157516
+ { dir: resolve2(home, ".config", "gh"), label: "GitHub CLI auth" }
157517
+ ];
157518
+ for (const { dir, label } of sensitiveDirs) {
157519
+ if (resolvedPath === dir || resolvedPath.startsWith(`${dir}/`)) {
157520
+ return label;
157521
+ }
157522
+ }
157523
+ return null;
157524
+ }
157419
157525
  function substituteConfigVariables(input) {
157420
157526
  const warnings = [];
157421
157527
  let text = input.text;
@@ -157469,6 +157575,10 @@ function substituteConfigVariables(input) {
157469
157575
  } else if (!isAbsolute(filePath)) {
157470
157576
  filePath = resolve2(configDir, filePath);
157471
157577
  }
157578
+ const sensitiveReason = sensitiveFilePathReason(filePath);
157579
+ if (sensitiveReason) {
157580
+ warnings.push(`${token} resolves to a sensitive path (${sensitiveReason}: ${filePath}); ` + "inlining its contents into config — make sure this is intentional.");
157581
+ }
157472
157582
  if (!existsSync4(filePath)) {
157473
157583
  warnings.push(`File not found for ${token} (resolved to ${filePath}); using empty string`);
157474
157584
  continue;
@@ -157507,7 +157617,8 @@ function loadConfigFile(path3, scope) {
157507
157617
  const rawText = readFileSync3(path3, "utf-8");
157508
157618
  const substituted = substituteConfigVariables({
157509
157619
  text: rawText,
157510
- configPath: path3
157620
+ configPath: path3,
157621
+ isProjectConfig: scope === "project"
157511
157622
  });
157512
157623
  return {
157513
157624
  path: path3,
@@ -157549,14 +157660,14 @@ function redactConfigValue(value) {
157549
157660
  }
157550
157661
  return typeof value;
157551
157662
  }
157552
- function isPlainObject2(value) {
157663
+ function isPlainObject4(value) {
157553
157664
  return typeof value === "object" && value !== null && !Array.isArray(value);
157554
157665
  }
157555
157666
  function mergeRawConfigs(base, override) {
157556
157667
  const merged = { ...base };
157557
157668
  for (const [key, overrideValue] of Object.entries(override)) {
157558
157669
  const baseValue = merged[key];
157559
- merged[key] = isPlainObject2(baseValue) && isPlainObject2(overrideValue) ? mergeRawConfigs(baseValue, overrideValue) : overrideValue;
157670
+ merged[key] = isPlainObject4(baseValue) && isPlainObject4(overrideValue) ? mergeRawConfigs(baseValue, overrideValue) : overrideValue;
157560
157671
  }
157561
157672
  return merged;
157562
157673
  }
@@ -157570,10 +157681,15 @@ function parsePiConfig(rawConfig, recoveredTopLevelKeys = []) {
157570
157681
  }
157571
157682
  const defaults = MagicContextConfigSchema.parse({});
157572
157683
  const errorPaths = new Set;
157684
+ const issuePathsByKey = new Map;
157573
157685
  for (const issue2 of parsed.error.issues) {
157574
157686
  const topKey = issue2.path[0];
157575
157687
  if (topKey !== undefined) {
157576
- errorPaths.add(String(topKey));
157688
+ const key = String(topKey);
157689
+ errorPaths.add(key);
157690
+ const paths = issuePathsByKey.get(key) ?? [];
157691
+ paths.push([...issue2.path]);
157692
+ issuePathsByKey.set(key, paths);
157577
157693
  }
157578
157694
  }
157579
157695
  const patched = { ...migrated };
@@ -157581,11 +157697,32 @@ function parsePiConfig(rawConfig, recoveredTopLevelKeys = []) {
157581
157697
  for (const key of errorPaths) {
157582
157698
  recoveredTopLevelKeys.push(key);
157583
157699
  const isAgentConfig = key === "historian" || key === "dreamer" || key === "sidekick";
157584
- delete patched[key];
157585
157700
  if (isAgentConfig) {
157701
+ delete patched[key];
157586
157702
  warnings.push(`"${key}": invalid agent configuration, ignoring. Check your magic-context.jsonc.`);
157587
157703
  continue;
157588
157704
  }
157705
+ const issuePaths = issuePathsByKey.get(key) ?? [];
157706
+ const rawValue = migrated[key];
157707
+ const allNested = issuePaths.length > 0 && issuePaths.every((p) => p.length >= 2) && typeof rawValue === "object" && rawValue !== null && !Array.isArray(rawValue);
157708
+ if (allNested) {
157709
+ let prunedBlock = {
157710
+ ...rawValue
157711
+ };
157712
+ const prunedLeaves = [];
157713
+ for (const p of issuePaths) {
157714
+ const relative = p.slice(1);
157715
+ const result = pruneNestedConfigLeaf(prunedBlock, relative);
157716
+ if (result) {
157717
+ prunedBlock = result.block;
157718
+ prunedLeaves.push(result.removed);
157719
+ }
157720
+ }
157721
+ patched[key] = prunedBlock;
157722
+ warnings.push(`"${key}": invalid nested field(s) ${prunedLeaves.map((l) => `"${l}"`).join(", ")}, using defaults for those.`);
157723
+ continue;
157724
+ }
157725
+ delete patched[key];
157589
157726
  const defaultValue = defaults[key];
157590
157727
  warnings.push(`"${key}": invalid value (${redactConfigValue(rawConfig[key])}), using default ${JSON.stringify(defaultValue)}.`);
157591
157728
  }
@@ -157618,10 +157755,22 @@ function loadPiConfig(opts = {}) {
157618
157755
  return 0;
157619
157756
  return a.scope === "user" ? -1 : 1;
157620
157757
  });
157758
+ const userRaw = mergeFiles.find((f) => f.scope === "user")?.config;
157621
157759
  for (const loaded of mergeFiles) {
157622
157760
  const prefix = loaded.scope === "user" ? "[user config]" : "[project config]";
157623
157761
  warnings.push(...loaded.warnings.map((warning) => `${prefix} ${warning}`));
157624
- rawConfig = mergeRawConfigs(rawConfig, loaded.config);
157762
+ if (loaded.scope === "project") {
157763
+ const projectRaw = { ...loaded.config };
157764
+ for (const warning of stripUnsafeProjectConfigFields(projectRaw)) {
157765
+ warnings.push(`${prefix} ${warning}`);
157766
+ }
157767
+ rawConfig = mergeRawConfigs(rawConfig, projectRaw);
157768
+ for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, rawConfig, userRaw)) {
157769
+ warnings.push(`${prefix} ${warning}`);
157770
+ }
157771
+ } else {
157772
+ rawConfig = mergeRawConfigs(rawConfig, loaded.config);
157773
+ }
157625
157774
  }
157626
157775
  const parsed = parsePiConfig(rawConfig);
157627
157776
  warnings.push(...parsed.warnings.map((warning) => `[merged config] ${warning}`));
@@ -157696,10 +157845,22 @@ function loadPiConfigDetailed(opts = {}) {
157696
157845
  return 0;
157697
157846
  return a.scope === "user" ? -1 : 1;
157698
157847
  });
157848
+ const userRaw = mergeFiles.find((f) => f.scope === "user")?.config;
157699
157849
  for (const loaded of mergeFiles) {
157700
157850
  const prefix = loaded.scope === "user" ? "[user config]" : "[project config]";
157701
157851
  warnings.push(...loaded.warnings.map((warning) => `${prefix} ${warning}`));
157702
- rawConfig = mergeRawConfigs(rawConfig, loaded.config);
157852
+ if (loaded.scope === "project") {
157853
+ const projectRaw = { ...loaded.config };
157854
+ for (const warning of stripUnsafeProjectConfigFields(projectRaw)) {
157855
+ warnings.push(`${prefix} ${warning}`);
157856
+ }
157857
+ rawConfig = mergeRawConfigs(rawConfig, projectRaw);
157858
+ for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, rawConfig, userRaw)) {
157859
+ warnings.push(`${prefix} ${warning}`);
157860
+ }
157861
+ } else {
157862
+ rawConfig = mergeRawConfigs(rawConfig, loaded.config);
157863
+ }
157703
157864
  }
157704
157865
  const recoveredTopLevelKeys = [];
157705
157866
  const parsed = parsePiConfig(rawConfig, recoveredTopLevelKeys);
@@ -157754,7 +157915,7 @@ function computeNormalizedHash(content) {
157754
157915
  }
157755
157916
 
157756
157917
  // ../plugin/src/features/magic-context/memory/embedding-identity.ts
157757
- function normalizeEndpoint(endpoint) {
157918
+ function normalizeEndpoint2(endpoint) {
157758
157919
  return endpoint?.trim().replace(/\/+$/, "") ?? "";
157759
157920
  }
157760
157921
  function getEmbeddingProviderIdentity(config2) {
@@ -157764,7 +157925,7 @@ function getEmbeddingProviderIdentity(config2) {
157764
157925
  const identityInput = config2.provider === "openai-compatible" ? {
157765
157926
  provider: "openai-compatible",
157766
157927
  model: config2.model.trim(),
157767
- endpoint: normalizeEndpoint(config2.endpoint),
157928
+ endpoint: normalizeEndpoint2(config2.endpoint),
157768
157929
  apiKeyPresent: Boolean(config2.api_key?.trim()),
157769
157930
  inputType: config2.input_type?.trim() || ""
157770
157931
  } : {
@@ -158115,8 +158276,61 @@ class LocalEmbeddingProvider {
158115
158276
  }
158116
158277
  }
158117
158278
 
158279
+ // ../plugin/src/features/magic-context/memory/embedding-ssrf.ts
158280
+ var METADATA_HOSTNAMES = new Set(["metadata.google.internal", "metadata.goog"]);
158281
+ var IPV6_METADATA_HOSTS = new Set(["fd00:ec2::254"]);
158282
+ function isLinkLocalIpv4(host) {
158283
+ return /^169\.254\.\d{1,3}\.\d{1,3}$/.test(host);
158284
+ }
158285
+ function ipv4FromMappedIpv6(host) {
158286
+ const m = /^::ffff:(.+)$/.exec(host);
158287
+ if (!m)
158288
+ return null;
158289
+ const tail = m[1];
158290
+ if (/^\d{1,3}(\.\d{1,3}){3}$/.test(tail))
158291
+ return tail;
158292
+ const hex3 = /^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(tail);
158293
+ if (hex3) {
158294
+ const hi = Number.parseInt(hex3[1], 16);
158295
+ const lo = Number.parseInt(hex3[2], 16);
158296
+ if (Number.isNaN(hi) || Number.isNaN(lo))
158297
+ return null;
158298
+ return `${hi >> 8 & 255}.${hi & 255}.${lo >> 8 & 255}.${lo & 255}`;
158299
+ }
158300
+ return null;
158301
+ }
158302
+ function blockedEmbeddingEndpointReason(endpoint) {
158303
+ const trimmed = endpoint.trim();
158304
+ if (trimmed.length === 0)
158305
+ return null;
158306
+ let url2;
158307
+ try {
158308
+ url2 = new URL(trimmed);
158309
+ } catch {
158310
+ return `embedding endpoint is not a valid URL: ${trimmed}`;
158311
+ }
158312
+ const host = url2.hostname.toLowerCase().replace(/^\[/, "").replace(/\]$/, "");
158313
+ if (METADATA_HOSTNAMES.has(host)) {
158314
+ return `embedding endpoint host ${host} is a cloud metadata service (blocked)`;
158315
+ }
158316
+ if (IPV6_METADATA_HOSTS.has(host)) {
158317
+ return `embedding endpoint host ${host} is the AWS IPv6 metadata service (blocked)`;
158318
+ }
158319
+ if (isLinkLocalIpv4(host)) {
158320
+ return `embedding endpoint host ${host} is link-local / cloud metadata (blocked)`;
158321
+ }
158322
+ const mappedV4 = ipv4FromMappedIpv6(host);
158323
+ if (mappedV4 && isLinkLocalIpv4(mappedV4)) {
158324
+ return `embedding endpoint host ${host} (IPv4-mapped ${mappedV4}) is link-local / cloud metadata (blocked)`;
158325
+ }
158326
+ if (host.startsWith("fe80:")) {
158327
+ return `embedding endpoint host ${host} is link-local / cloud metadata (blocked)`;
158328
+ }
158329
+ return null;
158330
+ }
158331
+
158118
158332
  // ../plugin/src/features/magic-context/memory/embedding-openai.ts
158119
- function normalizeEndpoint2(endpoint) {
158333
+ function normalizeEndpoint3(endpoint) {
158120
158334
  return endpoint?.trim().replace(/\/+$/, "") ?? "";
158121
158335
  }
158122
158336
  var FAILURE_THRESHOLD = 3;
@@ -158137,7 +158351,7 @@ class OpenAICompatibleEmbeddingProvider {
158137
158351
  openLogged = false;
158138
158352
  halfOpenProbeInFlight = false;
158139
158353
  constructor(options) {
158140
- this.endpoint = normalizeEndpoint2(options.endpoint);
158354
+ this.endpoint = normalizeEndpoint3(options.endpoint);
158141
158355
  this.model = options.model?.trim() ?? "";
158142
158356
  this.apiKey = options.apiKey?.trim() ?? "";
158143
158357
  this.inputType = options.inputType?.trim() ?? "";
@@ -158157,6 +158371,12 @@ class OpenAICompatibleEmbeddingProvider {
158157
158371
  this.initialized = false;
158158
158372
  return false;
158159
158373
  }
158374
+ const blockedReason = blockedEmbeddingEndpointReason(this.endpoint);
158375
+ if (blockedReason) {
158376
+ log(`[magic-context] embedding endpoint blocked: ${blockedReason}`);
158377
+ this.initialized = false;
158378
+ return false;
158379
+ }
158160
158380
  this.initialized = true;
158161
158381
  return true;
158162
158382
  }
@@ -158202,6 +158422,7 @@ class OpenAICompatibleEmbeddingProvider {
158202
158422
  ...this.inputType ? { input_type: this.inputType } : {},
158203
158423
  ...this.truncate ? { truncate: this.truncate } : {}
158204
158424
  }),
158425
+ redirect: "error",
158205
158426
  signal: internalController.signal
158206
158427
  });
158207
158428
  if (!response.ok) {
@@ -158681,11 +158902,15 @@ function resolveEmbeddingConfig(config2) {
158681
158902
  }
158682
158903
  if (config2.provider === "openai-compatible") {
158683
158904
  const apiKey = config2.api_key?.trim();
158905
+ const inputType = config2.input_type?.trim();
158906
+ const truncate = config2.truncate?.trim();
158684
158907
  return {
158685
158908
  provider: "openai-compatible",
158686
158909
  model: config2.model.trim(),
158687
158910
  endpoint: config2.endpoint.trim(),
158688
- ...apiKey ? { api_key: apiKey } : {}
158911
+ ...apiKey ? { api_key: apiKey } : {},
158912
+ ...inputType ? { input_type: inputType } : {},
158913
+ ...truncate ? { truncate } : {}
158689
158914
  };
158690
158915
  }
158691
158916
  return { provider: "off" };
@@ -158701,7 +158926,9 @@ function createProvider(config2) {
158701
158926
  return new OpenAICompatibleEmbeddingProvider({
158702
158927
  endpoint: config2.endpoint,
158703
158928
  model: config2.model,
158704
- apiKey: config2.api_key
158929
+ apiKey: config2.api_key,
158930
+ inputType: config2.input_type,
158931
+ truncate: config2.truncate
158705
158932
  });
158706
158933
  }
158707
158934
  return new LocalEmbeddingProvider(config2.model);
@@ -159140,6 +159367,19 @@ function hasGitDir(canonical) {
159140
159367
  return false;
159141
159368
  }
159142
159369
  }
159370
+ function normalizeStoredProjectPath(rawOrStored) {
159371
+ if (rawOrStored.startsWith("git:") || rawOrStored.startsWith("dir:")) {
159372
+ return rawOrStored;
159373
+ }
159374
+ try {
159375
+ return resolveProjectIdentity(rawOrStored);
159376
+ } catch {
159377
+ return directoryFallback(rawOrStored);
159378
+ }
159379
+ }
159380
+ function storedPathBelongsToIdentity(storedProjectPath, projectIdentity) {
159381
+ return storedProjectPath === projectIdentity || normalizeStoredProjectPath(storedProjectPath) === projectIdentity;
159382
+ }
159143
159383
 
159144
159384
  // ../plugin/src/plugin/embedding-bootstrap-helpers.ts
159145
159385
  import { createHash as createHash5 } from "node:crypto";
@@ -159147,10 +159387,20 @@ var EMBEDDING_AFFECTING_KEYS = new Set([
159147
159387
  "embedding.api_key",
159148
159388
  "embedding.endpoint",
159149
159389
  "embedding.model",
159150
- "embedding.provider"
159390
+ "embedding.provider",
159391
+ "embedding.input_type",
159392
+ "embedding.truncate"
159151
159393
  ]);
159152
159394
  var EMBEDDING_AFFECTING_TOP_LEVEL_KEYS = new Set(["embedding", "memory", "experimental"]);
159153
- var EMBEDDING_WARNING_TERMS = ["api_key", "endpoint", "model", "provider", "embedding"];
159395
+ var EMBEDDING_WARNING_TERMS = [
159396
+ "api_key",
159397
+ "endpoint",
159398
+ "model",
159399
+ "provider",
159400
+ "embedding",
159401
+ "input_type",
159402
+ "truncate"
159403
+ ];
159154
159404
  var loggedFailureSignatures = new Map;
159155
159405
  function sha256Prefix2(value, length = 16) {
159156
159406
  return createHash5("sha256").update(value).digest("hex").slice(0, length);
@@ -159586,12 +159836,6 @@ function isCompartmentRow(row) {
159586
159836
  const candidate = row;
159587
159837
  return typeof candidate.id === "number" && typeof candidate.session_id === "string" && typeof candidate.sequence === "number" && typeof candidate.start_message === "number" && typeof candidate.end_message === "number" && typeof candidate.start_message_id === "string" && typeof candidate.end_message_id === "string" && typeof candidate.title === "string" && typeof candidate.content === "string" && isStringOrNullish(candidate.p1) && isStringOrNullish(candidate.p2) && isStringOrNullish(candidate.p3) && isStringOrNullish(candidate.p4) && isNumberOrNullish(candidate.importance) && isStringOrNullish(candidate.episode_type) && isNumberOrNullish(candidate.legacy) && typeof candidate.created_at === "number";
159588
159838
  }
159589
- function isSessionFactRow(row) {
159590
- if (row === null || typeof row !== "object")
159591
- return false;
159592
- const candidate = row;
159593
- return typeof candidate.id === "number" && typeof candidate.session_id === "string" && typeof candidate.category === "string" && typeof candidate.content === "string" && typeof candidate.created_at === "number" && typeof candidate.updated_at === "number";
159594
- }
159595
159839
  function insertCompartmentRows(db, sessionId, compartments, now) {
159596
159840
  const stmt = getInsertCompartmentStatement(db);
159597
159841
  for (const compartment of compartments) {
@@ -159620,16 +159864,6 @@ function toCompartment(row) {
159620
159864
  createdAt: row.created_at
159621
159865
  };
159622
159866
  }
159623
- function toSessionFact(row) {
159624
- return {
159625
- id: row.id,
159626
- sessionId: row.session_id,
159627
- category: row.category,
159628
- content: row.content,
159629
- createdAt: row.created_at,
159630
- updatedAt: row.updated_at
159631
- };
159632
- }
159633
159867
  function getCompartments(db, sessionId) {
159634
159868
  const rows = db.prepare("SELECT * FROM compartments WHERE session_id = ? ORDER BY sequence ASC").all(sessionId).filter(isCompartmentRow);
159635
159869
  return rows.map(toCompartment);
@@ -159650,10 +159884,6 @@ function appendCompartments(db, sessionId, compartments) {
159650
159884
  insertCompartmentRows(db, sessionId, compartments, now);
159651
159885
  })();
159652
159886
  }
159653
- function getSessionFacts(db, sessionId) {
159654
- const rows = db.prepare("SELECT * FROM session_facts WHERE session_id = ? ORDER BY category ASC, id ASC").all(sessionId).filter(isSessionFactRow);
159655
- return rows.map(toSessionFact);
159656
- }
159657
159887
  function saveRecompStagingPass(db, sessionId, passNumber, compartments, facts) {
159658
159888
  const now = Date.now();
159659
159889
  db.transaction(() => {
@@ -159697,14 +159927,6 @@ function getRecompStaging(db, sessionId) {
159697
159927
  lastEndMessage: lastEnd
159698
159928
  };
159699
159929
  }
159700
- function invalidateAllMemoryBlockCaches(db) {
159701
- try {
159702
- const rows = db.prepare("SELECT session_id FROM session_meta").all();
159703
- for (const row of rows) {
159704
- clearCachedM0M1(db, row.session_id);
159705
- }
159706
- } catch {}
159707
- }
159708
159930
  function clearRecompStaging(db, sessionId) {
159709
159931
  db.transaction(() => {
159710
159932
  db.prepare("DELETE FROM recomp_compartments WHERE session_id = ?").run(sessionId);
@@ -165394,7 +165616,7 @@ function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal,
165394
165616
  }
165395
165617
  // ../plugin/src/features/magic-context/project-docs-hash.ts
165396
165618
  import { createHash as createHash6 } from "node:crypto";
165397
- import { readFileSync as readFileSync4, statSync as statSync2 } from "node:fs";
165619
+ import { lstatSync, readFileSync as readFileSync4, statSync as statSync2 } from "node:fs";
165398
165620
  import path4 from "node:path";
165399
165621
  var PROJECT_DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
165400
165622
  var PROJECT_DOCS_DELIMITER = `
@@ -165402,6 +165624,7 @@ var PROJECT_DOCS_DELIMITER = `
165402
165624
  ---
165403
165625
 
165404
165626
  `;
165627
+ var MAX_PROJECT_DOC_BYTES = 256 * 1024;
165405
165628
  var docsCache = new Map;
165406
165629
  function canonicalizeDocContent(raw) {
165407
165630
  return raw.replace(/^\uFEFF/, "").replace(/\r\n/g, `
@@ -165411,9 +165634,10 @@ function canonicalizeDocContent(raw) {
165411
165634
  }
165412
165635
  function fingerprintFile(filePath) {
165413
165636
  try {
165414
- const stat2 = statSync2(filePath);
165637
+ const stat2 = lstatSync(filePath);
165638
+ const isReadableDoc = stat2.isFile() && stat2.size <= MAX_PROJECT_DOC_BYTES;
165415
165639
  return {
165416
- exists: stat2.isFile(),
165640
+ exists: isReadableDoc,
165417
165641
  mtimeMs: stat2.mtimeMs,
165418
165642
  size: stat2.size
165419
165643
  };
@@ -165456,6 +165680,16 @@ function readCanonicalPieces(projectDirectory, files) {
165456
165680
  if (!fingerprint?.exists) {
165457
165681
  continue;
165458
165682
  }
165683
+ let safeToRead = false;
165684
+ try {
165685
+ const st = lstatSync(filePath);
165686
+ safeToRead = st.isFile() && st.size <= MAX_PROJECT_DOC_BYTES;
165687
+ } catch {
165688
+ safeToRead = false;
165689
+ }
165690
+ if (!safeToRead) {
165691
+ continue;
165692
+ }
165459
165693
  const canonicalContent = canonicalizeDocContent(readFileSync4(filePath, "utf8"));
165460
165694
  hashPieces.push(`file:${filename}
165461
165695
  ${canonicalContent}`);
@@ -165776,7 +166010,8 @@ function casUpdateJsonArrayColumn(db, sessionId, column, validator, mutate, opti
165776
166010
  }
165777
166011
  for (let attempt = 0;attempt < CAS_RETRY_LIMIT; attempt += 1) {
165778
166012
  const row = db.prepare(`SELECT ${column} FROM session_meta WHERE session_id = ?`).get(sessionId);
165779
- const currentBlob = row?.[column] ?? "[]";
166013
+ const rawCurrent = row?.[column] ?? null;
166014
+ const currentBlob = rawCurrent ?? "[]";
165780
166015
  const current = parseJsonArray(currentBlob, validator);
165781
166016
  const next = mutate(current);
165782
166017
  if (next === null)
@@ -165784,7 +166019,7 @@ function casUpdateJsonArrayColumn(db, sessionId, column, validator, mutate, opti
165784
166019
  const nextBlob = stableStringify(next);
165785
166020
  if (nextBlob === currentBlob)
165786
166021
  return true;
165787
- const result = db.prepare(`UPDATE session_meta SET ${column} = ? WHERE session_id = ? AND ${column} = ?`).run(nextBlob, sessionId, currentBlob);
166022
+ const result = db.prepare(`UPDATE session_meta SET ${column} = ? WHERE session_id = ? AND ${column} IS ?`).run(nextBlob, sessionId, rawCurrent);
165788
166023
  if (result.changes > 0)
165789
166024
  return true;
165790
166025
  }
@@ -165913,14 +166148,16 @@ function getHistorianFailureState(db, sessionId) {
165913
166148
  };
165914
166149
  }
165915
166150
  function incrementHistorianFailure(db, sessionId, error51) {
166151
+ let nextCount = 1;
165916
166152
  db.transaction(() => {
165917
166153
  ensureSessionMetaRow(db, sessionId);
165918
166154
  const current = getHistorianFailureState(db, sessionId);
165919
- const nextCount = current.failureCount + 1;
166155
+ nextCount = current.failureCount + 1;
165920
166156
  db.prepare("UPDATE session_meta SET historian_failure_count = ?, historian_last_error = ?, historian_last_failure_at = ? WHERE session_id = ?").run(nextCount, error51, Date.now(), sessionId);
165921
166157
  const reason = error51.replace(/\s+/g, " ").trim().slice(0, 300);
165922
166158
  sessionLog(sessionId, `historian failure recorded: count=${nextCount} reason="${reason}"`);
165923
166159
  })();
166160
+ return nextCount;
165924
166161
  }
165925
166162
  function clearHistorianFailureState(db, sessionId) {
165926
166163
  db.transaction(() => {
@@ -165989,10 +166226,39 @@ function getStrippedPlaceholderIds(db, sessionId) {
165989
166226
  } catch {}
165990
166227
  return new Set;
165991
166228
  }
165992
- function setStrippedPlaceholderIds(db, sessionId, ids) {
166229
+ function applyStrippedPlaceholderDelta(db, sessionId, delta) {
166230
+ const add = delta.add ? [...delta.add] : [];
166231
+ const remove = delta.remove ? [...delta.remove] : [];
166232
+ if (add.length === 0 && remove.length === 0)
166233
+ return true;
165993
166234
  ensureSessionMetaRow(db, sessionId);
165994
- const json2 = ids.size > 0 ? JSON.stringify([...ids]) : "";
165995
- db.prepare("UPDATE session_meta SET stripped_placeholder_ids = ? WHERE session_id = ?").run(json2, sessionId);
166235
+ for (let attempt = 0;attempt < CAS_RETRY_LIMIT; attempt += 1) {
166236
+ const row = db.prepare("SELECT stripped_placeholder_ids FROM session_meta WHERE session_id = ?").get(sessionId);
166237
+ const rawStored = row ? row.stripped_placeholder_ids ?? null : null;
166238
+ const current = new Set(parseStrippedBlob(rawStored));
166239
+ for (const id of add)
166240
+ current.add(id);
166241
+ for (const id of remove)
166242
+ current.delete(id);
166243
+ const nextBlob = current.size > 0 ? JSON.stringify([...current]) : "";
166244
+ if (nextBlob === (rawStored ?? ""))
166245
+ return true;
166246
+ const result = db.prepare("UPDATE session_meta SET stripped_placeholder_ids = ? WHERE session_id = ? AND stripped_placeholder_ids IS ?").run(nextBlob, sessionId, rawStored);
166247
+ if (result.changes > 0)
166248
+ return true;
166249
+ }
166250
+ sessionLog(sessionId, `stripped_placeholder_ids CAS: ${CAS_RETRY_LIMIT} retries exhausted`);
166251
+ return false;
166252
+ }
166253
+ function parseStrippedBlob(raw) {
166254
+ if (!raw || raw.length === 0)
166255
+ return [];
166256
+ try {
166257
+ const parsed = JSON.parse(raw);
166258
+ if (Array.isArray(parsed))
166259
+ return parsed.filter((v) => typeof v === "string");
166260
+ } catch {}
166261
+ return [];
165996
166262
  }
165997
166263
  function isPendingCompactionMarker(value) {
165998
166264
  return typeof value === "object" && value !== null && typeof value.ordinal === "number" && typeof value.endMessageId === "string" && typeof value.publishedAt === "number";
@@ -166897,7 +167163,6 @@ function createCtxMemoryTool(deps) {
166897
167163
  sourceType: dreamerAllowed ? "dreamer" : "agent"
166898
167164
  });
166899
167165
  queueEmbedding({ deps, projectIdentity, memoryId: memory2.id, content });
166900
- invalidateAllMemoryBlockCaches(deps.db);
166901
167166
  return ok2(`Saved memory [ID: ${memory2.id}] in ${rawCategory}.`);
166902
167167
  }
166903
167168
  if (params.action === "delete") {
@@ -166905,7 +167170,7 @@ function createCtxMemoryTool(deps) {
166905
167170
  return err2("Error: 'id' is required when action is 'delete'.");
166906
167171
  }
166907
167172
  const memory2 = getMemoryById(deps.db, params.id);
166908
- if (!memory2 || memory2.projectPath !== projectIdentity) {
167173
+ if (!memory2 || !storedPathBelongsToIdentity(memory2.projectPath, projectIdentity)) {
166909
167174
  return err2(`Error: Memory with ID ${params.id} was not found.`);
166910
167175
  }
166911
167176
  deps.db.transaction(() => {
@@ -166934,7 +167199,7 @@ function createCtxMemoryTool(deps) {
166934
167199
  return err2("Error: 'content' is required when action is 'update'.");
166935
167200
  }
166936
167201
  const memory2 = getMemoryById(deps.db, params.id);
166937
- if (!memory2 || memory2.projectPath !== projectIdentity) {
167202
+ if (!memory2 || !storedPathBelongsToIdentity(memory2.projectPath, projectIdentity)) {
166938
167203
  return err2(`Error: Memory with ID ${params.id} was not found.`);
166939
167204
  }
166940
167205
  const normalizedHash = computeNormalizedHash(content);
@@ -166968,9 +167233,6 @@ function createCtxMemoryTool(deps) {
166968
167233
  if (sourceMemories.length !== ids.length) {
166969
167234
  return err2("Error: One or more source memories were not found.");
166970
167235
  }
166971
- if (sourceMemories.some((memory2) => memory2.projectPath !== projectIdentity)) {
166972
- return err2("Error: All memories to merge must belong to the current project.");
166973
- }
166974
167236
  const requestedCategory = params.category?.trim();
166975
167237
  if (requestedCategory && !isMemoryCategory2(requestedCategory)) {
166976
167238
  return err2(`Error: Unknown memory category '${requestedCategory}'. Valid: ${CATEGORY_PRIORITY.join(", ")}`);
@@ -167029,7 +167291,7 @@ function createCtxMemoryTool(deps) {
167029
167291
  }
167030
167292
  supersededMemory(deps.db, memory2.id, canonicalMemory.id);
167031
167293
  queueMemoryMutation(deps.db, {
167032
- projectPath: memory2.projectPath,
167294
+ projectPath: normalizeStoredProjectPath(memory2.projectPath),
167033
167295
  mutationType: "superseded",
167034
167296
  targetMemoryId: memory2.id,
167035
167297
  supersededById: canonicalMemory.id
@@ -167037,7 +167299,7 @@ function createCtxMemoryTool(deps) {
167037
167299
  }
167038
167300
  if (canonicalExisting && canonicalContentChanged) {
167039
167301
  queueMemoryMutation(deps.db, {
167040
- projectPath: canonicalMemory.projectPath,
167302
+ projectPath: normalizeStoredProjectPath(canonicalMemory.projectPath),
167041
167303
  mutationType: "update",
167042
167304
  targetMemoryId: canonicalMemory.id,
167043
167305
  category,
@@ -167059,7 +167321,7 @@ function createCtxMemoryTool(deps) {
167059
167321
  return err2("Error: 'id' is required when action is 'archive'.");
167060
167322
  }
167061
167323
  const memory2 = getMemoryById(deps.db, params.id);
167062
- if (!memory2 || memory2.projectPath !== projectIdentity) {
167324
+ if (!memory2 || !storedPathBelongsToIdentity(memory2.projectPath, projectIdentity)) {
167063
167325
  return err2(`Error: Memory with ID ${params.id} was not found.`);
167064
167326
  }
167065
167327
  deps.db.transaction(() => {
@@ -167321,7 +167583,7 @@ ${smartNotes.map(formatNoteLine).join(`
167321
167583
  // ../plugin/src/features/magic-context/range-parser.ts
167322
167584
  function parseRangeString(input) {
167323
167585
  const maxRangeElements = 1000;
167324
- const trimmed = input.trim();
167586
+ const trimmed = input.replace(/§/g, "").trim();
167325
167587
  if (trimmed === "") {
167326
167588
  throw new Error("Range string must not be empty");
167327
167589
  }
@@ -168499,6 +168761,9 @@ function escapeXmlAttr2(s) {
168499
168761
  function escapeXmlContent2(s) {
168500
168762
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
168501
168763
  }
168764
+ function isTieredRow(c) {
168765
+ return typeof c.p1 === "string" && c.p1.length > 0;
168766
+ }
168502
168767
  function tierBody(c, tier2) {
168503
168768
  const tiers = [c.p1, c.p2, c.p3, c.p4];
168504
168769
  const requested = tiers[tier2 - 1];
@@ -168528,12 +168793,13 @@ function renderOneCompartment(c, tier2) {
168528
168793
  const baseAttrs = `start="${c.startMessage}" end="${c.endMessage}" title="${escapeXmlAttr2(c.title)}"`;
168529
168794
  if (tier2 >= 5)
168530
168795
  return "";
168531
- if (c.legacy === 1) {
168532
- if (tier2 >= 4)
168796
+ if (c.legacy === 1 || !isTieredRow(c)) {
168797
+ const flat = (c.content ?? "").trim();
168798
+ if (tier2 >= 4 || flat.length === 0)
168533
168799
  return `<compartment ${baseAttrs} />`;
168534
168800
  return [
168535
168801
  `<compartment ${baseAttrs}>`,
168536
- escapeXmlContent2(legacyBodyForTier(c.content, tier2)),
168802
+ escapeXmlContent2(legacyBodyForTier(flat, tier2)),
168537
168803
  "</compartment>"
168538
168804
  ].join(`
168539
168805
  `);
@@ -169080,6 +169346,7 @@ function createCtxSearchTool(deps) {
169080
169346
  const embeddingEnabled = snapshot ? snapshot.enabled || snapshot.gitCommitEnabled : deps.embeddingEnabled;
169081
169347
  const gitCommitsEnabled = snapshot?.gitCommitEnabled ?? deps.gitCommitsEnabled ?? false;
169082
169348
  const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
169349
+ const messageOrdinalCutoff = lastCompartmentEnd >= 0 ? lastCompartmentEnd : 0;
169083
169350
  const visibleMemoryIds = getVisibleMemoryIds(deps.db, sessionId);
169084
169351
  const results = await unifiedSearch(deps.db, sessionId, projectIdentity, query, {
169085
169352
  limit: normalizeLimit3(params.limit),
@@ -169090,7 +169357,7 @@ function createCtxSearchTool(deps) {
169090
169357
  return result?.vector ?? null;
169091
169358
  },
169092
169359
  isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
169093
- maxMessageOrdinal: lastCompartmentEnd >= 0 ? lastCompartmentEnd : undefined,
169360
+ maxMessageOrdinal: messageOrdinalCutoff,
169094
169361
  gitCommitsEnabled,
169095
169362
  sources: params.sources,
169096
169363
  visibleMemoryIds,