@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
@@ -2277,6 +2277,14 @@ async function ensureSchema() {
2277
2277
  );
2278
2278
  } catch {
2279
2279
  }
2280
+ try {
2281
+ await client.execute(
2282
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2283
+ ON memories(content_hash, agent_id, project_name, memory_type)
2284
+ WHERE content_hash IS NOT NULL`
2285
+ );
2286
+ } catch {
2287
+ }
2280
2288
  await client.executeMultiple(`
2281
2289
  CREATE TABLE IF NOT EXISTS entities (
2282
2290
  id TEXT PRIMARY KEY,
@@ -3022,6 +3030,196 @@ var init_state_bus = __esm({
3022
3030
  }
3023
3031
  });
3024
3032
 
3033
+ // src/lib/memory-write-governor.ts
3034
+ import { createHash } from "crypto";
3035
+ function normalizeMemoryText(text) {
3036
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
3037
+ }
3038
+ function classifyMemoryType(input) {
3039
+ if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
3040
+ const tool = input.tool_name.toLowerCase();
3041
+ const text = input.raw_text.toLowerCase();
3042
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
3043
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
3044
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
3045
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
3046
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
3047
+ if (tool === "store_memory" || tool === "manual") return "observation";
3048
+ return "raw";
3049
+ }
3050
+ function shouldDropMemory(text) {
3051
+ const normalized = normalizeMemoryText(text);
3052
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
3053
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
3054
+ return { drop: true, reason: "known_boilerplate_noise" };
3055
+ }
3056
+ return { drop: false };
3057
+ }
3058
+ function shouldSkipEmbedding(input) {
3059
+ const type = classifyMemoryType(input);
3060
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
3061
+ if (type === "raw" && input.raw_text.length > 2e4) return true;
3062
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
3063
+ return false;
3064
+ }
3065
+ function hashMemoryContent(text) {
3066
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
3067
+ }
3068
+ function scopedDedupArgs(input) {
3069
+ return [input.contentHash, input.agentId, input.projectName, input.memoryType];
3070
+ }
3071
+ function governMemoryRecord(record) {
3072
+ const normalized = normalizeMemoryText(record.raw_text);
3073
+ const memoryType = classifyMemoryType({
3074
+ raw_text: normalized,
3075
+ agent_id: record.agent_id,
3076
+ project_name: record.project_name,
3077
+ tool_name: record.tool_name,
3078
+ memory_type: record.memory_type
3079
+ });
3080
+ const drop = shouldDropMemory(normalized);
3081
+ const skipEmbedding = shouldSkipEmbedding({
3082
+ raw_text: normalized,
3083
+ agent_id: record.agent_id,
3084
+ project_name: record.project_name,
3085
+ tool_name: record.tool_name,
3086
+ memory_type: memoryType
3087
+ });
3088
+ return {
3089
+ record: {
3090
+ ...record,
3091
+ raw_text: normalized,
3092
+ memory_type: memoryType,
3093
+ vector: skipEmbedding ? null : record.vector
3094
+ },
3095
+ contentHash: hashMemoryContent(normalized),
3096
+ shouldDrop: drop.drop,
3097
+ dropReason: drop.reason,
3098
+ skipEmbedding,
3099
+ hygiene: {
3100
+ dedup: true,
3101
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
3102
+ }
3103
+ };
3104
+ }
3105
+ async function findScopedDuplicate(input) {
3106
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3107
+ const client = getClient2();
3108
+ const args = scopedDedupArgs(input);
3109
+ let sql = `SELECT id FROM memories
3110
+ WHERE content_hash = ?
3111
+ AND agent_id = ?
3112
+ AND project_name = ?
3113
+ AND COALESCE(memory_type, 'raw') = ?
3114
+ AND COALESCE(status, 'active') != 'deleted'`;
3115
+ if (input.excludeId) {
3116
+ sql += " AND id != ?";
3117
+ args.push(input.excludeId);
3118
+ }
3119
+ sql += " ORDER BY timestamp DESC LIMIT 1";
3120
+ const result = await client.execute({ sql, args });
3121
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
3122
+ }
3123
+ async function runPostWriteMemoryHygiene(memoryId) {
3124
+ try {
3125
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3126
+ const client = getClient2();
3127
+ const current = await client.execute({
3128
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
3129
+ importance, timestamp
3130
+ FROM memories
3131
+ WHERE id = ?
3132
+ LIMIT 1`,
3133
+ args: [memoryId]
3134
+ });
3135
+ const row = current.rows[0];
3136
+ if (!row) return;
3137
+ const memoryType = String(row.memory_type ?? "raw");
3138
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
3139
+ const agentId = String(row.agent_id);
3140
+ const projectName = String(row.project_name);
3141
+ if (contentHash) {
3142
+ await client.execute({
3143
+ sql: `UPDATE memories
3144
+ SET status = 'deleted',
3145
+ outcome = COALESCE(outcome, 'superseded')
3146
+ WHERE id != ?
3147
+ AND content_hash = ?
3148
+ AND agent_id = ?
3149
+ AND project_name = ?
3150
+ AND COALESCE(memory_type, 'raw') = ?
3151
+ AND COALESCE(status, 'active') = 'active'`,
3152
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
3153
+ });
3154
+ }
3155
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
3156
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
3157
+ const old = await client.execute({
3158
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
3159
+ args: [supersedesId]
3160
+ });
3161
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
3162
+ const newImportance = Number(row.importance ?? 0);
3163
+ await client.batch([
3164
+ {
3165
+ sql: `UPDATE memories
3166
+ SET status = 'archived',
3167
+ outcome = COALESCE(outcome, 'superseded')
3168
+ WHERE id = ?`,
3169
+ args: [supersedesId]
3170
+ },
3171
+ {
3172
+ sql: `UPDATE memories
3173
+ SET importance = MAX(COALESCE(importance, 5), ?),
3174
+ parent_memory_id = COALESCE(parent_memory_id, ?)
3175
+ WHERE id = ?`,
3176
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
3177
+ }
3178
+ ], "write");
3179
+ }
3180
+ } catch (err) {
3181
+ process.stderr.write(
3182
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
3183
+ `
3184
+ );
3185
+ }
3186
+ }
3187
+ function schedulePostWriteMemoryHygiene(memoryIds) {
3188
+ if (memoryIds.length === 0) return;
3189
+ const run = () => {
3190
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
3191
+ };
3192
+ if (typeof setImmediate === "function") setImmediate(run);
3193
+ else setTimeout(run, 0);
3194
+ }
3195
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
3196
+ var init_memory_write_governor = __esm({
3197
+ "src/lib/memory-write-governor.ts"() {
3198
+ "use strict";
3199
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
3200
+ "decision",
3201
+ "adr",
3202
+ "behavior",
3203
+ "procedure"
3204
+ ]);
3205
+ NOISE_DROP_PATTERNS = [
3206
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
3207
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
3208
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
3209
+ /^\s*Intercom is a speedup, not delivery/im,
3210
+ /^\s*Context bar reads as USAGE not remaining/im
3211
+ ];
3212
+ SKIP_EMBED_PATTERNS = [
3213
+ /tmux capture-pane\b/i,
3214
+ /docker ps\b/i,
3215
+ /docker images\b/i,
3216
+ /git status\b/i,
3217
+ /grep .*node_modules/i,
3218
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
3219
+ ];
3220
+ }
3221
+ });
3222
+
3025
3223
  // src/lib/shard-manager.ts
