@askexenow/exe-os 0.9.34 → 0.9.36

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 (76) hide show
  1. package/dist/bin/backfill-conversations.js +212 -11
  2. package/dist/bin/backfill-responses.js +212 -11
  3. package/dist/bin/backfill-vectors.js +14 -3
  4. package/dist/bin/cleanup-stale-review-tasks.js +224 -12
  5. package/dist/bin/cli.js +265 -23
  6. package/dist/bin/exe-agent.js +1 -1
  7. package/dist/bin/exe-assign.js +217 -12
  8. package/dist/bin/exe-boot.js +25 -4
  9. package/dist/bin/exe-call.js +7 -5
  10. package/dist/bin/exe-dispatch.js +229 -13
  11. package/dist/bin/exe-doctor.js +14 -3
  12. package/dist/bin/exe-export-behaviors.js +301 -14
  13. package/dist/bin/exe-forget.js +245 -21
  14. package/dist/bin/exe-gateway.js +229 -13
  15. package/dist/bin/exe-heartbeat.js +224 -12
  16. package/dist/bin/exe-kill.js +224 -12
  17. package/dist/bin/exe-launch-agent.js +177 -9
  18. package/dist/bin/exe-link.js +8 -0
  19. package/dist/bin/exe-new-employee.js +26 -2
  20. package/dist/bin/exe-pending-messages.js +224 -12
  21. package/dist/bin/exe-pending-notifications.js +224 -12
  22. package/dist/bin/exe-pending-reviews.js +224 -12
  23. package/dist/bin/exe-rename.js +9 -1
  24. package/dist/bin/exe-review.js +224 -12
  25. package/dist/bin/exe-search.js +246 -21
  26. package/dist/bin/exe-session-cleanup.js +229 -13
  27. package/dist/bin/exe-start-codex.js +161 -5
  28. package/dist/bin/exe-start-opencode.js +172 -5
  29. package/dist/bin/exe-status.js +224 -12
  30. package/dist/bin/exe-team.js +224 -12
  31. package/dist/bin/git-sweep.js +229 -13
  32. package/dist/bin/graph-backfill.js +94 -3
  33. package/dist/bin/graph-export.js +224 -12
  34. package/dist/bin/install.js +25 -1
  35. package/dist/bin/intercom-check.js +229 -13
  36. package/dist/bin/scan-tasks.js +229 -13
  37. package/dist/bin/setup.js +15 -5
  38. package/dist/bin/shard-migrate.js +94 -3
  39. package/dist/gateway/index.js +229 -13
  40. package/dist/hooks/bug-report-worker.js +229 -13
  41. package/dist/hooks/codex-stop-task-finalizer.js +224 -12
  42. package/dist/hooks/commit-complete.js +229 -13
  43. package/dist/hooks/error-recall.js +246 -21
  44. package/dist/hooks/ingest.js +224 -12
  45. package/dist/hooks/instructions-loaded.js +224 -12
  46. package/dist/hooks/notification.js +224 -12
  47. package/dist/hooks/post-compact.js +224 -12
  48. package/dist/hooks/post-tool-combined.js +246 -21
  49. package/dist/hooks/pre-compact.js +229 -13
  50. package/dist/hooks/pre-tool-use.js +234 -18
  51. package/dist/hooks/prompt-submit.js +346 -23
  52. package/dist/hooks/session-end.js +229 -13
  53. package/dist/hooks/session-start.js +418 -42
  54. package/dist/hooks/stop.js +224 -12
  55. package/dist/hooks/subagent-stop.js +224 -12
  56. package/dist/hooks/summary-worker.js +15 -2
  57. package/dist/index.js +229 -13
  58. package/dist/lib/cloud-sync.js +8 -0
  59. package/dist/lib/consolidation.js +3 -1
  60. package/dist/lib/database.js +8 -0
  61. package/dist/lib/db.js +8 -0
  62. package/dist/lib/device-registry.js +8 -0
  63. package/dist/lib/employee-templates.js +7 -5
  64. package/dist/lib/exe-daemon.js +1776 -1433
  65. package/dist/lib/hybrid-search.js +246 -21
  66. package/dist/lib/schedules.js +14 -3
  67. package/dist/lib/store.js +217 -12
  68. package/dist/lib/tasks.js +5 -1
  69. package/dist/lib/tmux-routing.js +5 -1
  70. package/dist/mcp/server.js +331 -37
  71. package/dist/mcp/tools/create-task.js +5 -1
  72. package/dist/mcp/tools/update-task.js +5 -1
  73. package/dist/runtime/index.js +229 -13
  74. package/dist/tui/App.js +229 -13
  75. package/package.json +1 -1
  76. package/src/commands/exe/save.md +48 -0
