@cortexkit/pi-magic-context 0.23.0 → 0.24.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
@@ -141961,6 +141961,7 @@ var SESSION_META_SELECT_COLUMNS = [
141961
141961
  "cached_m0_bytes",
141962
141962
  "cached_m1_bytes",
141963
141963
  "cached_m0_project_memory_epoch",
141964
+ "cached_m0_workspace_fingerprint",
141964
141965
  "cached_m0_project_user_profile_version",
141965
141966
  "cached_m0_max_compartment_seq",
141966
141967
  "cached_m0_max_memory_id",
@@ -142008,6 +142009,7 @@ var META_COLUMNS = {
142008
142009
  cachedM0Bytes: "cached_m0_bytes",
142009
142010
  cachedM1Bytes: "cached_m1_bytes",
142010
142011
  cachedM0ProjectMemoryEpoch: "cached_m0_project_memory_epoch",
142012
+ cachedM0WorkspaceFingerprint: "cached_m0_workspace_fingerprint",
142011
142013
  cachedM0ProjectUserProfileVersion: "cached_m0_project_user_profile_version",
142012
142014
  cachedM0MaxCompartmentSeq: "cached_m0_max_compartment_seq",
142013
142015
  cachedM0MaxMemoryId: "cached_m0_max_memory_id",
@@ -142037,6 +142039,7 @@ var NULL_BIND_META_KEYS = new Set([
142037
142039
  "cachedM0Bytes",
142038
142040
  "cachedM1Bytes",
142039
142041
  "cachedM0ProjectMemoryEpoch",
142042
+ "cachedM0WorkspaceFingerprint",
142040
142043
  "cachedM0ProjectUserProfileVersion",
142041
142044
  "cachedM0MaxCompartmentSeq",
142042
142045
  "cachedM0MaxMemoryId",
@@ -142070,7 +142073,7 @@ function isSessionMetaRow(row) {
142070
142073
  if (row === null || typeof row !== "object")
142071
142074
  return false;
142072
142075
  const r = row;
142073
- return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.observed_safe_input_tokens) && isNumberOrNull(r.cache_alert_sent) && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state) && isBlobOrNull(r.cached_m0_bytes) && isBlobOrNull(r.cached_m1_bytes) && isNumberOrNull(r.cached_m0_project_memory_epoch) && isNumberOrNull(r.cached_m0_project_user_profile_version) && isNumberOrNull(r.cached_m0_max_compartment_seq) && isNumberOrNull(r.cached_m0_max_memory_id) && isNumberOrNull(r.cached_m0_max_mutation_id) && isNumberOrNull(r.cached_m0_max_memory_mutation_id) && isStringOrNull(r.cached_m0_project_docs_hash) && isNumberOrNull(r.cached_m0_materialized_at) && isNumberOrNull(r.cached_m0_session_facts_version) && isStringOrNull(r.cached_m0_upgrade_state) && isStringOrNull(r.cached_m0_system_hash) && isStringOrNull(r.cached_m0_tool_set_hash) && isStringOrNull(r.cached_m0_model_key) && isStringOrNull(r.last_observed_model_key) && isNumberOrNull(r.last_usage_context_limit) && isNumberOrNull(r.prior_boundary_ordinal) && isNumberOrNull(r.protected_tail_policy_version) && isNumberOrNull(r.protected_tail_drain_window_started_at) && isNumberOrNull(r.protected_tail_drain_tokens) && isNumberOrNull(r.recovery_no_eligible_head_count) && isNumberOrNull(r.force_emergency_bypass_window_start) && isNumberOrNull(r.force_emergency_bypass_used) && isNumberOrNull(r.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme);
142076
+ return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.observed_safe_input_tokens) && isNumberOrNull(r.cache_alert_sent) && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state) && isBlobOrNull(r.cached_m0_bytes) && isBlobOrNull(r.cached_m1_bytes) && isNumberOrNull(r.cached_m0_project_memory_epoch) && isStringOrNull(r.cached_m0_workspace_fingerprint) && isNumberOrNull(r.cached_m0_project_user_profile_version) && isNumberOrNull(r.cached_m0_max_compartment_seq) && isNumberOrNull(r.cached_m0_max_memory_id) && isNumberOrNull(r.cached_m0_max_mutation_id) && isNumberOrNull(r.cached_m0_max_memory_mutation_id) && isStringOrNull(r.cached_m0_project_docs_hash) && isNumberOrNull(r.cached_m0_materialized_at) && isNumberOrNull(r.cached_m0_session_facts_version) && isStringOrNull(r.cached_m0_upgrade_state) && isStringOrNull(r.cached_m0_system_hash) && isStringOrNull(r.cached_m0_tool_set_hash) && isStringOrNull(r.cached_m0_model_key) && isStringOrNull(r.last_observed_model_key) && isNumberOrNull(r.last_usage_context_limit) && isNumberOrNull(r.prior_boundary_ordinal) && isNumberOrNull(r.protected_tail_policy_version) && isNumberOrNull(r.protected_tail_drain_window_started_at) && isNumberOrNull(r.protected_tail_drain_tokens) && isNumberOrNull(r.recovery_no_eligible_head_count) && isNumberOrNull(r.force_emergency_bypass_window_start) && isNumberOrNull(r.force_emergency_bypass_used) && isNumberOrNull(r.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme);
142074
142077
  }