3026
3224
  var shard_manager_exports = {};
3027
3225
  __export(shard_manager_exports, {
@@ -3186,7 +3384,8 @@ async function ensureShardSchema(client) {
3186
3384
  }
3187
3385
  for (const idx of [
3188
3386
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
3189
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
3387
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
3388
+ "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"
3190
3389
  ]) {
3191
3390
  try {
3192
3391
  await client.execute(idx);
@@ -3611,7 +3810,6 @@ __export(store_exports, {
3611
3810
  vectorToBlob: () => vectorToBlob,
3612
3811
  writeMemory: () => writeMemory
3613
3812
  });
3614
- import { createHash } from "crypto";
3615
3813
  function isBusyError2(err) {
3616
3814
  if (err instanceof Error) {
3617
3815
  const msg = err.message.toLowerCase();
@@ -3725,17 +3923,24 @@ async function writeMemory(record) {
3725
3923
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
3726
3924
  );
3727
3925
  }
3728
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
3729
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
3926
+ const governed = governMemoryRecord(record);
3927
+ if (governed.shouldDrop) return;
3928
+ record = governed.record;
3929
+ const contentHash = governed.contentHash;
3930
+ const memoryType = record.memory_type ?? "raw";
3931
+ if (_pendingRecords.some(
3932
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
3933
+ )) {
3730
3934
  return;
3731
3935
  }
3732
3936
  try {
3733
- const client = getClient();
3734
- const existing = await client.execute({
3735
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
3736
- args: [contentHash, record.agent_id]
3937
+ const existing = await findScopedDuplicate({
3938
+ contentHash,
3939
+ agentId: record.agent_id,
3940
+ projectName: record.project_name,
3941
+ memoryType
3737
3942
  });
3738
- if (existing.rows.length > 0) return;
3943
+ if (existing) return;
3739
3944
  } catch {
3740
3945
  }
3741
3946
  const dbRow = {
@@ -3766,7 +3971,7 @@ async function writeMemory(record) {
3766
3971
  tier: record.tier ?? classifyTier(record),
3767
3972
  supersedes_id: record.supersedes_id ?? null,
3768
3973
  draft: record.draft ? 1 : 0,
3769
- memory_type: record.memory_type ?? "raw",
3974
+ memory_type: memoryType,
3770
3975
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
3771
3976
  content_hash: contentHash,
3772
3977
  intent: record.intent ?? null,
@@ -3925,6 +4130,7 @@ async function flushBatch() {
3925
4130
  const globalClient = getClient();
3926
4131
  const globalStmts = batch.map(buildStmt);
3927
4132
  await globalClient.batch(globalStmts, "write");
4133
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
3928
4134
  _pendingRecords.splice(0, batch.length);
3929
4135
  try {
3930
4136
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -4183,6 +4389,7 @@ var init_store = __esm({
4183
4389
  init_keychain();
4184
4390
  init_config();
4185
4391
  init_state_bus();
4392
+ init_memory_write_governor();
4186
4393
  INIT_MAX_RETRIES = 3;
4187
4394
  INIT_RETRY_DELAY_MS = 1e3;
4188
4395
  _pendingRecords = [];
@@ -2321,6 +2321,14 @@ async function ensureSchema() {
2321
2321
  );
2322
2322
  } catch {
2323
2323
  }
2324
+ try {
2325
+ await client.execute(
2326
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2327
+ ON memories(content_hash, agent_id, project_name, memory_type)
2328
+ WHERE content_hash IS NOT NULL`
2329
+ );
2330
+ } catch {
2331
+ }
2324
2332
  await client.executeMultiple(`
2325
2333
  CREATE TABLE IF NOT EXISTS entities (
2326
2334
  id TEXT PRIMARY KEY,
@@ -3066,6 +3074,196 @@ var init_state_bus = __esm({
3066
3074
  }
3067
3075
  });
3068
3076
 
3077
+ // src/lib/memory-write-governor.ts
3078
+ import { createHash } from "crypto";
3079
+ function normalizeMemoryText(text) {
3080
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
3081
+ }
3082
+ function classifyMemoryType(input) {
3083
+ if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
3084
+ const tool = input.tool_name.toLowerCase();
3085
+ const text = input.raw_text.toLowerCase();
3086
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
3087
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
3088
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
3089
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
3090
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
3091
+ if (tool === "store_memory" || tool === "manual") return "observation";
3092
+ return "raw";
3093
+ }
3094
+ function shouldDropMemory(text) {
3095
+ const normalized = normalizeMemoryText(text);
3096
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
3097
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
3098
+ return { drop: true, reason: "known_boilerplate_noise" };
3099
+ }
3100
+ return { drop: false };
3101
+ }
3102
+ function shouldSkipEmbedding(input) {
3103
+ const type = classifyMemoryType(input);
3104
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
3105
+ if (type === "raw" && input.raw_text.length > 2e4) return true;
3106
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
3107
+ return false;
3108
+ }
3109
+ function hashMemoryContent(text) {
3110
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
3111
+ }
3112
+ function scopedDedupArgs(input) {
3113
+ return [input.contentHash, input.agentId, input.projectName, input.memoryType];
3114
+ }
3115
+ function governMemoryRecord(record) {
3116
+ const normalized = normalizeMemoryText(record.raw_text);
3117
+ const memoryType = classifyMemoryType({
3118
+ raw_text: normalized,
3119
+ agent_id: record.agent_id,
3120
+ project_name: record.project_name,
3121
+ tool_name: record.tool_name,
3122
+ memory_type: record.memory_type
3123
+ });
3124
+ const drop = shouldDropMemory(normalized);
3125
+ const skipEmbedding = shouldSkipEmbedding({
3126
+ raw_text: normalized,
3127
+ agent_id: record.agent_id,
3128
+ project_name: record.project_name,
3129
+ tool_name: record.tool_name,
3130
+ memory_type: memoryType
3131
+ });
3132
+ return {
3133
+ record: {
3134
+ ...record,
3135
+ raw_text: normalized,
3136
+ memory_type: memoryType,
3137
+ vector: skipEmbedding ? null : record.vector
3138
+ },
3139
+ contentHash: hashMemoryContent(normalized),
3140
+ shouldDrop: drop.drop,
3141
+ dropReason: drop.reason,
3142
+ skipEmbedding,
3143
+ hygiene: {
3144
+ dedup: true,
3145
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
3146
+ }
3147
+ };
3148
+ }
3149
+ async function findScopedDuplicate(input) {
3150
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3151
+ const client = getClient2();
3152
+ const args = scopedDedupArgs(input);
3153
+ let sql = `SELECT id FROM memories
3154
+ WHERE content_hash = ?
3155
+ AND agent_id = ?
3156
+ AND project_name = ?
3157
+ AND COALESCE(memory_type, 'raw') = ?
3158
+ AND COALESCE(status, 'active') != 'deleted'`;
3159
+ if (input.excludeId) {
3160
+ sql += " AND id != ?";
3161
+ args.push(input.excludeId);
3162
+ }
3163
+ sql += " ORDER BY timestamp DESC LIMIT 1";
3164
+ const result = await client.execute({ sql, args });
3165
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
3166
+ }
3167
+ async function runPostWriteMemoryHygiene(memoryId) {
3168
+ try {
3169
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3170
+ const client = getClient2();
3171
+ const current = await client.execute({
3172
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
3173
+ importance, timestamp
3174
+ FROM memories
3175
+ WHERE id = ?
3176
+ LIMIT 1`,
3177
+ args: [memoryId]
3178
+ });
3179
+ const row = current.rows[0];
3180
+ if (!row) return;
3181
+ const memoryType = String(row.memory_type ?? "raw");
3182
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
3183
+ const agentId = String(row.agent_id);
3184
+ const projectName = String(row.project_name);
3185
+ if (contentHash) {
3186
+ await client.execute({
3187
+ sql: `UPDATE memories
3188
+ SET status = 'deleted',
3189
+ outcome = COALESCE(outcome, 'superseded')
3190
+ WHERE id != ?
3191
+ AND content_hash = ?
3192
+ AND agent_id = ?
3193
+ AND project_name = ?
3194
+ AND COALESCE(memory_type, 'raw') = ?
3195
+ AND COALESCE(status, 'active') = 'active'`,
3196
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
3197
+ });
3198
+ }
3199
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
3200
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
3201
+ const old = await client.execute({
3202
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
3203
+ args: [supersedesId]
3204
+ });
3205
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
3206
+ const newImportance = Number(row.importance ?? 0);
3207
+ await client.batch([
3208
+ {
3209
+ sql: `UPDATE memories
3210
+ SET status = 'archived',
3211
+ outcome = COALESCE(outcome, 'superseded')
3212
+ WHERE id = ?`,
3213
+ args: [supersedesId]
3214
+ },
3215
+ {
3216
+ sql: `UPDATE memories
3217
+ SET importance = MAX(COALESCE(importance, 5), ?),
3218
+ parent_memory_id = COALESCE(parent_memory_id, ?)
3219
+ WHERE id = ?`,
3220
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
3221
+ }
3222
+ ], "write");
3223
+ }
3224
+ } catch (err) {
3225
+ process.stderr.write(
3226
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
3227
+ `
3228
+ );
3229
+ }
3230
+ }
3231
+ function schedulePostWriteMemoryHygiene(memoryIds) {
3232
+ if (memoryIds.length === 0) return;
3233
+ const run = () => {
3234
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
3235
+ };
3236
+ if (typeof setImmediate === "function") setImmediate(run);
3237
+ else setTimeout(run, 0);
3238
+ }
3239
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
3240
+ var init_memory_write_governor = __esm({
3241
+ "src/lib/memory-write-governor.ts"() {
3242
+ "use strict";
3243
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
3244
+ "decision",
3245
+ "adr",
3246
+ "behavior",
3247
+ "procedure"
3248
+ ]);
3249
+ NOISE_DROP_PATTERNS = [
3250
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
3251
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
3252
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
3253
+ /^\s*Intercom is a speedup, not delivery/im,
3254
+ /^\s*Context bar reads as USAGE not remaining/im
3255
+ ];
3256
+ SKIP_EMBED_PATTERNS = [
3257
+ /tmux capture-pane\b/i,
3258
+ /docker ps\b/i,
3259
+ /docker images\b/i,
3260
+ /git status\b/i,
3261
+ /grep .*node_modules/i,
3262
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
3263
+ ];
3264
+ }
3265
+ });
3266
+
3069
3267
  // src/lib/shard-manager.ts
3070
3268
  var shard_manager_exports = {};
3071
3269
  __export(shard_manager_exports, {
@@ -3230,7 +3428,8 @@ async function ensureShardSchema(client) {
3230
3428
  }
3231
3429
  for (const idx of [
3232
3430
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
3233
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
3431
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
3432
+ "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"
3234
3433
  ]) {
3235
3434
  try {
3236
3435
  await client.execute(idx);
@@ -3655,7 +3854,6 @@ __export(store_exports, {
3655
3854
  vectorToBlob: () => vectorToBlob,
3656
3855
  writeMemory: () => writeMemory
3657
3856
  });
3658
- import { createHash } from "crypto";
3659
3857
  function isBusyError2(err) {
3660
3858
  if (err instanceof Error) {
3661
3859
  const msg = err.message.toLowerCase();
@@ -3769,17 +3967,24 @@ async function writeMemory(record) {
3769
3967
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
3770
3968
  );
3771
3969
  }
3772
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
3773
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
3970
+ const governed = governMemoryRecord(record);
3971
+ if (governed.shouldDrop) return;
3972
+ record = governed.record;
3973
+ const contentHash = governed.contentHash;
3974
+ const memoryType = record.memory_type ?? "raw";
3975
+ if (_pendingRecords.some(
3976
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
3977
+ )) {
3774
3978
  return;
3775
3979
  }
3776
3980
  try {
3777
- const client = getClient();
3778
- const existing = await client.execute({
3779
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
3780
- args: [contentHash, record.agent_id]
3981
+ const existing = await findScopedDuplicate({
3982
+ contentHash,
3983
+ agentId: record.agent_id,
3984
+ projectName: record.project_name,
3985
+ memoryType
3781
3986
  });
3782
- if (existing.rows.length > 0) return;
3987
+ if (existing) return;
3783
3988
  } catch {
3784
3989
  }
3785
3990
  const dbRow = {
@@ -3810,7 +4015,7 @@ async function writeMemory(record) {
3810
4015
  tier: record.tier ?? classifyTier(record),
3811
4016
  supersedes_id: record.supersedes_id ?? null,
3812
4017
  draft: record.draft ? 1 : 0,
3813
- memory_type: record.memory_type ?? "raw",
4018
+ memory_type: memoryType,
3814
4019
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
3815
4020
  content_hash: contentHash,
3816
4021
  intent: record.intent ?? null,
@@ -3969,6 +4174,7 @@ async function flushBatch() {
3969
4174
  const globalClient = getClient();
3970
4175
  const globalStmts = batch.map(buildStmt);
3971
4176
  await globalClient.batch(globalStmts, "write");
4177
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
3972
4178
  _pendingRecords.splice(0, batch.length);
3973
4179
  try {
3974
4180
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -4227,6 +4433,7 @@ var init_store = __esm({
4227
4433
  init_keychain();
4228
4434
  init_config();
4229
4435
  init_state_bus();
4436
+ init_memory_write_governor();
4230
4437
  INIT_MAX_RETRIES = 3;
4231
4438
  INIT_RETRY_DELAY_MS = 1e3;
4232
4439
  _pendingRecords = [];
@@ -7673,7 +7880,11 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
7673
7880
  }
7674
7881
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
7675
7882
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
7676
- envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
7883
+ let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
7884
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
7885
+ ccModel += "[1m]";
7886
+ }
7887
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
7677
7888
  }
7678
7889
  }
7679
7890
  let spawnCommand;