@askexenow/exe-os 0.8.85 → 0.8.87
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 +510 -340
- 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 +50 -5
- 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
|
@@ -502,18 +502,69 @@ var init_provider_table = __esm({
|
|
|
502
502
|
}
|
|
503
503
|
});
|
|
504
504
|
|
|
505
|
-
// src/lib/
|
|
506
|
-
|
|
505
|
+
// src/lib/runtime-table.ts
|
|
506
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
507
|
+
var init_runtime_table = __esm({
|
|
508
|
+
"src/lib/runtime-table.ts"() {
|
|
509
|
+
"use strict";
|
|
510
|
+
RUNTIME_TABLE = {
|
|
511
|
+
codex: {
|
|
512
|
+
binary: "codex",
|
|
513
|
+
launchMode: "exec",
|
|
514
|
+
autoApproveFlag: "--full-auto",
|
|
515
|
+
inlineFlag: "--no-alt-screen",
|
|
516
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
517
|
+
defaultModel: "gpt-5.4"
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
DEFAULT_RUNTIME = "claude";
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
// src/lib/agent-config.ts
|
|
525
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
507
526
|
import path5 from "path";
|
|
527
|
+
function loadAgentConfig() {
|
|
528
|
+
if (!existsSync4(AGENT_CONFIG_PATH)) return {};
|
|
529
|
+
try {
|
|
530
|
+
return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
|
|
531
|
+
} catch {
|
|
532
|
+
return {};
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
function getAgentRuntime(agentId) {
|
|
536
|
+
const config = loadAgentConfig();
|
|
537
|
+
const entry = config[agentId];
|
|
538
|
+
if (entry) return entry;
|
|
539
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
540
|
+
}
|
|
541
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
542
|
+
var init_agent_config = __esm({
|
|
543
|
+
"src/lib/agent-config.ts"() {
|
|
544
|
+
"use strict";
|
|
545
|
+
init_config();
|
|
546
|
+
init_runtime_table();
|
|
547
|
+
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
548
|
+
DEFAULT_MODELS = {
|
|
549
|
+
claude: "claude-opus-4",
|
|
550
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
551
|
+
opencode: "minimax-m2.7"
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// src/lib/intercom-queue.ts
|
|
557
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync4 } from "fs";
|
|
558
|
+
import path6 from "path";
|
|
508
559
|
import os4 from "os";
|
|
509
560
|
function ensureDir() {
|
|
510
|
-
const dir =
|
|
511
|
-
if (!
|
|
561
|
+
const dir = path6.dirname(QUEUE_PATH);
|
|
562
|
+
if (!existsSync5(dir)) mkdirSync4(dir, { recursive: true });
|
|
512
563
|
}
|
|
513
564
|
function readQueue() {
|
|
514
565
|
try {
|
|
515
|
-
if (!
|
|
516
|
-
return JSON.parse(
|
|
566
|
+
if (!existsSync5(QUEUE_PATH)) return [];
|
|
567
|
+
return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
|
|
517
568
|
} catch {
|
|
518
569
|
return [];
|
|
519
570
|
}
|
|
@@ -521,7 +572,7 @@ function readQueue() {
|
|
|
521
572
|
function writeQueue(queue) {
|
|
522
573
|
ensureDir();
|
|
523
574
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
524
|
-
|
|
575
|
+
writeFileSync5(tmp, JSON.stringify(queue, null, 2));
|
|
525
576
|
renameSync3(tmp, QUEUE_PATH);
|
|
526
577
|
}
|
|
527
578
|
function queueIntercom(targetSession, reason) {
|
|
@@ -545,9 +596,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
545
596
|
var init_intercom_queue = __esm({
|
|
546
597
|
"src/lib/intercom-queue.ts"() {
|
|
547
598
|
"use strict";
|
|
548
|
-
QUEUE_PATH =
|
|
599
|
+
QUEUE_PATH = path6.join(os4.homedir(), ".exe-os", "intercom-queue.json");
|
|
549
600
|
TTL_MS = 60 * 60 * 1e3;
|
|
550
|
-
INTERCOM_LOG =
|
|
601
|
+
INTERCOM_LOG = path6.join(os4.homedir(), ".exe-os", "intercom.log");
|
|
551
602
|
}
|
|
552
603
|
});
|
|
553
604
|
|
|
@@ -610,8 +661,8 @@ var init_db_retry = __esm({
|
|
|
610
661
|
import net from "net";
|
|
611
662
|
import { spawn } from "child_process";
|
|
612
663
|
import { randomUUID } from "crypto";
|
|
613
|
-
import { existsSync as
|
|
614
|
-
import
|
|
664
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync3, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
|
|
665
|
+
import path7 from "path";
|
|
615
666
|
import { fileURLToPath } from "url";
|
|
616
667
|
function handleData(chunk) {
|
|
617
668
|
_buffer += chunk.toString();
|
|
@@ -639,9 +690,9 @@ function handleData(chunk) {
|
|
|
639
690
|
}
|
|
640
691
|
}
|
|
641
692
|
function cleanupStaleFiles() {
|
|
642
|
-
if (
|
|
693
|
+
if (existsSync6(PID_PATH)) {
|
|
643
694
|
try {
|
|
644
|
-
const pid = parseInt(
|
|
695
|
+
const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
|
|
645
696
|
if (pid > 0) {
|
|
646
697
|
try {
|
|
647
698
|
process.kill(pid, 0);
|
|
@@ -662,11 +713,11 @@ function cleanupStaleFiles() {
|
|
|
662
713
|
}
|
|
663
714
|
}
|
|
664
715
|
function findPackageRoot() {
|
|
665
|
-
let dir =
|
|
666
|
-
const { root } =
|
|
716
|
+
let dir = path7.dirname(fileURLToPath(import.meta.url));
|
|
717
|
+
const { root } = path7.parse(dir);
|
|
667
718
|
while (dir !== root) {
|
|
668
|
-
if (
|
|
669
|
-
dir =
|
|
719
|
+
if (existsSync6(path7.join(dir, "package.json"))) return dir;
|
|
720
|
+
dir = path7.dirname(dir);
|
|
670
721
|
}
|
|
671
722
|
return null;
|
|
672
723
|
}
|
|
@@ -676,8 +727,8 @@ function spawnDaemon() {
|
|
|
676
727
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
677
728
|
return;
|
|
678
729
|
}
|
|
679
|
-
const daemonPath =
|
|
680
|
-
if (!
|
|
730
|
+
const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
731
|
+
if (!existsSync6(daemonPath)) {
|
|
681
732
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
682
733
|
`);
|
|
683
734
|
return;
|
|
@@ -685,7 +736,7 @@ function spawnDaemon() {
|
|
|
685
736
|
const resolvedPath = daemonPath;
|
|
686
737
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
687
738
|
`);
|
|
688
|
-
const logPath =
|
|
739
|
+
const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
|
|
689
740
|
let stderrFd = "ignore";
|
|
690
741
|
try {
|
|
691
742
|
stderrFd = openSync(logPath, "a");
|
|
@@ -830,9 +881,9 @@ var init_exe_daemon_client = __esm({
|
|
|
830
881
|
"src/lib/exe-daemon-client.ts"() {
|
|
831
882
|
"use strict";
|
|
832
883
|
init_config();
|
|
833
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
834
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
835
|
-
SPAWN_LOCK_PATH =
|
|
884
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
|
|
885
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
|
|
886
|
+
SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
836
887
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
837
888
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
838
889
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -2057,18 +2108,18 @@ var init_database = __esm({
|
|
|
2057
2108
|
});
|
|
2058
2109
|
|
|
2059
2110
|
// src/lib/license.ts
|
|
2060
|
-
import { readFileSync as
|
|
2111
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync7, mkdirSync as mkdirSync5 } from "fs";
|
|
2061
2112
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2062
|
-
import
|
|
2113
|
+
import path8 from "path";
|
|
2063
2114
|
import { jwtVerify, importSPKI } from "jose";
|
|
2064
2115
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
2065
2116
|
var init_license = __esm({
|
|
2066
2117
|
"src/lib/license.ts"() {
|
|
2067
2118
|
"use strict";
|
|
2068
2119
|
init_config();
|
|
2069
|
-
LICENSE_PATH =
|
|
2070
|
-
CACHE_PATH =
|
|
2071
|
-
DEVICE_ID_PATH =
|
|
2120
|
+
LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
|
|
2121
|
+
CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
2122
|
+
DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
|
|
2072
2123
|
PLAN_LIMITS = {
|
|
2073
2124
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
2074
2125
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -2080,12 +2131,12 @@ var init_license = __esm({
|
|
|
2080
2131
|
});
|
|
2081
2132
|
|
|
2082
2133
|
// src/lib/plan-limits.ts
|
|
2083
|
-
import { readFileSync as
|
|
2084
|
-
import
|
|
2134
|
+
import { readFileSync as readFileSync9, existsSync as existsSync8 } from "fs";
|
|
2135
|
+
import path9 from "path";
|
|
2085
2136
|
function getLicenseSync() {
|
|
2086
2137
|
try {
|
|
2087
|
-
if (!
|
|
2088
|
-
const raw = JSON.parse(
|
|
2138
|
+
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
2139
|
+
const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
|
|
2089
2140
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
2090
2141
|
const parts = raw.token.split(".");
|
|
2091
2142
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -2123,8 +2174,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
2123
2174
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
2124
2175
|
let count = 0;
|
|
2125
2176
|
try {
|
|
2126
|
-
if (
|
|
2127
|
-
const raw =
|
|
2177
|
+
if (existsSync8(filePath)) {
|
|
2178
|
+
const raw = readFileSync9(filePath, "utf8");
|
|
2128
2179
|
const employees = JSON.parse(raw);
|
|
2129
2180
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
2130
2181
|
}
|
|
@@ -2153,19 +2204,19 @@ var init_plan_limits = __esm({
|
|
|
2153
2204
|
this.name = "PlanLimitError";
|
|
2154
2205
|
}
|
|
2155
2206
|
};
|
|
2156
|
-
CACHE_PATH2 =
|
|
2207
|
+
CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
2157
2208
|
}
|
|
2158
2209
|
});
|
|
2159
2210
|
|
|
2160
2211
|
// src/lib/notifications.ts
|
|
2161
2212
|
import crypto from "crypto";
|
|
2162
|
-
import
|
|
2213
|
+
import path10 from "path";
|
|
2163
2214
|
import os5 from "os";
|
|
2164
2215
|
import {
|
|
2165
|
-
readFileSync as
|
|
2216
|
+
readFileSync as readFileSync10,
|
|
2166
2217
|
readdirSync as readdirSync2,
|
|
2167
2218
|
unlinkSync as unlinkSync4,
|
|
2168
|
-
existsSync as
|
|
2219
|
+
existsSync as existsSync9,
|
|
2169
2220
|
rmdirSync
|
|
2170
2221
|
} from "fs";
|
|
2171
2222
|
async function writeNotification(notification) {
|
|
@@ -2300,11 +2351,11 @@ var init_state_bus = __esm({
|
|
|
2300
2351
|
|
|
2301
2352
|
// src/lib/tasks-crud.ts
|
|
2302
2353
|
import crypto3 from "crypto";
|
|
2303
|
-
import
|
|
2354
|
+
import path11 from "path";
|
|
2304
2355
|
import os6 from "os";
|
|
2305
2356
|
import { execSync as execSync5 } from "child_process";
|
|
2306
2357
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2307
|
-
import { existsSync as
|
|
2358
|
+
import { existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
|
|
2308
2359
|
async function writeCheckpoint(input2) {
|
|
2309
2360
|
const client = getClient();
|
|
2310
2361
|
const row = await resolveTask(client, input2.taskId);
|
|
@@ -2479,8 +2530,8 @@ ${laneWarning}` : laneWarning;
|
|
|
2479
2530
|
}
|
|
2480
2531
|
if (input2.baseDir) {
|
|
2481
2532
|
try {
|
|
2482
|
-
await mkdir3(
|
|
2483
|
-
await mkdir3(
|
|
2533
|
+
await mkdir3(path11.join(input2.baseDir, "exe", "output"), { recursive: true });
|
|
2534
|
+
await mkdir3(path11.join(input2.baseDir, "exe", "research"), { recursive: true });
|
|
2484
2535
|
await ensureArchitectureDoc(input2.baseDir, input2.projectName);
|
|
2485
2536
|
await ensureGitignoreExe(input2.baseDir);
|
|
2486
2537
|
} catch {
|
|
@@ -2516,10 +2567,10 @@ ${laneWarning}` : laneWarning;
|
|
|
2516
2567
|
});
|
|
2517
2568
|
if (input2.baseDir) {
|
|
2518
2569
|
try {
|
|
2519
|
-
const EXE_OS_DIR =
|
|
2520
|
-
const mdPath =
|
|
2521
|
-
const mdDir =
|
|
2522
|
-
if (!
|
|
2570
|
+
const EXE_OS_DIR = path11.join(os6.homedir(), ".exe-os");
|
|
2571
|
+
const mdPath = path11.join(EXE_OS_DIR, taskFile);
|
|
2572
|
+
const mdDir = path11.dirname(mdPath);
|
|
2573
|
+
if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
2523
2574
|
const reviewer = input2.reviewer ?? input2.assignedBy;
|
|
2524
2575
|
const mdContent = `# ${input2.title}
|
|
2525
2576
|
|
|
@@ -2544,7 +2595,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
|
|
|
2544
2595
|
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
2545
2596
|
`;
|
|
2546
2597
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
2547
|
-
} catch {
|
|
2598
|
+
} catch (err) {
|
|
2599
|
+
process.stderr.write(
|
|
2600
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
2601
|
+
`
|
|
2602
|
+
);
|
|
2548
2603
|
}
|
|
2549
2604
|
}
|
|
2550
2605
|
return {
|
|
@@ -2804,9 +2859,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
2804
2859
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
2805
2860
|
}
|
|
2806
2861
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
2807
|
-
const archPath =
|
|
2862
|
+
const archPath = path11.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
2808
2863
|
try {
|
|
2809
|
-
if (
|
|
2864
|
+
if (existsSync10(archPath)) return;
|
|
2810
2865
|
const template = [
|
|
2811
2866
|
`# ${projectName} \u2014 System Architecture`,
|
|
2812
2867
|
"",
|
|
@@ -2839,10 +2894,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
2839
2894
|
}
|
|
2840
2895
|
}
|
|
2841
2896
|
async function ensureGitignoreExe(baseDir) {
|
|
2842
|
-
const gitignorePath =
|
|
2897
|
+
const gitignorePath = path11.join(baseDir, ".gitignore");
|
|
2843
2898
|
try {
|
|
2844
|
-
if (
|
|
2845
|
-
const content =
|
|
2899
|
+
if (existsSync10(gitignorePath)) {
|
|
2900
|
+
const content = readFileSync11(gitignorePath, "utf-8");
|
|
2846
2901
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
2847
2902
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
2848
2903
|
} else {
|
|
@@ -2873,8 +2928,8 @@ var init_tasks_crud = __esm({
|
|
|
2873
2928
|
});
|
|
2874
2929
|
|
|
2875
2930
|
// src/lib/tasks-review.ts
|
|
2876
|
-
import
|
|
2877
|
-
import { existsSync as
|
|
2931
|
+
import path12 from "path";
|
|
2932
|
+
import { existsSync as existsSync11, readdirSync as readdirSync3, unlinkSync as unlinkSync5 } from "fs";
|
|
2878
2933
|
async function countPendingReviews(sessionScope) {
|
|
2879
2934
|
const client = getClient();
|
|
2880
2935
|
if (sessionScope) {
|
|
@@ -3055,11 +3110,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3055
3110
|
);
|
|
3056
3111
|
}
|
|
3057
3112
|
try {
|
|
3058
|
-
const cacheDir =
|
|
3059
|
-
if (
|
|
3113
|
+
const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
|
|
3114
|
+
if (existsSync11(cacheDir)) {
|
|
3060
3115
|
for (const f of readdirSync3(cacheDir)) {
|
|
3061
3116
|
if (f.startsWith("review-notified-")) {
|
|
3062
|
-
unlinkSync5(
|
|
3117
|
+
unlinkSync5(path12.join(cacheDir, f));
|
|
3063
3118
|
}
|
|
3064
3119
|
}
|
|
3065
3120
|
}
|
|
@@ -3080,7 +3135,7 @@ var init_tasks_review = __esm({
|
|
|
3080
3135
|
});
|
|
3081
3136
|
|
|
3082
3137
|
// src/lib/tasks-chain.ts
|
|
3083
|
-
import
|
|
3138
|
+
import path13 from "path";
|
|
3084
3139
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3085
3140
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3086
3141
|
const client = getClient();
|
|
@@ -3097,7 +3152,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3097
3152
|
});
|
|
3098
3153
|
for (const ur of unblockedRows.rows) {
|
|
3099
3154
|
try {
|
|
3100
|
-
const ubFile =
|
|
3155
|
+
const ubFile = path13.join(baseDir, String(ur.task_file));
|
|
3101
3156
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3102
3157
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3103
3158
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -3166,7 +3221,7 @@ var init_tasks_chain = __esm({
|
|
|
3166
3221
|
|
|
3167
3222
|
// src/lib/project-name.ts
|
|
3168
3223
|
import { execSync as execSync6 } from "child_process";
|
|
3169
|
-
import
|
|
3224
|
+
import path14 from "path";
|
|
3170
3225
|
function getProjectName(cwd) {
|
|
3171
3226
|
const dir = cwd ?? process.cwd();
|
|
3172
3227
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -3179,7 +3234,7 @@ function getProjectName(cwd) {
|
|
|
3179
3234
|
timeout: 2e3,
|
|
3180
3235
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3181
3236
|
}).trim();
|
|
3182
|
-
repoRoot =
|
|
3237
|
+
repoRoot = path14.dirname(gitCommonDir);
|
|
3183
3238
|
} catch {
|
|
3184
3239
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
3185
3240
|
cwd: dir,
|
|
@@ -3188,11 +3243,11 @@ function getProjectName(cwd) {
|
|
|
3188
3243
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3189
3244
|
}).trim();
|
|
3190
3245
|
}
|
|
3191
|
-
_cached2 =
|
|
3246
|
+
_cached2 = path14.basename(repoRoot);
|
|
3192
3247
|
_cachedCwd = dir;
|
|
3193
3248
|
return _cached2;
|
|
3194
3249
|
} catch {
|
|
3195
|
-
_cached2 =
|
|
3250
|
+
_cached2 = path14.basename(dir);
|
|
3196
3251
|
_cachedCwd = dir;
|
|
3197
3252
|
return _cached2;
|
|
3198
3253
|
}
|
|
@@ -3665,8 +3720,8 @@ __export(tasks_exports, {
|
|
|
3665
3720
|
updateTaskStatus: () => updateTaskStatus,
|
|
3666
3721
|
writeCheckpoint: () => writeCheckpoint
|
|
3667
3722
|
});
|
|
3668
|
-
import
|
|
3669
|
-
import { writeFileSync as
|
|
3723
|
+
import path15 from "path";
|
|
3724
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync6 } from "fs";
|
|
3670
3725
|
async function createTask(input2) {
|
|
3671
3726
|
const result = await createTaskCore(input2);
|
|
3672
3727
|
if (!input2.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -3685,11 +3740,11 @@ async function updateTask(input2) {
|
|
|
3685
3740
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input2);
|
|
3686
3741
|
try {
|
|
3687
3742
|
const agent = String(row.assigned_to);
|
|
3688
|
-
const cacheDir =
|
|
3689
|
-
const cachePath =
|
|
3743
|
+
const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
|
|
3744
|
+
const cachePath = path15.join(cacheDir, `current-task-${agent}.json`);
|
|
3690
3745
|
if (input2.status === "in_progress") {
|
|
3691
|
-
|
|
3692
|
-
|
|
3746
|
+
mkdirSync6(cacheDir, { recursive: true });
|
|
3747
|
+
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
3693
3748
|
} else if (input2.status === "done" || input2.status === "blocked" || input2.status === "cancelled") {
|
|
3694
3749
|
try {
|
|
3695
3750
|
unlinkSync6(cachePath);
|
|
@@ -4156,13 +4211,13 @@ __export(tmux_routing_exports, {
|
|
|
4156
4211
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
4157
4212
|
});
|
|
4158
4213
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
4159
|
-
import { readFileSync as
|
|
4160
|
-
import
|
|
4214
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, existsSync as existsSync12, appendFileSync } from "fs";
|
|
4215
|
+
import path16 from "path";
|
|
4161
4216
|
import os7 from "os";
|
|
4162
4217
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4163
4218
|
import { unlinkSync as unlinkSync7 } from "fs";
|
|
4164
4219
|
function spawnLockPath(sessionName) {
|
|
4165
|
-
return
|
|
4220
|
+
return path16.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4166
4221
|
}
|
|
4167
4222
|
function isProcessAlive(pid) {
|
|
4168
4223
|
try {
|
|
@@ -4173,13 +4228,13 @@ function isProcessAlive(pid) {
|
|
|
4173
4228
|
}
|
|
4174
4229
|
}
|
|
4175
4230
|
function acquireSpawnLock2(sessionName) {
|
|
4176
|
-
if (!
|
|
4177
|
-
|
|
4231
|
+
if (!existsSync12(SPAWN_LOCK_DIR)) {
|
|
4232
|
+
mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
|
|
4178
4233
|
}
|
|
4179
4234
|
const lockFile = spawnLockPath(sessionName);
|
|
4180
|
-
if (
|
|
4235
|
+
if (existsSync12(lockFile)) {
|
|
4181
4236
|
try {
|
|
4182
|
-
const lock = JSON.parse(
|
|
4237
|
+
const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
|
|
4183
4238
|
const age = Date.now() - lock.timestamp;
|
|
4184
4239
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
4185
4240
|
return false;
|
|
@@ -4187,7 +4242,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
4187
4242
|
} catch {
|
|
4188
4243
|
}
|
|
4189
4244
|
}
|
|
4190
|
-
|
|
4245
|
+
writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
4191
4246
|
return true;
|
|
4192
4247
|
}
|
|
4193
4248
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -4199,13 +4254,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
4199
4254
|
function resolveBehaviorsExporterScript() {
|
|
4200
4255
|
try {
|
|
4201
4256
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4202
|
-
const scriptPath =
|
|
4203
|
-
|
|
4257
|
+
const scriptPath = path16.join(
|
|
4258
|
+
path16.dirname(thisFile),
|
|
4204
4259
|
"..",
|
|
4205
4260
|
"bin",
|
|
4206
4261
|
"exe-export-behaviors.js"
|
|
4207
4262
|
);
|
|
4208
|
-
return
|
|
4263
|
+
return existsSync12(scriptPath) ? scriptPath : null;
|
|
4209
4264
|
} catch {
|
|
4210
4265
|
return null;
|
|
4211
4266
|
}
|
|
@@ -4271,12 +4326,12 @@ function extractRootExe(name) {
|
|
|
4271
4326
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
4272
4327
|
}
|
|
4273
4328
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
4274
|
-
if (!
|
|
4275
|
-
|
|
4329
|
+
if (!existsSync12(SESSION_CACHE)) {
|
|
4330
|
+
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
4276
4331
|
}
|
|
4277
4332
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
4278
|
-
const filePath =
|
|
4279
|
-
|
|
4333
|
+
const filePath = path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
4334
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
4280
4335
|
parentExe: rootExe,
|
|
4281
4336
|
dispatchedBy: dispatchedBy || rootExe,
|
|
4282
4337
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -4284,7 +4339,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4284
4339
|
}
|
|
4285
4340
|
function getParentExe(sessionKey) {
|
|
4286
4341
|
try {
|
|
4287
|
-
const data = JSON.parse(
|
|
4342
|
+
const data = JSON.parse(readFileSync12(path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4288
4343
|
return data.parentExe || null;
|
|
4289
4344
|
} catch {
|
|
4290
4345
|
return null;
|
|
@@ -4292,8 +4347,8 @@ function getParentExe(sessionKey) {
|
|
|
4292
4347
|
}
|
|
4293
4348
|
function getDispatchedBy(sessionKey) {
|
|
4294
4349
|
try {
|
|
4295
|
-
const data = JSON.parse(
|
|
4296
|
-
|
|
4350
|
+
const data = JSON.parse(readFileSync12(
|
|
4351
|
+
path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4297
4352
|
"utf8"
|
|
4298
4353
|
));
|
|
4299
4354
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -4354,32 +4409,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
4354
4409
|
}
|
|
4355
4410
|
function readDebounceState() {
|
|
4356
4411
|
try {
|
|
4357
|
-
if (!
|
|
4358
|
-
|
|
4412
|
+
if (!existsSync12(DEBOUNCE_FILE)) return {};
|
|
4413
|
+
const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
|
|
4414
|
+
const state = {};
|
|
4415
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
4416
|
+
if (typeof val === "number") {
|
|
4417
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
4418
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
4419
|
+
state[key] = val;
|
|
4420
|
+
}
|
|
4421
|
+
}
|
|
4422
|
+
return state;
|
|
4359
4423
|
} catch {
|
|
4360
4424
|
return {};
|
|
4361
4425
|
}
|
|
4362
4426
|
}
|
|
4363
4427
|
function writeDebounceState(state) {
|
|
4364
4428
|
try {
|
|
4365
|
-
if (!
|
|
4366
|
-
|
|
4429
|
+
if (!existsSync12(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
4430
|
+
writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
|
|
4367
4431
|
} catch {
|
|
4368
4432
|
}
|
|
4369
4433
|
}
|
|
4370
4434
|
function isDebounced(targetSession) {
|
|
4371
4435
|
const state = readDebounceState();
|
|
4372
|
-
const
|
|
4373
|
-
|
|
4436
|
+
const entry = state[targetSession];
|
|
4437
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
4438
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
4439
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
4440
|
+
state[targetSession].pending++;
|
|
4441
|
+
writeDebounceState(state);
|
|
4442
|
+
return true;
|
|
4443
|
+
}
|
|
4444
|
+
return false;
|
|
4374
4445
|
}
|
|
4375
4446
|
function recordDebounce(targetSession) {
|
|
4376
4447
|
const state = readDebounceState();
|
|
4377
|
-
state[targetSession]
|
|
4448
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
4449
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
4378
4450
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
4379
4451
|
for (const key of Object.keys(state)) {
|
|
4380
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
4452
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
4381
4453
|
}
|
|
4382
4454
|
writeDebounceState(state);
|
|
4455
|
+
return batched;
|
|
4383
4456
|
}
|
|
4384
4457
|
function logIntercom(msg) {
|
|
4385
4458
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -4424,7 +4497,7 @@ function sendIntercom(targetSession) {
|
|
|
4424
4497
|
return "skipped_exe";
|
|
4425
4498
|
}
|
|
4426
4499
|
if (isDebounced(targetSession)) {
|
|
4427
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
4500
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
4428
4501
|
return "debounced";
|
|
4429
4502
|
}
|
|
4430
4503
|
try {
|
|
@@ -4436,14 +4509,14 @@ function sendIntercom(targetSession) {
|
|
|
4436
4509
|
const sessionState = getSessionState(targetSession);
|
|
4437
4510
|
if (sessionState === "no_claude") {
|
|
4438
4511
|
queueIntercom(targetSession, "claude not running in session");
|
|
4439
|
-
recordDebounce(targetSession);
|
|
4440
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
4512
|
+
const batched2 = recordDebounce(targetSession);
|
|
4513
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
4441
4514
|
return "queued";
|
|
4442
4515
|
}
|
|
4443
4516
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
4444
4517
|
queueIntercom(targetSession, "session busy at send time");
|
|
4445
|
-
recordDebounce(targetSession);
|
|
4446
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
4518
|
+
const batched2 = recordDebounce(targetSession);
|
|
4519
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
4447
4520
|
return "queued";
|
|
4448
4521
|
}
|
|
4449
4522
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -4451,8 +4524,8 @@ function sendIntercom(targetSession) {
|
|
|
4451
4524
|
transport.sendKeys(targetSession, "q");
|
|
4452
4525
|
}
|
|
4453
4526
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
4454
|
-
recordDebounce(targetSession);
|
|
4455
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
4527
|
+
const batched = recordDebounce(targetSession);
|
|
4528
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
4456
4529
|
return "delivered";
|
|
4457
4530
|
} catch {
|
|
4458
4531
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -4554,26 +4627,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4554
4627
|
const transport = getTransport();
|
|
4555
4628
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4556
4629
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4557
|
-
const logDir =
|
|
4558
|
-
const logFile =
|
|
4559
|
-
if (!
|
|
4560
|
-
|
|
4630
|
+
const logDir = path16.join(os7.homedir(), ".exe-os", "session-logs");
|
|
4631
|
+
const logFile = path16.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4632
|
+
if (!existsSync12(logDir)) {
|
|
4633
|
+
mkdirSync7(logDir, { recursive: true });
|
|
4561
4634
|
}
|
|
4562
4635
|
transport.kill(sessionName);
|
|
4563
4636
|
let cleanupSuffix = "";
|
|
4564
4637
|
try {
|
|
4565
4638
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4566
|
-
const cleanupScript =
|
|
4567
|
-
if (
|
|
4639
|
+
const cleanupScript = path16.join(path16.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4640
|
+
if (existsSync12(cleanupScript)) {
|
|
4568
4641
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
4569
4642
|
}
|
|
4570
4643
|
} catch {
|
|
4571
4644
|
}
|
|
4572
4645
|
try {
|
|
4573
|
-
const claudeJsonPath =
|
|
4646
|
+
const claudeJsonPath = path16.join(os7.homedir(), ".claude.json");
|
|
4574
4647
|
let claudeJson = {};
|
|
4575
4648
|
try {
|
|
4576
|
-
claudeJson = JSON.parse(
|
|
4649
|
+
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
4577
4650
|
} catch {
|
|
4578
4651
|
}
|
|
4579
4652
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -4581,17 +4654,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4581
4654
|
const trustDir = opts?.cwd ?? projectDir;
|
|
4582
4655
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
4583
4656
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
4584
|
-
|
|
4657
|
+
writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
4585
4658
|
} catch {
|
|
4586
4659
|
}
|
|
4587
4660
|
try {
|
|
4588
|
-
const settingsDir =
|
|
4661
|
+
const settingsDir = path16.join(os7.homedir(), ".claude", "projects");
|
|
4589
4662
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4590
|
-
const projSettingsDir =
|
|
4591
|
-
const settingsPath =
|
|
4663
|
+
const projSettingsDir = path16.join(settingsDir, normalizedKey);
|
|
4664
|
+
const settingsPath = path16.join(projSettingsDir, "settings.json");
|
|
4592
4665
|
let settings = {};
|
|
4593
4666
|
try {
|
|
4594
|
-
settings = JSON.parse(
|
|
4667
|
+
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
4595
4668
|
} catch {
|
|
4596
4669
|
}
|
|
4597
4670
|
const perms = settings.permissions ?? {};
|
|
@@ -4619,20 +4692,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4619
4692
|
if (changed) {
|
|
4620
4693
|
perms.allow = allow;
|
|
4621
4694
|
settings.permissions = perms;
|
|
4622
|
-
|
|
4623
|
-
|
|
4695
|
+
mkdirSync7(projSettingsDir, { recursive: true });
|
|
4696
|
+
writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
4624
4697
|
}
|
|
4625
4698
|
} catch {
|
|
4626
4699
|
}
|
|
4627
4700
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
4628
4701
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
4629
|
-
const
|
|
4702
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
4703
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
4704
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
4705
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
4630
4706
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
4631
4707
|
let identityFlag = "";
|
|
4632
4708
|
let behaviorsFlag = "";
|
|
4633
4709
|
let legacyFallbackWarned = false;
|
|
4634
4710
|
if (!useExeAgent && !useBinSymlink) {
|
|
4635
|
-
const identityPath =
|
|
4711
|
+
const identityPath = path16.join(
|
|
4636
4712
|
os7.homedir(),
|
|
4637
4713
|
".exe-os",
|
|
4638
4714
|
"identity",
|
|
@@ -4642,13 +4718,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4642
4718
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
4643
4719
|
if (hasAgentFlag) {
|
|
4644
4720
|
identityFlag = ` --agent ${employeeName}`;
|
|
4645
|
-
} else if (
|
|
4721
|
+
} else if (existsSync12(identityPath)) {
|
|
4646
4722
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
4647
4723
|
legacyFallbackWarned = true;
|
|
4648
4724
|
}
|
|
4649
4725
|
const behaviorsFile = exportBehaviorsSync(
|
|
4650
4726
|
employeeName,
|
|
4651
|
-
|
|
4727
|
+
path16.basename(spawnCwd),
|
|
4652
4728
|
sessionName
|
|
4653
4729
|
);
|
|
4654
4730
|
if (behaviorsFile) {
|
|
@@ -4663,16 +4739,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4663
4739
|
}
|
|
4664
4740
|
let sessionContextFlag = "";
|
|
4665
4741
|
try {
|
|
4666
|
-
const ctxDir =
|
|
4667
|
-
|
|
4668
|
-
const ctxFile =
|
|
4742
|
+
const ctxDir = path16.join(os7.homedir(), ".exe-os", "session-cache");
|
|
4743
|
+
mkdirSync7(ctxDir, { recursive: true });
|
|
4744
|
+
const ctxFile = path16.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4669
4745
|
const ctxContent = [
|
|
4670
4746
|
`## Session Context`,
|
|
4671
4747
|
`You are running in tmux session: ${sessionName}.`,
|
|
4672
4748
|
`Your parent coordinator session is ${exeSession}.`,
|
|
4673
4749
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
4674
4750
|
].join("\n");
|
|
4675
|
-
|
|
4751
|
+
writeFileSync8(ctxFile, ctxContent);
|
|
4676
4752
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
4677
4753
|
} catch {
|
|
4678
4754
|
}
|
|
@@ -4686,9 +4762,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4686
4762
|
}
|
|
4687
4763
|
}
|
|
4688
4764
|
}
|
|
4765
|
+
if (useCodex) {
|
|
4766
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
4767
|
+
if (codexCfg?.apiKeyEnv) {
|
|
4768
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
4769
|
+
if (keyVal) {
|
|
4770
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
4771
|
+
}
|
|
4772
|
+
}
|
|
4773
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
4774
|
+
}
|
|
4775
|
+
if (useOpencode) {
|
|
4776
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
4777
|
+
if (ocCfg?.apiKeyEnv) {
|
|
4778
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
4779
|
+
if (keyVal) {
|
|
4780
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
4781
|
+
}
|
|
4782
|
+
}
|
|
4783
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4784
|
+
}
|
|
4785
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
4786
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
4787
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
4788
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4789
|
+
}
|
|
4790
|
+
}
|
|
4689
4791
|
let spawnCommand;
|
|
4690
4792
|
if (useExeAgent) {
|
|
4691
4793
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
4794
|
+
} else if (useCodex) {
|
|
4795
|
+
process.stderr.write(
|
|
4796
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
4797
|
+
`
|
|
4798
|
+
);
|
|
4799
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
4800
|
+
} else if (useOpencode) {
|
|
4801
|
+
const binName = `${employeeName}-opencode`;
|
|
4802
|
+
process.stderr.write(
|
|
4803
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
4804
|
+
`
|
|
4805
|
+
);
|
|
4806
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
4692
4807
|
} else if (useBinSymlink) {
|
|
4693
4808
|
const binName = `${employeeName}-${ccProvider}`;
|
|
4694
4809
|
process.stderr.write(
|
|
@@ -4710,11 +4825,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4710
4825
|
transport.pipeLog(sessionName, logFile);
|
|
4711
4826
|
try {
|
|
4712
4827
|
const mySession = getMySession();
|
|
4713
|
-
const dispatchInfo =
|
|
4714
|
-
|
|
4828
|
+
const dispatchInfo = path16.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
4829
|
+
writeFileSync8(dispatchInfo, JSON.stringify({
|
|
4715
4830
|
dispatchedBy: mySession,
|
|
4716
4831
|
rootExe: exeSession,
|
|
4717
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
4832
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
4833
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
4834
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
4718
4835
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4719
4836
|
}));
|
|
4720
4837
|
} catch {
|
|
@@ -4732,6 +4849,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4732
4849
|
booted = true;
|
|
4733
4850
|
break;
|
|
4734
4851
|
}
|
|
4852
|
+
} else if (useCodex) {
|
|
4853
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
4854
|
+
booted = true;
|
|
4855
|
+
break;
|
|
4856
|
+
}
|
|
4735
4857
|
} else {
|
|
4736
4858
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
4737
4859
|
booted = true;
|
|
@@ -4743,9 +4865,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4743
4865
|
}
|
|
4744
4866
|
if (!booted) {
|
|
4745
4867
|
releaseSpawnLock2(sessionName);
|
|
4746
|
-
|
|
4868
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
4869
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
4747
4870
|
}
|
|
4748
|
-
if (!useExeAgent) {
|
|
4871
|
+
if (!useExeAgent && !useCodex) {
|
|
4749
4872
|
try {
|
|
4750
4873
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
4751
4874
|
} catch {
|
|
@@ -4772,17 +4895,19 @@ var init_tmux_routing = __esm({
|
|
|
4772
4895
|
init_cc_agent_support();
|
|
4773
4896
|
init_mcp_prefix();
|
|
4774
4897
|
init_provider_table();
|
|
4898
|
+
init_agent_config();
|
|
4899
|
+
init_runtime_table();
|
|
4775
4900
|
init_intercom_queue();
|
|
4776
4901
|
init_plan_limits();
|
|
4777
4902
|
init_employees();
|
|
4778
|
-
SPAWN_LOCK_DIR =
|
|
4779
|
-
SESSION_CACHE =
|
|
4903
|
+
SPAWN_LOCK_DIR = path16.join(os7.homedir(), ".exe-os", "spawn-locks");
|
|
4904
|
+
SESSION_CACHE = path16.join(os7.homedir(), ".exe-os", "session-cache");
|
|
4780
4905
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4781
4906
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
4782
4907
|
VERIFY_PANE_LINES = 200;
|
|
4783
4908
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4784
|
-
INTERCOM_LOG2 =
|
|
4785
|
-
DEBOUNCE_FILE =
|
|
4909
|
+
INTERCOM_LOG2 = path16.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
4910
|
+
DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4786
4911
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4787
4912
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
4788
4913
|
}
|
|
@@ -4823,14 +4948,14 @@ var init_memory = __esm({
|
|
|
4823
4948
|
|
|
4824
4949
|
// src/lib/keychain.ts
|
|
4825
4950
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
4826
|
-
import { existsSync as
|
|
4827
|
-
import
|
|
4951
|
+
import { existsSync as existsSync13 } from "fs";
|
|
4952
|
+
import path17 from "path";
|
|
4828
4953
|
import os8 from "os";
|
|
4829
4954
|
function getKeyDir() {
|
|
4830
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
4955
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path17.join(os8.homedir(), ".exe-os");
|
|
4831
4956
|
}
|
|
4832
4957
|
function getKeyPath() {
|
|
4833
|
-
return
|
|
4958
|
+
return path17.join(getKeyDir(), "master.key");
|
|
4834
4959
|
}
|
|
4835
4960
|
async function tryKeytar() {
|
|
4836
4961
|
try {
|
|
@@ -4851,7 +4976,7 @@ async function getMasterKey() {
|
|
|
4851
4976
|
}
|
|
4852
4977
|
}
|
|
4853
4978
|
const keyPath = getKeyPath();
|
|
4854
|
-
if (!
|
|
4979
|
+
if (!existsSync13(keyPath)) {
|
|
4855
4980
|
process.stderr.write(
|
|
4856
4981
|
`[keychain] Key not found at ${keyPath} (HOME=${os8.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
4857
4982
|
`
|
|
@@ -4891,13 +5016,13 @@ __export(shard_manager_exports, {
|
|
|
4891
5016
|
listShards: () => listShards,
|
|
4892
5017
|
shardExists: () => shardExists
|
|
4893
5018
|
});
|
|
4894
|
-
import
|
|
4895
|
-
import { existsSync as
|
|
5019
|
+
import path18 from "path";
|
|
5020
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync8, readdirSync as readdirSync4 } from "fs";
|
|
4896
5021
|
import { createClient as createClient2 } from "@libsql/client";
|
|
4897
5022
|
function initShardManager(encryptionKey) {
|
|
4898
5023
|
_encryptionKey = encryptionKey;
|
|
4899
|
-
if (!
|
|
4900
|
-
|
|
5024
|
+
if (!existsSync14(SHARDS_DIR)) {
|
|
5025
|
+
mkdirSync8(SHARDS_DIR, { recursive: true });
|
|
4901
5026
|
}
|
|
4902
5027
|
_shardingEnabled = true;
|
|
4903
5028
|
}
|
|
@@ -4917,7 +5042,7 @@ function getShardClient(projectName) {
|
|
|
4917
5042
|
}
|
|
4918
5043
|
const cached = _shards.get(safeName);
|
|
4919
5044
|
if (cached) return cached;
|
|
4920
|
-
const dbPath =
|
|
5045
|
+
const dbPath = path18.join(SHARDS_DIR, `${safeName}.db`);
|
|
4921
5046
|
const client = createClient2({
|
|
4922
5047
|
url: `file:${dbPath}`,
|
|
4923
5048
|
encryptionKey: _encryptionKey
|
|
@@ -4927,10 +5052,10 @@ function getShardClient(projectName) {
|
|
|
4927
5052
|
}
|
|
4928
5053
|
function shardExists(projectName) {
|
|
4929
5054
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
4930
|
-
return
|
|
5055
|
+
return existsSync14(path18.join(SHARDS_DIR, `${safeName}.db`));
|
|
4931
5056
|
}
|
|
4932
5057
|
function listShards() {
|
|
4933
|
-
if (!
|
|
5058
|
+
if (!existsSync14(SHARDS_DIR)) return [];
|
|
4934
5059
|
return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
4935
5060
|
}
|
|
4936
5061
|
async function ensureShardSchema(client) {
|
|
@@ -5116,7 +5241,7 @@ var init_shard_manager = __esm({
|
|
|
5116
5241
|
"src/lib/shard-manager.ts"() {
|
|
5117
5242
|
"use strict";
|
|
5118
5243
|
init_config();
|
|
5119
|
-
SHARDS_DIR =
|
|
5244
|
+
SHARDS_DIR = path18.join(EXE_AI_DIR, "shards");
|
|
5120
5245
|
_shards = /* @__PURE__ */ new Map();
|
|
5121
5246
|
_encryptionKey = null;
|
|
5122
5247
|
_shardingEnabled = false;
|