@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
|
@@ -3287,18 +3287,69 @@ var init_provider_table = __esm({
|
|
|
3287
3287
|
}
|
|
3288
3288
|
});
|
|
3289
3289
|
|
|
3290
|
-
// src/lib/
|
|
3291
|
-
|
|
3290
|
+
// src/lib/runtime-table.ts
|
|
3291
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
3292
|
+
var init_runtime_table = __esm({
|
|
3293
|
+
"src/lib/runtime-table.ts"() {
|
|
3294
|
+
"use strict";
|
|
3295
|
+
RUNTIME_TABLE = {
|
|
3296
|
+
codex: {
|
|
3297
|
+
binary: "codex",
|
|
3298
|
+
launchMode: "exec",
|
|
3299
|
+
autoApproveFlag: "--full-auto",
|
|
3300
|
+
inlineFlag: "--no-alt-screen",
|
|
3301
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
3302
|
+
defaultModel: "gpt-5.4"
|
|
3303
|
+
}
|
|
3304
|
+
};
|
|
3305
|
+
DEFAULT_RUNTIME = "claude";
|
|
3306
|
+
}
|
|
3307
|
+
});
|
|
3308
|
+
|
|
3309
|
+
// src/lib/agent-config.ts
|
|
3310
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
|
|
3292
3311
|
import path7 from "path";
|
|
3312
|
+
function loadAgentConfig() {
|
|
3313
|
+
if (!existsSync7(AGENT_CONFIG_PATH)) return {};
|
|
3314
|
+
try {
|
|
3315
|
+
return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
|
|
3316
|
+
} catch {
|
|
3317
|
+
return {};
|
|
3318
|
+
}
|
|
3319
|
+
}
|
|
3320
|
+
function getAgentRuntime(agentId) {
|
|
3321
|
+
const config = loadAgentConfig();
|
|
3322
|
+
const entry = config[agentId];
|
|
3323
|
+
if (entry) return entry;
|
|
3324
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
3325
|
+
}
|
|
3326
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
3327
|
+
var init_agent_config = __esm({
|
|
3328
|
+
"src/lib/agent-config.ts"() {
|
|
3329
|
+
"use strict";
|
|
3330
|
+
init_config();
|
|
3331
|
+
init_runtime_table();
|
|
3332
|
+
AGENT_CONFIG_PATH = path7.join(EXE_AI_DIR, "agent-config.json");
|
|
3333
|
+
DEFAULT_MODELS = {
|
|
3334
|
+
claude: "claude-opus-4",
|
|
3335
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
3336
|
+
opencode: "minimax-m2.7"
|
|
3337
|
+
};
|
|
3338
|
+
}
|
|
3339
|
+
});
|
|
3340
|
+
|
|
3341
|
+
// src/lib/intercom-queue.ts
|
|
3342
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
|
|
3343
|
+
import path8 from "path";
|
|
3293
3344
|
import os5 from "os";
|
|
3294
3345
|
function ensureDir() {
|
|
3295
|
-
const dir =
|
|
3296
|
-
if (!
|
|
3346
|
+
const dir = path8.dirname(QUEUE_PATH);
|
|
3347
|
+
if (!existsSync8(dir)) mkdirSync4(dir, { recursive: true });
|
|
3297
3348
|
}
|
|
3298
3349
|
function readQueue() {
|
|
3299
3350
|
try {
|
|
3300
|
-
if (!
|
|
3301
|
-
return JSON.parse(
|
|
3351
|
+
if (!existsSync8(QUEUE_PATH)) return [];
|
|
3352
|
+
return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
|
|
3302
3353
|
} catch {
|
|
3303
3354
|
return [];
|
|
3304
3355
|
}
|
|
@@ -3306,7 +3357,7 @@ function readQueue() {
|
|
|
3306
3357
|
function writeQueue(queue) {
|
|
3307
3358
|
ensureDir();
|
|
3308
3359
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
3309
|
-
|
|
3360
|
+
writeFileSync4(tmp, JSON.stringify(queue, null, 2));
|
|
3310
3361
|
renameSync3(tmp, QUEUE_PATH);
|
|
3311
3362
|
}
|
|
3312
3363
|
function queueIntercom(targetSession, reason) {
|
|
@@ -3330,25 +3381,25 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
3330
3381
|
var init_intercom_queue = __esm({
|
|
3331
3382
|
"src/lib/intercom-queue.ts"() {
|
|
3332
3383
|
"use strict";
|
|
3333
|
-
QUEUE_PATH =
|
|
3384
|
+
QUEUE_PATH = path8.join(os5.homedir(), ".exe-os", "intercom-queue.json");
|
|
3334
3385
|
TTL_MS = 60 * 60 * 1e3;
|
|
3335
|
-
INTERCOM_LOG =
|
|
3386
|
+
INTERCOM_LOG = path8.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
3336
3387
|
}
|
|
3337
3388
|
});
|
|
3338
3389
|
|
|
3339
3390
|
// src/lib/license.ts
|
|
3340
|
-
import { readFileSync as
|
|
3391
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
|
|
3341
3392
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3342
|
-
import
|
|
3393
|
+
import path9 from "path";
|
|
3343
3394
|
import { jwtVerify, importSPKI } from "jose";
|
|
3344
3395
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
3345
3396
|
var init_license = __esm({
|
|
3346
3397
|
"src/lib/license.ts"() {
|
|
3347
3398
|
"use strict";
|
|
3348
3399
|
init_config();
|
|
3349
|
-
LICENSE_PATH =
|
|
3350
|
-
CACHE_PATH =
|
|
3351
|
-
DEVICE_ID_PATH =
|
|
3400
|
+
LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
|
|
3401
|
+
CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
3402
|
+
DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
|
|
3352
3403
|
PLAN_LIMITS = {
|
|
3353
3404
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
3354
3405
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -3360,12 +3411,12 @@ var init_license = __esm({
|
|
|
3360
3411
|
});
|
|
3361
3412
|
|
|
3362
3413
|
// src/lib/plan-limits.ts
|
|
3363
|
-
import { readFileSync as
|
|
3364
|
-
import
|
|
3414
|
+
import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
|
|
3415
|
+
import path10 from "path";
|
|
3365
3416
|
function getLicenseSync() {
|
|
3366
3417
|
try {
|
|
3367
|
-
if (!
|
|
3368
|
-
const raw = JSON.parse(
|
|
3418
|
+
if (!existsSync10(CACHE_PATH2)) return freeLicense();
|
|
3419
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
|
|
3369
3420
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
3370
3421
|
const parts = raw.token.split(".");
|
|
3371
3422
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -3403,8 +3454,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
3403
3454
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
3404
3455
|
let count = 0;
|
|
3405
3456
|
try {
|
|
3406
|
-
if (
|
|
3407
|
-
const raw =
|
|
3457
|
+
if (existsSync10(filePath)) {
|
|
3458
|
+
const raw = readFileSync8(filePath, "utf8");
|
|
3408
3459
|
const employees = JSON.parse(raw);
|
|
3409
3460
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
3410
3461
|
}
|
|
@@ -3433,19 +3484,19 @@ var init_plan_limits = __esm({
|
|
|
3433
3484
|
this.name = "PlanLimitError";
|
|
3434
3485
|
}
|
|
3435
3486
|
};
|
|
3436
|
-
CACHE_PATH2 =
|
|
3487
|
+
CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
3437
3488
|
}
|
|
3438
3489
|
});
|
|
3439
3490
|
|
|
3440
3491
|
// src/lib/notifications.ts
|
|
3441
3492
|
import crypto from "crypto";
|
|
3442
|
-
import
|
|
3493
|
+
import path11 from "path";
|
|
3443
3494
|
import os6 from "os";
|
|
3444
3495
|
import {
|
|
3445
|
-
readFileSync as
|
|
3496
|
+
readFileSync as readFileSync9,
|
|
3446
3497
|
readdirSync as readdirSync2,
|
|
3447
3498
|
unlinkSync as unlinkSync3,
|
|
3448
|
-
existsSync as
|
|
3499
|
+
existsSync as existsSync11,
|
|
3449
3500
|
rmdirSync
|
|
3450
3501
|
} from "fs";
|
|
3451
3502
|
async function writeNotification(notification) {
|
|
@@ -3541,11 +3592,11 @@ __export(tasks_crud_exports, {
|
|
|
3541
3592
|
writeCheckpoint: () => writeCheckpoint
|
|
3542
3593
|
});
|
|
3543
3594
|
import crypto3 from "crypto";
|
|
3544
|
-
import
|
|
3595
|
+
import path12 from "path";
|
|
3545
3596
|
import os7 from "os";
|
|
3546
3597
|
import { execSync as execSync4 } from "child_process";
|
|
3547
3598
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
3548
|
-
import { existsSync as
|
|
3599
|
+
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
|
|
3549
3600
|
async function writeCheckpoint(input) {
|
|
3550
3601
|
const client = getClient();
|
|
3551
3602
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -3720,8 +3771,8 @@ ${laneWarning}` : laneWarning;
|
|
|
3720
3771
|
}
|
|
3721
3772
|
if (input.baseDir) {
|
|
3722
3773
|
try {
|
|
3723
|
-
await mkdir4(
|
|
3724
|
-
await mkdir4(
|
|
3774
|
+
await mkdir4(path12.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
3775
|
+
await mkdir4(path12.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
3725
3776
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
3726
3777
|
await ensureGitignoreExe(input.baseDir);
|
|
3727
3778
|
} catch {
|
|
@@ -3757,10 +3808,10 @@ ${laneWarning}` : laneWarning;
|
|
|
3757
3808
|
});
|
|
3758
3809
|
if (input.baseDir) {
|
|
3759
3810
|
try {
|
|
3760
|
-
const EXE_OS_DIR =
|
|
3761
|
-
const mdPath =
|
|
3762
|
-
const mdDir =
|
|
3763
|
-
if (!
|
|
3811
|
+
const EXE_OS_DIR = path12.join(os7.homedir(), ".exe-os");
|
|
3812
|
+
const mdPath = path12.join(EXE_OS_DIR, taskFile);
|
|
3813
|
+
const mdDir = path12.dirname(mdPath);
|
|
3814
|
+
if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
3764
3815
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
3765
3816
|
const mdContent = `# ${input.title}
|
|
3766
3817
|
|
|
@@ -3785,7 +3836,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
|
|
|
3785
3836
|
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
3786
3837
|
`;
|
|
3787
3838
|
await writeFile4(mdPath, mdContent, "utf-8");
|
|
3788
|
-
} catch {
|
|
3839
|
+
} catch (err) {
|
|
3840
|
+
process.stderr.write(
|
|
3841
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
3842
|
+
`
|
|
3843
|
+
);
|
|
3789
3844
|
}
|
|
3790
3845
|
}
|
|
3791
3846
|
return {
|
|
@@ -4045,9 +4100,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
4045
4100
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
4046
4101
|
}
|
|
4047
4102
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
4048
|
-
const archPath =
|
|
4103
|
+
const archPath = path12.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
4049
4104
|
try {
|
|
4050
|
-
if (
|
|
4105
|
+
if (existsSync12(archPath)) return;
|
|
4051
4106
|
const template = [
|
|
4052
4107
|
`# ${projectName} \u2014 System Architecture`,
|
|
4053
4108
|
"",
|
|
@@ -4080,10 +4135,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
4080
4135
|
}
|
|
4081
4136
|
}
|
|
4082
4137
|
async function ensureGitignoreExe(baseDir) {
|
|
4083
|
-
const gitignorePath =
|
|
4138
|
+
const gitignorePath = path12.join(baseDir, ".gitignore");
|
|
4084
4139
|
try {
|
|
4085
|
-
if (
|
|
4086
|
-
const content =
|
|
4140
|
+
if (existsSync12(gitignorePath)) {
|
|
4141
|
+
const content = readFileSync10(gitignorePath, "utf-8");
|
|
4087
4142
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
4088
4143
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
4089
4144
|
} else {
|
|
@@ -4124,8 +4179,8 @@ __export(tasks_review_exports, {
|
|
|
4124
4179
|
getReviewChecklist: () => getReviewChecklist,
|
|
4125
4180
|
listPendingReviews: () => listPendingReviews
|
|
4126
4181
|
});
|
|
4127
|
-
import
|
|
4128
|
-
import { existsSync as
|
|
4182
|
+
import path13 from "path";
|
|
4183
|
+
import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
4129
4184
|
async function countPendingReviews(sessionScope) {
|
|
4130
4185
|
const client = getClient();
|
|
4131
4186
|
if (sessionScope) {
|
|
@@ -4395,11 +4450,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4395
4450
|
);
|
|
4396
4451
|
}
|
|
4397
4452
|
try {
|
|
4398
|
-
const cacheDir =
|
|
4399
|
-
if (
|
|
4453
|
+
const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
|
|
4454
|
+
if (existsSync13(cacheDir)) {
|
|
4400
4455
|
for (const f of readdirSync3(cacheDir)) {
|
|
4401
4456
|
if (f.startsWith("review-notified-")) {
|
|
4402
|
-
unlinkSync4(
|
|
4457
|
+
unlinkSync4(path13.join(cacheDir, f));
|
|
4403
4458
|
}
|
|
4404
4459
|
}
|
|
4405
4460
|
}
|
|
@@ -4420,7 +4475,7 @@ var init_tasks_review = __esm({
|
|
|
4420
4475
|
});
|
|
4421
4476
|
|
|
4422
4477
|
// src/lib/tasks-chain.ts
|
|
4423
|
-
import
|
|
4478
|
+
import path14 from "path";
|
|
4424
4479
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
4425
4480
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
4426
4481
|
const client = getClient();
|
|
@@ -4437,7 +4492,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
4437
4492
|
});
|
|
4438
4493
|
for (const ur of unblockedRows.rows) {
|
|
4439
4494
|
try {
|
|
4440
|
-
const ubFile =
|
|
4495
|
+
const ubFile = path14.join(baseDir, String(ur.task_file));
|
|
4441
4496
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
4442
4497
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
4443
4498
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -4506,7 +4561,7 @@ var init_tasks_chain = __esm({
|
|
|
4506
4561
|
|
|
4507
4562
|
// src/lib/project-name.ts
|
|
4508
4563
|
import { execSync as execSync5 } from "child_process";
|
|
4509
|
-
import
|
|
4564
|
+
import path15 from "path";
|
|
4510
4565
|
function getProjectName(cwd) {
|
|
4511
4566
|
const dir = cwd ?? process.cwd();
|
|
4512
4567
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -4519,7 +4574,7 @@ function getProjectName(cwd) {
|
|
|
4519
4574
|
timeout: 2e3,
|
|
4520
4575
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4521
4576
|
}).trim();
|
|
4522
|
-
repoRoot =
|
|
4577
|
+
repoRoot = path15.dirname(gitCommonDir);
|
|
4523
4578
|
} catch {
|
|
4524
4579
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
4525
4580
|
cwd: dir,
|
|
@@ -4528,11 +4583,11 @@ function getProjectName(cwd) {
|
|
|
4528
4583
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4529
4584
|
}).trim();
|
|
4530
4585
|
}
|
|
4531
|
-
_cached2 =
|
|
4586
|
+
_cached2 = path15.basename(repoRoot);
|
|
4532
4587
|
_cachedCwd = dir;
|
|
4533
4588
|
return _cached2;
|
|
4534
4589
|
} catch {
|
|
4535
|
-
_cached2 =
|
|
4590
|
+
_cached2 = path15.basename(dir);
|
|
4536
4591
|
_cachedCwd = dir;
|
|
4537
4592
|
return _cached2;
|
|
4538
4593
|
}
|
|
@@ -5005,8 +5060,8 @@ __export(tasks_exports, {
|
|
|
5005
5060
|
updateTaskStatus: () => updateTaskStatus,
|
|
5006
5061
|
writeCheckpoint: () => writeCheckpoint
|
|
5007
5062
|
});
|
|
5008
|
-
import
|
|
5009
|
-
import { writeFileSync as
|
|
5063
|
+
import path16 from "path";
|
|
5064
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
|
|
5010
5065
|
async function createTask(input) {
|
|
5011
5066
|
const result = await createTaskCore(input);
|
|
5012
5067
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -5025,11 +5080,11 @@ async function updateTask(input) {
|
|
|
5025
5080
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
5026
5081
|
try {
|
|
5027
5082
|
const agent = String(row.assigned_to);
|
|
5028
|
-
const cacheDir =
|
|
5029
|
-
const cachePath =
|
|
5083
|
+
const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
|
|
5084
|
+
const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
|
|
5030
5085
|
if (input.status === "in_progress") {
|
|
5031
|
-
|
|
5032
|
-
|
|
5086
|
+
mkdirSync6(cacheDir, { recursive: true });
|
|
5087
|
+
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
5033
5088
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
5034
5089
|
try {
|
|
5035
5090
|
unlinkSync5(cachePath);
|
|
@@ -5496,13 +5551,13 @@ __export(tmux_routing_exports, {
|
|
|
5496
5551
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
5497
5552
|
});
|
|
5498
5553
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
5499
|
-
import { readFileSync as
|
|
5500
|
-
import
|
|
5554
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync } from "fs";
|
|
5555
|
+
import path17 from "path";
|
|
5501
5556
|
import os8 from "os";
|
|
5502
5557
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5503
5558
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
5504
5559
|
function spawnLockPath(sessionName) {
|
|
5505
|
-
return
|
|
5560
|
+
return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
5506
5561
|
}
|
|
5507
5562
|
function isProcessAlive(pid) {
|
|
5508
5563
|
try {
|
|
@@ -5513,13 +5568,13 @@ function isProcessAlive(pid) {
|
|
|
5513
5568
|
}
|
|
5514
5569
|
}
|
|
5515
5570
|
function acquireSpawnLock2(sessionName) {
|
|
5516
|
-
if (!
|
|
5517
|
-
|
|
5571
|
+
if (!existsSync14(SPAWN_LOCK_DIR)) {
|
|
5572
|
+
mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
|
|
5518
5573
|
}
|
|
5519
5574
|
const lockFile = spawnLockPath(sessionName);
|
|
5520
|
-
if (
|
|
5575
|
+
if (existsSync14(lockFile)) {
|
|
5521
5576
|
try {
|
|
5522
|
-
const lock = JSON.parse(
|
|
5577
|
+
const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
|
|
5523
5578
|
const age = Date.now() - lock.timestamp;
|
|
5524
5579
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
5525
5580
|
return false;
|
|
@@ -5527,7 +5582,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
5527
5582
|
} catch {
|
|
5528
5583
|
}
|
|
5529
5584
|
}
|
|
5530
|
-
|
|
5585
|
+
writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
5531
5586
|
return true;
|
|
5532
5587
|
}
|
|
5533
5588
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -5539,13 +5594,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
5539
5594
|
function resolveBehaviorsExporterScript() {
|
|
5540
5595
|
try {
|
|
5541
5596
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5542
|
-
const scriptPath =
|
|
5543
|
-
|
|
5597
|
+
const scriptPath = path17.join(
|
|
5598
|
+
path17.dirname(thisFile),
|
|
5544
5599
|
"..",
|
|
5545
5600
|
"bin",
|
|
5546
5601
|
"exe-export-behaviors.js"
|
|
5547
5602
|
);
|
|
5548
|
-
return
|
|
5603
|
+
return existsSync14(scriptPath) ? scriptPath : null;
|
|
5549
5604
|
} catch {
|
|
5550
5605
|
return null;
|
|
5551
5606
|
}
|
|
@@ -5611,12 +5666,12 @@ function extractRootExe(name) {
|
|
|
5611
5666
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
5612
5667
|
}
|
|
5613
5668
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
5614
|
-
if (!
|
|
5615
|
-
|
|
5669
|
+
if (!existsSync14(SESSION_CACHE)) {
|
|
5670
|
+
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
5616
5671
|
}
|
|
5617
5672
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
5618
|
-
const filePath =
|
|
5619
|
-
|
|
5673
|
+
const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
5674
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
5620
5675
|
parentExe: rootExe,
|
|
5621
5676
|
dispatchedBy: dispatchedBy || rootExe,
|
|
5622
5677
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5624,7 +5679,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5624
5679
|
}
|
|
5625
5680
|
function getParentExe(sessionKey) {
|
|
5626
5681
|
try {
|
|
5627
|
-
const data = JSON.parse(
|
|
5682
|
+
const data = JSON.parse(readFileSync11(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
5628
5683
|
return data.parentExe || null;
|
|
5629
5684
|
} catch {
|
|
5630
5685
|
return null;
|
|
@@ -5632,8 +5687,8 @@ function getParentExe(sessionKey) {
|
|
|
5632
5687
|
}
|
|
5633
5688
|
function getDispatchedBy(sessionKey) {
|
|
5634
5689
|
try {
|
|
5635
|
-
const data = JSON.parse(
|
|
5636
|
-
|
|
5690
|
+
const data = JSON.parse(readFileSync11(
|
|
5691
|
+
path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
5637
5692
|
"utf8"
|
|
5638
5693
|
));
|
|
5639
5694
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -5694,32 +5749,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
5694
5749
|
}
|
|
5695
5750
|
function readDebounceState() {
|
|
5696
5751
|
try {
|
|
5697
|
-
if (!
|
|
5698
|
-
|
|
5752
|
+
if (!existsSync14(DEBOUNCE_FILE)) return {};
|
|
5753
|
+
const raw = JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
|
|
5754
|
+
const state = {};
|
|
5755
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
5756
|
+
if (typeof val === "number") {
|
|
5757
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
5758
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
5759
|
+
state[key] = val;
|
|
5760
|
+
}
|
|
5761
|
+
}
|
|
5762
|
+
return state;
|
|
5699
5763
|
} catch {
|
|
5700
5764
|
return {};
|
|
5701
5765
|
}
|
|
5702
5766
|
}
|
|
5703
5767
|
function writeDebounceState(state) {
|
|
5704
5768
|
try {
|
|
5705
|
-
if (!
|
|
5706
|
-
|
|
5769
|
+
if (!existsSync14(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
5770
|
+
writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
|
|
5707
5771
|
} catch {
|
|
5708
5772
|
}
|
|
5709
5773
|
}
|
|
5710
5774
|
function isDebounced(targetSession) {
|
|
5711
5775
|
const state = readDebounceState();
|
|
5712
|
-
const
|
|
5713
|
-
|
|
5776
|
+
const entry = state[targetSession];
|
|
5777
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
5778
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
5779
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
5780
|
+
state[targetSession].pending++;
|
|
5781
|
+
writeDebounceState(state);
|
|
5782
|
+
return true;
|
|
5783
|
+
}
|
|
5784
|
+
return false;
|
|
5714
5785
|
}
|
|
5715
5786
|
function recordDebounce(targetSession) {
|
|
5716
5787
|
const state = readDebounceState();
|
|
5717
|
-
state[targetSession]
|
|
5788
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
5789
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
5718
5790
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
5719
5791
|
for (const key of Object.keys(state)) {
|
|
5720
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
5792
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
5721
5793
|
}
|
|
5722
5794
|
writeDebounceState(state);
|
|
5795
|
+
return batched;
|
|
5723
5796
|
}
|
|
5724
5797
|
function logIntercom(msg) {
|
|
5725
5798
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -5764,7 +5837,7 @@ function sendIntercom(targetSession) {
|
|
|
5764
5837
|
return "skipped_exe";
|
|
5765
5838
|
}
|
|
5766
5839
|
if (isDebounced(targetSession)) {
|
|
5767
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
5840
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
5768
5841
|
return "debounced";
|
|
5769
5842
|
}
|
|
5770
5843
|
try {
|
|
@@ -5776,14 +5849,14 @@ function sendIntercom(targetSession) {
|
|
|
5776
5849
|
const sessionState = getSessionState(targetSession);
|
|
5777
5850
|
if (sessionState === "no_claude") {
|
|
5778
5851
|
queueIntercom(targetSession, "claude not running in session");
|
|
5779
|
-
recordDebounce(targetSession);
|
|
5780
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
5852
|
+
const batched2 = recordDebounce(targetSession);
|
|
5853
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
5781
5854
|
return "queued";
|
|
5782
5855
|
}
|
|
5783
5856
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
5784
5857
|
queueIntercom(targetSession, "session busy at send time");
|
|
5785
|
-
recordDebounce(targetSession);
|
|
5786
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
5858
|
+
const batched2 = recordDebounce(targetSession);
|
|
5859
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
5787
5860
|
return "queued";
|
|
5788
5861
|
}
|
|
5789
5862
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -5791,8 +5864,8 @@ function sendIntercom(targetSession) {
|
|
|
5791
5864
|
transport.sendKeys(targetSession, "q");
|
|
5792
5865
|
}
|
|
5793
5866
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
5794
|
-
recordDebounce(targetSession);
|
|
5795
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
5867
|
+
const batched = recordDebounce(targetSession);
|
|
5868
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
5796
5869
|
return "delivered";
|
|
5797
5870
|
} catch {
|
|
5798
5871
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -5894,26 +5967,26 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
5894
5967
|
const transport = getTransport();
|
|
5895
5968
|
const sessionName = employeeSessionName(employeeName, exeSession2, opts?.instance);
|
|
5896
5969
|
const instanceLabel2 = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
5897
|
-
const logDir =
|
|
5898
|
-
const logFile =
|
|
5899
|
-
if (!
|
|
5900
|
-
|
|
5970
|
+
const logDir = path17.join(os8.homedir(), ".exe-os", "session-logs");
|
|
5971
|
+
const logFile = path17.join(logDir, `${instanceLabel2}-${Date.now()}.log`);
|
|
5972
|
+
if (!existsSync14(logDir)) {
|
|
5973
|
+
mkdirSync7(logDir, { recursive: true });
|
|
5901
5974
|
}
|
|
5902
5975
|
transport.kill(sessionName);
|
|
5903
5976
|
let cleanupSuffix = "";
|
|
5904
5977
|
try {
|
|
5905
5978
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5906
|
-
const cleanupScript =
|
|
5907
|
-
if (
|
|
5979
|
+
const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
5980
|
+
if (existsSync14(cleanupScript)) {
|
|
5908
5981
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession2}"`;
|
|
5909
5982
|
}
|
|
5910
5983
|
} catch {
|
|
5911
5984
|
}
|
|
5912
5985
|
try {
|
|
5913
|
-
const claudeJsonPath =
|
|
5986
|
+
const claudeJsonPath = path17.join(os8.homedir(), ".claude.json");
|
|
5914
5987
|
let claudeJson = {};
|
|
5915
5988
|
try {
|
|
5916
|
-
claudeJson = JSON.parse(
|
|
5989
|
+
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
5917
5990
|
} catch {
|
|
5918
5991
|
}
|
|
5919
5992
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -5921,17 +5994,17 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
5921
5994
|
const trustDir = opts?.cwd ?? projectDir;
|
|
5922
5995
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
5923
5996
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
5924
|
-
|
|
5997
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
5925
5998
|
} catch {
|
|
5926
5999
|
}
|
|
5927
6000
|
try {
|
|
5928
|
-
const settingsDir =
|
|
6001
|
+
const settingsDir = path17.join(os8.homedir(), ".claude", "projects");
|
|
5929
6002
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
5930
|
-
const projSettingsDir =
|
|
5931
|
-
const settingsPath =
|
|
6003
|
+
const projSettingsDir = path17.join(settingsDir, normalizedKey);
|
|
6004
|
+
const settingsPath = path17.join(projSettingsDir, "settings.json");
|
|
5932
6005
|
let settings = {};
|
|
5933
6006
|
try {
|
|
5934
|
-
settings = JSON.parse(
|
|
6007
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
5935
6008
|
} catch {
|
|
5936
6009
|
}
|
|
5937
6010
|
const perms = settings.permissions ?? {};
|
|
@@ -5959,20 +6032,23 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
5959
6032
|
if (changed) {
|
|
5960
6033
|
perms.allow = allow;
|
|
5961
6034
|
settings.permissions = perms;
|
|
5962
|
-
|
|
5963
|
-
|
|
6035
|
+
mkdirSync7(projSettingsDir, { recursive: true });
|
|
6036
|
+
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
5964
6037
|
}
|
|
5965
6038
|
} catch {
|
|
5966
6039
|
}
|
|
5967
6040
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
5968
6041
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
5969
|
-
const
|
|
6042
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
6043
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
6044
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
6045
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
5970
6046
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
5971
6047
|
let identityFlag = "";
|
|
5972
6048
|
let behaviorsFlag = "";
|
|
5973
6049
|
let legacyFallbackWarned = false;
|
|
5974
6050
|
if (!useExeAgent && !useBinSymlink) {
|
|
5975
|
-
const identityPath =
|
|
6051
|
+
const identityPath = path17.join(
|
|
5976
6052
|
os8.homedir(),
|
|
5977
6053
|
".exe-os",
|
|
5978
6054
|
"identity",
|
|
@@ -5982,13 +6058,13 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
5982
6058
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
5983
6059
|
if (hasAgentFlag) {
|
|
5984
6060
|
identityFlag = ` --agent ${employeeName}`;
|
|
5985
|
-
} else if (
|
|
6061
|
+
} else if (existsSync14(identityPath)) {
|
|
5986
6062
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
5987
6063
|
legacyFallbackWarned = true;
|
|
5988
6064
|
}
|
|
5989
6065
|
const behaviorsFile = exportBehaviorsSync(
|
|
5990
6066
|
employeeName,
|
|
5991
|
-
|
|
6067
|
+
path17.basename(spawnCwd),
|
|
5992
6068
|
sessionName
|
|
5993
6069
|
);
|
|
5994
6070
|
if (behaviorsFile) {
|
|
@@ -6003,16 +6079,16 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6003
6079
|
}
|
|
6004
6080
|
let sessionContextFlag = "";
|
|
6005
6081
|
try {
|
|
6006
|
-
const ctxDir =
|
|
6007
|
-
|
|
6008
|
-
const ctxFile =
|
|
6082
|
+
const ctxDir = path17.join(os8.homedir(), ".exe-os", "session-cache");
|
|
6083
|
+
mkdirSync7(ctxDir, { recursive: true });
|
|
6084
|
+
const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
|
|
6009
6085
|
const ctxContent = [
|
|
6010
6086
|
`## Session Context`,
|
|
6011
6087
|
`You are running in tmux session: ${sessionName}.`,
|
|
6012
6088
|
`Your parent coordinator session is ${exeSession2}.`,
|
|
6013
6089
|
`Your employees (if any) use the -${exeSession2} suffix.`
|
|
6014
6090
|
].join("\n");
|
|
6015
|
-
|
|
6091
|
+
writeFileSync7(ctxFile, ctxContent);
|
|
6016
6092
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
6017
6093
|
} catch {
|
|
6018
6094
|
}
|
|
@@ -6026,9 +6102,48 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6026
6102
|
}
|
|
6027
6103
|
}
|
|
6028
6104
|
}
|
|
6105
|
+
if (useCodex) {
|
|
6106
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
6107
|
+
if (codexCfg?.apiKeyEnv) {
|
|
6108
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
6109
|
+
if (keyVal) {
|
|
6110
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
6111
|
+
}
|
|
6112
|
+
}
|
|
6113
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
6114
|
+
}
|
|
6115
|
+
if (useOpencode) {
|
|
6116
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
6117
|
+
if (ocCfg?.apiKeyEnv) {
|
|
6118
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
6119
|
+
if (keyVal) {
|
|
6120
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
6121
|
+
}
|
|
6122
|
+
}
|
|
6123
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
6124
|
+
}
|
|
6125
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
6126
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
6127
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
6128
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
6129
|
+
}
|
|
6130
|
+
}
|
|
6029
6131
|
let spawnCommand;
|
|
6030
6132
|
if (useExeAgent) {
|
|
6031
6133
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
6134
|
+
} else if (useCodex) {
|
|
6135
|
+
process.stderr.write(
|
|
6136
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
6137
|
+
`
|
|
6138
|
+
);
|
|
6139
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
6140
|
+
} else if (useOpencode) {
|
|
6141
|
+
const binName = `${employeeName}-opencode`;
|
|
6142
|
+
process.stderr.write(
|
|
6143
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
6144
|
+
`
|
|
6145
|
+
);
|
|
6146
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
6032
6147
|
} else if (useBinSymlink) {
|
|
6033
6148
|
const binName = `${employeeName}-${ccProvider}`;
|
|
6034
6149
|
process.stderr.write(
|
|
@@ -6050,11 +6165,13 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6050
6165
|
transport.pipeLog(sessionName, logFile);
|
|
6051
6166
|
try {
|
|
6052
6167
|
const mySession = getMySession();
|
|
6053
|
-
const dispatchInfo =
|
|
6054
|
-
|
|
6168
|
+
const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
6169
|
+
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
6055
6170
|
dispatchedBy: mySession,
|
|
6056
6171
|
rootExe: exeSession2,
|
|
6057
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
6172
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
6173
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
6174
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
6058
6175
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6059
6176
|
}));
|
|
6060
6177
|
} catch {
|
|
@@ -6072,6 +6189,11 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6072
6189
|
booted = true;
|
|
6073
6190
|
break;
|
|
6074
6191
|
}
|
|
6192
|
+
} else if (useCodex) {
|
|
6193
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
6194
|
+
booted = true;
|
|
6195
|
+
break;
|
|
6196
|
+
}
|
|
6075
6197
|
} else {
|
|
6076
6198
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
6077
6199
|
booted = true;
|
|
@@ -6083,9 +6205,10 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
|
|
|
6083
6205
|
}
|
|
6084
6206
|
if (!booted) {
|
|
6085
6207
|
releaseSpawnLock2(sessionName);
|
|
6086
|
-
|
|
6208
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
6209
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
6087
6210
|
}
|
|
6088
|
-
if (!useExeAgent) {
|
|
6211
|
+
if (!useExeAgent && !useCodex) {
|
|
6089
6212
|
try {
|
|
6090
6213
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
6091
6214
|
} catch {
|
|
@@ -6112,17 +6235,19 @@ var init_tmux_routing = __esm({
|
|
|
6112
6235
|
init_cc_agent_support();
|
|
6113
6236
|
init_mcp_prefix();
|
|
6114
6237
|
init_provider_table();
|
|
6238
|
+
init_agent_config();
|
|
6239
|
+
init_runtime_table();
|
|
6115
6240
|
init_intercom_queue();
|
|
6116
6241
|
init_plan_limits();
|
|
6117
6242
|
init_employees();
|
|
6118
|
-
SPAWN_LOCK_DIR =
|
|
6119
|
-
SESSION_CACHE =
|
|
6243
|
+
SPAWN_LOCK_DIR = path17.join(os8.homedir(), ".exe-os", "spawn-locks");
|
|
6244
|
+
SESSION_CACHE = path17.join(os8.homedir(), ".exe-os", "session-cache");
|
|
6120
6245
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
6121
6246
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
6122
6247
|
VERIFY_PANE_LINES = 200;
|
|
6123
6248
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
6124
|
-
INTERCOM_LOG2 =
|
|
6125
|
-
DEBOUNCE_FILE =
|
|
6249
|
+
INTERCOM_LOG2 = path17.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
6250
|
+
DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
|
|
6126
6251
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
6127
6252
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
6128
6253
|
}
|
|
@@ -6189,10 +6314,10 @@ async function disposeEmbedder() {
|
|
|
6189
6314
|
async function embedDirect(text) {
|
|
6190
6315
|
const llamaCpp = await import("node-llama-cpp");
|
|
6191
6316
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
6192
|
-
const { existsSync:
|
|
6193
|
-
const
|
|
6194
|
-
const modelPath =
|
|
6195
|
-
if (!
|
|
6317
|
+
const { existsSync: existsSync16 } = await import("fs");
|
|
6318
|
+
const path19 = await import("path");
|
|
6319
|
+
const modelPath = path19.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
6320
|
+
if (!existsSync16(modelPath)) {
|
|
6196
6321
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
6197
6322
|
}
|
|
6198
6323
|
const llama = await llamaCpp.getLlama();
|
|
@@ -6450,8 +6575,8 @@ __export(worktree_exports, {
|
|
|
6450
6575
|
worktreePath: () => worktreePath
|
|
6451
6576
|
});
|
|
6452
6577
|
import { execSync as execSync8 } from "child_process";
|
|
6453
|
-
import { existsSync as
|
|
6454
|
-
import
|
|
6578
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12, appendFileSync as appendFileSync2, mkdirSync as mkdirSync8, realpathSync } from "fs";
|
|
6579
|
+
import path18 from "path";
|
|
6455
6580
|
function getGitRoot(dir) {
|
|
6456
6581
|
try {
|
|
6457
6582
|
const root = execSync8("git rev-parse --show-toplevel", {
|
|
@@ -6471,14 +6596,14 @@ function getMainRepoRoot(dir) {
|
|
|
6471
6596
|
"git rev-parse --path-format=absolute --git-common-dir",
|
|
6472
6597
|
{ cwd: dir, encoding: "utf-8", timeout: GIT_TIMEOUT_MS, stdio: ["pipe", "pipe", "pipe"] }
|
|
6473
6598
|
).trim();
|
|
6474
|
-
return realpath(
|
|
6599
|
+
return realpath(path18.dirname(commonDir));
|
|
6475
6600
|
} catch {
|
|
6476
6601
|
return null;
|
|
6477
6602
|
}
|
|
6478
6603
|
}
|
|
6479
6604
|
function worktreePath(repoRoot, employeeName, instance) {
|
|
6480
6605
|
const label = instanceLabel(employeeName, instance);
|
|
6481
|
-
return
|
|
6606
|
+
return path18.join(repoRoot, ".worktrees", label);
|
|
6482
6607
|
}
|
|
6483
6608
|
function worktreeBranch(employeeName, instance) {
|
|
6484
6609
|
return `${instanceLabel(employeeName, instance)}-work`;
|
|
@@ -6491,11 +6616,11 @@ function ensureWorktree(projectDir, employeeName, instance) {
|
|
|
6491
6616
|
if (!repoRoot) return null;
|
|
6492
6617
|
const wtPath = worktreePath(repoRoot, employeeName, instance);
|
|
6493
6618
|
const branch = worktreeBranch(employeeName, instance);
|
|
6494
|
-
if (
|
|
6619
|
+
if (existsSync15(path18.join(wtPath, ".git"))) {
|
|
6495
6620
|
return wtPath;
|
|
6496
6621
|
}
|
|
6497
|
-
const worktreesDir =
|
|
6498
|
-
|
|
6622
|
+
const worktreesDir = path18.join(repoRoot, ".worktrees");
|
|
6623
|
+
mkdirSync8(worktreesDir, { recursive: true });
|
|
6499
6624
|
ensureGitignoreEntry(repoRoot, "/.worktrees/");
|
|
6500
6625
|
try {
|
|
6501
6626
|
execSync8("git worktree prune", {
|
|
@@ -6550,7 +6675,7 @@ function cleanupWorktree(projectDir, employeeName, instance) {
|
|
|
6550
6675
|
if (!repoRoot) return { cleaned: false, reason: "not a git repo" };
|
|
6551
6676
|
const wtPath = worktreePath(repoRoot, employeeName, instance);
|
|
6552
6677
|
const branch = worktreeBranch(employeeName, instance);
|
|
6553
|
-
if (!
|
|
6678
|
+
if (!existsSync15(wtPath)) {
|
|
6554
6679
|
return { cleaned: false, reason: "worktree does not exist" };
|
|
6555
6680
|
}
|
|
6556
6681
|
if (isWorktreeDirty(wtPath)) {
|
|
@@ -6628,9 +6753,9 @@ function realpath(p) {
|
|
|
6628
6753
|
}
|
|
6629
6754
|
function ensureGitignoreEntry(repoRoot, entry) {
|
|
6630
6755
|
try {
|
|
6631
|
-
const gitignorePath =
|
|
6632
|
-
if (
|
|
6633
|
-
const content =
|
|
6756
|
+
const gitignorePath = path18.join(repoRoot, ".gitignore");
|
|
6757
|
+
if (existsSync15(gitignorePath)) {
|
|
6758
|
+
const content = readFileSync12(gitignorePath, "utf-8");
|
|
6634
6759
|
if (content.includes(entry)) return;
|
|
6635
6760
|
appendFileSync2(gitignorePath, `
|
|
6636
6761
|
# Agent worktrees (exe-os)
|