@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
@@ -2869,6 +2869,14 @@ async function ensureSchema() {
2869
2869
  );
2870
2870
  } catch {
2871
2871
  }
2872
+ try {
2873
+ await client.execute(
2874
+ `CREATE INDEX IF NOT EXISTS idx_memories_scoped_content_hash
2875
+ ON memories(content_hash, agent_id, project_name, memory_type)
2876
+ WHERE content_hash IS NOT NULL`
2877
+ );
2878
+ } catch {
2879
+ }
2872
2880
  await client.executeMultiple(`
2873
2881
  CREATE TABLE IF NOT EXISTS entities (
2874
2882
  id TEXT PRIMARY KEY,
@@ -6574,7 +6582,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6574
6582
  }
6575
6583
  if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
6576
6584
  if (agentRtConfig.runtime === "claude" && agentRtConfig.model) {
6577
- envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
6585
+ let ccModel = agentRtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
6586
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
6587
+ ccModel += "[1m]";
6588
+ }
6589
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${ccModel}`;
6578
6590
  }
6579
6591
  }
6580
6592
  let spawnCommand;
@@ -6921,6 +6933,197 @@ var init_keychain = __esm({
6921
6933
  }
6922
6934
  });
6923
6935
 
6936
+ // src/lib/memory-write-governor.ts
6937
+ import { createHash } from "crypto";
6938
+ function normalizeMemoryText(text) {
6939
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
6940
+ }
6941
+ function classifyMemoryType(input2) {
6942
+ if (input2.memory_type && input2.memory_type.trim()) return input2.memory_type.trim();
6943
+ const tool = input2.tool_name.toLowerCase();
6944
+ const text = input2.raw_text.toLowerCase();
6945
+ if (tool.includes("store_decision") || tool.includes("decision")) return "decision";
6946
+ if (tool.includes("commit") || text.includes("adr-") || text.includes("architectural decision")) return "adr";
6947
+ if (tool.includes("store_behavior") || tool.includes("behavior")) return "behavior";
6948
+ if (tool.includes("global_procedure") || text.includes("organization-wide procedures")) return "procedure";
6949
+ if (tool.includes("send_whatsapp") || tool.includes("conversation")) return "conversation";
6950
+ if (tool === "store_memory" || tool === "manual") return "observation";
6951
+ return "raw";
6952
+ }
6953
+ function shouldDropMemory(text) {
6954
+ const normalized = normalizeMemoryText(text);
6955
+ if (normalized.length < 10) return { drop: true, reason: "too_short" };
6956
+ if (NOISE_DROP_PATTERNS.some((pattern) => pattern.test(normalized))) {
6957
+ return { drop: true, reason: "known_boilerplate_noise" };
6958
+ }
6959
+ return { drop: false };
6960
+ }
6961
+ function shouldSkipEmbedding(input2) {
6962
+ const type = classifyMemoryType(input2);
6963
+ if (HIGH_VALUE_SUPERSESSION_TYPES.has(type)) return false;
6964
+ if (type === "raw" && input2.raw_text.length > 2e4) return true;
6965
+ if (SKIP_EMBED_PATTERNS.some((pattern) => pattern.test(input2.raw_text))) return true;
6966
+ return false;
6967
+ }
6968
+ function hashMemoryContent(text) {
6969
+ return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
6970
+ }
6971
+ function scopedDedupArgs(input2) {
6972
+ return [input2.contentHash, input2.agentId, input2.projectName, input2.memoryType];
6973
+ }
6974
+ function governMemoryRecord(record) {
6975
+ const normalized = normalizeMemoryText(record.raw_text);
6976
+ const memoryType = classifyMemoryType({
6977
+ raw_text: normalized,
6978
+ agent_id: record.agent_id,
6979
+ project_name: record.project_name,
6980
+ tool_name: record.tool_name,
6981
+ memory_type: record.memory_type
6982
+ });
6983
+ const drop = shouldDropMemory(normalized);
6984
+ const skipEmbedding = shouldSkipEmbedding({
6985
+ raw_text: normalized,
6986
+ agent_id: record.agent_id,
6987
+ project_name: record.project_name,
6988
+ tool_name: record.tool_name,
6989
+ memory_type: memoryType
6990
+ });
6991
+ return {
6992
+ record: {
6993
+ ...record,
6994
+ raw_text: normalized,
6995
+ memory_type: memoryType,
6996
+ vector: skipEmbedding ? null : record.vector
6997
+ },
6998
+ contentHash: hashMemoryContent(normalized),
6999
+ shouldDrop: drop.drop,
7000
+ dropReason: drop.reason,
7001
+ skipEmbedding,
7002
+ hygiene: {
7003
+ dedup: true,
7004
+ supersession: HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)
7005
+ }
7006
+ };
7007
+ }
7008
+ async function findScopedDuplicate(input2) {
7009
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
7010
+ const client = getClient2();
7011
+ const args = scopedDedupArgs(input2);
7012
+ let sql = `SELECT id FROM memories
7013
+ WHERE content_hash = ?
7014
+ AND agent_id = ?
7015
+ AND project_name = ?
7016
+ AND COALESCE(memory_type, 'raw') = ?
7017
+ AND COALESCE(status, 'active') != 'deleted'`;
7018
+ if (input2.excludeId) {
7019
+ sql += " AND id != ?";
7020
+ args.push(input2.excludeId);
7021
+ }
7022
+ sql += " ORDER BY timestamp DESC LIMIT 1";
7023
+ const result = await client.execute({ sql, args });
7024
+ return result.rows[0]?.id ? String(result.rows[0].id) : null;
7025
+ }
7026
+ async function runPostWriteMemoryHygiene(memoryId) {
7027
+ try {
7028
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
7029
+ const client = getClient2();
7030
+ const current = await client.execute({
7031
+ sql: `SELECT id, agent_id, project_name, memory_type, content_hash, supersedes_id,
7032
+ importance, timestamp
7033
+ FROM memories
7034
+ WHERE id = ?
7035
+ LIMIT 1`,
7036
+ args: [memoryId]
7037
+ });
7038
+ const row = current.rows[0];
7039
+ if (!row) return;
7040
+ const memoryType = String(row.memory_type ?? "raw");
7041
+ const contentHash = row.content_hash ? String(row.content_hash) : null;
7042
+ const agentId = String(row.agent_id);
7043
+ const projectName = String(row.project_name);
7044
+ if (contentHash) {
7045
+ await client.execute({
7046
+ sql: `UPDATE memories
7047
+ SET status = 'deleted',
7048
+ outcome = COALESCE(outcome, 'superseded')
7049
+ WHERE id != ?
7050
+ AND content_hash = ?
7051
+ AND agent_id = ?
7052
+ AND project_name = ?
7053
+ AND COALESCE(memory_type, 'raw') = ?
7054
+ AND COALESCE(status, 'active') = 'active'`,
7055
+ args: [memoryId, contentHash, agentId, projectName, memoryType]
7056
+ });
7057
+ }
7058
+ const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
7059
+ if (supersedesId && HIGH_VALUE_SUPERSESSION_TYPES.has(memoryType)) {
7060
+ const old = await client.execute({
7061
+ sql: `SELECT importance FROM memories WHERE id = ? LIMIT 1`,
7062
+ args: [supersedesId]
7063
+ });
7064
+ const oldImportance = Number(old.rows[0]?.importance ?? 0);
7065
+ const newImportance = Number(row.importance ?? 0);
7066
+ await client.batch([
7067
+ {
7068
+ sql: `UPDATE memories
7069
+ SET status = 'archived',
7070
+ outcome = COALESCE(outcome, 'superseded')
7071
+ WHERE id = ?`,
7072
+ args: [supersedesId]
7073
+ },
7074
+ {
7075
+ sql: `UPDATE memories
7076
+ SET importance = MAX(COALESCE(importance, 5), ?),
7077
+ parent_memory_id = COALESCE(parent_memory_id, ?)
7078
+ WHERE id = ?`,
7079
+ args: [Math.max(oldImportance, newImportance), supersedesId, memoryId]
7080
+ }
7081
+ ], "write");
7082
+ }
7083
+ } catch (err) {
7084
+ process.stderr.write(
7085
+ `[memory-governor] post-write hygiene failed for ${memoryId}: ${err instanceof Error ? err.message : String(err)}
7086
+ `
7087
+ );
7088
+ }
7089
+ }
7090
+ function schedulePostWriteMemoryHygiene(memoryIds) {
7091
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
7092
+ if (memoryIds.length === 0) return;
7093
+ const run = () => {
7094
+ void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
7095
+ };
7096
+ if (typeof setImmediate === "function") setImmediate(run);
7097
+ else setTimeout(run, 0);
7098
+ }
7099
+ var HIGH_VALUE_SUPERSESSION_TYPES, NOISE_DROP_PATTERNS, SKIP_EMBED_PATTERNS;
7100
+ var init_memory_write_governor = __esm({
7101
+ "src/lib/memory-write-governor.ts"() {
7102
+ "use strict";
7103
+ HIGH_VALUE_SUPERSESSION_TYPES = /* @__PURE__ */ new Set([
7104
+ "decision",
7105
+ "adr",
7106
+ "behavior",
7107
+ "procedure"
7108
+ ]);
7109
+ NOISE_DROP_PATTERNS = [
7110
+ /^\s*\[📋\s+\d+\s+reviews?\s+pending\b/im,
7111
+ /^\s*<system-reminder>[\s\S]*?<\/system-reminder>\s*$/im,
7112
+ /^\s*The UserPromptSubmit hook checks the DB for new tasks/im,
7113
+ /^\s*Intercom is a speedup, not delivery/im,
7114
+ /^\s*Context bar reads as USAGE not remaining/im
7115
+ ];
7116
+ SKIP_EMBED_PATTERNS = [
7117
+ /tmux capture-pane\b/i,
7118
+ /docker ps\b/i,
7119
+ /docker images\b/i,
7120
+ /git status\b/i,
7121
+ /grep .*node_modules/i,
7122
+ /npm (install|ci)\b[\s\S]*(added \d+ packages|audited \d+ packages)/i
7123
+ ];
7124
+ }
7125
+ });
7126
+
6924
7127
  // src/lib/shard-manager.ts
6925
7128
  var shard_manager_exports = {};
6926
7129
  __export(shard_manager_exports, {
@@ -7085,7 +7288,8 @@ async function ensureShardSchema(client) {
7085
7288
  }
7086
7289
  for (const idx of [
7087
7290
  "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
7088
- "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
7291
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL",
7292
+ "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"
7089
7293
  ]) {
7090
7294
  try {
7091
7295
  await client.execute(idx);
@@ -7279,7 +7483,7 @@ var init_platform_procedures = __esm({
7279
7483
  title: "Chain of command \u2014 who talks to whom",
7280
7484
  domain: "workflow",
7281
7485
  priority: "p0",
7282
- 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."
7486
+ 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."
7283
7487
  },
7284
7488
  {
7285
7489
  title: "Single dispatch path \u2014 create_task only",
@@ -7510,7 +7714,6 @@ __export(store_exports, {
7510
7714
  vectorToBlob: () => vectorToBlob,
7511
7715
  writeMemory: () => writeMemory
7512
7716
  });
7513
- import { createHash } from "crypto";
7514
7717
  function isBusyError2(err) {
7515
7718
  if (err instanceof Error) {
7516
7719
  const msg = err.message.toLowerCase();
@@ -7624,17 +7827,24 @@ async function writeMemory(record) {
7624
7827
  `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
7625
7828
  );
7626
7829
  }
7627
- const contentHash = createHash("md5").update(record.raw_text).digest("hex");
7628
- if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
7830
+ const governed = governMemoryRecord(record);
7831
+ if (governed.shouldDrop) return;
7832
+ record = governed.record;
7833
+ const contentHash = governed.contentHash;
7834
+ const memoryType = record.memory_type ?? "raw";
7835
+ if (_pendingRecords.some(
7836
+ (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
7837
+ )) {
7629
7838
  return;
7630
7839
  }
7631
7840
  try {
7632
- const client = getClient();
7633
- const existing = await client.execute({
7634
- sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
7635
- args: [contentHash, record.agent_id]
7841
+ const existing = await findScopedDuplicate({
7842
+ contentHash,
7843
+ agentId: record.agent_id,
7844
+ projectName: record.project_name,
7845
+ memoryType
7636
7846
  });
7637
- if (existing.rows.length > 0) return;
7847
+ if (existing) return;
7638
7848
  } catch {
7639
7849
  }
7640
7850
  const dbRow = {
@@ -7665,7 +7875,7 @@ async function writeMemory(record) {
7665
7875
  tier: record.tier ?? classifyTier(record),
7666
7876
  supersedes_id: record.supersedes_id ?? null,
7667
7877
  draft: record.draft ? 1 : 0,
7668
- memory_type: record.memory_type ?? "raw",
7878
+ memory_type: memoryType,
7669
7879
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
7670
7880
  content_hash: contentHash,
7671
7881
  intent: record.intent ?? null,
@@ -7824,6 +8034,7 @@ async function flushBatch() {
7824
8034
  const globalClient = getClient();
7825
8035
  const globalStmts = batch.map(buildStmt);
7826
8036
  await globalClient.batch(globalStmts, "write");
8037
+ schedulePostWriteMemoryHygiene(batch.map((row) => row.id));
7827
8038
  _pendingRecords.splice(0, batch.length);
7828
8039
  try {
7829
8040
  const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -7944,7 +8155,11 @@ async function searchMemories(queryVector, agentId, options) {
7944
8155
  sql += ` AND timestamp >= ?`;
7945
8156
  args.push(options.since);
7946
8157
  }
7947
- if (options?.memoryType) {
8158
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
8159
+ const uniqueTypes = [...new Set(options.memoryTypes)];
8160
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
8161
+ args.push(...uniqueTypes);
8162
+ } else if (options?.memoryType) {
7948
8163
  sql += ` AND memory_type = ?`;
7949
8164
  args.push(options.memoryType);
7950
8165
  }
@@ -8082,6 +8297,7 @@ var init_store = __esm({
8082
8297
  init_keychain();
8083
8298
  init_config();
8084
8299
  init_state_bus();
8300
+ init_memory_write_governor();
8085
8301
  INIT_MAX_RETRIES = 3;
8086
8302
  INIT_RETRY_DELAY_MS = 1e3;
8087
8303
  _pendingRecords = [];