@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.
- package/README.md +9 -7
- package/dist/bin/agentic-ontology-backfill.js +54 -11
- package/dist/bin/agentic-reflection-backfill.js +29 -1
- package/dist/bin/agentic-semantic-label.js +29 -1
- package/dist/bin/backfill-conversations.js +53 -10
- package/dist/bin/backfill-responses.js +54 -11
- package/dist/bin/backfill-vectors.js +29 -1
- package/dist/bin/bulk-sync-postgres.js +55 -12
- package/dist/bin/cleanup-stale-review-tasks.js +75 -15
- package/dist/bin/cli.js +293 -76
- package/dist/bin/exe-agent-config.js +7 -1
- package/dist/bin/exe-agent.js +28 -2
- package/dist/bin/exe-assign.js +54 -11
- package/dist/bin/exe-boot.js +481 -147
- package/dist/bin/exe-call.js +45 -4
- package/dist/bin/exe-cloud.js +93 -15
- package/dist/bin/exe-dispatch.js +369 -24
- package/dist/bin/exe-doctor.js +53 -10
- package/dist/bin/exe-export-behaviors.js +54 -11
- package/dist/bin/exe-forget.js +54 -11
- package/dist/bin/exe-gateway.js +128 -23
- package/dist/bin/exe-heartbeat.js +75 -15
- package/dist/bin/exe-kill.js +54 -11
- package/dist/bin/exe-launch-agent.js +70 -12
- package/dist/bin/exe-new-employee.js +175 -7
- package/dist/bin/exe-pending-messages.js +75 -15
- package/dist/bin/exe-pending-notifications.js +75 -15
- package/dist/bin/exe-pending-reviews.js +75 -15
- package/dist/bin/exe-rename.js +54 -11
- package/dist/bin/exe-review.js +54 -11
- package/dist/bin/exe-search.js +54 -11
- package/dist/bin/exe-session-cleanup.js +491 -146
- package/dist/bin/exe-settings.js +10 -4
- package/dist/bin/exe-start-codex.js +524 -245
- package/dist/bin/exe-start-opencode.js +534 -165
- package/dist/bin/exe-status.js +75 -15
- package/dist/bin/exe-support.js +1 -1
- package/dist/bin/exe-team.js +54 -11
- package/dist/bin/git-sweep.js +369 -24
- package/dist/bin/graph-backfill.js +54 -11
- package/dist/bin/graph-export.js +54 -11
- package/dist/bin/install.js +62 -4
- package/dist/bin/intercom-check.js +491 -146
- package/dist/bin/pre-publish.js +13 -1
- package/dist/bin/scan-tasks.js +369 -24
- package/dist/bin/setup.js +91 -13
- package/dist/bin/shard-migrate.js +54 -11
- package/dist/bin/stack-update.js +1 -1
- package/dist/bin/update.js +3 -3
- package/dist/gateway/index.js +128 -23
- package/dist/hooks/bug-report-worker.js +128 -23
- package/dist/hooks/codex-stop-task-finalizer.js +512 -140
- package/dist/hooks/commit-complete.js +369 -24
- package/dist/hooks/error-recall.js +54 -11
- package/dist/hooks/ingest.js +4575 -252
- package/dist/hooks/instructions-loaded.js +54 -11
- package/dist/hooks/notification.js +54 -11
- package/dist/hooks/post-compact.js +75 -15
- package/dist/hooks/post-tool-combined.js +75 -15
- package/dist/hooks/pre-compact.js +449 -104
- package/dist/hooks/pre-tool-use.js +90 -15
- package/dist/hooks/prompt-submit.js +129 -24
- package/dist/hooks/session-end.js +451 -109
- package/dist/hooks/session-start.js +104 -16
- package/dist/hooks/stop.js +74 -14
- package/dist/hooks/subagent-stop.js +75 -15
- package/dist/hooks/summary-worker.js +73 -7
- package/dist/index.js +128 -23
- package/dist/lib/agent-config.js +16 -1
- package/dist/lib/cloud-sync.js +38 -1
- package/dist/lib/consolidation.js +16 -1
- package/dist/lib/database.js +16 -0
- package/dist/lib/db.js +16 -0
- package/dist/lib/device-registry.js +16 -0
- package/dist/lib/employee-templates.js +29 -3
- package/dist/lib/employees.js +16 -1
- package/dist/lib/exe-daemon.js +268 -42
- package/dist/lib/hybrid-search.js +54 -11
- package/dist/lib/license.js +3 -3
- package/dist/lib/messaging.js +21 -4
- package/dist/lib/schedules.js +29 -1
- package/dist/lib/skill-learning.js +458 -70
- package/dist/lib/status-brief.js +14 -1
- package/dist/lib/store.js +54 -11
- package/dist/lib/tasks.js +393 -91
- package/dist/lib/tmux-routing.js +316 -14
- package/dist/mcp/server.js +169 -30
- package/dist/mcp/tools/create-task.js +75 -13
- 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 +390 -91
- package/dist/runtime/index.js +446 -101
- package/dist/tui/App.js +208 -54
- 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);
|
|
@@ -3121,6 +3460,22 @@ async function ensureSchema() {
|
|
|
3121
3460
|
} catch (e) {
|
|
3122
3461
|
logCatchDebug("migration", e);
|
|
3123
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
|
+
}
|
|
3124
3479
|
}
|
|
3125
3480
|
async function disposeDatabase() {
|
|
3126
3481
|
if (_walCheckpointTimer) {
|
|
@@ -3186,14 +3541,14 @@ __export(shard_manager_exports, {
|
|
|
3186
3541
|
listShards: () => listShards,
|
|
3187
3542
|
shardExists: () => shardExists
|
|
3188
3543
|
});
|
|
3189
|
-
import
|
|
3190
|
-
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";
|
|
3191
3546
|
import { createClient as createClient2 } from "@libsql/client";
|
|
3192
3547
|
function initShardManager(encryptionKey) {
|
|
3193
3548
|
_encryptionKey = encryptionKey;
|
|
3194
3549
|
_keyValidated = false;
|
|
3195
3550
|
_keyValidationPromise = null;
|
|
3196
|
-
if (!
|
|
3551
|
+
if (!existsSync9(SHARDS_DIR)) {
|
|
3197
3552
|
mkdirSync3(SHARDS_DIR, { recursive: true });
|
|
3198
3553
|
}
|
|
3199
3554
|
const existingShards = readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db"));
|
|
@@ -3214,7 +3569,7 @@ async function validateEncryptionKey() {
|
|
|
3214
3569
|
return true;
|
|
3215
3570
|
}
|
|
3216
3571
|
for (const shardFile of existingShards.slice(0, 3)) {
|
|
3217
|
-
const dbPath =
|
|
3572
|
+
const dbPath = path8.join(SHARDS_DIR, shardFile);
|
|
3218
3573
|
const testClient = createClient2({ url: `file:${dbPath}`, encryptionKey: _encryptionKey });
|
|
3219
3574
|
try {
|
|
3220
3575
|
await testClient.execute("SELECT COUNT(*) FROM sqlite_schema");
|
|
@@ -3257,7 +3612,7 @@ function getShardClient(projectName) {
|
|
|
3257
3612
|
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
3258
3613
|
evictLRU();
|
|
3259
3614
|
}
|
|
3260
|
-
const dbPath =
|
|
3615
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
3261
3616
|
const client = createClient2({
|
|
3262
3617
|
url: `file:${dbPath}`,
|
|
3263
3618
|
encryptionKey: _encryptionKey
|
|
@@ -3268,13 +3623,13 @@ function getShardClient(projectName) {
|
|
|
3268
3623
|
}
|
|
3269
3624
|
function shardExists(projectName) {
|
|
3270
3625
|
const safeName = safeShardName(projectName);
|
|
3271
|
-
return
|
|
3626
|
+
return existsSync9(path8.join(SHARDS_DIR, `${safeName}.db`));
|
|
3272
3627
|
}
|
|
3273
3628
|
function safeShardName(projectName) {
|
|
3274
3629
|
return projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
3275
3630
|
}
|
|
3276
3631
|
function listShards() {
|
|
3277
|
-
if (!
|
|
3632
|
+
if (!existsSync9(SHARDS_DIR)) return [];
|
|
3278
3633
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3279
3634
|
}
|
|
3280
3635
|
async function auditShardHealth(options = {}) {
|
|
@@ -3286,7 +3641,7 @@ async function auditShardHealth(options = {}) {
|
|
|
3286
3641
|
const names = listShards();
|
|
3287
3642
|
const shards = [];
|
|
3288
3643
|
for (const name of names) {
|
|
3289
|
-
const dbPath =
|
|
3644
|
+
const dbPath = path8.join(SHARDS_DIR, `${name}.db`);
|
|
3290
3645
|
const stat = statSync4(dbPath);
|
|
3291
3646
|
const item = {
|
|
3292
3647
|
name,
|
|
@@ -3321,7 +3676,7 @@ async function auditShardHealth(options = {}) {
|
|
|
3321
3676
|
_shards.delete(name);
|
|
3322
3677
|
_shardLastAccess.delete(name);
|
|
3323
3678
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3324
|
-
const archivedPath =
|
|
3679
|
+
const archivedPath = path8.join(SHARDS_DIR, `${name}.db.broken-${stamp}`);
|
|
3325
3680
|
renameSync3(dbPath, archivedPath);
|
|
3326
3681
|
item.archivedPath = archivedPath;
|
|
3327
3682
|
}
|
|
@@ -3549,11 +3904,11 @@ async function getReadyShardClient(projectName) {
|
|
|
3549
3904
|
client.close();
|
|
3550
3905
|
_shards.delete(safeName);
|
|
3551
3906
|
_shardLastAccess.delete(safeName);
|
|
3552
|
-
const dbPath =
|
|
3553
|
-
if (
|
|
3907
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
3908
|
+
if (existsSync9(dbPath)) {
|
|
3554
3909
|
const stat = statSync4(dbPath);
|
|
3555
3910
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3556
|
-
const archivedPath =
|
|
3911
|
+
const archivedPath = path8.join(SHARDS_DIR, `${safeName}.db.broken-${stamp}`);
|
|
3557
3912
|
renameSync3(dbPath, archivedPath);
|
|
3558
3913
|
process.stderr.write(
|
|
3559
3914
|
`[shard-manager] Archived unreadable shard ${safeName}: ${archivedPath} (${stat.size} bytes, mtime ${stat.mtime.toISOString()})
|
|
@@ -3621,7 +3976,7 @@ var init_shard_manager = __esm({
|
|
|
3621
3976
|
"src/lib/shard-manager.ts"() {
|
|
3622
3977
|
"use strict";
|
|
3623
3978
|
init_config();
|
|
3624
|
-
SHARDS_DIR =
|
|
3979
|
+
SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
|
|
3625
3980
|
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
3626
3981
|
MAX_OPEN_SHARDS = 10;
|
|
3627
3982
|
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
@@ -3687,11 +4042,17 @@ var init_platform_procedures = __esm({
|
|
|
3687
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."
|
|
3688
4043
|
},
|
|
3689
4044
|
{
|
|
3690
|
-
title: "
|
|
4045
|
+
title: "Orchestration phase guidance \u2014 recommend, never trap",
|
|
3691
4046
|
domain: "workflow",
|
|
3692
4047
|
priority: "p1",
|
|
3693
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."
|
|
3694
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
|
+
},
|
|
3695
4056
|
{
|
|
3696
4057
|
title: "Single dispatch path \u2014 create_task only",
|
|
3697
4058
|
domain: "workflow",
|
|
@@ -3725,6 +4086,12 @@ var init_platform_procedures = __esm({
|
|
|
3725
4086
|
priority: "p0",
|
|
3726
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."
|
|
3727
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
|
+
},
|
|
3728
4095
|
{
|
|
3729
4096
|
title: "Customer patch triage \u2014 upstream bug vs customization",
|
|
3730
4097
|
domain: "support",
|
|
@@ -4010,10 +4377,24 @@ function stableId(memoryId, type, content) {
|
|
|
4010
4377
|
return createHash2("sha256").update(`${memoryId}:${type}:${content}`).digest("hex").slice(0, 32);
|
|
4011
4378
|
}
|
|
4012
4379
|
function cleanText(text) {
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
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;
|
|
4386
|
+
}
|
|
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;
|
|
4017
4398
|
}
|
|
4018
4399
|
function inferCardType(sentence, toolName) {
|
|
4019
4400
|
const lower = sentence.toLowerCase();
|
|
@@ -4045,12 +4426,12 @@ function predicateFor(type) {
|
|
|
4045
4426
|
}
|
|
4046
4427
|
}
|
|
4047
4428
|
function extractMemoryCards(row) {
|
|
4048
|
-
const
|
|
4429
|
+
const segments = splitSegments(row.raw_text);
|
|
4049
4430
|
const cards = [];
|
|
4050
|
-
for (const sentence of
|
|
4431
|
+
for (const sentence of segments) {
|
|
4051
4432
|
const type = inferCardType(sentence, row.tool_name);
|
|
4052
4433
|
const subject = extractSubject(sentence, row.agent_id);
|
|
4053
|
-
const content = sentence.length >
|
|
4434
|
+
const content = sentence.length > MAX_SEGMENT_CHARS ? `${sentence.slice(0, MAX_SEGMENT_CHARS - 1)}\u2026` : sentence;
|
|
4054
4435
|
cards.push({
|
|
4055
4436
|
id: stableId(row.id, type, content),
|
|
4056
4437
|
memory_id: row.id,
|
|
@@ -4146,13 +4527,14 @@ Source memory: ${String(row.source_ref ?? row.memory_id)}`,
|
|
|
4146
4527
|
last_accessed: String(row.timestamp)
|
|
4147
4528
|
}));
|
|
4148
4529
|
}
|
|
4149
|
-
var MAX_CARDS_PER_MEMORY,
|
|
4530
|
+
var MAX_CARDS_PER_MEMORY, MAX_SEGMENT_CHARS, MIN_SEGMENT_CHARS;
|
|
4150
4531
|
var init_memory_cards = __esm({
|
|
4151
4532
|
"src/lib/memory-cards.ts"() {
|
|
4152
4533
|
"use strict";
|
|
4153
4534
|
init_database();
|
|
4154
|
-
MAX_CARDS_PER_MEMORY =
|
|
4155
|
-
|
|
4535
|
+
MAX_CARDS_PER_MEMORY = 8;
|
|
4536
|
+
MAX_SEGMENT_CHARS = 500;
|
|
4537
|
+
MIN_SEGMENT_CHARS = 20;
|
|
4156
4538
|
}
|
|
4157
4539
|
});
|
|
4158
4540
|
|
|
@@ -4424,32 +4806,6 @@ var init_agentic_ontology = __esm({
|
|
|
4424
4806
|
}
|
|
4425
4807
|
});
|
|
4426
4808
|
|
|
4427
|
-
// src/lib/runtime-table.ts
|
|
4428
|
-
var RUNTIME_TABLE;
|
|
4429
|
-
var init_runtime_table = __esm({
|
|
4430
|
-
"src/lib/runtime-table.ts"() {
|
|
4431
|
-
"use strict";
|
|
4432
|
-
RUNTIME_TABLE = {
|
|
4433
|
-
codex: {
|
|
4434
|
-
binary: "codex",
|
|
4435
|
-
launchMode: "interactive",
|
|
4436
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
4437
|
-
inlineFlag: "--no-alt-screen",
|
|
4438
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
4439
|
-
defaultModel: "gpt-5.5"
|
|
4440
|
-
},
|
|
4441
|
-
opencode: {
|
|
4442
|
-
binary: "opencode",
|
|
4443
|
-
launchMode: "exec",
|
|
4444
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
4445
|
-
inlineFlag: "",
|
|
4446
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
4447
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
4448
|
-
}
|
|
4449
|
-
};
|
|
4450
|
-
}
|
|
4451
|
-
});
|
|
4452
|
-
|
|
4453
4809
|
// src/lib/session-key.ts
|
|
4454
4810
|
import { execSync as execSync4 } from "child_process";
|
|
4455
4811
|
function normalizeCommand(command) {
|
|
@@ -4541,9 +4897,9 @@ __export(active_agent_exports, {
|
|
|
4541
4897
|
resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
|
|
4542
4898
|
writeActiveAgent: () => writeActiveAgent
|
|
4543
4899
|
});
|
|
4544
|
-
import { readFileSync as
|
|
4900
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5, readdirSync as readdirSync3 } from "fs";
|
|
4545
4901
|
import { execSync as execSync5 } from "child_process";
|
|
4546
|
-
import
|
|
4902
|
+
import path10 from "path";
|
|
4547
4903
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
4548
4904
|
if (candidate === baseName) return true;
|
|
4549
4905
|
if (!candidate.startsWith(baseName)) return false;
|
|
@@ -4587,12 +4943,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
4587
4943
|
return null;
|
|
4588
4944
|
}
|
|
4589
4945
|
function getMarkerPath() {
|
|
4590
|
-
return
|
|
4946
|
+
return path10.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
4591
4947
|
}
|
|
4592
4948
|
function writeActiveAgent(agentId, agentRole) {
|
|
4593
4949
|
try {
|
|
4594
4950
|
mkdirSync5(CACHE_DIR, { recursive: true });
|
|
4595
|
-
|
|
4951
|
+
writeFileSync5(
|
|
4596
4952
|
getMarkerPath(),
|
|
4597
4953
|
JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
4598
4954
|
);
|
|
@@ -4610,7 +4966,7 @@ function getActiveAgent() {
|
|
|
4610
4966
|
if (httpCtx) return httpCtx;
|
|
4611
4967
|
try {
|
|
4612
4968
|
const markerPath = getMarkerPath();
|
|
4613
|
-
const raw =
|
|
4969
|
+
const raw = readFileSync6(markerPath, "utf8");
|
|
4614
4970
|
const data = JSON.parse(raw);
|
|
4615
4971
|
if (data.agentId) {
|
|
4616
4972
|
if (data.startedAt) {
|
|
@@ -4658,14 +5014,14 @@ function getAllActiveAgents() {
|
|
|
4658
5014
|
const key = file.slice("active-agent-".length, -".json".length);
|
|
4659
5015
|
if (key === "undefined") continue;
|
|
4660
5016
|
try {
|
|
4661
|
-
const raw =
|
|
5017
|
+
const raw = readFileSync6(path10.join(CACHE_DIR, file), "utf8");
|
|
4662
5018
|
const data = JSON.parse(raw);
|
|
4663
5019
|
if (!data.agentId) continue;
|
|
4664
5020
|
if (data.startedAt) {
|
|
4665
5021
|
const age = Date.now() - new Date(data.startedAt).getTime();
|
|
4666
5022
|
if (age > STALE_MS) {
|
|
4667
5023
|
try {
|
|
4668
|
-
unlinkSync5(
|
|
5024
|
+
unlinkSync5(path10.join(CACHE_DIR, file));
|
|
4669
5025
|
} catch {
|
|
4670
5026
|
}
|
|
4671
5027
|
continue;
|
|
@@ -4688,11 +5044,11 @@ function getAllActiveAgents() {
|
|
|
4688
5044
|
function cleanupSessionMarkers() {
|
|
4689
5045
|
const key = getSessionKey();
|
|
4690
5046
|
try {
|
|
4691
|
-
unlinkSync5(
|
|
5047
|
+
unlinkSync5(path10.join(CACHE_DIR, `active-agent-${key}.json`));
|
|
4692
5048
|
} catch {
|
|
4693
5049
|
}
|
|
4694
5050
|
try {
|
|
4695
|
-
unlinkSync5(
|
|
5051
|
+
unlinkSync5(path10.join(CACHE_DIR, "active-agent-undefined.json"));
|
|
4696
5052
|
} catch {
|
|
4697
5053
|
}
|
|
4698
5054
|
}
|
|
@@ -4704,16 +5060,16 @@ var init_active_agent = __esm({
|
|
|
4704
5060
|
init_session_key();
|
|
4705
5061
|
init_agent_context();
|
|
4706
5062
|
init_employees();
|
|
4707
|
-
CACHE_DIR =
|
|
5063
|
+
CACHE_DIR = path10.join(EXE_AI_DIR, "session-cache");
|
|
4708
5064
|
STALE_MS = 24 * 60 * 60 * 1e3;
|
|
4709
5065
|
}
|
|
4710
5066
|
});
|
|
4711
5067
|
|
|
4712
5068
|
// src/lib/agent-symlinks.ts
|
|
4713
5069
|
import os7 from "os";
|
|
4714
|
-
import
|
|
5070
|
+
import path11 from "path";
|
|
4715
5071
|
import {
|
|
4716
|
-
existsSync as
|
|
5072
|
+
existsSync as existsSync11,
|
|
4717
5073
|
lstatSync,
|
|
4718
5074
|
mkdirSync as mkdirSync6,
|
|
4719
5075
|
readlinkSync as readlinkSync2,
|
|
@@ -4741,8 +5097,8 @@ var init_mcp_prefix = __esm({
|
|
|
4741
5097
|
});
|
|
4742
5098
|
|
|
4743
5099
|
// src/lib/preferences.ts
|
|
4744
|
-
import { existsSync as
|
|
4745
|
-
import
|
|
5100
|
+
import { existsSync as existsSync12, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
5101
|
+
import path12 from "path";
|
|
4746
5102
|
import os8 from "os";
|
|
4747
5103
|
var init_preferences = __esm({
|
|
4748
5104
|
"src/lib/preferences.ts"() {
|
|
@@ -4752,10 +5108,20 @@ var init_preferences = __esm({
|
|
|
4752
5108
|
});
|
|
4753
5109
|
|
|
4754
5110
|
// src/adapters/mcp-http-config.ts
|
|
4755
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
5111
|
+
import { chmodSync as chmodSync3, existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
4756
5112
|
import { randomBytes } from "crypto";
|
|
4757
|
-
import
|
|
5113
|
+
import path13 from "path";
|
|
4758
5114
|
import os9 from "os";
|
|
5115
|
+
function resolveDefaultAgentId() {
|
|
5116
|
+
if (process.env.AGENT_ID && process.env.AGENT_ID !== "default") return process.env.AGENT_ID;
|
|
5117
|
+
try {
|
|
5118
|
+
const { loadEmployeesSync: loadEmployeesSync2 } = (init_employees(), __toCommonJS(employees_exports));
|
|
5119
|
+
const coo = loadEmployeesSync2().find((e) => e.role === "COO");
|
|
5120
|
+
if (coo) return coo.name;
|
|
5121
|
+
} catch {
|
|
5122
|
+
}
|
|
5123
|
+
return "exe";
|
|
5124
|
+
}
|
|
4759
5125
|
function mcpHttpPort() {
|
|
4760
5126
|
return process.env.EXE_MCP_PORT || DEFAULT_MCP_HTTP_PORT;
|
|
4761
5127
|
}
|
|
@@ -4763,18 +5129,18 @@ function mcpHttpUrl() {
|
|
|
4763
5129
|
return `http://127.0.0.1:${mcpHttpPort()}/mcp`;
|
|
4764
5130
|
}
|
|
4765
5131
|
function readOrCreateDaemonToken(homeDir = os9.homedir()) {
|
|
4766
|
-
const exeDir =
|
|
4767
|
-
const tokenPath =
|
|
4768
|
-
if (
|
|
5132
|
+
const exeDir = path13.join(homeDir, ".exe-os");
|
|
5133
|
+
const tokenPath = path13.join(exeDir, "exed.token");
|
|
5134
|
+
if (existsSync13(tokenPath)) {
|
|
4769
5135
|
try {
|
|
4770
|
-
const token2 =
|
|
5136
|
+
const token2 = readFileSync8(tokenPath, "utf-8").trim();
|
|
4771
5137
|
if (/^[a-f0-9]{64}$/i.test(token2)) return token2;
|
|
4772
5138
|
} catch {
|
|
4773
5139
|
}
|
|
4774
5140
|
}
|
|
4775
5141
|
const token = randomBytes(32).toString("hex");
|
|
4776
5142
|
mkdirSync7(exeDir, { recursive: true });
|
|
4777
|
-
|
|
5143
|
+
writeFileSync7(tokenPath, `${token}
|
|
4778
5144
|
`, "utf-8");
|
|
4779
5145
|
try {
|
|
4780
5146
|
chmodSync3(tokenPath, 384);
|
|
@@ -4785,18 +5151,21 @@ function readOrCreateDaemonToken(homeDir = os9.homedir()) {
|
|
|
4785
5151
|
function buildMcpHttpHeaders(homeDir = os9.homedir(), opts = {}) {
|
|
4786
5152
|
const agentId = opts.useShellPlaceholders ? "${AGENT_ID:-exe}" : opts.agentId ?? DEFAULT_MCP_HTTP_AGENT_ID;
|
|
4787
5153
|
const agentRole = opts.useShellPlaceholders ? "${AGENT_ROLE:-COO}" : opts.agentRole ?? DEFAULT_MCP_HTTP_AGENT_ROLE;
|
|
4788
|
-
|
|
5154
|
+
const sessionName = opts.useShellPlaceholders ? "$(tmux display-message -p '#{session_name}' 2>/dev/null || echo '')" : process.env.EXE_SESSION_NAME ?? "";
|
|
5155
|
+
const headers = {
|
|
4789
5156
|
Authorization: `Bearer ${readOrCreateDaemonToken(homeDir)}`,
|
|
4790
5157
|
"X-Agent-Id": agentId,
|
|
4791
5158
|
"X-Agent-Role": agentRole
|
|
4792
5159
|
};
|
|
5160
|
+
if (sessionName) headers["X-Exe-Session"] = sessionName;
|
|
5161
|
+
return headers;
|
|
4793
5162
|
}
|
|
4794
5163
|
var DEFAULT_MCP_HTTP_PORT, DEFAULT_MCP_HTTP_AGENT_ID, DEFAULT_MCP_HTTP_AGENT_ROLE;
|
|
4795
5164
|
var init_mcp_http_config = __esm({
|
|
4796
5165
|
"src/adapters/mcp-http-config.ts"() {
|
|
4797
5166
|
"use strict";
|
|
4798
5167
|
DEFAULT_MCP_HTTP_PORT = "48739";
|
|
4799
|
-
DEFAULT_MCP_HTTP_AGENT_ID =
|
|
5168
|
+
DEFAULT_MCP_HTTP_AGENT_ID = resolveDefaultAgentId();
|
|
4800
5169
|
DEFAULT_MCP_HTTP_AGENT_ROLE = "COO";
|
|
4801
5170
|
}
|
|
4802
5171
|
});
|
|
@@ -4944,28 +5313,28 @@ var init_runtime_hook_manifest = __esm({
|
|
|
4944
5313
|
|
|
4945
5314
|
// src/adapters/claude/installer.ts
|
|
4946
5315
|
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir, rm } from "fs/promises";
|
|
4947
|
-
import { existsSync as
|
|
5316
|
+
import { existsSync as existsSync14, readFileSync as readFileSync9, writeFileSync as writeFileSync8, copyFileSync as copyFileSync2, mkdirSync as mkdirSync8 } from "fs";
|
|
4948
5317
|
import { createHash as createHash4 } from "crypto";
|
|
4949
|
-
import
|
|
5318
|
+
import path14 from "path";
|
|
4950
5319
|
import os10 from "os";
|
|
4951
5320
|
import { execSync as execSync6 } from "child_process";
|
|
4952
5321
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4953
5322
|
function resolvePackageRoot() {
|
|
4954
5323
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4955
|
-
let dir =
|
|
4956
|
-
const root =
|
|
5324
|
+
let dir = path14.dirname(thisFile);
|
|
5325
|
+
const root = path14.parse(dir).root;
|
|
4957
5326
|
while (dir !== root) {
|
|
4958
|
-
const pkgPath =
|
|
4959
|
-
if (
|
|
5327
|
+
const pkgPath = path14.join(dir, "package.json");
|
|
5328
|
+
if (existsSync14(pkgPath)) {
|
|
4960
5329
|
try {
|
|
4961
|
-
const pkg = JSON.parse(
|
|
5330
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
4962
5331
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
4963
5332
|
} catch {
|
|
4964
5333
|
}
|
|
4965
5334
|
}
|
|
4966
|
-
dir =
|
|
5335
|
+
dir = path14.dirname(dir);
|
|
4967
5336
|
}
|
|
4968
|
-
return
|
|
5337
|
+
return path14.resolve(path14.dirname(thisFile), "..", "..", "..");
|
|
4969
5338
|
}
|
|
4970
5339
|
var EXE_SECTION_START, EXE_SECTION_END, ORCHESTRATION_RULES;
|
|
4971
5340
|
var init_installer = __esm({
|
|
@@ -5185,16 +5554,16 @@ __export(installer_exports, {
|
|
|
5185
5554
|
verifyOpenCodeHooks: () => verifyOpenCodeHooks
|
|
5186
5555
|
});
|
|
5187
5556
|
import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
|
|
5188
|
-
import { existsSync as
|
|
5189
|
-
import
|
|
5557
|
+
import { existsSync as existsSync15, readFileSync as readFileSync10 } from "fs";
|
|
5558
|
+
import path15 from "path";
|
|
5190
5559
|
import os11 from "os";
|
|
5191
5560
|
async function registerOpenCodeMcp(packageRoot, homeDir = os11.homedir()) {
|
|
5192
5561
|
void packageRoot;
|
|
5193
|
-
const configDir =
|
|
5194
|
-
const configPath =
|
|
5562
|
+
const configDir = path15.join(homeDir, ".config", "opencode");
|
|
5563
|
+
const configPath = path15.join(configDir, "opencode.json");
|
|
5195
5564
|
await mkdir5(configDir, { recursive: true });
|
|
5196
5565
|
let config = {};
|
|
5197
|
-
if (
|
|
5566
|
+
if (existsSync15(configPath)) {
|
|
5198
5567
|
try {
|
|
5199
5568
|
config = JSON.parse(await readFile5(configPath, "utf-8"));
|
|
5200
5569
|
} catch {
|
|
@@ -5222,14 +5591,14 @@ async function registerOpenCodeMcp(packageRoot, homeDir = os11.homedir()) {
|
|
|
5222
5591
|
return true;
|
|
5223
5592
|
}
|
|
5224
5593
|
async function installOpenCodePlugin(packageRoot, homeDir = os11.homedir()) {
|
|
5225
|
-
const pluginDir =
|
|
5226
|
-
const pluginPath =
|
|
5594
|
+
const pluginDir = path15.join(homeDir, ".config", "opencode", "plugins");
|
|
5595
|
+
const pluginPath = path15.join(pluginDir, "exe-os.mjs");
|
|
5227
5596
|
await mkdir5(pluginDir, { recursive: true });
|
|
5228
5597
|
const pluginContent = PLUGIN_TEMPLATE.replace(
|
|
5229
5598
|
/__PACKAGE_ROOT__/g,
|
|
5230
5599
|
packageRoot.replace(/\\/g, "\\\\")
|
|
5231
5600
|
);
|
|
5232
|
-
if (
|
|
5601
|
+
if (existsSync15(pluginPath)) {
|
|
5233
5602
|
const existing = await readFile5(pluginPath, "utf-8");
|
|
5234
5603
|
if (existing === pluginContent) {
|
|
5235
5604
|
return false;
|
|
@@ -5239,18 +5608,18 @@ async function installOpenCodePlugin(packageRoot, homeDir = os11.homedir()) {
|
|
|
5239
5608
|
return true;
|
|
5240
5609
|
}
|
|
5241
5610
|
function verifyOpenCodeHooks(homeDir = os11.homedir()) {
|
|
5242
|
-
const configPath =
|
|
5243
|
-
const pluginPath =
|
|
5244
|
-
if (!
|
|
5611
|
+
const configPath = path15.join(homeDir, ".config", "opencode", "opencode.json");
|
|
5612
|
+
const pluginPath = path15.join(homeDir, ".config", "opencode", "plugins", "exe-os.mjs");
|
|
5613
|
+
if (!existsSync15(configPath)) return false;
|
|
5245
5614
|
try {
|
|
5246
|
-
const config = JSON.parse(
|
|
5615
|
+
const config = JSON.parse(readFileSync10(configPath, "utf-8"));
|
|
5247
5616
|
if (!config.mcp?.["exe-os"]?.enabled) return false;
|
|
5248
5617
|
} catch {
|
|
5249
5618
|
return false;
|
|
5250
5619
|
}
|
|
5251
|
-
if (!
|
|
5620
|
+
if (!existsSync15(pluginPath)) return false;
|
|
5252
5621
|
try {
|
|
5253
|
-
const plugin =
|
|
5622
|
+
const plugin = readFileSync10(pluginPath, "utf-8");
|
|
5254
5623
|
if (!plugin.includes(EXE_HOOK_FILES.postToolCombined)) return false;
|
|
5255
5624
|
if (textHasLegacySplitPostToolHook(plugin)) return false;
|
|
5256
5625
|
} catch {
|
|
@@ -5283,11 +5652,11 @@ var init_installer2 = __esm({
|
|
|
5283
5652
|
|
|
5284
5653
|
// src/bin/exe-start-opencode.ts
|
|
5285
5654
|
import os12 from "os";
|
|
5286
|
-
import
|
|
5655
|
+
import path16 from "path";
|
|
5287
5656
|
import {
|
|
5288
|
-
existsSync as
|
|
5289
|
-
readFileSync as
|
|
5290
|
-
writeFileSync as
|
|
5657
|
+
existsSync as existsSync16,
|
|
5658
|
+
readFileSync as readFileSync11,
|
|
5659
|
+
writeFileSync as writeFileSync9,
|
|
5291
5660
|
mkdirSync as mkdirSync9,
|
|
5292
5661
|
readdirSync as readdirSync4
|
|
5293
5662
|
} from "fs";
|
|
@@ -5299,18 +5668,18 @@ init_database();
|
|
|
5299
5668
|
|
|
5300
5669
|
// src/lib/keychain.ts
|
|
5301
5670
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
5302
|
-
import { existsSync as
|
|
5671
|
+
import { existsSync as existsSync8, statSync as statSync3 } from "fs";
|
|
5303
5672
|
import { execSync as execSync3 } from "child_process";
|
|
5304
|
-
import
|
|
5673
|
+
import path7 from "path";
|
|
5305
5674
|
import os5 from "os";
|
|
5306
5675
|
var SERVICE = "exe-os";
|
|
5307
5676
|
var LEGACY_SERVICE = "exe-mem";
|
|
5308
5677
|
var ACCOUNT = "master-key";
|
|
5309
5678
|
function getKeyDir() {
|
|
5310
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
5679
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
|
|
5311
5680
|
}
|
|
5312
5681
|
function getKeyPath() {
|
|
5313
|
-
return
|
|
5682
|
+
return path7.join(getKeyDir(), "master.key");
|
|
5314
5683
|
}
|
|
5315
5684
|
function nativeKeychainAllowed() {
|
|
5316
5685
|
return process.env.EXE_OS_DISABLE_NATIVE_KEYCHAIN !== "1";
|
|
@@ -5342,7 +5711,7 @@ function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
|
5342
5711
|
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
5343
5712
|
if (uid === 0) return true;
|
|
5344
5713
|
const exeOsDir = process.env.EXE_OS_DIR;
|
|
5345
|
-
return Boolean(exeOsDir &&
|
|
5714
|
+
return Boolean(exeOsDir && path7.resolve(keyPath).startsWith(path7.resolve(exeOsDir) + path7.sep));
|
|
5346
5715
|
} catch {
|
|
5347
5716
|
return false;
|
|
5348
5717
|
}
|
|
@@ -5455,8 +5824,8 @@ function deriveMachineKey() {
|
|
|
5455
5824
|
}
|
|
5456
5825
|
function readMachineId() {
|
|
5457
5826
|
try {
|
|
5458
|
-
const { readFileSync:
|
|
5459
|
-
return
|
|
5827
|
+
const { readFileSync: readFileSync12 } = __require("fs");
|
|
5828
|
+
return readFileSync12("/etc/machine-id", "utf-8").trim();
|
|
5460
5829
|
} catch {
|
|
5461
5830
|
return "";
|
|
5462
5831
|
}
|
|
@@ -5540,7 +5909,7 @@ async function getMasterKey() {
|
|
|
5540
5909
|
}
|
|
5541
5910
|
}
|
|
5542
5911
|
const keyPath = getKeyPath();
|
|
5543
|
-
if (!
|
|
5912
|
+
if (!existsSync8(keyPath)) {
|
|
5544
5913
|
process.stderr.write(
|
|
5545
5914
|
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
5546
5915
|
`
|
|
@@ -6024,14 +6393,14 @@ function vectorToBlob(vector) {
|
|
|
6024
6393
|
|
|
6025
6394
|
// src/lib/behaviors-export.ts
|
|
6026
6395
|
import os6 from "os";
|
|
6027
|
-
import
|
|
6396
|
+
import path9 from "path";
|
|
6028
6397
|
import {
|
|
6029
|
-
existsSync as
|
|
6398
|
+
existsSync as existsSync10,
|
|
6030
6399
|
mkdirSync as mkdirSync4,
|
|
6031
6400
|
readdirSync as readdirSync2,
|
|
6032
6401
|
statSync as statSync5,
|
|
6033
6402
|
unlinkSync as unlinkSync4,
|
|
6034
|
-
writeFileSync as
|
|
6403
|
+
writeFileSync as writeFileSync4
|
|
6035
6404
|
} from "fs";
|
|
6036
6405
|
|
|
6037
6406
|
// src/lib/behaviors.ts
|
|
@@ -6077,7 +6446,7 @@ async function listBehaviors(agentId, projectName, limit = 30) {
|
|
|
6077
6446
|
}
|
|
6078
6447
|
|
|
6079
6448
|
// src/lib/behaviors-export.ts
|
|
6080
|
-
var BEHAVIORS_EXPORT_DIR =
|
|
6449
|
+
var BEHAVIORS_EXPORT_DIR = path9.join(
|
|
6081
6450
|
os6.homedir(),
|
|
6082
6451
|
".exe-os",
|
|
6083
6452
|
"behaviors-export"
|
|
@@ -6094,7 +6463,7 @@ function getBehaviorLimit() {
|
|
|
6094
6463
|
}
|
|
6095
6464
|
}
|
|
6096
6465
|
function sweepStaleBehaviorExports(now = Date.now()) {
|
|
6097
|
-
if (!
|
|
6466
|
+
if (!existsSync10(BEHAVIORS_EXPORT_DIR)) return;
|
|
6098
6467
|
let entries;
|
|
6099
6468
|
try {
|
|
6100
6469
|
entries = readdirSync2(BEHAVIORS_EXPORT_DIR);
|
|
@@ -6102,7 +6471,7 @@ function sweepStaleBehaviorExports(now = Date.now()) {
|
|
|
6102
6471
|
return;
|
|
6103
6472
|
}
|
|
6104
6473
|
for (const entry of entries) {
|
|
6105
|
-
const filePath =
|
|
6474
|
+
const filePath = path9.join(BEHAVIORS_EXPORT_DIR, entry);
|
|
6106
6475
|
try {
|
|
6107
6476
|
const stat = statSync5(filePath);
|
|
6108
6477
|
if (now - stat.mtimeMs > STALE_EXPORT_AGE_MS) {
|
|
@@ -6135,10 +6504,10 @@ function renderBehaviorExport(behaviors) {
|
|
|
6135
6504
|
}
|
|
6136
6505
|
function exportFilePath(agentId, projectName, sessionKey) {
|
|
6137
6506
|
if (!sessionKey) {
|
|
6138
|
-
return
|
|
6507
|
+
return path9.join(BEHAVIORS_EXPORT_DIR, `${agentId}.md`);
|
|
6139
6508
|
}
|
|
6140
6509
|
const safeProject = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
6141
|
-
return
|
|
6510
|
+
return path9.join(
|
|
6142
6511
|
BEHAVIORS_EXPORT_DIR,
|
|
6143
6512
|
`${agentId}-${safeProject}-${sessionKey}.md`
|
|
6144
6513
|
);
|
|
@@ -6150,7 +6519,7 @@ async function exportBehaviorsForAgent(agentId, projectName, sessionKey) {
|
|
|
6150
6519
|
if (behaviors.length === 0) return null;
|
|
6151
6520
|
const body = renderBehaviorExport(behaviors);
|
|
6152
6521
|
const target = exportFilePath(agentId, projectName, sessionKey);
|
|
6153
|
-
|
|
6522
|
+
writeFileSync4(target, body, "utf-8");
|
|
6154
6523
|
return target;
|
|
6155
6524
|
}
|
|
6156
6525
|
|
|
@@ -6167,7 +6536,7 @@ When done with a task: call update_task with status "done".
|
|
|
6167
6536
|
Always call store_memory to persist important findings.
|
|
6168
6537
|
`;
|
|
6169
6538
|
function resolveAgent(argv) {
|
|
6170
|
-
const invokedAs =
|
|
6539
|
+
const invokedAs = path16.basename(argv[1] ?? "");
|
|
6171
6540
|
if (invokedAs && invokedAs !== "exe-start-opencode" && !invokedAs.endsWith(".js")) {
|
|
6172
6541
|
const agent2 = invokedAs.replace(/-opencode$/, "").toLowerCase();
|
|
6173
6542
|
return { agent: agent2, passthrough: argv.slice(2) };
|
|
@@ -6184,24 +6553,24 @@ function resolveAgent(argv) {
|
|
|
6184
6553
|
return { agent, passthrough };
|
|
6185
6554
|
}
|
|
6186
6555
|
function loadIdentity(agent) {
|
|
6187
|
-
const dir =
|
|
6188
|
-
const exact =
|
|
6189
|
-
if (
|
|
6190
|
-
const content =
|
|
6556
|
+
const dir = path16.join(os12.homedir(), ".exe-os", "identity");
|
|
6557
|
+
const exact = path16.join(dir, `${agent}.md`);
|
|
6558
|
+
if (existsSync16(exact)) {
|
|
6559
|
+
const content = readFileSync11(exact, "utf-8").trim();
|
|
6191
6560
|
if (content) return content;
|
|
6192
6561
|
}
|
|
6193
6562
|
try {
|
|
6194
6563
|
const files = readdirSync4(dir);
|
|
6195
6564
|
const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
|
|
6196
6565
|
if (match) {
|
|
6197
|
-
const content =
|
|
6566
|
+
const content = readFileSync11(path16.join(dir, match), "utf-8").trim();
|
|
6198
6567
|
if (content) return content;
|
|
6199
6568
|
}
|
|
6200
6569
|
} catch {
|
|
6201
6570
|
}
|
|
6202
6571
|
try {
|
|
6203
|
-
const rosterPath =
|
|
6204
|
-
const roster = JSON.parse(
|
|
6572
|
+
const rosterPath = path16.join(os12.homedir(), ".exe-os", "exe-employees.json");
|
|
6573
|
+
const roster = JSON.parse(readFileSync11(rosterPath, "utf8"));
|
|
6205
6574
|
const emp = roster.find((e) => e.name.toLowerCase() === agent.toLowerCase());
|
|
6206
6575
|
if (emp?.systemPrompt && emp.systemPrompt.trim().length > 20) {
|
|
6207
6576
|
return emp.systemPrompt;
|
|
@@ -6211,18 +6580,18 @@ function loadIdentity(agent) {
|
|
|
6211
6580
|
return null;
|
|
6212
6581
|
}
|
|
6213
6582
|
function writeAgentFile(agent, identity, behaviorsPath) {
|
|
6214
|
-
const agentDir =
|
|
6583
|
+
const agentDir = path16.join(os12.homedir(), ".config", "opencode", "agents");
|
|
6215
6584
|
mkdirSync9(agentDir, { recursive: true });
|
|
6216
6585
|
let content = identity;
|
|
6217
|
-
if (behaviorsPath &&
|
|
6218
|
-
const behaviors =
|
|
6586
|
+
if (behaviorsPath && existsSync16(behaviorsPath)) {
|
|
6587
|
+
const behaviors = readFileSync11(behaviorsPath, "utf-8").trim();
|
|
6219
6588
|
if (behaviors) {
|
|
6220
6589
|
content += "\n\n" + behaviors;
|
|
6221
6590
|
}
|
|
6222
6591
|
}
|
|
6223
6592
|
content += "\n" + BOOT_INSTRUCTIONS;
|
|
6224
|
-
const outPath =
|
|
6225
|
-
|
|
6593
|
+
const outPath = path16.join(agentDir, `${agent}.md`);
|
|
6594
|
+
writeFileSync9(outPath, content, "utf-8");
|
|
6226
6595
|
return outPath;
|
|
6227
6596
|
}
|
|
6228
6597
|
async function main() {
|
|
@@ -6283,8 +6652,8 @@ async function main() {
|
|
|
6283
6652
|
process.env.EXE_RUNTIME = "opencode";
|
|
6284
6653
|
const empRole = (() => {
|
|
6285
6654
|
try {
|
|
6286
|
-
const emps =
|
|
6287
|
-
|
|
6655
|
+
const emps = readFileSync11(
|
|
6656
|
+
path16.join(os12.homedir(), ".exe-os", "exe-employees.json"),
|
|
6288
6657
|
"utf-8"
|
|
6289
6658
|
);
|
|
6290
6659
|
const found = JSON.parse(emps).find(
|