@askexenow/exe-os 0.8.85 → 0.8.86
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/cleanup-stale-review-tasks.js +57 -19
- package/dist/bin/cli.js +507 -337
- package/dist/bin/exe-agent-config.js +242 -0
- package/dist/bin/exe-agent.js +3 -3
- package/dist/bin/exe-boot.js +344 -346
- package/dist/bin/exe-dispatch.js +375 -250
- package/dist/bin/exe-forget.js +5 -1
- package/dist/bin/exe-gateway.js +260 -135
- package/dist/bin/exe-healthcheck.js +133 -1
- package/dist/bin/exe-heartbeat.js +72 -31
- package/dist/bin/exe-link.js +25 -2
- package/dist/bin/exe-new-employee.js +22 -0
- package/dist/bin/exe-pending-messages.js +55 -17
- package/dist/bin/exe-pending-reviews.js +57 -19
- package/dist/bin/exe-search.js +6 -2
- package/dist/bin/exe-session-cleanup.js +260 -135
- package/dist/bin/exe-start-codex.js +2598 -0
- package/dist/bin/exe-start.sh +15 -3
- package/dist/bin/exe-status.js +57 -19
- package/dist/bin/git-sweep.js +391 -266
- package/dist/bin/install.js +22 -0
- package/dist/bin/scan-tasks.js +394 -269
- package/dist/bin/setup.js +47 -2
- package/dist/gateway/index.js +257 -132
- package/dist/hooks/bug-report-worker.js +242 -117
- package/dist/hooks/commit-complete.js +389 -264
- package/dist/hooks/error-recall.js +6 -2
- package/dist/hooks/ingest-worker.js +314 -193
- package/dist/hooks/post-compact.js +84 -46
- package/dist/hooks/pre-compact.js +272 -147
- package/dist/hooks/pre-tool-use.js +104 -66
- package/dist/hooks/prompt-submit.js +126 -66
- package/dist/hooks/session-end.js +277 -152
- package/dist/hooks/session-start.js +70 -28
- package/dist/hooks/stop.js +90 -52
- package/dist/hooks/subagent-stop.js +84 -46
- package/dist/hooks/summary-worker.js +175 -114
- package/dist/index.js +296 -171
- package/dist/lib/agent-config.js +167 -0
- package/dist/lib/cloud-sync.js +25 -2
- package/dist/lib/exe-daemon.js +338 -213
- package/dist/lib/hybrid-search.js +7 -2
- package/dist/lib/messaging.js +95 -39
- package/dist/lib/runtime-table.js +16 -0
- package/dist/lib/session-wrappers.js +22 -0
- package/dist/lib/tasks.js +242 -117
- package/dist/lib/tmux-routing.js +314 -189
- package/dist/mcp/server.js +573 -274
- package/dist/mcp/tools/create-task.js +260 -135
- package/dist/mcp/tools/list-tasks.js +68 -30
- package/dist/mcp/tools/send-message.js +100 -44
- package/dist/mcp/tools/update-task.js +123 -67
- package/dist/runtime/index.js +276 -151
- package/dist/tui/App.js +479 -354
- package/package.json +1 -1
- package/src/commands/exe/agent-config.md +27 -0
- package/src/commands/exe/cc-doctor.md +10 -0
|
@@ -505,18 +505,69 @@ var init_provider_table = __esm({
|
|
|
505
505
|
}
|
|
506
506
|
});
|
|
507
507
|
|
|
508
|
-
// src/lib/
|
|
509
|
-
|
|
508
|
+
// src/lib/runtime-table.ts
|
|
509
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
510
|
+
var init_runtime_table = __esm({
|
|
511
|
+
"src/lib/runtime-table.ts"() {
|
|
512
|
+
"use strict";
|
|
513
|
+
RUNTIME_TABLE = {
|
|
514
|
+
codex: {
|
|
515
|
+
binary: "codex",
|
|
516
|
+
launchMode: "exec",
|
|
517
|
+
autoApproveFlag: "--full-auto",
|
|
518
|
+
inlineFlag: "--no-alt-screen",
|
|
519
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
520
|
+
defaultModel: "gpt-5.4"
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
DEFAULT_RUNTIME = "claude";
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// src/lib/agent-config.ts
|
|
528
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
510
529
|
import path5 from "path";
|
|
530
|
+
function loadAgentConfig() {
|
|
531
|
+
if (!existsSync4(AGENT_CONFIG_PATH)) return {};
|
|
532
|
+
try {
|
|
533
|
+
return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
|
|
534
|
+
} catch {
|
|
535
|
+
return {};
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
function getAgentRuntime(agentId) {
|
|
539
|
+
const config = loadAgentConfig();
|
|
540
|
+
const entry = config[agentId];
|
|
541
|
+
if (entry) return entry;
|
|
542
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
543
|
+
}
|
|
544
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
545
|
+
var init_agent_config = __esm({
|
|
546
|
+
"src/lib/agent-config.ts"() {
|
|
547
|
+
"use strict";
|
|
548
|
+
init_config();
|
|
549
|
+
init_runtime_table();
|
|
550
|
+
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
551
|
+
DEFAULT_MODELS = {
|
|
552
|
+
claude: "claude-opus-4",
|
|
553
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
554
|
+
opencode: "minimax-m2.7"
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
// src/lib/intercom-queue.ts
|
|
560
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync4 } from "fs";
|
|
561
|
+
import path6 from "path";
|
|
511
562
|
import os4 from "os";
|
|
512
563
|
function ensureDir() {
|
|
513
|
-
const dir =
|
|
514
|
-
if (!
|
|
564
|
+
const dir = path6.dirname(QUEUE_PATH);
|
|
565
|
+
if (!existsSync5(dir)) mkdirSync4(dir, { recursive: true });
|
|
515
566
|
}
|
|
516
567
|
function readQueue() {
|
|
517
568
|
try {
|
|
518
|
-
if (!
|
|
519
|
-
return JSON.parse(
|
|
569
|
+
if (!existsSync5(QUEUE_PATH)) return [];
|
|
570
|
+
return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
|
|
520
571
|
} catch {
|
|
521
572
|
return [];
|
|
522
573
|
}
|
|
@@ -524,7 +575,7 @@ function readQueue() {
|
|
|
524
575
|
function writeQueue(queue) {
|
|
525
576
|
ensureDir();
|
|
526
577
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
527
|
-
|
|
578
|
+
writeFileSync5(tmp, JSON.stringify(queue, null, 2));
|
|
528
579
|
renameSync3(tmp, QUEUE_PATH);
|
|
529
580
|
}
|
|
530
581
|
function queueIntercom(targetSession, reason) {
|
|
@@ -548,9 +599,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
548
599
|
var init_intercom_queue = __esm({
|
|
549
600
|
"src/lib/intercom-queue.ts"() {
|
|
550
601
|
"use strict";
|
|
551
|
-
QUEUE_PATH =
|
|
602
|
+
QUEUE_PATH = path6.join(os4.homedir(), ".exe-os", "intercom-queue.json");
|
|
552
603
|
TTL_MS = 60 * 60 * 1e3;
|
|
553
|
-
INTERCOM_LOG =
|
|
604
|
+
INTERCOM_LOG = path6.join(os4.homedir(), ".exe-os", "intercom.log");
|
|
554
605
|
}
|
|
555
606
|
});
|
|
556
607
|
|
|
@@ -613,8 +664,8 @@ var init_db_retry = __esm({
|
|
|
613
664
|
import net from "net";
|
|
614
665
|
import { spawn } from "child_process";
|
|
615
666
|
import { randomUUID } from "crypto";
|
|
616
|
-
import { existsSync as
|
|
617
|
-
import
|
|
667
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync3, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
|
|
668
|
+
import path7 from "path";
|
|
618
669
|
import { fileURLToPath } from "url";
|
|
619
670
|
function handleData(chunk) {
|
|
620
671
|
_buffer += chunk.toString();
|
|
@@ -642,9 +693,9 @@ function handleData(chunk) {
|
|
|
642
693
|
}
|
|
643
694
|
}
|
|
644
695
|
function cleanupStaleFiles() {
|
|
645
|
-
if (
|
|
696
|
+
if (existsSync6(PID_PATH)) {
|
|
646
697
|
try {
|
|
647
|
-
const pid = parseInt(
|
|
698
|
+
const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
|
|
648
699
|
if (pid > 0) {
|
|
649
700
|
try {
|
|
650
701
|
process.kill(pid, 0);
|
|
@@ -665,11 +716,11 @@ function cleanupStaleFiles() {
|
|
|
665
716
|
}
|
|
666
717
|
}
|
|
667
718
|
function findPackageRoot() {
|
|
668
|
-
let dir =
|
|
669
|
-
const { root } =
|
|
719
|
+
let dir = path7.dirname(fileURLToPath(import.meta.url));
|
|
720
|
+
const { root } = path7.parse(dir);
|
|
670
721
|
while (dir !== root) {
|
|
671
|
-
if (
|
|
672
|
-
dir =
|
|
722
|
+
if (existsSync6(path7.join(dir, "package.json"))) return dir;
|
|
723
|
+
dir = path7.dirname(dir);
|
|
673
724
|
}
|
|
674
725
|
return null;
|
|
675
726
|
}
|
|
@@ -679,8 +730,8 @@ function spawnDaemon() {
|
|
|
679
730
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
680
731
|
return;
|
|
681
732
|
}
|
|
682
|
-
const daemonPath =
|
|
683
|
-
if (!
|
|
733
|
+
const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
734
|
+
if (!existsSync6(daemonPath)) {
|
|
684
735
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
685
736
|
`);
|
|
686
737
|
return;
|
|
@@ -688,7 +739,7 @@ function spawnDaemon() {
|
|
|
688
739
|
const resolvedPath = daemonPath;
|
|
689
740
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
690
741
|
`);
|
|
691
|
-
const logPath =
|
|
742
|
+
const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
|
|
692
743
|
let stderrFd = "ignore";
|
|
693
744
|
try {
|
|
694
745
|
stderrFd = openSync(logPath, "a");
|
|
@@ -833,9 +884,9 @@ var init_exe_daemon_client = __esm({
|
|
|
833
884
|
"src/lib/exe-daemon-client.ts"() {
|
|
834
885
|
"use strict";
|
|
835
886
|
init_config();
|
|
836
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
837
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
838
|
-
SPAWN_LOCK_PATH =
|
|
887
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
|
|
888
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
|
|
889
|
+
SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
839
890
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
840
891
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
841
892
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -2060,18 +2111,18 @@ var init_database = __esm({
|
|
|
2060
2111
|
});
|
|
2061
2112
|
|
|
2062
2113
|
// src/lib/license.ts
|
|
2063
|
-
import { readFileSync as
|
|
2114
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync7, mkdirSync as mkdirSync5 } from "fs";
|
|
2064
2115
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2065
|
-
import
|
|
2116
|
+
import path8 from "path";
|
|
2066
2117
|
import { jwtVerify, importSPKI } from "jose";
|
|
2067
2118
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
2068
2119
|
var init_license = __esm({
|
|
2069
2120
|
"src/lib/license.ts"() {
|
|
2070
2121
|
"use strict";
|
|
2071
2122
|
init_config();
|
|
2072
|
-
LICENSE_PATH =
|
|
2073
|
-
CACHE_PATH =
|
|
2074
|
-
DEVICE_ID_PATH =
|
|
2123
|
+
LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
|
|
2124
|
+
CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
2125
|
+
DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
|
|
2075
2126
|
PLAN_LIMITS = {
|
|
2076
2127
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
2077
2128
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -2083,12 +2134,12 @@ var init_license = __esm({
|
|
|
2083
2134
|
});
|
|
2084
2135
|
|
|
2085
2136
|
// src/lib/plan-limits.ts
|
|
2086
|
-
import { readFileSync as
|
|
2087
|
-
import
|
|
2137
|
+
import { readFileSync as readFileSync9, existsSync as existsSync8 } from "fs";
|
|
2138
|
+
import path9 from "path";
|
|
2088
2139
|
function getLicenseSync() {
|
|
2089
2140
|
try {
|
|
2090
|
-
if (!
|
|
2091
|
-
const raw = JSON.parse(
|
|
2141
|
+
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
2142
|
+
const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
|
|
2092
2143
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
2093
2144
|
const parts = raw.token.split(".");
|
|
2094
2145
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -2126,8 +2177,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
2126
2177
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
2127
2178
|
let count = 0;
|
|
2128
2179
|
try {
|
|
2129
|
-
if (
|
|
2130
|
-
const raw =
|
|
2180
|
+
if (existsSync8(filePath)) {
|
|
2181
|
+
const raw = readFileSync9(filePath, "utf8");
|
|
2131
2182
|
const employees = JSON.parse(raw);
|
|
2132
2183
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
2133
2184
|
}
|
|
@@ -2156,7 +2207,7 @@ var init_plan_limits = __esm({
|
|
|
2156
2207
|
this.name = "PlanLimitError";
|
|
2157
2208
|
}
|
|
2158
2209
|
};
|
|
2159
|
-
CACHE_PATH2 =
|
|
2210
|
+
CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
2160
2211
|
}
|
|
2161
2212
|
});
|
|
2162
2213
|
|
|
@@ -2173,13 +2224,13 @@ __export(notifications_exports, {
|
|
|
2173
2224
|
writeNotification: () => writeNotification
|
|
2174
2225
|
});
|
|
2175
2226
|
import crypto from "crypto";
|
|
2176
|
-
import
|
|
2227
|
+
import path10 from "path";
|
|
2177
2228
|
import os5 from "os";
|
|
2178
2229
|
import {
|
|
2179
|
-
readFileSync as
|
|
2230
|
+
readFileSync as readFileSync10,
|
|
2180
2231
|
readdirSync as readdirSync2,
|
|
2181
2232
|
unlinkSync as unlinkSync4,
|
|
2182
|
-
existsSync as
|
|
2233
|
+
existsSync as existsSync9,
|
|
2183
2234
|
rmdirSync
|
|
2184
2235
|
} from "fs";
|
|
2185
2236
|
async function writeNotification(notification) {
|
|
@@ -2315,9 +2366,9 @@ function formatNotifications(notifications) {
|
|
|
2315
2366
|
return lines.join("\n");
|
|
2316
2367
|
}
|
|
2317
2368
|
async function migrateJsonNotifications() {
|
|
2318
|
-
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR ||
|
|
2319
|
-
const notifDir =
|
|
2320
|
-
if (!
|
|
2369
|
+
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path10.join(os5.homedir(), ".exe-os");
|
|
2370
|
+
const notifDir = path10.join(base, "notifications");
|
|
2371
|
+
if (!existsSync9(notifDir)) return 0;
|
|
2321
2372
|
let migrated = 0;
|
|
2322
2373
|
try {
|
|
2323
2374
|
const files = readdirSync2(notifDir).filter((f) => f.endsWith(".json"));
|
|
@@ -2325,8 +2376,8 @@ async function migrateJsonNotifications() {
|
|
|
2325
2376
|
const client = getClient();
|
|
2326
2377
|
for (const file of files) {
|
|
2327
2378
|
try {
|
|
2328
|
-
const filePath =
|
|
2329
|
-
const data = JSON.parse(
|
|
2379
|
+
const filePath = path10.join(notifDir, file);
|
|
2380
|
+
const data = JSON.parse(readFileSync10(filePath, "utf8"));
|
|
2330
2381
|
await client.execute({
|
|
2331
2382
|
sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
2332
2383
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -2502,11 +2553,11 @@ __export(tasks_crud_exports, {
|
|
|
2502
2553
|
writeCheckpoint: () => writeCheckpoint
|
|
2503
2554
|
});
|
|
2504
2555
|
import crypto3 from "crypto";
|
|
2505
|
-
import
|
|
2556
|
+
import path11 from "path";
|
|
2506
2557
|
import os6 from "os";
|
|
2507
2558
|
import { execSync as execSync5 } from "child_process";
|
|
2508
2559
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2509
|
-
import { existsSync as
|
|
2560
|
+
import { existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
|
|
2510
2561
|
async function writeCheckpoint(input2) {
|
|
2511
2562
|
const client = getClient();
|
|
2512
2563
|
const row = await resolveTask(client, input2.taskId);
|
|
@@ -2681,8 +2732,8 @@ ${laneWarning}` : laneWarning;
|
|
|
2681
2732
|
}
|
|
2682
2733
|
if (input2.baseDir) {
|
|
2683
2734
|
try {
|
|
2684
|
-
await mkdir3(
|
|
2685
|
-
await mkdir3(
|
|
2735
|
+
await mkdir3(path11.join(input2.baseDir, "exe", "output"), { recursive: true });
|
|
2736
|
+
await mkdir3(path11.join(input2.baseDir, "exe", "research"), { recursive: true });
|
|
2686
2737
|
await ensureArchitectureDoc(input2.baseDir, input2.projectName);
|
|
2687
2738
|
await ensureGitignoreExe(input2.baseDir);
|
|
2688
2739
|
} catch {
|
|
@@ -2718,10 +2769,10 @@ ${laneWarning}` : laneWarning;
|
|
|
2718
2769
|
});
|
|
2719
2770
|
if (input2.baseDir) {
|
|
2720
2771
|
try {
|
|
2721
|
-
const EXE_OS_DIR =
|
|
2722
|
-
const mdPath =
|
|
2723
|
-
const mdDir =
|
|
2724
|
-
if (!
|
|
2772
|
+
const EXE_OS_DIR = path11.join(os6.homedir(), ".exe-os");
|
|
2773
|
+
const mdPath = path11.join(EXE_OS_DIR, taskFile);
|
|
2774
|
+
const mdDir = path11.dirname(mdPath);
|
|
2775
|
+
if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
2725
2776
|
const reviewer = input2.reviewer ?? input2.assignedBy;
|
|
2726
2777
|
const mdContent = `# ${input2.title}
|
|
2727
2778
|
|
|
@@ -2746,7 +2797,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
|
|
|
2746
2797
|
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
2747
2798
|
`;
|
|
2748
2799
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
2749
|
-
} catch {
|
|
2800
|
+
} catch (err) {
|
|
2801
|
+
process.stderr.write(
|
|
2802
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
2803
|
+
`
|
|
2804
|
+
);
|
|
2750
2805
|
}
|
|
2751
2806
|
}
|
|
2752
2807
|
return {
|
|
@@ -3006,9 +3061,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
3006
3061
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
3007
3062
|
}
|
|
3008
3063
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
3009
|
-
const archPath =
|
|
3064
|
+
const archPath = path11.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
3010
3065
|
try {
|
|
3011
|
-
if (
|
|
3066
|
+
if (existsSync10(archPath)) return;
|
|
3012
3067
|
const template = [
|
|
3013
3068
|
`# ${projectName} \u2014 System Architecture`,
|
|
3014
3069
|
"",
|
|
@@ -3041,10 +3096,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
3041
3096
|
}
|
|
3042
3097
|
}
|
|
3043
3098
|
async function ensureGitignoreExe(baseDir) {
|
|
3044
|
-
const gitignorePath =
|
|
3099
|
+
const gitignorePath = path11.join(baseDir, ".gitignore");
|
|
3045
3100
|
try {
|
|
3046
|
-
if (
|
|
3047
|
-
const content =
|
|
3101
|
+
if (existsSync10(gitignorePath)) {
|
|
3102
|
+
const content = readFileSync11(gitignorePath, "utf-8");
|
|
3048
3103
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
3049
3104
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
3050
3105
|
} else {
|
|
@@ -3075,8 +3130,8 @@ var init_tasks_crud = __esm({
|
|
|
3075
3130
|
});
|
|
3076
3131
|
|
|
3077
3132
|
// src/lib/tasks-review.ts
|
|
3078
|
-
import
|
|
3079
|
-
import { existsSync as
|
|
3133
|
+
import path12 from "path";
|
|
3134
|
+
import { existsSync as existsSync11, readdirSync as readdirSync3, unlinkSync as unlinkSync5 } from "fs";
|
|
3080
3135
|
async function countPendingReviews(sessionScope) {
|
|
3081
3136
|
const client = getClient();
|
|
3082
3137
|
if (sessionScope) {
|
|
@@ -3257,11 +3312,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3257
3312
|
);
|
|
3258
3313
|
}
|
|
3259
3314
|
try {
|
|
3260
|
-
const cacheDir =
|
|
3261
|
-
if (
|
|
3315
|
+
const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
|
|
3316
|
+
if (existsSync11(cacheDir)) {
|
|
3262
3317
|
for (const f of readdirSync3(cacheDir)) {
|
|
3263
3318
|
if (f.startsWith("review-notified-")) {
|
|
3264
|
-
unlinkSync5(
|
|
3319
|
+
unlinkSync5(path12.join(cacheDir, f));
|
|
3265
3320
|
}
|
|
3266
3321
|
}
|
|
3267
3322
|
}
|
|
@@ -3282,7 +3337,7 @@ var init_tasks_review = __esm({
|
|
|
3282
3337
|
});
|
|
3283
3338
|
|
|
3284
3339
|
// src/lib/tasks-chain.ts
|
|
3285
|
-
import
|
|
3340
|
+
import path13 from "path";
|
|
3286
3341
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3287
3342
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3288
3343
|
const client = getClient();
|
|
@@ -3299,7 +3354,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3299
3354
|
});
|
|
3300
3355
|
for (const ur of unblockedRows.rows) {
|
|
3301
3356
|
try {
|
|
3302
|
-
const ubFile =
|
|
3357
|
+
const ubFile = path13.join(baseDir, String(ur.task_file));
|
|
3303
3358
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3304
3359
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3305
3360
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -3368,7 +3423,7 @@ var init_tasks_chain = __esm({
|
|
|
3368
3423
|
|
|
3369
3424
|
// src/lib/project-name.ts
|
|
3370
3425
|
import { execSync as execSync6 } from "child_process";
|
|
3371
|
-
import
|
|
3426
|
+
import path14 from "path";
|
|
3372
3427
|
function getProjectName(cwd) {
|
|
3373
3428
|
const dir = cwd ?? process.cwd();
|
|
3374
3429
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -3381,7 +3436,7 @@ function getProjectName(cwd) {
|
|
|
3381
3436
|
timeout: 2e3,
|
|
3382
3437
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3383
3438
|
}).trim();
|
|
3384
|
-
repoRoot =
|
|
3439
|
+
repoRoot = path14.dirname(gitCommonDir);
|
|
3385
3440
|
} catch {
|
|
3386
3441
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
3387
3442
|
cwd: dir,
|
|
@@ -3390,11 +3445,11 @@ function getProjectName(cwd) {
|
|
|
3390
3445
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3391
3446
|
}).trim();
|
|
3392
3447
|
}
|
|
3393
|
-
_cached2 =
|
|
3448
|
+
_cached2 = path14.basename(repoRoot);
|
|
3394
3449
|
_cachedCwd = dir;
|
|
3395
3450
|
return _cached2;
|
|
3396
3451
|
} catch {
|
|
3397
|
-
_cached2 =
|
|
3452
|
+
_cached2 = path14.basename(dir);
|
|
3398
3453
|
_cachedCwd = dir;
|
|
3399
3454
|
return _cached2;
|
|
3400
3455
|
}
|
|
@@ -3867,8 +3922,8 @@ __export(tasks_exports, {
|
|
|
3867
3922
|
updateTaskStatus: () => updateTaskStatus,
|
|
3868
3923
|
writeCheckpoint: () => writeCheckpoint
|
|
3869
3924
|
});
|
|
3870
|
-
import
|
|
3871
|
-
import { writeFileSync as
|
|
3925
|
+
import path15 from "path";
|
|
3926
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync6 } from "fs";
|
|
3872
3927
|
async function createTask(input2) {
|
|
3873
3928
|
const result = await createTaskCore(input2);
|
|
3874
3929
|
if (!input2.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -3887,11 +3942,11 @@ async function updateTask(input2) {
|
|
|
3887
3942
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input2);
|
|
3888
3943
|
try {
|
|
3889
3944
|
const agent = String(row.assigned_to);
|
|
3890
|
-
const cacheDir =
|
|
3891
|
-
const cachePath =
|
|
3945
|
+
const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
|
|
3946
|
+
const cachePath = path15.join(cacheDir, `current-task-${agent}.json`);
|
|
3892
3947
|
if (input2.status === "in_progress") {
|
|
3893
|
-
|
|
3894
|
-
|
|
3948
|
+
mkdirSync6(cacheDir, { recursive: true });
|
|
3949
|
+
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
3895
3950
|
} else if (input2.status === "done" || input2.status === "blocked" || input2.status === "cancelled") {
|
|
3896
3951
|
try {
|
|
3897
3952
|
unlinkSync6(cachePath);
|
|
@@ -4358,13 +4413,13 @@ __export(tmux_routing_exports, {
|
|
|
4358
4413
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
4359
4414
|
});
|
|
4360
4415
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
4361
|
-
import { readFileSync as
|
|
4362
|
-
import
|
|
4416
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, existsSync as existsSync12, appendFileSync } from "fs";
|
|
4417
|
+
import path16 from "path";
|
|
4363
4418
|
import os7 from "os";
|
|
4364
4419
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4365
4420
|
import { unlinkSync as unlinkSync7 } from "fs";
|
|
4366
4421
|
function spawnLockPath(sessionName) {
|
|
4367
|
-
return
|
|
4422
|
+
return path16.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4368
4423
|
}
|
|
4369
4424
|
function isProcessAlive(pid) {
|
|
4370
4425
|
try {
|
|
@@ -4375,13 +4430,13 @@ function isProcessAlive(pid) {
|
|
|
4375
4430
|
}
|
|
4376
4431
|
}
|
|
4377
4432
|
function acquireSpawnLock2(sessionName) {
|
|
4378
|
-
if (!
|
|
4379
|
-
|
|
4433
|
+
if (!existsSync12(SPAWN_LOCK_DIR)) {
|
|
4434
|
+
mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
|
|
4380
4435
|
}
|
|
4381
4436
|
const lockFile = spawnLockPath(sessionName);
|
|
4382
|
-
if (
|
|
4437
|
+
if (existsSync12(lockFile)) {
|
|
4383
4438
|
try {
|
|
4384
|
-
const lock = JSON.parse(
|
|
4439
|
+
const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
|
|
4385
4440
|
const age = Date.now() - lock.timestamp;
|
|
4386
4441
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
4387
4442
|
return false;
|
|
@@ -4389,7 +4444,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
4389
4444
|
} catch {
|
|
4390
4445
|
}
|
|
4391
4446
|
}
|
|
4392
|
-
|
|
4447
|
+
writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
4393
4448
|
return true;
|
|
4394
4449
|
}
|
|
4395
4450
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -4401,13 +4456,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
4401
4456
|
function resolveBehaviorsExporterScript() {
|
|
4402
4457
|
try {
|
|
4403
4458
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4404
|
-
const scriptPath =
|
|
4405
|
-
|
|
4459
|
+
const scriptPath = path16.join(
|
|
4460
|
+
path16.dirname(thisFile),
|
|
4406
4461
|
"..",
|
|
4407
4462
|
"bin",
|
|
4408
4463
|
"exe-export-behaviors.js"
|
|
4409
4464
|
);
|
|
4410
|
-
return
|
|
4465
|
+
return existsSync12(scriptPath) ? scriptPath : null;
|
|
4411
4466
|
} catch {
|
|
4412
4467
|
return null;
|
|
4413
4468
|
}
|
|
@@ -4473,12 +4528,12 @@ function extractRootExe(name) {
|
|
|
4473
4528
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
4474
4529
|
}
|
|
4475
4530
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
4476
|
-
if (!
|
|
4477
|
-
|
|
4531
|
+
if (!existsSync12(SESSION_CACHE)) {
|
|
4532
|
+
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
4478
4533
|
}
|
|
4479
4534
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
4480
|
-
const filePath =
|
|
4481
|
-
|
|
4535
|
+
const filePath = path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
4536
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
4482
4537
|
parentExe: rootExe,
|
|
4483
4538
|
dispatchedBy: dispatchedBy || rootExe,
|
|
4484
4539
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -4486,7 +4541,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4486
4541
|
}
|
|
4487
4542
|
function getParentExe(sessionKey) {
|
|
4488
4543
|
try {
|
|
4489
|
-
const data = JSON.parse(
|
|
4544
|
+
const data = JSON.parse(readFileSync12(path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4490
4545
|
return data.parentExe || null;
|
|
4491
4546
|
} catch {
|
|
4492
4547
|
return null;
|
|
@@ -4494,8 +4549,8 @@ function getParentExe(sessionKey) {
|
|
|
4494
4549
|
}
|
|
4495
4550
|
function getDispatchedBy(sessionKey) {
|
|
4496
4551
|
try {
|
|
4497
|
-
const data = JSON.parse(
|
|
4498
|
-
|
|
4552
|
+
const data = JSON.parse(readFileSync12(
|
|
4553
|
+
path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4499
4554
|
"utf8"
|
|
4500
4555
|
));
|
|
4501
4556
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -4556,32 +4611,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
4556
4611
|
}
|
|
4557
4612
|
function readDebounceState() {
|
|
4558
4613
|
try {
|
|
4559
|
-
if (!
|
|
4560
|
-
|
|
4614
|
+
if (!existsSync12(DEBOUNCE_FILE)) return {};
|
|
4615
|
+
const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
|
|
4616
|
+
const state = {};
|
|
4617
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
4618
|
+
if (typeof val === "number") {
|
|
4619
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
4620
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
4621
|
+
state[key] = val;
|
|
4622
|
+
}
|
|
4623
|
+
}
|
|
4624
|
+
return state;
|
|
4561
4625
|
} catch {
|
|
4562
4626
|
return {};
|
|
4563
4627
|
}
|
|
4564
4628
|
}
|
|
4565
4629
|
function writeDebounceState(state) {
|
|
4566
4630
|
try {
|
|
4567
|
-
if (!
|
|
4568
|
-
|
|
4631
|
+
if (!existsSync12(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
4632
|
+
writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
|
|
4569
4633
|
} catch {
|
|
4570
4634
|
}
|
|
4571
4635
|
}
|
|
4572
4636
|
function isDebounced(targetSession) {
|
|
4573
4637
|
const state = readDebounceState();
|
|
4574
|
-
const
|
|
4575
|
-
|
|
4638
|
+
const entry = state[targetSession];
|
|
4639
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
4640
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
4641
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
4642
|
+
state[targetSession].pending++;
|
|
4643
|
+
writeDebounceState(state);
|
|
4644
|
+
return true;
|
|
4645
|
+
}
|
|
4646
|
+
return false;
|
|
4576
4647
|
}
|
|
4577
4648
|
function recordDebounce(targetSession) {
|
|
4578
4649
|
const state = readDebounceState();
|
|
4579
|
-
state[targetSession]
|
|
4650
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
4651
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
4580
4652
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
4581
4653
|
for (const key of Object.keys(state)) {
|
|
4582
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
4654
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
4583
4655
|
}
|
|
4584
4656
|
writeDebounceState(state);
|
|
4657
|
+
return batched;
|
|
4585
4658
|
}
|
|
4586
4659
|
function logIntercom(msg) {
|
|
4587
4660
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -4626,7 +4699,7 @@ function sendIntercom(targetSession) {
|
|
|
4626
4699
|
return "skipped_exe";
|
|
4627
4700
|
}
|
|
4628
4701
|
if (isDebounced(targetSession)) {
|
|
4629
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
4702
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
4630
4703
|
return "debounced";
|
|
4631
4704
|
}
|
|
4632
4705
|
try {
|
|
@@ -4638,14 +4711,14 @@ function sendIntercom(targetSession) {
|
|
|
4638
4711
|
const sessionState = getSessionState(targetSession);
|
|
4639
4712
|
if (sessionState === "no_claude") {
|
|
4640
4713
|
queueIntercom(targetSession, "claude not running in session");
|
|
4641
|
-
recordDebounce(targetSession);
|
|
4642
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
4714
|
+
const batched2 = recordDebounce(targetSession);
|
|
4715
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
4643
4716
|
return "queued";
|
|
4644
4717
|
}
|
|
4645
4718
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
4646
4719
|
queueIntercom(targetSession, "session busy at send time");
|
|
4647
|
-
recordDebounce(targetSession);
|
|
4648
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
4720
|
+
const batched2 = recordDebounce(targetSession);
|
|
4721
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
4649
4722
|
return "queued";
|
|
4650
4723
|
}
|
|
4651
4724
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -4653,8 +4726,8 @@ function sendIntercom(targetSession) {
|
|
|
4653
4726
|
transport.sendKeys(targetSession, "q");
|
|
4654
4727
|
}
|
|
4655
4728
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
4656
|
-
recordDebounce(targetSession);
|
|
4657
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
4729
|
+
const batched = recordDebounce(targetSession);
|
|
4730
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
4658
4731
|
return "delivered";
|
|
4659
4732
|
} catch {
|
|
4660
4733
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -4756,26 +4829,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4756
4829
|
const transport = getTransport();
|
|
4757
4830
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4758
4831
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4759
|
-
const logDir =
|
|
4760
|
-
const logFile =
|
|
4761
|
-
if (!
|
|
4762
|
-
|
|
4832
|
+
const logDir = path16.join(os7.homedir(), ".exe-os", "session-logs");
|
|
4833
|
+
const logFile = path16.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4834
|
+
if (!existsSync12(logDir)) {
|
|
4835
|
+
mkdirSync7(logDir, { recursive: true });
|
|
4763
4836
|
}
|
|
4764
4837
|
transport.kill(sessionName);
|
|
4765
4838
|
let cleanupSuffix = "";
|
|
4766
4839
|
try {
|
|
4767
4840
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4768
|
-
const cleanupScript =
|
|
4769
|
-
if (
|
|
4841
|
+
const cleanupScript = path16.join(path16.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4842
|
+
if (existsSync12(cleanupScript)) {
|
|
4770
4843
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
4771
4844
|
}
|
|
4772
4845
|
} catch {
|
|
4773
4846
|
}
|
|
4774
4847
|
try {
|
|
4775
|
-
const claudeJsonPath =
|
|
4848
|
+
const claudeJsonPath = path16.join(os7.homedir(), ".claude.json");
|
|
4776
4849
|
let claudeJson = {};
|
|
4777
4850
|
try {
|
|
4778
|
-
claudeJson = JSON.parse(
|
|
4851
|
+
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
4779
4852
|
} catch {
|
|
4780
4853
|
}
|
|
4781
4854
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -4783,17 +4856,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4783
4856
|
const trustDir = opts?.cwd ?? projectDir;
|
|
4784
4857
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
4785
4858
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
4786
|
-
|
|
4859
|
+
writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
4787
4860
|
} catch {
|
|
4788
4861
|
}
|
|
4789
4862
|
try {
|
|
4790
|
-
const settingsDir =
|
|
4863
|
+
const settingsDir = path16.join(os7.homedir(), ".claude", "projects");
|
|
4791
4864
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4792
|
-
const projSettingsDir =
|
|
4793
|
-
const settingsPath =
|
|
4865
|
+
const projSettingsDir = path16.join(settingsDir, normalizedKey);
|
|
4866
|
+
const settingsPath = path16.join(projSettingsDir, "settings.json");
|
|
4794
4867
|
let settings = {};
|
|
4795
4868
|
try {
|
|
4796
|
-
settings = JSON.parse(
|
|
4869
|
+
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
4797
4870
|
} catch {
|
|
4798
4871
|
}
|
|
4799
4872
|
const perms = settings.permissions ?? {};
|
|
@@ -4821,20 +4894,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4821
4894
|
if (changed) {
|
|
4822
4895
|
perms.allow = allow;
|
|
4823
4896
|
settings.permissions = perms;
|
|
4824
|
-
|
|
4825
|
-
|
|
4897
|
+
mkdirSync7(projSettingsDir, { recursive: true });
|
|
4898
|
+
writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
4826
4899
|
}
|
|
4827
4900
|
} catch {
|
|
4828
4901
|
}
|
|
4829
4902
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
4830
4903
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
4831
|
-
const
|
|
4904
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
4905
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
4906
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
4907
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
4832
4908
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
4833
4909
|
let identityFlag = "";
|
|
4834
4910
|
let behaviorsFlag = "";
|
|
4835
4911
|
let legacyFallbackWarned = false;
|
|
4836
4912
|
if (!useExeAgent && !useBinSymlink) {
|
|
4837
|
-
const identityPath =
|
|
4913
|
+
const identityPath = path16.join(
|
|
4838
4914
|
os7.homedir(),
|
|
4839
4915
|
".exe-os",
|
|
4840
4916
|
"identity",
|
|
@@ -4844,13 +4920,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4844
4920
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
4845
4921
|
if (hasAgentFlag) {
|
|
4846
4922
|
identityFlag = ` --agent ${employeeName}`;
|
|
4847
|
-
} else if (
|
|
4923
|
+
} else if (existsSync12(identityPath)) {
|
|
4848
4924
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
4849
4925
|
legacyFallbackWarned = true;
|
|
4850
4926
|
}
|
|
4851
4927
|
const behaviorsFile = exportBehaviorsSync(
|
|
4852
4928
|
employeeName,
|
|
4853
|
-
|
|
4929
|
+
path16.basename(spawnCwd),
|
|
4854
4930
|
sessionName
|
|
4855
4931
|
);
|
|
4856
4932
|
if (behaviorsFile) {
|
|
@@ -4865,16 +4941,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4865
4941
|
}
|
|
4866
4942
|
let sessionContextFlag = "";
|
|
4867
4943
|
try {
|
|
4868
|
-
const ctxDir =
|
|
4869
|
-
|
|
4870
|
-
const ctxFile =
|
|
4944
|
+
const ctxDir = path16.join(os7.homedir(), ".exe-os", "session-cache");
|
|
4945
|
+
mkdirSync7(ctxDir, { recursive: true });
|
|
4946
|
+
const ctxFile = path16.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4871
4947
|
const ctxContent = [
|
|
4872
4948
|
`## Session Context`,
|
|
4873
4949
|
`You are running in tmux session: ${sessionName}.`,
|
|
4874
4950
|
`Your parent coordinator session is ${exeSession}.`,
|
|
4875
4951
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
4876
4952
|
].join("\n");
|
|
4877
|
-
|
|
4953
|
+
writeFileSync8(ctxFile, ctxContent);
|
|
4878
4954
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
4879
4955
|
} catch {
|
|
4880
4956
|
}
|
|
@@ -4888,9 +4964,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4888
4964
|
}
|
|
4889
4965
|
}
|
|
4890
4966
|
}
|
|
4967
|
+
if (useCodex) {
|
|
4968
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
4969
|
+
if (codexCfg?.apiKeyEnv) {
|
|
4970
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
4971
|
+
if (keyVal) {
|
|
4972
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
4973
|
+
}
|
|
4974
|
+
}
|
|
4975
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
4976
|
+
}
|
|
4977
|
+
if (useOpencode) {
|
|
4978
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
4979
|
+
if (ocCfg?.apiKeyEnv) {
|
|
4980
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
4981
|
+
if (keyVal) {
|
|
4982
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
4983
|
+
}
|
|
4984
|
+
}
|
|
4985
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4986
|
+
}
|
|
4987
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
4988
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
4989
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
4990
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4991
|
+
}
|
|
4992
|
+
}
|
|
4891
4993
|
let spawnCommand;
|
|
4892
4994
|
if (useExeAgent) {
|
|
4893
4995
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
4996
|
+
} else if (useCodex) {
|
|
4997
|
+
process.stderr.write(
|
|
4998
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
4999
|
+
`
|
|
5000
|
+
);
|
|
5001
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
5002
|
+
} else if (useOpencode) {
|
|
5003
|
+
const binName = `${employeeName}-opencode`;
|
|
5004
|
+
process.stderr.write(
|
|
5005
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
5006
|
+
`
|
|
5007
|
+
);
|
|
5008
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
4894
5009
|
} else if (useBinSymlink) {
|
|
4895
5010
|
const binName = `${employeeName}-${ccProvider}`;
|
|
4896
5011
|
process.stderr.write(
|
|
@@ -4912,11 +5027,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4912
5027
|
transport.pipeLog(sessionName, logFile);
|
|
4913
5028
|
try {
|
|
4914
5029
|
const mySession = getMySession();
|
|
4915
|
-
const dispatchInfo =
|
|
4916
|
-
|
|
5030
|
+
const dispatchInfo = path16.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
5031
|
+
writeFileSync8(dispatchInfo, JSON.stringify({
|
|
4917
5032
|
dispatchedBy: mySession,
|
|
4918
5033
|
rootExe: exeSession,
|
|
4919
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
5034
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
5035
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
5036
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
4920
5037
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4921
5038
|
}));
|
|
4922
5039
|
} catch {
|
|
@@ -4934,6 +5051,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4934
5051
|
booted = true;
|
|
4935
5052
|
break;
|
|
4936
5053
|
}
|
|
5054
|
+
} else if (useCodex) {
|
|
5055
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
5056
|
+
booted = true;
|
|
5057
|
+
break;
|
|
5058
|
+
}
|
|
4937
5059
|
} else {
|
|
4938
5060
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
4939
5061
|
booted = true;
|
|
@@ -4945,9 +5067,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4945
5067
|
}
|
|
4946
5068
|
if (!booted) {
|
|
4947
5069
|
releaseSpawnLock2(sessionName);
|
|
4948
|
-
|
|
5070
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
5071
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
4949
5072
|
}
|
|
4950
|
-
if (!useExeAgent) {
|
|
5073
|
+
if (!useExeAgent && !useCodex) {
|
|
4951
5074
|
try {
|
|
4952
5075
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
4953
5076
|
} catch {
|
|
@@ -4974,17 +5097,19 @@ var init_tmux_routing = __esm({
|
|
|
4974
5097
|
init_cc_agent_support();
|
|
4975
5098
|
init_mcp_prefix();
|
|
4976
5099
|
init_provider_table();
|
|
5100
|
+
init_agent_config();
|
|
5101
|
+
init_runtime_table();
|
|
4977
5102
|
init_intercom_queue();
|
|
4978
5103
|
init_plan_limits();
|
|
4979
5104
|
init_employees();
|
|
4980
|
-
SPAWN_LOCK_DIR =
|
|
4981
|
-
SESSION_CACHE =
|
|
5105
|
+
SPAWN_LOCK_DIR = path16.join(os7.homedir(), ".exe-os", "spawn-locks");
|
|
5106
|
+
SESSION_CACHE = path16.join(os7.homedir(), ".exe-os", "session-cache");
|
|
4982
5107
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4983
5108
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
4984
5109
|
VERIFY_PANE_LINES = 200;
|
|
4985
5110
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4986
|
-
INTERCOM_LOG2 =
|
|
4987
|
-
DEBOUNCE_FILE =
|
|
5111
|
+
INTERCOM_LOG2 = path16.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
5112
|
+
DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4988
5113
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4989
5114
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
4990
5115
|
}
|
|
@@ -5025,14 +5150,14 @@ var init_memory = __esm({
|
|
|
5025
5150
|
|
|
5026
5151
|
// src/lib/keychain.ts
|
|
5027
5152
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
5028
|
-
import { existsSync as
|
|
5029
|
-
import
|
|
5153
|
+
import { existsSync as existsSync13 } from "fs";
|
|
5154
|
+
import path17 from "path";
|
|
5030
5155
|
import os8 from "os";
|
|
5031
5156
|
function getKeyDir() {
|
|
5032
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
5157
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path17.join(os8.homedir(), ".exe-os");
|
|
5033
5158
|
}
|
|
5034
5159
|
function getKeyPath() {
|
|
5035
|
-
return
|
|
5160
|
+
return path17.join(getKeyDir(), "master.key");
|
|
5036
5161
|
}
|
|
5037
5162
|
async function tryKeytar() {
|
|
5038
5163
|
try {
|
|
@@ -5053,7 +5178,7 @@ async function getMasterKey() {
|
|
|
5053
5178
|
}
|
|
5054
5179
|
}
|
|
5055
5180
|
const keyPath = getKeyPath();
|
|
5056
|
-
if (!
|
|
5181
|
+
if (!existsSync13(keyPath)) {
|
|
5057
5182
|
process.stderr.write(
|
|
5058
5183
|
`[keychain] Key not found at ${keyPath} (HOME=${os8.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
5059
5184
|
`
|
|
@@ -5093,13 +5218,13 @@ __export(shard_manager_exports, {
|
|
|
5093
5218
|
listShards: () => listShards,
|
|
5094
5219
|
shardExists: () => shardExists
|
|
5095
5220
|
});
|
|
5096
|
-
import
|
|
5097
|
-
import { existsSync as
|
|
5221
|
+
import path18 from "path";
|
|
5222
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync8, readdirSync as readdirSync4 } from "fs";
|
|
5098
5223
|
import { createClient as createClient2 } from "@libsql/client";
|
|
5099
5224
|
function initShardManager(encryptionKey) {
|
|
5100
5225
|
_encryptionKey = encryptionKey;
|
|
5101
|
-
if (!
|
|
5102
|
-
|
|
5226
|
+
if (!existsSync14(SHARDS_DIR)) {
|
|
5227
|
+
mkdirSync8(SHARDS_DIR, { recursive: true });
|
|
5103
5228
|
}
|
|
5104
5229
|
_shardingEnabled = true;
|
|
5105
5230
|
}
|
|
@@ -5119,7 +5244,7 @@ function getShardClient(projectName) {
|
|
|
5119
5244
|
}
|
|
5120
5245
|
const cached = _shards.get(safeName);
|
|
5121
5246
|
if (cached) return cached;
|
|
5122
|
-
const dbPath =
|
|
5247
|
+
const dbPath = path18.join(SHARDS_DIR, `${safeName}.db`);
|
|
5123
5248
|
const client = createClient2({
|
|
5124
5249
|
url: `file:${dbPath}`,
|
|
5125
5250
|
encryptionKey: _encryptionKey
|
|
@@ -5129,10 +5254,10 @@ function getShardClient(projectName) {
|
|
|
5129
5254
|
}
|
|
5130
5255
|
function shardExists(projectName) {
|
|
5131
5256
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
5132
|
-
return
|
|
5257
|
+
return existsSync14(path18.join(SHARDS_DIR, `${safeName}.db`));
|
|
5133
5258
|
}
|
|
5134
5259
|
function listShards() {
|
|
5135
|
-
if (!
|
|
5260
|
+
if (!existsSync14(SHARDS_DIR)) return [];
|
|
5136
5261
|
return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
5137
5262
|
}
|
|
5138
5263
|
async function ensureShardSchema(client) {
|
|
@@ -5318,7 +5443,7 @@ var init_shard_manager = __esm({
|
|
|
5318
5443
|
"src/lib/shard-manager.ts"() {
|
|
5319
5444
|
"use strict";
|
|
5320
5445
|
init_config();
|
|
5321
|
-
SHARDS_DIR =
|
|
5446
|
+
SHARDS_DIR = path18.join(EXE_AI_DIR, "shards");
|
|
5322
5447
|
_shards = /* @__PURE__ */ new Map();
|
|
5323
5448
|
_encryptionKey = null;
|
|
5324
5449
|
_shardingEnabled = false;
|