@askexenow/exe-os 0.9.112 → 0.9.113

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 (95) hide show
  1. package/README.md +9 -7
  2. package/dist/bin/agentic-ontology-backfill.js +54 -11
  3. package/dist/bin/agentic-reflection-backfill.js +29 -1
  4. package/dist/bin/agentic-semantic-label.js +29 -1
  5. package/dist/bin/backfill-conversations.js +53 -10
  6. package/dist/bin/backfill-responses.js +54 -11
  7. package/dist/bin/backfill-vectors.js +29 -1
  8. package/dist/bin/bulk-sync-postgres.js +55 -12
  9. package/dist/bin/cleanup-stale-review-tasks.js +75 -15
  10. package/dist/bin/cli.js +293 -76
  11. package/dist/bin/exe-agent-config.js +7 -1
  12. package/dist/bin/exe-agent.js +28 -2
  13. package/dist/bin/exe-assign.js +54 -11
  14. package/dist/bin/exe-boot.js +481 -147
  15. package/dist/bin/exe-call.js +45 -4
  16. package/dist/bin/exe-cloud.js +93 -15
  17. package/dist/bin/exe-dispatch.js +369 -24
  18. package/dist/bin/exe-doctor.js +53 -10
  19. package/dist/bin/exe-export-behaviors.js +54 -11
  20. package/dist/bin/exe-forget.js +54 -11
  21. package/dist/bin/exe-gateway.js +128 -23
  22. package/dist/bin/exe-heartbeat.js +75 -15
  23. package/dist/bin/exe-kill.js +54 -11
  24. package/dist/bin/exe-launch-agent.js +70 -12
  25. package/dist/bin/exe-new-employee.js +175 -7
  26. package/dist/bin/exe-pending-messages.js +75 -15
  27. package/dist/bin/exe-pending-notifications.js +75 -15
  28. package/dist/bin/exe-pending-reviews.js +75 -15
  29. package/dist/bin/exe-rename.js +54 -11
  30. package/dist/bin/exe-review.js +54 -11
  31. package/dist/bin/exe-search.js +54 -11
  32. package/dist/bin/exe-session-cleanup.js +491 -146
  33. package/dist/bin/exe-settings.js +10 -4
  34. package/dist/bin/exe-start-codex.js +524 -245
  35. package/dist/bin/exe-start-opencode.js +534 -165
  36. package/dist/bin/exe-status.js +75 -15
  37. package/dist/bin/exe-support.js +1 -1
  38. package/dist/bin/exe-team.js +54 -11
  39. package/dist/bin/git-sweep.js +369 -24
  40. package/dist/bin/graph-backfill.js +54 -11
  41. package/dist/bin/graph-export.js +54 -11
  42. package/dist/bin/install.js +62 -4
  43. package/dist/bin/intercom-check.js +491 -146
  44. package/dist/bin/pre-publish.js +13 -1
  45. package/dist/bin/scan-tasks.js +369 -24
  46. package/dist/bin/setup.js +91 -13
  47. package/dist/bin/shard-migrate.js +54 -11
  48. package/dist/bin/stack-update.js +1 -1
  49. package/dist/bin/update.js +3 -3
  50. package/dist/gateway/index.js +128 -23
  51. package/dist/hooks/bug-report-worker.js +128 -23
  52. package/dist/hooks/codex-stop-task-finalizer.js +512 -140
  53. package/dist/hooks/commit-complete.js +369 -24
  54. package/dist/hooks/error-recall.js +54 -11
  55. package/dist/hooks/ingest.js +4575 -252
  56. package/dist/hooks/instructions-loaded.js +54 -11
  57. package/dist/hooks/notification.js +54 -11
  58. package/dist/hooks/post-compact.js +75 -15
  59. package/dist/hooks/post-tool-combined.js +75 -15
  60. package/dist/hooks/pre-compact.js +449 -104
  61. package/dist/hooks/pre-tool-use.js +90 -15
  62. package/dist/hooks/prompt-submit.js +129 -24
  63. package/dist/hooks/session-end.js +451 -109
  64. package/dist/hooks/session-start.js +104 -16
  65. package/dist/hooks/stop.js +74 -14
  66. package/dist/hooks/subagent-stop.js +75 -15
  67. package/dist/hooks/summary-worker.js +73 -7
  68. package/dist/index.js +128 -23
  69. package/dist/lib/agent-config.js +16 -1
  70. package/dist/lib/cloud-sync.js +38 -1
  71. package/dist/lib/consolidation.js +16 -1
  72. package/dist/lib/database.js +16 -0
  73. package/dist/lib/db.js +16 -0
  74. package/dist/lib/device-registry.js +16 -0
  75. package/dist/lib/employee-templates.js +29 -3
  76. package/dist/lib/employees.js +16 -1
  77. package/dist/lib/exe-daemon.js +268 -42
  78. package/dist/lib/hybrid-search.js +54 -11
  79. package/dist/lib/license.js +3 -3
  80. package/dist/lib/messaging.js +21 -4
  81. package/dist/lib/schedules.js +29 -1
  82. package/dist/lib/skill-learning.js +458 -70
  83. package/dist/lib/status-brief.js +14 -1
  84. package/dist/lib/store.js +54 -11
  85. package/dist/lib/tasks.js +393 -91
  86. package/dist/lib/tmux-routing.js +316 -14
  87. package/dist/mcp/server.js +169 -30
  88. package/dist/mcp/tools/create-task.js +75 -13
  89. package/dist/mcp/tools/deactivate-behavior.js +33 -24
  90. package/dist/mcp/tools/list-tasks.js +21 -4
  91. package/dist/mcp/tools/send-message.js +21 -4
  92. package/dist/mcp/tools/update-task.js +390 -91
  93. package/dist/runtime/index.js +446 -101
  94. package/dist/tui/App.js +208 -54
  95. package/package.json +1 -1
