@askexenow/exe-os 0.9.111 → 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.
- package/README.md +9 -7
- package/dist/bin/agentic-ontology-backfill.js +62 -12
- package/dist/bin/agentic-reflection-backfill.js +37 -2
- package/dist/bin/agentic-semantic-label.js +37 -2
- package/dist/bin/backfill-conversations.js +61 -11
- package/dist/bin/backfill-responses.js +62 -12
- package/dist/bin/backfill-vectors.js +37 -2
- package/dist/bin/bulk-sync-postgres.js +63 -13
- package/dist/bin/cleanup-stale-review-tasks.js +83 -16
- package/dist/bin/cli.js +312 -80
- package/dist/bin/exe-agent-config.js +7 -1
- package/dist/bin/exe-agent.js +29 -3
- package/dist/bin/exe-assign.js +62 -12
- package/dist/bin/exe-boot.js +500 -151
- package/dist/bin/exe-call.js +46 -5
- package/dist/bin/exe-cloud.js +101 -16
- package/dist/bin/exe-dispatch.js +827 -27
- package/dist/bin/exe-doctor.js +61 -11
- package/dist/bin/exe-export-behaviors.js +67 -14
- package/dist/bin/exe-forget.js +62 -12
- package/dist/bin/exe-gateway.js +147 -27
- package/dist/bin/exe-heartbeat.js +83 -16
- package/dist/bin/exe-kill.js +62 -12
- package/dist/bin/exe-launch-agent.js +83 -15
- package/dist/bin/exe-new-employee.js +176 -8
- package/dist/bin/exe-pending-messages.js +83 -16
- package/dist/bin/exe-pending-notifications.js +83 -16
- package/dist/bin/exe-pending-reviews.js +83 -16
- package/dist/bin/exe-rename.js +62 -12
- package/dist/bin/exe-review.js +62 -12
- package/dist/bin/exe-search.js +62 -12
- package/dist/bin/exe-session-cleanup.js +949 -149
- package/dist/bin/exe-settings.js +10 -4
- package/dist/bin/exe-start-codex.js +537 -248
- package/dist/bin/exe-start-opencode.js +547 -168
- package/dist/bin/exe-status.js +83 -16
- package/dist/bin/exe-support.js +1 -1
- package/dist/bin/exe-team.js +62 -12
- package/dist/bin/git-sweep.js +827 -27
- package/dist/bin/graph-backfill.js +62 -12
- package/dist/bin/graph-export.js +62 -12
- package/dist/bin/install.js +62 -4
- package/dist/bin/intercom-check.js +949 -149
- package/dist/bin/pre-publish.js +14 -2
- package/dist/bin/scan-tasks.js +827 -27
- package/dist/bin/setup.js +99 -14
- package/dist/bin/shard-migrate.js +62 -12
- package/dist/bin/stack-update.js +1 -1
- package/dist/bin/update.js +3 -3
- package/dist/gateway/index.js +586 -26
- package/dist/hooks/bug-report-worker.js +586 -26
- package/dist/hooks/codex-stop-task-finalizer.js +977 -143
- package/dist/hooks/commit-complete.js +827 -27
- package/dist/hooks/error-recall.js +62 -12
- package/dist/hooks/ingest.js +4579 -249
- package/dist/hooks/instructions-loaded.js +62 -12
- package/dist/hooks/notification.js +62 -12
- package/dist/hooks/post-compact.js +83 -16
- package/dist/hooks/post-tool-combined.js +83 -16
- package/dist/hooks/pre-compact.js +907 -107
- package/dist/hooks/pre-tool-use.js +98 -16
- package/dist/hooks/prompt-submit.js +596 -30
- package/dist/hooks/session-end.js +909 -112
- package/dist/hooks/session-start.js +112 -17
- package/dist/hooks/stop.js +82 -15
- package/dist/hooks/subagent-stop.js +83 -16
- package/dist/hooks/summary-worker.js +81 -8
- package/dist/index.js +595 -29
- package/dist/lib/agent-config.js +16 -1
- package/dist/lib/cloud-sync.js +45 -1
- package/dist/lib/consolidation.js +16 -1
- package/dist/lib/database.js +23 -0
- package/dist/lib/db.js +23 -0
- package/dist/lib/device-registry.js +23 -0
- package/dist/lib/employee-templates.js +30 -4
- package/dist/lib/employees.js +16 -1
- package/dist/lib/exe-daemon.js +482 -52
- package/dist/lib/hybrid-search.js +62 -12
- package/dist/lib/license.js +3 -3
- package/dist/lib/messaging.js +21 -4
- package/dist/lib/schedules.js +37 -2
- package/dist/lib/skill-learning.js +910 -41
- package/dist/lib/status-brief.js +14 -1
- package/dist/lib/store.js +62 -12
- package/dist/lib/tasks.js +843 -93
- package/dist/lib/tmux-routing.js +766 -16
- package/dist/mcp/server.js +238 -41
- package/dist/mcp/tools/create-task.js +525 -15
- package/dist/mcp/tools/deactivate-behavior.js +33 -24
- package/dist/mcp/tools/list-tasks.js +21 -4
- package/dist/mcp/tools/send-message.js +21 -4
- package/dist/mcp/tools/update-task.js +840 -93
- package/dist/runtime/index.js +913 -107
- package/dist/tui/App.js +227 -58
- package/package.json +1 -1
|
@@ -395,11 +395,168 @@ var init_config = __esm({
|
|
|
395
395
|
}
|
|
396
396
|
});
|
|
397
397
|
|
|
398
|
+
// src/lib/runtime-table.ts
|
|
399
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
400
|
+
var init_runtime_table = __esm({
|
|
401
|
+
"src/lib/runtime-table.ts"() {
|
|
402
|
+
"use strict";
|
|
403
|
+
RUNTIME_TABLE = {
|
|
404
|
+
codex: {
|
|
405
|
+
binary: "codex",
|
|
406
|
+
launchMode: "interactive",
|
|
407
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
408
|
+
inlineFlag: "--no-alt-screen",
|
|
409
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
410
|
+
defaultModel: "gpt-5.5"
|
|
411
|
+
},
|
|
412
|
+
opencode: {
|
|
413
|
+
binary: "opencode",
|
|
414
|
+
launchMode: "exec",
|
|
415
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
416
|
+
inlineFlag: "",
|
|
417
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
418
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
DEFAULT_RUNTIME = "claude";
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
// src/lib/agent-config.ts
|
|
426
|
+
var agent_config_exports = {};
|
|
427
|
+
__export(agent_config_exports, {
|
|
428
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
429
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
430
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
431
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
432
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
433
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
434
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
435
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
436
|
+
setAgentMcps: () => setAgentMcps,
|
|
437
|
+
setAgentRuntime: () => setAgentRuntime
|
|
438
|
+
});
|
|
439
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
440
|
+
import path2 from "path";
|
|
441
|
+
function loadAgentConfig() {
|
|
442
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
443
|
+
try {
|
|
444
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
445
|
+
} catch {
|
|
446
|
+
return {};
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function saveAgentConfig(config) {
|
|
450
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
451
|
+
ensurePrivateDirSync(dir);
|
|
452
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
453
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
454
|
+
}
|
|
455
|
+
function getAgentRuntime(agentId) {
|
|
456
|
+
const config = loadAgentConfig();
|
|
457
|
+
const entry = config[agentId];
|
|
458
|
+
if (entry) return entry;
|
|
459
|
+
const orgDefault = config["default"];
|
|
460
|
+
if (orgDefault) return orgDefault;
|
|
461
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
462
|
+
}
|
|
463
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
464
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
465
|
+
if (!knownModels) {
|
|
466
|
+
return {
|
|
467
|
+
ok: false,
|
|
468
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
if (!knownModels.includes(model)) {
|
|
472
|
+
return {
|
|
473
|
+
ok: false,
|
|
474
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
const config = loadAgentConfig();
|
|
478
|
+
const existing = config[agentId];
|
|
479
|
+
const entry = { runtime, model };
|
|
480
|
+
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
481
|
+
if (mcps !== void 0) {
|
|
482
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
483
|
+
} else if (existing?.mcps) {
|
|
484
|
+
entry.mcps = existing.mcps;
|
|
485
|
+
}
|
|
486
|
+
config[agentId] = entry;
|
|
487
|
+
saveAgentConfig(config);
|
|
488
|
+
return { ok: true };
|
|
489
|
+
}
|
|
490
|
+
function setAgentMcps(agentId, mcps) {
|
|
491
|
+
const config = loadAgentConfig();
|
|
492
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
493
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
494
|
+
config[agentId] = existing;
|
|
495
|
+
saveAgentConfig(config);
|
|
496
|
+
return { ok: true };
|
|
497
|
+
}
|
|
498
|
+
function clearAgentRuntime(agentId) {
|
|
499
|
+
const config = loadAgentConfig();
|
|
500
|
+
delete config[agentId];
|
|
501
|
+
saveAgentConfig(config);
|
|
502
|
+
}
|
|
503
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
504
|
+
var init_agent_config = __esm({
|
|
505
|
+
"src/lib/agent-config.ts"() {
|
|
506
|
+
"use strict";
|
|
507
|
+
init_config();
|
|
508
|
+
init_runtime_table();
|
|
509
|
+
init_secure_files();
|
|
510
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
511
|
+
KNOWN_RUNTIMES = {
|
|
512
|
+
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
513
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
514
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
515
|
+
};
|
|
516
|
+
RUNTIME_LABELS = {
|
|
517
|
+
claude: "Claude Code (Anthropic)",
|
|
518
|
+
codex: "Codex (OpenAI)",
|
|
519
|
+
opencode: "OpenCode (open source)"
|
|
520
|
+
};
|
|
521
|
+
DEFAULT_MODELS = {
|
|
522
|
+
claude: "claude-opus-4.6",
|
|
523
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
524
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
|
|
398
529
|
// src/lib/employees.ts
|
|
530
|
+
var employees_exports = {};
|
|
531
|
+
__export(employees_exports, {
|
|
532
|
+
COORDINATOR_ROLE: () => COORDINATOR_ROLE,
|
|
533
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
534
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
535
|
+
addEmployee: () => addEmployee,
|
|
536
|
+
baseAgentName: () => baseAgentName,
|
|
537
|
+
canCoordinate: () => canCoordinate,
|
|
538
|
+
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
539
|
+
getCoordinatorName: () => getCoordinatorName,
|
|
540
|
+
getEmployee: () => getEmployee,
|
|
541
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
542
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
543
|
+
hasRole: () => hasRole,
|
|
544
|
+
hireEmployee: () => hireEmployee,
|
|
545
|
+
isCoordinatorName: () => isCoordinatorName,
|
|
546
|
+
isCoordinatorRole: () => isCoordinatorRole,
|
|
547
|
+
isMultiInstance: () => isMultiInstance,
|
|
548
|
+
loadEmployees: () => loadEmployees,
|
|
549
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
550
|
+
normalizeRole: () => normalizeRole,
|
|
551
|
+
normalizeRosterCase: () => normalizeRosterCase,
|
|
552
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
553
|
+
saveEmployees: () => saveEmployees,
|
|
554
|
+
validateEmployeeName: () => validateEmployeeName
|
|
555
|
+
});
|
|
399
556
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
400
|
-
import { existsSync as
|
|
557
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
401
558
|
import { execSync } from "child_process";
|
|
402
|
-
import
|
|
559
|
+
import path3 from "path";
|
|
403
560
|
import os2 from "os";
|
|
404
561
|
function normalizeRole(role) {
|
|
405
562
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -413,10 +570,47 @@ function getCoordinatorEmployee(employees) {
|
|
|
413
570
|
function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
414
571
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
415
572
|
}
|
|
573
|
+
function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
574
|
+
if (!agentName) return false;
|
|
575
|
+
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
576
|
+
}
|
|
577
|
+
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
578
|
+
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
579
|
+
}
|
|
580
|
+
function validateEmployeeName(name) {
|
|
581
|
+
if (!name) {
|
|
582
|
+
return { valid: false, error: "Name is required" };
|
|
583
|
+
}
|
|
584
|
+
if (name.length > 32) {
|
|
585
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
586
|
+
}
|
|
587
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
588
|
+
return {
|
|
589
|
+
valid: false,
|
|
590
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
return { valid: true };
|
|
594
|
+
}
|
|
595
|
+
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
596
|
+
if (!existsSync4(employeesPath)) {
|
|
597
|
+
return [];
|
|
598
|
+
}
|
|
599
|
+
const raw = await readFile2(employeesPath, "utf-8");
|
|
600
|
+
try {
|
|
601
|
+
return JSON.parse(raw);
|
|
602
|
+
} catch {
|
|
603
|
+
return [];
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
607
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
608
|
+
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
609
|
+
}
|
|
416
610
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
417
|
-
if (!
|
|
611
|
+
if (!existsSync4(employeesPath)) return [];
|
|
418
612
|
try {
|
|
419
|
-
return JSON.parse(
|
|
613
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
420
614
|
} catch {
|
|
421
615
|
return [];
|
|
422
616
|
}
|
|
@@ -424,6 +618,19 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
|
424
618
|
function getEmployee(employees, name) {
|
|
425
619
|
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
426
620
|
}
|
|
621
|
+
function getEmployeeByRole(employees, role) {
|
|
622
|
+
const lower = role.toLowerCase();
|
|
623
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
624
|
+
}
|
|
625
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
626
|
+
const lower = role.toLowerCase();
|
|
627
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
628
|
+
}
|
|
629
|
+
function hasRole(agentName, role) {
|
|
630
|
+
const employees = loadEmployeesSync();
|
|
631
|
+
const emp = getEmployee(employees, agentName);
|
|
632
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
633
|
+
}
|
|
427
634
|
function baseAgentName(name, employees) {
|
|
428
635
|
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
429
636
|
if (!match) return name;
|
|
@@ -432,21 +639,153 @@ function baseAgentName(name, employees) {
|
|
|
432
639
|
if (getEmployee(roster, base)) return base;
|
|
433
640
|
return name;
|
|
434
641
|
}
|
|
435
|
-
|
|
642
|
+
function isMultiInstance(agentName, employees) {
|
|
643
|
+
const roster = employees ?? loadEmployeesSync();
|
|
644
|
+
const emp = getEmployee(roster, agentName);
|
|
645
|
+
if (!emp) return false;
|
|
646
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
647
|
+
}
|
|
648
|
+
function addEmployee(employees, employee) {
|
|
649
|
+
const { systemPrompt: _legacyPrompt, ...rest } = employee;
|
|
650
|
+
const normalized = { ...rest, name: employee.name.toLowerCase() };
|
|
651
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
652
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
653
|
+
}
|
|
654
|
+
return [...employees, normalized];
|
|
655
|
+
}
|
|
656
|
+
function appendToCoordinatorTeam(employee) {
|
|
657
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
658
|
+
if (!coordinator) return;
|
|
659
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
660
|
+
if (!existsSync4(idPath)) return;
|
|
661
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
662
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
663
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
664
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
665
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
666
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
667
|
+
const entry = `
|
|
668
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
669
|
+
`;
|
|
670
|
+
let updated;
|
|
671
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
672
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
673
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
674
|
+
} else {
|
|
675
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
676
|
+
}
|
|
677
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
678
|
+
}
|
|
679
|
+
function capitalize(s) {
|
|
680
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
681
|
+
}
|
|
682
|
+
async function hireEmployee(employee) {
|
|
683
|
+
const employees = await loadEmployees();
|
|
684
|
+
const updated = addEmployee(employees, employee);
|
|
685
|
+
await saveEmployees(updated);
|
|
686
|
+
try {
|
|
687
|
+
appendToCoordinatorTeam(employee);
|
|
688
|
+
} catch {
|
|
689
|
+
}
|
|
690
|
+
try {
|
|
691
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
692
|
+
const config = loadAgentConfig2();
|
|
693
|
+
const name = employee.name.toLowerCase();
|
|
694
|
+
if (!config[name] && config["default"]) {
|
|
695
|
+
config[name] = { ...config["default"] };
|
|
696
|
+
saveAgentConfig2(config);
|
|
697
|
+
}
|
|
698
|
+
} catch {
|
|
699
|
+
}
|
|
700
|
+
return updated;
|
|
701
|
+
}
|
|
702
|
+
async function normalizeRosterCase(rosterPath) {
|
|
703
|
+
const employees = await loadEmployees(rosterPath);
|
|
704
|
+
let changed = false;
|
|
705
|
+
for (const emp of employees) {
|
|
706
|
+
if (emp.name !== emp.name.toLowerCase()) {
|
|
707
|
+
const oldName = emp.name;
|
|
708
|
+
emp.name = emp.name.toLowerCase();
|
|
709
|
+
changed = true;
|
|
710
|
+
try {
|
|
711
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
712
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
713
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
714
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
715
|
+
renameSync2(oldPath, newPath);
|
|
716
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
717
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
718
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
719
|
+
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
720
|
+
unlinkSync(oldPath);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
} catch {
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (changed) {
|
|
728
|
+
await saveEmployees(employees, rosterPath);
|
|
729
|
+
}
|
|
730
|
+
return changed;
|
|
731
|
+
}
|
|
732
|
+
function findExeBin() {
|
|
733
|
+
try {
|
|
734
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
735
|
+
} catch {
|
|
736
|
+
return null;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
function registerBinSymlinks(name) {
|
|
740
|
+
const created = [];
|
|
741
|
+
const skipped = [];
|
|
742
|
+
const errors = [];
|
|
743
|
+
const exeBinPath = findExeBin();
|
|
744
|
+
if (!exeBinPath) {
|
|
745
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
746
|
+
return { created, skipped, errors };
|
|
747
|
+
}
|
|
748
|
+
const binDir = path3.dirname(exeBinPath);
|
|
749
|
+
let target;
|
|
750
|
+
try {
|
|
751
|
+
target = readlinkSync(exeBinPath);
|
|
752
|
+
} catch {
|
|
753
|
+
errors.push("Could not read 'exe' symlink");
|
|
754
|
+
return { created, skipped, errors };
|
|
755
|
+
}
|
|
756
|
+
for (const suffix of ["", "-opencode"]) {
|
|
757
|
+
const linkName = `${name}${suffix}`;
|
|
758
|
+
const linkPath = path3.join(binDir, linkName);
|
|
759
|
+
if (existsSync4(linkPath)) {
|
|
760
|
+
skipped.push(linkName);
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
try {
|
|
764
|
+
symlinkSync(target, linkPath);
|
|
765
|
+
created.push(linkName);
|
|
766
|
+
} catch (err) {
|
|
767
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
return { created, skipped, errors };
|
|
771
|
+
}
|
|
772
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
436
773
|
var init_employees = __esm({
|
|
437
774
|
"src/lib/employees.ts"() {
|
|
438
775
|
"use strict";
|
|
439
776
|
init_config();
|
|
440
|
-
EMPLOYEES_PATH =
|
|
777
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
441
778
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
442
779
|
COORDINATOR_ROLE = "COO";
|
|
443
|
-
|
|
780
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
781
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
782
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
444
783
|
}
|
|
445
784
|
});
|
|
446
785
|
|
|
447
786
|
// src/lib/database-adapter.ts
|
|
448
787
|
import os3 from "os";
|
|
449
|
-
import
|
|
788
|
+
import path4 from "path";
|
|
450
789
|
import { createRequire } from "module";
|
|
451
790
|
import { pathToFileURL } from "url";
|
|
452
791
|
function quotedIdentifier(identifier) {
|
|
@@ -757,8 +1096,8 @@ async function loadPrismaClient() {
|
|
|
757
1096
|
}
|
|
758
1097
|
return new PrismaClient2();
|
|
759
1098
|
}
|
|
760
|
-
const exeDbRoot = process.env.EXE_DB_ROOT ??
|
|
761
|
-
const requireFromExeDb = createRequire(
|
|
1099
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
|
|
1100
|
+
const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
|
|
762
1101
|
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
763
1102
|
const module = await import(pathToFileURL(prismaEntry).href);
|
|
764
1103
|
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
@@ -1030,8 +1369,8 @@ var init_database_adapter = __esm({
|
|
|
1030
1369
|
|
|
1031
1370
|
// src/lib/daemon-auth.ts
|
|
1032
1371
|
import crypto from "crypto";
|
|
1033
|
-
import
|
|
1034
|
-
import { existsSync as
|
|
1372
|
+
import path5 from "path";
|
|
1373
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
1035
1374
|
function normalizeToken(token) {
|
|
1036
1375
|
if (!token) return null;
|
|
1037
1376
|
const trimmed = token.trim();
|
|
@@ -1039,8 +1378,8 @@ function normalizeToken(token) {
|
|
|
1039
1378
|
}
|
|
1040
1379
|
function readDaemonToken() {
|
|
1041
1380
|
try {
|
|
1042
|
-
if (!
|
|
1043
|
-
return normalizeToken(
|
|
1381
|
+
if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
|
|
1382
|
+
return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
|
|
1044
1383
|
} catch {
|
|
1045
1384
|
return null;
|
|
1046
1385
|
}
|
|
@@ -1050,7 +1389,7 @@ function ensureDaemonToken(seed) {
|
|
|
1050
1389
|
if (existing) return existing;
|
|
1051
1390
|
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1052
1391
|
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1053
|
-
|
|
1392
|
+
writeFileSync3(DAEMON_TOKEN_PATH, `${token}
|
|
1054
1393
|
`, "utf8");
|
|
1055
1394
|
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1056
1395
|
return token;
|
|
@@ -1061,7 +1400,7 @@ var init_daemon_auth = __esm({
|
|
|
1061
1400
|
"use strict";
|
|
1062
1401
|
init_config();
|
|
1063
1402
|
init_secure_files();
|
|
1064
|
-
DAEMON_TOKEN_PATH =
|
|
1403
|
+
DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
|
|
1065
1404
|
}
|
|
1066
1405
|
});
|
|
1067
1406
|
|
|
@@ -1070,8 +1409,8 @@ import net from "net";
|
|
|
1070
1409
|
import os4 from "os";
|
|
1071
1410
|
import { spawn, execSync as execSync2 } from "child_process";
|
|
1072
1411
|
import { randomUUID } from "crypto";
|
|
1073
|
-
import { existsSync as
|
|
1074
|
-
import
|
|
1412
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
|
|
1413
|
+
import path6 from "path";
|
|
1075
1414
|
import { fileURLToPath } from "url";
|
|
1076
1415
|
function handleData(chunk) {
|
|
1077
1416
|
_buffer += chunk.toString();
|
|
@@ -1107,9 +1446,9 @@ function isZombie(pid) {
|
|
|
1107
1446
|
}
|
|
1108
1447
|
}
|
|
1109
1448
|
function cleanupStaleFiles() {
|
|
1110
|
-
if (
|
|
1449
|
+
if (existsSync6(PID_PATH)) {
|
|
1111
1450
|
try {
|
|
1112
|
-
const pid = parseInt(
|
|
1451
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1113
1452
|
if (pid > 0) {
|
|
1114
1453
|
try {
|
|
1115
1454
|
process.kill(pid, 0);
|
|
@@ -1134,11 +1473,11 @@ function cleanupStaleFiles() {
|
|
|
1134
1473
|
}
|
|
1135
1474
|
}
|
|
1136
1475
|
function findPackageRoot() {
|
|
1137
|
-
let dir =
|
|
1138
|
-
const { root } =
|
|
1476
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
1477
|
+
const { root } = path6.parse(dir);
|
|
1139
1478
|
while (dir !== root) {
|
|
1140
|
-
if (
|
|
1141
|
-
dir =
|
|
1479
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
1480
|
+
dir = path6.dirname(dir);
|
|
1142
1481
|
}
|
|
1143
1482
|
return null;
|
|
1144
1483
|
}
|
|
@@ -1156,8 +1495,8 @@ function spawnDaemon() {
|
|
|
1156
1495
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1157
1496
|
return;
|
|
1158
1497
|
}
|
|
1159
|
-
const daemonPath =
|
|
1160
|
-
if (!
|
|
1498
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1499
|
+
if (!existsSync6(daemonPath)) {
|
|
1161
1500
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1162
1501
|
`);
|
|
1163
1502
|
return;
|
|
@@ -1166,7 +1505,7 @@ function spawnDaemon() {
|
|
|
1166
1505
|
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1167
1506
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1168
1507
|
`);
|
|
1169
|
-
const logPath =
|
|
1508
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
1170
1509
|
let stderrFd = "ignore";
|
|
1171
1510
|
try {
|
|
1172
1511
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1316,9 +1655,9 @@ var init_exe_daemon_client = __esm({
|
|
|
1316
1655
|
"use strict";
|
|
1317
1656
|
init_config();
|
|
1318
1657
|
init_daemon_auth();
|
|
1319
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
1320
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
1321
|
-
SPAWN_LOCK_PATH =
|
|
1658
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
1659
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
1660
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
1322
1661
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
1323
1662
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
1324
1663
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -1545,7 +1884,7 @@ __export(database_exports, {
|
|
|
1545
1884
|
isInitialized: () => isInitialized,
|
|
1546
1885
|
setExternalClient: () => setExternalClient
|
|
1547
1886
|
});
|
|
1548
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
1887
|
+
import { chmodSync as chmodSync2, existsSync as existsSync7, statSync as statSync2, copyFileSync, unlinkSync as unlinkSync3, openSync as openSync2, closeSync as closeSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
1549
1888
|
import { createClient } from "@libsql/client";
|
|
1550
1889
|
import { homedir } from "os";
|
|
1551
1890
|
import { join } from "path";
|
|
@@ -1598,11 +1937,11 @@ function releaseDbLock() {
|
|
|
1598
1937
|
}
|
|
1599
1938
|
async function initDatabase(config) {
|
|
1600
1939
|
acquireDbLock();
|
|
1601
|
-
if (
|
|
1940
|
+
if (existsSync7(config.dbPath)) {
|
|
1602
1941
|
const dbStat = statSync2(config.dbPath);
|
|
1603
1942
|
if (dbStat.size === 0) {
|
|
1604
1943
|
const walPath = config.dbPath + "-wal";
|
|
1605
|
-
if (
|
|
1944
|
+
if (existsSync7(walPath) && statSync2(walPath).size > 0) {
|
|
1606
1945
|
const backupPath = config.dbPath + ".zeroed-" + Date.now();
|
|
1607
1946
|
copyFileSync(config.dbPath, backupPath);
|
|
1608
1947
|
unlinkSync3(config.dbPath);
|
|
@@ -1898,6 +2237,13 @@ async function ensureSchema() {
|
|
|
1898
2237
|
} catch (e) {
|
|
1899
2238
|
logCatchDebug("migration", e);
|
|
1900
2239
|
}
|
|
2240
|
+
for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
|
|
2241
|
+
try {
|
|
2242
|
+
await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
|
|
2243
|
+
} catch (e) {
|
|
2244
|
+
logCatchDebug("migration", e);
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
1901
2247
|
try {
|
|
1902
2248
|
await client.execute({
|
|
1903
2249
|
sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
|
|
@@ -3114,6 +3460,22 @@ async function ensureSchema() {
|
|
|
3114
3460
|
} catch (e) {
|
|
3115
3461
|
logCatchDebug("migration", e);
|
|
3116
3462
|
}
|
|
3463
|
+
try {
|
|
3464
|
+
await client.execute({
|
|
3465
|
+
sql: `ALTER TABLE memories ADD COLUMN visibility TEXT DEFAULT 'private'`,
|
|
3466
|
+
args: []
|
|
3467
|
+
});
|
|
3468
|
+
} catch (e) {
|
|
3469
|
+
logCatchDebug("migration", e);
|
|
3470
|
+
}
|
|
3471
|
+
try {
|
|
3472
|
+
await client.execute({
|
|
3473
|
+
sql: `ALTER TABLE memories ADD COLUMN strength REAL DEFAULT 1.0`,
|
|
3474
|
+
args: []
|
|
3475
|
+
});
|
|
3476
|
+
} catch (e) {
|
|
3477
|
+
logCatchDebug("migration", e);
|
|
3478
|
+
}
|
|
3117
3479
|
}
|
|
3118
3480
|
async function disposeDatabase() {
|
|
3119
3481
|
if (_walCheckpointTimer) {
|
|
@@ -3179,14 +3541,14 @@ __export(shard_manager_exports, {
|
|
|
3179
3541
|
listShards: () => listShards,
|
|
3180
3542
|
shardExists: () => shardExists
|
|
3181
3543
|
});
|
|
3182
|
-
import
|
|
3183
|
-
import { existsSync as
|
|
3544
|
+
import path8 from "path";
|
|
3545
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readdirSync, renameSync as renameSync3, statSync as statSync4 } from "fs";
|
|
3184
3546
|
import { createClient as createClient2 } from "@libsql/client";
|
|
3185
3547
|
function initShardManager(encryptionKey) {
|
|
3186
3548
|
_encryptionKey = encryptionKey;
|
|
3187
3549
|
_keyValidated = false;
|
|
3188
3550
|
_keyValidationPromise = null;
|
|
3189
|
-
if (!
|
|
3551
|
+
if (!existsSync9(SHARDS_DIR)) {
|
|
3190
3552
|
mkdirSync3(SHARDS_DIR, { recursive: true });
|
|
3191
3553
|
}
|
|
3192
3554
|
const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
|
|
@@ -3207,7 +3569,7 @@ async function validateEncryptionKey() {
|
|
|
3207
3569
|
return true;
|
|
3208
3570
|
}
|
|
3209
3571
|
for (const shardFile of existingShards.slice(0, 3)) {
|
|
3210
|
-
const dbPath =
|
|
3572
|
+
const dbPath = path8.join(SHARDS_DIR, shardFile);
|
|
3211
3573
|
const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
|
|
3212
3574
|
try {
|
|
3213
3575
|
await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
|
|
@@ -3250,7 +3612,7 @@ function getShardClient(projectName) {
|
|
|
3250
3612
|
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
3251
3613
|
evictLRU();
|
|
3252
3614
|
}
|
|
3253
|
-
const dbPath =
|
|
3615
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
3254
3616
|
const client = createClient2({
|
|
3255
3617
|
url: `file:${dbPath}`,
|
|
3256
3618
|
encryptionKey: _encryptionKey
|
|
@@ -3261,13 +3623,13 @@ function getShardClient(projectName) {
|
|
|
3261
3623
|
}
|
|
3262
3624
|
function shardExists(projectName) {
|
|
3263
3625
|
const safeName = safeShardName(projectName);
|
|
3264
|
-
return
|
|
3626
|
+
return existsSync9(path8.join(SHARDS_DIR, `${safeName}.db`));
|
|
3265
3627
|
}
|
|
3266
3628
|
function safeShardName(projectName) {
|
|
3267
3629
|
return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
3268
3630
|
}
|
|
3269
3631
|
function listShards() {
|
|
3270
|
-
if (!
|
|
3632
|
+
if (!existsSync9(SHARDS_DIR)) return [];
|
|
3271
3633
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3272
3634
|
}
|
|
3273
3635
|
async function auditShardHealth(options = {}) {
|
|
@@ -3279,7 +3641,7 @@ async function auditShardHealth(options = {}) {
|
|
|
3279
3641
|
const names = listShards();
|
|
3280
3642
|
const shards = [];
|
|
3281
3643
|
for (const name of names) {
|
|
3282
|
-
const dbPath =
|
|
3644
|
+
const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
|
|
3283
3645
|
const stat = statSync4(dbPath);
|
|
3284
3646
|
const item = {
|
|
3285
3647
|
name,
|
|
@@ -3314,7 +3676,7 @@ async function auditShardHealth(options = {}) {
|
|
|
3314
3676
|
_shards.delete(name);
|
|
3315
3677
|
_shardLastAccess.delete(name);
|
|
3316
3678
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3317
|
-
const archivedPath =
|
|
3679
|
+
const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3318
3680
|
renameSync3(dbPath, archivedPath);
|
|
3319
3681
|
item.archivedPath = archivedPath;
|
|
3320
3682
|
}
|
|
@@ -3542,11 +3904,11 @@ async function getReadyShardClient(projectName) {
|
|
|
3542
3904
|
client.close();
|
|
3543
3905
|
_shards.delete(safeName);
|
|
3544
3906
|
_shardLastAccess.delete(safeName);
|
|
3545
|
-
const dbPath =
|
|
3546
|
-
if (
|
|
3907
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
3908
|
+
if (existsSync9(dbPath)) {
|
|
3547
3909
|
const stat = statSync4(dbPath);
|
|
3548
3910
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3549
|
-
const archivedPath =
|
|
3911
|
+
const archivedPath = path8.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
|
|
3550
3912
|
renameSync3(dbPath, archivedPath);
|
|
3551
3913
|
process.stderr.write(
|
|
3552
3914
|
`[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
|
|
@@ -3614,7 +3976,7 @@ var init_shard_manager = __esm({
|
|
|
3614
3976
|
"src/lib/shard-manager.ts"() {
|
|
3615
3977
|
"use strict";
|
|
3616
3978
|
init_config();
|
|
3617
|
-
SHARDS_DIR =
|
|
3979
|
+
SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
|
|
3618
3980
|
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
3619
3981
|
MAX_OPEN_SHARDS = 10;
|
|
3620
3982
|
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
@@ -3680,11 +4042,17 @@ var init_platform_procedures = __esm({
|
|
|
3680
4042
|
content: "Founder -> coordinator (the executive agent, internally routed as 'COO') -> CTO/CMO. CTO -> engineers. CMO -> content production. Never skip levels: the coordinator does not bypass managers for specialist work. Specialists report to their manager. If you need cross-team info, use ask_team_memory \u2014 don't read other agents' task folders. Each level owns dispatch downward and review upward."
|
|
3681
4043
|
},
|
|
3682
4044
|
{
|
|
3683
|
-
title: "
|
|
4045
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
3684
4046
|
domain: "workflow",
|
|
3685
4047
|
priority: "p1",
|
|
3686
4048
|
content: "New customers start best in Phase 1: founder \u2194 coordinator/Chief of Staff, building company context. Suggest Phase 2 executives when domain work repeats; suggest Phase 3 parallel execution only when review/permission gates are ready. This is guidance, not a blocker: users may jump phases anytime. Never overwrite their phase, role titles, identities, or custom org design."
|
|
3687
4049
|
},
|
|
4050
|
+
{
|
|
4051
|
+
title: "Routing slot vs display title \u2014 internal 'coo' is plumbing, not your name",
|
|
4052
|
+
domain: "identity",
|
|
4053
|
+
priority: "p0",
|
|
4054
|
+
content: "These procedures reference 'COO' as a shorthand for the coordinator role. This is an INTERNAL routing slot used by exe-os code (chain-of-command checks, dispatch logic, session detection). It is NOT your display title. Your actual title comes from your identity file's `title:` field \u2014 that is what you use externally: introductions, sign-offs, team comms, and any user-facing text. If your identity says `title: AI Chief of Staff`, you are the AI Chief of Staff. The routing slot stays `role: coo` for code compatibility \u2014 never rename it, but also never introduce yourself as 'COO' unless your identity file explicitly says so. The founder chose your title; respect it."
|
|
4055
|
+
},
|
|
3688
4056
|
{
|
|
3689
4057
|
title: "Single dispatch path \u2014 create_task only",
|
|
3690
4058
|
domain: "workflow",
|
|
@@ -3718,6 +4086,12 @@ var init_platform_procedures = __esm({
|
|
|
3718
4086
|
priority: "p0",
|
|
3719
4087
|
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."
|
|
3720
4088
|
},
|
|
4089
|
+
{
|
|
4090
|
+
title: "Destructive operations \u2014 mandatory reviewer gate",
|
|
4091
|
+
domain: "security",
|
|
4092
|
+
priority: "p0",
|
|
4093
|
+
content: "Before ANY destructive operation (delete, remove, overwrite, drop, reset, force-push, truncate), you MUST: (1) Have your full task spec accessible \u2014 if you cannot read it, STOP and report to your reviewer. Never improvise destructive actions. (2) Confirm with your reviewer (assigned_by or COO) before executing. (3) If the task spec explicitly authorizes the operation, proceed \u2014 but log it. Violation = immediate task failure. This applies to ALL agents regardless of role."
|
|
4094
|
+
},
|
|
3721
4095
|
{
|
|
3722
4096
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
3723
4097
|
domain: "support",
|
|
@@ -3869,7 +4243,7 @@ var init_platform_procedures = __esm({
|
|
|
3869
4243
|
title: "MCP tool dispatch \u2014 all tools use action parameter",
|
|
3870
4244
|
domain: "tool-use",
|
|
3871
4245
|
priority: "p0",
|
|
3872
|
-
content: 'exe-os MCP tools
|
|
4246
|
+
content: 'exe-os MCP tools use consolidated action-based dispatch by default (19 tools). Call domain tools with an action parameter: memory(action="recall"), task(action="create"), config(action="list_employees"), etc. Legacy mode (108 separate tools like recall_my_memory, create_task) is still available via EXE_MCP_TOOL_SURFACE=legacy but will be removed in a future version. If you see specific tool names, call them directly \u2014 both surfaces are identical. Consolidated is the default and recommended surface.'
|
|
3873
4247
|
},
|
|
3874
4248
|
{
|
|
3875
4249
|
title: "MCP tools \u2014 memory, decision, and search",
|
|
@@ -4003,10 +4377,24 @@ function stableId(memoryId, type, content) {
|
|
|
4003
4377
|
return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
|
|
4004
4378
|
}
|
|
4005
4379
|
function cleanText(text) {
|
|
4006
|
-
|
|
4380
|
+
let cleaned = text.replace(
|
|
4381
|
+
/```(\w*)\n(.*?)(?:\n[\s\S]*?)```/g,
|
|
4382
|
+
(_m, lang, firstLine) => `[code${lang ? `:${lang}` : ""}] ${firstLine.trim()}`
|
|
4383
|
+
);
|
|
4384
|
+
cleaned = cleaned.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
4385
|
+
return cleaned;
|
|
4007
4386
|
}
|
|
4008
|
-
function
|
|
4009
|
-
|
|
4387
|
+
function splitSegments(text) {
|
|
4388
|
+
const cleaned = cleanText(text);
|
|
4389
|
+
const segments = cleaned.split(/(?<=[.!?:;])\s+|\n{2,}|(?<=\))\s+(?=[A-Z])|\s*[|│]\s*/).map((s) => s.trim()).filter((s) => s.length >= MIN_SEGMENT_CHARS && s.length <= MAX_SEGMENT_CHARS);
|
|
4390
|
+
if (segments.length === 0 && cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
4391
|
+
const lines = cleaned.split(/\n+/).map((l) => l.trim()).filter((l) => l.length >= MIN_SEGMENT_CHARS && l.length <= MAX_SEGMENT_CHARS);
|
|
4392
|
+
if (lines.length > 0) return lines;
|
|
4393
|
+
if (cleaned.length >= MIN_SEGMENT_CHARS) {
|
|
4394
|
+
return [cleaned.slice(0, MAX_SEGMENT_CHARS)];
|
|
4395
|
+
}
|
|
4396
|
+
}
|
|
4397
|
+
return segments;
|
|
4010
4398
|
}
|
|
4011
4399
|
function inferCardType(sentence, toolName) {
|
|
4012
4400
|
const lower = sentence.toLowerCase();
|
|
@@ -4038,12 +4426,12 @@ function predicateFor(type) {
|
|
|
4038
4426
|
}
|
|
4039
4427
|
}
|
|
4040
4428
|
function extractMemoryCards(row) {
|
|
4041
|
-
const
|
|
4429
|
+
const segments = splitSegments(row.raw_text);
|
|
4042
4430
|
const cards = [];
|
|
4043
|
-
for (const sentence of
|
|
4431
|
+
for (const sentence of segments) {
|
|
4044
4432
|
const type = inferCardType(sentence, row.tool_name);
|
|
4045
4433
|
const subject = extractSubject(sentence, row.agent_id);
|
|
4046
|
-
const content = sentence.length >
|
|
4434
|
+
const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
|
|
4047
4435
|
cards.push({
|
|
4048
4436
|
id: stableId(row.id, type, content),
|
|
4049
4437
|
memory_id: row.id,
|
|
@@ -4139,13 +4527,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
|
4139
4527
|
last_accessed: String(row.timestamp)
|
|
4140
4528
|
}));
|
|
4141
4529
|
}
|
|
4142
|
-
var MAX_CARDS_PER_MEMORY,
|
|
4530
|
+
var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
|
|
4143
4531
|
var init_memory_cards = __esm({
|
|
4144
4532
|
"src/lib/memory-cards.ts"() {
|
|
4145
4533
|
"use strict";
|
|
4146
4534
|
init_database();
|
|
4147
|
-
MAX_CARDS_PER_MEMORY =
|
|
4148
|
-
|
|
4535
|
+
MAX_CARDS_PER_MEMORY = 8;
|
|
4536
|
+
MAX_SEGMENT_CHARS = 500;
|
|
4537
|
+
MIN_SEGMENT_CHARS = 20;
|
|
4149
4538
|
}
|
|
4150
4539
|
});
|
|
4151
4540
|
|
|
@@ -4417,33 +4806,6 @@ var init_agentic_ontology = __esm({
|
|
|
4417
4806
|
}
|
|
4418
4807
|
});
|
|
4419
4808
|
|
|
4420
|
-
// src/lib/runtime-table.ts
|
|
4421
|
-
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
4422
|
-
var init_runtime_table = __esm({
|
|
4423
|
-
"src/lib/runtime-table.ts"() {
|
|
4424
|
-
"use strict";
|
|
4425
|
-
RUNTIME_TABLE = {
|
|
4426
|
-
codex: {
|
|
4427
|
-
binary: "codex",
|
|
4428
|
-
launchMode: "interactive",
|
|
4429
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
4430
|
-
inlineFlag: "--no-alt-screen",
|
|
4431
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
4432
|
-
defaultModel: "gpt-5.5"
|
|
4433
|
-
},
|
|
4434
|
-
opencode: {
|
|
4435
|
-
binary: "opencode",
|
|
4436
|
-
launchMode: "exec",
|
|
4437
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
4438
|
-
inlineFlag: "",
|
|
4439
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
4440
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
4441
|
-
}
|
|
4442
|
-
};
|
|
4443
|
-
DEFAULT_RUNTIME = "claude";
|
|
4444
|
-
}
|
|
4445
|
-
});
|
|
4446
|
-
|
|
4447
4809
|
// src/lib/session-key.ts
|
|
4448
4810
|
import { execSync as execSync4 } from "child_process";
|
|
4449
4811
|
function normalizeCommand(command) {
|
|
@@ -4535,9 +4897,9 @@ __export(active_agent_exports, {
|
|
|
4535
4897
|
resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
|
|
4536
4898
|
writeActiveAgent: () => writeActiveAgent
|
|
4537
4899
|
});
|
|
4538
|
-
import { readFileSync as
|
|
4900
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5, readdirSync as readdirSync3 } from "fs";
|
|
4539
4901
|
import { execSync as execSync5 } from "child_process";
|
|
4540
|
-
import
|
|
4902
|
+
import path10 from "path";
|
|
4541
4903
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
4542
4904
|
if (candidate === baseName) return true;
|
|
4543
4905
|
if (!candidate.startsWith(baseName)) return false;
|
|
@@ -4581,12 +4943,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
4581
4943
|
return null;
|
|
4582
4944
|
}
|
|
4583
4945
|
function getMarkerPath() {
|
|
4584
|
-
return
|
|
4946
|
+
return path10.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
4585
4947
|
}
|
|
4586
4948
|
function writeActiveAgent(agentId, agentRole) {
|
|
4587
4949
|
try {
|
|
4588
4950
|
mkdirSync5(CACHE_DIR, { recursive: true });
|
|
4589
|
-
|
|
4951
|
+
writeFileSync5(
|
|
4590
4952
|
getMarkerPath(),
|
|
4591
4953
|
JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
4592
4954
|
);
|
|
@@ -4604,7 +4966,7 @@ function getActiveAgent() {
|
|
|
4604
4966
|
if (httpCtx) return httpCtx;
|
|
4605
4967
|
try {
|
|
4606
4968
|
const markerPath = getMarkerPath();
|
|
4607
|
-
const raw =
|
|
4969
|
+
const raw = readFileSync6(markerPath, "utf8");
|
|
4608
4970
|
const data = JSON.parse(raw);
|
|
4609
4971
|
if (data.agentId) {
|
|
4610
4972
|
if (data.startedAt) {
|
|
@@ -4652,14 +5014,14 @@ function getAllActiveAgents() {
|
|
|
4652
5014
|
const key = file.slice("active-agent-".length, -".json".length);
|
|
4653
5015
|
if (key === "undefined") continue;
|
|
4654
5016
|
try {
|
|
4655
|
-
const raw =
|
|
5017
|
+
const raw = readFileSync6(path10.join(CACHE_DIR, file), "utf8");
|
|
4656
5018
|
const data = JSON.parse(raw);
|
|
4657
5019
|
if (!data.agentId) continue;
|
|
4658
5020
|
if (data.startedAt) {
|
|
4659
5021
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
4660
5022
|
if (age > STALE_MS) {
|
|
4661
5023
|
try {
|
|
4662
|
-
unlinkSync5(
|
|
5024
|
+
unlinkSync5(path10.join(CACHE_DIR, file));
|
|
4663
5025
|
} catch {
|
|
4664
5026
|
}
|
|
4665
5027
|
continue;
|
|
@@ -4682,11 +5044,11 @@ function getAllActiveAgents() {
|
|
|
4682
5044
|
function cleanupSessionMarkers() {
|
|
4683
5045
|
const key = getSessionKey();
|
|
4684
5046
|
try {
|
|
4685
|
-
unlinkSync5(
|
|
5047
|
+
unlinkSync5(path10.join(CACHE_DIR, `active-agent-${key}.json`));
|
|
4686
5048
|
} catch {
|
|
4687
5049
|
}
|
|
4688
5050
|
try {
|
|
4689
|
-
unlinkSync5(
|
|
5051
|
+
unlinkSync5(path10.join(CACHE_DIR, "active-agent-undefined.json"));
|
|
4690
5052
|
} catch {
|
|
4691
5053
|
}
|
|
4692
5054
|
}
|
|
@@ -4698,16 +5060,16 @@ var init_active_agent = __esm({
|
|
|
4698
5060
|
init_session_key();
|
|
4699
5061
|
init_agent_context();
|
|
4700
5062
|
init_employees();
|
|
4701
|
-
CACHE_DIR =
|
|
5063
|
+
CACHE_DIR = path10.join(EXE_AI_DIR, "session-cache");
|
|
4702
5064
|
STALE_MS = 24 * 60 * 60 * 1e3;
|
|
4703
5065
|
}
|
|
4704
5066
|
});
|
|
4705
5067
|
|
|
4706
5068
|
// src/lib/agent-symlinks.ts
|
|
4707
5069
|
import os7 from "os";
|
|
4708
|
-
import
|
|
5070
|
+
import path11 from "path";
|
|
4709
5071
|
import {
|
|
4710
|
-
existsSync as
|
|
5072
|
+
existsSync as existsSync11,
|
|
4711
5073
|
lstatSync,
|
|
4712
5074
|
mkdirSync as mkdirSync6,
|
|
4713
5075
|
readlinkSync as readlinkSync2,
|
|
@@ -4735,14 +5097,14 @@ var init_mcp_prefix = __esm({
|
|
|
4735
5097
|
});
|
|
4736
5098
|
|
|
4737
5099
|
// src/lib/preferences.ts
|
|
4738
|
-
import { existsSync as
|
|
4739
|
-
import
|
|
5100
|
+
import { existsSync as existsSync12, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
5101
|
+
import path12 from "path";
|
|
4740
5102
|
import os8 from "os";
|
|
4741
5103
|
function loadPreferences(homeDir = os8.homedir()) {
|
|
4742
|
-
const configPath =
|
|
4743
|
-
if (!
|
|
5104
|
+
const configPath = path12.join(homeDir, ".exe-os", "config.json");
|
|
5105
|
+
if (!existsSync12(configPath)) return {};
|
|
4744
5106
|
try {
|
|
4745
|
-
const config = JSON.parse(
|
|
5107
|
+
const config = JSON.parse(readFileSync7(configPath, "utf-8"));
|
|
4746
5108
|
return config.preferences ?? {};
|
|
4747
5109
|
} catch {
|
|
4748
5110
|
return {};
|
|
@@ -4756,10 +5118,20 @@ var init_preferences = __esm({
|
|
|
4756
5118
|
});
|
|
4757
5119
|
|
|
4758
5120
|
// src/adapters/mcp-http-config.ts
|
|
4759
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
5121
|
+
import { chmodSync as chmodSync3, existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
4760
5122
|
import { randomBytes } from "crypto";
|
|
4761
|
-
import
|
|
5123
|
+
import path13 from "path";
|
|
4762
5124
|
import os9 from "os";
|
|
5125
|
+
function resolveDefaultAgentId() {
|
|
5126
|
+
if (process.env.AGENT_ID && process.env.AGENT_ID !== "default") return process.env.AGENT_ID;
|
|
5127
|
+
try {
|
|
5128
|
+
const { loadEmployeesSync: loadEmployeesSync2 } = (init_employees(), __toCommonJS(employees_exports));
|
|
5129
|
+
const coo = loadEmployeesSync2().find((e) => e.role === "COO");
|
|
5130
|
+
if (coo) return coo.name;
|
|
5131
|
+
} catch {
|
|
5132
|
+
}
|
|
5133
|
+
return "exe";
|
|
5134
|
+
}
|
|
4763
5135
|
function mcpHttpPort() {
|
|
4764
5136
|
return process.env.EXE_MCP_PORT || DEFAULT_MCP_HTTP_PORT;
|
|
4765
5137
|
}
|
|
@@ -4767,18 +5139,18 @@ function mcpHttpUrl() {
|
|
|
4767
5139
|
return `http://127.0.0.1:${mcpHttpPort()}/mcp`;
|
|
4768
5140
|
}
|
|
4769
5141
|
function readOrCreateDaemonToken(homeDir = os9.homedir()) {
|
|
4770
|
-
const exeDir =
|
|
4771
|
-
const tokenPath =
|
|
4772
|
-
if (
|
|
5142
|
+
const exeDir = path13.join(homeDir, ".exe-os");
|
|
5143
|
+
const tokenPath = path13.join(exeDir, "exed.token");
|
|
5144
|
+
if (existsSync13(tokenPath)) {
|
|
4773
5145
|
try {
|
|
4774
|
-
const token2 =
|
|
5146
|
+
const token2 = readFileSync8(tokenPath, "utf-8").trim();
|
|
4775
5147
|
if (/^[a-f0-9]{64}$/i.test(token2)) return token2;
|
|
4776
5148
|
} catch {
|
|
4777
5149
|
}
|
|
4778
5150
|
}
|
|
4779
5151
|
const token = randomBytes(32).toString("hex");
|
|
4780
5152
|
mkdirSync7(exeDir, { recursive: true });
|
|
4781
|
-
|
|
5153
|
+
writeFileSync7(tokenPath, `${token}
|
|
4782
5154
|
`, "utf-8");
|
|
4783
5155
|
try {
|
|
4784
5156
|
chmodSync3(tokenPath, 384);
|
|
@@ -4789,18 +5161,21 @@ function readOrCreateDaemonToken(homeDir = os9.homedir()) {
|
|
|
4789
5161
|
function buildMcpHttpHeaders(homeDir = os9.homedir(), opts = {}) {
|
|
4790
5162
|
const agentId = opts.useShellPlaceholders ? "${AGENT_ID:-exe}" : opts.agentId ?? DEFAULT_MCP_HTTP_AGENT_ID;
|
|
4791
5163
|
const agentRole = opts.useShellPlaceholders ? "${AGENT_ROLE:-COO}" : opts.agentRole ?? DEFAULT_MCP_HTTP_AGENT_ROLE;
|
|
4792
|
-
|
|
5164
|
+
const sessionName = opts.useShellPlaceholders ? "$(tmux display-message -p '#{session_name}' 2>/dev/null || echo '')" : process.env.EXE_SESSION_NAME ?? "";
|
|
5165
|
+
const headers = {
|
|
4793
5166
|
Authorization: `Bearer ${readOrCreateDaemonToken(homeDir)}`,
|
|
4794
5167
|
"X-Agent-Id": agentId,
|
|
4795
5168
|
"X-Agent-Role": agentRole
|
|
4796
5169
|
};
|
|
5170
|
+
if (sessionName) headers["X-Exe-Session"] = sessionName;
|
|
5171
|
+
return headers;
|
|
4797
5172
|
}
|
|
4798
5173
|
var DEFAULT_MCP_HTTP_PORT, DEFAULT_MCP_HTTP_AGENT_ID, DEFAULT_MCP_HTTP_AGENT_ROLE;
|
|
4799
5174
|
var init_mcp_http_config = __esm({
|
|
4800
5175
|
"src/adapters/mcp-http-config.ts"() {
|
|
4801
5176
|
"use strict";
|
|
4802
5177
|
DEFAULT_MCP_HTTP_PORT = "48739";
|
|
4803
|
-
DEFAULT_MCP_HTTP_AGENT_ID =
|
|
5178
|
+
DEFAULT_MCP_HTTP_AGENT_ID = resolveDefaultAgentId();
|
|
4804
5179
|
DEFAULT_MCP_HTTP_AGENT_ROLE = "COO";
|
|
4805
5180
|
}
|
|
4806
5181
|
});
|
|
@@ -4950,28 +5325,28 @@ var init_runtime_hook_manifest = __esm({
|
|
|
4950
5325
|
|
|
4951
5326
|
// src/adapters/claude/installer.ts
|
|
4952
5327
|
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir, rm } from "fs/promises";
|
|
4953
|
-
import { existsSync as
|
|
5328
|
+
import { existsSync as existsSync14, readFileSync as readFileSync9, writeFileSync as writeFileSync8, copyFileSync as copyFileSync2, mkdirSync as mkdirSync8 } from "fs";
|
|
4954
5329
|
import { createHash as createHash4 } from "crypto";
|
|
4955
|
-
import
|
|
5330
|
+
import path14 from "path";
|
|
4956
5331
|
import os10 from "os";
|
|
4957
5332
|
import { execSync as execSync6 } from "child_process";
|
|
4958
5333
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4959
5334
|
function resolvePackageRoot() {
|
|
4960
5335
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4961
|
-
let dir =
|
|
4962
|
-
const root =
|
|
5336
|
+
let dir = path14.dirname(thisFile);
|
|
5337
|
+
const root = path14.parse(dir).root;
|
|
4963
5338
|
while (dir !== root) {
|
|
4964
|
-
const pkgPath =
|
|
4965
|
-
if (
|
|
5339
|
+
const pkgPath = path14.join(dir, "package.json");
|
|
5340
|
+
if (existsSync14(pkgPath)) {
|
|
4966
5341
|
try {
|
|
4967
|
-
const pkg = JSON.parse(
|
|
5342
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
4968
5343
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
4969
5344
|
} catch {
|
|
4970
5345
|
}
|
|
4971
5346
|
}
|
|
4972
|
-
dir =
|
|
5347
|
+
dir = path14.dirname(dir);
|
|
4973
5348
|
}
|
|
4974
|
-
return
|
|
5349
|
+
return path14.resolve(path14.dirname(thisFile), "..", "..", "..");
|
|
4975
5350
|
}
|
|
4976
5351
|
var EXE_SECTION_START, EXE_SECTION_END, ORCHESTRATION_RULES;
|
|
4977
5352
|
var init_installer = __esm({
|
|
@@ -5009,19 +5384,19 @@ __export(installer_exports, {
|
|
|
5009
5384
|
verifyCodexHooks: () => verifyCodexHooks
|
|
5010
5385
|
});
|
|
5011
5386
|
import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
|
|
5012
|
-
import { existsSync as
|
|
5013
|
-
import
|
|
5387
|
+
import { existsSync as existsSync15, readFileSync as readFileSync10 } from "fs";
|
|
5388
|
+
import path15 from "path";
|
|
5014
5389
|
import os11 from "os";
|
|
5015
5390
|
async function mergeCodexHooks(packageRoot, homeDir = os11.homedir()) {
|
|
5016
|
-
const codexDir =
|
|
5017
|
-
const hooksPath =
|
|
5018
|
-
const logsDir =
|
|
5019
|
-
const hookLogPath =
|
|
5391
|
+
const codexDir = path15.join(homeDir, ".codex");
|
|
5392
|
+
const hooksPath = path15.join(codexDir, "hooks.json");
|
|
5393
|
+
const logsDir = path15.join(homeDir, ".exe-os", "logs");
|
|
5394
|
+
const hookLogPath = path15.join(logsDir, "hooks.log");
|
|
5020
5395
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
5021
5396
|
await mkdir5(codexDir, { recursive: true });
|
|
5022
5397
|
await mkdir5(logsDir, { recursive: true });
|
|
5023
5398
|
let hooksJson = {};
|
|
5024
|
-
if (
|
|
5399
|
+
if (existsSync15(hooksPath)) {
|
|
5025
5400
|
try {
|
|
5026
5401
|
hooksJson = JSON.parse(await readFile5(hooksPath, "utf-8"));
|
|
5027
5402
|
} catch {
|
|
@@ -5041,7 +5416,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os11.homedir()) {
|
|
|
5041
5416
|
// Combined hook: runs ingest + error-recall in one Node process.
|
|
5042
5417
|
// Eliminates a cold-start cycle per tool call (~3-6s savings on Codex).
|
|
5043
5418
|
type: "command",
|
|
5044
|
-
command: `node "${
|
|
5419
|
+
command: `node "${path15.join(packageRoot, "dist", "hooks", "post-tool-combined.js")}"${logSuffix}`
|
|
5045
5420
|
}
|
|
5046
5421
|
]
|
|
5047
5422
|
},
|
|
@@ -5055,7 +5430,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os11.homedir()) {
|
|
|
5055
5430
|
// Single hook: prompt-submit handles memory retrieval + entity boost.
|
|
5056
5431
|
// exe-heartbeat-hook is CC-specific (intercom) — omitted on Codex.
|
|
5057
5432
|
type: "command",
|
|
5058
|
-
command: `node "${
|
|
5433
|
+
command: `node "${path15.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
5059
5434
|
}
|
|
5060
5435
|
]
|
|
5061
5436
|
},
|
|
@@ -5067,7 +5442,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os11.homedir()) {
|
|
|
5067
5442
|
hooks: [
|
|
5068
5443
|
{
|
|
5069
5444
|
type: "command",
|
|
5070
|
-
command: `node "${
|
|
5445
|
+
command: `node "${path15.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
5071
5446
|
}
|
|
5072
5447
|
]
|
|
5073
5448
|
},
|
|
@@ -5080,7 +5455,7 @@ async function mergeCodexHooks(packageRoot, homeDir = os11.homedir()) {
|
|
|
5080
5455
|
hooks: [
|
|
5081
5456
|
{
|
|
5082
5457
|
type: "command",
|
|
5083
|
-
command: `node "${
|
|
5458
|
+
command: `node "${path15.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
5084
5459
|
}
|
|
5085
5460
|
]
|
|
5086
5461
|
},
|
|
@@ -5129,10 +5504,10 @@ async function mergeCodexHooks(packageRoot, homeDir = os11.homedir()) {
|
|
|
5129
5504
|
return { added, skipped };
|
|
5130
5505
|
}
|
|
5131
5506
|
function verifyCodexHooks(homeDir = os11.homedir()) {
|
|
5132
|
-
const hooksPath =
|
|
5133
|
-
if (!
|
|
5507
|
+
const hooksPath = path15.join(homeDir, ".codex", "hooks.json");
|
|
5508
|
+
if (!existsSync15(hooksPath)) return false;
|
|
5134
5509
|
try {
|
|
5135
|
-
const hooksJson = JSON.parse(
|
|
5510
|
+
const hooksJson = JSON.parse(readFileSync10(hooksPath, "utf-8"));
|
|
5136
5511
|
if (!hooksJson.hooks) return false;
|
|
5137
5512
|
const required = ["PostToolUse", "UserPromptSubmit", "Stop", "PreToolUse"];
|
|
5138
5513
|
for (const event of required) {
|
|
@@ -5164,11 +5539,11 @@ function verifyCodexHooks(homeDir = os11.homedir()) {
|
|
|
5164
5539
|
async function installCodexStatusLine(homeDir = os11.homedir()) {
|
|
5165
5540
|
const prefs = loadPreferences(homeDir);
|
|
5166
5541
|
if (prefs.codexStatusLine === false) return "opted-out";
|
|
5167
|
-
const codexDir =
|
|
5168
|
-
const configPath =
|
|
5542
|
+
const codexDir = path15.join(homeDir, ".codex");
|
|
5543
|
+
const configPath = path15.join(codexDir, "config.toml");
|
|
5169
5544
|
await mkdir5(codexDir, { recursive: true });
|
|
5170
5545
|
let content = "";
|
|
5171
|
-
if (
|
|
5546
|
+
if (existsSync15(configPath)) {
|
|
5172
5547
|
content = await readFile5(configPath, "utf-8");
|
|
5173
5548
|
if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
|
|
5174
5549
|
return "already-configured";
|
|
@@ -5220,12 +5595,12 @@ ${line}
|
|
|
5220
5595
|
return { content: next, changed: next !== sectionContent };
|
|
5221
5596
|
}
|
|
5222
5597
|
async function registerCodexMcpServer(packageRoot, homeDir = os11.homedir()) {
|
|
5223
|
-
const codexDir =
|
|
5224
|
-
const configPath =
|
|
5598
|
+
const codexDir = path15.join(homeDir, ".codex");
|
|
5599
|
+
const configPath = path15.join(codexDir, "config.toml");
|
|
5225
5600
|
void packageRoot;
|
|
5226
5601
|
await mkdir5(codexDir, { recursive: true });
|
|
5227
5602
|
let content = "";
|
|
5228
|
-
if (
|
|
5603
|
+
if (existsSync15(configPath)) {
|
|
5229
5604
|
content = await readFile5(configPath, "utf-8");
|
|
5230
5605
|
}
|
|
5231
5606
|
const sectionHeader = "[mcp_servers.exe-os]";
|
|
@@ -5250,10 +5625,10 @@ async function registerCodexMcpServer(packageRoot, homeDir = os11.homedir()) {
|
|
|
5250
5625
|
return "registered";
|
|
5251
5626
|
}
|
|
5252
5627
|
async function ensureCodexHooksFeature(homeDir = os11.homedir()) {
|
|
5253
|
-
const configPath =
|
|
5254
|
-
await mkdir5(
|
|
5628
|
+
const configPath = path15.join(homeDir, ".codex", "config.toml");
|
|
5629
|
+
await mkdir5(path15.join(homeDir, ".codex"), { recursive: true });
|
|
5255
5630
|
let content = "";
|
|
5256
|
-
if (
|
|
5631
|
+
if (existsSync15(configPath)) {
|
|
5257
5632
|
content = await readFile5(configPath, "utf-8");
|
|
5258
5633
|
}
|
|
5259
5634
|
if (/\[features\][\s\S]*?codex_hooks\s*=\s*true/.test(content)) {
|
|
@@ -5322,95 +5697,6 @@ var init_installer2 = __esm({
|
|
|
5322
5697
|
}
|
|
5323
5698
|
});
|
|
5324
5699
|
|
|
5325
|
-
// src/lib/agent-config.ts
|
|
5326
|
-
var agent_config_exports = {};
|
|
5327
|
-
__export(agent_config_exports, {
|
|
5328
|
-
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
5329
|
-
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
5330
|
-
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
5331
|
-
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
5332
|
-
clearAgentRuntime: () => clearAgentRuntime,
|
|
5333
|
-
getAgentRuntime: () => getAgentRuntime,
|
|
5334
|
-
loadAgentConfig: () => loadAgentConfig,
|
|
5335
|
-
saveAgentConfig: () => saveAgentConfig,
|
|
5336
|
-
setAgentRuntime: () => setAgentRuntime
|
|
5337
|
-
});
|
|
5338
|
-
import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, existsSync as existsSync15 } from "fs";
|
|
5339
|
-
import path15 from "path";
|
|
5340
|
-
function loadAgentConfig() {
|
|
5341
|
-
if (!existsSync15(AGENT_CONFIG_PATH)) return {};
|
|
5342
|
-
try {
|
|
5343
|
-
return JSON.parse(readFileSync10(AGENT_CONFIG_PATH, "utf-8"));
|
|
5344
|
-
} catch {
|
|
5345
|
-
return {};
|
|
5346
|
-
}
|
|
5347
|
-
}
|
|
5348
|
-
function saveAgentConfig(config) {
|
|
5349
|
-
const dir = path15.dirname(AGENT_CONFIG_PATH);
|
|
5350
|
-
ensurePrivateDirSync(dir);
|
|
5351
|
-
writeFileSync8(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
5352
|
-
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
5353
|
-
}
|
|
5354
|
-
function getAgentRuntime(agentId) {
|
|
5355
|
-
const config = loadAgentConfig();
|
|
5356
|
-
const entry = config[agentId];
|
|
5357
|
-
if (entry) return entry;
|
|
5358
|
-
const orgDefault = config["default"];
|
|
5359
|
-
if (orgDefault) return orgDefault;
|
|
5360
|
-
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
5361
|
-
}
|
|
5362
|
-
function setAgentRuntime(agentId, runtime, model, reasoning_effort) {
|
|
5363
|
-
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
5364
|
-
if (!knownModels) {
|
|
5365
|
-
return {
|
|
5366
|
-
ok: false,
|
|
5367
|
-
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
5368
|
-
};
|
|
5369
|
-
}
|
|
5370
|
-
if (!knownModels.includes(model)) {
|
|
5371
|
-
return {
|
|
5372
|
-
ok: false,
|
|
5373
|
-
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
5374
|
-
};
|
|
5375
|
-
}
|
|
5376
|
-
const config = loadAgentConfig();
|
|
5377
|
-
const entry = { runtime, model };
|
|
5378
|
-
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
5379
|
-
config[agentId] = entry;
|
|
5380
|
-
saveAgentConfig(config);
|
|
5381
|
-
return { ok: true };
|
|
5382
|
-
}
|
|
5383
|
-
function clearAgentRuntime(agentId) {
|
|
5384
|
-
const config = loadAgentConfig();
|
|
5385
|
-
delete config[agentId];
|
|
5386
|
-
saveAgentConfig(config);
|
|
5387
|
-
}
|
|
5388
|
-
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
5389
|
-
var init_agent_config = __esm({
|
|
5390
|
-
"src/lib/agent-config.ts"() {
|
|
5391
|
-
"use strict";
|
|
5392
|
-
init_config();
|
|
5393
|
-
init_runtime_table();
|
|
5394
|
-
init_secure_files();
|
|
5395
|
-
AGENT_CONFIG_PATH = path15.join(EXE_AI_DIR, "agent-config.json");
|
|
5396
|
-
KNOWN_RUNTIMES = {
|
|
5397
|
-
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
5398
|
-
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
5399
|
-
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
5400
|
-
};
|
|
5401
|
-
RUNTIME_LABELS = {
|
|
5402
|
-
claude: "Claude Code (Anthropic)",
|
|
5403
|
-
codex: "Codex (OpenAI)",
|
|
5404
|
-
opencode: "OpenCode (open source)"
|
|
5405
|
-
};
|
|
5406
|
-
DEFAULT_MODELS = {
|
|
5407
|
-
claude: "claude-opus-4.6",
|
|
5408
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
5409
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
5410
|
-
};
|
|
5411
|
-
}
|
|
5412
|
-
});
|
|
5413
|
-
|
|
5414
5700
|
// src/bin/exe-start-codex.ts
|
|
5415
5701
|
import os12 from "os";
|
|
5416
5702
|
import path16 from "path";
|
|
@@ -5429,18 +5715,18 @@ init_database();
|
|
|
5429
5715
|
|
|
5430
5716
|
// src/lib/keychain.ts
|
|
5431
5717
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
5432
|
-
import { existsSync as
|
|
5718
|
+
import { existsSync as existsSync8, statSync as statSync3 } from "fs";
|
|
5433
5719
|
import { execSync as execSync3 } from "child_process";
|
|
5434
|
-
import
|
|
5720
|
+
import path7 from "path";
|
|
5435
5721
|
import os5 from "os";
|
|
5436
5722
|
var SERVICE = "exe-os";
|
|
5437
5723
|
var LEGACY_SERVICE = "exe-mem";
|
|
5438
5724
|
var ACCOUNT = "master-key";
|
|
5439
5725
|
function getKeyDir() {
|
|
5440
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
5726
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
|
|
5441
5727
|
}
|
|
5442
5728
|
function getKeyPath() {
|
|
5443
|
-
return
|
|
5729
|
+
return path7.join(getKeyDir(), "master.key");
|
|
5444
5730
|
}
|
|
5445
5731
|
function nativeKeychainAllowed() {
|
|
5446
5732
|
return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
|
|
@@ -5472,7 +5758,7 @@ function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
|
5472
5758
|
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
5473
5759
|
if (uid === 0) return true;
|
|
5474
5760
|
const exeOsDir = process.env.EXE_OS_DIR;
|
|
5475
|
-
return Boolean(exeOsDir &&
|
|
5761
|
+
return Boolean(exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep));
|
|
5476
5762
|
} catch {
|
|
5477
5763
|
return false;
|
|
5478
5764
|
}
|
|
@@ -5670,7 +5956,7 @@ async function getMasterKey() {
|
|
|
5670
5956
|
}
|
|
5671
5957
|
}
|
|
5672
5958
|
const keyPath = getKeyPath();
|
|
5673
|
-
if (!
|
|
5959
|
+
if (!existsSync8(keyPath)) {
|
|
5674
5960
|
process.stderr.write(
|
|
5675
5961
|
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
5676
5962
|
`
|
|
@@ -6154,14 +6440,14 @@ function vectorToBlob(vector) {
|
|
|
6154
6440
|
|
|
6155
6441
|
// src/lib/behaviors-export.ts
|
|
6156
6442
|
import os6 from "os";
|
|
6157
|
-
import
|
|
6443
|
+
import path9 from "path";
|
|
6158
6444
|
import {
|
|
6159
|
-
existsSync as
|
|
6445
|
+
existsSync as existsSync10,
|
|
6160
6446
|
mkdirSync as mkdirSync4,
|
|
6161
6447
|
readdirSync as readdirSync2,
|
|
6162
6448
|
statSync as statSync5,
|
|
6163
6449
|
unlinkSync as unlinkSync4,
|
|
6164
|
-
writeFileSync as
|
|
6450
|
+
writeFileSync as writeFileSync4
|
|
6165
6451
|
} from "fs";
|
|
6166
6452
|
|
|
6167
6453
|
// src/lib/behaviors.ts
|
|
@@ -6170,7 +6456,7 @@ import crypto2 from "crypto";
|
|
|
6170
6456
|
async function listBehaviors(agentId, projectName, limit = 30) {
|
|
6171
6457
|
const client = getClient();
|
|
6172
6458
|
const result = await client.execute({
|
|
6173
|
-
sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector
|
|
6459
|
+
sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector, created_by_agent, created_by_device, source_session_id
|
|
6174
6460
|
FROM behaviors
|
|
6175
6461
|
WHERE agent_id = ? AND active = 1
|
|
6176
6462
|
AND (project_name IS NULL OR project_name = ?)
|
|
@@ -6199,12 +6485,15 @@ async function listBehaviors(agentId, projectName, limit = 30) {
|
|
|
6199
6485
|
active: Number(r.active),
|
|
6200
6486
|
created_at: String(r.created_at),
|
|
6201
6487
|
updated_at: String(r.updated_at),
|
|
6202
|
-
vector: r.vector ? Array.from(new Float32Array(r.vector)) : null
|
|
6488
|
+
vector: r.vector ? Array.from(new Float32Array(r.vector)) : null,
|
|
6489
|
+
created_by_agent: r.created_by_agent ? String(r.created_by_agent) : null,
|
|
6490
|
+
created_by_device: r.created_by_device ? String(r.created_by_device) : null,
|
|
6491
|
+
source_session_id: r.source_session_id ? String(r.source_session_id) : null
|
|
6203
6492
|
}));
|
|
6204
6493
|
}
|
|
6205
6494
|
|
|
6206
6495
|
// src/lib/behaviors-export.ts
|
|
6207
|
-
var BEHAVIORS_EXPORT_DIR =
|
|
6496
|
+
var BEHAVIORS_EXPORT_DIR = path9.join(
|
|
6208
6497
|
os6.homedir(),
|
|
6209
6498
|
".exe-os",
|
|
6210
6499
|
"behaviors-export"
|
|
@@ -6221,7 +6510,7 @@ function getBehaviorLimit() {
|
|
|
6221
6510
|
}
|
|
6222
6511
|
}
|
|
6223
6512
|
function sweepStaleBehaviorExports(now = Date.now()) {
|
|
6224
|
-
if (!
|
|
6513
|
+
if (!existsSync10(BEHAVIORS_EXPORT_DIR)) return;
|
|
6225
6514
|
let entries;
|
|
6226
6515
|
try {
|
|
6227
6516
|
entries = readdirSync2(BEHAVIORS_EXPORT_DIR);
|
|
@@ -6229,7 +6518,7 @@ function sweepStaleBehaviorExports(now = Date.now()) {
|
|
|
6229
6518
|
return;
|
|
6230
6519
|
}
|
|
6231
6520
|
for (const entry of entries) {
|
|
6232
|
-
const filePath =
|
|
6521
|
+
const filePath = path9.join(BEHAVIORS_EXPORT_DIR, entry);
|
|
6233
6522
|
try {
|
|
6234
6523
|
const stat = statSync5(filePath);
|
|
6235
6524
|
if (now - stat.mtimeMs > STALE_EXPORT_AGE_MS) {
|
|
@@ -6262,10 +6551,10 @@ function renderBehaviorExport(behaviors) {
|
|
|
6262
6551
|
}
|
|
6263
6552
|
function exportFilePath(agentId, projectName, sessionKey) {
|
|
6264
6553
|
if (!sessionKey) {
|
|
6265
|
-
return
|
|
6554
|
+
return path9.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
|
|
6266
6555
|
}
|
|
6267
6556
|
const safeProject = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
6268
|
-
return
|
|
6557
|
+
return path9.join(
|
|
6269
6558
|
BEHAVIORS_EXPORT_DIR,
|
|
6270
6559
|
`${agentId}-${safeProject}-${sessionKey}.md`
|
|
6271
6560
|
);
|
|
@@ -6277,7 +6566,7 @@ async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
|
|
|
6277
6566
|
if (behaviors.length === 0) return null;
|
|
6278
6567
|
const body = renderBehaviorExport(behaviors);
|
|
6279
6568
|
const target = exportFilePath(agentId, projectName, sessionKey);
|
|
6280
|
-
|
|
6569
|
+
writeFileSync4(target, body, "utf-8");
|
|
6281
6570
|
return target;
|
|
6282
6571
|
}
|
|
6283
6572
|
|