@askexenow/exe-os 0.9.113 → 0.9.115
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/dist/bin/agentic-ontology-backfill.js +36 -12
- package/dist/bin/agentic-reflection-backfill.js +36 -12
- package/dist/bin/agentic-semantic-label.js +36 -12
- package/dist/bin/backfill-conversations.js +36 -12
- package/dist/bin/backfill-responses.js +36 -12
- package/dist/bin/backfill-vectors.js +36 -12
- package/dist/bin/bulk-sync-postgres.js +36 -12
- package/dist/bin/cleanup-stale-review-tasks.js +470 -113
- package/dist/bin/cli.js +413 -62
- package/dist/bin/exe-agent.js +27 -0
- package/dist/bin/exe-assign.js +36 -12
- package/dist/bin/exe-boot.js +246 -54
- package/dist/bin/exe-call.js +8 -0
- package/dist/bin/exe-cloud.js +47 -12
- package/dist/bin/exe-dispatch.js +348 -53
- package/dist/bin/exe-doctor.js +51 -13
- package/dist/bin/exe-export-behaviors.js +37 -12
- package/dist/bin/exe-forget.js +36 -12
- package/dist/bin/exe-gateway.js +348 -53
- package/dist/bin/exe-heartbeat.js +471 -113
- package/dist/bin/exe-kill.js +36 -12
- package/dist/bin/exe-launch-agent.js +117 -18
- package/dist/bin/exe-new-employee.js +9 -1
- package/dist/bin/exe-pending-messages.js +452 -95
- package/dist/bin/exe-pending-notifications.js +452 -95
- package/dist/bin/exe-pending-reviews.js +452 -95
- package/dist/bin/exe-rename.js +36 -12
- package/dist/bin/exe-review.js +36 -12
- package/dist/bin/exe-search.js +37 -12
- package/dist/bin/exe-session-cleanup.js +348 -53
- package/dist/bin/exe-settings.js +12 -0
- package/dist/bin/exe-start-codex.js +46 -13
- package/dist/bin/exe-start-opencode.js +46 -13
- package/dist/bin/exe-status.js +460 -114
- package/dist/bin/exe-support.js +12 -0
- package/dist/bin/exe-team.js +36 -12
- package/dist/bin/git-sweep.js +348 -53
- package/dist/bin/graph-backfill.js +36 -12
- package/dist/bin/graph-export.js +36 -12
- package/dist/bin/install.js +9 -1
- package/dist/bin/intercom-check.js +255 -53
- package/dist/bin/scan-tasks.js +348 -53
- package/dist/bin/setup.js +74 -12
- package/dist/bin/shard-migrate.js +36 -12
- package/dist/gateway/index.js +348 -53
- package/dist/hooks/bug-report-worker.js +348 -53
- package/dist/hooks/codex-stop-task-finalizer.js +308 -37
- package/dist/hooks/commit-complete.js +348 -53
- package/dist/hooks/error-recall.js +37 -12
- package/dist/hooks/ingest.js +363 -54
- package/dist/hooks/instructions-loaded.js +36 -12
- package/dist/hooks/notification.js +36 -12
- package/dist/hooks/post-compact.js +426 -72
- package/dist/hooks/post-tool-combined.js +501 -146
- package/dist/hooks/pre-compact.js +348 -53
- package/dist/hooks/pre-tool-use.js +92 -13
- package/dist/hooks/prompt-submit.js +348 -53
- package/dist/hooks/session-end.js +158 -53
- package/dist/hooks/session-start.js +66 -13
- package/dist/hooks/stop.js +420 -72
- package/dist/hooks/subagent-stop.js +419 -72
- package/dist/hooks/summary-worker.js +442 -121
- package/dist/index.js +375 -53
- package/dist/lib/agent-config.js +8 -0
- package/dist/lib/cloud-sync.js +35 -12
- package/dist/lib/config.js +13 -0
- package/dist/lib/consolidation.js +9 -1
- package/dist/lib/embedder.js +13 -0
- package/dist/lib/employees.js +8 -0
- package/dist/lib/exe-daemon.js +524 -60
- package/dist/lib/hybrid-search.js +37 -12
- package/dist/lib/keychain.js +25 -13
- package/dist/lib/messaging.js +395 -74
- package/dist/lib/schedules.js +36 -12
- package/dist/lib/skill-learning.js +21 -0
- package/dist/lib/store.js +36 -12
- package/dist/lib/tasks.js +324 -41
- package/dist/lib/tmux-routing.js +324 -41
- package/dist/mcp/server.js +374 -54
- package/dist/mcp/tools/create-task.js +324 -41
- package/dist/mcp/tools/list-tasks.js +406 -57
- package/dist/mcp/tools/send-message.js +395 -74
- package/dist/mcp/tools/update-task.js +324 -41
- package/dist/runtime/index.js +375 -53
- package/dist/tui/App.js +377 -55
- package/package.json +1 -1
package/dist/hooks/stop.js
CHANGED
|
@@ -139,6 +139,17 @@ function normalizeOrchestration(raw) {
|
|
|
139
139
|
const userOrg = raw.orchestration ?? {};
|
|
140
140
|
raw.orchestration = { ...defaultOrg, ...userOrg };
|
|
141
141
|
}
|
|
142
|
+
function normalizeCloudEndpoint(raw) {
|
|
143
|
+
const cloud = raw.cloud;
|
|
144
|
+
if (!cloud?.endpoint) return;
|
|
145
|
+
const ep = String(cloud.endpoint);
|
|
146
|
+
if (ep === "https://askexe.com/cloud" || ep === "https://askexe.com/cloud/") {
|
|
147
|
+
cloud.endpoint = "https://cloud.askexe.com";
|
|
148
|
+
process.stderr.write(
|
|
149
|
+
"[config] Auto-migrated cloud endpoint: askexe.com/cloud \u2192 cloud.askexe.com\n"
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
142
153
|
async function loadConfig() {
|
|
143
154
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
144
155
|
await ensurePrivateDir(dir);
|
|
@@ -164,6 +175,7 @@ async function loadConfig() {
|
|
|
164
175
|
normalizeSessionLifecycle(migratedCfg);
|
|
165
176
|
normalizeAutoUpdate(migratedCfg);
|
|
166
177
|
normalizeOrchestration(migratedCfg);
|
|
178
|
+
normalizeCloudEndpoint(migratedCfg);
|
|
167
179
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
168
180
|
if (config.dbPath.startsWith("~")) {
|
|
169
181
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
@@ -192,6 +204,7 @@ function loadConfigSync() {
|
|
|
192
204
|
normalizeSessionLifecycle(migratedCfg);
|
|
193
205
|
normalizeAutoUpdate(migratedCfg);
|
|
194
206
|
normalizeOrchestration(migratedCfg);
|
|
207
|
+
normalizeCloudEndpoint(migratedCfg);
|
|
195
208
|
const config = { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
|
|
196
209
|
if (config.dbPath.startsWith("~")) {
|
|
197
210
|
config.dbPath = config.dbPath.replace(/^~/, os.homedir());
|
|
@@ -361,11 +374,176 @@ var init_session_key = __esm({
|
|
|
361
374
|
}
|
|
362
375
|
});
|
|
363
376
|
|
|
377
|
+
// src/lib/runtime-table.ts
|
|
378
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
379
|
+
var init_runtime_table = __esm({
|
|
380
|
+
"src/lib/runtime-table.ts"() {
|
|
381
|
+
"use strict";
|
|
382
|
+
RUNTIME_TABLE = {
|
|
383
|
+
codex: {
|
|
384
|
+
binary: "codex",
|
|
385
|
+
launchMode: "interactive",
|
|
386
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
387
|
+
inlineFlag: "--no-alt-screen",
|
|
388
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
389
|
+
defaultModel: "gpt-5.5"
|
|
390
|
+
},
|
|
391
|
+
opencode: {
|
|
392
|
+
binary: "opencode",
|
|
393
|
+
launchMode: "exec",
|
|
394
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
395
|
+
inlineFlag: "",
|
|
396
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
397
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
DEFAULT_RUNTIME = "claude";
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// src/lib/agent-config.ts
|
|
405
|
+
var agent_config_exports = {};
|
|
406
|
+
__export(agent_config_exports, {
|
|
407
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
408
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
409
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
410
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
411
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
412
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
413
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
414
|
+
normalizeCcModelName: () => normalizeCcModelName,
|
|
415
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
416
|
+
setAgentMcps: () => setAgentMcps,
|
|
417
|
+
setAgentRuntime: () => setAgentRuntime
|
|
418
|
+
});
|
|
419
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
420
|
+
import path2 from "path";
|
|
421
|
+
function loadAgentConfig() {
|
|
422
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
423
|
+
try {
|
|
424
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
425
|
+
} catch {
|
|
426
|
+
return {};
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
function saveAgentConfig(config) {
|
|
430
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
431
|
+
ensurePrivateDirSync(dir);
|
|
432
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
433
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
434
|
+
}
|
|
435
|
+
function getAgentRuntime(agentId) {
|
|
436
|
+
const config = loadAgentConfig();
|
|
437
|
+
const entry = config[agentId];
|
|
438
|
+
if (entry) return entry;
|
|
439
|
+
const orgDefault = config["default"];
|
|
440
|
+
if (orgDefault) return orgDefault;
|
|
441
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
442
|
+
}
|
|
443
|
+
function normalizeCcModelName(model) {
|
|
444
|
+
let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
445
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
446
|
+
ccModel += "[1m]";
|
|
447
|
+
}
|
|
448
|
+
return ccModel;
|
|
449
|
+
}
|
|
450
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
451
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
452
|
+
if (!knownModels) {
|
|
453
|
+
return {
|
|
454
|
+
ok: false,
|
|
455
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
if (!knownModels.includes(model)) {
|
|
459
|
+
return {
|
|
460
|
+
ok: false,
|
|
461
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
const config = loadAgentConfig();
|
|
465
|
+
const existing = config[agentId];
|
|
466
|
+
const entry = { runtime, model };
|
|
467
|
+
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
468
|
+
if (mcps !== void 0) {
|
|
469
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
470
|
+
} else if (existing?.mcps) {
|
|
471
|
+
entry.mcps = existing.mcps;
|
|
472
|
+
}
|
|
473
|
+
config[agentId] = entry;
|
|
474
|
+
saveAgentConfig(config);
|
|
475
|
+
return { ok: true };
|
|
476
|
+
}
|
|
477
|
+
function setAgentMcps(agentId, mcps) {
|
|
478
|
+
const config = loadAgentConfig();
|
|
479
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
480
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
481
|
+
config[agentId] = existing;
|
|
482
|
+
saveAgentConfig(config);
|
|
483
|
+
return { ok: true };
|
|
484
|
+
}
|
|
485
|
+
function clearAgentRuntime(agentId) {
|
|
486
|
+
const config = loadAgentConfig();
|
|
487
|
+
delete config[agentId];
|
|
488
|
+
saveAgentConfig(config);
|
|
489
|
+
}
|
|
490
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
491
|
+
var init_agent_config = __esm({
|
|
492
|
+
"src/lib/agent-config.ts"() {
|
|
493
|
+
"use strict";
|
|
494
|
+
init_config();
|
|
495
|
+
init_runtime_table();
|
|
496
|
+
init_secure_files();
|
|
497
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
498
|
+
KNOWN_RUNTIMES = {
|
|
499
|
+
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
500
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
501
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
502
|
+
};
|
|
503
|
+
RUNTIME_LABELS = {
|
|
504
|
+
claude: "Claude Code (Anthropic)",
|
|
505
|
+
codex: "Codex (OpenAI)",
|
|
506
|
+
opencode: "OpenCode (open source)"
|
|
507
|
+
};
|
|
508
|
+
DEFAULT_MODELS = {
|
|
509
|
+
claude: "claude-opus-4.6",
|
|
510
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
511
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
|
|
364
516
|
// src/lib/employees.ts
|
|
517
|
+
var employees_exports = {};
|
|
518
|
+
__export(employees_exports, {
|
|
519
|
+
COORDINATOR_ROLE: () => COORDINATOR_ROLE,
|
|
520
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
521
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
522
|
+
addEmployee: () => addEmployee,
|
|
523
|
+
baseAgentName: () => baseAgentName,
|
|
524
|
+
canCoordinate: () => canCoordinate,
|
|
525
|
+
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
526
|
+
getCoordinatorName: () => getCoordinatorName,
|
|
527
|
+
getEmployee: () => getEmployee,
|
|
528
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
529
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
530
|
+
hasRole: () => hasRole,
|
|
531
|
+
hireEmployee: () => hireEmployee,
|
|
532
|
+
isCoordinatorName: () => isCoordinatorName,
|
|
533
|
+
isCoordinatorRole: () => isCoordinatorRole,
|
|
534
|
+
isMultiInstance: () => isMultiInstance,
|
|
535
|
+
loadEmployees: () => loadEmployees,
|
|
536
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
537
|
+
normalizeRole: () => normalizeRole,
|
|
538
|
+
normalizeRosterCase: () => normalizeRosterCase,
|
|
539
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
540
|
+
saveEmployees: () => saveEmployees,
|
|
541
|
+
validateEmployeeName: () => validateEmployeeName
|
|
542
|
+
});
|
|
365
543
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
366
|
-
import { existsSync as
|
|
544
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
367
545
|
import { execSync as execSync2 } from "child_process";
|
|
368
|
-
import
|
|
546
|
+
import path3 from "path";
|
|
369
547
|
import os2 from "os";
|
|
370
548
|
function normalizeRole(role) {
|
|
371
549
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -386,10 +564,40 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
|
386
564
|
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
387
565
|
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
388
566
|
}
|
|
567
|
+
function validateEmployeeName(name) {
|
|
568
|
+
if (!name) {
|
|
569
|
+
return { valid: false, error: "Name is required" };
|
|
570
|
+
}
|
|
571
|
+
if (name.length > 32) {
|
|
572
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
573
|
+
}
|
|
574
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
575
|
+
return {
|
|
576
|
+
valid: false,
|
|
577
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
return { valid: true };
|
|
581
|
+
}
|
|
582
|
+
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
583
|
+
if (!existsSync4(employeesPath)) {
|
|
584
|
+
return [];
|
|
585
|
+
}
|
|
586
|
+
const raw = await readFile2(employeesPath, "utf-8");
|
|
587
|
+
try {
|
|
588
|
+
return JSON.parse(raw);
|
|
589
|
+
} catch {
|
|
590
|
+
return [];
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
594
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
595
|
+
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
596
|
+
}
|
|
389
597
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
390
|
-
if (!
|
|
598
|
+
if (!existsSync4(employeesPath)) return [];
|
|
391
599
|
try {
|
|
392
|
-
return JSON.parse(
|
|
600
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
393
601
|
} catch {
|
|
394
602
|
return [];
|
|
395
603
|
}
|
|
@@ -397,26 +605,179 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
|
397
605
|
function getEmployee(employees, name) {
|
|
398
606
|
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
399
607
|
}
|
|
400
|
-
|
|
608
|
+
function getEmployeeByRole(employees, role) {
|
|
609
|
+
const lower = role.toLowerCase();
|
|
610
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
611
|
+
}
|
|
612
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
613
|
+
const lower = role.toLowerCase();
|
|
614
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
615
|
+
}
|
|
616
|
+
function hasRole(agentName, role) {
|
|
617
|
+
const employees = loadEmployeesSync();
|
|
618
|
+
const emp = getEmployee(employees, agentName);
|
|
619
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
620
|
+
}
|
|
621
|
+
function baseAgentName(name, employees) {
|
|
622
|
+
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
623
|
+
if (!match) return name;
|
|
624
|
+
const base = match[1];
|
|
625
|
+
const roster = employees ?? loadEmployeesSync();
|
|
626
|
+
if (getEmployee(roster, base)) return base;
|
|
627
|
+
return name;
|
|
628
|
+
}
|
|
629
|
+
function isMultiInstance(agentName, employees) {
|
|
630
|
+
const roster = employees ?? loadEmployeesSync();
|
|
631
|
+
const emp = getEmployee(roster, agentName);
|
|
632
|
+
if (!emp) return false;
|
|
633
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
634
|
+
}
|
|
635
|
+
function addEmployee(employees, employee) {
|
|
636
|
+
const { systemPrompt: _legacyPrompt, ...rest } = employee;
|
|
637
|
+
const normalized = { ...rest, name: employee.name.toLowerCase() };
|
|
638
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
639
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
640
|
+
}
|
|
641
|
+
return [...employees, normalized];
|
|
642
|
+
}
|
|
643
|
+
function appendToCoordinatorTeam(employee) {
|
|
644
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
645
|
+
if (!coordinator) return;
|
|
646
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
647
|
+
if (!existsSync4(idPath)) return;
|
|
648
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
649
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
650
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
651
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
652
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
653
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
654
|
+
const entry = `
|
|
655
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
656
|
+
`;
|
|
657
|
+
let updated;
|
|
658
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
659
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
660
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
661
|
+
} else {
|
|
662
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
663
|
+
}
|
|
664
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
665
|
+
}
|
|
666
|
+
function capitalize(s) {
|
|
667
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
668
|
+
}
|
|
669
|
+
async function hireEmployee(employee) {
|
|
670
|
+
const employees = await loadEmployees();
|
|
671
|
+
const updated = addEmployee(employees, employee);
|
|
672
|
+
await saveEmployees(updated);
|
|
673
|
+
try {
|
|
674
|
+
appendToCoordinatorTeam(employee);
|
|
675
|
+
} catch {
|
|
676
|
+
}
|
|
677
|
+
try {
|
|
678
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
679
|
+
const config = loadAgentConfig2();
|
|
680
|
+
const name = employee.name.toLowerCase();
|
|
681
|
+
if (!config[name] && config["default"]) {
|
|
682
|
+
config[name] = { ...config["default"] };
|
|
683
|
+
saveAgentConfig2(config);
|
|
684
|
+
}
|
|
685
|
+
} catch {
|
|
686
|
+
}
|
|
687
|
+
return updated;
|
|
688
|
+
}
|
|
689
|
+
async function normalizeRosterCase(rosterPath) {
|
|
690
|
+
const employees = await loadEmployees(rosterPath);
|
|
691
|
+
let changed = false;
|
|
692
|
+
for (const emp of employees) {
|
|
693
|
+
if (emp.name !== emp.name.toLowerCase()) {
|
|
694
|
+
const oldName = emp.name;
|
|
695
|
+
emp.name = emp.name.toLowerCase();
|
|
696
|
+
changed = true;
|
|
697
|
+
try {
|
|
698
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
699
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
700
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
701
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
702
|
+
renameSync2(oldPath, newPath);
|
|
703
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
704
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
705
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
706
|
+
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
707
|
+
unlinkSync(oldPath);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
} catch {
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
if (changed) {
|
|
715
|
+
await saveEmployees(employees, rosterPath);
|
|
716
|
+
}
|
|
717
|
+
return changed;
|
|
718
|
+
}
|
|
719
|
+
function findExeBin() {
|
|
720
|
+
try {
|
|
721
|
+
return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
722
|
+
} catch {
|
|
723
|
+
return null;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
function registerBinSymlinks(name) {
|
|
727
|
+
const created = [];
|
|
728
|
+
const skipped = [];
|
|
729
|
+
const errors = [];
|
|
730
|
+
const exeBinPath = findExeBin();
|
|
731
|
+
if (!exeBinPath) {
|
|
732
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
733
|
+
return { created, skipped, errors };
|
|
734
|
+
}
|
|
735
|
+
const binDir = path3.dirname(exeBinPath);
|
|
736
|
+
let target;
|
|
737
|
+
try {
|
|
738
|
+
target = readlinkSync(exeBinPath);
|
|
739
|
+
} catch {
|
|
740
|
+
errors.push("Could not read 'exe' symlink");
|
|
741
|
+
return { created, skipped, errors };
|
|
742
|
+
}
|
|
743
|
+
for (const suffix of ["", "-opencode"]) {
|
|
744
|
+
const linkName = `${name}${suffix}`;
|
|
745
|
+
const linkPath = path3.join(binDir, linkName);
|
|
746
|
+
if (existsSync4(linkPath)) {
|
|
747
|
+
skipped.push(linkName);
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
750
|
+
try {
|
|
751
|
+
symlinkSync(target, linkPath);
|
|
752
|
+
created.push(linkName);
|
|
753
|
+
} catch (err) {
|
|
754
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return { created, skipped, errors };
|
|
758
|
+
}
|
|
759
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
401
760
|
var init_employees = __esm({
|
|
402
761
|
"src/lib/employees.ts"() {
|
|
403
762
|
"use strict";
|
|
404
763
|
init_config();
|
|
405
|
-
EMPLOYEES_PATH =
|
|
764
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
406
765
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
407
766
|
COORDINATOR_ROLE = "COO";
|
|
408
|
-
|
|
767
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
768
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
769
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
409
770
|
}
|
|
410
771
|
});
|
|
411
772
|
|
|
412
773
|
// src/lib/session-registry.ts
|
|
413
|
-
import
|
|
774
|
+
import path5 from "path";
|
|
414
775
|
import os3 from "os";
|
|
415
776
|
var REGISTRY_PATH;
|
|
416
777
|
var init_session_registry = __esm({
|
|
417
778
|
"src/lib/session-registry.ts"() {
|
|
418
779
|
"use strict";
|
|
419
|
-
REGISTRY_PATH =
|
|
780
|
+
REGISTRY_PATH = path5.join(os3.homedir(), ".exe-os", "session-registry.json");
|
|
420
781
|
}
|
|
421
782
|
});
|
|
422
783
|
|
|
@@ -566,51 +927,6 @@ var init_provider_table = __esm({
|
|
|
566
927
|
}
|
|
567
928
|
});
|
|
568
929
|
|
|
569
|
-
// src/lib/runtime-table.ts
|
|
570
|
-
var RUNTIME_TABLE;
|
|
571
|
-
var init_runtime_table = __esm({
|
|
572
|
-
"src/lib/runtime-table.ts"() {
|
|
573
|
-
"use strict";
|
|
574
|
-
RUNTIME_TABLE = {
|
|
575
|
-
codex: {
|
|
576
|
-
binary: "codex",
|
|
577
|
-
launchMode: "interactive",
|
|
578
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
579
|
-
inlineFlag: "--no-alt-screen",
|
|
580
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
581
|
-
defaultModel: "gpt-5.5"
|
|
582
|
-
},
|
|
583
|
-
opencode: {
|
|
584
|
-
binary: "opencode",
|
|
585
|
-
launchMode: "exec",
|
|
586
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
587
|
-
inlineFlag: "",
|
|
588
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
589
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
590
|
-
}
|
|
591
|
-
};
|
|
592
|
-
}
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
// src/lib/agent-config.ts
|
|
596
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4 } from "fs";
|
|
597
|
-
import path5 from "path";
|
|
598
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
599
|
-
var init_agent_config = __esm({
|
|
600
|
-
"src/lib/agent-config.ts"() {
|
|
601
|
-
"use strict";
|
|
602
|
-
init_config();
|
|
603
|
-
init_runtime_table();
|
|
604
|
-
init_secure_files();
|
|
605
|
-
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
606
|
-
DEFAULT_MODELS = {
|
|
607
|
-
claude: "claude-opus-4.6",
|
|
608
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
609
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
610
|
-
};
|
|
611
|
-
}
|
|
612
|
-
});
|
|
613
|
-
|
|
614
930
|
// src/lib/intercom-queue.ts
|
|
615
931
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
616
932
|
import path6 from "path";
|
|
@@ -3713,6 +4029,21 @@ function isRootSession(name) {
|
|
|
3713
4029
|
function extractRootExe(name) {
|
|
3714
4030
|
if (!name) return null;
|
|
3715
4031
|
if (!name.includes("-")) return name;
|
|
4032
|
+
try {
|
|
4033
|
+
const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
|
|
4034
|
+
if (roster.length > 0) {
|
|
4035
|
+
const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
|
|
4036
|
+
for (const agentName of sortedNames) {
|
|
4037
|
+
const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4038
|
+
const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
|
|
4039
|
+
const match = name.match(regex);
|
|
4040
|
+
if (match) {
|
|
4041
|
+
return extractRootExe(match[1]);
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
4044
|
+
}
|
|
4045
|
+
} catch {
|
|
4046
|
+
}
|
|
3716
4047
|
const parts = name.split("-").filter(Boolean);
|
|
3717
4048
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
3718
4049
|
}
|
|
@@ -3731,6 +4062,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
3731
4062
|
function getParentExe(sessionKey) {
|
|
3732
4063
|
try {
|
|
3733
4064
|
const data = JSON.parse(readFileSync10(path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4065
|
+
if (data.registeredAt) {
|
|
4066
|
+
const age = Date.now() - new Date(data.registeredAt).getTime();
|
|
4067
|
+
if (age > PARENT_EXE_CACHE_TTL_MS) return null;
|
|
4068
|
+
}
|
|
3734
4069
|
return data.parentExe || null;
|
|
3735
4070
|
} catch {
|
|
3736
4071
|
return null;
|
|
@@ -3803,7 +4138,7 @@ function resolveExeSession() {
|
|
|
3803
4138
|
}
|
|
3804
4139
|
return candidate;
|
|
3805
4140
|
}
|
|
3806
|
-
var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
4141
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, PARENT_EXE_CACHE_TTL_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
3807
4142
|
var init_tmux_routing = __esm({
|
|
3808
4143
|
"src/lib/tmux-routing.ts"() {
|
|
3809
4144
|
"use strict";
|
|
@@ -3821,6 +4156,7 @@ var init_tmux_routing = __esm({
|
|
|
3821
4156
|
init_agent_symlinks();
|
|
3822
4157
|
SPAWN_LOCK_DIR = path13.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
3823
4158
|
SESSION_CACHE = path13.join(os9.homedir(), ".exe-os", "session-cache");
|
|
4159
|
+
PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
|
|
3824
4160
|
INTERCOM_LOG2 = path13.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
3825
4161
|
DEBOUNCE_FILE = path13.join(SESSION_CACHE, "intercom-debounce.json");
|
|
3826
4162
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
@@ -3861,7 +4197,7 @@ var init_task_scope = __esm({
|
|
|
3861
4197
|
});
|
|
3862
4198
|
|
|
3863
4199
|
// src/lib/keychain.ts
|
|
3864
|
-
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
4200
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
|
|
3865
4201
|
import { existsSync as existsSync13, statSync as statSync3 } from "fs";
|
|
3866
4202
|
import { execSync as execSync6 } from "child_process";
|
|
3867
4203
|
import path14 from "path";
|
|
@@ -3896,12 +4232,14 @@ function linuxSecretAvailable() {
|
|
|
3896
4232
|
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
3897
4233
|
if (process.platform !== "linux") return false;
|
|
3898
4234
|
try {
|
|
3899
|
-
const uid = typeof os10.userInfo().uid === "number" ? os10.userInfo().uid : -1;
|
|
3900
4235
|
const st = statSync3(keyPath);
|
|
3901
4236
|
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
4237
|
+
const uid = typeof os10.userInfo().uid === "number" ? os10.userInfo().uid : -1;
|
|
3902
4238
|
if (uid === 0) return true;
|
|
3903
4239
|
const exeOsDir = process.env.EXE_OS_DIR;
|
|
3904
|
-
|
|
4240
|
+
if (exeOsDir && path14.resolve(keyPath).startsWith(path14.resolve(exeOsDir) + path14.sep)) return true;
|
|
4241
|
+
if (!linuxSecretAvailable()) return true;
|
|
4242
|
+
return false;
|
|
3905
4243
|
} catch {
|
|
3906
4244
|
return false;
|
|
3907
4245
|
}
|
|
@@ -4051,15 +4389,25 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
4051
4389
|
await mkdir3(dir, { recursive: true });
|
|
4052
4390
|
const keyPath = getKeyPath();
|
|
4053
4391
|
const machineKey = deriveMachineKey();
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4392
|
+
const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
|
|
4393
|
+
const result = machineKey ? "encrypted" : "plaintext";
|
|
4394
|
+
const tmpPath = keyPath + ".tmp";
|
|
4395
|
+
try {
|
|
4396
|
+
if (existsSync13(keyPath)) {
|
|
4397
|
+
await copyFile(keyPath, keyPath + ".bak").catch(() => {
|
|
4398
|
+
});
|
|
4399
|
+
}
|
|
4400
|
+
await writeFile3(tmpPath, content, "utf-8");
|
|
4401
|
+
await chmod2(tmpPath, 384);
|
|
4402
|
+
await rename(tmpPath, keyPath);
|
|
4403
|
+
} catch (err) {
|
|
4404
|
+
try {
|
|
4405
|
+
await unlink(tmpPath);
|
|
4406
|
+
} catch {
|
|
4407
|
+
}
|
|
4408
|
+
throw err;
|
|
4059
4409
|
}
|
|
4060
|
-
|
|
4061
|
-
await chmod2(keyPath, 384);
|
|
4062
|
-
return "plaintext";
|
|
4410
|
+
return result;
|
|
4063
4411
|
}
|
|
4064
4412
|
async function getMasterKey() {
|
|
4065
4413
|
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
@@ -4126,7 +4474,7 @@ async function getMasterKey() {
|
|
|
4126
4474
|
b64Value = content;
|
|
4127
4475
|
}
|
|
4128
4476
|
const key = Buffer.from(b64Value, "base64");
|
|
4129
|
-
if (
|
|
4477
|
+
if (isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
4130
4478
|
return key;
|
|
4131
4479
|
}
|
|
4132
4480
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
@@ -6685,9 +7033,9 @@ import { fileURLToPath as fileURLToPath3 } from "url";
|
|
|
6685
7033
|
// src/lib/active-agent.ts
|
|
6686
7034
|
init_config();
|
|
6687
7035
|
init_session_key();
|
|
6688
|
-
import { readFileSync as
|
|
7036
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync } from "fs";
|
|
6689
7037
|
import { execSync as execSync3 } from "child_process";
|
|
6690
|
-
import
|
|
7038
|
+
import path4 from "path";
|
|
6691
7039
|
|
|
6692
7040
|
// src/mcp/agent-context.ts
|
|
6693
7041
|
import { AsyncLocalStorage } from "async_hooks";
|
|
@@ -6698,7 +7046,7 @@ function getAgentContext() {
|
|
|
6698
7046
|
|
|
6699
7047
|
// src/lib/active-agent.ts
|
|
6700
7048
|
init_employees();
|
|
6701
|
-
var CACHE_DIR =
|
|
7049
|
+
var CACHE_DIR = path4.join(EXE_AI_DIR, "session-cache");
|
|
6702
7050
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
6703
7051
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
6704
7052
|
if (candidate === baseName) return true;
|
|
@@ -6743,14 +7091,14 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
6743
7091
|
return null;
|
|
6744
7092
|
}
|
|
6745
7093
|
function getMarkerPath() {
|
|
6746
|
-
return
|
|
7094
|
+
return path4.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
6747
7095
|
}
|
|
6748
7096
|
function getActiveAgent() {
|
|
6749
7097
|
const httpCtx = getAgentContext();
|
|
6750
7098
|
if (httpCtx) return httpCtx;
|
|
6751
7099
|
try {
|
|
6752
7100
|
const markerPath = getMarkerPath();
|
|
6753
|
-
const raw =
|
|
7101
|
+
const raw = readFileSync4(markerPath, "utf8");
|
|
6754
7102
|
const data = JSON.parse(raw);
|
|
6755
7103
|
if (data.agentId) {
|
|
6756
7104
|
if (data.startedAt) {
|