@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
|
@@ -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());
|
|
@@ -333,11 +345,176 @@ var init_session_key = __esm({
|
|
|
333
345
|
}
|
|
334
346
|
});
|
|
335
347
|
|
|
348
|
+
// src/lib/runtime-table.ts
|
|
349
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
350
|
+
var init_runtime_table = __esm({
|
|
351
|
+
"src/lib/runtime-table.ts"() {
|
|
352
|
+
"use strict";
|
|
353
|
+
RUNTIME_TABLE = {
|
|
354
|
+
codex: {
|
|
355
|
+
binary: "codex",
|
|
356
|
+
launchMode: "interactive",
|
|
357
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
358
|
+
inlineFlag: "--no-alt-screen",
|
|
359
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
360
|
+
defaultModel: "gpt-5.5"
|
|
361
|
+
},
|
|
362
|
+
opencode: {
|
|
363
|
+
binary: "opencode",
|
|
364
|
+
launchMode: "exec",
|
|
365
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
366
|
+
inlineFlag: "",
|
|
367
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
368
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
DEFAULT_RUNTIME = "claude";
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// src/lib/agent-config.ts
|
|
376
|
+
var agent_config_exports = {};
|
|
377
|
+
__export(agent_config_exports, {
|
|
378
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
379
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
380
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
381
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
382
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
383
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
384
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
385
|
+
normalizeCcModelName: () => normalizeCcModelName,
|
|
386
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
387
|
+
setAgentMcps: () => setAgentMcps,
|
|
388
|
+
setAgentRuntime: () => setAgentRuntime
|
|
389
|
+
});
|
|
390
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
391
|
+
import path2 from "path";
|
|
392
|
+
function loadAgentConfig() {
|
|
393
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
394
|
+
try {
|
|
395
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
396
|
+
} catch {
|
|
397
|
+
return {};
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
function saveAgentConfig(config) {
|
|
401
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
402
|
+
ensurePrivateDirSync(dir);
|
|
403
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
404
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
405
|
+
}
|
|
406
|
+
function getAgentRuntime(agentId) {
|
|
407
|
+
const config = loadAgentConfig();
|
|
408
|
+
const entry = config[agentId];
|
|
409
|
+
if (entry) return entry;
|
|
410
|
+
const orgDefault = config["default"];
|
|
411
|
+
if (orgDefault) return orgDefault;
|
|
412
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
413
|
+
}
|
|
414
|
+
function normalizeCcModelName(model) {
|
|
415
|
+
let ccModel = model.replace(/(\d+)\.(\d+)/g, "$1-$2");
|
|
416
|
+
if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
|
|
417
|
+
ccModel += "[1m]";
|
|
418
|
+
}
|
|
419
|
+
return ccModel;
|
|
420
|
+
}
|
|
421
|
+
function setAgentRuntime(agentId, runtime, model, reasoning_effort, mcps) {
|
|
422
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
423
|
+
if (!knownModels) {
|
|
424
|
+
return {
|
|
425
|
+
ok: false,
|
|
426
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
if (!knownModels.includes(model)) {
|
|
430
|
+
return {
|
|
431
|
+
ok: false,
|
|
432
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
const config = loadAgentConfig();
|
|
436
|
+
const existing = config[agentId];
|
|
437
|
+
const entry = { runtime, model };
|
|
438
|
+
if (reasoning_effort) entry.reasoning_effort = reasoning_effort;
|
|
439
|
+
if (mcps !== void 0) {
|
|
440
|
+
entry.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
441
|
+
} else if (existing?.mcps) {
|
|
442
|
+
entry.mcps = existing.mcps;
|
|
443
|
+
}
|
|
444
|
+
config[agentId] = entry;
|
|
445
|
+
saveAgentConfig(config);
|
|
446
|
+
return { ok: true };
|
|
447
|
+
}
|
|
448
|
+
function setAgentMcps(agentId, mcps) {
|
|
449
|
+
const config = loadAgentConfig();
|
|
450
|
+
const existing = config[agentId] ?? getAgentRuntime(agentId);
|
|
451
|
+
existing.mcps = mcps.includes("exe-os") ? mcps : ["exe-os", ...mcps];
|
|
452
|
+
config[agentId] = existing;
|
|
453
|
+
saveAgentConfig(config);
|
|
454
|
+
return { ok: true };
|
|
455
|
+
}
|
|
456
|
+
function clearAgentRuntime(agentId) {
|
|
457
|
+
const config = loadAgentConfig();
|
|
458
|
+
delete config[agentId];
|
|
459
|
+
saveAgentConfig(config);
|
|
460
|
+
}
|
|
461
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
462
|
+
var init_agent_config = __esm({
|
|
463
|
+
"src/lib/agent-config.ts"() {
|
|
464
|
+
"use strict";
|
|
465
|
+
init_config();
|
|
466
|
+
init_runtime_table();
|
|
467
|
+
init_secure_files();
|
|
468
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
469
|
+
KNOWN_RUNTIMES = {
|
|
470
|
+
claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
471
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
472
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
473
|
+
};
|
|
474
|
+
RUNTIME_LABELS = {
|
|
475
|
+
claude: "Claude Code (Anthropic)",
|
|
476
|
+
codex: "Codex (OpenAI)",
|
|
477
|
+
opencode: "OpenCode (open source)"
|
|
478
|
+
};
|
|
479
|
+
DEFAULT_MODELS = {
|
|
480
|
+
claude: "claude-opus-4.6",
|
|
481
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
482
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
|
|
336
487
|
// src/lib/employees.ts
|
|
488
|
+
var employees_exports = {};
|
|
489
|
+
__export(employees_exports, {
|
|
490
|
+
COORDINATOR_ROLE: () => COORDINATOR_ROLE,
|
|
491
|
+
DEFAULT_COORDINATOR_TEMPLATE_NAME: () => DEFAULT_COORDINATOR_TEMPLATE_NAME,
|
|
492
|
+
EMPLOYEES_PATH: () => EMPLOYEES_PATH,
|
|
493
|
+
addEmployee: () => addEmployee,
|
|
494
|
+
baseAgentName: () => baseAgentName,
|
|
495
|
+
canCoordinate: () => canCoordinate,
|
|
496
|
+
getCoordinatorEmployee: () => getCoordinatorEmployee,
|
|
497
|
+
getCoordinatorName: () => getCoordinatorName,
|
|
498
|
+
getEmployee: () => getEmployee,
|
|
499
|
+
getEmployeeByRole: () => getEmployeeByRole,
|
|
500
|
+
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
501
|
+
hasRole: () => hasRole,
|
|
502
|
+
hireEmployee: () => hireEmployee,
|
|
503
|
+
isCoordinatorName: () => isCoordinatorName,
|
|
504
|
+
isCoordinatorRole: () => isCoordinatorRole,
|
|
505
|
+
isMultiInstance: () => isMultiInstance,
|
|
506
|
+
loadEmployees: () => loadEmployees,
|
|
507
|
+
loadEmployeesSync: () => loadEmployeesSync,
|
|
508
|
+
normalizeRole: () => normalizeRole,
|
|
509
|
+
normalizeRosterCase: () => normalizeRosterCase,
|
|
510
|
+
registerBinSymlinks: () => registerBinSymlinks,
|
|
511
|
+
saveEmployees: () => saveEmployees,
|
|
512
|
+
validateEmployeeName: () => validateEmployeeName
|
|
513
|
+
});
|
|
337
514
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
338
|
-
import { existsSync as
|
|
515
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
339
516
|
import { execSync as execSync2 } from "child_process";
|
|
340
|
-
import
|
|
517
|
+
import path3 from "path";
|
|
341
518
|
import os2 from "os";
|
|
342
519
|
function normalizeRole(role) {
|
|
343
520
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -358,10 +535,40 @@ function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
|
358
535
|
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
359
536
|
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
360
537
|
}
|
|
538
|
+
function validateEmployeeName(name) {
|
|
539
|
+
if (!name) {
|
|
540
|
+
return { valid: false, error: "Name is required" };
|
|
541
|
+
}
|
|
542
|
+
if (name.length > 32) {
|
|
543
|
+
return { valid: false, error: "Name must be 32 characters or fewer" };
|
|
544
|
+
}
|
|
545
|
+
if (!/^[a-z][a-z0-9]*$/.test(name)) {
|
|
546
|
+
return {
|
|
547
|
+
valid: false,
|
|
548
|
+
error: "Name must start with a letter and contain only lowercase alphanumeric characters"
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
return { valid: true };
|
|
552
|
+
}
|
|
553
|
+
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
554
|
+
if (!existsSync4(employeesPath)) {
|
|
555
|
+
return [];
|
|
556
|
+
}
|
|
557
|
+
const raw = await readFile2(employeesPath, "utf-8");
|
|
558
|
+
try {
|
|
559
|
+
return JSON.parse(raw);
|
|
560
|
+
} catch {
|
|
561
|
+
return [];
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
565
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
566
|
+
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
567
|
+
}
|
|
361
568
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
362
|
-
if (!
|
|
569
|
+
if (!existsSync4(employeesPath)) return [];
|
|
363
570
|
try {
|
|
364
|
-
return JSON.parse(
|
|
571
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
365
572
|
} catch {
|
|
366
573
|
return [];
|
|
367
574
|
}
|
|
@@ -369,26 +576,179 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
|
369
576
|
function getEmployee(employees, name) {
|
|
370
577
|
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
371
578
|
}
|
|
372
|
-
|
|
579
|
+
function getEmployeeByRole(employees, role) {
|
|
580
|
+
const lower = role.toLowerCase();
|
|
581
|
+
return employees.find((e) => e.role.toLowerCase() === lower);
|
|
582
|
+
}
|
|
583
|
+
function getEmployeeNamesByRole(employees, role) {
|
|
584
|
+
const lower = role.toLowerCase();
|
|
585
|
+
return employees.filter((e) => e.role.toLowerCase() === lower).map((e) => e.name);
|
|
586
|
+
}
|
|
587
|
+
function hasRole(agentName, role) {
|
|
588
|
+
const employees = loadEmployeesSync();
|
|
589
|
+
const emp = getEmployee(employees, agentName);
|
|
590
|
+
return emp ? emp.role.toLowerCase() === role.toLowerCase() : false;
|
|
591
|
+
}
|
|
592
|
+
function baseAgentName(name, employees) {
|
|
593
|
+
const match = name.match(/^([a-zA-Z]+)\d+$/);
|
|
594
|
+
if (!match) return name;
|
|
595
|
+
const base = match[1];
|
|
596
|
+
const roster = employees ?? loadEmployeesSync();
|
|
597
|
+
if (getEmployee(roster, base)) return base;
|
|
598
|
+
return name;
|
|
599
|
+
}
|
|
600
|
+
function isMultiInstance(agentName, employees) {
|
|
601
|
+
const roster = employees ?? loadEmployeesSync();
|
|
602
|
+
const emp = getEmployee(roster, agentName);
|
|
603
|
+
if (!emp) return false;
|
|
604
|
+
return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
|
|
605
|
+
}
|
|
606
|
+
function addEmployee(employees, employee) {
|
|
607
|
+
const { systemPrompt: _legacyPrompt, ...rest } = employee;
|
|
608
|
+
const normalized = { ...rest, name: employee.name.toLowerCase() };
|
|
609
|
+
if (employees.some((e) => e.name.toLowerCase() === normalized.name)) {
|
|
610
|
+
throw new Error(`Employee '${normalized.name}' already exists`);
|
|
611
|
+
}
|
|
612
|
+
return [...employees, normalized];
|
|
613
|
+
}
|
|
614
|
+
function appendToCoordinatorTeam(employee) {
|
|
615
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
616
|
+
if (!coordinator) return;
|
|
617
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
618
|
+
if (!existsSync4(idPath)) return;
|
|
619
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
620
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
621
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
622
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
623
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
624
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
625
|
+
const entry = `
|
|
626
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
627
|
+
`;
|
|
628
|
+
let updated;
|
|
629
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
630
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
631
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
632
|
+
} else {
|
|
633
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
634
|
+
}
|
|
635
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
636
|
+
}
|
|
637
|
+
function capitalize(s) {
|
|
638
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
639
|
+
}
|
|
640
|
+
async function hireEmployee(employee) {
|
|
641
|
+
const employees = await loadEmployees();
|
|
642
|
+
const updated = addEmployee(employees, employee);
|
|
643
|
+
await saveEmployees(updated);
|
|
644
|
+
try {
|
|
645
|
+
appendToCoordinatorTeam(employee);
|
|
646
|
+
} catch {
|
|
647
|
+
}
|
|
648
|
+
try {
|
|
649
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
650
|
+
const config = loadAgentConfig2();
|
|
651
|
+
const name = employee.name.toLowerCase();
|
|
652
|
+
if (!config[name] && config["default"]) {
|
|
653
|
+
config[name] = { ...config["default"] };
|
|
654
|
+
saveAgentConfig2(config);
|
|
655
|
+
}
|
|
656
|
+
} catch {
|
|
657
|
+
}
|
|
658
|
+
return updated;
|
|
659
|
+
}
|
|
660
|
+
async function normalizeRosterCase(rosterPath) {
|
|
661
|
+
const employees = await loadEmployees(rosterPath);
|
|
662
|
+
let changed = false;
|
|
663
|
+
for (const emp of employees) {
|
|
664
|
+
if (emp.name !== emp.name.toLowerCase()) {
|
|
665
|
+
const oldName = emp.name;
|
|
666
|
+
emp.name = emp.name.toLowerCase();
|
|
667
|
+
changed = true;
|
|
668
|
+
try {
|
|
669
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
670
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
671
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
672
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
673
|
+
renameSync2(oldPath, newPath);
|
|
674
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
675
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
676
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
677
|
+
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
678
|
+
unlinkSync(oldPath);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
} catch {
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
if (changed) {
|
|
686
|
+
await saveEmployees(employees, rosterPath);
|
|
687
|
+
}
|
|
688
|
+
return changed;
|
|
689
|
+
}
|
|
690
|
+
function findExeBin() {
|
|
691
|
+
try {
|
|
692
|
+
return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
|
|
693
|
+
} catch {
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
function registerBinSymlinks(name) {
|
|
698
|
+
const created = [];
|
|
699
|
+
const skipped = [];
|
|
700
|
+
const errors = [];
|
|
701
|
+
const exeBinPath = findExeBin();
|
|
702
|
+
if (!exeBinPath) {
|
|
703
|
+
errors.push("Could not find 'exe-os' in PATH");
|
|
704
|
+
return { created, skipped, errors };
|
|
705
|
+
}
|
|
706
|
+
const binDir = path3.dirname(exeBinPath);
|
|
707
|
+
let target;
|
|
708
|
+
try {
|
|
709
|
+
target = readlinkSync(exeBinPath);
|
|
710
|
+
} catch {
|
|
711
|
+
errors.push("Could not read 'exe' symlink");
|
|
712
|
+
return { created, skipped, errors };
|
|
713
|
+
}
|
|
714
|
+
for (const suffix of ["", "-opencode"]) {
|
|
715
|
+
const linkName = `${name}${suffix}`;
|
|
716
|
+
const linkPath = path3.join(binDir, linkName);
|
|
717
|
+
if (existsSync4(linkPath)) {
|
|
718
|
+
skipped.push(linkName);
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
try {
|
|
722
|
+
symlinkSync(target, linkPath);
|
|
723
|
+
created.push(linkName);
|
|
724
|
+
} catch (err) {
|
|
725
|
+
errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
return { created, skipped, errors };
|
|
729
|
+
}
|
|
730
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
373
731
|
var init_employees = __esm({
|
|
374
732
|
"src/lib/employees.ts"() {
|
|
375
733
|
"use strict";
|
|
376
734
|
init_config();
|
|
377
|
-
EMPLOYEES_PATH =
|
|
735
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
378
736
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
379
737
|
COORDINATOR_ROLE = "COO";
|
|
380
|
-
|
|
738
|
+
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
739
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
740
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
381
741
|
}
|
|
382
742
|
});
|
|
383
743
|
|
|
384
744
|
// src/lib/session-registry.ts
|
|
385
|
-
import
|
|
745
|
+
import path5 from "path";
|
|
386
746
|
import os3 from "os";
|
|
387
747
|
var REGISTRY_PATH;
|
|
388
748
|
var init_session_registry = __esm({
|
|
389
749
|
"src/lib/session-registry.ts"() {
|
|
390
750
|
"use strict";
|
|
391
|
-
REGISTRY_PATH =
|
|
751
|
+
REGISTRY_PATH = path5.join(os3.homedir(), ".exe-os", "session-registry.json");
|
|
392
752
|
}
|
|
393
753
|
});
|
|
394
754
|
|
|
@@ -538,51 +898,6 @@ var init_provider_table = __esm({
|
|
|
538
898
|
}
|
|
539
899
|
});
|
|
540
900
|
|
|
541
|
-
// src/lib/runtime-table.ts
|
|
542
|
-
var RUNTIME_TABLE;
|
|
543
|
-
var init_runtime_table = __esm({
|
|
544
|
-
"src/lib/runtime-table.ts"() {
|
|
545
|
-
"use strict";
|
|
546
|
-
RUNTIME_TABLE = {
|
|
547
|
-
codex: {
|
|
548
|
-
binary: "codex",
|
|
549
|
-
launchMode: "interactive",
|
|
550
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
551
|
-
inlineFlag: "--no-alt-screen",
|
|
552
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
553
|
-
defaultModel: "gpt-5.5"
|
|
554
|
-
},
|
|
555
|
-
opencode: {
|
|
556
|
-
binary: "opencode",
|
|
557
|
-
launchMode: "exec",
|
|
558
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
559
|
-
inlineFlag: "",
|
|
560
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
561
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
562
|
-
}
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
// src/lib/agent-config.ts
|
|
568
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4 } from "fs";
|
|
569
|
-
import path5 from "path";
|
|
570
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
571
|
-
var init_agent_config = __esm({
|
|
572
|
-
"src/lib/agent-config.ts"() {
|
|
573
|
-
"use strict";
|
|
574
|
-
init_config();
|
|
575
|
-
init_runtime_table();
|
|
576
|
-
init_secure_files();
|
|
577
|
-
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
578
|
-
DEFAULT_MODELS = {
|
|
579
|
-
claude: "claude-opus-4.6",
|
|
580
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
581
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
582
|
-
};
|
|
583
|
-
}
|
|
584
|
-
});
|
|
585
|
-
|
|
586
901
|
// src/lib/intercom-queue.ts
|
|
587
902
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
588
903
|
import path6 from "path";
|
|
@@ -3685,6 +4000,21 @@ function isRootSession(name) {
|
|
|
3685
4000
|
function extractRootExe(name) {
|
|
3686
4001
|
if (!name) return null;
|
|
3687
4002
|
if (!name.includes("-")) return name;
|
|
4003
|
+
try {
|
|
4004
|
+
const roster = (init_employees(), __toCommonJS(employees_exports)).loadEmployeesSync();
|
|
4005
|
+
if (roster.length > 0) {
|
|
4006
|
+
const sortedNames = roster.map((e) => e.name).sort((a, b) => b.length - a.length);
|
|
4007
|
+
for (const agentName of sortedNames) {
|
|
4008
|
+
const escaped = agentName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4009
|
+
const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
|
|
4010
|
+
const match = name.match(regex);
|
|
4011
|
+
if (match) {
|
|
4012
|
+
return extractRootExe(match[1]);
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
} catch {
|
|
4017
|
+
}
|
|
3688
4018
|
const parts = name.split("-").filter(Boolean);
|
|
3689
4019
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
3690
4020
|
}
|
|
@@ -3703,6 +4033,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
3703
4033
|
function getParentExe(sessionKey) {
|
|
3704
4034
|
try {
|
|
3705
4035
|
const data = JSON.parse(readFileSync10(path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4036
|
+
if (data.registeredAt) {
|
|
4037
|
+
const age = Date.now() - new Date(data.registeredAt).getTime();
|
|
4038
|
+
if (age > PARENT_EXE_CACHE_TTL_MS) return null;
|
|
4039
|
+
}
|
|
3706
4040
|
return data.parentExe || null;
|
|
3707
4041
|
} catch {
|
|
3708
4042
|
return null;
|
|
@@ -3775,7 +4109,7 @@ function resolveExeSession() {
|
|
|
3775
4109
|
}
|
|
3776
4110
|
return candidate;
|
|
3777
4111
|
}
|
|
3778
|
-
var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
4112
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, PARENT_EXE_CACHE_TTL_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
3779
4113
|
var init_tmux_routing = __esm({
|
|
3780
4114
|
"src/lib/tmux-routing.ts"() {
|
|
3781
4115
|
"use strict";
|
|
@@ -3793,6 +4127,7 @@ var init_tmux_routing = __esm({
|
|
|
3793
4127
|
init_agent_symlinks();
|
|
3794
4128
|
SPAWN_LOCK_DIR = path13.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
3795
4129
|
SESSION_CACHE = path13.join(os9.homedir(), ".exe-os", "session-cache");
|
|
4130
|
+
PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
|
|
3796
4131
|
INTERCOM_LOG2 = path13.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
3797
4132
|
DEBOUNCE_FILE = path13.join(SESSION_CACHE, "intercom-debounce.json");
|
|
3798
4133
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
@@ -3824,7 +4159,7 @@ var init_task_scope = __esm({
|
|
|
3824
4159
|
});
|
|
3825
4160
|
|
|
3826
4161
|
// src/lib/keychain.ts
|
|
3827
|
-
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
4162
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
|
|
3828
4163
|
import { existsSync as existsSync13, statSync as statSync3 } from "fs";
|
|
3829
4164
|
import { execSync as execSync6 } from "child_process";
|
|
3830
4165
|
import path14 from "path";
|
|
@@ -3859,12 +4194,14 @@ function linuxSecretAvailable() {
|
|
|
3859
4194
|
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
3860
4195
|
if (process.platform !== "linux") return false;
|
|
3861
4196
|
try {
|
|
3862
|
-
const uid = typeof os10.userInfo().uid === "number" ? os10.userInfo().uid : -1;
|
|
3863
4197
|
const st = statSync3(keyPath);
|
|
3864
4198
|
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
4199
|
+
const uid = typeof os10.userInfo().uid === "number" ? os10.userInfo().uid : -1;
|
|
3865
4200
|
if (uid === 0) return true;
|
|
3866
4201
|
const exeOsDir = process.env.EXE_OS_DIR;
|
|
3867
|
-
|
|
4202
|
+
if (exeOsDir && path14.resolve(keyPath).startsWith(path14.resolve(exeOsDir) + path14.sep)) return true;
|
|
4203
|
+
if (!linuxSecretAvailable()) return true;
|
|
4204
|
+
return false;
|
|
3868
4205
|
} catch {
|
|
3869
4206
|
return false;
|
|
3870
4207
|
}
|
|
@@ -4014,15 +4351,25 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
4014
4351
|
await mkdir3(dir, { recursive: true });
|
|
4015
4352
|
const keyPath = getKeyPath();
|
|
4016
4353
|
const machineKey = deriveMachineKey();
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4354
|
+
const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
|
|
4355
|
+
const result = machineKey ? "encrypted" : "plaintext";
|
|
4356
|
+
const tmpPath = keyPath + ".tmp";
|
|
4357
|
+
try {
|
|
4358
|
+
if (existsSync13(keyPath)) {
|
|
4359
|
+
await copyFile(keyPath, keyPath + ".bak").catch(() => {
|
|
4360
|
+
});
|
|
4361
|
+
}
|
|
4362
|
+
await writeFile3(tmpPath, content, "utf-8");
|
|
4363
|
+
await chmod2(tmpPath, 384);
|
|
4364
|
+
await rename(tmpPath, keyPath);
|
|
4365
|
+
} catch (err) {
|
|
4366
|
+
try {
|
|
4367
|
+
await unlink(tmpPath);
|
|
4368
|
+
} catch {
|
|
4369
|
+
}
|
|
4370
|
+
throw err;
|
|
4022
4371
|
}
|
|
4023
|
-
|
|
4024
|
-
await chmod2(keyPath, 384);
|
|
4025
|
-
return "plaintext";
|
|
4372
|
+
return result;
|
|
4026
4373
|
}
|
|
4027
4374
|
async function getMasterKey() {
|
|
4028
4375
|
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
@@ -4089,7 +4436,7 @@ async function getMasterKey() {
|
|
|
4089
4436
|
b64Value = content;
|
|
4090
4437
|
}
|
|
4091
4438
|
const key = Buffer.from(b64Value, "base64");
|
|
4092
|
-
if (
|
|
4439
|
+
if (isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
4093
4440
|
return key;
|
|
4094
4441
|
}
|
|
4095
4442
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
@@ -6398,9 +6745,9 @@ var init_fast_db_init = __esm({
|
|
|
6398
6745
|
// src/lib/active-agent.ts
|
|
6399
6746
|
init_config();
|
|
6400
6747
|
init_session_key();
|
|
6401
|
-
import { readFileSync as
|
|
6748
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync } from "fs";
|
|
6402
6749
|
import { execSync as execSync3 } from "child_process";
|
|
6403
|
-
import
|
|
6750
|
+
import path4 from "path";
|
|
6404
6751
|
|
|
6405
6752
|
// src/mcp/agent-context.ts
|
|
6406
6753
|
import { AsyncLocalStorage } from "async_hooks";
|
|
@@ -6411,7 +6758,7 @@ function getAgentContext() {
|
|
|
6411
6758
|
|
|
6412
6759
|
// src/lib/active-agent.ts
|
|
6413
6760
|
init_employees();
|
|
6414
|
-
var CACHE_DIR =
|
|
6761
|
+
var CACHE_DIR = path4.join(EXE_AI_DIR, "session-cache");
|
|
6415
6762
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
6416
6763
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
6417
6764
|
if (candidate === baseName) return true;
|
|
@@ -6456,14 +6803,14 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
6456
6803
|
return null;
|
|
6457
6804
|
}
|
|
6458
6805
|
function getMarkerPath() {
|
|
6459
|
-
return
|
|
6806
|
+
return path4.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
6460
6807
|
}
|
|
6461
6808
|
function getActiveAgent() {
|
|
6462
6809
|
const httpCtx = getAgentContext();
|
|
6463
6810
|
if (httpCtx) return httpCtx;
|
|
6464
6811
|
try {
|
|
6465
6812
|
const markerPath = getMarkerPath();
|
|
6466
|
-
const raw =
|
|
6813
|
+
const raw = readFileSync4(markerPath, "utf8");
|
|
6467
6814
|
const data = JSON.parse(raw);
|
|
6468
6815
|
if (data.agentId) {
|
|
6469
6816
|
if (data.startedAt) {
|