142075
142078
  function getDefaultSessionMeta(sessionId) {
142076
142079
  return {
@@ -142097,6 +142100,7 @@ function getDefaultSessionMeta(sessionId) {
142097
142100
  cachedM0Bytes: null,
142098
142101
  cachedM1Bytes: null,
142099
142102
  cachedM0ProjectMemoryEpoch: null,
142103
+ cachedM0WorkspaceFingerprint: null,
142100
142104
  cachedM0ProjectUserProfileVersion: null,
142101
142105
  cachedM0MaxCompartmentSeq: null,
142102
142106
  cachedM0MaxMemoryId: null,
@@ -142159,6 +142163,7 @@ function toSessionMeta(row) {
142159
142163
  cachedM0Bytes: toBufferOrNull(row.cached_m0_bytes),
142160
142164
  cachedM1Bytes: toBufferOrNull(row.cached_m1_bytes),
142161
142165
  cachedM0ProjectMemoryEpoch: numOrNull(row.cached_m0_project_memory_epoch),
142166
+ cachedM0WorkspaceFingerprint: stringOrNull(row.cached_m0_workspace_fingerprint),
142162
142167
  cachedM0ProjectUserProfileVersion: numOrNull(row.cached_m0_project_user_profile_version),
142163
142168
  cachedM0MaxCompartmentSeq: numOrNull(row.cached_m0_max_compartment_seq),
142164
142169
  cachedM0MaxMemoryId: numOrNull(row.cached_m0_max_memory_id),
@@ -142189,6 +142194,7 @@ function persistCachedM0(db, sessionId, payload) {
142189
142194
  db.prepare(`UPDATE session_meta SET
142190
142195
  cached_m0_bytes = ?,
142191
142196
  cached_m0_project_memory_epoch = ?,
142197
+ cached_m0_workspace_fingerprint = ?,
142192
142198
  cached_m0_project_user_profile_version = ?,
142193
142199
  cached_m0_max_compartment_seq = ?,
142194
142200
  cached_m0_max_memory_id = ?,
@@ -142200,9 +142206,8 @@ function persistCachedM0(db, sessionId, payload) {
142200
142206
  cached_m0_session_facts_version = ?,
142201
142207
  cached_m0_upgrade_state = ?,
142202
142208
  cached_m0_system_hash = ?,
142203
- cached_m0_tool_set_hash = ?,
142204
142209
  cached_m0_model_key = ?
142205
- WHERE session_id = ?`).run(Buffer2.from(payload.m0Bytes), payload.projectMemoryEpoch, payload.projectUserProfileVersion, payload.maxCompartmentSeq, payload.maxMemoryId, payload.maxMutationId, payload.maxMemoryMutationId ?? null, payload.m1Bytes ? Buffer2.from(payload.m1Bytes) : null, payload.projectDocsHash, payload.materializedAt, payload.sessionFactsVersion, payload.upgradeState, payload.systemHash ?? "", payload.toolSetHash ?? "", payload.modelKey ?? "", sessionId);
142210
+ WHERE session_id = ?`).run(Buffer2.from(payload.m0Bytes), payload.projectMemoryEpoch, payload.workspaceFingerprint ?? null, payload.projectUserProfileVersion, payload.maxCompartmentSeq, payload.maxMemoryId, payload.maxMutationId, payload.maxMemoryMutationId ?? null, payload.m1Bytes ? Buffer2.from(payload.m1Bytes) : null, payload.projectDocsHash, payload.materializedAt, payload.sessionFactsVersion, payload.upgradeState, payload.systemHash ?? "", payload.modelKey ?? "", sessionId);
142206
142211
  }
142207
142212
  function clearCachedM0M1(db, sessionId) {
142208
142213
  ensureSessionMetaRow(db, sessionId);
@@ -142211,6 +142216,7 @@ function clearCachedM0M1(db, sessionId) {
142211
142216
  ["cached_m0_bytes", null],
142212
142217
  ["cached_m1_bytes", null],
142213
142218
  ["cached_m0_project_memory_epoch", null],
142219
+ ["cached_m0_workspace_fingerprint", null],
142214
142220
  ["cached_m0_project_user_profile_version", null],
142215
142221
  ["cached_m0_max_compartment_seq", null],
142216
142222
  ["cached_m0_max_memory_id", null],
@@ -142925,7 +142931,7 @@ var databases = new Map;
142925
142931
  var persistenceByDatabase = new WeakMap;
142926
142932
  var persistenceErrorByDatabase = new WeakMap;
142927
142933
  var lastSchemaFenceRejection = null;
142928
- var LATEST_SUPPORTED_VERSION = 32;
142934
+ var LATEST_SUPPORTED_VERSION = 36;
142929
142935
  function resolveDatabasePath(dbPathOverride) {
142930
142936
  if (dbPathOverride) {
142931
142937
  return { dbDir: dirname2(dbPathOverride), dbPath: dbPathOverride };
@@ -143091,6 +143097,35 @@ function initializeDatabase(db) {
143091
143097
  );
143092
143098
  CREATE INDEX IF NOT EXISTS idx_compartments_session ON compartments(session_id);
143093
143099
 
143100
+ CREATE TABLE IF NOT EXISTS compartment_chunk_embeddings (
143101
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
143102
+ compartment_id INTEGER NOT NULL REFERENCES compartments(id) ON DELETE CASCADE,
143103
+ session_id TEXT NOT NULL,
143104
+ project_path TEXT NOT NULL,
143105
+ harness TEXT NOT NULL DEFAULT 'opencode',
143106
+ window_index INTEGER NOT NULL DEFAULT 0,
143107
+ start_ordinal INTEGER NOT NULL,
143108
+ end_ordinal INTEGER NOT NULL,
143109
+ chunk_hash TEXT NOT NULL,
143110
+ model_id TEXT NOT NULL,
143111
+ dims INTEGER NOT NULL,
143112
+ vector BLOB NOT NULL,
143113
+ created_at INTEGER NOT NULL,
143114
+ UNIQUE(compartment_id, window_index)
143115
+ );
143116
+ CREATE INDEX IF NOT EXISTS idx_cce_session ON compartment_chunk_embeddings(session_id);
143117
+ CREATE INDEX IF NOT EXISTS idx_cce_project_model ON compartment_chunk_embeddings(project_path, model_id);
143118
+
143119
+ CREATE TABLE IF NOT EXISTS session_projects (
143120
+ session_id TEXT NOT NULL,
143121
+ harness TEXT NOT NULL DEFAULT 'opencode',
143122
+ project_path TEXT NOT NULL,
143123
+ updated_at INTEGER NOT NULL,
143124
+ PRIMARY KEY(session_id, harness)
143125
+ );
143126
+ CREATE INDEX IF NOT EXISTS idx_session_projects_project
143127
+ ON session_projects(project_path);
143128
+
143094
143129
  CREATE TABLE IF NOT EXISTS compartment_events (
143095
143130
  id INTEGER PRIMARY KEY AUTOINCREMENT,
143096
143131
  session_id TEXT NOT NULL,
@@ -143276,6 +143311,25 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143276
143311
  rekeyed_at INTEGER NOT NULL
143277
143312
  );
143278
143313
 
143314
+ CREATE TABLE IF NOT EXISTS workspaces (
143315
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
143316
+ name TEXT NOT NULL UNIQUE,
143317
+ created_at INTEGER NOT NULL,
143318
+ updated_at INTEGER NOT NULL,
143319
+ share_categories TEXT NOT NULL DEFAULT '["CONSTRAINTS"]'
143320
+ );
143321
+
143322
+ CREATE TABLE IF NOT EXISTS workspace_members (
143323
+ workspace_id INTEGER NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
143324
+ project_path TEXT NOT NULL,
143325
+ display_name TEXT NOT NULL,
143326
+ display_path TEXT NOT NULL,
143327
+ added_at INTEGER NOT NULL,
143328
+ PRIMARY KEY (workspace_id, project_path)
143329
+ );
143330
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_member_unique ON workspace_members(project_path);
143331
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_member_name ON workspace_members(workspace_id, display_name);
143332
+
143279
143333
  CREATE TABLE IF NOT EXISTS v22_backfill_failures (
143280
143334
  id INTEGER PRIMARY KEY AUTOINCREMENT,
143281
143335
  table_name TEXT NOT NULL,
@@ -143387,6 +143441,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143387
143441
  deferred_execute_state TEXT,
143388
143442
  cached_m0_bytes BLOB,
143389
143443
  cached_m0_project_memory_epoch INTEGER,
143444
+ cached_m0_workspace_fingerprint TEXT,
143390
143445
  cached_m0_project_user_profile_version INTEGER,
143391
143446
  cached_m0_max_compartment_seq INTEGER,
143392
143447
  cached_m0_max_memory_id INTEGER,
@@ -143545,6 +143600,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143545
143600
  ensureColumn(db, "session_meta", "cleared_reasoning_through_tag", "INTEGER DEFAULT 0");
143546
143601
  ensureColumn(db, "session_meta", "stripped_placeholder_ids", "TEXT DEFAULT ''");
143547
143602
  ensureColumn(db, "session_meta", "stale_reduce_stripped_ids", "TEXT DEFAULT ''");
143603
+ ensureColumn(db, "session_meta", "processed_image_stripped_ids", "TEXT DEFAULT ''");
143548
143604
  ensureColumn(db, "compartments", "start_message_id", "TEXT DEFAULT ''");
143549
143605
  ensureColumn(db, "compartments", "end_message_id", "TEXT DEFAULT ''");
143550
143606
  ensureColumn(db, "memory_embeddings", "model_id", "TEXT");
@@ -143598,6 +143654,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143598
143654
  ensureColumn(db, "memories", "importance", "INTEGER");
143599
143655
  ensureColumn(db, "session_meta", "cached_m0_bytes", "BLOB");
143600
143656
  ensureColumn(db, "session_meta", "cached_m0_project_memory_epoch", "INTEGER");
143657
+ ensureColumn(db, "session_meta", "cached_m0_workspace_fingerprint", "TEXT");
143601
143658
  ensureColumn(db, "session_meta", "cached_m0_project_user_profile_version", "INTEGER");
143602
143659
  ensureColumn(db, "session_meta", "cached_m0_max_compartment_seq", "INTEGER");
143603
143660
  ensureColumn(db, "session_meta", "cached_m0_max_memory_id", "INTEGER");
@@ -143629,6 +143686,15 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143629
143686
  project_user_profile_version INTEGER NOT NULL DEFAULT 0,
143630
143687
  updated_at INTEGER NOT NULL DEFAULT 0
143631
143688
  );
143689
+ CREATE TABLE IF NOT EXISTS session_projects (
143690
+ session_id TEXT NOT NULL,
143691
+ harness TEXT NOT NULL DEFAULT 'opencode',
143692
+ project_path TEXT NOT NULL,
143693
+ updated_at INTEGER NOT NULL,
143694
+ PRIMARY KEY(session_id, harness)
143695
+ );
143696
+ CREATE INDEX IF NOT EXISTS idx_session_projects_project
143697
+ ON session_projects(project_path);
143632
143698
  CREATE TABLE IF NOT EXISTS m0_mutation_log (
143633
143699
  id INTEGER PRIMARY KEY AUTOINCREMENT,
143634
143700
  session_id TEXT NOT NULL,
@@ -143656,6 +143722,23 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143656
143722
  new_project_path TEXT NOT NULL,
143657
143723
  rekeyed_at INTEGER NOT NULL
143658
143724
  );
143725
+ CREATE TABLE IF NOT EXISTS workspaces (
143726
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
143727
+ name TEXT NOT NULL UNIQUE,
143728
+ created_at INTEGER NOT NULL,
143729
+ updated_at INTEGER NOT NULL,
143730
+ share_categories TEXT NOT NULL DEFAULT '["CONSTRAINTS"]'
143731
+ );
143732
+ CREATE TABLE IF NOT EXISTS workspace_members (
143733
+ workspace_id INTEGER NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
143734
+ project_path TEXT NOT NULL,
143735
+ display_name TEXT NOT NULL,
143736
+ display_path TEXT NOT NULL,
143737
+ added_at INTEGER NOT NULL,
143738
+ PRIMARY KEY (workspace_id, project_path)
143739
+ );
143740
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_member_unique ON workspace_members(project_path);
143741
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_member_name ON workspace_members(workspace_id, display_name);
143659
143742
  CREATE TABLE IF NOT EXISTS v22_backfill_failures (
143660
143743
  id INTEGER PRIMARY KEY AUTOINCREMENT,
143661
143744
  table_name TEXT NOT NULL,
@@ -143677,6 +143760,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
143677
143760
  ensureColumn(db, "recomp_compartments", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
143678
143761
  ensureColumn(db, "recomp_facts", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
143679
143762
  ensureColumn(db, "message_history_index", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
143763
+ ensureColumn(db, "workspaces", "share_categories", `TEXT NOT NULL DEFAULT '["CONSTRAINTS"]'`);
143680
143764
  }
143681
143765
  function healAllNullColumns(db) {
143682
143766
  healNullTextColumns(db);
@@ -143715,6 +143799,7 @@ function healNullTextColumns(db) {
143715
143799
  ["system_prompt_hash", ""],
143716
143800
  ["stripped_placeholder_ids", ""],
143717
143801
  ["stale_reduce_stripped_ids", ""],
143802
+ ["processed_image_stripped_ids", ""],
143718
143803
  ["memory_block_cache", ""],
143719
143804
  ["memory_block_ids", ""],
143720
143805
  ["compaction_marker_state", ""],
@@ -143759,7 +143844,7 @@ function healNullIntegerColumns(db) {
143759
143844
  }
143760
143845
  }
143761
143846
  function ensureColumn(db, table, column, definition) {
143762
- if (!/^[a-z][a-z0-9_]*$/.test(table) || !/^[a-z][a-z0-9_]*$/.test(column) || !/^[A-Z0-9_'(),[\]\s]+$/i.test(definition)) {
143847
+ if (!/^[a-z][a-z0-9_]*$/.test(table) || !/^[a-z][a-z0-9_]*$/.test(column) || !/^[A-Z0-9_"'(),[\]\s]+$/i.test(definition)) {
143763
143848
  throw new Error(`Unsafe schema identifier: ${table}.${column} ${definition}`);
143764
143849
  }
143765
143850
  const rows = db.prepare(`PRAGMA table_info(${table})`).all();
@@ -143836,10 +143921,291 @@ function openDatabase(dbPathOrOptions) {
143836
143921
  }
143837
143922
  }
143838
143923
 
143924
+ // ../plugin/src/features/magic-context/workspaces.ts
143925
+ import { createHash as createHash3 } from "node:crypto";
143926
+
143927
+ // ../plugin/src/features/magic-context/memory/constants.ts
143928
+ var V2_MEMORY_CATEGORIES = [
143929
+ "PROJECT_RULES",
143930
+ "ARCHITECTURE",
143931
+ "CONSTRAINTS",
143932
+ "CONFIG_VALUES",
143933
+ "NAMING"
143934
+ ];
143935
+ var PROMOTABLE_CATEGORIES = [
143936
+ "PROJECT_RULES",
143937
+ "ARCHITECTURE",
143938
+ "CONSTRAINTS",
143939
+ "CONFIG_VALUES",
143940
+ "NAMING",
143941
+ "ARCHITECTURE_DECISIONS",
143942
+ "CONFIG_DEFAULTS",
143943
+ "USER_PREFERENCES",
143944
+ "USER_DIRECTIVES",
143945
+ "ENVIRONMENT",
143946
+ "WORKFLOW_RULES",
143947
+ "KNOWN_ISSUES"
143948
+ ];
143949
+ var CATEGORY_PRIORITY = [
143950
+ "PROJECT_RULES",
143951
+ "ARCHITECTURE",
143952
+ "CONSTRAINTS",
143953
+ "CONFIG_VALUES",
143954
+ "NAMING",
143955
+ "USER_DIRECTIVES",
143956
+ "USER_PREFERENCES",
143957
+ "CONFIG_DEFAULTS",
143958
+ "ARCHITECTURE_DECISIONS",
143959
+ "ENVIRONMENT",
143960
+ "WORKFLOW_RULES",
143961
+ "KNOWN_ISSUES"
143962
+ ];
143963
+ var MEMORY_CATEGORY_ORDER_UNKNOWN = 99;
143964
+ var MEMORY_CATEGORY_ORDER_PRIORITY = CATEGORY_PRIORITY.reduce((acc, category, index) => {
143965
+ acc[category] = index;
143966
+ return acc;
143967
+ }, {});
143968
+ var MEMORY_CATEGORY_ORDER_SQL = `CASE category ${CATEGORY_PRIORITY.map((category, index) => `WHEN '${category}' THEN ${index}`).join(" ")} ELSE ${MEMORY_CATEGORY_ORDER_UNKNOWN} END`;
143969
+ var CATEGORY_DEFAULT_TTL = {
143970
+ WORKFLOW_RULES: 90 * 24 * 60 * 60 * 1000,
143971
+ KNOWN_ISSUES: 30 * 24 * 60 * 60 * 1000
143972
+ };
143973
+ // ../plugin/src/features/magic-context/workspaces.ts
143974
+ var VALID_SHARE_CATEGORIES = new Set(V2_MEMORY_CATEGORIES);
143975
+ function tableExists(db, tableName) {
143976
+ const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name = ? LIMIT 1").get(tableName);
143977
+ return Boolean(row);
143978
+ }
143979
+ function columnExists(db, tableName, columnName) {
143980
+ const rows = db.prepare(`PRAGMA table_info(${tableName})`).all();
143981
+ return rows.some((row) => row.name === columnName);
143982
+ }
143983
+ function uniqueSorted(values) {
143984
+ return [...new Set(values)].sort((left, right) => left.localeCompare(right));
143985
+ }
143986
+ function placeholders(values) {
143987
+ return values.map(() => "?").join(", ");
143988
+ }
143989
+ function normalizeShareCategories(raw) {
143990
+ if (raw === null || raw === undefined)
143991
+ return null;
143992
+ if (typeof raw !== "string")
143993
+ return null;
143994
+ let parsed;
143995
+ try {
143996
+ parsed = JSON.parse(raw);
143997
+ } catch {
143998
+ return null;
143999
+ }
144000
+ if (!Array.isArray(parsed))
144001
+ return null;
144002
+ const categories = [];
144003
+ for (const value of parsed) {
144004
+ if (typeof value !== "string" || !VALID_SHARE_CATEGORIES.has(value)) {
144005
+ return null;
144006
+ }
144007
+ if (!categories.includes(value))
144008
+ categories.push(value);
144009
+ }
144010
+ return categories.sort((left, right) => left.localeCompare(right));
144011
+ }
144012
+ function selectWorkspaceShareCategories(db, identities) {
144013
+ const candidates = uniqueSorted(identities.filter((identity) => identity.length > 0));
144014
+ if (candidates.length === 0 || !tableExists(db, "workspace_members") || !tableExists(db, "workspaces") || !columnExists(db, "workspaces", "share_categories")) {
144015
+ return null;
144016
+ }
144017
+ const row = db.prepare(`SELECT workspace.share_categories AS shareCategories
144018
+ FROM workspace_members AS member
144019
+ JOIN workspaces AS workspace ON workspace.id = member.workspace_id
144020
+ WHERE member.project_path IN (${placeholders(candidates)})
144021
+ ORDER BY workspace.id ASC
144022
+ LIMIT 1`).get(...candidates);
144023
+ return normalizeShareCategories(row?.shareCategories ?? null);
144024
+ }
144025
+ function resolveWorkspaceShareCategories(db, projectIdentity) {
144026
+ return selectWorkspaceShareCategories(db, [projectIdentity]);
144027
+ }
144028
+ function resolveWorkspaceIdentitySet(db, projectIdentity) {
144029
+ if (!tableExists(db, "workspace_members")) {
144030
+ return { identities: [projectIdentity], namesByIdentity: new Map };
144031
+ }
144032
+ const rows = db.prepare(`SELECT member.project_path AS identity, member.display_name AS displayName
144033
+ FROM workspace_members AS anchor
144034
+ JOIN workspace_members AS member ON member.workspace_id = anchor.workspace_id
144035
+ WHERE anchor.project_path = ?
144036
+ ORDER BY member.display_name ASC, member.project_path ASC`).all(projectIdentity);
144037
+ if (rows.length === 0) {
144038
+ return { identities: [projectIdentity], namesByIdentity: new Map };
144039
+ }
144040
+ const namesByIdentity = new Map;
144041
+ const identities = [];
144042
+ for (const row of rows) {
144043
+ if (typeof row.identity !== "string" || row.identity.length === 0)
144044
+ continue;
144045
+ if (identities.includes(row.identity))
144046
+ continue;
144047
+ identities.push(row.identity);
144048
+ if (typeof row.displayName === "string" && row.displayName.length > 0) {
144049
+ namesByIdentity.set(row.identity, row.displayName);
144050
+ }
144051
+ }
144052
+ return identities.length > 0 ? { identities, namesByIdentity } : { identities: [projectIdentity], namesByIdentity: new Map };
144053
+ }
144054
+ function expandWorkspaceIdentitySetWithAliases(db, identities) {
144055
+ const canonical = uniqueSorted(identities.filter((identity) => identity.length > 0));
144056
+ const expanded = new Set(canonical);
144057
+ const canonicalIdentityByStoredPath = new Map;
144058
+ for (const identity of canonical) {
144059
+ canonicalIdentityByStoredPath.set(identity, identity);
144060
+ }
144061
+ if (canonical.length === 0 || !tableExists(db, "v22_identity_rekey_map")) {
144062
+ return { expandedIdentities: [...expanded], canonicalIdentityByStoredPath };
144063
+ }
144064
+ const rows = db.prepare(`SELECT old_project_path AS oldProjectPath, new_project_path AS newProjectPath
144065
+ FROM v22_identity_rekey_map
144066
+ WHERE new_project_path IN (${placeholders(canonical)})
144067
+ ORDER BY old_project_path ASC`).all(...canonical);
144068
+ for (const row of rows) {
144069
+ if (typeof row.oldProjectPath !== "string" || typeof row.newProjectPath !== "string") {
144070
+ continue;
144071
+ }
144072
+ if (!canonicalIdentityByStoredPath.has(row.newProjectPath))
144073
+ continue;
144074
+ expanded.add(row.oldProjectPath);
144075
+ canonicalIdentityByStoredPath.set(row.oldProjectPath, row.newProjectPath);
144076
+ }
144077
+ return { expandedIdentities: [...expanded], canonicalIdentityByStoredPath };
144078
+ }
144079
+ function resolveStoredPathWorkspaceIdentity(storedProjectPath, memberIdentities, canonicalIdentityByStoredPath) {
144080
+ const direct = canonicalIdentityByStoredPath.get(storedProjectPath);
144081
+ if (direct)
144082
+ return direct;
144083
+ const normalized = normalizeStoredProjectPath(storedProjectPath);
144084
+ const normalizedDirect = canonicalIdentityByStoredPath.get(normalized);
144085
+ if (normalizedDirect)
144086
+ return normalizedDirect;
144087
+ if (memberIdentities.includes(normalized))
144088
+ return normalized;
144089
+ for (const identity of memberIdentities) {
144090
+ if (storedPathBelongsToIdentity(storedProjectPath, identity)) {
144091
+ return identity;
144092
+ }
144093
+ }
144094
+ return null;
144095
+ }
144096
+ function storedPathBelongsToWorkspace(storedProjectPath, memberIdentities, expandedIdentities, canonicalIdentityByStoredPath) {
144097
+ if (expandedIdentities.includes(storedProjectPath))
144098
+ return true;
144099
+ return resolveStoredPathWorkspaceIdentity(storedProjectPath, memberIdentities, canonicalIdentityByStoredPath) !== null;
144100
+ }
144101
+ function sourceNameForMemory(storedProjectPath, ownIdentity, memberIdentities, namesByIdentity, canonicalIdentityByStoredPath) {
144102
+ const canonicalIdentity = resolveStoredPathWorkspaceIdentity(storedProjectPath, memberIdentities, canonicalIdentityByStoredPath);
144103
+ if (!canonicalIdentity || canonicalIdentity === ownIdentity)
144104
+ return;
144105
+ return namesByIdentity.get(canonicalIdentity);
144106
+ }
144107
+ function getEpochMap(db, identities) {
144108
+ if (identities.length === 0)
144109
+ return new Map;
144110
+ const rows = db.prepare(`SELECT project_path AS projectPath, project_memory_epoch AS epoch
144111
+ FROM project_state
144112
+ WHERE project_path IN (${placeholders(identities)})`).all(...identities);
144113
+ const epochs = new Map;
144114
+ for (const row of rows) {
144115
+ if (typeof row.projectPath !== "string" || typeof row.epoch !== "number")
144116
+ continue;
144117
+ epochs.set(row.projectPath, row.epoch);
144118
+ }
144119
+ return epochs;
144120
+ }
144121
+ function computeWorkspaceEpochFingerprint(db, identities) {
144122
+ const canonical = uniqueSorted(identities.filter((identity) => identity.length > 0));
144123
+ const epochs = getEpochMap(db, canonical);
144124
+ const shareCategories = selectWorkspaceShareCategories(db, canonical);
144125
+ const hash = createHash3("sha256");
144126
+ hash.update("share_categories", "utf8");
144127
+ hash.update("\x00");
144128
+ hash.update(shareCategories === null ? "ALL" : JSON.stringify(shareCategories), "utf8");
144129
+ hash.update(`
144130
+ `);
144131
+ for (const identity of canonical) {
144132
+ hash.update(identity, "utf8");
144133
+ hash.update("\x00");
144134
+ hash.update(String(epochs.get(identity) ?? 0), "utf8");
144135
+ hash.update(`
144136
+ `);
144137
+ }
144138
+ return hash.digest("hex");
144139
+ }
144140
+ function isInTransaction(db) {
144141
+ const candidate = db;
144142
+ return candidate.inTransaction === true || candidate.isTransaction === true;
144143
+ }
144144
+ function workspaceMembersForIdentity(db, identity) {
144145
+ if (!tableExists(db, "workspace_members"))
144146
+ return [identity];
144147
+ const rows = db.prepare(`SELECT member.project_path AS identity
144148
+ FROM workspace_members AS anchor
144149
+ JOIN workspace_members AS member ON member.workspace_id = anchor.workspace_id
144150
+ WHERE anchor.project_path = ?
144151
+ ORDER BY member.project_path ASC`).all(identity);
144152
+ const identities = rows.map((row) => typeof row.identity === "string" ? row.identity : "").filter((value) => value.length > 0);
144153
+ return identities.length > 0 ? uniqueSorted(identities) : [identity];
144154
+ }
144155
+ function bumpEpochRows(db, identities, now) {
144156
+ const stmt = db.prepare(`INSERT INTO project_state
144157
+ (project_path, project_memory_epoch, project_user_profile_version, updated_at)
144158
+ VALUES (?, 1, 0, ?)
144159
+ ON CONFLICT(project_path) DO UPDATE SET
144160
+ project_memory_epoch = project_memory_epoch + 1,
144161
+ updated_at = excluded.updated_at`);
144162
+ for (const identity of uniqueSorted(identities)) {
144163
+ stmt.run(identity, now);
144164
+ }
144165
+ }
144166
+ function bumpEpochsForWorkspaceMembers(db, identity, now = Date.now()) {
144167
+ const run = () => bumpEpochRows(db, workspaceMembersForIdentity(db, identity), now);
144168
+ if (isInTransaction(db)) {
144169
+ run();
144170
+ return;
144171
+ }
144172
+ db.exec("BEGIN IMMEDIATE");
144173
+ try {
144174
+ run();
144175
+ db.exec("COMMIT");
144176
+ } catch (error) {
144177
+ try {
144178
+ db.exec("ROLLBACK");
144179
+ } catch {}
144180
+ throw error;
144181
+ }
144182
+ }
144183
+ function bumpEpochsForWorkspaceMemberSet(db, identities, now = Date.now()) {
144184
+ const run = () => bumpEpochRows(db, identities, now);
144185
+ if (isInTransaction(db)) {
144186
+ run();
144187
+ return;
144188
+ }
144189
+ db.exec("BEGIN IMMEDIATE");
144190
+ try {
144191
+ run();
144192
+ db.exec("COMMIT");
144193
+ } catch (error) {
144194
+ try {
144195
+ db.exec("ROLLBACK");
144196
+ } catch {}
144197
+ throw error;
144198
+ }
144199
+ }
144200
+
143839
144201
  // ../plugin/src/features/magic-context/migrations.ts
143840
- function tableExists(db, name2) {
144202
+ function tableExists2(db, name2) {
143841
144203
  return Boolean(db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(name2));
143842
144204
  }
144205
+ function columnExists2(db, table, column) {
144206
+ const rows = db.prepare(`PRAGMA table_info(${table})`).all();
144207
+ return rows.some((row) => row.name === column);
144208
+ }
143843
144209
  var MIGRATIONS = [
143844
144210
  {
143845
144211
  version: 1,
@@ -144298,9 +144664,9 @@ var MIGRATIONS = [
144298
144664
  version: 22,
144299
144665
  description: "v2.0 cache architecture schema foundation",
144300
144666
  up: (db) => {
144301
- const hasSessionMetaTable = tableExists(db, "session_meta");
144302
- const hasCompartmentsTable = tableExists(db, "compartments");
144303
- const hasMemoriesTable = tableExists(db, "memories");
144667
+ const hasSessionMetaTable = tableExists2(db, "session_meta");
144668
+ const hasCompartmentsTable = tableExists2(db, "compartments");
144669
+ const hasMemoriesTable = tableExists2(db, "memories");
144304
144670
  if (hasSessionMetaTable) {
144305
144671
  ensureColumn(db, "session_meta", "cached_m0_bytes", "BLOB");
144306
144672
  ensureColumn(db, "session_meta", "cached_m0_project_memory_epoch", "INTEGER");
@@ -144325,7 +144691,7 @@ var MIGRATIONS = [
144325
144691
  ensureColumn(db, "compartments", "p1_embedding_model_id", "TEXT");
144326
144692
  ensureColumn(db, "compartments", "legacy", "INTEGER NOT NULL DEFAULT 0");
144327
144693
  }
144328
- const hasRecompCompartmentsTable = tableExists(db, "recomp_compartments");
144694
+ const hasRecompCompartmentsTable = tableExists2(db, "recomp_compartments");
144329
144695
  if (hasRecompCompartmentsTable) {
144330
144696
  ensureColumn(db, "recomp_compartments", "p1", "TEXT");
144331
144697
  ensureColumn(db, "recomp_compartments", "p2", "TEXT");
@@ -144636,6 +145002,156 @@ var MIGRATIONS = [
144636
145002
  db.prepare("UPDATE session_meta SET force_emergency_bypass_used = 0 WHERE force_emergency_bypass_used IS NULL").run();
144637
145003
  db.prepare("UPDATE session_meta SET last_usage_context_limit = 0 WHERE last_usage_context_limit IS NULL").run();
144638
145004
  }
145005
+ },
145006
+ {
145007
+ version: 33,
145008
+ description: "Compartment chunk embeddings for semantic message-history search",
145009
+ up: (db) => {
145010
+ db.exec(`
145011
+ CREATE TABLE IF NOT EXISTS compartment_chunk_embeddings (
145012
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
145013
+ compartment_id INTEGER NOT NULL REFERENCES compartments(id) ON DELETE CASCADE,
145014
+ session_id TEXT NOT NULL,
145015
+ project_path TEXT NOT NULL,
145016
+ harness TEXT NOT NULL DEFAULT 'opencode',
145017
+ window_index INTEGER NOT NULL DEFAULT 0,
145018
+ start_ordinal INTEGER NOT NULL,
145019
+ end_ordinal INTEGER NOT NULL,
145020
+ chunk_hash TEXT NOT NULL,
145021
+ model_id TEXT NOT NULL,
145022
+ dims INTEGER NOT NULL,
145023
+ vector BLOB NOT NULL,
145024
+ created_at INTEGER NOT NULL,
145025
+ UNIQUE(compartment_id, window_index)
145026
+ );
145027
+ CREATE INDEX IF NOT EXISTS idx_cce_session
145028
+ ON compartment_chunk_embeddings(session_id);
145029
+ CREATE INDEX IF NOT EXISTS idx_cce_project_model
145030
+ ON compartment_chunk_embeddings(project_path, model_id);
145031
+ `);
145032
+ }
145033
+ },
145034
+ {
145035
+ version: 34,
145036
+ description: "workspace tables and m[0] workspace fingerprint cache reset",
145037
+ up: (db) => {
145038
+ db.exec(`
145039
+ CREATE TABLE IF NOT EXISTS workspaces (
145040
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
145041
+ name TEXT NOT NULL UNIQUE,
145042
+ created_at INTEGER NOT NULL,
145043
+ updated_at INTEGER NOT NULL
145044
+ );
145045
+ CREATE TABLE IF NOT EXISTS workspace_members (
145046
+ workspace_id INTEGER NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
145047
+ project_path TEXT NOT NULL,
145048
+ display_name TEXT NOT NULL,
145049
+ display_path TEXT NOT NULL,
145050
+ added_at INTEGER NOT NULL,
145051
+ PRIMARY KEY (workspace_id, project_path)
145052
+ );
145053
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_member_unique
145054
+ ON workspace_members(project_path);
145055
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_member_name
145056
+ ON workspace_members(workspace_id, display_name);
145057
+ `);
145058
+ const hasSessionMeta = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='session_meta' LIMIT 1").get();
145059
+ if (!hasSessionMeta)
145060
+ return;
145061
+ ensureColumn(db, "session_meta", "cached_m0_workspace_fingerprint", "TEXT");
145062
+ const columns = new Set(db.prepare("PRAGMA table_info(session_meta)").all().map((column) => column.name));
145063
+ const clears = [
145064
+ ["cached_m0_bytes", null],
145065
+ ["cached_m1_bytes", null],
145066
+ ["cached_m0_project_memory_epoch", null],
145067
+ ["cached_m0_workspace_fingerprint", null],
145068
+ ["cached_m0_project_user_profile_version", null],
145069
+ ["cached_m0_max_compartment_seq", null],
145070
+ ["cached_m0_max_memory_id", null],
145071
+ ["cached_m0_max_mutation_id", null],
145072
+ ["cached_m0_max_memory_mutation_id", null],
145073
+ ["cached_m0_project_docs_hash", null],
145074
+ ["cached_m0_materialized_at", null],
145075
+ ["cached_m0_session_facts_version", null],
145076
+ ["cached_m0_upgrade_state", null],
145077
+ ["cached_m0_system_hash", null],
145078
+ ["cached_m0_tool_set_hash", null],
145079
+ ["cached_m0_model_key", null],
145080
+ ["cached_m0_last_baseline_end_message_id", null],
145081
+ ["memory_block_cache", ""],
145082
+ ["memory_block_ids", ""],
145083
+ ["memory_block_count", 0]
145084
+ ];
145085
+ const setClauses = [];
145086
+ const values = [];
145087
+ for (const [column, value] of clears) {
145088
+ if (!columns.has(column))
145089
+ continue;
145090
+ setClauses.push(`${column} = ?`);
145091
+ values.push(value);
145092
+ }
145093
+ if (setClauses.length > 0) {
145094
+ db.prepare(`UPDATE session_meta SET ${setClauses.join(", ")}`).run(...values);
145095
+ }
145096
+ }
145097
+ },
145098
+ {
145099
+ version: 35,
145100
+ description: "workspace per-category share defaults and epoch refresh",
145101
+ up: (db) => {
145102
+ db.exec(`
145103
+ CREATE TABLE IF NOT EXISTS workspaces (
145104
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
145105
+ name TEXT NOT NULL UNIQUE,
145106
+ created_at INTEGER NOT NULL,
145107
+ updated_at INTEGER NOT NULL,
145108
+ share_categories TEXT NOT NULL DEFAULT '["CONSTRAINTS"]'
145109
+ );
145110
+ `);
145111
+ if (!columnExists2(db, "workspaces", "share_categories")) {
145112
+ db.exec(`ALTER TABLE workspaces ADD COLUMN share_categories TEXT NOT NULL DEFAULT '["CONSTRAINTS"]'`);
145113
+ }
145114
+ db.prepare(`UPDATE workspaces
145115
+ SET share_categories = '["CONSTRAINTS"]'
145116
+ WHERE share_categories IS NULL OR share_categories = ''`).run();
145117
+ if (!tableExists2(db, "workspace_members"))
145118
+ return;
145119
+ const rows = db.prepare(`SELECT DISTINCT project_path AS identity
145120
+ FROM workspace_members
145121
+ WHERE project_path IS NOT NULL AND project_path <> ''
145122
+ ORDER BY project_path ASC`).all();
145123
+ const identities = rows.map((row) => typeof row.identity === "string" ? row.identity : "").filter((identity) => identity.length > 0);
145124
+ if (identities.length > 0) {
145125
+ bumpEpochsForWorkspaceMemberSet(db, identities, Date.now());
145126
+ }
145127
+ }
145128
+ },
145129
+ {
145130
+ version: 36,
145131
+ description: "session project ownership map for compartment chunk backfill scoping",
145132
+ up: (db) => {
145133
+ db.exec(`
145134
+ CREATE TABLE IF NOT EXISTS session_projects (
145135
+ session_id TEXT NOT NULL,
145136
+ harness TEXT NOT NULL DEFAULT 'opencode',
145137
+ project_path TEXT NOT NULL,
145138
+ updated_at INTEGER NOT NULL,
145139
+ PRIMARY KEY(session_id, harness)
145140
+ );
145141
+ CREATE INDEX IF NOT EXISTS idx_session_projects_project
145142
+ ON session_projects(project_path);
145143
+ `);
145144
+ const hasChunkTable = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='compartment_chunk_embeddings'").get();
145145
+ if (hasChunkTable) {
145146
+ db.exec(`
145147
+ INSERT OR IGNORE INTO session_projects (session_id, harness, project_path, updated_at)
145148
+ SELECT session_id, harness, MIN(project_path), 0
145149
+ FROM compartment_chunk_embeddings
145150
+ GROUP BY session_id, harness
145151
+ HAVING COUNT(DISTINCT project_path) = 1;
145152
+ `);
145153
+ }
145154
+ }
144639
145155
  }
144640
145156
  ];
144641
145157
  var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -144705,7 +145221,7 @@ function runMigrations(db) {
144705
145221
  log(`[migrations] schema version now: ${MIGRATIONS[MIGRATIONS.length - 1].version}`);
144706
145222
  }
144707
145223
  // ../plugin/src/features/magic-context/project-docs-hash.ts
144708
- import { createHash as createHash3 } from "node:crypto";
145224
+ import { createHash as createHash4 } from "node:crypto";
144709
145225
  import { lstatSync, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
144710
145226
  import path4 from "node:path";
144711
145227
  var PROJECT_DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
@@ -144803,7 +145319,7 @@ function hashCanonicalPieces(hashPieces) {
144803
145319
  if (hashPieces.length === 0) {
144804
145320
  return "";
144805
145321
  }
144806
- return createHash3("sha256").update(hashPieces.join(PROJECT_DOCS_DELIMITER), "utf8").digest("hex");
145322
+ return createHash4("sha256").update(hashPieces.join(PROJECT_DOCS_DELIMITER), "utf8").digest("hex");
144807
145323
  }
144808
145324
  function readProjectDocsCanonical(projectDirectory) {
144809
145325
  const canonicalDirectory = path4.resolve(projectDirectory);
@@ -144909,18 +145425,13 @@ function getMemoryMutation(db, id) {
144909
145425
  WHERE id = ?`).get(id);
144910
145426
  return row ? toMemoryMutation(row) : null;
144911
145427
  }
144912
- function getMemoryMutationsForRender(db, projectPath, afterId, renderedMemoryIds) {
144913
- if (renderedMemoryIds.length === 0)
144914
- return [];
144915
- const uniqueIds = [...new Set(renderedMemoryIds)].sort((left, right) => left - right);
144916
- const placeholders = uniqueIds.map(() => "?").join(", ");
144917
- const rows = db.prepare(`SELECT id, project_path, mutation_type, target_memory_id,
144918
- superseded_by_id, category, new_content, queued_at
144919
- FROM memory_mutation_log
144920
- WHERE project_path = ?
144921
- AND id > ?
144922
- AND target_memory_id IN (${placeholders})
144923
- ORDER BY id ASC`).all(projectPath, afterId ?? 0, ...uniqueIds);
145428
+ function uniqueProjectPaths(projectPaths) {
145429
+ return [...new Set(projectPaths.filter((path5) => path5.length > 0))];
145430
+ }
145431
+ function placeholders2(values) {
145432
+ return values.map(() => "?").join(", ");
145433
+ }
145434
+ function coalesceMutations(rows) {
144924
145435
  const chosenByTarget = new Map;
144925
145436
  for (const dbRow of rows) {
144926
145437
  const candidate = toMemoryMutation(dbRow);
@@ -144941,10 +145452,54 @@ function getMemoryMutationsForRender(db, projectPath, afterId, renderedMemoryIds
144941
145452
  }
144942
145453
  return [...chosenByTarget.values()].sort((left, right) => left.id - right.id);
144943
145454
  }
145455
+ function getMemoryMutationsForRender(db, projectPath, afterId, renderedMemoryIds) {
145456
+ if (renderedMemoryIds.length === 0)
145457
+ return [];
145458
+ const uniqueIds = [...new Set(renderedMemoryIds)].sort((left, right) => left - right);
145459
+ const placeholders3 = uniqueIds.map(() => "?").join(", ");
145460
+ const rows = db.prepare(`SELECT id, project_path, mutation_type, target_memory_id,
145461
+ superseded_by_id, category, new_content, queued_at
145462
+ FROM memory_mutation_log
145463
+ WHERE project_path = ?
145464
+ AND id > ?
145465
+ AND target_memory_id IN (${placeholders3})
145466
+ ORDER BY id ASC`).all(projectPath, afterId ?? 0, ...uniqueIds);
145467
+ return coalesceMutations(rows);
145468
+ }
145469
+ function getMemoryMutationsForRenderByProjects(db, projectPaths, afterId, renderedMemoryIds) {
145470
+ if (renderedMemoryIds.length === 0)
145471
+ return [];
145472
+ const identities = uniqueProjectPaths(projectPaths);
145473
+ if (identities.length === 0)
145474
+ return [];
145475
+ if (identities.length === 1) {
145476
+ return getMemoryMutationsForRender(db, identities[0], afterId, renderedMemoryIds);
145477
+ }
145478
+ const uniqueIds = [...new Set(renderedMemoryIds)].sort((left, right) => left - right);
145479
+ const rows = db.prepare(`SELECT id, project_path, mutation_type, target_memory_id,
145480
+ superseded_by_id, category, new_content, queued_at
145481
+ FROM memory_mutation_log
145482
+ WHERE project_path IN (${placeholders2(identities)})
145483
+ AND id > ?
145484
+ AND target_memory_id IN (${placeholders2(uniqueIds)})
145485
+ ORDER BY id ASC`).all(...identities, afterId ?? 0, ...uniqueIds);
145486
+ return coalesceMutations(rows);
145487
+ }
144944
145488
  function getMaxMemoryMutationId(db, projectPath) {
144945
145489
  const row = db.prepare("SELECT MAX(id) AS max_id FROM memory_mutation_log WHERE project_path = ?").get(projectPath);
144946
145490
  return row?.max_id ?? null;
144947
145491
  }
145492
+ function getMaxMemoryMutationIdForProjects(db, projectPaths) {
145493
+ const identities = uniqueProjectPaths(projectPaths);
145494
+ if (identities.length === 0)
145495
+ return null;
145496
+ if (identities.length === 1)
145497
+ return getMaxMemoryMutationId(db, identities[0]);
145498
+ const row = db.prepare(`SELECT MAX(id) AS max_id
145499
+ FROM memory_mutation_log
145500
+ WHERE project_path IN (${placeholders2(identities)})`).get(...identities);
145501
+ return row?.max_id ?? null;
145502
+ }
144948
145503
  // ../plugin/src/features/magic-context/storage-meta-persisted.ts
144949
145504
  init_logger();
144950
145505
  var CAS_RETRY_LIMIT = 5;
@@ -145790,9 +146345,9 @@ function buildStatusClause(status) {
145790
146345
  if (statuses.length === 0) {
145791
146346
  return null;
145792
146347
  }
145793
- const placeholders = statuses.map(() => "?").join(", ");
146348
+ const placeholders3 = statuses.map(() => "?").join(", ");
145794
146349
  return {
145795
- sql: `status IN (${placeholders})`,
146350
+ sql: `status IN (${placeholders3})`,
145796
146351
  params: statuses
145797
146352
  };
145798
146353
  }
@@ -145988,19 +146543,6 @@ function getProjectState(db, projectPath) {
145988
146543
  WHERE project_path = ?`).get(projectPath);
145989
146544
  return row ? toProjectState(row) : null;
145990
146545
  }
145991
- function bumpProjectMemoryEpoch(db, projectPath, now = Date.now()) {
145992
- db.prepare(`INSERT INTO project_state
145993
- (project_path, project_memory_epoch, project_user_profile_version, updated_at)
145994
- VALUES (?, 1, 0, ?)
145995
- ON CONFLICT(project_path) DO UPDATE SET
145996
- project_memory_epoch = project_memory_epoch + 1,
145997
- updated_at = excluded.updated_at`).run(projectPath, now);
145998
- const state = getProjectState(db, projectPath);
145999
- if (!state) {
146000
- throw new Error(`Failed to bump project memory epoch for ${projectPath}`);
146001
- }
146002
- return state;
146003
- }
146004
146546
  function bumpProjectUserProfileVersion(db, projectPath = GLOBAL_USER_PROFILE_PROJECT_PATH, now = Date.now()) {
146005
146547
  db.prepare(`INSERT INTO project_state
146006
146548
  (project_path, project_memory_epoch, project_user_profile_version, updated_at)
@@ -146034,8 +146576,8 @@ function getSourceContents(db, sessionId, tagIds) {
146034
146576
  if (tagIds.length === 0) {
146035
146577
  return new Map;
146036
146578
  }
146037
- const placeholders = tagIds.map(() => "?").join(", ");
146038
- const rows = db.prepare(`SELECT tag_id, content FROM source_contents WHERE session_id = ? AND tag_id IN (${placeholders})`).all(sessionId, ...tagIds).filter(isSourceContentRow);
146579
+ const placeholders3 = tagIds.map(() => "?").join(", ");
146580
+ const rows = db.prepare(`SELECT tag_id, content FROM source_contents WHERE session_id = ? AND tag_id IN (${placeholders3})`).all(sessionId, ...tagIds).filter(isSourceContentRow);
146039
146581
  const sources = new Map;
146040
146582
  for (const row of rows) {
146041
146583
  sources.set(row.tag_id, row.content);
@@ -146325,8 +146867,8 @@ function getTagsByNumbers(db, sessionId, tagNumbers) {
146325
146867
  }
146326
146868
  return all;
146327
146869
  }
146328
- const placeholders = tagNumbers.map(() => "?").join(",");
146329
- const rows = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? AND tag_number IN (${placeholders}) ORDER BY tag_number ASC, id ASC`).all(sessionId, ...tagNumbers).filter(isTagRow);
146870
+ const placeholders3 = tagNumbers.map(() => "?").join(",");
146871
+ const rows = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? AND tag_number IN (${placeholders3}) ORDER BY tag_number ASC, id ASC`).all(sessionId, ...tagNumbers).filter(isTagRow);
146330
146872
  return rows.map(toTagEntry);
146331
146873
  }
146332
146874
  var getToolTagNumberByOwnerStatements = new WeakMap;
@@ -146396,7 +146938,7 @@ var ERROR_CLASSES = new Set([
146396
146938
  ]);
146397
146939
  // ../plugin/src/features/magic-context/v22-deferred-backfill.ts
146398
146940
  init_logger();
146399
- import { createHash as createHash4 } from "node:crypto";
146941
+ import { createHash as createHash5 } from "node:crypto";
146400
146942
  import { realpathSync as realpathSync2 } from "node:fs";
146401
146943
  import path5 from "node:path";
146402
146944
  var BATCH_SIZE = 25;
@@ -146450,7 +146992,7 @@ function computeLegacyRustDirIdentity(rawProjectPath) {
146450
146992
  } catch {
146451
146993
  canonical = path5.isAbsolute(rawProjectPath) ? rawProjectPath : path5.join(process.cwd(), rawProjectPath);
146452
146994
  }
146453
- return `dir:${createHash4("sha256").update(canonical, "utf8").digest("hex")}`;
146995
+ return `dir:${createHash5("sha256").update(canonical, "utf8").digest("hex")}`;
146454
146996
  }
146455
146997
  function upsertRekeyMap(db, oldProjectPath, newProjectPath, rekeyedAt) {
146456
146998
  db.prepare(`INSERT INTO v22_identity_rekey_map (old_project_path, new_project_path, rekeyed_at)
@@ -147008,7 +147550,7 @@ function clearNoteNudgeTriggerOnly(db, sessionId) {
147008
147550
  }
147009
147551
 
147010
147552
  // ../plugin/src/hooks/magic-context/todo-view.ts
147011
- import { createHash as createHash5 } from "node:crypto";
147553
+ import { createHash as createHash6 } from "node:crypto";
147012
147554
  var TERMINAL_STATUSES = new Set(["completed", "cancelled"]);
147013
147555
  var TITLE_DONE_STATUSES = new Set(["completed"]);
147014
147556
  var SYNTHETIC_CALL_ID_PREFIX = "mc_synthetic_todo_";
@@ -147053,7 +147595,7 @@ function buildSyntheticTodoPart(stateJson) {
147053
147595
  };
147054
147596
  }
147055
147597
  function computeSyntheticCallId(stateJson) {
147056
- const hash = createHash5("sha256").update(stateJson).digest("hex").slice(0, 16);
147598
+ const hash = createHash6("sha256").update(stateJson).digest("hex").slice(0, 16);
147057
147599
  return `${SYNTHETIC_CALL_ID_PREFIX}${hash}`;
147058
147600
  }
147059
147601
  function parseTodoState(stateJson) {
@@ -147194,13 +147736,13 @@ async function maybeSendUpgradeReminder(deps, sessionId) {
147194
147736
  init_data_path();
147195
147737
  import * as fs2 from "node:fs";
147196
147738
  import * as path6 from "node:path";
147197
- var ANNOUNCEMENT_VERSION = "0.23.0";
147739
+ var ANNOUNCEMENT_VERSION = "0.24.0";
147198
147740
  var ANNOUNCEMENT_FEATURES = [
147199
- "Smarter context nudges: gentle <system-reminder> notes on tool outputs replace the old chat nudges — quieter, cache-safe, and they now also help subagents manage their own context.",
147200
- "The agent can now maintain its own project memories: update, archive (batch), and merge the memories it sees — not just write new ones. Memory categories are schema-enforced.",
147201
- "Big-session performance: historian trigger and token math moved off the database hot path — multi-second stalls on large sessions are gone (measured 250ms → 2.4ms per pass).",
147202
- "History compaction unstuck for sparse sessions: the protected-tail boundary is now size-based, so sessions with few user turns compact reliably instead of growing forever (issue #132).",
147203
- "Fixed: session titles no longer fail to generate in fresh directories (issue #129), plus 20+ correctness fixes from three audit rounds."
147741
+ "Searchable session history: ctx_search can now find older discussion by meaning, not just keywords. New history is embedded automatically — to backfill an EXISTING session's older history, run /ctx-embed-history once (it works in the background).",
147742
+ "Cross-project workspaces: group related repos and share project memories across them, with per-category control over what's shared. Set them up in the dashboard's Workspaces panel.",
147743
+ "Pi: fixed sessions overflowing the model context while still showing moderate usage — Pi now sheds context before a tool-heavy turn overflows.",
147744
+ "Fewer prompt-cache busts: doc edits, processed screenshots, and a rebuild-then-bust-again case no longer re-bill large prompt prefixes.",
147745
+ "Setup wizard now lists your actual models with type-ahead instead of fixed recommendations, and explains the historian/dreamer roles (issue #144). Plus a GitHub Copilot tool-pairing fix (#135)."
147204
147746
  ];
147205
147747
  var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU";
147206
147748
  var STATE_FILENAME = "last_announced_version";
@@ -149083,7 +149625,7 @@ function isEmbeddingRow(row) {
149083
149625
  if (row === null || typeof row !== "object")
149084
149626
  return false;
149085
149627
  const candidate = row;
149086
- return typeof candidate.memoryId === "number" && isEmbeddingBlob(candidate.embedding);
149628
+ return typeof candidate.memoryId === "number" && isEmbeddingBlob(candidate.embedding) && (candidate.modelId === null || typeof candidate.modelId === "string");
149087
149629
  }
149088
149630
  function toFloat32Array(blob) {
149089
149631
  if (blob instanceof Uint8Array) {
@@ -149103,7 +149645,7 @@ function getSaveEmbeddingStatement(db) {
149103
149645
  function getLoadAllEmbeddingsStatement(db) {
149104
149646
  let stmt = loadAllEmbeddingsStatements.get(db);
149105
149647
  if (!stmt) {
149106
- stmt = db.prepare("SELECT memory_embeddings.memory_id AS memoryId, memory_embeddings.embedding AS embedding FROM memory_embeddings INNER JOIN memories ON memories.id = memory_embeddings.memory_id WHERE memories.project_path = ? ORDER BY memory_embeddings.memory_id ASC");
149648
+ stmt = db.prepare("SELECT memory_embeddings.memory_id AS memoryId, memory_embeddings.embedding AS embedding, memory_embeddings.model_id AS modelId FROM memory_embeddings INNER JOIN memories ON memories.id = memory_embeddings.memory_id WHERE memories.project_path = ? ORDER BY memory_embeddings.memory_id ASC");
149107
149649
  loadAllEmbeddingsStatements.set(db, stmt);
149108
149650
  }
149109
149651
  return stmt;
@@ -149132,7 +149674,10 @@ function loadAllEmbeddings(db, projectPath) {
149132
149674
  const rows = getLoadAllEmbeddingsStatement(db).all(projectPath).filter(isEmbeddingRow);
149133
149675
  const embeddings = new Map;
149134
149676
  for (const row of rows) {
149135
- embeddings.set(row.memoryId, toFloat32Array(row.embedding));
149677
+ embeddings.set(row.memoryId, {
149678
+ embedding: toFloat32Array(row.embedding),
149679
+ modelId: row.modelId
149680
+ });
149136
149681
  }
149137
149682
  return embeddings;
149138
149683
  }
@@ -149183,13 +149728,13 @@ function invalidateMemory(projectPath, memoryId) {
149183
149728
  }
149184
149729
 
149185
149730
  // ../plugin/src/features/magic-context/memory/normalize-hash.ts
149186
- import { createHash as createHash6 } from "node:crypto";
149731
+ import { createHash as createHash7 } from "node:crypto";
149187
149732
  function normalizeMemoryContent(content) {
149188
149733
  return content.toLowerCase().replace(/\s+/g, " ").trim();
149189
149734
  }
149190
149735
  function computeNormalizedHash(content) {
149191
149736
  const normalized = normalizeMemoryContent(content);
149192
- return createHash6("md5").update(normalized).digest("hex");
149737
+ return createHash7("md5").update(normalized).digest("hex");
149193
149738
  }
149194
149739
 
149195
149740
  // ../plugin/src/features/magic-context/memory/storage-memory.ts
@@ -149377,8 +149922,8 @@ function getMemoriesByProjectStatement(db, statuses) {
149377
149922
  }
149378
149923
  let stmt = statements.get(db);
149379
149924
  if (!stmt) {
149380
- const placeholders = statuses.map(() => "?").join(", ");
149381
- stmt = db.prepare(`SELECT ${getMemorySelectColumns(db)} FROM memories WHERE project_path = ? AND status IN (${placeholders}) AND (expires_at IS NULL OR expires_at > ?) ORDER BY category ASC, updated_at DESC, id ASC`);
149925
+ const placeholders3 = statuses.map(() => "?").join(", ");
149926
+ stmt = db.prepare(`SELECT ${getMemorySelectColumns(db)} FROM memories WHERE project_path = ? AND status IN (${placeholders3}) AND (expires_at IS NULL OR expires_at > ?) ORDER BY category ASC, updated_at DESC, id ASC`);
149382
149927
  statements.set(db, stmt);
149383
149928
  }
149384
149929
  return stmt;
@@ -149523,6 +150068,97 @@ function getMemoriesByProject(db, projectPath, statuses = ["active", "permanent"
149523
150068
  const rows = getMemoriesByProjectStatement(db, statuses).all(projectPath, ...statuses, expiryCutoff).filter(isMemoryRow);
149524
150069
  return rows.map(toMemory);
149525
150070
  }
150071
+ function sqlPlaceholders(values) {
150072
+ return values.map(() => "?").join(", ");
150073
+ }
150074
+ function uniqueValues(values) {
150075
+ return [...new Set(values.filter((value) => value.length > 0))];
150076
+ }
150077
+ function buildWorkspaceMemorySqlFilter(args) {
150078
+ if (args.shareCategories === null || args.shareCategories === undefined) {
150079
+ return { clause: "", params: [], active: false };
150080
+ }
150081
+ const identities = uniqueValues(args.identities);
150082
+ const identitySet = new Set(identities);
150083
+ const ownSet = new Set(uniqueValues(args.ownIdentities ?? []).filter((identity) => identitySet.has(identity)));
150084
+ const foreignIdentities = identities.filter((identity) => !ownSet.has(identity));
150085
+ if (foreignIdentities.length === 0) {
150086
+ return { clause: "", params: [], active: false };
150087
+ }
150088
+ const ownIdentities = identities.filter((identity) => ownSet.has(identity));
150089
+ const shareCategories = uniqueValues([...args.shareCategories]);
150090
+ const qualifier = args.tableName ? `${args.tableName}.` : "";
150091
+ const predicates = [];
150092
+ const params = [];
150093
+ if (ownIdentities.length > 0) {
150094
+ predicates.push(`${qualifier}project_path IN (${sqlPlaceholders(ownIdentities)})`);
150095
+ params.push(...ownIdentities);
150096
+ }
150097
+ if (foreignIdentities.length > 0 && shareCategories.length > 0) {
150098
+ predicates.push(`(${qualifier}project_path IN (${sqlPlaceholders(foreignIdentities)}) AND ${qualifier}category IN (${sqlPlaceholders(shareCategories)}))`);
150099
+ params.push(...foreignIdentities, ...shareCategories);
150100
+ }
150101
+ if (predicates.length === 0) {
150102
+ return { clause: " AND 0 = 1", params: [], active: true };
150103
+ }
150104
+ return { clause: ` AND (${predicates.join(" OR ")})`, params, active: true };
150105
+ }
150106
+ function getMemoriesByProjects(db, projectPaths, statuses = ["active", "permanent"], expiryCutoff = Date.now(), ownIdentities, shareCategories) {
150107
+ const identities = uniqueValues(projectPaths);
150108
+ if (identities.length === 0 || statuses.length === 0)
150109
+ return [];
150110
+ const sharingFilter = buildWorkspaceMemorySqlFilter({
150111
+ identities,
150112
+ ownIdentities,
150113
+ shareCategories
150114
+ });
150115
+ if (identities.length === 1 && !sharingFilter.active) {
150116
+ return getMemoriesByProject(db, identities[0], statuses, expiryCutoff);
150117
+ }
150118
+ const rows = db.prepare(`SELECT ${getMemorySelectColumns(db)}
150119
+ FROM memories
150120
+ WHERE project_path IN (${sqlPlaceholders(identities)})
150121
+ AND status IN (${sqlPlaceholders(statuses)})
150122
+ AND (expires_at IS NULL OR expires_at > ?)${sharingFilter.clause}
150123
+ ORDER BY category ASC, updated_at DESC, id ASC`).all(...identities, ...statuses, expiryCutoff, ...sharingFilter.params).filter(isMemoryRow);
150124
+ return rows.map(toMemory);
150125
+ }
150126
+ function getMaxMemoryIdForProjects(db, projectPaths, ownIdentities, shareCategories) {
150127
+ const identities = uniqueValues(projectPaths);
150128
+ if (identities.length === 0)
150129
+ return 0;
150130
+ const sharingFilter = buildWorkspaceMemorySqlFilter({
150131
+ identities,
150132
+ ownIdentities,
150133
+ shareCategories
150134
+ });
150135
+ if (identities.length === 1 && !sharingFilter.active) {
150136
+ const row2 = db.prepare("SELECT COALESCE(MAX(id), 0) AS max_id FROM memories WHERE project_path = ?").get(identities[0]);
150137
+ return typeof row2?.max_id === "number" ? row2.max_id : 0;
150138
+ }
150139
+ const row = db.prepare(`SELECT COALESCE(MAX(id), 0) AS max_id
150140
+ FROM memories
150141
+ WHERE project_path IN (${sqlPlaceholders(identities)})${sharingFilter.clause}`).get(...identities, ...sharingFilter.params);
150142
+ return typeof row?.max_id === "number" ? row.max_id : 0;
150143
+ }
150144
+ function readNewMemoriesForM1Union(db, projectPaths, afterId, expiryCutoff, ownIdentities, shareCategories) {
150145
+ const identities = uniqueValues(projectPaths);
150146
+ if (identities.length === 0)
150147
+ return [];
150148
+ const sharingFilter = buildWorkspaceMemorySqlFilter({
150149
+ identities,
150150
+ ownIdentities,
150151
+ shareCategories
150152
+ });
150153
+ const rows = db.prepare(`SELECT ${getMemorySelectColumns(db)}
150154
+ FROM memories
150155
+ WHERE project_path IN (${sqlPlaceholders(identities)})
150156
+ AND id > ?
150157
+ AND status IN ('active', 'permanent')
150158
+ AND (expires_at IS NULL OR expires_at > ?)${sharingFilter.clause}
150159
+ ORDER BY ${MEMORY_CATEGORY_ORDER_SQL}, id ASC`).all(...identities, afterId, expiryCutoff, ...sharingFilter.params).filter(isMemoryRow);
150160
+ return rows.map(toMemory);
150161
+ }
149526
150162
  function getAllActiveMemoriesForMigration(db, projectPath) {
149527
150163
  const rows = getActiveMemoriesNoExpiryStatement(db).all(projectPath).filter(isMemoryRow);
149528
150164
  return rows.map(toMemory);
@@ -150044,8 +150680,8 @@ function getUserMemoryCandidates(db) {
150044
150680
  function deleteUserMemoryCandidates(db, ids) {
150045
150681
  if (ids.length === 0)
150046
150682
  return;
150047
- const placeholders = ids.map(() => "?").join(",");
150048
- db.prepare(`DELETE FROM user_memory_candidates WHERE id IN (${placeholders})`).run(...ids);
150683
+ const placeholders3 = ids.map(() => "?").join(",");
150684
+ db.prepare(`DELETE FROM user_memory_candidates WHERE id IN (${placeholders3})`).run(...ids);
150049
150685
  }
150050
150686
  function insertUserMemory(db, content, sourceCandidateIds) {
150051
150687
  const now = Date.now();
@@ -165535,7 +166171,8 @@ var BaseEmbeddingConfigSchema = exports_external.object({
165535
166171
  endpoint: exports_external.string().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
165536
166172
  api_key: exports_external.string().optional().describe("API key for remote embedding provider (optional)"),
165537
166173
  input_type: exports_external.string().optional().describe("Optional input_type sent in the embedding request body. Required by some openai-compatible providers (e.g. NVIDIA NIM expects 'query' or 'passage'). Omitted from the request when unset."),
165538
- truncate: exports_external.string().optional().describe("Optional truncate mode sent in the embedding request body (e.g. NVIDIA NIM accepts 'NONE' | 'START' | 'END'). Omitted from the request when unset.")
166174
+ truncate: exports_external.string().optional().describe("Optional truncate mode sent in the embedding request body (e.g. NVIDIA NIM accepts 'NONE' | 'START' | 'END'). Omitted from the request when unset."),
166175
+ max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted.")
165539
166176
  }).superRefine((data, ctx) => {
165540
166177
  if (data.provider === "openai-compatible" && !data.endpoint?.trim()) {
165541
166178
  ctx.addIssue({
@@ -165556,7 +166193,8 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
165556
166193
  if (data.provider === "local") {
165557
166194
  return {
165558
166195
  provider: "local",
165559
- model: data.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL
166196
+ model: data.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
166197
+ ...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
165560
166198
  };
165561
166199
  }
165562
166200
  if (data.provider === "openai-compatible") {
@@ -165569,7 +166207,8 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
165569
166207
  endpoint: data.endpoint?.trim() ?? "",
165570
166208
  ...apiKey ? { api_key: apiKey } : {},
165571
166209
  ...inputType ? { input_type: inputType } : {},
165572
- ...truncate ? { truncate } : {}
166210
+ ...truncate ? { truncate } : {},
166211
+ ...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
165573
166212
  };
165574
166213
  }
165575
166214
  return { provider: "off" };
@@ -165651,6 +166290,441 @@ var MagicContextConfigSchema = exports_external.object({
165651
166290
  // ../plugin/src/features/magic-context/memory/embedding.ts
165652
166291
  init_logger();
165653
166292
 
166293
+ // ../plugin/src/features/magic-context/compartment-chunk-embedding.ts
166294
+ import { createHash as createHash8 } from "node:crypto";
166295
+ var DEFAULT_COMPARTMENT_CHUNK_MAX_INPUT_TOKENS = 512;
166296
+ var loadFtsRowsStatements = new WeakMap;
166297
+ var existingHashStatements = new WeakMap;
166298
+ var existingHashByProjectStatements = new WeakMap;
166299
+ var deleteByCompartmentStatements = new WeakMap;
166300
+ var insertEmbeddingStatements = new WeakMap;
166301
+ var distinctModelStatements = new WeakMap;
166302
+ var clearProjectStatements = new WeakMap;
166303
+ var clearProjectModelStatements = new WeakMap;
166304
+ var searchRowsStatements = new WeakMap;
166305
+ var searchRowsByModelStatements = new WeakMap;
166306
+ var backfillCandidateStatements = new WeakMap;
166307
+ function getLoadFtsRowsStatement(db) {
166308
+ let stmt = loadFtsRowsStatements.get(db);
166309
+ if (!stmt) {
166310
+ stmt = db.prepare(`SELECT message_ordinal AS messageOrdinal, role, content
166311
+ FROM message_history_fts
166312
+ WHERE session_id = ?
166313
+ AND message_ordinal >= ?
166314
+ AND message_ordinal <= ?
166315
+ AND role IN ('user', 'assistant')
166316
+ ORDER BY message_ordinal ASC`);
166317
+ loadFtsRowsStatements.set(db, stmt);
166318
+ }
166319
+ return stmt;
166320
+ }
166321
+ function getExistingHashStatement(db, scopedToProject) {
166322
+ const map2 = scopedToProject ? existingHashByProjectStatements : existingHashStatements;
166323
+ let stmt = map2.get(db);
166324
+ if (!stmt) {
166325
+ stmt = db.prepare(`SELECT window_index AS windowIndex, chunk_hash AS chunkHash
166326
+ FROM compartment_chunk_embeddings
166327
+ WHERE compartment_id = ?
166328
+ AND model_id = ?
166329
+ ${scopedToProject ? "AND project_path = ?" : ""}
166330
+ ORDER BY window_index ASC`);
166331
+ map2.set(db, stmt);
166332
+ }
166333
+ return stmt;
166334
+ }
166335
+ function getDeleteByCompartmentStatement(db) {
166336
+ let stmt = deleteByCompartmentStatements.get(db);
166337
+ if (!stmt) {
166338
+ stmt = db.prepare("DELETE FROM compartment_chunk_embeddings WHERE compartment_id = ?");
166339
+ deleteByCompartmentStatements.set(db, stmt);
166340
+ }
166341
+ return stmt;
166342
+ }
166343
+ function getInsertEmbeddingStatement(db) {
166344
+ let stmt = insertEmbeddingStatements.get(db);
166345
+ if (!stmt) {
166346
+ stmt = db.prepare(`INSERT INTO compartment_chunk_embeddings (
166347
+ compartment_id, session_id, project_path, harness, window_index,
166348
+ start_ordinal, end_ordinal, chunk_hash, model_id, dims, vector, created_at
166349
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
166350
+ insertEmbeddingStatements.set(db, stmt);
166351
+ }
166352
+ return stmt;
166353
+ }
166354
+ function getDistinctModelStatement(db) {
166355
+ let stmt = distinctModelStatements.get(db);
166356
+ if (!stmt) {
166357
+ stmt = db.prepare(`SELECT DISTINCT model_id AS modelId
166358
+ FROM compartment_chunk_embeddings
166359
+ WHERE project_path = ?`);
166360
+ distinctModelStatements.set(db, stmt);
166361
+ }
166362
+ return stmt;
166363
+ }
166364
+ function getClearProjectStatement(db) {
166365
+ let stmt = clearProjectStatements.get(db);
166366
+ if (!stmt) {
166367
+ stmt = db.prepare("DELETE FROM compartment_chunk_embeddings WHERE project_path = ?");
166368
+ clearProjectStatements.set(db, stmt);
166369
+ }
166370
+ return stmt;
166371
+ }
166372
+ function getClearProjectModelStatement(db) {
166373
+ let stmt = clearProjectModelStatements.get(db);
166374
+ if (!stmt) {
166375
+ stmt = db.prepare("DELETE FROM compartment_chunk_embeddings WHERE project_path = ? AND model_id = ?");
166376
+ clearProjectModelStatements.set(db, stmt);
166377
+ }
166378
+ return stmt;
166379
+ }
166380
+ function getSearchRowsStatement(db, withModel) {
166381
+ const map2 = withModel ? searchRowsByModelStatements : searchRowsStatements;
166382
+ let stmt = map2.get(db);
166383
+ if (!stmt) {
166384
+ stmt = db.prepare(`SELECT e.compartment_id AS compartmentId,
166385
+ e.session_id AS sessionId,
166386
+ c.title AS title,
166387
+ c.start_message AS compartmentStart,
166388
+ c.end_message AS compartmentEnd,
166389
+ e.window_index AS windowIndex,
166390
+ e.start_ordinal AS windowStart,
166391
+ e.end_ordinal AS windowEnd,
166392
+ e.chunk_hash AS chunkHash,
166393
+ e.model_id AS modelId,
166394
+ e.dims AS dims,
166395
+ e.vector AS vector
166396
+ FROM compartment_chunk_embeddings e
166397
+ JOIN compartments c ON c.id = e.compartment_id
166398
+ WHERE e.session_id = ?
166399
+ AND e.project_path = ?
166400
+ ${withModel ? "AND e.model_id = ?" : ""}
166401
+ ORDER BY e.compartment_id ASC, e.window_index ASC`);
166402
+ map2.set(db, stmt);
166403
+ }
166404
+ return stmt;
166405
+ }
166406
+ function isFinitePositiveInteger(value) {
166407
+ return typeof value === "number" && Number.isFinite(value) && value > 0;
166408
+ }
166409
+ function normalizeCompartmentChunkMaxInputTokens(value) {
166410
+ if (!isFinitePositiveInteger(value)) {
166411
+ return DEFAULT_COMPARTMENT_CHUNK_MAX_INPUT_TOKENS;
166412
+ }
166413
+ return Math.max(1, Math.floor(value));
166414
+ }
166415
+ function normalizeContent(text) {
166416
+ return text.replace(/\s+/g, " ").trim();
166417
+ }
166418
+ function formatOrdinalRange(start, end) {
166419
+ return start === end ? `[${start}]` : `[${start}-${end}]`;
166420
+ }
166421
+ function rolePrefix(role) {
166422
+ if (role === "user")
166423
+ return "U";
166424
+ if (role === "assistant")
166425
+ return "A";
166426
+ return null;
166427
+ }
166428
+ function parseOrdinal(value) {
166429
+ const parsed = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
166430
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
166431
+ }
166432
+ function parseCanonicalLineRange(line) {
166433
+ const match = /^\[(\d+)(?:-(\d+))?\]\s+[UA]:/.exec(line.trim());
166434
+ if (!match)
166435
+ return null;
166436
+ const start = Number.parseInt(match[1], 10);
166437
+ const end = match[2] ? Number.parseInt(match[2], 10) : start;
166438
+ if (!Number.isFinite(start) || !Number.isFinite(end))
166439
+ return null;
166440
+ return { start, end };
166441
+ }
166442
+ function hashChunkText(text) {
166443
+ return createHash8("sha256").update(text).digest("hex");
166444
+ }
166445
+ function vectorBlob(vector) {
166446
+ return new Uint8Array(vector.buffer, vector.byteOffset, vector.byteLength);
166447
+ }
166448
+ function toFloat32Array2(blob) {
166449
+ if (blob instanceof Uint8Array) {
166450
+ const buffer2 = blob.buffer.slice(blob.byteOffset, blob.byteOffset + blob.byteLength);
166451
+ return new Float32Array(buffer2);
166452
+ }
166453
+ return new Float32Array(blob.slice(0));
166454
+ }
166455
+ function buildCanonicalChunkTextFromFts(db, sessionId, startOrdinal, endOrdinal) {
166456
+ if (endOrdinal < startOrdinal)
166457
+ return "";
166458
+ const rows = getLoadFtsRowsStatement(db).all(sessionId, startOrdinal, endOrdinal).map((row) => row);
166459
+ const lines = [];
166460
+ let current = null;
166461
+ const flush2 = () => {
166462
+ if (!current || current.parts.length === 0)
166463
+ return;
166464
+ lines.push(`${formatOrdinalRange(current.start, current.end)} ${current.role}: ${current.parts.join(" / ")}`);
166465
+ current = null;
166466
+ };
166467
+ for (const row of rows) {
166468
+ const ordinal = parseOrdinal(row.messageOrdinal);
166469
+ const prefix = rolePrefix(row.role);
166470
+ const content = typeof row.content === "string" ? normalizeContent(row.content) : "";
166471
+ if (ordinal === null || prefix === null || content.length === 0)
166472
+ continue;
166473
+ if (current && current.role === prefix) {
166474
+ current.end = ordinal;
166475
+ current.parts.push(content);
166476
+ continue;
166477
+ }
166478
+ flush2();
166479
+ current = { role: prefix, start: ordinal, end: ordinal, parts: [content] };
166480
+ }
166481
+ flush2();
166482
+ return lines.join(`
166483
+ `);
166484
+ }
166485
+ function canonicalizeInMemoryChunkTextForEmbedding(chunkText, startOrdinal, endOrdinal) {
166486
+ const lines = [];
166487
+ for (const rawLine of chunkText.split(/\r?\n/)) {
166488
+ const line = rawLine.trim();
166489
+ const match = /^(\[(\d+)(?:-(\d+))?\]\s+[UA]:)\s*(.*)$/.exec(line);
166490
+ if (!match)
166491
+ continue;
166492
+ const lineStart = Number.parseInt(match[2], 10);
166493
+ const lineEnd = match[3] ? Number.parseInt(match[3], 10) : lineStart;
166494
+ if (startOrdinal != null && lineEnd < startOrdinal)
166495
+ continue;
166496
+ if (endOrdinal != null && lineStart > endOrdinal)
166497
+ continue;
166498
+ const rawParts = match[4].split(" / ").map((part) => normalizeContent(part)).filter((part) => part.length > 0);
166499
+ const ordinalSpan = lineEnd - lineStart + 1;
166500
+ const roleLabel = match[1].slice(match[1].indexOf("]") + 2);
166501
+ if (ordinalSpan === rawParts.length) {
166502
+ const retained = rawParts.map((part, index) => ({ ordinal: lineStart + index, part })).filter(({ ordinal, part }) => {
166503
+ if (part.startsWith("TC:"))
166504
+ return false;
166505
+ if (startOrdinal != null && ordinal < startOrdinal)
166506
+ return false;
166507
+ if (endOrdinal != null && ordinal > endOrdinal)
166508
+ return false;
166509
+ return true;
166510
+ });
166511
+ if (retained.length === 0)
166512
+ continue;
166513
+ const retainedStart = retained[0].ordinal;
166514
+ const retainedEnd = retained[retained.length - 1].ordinal;
166515
+ lines.push(`${formatOrdinalRange(retainedStart, retainedEnd)} ${roleLabel} ${retained.map(({ part }) => part).join(" / ")}`);
166516
+ continue;
166517
+ }
166518
+ const parts = rawParts.filter((part) => !part.startsWith("TC:"));
166519
+ if (parts.length === 0)
166520
+ continue;
166521
+ lines.push(`${match[1]} ${parts.join(" / ")}`);
166522
+ }
166523
+ return lines.join(`
166524
+ `);
166525
+ }
166526
+ function chunkCanonicalText(canonicalText, startOrdinal, endOrdinal, maxInputTokens) {
166527
+ const lines = canonicalText.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
166528
+ if (lines.length === 0 || endOrdinal < startOrdinal)
166529
+ return [];
166530
+ const normalizedMax = normalizeCompartmentChunkMaxInputTokens(maxInputTokens);
166531
+ const fullText = lines.join(`
166532
+ `);
166533
+ if (estimateTokens(fullText) <= normalizedMax) {
166534
+ return [
166535
+ {
166536
+ windowIndex: 0,
166537
+ startOrdinal,
166538
+ endOrdinal,
166539
+ text: fullText,
166540
+ chunkHash: hashChunkText(fullText)
166541
+ }
166542
+ ];
166543
+ }
166544
+ const windows = [];
166545
+ let currentLines = [];
166546
+ let currentStart = null;
166547
+ let currentEnd = null;
166548
+ let currentTokens = 0;
166549
+ const flush2 = () => {
166550
+ if (currentLines.length === 0 || currentStart === null || currentEnd === null)
166551
+ return;
166552
+ const text = currentLines.join(`
166553
+ `);
166554
+ windows.push({
166555
+ windowIndex: windows.length + 1,
166556
+ startOrdinal: currentStart,
166557
+ endOrdinal: currentEnd,
166558
+ text,
166559
+ chunkHash: hashChunkText(text)
166560
+ });
166561
+ currentLines = [];
166562
+ currentStart = null;
166563
+ currentEnd = null;
166564
+ currentTokens = 0;
166565
+ };
166566
+ for (const line of lines) {
166567
+ const range = parseCanonicalLineRange(line);
166568
+ const lineStart = range?.start ?? startOrdinal;
166569
+ const lineEnd = range?.end ?? lineStart;
166570
+ const lineTokens = estimateTokens(line);
166571
+ if (currentLines.length > 0 && currentTokens + lineTokens > normalizedMax) {
166572
+ flush2();
166573
+ }
166574
+ if (currentLines.length === 0) {
166575
+ currentStart = lineStart;
166576
+ }
166577
+ currentLines.push(line);
166578
+ currentEnd = lineEnd;
166579
+ currentTokens += lineTokens;
166580
+ }
166581
+ flush2();
166582
+ return windows;
166583
+ }
166584
+ function getExistingChunkHashes(db, compartmentId, modelId, projectPath) {
166585
+ const scoped = typeof projectPath === "string" && projectPath.length > 0;
166586
+ const rows = scoped ? getExistingHashStatement(db, true).all(compartmentId, modelId, projectPath) : getExistingHashStatement(db, false).all(compartmentId, modelId);
166587
+ return new Map(rows.filter((row) => typeof row.windowIndex === "number" && typeof row.chunkHash === "string").map((row) => [row.windowIndex, row.chunkHash]));
166588
+ }
166589
+ function chunkEmbeddingWindowsAreCurrent(db, compartmentId, modelId, windows, projectPath) {
166590
+ const existing = getExistingChunkHashes(db, compartmentId, modelId, projectPath);
166591
+ if (existing.size !== windows.length)
166592
+ return false;
166593
+ return windows.every((window) => existing.get(window.windowIndex) === window.chunkHash);
166594
+ }
166595
+ function replaceCompartmentChunkEmbeddings(db, rows) {
166596
+ if (rows.length === 0)
166597
+ return;
166598
+ const compartmentId = rows[0].compartmentId;
166599
+ const now = Date.now();
166600
+ db.transaction(() => {
166601
+ getDeleteByCompartmentStatement(db).run(compartmentId);
166602
+ const insert = getInsertEmbeddingStatement(db);
166603
+ for (const row of rows) {
166604
+ insert.run(row.compartmentId, row.sessionId, row.projectPath, getHarness(), row.window.windowIndex, row.window.startOrdinal, row.window.endOrdinal, row.window.chunkHash, row.modelId, row.vector.length, vectorBlob(row.vector), row.createdAt ?? now);
166605
+ }
166606
+ })();
166607
+ }
166608
+ function getDistinctChunkEmbeddingModelIds(db, projectPath) {
166609
+ const rows = getDistinctModelStatement(db).all(projectPath);
166610
+ return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
166611
+ }
166612
+ function clearChunkEmbeddingsForProject(db, projectPath, modelId) {
166613
+ if (modelId) {
166614
+ return getClearProjectModelStatement(db).run(projectPath, modelId).changes;
166615
+ }
166616
+ return getClearProjectStatement(db).run(projectPath).changes;
166617
+ }
166618
+ function loadCompartmentChunkEmbeddingsForSearch(db, sessionId, projectPath, modelId) {
166619
+ const rows = modelId ? getSearchRowsStatement(db, true).all(sessionId, projectPath, modelId) : getSearchRowsStatement(db, false).all(sessionId, projectPath);
166620
+ return rows.filter((row) => typeof row.compartmentId === "number" && typeof row.sessionId === "string" && typeof row.title === "string" && typeof row.compartmentStart === "number" && typeof row.compartmentEnd === "number" && typeof row.windowIndex === "number" && typeof row.windowStart === "number" && typeof row.windowEnd === "number" && typeof row.chunkHash === "string" && typeof row.modelId === "string" && typeof row.dims === "number" && (row.vector instanceof Uint8Array || row.vector instanceof ArrayBuffer)).map((row) => ({
166621
+ compartmentId: row.compartmentId,
166622
+ sessionId: row.sessionId,
166623
+ title: row.title,
166624
+ startOrdinal: row.compartmentStart,
166625
+ endOrdinal: row.compartmentEnd,
166626
+ windowIndex: row.windowIndex,
166627
+ windowStartOrdinal: row.windowStart,
166628
+ windowEndOrdinal: row.windowEnd,
166629
+ chunkHash: row.chunkHash,
166630
+ modelId: row.modelId,
166631
+ dims: row.dims,
166632
+ vector: toFloat32Array2(row.vector)
166633
+ }));
166634
+ }
166635
+ function mapBackfillCandidateRows(rows) {
166636
+ return rows.filter((row) => {
166637
+ if (row === null || typeof row !== "object")
166638
+ return false;
166639
+ const candidate = row;
166640
+ return typeof candidate.id === "number" && typeof candidate.sessionId === "string" && typeof candidate.startMessage === "number" && typeof candidate.endMessage === "number" && typeof candidate.title === "string";
166641
+ }).map((row) => ({
166642
+ id: row.id,
166643
+ sessionId: row.sessionId,
166644
+ startMessage: row.startMessage,
166645
+ endMessage: row.endMessage,
166646
+ title: row.title
166647
+ }));
166648
+ }
166649
+ var sessionBackfillCandidateStatements = new WeakMap;
166650
+ function loadUnembeddedSessionChunkCandidates(db, projectPath, sessionId, modelId, limit, excludeIds) {
166651
+ if (excludeIds && excludeIds.length > 0) {
166652
+ const placeholders3 = excludeIds.map(() => "?").join(", ");
166653
+ const stmt2 = db.prepare(`SELECT c.id AS id,
166654
+ c.session_id AS sessionId,
166655
+ c.start_message AS startMessage,
166656
+ c.end_message AS endMessage,
166657
+ c.title AS title
166658
+ FROM compartments c
166659
+ JOIN session_projects sp
166660
+ ON sp.session_id = c.session_id
166661
+ AND sp.harness = c.harness
166662
+ AND sp.project_path = ?
166663
+ WHERE c.session_id = ?
166664
+ AND c.start_message IS NOT NULL
166665
+ AND c.end_message IS NOT NULL
166666
+ AND c.id NOT IN (${placeholders3})
166667
+ AND NOT EXISTS (
166668
+ SELECT 1
166669
+ FROM compartment_chunk_embeddings current
166670
+ WHERE current.compartment_id = c.id
166671
+ AND current.project_path = ?
166672
+ AND current.model_id = ?
166673
+ )
166674
+ ORDER BY c.start_message ASC, c.id ASC
166675
+ LIMIT ?`);
166676
+ const rows2 = stmt2.all(projectPath, sessionId, ...excludeIds, projectPath, modelId, Math.max(1, limit));
166677
+ return mapBackfillCandidateRows(rows2);
166678
+ }
166679
+ let stmt = sessionBackfillCandidateStatements.get(db);
166680
+ if (!stmt) {
166681
+ stmt = db.prepare(`SELECT c.id AS id,
166682
+ c.session_id AS sessionId,
166683
+ c.start_message AS startMessage,
166684
+ c.end_message AS endMessage,
166685
+ c.title AS title
166686
+ FROM compartments c
166687
+ JOIN session_projects sp
166688
+ ON sp.session_id = c.session_id
166689
+ AND sp.harness = c.harness
166690
+ AND sp.project_path = ?
166691
+ WHERE c.session_id = ?
166692
+ AND c.start_message IS NOT NULL
166693
+ AND c.end_message IS NOT NULL
166694
+ AND NOT EXISTS (
166695
+ SELECT 1
166696
+ FROM compartment_chunk_embeddings current
166697
+ WHERE current.compartment_id = c.id
166698
+ AND current.project_path = ?
166699
+ AND current.model_id = ?
166700
+ )
166701
+ ORDER BY c.start_message ASC, c.id ASC
166702
+ LIMIT ?`);
166703
+ sessionBackfillCandidateStatements.set(db, stmt);
166704
+ }
166705
+ const rows = stmt.all(projectPath, sessionId, projectPath, modelId, Math.max(1, limit));
166706
+ return mapBackfillCandidateRows(rows);
166707
+ }
166708
+ function countUnembeddedSessionCompartments(db, projectPath, sessionId, modelId) {
166709
+ const row = db.prepare(`SELECT COUNT(*) AS n
166710
+ FROM compartments c
166711
+ JOIN session_projects sp
166712
+ ON sp.session_id = c.session_id
166713
+ AND sp.harness = c.harness
166714
+ AND sp.project_path = ?
166715
+ WHERE c.session_id = ?
166716
+ AND c.start_message IS NOT NULL
166717
+ AND c.end_message IS NOT NULL
166718
+ AND NOT EXISTS (
166719
+ SELECT 1
166720
+ FROM compartment_chunk_embeddings current
166721
+ WHERE current.compartment_id = c.id
166722
+ AND current.project_path = ?
166723
+ AND current.model_id = ?
166724
+ )`).get(projectPath, sessionId, projectPath, modelId);
166725
+ return typeof row?.n === "number" ? row.n : 0;
166726
+ }
166727
+
165654
166728
  // ../plugin/src/features/magic-context/memory/cosine-similarity.ts
165655
166729
  function cosineSimilarity(a, b) {
165656
166730
  if (a.length !== b.length) {
@@ -165809,19 +166883,19 @@ function isArrayLikeNumber(value) {
165809
166883
  }
165810
166884
  return arr.length === 0 || typeof arr[0] === "number";
165811
166885
  }
165812
- function toFloat32Array2(values) {
166886
+ function toFloat32Array3(values) {
165813
166887
  return values instanceof Float32Array ? new Float32Array(values) : Float32Array.from(Array.from(values));
165814
166888
  }
165815
166889
  function extractBatchEmbeddings(result, expectedCount) {
165816
166890
  const { data } = result;
165817
166891
  if (Array.isArray(data) && data.length === expectedCount && data.every((entry) => typeof entry !== "number" && isArrayLikeNumber(entry))) {
165818
- return data.map((entry) => toFloat32Array2(entry));
166892
+ return data.map((entry) => toFloat32Array3(entry));
165819
166893
  }
165820
166894
  if (!isArrayLikeNumber(data)) {
165821
166895
  log("[magic-context] embedding batch returned unexpected data shape");
165822
166896
  return Array.from({ length: expectedCount }, () => null);
165823
166897
  }
165824
- const flatData = toFloat32Array2(data);
166898
+ const flatData = toFloat32Array3(data);
165825
166899
  const dimension = result.dims?.at(-1) ?? flatData.length / expectedCount;
165826
166900
  if (!Number.isInteger(dimension) || dimension <= 0 || flatData.length !== expectedCount * dimension) {
165827
166901
  log("[magic-context] embedding batch returned invalid dimensions");
@@ -165836,6 +166910,7 @@ function extractBatchEmbeddings(result, expectedCount) {
165836
166910
 
165837
166911
  class LocalEmbeddingProvider {
165838
166912
  modelId;
166913
+ maxInputTokens;
165839
166914
  model;
165840
166915
  pipeline = null;
165841
166916
  initPromise = null;
@@ -165843,8 +166918,9 @@ class LocalEmbeddingProvider {
165843
166918
  disposing = false;
165844
166919
  disposePromise = null;
165845
166920
  inFlightWaiters = [];
165846
- constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL) {
166921
+ constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL, maxInputTokens = 512) {
165847
166922
  this.model = model;
166923
+ this.maxInputTokens = maxInputTokens;
165848
166924
  this.modelId = getEmbeddingProviderIdentity({ provider: "local", model });
165849
166925
  }
165850
166926
  async initialize() {
@@ -166099,6 +167175,7 @@ var FETCH_TIMEOUT_MS = 30000;
166099
167175
 
166100
167176
  class OpenAICompatibleEmbeddingProvider {
166101
167177
  modelId;
167178
+ maxInputTokens;
166102
167179
  endpoint;
166103
167180
  model;
166104
167181
  apiKey;
@@ -166115,11 +167192,13 @@ class OpenAICompatibleEmbeddingProvider {
166115
167192
  this.apiKey = options.apiKey?.trim() ?? "";
166116
167193
  this.inputType = options.inputType?.trim() ?? "";
166117
167194
  this.truncate = options.truncate?.trim() ?? "";
167195
+ this.maxInputTokens = typeof options.maxInputTokens === "number" && Number.isFinite(options.maxInputTokens) ? Math.max(1, Math.floor(options.maxInputTokens)) : 512;
166118
167196
  this.modelId = getEmbeddingProviderIdentity({
166119
167197
  provider: "openai-compatible",
166120
167198
  endpoint: this.endpoint,
166121
167199
  model: this.model,
166122
- ...this.apiKey ? { api_key: this.apiKey } : {}
167200
+ ...this.apiKey ? { api_key: this.apiKey } : {},
167201
+ ...this.inputType ? { input_type: this.inputType } : {}
166123
167202
  });
166124
167203
  }
166125
167204
  async initialize() {
@@ -166310,7 +167389,7 @@ class OpenAICompatibleEmbeddingProvider {
166310
167389
  }
166311
167390
 
166312
167391
  // ../plugin/src/features/magic-context/project-embedding-registry.ts
166313
- import { createHash as createHash7, randomUUID } from "node:crypto";
167392
+ import { createHash as createHash9, randomUUID } from "node:crypto";
166314
167393
  init_logger();
166315
167394
 
166316
167395
  // ../plugin/src/features/magic-context/git-commits/storage-git-commit-embeddings.ts
@@ -166318,7 +167397,7 @@ var saveStatements = new WeakMap;
166318
167397
  var loadProjectStatements = new WeakMap;
166319
167398
  var loadUnembeddedStatements = new WeakMap;
166320
167399
  var countEmbeddedStatements = new WeakMap;
166321
- var clearProjectStatements = new WeakMap;
167400
+ var clearProjectStatements2 = new WeakMap;
166322
167401
  var distinctModelIdStatements = new WeakMap;
166323
167402
  function getSaveStatement(db) {
166324
167403
  let stmt = saveStatements.get(db);
@@ -166366,12 +167445,12 @@ function getCountEmbeddedStatement(db) {
166366
167445
  }
166367
167446
  return stmt;
166368
167447
  }
166369
- function getClearProjectStatement(db) {
166370
- let stmt = clearProjectStatements.get(db);
167448
+ function getClearProjectStatement2(db) {
167449
+ let stmt = clearProjectStatements2.get(db);
166371
167450
  if (!stmt) {
166372
167451
  stmt = db.prepare(`DELETE FROM git_commit_embeddings
166373
167452
  WHERE sha IN (SELECT sha FROM git_commits WHERE project_path = ?)`);
166374
- clearProjectStatements.set(db, stmt);
167453
+ clearProjectStatements2.set(db, stmt);
166375
167454
  }
166376
167455
  return stmt;
166377
167456
  }
@@ -166407,7 +167486,7 @@ function countEmbeddedCommits(db, projectPath) {
166407
167486
  return row?.count ?? 0;
166408
167487
  }
166409
167488
  function clearProjectCommitEmbeddings(db, projectPath) {
166410
- return getClearProjectStatement(db).run(projectPath).changes;
167489
+ return getClearProjectStatement2(db).run(projectPath).changes;
166411
167490
  }
166412
167491
  function getDistinctCommitEmbeddingModelIds(db, projectPath) {
166413
167492
  const rows = getDistinctModelIdStatement(db).all(projectPath);
@@ -166533,9 +167612,83 @@ function releaseGitSweepLease(db, projectPath, holderId) {
166533
167612
  });
166534
167613
  }
166535
167614
 
167615
+ // ../plugin/src/features/magic-context/session-project-storage.ts
167616
+ var upsertSessionProjectStatements = new WeakMap;
167617
+ var repairSessionChunkProjectStatements = new WeakMap;
167618
+ var repairProjectChunkProjectStatements = new WeakMap;
167619
+ function getUpsertSessionProjectStatement(db) {
167620
+ let stmt = upsertSessionProjectStatements.get(db);
167621
+ if (!stmt) {
167622
+ stmt = db.prepare(`INSERT INTO session_projects (session_id, harness, project_path, updated_at)
167623
+ VALUES (?, ?, ?, ?)
167624
+ ON CONFLICT(session_id, harness) DO UPDATE SET
167625
+ project_path = excluded.project_path,
167626
+ updated_at = excluded.updated_at
167627
+ WHERE session_projects.project_path <> excluded.project_path`);
167628
+ upsertSessionProjectStatements.set(db, stmt);
167629
+ }
167630
+ return stmt;
167631
+ }
167632
+ function getRepairSessionChunkProjectStatement(db) {
167633
+ let stmt = repairSessionChunkProjectStatements.get(db);
167634
+ if (!stmt) {
167635
+ stmt = db.prepare(`UPDATE compartment_chunk_embeddings
167636
+ SET project_path = ?
167637
+ WHERE session_id = ?
167638
+ AND harness = ?
167639
+ AND project_path <> ?`);
167640
+ repairSessionChunkProjectStatements.set(db, stmt);
167641
+ }
167642
+ return stmt;
167643
+ }
167644
+ function getRepairProjectChunkProjectStatement(db) {
167645
+ let stmt = repairProjectChunkProjectStatements.get(db);
167646
+ if (!stmt) {
167647
+ stmt = db.prepare(`UPDATE compartment_chunk_embeddings
167648
+ SET project_path = (
167649
+ SELECT sp.project_path
167650
+ FROM session_projects sp
167651
+ WHERE sp.session_id = compartment_chunk_embeddings.session_id
167652
+ AND sp.harness = compartment_chunk_embeddings.harness
167653
+ LIMIT 1
167654
+ )
167655
+ WHERE EXISTS (
167656
+ SELECT 1
167657
+ FROM session_projects sp
167658
+ WHERE sp.session_id = compartment_chunk_embeddings.session_id
167659
+ AND sp.harness = compartment_chunk_embeddings.harness
167660
+ AND sp.project_path <> compartment_chunk_embeddings.project_path
167661
+ AND (
167662
+ sp.project_path = ?
167663
+ OR compartment_chunk_embeddings.project_path = ?
167664
+ )
167665
+ )`);
167666
+ repairProjectChunkProjectStatements.set(db, stmt);
167667
+ }
167668
+ return stmt;
167669
+ }
167670
+ function recordSessionProjectIdentity(db, sessionId, projectPath) {
167671
+ if (!sessionId || !projectPath)
167672
+ return;
167673
+ const harness = getHarness();
167674
+ const now = Date.now();
167675
+ db.transaction(() => {
167676
+ getUpsertSessionProjectStatement(db).run(sessionId, harness, projectPath, now);
167677
+ getRepairSessionChunkProjectStatement(db).run(projectPath, sessionId, harness, projectPath);
167678
+ })();
167679
+ }
167680
+ function repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectPath) {
167681
+ if (!projectPath)
167682
+ return 0;
167683
+ return getRepairProjectChunkProjectStatement(db).run(projectPath, projectPath).changes;
167684
+ }
167685
+
166536
167686
  // ../plugin/src/features/magic-context/project-embedding-registry.ts
166537
167687
  var OFF_PROVIDER_IDENTITY = "embedding-provider:off";
166538
167688
  var SWEEP_MAX_WALL_CLOCK_MS = 10 * 60 * 1000;
167689
+ var CHUNK_DRAIN_BATCH_SIZE = 8;
167690
+ var MAX_WINDOWS_PER_EMBED_CALL = 16;
167691
+ var SESSION_EMBED_LEASE_RENEWAL_MS = 60 * 1000;
166539
167692
  var projectRegistrations = new Map;
166540
167693
  var loadUnembeddedMemoriesStatements = new WeakMap;
166541
167694
  var globalRegistrationGeneration = 0;
@@ -166544,7 +167697,10 @@ function resolveEmbeddingConfig(config2) {
166544
167697
  if (!config2 || config2.provider === "local") {
166545
167698
  return {
166546
167699
  provider: "local",
166547
- model: config2?.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL
167700
+ model: config2?.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
167701
+ ...config2?.max_input_tokens ? {
167702
+ max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
167703
+ } : {}
166548
167704
  };
166549
167705
  }
166550
167706
  if (config2.provider === "openai-compatible") {
@@ -166557,7 +167713,10 @@ function resolveEmbeddingConfig(config2) {
166557
167713
  endpoint: config2.endpoint.trim(),
166558
167714
  ...apiKey ? { api_key: apiKey } : {},
166559
167715
  ...inputType ? { input_type: inputType } : {},
166560
- ...truncate ? { truncate } : {}
167716
+ ...truncate ? { truncate } : {},
167717
+ ...config2.max_input_tokens ? {
167718
+ max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
167719
+ } : {}
166561
167720
  };
166562
167721
  }
166563
167722
  return { provider: "off" };
@@ -166575,10 +167734,11 @@ function createProvider(config2) {
166575
167734
  model: config2.model,
166576
167735
  apiKey: config2.api_key,
166577
167736
  inputType: config2.input_type,
166578
- truncate: config2.truncate
167737
+ truncate: config2.truncate,
167738
+ maxInputTokens: config2.max_input_tokens
166579
167739
  });
166580
167740
  }
166581
- return new LocalEmbeddingProvider(config2.model);
167741
+ return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
166582
167742
  }
166583
167743
  function stableStringify2(value) {
166584
167744
  if (Array.isArray(value)) {
@@ -166591,7 +167751,7 @@ function stableStringify2(value) {
166591
167751
  return JSON.stringify(value);
166592
167752
  }
166593
167753
  function sha256Prefix(value, length = 16) {
166594
- return createHash7("sha256").update(value).digest("hex").slice(0, length);
167754
+ return createHash9("sha256").update(value).digest("hex").slice(0, length);
166595
167755
  }
166596
167756
  function getRuntimeFingerprint(config2) {
166597
167757
  if (config2.provider === "off") {
@@ -166599,6 +167759,18 @@ function getRuntimeFingerprint(config2) {
166599
167759
  }
166600
167760
  return `${getEmbeddingProviderIdentity(config2)}:${sha256Prefix(stableStringify2(config2))}`;
166601
167761
  }
167762
+ function getChunkEmbeddingModelId(config2, providerIdentity) {
167763
+ if (config2.provider === "off") {
167764
+ return OFF_PROVIDER_IDENTITY;
167765
+ }
167766
+ const chunkIdentity = {
167767
+ providerIdentity,
167768
+ chunkerVersion: 1,
167769
+ maxInputTokens: normalizeCompartmentChunkMaxInputTokens("max_input_tokens" in config2 ? config2.max_input_tokens : undefined),
167770
+ truncate: config2.provider === "openai-compatible" ? config2.truncate ?? "" : ""
167771
+ };
167772
+ return `${providerIdentity}:chunk:${sha256Prefix(stableStringify2(chunkIdentity))}`;
167773
+ }
166602
167774
  function sameFeatures(a, b) {
166603
167775
  return a.memoryEnabled === b.memoryEnabled && a.gitCommitEnabled === b.gitCommitEnabled;
166604
167776
  }
@@ -166615,7 +167787,8 @@ function snapshotFor(registration) {
166615
167787
  features: { ...registration.features },
166616
167788
  enabled,
166617
167789
  gitCommitEnabled,
166618
- modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId
167790
+ modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
167791
+ chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId
166619
167792
  };
166620
167793
  }
166621
167794
  function disposeProvider(provider) {
@@ -166635,7 +167808,7 @@ function anyStoredModelIdIsStale(storedIds, currentId) {
166635
167808
  }
166636
167809
  return false;
166637
167810
  }
166638
- function maybeWipeStaleEmbeddings(db, projectIdentity, currentProviderIdentity, features) {
167811
+ function maybeWipeStaleEmbeddings(db, projectIdentity, currentProviderIdentity, currentChunkIdentity, features) {
166639
167812
  if (currentProviderIdentity === OFF_PROVIDER_IDENTITY) {
166640
167813
  return false;
166641
167814
  }
@@ -166656,6 +167829,14 @@ function maybeWipeStaleEmbeddings(db, projectIdentity, currentProviderIdentity,
166656
167829
  wiped = true;
166657
167830
  }
166658
167831
  }
167832
+ if (features.memoryEnabled) {
167833
+ repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectIdentity);
167834
+ const chunkIds = getDistinctChunkEmbeddingModelIds(db, projectIdentity);
167835
+ if (anyStoredModelIdIsStale(chunkIds, currentChunkIdentity)) {
167836
+ clearChunkEmbeddingsForProject(db, projectIdentity);
167837
+ wiped = true;
167838
+ }
167839
+ }
166659
167840
  })();
166660
167841
  return wiped;
166661
167842
  }
@@ -166663,10 +167844,11 @@ function registerProjectEmbeddingAndMaybeWipe(db, projectIdentity, config2, feat
166663
167844
  const resolvedConfig = resolveEmbeddingConfig(config2);
166664
167845
  const providerIdentity = getEmbeddingProviderIdentity(resolvedConfig);
166665
167846
  const runtimeFingerprint = getRuntimeFingerprint(resolvedConfig);
167847
+ const chunkModelId = getChunkEmbeddingModelId(resolvedConfig, providerIdentity);
166666
167848
  const prior = projectRegistrations.get(projectIdentity);
166667
167849
  const canReuseProvider = prior !== undefined && !prior.observationMode && prior.runtimeFingerprint === runtimeFingerprint && prior.providerIdentity === providerIdentity;
166668
- const wiped = maybeWipeStaleEmbeddings(db, projectIdentity, providerIdentity, features);
166669
- const generationChanged = prior === undefined || prior.observationMode || prior.runtimeFingerprint !== runtimeFingerprint || !sameFeatures(prior.features, features) || wiped;
167850
+ const wiped = maybeWipeStaleEmbeddings(db, projectIdentity, providerIdentity, chunkModelId, features);
167851
+ const generationChanged = prior === undefined || prior.observationMode || prior.runtimeFingerprint !== runtimeFingerprint || prior.chunkModelId !== chunkModelId || !sameFeatures(prior.features, features) || wiped;
166670
167852
  const generation = generationChanged ? ++globalRegistrationGeneration : prior.generation;
166671
167853
  const registration = {
166672
167854
  projectIdentity,
@@ -166678,6 +167860,7 @@ function registerProjectEmbeddingAndMaybeWipe(db, projectIdentity, config2, feat
166678
167860
  generation,
166679
167861
  features: { ...features },
166680
167862
  modelId: providerIdentity === OFF_PROVIDER_IDENTITY ? "off" : providerIdentity,
167863
+ chunkModelId: providerIdentity === OFF_PROVIDER_IDENTITY ? "off" : chunkModelId,
166681
167864
  observationMode: false
166682
167865
  };
166683
167866
  projectRegistrations.set(projectIdentity, registration);
@@ -166700,6 +167883,7 @@ function registerProjectInObservationMode(db, projectIdentity, sourceDirectory,
166700
167883
  generation,
166701
167884
  features: { memoryEnabled: false, gitCommitEnabled: false },
166702
167885
  modelId: "off",
167886
+ chunkModelId: "off",
166703
167887
  observationMode: true
166704
167888
  };
166705
167889
  projectRegistrations.set(projectIdentity, registration);
@@ -166710,6 +167894,15 @@ function getProjectEmbeddingSnapshot(projectIdentity) {
166710
167894
  const registration = projectRegistrations.get(projectIdentity);
166711
167895
  return registration ? snapshotFor(registration) : null;
166712
167896
  }
167897
+ function getProjectChunkEmbeddingModelId(projectIdentity) {
167898
+ const registration = projectRegistrations.get(projectIdentity);
167899
+ return registration && !registration.observationMode ? registration.chunkModelId : "off";
167900
+ }
167901
+ function getProjectEmbeddingMaxInputTokens(projectIdentity) {
167902
+ const registration = projectRegistrations.get(projectIdentity);
167903
+ const configMax = registration?.config && "max_input_tokens" in registration.config ? registration.config.max_input_tokens : undefined;
167904
+ return normalizeCompartmentChunkMaxInputTokens(registration?.provider?.maxInputTokens ?? configMax);
167905
+ }
166713
167906
  function getOrCreateProjectProvider(registration) {
166714
167907
  if (registration.providerIdentity === OFF_PROVIDER_IDENTITY || registration.observationMode) {
166715
167908
  return null;
@@ -166804,6 +167997,131 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
166804
167997
  return 0;
166805
167998
  }
166806
167999
  }
168000
+ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates, signal) {
168001
+ const noWork = [];
168002
+ if (candidates.length === 0)
168003
+ return { embedded: 0, noWork };
168004
+ const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectIdentity);
168005
+ const prepared = [];
168006
+ for (const candidate of candidates) {
168007
+ const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage);
168008
+ if (canonicalText.length === 0) {
168009
+ noWork.push(candidate.id);
168010
+ continue;
168011
+ }
168012
+ const windows = chunkCanonicalText(canonicalText, candidate.startMessage, candidate.endMessage, maxInputTokens);
168013
+ if (windows.length === 0 || chunkEmbeddingWindowsAreCurrent(db, candidate.id, modelId, windows, projectIdentity)) {
168014
+ noWork.push(candidate.id);
168015
+ continue;
168016
+ }
168017
+ prepared.push({ candidate, windows });
168018
+ }
168019
+ if (prepared.length === 0)
168020
+ return { embedded: 0, noWork };
168021
+ let embedded = 0;
168022
+ let i = 0;
168023
+ while (i < prepared.length) {
168024
+ if (signal?.aborted)
168025
+ break;
168026
+ const slice = [];
168027
+ let windowCount = 0;
168028
+ do {
168029
+ const item = prepared[i];
168030
+ slice.push(item);
168031
+ windowCount += item.windows.length;
168032
+ i += 1;
168033
+ } while (i < prepared.length && windowCount + prepared[i].windows.length <= MAX_WINDOWS_PER_EMBED_CALL);
168034
+ const texts = [];
168035
+ for (const item of slice)
168036
+ texts.push(...item.windows.map((w) => w.text));
168037
+ try {
168038
+ const result = await embedBatchForProject(projectIdentity, texts, signal);
168039
+ if (!result)
168040
+ continue;
168041
+ if (signal?.aborted)
168042
+ break;
168043
+ let offset = 0;
168044
+ for (const item of slice) {
168045
+ const vectors = result.vectors.slice(offset, offset + item.windows.length);
168046
+ offset += item.windows.length;
168047
+ if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
168048
+ continue;
168049
+ }
168050
+ const rows = item.windows.map((window, index) => ({
168051
+ compartmentId: item.candidate.id,
168052
+ sessionId: item.candidate.sessionId,
168053
+ projectPath: projectIdentity,
168054
+ window,
168055
+ modelId,
168056
+ vector: vectors[index]
168057
+ }));
168058
+ replaceCompartmentChunkEmbeddings(db, rows);
168059
+ embedded += 1;
168060
+ }
168061
+ } catch (error51) {
168062
+ log("[magic-context] failed to proactively embed compartment chunks:", error51);
168063
+ }
168064
+ }
168065
+ return { embedded, noWork };
168066
+ }
168067
+ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, options) {
168068
+ const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
168069
+ if (!snapshot?.enabled || snapshot.chunkModelId === "off") {
168070
+ return { status: "disabled", embedded: 0, total: 0 };
168071
+ }
168072
+ recordSessionProjectIdentity(db, sessionId, projectIdentity);
168073
+ const total = countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId);
168074
+ if (total === 0)
168075
+ return { status: "nothing", embedded: 0, total: 0 };
168076
+ const holderId = `session-embed-${randomUUID()}`;
168077
+ const lease2 = acquireGitSweepLease(db, projectIdentity, holderId, { ignoreCooldown: true });
168078
+ if (!lease2.acquired)
168079
+ return { status: "busy", embedded: 0, total };
168080
+ const renewal = setInterval(() => {
168081
+ try {
168082
+ renewGitSweepLease(db, projectIdentity, holderId);
168083
+ } catch {}
168084
+ }, SESSION_EMBED_LEASE_RENEWAL_MS);
168085
+ renewal.unref?.();
168086
+ const batchSize = Math.max(1, options?.batchSize ?? CHUNK_DRAIN_BATCH_SIZE);
168087
+ const skipIds = [];
168088
+ let embedded = 0;
168089
+ let aborted2 = false;
168090
+ let providerStalled = false;
168091
+ try {
168092
+ options?.onProgress?.({ embedded, total });
168093
+ for (;; ) {
168094
+ if (options?.signal?.aborted) {
168095
+ aborted2 = true;
168096
+ break;
168097
+ }
168098
+ const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, skipIds);
168099
+ if (candidates.length === 0)
168100
+ break;
168101
+ const { embedded: n, noWork } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
168102
+ for (const id of noWork)
168103
+ skipIds.push(id);
168104
+ if (n === 0 && noWork.length === 0) {
168105
+ providerStalled = true;
168106
+ break;
168107
+ }
168108
+ embedded += n;
168109
+ options?.onProgress?.({ embedded: Math.min(embedded, total), total });
168110
+ await new Promise((resolve4) => setTimeout(resolve4, 0));
168111
+ }
168112
+ } finally {
168113
+ clearInterval(renewal);
168114
+ releaseGitSweepLease(db, projectIdentity, holderId);
168115
+ }
168116
+ if (aborted2)
168117
+ return { status: "aborted", embedded, total };
168118
+ if (providerStalled) {
168119
+ const remaining = Math.max(0, countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId) - skipIds.length);
168120
+ if (remaining > 0)
168121
+ return { status: "stalled", embedded, total, remaining };
168122
+ }
168123
+ return { status: "done", embedded, total };
168124
+ }
166807
168125
 
166808
168126
  // ../plugin/src/features/magic-context/memory/embedding.ts
166809
168127
  var DEFAULT_EMBEDDING_CONFIG = {
@@ -166823,10 +168141,11 @@ function createProvider2(config2) {
166823
168141
  model: config2.model,
166824
168142
  apiKey: config2.api_key,
166825
168143
  inputType: config2.input_type,
166826
- truncate: config2.truncate
168144
+ truncate: config2.truncate,
168145
+ maxInputTokens: config2.max_input_tokens
166827
168146
  });
166828
168147
  }
166829
- return new LocalEmbeddingProvider(config2.model);
168148
+ return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
166830
168149
  }
166831
168150
  function getOrCreateProvider() {
166832
168151
  if (provider) {
@@ -167069,6 +168388,7 @@ init_logger();
167069
168388
  // ../plugin/src/features/magic-context/memory/storage-memory-fts.ts
167070
168389
  var DEFAULT_SEARCH_LIMIT = 10;
167071
168390
  var searchStatements = new WeakMap;
168391
+ var unionSearchStatements = new Map;
167072
168392
  function getSearchStatement(db) {
167073
168393
  let stmt = searchStatements.get(db);
167074
168394
  if (!stmt) {
@@ -167077,6 +168397,23 @@ function getSearchStatement(db) {
167077
168397
  }
167078
168398
  return stmt;
167079
168399
  }
168400
+ function getUnionSearchStatement(db, arity) {
168401
+ let statements = unionSearchStatements.get(arity);
168402
+ if (!statements) {
168403
+ statements = new WeakMap;
168404
+ unionSearchStatements.set(arity, statements);
168405
+ }
168406
+ let stmt = statements.get(db);
168407
+ if (!stmt) {
168408
+ const placeholders3 = Array.from({ length: arity }, () => "?").join(", ");
168409
+ stmt = db.prepare(`SELECT ${getMemorySelectColumns(db)} FROM memories_fts INNER JOIN memories ON memories.id = memories_fts.rowid WHERE memories.project_path IN (${placeholders3}) AND memories.status IN ('active', 'permanent') AND (memories.expires_at IS NULL OR memories.expires_at > ?) AND memories_fts MATCH ? ORDER BY bm25(memories_fts), memories.updated_at DESC, memories.id ASC LIMIT ?`);
168410
+ statements.set(db, stmt);
168411
+ }
168412
+ return stmt;
168413
+ }
168414
+ function uniqueProjectPaths2(projectPaths) {
168415
+ return [...new Set(projectPaths.filter((path7) => path7.length > 0))];
168416
+ }
167080
168417
  function sanitizeFtsQuery(query) {
167081
168418
  const tokens = query.split(/\s+/).filter((token) => token.length > 0);
167082
168419
  if (tokens.length === 0)
@@ -167095,6 +168432,28 @@ function searchMemoriesFTS(db, projectPath, query, limit = DEFAULT_SEARCH_LIMIT)
167095
168432
  const rows = getSearchStatement(db).all(projectPath, Date.now(), sanitized, limit).filter(isMemoryRow);
167096
168433
  return rows.map(toMemory);
167097
168434
  }
168435
+ function searchMemoriesFTSUnion(db, projectPaths, query, limit = DEFAULT_SEARCH_LIMIT, ownIdentities, shareCategories) {
168436
+ const identities = uniqueProjectPaths2(projectPaths);
168437
+ if (identities.length === 0)
168438
+ return [];
168439
+ const sharingFilter = buildWorkspaceMemorySqlFilter({
168440
+ identities,
168441
+ ownIdentities,
168442
+ shareCategories,
168443
+ tableName: "memories"
168444
+ });
168445
+ if (identities.length === 1 && !sharingFilter.active) {
168446
+ return searchMemoriesFTS(db, identities[0], query, limit);
168447
+ }
168448
+ const trimmedQuery = query.trim();
168449
+ if (trimmedQuery.length === 0 || limit <= 0)
168450
+ return [];
168451
+ const sanitized = sanitizeFtsQuery(trimmedQuery);
168452
+ if (sanitized.length === 0)
168453
+ return [];
168454
+ const rows = sharingFilter.active ? db.prepare(`SELECT ${getMemorySelectColumns(db)} FROM memories_fts INNER JOIN memories ON memories.id = memories_fts.rowid WHERE memories.project_path IN (${identities.map(() => "?").join(", ")}) AND memories.status IN ('active', 'permanent') AND (memories.expires_at IS NULL OR memories.expires_at > ?) AND memories_fts MATCH ?${sharingFilter.clause} ORDER BY bm25(memories_fts), memories.updated_at DESC, memories.id ASC LIMIT ?`).all(...identities, Date.now(), sanitized, ...sharingFilter.params, limit).filter(isMemoryRow) : getUnionSearchStatement(db, identities.length).all(...identities, Date.now(), sanitized, limit).filter(isMemoryRow);
168455
+ return rows.map(toMemory);
168456
+ }
167098
168457
 
167099
168458
  // ../plugin/src/features/magic-context/git-commits/search-git-commits.ts
167100
168459
  var ftsStatements = new WeakMap;
@@ -167387,7 +168746,7 @@ async function sweepGitCommits(args) {
167387
168746
  }
167388
168747
 
167389
168748
  // ../plugin/src/plugin/embedding-bootstrap-helpers.ts
167390
- import { createHash as createHash8 } from "node:crypto";
168749
+ import { createHash as createHash10 } from "node:crypto";
167391
168750
  init_logger();
167392
168751
  var EMBEDDING_AFFECTING_KEYS = new Set([
167393
168752
  "embedding.api_key",
@@ -167409,7 +168768,7 @@ var EMBEDDING_WARNING_TERMS = [
167409
168768
  ];
167410
168769
  var loggedFailureSignatures = new Map;
167411
168770
  function sha256Prefix2(value, length = 16) {
167412
- return createHash8("sha256").update(value).digest("hex").slice(0, length);
168771
+ return createHash10("sha256").update(value).digest("hex").slice(0, length);
167413
168772
  }
167414
168773
  function warningLooksEmbeddingRelated(message) {
167415
168774
  const lower = message.toLowerCase();
@@ -168451,6 +169810,88 @@ function registerCtxDreamCommand(pi, deps) {
168451
169810
  });
168452
169811
  }
168453
169812
 
169813
+ // src/commands/ctx-embed-history.ts
169814
+ function registerCtxEmbedHistoryCommand(pi, deps) {
169815
+ pi.registerCommand("ctx-embed-history", {
169816
+ description: "Embed all of this session's history compartments for semantic search, in one pass",
169817
+ handler: async (_args, ctx) => {
169818
+ const sessionId = resolveSessionId(ctx);
169819
+ if (!sessionId) {
169820
+ sendCtxStatusMessage(pi, {
169821
+ title: "/ctx-embed-history",
169822
+ text: `## /ctx-embed-history
169823
+
169824
+ No active Pi session is available.`,
169825
+ level: "error"
169826
+ });
169827
+ return;
169828
+ }
169829
+ if (deps.memoryEnabled === false) {
169830
+ sendCtxStatusMessage(pi, {
169831
+ title: "/ctx-embed-history",
169832
+ text: `## /ctx-embed-history
169833
+
169834
+ Memory is disabled for this project, so there is no semantic embedding to backfill.`,
169835
+ level: "info"
169836
+ });
169837
+ return;
169838
+ }
169839
+ const project = deps.resolveProject?.(ctx) ?? {
169840
+ projectDir: deps.projectDir,
169841
+ projectIdentity: deps.projectIdentity
169842
+ };
169843
+ await ensureProjectRegisteredFromPiDirectory(project.projectDir, deps.db);
169844
+ const outcome = await embedSessionCompartmentChunks(deps.db, project.projectIdentity, sessionId);
169845
+ const { text, level } = (() => {
169846
+ switch (outcome.status) {
169847
+ case "nothing":
169848
+ return {
169849
+ text: `## /ctx-embed-history
169850
+
169851
+ All of this session's history is already embedded.`,
169852
+ level: "info"
169853
+ };
169854
+ case "disabled":
169855
+ return {
169856
+ text: `## /ctx-embed-history
169857
+
169858
+ No embedding provider is configured, so there is nothing to embed.`,
169859
+ level: "info"
169860
+ };
169861
+ case "busy":
169862
+ return {
169863
+ text: `## /ctx-embed-history
169864
+
169865
+ Embedding is already running for this project — ${outcome.total} compartment${outcome.total === 1 ? "" : "s"} still pending. Try again shortly.`,
169866
+ level: "info"
169867
+ };
169868
+ case "stalled":
169869
+ return {
169870
+ text: `## /ctx-embed-history
169871
+
169872
+ Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"}; ${outcome.remaining} could not be embedded (the provider returned no result). Run /ctx-embed-history again to retry them.`,
169873
+ level: "info"
169874
+ };
169875
+ default:
169876
+ return {
169877
+ text: `## /ctx-embed-history
169878
+
169879
+ Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search.`,
169880
+ level: "success"
169881
+ };
169882
+ }
169883
+ })();
169884
+ sendCtxStatusMessage(pi, { title: "/ctx-embed-history", text, level }, {
169885
+ sessionId,
169886
+ projectIdentity: project.projectIdentity,
169887
+ status: outcome.status,
169888
+ embedded: outcome.embedded,
169889
+ total: outcome.total
169890
+ });
169891
+ }
169892
+ });
169893
+ }
169894
+
168454
169895
  // ../plugin/src/hooks/magic-context/execute-flush.ts
168455
169896
  init_logger();
168456
169897
  function executeFlush(db, sessionId) {
@@ -168866,6 +170307,7 @@ function computePiWorkMetrics(sessionEntries) {
168866
170307
 
168867
170308
  // ../plugin/src/hooks/magic-context/apply-operations.ts
168868
170309
  var USER_DROP_PREVIEW_CHARS = 250;
170310
+ var RECENT_TOOL_SKELETON_WINDOW = 20;
168869
170311
  function buildReplacementContent(tagId, target) {
168870
170312
  const role = target.message?.info.role;
168871
170313
  if (role !== "user") {
@@ -168891,6 +170333,7 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
168891
170333
  const tagTypeById = new Map(tags.map((tag) => [tag.tagNumber, tag.type]));
168892
170334
  const protectedTagIds = protectedTags > 0 ? new Set(tags.filter((tag) => tag.status === "active").map((tag) => tag.tagNumber).sort((left, right) => right - left).slice(0, protectedTags)) : new Set;
168893
170335
  const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
170336
+ const skeletonWindow = new Set(tags.filter((tag) => tag.type === "tool").map((tag) => tag.tagNumber).sort((left, right) => right - left).slice(0, RECENT_TOOL_SKELETON_WINDOW));
168894
170337
  for (const pendingOp of pendingOps) {
168895
170338
  const tagStatus = tagStatusById.get(pendingOp.tagId);
168896
170339
  if (tagStatus === "compacted" || tagStatus === "dropped") {
@@ -168903,14 +170346,25 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
168903
170346
  const target = targets.get(pendingOp.tagId);
168904
170347
  const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
168905
170348
  if (isToolTag) {
168906
- const dropResult = target?.drop?.() ?? "absent";
168907
- if (dropResult === "incomplete") {
168908
- continue;
168909
- }
168910
- if (dropResult === "removed") {
168911
- didMutateMessage = true;
170349
+ if (skeletonWindow.has(pendingOp.tagId)) {
170350
+ const truncResult = target?.truncate?.() ?? "absent";
170351
+ if (truncResult === "incomplete") {
170352
+ continue;
170353
+ }
170354
+ if (truncResult === "truncated") {
170355
+ didMutateMessage = true;
170356
+ }
170357
+ updateTagDropMode(db, sessionId, pendingOp.tagId, "truncated");
170358
+ } else {
170359
+ const dropResult = target?.drop?.() ?? "absent";
170360
+ if (dropResult === "incomplete") {
170361
+ continue;
170362
+ }
170363
+ if (dropResult === "removed") {
170364
+ didMutateMessage = true;
170365
+ }
170366
+ updateTagDropMode(db, sessionId, pendingOp.tagId, "full");
168912
170367
  }
168913
- updateTagDropMode(db, sessionId, pendingOp.tagId, "full");
168914
170368
  } else if (target) {
168915
170369
  const changed = target.setContent(buildReplacementContent(pendingOp.tagId, target));
168916
170370
  if (changed)
@@ -170573,53 +172027,6 @@ ${body}
170573
172027
  </system-reminder>`;
170574
172028
  }
170575
172029
 
170576
- // ../plugin/src/features/magic-context/memory/constants.ts
170577
- var V2_MEMORY_CATEGORIES = [
170578
- "PROJECT_RULES",
170579
- "ARCHITECTURE",
170580
- "CONSTRAINTS",
170581
- "CONFIG_VALUES",
170582
- "NAMING"
170583
- ];
170584
- var PROMOTABLE_CATEGORIES = [
170585
- "PROJECT_RULES",
170586
- "ARCHITECTURE",
170587
- "CONSTRAINTS",
170588
- "CONFIG_VALUES",
170589
- "NAMING",
170590
- "ARCHITECTURE_DECISIONS",
170591
- "CONFIG_DEFAULTS",
170592
- "USER_PREFERENCES",
170593
- "USER_DIRECTIVES",
170594
- "ENVIRONMENT",
170595
- "WORKFLOW_RULES",
170596
- "KNOWN_ISSUES"
170597
- ];
170598
- var CATEGORY_PRIORITY = [
170599
- "PROJECT_RULES",
170600
- "ARCHITECTURE",
170601
- "CONSTRAINTS",
170602
- "CONFIG_VALUES",
170603
- "NAMING",
170604
- "USER_DIRECTIVES",
170605
- "USER_PREFERENCES",
170606
- "CONFIG_DEFAULTS",
170607
- "ARCHITECTURE_DECISIONS",
170608
- "ENVIRONMENT",
170609
- "WORKFLOW_RULES",
170610
- "KNOWN_ISSUES"
170611
- ];
170612
- var MEMORY_CATEGORY_ORDER_UNKNOWN = 99;
170613
- var MEMORY_CATEGORY_ORDER_PRIORITY = CATEGORY_PRIORITY.reduce((acc, category, index) => {
170614
- acc[category] = index;
170615
- return acc;
170616
- }, {});
170617
- var MEMORY_CATEGORY_ORDER_SQL = `CASE category ${CATEGORY_PRIORITY.map((category, index) => `WHEN '${category}' THEN ${index}`).join(" ")} ELSE ${MEMORY_CATEGORY_ORDER_UNKNOWN} END`;
170618
- var CATEGORY_DEFAULT_TTL = {
170619
- WORKFLOW_RULES: 90 * 24 * 60 * 60 * 1000,
170620
- KNOWN_ISSUES: 30 * 24 * 60 * 60 * 1000
170621
- };
170622
-
170623
172030
  // ../plugin/src/shared/bounded-session-map.ts
170624
172031
  class BoundedSessionMap {
170625
172032
  maxEntries;
@@ -171030,26 +172437,40 @@ ${sections.join(`
171030
172437
  var DEFAULT_MEMORY_BUDGET_TOKENS = 8000;
171031
172438
  var MEMORY_BLOCK_WRAPPER_TOKENS = 6;
171032
172439
  var DEFAULT_USER_PROFILE_BUDGET_TOKENS = 4000;
172440
+ function memoryCanonicalIdentity(memory, workspace) {
172441
+ return resolveStoredPathWorkspaceIdentity(memory.projectPath, workspace.identities, workspace.canonicalIdentityByStoredPath);
172442
+ }
172443
+ function memorySelectionOrder(left, right) {
172444
+ if (left.status === "permanent" && right.status !== "permanent")
172445
+ return -1;
172446
+ if (right.status === "permanent" && left.status !== "permanent")
172447
+ return 1;
172448
+ const leftImportance = left.importance ?? Number.NEGATIVE_INFINITY;
172449
+ const rightImportance = right.importance ?? Number.NEGATIVE_INFINITY;
172450
+ const importanceDiff = rightImportance - leftImportance;
172451
+ if (importanceDiff !== 0)
172452
+ return importanceDiff;
172453
+ return left.id - right.id;
172454
+ }
172455
+ function memoryRenderOrder(left, right) {
172456
+ const aPriority = MEMORY_CATEGORY_ORDER_PRIORITY[left.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
172457
+ const bPriority = MEMORY_CATEGORY_ORDER_PRIORITY[right.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
172458
+ const categoryDiff = aPriority - bPriority;
172459
+ if (categoryDiff !== 0)
172460
+ return categoryDiff;
172461
+ return left.id - right.id;
172462
+ }
171033
172463
  var maxCompartmentSeqStatements = new WeakMap;
171034
172464
  var maxMemoryIdStatements = new WeakMap;
171035
172465
  var legacyCompartmentCountStatements = new WeakMap;
171036
172466
  var m0CompartmentStatements = new WeakMap;
171037
172467
  var newCompartmentStatements = new WeakMap;
171038
- function trimMemoriesToBudgetV2(sessionId, memories, budgetTokens) {
171039
- const selectionOrder = [...memories].sort((a, b) => {
171040
- if (a.status === "permanent" && b.status !== "permanent")
171041
- return -1;
171042
- if (b.status === "permanent" && a.status !== "permanent")
171043
- return 1;
171044
- const importanceDiff = (b.importance ?? 50) - (a.importance ?? 50);
171045
- if (importanceDiff !== 0)
171046
- return importanceDiff;
171047
- return a.id - b.id;
171048
- });
172468
+ function trimMemoriesToBudgetV2(sessionId, memories, budgetTokens, renderOptions = {}) {
172469
+ const selectionOrder = [...memories].sort(memorySelectionOrder);
171049
172470
  const selected = [];
171050
172471
  let usedTokens = MEMORY_BLOCK_WRAPPER_TOKENS;
171051
172472
  for (const memory of selectionOrder) {
171052
- const memoryTokens = estimateTokens(renderMemoryLineV2(memory));
172473
+ const memoryTokens = estimateTokens(renderMemoryLineV2(memory, renderOptions.sourceNameByMemoryId?.get(memory.id)));
171053
172474
  if (usedTokens + memoryTokens > budgetTokens)
171054
172475
  continue;
171055
172476
  selected.push(memory);
@@ -171058,16 +172479,70 @@ function trimMemoriesToBudgetV2(sessionId, memories, budgetTokens) {
171058
172479
  if (selected.length < memories.length) {
171059
172480
  sessionLog(sessionId, `v2 trimmed memories from ${memories.length} to ${selected.length} to fit injection budget of ${budgetTokens} tokens`);
171060
172481
  }
171061
- const renderOrder = [...selected].sort((a, b) => {
171062
- const aPriority = MEMORY_CATEGORY_ORDER_PRIORITY[a.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
171063
- const bPriority = MEMORY_CATEGORY_ORDER_PRIORITY[b.category] ?? MEMORY_CATEGORY_ORDER_UNKNOWN;
171064
- const categoryDiff = aPriority - bPriority;
171065
- if (categoryDiff !== 0)
171066
- return categoryDiff;
171067
- return a.id - b.id;
171068
- });
172482
+ const renderOrder = [...selected].sort(memoryRenderOrder);
171069
172483
  return { selected, renderOrder };
171070
172484
  }
172485
+ function trimWorkspaceMemoriesToBudgetV2(sessionId, memories, budgetTokens, workspace, renderOptions = {}) {
172486
+ if (!workspace.isWorkspaced) {
172487
+ return trimMemoriesToBudgetV2(sessionId, memories, budgetTokens, renderOptions);
172488
+ }
172489
+ const selected = [];
172490
+ const selectedIds = new Set;
172491
+ let usedTokens = MEMORY_BLOCK_WRAPPER_TOKENS;
172492
+ const tokenCost = (memory) => estimateTokens(renderMemoryLineV2(memory, renderOptions.sourceNameByMemoryId?.get(memory.id)));
172493
+ const trySelect = (memory) => {
172494
+ if (selectedIds.has(memory.id))
172495
+ return false;
172496
+ const tokens = tokenCost(memory);
172497
+ if (usedTokens + tokens > budgetTokens)
172498
+ return false;
172499
+ selected.push(memory);
172500
+ selectedIds.add(memory.id);
172501
+ usedTokens += tokens;
172502
+ return true;
172503
+ };
172504
+ for (const memory of memories.filter((candidate) => candidate.status === "permanent").sort(memorySelectionOrder)) {
172505
+ trySelect(memory);
172506
+ }
172507
+ const remainingAfterPermanent = Math.max(0, budgetTokens - usedTokens);
172508
+ const floorTokens = remainingAfterPermanent / Math.max(1, workspace.identities.length);
172509
+ const byIdentity = new Map;
172510
+ for (const memory of memories) {
172511
+ if (memory.status === "permanent")
172512
+ continue;
172513
+ const identity = memoryCanonicalIdentity(memory, workspace);
172514
+ if (!identity)
172515
+ continue;
172516
+ const list = byIdentity.get(identity) ?? [];
172517
+ list.push(memory);
172518
+ byIdentity.set(identity, list);
172519
+ }
172520
+ for (const identity of workspace.identities) {
172521
+ let memberTokens = 0;
172522
+ const candidates = (byIdentity.get(identity) ?? []).sort(memorySelectionOrder);
172523
+ for (const memory of candidates) {
172524
+ if (selectedIds.has(memory.id))
172525
+ continue;
172526
+ const tokens = tokenCost(memory);
172527
+ if (memberTokens + tokens > floorTokens)
172528
+ continue;
172529
+ if (usedTokens + tokens > budgetTokens)
172530
+ continue;
172531
+ selected.push(memory);
172532
+ selectedIds.add(memory.id);
172533
+ usedTokens += tokens;
172534
+ memberTokens += tokens;
172535
+ }
172536
+ }
172537
+ const remaining = memories.filter((memory) => !selectedIds.has(memory.id)).sort(memorySelectionOrder);
172538
+ for (const memory of remaining) {
172539
+ trySelect(memory);
172540
+ }
172541
+ if (selected.length < memories.length) {
172542
+ sessionLog(sessionId, `v2 trimmed memories from ${memories.length} to ${selected.length} to fit injection budget of ${budgetTokens} tokens`);
172543
+ }
172544
+ return { selected, renderOrder: [...selected].sort(memoryRenderOrder) };
172545
+ }
171071
172546
  function trimUserMemoriesToBudget(memories, budgetTokens) {
171072
172547
  const selected = [];
171073
172548
  let usedTokens = 0;
@@ -171080,15 +172555,16 @@ function trimUserMemoriesToBudget(memories, budgetTokens) {
171080
172555
  }
171081
172556
  return selected;
171082
172557
  }
171083
- function renderMemoryLineV2(memory) {
171084
- return ` <memory id="${memory.id}" category="${escapeXmlAttr(memory.category)}" importance="${memory.importance ?? 50}">${escapeXmlContent(memory.content)}</memory>`;
172558
+ function renderMemoryLineV2(memory, sourceName) {
172559
+ const sourceAttr = sourceName ? ` source="${escapeXmlAttr(sourceName)}"` : "";
172560
+ return ` <memory id="${memory.id}" category="${escapeXmlAttr(memory.category)}"${sourceAttr} importance="${memory.importance ?? 50}">${escapeXmlContent(memory.content)}</memory>`;
171085
172561
  }
171086
- function renderMemoryBlockV2(memories, wrapper = "project-memory") {
172562
+ function renderMemoryBlockV2(memories, wrapper = "project-memory", renderOptions = {}) {
171087
172563
  if (memories.length === 0)
171088
172564
  return "";
171089
172565
  const lines = [`<${wrapper}>`];
171090
172566
  for (const memory of memories) {
171091
- lines.push(renderMemoryLineV2(memory));
172567
+ lines.push(renderMemoryLineV2(memory, renderOptions.sourceNameByMemoryId?.get(memory.id)));
171092
172568
  }
171093
172569
  lines.push(`</${wrapper}>`);
171094
172570
  return lines.join(`
@@ -171682,7 +173158,7 @@ async function ensureMemoryEmbeddings(args) {
171682
173158
  continue;
171683
173159
  }
171684
173160
  saveEmbedding(args.db, memory.id, embedding, result.modelId);
171685
- staged.set(memory.id, embedding);
173161
+ staged.set(memory.id, { embedding, modelId: result.modelId });
171686
173162
  }
171687
173163
  })();
171688
173164
  const currentSnapshot = getProjectEmbeddingSnapshot(args.projectIdentity);
@@ -171773,6 +173249,37 @@ function previewText(text) {
171773
173249
  }
171774
173250
  return `${normalized.slice(0, RESULT_PREVIEW_LIMIT - 1).trimEnd()}…`;
171775
173251
  }
173252
+ function resolveSearchWorkspaceContext(db, projectPath, identitySet) {
173253
+ const resolved = identitySet ?? resolveWorkspaceIdentitySet(db, projectPath);
173254
+ const isWorkspaced = resolved.identities.length > 1;
173255
+ const expanded = expandWorkspaceIdentitySetWithAliases(db, resolved.identities);
173256
+ const expandedIdentities = isWorkspaced ? expanded.expandedIdentities : resolved.identities;
173257
+ const canonicalIdentityByStoredPath = isWorkspaced ? expanded.canonicalIdentityByStoredPath : new Map(resolved.identities.map((identity) => [identity, identity]));
173258
+ const ownIdentities = expandedIdentities.filter((identity) => canonicalIdentityByStoredPath.get(identity) === projectPath);
173259
+ return {
173260
+ identities: resolved.identities,
173261
+ expandedIdentities,
173262
+ ownIdentities,
173263
+ shareCategories: isWorkspaced ? resolveWorkspaceShareCategories(db, projectPath) : null,
173264
+ namesByIdentity: resolved.namesByIdentity,
173265
+ canonicalIdentityByStoredPath,
173266
+ isWorkspaced
173267
+ };
173268
+ }
173269
+ function memoryWorkspaceIdentity(memory, workspace) {
173270
+ return resolveStoredPathWorkspaceIdentity(memory.projectPath, workspace.identities, workspace.canonicalIdentityByStoredPath);
173271
+ }
173272
+ function sourceNamesForSearchMemories(args) {
173273
+ if (!args.workspace.isWorkspaced)
173274
+ return;
173275
+ const sourceNames = new Map;
173276
+ for (const memory of args.memories) {
173277
+ const source = sourceNameForMemory(memory.projectPath, args.projectPath, args.workspace.identities, args.workspace.namesByIdentity, args.workspace.canonicalIdentityByStoredPath);
173278
+ if (source)
173279
+ sourceNames.set(memory.id, source);
173280
+ }
173281
+ return sourceNames.size > 0 ? sourceNames : undefined;
173282
+ }
171776
173283
  function getMessageSearchStatement(db) {
171777
173284
  let stmt = messageSearchStatements.get(db);
171778
173285
  if (!stmt) {
@@ -171781,6 +173288,30 @@ function getMessageSearchStatement(db) {
171781
173288
  }
171782
173289
  return stmt;
171783
173290
  }
173291
+ var ftsRowCountStatements = new WeakMap;
173292
+ var ftsMatchCountStatements = new WeakMap;
173293
+ function getSessionFtsRowCount(db, sessionId) {
173294
+ let stmt = ftsRowCountStatements.get(db);
173295
+ if (!stmt) {
173296
+ stmt = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ?");
173297
+ ftsRowCountStatements.set(db, stmt);
173298
+ }
173299
+ const row = stmt.get(sessionId);
173300
+ return typeof row?.n === "number" ? row.n : 0;
173301
+ }
173302
+ function countSessionFtsMatches(db, sessionId, ftsQuery) {
173303
+ let stmt = ftsMatchCountStatements.get(db);
173304
+ if (!stmt) {
173305
+ stmt = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ? AND message_history_fts MATCH ?");
173306
+ ftsMatchCountStatements.set(db, stmt);
173307
+ }
173308
+ try {
173309
+ const row = stmt.get(sessionId, ftsQuery);
173310
+ return typeof row?.n === "number" ? row.n : 0;
173311
+ } catch {
173312
+ return 0;
173313
+ }
173314
+ }
171784
173315
  function getMessageOrdinal(value) {
171785
173316
  if (typeof value === "number" && Number.isFinite(value)) {
171786
173317
  return value;
@@ -171796,25 +173327,63 @@ async function getSemanticScores(args) {
171796
173327
  if (!args.queryEmbedding || args.memories.length === 0) {
171797
173328
  return semanticScores;
171798
173329
  }
171799
- const cachedEmbeddings = getProjectEmbeddings(args.db, args.projectPath);
171800
- const embeddings = await ensureMemoryEmbeddings({
171801
- db: args.db,
171802
- projectIdentity: args.projectPath,
171803
- memories: args.memories,
171804
- existingEmbeddings: cachedEmbeddings
171805
- });
173330
+ if (!args.workspace?.isWorkspaced) {
173331
+ const cachedEmbeddings = getProjectEmbeddings(args.db, args.projectPath);
173332
+ const embeddings = await ensureMemoryEmbeddings({
173333
+ db: args.db,
173334
+ projectIdentity: args.projectPath,
173335
+ memories: args.memories,
173336
+ existingEmbeddings: cachedEmbeddings
173337
+ });
173338
+ for (const memory of args.memories) {
173339
+ const memoryEmbedding = embeddings.get(memory.id);
173340
+ if (!memoryEmbedding) {
173341
+ continue;
173342
+ }
173343
+ semanticScores.set(memory.id, normalizeCosineScore(cosineSimilarity(args.queryEmbedding, memoryEmbedding.embedding)));
173344
+ }
173345
+ return semanticScores;
173346
+ }
173347
+ if (!args.queryModelId || args.queryModelId === "off") {
173348
+ return semanticScores;
173349
+ }
173350
+ const workspace = args.workspace;
173351
+ const memoriesByIdentity = new Map;
171806
173352
  for (const memory of args.memories) {
171807
- const memoryEmbedding = embeddings.get(memory.id);
171808
- if (!memoryEmbedding) {
173353
+ const identity = memoryWorkspaceIdentity(memory, workspace);
173354
+ if (!identity)
173355
+ continue;
173356
+ const list = memoriesByIdentity.get(identity) ?? [];
173357
+ list.push(memory);
173358
+ memoriesByIdentity.set(identity, list);
173359
+ }
173360
+ const ownMemories = memoriesByIdentity.get(args.projectPath) ?? [];
173361
+ if (ownMemories.length > 0) {
173362
+ const ownEmbeddings = getProjectEmbeddings(args.db, args.projectPath);
173363
+ await ensureMemoryEmbeddings({
173364
+ db: args.db,
173365
+ projectIdentity: args.projectPath,
173366
+ memories: ownMemories,
173367
+ existingEmbeddings: ownEmbeddings
173368
+ });
173369
+ }
173370
+ for (const identity of workspace.identities) {
173371
+ const memberMemories = memoriesByIdentity.get(identity) ?? [];
173372
+ if (memberMemories.length === 0)
171809
173373
  continue;
173374
+ const cachedEmbeddings = getProjectEmbeddings(args.db, identity);
173375
+ for (const memory of memberMemories) {
173376
+ const memoryEmbedding = cachedEmbeddings.get(memory.id);
173377
+ if (!memoryEmbedding || memoryEmbedding.modelId !== args.queryModelId)
173378
+ continue;
173379
+ semanticScores.set(memory.id, normalizeCosineScore(cosineSimilarity(args.queryEmbedding, memoryEmbedding.embedding)));
171810
173380
  }
171811
- semanticScores.set(memory.id, normalizeCosineScore(cosineSimilarity(args.queryEmbedding, memoryEmbedding)));
171812
173381
  }
171813
173382
  return semanticScores;
171814
173383
  }
171815
173384
  function getFtsMatches(args) {
171816
173385
  try {
171817
- return searchMemoriesFTS(args.db, args.projectPath, args.query, args.limit);
173386
+ return args.workspace?.isWorkspaced ? searchMemoriesFTSUnion(args.db, args.workspace.expandedIdentities, args.query, args.limit, args.workspace.ownIdentities, args.workspace.shareCategories) : searchMemoriesFTS(args.db, args.projectPath, args.query, args.limit);
171818
173387
  } catch (error51) {
171819
173388
  log(`[search] FTS query failed for "${args.query}": ${error51 instanceof Error ? error51.message : String(error51)}`);
171820
173389
  return [];
@@ -171828,8 +173397,11 @@ function selectSemanticCandidates(args) {
171828
173397
  return args.memories;
171829
173398
  }
171830
173399
  const candidateIds = new Set(args.ftsMatches.map((memory) => memory.id));
171831
- const cachedEmbeddings = peekProjectEmbeddings(args.projectPath);
171832
- if (cachedEmbeddings) {
173400
+ const embeddingProjects = args.workspace?.isWorkspaced ? args.workspace.identities : [args.projectPath];
173401
+ for (const projectPath of embeddingProjects) {
173402
+ const cachedEmbeddings = peekProjectEmbeddings(projectPath);
173403
+ if (!cachedEmbeddings)
173404
+ continue;
171833
173405
  for (const memoryId of cachedEmbeddings.keys()) {
171834
173406
  candidateIds.add(memoryId);
171835
173407
  }
@@ -171871,7 +173443,8 @@ function mergeMemoryResults(args) {
171871
173443
  score,
171872
173444
  memoryId: memory.id,
171873
173445
  category: memory.category,
171874
- matchType
173446
+ matchType,
173447
+ sourceName: args.sourceNameByMemoryId?.get(memory.id)
171875
173448
  });
171876
173449
  }
171877
173450
  return results.sort((left, right) => {
@@ -171885,7 +173458,7 @@ async function searchMemories(args) {
171885
173458
  if (!args.memoryEnabled) {
171886
173459
  return [];
171887
173460
  }
171888
- const memories = getMemoriesByProject(args.db, args.projectPath);
173461
+ const memories = args.workspace?.isWorkspaced ? getMemoriesByProjects(args.db, args.workspace.expandedIdentities, ["active", "permanent"], Date.now(), args.workspace.ownIdentities, args.workspace.shareCategories) : getMemoriesByProject(args.db, args.projectPath);
171889
173462
  if (memories.length === 0) {
171890
173463
  return [];
171891
173464
  }
@@ -171893,26 +173466,43 @@ async function searchMemories(args) {
171893
173466
  db: args.db,
171894
173467
  projectPath: args.projectPath,
171895
173468
  query: args.query,
171896
- limit: FTS_SEMANTIC_CANDIDATE_LIMIT
173469
+ limit: FTS_SEMANTIC_CANDIDATE_LIMIT,
173470
+ workspace: args.workspace
171897
173471
  });
171898
173472
  const ftsScores = getFtsScores(ftsMatches);
171899
173473
  const semanticCandidates = selectSemanticCandidates({
171900
173474
  memories,
171901
173475
  projectPath: args.projectPath,
171902
- ftsMatches
173476
+ ftsMatches,
173477
+ workspace: args.workspace
171903
173478
  });
171904
173479
  const semanticScores = await getSemanticScores({
171905
173480
  db: args.db,
171906
173481
  projectPath: args.projectPath,
171907
173482
  memories: semanticCandidates,
171908
- queryEmbedding: args.queryEmbedding
173483
+ queryEmbedding: args.queryEmbedding,
173484
+ queryModelId: args.queryModelId,
173485
+ workspace: args.workspace
171909
173486
  });
171910
173487
  return mergeMemoryResults({
171911
173488
  memories,
171912
173489
  semanticScores,
171913
173490
  ftsScores,
171914
173491
  limit: args.limit,
171915
- visibleMemoryIds: args.visibleMemoryIds
173492
+ visibleMemoryIds: args.visibleMemoryIds,
173493
+ sourceNameByMemoryId: sourceNamesForSearchMemories({
173494
+ memories,
173495
+ projectPath: args.projectPath,
173496
+ workspace: args.workspace ?? {
173497
+ identities: [args.projectPath],
173498
+ expandedIdentities: [args.projectPath],
173499
+ namesByIdentity: new Map,
173500
+ canonicalIdentityByStoredPath: new Map([[args.projectPath, args.projectPath]]),
173501
+ ownIdentities: [args.projectPath],
173502
+ shareCategories: null,
173503
+ isWorkspaced: false
173504
+ }
173505
+ })
171916
173506
  });
171917
173507
  }
171918
173508
  function linearDecayScore(rank, total) {
@@ -171943,7 +173533,13 @@ function runMessageFtsQuery(db, sessionId, ftsQuery, fetchLimit, cutoff) {
171943
173533
  return result;
171944
173534
  }
171945
173535
  var RRF_K = 60;
171946
- var VERBATIM_PROBE_BONUS = 0.5;
173536
+ var VERBATIM_RANK_BONUS = 1 / RRF_K;
173537
+ var IDF_FALLOFF = 100;
173538
+ function probeDiscriminationWeight(df, corpusSize) {
173539
+ if (corpusSize <= 0 || df <= 0)
173540
+ return 1;
173541
+ return 1 / (1 + IDF_FALLOFF * df / corpusSize);
173542
+ }
171947
173543
  function searchMessages(args) {
171948
173544
  const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
171949
173545
  const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
@@ -171960,20 +173556,31 @@ function searchMessages(args) {
171960
173556
  role: row.role
171961
173557
  }));
171962
173558
  }
173559
+ const corpusSize = getSessionFtsRowCount(args.db, args.sessionId);
171963
173560
  const queryLists = [];
171964
173561
  if (baseQuery.length > 0) {
171965
- queryLists.push(runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff));
173562
+ queryLists.push({
173563
+ rows: runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff),
173564
+ weight: 1
173565
+ });
171966
173566
  }
173567
+ const probeWeights = new Map;
171967
173568
  for (const probe of probes) {
171968
173569
  const probeQuery = sanitizeFtsQuery(probe);
171969
173570
  if (probeQuery.length === 0)
171970
173571
  continue;
171971
- queryLists.push(runMessageFtsQuery(args.db, args.sessionId, probeQuery, fetchLimit, cutoff));
173572
+ const df = countSessionFtsMatches(args.db, args.sessionId, probeQuery);
173573
+ const weight = probeDiscriminationWeight(df, corpusSize);
173574
+ probeWeights.set(probe, weight);
173575
+ queryLists.push({
173576
+ rows: runMessageFtsQuery(args.db, args.sessionId, probeQuery, fetchLimit, cutoff),
173577
+ weight
173578
+ });
171972
173579
  }
171973
173580
  const fused = new Map;
171974
173581
  for (const list of queryLists) {
171975
- list.forEach((row, rank) => {
171976
- const rrf = 1 / (RRF_K + rank);
173582
+ list.rows.forEach((row, rank) => {
173583
+ const rrf = list.weight / (RRF_K + rank);
171977
173584
  const existing = fused.get(row.messageId);
171978
173585
  if (existing) {
171979
173586
  existing.score += rrf;
@@ -171983,26 +173590,107 @@ function searchMessages(args) {
171983
173590
  });
171984
173591
  }
171985
173592
  for (const entry of fused.values()) {
171986
- if (containsProbeVerbatim(entry.row.content, probes)) {
171987
- entry.score += VERBATIM_PROBE_BONUS;
173593
+ let best = 0;
173594
+ for (const probe of probes) {
173595
+ const weight = probeWeights.get(probe) ?? 0;
173596
+ if (weight > best && containsProbeVerbatim(entry.row.content, [probe])) {
173597
+ best = weight;
173598
+ }
173599
+ }
173600
+ if (best > 0) {
173601
+ entry.score += best * VERBATIM_RANK_BONUS;
171988
173602
  }
171989
173603
  }
171990
173604
  const ranked = [...fused.values()].sort((a, b) => b.score !== a.score ? b.score - a.score : a.row.messageOrdinal - b.row.messageOrdinal).slice(0, args.limit);
171991
- const maxScore = ranked.length > 0 ? ranked[0].score : 1;
171992
- return ranked.map((entry) => ({
173605
+ return ranked.map((entry, rank) => ({
171993
173606
  source: "message",
171994
173607
  content: previewText(entry.row.content),
171995
- score: maxScore > 0 ? entry.score / maxScore : 0,
173608
+ score: linearDecayScore(rank, ranked.length),
171996
173609
  messageOrdinal: entry.row.messageOrdinal,
171997
173610
  messageId: entry.row.messageId,
171998
173611
  role: entry.row.role
171999
173612
  }));
172000
173613
  }
173614
+ function searchCompartmentChunks(args) {
173615
+ if (!args.queryEmbedding || args.limit <= 0)
173616
+ return [];
173617
+ const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
173618
+ const rows = loadCompartmentChunkEmbeddingsForSearch(args.db, args.sessionId, args.projectPath, args.modelId);
173619
+ if (rows.length === 0)
173620
+ return [];
173621
+ const byCompartment = new Map;
173622
+ for (const row of rows) {
173623
+ if (cutoff !== null && row.endOrdinal > cutoff) {
173624
+ continue;
173625
+ }
173626
+ const score = normalizeCosineScore(cosineSimilarity(args.queryEmbedding, row.vector));
173627
+ if (score <= 0)
173628
+ continue;
173629
+ const existing = byCompartment.get(row.compartmentId);
173630
+ if (!existing || score > existing.score) {
173631
+ byCompartment.set(row.compartmentId, { row, score });
173632
+ }
173633
+ }
173634
+ return [...byCompartment.values()].sort((left, right) => right.score !== left.score ? right.score - left.score : left.row.startOrdinal - right.row.startOrdinal).slice(0, args.limit).map(({ row, score }) => ({
173635
+ source: "compartment",
173636
+ content: previewText(row.title),
173637
+ score: score * SINGLE_SOURCE_PENALTY,
173638
+ compartmentId: row.compartmentId,
173639
+ sessionId: row.sessionId,
173640
+ title: row.title,
173641
+ startOrdinal: row.startOrdinal,
173642
+ endOrdinal: row.endOrdinal,
173643
+ matchType: "semantic"
173644
+ }));
173645
+ }
173646
+ function mergeMessageAndCompartmentResults(args) {
173647
+ if (args.compartments.length === 0)
173648
+ return args.messages;
173649
+ if (args.messages.length === 0)
173650
+ return args.compartments;
173651
+ const fused = new Map;
173652
+ const add = (key, result, score, tieOrdinal) => {
173653
+ const existing = fused.get(key);
173654
+ if (existing) {
173655
+ existing.score += score;
173656
+ return existing;
173657
+ }
173658
+ const entry = { result, score, tieOrdinal, snippetScore: -1 };
173659
+ fused.set(key, entry);
173660
+ return entry;
173661
+ };
173662
+ args.compartments.forEach((compartment, rank) => {
173663
+ add(`compartment:${compartment.compartmentId}`, compartment, 1 / (RRF_K + rank), compartment.startOrdinal);
173664
+ });
173665
+ for (const [rank, message] of args.messages.entries()) {
173666
+ const containing = args.compartments.find((compartment) => message.messageOrdinal >= compartment.startOrdinal && message.messageOrdinal <= compartment.endOrdinal);
173667
+ const contribution = 1 / (RRF_K + rank);
173668
+ if (!containing) {
173669
+ add(`message:${message.messageId}`, message, contribution, message.messageOrdinal);
173670
+ continue;
173671
+ }
173672
+ const entry = add(`compartment:${containing.compartmentId}`, containing, contribution, containing.startOrdinal);
173673
+ if (message.score > entry.snippetScore && entry.result.source === "compartment") {
173674
+ entry.snippetScore = message.score;
173675
+ entry.result = {
173676
+ ...entry.result,
173677
+ matchType: "hybrid",
173678
+ snippet: message.content
173679
+ };
173680
+ }
173681
+ }
173682
+ const ranked = [...fused.values()].sort((left, right) => right.score !== left.score ? right.score - left.score : left.tieOrdinal - right.tieOrdinal).slice(0, args.limit);
173683
+ return ranked.map((entry, rank) => ({
173684
+ ...entry.result,
173685
+ score: linearDecayScore(rank, ranked.length)
173686
+ }));
173687
+ }
172001
173688
  function getSourceBoost(result) {
172002
173689
  switch (result.source) {
172003
173690
  case "memory":
172004
173691
  return MEMORY_SOURCE_BOOST;
172005
173692
  case "message":
173693
+ case "compartment":
172006
173694
  return MESSAGE_SOURCE_BOOST;
172007
173695
  case "git_commit":
172008
173696
  return GIT_COMMIT_SOURCE_BOOST;
@@ -172020,6 +173708,9 @@ function compareUnifiedResults(left, right) {
172020
173708
  if (left.source === "message" && right.source === "message") {
172021
173709
  return left.messageOrdinal - right.messageOrdinal;
172022
173710
  }
173711
+ if (left.source === "compartment" && right.source === "compartment") {
173712
+ return left.startOrdinal - right.startOrdinal;
173713
+ }
172023
173714
  if (left.source === "git_commit" && right.source === "git_commit") {
172024
173715
  return right.committedAtMs - left.committedAtMs;
172025
173716
  }
@@ -172070,10 +173761,12 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
172070
173761
  const isEmbeddingRuntimeEnabled = options.isEmbeddingRuntimeEnabled ?? isEmbeddingEnabled;
172071
173762
  const gitCommitsEnabled = options.gitCommitsEnabled ?? false;
172072
173763
  const activeSources = resolveSources(options.sources);
172073
- const runMemory = activeSources.has("memory") && (options.memoryEnabled ?? true);
173764
+ const memoryFeatureEnabled = options.memoryEnabled ?? true;
173765
+ const runMemory = activeSources.has("memory") && memoryFeatureEnabled;
172074
173766
  const runMessages = activeSources.has("message");
172075
173767
  const runGitCommits = activeSources.has("git_commit") && gitCommitsEnabled;
172076
- const needsEmbedding = (runMemory || runGitCommits) && embeddingEnabled && isEmbeddingRuntimeEnabled();
173768
+ const runCompartmentChunks = runMessages && memoryFeatureEnabled && embeddingEnabled;
173769
+ const needsEmbedding = (runMemory || runGitCommits || runCompartmentChunks) && embeddingEnabled && isEmbeddingRuntimeEnabled();
172077
173770
  const queryEmbeddingPromise = needsEmbedding ? embedQuery(trimmedQuery, options.signal).catch((error51) => {
172078
173771
  log(`[search] query embedding failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
172079
173772
  return null;
@@ -172089,6 +173782,24 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
172089
173782
  probes: messageProbes
172090
173783
  }) : [];
172091
173784
  const queryEmbedding = await queryEmbeddingPromise;
173785
+ const workspace = resolveSearchWorkspaceContext(db, projectPath);
173786
+ const embeddingSnapshot = getProjectEmbeddingSnapshot(projectPath);
173787
+ const embeddingModelId = embeddingSnapshot?.modelId;
173788
+ const chunkModelId = embeddingSnapshot?.chunkModelId;
173789
+ const compartmentResults = runCompartmentChunks ? searchCompartmentChunks({
173790
+ db,
173791
+ sessionId,
173792
+ projectPath,
173793
+ queryEmbedding,
173794
+ limit: tierLimit,
173795
+ maxOrdinal: options.maxMessageOrdinal,
173796
+ modelId: chunkModelId && chunkModelId !== "off" ? chunkModelId : null
173797
+ }) : [];
173798
+ const messageLikeResults = mergeMessageAndCompartmentResults({
173799
+ messages: messageResults,
173800
+ compartments: compartmentResults,
173801
+ limit: tierLimit
173802
+ });
172092
173803
  const [memoryResults, gitCommitResults] = await Promise.all([
172093
173804
  runMemory ? searchMemories({
172094
173805
  db,
@@ -172097,6 +173808,8 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
172097
173808
  limit: tierLimit,
172098
173809
  memoryEnabled: true,
172099
173810
  queryEmbedding,
173811
+ queryModelId: embeddingModelId && embeddingModelId !== "off" ? embeddingModelId : null,
173812
+ workspace,
172100
173813
  visibleMemoryIds: options.visibleMemoryIds
172101
173814
  }) : Promise.resolve([]),
172102
173815
  runGitCommits ? Promise.resolve(searchGitCommits({
@@ -172107,7 +173820,7 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
172107
173820
  queryEmbedding
172108
173821
  })) : Promise.resolve([])
172109
173822
  ]);
172110
- const results = [...memoryResults, ...messageResults, ...gitCommitResults].sort(compareUnifiedResults).slice(0, limit);
173823
+ const results = [...memoryResults, ...messageLikeResults, ...gitCommitResults].sort(compareUnifiedResults).slice(0, limit);
172111
173824
  const countRetrievals = options.countRetrievals ?? true;
172112
173825
  if (countRetrievals) {
172113
173826
  const memoryIds = results.filter((result) => result.source === "memory").map((result) => result.memoryId);
@@ -172167,6 +173880,11 @@ function renderFragment(result, charCap) {
172167
173880
  const compressed = cavemanCompress(result.content, "ultra");
172168
173881
  return truncate(compressed, charCap);
172169
173882
  }
173883
+ case "compartment": {
173884
+ const source = result.snippet ?? result.title;
173885
+ const compressed = cavemanCompress(source, "ultra");
173886
+ return truncate(compressed, charCap);
173887
+ }
172170
173888
  }
172171
173889
  }
172172
173890
  function buildAutoSearchHint(results, options = {}) {
@@ -173590,9 +175308,51 @@ function safeGetActiveUserMemoriesPi(db) {
173590
175308
  function memoryProjectPath(state) {
173591
175309
  return state.memoryEnabled === false ? undefined : state.projectIdentity;
173592
175310
  }
175311
+ function resolveWorkspaceRenderContextPi(state, db) {
175312
+ const memPath = memoryProjectPath(state);
175313
+ if (!memPath) {
175314
+ return {
175315
+ identities: [],
175316
+ expandedIdentities: [],
175317
+ ownIdentities: [],
175318
+ shareCategories: null,
175319
+ namesByIdentity: new Map,
175320
+ canonicalIdentityByStoredPath: new Map,
175321
+ isWorkspaced: false
175322
+ };
175323
+ }
175324
+ const identitySet = resolveWorkspaceIdentitySet(db, memPath);
175325
+ const isWorkspaced = identitySet.identities.length > 1;
175326
+ const expanded = expandWorkspaceIdentitySetWithAliases(db, identitySet.identities);
175327
+ const expandedIdentities = isWorkspaced ? expanded.expandedIdentities : identitySet.identities;
175328
+ const canonicalIdentityByStoredPath = isWorkspaced ? expanded.canonicalIdentityByStoredPath : new Map(identitySet.identities.map((identity) => [identity, identity]));
175329
+ let ownIdentities = expandedIdentities.filter((identity) => canonicalIdentityByStoredPath.get(identity) === memPath);
175330
+ if (ownIdentities.length === 0 && expandedIdentities.includes(memPath)) {
175331
+ ownIdentities = [memPath];
175332
+ }
175333
+ return {
175334
+ identities: identitySet.identities,
175335
+ expandedIdentities,
175336
+ ownIdentities,
175337
+ shareCategories: isWorkspaced ? resolveWorkspaceShareCategories(db, memPath) : null,
175338
+ namesByIdentity: identitySet.namesByIdentity,
175339
+ canonicalIdentityByStoredPath,
175340
+ isWorkspaced
175341
+ };
175342
+ }
175343
+ function sourceNamesForPiMemories(args) {
175344
+ if (!args.projectPath || !args.workspace.isWorkspaced)
175345
+ return;
175346
+ const names = new Map;
175347
+ for (const memory of args.memories) {
175348
+ const source = sourceNameForMemory(memory.projectPath, args.projectPath, args.workspace.identities, args.workspace.namesByIdentity, args.workspace.canonicalIdentityByStoredPath);
175349
+ if (source)
175350
+ names.set(memory.id, source);
175351
+ }
175352
+ return names.size > 0 ? names : undefined;
175353
+ }
173593
175354
  var EMPTY_PI_HARD_SIGNALS = {
173594
175355
  systemHash: "",
173595
- toolSetHash: "",
173596
175356
  modelKey: "",
173597
175357
  cacheExpired: false,
173598
175358
  lastResponseTime: 0
@@ -173638,6 +175398,7 @@ function getCachedMarkers(db, state, compartmentsForNormalization) {
173638
175398
  maxMutationId: meta3.cachedM0MaxMutationId,
173639
175399
  maxMemoryMutationId: meta3.cachedM0MaxMemoryMutationId,
173640
175400
  projectMemoryEpoch: meta3.cachedM0ProjectMemoryEpoch,
175401
+ workspaceFingerprint: meta3.cachedM0WorkspaceFingerprint,
173641
175402
  projectUserProfileVersion: meta3.cachedM0ProjectUserProfileVersion,
173642
175403
  projectDocsHash: meta3.cachedM0ProjectDocsHash,
173643
175404
  sessionFactsVersion: meta3.cachedM0SessionFactsVersion,
@@ -173645,7 +175406,6 @@ function getCachedMarkers(db, state, compartmentsForNormalization) {
173645
175406
  upgradeState: meta3.cachedM0UpgradeState,
173646
175407
  lastBaselineEndMessageId: cachedBoundary,
173647
175408
  systemHash: meta3.cachedM0SystemHash ?? "",
173648
- toolSetHash: meta3.cachedM0ToolSetHash ?? "",
173649
175409
  modelKey: meta3.cachedM0ModelKey ?? ""
173650
175410
  };
173651
175411
  }
@@ -173658,15 +175418,17 @@ function readCurrentMarkers(db, state, projectDocsHash) {
173658
175418
  }
173659
175419
  function readCurrentMarkersFromCompartments(db, state, compartments, projectDocsHash) {
173660
175420
  const memPath = memoryProjectPath(state);
173661
- const memories = memPath ? getMemoriesByProject(db, memPath, ["active", "permanent"]) : [];
175421
+ const workspace = resolveWorkspaceRenderContextPi(state, db);
175422
+ const maxMemoryId = memPath ? workspace.isWorkspaced ? getMaxMemoryIdForProjects(db, workspace.expandedIdentities, workspace.ownIdentities, workspace.shareCategories) : getMaxMemoryIdForProjects(db, [memPath]) : 0;
173662
175423
  const projectState = memPath ? getProjectState(db, memPath) : undefined;
173663
175424
  const globalState = getProjectState(db, GLOBAL_USER_PROFILE_PROJECT_PATH);
173664
175425
  return {
173665
175426
  maxCompartmentSeq: compartments.length > 0 ? compartments.reduce((max, compartment) => compartment.sequence > max ? compartment.sequence : max, EMPTY_MAX_COMPARTMENT_SEQ) : EMPTY_MAX_COMPARTMENT_SEQ,
173666
- maxMemoryId: memories.length > 0 ? memories.reduce((max, memory) => memory.id > max ? memory.id : max, 0) : 0,
175427
+ maxMemoryId,
173667
175428
  maxMutationId: getMaxM0MutationId(db, state.sessionId) ?? 0,
173668
- maxMemoryMutationId: memPath ? getMaxMemoryMutationId(db, memPath) ?? 0 : 0,
175429
+ maxMemoryMutationId: memPath ? workspace.isWorkspaced ? getMaxMemoryMutationIdForProjects(db, workspace.expandedIdentities) ?? 0 : getMaxMemoryMutationId(db, memPath) ?? 0 : 0,
173669
175430
  projectMemoryEpoch: projectState?.projectMemoryEpoch ?? 0,
175431
+ workspaceFingerprint: workspace.isWorkspaced ? computeWorkspaceEpochFingerprint(db, workspace.identities) : null,
173670
175432
  projectUserProfileVersion: globalState?.projectUserProfileVersion ?? 0,
173671
175433
  projectDocsHash: projectDocsHash ?? readProjectDocsCanonical(state.projectDirectory).canonicalHash,
173672
175434
  sessionFactsVersion: getSessionFactsVersion(db, state.sessionId),
@@ -173674,7 +175436,6 @@ function readCurrentMarkersFromCompartments(db, state, compartments, projectDocs
173674
175436
  upgradeState: `${PI_M0_UPGRADE_STATE}:${compartments.some((c) => c.legacy === 1) ? "legacy" : "ready"}`,
173675
175437
  lastBaselineEndMessageId: lastBaselineEndMessageId(compartments),
173676
175438
  systemHash: (state.hardSignals ?? EMPTY_PI_HARD_SIGNALS).systemHash,
173677
- toolSetHash: (state.hardSignals ?? EMPTY_PI_HARD_SIGNALS).toolSetHash,
173678
175439
  modelKey: (state.hardSignals ?? EMPTY_PI_HARD_SIGNALS).modelKey
173679
175440
  };
173680
175441
  }
@@ -173699,19 +175460,17 @@ function mustMaterializePi(state, db, currentCompartmentsOverride) {
173699
175460
  if (hard.systemHash !== "" && hard.systemHash !== (meta3.cachedM0SystemHash ?? "")) {
173700
175461
  return { value: true, reason: "system_hash" };
173701
175462
  }
173702
- if (hard.toolSetHash !== "" && hard.toolSetHash !== (meta3.cachedM0ToolSetHash ?? "")) {
173703
- return { value: true, reason: "tool_set_hash" };
173704
- }
173705
175463
  if (hard.cacheExpired && hard.lastResponseTime > 0 && hard.lastResponseTime > (meta3.cachedM0MaterializedAt ?? 0)) {
173706
175464
  return { value: true, reason: "ttl_idle" };
173707
175465
  }
173708
175466
  if (meta3.cachedM0UpgradeState !== current.upgradeState) {
173709
175467
  return { value: true, reason: "renderer_upgrade" };
173710
175468
  }
173711
- if (current.projectDocsHash !== (meta3.cachedM0ProjectDocsHash ?? "")) {
173712
- return { value: true, reason: "project_docs_change" };
173713
- }
173714
- if (current.projectMemoryEpoch !== (meta3.cachedM0ProjectMemoryEpoch ?? 0)) {
175469
+ if (current.workspaceFingerprint !== null || (meta3.cachedM0WorkspaceFingerprint ?? null) !== null) {
175470
+ if (current.workspaceFingerprint !== (meta3.cachedM0WorkspaceFingerprint ?? null)) {
175471
+ return { value: true, reason: "project_memory_change" };
175472
+ }
175473
+ } else if (current.projectMemoryEpoch !== (meta3.cachedM0ProjectMemoryEpoch ?? 0)) {
173715
175474
  return { value: true, reason: "project_memory_change" };
173716
175475
  }
173717
175476
  if (current.maxMutationId !== (meta3.cachedM0MaxMutationId ?? 0)) {
@@ -173728,11 +175487,19 @@ ${memories.map((memory) => `- ${escapeXmlContent(memory.content)}`).join(`
173728
175487
  `)}
173729
175488
  </${wrapper}>`;
173730
175489
  }
173731
- function renderM0Pi(state, db, projectDocs = readProjectDocsCanonical(state.projectDirectory).renderedBlock, decayPressureMultiplier = 1, memoriesOverride, compartmentsOverride, userProfileOverride) {
175490
+ function renderM0Pi(state, db, projectDocs = readProjectDocsCanonical(state.projectDirectory).renderedBlock, decayPressureMultiplier = 1, memoriesOverride, compartmentsOverride, userProfileOverride, workspaceOverride) {
173732
175491
  const memPath = memoryProjectPath(state);
173733
- const allMemories = memoriesOverride ?? (memPath ? getMemoriesByProject(db, memPath, ["active", "permanent"]) : []);
173734
- const memories = allMemories.length > 0 ? trimMemoriesToBudgetV2(state.sessionId, allMemories, state.injectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS).renderOrder : allMemories;
173735
- const memoryBlock = memories.length > 0 ? renderMemoryBlockV2(memories) : undefined;
175492
+ const workspace = workspaceOverride ?? resolveWorkspaceRenderContextPi(state, db);
175493
+ const allMemories = memoriesOverride ?? (memPath ? workspace.isWorkspaced ? getMemoriesByProjects(db, workspace.expandedIdentities, ["active", "permanent"], Date.now(), workspace.ownIdentities, workspace.shareCategories) : getMemoriesByProject(db, memPath, ["active", "permanent"]) : []);
175494
+ const memoryRenderOptions = {
175495
+ sourceNameByMemoryId: sourceNamesForPiMemories({
175496
+ memories: allMemories,
175497
+ projectPath: memPath,
175498
+ workspace
175499
+ })
175500
+ };
175501
+ const memories = allMemories.length > 0 ? workspace.isWorkspaced ? trimWorkspaceMemoriesToBudgetV2(state.sessionId, allMemories, state.injectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS, workspace, memoryRenderOptions).renderOrder : trimMemoriesToBudgetV2(state.sessionId, allMemories, state.injectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS).renderOrder : allMemories;
175502
+ const memoryBlock = memories.length > 0 ? renderMemoryBlockV2(memories, "project-memory", memoryRenderOptions) : undefined;
173736
175503
  const baseHistoryBudget = state.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS;
173737
175504
  const decayed = renderDecayedCompartments({
173738
175505
  compartments: compartmentsOverride ?? getCompartments(db, state.sessionId),
@@ -173754,10 +175521,19 @@ ${decayed}
173754
175521
 
173755
175522
  `).trim();
173756
175523
  }
173757
- function renderedMemoryIdsForPi(state, memories) {
175524
+ function renderedMemoryIdsForPi(state, memories, workspace, db) {
173758
175525
  if (memories.length === 0)
173759
175526
  return [];
173760
- return trimMemoriesToBudgetV2(state.sessionId, [...memories], state.injectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS).renderOrder.map((memory) => memory.id);
175527
+ const resolvedWorkspace = workspace ?? (db ? resolveWorkspaceRenderContextPi(state, db) : undefined);
175528
+ const renderOptions = resolvedWorkspace ? {
175529
+ sourceNameByMemoryId: sourceNamesForPiMemories({
175530
+ memories,
175531
+ projectPath: memoryProjectPath(state),
175532
+ workspace: resolvedWorkspace
175533
+ })
175534
+ } : {};
175535
+ const trimmed = resolvedWorkspace?.isWorkspaced ? trimWorkspaceMemoriesToBudgetV2(state.sessionId, [...memories], state.injectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS, resolvedWorkspace, renderOptions) : trimMemoriesToBudgetV2(state.sessionId, [...memories], state.injectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS);
175536
+ return trimmed.renderOrder.map((memory) => memory.id);
173761
175537
  }
173762
175538
  function isTransientSqliteLockError(error51) {
173763
175539
  if (!error51 || typeof error51 !== "object")
@@ -173782,17 +175558,19 @@ class PiMaterializeContentionError extends Error {
173782
175558
  function readFrozenM0InputsPi(state, db, docs = readProjectDocsCanonical(state.projectDirectory), memoryCutoff) {
173783
175559
  const memPath = memoryProjectPath(state);
173784
175560
  const read = db.transaction(() => {
175561
+ const workspace = resolveWorkspaceRenderContextPi(state, db);
173785
175562
  const compartments = getCompartments(db, state.sessionId);
173786
- const memories = memPath ? getMemoriesByProject(db, memPath, ["active", "permanent"], memoryCutoff) : [];
175563
+ const memories = memPath ? workspace.isWorkspaced ? getMemoriesByProjects(db, workspace.expandedIdentities, ["active", "permanent"], memoryCutoff, workspace.ownIdentities, workspace.shareCategories) : getMemoriesByProject(db, memPath, ["active", "permanent"], memoryCutoff) : [];
173787
175564
  const userProfile = safeGetActiveUserMemoriesPi(db);
173788
175565
  const projectState = memPath ? getProjectState(db, memPath) : undefined;
173789
175566
  const globalState = getProjectState(db, GLOBAL_USER_PROFILE_PROJECT_PATH);
173790
175567
  const markers = {
173791
175568
  maxCompartmentSeq: compartments.reduce((max, compartment) => compartment.sequence > max ? compartment.sequence : max, EMPTY_MAX_COMPARTMENT_SEQ),
173792
- maxMemoryId: memories.reduce((max, memory) => memory.id > max ? memory.id : max, 0),
175569
+ maxMemoryId: memPath ? workspace.isWorkspaced ? getMaxMemoryIdForProjects(db, workspace.expandedIdentities, workspace.ownIdentities, workspace.shareCategories) : getMaxMemoryIdForProjects(db, [memPath]) : 0,
173793
175570
  maxMutationId: getMaxM0MutationId(db, state.sessionId) ?? 0,
173794
- maxMemoryMutationId: memPath ? getMaxMemoryMutationId(db, memPath) ?? 0 : 0,
175571
+ maxMemoryMutationId: memPath ? workspace.isWorkspaced ? getMaxMemoryMutationIdForProjects(db, workspace.expandedIdentities) ?? 0 : getMaxMemoryMutationId(db, memPath) ?? 0 : 0,
173795
175572
  projectMemoryEpoch: projectState?.projectMemoryEpoch ?? 0,
175573
+ workspaceFingerprint: workspace.isWorkspaced ? computeWorkspaceEpochFingerprint(db, workspace.identities) : null,
173796
175574
  projectUserProfileVersion: globalState?.projectUserProfileVersion ?? 0,
173797
175575
  projectDocsHash: docs.canonicalHash,
173798
175576
  sessionFactsVersion: getSessionFactsVersion(db, state.sessionId),
@@ -173800,10 +175578,9 @@ function readFrozenM0InputsPi(state, db, docs = readProjectDocsCanonical(state.p
173800
175578
  upgradeState: `${PI_M0_UPGRADE_STATE}:${compartments.some((c) => c.legacy === 1) ? "legacy" : "ready"}`,
173801
175579
  lastBaselineEndMessageId: lastBaselineEndMessageId(compartments),
173802
175580
  systemHash: (state.hardSignals ?? EMPTY_PI_HARD_SIGNALS).systemHash,
173803
- toolSetHash: (state.hardSignals ?? EMPTY_PI_HARD_SIGNALS).toolSetHash,
173804
175581
  modelKey: (state.hardSignals ?? EMPTY_PI_HARD_SIGNALS).modelKey
173805
175582
  };
173806
- return { docs, markers, compartments, memories, userProfile };
175583
+ return { docs, markers, compartments, memories, userProfile, workspace };
173807
175584
  });
173808
175585
  return read();
173809
175586
  }
@@ -173814,17 +175591,17 @@ function renderFreshM0PiNonPersisted(state, db) {
173814
175591
  frozen.markers.materializedAt = cachedMaterializedAt;
173815
175592
  const historyBudget = state.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS;
173816
175593
  let dpm = 1;
173817
- let m0 = renderM0Pi(state, db, docs.renderedBlock, dpm, frozen.memories, frozen.compartments, frozen.userProfile);
175594
+ let m0 = renderM0Pi(state, db, docs.renderedBlock, dpm, frozen.memories, frozen.compartments, frozen.userProfile, frozen.workspace);
173818
175595
  let attempts = 0;
173819
175596
  while (historyBudget > 0 && historySliceTokensPi(m0) > historyBudget * 1.05 && attempts < 3) {
173820
175597
  dpm *= 1.15;
173821
- m0 = renderM0Pi(state, db, docs.renderedBlock, dpm, frozen.memories, frozen.compartments, frozen.userProfile);
175598
+ m0 = renderM0Pi(state, db, docs.renderedBlock, dpm, frozen.memories, frozen.compartments, frozen.userProfile, frozen.workspace);
173822
175599
  attempts += 1;
173823
175600
  }
173824
175601
  return {
173825
175602
  m0,
173826
175603
  snapshotMarkers: frozen.markers,
173827
- renderedMemoryIds: renderedMemoryIdsForPi(state, frozen.memories)
175604
+ renderedMemoryIds: renderedMemoryIdsForPi(state, frozen.memories, frozen.workspace, db)
173828
175605
  };
173829
175606
  }
173830
175607
  function materializeM0Pi(state, db) {
@@ -173834,14 +175611,14 @@ function materializeM0Pi(state, db) {
173834
175611
  const snapshotMemories = frozen.memories;
173835
175612
  const snapshotCompartments = frozen.compartments;
173836
175613
  const snapshotUserProfile = frozen.userProfile;
173837
- const renderedMemoryIds = renderedMemoryIdsForPi(state, snapshotMemories);
175614
+ const renderedMemoryIds = renderedMemoryIdsForPi(state, snapshotMemories, frozen.workspace, db);
173838
175615
  let decayPressureMultiplier = 1;
173839
- let m0 = renderM0Pi(state, db, docs.renderedBlock, decayPressureMultiplier, snapshotMemories, snapshotCompartments, snapshotUserProfile);
175616
+ let m0 = renderM0Pi(state, db, docs.renderedBlock, decayPressureMultiplier, snapshotMemories, snapshotCompartments, snapshotUserProfile, frozen.workspace);
173840
175617
  const historyBudget = state.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS;
173841
175618
  let attempts = 0;
173842
175619
  while (historyBudget > 0 && historySliceTokensPi(m0) > historyBudget * 1.05 && attempts < 3) {
173843
175620
  decayPressureMultiplier *= 1.15;
173844
- m0 = renderM0Pi(state, db, docs.renderedBlock, decayPressureMultiplier, snapshotMemories, snapshotCompartments, snapshotUserProfile);
175621
+ m0 = renderM0Pi(state, db, docs.renderedBlock, decayPressureMultiplier, snapshotMemories, snapshotCompartments, snapshotUserProfile, frozen.workspace);
173845
175622
  attempts += 1;
173846
175623
  }
173847
175624
  const m0Bytes = Buffer.from(m0, "utf8");
@@ -173857,7 +175634,8 @@ function materializeM0Pi(state, db) {
173857
175634
  }
173858
175635
  try {
173859
175636
  const current = readCurrentMarkers(db, state, phase3ProjectDocsHash);
173860
- const stale = current.projectMemoryEpoch !== snapshotMarkers.projectMemoryEpoch || current.projectUserProfileVersion !== snapshotMarkers.projectUserProfileVersion || current.maxCompartmentSeq !== snapshotMarkers.maxCompartmentSeq || current.maxMutationId !== snapshotMarkers.maxMutationId || current.maxMemoryMutationId !== snapshotMarkers.maxMemoryMutationId || current.projectDocsHash !== snapshotMarkers.projectDocsHash || current.sessionFactsVersion !== snapshotMarkers.sessionFactsVersion || current.upgradeState !== snapshotMarkers.upgradeState;
175637
+ const memoryEpochStale = current.workspaceFingerprint !== null || snapshotMarkers.workspaceFingerprint !== null ? current.workspaceFingerprint !== snapshotMarkers.workspaceFingerprint : current.projectMemoryEpoch !== snapshotMarkers.projectMemoryEpoch;
175638
+ const stale = memoryEpochStale || current.projectUserProfileVersion !== snapshotMarkers.projectUserProfileVersion || current.maxCompartmentSeq !== snapshotMarkers.maxCompartmentSeq || current.maxMutationId !== snapshotMarkers.maxMutationId || current.maxMemoryMutationId !== snapshotMarkers.maxMemoryMutationId || current.sessionFactsVersion !== snapshotMarkers.sessionFactsVersion || current.upgradeState !== snapshotMarkers.upgradeState;
173861
175639
  if (stale) {
173862
175640
  db.exec("ROLLBACK");
173863
175641
  throw new PiMaterializeContentionError("snapshot changed before persist");
@@ -173868,6 +175646,7 @@ function materializeM0Pi(state, db) {
173868
175646
  persistCachedM0(db, state.sessionId, {
173869
175647
  m0Bytes,
173870
175648
  projectMemoryEpoch: snapshotMarkers.projectMemoryEpoch,
175649
+ workspaceFingerprint: snapshotMarkers.workspaceFingerprint,
173871
175650
  projectUserProfileVersion: snapshotMarkers.projectUserProfileVersion,
173872
175651
  maxCompartmentSeq: snapshotMarkers.maxCompartmentSeq,
173873
175652
  maxMemoryId: snapshotMarkers.maxMemoryId,
@@ -173879,7 +175658,6 @@ function materializeM0Pi(state, db) {
173879
175658
  sessionFactsVersion: snapshotMarkers.sessionFactsVersion,
173880
175659
  upgradeState: snapshotMarkers.upgradeState,
173881
175660
  systemHash: snapshotMarkers.systemHash,
173882
- toolSetHash: snapshotMarkers.toolSetHash,
173883
175661
  modelKey: snapshotMarkers.modelKey
173884
175662
  });
173885
175663
  db.prepare("UPDATE session_meta SET memory_block_count = ?, memory_block_ids = ? WHERE session_id = ?").run(renderedMemoryIds.length, JSON.stringify(renderedMemoryIds), state.sessionId);
@@ -173933,7 +175711,7 @@ function renderMemoryUpdatesBlockPi(args) {
173933
175711
  if (args.renderedMemoryIds.length === 0)
173934
175712
  return { block: "", count: 0 };
173935
175713
  const renderedIds = new Set(args.renderedMemoryIds);
173936
- const mutations = getMemoryMutationsForRender(args.db, args.projectPath, args.afterId, args.renderedMemoryIds);
175714
+ const mutations = args.workspace.isWorkspaced ? getMemoryMutationsForRenderByProjects(args.db, args.workspace.expandedIdentities, args.afterId, args.renderedMemoryIds) : getMemoryMutationsForRender(args.db, args.projectPath, args.afterId, args.renderedMemoryIds);
173937
175715
  if (mutations.length === 0)
173938
175716
  return { block: "", count: 0 };
173939
175717
  const lines = [
@@ -173964,6 +175742,7 @@ ${lines.join(`
173964
175742
  }
173965
175743
  function renderM1PiWithMetadata(state, db, markers, renderedMemoryIds, preRenderedKeyFilesBlock, compartmentsOverride) {
173966
175744
  const sections = [];
175745
+ const workspace = resolveWorkspaceRenderContextPi(state, db);
173967
175746
  const keyFiles = renderedKeyFilesBlockPi(state, db, preRenderedKeyFilesBlock);
173968
175747
  if (keyFiles)
173969
175748
  sections.push(keyFiles);
@@ -173971,6 +175750,7 @@ function renderM1PiWithMetadata(state, db, markers, renderedMemoryIds, preRender
173971
175750
  const memoryUpdates = memPath ? renderMemoryUpdatesBlockPi({
173972
175751
  db,
173973
175752
  projectPath: memPath,
175753
+ workspace,
173974
175754
  afterId: markers.maxMemoryMutationId,
173975
175755
  renderedMemoryIds
173976
175756
  }) : { block: undefined, count: 0 };
@@ -173985,11 +175765,18 @@ function renderM1PiWithMetadata(state, db, markers, renderedMemoryIds, preRender
173985
175765
  ${body}
173986
175766
  </new-compartments>`);
173987
175767
  }
173988
- const newMemories = memPath ? getMemoriesByProject(db, memPath, ["active", "permanent"], markers.materializedAt).filter((memory) => memory.id > markers.maxMemoryId) : [];
175768
+ const newMemories = memPath ? workspace.isWorkspaced ? readNewMemoriesForM1Union(db, workspace.expandedIdentities, markers.maxMemoryId, markers.materializedAt, workspace.ownIdentities, workspace.shareCategories) : getMemoriesByProject(db, memPath, ["active", "permanent"], markers.materializedAt).filter((memory) => memory.id > markers.maxMemoryId) : [];
173989
175769
  if (newMemories.length > 0) {
173990
175770
  const memoryBudget = state.injectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS;
173991
- const trimmedNewMemories = trimMemoriesToBudgetV2(state.sessionId, newMemories, Math.max(1, Math.floor(memoryBudget * 0.25))).renderOrder;
173992
- const newMemoriesBlock = renderMemoryBlockV2(trimmedNewMemories, "new-memories");
175771
+ const memoryRenderOptions = {
175772
+ sourceNameByMemoryId: sourceNamesForPiMemories({
175773
+ memories: newMemories,
175774
+ projectPath: memPath,
175775
+ workspace
175776
+ })
175777
+ };
175778
+ const trimmedNewMemories = trimMemoriesToBudgetV2(state.sessionId, newMemories, Math.max(1, Math.floor(memoryBudget * 0.25)), memoryRenderOptions).renderOrder;
175779
+ const newMemoriesBlock = renderMemoryBlockV2(trimmedNewMemories, "new-memories", memoryRenderOptions);
173993
175780
  if (newMemoriesBlock)
173994
175781
  sections.push(newMemoriesBlock);
173995
175782
  }
@@ -174038,6 +175825,7 @@ function parseMemoryBlockIds(raw) {
174038
175825
  function readCachedPiM0M1Row(db, sessionId) {
174039
175826
  return db.prepare(`SELECT cached_m0_bytes, cached_m1_bytes,
174040
175827
  cached_m0_project_memory_epoch,
175828
+ cached_m0_workspace_fingerprint,
174041
175829
  cached_m0_project_user_profile_version,
174042
175830
  cached_m0_max_compartment_seq,
174043
175831
  cached_m0_max_memory_id,
@@ -174048,7 +175836,6 @@ function readCachedPiM0M1Row(db, sessionId) {
174048
175836
  cached_m0_session_facts_version,
174049
175837
  cached_m0_upgrade_state,
174050
175838
  cached_m0_system_hash,
174051
- cached_m0_tool_set_hash,
174052
175839
  cached_m0_model_key,
174053
175840
  cached_m0_last_baseline_end_message_id,
174054
175841
  memory_block_ids
@@ -174082,6 +175869,7 @@ function markersFromCachedPiRow(row, compartmentsForNormalization) {
174082
175869
  maxMutationId: row.cached_m0_max_mutation_id,
174083
175870
  maxMemoryMutationId: row.cached_m0_max_memory_mutation_id,
174084
175871
  projectMemoryEpoch: row.cached_m0_project_memory_epoch,
175872
+ workspaceFingerprint: row.cached_m0_workspace_fingerprint,
174085
175873
  projectUserProfileVersion: row.cached_m0_project_user_profile_version,
174086
175874
  projectDocsHash: row.cached_m0_project_docs_hash ?? "",
174087
175875
  materializedAt: row.cached_m0_materialized_at,
@@ -174089,7 +175877,6 @@ function markersFromCachedPiRow(row, compartmentsForNormalization) {
174089
175877
  upgradeState: row.cached_m0_upgrade_state,
174090
175878
  lastBaselineEndMessageId: typeof row.cached_m0_last_baseline_end_message_id === "string" && row.cached_m0_last_baseline_end_message_id.length > 0 ? row.cached_m0_last_baseline_end_message_id : null,
174091
175879
  systemHash: row.cached_m0_system_hash ?? "",
174092
- toolSetHash: row.cached_m0_tool_set_hash ?? "",
174093
175880
  modelKey: row.cached_m0_model_key ?? ""
174094
175881
  };
174095
175882
  }
@@ -174097,7 +175884,7 @@ function cachedPiRowMatchesSnapshot(args) {
174097
175884
  const rowMarkers = markersFromCachedPiRow(args.row, args.compartmentsForNormalization);
174098
175885
  if (!rowMarkers)
174099
175886
  return false;
174100
- return bufferEqualsNullable(args.row.cached_m0_bytes, args.m0Bytes) && rowMarkers.projectMemoryEpoch === args.markers.projectMemoryEpoch && rowMarkers.projectUserProfileVersion === args.markers.projectUserProfileVersion && rowMarkers.maxCompartmentSeq === args.markers.maxCompartmentSeq && rowMarkers.maxMemoryId === args.markers.maxMemoryId && rowMarkers.maxMutationId === args.markers.maxMutationId && rowMarkers.maxMemoryMutationId === args.markers.maxMemoryMutationId && (rowMarkers.projectDocsHash ?? "") === (args.markers.projectDocsHash ?? "") && rowMarkers.materializedAt === args.markers.materializedAt && rowMarkers.sessionFactsVersion === args.markers.sessionFactsVersion && (rowMarkers.upgradeState ?? null) === (args.markers.upgradeState ?? null) && (rowMarkers.systemHash ?? "") === (args.markers.systemHash ?? "") && (rowMarkers.toolSetHash ?? "") === (args.markers.toolSetHash ?? "") && (rowMarkers.modelKey ?? "") === (args.markers.modelKey ?? "");
175887
+ return bufferEqualsNullable(args.row.cached_m0_bytes, args.m0Bytes) && rowMarkers.projectMemoryEpoch === args.markers.projectMemoryEpoch && rowMarkers.projectUserProfileVersion === args.markers.projectUserProfileVersion && rowMarkers.maxCompartmentSeq === args.markers.maxCompartmentSeq && rowMarkers.maxMemoryId === args.markers.maxMemoryId && rowMarkers.maxMutationId === args.markers.maxMutationId && rowMarkers.maxMemoryMutationId === args.markers.maxMemoryMutationId && rowMarkers.materializedAt === args.markers.materializedAt && rowMarkers.sessionFactsVersion === args.markers.sessionFactsVersion && (rowMarkers.upgradeState ?? null) === (args.markers.upgradeState ?? null) && (rowMarkers.systemHash ?? "") === (args.markers.systemHash ?? "") && (rowMarkers.modelKey ?? "") === (args.markers.modelKey ?? "") && (rowMarkers.workspaceFingerprint ?? null) === (args.markers.workspaceFingerprint ?? null);
174101
175888
  }
174102
175889
  function decodeCachedM1(row, sessionId) {
174103
175890
  if (!row.cached_m1_bytes) {
@@ -174308,14 +176095,14 @@ function injectM0M1Pi(state, db, piMessages, entryIds, recomputeM1ThisPass = fal
174308
176095
  const skippedVisibleMessages = boundaryId ? trimPiMessagesToBoundary(piMessages, entryIds, boundaryId) : 0;
174309
176096
  prependM0M1Messages(piMessages, m0, m1);
174310
176097
  sessionLog(state.sessionId, `injected m[0]/m[1] into Pi messages (${m0.length} + ${m1.length} bytes, materialized=${materialized}${decision.reason ? ` reason=${decision.reason}` : ""})`);
176098
+ const memPath = memoryProjectPath(state);
176099
+ const workspace = resolveWorkspaceRenderContextPi(state, db);
176100
+ const memoryCount = memPath ? workspace.isWorkspaced ? getMemoriesByProjects(db, workspace.expandedIdentities, ["active", "permanent"], Date.now(), workspace.ownIdentities, workspace.shareCategories).length : getMemoriesByProject(db, memPath, ["active", "permanent"]).length : 0;
174311
176101
  return {
174312
176102
  injected: true,
174313
176103
  compartmentCount: getCompartments(db, state.sessionId).length,
174314
176104
  factCount: 0,
174315
- memoryCount: memoryProjectPath(state) ? getMemoriesByProject(db, memoryProjectPath(state), [
174316
- "active",
174317
- "permanent"
174318
- ]).length : 0,
176105
+ memoryCount,
174319
176106
  skippedVisibleMessages,
174320
176107
  m0Materialized: materialized,
174321
176108
  m0Reason: decision.reason,
@@ -174376,21 +176163,48 @@ import * as crypto2 from "node:crypto";
174376
176163
 
174377
176164
  // ../plugin/src/features/magic-context/compartment-embedding.ts
174378
176165
  init_logger();
174379
- async function embedAndStoreCompartments(db, sessionId, projectPath, compartments) {
176166
+ async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compartments) {
174380
176167
  if (compartments.length === 0)
174381
176168
  return;
174382
- const update = db.prepare("UPDATE compartments SET p1_embedding = ?, p1_embedding_model_id = ? WHERE id = ?");
174383
- for (const c of compartments) {
174384
- if (!c.p1 || c.p1.length === 0)
174385
- continue;
176169
+ const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectPath);
176170
+ for (const compartment of compartments) {
174386
176171
  try {
174387
- const result = await embedTextForProject(projectPath, c.p1);
174388
- if (result) {
174389
- const blob = Buffer.from(result.vector.buffer, result.vector.byteOffset, result.vector.byteLength);
174390
- update.run(blob, result.modelId, c.id);
176172
+ const fromMemory = compartment.sourceChunkText ? canonicalizeInMemoryChunkTextForEmbedding(compartment.sourceChunkText, compartment.startMessage, compartment.endMessage) : "";
176173
+ const canonicalText = fromMemory || buildCanonicalChunkTextFromFts(db, sessionId, compartment.startMessage, compartment.endMessage);
176174
+ if (canonicalText.length === 0)
176175
+ continue;
176176
+ const windows = chunkCanonicalText(canonicalText, compartment.startMessage, compartment.endMessage, maxInputTokens);
176177
+ if (windows.length === 0)
176178
+ continue;
176179
+ const currentModelId = getProjectChunkEmbeddingModelId(projectPath);
176180
+ if (currentModelId !== "off" && chunkEmbeddingWindowsAreCurrent(db, compartment.id, currentModelId, windows, projectPath)) {
176181
+ continue;
176182
+ }
176183
+ const result = await embedBatchForProject(projectPath, windows.map((window) => window.text));
176184
+ if (!result)
176185
+ continue;
176186
+ if (chunkEmbeddingWindowsAreCurrent(db, compartment.id, currentModelId, windows, projectPath)) {
176187
+ continue;
176188
+ }
176189
+ const rows = [];
176190
+ for (const [index, window] of windows.entries()) {
176191
+ const vector = result.vectors[index];
176192
+ if (!vector)
176193
+ continue;
176194
+ rows.push({
176195
+ compartmentId: compartment.id,
176196
+ sessionId,
176197
+ projectPath,
176198
+ window,
176199
+ modelId: currentModelId,
176200
+ vector
176201
+ });
176202
+ }
176203
+ if (rows.length === windows.length) {
176204
+ replaceCompartmentChunkEmbeddings(db, rows);
174391
176205
  }
174392
176206
  } catch (error51) {
174393
- sessionLog(sessionId, `compartment embedding failed for compartment ${c.id}:`, error51);
176207
+ sessionLog(sessionId, `compartment chunk embedding failed for compartment ${compartment.id}:`, error51);
174394
176208
  }
174395
176209
  }
174396
176210
  }
@@ -177459,6 +179273,7 @@ async function runPiHistorian(deps) {
177459
179273
  fallbackModels,
177460
179274
  historianChunkTokens,
177461
179275
  boundarySnapshot: providedBoundarySnapshot,
179276
+ refreshBoundarySnapshot,
177462
179277
  currentContextLimit,
177463
179278
  historianTimeoutMs = DEFAULT_HISTORIAN_TIMEOUT_MS2,
177464
179279
  twoPass,
@@ -177512,16 +179327,29 @@ async function runPiHistorian(deps) {
177512
179327
  return;
177513
179328
  }
177514
179329
  const offset = priorCompartments.length > 0 ? priorCompartments[priorCompartments.length - 1].endMessage + 1 : 1;
177515
- const boundarySnapshot = providedBoundarySnapshot ?? null;
179330
+ let boundarySnapshot = providedBoundarySnapshot ?? null;
177516
179331
  if (!boundarySnapshot) {
177517
179332
  sessionLog(sessionId, "historian no-op: missing protected-tail boundary snapshot from Pi trigger decision");
177518
179333
  return;
177519
179334
  }
177520
- const validation = boundarySnapshot.rawRangeFingerprint.length > 0 ? validateBoundarySnapshot({
179335
+ let validation = boundarySnapshot.rawRangeFingerprint.length > 0 ? validateBoundarySnapshot({
177521
179336
  db,
177522
179337
  snapshot: boundarySnapshot,
177523
179338
  currentContextLimit: currentContextLimit ?? boundarySnapshot.contextLimit
177524
179339
  }) : { ok: true };
179340
+ if (!validation.ok && validation.reason === "stale_snapshot" && refreshBoundarySnapshot) {
179341
+ try {
179342
+ const refreshed = refreshBoundarySnapshot();
179343
+ if (hasRunnableCompartmentWindow(refreshed)) {
179344
+ sessionLog(sessionId, `historian: refreshed stale protected-tail snapshot at run time (was: ${validation.detail ?? "stale"}) — eligible head ${refreshed.offset}-${refreshed.eligibleEndOrdinal - 1}`);
179345
+ boundarySnapshot = refreshed;
179346
+ validation = { ok: true };
179347
+ }
179348
+ } catch (error51) {
179349
+ const desc = describeError(error51);
179350
+ sessionLog(sessionId, `historian: failed to refresh stale protected-tail snapshot at run time (${validation.detail ?? "stale"}): ${desc.brief}`);
179351
+ }
179352
+ }
177525
179353
  if (!validation.ok) {
177526
179354
  sessionLog(sessionId, `historian no-op: stale protected-tail snapshot (${validation.detail ?? validation.reason ?? "unknown"})`);
177527
179355
  return;
@@ -177850,8 +179678,13 @@ ${chunkText}`,
177850
179678
  }
177851
179679
  }
177852
179680
  if (embeddingActive) {
177853
- const toEmbed = newCompartments.map((c, i) => ({ id: persistedIds[i], p1: c.p1 ?? c.content })).filter((c) => typeof c.id === "number" && c.p1.length > 0);
177854
- embedAndStoreCompartments(db, sessionId, projectPath, toEmbed);
179681
+ const chunksToEmbed = newCompartments.map((c, i) => ({
179682
+ id: persistedIds[i],
179683
+ startMessage: c.startMessage,
179684
+ endMessage: c.endMessage,
179685
+ sourceChunkText: chunk.text
179686
+ })).filter((c) => typeof c.id === "number");
179687
+ embedAndStoreCompartmentChunks(db, sessionId, projectPath, chunksToEmbed);
177855
179688
  }
177856
179689
  onPublished?.();
177857
179690
  completedSuccessfully = true;
@@ -178399,7 +180232,7 @@ function stripPiDroppedPlaceholderMessages(args) {
178399
180232
  }
178400
180233
 
178401
180234
  // src/system-prompt.ts
178402
- import { createHash as createHash9 } from "node:crypto";
180235
+ import { createHash as createHash11 } from "node:crypto";
178403
180236
 
178404
180237
  // ../plugin/src/agents/magic-context-prompt.ts
178405
180238
  var LONG_TERM_PARTNER_FRAME = `### You are the user's long-term partner on this project — not a one-off hire
@@ -178550,7 +180383,7 @@ function processSystemPromptForCache(args) {
178550
180383
  sessionLog(sessionId, `system prompt date frozen: real=${liveDate}, using=${stickyDate} (cache-stable pass)`);
178551
180384
  }
178552
180385
  }
178553
- const currentHash = createHash9("md5").update(frozenPrompt).digest("hex");
180386
+ const currentHash = createHash11("md5").update(frozenPrompt).digest("hex");
178554
180387
  const hashChanged = !isFirstHash && currentHash !== previousHash;
178555
180388
  if (hashChanged) {
178556
180389
  sessionLog(sessionId, `system prompt hash changed: ${previousHash} → ${currentHash} (len=${frozenPrompt.length})`);
@@ -179177,6 +181010,19 @@ function extractStableId(msg, index, entryIds) {
179177
181010
  // src/context-handler.ts
179178
181011
  var FORCE_MATERIALIZATION_PERCENTAGE = 85;
179179
181012
  var EMERGENCY_BLOCK_PERCENTAGE = 95;
181013
+ var FORWARD_PRESSURE_LIMIT_FACTOR = 0.85;
181014
+ function applyForwardPressureFloor(trailingPercentage, trailingInputTokens, piUsageTokens, correctedLimit) {
181015
+ const forwardTokens = typeof piUsageTokens === "number" && piUsageTokens > 0 ? piUsageTokens : 0;
181016
+ if (forwardTokens === 0 || !isSaneLimit(correctedLimit)) {
181017
+ return { percentage: trailingPercentage, inputTokens: trailingInputTokens };
181018
+ }
181019
+ const forwardPressureLimit = correctedLimit * FORWARD_PRESSURE_LIMIT_FACTOR;
181020
+ const forwardPercentage = forwardTokens / forwardPressureLimit * 100;
181021
+ return forwardPercentage > trailingPercentage ? {
181022
+ percentage: forwardPercentage,
181023
+ inputTokens: Math.max(trailingInputTokens, forwardTokens)
181024
+ } : { percentage: trailingPercentage, inputTokens: trailingInputTokens };
181025
+ }
179180
181026
  var DEFAULT_CLEAR_REASONING_AGE = 50;
179181
181027
  var PI_STABLE_ID_SCHEME = 1;
179182
181028
  var lastEmergencyNotificationAtMs = new Map;
@@ -179261,7 +181107,7 @@ function trackSessionForProject(projectIdentity, sessionId) {
179261
181107
  function isContextHandlerSessionActive(sessionId) {
179262
181108
  return activeContextHandlerSessions.has(sessionId);
179263
181109
  }
179264
- function updateSessionProjectTracking(sessionId, projectIdentity) {
181110
+ function updateSessionProjectTracking(sessionId, projectIdentity, db) {
179265
181111
  const prev = lastSeenProjectIdentityBySession.get(sessionId);
179266
181112
  if (prev && prev !== projectIdentity) {
179267
181113
  const prevSessions = sessionsByProject.get(prev);
@@ -179270,6 +181116,11 @@ function updateSessionProjectTracking(sessionId, projectIdentity) {
179270
181116
  sessionsByProject.delete(prev);
179271
181117
  clearPiSystemPromptSession(sessionId);
179272
181118
  }
181119
+ if (db && prev !== projectIdentity) {
181120
+ try {
181121
+ recordSessionProjectIdentity(db, sessionId, projectIdentity);
181122
+ } catch {}
181123
+ }
179273
181124
  trackSessionForProject(projectIdentity, sessionId);
179274
181125
  lastSeenProjectIdentityBySession.set(sessionId, projectIdentity);
179275
181126
  }
@@ -179550,7 +181401,7 @@ function registerPiContextHandler(pi, baseOptions) {
179550
181401
  const schedulerConfig = options.scheduler ?? DEFAULT_SCHEDULER_CONFIG;
179551
181402
  const scheduler2 = schedulerFor(options);
179552
181403
  const projectIdentity = resolveProjectIdentity(projectDirectory);
179553
- updateSessionProjectTracking(sessionId, projectIdentity);
181404
+ updateSessionProjectTracking(sessionId, projectIdentity, options.db);
179554
181405
  logTransformTiming(sessionId, "findSessionId", tFindSession, `messages=${event.messages.length}`);
179555
181406
  const branchEntries = readPiBranchEntriesForContext(ctx, sessionId);
179556
181407
  const rawMessageProvider = {
@@ -179659,9 +181510,10 @@ function registerPiContextHandler(pi, baseOptions) {
179659
181510
  if (!usedPersistedUsage && isSaneLimit(usageContextLimit) && usageInputTokens > 0) {
179660
181511
  usagePercentage = usageInputTokens / usageContextLimit * 100;
179661
181512
  }
181513
+ ({ percentage: usagePercentage, inputTokens: usageInputTokens } = applyForwardPressureFloor(usagePercentage, usageInputTokens, piUsage?.tokens, usageContextLimit));
179662
181514
  if (needsEmergencyBump) {
179663
181515
  sessionLog(sessionId, `transform: overflow recovery flag set — bumping percentage to 95% (detectedLimit=${usageContextLimit ?? "unknown"})`);
179664
- usagePercentage = 95;
181516
+ usagePercentage = Math.max(usagePercentage, 95);
179665
181517
  }
179666
181518
  let schedulerDecision;
179667
181519
  const tScheduler = performance.now();
@@ -180045,6 +181897,7 @@ function spawnPiHistorianRun(args) {
180045
181897
  provider: provider2,
180046
181898
  unregister,
180047
181899
  boundarySnapshot,
181900
+ refreshBoundarySnapshot,
180048
181901
  currentContextLimit
180049
181902
  } = args;
180050
181903
  const holderId = crypto3.randomUUID();
@@ -180068,6 +181921,7 @@ function spawnPiHistorianRun(args) {
180068
181921
  fallbackModels: historian.fallbackModels,
180069
181922
  historianChunkTokens: historian.historianChunkTokens,
180070
181923
  boundarySnapshot,
181924
+ refreshBoundarySnapshot,
180071
181925
  currentContextLimit,
180072
181926
  historianTimeoutMs: historian.timeoutMs,
180073
181927
  twoPass: historian.twoPass,
@@ -180134,6 +181988,7 @@ function maybeFireHistorian(args) {
180134
181988
  let usageContextLimit;
180135
181989
  try {
180136
181990
  const piUsage = ctx.getContextUsage?.();
181991
+ let usageSource;
180137
181992
  usageContextLimit = isSaneLimit(piUsage?.contextWindow) ? piUsage.contextWindow : undefined;
180138
181993
  if (usageContextLimit === undefined && isSaneLimit(ctx.model?.contextWindow)) {
180139
181994
  usageContextLimit = ctx.model.contextWindow;
@@ -180150,7 +182005,7 @@ function maybeFireHistorian(args) {
180150
182005
  percentage: sessionMetaForUsage.lastContextPercentage,
180151
182006
  inputTokens: sessionMetaForUsage.lastInputTokens
180152
182007
  };
180153
- sessionLog(sessionId, `historian trigger eval: usage=${usage.percentage.toFixed(1)}% (${usage.inputTokens} tokens) [from session_meta], checking trigger...`);
182008
+ usageSource = "session_meta";
180154
182009
  } else {
180155
182010
  if (!piUsage || piUsage.tokens === null || piUsage.percent === null || piUsage.contextWindow === 0) {
180156
182011
  sessionLog(sessionId, `historian trigger eval: no usage info yet (tokens=${piUsage?.tokens ?? "<no piUsage>"}, percent=${piUsage?.percent ?? "<no piUsage>"}, contextWindow=${piUsage?.contextWindow ?? "<no piUsage>"})`);
@@ -180161,8 +182016,10 @@ function maybeFireHistorian(args) {
180161
182016
  percentage: fallbackPercentage,
180162
182017
  inputTokens: piUsage.tokens
180163
182018
  };
180164
- sessionLog(sessionId, `historian trigger eval: usage=${usage.percentage.toFixed(1)}% (${usage.inputTokens} tokens) [piUsage fallback], checking trigger...`);
182019
+ usageSource = "piUsage fallback";
180165
182020
  }
182021
+ usage = applyForwardPressureFloor(usage.percentage, usage.inputTokens, piUsage?.tokens, usageContextLimit);
182022
+ sessionLog(sessionId, `historian trigger eval: usage=${usage.percentage.toFixed(1)}% (${usage.inputTokens} tokens) [${usageSource}], checking trigger...`);
180166
182023
  } catch (err) {
180167
182024
  sessionLog(sessionId, `historian trigger eval: getContextUsage threw: ${err instanceof Error ? err.message : String(err)}`);
180168
182025
  return;
@@ -180193,10 +182050,14 @@ function maybeFireHistorian(args) {
180193
182050
  cacheNamespace: `pi:${sessionId}`,
180194
182051
  emergencyTailScale
180195
182052
  }));
180196
- let boundarySnapshot = ensureRunnablePiBoundaryForTests(resolvePiBoundarySnapshot());
180197
- if (!hasRunnableCompartmentWindow(boundarySnapshot) && usage.percentage >= 80) {
180198
- boundarySnapshot = ensureRunnablePiBoundaryForTests(resolvePiBoundarySnapshot(usage.percentage >= 95 ? 0.25 : 0.5));
180199
- }
182053
+ const resolveRunnablePiBoundarySnapshot = () => {
182054
+ let snapshot = ensureRunnablePiBoundaryForTests(resolvePiBoundarySnapshot());
182055
+ if (!hasRunnableCompartmentWindow(snapshot) && usage.percentage >= 80) {
182056
+ snapshot = ensureRunnablePiBoundaryForTests(resolvePiBoundarySnapshot(usage.percentage >= 95 ? 0.25 : 0.5));
182057
+ }
182058
+ return snapshot;
182059
+ };
182060
+ const boundarySnapshot = resolveRunnablePiBoundarySnapshot();
180200
182061
  let triggered = false;
180201
182062
  try {
180202
182063
  if (isFirstContextPassForSession) {
@@ -180221,6 +182082,7 @@ Historian previously failed ${failureState.failureCount} time(s), so Magic Conte
180221
182082
  provider: provider2,
180222
182083
  unregister,
180223
182084
  boundarySnapshot,
182085
+ refreshBoundarySnapshot: resolveRunnablePiBoundarySnapshot,
180224
182086
  currentContextLimit: boundaryContextLimit
180225
182087
  });
180226
182088
  return;
@@ -180244,6 +182106,7 @@ Historian previously failed ${failureState.failureCount} time(s), so Magic Conte
180244
182106
  resolvedBoundarySnapshot: boundarySnapshot,
180245
182107
  triggerBoundarySnapshot: trigger.boundarySnapshot
180246
182108
  }),
182109
+ refreshBoundarySnapshot: resolveRunnablePiBoundarySnapshot,
180247
182110
  currentContextLimit: boundaryContextLimit
180248
182111
  });
180249
182112
  } catch (err) {
@@ -180284,7 +182147,31 @@ async function runPipeline(args) {
180284
182147
  const alreadyRanHeuristicsThisTurn = currentTurnId !== null && lastHeuristicsTurnIdBySession.get(args.sessionId) === currentTurnId;
180285
182148
  const canConsumeDeferredLate = args.schedulerDecision === "execute" || args.forceMaterialization === true || args.contextUsage.percentage >= FORCE_MATERIALIZATION_PERCENTAGE;
180286
182149
  const deferredMaterializeEligible = canConsumeDeferredLate && deferredMaterializationSessions.has(args.sessionId);
180287
- const shouldRunHeuristics = args.heuristics !== undefined && (args.forceMaterialization === true || hasPendingMaterialization(args.sessionId) || deferredMaterializeEligible || args.schedulerDecision === "execute" && !alreadyRanHeuristicsThisTurn);
182150
+ const piHardSignals = args.injection ? (() => {
182151
+ const hardMeta = getOrCreateSessionMeta(args.db, args.sessionId);
182152
+ let piTtlMs = 5 * 60 * 1000;
182153
+ try {
182154
+ piTtlMs = parseCacheTtl(hardMeta.cacheTtl);
182155
+ } catch {}
182156
+ return {
182157
+ systemHash: typeof hardMeta.systemPromptHash === "string" ? hardMeta.systemPromptHash : "",
182158
+ modelKey: liveModelBySession.get(args.sessionId) ?? "",
182159
+ cacheExpired: hardMeta.lastResponseTime > 0 && Date.now() - hardMeta.lastResponseTime >= piTtlMs,
182160
+ lastResponseTime: hardMeta.lastResponseTime
182161
+ };
182162
+ })() : undefined;
182163
+ const m0HardFoldThisPass = args.injection && piHardSignals ? mustMaterializePi({
182164
+ sessionId: args.sessionId,
182165
+ projectIdentity: args.projectIdentity,
182166
+ projectDirectory: args.projectDirectory,
182167
+ memoryEnabled: args.injection.memoryEnabled,
182168
+ injectionBudgetTokens: args.injection.injectionBudgetTokens,
182169
+ historyBudgetTokens: args.injection.historyBudgetTokens,
182170
+ keyFilesEnabled: args.injection.keyFilesEnabled,
182171
+ keyFilesTokenBudget: args.injection.keyFilesTokenBudget,
182172
+ hardSignals: piHardSignals
182173
+ }, args.db, getCompartments(args.db, args.sessionId)).value : false;
182174
+ const shouldRunHeuristics = args.heuristics !== undefined && (args.forceMaterialization === true || hasPendingMaterialization(args.sessionId) || deferredMaterializeEligible || m0HardFoldThisPass || args.schedulerDecision === "execute" && !alreadyRanHeuristicsThisTurn);
180288
182175
  const entryFingerprintByMessageId = buildEntryFingerprintMap(args.messages, stableIdResolver);
180289
182176
  adoptPiFallbackTags(args.db, args.sessionId, args.tagger, entryFingerprintByMessageId);
180290
182177
  const tTag = performance.now();
@@ -180311,12 +182198,12 @@ async function runPipeline(args) {
180311
182198
  const deferredMaterializationWasPending = deferredMaterializationSessions.has(args.sessionId);
180312
182199
  const deferredHistoryRefreshWasPending = deferredHistoryWasPendingAtPassStart;
180313
182200
  const pendingOps = getPendingOps(args.db, args.sessionId);
180314
- const baseShouldApplyPendingOps = args.schedulerDecision === "execute" || args.forceMaterialization || hasPendingMaterializeSignal;
182201
+ const baseShouldApplyPendingOps = args.schedulerDecision === "execute" || args.forceMaterialization || hasPendingMaterializeSignal || m0HardFoldThisPass;
180315
182202
  const deferredMaterialize = canConsumeDeferredLate && deferredMaterializationWasPending;
180316
182203
  const deferredHistoryRefresh = canConsumeDeferredLate && deferredHistoryRefreshWasPending;
180317
182204
  const shouldApplyPendingOps = baseShouldApplyPendingOps || deferredMaterialize;
180318
182205
  if (shouldApplyPendingOps) {
180319
- const applyReason = hasPendingMaterializeSignal ? "explicit_flush" : deferredMaterialize ? "deferred_publication" : args.forceMaterialization ? "force_materialization" : `scheduler_execute (scheduler=${args.schedulerDecision})`;
182206
+ const applyReason = hasPendingMaterializeSignal ? "explicit_flush" : deferredMaterialize ? "deferred_publication" : args.forceMaterialization ? "force_materialization" : m0HardFoldThisPass && args.schedulerDecision !== "execute" ? `m0_hard_fold (drain folded into known m[0] bust, scheduler=${args.schedulerDecision})` : `scheduler_execute (scheduler=${args.schedulerDecision})`;
180320
182207
  sessionLog(args.sessionId, `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${pendingOps.length}, context=${args.contextUsage.percentage.toFixed(1)}%`);
180321
182208
  try {
180322
182209
  const tApplyPending = performance.now();
@@ -180387,7 +182274,7 @@ async function runPipeline(args) {
180387
182274
  const activeTags = getActiveTagsBySession(args.db, args.sessionId);
180388
182275
  logTransformTiming(args.sessionId, "getActiveTagsBySession", tActiveTags, `count=${activeTags.length}`);
180389
182276
  if (shouldRunHeuristics) {
180390
- const reason = args.forceMaterialization ? "force_materialization" : `scheduler_execute (pendingOps=${pendingOps.length}, scheduler=${args.schedulerDecision})`;
182277
+ const reason = args.forceMaterialization ? "force_materialization" : m0HardFoldThisPass && args.schedulerDecision !== "execute" ? `m0_hard_fold (drain folded into known m[0] bust, scheduler=${args.schedulerDecision})` : `scheduler_execute (pendingOps=${pendingOps.length}, scheduler=${args.schedulerDecision})`;
180391
182278
  sessionLog(args.sessionId, `heuristics WILL RUN — reason=${reason}, context=${args.contextUsage.percentage.toFixed(1)}%, turn=n/a`);
180392
182279
  } else {
180393
182280
  const reason = args.heuristics === undefined ? "disabled" : "scheduler_defer";
@@ -180475,18 +182362,6 @@ async function runPipeline(args) {
180475
182362
  if (args.injection) {
180476
182363
  try {
180477
182364
  const tInjection = performance.now();
180478
- const hardMeta = getOrCreateSessionMeta(args.db, args.sessionId);
180479
- let piTtlMs = 5 * 60 * 1000;
180480
- try {
180481
- piTtlMs = parseCacheTtl(hardMeta.cacheTtl);
180482
- } catch {}
180483
- const piHardSignals = {
180484
- systemHash: typeof hardMeta.systemPromptHash === "string" ? hardMeta.systemPromptHash : "",
180485
- toolSetHash: "",
180486
- modelKey: liveModelBySession.get(args.sessionId) ?? "",
180487
- cacheExpired: hardMeta.lastResponseTime > 0 && Date.now() - hardMeta.lastResponseTime >= piTtlMs,
180488
- lastResponseTime: hardMeta.lastResponseTime
180489
- };
180490
182365
  injectionResult = injectM0M1Pi({
180491
182366
  sessionId: args.sessionId,
180492
182367
  projectIdentity: args.projectIdentity,
@@ -181576,8 +183451,12 @@ Found ${existingStaging.compartments.length} staged compartment(s) from ${existi
181576
183451
  const projectIdentity = resolveProjectIdentity(sessionDirectory);
181577
183452
  await deps.ensureProjectRegistered?.(sessionDirectory, db);
181578
183453
  const liveCompartments = getCompartments(db, sessionId);
181579
- const toEmbed = liveCompartments.map((c) => ({ id: c.id, p1: c.p1 ?? c.content })).filter((c) => typeof c.id === "number" && c.p1.length > 0);
181580
- embedAndStoreCompartments(db, sessionId, projectIdentity, toEmbed);
183454
+ const chunksToEmbed = liveCompartments.map((c) => ({
183455
+ id: c.id,
183456
+ startMessage: c.startMessage,
183457
+ endMessage: c.endMessage
183458
+ }));
183459
+ embedAndStoreCompartmentChunks(db, sessionId, projectIdentity, chunksToEmbed);
181581
183460
  }
181582
183461
  const lastCompartmentEnd2 = promoted2.compartments[promoted2.compartments.length - 1]?.endMessage ?? 0;
181583
183462
  if (lastCompartmentEnd2 > 0) {
@@ -181790,8 +183669,12 @@ Another process acquired the compartment-state lease before recomp could publish
181790
183669
  const projectIdentity = resolveProjectIdentity(sessionDirectory);
181791
183670
  await deps.ensureProjectRegistered?.(sessionDirectory, db);
181792
183671
  const liveCompartments = getCompartments(db, sessionId);
181793
- const toEmbed = liveCompartments.map((c) => ({ id: c.id, p1: c.p1 ?? c.content })).filter((c) => typeof c.id === "number" && c.p1.length > 0);
181794
- embedAndStoreCompartments(db, sessionId, projectIdentity, toEmbed);
183672
+ const chunksToEmbed = liveCompartments.map((c) => ({
183673
+ id: c.id,
183674
+ startMessage: c.startMessage,
183675
+ endMessage: c.endMessage
183676
+ }));
183677
+ embedAndStoreCompartmentChunks(db, sessionId, projectIdentity, chunksToEmbed);
181795
183678
  }
181796
183679
  if (lastCompartmentEnd > 0) {
181797
183680
  const markerUpdated = updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, deps.directory);
@@ -181948,8 +183831,12 @@ Could not acquire the compartment-state lease for this session.`;
181948
183831
  if (deps.memoryEnabled !== false) {
181949
183832
  const projectIdentity = resolveProjectIdentity(sessionDirectory);
181950
183833
  const liveCompartments = getCompartments(db, sessionId);
181951
- const toEmbed = liveCompartments.map((c) => ({ id: c.id, p1: c.p1 ?? c.content })).filter((c) => typeof c.id === "number" && c.p1.length > 0);
181952
- Promise.resolve(deps.ensureProjectRegistered?.(sessionDirectory, db)).then(() => embedAndStoreCompartments(db, sessionId, projectIdentity, toEmbed));
183834
+ const chunksToEmbed = liveCompartments.map((c) => ({
183835
+ id: c.id,
183836
+ startMessage: c.startMessage,
183837
+ endMessage: c.endMessage
183838
+ }));
183839
+ Promise.resolve(deps.ensureProjectRegistered?.(sessionDirectory, db)).then(() => embedAndStoreCompartmentChunks(db, sessionId, projectIdentity, chunksToEmbed));
181953
183840
  }
181954
183841
  const lastEnd = merged[merged.length - 1]?.endMessage ?? snapEnd;
181955
183842
  if (lastEnd > 0) {
@@ -182761,7 +184648,7 @@ function applyMemoryMigration(db, projectPath, result) {
182761
184648
  inserted++;
182762
184649
  }
182763
184650
  if (removed > 0 || inserted > 0) {
182764
- bumpProjectMemoryEpoch(db, projectPath);
184651
+ bumpEpochsForWorkspaceMembers(db, projectPath);
182765
184652
  }
182766
184653
  })();
182767
184654
  return { removed, inserted };
@@ -183237,7 +185124,7 @@ function formatThresholdPercent(value) {
183237
185124
  // package.json
183238
185125
  var package_default = {
183239
185126
  name: "@cortexkit/pi-magic-context",
183240
- version: "0.23.0",
185127
+ version: "0.24.0",
183241
185128
  type: "module",
183242
185129
  description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
183243
185130
  main: "dist/index.js",
@@ -188101,6 +189988,23 @@ function createCtxMemoryTool(deps) {
188101
189988
  }
188102
189989
  const projectIdentity = resolveProjectIdentity(ctx.cwd);
188103
189990
  await deps.ensureProjectRegistered?.(ctx.cwd, deps.db);
189991
+ const workspaceIdentitySet = resolveWorkspaceIdentitySet(deps.db, projectIdentity);
189992
+ const expandedWorkspace = expandWorkspaceIdentitySetWithAliases(deps.db, workspaceIdentitySet.identities);
189993
+ const workspaceVisibleIdentities = workspaceIdentitySet.identities.length > 1 ? expandedWorkspace.expandedIdentities : workspaceIdentitySet.identities;
189994
+ const targetIdentityForStoredPath = (rawProjectPath) => workspaceIdentitySet.identities.length > 1 ? resolveStoredPathWorkspaceIdentity(rawProjectPath, workspaceIdentitySet.identities, expandedWorkspace.canonicalIdentityByStoredPath) ?? normalizeStoredProjectPath(rawProjectPath) : normalizeStoredProjectPath(rawProjectPath);
189995
+ const toolShareCategories = workspaceIdentitySet.identities.length > 1 ? resolveWorkspaceShareCategories(deps.db, projectIdentity) : null;
189996
+ const memoryVisibleToTool = (memory2) => {
189997
+ if (workspaceIdentitySet.identities.length <= 1) {
189998
+ return storedPathBelongsToIdentity(memory2.projectPath, projectIdentity);
189999
+ }
190000
+ if (!storedPathBelongsToWorkspace(memory2.projectPath, workspaceIdentitySet.identities, workspaceVisibleIdentities, expandedWorkspace.canonicalIdentityByStoredPath)) {
190001
+ return false;
190002
+ }
190003
+ const isOwn = targetIdentityForStoredPath(memory2.projectPath) === projectIdentity;
190004
+ if (isOwn)
190005
+ return true;
190006
+ return toolShareCategories === null || toolShareCategories.includes(memory2.category);
190007
+ };
188104
190008
  const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
188105
190009
  if (snapshot ? !snapshot.features.memoryEnabled : deps.memoryEnabled === false) {
188106
190010
  return err2("Cross-session memory is disabled for this project.");
@@ -188147,28 +190051,34 @@ function createCtxMemoryTool(deps) {
188147
190051
  return err2("Error: 'content' is required when action is 'update'.");
188148
190052
  }
188149
190053
  const memory2 = getMemoryById(deps.db, updateId);
188150
- if (!memory2 || !storedPathBelongsToIdentity(memory2.projectPath, projectIdentity)) {
190054
+ if (!memory2 || !memoryVisibleToTool(memory2)) {
188151
190055
  return err2(`Error: Memory with ID ${updateId} was not found.`);
188152
190056
  }
188153
190057
  if (!dreamerAllowed && !isPrimaryMutableMemory(memory2)) {
188154
190058
  return err2(inactiveMemoryError(updateId, "updating"));
188155
190059
  }
188156
190060
  const normalizedHash = computeNormalizedHash(content);
188157
- const duplicate = getMemoryByHash(deps.db, projectIdentity, memory2.category, normalizedHash);
190061
+ const targetIdentity = targetIdentityForStoredPath(memory2.projectPath);
190062
+ const duplicate = getMemoryByHash(deps.db, targetIdentity, memory2.category, normalizedHash);
188158
190063
  if (duplicate && duplicate.id !== memory2.id) {
188159
190064
  return err2(`Error: Memory content already exists as ID ${duplicate.id}; merge or archive duplicates instead.`);
188160
190065
  }
188161
190066
  deps.db.transaction(() => {
188162
190067
  updateMemoryContent(deps.db, memory2.id, content, normalizedHash);
188163
190068
  queueMemoryMutation(deps.db, {
188164
- projectPath: projectIdentity,
190069
+ projectPath: targetIdentity,
188165
190070
  mutationType: "update",
188166
190071
  targetMemoryId: memory2.id,
188167
190072
  category: memory2.category,
188168
190073
  newContent: content
188169
190074
  });
188170
190075
  })();
188171
- queueEmbedding({ deps, projectIdentity, memoryId: memory2.id, content });
190076
+ queueEmbedding({
190077
+ deps,
190078
+ projectIdentity: targetIdentity,
190079
+ memoryId: memory2.id,
190080
+ content
190081
+ });
188172
190082
  return ok2(`Updated memory [ID: ${memory2.id}] in ${memory2.category}.`);
188173
190083
  }
188174
190084
  if (params.action === "merge") {
@@ -188188,7 +190098,7 @@ function createCtxMemoryTool(deps) {
188188
190098
  return err2("Error: One or more source memories were not found.");
188189
190099
  }
188190
190100
  if (!dreamerAllowed) {
188191
- const foreign = sourceMemories.find((memory2) => !storedPathBelongsToIdentity(memory2.projectPath, projectIdentity));
190101
+ const foreign = sourceMemories.find((memory2) => !memoryVisibleToTool(memory2));
188192
190102
  if (foreign) {
188193
190103
  return err2(`Error: Memory with ID ${foreign.id} was not found.`);
188194
190104
  }
@@ -188277,26 +190187,36 @@ function createCtxMemoryTool(deps) {
188277
190187
  return ok2(`Merged memories [${ids.join(", ")}] into canonical memory [ID: ${canonicalMemory.id}] in ${category}; superseded [${supersededIds.join(", ")}].`);
188278
190188
  }
188279
190189
  if (params.action === "archive") {
188280
- const archiveIds = params.ids;
188281
- if (!archiveIds || archiveIds.length === 0 || !archiveIds.every(Number.isInteger)) {
190190
+ const rawArchiveIds = params.ids;
190191
+ if (!rawArchiveIds || rawArchiveIds.length === 0 || !rawArchiveIds.every(Number.isInteger)) {
188282
190192
  return err2("Error: 'ids' must contain at least one integer memory ID when action is 'archive'.");
188283
190193
  }
190194
+ const archiveIds = [...new Set(rawArchiveIds)];
188284
190195
  for (const memoryId of archiveIds) {
188285
190196
  const memory2 = getMemoryById(deps.db, memoryId);
188286
- if (!memory2 || !storedPathBelongsToIdentity(memory2.projectPath, projectIdentity)) {
190197
+ if (!memory2 || !memoryVisibleToTool(memory2)) {
188287
190198
  return err2(`Error: Memory with ID ${memoryId} was not found.`);
188288
190199
  }
188289
190200
  if (!dreamerAllowed && !isPrimaryMutableMemory(memory2)) {
188290
190201
  return err2(inactiveMemoryError(memoryId, "archiving"));
188291
190202
  }
188292
190203
  }
190204
+ const targets = archiveIds.map((memoryId) => {
190205
+ const memory2 = getMemoryById(deps.db, memoryId);
190206
+ if (!memory2)
190207
+ throw new Error(`validated memory ${memoryId} disappeared`);
190208
+ return {
190209
+ memoryId,
190210
+ projectIdentity: targetIdentityForStoredPath(memory2.projectPath)
190211
+ };
190212
+ });
188293
190213
  deps.db.transaction(() => {
188294
- for (const memoryId of archiveIds) {
188295
- archiveMemory(deps.db, memoryId, params.reason);
190214
+ for (const target2 of targets) {
190215
+ archiveMemory(deps.db, target2.memoryId, params.reason);
188296
190216
  queueMemoryMutation(deps.db, {
188297
- projectPath: projectIdentity,
190217
+ projectPath: target2.projectIdentity,
188298
190218
  mutationType: "archive",
188299
- targetMemoryId: memoryId
190219
+ targetMemoryId: target2.memoryId
188300
190220
  });
188301
190221
  }
188302
190222
  })();
@@ -188804,8 +190724,9 @@ function formatAge2(committedAtMs) {
188804
190724
  }
188805
190725
  function formatResult(result, index) {
188806
190726
  if (result.source === "memory") {
190727
+ const source = result.sourceName ? ` source=${result.sourceName}` : "";
188807
190728
  return [
188808
- `[${index}] [memory] score=${result.score.toFixed(2)} id=${result.memoryId} category=${result.category} match=${result.matchType}`,
190729
+ `[${index}] [memory] score=${result.score.toFixed(2)} id=${result.memoryId} category=${result.category}${source} match=${result.matchType}`,
188809
190730
  result.content
188810
190731
  ].join(`
188811
190732
  `);
@@ -188815,6 +190736,13 @@ function formatResult(result, index) {
188815
190736
  `[${index}] [git_commit] score=${result.score.toFixed(2)} sha=${result.shortSha} ${formatAge2(result.committedAtMs)} match=${result.matchType}`,
188816
190737
  result.content
188817
190738
  ].join(`
190739
+ `);
190740
+ }
190741
+ if (result.source === "compartment") {
190742
+ return [
190743
+ `[${index}] [message] score=${result.score.toFixed(2)} compartment_id=${result.compartmentId} range=${result.startOrdinal}-${result.endOrdinal} match=${result.matchType} title=${result.title}`,
190744
+ result.snippet ? `Snippet: ${result.snippet}` : result.content
190745
+ ].join(`
188818
190746
  `);
188819
190747
  }
188820
190748
  const expandStart = Math.max(1, result.messageOrdinal - 3);
@@ -188830,7 +190758,7 @@ function formatSearchResults(query, results) {
188830
190758
  return `No results found for "${query}" across memories, git commits, or message history.`;
188831
190759
  }
188832
190760
  const bodyParts = results.map((result, index) => formatResult(result, index + 1));
188833
- if (results.some((result) => result.source === "message")) {
190761
+ if (results.some((result) => result.source === "message" || result.source === "compartment")) {
188834
190762
  bodyParts.push("Use ctx_expand(start, end) with the range from any message result above to read the full conversation context.");
188835
190763
  }
188836
190764
  const body = bodyParts.join(`
@@ -189326,6 +191254,14 @@ async function src_default2(pi) {
189326
191254
  onProjectSeen: (identity) => seenDreamerProjectIdentities.add(identity)
189327
191255
  });
189328
191256
  info("registered /ctx-dream");
191257
+ registerCtxEmbedHistoryCommand(pi, {
191258
+ db,
191259
+ projectDir,
191260
+ projectIdentity,
191261
+ memoryEnabled: config2.memory.enabled,
191262
+ resolveProject: resolveCurrentProject
191263
+ });
191264
+ info("registered /ctx-embed-history");
189329
191265
  const dreamerConfig = resolveDreamerFromConfig(config2);
189330
191266
  if (dreamerConfig) {
189331
191267
  registerPiDreamerProject({