@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
@@ -2387,6 +2387,14 @@ async function ensureSchema() {
2387
2387
  );
2388
2388
  } catch {
2389
2389
  }
2390
+ try {
2391
+ await client.execute(
2392
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2393
+ ON memories(content_hash, agent_id, project_name, memory_type)
2394
+ WHERE content_hash IS NOT NULL`
2395
+ );
2396
+ } catch {
2397
+ }
2390
2398
  await client.executeMultiple(`
2391
2399
  CREATE TABLE IF NOT EXISTS entities (
2392
2400
  id TEXT PRIMARY KEY,
@@ -3132,6 +3140,197 @@ var init_state_bus = __esm({
3132
3140
  }
3133
3141
  });
3134
3142
 
3143
+ // src/lib/memory-write-governor.ts
3144
+ import { createHash } from "crypto";
3145
+ function normalizeMemoryText(text) {
3146
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
3147
+ }
3148
+ function classifyMemoryType(input) {
3149
+ if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
3150
+ const tool = input.tool_name.toLowerCase();
3151
+ const text = input.raw_text.toLowerCase();
3152
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
3153
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
3154
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
3155
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
3156
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
3157
+ if (tool === "store_memory" || tool === "manual") return "observation";
3158
+ return "raw";
3159
+ }
3160
+ function shouldDropMemory(text) {
3161
+ const normalized = normalizeMemoryText(text);
3162
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
3163
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
3164
+ return { drop: true, reason: "known_boilerplate_noise" };
3165
+ }
3166
+ return { drop: false };
3167
+ }
3168
+ function shouldSkipEmbedding(input) {
3169
+ const type = classifyMemoryType(input);
3170
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
3171
+ if (type === "raw" && input.raw_text.length > 2e4) return true;
3172
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
3173
+ return false;
3174
+ }
3175
+ function hashMemoryContent(text) {
3176
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
3177
+ }
3178
+ function scopedDedupArgs(input) {
3179
+ return [input.contentHash, input.agentId, input.projectName, input.memoryType];
3180
+ }
3181
+ function governMemoryRecord(record) {
3182
+ const normalized = normalizeMemoryText(record.raw_text);
3183
+ const memoryType = classifyMemoryType({
3184
+ raw_text: normalized,
3185
+ agent_id: record.agent_id,
3186
+ project_name: record.project_name,
3187
+ tool_name: record.tool_name,
3188
+ memory_type: record.memory_type
3189
+ });
3190
+ const drop = shouldDropMemory(normalized);
3191
+ const skipEmbedding = shouldSkipEmbedding({
3192
+ raw_text: normalized,
3193
+ agent_id: record.agent_id,
3194
+ project_name: record.project_name,
3195
+ tool_name: record.tool_name,
3196
+ memory_type: memoryType
3197
+ });
3198
+ return {
3199
+ record: {
3200
+ ...record,
3201
+ raw_text: normalized,
3202
+ memory_type: memoryType,
3203
+ vector: skipEmbedding ? null : record.vector
3204
+ },
3205
+ contentHash: hashMemoryContent(normalized),
3206
+ shouldDrop: drop.drop,
3207
+ dropReason: drop.reason,
3208
+ skipEmbedding,
3209
+ hygiene: {
3210
+ dedup: true,
3211
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
3212
+ }
3213
+ };
3214
+ }
3215
+ async function findScopedDuplicate(input) {
3216
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3217
+ const client = getClient2();
3218
+ const args = scopedDedupArgs(input);
3219
+ let sql = `SELECT id FROM memories
3220
+ WHERE content_hash = ?
3221
+ AND agent_id = ?
3222
+ AND project_name = ?
3223
+ AND COALESCE(memory_type, 'raw') = ?
3224
+ AND COALESCE(status, 'active') != 'deleted'`;
3225
+ if (input.excludeId) {
3226
+ sql += " AND id != ?";
3227
+ args.push(input.excludeId);
3228
+ }
3229
+ sql += " ORDER BY timestamp DESC LIMIT 1";
3230
+ const result = await client.execute({ sql, args });
3231
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
3232
+ }
3233
+ async function runPostWriteMemoryHygiene(memoryId) {
3234
+ try {
3235
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3236
+ const client = getClient2();
3237
+ const current = await client.execute({
3238
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
3239
+ importance, timestamp
3240
+ FROM memories
3241
+ WHERE id = ?
3242
+ LIMIT 1`,
3243
+ args: [memoryId]
3244
+ });
3245
+ const row = current.rows[0];
3246
+ if (!row) return;
3247
+ const memoryType = String(row.memory_type ?? "raw");
3248
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
3249
+ const agentId = String(row.agent_id);
3250
+ const projectName = String(row.project_name);
3251
+ if (contentHash) {
3252
+ await client.execute({
3253
+ sql: `UPDATE memories
3254
+ SET status = 'deleted',
3255
+ outcome = COALESCE(outcome, 'superseded')
3256
+ WHERE id != ?
3257
+ AND content_hash = ?
3258
+ AND agent_id = ?
3259
+ AND project_name = ?
3260
+ AND COALESCE(memory_type, 'raw') = ?
3261
+ AND COALESCE(status, 'active') = 'active'`,
3262
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
3263
+ });
3264
+ }
3265
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
3266
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
3267
+ const old = await client.execute({
3268
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
3269
+ args: [supersedesId]
3270
+ });
3271
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
3272
+ const newImportance = Number(row.importance ?? 0);
3273
+ await client.batch([
3274
+ {
3275
+ sql: `UPDATE memories
3276
+ SET status = 'archived',
3277
+ outcome = COALESCE(outcome, 'superseded')
3278
+ WHERE id = ?`,
3279
+ args: [supersedesId]
3280
+ },
3281
+ {
3282
+ sql: `UPDATE memories
3283
+ SET importance = MAX(COALESCE(importance, 5), ?),
3284
+ parent_memory_id = COALESCE(parent_memory_id, ?)
3285
+ WHERE id = ?`,
3286
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
3287
+ }
3288
+ ], "write");
3289
+ }
3290
+ } catch (err) {
3291
+ process.stderr.write(
3292
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
3293
+ `
3294
+ );
3295
+ }
3296
+ }
3297
+ function schedulePostWriteMemoryHygiene(memoryIds) {
3298
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
3299
+ if (memoryIds.length === 0) return;
3300
+ const run = () => {
3301
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
3302
+ };
3303
+ if (typeof setImmediate === "function") setImmediate(run);
3304
+ else setTimeout(run, 0);
3305
+ }
3306
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
3307
+ var init_memory_write_governor = __esm({
3308
+ "src/lib/memory-write-governor.ts"() {
3309
+ "use strict";
3310
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
3311
+ "decision",
3312
+ "adr",
3313
+ "behavior",
3314
+ "procedure"
3315
+ ]);
3316
+ NOISE_DROP_PATTERNS = [
3317
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
3318
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
3319
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
3320
+ /^\s*Intercom is a speedup, not delivery/im,
3321
+ /^\s*Context bar reads as USAGE not remaining/im
3322
+ ];
3323
+ SKIP_EMBED_PATTERNS = [
3324
+ /tmux capture-pane\b/i,
3325
+ /docker ps\b/i,
3326
+ /docker images\b/i,
3327
+ /git status\b/i,
3328
+ /grep .*node_modules/i,
3329
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
3330
+ ];
3331
+ }
3332
+ });
3333
+
3135
3334
  // src/lib/shard-manager.ts
