@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
@@ -3085,7 +3085,7 @@ var init_platform_procedures = __esm({
3085
3085
  title: "Chain of command \u2014 who talks to whom",
3086
3086
  domain: "workflow",
3087
3087
  priority: "p0",
3088
- 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."
3088
+ 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."
3089
3089
  },
3090
3090
  {
3091
3091
  title: "Single dispatch path \u2014 create_task only",
@@ -3718,6 +3718,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
3718
3718
  }
3719
3719
  }
3720
3720
  function schedulePostWriteMemoryHygiene(memoryIds) {
3721
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
3721
3722
  if (memoryIds.length === 0) return;
3722
3723
  const run = () => {
3723
3724
  void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
@@ -3085,7 +3085,7 @@ var init_platform_procedures = __esm({
3085
3085
  title: "Chain of command \u2014 who talks to whom",
3086
3086
  domain: "workflow",
3087
3087
  priority: "p0",
3088
- 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."
3088
+ 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."
3089
3089
  },
3090
3090
  {
3091
3091
  title: "Single dispatch path \u2014 create_task only",
@@ -3717,6 +3717,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
3717
3717
  }
3718
3718
  }
3719
3719
  function schedulePostWriteMemoryHygiene(memoryIds) {
3720
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
3720
3721
  if (memoryIds.length === 0) return;
3721
3722
  const run = () => {
3722
3723
  void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
@@ -3081,7 +3081,7 @@ var init_platform_procedures = __esm({
3081
3081
  title: "Chain of command \u2014 who talks to whom",
3082
3082
  domain: "workflow",
3083
3083
  priority: "p0",
3084
- 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."
3084
+ 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."
3085
3085
  },
3086
3086
  {
3087
3087
  title: "Single dispatch path \u2014 create_task only",
@@ -3207,6 +3207,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
3207
3207
  }
3208
3208
  }
3209
3209
  function schedulePostWriteMemoryHygiene(memoryIds) {
3210
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
3210
3211
  if (memoryIds.length === 0) return;
3211
3212
  const run = () => {
3212
3213
  void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
@@ -3601,7 +3602,7 @@ var init_platform_procedures = __esm({
3601
3602
  title: "Chain of command \u2014 who talks to whom",
3602
3603
  domain: "workflow",
3603
3604
  priority: "p0",
3604
- content: "Founder -> COO -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the COO does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
3605
+ content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
3605
3606
  },
3606
3607
  {
3607
3608
  title: "Single dispatch path \u2014 create_task only",
@@ -4273,7 +4274,11 @@ async function searchMemories(queryVector, agentId, options) {
4273
4274
  sql += ` AND timestamp >= ?`;
4274
4275
  args.push(options.since);
4275
4276
  }
4276
- if (options?.memoryType) {
4277
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
4278
+ const uniqueTypes = [...new Set(options.memoryTypes)];
4279
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
4280
+ args.push(...uniqueTypes);
4281
+ } else if (options?.memoryType) {
4277
4282
  sql += ` AND memory_type = ?`;
4278
4283
  args.push(options.memoryType);
4279
4284
  }
package/dist/bin/cli.js CHANGED
@@ -824,6 +824,7 @@ __export(installer_exports, {
824
824
  });
825
825
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
826
826
  import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync4, copyFileSync, mkdirSync as mkdirSync3 } from "fs";
827
+ import { createHash } from "crypto";
827
828
  import path6 from "path";
828
829
  import os5 from "os";
829
830
  import { execSync as execSync2 } from "child_process";
@@ -1508,7 +1509,30 @@ function setupTmux(home) {
1508
1509
  return;
1509
1510
  }
1510
1511
  mkdirSync3(exeDir, { recursive: true });
1511
- copyFileSync(assetPath, exeTmuxConf);
1512
+ if (existsSync7(exeTmuxConf)) {
1513
+ const currentContent = readFileSync5(exeTmuxConf, "utf8");
1514
+ const newContent = readFileSync5(assetPath, "utf8");
1515
+ const currentHash = createHash("sha256").update(currentContent).digest("hex");
1516
+ const newHash = createHash("sha256").update(newContent).digest("hex");
1517
+ if (currentHash !== newHash) {
1518
+ const shippedPath = path6.join(exeDir, ".tmux.conf.shipped-hash");
1519
+ const lastShippedHash = existsSync7(shippedPath) ? readFileSync5(shippedPath, "utf8").trim() : "";
1520
+ if (lastShippedHash && currentHash !== lastShippedHash) {
1521
+ process.stderr.write("exe-os: tmux config has user customizations \u2014 skipping overwrite\n");
1522
+ } else {
1523
+ copyFileSync(assetPath, exeTmuxConf);
1524
+ process.stderr.write("exe-os: tmux config updated\n");
1525
+ }
1526
+ writeFileSync4(shippedPath, newHash, "utf8");
1527
+ } else {
1528
+ process.stderr.write("exe-os: tmux config already up to date\n");
1529
+ }
1530
+ } else {
1531
+ copyFileSync(assetPath, exeTmuxConf);
1532
+ const newContent = readFileSync5(assetPath, "utf8");
1533
+ const newHash = createHash("sha256").update(newContent).digest("hex");
1534
+ writeFileSync4(path6.join(exeDir, ".tmux.conf.shipped-hash"), newHash, "utf8");
1535
+ }
1512
1536
  if (existsSync7(userTmuxConf)) {
1513
1537
  const existing = readFileSync5(userTmuxConf, "utf8");
1514
1538
  if (!existing.includes(sourceLine)) {
@@ -6803,7 +6827,7 @@ var init_state_bus = __esm({
6803
6827
  });
6804
6828
 
6805
6829
  // src/lib/memory-write-governor.ts
6806
- import { createHash } from "crypto";
6830
+ import { createHash as createHash2 } from "crypto";
6807
6831
  function normalizeMemoryText(text) {
6808
6832
  return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").replace(/\n{4,}/g, "\n\n\n").trim();
6809
6833
  }
@@ -6835,7 +6859,7 @@ function shouldSkipEmbedding(input) {
6835
6859
  return false;
6836
6860
  }
6837
6861
  function hashMemoryContent(text) {
6838
- return createHash("sha256").update(normalizeMemoryText(text)).digest("hex");
6862
+ return createHash2("sha256").update(normalizeMemoryText(text)).digest("hex");
6839
6863
  }
6840
6864
  function scopedDedupArgs(input) {
6841
6865
  return [input.contentHash, input.agentId, input.projectName, input.memoryType];
@@ -6957,6 +6981,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
6957
6981
  }
6958
6982
  }
6959
6983
  function schedulePostWriteMemoryHygiene(memoryIds) {
6984
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
6960
6985
  if (memoryIds.length === 0) return;
6961
6986
  const run = () => {
6962
6987
  void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
@@ -7351,7 +7376,7 @@ var init_platform_procedures = __esm({
7351
7376
  title: "Chain of command \u2014 who talks to whom",
7352
7377
  domain: "workflow",
7353
7378
  priority: "p0",
7354
- 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."
7379
+ 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."
7355
7380
  },
7356
7381
  {
7357
7382
  title: "Single dispatch path \u2014 create_task only",
@@ -8023,7 +8048,11 @@ async function searchMemories(queryVector, agentId, options) {
8023
8048
  sql += ` AND timestamp >= ?`;
8024
8049
  args2.push(options.since);
8025
8050
  }
8026
- if (options?.memoryType) {
8051
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
8052
+ const uniqueTypes = [...new Set(options.memoryTypes)];
8053
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
8054
+ args2.push(...uniqueTypes);
8055
+ } else if (options?.memoryType) {
8027
8056
  sql += ` AND memory_type = ?`;
8028
8057
  args2.push(options.memoryType);
8029
8058
  }
@@ -9156,7 +9185,7 @@ __export(identity_exports, {
9156
9185
  import { existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
9157
9186
  import { readdirSync as readdirSync4 } from "fs";
9158
9187
  import path17 from "path";
9159
- import { createHash as createHash2 } from "crypto";
9188
+ import { createHash as createHash3 } from "crypto";
9160
9189
  function ensureDir() {
9161
9190
  if (!existsSync16(IDENTITY_DIR2)) {
9162
9191
  mkdirSync9(IDENTITY_DIR2, { recursive: true });
@@ -9200,7 +9229,7 @@ function parseFrontmatter(raw) {
9200
9229
  };
9201
9230
  }
9202
9231
  function contentHash(content) {
9203
- return createHash2("sha256").update(content).digest("hex").slice(0, 16);
9232
+ return createHash3("sha256").update(content).digest("hex").slice(0, 16);
9204
9233
  }
9205
9234
  function getIdentity(agentId) {
9206
9235
  const filePath = identityPath(agentId);
@@ -13797,8 +13826,9 @@ function personalizePrompt(prompt, templateName, actualName) {
13797
13826
  return prompt.replace(new RegExp(`\\bYou are ${escaped}\\b`, "g"), `You are ${actualName}`);
13798
13827
  }
13799
13828
  function renderClientCOOTemplate(vars) {
13829
+ const resolved = { ...vars, title: vars.title || "Chief Operating Officer" };
13800
13830
  for (const key of CLIENT_COO_PLACEHOLDERS) {
13801
- const value = vars[key];
13831
+ const value = resolved[key];
13802
13832
  if (typeof value !== "string" || value.length === 0) {
13803
13833
  throw new Error(
13804
13834
  `renderClientCOOTemplate: missing required variable "${key}"`
@@ -13807,7 +13837,7 @@ function renderClientCOOTemplate(vars) {
13807
13837
  }
13808
13838
  let out = CLIENT_COO_TEMPLATE;
13809
13839
  for (const key of CLIENT_COO_PLACEHOLDERS) {
13810
- out = out.split(`{{${key}}}`).join(vars[key]);
13840
+ out = out.split(`{{${key}}}`).join(resolved[key]);
13811
13841
  }
13812
13842
  if (vars.industry_context) {
13813
13843
  out += "\n" + vars.industry_context;
@@ -14284,7 +14314,7 @@ created_by: system
14284
14314
  ---
14285
14315
  ## Identity
14286
14316
 
14287
- You are {{agent_name}}, the Chief Operating Officer at {{company_name}}.
14317
+ You are {{agent_name}}, the {{title}} at {{company_name}}.
14288
14318
 
14289
14319
  You are {{founder_name}}'s most reliable teammate in business \u2014 the knowledgeable older sibling who has been through it all. You have seen projects succeed and fail. You know what matters and what is noise. You do not get anxious about problems; you see them coming, stay calm, and handle them.
14290
14320
 
@@ -14371,7 +14401,8 @@ All memory, tasks, behaviors, documents, and wiki content belonging to {{company
14371
14401
  CLIENT_COO_PLACEHOLDERS = [
14372
14402
  "agent_name",
14373
14403
  "company_name",
14374
- "founder_name"
14404
+ "founder_name",
14405
+ "title"
14375
14406
  ];
14376
14407
  }
14377
14408
  });
@@ -14579,7 +14610,7 @@ var init_exe_rename = __esm({
14579
14610
  // src/lib/model-downloader.ts
14580
14611
  import { createWriteStream, createReadStream as createReadStream2, existsSync as existsSync26, unlinkSync as unlinkSync12, renameSync as renameSync5 } from "fs";
14581
14612
  import { mkdir as mkdir6 } from "fs/promises";
14582
- import { createHash as createHash3 } from "crypto";
14613
+ import { createHash as createHash4 } from "crypto";
14583
14614
  import path32 from "path";
14584
14615
  async function downloadModel(opts) {
14585
14616
  const { destDir, onProgress, fetchFn = globalThis.fetch } = opts;
@@ -14607,7 +14638,7 @@ async function downloadModel(opts) {
14607
14638
  throw new Error(`Download failed: HTTP ${response.status}`);
14608
14639
  }
14609
14640
  const contentLength = Number(response.headers.get("content-length") ?? EXPECTED_SIZE);
14610
- const hash = createHash3("sha256");
14641
+ const hash = createHash4("sha256");
14611
14642
  const fileStream = createWriteStream(tmpPath);
14612
14643
  const reader = response.body.getReader();
14613
14644
  try {
@@ -14651,7 +14682,7 @@ Download attempt ${attempt} failed, retrying...
14651
14682
  }
14652
14683
  async function fileHash(filePath) {
14653
14684
  return new Promise((resolve, reject) => {
14654
- const hash = createHash3("sha256");
14685
+ const hash = createHash4("sha256");
14655
14686
  const stream = createReadStream2(filePath);
14656
14687
  stream.on("data", (chunk) => hash.update(chunk));
14657
14688
  stream.on("end", () => resolve(hash.digest("hex")));
@@ -1323,7 +1323,7 @@ var PLATFORM_PROCEDURES = [
1323
1323
  title: "Chain of command \u2014 who talks to whom",
1324
1324
  domain: "workflow",
1325
1325
  priority: "p0",
1326
- 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."
1326
+ 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."
1327
1327
  },
1328
1328
  {
1329
1329
  title: "Single dispatch path \u2014 create_task only",
@@ -3095,7 +3095,7 @@ var init_platform_procedures = __esm({
3095
3095
  title: "Chain of command \u2014 who talks to whom",
3096
3096
  domain: "workflow",
3097
3097
  priority: "p0",
3098
- 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."
3098
+ 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."
3099
3099
  },
3100
3100
  {
3101
3101
  title: "Single dispatch path \u2014 create_task only",
@@ -3865,6 +3865,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
3865
3865
  }
3866
3866
  }
3867
3867
  function schedulePostWriteMemoryHygiene(memoryIds) {
3868
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
3868
3869
  if (memoryIds.length === 0) return;
3869
3870
  const run = () => {
3870
3871
  void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
@@ -4323,7 +4324,11 @@ async function searchMemories(queryVector, agentId, options) {
4323
4324
  sql += ` AND timestamp >= ?`;
4324
4325
  args.push(options.since);
4325
4326
  }
4326
- if (options?.memoryType) {
4327
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
4328
+ const uniqueTypes = [...new Set(options.memoryTypes)];
4329
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
4330
+ args.push(...uniqueTypes);
4331
+ } else if (options?.memoryType) {
4327
4332
  sql += ` AND memory_type = ?`;
4328
4333
  args.push(options.memoryType);
4329
4334
  }
@@ -2914,7 +2914,7 @@ var init_platform_procedures = __esm({
2914
2914
  title: "Chain of command \u2014 who talks to whom",
2915
2915
  domain: "workflow",
2916
2916
  priority: "p0",
2917
- 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."
2917
+ 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."
2918
2918
  },
2919
2919
  {
2920
2920
  title: "Single dispatch path \u2014 create_task only",
@@ -581,7 +581,7 @@ var init_platform_procedures = __esm({
581
581
  title: "Chain of command \u2014 who talks to whom",
582
582
  domain: "workflow",
583
583
  priority: "p0",
584
- 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."
584
+ 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."
585
585
  },
586
586
  {
587
587
  title: "Single dispatch path \u2014 create_task only",
@@ -786,8 +786,9 @@ function personalizePrompt(prompt, templateName, actualName) {
786
786
  return prompt.replace(new RegExp(`\\bYou are ${escaped}\\b`, "g"), `You are ${actualName}`);
787
787
  }
788
788
  function renderClientCOOTemplate(vars) {
789
+ const resolved = { ...vars, title: vars.title || "Chief Operating Officer" };
789
790
  for (const key of CLIENT_COO_PLACEHOLDERS) {
790
- const value = vars[key];
791
+ const value = resolved[key];
791
792
  if (typeof value !== "string" || value.length === 0) {
792
793
  throw new Error(
793
794
  `renderClientCOOTemplate: missing required variable "${key}"`
@@ -796,7 +797,7 @@ function renderClientCOOTemplate(vars) {
796
797
  }
797
798
  let out = CLIENT_COO_TEMPLATE;
798
799
  for (const key of CLIENT_COO_PLACEHOLDERS) {
799
- out = out.split(`{{${key}}}`).join(vars[key]);
800
+ out = out.split(`{{${key}}}`).join(resolved[key]);
800
801
  }
801
802
  if (vars.industry_context) {
802
803
  out += "\n" + vars.industry_context;
@@ -1273,7 +1274,7 @@ created_by: system
1273
1274
  ---
1274
1275
  ## Identity
1275
1276
 
1276
- You are {{agent_name}}, the Chief Operating Officer at {{company_name}}.
1277
+ You are {{agent_name}}, the {{title}} at {{company_name}}.
1277
1278
 
1278
1279
  You are {{founder_name}}'s most reliable teammate in business \u2014 the knowledgeable older sibling who has been through it all. You have seen projects succeed and fail. You know what matters and what is noise. You do not get anxious about problems; you see them coming, stay calm, and handle them.
1279
1280
 
@@ -1360,7 +1361,8 @@ All memory, tasks, behaviors, documents, and wiki content belonging to {{company
1360
1361
  CLIENT_COO_PLACEHOLDERS = [
1361
1362
  "agent_name",
1362
1363
  "company_name",
1363
- "founder_name"
1364
+ "founder_name",
1365
+ "title"
1364
1366
  ];
1365
1367
  }
1366
1368
  });
@@ -6880,6 +6880,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
6880
6880
  }
6881
6881
  }
6882
6882
  function schedulePostWriteMemoryHygiene(memoryIds) {
6883
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
6883
6884
  if (memoryIds.length === 0) return;
6884
6885
  const run = () => {
6885
6886
  void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
@@ -7274,7 +7275,7 @@ var init_platform_procedures = __esm({
7274
7275
  title: "Chain of command \u2014 who talks to whom",
7275
7276
  domain: "workflow",
7276
7277
  priority: "p0",
7277
- 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."
7278
+ 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."
7278
7279
  },
7279
7280
  {
7280
7281
  title: "Single dispatch path \u2014 create_task only",
@@ -7946,7 +7947,11 @@ async function searchMemories(queryVector, agentId, options) {
7946
7947
  sql += ` AND timestamp >= ?`;
7947
7948
  args.push(options.since);
7948
7949
  }
7949
- if (options?.memoryType) {
7950
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
7951
+ const uniqueTypes = [...new Set(options.memoryTypes)];
7952
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
7953
+ args.push(...uniqueTypes);
7954
+ } else if (options?.memoryType) {
7950
7955
  sql += ` AND memory_type = ?`;
7951
7956
  args.push(options.memoryType);
7952
7957
  }
@@ -2945,7 +2945,7 @@ var init_platform_procedures = __esm({
2945
2945
  title: "Chain of command \u2014 who talks to whom",
2946
2946
  domain: "workflow",
2947
2947
  priority: "p0",
2948
- 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."
2948
+ 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."
2949
2949
  },
2950
2950
  {
2951
2951
  title: "Single dispatch path \u2014 create_task only",
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
6
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
7
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
8
  }) : x)(function(x) {
@@ -14,6 +16,15 @@ var __export = (target, all) => {
14
16
  for (var name in all)
15
17
  __defProp(target, name, { get: all[name], enumerable: true });
16
18
  };
19
+ var __copyProps = (to, from, except, desc) => {
20
+ if (from && typeof from === "object" || typeof from === "function") {
21
+ for (let key of __getOwnPropNames(from))
22
+ if (!__hasOwnProp.call(to, key) && key !== except)
23
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
24
+ }
25
+ return to;
26
+ };
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
17
28
 
18
29
  // src/types/memory.ts
19
30
  var EMBEDDING_DIM;
@@ -118,6 +129,21 @@ var init_secure_files = __esm({
118
129
  });
119
130
 
120
131
  // src/lib/config.ts
132
+ var config_exports = {};
133
+ __export(config_exports, {
134
+ CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
135
+ CONFIG_PATH: () => CONFIG_PATH,
136
+ CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
137
+ DB_PATH: () => DB_PATH,
138
+ EXE_AI_DIR: () => EXE_AI_DIR,
139
+ LEGACY_LANCE_PATH: () => LEGACY_LANCE_PATH,
140
+ MODELS_DIR: () => MODELS_DIR,
141
+ loadConfig: () => loadConfig,
142
+ loadConfigFrom: () => loadConfigFrom,
143
+ loadConfigSync: () => loadConfigSync,
144
+ migrateConfig: () => migrateConfig,
145
+ saveConfig: () => saveConfig
146
+ });
121
147
  import { readFile, writeFile } from "fs/promises";
122
148
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
123
149
  import path from "path";
@@ -221,6 +247,46 @@ async function loadConfig() {
221
247
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
222
248
  }
223
249
  }
250
+ function loadConfigSync() {
251
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
252
+ const configPath = path.join(dir, "config.json");
253
+ if (!existsSync2(configPath)) {
254
+ return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
255
+ }
256
+ try {
257
+ const raw = readFileSync(configPath, "utf-8");
258
+ let parsed = JSON.parse(raw);
259
+ parsed = migrateLegacyConfig(parsed);
260
+ const { config: migratedCfg } = migrateConfig(parsed);
261
+ normalizeScalingRoadmap(migratedCfg);
262
+ normalizeSessionLifecycle(migratedCfg);
263
+ normalizeAutoUpdate(migratedCfg);
264
+ return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
265
+ } catch {
266
+ return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
267
+ }
268
+ }
269
+ async function saveConfig(config) {
270
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
271
+ await ensurePrivateDir(dir);
272
+ const configPath = path.join(dir, "config.json");
273
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
274
+ await enforcePrivateFile(configPath);
275
+ }
276
+ async function loadConfigFrom(configPath) {
277
+ const raw = await readFile(configPath, "utf-8");
278
+ try {
279
+ let parsed = JSON.parse(raw);
280
+ parsed = migrateLegacyConfig(parsed);
281
+ const { config: migratedCfg } = migrateConfig(parsed);
282
+ normalizeScalingRoadmap(migratedCfg);
283
+ normalizeSessionLifecycle(migratedCfg);
284
+ normalizeAutoUpdate(migratedCfg);
285
+ return { ...DEFAULT_CONFIG, ...migratedCfg };
286
+ } catch {
287
+ return { ...DEFAULT_CONFIG };
288
+ }
289
+ }
224
290
  var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
225
291
  var init_config = __esm({
226
292
  "src/lib/config.ts"() {
@@ -3196,6 +3262,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
3196
3262
  }
3197
3263
  }
3198
3264
  function schedulePostWriteMemoryHygiene(memoryIds) {
3265
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
3199
3266
  if (memoryIds.length === 0) return;
3200
3267
  const run = () => {
3201
3268
  void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
@@ -3590,7 +3657,7 @@ var init_platform_procedures = __esm({
3590
3657
  title: "Chain of command \u2014 who talks to whom",
3591
3658
  domain: "workflow",
3592
3659
  priority: "p0",
3593
- 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."
3660
+ 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."
3594
3661
  },
3595
3662
  {
3596
3663
  title: "Single dispatch path \u2014 create_task only",
@@ -4262,7 +4329,11 @@ async function searchMemories(queryVector, agentId2, options) {
4262
4329
  sql += ` AND timestamp >= ?`;
4263
4330
  args.push(options.since);
4264
4331
  }
4265
- if (options?.memoryType) {
4332
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
4333
+ const uniqueTypes = [...new Set(options.memoryTypes)];
4334
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
4335
+ args.push(...uniqueTypes);
4336
+ } else if (options?.memoryType) {
4266
4337
  sql += ` AND memory_type = ?`;
4267
4338
  args.push(options.memoryType);
4268
4339
  }
@@ -4529,7 +4600,16 @@ var BEHAVIORS_EXPORT_DIR = path8.join(
4529
4600
  "behaviors-export"
4530
4601
  );
4531
4602
  var STALE_EXPORT_AGE_MS = 60 * 60 * 1e3;
4532
- var EXPORT_BEHAVIOR_LIMIT = 30;
4603
+ var DEFAULT_BEHAVIOR_LIMIT = 30;
4604
+ function getBehaviorLimit() {
4605
+ try {
4606
+ const { loadConfigSync: loadConfigSync2 } = (init_config(), __toCommonJS(config_exports));
4607
+ const cfg = loadConfigSync2();
4608
+ return cfg.behaviorExportLimit ?? DEFAULT_BEHAVIOR_LIMIT;
4609
+ } catch {
4610
+ return DEFAULT_BEHAVIOR_LIMIT;
4611
+ }
4612
+ }
4533
4613
  function sweepStaleBehaviorExports(now = Date.now()) {
4534
4614
  if (!existsSync8(BEHAVIORS_EXPORT_DIR)) return;
4535
4615
  let entries;
@@ -4583,7 +4663,7 @@ function exportFilePath(agentId2, projectName2, sessionKey2) {
4583
4663
  async function exportBehaviorsForAgent(agentId2, projectName2, sessionKey2) {
4584
4664
  mkdirSync3(BEHAVIORS_EXPORT_DIR, { recursive: true });
4585
4665
  sweepStaleBehaviorExports();
4586
- const behaviors = await listBehaviors(agentId2, projectName2, EXPORT_BEHAVIOR_LIMIT);
4666
+ const behaviors = await listBehaviors(agentId2, projectName2, getBehaviorLimit());
4587
4667
  if (behaviors.length === 0) return null;
4588
4668
  const body = renderBehaviorExport(behaviors);
4589
4669
  const target = exportFilePath(agentId2, projectName2, sessionKey2);
@@ -3196,6 +3196,7 @@ async function runPostWriteMemoryHygiene(memoryId) {
3196
3196
  }
3197
3197
  }
3198
3198
  function schedulePostWriteMemoryHygiene(memoryIds) {
3199
+ if (process.env.EXE_SKIP_MEMORY_HYGIENE === "1") return;
3199
3200
  if (memoryIds.length === 0) return;
3200
3201
  const run = () => {
3201
3202
  void Promise.all(memoryIds.map((id) => runPostWriteMemoryHygiene(id)));
@@ -3590,7 +3591,7 @@ var init_platform_procedures = __esm({
3590
3591
  title: "Chain of command \u2014 who talks to whom",
3591
3592
  domain: "workflow",
3592
3593
  priority: "p0",
3593
- 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."
3594
+ 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."
3594
3595
  },
3595
3596
  {
3596
3597
  title: "Single dispatch path \u2014 create_task only",
@@ -4262,7 +4263,11 @@ async function searchMemories(queryVector, agentId, options) {
4262
4263
  sql += ` AND timestamp >= ?`;
4263
4264
  args.push(options.since);
4264
4265
  }
4265
- if (options?.memoryType) {
4266
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
4267
+ const uniqueTypes = [...new Set(options.memoryTypes)];
4268
+ sql += ` AND memory_type IN (${uniqueTypes.map(() => "?").join(",")})`;
4269
+ args.push(...uniqueTypes);
4270
+ } else if (options?.memoryType) {
4266
4271
  sql += ` AND memory_type = ?`;
4267
4272
  args.push(options.memoryType);
4268
4273
  }
@@ -4484,6 +4489,17 @@ init_database();
4484
4489
  // src/lib/hybrid-search.ts
4485
4490
  init_store();
4486
4491
  init_database();
4492
+ function appendMemoryTypeFilter(sql, args, column, options) {
4493
+ if (options?.memoryTypes && options.memoryTypes.length > 0) {
4494
+ const uniqueTypes = [...new Set(options.memoryTypes)];
4495
+ sql += ` AND ${column} IN (${uniqueTypes.map(() => "?").join(",")})`;
4496
+ args.push(...uniqueTypes);
4497
+ } else if (options?.memoryType) {
4498
+ sql += ` AND ${column} = ?`;
4499
+ args.push(options.memoryType);
4500
+ }
4501
+ return sql;
4502
+ }
4487
4503
  async function lightweightSearch(queryText, agentId, options) {
4488
4504
  const client = getClient();
4489
4505
  const limit = options?.limit ?? 5;
@@ -4551,10 +4567,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
4551
4567
  sql += ` AND m.timestamp >= ?`;
4552
4568
  args.push(options.since);
4553
4569
  }
4554
- if (options?.memoryType) {
4555
- sql += ` AND m.memory_type = ?`;
4556
- args.push(options.memoryType);
4557
- }
4570
+ sql = appendMemoryTypeFilter(sql, args, "m.memory_type", options);
4558
4571
  sql += ` ORDER BY rank LIMIT ?`;
4559
4572
  args.push(limit);
4560
4573
  const result = await client.execute({ sql, args });
@@ -4611,9 +4624,16 @@ async function recentRecords(agentId, options, limit, textFilter) {
4611
4624
  AND timestamp >= ? AND timestamp <= ?
4612
4625
  AND COALESCE(status, 'active') = 'active'
4613
4626
  AND ${options?.includeRaw === false ? "COALESCE(memory_type, 'raw') != 'raw'" : "1 = 1"}
4627
+ ${options?.memoryTypes?.length ? `AND memory_type IN (${options.memoryTypes.map(() => "?").join(",")})` : options?.memoryType ? "AND memory_type = ?" : ""}
4614
4628
  AND COALESCE(confidence, 0.7) >= 0.3
4615
4629
  ORDER BY timestamp DESC LIMIT ?`,
4616
- args: [agentId, windowStart, killedAt, boundarySlots]
4630
+ args: [
4631
+ agentId,
4632
+ windowStart,
4633
+ killedAt,
4634
+ ...options?.memoryTypes?.length ? [...new Set(options.memoryTypes)] : options?.memoryType ? [options.memoryType] : [],
4635
+ boundarySlots
4636
+ ]
4617
4637
  });
4618
4638
  for (const row of boundaryResult.rows) {
4619
4639
  sessionBoundaryMemories.push(rowToMemoryRecord(row));
@@ -4659,10 +4679,7 @@ async function recentRecords(agentId, options, limit, textFilter) {
4659
4679
  sql += ` AND timestamp >= ?`;
4660
4680
  args.push(options.since);
4661
4681
  }
4662
- if (options?.memoryType) {
4663
- sql += ` AND memory_type = ?`;
4664
- args.push(options.memoryType);
4665
- }
4682
+ sql = appendMemoryTypeFilter(sql, args, "memory_type", options);
4666
4683
  if (textFilter) {
4667
4684
  sql += ` AND raw_text LIKE '%' || ? || '%'`;
4668
4685
  args.push(textFilter);