@askexenow/exe-os 0.9.35 → 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 (66) hide show
  1. package/dist/bin/backfill-conversations.js +2 -1
  2. package/dist/bin/backfill-responses.js +2 -1
  3. package/dist/bin/backfill-vectors.js +1 -1
  4. package/dist/bin/cleanup-stale-review-tasks.js +7 -2
  5. package/dist/bin/cli.js +45 -14
  6. package/dist/bin/exe-agent.js +1 -1
  7. package/dist/bin/exe-assign.js +7 -2
  8. package/dist/bin/exe-boot.js +1 -1
  9. package/dist/bin/exe-call.js +7 -5
  10. package/dist/bin/exe-dispatch.js +7 -2
  11. package/dist/bin/exe-doctor.js +1 -1
  12. package/dist/bin/exe-export-behaviors.js +84 -4
  13. package/dist/bin/exe-forget.js +28 -11
  14. package/dist/bin/exe-gateway.js +7 -2
  15. package/dist/bin/exe-heartbeat.js +7 -2
  16. package/dist/bin/exe-kill.js +7 -2
  17. package/dist/bin/exe-launch-agent.js +85 -7
  18. package/dist/bin/exe-new-employee.js +26 -2
  19. package/dist/bin/exe-pending-messages.js +7 -2
  20. package/dist/bin/exe-pending-notifications.js +7 -2
  21. package/dist/bin/exe-pending-reviews.js +7 -2
  22. package/dist/bin/exe-rename.js +1 -1
  23. package/dist/bin/exe-review.js +7 -2
  24. package/dist/bin/exe-search.js +29 -11
  25. package/dist/bin/exe-session-cleanup.js +7 -2
  26. package/dist/bin/exe-start-codex.js +69 -3
  27. package/dist/bin/exe-start-opencode.js +80 -3
  28. package/dist/bin/exe-status.js +7 -2
  29. package/dist/bin/exe-team.js +7 -2
  30. package/dist/bin/git-sweep.js +7 -2
  31. package/dist/bin/graph-backfill.js +2 -1
  32. package/dist/bin/graph-export.js +7 -2
  33. package/dist/bin/install.js +25 -1
  34. package/dist/bin/intercom-check.js +7 -2
  35. package/dist/bin/scan-tasks.js +7 -2
  36. package/dist/bin/setup.js +7 -5
  37. package/dist/bin/shard-migrate.js +2 -1
  38. package/dist/gateway/index.js +7 -2
  39. package/dist/hooks/bug-report-worker.js +7 -2
  40. package/dist/hooks/codex-stop-task-finalizer.js +7 -2
  41. package/dist/hooks/commit-complete.js +7 -2
  42. package/dist/hooks/error-recall.js +29 -11
  43. package/dist/hooks/ingest.js +7 -2
  44. package/dist/hooks/instructions-loaded.js +7 -2
  45. package/dist/hooks/notification.js +7 -2
  46. package/dist/hooks/post-compact.js +7 -2
  47. package/dist/hooks/post-tool-combined.js +29 -11
  48. package/dist/hooks/pre-compact.js +7 -2
  49. package/dist/hooks/pre-tool-use.js +17 -8
  50. package/dist/hooks/prompt-submit.js +124 -12
  51. package/dist/hooks/session-end.js +7 -2
  52. package/dist/hooks/session-start.js +207 -38
  53. package/dist/hooks/stop.js +7 -2
  54. package/dist/hooks/subagent-stop.js +7 -2
  55. package/dist/hooks/summary-worker.js +1 -1
  56. package/dist/index.js +7 -2
  57. package/dist/lib/employee-templates.js +7 -5
  58. package/dist/lib/exe-daemon.js +115 -26
  59. package/dist/lib/hybrid-search.js +29 -11
  60. package/dist/lib/schedules.js +1 -1
  61. package/dist/lib/store.js +7 -2
  62. package/dist/mcp/server.js +109 -26
  63. package/dist/runtime/index.js +7 -2
  64. package/dist/tui/App.js +7 -2
  65. package/package.json +1 -1
  66. package/src/commands/exe/save.md +48 -0
@@ -3529,6 +3529,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
3529
3529
  }
3530
3530
  }
