@askexenow/exe-os 0.9.34 → 0.9.35

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.
Files changed (70) hide show
  1. package/dist/bin/backfill-conversations.js +210 -10
  2. package/dist/bin/backfill-responses.js +210 -10
  3. package/dist/bin/backfill-vectors.js +13 -2
  4. package/dist/bin/cleanup-stale-review-tasks.js +217 -10
  5. package/dist/bin/cli.js +222 -11
  6. package/dist/bin/exe-assign.js +210 -10
  7. package/dist/bin/exe-boot.js +24 -3
  8. package/dist/bin/exe-dispatch.js +222 -11
  9. package/dist/bin/exe-doctor.js +13 -2
  10. package/dist/bin/exe-export-behaviors.js +217 -10
  11. package/dist/bin/exe-forget.js +217 -10
  12. package/dist/bin/exe-gateway.js +222 -11
  13. package/dist/bin/exe-heartbeat.js +217 -10
  14. package/dist/bin/exe-kill.js +217 -10
  15. package/dist/bin/exe-launch-agent.js +92 -2
  16. package/dist/bin/exe-link.js +8 -0
  17. package/dist/bin/exe-pending-messages.js +217 -10
  18. package/dist/bin/exe-pending-notifications.js +217 -10
  19. package/dist/bin/exe-pending-reviews.js +217 -10
  20. package/dist/bin/exe-rename.js +8 -0
  21. package/dist/bin/exe-review.js +217 -10
  22. package/dist/bin/exe-search.js +217 -10
  23. package/dist/bin/exe-session-cleanup.js +222 -11
  24. package/dist/bin/exe-start-codex.js +92 -2
  25. package/dist/bin/exe-start-opencode.js +92 -2
  26. package/dist/bin/exe-status.js +217 -10
  27. package/dist/bin/exe-team.js +217 -10
  28. package/dist/bin/git-sweep.js +222 -11
  29. package/dist/bin/graph-backfill.js +92 -2
  30. package/dist/bin/graph-export.js +217 -10
  31. package/dist/bin/intercom-check.js +222 -11
  32. package/dist/bin/scan-tasks.js +222 -11
  33. package/dist/bin/setup.js +8 -0
  34. package/dist/bin/shard-migrate.js +92 -2
  35. package/dist/gateway/index.js +222 -11
  36. package/dist/hooks/bug-report-worker.js +222 -11
  37. package/dist/hooks/codex-stop-task-finalizer.js +217 -10
  38. package/dist/hooks/commit-complete.js +222 -11
  39. package/dist/hooks/error-recall.js +217 -10
  40. package/dist/hooks/ingest.js +217 -10
  41. package/dist/hooks/instructions-loaded.js +217 -10
  42. package/dist/hooks/notification.js +217 -10
  43. package/dist/hooks/post-compact.js +217 -10
  44. package/dist/hooks/post-tool-combined.js +217 -10
  45. package/dist/hooks/pre-compact.js +222 -11
  46. package/dist/hooks/pre-tool-use.js +217 -10
  47. package/dist/hooks/prompt-submit.js +222 -11
  48. package/dist/hooks/session-end.js +222 -11
  49. package/dist/hooks/session-start.js +217 -10
  50. package/dist/hooks/stop.js +217 -10
  51. package/dist/hooks/subagent-stop.js +217 -10
  52. package/dist/hooks/summary-worker.js +14 -1
  53. package/dist/index.js +222 -11
  54. package/dist/lib/cloud-sync.js +8 -0
  55. package/dist/lib/consolidation.js +3 -1
  56. package/dist/lib/database.js +8 -0
  57. package/dist/lib/db.js +8 -0
  58. package/dist/lib/device-registry.js +8 -0
  59. package/dist/lib/exe-daemon.js +1667 -1413
  60. package/dist/lib/hybrid-search.js +217 -10
  61. package/dist/lib/schedules.js +13 -2
  62. package/dist/lib/store.js +210 -10
  63. package/dist/lib/tasks.js +5 -1
  64. package/dist/lib/tmux-routing.js +5 -1
  65. package/dist/mcp/server.js +222 -11
  66. package/dist/mcp/tools/create-task.js +5 -1
  67. package/dist/mcp/tools/update-task.js +5 -1
  68. package/dist/runtime/index.js +222 -11
  69. package/dist/tui/App.js +222 -11
  70. package/package.json +1 -1
