@askexenow/exe-os 0.9.16 → 0.9.18

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 (97) hide show
  1. package/dist/bin/backfill-conversations.js +1242 -909
  2. package/dist/bin/backfill-responses.js +1245 -912
  3. package/dist/bin/backfill-vectors.js +1244 -906
  4. package/dist/bin/cleanup-stale-review-tasks.js +1870 -417
  5. package/dist/bin/cli.js +217 -107
  6. package/dist/bin/exe-agent-config.js +2 -2
  7. package/dist/bin/exe-agent.js +62 -0
  8. package/dist/bin/exe-assign.js +346 -10
  9. package/dist/bin/exe-boot.js +387 -32
  10. package/dist/bin/exe-call.js +72 -2
  11. package/dist/bin/exe-cloud.js +8 -0
  12. package/dist/bin/exe-dispatch.js +1821 -225
  13. package/dist/bin/exe-doctor.js +720 -52
  14. package/dist/bin/exe-export-behaviors.js +1429 -148
  15. package/dist/bin/exe-forget.js +1408 -34
  16. package/dist/bin/exe-gateway.js +1629 -1295
  17. package/dist/bin/exe-heartbeat.js +1899 -448
  18. package/dist/bin/exe-kill.js +1624 -346
  19. package/dist/bin/exe-launch-agent.js +726 -90
  20. package/dist/bin/exe-link.js +27 -8
  21. package/dist/bin/exe-new-employee.js +75 -9
  22. package/dist/bin/exe-pending-messages.js +2769 -1316
  23. package/dist/bin/exe-pending-notifications.js +2829 -1376
  24. package/dist/bin/exe-pending-reviews.js +2847 -1392
  25. package/dist/bin/exe-rename.js +89 -8
  26. package/dist/bin/exe-review.js +1494 -312
  27. package/dist/bin/exe-search.js +1608 -1300
  28. package/dist/bin/exe-session-cleanup.js +194 -91
  29. package/dist/bin/exe-settings.js +10 -2
  30. package/dist/bin/exe-start-codex.js +769 -120
  31. package/dist/bin/exe-start-opencode.js +763 -108
  32. package/dist/bin/exe-status.js +1887 -434
  33. package/dist/bin/exe-team.js +1782 -324
  34. package/dist/bin/git-sweep.js +408 -33
  35. package/dist/bin/graph-backfill.js +681 -27
  36. package/dist/bin/graph-export.js +1419 -141
  37. package/dist/bin/install.js +4 -8
  38. package/dist/bin/intercom-check.js +8641 -0
  39. package/dist/bin/scan-tasks.js +553 -38
  40. package/dist/bin/setup.js +82 -10
  41. package/dist/bin/shard-migrate.js +682 -28
  42. package/dist/gateway/index.js +1629 -1295
  43. package/dist/hooks/bug-report-worker.js +1136 -183
  44. package/dist/hooks/codex-stop-task-finalizer.js +1060 -107
  45. package/dist/hooks/commit-complete.js +408 -33
  46. package/dist/hooks/error-recall.js +1608 -1300
  47. package/dist/hooks/ingest-worker.js +250 -7966
  48. package/dist/hooks/ingest.js +707 -119
  49. package/dist/hooks/instructions-loaded.js +383 -20
  50. package/dist/hooks/notification.js +383 -20
  51. package/dist/hooks/post-compact.js +384 -21
  52. package/dist/hooks/{response-ingest-worker.js → post-tool-combined.js} +4157 -1716
  53. package/dist/hooks/pre-compact.js +486 -45
  54. package/dist/hooks/pre-tool-use.js +385 -22
  55. package/dist/hooks/prompt-submit.js +291 -96
  56. package/dist/hooks/session-end.js +490 -48
  57. package/dist/hooks/session-start.js +236 -22
  58. package/dist/hooks/stop.js +192 -50
  59. package/dist/hooks/subagent-stop.js +95 -18
  60. package/dist/hooks/summary-worker.js +205 -361
  61. package/dist/index.js +221 -105
  62. package/dist/lib/agent-config.js +2 -2
  63. package/dist/lib/cloud-sync.js +27 -8
  64. package/dist/lib/consolidation.js +437 -41
  65. package/dist/lib/database.js +20 -8
  66. package/dist/lib/db-daemon-client.js +2 -2
  67. package/dist/lib/db.js +20 -8
  68. package/dist/lib/device-registry.js +27 -8
  69. package/dist/lib/employee-templates.js +62 -0
  70. package/dist/lib/employees.js +2 -2
  71. package/dist/lib/exe-daemon.js +6703 -6259
  72. package/dist/lib/hybrid-search.js +1608 -1300
  73. package/dist/lib/identity.js +1 -1
  74. package/dist/lib/messaging.js +11 -3
  75. package/dist/lib/reminders.js +1 -1
  76. package/dist/lib/schedules.js +718 -26
  77. package/dist/lib/session-registry.js +11 -0
  78. package/dist/lib/skill-learning.js +639 -9
  79. package/dist/lib/store.js +676 -27
  80. package/dist/lib/tasks.js +665 -27
  81. package/dist/lib/tmux-routing.js +732 -94
  82. package/dist/lib/token-spend.js +1 -1
  83. package/dist/mcp/server.js +856 -383
  84. package/dist/mcp/tools/complete-reminder.js +1 -1
  85. package/dist/mcp/tools/create-reminder.js +1 -1
  86. package/dist/mcp/tools/create-task.js +616 -46
  87. package/dist/mcp/tools/deactivate-behavior.js +9 -1
  88. package/dist/mcp/tools/list-reminders.js +1 -1
  89. package/dist/mcp/tools/list-tasks.js +10 -2
  90. package/dist/mcp/tools/send-message.js +11 -3
  91. package/dist/mcp/tools/update-task.js +677 -39
  92. package/dist/runtime/index.js +425 -37
  93. package/dist/tui/App.js +365 -34
  94. package/package.json +5 -2
  95. package/src/commands/exe/intercom.md +6 -17
  96. package/dist/bin/wiki-sync.js +0 -2991
  97. package/dist/hooks/prompt-ingest-worker.js +0 -3979
