@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/gateway/index.js
CHANGED
|
@@ -2194,10 +2194,10 @@ async function disposeEmbedder() {
|
|
|
2194
2194
|
async function embedDirect(text) {
|
|
2195
2195
|
const llamaCpp = await import("node-llama-cpp");
|
|
2196
2196
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
2197
|
-
const { existsSync:
|
|
2198
|
-
const
|
|
2199
|
-
const modelPath =
|
|
2200
|
-
if (!
|
|
2197
|
+
const { existsSync: existsSync16 } = await import("fs");
|
|
2198
|
+
const path20 = await import("path");
|
|
2199
|
+
const modelPath = path20.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
2200
|
+
if (!existsSync16(modelPath)) {
|
|
2201
2201
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
2202
2202
|
}
|
|
2203
2203
|
const llama = await llamaCpp.getLlama();
|
|
@@ -3285,8 +3285,8 @@ __export(wiki_client_exports, {
|
|
|
3285
3285
|
listDocuments: () => listDocuments,
|
|
3286
3286
|
listWorkspaces: () => listWorkspaces
|
|
3287
3287
|
});
|
|
3288
|
-
async function wikiFetch(config2,
|
|
3289
|
-
const url = `${config2.baseUrl}/api/v1${
|
|
3288
|
+
async function wikiFetch(config2, path20, method = "GET", body) {
|
|
3289
|
+
const url = `${config2.baseUrl}/api/v1${path20}`;
|
|
3290
3290
|
const headers = {
|
|
3291
3291
|
Authorization: `Bearer ${config2.apiKey}`,
|
|
3292
3292
|
"Content-Type": "application/json"
|
|
@@ -3319,7 +3319,7 @@ async function wikiFetch(config2, path19, method = "GET", body) {
|
|
|
3319
3319
|
}
|
|
3320
3320
|
}
|
|
3321
3321
|
if (!response.ok) {
|
|
3322
|
-
throw new Error(`Wiki API ${method} ${
|
|
3322
|
+
throw new Error(`Wiki API ${method} ${path20}: ${response.status} ${response.statusText}`);
|
|
3323
3323
|
}
|
|
3324
3324
|
return response.json();
|
|
3325
3325
|
} finally {
|
|
@@ -4634,18 +4634,69 @@ var init_provider_table = __esm({
|
|
|
4634
4634
|
}
|
|
4635
4635
|
});
|
|
4636
4636
|
|
|
4637
|
-
// src/lib/
|
|
4638
|
-
|
|
4637
|
+
// src/lib/runtime-table.ts
|
|
4638
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
4639
|
+
var init_runtime_table = __esm({
|
|
4640
|
+
"src/lib/runtime-table.ts"() {
|
|
4641
|
+
"use strict";
|
|
4642
|
+
RUNTIME_TABLE = {
|
|
4643
|
+
codex: {
|
|
4644
|
+
binary: "codex",
|
|
4645
|
+
launchMode: "exec",
|
|
4646
|
+
autoApproveFlag: "--full-auto",
|
|
4647
|
+
inlineFlag: "--no-alt-screen",
|
|
4648
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
4649
|
+
defaultModel: "gpt-5.4"
|
|
4650
|
+
}
|
|
4651
|
+
};
|
|
4652
|
+
DEFAULT_RUNTIME = "claude";
|
|
4653
|
+
}
|
|
4654
|
+
});
|
|
4655
|
+
|
|
4656
|
+
// src/lib/agent-config.ts
|
|
4657
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
4639
4658
|
import path8 from "path";
|
|
4659
|
+
function loadAgentConfig() {
|
|
4660
|
+
if (!existsSync7(AGENT_CONFIG_PATH)) return {};
|
|
4661
|
+
try {
|
|
4662
|
+
return JSON.parse(readFileSync6(AGENT_CONFIG_PATH, "utf-8"));
|
|
4663
|
+
} catch {
|
|
4664
|
+
return {};
|
|
4665
|
+
}
|
|
4666
|
+
}
|
|
4667
|
+
function getAgentRuntime(agentId) {
|
|
4668
|
+
const config2 = loadAgentConfig();
|
|
4669
|
+
const entry = config2[agentId];
|
|
4670
|
+
if (entry) return entry;
|
|
4671
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
4672
|
+
}
|
|
4673
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
4674
|
+
var init_agent_config = __esm({
|
|
4675
|
+
"src/lib/agent-config.ts"() {
|
|
4676
|
+
"use strict";
|
|
4677
|
+
init_config();
|
|
4678
|
+
init_runtime_table();
|
|
4679
|
+
AGENT_CONFIG_PATH = path8.join(EXE_AI_DIR, "agent-config.json");
|
|
4680
|
+
DEFAULT_MODELS = {
|
|
4681
|
+
claude: "claude-opus-4",
|
|
4682
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
4683
|
+
opencode: "minimax-m2.7"
|
|
4684
|
+
};
|
|
4685
|
+
}
|
|
4686
|
+
});
|
|
4687
|
+
|
|
4688
|
+
// src/lib/intercom-queue.ts
|
|
4689
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
|
|
4690
|
+
import path9 from "path";
|
|
4640
4691
|
import os6 from "os";
|
|
4641
4692
|
function ensureDir() {
|
|
4642
|
-
const dir =
|
|
4643
|
-
if (!
|
|
4693
|
+
const dir = path9.dirname(QUEUE_PATH);
|
|
4694
|
+
if (!existsSync8(dir)) mkdirSync5(dir, { recursive: true });
|
|
4644
4695
|
}
|
|
4645
4696
|
function readQueue() {
|
|
4646
4697
|
try {
|
|
4647
|
-
if (!
|
|
4648
|
-
return JSON.parse(
|
|
4698
|
+
if (!existsSync8(QUEUE_PATH)) return [];
|
|
4699
|
+
return JSON.parse(readFileSync7(QUEUE_PATH, "utf8"));
|
|
4649
4700
|
} catch {
|
|
4650
4701
|
return [];
|
|
4651
4702
|
}
|
|
@@ -4653,7 +4704,7 @@ function readQueue() {
|
|
|
4653
4704
|
function writeQueue(queue) {
|
|
4654
4705
|
ensureDir();
|
|
4655
4706
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
4656
|
-
|
|
4707
|
+
writeFileSync4(tmp, JSON.stringify(queue, null, 2));
|
|
4657
4708
|
renameSync3(tmp, QUEUE_PATH);
|
|
4658
4709
|
}
|
|
4659
4710
|
function queueIntercom(targetSession, reason) {
|
|
@@ -4677,25 +4728,25 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
4677
4728
|
var init_intercom_queue = __esm({
|
|
4678
4729
|
"src/lib/intercom-queue.ts"() {
|
|
4679
4730
|
"use strict";
|
|
4680
|
-
QUEUE_PATH =
|
|
4731
|
+
QUEUE_PATH = path9.join(os6.homedir(), ".exe-os", "intercom-queue.json");
|
|
4681
4732
|
TTL_MS = 60 * 60 * 1e3;
|
|
4682
|
-
INTERCOM_LOG =
|
|
4733
|
+
INTERCOM_LOG = path9.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
4683
4734
|
}
|
|
4684
4735
|
});
|
|
4685
4736
|
|
|
4686
4737
|
// src/lib/license.ts
|
|
4687
|
-
import { readFileSync as
|
|
4738
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
|
|
4688
4739
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
4689
|
-
import
|
|
4740
|
+
import path10 from "path";
|
|
4690
4741
|
import { jwtVerify, importSPKI } from "jose";
|
|
4691
4742
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
4692
4743
|
var init_license = __esm({
|
|
4693
4744
|
"src/lib/license.ts"() {
|
|
4694
4745
|
"use strict";
|
|
4695
4746
|
init_config();
|
|
4696
|
-
LICENSE_PATH =
|
|
4697
|
-
CACHE_PATH =
|
|
4698
|
-
DEVICE_ID_PATH =
|
|
4747
|
+
LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
|
|
4748
|
+
CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
4749
|
+
DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
|
|
4699
4750
|
PLAN_LIMITS = {
|
|
4700
4751
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
4701
4752
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -4707,12 +4758,12 @@ var init_license = __esm({
|
|
|
4707
4758
|
});
|
|
4708
4759
|
|
|
4709
4760
|
// src/lib/plan-limits.ts
|
|
4710
|
-
import { readFileSync as
|
|
4711
|
-
import
|
|
4761
|
+
import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
|
|
4762
|
+
import path11 from "path";
|
|
4712
4763
|
function getLicenseSync() {
|
|
4713
4764
|
try {
|
|
4714
|
-
if (!
|
|
4715
|
-
const raw = JSON.parse(
|
|
4765
|
+
if (!existsSync10(CACHE_PATH2)) return freeLicense();
|
|
4766
|
+
const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
|
|
4716
4767
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
4717
4768
|
const parts = raw.token.split(".");
|
|
4718
4769
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -4750,8 +4801,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
4750
4801
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
4751
4802
|
let count = 0;
|
|
4752
4803
|
try {
|
|
4753
|
-
if (
|
|
4754
|
-
const raw =
|
|
4804
|
+
if (existsSync10(filePath)) {
|
|
4805
|
+
const raw = readFileSync9(filePath, "utf8");
|
|
4755
4806
|
const employees = JSON.parse(raw);
|
|
4756
4807
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
4757
4808
|
}
|
|
@@ -4780,19 +4831,19 @@ var init_plan_limits = __esm({
|
|
|
4780
4831
|
this.name = "PlanLimitError";
|
|
4781
4832
|
}
|
|
4782
4833
|
};
|
|
4783
|
-
CACHE_PATH2 =
|
|
4834
|
+
CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
4784
4835
|
}
|
|
4785
4836
|
});
|
|
4786
4837
|
|
|
4787
4838
|
// src/lib/notifications.ts
|
|
4788
4839
|
import crypto3 from "crypto";
|
|
4789
|
-
import
|
|
4840
|
+
import path12 from "path";
|
|
4790
4841
|
import os7 from "os";
|
|
4791
4842
|
import {
|
|
4792
|
-
readFileSync as
|
|
4843
|
+
readFileSync as readFileSync10,
|
|
4793
4844
|
readdirSync as readdirSync2,
|
|
4794
4845
|
unlinkSync as unlinkSync3,
|
|
4795
|
-
existsSync as
|
|
4846
|
+
existsSync as existsSync11,
|
|
4796
4847
|
rmdirSync
|
|
4797
4848
|
} from "fs";
|
|
4798
4849
|
async function writeNotification(notification) {
|
|
@@ -4896,11 +4947,11 @@ var init_task_scope = __esm({
|
|
|
4896
4947
|
|
|
4897
4948
|
// src/lib/tasks-crud.ts
|
|
4898
4949
|
import crypto5 from "crypto";
|
|
4899
|
-
import
|
|
4950
|
+
import path13 from "path";
|
|
4900
4951
|
import os8 from "os";
|
|
4901
4952
|
import { execSync as execSync4 } from "child_process";
|
|
4902
4953
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
4903
|
-
import { existsSync as
|
|
4954
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
4904
4955
|
async function writeCheckpoint(input) {
|
|
4905
4956
|
const client = getClient();
|
|
4906
4957
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -5075,8 +5126,8 @@ ${laneWarning}` : laneWarning;
|
|
|
5075
5126
|
}
|
|
5076
5127
|
if (input.baseDir) {
|
|
5077
5128
|
try {
|
|
5078
|
-
await mkdir4(
|
|
5079
|
-
await mkdir4(
|
|
5129
|
+
await mkdir4(path13.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
5130
|
+
await mkdir4(path13.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
5080
5131
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
5081
5132
|
await ensureGitignoreExe(input.baseDir);
|
|
5082
5133
|
} catch {
|
|
@@ -5112,10 +5163,10 @@ ${laneWarning}` : laneWarning;
|
|
|
5112
5163
|
});
|
|
5113
5164
|
if (input.baseDir) {
|
|
5114
5165
|
try {
|
|
5115
|
-
const EXE_OS_DIR =
|
|
5116
|
-
const mdPath =
|
|
5117
|
-
const mdDir =
|
|
5118
|
-
if (!
|
|
5166
|
+
const EXE_OS_DIR = path13.join(os8.homedir(), ".exe-os");
|
|
5167
|
+
const mdPath = path13.join(EXE_OS_DIR, taskFile);
|
|
5168
|
+
const mdDir = path13.dirname(mdPath);
|
|
5169
|
+
if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
5119
5170
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
5120
5171
|
const mdContent = `# ${input.title}
|
|
5121
5172
|
|
|
@@ -5140,7 +5191,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
|
|
|
5140
5191
|
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
5141
5192
|
`;
|
|
5142
5193
|
await writeFile4(mdPath, mdContent, "utf-8");
|
|
5143
|
-
} catch {
|
|
5194
|
+
} catch (err) {
|
|
5195
|
+
process.stderr.write(
|
|
5196
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
5197
|
+
`
|
|
5198
|
+
);
|
|
5144
5199
|
}
|
|
5145
5200
|
}
|
|
5146
5201
|
return {
|
|
@@ -5400,9 +5455,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
5400
5455
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
5401
5456
|
}
|
|
5402
5457
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
5403
|
-
const archPath =
|
|
5458
|
+
const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
5404
5459
|
try {
|
|
5405
|
-
if (
|
|
5460
|
+
if (existsSync12(archPath)) return;
|
|
5406
5461
|
const template = [
|
|
5407
5462
|
`# ${projectName} \u2014 System Architecture`,
|
|
5408
5463
|
"",
|
|
@@ -5435,10 +5490,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
5435
5490
|
}
|
|
5436
5491
|
}
|
|
5437
5492
|
async function ensureGitignoreExe(baseDir) {
|
|
5438
|
-
const gitignorePath =
|
|
5493
|
+
const gitignorePath = path13.join(baseDir, ".gitignore");
|
|
5439
5494
|
try {
|
|
5440
|
-
if (
|
|
5441
|
-
const content =
|
|
5495
|
+
if (existsSync12(gitignorePath)) {
|
|
5496
|
+
const content = readFileSync11(gitignorePath, "utf-8");
|
|
5442
5497
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
5443
5498
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
5444
5499
|
} else {
|
|
@@ -5469,8 +5524,8 @@ var init_tasks_crud = __esm({
|
|
|
5469
5524
|
});
|
|
5470
5525
|
|
|
5471
5526
|
// src/lib/tasks-review.ts
|
|
5472
|
-
import
|
|
5473
|
-
import { existsSync as
|
|
5527
|
+
import path14 from "path";
|
|
5528
|
+
import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
5474
5529
|
async function countPendingReviews(sessionScope) {
|
|
5475
5530
|
const client = getClient();
|
|
5476
5531
|
if (sessionScope) {
|
|
@@ -5651,11 +5706,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
5651
5706
|
);
|
|
5652
5707
|
}
|
|
5653
5708
|
try {
|
|
5654
|
-
const cacheDir =
|
|
5655
|
-
if (
|
|
5709
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
5710
|
+
if (existsSync13(cacheDir)) {
|
|
5656
5711
|
for (const f of readdirSync3(cacheDir)) {
|
|
5657
5712
|
if (f.startsWith("review-notified-")) {
|
|
5658
|
-
unlinkSync4(
|
|
5713
|
+
unlinkSync4(path14.join(cacheDir, f));
|
|
5659
5714
|
}
|
|
5660
5715
|
}
|
|
5661
5716
|
}
|
|
@@ -5676,7 +5731,7 @@ var init_tasks_review = __esm({
|
|
|
5676
5731
|
});
|
|
5677
5732
|
|
|
5678
5733
|
// src/lib/tasks-chain.ts
|
|
5679
|
-
import
|
|
5734
|
+
import path15 from "path";
|
|
5680
5735
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
5681
5736
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
5682
5737
|
const client = getClient();
|
|
@@ -5693,7 +5748,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
5693
5748
|
});
|
|
5694
5749
|
for (const ur of unblockedRows.rows) {
|
|
5695
5750
|
try {
|
|
5696
|
-
const ubFile =
|
|
5751
|
+
const ubFile = path15.join(baseDir, String(ur.task_file));
|
|
5697
5752
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
5698
5753
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
5699
5754
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -5762,7 +5817,7 @@ var init_tasks_chain = __esm({
|
|
|
5762
5817
|
|
|
5763
5818
|
// src/lib/project-name.ts
|
|
5764
5819
|
import { execSync as execSync5 } from "child_process";
|
|
5765
|
-
import
|
|
5820
|
+
import path16 from "path";
|
|
5766
5821
|
function getProjectName(cwd) {
|
|
5767
5822
|
const dir = cwd ?? process.cwd();
|
|
5768
5823
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -5775,7 +5830,7 @@ function getProjectName(cwd) {
|
|
|
5775
5830
|
timeout: 2e3,
|
|
5776
5831
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5777
5832
|
}).trim();
|
|
5778
|
-
repoRoot =
|
|
5833
|
+
repoRoot = path16.dirname(gitCommonDir);
|
|
5779
5834
|
} catch {
|
|
5780
5835
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
5781
5836
|
cwd: dir,
|
|
@@ -5784,11 +5839,11 @@ function getProjectName(cwd) {
|
|
|
5784
5839
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5785
5840
|
}).trim();
|
|
5786
5841
|
}
|
|
5787
|
-
_cached2 =
|
|
5842
|
+
_cached2 = path16.basename(repoRoot);
|
|
5788
5843
|
_cachedCwd = dir;
|
|
5789
5844
|
return _cached2;
|
|
5790
5845
|
} catch {
|
|
5791
|
-
_cached2 =
|
|
5846
|
+
_cached2 = path16.basename(dir);
|
|
5792
5847
|
_cachedCwd = dir;
|
|
5793
5848
|
return _cached2;
|
|
5794
5849
|
}
|
|
@@ -6261,8 +6316,8 @@ __export(tasks_exports, {
|
|
|
6261
6316
|
updateTaskStatus: () => updateTaskStatus,
|
|
6262
6317
|
writeCheckpoint: () => writeCheckpoint
|
|
6263
6318
|
});
|
|
6264
|
-
import
|
|
6265
|
-
import { writeFileSync as
|
|
6319
|
+
import path17 from "path";
|
|
6320
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
|
|
6266
6321
|
async function createTask(input) {
|
|
6267
6322
|
const result = await createTaskCore(input);
|
|
6268
6323
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -6281,11 +6336,11 @@ async function updateTask(input) {
|
|
|
6281
6336
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
6282
6337
|
try {
|
|
6283
6338
|
const agent = String(row.assigned_to);
|
|
6284
|
-
const cacheDir =
|
|
6285
|
-
const cachePath =
|
|
6339
|
+
const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
|
|
6340
|
+
const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
|
|
6286
6341
|
if (input.status === "in_progress") {
|
|
6287
|
-
|
|
6288
|
-
|
|
6342
|
+
mkdirSync7(cacheDir, { recursive: true });
|
|
6343
|
+
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
6289
6344
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
6290
6345
|
try {
|
|
6291
6346
|
unlinkSync5(cachePath);
|
|
@@ -6752,13 +6807,13 @@ __export(tmux_routing_exports, {
|
|
|
6752
6807
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
6753
6808
|
});
|
|
6754
6809
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
6755
|
-
import { readFileSync as
|
|
6756
|
-
import
|
|
6810
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, existsSync as existsSync14, appendFileSync } from "fs";
|
|
6811
|
+
import path18 from "path";
|
|
6757
6812
|
import os9 from "os";
|
|
6758
6813
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6759
6814
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
6760
6815
|
function spawnLockPath(sessionName) {
|
|
6761
|
-
return
|
|
6816
|
+
return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
6762
6817
|
}
|
|
6763
6818
|
function isProcessAlive(pid) {
|
|
6764
6819
|
try {
|
|
@@ -6769,13 +6824,13 @@ function isProcessAlive(pid) {
|
|
|
6769
6824
|
}
|
|
6770
6825
|
}
|
|
6771
6826
|
function acquireSpawnLock2(sessionName) {
|
|
6772
|
-
if (!
|
|
6773
|
-
|
|
6827
|
+
if (!existsSync14(SPAWN_LOCK_DIR)) {
|
|
6828
|
+
mkdirSync8(SPAWN_LOCK_DIR, { recursive: true });
|
|
6774
6829
|
}
|
|
6775
6830
|
const lockFile = spawnLockPath(sessionName);
|
|
6776
|
-
if (
|
|
6831
|
+
if (existsSync14(lockFile)) {
|
|
6777
6832
|
try {
|
|
6778
|
-
const lock = JSON.parse(
|
|
6833
|
+
const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
|
|
6779
6834
|
const age = Date.now() - lock.timestamp;
|
|
6780
6835
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
6781
6836
|
return false;
|
|
@@ -6783,7 +6838,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
6783
6838
|
} catch {
|
|
6784
6839
|
}
|
|
6785
6840
|
}
|
|
6786
|
-
|
|
6841
|
+
writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
6787
6842
|
return true;
|
|
6788
6843
|
}
|
|
6789
6844
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -6795,13 +6850,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
6795
6850
|
function resolveBehaviorsExporterScript() {
|
|
6796
6851
|
try {
|
|
6797
6852
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
6798
|
-
const scriptPath =
|
|
6799
|
-
|
|
6853
|
+
const scriptPath = path18.join(
|
|
6854
|
+
path18.dirname(thisFile),
|
|
6800
6855
|
"..",
|
|
6801
6856
|
"bin",
|
|
6802
6857
|
"exe-export-behaviors.js"
|
|
6803
6858
|
);
|
|
6804
|
-
return
|
|
6859
|
+
return existsSync14(scriptPath) ? scriptPath : null;
|
|
6805
6860
|
} catch {
|
|
6806
6861
|
return null;
|
|
6807
6862
|
}
|
|
@@ -6867,12 +6922,12 @@ function extractRootExe(name) {
|
|
|
6867
6922
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
6868
6923
|
}
|
|
6869
6924
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
6870
|
-
if (!
|
|
6871
|
-
|
|
6925
|
+
if (!existsSync14(SESSION_CACHE)) {
|
|
6926
|
+
mkdirSync8(SESSION_CACHE, { recursive: true });
|
|
6872
6927
|
}
|
|
6873
6928
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
6874
|
-
const filePath =
|
|
6875
|
-
|
|
6929
|
+
const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
6930
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
6876
6931
|
parentExe: rootExe,
|
|
6877
6932
|
dispatchedBy: dispatchedBy || rootExe,
|
|
6878
6933
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -6880,7 +6935,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
6880
6935
|
}
|
|
6881
6936
|
function getParentExe(sessionKey) {
|
|
6882
6937
|
try {
|
|
6883
|
-
const data = JSON.parse(
|
|
6938
|
+
const data = JSON.parse(readFileSync12(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
6884
6939
|
return data.parentExe || null;
|
|
6885
6940
|
} catch {
|
|
6886
6941
|
return null;
|
|
@@ -6888,8 +6943,8 @@ function getParentExe(sessionKey) {
|
|
|
6888
6943
|
}
|
|
6889
6944
|
function getDispatchedBy(sessionKey) {
|
|
6890
6945
|
try {
|
|
6891
|
-
const data = JSON.parse(
|
|
6892
|
-
|
|
6946
|
+
const data = JSON.parse(readFileSync12(
|
|
6947
|
+
path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
6893
6948
|
"utf8"
|
|
6894
6949
|
));
|
|
6895
6950
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -6950,32 +7005,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
6950
7005
|
}
|
|
6951
7006
|
function readDebounceState() {
|
|
6952
7007
|
try {
|
|
6953
|
-
if (!
|
|
6954
|
-
|
|
7008
|
+
if (!existsSync14(DEBOUNCE_FILE)) return {};
|
|
7009
|
+
const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
|
|
7010
|
+
const state = {};
|
|
7011
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
7012
|
+
if (typeof val === "number") {
|
|
7013
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
7014
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
7015
|
+
state[key] = val;
|
|
7016
|
+
}
|
|
7017
|
+
}
|
|
7018
|
+
return state;
|
|
6955
7019
|
} catch {
|
|
6956
7020
|
return {};
|
|
6957
7021
|
}
|
|
6958
7022
|
}
|
|
6959
7023
|
function writeDebounceState(state) {
|
|
6960
7024
|
try {
|
|
6961
|
-
if (!
|
|
6962
|
-
|
|
7025
|
+
if (!existsSync14(SESSION_CACHE)) mkdirSync8(SESSION_CACHE, { recursive: true });
|
|
7026
|
+
writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
|
|
6963
7027
|
} catch {
|
|
6964
7028
|
}
|
|
6965
7029
|
}
|
|
6966
7030
|
function isDebounced(targetSession) {
|
|
6967
7031
|
const state = readDebounceState();
|
|
6968
|
-
const
|
|
6969
|
-
|
|
7032
|
+
const entry = state[targetSession];
|
|
7033
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
7034
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
7035
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
7036
|
+
state[targetSession].pending++;
|
|
7037
|
+
writeDebounceState(state);
|
|
7038
|
+
return true;
|
|
7039
|
+
}
|
|
7040
|
+
return false;
|
|
6970
7041
|
}
|
|
6971
7042
|
function recordDebounce(targetSession) {
|
|
6972
7043
|
const state = readDebounceState();
|
|
6973
|
-
state[targetSession]
|
|
7044
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
7045
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
6974
7046
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
6975
7047
|
for (const key of Object.keys(state)) {
|
|
6976
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
7048
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
6977
7049
|
}
|
|
6978
7050
|
writeDebounceState(state);
|
|
7051
|
+
return batched;
|
|
6979
7052
|
}
|
|
6980
7053
|
function logIntercom(msg) {
|
|
6981
7054
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -7020,7 +7093,7 @@ function sendIntercom(targetSession) {
|
|
|
7020
7093
|
return "skipped_exe";
|
|
7021
7094
|
}
|
|
7022
7095
|
if (isDebounced(targetSession)) {
|
|
7023
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
7096
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
7024
7097
|
return "debounced";
|
|
7025
7098
|
}
|
|
7026
7099
|
try {
|
|
@@ -7032,14 +7105,14 @@ function sendIntercom(targetSession) {
|
|
|
7032
7105
|
const sessionState = getSessionState(targetSession);
|
|
7033
7106
|
if (sessionState === "no_claude") {
|
|
7034
7107
|
queueIntercom(targetSession, "claude not running in session");
|
|
7035
|
-
recordDebounce(targetSession);
|
|
7036
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
7108
|
+
const batched2 = recordDebounce(targetSession);
|
|
7109
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
7037
7110
|
return "queued";
|
|
7038
7111
|
}
|
|
7039
7112
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
7040
7113
|
queueIntercom(targetSession, "session busy at send time");
|
|
7041
|
-
recordDebounce(targetSession);
|
|
7042
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
7114
|
+
const batched2 = recordDebounce(targetSession);
|
|
7115
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
7043
7116
|
return "queued";
|
|
7044
7117
|
}
|
|
7045
7118
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -7047,8 +7120,8 @@ function sendIntercom(targetSession) {
|
|
|
7047
7120
|
transport.sendKeys(targetSession, "q");
|
|
7048
7121
|
}
|
|
7049
7122
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
7050
|
-
recordDebounce(targetSession);
|
|
7051
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
7123
|
+
const batched = recordDebounce(targetSession);
|
|
7124
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
7052
7125
|
return "delivered";
|
|
7053
7126
|
} catch {
|
|
7054
7127
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -7150,26 +7223,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7150
7223
|
const transport = getTransport();
|
|
7151
7224
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
7152
7225
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
7153
|
-
const logDir =
|
|
7154
|
-
const logFile =
|
|
7155
|
-
if (!
|
|
7156
|
-
|
|
7226
|
+
const logDir = path18.join(os9.homedir(), ".exe-os", "session-logs");
|
|
7227
|
+
const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
7228
|
+
if (!existsSync14(logDir)) {
|
|
7229
|
+
mkdirSync8(logDir, { recursive: true });
|
|
7157
7230
|
}
|
|
7158
7231
|
transport.kill(sessionName);
|
|
7159
7232
|
let cleanupSuffix = "";
|
|
7160
7233
|
try {
|
|
7161
7234
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
7162
|
-
const cleanupScript =
|
|
7163
|
-
if (
|
|
7235
|
+
const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
7236
|
+
if (existsSync14(cleanupScript)) {
|
|
7164
7237
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
7165
7238
|
}
|
|
7166
7239
|
} catch {
|
|
7167
7240
|
}
|
|
7168
7241
|
try {
|
|
7169
|
-
const claudeJsonPath =
|
|
7242
|
+
const claudeJsonPath = path18.join(os9.homedir(), ".claude.json");
|
|
7170
7243
|
let claudeJson = {};
|
|
7171
7244
|
try {
|
|
7172
|
-
claudeJson = JSON.parse(
|
|
7245
|
+
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
7173
7246
|
} catch {
|
|
7174
7247
|
}
|
|
7175
7248
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -7177,17 +7250,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7177
7250
|
const trustDir = opts?.cwd ?? projectDir;
|
|
7178
7251
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
7179
7252
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
7180
|
-
|
|
7253
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
7181
7254
|
} catch {
|
|
7182
7255
|
}
|
|
7183
7256
|
try {
|
|
7184
|
-
const settingsDir =
|
|
7257
|
+
const settingsDir = path18.join(os9.homedir(), ".claude", "projects");
|
|
7185
7258
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
7186
|
-
const projSettingsDir =
|
|
7187
|
-
const settingsPath =
|
|
7259
|
+
const projSettingsDir = path18.join(settingsDir, normalizedKey);
|
|
7260
|
+
const settingsPath = path18.join(projSettingsDir, "settings.json");
|
|
7188
7261
|
let settings = {};
|
|
7189
7262
|
try {
|
|
7190
|
-
settings = JSON.parse(
|
|
7263
|
+
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
7191
7264
|
} catch {
|
|
7192
7265
|
}
|
|
7193
7266
|
const perms = settings.permissions ?? {};
|
|
@@ -7215,20 +7288,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7215
7288
|
if (changed) {
|
|
7216
7289
|
perms.allow = allow;
|
|
7217
7290
|
settings.permissions = perms;
|
|
7218
|
-
|
|
7219
|
-
|
|
7291
|
+
mkdirSync8(projSettingsDir, { recursive: true });
|
|
7292
|
+
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
7220
7293
|
}
|
|
7221
7294
|
} catch {
|
|
7222
7295
|
}
|
|
7223
7296
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
7224
7297
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
7225
|
-
const
|
|
7298
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
7299
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
7300
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
7301
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
7226
7302
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
7227
7303
|
let identityFlag = "";
|
|
7228
7304
|
let behaviorsFlag = "";
|
|
7229
7305
|
let legacyFallbackWarned = false;
|
|
7230
7306
|
if (!useExeAgent && !useBinSymlink) {
|
|
7231
|
-
const identityPath =
|
|
7307
|
+
const identityPath = path18.join(
|
|
7232
7308
|
os9.homedir(),
|
|
7233
7309
|
".exe-os",
|
|
7234
7310
|
"identity",
|
|
@@ -7238,13 +7314,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7238
7314
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
7239
7315
|
if (hasAgentFlag) {
|
|
7240
7316
|
identityFlag = ` --agent ${employeeName}`;
|
|
7241
|
-
} else if (
|
|
7317
|
+
} else if (existsSync14(identityPath)) {
|
|
7242
7318
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
7243
7319
|
legacyFallbackWarned = true;
|
|
7244
7320
|
}
|
|
7245
7321
|
const behaviorsFile = exportBehaviorsSync(
|
|
7246
7322
|
employeeName,
|
|
7247
|
-
|
|
7323
|
+
path18.basename(spawnCwd),
|
|
7248
7324
|
sessionName
|
|
7249
7325
|
);
|
|
7250
7326
|
if (behaviorsFile) {
|
|
@@ -7259,16 +7335,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7259
7335
|
}
|
|
7260
7336
|
let sessionContextFlag = "";
|
|
7261
7337
|
try {
|
|
7262
|
-
const ctxDir =
|
|
7263
|
-
|
|
7264
|
-
const ctxFile =
|
|
7338
|
+
const ctxDir = path18.join(os9.homedir(), ".exe-os", "session-cache");
|
|
7339
|
+
mkdirSync8(ctxDir, { recursive: true });
|
|
7340
|
+
const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
|
|
7265
7341
|
const ctxContent = [
|
|
7266
7342
|
`## Session Context`,
|
|
7267
7343
|
`You are running in tmux session: ${sessionName}.`,
|
|
7268
7344
|
`Your parent coordinator session is ${exeSession}.`,
|
|
7269
7345
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
7270
7346
|
].join("\n");
|
|
7271
|
-
|
|
7347
|
+
writeFileSync7(ctxFile, ctxContent);
|
|
7272
7348
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
7273
7349
|
} catch {
|
|
7274
7350
|
}
|
|
@@ -7282,9 +7358,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7282
7358
|
}
|
|
7283
7359
|
}
|
|
7284
7360
|
}
|
|
7361
|
+
if (useCodex) {
|
|
7362
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
7363
|
+
if (codexCfg?.apiKeyEnv) {
|
|
7364
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
7365
|
+
if (keyVal) {
|
|
7366
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
7367
|
+
}
|
|
7368
|
+
}
|
|
7369
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
7370
|
+
}
|
|
7371
|
+
if (useOpencode) {
|
|
7372
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
7373
|
+
if (ocCfg?.apiKeyEnv) {
|
|
7374
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
7375
|
+
if (keyVal) {
|
|
7376
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
7377
|
+
}
|
|
7378
|
+
}
|
|
7379
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
7380
|
+
}
|
|
7381
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
7382
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
7383
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
7384
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
7385
|
+
}
|
|
7386
|
+
}
|
|
7285
7387
|
let spawnCommand;
|
|
7286
7388
|
if (useExeAgent) {
|
|
7287
7389
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
7390
|
+
} else if (useCodex) {
|
|
7391
|
+
process.stderr.write(
|
|
7392
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
7393
|
+
`
|
|
7394
|
+
);
|
|
7395
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
7396
|
+
} else if (useOpencode) {
|
|
7397
|
+
const binName = `${employeeName}-opencode`;
|
|
7398
|
+
process.stderr.write(
|
|
7399
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
7400
|
+
`
|
|
7401
|
+
);
|
|
7402
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
7288
7403
|
} else if (useBinSymlink) {
|
|
7289
7404
|
const binName = `${employeeName}-${ccProvider}`;
|
|
7290
7405
|
process.stderr.write(
|
|
@@ -7306,11 +7421,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7306
7421
|
transport.pipeLog(sessionName, logFile);
|
|
7307
7422
|
try {
|
|
7308
7423
|
const mySession = getMySession();
|
|
7309
|
-
const dispatchInfo =
|
|
7310
|
-
|
|
7424
|
+
const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
7425
|
+
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
7311
7426
|
dispatchedBy: mySession,
|
|
7312
7427
|
rootExe: exeSession,
|
|
7313
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
7428
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
7429
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
7430
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
7314
7431
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7315
7432
|
}));
|
|
7316
7433
|
} catch {
|
|
@@ -7328,6 +7445,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7328
7445
|
booted = true;
|
|
7329
7446
|
break;
|
|
7330
7447
|
}
|
|
7448
|
+
} else if (useCodex) {
|
|
7449
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
7450
|
+
booted = true;
|
|
7451
|
+
break;
|
|
7452
|
+
}
|
|
7331
7453
|
} else {
|
|
7332
7454
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
7333
7455
|
booted = true;
|
|
@@ -7339,9 +7461,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7339
7461
|
}
|
|
7340
7462
|
if (!booted) {
|
|
7341
7463
|
releaseSpawnLock2(sessionName);
|
|
7342
|
-
|
|
7464
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
7465
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
7343
7466
|
}
|
|
7344
|
-
if (!useExeAgent) {
|
|
7467
|
+
if (!useExeAgent && !useCodex) {
|
|
7345
7468
|
try {
|
|
7346
7469
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
7347
7470
|
} catch {
|
|
@@ -7368,17 +7491,19 @@ var init_tmux_routing = __esm({
|
|
|
7368
7491
|
init_cc_agent_support();
|
|
7369
7492
|
init_mcp_prefix();
|
|
7370
7493
|
init_provider_table();
|
|
7494
|
+
init_agent_config();
|
|
7495
|
+
init_runtime_table();
|
|
7371
7496
|
init_intercom_queue();
|
|
7372
7497
|
init_plan_limits();
|
|
7373
7498
|
init_employees();
|
|
7374
|
-
SPAWN_LOCK_DIR =
|
|
7375
|
-
SESSION_CACHE =
|
|
7499
|
+
SPAWN_LOCK_DIR = path18.join(os9.homedir(), ".exe-os", "spawn-locks");
|
|
7500
|
+
SESSION_CACHE = path18.join(os9.homedir(), ".exe-os", "session-cache");
|
|
7376
7501
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
7377
7502
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
7378
7503
|
VERIFY_PANE_LINES = 200;
|
|
7379
7504
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
7380
|
-
INTERCOM_LOG2 =
|
|
7381
|
-
DEBOUNCE_FILE =
|
|
7505
|
+
INTERCOM_LOG2 = path18.join(os9.homedir(), ".exe-os", "intercom.log");
|
|
7506
|
+
DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
|
|
7382
7507
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
7383
7508
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
7384
7509
|
}
|
|
@@ -11410,11 +11535,11 @@ async function ensureCRMContact(info) {
|
|
|
11410
11535
|
}
|
|
11411
11536
|
|
|
11412
11537
|
// src/automation/trigger-engine.ts
|
|
11413
|
-
import { readFileSync as
|
|
11538
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, existsSync as existsSync15, mkdirSync as mkdirSync9 } from "fs";
|
|
11414
11539
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
11415
|
-
import
|
|
11540
|
+
import path19 from "path";
|
|
11416
11541
|
import os10 from "os";
|
|
11417
|
-
var TRIGGERS_PATH =
|
|
11542
|
+
var TRIGGERS_PATH = path19.join(os10.homedir(), ".exe-os", "triggers.json");
|
|
11418
11543
|
var GRAPH_API_VERSION = "v21.0";
|
|
11419
11544
|
function substituteTemplate(template, record) {
|
|
11420
11545
|
return template.replace(
|
|
@@ -11468,9 +11593,9 @@ function evaluateConditions(conditions, record) {
|
|
|
11468
11593
|
return conditions.every((c) => evaluateCondition(c, record));
|
|
11469
11594
|
}
|
|
11470
11595
|
function loadTriggers(project) {
|
|
11471
|
-
if (!
|
|
11596
|
+
if (!existsSync15(TRIGGERS_PATH)) return [];
|
|
11472
11597
|
try {
|
|
11473
|
-
const raw =
|
|
11598
|
+
const raw = readFileSync13(TRIGGERS_PATH, "utf-8");
|
|
11474
11599
|
const all = JSON.parse(raw);
|
|
11475
11600
|
if (!Array.isArray(all)) return [];
|
|
11476
11601
|
if (project) {
|