@@ -2866,6 +2866,14 @@ async function ensureSchema() {
2866
2866
  );
2867
2867
  } catch {
2868
2868
  }
2869
+ try {
2870
+ await client.execute(
2871
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2872
+ ON memories(content_hash, agent_id, project_name, memory_type)
2873
+ WHERE content_hash IS NOT NULL`
2874
+ );
2875
+ } catch {
2876
+ }
2869
2877
  await client.executeMultiple(`
2870
2878
  CREATE TABLE IF NOT EXISTS entities (
2871
2879
  id TEXT PRIMARY KEY,
@@ -6381,7 +6389,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6381
6389
  }
6382
6390
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
6383
6391
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
6384
- envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
6392
+ let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
6393
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
6394
+ ccModel += "[1m]";
6395
+ }
6396
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
6385
6397
  }
6386
6398
  }
6387
6399
  let spawnCommand;
@@ -6728,6 +6740,196 @@ var init_keychain = __esm({
6728
6740
  }
6729
6741
  });
6730
6742
 
6743
+ // src/lib/memory-write-governor.ts
6744
+ import { createHash } from "crypto";
6745
+ function normalizeMemoryText(text) {
6746
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
6747
+ }
6748
+ function classifyMemoryType(input) {
6749
+ if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
6750
+ const tool = input.tool_name.toLowerCase();
6751
+ const text = input.raw_text.toLowerCase();
6752
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
6753
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
6754
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
6755
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
6756
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
6757
+ if (tool === "store_memory" || tool === "manual") return "observation";
6758
+ return "raw";
6759
+ }
6760
+ function shouldDropMemory(text) {
6761
+ const normalized = normalizeMemoryText(text);
6762
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
6763
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
6764
+ return { drop: true, reason: "known_boilerplate_noise" };
6765
+ }
6766
+ return { drop: false };
6767
+ }
6768
+ function shouldSkipEmbedding(input) {
6769
+ const type = classifyMemoryType(input);
6770
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
6771
+ if (type === "raw" && input.raw_text.length > 2e4) return true;
6772
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
6773
+ return false;
6774
+ }
6775
+ function hashMemoryContent(text) {
6776
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
6777
+ }
6778
+ function scopedDedupArgs(input) {
6779
+ return [input.contentHash, input.agentId, input.projectName, input.memoryType];
6780
+ }
6781
+ function governMemoryRecord(record) {
6782
+ const normalized = normalizeMemoryText(record.raw_text);
6783
+ const memoryType = classifyMemoryType({
6784
+ raw_text: normalized,
6785
+ agent_id: record.agent_id,
6786
+ project_name: record.project_name,
6787
+ tool_name: record.tool_name,
6788
+ memory_type: record.memory_type
6789
+ });
6790
+ const drop = shouldDropMemory(normalized);
6791
+ const skipEmbedding = shouldSkipEmbedding({
6792
+ raw_text: normalized,
6793
+ agent_id: record.agent_id,
6794
+ project_name: record.project_name,
6795
+ tool_name: record.tool_name,
6796
+ memory_type: memoryType
6797
+ });
6798
+ return {
6799
+ record: {
6800
+ ...record,
6801
+ raw_text: normalized,
6802
+ memory_type: memoryType,
6803
+ vector: skipEmbedding ? null : record.vector
6804
+ },
6805
+ contentHash: hashMemoryContent(normalized),
6806
+ shouldDrop: drop.drop,
6807
+ dropReason: drop.reason,
6808
+ skipEmbedding,
6809
+ hygiene: {
6810
+ dedup: true,
6811
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
6812
+ }
6813
+ };
6814
+ }
6815
+ async function findScopedDuplicate(input) {
6816
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6817
+ const client = getClient2();
6818
+ const args = scopedDedupArgs(input);
6819
+ let sql = `SELECT id FROM memories
6820
+ WHERE content_hash = ?
6821
+ AND agent_id = ?
6822
+ AND project_name = ?
6823
+ AND COALESCE(memory_type, 'raw') = ?
6824
+ AND COALESCE(status, 'active') != 'deleted'`;
6825
+ if (input.excludeId) {
6826
+ sql += " AND id != ?";
6827
+ args.push(input.excludeId);
6828
+ }
6829
+ sql += " ORDER BY timestamp DESC LIMIT 1";
6830
+ const result = await client.execute({ sql, args });
6831
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
6832
+ }
6833
+ async function runPostWriteMemoryHygiene(memoryId) {
6834
+ try {
6835
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6836
+ const client = getClient2();
6837
+ const current = await client.execute({
6838
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
6839
+ importance, timestamp
6840
+ FROM memories
6841
+ WHERE id = ?
6842
+ LIMIT 1`,
6843
+ args: [memoryId]
6844
+ });
6845
+ const row = current.rows[0];
6846
+ if (!row) return;
6847
+ const memoryType = String(row.memory_type ?? "raw");
6848
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
6849
+ const agentId = String(row.agent_id);
6850
+ const projectName = String(row.project_name);
6851
+ if (contentHash) {
6852
+ await client.execute({
6853
+ sql: `UPDATE memories
6854
+ SET status = 'deleted',
6855
+ outcome = COALESCE(outcome, 'superseded')
6856
+ WHERE id != ?
6857
+ AND content_hash = ?
6858
+ AND agent_id = ?
6859
+ AND project_name = ?
6860
+ AND COALESCE(memory_type, 'raw') = ?
6861
+ AND COALESCE(status, 'active') = 'active'`,
6862
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
6863
+ });
6864
+ }
6865
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
6866
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
6867
+ const old = await client.execute({
6868
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
6869
+ args: [supersedesId]
6870
+ });
6871
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
6872
+ const newImportance = Number(row.importance ?? 0);
6873
+ await client.batch([
6874
+ {
6875
+ sql: `UPDATE memories
6876
+ SET status = 'archived',
6877
+ outcome = COALESCE(outcome, 'superseded')
6878
+ WHERE id = ?`,
6879
+ args: [supersedesId]
6880
+ },
6881
+ {
6882
+ sql: `UPDATE memories
6883
+ SET importance = MAX(COALESCE(importance, 5), ?),
6884
+ parent_memory_id = COALESCE(parent_memory_id, ?)
6885
+ WHERE id = ?`,
6886
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
6887
+ }
6888
+ ], "write");
6889
+ }
6890
+ } catch (err) {
6891
+ process.stderr.write(
6892
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
6893
+ `
6894
+ );
6895
+ }
6896
+ }
6897
+ function schedulePostWriteMemoryHygiene(memoryIds) {
6898
+ if (memoryIds.length === 0) return;
6899
+ const run = () => {
6900
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
6901
+ };
6902
+ if (typeof setImmediate === "function") setImmediate(run);
6903
+ else setTimeout(run, 0);
6904
+ }
6905
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
6906
+ var init_memory_write_governor = __esm({
6907
+ "src/lib/memory-write-governor.ts"() {
6908
+ "use strict";
6909
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
6910
+ "decision",
6911
+ "adr",
6912
+ "behavior",
6913
+ "procedure"
6914
+ ]);
6915
+ NOISE_DROP_PATTERNS = [
6916
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
6917
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
6918
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
6919
+ /^\s*Intercom is a speedup, not delivery/im,
6920
+ /^\s*Context bar reads as USAGE not remaining/im
6921
+ ];
6922
+ SKIP_EMBED_PATTERNS = [
6923
+ /tmux capture-pane\b/i,
6924
+ /docker ps\b/i,
6925
+ /docker images\b/i,
6926
+ /git status\b/i,
6927
+ /grep .*node_modules/i,
6928
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
6929
+ ];
6930
+ }
6931
+ });
6932
+
6731
6933
  // src/lib/shard-manager.ts
