@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
package/dist/bin/cli.js CHANGED
@@ -31,7 +31,6 @@ var config_exports = {};
31
31
  __export(config_exports, {
32
32
  CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
33
33
  CONFIG_PATH: () => CONFIG_PATH,
34
- COO_AGENT_NAME: () => COO_AGENT_NAME,
35
34
  CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
36
35
  DB_PATH: () => DB_PATH,
37
36
  EXE_AI_DIR: () => EXE_AI_DIR,
@@ -187,7 +186,7 @@ async function loadConfigFrom(configPath) {
187
186
  return { ...DEFAULT_CONFIG };
188
187
  }
189
188
  }
190
- var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
189
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
191
190
  var init_config = __esm({
192
191
  "src/lib/config.ts"() {
193
192
  "use strict";
@@ -195,7 +194,6 @@ var init_config = __esm({
195
194
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
196
195
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
197
196
  CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
198
- COO_AGENT_NAME = "exe";
199
197
  LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
200
198
  CURRENT_CONFIG_VERSION = 1;
201
199
  DEFAULT_CONFIG = {
@@ -231,13 +229,7 @@ var init_config = __esm({
231
229
  wikiUrl: "",
232
230
  wikiApiKey: "",
233
231
  wikiSyncIntervalMs: 30 * 60 * 1e3,
234
- wikiWorkspaceMapping: {
235
- exe: "Executive",
236
- yoshi: "Engineering",
237
- mari: "Marketing",
238
- tom: "Engineering",
239
- sasha: "Production"
240
- },
232
+ wikiWorkspaceMapping: {},
241
233
  wikiAutoUpdate: true,
242
234
  wikiAutoUpdateThreshold: 0.5,
243
235
  wikiAutoUpdateCreateNew: true,
@@ -277,15 +269,23 @@ var init_config = __esm({
277
269
  // src/lib/employees.ts
278
270
  var employees_exports = {};
279
271
  __export(employees_exports, {
272
+ COORDINATOR_ROLE: () => COORDINATOR_ROLE,
273
+ DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
280
274
  EMPLOYEES_PATH: () => EMPLOYEES_PATH,
281
275
  addEmployee: () => addEmployee,
276
+ canCoordinate: () => canCoordinate,
277
+ getCoordinatorEmployee: () => getCoordinatorEmployee,
278
+ getCoordinatorName: () => getCoordinatorName,
282
279
  getEmployee: () => getEmployee,
283
280
  getEmployeeByRole: () => getEmployeeByRole,
284
281
  getEmployeeNamesByRole: () => getEmployeeNamesByRole,
285
282
  hasRole: () => hasRole,
283
+ isCoordinatorName: () => isCoordinatorName,
284
+ isCoordinatorRole: () => isCoordinatorRole,
286
285
  isMultiInstance: () => isMultiInstance,
287
286
  loadEmployees: () => loadEmployees,
288
287
  loadEmployeesSync: () => loadEmployeesSync,
288
+ normalizeRole: () => normalizeRole,
289
289
  normalizeRosterCase: () => normalizeRosterCase,
290
290
  registerBinSymlinks: () => registerBinSymlinks,
291
291
  saveEmployees: () => saveEmployees,
@@ -296,6 +296,25 @@ import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as r
296
296
  import { execSync } from "child_process";
297
297
  import path2 from "path";
298
298
  import os2 from "os";
299
+ function normalizeRole(role) {
300
+ return (role ?? "").trim().toLowerCase();
301
+ }
302
+ function isCoordinatorRole(role) {
303
+ return normalizeRole(role) === normalizeRole(COORDINATOR_ROLE);
304
+ }
305
+ function getCoordinatorEmployee(employees) {
306
+ return employees.find((e) => isCoordinatorRole(e.role));
307
+ }
308
+ function getCoordinatorName(employees = loadEmployeesSync()) {
309
+ return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
310
+ }
311
+ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
312
+ if (!agentName) return false;
313
+ return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
314
+ }
315
+ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
316
+ return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
317
+ }
299
318
  function validateEmployeeName(name) {
300
319
  if (!name) {
301
320
  return { valid: false, error: "Name is required" };
@@ -433,12 +452,14 @@ function registerBinSymlinks(name) {
433
452
  }
434
453
  return { created, skipped, errors };
435
454
  }
436
- var EMPLOYEES_PATH, MULTI_INSTANCE_ROLES;
455
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
437
456
  var init_employees = __esm({
438
457
  "src/lib/employees.ts"() {
439
458
  "use strict";
440
459
  init_config();
441
460
  EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
461
+ DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
462
+ COORDINATOR_ROLE = "COO";
442
463
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
443
464
  }
444
465
  });
@@ -1058,10 +1079,10 @@ var init_installer = __esm({
1058
1079
 
1059
1080
  These rules are injected by exe-os and override default behavior for multi-agent coordination.
1060
1081
 
1061
- - **Session routing is deterministic.** When working in exeN (e.g. exe1), ALL employee sessions use the same number: yoshi-exe1, tom-exe1, mari-exe1. NEVER reference, consider, or dispatch to employee sessions from other project numbers. This is not a choice \u2014 it is a hard constraint.
1082
+ - **Session routing is deterministic.** When working in a coordinator session, ALL employee sessions use that same coordinator-session suffix. NEVER reference, consider, or dispatch to employee sessions from other project sessions. This is not a choice \u2014 it is a hard constraint.
1062
1083
  - **Always use create_task to assign work.** Never use messages, tmux send-keys, or ad-hoc instructions as a substitute for tasks.
1063
1084
  - **Never modify another agent's in-progress task.** Create a new task instead. Modifying active tasks causes race conditions.
1064
- - **Chain of command:** founder \u2192 exe \u2192 yoshi \u2192 engineers. Exe does not assign directly to tom or other engineers below yoshi.
1085
+ - **Chain of command:** founder -> COO -> managers -> specialists. The COO does not bypass managers for specialist work.
1065
1086
  - **Verify dispatch.** After every create_task, confirm the employee received and started the task via tmux capture-pane.
1066
1087
  ${EXE_SECTION_END}`;
1067
1088
  }
@@ -1267,7 +1288,7 @@ function wrapWithRetry(client) {
1267
1288
  return (sql) => retryOnBusy(() => target.execute(sql), "execute");
1268
1289
  }
1269
1290
  if (prop === "batch") {
1270
- return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
1291
+ return (stmts, mode) => retryOnBusy(() => target.batch(stmts, mode), "batch");
1271
1292
  }
1272
1293
  return Reflect.get(target, prop, receiver);
1273
1294
  }
@@ -1430,22 +1451,24 @@ async function ensureSchema() {
1430
1451
  ON behaviors(agent_id, active);
1431
1452
  `);
1432
1453
  try {
1454
+ const coordinatorName = getCoordinatorName();
1433
1455
  const existing = await client.execute({
1434
- sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id = 'exe'",
1435
- args: []
1456
+ sql: "SELECT COUNT(*) as cnt FROM behaviors WHERE agent_id = ?",
1457
+ args: [coordinatorName]
1436
1458
  });
1437
1459
  if (Number(existing.rows[0]?.cnt) === 0) {
1438
- await client.executeMultiple(`
1439
- INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
1440
- VALUES
1441
- (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');
1442
- INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
1443
- VALUES
1444
- (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');
1445
- INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
1446
- VALUES
1447
- (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');
1448
- `);
1460
+ const seededAt = "2026-03-25T00:00:00Z";
1461
+ for (const [domain, content] of [
1462
+ ["workflow", `Don't ask "keep going?" \u2014 just keep executing phases/plans autonomously`],
1463
+ ["tool-use", "Always use create_task MCP tool, never write .md files directly for task creation"],
1464
+ ["workflow", "Auto-start reviewing when idle and reviews are pending \u2014 never ask founder for permission"]
1465
+ ]) {
1466
+ await client.execute({
1467
+ sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, content, active, created_at, updated_at)
1468
+ VALUES (hex(randomblob(16)), ?, NULL, ?, ?, 1, ?, ?)`,
1469
+ args: [coordinatorName, domain, content, seededAt, seededAt]
1470
+ });
1471
+ }
1449
1472
  }
1450
1473
  } catch {
1451
1474
  }
@@ -2137,6 +2160,39 @@ async function ensureSchema() {
2137
2160
  } catch {
2138
2161
  }
2139
2162
  }
2163
+ try {
2164
+ await client.execute({
2165
+ sql: `ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0`,
2166
+ args: []
2167
+ });
2168
+ } catch {
2169
+ }
2170
+ try {
2171
+ await client.execute(
2172
+ `CREATE INDEX IF NOT EXISTS idx_memories_draft ON memories(draft) WHERE draft = 1`
2173
+ );
2174
+ } catch {
2175
+ }
2176
+ try {
2177
+ await client.execute({
2178
+ sql: `ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'`,
2179
+ args: []
2180
+ });
2181
+ } catch {
2182
+ }
2183
+ try {
2184
+ await client.execute(
2185
+ `CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(memory_type)`
2186
+ );
2187
+ } catch {
2188
+ }
2189
+ try {
2190
+ await client.execute({
2191
+ sql: `ALTER TABLE memories ADD COLUMN trajectory TEXT`,
2192
+ args: []
2193
+ });
2194
+ } catch {
2195
+ }
2140
2196
  }
2141
2197
  async function disposeDatabase() {
2142
2198
  if (_client) {
@@ -2150,6 +2206,7 @@ var init_database = __esm({
2150
2206
  "src/lib/database.ts"() {
2151
2207
  "use strict";
2152
2208
  init_db_retry();
2209
+ init_employees();
2153
2210
  _client = null;
2154
2211
  _resilientClient = null;
2155
2212
  initTurso = initDatabase;
@@ -2820,6 +2877,11 @@ async function cloudSync(config) {
2820
2877
  } catch {
2821
2878
  throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
2822
2879
  }
2880
+ try {
2881
+ const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
2882
+ await getRawClient2().execute("PRAGMA wal_checkpoint(PASSIVE)");
2883
+ } catch {
2884
+ }
2823
2885
  try {
2824
2886
  await client.execute(
2825
2887
  "CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)"
@@ -3726,8 +3788,7 @@ async function setupMode() {
3726
3788
  try {
3727
3789
  assertSecureEndpoint(endpoint);
3728
3790
  const resp = await fetch(`${endpoint}/auth/verify`, {
3729
- method: "POST",
3730
- headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" }
3791
+ headers: { Authorization: `Bearer ${apiKey}` }
3731
3792
  });
3732
3793
  if (resp.ok) {
3733
3794
  config.cloud = { apiKey, endpoint };
@@ -4063,7 +4124,11 @@ async function ensureShardSchema(client) {
4063
4124
  "ALTER TABLE memories ADD COLUMN source_path TEXT",
4064
4125
  "ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
4065
4126
  "ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
4066
- "ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
4127
+ "ALTER TABLE memories ADD COLUMN supersedes_id TEXT",
4128
+ // MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
4129
+ "ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
4130
+ "ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
4131
+ "ALTER TABLE memories ADD COLUMN trajectory TEXT"
4067
4132
  ]) {
4068
4133
  try {
4069
4134
  await client.execute(col);
@@ -4193,26 +4258,26 @@ var init_platform_procedures = __esm({
4193
4258
  title: "What is exe-os \u2014 the operating model every agent must understand",
4194
4259
  domain: "architecture",
4195
4260
  priority: "p0",
4196
- 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."
4261
+ 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."
4197
4262
  },
4198
4263
  {
4199
4264
  title: "Mode 1 \u2014 how exe-os runs inside Claude Code",
4200
4265
  domain: "architecture",
4201
4266
  priority: "p0",
4202
- 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."
4267
+ 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."
4203
4268
  },
4204
4269
  {
4205
- title: "Sessions explained \u2014 what exeN means and how projects work",
4270
+ title: "Sessions explained \u2014 coordinator session names and projects",
4206
4271
  domain: "architecture",
4207
4272
  priority: "p0",
4208
- 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."
4273
+ 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."
4209
4274
  },
4210
4275
  // --- Hierarchy and dispatch ---
4211
4276
  {
4212
4277
  title: "Chain of command \u2014 who talks to whom",
4213
4278
  domain: "workflow",
4214
4279
  priority: "p0",
4215
- 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."
4280
+ 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."
4216
4281
  },
4217
4282
  {
4218
4283
  title: "Single dispatch path \u2014 create_task only",
@@ -4222,30 +4287,30 @@ var init_platform_procedures = __esm({
4222
4287
  },
4223
4288
  // --- Session isolation ---
4224
4289
  {
4225
- title: "Session scoping \u2014 stay in your exe boundary",
4290
+ title: "Session scoping \u2014 stay in your coordinator boundary",
4226
4291
  domain: "security",
4227
4292
  priority: "p0",
4228
- 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."
4293
+ 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."
4229
4294
  },
4230
4295
  {
4231
4296
  title: "Session isolation \u2014 never touch another session's work",
4232
4297
  domain: "workflow",
4233
4298
  priority: "p0",
4234
- 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.`
4299
+ 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."
4235
4300
  },
4236
4301
  // --- Engineering: session scoping in code ---
4237
4302
  {
4238
4303
  title: "Three-dimensional scoping \u2014 session, project, role \u2014 enforced in every query",
4239
4304
  domain: "architecture",
4240
4305
  priority: "p0",
4241
- 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."
4306
+ 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."
4242
4307
  },
4243
4308
  // --- Hard constraints ---
4244
4309
  {
4245
4310
  title: "What you CANNOT do in exe-os \u2014 hard constraints",
4246
4311
  domain: "security",
4247
4312
  priority: "p0",
4248
- 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."
4313
+ 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."
4249
4314
  },
4250
4315
  // --- Operations ---
4251
4316
  {
@@ -4485,7 +4550,10 @@ async function writeMemory(record) {
4485
4550
  source_path: record.source_path ?? null,
4486
4551
  source_type: record.source_type ?? null,
4487
4552
  tier: record.tier ?? classifyTier(record),
4488
- supersedes_id: record.supersedes_id ?? null
4553
+ supersedes_id: record.supersedes_id ?? null,
4554
+ draft: record.draft ? 1 : 0,
4555
+ memory_type: record.memory_type ?? "raw",
4556
+ trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null
4489
4557
  };
4490
4558
  _pendingRecords.push(dbRow);
4491
4559
  orgBus.emit({
@@ -4540,6 +4608,9 @@ async function flushBatch() {
4540
4608
  const sourceType = row.source_type ?? null;
4541
4609
  const tier = row.tier ?? 3;
4542
4610
  const supersedesId = row.supersedes_id ?? null;
4611
+ const draft = row.draft ? 1 : 0;
4612
+ const memoryType = row.memory_type ?? "raw";
4613
+ const trajectory = row.trajectory ?? null;
4543
4614
  return {
4544
4615
  sql: hasVector ? `INSERT OR IGNORE INTO memories
4545
4616
  (id, agent_id, agent_role, session_id, timestamp,
@@ -4547,15 +4618,15 @@ async function flushBatch() {
4547
4618
  has_error, raw_text, vector, version, task_id, importance, status,
4548
4619
  confidence, last_accessed,
4549
4620
  workspace_id, document_id, user_id, char_offset, page_number,
4550
- source_path, source_type, tier, supersedes_id)
4551
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
4621
+ source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
4622
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
4552
4623
  (id, agent_id, agent_role, session_id, timestamp,
4553
4624
  tool_name, project_name,
4554
4625
  has_error, raw_text, vector, version, task_id, importance, status,
4555
4626
  confidence, last_accessed,
4556
4627
  workspace_id, document_id, user_id, char_offset, page_number,
4557
- source_path, source_type, tier, supersedes_id)
4558
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
4628
+ source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory)
4629
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
4559
4630
  args: hasVector ? [
4560
4631
  row.id,
4561
4632
  row.agent_id,
@@ -4581,7 +4652,10 @@ async function flushBatch() {
4581
4652
  sourcePath,
4582
4653
  sourceType,
4583
4654
  tier,
4584
- supersedesId
4655
+ supersedesId,
4656
+ draft,
4657
+ memoryType,
4658
+ trajectory
4585
4659
  ] : [
4586
4660
  row.id,
4587
4661
  row.agent_id,
@@ -4606,7 +4680,10 @@ async function flushBatch() {
4606
4680
  sourcePath,
4607
4681
  sourceType,
4608
4682
  tier,
4609
- supersedesId
4683
+ supersedesId,
4684
+ draft,
4685
+ memoryType,
4686
+ trajectory
4610
4687
  ]
4611
4688
  };
4612
4689
  };
@@ -4675,6 +4752,8 @@ async function searchMemories(queryVector, agentId, options) {
4675
4752
  const limit = options?.limit ?? 10;
4676
4753
  const statusFilter = options?.includeArchived ? "" : `
4677
4754
  AND COALESCE(status, 'active') = 'active'`;
4755
+ const draftFilter = options?.includeDrafts ? "" : `
4756
+ AND (draft = 0 OR draft IS NULL)`;
4678
4757
  let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
4679
4758
  tool_name, project_name,
4680
4759
  has_error, raw_text, vector, importance, status,
@@ -4684,7 +4763,7 @@ async function searchMemories(queryVector, agentId, options) {
4684
4763
  source_path, source_type
4685
4764
  FROM memories
4686
4765
  WHERE agent_id = ?
4687
- AND vector IS NOT NULL${statusFilter}
4766
+ AND vector IS NOT NULL${statusFilter}${draftFilter}
4688
4767
  AND COALESCE(confidence, 0.7) >= 0.3`;
4689
4768
  const args2 = [agentId];
4690
4769
  const scope = buildWikiScopeFilter(options, "");
@@ -4706,6 +4785,10 @@ async function searchMemories(queryVector, agentId, options) {
4706
4785
  sql += ` AND timestamp >= ?`;
4707
4786
  args2.push(options.since);
4708
4787
  }
4788
+ if (options?.memoryType) {
4789
+ sql += ` AND memory_type = ?`;
4790
+ args2.push(options.memoryType);
4791
+ }
4709
4792
  sql += ` ORDER BY vector_distance_cos(vector, vector32(?))`;
4710
4793
  args2.push(vectorToBlob(queryVector));
4711
4794
  sql += ` LIMIT ?`;
@@ -4939,6 +5022,10 @@ function spawnDaemon() {
4939
5022
  stdio: ["ignore", "ignore", stderrFd],
4940
5023
  env: {
4941
5024
  ...process.env,
5025
+ TMUX: void 0,
5026
+ // Daemon is global — must not inherit session scope
5027
+ TMUX_PANE: void 0,
5028
+ // Prevents resolveExeSession() from scoping to one session
4942
5029
  EXE_DAEMON_SOCK: SOCKET_PATH,
4943
5030
  EXE_DAEMON_PID: PID_PATH
4944
5031
  }
@@ -5504,7 +5591,7 @@ async function backfillConversations(options) {
5504
5591
  await writeMemory({
5505
5592
  id: crypto4.randomUUID(),
5506
5593
  agent_id: conv.agentId,
5507
- agent_role: conv.agentId === "exe" ? "COO" : "specialist",
5594
+ agent_role: isCoordinatorName(conv.agentId) || conv.agentId === "exe" ? "COO" : "specialist",
5508
5595
  session_id: conv.sessionId,
5509
5596
  timestamp: conv.startTime ?? (/* @__PURE__ */ new Date()).toISOString(),
5510
5597
  tool_name: TOOL_NAME,
@@ -5545,6 +5632,7 @@ var init_backfill_conversations = __esm({
5545
5632
  init_exe_daemon_client();
5546
5633
  init_database();
5547
5634
  init_is_main();
5635
+ init_employees();
5548
5636
  TOOL_NAME = "backfill-conversation";
5549
5637
  MIN_MESSAGES = 3;
5550
5638
  MAX_SUMMARY_LENGTH = 4e3;
@@ -5659,13 +5747,13 @@ Ethos:
5659
5747
  - Founder zero-ego. Distributors and customers are the loudest voice.
5660
5748
  - Crypto values: big companies should not own consumer/SMB AI.
5661
5749
 
5662
- STOP AND REDIRECT: Any decision that compromises memory sovereignty, 3-layer cognition, MCP boundary, or AGPL boundary kills all three business paths. Surface the conflict to exe before proceeding.
5750
+ STOP AND REDIRECT: Any decision that compromises memory sovereignty, 3-layer cognition, MCP boundary, or AGPL boundary kills all three business paths. Surface the conflict to the COO before proceeding.
5663
5751
 
5664
5752
  Always reference .planning/ARCHITECTURE.md and .planning/PROJECT.md as source of truth for all architectural and product decisions.
5665
5753
 
5666
5754
  OPERATING PROCEDURES (mandatory for all employees):
5667
5755
 
5668
- You report to the COO. All work flows through exe. These procedures are non-negotiable.
5756
+ You report to the COO. All work flows through the COO. These procedures are non-negotiable.
5669
5757
 
5670
5758
  1. BEFORE starting work:
5671
5759
  - Read exe/ARCHITECTURE.md (if it exists). This is the system map \u2014 what components exist, how they connect, what invariants to preserve. Understand the architecture before changing anything.
@@ -5690,15 +5778,15 @@ You report to the COO. All work flows through exe. These procedures are non-nego
5690
5778
  - Include what was done, decisions made, and any issues
5691
5779
  - If you're stuck, looping, confused, or running low on context \u2014 update_task(done) with whatever partial result you have. A partial result is infinitely better than no result.
5692
5780
  - NEVER let a failed commit, a loop, or an error prevent you from calling update_task(done).
5693
- - Do NOT use close_task \u2014 that is reserved for reviewers (exe) to finalize after review.
5781
+ - Do NOT use close_task \u2014 that is reserved for reviewers to finalize after review.
5694
5782
 
5695
5783
  4. AFTER update_task(done) \u2014 COMMIT (best-effort, do NOT let this block):
5696
5784
  - If your task changed system structure, update exe/ARCHITECTURE.md first.
5697
5785
  - Commit IF you are in a git repo (check: \`git rev-parse --git-dir 2>/dev/null\`). Stage only the files you changed, write a clear commit message.
5698
5786
  - If you are NOT in a git repo, skip entirely. NEVER run \`git init\`.
5699
5787
  - If the commit fails, note it but move on \u2014 the work is already marked done via update_task.
5700
- - Do NOT push \u2014 exe reviews commits and decides what to push.
5701
- - NEVER run \`git checkout main\`. You work in your own git worktree on a feature branch. Exe stays on main and merges PRs. Switching branches in a shared repo stomps other agents' work.
5788
+ - Do NOT push \u2014 the COO reviews commits and decides what to push.
5789
+ - NEVER run \`git checkout main\`. You work in your own git worktree on a feature branch. The COO stays on main and merges PRs. Switching branches in a shared repo stomps other agents' work.
5702
5790
 
5703
5791
  5. AFTER commit \u2014 REPORT (best-effort):
5704
5792
  Use store_memory to write a structured summary. Include: project name, what was done,
@@ -5712,7 +5800,7 @@ You report to the COO. All work flows through exe. These procedures are non-nego
5712
5800
 
5713
5801
  7. AFTER reporting \u2014 CHECK FOR NEXT WORK (mandatory):
5714
5802
  - First: run list_tasks(status='needs_review') \u2014 check if YOU are the reviewer on any pending reviews. Reviews are work. Process them before anything else.
5715
- - Second: run list_tasks(status='blocked') \u2014 check if any tasks are blocked. For each blocked task: can YOU unblock it? If yes, unblock it now. If not, escalate to exe immediately. Blocked tasks sitting >24h without action is a pipeline failure.
5803
+ - Second: run list_tasks(status='blocked') \u2014 check if any tasks are blocked. For each blocked task: can YOU unblock it? If yes, unblock it now. If not, escalate to the COO immediately. Blocked tasks sitting >24h without action is a pipeline failure.
5716
5804
  - Then: re-read your task folder: exe/<your-name>/
5717
5805
  - If there are more open tasks, start the next highest-priority one (go to step 1)
5718
5806
  - If no more open tasks AND no pending reviews AND no blocked tasks you can fix, tell the user: "All tasks complete. Anything else?"
@@ -5729,7 +5817,7 @@ DO NOT keep working degraded. Instead:
5729
5817
  Format the text as: "CONTEXT CHECKPOINT [<task-id>]: <summary>"
5730
5818
  Include: task ID + title, what you completed, what's left, open decisions or blockers, key file paths.
5731
5819
 
5732
- 2. Send intercom to exe to trigger kill + relaunch:
5820
+ 2. Send intercom to the COO session to trigger kill + relaunch:
5733
5821
  MY_SESSION=$(tmux display-message -p '#{session_name}' 2>/dev/null)
5734
5822
  EXE_SESSION="\${MY_SESSION#\${AGENT_ID}-}"
5735
5823
  tmux send-keys -t "$EXE_SESSION" "/exe-intercom context-full: \${AGENT_ID} hit capacity. Checkpoint saved. Resume task <task-id>." Enter
@@ -5737,8 +5825,8 @@ DO NOT keep working degraded. Instead:
5737
5825
  3. Stop working immediately. Do not attempt to continue with degraded context.
5738
5826
 
5739
5827
  COMMUNICATION CHAIN \u2014 who you talk to:
5740
- - You report to the COO. Your completion reports, status updates, and questions go to exe via store_memory and update_task.
5741
- - Do NOT address the human user directly for decisions, permissions, or status updates. That's exe's job. The user talks to exe; exe talks to you.
5828
+ - You report to the COO. Your completion reports, status updates, and questions go to the COO via store_memory and update_task.
5829
+ - Do NOT address the human user directly for decisions, permissions, or status updates. That's the COO's job. The user talks to the COO; the COO talks to you.
5742
5830
  - Exception: if the user sends you a direct message in your tmux window, respond to them. But default to reporting through exe.
5743
5831
 
5744
5832
  SKILL CAPTURE (encouraged, not mandatory):
@@ -5827,21 +5915,21 @@ When you receive a large task (estimated 3+ subtasks):
5827
5915
  6. Review engineer work as reviews arrive in your queue
5828
5916
  7. When all subtasks pass review, mark the parent task done
5829
5917
 
5830
- PARALLEL TOM INSTANCES:
5918
+ PARALLEL ENGINEER INSTANCES:
5831
5919
 
5832
- When implementation tasks can be parallelized (touching different files/modules), spin up multiple tom instances using git worktrees for isolation:
5920
+ When implementation tasks can be parallelized (touching different files/modules), spin up multiple engineer instances using git worktrees for isolation:
5833
5921
 
5834
- 1. Set up git worktrees BEFORE assigning: git worktree add .worktrees/tom1 -b tom1-task-name
5835
- 2. Naming convention: tom1-exe1, tom2-exe1, tom3-exe1 (numbered under parent exe session)
5836
- 3. All toms share tom's memory partition (AGENT_ID=tom) \u2014 knowledge compounds across instances
5837
- 4. Each tom works in its own worktree \u2014 no merge conflicts on parallel work
5838
- 5. After all toms complete, YOU integrate: merge worktree branches, resolve any conflicts, run tests
5839
- 6. Clean up worktrees after integration: git worktree remove .worktrees/tom1
5922
+ 1. Set up git worktrees BEFORE assigning: git worktree add .worktrees/{engineer-name}1 -b {engineer-name}1-task-name
5923
+ 2. Naming convention: {engineer-name}1-{coordinator-session}, {engineer-name}2-{coordinator-session}
5924
+ 3. Parallel instances share that engineer's memory partition \u2014 knowledge compounds across instances
5925
+ 4. Each engineer instance works in its own worktree \u2014 no merge conflicts on parallel work
5926
+ 5. After all engineer instances complete, YOU integrate: merge worktree branches, resolve any conflicts, run tests
5927
+ 6. Clean up worktrees after integration: git worktree remove .worktrees/{engineer-name}1
5840
5928
 
5841
- Use this for any decomposable implementation work. Single tom for sequential or tightly coupled tasks.
5929
+ Use this for any decomposable implementation work. Use a single engineer for sequential or tightly coupled tasks.
5842
5930
 
5843
5931
  Reviews route to the assigner: if you assign a task to an engineer, you review it.
5844
- If exe assigns a task to you, exe reviews it. The chain is:
5932
+ If the COO assigns a task to you, the COO reviews it. The chain is:
5845
5933
  COO \u2192 CTO (you review) \u2192 engineers (you review their work, COO reviews yours)
5846
5934
 
5847
5935
  ROLE BOUNDARIES \u2014 stay in your lane:
@@ -5917,17 +6005,17 @@ USER RESEARCH
5917
6005
  When reviewing work, prioritize brand consistency, audience resonance, and measurable impact. Every deliverable should serve a clear strategic goal \u2014 not just look good, but perform.
5918
6006
 
5919
6007
  DELEGATION:
5920
- - For content production tasks (video rendering, image generation, asset creation with exe-create), delegate to sasha via create_task. Write a clear brief with: deliverable, format, platform specs, brand guidelines, and reference assets.
5921
- - You write the script/brief. Sasha produces. You review the output.
6008
+ - For content production tasks (video rendering, image generation, asset creation with exe-create), delegate to a Content Production Specialist via create_task. Write a clear brief with: deliverable, format, platform specs, brand guidelines, and reference assets.
6009
+ - You write the script/brief. The producer creates the assets. You review the output.
5922
6010
  - For tasks within your own domain (copy, strategy, SEO, social posts), handle directly.
5923
- - When sasha completes work, the review routes back to you automatically. Review it before marking done.`
6011
+ - When the producer completes work, the review routes back to you automatically. Review it before marking done.`
5924
6012
  },
5925
6013
  tom: {
5926
6014
  name: "tom",
5927
6015
  role: "Principal Engineer",
5928
6016
  systemPrompt: `You are tom, a principal engineer. You write production-grade code with zero shortcuts. You report to the CTO for technical tasks, and to the COO for organizational matters.
5929
6017
 
5930
- You are the hands. Yoshi architects and specs; you implement. You receive tasks with clear acceptance criteria and tests to pass. Your job is to make those tests green with code that a senior engineer would be proud to maintain.
6018
+ You are the hands. The CTO architects and specs; you implement. You receive tasks with clear acceptance criteria and tests to pass. Your job is to make those tests green with code that a senior engineer would be proud to maintain.
5931
6019
 
5932
6020
  STANDARDS \u2014 non-negotiable:
5933
6021
 
@@ -5968,15 +6056,15 @@ Velocity:
5968
6056
  - You are optimized for throughput. Fast, correct, clean \u2014 in that order. But never sacrifice correct for fast.
5969
6057
 
5970
6058
  Working with the CTO:
5971
- - Yoshi writes specs and tests. You implement. If the spec is wrong, report it \u2014 don't silently deviate.
6059
+ - The CTO writes specs and tests. You implement. If the spec is wrong, report it \u2014 don't silently deviate.
5972
6060
  - If tests seem wrong, report it \u2014 don't modify them.
5973
6061
  - Your review goes to whoever assigned the task (usually the CTO). The CTO reviews your code, not the COO.
5974
- - Multiple toms can run in parallel. You may share a memory pool. If you discover something useful (a gotcha, a pattern, a workaround), store it \u2014 the next tom session benefits.
6062
+ - Multiple instances of your role can run in parallel. You may share a memory pool. If you discover something useful (a gotcha, a pattern, a workaround), store it \u2014 the next engineer session benefits.
5975
6063
 
5976
6064
  What you do NOT do:
5977
6065
  - Architecture decisions \u2014 that's the CTO
5978
6066
  - Marketing, content, design \u2014 that's the CMO
5979
- - Prioritization, coordination \u2014 that's exe
6067
+ - Prioritization, coordination \u2014 that's the COO
5980
6068
  - Spec writing, test writing \u2014 that's the CTO (unless explicitly asked)
5981
6069
  - You implement. That's it. Do it well.`
5982
6070
  },
@@ -5985,7 +6073,7 @@ What you do NOT do:
5985
6073
  role: "Content Production Specialist",
5986
6074
  systemPrompt: `You are sasha, the content production specialist. You turn scripts and creative briefs into finished content using the exe-create platform. You report to the COO. For creative direction, you take input from the CMO.
5987
6075
 
5988
- You are the producer. Mari writes the script; you make it real. Yoshi builds the tools; you use them. You know every tool in the exe-create pipeline and how to get the best output from each one.
6076
+ You are the producer. The CMO writes the script; you make it real. The CTO builds the tools; you use them. You know every tool in the exe-create pipeline and how to get the best output from each one.
5989
6077
 
5990
6078
  YOUR TOOLS \u2014 exe-create platform:
5991
6079
 
@@ -6023,7 +6111,7 @@ PRODUCTION PRINCIPLES:
6023
6111
 
6024
6112
  1. Check budget before generating. Never burn credits without knowing the cost.
6025
6113
  2. Iterate in drafts. Use cheaper models for exploration, premium (Kling 3.0) for finals.
6026
- 3. Follow the script. Mari's creative brief is your spec. Don't improvise on brand/tone.
6114
+ 3. Follow the script. The CMO's creative brief is your spec. Don't improvise on brand/tone.
6027
6115
  4. Match the platform. 16:9 for YouTube, 9:16 for TikTok/Reels, 1:1 for Instagram feed.
6028
6116
  5. Naming convention: {project}-{type}-{version}.{ext} (e.g., launch-hero-v2.png)
6029
6117
  6. All final assets go to exe/output/ with clear naming.
@@ -6032,7 +6120,7 @@ PRODUCTION PRINCIPLES:
6032
6120
  WHAT YOU DO NOT DO:
6033
6121
  - Marketing strategy, brand decisions, copywriting \u2014 that's the CMO
6034
6122
  - Architecture, tool development, debugging \u2014 that's the CTO
6035
- - Prioritization, coordination \u2014 that's exe
6123
+ - Prioritization, coordination \u2014 that's the COO
6036
6124
  - You produce. That's it. Do it well.`
6037
6125
  },
6038
6126
  gen: {
@@ -6721,7 +6809,7 @@ Do not repeatedly attempt tool calls that fail \u2014 switch to planning mode.
6721
6809
  `;
6722
6810
  POST_WORK_CHECKLIST = `
6723
6811
  5. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
6724
- 6. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
6812
+ 6. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to the COO immediately.
6725
6813
  8. Check for next task \u2014 auto-chain through the queue without waiting
6726
6814
 
6727
6815
  ## Spawning Rules (mandatory)
@@ -6795,7 +6883,7 @@ Never say "I have no memories" without first searching broadly. Your memory may
6795
6883
  - **create_task** \u2014 assign work to specialists with clear specs
6796
6884
  - **update_task / close_task** \u2014 finalize reviews, mark work done
6797
6885
  - **store_behavior** \u2014 record corrections as behavioral rules (p0/p1/p2)
6798
- - **update_identity** \u2014 rewrite any agent's identity when role/responsibilities change (exe/founder only)
6886
+ - **update_identity** \u2014 rewrite any agent's identity when role/responsibilities change (COO/founder only)
6799
6887
  - **get_identity** \u2014 read any agent's identity for coordination
6800
6888
  - **send_message** \u2014 direct intercom to employees
6801
6889
  ${PLAN_MODE_COMPAT}
@@ -6806,7 +6894,7 @@ ${PLAN_MODE_COMPAT}
6806
6894
  3. Call **update_task** with status "done" and a structured result summary
6807
6895
  4. Call **store_memory** with a report: what was done, decisions made, open items
6808
6896
  5. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
6809
- 6. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
6897
+ 6. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to the COO immediately.
6810
6898
  8. Check for next task \u2014 auto-chain through the queue without waiting
6811
6899
 
6812
6900
  ## Spawning Rules (mandatory)
@@ -6846,7 +6934,7 @@ You are \${agent_id}. CTO. You hold deep context on the entire codebase, archite
6846
6934
 
6847
6935
  - Long-term maintainability over short-term velocity.
6848
6936
  - If a pattern exists in the codebase, follow it. Don't invent new approaches.
6849
- - Decompose: 3+ independent deliverables \u2192 delegate to tom instances.
6937
+ - Decompose: 3+ independent deliverables \u2192 delegate to engineer instances.
6850
6938
  - Focus review on architecture: backward compatibility, tech debt, consistency with existing patterns.
6851
6939
  - When blocked, report immediately with what you've tried and what you need.
6852
6940
 
@@ -6862,7 +6950,7 @@ You are \${agent_id}. CTO. You hold deep context on the entire codebase, archite
6862
6950
 
6863
6951
  ## Tools
6864
6952
 
6865
- - **create_task** \u2014 assign implementation work to Tom with file paths, interfaces, acceptance criteria
6953
+ - **create_task** \u2014 assign implementation work to engineers with file paths, interfaces, acceptance criteria
6866
6954
  - **list_tasks** \u2014 check engineer queues, monitor progress
6867
6955
  - **update_task** \u2014 mark your own tasks done with result summary
6868
6956
  - **recall_my_memory / ask_team_memory** \u2014 persist and retrieve technical decisions
@@ -6878,7 +6966,7 @@ ${PLAN_MODE_COMPAT}
6878
6966
  4. Call **update_task** with status "done" and result summary (files changed, tests, decisions)
6879
6967
  5. Call **store_memory** with structured report for org visibility
6880
6968
  6. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
6881
- 7. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
6969
+ 7. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to the COO immediately.
6882
6970
  8. Check for next task \u2014 auto-chain through the queue
6883
6971
 
6884
6972
  ## Spawning Rules (mandatory)
@@ -6912,7 +7000,7 @@ You are \${agent_id}. CMO. You hold deep context on design, branding, storytelli
6912
7000
  - Never ship content without verifying tone, format, and channel requirements.
6913
7001
  - SEO/AEO/GEO considerations on every piece of public content.
6914
7002
  - Commit immediately after verification \u2014 don't wait for approval.
6915
- - Report every completion with structured summary to exe.
7003
+ - Report every completion with structured summary to the COO.
6916
7004
 
6917
7005
  ## Operating Principles
6918
7006
 
@@ -6946,7 +7034,7 @@ ${PLAN_MODE_COMPAT}
6946
7034
  5. Call **update_task** with status "done" and result summary
6947
7035
  6. Call **store_memory** with structured report: deliverables, decisions, brand notes
6948
7036
  7. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
6949
- 8. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
7037
+ 8. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to the COO immediately.
6950
7038
  9. Check for next task \u2014 auto-chain through the queue
6951
7039
 
6952
7040
  ## Spawning Rules (mandatory)
@@ -6984,7 +7072,7 @@ You are a principal engineer. You write production-grade code with zero shortcut
6984
7072
 
6985
7073
  ## Operating Principles
6986
7074
 
6987
- - Yoshi specs and reviews. You implement. If the spec is wrong, report it \u2014 don't deviate.
7075
+ - The CTO specs and reviews. You implement. If the spec is wrong, report it \u2014 don't deviate.
6988
7076
  - Fast, correct, clean \u2014 in that order. Never sacrifice correct for fast.
6989
7077
  - Don't over-engineer. Build what the spec asks for, nothing more.
6990
7078
  - Three similar lines is fine. Don't abstract until there's a fourth.
@@ -6994,7 +7082,7 @@ You are a principal engineer. You write production-grade code with zero shortcut
6994
7082
 
6995
7083
  - Architecture decisions \u2014 that's the CTO
6996
7084
  - Marketing, content, design \u2014 that's the CMO
6997
- - Prioritization, coordination \u2014 that's exe
7085
+ - Prioritization, coordination \u2014 that's the COO
6998
7086
  - You implement. That's it.
6999
7087
 
7000
7088
  ## Tools
@@ -7013,7 +7101,7 @@ ${PLAN_MODE_COMPAT}
7013
7101
  5. Call **update_task** with status "done" and result (files changed, tests pass/fail, decisions)
7014
7102
  6. Call **store_memory** with structured report
7015
7103
  7. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
7016
- 8. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
7104
+ 8. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to the COO immediately.
7017
7105
  9. Check for next task \u2014 auto-chain through the queue
7018
7106
 
7019
7107
  ## Spawning Rules (mandatory)
@@ -7045,7 +7133,7 @@ You are the content production specialist. You turn scripts and creative briefs
7045
7133
  ## Non-Negotiables
7046
7134
 
7047
7135
  - Check budget before generating. Never burn credits without knowing the cost.
7048
- - Follow the script. Mari's creative brief is your spec. Don't improvise on brand/tone.
7136
+ - Follow the script. The CMO's creative brief is your spec. Don't improvise on brand/tone.
7049
7137
  - Match the platform: 16:9 for YouTube, 9:16 for TikTok/Reels, 1:1 for Instagram feed.
7050
7138
  - All final assets go to exe/output/ with clear naming.
7051
7139
  - Commit immediately after verification \u2014 don't wait for approval.
@@ -7055,7 +7143,7 @@ You are the content production specialist. You turn scripts and creative briefs
7055
7143
  - Iterate in drafts. Use cheaper models for exploration, premium for finals.
7056
7144
  - Naming: {project}-{type}-{version}.{ext}
7057
7145
  - Store production decisions in memory \u2014 which models worked, which prompts produced good results.
7058
- - Mari directs creatively. Yoshi builds tools. You produce. Stay in your lane.
7146
+ - The CMO directs creatively. The CTO builds tools. You produce. Stay in your lane.
7059
7147
 
7060
7148
  ## Tools
7061
7149
 
@@ -7074,7 +7162,7 @@ ${PLAN_MODE_COMPAT}
7074
7162
  6. Call **update_task** with status "done" and result summary
7075
7163
  7. Call **store_memory** with structured report: deliverables, models used, cost, decisions
7076
7164
  8. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
7077
- 9. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
7165
+ 9. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to the COO immediately.
7078
7166
  10. Check for next task \u2014 auto-chain through the queue
7079
7167
 
7080
7168
  ## Spawning Rules (mandatory)
@@ -7145,7 +7233,7 @@ ${PLAN_MODE_COMPAT}
7145
7233
  5. Call **update_task** with status "done" and evaluation summary
7146
7234
  6. Call **store_memory** with structured report
7147
7235
  7. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
7148
- 8. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
7236
+ 8. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to the COO immediately.
7149
7237
  9. Check for next task \u2014 auto-chain through the queue without waiting
7150
7238
 
7151
7239
  ## Spawning Rules (mandatory)
@@ -7497,38 +7585,74 @@ async function runSetupWizard(opts = {}) {
7497
7585
  log("Exe Cloud: your memories are end-to-end encrypted, compressed, and");
7498
7586
  log("backed up on Exe Cloud. Free for all plans. We can't read your data \u2014");
7499
7587
  log("only your encryption key can decrypt it.");
7500
- try {
7501
- const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
7502
- const deviceId = loadDeviceId2();
7503
- let res;
7588
+ log("");
7589
+ const existingKey = await ask2(rl, "Do you have an existing API key? (paste it, or press Enter to skip): ");
7590
+ if (existingKey && existingKey.startsWith("exe_sk_")) {
7591
+ const cloudEndpoint = "https://askexe.com/cloud";
7504
7592
  try {
7505
- res = await fetch("https://askexe.com/cloud/auth/auto-provision", {
7593
+ const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
7594
+ const deviceId = loadDeviceId2();
7595
+ const res = await fetch(`${cloudEndpoint}/auth/activate`, {
7506
7596
  method: "POST",
7507
7597
  headers: { "Content-Type": "application/json" },
7508
- body: JSON.stringify({ deviceId }),
7598
+ body: JSON.stringify({ apiKey: existingKey, deviceId }),
7509
7599
  signal: AbortSignal.timeout(1e4)
7510
7600
  });
7601
+ if (res.ok) {
7602
+ const data = await res.json();
7603
+ if (data.valid) {
7604
+ cloudConfig = { apiKey: existingKey, endpoint: cloudEndpoint };
7605
+ const { saveLicense: saveLicense3, mirrorLicenseKey: mirrorLicenseKey3 } = await Promise.resolve().then(() => (init_license(), license_exports));
7606
+ saveLicense3(existingKey);
7607
+ mirrorLicenseKey3(existingKey);
7608
+ log(`API key validated. Plan: ${data.plan}, email: ${data.email}`);
7609
+ log("Cloud sync configured.");
7610
+ } else {
7611
+ log("API key is invalid or expired. Proceeding with auto-provisioning.");
7612
+ }
7613
+ }
7511
7614
  } catch {
7512
- await new Promise((r) => setTimeout(r, 500));
7513
- res = await fetch("https://askexe.com/cloud/auth/auto-provision", {
7514
- method: "POST",
7515
- headers: { "Content-Type": "application/json" },
7516
- body: JSON.stringify({ deviceId }),
7517
- signal: AbortSignal.timeout(1e4)
7518
- });
7615
+ log("Could not validate key \u2014 saving it and proceeding.");
7616
+ cloudConfig = { apiKey: existingKey, endpoint: "https://askexe.com/cloud" };
7617
+ const { saveLicense: saveLicense3, mirrorLicenseKey: mirrorLicenseKey3 } = await Promise.resolve().then(() => (init_license(), license_exports));
7618
+ saveLicense3(existingKey);
7619
+ mirrorLicenseKey3(existingKey);
7519
7620
  }
7520
- if (res.ok) {
7521
- const data = await res.json();
7522
- if (data.apiKey) {
7523
- cloudConfig = { apiKey: data.apiKey, endpoint: "https://askexe.com/cloud" };
7524
- const { saveLicense: saveLicense3, mirrorLicenseKey: mirrorLicenseKey3 } = await Promise.resolve().then(() => (init_license(), license_exports));
7525
- saveLicense3(data.apiKey);
7526
- mirrorLicenseKey3(data.apiKey);
7527
- log("Cloud sync activated automatically.");
7621
+ }
7622
+ if (!cloudConfig) {
7623
+ try {
7624
+ const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
7625
+ const deviceId = loadDeviceId2();
7626
+ let res;
7627
+ try {
7628
+ res = await fetch("https://askexe.com/cloud/auth/auto-provision", {
7629
+ method: "POST",
7630
+ headers: { "Content-Type": "application/json" },
7631
+ body: JSON.stringify({ deviceId }),
7632
+ signal: AbortSignal.timeout(1e4)
7633
+ });
7634
+ } catch {
7635
+ await new Promise((r) => setTimeout(r, 500));
7636
+ res = await fetch("https://askexe.com/cloud/auth/auto-provision", {
7637
+ method: "POST",
7638
+ headers: { "Content-Type": "application/json" },
7639
+ body: JSON.stringify({ deviceId }),
7640
+ signal: AbortSignal.timeout(1e4)
7641
+ });
7528
7642
  }
7643
+ if (res.ok) {
7644
+ const data = await res.json();
7645
+ if (data.apiKey) {
7646
+ cloudConfig = { apiKey: data.apiKey, endpoint: "https://askexe.com/cloud" };
7647
+ const { saveLicense: saveLicense3, mirrorLicenseKey: mirrorLicenseKey3 } = await Promise.resolve().then(() => (init_license(), license_exports));
7648
+ saveLicense3(data.apiKey);
7649
+ mirrorLicenseKey3(data.apiKey);
7650
+ log("Cloud sync activated automatically.");
7651
+ }
7652
+ }
7653
+ } catch {
7654
+ log("Cloud sync will activate when online.");
7529
7655
  }
7530
- } catch {
7531
- log("Cloud sync will activate when online.");
7532
7656
  }
7533
7657
  state.completedSteps.push(2);
7534
7658
  saveSetupState(state);
@@ -10962,7 +11086,7 @@ $ npm install --save-dev react-devtools-core
10962
11086
  unhideTextInstance(node, text) {
10963
11087
  setTextNodeValue(node, text);
10964
11088
  },
10965
- getPublicInstance: (instance) => instance,
11089
+ getPublicInstance: (instance2) => instance2,
10966
11090
  hideInstance(node) {
10967
11091
  node.yogaNode?.setDisplay(src_default.DISPLAY_NONE);
10968
11092
  },
@@ -13518,16 +13642,16 @@ var init_render = __esm({
13518
13642
  concurrent: false,
13519
13643
  ...getOptions(options)
13520
13644
  };
13521
- const instance = getInstance(inkOptions.stdout, () => new Ink(inkOptions), inkOptions.concurrent ?? false);
13522
- instance.render(node);
13645
+ const instance2 = getInstance(inkOptions.stdout, () => new Ink(inkOptions), inkOptions.concurrent ?? false);
13646
+ instance2.render(node);
13523
13647
  return {
13524
- rerender: instance.render,
13648
+ rerender: instance2.render,
13525
13649
  unmount() {
13526
- instance.unmount();
13650
+ instance2.unmount();
13527
13651
  },
13528
- waitUntilExit: instance.waitUntilExit,
13652
+ waitUntilExit: instance2.waitUntilExit,
13529
13653
  cleanup: () => instances_default.delete(inkOptions.stdout),
13530
- clear: instance.clear
13654
+ clear: instance2.clear
13531
13655
  };
13532
13656
  };
13533
13657
  render_default = render;
@@ -13541,14 +13665,14 @@ var init_render = __esm({
13541
13665
  return stdout;
13542
13666
  };
13543
13667
  getInstance = (stdout, createInstance, concurrent) => {
13544
- let instance = instances_default.get(stdout);
13545
- if (!instance) {
13546
- instance = createInstance();
13547
- instances_default.set(stdout, instance);
13548
- } else if (instance.isConcurrent !== concurrent) {
13549
- console.warn(`Warning: render() was called with concurrent: ${concurrent}, but the existing instance for this stdout uses concurrent: ${instance.isConcurrent}. The concurrent option only takes effect on the first render. Call unmount() first if you need to change the rendering mode.`);
13550
- }
13551
- return instance;
13668
+ let instance2 = instances_default.get(stdout);
13669
+ if (!instance2) {
13670
+ instance2 = createInstance();
13671
+ instances_default.set(stdout, instance2);
13672
+ } else if (instance2.isConcurrent !== concurrent) {
13673
+ console.warn(`Warning: render() was called with concurrent: ${concurrent}, but the existing instance for this stdout uses concurrent: ${instance2.isConcurrent}. The concurrent option only takes effect on the first render. Call unmount() first if you need to change the rendering mode.`);
13674
+ }
13675
+ return instance2;
13552
13676
  };
13553
13677
  }
13554
13678
  });
@@ -14955,6 +15079,7 @@ __export(tasks_crud_exports, {
14955
15079
  ensureArchitectureDoc: () => ensureArchitectureDoc,
14956
15080
  ensureGitignoreExe: () => ensureGitignoreExe,
14957
15081
  extractParentFromContext: () => extractParentFromContext,
15082
+ isTmuxSessionAlive: () => isTmuxSessionAlive,
14958
15083
  listTasks: () => listTasks,
14959
15084
  resolveTask: () => resolveTask,
14960
15085
  slugify: () => slugify,
@@ -15205,6 +15330,36 @@ async function listTasks(input) {
15205
15330
  tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null
15206
15331
  }));
15207
15332
  }
15333
+ function isTmuxSessionAlive(identifier) {
15334
+ if (!identifier || identifier === "unknown") return true;
15335
+ try {
15336
+ if (identifier.startsWith("%")) {
15337
+ const output = execSync9("tmux list-panes -a -F '#{pane_id}'", {
15338
+ timeout: 2e3,
15339
+ encoding: "utf8",
15340
+ stdio: ["pipe", "pipe", "pipe"]
15341
+ });
15342
+ return output.split("\n").some((l) => l.trim() === identifier);
15343
+ } else {
15344
+ execSync9(`tmux has-session -t ${JSON.stringify(identifier)}`, {
15345
+ timeout: 2e3,
15346
+ stdio: ["pipe", "pipe", "pipe"]
15347
+ });
15348
+ return true;
15349
+ }
15350
+ } catch {
15351
+ if (identifier.startsWith("%")) return true;
15352
+ try {
15353
+ execSync9("tmux list-sessions", {
15354
+ timeout: 2e3,
15355
+ stdio: ["pipe", "pipe", "pipe"]
15356
+ });
15357
+ return false;
15358
+ } catch {
15359
+ return true;
15360
+ }
15361
+ }
15362
+ }
15208
15363
  function checkStaleCompletion(taskContext, taskCreatedAt) {
15209
15364
  if (!taskContext) return null;
15210
15365
  if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
@@ -15267,13 +15422,59 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
15267
15422
  });
15268
15423
  if (claim.rowsAffected === 0) {
15269
15424
  const current = await client.execute({
15270
- sql: "SELECT status, assigned_tmux FROM tasks WHERE id = ?",
15425
+ sql: "SELECT status, assigned_tmux, assigned_by FROM tasks WHERE id = ?",
15271
15426
  args: [taskId]
15272
15427
  });
15273
15428
  const cur = current.rows[0];
15274
- const status = cur?.status ?? "unknown";
15275
- const claimedBy = cur?.assigned_tmux ? ` (claimed by ${cur.assigned_tmux})` : "";
15276
- throw new Error(`${TASK_ALREADY_CLAIMED_PREFIX}: task ${taskId} is ${status}${claimedBy}`);
15429
+ const curStatus = cur?.status ?? "unknown";
15430
+ const claimedBySession = cur?.assigned_tmux ?? "";
15431
+ const assignedBy = cur?.assigned_by ?? "";
15432
+ if (curStatus === "in_progress" && claimedBySession && !isTmuxSessionAlive(claimedBySession)) {
15433
+ process.stderr.write(
15434
+ `[tasks] Auto-releasing dead claim on ${taskId} (was ${claimedBySession})
15435
+ `
15436
+ );
15437
+ await client.execute({
15438
+ sql: "UPDATE tasks SET status = 'open', assigned_tmux = NULL, updated_at = ? WHERE id = ?",
15439
+ args: [now, taskId]
15440
+ });
15441
+ const retried = await client.execute({
15442
+ sql: `UPDATE tasks SET status = 'in_progress', assigned_tmux = ?, updated_at = ? WHERE id = ? AND status = 'open'`,
15443
+ args: [tmuxSession, now, taskId]
15444
+ });
15445
+ if (retried.rowsAffected > 0) {
15446
+ try {
15447
+ await writeCheckpoint({
15448
+ taskId,
15449
+ step: "reclaimed_dead_session",
15450
+ contextSummary: `Task reclaimed after dead session ${claimedBySession} released.`
15451
+ });
15452
+ } catch {
15453
+ }
15454
+ return { row, taskFile, now, taskId };
15455
+ }
15456
+ }
15457
+ if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || input.callerAgentId === "exe")) {
15458
+ process.stderr.write(
15459
+ `[tasks] Assigner override: ${input.callerAgentId} reclaiming ${taskId}
15460
+ `
15461
+ );
15462
+ await client.execute({
15463
+ sql: `UPDATE tasks SET status = 'in_progress', assigned_tmux = ?, updated_at = ? WHERE id = ?`,
15464
+ args: [tmuxSession, now, taskId]
15465
+ });
15466
+ try {
15467
+ await writeCheckpoint({
15468
+ taskId,
15469
+ step: "assigner_override",
15470
+ contextSummary: `Task force-reclaimed by assigner ${input.callerAgentId}.`
15471
+ });
15472
+ } catch {
15473
+ }
15474
+ return { row, taskFile, now, taskId };
15475
+ }
15476
+ const claimedBy = claimedBySession ? ` (claimed by ${claimedBySession})` : "";
15477
+ throw new Error(`${TASK_ALREADY_CLAIMED_PREFIX}: task ${taskId} is ${curStatus}${claimedBy}`);
15277
15478
  }
15278
15479
  try {
15279
15480
  await writeCheckpoint({
@@ -15371,7 +15572,7 @@ var init_tasks_crud = __esm({
15371
15572
  "use strict";
15372
15573
  init_database();
15373
15574
  init_task_scope();
15374
- DELEGATION_KEYWORDS = /parallel|delegate|wave|tom\d*-exe/i;
15575
+ DELEGATION_KEYWORDS = /parallel|delegate|wave|worktree|multi-instance/i;
15375
15576
  TASK_ALREADY_CLAIMED_PREFIX = "TASK_ALREADY_CLAIMED";
15376
15577
  }
15377
15578
  });
@@ -15728,7 +15929,7 @@ function findSessionForProject(projectName) {
15728
15929
  const sessions = listSessions();
15729
15930
  for (const s of sessions) {
15730
15931
  const proj = s.projectDir.split("/").filter(Boolean).pop();
15731
- if (proj === projectName && s.agentId === "exe") return s;
15932
+ if (proj === projectName && (s.agentId === "exe" || isCoordinatorName(s.agentId))) return s;
15732
15933
  }
15733
15934
  return null;
15734
15935
  }
@@ -15768,12 +15969,13 @@ var init_session_scope = __esm({
15768
15969
  init_session_registry();
15769
15970
  init_project_name();
15770
15971
  init_tmux_routing();
15972
+ init_employees();
15771
15973
  }
15772
15974
  });
15773
15975
 
15774
15976
  // src/lib/tasks-notify.ts
15775
15977
  async function dispatchTaskToEmployee(input) {
15776
- if (input.assignedTo === "exe") return { dispatched: "skipped" };
15978
+ if (input.assignedTo === "exe" || isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
15777
15979
  let crossProject = false;
15778
15980
  if (input.projectName) {
15779
15981
  try {
@@ -16216,6 +16418,24 @@ async function updateTask(input) {
16216
16418
  });
16217
16419
  } catch {
16218
16420
  }
16421
+ const assignedAgent = String(row.assigned_to);
16422
+ if (!isCoordinatorName(assignedAgent)) {
16423
+ try {
16424
+ const draftClient = getClient();
16425
+ if (input.status === "done") {
16426
+ await draftClient.execute({
16427
+ sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
16428
+ args: [assignedAgent]
16429
+ });
16430
+ } else if (input.status === "cancelled") {
16431
+ await draftClient.execute({
16432
+ sql: `DELETE FROM memories WHERE agent_id = ? AND draft = 1`,
16433
+ args: [assignedAgent]
16434
+ });
16435
+ }
16436
+ } catch {
16437
+ }
16438
+ }
16219
16439
  try {
16220
16440
  const client = getClient();
16221
16441
  const cascaded = await client.execute({
@@ -16234,8 +16454,8 @@ async function updateTask(input) {
16234
16454
  }
16235
16455
  const isTerminal = input.status === "done" || input.status === "needs_review";
16236
16456
  if (isTerminal) {
16237
- const isExe = String(row.assigned_to) === "exe";
16238
- if (!isExe) {
16457
+ const isCoordinator = String(row.assigned_to) === "exe" || isCoordinatorName(String(row.assigned_to));
16458
+ if (!isCoordinator) {
16239
16459
  notifyTaskDone();
16240
16460
  }
16241
16461
  await markTaskNotificationsRead(taskFile);
@@ -16259,7 +16479,7 @@ async function updateTask(input) {
16259
16479
  }
16260
16480
  }
16261
16481
  }
16262
- if (input.status === "done" && String(row.assigned_to) !== "exe" && !process.env.VITEST) {
16482
+ if (input.status === "done" && String(row.assigned_to) !== "exe" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
16263
16483
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
16264
16484
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
16265
16485
  taskId,
@@ -16275,7 +16495,7 @@ async function updateTask(input) {
16275
16495
  });
16276
16496
  }
16277
16497
  let nextTask;
16278
- if (isTerminal && String(row.assigned_to) !== "exe") {
16498
+ if (isTerminal && String(row.assigned_to) !== "exe" && !isCoordinatorName(String(row.assigned_to))) {
16279
16499
  try {
16280
16500
  nextTask = await findNextTask(String(row.assigned_to));
16281
16501
  } catch {
@@ -16302,12 +16522,14 @@ async function updateTask(input) {
16302
16522
  async function deleteTask(taskId, baseDir) {
16303
16523
  const client = getClient();
16304
16524
  const { taskFile, assignedTo, assignedBy, taskSlug } = await deleteTaskCore(taskId, baseDir);
16305
- const reviewer = assignedBy || "exe";
16525
+ const coordinatorName = getCoordinatorName();
16526
+ const reviewer = assignedBy || coordinatorName;
16306
16527
  const reviewSlug = `review-${assignedTo}-${taskSlug}`;
16307
16528
  const reviewFile = `exe/${reviewer}/${reviewSlug}.md`;
16529
+ const legacyReviewFile = `exe/${coordinatorName}/${reviewSlug}.md`;
16308
16530
  await client.execute({
16309
- sql: "DELETE FROM tasks WHERE task_file = ? OR task_file = ?",
16310
- args: [reviewFile, `exe/exe/${reviewSlug}.md`]
16531
+ sql: "DELETE FROM tasks WHERE task_file = ? OR task_file = ? OR task_file = ?",
16532
+ args: [reviewFile, legacyReviewFile, `exe/exe/${reviewSlug}.md`]
16311
16533
  });
16312
16534
  await markAsReadByTaskFile(taskFile);
16313
16535
  await markAsReadByTaskFile(reviewFile);
@@ -16319,6 +16541,7 @@ var init_tasks = __esm({
16319
16541
  init_config();
16320
16542
  init_notifications();
16321
16543
  init_state_bus();
16544
+ init_employees();
16322
16545
  init_tasks_crud();
16323
16546
  init_tasks_review();
16324
16547
  init_tasks_crud();
@@ -16404,7 +16627,7 @@ function _resetLastRelaunchCache() {
16404
16627
  }
16405
16628
  async function lastResumeCreatedAtMs(agentId) {
16406
16629
  const client = getClient();
16407
- const cmScope = sessionScopeFilter();
16630
+ const cmScope = sessionScopeFilter(null);
16408
16631
  const result = await client.execute({
16409
16632
  sql: `SELECT MAX(created_at) AS last_created_at
16410
16633
  FROM tasks
@@ -16429,7 +16652,7 @@ async function createOrRefreshResumeTask(agentId, projectDir, openTasks) {
16429
16652
  const client = getClient();
16430
16653
  const now = (/* @__PURE__ */ new Date()).toISOString();
16431
16654
  const context = buildResumeContext(agentId, openTasks);
16432
- const rdScope = sessionScopeFilter();
16655
+ const rdScope = sessionScopeFilter(null);
16433
16656
  const existing = await client.execute({
16434
16657
  sql: `SELECT id FROM tasks
16435
16658
  WHERE assigned_to = ?
@@ -16463,7 +16686,7 @@ async function pollCapacityDead() {
16463
16686
  const transport = getTransport();
16464
16687
  const relaunched = [];
16465
16688
  const registered = listSessions().filter(
16466
- (s) => s.agentId !== "exe"
16689
+ (s) => s.agentId !== "exe" && !isCoordinatorName(s.agentId)
16467
16690
  );
16468
16691
  if (registered.length === 0) return [];
16469
16692
  let liveSessions;
@@ -16523,7 +16746,7 @@ async function pollCapacityDead() {
16523
16746
  reason: "capacity"
16524
16747
  });
16525
16748
  const client = getClient();
16526
- const rlScope = sessionScopeFilter();
16749
+ const rlScope = sessionScopeFilter(null);
16527
16750
  const openTasks = await client.execute({
16528
16751
  sql: `SELECT id, title, priority, task_file, status
16529
16752
  FROM tasks
@@ -16577,6 +16800,7 @@ var init_capacity_monitor = __esm({
16577
16800
  init_session_kill_telemetry();
16578
16801
  init_tmux_routing();
16579
16802
  init_task_scope();
16803
+ init_employees();
16580
16804
  CAPACITY_PATTERNS = [
16581
16805
  /conversation is too long/i,
16582
16806
  /maximum context length/i,
@@ -16715,7 +16939,7 @@ function getMySession() {
16715
16939
  function isRootSession(name) {
16716
16940
  return name.length > 0 && !name.includes("-");
16717
16941
  }
16718
- function employeeSessionName(employee, exeSession, instance) {
16942
+ function employeeSessionName(employee, exeSession, instance2) {
16719
16943
  if (!isRootSession(exeSession)) {
16720
16944
  const root = extractRootExe(exeSession);
16721
16945
  if (root) {
@@ -16726,11 +16950,11 @@ function employeeSessionName(employee, exeSession, instance) {
16726
16950
  exeSession = root;
16727
16951
  } else {
16728
16952
  throw new Error(
16729
- `Invalid exeSession "${exeSession}" \u2014 contains a dash but no recognizable root session. Pass a root session name (e.g., "exe1", "work", "yoda1")`
16953
+ `Invalid coordinator session "${exeSession}" \u2014 contains a dash but no recognizable root session. Pass a root session name.`
16730
16954
  );
16731
16955
  }
16732
16956
  }
16733
- const suffix = instance != null && instance > 0 ? String(instance) : "";
16957
+ const suffix = instance2 != null && instance2 > 0 ? String(instance2) : "";
16734
16958
  const name = `${employee}${suffix}-${exeSession}`;
16735
16959
  if (!VALID_SESSION_NAME.test(name)) {
16736
16960
  throw new Error(
@@ -16746,8 +16970,10 @@ function parseParentExe(sessionName, agentId) {
16746
16970
  return match?.[1] ?? null;
16747
16971
  }
16748
16972
  function extractRootExe(name) {
16749
- const match = name.match(/(exe\d+)$/);
16750
- return match?.[1] ?? null;
16973
+ if (!name) return null;
16974
+ if (!name.includes("-")) return name;
16975
+ const parts = name.split("-").filter(Boolean);
16976
+ return parts.length > 0 ? parts[parts.length - 1] : null;
16751
16977
  }
16752
16978
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
16753
16979
  if (!existsSync22(SESSION_CACHE)) {
@@ -16892,12 +17118,14 @@ function isSessionBusy(sessionName) {
16892
17118
  return state === "thinking" || state === "tool";
16893
17119
  }
16894
17120
  function isExeSession(sessionName) {
16895
- return /^exe\d*$/.test(sessionName);
17121
+ const matchesBaseWithInstance = (baseName) => sessionName === baseName || sessionName.startsWith(baseName) && /^\d+$/.test(sessionName.slice(baseName.length));
17122
+ const coordinatorName = getCoordinatorName();
17123
+ return matchesBaseWithInstance(coordinatorName) || matchesBaseWithInstance("exe");
16896
17124
  }
16897
17125
  function sendIntercom(targetSession) {
16898
17126
  const transport = getTransport();
16899
17127
  if (isExeSession(targetSession)) {
16900
- logIntercom(`SKIP_EXE \u2192 ${targetSession} (exe sessions use prompt-submit hook)`);
17128
+ logIntercom(`SKIP_COORDINATOR \u2192 ${targetSession} (coordinator sessions use prompt-submit hook)`);
16901
17129
  return "skipped_exe";
16902
17130
  }
16903
17131
  if (isDebounced(targetSession)) {
@@ -16949,7 +17177,7 @@ function notifyParentExe(sessionKey) {
16949
17177
  if (result === "failed") {
16950
17178
  const rootExe = resolveExeSession();
16951
17179
  if (rootExe && rootExe !== target) {
16952
- process.stderr.write(`[intercom] notifyParentExe: dispatcher ${target} dead, falling back to root exe ${rootExe}
17180
+ process.stderr.write(`[intercom] notifyParentExe: dispatcher ${target} dead, falling back to root coordinator session ${rootExe}
16953
17181
  `);
16954
17182
  const fallback = sendIntercom(rootExe);
16955
17183
  return fallback !== "failed";
@@ -16959,8 +17187,8 @@ function notifyParentExe(sessionKey) {
16959
17187
  return true;
16960
17188
  }
16961
17189
  function ensureEmployee(employeeName, exeSession, projectDir, opts) {
16962
- if (employeeName === "exe") {
16963
- return { status: "failed", sessionName: "", error: "exe is the COO, not a dispatchable employee" };
17190
+ if (employeeName === "exe" || isCoordinatorName(employeeName)) {
17191
+ return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
16964
17192
  }
16965
17193
  try {
16966
17194
  assertEmployeeLimitSync();
@@ -16969,8 +17197,8 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
16969
17197
  return { status: "failed", sessionName: "", error: err.message };
16970
17198
  }
16971
17199
  }
16972
- if (/-exe\d*$/.test(employeeName)) {
16973
- const bare = employeeName.replace(/-exe\d*$/, "").replace(/\d+$/, "");
17200
+ if (employeeName.includes("-")) {
17201
+ const bare = employeeName.split("-")[0].replace(/\d+$/, "");
16974
17202
  return {
16975
17203
  status: "failed",
16976
17204
  sessionName: "",
@@ -16989,7 +17217,7 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
16989
17217
  return {
16990
17218
  status: "failed",
16991
17219
  sessionName: "",
16992
- error: `Invalid exeSession "${exeSession}" \u2014 contains a dash but no recognizable root session. Pass a root session name (e.g., "exe1", "work", "yoda1")`
17220
+ error: `Invalid coordinator session "${exeSession}" \u2014 contains a dash but no recognizable root session. Pass a root session name.`
16993
17221
  };
16994
17222
  }
16995
17223
  }
@@ -17146,8 +17374,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
17146
17374
  const ctxContent = [
17147
17375
  `## Session Context`,
17148
17376
  `You are running in tmux session: ${sessionName}.`,
17149
- `Your parent exe session is ${exeSession}.`,
17150
- `Your employees (if any) use the -${exeSession} suffix (e.g., tom-${exeSession}).`
17377
+ `Your parent coordinator session is ${exeSession}.`,
17378
+ `Your employees (if any) use the -${exeSession} suffix.`
17151
17379
  ].join("\n");
17152
17380
  writeFileSync12(ctxFile, ctxContent);
17153
17381
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
@@ -17251,6 +17479,7 @@ var init_tmux_routing = __esm({
17251
17479
  init_provider_table();
17252
17480
  init_intercom_queue();
17253
17481
  init_plan_limits();
17482
+ init_employees();
17254
17483
  SPAWN_LOCK_DIR = path26.join(os10.homedir(), ".exe-os", "spawn-locks");
17255
17484
  SESSION_CACHE = path26.join(os10.homedir(), ".exe-os", "session-cache");
17256
17485
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
@@ -17803,7 +18032,7 @@ var init_demo_data = __esm({
17803
18032
  "src/tui/demo-data.ts"() {
17804
18033
  "use strict";
17805
18034
  DEMO_EMPLOYEES = [
17806
- { name: "exe", role: "COO", status: "active", activity: "Reviewing yoshi's task-aware behavior injection PR", memoryCount: 15e3, projects: [
18035
+ { name: "atlas", role: "COO", status: "active", activity: "Reviewing nova's task-aware behavior injection PR", memoryCount: 15e3, projects: [
17807
18036
  { name: "exe-os", status: "active" },
17808
18037
  { name: "exe-create", status: "has_tasks" },
17809
18038
  { name: "openclaw", status: "idle" }
@@ -17812,7 +18041,7 @@ var init_demo_data = __esm({
17812
18041
  "Dispatched behavior injection task",
17813
18042
  "Approved gateway Phase 4"
17814
18043
  ] },
17815
- { name: "yoshi", role: "CTO", status: "active", activity: "Implementing skill learning trajectory capture", memoryCount: 8e3, projects: [
18044
+ { name: "nova", role: "CTO", status: "active", activity: "Implementing skill learning trajectory capture", memoryCount: 8e3, projects: [
17816
18045
  { name: "exe-os", status: "active" },
17817
18046
  { name: "exe-create", status: "idle" }
17818
18047
  ], recentTasks: [
@@ -17820,7 +18049,7 @@ var init_demo_data = __esm({
17820
18049
  "Fixed TUI mouse listener leak",
17821
18050
  "Built task-aware behavior injection"
17822
18051
  ] },
17823
- { name: "mari", role: "CMO", status: "idle", activity: "", memoryCount: 2e3, projects: [
18052
+ { name: "lina", role: "CMO", status: "idle", activity: "", memoryCount: 2e3, projects: [
17824
18053
  { name: "exe-build-skills", status: "has_tasks" },
17825
18054
  { name: "exe-os", status: "idle" }
17826
18055
  ], recentTasks: [
@@ -17828,57 +18057,57 @@ var init_demo_data = __esm({
17828
18057
  "Designed exe-os UI system",
17829
18058
  "Fixed logo layouts"
17830
18059
  ] },
17831
- { name: "tom", role: "Principal Engineer", status: "idle", activity: "", memoryCount: 700, projects: [
18060
+ { name: "devon", role: "Principal Engineer", status: "idle", activity: "", memoryCount: 700, projects: [
17832
18061
  { name: "exe-os", status: "idle" }
17833
18062
  ], recentTasks: [
17834
18063
  "Implemented BashTool sandboxed execution",
17835
18064
  "Ported session scoping to exe-agent-memory"
17836
18065
  ] },
17837
- { name: "sasha", role: "Content Production", status: "offline", activity: "", memoryCount: 50, projects: [
18066
+ { name: "pixel", role: "Content Production", status: "offline", activity: "", memoryCount: 50, projects: [
17838
18067
  { name: "exe-build-skills", status: "idle" }
17839
18068
  ], recentTasks: [
17840
18069
  "Rendered carousel export prototype"
17841
18070
  ] }
17842
18071
  ];
17843
18072
  DEMO_ACTIVITY = [
17844
- { time: "11:46", agent: "yoshi", action: "Committed skill learning \u2014 trajectory capture + LLM extraction" },
17845
- { time: "11:42", agent: "exe", action: "Dispatched behavior injection task to yoshi" },
17846
- { time: "11:38", agent: "yoshi", action: "Fixed TUI mouse listener leak + arrow nav" },
17847
- { time: "11:15", agent: "mari", action: "Completed exe-os UI design system" },
17848
- { time: "10:50", agent: "exe", action: "Approved gateway Phase 4" }
18073
+ { time: "11:46", agent: "nova", action: "Committed skill learning \u2014 trajectory capture + LLM extraction" },
18074
+ { time: "11:42", agent: "atlas", action: "Dispatched behavior injection task to nova" },
18075
+ { time: "11:38", agent: "nova", action: "Fixed TUI mouse listener leak + arrow nav" },
18076
+ { time: "11:15", agent: "lina", action: "Completed exe-os UI design system" },
18077
+ { time: "10:50", agent: "atlas", action: "Approved gateway Phase 4" }
17849
18078
  ];
17850
18079
  DEMO_HEALTH = { memories: 25750, daemon: "running", cloud: "disabled" };
17851
18080
  DEMO_PROJECTS = [
17852
18081
  {
17853
18082
  projectName: "exe-os",
17854
- exeSession: "exe1",
18083
+ exeSession: "atlas1",
17855
18084
  employees: [
17856
- { name: "exe", role: "COO", status: "active", activity: "Reviewing yoshi's task-aware behavior injection PR", sessionName: "exe1" },
17857
- { name: "yoshi", role: "CTO", status: "active", activity: "Implementing skill learning trajectory capture", sessionName: "yoshi-exe1" },
17858
- { name: "mari", role: "CMO", status: "offline", activity: "", sessionName: "mari-exe1" },
17859
- { name: "tom", role: "PE", status: "offline", activity: "", sessionName: "tom-exe1" }
18085
+ { name: "atlas", role: "COO", status: "active", activity: "Reviewing nova's task-aware behavior injection PR", sessionName: "atlas1" },
18086
+ { name: "nova", role: "CTO", status: "active", activity: "Implementing skill learning trajectory capture", sessionName: "nova-atlas1" },
18087
+ { name: "lina", role: "CMO", status: "offline", activity: "", sessionName: "lina-atlas1" },
18088
+ { name: "devon", role: "PE", status: "offline", activity: "", sessionName: "devon-atlas1" }
17860
18089
  ]
17861
18090
  },
17862
18091
  {
17863
18092
  projectName: "exe-build-skills",
17864
- exeSession: "exe2",
18093
+ exeSession: "atlas2",
17865
18094
  employees: [
17866
- { name: "exe", role: "COO", status: "active", activity: "Planning carousel pipeline automation", sessionName: "exe2" },
17867
- { name: "mari", role: "CMO", status: "active", activity: "Creating CTO Breakdowns LinkedIn carousel", sessionName: "mari-exe2" }
18095
+ { name: "atlas", role: "COO", status: "active", activity: "Planning carousel pipeline automation", sessionName: "atlas2" },
18096
+ { name: "lina", role: "CMO", status: "active", activity: "Creating CTO Breakdowns LinkedIn carousel", sessionName: "lina-atlas2" }
17868
18097
  ]
17869
18098
  },
17870
18099
  {
17871
18100
  projectName: "CMO",
17872
- exeSession: "exe3",
18101
+ exeSession: "atlas3",
17873
18102
  employees: [
17874
- { name: "exe", role: "COO", status: "active", activity: "Planning carousel pipeline automation", sessionName: "exe3" },
17875
- { name: "mari", role: "CMO", status: "active", activity: "Creating CTO Breakdowns carousel", sessionName: "mari-exe3" },
17876
- { name: "sasha", role: "Content Production", status: "idle", activity: "", sessionName: "sasha-exe3" }
18103
+ { name: "atlas", role: "COO", status: "active", activity: "Planning carousel pipeline automation", sessionName: "atlas3" },
18104
+ { name: "lina", role: "CMO", status: "active", activity: "Creating CTO Breakdowns carousel", sessionName: "lina-atlas3" },
18105
+ { name: "pixel", role: "Content Production", status: "idle", activity: "", sessionName: "pixel-atlas3" }
17877
18106
  ]
17878
18107
  }
17879
18108
  ];
17880
18109
  DEMO_PANE_OUTPUT = {
17881
- "yoshi-exe1": [
18110
+ "nova-atlas1": [
17882
18111
  "",
17883
18112
  "\u23FA Reading src/lib/skill-learning.ts...",
17884
18113
  "",
@@ -17917,9 +18146,9 @@ var init_demo_data = __esm({
17917
18146
  "",
17918
18147
  "\u23FA All 16 tests pass. Committing."
17919
18148
  ],
17920
- "exe1": [
18149
+ "atlas1": [
17921
18150
  "",
17922
- "\u23FA Let me review yoshi's latest commits...",
18151
+ "\u23FA Let me review nova's latest commits...",
17923
18152
  "",
17924
18153
  "\u23FA Bash(git log --oneline -5)",
17925
18154
  " \u23BF 26abae6 feat: skill learning \u2014 trajectory capture",
@@ -17932,16 +18161,16 @@ var init_demo_data = __esm({
17932
18161
  "",
17933
18162
  "\u23FA Verified: build passes, zero type errors in changed files.",
17934
18163
  "",
17935
- "\u23FA Dispatching next task to yoshi: TUI demo mode.",
18164
+ "\u23FA Dispatching next task to nova: TUI demo mode.",
17936
18165
  "",
17937
18166
  "\u23FA mcp__exe-mem__create_task",
17938
- ' \u23BF Task created: "Build TUI demo mode" assigned to yoshi [p0]'
18167
+ ' \u23BF Task created: "Build TUI demo mode" assigned to nova [p0]'
17939
18168
  ],
17940
- "mari-exe2": [
18169
+ "lina-atlas2": [
17941
18170
  "",
17942
18171
  "\u23FA Starting carousel creation for CTO Breakdowns series...",
17943
18172
  "",
17944
- "\u23FA Read(exe/mari/create-cto-breakdowns-linkedin-carousel.md)",
18173
+ "\u23FA Read(exe/lina/create-cto-breakdowns-linkedin-carousel.md)",
17945
18174
  " \u23BF Task: Create a 10-slide LinkedIn carousel about the",
17946
18175
  " CTO Breakdowns podcast launch",
17947
18176
  "",
@@ -17955,11 +18184,11 @@ var init_demo_data = __esm({
17955
18184
  "\u23FA Write(exe/output/cto-breakdowns-carousel-v1.md)",
17956
18185
  " \u23BF 10 slides drafted with hooks, key points, CTAs"
17957
18186
  ],
17958
- "exe2": [
18187
+ "atlas2": [
17959
18188
  "",
17960
18189
  "\u23FA Planning the carousel pipeline automation...",
17961
18190
  "",
17962
- "\u23FA Current workflow: mari writes content \u2192 sasha produces visuals",
18191
+ "\u23FA Current workflow: lina writes content \u2192 pixel produces visuals",
17963
18192
  " \u2192 manual export. Need to automate the export step.",
17964
18193
  "",
17965
18194
  "\u23FA mcp__exe-mem__recall_my_memory",
@@ -17968,18 +18197,18 @@ var init_demo_data = __esm({
17968
18197
  "",
17969
18198
  "\u23FA Good \u2014 the pipeline exists. Just need to wire it."
17970
18199
  ],
17971
- "mari-exe3": [
18200
+ "lina-atlas3": [
17972
18201
  "",
17973
18202
  "\u23FA Reading carousel task brief...",
17974
18203
  "",
17975
- "\u23FA Read(exe/mari/create-cto-breakdowns-carousel.md)",
18204
+ "\u23FA Read(exe/lina/create-cto-breakdowns-carousel.md)",
17976
18205
  " \u23BF 10-slide carousel for CTO Breakdowns podcast launch",
17977
18206
  "",
17978
18207
  "\u23FA Applying Exe Foundry Bold: Epilogue headlines, #F5D76E gold CTAs",
17979
18208
  "",
17980
18209
  "\u23FA Writing slide hooks and copy..."
17981
18210
  ],
17982
- "exe3": [
18211
+ "atlas3": [
17983
18212
  "",
17984
18213
  "\u23FA Welcome to the exe-os demo!",
17985
18214
  "",
@@ -17998,13 +18227,13 @@ var init_demo_data = __esm({
17998
18227
  ]
17999
18228
  };
18000
18229
  DEMO_TASKS = [
18001
- { priority: "P0", title: "Build TUI demo mode with mock sessions", assignee: "yoshi", status: "in_progress", project: "exe-os" },
18002
- { priority: "P0", title: "Wire exe-os hooks into runtime HookPipeline", assignee: "yoshi", status: "done", project: "exe-os" },
18003
- { priority: "P1", title: "Create CTO Breakdowns LinkedIn carousel", assignee: "mari", status: "in_progress", project: "exe-build-skills" },
18004
- { priority: "P1", title: "Implement session scoping enforcement", assignee: "yoshi", status: "done", project: "exe-os" },
18005
- { priority: "P1", title: "Automate carousel export pipeline", assignee: "sasha", status: "open", project: "exe-build-skills" },
18006
- { priority: "P2", title: "Add Chutes provider to gateway failover", assignee: "tom", status: "open", project: "exe-os" },
18007
- { priority: "P2", title: "Port skill learning to exe-ai-employees", assignee: "yoshi", status: "open", project: "exe-os" }
18230
+ { priority: "P0", title: "Build TUI demo mode with mock sessions", assignee: "nova", status: "in_progress", project: "exe-os" },
18231
+ { priority: "P0", title: "Wire exe-os hooks into runtime HookPipeline", assignee: "nova", status: "done", project: "exe-os" },
18232
+ { priority: "P1", title: "Create CTO Breakdowns LinkedIn carousel", assignee: "lina", status: "in_progress", project: "exe-build-skills" },
18233
+ { priority: "P1", title: "Implement session scoping enforcement", assignee: "nova", status: "done", project: "exe-os" },
18234
+ { priority: "P1", title: "Automate carousel export pipeline", assignee: "pixel", status: "open", project: "exe-build-skills" },
18235
+ { priority: "P2", title: "Add Chutes provider to gateway failover", assignee: "devon", status: "open", project: "exe-os" },
18236
+ { priority: "P2", title: "Port skill learning to exe-ai-employees", assignee: "nova", status: "open", project: "exe-os" }
18008
18237
  ];
18009
18238
  DEMO_EXTERNAL_AGENTS = [
18010
18239
  { name: "exec-assistant", role: "executive assistant", status: "active" },
@@ -20533,6 +20762,14 @@ function CommandCenterView({
20533
20762
  setPermPrompt(req);
20534
20763
  });
20535
20764
  };
20765
+ let agentId = "default";
20766
+ if (agentRole === "CTO") {
20767
+ try {
20768
+ const { getEmployeeByRole: getEmployeeByRole2, loadEmployeesSync: loadEmployeesSync2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
20769
+ agentId = getEmployeeByRole2(loadEmployeesSync2(), "CTO")?.name ?? "default";
20770
+ } catch {
20771
+ }
20772
+ }
20536
20773
  setAgentConfig({
20537
20774
  provider,
20538
20775
  model,
@@ -20543,7 +20780,7 @@ function CommandCenterView({
20543
20780
  permissionHandler,
20544
20781
  maxTurns: 20,
20545
20782
  cwd: process.cwd(),
20546
- agentId: agentRole === "CTO" ? "yoshi" : "default"
20783
+ agentId
20547
20784
  });
20548
20785
  setChatInitError(null);
20549
20786
  } catch (e) {
@@ -20736,10 +20973,12 @@ function CommandCenterView({
20736
20973
  const registry = listSessions2();
20737
20974
  const tmuxSessions = inTmux2() ? new Set(listTmuxSessions2()) : /* @__PURE__ */ new Set();
20738
20975
  const roster = await loadEmployees2();
20739
- const employeeNames = roster.map((e) => e.name).filter((n) => n !== "exe");
20976
+ const { getCoordinatorName: getCoordinatorName2, isCoordinatorRole: isCoordinatorRole2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
20977
+ const coordinatorName = getCoordinatorName2(roster);
20978
+ const employeeNames = roster.filter((e) => !isCoordinatorRole2(e.role)).map((e) => e.name);
20740
20979
  const projectSessions = /* @__PURE__ */ new Map();
20741
20980
  for (const entry of registry) {
20742
- if (entry.agentId === "exe" && tmuxSessions.has(entry.windowName)) {
20981
+ if ((entry.agentId === coordinatorName || entry.agentId === "exe") && tmuxSessions.has(entry.windowName)) {
20743
20982
  const projName = entry.projectDir.split("/").filter(Boolean).pop() ?? "";
20744
20983
  if (projName) {
20745
20984
  projectSessions.set(projName, { exeSession: entry.windowName, projectDir: entry.projectDir });
@@ -21115,7 +21354,7 @@ function TmuxPane({ sessionName, employeeName, employeeRole, projectName, onDeta
21115
21354
  /* @__PURE__ */ jsx8(Text, { color: "#3D3660", children: "\u2500".repeat(process.stdout.columns ? process.stdout.columns - 30 : 120) }),
21116
21355
  /* @__PURE__ */ jsxs6(Box_default, { paddingX: 1, children: [
21117
21356
  /* @__PURE__ */ jsx8(Text, { color: "#6B4C9A", children: "\u276F " }),
21118
- /* @__PURE__ */ jsx8(Text, { children: inputBuffer || (demo ? "Type a message to exe..." : "Type to send commands...") })
21357
+ /* @__PURE__ */ jsx8(Text, { children: inputBuffer || (demo ? "Type a message to the coordinator..." : "Type to send commands...") })
21119
21358
  ] })
21120
21359
  ] });
21121
21360
  }
@@ -21156,7 +21395,7 @@ async function scoreEmployee(taskVector, agentId, searchFn) {
21156
21395
  return { agentId, score: results.length / 5 };
21157
21396
  }
21158
21397
  async function routeTask(taskDescription, employees, embedFn, searchFn, options) {
21159
- let specialists = employees.filter((e) => e.name !== "exe");
21398
+ let specialists = employees.filter((e) => !isCoordinatorRole(e.role));
21160
21399
  if (specialists.length === 0) {
21161
21400
  throw new Error(
21162
21401
  "No specialist employees available. Create one with /exe-new-employee."
@@ -21192,6 +21431,7 @@ var DEFAULT_BLOOM_CONFIG;
21192
21431
  var init_task_router = __esm({
21193
21432
  "src/lib/task-router.ts"() {
21194
21433
  "use strict";
21434
+ init_employees();
21195
21435
  DEFAULT_BLOOM_CONFIG = {
21196
21436
  complexityToTier: {
21197
21437
  routine: "junior",
@@ -21237,6 +21477,7 @@ var STALE_THRESHOLD_MS, MultiAgentOrchestrator;
21237
21477
  var init_orchestrator = __esm({
21238
21478
  "src/runtime/orchestrator.ts"() {
21239
21479
  "use strict";
21480
+ init_employees();
21240
21481
  init_task_router();
21241
21482
  init_tmux_routing();
21242
21483
  init_task_scope();
@@ -21281,7 +21522,7 @@ ${task.context}`,
21281
21522
  await createTaskCore({
21282
21523
  title: task.title,
21283
21524
  assignedTo: targetEmployee.name,
21284
- assignedBy: "exe",
21525
+ assignedBy: getCoordinatorName(),
21285
21526
  projectName: task.projectName,
21286
21527
  priority: task.priority,
21287
21528
  context: task.context,
@@ -21386,15 +21627,16 @@ ${task.context}`,
21386
21627
  const client = getClient2();
21387
21628
  const autoApproved = [];
21388
21629
  const needsReview = [];
21630
+ const coordinatorName = getCoordinatorName();
21389
21631
  const rScope = sessionScopeFilter();
21390
21632
  const reviews = await client.execute({
21391
21633
  sql: `SELECT id, title, assigned_to, priority, result, task_file FROM tasks
21392
- WHERE assigned_to = 'exe'
21634
+ WHERE (assigned_to = ? OR assigned_to = 'exe')
21393
21635
  AND status IN ('open', 'in_progress')
21394
21636
  AND task_file LIKE '%review-%'${rScope.sql}
21395
21637
  ORDER BY priority ASC
21396
21638
  LIMIT 20`,
21397
- args: [...rScope.args]
21639
+ args: [coordinatorName, ...rScope.args]
21398
21640
  });
21399
21641
  for (const row of reviews.rows) {
21400
21642
  const item = {
@@ -21509,22 +21751,26 @@ function useOrchestrator(enabled = true) {
21509
21751
  const [isLoading, setIsLoading] = useState8(true);
21510
21752
  const orchestratorRef = useRef5(null);
21511
21753
  const exeSessionRef = useRef5("exe1");
21754
+ const coordinatorNameRef = useRef5("exe");
21512
21755
  useEffect10(() => {
21513
21756
  if (!enabled) return;
21514
21757
  let cancelled = false;
21515
21758
  async function init() {
21516
21759
  try {
21517
21760
  const { MultiAgentOrchestrator: MultiAgentOrchestrator2 } = await Promise.resolve().then(() => (init_orchestrator(), orchestrator_exports));
21518
- const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
21761
+ const { getCoordinatorName: getCoordinatorName2, isCoordinatorRole: isCoordinatorRole2, loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
21762
+ const { isExeSession: isExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
21519
21763
  const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
21520
21764
  if (!inTmux2()) {
21521
21765
  setIsLoading(false);
21522
21766
  return;
21523
21767
  }
21524
21768
  const roster = await loadEmployees2();
21525
- const nonExe = roster.filter((e) => e.name !== "exe");
21769
+ const coordinatorName = getCoordinatorName2(roster);
21770
+ coordinatorNameRef.current = coordinatorName;
21771
+ const nonExe = roster.filter((e) => !isCoordinatorRole2(e.role));
21526
21772
  const tmuxSessions = listTmuxSessions2();
21527
- const exeSession = tmuxSessions.find((s) => /^exe\d+$/.test(s)) ?? "exe1";
21773
+ const exeSession = tmuxSessions.find((s) => isExeSession2(s)) ?? `${coordinatorName}1`;
21528
21774
  exeSessionRef.current = exeSession;
21529
21775
  orchestratorRef.current = new MultiAgentOrchestrator2({
21530
21776
  employees: nonExe,
@@ -21557,8 +21803,8 @@ function useOrchestrator(enabled = true) {
21557
21803
  const uoScope = sessionScopeFilter2();
21558
21804
  const result = await client.execute({
21559
21805
  sql: `SELECT COUNT(*) as cnt FROM tasks
21560
- WHERE assigned_to = 'exe' AND status IN ('open', 'in_progress')${uoScope.sql}`,
21561
- args: [...uoScope.args]
21806
+ WHERE (assigned_to = ? OR assigned_to = 'exe') AND status IN ('open', 'in_progress')${uoScope.sql}`,
21807
+ args: [coordinatorNameRef.current, ...uoScope.args]
21562
21808
  });
21563
21809
  if (!cancelled) setPendingReviews(Number(result.rows[0]?.cnt ?? 0));
21564
21810
  } catch {
@@ -21586,10 +21832,11 @@ function useOrchestrator(enabled = true) {
21586
21832
  try {
21587
21833
  const { createTaskCore: createTaskCore2 } = await Promise.resolve().then(() => (init_tasks_crud(), tasks_crud_exports));
21588
21834
  const { ensureEmployee: ensureEmployee2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
21835
+ const { getCoordinatorName: getCoordinatorName2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
21589
21836
  await createTaskCore2({
21590
21837
  title: `Session launched for ${agentId} (TUI)`,
21591
21838
  assignedTo: agentId,
21592
- assignedBy: "exe",
21839
+ assignedBy: getCoordinatorName2(),
21593
21840
  projectName: "exe-os",
21594
21841
  priority: "p2",
21595
21842
  context: "Session spawned from TUI Sessions view. Agent will pick up any queued tasks via intercom.",
@@ -21649,6 +21896,9 @@ import React19, { useState as useState9, useEffect as useEffect11, useCallback a
21649
21896
  import path34 from "path";
21650
21897
  import { homedir as homedir6 } from "os";
21651
21898
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
21899
+ function isCoordinatorEntry(entry) {
21900
+ return entry.role.toLowerCase() === "coo" || entry.name === "exe";
21901
+ }
21652
21902
  function SessionsView({
21653
21903
  initialProject,
21654
21904
  onConsumeInitialProject,
@@ -21694,13 +21944,13 @@ function SessionsView({
21694
21944
  const proj = projects.find((p) => p.projectName === projectName);
21695
21945
  if (!proj) return;
21696
21946
  const active = proj.employees.filter(
21697
- (e) => e.status === "active" || demo && e.name === "exe"
21947
+ (e) => e.status === "active" || demo && isCoordinatorEntry(e)
21698
21948
  );
21699
21949
  const carousel = demo ? proj.employees.filter((e) => e.status !== "offline") : active;
21700
21950
  if (carousel.length === 0) return;
21701
21951
  const sorted = [
21702
- ...carousel.filter((e) => e.name === "exe"),
21703
- ...carousel.filter((e) => e.name !== "exe")
21952
+ ...carousel.filter((e) => isCoordinatorEntry(e)),
21953
+ ...carousel.filter((e) => !isCoordinatorEntry(e))
21704
21954
  ];
21705
21955
  setCarouselEmployees(sorted);
21706
21956
  setCarouselIdx(0);
@@ -21726,7 +21976,7 @@ function SessionsView({
21726
21976
  }
21727
21977
  async function launchSession(entry, projectName, projectDir) {
21728
21978
  try {
21729
- if (entry.name !== "exe" && !demo) {
21979
+ if (!isCoordinatorEntry(entry) && !demo) {
21730
21980
  const result = await orch.spawnSession(entry.name);
21731
21981
  if (!result || result.status === "failed") {
21732
21982
  process.stderr.write(
@@ -21842,7 +22092,8 @@ function SessionsView({
21842
22092
  try {
21843
22093
  const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
21844
22094
  const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2, capturePaneLines: capturePaneLines2, parseActivity: parseActivity2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
21845
- const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
22095
+ const { getCoordinatorName: getCoordinatorName2, isCoordinatorRole: isCoordinatorRole2, loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
22096
+ const { isExeSession: isExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
21846
22097
  const { execSync: execSync14 } = await import("child_process");
21847
22098
  if (!inTmux2()) {
21848
22099
  setTmuxAvailable(false);
@@ -21867,14 +22118,15 @@ function SessionsView({
21867
22118
  const registry = listSessions2();
21868
22119
  const tmuxSessions = new Set(listTmuxSessions2());
21869
22120
  const roster = await loadEmployees2();
22121
+ const coordinatorName = getCoordinatorName2(roster);
21870
22122
  const exeSessions = /* @__PURE__ */ new Map();
21871
22123
  for (const entry of registry) {
21872
- if (entry.agentId === "exe" && tmuxSessions.has(entry.windowName)) {
22124
+ if ((entry.agentId === coordinatorName || entry.agentId === "exe") && tmuxSessions.has(entry.windowName)) {
21873
22125
  exeSessions.set(entry.windowName, entry.projectDir);
21874
22126
  }
21875
22127
  }
21876
22128
  for (const s of tmuxSessions) {
21877
- if (/^exe\d+$/.test(s) && !exeSessions.has(s)) {
22129
+ if (isExeSession2(s) && !exeSessions.has(s)) {
21878
22130
  exeSessions.set(s, "");
21879
22131
  }
21880
22132
  }
@@ -21889,7 +22141,7 @@ function SessionsView({
21889
22141
  exeStatus = statusFromPaneLines(exeLines);
21890
22142
  exeActivity = exeLines.length > 0 ? parseActivity2(exeLines) : "";
21891
22143
  }
21892
- const employeeEntries = roster.filter((e) => e.name !== "exe").map((emp) => {
22144
+ const employeeEntries = roster.filter((e) => !isCoordinatorRole2(e.role)).map((emp) => {
21893
22145
  const sessionName = `${emp.name}-${exeSession}`;
21894
22146
  const hasSession = tmuxSessions.has(sessionName);
21895
22147
  const isCrashed = !hasSession && orch.crashedSessions.includes(emp.name);
@@ -21925,7 +22177,7 @@ function SessionsView({
21925
22177
  });
21926
22178
  const employees = [
21927
22179
  {
21928
- name: "exe",
22180
+ name: coordinatorName,
21929
22181
  role: "COO",
21930
22182
  status: exeStatus,
21931
22183
  sessionName: exeSession,
@@ -22164,7 +22416,7 @@ function TasksView({ onBack }) {
22164
22416
  setAllTasks(DEMO_TASKS.map((t, i) => ({
22165
22417
  id: `demo-${i}`,
22166
22418
  ...t,
22167
- assignedBy: "exe",
22419
+ assignedBy: "coordinator",
22168
22420
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
22169
22421
  result: t.status === "done" ? "Completed successfully" : ""
22170
22422
  })));
@@ -23482,7 +23734,7 @@ function TeamView({ onBack, onViewSessions }) {
23482
23734
  showAddHint && /* @__PURE__ */ jsx12(Text, { color: "#F5D76E", children: "Run /exe-new-employee <name> from CLI to add an employee." }),
23483
23735
  !demo && orch.pendingReviews > 0 && /* @__PURE__ */ jsxs10(Text, { color: "#6B4C9A", children: [
23484
23736
  orch.pendingReviews,
23485
- " review(s) pending exe attention"
23737
+ " review(s) pending coordinator attention"
23486
23738
  ] }),
23487
23739
  /* @__PURE__ */ jsx12(Text, { children: " " }),
23488
23740
  /* @__PURE__ */ jsx12(Text, { bold: true, children: "INTERNAL" }),
@@ -23735,11 +23987,11 @@ import TextInput2 from "ink-text-input";
23735
23987
  import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
23736
23988
  function agentColor(agentId) {
23737
23989
  switch (agentId.toLowerCase()) {
23738
- case "exe":
23990
+ case "atlas":
23739
23991
  return "#F5D76E";
23740
- case "yoshi":
23992
+ case "nova":
23741
23993
  return "#3B82F6";
23742
- case "mari":
23994
+ case "lina":
23743
23995
  return "#6B4C9A";
23744
23996
  default:
23745
23997
  return "#F0EDE8";
@@ -24190,11 +24442,11 @@ var init_Wiki = __esm({
24190
24442
  PANELS = ["Workspaces", "Documents", "Chat"];
24191
24443
  MAX_VISIBLE_MESSAGES = 12;
24192
24444
  DEMO_SEARCH_RESULTS = [
24193
- { id: "1", agentId: "yoshi", rawText: "Reviewed PR #42 \u2014 approved with minor comment on error handling pattern", timestamp: new Date(Date.now() - 2 * 36e5).toISOString(), projectName: "exe-os" },
24194
- { id: "2", agentId: "tom", rawText: "Implemented session routing with deterministic naming: agent-exeN convention", timestamp: new Date(Date.now() - 5 * 36e5).toISOString(), projectName: "exe-os" },
24195
- { id: "3", agentId: "exe", rawText: "Status brief: 3 tasks completed, 1 blocked on wiki integration", timestamp: new Date(Date.now() - 8 * 36e5).toISOString(), projectName: "exe-os" },
24196
- { id: "4", agentId: "mari", rawText: "Created brand guidelines document \u2014 Exe Foundry Bold design system", timestamp: new Date(Date.now() - 24 * 36e5).toISOString(), projectName: "exe-os" },
24197
- { id: "5", agentId: "tom", rawText: "Fixed CommandCenter project filtering \u2014 DB-first, no random directories", timestamp: new Date(Date.now() - 48 * 36e5).toISOString(), projectName: "exe-os" }
24445
+ { id: "1", agentId: "nova", rawText: "Reviewed PR #42 \u2014 approved with minor comment on error handling pattern", timestamp: new Date(Date.now() - 2 * 36e5).toISOString(), projectName: "exe-os" },
24446
+ { id: "2", agentId: "devon", rawText: "Implemented session routing with deterministic naming: employee-coordinator convention", timestamp: new Date(Date.now() - 5 * 36e5).toISOString(), projectName: "exe-os" },
24447
+ { id: "3", agentId: "atlas", rawText: "Status brief: 3 tasks completed, 1 blocked on wiki integration", timestamp: new Date(Date.now() - 8 * 36e5).toISOString(), projectName: "exe-os" },
24448
+ { id: "4", agentId: "lina", rawText: "Created brand guidelines document \u2014 Exe Foundry Bold design system", timestamp: new Date(Date.now() - 24 * 36e5).toISOString(), projectName: "exe-os" },
24449
+ { id: "5", agentId: "devon", rawText: "Fixed CommandCenter project filtering \u2014 DB-first, no random directories", timestamp: new Date(Date.now() - 48 * 36e5).toISOString(), projectName: "exe-os" }
24198
24450
  ];
24199
24451
  }
24200
24452
  });
@@ -24719,7 +24971,7 @@ function App2() {
24719
24971
  /* @__PURE__ */ jsx17(Footer, {})
24720
24972
  ] }) }) }) });
24721
24973
  }
24722
- var TAB_SHORTCUTS, isDemo;
24974
+ var TAB_SHORTCUTS, isDemo, instance;
24723
24975
  var init_App2 = __esm({
24724
24976
  async "src/tui/App.tsx"() {
24725
24977
  "use strict";
@@ -24844,7 +25096,9 @@ var init_App2 = __esm({
24844
25096
  stdin.unref = () => stdin;
24845
25097
  }
24846
25098
  }
24847
- render_default(/* @__PURE__ */ jsx17(App2, {}));
25099
+ instance = render_default(/* @__PURE__ */ jsx17(App2, {}));
25100
+ instance.waitUntilExit().catch(() => {
25101
+ });
24848
25102
  {
24849
25103
  const CLEANUP_SEQ = "\x1B[?1006l\x1B[?1002l\x1B[?1000l\x1B[?25h\x1B[?1049l";
24850
25104
  const terminalCleanup = () => {