@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
|
@@ -337,11 +337,168 @@ var init_config = __esm({
|
|
|
337
337
|
}
|
|
338
338
|
});
|
|
339
339
|
|
|
340
|
+
// src/lib/runtime-table.ts
|
|
341
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
342
|
+
var init_runtime_table = __esm({
|
|
343
|
+
"src/lib/runtime-table.ts"() {
|
|
344
|
+
"use strict";
|
|
345
|
+
RUNTIME_TABLE = {
|
|
346
|
+
codex: {
|
|
347
|
+
binary: "codex",
|
|
348
|
+
launchMode: "interactive",
|
|
349
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
350
|
+
inlineFlag: "--no-alt-screen",
|
|
351
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
352
|
+
defaultModel: "gpt-5.5"
|
|
353
|
+
},
|
|
354
|
+
opencode: {
|
|
355
|
+
binary: "opencode",
|
|
356
|
+
launchMode: "exec",
|
|
357
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
358
|
+
inlineFlag: "",
|
|
359
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
360
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
DEFAULT_RUNTIME = "claude";
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// src/lib/agent-config.ts
|
|
368
|
+
var agent_config_exports = {};
|
|
369
|
+
__export(agent_config_exports, {
|
|
370
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
371
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
372
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
373
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
374
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
375
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
376
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
377
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
378
|
+
setAgentMcps: () => setAgentMcps,
|
|
379
|
+
setAgentRuntime: () => setAgentRuntime
|
|
380
|
+
});
|
|
381
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
382
|
+
import path2 from "path";
|
|
383
|
+
function loadAgentConfig() {
|
|
384
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
385
|
+
try {
|
|
386
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
387
|
+
} catch {
|
|
388
|
+
return {};
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function saveAgentConfig(config) {
|
|
392
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
393
|
+
ensurePrivateDirSync(dir);
|
|
394
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
395
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
396
|
+
}
|
|
397
|
+
function getAgentRuntime(agentId) {
|
|
398
|
+
const config = loadAgentConfig();
|
|
399
|
+
const entry = config[agentId];
|
|
400
|
+
if (entry) return entry;
|
|
401
|
+
const orgDefault = config["default"];
|
|
402
|
+
if (orgDefault) return orgDefault;
|
|
403
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
404
|
+
}
|
|
405
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
406
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
407
|
+
if (!knownModels) {
|
|
408
|
+
return {
|
|
409
|
+
ok: false,
|
|
410
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
if (!knownModels.includes(model)) {
|
|
414
|
+
return {
|
|
415
|
+
ok: false,
|
|
416
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
const config = loadAgentConfig();
|
|
420
|
+
const existing = config[agentId];
|
|
421
|
+
const entry = { runtime, model };
|
|
422
|
+
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
423
|
+
if (mcps !== void 0) {
|
|
424
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
425
|
+
} else if (existing?.mcps) {
|
|
426
|
+
entry.mcps = existing.mcps;
|
|
427
|
+
}
|
|
428
|
+
config[agentId] = entry;
|
|
429
|
+
saveAgentConfig(config);
|
|
430
|
+
return { ok: true };
|
|
431
|
+
}
|
|
432
|
+
function setAgentMcps(agentId, mcps) {
|
|
433
|
+
const config = loadAgentConfig();
|
|
434
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
435
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
436
|
+
config[agentId] = existing;
|
|
437
|
+
saveAgentConfig(config);
|
|
438
|
+
return { ok: true };
|
|
439
|
+
}
|
|
440
|
+
function clearAgentRuntime(agentId) {
|
|
441
|
+
const config = loadAgentConfig();
|
|
442
|
+
delete config[agentId];
|
|
443
|
+
saveAgentConfig(config);
|
|
444
|
+
}
|
|
445
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
446
|
+
var init_agent_config = __esm({
|
|
447
|
+
"src/lib/agent-config.ts"() {
|
|
448
|
+
"use strict";
|
|
449
|
+
init_config();
|
|
450
|
+
init_runtime_table();
|
|
451
|
+
init_secure_files();
|
|
452
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
453
|
+
KNOWN_RUNTIMES = {
|
|
454
|
+
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
455
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
456
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
457
|
+
};
|
|
458
|
+
RUNTIME_LABELS = {
|
|
459
|
+
claude: "Claude Code (Anthropic)",
|
|
460
|
+
codex: "Codex (OpenAI)",
|
|
461
|
+
opencode: "OpenCode (open source)"
|
|
462
|
+
};
|
|
463
|
+
DEFAULT_MODELS = {
|
|
464
|
+
claude: "claude-opus-4.6",
|
|
465
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
466
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
|
|
340
471
|
// src/lib/employees.ts
|
|
472
|
+
var employees_exports = {};
|
|
473
|
+
__export(employees_exports, {
|
|
474
|
+
COORDINATOR_ROLE: () => COORDINATOR_ROLE,
|
|
475
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
476
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
477
|
+
addEmployee: () => addEmployee,
|
|
478
|
+
baseAgentName: () => baseAgentName,
|
|
479
|
+
canCoordinate: () => canCoordinate,
|
|
480
|
+
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
481
|
+
getCoordinatorName: () => getCoordinatorName,
|
|
482
|
+
getEmployee: () => getEmployee,
|
|
483
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
484
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
485
|
+
hasRole: () => hasRole,
|
|
486
|
+
hireEmployee: () => hireEmployee,
|
|
487
|
+
isCoordinatorName: () => isCoordinatorName,
|
|
488
|
+
isCoordinatorRole: () => isCoordinatorRole,
|
|
489
|
+
isMultiInstance: () => isMultiInstance,
|
|
490
|
+
loadEmployees: () => loadEmployees,
|
|
491
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
492
|
+
normalizeRole: () => normalizeRole,
|
|
493
|
+
normalizeRosterCase: () => normalizeRosterCase,
|
|
494
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
495
|
+
saveEmployees: () => saveEmployees,
|
|
496
|
+
validateEmployeeName: () => validateEmployeeName
|
|
497
|
+
});
|
|
341
498
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
342
|
-
import { existsSync as
|
|
499
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
343
500
|
import { execSync } from "child_process";
|
|
344
|
-
import
|
|
501
|
+
import path3 from "path";
|
|
345
502
|
import os2 from "os";
|
|
346
503
|
function normalizeRole(role) {
|
|
347
504
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -362,8 +519,23 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
|
362
519
|
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
363
520
|
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
364
521
|
}
|
|
522
|
+
function validateEmployeeName(name) {
|
|
523
|
+
if (!name) {
|
|
524
|
+
return { valid: false, error: "Name is required" };
|
|
525
|
+
}
|
|
526
|
+
if (name.length > 32) {
|
|
527
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
528
|
+
}
|
|
529
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
530
|
+
return {
|
|
531
|
+
valid: false,
|
|
532
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
return { valid: true };
|
|
536
|
+
}
|
|
365
537
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
366
|
-
if (!
|
|
538
|
+
if (!existsSync4(employeesPath)) {
|
|
367
539
|
return [];
|
|
368
540
|
}
|
|
369
541
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -373,10 +545,14 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
373
545
|
return [];
|
|
374
546
|
}
|
|
375
547
|
}
|
|
548
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
549
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
550
|
+
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
551
|
+
}
|
|
376
552
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
377
|
-
if (!
|
|
553
|
+
if (!existsSync4(employeesPath)) return [];
|
|
378
554
|
try {
|
|
379
|
-
return JSON.parse(
|
|
555
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
380
556
|
} catch {
|
|
381
557
|
return [];
|
|
382
558
|
}
|
|
@@ -384,6 +560,19 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
|
384
560
|
function getEmployee(employees, name) {
|
|
385
561
|
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
386
562
|
}
|
|
563
|
+
function getEmployeeByRole(employees, role) {
|
|
564
|
+
const lower = role.toLowerCase();
|
|
565
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
566
|
+
}
|
|
567
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
568
|
+
const lower = role.toLowerCase();
|
|
569
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
570
|
+
}
|
|
571
|
+
function hasRole(agentName, role) {
|
|
572
|
+
const employees = loadEmployeesSync();
|
|
573
|
+
const emp = getEmployee(employees, agentName);
|
|
574
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
575
|
+
}
|
|
387
576
|
function baseAgentName(name, employees) {
|
|
388
577
|
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
389
578
|
if (!match) return name;
|
|
@@ -398,22 +587,147 @@ function isMultiInstance(agentName, employees) {
|
|
|
398
587
|
if (!emp) return false;
|
|
399
588
|
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
400
589
|
}
|
|
401
|
-
|
|
590
|
+
function addEmployee(employees, employee) {
|
|
591
|
+
const { systemPrompt: _legacyPrompt, ...rest } = employee;
|
|
592
|
+
const normalized = { ...rest, name: employee.name.toLowerCase() };
|
|
593
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
594
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
595
|
+
}
|
|
596
|
+
return [...employees, normalized];
|
|
597
|
+
}
|
|
598
|
+
function appendToCoordinatorTeam(employee) {
|
|
599
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
600
|
+
if (!coordinator) return;
|
|
601
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
602
|
+
if (!existsSync4(idPath)) return;
|
|
603
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
604
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
605
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
606
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
607
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
608
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
609
|
+
const entry = `
|
|
610
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
611
|
+
`;
|
|
612
|
+
let updated;
|
|
613
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
614
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
615
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
616
|
+
} else {
|
|
617
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
618
|
+
}
|
|
619
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
620
|
+
}
|
|
621
|
+
function capitalize(s) {
|
|
622
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
623
|
+
}
|
|
624
|
+
async function hireEmployee(employee) {
|
|
625
|
+
const employees = await loadEmployees();
|
|
626
|
+
const updated = addEmployee(employees, employee);
|
|
627
|
+
await saveEmployees(updated);
|
|
628
|
+
try {
|
|
629
|
+
appendToCoordinatorTeam(employee);
|
|
630
|
+
} catch {
|
|
631
|
+
}
|
|
632
|
+
try {
|
|
633
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
634
|
+
const config = loadAgentConfig2();
|
|
635
|
+
const name = employee.name.toLowerCase();
|
|
636
|
+
if (!config[name] && config["default"]) {
|
|
637
|
+
config[name] = { ...config["default"] };
|
|
638
|
+
saveAgentConfig2(config);
|
|
639
|
+
}
|
|
640
|
+
} catch {
|
|
641
|
+
}
|
|
642
|
+
return updated;
|
|
643
|
+
}
|
|
644
|
+
async function normalizeRosterCase(rosterPath) {
|
|
645
|
+
const employees = await loadEmployees(rosterPath);
|
|
646
|
+
let changed = false;
|
|
647
|
+
for (const emp of employees) {
|
|
648
|
+
if (emp.name !== emp.name.toLowerCase()) {
|
|
649
|
+
const oldName = emp.name;
|
|
650
|
+
emp.name = emp.name.toLowerCase();
|
|
651
|
+
changed = true;
|
|
652
|
+
try {
|
|
653
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
654
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
655
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
656
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
657
|
+
renameSync2(oldPath, newPath);
|
|
658
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
659
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
660
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
661
|
+
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
662
|
+
unlinkSync(oldPath);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
} catch {
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
if (changed) {
|
|
670
|
+
await saveEmployees(employees, rosterPath);
|
|
671
|
+
}
|
|
672
|
+
return changed;
|
|
673
|
+
}
|
|
674
|
+
function findExeBin() {
|
|
675
|
+
try {
|
|
676
|
+
return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
677
|
+
} catch {
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
function registerBinSymlinks(name) {
|
|
682
|
+
const created = [];
|
|
683
|
+
const skipped = [];
|
|
684
|
+
const errors = [];
|
|
685
|
+
const exeBinPath = findExeBin();
|
|
686
|
+
if (!exeBinPath) {
|
|
687
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
688
|
+
return { created, skipped, errors };
|
|
689
|
+
}
|
|
690
|
+
const binDir = path3.dirname(exeBinPath);
|
|
691
|
+
let target;
|
|
692
|
+
try {
|
|
693
|
+
target = readlinkSync(exeBinPath);
|
|
694
|
+
} catch {
|
|
695
|
+
errors.push("Could not read 'exe' symlink");
|
|
696
|
+
return { created, skipped, errors };
|
|
697
|
+
}
|
|
698
|
+
for (const suffix of ["", "-opencode"]) {
|
|
699
|
+
const linkName = `${name}${suffix}`;
|
|
700
|
+
const linkPath = path3.join(binDir, linkName);
|
|
701
|
+
if (existsSync4(linkPath)) {
|
|
702
|
+
skipped.push(linkName);
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
try {
|
|
706
|
+
symlinkSync(target, linkPath);
|
|
707
|
+
created.push(linkName);
|
|
708
|
+
} catch (err) {
|
|
709
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return { created, skipped, errors };
|
|
713
|
+
}
|
|
714
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
402
715
|
var init_employees = __esm({
|
|
403
716
|
"src/lib/employees.ts"() {
|
|
404
717
|
"use strict";
|
|
405
718
|
init_config();
|
|
406
|
-
EMPLOYEES_PATH =
|
|
719
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
407
720
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
408
721
|
COORDINATOR_ROLE = "COO";
|
|
409
722
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
410
|
-
IDENTITY_DIR =
|
|
723
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
724
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
411
725
|
}
|
|
412
726
|
});
|
|
413
727
|
|
|
414
728
|
// src/lib/database-adapter.ts
|
|
415
729
|
import os3 from "os";
|
|
416
|
-
import
|
|
730
|
+
import path4 from "path";
|
|
417
731
|
import { createRequire } from "module";
|
|
418
732
|
import { pathToFileURL } from "url";
|
|
419
733
|
var BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES;
|
|
@@ -442,7 +756,7 @@ var init_memory = __esm({
|
|
|
442
756
|
});
|
|
443
757
|
|
|
444
758
|
// src/lib/database.ts
|
|
445
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
759
|
+
import { chmodSync as chmodSync2, existsSync as existsSync5, statSync, copyFileSync, unlinkSync as unlinkSync2, openSync, closeSync, mkdirSync as mkdirSync2 } from "fs";
|
|
446
760
|
import { createClient } from "@libsql/client";
|
|
447
761
|
import { homedir } from "os";
|
|
448
762
|
import { join } from "path";
|
|
@@ -495,12 +809,12 @@ var init_database = __esm({
|
|
|
495
809
|
});
|
|
496
810
|
|
|
497
811
|
// src/lib/session-registry.ts
|
|
498
|
-
import { readFileSync as
|
|
499
|
-
import
|
|
812
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync6 } from "fs";
|
|
813
|
+
import path5 from "path";
|
|
500
814
|
import os4 from "os";
|
|
501
815
|
function registerSession(entry) {
|
|
502
|
-
const dir =
|
|
503
|
-
if (!
|
|
816
|
+
const dir = path5.dirname(REGISTRY_PATH);
|
|
817
|
+
if (!existsSync6(dir)) {
|
|
504
818
|
mkdirSync3(dir, { recursive: true });
|
|
505
819
|
}
|
|
506
820
|
const sessions = listSessions();
|
|
@@ -510,11 +824,11 @@ function registerSession(entry) {
|
|
|
510
824
|
} else {
|
|
511
825
|
sessions.push(entry);
|
|
512
826
|
}
|
|
513
|
-
|
|
827
|
+
writeFileSync3(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
514
828
|
}
|
|
515
829
|
function listSessions() {
|
|
516
830
|
try {
|
|
517
|
-
const raw =
|
|
831
|
+
const raw = readFileSync4(REGISTRY_PATH, "utf8");
|
|
518
832
|
return JSON.parse(raw);
|
|
519
833
|
} catch {
|
|
520
834
|
return [];
|
|
@@ -524,7 +838,7 @@ var REGISTRY_PATH;
|
|
|
524
838
|
var init_session_registry = __esm({
|
|
525
839
|
"src/lib/session-registry.ts"() {
|
|
526
840
|
"use strict";
|
|
527
|
-
REGISTRY_PATH =
|
|
841
|
+
REGISTRY_PATH = path5.join(os4.homedir(), ".exe-os", "session-registry.json");
|
|
528
842
|
}
|
|
529
843
|
});
|
|
530
844
|
|
|
@@ -786,68 +1100,6 @@ var init_provider_table = __esm({
|
|
|
786
1100
|
}
|
|
787
1101
|
});
|
|
788
1102
|
|
|
789
|
-
// src/lib/runtime-table.ts
|
|
790
|
-
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
791
|
-
var init_runtime_table = __esm({
|
|
792
|
-
"src/lib/runtime-table.ts"() {
|
|
793
|
-
"use strict";
|
|
794
|
-
RUNTIME_TABLE = {
|
|
795
|
-
codex: {
|
|
796
|
-
binary: "codex",
|
|
797
|
-
launchMode: "interactive",
|
|
798
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
799
|
-
inlineFlag: "--no-alt-screen",
|
|
800
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
801
|
-
defaultModel: "gpt-5.5"
|
|
802
|
-
},
|
|
803
|
-
opencode: {
|
|
804
|
-
binary: "opencode",
|
|
805
|
-
launchMode: "exec",
|
|
806
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
807
|
-
inlineFlag: "",
|
|
808
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
809
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
810
|
-
}
|
|
811
|
-
};
|
|
812
|
-
DEFAULT_RUNTIME = "claude";
|
|
813
|
-
}
|
|
814
|
-
});
|
|
815
|
-
|
|
816
|
-
// src/lib/agent-config.ts
|
|
817
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync6 } from "fs";
|
|
818
|
-
import path5 from "path";
|
|
819
|
-
function loadAgentConfig() {
|
|
820
|
-
if (!existsSync6(AGENT_CONFIG_PATH)) return {};
|
|
821
|
-
try {
|
|
822
|
-
return JSON.parse(readFileSync4(AGENT_CONFIG_PATH, "utf-8"));
|
|
823
|
-
} catch {
|
|
824
|
-
return {};
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
function getAgentRuntime(agentId) {
|
|
828
|
-
const config = loadAgentConfig();
|
|
829
|
-
const entry = config[agentId];
|
|
830
|
-
if (entry) return entry;
|
|
831
|
-
const orgDefault = config["default"];
|
|
832
|
-
if (orgDefault) return orgDefault;
|
|
833
|
-
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
834
|
-
}
|
|
835
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
836
|
-
var init_agent_config = __esm({
|
|
837
|
-
"src/lib/agent-config.ts"() {
|
|
838
|
-
"use strict";
|
|
839
|
-
init_config();
|
|
840
|
-
init_runtime_table();
|
|
841
|
-
init_secure_files();
|
|
842
|
-
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
843
|
-
DEFAULT_MODELS = {
|
|
844
|
-
claude: "claude-opus-4.6",
|
|
845
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
846
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
});
|
|
850
|
-
|
|
851
1103
|
// src/lib/intercom-queue.ts
|
|
852
1104
|
var intercom_queue_exports = {};
|
|
853
1105
|
__export(intercom_queue_exports, {
|
|
@@ -1342,7 +1594,7 @@ async function assertVpsLicense(opts) {
|
|
|
1342
1594
|
}
|
|
1343
1595
|
if (!transientFailure) {
|
|
1344
1596
|
throw new Error(
|
|
1345
|
-
"License validation failed: unknown backend state. Restore network connectivity to https://askexe.com
|
|
1597
|
+
"License validation failed: unknown backend state. Restore network connectivity to https://cloud.askexe.com and retry."
|
|
1346
1598
|
);
|
|
1347
1599
|
}
|
|
1348
1600
|
const fresh = await getCachedLicense();
|
|
@@ -1379,7 +1631,7 @@ async function assertVpsLicense(opts) {
|
|
|
1379
1631
|
} catch {
|
|
1380
1632
|
}
|
|
1381
1633
|
throw new Error(
|
|
1382
|
-
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com
|
|
1634
|
+
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://cloud.askexe.com and retry. This VPS image refuses to boot after the offline grace window.`
|
|
1383
1635
|
);
|
|
1384
1636
|
}
|
|
1385
1637
|
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
@@ -1411,7 +1663,7 @@ var init_license = __esm({
|
|
|
1411
1663
|
LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
|
|
1412
1664
|
CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
1413
1665
|
DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
|
|
1414
|
-
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com
|
|
1666
|
+
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://cloud.askexe.com";
|
|
1415
1667
|
RETRY_DELAY_MS = 500;
|
|
1416
1668
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
1417
1669
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
@@ -2437,11 +2689,12 @@ function getDispatchedBy(sessionKey) {
|
|
|
2437
2689
|
}
|
|
2438
2690
|
}
|
|
2439
2691
|
function resolveExeSession() {
|
|
2692
|
+
if (process.env.EXE_SESSION_NAME) {
|
|
2693
|
+
const fromEnv = extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
2694
|
+
if (fromEnv) return fromEnv;
|
|
2695
|
+
}
|
|
2440
2696
|
const mySession = getMySession();
|
|
2441
2697
|
if (!mySession) {
|
|
2442
|
-
if (process.env.EXE_SESSION_NAME) {
|
|
2443
|
-
return extractRootExe(process.env.EXE_SESSION_NAME) ?? process.env.EXE_SESSION_NAME;
|
|
2444
|
-
}
|
|
2445
2698
|
return null;
|
|
2446
2699
|
}
|
|
2447
2700
|
const fromSessionName = extractRootExe(mySession);
|
|
@@ -2456,6 +2709,10 @@ function resolveExeSession() {
|
|
|
2456
2709
|
`[tmux-routing] WARN: cache says "${fromCache}" but session name says "${fromSessionName}". Trusting session name.
|
|
2457
2710
|
`
|
|
2458
2711
|
);
|
|
2712
|
+
try {
|
|
2713
|
+
registerParentExe(key, fromSessionName);
|
|
2714
|
+
} catch {
|
|
2715
|
+
}
|
|
2459
2716
|
candidate = fromSessionName;
|
|
2460
2717
|
} else {
|
|
2461
2718
|
candidate = fromCache;
|
|
@@ -3409,6 +3666,19 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
3409
3666
|
args: [identifier, ...scope.args]
|
|
3410
3667
|
});
|
|
3411
3668
|
if (result.rows.length === 1) return result.rows[0];
|
|
3669
|
+
if (/^[a-f0-9]{7,12}$/i.test(identifier)) {
|
|
3670
|
+
result = await client.execute({
|
|
3671
|
+
sql: `SELECT * FROM tasks WHERE id LIKE ?`,
|
|
3672
|
+
args: [`${identifier}%`]
|
|
3673
|
+
});
|
|
3674
|
+
if (result.rows.length === 1) return result.rows[0];
|
|
3675
|
+
if (result.rows.length > 1) {
|
|
3676
|
+
const matches = result.rows.map((r) => `${String(r.id)} "${String(r.title)}" (${String(r.status)})`).join(", ");
|
|
3677
|
+
throw new Error(
|
|
3678
|
+
`Multiple tasks match short-ID "${identifier}": ${matches}. Use a longer prefix to disambiguate.`
|
|
3679
|
+
);
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3412
3682
|
result = await client.execute({
|
|
3413
3683
|
sql: `SELECT * FROM tasks WHERE task_file LIKE ?${scope.sql}`,
|
|
3414
3684
|
args: [`%${identifier}%`, ...scope.args]
|
|
@@ -3955,12 +4225,13 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3955
4225
|
WHERE blocked_by = ? AND status = 'blocked'`,
|
|
3956
4226
|
args: [now, taskId]
|
|
3957
4227
|
});
|
|
3958
|
-
if (
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
4228
|
+
if (unblocked.rowsAffected === 0) return;
|
|
4229
|
+
const ubScope = sessionScopeFilter();
|
|
4230
|
+
const unblockedRows = await client.execute({
|
|
4231
|
+
sql: `SELECT id, title, assigned_to, priority, task_file FROM tasks WHERE blocked_by IS NULL AND updated_at = ?${ubScope.sql}`,
|
|
4232
|
+
args: [now, ...ubScope.args]
|
|
4233
|
+
});
|
|
4234
|
+
if (baseDir) {
|
|
3964
4235
|
for (const ur of unblockedRows.rows) {
|
|
3965
4236
|
try {
|
|
3966
4237
|
const ubFile = path15.join(baseDir, String(ur.task_file));
|
|
@@ -3972,6 +4243,19 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3972
4243
|
}
|
|
3973
4244
|
}
|
|
3974
4245
|
}
|
|
4246
|
+
if (unblockedRows.rows.length > 0 && !process.env.VITEST) {
|
|
4247
|
+
try {
|
|
4248
|
+
const { queueIntercom: queueIntercom2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
4249
|
+
const dispatched = /* @__PURE__ */ new Set();
|
|
4250
|
+
for (const ur of unblockedRows.rows) {
|
|
4251
|
+
const assignee = String(ur.assigned_to);
|
|
4252
|
+
if (dispatched.has(assignee)) continue;
|
|
4253
|
+
dispatched.add(assignee);
|
|
4254
|
+
queueIntercom2(`${assignee}`, `unblocked: "${String(ur.title)}" is now ready`);
|
|
4255
|
+
}
|
|
4256
|
+
} catch {
|
|
4257
|
+
}
|
|
4258
|
+
}
|
|
3975
4259
|
}
|
|
3976
4260
|
async function findNextTask(assignedTo) {
|
|
3977
4261
|
const client = getClient();
|
|
@@ -4630,6 +4914,15 @@ var init_embedder = __esm({
|
|
|
4630
4914
|
// src/lib/behaviors.ts
|
|
4631
4915
|
import crypto5 from "crypto";
|
|
4632
4916
|
async function storeBehavior(opts) {
|
|
4917
|
+
try {
|
|
4918
|
+
const { loadEmployeesSync: loadEmployeesSync2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
4919
|
+
const roster = loadEmployeesSync2();
|
|
4920
|
+
if (roster.length > 0 && !roster.some((e) => e.name === opts.agentId)) {
|
|
4921
|
+
throw new Error(`Agent "${opts.agentId}" not found in roster. Cannot store behavior for unregistered agent.`);
|
|
4922
|
+
}
|
|
4923
|
+
} catch (e) {
|
|
4924
|
+
if (e instanceof Error && e.message.includes("not found in roster")) throw e;
|
|
4925
|
+
}
|
|
4633
4926
|
const client = getClient();
|
|
4634
4927
|
const id = crypto5.randomUUID();
|
|
4635
4928
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -5083,6 +5376,12 @@ async function updateTask(input) {
|
|
|
5083
5376
|
}
|
|
5084
5377
|
}
|
|
5085
5378
|
}
|
|
5379
|
+
if (input.status === "cancelled") {
|
|
5380
|
+
try {
|
|
5381
|
+
await cascadeUnblock(taskId, input.baseDir, now);
|
|
5382
|
+
} catch {
|
|
5383
|
+
}
|
|
5384
|
+
}
|
|
5086
5385
|
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
5087
5386
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
5088
5387
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|