3531
3531
  function schedulePostWriteMemoryHygiene(memoryIds) {
3532
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
3532
3533
  if (memoryIds.length === 0) return;
3533
3534
  const run = () => {
3534
3535
  void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
@@ -3923,7 +3924,7 @@ var init_platform_procedures = __esm({
3923
3924
  title: "Chain of command \u2014 who talks to whom",
3924
3925
  domain: "workflow",
3925
3926
  priority: "p0",
3926
- 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."
3927
+ 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."
3927
3928
  },
3928
3929
  {
3929
3930
  title: "Single dispatch path \u2014 create_task only",
@@ -4595,7 +4596,11 @@ async function searchMemories(queryVector, agentId, options) {
4595
4596
  sql += ` AND timestamp >= ?`;
4596
4597
  args.push(options.since);
4597
4598
  }
4598
- if (options?.memoryType) {
4599
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
4600
+ const uniqueTypes = [...new Set(options.memoryTypes)];
4601
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
4602
+ args.push(...uniqueTypes);
4603
+ } else if (options?.memoryType) {
4599
4604
  sql += ` AND memory_type = ?`;
4600
4605
  args.push(options.memoryType);
4601
4606
  }
@@ -7723,6 +7728,13 @@ var init_tasks_notify = __esm({
7723
7728
  });
7724
7729
 
7725
7730
  // src/lib/behaviors.ts
7731
+ var behaviors_exports = {};
7732
+ __export(behaviors_exports, {
7733
+ deactivateBehavior: () => deactivateBehavior,
7734
+ listBehaviors: () => listBehaviors,
7735
+ listBehaviorsByDomain: () => listBehaviorsByDomain,
7736
+ storeBehavior: () => storeBehavior
7737
+ });
7726
7738
  import crypto6 from "crypto";
7727
7739
  async function storeBehavior(opts) {
7728
7740
  const client = getClient();
@@ -7742,6 +7754,63 @@ async function storeBehavior(opts) {
7742
7754
  });
7743
7755
  return id;
7744
7756
  }
7757
+ async function listBehaviors(agentId, projectName, limit = 30) {
7758
+ const client = getClient();
7759
+ const result = await client.execute({
7760
+ sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector
7761
+ FROM behaviors
7762
+ WHERE agent_id = ? AND active = 1
7763
+ AND (project_name IS NULL OR project_name = ?)
7764
+ ORDER BY
7765
+ CASE WHEN priority = 'p0' THEN 0
7766
+ WHEN priority = 'p1' THEN 1
7767
+ ELSE 2 END,
7768
+ updated_at DESC
7769
+ LIMIT ?`,
7770
+ args: [agentId, projectName ?? "", limit]
7771
+ });
7772
+ return result.rows.map((r) => ({
7773
+ id: String(r.id),
7774
+ agent_id: String(r.agent_id),
7775
+ project_name: r.project_name ? String(r.project_name) : null,
7776
+ domain: r.domain ? String(r.domain) : null,
7777
+ priority: String(r.priority || "p1"),
7778
+ content: String(r.content),
7779
+ active: Number(r.active),
7780
+ created_at: String(r.created_at),
7781
+ updated_at: String(r.updated_at),
7782
+ vector: r.vector ? Array.from(new Float32Array(r.vector)) : null
7783
+ }));
7784
+ }
7785
+ async function listBehaviorsByDomain(agentId, domain) {
7786
+ const client = getClient();
7787
+ const result = await client.execute({
7788
+ sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector
7789
+ FROM behaviors
7790
+ WHERE agent_id = ? AND domain = ? AND active = 1`,
7791
+ args: [agentId, domain]
7792
+ });
7793
+ return result.rows.map((r) => ({
7794
+ id: String(r.id),
7795
+ agent_id: String(r.agent_id),
7796
+ project_name: r.project_name ? String(r.project_name) : null,
7797
+ domain: r.domain ? String(r.domain) : null,
7798
+ priority: String(r.priority || "p1"),
7799
+ content: String(r.content),
7800
+ active: Number(r.active),
7801
+ created_at: String(r.created_at),
7802
+ updated_at: String(r.updated_at),
7803
+ vector: r.vector ? Array.from(new Float32Array(r.vector)) : null
7804
+ }));
7805
+ }
7806
+ async function deactivateBehavior(id) {
7807
+ const client = getClient();
7808
+ const result = await client.execute({
7809
+ sql: `UPDATE behaviors SET active = 0, updated_at = ? WHERE id = ? AND active = 1`,
7810
+ args: [(/* @__PURE__ */ new Date()).toISOString(), id]
7811
+ });
7812
+ return (result.rowsAffected ?? 0) > 0;
7813
+ }
7745
7814
  var init_behaviors = __esm({
7746
7815
  "src/lib/behaviors.ts"() {
7747
7816
  "use strict";
@@ -9697,6 +9766,17 @@ import path25 from "path";
9697
9766
  init_store();
9698
9767
  init_database();
9699
9768
  var RRF_K = 60;
9769
+ function appendMemoryTypeFilter(sql, args, column, options) {
9770
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
9771
+ const uniqueTypes = [...new Set(options.memoryTypes)];
9772
+ sql += ` AND ${column} IN (${uniqueTypes.map(() => "?").join(",")})`;
9773
+ args.push(...uniqueTypes);
9774
+ } else if (options?.memoryType) {
9775
+ sql += ` AND ${column} = ?`;
9776
+ args.push(options.memoryType);
9777
+ }
9778
+ return sql;
9779
+ }
9700
9780
  async function hybridSearch(queryText, agentId, options) {
9701
9781
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
9702
9782
  const config = await loadConfig2();
@@ -9925,6 +10005,7 @@ async function estimateCardinality(agentId, options) {
9925
10005
  sql += ` AND timestamp >= ?`;
9926
10006
  args.push(options.since);
9927
10007
  }
10008
+ sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
9928
10009
  try {
9929
10010
  const result = await client.execute({ sql, args });
9930
10011
  return Number(result.rows[0]?.cnt) || 0;
@@ -10043,10 +10124,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
10043
10124
  sql += ` AND m.timestamp >= ?`;
10044
10125
  args.push(options.since);
10045
10126
  }
10046
- if (options?.memoryType) {
10047
- sql += ` AND m.memory_type = ?`;
10048
- args.push(options.memoryType);
10049
- }
10127
+ sql = appendMemoryTypeFilter(sql, args, "m.memory_type", options);
10050
10128
  sql += ` ORDER BY rank LIMIT ?`;
10051
10129
  args.push(limit);
10052
10130
  const result = await client.execute({ sql, args });
@@ -10103,9 +10181,16 @@ async function recentRecords(agentId, options, limit, textFilter) {
10103
10181
  AND timestamp >= ? AND timestamp <= ?
10104
10182
  AND COALESCE(status, 'active') = 'active'
10105
10183
  AND ${options?.includeRaw === false ? "COALESCE(memory_type, 'raw') != 'raw'" : "1 = 1"}
10184
+ ${options?.memoryTypes?.length ? `AND memory_type IN (${options.memoryTypes.map(() => "?").join(",")})` : options?.memoryType ? "AND memory_type = ?" : ""}
10106
10185
  AND COALESCE(confidence, 0.7) >= 0.3
10107
10186
  ORDER BY timestamp DESC LIMIT ?`,
10108
- args: [agentId, windowStart, killedAt, boundarySlots]
10187
+ args: [
10188
+ agentId,
10189
+ windowStart,
10190
+ killedAt,
10191
+ ...options?.memoryTypes?.length ? [...new Set(options.memoryTypes)] : options?.memoryType ? [options.memoryType] : [],
10192
+ boundarySlots
10193
+ ]
10109
10194
  });
10110
10195
  for (const row of boundaryResult.rows) {
10111
10196
  sessionBoundaryMemories.push(rowToMemoryRecord(row));
@@ -10151,10 +10236,7 @@ async function recentRecords(agentId, options, limit, textFilter) {
10151
10236
  sql += ` AND timestamp >= ?`;
10152
10237
  args.push(options.since);
10153
10238
  }
10154
- if (options?.memoryType) {
10155
- sql += ` AND memory_type = ?`;
10156
- args.push(options.memoryType);
10157
- }
10239
+ sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
10158
10240
  if (textFilter) {
10159
10241
  sql += ` AND raw_text LIKE '%' || ? || '%'`;
10160
10242
  args.push(textFilter);
@@ -10600,6 +10682,36 @@ ${fresh.map(
10600
10682
  }
10601
10683
  }
10602
10684
  }
10685
+ let behaviorContext = "";
10686
+ if (!IS_CODEX_RUNTIME && agent.agentId !== "default") {
10687
+ try {
10688
+ const counterPath = path25.join(CACHE_DIR3, `prompt-count-${getSessionKey()}`);
10689
+ let count = 1;
10690
+ try {
10691
+ count = parseInt(readFileSync17(counterPath, "utf8").trim(), 10) + 1;
10692
+ } catch {
10693
+ }
10694
+ writeFileSync11(counterPath, String(count), "utf8");
10695
+ const BEHAVIOR_REINJECT_INTERVAL = 50;
10696
+ if (count === 1 || count % BEHAVIOR_REINJECT_INTERVAL === 0) {
10697
+ const { listBehaviors: listBehaviors2 } = await Promise.resolve().then(() => (init_behaviors(), behaviors_exports));
10698
+ const behaviors = await listBehaviors2(agent.agentId);
10699
+ const active = behaviors.filter((b) => b.active !== 0).sort((a, b) => (a.priority === "p0" ? -1 : 1) - (b.priority === "p0" ? -1 : 1)).slice(0, 15);
10700
+ if (active.length > 0) {
10701
+ const lines = active.map(
10702
+ (b) => `- [${b.domain ?? "workflow"}] ${b.content.slice(0, 200)}`
10703
+ );
10704
+ behaviorContext = `
10705
+ ## Your Behavioral Memory
10706
+ Validated patterns and corrections for this session. Follow them.
10707
+ Sourced from exe-os Layer 2 (Expertise). Changes apply on next spawn.
10708
+
10709
+ ${lines.join("\n")}`;
10710
+ }
10711
+ }
10712
+ } catch {
10713
+ }
10714
+ }
10603
10715
  let reviewContext = "";
10604
10716
  if (canCoordinate(agent.agentId, agent.agentRole)) {
10605
10717
  try {
@@ -10655,7 +10767,7 @@ IMPORTANT: After completing your current task, you MUST address the pending revi
10655
10767
  } catch {
10656
10768
  }
10657
10769
  }
10658
- const combined = [cacheContext, memoryContext, reviewContext].filter(Boolean).join("\n\n");
10770
+ const combined = [cacheContext, memoryContext, behaviorContext, reviewContext].filter(Boolean).join("\n\n");
10659
10771
  if (combined.length > 0) {
10660
10772
  const output = JSON.stringify({
10661
10773
  hookSpecificOutput: {
@@ -7088,6 +7088,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
7088
7088
  }
7089
7089
  }
7090
7090
  function schedulePostWriteMemoryHygiene(memoryIds) {
7091
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
7091
7092
  if (memoryIds.length === 0) return;
7092
7093
  const run = () => {
7093
7094
  void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
@@ -7482,7 +7483,7 @@ var init_platform_procedures = __esm({
7482
7483
  title: "Chain of command \u2014 who talks to whom",
7483
7484
  domain: "workflow",
7484
7485
  priority: "p0",
7485
- 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."
7486
7487
  },
7487
7488
  {
7488
7489
  title: "Single dispatch path \u2014 create_task only",
@@ -8154,7 +8155,11 @@ async function searchMemories(queryVector, agentId, options) {
8154
8155
  sql += ` AND timestamp >= ?`;
8155
8156
  args.push(options.since);
8156
8157
  }
8157
- 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) {
8158
8163
  sql += ` AND memory_type = ?`;
8159
8164
  args.push(options.memoryType);
8160
8165
  }
@@ -3290,8 +3290,8 @@ function deriveMachineKey() {
3290
3290
  }
3291
3291
  function readMachineId() {
3292
3292
  try {
3293
- const { readFileSync: readFileSync13 } = __require("fs");
3294
- return readFileSync13("/etc/machine-id", "utf-8").trim();
3293
+ const { readFileSync: readFileSync14 } = __require("fs");
3294
+ return readFileSync14("/etc/machine-id", "utf-8").trim();
3295
3295
  } catch {
3296
3296
  return "";
3297
3297
  }
@@ -3545,10 +3545,10 @@ async function runPostWriteMemoryHygiene(memoryId) {
3545
3545
  const row = current.rows[0];
3546
3546
  if (!row) return;
3547
3547
  const memoryType = String(row.memory_type ?? "raw");
3548
- const contentHash = row.content_hash ? String(row.content_hash) : null;
3548
+ const contentHash2 = row.content_hash ? String(row.content_hash) : null;
3549
3549
  const agentId = String(row.agent_id);
3550
3550
  const projectName = String(row.project_name);
3551
- if (contentHash) {
3551
+ if (contentHash2) {
3552
3552
  await client.execute({
3553
3553
  sql: `UPDATE memories
3554
3554
  SET status = 'deleted',
@@ -3559,7 +3559,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
3559
3559
  AND project_name = ?
3560
3560
  AND COALESCE(memory_type, 'raw') = ?
3561
3561
  AND COALESCE(status, 'active') = 'active'`,
3562
- args: [memoryId, contentHash, agentId, projectName, memoryType]
3562
+ args: [memoryId, contentHash2, agentId, projectName, memoryType]
3563
3563
  });
3564
3564
  }
3565
3565
  const supersedesId = row.supersedes_id ? String(row.supersedes_id) : null;
@@ -3595,6 +3595,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
3595
3595
  }
3596
3596
  }
3597
3597
  function schedulePostWriteMemoryHygiene(memoryIds) {
3598
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
3598
3599
  if (memoryIds.length === 0) return;
3599
3600
  const run = () => {
3600
3601
  void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
@@ -3989,7 +3990,7 @@ var init_platform_procedures = __esm({
3989
3990
  title: "Chain of command \u2014 who talks to whom",
3990
3991
  domain: "workflow",
3991
3992
  priority: "p0",
3992
- 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."
3993
+ 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."
3993
3994
  },
3994
3995
  {
3995
3996
  title: "Single dispatch path \u2014 create_task only",
@@ -4336,16 +4337,16 @@ async function writeMemory(record) {
4336
4337
  const governed = governMemoryRecord(record);
4337
4338
  if (governed.shouldDrop) return;
4338
4339
  record = governed.record;
4339
- const contentHash = governed.contentHash;
4340
+ const contentHash2 = governed.contentHash;
4340
4341
  const memoryType = record.memory_type ?? "raw";
4341
4342
  if (_pendingRecords.some(
4342
- (r) => r.content_hash === contentHash && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
4343
+ (r) => r.content_hash === contentHash2 && r.agent_id === record.agent_id && r.project_name === record.project_name && (r.memory_type ?? "raw") === memoryType
4343
4344
  )) {
4344
4345
  return;
4345
4346
  }
4346
4347
  try {
4347
4348
  const existing = await findScopedDuplicate({
4348
- contentHash,
4349
+ contentHash: contentHash2,
4349
4350
  agentId: record.agent_id,
4350
4351
  projectName: record.project_name,
4351
4352
  memoryType
@@ -4383,7 +4384,7 @@ async function writeMemory(record) {
4383
4384
  draft: record.draft ? 1 : 0,
4384
4385
  memory_type: memoryType,
4385
4386
  trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
4386
- content_hash: contentHash,
4387
+ content_hash: contentHash2,
4387
4388
  intent: record.intent ?? null,
4388
4389
  outcome: record.outcome ?? null,
4389
4390
  domain: record.domain ?? inferDomain(record),
@@ -4456,7 +4457,7 @@ async function flushBatch() {
4456
4457
  const draft = row.draft ? 1 : 0;
4457
4458
  const memoryType = row.memory_type ?? "raw";
4458
4459
  const trajectory = row.trajectory ?? null;
4459
- const contentHash = row.content_hash ?? null;
4460
+ const contentHash2 = row.content_hash ?? null;
4460
4461
  const intent = row.intent ?? null;
4461
4462
  const outcome = row.outcome ?? null;
4462
4463
  const domain = row.domain ?? null;
@@ -4528,7 +4529,7 @@ async function flushBatch() {
4528
4529
  draft,
4529
4530
  memoryType,
4530
4531
  trajectory,
4531
- contentHash
4532
+ contentHash2
4532
4533
  ];
4533
4534
  return {
4534
4535
  sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
@@ -4661,7 +4662,11 @@ async function searchMemories(queryVector, agentId, options) {
4661
4662
  sql += ` AND timestamp >= ?`;
4662
4663
  args.push(options.since);
4663
4664
  }
4664
- if (options?.memoryType) {
4665
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
4666
+ const uniqueTypes = [...new Set(options.memoryTypes)];
4667
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
4668
+ args.push(...uniqueTypes);
4669
+ } else if (options?.memoryType) {
4665
4670
  sql += ` AND memory_type = ?`;
4666
4671
  args.push(options.memoryType);
4667
4672
  }
@@ -5066,10 +5071,10 @@ async function disposeEmbedder() {
5066
5071
  async function embedDirect(text) {
5067
5072
  const llamaCpp = await import("node-llama-cpp");
5068
5073
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
5069
- const { existsSync: existsSync16 } = await import("fs");
5070
- const path20 = await import("path");
5071
- const modelPath = path20.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
5072
- if (!existsSync16(modelPath)) {
5074
+ const { existsSync: existsSync17 } = await import("fs");
5075
+ const path21 = await import("path");
5076
+ const modelPath = path21.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
5077
+ if (!existsSync17(modelPath)) {
5073
5078
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
5074
5079
  }
5075
5080
  const llama = await llamaCpp.getLlama();
@@ -5959,6 +5964,17 @@ __export(hybrid_search_exports, {
5959
5964
  rrfMerge: () => rrfMerge,
5960
5965
  rrfMergeMulti: () => rrfMergeMulti
5961
5966
  });
5967
+ function appendMemoryTypeFilter(sql, args, column, options) {
5968
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
5969
+ const uniqueTypes = [...new Set(options.memoryTypes)];
5970
+ sql += ` AND ${column} IN (${uniqueTypes.map(() => "?").join(",")})`;
5971
+ args.push(...uniqueTypes);
5972
+ } else if (options?.memoryType) {
5973
+ sql += ` AND ${column} = ?`;
5974
+ args.push(options.memoryType);
5975
+ }
5976
+ return sql;
5977
+ }
5962
5978
  async function hybridSearch(queryText, agentId, options) {
5963
5979
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
5964
5980
  const config = await loadConfig2();
@@ -6137,10 +6153,10 @@ async function hybridSearch(queryText, agentId, options) {
6137
6153
  };
6138
6154
  try {
6139
6155
  const fs = await import("fs");
6140
- const path20 = await import("path");
6156
+ const path21 = await import("path");
6141
6157
  const os10 = await import("os");
6142
- const logPath = path20.join(os10.homedir(), ".exe-os", "search-quality.jsonl");
6143
- fs.mkdirSync(path20.dirname(logPath), { recursive: true });
6158
+ const logPath = path21.join(os10.homedir(), ".exe-os", "search-quality.jsonl");
6159
+ fs.mkdirSync(path21.dirname(logPath), { recursive: true });
6144
6160
  fs.appendFileSync(logPath, JSON.stringify(logEntry) + "\n");
6145
6161
  } catch {
6146
6162
  }
@@ -6187,6 +6203,7 @@ async function estimateCardinality(agentId, options) {
6187
6203
  sql += ` AND timestamp >= ?`;
6188
6204
  args.push(options.since);
6189
6205
  }
6206
+ sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
6190
6207
  try {
6191
6208
  const result = await client.execute({ sql, args });
6192
6209
  return Number(result.rows[0]?.cnt) || 0;
@@ -6308,10 +6325,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
6308
6325
  sql += ` AND m.timestamp >= ?`;
6309
6326
  args.push(options.since);
6310
6327
  }
6311
- if (options?.memoryType) {
6312
- sql += ` AND m.memory_type = ?`;
6313
- args.push(options.memoryType);
6314
- }
6328
+ sql = appendMemoryTypeFilter(sql, args, "m.memory_type", options);
6315
6329
  sql += ` ORDER BY rank LIMIT ?`;
6316
6330
  args.push(limit);
6317
6331
  const result = await client.execute({ sql, args });
@@ -6368,9 +6382,16 @@ async function recentRecords(agentId, options, limit, textFilter) {
6368
6382
  AND timestamp >= ? AND timestamp <= ?
6369
6383
  AND COALESCE(status, 'active') = 'active'
6370
6384
  AND ${options?.includeRaw === false ? "COALESCE(memory_type, 'raw') != 'raw'" : "1 = 1"}
6385
+ ${options?.memoryTypes?.length ? `AND memory_type IN (${options.memoryTypes.map(() => "?").join(",")})` : options?.memoryType ? "AND memory_type = ?" : ""}
6371
6386
  AND COALESCE(confidence, 0.7) >= 0.3
6372
6387
  ORDER BY timestamp DESC LIMIT ?`,
6373
- args: [agentId, windowStart, killedAt, boundarySlots]
6388
+ args: [
6389
+ agentId,
6390
+ windowStart,
6391
+ killedAt,
6392
+ ...options?.memoryTypes?.length ? [...new Set(options.memoryTypes)] : options?.memoryType ? [options.memoryType] : [],
6393
+ boundarySlots
6394
+ ]
6374
6395
  });
6375
6396
  for (const row of boundaryResult.rows) {
6376
6397
  sessionBoundaryMemories.push(rowToMemoryRecord(row));
@@ -6416,10 +6437,7 @@ async function recentRecords(agentId, options, limit, textFilter) {
6416
6437
  sql += ` AND timestamp >= ?`;
6417
6438
  args.push(options.since);
6418
6439
  }
6419
- if (options?.memoryType) {
6420
- sql += ` AND memory_type = ?`;
6421
- args.push(options.memoryType);
6422
- }
6440
+ sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
6423
6441
  if (textFilter) {
6424
6442
  sql += ` AND raw_text LIKE '%' || ? || '%'`;
6425
6443
  args.push(textFilter);
@@ -7443,10 +7461,145 @@ var init_git_staleness = __esm({
7443
7461
  }
7444
7462
  });
7445
7463
 
7464
+ // src/lib/identity.ts
7465
+ var identity_exports = {};
7466
+ __export(identity_exports, {
7467
+ getIdentity: () => getIdentity,
7468
+ getIdentityInjection: () => getIdentityInjection,
7469
+ identityPath: () => identityPath,
7470
+ listIdentities: () => listIdentities,
7471
+ updateIdentity: () => updateIdentity
7472
+ });
7473
+ import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "fs";
7474
+ import { readdirSync as readdirSync5 } from "fs";
7475
+ import path19 from "path";
7476
+ import { createHash as createHash2 } from "crypto";
7477
+ function ensureDir() {
7478
+ if (!existsSync15(IDENTITY_DIR2)) {
7479
+ mkdirSync7(IDENTITY_DIR2, { recursive: true });
7480
+ }
7481
+ }
7482
+ function identityPath(agentId) {
7483
+ return path19.join(IDENTITY_DIR2, `${agentId}.md`);
7484
+ }
7485
+ function parseFrontmatter(raw) {
7486
+ const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
7487
+ if (!match) {
7488
+ return {
7489
+ frontmatter: {
7490
+ role: "unknown",
7491
+ title: "Unknown",
7492
+ agent_id: "unknown",
7493
+ org_level: "specialist",
7494
+ created_by: "system",
7495
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
7496
+ },
7497
+ body: raw
7498
+ };
7499
+ }
7500
+ const yamlStr = match[1];
7501
+ const body = match[2].trim();
7502
+ const fm = {};
7503
+ for (const line of yamlStr.split("\n")) {
7504
+ const kv = line.match(/^(\w+):\s*(.+)$/);
7505
+ if (kv) fm[kv[1]] = kv[2].trim();
7506
+ }
7507
+ return {
7508
+ frontmatter: {
7509
+ role: fm.role ?? "unknown",
7510
+ title: fm.title ?? "Unknown",
7511
+ agent_id: fm.agent_id ?? "unknown",
7512
+ org_level: fm.org_level ?? "specialist",
7513
+ created_by: fm.created_by ?? "system",
7514
+ updated_at: fm.updated_at ?? (/* @__PURE__ */ new Date()).toISOString()
7515
+ },
7516
+ body
7517
+ };
7518
+ }
7519
+ function contentHash(content) {
7520
+ return createHash2("sha256").update(content).digest("hex").slice(0, 16);
7521
+ }
7522
+ function getIdentity(agentId) {
7523
+ const filePath = identityPath(agentId);
7524
+ if (!existsSync15(filePath)) return null;
7525
+ const raw = readFileSync12(filePath, "utf-8");
7526
+ const { frontmatter, body } = parseFrontmatter(raw);
7527
+ return {
7528
+ agentId,
7529
+ frontmatter,
7530
+ body,
7531
+ raw,
7532
+ contentHash: contentHash(raw)
7533
+ };
7534
+ }
7535
+ async function updateIdentity(agentId, content, updatedBy) {
7536
+ ensureDir();
7537
+ const filePath = identityPath(agentId);
7538
+ const hash = contentHash(content);
7539
+ writeFileSync8(filePath, content, "utf-8");
7540
+ try {
7541
+ const client = getClient();
7542
+ await client.execute({
7543
+ sql: `INSERT INTO identity (agent_id, content_hash, updated_at, updated_by)
7544
+ VALUES (?, ?, ?, ?)
7545
+ ON CONFLICT(agent_id) DO UPDATE SET
7546
+ content_hash = excluded.content_hash,
7547
+ updated_at = excluded.updated_at,
7548
+ updated_by = excluded.updated_by`,
7549
+ args: [agentId, hash, (/* @__PURE__ */ new Date()).toISOString(), updatedBy]
7550
+ });
7551
+ } catch {
7552
+ }
7553
+ }
7554
+ function listIdentities() {
7555
+ ensureDir();
7556
+ const files = readdirSync5(IDENTITY_DIR2).filter((f) => f.endsWith(".md"));
7557
+ const results = [];
7558
+ for (const file of files) {
7559
+ const agentId = file.replace(".md", "");
7560
+ const identity = getIdentity(agentId);
7561
+ if (!identity) continue;
7562
+ const lines = identity.body.split("\n").filter((l) => l.trim() && !l.startsWith("#"));
7563
+ const summary = lines[0]?.trim().slice(0, 120) ?? identity.frontmatter.title;
7564
+ results.push({
7565
+ agentId,
7566
+ title: `${identity.frontmatter.title} (${identity.frontmatter.role.toUpperCase()})`,
7567
+ summary
7568
+ });
7569
+ }
7570
+ return results;
7571
+ }
7572
+ function getIdentityInjection(agentId) {
7573
+ const own = getIdentity(agentId);
7574
+ const all = listIdentities();
7575
+ const parts = [];
7576
+ if (own) {
7577
+ parts.push(`## Your Identity (exe.md)
7578
+ These define WHO YOU ARE. Non-negotiable. Permanent.
7579
+
7580
+ ${own.body}`);
7581
+ }
7582
+ const teamLines = all.filter((a) => a.agentId !== agentId).map((a) => `- ${a.agentId} (${a.title}): ${a.summary}`);
7583
+ if (teamLines.length > 0) {
7584
+ parts.push(`## Team Identities
7585
+ ${teamLines.join("\n")}`);
7586
+ }
7587
+ return parts.join("\n\n");
7588
+ }
7589
+ var IDENTITY_DIR2;
7590
+ var init_identity = __esm({
7591
+ "src/lib/identity.ts"() {
7592
+ "use strict";
7593
+ init_config();
7594
+ init_database();
7595
+ IDENTITY_DIR2 = path19.join(EXE_AI_DIR, "identity");
7596
+ }
7597
+ });
7598
+
7446
7599
  // src/adapters/claude/hooks/session-start.ts
7447
7600
  init_config();
7448
- import path19 from "path";
7449
- import { unlinkSync as unlinkSync4, existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
7601
+ import path20 from "path";
7602
+ import { unlinkSync as unlinkSync4, existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
7450
7603
  if (!process.env.AGENT_ID) {
7451
7604
  process.env.AGENT_ID = "default";
7452
7605
  process.env.AGENT_ROLE = "employee";
@@ -7479,8 +7632,8 @@ process.stdin.on("end", async () => {
7479
7632
  const sessionScope = getCurrentSessionScope2();
7480
7633
  if (source === "startup") {
7481
7634
  try {
7482
- const undefinedPath = path19.join(
7483
- process.env.EXE_OS_DIR ?? path19.join(process.env.HOME ?? "", ".exe-os"),
7635
+ const undefinedPath = path20.join(
7636
+ process.env.EXE_OS_DIR ?? path20.join(process.env.HOME ?? "", ".exe-os"),
7484
7637
  "session-cache",
7485
7638
  "active-agent-undefined.json"
7486
7639
  );
@@ -7494,13 +7647,13 @@ process.stdin.on("end", async () => {
7494
7647
  const agentId = agent.agentId;
7495
7648
  if (agentId !== "default") {
7496
7649
  try {
7497
- const cacheDir = path19.join(
7498
- process.env.EXE_OS_DIR ?? path19.join(process.env.HOME ?? "", ".exe-os"),
7650
+ const cacheDir = path20.join(
7651
+ process.env.EXE_OS_DIR ?? path20.join(process.env.HOME ?? "", ".exe-os"),
7499
7652
  "session-cache"
7500
7653
  );
7501
- const markerPath = path19.join(cacheDir, `current-task-${agentId}.json`);
7502
- if (existsSync15(markerPath)) {
7503
- const marker = JSON.parse(readFileSync12(markerPath, "utf-8"));
7654
+ const markerPath = path20.join(cacheDir, `current-task-${agentId}.json`);
7655
+ if (existsSync16(markerPath)) {
7656
+ const marker = JSON.parse(readFileSync13(markerPath, "utf-8"));
7504
7657
  if (marker.taskId) {
7505
7658
  const client = getClient2();
7506
7659
  const markerScope = sessionScopeFilter2(sessionScope);
@@ -7608,6 +7761,22 @@ flow, so bug fixes are fast.`;
7608
7761
  } catch {
7609
7762
  }
7610
7763
  let additionalContext = "";
7764
+ if (agentId !== "default") {
7765
+ try {
7766
+ const { getIdentity: getIdentity2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
7767
+ const identity = getIdentity2(agentId);
7768
+ if (identity) {
7769
+ const title = identity.frontmatter.title ?? identity.frontmatter.role ?? agentId;
7770
+ const role = identity.frontmatter.role ?? "";
7771
+ const firstLine = identity.body.split("\n").find((l) => l.trim().length > 0) ?? "";
7772
+ additionalContext += `## Your Identity
7773
+ You are **${title}**${role ? ` (${role})` : ""}. ${firstLine}
7774
+
7775
+ `;
7776
+ }
7777
+ } catch {
7778
+ }
7779
+ }
7611
7780
  if (memories.length > 0) {
7612
7781
  const brief = memories.map(
7613
7782
  (m) => `[${m.timestamp}] ${m.tool_name}: ${m.raw_text.slice(0, 200)}`
@@ -3644,6 +3644,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
3644
3644
  }
3645
3645
  }
3646
3646
  function schedulePostWriteMemoryHygiene(memoryIds) {
3647
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
3647
3648
  if (memoryIds.length === 0) return;
3648
3649
  const run = () => {
3649
3650
  void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
@@ -4038,7 +4039,7 @@ var init_platform_procedures = __esm({
4038
4039
  title: "Chain of command \u2014 who talks to whom",
4039
4040
  domain: "workflow",
4040
4041
  priority: "p0",
4041
- 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."
4042
+ 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."
4042
4043
  },
4043
4044
  {
4044
4045
  title: "Single dispatch path \u2014 create_task only",
@@ -4710,7 +4711,11 @@ async function searchMemories(queryVector, agentId, options) {
4710
4711
  sql += ` AND timestamp >= ?`;
4711
4712
  args.push(options.since);
4712
4713
  }
4713
- if (options?.memoryType) {
4714
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
4715
+ const uniqueTypes = [...new Set(options.memoryTypes)];
4716
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
4717
+ args.push(...uniqueTypes);
4718
+ } else if (options?.memoryType) {
4714
4719
  sql += ` AND memory_type = ?`;
4715
4720
  args.push(options.memoryType);
4716
4721
  }