3136
3335
  var shard_manager_exports = {};
3137
3336
  __export(shard_manager_exports, {
@@ -3296,7 +3495,8 @@ async function ensureShardSchema(client) {
3296
3495
  }
3297
3496
  for (const idx of [
3298
3497
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
3299
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
3498
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
3499
+ "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"
3300
3500
  ]) {
3301
3501
  try {
3302
3502
  await client.execute(idx);
@@ -3490,7 +3690,7 @@ var init_platform_procedures = __esm({
3490
3690
  title: "Chain of command \u2014 who talks to whom",
3491
3691
  domain: "workflow",
3492
3692
  priority: "p0",
3493
- 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."
3693
+ 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."
3494
3694
  },
3495
3695
  {
3496
3696
  title: "Single dispatch path \u2014 create_task only",
@@ -3721,7 +3921,6 @@ __export(store_exports, {
3721
3921
  vectorToBlob: () => vectorToBlob,
3722
3922
  writeMemory: () => writeMemory
3723
3923
  });
3724
- import { createHash } from "crypto";
3725
3924
  function isBusyError2(err) {
3726
3925
  if (err instanceof Error) {
3727
3926
  const msg = err.message.toLowerCase();
@@ -3835,17 +4034,24 @@ async function writeMemory(record) {
3835
4034
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
3836
4035
  );
3837
4036
  }
3838
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
3839
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
4037
+ const governed = governMemoryRecord(record);
4038
+ if (governed.shouldDrop) return;
4039
+ record = governed.record;
4040
+ const contentHash = governed.contentHash;
4041
+ const memoryType = record.memory_type ?? "raw";
4042
+ if (_pendingRecords.some(
4043
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
4044
+ )) {
3840
4045
  return;
3841
4046
  }
3842
4047
  try {
3843
- const client = getClient();
3844
- const existing = await client.execute({
3845
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
3846
- args: [contentHash, record.agent_id]
4048
+ const existing = await findScopedDuplicate({
4049
+ contentHash,
4050
+ agentId: record.agent_id,
4051
+ projectName: record.project_name,
4052
+ memoryType
3847
4053
  });
3848
- if (existing.rows.length > 0) return;
4054
+ if (existing) return;
3849
4055
  } catch {
3850
4056
  }
3851
4057
  const dbRow = {
@@ -3876,7 +4082,7 @@ async function writeMemory(record) {
3876
4082
  tier: record.tier ?? classifyTier(record),
3877
4083
  supersedes_id: record.supersedes_id ?? null,
3878
4084
  draft: record.draft ? 1 : 0,
3879
- memory_type: record.memory_type ?? "raw",
4085
+ memory_type: memoryType,
3880
4086
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
3881
4087
  content_hash: contentHash,
3882
4088
  intent: record.intent ?? null,
@@ -4035,6 +4241,7 @@ async function flushBatch() {
4035
4241
  const globalClient = getClient();
4036
4242
  const globalStmts = batch.map(buildStmt);
4037
4243
  await globalClient.batch(globalStmts, "write");
4244
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4038
4245
  _pendingRecords.splice(0, batch.length);
4039
4246
  try {
4040
4247
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -4155,7 +4362,11 @@ async function searchMemories(queryVector, agentId, options) {
4155
4362
  sql += ` AND timestamp >= ?`;
4156
4363
  args.push(options.since);
4157
4364
  }
4158
- if (options?.memoryType) {
4365
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
4366
+ const uniqueTypes = [...new Set(options.memoryTypes)];
4367
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
4368
+ args.push(...uniqueTypes);
4369
+ } else if (options?.memoryType) {
4159
4370
  sql += ` AND memory_type = ?`;
4160
4371
  args.push(options.memoryType);
4161
4372
  }
@@ -4293,6 +4504,7 @@ var init_store = __esm({
4293
4504
  init_keychain();
4294
4505
  init_config();
4295
4506
  init_state_bus();
4507
+ init_memory_write_governor();
4296
4508
  INIT_MAX_RETRIES = 3;
4297
4509
  INIT_RETRY_DELAY_MS = 1e3;
4298
4510
  _pendingRecords = [];
@@ -7451,7 +7663,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7451
7663
  }
7452
7664
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
7453
7665
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
7454
- envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
7666
+ let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
7667
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
7668
+ ccModel += "[1m]";
7669
+ }
7670
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
7455
7671
  }
7456
7672
  }
7457
7673
  let spawnCommand;
@@ -2879,6 +2879,14 @@ async function ensureSchema() {
2879
2879
  );
2880
2880
  } catch {
2881
2881
  }
2882
+ try {
2883
+ await client.execute(
2884
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2885
+ ON memories(content_hash, agent_id, project_name, memory_type)
2886
+ WHERE content_hash IS NOT NULL`
2887
+ );
2888
+ } catch {
2889
+ }
2882
2890
  await client.executeMultiple(`
2883
2891
  CREATE TABLE IF NOT EXISTS entities (
2884
2892
  id TEXT PRIMARY KEY,
@@ -6387,7 +6395,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6387
6395
  }
6388
6396
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
6389
6397
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
6390
- envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
6398
+ let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
6399
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
6400
+ ccModel += "[1m]";
6401
+ }
6402
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
6391
6403
  }
6392
6404
  }
6393
6405
  let spawnCommand;
@@ -6734,6 +6746,197 @@ var init_keychain = __esm({
6734
6746
  }
6735
6747
  });
6736
6748
 
6749
+ // src/lib/memory-write-governor.ts
6750
+ import { createHash } from "crypto";
6751
+ function normalizeMemoryText(text) {
6752
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
6753
+ }
6754
+ function classifyMemoryType(input) {
6755
+ if (input.memory_type && input.memory_type.trim()) return input.memory_type.trim();
6756
+ const tool = input.tool_name.toLowerCase();
6757
+ const text = input.raw_text.toLowerCase();
6758
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
6759
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
6760
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
6761
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
6762
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
6763
+ if (tool === "store_memory" || tool === "manual") return "observation";
6764
+ return "raw";
6765
+ }
6766
+ function shouldDropMemory(text) {
6767
+ const normalized = normalizeMemoryText(text);
6768
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
6769
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
6770
+ return { drop: true, reason: "known_boilerplate_noise" };
6771
+ }
6772
+ return { drop: false };
6773
+ }
6774
+ function shouldSkipEmbedding(input) {
6775
+ const type = classifyMemoryType(input);
6776
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
6777
+ if (type === "raw" && input.raw_text.length > 2e4) return true;
6778
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input.raw_text))) return true;
6779
+ return false;
6780
+ }
6781
+ function hashMemoryContent(text) {
6782
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
6783
+ }
6784
+ function scopedDedupArgs(input) {
6785
+ return [input.contentHash, input.agentId, input.projectName, input.memoryType];
6786
+ }
6787
+ function governMemoryRecord(record) {
6788
+ const normalized = normalizeMemoryText(record.raw_text);
6789
+ const memoryType = classifyMemoryType({
6790
+ raw_text: normalized,
6791
+ agent_id: record.agent_id,
6792
+ project_name: record.project_name,
6793
+ tool_name: record.tool_name,
6794
+ memory_type: record.memory_type
6795
+ });
6796
+ const drop = shouldDropMemory(normalized);
6797
+ const skipEmbedding = shouldSkipEmbedding({
6798
+ raw_text: normalized,
6799
+ agent_id: record.agent_id,
6800
+ project_name: record.project_name,
6801
+ tool_name: record.tool_name,
6802
+ memory_type: memoryType
6803
+ });
6804
+ return {
6805
+ record: {
6806
+ ...record,
6807
+ raw_text: normalized,
6808
+ memory_type: memoryType,
6809
+ vector: skipEmbedding ? null : record.vector
6810
+ },
6811
+ contentHash: hashMemoryContent(normalized),
6812
+ shouldDrop: drop.drop,
6813
+ dropReason: drop.reason,
6814
+ skipEmbedding,
6815
+ hygiene: {
6816
+ dedup: true,
6817
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
6818
+ }
6819
+ };
6820
+ }
6821
+ async function findScopedDuplicate(input) {
6822
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6823
+ const client = getClient2();
6824
+ const args = scopedDedupArgs(input);
6825
+ let sql = `SELECT id FROM memories
6826
+ WHERE content_hash = ?
6827
+ AND agent_id = ?
6828
+ AND project_name = ?
6829
+ AND COALESCE(memory_type, 'raw') = ?
6830
+ AND COALESCE(status, 'active') != 'deleted'`;
6831
+ if (input.excludeId) {
6832
+ sql += " AND id != ?";
6833
+ args.push(input.excludeId);
6834
+ }
6835
+ sql += " ORDER BY timestamp DESC LIMIT 1";
6836
+ const result = await client.execute({ sql, args });
6837
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
6838
+ }
6839
+ async function runPostWriteMemoryHygiene(memoryId) {
6840
+ try {
6841
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6842
+ const client = getClient2();
6843
+ const current = await client.execute({
6844
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
6845
+ importance, timestamp
6846
+ FROM memories
6847
+ WHERE id = ?
6848
+ LIMIT 1`,
6849
+ args: [memoryId]
6850
+ });
6851
+ const row = current.rows[0];
6852
+ if (!row) return;
6853
+ const memoryType = String(row.memory_type ?? "raw");
6854
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
6855
+ const agentId = String(row.agent_id);
6856
+ const projectName = String(row.project_name);
6857
+ if (contentHash) {
6858
+ await client.execute({
6859
+ sql: `UPDATE memories
6860
+ SET status = 'deleted',
6861
+ outcome = COALESCE(outcome, 'superseded')
6862
+ WHERE id != ?
6863
+ AND content_hash = ?
6864
+ AND agent_id = ?
6865
+ AND project_name = ?
6866
+ AND COALESCE(memory_type, 'raw') = ?
6867
+ AND COALESCE(status, 'active') = 'active'`,
6868
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
6869
+ });
6870
+ }
6871
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
6872
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
6873
+ const old = await client.execute({
6874
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
6875
+ args: [supersedesId]
6876
+ });
6877
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
6878
+ const newImportance = Number(row.importance ?? 0);
6879
+ await client.batch([
6880
+ {
6881
+ sql: `UPDATE memories
6882
+ SET status = 'archived',
6883
+ outcome = COALESCE(outcome, 'superseded')
6884
+ WHERE id = ?`,
6885
+ args: [supersedesId]
6886
+ },
6887
+ {
6888
+ sql: `UPDATE memories
6889
+ SET importance = MAX(COALESCE(importance, 5), ?),
6890
+ parent_memory_id = COALESCE(parent_memory_id, ?)
6891
+ WHERE id = ?`,
6892
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
6893
+ }
6894
+ ], "write");
6895
+ }
6896
+ } catch (err) {
6897
+ process.stderr.write(
6898
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
6899
+ `
6900
+ );
6901
+ }
6902
+ }
6903
+ function schedulePostWriteMemoryHygiene(memoryIds) {
6904
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
6905
+ if (memoryIds.length === 0) return;
6906
+ const run = () => {
6907
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
6908
+ };
6909
+ if (typeof setImmediate === "function") setImmediate(run);
6910
+ else setTimeout(run, 0);
6911
+ }
6912
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
6913
+ var init_memory_write_governor = __esm({
6914
+ "src/lib/memory-write-governor.ts"() {
6915
+ "use strict";
6916
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
6917
+ "decision",
6918
+ "adr",
6919
+ "behavior",
6920
+ "procedure"
6921
+ ]);
6922
+ NOISE_DROP_PATTERNS = [
6923
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
6924
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
6925
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
6926
+ /^\s*Intercom is a speedup, not delivery/im,
6927
+ /^\s*Context bar reads as USAGE not remaining/im
6928
+ ];
6929
+ SKIP_EMBED_PATTERNS = [
6930
+ /tmux capture-pane\b/i,
6931
+ /docker ps\b/i,
6932
+ /docker images\b/i,
6933
+ /git status\b/i,
6934
+ /grep .*node_modules/i,
6935
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
6936
+ ];
6937
+ }
6938
+ });
6939
+
6737
6940
  // src/lib/shard-manager.ts
6738
6941
  var shard_manager_exports = {};
6739
6942
  __export(shard_manager_exports, {
@@ -6898,7 +7101,8 @@ async function ensureShardSchema(client) {
6898
7101
  }
6899
7102
  for (const idx of [
6900
7103
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
6901
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
7104
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
7105
+ "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"
6902
7106
  ]) {
6903
7107
  try {
6904
7108
  await client.execute(idx);
@@ -7092,7 +7296,7 @@ var init_platform_procedures = __esm({
7092
7296
  title: "Chain of command \u2014 who talks to whom",
7093
7297
  domain: "workflow",
7094
7298
  priority: "p0",
7095
- 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."
7299
+ 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."
7096
7300
  },
7097
7301
  {
7098
7302
  title: "Single dispatch path \u2014 create_task only",
@@ -7323,7 +7527,6 @@ __export(store_exports, {
7323
7527
  vectorToBlob: () => vectorToBlob,
7324
7528
  writeMemory: () => writeMemory
7325
7529
  });
7326
- import { createHash } from "crypto";
7327
7530
  function isBusyError2(err) {
7328
7531
  if (err instanceof Error) {
7329
7532
  const msg = err.message.toLowerCase();
@@ -7437,17 +7640,24 @@ async function writeMemory(record) {
7437
7640
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
7438
7641
  );
7439
7642
  }
7440
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
7441
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
7643
+ const governed = governMemoryRecord(record);
7644
+ if (governed.shouldDrop) return;
7645
+ record = governed.record;
7646
+ const contentHash = governed.contentHash;
7647
+ const memoryType = record.memory_type ?? "raw";
7648
+ if (_pendingRecords.some(
7649
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
7650
+ )) {
7442
7651
  return;
7443
7652
  }
7444
7653
  try {
7445
- const client = getClient();
7446
- const existing = await client.execute({
7447
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
7448
- args: [contentHash, record.agent_id]
7654
+ const existing = await findScopedDuplicate({
7655
+ contentHash,
7656
+ agentId: record.agent_id,
7657
+ projectName: record.project_name,
7658
+ memoryType
7449
7659
  });
7450
- if (existing.rows.length > 0) return;
7660
+ if (existing) return;
7451
7661
  } catch {
7452
7662
  }
7453
7663
  const dbRow = {
@@ -7478,7 +7688,7 @@ async function writeMemory(record) {
7478
7688
  tier: record.tier ?? classifyTier(record),
7479
7689
  supersedes_id: record.supersedes_id ?? null,
7480
7690
  draft: record.draft ? 1 : 0,
7481
- memory_type: record.memory_type ?? "raw",
7691
+ memory_type: memoryType,
7482
7692
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
7483
7693
  content_hash: contentHash,
7484
7694
  intent: record.intent ?? null,
@@ -7637,6 +7847,7 @@ async function flushBatch() {
7637
7847
  const globalClient = getClient();
7638
7848
  const globalStmts = batch.map(buildStmt);
7639
7849
  await globalClient.batch(globalStmts, "write");
7850
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
7640
7851
  _pendingRecords.splice(0, batch.length);
7641
7852
  try {
7642
7853
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -7757,7 +7968,11 @@ async function searchMemories(queryVector, agentId, options) {
7757
7968
  sql += ` AND timestamp >= ?`;
7758
7969
  args.push(options.since);
7759
7970
  }
7760
- if (options?.memoryType) {
7971
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
7972
+ const uniqueTypes = [...new Set(options.memoryTypes)];
7973
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
7974
+ args.push(...uniqueTypes);
7975
+ } else if (options?.memoryType) {
7761
7976
  sql += ` AND memory_type = ?`;
7762
7977
  args.push(options.memoryType);
7763
7978
  }
@@ -7895,6 +8110,7 @@ var init_store = __esm({
7895
8110
  init_keychain();
7896
8111
  init_config();
7897
8112
  init_state_bus();
8113
+ init_memory_write_governor();
7898
8114
  INIT_MAX_RETRIES = 3;
7899
8115
  INIT_RETRY_DELAY_MS = 1e3;
7900
8116
  _pendingRecords = [];