@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
@@ -2866,6 +2866,14 @@ async function ensureSchema() {
2866
2866
  );
2867
2867
  } catch {
2868
2868
  }
2869
+ try {
2870
+ await client.execute(
2871
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2872
+ ON memories(content_hash, agent_id, project_name, memory_type)
2873
+ WHERE content_hash IS NOT NULL`
2874
+ );
2875
+ } catch {
2876
+ }
2869
2877
  await client.executeMultiple(`
2870
2878
  CREATE TABLE IF NOT EXISTS entities (
2871
2879
  id TEXT PRIMARY KEY,
@@ -6365,7 +6373,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6365
6373
  }
6366
6374
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
6367
6375
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
6368
- envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
6376
+ let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
6377
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
6378
+ ccModel += "[1m]";
6379
+ }
6380
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
6369
6381
  }
6370
6382
  }
6371
6383
  let spawnCommand;
@@ -6712,6 +6724,197 @@ var init_keychain = __esm({
6712
6724
  }
6713
6725
  });
6714
6726
 
6727
+ // src/lib/memory-write-governor.ts
6728
+ import { createHash } from "crypto";
6729
+ function normalizeMemoryText(text) {
6730
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
6731
+ }
6732
+ function classifyMemoryType(input2) {
6733
+ if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
6734
+ const tool = input2.tool_name.toLowerCase();
6735
+ const text = input2.raw_text.toLowerCase();
6736
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
6737
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
6738
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
6739
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
6740
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
6741
+ if (tool === "store_memory" || tool === "manual") return "observation";
6742
+ return "raw";
6743
+ }
6744
+ function shouldDropMemory(text) {
6745
+ const normalized = normalizeMemoryText(text);
6746
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
6747
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
6748
+ return { drop: true, reason: "known_boilerplate_noise" };
6749
+ }
6750
+ return { drop: false };
6751
+ }
6752
+ function shouldSkipEmbedding(input2) {
6753
+ const type = classifyMemoryType(input2);
6754
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
6755
+ if (type === "raw" && input2.raw_text.length > 2e4) return true;
6756
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
6757
+ return false;
6758
+ }
6759
+ function hashMemoryContent(text) {
6760
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
6761
+ }
6762
+ function scopedDedupArgs(input2) {
6763
+ return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
6764
+ }
6765
+ function governMemoryRecord(record) {
6766
+ const normalized = normalizeMemoryText(record.raw_text);
6767
+ const memoryType = classifyMemoryType({
6768
+ raw_text: normalized,
6769
+ agent_id: record.agent_id,
6770
+ project_name: record.project_name,
6771
+ tool_name: record.tool_name,
6772
+ memory_type: record.memory_type
6773
+ });
6774
+ const drop = shouldDropMemory(normalized);
6775
+ const skipEmbedding = shouldSkipEmbedding({
6776
+ raw_text: normalized,
6777
+ agent_id: record.agent_id,
6778
+ project_name: record.project_name,
6779
+ tool_name: record.tool_name,
6780
+ memory_type: memoryType
6781
+ });
6782
+ return {
6783
+ record: {
6784
+ ...record,
6785
+ raw_text: normalized,
6786
+ memory_type: memoryType,
6787
+ vector: skipEmbedding ? null : record.vector
6788
+ },
6789
+ contentHash: hashMemoryContent(normalized),
6790
+ shouldDrop: drop.drop,
6791
+ dropReason: drop.reason,
6792
+ skipEmbedding,
6793
+ hygiene: {
6794
+ dedup: true,
6795
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
6796
+ }
6797
+ };
6798
+ }
6799
+ async function findScopedDuplicate(input2) {
6800
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6801
+ const client = getClient2();
6802
+ const args = scopedDedupArgs(input2);
6803
+ let sql = `SELECT id FROM memories
6804
+ WHERE content_hash = ?
6805
+ AND agent_id = ?
6806
+ AND project_name = ?
6807
+ AND COALESCE(memory_type, 'raw') = ?
6808
+ AND COALESCE(status, 'active') != 'deleted'`;
6809
+ if (input2.excludeId) {
6810
+ sql += " AND id != ?";
6811
+ args.push(input2.excludeId);
6812
+ }
6813
+ sql += " ORDER BY timestamp DESC LIMIT 1";
6814
+ const result = await client.execute({ sql, args });
6815
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
6816
+ }
6817
+ async function runPostWriteMemoryHygiene(memoryId) {
6818
+ try {
6819
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6820
+ const client = getClient2();
6821
+ const current = await client.execute({
6822
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
6823
+ importance, timestamp
6824
+ FROM memories
6825
+ WHERE id = ?
6826
+ LIMIT 1`,
6827
+ args: [memoryId]
6828
+ });
6829
+ const row = current.rows[0];
6830
+ if (!row) return;
6831
+ const memoryType = String(row.memory_type ?? "raw");
6832
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
6833
+ const agentId = String(row.agent_id);
6834
+ const projectName = String(row.project_name);
6835
+ if (contentHash) {
6836
+ await client.execute({
6837
+ sql: `UPDATE memories
6838
+ SET status = 'deleted',
6839
+ outcome = COALESCE(outcome, 'superseded')
6840
+ WHERE id != ?
6841
+ AND content_hash = ?
6842
+ AND agent_id = ?
6843
+ AND project_name = ?
6844
+ AND COALESCE(memory_type, 'raw') = ?
6845
+ AND COALESCE(status, 'active') = 'active'`,
6846
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
6847
+ });
6848
+ }
6849
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
6850
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
6851
+ const old = await client.execute({
6852
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
6853
+ args: [supersedesId]
6854
+ });
6855
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
6856
+ const newImportance = Number(row.importance ?? 0);
6857
+ await client.batch([
6858
+ {
6859
+ sql: `UPDATE memories
6860
+ SET status = 'archived',
6861
+ outcome = COALESCE(outcome, 'superseded')
6862
+ WHERE id = ?`,
6863
+ args: [supersedesId]
6864
+ },
6865
+ {
6866
+ sql: `UPDATE memories
6867
+ SET importance = MAX(COALESCE(importance, 5), ?),
6868
+ parent_memory_id = COALESCE(parent_memory_id, ?)
6869
+ WHERE id = ?`,
6870
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
6871
+ }
6872
+ ], "write");
6873
+ }
6874
+ } catch (err) {
6875
+ process.stderr.write(
6876
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
6877
+ `
6878
+ );
6879
+ }
6880
+ }
6881
+ function schedulePostWriteMemoryHygiene(memoryIds) {
6882
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
6883
+ if (memoryIds.length === 0) return;
6884
+ const run = () => {
6885
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
6886
+ };
6887
+ if (typeof setImmediate === "function") setImmediate(run);
6888
+ else setTimeout(run, 0);
6889
+ }
6890
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
6891
+ var init_memory_write_governor = __esm({
6892
+ "src/lib/memory-write-governor.ts"() {
6893
+ "use strict";
6894
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
6895
+ "decision",
6896
+ "adr",
6897
+ "behavior",
6898
+ "procedure"
6899
+ ]);
6900
+ NOISE_DROP_PATTERNS = [
6901
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
6902
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
6903
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
6904
+ /^\s*Intercom is a speedup, not delivery/im,
6905
+ /^\s*Context bar reads as USAGE not remaining/im
6906
+ ];
6907
+ SKIP_EMBED_PATTERNS = [
6908
+ /tmux capture-pane\b/i,
6909
+ /docker ps\b/i,
6910
+ /docker images\b/i,
6911
+ /git status\b/i,
6912
+ /grep .*node_modules/i,
6913
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
6914
+ ];
6915
+ }
6916
+ });
6917
+
6715
6918
  // src/lib/shard-manager.ts
