@askexenow/exe-os 0.9.16 → 0.9.18
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/backfill-conversations.js +1242 -909
- package/dist/bin/backfill-responses.js +1245 -912
- package/dist/bin/backfill-vectors.js +1244 -906
- package/dist/bin/cleanup-stale-review-tasks.js +1870 -417
- package/dist/bin/cli.js +217 -107
- package/dist/bin/exe-agent-config.js +2 -2
- package/dist/bin/exe-agent.js +62 -0
- package/dist/bin/exe-assign.js +346 -10
- package/dist/bin/exe-boot.js +387 -32
- package/dist/bin/exe-call.js +72 -2
- package/dist/bin/exe-cloud.js +8 -0
- package/dist/bin/exe-dispatch.js +1821 -225
- package/dist/bin/exe-doctor.js +720 -52
- package/dist/bin/exe-export-behaviors.js +1429 -148
- package/dist/bin/exe-forget.js +1408 -34
- package/dist/bin/exe-gateway.js +1629 -1295
- package/dist/bin/exe-heartbeat.js +1899 -448
- package/dist/bin/exe-kill.js +1624 -346
- package/dist/bin/exe-launch-agent.js +726 -90
- package/dist/bin/exe-link.js +27 -8
- package/dist/bin/exe-new-employee.js +75 -9
- package/dist/bin/exe-pending-messages.js +2769 -1316
- package/dist/bin/exe-pending-notifications.js +2829 -1376
- package/dist/bin/exe-pending-reviews.js +2847 -1392
- package/dist/bin/exe-rename.js +89 -8
- package/dist/bin/exe-review.js +1494 -312
- package/dist/bin/exe-search.js +1608 -1300
- package/dist/bin/exe-session-cleanup.js +194 -91
- package/dist/bin/exe-settings.js +10 -2
- package/dist/bin/exe-start-codex.js +769 -120
- package/dist/bin/exe-start-opencode.js +763 -108
- package/dist/bin/exe-status.js +1887 -434
- package/dist/bin/exe-team.js +1782 -324
- package/dist/bin/git-sweep.js +408 -33
- package/dist/bin/graph-backfill.js +681 -27
- package/dist/bin/graph-export.js +1419 -141
- package/dist/bin/install.js +4 -8
- package/dist/bin/intercom-check.js +8641 -0
- package/dist/bin/scan-tasks.js +553 -38
- package/dist/bin/setup.js +82 -10
- package/dist/bin/shard-migrate.js +682 -28
- package/dist/gateway/index.js +1629 -1295
- package/dist/hooks/bug-report-worker.js +1136 -183
- package/dist/hooks/codex-stop-task-finalizer.js +1060 -107
- package/dist/hooks/commit-complete.js +408 -33
- package/dist/hooks/error-recall.js +1608 -1300
- package/dist/hooks/ingest-worker.js +250 -7966
- package/dist/hooks/ingest.js +707 -119
- package/dist/hooks/instructions-loaded.js +383 -20
- package/dist/hooks/notification.js +383 -20
- package/dist/hooks/post-compact.js +384 -21
- package/dist/hooks/{response-ingest-worker.js → post-tool-combined.js} +4157 -1716
- package/dist/hooks/pre-compact.js +486 -45
- package/dist/hooks/pre-tool-use.js +385 -22
- package/dist/hooks/prompt-submit.js +291 -96
- package/dist/hooks/session-end.js +490 -48
- package/dist/hooks/session-start.js +236 -22
- package/dist/hooks/stop.js +192 -50
- package/dist/hooks/subagent-stop.js +95 -18
- package/dist/hooks/summary-worker.js +205 -361
- package/dist/index.js +221 -105
- package/dist/lib/agent-config.js +2 -2
- package/dist/lib/cloud-sync.js +27 -8
- package/dist/lib/consolidation.js +437 -41
- package/dist/lib/database.js +20 -8
- package/dist/lib/db-daemon-client.js +2 -2
- package/dist/lib/db.js +20 -8
- package/dist/lib/device-registry.js +27 -8
- package/dist/lib/employee-templates.js +62 -0
- package/dist/lib/employees.js +2 -2
- package/dist/lib/exe-daemon.js +6703 -6259
- package/dist/lib/hybrid-search.js +1608 -1300
- package/dist/lib/identity.js +1 -1
- package/dist/lib/messaging.js +11 -3
- package/dist/lib/reminders.js +1 -1
- package/dist/lib/schedules.js +718 -26
- package/dist/lib/session-registry.js +11 -0
- package/dist/lib/skill-learning.js +639 -9
- package/dist/lib/store.js +676 -27
- package/dist/lib/tasks.js +665 -27
- package/dist/lib/tmux-routing.js +732 -94
- package/dist/lib/token-spend.js +1 -1
- package/dist/mcp/server.js +856 -383
- package/dist/mcp/tools/complete-reminder.js +1 -1
- package/dist/mcp/tools/create-reminder.js +1 -1
- package/dist/mcp/tools/create-task.js +616 -46
- package/dist/mcp/tools/deactivate-behavior.js +9 -1
- package/dist/mcp/tools/list-reminders.js +1 -1
- package/dist/mcp/tools/list-tasks.js +10 -2
- package/dist/mcp/tools/send-message.js +11 -3
- package/dist/mcp/tools/update-task.js +677 -39
- package/dist/runtime/index.js +425 -37
- package/dist/tui/App.js +365 -34
- package/package.json +5 -2
- package/src/commands/exe/intercom.md +6 -17
- package/dist/bin/wiki-sync.js +0 -2991
- package/dist/hooks/prompt-ingest-worker.js +0 -3979
package/dist/lib/tmux-routing.js
CHANGED
|
@@ -327,12 +327,25 @@ async function ensurePrivateDir(dirPath) {
|
|
|
327
327
|
} catch {
|
|
328
328
|
}
|
|
329
329
|
}
|
|
330
|
+
function ensurePrivateDirSync(dirPath) {
|
|
331
|
+
mkdirSync2(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
332
|
+
try {
|
|
333
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
334
|
+
} catch {
|
|
335
|
+
}
|
|
336
|
+
}
|
|
330
337
|
async function enforcePrivateFile(filePath) {
|
|
331
338
|
try {
|
|
332
339
|
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
333
340
|
} catch {
|
|
334
341
|
}
|
|
335
342
|
}
|
|
343
|
+
function enforcePrivateFileSync(filePath) {
|
|
344
|
+
try {
|
|
345
|
+
if (existsSync2(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
346
|
+
} catch {
|
|
347
|
+
}
|
|
348
|
+
}
|
|
336
349
|
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
337
350
|
var init_secure_files = __esm({
|
|
338
351
|
"src/lib/secure-files.ts"() {
|
|
@@ -343,6 +356,21 @@ var init_secure_files = __esm({
|
|
|
343
356
|
});
|
|
344
357
|
|
|
345
358
|
// src/lib/config.ts
|
|
359
|
+
var config_exports = {};
|
|
360
|
+
__export(config_exports, {
|
|
361
|
+
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
362
|
+
CONFIG_PATH: () => CONFIG_PATH,
|
|
363
|
+
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
364
|
+
DB_PATH: () => DB_PATH,
|
|
365
|
+
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
366
|
+
LEGACY_LANCE_PATH: () => LEGACY_LANCE_PATH,
|
|
367
|
+
MODELS_DIR: () => MODELS_DIR,
|
|
368
|
+
loadConfig: () => loadConfig,
|
|
369
|
+
loadConfigFrom: () => loadConfigFrom,
|
|
370
|
+
loadConfigSync: () => loadConfigSync,
|
|
371
|
+
migrateConfig: () => migrateConfig,
|
|
372
|
+
saveConfig: () => saveConfig
|
|
373
|
+
});
|
|
346
374
|
import { readFile, writeFile } from "fs/promises";
|
|
347
375
|
import { readFileSync as readFileSync2, existsSync as existsSync3, renameSync } from "fs";
|
|
348
376
|
import path2 from "path";
|
|
@@ -446,6 +474,46 @@ async function loadConfig() {
|
|
|
446
474
|
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
447
475
|
}
|
|
448
476
|
}
|
|
477
|
+
function loadConfigSync() {
|
|
478
|
+
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
479
|
+
const configPath = path2.join(dir, "config.json");
|
|
480
|
+
if (!existsSync3(configPath)) {
|
|
481
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
482
|
+
}
|
|
483
|
+
try {
|
|
484
|
+
const raw = readFileSync2(configPath, "utf-8");
|
|
485
|
+
let parsed = JSON.parse(raw);
|
|
486
|
+
parsed = migrateLegacyConfig(parsed);
|
|
487
|
+
const { config: migratedCfg } = migrateConfig(parsed);
|
|
488
|
+
normalizeScalingRoadmap(migratedCfg);
|
|
489
|
+
normalizeSessionLifecycle(migratedCfg);
|
|
490
|
+
normalizeAutoUpdate(migratedCfg);
|
|
491
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
|
|
492
|
+
} catch {
|
|
493
|
+
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
async function saveConfig(config) {
|
|
497
|
+
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
498
|
+
await ensurePrivateDir(dir);
|
|
499
|
+
const configPath = path2.join(dir, "config.json");
|
|
500
|
+
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
501
|
+
await enforcePrivateFile(configPath);
|
|
502
|
+
}
|
|
503
|
+
async function loadConfigFrom(configPath) {
|
|
504
|
+
const raw = await readFile(configPath, "utf-8");
|
|
505
|
+
try {
|
|
506
|
+
let parsed = JSON.parse(raw);
|
|
507
|
+
parsed = migrateLegacyConfig(parsed);
|
|
508
|
+
const { config: migratedCfg } = migrateConfig(parsed);
|
|
509
|
+
normalizeScalingRoadmap(migratedCfg);
|
|
510
|
+
normalizeSessionLifecycle(migratedCfg);
|
|
511
|
+
normalizeAutoUpdate(migratedCfg);
|
|
512
|
+
return { ...DEFAULT_CONFIG, ...migratedCfg };
|
|
513
|
+
} catch {
|
|
514
|
+
return { ...DEFAULT_CONFIG };
|
|
515
|
+
}
|
|
516
|
+
}
|
|
449
517
|
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
450
518
|
var init_config = __esm({
|
|
451
519
|
"src/lib/config.ts"() {
|
|
@@ -582,7 +650,7 @@ var init_agent_config = __esm({
|
|
|
582
650
|
init_secure_files();
|
|
583
651
|
AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
|
|
584
652
|
DEFAULT_MODELS = {
|
|
585
|
-
claude: "claude-opus-4",
|
|
653
|
+
claude: "claude-opus-4.6",
|
|
586
654
|
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
587
655
|
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
588
656
|
};
|
|
@@ -814,13 +882,22 @@ var init_database_adapter = __esm({
|
|
|
814
882
|
}
|
|
815
883
|
});
|
|
816
884
|
|
|
885
|
+
// src/types/memory.ts
|
|
886
|
+
var EMBEDDING_DIM;
|
|
887
|
+
var init_memory = __esm({
|
|
888
|
+
"src/types/memory.ts"() {
|
|
889
|
+
"use strict";
|
|
890
|
+
EMBEDDING_DIM = 1024;
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
|
|
817
894
|
// src/lib/database.ts
|
|
818
895
|
import { createClient } from "@libsql/client";
|
|
819
896
|
function getClient() {
|
|
820
897
|
if (!_adapterClient) {
|
|
821
898
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
822
899
|
}
|
|
823
|
-
if (process.env.DATABASE_URL) {
|
|
900
|
+
if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
|
|
824
901
|
return _adapterClient;
|
|
825
902
|
}
|
|
826
903
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
@@ -838,6 +915,7 @@ var init_database = __esm({
|
|
|
838
915
|
init_db_retry();
|
|
839
916
|
init_employees();
|
|
840
917
|
init_database_adapter();
|
|
918
|
+
init_memory();
|
|
841
919
|
_resilientClient = null;
|
|
842
920
|
_daemonClient = null;
|
|
843
921
|
_adapterClient = null;
|
|
@@ -1362,15 +1440,20 @@ async function createTaskCore(input) {
|
|
|
1362
1440
|
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
1363
1441
|
const resolved = resolveExeSession2();
|
|
1364
1442
|
if (resolved && input.projectName) {
|
|
1365
|
-
const
|
|
1366
|
-
|
|
1367
|
-
if (sessionProject && sessionProject !== input.projectName) {
|
|
1368
|
-
scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
|
|
1369
|
-
process.stderr.write(`[create_task] ${scopeMismatchWarning}
|
|
1370
|
-
`);
|
|
1371
|
-
earlySessionScope = null;
|
|
1372
|
-
} else {
|
|
1443
|
+
const isCoordinatorSession = !resolved.includes("-");
|
|
1444
|
+
if (isCoordinatorSession) {
|
|
1373
1445
|
earlySessionScope = resolved;
|
|
1446
|
+
} else {
|
|
1447
|
+
const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
|
|
1448
|
+
const sessionProject = getSessionProject2(resolved);
|
|
1449
|
+
if (sessionProject && sessionProject !== input.projectName) {
|
|
1450
|
+
scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
|
|
1451
|
+
process.stderr.write(`[create_task] ${scopeMismatchWarning}
|
|
1452
|
+
`);
|
|
1453
|
+
earlySessionScope = null;
|
|
1454
|
+
} else {
|
|
1455
|
+
earlySessionScope = resolved;
|
|
1456
|
+
}
|
|
1374
1457
|
}
|
|
1375
1458
|
} else {
|
|
1376
1459
|
earlySessionScope = resolved;
|
|
@@ -2255,6 +2338,10 @@ async function dispatchTaskToEmployee(input) {
|
|
|
2255
2338
|
if (transport.isAlive(sessionName)) {
|
|
2256
2339
|
const result = sendIntercom(sessionName);
|
|
2257
2340
|
const dispatched = result === "acknowledged" || result === "debounced" || result === "queued" ? "verified" : result === "delivered" ? "sent_unverified" : "session_dead";
|
|
2341
|
+
process.stderr.write(
|
|
2342
|
+
`[dispatch-audit] intercom \u2192 ${sessionName} | task="${input.title}" [${input.priority}] | result=${dispatched} (sendIntercom=${result})
|
|
2343
|
+
`
|
|
2344
|
+
);
|
|
2258
2345
|
return { dispatched, session: sessionName, crossProject };
|
|
2259
2346
|
} else {
|
|
2260
2347
|
const projectDir = input.projectDir ?? process.cwd();
|
|
@@ -2263,11 +2350,15 @@ async function dispatchTaskToEmployee(input) {
|
|
|
2263
2350
|
});
|
|
2264
2351
|
if (result.status === "failed") {
|
|
2265
2352
|
process.stderr.write(
|
|
2266
|
-
`[dispatch]
|
|
2353
|
+
`[dispatch-audit] SPAWN FAILED \u2192 ${input.assignedTo} | task="${input.title}" [${input.priority}] | error=${result.error}
|
|
2267
2354
|
`
|
|
2268
2355
|
);
|
|
2269
2356
|
return { dispatched: "session_missing" };
|
|
2270
2357
|
}
|
|
2358
|
+
process.stderr.write(
|
|
2359
|
+
`[dispatch-audit] SPAWNED \u2192 ${result.sessionName} | task="${input.title}" [${input.priority}]
|
|
2360
|
+
`
|
|
2361
|
+
);
|
|
2271
2362
|
return { dispatched: "spawned", session: result.sessionName, crossProject };
|
|
2272
2363
|
}
|
|
2273
2364
|
} catch {
|
|
@@ -2277,7 +2368,13 @@ async function dispatchTaskToEmployee(input) {
|
|
|
2277
2368
|
function notifyTaskDone() {
|
|
2278
2369
|
try {
|
|
2279
2370
|
const key = getSessionKey();
|
|
2280
|
-
if (key && !process.env.VITEST)
|
|
2371
|
+
if (key && !process.env.VITEST) {
|
|
2372
|
+
notifyParentExe(key);
|
|
2373
|
+
process.stderr.write(
|
|
2374
|
+
`[dispatch-audit] notifyTaskDone \u2192 parent coordinator (session=${key})
|
|
2375
|
+
`
|
|
2376
|
+
);
|
|
2377
|
+
}
|
|
2281
2378
|
} catch {
|
|
2282
2379
|
}
|
|
2283
2380
|
}
|
|
@@ -2298,16 +2395,557 @@ var init_tasks_notify = __esm({
|
|
|
2298
2395
|
}
|
|
2299
2396
|
});
|
|
2300
2397
|
|
|
2301
|
-
// src/lib/
|
|
2398
|
+
// src/lib/daemon-auth.ts
|
|
2302
2399
|
import crypto4 from "crypto";
|
|
2400
|
+
import path14 from "path";
|
|
2401
|
+
import { existsSync as existsSync12, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
2402
|
+
function normalizeToken(token) {
|
|
2403
|
+
if (!token) return null;
|
|
2404
|
+
const trimmed = token.trim();
|
|
2405
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2406
|
+
}
|
|
2407
|
+
function readDaemonToken() {
|
|
2408
|
+
try {
|
|
2409
|
+
if (!existsSync12(DAEMON_TOKEN_PATH)) return null;
|
|
2410
|
+
return normalizeToken(readFileSync10(DAEMON_TOKEN_PATH, "utf8"));
|
|
2411
|
+
} catch {
|
|
2412
|
+
return null;
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
function ensureDaemonToken(seed) {
|
|
2416
|
+
const existing = readDaemonToken();
|
|
2417
|
+
if (existing) return existing;
|
|
2418
|
+
const token = normalizeToken(seed) ?? crypto4.randomBytes(32).toString("hex");
|
|
2419
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
2420
|
+
writeFileSync6(DAEMON_TOKEN_PATH, `${token}
|
|
2421
|
+
`, "utf8");
|
|
2422
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
2423
|
+
return token;
|
|
2424
|
+
}
|
|
2425
|
+
var DAEMON_TOKEN_PATH;
|
|
2426
|
+
var init_daemon_auth = __esm({
|
|
2427
|
+
"src/lib/daemon-auth.ts"() {
|
|
2428
|
+
"use strict";
|
|
2429
|
+
init_config();
|
|
2430
|
+
init_secure_files();
|
|
2431
|
+
DAEMON_TOKEN_PATH = path14.join(EXE_AI_DIR, "exed.token");
|
|
2432
|
+
}
|
|
2433
|
+
});
|
|
2434
|
+
|
|
2435
|
+
// src/lib/exe-daemon-client.ts
|
|
2436
|
+
import net from "net";
|
|
2437
|
+
import os9 from "os";
|
|
2438
|
+
import { spawn } from "child_process";
|
|
2439
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2440
|
+
import { existsSync as existsSync13, unlinkSync as unlinkSync4, readFileSync as readFileSync11, openSync, closeSync, statSync } from "fs";
|
|
2441
|
+
import path15 from "path";
|
|
2442
|
+
import { fileURLToPath } from "url";
|
|
2443
|
+
function handleData(chunk) {
|
|
2444
|
+
_buffer += chunk.toString();
|
|
2445
|
+
if (_buffer.length > MAX_BUFFER) {
|
|
2446
|
+
_buffer = "";
|
|
2447
|
+
return;
|
|
2448
|
+
}
|
|
2449
|
+
let newlineIdx;
|
|
2450
|
+
while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
|
|
2451
|
+
const line = _buffer.slice(0, newlineIdx).trim();
|
|
2452
|
+
_buffer = _buffer.slice(newlineIdx + 1);
|
|
2453
|
+
if (!line) continue;
|
|
2454
|
+
try {
|
|
2455
|
+
const response = JSON.parse(line);
|
|
2456
|
+
const id = response.id;
|
|
2457
|
+
if (!id) continue;
|
|
2458
|
+
const entry = _pending.get(id);
|
|
2459
|
+
if (entry) {
|
|
2460
|
+
clearTimeout(entry.timer);
|
|
2461
|
+
_pending.delete(id);
|
|
2462
|
+
entry.resolve(response);
|
|
2463
|
+
}
|
|
2464
|
+
} catch {
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
function cleanupStaleFiles() {
|
|
2469
|
+
if (existsSync13(PID_PATH)) {
|
|
2470
|
+
try {
|
|
2471
|
+
const pid = parseInt(readFileSync11(PID_PATH, "utf8").trim(), 10);
|
|
2472
|
+
if (pid > 0) {
|
|
2473
|
+
try {
|
|
2474
|
+
process.kill(pid, 0);
|
|
2475
|
+
return;
|
|
2476
|
+
} catch {
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
} catch {
|
|
2480
|
+
}
|
|
2481
|
+
try {
|
|
2482
|
+
unlinkSync4(PID_PATH);
|
|
2483
|
+
} catch {
|
|
2484
|
+
}
|
|
2485
|
+
try {
|
|
2486
|
+
unlinkSync4(SOCKET_PATH);
|
|
2487
|
+
} catch {
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
function findPackageRoot() {
|
|
2492
|
+
let dir = path15.dirname(fileURLToPath(import.meta.url));
|
|
2493
|
+
const { root } = path15.parse(dir);
|
|
2494
|
+
while (dir !== root) {
|
|
2495
|
+
if (existsSync13(path15.join(dir, "package.json"))) return dir;
|
|
2496
|
+
dir = path15.dirname(dir);
|
|
2497
|
+
}
|
|
2498
|
+
return null;
|
|
2499
|
+
}
|
|
2500
|
+
function getAvailableMemoryGB() {
|
|
2501
|
+
if (process.platform === "darwin") {
|
|
2502
|
+
try {
|
|
2503
|
+
const { execSync: execSync7 } = __require("child_process");
|
|
2504
|
+
const vmstat = execSync7("vm_stat", { encoding: "utf8" });
|
|
2505
|
+
const pageSize = 16384;
|
|
2506
|
+
const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
|
|
2507
|
+
const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
|
|
2508
|
+
const free = vmstat.match(/Pages free:\s+(\d+)/);
|
|
2509
|
+
const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
|
|
2510
|
+
const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
|
|
2511
|
+
const freePages = free ? parseInt(free[1], 10) : 0;
|
|
2512
|
+
const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
|
|
2513
|
+
const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
|
|
2514
|
+
return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
|
|
2515
|
+
} catch {
|
|
2516
|
+
return os9.freemem() / (1024 * 1024 * 1024);
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
return os9.freemem() / (1024 * 1024 * 1024);
|
|
2520
|
+
}
|
|
2521
|
+
function spawnDaemon() {
|
|
2522
|
+
const freeGB = getAvailableMemoryGB();
|
|
2523
|
+
const totalGB = os9.totalmem() / (1024 * 1024 * 1024);
|
|
2524
|
+
if (totalGB <= 8) {
|
|
2525
|
+
process.stderr.write(
|
|
2526
|
+
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
2527
|
+
`
|
|
2528
|
+
);
|
|
2529
|
+
return;
|
|
2530
|
+
}
|
|
2531
|
+
if (totalGB <= 16 && freeGB < 2) {
|
|
2532
|
+
process.stderr.write(
|
|
2533
|
+
`[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
|
|
2534
|
+
`
|
|
2535
|
+
);
|
|
2536
|
+
return;
|
|
2537
|
+
}
|
|
2538
|
+
const pkgRoot = findPackageRoot();
|
|
2539
|
+
if (!pkgRoot) {
|
|
2540
|
+
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
2541
|
+
return;
|
|
2542
|
+
}
|
|
2543
|
+
const daemonPath = path15.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2544
|
+
if (!existsSync13(daemonPath)) {
|
|
2545
|
+
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
2546
|
+
`);
|
|
2547
|
+
return;
|
|
2548
|
+
}
|
|
2549
|
+
const resolvedPath = daemonPath;
|
|
2550
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
2551
|
+
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
2552
|
+
`);
|
|
2553
|
+
const logPath = path15.join(path15.dirname(SOCKET_PATH), "exed.log");
|
|
2554
|
+
let stderrFd = "ignore";
|
|
2555
|
+
try {
|
|
2556
|
+
stderrFd = openSync(logPath, "a");
|
|
2557
|
+
} catch {
|
|
2558
|
+
}
|
|
2559
|
+
const heapCapMB = totalGB <= 8 ? 256 : 512;
|
|
2560
|
+
const nodeArgs = [`--max-old-space-size=${heapCapMB}`, resolvedPath];
|
|
2561
|
+
const child = spawn(process.execPath, nodeArgs, {
|
|
2562
|
+
detached: true,
|
|
2563
|
+
stdio: ["ignore", "ignore", stderrFd],
|
|
2564
|
+
env: {
|
|
2565
|
+
...process.env,
|
|
2566
|
+
TMUX: void 0,
|
|
2567
|
+
// Daemon is global — must not inherit session scope
|
|
2568
|
+
TMUX_PANE: void 0,
|
|
2569
|
+
// Prevents resolveExeSession() from scoping to one session
|
|
2570
|
+
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
2571
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
2572
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
2573
|
+
}
|
|
2574
|
+
});
|
|
2575
|
+
child.unref();
|
|
2576
|
+
if (typeof stderrFd === "number") {
|
|
2577
|
+
try {
|
|
2578
|
+
closeSync(stderrFd);
|
|
2579
|
+
} catch {
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
function acquireSpawnLock() {
|
|
2584
|
+
try {
|
|
2585
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
2586
|
+
closeSync(fd);
|
|
2587
|
+
return true;
|
|
2588
|
+
} catch {
|
|
2589
|
+
try {
|
|
2590
|
+
const stat = statSync(SPAWN_LOCK_PATH);
|
|
2591
|
+
if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
|
|
2592
|
+
try {
|
|
2593
|
+
unlinkSync4(SPAWN_LOCK_PATH);
|
|
2594
|
+
} catch {
|
|
2595
|
+
}
|
|
2596
|
+
try {
|
|
2597
|
+
const fd = openSync(SPAWN_LOCK_PATH, "wx");
|
|
2598
|
+
closeSync(fd);
|
|
2599
|
+
return true;
|
|
2600
|
+
} catch {
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
} catch {
|
|
2604
|
+
}
|
|
2605
|
+
return false;
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
function releaseSpawnLock() {
|
|
2609
|
+
try {
|
|
2610
|
+
unlinkSync4(SPAWN_LOCK_PATH);
|
|
2611
|
+
} catch {
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
function connectToSocket() {
|
|
2615
|
+
return new Promise((resolve) => {
|
|
2616
|
+
if (_socket && _connected) {
|
|
2617
|
+
resolve(true);
|
|
2618
|
+
return;
|
|
2619
|
+
}
|
|
2620
|
+
const socket = net.createConnection({ path: SOCKET_PATH });
|
|
2621
|
+
const connectTimeout = setTimeout(() => {
|
|
2622
|
+
socket.destroy();
|
|
2623
|
+
resolve(false);
|
|
2624
|
+
}, 2e3);
|
|
2625
|
+
socket.on("connect", () => {
|
|
2626
|
+
clearTimeout(connectTimeout);
|
|
2627
|
+
_socket = socket;
|
|
2628
|
+
_connected = true;
|
|
2629
|
+
_buffer = "";
|
|
2630
|
+
socket.on("data", handleData);
|
|
2631
|
+
socket.on("close", () => {
|
|
2632
|
+
_connected = false;
|
|
2633
|
+
_socket = null;
|
|
2634
|
+
for (const [id, entry] of _pending) {
|
|
2635
|
+
clearTimeout(entry.timer);
|
|
2636
|
+
_pending.delete(id);
|
|
2637
|
+
entry.resolve({ error: "Connection closed" });
|
|
2638
|
+
}
|
|
2639
|
+
});
|
|
2640
|
+
socket.on("error", () => {
|
|
2641
|
+
_connected = false;
|
|
2642
|
+
_socket = null;
|
|
2643
|
+
});
|
|
2644
|
+
resolve(true);
|
|
2645
|
+
});
|
|
2646
|
+
socket.on("error", () => {
|
|
2647
|
+
clearTimeout(connectTimeout);
|
|
2648
|
+
resolve(false);
|
|
2649
|
+
});
|
|
2650
|
+
});
|
|
2651
|
+
}
|
|
2652
|
+
async function connectEmbedDaemon() {
|
|
2653
|
+
if (_socket && _connected) return true;
|
|
2654
|
+
if (await connectToSocket()) return true;
|
|
2655
|
+
if (acquireSpawnLock()) {
|
|
2656
|
+
try {
|
|
2657
|
+
cleanupStaleFiles();
|
|
2658
|
+
spawnDaemon();
|
|
2659
|
+
} finally {
|
|
2660
|
+
releaseSpawnLock();
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
const start = Date.now();
|
|
2664
|
+
let delay = 100;
|
|
2665
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2666
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
2667
|
+
if (await connectToSocket()) return true;
|
|
2668
|
+
delay = Math.min(delay * 2, 3e3);
|
|
2669
|
+
}
|
|
2670
|
+
return false;
|
|
2671
|
+
}
|
|
2672
|
+
function sendRequest(texts, priority) {
|
|
2673
|
+
return sendDaemonRequest({ texts, priority });
|
|
2674
|
+
}
|
|
2675
|
+
function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
2676
|
+
return new Promise((resolve) => {
|
|
2677
|
+
if (!_socket || !_connected) {
|
|
2678
|
+
resolve({ error: "Not connected" });
|
|
2679
|
+
return;
|
|
2680
|
+
}
|
|
2681
|
+
const id = randomUUID2();
|
|
2682
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
2683
|
+
const timer = setTimeout(() => {
|
|
2684
|
+
_pending.delete(id);
|
|
2685
|
+
resolve({ error: "Request timeout" });
|
|
2686
|
+
}, timeoutMs);
|
|
2687
|
+
_pending.set(id, { resolve, timer });
|
|
2688
|
+
try {
|
|
2689
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
2690
|
+
} catch {
|
|
2691
|
+
clearTimeout(timer);
|
|
2692
|
+
_pending.delete(id);
|
|
2693
|
+
resolve({ error: "Write failed" });
|
|
2694
|
+
}
|
|
2695
|
+
});
|
|
2696
|
+
}
|
|
2697
|
+
async function pingDaemon() {
|
|
2698
|
+
if (!_socket || !_connected) return null;
|
|
2699
|
+
const response = await sendDaemonRequest({ type: "health" }, 5e3);
|
|
2700
|
+
if (response.health) {
|
|
2701
|
+
return response.health;
|
|
2702
|
+
}
|
|
2703
|
+
return null;
|
|
2704
|
+
}
|
|
2705
|
+
function killAndRespawnDaemon() {
|
|
2706
|
+
if (!acquireSpawnLock()) {
|
|
2707
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
2708
|
+
if (_socket) {
|
|
2709
|
+
_socket.destroy();
|
|
2710
|
+
_socket = null;
|
|
2711
|
+
}
|
|
2712
|
+
_connected = false;
|
|
2713
|
+
_buffer = "";
|
|
2714
|
+
return;
|
|
2715
|
+
}
|
|
2716
|
+
try {
|
|
2717
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
2718
|
+
if (existsSync13(PID_PATH)) {
|
|
2719
|
+
try {
|
|
2720
|
+
const pid = parseInt(readFileSync11(PID_PATH, "utf8").trim(), 10);
|
|
2721
|
+
if (pid > 0) {
|
|
2722
|
+
try {
|
|
2723
|
+
process.kill(pid, "SIGKILL");
|
|
2724
|
+
} catch {
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
} catch {
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
if (_socket) {
|
|
2731
|
+
_socket.destroy();
|
|
2732
|
+
_socket = null;
|
|
2733
|
+
}
|
|
2734
|
+
_connected = false;
|
|
2735
|
+
_buffer = "";
|
|
2736
|
+
try {
|
|
2737
|
+
unlinkSync4(PID_PATH);
|
|
2738
|
+
} catch {
|
|
2739
|
+
}
|
|
2740
|
+
try {
|
|
2741
|
+
unlinkSync4(SOCKET_PATH);
|
|
2742
|
+
} catch {
|
|
2743
|
+
}
|
|
2744
|
+
spawnDaemon();
|
|
2745
|
+
} finally {
|
|
2746
|
+
releaseSpawnLock();
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
function isDaemonTooYoung() {
|
|
2750
|
+
try {
|
|
2751
|
+
const stat = statSync(PID_PATH);
|
|
2752
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
2753
|
+
} catch {
|
|
2754
|
+
return false;
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
async function retryThenRestart(doRequest, label) {
|
|
2758
|
+
const result = await doRequest();
|
|
2759
|
+
if (!result.error) {
|
|
2760
|
+
_consecutiveFailures = 0;
|
|
2761
|
+
return result;
|
|
2762
|
+
}
|
|
2763
|
+
_consecutiveFailures++;
|
|
2764
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
2765
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
2766
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
2767
|
+
`);
|
|
2768
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
2769
|
+
if (!_connected) {
|
|
2770
|
+
if (!await connectToSocket()) continue;
|
|
2771
|
+
}
|
|
2772
|
+
const retry = await doRequest();
|
|
2773
|
+
if (!retry.error) {
|
|
2774
|
+
_consecutiveFailures = 0;
|
|
2775
|
+
return retry;
|
|
2776
|
+
}
|
|
2777
|
+
_consecutiveFailures++;
|
|
2778
|
+
}
|
|
2779
|
+
if (isDaemonTooYoung()) {
|
|
2780
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
2781
|
+
`);
|
|
2782
|
+
return { error: result.error };
|
|
2783
|
+
}
|
|
2784
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
2785
|
+
`);
|
|
2786
|
+
killAndRespawnDaemon();
|
|
2787
|
+
const start = Date.now();
|
|
2788
|
+
let delay = 200;
|
|
2789
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2790
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
2791
|
+
if (await connectToSocket()) break;
|
|
2792
|
+
delay = Math.min(delay * 2, 3e3);
|
|
2793
|
+
}
|
|
2794
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
2795
|
+
const final = await doRequest();
|
|
2796
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
2797
|
+
return final;
|
|
2798
|
+
}
|
|
2799
|
+
async function embedViaClient(text, priority = "high") {
|
|
2800
|
+
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
2801
|
+
_requestCount++;
|
|
2802
|
+
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
2803
|
+
const health = await pingDaemon();
|
|
2804
|
+
if (!health && !isDaemonTooYoung()) {
|
|
2805
|
+
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
2806
|
+
`);
|
|
2807
|
+
killAndRespawnDaemon();
|
|
2808
|
+
const start = Date.now();
|
|
2809
|
+
let d = 200;
|
|
2810
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2811
|
+
await new Promise((r) => setTimeout(r, d));
|
|
2812
|
+
if (await connectToSocket()) break;
|
|
2813
|
+
d = Math.min(d * 2, 3e3);
|
|
2814
|
+
}
|
|
2815
|
+
if (!_connected) return null;
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
const result = await retryThenRestart(
|
|
2819
|
+
() => sendRequest([text], priority),
|
|
2820
|
+
"Embed"
|
|
2821
|
+
);
|
|
2822
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
2823
|
+
}
|
|
2824
|
+
function disconnectClient() {
|
|
2825
|
+
if (_socket) {
|
|
2826
|
+
_socket.destroy();
|
|
2827
|
+
_socket = null;
|
|
2828
|
+
}
|
|
2829
|
+
_connected = false;
|
|
2830
|
+
_buffer = "";
|
|
2831
|
+
for (const [id, entry] of _pending) {
|
|
2832
|
+
clearTimeout(entry.timer);
|
|
2833
|
+
_pending.delete(id);
|
|
2834
|
+
entry.resolve({ error: "Client disconnected" });
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
|
|
2838
|
+
var init_exe_daemon_client = __esm({
|
|
2839
|
+
"src/lib/exe-daemon-client.ts"() {
|
|
2840
|
+
"use strict";
|
|
2841
|
+
init_config();
|
|
2842
|
+
init_daemon_auth();
|
|
2843
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path15.join(EXE_AI_DIR, "exed.sock");
|
|
2844
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path15.join(EXE_AI_DIR, "exed.pid");
|
|
2845
|
+
SPAWN_LOCK_PATH = path15.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
2846
|
+
SPAWN_LOCK_STALE_MS = 3e4;
|
|
2847
|
+
CONNECT_TIMEOUT_MS = 15e3;
|
|
2848
|
+
REQUEST_TIMEOUT_MS = 3e4;
|
|
2849
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
2850
|
+
_socket = null;
|
|
2851
|
+
_connected = false;
|
|
2852
|
+
_buffer = "";
|
|
2853
|
+
_requestCount = 0;
|
|
2854
|
+
_consecutiveFailures = 0;
|
|
2855
|
+
HEALTH_CHECK_INTERVAL = 100;
|
|
2856
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
2857
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
2858
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
2859
|
+
_pending = /* @__PURE__ */ new Map();
|
|
2860
|
+
MAX_BUFFER = 1e7;
|
|
2861
|
+
}
|
|
2862
|
+
});
|
|
2863
|
+
|
|
2864
|
+
// src/lib/embedder.ts
|
|
2865
|
+
var embedder_exports = {};
|
|
2866
|
+
__export(embedder_exports, {
|
|
2867
|
+
disposeEmbedder: () => disposeEmbedder,
|
|
2868
|
+
embed: () => embed,
|
|
2869
|
+
embedDirect: () => embedDirect,
|
|
2870
|
+
getEmbedder: () => getEmbedder
|
|
2871
|
+
});
|
|
2872
|
+
async function getEmbedder() {
|
|
2873
|
+
const ok = await connectEmbedDaemon();
|
|
2874
|
+
if (!ok) {
|
|
2875
|
+
throw new Error(
|
|
2876
|
+
"Could not connect to embedding daemon. Ensure the model is installed (run /exe-setup)."
|
|
2877
|
+
);
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
async function embed(text) {
|
|
2881
|
+
const priority = process.env.EXE_EMBED_PRIORITY === "low" ? "low" : "high";
|
|
2882
|
+
const vector = await embedViaClient(text, priority);
|
|
2883
|
+
if (!vector) {
|
|
2884
|
+
throw new Error(
|
|
2885
|
+
"Embedding failed: daemon unavailable. Run /exe-setup to verify model installation."
|
|
2886
|
+
);
|
|
2887
|
+
}
|
|
2888
|
+
if (vector.length !== EMBEDDING_DIM) {
|
|
2889
|
+
throw new Error(
|
|
2890
|
+
`Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}. Ensure the correct Jina v5-small Q4_K_M GGUF model is installed.`
|
|
2891
|
+
);
|
|
2892
|
+
}
|
|
2893
|
+
return vector;
|
|
2894
|
+
}
|
|
2895
|
+
async function disposeEmbedder() {
|
|
2896
|
+
disconnectClient();
|
|
2897
|
+
}
|
|
2898
|
+
async function embedDirect(text) {
|
|
2899
|
+
const llamaCpp = await import("node-llama-cpp");
|
|
2900
|
+
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
2901
|
+
const { existsSync: existsSync15 } = await import("fs");
|
|
2902
|
+
const path18 = await import("path");
|
|
2903
|
+
const modelPath = path18.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
2904
|
+
if (!existsSync15(modelPath)) {
|
|
2905
|
+
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
2906
|
+
}
|
|
2907
|
+
const llama = await llamaCpp.getLlama();
|
|
2908
|
+
const model = await llama.loadModel({ modelPath });
|
|
2909
|
+
const context = await model.createEmbeddingContext();
|
|
2910
|
+
try {
|
|
2911
|
+
const embedding = await context.getEmbeddingFor(text);
|
|
2912
|
+
const vector = Array.from(embedding.vector);
|
|
2913
|
+
if (vector.length !== EMBEDDING_DIM) {
|
|
2914
|
+
throw new Error(
|
|
2915
|
+
`Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}.`
|
|
2916
|
+
);
|
|
2917
|
+
}
|
|
2918
|
+
return vector;
|
|
2919
|
+
} finally {
|
|
2920
|
+
await context.dispose();
|
|
2921
|
+
await model.dispose();
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
var init_embedder = __esm({
|
|
2925
|
+
"src/lib/embedder.ts"() {
|
|
2926
|
+
"use strict";
|
|
2927
|
+
init_memory();
|
|
2928
|
+
init_exe_daemon_client();
|
|
2929
|
+
}
|
|
2930
|
+
});
|
|
2931
|
+
|
|
2932
|
+
// src/lib/behaviors.ts
|
|
2933
|
+
import crypto5 from "crypto";
|
|
2303
2934
|
async function storeBehavior(opts) {
|
|
2304
2935
|
const client = getClient();
|
|
2305
|
-
const id =
|
|
2936
|
+
const id = crypto5.randomUUID();
|
|
2306
2937
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2938
|
+
let vector = null;
|
|
2939
|
+
try {
|
|
2940
|
+
const { embed: embed2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
|
|
2941
|
+
const vec = await embed2(opts.content);
|
|
2942
|
+
vector = new Float32Array(vec);
|
|
2943
|
+
} catch {
|
|
2944
|
+
}
|
|
2307
2945
|
await client.execute({
|
|
2308
|
-
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
2309
|
-
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)`,
|
|
2310
|
-
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now]
|
|
2946
|
+
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
|
|
2947
|
+
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
|
|
2948
|
+
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
|
|
2311
2949
|
});
|
|
2312
2950
|
return id;
|
|
2313
2951
|
}
|
|
@@ -2331,7 +2969,7 @@ __export(skill_learning_exports, {
|
|
|
2331
2969
|
storeTrajectory: () => storeTrajectory,
|
|
2332
2970
|
sweepTrajectories: () => sweepTrajectories
|
|
2333
2971
|
});
|
|
2334
|
-
import
|
|
2972
|
+
import crypto6 from "crypto";
|
|
2335
2973
|
async function extractTrajectory(taskId, agentId) {
|
|
2336
2974
|
const client = getClient();
|
|
2337
2975
|
const result = await client.execute({
|
|
@@ -2360,11 +2998,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
2360
2998
|
return signature;
|
|
2361
2999
|
}
|
|
2362
3000
|
function hashSignature(signature) {
|
|
2363
|
-
return
|
|
3001
|
+
return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
2364
3002
|
}
|
|
2365
3003
|
async function storeTrajectory(opts) {
|
|
2366
3004
|
const client = getClient();
|
|
2367
|
-
const id =
|
|
3005
|
+
const id = crypto6.randomUUID();
|
|
2368
3006
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2369
3007
|
const signatureHash = hashSignature(opts.signature);
|
|
2370
3008
|
await client.execute({
|
|
@@ -2629,8 +3267,8 @@ __export(tasks_exports, {
|
|
|
2629
3267
|
updateTaskStatus: () => updateTaskStatus,
|
|
2630
3268
|
writeCheckpoint: () => writeCheckpoint
|
|
2631
3269
|
});
|
|
2632
|
-
import
|
|
2633
|
-
import { writeFileSync as
|
|
3270
|
+
import path16 from "path";
|
|
3271
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
2634
3272
|
async function createTask(input) {
|
|
2635
3273
|
const result = await createTaskCore(input);
|
|
2636
3274
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -2649,14 +3287,14 @@ async function updateTask(input) {
|
|
|
2649
3287
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
2650
3288
|
try {
|
|
2651
3289
|
const agent = String(row.assigned_to);
|
|
2652
|
-
const cacheDir =
|
|
2653
|
-
const cachePath =
|
|
3290
|
+
const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
|
|
3291
|
+
const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
|
|
2654
3292
|
if (input.status === "in_progress") {
|
|
2655
3293
|
mkdirSync5(cacheDir, { recursive: true });
|
|
2656
|
-
|
|
3294
|
+
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
2657
3295
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
2658
3296
|
try {
|
|
2659
|
-
|
|
3297
|
+
unlinkSync5(cachePath);
|
|
2660
3298
|
} catch {
|
|
2661
3299
|
}
|
|
2662
3300
|
}
|
|
@@ -3098,7 +3736,7 @@ var init_capacity_monitor = __esm({
|
|
|
3098
3736
|
// src/lib/tmux-routing.ts
|
|
3099
3737
|
var tmux_routing_exports = {};
|
|
3100
3738
|
__export(tmux_routing_exports, {
|
|
3101
|
-
acquireSpawnLock: () =>
|
|
3739
|
+
acquireSpawnLock: () => acquireSpawnLock2,
|
|
3102
3740
|
employeeSessionName: () => employeeSessionName,
|
|
3103
3741
|
ensureEmployee: () => ensureEmployee,
|
|
3104
3742
|
extractRootExe: () => extractRootExe,
|
|
@@ -3114,20 +3752,20 @@ __export(tmux_routing_exports, {
|
|
|
3114
3752
|
notifyParentExe: () => notifyParentExe,
|
|
3115
3753
|
parseParentExe: () => parseParentExe,
|
|
3116
3754
|
registerParentExe: () => registerParentExe,
|
|
3117
|
-
releaseSpawnLock: () =>
|
|
3755
|
+
releaseSpawnLock: () => releaseSpawnLock2,
|
|
3118
3756
|
resolveExeSession: () => resolveExeSession,
|
|
3119
3757
|
sendIntercom: () => sendIntercom,
|
|
3120
3758
|
spawnEmployee: () => spawnEmployee,
|
|
3121
3759
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
3122
3760
|
});
|
|
3123
3761
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
3124
|
-
import { readFileSync as
|
|
3125
|
-
import
|
|
3126
|
-
import
|
|
3127
|
-
import { fileURLToPath } from "url";
|
|
3128
|
-
import { unlinkSync as
|
|
3762
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync3 } from "fs";
|
|
3763
|
+
import path17 from "path";
|
|
3764
|
+
import os10 from "os";
|
|
3765
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3766
|
+
import { unlinkSync as unlinkSync6 } from "fs";
|
|
3129
3767
|
function spawnLockPath(sessionName) {
|
|
3130
|
-
return
|
|
3768
|
+
return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
3131
3769
|
}
|
|
3132
3770
|
function isProcessAlive(pid) {
|
|
3133
3771
|
try {
|
|
@@ -3137,14 +3775,14 @@ function isProcessAlive(pid) {
|
|
|
3137
3775
|
return false;
|
|
3138
3776
|
}
|
|
3139
3777
|
}
|
|
3140
|
-
function
|
|
3141
|
-
if (!
|
|
3778
|
+
function acquireSpawnLock2(sessionName) {
|
|
3779
|
+
if (!existsSync14(SPAWN_LOCK_DIR)) {
|
|
3142
3780
|
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
3143
3781
|
}
|
|
3144
3782
|
const lockFile = spawnLockPath(sessionName);
|
|
3145
|
-
if (
|
|
3783
|
+
if (existsSync14(lockFile)) {
|
|
3146
3784
|
try {
|
|
3147
|
-
const lock = JSON.parse(
|
|
3785
|
+
const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
|
|
3148
3786
|
const age = Date.now() - lock.timestamp;
|
|
3149
3787
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
3150
3788
|
return false;
|
|
@@ -3152,25 +3790,25 @@ function acquireSpawnLock(sessionName) {
|
|
|
3152
3790
|
} catch {
|
|
3153
3791
|
}
|
|
3154
3792
|
}
|
|
3155
|
-
|
|
3793
|
+
writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
3156
3794
|
return true;
|
|
3157
3795
|
}
|
|
3158
|
-
function
|
|
3796
|
+
function releaseSpawnLock2(sessionName) {
|
|
3159
3797
|
try {
|
|
3160
|
-
|
|
3798
|
+
unlinkSync6(spawnLockPath(sessionName));
|
|
3161
3799
|
} catch {
|
|
3162
3800
|
}
|
|
3163
3801
|
}
|
|
3164
3802
|
function resolveBehaviorsExporterScript() {
|
|
3165
3803
|
try {
|
|
3166
|
-
const thisFile =
|
|
3167
|
-
const scriptPath =
|
|
3168
|
-
|
|
3804
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
3805
|
+
const scriptPath = path17.join(
|
|
3806
|
+
path17.dirname(thisFile),
|
|
3169
3807
|
"..",
|
|
3170
3808
|
"bin",
|
|
3171
3809
|
"exe-export-behaviors.js"
|
|
3172
3810
|
);
|
|
3173
|
-
return
|
|
3811
|
+
return existsSync14(scriptPath) ? scriptPath : null;
|
|
3174
3812
|
} catch {
|
|
3175
3813
|
return null;
|
|
3176
3814
|
}
|
|
@@ -3236,12 +3874,12 @@ function extractRootExe(name) {
|
|
|
3236
3874
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
3237
3875
|
}
|
|
3238
3876
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
3239
|
-
if (!
|
|
3877
|
+
if (!existsSync14(SESSION_CACHE)) {
|
|
3240
3878
|
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
3241
3879
|
}
|
|
3242
3880
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
3243
|
-
const filePath =
|
|
3244
|
-
|
|
3881
|
+
const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
3882
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
3245
3883
|
parentExe: rootExe,
|
|
3246
3884
|
dispatchedBy: dispatchedBy || rootExe,
|
|
3247
3885
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -3249,7 +3887,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
3249
3887
|
}
|
|
3250
3888
|
function getParentExe(sessionKey) {
|
|
3251
3889
|
try {
|
|
3252
|
-
const data = JSON.parse(
|
|
3890
|
+
const data = JSON.parse(readFileSync12(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
3253
3891
|
return data.parentExe || null;
|
|
3254
3892
|
} catch {
|
|
3255
3893
|
return null;
|
|
@@ -3257,8 +3895,8 @@ function getParentExe(sessionKey) {
|
|
|
3257
3895
|
}
|
|
3258
3896
|
function getDispatchedBy(sessionKey) {
|
|
3259
3897
|
try {
|
|
3260
|
-
const data = JSON.parse(
|
|
3261
|
-
|
|
3898
|
+
const data = JSON.parse(readFileSync12(
|
|
3899
|
+
path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
3262
3900
|
"utf8"
|
|
3263
3901
|
));
|
|
3264
3902
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -3293,10 +3931,10 @@ function isEmployeeAlive(sessionName) {
|
|
|
3293
3931
|
}
|
|
3294
3932
|
function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
|
|
3295
3933
|
const base = employeeSessionName(employeeName, exeSession);
|
|
3296
|
-
if (!isAlive(base) &&
|
|
3934
|
+
if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
|
|
3297
3935
|
for (let i = 2; i <= maxInstances; i++) {
|
|
3298
3936
|
const candidate = employeeSessionName(employeeName, exeSession, i);
|
|
3299
|
-
if (!isAlive(candidate) &&
|
|
3937
|
+
if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
|
|
3300
3938
|
}
|
|
3301
3939
|
return null;
|
|
3302
3940
|
}
|
|
@@ -3328,8 +3966,8 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
3328
3966
|
}
|
|
3329
3967
|
function readDebounceState() {
|
|
3330
3968
|
try {
|
|
3331
|
-
if (!
|
|
3332
|
-
const raw = JSON.parse(
|
|
3969
|
+
if (!existsSync14(DEBOUNCE_FILE)) return {};
|
|
3970
|
+
const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
|
|
3333
3971
|
const state = {};
|
|
3334
3972
|
for (const [key, val] of Object.entries(raw)) {
|
|
3335
3973
|
if (typeof val === "number") {
|
|
@@ -3345,8 +3983,8 @@ function readDebounceState() {
|
|
|
3345
3983
|
}
|
|
3346
3984
|
function writeDebounceState(state) {
|
|
3347
3985
|
try {
|
|
3348
|
-
if (!
|
|
3349
|
-
|
|
3986
|
+
if (!existsSync14(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
3987
|
+
writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
|
|
3350
3988
|
} catch {
|
|
3351
3989
|
}
|
|
3352
3990
|
}
|
|
@@ -3410,7 +4048,7 @@ function isSessionBusy(sessionName) {
|
|
|
3410
4048
|
function isExeSession(sessionName) {
|
|
3411
4049
|
const matchesBaseWithInstance = (baseName) => sessionName === baseName || sessionName.startsWith(baseName) && /^\d+$/.test(sessionName.slice(baseName.length));
|
|
3412
4050
|
const coordinatorName = getCoordinatorName();
|
|
3413
|
-
return matchesBaseWithInstance(coordinatorName)
|
|
4051
|
+
return matchesBaseWithInstance(coordinatorName);
|
|
3414
4052
|
}
|
|
3415
4053
|
function sendIntercom(targetSession) {
|
|
3416
4054
|
const transport = getTransport();
|
|
@@ -3445,8 +4083,8 @@ function sendIntercom(targetSession) {
|
|
|
3445
4083
|
try {
|
|
3446
4084
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
3447
4085
|
const agent = baseAgentName(rawAgent);
|
|
3448
|
-
const markerPath =
|
|
3449
|
-
if (
|
|
4086
|
+
const markerPath = path17.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
4087
|
+
if (existsSync14(markerPath)) {
|
|
3450
4088
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker + not idle \u2014 will auto-chain)`);
|
|
3451
4089
|
return "debounced";
|
|
3452
4090
|
}
|
|
@@ -3456,8 +4094,8 @@ function sendIntercom(targetSession) {
|
|
|
3456
4094
|
try {
|
|
3457
4095
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
3458
4096
|
const agent = baseAgentName(rawAgent);
|
|
3459
|
-
const taskDir =
|
|
3460
|
-
if (
|
|
4097
|
+
const taskDir = path17.join(process.cwd(), "exe", agent);
|
|
4098
|
+
if (existsSync14(taskDir)) {
|
|
3461
4099
|
const files = readdirSync3(taskDir).filter(
|
|
3462
4100
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
3463
4101
|
);
|
|
@@ -3624,26 +4262,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3624
4262
|
const transport = getTransport();
|
|
3625
4263
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
3626
4264
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
3627
|
-
const logDir =
|
|
3628
|
-
const logFile =
|
|
3629
|
-
if (!
|
|
4265
|
+
const logDir = path17.join(os10.homedir(), ".exe-os", "session-logs");
|
|
4266
|
+
const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
4267
|
+
if (!existsSync14(logDir)) {
|
|
3630
4268
|
mkdirSync6(logDir, { recursive: true });
|
|
3631
4269
|
}
|
|
3632
4270
|
transport.kill(sessionName);
|
|
3633
4271
|
let cleanupSuffix = "";
|
|
3634
4272
|
try {
|
|
3635
|
-
const thisFile =
|
|
3636
|
-
const cleanupScript =
|
|
3637
|
-
if (
|
|
4273
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
4274
|
+
const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
4275
|
+
if (existsSync14(cleanupScript)) {
|
|
3638
4276
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
3639
4277
|
}
|
|
3640
4278
|
} catch {
|
|
3641
4279
|
}
|
|
3642
4280
|
try {
|
|
3643
|
-
const claudeJsonPath =
|
|
4281
|
+
const claudeJsonPath = path17.join(os10.homedir(), ".claude.json");
|
|
3644
4282
|
let claudeJson = {};
|
|
3645
4283
|
try {
|
|
3646
|
-
claudeJson = JSON.parse(
|
|
4284
|
+
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
3647
4285
|
} catch {
|
|
3648
4286
|
}
|
|
3649
4287
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -3651,17 +4289,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3651
4289
|
const trustDir = opts?.cwd ?? projectDir;
|
|
3652
4290
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
3653
4291
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
3654
|
-
|
|
4292
|
+
writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
3655
4293
|
} catch {
|
|
3656
4294
|
}
|
|
3657
4295
|
try {
|
|
3658
|
-
const settingsDir =
|
|
4296
|
+
const settingsDir = path17.join(os10.homedir(), ".claude", "projects");
|
|
3659
4297
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
3660
|
-
const projSettingsDir =
|
|
3661
|
-
const settingsPath =
|
|
4298
|
+
const projSettingsDir = path17.join(settingsDir, normalizedKey);
|
|
4299
|
+
const settingsPath = path17.join(projSettingsDir, "settings.json");
|
|
3662
4300
|
let settings = {};
|
|
3663
4301
|
try {
|
|
3664
|
-
settings = JSON.parse(
|
|
4302
|
+
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
3665
4303
|
} catch {
|
|
3666
4304
|
}
|
|
3667
4305
|
const perms = settings.permissions ?? {};
|
|
@@ -3690,7 +4328,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3690
4328
|
perms.allow = allow;
|
|
3691
4329
|
settings.permissions = perms;
|
|
3692
4330
|
mkdirSync6(projSettingsDir, { recursive: true });
|
|
3693
|
-
|
|
4331
|
+
writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
3694
4332
|
}
|
|
3695
4333
|
} catch {
|
|
3696
4334
|
}
|
|
@@ -3705,8 +4343,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3705
4343
|
let behaviorsFlag = "";
|
|
3706
4344
|
let legacyFallbackWarned = false;
|
|
3707
4345
|
if (!useExeAgent && !useBinSymlink) {
|
|
3708
|
-
const identityPath =
|
|
3709
|
-
|
|
4346
|
+
const identityPath = path17.join(
|
|
4347
|
+
os10.homedir(),
|
|
3710
4348
|
".exe-os",
|
|
3711
4349
|
"identity",
|
|
3712
4350
|
`${employeeName}.md`
|
|
@@ -3715,13 +4353,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3715
4353
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
3716
4354
|
if (hasAgentFlag) {
|
|
3717
4355
|
identityFlag = ` --agent ${employeeName}`;
|
|
3718
|
-
} else if (
|
|
4356
|
+
} else if (existsSync14(identityPath)) {
|
|
3719
4357
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
3720
4358
|
legacyFallbackWarned = true;
|
|
3721
4359
|
}
|
|
3722
4360
|
const behaviorsFile = exportBehaviorsSync(
|
|
3723
4361
|
employeeName,
|
|
3724
|
-
|
|
4362
|
+
path17.basename(spawnCwd),
|
|
3725
4363
|
sessionName
|
|
3726
4364
|
);
|
|
3727
4365
|
if (behaviorsFile) {
|
|
@@ -3736,16 +4374,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3736
4374
|
}
|
|
3737
4375
|
let sessionContextFlag = "";
|
|
3738
4376
|
try {
|
|
3739
|
-
const ctxDir =
|
|
4377
|
+
const ctxDir = path17.join(os10.homedir(), ".exe-os", "session-cache");
|
|
3740
4378
|
mkdirSync6(ctxDir, { recursive: true });
|
|
3741
|
-
const ctxFile =
|
|
4379
|
+
const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
|
|
3742
4380
|
const ctxContent = [
|
|
3743
4381
|
`## Session Context`,
|
|
3744
4382
|
`You are running in tmux session: ${sessionName}.`,
|
|
3745
4383
|
`Your parent coordinator session is ${exeSession}.`,
|
|
3746
4384
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
3747
4385
|
].join("\n");
|
|
3748
|
-
|
|
4386
|
+
writeFileSync8(ctxFile, ctxContent);
|
|
3749
4387
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
3750
4388
|
} catch {
|
|
3751
4389
|
}
|
|
@@ -3819,14 +4457,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3819
4457
|
command: spawnCommand
|
|
3820
4458
|
});
|
|
3821
4459
|
if (spawnResult.error) {
|
|
3822
|
-
|
|
4460
|
+
releaseSpawnLock2(sessionName);
|
|
3823
4461
|
return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
|
|
3824
4462
|
}
|
|
3825
4463
|
transport.pipeLog(sessionName, logFile);
|
|
3826
4464
|
try {
|
|
3827
4465
|
const mySession = getMySession();
|
|
3828
|
-
const dispatchInfo =
|
|
3829
|
-
|
|
4466
|
+
const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
4467
|
+
writeFileSync8(dispatchInfo, JSON.stringify({
|
|
3830
4468
|
dispatchedBy: mySession,
|
|
3831
4469
|
rootExe: exeSession,
|
|
3832
4470
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -3864,7 +4502,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3864
4502
|
}
|
|
3865
4503
|
}
|
|
3866
4504
|
if (!booted) {
|
|
3867
|
-
|
|
4505
|
+
releaseSpawnLock2(sessionName);
|
|
3868
4506
|
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
3869
4507
|
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
3870
4508
|
}
|
|
@@ -3882,7 +4520,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
3882
4520
|
pid: 0,
|
|
3883
4521
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3884
4522
|
});
|
|
3885
|
-
|
|
4523
|
+
releaseSpawnLock2(sessionName);
|
|
3886
4524
|
return { sessionName };
|
|
3887
4525
|
}
|
|
3888
4526
|
var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, CODEX_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
|
|
@@ -3899,22 +4537,22 @@ var init_tmux_routing = __esm({
|
|
|
3899
4537
|
init_intercom_queue();
|
|
3900
4538
|
init_plan_limits();
|
|
3901
4539
|
init_employees();
|
|
3902
|
-
SPAWN_LOCK_DIR =
|
|
3903
|
-
SESSION_CACHE =
|
|
4540
|
+
SPAWN_LOCK_DIR = path17.join(os10.homedir(), ".exe-os", "spawn-locks");
|
|
4541
|
+
SESSION_CACHE = path17.join(os10.homedir(), ".exe-os", "session-cache");
|
|
3904
4542
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
3905
4543
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
3906
4544
|
VERIFY_PANE_LINES = 200;
|
|
3907
4545
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
3908
4546
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
3909
|
-
INTERCOM_LOG2 =
|
|
3910
|
-
DEBOUNCE_FILE =
|
|
4547
|
+
INTERCOM_LOG2 = path17.join(os10.homedir(), ".exe-os", "intercom.log");
|
|
4548
|
+
DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
|
|
3911
4549
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
3912
4550
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
3913
4551
|
}
|
|
3914
4552
|
});
|
|
3915
4553
|
init_tmux_routing();
|
|
3916
4554
|
export {
|
|
3917
|
-
acquireSpawnLock,
|
|
4555
|
+
acquireSpawnLock2 as acquireSpawnLock,
|
|
3918
4556
|
employeeSessionName,
|
|
3919
4557
|
ensureEmployee,
|
|
3920
4558
|
extractRootExe,
|
|
@@ -3930,7 +4568,7 @@ export {
|
|
|
3930
4568
|
notifyParentExe,
|
|
3931
4569
|
parseParentExe,
|
|
3932
4570
|
registerParentExe,
|
|
3933
|
-
releaseSpawnLock,
|
|
4571
|
+
releaseSpawnLock2 as releaseSpawnLock,
|
|
3934
4572
|
resolveExeSession,
|
|
3935
4573
|
sendIntercom,
|
|
3936
4574
|
spawnEmployee,
|