6732
6934
  var shard_manager_exports = {};
6733
6935
  __export(shard_manager_exports, {
@@ -6892,7 +7094,8 @@ async function ensureShardSchema(client) {
6892
7094
  }
6893
7095
  for (const idx of [
6894
7096
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
6895
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
7097
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
7098
+ "CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash ON memories(content_hash, agent_id, project_name, memory_type) WHERE content_hash IS NOT NULL"
6896
7099
  ]) {
6897
7100
  try {
6898
7101
  await client.execute(idx);
@@ -7317,7 +7520,6 @@ __export(store_exports, {
7317
7520
  vectorToBlob: () => vectorToBlob,
7318
7521
  writeMemory: () => writeMemory
7319
7522
  });
7320
- import { createHash } from "crypto";
7321
7523
  function isBusyError2(err) {
7322
7524
  if (err instanceof Error) {
7323
7525
  const msg = err.message.toLowerCase();
@@ -7431,17 +7633,24 @@ async function writeMemory(record) {
7431
7633
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
7432
7634
  );
7433
7635
  }
7434
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
7435
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
7636
+ const governed = governMemoryRecord(record);
7637
+ if (governed.shouldDrop) return;
7638
+ record = governed.record;
7639
+ const contentHash = governed.contentHash;
7640
+ const memoryType = record.memory_type ?? "raw";
7641
+ if (_pendingRecords.some(
7642
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
7643
+ )) {
7436
7644
  return;
7437
7645
  }
7438
7646
  try {
7439
- const client = getClient();
7440
- const existing = await client.execute({
7441
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
7442
- args: [contentHash, record.agent_id]
7647
+ const existing = await findScopedDuplicate({
7648
+ contentHash,
7649
+ agentId: record.agent_id,
7650
+ projectName: record.project_name,
7651
+ memoryType
7443
7652
  });
7444
- if (existing.rows.length > 0) return;
7653
+ if (existing) return;
7445
7654
  } catch {
7446
7655
  }
7447
7656
  const dbRow = {
@@ -7472,7 +7681,7 @@ async function writeMemory(record) {
7472
7681
  tier: record.tier ?? classifyTier(record),
7473
7682
  supersedes_id: record.supersedes_id ?? null,
7474
7683
  draft: record.draft ? 1 : 0,
7475
- memory_type: record.memory_type ?? "raw",
7684
+ memory_type: memoryType,
7476
7685
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
7477
7686
  content_hash: contentHash,
7478
7687
  intent: record.intent ?? null,
@@ -7631,6 +7840,7 @@ async function flushBatch() {
7631
7840
  const globalClient = getClient();
7632
7841
  const globalStmts = batch.map(buildStmt);
7633
7842
  await globalClient.batch(globalStmts, "write");
7843
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
7634
7844
  _pendingRecords.splice(0, batch.length);
7635
7845
  try {
7636
7846
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -7889,6 +8099,7 @@ var init_store = __esm({
7889
8099
  init_keychain();
7890
8100
  init_config();
7891
8101
  init_state_bus();
8102
+ init_memory_write_governor();
7892
8103
  INIT_MAX_RETRIES = 3;
7893
8104
  INIT_RETRY_DELAY_MS = 1e3;
7894
8105
  _pendingRecords = [];
@@ -2279,6 +2279,14 @@ async function ensureSchema() {
2279
2279
  );
2280
2280
  } catch {
2281
2281
  }
2282
+ try {
2283
+ await client.execute(
2284
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2285
+ ON memories(content_hash, agent_id, project_name, memory_type)
2286
+ WHERE content_hash IS NOT NULL`
2287
+ );
2288
+ } catch {
2289
+ }
2282
2290
  await client.executeMultiple(`
2283
2291
  CREATE TABLE IF NOT EXISTS entities (
2284
2292
  id TEXT PRIMARY KEY,
@@ -3024,6 +3032,196 @@ var init_state_bus = __esm({
3024
3032
  }
3025
3033
  });
3026
3034
 
3035
+ // src/lib/memory-write-governor.ts
3036
+ import { createHash } from "crypto";
3037
+ function normalizeMemoryText(text) {
3038
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
3039
+ }
3040
+ function classifyMemoryType(input2) {
3041
+ if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
3042
+ const tool = input2.tool_name.toLowerCase();
3043
+ const text = input2.raw_text.toLowerCase();
3044
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
3045
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
3046
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
3047
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
3048
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
3049
+ if (tool === "store_memory" || tool === "manual") return "observation";
3050
+ return "raw";
3051
+ }
3052
+ function shouldDropMemory(text) {
3053
+ const normalized = normalizeMemoryText(text);
3054
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
3055
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
3056
+ return { drop: true, reason: "known_boilerplate_noise" };
3057
+ }
3058
+ return { drop: false };
3059
+ }
3060
+ function shouldSkipEmbedding(input2) {
3061
+ const type = classifyMemoryType(input2);
3062
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
3063
+ if (type === "raw" && input2.raw_text.length > 2e4) return true;
3064
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
3065
+ return false;
3066
+ }
3067
+ function hashMemoryContent(text) {
3068
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
3069
+ }
3070
+ function scopedDedupArgs(input2) {
3071
+ return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
3072
+ }
3073
+ function governMemoryRecord(record) {
3074
+ const normalized = normalizeMemoryText(record.raw_text);
3075
+ const memoryType = classifyMemoryType({
3076
+ raw_text: normalized,
3077
+ agent_id: record.agent_id,
3078
+ project_name: record.project_name,
3079
+ tool_name: record.tool_name,
3080
+ memory_type: record.memory_type
3081
+ });
3082
+ const drop = shouldDropMemory(normalized);
3083
+ const skipEmbedding = shouldSkipEmbedding({
3084
+ raw_text: normalized,
3085
+ agent_id: record.agent_id,
3086
+ project_name: record.project_name,
3087
+ tool_name: record.tool_name,
3088
+ memory_type: memoryType
3089
+ });
3090
+ return {
3091
+ record: {
3092
+ ...record,
3093
+ raw_text: normalized,
3094
+ memory_type: memoryType,
3095
+ vector: skipEmbedding ? null : record.vector
3096
+ },
3097
+ contentHash: hashMemoryContent(normalized),
3098
+ shouldDrop: drop.drop,
3099
+ dropReason: drop.reason,
3100
+ skipEmbedding,
3101
+ hygiene: {
3102
+ dedup: true,
3103
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
3104
+ }
3105
+ };
3106
+ }
3107
+ async function findScopedDuplicate(input2) {
3108
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3109
+ const client = getClient2();
3110
+ const args = scopedDedupArgs(input2);
3111
+ let sql = `SELECT id FROM memories
3112
+ WHERE content_hash = ?
3113
+ AND agent_id = ?
3114
+ AND project_name = ?
3115
+ AND COALESCE(memory_type, 'raw') = ?
3116
+ AND COALESCE(status, 'active') != 'deleted'`;
3117
+ if (input2.excludeId) {
3118
+ sql += " AND id != ?";
3119
+ args.push(input2.excludeId);
3120
+ }
3121
+ sql += " ORDER BY timestamp DESC LIMIT 1";
3122
+ const result = await client.execute({ sql, args });
3123
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
3124
+ }
3125
+ async function runPostWriteMemoryHygiene(memoryId) {
3126
+ try {
3127
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3128
+ const client = getClient2();
3129
+ const current = await client.execute({
3130
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
3131
+ importance, timestamp
3132
+ FROM memories
3133
+ WHERE id = ?
3134
+ LIMIT 1`,
3135
+ args: [memoryId]
3136
+ });
3137
+ const row = current.rows[0];
3138
+ if (!row) return;
3139
+ const memoryType = String(row.memory_type ?? "raw");
3140
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
3141
+ const agentId = String(row.agent_id);
3142
+ const projectName = String(row.project_name);
3143
+ if (contentHash) {
3144
+ await client.execute({
3145
+ sql: `UPDATE memories
3146
+ SET status = 'deleted',
3147
+ outcome = COALESCE(outcome, 'superseded')
3148
+ WHERE id != ?
3149
+ AND content_hash = ?
3150
+ AND agent_id = ?
3151
+ AND project_name = ?
3152
+ AND COALESCE(memory_type, 'raw') = ?
3153
+ AND COALESCE(status, 'active') = 'active'`,
3154
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
3155
+ });
3156
+ }
3157
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
3158
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
3159
+ const old = await client.execute({
3160
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
3161
+ args: [supersedesId]
3162
+ });
3163
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
3164
+ const newImportance = Number(row.importance ?? 0);
3165
+ await client.batch([
3166
+ {
3167
+ sql: `UPDATE memories
3168
+ SET status = 'archived',
3169
+ outcome = COALESCE(outcome, 'superseded')
3170
+ WHERE id = ?`,
3171
+ args: [supersedesId]
3172
+ },
3173
+ {
3174
+ sql: `UPDATE memories
3175
+ SET importance = MAX(COALESCE(importance, 5), ?),
3176
+ parent_memory_id = COALESCE(parent_memory_id, ?)
3177
+ WHERE id = ?`,
3178
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
3179
+ }
3180
+ ], "write");
3181
+ }
3182
+ } catch (err) {
3183
+ process.stderr.write(
3184
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
3185
+ `
3186
+ );
3187
+ }
3188
+ }
3189
+ function schedulePostWriteMemoryHygiene(memoryIds) {
3190
+ if (memoryIds.length === 0) return;
3191
+ const run = () => {
3192
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
3193
+ };
3194
+ if (typeof setImmediate === "function") setImmediate(run);
3195
+ else setTimeout(run, 0);
3196
+ }
3197
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
3198
+ var init_memory_write_governor = __esm({
3199
+ "src/lib/memory-write-governor.ts"() {
3200
+ "use strict";
3201
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
3202
+ "decision",
3203
+ "adr",
3204
+ "behavior",
3205
+ "procedure"
3206
+ ]);
3207
+ NOISE_DROP_PATTERNS = [
3208
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
3209
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
3210
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
3211
+ /^\s*Intercom is a speedup, not delivery/im,
3212
+ /^\s*Context bar reads as USAGE not remaining/im
3213
+ ];
3214
+ SKIP_EMBED_PATTERNS = [
3215
+ /tmux capture-pane\b/i,
3216
+ /docker ps\b/i,
3217
+ /docker images\b/i,
3218
+ /git status\b/i,
3219
+ /grep .*node_modules/i,
3220
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
3221
+ ];
3222
+ }
3223
+ });
3224
+
3027
3225
  // src/lib/shard-manager.ts
3028
3226
  var shard_manager_exports = {};
3029
3227
  __export(shard_manager_exports, {
@@ -3188,7 +3386,8 @@ async function ensureShardSchema(client) {
3188
3386
  }
3189
3387
  for (const idx of [
3190
3388
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
3191
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
3389
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
3390
+ "CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash ON memories(content_hash, agent_id, project_name, memory_type) WHERE content_hash IS NOT NULL"
3192
3391
  ]) {
3193
3392
  try {
3194
3393
  await client.execute(idx);
@@ -3613,7 +3812,6 @@ __export(store_exports, {
3613
3812
  vectorToBlob: () => vectorToBlob,
3614
3813
  writeMemory: () => writeMemory
3615
3814
  });
3616
- import { createHash } from "crypto";
3617
3815
  function isBusyError2(err) {
3618
3816
  if (err instanceof Error) {
3619
3817
  const msg = err.message.toLowerCase();
@@ -3727,17 +3925,24 @@ async function writeMemory(record) {
3727
3925
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
3728
3926
  );
3729
3927
  }
3730
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
3731
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
3928
+ const governed = governMemoryRecord(record);
3929
+ if (governed.shouldDrop) return;
3930
+ record = governed.record;
3931
+ const contentHash = governed.contentHash;
3932
+ const memoryType = record.memory_type ?? "raw";
3933
+ if (_pendingRecords.some(
3934
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
3935
+ )) {
3732
3936
  return;
3733
3937
  }
3734
3938
  try {
3735
- const client = getClient();
3736
- const existing = await client.execute({
3737
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
3738
- args: [contentHash, record.agent_id]
3939
+ const existing = await findScopedDuplicate({
3940
+ contentHash,
3941
+ agentId: record.agent_id,
3942
+ projectName: record.project_name,
3943
+ memoryType
3739
3944
  });
3740
- if (existing.rows.length > 0) return;
3945
+ if (existing) return;
3741
3946
  } catch {
3742
3947
  }
3743
3948
  const dbRow = {
@@ -3768,7 +3973,7 @@ async function writeMemory(record) {
3768
3973
  tier: record.tier ?? classifyTier(record),
3769
3974
  supersedes_id: record.supersedes_id ?? null,
3770
3975
  draft: record.draft ? 1 : 0,
3771
- memory_type: record.memory_type ?? "raw",
3976
+ memory_type: memoryType,
3772
3977
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
3773
3978
  content_hash: contentHash,
3774
3979
  intent: record.intent ?? null,
@@ -3927,6 +4132,7 @@ async function flushBatch() {
3927
4132
  const globalClient = getClient();
3928
4133
  const globalStmts = batch.map(buildStmt);
3929
4134
  await globalClient.batch(globalStmts, "write");
4135
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
3930
4136
  _pendingRecords.splice(0, batch.length);
3931
4137
  try {
3932
4138
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -4185,6 +4391,7 @@ var init_store = __esm({
4185
4391
  init_keychain();
4186
4392
  init_config();
4187
4393
  init_state_bus();
4394
+ init_memory_write_governor();
4188
4395
  INIT_MAX_RETRIES = 3;
4189
4396
  INIT_RETRY_DELAY_MS = 1e3;
4190
4397
  _pendingRecords = [];