6716
6919
  var shard_manager_exports = {};
6717
6920
  __export(shard_manager_exports, {
@@ -6876,7 +7079,8 @@ async function ensureShardSchema(client) {
6876
7079
  }
6877
7080
  for (const idx of [
6878
7081
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
6879
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
7082
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
7083
+ "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"
6880
7084
  ]) {
6881
7085
  try {
6882
7086
  await client.execute(idx);
@@ -7070,7 +7274,7 @@ var init_platform_procedures = __esm({
7070
7274
  title: "Chain of command \u2014 who talks to whom",
7071
7275
  domain: "workflow",
7072
7276
  priority: "p0",
7073
- 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."
7277
+ 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."
7074
7278
  },
7075
7279
  {
7076
7280
  title: "Single dispatch path \u2014 create_task only",
@@ -7301,7 +7505,6 @@ __export(store_exports, {
7301
7505
  vectorToBlob: () => vectorToBlob,
7302
7506
  writeMemory: () => writeMemory
7303
7507
  });
7304
- import { createHash } from "crypto";
7305
7508
  function isBusyError2(err) {
7306
7509
  if (err instanceof Error) {
7307
7510
  const msg = err.message.toLowerCase();
@@ -7415,17 +7618,24 @@ async function writeMemory(record) {
7415
7618
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
7416
7619
  );
7417
7620
  }
7418
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
7419
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
7621
+ const governed = governMemoryRecord(record);
7622
+ if (governed.shouldDrop) return;
7623
+ record = governed.record;
7624
+ const contentHash = governed.contentHash;
7625
+ const memoryType = record.memory_type ?? "raw";
7626
+ if (_pendingRecords.some(
7627
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
7628
+ )) {
7420
7629
  return;
7421
7630
  }
7422
7631
  try {
7423
- const client = getClient();
7424
- const existing = await client.execute({
7425
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
7426
- args: [contentHash, record.agent_id]
7632
+ const existing = await findScopedDuplicate({
7633
+ contentHash,
7634
+ agentId: record.agent_id,
7635
+ projectName: record.project_name,
7636
+ memoryType
7427
7637
  });
7428
- if (existing.rows.length > 0) return;
7638
+ if (existing) return;
7429
7639
  } catch {
7430
7640
  }
7431
7641
  const dbRow = {
@@ -7456,7 +7666,7 @@ async function writeMemory(record) {
7456
7666
  tier: record.tier ?? classifyTier(record),
7457
7667
  supersedes_id: record.supersedes_id ?? null,
7458
7668
  draft: record.draft ? 1 : 0,
7459
- memory_type: record.memory_type ?? "raw",
7669
+ memory_type: memoryType,
7460
7670
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
7461
7671
  content_hash: contentHash,
7462
7672
  intent: record.intent ?? null,
@@ -7615,6 +7825,7 @@ async function flushBatch() {
7615
7825
  const globalClient = getClient();
7616
7826
  const globalStmts = batch.map(buildStmt);
7617
7827
  await globalClient.batch(globalStmts, "write");
7828
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
7618
7829
  _pendingRecords.splice(0, batch.length);
7619
7830
  try {
7620
7831
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -7735,7 +7946,11 @@ async function searchMemories(queryVector, agentId, options) {
7735
7946
  sql += ` AND timestamp >= ?`;
7736
7947
  args.push(options.since);
7737
7948
  }
7738
- if (options?.memoryType) {
7949
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
7950
+ const uniqueTypes = [...new Set(options.memoryTypes)];
7951
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
7952
+ args.push(...uniqueTypes);
7953
+ } else if (options?.memoryType) {
7739
7954
  sql += ` AND memory_type = ?`;
7740
7955
  args.push(options.memoryType);
7741
7956
  }
@@ -7873,6 +8088,7 @@ var init_store = __esm({
7873
8088
  init_keychain();
7874
8089
  init_config();
7875
8090
  init_state_bus();
8091
+ init_memory_write_governor();
7876
8092
  INIT_MAX_RETRIES = 3;
7877
8093
  INIT_RETRY_DELAY_MS = 1e3;
7878
8094
  _pendingRecords = [];
@@ -2871,6 +2871,14 @@ async function ensureSchema() {
2871
2871
  );
2872
2872
  } catch {
2873
2873
  }
2874
+ try {
2875
+ await client.execute(
2876
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2877
+ ON memories(content_hash, agent_id, project_name, memory_type)
2878
+ WHERE content_hash IS NOT NULL`
2879
+ );
2880
+ } catch {
2881
+ }
2874
2882
  await client.executeMultiple(`
2875
2883
  CREATE TABLE IF NOT EXISTS entities (
2876
2884
  id TEXT PRIMARY KEY,
@@ -3675,9 +3683,12 @@ var init_cto_delegation_gate = __esm({
3675
3683
  SCRATCHPAD_MAX_AGE_MS = 10 * 60 * 1e3;
3676
3684
  SCRATCHPAD_ESCAPE_PATTERN = /^(?:ENGINEER|TOM)-CAPABLE:\s*NO\s*—\s*reason:\s*\S+/m;
3677
3685
  GATE_BLOCK_MESSAGE = [
3678
- "DELEGATE CHECK: this edit looks implementable by an engineer.",
3679
- "Dispatch create_task now OR mark ENGINEER-CAPABLE: NO with reason.",
3680
- "No code until decided."
3686
+ "ACTION BLOCKED by CTO delegation gate. Your Write/Edit was DENIED \u2014 it did NOT execute.",
3687
+ "This edit looks implementable by an engineer. You MUST either:",
3688
+ " 1. Dispatch create_task to an engineer, OR",
3689
+ " 2. Add ENGINEER-CAPABLE: NO with explicit reason to proceed yourself.",
3690
+ "Do NOT report this action as successful. It was blocked. No file was changed.",
3691
+ "If you are a sub-agent, report this FAILURE to your parent agent."
3681
3692
  ].join("\n");
3682
3693
  }
3683
3694
  });
@@ -3922,6 +3933,197 @@ var init_state_bus = __esm({
3922
3933
  }
3923
3934
  });
3924
3935
 
3936
+ // src/lib/memory-write-governor.ts
3937
+ import { createHash } from "crypto";
3938
+ function normalizeMemoryText(text) {
3939
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
3940
+ }
3941
+ function classifyMemoryType(input2) {
3942
+ if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
3943
+ const tool = input2.tool_name.toLowerCase();
3944
+ const text = input2.raw_text.toLowerCase();
3945
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
3946
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
3947
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
3948
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
3949
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
3950
+ if (tool === "store_memory" || tool === "manual") return "observation";
3951
+ return "raw";
3952
+ }
3953
+ function shouldDropMemory(text) {
3954
+ const normalized = normalizeMemoryText(text);
3955
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
3956
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
3957
+ return { drop: true, reason: "known_boilerplate_noise" };
3958
+ }
3959
+ return { drop: false };
3960
+ }
3961
+ function shouldSkipEmbedding(input2) {
3962
+ const type = classifyMemoryType(input2);
3963
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
3964
+ if (type === "raw" && input2.raw_text.length > 2e4) return true;
3965
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
3966
+ return false;
3967
+ }
3968
+ function hashMemoryContent(text) {
3969
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
3970
+ }
3971
+ function scopedDedupArgs(input2) {
3972
+ return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
3973
+ }
3974
+ function governMemoryRecord(record) {
3975
+ const normalized = normalizeMemoryText(record.raw_text);
3976
+ const memoryType = classifyMemoryType({
3977
+ raw_text: normalized,
3978
+ agent_id: record.agent_id,
3979
+ project_name: record.project_name,
3980
+ tool_name: record.tool_name,
3981
+ memory_type: record.memory_type
3982
+ });
3983
+ const drop = shouldDropMemory(normalized);
3984
+ const skipEmbedding = shouldSkipEmbedding({
3985
+ raw_text: normalized,
3986
+ agent_id: record.agent_id,
3987
+ project_name: record.project_name,
3988
+ tool_name: record.tool_name,
3989
+ memory_type: memoryType
3990
+ });
3991
+ return {
3992
+ record: {
3993
+ ...record,
3994
+ raw_text: normalized,
3995
+ memory_type: memoryType,
3996
+ vector: skipEmbedding ? null : record.vector
3997
+ },
3998
+ contentHash: hashMemoryContent(normalized),
3999
+ shouldDrop: drop.drop,
4000
+ dropReason: drop.reason,
4001
+ skipEmbedding,
4002
+ hygiene: {
4003
+ dedup: true,
4004
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
4005
+ }
4006
+ };
4007
+ }
4008
+ async function findScopedDuplicate(input2) {
4009
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4010
+ const client = getClient2();
4011
+ const args = scopedDedupArgs(input2);
4012
+ let sql = `SELECT id FROM memories
4013
+ WHERE content_hash = ?
4014
+ AND agent_id = ?
4015
+ AND project_name = ?
4016
+ AND COALESCE(memory_type, 'raw') = ?
4017
+ AND COALESCE(status, 'active') != 'deleted'`;
4018
+ if (input2.excludeId) {
4019
+ sql += " AND id != ?";
4020
+ args.push(input2.excludeId);
4021
+ }
4022
+ sql += " ORDER BY timestamp DESC LIMIT 1";
4023
+ const result = await client.execute({ sql, args });
4024
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
4025
+ }
4026
+ async function runPostWriteMemoryHygiene(memoryId) {
4027
+ try {
4028
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4029
+ const client = getClient2();
4030
+ const current = await client.execute({
4031
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
4032
+ importance, timestamp
4033
+ FROM memories
4034
+ WHERE id = ?
4035
+ LIMIT 1`,
4036
+ args: [memoryId]
4037
+ });
4038
+ const row = current.rows[0];
4039
+ if (!row) return;
4040
+ const memoryType = String(row.memory_type ?? "raw");
4041
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
4042
+ const agentId = String(row.agent_id);
4043
+ const projectName = String(row.project_name);
4044
+ if (contentHash) {
4045
+ await client.execute({
4046
+ sql: `UPDATE memories
4047
+ SET status = 'deleted',
4048
+ outcome = COALESCE(outcome, 'superseded')
4049
+ WHERE id != ?
4050
+ AND content_hash = ?
4051
+ AND agent_id = ?
4052
+ AND project_name = ?
4053
+ AND COALESCE(memory_type, 'raw') = ?
4054
+ AND COALESCE(status, 'active') = 'active'`,
4055
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
4056
+ });
4057
+ }
4058
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
4059
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
4060
+ const old = await client.execute({
4061
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
4062
+ args: [supersedesId]
4063
+ });
4064
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
4065
+ const newImportance = Number(row.importance ?? 0);
4066
+ await client.batch([
4067
+ {
4068
+ sql: `UPDATE memories
4069
+ SET status = 'archived',
4070
+ outcome = COALESCE(outcome, 'superseded')
4071
+ WHERE id = ?`,
4072
+ args: [supersedesId]
4073
+ },
4074
+ {
4075
+ sql: `UPDATE memories
4076
+ SET importance = MAX(COALESCE(importance, 5), ?),
4077
+ parent_memory_id = COALESCE(parent_memory_id, ?)
4078
+ WHERE id = ?`,
4079
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
4080
+ }
4081
+ ], "write");
4082
+ }
4083
+ } catch (err) {
4084
+ process.stderr.write(
4085
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
4086
+ `
4087
+ );
4088
+ }
4089
+ }
4090
+ function schedulePostWriteMemoryHygiene(memoryIds) {
4091
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
4092
+ if (memoryIds.length === 0) return;
4093
+ const run = () => {
4094
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
4095
+ };
4096
+ if (typeof setImmediate === "function") setImmediate(run);
4097
+ else setTimeout(run, 0);
4098
+ }
4099
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
4100
+ var init_memory_write_governor = __esm({
4101
+ "src/lib/memory-write-governor.ts"() {
4102
+ "use strict";
4103
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
4104
+ "decision",
4105
+ "adr",
4106
+ "behavior",
4107
+ "procedure"
4108
+ ]);
4109
+ NOISE_DROP_PATTERNS = [
4110
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
4111
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
4112
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
4113
+ /^\s*Intercom is a speedup, not delivery/im,
4114
+ /^\s*Context bar reads as USAGE not remaining/im
4115
+ ];
4116
+ SKIP_EMBED_PATTERNS = [
4117
+ /tmux capture-pane\b/i,
4118
+ /docker ps\b/i,
4119
+ /docker images\b/i,
4120
+ /git status\b/i,
4121
+ /grep .*node_modules/i,
4122
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
4123
+ ];
4124
+ }
4125
+ });
4126
+
3925
4127
  // src/lib/shard-manager.ts
3926
4128
  var shard_manager_exports = {};
3927
4129
  __export(shard_manager_exports, {
@@ -4086,7 +4288,8 @@ async function ensureShardSchema(client) {
4086
4288
  }
4087
4289
  for (const idx of [
4088
4290
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
4089
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
4291
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
4292
+ "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"
4090
4293
  ]) {
4091
4294
  try {
4092
4295
  await client.execute(idx);
@@ -4280,7 +4483,7 @@ var init_platform_procedures = __esm({
4280
4483
  title: "Chain of command \u2014 who talks to whom",
4281
4484
  domain: "workflow",
4282
4485
  priority: "p0",
4283
- 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."
4486
+ 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."
4284
4487
  },
4285
4488
  {
4286
4489
  title: "Single dispatch path \u2014 create_task only",
@@ -4511,7 +4714,6 @@ __export(store_exports, {
4511
4714
  vectorToBlob: () => vectorToBlob,
4512
4715
  writeMemory: () => writeMemory
4513
4716
  });
4514
- import { createHash } from "crypto";
4515
4717
  function isBusyError2(err) {
4516
4718
  if (err instanceof Error) {
4517
4719
  const msg = err.message.toLowerCase();
@@ -4625,17 +4827,24 @@ async function writeMemory(record) {
4625
4827
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
4626
4828
  );
4627
4829
  }
4628
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
4629
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
4830
+ const governed = governMemoryRecord(record);
4831
+ if (governed.shouldDrop) return;
4832
+ record = governed.record;
4833
+ const contentHash = governed.contentHash;
4834
+ const memoryType = record.memory_type ?? "raw";
4835
+ if (_pendingRecords.some(
4836
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
4837
+ )) {
4630
4838
  return;
4631
4839
  }
4632
4840
  try {
4633
- const client = getClient();
4634
- const existing = await client.execute({
4635
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
4636
- args: [contentHash, record.agent_id]
4841
+ const existing = await findScopedDuplicate({
4842
+ contentHash,
4843
+ agentId: record.agent_id,
4844
+ projectName: record.project_name,
4845
+ memoryType
4637
4846
  });
4638
- if (existing.rows.length > 0) return;
4847
+ if (existing) return;
4639
4848
  } catch {
4640
4849
  }
4641
4850
  const dbRow = {
@@ -4666,7 +4875,7 @@ async function writeMemory(record) {
4666
4875
  tier: record.tier ?? classifyTier(record),
4667
4876
  supersedes_id: record.supersedes_id ?? null,
4668
4877
  draft: record.draft ? 1 : 0,
4669
- memory_type: record.memory_type ?? "raw",
4878
+ memory_type: memoryType,
4670
4879
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
4671
4880
  content_hash: contentHash,
4672
4881
  intent: record.intent ?? null,
@@ -4825,6 +5034,7 @@ async function flushBatch() {
4825
5034
  const globalClient = getClient();
4826
5035
  const globalStmts = batch.map(buildStmt);
4827
5036
  await globalClient.batch(globalStmts, "write");
5037
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
4828
5038
  _pendingRecords.splice(0, batch.length);
4829
5039
  try {
4830
5040
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -4945,7 +5155,11 @@ async function searchMemories(queryVector, agentId, options) {
4945
5155
  sql += ` AND timestamp >= ?`;
4946
5156
  args.push(options.since);
4947
5157
  }
4948
- if (options?.memoryType) {
5158
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
5159
+ const uniqueTypes = [...new Set(options.memoryTypes)];
5160
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
5161
+ args.push(...uniqueTypes);
5162
+ } else if (options?.memoryType) {
4949
5163
  sql += ` AND memory_type = ?`;
4950
5164
  args.push(options.memoryType);
4951
5165
  }
@@ -5083,6 +5297,7 @@ var init_store = __esm({
5083
5297
  init_keychain();
5084
5298
  init_config();
5085
5299
  init_state_bus();
5300
+ init_memory_write_governor();
5086
5301
  INIT_MAX_RETRIES = 3;
5087
5302
  INIT_RETRY_DELAY_MS = 1e3;
5088
5303
  _pendingRecords = [];
@@ -5483,9 +5698,10 @@ process.stdin.on("end", async () => {
5483
5698
  hookSpecificOutput: {
5484
5699
  permissionDecision: "deny"
5485
5700
  },
5486
- systemMessage: `BLOCKED: You attempted to write to exe/${target}/ \u2014 that is ${target}'s task folder.
5487
- Your tasks are in exe/${agent.agentId}/. Use ask_team_memory("${target}") for context.
5488
- This write was prevented. Do NOT retry.`
5701
+ systemMessage: `ACTION BLOCKED: Your Write/Edit to exe/${target}/ was DENIED \u2014 it did NOT execute.
5702
+ That is ${target}'s task folder, not yours. Your tasks are in exe/${agent.agentId}/.
5703
+ Use ask_team_memory("${target}") if you need context from ${target}.
5704
+ Do NOT report this as successful. No file was changed. If you are a sub-agent, report FAILURE.`
5489
5705
  });
5490
5706
  process.stdout.write(output);
5491
5707
  process.exit(0);