@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/index.js
CHANGED
|
@@ -25,6 +25,44 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
25
25
|
};
|
|
26
26
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
27
|
|
|
28
|
+
// src/lib/secure-files.ts
|
|
29
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
30
|
+
import { chmod, mkdir } from "fs/promises";
|
|
31
|
+
async function ensurePrivateDir(dirPath) {
|
|
32
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
33
|
+
try {
|
|
34
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function ensurePrivateDirSync(dirPath) {
|
|
39
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
40
|
+
try {
|
|
41
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function enforcePrivateFile(filePath) {
|
|
46
|
+
try {
|
|
47
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function enforcePrivateFileSync(filePath) {
|
|
52
|
+
try {
|
|
53
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
58
|
+
var init_secure_files = __esm({
|
|
59
|
+
"src/lib/secure-files.ts"() {
|
|
60
|
+
"use strict";
|
|
61
|
+
PRIVATE_DIR_MODE = 448;
|
|
62
|
+
PRIVATE_FILE_MODE = 384;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
28
66
|
// src/lib/config.ts
|
|
29
67
|
var config_exports = {};
|
|
30
68
|
__export(config_exports, {
|
|
@@ -41,8 +79,8 @@ __export(config_exports, {
|
|
|
41
79
|
migrateConfig: () => migrateConfig,
|
|
42
80
|
saveConfig: () => saveConfig
|
|
43
81
|
});
|
|
44
|
-
import { readFile, writeFile
|
|
45
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
82
|
+
import { readFile, writeFile } from "fs/promises";
|
|
83
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
46
84
|
import path2 from "path";
|
|
47
85
|
import os2 from "os";
|
|
48
86
|
function resolveDataDir() {
|
|
@@ -50,7 +88,7 @@ function resolveDataDir() {
|
|
|
50
88
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
51
89
|
const newDir = path2.join(os2.homedir(), ".exe-os");
|
|
52
90
|
const legacyDir = path2.join(os2.homedir(), ".exe-mem");
|
|
53
|
-
if (!
|
|
91
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
54
92
|
try {
|
|
55
93
|
renameSync(legacyDir, newDir);
|
|
56
94
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -113,9 +151,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
113
151
|
}
|
|
114
152
|
async function loadConfig() {
|
|
115
153
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
116
|
-
await
|
|
154
|
+
await ensurePrivateDir(dir);
|
|
117
155
|
const configPath = path2.join(dir, "config.json");
|
|
118
|
-
if (!
|
|
156
|
+
if (!existsSync2(configPath)) {
|
|
119
157
|
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
120
158
|
}
|
|
121
159
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -128,6 +166,7 @@ async function loadConfig() {
|
|
|
128
166
|
`);
|
|
129
167
|
try {
|
|
130
168
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
169
|
+
await enforcePrivateFile(configPath);
|
|
131
170
|
} catch {
|
|
132
171
|
}
|
|
133
172
|
}
|
|
@@ -146,7 +185,7 @@ async function loadConfig() {
|
|
|
146
185
|
function loadConfigSync() {
|
|
147
186
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
148
187
|
const configPath = path2.join(dir, "config.json");
|
|
149
|
-
if (!
|
|
188
|
+
if (!existsSync2(configPath)) {
|
|
150
189
|
return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
|
|
151
190
|
}
|
|
152
191
|
try {
|
|
@@ -164,12 +203,10 @@ function loadConfigSync() {
|
|
|
164
203
|
}
|
|
165
204
|
async function saveConfig(config2) {
|
|
166
205
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
167
|
-
await
|
|
206
|
+
await ensurePrivateDir(dir);
|
|
168
207
|
const configPath = path2.join(dir, "config.json");
|
|
169
208
|
await writeFile(configPath, JSON.stringify(config2, null, 2) + "\n");
|
|
170
|
-
|
|
171
|
-
await chmod(configPath, 384);
|
|
172
|
-
}
|
|
209
|
+
await enforcePrivateFile(configPath);
|
|
173
210
|
}
|
|
174
211
|
async function loadConfigFrom(configPath) {
|
|
175
212
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -189,6 +226,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
189
226
|
var init_config = __esm({
|
|
190
227
|
"src/lib/config.ts"() {
|
|
191
228
|
"use strict";
|
|
229
|
+
init_secure_files();
|
|
192
230
|
EXE_AI_DIR = resolveDataDir();
|
|
193
231
|
DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
|
|
194
232
|
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
@@ -305,10 +343,10 @@ __export(agent_config_exports, {
|
|
|
305
343
|
saveAgentConfig: () => saveAgentConfig,
|
|
306
344
|
setAgentRuntime: () => setAgentRuntime
|
|
307
345
|
});
|
|
308
|
-
import { readFileSync as readFileSync2, writeFileSync, existsSync as
|
|
346
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
309
347
|
import path3 from "path";
|
|
310
348
|
function loadAgentConfig() {
|
|
311
|
-
if (!
|
|
349
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
312
350
|
try {
|
|
313
351
|
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
314
352
|
} catch {
|
|
@@ -317,8 +355,9 @@ function loadAgentConfig() {
|
|
|
317
355
|
}
|
|
318
356
|
function saveAgentConfig(config2) {
|
|
319
357
|
const dir = path3.dirname(AGENT_CONFIG_PATH);
|
|
320
|
-
|
|
358
|
+
ensurePrivateDirSync(dir);
|
|
321
359
|
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
360
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
322
361
|
}
|
|
323
362
|
function getAgentRuntime(agentId) {
|
|
324
363
|
const config2 = loadAgentConfig();
|
|
@@ -358,6 +397,7 @@ var init_agent_config = __esm({
|
|
|
358
397
|
"use strict";
|
|
359
398
|
init_config();
|
|
360
399
|
init_runtime_table();
|
|
400
|
+
init_secure_files();
|
|
361
401
|
AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
|
|
362
402
|
KNOWN_RUNTIMES = {
|
|
363
403
|
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
@@ -405,7 +445,7 @@ __export(employees_exports, {
|
|
|
405
445
|
validateEmployeeName: () => validateEmployeeName
|
|
406
446
|
});
|
|
407
447
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
408
|
-
import { existsSync as
|
|
448
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
409
449
|
import { execSync } from "child_process";
|
|
410
450
|
import path4 from "path";
|
|
411
451
|
import os3 from "os";
|
|
@@ -444,7 +484,7 @@ function validateEmployeeName(name) {
|
|
|
444
484
|
return { valid: true };
|
|
445
485
|
}
|
|
446
486
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
447
|
-
if (!
|
|
487
|
+
if (!existsSync4(employeesPath)) {
|
|
448
488
|
return [];
|
|
449
489
|
}
|
|
450
490
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -459,7 +499,7 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
|
459
499
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
460
500
|
}
|
|
461
501
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
462
|
-
if (!
|
|
502
|
+
if (!existsSync4(employeesPath)) return [];
|
|
463
503
|
try {
|
|
464
504
|
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
465
505
|
} catch {
|
|
@@ -507,7 +547,7 @@ function appendToCoordinatorTeam(employee) {
|
|
|
507
547
|
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
508
548
|
if (!coordinator) return;
|
|
509
549
|
const idPath = path4.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
510
|
-
if (!
|
|
550
|
+
if (!existsSync4(idPath)) return;
|
|
511
551
|
const content = readFileSync3(idPath, "utf-8");
|
|
512
552
|
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
513
553
|
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
@@ -561,9 +601,9 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
561
601
|
const identityDir = path4.join(os3.homedir(), ".exe-os", "identity");
|
|
562
602
|
const oldPath = path4.join(identityDir, `${oldName}.md`);
|
|
563
603
|
const newPath = path4.join(identityDir, `${emp.name}.md`);
|
|
564
|
-
if (
|
|
604
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
565
605
|
renameSync2(oldPath, newPath);
|
|
566
|
-
} else if (
|
|
606
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
567
607
|
const content = readFileSync3(oldPath, "utf-8");
|
|
568
608
|
writeFileSync2(newPath, content, "utf-8");
|
|
569
609
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
@@ -606,7 +646,7 @@ function registerBinSymlinks(name) {
|
|
|
606
646
|
for (const suffix of ["", "-opencode"]) {
|
|
607
647
|
const linkName = `${name}${suffix}`;
|
|
608
648
|
const linkPath = path4.join(binDir, linkName);
|
|
609
|
-
if (
|
|
649
|
+
if (existsSync4(linkPath)) {
|
|
610
650
|
skipped.push(linkName);
|
|
611
651
|
continue;
|
|
612
652
|
}
|
|
@@ -640,13 +680,13 @@ __export(session_registry_exports, {
|
|
|
640
680
|
pruneStaleSessions: () => pruneStaleSessions,
|
|
641
681
|
registerSession: () => registerSession
|
|
642
682
|
});
|
|
643
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as
|
|
683
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync5 } from "fs";
|
|
644
684
|
import { execSync as execSync2 } from "child_process";
|
|
645
685
|
import path5 from "path";
|
|
646
686
|
import os4 from "os";
|
|
647
687
|
function registerSession(entry) {
|
|
648
688
|
const dir = path5.dirname(REGISTRY_PATH);
|
|
649
|
-
if (!
|
|
689
|
+
if (!existsSync5(dir)) {
|
|
650
690
|
mkdirSync2(dir, { recursive: true });
|
|
651
691
|
}
|
|
652
692
|
const sessions = listSessions();
|
|
@@ -960,16 +1000,16 @@ __export(intercom_queue_exports, {
|
|
|
960
1000
|
queueIntercom: () => queueIntercom,
|
|
961
1001
|
readQueue: () => readQueue
|
|
962
1002
|
});
|
|
963
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as
|
|
1003
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
964
1004
|
import path6 from "path";
|
|
965
1005
|
import os5 from "os";
|
|
966
1006
|
function ensureDir() {
|
|
967
1007
|
const dir = path6.dirname(QUEUE_PATH);
|
|
968
|
-
if (!
|
|
1008
|
+
if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
|
|
969
1009
|
}
|
|
970
1010
|
function readQueue() {
|
|
971
1011
|
try {
|
|
972
|
-
if (!
|
|
1012
|
+
if (!existsSync6(QUEUE_PATH)) return [];
|
|
973
1013
|
return JSON.parse(readFileSync5(QUEUE_PATH, "utf8"));
|
|
974
1014
|
} catch {
|
|
975
1015
|
return [];
|
|
@@ -1716,13 +1756,50 @@ var init_database_adapter = __esm({
|
|
|
1716
1756
|
}
|
|
1717
1757
|
});
|
|
1718
1758
|
|
|
1759
|
+
// src/lib/daemon-auth.ts
|
|
1760
|
+
import crypto from "crypto";
|
|
1761
|
+
import path8 from "path";
|
|
1762
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
1763
|
+
function normalizeToken(token) {
|
|
1764
|
+
if (!token) return null;
|
|
1765
|
+
const trimmed = token.trim();
|
|
1766
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1767
|
+
}
|
|
1768
|
+
function readDaemonToken() {
|
|
1769
|
+
try {
|
|
1770
|
+
if (!existsSync7(DAEMON_TOKEN_PATH)) return null;
|
|
1771
|
+
return normalizeToken(readFileSync6(DAEMON_TOKEN_PATH, "utf8"));
|
|
1772
|
+
} catch {
|
|
1773
|
+
return null;
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
function ensureDaemonToken(seed) {
|
|
1777
|
+
const existing = readDaemonToken();
|
|
1778
|
+
if (existing) return existing;
|
|
1779
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
1780
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
1781
|
+
writeFileSync5(DAEMON_TOKEN_PATH, `${token}
|
|
1782
|
+
`, "utf8");
|
|
1783
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
1784
|
+
return token;
|
|
1785
|
+
}
|
|
1786
|
+
var DAEMON_TOKEN_PATH;
|
|
1787
|
+
var init_daemon_auth = __esm({
|
|
1788
|
+
"src/lib/daemon-auth.ts"() {
|
|
1789
|
+
"use strict";
|
|
1790
|
+
init_config();
|
|
1791
|
+
init_secure_files();
|
|
1792
|
+
DAEMON_TOKEN_PATH = path8.join(EXE_AI_DIR, "exed.token");
|
|
1793
|
+
}
|
|
1794
|
+
});
|
|
1795
|
+
|
|
1719
1796
|
// src/lib/exe-daemon-client.ts
|
|
1720
1797
|
import net from "net";
|
|
1721
1798
|
import os7 from "os";
|
|
1722
1799
|
import { spawn } from "child_process";
|
|
1723
1800
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
1724
|
-
import { existsSync as
|
|
1725
|
-
import
|
|
1801
|
+
import { existsSync as existsSync8, unlinkSync as unlinkSync2, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
|
|
1802
|
+
import path9 from "path";
|
|
1726
1803
|
import { fileURLToPath } from "url";
|
|
1727
1804
|
function handleData(chunk) {
|
|
1728
1805
|
_buffer += chunk.toString();
|
|
@@ -1750,9 +1827,9 @@ function handleData(chunk) {
|
|
|
1750
1827
|
}
|
|
1751
1828
|
}
|
|
1752
1829
|
function cleanupStaleFiles() {
|
|
1753
|
-
if (
|
|
1830
|
+
if (existsSync8(PID_PATH)) {
|
|
1754
1831
|
try {
|
|
1755
|
-
const pid = parseInt(
|
|
1832
|
+
const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
|
|
1756
1833
|
if (pid > 0) {
|
|
1757
1834
|
try {
|
|
1758
1835
|
process.kill(pid, 0);
|
|
@@ -1773,11 +1850,11 @@ function cleanupStaleFiles() {
|
|
|
1773
1850
|
}
|
|
1774
1851
|
}
|
|
1775
1852
|
function findPackageRoot() {
|
|
1776
|
-
let dir =
|
|
1777
|
-
const { root } =
|
|
1853
|
+
let dir = path9.dirname(fileURLToPath(import.meta.url));
|
|
1854
|
+
const { root } = path9.parse(dir);
|
|
1778
1855
|
while (dir !== root) {
|
|
1779
|
-
if (
|
|
1780
|
-
dir =
|
|
1856
|
+
if (existsSync8(path9.join(dir, "package.json"))) return dir;
|
|
1857
|
+
dir = path9.dirname(dir);
|
|
1781
1858
|
}
|
|
1782
1859
|
return null;
|
|
1783
1860
|
}
|
|
@@ -1803,16 +1880,17 @@ function spawnDaemon() {
|
|
|
1803
1880
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1804
1881
|
return;
|
|
1805
1882
|
}
|
|
1806
|
-
const daemonPath =
|
|
1807
|
-
if (!
|
|
1883
|
+
const daemonPath = path9.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1884
|
+
if (!existsSync8(daemonPath)) {
|
|
1808
1885
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1809
1886
|
`);
|
|
1810
1887
|
return;
|
|
1811
1888
|
}
|
|
1812
1889
|
const resolvedPath = daemonPath;
|
|
1890
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1813
1891
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1814
1892
|
`);
|
|
1815
|
-
const logPath =
|
|
1893
|
+
const logPath = path9.join(path9.dirname(SOCKET_PATH), "exed.log");
|
|
1816
1894
|
let stderrFd = "ignore";
|
|
1817
1895
|
try {
|
|
1818
1896
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1830,7 +1908,8 @@ function spawnDaemon() {
|
|
|
1830
1908
|
TMUX_PANE: void 0,
|
|
1831
1909
|
// Prevents resolveExeSession() from scoping to one session
|
|
1832
1910
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
1833
|
-
EXE_DAEMON_PID: PID_PATH
|
|
1911
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
1912
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
1834
1913
|
}
|
|
1835
1914
|
});
|
|
1836
1915
|
child.unref();
|
|
@@ -1940,13 +2019,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
1940
2019
|
return;
|
|
1941
2020
|
}
|
|
1942
2021
|
const id = randomUUID2();
|
|
2022
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
1943
2023
|
const timer = setTimeout(() => {
|
|
1944
2024
|
_pending.delete(id);
|
|
1945
2025
|
resolve({ error: "Request timeout" });
|
|
1946
2026
|
}, timeoutMs);
|
|
1947
2027
|
_pending.set(id, { resolve, timer });
|
|
1948
2028
|
try {
|
|
1949
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
2029
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
1950
2030
|
} catch {
|
|
1951
2031
|
clearTimeout(timer);
|
|
1952
2032
|
_pending.delete(id);
|
|
@@ -1975,9 +2055,9 @@ function killAndRespawnDaemon() {
|
|
|
1975
2055
|
}
|
|
1976
2056
|
try {
|
|
1977
2057
|
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
1978
|
-
if (
|
|
2058
|
+
if (existsSync8(PID_PATH)) {
|
|
1979
2059
|
try {
|
|
1980
|
-
const pid = parseInt(
|
|
2060
|
+
const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
|
|
1981
2061
|
if (pid > 0) {
|
|
1982
2062
|
try {
|
|
1983
2063
|
process.kill(pid, "SIGKILL");
|
|
@@ -2097,17 +2177,19 @@ function disconnectClient() {
|
|
|
2097
2177
|
function isClientConnected() {
|
|
2098
2178
|
return _connected;
|
|
2099
2179
|
}
|
|
2100
|
-
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;
|
|
2180
|
+
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;
|
|
2101
2181
|
var init_exe_daemon_client = __esm({
|
|
2102
2182
|
"src/lib/exe-daemon-client.ts"() {
|
|
2103
2183
|
"use strict";
|
|
2104
2184
|
init_config();
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2185
|
+
init_daemon_auth();
|
|
2186
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path9.join(EXE_AI_DIR, "exed.sock");
|
|
2187
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path9.join(EXE_AI_DIR, "exed.pid");
|
|
2188
|
+
SPAWN_LOCK_PATH = path9.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
2108
2189
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
2109
2190
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
2110
2191
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
2192
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
2111
2193
|
_socket = null;
|
|
2112
2194
|
_connected = false;
|
|
2113
2195
|
_buffer = "";
|
|
@@ -2692,6 +2774,7 @@ async function ensureSchema() {
|
|
|
2692
2774
|
project TEXT NOT NULL,
|
|
2693
2775
|
summary TEXT NOT NULL,
|
|
2694
2776
|
task_file TEXT,
|
|
2777
|
+
session_scope TEXT,
|
|
2695
2778
|
read INTEGER NOT NULL DEFAULT 0,
|
|
2696
2779
|
created_at TEXT NOT NULL
|
|
2697
2780
|
);
|
|
@@ -2700,7 +2783,7 @@ async function ensureSchema() {
|
|
|
2700
2783
|
ON notifications(read);
|
|
2701
2784
|
|
|
2702
2785
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
2703
|
-
ON notifications(agent_id);
|
|
2786
|
+
ON notifications(agent_id, session_scope);
|
|
2704
2787
|
|
|
2705
2788
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
2706
2789
|
ON notifications(task_file);
|
|
@@ -2738,6 +2821,7 @@ async function ensureSchema() {
|
|
|
2738
2821
|
target_agent TEXT NOT NULL,
|
|
2739
2822
|
target_project TEXT,
|
|
2740
2823
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
2824
|
+
session_scope TEXT,
|
|
2741
2825
|
content TEXT NOT NULL,
|
|
2742
2826
|
priority TEXT DEFAULT 'normal',
|
|
2743
2827
|
status TEXT DEFAULT 'pending',
|
|
@@ -2751,10 +2835,31 @@ async function ensureSchema() {
|
|
|
2751
2835
|
);
|
|
2752
2836
|
|
|
2753
2837
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
2754
|
-
ON messages(target_agent, status);
|
|
2838
|
+
ON messages(target_agent, session_scope, status);
|
|
2755
2839
|
|
|
2756
2840
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
2757
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
2841
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
2842
|
+
`);
|
|
2843
|
+
try {
|
|
2844
|
+
await client.execute({
|
|
2845
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
2846
|
+
args: []
|
|
2847
|
+
});
|
|
2848
|
+
} catch {
|
|
2849
|
+
}
|
|
2850
|
+
try {
|
|
2851
|
+
await client.execute({
|
|
2852
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
2853
|
+
args: []
|
|
2854
|
+
});
|
|
2855
|
+
} catch {
|
|
2856
|
+
}
|
|
2857
|
+
await client.executeMultiple(`
|
|
2858
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
2859
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
2860
|
+
|
|
2861
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
2862
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
2758
2863
|
`);
|
|
2759
2864
|
try {
|
|
2760
2865
|
await client.execute({
|
|
@@ -3338,6 +3443,13 @@ async function ensureSchema() {
|
|
|
3338
3443
|
} catch {
|
|
3339
3444
|
}
|
|
3340
3445
|
}
|
|
3446
|
+
try {
|
|
3447
|
+
await client.execute({
|
|
3448
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
3449
|
+
args: []
|
|
3450
|
+
});
|
|
3451
|
+
} catch {
|
|
3452
|
+
}
|
|
3341
3453
|
}
|
|
3342
3454
|
async function disposeDatabase() {
|
|
3343
3455
|
if (_walCheckpointTimer) {
|
|
@@ -3376,18 +3488,21 @@ var init_database = __esm({
|
|
|
3376
3488
|
});
|
|
3377
3489
|
|
|
3378
3490
|
// src/lib/license.ts
|
|
3379
|
-
import { readFileSync as
|
|
3491
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
|
|
3380
3492
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3381
|
-
import
|
|
3493
|
+
import { createRequire as createRequire2 } from "module";
|
|
3494
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
3495
|
+
import os8 from "os";
|
|
3496
|
+
import path10 from "path";
|
|
3382
3497
|
import { jwtVerify, importSPKI } from "jose";
|
|
3383
3498
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
3384
3499
|
var init_license = __esm({
|
|
3385
3500
|
"src/lib/license.ts"() {
|
|
3386
3501
|
"use strict";
|
|
3387
3502
|
init_config();
|
|
3388
|
-
LICENSE_PATH =
|
|
3389
|
-
CACHE_PATH =
|
|
3390
|
-
DEVICE_ID_PATH =
|
|
3503
|
+
LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
|
|
3504
|
+
CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
3505
|
+
DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
|
|
3391
3506
|
PLAN_LIMITS = {
|
|
3392
3507
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
3393
3508
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -3399,12 +3514,12 @@ var init_license = __esm({
|
|
|
3399
3514
|
});
|
|
3400
3515
|
|
|
3401
3516
|
// src/lib/plan-limits.ts
|
|
3402
|
-
import { readFileSync as
|
|
3403
|
-
import
|
|
3517
|
+
import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
|
|
3518
|
+
import path11 from "path";
|
|
3404
3519
|
function getLicenseSync() {
|
|
3405
3520
|
try {
|
|
3406
|
-
if (!
|
|
3407
|
-
const raw = JSON.parse(
|
|
3521
|
+
if (!existsSync10(CACHE_PATH2)) return freeLicense();
|
|
3522
|
+
const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
|
|
3408
3523
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
3409
3524
|
const parts = raw.token.split(".");
|
|
3410
3525
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -3442,8 +3557,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
3442
3557
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
3443
3558
|
let count = 0;
|
|
3444
3559
|
try {
|
|
3445
|
-
if (
|
|
3446
|
-
const raw =
|
|
3560
|
+
if (existsSync10(filePath)) {
|
|
3561
|
+
const raw = readFileSync9(filePath, "utf8");
|
|
3447
3562
|
const employees = JSON.parse(raw);
|
|
3448
3563
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
3449
3564
|
}
|
|
@@ -3472,29 +3587,69 @@ var init_plan_limits = __esm({
|
|
|
3472
3587
|
this.name = "PlanLimitError";
|
|
3473
3588
|
}
|
|
3474
3589
|
};
|
|
3475
|
-
CACHE_PATH2 =
|
|
3590
|
+
CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
|
|
3591
|
+
}
|
|
3592
|
+
});
|
|
3593
|
+
|
|
3594
|
+
// src/lib/task-scope.ts
|
|
3595
|
+
var task_scope_exports = {};
|
|
3596
|
+
__export(task_scope_exports, {
|
|
3597
|
+
getCurrentSessionScope: () => getCurrentSessionScope,
|
|
3598
|
+
sessionScopeFilter: () => sessionScopeFilter,
|
|
3599
|
+
strictSessionScopeFilter: () => strictSessionScopeFilter
|
|
3600
|
+
});
|
|
3601
|
+
function getCurrentSessionScope() {
|
|
3602
|
+
try {
|
|
3603
|
+
return resolveExeSession();
|
|
3604
|
+
} catch {
|
|
3605
|
+
return null;
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
function sessionScopeFilter(sessionScope, tableAlias) {
|
|
3609
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
3610
|
+
if (!scope) return { sql: "", args: [] };
|
|
3611
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
3612
|
+
return {
|
|
3613
|
+
sql: ` AND (${col} IS NULL OR ${col} = ?)`,
|
|
3614
|
+
args: [scope]
|
|
3615
|
+
};
|
|
3616
|
+
}
|
|
3617
|
+
function strictSessionScopeFilter(sessionScope, tableAlias) {
|
|
3618
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
3619
|
+
if (!scope) return { sql: "", args: [] };
|
|
3620
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
3621
|
+
return {
|
|
3622
|
+
sql: ` AND ${col} = ?`,
|
|
3623
|
+
args: [scope]
|
|
3624
|
+
};
|
|
3625
|
+
}
|
|
3626
|
+
var init_task_scope = __esm({
|
|
3627
|
+
"src/lib/task-scope.ts"() {
|
|
3628
|
+
"use strict";
|
|
3629
|
+
init_tmux_routing();
|
|
3476
3630
|
}
|
|
3477
3631
|
});
|
|
3478
3632
|
|
|
3479
3633
|
// src/lib/notifications.ts
|
|
3480
|
-
import
|
|
3481
|
-
import
|
|
3482
|
-
import
|
|
3634
|
+
import crypto2 from "crypto";
|
|
3635
|
+
import path12 from "path";
|
|
3636
|
+
import os9 from "os";
|
|
3483
3637
|
import {
|
|
3484
|
-
readFileSync as
|
|
3638
|
+
readFileSync as readFileSync10,
|
|
3485
3639
|
readdirSync,
|
|
3486
3640
|
unlinkSync as unlinkSync3,
|
|
3487
|
-
existsSync as
|
|
3641
|
+
existsSync as existsSync11,
|
|
3488
3642
|
rmdirSync
|
|
3489
3643
|
} from "fs";
|
|
3490
3644
|
async function writeNotification(notification) {
|
|
3491
3645
|
try {
|
|
3492
3646
|
const client = getClient();
|
|
3493
|
-
const id =
|
|
3647
|
+
const id = crypto2.randomUUID();
|
|
3494
3648
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3649
|
+
const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
|
|
3495
3650
|
await client.execute({
|
|
3496
|
-
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
3497
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
3651
|
+
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
|
|
3652
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
3498
3653
|
args: [
|
|
3499
3654
|
id,
|
|
3500
3655
|
notification.agentId,
|
|
@@ -3503,6 +3658,7 @@ async function writeNotification(notification) {
|
|
|
3503
3658
|
notification.project,
|
|
3504
3659
|
notification.summary,
|
|
3505
3660
|
notification.taskFile ?? null,
|
|
3661
|
+
sessionScope,
|
|
3506
3662
|
now
|
|
3507
3663
|
]
|
|
3508
3664
|
});
|
|
@@ -3511,12 +3667,14 @@ async function writeNotification(notification) {
|
|
|
3511
3667
|
`);
|
|
3512
3668
|
}
|
|
3513
3669
|
}
|
|
3514
|
-
async function markAsReadByTaskFile(taskFile) {
|
|
3670
|
+
async function markAsReadByTaskFile(taskFile, sessionScope) {
|
|
3515
3671
|
try {
|
|
3516
3672
|
const client = getClient();
|
|
3673
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
3517
3674
|
await client.execute({
|
|
3518
|
-
sql:
|
|
3519
|
-
|
|
3675
|
+
sql: `UPDATE notifications SET read = 1
|
|
3676
|
+
WHERE task_file = ? AND read = 0${scope.sql}`,
|
|
3677
|
+
args: [taskFile, ...scope.args]
|
|
3520
3678
|
});
|
|
3521
3679
|
} catch {
|
|
3522
3680
|
}
|
|
@@ -3525,11 +3683,12 @@ var init_notifications = __esm({
|
|
|
3525
3683
|
"src/lib/notifications.ts"() {
|
|
3526
3684
|
"use strict";
|
|
3527
3685
|
init_database();
|
|
3686
|
+
init_task_scope();
|
|
3528
3687
|
}
|
|
3529
3688
|
});
|
|
3530
3689
|
|
|
3531
3690
|
// src/lib/session-kill-telemetry.ts
|
|
3532
|
-
import
|
|
3691
|
+
import crypto3 from "crypto";
|
|
3533
3692
|
async function recordSessionKill(input) {
|
|
3534
3693
|
try {
|
|
3535
3694
|
const client = getClient();
|
|
@@ -3539,7 +3698,7 @@ async function recordSessionKill(input) {
|
|
|
3539
3698
|
ticks_idle, estimated_tokens_saved)
|
|
3540
3699
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
3541
3700
|
args: [
|
|
3542
|
-
|
|
3701
|
+
crypto3.randomUUID(),
|
|
3543
3702
|
input.sessionName,
|
|
3544
3703
|
input.agentId,
|
|
3545
3704
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3562,35 +3721,6 @@ var init_session_kill_telemetry = __esm({
|
|
|
3562
3721
|
}
|
|
3563
3722
|
});
|
|
3564
3723
|
|
|
3565
|
-
// src/lib/task-scope.ts
|
|
3566
|
-
var task_scope_exports = {};
|
|
3567
|
-
__export(task_scope_exports, {
|
|
3568
|
-
getCurrentSessionScope: () => getCurrentSessionScope,
|
|
3569
|
-
sessionScopeFilter: () => sessionScopeFilter
|
|
3570
|
-
});
|
|
3571
|
-
function getCurrentSessionScope() {
|
|
3572
|
-
try {
|
|
3573
|
-
return resolveExeSession();
|
|
3574
|
-
} catch {
|
|
3575
|
-
return null;
|
|
3576
|
-
}
|
|
3577
|
-
}
|
|
3578
|
-
function sessionScopeFilter(sessionScope, tableAlias) {
|
|
3579
|
-
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
3580
|
-
if (!scope) return { sql: "", args: [] };
|
|
3581
|
-
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
3582
|
-
return {
|
|
3583
|
-
sql: ` AND (${col} IS NULL OR ${col} = ?)`,
|
|
3584
|
-
args: [scope]
|
|
3585
|
-
};
|
|
3586
|
-
}
|
|
3587
|
-
var init_task_scope = __esm({
|
|
3588
|
-
"src/lib/task-scope.ts"() {
|
|
3589
|
-
"use strict";
|
|
3590
|
-
init_tmux_routing();
|
|
3591
|
-
}
|
|
3592
|
-
});
|
|
3593
|
-
|
|
3594
3724
|
// src/lib/state-bus.ts
|
|
3595
3725
|
var StateBus, orgBus;
|
|
3596
3726
|
var init_state_bus = __esm({
|
|
@@ -3647,12 +3777,12 @@ var init_state_bus = __esm({
|
|
|
3647
3777
|
});
|
|
3648
3778
|
|
|
3649
3779
|
// src/lib/tasks-crud.ts
|
|
3650
|
-
import
|
|
3651
|
-
import
|
|
3652
|
-
import
|
|
3780
|
+
import crypto4 from "crypto";
|
|
3781
|
+
import path13 from "path";
|
|
3782
|
+
import os10 from "os";
|
|
3653
3783
|
import { execSync as execSync5 } from "child_process";
|
|
3654
3784
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
3655
|
-
import { existsSync as
|
|
3785
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
3656
3786
|
async function writeCheckpoint(input) {
|
|
3657
3787
|
const client = getClient();
|
|
3658
3788
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -3768,7 +3898,7 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
3768
3898
|
}
|
|
3769
3899
|
async function createTaskCore(input) {
|
|
3770
3900
|
const client = getClient();
|
|
3771
|
-
const id =
|
|
3901
|
+
const id = crypto4.randomUUID();
|
|
3772
3902
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3773
3903
|
const slug = slugify(input.title);
|
|
3774
3904
|
let earlySessionScope = null;
|
|
@@ -3827,8 +3957,8 @@ ${laneWarning}` : laneWarning;
|
|
|
3827
3957
|
}
|
|
3828
3958
|
if (input.baseDir) {
|
|
3829
3959
|
try {
|
|
3830
|
-
await mkdir3(
|
|
3831
|
-
await mkdir3(
|
|
3960
|
+
await mkdir3(path13.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
3961
|
+
await mkdir3(path13.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
3832
3962
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
3833
3963
|
await ensureGitignoreExe(input.baseDir);
|
|
3834
3964
|
} catch {
|
|
@@ -3864,13 +3994,19 @@ ${laneWarning}` : laneWarning;
|
|
|
3864
3994
|
});
|
|
3865
3995
|
if (input.baseDir) {
|
|
3866
3996
|
try {
|
|
3867
|
-
const EXE_OS_DIR =
|
|
3868
|
-
const mdPath =
|
|
3869
|
-
const mdDir =
|
|
3870
|
-
if (!
|
|
3997
|
+
const EXE_OS_DIR = path13.join(os10.homedir(), ".exe-os");
|
|
3998
|
+
const mdPath = path13.join(EXE_OS_DIR, taskFile);
|
|
3999
|
+
const mdDir = path13.dirname(mdPath);
|
|
4000
|
+
if (!existsSync12(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
3871
4001
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
3872
4002
|
const mdContent = `# ${input.title}
|
|
3873
4003
|
|
|
4004
|
+
## MANDATORY: When done
|
|
4005
|
+
|
|
4006
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
4007
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
4008
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
4009
|
+
|
|
3874
4010
|
**ID:** ${id}
|
|
3875
4011
|
**Status:** ${initialStatus}
|
|
3876
4012
|
**Priority:** ${input.priority}
|
|
@@ -3884,12 +4020,6 @@ ${laneWarning}` : laneWarning;
|
|
|
3884
4020
|
## Context
|
|
3885
4021
|
|
|
3886
4022
|
${input.context}
|
|
3887
|
-
|
|
3888
|
-
## MANDATORY: When done
|
|
3889
|
-
|
|
3890
|
-
You MUST call update_task with status "done" and a result summary when finished.
|
|
3891
|
-
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
3892
|
-
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
3893
4023
|
`;
|
|
3894
4024
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
3895
4025
|
} catch (err) {
|
|
@@ -4138,7 +4268,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
4138
4268
|
await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
4139
4269
|
} catch {
|
|
4140
4270
|
}
|
|
4141
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
4271
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
4142
4272
|
try {
|
|
4143
4273
|
const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
4144
4274
|
clearQueueForAgent2(String(row.assigned_to));
|
|
@@ -4167,9 +4297,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
4167
4297
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
4168
4298
|
}
|
|
4169
4299
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
4170
|
-
const archPath =
|
|
4300
|
+
const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
4171
4301
|
try {
|
|
4172
|
-
if (
|
|
4302
|
+
if (existsSync12(archPath)) return;
|
|
4173
4303
|
const template = [
|
|
4174
4304
|
`# ${projectName} \u2014 System Architecture`,
|
|
4175
4305
|
"",
|
|
@@ -4202,10 +4332,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
4202
4332
|
}
|
|
4203
4333
|
}
|
|
4204
4334
|
async function ensureGitignoreExe(baseDir) {
|
|
4205
|
-
const gitignorePath =
|
|
4335
|
+
const gitignorePath = path13.join(baseDir, ".gitignore");
|
|
4206
4336
|
try {
|
|
4207
|
-
if (
|
|
4208
|
-
const content =
|
|
4337
|
+
if (existsSync12(gitignorePath)) {
|
|
4338
|
+
const content = readFileSync11(gitignorePath, "utf-8");
|
|
4209
4339
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
4210
4340
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
4211
4341
|
} else {
|
|
@@ -4236,58 +4366,42 @@ var init_tasks_crud = __esm({
|
|
|
4236
4366
|
});
|
|
4237
4367
|
|
|
4238
4368
|
// src/lib/tasks-review.ts
|
|
4239
|
-
import
|
|
4240
|
-
import { existsSync as
|
|
4369
|
+
import path14 from "path";
|
|
4370
|
+
import { existsSync as existsSync13, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
4241
4371
|
async function countPendingReviews(sessionScope) {
|
|
4242
4372
|
const client = getClient();
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
args: [sessionScope]
|
|
4247
|
-
});
|
|
4248
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
4249
|
-
}
|
|
4373
|
+
const scope = strictSessionScopeFilter(
|
|
4374
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
4375
|
+
);
|
|
4250
4376
|
const result = await client.execute({
|
|
4251
|
-
sql:
|
|
4252
|
-
|
|
4377
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
4378
|
+
WHERE status = 'needs_review'${scope.sql}`,
|
|
4379
|
+
args: [...scope.args]
|
|
4253
4380
|
});
|
|
4254
4381
|
return Number(result.rows[0]?.cnt) || 0;
|
|
4255
4382
|
}
|
|
4256
4383
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
4257
4384
|
const client = getClient();
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
WHERE status = 'needs_review' AND updated_at > ?
|
|
4262
|
-
AND session_scope = ?`,
|
|
4263
|
-
args: [sinceIso, sessionScope]
|
|
4264
|
-
});
|
|
4265
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
4266
|
-
}
|
|
4385
|
+
const scope = strictSessionScopeFilter(
|
|
4386
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
4387
|
+
);
|
|
4267
4388
|
const result = await client.execute({
|
|
4268
4389
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
4269
|
-
WHERE status = 'needs_review' AND updated_at >
|
|
4270
|
-
args: [sinceIso]
|
|
4390
|
+
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
4391
|
+
args: [sinceIso, ...scope.args]
|
|
4271
4392
|
});
|
|
4272
4393
|
return Number(result.rows[0]?.cnt) || 0;
|
|
4273
4394
|
}
|
|
4274
4395
|
async function listPendingReviews(limit, sessionScope) {
|
|
4275
4396
|
const client = getClient();
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
WHERE status = 'needs_review'
|
|
4280
|
-
AND session_scope = ?
|
|
4281
|
-
ORDER BY updated_at ASC LIMIT ?`,
|
|
4282
|
-
args: [sessionScope, limit]
|
|
4283
|
-
});
|
|
4284
|
-
return result2.rows;
|
|
4285
|
-
}
|
|
4397
|
+
const scope = strictSessionScopeFilter(
|
|
4398
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
4399
|
+
);
|
|
4286
4400
|
const result = await client.execute({
|
|
4287
4401
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
4288
|
-
WHERE status = 'needs_review'
|
|
4402
|
+
WHERE status = 'needs_review'${scope.sql}
|
|
4289
4403
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
4290
|
-
args: [limit]
|
|
4404
|
+
args: [...scope.args, limit]
|
|
4291
4405
|
});
|
|
4292
4406
|
return result.rows;
|
|
4293
4407
|
}
|
|
@@ -4299,7 +4413,7 @@ async function cleanupOrphanedReviews() {
|
|
|
4299
4413
|
WHERE status IN ('open', 'needs_review', 'in_progress')
|
|
4300
4414
|
AND assigned_by = 'system'
|
|
4301
4415
|
AND title LIKE 'Review:%'
|
|
4302
|
-
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
4416
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
|
|
4303
4417
|
args: [now]
|
|
4304
4418
|
});
|
|
4305
4419
|
const r1b = await client.execute({
|
|
@@ -4418,11 +4532,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4418
4532
|
);
|
|
4419
4533
|
}
|
|
4420
4534
|
try {
|
|
4421
|
-
const cacheDir =
|
|
4422
|
-
if (
|
|
4535
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
4536
|
+
if (existsSync13(cacheDir)) {
|
|
4423
4537
|
for (const f of readdirSync2(cacheDir)) {
|
|
4424
4538
|
if (f.startsWith("review-notified-")) {
|
|
4425
|
-
unlinkSync4(
|
|
4539
|
+
unlinkSync4(path14.join(cacheDir, f));
|
|
4426
4540
|
}
|
|
4427
4541
|
}
|
|
4428
4542
|
}
|
|
@@ -4439,11 +4553,12 @@ var init_tasks_review = __esm({
|
|
|
4439
4553
|
init_tmux_routing();
|
|
4440
4554
|
init_session_key();
|
|
4441
4555
|
init_state_bus();
|
|
4556
|
+
init_task_scope();
|
|
4442
4557
|
}
|
|
4443
4558
|
});
|
|
4444
4559
|
|
|
4445
4560
|
// src/lib/tasks-chain.ts
|
|
4446
|
-
import
|
|
4561
|
+
import path15 from "path";
|
|
4447
4562
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
4448
4563
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
4449
4564
|
const client = getClient();
|
|
@@ -4460,7 +4575,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
4460
4575
|
});
|
|
4461
4576
|
for (const ur of unblockedRows.rows) {
|
|
4462
4577
|
try {
|
|
4463
|
-
const ubFile =
|
|
4578
|
+
const ubFile = path15.join(baseDir, String(ur.task_file));
|
|
4464
4579
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
4465
4580
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
4466
4581
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -4495,7 +4610,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
|
|
|
4495
4610
|
const scScope = sessionScopeFilter();
|
|
4496
4611
|
const remaining = await client.execute({
|
|
4497
4612
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
4498
|
-
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
|
|
4613
|
+
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
|
|
4499
4614
|
args: [parentTaskId, ...scScope.args]
|
|
4500
4615
|
});
|
|
4501
4616
|
const cnt = Number(remaining.rows[0]?.cnt ?? 1);
|
|
@@ -4529,7 +4644,7 @@ var init_tasks_chain = __esm({
|
|
|
4529
4644
|
|
|
4530
4645
|
// src/lib/project-name.ts
|
|
4531
4646
|
import { execSync as execSync6 } from "child_process";
|
|
4532
|
-
import
|
|
4647
|
+
import path16 from "path";
|
|
4533
4648
|
function getProjectName(cwd) {
|
|
4534
4649
|
const dir = cwd ?? process.cwd();
|
|
4535
4650
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -4542,7 +4657,7 @@ function getProjectName(cwd) {
|
|
|
4542
4657
|
timeout: 2e3,
|
|
4543
4658
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4544
4659
|
}).trim();
|
|
4545
|
-
repoRoot =
|
|
4660
|
+
repoRoot = path16.dirname(gitCommonDir);
|
|
4546
4661
|
} catch {
|
|
4547
4662
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
4548
4663
|
cwd: dir,
|
|
@@ -4551,11 +4666,11 @@ function getProjectName(cwd) {
|
|
|
4551
4666
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4552
4667
|
}).trim();
|
|
4553
4668
|
}
|
|
4554
|
-
_cached2 =
|
|
4669
|
+
_cached2 = path16.basename(repoRoot);
|
|
4555
4670
|
_cachedCwd = dir;
|
|
4556
4671
|
return _cached2;
|
|
4557
4672
|
} catch {
|
|
4558
|
-
_cached2 =
|
|
4673
|
+
_cached2 = path16.basename(dir);
|
|
4559
4674
|
_cachedCwd = dir;
|
|
4560
4675
|
return _cached2;
|
|
4561
4676
|
}
|
|
@@ -4705,10 +4820,10 @@ __export(behaviors_exports, {
|
|
|
4705
4820
|
listBehaviorsByDomain: () => listBehaviorsByDomain,
|
|
4706
4821
|
storeBehavior: () => storeBehavior
|
|
4707
4822
|
});
|
|
4708
|
-
import
|
|
4823
|
+
import crypto5 from "crypto";
|
|
4709
4824
|
async function storeBehavior(opts) {
|
|
4710
4825
|
const client = getClient();
|
|
4711
|
-
const id =
|
|
4826
|
+
const id = crypto5.randomUUID();
|
|
4712
4827
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4713
4828
|
await client.execute({
|
|
4714
4829
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -4792,7 +4907,7 @@ __export(skill_learning_exports, {
|
|
|
4792
4907
|
storeTrajectory: () => storeTrajectory,
|
|
4793
4908
|
sweepTrajectories: () => sweepTrajectories
|
|
4794
4909
|
});
|
|
4795
|
-
import
|
|
4910
|
+
import crypto6 from "crypto";
|
|
4796
4911
|
async function extractTrajectory(taskId, agentId) {
|
|
4797
4912
|
const client = getClient();
|
|
4798
4913
|
const result = await client.execute({
|
|
@@ -4821,11 +4936,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
4821
4936
|
return signature;
|
|
4822
4937
|
}
|
|
4823
4938
|
function hashSignature(signature) {
|
|
4824
|
-
return
|
|
4939
|
+
return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
4825
4940
|
}
|
|
4826
4941
|
async function storeTrajectory(opts) {
|
|
4827
4942
|
const client = getClient();
|
|
4828
|
-
const id =
|
|
4943
|
+
const id = crypto6.randomUUID();
|
|
4829
4944
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4830
4945
|
const signatureHash = hashSignature(opts.signature);
|
|
4831
4946
|
await client.execute({
|
|
@@ -5090,8 +5205,8 @@ __export(tasks_exports, {
|
|
|
5090
5205
|
updateTaskStatus: () => updateTaskStatus,
|
|
5091
5206
|
writeCheckpoint: () => writeCheckpoint
|
|
5092
5207
|
});
|
|
5093
|
-
import
|
|
5094
|
-
import { writeFileSync as
|
|
5208
|
+
import path17 from "path";
|
|
5209
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
|
|
5095
5210
|
async function createTask(input) {
|
|
5096
5211
|
const result = await createTaskCore(input);
|
|
5097
5212
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -5110,12 +5225,12 @@ async function updateTask(input) {
|
|
|
5110
5225
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
5111
5226
|
try {
|
|
5112
5227
|
const agent = String(row.assigned_to);
|
|
5113
|
-
const cacheDir =
|
|
5114
|
-
const cachePath =
|
|
5228
|
+
const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
|
|
5229
|
+
const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
|
|
5115
5230
|
if (input.status === "in_progress") {
|
|
5116
5231
|
mkdirSync5(cacheDir, { recursive: true });
|
|
5117
|
-
|
|
5118
|
-
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
5232
|
+
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
5233
|
+
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
5119
5234
|
try {
|
|
5120
5235
|
unlinkSync5(cachePath);
|
|
5121
5236
|
} catch {
|
|
@@ -5123,10 +5238,10 @@ async function updateTask(input) {
|
|
|
5123
5238
|
}
|
|
5124
5239
|
} catch {
|
|
5125
5240
|
}
|
|
5126
|
-
if (input.status === "done") {
|
|
5241
|
+
if (input.status === "done" || input.status === "closed") {
|
|
5127
5242
|
await cleanupReviewFile(row, taskFile, input.baseDir);
|
|
5128
5243
|
}
|
|
5129
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
5244
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
5130
5245
|
try {
|
|
5131
5246
|
const client = getClient();
|
|
5132
5247
|
const taskTitle = String(row.title);
|
|
@@ -5142,7 +5257,7 @@ async function updateTask(input) {
|
|
|
5142
5257
|
if (!isCoordinatorName(assignedAgent)) {
|
|
5143
5258
|
try {
|
|
5144
5259
|
const draftClient = getClient();
|
|
5145
|
-
if (input.status === "done") {
|
|
5260
|
+
if (input.status === "done" || input.status === "closed") {
|
|
5146
5261
|
await draftClient.execute({
|
|
5147
5262
|
sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
|
|
5148
5263
|
args: [assignedAgent]
|
|
@@ -5159,7 +5274,7 @@ async function updateTask(input) {
|
|
|
5159
5274
|
try {
|
|
5160
5275
|
const client = getClient();
|
|
5161
5276
|
const cascaded = await client.execute({
|
|
5162
|
-
sql: `UPDATE tasks SET status = '
|
|
5277
|
+
sql: `UPDATE tasks SET status = 'closed', updated_at = ?
|
|
5163
5278
|
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
5164
5279
|
args: [now, taskId]
|
|
5165
5280
|
});
|
|
@@ -5172,14 +5287,14 @@ async function updateTask(input) {
|
|
|
5172
5287
|
} catch {
|
|
5173
5288
|
}
|
|
5174
5289
|
}
|
|
5175
|
-
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
5290
|
+
const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
|
|
5176
5291
|
if (isTerminal) {
|
|
5177
5292
|
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
5178
5293
|
if (!isCoordinator) {
|
|
5179
5294
|
notifyTaskDone();
|
|
5180
5295
|
}
|
|
5181
5296
|
await markTaskNotificationsRead(taskFile);
|
|
5182
|
-
if (input.status === "done") {
|
|
5297
|
+
if (input.status === "done" || input.status === "closed") {
|
|
5183
5298
|
try {
|
|
5184
5299
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
5185
5300
|
} catch {
|
|
@@ -5199,7 +5314,7 @@ async function updateTask(input) {
|
|
|
5199
5314
|
}
|
|
5200
5315
|
}
|
|
5201
5316
|
}
|
|
5202
|
-
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
5317
|
+
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
5203
5318
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
5204
5319
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
5205
5320
|
taskId,
|
|
@@ -5571,6 +5686,7 @@ __export(tmux_routing_exports, {
|
|
|
5571
5686
|
isEmployeeAlive: () => isEmployeeAlive,
|
|
5572
5687
|
isExeSession: () => isExeSession,
|
|
5573
5688
|
isSessionBusy: () => isSessionBusy,
|
|
5689
|
+
notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
|
|
5574
5690
|
notifyParentExe: () => notifyParentExe,
|
|
5575
5691
|
parseParentExe: () => parseParentExe,
|
|
5576
5692
|
registerParentExe: () => registerParentExe,
|
|
@@ -5581,13 +5697,13 @@ __export(tmux_routing_exports, {
|
|
|
5581
5697
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
5582
5698
|
});
|
|
5583
5699
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
5584
|
-
import { readFileSync as
|
|
5585
|
-
import
|
|
5586
|
-
import
|
|
5700
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync3 } from "fs";
|
|
5701
|
+
import path18 from "path";
|
|
5702
|
+
import os11 from "os";
|
|
5587
5703
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5588
5704
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
5589
5705
|
function spawnLockPath(sessionName) {
|
|
5590
|
-
return
|
|
5706
|
+
return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
5591
5707
|
}
|
|
5592
5708
|
function isProcessAlive(pid) {
|
|
5593
5709
|
try {
|
|
@@ -5598,13 +5714,13 @@ function isProcessAlive(pid) {
|
|
|
5598
5714
|
}
|
|
5599
5715
|
}
|
|
5600
5716
|
function acquireSpawnLock2(sessionName) {
|
|
5601
|
-
if (!
|
|
5717
|
+
if (!existsSync14(SPAWN_LOCK_DIR)) {
|
|
5602
5718
|
mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
|
|
5603
5719
|
}
|
|
5604
5720
|
const lockFile = spawnLockPath(sessionName);
|
|
5605
|
-
if (
|
|
5721
|
+
if (existsSync14(lockFile)) {
|
|
5606
5722
|
try {
|
|
5607
|
-
const lock = JSON.parse(
|
|
5723
|
+
const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
|
|
5608
5724
|
const age = Date.now() - lock.timestamp;
|
|
5609
5725
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
5610
5726
|
return false;
|
|
@@ -5612,7 +5728,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
5612
5728
|
} catch {
|
|
5613
5729
|
}
|
|
5614
5730
|
}
|
|
5615
|
-
|
|
5731
|
+
writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
5616
5732
|
return true;
|
|
5617
5733
|
}
|
|
5618
5734
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -5624,13 +5740,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
5624
5740
|
function resolveBehaviorsExporterScript() {
|
|
5625
5741
|
try {
|
|
5626
5742
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5627
|
-
const scriptPath =
|
|
5628
|
-
|
|
5743
|
+
const scriptPath = path18.join(
|
|
5744
|
+
path18.dirname(thisFile),
|
|
5629
5745
|
"..",
|
|
5630
5746
|
"bin",
|
|
5631
5747
|
"exe-export-behaviors.js"
|
|
5632
5748
|
);
|
|
5633
|
-
return
|
|
5749
|
+
return existsSync14(scriptPath) ? scriptPath : null;
|
|
5634
5750
|
} catch {
|
|
5635
5751
|
return null;
|
|
5636
5752
|
}
|
|
@@ -5696,12 +5812,12 @@ function extractRootExe(name) {
|
|
|
5696
5812
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
5697
5813
|
}
|
|
5698
5814
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
5699
|
-
if (!
|
|
5815
|
+
if (!existsSync14(SESSION_CACHE)) {
|
|
5700
5816
|
mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
5701
5817
|
}
|
|
5702
5818
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
5703
|
-
const filePath =
|
|
5704
|
-
|
|
5819
|
+
const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
5820
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
5705
5821
|
parentExe: rootExe,
|
|
5706
5822
|
dispatchedBy: dispatchedBy || rootExe,
|
|
5707
5823
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5709,7 +5825,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5709
5825
|
}
|
|
5710
5826
|
function getParentExe(sessionKey) {
|
|
5711
5827
|
try {
|
|
5712
|
-
const data = JSON.parse(
|
|
5828
|
+
const data = JSON.parse(readFileSync12(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
5713
5829
|
return data.parentExe || null;
|
|
5714
5830
|
} catch {
|
|
5715
5831
|
return null;
|
|
@@ -5717,8 +5833,8 @@ function getParentExe(sessionKey) {
|
|
|
5717
5833
|
}
|
|
5718
5834
|
function getDispatchedBy(sessionKey) {
|
|
5719
5835
|
try {
|
|
5720
|
-
const data = JSON.parse(
|
|
5721
|
-
|
|
5836
|
+
const data = JSON.parse(readFileSync12(
|
|
5837
|
+
path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
5722
5838
|
"utf8"
|
|
5723
5839
|
));
|
|
5724
5840
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -5788,8 +5904,8 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
5788
5904
|
}
|
|
5789
5905
|
function readDebounceState() {
|
|
5790
5906
|
try {
|
|
5791
|
-
if (!
|
|
5792
|
-
const raw = JSON.parse(
|
|
5907
|
+
if (!existsSync14(DEBOUNCE_FILE)) return {};
|
|
5908
|
+
const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
|
|
5793
5909
|
const state = {};
|
|
5794
5910
|
for (const [key, val] of Object.entries(raw)) {
|
|
5795
5911
|
if (typeof val === "number") {
|
|
@@ -5805,8 +5921,8 @@ function readDebounceState() {
|
|
|
5805
5921
|
}
|
|
5806
5922
|
function writeDebounceState(state) {
|
|
5807
5923
|
try {
|
|
5808
|
-
if (!
|
|
5809
|
-
|
|
5924
|
+
if (!existsSync14(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
|
|
5925
|
+
writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
|
|
5810
5926
|
} catch {
|
|
5811
5927
|
}
|
|
5812
5928
|
}
|
|
@@ -5904,8 +6020,8 @@ function sendIntercom(targetSession) {
|
|
|
5904
6020
|
try {
|
|
5905
6021
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
5906
6022
|
const agent = baseAgentName(rawAgent);
|
|
5907
|
-
const markerPath =
|
|
5908
|
-
if (
|
|
6023
|
+
const markerPath = path18.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
6024
|
+
if (existsSync14(markerPath)) {
|
|
5909
6025
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
5910
6026
|
return "debounced";
|
|
5911
6027
|
}
|
|
@@ -5914,8 +6030,8 @@ function sendIntercom(targetSession) {
|
|
|
5914
6030
|
try {
|
|
5915
6031
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
5916
6032
|
const agent = baseAgentName(rawAgent);
|
|
5917
|
-
const taskDir =
|
|
5918
|
-
if (
|
|
6033
|
+
const taskDir = path18.join(process.cwd(), "exe", agent);
|
|
6034
|
+
if (existsSync14(taskDir)) {
|
|
5919
6035
|
const files = readdirSync3(taskDir).filter(
|
|
5920
6036
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
5921
6037
|
);
|
|
@@ -5975,6 +6091,21 @@ function notifyParentExe(sessionKey) {
|
|
|
5975
6091
|
}
|
|
5976
6092
|
return true;
|
|
5977
6093
|
}
|
|
6094
|
+
function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
|
|
6095
|
+
const transport = getTransport();
|
|
6096
|
+
try {
|
|
6097
|
+
const sessions = transport.listSessions();
|
|
6098
|
+
if (!sessions.includes(coordinatorSession)) return false;
|
|
6099
|
+
execSync7(
|
|
6100
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
6101
|
+
{ timeout: 3e3 }
|
|
6102
|
+
);
|
|
6103
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
|
|
6104
|
+
return true;
|
|
6105
|
+
} catch {
|
|
6106
|
+
return false;
|
|
6107
|
+
}
|
|
6108
|
+
}
|
|
5978
6109
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
5979
6110
|
if (isCoordinatorName(employeeName)) {
|
|
5980
6111
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
@@ -6048,26 +6179,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6048
6179
|
const transport = getTransport();
|
|
6049
6180
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
6050
6181
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
6051
|
-
const logDir =
|
|
6052
|
-
const logFile =
|
|
6053
|
-
if (!
|
|
6182
|
+
const logDir = path18.join(os11.homedir(), ".exe-os", "session-logs");
|
|
6183
|
+
const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
6184
|
+
if (!existsSync14(logDir)) {
|
|
6054
6185
|
mkdirSync6(logDir, { recursive: true });
|
|
6055
6186
|
}
|
|
6056
6187
|
transport.kill(sessionName);
|
|
6057
6188
|
let cleanupSuffix = "";
|
|
6058
6189
|
try {
|
|
6059
6190
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
6060
|
-
const cleanupScript =
|
|
6061
|
-
if (
|
|
6191
|
+
const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
6192
|
+
if (existsSync14(cleanupScript)) {
|
|
6062
6193
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
6063
6194
|
}
|
|
6064
6195
|
} catch {
|
|
6065
6196
|
}
|
|
6066
6197
|
try {
|
|
6067
|
-
const claudeJsonPath =
|
|
6198
|
+
const claudeJsonPath = path18.join(os11.homedir(), ".claude.json");
|
|
6068
6199
|
let claudeJson = {};
|
|
6069
6200
|
try {
|
|
6070
|
-
claudeJson = JSON.parse(
|
|
6201
|
+
claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
|
|
6071
6202
|
} catch {
|
|
6072
6203
|
}
|
|
6073
6204
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -6075,17 +6206,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6075
6206
|
const trustDir = opts?.cwd ?? projectDir;
|
|
6076
6207
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
6077
6208
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
6078
|
-
|
|
6209
|
+
writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
6079
6210
|
} catch {
|
|
6080
6211
|
}
|
|
6081
6212
|
try {
|
|
6082
|
-
const settingsDir =
|
|
6213
|
+
const settingsDir = path18.join(os11.homedir(), ".claude", "projects");
|
|
6083
6214
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
6084
|
-
const projSettingsDir =
|
|
6085
|
-
const settingsPath =
|
|
6215
|
+
const projSettingsDir = path18.join(settingsDir, normalizedKey);
|
|
6216
|
+
const settingsPath = path18.join(projSettingsDir, "settings.json");
|
|
6086
6217
|
let settings = {};
|
|
6087
6218
|
try {
|
|
6088
|
-
settings = JSON.parse(
|
|
6219
|
+
settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
|
|
6089
6220
|
} catch {
|
|
6090
6221
|
}
|
|
6091
6222
|
const perms = settings.permissions ?? {};
|
|
@@ -6114,7 +6245,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6114
6245
|
perms.allow = allow;
|
|
6115
6246
|
settings.permissions = perms;
|
|
6116
6247
|
mkdirSync6(projSettingsDir, { recursive: true });
|
|
6117
|
-
|
|
6248
|
+
writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
6118
6249
|
}
|
|
6119
6250
|
} catch {
|
|
6120
6251
|
}
|
|
@@ -6129,8 +6260,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6129
6260
|
let behaviorsFlag = "";
|
|
6130
6261
|
let legacyFallbackWarned = false;
|
|
6131
6262
|
if (!useExeAgent && !useBinSymlink) {
|
|
6132
|
-
const identityPath =
|
|
6133
|
-
|
|
6263
|
+
const identityPath = path18.join(
|
|
6264
|
+
os11.homedir(),
|
|
6134
6265
|
".exe-os",
|
|
6135
6266
|
"identity",
|
|
6136
6267
|
`${employeeName}.md`
|
|
@@ -6139,13 +6270,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6139
6270
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
6140
6271
|
if (hasAgentFlag) {
|
|
6141
6272
|
identityFlag = ` --agent ${employeeName}`;
|
|
6142
|
-
} else if (
|
|
6273
|
+
} else if (existsSync14(identityPath)) {
|
|
6143
6274
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
6144
6275
|
legacyFallbackWarned = true;
|
|
6145
6276
|
}
|
|
6146
6277
|
const behaviorsFile = exportBehaviorsSync(
|
|
6147
6278
|
employeeName,
|
|
6148
|
-
|
|
6279
|
+
path18.basename(spawnCwd),
|
|
6149
6280
|
sessionName
|
|
6150
6281
|
);
|
|
6151
6282
|
if (behaviorsFile) {
|
|
@@ -6160,16 +6291,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6160
6291
|
}
|
|
6161
6292
|
let sessionContextFlag = "";
|
|
6162
6293
|
try {
|
|
6163
|
-
const ctxDir =
|
|
6294
|
+
const ctxDir = path18.join(os11.homedir(), ".exe-os", "session-cache");
|
|
6164
6295
|
mkdirSync6(ctxDir, { recursive: true });
|
|
6165
|
-
const ctxFile =
|
|
6296
|
+
const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
|
|
6166
6297
|
const ctxContent = [
|
|
6167
6298
|
`## Session Context`,
|
|
6168
6299
|
`You are running in tmux session: ${sessionName}.`,
|
|
6169
6300
|
`Your parent coordinator session is ${exeSession}.`,
|
|
6170
6301
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
6171
6302
|
].join("\n");
|
|
6172
|
-
|
|
6303
|
+
writeFileSync8(ctxFile, ctxContent);
|
|
6173
6304
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
6174
6305
|
} catch {
|
|
6175
6306
|
}
|
|
@@ -6246,8 +6377,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6246
6377
|
transport.pipeLog(sessionName, logFile);
|
|
6247
6378
|
try {
|
|
6248
6379
|
const mySession = getMySession();
|
|
6249
|
-
const dispatchInfo =
|
|
6250
|
-
|
|
6380
|
+
const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
6381
|
+
writeFileSync8(dispatchInfo, JSON.stringify({
|
|
6251
6382
|
dispatchedBy: mySession,
|
|
6252
6383
|
rootExe: exeSession,
|
|
6253
6384
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -6321,15 +6452,15 @@ var init_tmux_routing = __esm({
|
|
|
6321
6452
|
init_intercom_queue();
|
|
6322
6453
|
init_plan_limits();
|
|
6323
6454
|
init_employees();
|
|
6324
|
-
SPAWN_LOCK_DIR =
|
|
6325
|
-
SESSION_CACHE =
|
|
6455
|
+
SPAWN_LOCK_DIR = path18.join(os11.homedir(), ".exe-os", "spawn-locks");
|
|
6456
|
+
SESSION_CACHE = path18.join(os11.homedir(), ".exe-os", "session-cache");
|
|
6326
6457
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
6327
6458
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
6328
6459
|
VERIFY_PANE_LINES = 200;
|
|
6329
6460
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
6330
6461
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
6331
|
-
INTERCOM_LOG2 =
|
|
6332
|
-
DEBOUNCE_FILE =
|
|
6462
|
+
INTERCOM_LOG2 = path18.join(os11.homedir(), ".exe-os", "intercom.log");
|
|
6463
|
+
DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
|
|
6333
6464
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
6334
6465
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
6335
6466
|
}
|
|
@@ -6346,14 +6477,14 @@ var init_memory = __esm({
|
|
|
6346
6477
|
|
|
6347
6478
|
// src/lib/keychain.ts
|
|
6348
6479
|
import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
|
|
6349
|
-
import { existsSync as
|
|
6350
|
-
import
|
|
6351
|
-
import
|
|
6480
|
+
import { existsSync as existsSync15 } from "fs";
|
|
6481
|
+
import path19 from "path";
|
|
6482
|
+
import os12 from "os";
|
|
6352
6483
|
function getKeyDir() {
|
|
6353
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
6484
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path19.join(os12.homedir(), ".exe-os");
|
|
6354
6485
|
}
|
|
6355
6486
|
function getKeyPath() {
|
|
6356
|
-
return
|
|
6487
|
+
return path19.join(getKeyDir(), "master.key");
|
|
6357
6488
|
}
|
|
6358
6489
|
async function tryKeytar() {
|
|
6359
6490
|
try {
|
|
@@ -6374,9 +6505,9 @@ async function getMasterKey() {
|
|
|
6374
6505
|
}
|
|
6375
6506
|
}
|
|
6376
6507
|
const keyPath = getKeyPath();
|
|
6377
|
-
if (!
|
|
6508
|
+
if (!existsSync15(keyPath)) {
|
|
6378
6509
|
process.stderr.write(
|
|
6379
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
6510
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os12.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
6380
6511
|
`
|
|
6381
6512
|
);
|
|
6382
6513
|
return null;
|
|
@@ -6406,6 +6537,7 @@ var shard_manager_exports = {};
|
|
|
6406
6537
|
__export(shard_manager_exports, {
|
|
6407
6538
|
disposeShards: () => disposeShards,
|
|
6408
6539
|
ensureShardSchema: () => ensureShardSchema,
|
|
6540
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
6409
6541
|
getReadyShardClient: () => getReadyShardClient,
|
|
6410
6542
|
getShardClient: () => getShardClient,
|
|
6411
6543
|
getShardsDir: () => getShardsDir,
|
|
@@ -6414,15 +6546,18 @@ __export(shard_manager_exports, {
|
|
|
6414
6546
|
listShards: () => listShards,
|
|
6415
6547
|
shardExists: () => shardExists
|
|
6416
6548
|
});
|
|
6417
|
-
import
|
|
6418
|
-
import { existsSync as
|
|
6549
|
+
import path20 from "path";
|
|
6550
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
|
|
6419
6551
|
import { createClient as createClient2 } from "@libsql/client";
|
|
6420
6552
|
function initShardManager(encryptionKey) {
|
|
6421
6553
|
_encryptionKey = encryptionKey;
|
|
6422
|
-
if (!
|
|
6554
|
+
if (!existsSync16(SHARDS_DIR)) {
|
|
6423
6555
|
mkdirSync7(SHARDS_DIR, { recursive: true });
|
|
6424
6556
|
}
|
|
6425
6557
|
_shardingEnabled = true;
|
|
6558
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
6559
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
6560
|
+
_evictionTimer.unref();
|
|
6426
6561
|
}
|
|
6427
6562
|
function isShardingEnabled() {
|
|
6428
6563
|
return _shardingEnabled;
|
|
@@ -6439,21 +6574,28 @@ function getShardClient(projectName) {
|
|
|
6439
6574
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
6440
6575
|
}
|
|
6441
6576
|
const cached = _shards.get(safeName);
|
|
6442
|
-
if (cached)
|
|
6443
|
-
|
|
6577
|
+
if (cached) {
|
|
6578
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
6579
|
+
return cached;
|
|
6580
|
+
}
|
|
6581
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
6582
|
+
evictLRU();
|
|
6583
|
+
}
|
|
6584
|
+
const dbPath = path20.join(SHARDS_DIR, `${safeName}.db`);
|
|
6444
6585
|
const client = createClient2({
|
|
6445
6586
|
url: `file:${dbPath}`,
|
|
6446
6587
|
encryptionKey: _encryptionKey
|
|
6447
6588
|
});
|
|
6448
6589
|
_shards.set(safeName, client);
|
|
6590
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
6449
6591
|
return client;
|
|
6450
6592
|
}
|
|
6451
6593
|
function shardExists(projectName) {
|
|
6452
6594
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
6453
|
-
return
|
|
6595
|
+
return existsSync16(path20.join(SHARDS_DIR, `${safeName}.db`));
|
|
6454
6596
|
}
|
|
6455
6597
|
function listShards() {
|
|
6456
|
-
if (!
|
|
6598
|
+
if (!existsSync16(SHARDS_DIR)) return [];
|
|
6457
6599
|
return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
6458
6600
|
}
|
|
6459
6601
|
async function ensureShardSchema(client) {
|
|
@@ -6505,6 +6647,8 @@ async function ensureShardSchema(client) {
|
|
|
6505
6647
|
for (const col of [
|
|
6506
6648
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
6507
6649
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
6650
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
6651
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
6508
6652
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
6509
6653
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
6510
6654
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -6642,21 +6786,69 @@ async function getReadyShardClient(projectName) {
|
|
|
6642
6786
|
await ensureShardSchema(client);
|
|
6643
6787
|
return client;
|
|
6644
6788
|
}
|
|
6789
|
+
function evictLRU() {
|
|
6790
|
+
let oldest = null;
|
|
6791
|
+
let oldestTime = Infinity;
|
|
6792
|
+
for (const [name, time] of _shardLastAccess) {
|
|
6793
|
+
if (time < oldestTime) {
|
|
6794
|
+
oldestTime = time;
|
|
6795
|
+
oldest = name;
|
|
6796
|
+
}
|
|
6797
|
+
}
|
|
6798
|
+
if (oldest) {
|
|
6799
|
+
const client = _shards.get(oldest);
|
|
6800
|
+
if (client) {
|
|
6801
|
+
client.close();
|
|
6802
|
+
}
|
|
6803
|
+
_shards.delete(oldest);
|
|
6804
|
+
_shardLastAccess.delete(oldest);
|
|
6805
|
+
}
|
|
6806
|
+
}
|
|
6807
|
+
function evictIdleShards() {
|
|
6808
|
+
const now = Date.now();
|
|
6809
|
+
const toEvict = [];
|
|
6810
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
6811
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
6812
|
+
toEvict.push(name);
|
|
6813
|
+
}
|
|
6814
|
+
}
|
|
6815
|
+
for (const name of toEvict) {
|
|
6816
|
+
const client = _shards.get(name);
|
|
6817
|
+
if (client) {
|
|
6818
|
+
client.close();
|
|
6819
|
+
}
|
|
6820
|
+
_shards.delete(name);
|
|
6821
|
+
_shardLastAccess.delete(name);
|
|
6822
|
+
}
|
|
6823
|
+
}
|
|
6824
|
+
function getOpenShardCount() {
|
|
6825
|
+
return _shards.size;
|
|
6826
|
+
}
|
|
6645
6827
|
function disposeShards() {
|
|
6828
|
+
if (_evictionTimer) {
|
|
6829
|
+
clearInterval(_evictionTimer);
|
|
6830
|
+
_evictionTimer = null;
|
|
6831
|
+
}
|
|
6646
6832
|
for (const [, client] of _shards) {
|
|
6647
6833
|
client.close();
|
|
6648
6834
|
}
|
|
6649
6835
|
_shards.clear();
|
|
6836
|
+
_shardLastAccess.clear();
|
|
6650
6837
|
_shardingEnabled = false;
|
|
6651
6838
|
_encryptionKey = null;
|
|
6652
6839
|
}
|
|
6653
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
6840
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
6654
6841
|
var init_shard_manager = __esm({
|
|
6655
6842
|
"src/lib/shard-manager.ts"() {
|
|
6656
6843
|
"use strict";
|
|
6657
6844
|
init_config();
|
|
6658
|
-
SHARDS_DIR =
|
|
6845
|
+
SHARDS_DIR = path20.join(EXE_AI_DIR, "shards");
|
|
6846
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
6847
|
+
MAX_OPEN_SHARDS = 10;
|
|
6848
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
6659
6849
|
_shards = /* @__PURE__ */ new Map();
|
|
6850
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
6851
|
+
_evictionTimer = null;
|
|
6660
6852
|
_encryptionKey = null;
|
|
6661
6853
|
_shardingEnabled = false;
|
|
6662
6854
|
}
|
|
@@ -7452,8 +7644,8 @@ function findContainingChunk(filePath, snippet) {
|
|
|
7452
7644
|
try {
|
|
7453
7645
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
7454
7646
|
if (ext !== "ts" && ext !== "tsx" && ext !== "js" && ext !== "jsx") return "";
|
|
7455
|
-
const { readFileSync:
|
|
7456
|
-
const source =
|
|
7647
|
+
const { readFileSync: readFileSync15 } = __require("fs");
|
|
7648
|
+
const source = readFileSync15(filePath, "utf8");
|
|
7457
7649
|
const lines = source.split("\n");
|
|
7458
7650
|
const lowerSnippet = snippet.toLowerCase().slice(0, 80);
|
|
7459
7651
|
let matchLine = -1;
|
|
@@ -7519,9 +7711,9 @@ function extractBash(input, response) {
|
|
|
7519
7711
|
}
|
|
7520
7712
|
function extractGrep(input, response) {
|
|
7521
7713
|
const pattern = String(input.pattern ?? "");
|
|
7522
|
-
const
|
|
7714
|
+
const path23 = input.path ? String(input.path) : "";
|
|
7523
7715
|
const output = String(response.text ?? response.content ?? JSON.stringify(response).slice(0, MAX_OUTPUT));
|
|
7524
|
-
return `Searched for "${pattern}"${
|
|
7716
|
+
return `Searched for "${pattern}"${path23 ? ` in ${path23}` : ""}
|
|
7525
7717
|
${output.slice(0, MAX_OUTPUT)}`;
|
|
7526
7718
|
}
|
|
7527
7719
|
function extractGlob(input, response) {
|
|
@@ -7624,7 +7816,7 @@ __export(error_detector_exports, {
|
|
|
7624
7816
|
errorFingerprint: () => errorFingerprint,
|
|
7625
7817
|
isExeOsError: () => isExeOsError
|
|
7626
7818
|
});
|
|
7627
|
-
import
|
|
7819
|
+
import crypto7 from "crypto";
|
|
7628
7820
|
function isRealStderr(stderr) {
|
|
7629
7821
|
const lines = stderr.trim().split("\n");
|
|
7630
7822
|
const meaningful = lines.filter(
|
|
@@ -7695,7 +7887,7 @@ function classifyError(errorText) {
|
|
|
7695
7887
|
}
|
|
7696
7888
|
function errorFingerprint(toolName, errorText) {
|
|
7697
7889
|
const normalized = errorText.replace(/\d{4}-\d{2}-\d{2}T[\d:.]+Z/g, "TIMESTAMP").replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "UUID").replace(/\/Users\/[^\s]+/g, "PATH").replace(/:\d+:\d+/g, ":LINE:COL").slice(0, 200);
|
|
7698
|
-
return
|
|
7890
|
+
return crypto7.createHash("sha256").update(`${toolName}:${normalized}`).digest("hex").slice(0, 16);
|
|
7699
7891
|
}
|
|
7700
7892
|
var ERROR_PATTERNS, FILE_CONTENT_TOOLS, STDERR_IGNORE_PATTERNS, USER_ERROR_PATTERNS, SYSTEM_BUG_PATTERNS;
|
|
7701
7893
|
var init_error_detector = __esm({
|
|
@@ -8059,10 +8251,10 @@ async function disposeEmbedder() {
|
|
|
8059
8251
|
async function embedDirect(text) {
|
|
8060
8252
|
const llamaCpp = await import("node-llama-cpp");
|
|
8061
8253
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
8062
|
-
const { existsSync:
|
|
8063
|
-
const
|
|
8064
|
-
const modelPath =
|
|
8065
|
-
if (!
|
|
8254
|
+
const { existsSync: existsSync18 } = await import("fs");
|
|
8255
|
+
const path23 = await import("path");
|
|
8256
|
+
const modelPath = path23.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
8257
|
+
if (!existsSync18(modelPath)) {
|
|
8066
8258
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
8067
8259
|
}
|
|
8068
8260
|
const llama = await llamaCpp.getLlama();
|
|
@@ -8099,8 +8291,8 @@ __export(wiki_client_exports, {
|
|
|
8099
8291
|
listDocuments: () => listDocuments,
|
|
8100
8292
|
listWorkspaces: () => listWorkspaces
|
|
8101
8293
|
});
|
|
8102
|
-
async function wikiFetch(config2,
|
|
8103
|
-
const url = `${config2.baseUrl}/api/v1${
|
|
8294
|
+
async function wikiFetch(config2, path23, method = "GET", body) {
|
|
8295
|
+
const url = `${config2.baseUrl}/api/v1${path23}`;
|
|
8104
8296
|
const headers = {
|
|
8105
8297
|
Authorization: `Bearer ${config2.apiKey}`,
|
|
8106
8298
|
"Content-Type": "application/json"
|
|
@@ -8133,7 +8325,7 @@ async function wikiFetch(config2, path22, method = "GET", body) {
|
|
|
8133
8325
|
}
|
|
8134
8326
|
}
|
|
8135
8327
|
if (!response.ok) {
|
|
8136
|
-
throw new Error(`Wiki API ${method} ${
|
|
8328
|
+
throw new Error(`Wiki API ${method} ${path23}: ${response.status} ${response.statusText}`);
|
|
8137
8329
|
}
|
|
8138
8330
|
return response.json();
|
|
8139
8331
|
} finally {
|
|
@@ -8426,13 +8618,13 @@ __export(graph_rag_exports, {
|
|
|
8426
8618
|
resolveAlias: () => resolveAlias,
|
|
8427
8619
|
storeExtraction: () => storeExtraction
|
|
8428
8620
|
});
|
|
8429
|
-
import
|
|
8621
|
+
import crypto8 from "crypto";
|
|
8430
8622
|
function normalizeEntityName(name) {
|
|
8431
8623
|
return name.replace(/\s*\([^)]*\)\s*/g, "").trim().toLowerCase();
|
|
8432
8624
|
}
|
|
8433
8625
|
function entityId(name, type) {
|
|
8434
8626
|
const normalized = normalizeEntityName(name);
|
|
8435
|
-
return
|
|
8627
|
+
return crypto8.createHash("sha256").update(`${normalized}::${type.toLowerCase()}`).digest("hex").slice(0, 16);
|
|
8436
8628
|
}
|
|
8437
8629
|
async function resolveAlias(client, name) {
|
|
8438
8630
|
const normalized = normalizeEntityName(name);
|
|
@@ -8682,7 +8874,7 @@ async function storeExtraction(client, extraction, memoryId, timestamp) {
|
|
|
8682
8874
|
const targetAlias = await resolveAlias(client, r.target);
|
|
8683
8875
|
const sourceId = sourceAlias ?? entityId(r.source, r.sourceType);
|
|
8684
8876
|
const targetId = targetAlias ?? entityId(r.target, r.targetType);
|
|
8685
|
-
const relId =
|
|
8877
|
+
const relId = crypto8.randomUUID().slice(0, 16);
|
|
8686
8878
|
try {
|
|
8687
8879
|
await client.execute({
|
|
8688
8880
|
sql: `INSERT OR IGNORE INTO entities (id, name, type, first_seen, last_seen)
|
|
@@ -8745,7 +8937,7 @@ async function storeExtraction(client, extraction, memoryId, timestamp) {
|
|
|
8745
8937
|
}
|
|
8746
8938
|
}
|
|
8747
8939
|
for (const h of extraction.hyperedges) {
|
|
8748
|
-
const hId =
|
|
8940
|
+
const hId = crypto8.randomUUID().slice(0, 16);
|
|
8749
8941
|
try {
|
|
8750
8942
|
await client.execute({
|
|
8751
8943
|
sql: `INSERT OR IGNORE INTO hyperedges (id, label, relation, confidence, timestamp)
|
|
@@ -8809,7 +9001,7 @@ async function extractBatch(client, batchSize = 50, model = "claude-haiku-4-5-20
|
|
|
8809
9001
|
totalEntities += stored.entitiesStored;
|
|
8810
9002
|
totalRelationships += stored.relationshipsStored;
|
|
8811
9003
|
}
|
|
8812
|
-
const contentHash =
|
|
9004
|
+
const contentHash = crypto8.createHash("sha256").update(rawContent).digest("hex").slice(0, 32);
|
|
8813
9005
|
await client.execute({
|
|
8814
9006
|
sql: "UPDATE memories SET graph_extracted = 1, content_hash = ?, graph_extracted_hash = ? WHERE id = ?",
|
|
8815
9007
|
args: [contentHash, contentHash, memoryId]
|
|
@@ -9154,13 +9346,13 @@ __export(whatsapp_accounts_exports, {
|
|
|
9154
9346
|
getDefaultAccount: () => getDefaultAccount,
|
|
9155
9347
|
loadAccounts: () => loadAccounts
|
|
9156
9348
|
});
|
|
9157
|
-
import { readFileSync as
|
|
9349
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
9158
9350
|
import { join as join2 } from "path";
|
|
9159
9351
|
import { homedir as homedir2 } from "os";
|
|
9160
9352
|
function loadAccounts() {
|
|
9161
9353
|
if (cachedAccounts !== null) return cachedAccounts;
|
|
9162
9354
|
try {
|
|
9163
|
-
const raw =
|
|
9355
|
+
const raw = readFileSync13(CONFIG_PATH2, "utf8");
|
|
9164
9356
|
const parsed = JSON.parse(raw);
|
|
9165
9357
|
if (!Array.isArray(parsed)) {
|
|
9166
9358
|
console.warn("[whatsapp] Config is not an array, ignoring");
|
|
@@ -9216,10 +9408,10 @@ __export(messaging_exports, {
|
|
|
9216
9408
|
sendMessage: () => sendMessage,
|
|
9217
9409
|
setWsClientSend: () => setWsClientSend
|
|
9218
9410
|
});
|
|
9219
|
-
import
|
|
9411
|
+
import crypto10 from "crypto";
|
|
9220
9412
|
function generateUlid() {
|
|
9221
9413
|
const timestamp = Date.now().toString(36).padStart(10, "0");
|
|
9222
|
-
const random =
|
|
9414
|
+
const random = crypto10.randomBytes(10).toString("hex").slice(0, 16);
|
|
9223
9415
|
return (timestamp + random).toUpperCase();
|
|
9224
9416
|
}
|
|
9225
9417
|
function rowToMessage(row) {
|
|
@@ -9230,6 +9422,7 @@ function rowToMessage(row) {
|
|
|
9230
9422
|
targetAgent: row.target_agent,
|
|
9231
9423
|
targetProject: row.target_project ?? null,
|
|
9232
9424
|
targetDevice: row.target_device,
|
|
9425
|
+
sessionScope: row.session_scope ?? null,
|
|
9233
9426
|
content: row.content,
|
|
9234
9427
|
priority: row.priority ?? "normal",
|
|
9235
9428
|
status: row.status ?? "pending",
|
|
@@ -9247,15 +9440,17 @@ async function sendMessage(input) {
|
|
|
9247
9440
|
const id = generateUlid();
|
|
9248
9441
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9249
9442
|
const targetDevice = input.targetDevice ?? "local";
|
|
9443
|
+
const sessionScope = input.sessionScope === void 0 ? resolveExeSession() : input.sessionScope;
|
|
9250
9444
|
await client.execute({
|
|
9251
|
-
sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, content, priority, status, created_at)
|
|
9252
|
-
VALUES (?, ?, 'local', ?, ?, ?, ?, ?, 'pending', ?)`,
|
|
9445
|
+
sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, session_scope, content, priority, status, created_at)
|
|
9446
|
+
VALUES (?, ?, 'local', ?, ?, ?, ?, ?, ?, 'pending', ?)`,
|
|
9253
9447
|
args: [
|
|
9254
9448
|
id,
|
|
9255
9449
|
input.fromAgent,
|
|
9256
9450
|
input.targetAgent,
|
|
9257
9451
|
input.targetProject ?? null,
|
|
9258
9452
|
targetDevice,
|
|
9453
|
+
sessionScope,
|
|
9259
9454
|
input.content,
|
|
9260
9455
|
input.priority ?? "normal",
|
|
9261
9456
|
now
|
|
@@ -9269,9 +9464,10 @@ async function sendMessage(input) {
|
|
|
9269
9464
|
}
|
|
9270
9465
|
} catch {
|
|
9271
9466
|
}
|
|
9467
|
+
const sentScope = strictSessionScopeFilter(sessionScope);
|
|
9272
9468
|
const result = await client.execute({
|
|
9273
|
-
sql:
|
|
9274
|
-
args: [id]
|
|
9469
|
+
sql: `SELECT * FROM messages WHERE id = ?${sentScope.sql}`,
|
|
9470
|
+
args: [id, ...sentScope.args]
|
|
9275
9471
|
});
|
|
9276
9472
|
return rowToMessage(result.rows[0]);
|
|
9277
9473
|
}
|
|
@@ -9295,6 +9491,7 @@ async function deliverCrossMachineMessage(messageId, targetDevice) {
|
|
|
9295
9491
|
fromAgent: msg.fromAgent,
|
|
9296
9492
|
targetAgent: msg.targetAgent,
|
|
9297
9493
|
targetProject: msg.targetProject,
|
|
9494
|
+
sessionScope: msg.sessionScope,
|
|
9298
9495
|
content: msg.content,
|
|
9299
9496
|
priority: msg.priority,
|
|
9300
9497
|
createdAt: msg.createdAt
|
|
@@ -9338,7 +9535,7 @@ async function deliverLocalMessage(messageId) {
|
|
|
9338
9535
|
} catch {
|
|
9339
9536
|
const newRetryCount = msg.retryCount + 1;
|
|
9340
9537
|
if (newRetryCount >= MAX_RETRIES3) {
|
|
9341
|
-
await markFailed(messageId, "session unavailable after 10 retries");
|
|
9538
|
+
await markFailed(messageId, "session unavailable after 10 retries", msg.sessionScope);
|
|
9342
9539
|
} else {
|
|
9343
9540
|
await client.execute({
|
|
9344
9541
|
sql: "UPDATE messages SET retry_count = ? WHERE id = ?",
|
|
@@ -9348,85 +9545,101 @@ async function deliverLocalMessage(messageId) {
|
|
|
9348
9545
|
return false;
|
|
9349
9546
|
}
|
|
9350
9547
|
}
|
|
9351
|
-
async function getPendingMessages(targetAgent) {
|
|
9548
|
+
async function getPendingMessages(targetAgent, sessionScope) {
|
|
9352
9549
|
const client = getClient();
|
|
9550
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
9353
9551
|
const result = await client.execute({
|
|
9354
9552
|
sql: `SELECT * FROM messages
|
|
9355
|
-
WHERE target_agent = ? AND status IN ('pending', 'delivered')
|
|
9553
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}
|
|
9356
9554
|
ORDER BY id`,
|
|
9357
|
-
args: [targetAgent]
|
|
9555
|
+
args: [targetAgent, ...scope.args]
|
|
9358
9556
|
});
|
|
9359
9557
|
return result.rows.map((row) => rowToMessage(row));
|
|
9360
9558
|
}
|
|
9361
|
-
async function markRead(messageId) {
|
|
9559
|
+
async function markRead(messageId, sessionScope) {
|
|
9362
9560
|
const client = getClient();
|
|
9561
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
9363
9562
|
await client.execute({
|
|
9364
|
-
sql:
|
|
9365
|
-
|
|
9563
|
+
sql: `UPDATE messages SET status = 'read'
|
|
9564
|
+
WHERE id = ? AND status IN ('pending', 'delivered')${scope.sql}`,
|
|
9565
|
+
args: [messageId, ...scope.args]
|
|
9366
9566
|
});
|
|
9367
9567
|
}
|
|
9368
|
-
async function markAcknowledged(messageId) {
|
|
9568
|
+
async function markAcknowledged(messageId, sessionScope) {
|
|
9369
9569
|
const client = getClient();
|
|
9570
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
9370
9571
|
await client.execute({
|
|
9371
|
-
sql:
|
|
9372
|
-
|
|
9572
|
+
sql: `UPDATE messages SET status = 'acknowledged', processed_at = ?
|
|
9573
|
+
WHERE id = ? AND status = 'read'${scope.sql}`,
|
|
9574
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), messageId, ...scope.args]
|
|
9373
9575
|
});
|
|
9374
9576
|
}
|
|
9375
|
-
async function markProcessed(messageId) {
|
|
9577
|
+
async function markProcessed(messageId, sessionScope) {
|
|
9376
9578
|
const client = getClient();
|
|
9579
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
9377
9580
|
await client.execute({
|
|
9378
|
-
sql:
|
|
9379
|
-
|
|
9581
|
+
sql: `UPDATE messages SET status = 'processed', processed_at = ?
|
|
9582
|
+
WHERE id = ?${scope.sql}`,
|
|
9583
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), messageId, ...scope.args]
|
|
9380
9584
|
});
|
|
9381
9585
|
}
|
|
9382
|
-
async function getMessageStatus(messageId) {
|
|
9586
|
+
async function getMessageStatus(messageId, sessionScope) {
|
|
9383
9587
|
const client = getClient();
|
|
9588
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
9384
9589
|
const result = await client.execute({
|
|
9385
|
-
sql:
|
|
9386
|
-
args: [messageId]
|
|
9590
|
+
sql: `SELECT status FROM messages WHERE id = ?${scope.sql}`,
|
|
9591
|
+
args: [messageId, ...scope.args]
|
|
9387
9592
|
});
|
|
9388
9593
|
return result.rows[0]?.status ?? null;
|
|
9389
9594
|
}
|
|
9390
|
-
async function getUnacknowledgedMessages(targetAgent) {
|
|
9595
|
+
async function getUnacknowledgedMessages(targetAgent, sessionScope) {
|
|
9391
9596
|
const client = getClient();
|
|
9597
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
9392
9598
|
const result = await client.execute({
|
|
9393
9599
|
sql: `SELECT * FROM messages
|
|
9394
|
-
WHERE target_agent = ? AND status IN ('pending', 'delivered', 'read')
|
|
9600
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered', 'read')${scope.sql}
|
|
9395
9601
|
ORDER BY id`,
|
|
9396
|
-
args: [targetAgent]
|
|
9602
|
+
args: [targetAgent, ...scope.args]
|
|
9397
9603
|
});
|
|
9398
9604
|
return result.rows.map((row) => rowToMessage(row));
|
|
9399
9605
|
}
|
|
9400
|
-
async function getReadMessages(targetAgent) {
|
|
9606
|
+
async function getReadMessages(targetAgent, sessionScope) {
|
|
9401
9607
|
const client = getClient();
|
|
9608
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
9402
9609
|
const result = await client.execute({
|
|
9403
|
-
sql:
|
|
9404
|
-
|
|
9610
|
+
sql: `SELECT * FROM messages
|
|
9611
|
+
WHERE target_agent = ? AND status = 'read'${scope.sql}
|
|
9612
|
+
ORDER BY id`,
|
|
9613
|
+
args: [targetAgent, ...scope.args]
|
|
9405
9614
|
});
|
|
9406
9615
|
return result.rows.map((row) => rowToMessage(row));
|
|
9407
9616
|
}
|
|
9408
|
-
async function markFailed(messageId, reason) {
|
|
9617
|
+
async function markFailed(messageId, reason, sessionScope) {
|
|
9409
9618
|
const client = getClient();
|
|
9619
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
9410
9620
|
await client.execute({
|
|
9411
|
-
sql:
|
|
9412
|
-
|
|
9621
|
+
sql: `UPDATE messages SET status = 'failed', failed_at = ?, failure_reason = ?
|
|
9622
|
+
WHERE id = ?${scope.sql}`,
|
|
9623
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId, ...scope.args]
|
|
9413
9624
|
});
|
|
9414
9625
|
}
|
|
9415
|
-
async function getFailedMessages() {
|
|
9626
|
+
async function getFailedMessages(sessionScope) {
|
|
9416
9627
|
const client = getClient();
|
|
9628
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
9417
9629
|
const result = await client.execute({
|
|
9418
|
-
sql:
|
|
9419
|
-
args: []
|
|
9630
|
+
sql: `SELECT * FROM messages WHERE status = 'failed'${scope.sql} ORDER BY created_at DESC`,
|
|
9631
|
+
args: [...scope.args]
|
|
9420
9632
|
});
|
|
9421
9633
|
return result.rows.map((row) => rowToMessage(row));
|
|
9422
9634
|
}
|
|
9423
|
-
async function retryPendingMessages() {
|
|
9635
|
+
async function retryPendingMessages(sessionScope) {
|
|
9424
9636
|
const client = getClient();
|
|
9637
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
9425
9638
|
const result = await client.execute({
|
|
9426
9639
|
sql: `SELECT * FROM messages
|
|
9427
|
-
WHERE status = 'pending' AND retry_count <
|
|
9640
|
+
WHERE status = 'pending' AND retry_count < ?${scope.sql}
|
|
9428
9641
|
ORDER BY id`,
|
|
9429
|
-
args: [MAX_RETRIES3]
|
|
9642
|
+
args: [MAX_RETRIES3, ...scope.args]
|
|
9430
9643
|
});
|
|
9431
9644
|
let delivered = 0;
|
|
9432
9645
|
for (const row of result.rows) {
|
|
@@ -9445,6 +9658,7 @@ var init_messaging = __esm({
|
|
|
9445
9658
|
"use strict";
|
|
9446
9659
|
init_database();
|
|
9447
9660
|
init_tmux_routing();
|
|
9661
|
+
init_task_scope();
|
|
9448
9662
|
MAX_RETRIES3 = 10;
|
|
9449
9663
|
_wsClientSend = null;
|
|
9450
9664
|
}
|
|
@@ -11925,11 +12139,11 @@ init_crm_bridge();
|
|
|
11925
12139
|
|
|
11926
12140
|
// src/lib/pipeline-router.ts
|
|
11927
12141
|
init_database();
|
|
11928
|
-
import
|
|
12142
|
+
import crypto9 from "crypto";
|
|
11929
12143
|
async function sinkConversationStore(msg, agentResponse, agentName) {
|
|
11930
12144
|
try {
|
|
11931
12145
|
const client = getClient();
|
|
11932
|
-
const id =
|
|
12146
|
+
const id = crypto9.randomUUID();
|
|
11933
12147
|
const mediaJson = msg.media ? JSON.stringify(msg.media) : null;
|
|
11934
12148
|
await client.execute({
|
|
11935
12149
|
sql: `INSERT INTO conversations
|
|
@@ -11979,7 +12193,7 @@ async function sinkMemory(msg, agentResponse, agentName) {
|
|
|
11979
12193
|
].filter(Boolean).join("\n");
|
|
11980
12194
|
const vector = await embed2(rawText);
|
|
11981
12195
|
await writeMemory2({
|
|
11982
|
-
id:
|
|
12196
|
+
id: crypto9.randomUUID(),
|
|
11983
12197
|
agent_id: agentName ?? "gateway",
|
|
11984
12198
|
agent_role: "gateway",
|
|
11985
12199
|
session_id: `gateway-${msg.platform}`,
|
|
@@ -14660,12 +14874,12 @@ var SlackAdapter = class {
|
|
|
14660
14874
|
// src/gateway/adapters/imessage.ts
|
|
14661
14875
|
import { execFile } from "child_process";
|
|
14662
14876
|
import { promisify } from "util";
|
|
14663
|
-
import
|
|
14664
|
-
import
|
|
14877
|
+
import os13 from "os";
|
|
14878
|
+
import path21 from "path";
|
|
14665
14879
|
var execFileAsync = promisify(execFile);
|
|
14666
14880
|
var POLL_INTERVAL_MS = 5e3;
|
|
14667
|
-
var MESSAGES_DB_PATH =
|
|
14668
|
-
process.env.HOME ??
|
|
14881
|
+
var MESSAGES_DB_PATH = path21.join(
|
|
14882
|
+
process.env.HOME ?? os13.homedir(),
|
|
14669
14883
|
"Library/Messages/chat.db"
|
|
14670
14884
|
);
|
|
14671
14885
|
var IMessageAdapter = class {
|
|
@@ -15508,11 +15722,11 @@ async function ensureCRMContact(info) {
|
|
|
15508
15722
|
}
|
|
15509
15723
|
|
|
15510
15724
|
// src/automation/trigger-engine.ts
|
|
15511
|
-
import { readFileSync as
|
|
15725
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, existsSync as existsSync17, mkdirSync as mkdirSync9 } from "fs";
|
|
15512
15726
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
15513
|
-
import
|
|
15514
|
-
import
|
|
15515
|
-
var TRIGGERS_PATH =
|
|
15727
|
+
import path22 from "path";
|
|
15728
|
+
import os14 from "os";
|
|
15729
|
+
var TRIGGERS_PATH = path22.join(os14.homedir(), ".exe-os", "triggers.json");
|
|
15516
15730
|
var GRAPH_API_VERSION = "v21.0";
|
|
15517
15731
|
function substituteTemplate(template, record) {
|
|
15518
15732
|
return template.replace(
|
|
@@ -15566,9 +15780,9 @@ function evaluateConditions(conditions, record) {
|
|
|
15566
15780
|
return conditions.every((c) => evaluateCondition(c, record));
|
|
15567
15781
|
}
|
|
15568
15782
|
function loadTriggers(project) {
|
|
15569
|
-
if (!
|
|
15783
|
+
if (!existsSync17(TRIGGERS_PATH)) return [];
|
|
15570
15784
|
try {
|
|
15571
|
-
const raw =
|
|
15785
|
+
const raw = readFileSync14(TRIGGERS_PATH, "utf-8");
|
|
15572
15786
|
const all = JSON.parse(raw);
|
|
15573
15787
|
if (!Array.isArray(all)) return [];
|
|
15574
15788
|
if (project) {
|