@@ -1,7 +1,19 @@
1
+ var __defProp = Object.defineProperty;
1
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
2
3
  var __esm = (fn, res) => function __init() {
3
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
4
5
  };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // src/types/memory.ts
12
+ var init_memory = __esm({
13
+ "src/types/memory.ts"() {
14
+ "use strict";
15
+ }
16
+ });
5
17
 
6
18
  // src/lib/db-retry.ts
7
19
  var init_db_retry = __esm({
@@ -13,9 +25,25 @@ var init_db_retry = __esm({
13
25
  // src/lib/secure-files.ts
14
26
  import { chmodSync, existsSync, mkdirSync } from "fs";
15
27
  import { chmod, mkdir } from "fs/promises";
28
+ function ensurePrivateDirSync(dirPath) {
29
+ mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
30
+ try {
31
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
32
+ } catch {
33
+ }
34
+ }
35
+ function enforcePrivateFileSync(filePath) {
36
+ try {
37
+ if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
38
+ } catch {
39
+ }
40
+ }
41
+ var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
16
42
  var init_secure_files = __esm({
17
43
  "src/lib/secure-files.ts"() {
18
44
  "use strict";
45
+ PRIVATE_DIR_MODE = 448;
46
+ PRIVATE_FILE_MODE = 384;
19
47
  }
20
48
  });
21
49
 
@@ -111,11 +139,153 @@ var init_config = __esm({
111
139
  }
112
140
  });
113
141
 
142
+ // src/lib/runtime-table.ts
143
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
144
+ var init_runtime_table = __esm({
145
+ "src/lib/runtime-table.ts"() {
146
+ "use strict";
147
+ RUNTIME_TABLE = {
148
+ codex: {
149
+ binary: "codex",
150
+ launchMode: "interactive",
151
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
152
+ inlineFlag: "--no-alt-screen",
153
+ apiKeyEnv: "OPENAI_API_KEY",
154
+ defaultModel: "gpt-5.4"
155
+ },
156
+ opencode: {
157
+ binary: "opencode",
158
+ launchMode: "exec",
159
+ autoApproveFlag: "--dangerously-skip-permissions",
160
+ inlineFlag: "",
161
+ apiKeyEnv: "ANTHROPIC_API_KEY",
162
+ defaultModel: "anthropic/claude-sonnet-4-6"
163
+ }
164
+ };
165
+ DEFAULT_RUNTIME = "claude";
166
+ }
167
+ });
168
+
169
+ // src/lib/agent-config.ts
170
+ var agent_config_exports = {};
171
+ __export(agent_config_exports, {
172
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
173
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
174
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
175
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
176
+ clearAgentRuntime: () => clearAgentRuntime,
177
+ getAgentRuntime: () => getAgentRuntime,
178
+ loadAgentConfig: () => loadAgentConfig,
179
+ saveAgentConfig: () => saveAgentConfig,
180
+ setAgentRuntime: () => setAgentRuntime
181
+ });
182
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
183
+ import path2 from "path";
184
+ function loadAgentConfig() {
185
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
186
+ try {
187
+ return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
188
+ } catch {
189
+ return {};
190
+ }
191
+ }
192
+ function saveAgentConfig(config) {
193
+ const dir = path2.dirname(AGENT_CONFIG_PATH);
194
+ ensurePrivateDirSync(dir);
195
+ writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
196
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
197
+ }
198
+ function getAgentRuntime(agentId) {
199
+ const config = loadAgentConfig();
200
+ const entry = config[agentId];
201
+ if (entry) return entry;
202
+ const orgDefault = config["default"];
203
+ if (orgDefault) return orgDefault;
204
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
205
+ }
206
+ function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
207
+ const knownModels = KNOWN_RUNTIMES[runtime];
208
+ if (!knownModels) {
209
+ return {
210
+ ok: false,
211
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
212
+ };
213
+ }
214
+ if (!knownModels.includes(model)) {
215
+ return {
216
+ ok: false,
217
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
218
+ };
219
+ }
220
+ const config = loadAgentConfig();
221
+ const entry = { runtime, model };
222
+ if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
223
+ config[agentId] = entry;
224
+ saveAgentConfig(config);
225
+ return { ok: true };
226
+ }
227
+ function clearAgentRuntime(agentId) {
228
+ const config = loadAgentConfig();
229
+ delete config[agentId];
230
+ saveAgentConfig(config);
231
+ }
232
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
233
+ var init_agent_config = __esm({
234
+ "src/lib/agent-config.ts"() {
235
+ "use strict";
236
+ init_config();
237
+ init_runtime_table();
238
+ init_secure_files();
239
+ AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
240
+ KNOWN_RUNTIMES = {
241
+ claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
242
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
243
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
244
+ };
245
+ RUNTIME_LABELS = {
246
+ claude: "Claude Code (Anthropic)",
247
+ codex: "Codex (OpenAI)",
248
+ opencode: "OpenCode (open source)"
249
+ };
250
+ DEFAULT_MODELS = {
251
+ claude: "claude-opus-4.6",
252
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
253
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
254
+ };
255
+ }
256
+ });
257
+
114
258
  // src/lib/employees.ts
259
+ var employees_exports = {};
260
+ __export(employees_exports, {
261
+ COORDINATOR_ROLE: () => COORDINATOR_ROLE,
262
+ DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
263
+ EMPLOYEES_PATH: () => EMPLOYEES_PATH,
264
+ addEmployee: () => addEmployee,
265
+ baseAgentName: () => baseAgentName,
266
+ canCoordinate: () => canCoordinate,
267
+ getCoordinatorEmployee: () => getCoordinatorEmployee,
268
+ getCoordinatorName: () => getCoordinatorName,
269
+ getEmployee: () => getEmployee,
270
+ getEmployeeByRole: () => getEmployeeByRole,
271
+ getEmployeeNamesByRole: () => getEmployeeNamesByRole,
272
+ hasRole: () => hasRole,
273
+ hireEmployee: () => hireEmployee,
274
+ isCoordinatorName: () => isCoordinatorName,
275
+ isCoordinatorRole: () => isCoordinatorRole,
276
+ isMultiInstance: () => isMultiInstance,
277
+ loadEmployees: () => loadEmployees,
278
+ loadEmployeesSync: () => loadEmployeesSync,
279
+ normalizeRole: () => normalizeRole,
280
+ normalizeRosterCase: () => normalizeRosterCase,
281
+ registerBinSymlinks: () => registerBinSymlinks,
282
+ saveEmployees: () => saveEmployees,
283
+ validateEmployeeName: () => validateEmployeeName
284
+ });
115
285
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
116
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
286
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
117
287
  import { execSync } from "child_process";
118
- import path2 from "path";
288
+ import path3 from "path";
119
289
  import os2 from "os";
120
290
  function normalizeRole(role) {
121
291
  return (role ?? "").trim().toLowerCase();
@@ -133,29 +303,217 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
133
303
  if (!agentName) return false;
134
304
  return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
135
305
  }
306
+ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
307
+ return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
308
+ }
309
+ function validateEmployeeName(name) {
310
+ if (!name) {
311
+ return { valid: false, error: "Name is required" };
312
+ }
313
+ if (name.length > 32) {
314
+ return { valid: false, error: "Name must be 32 characters or fewer" };
315
+ }
316
+ if (!/^[a-z][a-z0-9]*$/.test(name)) {
317
+ return {
318
+ valid: false,
319
+ error: "Name must start with a letter and contain only lowercase alphanumeric characters"
320
+ };
321
+ }
322
+ return { valid: true };
323
+ }
324
+ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
325
+ if (!existsSync4(employeesPath)) {
326
+ return [];
327
+ }
328
+ const raw = await readFile2(employeesPath, "utf-8");
329
+ try {
330
+ return JSON.parse(raw);
331
+ } catch {
332
+ return [];
333
+ }
334
+ }
335
+ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
336
+ await mkdir2(path3.dirname(employeesPath), { recursive: true });
337
+ await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
338
+ }
136
339
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
137
- if (!existsSync3(employeesPath)) return [];
340
+ if (!existsSync4(employeesPath)) return [];
138
341
  try {
139
- return JSON.parse(readFileSync2(employeesPath, "utf-8"));
342
+ return JSON.parse(readFileSync3(employeesPath, "utf-8"));
140
343
  } catch {
141
344
  return [];
142
345
  }
