@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();
|
|
@@ -351,10 +528,47 @@ function getCoordinatorEmployee(employees) {
|
|
|
351
528
|
function getCoordinatorName(employees = loadEmployeesSync()) {
|
|
352
529
|
return getCoordinatorEmployee(employees)?.name ?? DEFAULT_COORDINATOR_TEMPLATE_NAME;
|
|
353
530
|
}
|
|
531
|
+
function isCoordinatorName(agentName, employees = loadEmployeesSync()) {
|
|
532
|
+
if (!agentName) return false;
|
|
533
|
+
return agentName.toLowerCase() === getCoordinatorName(employees).toLowerCase();
|
|
534
|
+
}
|
|
535
|
+
function canCoordinate(agentName, agentRole, employees = loadEmployeesSync()) {
|
|
536
|
+
return agentName === "default" || isCoordinatorRole(agentRole) || isCoordinatorName(agentName, employees);
|
|
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
|
+
}
|
|
354
568
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
355
|
-
if (!
|
|
569
|
+
if (!existsSync4(employeesPath)) return [];
|
|
356
570
|
try {
|
|
357
|
-
return JSON.parse(
|
|
571
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
358
572
|
} catch {
|
|
359
573
|
return [];
|
|
360
574
|
}
|
|
@@ -362,26 +576,179 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
|
362
576
|
function getEmployee(employees, name) {
|
|
363
577
|
return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
|
|
364
578
|
}
|
|
365
|
-
|
|
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;
|
|
366
731
|
var init_employees = __esm({
|
|
367
732
|
"src/lib/employees.ts"() {
|
|
368
733
|
"use strict";
|
|
369
734
|
init_config();
|
|
370
|
-
EMPLOYEES_PATH =
|
|
735
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
371
736
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
372
737
|
COORDINATOR_ROLE = "COO";
|
|
373
|
-
|
|
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;
|
|
374
741
|
}
|
|
375
742
|
});
|
|
376
743
|
|
|
377
744
|
// src/lib/session-registry.ts
|
|
378
|
-
import
|
|
745
|
+
import path5 from "path";
|
|
379
746
|
import os3 from "os";
|
|
380
747
|
var REGISTRY_PATH;
|
|
381
748
|
var init_session_registry = __esm({
|
|
382
749
|
"src/lib/session-registry.ts"() {
|
|
383
750
|
"use strict";
|
|
384
|
-
REGISTRY_PATH =
|
|
751
|
+
REGISTRY_PATH = path5.join(os3.homedir(), ".exe-os", "session-registry.json");
|
|
385
752
|
}
|
|
386
753
|
});
|
|
387
754
|
|
|
@@ -531,51 +898,6 @@ var init_provider_table = __esm({
|
|
|
531
898
|
}
|
|
532
899
|
});
|
|
533
900
|
|
|
534
|
-
// src/lib/runtime-table.ts
|
|
535
|
-
var RUNTIME_TABLE;
|
|
536
|
-
var init_runtime_table = __esm({
|
|
537
|
-
"src/lib/runtime-table.ts"() {
|
|
538
|
-
"use strict";
|
|
539
|
-
RUNTIME_TABLE = {
|
|
540
|
-
codex: {
|
|
541
|
-
binary: "codex",
|
|
542
|
-
launchMode: "interactive",
|
|
543
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
544
|
-
inlineFlag: "--no-alt-screen",
|
|
545
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
546
|
-
defaultModel: "gpt-5.5"
|
|
547
|
-
},
|
|
548
|
-
opencode: {
|
|
549
|
-
binary: "opencode",
|
|
550
|
-
launchMode: "exec",
|
|
551
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
552
|
-
inlineFlag: "",
|
|
553
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
554
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
555
|
-
}
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
});
|
|
559
|
-
|
|
560
|
-
// src/lib/agent-config.ts
|
|
561
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4 } from "fs";
|
|
562
|
-
import path5 from "path";
|
|
563
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
564
|
-
var init_agent_config = __esm({
|
|
565
|
-
"src/lib/agent-config.ts"() {
|
|
566
|
-
"use strict";
|
|
567
|
-
init_config();
|
|
568
|
-
init_runtime_table();
|
|
569
|
-
init_secure_files();
|
|
570
|
-
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
571
|
-
DEFAULT_MODELS = {
|
|
572
|
-
claude: "claude-opus-4.6",
|
|
573
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
574
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
});
|
|
578
|
-
|
|
579
901
|
// src/lib/intercom-queue.ts
|
|
580
902
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
581
903
|
import path6 from "path";
|
|
@@ -3678,6 +4000,21 @@ function isRootSession(name) {
|
|
|
3678
4000
|
function extractRootExe(name) {
|
|
3679
4001
|
if (!name) return null;
|
|
3680
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
|
+
}
|
|
3681
4018
|
const parts = name.split("-").filter(Boolean);
|
|
3682
4019
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
3683
4020
|
}
|
|
@@ -3696,6 +4033,10 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
3696
4033
|
function getParentExe(sessionKey) {
|
|
3697
4034
|
try {
|
|
3698
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
|
+
}
|
|
3699
4040
|
return data.parentExe || null;
|
|
3700
4041
|
} catch {
|
|
3701
4042
|
return null;
|
|
@@ -3768,7 +4109,7 @@ function resolveExeSession() {
|
|
|
3768
4109
|
}
|
|
3769
4110
|
return candidate;
|
|
3770
4111
|
}
|
|
3771
|
-
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;
|
|
3772
4113
|
var init_tmux_routing = __esm({
|
|
3773
4114
|
"src/lib/tmux-routing.ts"() {
|
|
3774
4115
|
"use strict";
|
|
@@ -3786,6 +4127,7 @@ var init_tmux_routing = __esm({
|
|
|
3786
4127
|
init_agent_symlinks();
|
|
3787
4128
|
SPAWN_LOCK_DIR = path13.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
3788
4129
|
SESSION_CACHE = path13.join(os9.homedir(), ".exe-os", "session-cache");
|
|
4130
|
+
PARENT_EXE_CACHE_TTL_MS = 4 * 60 * 60 * 1e3;
|
|
3789
4131
|
INTERCOM_LOG2 = path13.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
3790
4132
|
DEBOUNCE_FILE = path13.join(SESSION_CACHE, "intercom-debounce.json");
|
|
3791
4133
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
@@ -3957,7 +4299,7 @@ var init_identity = __esm({
|
|
|
3957
4299
|
});
|
|
3958
4300
|
|
|
3959
4301
|
// src/lib/keychain.ts
|
|
3960
|
-
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
4302
|
+
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2, rename, copyFile } from "fs/promises";
|
|
3961
4303
|
import { existsSync as existsSync14, statSync as statSync3 } from "fs";
|
|
3962
4304
|
import { execSync as execSync6 } from "child_process";
|
|
3963
4305
|
import path15 from "path";
|
|
@@ -3992,12 +4334,14 @@ function linuxSecretAvailable() {
|
|
|
3992
4334
|
function isRootOnlyTrustedServerKeyFile(keyPath) {
|
|
3993
4335
|
if (process.platform !== "linux") return false;
|
|
3994
4336
|
try {
|
|
3995
|
-
const uid = typeof os10.userInfo().uid === "number" ? os10.userInfo().uid : -1;
|
|
3996
4337
|
const st = statSync3(keyPath);
|
|
3997
4338
|
if (!st.isFile() || (st.mode & 63) !== 0) return false;
|
|
4339
|
+
const uid = typeof os10.userInfo().uid === "number" ? os10.userInfo().uid : -1;
|
|
3998
4340
|
if (uid === 0) return true;
|
|
3999
4341
|
const exeOsDir = process.env.EXE_OS_DIR;
|
|
4000
|
-
|
|
4342
|
+
if (exeOsDir && path15.resolve(keyPath).startsWith(path15.resolve(exeOsDir) + path15.sep)) return true;
|
|
4343
|
+
if (!linuxSecretAvailable()) return true;
|
|
4344
|
+
return false;
|
|
4001
4345
|
} catch {
|
|
4002
4346
|
return false;
|
|
4003
4347
|
}
|
|
@@ -4147,15 +4491,25 @@ async function writeMachineBoundFileFallback(b64) {
|
|
|
4147
4491
|
await mkdir3(dir, { recursive: true });
|
|
4148
4492
|
const keyPath = getKeyPath();
|
|
4149
4493
|
const machineKey = deriveMachineKey();
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4494
|
+
const content = machineKey ? encryptWithMachineKey(b64, machineKey) + "\n" : b64 + "\n";
|
|
4495
|
+
const result = machineKey ? "encrypted" : "plaintext";
|
|
4496
|
+
const tmpPath = keyPath + ".tmp";
|
|
4497
|
+
try {
|
|
4498
|
+
if (existsSync14(keyPath)) {
|
|
4499
|
+
await copyFile(keyPath, keyPath + ".bak").catch(() => {
|
|
4500
|
+
});
|
|
4501
|
+
}
|
|
4502
|
+
await writeFile3(tmpPath, content, "utf-8");
|
|
4503
|
+
await chmod2(tmpPath, 384);
|
|
4504
|
+
await rename(tmpPath, keyPath);
|
|
4505
|
+
} catch (err) {
|
|
4506
|
+
try {
|
|
4507
|
+
await unlink(tmpPath);
|
|
4508
|
+
} catch {
|
|
4509
|
+
}
|
|
4510
|
+
throw err;
|
|
4155
4511
|
}
|
|
4156
|
-
|
|
4157
|
-
await chmod2(keyPath, 384);
|
|
4158
|
-
return "plaintext";
|
|
4512
|
+
return result;
|
|
4159
4513
|
}
|
|
4160
4514
|
async function getMasterKey() {
|
|
4161
4515
|
let nativeValue = macKeychainGet() ?? linuxSecretGet();
|
|
@@ -4222,7 +4576,7 @@ async function getMasterKey() {
|
|
|
4222
4576
|
b64Value = content;
|
|
4223
4577
|
}
|
|
4224
4578
|
const key = Buffer.from(b64Value, "base64");
|
|
4225
|
-
if (
|
|
4579
|
+
if (isRootOnlyTrustedServerKeyFile(keyPath)) {
|
|
4226
4580
|
return key;
|
|
4227
4581
|
}
|
|
4228
4582
|
const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
|
|
@@ -6531,9 +6885,9 @@ var init_fast_db_init = __esm({
|
|
|
6531
6885
|
// src/lib/active-agent.ts
|
|
6532
6886
|
init_config();
|
|
6533
6887
|
init_session_key();
|
|
6534
|
-
import { readFileSync as
|
|
6888
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync } from "fs";
|
|
6535
6889
|
import { execSync as execSync3 } from "child_process";
|
|
6536
|
-
import
|
|
6890
|
+
import path4 from "path";
|
|
6537
6891
|
|
|
6538
6892
|
// src/mcp/agent-context.ts
|
|
6539
6893
|
import { AsyncLocalStorage } from "async_hooks";
|
|
@@ -6544,7 +6898,7 @@ function getAgentContext() {
|
|
|
6544
6898
|
|
|
6545
6899
|
// src/lib/active-agent.ts
|
|
6546
6900
|
init_employees();
|
|
6547
|
-
var CACHE_DIR =
|
|
6901
|
+
var CACHE_DIR = path4.join(EXE_AI_DIR, "session-cache");
|
|
6548
6902
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
6549
6903
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
6550
6904
|
if (candidate === baseName) return true;
|
|
@@ -6589,14 +6943,14 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
6589
6943
|
return null;
|
|
6590
6944
|
}
|
|
6591
6945
|
function getMarkerPath() {
|
|
6592
|
-
return
|
|
6946
|
+
return path4.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
6593
6947
|
}
|
|
6594
6948
|
function getActiveAgent() {
|
|
6595
6949
|
const httpCtx = getAgentContext();
|
|
6596
6950
|
if (httpCtx) return httpCtx;
|
|
6597
6951
|
try {
|
|
6598
6952
|
const markerPath = getMarkerPath();
|
|
6599
|
-
const raw =
|
|
6953
|
+
const raw = readFileSync4(markerPath, "utf8");
|
|
6600
6954
|
const data = JSON.parse(raw);
|
|
6601
6955
|
if (data.agentId) {
|
|
6602
6956
|
if (data.startedAt) {
|