@askexenow/exe-os 0.9.8 → 0.9.9
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 +222 -49
- package/dist/bin/backfill-responses.js +221 -48
- package/dist/bin/backfill-vectors.js +225 -52
- package/dist/bin/cleanup-stale-review-tasks.js +150 -28
- package/dist/bin/cli.js +1295 -856
- package/dist/bin/exe-agent-config.js +36 -8
- package/dist/bin/exe-agent.js +14 -4
- package/dist/bin/exe-assign.js +221 -48
- package/dist/bin/exe-boot.js +778 -427
- package/dist/bin/exe-call.js +41 -13
- package/dist/bin/exe-cloud.js +163 -58
- package/dist/bin/exe-dispatch.js +276 -139
- package/dist/bin/exe-doctor.js +145 -27
- package/dist/bin/exe-export-behaviors.js +141 -23
- package/dist/bin/exe-forget.js +137 -19
- package/dist/bin/exe-gateway.js +677 -388
- package/dist/bin/exe-heartbeat.js +227 -108
- package/dist/bin/exe-kill.js +138 -20
- package/dist/bin/exe-launch-agent.js +172 -39
- package/dist/bin/exe-link.js +291 -100
- package/dist/bin/exe-new-employee.js +214 -106
- package/dist/bin/exe-pending-messages.js +395 -33
- package/dist/bin/exe-pending-notifications.js +684 -99
- package/dist/bin/exe-pending-reviews.js +420 -74
- package/dist/bin/exe-rename.js +147 -49
- package/dist/bin/exe-review.js +138 -20
- package/dist/bin/exe-search.js +240 -69
- package/dist/bin/exe-session-cleanup.js +440 -250
- package/dist/bin/exe-settings.js +61 -17
- package/dist/bin/exe-start-codex.js +158 -39
- package/dist/bin/exe-start-opencode.js +157 -38
- package/dist/bin/exe-status.js +151 -29
- package/dist/bin/exe-team.js +138 -20
- package/dist/bin/git-sweep.js +404 -212
- package/dist/bin/graph-backfill.js +137 -19
- package/dist/bin/graph-export.js +140 -22
- package/dist/bin/install.js +90 -61
- package/dist/bin/scan-tasks.js +412 -220
- package/dist/bin/setup.js +564 -293
- package/dist/bin/shard-migrate.js +139 -21
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +137 -19
- package/dist/gateway/index.js +533 -320
- package/dist/hooks/bug-report-worker.js +344 -193
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +402 -210
- package/dist/hooks/error-recall.js +245 -74
- package/dist/hooks/exe-heartbeat-hook.js +16 -6
- package/dist/hooks/ingest-worker.js +3423 -3157
- package/dist/hooks/ingest.js +832 -97
- package/dist/hooks/instructions-loaded.js +227 -54
- package/dist/hooks/notification.js +216 -43
- package/dist/hooks/post-compact.js +239 -62
- package/dist/hooks/pre-compact.js +408 -216
- package/dist/hooks/pre-tool-use.js +268 -90
- package/dist/hooks/prompt-ingest-worker.js +352 -102
- package/dist/hooks/prompt-submit.js +541 -328
- package/dist/hooks/response-ingest-worker.js +372 -122
- package/dist/hooks/session-end.js +443 -240
- package/dist/hooks/session-start.js +313 -127
- package/dist/hooks/stop.js +293 -98
- package/dist/hooks/subagent-stop.js +239 -62
- package/dist/hooks/summary-worker.js +568 -236
- package/dist/index.js +538 -324
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +284 -105
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +16 -6
- package/dist/lib/database.js +123 -25
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +123 -25
- package/dist/lib/device-registry.js +133 -35
- package/dist/lib/embedder.js +107 -32
- package/dist/lib/employee-templates.js +14 -4
- package/dist/lib/employees.js +41 -13
- package/dist/lib/exe-daemon-client.js +88 -22
- package/dist/lib/exe-daemon.js +935 -587
- package/dist/lib/hybrid-search.js +240 -69
- package/dist/lib/identity.js +18 -8
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +116 -56
- package/dist/lib/reminders.js +14 -4
- package/dist/lib/schedules.js +137 -19
- package/dist/lib/skill-learning.js +33 -6
- package/dist/lib/store.js +137 -19
- package/dist/lib/task-router.js +14 -4
- package/dist/lib/tasks.js +280 -234
- package/dist/lib/tmux-routing.js +172 -125
- package/dist/lib/token-spend.js +26 -8
- package/dist/mcp/server.js +1326 -609
- package/dist/mcp/tools/complete-reminder.js +14 -4
- package/dist/mcp/tools/create-reminder.js +14 -4
- package/dist/mcp/tools/create-task.js +306 -248
- package/dist/mcp/tools/deactivate-behavior.js +16 -6
- package/dist/mcp/tools/list-reminders.js +14 -4
- package/dist/mcp/tools/list-tasks.js +123 -107
- package/dist/mcp/tools/send-message.js +75 -29
- package/dist/mcp/tools/update-task.js +1848 -199
- package/dist/runtime/index.js +441 -248
- package/dist/tui/App.js +761 -424
- package/package.json +1 -1
package/dist/gateway/index.js
CHANGED
|
@@ -391,6 +391,44 @@ var init_db_retry = __esm({
|
|
|
391
391
|
}
|
|
392
392
|
});
|
|
393
393
|
|
|
394
|
+
// src/lib/secure-files.ts
|
|
395
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
396
|
+
import { chmod, mkdir } from "fs/promises";
|
|
397
|
+
async function ensurePrivateDir(dirPath) {
|
|
398
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
399
|
+
try {
|
|
400
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
401
|
+
} catch {
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function ensurePrivateDirSync(dirPath) {
|
|
405
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
406
|
+
try {
|
|
407
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
408
|
+
} catch {
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
async function enforcePrivateFile(filePath) {
|
|
412
|
+
try {
|
|
413
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
414
|
+
} catch {
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function enforcePrivateFileSync(filePath) {
|
|
418
|
+
try {
|
|
419
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
420
|
+
} catch {
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
424
|
+
var init_secure_files = __esm({
|
|
425
|
+
"src/lib/secure-files.ts"() {
|
|
426
|
+
"use strict";
|
|
427
|
+
PRIVATE_DIR_MODE = 448;
|
|
428
|
+
PRIVATE_FILE_MODE = 384;
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
394
432
|
// src/lib/config.ts
|
|
395
433
|
var config_exports = {};
|
|
396
434
|
__export(config_exports, {
|
|
@@ -407,8 +445,8 @@ __export(config_exports, {
|
|
|
407
445
|
migrateConfig: () => migrateConfig,
|
|
408
446
|
saveConfig: () => saveConfig
|
|
409
447
|
});
|
|
410
|
-
import { readFile, writeFile
|
|
411
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
448
|
+
import { readFile, writeFile } from "fs/promises";
|
|
449
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
412
450
|
import path from "path";
|
|
413
451
|
import os from "os";
|
|
414
452
|
function resolveDataDir() {
|
|
@@ -416,7 +454,7 @@ function resolveDataDir() {
|
|
|
416
454
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
417
455
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
418
456
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
419
|
-
if (!
|
|
457
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
420
458
|
try {
|
|
421
459
|
renameSync(legacyDir, newDir);
|
|
422
460
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -479,9 +517,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
479
517
|
}
|
|
480
518
|
async function loadConfig() {
|
|
481
519
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
482
|
-
await
|
|
520
|
+
await ensurePrivateDir(dir);
|
|
483
521
|
const configPath = path.join(dir, "config.json");
|
|
484
|
-
if (!
|
|
522
|
+
if (!existsSync2(configPath)) {
|
|
485
523
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
486
524
|
}
|
|
487
525
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -494,6 +532,7 @@ async function loadConfig() {
|
|
|
494
532
|
`);
|
|
495
533
|
try {
|
|
496
534
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
535
|
+
await enforcePrivateFile(configPath);
|
|
497
536
|
} catch {
|
|
498
537
|
}
|
|
499
538
|
}
|
|
@@ -512,7 +551,7 @@ async function loadConfig() {
|
|
|
512
551
|
function loadConfigSync() {
|
|
513
552
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
514
553
|
const configPath = path.join(dir, "config.json");
|
|
515
|
-
if (!
|
|
554
|
+
if (!existsSync2(configPath)) {
|
|
516
555
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
517
556
|
}
|
|
518
557
|
try {
|
|
@@ -530,12 +569,10 @@ function loadConfigSync() {
|
|
|
530
569
|
}
|
|
531
570
|
async function saveConfig(config2) {
|
|
532
571
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
533
|
-
await
|
|
572
|
+
await ensurePrivateDir(dir);
|
|
534
573
|
const configPath = path.join(dir, "config.json");
|
|
535
574
|
await writeFile(configPath, JSON.stringify(config2, null, 2) + "\n");
|
|
536
|
-
|
|
537
|
-
await chmod(configPath, 384);
|
|
538
|
-
}
|
|
575
|
+
await enforcePrivateFile(configPath);
|
|
539
576
|
}
|
|
540
577
|
async function loadConfigFrom(configPath) {
|
|
541
578
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -555,6 +592,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
555
592
|
var init_config = __esm({
|
|
556
593
|
"src/lib/config.ts"() {
|
|
557
594
|
"use strict";
|
|
595
|
+
init_secure_files();
|
|
558
596
|
EXE_AI_DIR = resolveDataDir();
|
|
559
597
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
560
598
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -671,10 +709,10 @@ __export(agent_config_exports, {
|
|
|
671
709
|
saveAgentConfig: () => saveAgentConfig,
|
|
672
710
|
setAgentRuntime: () => setAgentRuntime
|
|
673
711
|
});
|
|
674
|
-
import { readFileSync as readFileSync2, writeFileSync, existsSync as
|
|
712
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
675
713
|
import path2 from "path";
|
|
676
714
|
function loadAgentConfig() {
|
|
677
|
-
if (!
|
|
715
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
678
716
|
try {
|
|
679
717
|
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
680
718
|
} catch {
|
|
@@ -683,8 +721,9 @@ function loadAgentConfig() {
|
|
|
683
721
|
}
|
|
684
722
|
function saveAgentConfig(config2) {
|
|
685
723
|
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
686
|
-
|
|
724
|
+
ensurePrivateDirSync(dir);
|
|
687
725
|
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
726
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
688
727
|
}
|
|
689
728
|
function getAgentRuntime(agentId) {
|
|
690
729
|
const config2 = loadAgentConfig();
|
|
@@ -724,6 +763,7 @@ var init_agent_config = __esm({
|
|
|
724
763
|
"use strict";
|
|
725
764
|
init_config();
|
|
726
765
|
init_runtime_table();
|
|
766
|
+
init_secure_files();
|
|
727
767
|
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
728
768
|
KNOWN_RUNTIMES = {
|
|
729
769
|
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
@@ -771,7 +811,7 @@ __export(employees_exports, {
|
|
|
771
811
|
validateEmployeeName: () => validateEmployeeName
|
|
772
812
|
});
|
|
773
813
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
774
|
-
import { existsSync as
|
|
814
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
775
815
|
import { execSync } from "child_process";
|
|
776
816
|
import path3 from "path";
|
|
777
817
|
import os2 from "os";
|
|
@@ -810,7 +850,7 @@ function validateEmployeeName(name) {
|
|
|
810
850
|
return { valid: true };
|
|
811
851
|
}
|
|
812
852
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
813
|
-
if (!
|
|
853
|
+
if (!existsSync4(employeesPath)) {
|
|
814
854
|
return [];
|
|
815
855
|
}
|
|
816
856
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -825,7 +865,7 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
|
825
865
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
826
866
|
}
|
|
827
867
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
828
|
-
if (!
|
|
868
|
+
if (!existsSync4(employeesPath)) return [];
|
|
829
869
|
try {
|
|
830
870
|
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
831
871
|
} catch {
|
|
@@ -873,7 +913,7 @@ function appendToCoordinatorTeam(employee) {
|
|
|
873
913
|
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
874
914
|
if (!coordinator) return;
|
|
875
915
|
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
876
|
-
if (!
|
|
916
|
+
if (!existsSync4(idPath)) return;
|
|
877
917
|
const content = readFileSync3(idPath, "utf-8");
|
|
878
918
|
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
879
919
|
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
@@ -927,9 +967,9 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
927
967
|
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
928
968
|
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
929
969
|
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
930
|
-
if (
|
|
970
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
931
971
|
renameSync2(oldPath, newPath);
|
|
932
|
-
} else if (
|
|
972
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
933
973
|
const content = readFileSync3(oldPath, "utf-8");
|
|
934
974
|
writeFileSync2(newPath, content, "utf-8");
|
|
935
975
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
@@ -972,7 +1012,7 @@ function registerBinSymlinks(name) {
|
|
|
972
1012
|
for (const suffix of ["", "-opencode"]) {
|
|
973
1013
|
const linkName = `${name}${suffix}`;
|
|
974
1014
|
const linkPath = path3.join(binDir, linkName);
|
|
975
|
-
if (
|
|
1015
|
+
if (existsSync4(linkPath)) {
|
|
976
1016
|
skipped.push(linkName);
|
|
977
1017
|
continue;
|
|
978
1018
|
}
|
|
@@ -1925,6 +1965,7 @@ async function ensureSchema() {
|
|
|
1925
1965
|
project TEXT NOT NULL,
|
|
1926
1966
|
summary TEXT NOT NULL,
|
|
1927
1967
|
task_file TEXT,
|
|
1968
|
+
session_scope TEXT,
|
|
1928
1969
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1929
1970
|
created_at TEXT NOT NULL
|
|
1930
1971
|
);
|
|
@@ -1933,7 +1974,7 @@ async function ensureSchema() {
|
|
|
1933
1974
|
ON notifications(read);
|
|
1934
1975
|
|
|
1935
1976
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
1936
|
-
ON notifications(agent_id);
|
|
1977
|
+
ON notifications(agent_id, session_scope);
|
|
1937
1978
|
|
|
1938
1979
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
1939
1980
|
ON notifications(task_file);
|
|
@@ -1971,6 +2012,7 @@ async function ensureSchema() {
|
|
|
1971
2012
|
target_agent TEXT NOT NULL,
|
|
1972
2013
|
target_project TEXT,
|
|
1973
2014
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
2015
|
+
session_scope TEXT,
|
|
1974
2016
|
content TEXT NOT NULL,
|
|
1975
2017
|
priority TEXT DEFAULT 'normal',
|
|
1976
2018
|
status TEXT DEFAULT 'pending',
|
|
@@ -1984,10 +2026,31 @@ async function ensureSchema() {
|
|
|
1984
2026
|
);
|
|
1985
2027
|
|
|
1986
2028
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
1987
|
-
ON messages(target_agent, status);
|
|
2029
|
+
ON messages(target_agent, session_scope, status);
|
|
1988
2030
|
|
|
1989
2031
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
1990
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
2032
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
2033
|
+
`);
|
|
2034
|
+
try {
|
|
2035
|
+
await client.execute({
|
|
2036
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
2037
|
+
args: []
|
|
2038
|
+
});
|
|
2039
|
+
} catch {
|
|
2040
|
+
}
|
|
2041
|
+
try {
|
|
2042
|
+
await client.execute({
|
|
2043
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
2044
|
+
args: []
|
|
2045
|
+
});
|
|
2046
|
+
} catch {
|
|
2047
|
+
}
|
|
2048
|
+
await client.executeMultiple(`
|
|
2049
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
2050
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
2051
|
+
|
|
2052
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
2053
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
1991
2054
|
`);
|
|
1992
2055
|
try {
|
|
1993
2056
|
await client.execute({
|
|
@@ -2571,6 +2634,13 @@ async function ensureSchema() {
|
|
|
2571
2634
|
} catch {
|
|
2572
2635
|
}
|
|
2573
2636
|
}
|
|
2637
|
+
try {
|
|
2638
|
+
await client.execute({
|
|
2639
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
2640
|
+
args: []
|
|
2641
|
+
});
|
|
2642
|
+
} catch {
|
|
2643
|
+
}
|
|
2574
2644
|
}
|
|
2575
2645
|
async function disposeDatabase() {
|
|
2576
2646
|
if (_walCheckpointTimer) {
|
|
@@ -2617,13 +2687,50 @@ var init_memory = __esm({
|
|
|
2617
2687
|
}
|
|
2618
2688
|
});
|
|
2619
2689
|
|
|
2690
|
+
// src/lib/daemon-auth.ts
|
|
2691
|
+
import crypto from "crypto";
|
|
2692
|
+
import path5 from "path";
|
|
2693
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
2694
|
+
function normalizeToken(token) {
|
|
2695
|
+
if (!token) return null;
|
|
2696
|
+
const trimmed = token.trim();
|
|
2697
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2698
|
+
}
|
|
2699
|
+
function readDaemonToken() {
|
|
2700
|
+
try {
|
|
2701
|
+
if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
|
|
2702
|
+
return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
|
|
2703
|
+
} catch {
|
|
2704
|
+
return null;
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
function ensureDaemonToken(seed) {
|
|
2708
|
+
const existing = readDaemonToken();
|
|
2709
|
+
if (existing) return existing;
|
|
2710
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
2711
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
2712
|
+
writeFileSync3(DAEMON_TOKEN_PATH, `${token}
|
|
2713
|
+
`, "utf8");
|
|
2714
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
2715
|
+
return token;
|
|
2716
|
+
}
|
|
2717
|
+
var DAEMON_TOKEN_PATH;
|
|
2718
|
+
var init_daemon_auth = __esm({
|
|
2719
|
+
"src/lib/daemon-auth.ts"() {
|
|
2720
|
+
"use strict";
|
|
2721
|
+
init_config();
|
|
2722
|
+
init_secure_files();
|
|
2723
|
+
DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
|
|
2724
|
+
}
|
|
2725
|
+
});
|
|
2726
|
+
|
|
2620
2727
|
// src/lib/exe-daemon-client.ts
|
|
2621
2728
|
import net from "net";
|
|
2622
2729
|
import os4 from "os";
|
|
2623
2730
|
import { spawn } from "child_process";
|
|
2624
2731
|
import { randomUUID } from "crypto";
|
|
2625
|
-
import { existsSync as
|
|
2626
|
-
import
|
|
2732
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
|
|
2733
|
+
import path6 from "path";
|
|
2627
2734
|
import { fileURLToPath } from "url";
|
|
2628
2735
|
function handleData(chunk) {
|
|
2629
2736
|
_buffer += chunk.toString();
|
|
@@ -2651,9 +2758,9 @@ function handleData(chunk) {
|
|
|
2651
2758
|
}
|
|
2652
2759
|
}
|
|
2653
2760
|
function cleanupStaleFiles() {
|
|
2654
|
-
if (
|
|
2761
|
+
if (existsSync6(PID_PATH)) {
|
|
2655
2762
|
try {
|
|
2656
|
-
const pid = parseInt(
|
|
2763
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
2657
2764
|
if (pid > 0) {
|
|
2658
2765
|
try {
|
|
2659
2766
|
process.kill(pid, 0);
|
|
@@ -2674,11 +2781,11 @@ function cleanupStaleFiles() {
|
|
|
2674
2781
|
}
|
|
2675
2782
|
}
|
|
2676
2783
|
function findPackageRoot() {
|
|
2677
|
-
let dir =
|
|
2678
|
-
const { root } =
|
|
2784
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
2785
|
+
const { root } = path6.parse(dir);
|
|
2679
2786
|
while (dir !== root) {
|
|
2680
|
-
if (
|
|
2681
|
-
dir =
|
|
2787
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
2788
|
+
dir = path6.dirname(dir);
|
|
2682
2789
|
}
|
|
2683
2790
|
return null;
|
|
2684
2791
|
}
|
|
@@ -2704,16 +2811,17 @@ function spawnDaemon() {
|
|
|
2704
2811
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
2705
2812
|
return;
|
|
2706
2813
|
}
|
|
2707
|
-
const daemonPath =
|
|
2708
|
-
if (!
|
|
2814
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2815
|
+
if (!existsSync6(daemonPath)) {
|
|
2709
2816
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
2710
2817
|
`);
|
|
2711
2818
|
return;
|
|
2712
2819
|
}
|
|
2713
2820
|
const resolvedPath = daemonPath;
|
|
2821
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
2714
2822
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
2715
2823
|
`);
|
|
2716
|
-
const logPath =
|
|
2824
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
2717
2825
|
let stderrFd = "ignore";
|
|
2718
2826
|
try {
|
|
2719
2827
|
stderrFd = openSync(logPath, "a");
|
|
@@ -2731,7 +2839,8 @@ function spawnDaemon() {
|
|
|
2731
2839
|
TMUX_PANE: void 0,
|
|
2732
2840
|
// Prevents resolveExeSession() from scoping to one session
|
|
2733
2841
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
2734
|
-
EXE_DAEMON_PID: PID_PATH
|
|
2842
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
2843
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
2735
2844
|
}
|
|
2736
2845
|
});
|
|
2737
2846
|
child.unref();
|
|
@@ -2841,13 +2950,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
2841
2950
|
return;
|
|
2842
2951
|
}
|
|
2843
2952
|
const id = randomUUID();
|
|
2953
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
2844
2954
|
const timer = setTimeout(() => {
|
|
2845
2955
|
_pending.delete(id);
|
|
2846
2956
|
resolve({ error: "Request timeout" });
|
|
2847
2957
|
}, timeoutMs);
|
|
2848
2958
|
_pending.set(id, { resolve, timer });
|
|
2849
2959
|
try {
|
|
2850
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
2960
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
2851
2961
|
} catch {
|
|
2852
2962
|
clearTimeout(timer);
|
|
2853
2963
|
_pending.delete(id);
|
|
@@ -2876,9 +2986,9 @@ function killAndRespawnDaemon() {
|
|
|
2876
2986
|
}
|
|
2877
2987
|
try {
|
|
2878
2988
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
2879
|
-
if (
|
|
2989
|
+
if (existsSync6(PID_PATH)) {
|
|
2880
2990
|
try {
|
|
2881
|
-
const pid = parseInt(
|
|
2991
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
2882
2992
|
if (pid > 0) {
|
|
2883
2993
|
try {
|
|
2884
2994
|
process.kill(pid, "SIGKILL");
|
|
@@ -2995,17 +3105,19 @@ function disconnectClient() {
|
|
|
2995
3105
|
entry.resolve({ error: "Client disconnected" });
|
|
2996
3106
|
}
|
|
2997
3107
|
}
|
|
2998
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
|
|
3108
|
+
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;
|
|
2999
3109
|
var init_exe_daemon_client = __esm({
|
|
3000
3110
|
"src/lib/exe-daemon-client.ts"() {
|
|
3001
3111
|
"use strict";
|
|
3002
3112
|
init_config();
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3113
|
+
init_daemon_auth();
|
|
3114
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
3115
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
3116
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
3006
3117
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
3007
3118
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
3008
3119
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
3120
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
3009
3121
|
_socket = null;
|
|
3010
3122
|
_connected = false;
|
|
3011
3123
|
_buffer = "";
|
|
@@ -3057,10 +3169,10 @@ async function disposeEmbedder() {
|
|
|
3057
3169
|
async function embedDirect(text) {
|
|
3058
3170
|
const llamaCpp = await import("node-llama-cpp");
|
|
3059
3171
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
3060
|
-
const { existsSync:
|
|
3061
|
-
const
|
|
3062
|
-
const modelPath =
|
|
3063
|
-
if (!
|
|
3172
|
+
const { existsSync: existsSync18 } = await import("fs");
|
|
3173
|
+
const path22 = await import("path");
|
|
3174
|
+
const modelPath = path22.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
3175
|
+
if (!existsSync18(modelPath)) {
|
|
3064
3176
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
3065
3177
|
}
|
|
3066
3178
|
const llama = await llamaCpp.getLlama();
|
|
@@ -3090,14 +3202,14 @@ var init_embedder = __esm({
|
|
|
3090
3202
|
|
|
3091
3203
|
// src/lib/keychain.ts
|
|
3092
3204
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
3093
|
-
import { existsSync as
|
|
3094
|
-
import
|
|
3205
|
+
import { existsSync as existsSync7 } from "fs";
|
|
3206
|
+
import path7 from "path";
|
|
3095
3207
|
import os5 from "os";
|
|
3096
3208
|
function getKeyDir() {
|
|
3097
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
3209
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
|
|
3098
3210
|
}
|
|
3099
3211
|
function getKeyPath() {
|
|
3100
|
-
return
|
|
3212
|
+
return path7.join(getKeyDir(), "master.key");
|
|
3101
3213
|
}
|
|
3102
3214
|
async function tryKeytar() {
|
|
3103
3215
|
try {
|
|
@@ -3118,7 +3230,7 @@ async function getMasterKey() {
|
|
|
3118
3230
|
}
|
|
3119
3231
|
}
|
|
3120
3232
|
const keyPath = getKeyPath();
|
|
3121
|
-
if (!
|
|
3233
|
+
if (!existsSync7(keyPath)) {
|
|
3122
3234
|
process.stderr.write(
|
|
3123
3235
|
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
3124
3236
|
`
|
|
@@ -3150,6 +3262,7 @@ var shard_manager_exports = {};
|
|
|
3150
3262
|
__export(shard_manager_exports, {
|
|
3151
3263
|
disposeShards: () => disposeShards,
|
|
3152
3264
|
ensureShardSchema: () => ensureShardSchema,
|
|
3265
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
3153
3266
|
getReadyShardClient: () => getReadyShardClient,
|
|
3154
3267
|
getShardClient: () => getShardClient,
|
|
3155
3268
|
getShardsDir: () => getShardsDir,
|
|
@@ -3158,15 +3271,18 @@ __export(shard_manager_exports, {
|
|
|
3158
3271
|
listShards: () => listShards,
|
|
3159
3272
|
shardExists: () => shardExists
|
|
3160
3273
|
});
|
|
3161
|
-
import
|
|
3162
|
-
import { existsSync as
|
|
3274
|
+
import path8 from "path";
|
|
3275
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
3163
3276
|
import { createClient as createClient2 } from "@libsql/client";
|
|
3164
3277
|
function initShardManager(encryptionKey) {
|
|
3165
3278
|
_encryptionKey = encryptionKey;
|
|
3166
|
-
if (!
|
|
3279
|
+
if (!existsSync8(SHARDS_DIR)) {
|
|
3167
3280
|
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
3168
3281
|
}
|
|
3169
3282
|
_shardingEnabled = true;
|
|
3283
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
3284
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
3285
|
+
_evictionTimer.unref();
|
|
3170
3286
|
}
|
|
3171
3287
|
function isShardingEnabled() {
|
|
3172
3288
|
return _shardingEnabled;
|
|
@@ -3183,21 +3299,28 @@ function getShardClient(projectName) {
|
|
|
3183
3299
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
3184
3300
|
}
|
|
3185
3301
|
const cached = _shards.get(safeName);
|
|
3186
|
-
if (cached)
|
|
3187
|
-
|
|
3302
|
+
if (cached) {
|
|
3303
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
3304
|
+
return cached;
|
|
3305
|
+
}
|
|
3306
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
3307
|
+
evictLRU();
|
|
3308
|
+
}
|
|
3309
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
3188
3310
|
const client = createClient2({
|
|
3189
3311
|
url: `file:${dbPath}`,
|
|
3190
3312
|
encryptionKey: _encryptionKey
|
|
3191
3313
|
});
|
|
3192
3314
|
_shards.set(safeName, client);
|
|
3315
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
3193
3316
|
return client;
|
|
3194
3317
|
}
|
|
3195
3318
|
function shardExists(projectName) {
|
|
3196
3319
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
3197
|
-
return
|
|
3320
|
+
return existsSync8(path8.join(SHARDS_DIR, `${safeName}.db`));
|
|
3198
3321
|
}
|
|
3199
3322
|
function listShards() {
|
|
3200
|
-
if (!
|
|
3323
|
+
if (!existsSync8(SHARDS_DIR)) return [];
|
|
3201
3324
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
3202
3325
|
}
|
|
3203
3326
|
async function ensureShardSchema(client) {
|
|
@@ -3249,6 +3372,8 @@ async function ensureShardSchema(client) {
|
|
|
3249
3372
|
for (const col of [
|
|
3250
3373
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
3251
3374
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
3375
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
3376
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
3252
3377
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
3253
3378
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
3254
3379
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -3386,21 +3511,69 @@ async function getReadyShardClient(projectName) {
|
|
|
3386
3511
|
await ensureShardSchema(client);
|
|
3387
3512
|
return client;
|
|
3388
3513
|
}
|
|
3514
|
+
function evictLRU() {
|
|
3515
|
+
let oldest = null;
|
|
3516
|
+
let oldestTime = Infinity;
|
|
3517
|
+
for (const [name, time] of _shardLastAccess) {
|
|
3518
|
+
if (time < oldestTime) {
|
|
3519
|
+
oldestTime = time;
|
|
3520
|
+
oldest = name;
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
if (oldest) {
|
|
3524
|
+
const client = _shards.get(oldest);
|
|
3525
|
+
if (client) {
|
|
3526
|
+
client.close();
|
|
3527
|
+
}
|
|
3528
|
+
_shards.delete(oldest);
|
|
3529
|
+
_shardLastAccess.delete(oldest);
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
function evictIdleShards() {
|
|
3533
|
+
const now = Date.now();
|
|
3534
|
+
const toEvict = [];
|
|
3535
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
3536
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
3537
|
+
toEvict.push(name);
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
for (const name of toEvict) {
|
|
3541
|
+
const client = _shards.get(name);
|
|
3542
|
+
if (client) {
|
|
3543
|
+
client.close();
|
|
3544
|
+
}
|
|
3545
|
+
_shards.delete(name);
|
|
3546
|
+
_shardLastAccess.delete(name);
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
function getOpenShardCount() {
|
|
3550
|
+
return _shards.size;
|
|
3551
|
+
}
|
|
3389
3552
|
function disposeShards() {
|
|
3553
|
+
if (_evictionTimer) {
|
|
3554
|
+
clearInterval(_evictionTimer);
|
|
3555
|
+
_evictionTimer = null;
|
|
3556
|
+
}
|
|
3390
3557
|
for (const [, client] of _shards) {
|
|
3391
3558
|
client.close();
|
|
3392
3559
|
}
|
|
3393
3560
|
_shards.clear();
|
|
3561
|
+
_shardLastAccess.clear();
|
|
3394
3562
|
_shardingEnabled = false;
|
|
3395
3563
|
_encryptionKey = null;
|
|
3396
3564
|
}
|
|
3397
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
3565
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
3398
3566
|
var init_shard_manager = __esm({
|
|
3399
3567
|
"src/lib/shard-manager.ts"() {
|
|
3400
3568
|
"use strict";
|
|
3401
3569
|
init_config();
|
|
3402
|
-
SHARDS_DIR =
|
|
3570
|
+
SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
|
|
3571
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
3572
|
+
MAX_OPEN_SHARDS = 10;
|
|
3573
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
3403
3574
|
_shards = /* @__PURE__ */ new Map();
|
|
3575
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
3576
|
+
_evictionTimer = null;
|
|
3404
3577
|
_encryptionKey = null;
|
|
3405
3578
|
_shardingEnabled = false;
|
|
3406
3579
|
}
|
|
@@ -4172,8 +4345,8 @@ __export(wiki_client_exports, {
|
|
|
4172
4345
|
listDocuments: () => listDocuments,
|
|
4173
4346
|
listWorkspaces: () => listWorkspaces
|
|
4174
4347
|
});
|
|
4175
|
-
async function wikiFetch(config2,
|
|
4176
|
-
const url = `${config2.baseUrl}/api/v1${
|
|
4348
|
+
async function wikiFetch(config2, path22, method = "GET", body) {
|
|
4349
|
+
const url = `${config2.baseUrl}/api/v1${path22}`;
|
|
4177
4350
|
const headers = {
|
|
4178
4351
|
Authorization: `Bearer ${config2.apiKey}`,
|
|
4179
4352
|
"Content-Type": "application/json"
|
|
@@ -4206,7 +4379,7 @@ async function wikiFetch(config2, path21, method = "GET", body) {
|
|
|
4206
4379
|
}
|
|
4207
4380
|
}
|
|
4208
4381
|
if (!response.ok) {
|
|
4209
|
-
throw new Error(`Wiki API ${method} ${
|
|
4382
|
+
throw new Error(`Wiki API ${method} ${path22}: ${response.status} ${response.statusText}`);
|
|
4210
4383
|
}
|
|
4211
4384
|
return response.json();
|
|
4212
4385
|
} finally {
|
|
@@ -4499,13 +4672,13 @@ __export(graph_rag_exports, {
|
|
|
4499
4672
|
resolveAlias: () => resolveAlias,
|
|
4500
4673
|
storeExtraction: () => storeExtraction
|
|
4501
4674
|
});
|
|
4502
|
-
import
|
|
4675
|
+
import crypto2 from "crypto";
|
|
4503
4676
|
function normalizeEntityName(name) {
|
|
4504
4677
|
return name.replace(/\s*\([^)]*\)\s*/g, "").trim().toLowerCase();
|
|
4505
4678
|
}
|
|
4506
4679
|
function entityId(name, type) {
|
|
4507
4680
|
const normalized = normalizeEntityName(name);
|
|
4508
|
-
return
|
|
4681
|
+
return crypto2.createHash("sha256").update(`${normalized}::${type.toLowerCase()}`).digest("hex").slice(0, 16);
|
|
4509
4682
|
}
|
|
4510
4683
|
async function resolveAlias(client, name) {
|
|
4511
4684
|
const normalized = normalizeEntityName(name);
|
|
@@ -4755,7 +4928,7 @@ async function storeExtraction(client, extraction, memoryId, timestamp) {
|
|
|
4755
4928
|
const targetAlias = await resolveAlias(client, r.target);
|
|
4756
4929
|
const sourceId = sourceAlias ?? entityId(r.source, r.sourceType);
|
|
4757
4930
|
const targetId = targetAlias ?? entityId(r.target, r.targetType);
|
|
4758
|
-
const relId =
|
|
4931
|
+
const relId = crypto2.randomUUID().slice(0, 16);
|
|
4759
4932
|
try {
|
|
4760
4933
|
await client.execute({
|
|
4761
4934
|
sql: `INSERT OR IGNORE INTO entities (id, name, type, first_seen, last_seen)
|
|
@@ -4818,7 +4991,7 @@ async function storeExtraction(client, extraction, memoryId, timestamp) {
|
|
|
4818
4991
|
}
|
|
4819
4992
|
}
|
|
4820
4993
|
for (const h of extraction.hyperedges) {
|
|
4821
|
-
const hId =
|
|
4994
|
+
const hId = crypto2.randomUUID().slice(0, 16);
|
|
4822
4995
|
try {
|
|
4823
4996
|
await client.execute({
|
|
4824
4997
|
sql: `INSERT OR IGNORE INTO hyperedges (id, label, relation, confidence, timestamp)
|
|
@@ -4882,7 +5055,7 @@ async function extractBatch(client, batchSize = 50, model = "claude-haiku-4-5-20
|
|
|
4882
5055
|
totalEntities += stored.entitiesStored;
|
|
4883
5056
|
totalRelationships += stored.relationshipsStored;
|
|
4884
5057
|
}
|
|
4885
|
-
const contentHash =
|
|
5058
|
+
const contentHash = crypto2.createHash("sha256").update(rawContent).digest("hex").slice(0, 32);
|
|
4886
5059
|
await client.execute({
|
|
4887
5060
|
sql: "UPDATE memories SET graph_extracted = 1, content_hash = ?, graph_extracted_hash = ? WHERE id = ?",
|
|
4888
5061
|
args: [contentHash, contentHash, memoryId]
|
|
@@ -5227,13 +5400,13 @@ __export(whatsapp_accounts_exports, {
|
|
|
5227
5400
|
getDefaultAccount: () => getDefaultAccount,
|
|
5228
5401
|
loadAccounts: () => loadAccounts
|
|
5229
5402
|
});
|
|
5230
|
-
import { readFileSync as
|
|
5403
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
5231
5404
|
import { join as join2 } from "path";
|
|
5232
5405
|
import { homedir as homedir2 } from "os";
|
|
5233
5406
|
function loadAccounts() {
|
|
5234
5407
|
if (cachedAccounts !== null) return cachedAccounts;
|
|
5235
5408
|
try {
|
|
5236
|
-
const raw =
|
|
5409
|
+
const raw = readFileSync6(CONFIG_PATH2, "utf8");
|
|
5237
5410
|
const parsed = JSON.parse(raw);
|
|
5238
5411
|
if (!Array.isArray(parsed)) {
|
|
5239
5412
|
console.warn("[whatsapp] Config is not an array, ignoring");
|
|
@@ -5273,12 +5446,12 @@ var init_whatsapp_accounts = __esm({
|
|
|
5273
5446
|
});
|
|
5274
5447
|
|
|
5275
5448
|
// src/lib/session-registry.ts
|
|
5276
|
-
import { readFileSync as
|
|
5277
|
-
import
|
|
5449
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync9 } from "fs";
|
|
5450
|
+
import path10 from "path";
|
|
5278
5451
|
import os7 from "os";
|
|
5279
5452
|
function registerSession(entry) {
|
|
5280
|
-
const dir =
|
|
5281
|
-
if (!
|
|
5453
|
+
const dir = path10.dirname(REGISTRY_PATH);
|
|
5454
|
+
if (!existsSync9(dir)) {
|
|
5282
5455
|
mkdirSync4(dir, { recursive: true });
|
|
5283
5456
|
}
|
|
5284
5457
|
const sessions = listSessions();
|
|
@@ -5288,11 +5461,11 @@ function registerSession(entry) {
|
|
|
5288
5461
|
} else {
|
|
5289
5462
|
sessions.push(entry);
|
|
5290
5463
|
}
|
|
5291
|
-
|
|
5464
|
+
writeFileSync4(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
5292
5465
|
}
|
|
5293
5466
|
function listSessions() {
|
|
5294
5467
|
try {
|
|
5295
|
-
const raw =
|
|
5468
|
+
const raw = readFileSync7(REGISTRY_PATH, "utf8");
|
|
5296
5469
|
return JSON.parse(raw);
|
|
5297
5470
|
} catch {
|
|
5298
5471
|
return [];
|
|
@@ -5302,7 +5475,7 @@ var REGISTRY_PATH;
|
|
|
5302
5475
|
var init_session_registry = __esm({
|
|
5303
5476
|
"src/lib/session-registry.ts"() {
|
|
5304
5477
|
"use strict";
|
|
5305
|
-
REGISTRY_PATH =
|
|
5478
|
+
REGISTRY_PATH = path10.join(os7.homedir(), ".exe-os", "session-registry.json");
|
|
5306
5479
|
}
|
|
5307
5480
|
});
|
|
5308
5481
|
|
|
@@ -5563,17 +5736,17 @@ __export(intercom_queue_exports, {
|
|
|
5563
5736
|
queueIntercom: () => queueIntercom,
|
|
5564
5737
|
readQueue: () => readQueue
|
|
5565
5738
|
});
|
|
5566
|
-
import { readFileSync as
|
|
5567
|
-
import
|
|
5739
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync10, mkdirSync as mkdirSync5 } from "fs";
|
|
5740
|
+
import path11 from "path";
|
|
5568
5741
|
import os8 from "os";
|
|
5569
5742
|
function ensureDir() {
|
|
5570
|
-
const dir =
|
|
5571
|
-
if (!
|
|
5743
|
+
const dir = path11.dirname(QUEUE_PATH);
|
|
5744
|
+
if (!existsSync10(dir)) mkdirSync5(dir, { recursive: true });
|
|
5572
5745
|
}
|
|
5573
5746
|
function readQueue() {
|
|
5574
5747
|
try {
|
|
5575
|
-
if (!
|
|
5576
|
-
return JSON.parse(
|
|
5748
|
+
if (!existsSync10(QUEUE_PATH)) return [];
|
|
5749
|
+
return JSON.parse(readFileSync8(QUEUE_PATH, "utf8"));
|
|
5577
5750
|
} catch {
|
|
5578
5751
|
return [];
|
|
5579
5752
|
}
|
|
@@ -5581,7 +5754,7 @@ function readQueue() {
|
|
|
5581
5754
|
function writeQueue(queue) {
|
|
5582
5755
|
ensureDir();
|
|
5583
5756
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
5584
|
-
|
|
5757
|
+
writeFileSync5(tmp, JSON.stringify(queue, null, 2));
|
|
5585
5758
|
renameSync3(tmp, QUEUE_PATH);
|
|
5586
5759
|
}
|
|
5587
5760
|
function queueIntercom(targetSession, reason) {
|
|
@@ -5673,26 +5846,29 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
5673
5846
|
var init_intercom_queue = __esm({
|
|
5674
5847
|
"src/lib/intercom-queue.ts"() {
|
|
5675
5848
|
"use strict";
|
|
5676
|
-
QUEUE_PATH =
|
|
5849
|
+
QUEUE_PATH = path11.join(os8.homedir(), ".exe-os", "intercom-queue.json");
|
|
5677
5850
|
MAX_RETRIES2 = 5;
|
|
5678
5851
|
TTL_MS = 60 * 60 * 1e3;
|
|
5679
|
-
INTERCOM_LOG =
|
|
5852
|
+
INTERCOM_LOG = path11.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
5680
5853
|
}
|
|
5681
5854
|
});
|
|
5682
5855
|
|
|
5683
5856
|
// src/lib/license.ts
|
|
5684
|
-
import { readFileSync as
|
|
5857
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync6 } from "fs";
|
|
5685
5858
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
5686
|
-
import
|
|
5859
|
+
import { createRequire as createRequire2 } from "module";
|
|
5860
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
5861
|
+
import os9 from "os";
|
|
5862
|
+
import path12 from "path";
|
|
5687
5863
|
import { jwtVerify, importSPKI } from "jose";
|
|
5688
5864
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
5689
5865
|
var init_license = __esm({
|
|
5690
5866
|
"src/lib/license.ts"() {
|
|
5691
5867
|
"use strict";
|
|
5692
5868
|
init_config();
|
|
5693
|
-
LICENSE_PATH =
|
|
5694
|
-
CACHE_PATH =
|
|
5695
|
-
DEVICE_ID_PATH =
|
|
5869
|
+
LICENSE_PATH = path12.join(EXE_AI_DIR, "license.key");
|
|
5870
|
+
CACHE_PATH = path12.join(EXE_AI_DIR, "license-cache.json");
|
|
5871
|
+
DEVICE_ID_PATH = path12.join(EXE_AI_DIR, "device-id");
|
|
5696
5872
|
PLAN_LIMITS = {
|
|
5697
5873
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
5698
5874
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -5704,12 +5880,12 @@ var init_license = __esm({
|
|
|
5704
5880
|
});
|
|
5705
5881
|
|
|
5706
5882
|
// src/lib/plan-limits.ts
|
|
5707
|
-
import { readFileSync as
|
|
5708
|
-
import
|
|
5883
|
+
import { readFileSync as readFileSync10, existsSync as existsSync12 } from "fs";
|
|
5884
|
+
import path13 from "path";
|
|
5709
5885
|
function getLicenseSync() {
|
|
5710
5886
|
try {
|
|
5711
|
-
if (!
|
|
5712
|
-
const raw = JSON.parse(
|
|
5887
|
+
if (!existsSync12(CACHE_PATH2)) return freeLicense();
|
|
5888
|
+
const raw = JSON.parse(readFileSync10(CACHE_PATH2, "utf8"));
|
|
5713
5889
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
5714
5890
|
const parts = raw.token.split(".");
|
|
5715
5891
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -5747,8 +5923,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
5747
5923
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
5748
5924
|
let count = 0;
|
|
5749
5925
|
try {
|
|
5750
|
-
if (
|
|
5751
|
-
const raw =
|
|
5926
|
+
if (existsSync12(filePath)) {
|
|
5927
|
+
const raw = readFileSync10(filePath, "utf8");
|
|
5752
5928
|
const employees = JSON.parse(raw);
|
|
5753
5929
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
5754
5930
|
}
|
|
@@ -5777,29 +5953,63 @@ var init_plan_limits = __esm({
|
|
|
5777
5953
|
this.name = "PlanLimitError";
|
|
5778
5954
|
}
|
|
5779
5955
|
};
|
|
5780
|
-
CACHE_PATH2 =
|
|
5956
|
+
CACHE_PATH2 = path13.join(EXE_AI_DIR, "license-cache.json");
|
|
5957
|
+
}
|
|
5958
|
+
});
|
|
5959
|
+
|
|
5960
|
+
// src/lib/task-scope.ts
|
|
5961
|
+
function getCurrentSessionScope() {
|
|
5962
|
+
try {
|
|
5963
|
+
return resolveExeSession();
|
|
5964
|
+
} catch {
|
|
5965
|
+
return null;
|
|
5966
|
+
}
|
|
5967
|
+
}
|
|
5968
|
+
function sessionScopeFilter(sessionScope, tableAlias) {
|
|
5969
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
5970
|
+
if (!scope) return { sql: "", args: [] };
|
|
5971
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
5972
|
+
return {
|
|
5973
|
+
sql: ` AND (${col} IS NULL OR ${col} = ?)`,
|
|
5974
|
+
args: [scope]
|
|
5975
|
+
};
|
|
5976
|
+
}
|
|
5977
|
+
function strictSessionScopeFilter(sessionScope, tableAlias) {
|
|
5978
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
5979
|
+
if (!scope) return { sql: "", args: [] };
|
|
5980
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
5981
|
+
return {
|
|
5982
|
+
sql: ` AND ${col} = ?`,
|
|
5983
|
+
args: [scope]
|
|
5984
|
+
};
|
|
5985
|
+
}
|
|
5986
|
+
var init_task_scope = __esm({
|
|
5987
|
+
"src/lib/task-scope.ts"() {
|
|
5988
|
+
"use strict";
|
|
5989
|
+
init_tmux_routing();
|
|
5781
5990
|
}
|
|
5782
5991
|
});
|
|
5783
5992
|
|
|
5784
5993
|
// src/lib/notifications.ts
|
|
5785
|
-
import
|
|
5786
|
-
import
|
|
5787
|
-
import
|
|
5994
|
+
import crypto4 from "crypto";
|
|
5995
|
+
import path14 from "path";
|
|
5996
|
+
import os10 from "os";
|
|
5788
5997
|
import {
|
|
5789
|
-
readFileSync as
|
|
5998
|
+
readFileSync as readFileSync11,
|
|
5790
5999
|
readdirSync as readdirSync2,
|
|
5791
6000
|
unlinkSync as unlinkSync3,
|
|
5792
|
-
existsSync as
|
|
6001
|
+
existsSync as existsSync13,
|
|
5793
6002
|
rmdirSync
|
|
5794
6003
|
} from "fs";
|
|
5795
6004
|
async function writeNotification(notification) {
|
|
5796
6005
|
try {
|
|
5797
6006
|
const client = getClient();
|
|
5798
|
-
const id =
|
|
6007
|
+
const id = crypto4.randomUUID();
|
|
5799
6008
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6009
|
+
const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
|
|
5800
6010
|
await client.execute({
|
|
5801
|
-
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
5802
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
6011
|
+
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
|
|
6012
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
5803
6013
|
args: [
|
|
5804
6014
|
id,
|
|
5805
6015
|
notification.agentId,
|
|
@@ -5808,6 +6018,7 @@ async function writeNotification(notification) {
|
|
|
5808
6018
|
notification.project,
|
|
5809
6019
|
notification.summary,
|
|
5810
6020
|
notification.taskFile ?? null,
|
|
6021
|
+
sessionScope,
|
|
5811
6022
|
now
|
|
5812
6023
|
]
|
|
5813
6024
|
});
|
|
@@ -5816,12 +6027,14 @@ async function writeNotification(notification) {
|
|
|
5816
6027
|
`);
|
|
5817
6028
|
}
|
|
5818
6029
|
}
|
|
5819
|
-
async function markAsReadByTaskFile(taskFile) {
|
|
6030
|
+
async function markAsReadByTaskFile(taskFile, sessionScope) {
|
|
5820
6031
|
try {
|
|
5821
6032
|
const client = getClient();
|
|
6033
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
5822
6034
|
await client.execute({
|
|
5823
|
-
sql:
|
|
5824
|
-
|
|
6035
|
+
sql: `UPDATE notifications SET read = 1
|
|
6036
|
+
WHERE task_file = ? AND read = 0${scope.sql}`,
|
|
6037
|
+
args: [taskFile, ...scope.args]
|
|
5825
6038
|
});
|
|
5826
6039
|
} catch {
|
|
5827
6040
|
}
|
|
@@ -5830,11 +6043,12 @@ var init_notifications = __esm({
|
|
|
5830
6043
|
"src/lib/notifications.ts"() {
|
|
5831
6044
|
"use strict";
|
|
5832
6045
|
init_database();
|
|
6046
|
+
init_task_scope();
|
|
5833
6047
|
}
|
|
5834
6048
|
});
|
|
5835
6049
|
|
|
5836
6050
|
// src/lib/session-kill-telemetry.ts
|
|
5837
|
-
import
|
|
6051
|
+
import crypto5 from "crypto";
|
|
5838
6052
|
async function recordSessionKill(input) {
|
|
5839
6053
|
try {
|
|
5840
6054
|
const client = getClient();
|
|
@@ -5844,7 +6058,7 @@ async function recordSessionKill(input) {
|
|
|
5844
6058
|
ticks_idle, estimated_tokens_saved)
|
|
5845
6059
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
5846
6060
|
args: [
|
|
5847
|
-
|
|
6061
|
+
crypto5.randomUUID(),
|
|
5848
6062
|
input.sessionName,
|
|
5849
6063
|
input.agentId,
|
|
5850
6064
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5867,37 +6081,13 @@ var init_session_kill_telemetry = __esm({
|
|
|
5867
6081
|
}
|
|
5868
6082
|
});
|
|
5869
6083
|
|
|
5870
|
-
// src/lib/task-scope.ts
|
|
5871
|
-
function getCurrentSessionScope() {
|
|
5872
|
-
try {
|
|
5873
|
-
return resolveExeSession();
|
|
5874
|
-
} catch {
|
|
5875
|
-
return null;
|
|
5876
|
-
}
|
|
5877
|
-
}
|
|
5878
|
-
function sessionScopeFilter(sessionScope, tableAlias) {
|
|
5879
|
-
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
5880
|
-
if (!scope) return { sql: "", args: [] };
|
|
5881
|
-
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
5882
|
-
return {
|
|
5883
|
-
sql: ` AND (${col} IS NULL OR ${col} = ?)`,
|
|
5884
|
-
args: [scope]
|
|
5885
|
-
};
|
|
5886
|
-
}
|
|
5887
|
-
var init_task_scope = __esm({
|
|
5888
|
-
"src/lib/task-scope.ts"() {
|
|
5889
|
-
"use strict";
|
|
5890
|
-
init_tmux_routing();
|
|
5891
|
-
}
|
|
5892
|
-
});
|
|
5893
|
-
|
|
5894
6084
|
// src/lib/tasks-crud.ts
|
|
5895
|
-
import
|
|
5896
|
-
import
|
|
5897
|
-
import
|
|
6085
|
+
import crypto6 from "crypto";
|
|
6086
|
+
import path15 from "path";
|
|
6087
|
+
import os11 from "os";
|
|
5898
6088
|
import { execSync as execSync4 } from "child_process";
|
|
5899
6089
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
5900
|
-
import { existsSync as
|
|
6090
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
5901
6091
|
async function writeCheckpoint(input) {
|
|
5902
6092
|
const client = getClient();
|
|
5903
6093
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -6013,7 +6203,7 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
6013
6203
|
}
|
|
6014
6204
|
async function createTaskCore(input) {
|
|
6015
6205
|
const client = getClient();
|
|
6016
|
-
const id =
|
|
6206
|
+
const id = crypto6.randomUUID();
|
|
6017
6207
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6018
6208
|
const slug = slugify(input.title);
|
|
6019
6209
|
let earlySessionScope = null;
|
|
@@ -6072,8 +6262,8 @@ ${laneWarning}` : laneWarning;
|
|
|
6072
6262
|
}
|
|
6073
6263
|
if (input.baseDir) {
|
|
6074
6264
|
try {
|
|
6075
|
-
await mkdir4(
|
|
6076
|
-
await mkdir4(
|
|
6265
|
+
await mkdir4(path15.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
6266
|
+
await mkdir4(path15.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
6077
6267
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
6078
6268
|
await ensureGitignoreExe(input.baseDir);
|
|
6079
6269
|
} catch {
|
|
@@ -6109,13 +6299,19 @@ ${laneWarning}` : laneWarning;
|
|
|
6109
6299
|
});
|
|
6110
6300
|
if (input.baseDir) {
|
|
6111
6301
|
try {
|
|
6112
|
-
const EXE_OS_DIR =
|
|
6113
|
-
const mdPath =
|
|
6114
|
-
const mdDir =
|
|
6115
|
-
if (!
|
|
6302
|
+
const EXE_OS_DIR = path15.join(os11.homedir(), ".exe-os");
|
|
6303
|
+
const mdPath = path15.join(EXE_OS_DIR, taskFile);
|
|
6304
|
+
const mdDir = path15.dirname(mdPath);
|
|
6305
|
+
if (!existsSync14(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
6116
6306
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
6117
6307
|
const mdContent = `# ${input.title}
|
|
6118
6308
|
|
|
6309
|
+
## MANDATORY: When done
|
|
6310
|
+
|
|
6311
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
6312
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
6313
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
6314
|
+
|
|
6119
6315
|
**ID:** ${id}
|
|
6120
6316
|
**Status:** ${initialStatus}
|
|
6121
6317
|
**Priority:** ${input.priority}
|
|
@@ -6129,12 +6325,6 @@ ${laneWarning}` : laneWarning;
|
|
|
6129
6325
|
## Context
|
|
6130
6326
|
|
|
6131
6327
|
${input.context}
|
|
6132
|
-
|
|
6133
|
-
## MANDATORY: When done
|
|
6134
|
-
|
|
6135
|
-
You MUST call update_task with status "done" and a result summary when finished.
|
|
6136
|
-
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
6137
|
-
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
6138
6328
|
`;
|
|
6139
6329
|
await writeFile4(mdPath, mdContent, "utf-8");
|
|
6140
6330
|
} catch (err) {
|
|
@@ -6383,7 +6573,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
6383
6573
|
await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
6384
6574
|
} catch {
|
|
6385
6575
|
}
|
|
6386
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
6576
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
6387
6577
|
try {
|
|
6388
6578
|
const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
6389
6579
|
clearQueueForAgent2(String(row.assigned_to));
|
|
@@ -6412,9 +6602,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
6412
6602
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
6413
6603
|
}
|
|
6414
6604
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
6415
|
-
const archPath =
|
|
6605
|
+
const archPath = path15.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
6416
6606
|
try {
|
|
6417
|
-
if (
|
|
6607
|
+
if (existsSync14(archPath)) return;
|
|
6418
6608
|
const template = [
|
|
6419
6609
|
`# ${projectName} \u2014 System Architecture`,
|
|
6420
6610
|
"",
|
|
@@ -6447,10 +6637,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
6447
6637
|
}
|
|
6448
6638
|
}
|
|
6449
6639
|
async function ensureGitignoreExe(baseDir) {
|
|
6450
|
-
const gitignorePath =
|
|
6640
|
+
const gitignorePath = path15.join(baseDir, ".gitignore");
|
|
6451
6641
|
try {
|
|
6452
|
-
if (
|
|
6453
|
-
const content =
|
|
6642
|
+
if (existsSync14(gitignorePath)) {
|
|
6643
|
+
const content = readFileSync12(gitignorePath, "utf-8");
|
|
6454
6644
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
6455
6645
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
6456
6646
|
} else {
|
|
@@ -6481,58 +6671,42 @@ var init_tasks_crud = __esm({
|
|
|
6481
6671
|
});
|
|
6482
6672
|
|
|
6483
6673
|
// src/lib/tasks-review.ts
|
|
6484
|
-
import
|
|
6485
|
-
import { existsSync as
|
|
6674
|
+
import path16 from "path";
|
|
6675
|
+
import { existsSync as existsSync15, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
6486
6676
|
async function countPendingReviews(sessionScope) {
|
|
6487
6677
|
const client = getClient();
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
args: [sessionScope]
|
|
6492
|
-
});
|
|
6493
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
6494
|
-
}
|
|
6678
|
+
const scope = strictSessionScopeFilter(
|
|
6679
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
6680
|
+
);
|
|
6495
6681
|
const result = await client.execute({
|
|
6496
|
-
sql:
|
|
6497
|
-
|
|
6682
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
6683
|
+
WHERE status = 'needs_review'${scope.sql}`,
|
|
6684
|
+
args: [...scope.args]
|
|
6498
6685
|
});
|
|
6499
6686
|
return Number(result.rows[0]?.cnt) || 0;
|
|
6500
6687
|
}
|
|
6501
6688
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
6502
6689
|
const client = getClient();
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
WHERE status = 'needs_review' AND updated_at > ?
|
|
6507
|
-
AND session_scope = ?`,
|
|
6508
|
-
args: [sinceIso, sessionScope]
|
|
6509
|
-
});
|
|
6510
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
6511
|
-
}
|
|
6690
|
+
const scope = strictSessionScopeFilter(
|
|
6691
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
6692
|
+
);
|
|
6512
6693
|
const result = await client.execute({
|
|
6513
6694
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
6514
|
-
WHERE status = 'needs_review' AND updated_at >
|
|
6515
|
-
args: [sinceIso]
|
|
6695
|
+
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
6696
|
+
args: [sinceIso, ...scope.args]
|
|
6516
6697
|
});
|
|
6517
6698
|
return Number(result.rows[0]?.cnt) || 0;
|
|
6518
6699
|
}
|
|
6519
6700
|
async function listPendingReviews(limit, sessionScope) {
|
|
6520
6701
|
const client = getClient();
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
WHERE status = 'needs_review'
|
|
6525
|
-
AND session_scope = ?
|
|
6526
|
-
ORDER BY updated_at ASC LIMIT ?`,
|
|
6527
|
-
args: [sessionScope, limit]
|
|
6528
|
-
});
|
|
6529
|
-
return result2.rows;
|
|
6530
|
-
}
|
|
6702
|
+
const scope = strictSessionScopeFilter(
|
|
6703
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
6704
|
+
);
|
|
6531
6705
|
const result = await client.execute({
|
|
6532
6706
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
6533
|
-
WHERE status = 'needs_review'
|
|
6707
|
+
WHERE status = 'needs_review'${scope.sql}
|
|
6534
6708
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
6535
|
-
args: [limit]
|
|
6709
|
+
args: [...scope.args, limit]
|
|
6536
6710
|
});
|
|
6537
6711
|
return result.rows;
|
|
6538
6712
|
}
|
|
@@ -6544,7 +6718,7 @@ async function cleanupOrphanedReviews() {
|
|
|
6544
6718
|
WHERE status IN ('open', 'needs_review', 'in_progress')
|
|
6545
6719
|
AND assigned_by = 'system'
|
|
6546
6720
|
AND title LIKE 'Review:%'
|
|
6547
|
-
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
6721
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
|
|
6548
6722
|
args: [now]
|
|
6549
6723
|
});
|
|
6550
6724
|
const r1b = await client.execute({
|
|
@@ -6663,11 +6837,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
6663
6837
|
);
|
|
6664
6838
|
}
|
|
6665
6839
|
try {
|
|
6666
|
-
const cacheDir =
|
|
6667
|
-
if (
|
|
6840
|
+
const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
|
|
6841
|
+
if (existsSync15(cacheDir)) {
|
|
6668
6842
|
for (const f of readdirSync3(cacheDir)) {
|
|
6669
6843
|
if (f.startsWith("review-notified-")) {
|
|
6670
|
-
unlinkSync4(
|
|
6844
|
+
unlinkSync4(path16.join(cacheDir, f));
|
|
6671
6845
|
}
|
|
6672
6846
|
}
|
|
6673
6847
|
}
|
|
@@ -6684,11 +6858,12 @@ var init_tasks_review = __esm({
|
|
|
6684
6858
|
init_tmux_routing();
|
|
6685
6859
|
init_session_key();
|
|
6686
6860
|
init_state_bus();
|
|
6861
|
+
init_task_scope();
|
|
6687
6862
|
}
|
|
6688
6863
|
});
|
|
6689
6864
|
|
|
6690
6865
|
// src/lib/tasks-chain.ts
|
|
6691
|
-
import
|
|
6866
|
+
import path17 from "path";
|
|
6692
6867
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
6693
6868
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
6694
6869
|
const client = getClient();
|
|
@@ -6705,7 +6880,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
6705
6880
|
});
|
|
6706
6881
|
for (const ur of unblockedRows.rows) {
|
|
6707
6882
|
try {
|
|
6708
|
-
const ubFile =
|
|
6883
|
+
const ubFile = path17.join(baseDir, String(ur.task_file));
|
|
6709
6884
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
6710
6885
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
6711
6886
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -6740,7 +6915,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
|
|
|
6740
6915
|
const scScope = sessionScopeFilter();
|
|
6741
6916
|
const remaining = await client.execute({
|
|
6742
6917
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
6743
|
-
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
|
|
6918
|
+
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
|
|
6744
6919
|
args: [parentTaskId, ...scScope.args]
|
|
6745
6920
|
});
|
|
6746
6921
|
const cnt = Number(remaining.rows[0]?.cnt ?? 1);
|
|
@@ -6774,7 +6949,7 @@ var init_tasks_chain = __esm({
|
|
|
6774
6949
|
|
|
6775
6950
|
// src/lib/project-name.ts
|
|
6776
6951
|
import { execSync as execSync5 } from "child_process";
|
|
6777
|
-
import
|
|
6952
|
+
import path18 from "path";
|
|
6778
6953
|
function getProjectName(cwd) {
|
|
6779
6954
|
const dir = cwd ?? process.cwd();
|
|
6780
6955
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -6787,7 +6962,7 @@ function getProjectName(cwd) {
|
|
|
6787
6962
|
timeout: 2e3,
|
|
6788
6963
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6789
6964
|
}).trim();
|
|
6790
|
-
repoRoot =
|
|
6965
|
+
repoRoot = path18.dirname(gitCommonDir);
|
|
6791
6966
|
} catch {
|
|
6792
6967
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
6793
6968
|
cwd: dir,
|
|
@@ -6796,11 +6971,11 @@ function getProjectName(cwd) {
|
|
|
6796
6971
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6797
6972
|
}).trim();
|
|
6798
6973
|
}
|
|
6799
|
-
_cached2 =
|
|
6974
|
+
_cached2 = path18.basename(repoRoot);
|
|
6800
6975
|
_cachedCwd = dir;
|
|
6801
6976
|
return _cached2;
|
|
6802
6977
|
} catch {
|
|
6803
|
-
_cached2 =
|
|
6978
|
+
_cached2 = path18.basename(dir);
|
|
6804
6979
|
_cachedCwd = dir;
|
|
6805
6980
|
return _cached2;
|
|
6806
6981
|
}
|
|
@@ -6943,10 +7118,10 @@ var init_tasks_notify = __esm({
|
|
|
6943
7118
|
});
|
|
6944
7119
|
|
|
6945
7120
|
// src/lib/behaviors.ts
|
|
6946
|
-
import
|
|
7121
|
+
import crypto7 from "crypto";
|
|
6947
7122
|
async function storeBehavior(opts) {
|
|
6948
7123
|
const client = getClient();
|
|
6949
|
-
const id =
|
|
7124
|
+
const id = crypto7.randomUUID();
|
|
6950
7125
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6951
7126
|
await client.execute({
|
|
6952
7127
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -6975,7 +7150,7 @@ __export(skill_learning_exports, {
|
|
|
6975
7150
|
storeTrajectory: () => storeTrajectory,
|
|
6976
7151
|
sweepTrajectories: () => sweepTrajectories
|
|
6977
7152
|
});
|
|
6978
|
-
import
|
|
7153
|
+
import crypto8 from "crypto";
|
|
6979
7154
|
async function extractTrajectory(taskId, agentId) {
|
|
6980
7155
|
const client = getClient();
|
|
6981
7156
|
const result = await client.execute({
|
|
@@ -7004,11 +7179,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
7004
7179
|
return signature;
|
|
7005
7180
|
}
|
|
7006
7181
|
function hashSignature(signature) {
|
|
7007
|
-
return
|
|
7182
|
+
return crypto8.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
7008
7183
|
}
|
|
7009
7184
|
async function storeTrajectory(opts) {
|
|
7010
7185
|
const client = getClient();
|
|
7011
|
-
const id =
|
|
7186
|
+
const id = crypto8.randomUUID();
|
|
7012
7187
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7013
7188
|
const signatureHash = hashSignature(opts.signature);
|
|
7014
7189
|
await client.execute({
|
|
@@ -7273,8 +7448,8 @@ __export(tasks_exports, {
|
|
|
7273
7448
|
updateTaskStatus: () => updateTaskStatus,
|
|
7274
7449
|
writeCheckpoint: () => writeCheckpoint
|
|
7275
7450
|
});
|
|
7276
|
-
import
|
|
7277
|
-
import { writeFileSync as
|
|
7451
|
+
import path19 from "path";
|
|
7452
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
|
|
7278
7453
|
async function createTask(input) {
|
|
7279
7454
|
const result = await createTaskCore(input);
|
|
7280
7455
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -7293,12 +7468,12 @@ async function updateTask(input) {
|
|
|
7293
7468
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
7294
7469
|
try {
|
|
7295
7470
|
const agent = String(row.assigned_to);
|
|
7296
|
-
const cacheDir =
|
|
7297
|
-
const cachePath =
|
|
7471
|
+
const cacheDir = path19.join(EXE_AI_DIR, "session-cache");
|
|
7472
|
+
const cachePath = path19.join(cacheDir, `current-task-${agent}.json`);
|
|
7298
7473
|
if (input.status === "in_progress") {
|
|
7299
7474
|
mkdirSync7(cacheDir, { recursive: true });
|
|
7300
|
-
|
|
7301
|
-
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
7475
|
+
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
7476
|
+
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
7302
7477
|
try {
|
|
7303
7478
|
unlinkSync5(cachePath);
|
|
7304
7479
|
} catch {
|
|
@@ -7306,10 +7481,10 @@ async function updateTask(input) {
|
|
|
7306
7481
|
}
|
|
7307
7482
|
} catch {
|
|
7308
7483
|
}
|
|
7309
|
-
if (input.status === "done") {
|
|
7484
|
+
if (input.status === "done" || input.status === "closed") {
|
|
7310
7485
|
await cleanupReviewFile(row, taskFile, input.baseDir);
|
|
7311
7486
|
}
|
|
7312
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
7487
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
7313
7488
|
try {
|
|
7314
7489
|
const client = getClient();
|
|
7315
7490
|
const taskTitle = String(row.title);
|
|
@@ -7325,7 +7500,7 @@ async function updateTask(input) {
|
|
|
7325
7500
|
if (!isCoordinatorName(assignedAgent)) {
|
|
7326
7501
|
try {
|
|
7327
7502
|
const draftClient = getClient();
|
|
7328
|
-
if (input.status === "done") {
|
|
7503
|
+
if (input.status === "done" || input.status === "closed") {
|
|
7329
7504
|
await draftClient.execute({
|
|
7330
7505
|
sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
|
|
7331
7506
|
args: [assignedAgent]
|
|
@@ -7342,7 +7517,7 @@ async function updateTask(input) {
|
|
|
7342
7517
|
try {
|
|
7343
7518
|
const client = getClient();
|
|
7344
7519
|
const cascaded = await client.execute({
|
|
7345
|
-
sql: `UPDATE tasks SET status = '
|
|
7520
|
+
sql: `UPDATE tasks SET status = 'closed', updated_at = ?
|
|
7346
7521
|
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
7347
7522
|
args: [now, taskId]
|
|
7348
7523
|
});
|
|
@@ -7355,14 +7530,14 @@ async function updateTask(input) {
|
|
|
7355
7530
|
} catch {
|
|
7356
7531
|
}
|
|
7357
7532
|
}
|
|
7358
|
-
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
7533
|
+
const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
|
|
7359
7534
|
if (isTerminal) {
|
|
7360
7535
|
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
7361
7536
|
if (!isCoordinator) {
|
|
7362
7537
|
notifyTaskDone();
|
|
7363
7538
|
}
|
|
7364
7539
|
await markTaskNotificationsRead(taskFile);
|
|
7365
|
-
if (input.status === "done") {
|
|
7540
|
+
if (input.status === "done" || input.status === "closed") {
|
|
7366
7541
|
try {
|
|
7367
7542
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
7368
7543
|
} catch {
|
|
@@ -7382,7 +7557,7 @@ async function updateTask(input) {
|
|
|
7382
7557
|
}
|
|
7383
7558
|
}
|
|
7384
7559
|
}
|
|
7385
|
-
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
7560
|
+
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
7386
7561
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
7387
7562
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
7388
7563
|
taskId,
|
|
@@ -7754,6 +7929,7 @@ __export(tmux_routing_exports, {
|
|
|
7754
7929
|
isEmployeeAlive: () => isEmployeeAlive,
|
|
7755
7930
|
isExeSession: () => isExeSession,
|
|
7756
7931
|
isSessionBusy: () => isSessionBusy,
|
|
7932
|
+
notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
|
|
7757
7933
|
notifyParentExe: () => notifyParentExe,
|
|
7758
7934
|
parseParentExe: () => parseParentExe,
|
|
7759
7935
|
registerParentExe: () => registerParentExe,
|
|
@@ -7764,13 +7940,13 @@ __export(tmux_routing_exports, {
|
|
|
7764
7940
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
7765
7941
|
});
|
|
7766
7942
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
7767
|
-
import { readFileSync as
|
|
7768
|
-
import
|
|
7769
|
-
import
|
|
7943
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, existsSync as existsSync16, appendFileSync, readdirSync as readdirSync4 } from "fs";
|
|
7944
|
+
import path20 from "path";
|
|
7945
|
+
import os12 from "os";
|
|
7770
7946
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7771
7947
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
7772
7948
|
function spawnLockPath(sessionName) {
|
|
7773
|
-
return
|
|
7949
|
+
return path20.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
7774
7950
|
}
|
|
7775
7951
|
function isProcessAlive(pid) {
|
|
7776
7952
|
try {
|
|
@@ -7781,13 +7957,13 @@ function isProcessAlive(pid) {
|
|
|
7781
7957
|
}
|
|
7782
7958
|
}
|
|
7783
7959
|
function acquireSpawnLock2(sessionName) {
|
|
7784
|
-
if (!
|
|
7960
|
+
if (!existsSync16(SPAWN_LOCK_DIR)) {
|
|
7785
7961
|
mkdirSync8(SPAWN_LOCK_DIR, { recursive: true });
|
|
7786
7962
|
}
|
|
7787
7963
|
const lockFile = spawnLockPath(sessionName);
|
|
7788
|
-
if (
|
|
7964
|
+
if (existsSync16(lockFile)) {
|
|
7789
7965
|
try {
|
|
7790
|
-
const lock = JSON.parse(
|
|
7966
|
+
const lock = JSON.parse(readFileSync13(lockFile, "utf8"));
|
|
7791
7967
|
const age = Date.now() - lock.timestamp;
|
|
7792
7968
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
7793
7969
|
return false;
|
|
@@ -7795,7 +7971,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
7795
7971
|
} catch {
|
|
7796
7972
|
}
|
|
7797
7973
|
}
|
|
7798
|
-
|
|
7974
|
+
writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
7799
7975
|
return true;
|
|
7800
7976
|
}
|
|
7801
7977
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -7807,13 +7983,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
7807
7983
|
function resolveBehaviorsExporterScript() {
|
|
7808
7984
|
try {
|
|
7809
7985
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
7810
|
-
const scriptPath =
|
|
7811
|
-
|
|
7986
|
+
const scriptPath = path20.join(
|
|
7987
|
+
path20.dirname(thisFile),
|
|
7812
7988
|
"..",
|
|
7813
7989
|
"bin",
|
|
7814
7990
|
"exe-export-behaviors.js"
|
|
7815
7991
|
);
|
|
7816
|
-
return
|
|
7992
|
+
return existsSync16(scriptPath) ? scriptPath : null;
|
|
7817
7993
|
} catch {
|
|
7818
7994
|
return null;
|
|
7819
7995
|
}
|
|
@@ -7879,12 +8055,12 @@ function extractRootExe(name) {
|
|
|
7879
8055
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
7880
8056
|
}
|
|
7881
8057
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
7882
|
-
if (!
|
|
8058
|
+
if (!existsSync16(SESSION_CACHE)) {
|
|
7883
8059
|
mkdirSync8(SESSION_CACHE, { recursive: true });
|
|
7884
8060
|
}
|
|
7885
8061
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
7886
|
-
const filePath =
|
|
7887
|
-
|
|
8062
|
+
const filePath = path20.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
8063
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
7888
8064
|
parentExe: rootExe,
|
|
7889
8065
|
dispatchedBy: dispatchedBy || rootExe,
|
|
7890
8066
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -7892,7 +8068,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
7892
8068
|
}
|
|
7893
8069
|
function getParentExe(sessionKey) {
|
|
7894
8070
|
try {
|
|
7895
|
-
const data = JSON.parse(
|
|
8071
|
+
const data = JSON.parse(readFileSync13(path20.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
7896
8072
|
return data.parentExe || null;
|
|
7897
8073
|
} catch {
|
|
7898
8074
|
return null;
|
|
@@ -7900,8 +8076,8 @@ function getParentExe(sessionKey) {
|
|
|
7900
8076
|
}
|
|
7901
8077
|
function getDispatchedBy(sessionKey) {
|
|
7902
8078
|
try {
|
|
7903
|
-
const data = JSON.parse(
|
|
7904
|
-
|
|
8079
|
+
const data = JSON.parse(readFileSync13(
|
|
8080
|
+
path20.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
7905
8081
|
"utf8"
|
|
7906
8082
|
));
|
|
7907
8083
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -7971,8 +8147,8 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
7971
8147
|
}
|
|
7972
8148
|
function readDebounceState() {
|
|
7973
8149
|
try {
|
|
7974
|
-
if (!
|
|
7975
|
-
const raw = JSON.parse(
|
|
8150
|
+
if (!existsSync16(DEBOUNCE_FILE)) return {};
|
|
8151
|
+
const raw = JSON.parse(readFileSync13(DEBOUNCE_FILE, "utf8"));
|
|
7976
8152
|
const state = {};
|
|
7977
8153
|
for (const [key, val] of Object.entries(raw)) {
|
|
7978
8154
|
if (typeof val === "number") {
|
|
@@ -7988,8 +8164,8 @@ function readDebounceState() {
|
|
|
7988
8164
|
}
|
|
7989
8165
|
function writeDebounceState(state) {
|
|
7990
8166
|
try {
|
|
7991
|
-
if (!
|
|
7992
|
-
|
|
8167
|
+
if (!existsSync16(SESSION_CACHE)) mkdirSync8(SESSION_CACHE, { recursive: true });
|
|
8168
|
+
writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
|
|
7993
8169
|
} catch {
|
|
7994
8170
|
}
|
|
7995
8171
|
}
|
|
@@ -8087,8 +8263,8 @@ function sendIntercom(targetSession) {
|
|
|
8087
8263
|
try {
|
|
8088
8264
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
8089
8265
|
const agent = baseAgentName(rawAgent);
|
|
8090
|
-
const markerPath =
|
|
8091
|
-
if (
|
|
8266
|
+
const markerPath = path20.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
8267
|
+
if (existsSync16(markerPath)) {
|
|
8092
8268
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
8093
8269
|
return "debounced";
|
|
8094
8270
|
}
|
|
@@ -8097,8 +8273,8 @@ function sendIntercom(targetSession) {
|
|
|
8097
8273
|
try {
|
|
8098
8274
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
8099
8275
|
const agent = baseAgentName(rawAgent);
|
|
8100
|
-
const taskDir =
|
|
8101
|
-
if (
|
|
8276
|
+
const taskDir = path20.join(process.cwd(), "exe", agent);
|
|
8277
|
+
if (existsSync16(taskDir)) {
|
|
8102
8278
|
const files = readdirSync4(taskDir).filter(
|
|
8103
8279
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
8104
8280
|
);
|
|
@@ -8158,6 +8334,21 @@ function notifyParentExe(sessionKey) {
|
|
|
8158
8334
|
}
|
|
8159
8335
|
return true;
|
|
8160
8336
|
}
|
|
8337
|
+
function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
|
|
8338
|
+
const transport = getTransport();
|
|
8339
|
+
try {
|
|
8340
|
+
const sessions = transport.listSessions();
|
|
8341
|
+
if (!sessions.includes(coordinatorSession)) return false;
|
|
8342
|
+
execSync6(
|
|
8343
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
8344
|
+
{ timeout: 3e3 }
|
|
8345
|
+
);
|
|
8346
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
|
|
8347
|
+
return true;
|
|
8348
|
+
} catch {
|
|
8349
|
+
return false;
|
|
8350
|
+
}
|
|
8351
|
+
}
|
|
8161
8352
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
8162
8353
|
if (isCoordinatorName(employeeName)) {
|
|
8163
8354
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
@@ -8231,26 +8422,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
8231
8422
|
const transport = getTransport();
|
|
8232
8423
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
8233
8424
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
8234
|
-
const logDir =
|
|
8235
|
-
const logFile =
|
|
8236
|
-
if (!
|
|
8425
|
+
const logDir = path20.join(os12.homedir(), ".exe-os", "session-logs");
|
|
8426
|
+
const logFile = path20.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
8427
|
+
if (!existsSync16(logDir)) {
|
|
8237
8428
|
mkdirSync8(logDir, { recursive: true });
|
|
8238
8429
|
}
|
|
8239
8430
|
transport.kill(sessionName);
|
|
8240
8431
|
let cleanupSuffix = "";
|
|
8241
8432
|
try {
|
|
8242
8433
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
8243
|
-
const cleanupScript =
|
|
8244
|
-
if (
|
|
8434
|
+
const cleanupScript = path20.join(path20.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
8435
|
+
if (existsSync16(cleanupScript)) {
|
|
8245
8436
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
8246
8437
|
}
|
|
8247
8438
|
} catch {
|
|
8248
8439
|
}
|
|
8249
8440
|
try {
|
|
8250
|
-
const claudeJsonPath =
|
|
8441
|
+
const claudeJsonPath = path20.join(os12.homedir(), ".claude.json");
|
|
8251
8442
|
let claudeJson = {};
|
|
8252
8443
|
try {
|
|
8253
|
-
claudeJson = JSON.parse(
|
|
8444
|
+
claudeJson = JSON.parse(readFileSync13(claudeJsonPath, "utf8"));
|
|
8254
8445
|
} catch {
|
|
8255
8446
|
}
|
|
8256
8447
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -8258,17 +8449,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
8258
8449
|
const trustDir = opts?.cwd ?? projectDir;
|
|
8259
8450
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
8260
8451
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
8261
|
-
|
|
8452
|
+
writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
8262
8453
|
} catch {
|
|
8263
8454
|
}
|
|
8264
8455
|
try {
|
|
8265
|
-
const settingsDir =
|
|
8456
|
+
const settingsDir = path20.join(os12.homedir(), ".claude", "projects");
|
|
8266
8457
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
8267
|
-
const projSettingsDir =
|
|
8268
|
-
const settingsPath =
|
|
8458
|
+
const projSettingsDir = path20.join(settingsDir, normalizedKey);
|
|
8459
|
+
const settingsPath = path20.join(projSettingsDir, "settings.json");
|
|
8269
8460
|
let settings = {};
|
|
8270
8461
|
try {
|
|
8271
|
-
settings = JSON.parse(
|
|
8462
|
+
settings = JSON.parse(readFileSync13(settingsPath, "utf8"));
|
|
8272
8463
|
} catch {
|
|
8273
8464
|
}
|
|
8274
8465
|
const perms = settings.permissions ?? {};
|
|
@@ -8297,7 +8488,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
8297
8488
|
perms.allow = allow;
|
|
8298
8489
|
settings.permissions = perms;
|
|
8299
8490
|
mkdirSync8(projSettingsDir, { recursive: true });
|
|
8300
|
-
|
|
8491
|
+
writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
8301
8492
|
}
|
|
8302
8493
|
} catch {
|
|
8303
8494
|
}
|
|
@@ -8312,8 +8503,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
8312
8503
|
let behaviorsFlag = "";
|
|
8313
8504
|
let legacyFallbackWarned = false;
|
|
8314
8505
|
if (!useExeAgent && !useBinSymlink) {
|
|
8315
|
-
const identityPath =
|
|
8316
|
-
|
|
8506
|
+
const identityPath = path20.join(
|
|
8507
|
+
os12.homedir(),
|
|
8317
8508
|
".exe-os",
|
|
8318
8509
|
"identity",
|
|
8319
8510
|
`${employeeName}.md`
|
|
@@ -8322,13 +8513,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
8322
8513
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
8323
8514
|
if (hasAgentFlag) {
|
|
8324
8515
|
identityFlag = ` --agent ${employeeName}`;
|
|
8325
|
-
} else if (
|
|
8516
|
+
} else if (existsSync16(identityPath)) {
|
|
8326
8517
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
8327
8518
|
legacyFallbackWarned = true;
|
|
8328
8519
|
}
|
|
8329
8520
|
const behaviorsFile = exportBehaviorsSync(
|
|
8330
8521
|
employeeName,
|
|
8331
|
-
|
|
8522
|
+
path20.basename(spawnCwd),
|
|
8332
8523
|
sessionName
|
|
8333
8524
|
);
|
|
8334
8525
|
if (behaviorsFile) {
|
|
@@ -8343,16 +8534,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
8343
8534
|
}
|
|
8344
8535
|
let sessionContextFlag = "";
|
|
8345
8536
|
try {
|
|
8346
|
-
const ctxDir =
|
|
8537
|
+
const ctxDir = path20.join(os12.homedir(), ".exe-os", "session-cache");
|
|
8347
8538
|
mkdirSync8(ctxDir, { recursive: true });
|
|
8348
|
-
const ctxFile =
|
|
8539
|
+
const ctxFile = path20.join(ctxDir, `session-context-${sessionName}.md`);
|
|
8349
8540
|
const ctxContent = [
|
|
8350
8541
|
`## Session Context`,
|
|
8351
8542
|
`You are running in tmux session: ${sessionName}.`,
|
|
8352
8543
|
`Your parent coordinator session is ${exeSession}.`,
|
|
8353
8544
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
8354
8545
|
].join("\n");
|
|
8355
|
-
|
|
8546
|
+
writeFileSync8(ctxFile, ctxContent);
|
|
8356
8547
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
8357
8548
|
} catch {
|
|
8358
8549
|
}
|
|
@@ -8429,8 +8620,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
8429
8620
|
transport.pipeLog(sessionName, logFile);
|
|
8430
8621
|
try {
|
|
8431
8622
|
const mySession = getMySession();
|
|
8432
|
-
const dispatchInfo =
|
|
8433
|
-
|
|
8623
|
+
const dispatchInfo = path20.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
8624
|
+
writeFileSync8(dispatchInfo, JSON.stringify({
|
|
8434
8625
|
dispatchedBy: mySession,
|
|
8435
8626
|
rootExe: exeSession,
|
|
8436
8627
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -8504,15 +8695,15 @@ var init_tmux_routing = __esm({
|
|
|
8504
8695
|
init_intercom_queue();
|
|
8505
8696
|
init_plan_limits();
|
|
8506
8697
|
init_employees();
|
|
8507
|
-
SPAWN_LOCK_DIR =
|
|
8508
|
-
SESSION_CACHE =
|
|
8698
|
+
SPAWN_LOCK_DIR = path20.join(os12.homedir(), ".exe-os", "spawn-locks");
|
|
8699
|
+
SESSION_CACHE = path20.join(os12.homedir(), ".exe-os", "session-cache");
|
|
8509
8700
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
8510
8701
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
8511
8702
|
VERIFY_PANE_LINES = 200;
|
|
8512
8703
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
8513
8704
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
8514
|
-
INTERCOM_LOG2 =
|
|
8515
|
-
DEBOUNCE_FILE =
|
|
8705
|
+
INTERCOM_LOG2 = path20.join(os12.homedir(), ".exe-os", "intercom.log");
|
|
8706
|
+
DEBOUNCE_FILE = path20.join(SESSION_CACHE, "intercom-debounce.json");
|
|
8516
8707
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
8517
8708
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
8518
8709
|
}
|
|
@@ -8535,10 +8726,10 @@ __export(messaging_exports, {
|
|
|
8535
8726
|
sendMessage: () => sendMessage,
|
|
8536
8727
|
setWsClientSend: () => setWsClientSend
|
|
8537
8728
|
});
|
|
8538
|
-
import
|
|
8729
|
+
import crypto9 from "crypto";
|
|
8539
8730
|
function generateUlid() {
|
|
8540
8731
|
const timestamp = Date.now().toString(36).padStart(10, "0");
|
|
8541
|
-
const random =
|
|
8732
|
+
const random = crypto9.randomBytes(10).toString("hex").slice(0, 16);
|
|
8542
8733
|
return (timestamp + random).toUpperCase();
|
|
8543
8734
|
}
|
|
8544
8735
|
function rowToMessage(row) {
|
|
@@ -8549,6 +8740,7 @@ function rowToMessage(row) {
|
|
|
8549
8740
|
targetAgent: row.target_agent,
|
|
8550
8741
|
targetProject: row.target_project ?? null,
|
|
8551
8742
|
targetDevice: row.target_device,
|
|
8743
|
+
sessionScope: row.session_scope ?? null,
|
|
8552
8744
|
content: row.content,
|
|
8553
8745
|
priority: row.priority ?? "normal",
|
|
8554
8746
|
status: row.status ?? "pending",
|
|
@@ -8566,15 +8758,17 @@ async function sendMessage(input) {
|
|
|
8566
8758
|
const id = generateUlid();
|
|
8567
8759
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8568
8760
|
const targetDevice = input.targetDevice ?? "local";
|
|
8761
|
+
const sessionScope = input.sessionScope === void 0 ? resolveExeSession() : input.sessionScope;
|
|
8569
8762
|
await client.execute({
|
|
8570
|
-
sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, content, priority, status, created_at)
|
|
8571
|
-
VALUES (?, ?, 'local', ?, ?, ?, ?, ?, 'pending', ?)`,
|
|
8763
|
+
sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, session_scope, content, priority, status, created_at)
|
|
8764
|
+
VALUES (?, ?, 'local', ?, ?, ?, ?, ?, ?, 'pending', ?)`,
|
|
8572
8765
|
args: [
|
|
8573
8766
|
id,
|
|
8574
8767
|
input.fromAgent,
|
|
8575
8768
|
input.targetAgent,
|
|
8576
8769
|
input.targetProject ?? null,
|
|
8577
8770
|
targetDevice,
|
|
8771
|
+
sessionScope,
|
|
8578
8772
|
input.content,
|
|
8579
8773
|
input.priority ?? "normal",
|
|
8580
8774
|
now
|
|
@@ -8588,9 +8782,10 @@ async function sendMessage(input) {
|
|
|
8588
8782
|
}
|
|
8589
8783
|
} catch {
|
|
8590
8784
|
}
|
|
8785
|
+
const sentScope = strictSessionScopeFilter(sessionScope);
|
|
8591
8786
|
const result = await client.execute({
|
|
8592
|
-
sql:
|
|
8593
|
-
args: [id]
|
|
8787
|
+
sql: `SELECT * FROM messages WHERE id = ?${sentScope.sql}`,
|
|
8788
|
+
args: [id, ...sentScope.args]
|
|
8594
8789
|
});
|
|
8595
8790
|
return rowToMessage(result.rows[0]);
|
|
8596
8791
|
}
|
|
@@ -8614,6 +8809,7 @@ async function deliverCrossMachineMessage(messageId, targetDevice) {
|
|
|
8614
8809
|
fromAgent: msg.fromAgent,
|
|
8615
8810
|
targetAgent: msg.targetAgent,
|
|
8616
8811
|
targetProject: msg.targetProject,
|
|
8812
|
+
sessionScope: msg.sessionScope,
|
|
8617
8813
|
content: msg.content,
|
|
8618
8814
|
priority: msg.priority,
|
|
8619
8815
|
createdAt: msg.createdAt
|
|
@@ -8657,7 +8853,7 @@ async function deliverLocalMessage(messageId) {
|
|
|
8657
8853
|
} catch {
|
|
8658
8854
|
const newRetryCount = msg.retryCount + 1;
|
|
8659
8855
|
if (newRetryCount >= MAX_RETRIES3) {
|
|
8660
|
-
await markFailed(messageId, "session unavailable after 10 retries");
|
|
8856
|
+
await markFailed(messageId, "session unavailable after 10 retries", msg.sessionScope);
|
|
8661
8857
|
} else {
|
|
8662
8858
|
await client.execute({
|
|
8663
8859
|
sql: "UPDATE messages SET retry_count = ? WHERE id = ?",
|
|
@@ -8667,85 +8863,101 @@ async function deliverLocalMessage(messageId) {
|
|
|
8667
8863
|
return false;
|
|
8668
8864
|
}
|
|
8669
8865
|
}
|
|
8670
|
-
async function getPendingMessages(targetAgent) {
|
|
8866
|
+
async function getPendingMessages(targetAgent, sessionScope) {
|
|
8671
8867
|
const client = getClient();
|
|
8868
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
8672
8869
|
const result = await client.execute({
|
|
8673
8870
|
sql: `SELECT * FROM messages
|
|
8674
|
-
WHERE target_agent = ? AND status IN ('pending', 'delivered')
|
|
8871
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}
|
|
8675
8872
|
ORDER BY id`,
|
|
8676
|
-
args: [targetAgent]
|
|
8873
|
+
args: [targetAgent, ...scope.args]
|
|
8677
8874
|
});
|
|
8678
8875
|
return result.rows.map((row) => rowToMessage(row));
|
|
8679
8876
|
}
|
|
8680
|
-
async function markRead(messageId) {
|
|
8877
|
+
async function markRead(messageId, sessionScope) {
|
|
8681
8878
|
const client = getClient();
|
|
8879
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
8682
8880
|
await client.execute({
|
|
8683
|
-
sql:
|
|
8684
|
-
|
|
8881
|
+
sql: `UPDATE messages SET status = 'read'
|
|
8882
|
+
WHERE id = ? AND status IN ('pending', 'delivered')${scope.sql}`,
|
|
8883
|
+
args: [messageId, ...scope.args]
|
|
8685
8884
|
});
|
|
8686
8885
|
}
|
|
8687
|
-
async function markAcknowledged(messageId) {
|
|
8886
|
+
async function markAcknowledged(messageId, sessionScope) {
|
|
8688
8887
|
const client = getClient();
|
|
8888
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
8689
8889
|
await client.execute({
|
|
8690
|
-
sql:
|
|
8691
|
-
|
|
8890
|
+
sql: `UPDATE messages SET status = 'acknowledged', processed_at = ?
|
|
8891
|
+
WHERE id = ? AND status = 'read'${scope.sql}`,
|
|
8892
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), messageId, ...scope.args]
|
|
8692
8893
|
});
|
|
8693
8894
|
}
|
|
8694
|
-
async function markProcessed(messageId) {
|
|
8895
|
+
async function markProcessed(messageId, sessionScope) {
|
|
8695
8896
|
const client = getClient();
|
|
8897
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
8696
8898
|
await client.execute({
|
|
8697
|
-
sql:
|
|
8698
|
-
|
|
8899
|
+
sql: `UPDATE messages SET status = 'processed', processed_at = ?
|
|
8900
|
+
WHERE id = ?${scope.sql}`,
|
|
8901
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), messageId, ...scope.args]
|
|
8699
8902
|
});
|
|
8700
8903
|
}
|
|
8701
|
-
async function getMessageStatus(messageId) {
|
|
8904
|
+
async function getMessageStatus(messageId, sessionScope) {
|
|
8702
8905
|
const client = getClient();
|
|
8906
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
8703
8907
|
const result = await client.execute({
|
|
8704
|
-
sql:
|
|
8705
|
-
args: [messageId]
|
|
8908
|
+
sql: `SELECT status FROM messages WHERE id = ?${scope.sql}`,
|
|
8909
|
+
args: [messageId, ...scope.args]
|
|
8706
8910
|
});
|
|
8707
8911
|
return result.rows[0]?.status ?? null;
|
|
8708
8912
|
}
|
|
8709
|
-
async function getUnacknowledgedMessages(targetAgent) {
|
|
8913
|
+
async function getUnacknowledgedMessages(targetAgent, sessionScope) {
|
|
8710
8914
|
const client = getClient();
|
|
8915
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
8711
8916
|
const result = await client.execute({
|
|
8712
8917
|
sql: `SELECT * FROM messages
|
|
8713
|
-
WHERE target_agent = ? AND status IN ('pending', 'delivered', 'read')
|
|
8918
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered', 'read')${scope.sql}
|
|
8714
8919
|
ORDER BY id`,
|
|
8715
|
-
args: [targetAgent]
|
|
8920
|
+
args: [targetAgent, ...scope.args]
|
|
8716
8921
|
});
|
|
8717
8922
|
return result.rows.map((row) => rowToMessage(row));
|
|
8718
8923
|
}
|
|
8719
|
-
async function getReadMessages(targetAgent) {
|
|
8924
|
+
async function getReadMessages(targetAgent, sessionScope) {
|
|
8720
8925
|
const client = getClient();
|
|
8926
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
8721
8927
|
const result = await client.execute({
|
|
8722
|
-
sql:
|
|
8723
|
-
|
|
8928
|
+
sql: `SELECT * FROM messages
|
|
8929
|
+
WHERE target_agent = ? AND status = 'read'${scope.sql}
|
|
8930
|
+
ORDER BY id`,
|
|
8931
|
+
args: [targetAgent, ...scope.args]
|
|
8724
8932
|
});
|
|
8725
8933
|
return result.rows.map((row) => rowToMessage(row));
|
|
8726
8934
|
}
|
|
8727
|
-
async function markFailed(messageId, reason) {
|
|
8935
|
+
async function markFailed(messageId, reason, sessionScope) {
|
|
8728
8936
|
const client = getClient();
|
|
8937
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
8729
8938
|
await client.execute({
|
|
8730
|
-
sql:
|
|
8731
|
-
|
|
8939
|
+
sql: `UPDATE messages SET status = 'failed', failed_at = ?, failure_reason = ?
|
|
8940
|
+
WHERE id = ?${scope.sql}`,
|
|
8941
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId, ...scope.args]
|
|
8732
8942
|
});
|
|
8733
8943
|
}
|
|
8734
|
-
async function getFailedMessages() {
|
|
8944
|
+
async function getFailedMessages(sessionScope) {
|
|
8735
8945
|
const client = getClient();
|
|
8946
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
8736
8947
|
const result = await client.execute({
|
|
8737
|
-
sql:
|
|
8738
|
-
args: []
|
|
8948
|
+
sql: `SELECT * FROM messages WHERE status = 'failed'${scope.sql} ORDER BY created_at DESC`,
|
|
8949
|
+
args: [...scope.args]
|
|
8739
8950
|
});
|
|
8740
8951
|
return result.rows.map((row) => rowToMessage(row));
|
|
8741
8952
|
}
|
|
8742
|
-
async function retryPendingMessages() {
|
|
8953
|
+
async function retryPendingMessages(sessionScope) {
|
|
8743
8954
|
const client = getClient();
|
|
8955
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
8744
8956
|
const result = await client.execute({
|
|
8745
8957
|
sql: `SELECT * FROM messages
|
|
8746
|
-
WHERE status = 'pending' AND retry_count <
|
|
8958
|
+
WHERE status = 'pending' AND retry_count < ?${scope.sql}
|
|
8747
8959
|
ORDER BY id`,
|
|
8748
|
-
args: [MAX_RETRIES3]
|
|
8960
|
+
args: [MAX_RETRIES3, ...scope.args]
|
|
8749
8961
|
});
|
|
8750
8962
|
let delivered = 0;
|
|
8751
8963
|
for (const row of result.rows) {
|
|
@@ -8764,6 +8976,7 @@ var init_messaging = __esm({
|
|
|
8764
8976
|
"use strict";
|
|
8765
8977
|
init_database();
|
|
8766
8978
|
init_tmux_routing();
|
|
8979
|
+
init_task_scope();
|
|
8767
8980
|
MAX_RETRIES3 = 10;
|
|
8768
8981
|
_wsClientSend = null;
|
|
8769
8982
|
}
|
|
@@ -8961,11 +9174,11 @@ init_crm_bridge();
|
|
|
8961
9174
|
|
|
8962
9175
|
// src/lib/pipeline-router.ts
|
|
8963
9176
|
init_database();
|
|
8964
|
-
import
|
|
9177
|
+
import crypto3 from "crypto";
|
|
8965
9178
|
async function sinkConversationStore(msg, agentResponse, agentName) {
|
|
8966
9179
|
try {
|
|
8967
9180
|
const client = getClient();
|
|
8968
|
-
const id =
|
|
9181
|
+
const id = crypto3.randomUUID();
|
|
8969
9182
|
const mediaJson = msg.media ? JSON.stringify(msg.media) : null;
|
|
8970
9183
|
await client.execute({
|
|
8971
9184
|
sql: `INSERT INTO conversations
|
|
@@ -9015,7 +9228,7 @@ async function sinkMemory(msg, agentResponse, agentName) {
|
|
|
9015
9228
|
].filter(Boolean).join("\n");
|
|
9016
9229
|
const vector = await embed2(rawText);
|
|
9017
9230
|
await writeMemory2({
|
|
9018
|
-
id:
|
|
9231
|
+
id: crypto3.randomUUID(),
|
|
9019
9232
|
agent_id: agentName ?? "gateway",
|
|
9020
9233
|
agent_role: "gateway",
|
|
9021
9234
|
session_id: `gateway-${msg.platform}`,
|
|
@@ -11697,10 +11910,10 @@ var SlackAdapter = class {
|
|
|
11697
11910
|
import { execFile } from "child_process";
|
|
11698
11911
|
import { promisify } from "util";
|
|
11699
11912
|
import os6 from "os";
|
|
11700
|
-
import
|
|
11913
|
+
import path9 from "path";
|
|
11701
11914
|
var execFileAsync = promisify(execFile);
|
|
11702
11915
|
var POLL_INTERVAL_MS = 5e3;
|
|
11703
|
-
var MESSAGES_DB_PATH =
|
|
11916
|
+
var MESSAGES_DB_PATH = path9.join(
|
|
11704
11917
|
process.env.HOME ?? os6.homedir(),
|
|
11705
11918
|
"Library/Messages/chat.db"
|
|
11706
11919
|
);
|
|
@@ -12544,11 +12757,11 @@ async function ensureCRMContact(info) {
|
|
|
12544
12757
|
}
|
|
12545
12758
|
|
|
12546
12759
|
// src/automation/trigger-engine.ts
|
|
12547
|
-
import { readFileSync as
|
|
12760
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, existsSync as existsSync17, mkdirSync as mkdirSync9 } from "fs";
|
|
12548
12761
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
12549
|
-
import
|
|
12550
|
-
import
|
|
12551
|
-
var TRIGGERS_PATH =
|
|
12762
|
+
import path21 from "path";
|
|
12763
|
+
import os13 from "os";
|
|
12764
|
+
var TRIGGERS_PATH = path21.join(os13.homedir(), ".exe-os", "triggers.json");
|
|
12552
12765
|
var GRAPH_API_VERSION = "v21.0";
|
|
12553
12766
|
function substituteTemplate(template, record) {
|
|
12554
12767
|
return template.replace(
|
|
@@ -12602,9 +12815,9 @@ function evaluateConditions(conditions, record) {
|
|
|
12602
12815
|
return conditions.every((c) => evaluateCondition(c, record));
|
|
12603
12816
|
}
|
|
12604
12817
|
function loadTriggers(project) {
|
|
12605
|
-
if (!
|
|
12818
|
+
if (!existsSync17(TRIGGERS_PATH)) return [];
|
|
12606
12819
|
try {
|
|
12607
|
-
const raw =
|
|
12820
|
+
const raw = readFileSync14(TRIGGERS_PATH, "utf-8");
|
|
12608
12821
|
const all = JSON.parse(raw);
|
|
12609
12822
|
if (!Array.isArray(all)) return [];
|
|
12610
12823
|
if (project) {
|