@@ -2299,6 +2299,14 @@ async function ensureSchema() {
2299
2299
  );
2300
2300
  } catch {
2301
2301
  }
2302
+ try {
2303
+ await client.execute(
2304
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2305
+ ON memories(content_hash, agent_id, project_name, memory_type)
2306
+ WHERE content_hash IS NOT NULL`
2307
+ );
2308
+ } catch {
2309
+ }
2302
2310
  await client.executeMultiple(`
2303
2311
  CREATE TABLE IF NOT EXISTS entities (
2304
2312
  id TEXT PRIMARY KEY,
@@ -3044,6 +3052,197 @@ var init_state_bus = __esm({
3044
3052
  }
3045
3053
  });
3046
3054
 
3055
+ // src/lib/memory-write-governor.ts
3056
+ import { createHash } from "crypto";
3057
+ function normalizeMemoryText(text) {
3058
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
3059
+ }
3060
+ function classifyMemoryType(input) {
3061
+ if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
3062
+ const tool = input.tool_name.toLowerCase();
3063
+ const text = input.raw_text.toLowerCase();
3064
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
3065
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
3066
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
3067
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
3068
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
3069
+ if (tool === "store_memory" || tool === "manual") return "observation";
3070
+ return "raw";
3071
+ }
3072
+ function shouldDropMemory(text) {
3073
+ const normalized = normalizeMemoryText(text);
3074
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
3075
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
3076
+ return { drop: true, reason: "known_boilerplate_noise" };
3077
+ }
3078
+ return { drop: false };
3079
+ }
3080
+ function shouldSkipEmbedding(input) {
3081
+ const type = classifyMemoryType(input);
3082
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
3083
+ if (type === "raw" && input.raw_text.length > 2e4) return true;
3084
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
3085
+ return false;
3086
+ }
3087
+ function hashMemoryContent(text) {
3088
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
3089
+ }
3090
+ function scopedDedupArgs(input) {
3091
+ return [input.contentHash, input.agentId, input.projectName, input.memoryType];
3092
+ }
3093
+ function governMemoryRecord(record) {
3094
+ const normalized = normalizeMemoryText(record.raw_text);
3095
+ const memoryType = classifyMemoryType({
3096
+ raw_text: normalized,
3097
+ agent_id: record.agent_id,
3098
+ project_name: record.project_name,
3099
+ tool_name: record.tool_name,
3100
+ memory_type: record.memory_type
3101
+ });
3102
+ const drop = shouldDropMemory(normalized);
3103
+ const skipEmbedding = shouldSkipEmbedding({
3104
+ raw_text: normalized,
3105
+ agent_id: record.agent_id,
3106
+ project_name: record.project_name,
3107
+ tool_name: record.tool_name,
3108
+ memory_type: memoryType
3109
+ });
3110
+ return {
3111
+ record: {
3112
+ ...record,
3113
+ raw_text: normalized,
3114
+ memory_type: memoryType,
3115
+ vector: skipEmbedding ? null : record.vector
3116
+ },
3117
+ contentHash: hashMemoryContent(normalized),
3118
+ shouldDrop: drop.drop,
3119
+ dropReason: drop.reason,
3120
+ skipEmbedding,
3121
+ hygiene: {
3122
+ dedup: true,
3123
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
3124
+ }
3125
+ };
3126
+ }
3127
+ async function findScopedDuplicate(input) {
3128
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3129
+ const client = getClient2();
3130
+ const args = scopedDedupArgs(input);
3131
+ let sql = `SELECT id FROM memories
3132
+ WHERE content_hash = ?
3133
+ AND agent_id = ?
3134
+ AND project_name = ?
3135
+ AND COALESCE(memory_type, 'raw') = ?
3136
+ AND COALESCE(status, 'active') != 'deleted'`;
3137
+ if (input.excludeId) {
3138
+ sql += " AND id != ?";
3139
+ args.push(input.excludeId);
3140
+ }
3141
+ sql += " ORDER BY timestamp DESC LIMIT 1";
3142
+ const result = await client.execute({ sql, args });
3143
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
3144
+ }
3145
+ async function runPostWriteMemoryHygiene(memoryId) {
3146
+ try {
3147
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3148
+ const client = getClient2();
3149
+ const current = await client.execute({
3150
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
3151
+ importance, timestamp
3152
+ FROM memories
3153
+ WHERE id = ?
3154
+ LIMIT 1`,
3155
+ args: [memoryId]
3156
+ });
3157
+ const row = current.rows[0];
3158
+ if (!row) return;
3159
+ const memoryType = String(row.memory_type ?? "raw");
3160
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
3161
+ const agentId = String(row.agent_id);
3162
+ const projectName = String(row.project_name);
3163
+ if (contentHash) {
3164
+ await client.execute({
3165
+ sql: `UPDATE memories
3166
+ SET status = 'deleted',
3167
+ outcome = COALESCE(outcome, 'superseded')
3168
+ WHERE id != ?
3169
+ AND content_hash = ?
3170
+ AND agent_id = ?
3171
+ AND project_name = ?
3172
+ AND COALESCE(memory_type, 'raw') = ?
3173
+ AND COALESCE(status, 'active') = 'active'`,
3174
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
3175
+ });
3176
+ }
3177
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
3178
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
3179
+ const old = await client.execute({
3180
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
3181
+ args: [supersedesId]
3182
+ });
3183
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
3184
+ const newImportance = Number(row.importance ?? 0);
3185
+ await client.batch([
3186
+ {
3187
+ sql: `UPDATE memories
3188
+ SET status = 'archived',
3189
+ outcome = COALESCE(outcome, 'superseded')
3190
+ WHERE id = ?`,
3191
+ args: [supersedesId]
3192
+ },
3193
+ {
3194
+ sql: `UPDATE memories
3195
+ SET importance = MAX(COALESCE(importance, 5), ?),
3196
+ parent_memory_id = COALESCE(parent_memory_id, ?)
3197
+ WHERE id = ?`,
3198
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
3199
+ }
3200
+ ], "write");
3201
+ }
3202
+ } catch (err) {
3203
+ process.stderr.write(
3204
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
3205
+ `
3206
+ );
3207
+ }
3208
+ }
3209
+ function schedulePostWriteMemoryHygiene(memoryIds) {
3210
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
3211
+ if (memoryIds.length === 0) return;
3212
+ const run = () => {
3213
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
3214
+ };
3215
+ if (typeof setImmediate === "function") setImmediate(run);
3216
+ else setTimeout(run, 0);
3217
+ }
3218
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
3219
+ var init_memory_write_governor = __esm({
3220
+ "src/lib/memory-write-governor.ts"() {
3221
+ "use strict";
3222
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
3223
+ "decision",
3224
+ "adr",
3225
+ "behavior",
3226
+ "procedure"
3227
+ ]);
3228
+ NOISE_DROP_PATTERNS = [
3229
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
3230
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
3231
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
3232
+ /^\s*Intercom is a speedup, not delivery/im,
3233
+ /^\s*Context bar reads as USAGE not remaining/im
3234
+ ];
3235
+ SKIP_EMBED_PATTERNS = [
3236
+ /tmux capture-pane\b/i,
3237
+ /docker ps\b/i,
3238
+ /docker images\b/i,
3239
+ /git status\b/i,
3240
+ /grep .*node_modules/i,
3241
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
3242
+ ];
3243
+ }
3244
+ });
3245
+
3047
3246
  // src/lib/shard-manager.ts