@@ -319,6 +319,394 @@ var init_config = __esm({
319
319
  }
320
320
  });
321
321
 
322
+ // src/lib/runtime-table.ts
323
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
324
+ var init_runtime_table = __esm({
325
+ "src/lib/runtime-table.ts"() {
326
+ "use strict";
327
+ RUNTIME_TABLE = {
328
+ codex: {
329
+ binary: "codex",
330
+ launchMode: "interactive",
331
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
332
+ inlineFlag: "--no-alt-screen",
333
+ apiKeyEnv: "OPENAI_API_KEY",
334
+ defaultModel: "gpt-5.5"
335
+ },
336
+ opencode: {
337
+ binary: "opencode",
338
+ launchMode: "exec",
339
+ autoApproveFlag: "--dangerously-skip-permissions",
340
+ inlineFlag: "",
341
+ apiKeyEnv: "ANTHROPIC_API_KEY",
342
+ defaultModel: "anthropic/claude-sonnet-4-6"
343
+ }
344
+ };
345
+ DEFAULT_RUNTIME = "claude";
346
+ }
347
+ });
348
+
349
+ // src/lib/agent-config.ts
350
+ var agent_config_exports = {};
351
+ __export(agent_config_exports, {
352
+ AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
353
+ DEFAULT_MODELS: () => DEFAULT_MODELS,
354
+ KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
355
+ RUNTIME_LABELS: () => RUNTIME_LABELS,
356
+ clearAgentRuntime: () => clearAgentRuntime,
357
+ getAgentRuntime: () => getAgentRuntime,
358
+ loadAgentConfig: () => loadAgentConfig,
359
+ saveAgentConfig: () => saveAgentConfig,
360
+ setAgentMcps: () => setAgentMcps,
361
+ setAgentRuntime: () => setAgentRuntime
362
+ });
363
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
364
+ import path2 from "path";
365
+ function loadAgentConfig() {
366
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
367
+ try {
368
+ return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
369
+ } catch {
370
+ return {};
371
+ }
372
+ }
373
+ function saveAgentConfig(config) {
374
+ const dir = path2.dirname(AGENT_CONFIG_PATH);
375
+ ensurePrivateDirSync(dir);
376
+ writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
377
+ enforcePrivateFileSync(AGENT_CONFIG_PATH);
378
+ }
379
+ function getAgentRuntime(agentId) {
380
+ const config = loadAgentConfig();
381
+ const entry = config[agentId];
382
+ if (entry) return entry;
383
+ const orgDefault = config["default"];
384
+ if (orgDefault) return orgDefault;
385
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
386
+ }
387
+ function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
388
+ const knownModels = KNOWN_RUNTIMES[runtime];
389
+ if (!knownModels) {
390
+ return {
391
+ ok: false,
392
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
393
+ };
394
+ }
395
+ if (!knownModels.includes(model)) {
396
+ return {
397
+ ok: false,
398
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
399
+ };
400
+ }
401
+ const config = loadAgentConfig();
402
+ const existing = config[agentId];
403
+ const entry = { runtime, model };
404
+ if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
405
+ if (mcps !== void 0) {
406
+ entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
407
+ } else if (existing?.mcps) {
408
+ entry.mcps = existing.mcps;
409
+ }
410
+ config[agentId] = entry;
411
+ saveAgentConfig(config);
412
+ return { ok: true };
413
+ }
414
+ function setAgentMcps(agentId, mcps) {
415
+ const config = loadAgentConfig();
416
+ const existing = config[agentId] ?? getAgentRuntime(agentId);
417
+ existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
418
+ config[agentId] = existing;
419
+ saveAgentConfig(config);
420
+ return { ok: true };
421
+ }
422
+ function clearAgentRuntime(agentId) {
423
+ const config = loadAgentConfig();
424
+ delete config[agentId];
425
+ saveAgentConfig(config);
426
+ }
427
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
428
+ var init_agent_config = __esm({
429
+ "src/lib/agent-config.ts"() {
430
+ "use strict";
431
+ init_config();
432
+ init_runtime_table();
433
+ init_secure_files();
434
+ AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
435
+ KNOWN_RUNTIMES = {
436
+ claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
437
+ codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
438
+ opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
439
+ };
440
+ RUNTIME_LABELS = {
441
+ claude: "Claude Code (Anthropic)",
442
+ codex: "Codex (OpenAI)",
443
+ opencode: "OpenCode (open source)"
444
+ };
445
+ DEFAULT_MODELS = {
446
+ claude: "claude-opus-4.6",
447
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
448
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
449
+ };
450
+ }
451
+ });
452
+
453
+ // src/lib/employees.ts
454
+ var employees_exports = {};
455
+ __export(employees_exports, {
456
+ COORDINATOR_ROLE: () => COORDINATOR_ROLE,
457
+ DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
458
+ EMPLOYEES_PATH: () => EMPLOYEES_PATH,
459
+ addEmployee: () => addEmployee,
460
+ baseAgentName: () => baseAgentName,
461
+ canCoordinate: () => canCoordinate,
462
+ getCoordinatorEmployee: () => getCoordinatorEmployee,
463
+ getCoordinatorName: () => getCoordinatorName,
464
+ getEmployee: () => getEmployee,
465
+ getEmployeeByRole: () => getEmployeeByRole,
466
+ getEmployeeNamesByRole: () => getEmployeeNamesByRole,
467
+ hasRole: () => hasRole,
468
+ hireEmployee: () => hireEmployee,
469
+ isCoordinatorName: () => isCoordinatorName,
470
+ isCoordinatorRole: () => isCoordinatorRole,
471
+ isMultiInstance: () => isMultiInstance,
472
+ loadEmployees: () => loadEmployees,
473
+ loadEmployeesSync: () => loadEmployeesSync,
474
+ normalizeRole: () => normalizeRole,
475
+ normalizeRosterCase: () => normalizeRosterCase,
476
+ registerBinSymlinks: () => registerBinSymlinks,
477
+ saveEmployees: () => saveEmployees,
478
+ validateEmployeeName: () => validateEmployeeName
479
+ });
480
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
481
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
482
+ import { execSync } from "child_process";
483
+ import path3 from "path";
484
+ import os2 from "os";
485
+ function normalizeRole(role) {
486
+ return (role ?? "").trim().toLowerCase();
487
+ }
488
+ function isCoordinatorRole(role) {
489
+ return normalizeRole(role) === normalizeRole(COORDINATOR_ROLE);
490
+ }
491
+ function getCoordinatorEmployee(employees) {
492
+ return employees.find((e) => isCoordinatorRole(e.role));
493
+ }
494
+ function getCoordinatorName(employees = loadEmployeesSync()) {
495
+ return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
496
+ }
497
+ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
498
+ if (!agentName) return false;
499
+ return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
500
+ }
501
+ function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
502
+ return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
503
+ }
504
+ function validateEmployeeName(name) {
505
+ if (!name) {
506
+ return { valid: false, error: "Name is required" };
507
+ }
508
+ if (name.length > 32) {
509
+ return { valid: false, error: "Name must be 32 characters or fewer" };
510
+ }
511
+ if (!/^[a-z][a-z0-9]*$/.test(name)) {
512
+ return {
513
+ valid: false,
514
+ error: "Name must start with a letter and contain only lowercase alphanumeric characters"
515
+ };
516
+ }
517
+ return { valid: true };
518
+ }
519
+ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
520
+ if (!existsSync4(employeesPath)) {
521
+ return [];
522
+ }
523
+ const raw = await readFile2(employeesPath, "utf-8");
524
+ try {
525
+ return JSON.parse(raw);
526
+ } catch {
527
+ return [];
528
+ }
529
+ }
530
+ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
531
+ await mkdir2(path3.dirname(employeesPath), { recursive: true });
532
+ await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
533
+ }
534
+ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
535
+ if (!existsSync4(employeesPath)) return [];
536
+ try {
537
+ return JSON.parse(readFileSync3(employeesPath, "utf-8"));
538
+ } catch {
539
+ return [];
540
+ }
541
+ }
542
+ function getEmployee(employees, name) {
543
+ return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
544
+ }
545
+ function getEmployeeByRole(employees, role) {
546
+ const lower = role.toLowerCase();
547
+ return employees.find((e) => e.role.toLowerCase() === lower);
548
+ }
549
+ function getEmployeeNamesByRole(employees, role) {
550
+ const lower = role.toLowerCase();
551
+ return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
552
+ }
553
+ function hasRole(agentName, role) {
554
+ const employees = loadEmployeesSync();
555
+ const emp = getEmployee(employees, agentName);
556
+ return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
557
+ }
558
+ function baseAgentName(name, employees) {
559
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
560
+ if (!match) return name;
561
+ const base = match[1];
562
+ const roster = employees ?? loadEmployeesSync();
563
+ if (getEmployee(roster, base)) return base;
564
+ return name;
565
+ }
566
+ function isMultiInstance(agentName, employees) {
567
+ const roster = employees ?? loadEmployeesSync();
568
+ const emp = getEmployee(roster, agentName);
569
+ if (!emp) return false;
570
+ return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
571
+ }
572
+ function addEmployee(employees, employee) {
573
+ const { systemPrompt: _legacyPrompt, ...rest } = employee;
574
+ const normalized = { ...rest, name: employee.name.toLowerCase() };
575
+ if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
576
+ throw new Error(`Employee '${normalized.name}' already exists`);
577
+ }
578
+ return [...employees, normalized];
579
+ }
580
+ function appendToCoordinatorTeam(employee) {
581
+ const coordinator = getCoordinatorEmployee(loadEmployeesSync());
582
+ if (!coordinator) return;
583
+ const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
584
+ if (!existsSync4(idPath)) return;
585
+ const content = readFileSync3(idPath, "utf-8");
586
+ if (content.includes(`**${capitalize(employee.name)}`)) return;
587
+ const teamMatch = content.match(TEAM_SECTION_RE);
588
+ if (!teamMatch || teamMatch.index === void 0) return;
589
+ const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
590
+ const nextHeading = afterTeam.match(/\n## /);
591
+ const entry = `
592
+ **${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
593
+ `;
594
+ let updated;
595
+ if (nextHeading && nextHeading.index !== void 0) {
596
+ const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
597
+ updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
598
+ } else {
599
+ updated = content.trimEnd() + "\n" + entry;
600
+ }
601
+ writeFileSync2(idPath, updated, "utf-8");
602
+ }
603
+ function capitalize(s) {
604
+ return s.charAt(0).toUpperCase() + s.slice(1);
605
+ }
606
+ async function hireEmployee(employee) {
607
+ const employees = await loadEmployees();
608
+ const updated = addEmployee(employees, employee);
609
+ await saveEmployees(updated);
610
+ try {
611
+ appendToCoordinatorTeam(employee);
612
+ } catch {
613
+ }
614
+ try {
615
+ const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
616
+ const config = loadAgentConfig2();
617
+ const name = employee.name.toLowerCase();
618
+ if (!config[name] && config["default"]) {
619
+ config[name] = { ...config["default"] };
620
+ saveAgentConfig2(config);
621
+ }
622
+ } catch {
623
+ }
624
+ return updated;
625
+ }
626
+ async function normalizeRosterCase(rosterPath) {
627
+ const employees = await loadEmployees(rosterPath);
628
+ let changed = false;
629
+ for (const emp of employees) {
630
+ if (emp.name !== emp.name.toLowerCase()) {
631
+ const oldName = emp.name;
632
+ emp.name = emp.name.toLowerCase();
633
+ changed = true;
634
+ try {
635
+ const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
636
+ const oldPath = path3.join(identityDir, `${oldName}.md`);
637
+ const newPath = path3.join(identityDir, `${emp.name}.md`);
638
+ if (existsSync4(oldPath) && !existsSync4(newPath)) {
639
+ renameSync2(oldPath, newPath);
640
+ } else if (existsSync4(oldPath) && oldPath !== newPath) {
641
+ const content = readFileSync3(oldPath, "utf-8");
642
+ writeFileSync2(newPath, content, "utf-8");
643
+ if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
644
+ unlinkSync(oldPath);
645
+ }
646
+ }
647
+ } catch {
648
+ }
649
+ }
650
+ }
651
+ if (changed) {
652
+ await saveEmployees(employees, rosterPath);
653
+ }
654
+ return changed;
655
+ }
656
+ function findExeBin() {
657
+ try {
658
+ return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
659
+ } catch {
660
+ return null;
661
+ }
662
+ }
663
+ function registerBinSymlinks(name) {
664
+ const created = [];
665
+ const skipped = [];
666
+ const errors = [];
667
+ const exeBinPath = findExeBin();
668
+ if (!exeBinPath) {
669
+ errors.push("Could not find 'exe-os' in PATH");
670
+ return { created, skipped, errors };
671
+ }
672
+ const binDir = path3.dirname(exeBinPath);
673
+ let target;
674
+ try {
675
+ target = readlinkSync(exeBinPath);
676
+ } catch {
677
+ errors.push("Could not read 'exe' symlink");
678
+ return { created, skipped, errors };
679
+ }
680
+ for (const suffix of ["", "-opencode"]) {
681
+ const linkName = `${name}${suffix}`;
682
+ const linkPath = path3.join(binDir, linkName);
683
+ if (existsSync4(linkPath)) {
684
+ skipped.push(linkName);
685
+ continue;
686
+ }
687
+ try {
688
+ symlinkSync(target, linkPath);
689
+ created.push(linkName);
690
+ } catch (err) {
691
+ errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
692
+ }
693
+ }
694
+ return { created, skipped, errors };
695
+ }
696
+ var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
697
+ var init_employees = __esm({
698
+ "src/lib/employees.ts"() {
699
+ "use strict";
700
+ init_config();
701
+ EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
702
+ DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
703
+ COORDINATOR_ROLE = "COO";
704
+ MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
705
+ IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
706
+ TEAM_SECTION_RE = /^## Team\b.*$/m;
707
+ }
708
+ });
709
+
322
710
  // src/types/memory.ts
