@askexenow/exe-os 0.8.80 → 0.8.82

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 (110) hide show
  1. package/dist/bin/backfill-conversations.js +359 -267
  2. package/dist/bin/backfill-responses.js +357 -265
  3. package/dist/bin/backfill-vectors.js +339 -264
  4. package/dist/bin/cleanup-stale-review-tasks.js +315 -256
  5. package/dist/bin/cli.js +494 -240
  6. package/dist/bin/exe-agent.js +141 -46
  7. package/dist/bin/exe-assign.js +151 -63
  8. package/dist/bin/exe-boot.js +294 -115
  9. package/dist/bin/exe-call.js +76 -51
  10. package/dist/bin/exe-cloud.js +58 -45
  11. package/dist/bin/exe-dispatch.js +434 -277
  12. package/dist/bin/exe-doctor.js +317 -246
  13. package/dist/bin/exe-export-behaviors.js +328 -248
  14. package/dist/bin/exe-forget.js +314 -231
  15. package/dist/bin/exe-gateway.js +2676 -1402
  16. package/dist/bin/exe-heartbeat.js +329 -264
  17. package/dist/bin/exe-kill.js +324 -244
  18. package/dist/bin/exe-launch-agent.js +574 -463
  19. package/dist/bin/exe-link.js +1055 -95
  20. package/dist/bin/exe-new-employee.js +49 -54
  21. package/dist/bin/exe-pending-messages.js +310 -253
  22. package/dist/bin/exe-pending-notifications.js +299 -228
  23. package/dist/bin/exe-pending-reviews.js +314 -245
  24. package/dist/bin/exe-rename.js +259 -195
  25. package/dist/bin/exe-review.js +140 -64
  26. package/dist/bin/exe-search.js +543 -356
  27. package/dist/bin/exe-session-cleanup.js +463 -382
  28. package/dist/bin/exe-settings.js +129 -99
  29. package/dist/bin/exe-start.sh +6 -6
  30. package/dist/bin/exe-status.js +95 -36
  31. package/dist/bin/exe-team.js +116 -51
  32. package/dist/bin/git-sweep.js +482 -307
  33. package/dist/bin/graph-backfill.js +357 -245
  34. package/dist/bin/graph-export.js +324 -244
  35. package/dist/bin/install.js +33 -10
  36. package/dist/bin/scan-tasks.js +481 -307
  37. package/dist/bin/setup.js +1147 -140
  38. package/dist/bin/shard-migrate.js +321 -241
  39. package/dist/bin/update.js +1 -7
  40. package/dist/bin/wiki-sync.js +318 -238
  41. package/dist/gateway/index.js +2656 -1383
  42. package/dist/hooks/bug-report-worker.js +641 -472
  43. package/dist/hooks/commit-complete.js +482 -307
  44. package/dist/hooks/error-recall.js +363 -135
  45. package/dist/hooks/exe-heartbeat-hook.js +97 -27
  46. package/dist/hooks/ingest-worker.js +584 -397
  47. package/dist/hooks/ingest.js +123 -58
  48. package/dist/hooks/instructions-loaded.js +212 -82
  49. package/dist/hooks/notification.js +200 -70
  50. package/dist/hooks/post-compact.js +199 -81
  51. package/dist/hooks/pre-compact.js +352 -140
  52. package/dist/hooks/pre-tool-use.js +416 -278
  53. package/dist/hooks/prompt-ingest-worker.js +376 -299
  54. package/dist/hooks/prompt-submit.js +414 -188
  55. package/dist/hooks/response-ingest-worker.js +408 -338
  56. package/dist/hooks/session-end.js +209 -83
  57. package/dist/hooks/session-start.js +382 -158
  58. package/dist/hooks/stop.js +209 -83
  59. package/dist/hooks/subagent-stop.js +209 -85
  60. package/dist/hooks/summary-worker.js +606 -510
  61. package/dist/index.js +2133 -855
  62. package/dist/lib/cloud-sync.js +1175 -184
  63. package/dist/lib/config.js +1 -9
  64. package/dist/lib/consolidation.js +71 -34
  65. package/dist/lib/database.js +166 -14
  66. package/dist/lib/device-registry.js +189 -117
  67. package/dist/lib/embedder.js +6 -10
  68. package/dist/lib/employee-templates.js +134 -39
  69. package/dist/lib/employees.js +30 -7
  70. package/dist/lib/exe-daemon-client.js +5 -7
  71. package/dist/lib/exe-daemon.js +514 -152
  72. package/dist/lib/hybrid-search.js +543 -356
  73. package/dist/lib/identity-templates.js +15 -15
  74. package/dist/lib/identity.js +19 -15
  75. package/dist/lib/license.js +1 -7
  76. package/dist/lib/messaging.js +157 -135
  77. package/dist/lib/reminders.js +97 -0
  78. package/dist/lib/schedules.js +302 -231
  79. package/dist/lib/skill-learning.js +33 -27
  80. package/dist/lib/status-brief.js +11 -14
  81. package/dist/lib/store.js +326 -237
  82. package/dist/lib/task-router.js +105 -1
  83. package/dist/lib/tasks.js +233 -116
  84. package/dist/lib/tmux-routing.js +173 -56
  85. package/dist/lib/ws-client.js +13 -3
  86. package/dist/mcp/server.js +2009 -1015
  87. package/dist/mcp/tools/complete-reminder.js +97 -0
  88. package/dist/mcp/tools/create-reminder.js +97 -0
  89. package/dist/mcp/tools/create-task.js +426 -262
  90. package/dist/mcp/tools/deactivate-behavior.js +119 -44
  91. package/dist/mcp/tools/list-reminders.js +97 -0
  92. package/dist/mcp/tools/list-tasks.js +56 -57
  93. package/dist/mcp/tools/send-message.js +206 -143
  94. package/dist/mcp/tools/update-task.js +259 -85
  95. package/dist/runtime/index.js +495 -316
  96. package/dist/tui/App.js +1128 -919
  97. package/package.json +2 -10
  98. package/src/commands/exe/afk.md +8 -8
  99. package/src/commands/exe/assign.md +1 -1
  100. package/src/commands/exe/build-adv.md +1 -1
  101. package/src/commands/exe/call.md +10 -10
  102. package/src/commands/exe/employee-heartbeat.md +9 -6
  103. package/src/commands/exe/heartbeat.md +5 -5
  104. package/src/commands/exe/intercom.md +26 -15
  105. package/src/commands/exe/launch.md +2 -2
  106. package/src/commands/exe/new-employee.md +1 -1
  107. package/src/commands/exe/review.md +2 -2
  108. package/src/commands/exe/schedule.md +1 -1
  109. package/src/commands/exe/sessions.md +2 -2
  110. package/src/commands/exe.md +22 -20