143
346
  }
144
- var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, IDENTITY_DIR;
347
+ function getEmployee(employees, name) {
348
+ return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
349
+ }
350
+ function getEmployeeByRole(employees, role) {
351
+ const lower = role.toLowerCase();
352
+ return employees.find((e) => e.role.toLowerCase() === lower);
353
+ }
354
+ function getEmployeeNamesByRole(employees, role) {
355
+ const lower = role.toLowerCase();
356
+ return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
357
+ }
358
+ function hasRole(agentName, role) {
359
+ const employees = loadEmployeesSync();
360
+ const emp = getEmployee(employees, agentName);
361
+ return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
362
+ }
363
+ function baseAgentName(name, employees) {
364
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
365
+ if (!match) return name;
366
+ const base = match[1];
367
+ const roster = employees ?? loadEmployeesSync();
368
+ if (getEmployee(roster, base)) return base;
369
+ return name;
370
+ }
371
+ function isMultiInstance(agentName, employees) {
372
+ const roster = employees ?? loadEmployeesSync();
373
+ const emp = getEmployee(roster, agentName);
374
+ if (!emp) return false;
375
+ return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
376
+ }
377
+ function addEmployee(employees, employee) {
378
+ const normalized = { ...employee, name: employee.name.toLowerCase() };
379
+ if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
380
+ throw new Error(`Employee '${normalized.name}' already exists`);
381
+ }
382
+ return [...employees, normalized];
383
+ }
384
+ function appendToCoordinatorTeam(employee) {
385
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
386
+ if (!coordinator) return;
387
+ const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
388
+ if (!existsSync4(idPath)) return;
389
+ const content = readFileSync3(idPath, "utf-8");
390
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
391
+ const teamMatch = content.match(TEAM_SECTION_RE);
392
+ if (!teamMatch || teamMatch.index === void 0) return;
393
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
394
+ const nextHeading = afterTeam.match(/\n## /);
395
+ const entry = `
396
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
397
+ `;
398
+ let updated;
399
+ if (nextHeading && nextHeading.index !== void 0) {
400
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
401
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
402
+ } else {
403
+ updated = content.trimEnd() + "\n" + entry;
404
+ }
405
+ writeFileSync2(idPath, updated, "utf-8");
406
+ }
407
+ function capitalize(s) {
408
+ return s.charAt(0).toUpperCase() + s.slice(1);
409
+ }
410
+ async function hireEmployee(employee) {
411
+ const employees = await loadEmployees();
412
+ const updated = addEmployee(employees, employee);
413
+ await saveEmployees(updated);
414
+ try {
415
+ appendToCoordinatorTeam(employee);
416
+ } catch {
417
+ }
418
+ try {
419
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
420
+ const config = loadAgentConfig2();
421
+ const name = employee.name.toLowerCase();
422
+ if (!config[name] && config["default"]) {
423
+ config[name] = { ...config["default"] };
424
+ saveAgentConfig2(config);
425
+ }
426
+ } catch {
427
+ }
428
+ return updated;
429
+ }
430
+ async function normalizeRosterCase(rosterPath) {
431
+ const employees = await loadEmployees(rosterPath);
432
+ let changed = false;
433
+ for (const emp of employees) {
434
+ if (emp.name !== emp.name.toLowerCase()) {
435
+ const oldName = emp.name;
436
+ emp.name = emp.name.toLowerCase();
437
+ changed = true;
438
+ try {
439
+ const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
440
+ const oldPath = path3.join(identityDir, `${oldName}.md`);
441
+ const newPath = path3.join(identityDir, `${emp.name}.md`);
442
+ if (existsSync4(oldPath) && !existsSync4(newPath)) {
443
+ renameSync2(oldPath, newPath);
444
+ } else if (existsSync4(oldPath) && oldPath !== newPath) {
445
+ const content = readFileSync3(oldPath, "utf-8");
446
+ writeFileSync2(newPath, content, "utf-8");
447
+ if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
448
+ unlinkSync(oldPath);
449
+ }
450
+ }
451
+ } catch {
452
+ }
453
+ }
454
+ }
455
+ if (changed) {
456
+ await saveEmployees(employees, rosterPath);
457
+ }
458
+ return changed;
459
+ }
460
+ function findExeBin() {
461
+ try {
462
+ return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
463
+ } catch {
464
+ return null;
465
+ }
466
+ }
467
+ function registerBinSymlinks(name) {
468
+ const created = [];
469
+ const skipped = [];
470
+ const errors = [];
471
+ const exeBinPath = findExeBin();
472
+ if (!exeBinPath) {
473
+ errors.push("Could not find 'exe-os' in PATH");
474
+ return { created, skipped, errors };
475
+ }
476
+ const binDir = path3.dirname(exeBinPath);
477
+ let target;
478
+ try {
479
+ target = readlinkSync(exeBinPath);
480
+ } catch {
481
+ errors.push("Could not read 'exe' symlink");
482
+ return { created, skipped, errors };
483
+ }
484
+ for (const suffix of ["", "-opencode"]) {
485
+ const linkName = `${name}${suffix}`;
486
+ const linkPath = path3.join(binDir, linkName);
487
+ if (existsSync4(linkPath)) {
488
+ skipped.push(linkName);
489
+ continue;
490
+ }
491
+ try {
492
+ symlinkSync(target, linkPath);
493
+ created.push(linkName);
494
+ } catch (err) {
495
+ errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
496
+ }
497
+ }
498
+ return { created, skipped, errors };
499
+ }
500
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
145
501
  var init_employees = __esm({
146
502
  "src/lib/employees.ts"() {
147
503
  "use strict";
148
504
  init_config();
149
- EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
505
+ EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
150
506
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
151
507
  COORDINATOR_ROLE = "COO";
152
- IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
508
+ MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
509
+ IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
510
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
153
511
  }
154
512
  });