323
711
  var EMBEDDING_DIM;
324
712
  var init_memory = __esm({
@@ -330,8 +718,8 @@ var init_memory = __esm({
330
718
 
331
719
  // src/lib/daemon-auth.ts
332
720
  import crypto from "crypto";
333
- import path4 from "path";
334
- import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
721
+ import path5 from "path";
722
+ import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
335
723
  function normalizeToken(token) {
336
724
  if (!token) return null;
337
725
  const trimmed = token.trim();
@@ -339,8 +727,8 @@ function normalizeToken(token) {
339
727
  }
340
728
  function readDaemonToken() {
341
729
  try {
342
- if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
343
- return normalizeToken(readFileSync3(DAEMON_TOKEN_PATH, "utf8"));
730
+ if (!existsSync6(DAEMON_TOKEN_PATH)) return null;
731
+ return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
344
732
  } catch {
345
733
  return null;
346
734
  }
@@ -350,7 +738,7 @@ function ensureDaemonToken(seed) {
350
738
  if (existing) return existing;
351
739
  const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
352
740
  ensurePrivateDirSync(EXE_AI_DIR);
353
- writeFileSync2(DAEMON_TOKEN_PATH, `${token}
741
+ writeFileSync3(DAEMON_TOKEN_PATH, `${token}
354
742
  `, "utf8");
355
743
  enforcePrivateFileSync(DAEMON_TOKEN_PATH);
356
744
  return token;
@@ -361,7 +749,7 @@ var init_daemon_auth = __esm({
361
749
  "use strict";
362
750
  init_config();
363
751
  init_secure_files();
364
- DAEMON_TOKEN_PATH = path4.join(EXE_AI_DIR, "exed.token");
752
+ DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
365
753
  }
366
754
  });
367
755
 
@@ -370,8 +758,8 @@ import net from "net";
370
758
  import os4 from "os";
371
759
  import { spawn, execSync as execSync2 } from "child_process";
372
760
  import { randomUUID } from "crypto";
373
- import { existsSync as existsSync6, unlinkSync as unlinkSync3, readFileSync as readFileSync4, openSync as openSync2, closeSync as closeSync2, statSync as statSync2 } from "fs";
374
- import path5 from "path";
761
+ import { existsSync as existsSync7, unlinkSync as unlinkSync3, readFileSync as readFileSync5, openSync as openSync2, closeSync as closeSync2, statSync as statSync2 } from "fs";
762
+ import path6 from "path";
375
763
  import { fileURLToPath } from "url";
376
764
  function handleData(chunk) {
377
765
  _buffer += chunk.toString();
@@ -407,9 +795,9 @@ function isZombie(pid) {
407
795
  }
408
796
  }
409
797
  function cleanupStaleFiles() {
410
- if (existsSync6(PID_PATH)) {
798
+ if (existsSync7(PID_PATH)) {
411
799
  try {
412
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
800
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
413
801
  if (pid > 0) {
414
802
  try {
415
803
  process.kill(pid, 0);
@@ -434,11 +822,11 @@ function cleanupStaleFiles() {
434
822
  }
435
823
  }
436
824
  function findPackageRoot() {
437
- let dir = path5.dirname(fileURLToPath(import.meta.url));
438
- const { root } = path5.parse(dir);
825
+ let dir = path6.dirname(fileURLToPath(import.meta.url));
826
+ const { root } = path6.parse(dir);
439
827
  while (dir !== root) {
440
- if (existsSync6(path5.join(dir, "package.json"))) return dir;
441
- dir = path5.dirname(dir);
828
+ if (existsSync7(path6.join(dir, "package.json"))) return dir;
829
+ dir = path6.dirname(dir);
442
830
  }
443
831
  return null;
444
832
  }
@@ -456,8 +844,8 @@ function spawnDaemon() {
456
844
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
457
845
  return;
458
846
  }
459
- const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
460
- if (!existsSync6(daemonPath)) {
847
+ const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
848
+ if (!existsSync7(daemonPath)) {
461
849
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
462
850
  `);
463
851
  return;
@@ -466,7 +854,7 @@ function spawnDaemon() {
466
854
  const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
467
855
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
468
856
  `);
469
- const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
857
+ const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
470
858
  let stderrFd = "ignore";
471
859
  try {
472
860
  stderrFd = openSync2(logPath, "a");
@@ -631,9 +1019,9 @@ function killAndRespawnDaemon() {
631
1019
  }
632
1020
  try {
633
1021
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
634
- if (existsSync6(PID_PATH)) {
1022
+ if (existsSync7(PID_PATH)) {
635
1023
  try {
636
- const pid = parseInt(readFileSync4(PID_PATH, "utf8").trim(), 10);
1024
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
637
1025
  if (pid > 0) {
638
1026
  try {
639
1027
  process.kill(pid, "SIGKILL");
@@ -756,9 +1144,9 @@ var init_exe_daemon_client = __esm({
756
1144
  "use strict";
757
1145
  init_config();
758
1146
  init_daemon_auth();
759
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
760
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
761
- SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
1147
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
1148
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
1149
+ SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
762
1150
  SPAWN_LOCK_STALE_MS = 3e4;
763
1151
  CONNECT_TIMEOUT_MS = 15e3;
764
1152
  REQUEST_TIMEOUT_MS = 3e4;
@@ -814,10 +1202,10 @@ async function disposeEmbedder() {
814
1202
  async function embedDirect(text) {
815
1203
  const llamaCpp = await import("node-llama-cpp");
816
1204
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
817
- const { existsSync: existsSync8 } = await import("fs");
818
- const path7 = await import("path");
819
- const modelPath = path7.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
820
- if (!existsSync8(modelPath)) {
1205
+ const { existsSync: existsSync9 } = await import("fs");
1206
+ const path8 = await import("path");
1207
+ const modelPath = path8.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
1208
+ if (!existsSync9(modelPath)) {
821
1209
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
822
1210
  }
823
1211
  const llama = await llamaCpp.getLlama();
@@ -863,12 +1251,12 @@ __export(license_exports, {
863
1251
  stopLicenseRevalidation: () => stopLicenseRevalidation,
864
1252
  validateLicense: () => validateLicense
865
1253
  });
866
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
1254
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
867
1255
  import { randomUUID as randomUUID2 } from "crypto";
868
1256
  import { createRequire as createRequire2 } from "module";
869
1257
  import { pathToFileURL as pathToFileURL2 } from "url";
870
1258
  import os5 from "os";
871
- import path6 from "path";
1259
+ import path7 from "path";
872
1260
  import { jwtVerify, importSPKI } from "jose";
873
1261
  async function fetchRetry(url, init) {
874
1262
  try {
@@ -879,37 +1267,37 @@ async function fetchRetry(url, init) {
879
1267
  }
880
1268
  }
881
1269
  function loadDeviceId() {
882
- const deviceJsonPath = path6.join(EXE_AI_DIR, "device.json");
1270
+ const deviceJsonPath = path7.join(EXE_AI_DIR, "device.json");
883
1271
  try {
884
- if (existsSync7(deviceJsonPath)) {
885
- const data = JSON.parse(readFileSync5(deviceJsonPath, "utf8"));
1272
+ if (existsSync8(deviceJsonPath)) {
1273
+ const data = JSON.parse(readFileSync6(deviceJsonPath, "utf8"));
886
1274
  if (data.deviceId) return data.deviceId;
887
1275
  }
888
1276
  } catch {
889
1277
  }
890
1278
  try {
891
- if (existsSync7(DEVICE_ID_PATH)) {
892
- const id2 = readFileSync5(DEVICE_ID_PATH, "utf8").trim();
1279
+ if (existsSync8(DEVICE_ID_PATH)) {
1280
+ const id2 = readFileSync6(DEVICE_ID_PATH, "utf8").trim();
893
1281
  if (id2) return id2;
894
1282
  }
895
1283
  } catch {
896
1284
  }
897
1285
  const id = randomUUID2();
898
1286
  mkdirSync3(EXE_AI_DIR, { recursive: true });
899
- writeFileSync3(DEVICE_ID_PATH, id, "utf8");
1287
+ writeFileSync4(DEVICE_ID_PATH, id, "utf8");
900
1288
  return id;
901
1289
  }
902
1290
  function loadLicense() {
903
1291
  try {
904
- if (!existsSync7(LICENSE_PATH)) return null;
905
- return readFileSync5(LICENSE_PATH, "utf8").trim();
1292
+ if (!existsSync8(LICENSE_PATH)) return null;
1293
+ return readFileSync6(LICENSE_PATH, "utf8").trim();
906
1294
  } catch {
907
1295
  return null;
908
1296
  }
909
1297
  }
910
1298
  function saveLicense(apiKey) {
911
1299
  mkdirSync3(EXE_AI_DIR, { recursive: true });
912
- writeFileSync3(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
1300
+ writeFileSync4(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
913
1301
  }
914
1302
  async function verifyLicenseJwt(token) {
915
1303
  try {
@@ -935,8 +1323,8 @@ async function verifyLicenseJwt(token) {
935
1323
  }
936
1324
  async function getCachedLicense() {
937
1325
  try {
938
- if (!existsSync7(CACHE_PATH)) return null;
939
- const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
1326
+ if (!existsSync8(CACHE_PATH)) return null;
1327
+ const raw = JSON.parse(readFileSync6(CACHE_PATH, "utf8"));
940
1328
  if (!raw.token || typeof raw.token !== "string") return null;
941
1329
  return await verifyLicenseJwt(raw.token);
942
1330
  } catch {
@@ -945,8 +1333,8 @@ async function getCachedLicense() {
945
1333
  }
946
1334
  function readCachedLicenseToken() {
947
1335
  try {
948
- if (!existsSync7(CACHE_PATH)) return null;
949
- const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
1336
+ if (!existsSync8(CACHE_PATH)) return null;
1337
+ const raw = JSON.parse(readFileSync6(CACHE_PATH, "utf8"));
950
1338
  return typeof raw.token === "string" ? raw.token : null;
951
1339
  } catch {
952
1340
  return null;
@@ -980,7 +1368,7 @@ function getRawCachedPlan() {
980
1368
  }
981
1369
  function cacheResponse(token) {
982
1370
  try {
983
- writeFileSync3(CACHE_PATH, JSON.stringify({ token }), "utf8");
1371
+ writeFileSync4(CACHE_PATH, JSON.stringify({ token }), "utf8");
984
1372
  } catch {
985
1373
  }
986
1374
  }
@@ -988,8 +1376,8 @@ function loadPrismaForLicense() {
988
1376
  if (_prismaFailed) return null;
989
1377
  const dbUrl = process.env.DATABASE_URL;
990
1378
  if (!dbUrl) {
991
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os5.homedir(), "exe-db");
992
- if (!existsSync7(path6.join(exeDbRoot, "package.json"))) {
1379
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path7.join(os5.homedir(), "exe-db");
1380
+ if (!existsSync8(path7.join(exeDbRoot, "package.json"))) {
993
1381
  _prismaFailed = true;
994
1382
  return null;
995
1383
  }
@@ -1003,8 +1391,8 @@ function loadPrismaForLicense() {
1003
1391
  if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
1004
1392
  return new Ctor2();
1005
1393
  }
1006
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path6.join(os5.homedir(), "exe-db");
1007
- const req = createRequire2(path6.join(exeDbRoot, "package.json"));
1394
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path7.join(os5.homedir(), "exe-db");
1395
+ const req = createRequire2(path7.join(exeDbRoot, "package.json"));
1008
1396
  const entry = req.resolve("@prisma/client");
1009
1397
  const mod = await import(pathToFileURL2(entry).href);
1010
1398
  const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
@@ -1083,7 +1471,7 @@ async function validateLicense(apiKey, deviceId) {
1083
1471
  const pgResult = await validateViaPostgres(apiKey);
1084
1472
  if (pgResult) {
1085
1473
  try {
1086
- writeFileSync3(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
1474
+ writeFileSync4(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
1087
1475
  } catch {
1088
1476
  }
1089
1477
  return pgResult;
@@ -1093,8 +1481,8 @@ async function validateLicense(apiKey, deviceId) {
1093
1481
  const cached = await getCachedLicense();
1094
1482
  if (cached) return cached;
1095
1483
  try {
1096
- if (existsSync7(CACHE_PATH)) {
1097
- const raw = JSON.parse(readFileSync5(CACHE_PATH, "utf8"));
1484
+ if (existsSync8(CACHE_PATH)) {
1485
+ const raw = JSON.parse(readFileSync6(CACHE_PATH, "utf8"));
1098
1486
  if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
1099
1487
  return raw.pgLicense;
1100
1488
  }
@@ -1118,9 +1506,9 @@ async function checkLicense() {
1118
1506
  let key = loadLicense();
1119
1507
  if (!key) {
1120
1508
  try {
1121
- const configPath = path6.join(EXE_AI_DIR, "config.json");
1122
- if (existsSync7(configPath)) {
1123
- const raw = JSON.parse(readFileSync5(configPath, "utf8"));
1509
+ const configPath = path7.join(EXE_AI_DIR, "config.json");
1510
+ if (existsSync8(configPath)) {
1511
+ const raw = JSON.parse(readFileSync6(configPath, "utf8"));
1124
1512
  const cloud = raw.cloud;
1125
1513
  if (cloud?.apiKey) {
1126
1514
  key = cloud.apiKey;
@@ -1213,7 +1601,7 @@ async function assertVpsLicense(opts) {
1213
1601
  }
1214
1602
  if (!transientFailure) {
1215
1603
  throw new Error(
1216
- "License validation failed: unknown backend state. Restore network connectivity to https://askexe.com/cloud and retry."
1604
+ "License validation failed: unknown backend state. Restore network connectivity to https://cloud.askexe.com and retry."
1217
1605
  );
1218
1606
  }
1219
1607
  const fresh = await getCachedLicense();
@@ -1250,7 +1638,7 @@ async function assertVpsLicense(opts) {
1250
1638
  } catch {
1251
1639
  }
1252
1640
  throw new Error(
1253
- `License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com/cloud and retry. This VPS image refuses to boot after the offline grace window.`
1641
+ `License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://cloud.askexe.com and retry. This VPS image refuses to boot after the offline grace window.`
1254
1642
  );
1255
1643
  }
1256
1644
  function startLicenseRevalidation(intervalMs = 36e5) {
@@ -1279,10 +1667,10 @@ var init_license = __esm({
1279
1667
  "src/lib/license.ts"() {
1280
1668
  "use strict";
1281
1669
  init_config();
1282
- LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
1283
- CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
1284
- DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
1285
- API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
1670
+ LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
1671
+ CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
1672
+ DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
1673
+ API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
1286
1674
  RETRY_DELAY_MS = 500;
1287
1675
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
1288
1676
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
@@ -1316,24 +1704,15 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
1316
1704
  import crypto3 from "crypto";
1317
1705
 
1318
1706
  // src/lib/database.ts
1319
- import { chmodSync as chmodSync2, existsSync as existsSync4, statSync, copyFileSync, unlinkSync as unlinkSync2, openSync, closeSync, mkdirSync as mkdirSync2 } from "fs";
1707
+ import { chmodSync as chmodSync2, existsSync as existsSync5, statSync, copyFileSync, unlinkSync as unlinkSync2, openSync, closeSync, mkdirSync as mkdirSync2 } from "fs";
1320
1708
  import { createClient } from "@libsql/client";
1321
1709
  import { homedir } from "os";
1322
1710
  import { join } from "path";
1323
-
1324
- // src/lib/employees.ts
1325
- init_config();
1326
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
1327
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
1328
- import { execSync } from "child_process";
1329
- import path2 from "path";
1330
- import os2 from "os";
1331
- var EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
1332
- var IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
1711
+ init_employees();
1333
1712
 
1334
1713
  // src/lib/database-adapter.ts
1335
1714
  import os3 from "os";
1336
- import path3 from "path";
1715
+ import path4 from "path";
1337
1716
  import { createRequire } from "module";
1338
1717
  import { pathToFileURL } from "url";
1339
1718
  var BOOLEAN_COLUMNS_BY_TABLE = {
@@ -1389,6 +1768,15 @@ function getClient() {
1389
1768
  // src/lib/behaviors.ts
1390
1769
  import crypto2 from "crypto";
1391
1770
  async function storeBehavior(opts) {
1771
+ try {
1772
+ const { loadEmployeesSync: loadEmployeesSync2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
1773
+ const roster = loadEmployeesSync2();
1774
+ if (roster.length > 0 && !roster.some((e) => e.name === opts.agentId)) {
1775
+ throw new Error(`Agent "${opts.agentId}" not found in roster. Cannot store behavior for unregistered agent.`);
1776
+ }
1777
+ } catch (e) {
1778
+ if (e instanceof Error && e.message.includes("not found in roster")) throw e;
1779
+ }
1392
1780
  const client = getClient();
1393
1781
  const id = crypto2.randomUUID();
1394
1782
  const now = (/* @__PURE__ */ new Date()).toISOString();