@@ -24,7 +24,6 @@ var config_exports = {};
24
24
  __export(config_exports, {
25
25
  CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
26
26
  CONFIG_PATH: () => CONFIG_PATH,
27
- COO_AGENT_NAME: () => COO_AGENT_NAME,
28
27
  CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
29
28
  DB_PATH: () => DB_PATH,
30
29
  EXE_AI_DIR: () => EXE_AI_DIR,
@@ -180,7 +179,7 @@ async function loadConfigFrom(configPath) {
180
179
  return { ...DEFAULT_CONFIG };
181
180
  }
182
181
  }
183
- var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
182
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
184
183
  var init_config = __esm({
185
184
  "src/lib/config.ts"() {
186
185
  "use strict";
@@ -188,7 +187,6 @@ var init_config = __esm({
188
187
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
189
188
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
190
189
  CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
191
- COO_AGENT_NAME = "exe";
192
190
  LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
193
191
  CURRENT_CONFIG_VERSION = 1;
194
192
  DEFAULT_CONFIG = {
@@ -224,13 +222,7 @@ var init_config = __esm({
224
222
  wikiUrl: "",
225
223
  wikiApiKey: "",
226
224
  wikiSyncIntervalMs: 30 * 60 * 1e3,
227
- wikiWorkspaceMapping: {
228
- exe: "Executive",
229
- yoshi: "Engineering",
230
- mari: "Marketing",
231
- tom: "Engineering",
232
- sasha: "Production"
233
- },
225
+ wikiWorkspaceMapping: {},
234
226
  wikiAutoUpdate: true,
235
227
  wikiAutoUpdateThreshold: 0.5,
236
228
  wikiAutoUpdateCreateNew: true,
@@ -315,7 +307,7 @@ function wrapWithRetry(client) {
315
307
  return (sql) => retryOnBusy(() => target.execute(sql), "execute");
316
308
  }
317
309
  if (prop === "batch") {
318
- return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
310
+ return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
319
311
  }
320
312
  return Reflect.get(target, prop, receiver);
321
313
  }
@@ -331,6 +323,53 @@ var init_db_retry = __esm({
331
323
  }
332
324
  });
333
325
 
326
+ // src/lib/employees.ts
327
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
328
+ import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
329
+ import { execSync } from "child_process";
330
+ import path2 from "path";
331
+ import os2 from "os";
332
+ function normalizeRole(role) {
333
+ return (role ?? "").trim().toLowerCase();
334
+ }
335
+ function isCoordinatorRole(role) {
336
+ return normalizeRole(role) === normalizeRole(COORDINATOR_ROLE);
337
+ }
338
+ function getCoordinatorEmployee(employees) {
339
+ return employees.find((e) => isCoordinatorRole(e.role));
340
+ }
341
+ function getCoordinatorName(employees = loadEmployeesSync()) {
342
+ return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
343
+ }
344
+ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
345
+ if (!agentName) return false;
346
+ return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
347
+ }
348
+ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
349
+ return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
350
+ }
351
+ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
352
+ if (!existsSync2(employeesPath)) return [];
353
+ try {
354
+ return JSON.parse(readFileSync2(employeesPath, "utf-8"));
355
+ } catch {
356
+ return [];
357
+ }
358
+ }
359
+ function getEmployee(employees, name) {
360
+ return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
361
+ }
362
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE;
363
+ var init_employees = __esm({
364
+ "src/lib/employees.ts"() {
365
+ "use strict";
366
+ init_config();
367
+ EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
368
+ DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
369
+ COORDINATOR_ROLE = "COO";
370
+ }
371
+ });
372
+
334
373
  // src/lib/database.ts
335
374
  import { createClient } from "@libsql/client";
336
375
  async function initDatabase(config) {
@@ -464,22 +503,24 @@ async function ensureSchema() {
464
503
  ON behaviors(agent_id, active);
465
504
  `);
466
505
  try {
506
+ const coordinatorName = getCoordinatorName();
467
507
  const existing = await client.execute({
468
- sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id = 'exe'",
469
- args: []
508
+ sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id = ?",
509
+ args: [coordinatorName]
470
510
  });
471
511
  if (Number(existing.rows[0]?.cnt) === 0) {
472
- await client.executeMultiple(`
473
- INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
474
- VALUES
475
- (hex(randomblob(16)), 'exe', NULL, 'workflow', 'Don''t ask "keep going?" \u2014 just keep executing phases/plans autonomously', 1, '2026-03-25T00:00:00Z', '2026-03-25T00:00:00Z');
476
- INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
477
- VALUES
478
- (hex(randomblob(16)), 'exe', NULL, 'tool-use', 'Always use create_task MCP tool, never write .md files directly for task creation', 1, '2026-03-25T00:00:00Z', '2026-03-25T00:00:00Z');
479
- INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
480
- VALUES
481
- (hex(randomblob(16)), 'exe', NULL, 'workflow', 'Auto-start reviewing when idle and reviews are pending \u2014 never ask founder for permission', 1, '2026-03-25T00:00:00Z', '2026-03-25T00:00:00Z');
482
- `);
512
+ const seededAt = "2026-03-25T00:00:00Z";
513
+ for (const [domain, content] of [
514
+ ["workflow", `Don't ask "keep going?" \u2014 just keep executing phases/plans autonomously`],
515
+ ["tool-use", "Always use create_task MCP tool, never write .md files directly for task creation"],
516
+ ["workflow", "Auto-start reviewing when idle and reviews are pending \u2014 never ask founder for permission"]
517
+ ]) {
518
+ await client.execute({
519
+ sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
520
+ VALUES (hex(randomblob(16)), ?, NULL, ?, ?, 1, ?, ?)`,
521
+ args: [coordinatorName, domain, content, seededAt, seededAt]
522
+ });
523
+ }
483
524
  }
484
525
  } catch {
485
526
  }
@@ -1171,6 +1212,39 @@ async function ensureSchema() {
1171
1212
  } catch {
1172
1213
  }
1173
1214
  }
1215
+ try {
1216
+ await client.execute({
1217
+ sql: `ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0`,
1218
+ args: []
1219
+ });
1220
+ } catch {
1221
+ }
1222
+ try {
1223
+ await client.execute(
1224
+ `CREATE INDEX IF NOT EXISTS idx_memories_draft ON memories(draft) WHERE draft = 1`
1225
+ );
1226
+ } catch {
1227
+ }
1228
+ try {
1229
+ await client.execute({
1230
+ sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
1231
+ args: []
1232
+ });
1233
+ } catch {
1234
+ }
1235
+ try {
1236
+ await client.execute(
1237
+ `CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(memory_type)`
1238
+ );
1239
+ } catch {
1240
+ }
1241
+ try {
1242
+ await client.execute({
1243
+ sql: `ALTER TABLE memories ADD COLUMN trajectory TEXT`,
1244
+ args: []
1245
+ });
1246
+ } catch {
1247
+ }
1174
1248
  }
1175
1249
  async function disposeDatabase() {
1176
1250
  if (_client) {
@@ -1184,6 +1258,7 @@ var init_database = __esm({
1184
1258
  "src/lib/database.ts"() {
1185
1259
  "use strict";
1186
1260
  init_db_retry();
1261
+ init_employees();
1187
1262
  _client = null;
1188
1263
  _resilientClient = null;
1189
1264
  initTurso = initDatabase;
@@ -1192,15 +1267,15 @@ var init_database = __esm({
1192
1267
  });
1193
1268
 
1194
1269
  // src/lib/keychain.ts
1195
- import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
1196
- import { existsSync as existsSync2 } from "fs";
1197
- import path2 from "path";
1198
- import os2 from "os";
1270
+ import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
1271
+ import { existsSync as existsSync3 } from "fs";
1272
+ import path3 from "path";
1273
+ import os3 from "os";
1199
1274
  function getKeyDir() {
1200
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path2.join(os2.homedir(), ".exe-os");
1275
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os3.homedir(), ".exe-os");
1201
1276
  }
1202
1277
  function getKeyPath() {
1203
- return path2.join(getKeyDir(), "master.key");
1278
+ return path3.join(getKeyDir(), "master.key");
1204
1279
  }
1205
1280
  async function tryKeytar() {
1206
1281
  try {
@@ -1221,11 +1296,11 @@ async function getMasterKey() {
1221
1296
  }
1222
1297
  }
1223
1298
  const keyPath = getKeyPath();
1224
- if (!existsSync2(keyPath)) {
1299
+ if (!existsSync3(keyPath)) {
1225
1300
  return null;
1226
1301
  }
1227
1302
  try {
1228
- const content = await readFile2(keyPath, "utf-8");
1303
+ const content = await readFile3(keyPath, "utf-8");
1229
1304
  return Buffer.from(content.trim(), "base64");
1230
1305
  } catch {
1231
1306
  return null;
@@ -1308,12 +1383,12 @@ __export(shard_manager_exports, {
1308
1383
  listShards: () => listShards,
1309
1384
  shardExists: () => shardExists
1310
1385
  });
1311
- import path3 from "path";
1312
- import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
1386
+ import path4 from "path";
1387
+ import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
1313
1388
  import { createClient as createClient2 } from "@libsql/client";
1314
1389
  function initShardManager(encryptionKey) {
1315
1390
  _encryptionKey = encryptionKey;
1316
- if (!existsSync3(SHARDS_DIR)) {
1391
+ if (!existsSync4(SHARDS_DIR)) {
1317
1392
  mkdirSync(SHARDS_DIR, { recursive: true });
1318
1393
  }
1319
1394
  _shardingEnabled = true;
@@ -1334,7 +1409,7 @@ function getShardClient(projectName) {
1334
1409
  }
1335
1410
  const cached = _shards.get(safeName);
1336
1411
  if (cached) return cached;
1337
- const dbPath = path3.join(SHARDS_DIR, `${safeName}.db`);
1412
+ const dbPath = path4.join(SHARDS_DIR, `${safeName}.db`);
1338
1413
  const client = createClient2({
1339
1414
  url: `file:${dbPath}`,
1340
1415
  encryptionKey: _encryptionKey
@@ -1344,10 +1419,10 @@ function getShardClient(projectName) {
1344
1419
  }
1345
1420
  function shardExists(projectName) {
1346
1421
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
1347
- return existsSync3(path3.join(SHARDS_DIR, `${safeName}.db`));
1422
+ return existsSync4(path4.join(SHARDS_DIR, `${safeName}.db`));
1348
1423
  }
1349
1424
  function listShards() {
1350
- if (!existsSync3(SHARDS_DIR)) return [];
1425
+ if (!existsSync4(SHARDS_DIR)) return [];
1351
1426
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1352
1427
  }
1353
1428
  async function ensureShardSchema(client) {
@@ -1417,7 +1492,11 @@ async function ensureShardSchema(client) {
1417
1492
  "ALTER TABLE memories ADD COLUMN source_path TEXT",
1418
1493
  "ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
1419
1494
  "ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
1420
- "ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
1495
+ "ALTER TABLE memories ADD COLUMN supersedes_id TEXT",
1496
+ // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
1497
+ "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
1498
+ "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
1499
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT"
1421
1500
  ]) {
1422
1501
  try {
1423
1502
  await client.execute(col);
@@ -1529,7 +1608,7 @@ var init_shard_manager = __esm({
1529
1608
  "src/lib/shard-manager.ts"() {
1530
1609
  "use strict";
1531
1610
  init_config();
1532
- SHARDS_DIR = path3.join(EXE_AI_DIR, "shards");
1611
+ SHARDS_DIR = path4.join(EXE_AI_DIR, "shards");
1533
1612
  _shards = /* @__PURE__ */ new Map();
1534
1613
  _encryptionKey = null;
1535
1614
  _shardingEnabled = false;
@@ -1547,26 +1626,26 @@ var init_platform_procedures = __esm({
1547
1626
  title: "What is exe-os \u2014 the operating model every agent must understand",
1548
1627
  domain: "architecture",
1549
1628
  priority: "p0",
1550
- content: "Exe OS is an AI employee operating system. A founder runs 5-10 AI agents as a real org: COO (exe), CTO (yoshi), CMO (mari), engineers (tom), content (sasha). Each agent has identity, expertise, and experience layers \u2014 persistent memory that makes them better over time. All data is local-first, E2EE, owned by the user. The MCP server is the ONLY data interface \u2014 never access the DB directly."
1629
+ content: "Exe OS is an AI employee operating system. A founder runs 5-10 AI agents as a real org: COO, CTO, CMO, engineers, and content production specialists. Each agent has identity, expertise, and experience layers \u2014 persistent memory that makes them better over time. All data is local-first, E2EE, owned by the user. The MCP server is the ONLY data interface \u2014 never access the DB directly."
1551
1630
  },
1552
1631
  {
1553
1632
  title: "Mode 1 \u2014 how exe-os runs inside Claude Code",
1554
1633
  domain: "architecture",
1555
1634
  priority: "p0",
1556
- content: "Mode 1: exe-os runs AS hooks + MCP + skills inside Claude Code. The founder opens CC, runs /exe to boot the COO. exe manages employees in tmux sessions. Each exeN is a separate CC window/project. Employees (yoshi, tom, mari) run in their own tmux panes via create_task auto-spawn. The founder talks to exe; exe orchestrates the team. CC is the shell, exe-os is the brain."
1635
+ content: "Mode 1: exe-os runs AS hooks + MCP + skills inside Claude Code. The founder opens CC and boots the COO. The COO manages employees in tmux sessions. Each coordinator session is a separate CC window/project. Employees run in their own tmux panes via create_task auto-spawn. The founder talks to the COO; the COO orchestrates the team. CC is the shell, exe-os is the brain."
1557
1636
  },
1558
1637
  {
1559
- title: "Sessions explained \u2014 what exeN means and how projects work",
1638
+ title: "Sessions explained \u2014 coordinator session names and projects",
1560
1639
  domain: "architecture",
1561
1640
  priority: "p0",
1562
- content: "Each exeN (exe1, exe2, exe3) is an isolated project session. exe1 might be exe-os development, exe2 might be exe-wiki. Each session spawns its own employees: exe1\u2192yoshi-exe1\u2192tom-exe1. Sessions share the same memory DB but tasks are scoped to the session that created them. A founder can run multiple projects simultaneously. Sessions never interfere with each other."
1641
+ content: "Each coordinator session is an isolated project session. One might be exe-os development, another might be exe-wiki. Each session spawns its own employees using {employee}-{coordinatorSession}. Sessions share the same memory DB but tasks are scoped to the session that created them. A founder can run multiple projects simultaneously. Sessions never interfere with each other."
1563
1642
  },
1564
1643
  // --- Hierarchy and dispatch ---
1565
1644
  {
1566
1645
  title: "Chain of command \u2014 who talks to whom",
1567
1646
  domain: "workflow",
1568
1647
  priority: "p0",
1569
- content: "Founder \u2192 exe (COO) \u2192 yoshi (CTO) / mari (CMO). Yoshi \u2192 tom (engineer). Mari \u2192 sasha (content). Never skip levels: exe never assigns directly to tom. Tom never reports directly to exe. 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."
1648
+ 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."
1570
1649
  },
1571
1650
  {
1572
1651
  title: "Single dispatch path \u2014 create_task only",
@@ -1576,30 +1655,30 @@ var init_platform_procedures = __esm({
1576
1655
  },
1577
1656
  // --- Session isolation ---
1578
1657
  {
1579
- title: "Session scoping \u2014 stay in your exe boundary",
1658
+ title: "Session scoping \u2014 stay in your coordinator boundary",
1580
1659
  domain: "security",
1581
1660
  priority: "p0",
1582
- content: "Session scoping is mandatory. Managers dispatch to workers within their own exe session ONLY. exe1\u2192yoshi-exe1\u2192tom-exe1. exe2\u2192yoshi-exe2\u2192tom2-exe2. Cross-session dispatch is blocked by the system. Verify session names before dispatch. Tasks are scoped to the creating exe session."
1661
+ content: "Session scoping is mandatory. Managers dispatch to workers within their own coordinator session ONLY. Employee sessions use {employee}-{coordinatorSession}. Cross-session dispatch is blocked by the system. Verify session names before dispatch. Tasks are scoped to the creating coordinator session."
1583
1662
  },
1584
1663
  {
1585
1664
  title: "Session isolation \u2014 never touch another session's work",
1586
1665
  domain: "workflow",
1587
1666
  priority: "p0",
1588
- content: `Sessions are isolated. exeN owns ONLY tasks it dispatched. (1) Never close/update/cancel tasks from another exe session. (2) Never review work from a different session \u2014 report "belongs to exeN" and skip. (3) Ignore other sessions' items in list_tasks results. (4) Employees inherit session: yoshi-exe1 works ONLY on exe1 tasks. Cross-session work is a system violation.`
1667
+ content: "Sessions are isolated. A coordinator session owns ONLY tasks it dispatched. (1) Never close/update/cancel tasks from another coordinator session. (2) Never review work from a different session \u2014 report that it belongs to another session and skip. (3) Ignore other sessions' items in list_tasks results. (4) Employees inherit session: employee sessions work ONLY on their parent coordinator session's tasks. Cross-session work is a system violation."
1589
1668
  },
1590
1669
  // --- Engineering: session scoping in code ---
1591
1670
  {
1592
1671
  title: "Three-dimensional scoping \u2014 session, project, role \u2014 enforced in every query",
1593
1672
  domain: "architecture",
1594
1673
  priority: "p0",
1595
- content: "Every DB query, notification, review count, and task operation MUST be scoped on 3 dimensions: (1) Session \u2014 filter by session_scope matching current exeN. (2) Project \u2014 filter by project_name. (3) Role \u2014 agents only see data at their hierarchy level. When writing ANY function that touches tasks, reviews, messages, or notifications: always accept a sessionScope parameter and pass it to the SQL WHERE clause. Unscoped queries are bugs. Test by running 2+ exe sessions simultaneously."
1674
+ content: "Every DB query, notification, review count, and task operation MUST be scoped on 3 dimensions: (1) Session \u2014 filter by session_scope matching the current coordinator session. (2) Project \u2014 filter by project_name. (3) Role \u2014 agents only see data at their hierarchy level. When writing ANY function that touches tasks, reviews, messages, or notifications: always accept a sessionScope parameter and pass it to the SQL WHERE clause. Unscoped queries are bugs. Test by running 2+ coordinator sessions simultaneously."
1596
1675
  },
1597
1676
  // --- Hard constraints ---
1598
1677
  {
1599
1678
  title: "What you CANNOT do in exe-os \u2014 hard constraints",
1600
1679
  domain: "security",
1601
1680
  priority: "p0",
1602
- content: "NEVER: (1) Access the database directly \u2014 it's SQLCipher encrypted, always fails. Use MCP tools only. (2) Manually spawn tmux sessions \u2014 create_task handles it. (3) Run git checkout main \u2014 agents work in worktrees. (4) Modify another agent's in-progress task. (5) Push to remote \u2014 exe reviews and pushes. (6) Skip update_task(done) \u2014 it's the ONLY way your work gets reviewed. (7) Run git init."
1681
+ content: "NEVER: (1) Access the database directly \u2014 it's SQLCipher encrypted, always fails. Use MCP tools only. (2) Manually spawn tmux sessions \u2014 create_task handles it. (3) Run git checkout main \u2014 agents work in worktrees. (4) Modify another agent's in-progress task. (5) Push to remote \u2014 the COO reviews and pushes. (6) Skip update_task(done) \u2014 it's the ONLY way your work gets reviewed. (7) Run git init."
1603
1682
  },
1604
1683
  // --- Operations ---
1605
1684
  {
@@ -1839,7 +1918,10 @@ async function writeMemory(record) {
1839
1918
  source_path: record.source_path ?? null,
1840
1919
  source_type: record.source_type ?? null,
1841
1920
  tier: record.tier ?? classifyTier(record),
1842
- supersedes_id: record.supersedes_id ?? null
1921
+ supersedes_id: record.supersedes_id ?? null,
1922
+ draft: record.draft ? 1 : 0,
1923
+ memory_type: record.memory_type ?? "raw",
1924
+ trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
1843
1925
  };
1844
1926
  _pendingRecords.push(dbRow);
1845
1927
  orgBus.emit({
@@ -1894,6 +1976,9 @@ async function flushBatch() {
1894
1976
  const sourceType = row.source_type ?? null;
1895
1977
  const tier = row.tier ?? 3;
1896
1978
  const supersedesId = row.supersedes_id ?? null;
1979
+ const draft = row.draft ? 1 : 0;
1980
+ const memoryType = row.memory_type ?? "raw";
1981
+ const trajectory = row.trajectory ?? null;
1897
1982
  return {
1898
1983
  sql: hasVector ? `INSERT OR IGNORE INTO memories
1899
1984
  (id, agent_id, agent_role, session_id, timestamp,
@@ -1901,15 +1986,15 @@ async function flushBatch() {
1901
1986
  has_error, raw_text, vector, version, task_id, importance, status,
1902
1987
  confidence, last_accessed,
1903
1988
  workspace_id, document_id, user_id, char_offset, page_number,
1904
- source_path, source_type, tier, supersedes_id)
1905
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
1989
+ source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
1990
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
1906
1991
  (id, agent_id, agent_role, session_id, timestamp,
1907
1992
  tool_name, project_name,
1908
1993
  has_error, raw_text, vector, version, task_id, importance, status,
1909
1994
  confidence, last_accessed,
1910
1995
  workspace_id, document_id, user_id, char_offset, page_number,
1911
- source_path, source_type, tier, supersedes_id)
1912
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1996
+ source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
1997
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1913
1998
  args: hasVector ? [
1914
1999
  row.id,
1915
2000
  row.agent_id,
@@ -1935,7 +2020,10 @@ async function flushBatch() {
1935
2020
  sourcePath,
1936
2021
  sourceType,
1937
2022
  tier,
1938
- supersedesId
2023
+ supersedesId,
2024
+ draft,
2025
+ memoryType,
2026
+ trajectory
1939
2027
  ] : [
1940
2028
  row.id,
1941
2029
  row.agent_id,
@@ -1960,7 +2048,10 @@ async function flushBatch() {
1960
2048
  sourcePath,
1961
2049
  sourceType,
1962
2050
  tier,
1963
- supersedesId
2051
+ supersedesId,
2052
+ draft,
2053
+ memoryType,
2054
+ trajectory
1964
2055
  ]
1965
2056
  };
1966
2057
  };
@@ -2029,6 +2120,8 @@ async function searchMemories(queryVector, agentId, options) {
2029
2120
  const limit = options?.limit ?? 10;
2030
2121
  const statusFilter = options?.includeArchived ? "" : `
2031
2122
  AND COALESCE(status, 'active') = 'active'`;
2123
+ const draftFilter = options?.includeDrafts ? "" : `
2124
+ AND (draft = 0 OR draft IS NULL)`;
2032
2125
  let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
2033
2126
  tool_name, project_name,
2034
2127
  has_error, raw_text, vector, importance, status,
@@ -2038,7 +2131,7 @@ async function searchMemories(queryVector, agentId, options) {
2038
2131
  source_path, source_type
2039
2132
  FROM memories
2040
2133
  WHERE agent_id = ?
2041
- AND vector IS NOT NULL${statusFilter}
2134
+ AND vector IS NOT NULL${statusFilter}${draftFilter}
2042
2135
  AND COALESCE(confidence, 0.7) >= 0.3`;
2043
2136
  const args = [agentId];
2044
2137
  const scope = buildWikiScopeFilter(options, "");
@@ -2060,6 +2153,10 @@ async function searchMemories(queryVector, agentId, options) {
2060
2153
  sql += ` AND timestamp >= ?`;
2061
2154
  args.push(options.since);
2062
2155
  }
2156
+ if (options?.memoryType) {
2157
+ sql += ` AND memory_type = ?`;
2158
+ args.push(options.memoryType);
2159
+ }
2063
2160
  sql += ` ORDER BY vector_distance_cos(vector, vector32(?))`;
2064
2161
  args.push(vectorToBlob(queryVector));
2065
2162
  sql += ` LIMIT ?`;
@@ -2285,7 +2382,7 @@ var init_self_query_router = __esm({
2285
2382
  },
2286
2383
  is_broad_query: {
2287
2384
  type: "boolean",
2288
- description: "True if the query is exploratory/broad (e.g., 'what has yoshi been working on', 'summarize recent activity'). False if targeted (e.g., 'how did we fix the auth bug')."
2385
+ description: "True if the query is exploratory/broad (e.g., 'what has the CTO been working on', 'summarize recent activity'). False if targeted (e.g., 'how did we fix the auth bug')."
2289
2386
  }
2290
2387
  },
2291
2388
  required: ["semantic_query", "project_filter", "role_filter", "time_filter", "is_broad_query"]
@@ -2298,8 +2395,8 @@ var init_self_query_router = __esm({
2298
2395
  import net from "net";
2299
2396
  import { spawn } from "child_process";
2300
2397
  import { randomUUID as randomUUID2 } from "crypto";
2301
- import { existsSync as existsSync4, unlinkSync, readFileSync as readFileSync2, openSync, closeSync, statSync } from "fs";
2302
- import path4 from "path";
2398
+ import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
2399
+ import path5 from "path";
2303
2400
  import { fileURLToPath } from "url";
2304
2401
  function handleData(chunk) {
2305
2402
  _buffer += chunk.toString();
@@ -2325,9 +2422,9 @@ function handleData(chunk) {
2325
2422
  }
2326
2423
  }
2327
2424
  function cleanupStaleFiles() {
2328
- if (existsSync4(PID_PATH)) {
2425
+ if (existsSync5(PID_PATH)) {
2329
2426
  try {
2330
- const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
2427
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
2331
2428
  if (pid > 0) {
2332
2429
  try {
2333
2430
  process.kill(pid, 0);
@@ -2338,21 +2435,21 @@ function cleanupStaleFiles() {
2338
2435
  } catch {
2339
2436
  }
2340
2437
  try {
2341
- unlinkSync(PID_PATH);
2438
+ unlinkSync2(PID_PATH);
2342
2439
  } catch {
2343
2440
  }
2344
2441
  try {
2345
- unlinkSync(SOCKET_PATH);
2442
+ unlinkSync2(SOCKET_PATH);
2346
2443
  } catch {
2347
2444
  }
2348
2445
  }
2349
2446
  }
2350
2447
  function findPackageRoot() {
2351
- let dir = path4.dirname(fileURLToPath(import.meta.url));
2352
- const { root } = path4.parse(dir);
2448
+ let dir = path5.dirname(fileURLToPath(import.meta.url));
2449
+ const { root } = path5.parse(dir);
2353
2450
  while (dir !== root) {
2354
- if (existsSync4(path4.join(dir, "package.json"))) return dir;
2355
- dir = path4.dirname(dir);
2451
+ if (existsSync5(path5.join(dir, "package.json"))) return dir;
2452
+ dir = path5.dirname(dir);
2356
2453
  }
2357
2454
  return null;
2358
2455
  }
@@ -2362,8 +2459,8 @@ function spawnDaemon() {
2362
2459
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
2363
2460
  return;
2364
2461
  }
2365
- const daemonPath = path4.join(pkgRoot, "dist", "lib", "exe-daemon.js");
2366
- if (!existsSync4(daemonPath)) {
2462
+ const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
2463
+ if (!existsSync5(daemonPath)) {
2367
2464
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
2368
2465
  `);
2369
2466
  return;
@@ -2371,7 +2468,7 @@ function spawnDaemon() {
2371
2468
  const resolvedPath = daemonPath;
2372
2469
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
2373
2470
  `);
2374
- const logPath = path4.join(path4.dirname(SOCKET_PATH), "exed.log");
2471
+ const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
2375
2472
  let stderrFd = "ignore";
2376
2473
  try {
2377
2474
  stderrFd = openSync(logPath, "a");
@@ -2382,6 +2479,10 @@ function spawnDaemon() {
2382
2479
  stdio: ["ignore", "ignore", stderrFd],
2383
2480
  env: {
2384
2481
  ...process.env,
2482
+ TMUX: void 0,
2483
+ // Daemon is global — must not inherit session scope
2484
+ TMUX_PANE: void 0,
2485
+ // Prevents resolveExeSession() from scoping to one session
2385
2486
  EXE_DAEMON_SOCK: SOCKET_PATH,
2386
2487
  EXE_DAEMON_PID: PID_PATH
2387
2488
  }
@@ -2404,7 +2505,7 @@ function acquireSpawnLock() {
2404
2505
  const stat = statSync(SPAWN_LOCK_PATH);
2405
2506
  if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
2406
2507
  try {
2407
- unlinkSync(SPAWN_LOCK_PATH);
2508
+ unlinkSync2(SPAWN_LOCK_PATH);
2408
2509
  } catch {
2409
2510
  }
2410
2511
  try {
@@ -2421,7 +2522,7 @@ function acquireSpawnLock() {
2421
2522
  }
2422
2523
  function releaseSpawnLock() {
2423
2524
  try {
2424
- unlinkSync(SPAWN_LOCK_PATH);
2525
+ unlinkSync2(SPAWN_LOCK_PATH);
2425
2526
  } catch {
2426
2527
  }
2427
2528
  }
@@ -2533,9 +2634,9 @@ async function pingDaemon() {
2533
2634
  }
2534
2635
  function killAndRespawnDaemon() {
2535
2636
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
2536
- if (existsSync4(PID_PATH)) {
2637
+ if (existsSync5(PID_PATH)) {
2537
2638
  try {
2538
- const pid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
2639
+ const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
2539
2640
  if (pid > 0) {
2540
2641
  try {
2541
2642
  process.kill(pid, "SIGKILL");
@@ -2552,11 +2653,11 @@ function killAndRespawnDaemon() {
2552
2653
  _connected = false;
2553
2654
  _buffer = "";
2554
2655
  try {
2555
- unlinkSync(PID_PATH);
2656
+ unlinkSync2(PID_PATH);
2556
2657
  } catch {
2557
2658
  }
2558
2659
  try {
2559
- unlinkSync(SOCKET_PATH);
2660
+ unlinkSync2(SOCKET_PATH);
2560
2661
  } catch {
2561
2662
  }
2562
2663
  spawnDaemon();
@@ -2619,9 +2720,9 @@ var init_exe_daemon_client = __esm({
2619
2720
  "src/lib/exe-daemon-client.ts"() {
2620
2721
  "use strict";
2621
2722
  init_config();
2622
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path4.join(EXE_AI_DIR, "exed.sock");
2623
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path4.join(EXE_AI_DIR, "exed.pid");
2624
- SPAWN_LOCK_PATH = path4.join(EXE_AI_DIR, "exed-spawn.lock");
2723
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
2724
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
2725
+ SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
2625
2726
  SPAWN_LOCK_STALE_MS = 3e4;
2626
2727
  CONNECT_TIMEOUT_MS = 15e3;
2627
2728
  REQUEST_TIMEOUT_MS = 3e4;
@@ -2709,34 +2810,34 @@ __export(project_name_exports, {
2709
2810
  _resetCache: () => _resetCache,
2710
2811
  getProjectName: () => getProjectName
2711
2812
  });
2712
- import { execSync } from "child_process";
2713
- import path5 from "path";
2813
+ import { execSync as execSync2 } from "child_process";
2814
+ import path6 from "path";
2714
2815
  function getProjectName(cwd) {
2715
2816
  const dir = cwd ?? process.cwd();
2716
2817
  if (_cached && _cachedCwd === dir) return _cached;
2717
2818
  try {
2718
2819
  let repoRoot;
2719
2820
  try {
2720
- const gitCommonDir = execSync("git rev-parse --path-format=absolute --git-common-dir", {
2821
+ const gitCommonDir = execSync2("git rev-parse --path-format=absolute --git-common-dir", {
2721
2822
  cwd: dir,
2722
2823
  encoding: "utf8",
2723
2824
  timeout: 2e3,
2724
2825
  stdio: ["pipe", "pipe", "pipe"]
2725
2826
  }).trim();
2726
- repoRoot = path5.dirname(gitCommonDir);
2827
+ repoRoot = path6.dirname(gitCommonDir);
2727
2828
  } catch {
2728
- repoRoot = execSync("git rev-parse --show-toplevel", {
2829
+ repoRoot = execSync2("git rev-parse --show-toplevel", {
2729
2830
  cwd: dir,
2730
2831
  encoding: "utf8",
2731
2832
  timeout: 2e3,
2732
2833
  stdio: ["pipe", "pipe", "pipe"]
2733
2834
  }).trim();
2734
2835
  }
2735
- _cached = path5.basename(repoRoot);
2836
+ _cached = path6.basename(repoRoot);
2736
2837
  _cachedCwd = dir;
2737
2838
  return _cached;
2738
2839
  } catch {
2739
- _cached = path5.basename(dir);
2840
+ _cached = path6.basename(dir);
2740
2841
  _cachedCwd = dir;
2741
2842
  return _cached;
2742
2843
  }
@@ -2759,14 +2860,14 @@ var file_grep_exports = {};
2759
2860
  __export(file_grep_exports, {
2760
2861
  grepProjectFiles: () => grepProjectFiles
2761
2862
  });
2762
- import { execSync as execSync2 } from "child_process";
2763
- import { readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync5 } from "fs";
2764
- import path6 from "path";
2863
+ import { execSync as execSync3 } from "child_process";
2864
+ import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync6 } from "fs";
2865
+ import path7 from "path";
2765
2866
  import crypto from "crypto";
2766
2867
  function hasRipgrep() {
2767
2868
  if (_hasRg === null) {
2768
2869
  try {
2769
- execSync2("rg --version", { stdio: "ignore", timeout: 2e3 });
2870
+ execSync3("rg --version", { stdio: "ignore", timeout: 2e3 });
2770
2871
  _hasRg = true;
2771
2872
  } catch {
2772
2873
  _hasRg = false;
@@ -2801,7 +2902,7 @@ async function grepProjectFiles(query, projectRoot, options) {
2801
2902
  session_id: "file-grep",
2802
2903
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2803
2904
  tool_name: "file_grep",
2804
- project_name: path6.basename(projectRoot),
2905
+ project_name: path7.basename(projectRoot),
2805
2906
  has_error: false,
2806
2907
  raw_text: `${prefix} ${buildSnippet(hit, projectRoot)}`,
2807
2908
  vector: null,
@@ -2813,7 +2914,7 @@ function getChunkContext(filePath, lineNumber) {
2813
2914
  try {
2814
2915
  const ext = filePath.split(".").pop()?.toLowerCase();
2815
2916
  if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
2816
- const source = readFileSync3(filePath, "utf8");
2917
+ const source = readFileSync4(filePath, "utf8");
2817
2918
  const lines = source.split("\n");
2818
2919
  for (let i = Math.min(lineNumber - 1, lines.length - 1); i >= 0; i--) {
2819
2920
  const line = lines[i];
@@ -2832,7 +2933,7 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
2832
2933
  const globs = (patterns ?? DEFAULT_PATTERNS).map((p) => `--glob '${p}'`).join(" ");
2833
2934
  const excludes = EXCLUDE_DIRS.map((d) => `--glob '!${d}'`).join(" ");
2834
2935
  const cmd = `rg -i -c --hidden --no-config --no-ignore '${pattern.replace(/'/g, "\\'")}' . ${globs} ${excludes} --max-filesize ${MAX_FILE_SIZE} 2>/dev/null || true`;
2835
- const output = execSync2(cmd, {
2936
+ const output = execSync3(cmd, {
2836
2937
  cwd: projectRoot,
2837
2938
  encoding: "utf8",
2838
2939
  timeout: 3e3,
@@ -2847,12 +2948,12 @@ function grepWithRipgrep(pattern, projectRoot, patterns) {
2847
2948
  const matchCount = parseInt(line.slice(colonIdx + 1));
2848
2949
  if (isNaN(matchCount) || matchCount === 0) continue;
2849
2950
  try {
2850
- const firstMatch = execSync2(
2951
+ const firstMatch = execSync3(
2851
2952
  `rg -i -n --hidden '${pattern.replace(/'/g, "\\'")}' '${filePath}' --max-count 1 2>/dev/null | head -1`,
2852
2953
  { cwd: projectRoot, encoding: "utf8", timeout: 1e3 }
2853
2954
  ).trim();
2854
2955
  const lineNum = parseInt(firstMatch.split(":")[0] ?? "1");
2855
- const totalLines = execSync2(`wc -l < '${filePath}'`, {
2956
+ const totalLines = execSync3(`wc -l < '${filePath}'`, {
2856
2957
  cwd: projectRoot,
2857
2958
  encoding: "utf8",
2858
2959
  timeout: 1e3
@@ -2875,11 +2976,11 @@ function grepWithNodeFs(pattern, projectRoot, patterns) {
2875
2976
  const files = collectFiles(projectRoot, patterns ?? DEFAULT_PATTERNS);
2876
2977
  const hits = [];
2877
2978
  for (const filePath of files.slice(0, MAX_FILES)) {
2878
- const absPath = path6.join(projectRoot, filePath);
2979
+ const absPath = path7.join(projectRoot, filePath);
2879
2980
  try {
2880
2981
  const stat = statSync2(absPath);
2881
2982
  if (stat.size > MAX_FILE_SIZE) continue;
2882
- const content = readFileSync3(absPath, "utf8");
2983
+ const content = readFileSync4(absPath, "utf8");
2883
2984
  const lines = content.split("\n");
2884
2985
  const matches = content.match(regex);
2885
2986
  if (!matches || matches.length === 0) continue;
@@ -2902,15 +3003,15 @@ function collectFiles(root, patterns) {
2902
3003
  const files = [];
2903
3004
  function walk(dir, relative) {
2904
3005
  if (files.length >= MAX_FILES) return;
2905
- const basename = path6.basename(dir);
3006
+ const basename = path7.basename(dir);
2906
3007
  if (EXCLUDE_DIRS.includes(basename)) return;
2907
3008
  try {
2908
3009
  const entries = readdirSync2(dir, { withFileTypes: true });
2909
3010
  for (const entry of entries) {
2910
3011
  if (files.length >= MAX_FILES) return;
2911
- const rel = path6.join(relative, entry.name);
3012
+ const rel = path7.join(relative, entry.name);
2912
3013
  if (entry.isDirectory()) {
2913
- walk(path6.join(dir, entry.name), rel);
3014
+ walk(path7.join(dir, entry.name), rel);
2914
3015
  } else if (entry.isFile()) {
2915
3016
  for (const pat of patterns) {
2916
3017
  if (matchGlob(rel, pat)) {
@@ -2942,7 +3043,7 @@ function matchGlob(filePath, pattern) {
2942
3043
  if (slashIdx !== -1) {
2943
3044
  const dir = pattern.slice(0, slashIdx);
2944
3045
  const ext2 = pattern.slice(slashIdx + 1).replace("*", "");
2945
- const fileDir = path6.dirname(filePath);
3046
+ const fileDir = path7.dirname(filePath);
2946
3047
  return fileDir === dir && filePath.endsWith(ext2);
2947
3048
  }
2948
3049
  const ext = pattern.replace("*", "");
@@ -2950,9 +3051,9 @@ function matchGlob(filePath, pattern) {
2950
3051
  }
2951
3052
  function buildSnippet(hit, projectRoot) {
2952
3053
  try {
2953
- const absPath = path6.join(projectRoot, hit.filePath);
2954
- if (!existsSync5(absPath)) return hit.matchLine;
2955
- const lines = readFileSync3(absPath, "utf8").split("\n");
3054
+ const absPath = path7.join(projectRoot, hit.filePath);
3055
+ if (!existsSync6(absPath)) return hit.matchLine;
3056
+ const lines = readFileSync4(absPath, "utf8").split("\n");
2956
3057
  const start = Math.max(0, hit.lineNumber - 3);
2957
3058
  const end = Math.min(lines.length, hit.lineNumber + 2);
2958
3059
  return lines.slice(start, end).join("\n").slice(0, 500);
@@ -2986,8 +3087,8 @@ __export(reranker_exports, {
2986
3087
  rerank: () => rerank,
2987
3088
  rerankWithScores: () => rerankWithScores
2988
3089
  });
2989
- import path7 from "path";
2990
- import { existsSync as existsSync6 } from "fs";
3090
+ import path8 from "path";
3091
+ import { existsSync as existsSync7 } from "fs";
2991
3092
  function resetIdleTimer() {
2992
3093
  if (_idleTimer) clearTimeout(_idleTimer);
2993
3094
  _idleTimer = setTimeout(() => {
@@ -2998,18 +3099,18 @@ function resetIdleTimer() {
2998
3099
  }
2999
3100
  }
3000
3101
  function isRerankerAvailable() {
3001
- return existsSync6(path7.join(MODELS_DIR, RERANKER_MODEL_FILE));
3102
+ return existsSync7(path8.join(MODELS_DIR, RERANKER_MODEL_FILE));
3002
3103
  }
3003
3104
  function getRerankerModelPath() {
3004
- return path7.join(MODELS_DIR, RERANKER_MODEL_FILE);
3105
+ return path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
3005
3106
  }
3006
3107
  async function ensureLoaded() {
3007
3108
  if (_rerankerContext) {
3008
3109
  resetIdleTimer();
3009
3110
  return;
3010
3111
  }
3011
- const modelPath = path7.join(MODELS_DIR, RERANKER_MODEL_FILE);
3012
- if (!existsSync6(modelPath)) {
3112
+ const modelPath = path8.join(MODELS_DIR, RERANKER_MODEL_FILE);
3113
+ if (!existsSync7(modelPath)) {
3013
3114
  throw new Error(
3014
3115
  `Reranker model not found at ${modelPath}. Run /exe-setup to download it.`
3015
3116
  );
@@ -3086,13 +3187,13 @@ var init_reranker = __esm({
3086
3187
  });
3087
3188
 
3088
3189
  // src/lib/session-key.ts
3089
- import { execSync as execSync3 } from "child_process";
3190
+ import { execSync as execSync4 } from "child_process";
3090
3191
  function getSessionKey() {
3091
3192
  if (_cached2) return _cached2;
3092
3193
  let pid = process.ppid;
3093
3194
  for (let i = 0; i < 10; i++) {
3094
3195
  try {
3095
- const info = execSync3(`ps -p ${pid} -o ppid=,comm=`, {
3196
+ const info = execSync4(`ps -p ${pid} -o ppid=,comm=`, {
3096
3197
  encoding: "utf8",
3097
3198
  timeout: 2e3
3098
3199
  }).trim();
@@ -3121,13 +3222,13 @@ var init_session_key = __esm({
3121
3222
  });
3122
3223
 
3123
3224
  // src/lib/session-registry.ts
3124
- import path9 from "path";
3125
- import os3 from "os";
3225
+ import path10 from "path";
3226
+ import os4 from "os";
3126
3227
  var REGISTRY_PATH;
3127
3228
  var init_session_registry = __esm({
3128
3229
  "src/lib/session-registry.ts"() {
3129
3230
  "use strict";
3130
- REGISTRY_PATH = path9.join(os3.homedir(), ".exe-os", "session-registry.json");
3231
+ REGISTRY_PATH = path10.join(os4.homedir(), ".exe-os", "session-registry.json");
3131
3232
  }
3132
3233
  });
3133
3234
 
@@ -3239,7 +3340,7 @@ var init_transport = __esm({
3239
3340
  });
3240
3341
 
3241
3342
  // src/lib/cc-agent-support.ts
3242
- import { execSync as execSync5 } from "child_process";
3343
+ import { execSync as execSync6 } from "child_process";
3243
3344
  var init_cc_agent_support = __esm({
3244
3345
  "src/lib/cc-agent-support.ts"() {
3245
3346
  "use strict";
@@ -3268,31 +3369,16 @@ var init_provider_table = __esm({
3268
3369
  });
3269
3370
 
3270
3371
  // src/lib/intercom-queue.ts
3271
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, renameSync as renameSync2, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
3272
- import path10 from "path";
3273
- import os4 from "os";
3372
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
3373
+ import path11 from "path";
3374
+ import os5 from "os";
3274
3375
  var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
3275
3376
  var init_intercom_queue = __esm({
3276
3377
  "src/lib/intercom-queue.ts"() {
3277
3378
  "use strict";
3278
- QUEUE_PATH = path10.join(os4.homedir(), ".exe-os", "intercom-queue.json");
3379
+ QUEUE_PATH = path11.join(os5.homedir(), ".exe-os", "intercom-queue.json");
3279
3380
  TTL_MS = 60 * 60 * 1e3;
3280
- INTERCOM_LOG = path10.join(os4.homedir(), ".exe-os", "intercom.log");
3281
- }
3282
- });
3283
-
3284
- // src/lib/employees.ts
3285
- import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
3286
- import { existsSync as existsSync8, symlinkSync, readlinkSync, readFileSync as readFileSync6, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "fs";
3287
- import { execSync as execSync6 } from "child_process";
3288
- import path11 from "path";
3289
- import os5 from "os";
3290
- var EMPLOYEES_PATH;
3291
- var init_employees = __esm({
3292
- "src/lib/employees.ts"() {
3293
- "use strict";
3294
- init_config();
3295
- EMPLOYEES_PATH = path11.join(EXE_AI_DIR, "exe-employees.json");
3381
+ INTERCOM_LOG = path11.join(os5.homedir(), ".exe-os", "intercom.log");
3296
3382
  }
3297
3383
  });
3298
3384
 
@@ -3336,8 +3422,10 @@ function getMySession() {
3336
3422
  return getTransport().getMySession();
3337
3423
  }
3338
3424
  function extractRootExe(name) {
3339
- const match = name.match(/(exe\d+)$/);
3340
- return match?.[1] ?? null;
3425
+ if (!name) return null;
3426
+ if (!name.includes("-")) return name;
3427
+ const parts = name.split("-").filter(Boolean);
3428
+ return parts.length > 0 ? parts[parts.length - 1] : null;
3341
3429
  }
3342
3430
  function getParentExe(sessionKey) {
3343
3431
  try {
@@ -3372,6 +3460,7 @@ var init_tmux_routing = __esm({
3372
3460
  init_provider_table();
3373
3461
  init_intercom_queue();
3374
3462
  init_plan_limits();
3463
+ init_employees();
3375
3464
  SPAWN_LOCK_DIR = path14.join(os6.homedir(), ".exe-os", "spawn-locks");
3376
3465
  SESSION_CACHE = path14.join(os6.homedir(), ".exe-os", "session-cache");
3377
3466
  INTERCOM_LOG2 = path14.join(os6.homedir(), ".exe-os", "intercom.log");
@@ -3423,6 +3512,8 @@ async function hybridSearch(queryText, agentId, options) {
3423
3512
  return lightweightSearch(queryText, agentId, options);
3424
3513
  }
3425
3514
  const limit = options?.limit ?? 10;
3515
+ const trajectoryResults = await trajectoryBypass(queryText, agentId, options, limit);
3516
+ if (trajectoryResults !== null) return trajectoryResults;
3426
3517
  let effectiveQuery = queryText;
3427
3518
  let effectiveOptions = { ...options };
3428
3519
  let _isBroadQuery = false;
@@ -3646,6 +3737,8 @@ async function lightweightSearch(queryText, agentId, options) {
3646
3737
  async function ftsQuery(client, matchExpr, agentId, options, limit) {
3647
3738
  const statusFilter = options?.includeArchived ? "" : `
3648
3739
  AND COALESCE(m.status, 'active') = 'active'`;
3740
+ const draftFilter = options?.includeDrafts ? "" : `
3741
+ AND (m.draft = 0 OR m.draft IS NULL)`;
3649
3742
  let sql = `SELECT m.id, m.agent_id, m.agent_role, m.session_id, m.timestamp,
3650
3743
  m.tool_name, m.project_name,
3651
3744
  m.has_error, m.raw_text, m.vector, m.task_id,
@@ -3656,7 +3749,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
3656
3749
  FROM memories m
3657
3750
  JOIN memories_fts fts ON m.rowid = fts.rowid
3658
3751
  WHERE memories_fts MATCH ?
3659
- AND m.agent_id = ?${statusFilter}
3752
+ AND m.agent_id = ?${statusFilter}${draftFilter}
3660
3753
  AND COALESCE(m.confidence, 0.7) >= 0.3`;
3661
3754
  const args = [matchExpr, agentId];
3662
3755
  const scope = buildWikiScopeFilter(options, "m.");
@@ -3678,6 +3771,10 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
3678
3771
  sql += ` AND m.timestamp >= ?`;
3679
3772
  args.push(options.since);
3680
3773
  }
3774
+ if (options?.memoryType) {
3775
+ sql += ` AND m.memory_type = ?`;
3776
+ args.push(options.memoryType);
3777
+ }
3681
3778
  sql += ` ORDER BY rank LIMIT ?`;
3682
3779
  args.push(limit);
3683
3780
  const result = await client.execute({ sql, args });
@@ -3710,6 +3807,8 @@ async function recentRecords(agentId, options, limit) {
3710
3807
  const client = getClient();
3711
3808
  const statusFilter = options?.includeArchived ? "" : `
3712
3809
  AND COALESCE(status, 'active') = 'active'`;
3810
+ const draftFilter = options?.includeDrafts ? "" : `
3811
+ AND (draft = 0 OR draft IS NULL)`;
3713
3812
  let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
3714
3813
  tool_name, project_name,
3715
3814
  has_error, raw_text, vector, task_id,
@@ -3718,7 +3817,7 @@ async function recentRecords(agentId, options, limit) {
3718
3817
  char_offset, page_number,
3719
3818
  source_path, source_type
3720
3819
  FROM memories
3721
- WHERE agent_id = ?${statusFilter}
3820
+ WHERE agent_id = ?${statusFilter}${draftFilter}
3722
3821
  AND COALESCE(confidence, 0.7) >= 0.3`;
3723
3822
  const args = [agentId];
3724
3823
  const scope = buildWikiScopeFilter(options, "");
@@ -3740,6 +3839,10 @@ async function recentRecords(agentId, options, limit) {
3740
3839
  sql += ` AND timestamp >= ?`;
3741
3840
  args.push(options.since);
3742
3841
  }
3842
+ if (options?.memoryType) {
3843
+ sql += ` AND memory_type = ?`;
3844
+ args.push(options.memoryType);
3845
+ }
3743
3846
  sql += ` ORDER BY timestamp DESC LIMIT ?`;
3744
3847
  args.push(limit);
3745
3848
  const result = await client.execute({ sql, args });
@@ -3768,33 +3871,158 @@ async function recentRecords(agentId, options, limit) {
3768
3871
  source_type: row.source_type ?? null
3769
3872
  }));
3770
3873
  }
3874
+ var KNOWN_TOOLS = /* @__PURE__ */ new Set([
3875
+ "Bash",
3876
+ "Read",
3877
+ "Write",
3878
+ "Edit",
3879
+ "Grep",
3880
+ "Glob",
3881
+ "Agent",
3882
+ "Skill",
3883
+ "WebSearch",
3884
+ "WebFetch"
3885
+ ]);
3886
+ function detectToolQuery(query) {
3887
+ const lower = query.toLowerCase().trim();
3888
+ for (const tool of KNOWN_TOOLS) {
3889
+ if (lower === tool.toLowerCase()) return tool;
3890
+ if (lower.startsWith(tool.toLowerCase() + " ")) return tool;
3891
+ if (lower.includes(`tool:${tool.toLowerCase()}`)) return tool;
3892
+ }
3893
+ return null;
3894
+ }
3895
+ async function trajectoryBypass(queryText, agentId, options, limit) {
3896
+ const toolName = detectToolQuery(queryText);
3897
+ if (!toolName) return null;
3898
+ try {
3899
+ const client = getClient();
3900
+ const statusFilter = options?.includeArchived ? "" : `
3901
+ AND COALESCE(status, 'active') = 'active'`;
3902
+ const draftFilter = options?.includeDrafts ? "" : `
3903
+ AND (draft = 0 OR draft IS NULL)`;
3904
+ let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
3905
+ tool_name, project_name,
3906
+ has_error, raw_text, vector, task_id,
3907
+ importance, status, confidence, last_accessed,
3908
+ workspace_id, document_id, user_id,
3909
+ char_offset, page_number,
3910
+ source_path, source_type
3911
+ FROM memories
3912
+ WHERE trajectory IS NOT NULL
3913
+ AND json_extract(trajectory, '$.tool') = ?
3914
+ AND agent_id = ?${statusFilter}${draftFilter}`;
3915
+ const args = [toolName, agentId];
3916
+ if (options?.projectName) {
3917
+ sql += ` AND project_name = ?`;
3918
+ args.push(options.projectName);
3919
+ }
3920
+ if (options?.since) {
3921
+ sql += ` AND timestamp >= ?`;
3922
+ args.push(options.since);
3923
+ }
3924
+ sql += ` ORDER BY timestamp DESC LIMIT ?`;
3925
+ args.push(limit);
3926
+ const result = await client.execute({ sql, args });
3927
+ if (result.rows.length < 3) return null;
3928
+ return result.rows.map((row) => ({
3929
+ id: row.id,
3930
+ agent_id: row.agent_id,
3931
+ agent_role: row.agent_role,
3932
+ session_id: row.session_id,
3933
+ timestamp: row.timestamp,
3934
+ tool_name: row.tool_name,
3935
+ project_name: row.project_name,
3936
+ has_error: row.has_error === 1,
3937
+ raw_text: row.raw_text,
3938
+ vector: row.vector == null ? [] : Array.isArray(row.vector) ? row.vector : Array.from(row.vector),
3939
+ task_id: row.task_id ?? null,
3940
+ importance: row.importance ?? 5,
3941
+ status: row.status ?? "active",
3942
+ confidence: row.confidence ?? 0.7,
3943
+ last_accessed: row.last_accessed ?? row.timestamp,
3944
+ workspace_id: row.workspace_id ?? null,
3945
+ document_id: row.document_id ?? null,
3946
+ user_id: row.user_id ?? null,
3947
+ char_offset: row.char_offset ?? null,
3948
+ page_number: row.page_number ?? null,
3949
+ source_path: row.source_path ?? null,
3950
+ source_type: row.source_type ?? null
3951
+ }));
3952
+ } catch {
3953
+ return null;
3954
+ }
3955
+ }
3771
3956
 
3772
3957
  // src/adapters/claude/active-agent.ts
3773
3958
  init_config();
3774
- import { readFileSync as readFileSync4, writeFileSync, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync as readdirSync3 } from "fs";
3775
- import { execSync as execSync4 } from "child_process";
3776
- import path8 from "path";
3959
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync3, readdirSync as readdirSync3 } from "fs";
3960
+ import { execSync as execSync5 } from "child_process";
3961
+ import path9 from "path";
3777
3962
 
3778
3963
  // src/adapters/claude/session-key.ts
3779
3964
  init_session_key();
3780
3965
 
3781
3966
  // src/adapters/claude/active-agent.ts
3782
- var CACHE_DIR = path8.join(EXE_AI_DIR, "session-cache");
3967
+ init_employees();
3968
+ var CACHE_DIR = path9.join(EXE_AI_DIR, "session-cache");
3783
3969
  var STALE_MS = 24 * 60 * 60 * 1e3;
3970
+ function isNameWithOptionalInstance(candidate, baseName) {
3971
+ if (candidate === baseName) return true;
3972
+ if (!candidate.startsWith(baseName)) return false;
3973
+ return /^\d+$/.test(candidate.slice(baseName.length));
3974
+ }
3975
+ function resolveEmployeeFromSessionPrefix(prefix, employees) {
3976
+ const sorted = [...employees].sort((a, b) => b.name.length - a.name.length);
3977
+ for (const employee of sorted) {
3978
+ if (isNameWithOptionalInstance(prefix, employee.name)) {
3979
+ return { agentId: employee.name, agentRole: employee.role };
3980
+ }
3981
+ }
3982
+ return null;
3983
+ }
3984
+ function resolveActiveAgentFromTmuxSession(sessionName) {
3985
+ const employees = loadEmployeesSync();
3986
+ const coordinator = getCoordinatorEmployee(employees);
3987
+ const coordinatorName = coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
3988
+ if (isNameWithOptionalInstance(sessionName, coordinatorName)) {
3989
+ return {
3990
+ agentId: coordinatorName,
3991
+ agentRole: coordinator?.role ?? "COO"
3992
+ };
3993
+ }
3994
+ if (isNameWithOptionalInstance(sessionName, DEFAULT_COORDINATOR_TEMPLATE_NAME)) {
3995
+ return {
3996
+ agentId: coordinator?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME,
3997
+ agentRole: coordinator?.role ?? "COO"
3998
+ };
3999
+ }
4000
+ if (sessionName.includes("-")) {
4001
+ const prefix = sessionName.split("-")[0] ?? "";
4002
+ const employee = resolveEmployeeFromSessionPrefix(prefix, employees);
4003
+ if (employee) return employee;
4004
+ const legacy = prefix.match(/^([a-zA-Z]+)\d*$/);
4005
+ if (legacy?.[1] && legacy[1] !== DEFAULT_COORDINATOR_TEMPLATE_NAME) {
4006
+ const emp = getEmployee(employees, legacy[1]);
4007
+ return { agentId: emp?.name ?? legacy[1], agentRole: emp?.role ?? "employee" };
4008
+ }
4009
+ }
4010
+ return null;
4011
+ }
3784
4012
  function getMarkerPath() {
3785
- return path8.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
4013
+ return path9.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
3786
4014
  }
3787
4015
  function getActiveAgent() {
3788
4016
  try {
3789
4017
  const markerPath = getMarkerPath();
3790
- const raw = readFileSync4(markerPath, "utf8");
4018
+ const raw = readFileSync5(markerPath, "utf8");
3791
4019
  const data = JSON.parse(raw);
3792
4020
  if (data.agentId) {
3793
4021
  if (data.startedAt) {
3794
4022
  const age = Date.now() - new Date(data.startedAt).getTime();
3795
4023
  if (age > STALE_MS) {
3796
4024
  try {
3797
- unlinkSync2(markerPath);
4025
+ unlinkSync3(markerPath);
3798
4026
  } catch {
3799
4027
  }
3800
4028
  } else {
@@ -3813,17 +4041,12 @@ function getActiveAgent() {
3813
4041
  } catch {
3814
4042
  }
3815
4043
  try {
3816
- const sessionName = execSync4(
4044
+ const sessionName = execSync5(
3817
4045
  "tmux display-message -p '#{session_name}' 2>/dev/null",
3818
4046
  { encoding: "utf8", timeout: 2e3 }
3819
4047
  ).trim();
3820
- const empMatch = sessionName.match(/^([a-zA-Z]+)\d*-exe\d+$/);
3821
- if (empMatch && empMatch[1] !== "exe") {
3822
- return { agentId: empMatch[1], agentRole: "employee" };
3823
- }
3824
- if (/^exe\d+$/.test(sessionName)) {
3825
- return { agentId: "exe", agentRole: "COO" };
3826
- }
4048
+ const resolved = resolveActiveAgentFromTmuxSession(sessionName);
4049
+ if (resolved) return resolved;
3827
4050
  } catch {
3828
4051
  }
3829
4052
  return {
@@ -3835,6 +4058,7 @@ function getActiveAgent() {
3835
4058
  // src/adapters/claude/hooks/session-start.ts
3836
4059
  init_project_name();
3837
4060
  init_task_scope();
4061
+ init_employees();
3838
4062
  if (!process.env.AGENT_ID) {
3839
4063
  process.env.AGENT_ID = "default";
3840
4064
  process.env.AGENT_ROLE = "employee";
@@ -3903,7 +4127,7 @@ process.stdin.on("end", async () => {
3903
4127
 
3904
4128
  ## Your Tasks
3905
4129
  ${taskLines.join("\n")}`;
3906
- if (agentId !== "exe" && agentId !== "default") {
4130
+ if (!canCoordinate(agentId, agent.agentRole)) {
3907
4131
  taskBrief += `
3908
4132
 
3909
4133
  ## MANDATORY PROCESS