@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
package/dist/runtime/index.js
CHANGED
|
@@ -544,18 +544,69 @@ var init_provider_table = __esm({
|
|
|
544
544
|
}
|
|
545
545
|
});
|
|
546
546
|
|
|
547
|
-
// src/lib/
|
|
548
|
-
|
|
547
|
+
// src/lib/runtime-table.ts
|
|
548
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
549
|
+
var init_runtime_table = __esm({
|
|
550
|
+
"src/lib/runtime-table.ts"() {
|
|
551
|
+
"use strict";
|
|
552
|
+
RUNTIME_TABLE = {
|
|
553
|
+
codex: {
|
|
554
|
+
binary: "codex",
|
|
555
|
+
launchMode: "exec",
|
|
556
|
+
autoApproveFlag: "--full-auto",
|
|
557
|
+
inlineFlag: "--no-alt-screen",
|
|
558
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
559
|
+
defaultModel: "gpt-5.4"
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
DEFAULT_RUNTIME = "claude";
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
// src/lib/agent-config.ts
|
|
567
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
|
|
549
568
|
import path5 from "path";
|
|
569
|
+
function loadAgentConfig() {
|
|
570
|
+
if (!existsSync4(AGENT_CONFIG_PATH)) return {};
|
|
571
|
+
try {
|
|
572
|
+
return JSON.parse(readFileSync4(AGENT_CONFIG_PATH, "utf-8"));
|
|
573
|
+
} catch {
|
|
574
|
+
return {};
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
function getAgentRuntime(agentId) {
|
|
578
|
+
const config = loadAgentConfig();
|
|
579
|
+
const entry = config[agentId];
|
|
580
|
+
if (entry) return entry;
|
|
581
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
582
|
+
}
|
|
583
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
584
|
+
var init_agent_config = __esm({
|
|
585
|
+
"src/lib/agent-config.ts"() {
|
|
586
|
+
"use strict";
|
|
587
|
+
init_config();
|
|
588
|
+
init_runtime_table();
|
|
589
|
+
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
590
|
+
DEFAULT_MODELS = {
|
|
591
|
+
claude: "claude-opus-4",
|
|
592
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
593
|
+
opencode: "minimax-m2.7"
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
// src/lib/intercom-queue.ts
|
|
599
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
600
|
+
import path6 from "path";
|
|
550
601
|
import os5 from "os";
|
|
551
602
|
function ensureDir() {
|
|
552
|
-
const dir =
|
|
553
|
-
if (!
|
|
603
|
+
const dir = path6.dirname(QUEUE_PATH);
|
|
604
|
+
if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
|
|
554
605
|
}
|
|
555
606
|
function readQueue() {
|
|
556
607
|
try {
|
|
557
|
-
if (!
|
|
558
|
-
return JSON.parse(
|
|
608
|
+
if (!existsSync5(QUEUE_PATH)) return [];
|
|
609
|
+
return JSON.parse(readFileSync5(QUEUE_PATH, "utf8"));
|
|
559
610
|
} catch {
|
|
560
611
|
return [];
|
|
561
612
|
}
|
|
@@ -563,7 +614,7 @@ function readQueue() {
|
|
|
563
614
|
function writeQueue(queue) {
|
|
564
615
|
ensureDir();
|
|
565
616
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
566
|
-
|
|
617
|
+
writeFileSync4(tmp, JSON.stringify(queue, null, 2));
|
|
567
618
|
renameSync3(tmp, QUEUE_PATH);
|
|
568
619
|
}
|
|
569
620
|
function queueIntercom(targetSession, reason) {
|
|
@@ -587,9 +638,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
587
638
|
var init_intercom_queue = __esm({
|
|
588
639
|
"src/lib/intercom-queue.ts"() {
|
|
589
640
|
"use strict";
|
|
590
|
-
QUEUE_PATH =
|
|
641
|
+
QUEUE_PATH = path6.join(os5.homedir(), ".exe-os", "intercom-queue.json");
|
|
591
642
|
TTL_MS = 60 * 60 * 1e3;
|
|
592
|
-
INTERCOM_LOG =
|
|
643
|
+
INTERCOM_LOG = path6.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
593
644
|
}
|
|
594
645
|
});
|
|
595
646
|
|
|
@@ -652,8 +703,8 @@ var init_db_retry = __esm({
|
|
|
652
703
|
import net from "net";
|
|
653
704
|
import { spawn } from "child_process";
|
|
654
705
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
655
|
-
import { existsSync as
|
|
656
|
-
import
|
|
706
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
|
|
707
|
+
import path7 from "path";
|
|
657
708
|
import { fileURLToPath } from "url";
|
|
658
709
|
function handleData(chunk) {
|
|
659
710
|
_buffer += chunk.toString();
|
|
@@ -681,9 +732,9 @@ function handleData(chunk) {
|
|
|
681
732
|
}
|
|
682
733
|
}
|
|
683
734
|
function cleanupStaleFiles() {
|
|
684
|
-
if (
|
|
735
|
+
if (existsSync6(PID_PATH)) {
|
|
685
736
|
try {
|
|
686
|
-
const pid = parseInt(
|
|
737
|
+
const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
|
|
687
738
|
if (pid > 0) {
|
|
688
739
|
try {
|
|
689
740
|
process.kill(pid, 0);
|
|
@@ -704,11 +755,11 @@ function cleanupStaleFiles() {
|
|
|
704
755
|
}
|
|
705
756
|
}
|
|
706
757
|
function findPackageRoot() {
|
|
707
|
-
let dir =
|
|
708
|
-
const { root } =
|
|
758
|
+
let dir = path7.dirname(fileURLToPath(import.meta.url));
|
|
759
|
+
const { root } = path7.parse(dir);
|
|
709
760
|
while (dir !== root) {
|
|
710
|
-
if (
|
|
711
|
-
dir =
|
|
761
|
+
if (existsSync6(path7.join(dir, "package.json"))) return dir;
|
|
762
|
+
dir = path7.dirname(dir);
|
|
712
763
|
}
|
|
713
764
|
return null;
|
|
714
765
|
}
|
|
@@ -718,8 +769,8 @@ function spawnDaemon() {
|
|
|
718
769
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
719
770
|
return;
|
|
720
771
|
}
|
|
721
|
-
const daemonPath =
|
|
722
|
-
if (!
|
|
772
|
+
const daemonPath = path7.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
773
|
+
if (!existsSync6(daemonPath)) {
|
|
723
774
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
724
775
|
`);
|
|
725
776
|
return;
|
|
@@ -727,7 +778,7 @@ function spawnDaemon() {
|
|
|
727
778
|
const resolvedPath = daemonPath;
|
|
728
779
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
729
780
|
`);
|
|
730
|
-
const logPath =
|
|
781
|
+
const logPath = path7.join(path7.dirname(SOCKET_PATH), "exed.log");
|
|
731
782
|
let stderrFd = "ignore";
|
|
732
783
|
try {
|
|
733
784
|
stderrFd = openSync(logPath, "a");
|
|
@@ -872,9 +923,9 @@ var init_exe_daemon_client = __esm({
|
|
|
872
923
|
"src/lib/exe-daemon-client.ts"() {
|
|
873
924
|
"use strict";
|
|
874
925
|
init_config();
|
|
875
|
-
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ??
|
|
876
|
-
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ??
|
|
877
|
-
SPAWN_LOCK_PATH =
|
|
926
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path7.join(EXE_AI_DIR, "exed.sock");
|
|
927
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path7.join(EXE_AI_DIR, "exed.pid");
|
|
928
|
+
SPAWN_LOCK_PATH = path7.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
878
929
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
879
930
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
880
931
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -2099,18 +2150,18 @@ var init_database = __esm({
|
|
|
2099
2150
|
});
|
|
2100
2151
|
|
|
2101
2152
|
// src/lib/license.ts
|
|
2102
|
-
import { readFileSync as
|
|
2153
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
2103
2154
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2104
|
-
import
|
|
2155
|
+
import path8 from "path";
|
|
2105
2156
|
import { jwtVerify, importSPKI } from "jose";
|
|
2106
2157
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
2107
2158
|
var init_license = __esm({
|
|
2108
2159
|
"src/lib/license.ts"() {
|
|
2109
2160
|
"use strict";
|
|
2110
2161
|
init_config();
|
|
2111
|
-
LICENSE_PATH =
|
|
2112
|
-
CACHE_PATH =
|
|
2113
|
-
DEVICE_ID_PATH =
|
|
2162
|
+
LICENSE_PATH = path8.join(EXE_AI_DIR, "license.key");
|
|
2163
|
+
CACHE_PATH = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
2164
|
+
DEVICE_ID_PATH = path8.join(EXE_AI_DIR, "device-id");
|
|
2114
2165
|
PLAN_LIMITS = {
|
|
2115
2166
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
2116
2167
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -2122,12 +2173,12 @@ var init_license = __esm({
|
|
|
2122
2173
|
});
|
|
2123
2174
|
|
|
2124
2175
|
// src/lib/plan-limits.ts
|
|
2125
|
-
import { readFileSync as
|
|
2126
|
-
import
|
|
2176
|
+
import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
|
|
2177
|
+
import path9 from "path";
|
|
2127
2178
|
function getLicenseSync() {
|
|
2128
2179
|
try {
|
|
2129
|
-
if (!
|
|
2130
|
-
const raw = JSON.parse(
|
|
2180
|
+
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
2181
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
|
|
2131
2182
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
2132
2183
|
const parts = raw.token.split(".");
|
|
2133
2184
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -2165,8 +2216,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
2165
2216
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
2166
2217
|
let count = 0;
|
|
2167
2218
|
try {
|
|
2168
|
-
if (
|
|
2169
|
-
const raw =
|
|
2219
|
+
if (existsSync8(filePath)) {
|
|
2220
|
+
const raw = readFileSync8(filePath, "utf8");
|
|
2170
2221
|
const employees = JSON.parse(raw);
|
|
2171
2222
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
2172
2223
|
}
|
|
@@ -2195,19 +2246,19 @@ var init_plan_limits = __esm({
|
|
|
2195
2246
|
this.name = "PlanLimitError";
|
|
2196
2247
|
}
|
|
2197
2248
|
};
|
|
2198
|
-
CACHE_PATH2 =
|
|
2249
|
+
CACHE_PATH2 = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
2199
2250
|
}
|
|
2200
2251
|
});
|
|
2201
2252
|
|
|
2202
2253
|
// src/lib/notifications.ts
|
|
2203
2254
|
import crypto from "crypto";
|
|
2204
|
-
import
|
|
2255
|
+
import path10 from "path";
|
|
2205
2256
|
import os6 from "os";
|
|
2206
2257
|
import {
|
|
2207
|
-
readFileSync as
|
|
2258
|
+
readFileSync as readFileSync9,
|
|
2208
2259
|
readdirSync,
|
|
2209
2260
|
unlinkSync as unlinkSync3,
|
|
2210
|
-
existsSync as
|
|
2261
|
+
existsSync as existsSync9,
|
|
2211
2262
|
rmdirSync
|
|
2212
2263
|
} from "fs";
|
|
2213
2264
|
async function writeNotification(notification) {
|
|
@@ -2371,11 +2422,11 @@ var init_state_bus = __esm({
|
|
|
2371
2422
|
|
|
2372
2423
|
// src/lib/tasks-crud.ts
|
|
2373
2424
|
import crypto3 from "crypto";
|
|
2374
|
-
import
|
|
2425
|
+
import path11 from "path";
|
|
2375
2426
|
import os7 from "os";
|
|
2376
2427
|
import { execSync as execSync5 } from "child_process";
|
|
2377
2428
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2378
|
-
import { existsSync as
|
|
2429
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
2379
2430
|
async function writeCheckpoint(input) {
|
|
2380
2431
|
const client = getClient();
|
|
2381
2432
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -2550,8 +2601,8 @@ ${laneWarning}` : laneWarning;
|
|
|
2550
2601
|
}
|
|
2551
2602
|
if (input.baseDir) {
|
|
2552
2603
|
try {
|
|
2553
|
-
await mkdir3(
|
|
2554
|
-
await mkdir3(
|
|
2604
|
+
await mkdir3(path11.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
2605
|
+
await mkdir3(path11.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
2555
2606
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
2556
2607
|
await ensureGitignoreExe(input.baseDir);
|
|
2557
2608
|
} catch {
|
|
@@ -2587,10 +2638,10 @@ ${laneWarning}` : laneWarning;
|
|
|
2587
2638
|
});
|
|
2588
2639
|
if (input.baseDir) {
|
|
2589
2640
|
try {
|
|
2590
|
-
const EXE_OS_DIR =
|
|
2591
|
-
const mdPath =
|
|
2592
|
-
const mdDir =
|
|
2593
|
-
if (!
|
|
2641
|
+
const EXE_OS_DIR = path11.join(os7.homedir(), ".exe-os");
|
|
2642
|
+
const mdPath = path11.join(EXE_OS_DIR, taskFile);
|
|
2643
|
+
const mdDir = path11.dirname(mdPath);
|
|
2644
|
+
if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
2594
2645
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
2595
2646
|
const mdContent = `# ${input.title}
|
|
2596
2647
|
|
|
@@ -2615,7 +2666,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
|
|
|
2615
2666
|
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
2616
2667
|
`;
|
|
2617
2668
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
2618
|
-
} catch {
|
|
2669
|
+
} catch (err) {
|
|
2670
|
+
process.stderr.write(
|
|
2671
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
2672
|
+
`
|
|
2673
|
+
);
|
|
2619
2674
|
}
|
|
2620
2675
|
}
|
|
2621
2676
|
return {
|
|
@@ -2875,9 +2930,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
2875
2930
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
2876
2931
|
}
|
|
2877
2932
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
2878
|
-
const archPath =
|
|
2933
|
+
const archPath = path11.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
2879
2934
|
try {
|
|
2880
|
-
if (
|
|
2935
|
+
if (existsSync10(archPath)) return;
|
|
2881
2936
|
const template = [
|
|
2882
2937
|
`# ${projectName} \u2014 System Architecture`,
|
|
2883
2938
|
"",
|
|
@@ -2910,10 +2965,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
2910
2965
|
}
|
|
2911
2966
|
}
|
|
2912
2967
|
async function ensureGitignoreExe(baseDir) {
|
|
2913
|
-
const gitignorePath =
|
|
2968
|
+
const gitignorePath = path11.join(baseDir, ".gitignore");
|
|
2914
2969
|
try {
|
|
2915
|
-
if (
|
|
2916
|
-
const content =
|
|
2970
|
+
if (existsSync10(gitignorePath)) {
|
|
2971
|
+
const content = readFileSync10(gitignorePath, "utf-8");
|
|
2917
2972
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
2918
2973
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
2919
2974
|
} else {
|
|
@@ -2944,8 +2999,8 @@ var init_tasks_crud = __esm({
|
|
|
2944
2999
|
});
|
|
2945
3000
|
|
|
2946
3001
|
// src/lib/tasks-review.ts
|
|
2947
|
-
import
|
|
2948
|
-
import { existsSync as
|
|
3002
|
+
import path12 from "path";
|
|
3003
|
+
import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
2949
3004
|
async function countPendingReviews(sessionScope) {
|
|
2950
3005
|
const client = getClient();
|
|
2951
3006
|
if (sessionScope) {
|
|
@@ -3126,11 +3181,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
3126
3181
|
);
|
|
3127
3182
|
}
|
|
3128
3183
|
try {
|
|
3129
|
-
const cacheDir =
|
|
3130
|
-
if (
|
|
3184
|
+
const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
|
|
3185
|
+
if (existsSync11(cacheDir)) {
|
|
3131
3186
|
for (const f of readdirSync2(cacheDir)) {
|
|
3132
3187
|
if (f.startsWith("review-notified-")) {
|
|
3133
|
-
unlinkSync4(
|
|
3188
|
+
unlinkSync4(path12.join(cacheDir, f));
|
|
3134
3189
|
}
|
|
3135
3190
|
}
|
|
3136
3191
|
}
|
|
@@ -3151,7 +3206,7 @@ var init_tasks_review = __esm({
|
|
|
3151
3206
|
});
|
|
3152
3207
|
|
|
3153
3208
|
// src/lib/tasks-chain.ts
|
|
3154
|
-
import
|
|
3209
|
+
import path13 from "path";
|
|
3155
3210
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
3156
3211
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
3157
3212
|
const client = getClient();
|
|
@@ -3168,7 +3223,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
3168
3223
|
});
|
|
3169
3224
|
for (const ur of unblockedRows.rows) {
|
|
3170
3225
|
try {
|
|
3171
|
-
const ubFile =
|
|
3226
|
+
const ubFile = path13.join(baseDir, String(ur.task_file));
|
|
3172
3227
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
3173
3228
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
3174
3229
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -3237,7 +3292,7 @@ var init_tasks_chain = __esm({
|
|
|
3237
3292
|
|
|
3238
3293
|
// src/lib/project-name.ts
|
|
3239
3294
|
import { execSync as execSync6 } from "child_process";
|
|
3240
|
-
import
|
|
3295
|
+
import path14 from "path";
|
|
3241
3296
|
function getProjectName(cwd) {
|
|
3242
3297
|
const dir = cwd ?? process.cwd();
|
|
3243
3298
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -3250,7 +3305,7 @@ function getProjectName(cwd) {
|
|
|
3250
3305
|
timeout: 2e3,
|
|
3251
3306
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3252
3307
|
}).trim();
|
|
3253
|
-
repoRoot =
|
|
3308
|
+
repoRoot = path14.dirname(gitCommonDir);
|
|
3254
3309
|
} catch {
|
|
3255
3310
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
3256
3311
|
cwd: dir,
|
|
@@ -3259,11 +3314,11 @@ function getProjectName(cwd) {
|
|
|
3259
3314
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3260
3315
|
}).trim();
|
|
3261
3316
|
}
|
|
3262
|
-
_cached2 =
|
|
3317
|
+
_cached2 = path14.basename(repoRoot);
|
|
3263
3318
|
_cachedCwd = dir;
|
|
3264
3319
|
return _cached2;
|
|
3265
3320
|
} catch {
|
|
3266
|
-
_cached2 =
|
|
3321
|
+
_cached2 = path14.basename(dir);
|
|
3267
3322
|
_cachedCwd = dir;
|
|
3268
3323
|
return _cached2;
|
|
3269
3324
|
}
|
|
@@ -3798,8 +3853,8 @@ __export(tasks_exports, {
|
|
|
3798
3853
|
updateTaskStatus: () => updateTaskStatus,
|
|
3799
3854
|
writeCheckpoint: () => writeCheckpoint
|
|
3800
3855
|
});
|
|
3801
|
-
import
|
|
3802
|
-
import { writeFileSync as
|
|
3856
|
+
import path15 from "path";
|
|
3857
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
3803
3858
|
async function createTask(input) {
|
|
3804
3859
|
const result = await createTaskCore(input);
|
|
3805
3860
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -3818,11 +3873,11 @@ async function updateTask(input) {
|
|
|
3818
3873
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
3819
3874
|
try {
|
|
3820
3875
|
const agent = String(row.assigned_to);
|
|
3821
|
-
const cacheDir =
|
|
3822
|
-
const cachePath =
|
|
3876
|
+
const cacheDir = path15.join(EXE_AI_DIR, "session-cache");
|
|
3877
|
+
const cachePath = path15.join(cacheDir, `current-task-${agent}.json`);
|
|
3823
3878
|
if (input.status === "in_progress") {
|
|
3824
|
-
|
|
3825
|
-
|
|
3879
|
+
mkdirSync5(cacheDir, { recursive: true });
|
|
3880
|
+
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
3826
3881
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
3827
3882
|
try {
|
|
3828
3883
|
unlinkSync5(cachePath);
|
|
@@ -4289,13 +4344,13 @@ __export(tmux_routing_exports, {
|
|
|
4289
4344
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
4290
4345
|
});
|
|
4291
4346
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
4292
|
-
import { readFileSync as
|
|
4293
|
-
import
|
|
4347
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
|
|
4348
|
+
import path16 from "path";
|
|
4294
4349
|
import os8 from "os";
|
|
4295
4350
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4296
4351
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
4297
4352
|
function spawnLockPath(sessionName) {
|
|
4298
|
-
return
|
|
4353
|
+
return path16.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
4299
4354
|
}
|
|
4300
4355
|
function isProcessAlive(pid) {
|
|
4301
4356
|
try {
|
|
@@ -4306,13 +4361,13 @@ function isProcessAlive(pid) {
|
|
|
4306
4361
|
}
|
|
4307
4362
|
}
|
|
4308
4363
|
function acquireSpawnLock2(sessionName) {
|
|
4309
|
-
if (!
|
|
4310
|
-
|
|
4364
|
+
if (!existsSync12(SPAWN_LOCK_DIR)) {
|
|
4365
|
+
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
4311
4366
|
}
|
|
4312
4367
|
const lockFile = spawnLockPath(sessionName);
|
|
4313
|
-
if (
|
|
4368
|
+
if (existsSync12(lockFile)) {
|
|
4314
4369
|
try {
|
|
4315
|
-
const lock = JSON.parse(
|
|
4370
|
+
const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
|
|
4316
4371
|
const age = Date.now() - lock.timestamp;
|
|
4317
4372
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
4318
4373
|
return false;
|
|
@@ -4320,7 +4375,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
4320
4375
|
} catch {
|
|
4321
4376
|
}
|
|
4322
4377
|
}
|
|
4323
|
-
|
|
4378
|
+
writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
4324
4379
|
return true;
|
|
4325
4380
|
}
|
|
4326
4381
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -4332,13 +4387,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
4332
4387
|
function resolveBehaviorsExporterScript() {
|
|
4333
4388
|
try {
|
|
4334
4389
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4335
|
-
const scriptPath =
|
|
4336
|
-
|
|
4390
|
+
const scriptPath = path16.join(
|
|
4391
|
+
path16.dirname(thisFile),
|
|
4337
4392
|
"..",
|
|
4338
4393
|
"bin",
|
|
4339
4394
|
"exe-export-behaviors.js"
|
|
4340
4395
|
);
|
|
4341
|
-
return
|
|
4396
|
+
return existsSync12(scriptPath) ? scriptPath : null;
|
|
4342
4397
|
} catch {
|
|
4343
4398
|
return null;
|
|
4344
4399
|
}
|
|
@@ -4404,12 +4459,12 @@ function extractRootExe(name) {
|
|
|
4404
4459
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
4405
4460
|
}
|
|
4406
4461
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
4407
|
-
if (!
|
|
4408
|
-
|
|
4462
|
+
if (!existsSync12(SESSION_CACHE)) {
|
|
4463
|
+
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4409
4464
|
}
|
|
4410
4465
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
4411
|
-
const filePath =
|
|
4412
|
-
|
|
4466
|
+
const filePath = path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
4467
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
4413
4468
|
parentExe: rootExe,
|
|
4414
4469
|
dispatchedBy: dispatchedBy || rootExe,
|
|
4415
4470
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -4417,7 +4472,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
4417
4472
|
}
|
|
4418
4473
|
function getParentExe(sessionKey) {
|
|
4419
4474
|
try {
|
|
4420
|
-
const data = JSON.parse(
|
|
4475
|
+
const data = JSON.parse(readFileSync11(path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
4421
4476
|
return data.parentExe || null;
|
|
4422
4477
|
} catch {
|
|
4423
4478
|
return null;
|
|
@@ -4425,8 +4480,8 @@ function getParentExe(sessionKey) {
|
|
|
4425
4480
|
}
|
|
4426
4481
|
function getDispatchedBy(sessionKey) {
|
|
4427
4482
|
try {
|
|
4428
|
-
const data = JSON.parse(
|
|
4429
|
-
|
|
4483
|
+
const data = JSON.parse(readFileSync11(
|
|
4484
|
+
path16.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
4430
4485
|
"utf8"
|
|
4431
4486
|
));
|
|
4432
4487
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -4487,32 +4542,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
4487
4542
|
}
|
|
4488
4543
|
function readDebounceState() {
|
|
4489
4544
|
try {
|
|
4490
|
-
if (!
|
|
4491
|
-
|
|
4545
|
+
if (!existsSync12(DEBOUNCE_FILE)) return {};
|
|
4546
|
+
const raw = JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
|
|
4547
|
+
const state = {};
|
|
4548
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
4549
|
+
if (typeof val === "number") {
|
|
4550
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
4551
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
4552
|
+
state[key] = val;
|
|
4553
|
+
}
|
|
4554
|
+
}
|
|
4555
|
+
return state;
|
|
4492
4556
|
} catch {
|
|
4493
4557
|
return {};
|
|
4494
4558
|
}
|
|
4495
4559
|
}
|
|
4496
4560
|
function writeDebounceState(state) {
|
|
4497
4561
|
try {
|
|
4498
|
-
if (!
|
|
4499
|
-
|
|
4562
|
+
if (!existsSync12(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
4563
|
+
writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
|
|
4500
4564
|
} catch {
|
|
4501
4565
|
}
|
|
4502
4566
|
}
|
|
4503
4567
|
function isDebounced(targetSession) {
|
|
4504
4568
|
const state = readDebounceState();
|
|
4505
|
-
const
|
|
4506
|
-
|
|
4569
|
+
const entry = state[targetSession];
|
|
4570
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
4571
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
4572
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
4573
|
+
state[targetSession].pending++;
|
|
4574
|
+
writeDebounceState(state);
|
|
4575
|
+
return true;
|
|
4576
|
+
}
|
|
4577
|
+
return false;
|
|
4507
4578
|
}
|
|
4508
4579
|
function recordDebounce(targetSession) {
|
|
4509
4580
|
const state = readDebounceState();
|
|
4510
|
-
state[targetSession]
|
|
4581
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
4582
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
4511
4583
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
4512
4584
|
for (const key of Object.keys(state)) {
|
|
4513
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
4585
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
4514
4586
|
}
|
|
4515
4587
|
writeDebounceState(state);
|
|
4588
|
+
return batched;
|
|
4516
4589
|
}
|
|
4517
4590
|
function logIntercom(msg) {
|
|
4518
4591
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -4557,7 +4630,7 @@ function sendIntercom(targetSession) {
|
|
|
4557
4630
|
return "skipped_exe";
|
|
4558
4631
|
}
|
|
4559
4632
|
if (isDebounced(targetSession)) {
|
|
4560
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
4633
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
4561
4634
|
return "debounced";
|
|
4562
4635
|
}
|
|
4563
4636
|
try {
|
|
@@ -4569,14 +4642,14 @@ function sendIntercom(targetSession) {
|
|
|
4569
4642
|
const sessionState = getSessionState(targetSession);
|
|
4570
4643
|
if (sessionState === "no_claude") {
|
|
4571
4644
|
queueIntercom(targetSession, "claude not running in session");
|
|
4572
|
-
recordDebounce(targetSession);
|
|
4573
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
4645
|
+
const batched2 = recordDebounce(targetSession);
|
|
4646
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
4574
4647
|
return "queued";
|
|
4575
4648
|
}
|
|
4576
4649
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
4577
4650
|
queueIntercom(targetSession, "session busy at send time");
|
|
4578
|
-
recordDebounce(targetSession);
|
|
4579
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
4651
|
+
const batched2 = recordDebounce(targetSession);
|
|
4652
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
4580
4653
|
return "queued";
|
|
4581
4654
|
}
|
|
4582
4655
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -4584,8 +4657,8 @@ function sendIntercom(targetSession) {
|
|
|
4584
4657
|
transport.sendKeys(targetSession, "q");
|
|
4585
4658
|
}
|
|
4586
4659
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
4587
|
-
recordDebounce(targetSession);
|
|
4588
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
4660
|
+
const batched = recordDebounce(targetSession);
|
|
4661
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
4589
4662
|
return "delivered";
|
|
4590
4663
|
} catch {
|
|
4591
4664
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -4687,26 +4760,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4687
4760
|
const transport = getTransport();
|
|
4688
4761
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
4689
4762
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
4690
|
-
const logDir =
|
|
4691
|
-
const logFile =
|
|
4692
|
-
if (!
|
|
4693
|
-
|
|
4763
|
+
const logDir = path16.join(os8.homedir(), ".exe-os", "session-logs");
|
|
4764
|
+
const logFile = path16.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4765
|
+
if (!existsSync12(logDir)) {
|
|
4766
|
+
mkdirSync6(logDir, { recursive: true });
|
|
4694
4767
|
}
|
|
4695
4768
|
transport.kill(sessionName);
|
|
4696
4769
|
let cleanupSuffix = "";
|
|
4697
4770
|
try {
|
|
4698
4771
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
4699
|
-
const cleanupScript =
|
|
4700
|
-
if (
|
|
4772
|
+
const cleanupScript = path16.join(path16.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4773
|
+
if (existsSync12(cleanupScript)) {
|
|
4701
4774
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
4702
4775
|
}
|
|
4703
4776
|
} catch {
|
|
4704
4777
|
}
|
|
4705
4778
|
try {
|
|
4706
|
-
const claudeJsonPath =
|
|
4779
|
+
const claudeJsonPath = path16.join(os8.homedir(), ".claude.json");
|
|
4707
4780
|
let claudeJson = {};
|
|
4708
4781
|
try {
|
|
4709
|
-
claudeJson = JSON.parse(
|
|
4782
|
+
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
4710
4783
|
} catch {
|
|
4711
4784
|
}
|
|
4712
4785
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -4714,17 +4787,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4714
4787
|
const trustDir = opts?.cwd ?? projectDir;
|
|
4715
4788
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
4716
4789
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
4717
|
-
|
|
4790
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
4718
4791
|
} catch {
|
|
4719
4792
|
}
|
|
4720
4793
|
try {
|
|
4721
|
-
const settingsDir =
|
|
4794
|
+
const settingsDir = path16.join(os8.homedir(), ".claude", "projects");
|
|
4722
4795
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
4723
|
-
const projSettingsDir =
|
|
4724
|
-
const settingsPath =
|
|
4796
|
+
const projSettingsDir = path16.join(settingsDir, normalizedKey);
|
|
4797
|
+
const settingsPath = path16.join(projSettingsDir, "settings.json");
|
|
4725
4798
|
let settings = {};
|
|
4726
4799
|
try {
|
|
4727
|
-
settings = JSON.parse(
|
|
4800
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
4728
4801
|
} catch {
|
|
4729
4802
|
}
|
|
4730
4803
|
const perms = settings.permissions ?? {};
|
|
@@ -4752,20 +4825,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4752
4825
|
if (changed) {
|
|
4753
4826
|
perms.allow = allow;
|
|
4754
4827
|
settings.permissions = perms;
|
|
4755
|
-
|
|
4756
|
-
|
|
4828
|
+
mkdirSync6(projSettingsDir, { recursive: true });
|
|
4829
|
+
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
4757
4830
|
}
|
|
4758
4831
|
} catch {
|
|
4759
4832
|
}
|
|
4760
4833
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
4761
4834
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
4762
|
-
const
|
|
4835
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
4836
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
4837
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
4838
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
4763
4839
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
4764
4840
|
let identityFlag = "";
|
|
4765
4841
|
let behaviorsFlag = "";
|
|
4766
4842
|
let legacyFallbackWarned = false;
|
|
4767
4843
|
if (!useExeAgent && !useBinSymlink) {
|
|
4768
|
-
const identityPath =
|
|
4844
|
+
const identityPath = path16.join(
|
|
4769
4845
|
os8.homedir(),
|
|
4770
4846
|
".exe-os",
|
|
4771
4847
|
"identity",
|
|
@@ -4775,13 +4851,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4775
4851
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
4776
4852
|
if (hasAgentFlag) {
|
|
4777
4853
|
identityFlag = ` --agent ${employeeName}`;
|
|
4778
|
-
} else if (
|
|
4854
|
+
} else if (existsSync12(identityPath)) {
|
|
4779
4855
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
4780
4856
|
legacyFallbackWarned = true;
|
|
4781
4857
|
}
|
|
4782
4858
|
const behaviorsFile = exportBehaviorsSync(
|
|
4783
4859
|
employeeName,
|
|
4784
|
-
|
|
4860
|
+
path16.basename(spawnCwd),
|
|
4785
4861
|
sessionName
|
|
4786
4862
|
);
|
|
4787
4863
|
if (behaviorsFile) {
|
|
@@ -4796,16 +4872,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4796
4872
|
}
|
|
4797
4873
|
let sessionContextFlag = "";
|
|
4798
4874
|
try {
|
|
4799
|
-
const ctxDir =
|
|
4800
|
-
|
|
4801
|
-
const ctxFile =
|
|
4875
|
+
const ctxDir = path16.join(os8.homedir(), ".exe-os", "session-cache");
|
|
4876
|
+
mkdirSync6(ctxDir, { recursive: true });
|
|
4877
|
+
const ctxFile = path16.join(ctxDir, `session-context-${sessionName}.md`);
|
|
4802
4878
|
const ctxContent = [
|
|
4803
4879
|
`## Session Context`,
|
|
4804
4880
|
`You are running in tmux session: ${sessionName}.`,
|
|
4805
4881
|
`Your parent coordinator session is ${exeSession}.`,
|
|
4806
4882
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
4807
4883
|
].join("\n");
|
|
4808
|
-
|
|
4884
|
+
writeFileSync7(ctxFile, ctxContent);
|
|
4809
4885
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
4810
4886
|
} catch {
|
|
4811
4887
|
}
|
|
@@ -4819,9 +4895,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4819
4895
|
}
|
|
4820
4896
|
}
|
|
4821
4897
|
}
|
|
4898
|
+
if (useCodex) {
|
|
4899
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
4900
|
+
if (codexCfg?.apiKeyEnv) {
|
|
4901
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
4902
|
+
if (keyVal) {
|
|
4903
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4906
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
4907
|
+
}
|
|
4908
|
+
if (useOpencode) {
|
|
4909
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
4910
|
+
if (ocCfg?.apiKeyEnv) {
|
|
4911
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
4912
|
+
if (keyVal) {
|
|
4913
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4917
|
+
}
|
|
4918
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
4919
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
4920
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
4921
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
4922
|
+
}
|
|
4923
|
+
}
|
|
4822
4924
|
let spawnCommand;
|
|
4823
4925
|
if (useExeAgent) {
|
|
4824
4926
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
4927
|
+
} else if (useCodex) {
|
|
4928
|
+
process.stderr.write(
|
|
4929
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
4930
|
+
`
|
|
4931
|
+
);
|
|
4932
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
4933
|
+
} else if (useOpencode) {
|
|
4934
|
+
const binName = `${employeeName}-opencode`;
|
|
4935
|
+
process.stderr.write(
|
|
4936
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
4937
|
+
`
|
|
4938
|
+
);
|
|
4939
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
4825
4940
|
} else if (useBinSymlink) {
|
|
4826
4941
|
const binName = `${employeeName}-${ccProvider}`;
|
|
4827
4942
|
process.stderr.write(
|
|
@@ -4843,11 +4958,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4843
4958
|
transport.pipeLog(sessionName, logFile);
|
|
4844
4959
|
try {
|
|
4845
4960
|
const mySession = getMySession();
|
|
4846
|
-
const dispatchInfo =
|
|
4847
|
-
|
|
4961
|
+
const dispatchInfo = path16.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
4962
|
+
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
4848
4963
|
dispatchedBy: mySession,
|
|
4849
4964
|
rootExe: exeSession,
|
|
4850
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
4965
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
4966
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
4967
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
4851
4968
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4852
4969
|
}));
|
|
4853
4970
|
} catch {
|
|
@@ -4865,6 +4982,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4865
4982
|
booted = true;
|
|
4866
4983
|
break;
|
|
4867
4984
|
}
|
|
4985
|
+
} else if (useCodex) {
|
|
4986
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
4987
|
+
booted = true;
|
|
4988
|
+
break;
|
|
4989
|
+
}
|
|
4868
4990
|
} else {
|
|
4869
4991
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
4870
4992
|
booted = true;
|
|
@@ -4876,9 +4998,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
4876
4998
|
}
|
|
4877
4999
|
if (!booted) {
|
|
4878
5000
|
releaseSpawnLock2(sessionName);
|
|
4879
|
-
|
|
5001
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
5002
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
4880
5003
|
}
|
|
4881
|
-
if (!useExeAgent) {
|
|
5004
|
+
if (!useExeAgent && !useCodex) {
|
|
4882
5005
|
try {
|
|
4883
5006
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
4884
5007
|
} catch {
|
|
@@ -4905,17 +5028,19 @@ var init_tmux_routing = __esm({
|
|
|
4905
5028
|
init_cc_agent_support();
|
|
4906
5029
|
init_mcp_prefix();
|
|
4907
5030
|
init_provider_table();
|
|
5031
|
+
init_agent_config();
|
|
5032
|
+
init_runtime_table();
|
|
4908
5033
|
init_intercom_queue();
|
|
4909
5034
|
init_plan_limits();
|
|
4910
5035
|
init_employees();
|
|
4911
|
-
SPAWN_LOCK_DIR =
|
|
4912
|
-
SESSION_CACHE =
|
|
5036
|
+
SPAWN_LOCK_DIR = path16.join(os8.homedir(), ".exe-os", "spawn-locks");
|
|
5037
|
+
SESSION_CACHE = path16.join(os8.homedir(), ".exe-os", "session-cache");
|
|
4913
5038
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
4914
5039
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
4915
5040
|
VERIFY_PANE_LINES = 200;
|
|
4916
5041
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
4917
|
-
INTERCOM_LOG2 =
|
|
4918
|
-
DEBOUNCE_FILE =
|
|
5042
|
+
INTERCOM_LOG2 = path16.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
5043
|
+
DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
|
|
4919
5044
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
4920
5045
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
4921
5046
|
}
|
|
@@ -4932,14 +5057,14 @@ var init_memory = __esm({
|
|
|
4932
5057
|
|
|
4933
5058
|
// src/lib/keychain.ts
|
|
4934
5059
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
4935
|
-
import { existsSync as
|
|
4936
|
-
import
|
|
5060
|
+
import { existsSync as existsSync13 } from "fs";
|
|
5061
|
+
import path17 from "path";
|
|
4937
5062
|
import os9 from "os";
|
|
4938
5063
|
function getKeyDir() {
|
|
4939
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
5064
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path17.join(os9.homedir(), ".exe-os");
|
|
4940
5065
|
}
|
|
4941
5066
|
function getKeyPath() {
|
|
4942
|
-
return
|
|
5067
|
+
return path17.join(getKeyDir(), "master.key");
|
|
4943
5068
|
}
|
|
4944
5069
|
async function tryKeytar() {
|
|
4945
5070
|
try {
|
|
@@ -4960,7 +5085,7 @@ async function getMasterKey() {
|
|
|
4960
5085
|
}
|
|
4961
5086
|
}
|
|
4962
5087
|
const keyPath = getKeyPath();
|
|
4963
|
-
if (!
|
|
5088
|
+
if (!existsSync13(keyPath)) {
|
|
4964
5089
|
process.stderr.write(
|
|
4965
5090
|
`[keychain] Key not found at ${keyPath} (HOME=${os9.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
4966
5091
|
`
|
|
@@ -5000,13 +5125,13 @@ __export(shard_manager_exports, {
|
|
|
5000
5125
|
listShards: () => listShards,
|
|
5001
5126
|
shardExists: () => shardExists
|
|
5002
5127
|
});
|
|
5003
|
-
import
|
|
5004
|
-
import { existsSync as
|
|
5128
|
+
import path18 from "path";
|
|
5129
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync7, readdirSync as readdirSync3 } from "fs";
|
|
5005
5130
|
import { createClient as createClient2 } from "@libsql/client";
|
|
5006
5131
|
function initShardManager(encryptionKey) {
|
|
5007
5132
|
_encryptionKey = encryptionKey;
|
|
5008
|
-
if (!
|
|
5009
|
-
|
|
5133
|
+
if (!existsSync14(SHARDS_DIR)) {
|
|
5134
|
+
mkdirSync7(SHARDS_DIR, { recursive: true });
|
|
5010
5135
|
}
|
|
5011
5136
|
_shardingEnabled = true;
|
|
5012
5137
|
}
|
|
@@ -5026,7 +5151,7 @@ function getShardClient(projectName) {
|
|
|
5026
5151
|
}
|
|
5027
5152
|
const cached = _shards.get(safeName);
|
|
5028
5153
|
if (cached) return cached;
|
|
5029
|
-
const dbPath =
|
|
5154
|
+
const dbPath = path18.join(SHARDS_DIR, `${safeName}.db`);
|
|
5030
5155
|
const client = createClient2({
|
|
5031
5156
|
url: `file:${dbPath}`,
|
|
5032
5157
|
encryptionKey: _encryptionKey
|
|
@@ -5036,10 +5161,10 @@ function getShardClient(projectName) {
|
|
|
5036
5161
|
}
|
|
5037
5162
|
function shardExists(projectName) {
|
|
5038
5163
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
5039
|
-
return
|
|
5164
|
+
return existsSync14(path18.join(SHARDS_DIR, `${safeName}.db`));
|
|
5040
5165
|
}
|
|
5041
5166
|
function listShards() {
|
|
5042
|
-
if (!
|
|
5167
|
+
if (!existsSync14(SHARDS_DIR)) return [];
|
|
5043
5168
|
return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
5044
5169
|
}
|
|
5045
5170
|
async function ensureShardSchema(client) {
|
|
@@ -5225,7 +5350,7 @@ var init_shard_manager = __esm({
|
|
|
5225
5350
|
"src/lib/shard-manager.ts"() {
|
|
5226
5351
|
"use strict";
|
|
5227
5352
|
init_config();
|
|
5228
|
-
SHARDS_DIR =
|
|
5353
|
+
SHARDS_DIR = path18.join(EXE_AI_DIR, "shards");
|
|
5229
5354
|
_shards = /* @__PURE__ */ new Map();
|
|
5230
5355
|
_encryptionKey = null;
|
|
5231
5356
|
_shardingEnabled = false;
|
|
@@ -6014,8 +6139,8 @@ function findContainingChunk(filePath, snippet) {
|
|
|
6014
6139
|
try {
|
|
6015
6140
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
6016
6141
|
if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
|
|
6017
|
-
const { readFileSync:
|
|
6018
|
-
const source =
|
|
6142
|
+
const { readFileSync: readFileSync12 } = __require("fs");
|
|
6143
|
+
const source = readFileSync12(filePath, "utf8");
|
|
6019
6144
|
const lines = source.split("\n");
|
|
6020
6145
|
const lowerSnippet = snippet.toLowerCase().slice(0, 80);
|
|
6021
6146
|
let matchLine = -1;
|
|
@@ -6081,9 +6206,9 @@ function extractBash(input, response) {
|
|
|
6081
6206
|
}
|
|
6082
6207
|
function extractGrep(input, response) {
|
|
6083
6208
|
const pattern = String(input.pattern ?? "");
|
|
6084
|
-
const
|
|
6209
|
+
const path19 = input.path ? String(input.path) : "";
|
|
6085
6210
|
const output = String(response.text ?? response.content ?? JSON.stringify(response).slice(0, MAX_OUTPUT));
|
|
6086
|
-
return `Searched for "${pattern}"${
|
|
6211
|
+
return `Searched for "${pattern}"${path19 ? ` in ${path19}` : ""}
|
|
6087
6212
|
${output.slice(0, MAX_OUTPUT)}`;
|
|
6088
6213
|
}
|
|
6089
6214
|
function extractGlob(input, response) {
|