155
513
 
156
514
  // src/lib/database-adapter.ts
157
515
  import os3 from "os";
158
- import path3 from "path";
516
+ import path4 from "path";
159
517
  import { createRequire } from "module";
160
518
  import { pathToFileURL } from "url";
161
519
  var BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES;
@@ -182,6 +540,7 @@ var init_database = __esm({
182
540
  init_db_retry();
183
541
  init_employees();
184
542
  init_database_adapter();
543
+ init_memory();
185
544
  }
186
545
  });
187
546
 
@@ -189,13 +548,14 @@ var init_database = __esm({
189
548
  import { randomUUID } from "crypto";
190
549
 
191
550
  // src/lib/store.ts
192
- import { createHash } from "crypto";
551
+ init_memory();
193
552
  init_database();
553
+ import { createHash } from "crypto";
194
554
 
195
555
  // src/lib/keychain.ts
196
556
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
197
- import { existsSync as existsSync4 } from "fs";
198
- import path4 from "path";
557
+ import { existsSync as existsSync5 } from "fs";
558
+ import path5 from "path";
199
559
  import os4 from "os";
200
560
 
201
561
  // src/lib/store.ts
@@ -263,7 +623,6 @@ async function selectUnconsolidated(client, limit = 200) {
263
623
  sql: `SELECT id, agent_id, project_name, tool_name, raw_text, timestamp
264
624
  FROM memories
265
625
  WHERE consolidated = 0
266
- AND timestamp >= datetime('now', '-7 days')
267
626
  ORDER BY timestamp DESC
268
627
  LIMIT ?`,
269
628
  args: [limit]
@@ -318,11 +677,42 @@ function groupMemories(memories) {
318
677
  }
319
678
  return clusters;
320
679
  }
321
- function buildConsolidationPrompt(cluster) {
680
+ var ROLE_PROMPTS = {
681
+ "COO": `Extract EXACTLY THREE types of insights:
682
+ 1. KEY DECISIONS \u2014 choices that were made and why (at most 3)
683
+ 2. RECURRING PATTERNS \u2014 actions or approaches that repeated (at most 3)
684
+ 3. OPEN QUESTIONS \u2014 things that seem unresolved or risky (at most 2)`,
685
+ "CTO": `Extract EXACTLY THREE types of insights:
686
+ 1. ARCHITECTURE DECISIONS \u2014 design choices, tradeoffs, delegation patterns (at most 3)
687
+ 2. REVIEW OUTCOMES \u2014 what was approved, rejected, or flagged during code review (at most 3)
688
+ 3. TECHNICAL DEBT \u2014 unresolved issues, deferred work, risks identified (at most 2)`,
689
+ "Principal Engineer": `Extract EXACTLY THREE types of insights:
690
+ 1. CODE DECISIONS \u2014 implementation choices, patterns used, bugs fixed (at most 3)
691
+ 2. FILE CHANGES \u2014 key files modified and why (at most 3)
692
+ 3. BLOCKERS \u2014 things that slowed down or prevented progress (at most 2)`,
693
+ "Staff Code Reviewer": `Extract EXACTLY THREE types of insights:
694
+ 1. AUDIT FINDINGS \u2014 issues found, severity, fix status (at most 3)
695
+ 2. RECURRING ISSUES \u2014 patterns that keep appearing across reviews (at most 3)
696
+ 3. QUALITY SIGNALS \u2014 areas of the codebase that are strong or weak (at most 2)`,
697
+ "CMO": `Extract EXACTLY THREE types of insights:
698
+ 1. DESIGN DECISIONS \u2014 brand, layout, typography, or content choices made (at most 3)
699
+ 2. CONTENT STRATEGY \u2014 messaging, SEO, platform-specific decisions (at most 3)
700
+ 3. BRAND ALIGNMENT \u2014 consistency issues or guidelines established (at most 2)`,
701
+ "AI Product Lead": `Extract EXACTLY THREE types of insights:
702
+ 1. RESEARCH FINDINGS \u2014 tools, repos, or models evaluated with conclusions (at most 3)
703
+ 2. COMPETITIVE GAPS \u2014 features or patterns worth adopting from competitors (at most 3)
704
+ 3. BUILD RECOMMENDATIONS \u2014 what to build, skip, or watch (at most 2)`
705
+ };
706
+ var DEFAULT_ROLE_PROMPT = `Extract EXACTLY THREE types of insights:
707
+ 1. KEY DECISIONS \u2014 choices that were made and why (at most 3)
708
+ 2. RECURRING PATTERNS \u2014 actions or approaches that repeated (at most 3)
709
+ 3. OPEN QUESTIONS \u2014 things that seem unresolved or risky (at most 2)`;
710
+ function buildConsolidationPrompt(cluster, agentRole) {
322
711
  const snippets = cluster.memories.map((m, i) => {
323
712
  const text = m.raw_text.length > 200 ? m.raw_text.slice(0, 200) + "..." : m.raw_text;
324
713
  return `${i + 1}. [${m.tool_name}] ${text}`;
325
714
  }).join("\n");
715
+ const rolePrompt = agentRole && ROLE_PROMPTS[agentRole] || DEFAULT_ROLE_PROMPT;
326
716
  return `You are reviewing a set of work memories from an AI coding agent.
327
717
  These are raw tool call records from ${cluster.dateRange}.
328
718
 
@@ -331,19 +721,15 @@ Agent: ${cluster.agentId} | Project: ${cluster.projectName} | Date: ${cluster.da
331
721
  MEMORIES:
332
722
  ${snippets}
333
723
 
334
- Extract EXACTLY THREE types of insights:
335
-
336
- 1. KEY DECISIONS \u2014 choices that were made and why (at most 3)
337
- 2. RECURRING PATTERNS \u2014 actions or approaches that repeated (at most 3)
338
- 3. OPEN QUESTIONS \u2014 things that seem unresolved or risky (at most 2)
724
+ ${rolePrompt}
339
725
 
340
726
  Format each as a single sentence. Be specific \u2014 include file names,
341
727
  function names, and concrete details. Skip if no insight for a category.`;
342
728
  }
343
- async function consolidateCluster(cluster, model) {
729
+ async function consolidateCluster(cluster, model, agentRole) {
344
730
  const Anthropic = (await import("@anthropic-ai/sdk")).default;
345
731
  const client = new Anthropic();
346
- const prompt = buildConsolidationPrompt(cluster);
732
+ const prompt = buildConsolidationPrompt(cluster, agentRole);
347
733
  const response = await client.messages.create({
348
734
  model,
349
735
  max_tokens: 300,
@@ -487,6 +873,15 @@ async function runConsolidation(client, options) {
487
873
  if (memories.length < 20) {
488
874
  return { clustersProcessed: 0, memoriesConsolidated: 0 };
489
875
  }
876
+ let roleMap = {};
877
+ try {
878
+ const { loadEmployeesSync: loadEmployeesSync2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
879
+ const employees = loadEmployeesSync2();
880
+ for (const e of employees) {
881
+ roleMap[e.name] = e.role;
882
+ }
883
+ } catch {
884
+ }
490
885
  const clusters = groupMemories(memories);
491
886
  let clustersProcessed = 0;
492
887
  let memoriesConsolidated = 0;
@@ -495,21 +890,27 @@ async function runConsolidation(client, options) {
495
890
  if (cluster.memories.length < 3) continue;
496
891
  try {
497
892
  const isCoordinator = isCoordinatorName(cluster.agentId);
498
- if (isCoordinator) {
499
- const synthesis = await consolidateCluster(cluster, options.model);
500
- if (!synthesis.trim()) continue;
501
- const result = await storeConsolidation(client, cluster, synthesis, options.embedFn);
502
- if (options.wikiConfig) {
503
- await pushToWiki(
504
- { ...result, projectName: cluster.projectName },
505
- options.wikiConfig
506
- ).catch((err) => {
507
- process.stderr.write(
508
- `[consolidation] Wiki push error (non-fatal): ${err instanceof Error ? err.message : String(err)}
893
+ const agentRole = roleMap[cluster.agentId];
894
+ const dedupCount = await dedupCluster(client, cluster, options.embedFn);
895
+ memoriesConsolidated += dedupCount;
896
+ const synthesis = await consolidateCluster(cluster, options.model, agentRole);
897
+ if (!synthesis.trim()) {
898
+ if (dedupCount > 0) clustersProcessed++;
899
+ continue;
900
+ }
901
+ const result = await storeConsolidation(client, cluster, synthesis, options.embedFn);
902
+ if (isCoordinator && options.wikiConfig) {
903
+ await pushToWiki(
904
+ { ...result, projectName: cluster.projectName },
905
+ options.wikiConfig
906
+ ).catch((err) => {
907
+ process.stderr.write(
908
+ `[consolidation] Wiki push error (non-fatal): ${err instanceof Error ? err.message : String(err)}
509
909
  `
510
- );
511
- });
512
- }
910
+ );
911
+ });
912
+ }
913
+ if (isCoordinator) {
513
914
  const sourceIds = result.sourceIds;
514
915
  if (sourceIds.length > 0) {
515
916
  const placeholders = sourceIds.map(() => "?").join(",");
@@ -518,13 +919,9 @@ async function runConsolidation(client, options) {
518
919
  args: sourceIds
519
920
  });
520
921
  }
521
- } else {
522
- const dedupCount = await dedupCluster(client, cluster, options.embedFn);
523
- memoriesConsolidated += dedupCount;
524
- if (dedupCount === 0) continue;
525
922
  }
526
923
  clustersProcessed++;
527
- memoriesConsolidated += isCoordinator ? cluster.memories.length : 0;
924
+ memoriesConsolidated += cluster.memories.length;
528
925
  } catch (err) {
529
926
  process.stderr.write(
530
927
  `[consolidation] Cluster failed (${cluster.projectName}/${cluster.dateRange}): ${err instanceof Error ? err.message : String(err)}
@@ -600,8 +997,7 @@ async function isUserIdle(client, idleMinutes = 30) {
600
997
  async function countUnconsolidated(client) {
601
998
  const result = await client.execute({
602
999
  sql: `SELECT COUNT(*) as cnt FROM memories
603
- WHERE consolidated = 0
604
- AND timestamp >= datetime('now', '-7 days')`,
1000
+ WHERE consolidated = 0`,
605
1001
  args: []
606
1002
  });
607
1003
  return Number(result.rows[0]?.cnt ?? 0);
@@ -553,7 +553,7 @@ function createDaemonDbClient(fallbackClient) {
553
553
  });
554
554
  if (response.error) {
555
555
  const errMsg = String(response.error);
556
- if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
556
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
557
557
  process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
558
558
  `);
559
559
  return fallbackClient.execute(stmt);
@@ -578,7 +578,7 @@ function createDaemonDbClient(fallbackClient) {
578
578
  });
579
579
  if (response.error) {
580
580
  const errMsg = String(response.error);
581
- if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed") {
581
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
582
582
  process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
583
583
  `);
584
584
  return fallbackClient.batch(stmts, mode);
@@ -654,9 +654,9 @@ var init_db_daemon_client = __esm({
654
654
  import { createClient } from "@libsql/client";
655
655
 
656
656
  // src/lib/db-retry.ts
657
- var MAX_RETRIES = 3;
658
- var BASE_DELAY_MS = 200;
659
- var MAX_JITTER_MS = 300;
657
+ var MAX_RETRIES = 5;
658
+ var BASE_DELAY_MS = 250;
659
+ var MAX_JITTER_MS = 400;
660
660
  function isBusyError(err) {
661
661
  if (err instanceof Error) {
662
662
  const msg = err.message.toLowerCase();
@@ -1312,6 +1312,9 @@ async function createPrismaDbAdapter(fallbackClient) {
1312
1312
  return adapter;
1313
1313
  }
1314
1314
 
1315
+ // src/types/memory.ts
1316
+ var EMBEDDING_DIM = 1024;
1317
+
1315
1318
  // src/lib/database.ts
1316
1319
  var _client = null;
1317
1320
  var _resilientClient = null;
@@ -1356,7 +1359,7 @@ async function initDatabase(config) {
1356
1359
  });
1357
1360
  }, 3e4);
1358
1361
  _walCheckpointTimer.unref();
1359
- if (process.env.DATABASE_URL) {
1362
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
1360
1363
  _adapterClient = await createPrismaDbAdapter(_resilientClient);
1361
1364
  }
1362
1365
  }
@@ -1367,7 +1370,7 @@ function getClient() {
1367
1370
  if (!_adapterClient) {
1368
1371
  throw new Error("Database client not initialized. Call initDatabase() first.");
1369
1372
  }
1370
- if (process.env.DATABASE_URL) {
1373
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
1371
1374
  return _adapterClient;
1372
1375
  }
1373
1376
  if (process.env.EXE_IS_DAEMON === "1") {
@@ -1379,9 +1382,11 @@ function getClient() {
1379
1382
  return _resilientClient;
1380
1383
  }
1381
1384
  async function initDaemonClient() {
1382
- if (process.env.DATABASE_URL) return;
1385
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") return;
1383
1386
  if (process.env.EXE_IS_DAEMON === "1") return;
1387
+ if (process.env.VITEST) return;
1384
1388
  if (!_resilientClient) return;
1389
+ if (_daemonClient) return;
1385
1390
  try {
1386
1391
  const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
1387
1392
  _daemonClient = await initDaemonDbClient2(_resilientClient);
@@ -1530,6 +1535,13 @@ async function ensureSchema() {
1530
1535
  });
1531
1536
  } catch {
1532
1537
  }
1538
+ try {
1539
+ await client.execute({
1540
+ sql: `ALTER TABLE behaviors ADD COLUMN vector F32_BLOB(${EMBEDDING_DIM})`,
1541
+ args: []
1542
+ });
1543
+ } catch {
1544
+ }
1533
1545
  try {
1534
1546
  await client.execute({
1535
1547
  sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,