3048
3247
  var shard_manager_exports = {};
3049
3248
  __export(shard_manager_exports, {
@@ -3208,7 +3407,8 @@ async function ensureShardSchema(client) {
3208
3407
  }
3209
3408
  for (const idx of [
3210
3409
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
3211
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
3410
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
3411
+ "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"
3212
3412
  ]) {
3213
3413
  try {
3214
3414
  await client.execute(idx);
@@ -3402,7 +3602,7 @@ var init_platform_procedures = __esm({
3402
3602
  title: "Chain of command \u2014 who talks to whom",
3403
3603
  domain: "workflow",
3404
3604
  priority: "p0",
3405
- content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the COO does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
3605
+ content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
3406
3606
  },
3407
3607
  {
3408
3608
  title: "Single dispatch path \u2014 create_task only",
@@ -3633,7 +3833,6 @@ __export(store_exports, {
3633
3833
  vectorToBlob: () => vectorToBlob,
3634
3834
  writeMemory: () => writeMemory
3635
3835
  });
3636
- import { createHash } from "crypto";
3637
3836
  function isBusyError2(err) {
3638
3837
  if (err instanceof Error) {
3639
3838
  const msg = err.message.toLowerCase();
@@ -3747,17 +3946,24 @@ async function writeMemory(record) {
3747
3946
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
3748
3947
  );
3749
3948
  }
3750
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
3751
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
3949
+ const governed = governMemoryRecord(record);
3950
+ if (governed.shouldDrop) return;
3951
+ record = governed.record;
3952
+ const contentHash = governed.contentHash;
3953
+ const memoryType = record.memory_type ?? "raw";
3954
+ if (_pendingRecords.some(
3955
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
3956
+ )) {
3752
3957
  return;
3753
3958
  }
3754
3959
  try {
3755
- const client = getClient();
3756
- const existing = await client.execute({
3757
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
3758
- args: [contentHash, record.agent_id]
3960
+ const existing = await findScopedDuplicate({
3961
+ contentHash,
3962
+ agentId: record.agent_id,
3963
+ projectName: record.project_name,
3964
+ memoryType
3759
3965
  });
3760
- if (existing.rows.length > 0) return;
3966
+ if (existing) return;
3761
3967
  } catch {
3762
3968
  }
3763
3969
  const dbRow = {
@@ -3788,7 +3994,7 @@ async function writeMemory(record) {
3788
3994
  tier: record.tier ?? classifyTier(record),
3789
3995
  supersedes_id: record.supersedes_id ?? null,
3790
3996
  draft: record.draft ? 1 : 0,
3791
- memory_type: record.memory_type ?? "raw",
3997
+ memory_type: memoryType,
3792
3998
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
3793
3999
  content_hash: contentHash,
3794
4000
  intent: record.intent ?? null,
@@ -3947,6 +4153,7 @@ async function flushBatch() {
3947
4153
  const globalClient = getClient();
3948
4154
  const globalStmts = batch.map(buildStmt);
3949
4155
  await globalClient.batch(globalStmts, "write");
4156
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
3950
4157
  _pendingRecords.splice(0, batch.length);
3951
4158
  try {
3952
4159
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -4067,7 +4274,11 @@ async function searchMemories(queryVector, agentId, options) {
4067
4274
  sql += ` AND timestamp >= ?`;
4068
4275
  args.push(options.since);
4069
4276
  }
4070
- if (options?.memoryType) {
4277
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
4278
+ const uniqueTypes = [...new Set(options.memoryTypes)];
4279
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
4280
+ args.push(...uniqueTypes);
4281
+ } else if (options?.memoryType) {
4071
4282
  sql += ` AND memory_type = ?`;
4072
4283
  args.push(options.memoryType);
4073
4284
  }
@@ -4205,6 +4416,7 @@ var init_store = __esm({
4205
4416
  init_keychain();
4206
4417
  init_config();
4207
4418
  init_state_bus();
4419
+ init_memory_write_governor();
4208
4420
  INIT_MAX_RETRIES = 3;
4209
4421
  INIT_RETRY_DELAY_MS = 1e3;
4210
4422
  _pendingRecords = [];
@@ -2801,6 +2801,14 @@ async function ensureSchema() {
2801
2801
  );
2802
2802
  } catch {
2803
2803
  }
2804
+ try {
2805
+ await client.execute(
2806
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2807
+ ON memories(content_hash, agent_id, project_name, memory_type)
2808
+ WHERE content_hash IS NOT NULL`
2809
+ );
2810
+ } catch {
2811
+ }
2804
2812
  await client.executeMultiple(`
2805
2813
  CREATE TABLE IF NOT EXISTS entities (
2806
2814
  id TEXT PRIMARY KEY,
@@ -6316,7 +6324,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6316
6324
  }
6317
6325
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
6318
6326
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
6319
- envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
6327
+ let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
6328
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
6329
+ ccModel += "[1m]";
6330
+ }
6331
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
6320
6332
  }
6321
6333
  }
6322
6334
  let spawnCommand;
@@ -6663,6 +6675,197 @@ var init_keychain = __esm({
6663
6675
  }
6664
6676
  });
6665
6677
 
6678
+ // src/lib/memory-write-governor.ts
6679
+ import { createHash } from "crypto";
6680
+ function normalizeMemoryText(text) {
6681
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
6682
+ }
6683
+ function classifyMemoryType(input) {
6684
+ if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
6685
+ const tool = input.tool_name.toLowerCase();
6686
+ const text = input.raw_text.toLowerCase();
6687
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
6688
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
6689
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
6690
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
6691
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
6692
+ if (tool === "store_memory" || tool === "manual") return "observation";
6693
+ return "raw";
6694
+ }
6695
+ function shouldDropMemory(text) {
6696
+ const normalized = normalizeMemoryText(text);
6697
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
6698
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
6699
+ return { drop: true, reason: "known_boilerplate_noise" };
6700
+ }
6701
+ return { drop: false };
6702
+ }
6703
+ function shouldSkipEmbedding(input) {
6704
+ const type = classifyMemoryType(input);
6705
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
6706
+ if (type === "raw" && input.raw_text.length > 2e4) return true;
6707
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
6708
+ return false;
6709
+ }
6710
+ function hashMemoryContent(text) {
6711
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
6712
+ }
6713
+ function scopedDedupArgs(input) {
6714
+ return [input.contentHash, input.agentId, input.projectName, input.memoryType];
6715
+ }
6716
+ function governMemoryRecord(record) {
6717
+ const normalized = normalizeMemoryText(record.raw_text);
6718
+ const memoryType = classifyMemoryType({
6719
+ raw_text: normalized,
6720
+ agent_id: record.agent_id,
6721
+ project_name: record.project_name,
6722
+ tool_name: record.tool_name,
6723
+ memory_type: record.memory_type
6724
+ });
6725
+ const drop = shouldDropMemory(normalized);
6726
+ const skipEmbedding = shouldSkipEmbedding({
6727
+ raw_text: normalized,
6728
+ agent_id: record.agent_id,
6729
+ project_name: record.project_name,
6730
+ tool_name: record.tool_name,
6731
+ memory_type: memoryType
6732
+ });
6733
+ return {
6734
+ record: {
6735
+ ...record,
6736
+ raw_text: normalized,
6737
+ memory_type: memoryType,
6738
+ vector: skipEmbedding ? null : record.vector
6739
+ },
6740
+ contentHash: hashMemoryContent(normalized),
6741
+ shouldDrop: drop.drop,
6742
+ dropReason: drop.reason,
6743
+ skipEmbedding,
6744
+ hygiene: {
6745
+ dedup: true,
6746
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
6747
+ }
6748
+ };
6749
+ }
6750
+ async function findScopedDuplicate(input) {
6751
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6752
+ const client = getClient2();
6753
+ const args2 = scopedDedupArgs(input);
6754
+ let sql = `SELECT id FROM memories
6755
+ WHERE content_hash = ?
6756
+ AND agent_id = ?
6757
+ AND project_name = ?
6758
+ AND COALESCE(memory_type, 'raw') = ?
6759
+ AND COALESCE(status, 'active') != 'deleted'`;
6760
+ if (input.excludeId) {
6761
+ sql += " AND id != ?";
6762
+ args2.push(input.excludeId);
6763
+ }
6764
+ sql += " ORDER BY timestamp DESC LIMIT 1";
6765
+ const result = await client.execute({ sql, args: args2 });
6766
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
6767
+ }
6768
+ async function runPostWriteMemoryHygiene(memoryId) {
6769
+ try {
6770
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6771
+ const client = getClient2();
6772
+ const current = await client.execute({
6773
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
6774
+ importance, timestamp
6775
+ FROM memories
6776
+ WHERE id = ?
6777
+ LIMIT 1`,
6778
+ args: [memoryId]
6779
+ });
6780
+ const row = current.rows[0];
6781
+ if (!row) return;
6782
+ const memoryType = String(row.memory_type ?? "raw");
6783
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
6784
+ const agentId = String(row.agent_id);
6785
+ const projectName2 = String(row.project_name);
6786
+ if (contentHash) {
6787
+ await client.execute({
6788
+ sql: `UPDATE memories
6789
+ SET status = 'deleted',
6790
+ outcome = COALESCE(outcome, 'superseded')
6791
+ WHERE id != ?
6792
+ AND content_hash = ?
6793
+ AND agent_id = ?
6794
+ AND project_name = ?
6795
+ AND COALESCE(memory_type, 'raw') = ?
6796
+ AND COALESCE(status, 'active') = 'active'`,
6797
+ args: [memoryId, contentHash, agentId, projectName2, memoryType]
6798
+ });
6799
+ }
6800
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
6801
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
6802
+ const old = await client.execute({
6803
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
6804
+ args: [supersedesId]
6805
+ });
6806
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
6807
+ const newImportance = Number(row.importance ?? 0);
6808
+ await client.batch([
6809
+ {
6810
+ sql: `UPDATE memories
6811
+ SET status = 'archived',
6812
+ outcome = COALESCE(outcome, 'superseded')
6813
+ WHERE id = ?`,
6814
+ args: [supersedesId]
6815
+ },
6816
+ {
6817
+ sql: `UPDATE memories
6818
+ SET importance = MAX(COALESCE(importance, 5), ?),
6819
+ parent_memory_id = COALESCE(parent_memory_id, ?)
6820
+ WHERE id = ?`,
6821
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
6822
+ }
6823
+ ], "write");
6824
+ }
6825
+ } catch (err) {
6826
+ process.stderr.write(
6827
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
6828
+ `
6829
+ );
6830
+ }
6831
+ }
6832
+ function schedulePostWriteMemoryHygiene(memoryIds) {
6833
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
6834
+ if (memoryIds.length === 0) return;
6835
+ const run = () => {
6836
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
6837
+ };
6838
+ if (typeof setImmediate === "function") setImmediate(run);
6839
+ else setTimeout(run, 0);
6840
+ }
6841
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
6842
+ var init_memory_write_governor = __esm({
6843
+ "src/lib/memory-write-governor.ts"() {
6844
+ "use strict";
6845
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
6846
+ "decision",
6847
+ "adr",
6848
+ "behavior",
6849
+ "procedure"
6850
+ ]);
6851
+ NOISE_DROP_PATTERNS = [
6852
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
6853
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
6854
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
6855
+ /^\s*Intercom is a speedup, not delivery/im,
6856
+ /^\s*Context bar reads as USAGE not remaining/im
6857
+ ];
6858
+ SKIP_EMBED_PATTERNS = [
6859
+ /tmux capture-pane\b/i,
6860
+ /docker ps\b/i,
6861
+ /docker images\b/i,
6862
+ /git status\b/i,
6863
+ /grep .*node_modules/i,
6864
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
6865
+ ];
6866
+ }
6867
+ });
6868
+
6666
6869
  // src/lib/shard-manager.ts
6667
6870
  var shard_manager_exports = {};
6668
6871
  __export(shard_manager_exports, {
@@ -6827,7 +7030,8 @@ async function ensureShardSchema(client) {
6827
7030
  }
6828
7031
  for (const idx of [
6829
7032
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
6830
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
7033
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
7034
+ "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"
6831
7035
  ]) {
6832
7036
  try {
6833
7037
  await client.execute(idx);
@@ -7021,7 +7225,7 @@ var init_platform_procedures = __esm({
7021
7225
  title: "Chain of command \u2014 who talks to whom",
7022
7226
  domain: "workflow",
7023
7227
  priority: "p0",
7024
- content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the COO does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
7228
+ content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
7025
7229
  },
7026
7230
  {
7027
7231
  title: "Single dispatch path \u2014 create_task only",
@@ -7252,7 +7456,6 @@ __export(store_exports, {
7252
7456
  vectorToBlob: () => vectorToBlob,
7253
7457
  writeMemory: () => writeMemory
7254
7458
  });
7255
- import { createHash } from "crypto";
7256
7459
  function isBusyError2(err) {
7257
7460
  if (err instanceof Error) {
7258
7461
  const msg = err.message.toLowerCase();
@@ -7366,17 +7569,24 @@ async function writeMemory(record) {
7366
7569
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
7367
7570
  );
7368
7571
  }
7369
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
7370
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
7572
+ const governed = governMemoryRecord(record);
7573
+ if (governed.shouldDrop) return;
7574
+ record = governed.record;
7575
+ const contentHash = governed.contentHash;
7576
+ const memoryType = record.memory_type ?? "raw";
7577
+ if (_pendingRecords.some(
7578
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
7579
+ )) {
7371
7580
  return;
7372
7581
  }
7373
7582
  try {
7374
- const client = getClient();
7375
- const existing = await client.execute({
7376
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
7377
- args: [contentHash, record.agent_id]
7583
+ const existing = await findScopedDuplicate({
7584
+ contentHash,
7585
+ agentId: record.agent_id,
7586
+ projectName: record.project_name,
7587
+ memoryType
7378
7588
  });
7379
- if (existing.rows.length > 0) return;
7589
+ if (existing) return;
7380
7590
  } catch {
7381
7591
  }
7382
7592
  const dbRow = {
@@ -7407,7 +7617,7 @@ async function writeMemory(record) {
7407
7617
  tier: record.tier ?? classifyTier(record),
7408
7618
  supersedes_id: record.supersedes_id ?? null,
7409
7619
  draft: record.draft ? 1 : 0,
7410
- memory_type: record.memory_type ?? "raw",
7620
+ memory_type: memoryType,
7411
7621
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
7412
7622
  content_hash: contentHash,
7413
7623
  intent: record.intent ?? null,
@@ -7566,6 +7776,7 @@ async function flushBatch() {
7566
7776
  const globalClient = getClient();
7567
7777
  const globalStmts = batch.map(buildStmt);
7568
7778
  await globalClient.batch(globalStmts, "write");
7779
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
7569
7780
  _pendingRecords.splice(0, batch.length);
7570
7781
  try {
7571
7782
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -7686,7 +7897,11 @@ async function searchMemories(queryVector, agentId, options) {
7686
7897
  sql += ` AND timestamp >= ?`;
7687
7898
  args2.push(options.since);
7688
7899
  }
7689
- if (options?.memoryType) {
7900
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
7901
+ const uniqueTypes = [...new Set(options.memoryTypes)];
7902
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
7903
+ args2.push(...uniqueTypes);
7904
+ } else if (options?.memoryType) {
7690
7905
  sql += ` AND memory_type = ?`;
7691
7906
  args2.push(options.memoryType);
7692
7907
  }
@@ -7824,6 +8039,7 @@ var init_store = __esm({
7824
8039
  init_keychain();
7825
8040
  init_config();
7826
8041
  init_state_bus();
8042
+ init_memory_write_governor();
7827
8043
  INIT_MAX_RETRIES = 3;
7828
8044
  INIT_RETRY_DELAY_MS = 1e3;
7829
8045
  _pendingRecords = [];