@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/mcp/server.js
CHANGED
|
@@ -679,10 +679,10 @@ async function disposeEmbedder() {
|
|
|
679
679
|
async function embedDirect(text) {
|
|
680
680
|
const llamaCpp = await import("node-llama-cpp");
|
|
681
681
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
682
|
-
const { existsSync:
|
|
683
|
-
const
|
|
684
|
-
const modelPath =
|
|
685
|
-
if (!
|
|
682
|
+
const { existsSync: existsSync31 } = await import("fs");
|
|
683
|
+
const path38 = await import("path");
|
|
684
|
+
const modelPath = path38.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
685
|
+
if (!existsSync31(modelPath)) {
|
|
686
686
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
687
687
|
}
|
|
688
688
|
const llama = await llamaCpp.getLlama();
|
|
@@ -4425,6 +4425,7 @@ __export(hybrid_search_exports, {
|
|
|
4425
4425
|
estimateCardinality: () => estimateCardinality,
|
|
4426
4426
|
hybridSearch: () => hybridSearch,
|
|
4427
4427
|
lightweightSearch: () => lightweightSearch,
|
|
4428
|
+
recentRecords: () => recentRecords,
|
|
4428
4429
|
rrfMerge: () => rrfMerge,
|
|
4429
4430
|
rrfMergeMulti: () => rrfMergeMulti
|
|
4430
4431
|
});
|
|
@@ -4485,7 +4486,7 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
4485
4486
|
process.stderr.write("[hybrid-search] Embed daemon unavailable \u2014 FTS-only mode\n");
|
|
4486
4487
|
}
|
|
4487
4488
|
let grepPromise = Promise.resolve([]);
|
|
4488
|
-
if (config2.fileGrepEnabled
|
|
4489
|
+
if (config2.fileGrepEnabled === true) {
|
|
4489
4490
|
try {
|
|
4490
4491
|
const { getProjectName: getProjectName2 } = await Promise.resolve().then(() => (init_project_name(), project_name_exports));
|
|
4491
4492
|
const projectRoot = process.cwd();
|
|
@@ -4754,7 +4755,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
|
|
|
4754
4755
|
source_type: row.source_type ?? null
|
|
4755
4756
|
}));
|
|
4756
4757
|
}
|
|
4757
|
-
async function recentRecords(agentId, options, limit) {
|
|
4758
|
+
async function recentRecords(agentId, options, limit, textFilter) {
|
|
4758
4759
|
const client = getClient();
|
|
4759
4760
|
const statusFilter = options?.includeArchived ? "" : `
|
|
4760
4761
|
AND COALESCE(status, 'active') = 'active'`;
|
|
@@ -4794,6 +4795,10 @@ async function recentRecords(agentId, options, limit) {
|
|
|
4794
4795
|
sql += ` AND memory_type = ?`;
|
|
4795
4796
|
args.push(options.memoryType);
|
|
4796
4797
|
}
|
|
4798
|
+
if (textFilter) {
|
|
4799
|
+
sql += ` AND raw_text LIKE '%' || ? || '%'`;
|
|
4800
|
+
args.push(textFilter);
|
|
4801
|
+
}
|
|
4797
4802
|
sql += ` ORDER BY timestamp DESC LIMIT ?`;
|
|
4798
4803
|
args.push(limit);
|
|
4799
4804
|
const result = await client.execute({ sql, args });
|
|
@@ -5910,18 +5915,98 @@ var init_provider_table = __esm({
|
|
|
5910
5915
|
}
|
|
5911
5916
|
});
|
|
5912
5917
|
|
|
5913
|
-
// src/lib/
|
|
5914
|
-
|
|
5918
|
+
// src/lib/runtime-table.ts
|
|
5919
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
5920
|
+
var init_runtime_table = __esm({
|
|
5921
|
+
"src/lib/runtime-table.ts"() {
|
|
5922
|
+
"use strict";
|
|
5923
|
+
RUNTIME_TABLE = {
|
|
5924
|
+
codex: {
|
|
5925
|
+
binary: "codex",
|
|
5926
|
+
launchMode: "exec",
|
|
5927
|
+
autoApproveFlag: "--full-auto",
|
|
5928
|
+
inlineFlag: "--no-alt-screen",
|
|
5929
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
5930
|
+
defaultModel: "gpt-5.4"
|
|
5931
|
+
}
|
|
5932
|
+
};
|
|
5933
|
+
DEFAULT_RUNTIME = "claude";
|
|
5934
|
+
}
|
|
5935
|
+
});
|
|
5936
|
+
|
|
5937
|
+
// src/lib/agent-config.ts
|
|
5938
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, existsSync as existsSync12, mkdirSync as mkdirSync5 } from "fs";
|
|
5915
5939
|
import path16 from "path";
|
|
5940
|
+
function loadAgentConfig() {
|
|
5941
|
+
if (!existsSync12(AGENT_CONFIG_PATH)) return {};
|
|
5942
|
+
try {
|
|
5943
|
+
return JSON.parse(readFileSync10(AGENT_CONFIG_PATH, "utf-8"));
|
|
5944
|
+
} catch {
|
|
5945
|
+
return {};
|
|
5946
|
+
}
|
|
5947
|
+
}
|
|
5948
|
+
function saveAgentConfig(config2) {
|
|
5949
|
+
const dir = path16.dirname(AGENT_CONFIG_PATH);
|
|
5950
|
+
if (!existsSync12(dir)) mkdirSync5(dir, { recursive: true });
|
|
5951
|
+
writeFileSync7(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
5952
|
+
}
|
|
5953
|
+
function getAgentRuntime(agentId) {
|
|
5954
|
+
const config2 = loadAgentConfig();
|
|
5955
|
+
const entry = config2[agentId];
|
|
5956
|
+
if (entry) return entry;
|
|
5957
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
5958
|
+
}
|
|
5959
|
+
function setAgentRuntime(agentId, runtime, model) {
|
|
5960
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
5961
|
+
if (!knownModels) {
|
|
5962
|
+
return {
|
|
5963
|
+
ok: false,
|
|
5964
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
5965
|
+
};
|
|
5966
|
+
}
|
|
5967
|
+
if (!knownModels.includes(model)) {
|
|
5968
|
+
return {
|
|
5969
|
+
ok: false,
|
|
5970
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
5971
|
+
};
|
|
5972
|
+
}
|
|
5973
|
+
const config2 = loadAgentConfig();
|
|
5974
|
+
config2[agentId] = { runtime, model };
|
|
5975
|
+
saveAgentConfig(config2);
|
|
5976
|
+
return { ok: true };
|
|
5977
|
+
}
|
|
5978
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, DEFAULT_MODELS;
|
|
5979
|
+
var init_agent_config = __esm({
|
|
5980
|
+
"src/lib/agent-config.ts"() {
|
|
5981
|
+
"use strict";
|
|
5982
|
+
init_config();
|
|
5983
|
+
init_runtime_table();
|
|
5984
|
+
AGENT_CONFIG_PATH = path16.join(EXE_AI_DIR, "agent-config.json");
|
|
5985
|
+
KNOWN_RUNTIMES = {
|
|
5986
|
+
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-3.5"],
|
|
5987
|
+
codex: ["gpt-5.4", "gpt-5.5", "o3", "o4-mini"],
|
|
5988
|
+
opencode: ["minimax-m2.7"]
|
|
5989
|
+
};
|
|
5990
|
+
DEFAULT_MODELS = {
|
|
5991
|
+
claude: "claude-opus-4",
|
|
5992
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
5993
|
+
opencode: "minimax-m2.7"
|
|
5994
|
+
};
|
|
5995
|
+
}
|
|
5996
|
+
});
|
|
5997
|
+
|
|
5998
|
+
// src/lib/intercom-queue.ts
|
|
5999
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, renameSync as renameSync3, existsSync as existsSync13, mkdirSync as mkdirSync6 } from "fs";
|
|
6000
|
+
import path17 from "path";
|
|
5916
6001
|
import os6 from "os";
|
|
5917
6002
|
function ensureDir() {
|
|
5918
|
-
const dir =
|
|
5919
|
-
if (!
|
|
6003
|
+
const dir = path17.dirname(QUEUE_PATH);
|
|
6004
|
+
if (!existsSync13(dir)) mkdirSync6(dir, { recursive: true });
|
|
5920
6005
|
}
|
|
5921
6006
|
function readQueue() {
|
|
5922
6007
|
try {
|
|
5923
|
-
if (!
|
|
5924
|
-
return JSON.parse(
|
|
6008
|
+
if (!existsSync13(QUEUE_PATH)) return [];
|
|
6009
|
+
return JSON.parse(readFileSync11(QUEUE_PATH, "utf8"));
|
|
5925
6010
|
} catch {
|
|
5926
6011
|
return [];
|
|
5927
6012
|
}
|
|
@@ -5929,7 +6014,7 @@ function readQueue() {
|
|
|
5929
6014
|
function writeQueue(queue) {
|
|
5930
6015
|
ensureDir();
|
|
5931
6016
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
5932
|
-
|
|
6017
|
+
writeFileSync8(tmp, JSON.stringify(queue, null, 2));
|
|
5933
6018
|
renameSync3(tmp, QUEUE_PATH);
|
|
5934
6019
|
}
|
|
5935
6020
|
function queueIntercom(targetSession, reason) {
|
|
@@ -5953,9 +6038,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
5953
6038
|
var init_intercom_queue = __esm({
|
|
5954
6039
|
"src/lib/intercom-queue.ts"() {
|
|
5955
6040
|
"use strict";
|
|
5956
|
-
QUEUE_PATH =
|
|
6041
|
+
QUEUE_PATH = path17.join(os6.homedir(), ".exe-os", "intercom-queue.json");
|
|
5957
6042
|
TTL_MS = 60 * 60 * 1e3;
|
|
5958
|
-
INTERCOM_LOG =
|
|
6043
|
+
INTERCOM_LOG = path17.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
5959
6044
|
}
|
|
5960
6045
|
});
|
|
5961
6046
|
|
|
@@ -6333,13 +6418,13 @@ __export(tmux_routing_exports, {
|
|
|
6333
6418
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
6334
6419
|
});
|
|
6335
6420
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
6336
|
-
import { readFileSync as
|
|
6337
|
-
import
|
|
6421
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync } from "fs";
|
|
6422
|
+
import path18 from "path";
|
|
6338
6423
|
import os7 from "os";
|
|
6339
6424
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6340
6425
|
import { unlinkSync as unlinkSync5 } from "fs";
|
|
6341
6426
|
function spawnLockPath(sessionName) {
|
|
6342
|
-
return
|
|
6427
|
+
return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
6343
6428
|
}
|
|
6344
6429
|
function isProcessAlive(pid) {
|
|
6345
6430
|
try {
|
|
@@ -6350,13 +6435,13 @@ function isProcessAlive(pid) {
|
|
|
6350
6435
|
}
|
|
6351
6436
|
}
|
|
6352
6437
|
function acquireSpawnLock2(sessionName) {
|
|
6353
|
-
if (!
|
|
6354
|
-
|
|
6438
|
+
if (!existsSync14(SPAWN_LOCK_DIR)) {
|
|
6439
|
+
mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
|
|
6355
6440
|
}
|
|
6356
6441
|
const lockFile = spawnLockPath(sessionName);
|
|
6357
|
-
if (
|
|
6442
|
+
if (existsSync14(lockFile)) {
|
|
6358
6443
|
try {
|
|
6359
|
-
const lock = JSON.parse(
|
|
6444
|
+
const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
|
|
6360
6445
|
const age = Date.now() - lock.timestamp;
|
|
6361
6446
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
6362
6447
|
return false;
|
|
@@ -6364,7 +6449,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
6364
6449
|
} catch {
|
|
6365
6450
|
}
|
|
6366
6451
|
}
|
|
6367
|
-
|
|
6452
|
+
writeFileSync9(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
6368
6453
|
return true;
|
|
6369
6454
|
}
|
|
6370
6455
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -6376,13 +6461,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
6376
6461
|
function resolveBehaviorsExporterScript() {
|
|
6377
6462
|
try {
|
|
6378
6463
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
6379
|
-
const scriptPath =
|
|
6380
|
-
|
|
6464
|
+
const scriptPath = path18.join(
|
|
6465
|
+
path18.dirname(thisFile),
|
|
6381
6466
|
"..",
|
|
6382
6467
|
"bin",
|
|
6383
6468
|
"exe-export-behaviors.js"
|
|
6384
6469
|
);
|
|
6385
|
-
return
|
|
6470
|
+
return existsSync14(scriptPath) ? scriptPath : null;
|
|
6386
6471
|
} catch {
|
|
6387
6472
|
return null;
|
|
6388
6473
|
}
|
|
@@ -6448,12 +6533,12 @@ function extractRootExe(name) {
|
|
|
6448
6533
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
6449
6534
|
}
|
|
6450
6535
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
6451
|
-
if (!
|
|
6452
|
-
|
|
6536
|
+
if (!existsSync14(SESSION_CACHE)) {
|
|
6537
|
+
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
6453
6538
|
}
|
|
6454
6539
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
6455
|
-
const filePath =
|
|
6456
|
-
|
|
6540
|
+
const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
6541
|
+
writeFileSync9(filePath, JSON.stringify({
|
|
6457
6542
|
parentExe: rootExe,
|
|
6458
6543
|
dispatchedBy: dispatchedBy || rootExe,
|
|
6459
6544
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -6461,7 +6546,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
6461
6546
|
}
|
|
6462
6547
|
function getParentExe(sessionKey) {
|
|
6463
6548
|
try {
|
|
6464
|
-
const data = JSON.parse(
|
|
6549
|
+
const data = JSON.parse(readFileSync12(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
6465
6550
|
return data.parentExe || null;
|
|
6466
6551
|
} catch {
|
|
6467
6552
|
return null;
|
|
@@ -6469,8 +6554,8 @@ function getParentExe(sessionKey) {
|
|
|
6469
6554
|
}
|
|
6470
6555
|
function getDispatchedBy(sessionKey) {
|
|
6471
6556
|
try {
|
|
6472
|
-
const data = JSON.parse(
|
|
6473
|
-
|
|
6557
|
+
const data = JSON.parse(readFileSync12(
|
|
6558
|
+
path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
6474
6559
|
"utf8"
|
|
6475
6560
|
));
|
|
6476
6561
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -6531,32 +6616,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
6531
6616
|
}
|
|
6532
6617
|
function readDebounceState() {
|
|
6533
6618
|
try {
|
|
6534
|
-
if (!
|
|
6535
|
-
|
|
6619
|
+
if (!existsSync14(DEBOUNCE_FILE)) return {};
|
|
6620
|
+
const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
|
|
6621
|
+
const state = {};
|
|
6622
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
6623
|
+
if (typeof val === "number") {
|
|
6624
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
6625
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
6626
|
+
state[key] = val;
|
|
6627
|
+
}
|
|
6628
|
+
}
|
|
6629
|
+
return state;
|
|
6536
6630
|
} catch {
|
|
6537
6631
|
return {};
|
|
6538
6632
|
}
|
|
6539
6633
|
}
|
|
6540
6634
|
function writeDebounceState(state) {
|
|
6541
6635
|
try {
|
|
6542
|
-
if (!
|
|
6543
|
-
|
|
6636
|
+
if (!existsSync14(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
6637
|
+
writeFileSync9(DEBOUNCE_FILE, JSON.stringify(state));
|
|
6544
6638
|
} catch {
|
|
6545
6639
|
}
|
|
6546
6640
|
}
|
|
6547
6641
|
function isDebounced(targetSession) {
|
|
6548
6642
|
const state = readDebounceState();
|
|
6549
|
-
const
|
|
6550
|
-
|
|
6643
|
+
const entry = state[targetSession];
|
|
6644
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
6645
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
6646
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
6647
|
+
state[targetSession].pending++;
|
|
6648
|
+
writeDebounceState(state);
|
|
6649
|
+
return true;
|
|
6650
|
+
}
|
|
6651
|
+
return false;
|
|
6551
6652
|
}
|
|
6552
6653
|
function recordDebounce(targetSession) {
|
|
6553
6654
|
const state = readDebounceState();
|
|
6554
|
-
state[targetSession]
|
|
6655
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
6656
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
6555
6657
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
6556
6658
|
for (const key of Object.keys(state)) {
|
|
6557
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
6659
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
6558
6660
|
}
|
|
6559
6661
|
writeDebounceState(state);
|
|
6662
|
+
return batched;
|
|
6560
6663
|
}
|
|
6561
6664
|
function logIntercom(msg) {
|
|
6562
6665
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -6601,7 +6704,7 @@ function sendIntercom(targetSession) {
|
|
|
6601
6704
|
return "skipped_exe";
|
|
6602
6705
|
}
|
|
6603
6706
|
if (isDebounced(targetSession)) {
|
|
6604
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
6707
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
6605
6708
|
return "debounced";
|
|
6606
6709
|
}
|
|
6607
6710
|
try {
|
|
@@ -6613,14 +6716,14 @@ function sendIntercom(targetSession) {
|
|
|
6613
6716
|
const sessionState = getSessionState(targetSession);
|
|
6614
6717
|
if (sessionState === "no_claude") {
|
|
6615
6718
|
queueIntercom(targetSession, "claude not running in session");
|
|
6616
|
-
recordDebounce(targetSession);
|
|
6617
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
6719
|
+
const batched2 = recordDebounce(targetSession);
|
|
6720
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
6618
6721
|
return "queued";
|
|
6619
6722
|
}
|
|
6620
6723
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
6621
6724
|
queueIntercom(targetSession, "session busy at send time");
|
|
6622
|
-
recordDebounce(targetSession);
|
|
6623
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
6725
|
+
const batched2 = recordDebounce(targetSession);
|
|
6726
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
6624
6727
|
return "queued";
|
|
6625
6728
|
}
|
|
6626
6729
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -6628,8 +6731,8 @@ function sendIntercom(targetSession) {
|
|
|
6628
6731
|
transport.sendKeys(targetSession, "q");
|
|
6629
6732
|
}
|
|
6630
6733
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
6631
|
-
recordDebounce(targetSession);
|
|
6632
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
6734
|
+
const batched = recordDebounce(targetSession);
|
|
6735
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
6633
6736
|
return "delivered";
|
|
6634
6737
|
} catch {
|
|
6635
6738
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -6731,26 +6834,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6731
6834
|
const transport = getTransport();
|
|
6732
6835
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
6733
6836
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
6734
|
-
const logDir =
|
|
6735
|
-
const logFile =
|
|
6736
|
-
if (!
|
|
6737
|
-
|
|
6837
|
+
const logDir = path18.join(os7.homedir(), ".exe-os", "session-logs");
|
|
6838
|
+
const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
6839
|
+
if (!existsSync14(logDir)) {
|
|
6840
|
+
mkdirSync7(logDir, { recursive: true });
|
|
6738
6841
|
}
|
|
6739
6842
|
transport.kill(sessionName);
|
|
6740
6843
|
let cleanupSuffix = "";
|
|
6741
6844
|
try {
|
|
6742
6845
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
6743
|
-
const cleanupScript =
|
|
6744
|
-
if (
|
|
6846
|
+
const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
6847
|
+
if (existsSync14(cleanupScript)) {
|
|
6745
6848
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
6746
6849
|
}
|
|
6747
6850
|
} catch {
|
|
6748
6851
|
}
|
|
6749
6852
|
try {
|
|
6750
|
-
const claudeJsonPath =
|
|
6853
|
+
const claudeJsonPath = path18.join(os7.homedir(), ".claude.json");
|
|
6751
6854
|
let claudeJson = {};
|
|
6752
6855
|
try {
|
|
6753
|
-
claudeJson = JSON.parse(
|
|
6856
|
+
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
6754
6857
|
} catch {
|
|
6755
6858
|
}
|
|
6756
6859
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -6758,17 +6861,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6758
6861
|
const trustDir = opts?.cwd ?? projectDir;
|
|
6759
6862
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
6760
6863
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
6761
|
-
|
|
6864
|
+
writeFileSync9(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
6762
6865
|
} catch {
|
|
6763
6866
|
}
|
|
6764
6867
|
try {
|
|
6765
|
-
const settingsDir =
|
|
6868
|
+
const settingsDir = path18.join(os7.homedir(), ".claude", "projects");
|
|
6766
6869
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
6767
|
-
const projSettingsDir =
|
|
6768
|
-
const settingsPath =
|
|
6870
|
+
const projSettingsDir = path18.join(settingsDir, normalizedKey);
|
|
6871
|
+
const settingsPath = path18.join(projSettingsDir, "settings.json");
|
|
6769
6872
|
let settings = {};
|
|
6770
6873
|
try {
|
|
6771
|
-
settings = JSON.parse(
|
|
6874
|
+
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
6772
6875
|
} catch {
|
|
6773
6876
|
}
|
|
6774
6877
|
const perms = settings.permissions ?? {};
|
|
@@ -6796,20 +6899,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6796
6899
|
if (changed) {
|
|
6797
6900
|
perms.allow = allow;
|
|
6798
6901
|
settings.permissions = perms;
|
|
6799
|
-
|
|
6800
|
-
|
|
6902
|
+
mkdirSync7(projSettingsDir, { recursive: true });
|
|
6903
|
+
writeFileSync9(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
6801
6904
|
}
|
|
6802
6905
|
} catch {
|
|
6803
6906
|
}
|
|
6804
6907
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
6805
6908
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
6806
|
-
const
|
|
6909
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
6910
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
6911
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
6912
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
6807
6913
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
6808
6914
|
let identityFlag = "";
|
|
6809
6915
|
let behaviorsFlag = "";
|
|
6810
6916
|
let legacyFallbackWarned = false;
|
|
6811
6917
|
if (!useExeAgent && !useBinSymlink) {
|
|
6812
|
-
const identityPath2 =
|
|
6918
|
+
const identityPath2 = path18.join(
|
|
6813
6919
|
os7.homedir(),
|
|
6814
6920
|
".exe-os",
|
|
6815
6921
|
"identity",
|
|
@@ -6819,13 +6925,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6819
6925
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
6820
6926
|
if (hasAgentFlag) {
|
|
6821
6927
|
identityFlag = ` --agent ${employeeName}`;
|
|
6822
|
-
} else if (
|
|
6928
|
+
} else if (existsSync14(identityPath2)) {
|
|
6823
6929
|
identityFlag = ` --append-system-prompt-file ${identityPath2}`;
|
|
6824
6930
|
legacyFallbackWarned = true;
|
|
6825
6931
|
}
|
|
6826
6932
|
const behaviorsFile = exportBehaviorsSync(
|
|
6827
6933
|
employeeName,
|
|
6828
|
-
|
|
6934
|
+
path18.basename(spawnCwd),
|
|
6829
6935
|
sessionName
|
|
6830
6936
|
);
|
|
6831
6937
|
if (behaviorsFile) {
|
|
@@ -6840,16 +6946,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6840
6946
|
}
|
|
6841
6947
|
let sessionContextFlag = "";
|
|
6842
6948
|
try {
|
|
6843
|
-
const ctxDir =
|
|
6844
|
-
|
|
6845
|
-
const ctxFile =
|
|
6949
|
+
const ctxDir = path18.join(os7.homedir(), ".exe-os", "session-cache");
|
|
6950
|
+
mkdirSync7(ctxDir, { recursive: true });
|
|
6951
|
+
const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
|
|
6846
6952
|
const ctxContent = [
|
|
6847
6953
|
`## Session Context`,
|
|
6848
6954
|
`You are running in tmux session: ${sessionName}.`,
|
|
6849
6955
|
`Your parent coordinator session is ${exeSession}.`,
|
|
6850
6956
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
6851
6957
|
].join("\n");
|
|
6852
|
-
|
|
6958
|
+
writeFileSync9(ctxFile, ctxContent);
|
|
6853
6959
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
6854
6960
|
} catch {
|
|
6855
6961
|
}
|
|
@@ -6863,9 +6969,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6863
6969
|
}
|
|
6864
6970
|
}
|
|
6865
6971
|
}
|
|
6972
|
+
if (useCodex) {
|
|
6973
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
6974
|
+
if (codexCfg?.apiKeyEnv) {
|
|
6975
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
6976
|
+
if (keyVal) {
|
|
6977
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
6978
|
+
}
|
|
6979
|
+
}
|
|
6980
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
6981
|
+
}
|
|
6982
|
+
if (useOpencode) {
|
|
6983
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
6984
|
+
if (ocCfg?.apiKeyEnv) {
|
|
6985
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
6986
|
+
if (keyVal) {
|
|
6987
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
6988
|
+
}
|
|
6989
|
+
}
|
|
6990
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
6991
|
+
}
|
|
6992
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
6993
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
6994
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
6995
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
6996
|
+
}
|
|
6997
|
+
}
|
|
6866
6998
|
let spawnCommand;
|
|
6867
6999
|
if (useExeAgent) {
|
|
6868
7000
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
7001
|
+
} else if (useCodex) {
|
|
7002
|
+
process.stderr.write(
|
|
7003
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
7004
|
+
`
|
|
7005
|
+
);
|
|
7006
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
7007
|
+
} else if (useOpencode) {
|
|
7008
|
+
const binName = `${employeeName}-opencode`;
|
|
7009
|
+
process.stderr.write(
|
|
7010
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
7011
|
+
`
|
|
7012
|
+
);
|
|
7013
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
6869
7014
|
} else if (useBinSymlink) {
|
|
6870
7015
|
const binName = `${employeeName}-${ccProvider}`;
|
|
6871
7016
|
process.stderr.write(
|
|
@@ -6887,11 +7032,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6887
7032
|
transport.pipeLog(sessionName, logFile);
|
|
6888
7033
|
try {
|
|
6889
7034
|
const mySession = getMySession();
|
|
6890
|
-
const dispatchInfo =
|
|
6891
|
-
|
|
7035
|
+
const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
7036
|
+
writeFileSync9(dispatchInfo, JSON.stringify({
|
|
6892
7037
|
dispatchedBy: mySession,
|
|
6893
7038
|
rootExe: exeSession,
|
|
6894
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
7039
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
7040
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
7041
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
6895
7042
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6896
7043
|
}));
|
|
6897
7044
|
} catch {
|
|
@@ -6909,6 +7056,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6909
7056
|
booted = true;
|
|
6910
7057
|
break;
|
|
6911
7058
|
}
|
|
7059
|
+
} else if (useCodex) {
|
|
7060
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
7061
|
+
booted = true;
|
|
7062
|
+
break;
|
|
7063
|
+
}
|
|
6912
7064
|
} else {
|
|
6913
7065
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
6914
7066
|
booted = true;
|
|
@@ -6920,9 +7072,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6920
7072
|
}
|
|
6921
7073
|
if (!booted) {
|
|
6922
7074
|
releaseSpawnLock2(sessionName);
|
|
6923
|
-
|
|
7075
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
7076
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
6924
7077
|
}
|
|
6925
|
-
if (!useExeAgent) {
|
|
7078
|
+
if (!useExeAgent && !useCodex) {
|
|
6926
7079
|
try {
|
|
6927
7080
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
6928
7081
|
} catch {
|
|
@@ -6949,17 +7102,19 @@ var init_tmux_routing = __esm({
|
|
|
6949
7102
|
init_cc_agent_support();
|
|
6950
7103
|
init_mcp_prefix();
|
|
6951
7104
|
init_provider_table();
|
|
7105
|
+
init_agent_config();
|
|
7106
|
+
init_runtime_table();
|
|
6952
7107
|
init_intercom_queue();
|
|
6953
7108
|
init_plan_limits();
|
|
6954
7109
|
init_employees();
|
|
6955
|
-
SPAWN_LOCK_DIR =
|
|
6956
|
-
SESSION_CACHE =
|
|
7110
|
+
SPAWN_LOCK_DIR = path18.join(os7.homedir(), ".exe-os", "spawn-locks");
|
|
7111
|
+
SESSION_CACHE = path18.join(os7.homedir(), ".exe-os", "session-cache");
|
|
6957
7112
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
6958
7113
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
6959
7114
|
VERIFY_PANE_LINES = 200;
|
|
6960
7115
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
6961
|
-
INTERCOM_LOG2 =
|
|
6962
|
-
DEBOUNCE_FILE =
|
|
7116
|
+
INTERCOM_LOG2 = path18.join(os7.homedir(), ".exe-os", "intercom.log");
|
|
7117
|
+
DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
|
|
6963
7118
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
6964
7119
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
6965
7120
|
}
|
|
@@ -7007,11 +7162,11 @@ __export(tasks_crud_exports, {
|
|
|
7007
7162
|
writeCheckpoint: () => writeCheckpoint
|
|
7008
7163
|
});
|
|
7009
7164
|
import crypto6 from "crypto";
|
|
7010
|
-
import
|
|
7165
|
+
import path19 from "path";
|
|
7011
7166
|
import os8 from "os";
|
|
7012
7167
|
import { execSync as execSync8 } from "child_process";
|
|
7013
7168
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
7014
|
-
import { existsSync as
|
|
7169
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
7015
7170
|
async function writeCheckpoint(input) {
|
|
7016
7171
|
const client = getClient();
|
|
7017
7172
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -7186,8 +7341,8 @@ ${laneWarning}` : laneWarning;
|
|
|
7186
7341
|
}
|
|
7187
7342
|
if (input.baseDir) {
|
|
7188
7343
|
try {
|
|
7189
|
-
await mkdir4(
|
|
7190
|
-
await mkdir4(
|
|
7344
|
+
await mkdir4(path19.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
7345
|
+
await mkdir4(path19.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
7191
7346
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
7192
7347
|
await ensureGitignoreExe(input.baseDir);
|
|
7193
7348
|
} catch {
|
|
@@ -7223,10 +7378,10 @@ ${laneWarning}` : laneWarning;
|
|
|
7223
7378
|
});
|
|
7224
7379
|
if (input.baseDir) {
|
|
7225
7380
|
try {
|
|
7226
|
-
const EXE_OS_DIR =
|
|
7227
|
-
const mdPath =
|
|
7228
|
-
const mdDir =
|
|
7229
|
-
if (!
|
|
7381
|
+
const EXE_OS_DIR = path19.join(os8.homedir(), ".exe-os");
|
|
7382
|
+
const mdPath = path19.join(EXE_OS_DIR, taskFile);
|
|
7383
|
+
const mdDir = path19.dirname(mdPath);
|
|
7384
|
+
if (!existsSync15(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
7230
7385
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
7231
7386
|
const mdContent = `# ${input.title}
|
|
7232
7387
|
|
|
@@ -7251,7 +7406,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
|
|
|
7251
7406
|
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
7252
7407
|
`;
|
|
7253
7408
|
await writeFile4(mdPath, mdContent, "utf-8");
|
|
7254
|
-
} catch {
|
|
7409
|
+
} catch (err) {
|
|
7410
|
+
process.stderr.write(
|
|
7411
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
7412
|
+
`
|
|
7413
|
+
);
|
|
7255
7414
|
}
|
|
7256
7415
|
}
|
|
7257
7416
|
return {
|
|
@@ -7511,9 +7670,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
7511
7670
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
7512
7671
|
}
|
|
7513
7672
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
7514
|
-
const archPath =
|
|
7673
|
+
const archPath = path19.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
7515
7674
|
try {
|
|
7516
|
-
if (
|
|
7675
|
+
if (existsSync15(archPath)) return;
|
|
7517
7676
|
const template = [
|
|
7518
7677
|
`# ${projectName} \u2014 System Architecture`,
|
|
7519
7678
|
"",
|
|
@@ -7546,10 +7705,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
7546
7705
|
}
|
|
7547
7706
|
}
|
|
7548
7707
|
async function ensureGitignoreExe(baseDir) {
|
|
7549
|
-
const gitignorePath =
|
|
7708
|
+
const gitignorePath = path19.join(baseDir, ".gitignore");
|
|
7550
7709
|
try {
|
|
7551
|
-
if (
|
|
7552
|
-
const content =
|
|
7710
|
+
if (existsSync15(gitignorePath)) {
|
|
7711
|
+
const content = readFileSync13(gitignorePath, "utf-8");
|
|
7553
7712
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
7554
7713
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
7555
7714
|
} else {
|
|
@@ -7580,8 +7739,8 @@ var init_tasks_crud = __esm({
|
|
|
7580
7739
|
});
|
|
7581
7740
|
|
|
7582
7741
|
// src/lib/tasks-review.ts
|
|
7583
|
-
import
|
|
7584
|
-
import { existsSync as
|
|
7742
|
+
import path20 from "path";
|
|
7743
|
+
import { existsSync as existsSync16, readdirSync as readdirSync5, unlinkSync as unlinkSync6 } from "fs";
|
|
7585
7744
|
async function countPendingReviews(sessionScope) {
|
|
7586
7745
|
const client = getClient();
|
|
7587
7746
|
if (sessionScope) {
|
|
@@ -7762,11 +7921,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
7762
7921
|
);
|
|
7763
7922
|
}
|
|
7764
7923
|
try {
|
|
7765
|
-
const cacheDir =
|
|
7766
|
-
if (
|
|
7924
|
+
const cacheDir = path20.join(EXE_AI_DIR, "session-cache");
|
|
7925
|
+
if (existsSync16(cacheDir)) {
|
|
7767
7926
|
for (const f of readdirSync5(cacheDir)) {
|
|
7768
7927
|
if (f.startsWith("review-notified-")) {
|
|
7769
|
-
unlinkSync6(
|
|
7928
|
+
unlinkSync6(path20.join(cacheDir, f));
|
|
7770
7929
|
}
|
|
7771
7930
|
}
|
|
7772
7931
|
}
|
|
@@ -7787,7 +7946,7 @@ var init_tasks_review = __esm({
|
|
|
7787
7946
|
});
|
|
7788
7947
|
|
|
7789
7948
|
// src/lib/tasks-chain.ts
|
|
7790
|
-
import
|
|
7949
|
+
import path21 from "path";
|
|
7791
7950
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
7792
7951
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
7793
7952
|
const client = getClient();
|
|
@@ -7804,7 +7963,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
7804
7963
|
});
|
|
7805
7964
|
for (const ur of unblockedRows.rows) {
|
|
7806
7965
|
try {
|
|
7807
|
-
const ubFile =
|
|
7966
|
+
const ubFile = path21.join(baseDir, String(ur.task_file));
|
|
7808
7967
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
7809
7968
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
7810
7969
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -8358,8 +8517,8 @@ __export(tasks_exports, {
|
|
|
8358
8517
|
updateTaskStatus: () => updateTaskStatus,
|
|
8359
8518
|
writeCheckpoint: () => writeCheckpoint
|
|
8360
8519
|
});
|
|
8361
|
-
import
|
|
8362
|
-
import { writeFileSync as
|
|
8520
|
+
import path22 from "path";
|
|
8521
|
+
import { writeFileSync as writeFileSync10, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
|
|
8363
8522
|
async function createTask(input) {
|
|
8364
8523
|
const result = await createTaskCore(input);
|
|
8365
8524
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -8378,11 +8537,11 @@ async function updateTask(input) {
|
|
|
8378
8537
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
8379
8538
|
try {
|
|
8380
8539
|
const agent = String(row.assigned_to);
|
|
8381
|
-
const cacheDir =
|
|
8382
|
-
const cachePath =
|
|
8540
|
+
const cacheDir = path22.join(EXE_AI_DIR, "session-cache");
|
|
8541
|
+
const cachePath = path22.join(cacheDir, `current-task-${agent}.json`);
|
|
8383
8542
|
if (input.status === "in_progress") {
|
|
8384
|
-
|
|
8385
|
-
|
|
8543
|
+
mkdirSync8(cacheDir, { recursive: true });
|
|
8544
|
+
writeFileSync10(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
8386
8545
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
8387
8546
|
try {
|
|
8388
8547
|
unlinkSync7(cachePath);
|
|
@@ -8548,17 +8707,17 @@ __export(identity_exports, {
|
|
|
8548
8707
|
listIdentities: () => listIdentities,
|
|
8549
8708
|
updateIdentity: () => updateIdentity
|
|
8550
8709
|
});
|
|
8551
|
-
import { existsSync as
|
|
8710
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
|
|
8552
8711
|
import { readdirSync as readdirSync6 } from "fs";
|
|
8553
|
-
import
|
|
8712
|
+
import path23 from "path";
|
|
8554
8713
|
import { createHash as createHash2 } from "crypto";
|
|
8555
8714
|
function ensureDir2() {
|
|
8556
|
-
if (!
|
|
8557
|
-
|
|
8715
|
+
if (!existsSync17(IDENTITY_DIR)) {
|
|
8716
|
+
mkdirSync9(IDENTITY_DIR, { recursive: true });
|
|
8558
8717
|
}
|
|
8559
8718
|
}
|
|
8560
8719
|
function identityPath(agentId) {
|
|
8561
|
-
return
|
|
8720
|
+
return path23.join(IDENTITY_DIR, `${agentId}.md`);
|
|
8562
8721
|
}
|
|
8563
8722
|
function parseFrontmatter(raw) {
|
|
8564
8723
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -8599,8 +8758,8 @@ function contentHash(content) {
|
|
|
8599
8758
|
}
|
|
8600
8759
|
function getIdentity(agentId) {
|
|
8601
8760
|
const filePath = identityPath(agentId);
|
|
8602
|
-
if (!
|
|
8603
|
-
const raw =
|
|
8761
|
+
if (!existsSync17(filePath)) return null;
|
|
8762
|
+
const raw = readFileSync14(filePath, "utf-8");
|
|
8604
8763
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
8605
8764
|
return {
|
|
8606
8765
|
agentId,
|
|
@@ -8614,7 +8773,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
8614
8773
|
ensureDir2();
|
|
8615
8774
|
const filePath = identityPath(agentId);
|
|
8616
8775
|
const hash = contentHash(content);
|
|
8617
|
-
|
|
8776
|
+
writeFileSync11(filePath, content, "utf-8");
|
|
8618
8777
|
try {
|
|
8619
8778
|
const client = getClient();
|
|
8620
8779
|
await client.execute({
|
|
@@ -8670,7 +8829,7 @@ var init_identity = __esm({
|
|
|
8670
8829
|
"use strict";
|
|
8671
8830
|
init_config();
|
|
8672
8831
|
init_database();
|
|
8673
|
-
IDENTITY_DIR =
|
|
8832
|
+
IDENTITY_DIR = path23.join(EXE_AI_DIR, "identity");
|
|
8674
8833
|
}
|
|
8675
8834
|
});
|
|
8676
8835
|
|
|
@@ -9353,13 +9512,13 @@ var init_messaging = __esm({
|
|
|
9353
9512
|
});
|
|
9354
9513
|
|
|
9355
9514
|
// src/gateway/whatsapp-accounts.ts
|
|
9356
|
-
import { readFileSync as
|
|
9515
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
9357
9516
|
import { join } from "path";
|
|
9358
9517
|
import { homedir } from "os";
|
|
9359
9518
|
function loadAccounts() {
|
|
9360
9519
|
if (cachedAccounts !== null) return cachedAccounts;
|
|
9361
9520
|
try {
|
|
9362
|
-
const raw =
|
|
9521
|
+
const raw = readFileSync15(CONFIG_PATH2, "utf8");
|
|
9363
9522
|
const parsed = JSON.parse(raw);
|
|
9364
9523
|
if (!Array.isArray(parsed)) {
|
|
9365
9524
|
console.warn("[whatsapp] Config is not an array, ignoring");
|
|
@@ -9772,8 +9931,8 @@ __export(wiki_client_exports, {
|
|
|
9772
9931
|
listDocuments: () => listDocuments,
|
|
9773
9932
|
listWorkspaces: () => listWorkspaces
|
|
9774
9933
|
});
|
|
9775
|
-
async function wikiFetch(config2,
|
|
9776
|
-
const url = `${config2.baseUrl}/api/v1${
|
|
9934
|
+
async function wikiFetch(config2, path38, method = "GET", body) {
|
|
9935
|
+
const url = `${config2.baseUrl}/api/v1${path38}`;
|
|
9777
9936
|
const headers = {
|
|
9778
9937
|
Authorization: `Bearer ${config2.apiKey}`,
|
|
9779
9938
|
"Content-Type": "application/json"
|
|
@@ -9806,7 +9965,7 @@ async function wikiFetch(config2, path37, method = "GET", body) {
|
|
|
9806
9965
|
}
|
|
9807
9966
|
}
|
|
9808
9967
|
if (!response.ok) {
|
|
9809
|
-
throw new Error(`Wiki API ${method} ${
|
|
9968
|
+
throw new Error(`Wiki API ${method} ${path38}: ${response.status} ${response.statusText}`);
|
|
9810
9969
|
}
|
|
9811
9970
|
return response.json();
|
|
9812
9971
|
} finally {
|
|
@@ -9915,14 +10074,14 @@ __export(worker_gate_exports, {
|
|
|
9915
10074
|
tryAcquireBackfillLock: () => tryAcquireBackfillLock,
|
|
9916
10075
|
tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
|
|
9917
10076
|
});
|
|
9918
|
-
import { readdirSync as readdirSync9, writeFileSync as
|
|
9919
|
-
import
|
|
10077
|
+
import { readdirSync as readdirSync9, writeFileSync as writeFileSync15, unlinkSync as unlinkSync8, mkdirSync as mkdirSync12, existsSync as existsSync24 } from "fs";
|
|
10078
|
+
import path31 from "path";
|
|
9920
10079
|
function tryAcquireWorkerSlot() {
|
|
9921
10080
|
try {
|
|
9922
|
-
|
|
10081
|
+
mkdirSync12(WORKER_PID_DIR, { recursive: true });
|
|
9923
10082
|
const reservationId = `res-${process.pid}-${Date.now()}`;
|
|
9924
|
-
const reservationPath =
|
|
9925
|
-
|
|
10083
|
+
const reservationPath = path31.join(WORKER_PID_DIR, `${reservationId}.pid`);
|
|
10084
|
+
writeFileSync15(reservationPath, String(process.pid));
|
|
9926
10085
|
const files = readdirSync9(WORKER_PID_DIR);
|
|
9927
10086
|
let alive = 0;
|
|
9928
10087
|
for (const f of files) {
|
|
@@ -9939,7 +10098,7 @@ function tryAcquireWorkerSlot() {
|
|
|
9939
10098
|
alive++;
|
|
9940
10099
|
} catch {
|
|
9941
10100
|
try {
|
|
9942
|
-
unlinkSync8(
|
|
10101
|
+
unlinkSync8(path31.join(WORKER_PID_DIR, f));
|
|
9943
10102
|
} catch {
|
|
9944
10103
|
}
|
|
9945
10104
|
}
|
|
@@ -9962,21 +10121,21 @@ function tryAcquireWorkerSlot() {
|
|
|
9962
10121
|
}
|
|
9963
10122
|
function registerWorkerPid(pid) {
|
|
9964
10123
|
try {
|
|
9965
|
-
|
|
9966
|
-
|
|
10124
|
+
mkdirSync12(WORKER_PID_DIR, { recursive: true });
|
|
10125
|
+
writeFileSync15(path31.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
|
|
9967
10126
|
} catch {
|
|
9968
10127
|
}
|
|
9969
10128
|
}
|
|
9970
10129
|
function cleanupWorkerPid() {
|
|
9971
10130
|
try {
|
|
9972
|
-
unlinkSync8(
|
|
10131
|
+
unlinkSync8(path31.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
|
|
9973
10132
|
} catch {
|
|
9974
10133
|
}
|
|
9975
10134
|
}
|
|
9976
10135
|
function tryAcquireBackfillLock() {
|
|
9977
10136
|
try {
|
|
9978
|
-
|
|
9979
|
-
if (
|
|
10137
|
+
mkdirSync12(WORKER_PID_DIR, { recursive: true });
|
|
10138
|
+
if (existsSync24(BACKFILL_LOCK)) {
|
|
9980
10139
|
try {
|
|
9981
10140
|
const pid = parseInt(
|
|
9982
10141
|
__require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
|
|
@@ -9992,7 +10151,7 @@ function tryAcquireBackfillLock() {
|
|
|
9992
10151
|
} catch {
|
|
9993
10152
|
}
|
|
9994
10153
|
}
|
|
9995
|
-
|
|
10154
|
+
writeFileSync15(BACKFILL_LOCK, String(process.pid));
|
|
9996
10155
|
return true;
|
|
9997
10156
|
} catch {
|
|
9998
10157
|
return true;
|
|
@@ -10009,9 +10168,9 @@ var init_worker_gate = __esm({
|
|
|
10009
10168
|
"src/lib/worker-gate.ts"() {
|
|
10010
10169
|
"use strict";
|
|
10011
10170
|
init_config();
|
|
10012
|
-
WORKER_PID_DIR =
|
|
10171
|
+
WORKER_PID_DIR = path31.join(EXE_AI_DIR, "worker-pids");
|
|
10013
10172
|
MAX_CONCURRENT_WORKERS = 3;
|
|
10014
|
-
BACKFILL_LOCK =
|
|
10173
|
+
BACKFILL_LOCK = path31.join(WORKER_PID_DIR, "backfill.lock");
|
|
10015
10174
|
}
|
|
10016
10175
|
});
|
|
10017
10176
|
|
|
@@ -10034,8 +10193,8 @@ __export(crdt_sync_exports, {
|
|
|
10034
10193
|
rebuildFromDb: () => rebuildFromDb
|
|
10035
10194
|
});
|
|
10036
10195
|
import * as Y from "yjs";
|
|
10037
|
-
import { readFileSync as
|
|
10038
|
-
import
|
|
10196
|
+
import { readFileSync as readFileSync21, writeFileSync as writeFileSync16, existsSync as existsSync27, mkdirSync as mkdirSync13, unlinkSync as unlinkSync9 } from "fs";
|
|
10197
|
+
import path34 from "path";
|
|
10039
10198
|
import { homedir as homedir5 } from "os";
|
|
10040
10199
|
function getStatePath() {
|
|
10041
10200
|
return _statePathOverride ?? DEFAULT_STATE_PATH;
|
|
@@ -10047,9 +10206,9 @@ function initCrdtDoc() {
|
|
|
10047
10206
|
if (doc) return doc;
|
|
10048
10207
|
doc = new Y.Doc();
|
|
10049
10208
|
const sp = getStatePath();
|
|
10050
|
-
if (
|
|
10209
|
+
if (existsSync27(sp)) {
|
|
10051
10210
|
try {
|
|
10052
|
-
const state =
|
|
10211
|
+
const state = readFileSync21(sp);
|
|
10053
10212
|
Y.applyUpdate(doc, new Uint8Array(state));
|
|
10054
10213
|
} catch {
|
|
10055
10214
|
console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
|
|
@@ -10191,10 +10350,10 @@ function persistState() {
|
|
|
10191
10350
|
if (!doc) return;
|
|
10192
10351
|
try {
|
|
10193
10352
|
const sp = getStatePath();
|
|
10194
|
-
const dir =
|
|
10195
|
-
if (!
|
|
10353
|
+
const dir = path34.dirname(sp);
|
|
10354
|
+
if (!existsSync27(dir)) mkdirSync13(dir, { recursive: true });
|
|
10196
10355
|
const state = Y.encodeStateAsUpdate(doc);
|
|
10197
|
-
|
|
10356
|
+
writeFileSync16(sp, Buffer.from(state));
|
|
10198
10357
|
} catch {
|
|
10199
10358
|
}
|
|
10200
10359
|
}
|
|
@@ -10235,7 +10394,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
|
|
|
10235
10394
|
var init_crdt_sync = __esm({
|
|
10236
10395
|
"src/lib/crdt-sync.ts"() {
|
|
10237
10396
|
"use strict";
|
|
10238
|
-
DEFAULT_STATE_PATH =
|
|
10397
|
+
DEFAULT_STATE_PATH = path34.join(homedir5(), ".exe-os", "crdt-state.bin");
|
|
10239
10398
|
_statePathOverride = null;
|
|
10240
10399
|
doc = null;
|
|
10241
10400
|
}
|
|
@@ -10248,12 +10407,13 @@ init_database();
|
|
|
10248
10407
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10249
10408
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10250
10409
|
import { spawn as spawn3 } from "child_process";
|
|
10251
|
-
import { existsSync as
|
|
10252
|
-
import
|
|
10410
|
+
import { existsSync as existsSync30, openSync as openSync3, mkdirSync as mkdirSync15, closeSync as closeSync3 } from "fs";
|
|
10411
|
+
import path37 from "path";
|
|
10253
10412
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
10254
10413
|
|
|
10255
10414
|
// src/mcp/tools/recall-my-memory.ts
|
|
10256
10415
|
init_hybrid_search();
|
|
10416
|
+
init_store();
|
|
10257
10417
|
init_active_agent();
|
|
10258
10418
|
import { z } from "zod";
|
|
10259
10419
|
function formatSourceLine(record) {
|
|
@@ -10273,7 +10433,10 @@ function registerRecallMyMemory(server2) {
|
|
|
10273
10433
|
title: "Recall My Memory",
|
|
10274
10434
|
description: "Search your past work memories using semantic search. Returns relevant past tool calls, outputs, and decisions.",
|
|
10275
10435
|
inputSchema: {
|
|
10276
|
-
query: z.string().describe("What to search for in your memories"),
|
|
10436
|
+
query: z.string().optional().describe("What to search for in your memories"),
|
|
10437
|
+
recent: z.boolean().optional().default(false).describe(
|
|
10438
|
+
"When true, return most recent memories sorted by time (no semantic search). Use for session recovery after crashes."
|
|
10439
|
+
),
|
|
10277
10440
|
project_name: z.string().optional().describe("Filter by project name"),
|
|
10278
10441
|
has_error: z.boolean().optional().describe("Filter for error-containing memories"),
|
|
10279
10442
|
tool_name: z.string().optional().describe("Filter by tool name (Bash, Write, etc)"),
|
|
@@ -10293,6 +10456,7 @@ function registerRecallMyMemory(server2) {
|
|
|
10293
10456
|
},
|
|
10294
10457
|
async ({
|
|
10295
10458
|
query,
|
|
10459
|
+
recent,
|
|
10296
10460
|
project_name,
|
|
10297
10461
|
has_error,
|
|
10298
10462
|
tool_name,
|
|
@@ -10304,6 +10468,12 @@ function registerRecallMyMemory(server2) {
|
|
|
10304
10468
|
include_source
|
|
10305
10469
|
}) => {
|
|
10306
10470
|
try {
|
|
10471
|
+
if (!recent && !query) {
|
|
10472
|
+
return {
|
|
10473
|
+
content: [{ type: "text", text: "query is required when recent mode is not enabled." }],
|
|
10474
|
+
isError: true
|
|
10475
|
+
};
|
|
10476
|
+
}
|
|
10307
10477
|
const { agentId } = getActiveAgent();
|
|
10308
10478
|
const searchOptions = {
|
|
10309
10479
|
projectName: project_name,
|
|
@@ -10317,7 +10487,15 @@ function registerRecallMyMemory(server2) {
|
|
|
10317
10487
|
includeDrafts: true,
|
|
10318
10488
|
...user_id !== void 0 ? { userId: user_id } : {}
|
|
10319
10489
|
};
|
|
10320
|
-
|
|
10490
|
+
let results;
|
|
10491
|
+
if (recent) {
|
|
10492
|
+
results = await recentRecords(agentId, searchOptions, limit, query);
|
|
10493
|
+
if (include_source && results.length > 0) {
|
|
10494
|
+
await attachDocumentMetadata(results);
|
|
10495
|
+
}
|
|
10496
|
+
} else {
|
|
10497
|
+
results = await hybridSearch(query, agentId, searchOptions);
|
|
10498
|
+
}
|
|
10321
10499
|
if (results.length === 0) {
|
|
10322
10500
|
return {
|
|
10323
10501
|
content: [
|
|
@@ -10900,10 +11078,10 @@ function registerCreateTask(server2) {
|
|
|
10900
11078
|
skipDispatch: true
|
|
10901
11079
|
});
|
|
10902
11080
|
try {
|
|
10903
|
-
const { existsSync:
|
|
11081
|
+
const { existsSync: existsSync31, mkdirSync: mkdirSync16, writeFileSync: writeFileSync18 } = await import("fs");
|
|
10904
11082
|
const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
10905
11083
|
const idPath = identityPath2(assigned_to);
|
|
10906
|
-
if (!
|
|
11084
|
+
if (!existsSync31(idPath)) {
|
|
10907
11085
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
10908
11086
|
const employees = await loadEmployees2();
|
|
10909
11087
|
const emp = employees.find((e) => e.name === assigned_to);
|
|
@@ -10912,8 +11090,8 @@ function registerCreateTask(server2) {
|
|
|
10912
11090
|
const template = getTemplateForTitle2(emp.role);
|
|
10913
11091
|
if (template) {
|
|
10914
11092
|
const dir = (await import("path")).dirname(idPath);
|
|
10915
|
-
if (!
|
|
10916
|
-
|
|
11093
|
+
if (!existsSync31(dir)) mkdirSync16(dir, { recursive: true });
|
|
11094
|
+
writeFileSync18(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
|
|
10917
11095
|
}
|
|
10918
11096
|
}
|
|
10919
11097
|
}
|
|
@@ -12745,15 +12923,15 @@ function registerSendWhatsapp(server2) {
|
|
|
12745
12923
|
import { z as z29 } from "zod";
|
|
12746
12924
|
|
|
12747
12925
|
// src/automation/trigger-engine.ts
|
|
12748
|
-
import { readFileSync as
|
|
12926
|
+
import { readFileSync as readFileSync16, writeFileSync as writeFileSync12, existsSync as existsSync18, mkdirSync as mkdirSync10 } from "fs";
|
|
12749
12927
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
12750
|
-
import
|
|
12928
|
+
import path24 from "path";
|
|
12751
12929
|
import os9 from "os";
|
|
12752
|
-
var TRIGGERS_PATH =
|
|
12930
|
+
var TRIGGERS_PATH = path24.join(os9.homedir(), ".exe-os", "triggers.json");
|
|
12753
12931
|
function loadTriggers(project) {
|
|
12754
|
-
if (!
|
|
12932
|
+
if (!existsSync18(TRIGGERS_PATH)) return [];
|
|
12755
12933
|
try {
|
|
12756
|
-
const raw =
|
|
12934
|
+
const raw = readFileSync16(TRIGGERS_PATH, "utf-8");
|
|
12757
12935
|
const all = JSON.parse(raw);
|
|
12758
12936
|
if (!Array.isArray(all)) return [];
|
|
12759
12937
|
if (project) {
|
|
@@ -12765,9 +12943,9 @@ function loadTriggers(project) {
|
|
|
12765
12943
|
}
|
|
12766
12944
|
}
|
|
12767
12945
|
function saveTriggers(triggers) {
|
|
12768
|
-
const dir =
|
|
12769
|
-
if (!
|
|
12770
|
-
|
|
12946
|
+
const dir = path24.dirname(TRIGGERS_PATH);
|
|
12947
|
+
if (!existsSync18(dir)) mkdirSync10(dir, { recursive: true });
|
|
12948
|
+
writeFileSync12(TRIGGERS_PATH, JSON.stringify(triggers, null, 2), "utf-8");
|
|
12771
12949
|
}
|
|
12772
12950
|
function createNewTrigger(input) {
|
|
12773
12951
|
const triggers = loadTriggers();
|
|
@@ -13051,48 +13229,48 @@ function registerListTriggers(server2) {
|
|
|
13051
13229
|
import { z as z31 } from "zod";
|
|
13052
13230
|
|
|
13053
13231
|
// src/automation/starter-packs/index.ts
|
|
13054
|
-
import { readFileSync as
|
|
13055
|
-
import
|
|
13232
|
+
import { readFileSync as readFileSync17, readdirSync as readdirSync7, existsSync as existsSync19 } from "fs";
|
|
13233
|
+
import path25 from "path";
|
|
13056
13234
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
13057
|
-
var __dirname =
|
|
13235
|
+
var __dirname = path25.dirname(fileURLToPath3(import.meta.url));
|
|
13058
13236
|
function listPacks() {
|
|
13059
|
-
const packsDir =
|
|
13060
|
-
if (!
|
|
13237
|
+
const packsDir = path25.join(__dirname, ".");
|
|
13238
|
+
if (!existsSync19(packsDir)) return [];
|
|
13061
13239
|
return readdirSync7(packsDir, { withFileTypes: true }).filter(
|
|
13062
|
-
(d) => d.isDirectory() &&
|
|
13240
|
+
(d) => d.isDirectory() && existsSync19(path25.join(packsDir, d.name, "custom-objects.json"))
|
|
13063
13241
|
).map((d) => d.name);
|
|
13064
13242
|
}
|
|
13065
13243
|
function loadPack(industry) {
|
|
13066
|
-
const packDir =
|
|
13067
|
-
const objectsPath =
|
|
13068
|
-
const triggersPath =
|
|
13069
|
-
const wikiDir =
|
|
13070
|
-
const manifestPath =
|
|
13071
|
-
const identityContextPath =
|
|
13072
|
-
if (!
|
|
13244
|
+
const packDir = path25.join(__dirname, industry);
|
|
13245
|
+
const objectsPath = path25.join(packDir, "custom-objects.json");
|
|
13246
|
+
const triggersPath = path25.join(packDir, "triggers.json");
|
|
13247
|
+
const wikiDir = path25.join(packDir, "wiki-seeds");
|
|
13248
|
+
const manifestPath = path25.join(packDir, "pack.json");
|
|
13249
|
+
const identityContextPath = path25.join(packDir, "identity-context.md");
|
|
13250
|
+
if (!existsSync19(objectsPath)) return null;
|
|
13073
13251
|
let customObjects = [];
|
|
13074
13252
|
try {
|
|
13075
13253
|
customObjects = JSON.parse(
|
|
13076
|
-
|
|
13254
|
+
readFileSync17(objectsPath, "utf-8")
|
|
13077
13255
|
);
|
|
13078
13256
|
} catch {
|
|
13079
13257
|
customObjects = [];
|
|
13080
13258
|
}
|
|
13081
13259
|
let triggers = [];
|
|
13082
|
-
if (
|
|
13260
|
+
if (existsSync19(triggersPath)) {
|
|
13083
13261
|
try {
|
|
13084
13262
|
triggers = JSON.parse(
|
|
13085
|
-
|
|
13263
|
+
readFileSync17(triggersPath, "utf-8")
|
|
13086
13264
|
);
|
|
13087
13265
|
} catch {
|
|
13088
13266
|
triggers = [];
|
|
13089
13267
|
}
|
|
13090
13268
|
}
|
|
13091
13269
|
const wikiSeeds = [];
|
|
13092
|
-
if (
|
|
13270
|
+
if (existsSync19(wikiDir)) {
|
|
13093
13271
|
const files = readdirSync7(wikiDir).filter((f) => f.endsWith(".md"));
|
|
13094
13272
|
for (const file of files) {
|
|
13095
|
-
const content =
|
|
13273
|
+
const content = readFileSync17(path25.join(wikiDir, file), "utf-8");
|
|
13096
13274
|
const titleMatch = content.match(/^#\s+(.+)/m);
|
|
13097
13275
|
wikiSeeds.push({
|
|
13098
13276
|
filename: file,
|
|
@@ -13102,17 +13280,17 @@ function loadPack(industry) {
|
|
|
13102
13280
|
}
|
|
13103
13281
|
}
|
|
13104
13282
|
let manifest = {};
|
|
13105
|
-
if (
|
|
13283
|
+
if (existsSync19(manifestPath)) {
|
|
13106
13284
|
try {
|
|
13107
|
-
manifest = JSON.parse(
|
|
13285
|
+
manifest = JSON.parse(readFileSync17(manifestPath, "utf-8"));
|
|
13108
13286
|
} catch {
|
|
13109
13287
|
manifest = {};
|
|
13110
13288
|
}
|
|
13111
13289
|
}
|
|
13112
13290
|
let identityContext = null;
|
|
13113
|
-
if (
|
|
13291
|
+
if (existsSync19(identityContextPath)) {
|
|
13114
13292
|
try {
|
|
13115
|
-
identityContext =
|
|
13293
|
+
identityContext = readFileSync17(identityContextPath, "utf-8");
|
|
13116
13294
|
} catch {
|
|
13117
13295
|
identityContext = null;
|
|
13118
13296
|
}
|
|
@@ -13169,8 +13347,8 @@ function applyPack(industry, project) {
|
|
|
13169
13347
|
|
|
13170
13348
|
// src/lib/client-coo.ts
|
|
13171
13349
|
init_config();
|
|
13172
|
-
import { existsSync as
|
|
13173
|
-
import
|
|
13350
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync11, writeFileSync as writeFileSync13 } from "fs";
|
|
13351
|
+
import path26 from "path";
|
|
13174
13352
|
|
|
13175
13353
|
// src/lib/employee-templates.ts
|
|
13176
13354
|
init_global_procedures();
|
|
@@ -13308,18 +13486,18 @@ var ClientCOOClobberError = class extends Error {
|
|
|
13308
13486
|
var COO_ROLE = "Chief Operating Officer";
|
|
13309
13487
|
var FEEDBACK_BEHAVIOR_CONTENT = "Tag exe-os issues with needs_improvement \u2014 this is the feedback loop that surfaces bugs, gaps, and friction to the founder each Monday.";
|
|
13310
13488
|
async function provisionClientCOO(vars, opts = {}) {
|
|
13311
|
-
const identityDir = opts.identityDir ??
|
|
13489
|
+
const identityDir = opts.identityDir ?? path26.join(EXE_AI_DIR, "identity");
|
|
13312
13490
|
const rosterPath = opts.employeesPath ?? EMPLOYEES_PATH;
|
|
13313
13491
|
const storeFeedbackBehavior = opts.storeFeedbackBehavior ?? true;
|
|
13314
|
-
const identityPath2 =
|
|
13315
|
-
if (
|
|
13492
|
+
const identityPath2 = path26.join(identityDir, `${vars.agent_name}.md`);
|
|
13493
|
+
if (existsSync20(identityPath2)) {
|
|
13316
13494
|
throw new ClientCOOClobberError(vars.agent_name, identityPath2);
|
|
13317
13495
|
}
|
|
13318
13496
|
const body = renderClientCOOTemplate(vars);
|
|
13319
|
-
if (!
|
|
13320
|
-
|
|
13497
|
+
if (!existsSync20(identityDir)) {
|
|
13498
|
+
mkdirSync11(identityDir, { recursive: true });
|
|
13321
13499
|
}
|
|
13322
|
-
|
|
13500
|
+
writeFileSync13(identityPath2, body, "utf-8");
|
|
13323
13501
|
const employees = await loadEmployees(rosterPath);
|
|
13324
13502
|
const existing = employees.find((e) => e.name === vars.agent_name);
|
|
13325
13503
|
let addedToRoster = false;
|
|
@@ -14209,8 +14387,8 @@ function registerUpdateWikiPage(server2) {
|
|
|
14209
14387
|
import { z as z37 } from "zod";
|
|
14210
14388
|
import { execFile } from "child_process";
|
|
14211
14389
|
import { promisify } from "util";
|
|
14212
|
-
import
|
|
14213
|
-
import { existsSync as
|
|
14390
|
+
import path27 from "path";
|
|
14391
|
+
import { existsSync as existsSync21 } from "fs";
|
|
14214
14392
|
|
|
14215
14393
|
// src/lib/hostinger-api.ts
|
|
14216
14394
|
var DEFAULT_BASE_URL = "https://developers.hostinger.com/api/vps/v1";
|
|
@@ -14258,9 +14436,9 @@ var HostingerApiClient = class {
|
|
|
14258
14436
|
}
|
|
14259
14437
|
this.lastRequestTime = Date.now();
|
|
14260
14438
|
}
|
|
14261
|
-
async request(method,
|
|
14439
|
+
async request(method, path38, body) {
|
|
14262
14440
|
await this.rateLimit();
|
|
14263
|
-
const url = `${this.baseUrl}${
|
|
14441
|
+
const url = `${this.baseUrl}${path38}`;
|
|
14264
14442
|
const headers = {
|
|
14265
14443
|
Authorization: `Bearer ${this.apiKey}`,
|
|
14266
14444
|
"Content-Type": "application/json",
|
|
@@ -14411,12 +14589,12 @@ async function waitForReady(client, vpsId) {
|
|
|
14411
14589
|
}
|
|
14412
14590
|
async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
|
|
14413
14591
|
const safeClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
14414
|
-
const playbookDir =
|
|
14415
|
-
const playbookPath =
|
|
14416
|
-
const inventoryPath =
|
|
14417
|
-
const clientVarsPath =
|
|
14418
|
-
const varsDir =
|
|
14419
|
-
if (!
|
|
14592
|
+
const playbookDir = path27.resolve(process.cwd(), "infrastructure", "ansible");
|
|
14593
|
+
const playbookPath = path27.join(playbookDir, "deploy.yml");
|
|
14594
|
+
const inventoryPath = path27.join(playbookDir, "inventory", "hosts.yml");
|
|
14595
|
+
const clientVarsPath = path27.join(playbookDir, "vars", `${safeClientName}.yml`);
|
|
14596
|
+
const varsDir = path27.join(playbookDir, "vars");
|
|
14597
|
+
if (!path27.resolve(clientVarsPath).startsWith(path27.resolve(varsDir))) {
|
|
14420
14598
|
throw new Error(`Invalid client name for vars path: ${clientName}`);
|
|
14421
14599
|
}
|
|
14422
14600
|
const args = [
|
|
@@ -14432,7 +14610,7 @@ async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
|
|
|
14432
14610
|
"-e",
|
|
14433
14611
|
`client_name=${safeClientName}`
|
|
14434
14612
|
];
|
|
14435
|
-
if (
|
|
14613
|
+
if (existsSync21(clientVarsPath)) {
|
|
14436
14614
|
args.push("-e", `@${clientVarsPath}`);
|
|
14437
14615
|
}
|
|
14438
14616
|
try {
|
|
@@ -14595,18 +14773,18 @@ function registerQueryConversations(server2) {
|
|
|
14595
14773
|
|
|
14596
14774
|
// src/mcp/tools/load-skill.ts
|
|
14597
14775
|
import { z as z39 } from "zod";
|
|
14598
|
-
import { readFileSync as
|
|
14599
|
-
import
|
|
14776
|
+
import { readFileSync as readFileSync18, readdirSync as readdirSync8, statSync as statSync3 } from "fs";
|
|
14777
|
+
import path28 from "path";
|
|
14600
14778
|
import { homedir as homedir2 } from "os";
|
|
14601
|
-
var SKILLS_DIR =
|
|
14779
|
+
var SKILLS_DIR = path28.join(homedir2(), ".claude", "skills");
|
|
14602
14780
|
function listAvailableSkills() {
|
|
14603
14781
|
try {
|
|
14604
14782
|
const entries = readdirSync8(SKILLS_DIR);
|
|
14605
14783
|
return entries.filter((entry) => {
|
|
14606
14784
|
try {
|
|
14607
|
-
const entryPath =
|
|
14785
|
+
const entryPath = path28.join(SKILLS_DIR, entry);
|
|
14608
14786
|
if (!statSync3(entryPath).isDirectory()) return false;
|
|
14609
|
-
const skillFile =
|
|
14787
|
+
const skillFile = path28.join(entryPath, "SKILL.md");
|
|
14610
14788
|
statSync3(skillFile);
|
|
14611
14789
|
return true;
|
|
14612
14790
|
} catch {
|
|
@@ -14649,10 +14827,10 @@ ${skills.map((s) => `- ${s}`).join("\n")}`
|
|
|
14649
14827
|
}]
|
|
14650
14828
|
};
|
|
14651
14829
|
}
|
|
14652
|
-
const sanitized =
|
|
14653
|
-
const skillFile =
|
|
14830
|
+
const sanitized = path28.basename(skill_name);
|
|
14831
|
+
const skillFile = path28.join(SKILLS_DIR, sanitized, "SKILL.md");
|
|
14654
14832
|
try {
|
|
14655
|
-
const content =
|
|
14833
|
+
const content = readFileSync18(skillFile, "utf-8");
|
|
14656
14834
|
return {
|
|
14657
14835
|
content: [{
|
|
14658
14836
|
type: "text",
|
|
@@ -15291,7 +15469,7 @@ init_database();
|
|
|
15291
15469
|
import { readdir } from "fs/promises";
|
|
15292
15470
|
import { createReadStream } from "fs";
|
|
15293
15471
|
import { createInterface } from "readline";
|
|
15294
|
-
import
|
|
15472
|
+
import path29 from "path";
|
|
15295
15473
|
import os10 from "os";
|
|
15296
15474
|
var MODEL_PRICING = {
|
|
15297
15475
|
// Opus 4.5+ ($5/$25 — Anthropic price drop from original Opus 4)
|
|
@@ -15340,18 +15518,18 @@ async function getAgentSpend(period = "7d") {
|
|
|
15340
15518
|
for (const row of result.rows) {
|
|
15341
15519
|
sessionAgent.set(row.session_uuid, row.agent_id);
|
|
15342
15520
|
}
|
|
15343
|
-
const claudeDir =
|
|
15521
|
+
const claudeDir = path29.join(os10.homedir(), ".claude", "projects");
|
|
15344
15522
|
let projectDirs = [];
|
|
15345
15523
|
try {
|
|
15346
15524
|
const entries = await readdir(claudeDir);
|
|
15347
|
-
projectDirs = entries.map((e) =>
|
|
15525
|
+
projectDirs = entries.map((e) => path29.join(claudeDir, e));
|
|
15348
15526
|
} catch {
|
|
15349
15527
|
return [];
|
|
15350
15528
|
}
|
|
15351
15529
|
const agentTotals = /* @__PURE__ */ new Map();
|
|
15352
15530
|
for (const [sessionUuid, agentId] of sessionAgent) {
|
|
15353
15531
|
for (const dir of projectDirs) {
|
|
15354
|
-
const jsonlPath =
|
|
15532
|
+
const jsonlPath = path29.join(dir, `${sessionUuid}.jsonl`);
|
|
15355
15533
|
try {
|
|
15356
15534
|
const usage = await extractSessionUsage(jsonlPath);
|
|
15357
15535
|
if (usage.input === 0 && usage.output === 0) continue;
|
|
@@ -15929,12 +16107,12 @@ function registerExportGraph(server2) {
|
|
|
15929
16107
|
}
|
|
15930
16108
|
const html = await exportGraphHTML(client);
|
|
15931
16109
|
const fs = await import("fs");
|
|
15932
|
-
const
|
|
16110
|
+
const path38 = await import("path");
|
|
15933
16111
|
const os11 = await import("os");
|
|
15934
|
-
const outDir =
|
|
16112
|
+
const outDir = path38.join(os11.homedir(), ".exe-os", "exports");
|
|
15935
16113
|
fs.mkdirSync(outDir, { recursive: true });
|
|
15936
16114
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
15937
|
-
const filePath =
|
|
16115
|
+
const filePath = path38.join(outDir, `graph-${timestamp}.html`);
|
|
15938
16116
|
fs.writeFileSync(filePath, html, "utf-8");
|
|
15939
16117
|
return {
|
|
15940
16118
|
content: [
|
|
@@ -16155,10 +16333,10 @@ function registerListAgentSessions(server2) {
|
|
|
16155
16333
|
|
|
16156
16334
|
// src/mcp/tools/get-daemon-health.ts
|
|
16157
16335
|
import { z as z54 } from "zod";
|
|
16158
|
-
import { existsSync as
|
|
16159
|
-
import
|
|
16336
|
+
import { existsSync as existsSync22, readFileSync as readFileSync19 } from "fs";
|
|
16337
|
+
import path30 from "path";
|
|
16160
16338
|
import { homedir as homedir3 } from "os";
|
|
16161
|
-
var PID_PATH2 =
|
|
16339
|
+
var PID_PATH2 = path30.join(homedir3(), ".exe-os", "exed.pid");
|
|
16162
16340
|
function formatUptime(seconds) {
|
|
16163
16341
|
const h = Math.floor(seconds / 3600);
|
|
16164
16342
|
const m = Math.floor(seconds % 3600 / 60);
|
|
@@ -16169,8 +16347,8 @@ function formatUptime(seconds) {
|
|
|
16169
16347
|
}
|
|
16170
16348
|
function isDaemonAlive() {
|
|
16171
16349
|
try {
|
|
16172
|
-
if (!
|
|
16173
|
-
const pid = parseInt(
|
|
16350
|
+
if (!existsSync22(PID_PATH2)) return { alive: false, pid: null };
|
|
16351
|
+
const pid = parseInt(readFileSync19(PID_PATH2, "utf8").trim(), 10);
|
|
16174
16352
|
if (isNaN(pid) || pid <= 0) return { alive: false, pid: null };
|
|
16175
16353
|
process.kill(pid, 0);
|
|
16176
16354
|
return { alive: true, pid };
|
|
@@ -16244,7 +16422,7 @@ init_tmux_routing();
|
|
|
16244
16422
|
init_task_scope();
|
|
16245
16423
|
init_employees();
|
|
16246
16424
|
import { execSync as execSync10 } from "child_process";
|
|
16247
|
-
import { existsSync as
|
|
16425
|
+
import { existsSync as existsSync23, readFileSync as readFileSync20, writeFileSync as writeFileSync14 } from "fs";
|
|
16248
16426
|
import { homedir as homedir4 } from "os";
|
|
16249
16427
|
import { join as join2 } from "path";
|
|
16250
16428
|
var IDLE_NUDGE_DEDUP_MS = Number(process.env.EXE_NUDGE_INTERVAL_MS) || 6e4;
|
|
@@ -16346,15 +16524,15 @@ function registerGetAutoWakeStatus(server2) {
|
|
|
16346
16524
|
init_worker_gate();
|
|
16347
16525
|
init_config();
|
|
16348
16526
|
import { z as z56 } from "zod";
|
|
16349
|
-
import { readdirSync as readdirSync10, existsSync as
|
|
16350
|
-
import
|
|
16351
|
-
var WORKER_PID_DIR2 =
|
|
16527
|
+
import { readdirSync as readdirSync10, existsSync as existsSync25 } from "fs";
|
|
16528
|
+
import path32 from "path";
|
|
16529
|
+
var WORKER_PID_DIR2 = path32.join(EXE_AI_DIR, "worker-pids");
|
|
16352
16530
|
function countAliveWorkers() {
|
|
16353
16531
|
let alive = 0;
|
|
16354
16532
|
let stale = 0;
|
|
16355
16533
|
let reservations = 0;
|
|
16356
16534
|
try {
|
|
16357
|
-
if (!
|
|
16535
|
+
if (!existsSync25(WORKER_PID_DIR2)) return { alive: 0, stale: 0, reservations: 0 };
|
|
16358
16536
|
const files = readdirSync10(WORKER_PID_DIR2);
|
|
16359
16537
|
for (const f of files) {
|
|
16360
16538
|
if (!f.endsWith(".pid")) continue;
|
|
@@ -16441,9 +16619,9 @@ function isMainModule(importMetaUrl) {
|
|
|
16441
16619
|
}
|
|
16442
16620
|
|
|
16443
16621
|
// src/bin/exe-doctor.ts
|
|
16444
|
-
import { existsSync as
|
|
16622
|
+
import { existsSync as existsSync26 } from "fs";
|
|
16445
16623
|
import { spawn as spawn2 } from "child_process";
|
|
16446
|
-
import
|
|
16624
|
+
import path33 from "path";
|
|
16447
16625
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
16448
16626
|
|
|
16449
16627
|
// src/lib/conflict-detector.ts
|
|
@@ -16846,7 +17024,7 @@ async function auditOrphanedProjects(client) {
|
|
|
16846
17024
|
for (const row of result.rows) {
|
|
16847
17025
|
const name = row.project_name;
|
|
16848
17026
|
const count = Number(row.cnt);
|
|
16849
|
-
const exists =
|
|
17027
|
+
const exists = existsSync26(path33.join(home, name)) || existsSync26(path33.join(home, "..", name)) || existsSync26(path33.join(process.cwd(), "..", name));
|
|
16850
17028
|
if (!exists) {
|
|
16851
17029
|
orphans.push({ project_name: name, count });
|
|
16852
17030
|
}
|
|
@@ -17004,7 +17182,7 @@ async function fixNullVectors() {
|
|
|
17004
17182
|
}
|
|
17005
17183
|
}
|
|
17006
17184
|
const npmRoot = (await import("child_process")).execSync("npm root -g", { encoding: "utf8" }).trim();
|
|
17007
|
-
const backfillPath =
|
|
17185
|
+
const backfillPath = path33.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
|
|
17008
17186
|
return new Promise((resolve, reject) => {
|
|
17009
17187
|
const child = spawn2("node", [backfillPath], { stdio: "inherit" });
|
|
17010
17188
|
if (child.pid) registerWorkerPid2(child.pid);
|
|
@@ -17202,9 +17380,9 @@ import { z as z58 } from "zod";
|
|
|
17202
17380
|
|
|
17203
17381
|
// src/lib/cloud-sync.ts
|
|
17204
17382
|
init_database();
|
|
17205
|
-
import { readFileSync as
|
|
17383
|
+
import { readFileSync as readFileSync22, writeFileSync as writeFileSync17, existsSync as existsSync28, readdirSync as readdirSync11, mkdirSync as mkdirSync14, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
17206
17384
|
import crypto16 from "crypto";
|
|
17207
|
-
import
|
|
17385
|
+
import path35 from "path";
|
|
17208
17386
|
import { homedir as homedir6 } from "os";
|
|
17209
17387
|
|
|
17210
17388
|
// src/lib/crypto.ts
|
|
@@ -17278,7 +17456,7 @@ function sqlSafe(v) {
|
|
|
17278
17456
|
}
|
|
17279
17457
|
function logError(msg) {
|
|
17280
17458
|
try {
|
|
17281
|
-
const logPath =
|
|
17459
|
+
const logPath = path35.join(homedir6(), ".exe-os", "workers.log");
|
|
17282
17460
|
appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
17283
17461
|
`);
|
|
17284
17462
|
} catch {
|
|
@@ -17287,24 +17465,24 @@ function logError(msg) {
|
|
|
17287
17465
|
var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
17288
17466
|
var FETCH_TIMEOUT_MS4 = 3e4;
|
|
17289
17467
|
var PUSH_BATCH_SIZE = 5e3;
|
|
17290
|
-
var ROSTER_LOCK_PATH =
|
|
17468
|
+
var ROSTER_LOCK_PATH = path35.join(EXE_AI_DIR, "roster-merge.lock");
|
|
17291
17469
|
var LOCK_STALE_MS = 3e4;
|
|
17292
17470
|
async function withRosterLock(fn) {
|
|
17293
17471
|
try {
|
|
17294
17472
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
17295
17473
|
closeSync2(fd);
|
|
17296
|
-
|
|
17474
|
+
writeFileSync17(ROSTER_LOCK_PATH, String(Date.now()));
|
|
17297
17475
|
} catch (err) {
|
|
17298
17476
|
if (err.code === "EEXIST") {
|
|
17299
17477
|
try {
|
|
17300
|
-
const ts2 = parseInt(
|
|
17478
|
+
const ts2 = parseInt(readFileSync22(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
17301
17479
|
if (Date.now() - ts2 < LOCK_STALE_MS) {
|
|
17302
17480
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
17303
17481
|
}
|
|
17304
17482
|
unlinkSync10(ROSTER_LOCK_PATH);
|
|
17305
17483
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
17306
17484
|
closeSync2(fd);
|
|
17307
|
-
|
|
17485
|
+
writeFileSync17(ROSTER_LOCK_PATH, String(Date.now()));
|
|
17308
17486
|
} catch (retryErr) {
|
|
17309
17487
|
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
17310
17488
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
@@ -17678,8 +17856,8 @@ async function cloudSync(config2) {
|
|
|
17678
17856
|
try {
|
|
17679
17857
|
const employees = await loadEmployees();
|
|
17680
17858
|
rosterResult.employees = employees.length;
|
|
17681
|
-
const idDir =
|
|
17682
|
-
if (
|
|
17859
|
+
const idDir = path35.join(EXE_AI_DIR, "identity");
|
|
17860
|
+
if (existsSync28(idDir)) {
|
|
17683
17861
|
rosterResult.identities = readdirSync11(idDir).filter((f) => f.endsWith(".md")).length;
|
|
17684
17862
|
}
|
|
17685
17863
|
} catch {
|
|
@@ -17697,48 +17875,56 @@ async function cloudSync(config2) {
|
|
|
17697
17875
|
roster: rosterResult
|
|
17698
17876
|
};
|
|
17699
17877
|
}
|
|
17700
|
-
var ROSTER_DELETIONS_PATH =
|
|
17878
|
+
var ROSTER_DELETIONS_PATH = path35.join(EXE_AI_DIR, "roster-deletions.json");
|
|
17701
17879
|
function consumeRosterDeletions() {
|
|
17702
17880
|
try {
|
|
17703
|
-
if (!
|
|
17704
|
-
const deletions = JSON.parse(
|
|
17705
|
-
|
|
17881
|
+
if (!existsSync28(ROSTER_DELETIONS_PATH)) return [];
|
|
17882
|
+
const deletions = JSON.parse(readFileSync22(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
17883
|
+
writeFileSync17(ROSTER_DELETIONS_PATH, "[]");
|
|
17706
17884
|
return deletions;
|
|
17707
17885
|
} catch {
|
|
17708
17886
|
return [];
|
|
17709
17887
|
}
|
|
17710
17888
|
}
|
|
17711
17889
|
function buildRosterBlob(paths) {
|
|
17712
|
-
const rosterPath = paths?.rosterPath ??
|
|
17713
|
-
const identityDir = paths?.identityDir ??
|
|
17714
|
-
const configPath = paths?.configPath ??
|
|
17890
|
+
const rosterPath = paths?.rosterPath ?? path35.join(EXE_AI_DIR, "exe-employees.json");
|
|
17891
|
+
const identityDir = paths?.identityDir ?? path35.join(EXE_AI_DIR, "identity");
|
|
17892
|
+
const configPath = paths?.configPath ?? path35.join(EXE_AI_DIR, "config.json");
|
|
17715
17893
|
let roster = [];
|
|
17716
|
-
if (
|
|
17894
|
+
if (existsSync28(rosterPath)) {
|
|
17717
17895
|
try {
|
|
17718
|
-
roster = JSON.parse(
|
|
17896
|
+
roster = JSON.parse(readFileSync22(rosterPath, "utf-8"));
|
|
17719
17897
|
} catch {
|
|
17720
17898
|
}
|
|
17721
17899
|
}
|
|
17722
17900
|
const identities = {};
|
|
17723
|
-
if (
|
|
17901
|
+
if (existsSync28(identityDir)) {
|
|
17724
17902
|
for (const file of readdirSync11(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
17725
17903
|
try {
|
|
17726
|
-
identities[file] =
|
|
17904
|
+
identities[file] = readFileSync22(path35.join(identityDir, file), "utf-8");
|
|
17727
17905
|
} catch {
|
|
17728
17906
|
}
|
|
17729
17907
|
}
|
|
17730
17908
|
}
|
|
17731
17909
|
let config2;
|
|
17732
|
-
if (
|
|
17910
|
+
if (existsSync28(configPath)) {
|
|
17733
17911
|
try {
|
|
17734
|
-
config2 = JSON.parse(
|
|
17912
|
+
config2 = JSON.parse(readFileSync22(configPath, "utf-8"));
|
|
17913
|
+
} catch {
|
|
17914
|
+
}
|
|
17915
|
+
}
|
|
17916
|
+
let agentConfig;
|
|
17917
|
+
const agentConfigPath = path35.join(EXE_AI_DIR, "agent-config.json");
|
|
17918
|
+
if (existsSync28(agentConfigPath)) {
|
|
17919
|
+
try {
|
|
17920
|
+
agentConfig = JSON.parse(readFileSync22(agentConfigPath, "utf-8"));
|
|
17735
17921
|
} catch {
|
|
17736
17922
|
}
|
|
17737
17923
|
}
|
|
17738
17924
|
const deletedNames = consumeRosterDeletions();
|
|
17739
|
-
const content = JSON.stringify({ roster, identities, config: config2, deletedNames });
|
|
17925
|
+
const content = JSON.stringify({ roster, identities, config: config2, agentConfig, deletedNames });
|
|
17740
17926
|
const hash = crypto16.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
17741
|
-
return { roster, identities, config: config2, deletedNames, version: hash };
|
|
17927
|
+
return { roster, identities, config: config2, agentConfig, deletedNames, version: hash };
|
|
17742
17928
|
}
|
|
17743
17929
|
async function cloudPushRoster(config2) {
|
|
17744
17930
|
assertSecureEndpoint(config2.endpoint);
|
|
@@ -17807,23 +17993,23 @@ async function cloudPullRoster(config2) {
|
|
|
17807
17993
|
}
|
|
17808
17994
|
}
|
|
17809
17995
|
function mergeConfig(remoteConfig, configPath) {
|
|
17810
|
-
const cfgPath = configPath ??
|
|
17996
|
+
const cfgPath = configPath ?? path35.join(EXE_AI_DIR, "config.json");
|
|
17811
17997
|
let local = {};
|
|
17812
|
-
if (
|
|
17998
|
+
if (existsSync28(cfgPath)) {
|
|
17813
17999
|
try {
|
|
17814
|
-
local = JSON.parse(
|
|
18000
|
+
local = JSON.parse(readFileSync22(cfgPath, "utf-8"));
|
|
17815
18001
|
} catch {
|
|
17816
18002
|
}
|
|
17817
18003
|
}
|
|
17818
18004
|
const merged = { ...remoteConfig, ...local };
|
|
17819
|
-
const dir =
|
|
17820
|
-
if (!
|
|
17821
|
-
|
|
18005
|
+
const dir = path35.dirname(cfgPath);
|
|
18006
|
+
if (!existsSync28(dir)) mkdirSync14(dir, { recursive: true });
|
|
18007
|
+
writeFileSync17(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
17822
18008
|
}
|
|
17823
18009
|
async function mergeRosterFromRemote(remote, paths) {
|
|
17824
18010
|
return withRosterLock(async () => {
|
|
17825
18011
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
17826
|
-
const identityDir = paths?.identityDir ??
|
|
18012
|
+
const identityDir = paths?.identityDir ?? path35.join(EXE_AI_DIR, "identity");
|
|
17827
18013
|
const localEmployees = await loadEmployees(rosterPath);
|
|
17828
18014
|
const localNames = new Set(localEmployees.map((e) => e.name));
|
|
17829
18015
|
let added = 0;
|
|
@@ -17844,15 +18030,15 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
17844
18030
|
) ?? lookupKey;
|
|
17845
18031
|
const remoteIdentity = remote.identities[matchedKey];
|
|
17846
18032
|
if (remoteIdentity) {
|
|
17847
|
-
if (!
|
|
17848
|
-
const idPath =
|
|
18033
|
+
if (!existsSync28(identityDir)) mkdirSync14(identityDir, { recursive: true });
|
|
18034
|
+
const idPath = path35.join(identityDir, `${remoteEmp.name}.md`);
|
|
17849
18035
|
let localIdentity = null;
|
|
17850
18036
|
try {
|
|
17851
|
-
localIdentity =
|
|
18037
|
+
localIdentity = existsSync28(idPath) ? readFileSync22(idPath, "utf-8") : null;
|
|
17852
18038
|
} catch {
|
|
17853
18039
|
}
|
|
17854
18040
|
if (localIdentity !== remoteIdentity) {
|
|
17855
|
-
|
|
18041
|
+
writeFileSync17(idPath, remoteIdentity, "utf-8");
|
|
17856
18042
|
identitiesUpdated++;
|
|
17857
18043
|
}
|
|
17858
18044
|
}
|
|
@@ -17876,6 +18062,21 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
17876
18062
|
} catch {
|
|
17877
18063
|
}
|
|
17878
18064
|
}
|
|
18065
|
+
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
18066
|
+
try {
|
|
18067
|
+
const agentConfigPath = path35.join(EXE_AI_DIR, "agent-config.json");
|
|
18068
|
+
let local = {};
|
|
18069
|
+
if (existsSync28(agentConfigPath)) {
|
|
18070
|
+
try {
|
|
18071
|
+
local = JSON.parse(readFileSync22(agentConfigPath, "utf-8"));
|
|
18072
|
+
} catch {
|
|
18073
|
+
}
|
|
18074
|
+
}
|
|
18075
|
+
const merged = { ...remote.agentConfig, ...local };
|
|
18076
|
+
writeFileSync17(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
18077
|
+
} catch {
|
|
18078
|
+
}
|
|
18079
|
+
}
|
|
17879
18080
|
return { added, identitiesUpdated };
|
|
17880
18081
|
});
|
|
17881
18082
|
}
|
|
@@ -18598,11 +18799,11 @@ import { z as z62 } from "zod";
|
|
|
18598
18799
|
// src/lib/people.ts
|
|
18599
18800
|
init_config();
|
|
18600
18801
|
import { readFile as readFile5, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
|
|
18601
|
-
import { existsSync as
|
|
18602
|
-
import
|
|
18603
|
-
var PEOPLE_PATH =
|
|
18802
|
+
import { existsSync as existsSync29, readFileSync as readFileSync23 } from "fs";
|
|
18803
|
+
import path36 from "path";
|
|
18804
|
+
var PEOPLE_PATH = path36.join(EXE_AI_DIR, "people.json");
|
|
18604
18805
|
async function loadPeople() {
|
|
18605
|
-
if (!
|
|
18806
|
+
if (!existsSync29(PEOPLE_PATH)) return [];
|
|
18606
18807
|
try {
|
|
18607
18808
|
const raw = await readFile5(PEOPLE_PATH, "utf-8");
|
|
18608
18809
|
return JSON.parse(raw);
|
|
@@ -18611,7 +18812,7 @@ async function loadPeople() {
|
|
|
18611
18812
|
}
|
|
18612
18813
|
}
|
|
18613
18814
|
async function savePeople(people) {
|
|
18614
|
-
await mkdir5(
|
|
18815
|
+
await mkdir5(path36.dirname(PEOPLE_PATH), { recursive: true });
|
|
18615
18816
|
await writeFile6(PEOPLE_PATH, JSON.stringify(people, null, 2) + "\n", "utf-8");
|
|
18616
18817
|
}
|
|
18617
18818
|
async function addPerson(person) {
|
|
@@ -18713,6 +18914,103 @@ Notes: ${person.notes}` : ""}`
|
|
|
18713
18914
|
);
|
|
18714
18915
|
}
|
|
18715
18916
|
|
|
18917
|
+
// src/mcp/tools/set-agent-config.ts
|
|
18918
|
+
init_active_agent();
|
|
18919
|
+
init_agent_config();
|
|
18920
|
+
init_runtime_table();
|
|
18921
|
+
init_employees();
|
|
18922
|
+
import { z as z63 } from "zod";
|
|
18923
|
+
function registerSetAgentConfig(server2) {
|
|
18924
|
+
server2.registerTool(
|
|
18925
|
+
"set_agent_config",
|
|
18926
|
+
{
|
|
18927
|
+
title: "Set Agent Config",
|
|
18928
|
+
description: "Set or view per-agent runtime + model configuration. Controls which runtime (claude, codex, opencode) and model each agent uses when dispatched. COO-only. Omit runtime/model to view current config for an agent or all agents.",
|
|
18929
|
+
inputSchema: {
|
|
18930
|
+
agent_id: z63.string().optional().describe("Agent name. Omit to view all agents."),
|
|
18931
|
+
runtime: z63.string().optional().describe("Runtime: claude, codex, or opencode"),
|
|
18932
|
+
model: z63.string().optional().describe("Model name (e.g. claude-opus-4, gpt-5.4, minimax-m2.7)")
|
|
18933
|
+
}
|
|
18934
|
+
},
|
|
18935
|
+
async ({ agent_id, runtime, model }) => {
|
|
18936
|
+
const { agentRole } = getActiveAgent();
|
|
18937
|
+
if (agentRole !== "COO") {
|
|
18938
|
+
return {
|
|
18939
|
+
content: [
|
|
18940
|
+
{
|
|
18941
|
+
type: "text",
|
|
18942
|
+
text: `Access denied. set_agent_config is COO-only. Your role: ${agentRole}`
|
|
18943
|
+
}
|
|
18944
|
+
],
|
|
18945
|
+
isError: true
|
|
18946
|
+
};
|
|
18947
|
+
}
|
|
18948
|
+
if (!runtime || !model) {
|
|
18949
|
+
if (agent_id) {
|
|
18950
|
+
const cfg = getAgentRuntime(agent_id);
|
|
18951
|
+
return {
|
|
18952
|
+
content: [
|
|
18953
|
+
{
|
|
18954
|
+
type: "text",
|
|
18955
|
+
text: `${agent_id}: runtime=${cfg.runtime} model=${cfg.model}`
|
|
18956
|
+
}
|
|
18957
|
+
]
|
|
18958
|
+
};
|
|
18959
|
+
}
|
|
18960
|
+
const config2 = loadAgentConfig();
|
|
18961
|
+
let employees = [];
|
|
18962
|
+
try {
|
|
18963
|
+
employees = loadEmployeesSync();
|
|
18964
|
+
} catch {
|
|
18965
|
+
}
|
|
18966
|
+
const lines = ["Agent Runtime Configuration", "\u2550".repeat(55)];
|
|
18967
|
+
lines.push(
|
|
18968
|
+
"Agent".padEnd(12) + "Role".padEnd(16) + "Runtime".padEnd(12) + "Model"
|
|
18969
|
+
);
|
|
18970
|
+
lines.push("\u2500".repeat(55));
|
|
18971
|
+
for (const emp of employees) {
|
|
18972
|
+
const entry = config2[emp.name];
|
|
18973
|
+
const rt = entry?.runtime ?? DEFAULT_RUNTIME;
|
|
18974
|
+
const mdl = entry?.model ?? DEFAULT_MODELS[DEFAULT_RUNTIME];
|
|
18975
|
+
const tag = entry ? "" : " (default)";
|
|
18976
|
+
lines.push(
|
|
18977
|
+
emp.name.padEnd(12) + (emp.role ?? "").padEnd(16) + rt.padEnd(12) + mdl + tag
|
|
18978
|
+
);
|
|
18979
|
+
}
|
|
18980
|
+
return {
|
|
18981
|
+
content: [{ type: "text", text: lines.join("\n") }]
|
|
18982
|
+
};
|
|
18983
|
+
}
|
|
18984
|
+
if (!agent_id) {
|
|
18985
|
+
return {
|
|
18986
|
+
content: [
|
|
18987
|
+
{
|
|
18988
|
+
type: "text",
|
|
18989
|
+
text: "agent_id is required when setting runtime/model."
|
|
18990
|
+
}
|
|
18991
|
+
],
|
|
18992
|
+
isError: true
|
|
18993
|
+
};
|
|
18994
|
+
}
|
|
18995
|
+
const result = setAgentRuntime(agent_id, runtime, model);
|
|
18996
|
+
if (!result.ok) {
|
|
18997
|
+
return {
|
|
18998
|
+
content: [{ type: "text", text: result.error }],
|
|
18999
|
+
isError: true
|
|
19000
|
+
};
|
|
19001
|
+
}
|
|
19002
|
+
return {
|
|
19003
|
+
content: [
|
|
19004
|
+
{
|
|
19005
|
+
type: "text",
|
|
19006
|
+
text: `Set ${agent_id} \u2192 runtime=${runtime} model=${model}`
|
|
19007
|
+
}
|
|
19008
|
+
]
|
|
19009
|
+
};
|
|
19010
|
+
}
|
|
19011
|
+
);
|
|
19012
|
+
}
|
|
19013
|
+
|
|
18716
19014
|
// src/lib/telemetry.ts
|
|
18717
19015
|
var ENABLED = process.env.EXE_TELEMETRY === "1";
|
|
18718
19016
|
var initialized = false;
|
|
@@ -18844,6 +19142,7 @@ registerGetLicenseStatus(server);
|
|
|
18844
19142
|
registerAddPerson(server);
|
|
18845
19143
|
registerListPeople(server);
|
|
18846
19144
|
registerGetPerson(server);
|
|
19145
|
+
registerSetAgentConfig(server);
|
|
18847
19146
|
try {
|
|
18848
19147
|
await initStore();
|
|
18849
19148
|
process.stderr.write("[exe-os] MCP server starting...\n");
|
|
@@ -18904,14 +19203,14 @@ try {
|
|
|
18904
19203
|
`
|
|
18905
19204
|
);
|
|
18906
19205
|
const thisFile = fileURLToPath5(import.meta.url);
|
|
18907
|
-
const backfillPath =
|
|
18908
|
-
|
|
19206
|
+
const backfillPath = path37.resolve(
|
|
19207
|
+
path37.dirname(thisFile),
|
|
18909
19208
|
"../bin/backfill-vectors.js"
|
|
18910
19209
|
);
|
|
18911
|
-
if (
|
|
19210
|
+
if (existsSync30(backfillPath)) {
|
|
18912
19211
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
18913
|
-
const logPath =
|
|
18914
|
-
|
|
19212
|
+
const logPath = path37.join(exeDir, "workers.log");
|
|
19213
|
+
mkdirSync15(path37.dirname(logPath), { recursive: true });
|
|
18915
19214
|
let logFd = "ignore";
|
|
18916
19215
|
try {
|
|
18917
19216
|
logFd = openSync3(logPath, "a");
|