@askexenow/exe-os 0.9.7 → 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 +953 -105
- package/dist/bin/backfill-responses.js +952 -104
- package/dist/bin/backfill-vectors.js +956 -108
- package/dist/bin/cleanup-stale-review-tasks.js +802 -58
- package/dist/bin/cli.js +2292 -1070
- package/dist/bin/exe-agent-config.js +157 -101
- package/dist/bin/exe-agent.js +55 -29
- package/dist/bin/exe-assign.js +940 -92
- package/dist/bin/exe-boot.js +1424 -442
- package/dist/bin/exe-call.js +240 -141
- package/dist/bin/exe-cloud.js +198 -70
- package/dist/bin/exe-dispatch.js +951 -192
- package/dist/bin/exe-doctor.js +791 -51
- package/dist/bin/exe-export-behaviors.js +790 -42
- package/dist/bin/exe-forget.js +771 -31
- package/dist/bin/exe-gateway.js +1592 -521
- package/dist/bin/exe-heartbeat.js +850 -109
- package/dist/bin/exe-kill.js +783 -35
- package/dist/bin/exe-launch-agent.js +1030 -107
- package/dist/bin/exe-link.js +916 -110
- package/dist/bin/exe-new-employee.js +526 -217
- package/dist/bin/exe-pending-messages.js +1046 -62
- package/dist/bin/exe-pending-notifications.js +1318 -111
- package/dist/bin/exe-pending-reviews.js +1040 -72
- package/dist/bin/exe-rename.js +772 -59
- package/dist/bin/exe-review.js +772 -32
- package/dist/bin/exe-search.js +982 -128
- package/dist/bin/exe-session-cleanup.js +1180 -306
- package/dist/bin/exe-settings.js +185 -105
- package/dist/bin/exe-start-codex.js +886 -132
- package/dist/bin/exe-start-opencode.js +873 -119
- package/dist/bin/exe-status.js +803 -59
- package/dist/bin/exe-team.js +772 -32
- package/dist/bin/git-sweep.js +1046 -223
- package/dist/bin/graph-backfill.js +779 -31
- package/dist/bin/graph-export.js +785 -37
- package/dist/bin/install.js +632 -200
- package/dist/bin/scan-tasks.js +1055 -232
- package/dist/bin/setup.js +1419 -320
- package/dist/bin/shard-migrate.js +783 -35
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +782 -34
- package/dist/gateway/index.js +1444 -449
- package/dist/hooks/bug-report-worker.js +1141 -269
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +1044 -221
- package/dist/hooks/error-recall.js +989 -135
- package/dist/hooks/exe-heartbeat-hook.js +99 -75
- package/dist/hooks/ingest-worker.js +4176 -3226
- package/dist/hooks/ingest.js +920 -168
- package/dist/hooks/instructions-loaded.js +874 -70
- package/dist/hooks/notification.js +860 -56
- package/dist/hooks/post-compact.js +881 -73
- package/dist/hooks/pre-compact.js +1050 -227
- package/dist/hooks/pre-tool-use.js +1084 -159
- package/dist/hooks/prompt-ingest-worker.js +1089 -164
- package/dist/hooks/prompt-submit.js +1469 -515
- package/dist/hooks/response-ingest-worker.js +1104 -179
- package/dist/hooks/session-end.js +1085 -251
- package/dist/hooks/session-start.js +1241 -231
- package/dist/hooks/stop.js +935 -109
- package/dist/hooks/subagent-stop.js +881 -73
- package/dist/hooks/summary-worker.js +1323 -307
- package/dist/index.js +1449 -452
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +909 -115
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +42 -9
- package/dist/lib/database.js +739 -33
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +2359 -0
- package/dist/lib/device-registry.js +760 -47
- package/dist/lib/embedder.js +201 -73
- package/dist/lib/employee-templates.js +30 -4
- package/dist/lib/employees.js +290 -86
- package/dist/lib/exe-daemon-client.js +187 -83
- package/dist/lib/exe-daemon.js +1696 -616
- package/dist/lib/hybrid-search.js +982 -128
- package/dist/lib/identity.js +43 -13
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +167 -80
- package/dist/lib/reminders.js +35 -5
- package/dist/lib/schedules.js +772 -32
- package/dist/lib/skill-learning.js +54 -7
- package/dist/lib/store.js +779 -31
- package/dist/lib/task-router.js +94 -73
- package/dist/lib/tasks.js +298 -225
- package/dist/lib/tmux-routing.js +246 -172
- package/dist/lib/token-spend.js +52 -14
- package/dist/mcp/server.js +2893 -850
- package/dist/mcp/tools/complete-reminder.js +35 -5
- package/dist/mcp/tools/create-reminder.js +35 -5
- package/dist/mcp/tools/create-task.js +507 -323
- package/dist/mcp/tools/deactivate-behavior.js +40 -10
- package/dist/mcp/tools/list-reminders.js +35 -5
- package/dist/mcp/tools/list-tasks.js +277 -104
- package/dist/mcp/tools/send-message.js +129 -56
- package/dist/mcp/tools/update-task.js +1864 -188
- package/dist/runtime/index.js +1083 -259
- package/dist/tui/App.js +1501 -434
- package/package.json +3 -2
package/dist/gateway/index.js
CHANGED
|
@@ -391,6 +391,44 @@ var init_db_retry = __esm({
|
|
|
391
391
|
}
|
|
392
392
|
});
|
|
393
393
|
|
|
394
|
+
// src/lib/secure-files.ts
|
|
395
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
396
|
+
import { chmod, mkdir } from "fs/promises";
|
|
397
|
+
async function ensurePrivateDir(dirPath) {
|
|
398
|
+
await mkdir(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
399
|
+
try {
|
|
400
|
+
await chmod(dirPath, PRIVATE_DIR_MODE);
|
|
401
|
+
} catch {
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function ensurePrivateDirSync(dirPath) {
|
|
405
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
406
|
+
try {
|
|
407
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
408
|
+
} catch {
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
async function enforcePrivateFile(filePath) {
|
|
412
|
+
try {
|
|
413
|
+
await chmod(filePath, PRIVATE_FILE_MODE);
|
|
414
|
+
} catch {
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function enforcePrivateFileSync(filePath) {
|
|
418
|
+
try {
|
|
419
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
420
|
+
} catch {
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
424
|
+
var init_secure_files = __esm({
|
|
425
|
+
"src/lib/secure-files.ts"() {
|
|
426
|
+
"use strict";
|
|
427
|
+
PRIVATE_DIR_MODE = 448;
|
|
428
|
+
PRIVATE_FILE_MODE = 384;
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
394
432
|
// src/lib/config.ts
|
|
395
433
|
var config_exports = {};
|
|
396
434
|
__export(config_exports, {
|
|
@@ -407,8 +445,8 @@ __export(config_exports, {
|
|
|
407
445
|
migrateConfig: () => migrateConfig,
|
|
408
446
|
saveConfig: () => saveConfig
|
|
409
447
|
});
|
|
410
|
-
import { readFile, writeFile
|
|
411
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
448
|
+
import { readFile, writeFile } from "fs/promises";
|
|
449
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
412
450
|
import path from "path";
|
|
413
451
|
import os from "os";
|
|
414
452
|
function resolveDataDir() {
|
|
@@ -416,7 +454,7 @@ function resolveDataDir() {
|
|
|
416
454
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
417
455
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
418
456
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
419
|
-
if (!
|
|
457
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
420
458
|
try {
|
|
421
459
|
renameSync(legacyDir, newDir);
|
|
422
460
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -479,9 +517,9 @@ function normalizeAutoUpdate(raw) {
|
|
|
479
517
|
}
|
|
480
518
|
async function loadConfig() {
|
|
481
519
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
482
|
-
await
|
|
520
|
+
await ensurePrivateDir(dir);
|
|
483
521
|
const configPath = path.join(dir, "config.json");
|
|
484
|
-
if (!
|
|
522
|
+
if (!existsSync2(configPath)) {
|
|
485
523
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
486
524
|
}
|
|
487
525
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -494,6 +532,7 @@ async function loadConfig() {
|
|
|
494
532
|
`);
|
|
495
533
|
try {
|
|
496
534
|
await writeFile(configPath, JSON.stringify(migratedCfg, null, 2) + "\n");
|
|
535
|
+
await enforcePrivateFile(configPath);
|
|
497
536
|
} catch {
|
|
498
537
|
}
|
|
499
538
|
}
|
|
@@ -512,7 +551,7 @@ async function loadConfig() {
|
|
|
512
551
|
function loadConfigSync() {
|
|
513
552
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
514
553
|
const configPath = path.join(dir, "config.json");
|
|
515
|
-
if (!
|
|
554
|
+
if (!existsSync2(configPath)) {
|
|
516
555
|
return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
|
|
517
556
|
}
|
|
518
557
|
try {
|
|
@@ -530,12 +569,10 @@ function loadConfigSync() {
|
|
|
530
569
|
}
|
|
531
570
|
async function saveConfig(config2) {
|
|
532
571
|
const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
|
|
533
|
-
await
|
|
572
|
+
await ensurePrivateDir(dir);
|
|
534
573
|
const configPath = path.join(dir, "config.json");
|
|
535
574
|
await writeFile(configPath, JSON.stringify(config2, null, 2) + "\n");
|
|
536
|
-
|
|
537
|
-
await chmod(configPath, 384);
|
|
538
|
-
}
|
|
575
|
+
await enforcePrivateFile(configPath);
|
|
539
576
|
}
|
|
540
577
|
async function loadConfigFrom(configPath) {
|
|
541
578
|
const raw = await readFile(configPath, "utf-8");
|
|
@@ -555,6 +592,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
555
592
|
var init_config = __esm({
|
|
556
593
|
"src/lib/config.ts"() {
|
|
557
594
|
"use strict";
|
|
595
|
+
init_secure_files();
|
|
558
596
|
EXE_AI_DIR = resolveDataDir();
|
|
559
597
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
560
598
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -631,6 +669,120 @@ var init_config = __esm({
|
|
|
631
669
|
}
|
|
632
670
|
});
|
|
633
671
|
|
|
672
|
+
// src/lib/runtime-table.ts
|
|
673
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
674
|
+
var init_runtime_table = __esm({
|
|
675
|
+
"src/lib/runtime-table.ts"() {
|
|
676
|
+
"use strict";
|
|
677
|
+
RUNTIME_TABLE = {
|
|
678
|
+
codex: {
|
|
679
|
+
binary: "codex",
|
|
680
|
+
launchMode: "interactive",
|
|
681
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
682
|
+
inlineFlag: "--no-alt-screen",
|
|
683
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
684
|
+
defaultModel: "gpt-5.4"
|
|
685
|
+
},
|
|
686
|
+
opencode: {
|
|
687
|
+
binary: "opencode",
|
|
688
|
+
launchMode: "exec",
|
|
689
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
690
|
+
inlineFlag: "",
|
|
691
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
692
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
DEFAULT_RUNTIME = "claude";
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
// src/lib/agent-config.ts
|
|
700
|
+
var agent_config_exports = {};
|
|
701
|
+
__export(agent_config_exports, {
|
|
702
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
703
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
704
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
705
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
706
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
707
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
708
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
709
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
710
|
+
setAgentRuntime: () => setAgentRuntime
|
|
711
|
+
});
|
|
712
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
713
|
+
import path2 from "path";
|
|
714
|
+
function loadAgentConfig() {
|
|
715
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
716
|
+
try {
|
|
717
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
718
|
+
} catch {
|
|
719
|
+
return {};
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
function saveAgentConfig(config2) {
|
|
723
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
724
|
+
ensurePrivateDirSync(dir);
|
|
725
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
726
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
727
|
+
}
|
|
728
|
+
function getAgentRuntime(agentId) {
|
|
729
|
+
const config2 = loadAgentConfig();
|
|
730
|
+
const entry = config2[agentId];
|
|
731
|
+
if (entry) return entry;
|
|
732
|
+
const orgDefault = config2["default"];
|
|
733
|
+
if (orgDefault) return orgDefault;
|
|
734
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
735
|
+
}
|
|
736
|
+
function setAgentRuntime(agentId, runtime, model) {
|
|
737
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
738
|
+
if (!knownModels) {
|
|
739
|
+
return {
|
|
740
|
+
ok: false,
|
|
741
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
if (!knownModels.includes(model)) {
|
|
745
|
+
return {
|
|
746
|
+
ok: false,
|
|
747
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
const config2 = loadAgentConfig();
|
|
751
|
+
config2[agentId] = { runtime, model };
|
|
752
|
+
saveAgentConfig(config2);
|
|
753
|
+
return { ok: true };
|
|
754
|
+
}
|
|
755
|
+
function clearAgentRuntime(agentId) {
|
|
756
|
+
const config2 = loadAgentConfig();
|
|
757
|
+
delete config2[agentId];
|
|
758
|
+
saveAgentConfig(config2);
|
|
759
|
+
}
|
|
760
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
761
|
+
var init_agent_config = __esm({
|
|
762
|
+
"src/lib/agent-config.ts"() {
|
|
763
|
+
"use strict";
|
|
764
|
+
init_config();
|
|
765
|
+
init_runtime_table();
|
|
766
|
+
init_secure_files();
|
|
767
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
768
|
+
KNOWN_RUNTIMES = {
|
|
769
|
+
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
770
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
771
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
772
|
+
};
|
|
773
|
+
RUNTIME_LABELS = {
|
|
774
|
+
claude: "Claude Code (Anthropic)",
|
|
775
|
+
codex: "Codex (OpenAI)",
|
|
776
|
+
opencode: "OpenCode (open source)"
|
|
777
|
+
};
|
|
778
|
+
DEFAULT_MODELS = {
|
|
779
|
+
claude: "claude-opus-4",
|
|
780
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
781
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
|
|
634
786
|
// src/lib/employees.ts
|
|
635
787
|
var employees_exports = {};
|
|
636
788
|
__export(employees_exports, {
|
|
@@ -646,6 +798,7 @@ __export(employees_exports, {
|
|
|
646
798
|
getEmployeeByRole: () => getEmployeeByRole,
|
|
647
799
|
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
648
800
|
hasRole: () => hasRole,
|
|
801
|
+
hireEmployee: () => hireEmployee,
|
|
649
802
|
isCoordinatorName: () => isCoordinatorName,
|
|
650
803
|
isCoordinatorRole: () => isCoordinatorRole,
|
|
651
804
|
isMultiInstance: () => isMultiInstance,
|
|
@@ -658,9 +811,9 @@ __export(employees_exports, {
|
|
|
658
811
|
validateEmployeeName: () => validateEmployeeName
|
|
659
812
|
});
|
|
660
813
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
661
|
-
import { existsSync as
|
|
814
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
662
815
|
import { execSync } from "child_process";
|
|
663
|
-
import
|
|
816
|
+
import path3 from "path";
|
|
664
817
|
import os2 from "os";
|
|
665
818
|
function normalizeRole(role) {
|
|
666
819
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -697,7 +850,7 @@ function validateEmployeeName(name) {
|
|
|
697
850
|
return { valid: true };
|
|
698
851
|
}
|
|
699
852
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
700
|
-
if (!
|
|
853
|
+
if (!existsSync4(employeesPath)) {
|
|
701
854
|
return [];
|
|
702
855
|
}
|
|
703
856
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -708,13 +861,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
708
861
|
}
|
|
709
862
|
}
|
|
710
863
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
711
|
-
await mkdir2(
|
|
864
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
712
865
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
713
866
|
}
|
|
714
867
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
715
|
-
if (!
|
|
868
|
+
if (!existsSync4(employeesPath)) return [];
|
|
716
869
|
try {
|
|
717
|
-
return JSON.parse(
|
|
870
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
718
871
|
} catch {
|
|
719
872
|
return [];
|
|
720
873
|
}
|
|
@@ -756,6 +909,52 @@ function addEmployee(employees, employee) {
|
|
|
756
909
|
}
|
|
757
910
|
return [...employees, normalized];
|
|
758
911
|
}
|
|
912
|
+
function appendToCoordinatorTeam(employee) {
|
|
913
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
914
|
+
if (!coordinator) return;
|
|
915
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
916
|
+
if (!existsSync4(idPath)) return;
|
|
917
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
918
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
919
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
920
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
921
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
922
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
923
|
+
const entry = `
|
|
924
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
925
|
+
`;
|
|
926
|
+
let updated;
|
|
927
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
928
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
929
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
930
|
+
} else {
|
|
931
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
932
|
+
}
|
|
933
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
934
|
+
}
|
|
935
|
+
function capitalize(s) {
|
|
936
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
937
|
+
}
|
|
938
|
+
async function hireEmployee(employee) {
|
|
939
|
+
const employees = await loadEmployees();
|
|
940
|
+
const updated = addEmployee(employees, employee);
|
|
941
|
+
await saveEmployees(updated);
|
|
942
|
+
try {
|
|
943
|
+
appendToCoordinatorTeam(employee);
|
|
944
|
+
} catch {
|
|
945
|
+
}
|
|
946
|
+
try {
|
|
947
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
948
|
+
const config2 = loadAgentConfig2();
|
|
949
|
+
const name = employee.name.toLowerCase();
|
|
950
|
+
if (!config2[name] && config2["default"]) {
|
|
951
|
+
config2[name] = { ...config2["default"] };
|
|
952
|
+
saveAgentConfig2(config2);
|
|
953
|
+
}
|
|
954
|
+
} catch {
|
|
955
|
+
}
|
|
956
|
+
return updated;
|
|
957
|
+
}
|
|
759
958
|
async function normalizeRosterCase(rosterPath) {
|
|
760
959
|
const employees = await loadEmployees(rosterPath);
|
|
761
960
|
let changed = false;
|
|
@@ -765,14 +964,14 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
765
964
|
emp.name = emp.name.toLowerCase();
|
|
766
965
|
changed = true;
|
|
767
966
|
try {
|
|
768
|
-
const identityDir =
|
|
769
|
-
const oldPath =
|
|
770
|
-
const newPath =
|
|
771
|
-
if (
|
|
967
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
968
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
969
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
970
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
772
971
|
renameSync2(oldPath, newPath);
|
|
773
|
-
} else if (
|
|
774
|
-
const content =
|
|
775
|
-
|
|
972
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
973
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
974
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
776
975
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
777
976
|
unlinkSync(oldPath);
|
|
778
977
|
}
|
|
@@ -802,7 +1001,7 @@ function registerBinSymlinks(name) {
|
|
|
802
1001
|
errors.push("Could not find 'exe-os' in PATH");
|
|
803
1002
|
return { created, skipped, errors };
|
|
804
1003
|
}
|
|
805
|
-
const binDir =
|
|
1004
|
+
const binDir = path3.dirname(exeBinPath);
|
|
806
1005
|
let target;
|
|
807
1006
|
try {
|
|
808
1007
|
target = readlinkSync(exeBinPath);
|
|
@@ -812,8 +1011,8 @@ function registerBinSymlinks(name) {
|
|
|
812
1011
|
}
|
|
813
1012
|
for (const suffix of ["", "-opencode"]) {
|
|
814
1013
|
const linkName = `${name}${suffix}`;
|
|
815
|
-
const linkPath =
|
|
816
|
-
if (
|
|
1014
|
+
const linkPath = path3.join(binDir, linkName);
|
|
1015
|
+
if (existsSync4(linkPath)) {
|
|
817
1016
|
skipped.push(linkName);
|
|
818
1017
|
continue;
|
|
819
1018
|
}
|
|
@@ -826,21 +1025,619 @@ function registerBinSymlinks(name) {
|
|
|
826
1025
|
}
|
|
827
1026
|
return { created, skipped, errors };
|
|
828
1027
|
}
|
|
829
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
1028
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
830
1029
|
var init_employees = __esm({
|
|
831
1030
|
"src/lib/employees.ts"() {
|
|
832
1031
|
"use strict";
|
|
833
1032
|
init_config();
|
|
834
|
-
EMPLOYEES_PATH =
|
|
1033
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
835
1034
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
836
1035
|
COORDINATOR_ROLE = "COO";
|
|
837
1036
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
1037
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
1038
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
// src/lib/database-adapter.ts
|
|
1043
|
+
import os3 from "os";
|
|
1044
|
+
import path4 from "path";
|
|
1045
|
+
import { createRequire } from "module";
|
|
1046
|
+
import { pathToFileURL } from "url";
|
|
1047
|
+
function quotedIdentifier(identifier) {
|
|
1048
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
1049
|
+
}
|
|
1050
|
+
function unqualifiedTableName(name) {
|
|
1051
|
+
const raw = name.trim().replace(/^"|"$/g, "");
|
|
1052
|
+
const parts = raw.split(".");
|
|
1053
|
+
return parts[parts.length - 1].replace(/^"|"$/g, "").toLowerCase();
|
|
1054
|
+
}
|
|
1055
|
+
function stripTrailingSemicolon(sql) {
|
|
1056
|
+
return sql.trim().replace(/;+\s*$/u, "");
|
|
1057
|
+
}
|
|
1058
|
+
function appendClause(sql, clause) {
|
|
1059
|
+
const trimmed = stripTrailingSemicolon(sql);
|
|
1060
|
+
const returningMatch = /\sRETURNING\b[\s\S]*$/iu.exec(trimmed);
|
|
1061
|
+
if (!returningMatch) {
|
|
1062
|
+
return `${trimmed}${clause}`;
|
|
1063
|
+
}
|
|
1064
|
+
const idx = returningMatch.index;
|
|
1065
|
+
return `${trimmed.slice(0, idx)}${clause}${trimmed.slice(idx)}`;
|
|
1066
|
+
}
|
|
1067
|
+
function normalizeStatement(stmt) {
|
|
1068
|
+
if (typeof stmt === "string") {
|
|
1069
|
+
return { kind: "positional", sql: stmt, args: [] };
|
|
1070
|
+
}
|
|
1071
|
+
const sql = stmt.sql;
|
|
1072
|
+
if (Array.isArray(stmt.args) || stmt.args === void 0) {
|
|
1073
|
+
return { kind: "positional", sql, args: stmt.args ?? [] };
|
|
1074
|
+
}
|
|
1075
|
+
return { kind: "named", sql, args: stmt.args };
|
|
1076
|
+
}
|
|
1077
|
+
function rewriteBooleanLiterals(sql) {
|
|
1078
|
+
let out = sql;
|
|
1079
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
1080
|
+
const scoped = `((?:\\b[a-z_][a-z0-9_]*\\.)?${column})`;
|
|
1081
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*0\\b`, "giu"), "$1 = FALSE");
|
|
1082
|
+
out = out.replace(new RegExp(`${scoped}\\s*=\\s*1\\b`, "giu"), "$1 = TRUE");
|
|
1083
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*0\\b`, "giu"), "$1 != FALSE");
|
|
1084
|
+
out = out.replace(new RegExp(`${scoped}\\s*!=\\s*1\\b`, "giu"), "$1 != TRUE");
|
|
1085
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*0\\b`, "giu"), "$1 <> FALSE");
|
|
1086
|
+
out = out.replace(new RegExp(`${scoped}\\s*<>\\s*1\\b`, "giu"), "$1 <> TRUE");
|
|
1087
|
+
}
|
|
1088
|
+
return out;
|
|
1089
|
+
}
|
|
1090
|
+
function rewriteInsertOrIgnore(sql) {
|
|
1091
|
+
if (!/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu.test(sql)) {
|
|
1092
|
+
return sql;
|
|
1093
|
+
}
|
|
1094
|
+
const replaced = sql.replace(/^\s*INSERT\s+OR\s+IGNORE\s+INTO\b/iu, "INSERT INTO");
|
|
1095
|
+
return /\bON\s+CONFLICT\b/iu.test(replaced) ? replaced : appendClause(replaced, " ON CONFLICT DO NOTHING");
|
|
1096
|
+
}
|
|
1097
|
+
function rewriteInsertOrReplace(sql) {
|
|
1098
|
+
const match = /^\s*INSERT\s+OR\s+REPLACE\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)([\s\S]*)$/iu.exec(sql);
|
|
1099
|
+
if (!match) {
|
|
1100
|
+
return sql;
|
|
1101
|
+
}
|
|
1102
|
+
const rawTable = match[1];
|
|
1103
|
+
const rawColumns = match[2];
|
|
1104
|
+
const remainder = match[3];
|
|
1105
|
+
const tableName = unqualifiedTableName(rawTable);
|
|
1106
|
+
const conflictKeys = UPSERT_KEYS[tableName];
|
|
1107
|
+
if (!conflictKeys?.length) {
|
|
1108
|
+
return sql;
|
|
1109
|
+
}
|
|
1110
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
1111
|
+
const updateColumns = columns.filter((col) => !conflictKeys.includes(col));
|
|
1112
|
+
const conflictTarget = conflictKeys.map(quotedIdentifier).join(", ");
|
|
1113
|
+
const updateClause = updateColumns.length === 0 ? " DO NOTHING" : ` DO UPDATE SET ${updateColumns.map((col) => `${quotedIdentifier(col)} = EXCLUDED.${quotedIdentifier(col)}`).join(", ")}`;
|
|
1114
|
+
return `INSERT INTO ${rawTable} (${rawColumns})${appendClause(remainder, ` ON CONFLICT (${conflictTarget})${updateClause}`)}`;
|
|
1115
|
+
}
|
|
1116
|
+
function rewriteSql(sql) {
|
|
1117
|
+
let out = sql;
|
|
1118
|
+
out = out.replace(/\bdatetime\(\s*['"]now['"]\s*\)/giu, "CURRENT_TIMESTAMP");
|
|
1119
|
+
out = out.replace(/\bvector32\s*\(\s*\?\s*\)/giu, "?");
|
|
1120
|
+
out = rewriteBooleanLiterals(out);
|
|
1121
|
+
out = rewriteInsertOrReplace(out);
|
|
1122
|
+
out = rewriteInsertOrIgnore(out);
|
|
1123
|
+
return stripTrailingSemicolon(out);
|
|
1124
|
+
}
|
|
1125
|
+
function toBoolean(value) {
|
|
1126
|
+
if (value === null || value === void 0) return value;
|
|
1127
|
+
if (typeof value === "boolean") return value;
|
|
1128
|
+
if (typeof value === "number") return value !== 0;
|
|
1129
|
+
if (typeof value === "bigint") return value !== 0n;
|
|
1130
|
+
if (typeof value === "string") {
|
|
1131
|
+
const normalized = value.trim().toLowerCase();
|
|
1132
|
+
if (normalized === "0" || normalized === "false") return false;
|
|
1133
|
+
if (normalized === "1" || normalized === "true") return true;
|
|
1134
|
+
}
|
|
1135
|
+
return Boolean(value);
|
|
1136
|
+
}
|
|
1137
|
+
function countQuestionMarks(sql, end) {
|
|
1138
|
+
let count = 0;
|
|
1139
|
+
let inSingle = false;
|
|
1140
|
+
let inDouble = false;
|
|
1141
|
+
let inLineComment = false;
|
|
1142
|
+
let inBlockComment = false;
|
|
1143
|
+
for (let i = 0; i < end; i++) {
|
|
1144
|
+
const ch = sql[i];
|
|
1145
|
+
const next = sql[i + 1];
|
|
1146
|
+
if (inLineComment) {
|
|
1147
|
+
if (ch === "\n") inLineComment = false;
|
|
1148
|
+
continue;
|
|
1149
|
+
}
|
|
1150
|
+
if (inBlockComment) {
|
|
1151
|
+
if (ch === "*" && next === "/") {
|
|
1152
|
+
inBlockComment = false;
|
|
1153
|
+
i += 1;
|
|
1154
|
+
}
|
|
1155
|
+
continue;
|
|
1156
|
+
}
|
|
1157
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1158
|
+
inLineComment = true;
|
|
1159
|
+
i += 1;
|
|
1160
|
+
continue;
|
|
1161
|
+
}
|
|
1162
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1163
|
+
inBlockComment = true;
|
|
1164
|
+
i += 1;
|
|
1165
|
+
continue;
|
|
1166
|
+
}
|
|
1167
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1168
|
+
inSingle = !inSingle;
|
|
1169
|
+
continue;
|
|
1170
|
+
}
|
|
1171
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1172
|
+
inDouble = !inDouble;
|
|
1173
|
+
continue;
|
|
1174
|
+
}
|
|
1175
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1176
|
+
count += 1;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
return count;
|
|
1180
|
+
}
|
|
1181
|
+
function findBooleanPlaceholderIndexes(sql) {
|
|
1182
|
+
const indexes = /* @__PURE__ */ new Set();
|
|
1183
|
+
for (const column of BOOLEAN_COLUMN_NAMES) {
|
|
1184
|
+
const pattern = new RegExp(`(?:\\b[a-z_][a-z0-9_]*\\.)?${column}\\s*=\\s*\\?`, "giu");
|
|
1185
|
+
for (const match of sql.matchAll(pattern)) {
|
|
1186
|
+
const matchText = match[0];
|
|
1187
|
+
const qIndex = match.index + matchText.lastIndexOf("?");
|
|
1188
|
+
indexes.add(countQuestionMarks(sql, qIndex + 1));
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
return indexes;
|
|
1192
|
+
}
|
|
1193
|
+
function coerceInsertBooleanArgs(sql, args) {
|
|
1194
|
+
const match = /^\s*INSERT(?:\s+OR\s+(?:IGNORE|REPLACE))?\s+INTO\s+([A-Za-z0-9_."]+)\s*\(([^)]+)\)/iu.exec(sql);
|
|
1195
|
+
if (!match) return;
|
|
1196
|
+
const rawTable = match[1];
|
|
1197
|
+
const rawColumns = match[2];
|
|
1198
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1199
|
+
if (!boolColumns?.size) return;
|
|
1200
|
+
const columns = rawColumns.split(",").map((col) => col.trim().replace(/^"|"$/g, ""));
|
|
1201
|
+
for (const [index, column] of columns.entries()) {
|
|
1202
|
+
if (boolColumns.has(column) && index < args.length) {
|
|
1203
|
+
args[index] = toBoolean(args[index]);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
function coerceUpdateBooleanArgs(sql, args) {
|
|
1208
|
+
const match = /^\s*UPDATE\s+([A-Za-z0-9_."]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE\b|$)/iu.exec(sql);
|
|
1209
|
+
if (!match) return;
|
|
1210
|
+
const rawTable = match[1];
|
|
1211
|
+
const setClause = match[2];
|
|
1212
|
+
const boolColumns = BOOLEAN_COLUMNS_BY_TABLE[unqualifiedTableName(rawTable)];
|
|
1213
|
+
if (!boolColumns?.size) return;
|
|
1214
|
+
const assignments = setClause.split(",");
|
|
1215
|
+
let placeholderIndex = 0;
|
|
1216
|
+
for (const assignment of assignments) {
|
|
1217
|
+
if (!assignment.includes("?")) continue;
|
|
1218
|
+
placeholderIndex += 1;
|
|
1219
|
+
const colMatch = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*\.)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\?/iu.exec(assignment);
|
|
1220
|
+
if (colMatch && boolColumns.has(colMatch[1])) {
|
|
1221
|
+
args[placeholderIndex - 1] = toBoolean(args[placeholderIndex - 1]);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
function coerceBooleanArgs(sql, args) {
|
|
1226
|
+
const nextArgs = [...args];
|
|
1227
|
+
coerceInsertBooleanArgs(sql, nextArgs);
|
|
1228
|
+
coerceUpdateBooleanArgs(sql, nextArgs);
|
|
1229
|
+
const placeholderIndexes = findBooleanPlaceholderIndexes(sql);
|
|
1230
|
+
for (const index of placeholderIndexes) {
|
|
1231
|
+
if (index > 0 && index <= nextArgs.length) {
|
|
1232
|
+
nextArgs[index - 1] = toBoolean(nextArgs[index - 1]);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
return nextArgs;
|
|
1236
|
+
}
|
|
1237
|
+
function convertQuestionMarksToDollarParams(sql) {
|
|
1238
|
+
let out = "";
|
|
1239
|
+
let placeholder = 0;
|
|
1240
|
+
let inSingle = false;
|
|
1241
|
+
let inDouble = false;
|
|
1242
|
+
let inLineComment = false;
|
|
1243
|
+
let inBlockComment = false;
|
|
1244
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1245
|
+
const ch = sql[i];
|
|
1246
|
+
const next = sql[i + 1];
|
|
1247
|
+
if (inLineComment) {
|
|
1248
|
+
out += ch;
|
|
1249
|
+
if (ch === "\n") inLineComment = false;
|
|
1250
|
+
continue;
|
|
1251
|
+
}
|
|
1252
|
+
if (inBlockComment) {
|
|
1253
|
+
out += ch;
|
|
1254
|
+
if (ch === "*" && next === "/") {
|
|
1255
|
+
out += next;
|
|
1256
|
+
inBlockComment = false;
|
|
1257
|
+
i += 1;
|
|
1258
|
+
}
|
|
1259
|
+
continue;
|
|
1260
|
+
}
|
|
1261
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1262
|
+
out += ch + next;
|
|
1263
|
+
inLineComment = true;
|
|
1264
|
+
i += 1;
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1267
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1268
|
+
out += ch + next;
|
|
1269
|
+
inBlockComment = true;
|
|
1270
|
+
i += 1;
|
|
1271
|
+
continue;
|
|
1272
|
+
}
|
|
1273
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1274
|
+
inSingle = !inSingle;
|
|
1275
|
+
out += ch;
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1278
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1279
|
+
inDouble = !inDouble;
|
|
1280
|
+
out += ch;
|
|
1281
|
+
continue;
|
|
1282
|
+
}
|
|
1283
|
+
if (!inSingle && !inDouble && ch === "?") {
|
|
1284
|
+
placeholder += 1;
|
|
1285
|
+
out += `$${placeholder}`;
|
|
1286
|
+
continue;
|
|
1287
|
+
}
|
|
1288
|
+
out += ch;
|
|
1289
|
+
}
|
|
1290
|
+
return out;
|
|
1291
|
+
}
|
|
1292
|
+
function translateStatementForPostgres(stmt) {
|
|
1293
|
+
const normalized = normalizeStatement(stmt);
|
|
1294
|
+
if (normalized.kind === "named") {
|
|
1295
|
+
throw new Error("Named SQL parameters are not supported by the Prisma adapter.");
|
|
1296
|
+
}
|
|
1297
|
+
const rewrittenSql = rewriteSql(normalized.sql);
|
|
1298
|
+
const coercedArgs = coerceBooleanArgs(rewrittenSql, normalized.args);
|
|
1299
|
+
return {
|
|
1300
|
+
sql: convertQuestionMarksToDollarParams(rewrittenSql),
|
|
1301
|
+
args: coercedArgs
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
function shouldBypassPostgres(stmt) {
|
|
1305
|
+
const normalized = normalizeStatement(stmt);
|
|
1306
|
+
if (normalized.kind === "named") {
|
|
1307
|
+
return true;
|
|
1308
|
+
}
|
|
1309
|
+
return IMMEDIATE_FALLBACK_PATTERNS.some((pattern) => pattern.test(normalized.sql));
|
|
1310
|
+
}
|
|
1311
|
+
function shouldFallbackOnError(error) {
|
|
1312
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1313
|
+
return /42P01|42883|42601|does not exist|syntax error|not supported|Named SQL parameters are not supported/iu.test(message);
|
|
1314
|
+
}
|
|
1315
|
+
function isReadQuery(sql) {
|
|
1316
|
+
const trimmed = sql.trimStart();
|
|
1317
|
+
return /^(SELECT|WITH|SHOW|EXPLAIN|VALUES)\b/iu.test(trimmed) || /\bRETURNING\b/iu.test(trimmed);
|
|
1318
|
+
}
|
|
1319
|
+
function buildRow(row, columns) {
|
|
1320
|
+
const values = columns.map((column) => row[column]);
|
|
1321
|
+
return Object.assign(values, row);
|
|
1322
|
+
}
|
|
1323
|
+
function buildResultSet(rows, rowsAffected = 0) {
|
|
1324
|
+
const columns = rows[0] ? Object.keys(rows[0]) : [];
|
|
1325
|
+
const resultRows = rows.map((row) => buildRow(row, columns));
|
|
1326
|
+
return {
|
|
1327
|
+
columns,
|
|
1328
|
+
columnTypes: columns.map(() => ""),
|
|
1329
|
+
rows: resultRows,
|
|
1330
|
+
rowsAffected,
|
|
1331
|
+
lastInsertRowid: void 0,
|
|
1332
|
+
toJSON() {
|
|
1333
|
+
return {
|
|
1334
|
+
columns,
|
|
1335
|
+
columnTypes: columns.map(() => ""),
|
|
1336
|
+
rows,
|
|
1337
|
+
rowsAffected,
|
|
1338
|
+
lastInsertRowid: void 0
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
async function loadPrismaClient() {
|
|
1344
|
+
if (!prismaClientPromise) {
|
|
1345
|
+
prismaClientPromise = (async () => {
|
|
1346
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
1347
|
+
if (explicitPath) {
|
|
1348
|
+
const module2 = await import(pathToFileURL(explicitPath).href);
|
|
1349
|
+
const PrismaClient2 = module2.PrismaClient ?? module2.default?.PrismaClient;
|
|
1350
|
+
if (!PrismaClient2) {
|
|
1351
|
+
throw new Error(`No PrismaClient export found at ${explicitPath}`);
|
|
1352
|
+
}
|
|
1353
|
+
return new PrismaClient2();
|
|
1354
|
+
}
|
|
1355
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path4.join(os3.homedir(), "exe-db");
|
|
1356
|
+
const requireFromExeDb = createRequire(path4.join(exeDbRoot, "package.json"));
|
|
1357
|
+
const prismaEntry = requireFromExeDb.resolve("@prisma/client");
|
|
1358
|
+
const module = await import(pathToFileURL(prismaEntry).href);
|
|
1359
|
+
const PrismaClient = module.PrismaClient ?? module.default?.PrismaClient;
|
|
1360
|
+
if (!PrismaClient) {
|
|
1361
|
+
throw new Error(`No PrismaClient export found in ${prismaEntry}`);
|
|
1362
|
+
}
|
|
1363
|
+
return new PrismaClient();
|
|
1364
|
+
})();
|
|
1365
|
+
}
|
|
1366
|
+
return prismaClientPromise;
|
|
1367
|
+
}
|
|
1368
|
+
async function ensureCompatibilityViews(prisma) {
|
|
1369
|
+
if (!compatibilityBootstrapPromise) {
|
|
1370
|
+
compatibilityBootstrapPromise = (async () => {
|
|
1371
|
+
for (const mapping of VIEW_MAPPINGS) {
|
|
1372
|
+
const relation = mapping.source.replace(/"/g, "");
|
|
1373
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
1374
|
+
"SELECT to_regclass($1) AS regclass",
|
|
1375
|
+
relation
|
|
1376
|
+
);
|
|
1377
|
+
if (!rows[0]?.regclass) {
|
|
1378
|
+
continue;
|
|
1379
|
+
}
|
|
1380
|
+
await prisma.$executeRawUnsafe(
|
|
1381
|
+
`CREATE OR REPLACE VIEW public.${quotedIdentifier(mapping.view)} AS SELECT * FROM ${mapping.source}`
|
|
1382
|
+
);
|
|
1383
|
+
}
|
|
1384
|
+
})();
|
|
1385
|
+
}
|
|
1386
|
+
return compatibilityBootstrapPromise;
|
|
1387
|
+
}
|
|
1388
|
+
async function executeOnPrisma(executor, stmt) {
|
|
1389
|
+
const translated = translateStatementForPostgres(stmt);
|
|
1390
|
+
if (isReadQuery(translated.sql)) {
|
|
1391
|
+
const rows = await executor.$queryRawUnsafe(
|
|
1392
|
+
translated.sql,
|
|
1393
|
+
...translated.args
|
|
1394
|
+
);
|
|
1395
|
+
return buildResultSet(rows, /\bRETURNING\b/iu.test(translated.sql) ? rows.length : 0);
|
|
1396
|
+
}
|
|
1397
|
+
const rowsAffected = await executor.$executeRawUnsafe(translated.sql, ...translated.args);
|
|
1398
|
+
return buildResultSet([], rowsAffected);
|
|
1399
|
+
}
|
|
1400
|
+
function splitSqlStatements(sql) {
|
|
1401
|
+
const parts = [];
|
|
1402
|
+
let current = "";
|
|
1403
|
+
let inSingle = false;
|
|
1404
|
+
let inDouble = false;
|
|
1405
|
+
let inLineComment = false;
|
|
1406
|
+
let inBlockComment = false;
|
|
1407
|
+
for (let i = 0; i < sql.length; i++) {
|
|
1408
|
+
const ch = sql[i];
|
|
1409
|
+
const next = sql[i + 1];
|
|
1410
|
+
if (inLineComment) {
|
|
1411
|
+
current += ch;
|
|
1412
|
+
if (ch === "\n") inLineComment = false;
|
|
1413
|
+
continue;
|
|
1414
|
+
}
|
|
1415
|
+
if (inBlockComment) {
|
|
1416
|
+
current += ch;
|
|
1417
|
+
if (ch === "*" && next === "/") {
|
|
1418
|
+
current += next;
|
|
1419
|
+
inBlockComment = false;
|
|
1420
|
+
i += 1;
|
|
1421
|
+
}
|
|
1422
|
+
continue;
|
|
1423
|
+
}
|
|
1424
|
+
if (!inSingle && !inDouble && ch === "-" && next === "-") {
|
|
1425
|
+
current += ch + next;
|
|
1426
|
+
inLineComment = true;
|
|
1427
|
+
i += 1;
|
|
1428
|
+
continue;
|
|
1429
|
+
}
|
|
1430
|
+
if (!inSingle && !inDouble && ch === "/" && next === "*") {
|
|
1431
|
+
current += ch + next;
|
|
1432
|
+
inBlockComment = true;
|
|
1433
|
+
i += 1;
|
|
1434
|
+
continue;
|
|
1435
|
+
}
|
|
1436
|
+
if (!inDouble && ch === "'" && sql[i - 1] !== "\\") {
|
|
1437
|
+
inSingle = !inSingle;
|
|
1438
|
+
current += ch;
|
|
1439
|
+
continue;
|
|
1440
|
+
}
|
|
1441
|
+
if (!inSingle && ch === '"' && sql[i - 1] !== "\\") {
|
|
1442
|
+
inDouble = !inDouble;
|
|
1443
|
+
current += ch;
|
|
1444
|
+
continue;
|
|
1445
|
+
}
|
|
1446
|
+
if (!inSingle && !inDouble && ch === ";") {
|
|
1447
|
+
if (current.trim()) {
|
|
1448
|
+
parts.push(current.trim());
|
|
1449
|
+
}
|
|
1450
|
+
current = "";
|
|
1451
|
+
continue;
|
|
1452
|
+
}
|
|
1453
|
+
current += ch;
|
|
1454
|
+
}
|
|
1455
|
+
if (current.trim()) {
|
|
1456
|
+
parts.push(current.trim());
|
|
1457
|
+
}
|
|
1458
|
+
return parts;
|
|
1459
|
+
}
|
|
1460
|
+
async function createPrismaDbAdapter(fallbackClient) {
|
|
1461
|
+
const prisma = await loadPrismaClient();
|
|
1462
|
+
await ensureCompatibilityViews(prisma);
|
|
1463
|
+
let closed = false;
|
|
1464
|
+
let adapter;
|
|
1465
|
+
const fallbackExecute = async (stmt, error) => {
|
|
1466
|
+
if (!fallbackClient) {
|
|
1467
|
+
if (error) throw error;
|
|
1468
|
+
throw new Error("No fallback SQLite client is available for this Prisma-routed query.");
|
|
1469
|
+
}
|
|
1470
|
+
if (error) {
|
|
1471
|
+
process.stderr.write(
|
|
1472
|
+
`[database-adapter] Falling back to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1473
|
+
`
|
|
1474
|
+
);
|
|
1475
|
+
}
|
|
1476
|
+
return fallbackClient.execute(stmt);
|
|
1477
|
+
};
|
|
1478
|
+
adapter = {
|
|
1479
|
+
async execute(stmt) {
|
|
1480
|
+
if (shouldBypassPostgres(stmt)) {
|
|
1481
|
+
return fallbackExecute(stmt);
|
|
1482
|
+
}
|
|
1483
|
+
try {
|
|
1484
|
+
return await executeOnPrisma(prisma, stmt);
|
|
1485
|
+
} catch (error) {
|
|
1486
|
+
if (shouldFallbackOnError(error)) {
|
|
1487
|
+
return fallbackExecute(stmt, error);
|
|
1488
|
+
}
|
|
1489
|
+
throw error;
|
|
1490
|
+
}
|
|
1491
|
+
},
|
|
1492
|
+
async batch(stmts, mode) {
|
|
1493
|
+
if (stmts.some((stmt) => shouldBypassPostgres(stmt))) {
|
|
1494
|
+
if (!fallbackClient) {
|
|
1495
|
+
throw new Error("Cannot batch unsupported SQLite-only statements without a fallback client.");
|
|
1496
|
+
}
|
|
1497
|
+
return fallbackClient.batch(stmts, mode);
|
|
1498
|
+
}
|
|
1499
|
+
try {
|
|
1500
|
+
if (prisma.$transaction) {
|
|
1501
|
+
return await prisma.$transaction(async (tx) => {
|
|
1502
|
+
const results2 = [];
|
|
1503
|
+
for (const stmt of stmts) {
|
|
1504
|
+
results2.push(await executeOnPrisma(tx, stmt));
|
|
1505
|
+
}
|
|
1506
|
+
return results2;
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
const results = [];
|
|
1510
|
+
for (const stmt of stmts) {
|
|
1511
|
+
results.push(await executeOnPrisma(prisma, stmt));
|
|
1512
|
+
}
|
|
1513
|
+
return results;
|
|
1514
|
+
} catch (error) {
|
|
1515
|
+
if (fallbackClient && shouldFallbackOnError(error)) {
|
|
1516
|
+
process.stderr.write(
|
|
1517
|
+
`[database-adapter] Falling back batch to SQLite: ${error instanceof Error ? error.message : String(error)}
|
|
1518
|
+
`
|
|
1519
|
+
);
|
|
1520
|
+
return fallbackClient.batch(stmts, mode);
|
|
1521
|
+
}
|
|
1522
|
+
throw error;
|
|
1523
|
+
}
|
|
1524
|
+
},
|
|
1525
|
+
async migrate(stmts) {
|
|
1526
|
+
if (fallbackClient) {
|
|
1527
|
+
return fallbackClient.migrate(stmts);
|
|
1528
|
+
}
|
|
1529
|
+
return adapter.batch(stmts, "deferred");
|
|
1530
|
+
},
|
|
1531
|
+
async transaction(mode) {
|
|
1532
|
+
if (!fallbackClient) {
|
|
1533
|
+
throw new Error("Interactive transactions are only supported on the SQLite fallback client.");
|
|
1534
|
+
}
|
|
1535
|
+
return fallbackClient.transaction(mode);
|
|
1536
|
+
},
|
|
1537
|
+
async executeMultiple(sql) {
|
|
1538
|
+
if (fallbackClient && shouldBypassPostgres(sql)) {
|
|
1539
|
+
return fallbackClient.executeMultiple(sql);
|
|
1540
|
+
}
|
|
1541
|
+
for (const statement of splitSqlStatements(sql)) {
|
|
1542
|
+
await adapter.execute(statement);
|
|
1543
|
+
}
|
|
1544
|
+
},
|
|
1545
|
+
async sync() {
|
|
1546
|
+
if (fallbackClient) {
|
|
1547
|
+
return fallbackClient.sync();
|
|
1548
|
+
}
|
|
1549
|
+
return { frame_no: 0, frames_synced: 0 };
|
|
1550
|
+
},
|
|
1551
|
+
close() {
|
|
1552
|
+
closed = true;
|
|
1553
|
+
prismaClientPromise = null;
|
|
1554
|
+
compatibilityBootstrapPromise = null;
|
|
1555
|
+
void prisma.$disconnect?.();
|
|
1556
|
+
},
|
|
1557
|
+
get closed() {
|
|
1558
|
+
return closed;
|
|
1559
|
+
},
|
|
1560
|
+
get protocol() {
|
|
1561
|
+
return "prisma-postgres";
|
|
1562
|
+
}
|
|
1563
|
+
};
|
|
1564
|
+
return adapter;
|
|
1565
|
+
}
|
|
1566
|
+
var VIEW_MAPPINGS, UPSERT_KEYS, BOOLEAN_COLUMNS_BY_TABLE, BOOLEAN_COLUMN_NAMES, IMMEDIATE_FALLBACK_PATTERNS, prismaClientPromise, compatibilityBootstrapPromise;
|
|
1567
|
+
var init_database_adapter = __esm({
|
|
1568
|
+
"src/lib/database-adapter.ts"() {
|
|
1569
|
+
"use strict";
|
|
1570
|
+
VIEW_MAPPINGS = [
|
|
1571
|
+
{ view: "memories", source: "memory.memory_records" },
|
|
1572
|
+
{ view: "tasks", source: "memory.tasks" },
|
|
1573
|
+
{ view: "behaviors", source: "memory.behaviors" },
|
|
1574
|
+
{ view: "entities", source: "memory.entities" },
|
|
1575
|
+
{ view: "relationships", source: "memory.relationships" },
|
|
1576
|
+
{ view: "entity_memories", source: "memory.entity_memories" },
|
|
1577
|
+
{ view: "entity_aliases", source: "memory.entity_aliases" },
|
|
1578
|
+
{ view: "notifications", source: "memory.notifications" },
|
|
1579
|
+
{ view: "messages", source: "memory.messages" },
|
|
1580
|
+
{ view: "users", source: "wiki.users" },
|
|
1581
|
+
{ view: "workspaces", source: "wiki.workspaces" },
|
|
1582
|
+
{ view: "workspace_users", source: "wiki.workspace_users" },
|
|
1583
|
+
{ view: "documents", source: "wiki.workspace_documents" },
|
|
1584
|
+
{ view: "chats", source: "wiki.workspace_chats" }
|
|
1585
|
+
];
|
|
1586
|
+
UPSERT_KEYS = {
|
|
1587
|
+
memories: ["id"],
|
|
1588
|
+
tasks: ["id"],
|
|
1589
|
+
behaviors: ["id"],
|
|
1590
|
+
entities: ["id"],
|
|
1591
|
+
relationships: ["id"],
|
|
1592
|
+
entity_aliases: ["alias"],
|
|
1593
|
+
notifications: ["id"],
|
|
1594
|
+
messages: ["id"],
|
|
1595
|
+
users: ["id"],
|
|
1596
|
+
workspaces: ["id"],
|
|
1597
|
+
workspace_users: ["id"],
|
|
1598
|
+
documents: ["id"],
|
|
1599
|
+
chats: ["id"]
|
|
1600
|
+
};
|
|
1601
|
+
BOOLEAN_COLUMNS_BY_TABLE = {
|
|
1602
|
+
memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
|
|
1603
|
+
behaviors: /* @__PURE__ */ new Set(["active"]),
|
|
1604
|
+
notifications: /* @__PURE__ */ new Set(["read"]),
|
|
1605
|
+
users: /* @__PURE__ */ new Set(["has_personal_memory"])
|
|
1606
|
+
};
|
|
1607
|
+
BOOLEAN_COLUMN_NAMES = new Set(
|
|
1608
|
+
Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
|
|
1609
|
+
);
|
|
1610
|
+
IMMEDIATE_FALLBACK_PATTERNS = [
|
|
1611
|
+
/\bPRAGMA\b/i,
|
|
1612
|
+
/\bsqlite_master\b/i,
|
|
1613
|
+
/(?:^|[.\s])(?:memories|conversations|entities)_fts\b/i,
|
|
1614
|
+
/\bMATCH\b/i,
|
|
1615
|
+
/\bvector_distance_cos\s*\(/i,
|
|
1616
|
+
/\bjson_extract\s*\(/i,
|
|
1617
|
+
/\bjulianday\s*\(/i,
|
|
1618
|
+
/\bstrftime\s*\(/i,
|
|
1619
|
+
/\blast_insert_rowid\s*\(/i
|
|
1620
|
+
];
|
|
1621
|
+
prismaClientPromise = null;
|
|
1622
|
+
compatibilityBootstrapPromise = null;
|
|
838
1623
|
}
|
|
839
1624
|
});
|
|
840
1625
|
|
|
841
1626
|
// src/lib/database.ts
|
|
842
1627
|
import { createClient } from "@libsql/client";
|
|
843
1628
|
async function initDatabase(config2) {
|
|
1629
|
+
if (_walCheckpointTimer) {
|
|
1630
|
+
clearInterval(_walCheckpointTimer);
|
|
1631
|
+
_walCheckpointTimer = null;
|
|
1632
|
+
}
|
|
1633
|
+
if (_daemonClient) {
|
|
1634
|
+
_daemonClient.close();
|
|
1635
|
+
_daemonClient = null;
|
|
1636
|
+
}
|
|
1637
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
1638
|
+
_adapterClient.close();
|
|
1639
|
+
}
|
|
1640
|
+
_adapterClient = null;
|
|
844
1641
|
if (_client) {
|
|
845
1642
|
_client.close();
|
|
846
1643
|
_client = null;
|
|
@@ -854,6 +1651,7 @@ async function initDatabase(config2) {
|
|
|
854
1651
|
}
|
|
855
1652
|
_client = createClient(opts);
|
|
856
1653
|
_resilientClient = wrapWithRetry(_client);
|
|
1654
|
+
_adapterClient = _resilientClient;
|
|
857
1655
|
_client.execute("PRAGMA busy_timeout = 30000").catch(() => {
|
|
858
1656
|
});
|
|
859
1657
|
_client.execute("PRAGMA journal_mode = WAL").catch(() => {
|
|
@@ -864,11 +1662,17 @@ async function initDatabase(config2) {
|
|
|
864
1662
|
});
|
|
865
1663
|
}, 3e4);
|
|
866
1664
|
_walCheckpointTimer.unref();
|
|
1665
|
+
if (process.env.DATABASE_URL) {
|
|
1666
|
+
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
1667
|
+
}
|
|
867
1668
|
}
|
|
868
1669
|
function getClient() {
|
|
869
|
-
if (!
|
|
1670
|
+
if (!_adapterClient) {
|
|
870
1671
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
871
1672
|
}
|
|
1673
|
+
if (process.env.DATABASE_URL) {
|
|
1674
|
+
return _adapterClient;
|
|
1675
|
+
}
|
|
872
1676
|
if (process.env.EXE_IS_DAEMON === "1") {
|
|
873
1677
|
return _resilientClient;
|
|
874
1678
|
}
|
|
@@ -1161,6 +1965,7 @@ async function ensureSchema() {
|
|
|
1161
1965
|
project TEXT NOT NULL,
|
|
1162
1966
|
summary TEXT NOT NULL,
|
|
1163
1967
|
task_file TEXT,
|
|
1968
|
+
session_scope TEXT,
|
|
1164
1969
|
read INTEGER NOT NULL DEFAULT 0,
|
|
1165
1970
|
created_at TEXT NOT NULL
|
|
1166
1971
|
);
|
|
@@ -1169,7 +1974,7 @@ async function ensureSchema() {
|
|
|
1169
1974
|
ON notifications(read);
|
|
1170
1975
|
|
|
1171
1976
|
CREATE INDEX IF NOT EXISTS idx_notifications_agent
|
|
1172
|
-
ON notifications(agent_id);
|
|
1977
|
+
ON notifications(agent_id, session_scope);
|
|
1173
1978
|
|
|
1174
1979
|
CREATE INDEX IF NOT EXISTS idx_notifications_task_file
|
|
1175
1980
|
ON notifications(task_file);
|
|
@@ -1207,6 +2012,7 @@ async function ensureSchema() {
|
|
|
1207
2012
|
target_agent TEXT NOT NULL,
|
|
1208
2013
|
target_project TEXT,
|
|
1209
2014
|
target_device TEXT NOT NULL DEFAULT 'local',
|
|
2015
|
+
session_scope TEXT,
|
|
1210
2016
|
content TEXT NOT NULL,
|
|
1211
2017
|
priority TEXT DEFAULT 'normal',
|
|
1212
2018
|
status TEXT DEFAULT 'pending',
|
|
@@ -1220,10 +2026,31 @@ async function ensureSchema() {
|
|
|
1220
2026
|
);
|
|
1221
2027
|
|
|
1222
2028
|
CREATE INDEX IF NOT EXISTS idx_messages_target
|
|
1223
|
-
ON messages(target_agent, status);
|
|
2029
|
+
ON messages(target_agent, session_scope, status);
|
|
1224
2030
|
|
|
1225
2031
|
CREATE INDEX IF NOT EXISTS idx_messages_conversation_order
|
|
1226
|
-
ON messages(target_agent, from_agent, server_seq);
|
|
2032
|
+
ON messages(target_agent, session_scope, from_agent, server_seq);
|
|
2033
|
+
`);
|
|
2034
|
+
try {
|
|
2035
|
+
await client.execute({
|
|
2036
|
+
sql: `ALTER TABLE notifications ADD COLUMN session_scope TEXT`,
|
|
2037
|
+
args: []
|
|
2038
|
+
});
|
|
2039
|
+
} catch {
|
|
2040
|
+
}
|
|
2041
|
+
try {
|
|
2042
|
+
await client.execute({
|
|
2043
|
+
sql: `ALTER TABLE messages ADD COLUMN session_scope TEXT`,
|
|
2044
|
+
args: []
|
|
2045
|
+
});
|
|
2046
|
+
} catch {
|
|
2047
|
+
}
|
|
2048
|
+
await client.executeMultiple(`
|
|
2049
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_agent_scope_read
|
|
2050
|
+
ON notifications(agent_id, session_scope, read, created_at);
|
|
2051
|
+
|
|
2052
|
+
CREATE INDEX IF NOT EXISTS idx_messages_target_scope_status
|
|
2053
|
+
ON messages(target_agent, session_scope, status, created_at);
|
|
1227
2054
|
`);
|
|
1228
2055
|
try {
|
|
1229
2056
|
await client.execute({
|
|
@@ -1807,28 +2634,45 @@ async function ensureSchema() {
|
|
|
1807
2634
|
} catch {
|
|
1808
2635
|
}
|
|
1809
2636
|
}
|
|
2637
|
+
try {
|
|
2638
|
+
await client.execute({
|
|
2639
|
+
sql: `UPDATE tasks SET status = 'closed' WHERE status = 'done' AND result IS NOT NULL`,
|
|
2640
|
+
args: []
|
|
2641
|
+
});
|
|
2642
|
+
} catch {
|
|
2643
|
+
}
|
|
1810
2644
|
}
|
|
1811
2645
|
async function disposeDatabase() {
|
|
2646
|
+
if (_walCheckpointTimer) {
|
|
2647
|
+
clearInterval(_walCheckpointTimer);
|
|
2648
|
+
_walCheckpointTimer = null;
|
|
2649
|
+
}
|
|
1812
2650
|
if (_daemonClient) {
|
|
1813
2651
|
_daemonClient.close();
|
|
1814
2652
|
_daemonClient = null;
|
|
1815
2653
|
}
|
|
2654
|
+
if (_adapterClient && _adapterClient !== _resilientClient) {
|
|
2655
|
+
_adapterClient.close();
|
|
2656
|
+
}
|
|
2657
|
+
_adapterClient = null;
|
|
1816
2658
|
if (_client) {
|
|
1817
2659
|
_client.close();
|
|
1818
2660
|
_client = null;
|
|
1819
2661
|
_resilientClient = null;
|
|
1820
2662
|
}
|
|
1821
2663
|
}
|
|
1822
|
-
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, initTurso, disposeTurso;
|
|
2664
|
+
var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
|
|
1823
2665
|
var init_database = __esm({
|
|
1824
2666
|
"src/lib/database.ts"() {
|
|
1825
2667
|
"use strict";
|
|
1826
2668
|
init_db_retry();
|
|
1827
2669
|
init_employees();
|
|
2670
|
+
init_database_adapter();
|
|
1828
2671
|
_client = null;
|
|
1829
2672
|
_resilientClient = null;
|
|
1830
2673
|
_walCheckpointTimer = null;
|
|
1831
2674
|
_daemonClient = null;
|
|
2675
|
+
_adapterClient = null;
|
|
1832
2676
|
initTurso = initDatabase;
|
|
1833
2677
|
disposeTurso = disposeDatabase;
|
|
1834
2678
|
}
|
|
@@ -1843,13 +2687,50 @@ var init_memory = __esm({
|
|
|
1843
2687
|
}
|
|
1844
2688
|
});
|
|
1845
2689
|
|
|
2690
|
+
// src/lib/daemon-auth.ts
|
|
2691
|
+
import crypto from "crypto";
|
|
2692
|
+
import path5 from "path";
|
|
2693
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
2694
|
+
function normalizeToken(token) {
|
|
2695
|
+
if (!token) return null;
|
|
2696
|
+
const trimmed = token.trim();
|
|
2697
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2698
|
+
}
|
|
2699
|
+
function readDaemonToken() {
|
|
2700
|
+
try {
|
|
2701
|
+
if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
|
|
2702
|
+
return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
|
|
2703
|
+
} catch {
|
|
2704
|
+
return null;
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
function ensureDaemonToken(seed) {
|
|
2708
|
+
const existing = readDaemonToken();
|
|
2709
|
+
if (existing) return existing;
|
|
2710
|
+
const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
|
|
2711
|
+
ensurePrivateDirSync(EXE_AI_DIR);
|
|
2712
|
+
writeFileSync3(DAEMON_TOKEN_PATH, `${token}
|
|
2713
|
+
`, "utf8");
|
|
2714
|
+
enforcePrivateFileSync(DAEMON_TOKEN_PATH);
|
|
2715
|
+
return token;
|
|
2716
|
+
}
|
|
2717
|
+
var DAEMON_TOKEN_PATH;
|
|
2718
|
+
var init_daemon_auth = __esm({
|
|
2719
|
+
"src/lib/daemon-auth.ts"() {
|
|
2720
|
+
"use strict";
|
|
2721
|
+
init_config();
|
|
2722
|
+
init_secure_files();
|
|
2723
|
+
DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
|
|
2724
|
+
}
|
|
2725
|
+
});
|
|
2726
|
+
|
|
1846
2727
|
// src/lib/exe-daemon-client.ts
|
|
1847
2728
|
import net from "net";
|
|
1848
|
-
import
|
|
2729
|
+
import os4 from "os";
|
|
1849
2730
|
import { spawn } from "child_process";
|
|
1850
2731
|
import { randomUUID } from "crypto";
|
|
1851
|
-
import { existsSync as
|
|
1852
|
-
import
|
|
2732
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
|
|
2733
|
+
import path6 from "path";
|
|
1853
2734
|
import { fileURLToPath } from "url";
|
|
1854
2735
|
function handleData(chunk) {
|
|
1855
2736
|
_buffer += chunk.toString();
|
|
@@ -1877,9 +2758,9 @@ function handleData(chunk) {
|
|
|
1877
2758
|
}
|
|
1878
2759
|
}
|
|
1879
2760
|
function cleanupStaleFiles() {
|
|
1880
|
-
if (
|
|
2761
|
+
if (existsSync6(PID_PATH)) {
|
|
1881
2762
|
try {
|
|
1882
|
-
const pid = parseInt(
|
|
2763
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
1883
2764
|
if (pid > 0) {
|
|
1884
2765
|
try {
|
|
1885
2766
|
process.kill(pid, 0);
|
|
@@ -1900,17 +2781,17 @@ function cleanupStaleFiles() {
|
|
|
1900
2781
|
}
|
|
1901
2782
|
}
|
|
1902
2783
|
function findPackageRoot() {
|
|
1903
|
-
let dir =
|
|
1904
|
-
const { root } =
|
|
2784
|
+
let dir = path6.dirname(fileURLToPath(import.meta.url));
|
|
2785
|
+
const { root } = path6.parse(dir);
|
|
1905
2786
|
while (dir !== root) {
|
|
1906
|
-
if (
|
|
1907
|
-
dir =
|
|
2787
|
+
if (existsSync6(path6.join(dir, "package.json"))) return dir;
|
|
2788
|
+
dir = path6.dirname(dir);
|
|
1908
2789
|
}
|
|
1909
2790
|
return null;
|
|
1910
2791
|
}
|
|
1911
2792
|
function spawnDaemon() {
|
|
1912
|
-
const freeGB =
|
|
1913
|
-
const totalGB =
|
|
2793
|
+
const freeGB = os4.freemem() / (1024 * 1024 * 1024);
|
|
2794
|
+
const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
|
|
1914
2795
|
if (totalGB <= 8) {
|
|
1915
2796
|
process.stderr.write(
|
|
1916
2797
|
`[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
|
|
@@ -1930,16 +2811,17 @@ function spawnDaemon() {
|
|
|
1930
2811
|
process.stderr.write("[exed-client] WARN: cannot find package root\n");
|
|
1931
2812
|
return;
|
|
1932
2813
|
}
|
|
1933
|
-
const daemonPath =
|
|
1934
|
-
if (!
|
|
2814
|
+
const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
2815
|
+
if (!existsSync6(daemonPath)) {
|
|
1935
2816
|
process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
|
|
1936
2817
|
`);
|
|
1937
2818
|
return;
|
|
1938
2819
|
}
|
|
1939
2820
|
const resolvedPath = daemonPath;
|
|
2821
|
+
const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
|
|
1940
2822
|
process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
|
|
1941
2823
|
`);
|
|
1942
|
-
const logPath =
|
|
2824
|
+
const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
|
|
1943
2825
|
let stderrFd = "ignore";
|
|
1944
2826
|
try {
|
|
1945
2827
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1957,7 +2839,8 @@ function spawnDaemon() {
|
|
|
1957
2839
|
TMUX_PANE: void 0,
|
|
1958
2840
|
// Prevents resolveExeSession() from scoping to one session
|
|
1959
2841
|
EXE_DAEMON_SOCK: SOCKET_PATH,
|
|
1960
|
-
EXE_DAEMON_PID: PID_PATH
|
|
2842
|
+
EXE_DAEMON_PID: PID_PATH,
|
|
2843
|
+
[DAEMON_TOKEN_ENV]: daemonToken
|
|
1961
2844
|
}
|
|
1962
2845
|
});
|
|
1963
2846
|
child.unref();
|
|
@@ -2067,13 +2950,14 @@ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
|
2067
2950
|
return;
|
|
2068
2951
|
}
|
|
2069
2952
|
const id = randomUUID();
|
|
2953
|
+
const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
|
|
2070
2954
|
const timer = setTimeout(() => {
|
|
2071
2955
|
_pending.delete(id);
|
|
2072
2956
|
resolve({ error: "Request timeout" });
|
|
2073
2957
|
}, timeoutMs);
|
|
2074
2958
|
_pending.set(id, { resolve, timer });
|
|
2075
2959
|
try {
|
|
2076
|
-
_socket.write(JSON.stringify({ id, ...payload }) + "\n");
|
|
2960
|
+
_socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
|
|
2077
2961
|
} catch {
|
|
2078
2962
|
clearTimeout(timer);
|
|
2079
2963
|
_pending.delete(id);
|
|
@@ -2090,74 +2974,123 @@ async function pingDaemon() {
|
|
|
2090
2974
|
return null;
|
|
2091
2975
|
}
|
|
2092
2976
|
function killAndRespawnDaemon() {
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2977
|
+
if (!acquireSpawnLock()) {
|
|
2978
|
+
process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
|
|
2979
|
+
if (_socket) {
|
|
2980
|
+
_socket.destroy();
|
|
2981
|
+
_socket = null;
|
|
2982
|
+
}
|
|
2983
|
+
_connected = false;
|
|
2984
|
+
_buffer = "";
|
|
2985
|
+
return;
|
|
2986
|
+
}
|
|
2987
|
+
try {
|
|
2988
|
+
process.stderr.write("[exed-client] Killing daemon for restart...\n");
|
|
2989
|
+
if (existsSync6(PID_PATH)) {
|
|
2990
|
+
try {
|
|
2991
|
+
const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
|
|
2992
|
+
if (pid > 0) {
|
|
2993
|
+
try {
|
|
2994
|
+
process.kill(pid, "SIGKILL");
|
|
2995
|
+
} catch {
|
|
2996
|
+
}
|
|
2101
2997
|
}
|
|
2998
|
+
} catch {
|
|
2102
2999
|
}
|
|
3000
|
+
}
|
|
3001
|
+
if (_socket) {
|
|
3002
|
+
_socket.destroy();
|
|
3003
|
+
_socket = null;
|
|
3004
|
+
}
|
|
3005
|
+
_connected = false;
|
|
3006
|
+
_buffer = "";
|
|
3007
|
+
try {
|
|
3008
|
+
unlinkSync2(PID_PATH);
|
|
2103
3009
|
} catch {
|
|
2104
3010
|
}
|
|
3011
|
+
try {
|
|
3012
|
+
unlinkSync2(SOCKET_PATH);
|
|
3013
|
+
} catch {
|
|
3014
|
+
}
|
|
3015
|
+
spawnDaemon();
|
|
3016
|
+
} finally {
|
|
3017
|
+
releaseSpawnLock();
|
|
2105
3018
|
}
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
_socket = null;
|
|
2109
|
-
}
|
|
2110
|
-
_connected = false;
|
|
2111
|
-
_buffer = "";
|
|
3019
|
+
}
|
|
3020
|
+
function isDaemonTooYoung() {
|
|
2112
3021
|
try {
|
|
2113
|
-
|
|
3022
|
+
const stat = statSync(PID_PATH);
|
|
3023
|
+
return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
|
|
2114
3024
|
} catch {
|
|
3025
|
+
return false;
|
|
2115
3026
|
}
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
3027
|
+
}
|
|
3028
|
+
async function retryThenRestart(doRequest, label) {
|
|
3029
|
+
const result = await doRequest();
|
|
3030
|
+
if (!result.error) {
|
|
3031
|
+
_consecutiveFailures = 0;
|
|
3032
|
+
return result;
|
|
2119
3033
|
}
|
|
2120
|
-
|
|
3034
|
+
_consecutiveFailures++;
|
|
3035
|
+
for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
|
|
3036
|
+
const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
|
|
3037
|
+
process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
|
|
3038
|
+
`);
|
|
3039
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
3040
|
+
if (!_connected) {
|
|
3041
|
+
if (!await connectToSocket()) continue;
|
|
3042
|
+
}
|
|
3043
|
+
const retry = await doRequest();
|
|
3044
|
+
if (!retry.error) {
|
|
3045
|
+
_consecutiveFailures = 0;
|
|
3046
|
+
return retry;
|
|
3047
|
+
}
|
|
3048
|
+
_consecutiveFailures++;
|
|
3049
|
+
}
|
|
3050
|
+
if (isDaemonTooYoung()) {
|
|
3051
|
+
process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
|
|
3052
|
+
`);
|
|
3053
|
+
return { error: result.error };
|
|
3054
|
+
}
|
|
3055
|
+
process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
|
|
3056
|
+
`);
|
|
3057
|
+
killAndRespawnDaemon();
|
|
3058
|
+
const start = Date.now();
|
|
3059
|
+
let delay2 = 200;
|
|
3060
|
+
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
3061
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
3062
|
+
if (await connectToSocket()) break;
|
|
3063
|
+
delay2 = Math.min(delay2 * 2, 3e3);
|
|
3064
|
+
}
|
|
3065
|
+
if (!_connected) return { error: "Daemon restart failed" };
|
|
3066
|
+
const final = await doRequest();
|
|
3067
|
+
if (!final.error) _consecutiveFailures = 0;
|
|
3068
|
+
return final;
|
|
2121
3069
|
}
|
|
2122
3070
|
async function embedViaClient(text, priority = "high") {
|
|
2123
3071
|
if (!_connected && !await connectEmbedDaemon()) return null;
|
|
2124
3072
|
_requestCount++;
|
|
2125
3073
|
if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
|
|
2126
3074
|
const health = await pingDaemon();
|
|
2127
|
-
if (!health) {
|
|
3075
|
+
if (!health && !isDaemonTooYoung()) {
|
|
2128
3076
|
process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
|
|
2129
3077
|
`);
|
|
2130
3078
|
killAndRespawnDaemon();
|
|
2131
3079
|
const start = Date.now();
|
|
2132
|
-
let
|
|
3080
|
+
let d = 200;
|
|
2133
3081
|
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2134
|
-
await new Promise((r) => setTimeout(r,
|
|
3082
|
+
await new Promise((r) => setTimeout(r, d));
|
|
2135
3083
|
if (await connectToSocket()) break;
|
|
2136
|
-
|
|
2137
|
-
}
|
|
2138
|
-
if (!_connected) return null;
|
|
2139
|
-
}
|
|
2140
|
-
}
|
|
2141
|
-
const result = await sendRequest([text], priority);
|
|
2142
|
-
if (!result.error && result.vectors?.[0]) return result.vectors[0];
|
|
2143
|
-
if (result.error) {
|
|
2144
|
-
process.stderr.write(`[exed-client] Embed failed (${result.error}) \u2014 attempting restart
|
|
2145
|
-
`);
|
|
2146
|
-
killAndRespawnDaemon();
|
|
2147
|
-
const start = Date.now();
|
|
2148
|
-
let delay2 = 200;
|
|
2149
|
-
while (Date.now() - start < CONNECT_TIMEOUT_MS) {
|
|
2150
|
-
await new Promise((r) => setTimeout(r, delay2));
|
|
2151
|
-
if (await connectToSocket()) break;
|
|
2152
|
-
delay2 = Math.min(delay2 * 2, 3e3);
|
|
2153
|
-
}
|
|
2154
|
-
if (!_connected) return null;
|
|
2155
|
-
const retry = await sendRequest([text], priority);
|
|
2156
|
-
if (!retry.error && retry.vectors?.[0]) return retry.vectors[0];
|
|
2157
|
-
process.stderr.write(`[exed-client] Embed retry also failed: ${retry.error ?? "no vector"}
|
|
2158
|
-
`);
|
|
3084
|
+
d = Math.min(d * 2, 3e3);
|
|
3085
|
+
}
|
|
3086
|
+
if (!_connected) return null;
|
|
3087
|
+
}
|
|
2159
3088
|
}
|
|
2160
|
-
|
|
3089
|
+
const result = await retryThenRestart(
|
|
3090
|
+
() => sendRequest([text], priority),
|
|
3091
|
+
"Embed"
|
|
3092
|
+
);
|
|
3093
|
+
return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
|
|
2161
3094
|
}
|
|
2162
3095
|
function disconnectClient() {
|
|
2163
3096
|
if (_socket) {
|
|
@@ -2172,22 +3105,28 @@ function disconnectClient() {
|
|
|
2172
3105
|
entry.resolve({ error: "Client disconnected" });
|
|
2173
3106
|
}
|
|
2174
3107
|
}
|
|
2175
|
-
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
|
|
3108
|
+
var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
|
|
2176
3109
|
var init_exe_daemon_client = __esm({
|
|
2177
3110
|
"src/lib/exe-daemon-client.ts"() {
|
|
2178
3111
|
"use strict";
|
|
2179
3112
|
init_config();
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
3113
|
+
init_daemon_auth();
|
|
3114
|
+
SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
|
|
3115
|
+
PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
|
|
3116
|
+
SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
|
|
2183
3117
|
SPAWN_LOCK_STALE_MS = 3e4;
|
|
2184
3118
|
CONNECT_TIMEOUT_MS = 15e3;
|
|
2185
3119
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
3120
|
+
DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
|
|
2186
3121
|
_socket = null;
|
|
2187
3122
|
_connected = false;
|
|
2188
3123
|
_buffer = "";
|
|
2189
3124
|
_requestCount = 0;
|
|
3125
|
+
_consecutiveFailures = 0;
|
|
2190
3126
|
HEALTH_CHECK_INTERVAL = 100;
|
|
3127
|
+
MAX_RETRIES_BEFORE_RESTART = 3;
|
|
3128
|
+
RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
|
|
3129
|
+
MIN_DAEMON_AGE_MS = 3e4;
|
|
2191
3130
|
_pending = /* @__PURE__ */ new Map();
|
|
2192
3131
|
MAX_BUFFER = 1e7;
|
|
2193
3132
|
}
|
|
@@ -2230,10 +3169,10 @@ async function disposeEmbedder() {
|
|
|
2230
3169
|
async function embedDirect(text) {
|
|
2231
3170
|
const llamaCpp = await import("node-llama-cpp");
|
|
2232
3171
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
2233
|
-
const { existsSync:
|
|
2234
|
-
const
|
|
2235
|
-
const modelPath =
|
|
2236
|
-
if (!
|
|
3172
|
+
const { existsSync: existsSync18 } = await import("fs");
|
|
3173
|
+
const path22 = await import("path");
|
|
3174
|
+
const modelPath = path22.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
3175
|
+
if (!existsSync18(modelPath)) {
|
|
2237
3176
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
2238
3177
|
}
|
|
2239
3178
|
const llama = await llamaCpp.getLlama();
|
|
@@ -2263,14 +3202,14 @@ var init_embedder = __esm({
|
|
|
2263
3202
|
|
|
2264
3203
|
// src/lib/keychain.ts
|
|
2265
3204
|
import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
|
|
2266
|
-
import { existsSync as
|
|
2267
|
-
import
|
|
2268
|
-
import
|
|
3205
|
+
import { existsSync as existsSync7 } from "fs";
|
|
3206
|
+
import path7 from "path";
|
|
3207
|
+
import os5 from "os";
|
|
2269
3208
|
function getKeyDir() {
|
|
2270
|
-
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ??
|
|
3209
|
+
return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
|
|
2271
3210
|
}
|
|
2272
3211
|
function getKeyPath() {
|
|
2273
|
-
return
|
|
3212
|
+
return path7.join(getKeyDir(), "master.key");
|
|
2274
3213
|
}
|
|
2275
3214
|
async function tryKeytar() {
|
|
2276
3215
|
try {
|
|
@@ -2291,9 +3230,9 @@ async function getMasterKey() {
|
|
|
2291
3230
|
}
|
|
2292
3231
|
}
|
|
2293
3232
|
const keyPath = getKeyPath();
|
|
2294
|
-
if (!
|
|
3233
|
+
if (!existsSync7(keyPath)) {
|
|
2295
3234
|
process.stderr.write(
|
|
2296
|
-
`[keychain] Key not found at ${keyPath} (HOME=${
|
|
3235
|
+
`[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
|
|
2297
3236
|
`
|
|
2298
3237
|
);
|
|
2299
3238
|
return null;
|
|
@@ -2323,6 +3262,7 @@ var shard_manager_exports = {};
|
|
|
2323
3262
|
__export(shard_manager_exports, {
|
|
2324
3263
|
disposeShards: () => disposeShards,
|
|
2325
3264
|
ensureShardSchema: () => ensureShardSchema,
|
|
3265
|
+
getOpenShardCount: () => getOpenShardCount,
|
|
2326
3266
|
getReadyShardClient: () => getReadyShardClient,
|
|
2327
3267
|
getShardClient: () => getShardClient,
|
|
2328
3268
|
getShardsDir: () => getShardsDir,
|
|
@@ -2331,15 +3271,18 @@ __export(shard_manager_exports, {
|
|
|
2331
3271
|
listShards: () => listShards,
|
|
2332
3272
|
shardExists: () => shardExists
|
|
2333
3273
|
});
|
|
2334
|
-
import
|
|
2335
|
-
import { existsSync as
|
|
3274
|
+
import path8 from "path";
|
|
3275
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
2336
3276
|
import { createClient as createClient2 } from "@libsql/client";
|
|
2337
3277
|
function initShardManager(encryptionKey) {
|
|
2338
3278
|
_encryptionKey = encryptionKey;
|
|
2339
|
-
if (!
|
|
2340
|
-
|
|
3279
|
+
if (!existsSync8(SHARDS_DIR)) {
|
|
3280
|
+
mkdirSync2(SHARDS_DIR, { recursive: true });
|
|
2341
3281
|
}
|
|
2342
3282
|
_shardingEnabled = true;
|
|
3283
|
+
if (_evictionTimer) clearInterval(_evictionTimer);
|
|
3284
|
+
_evictionTimer = setInterval(evictIdleShards, EVICTION_INTERVAL_MS);
|
|
3285
|
+
_evictionTimer.unref();
|
|
2343
3286
|
}
|
|
2344
3287
|
function isShardingEnabled() {
|
|
2345
3288
|
return _shardingEnabled;
|
|
@@ -2356,21 +3299,28 @@ function getShardClient(projectName) {
|
|
|
2356
3299
|
throw new Error(`Invalid project name for shard: "${projectName}"`);
|
|
2357
3300
|
}
|
|
2358
3301
|
const cached = _shards.get(safeName);
|
|
2359
|
-
if (cached)
|
|
2360
|
-
|
|
3302
|
+
if (cached) {
|
|
3303
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
3304
|
+
return cached;
|
|
3305
|
+
}
|
|
3306
|
+
while (_shards.size >= MAX_OPEN_SHARDS) {
|
|
3307
|
+
evictLRU();
|
|
3308
|
+
}
|
|
3309
|
+
const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
|
|
2361
3310
|
const client = createClient2({
|
|
2362
3311
|
url: `file:${dbPath}`,
|
|
2363
3312
|
encryptionKey: _encryptionKey
|
|
2364
3313
|
});
|
|
2365
3314
|
_shards.set(safeName, client);
|
|
3315
|
+
_shardLastAccess.set(safeName, Date.now());
|
|
2366
3316
|
return client;
|
|
2367
3317
|
}
|
|
2368
3318
|
function shardExists(projectName) {
|
|
2369
3319
|
const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2370
|
-
return
|
|
3320
|
+
return existsSync8(path8.join(SHARDS_DIR, `${safeName}.db`));
|
|
2371
3321
|
}
|
|
2372
3322
|
function listShards() {
|
|
2373
|
-
if (!
|
|
3323
|
+
if (!existsSync8(SHARDS_DIR)) return [];
|
|
2374
3324
|
return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
|
|
2375
3325
|
}
|
|
2376
3326
|
async function ensureShardSchema(client) {
|
|
@@ -2422,6 +3372,8 @@ async function ensureShardSchema(client) {
|
|
|
2422
3372
|
for (const col of [
|
|
2423
3373
|
"ALTER TABLE memories ADD COLUMN task_id TEXT",
|
|
2424
3374
|
"ALTER TABLE memories ADD COLUMN consolidated INTEGER NOT NULL DEFAULT 0",
|
|
3375
|
+
"ALTER TABLE memories ADD COLUMN author_device_id TEXT",
|
|
3376
|
+
"ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'business'",
|
|
2425
3377
|
"ALTER TABLE memories ADD COLUMN importance INTEGER DEFAULT 5",
|
|
2426
3378
|
"ALTER TABLE memories ADD COLUMN status TEXT DEFAULT 'active'",
|
|
2427
3379
|
"ALTER TABLE memories ADD COLUMN wiki_synced INTEGER DEFAULT 0",
|
|
@@ -2444,7 +3396,23 @@ async function ensureShardSchema(client) {
|
|
|
2444
3396
|
// MS-11: draft staging, MS-6a: memory_type, MS-7: trajectory
|
|
2445
3397
|
"ALTER TABLE memories ADD COLUMN draft INTEGER DEFAULT 0",
|
|
2446
3398
|
"ALTER TABLE memories ADD COLUMN memory_type TEXT DEFAULT 'raw'",
|
|
2447
|
-
"ALTER TABLE memories ADD COLUMN trajectory TEXT"
|
|
3399
|
+
"ALTER TABLE memories ADD COLUMN trajectory TEXT",
|
|
3400
|
+
// Metadata enrichment columns (must match database.ts)
|
|
3401
|
+
"ALTER TABLE memories ADD COLUMN intent TEXT",
|
|
3402
|
+
"ALTER TABLE memories ADD COLUMN outcome TEXT",
|
|
3403
|
+
"ALTER TABLE memories ADD COLUMN domain TEXT",
|
|
3404
|
+
"ALTER TABLE memories ADD COLUMN referenced_entities TEXT",
|
|
3405
|
+
"ALTER TABLE memories ADD COLUMN retrieval_count INTEGER DEFAULT 0",
|
|
3406
|
+
"ALTER TABLE memories ADD COLUMN chain_position TEXT",
|
|
3407
|
+
"ALTER TABLE memories ADD COLUMN review_status TEXT",
|
|
3408
|
+
"ALTER TABLE memories ADD COLUMN context_window_pct INTEGER",
|
|
3409
|
+
"ALTER TABLE memories ADD COLUMN file_paths TEXT",
|
|
3410
|
+
"ALTER TABLE memories ADD COLUMN commit_hash TEXT",
|
|
3411
|
+
"ALTER TABLE memories ADD COLUMN duration_ms INTEGER",
|
|
3412
|
+
"ALTER TABLE memories ADD COLUMN token_cost REAL",
|
|
3413
|
+
"ALTER TABLE memories ADD COLUMN audience TEXT",
|
|
3414
|
+
"ALTER TABLE memories ADD COLUMN language_type TEXT",
|
|
3415
|
+
"ALTER TABLE memories ADD COLUMN parent_memory_id TEXT"
|
|
2448
3416
|
]) {
|
|
2449
3417
|
try {
|
|
2450
3418
|
await client.execute(col);
|
|
@@ -2543,21 +3511,69 @@ async function getReadyShardClient(projectName) {
|
|
|
2543
3511
|
await ensureShardSchema(client);
|
|
2544
3512
|
return client;
|
|
2545
3513
|
}
|
|
3514
|
+
function evictLRU() {
|
|
3515
|
+
let oldest = null;
|
|
3516
|
+
let oldestTime = Infinity;
|
|
3517
|
+
for (const [name, time] of _shardLastAccess) {
|
|
3518
|
+
if (time < oldestTime) {
|
|
3519
|
+
oldestTime = time;
|
|
3520
|
+
oldest = name;
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
if (oldest) {
|
|
3524
|
+
const client = _shards.get(oldest);
|
|
3525
|
+
if (client) {
|
|
3526
|
+
client.close();
|
|
3527
|
+
}
|
|
3528
|
+
_shards.delete(oldest);
|
|
3529
|
+
_shardLastAccess.delete(oldest);
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
function evictIdleShards() {
|
|
3533
|
+
const now = Date.now();
|
|
3534
|
+
const toEvict = [];
|
|
3535
|
+
for (const [name, lastAccess] of _shardLastAccess) {
|
|
3536
|
+
if (now - lastAccess > SHARD_IDLE_MS) {
|
|
3537
|
+
toEvict.push(name);
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
for (const name of toEvict) {
|
|
3541
|
+
const client = _shards.get(name);
|
|
3542
|
+
if (client) {
|
|
3543
|
+
client.close();
|
|
3544
|
+
}
|
|
3545
|
+
_shards.delete(name);
|
|
3546
|
+
_shardLastAccess.delete(name);
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
function getOpenShardCount() {
|
|
3550
|
+
return _shards.size;
|
|
3551
|
+
}
|
|
2546
3552
|
function disposeShards() {
|
|
3553
|
+
if (_evictionTimer) {
|
|
3554
|
+
clearInterval(_evictionTimer);
|
|
3555
|
+
_evictionTimer = null;
|
|
3556
|
+
}
|
|
2547
3557
|
for (const [, client] of _shards) {
|
|
2548
3558
|
client.close();
|
|
2549
3559
|
}
|
|
2550
3560
|
_shards.clear();
|
|
3561
|
+
_shardLastAccess.clear();
|
|
2551
3562
|
_shardingEnabled = false;
|
|
2552
3563
|
_encryptionKey = null;
|
|
2553
3564
|
}
|
|
2554
|
-
var SHARDS_DIR, _shards, _encryptionKey, _shardingEnabled;
|
|
3565
|
+
var SHARDS_DIR, SHARD_IDLE_MS, MAX_OPEN_SHARDS, EVICTION_INTERVAL_MS, _shards, _shardLastAccess, _evictionTimer, _encryptionKey, _shardingEnabled;
|
|
2555
3566
|
var init_shard_manager = __esm({
|
|
2556
3567
|
"src/lib/shard-manager.ts"() {
|
|
2557
3568
|
"use strict";
|
|
2558
3569
|
init_config();
|
|
2559
|
-
SHARDS_DIR =
|
|
3570
|
+
SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
|
|
3571
|
+
SHARD_IDLE_MS = 5 * 60 * 1e3;
|
|
3572
|
+
MAX_OPEN_SHARDS = 10;
|
|
3573
|
+
EVICTION_INTERVAL_MS = 60 * 1e3;
|
|
2560
3574
|
_shards = /* @__PURE__ */ new Map();
|
|
3575
|
+
_shardLastAccess = /* @__PURE__ */ new Map();
|
|
3576
|
+
_evictionTimer = null;
|
|
2561
3577
|
_encryptionKey = null;
|
|
2562
3578
|
_shardingEnabled = false;
|
|
2563
3579
|
}
|
|
@@ -3329,8 +4345,8 @@ __export(wiki_client_exports, {
|
|
|
3329
4345
|
listDocuments: () => listDocuments,
|
|
3330
4346
|
listWorkspaces: () => listWorkspaces
|
|
3331
4347
|
});
|
|
3332
|
-
async function wikiFetch(config2,
|
|
3333
|
-
const url = `${config2.baseUrl}/api/v1${
|
|
4348
|
+
async function wikiFetch(config2, path22, method = "GET", body) {
|
|
4349
|
+
const url = `${config2.baseUrl}/api/v1${path22}`;
|
|
3334
4350
|
const headers = {
|
|
3335
4351
|
Authorization: `Bearer ${config2.apiKey}`,
|
|
3336
4352
|
"Content-Type": "application/json"
|
|
@@ -3363,7 +4379,7 @@ async function wikiFetch(config2, path20, method = "GET", body) {
|
|
|
3363
4379
|
}
|
|
3364
4380
|
}
|
|
3365
4381
|
if (!response.ok) {
|
|
3366
|
-
throw new Error(`Wiki API ${method} ${
|
|
4382
|
+
throw new Error(`Wiki API ${method} ${path22}: ${response.status} ${response.statusText}`);
|
|
3367
4383
|
}
|
|
3368
4384
|
return response.json();
|
|
3369
4385
|
} finally {
|
|
@@ -3656,13 +4672,13 @@ __export(graph_rag_exports, {
|
|
|
3656
4672
|
resolveAlias: () => resolveAlias,
|
|
3657
4673
|
storeExtraction: () => storeExtraction
|
|
3658
4674
|
});
|
|
3659
|
-
import
|
|
4675
|
+
import crypto2 from "crypto";
|
|
3660
4676
|
function normalizeEntityName(name) {
|
|
3661
4677
|
return name.replace(/\s*\([^)]*\)\s*/g, "").trim().toLowerCase();
|
|
3662
4678
|
}
|
|
3663
4679
|
function entityId(name, type) {
|
|
3664
4680
|
const normalized = normalizeEntityName(name);
|
|
3665
|
-
return
|
|
4681
|
+
return crypto2.createHash("sha256").update(`${normalized}::${type.toLowerCase()}`).digest("hex").slice(0, 16);
|
|
3666
4682
|
}
|
|
3667
4683
|
async function resolveAlias(client, name) {
|
|
3668
4684
|
const normalized = normalizeEntityName(name);
|
|
@@ -3912,7 +4928,7 @@ async function storeExtraction(client, extraction, memoryId, timestamp) {
|
|
|
3912
4928
|
const targetAlias = await resolveAlias(client, r.target);
|
|
3913
4929
|
const sourceId = sourceAlias ?? entityId(r.source, r.sourceType);
|
|
3914
4930
|
const targetId = targetAlias ?? entityId(r.target, r.targetType);
|
|
3915
|
-
const relId =
|
|
4931
|
+
const relId = crypto2.randomUUID().slice(0, 16);
|
|
3916
4932
|
try {
|
|
3917
4933
|
await client.execute({
|
|
3918
4934
|
sql: `INSERT OR IGNORE INTO entities (id, name, type, first_seen, last_seen)
|
|
@@ -3975,7 +4991,7 @@ async function storeExtraction(client, extraction, memoryId, timestamp) {
|
|
|
3975
4991
|
}
|
|
3976
4992
|
}
|
|
3977
4993
|
for (const h of extraction.hyperedges) {
|
|
3978
|
-
const hId =
|
|
4994
|
+
const hId = crypto2.randomUUID().slice(0, 16);
|
|
3979
4995
|
try {
|
|
3980
4996
|
await client.execute({
|
|
3981
4997
|
sql: `INSERT OR IGNORE INTO hyperedges (id, label, relation, confidence, timestamp)
|
|
@@ -4039,7 +5055,7 @@ async function extractBatch(client, batchSize = 50, model = "claude-haiku-4-5-20
|
|
|
4039
5055
|
totalEntities += stored.entitiesStored;
|
|
4040
5056
|
totalRelationships += stored.relationshipsStored;
|
|
4041
5057
|
}
|
|
4042
|
-
const contentHash =
|
|
5058
|
+
const contentHash = crypto2.createHash("sha256").update(rawContent).digest("hex").slice(0, 32);
|
|
4043
5059
|
await client.execute({
|
|
4044
5060
|
sql: "UPDATE memories SET graph_extracted = 1, content_hash = ?, graph_extracted_hash = ? WHERE id = ?",
|
|
4045
5061
|
args: [contentHash, contentHash, memoryId]
|
|
@@ -4384,13 +5400,13 @@ __export(whatsapp_accounts_exports, {
|
|
|
4384
5400
|
getDefaultAccount: () => getDefaultAccount,
|
|
4385
5401
|
loadAccounts: () => loadAccounts
|
|
4386
5402
|
});
|
|
4387
|
-
import { readFileSync as
|
|
5403
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
4388
5404
|
import { join as join2 } from "path";
|
|
4389
5405
|
import { homedir as homedir2 } from "os";
|
|
4390
5406
|
function loadAccounts() {
|
|
4391
5407
|
if (cachedAccounts !== null) return cachedAccounts;
|
|
4392
5408
|
try {
|
|
4393
|
-
const raw =
|
|
5409
|
+
const raw = readFileSync6(CONFIG_PATH2, "utf8");
|
|
4394
5410
|
const parsed = JSON.parse(raw);
|
|
4395
5411
|
if (!Array.isArray(parsed)) {
|
|
4396
5412
|
console.warn("[whatsapp] Config is not an array, ignoring");
|
|
@@ -4430,13 +5446,13 @@ var init_whatsapp_accounts = __esm({
|
|
|
4430
5446
|
});
|
|
4431
5447
|
|
|
4432
5448
|
// src/lib/session-registry.ts
|
|
4433
|
-
import { readFileSync as
|
|
4434
|
-
import
|
|
4435
|
-
import
|
|
5449
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync9 } from "fs";
|
|
5450
|
+
import path10 from "path";
|
|
5451
|
+
import os7 from "os";
|
|
4436
5452
|
function registerSession(entry) {
|
|
4437
|
-
const dir =
|
|
4438
|
-
if (!
|
|
4439
|
-
|
|
5453
|
+
const dir = path10.dirname(REGISTRY_PATH);
|
|
5454
|
+
if (!existsSync9(dir)) {
|
|
5455
|
+
mkdirSync4(dir, { recursive: true });
|
|
4440
5456
|
}
|
|
4441
5457
|
const sessions = listSessions();
|
|
4442
5458
|
const idx = sessions.findIndex((s) => s.windowName === entry.windowName);
|
|
@@ -4445,11 +5461,11 @@ function registerSession(entry) {
|
|
|
4445
5461
|
} else {
|
|
4446
5462
|
sessions.push(entry);
|
|
4447
5463
|
}
|
|
4448
|
-
|
|
5464
|
+
writeFileSync4(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
|
|
4449
5465
|
}
|
|
4450
5466
|
function listSessions() {
|
|
4451
5467
|
try {
|
|
4452
|
-
const raw =
|
|
5468
|
+
const raw = readFileSync7(REGISTRY_PATH, "utf8");
|
|
4453
5469
|
return JSON.parse(raw);
|
|
4454
5470
|
} catch {
|
|
4455
5471
|
return [];
|
|
@@ -4459,7 +5475,7 @@ var REGISTRY_PATH;
|
|
|
4459
5475
|
var init_session_registry = __esm({
|
|
4460
5476
|
"src/lib/session-registry.ts"() {
|
|
4461
5477
|
"use strict";
|
|
4462
|
-
REGISTRY_PATH =
|
|
5478
|
+
REGISTRY_PATH = path10.join(os7.homedir(), ".exe-os", "session-registry.json");
|
|
4463
5479
|
}
|
|
4464
5480
|
});
|
|
4465
5481
|
|
|
@@ -4711,67 +5727,6 @@ var init_provider_table = __esm({
|
|
|
4711
5727
|
}
|
|
4712
5728
|
});
|
|
4713
5729
|
|
|
4714
|
-
// src/lib/runtime-table.ts
|
|
4715
|
-
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
4716
|
-
var init_runtime_table = __esm({
|
|
4717
|
-
"src/lib/runtime-table.ts"() {
|
|
4718
|
-
"use strict";
|
|
4719
|
-
RUNTIME_TABLE = {
|
|
4720
|
-
codex: {
|
|
4721
|
-
binary: "codex",
|
|
4722
|
-
launchMode: "interactive",
|
|
4723
|
-
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
4724
|
-
inlineFlag: "--no-alt-screen",
|
|
4725
|
-
apiKeyEnv: "OPENAI_API_KEY",
|
|
4726
|
-
defaultModel: "gpt-5.4"
|
|
4727
|
-
},
|
|
4728
|
-
opencode: {
|
|
4729
|
-
binary: "opencode",
|
|
4730
|
-
launchMode: "exec",
|
|
4731
|
-
autoApproveFlag: "--dangerously-skip-permissions",
|
|
4732
|
-
inlineFlag: "",
|
|
4733
|
-
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
4734
|
-
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
4735
|
-
}
|
|
4736
|
-
};
|
|
4737
|
-
DEFAULT_RUNTIME = "claude";
|
|
4738
|
-
}
|
|
4739
|
-
});
|
|
4740
|
-
|
|
4741
|
-
// src/lib/agent-config.ts
|
|
4742
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
4743
|
-
import path8 from "path";
|
|
4744
|
-
function loadAgentConfig() {
|
|
4745
|
-
if (!existsSync7(AGENT_CONFIG_PATH)) return {};
|
|
4746
|
-
try {
|
|
4747
|
-
return JSON.parse(readFileSync6(AGENT_CONFIG_PATH, "utf-8"));
|
|
4748
|
-
} catch {
|
|
4749
|
-
return {};
|
|
4750
|
-
}
|
|
4751
|
-
}
|
|
4752
|
-
function getAgentRuntime(agentId) {
|
|
4753
|
-
const config2 = loadAgentConfig();
|
|
4754
|
-
const entry = config2[agentId];
|
|
4755
|
-
if (entry) return entry;
|
|
4756
|
-
const orgDefault = config2["default"];
|
|
4757
|
-
if (orgDefault) return orgDefault;
|
|
4758
|
-
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
4759
|
-
}
|
|
4760
|
-
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
4761
|
-
var init_agent_config = __esm({
|
|
4762
|
-
"src/lib/agent-config.ts"() {
|
|
4763
|
-
"use strict";
|
|
4764
|
-
init_config();
|
|
4765
|
-
init_runtime_table();
|
|
4766
|
-
AGENT_CONFIG_PATH = path8.join(EXE_AI_DIR, "agent-config.json");
|
|
4767
|
-
DEFAULT_MODELS = {
|
|
4768
|
-
claude: "claude-opus-4",
|
|
4769
|
-
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
4770
|
-
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
4771
|
-
};
|
|
4772
|
-
}
|
|
4773
|
-
});
|
|
4774
|
-
|
|
4775
5730
|
// src/lib/intercom-queue.ts
|
|
4776
5731
|
var intercom_queue_exports = {};
|
|
4777
5732
|
__export(intercom_queue_exports, {
|
|
@@ -4781,17 +5736,17 @@ __export(intercom_queue_exports, {
|
|
|
4781
5736
|
queueIntercom: () => queueIntercom,
|
|
4782
5737
|
readQueue: () => readQueue
|
|
4783
5738
|
});
|
|
4784
|
-
import { readFileSync as
|
|
4785
|
-
import
|
|
4786
|
-
import
|
|
5739
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync10, mkdirSync as mkdirSync5 } from "fs";
|
|
5740
|
+
import path11 from "path";
|
|
5741
|
+
import os8 from "os";
|
|
4787
5742
|
function ensureDir() {
|
|
4788
|
-
const dir =
|
|
4789
|
-
if (!
|
|
5743
|
+
const dir = path11.dirname(QUEUE_PATH);
|
|
5744
|
+
if (!existsSync10(dir)) mkdirSync5(dir, { recursive: true });
|
|
4790
5745
|
}
|
|
4791
5746
|
function readQueue() {
|
|
4792
5747
|
try {
|
|
4793
|
-
if (!
|
|
4794
|
-
return JSON.parse(
|
|
5748
|
+
if (!existsSync10(QUEUE_PATH)) return [];
|
|
5749
|
+
return JSON.parse(readFileSync8(QUEUE_PATH, "utf8"));
|
|
4795
5750
|
} catch {
|
|
4796
5751
|
return [];
|
|
4797
5752
|
}
|
|
@@ -4799,7 +5754,7 @@ function readQueue() {
|
|
|
4799
5754
|
function writeQueue(queue) {
|
|
4800
5755
|
ensureDir();
|
|
4801
5756
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
4802
|
-
|
|
5757
|
+
writeFileSync5(tmp, JSON.stringify(queue, null, 2));
|
|
4803
5758
|
renameSync3(tmp, QUEUE_PATH);
|
|
4804
5759
|
}
|
|
4805
5760
|
function queueIntercom(targetSession, reason) {
|
|
@@ -4891,26 +5846,29 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
|
|
|
4891
5846
|
var init_intercom_queue = __esm({
|
|
4892
5847
|
"src/lib/intercom-queue.ts"() {
|
|
4893
5848
|
"use strict";
|
|
4894
|
-
QUEUE_PATH =
|
|
5849
|
+
QUEUE_PATH = path11.join(os8.homedir(), ".exe-os", "intercom-queue.json");
|
|
4895
5850
|
MAX_RETRIES2 = 5;
|
|
4896
5851
|
TTL_MS = 60 * 60 * 1e3;
|
|
4897
|
-
INTERCOM_LOG =
|
|
5852
|
+
INTERCOM_LOG = path11.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
4898
5853
|
}
|
|
4899
5854
|
});
|
|
4900
5855
|
|
|
4901
5856
|
// src/lib/license.ts
|
|
4902
|
-
import { readFileSync as
|
|
5857
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync6 } from "fs";
|
|
4903
5858
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
4904
|
-
import
|
|
5859
|
+
import { createRequire as createRequire2 } from "module";
|
|
5860
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
5861
|
+
import os9 from "os";
|
|
5862
|
+
import path12 from "path";
|
|
4905
5863
|
import { jwtVerify, importSPKI } from "jose";
|
|
4906
5864
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
4907
5865
|
var init_license = __esm({
|
|
4908
5866
|
"src/lib/license.ts"() {
|
|
4909
5867
|
"use strict";
|
|
4910
5868
|
init_config();
|
|
4911
|
-
LICENSE_PATH =
|
|
4912
|
-
CACHE_PATH =
|
|
4913
|
-
DEVICE_ID_PATH =
|
|
5869
|
+
LICENSE_PATH = path12.join(EXE_AI_DIR, "license.key");
|
|
5870
|
+
CACHE_PATH = path12.join(EXE_AI_DIR, "license-cache.json");
|
|
5871
|
+
DEVICE_ID_PATH = path12.join(EXE_AI_DIR, "device-id");
|
|
4914
5872
|
PLAN_LIMITS = {
|
|
4915
5873
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
4916
5874
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -4922,12 +5880,12 @@ var init_license = __esm({
|
|
|
4922
5880
|
});
|
|
4923
5881
|
|
|
4924
5882
|
// src/lib/plan-limits.ts
|
|
4925
|
-
import { readFileSync as
|
|
4926
|
-
import
|
|
5883
|
+
import { readFileSync as readFileSync10, existsSync as existsSync12 } from "fs";
|
|
5884
|
+
import path13 from "path";
|
|
4927
5885
|
function getLicenseSync() {
|
|
4928
5886
|
try {
|
|
4929
|
-
if (!
|
|
4930
|
-
const raw = JSON.parse(
|
|
5887
|
+
if (!existsSync12(CACHE_PATH2)) return freeLicense();
|
|
5888
|
+
const raw = JSON.parse(readFileSync10(CACHE_PATH2, "utf8"));
|
|
4931
5889
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
4932
5890
|
const parts = raw.token.split(".");
|
|
4933
5891
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -4965,8 +5923,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
4965
5923
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
4966
5924
|
let count = 0;
|
|
4967
5925
|
try {
|
|
4968
|
-
if (
|
|
4969
|
-
const raw =
|
|
5926
|
+
if (existsSync12(filePath)) {
|
|
5927
|
+
const raw = readFileSync10(filePath, "utf8");
|
|
4970
5928
|
const employees = JSON.parse(raw);
|
|
4971
5929
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
4972
5930
|
}
|
|
@@ -4995,29 +5953,63 @@ var init_plan_limits = __esm({
|
|
|
4995
5953
|
this.name = "PlanLimitError";
|
|
4996
5954
|
}
|
|
4997
5955
|
};
|
|
4998
|
-
CACHE_PATH2 =
|
|
5956
|
+
CACHE_PATH2 = path13.join(EXE_AI_DIR, "license-cache.json");
|
|
5957
|
+
}
|
|
5958
|
+
});
|
|
5959
|
+
|
|
5960
|
+
// src/lib/task-scope.ts
|
|
5961
|
+
function getCurrentSessionScope() {
|
|
5962
|
+
try {
|
|
5963
|
+
return resolveExeSession();
|
|
5964
|
+
} catch {
|
|
5965
|
+
return null;
|
|
5966
|
+
}
|
|
5967
|
+
}
|
|
5968
|
+
function sessionScopeFilter(sessionScope, tableAlias) {
|
|
5969
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
5970
|
+
if (!scope) return { sql: "", args: [] };
|
|
5971
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
5972
|
+
return {
|
|
5973
|
+
sql: ` AND (${col} IS NULL OR ${col} = ?)`,
|
|
5974
|
+
args: [scope]
|
|
5975
|
+
};
|
|
5976
|
+
}
|
|
5977
|
+
function strictSessionScopeFilter(sessionScope, tableAlias) {
|
|
5978
|
+
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
5979
|
+
if (!scope) return { sql: "", args: [] };
|
|
5980
|
+
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
5981
|
+
return {
|
|
5982
|
+
sql: ` AND ${col} = ?`,
|
|
5983
|
+
args: [scope]
|
|
5984
|
+
};
|
|
5985
|
+
}
|
|
5986
|
+
var init_task_scope = __esm({
|
|
5987
|
+
"src/lib/task-scope.ts"() {
|
|
5988
|
+
"use strict";
|
|
5989
|
+
init_tmux_routing();
|
|
4999
5990
|
}
|
|
5000
5991
|
});
|
|
5001
5992
|
|
|
5002
5993
|
// src/lib/notifications.ts
|
|
5003
|
-
import
|
|
5004
|
-
import
|
|
5005
|
-
import
|
|
5994
|
+
import crypto4 from "crypto";
|
|
5995
|
+
import path14 from "path";
|
|
5996
|
+
import os10 from "os";
|
|
5006
5997
|
import {
|
|
5007
|
-
readFileSync as
|
|
5998
|
+
readFileSync as readFileSync11,
|
|
5008
5999
|
readdirSync as readdirSync2,
|
|
5009
6000
|
unlinkSync as unlinkSync3,
|
|
5010
|
-
existsSync as
|
|
6001
|
+
existsSync as existsSync13,
|
|
5011
6002
|
rmdirSync
|
|
5012
6003
|
} from "fs";
|
|
5013
6004
|
async function writeNotification(notification) {
|
|
5014
6005
|
try {
|
|
5015
6006
|
const client = getClient();
|
|
5016
|
-
const id =
|
|
6007
|
+
const id = crypto4.randomUUID();
|
|
5017
6008
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6009
|
+
const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
|
|
5018
6010
|
await client.execute({
|
|
5019
|
-
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
5020
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
6011
|
+
sql: `INSERT INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, session_scope, read, created_at)
|
|
6012
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`,
|
|
5021
6013
|
args: [
|
|
5022
6014
|
id,
|
|
5023
6015
|
notification.agentId,
|
|
@@ -5026,6 +6018,7 @@ async function writeNotification(notification) {
|
|
|
5026
6018
|
notification.project,
|
|
5027
6019
|
notification.summary,
|
|
5028
6020
|
notification.taskFile ?? null,
|
|
6021
|
+
sessionScope,
|
|
5029
6022
|
now
|
|
5030
6023
|
]
|
|
5031
6024
|
});
|
|
@@ -5034,12 +6027,14 @@ async function writeNotification(notification) {
|
|
|
5034
6027
|
`);
|
|
5035
6028
|
}
|
|
5036
6029
|
}
|
|
5037
|
-
async function markAsReadByTaskFile(taskFile) {
|
|
6030
|
+
async function markAsReadByTaskFile(taskFile, sessionScope) {
|
|
5038
6031
|
try {
|
|
5039
6032
|
const client = getClient();
|
|
6033
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
5040
6034
|
await client.execute({
|
|
5041
|
-
sql:
|
|
5042
|
-
|
|
6035
|
+
sql: `UPDATE notifications SET read = 1
|
|
6036
|
+
WHERE task_file = ? AND read = 0${scope.sql}`,
|
|
6037
|
+
args: [taskFile, ...scope.args]
|
|
5043
6038
|
});
|
|
5044
6039
|
} catch {
|
|
5045
6040
|
}
|
|
@@ -5048,11 +6043,12 @@ var init_notifications = __esm({
|
|
|
5048
6043
|
"src/lib/notifications.ts"() {
|
|
5049
6044
|
"use strict";
|
|
5050
6045
|
init_database();
|
|
6046
|
+
init_task_scope();
|
|
5051
6047
|
}
|
|
5052
6048
|
});
|
|
5053
6049
|
|
|
5054
6050
|
// src/lib/session-kill-telemetry.ts
|
|
5055
|
-
import
|
|
6051
|
+
import crypto5 from "crypto";
|
|
5056
6052
|
async function recordSessionKill(input) {
|
|
5057
6053
|
try {
|
|
5058
6054
|
const client = getClient();
|
|
@@ -5062,7 +6058,7 @@ async function recordSessionKill(input) {
|
|
|
5062
6058
|
ticks_idle, estimated_tokens_saved)
|
|
5063
6059
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
5064
6060
|
args: [
|
|
5065
|
-
|
|
6061
|
+
crypto5.randomUUID(),
|
|
5066
6062
|
input.sessionName,
|
|
5067
6063
|
input.agentId,
|
|
5068
6064
|
(/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5085,37 +6081,13 @@ var init_session_kill_telemetry = __esm({
|
|
|
5085
6081
|
}
|
|
5086
6082
|
});
|
|
5087
6083
|
|
|
5088
|
-
// src/lib/task-scope.ts
|
|
5089
|
-
function getCurrentSessionScope() {
|
|
5090
|
-
try {
|
|
5091
|
-
return resolveExeSession();
|
|
5092
|
-
} catch {
|
|
5093
|
-
return null;
|
|
5094
|
-
}
|
|
5095
|
-
}
|
|
5096
|
-
function sessionScopeFilter(sessionScope, tableAlias) {
|
|
5097
|
-
const scope = sessionScope !== void 0 ? sessionScope : getCurrentSessionScope();
|
|
5098
|
-
if (!scope) return { sql: "", args: [] };
|
|
5099
|
-
const col = tableAlias ? `${tableAlias}.session_scope` : "session_scope";
|
|
5100
|
-
return {
|
|
5101
|
-
sql: ` AND (${col} IS NULL OR ${col} = ?)`,
|
|
5102
|
-
args: [scope]
|
|
5103
|
-
};
|
|
5104
|
-
}
|
|
5105
|
-
var init_task_scope = __esm({
|
|
5106
|
-
"src/lib/task-scope.ts"() {
|
|
5107
|
-
"use strict";
|
|
5108
|
-
init_tmux_routing();
|
|
5109
|
-
}
|
|
5110
|
-
});
|
|
5111
|
-
|
|
5112
6084
|
// src/lib/tasks-crud.ts
|
|
5113
|
-
import
|
|
5114
|
-
import
|
|
5115
|
-
import
|
|
6085
|
+
import crypto6 from "crypto";
|
|
6086
|
+
import path15 from "path";
|
|
6087
|
+
import os11 from "os";
|
|
5116
6088
|
import { execSync as execSync4 } from "child_process";
|
|
5117
6089
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
5118
|
-
import { existsSync as
|
|
6090
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
5119
6091
|
async function writeCheckpoint(input) {
|
|
5120
6092
|
const client = getClient();
|
|
5121
6093
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -5231,7 +6203,7 @@ async function resolveTask(client, identifier, scopeSession) {
|
|
|
5231
6203
|
}
|
|
5232
6204
|
async function createTaskCore(input) {
|
|
5233
6205
|
const client = getClient();
|
|
5234
|
-
const id =
|
|
6206
|
+
const id = crypto6.randomUUID();
|
|
5235
6207
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5236
6208
|
const slug = slugify(input.title);
|
|
5237
6209
|
let earlySessionScope = null;
|
|
@@ -5290,8 +6262,8 @@ ${laneWarning}` : laneWarning;
|
|
|
5290
6262
|
}
|
|
5291
6263
|
if (input.baseDir) {
|
|
5292
6264
|
try {
|
|
5293
|
-
await mkdir4(
|
|
5294
|
-
await mkdir4(
|
|
6265
|
+
await mkdir4(path15.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
6266
|
+
await mkdir4(path15.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
5295
6267
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
5296
6268
|
await ensureGitignoreExe(input.baseDir);
|
|
5297
6269
|
} catch {
|
|
@@ -5327,13 +6299,19 @@ ${laneWarning}` : laneWarning;
|
|
|
5327
6299
|
});
|
|
5328
6300
|
if (input.baseDir) {
|
|
5329
6301
|
try {
|
|
5330
|
-
const EXE_OS_DIR =
|
|
5331
|
-
const mdPath =
|
|
5332
|
-
const mdDir =
|
|
5333
|
-
if (!
|
|
6302
|
+
const EXE_OS_DIR = path15.join(os11.homedir(), ".exe-os");
|
|
6303
|
+
const mdPath = path15.join(EXE_OS_DIR, taskFile);
|
|
6304
|
+
const mdDir = path15.dirname(mdPath);
|
|
6305
|
+
if (!existsSync14(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
5334
6306
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
5335
6307
|
const mdContent = `# ${input.title}
|
|
5336
6308
|
|
|
6309
|
+
## MANDATORY: When done
|
|
6310
|
+
|
|
6311
|
+
You MUST call update_task with status "done" and a result summary when finished.
|
|
6312
|
+
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
6313
|
+
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
6314
|
+
|
|
5337
6315
|
**ID:** ${id}
|
|
5338
6316
|
**Status:** ${initialStatus}
|
|
5339
6317
|
**Priority:** ${input.priority}
|
|
@@ -5347,12 +6325,6 @@ ${laneWarning}` : laneWarning;
|
|
|
5347
6325
|
## Context
|
|
5348
6326
|
|
|
5349
6327
|
${input.context}
|
|
5350
|
-
|
|
5351
|
-
## MANDATORY: When done
|
|
5352
|
-
|
|
5353
|
-
You MUST call update_task with status "done" and a result summary when finished.
|
|
5354
|
-
If you skip this, your reviewer will not know you're done and your work won't be reviewed.
|
|
5355
|
-
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
5356
6328
|
`;
|
|
5357
6329
|
await writeFile4(mdPath, mdContent, "utf-8");
|
|
5358
6330
|
} catch (err) {
|
|
@@ -5601,7 +6573,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
5601
6573
|
await client.execute("PRAGMA wal_checkpoint(PASSIVE)");
|
|
5602
6574
|
} catch {
|
|
5603
6575
|
}
|
|
5604
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
6576
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
5605
6577
|
try {
|
|
5606
6578
|
const { clearQueueForAgent: clearQueueForAgent2 } = await Promise.resolve().then(() => (init_intercom_queue(), intercom_queue_exports));
|
|
5607
6579
|
clearQueueForAgent2(String(row.assigned_to));
|
|
@@ -5630,9 +6602,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
5630
6602
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
5631
6603
|
}
|
|
5632
6604
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
5633
|
-
const archPath =
|
|
6605
|
+
const archPath = path15.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
5634
6606
|
try {
|
|
5635
|
-
if (
|
|
6607
|
+
if (existsSync14(archPath)) return;
|
|
5636
6608
|
const template = [
|
|
5637
6609
|
`# ${projectName} \u2014 System Architecture`,
|
|
5638
6610
|
"",
|
|
@@ -5665,10 +6637,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
5665
6637
|
}
|
|
5666
6638
|
}
|
|
5667
6639
|
async function ensureGitignoreExe(baseDir) {
|
|
5668
|
-
const gitignorePath =
|
|
6640
|
+
const gitignorePath = path15.join(baseDir, ".gitignore");
|
|
5669
6641
|
try {
|
|
5670
|
-
if (
|
|
5671
|
-
const content =
|
|
6642
|
+
if (existsSync14(gitignorePath)) {
|
|
6643
|
+
const content = readFileSync12(gitignorePath, "utf-8");
|
|
5672
6644
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
5673
6645
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
5674
6646
|
} else {
|
|
@@ -5699,58 +6671,42 @@ var init_tasks_crud = __esm({
|
|
|
5699
6671
|
});
|
|
5700
6672
|
|
|
5701
6673
|
// src/lib/tasks-review.ts
|
|
5702
|
-
import
|
|
5703
|
-
import { existsSync as
|
|
6674
|
+
import path16 from "path";
|
|
6675
|
+
import { existsSync as existsSync15, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
5704
6676
|
async function countPendingReviews(sessionScope) {
|
|
5705
6677
|
const client = getClient();
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
args: [sessionScope]
|
|
5710
|
-
});
|
|
5711
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
5712
|
-
}
|
|
6678
|
+
const scope = strictSessionScopeFilter(
|
|
6679
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
6680
|
+
);
|
|
5713
6681
|
const result = await client.execute({
|
|
5714
|
-
sql:
|
|
5715
|
-
|
|
6682
|
+
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
6683
|
+
WHERE status = 'needs_review'${scope.sql}`,
|
|
6684
|
+
args: [...scope.args]
|
|
5716
6685
|
});
|
|
5717
6686
|
return Number(result.rows[0]?.cnt) || 0;
|
|
5718
6687
|
}
|
|
5719
6688
|
async function countNewPendingReviewsSince(sinceIso, sessionScope) {
|
|
5720
6689
|
const client = getClient();
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
WHERE status = 'needs_review' AND updated_at > ?
|
|
5725
|
-
AND session_scope = ?`,
|
|
5726
|
-
args: [sinceIso, sessionScope]
|
|
5727
|
-
});
|
|
5728
|
-
return Number(result2.rows[0]?.cnt) || 0;
|
|
5729
|
-
}
|
|
6690
|
+
const scope = strictSessionScopeFilter(
|
|
6691
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
6692
|
+
);
|
|
5730
6693
|
const result = await client.execute({
|
|
5731
6694
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
5732
|
-
WHERE status = 'needs_review' AND updated_at >
|
|
5733
|
-
args: [sinceIso]
|
|
6695
|
+
WHERE status = 'needs_review' AND updated_at > ?${scope.sql}`,
|
|
6696
|
+
args: [sinceIso, ...scope.args]
|
|
5734
6697
|
});
|
|
5735
6698
|
return Number(result.rows[0]?.cnt) || 0;
|
|
5736
6699
|
}
|
|
5737
6700
|
async function listPendingReviews(limit, sessionScope) {
|
|
5738
6701
|
const client = getClient();
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
WHERE status = 'needs_review'
|
|
5743
|
-
AND session_scope = ?
|
|
5744
|
-
ORDER BY updated_at ASC LIMIT ?`,
|
|
5745
|
-
args: [sessionScope, limit]
|
|
5746
|
-
});
|
|
5747
|
-
return result2.rows;
|
|
5748
|
-
}
|
|
6702
|
+
const scope = strictSessionScopeFilter(
|
|
6703
|
+
sessionScope === void 0 ? getCurrentSessionScope() : sessionScope
|
|
6704
|
+
);
|
|
5749
6705
|
const result = await client.execute({
|
|
5750
6706
|
sql: `SELECT title, assigned_to, project_name, updated_at FROM tasks
|
|
5751
|
-
WHERE status = 'needs_review'
|
|
6707
|
+
WHERE status = 'needs_review'${scope.sql}
|
|
5752
6708
|
ORDER BY updated_at ASC LIMIT ?`,
|
|
5753
|
-
args: [limit]
|
|
6709
|
+
args: [...scope.args, limit]
|
|
5754
6710
|
});
|
|
5755
6711
|
return result.rows;
|
|
5756
6712
|
}
|
|
@@ -5762,7 +6718,7 @@ async function cleanupOrphanedReviews() {
|
|
|
5762
6718
|
WHERE status IN ('open', 'needs_review', 'in_progress')
|
|
5763
6719
|
AND assigned_by = 'system'
|
|
5764
6720
|
AND title LIKE 'Review:%'
|
|
5765
|
-
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
6721
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled', 'closed'))`,
|
|
5766
6722
|
args: [now]
|
|
5767
6723
|
});
|
|
5768
6724
|
const r1b = await client.execute({
|
|
@@ -5881,11 +6837,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
5881
6837
|
);
|
|
5882
6838
|
}
|
|
5883
6839
|
try {
|
|
5884
|
-
const cacheDir =
|
|
5885
|
-
if (
|
|
6840
|
+
const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
|
|
6841
|
+
if (existsSync15(cacheDir)) {
|
|
5886
6842
|
for (const f of readdirSync3(cacheDir)) {
|
|
5887
6843
|
if (f.startsWith("review-notified-")) {
|
|
5888
|
-
unlinkSync4(
|
|
6844
|
+
unlinkSync4(path16.join(cacheDir, f));
|
|
5889
6845
|
}
|
|
5890
6846
|
}
|
|
5891
6847
|
}
|
|
@@ -5902,11 +6858,12 @@ var init_tasks_review = __esm({
|
|
|
5902
6858
|
init_tmux_routing();
|
|
5903
6859
|
init_session_key();
|
|
5904
6860
|
init_state_bus();
|
|
6861
|
+
init_task_scope();
|
|
5905
6862
|
}
|
|
5906
6863
|
});
|
|
5907
6864
|
|
|
5908
6865
|
// src/lib/tasks-chain.ts
|
|
5909
|
-
import
|
|
6866
|
+
import path17 from "path";
|
|
5910
6867
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
5911
6868
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
5912
6869
|
const client = getClient();
|
|
@@ -5923,7 +6880,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
5923
6880
|
});
|
|
5924
6881
|
for (const ur of unblockedRows.rows) {
|
|
5925
6882
|
try {
|
|
5926
|
-
const ubFile =
|
|
6883
|
+
const ubFile = path17.join(baseDir, String(ur.task_file));
|
|
5927
6884
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
5928
6885
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
5929
6886
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -5958,7 +6915,7 @@ async function checkSubtaskCompletion(parentTaskId, projectName) {
|
|
|
5958
6915
|
const scScope = sessionScopeFilter();
|
|
5959
6916
|
const remaining = await client.execute({
|
|
5960
6917
|
sql: `SELECT COUNT(*) as cnt FROM tasks
|
|
5961
|
-
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled')${scScope.sql}`,
|
|
6918
|
+
WHERE parent_task_id = ? AND status NOT IN ('done', 'cancelled', 'closed')${scScope.sql}`,
|
|
5962
6919
|
args: [parentTaskId, ...scScope.args]
|
|
5963
6920
|
});
|
|
5964
6921
|
const cnt = Number(remaining.rows[0]?.cnt ?? 1);
|
|
@@ -5992,7 +6949,7 @@ var init_tasks_chain = __esm({
|
|
|
5992
6949
|
|
|
5993
6950
|
// src/lib/project-name.ts
|
|
5994
6951
|
import { execSync as execSync5 } from "child_process";
|
|
5995
|
-
import
|
|
6952
|
+
import path18 from "path";
|
|
5996
6953
|
function getProjectName(cwd) {
|
|
5997
6954
|
const dir = cwd ?? process.cwd();
|
|
5998
6955
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -6005,7 +6962,7 @@ function getProjectName(cwd) {
|
|
|
6005
6962
|
timeout: 2e3,
|
|
6006
6963
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6007
6964
|
}).trim();
|
|
6008
|
-
repoRoot =
|
|
6965
|
+
repoRoot = path18.dirname(gitCommonDir);
|
|
6009
6966
|
} catch {
|
|
6010
6967
|
repoRoot = execSync5("git rev-parse --show-toplevel", {
|
|
6011
6968
|
cwd: dir,
|
|
@@ -6014,11 +6971,11 @@ function getProjectName(cwd) {
|
|
|
6014
6971
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6015
6972
|
}).trim();
|
|
6016
6973
|
}
|
|
6017
|
-
_cached2 =
|
|
6974
|
+
_cached2 = path18.basename(repoRoot);
|
|
6018
6975
|
_cachedCwd = dir;
|
|
6019
6976
|
return _cached2;
|
|
6020
6977
|
} catch {
|
|
6021
|
-
_cached2 =
|
|
6978
|
+
_cached2 = path18.basename(dir);
|
|
6022
6979
|
_cachedCwd = dir;
|
|
6023
6980
|
return _cached2;
|
|
6024
6981
|
}
|
|
@@ -6161,10 +7118,10 @@ var init_tasks_notify = __esm({
|
|
|
6161
7118
|
});
|
|
6162
7119
|
|
|
6163
7120
|
// src/lib/behaviors.ts
|
|
6164
|
-
import
|
|
7121
|
+
import crypto7 from "crypto";
|
|
6165
7122
|
async function storeBehavior(opts) {
|
|
6166
7123
|
const client = getClient();
|
|
6167
|
-
const id =
|
|
7124
|
+
const id = crypto7.randomUUID();
|
|
6168
7125
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6169
7126
|
await client.execute({
|
|
6170
7127
|
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
|
|
@@ -6193,7 +7150,7 @@ __export(skill_learning_exports, {
|
|
|
6193
7150
|
storeTrajectory: () => storeTrajectory,
|
|
6194
7151
|
sweepTrajectories: () => sweepTrajectories
|
|
6195
7152
|
});
|
|
6196
|
-
import
|
|
7153
|
+
import crypto8 from "crypto";
|
|
6197
7154
|
async function extractTrajectory(taskId, agentId) {
|
|
6198
7155
|
const client = getClient();
|
|
6199
7156
|
const result = await client.execute({
|
|
@@ -6222,11 +7179,11 @@ async function extractTrajectory(taskId, agentId) {
|
|
|
6222
7179
|
return signature;
|
|
6223
7180
|
}
|
|
6224
7181
|
function hashSignature(signature) {
|
|
6225
|
-
return
|
|
7182
|
+
return crypto8.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
|
|
6226
7183
|
}
|
|
6227
7184
|
async function storeTrajectory(opts) {
|
|
6228
7185
|
const client = getClient();
|
|
6229
|
-
const id =
|
|
7186
|
+
const id = crypto8.randomUUID();
|
|
6230
7187
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6231
7188
|
const signatureHash = hashSignature(opts.signature);
|
|
6232
7189
|
await client.execute({
|
|
@@ -6491,8 +7448,8 @@ __export(tasks_exports, {
|
|
|
6491
7448
|
updateTaskStatus: () => updateTaskStatus,
|
|
6492
7449
|
writeCheckpoint: () => writeCheckpoint
|
|
6493
7450
|
});
|
|
6494
|
-
import
|
|
6495
|
-
import { writeFileSync as
|
|
7451
|
+
import path19 from "path";
|
|
7452
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
|
|
6496
7453
|
async function createTask(input) {
|
|
6497
7454
|
const result = await createTaskCore(input);
|
|
6498
7455
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -6511,12 +7468,12 @@ async function updateTask(input) {
|
|
|
6511
7468
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
6512
7469
|
try {
|
|
6513
7470
|
const agent = String(row.assigned_to);
|
|
6514
|
-
const cacheDir =
|
|
6515
|
-
const cachePath =
|
|
7471
|
+
const cacheDir = path19.join(EXE_AI_DIR, "session-cache");
|
|
7472
|
+
const cachePath = path19.join(cacheDir, `current-task-${agent}.json`);
|
|
6516
7473
|
if (input.status === "in_progress") {
|
|
6517
7474
|
mkdirSync7(cacheDir, { recursive: true });
|
|
6518
|
-
|
|
6519
|
-
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
7475
|
+
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
7476
|
+
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
|
|
6520
7477
|
try {
|
|
6521
7478
|
unlinkSync5(cachePath);
|
|
6522
7479
|
} catch {
|
|
@@ -6524,10 +7481,10 @@ async function updateTask(input) {
|
|
|
6524
7481
|
}
|
|
6525
7482
|
} catch {
|
|
6526
7483
|
}
|
|
6527
|
-
if (input.status === "done") {
|
|
7484
|
+
if (input.status === "done" || input.status === "closed") {
|
|
6528
7485
|
await cleanupReviewFile(row, taskFile, input.baseDir);
|
|
6529
7486
|
}
|
|
6530
|
-
if (input.status === "done" || input.status === "cancelled") {
|
|
7487
|
+
if (input.status === "done" || input.status === "cancelled" || input.status === "closed") {
|
|
6531
7488
|
try {
|
|
6532
7489
|
const client = getClient();
|
|
6533
7490
|
const taskTitle = String(row.title);
|
|
@@ -6543,7 +7500,7 @@ async function updateTask(input) {
|
|
|
6543
7500
|
if (!isCoordinatorName(assignedAgent)) {
|
|
6544
7501
|
try {
|
|
6545
7502
|
const draftClient = getClient();
|
|
6546
|
-
if (input.status === "done") {
|
|
7503
|
+
if (input.status === "done" || input.status === "closed") {
|
|
6547
7504
|
await draftClient.execute({
|
|
6548
7505
|
sql: `UPDATE memories SET draft = 0 WHERE agent_id = ? AND draft = 1`,
|
|
6549
7506
|
args: [assignedAgent]
|
|
@@ -6560,7 +7517,7 @@ async function updateTask(input) {
|
|
|
6560
7517
|
try {
|
|
6561
7518
|
const client = getClient();
|
|
6562
7519
|
const cascaded = await client.execute({
|
|
6563
|
-
sql: `UPDATE tasks SET status = '
|
|
7520
|
+
sql: `UPDATE tasks SET status = 'closed', updated_at = ?
|
|
6564
7521
|
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
6565
7522
|
args: [now, taskId]
|
|
6566
7523
|
});
|
|
@@ -6573,14 +7530,14 @@ async function updateTask(input) {
|
|
|
6573
7530
|
} catch {
|
|
6574
7531
|
}
|
|
6575
7532
|
}
|
|
6576
|
-
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
7533
|
+
const isTerminal = input.status === "done" || input.status === "needs_review" || input.status === "closed";
|
|
6577
7534
|
if (isTerminal) {
|
|
6578
7535
|
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
6579
7536
|
if (!isCoordinator) {
|
|
6580
7537
|
notifyTaskDone();
|
|
6581
7538
|
}
|
|
6582
7539
|
await markTaskNotificationsRead(taskFile);
|
|
6583
|
-
if (input.status === "done") {
|
|
7540
|
+
if (input.status === "done" || input.status === "closed") {
|
|
6584
7541
|
try {
|
|
6585
7542
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
6586
7543
|
} catch {
|
|
@@ -6600,7 +7557,7 @@ async function updateTask(input) {
|
|
|
6600
7557
|
}
|
|
6601
7558
|
}
|
|
6602
7559
|
}
|
|
6603
|
-
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
7560
|
+
if ((input.status === "done" || input.status === "closed") && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
6604
7561
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
6605
7562
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
6606
7563
|
taskId,
|
|
@@ -6972,6 +7929,7 @@ __export(tmux_routing_exports, {
|
|
|
6972
7929
|
isEmployeeAlive: () => isEmployeeAlive,
|
|
6973
7930
|
isExeSession: () => isExeSession,
|
|
6974
7931
|
isSessionBusy: () => isSessionBusy,
|
|
7932
|
+
notifyCoordinatorTaskCompletion: () => notifyCoordinatorTaskCompletion,
|
|
6975
7933
|
notifyParentExe: () => notifyParentExe,
|
|
6976
7934
|
parseParentExe: () => parseParentExe,
|
|
6977
7935
|
registerParentExe: () => registerParentExe,
|
|
@@ -6982,13 +7940,13 @@ __export(tmux_routing_exports, {
|
|
|
6982
7940
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
6983
7941
|
});
|
|
6984
7942
|
import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
|
|
6985
|
-
import { readFileSync as
|
|
6986
|
-
import
|
|
6987
|
-
import
|
|
7943
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, existsSync as existsSync16, appendFileSync, readdirSync as readdirSync4 } from "fs";
|
|
7944
|
+
import path20 from "path";
|
|
7945
|
+
import os12 from "os";
|
|
6988
7946
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6989
7947
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
6990
7948
|
function spawnLockPath(sessionName) {
|
|
6991
|
-
return
|
|
7949
|
+
return path20.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
6992
7950
|
}
|
|
6993
7951
|
function isProcessAlive(pid) {
|
|
6994
7952
|
try {
|
|
@@ -6999,13 +7957,13 @@ function isProcessAlive(pid) {
|
|
|
6999
7957
|
}
|
|
7000
7958
|
}
|
|
7001
7959
|
function acquireSpawnLock2(sessionName) {
|
|
7002
|
-
if (!
|
|
7960
|
+
if (!existsSync16(SPAWN_LOCK_DIR)) {
|
|
7003
7961
|
mkdirSync8(SPAWN_LOCK_DIR, { recursive: true });
|
|
7004
7962
|
}
|
|
7005
7963
|
const lockFile = spawnLockPath(sessionName);
|
|
7006
|
-
if (
|
|
7964
|
+
if (existsSync16(lockFile)) {
|
|
7007
7965
|
try {
|
|
7008
|
-
const lock = JSON.parse(
|
|
7966
|
+
const lock = JSON.parse(readFileSync13(lockFile, "utf8"));
|
|
7009
7967
|
const age = Date.now() - lock.timestamp;
|
|
7010
7968
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
7011
7969
|
return false;
|
|
@@ -7013,7 +7971,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
7013
7971
|
} catch {
|
|
7014
7972
|
}
|
|
7015
7973
|
}
|
|
7016
|
-
|
|
7974
|
+
writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
7017
7975
|
return true;
|
|
7018
7976
|
}
|
|
7019
7977
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -7025,13 +7983,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
7025
7983
|
function resolveBehaviorsExporterScript() {
|
|
7026
7984
|
try {
|
|
7027
7985
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
7028
|
-
const scriptPath =
|
|
7029
|
-
|
|
7986
|
+
const scriptPath = path20.join(
|
|
7987
|
+
path20.dirname(thisFile),
|
|
7030
7988
|
"..",
|
|
7031
7989
|
"bin",
|
|
7032
7990
|
"exe-export-behaviors.js"
|
|
7033
7991
|
);
|
|
7034
|
-
return
|
|
7992
|
+
return existsSync16(scriptPath) ? scriptPath : null;
|
|
7035
7993
|
} catch {
|
|
7036
7994
|
return null;
|
|
7037
7995
|
}
|
|
@@ -7097,12 +8055,12 @@ function extractRootExe(name) {
|
|
|
7097
8055
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
7098
8056
|
}
|
|
7099
8057
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
7100
|
-
if (!
|
|
8058
|
+
if (!existsSync16(SESSION_CACHE)) {
|
|
7101
8059
|
mkdirSync8(SESSION_CACHE, { recursive: true });
|
|
7102
8060
|
}
|
|
7103
8061
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
7104
|
-
const filePath =
|
|
7105
|
-
|
|
8062
|
+
const filePath = path20.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
8063
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
7106
8064
|
parentExe: rootExe,
|
|
7107
8065
|
dispatchedBy: dispatchedBy || rootExe,
|
|
7108
8066
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -7110,7 +8068,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
7110
8068
|
}
|
|
7111
8069
|
function getParentExe(sessionKey) {
|
|
7112
8070
|
try {
|
|
7113
|
-
const data = JSON.parse(
|
|
8071
|
+
const data = JSON.parse(readFileSync13(path20.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
7114
8072
|
return data.parentExe || null;
|
|
7115
8073
|
} catch {
|
|
7116
8074
|
return null;
|
|
@@ -7118,8 +8076,8 @@ function getParentExe(sessionKey) {
|
|
|
7118
8076
|
}
|
|
7119
8077
|
function getDispatchedBy(sessionKey) {
|
|
7120
8078
|
try {
|
|
7121
|
-
const data = JSON.parse(
|
|
7122
|
-
|
|
8079
|
+
const data = JSON.parse(readFileSync13(
|
|
8080
|
+
path20.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
7123
8081
|
"utf8"
|
|
7124
8082
|
));
|
|
7125
8083
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -7189,8 +8147,8 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
7189
8147
|
}
|
|
7190
8148
|
function readDebounceState() {
|
|
7191
8149
|
try {
|
|
7192
|
-
if (!
|
|
7193
|
-
const raw = JSON.parse(
|
|
8150
|
+
if (!existsSync16(DEBOUNCE_FILE)) return {};
|
|
8151
|
+
const raw = JSON.parse(readFileSync13(DEBOUNCE_FILE, "utf8"));
|
|
7194
8152
|
const state = {};
|
|
7195
8153
|
for (const [key, val] of Object.entries(raw)) {
|
|
7196
8154
|
if (typeof val === "number") {
|
|
@@ -7206,8 +8164,8 @@ function readDebounceState() {
|
|
|
7206
8164
|
}
|
|
7207
8165
|
function writeDebounceState(state) {
|
|
7208
8166
|
try {
|
|
7209
|
-
if (!
|
|
7210
|
-
|
|
8167
|
+
if (!existsSync16(SESSION_CACHE)) mkdirSync8(SESSION_CACHE, { recursive: true });
|
|
8168
|
+
writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
|
|
7211
8169
|
} catch {
|
|
7212
8170
|
}
|
|
7213
8171
|
}
|
|
@@ -7305,8 +8263,8 @@ function sendIntercom(targetSession) {
|
|
|
7305
8263
|
try {
|
|
7306
8264
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
7307
8265
|
const agent = baseAgentName(rawAgent);
|
|
7308
|
-
const markerPath =
|
|
7309
|
-
if (
|
|
8266
|
+
const markerPath = path20.join(SESSION_CACHE, `current-task-${agent}.json`);
|
|
8267
|
+
if (existsSync16(markerPath)) {
|
|
7310
8268
|
logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker \u2014 will auto-chain)`);
|
|
7311
8269
|
return "debounced";
|
|
7312
8270
|
}
|
|
@@ -7315,8 +8273,8 @@ function sendIntercom(targetSession) {
|
|
|
7315
8273
|
try {
|
|
7316
8274
|
const rawAgent = targetSession.split("-")[0] ?? targetSession;
|
|
7317
8275
|
const agent = baseAgentName(rawAgent);
|
|
7318
|
-
const taskDir =
|
|
7319
|
-
if (
|
|
8276
|
+
const taskDir = path20.join(process.cwd(), "exe", agent);
|
|
8277
|
+
if (existsSync16(taskDir)) {
|
|
7320
8278
|
const files = readdirSync4(taskDir).filter(
|
|
7321
8279
|
(f) => f.endsWith(".md") && f !== "DONE.txt"
|
|
7322
8280
|
);
|
|
@@ -7376,6 +8334,21 @@ function notifyParentExe(sessionKey) {
|
|
|
7376
8334
|
}
|
|
7377
8335
|
return true;
|
|
7378
8336
|
}
|
|
8337
|
+
function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitle) {
|
|
8338
|
+
const transport = getTransport();
|
|
8339
|
+
try {
|
|
8340
|
+
const sessions = transport.listSessions();
|
|
8341
|
+
if (!sessions.includes(coordinatorSession)) return false;
|
|
8342
|
+
execSync6(
|
|
8343
|
+
`tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
|
|
8344
|
+
{ timeout: 3e3 }
|
|
8345
|
+
);
|
|
8346
|
+
logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
|
|
8347
|
+
return true;
|
|
8348
|
+
} catch {
|
|
8349
|
+
return false;
|
|
8350
|
+
}
|
|
8351
|
+
}
|
|
7379
8352
|
function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
7380
8353
|
if (isCoordinatorName(employeeName)) {
|
|
7381
8354
|
return { status: "failed", sessionName: "", error: "The COO is not a dispatchable employee" };
|
|
@@ -7449,26 +8422,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7449
8422
|
const transport = getTransport();
|
|
7450
8423
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
7451
8424
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
7452
|
-
const logDir =
|
|
7453
|
-
const logFile =
|
|
7454
|
-
if (!
|
|
8425
|
+
const logDir = path20.join(os12.homedir(), ".exe-os", "session-logs");
|
|
8426
|
+
const logFile = path20.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
8427
|
+
if (!existsSync16(logDir)) {
|
|
7455
8428
|
mkdirSync8(logDir, { recursive: true });
|
|
7456
8429
|
}
|
|
7457
8430
|
transport.kill(sessionName);
|
|
7458
8431
|
let cleanupSuffix = "";
|
|
7459
8432
|
try {
|
|
7460
8433
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
7461
|
-
const cleanupScript =
|
|
7462
|
-
if (
|
|
8434
|
+
const cleanupScript = path20.join(path20.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
8435
|
+
if (existsSync16(cleanupScript)) {
|
|
7463
8436
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
7464
8437
|
}
|
|
7465
8438
|
} catch {
|
|
7466
8439
|
}
|
|
7467
8440
|
try {
|
|
7468
|
-
const claudeJsonPath =
|
|
8441
|
+
const claudeJsonPath = path20.join(os12.homedir(), ".claude.json");
|
|
7469
8442
|
let claudeJson = {};
|
|
7470
8443
|
try {
|
|
7471
|
-
claudeJson = JSON.parse(
|
|
8444
|
+
claudeJson = JSON.parse(readFileSync13(claudeJsonPath, "utf8"));
|
|
7472
8445
|
} catch {
|
|
7473
8446
|
}
|
|
7474
8447
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -7476,17 +8449,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7476
8449
|
const trustDir = opts?.cwd ?? projectDir;
|
|
7477
8450
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
7478
8451
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
7479
|
-
|
|
8452
|
+
writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
7480
8453
|
} catch {
|
|
7481
8454
|
}
|
|
7482
8455
|
try {
|
|
7483
|
-
const settingsDir =
|
|
8456
|
+
const settingsDir = path20.join(os12.homedir(), ".claude", "projects");
|
|
7484
8457
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
7485
|
-
const projSettingsDir =
|
|
7486
|
-
const settingsPath =
|
|
8458
|
+
const projSettingsDir = path20.join(settingsDir, normalizedKey);
|
|
8459
|
+
const settingsPath = path20.join(projSettingsDir, "settings.json");
|
|
7487
8460
|
let settings = {};
|
|
7488
8461
|
try {
|
|
7489
|
-
settings = JSON.parse(
|
|
8462
|
+
settings = JSON.parse(readFileSync13(settingsPath, "utf8"));
|
|
7490
8463
|
} catch {
|
|
7491
8464
|
}
|
|
7492
8465
|
const perms = settings.permissions ?? {};
|
|
@@ -7515,7 +8488,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7515
8488
|
perms.allow = allow;
|
|
7516
8489
|
settings.permissions = perms;
|
|
7517
8490
|
mkdirSync8(projSettingsDir, { recursive: true });
|
|
7518
|
-
|
|
8491
|
+
writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
7519
8492
|
}
|
|
7520
8493
|
} catch {
|
|
7521
8494
|
}
|
|
@@ -7530,8 +8503,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7530
8503
|
let behaviorsFlag = "";
|
|
7531
8504
|
let legacyFallbackWarned = false;
|
|
7532
8505
|
if (!useExeAgent && !useBinSymlink) {
|
|
7533
|
-
const identityPath =
|
|
7534
|
-
|
|
8506
|
+
const identityPath = path20.join(
|
|
8507
|
+
os12.homedir(),
|
|
7535
8508
|
".exe-os",
|
|
7536
8509
|
"identity",
|
|
7537
8510
|
`${employeeName}.md`
|
|
@@ -7540,13 +8513,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7540
8513
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
7541
8514
|
if (hasAgentFlag) {
|
|
7542
8515
|
identityFlag = ` --agent ${employeeName}`;
|
|
7543
|
-
} else if (
|
|
8516
|
+
} else if (existsSync16(identityPath)) {
|
|
7544
8517
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
7545
8518
|
legacyFallbackWarned = true;
|
|
7546
8519
|
}
|
|
7547
8520
|
const behaviorsFile = exportBehaviorsSync(
|
|
7548
8521
|
employeeName,
|
|
7549
|
-
|
|
8522
|
+
path20.basename(spawnCwd),
|
|
7550
8523
|
sessionName
|
|
7551
8524
|
);
|
|
7552
8525
|
if (behaviorsFile) {
|
|
@@ -7561,16 +8534,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7561
8534
|
}
|
|
7562
8535
|
let sessionContextFlag = "";
|
|
7563
8536
|
try {
|
|
7564
|
-
const ctxDir =
|
|
8537
|
+
const ctxDir = path20.join(os12.homedir(), ".exe-os", "session-cache");
|
|
7565
8538
|
mkdirSync8(ctxDir, { recursive: true });
|
|
7566
|
-
const ctxFile =
|
|
8539
|
+
const ctxFile = path20.join(ctxDir, `session-context-${sessionName}.md`);
|
|
7567
8540
|
const ctxContent = [
|
|
7568
8541
|
`## Session Context`,
|
|
7569
8542
|
`You are running in tmux session: ${sessionName}.`,
|
|
7570
8543
|
`Your parent coordinator session is ${exeSession}.`,
|
|
7571
8544
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
7572
8545
|
].join("\n");
|
|
7573
|
-
|
|
8546
|
+
writeFileSync8(ctxFile, ctxContent);
|
|
7574
8547
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
7575
8548
|
} catch {
|
|
7576
8549
|
}
|
|
@@ -7647,8 +8620,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
7647
8620
|
transport.pipeLog(sessionName, logFile);
|
|
7648
8621
|
try {
|
|
7649
8622
|
const mySession = getMySession();
|
|
7650
|
-
const dispatchInfo =
|
|
7651
|
-
|
|
8623
|
+
const dispatchInfo = path20.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
8624
|
+
writeFileSync8(dispatchInfo, JSON.stringify({
|
|
7652
8625
|
dispatchedBy: mySession,
|
|
7653
8626
|
rootExe: exeSession,
|
|
7654
8627
|
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
@@ -7722,15 +8695,15 @@ var init_tmux_routing = __esm({
|
|
|
7722
8695
|
init_intercom_queue();
|
|
7723
8696
|
init_plan_limits();
|
|
7724
8697
|
init_employees();
|
|
7725
|
-
SPAWN_LOCK_DIR =
|
|
7726
|
-
SESSION_CACHE =
|
|
8698
|
+
SPAWN_LOCK_DIR = path20.join(os12.homedir(), ".exe-os", "spawn-locks");
|
|
8699
|
+
SESSION_CACHE = path20.join(os12.homedir(), ".exe-os", "session-cache");
|
|
7727
8700
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
7728
8701
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
7729
8702
|
VERIFY_PANE_LINES = 200;
|
|
7730
8703
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
7731
8704
|
CODEX_DEBOUNCE_MS = 12e4;
|
|
7732
|
-
INTERCOM_LOG2 =
|
|
7733
|
-
DEBOUNCE_FILE =
|
|
8705
|
+
INTERCOM_LOG2 = path20.join(os12.homedir(), ".exe-os", "intercom.log");
|
|
8706
|
+
DEBOUNCE_FILE = path20.join(SESSION_CACHE, "intercom-debounce.json");
|
|
7734
8707
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
7735
8708
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
|
|
7736
8709
|
}
|
|
@@ -7753,10 +8726,10 @@ __export(messaging_exports, {
|
|
|
7753
8726
|
sendMessage: () => sendMessage,
|
|
7754
8727
|
setWsClientSend: () => setWsClientSend
|
|
7755
8728
|
});
|
|
7756
|
-
import
|
|
8729
|
+
import crypto9 from "crypto";
|
|
7757
8730
|
function generateUlid() {
|
|
7758
8731
|
const timestamp = Date.now().toString(36).padStart(10, "0");
|
|
7759
|
-
const random =
|
|
8732
|
+
const random = crypto9.randomBytes(10).toString("hex").slice(0, 16);
|
|
7760
8733
|
return (timestamp + random).toUpperCase();
|
|
7761
8734
|
}
|
|
7762
8735
|
function rowToMessage(row) {
|
|
@@ -7767,6 +8740,7 @@ function rowToMessage(row) {
|
|
|
7767
8740
|
targetAgent: row.target_agent,
|
|
7768
8741
|
targetProject: row.target_project ?? null,
|
|
7769
8742
|
targetDevice: row.target_device,
|
|
8743
|
+
sessionScope: row.session_scope ?? null,
|
|
7770
8744
|
content: row.content,
|
|
7771
8745
|
priority: row.priority ?? "normal",
|
|
7772
8746
|
status: row.status ?? "pending",
|
|
@@ -7784,15 +8758,17 @@ async function sendMessage(input) {
|
|
|
7784
8758
|
const id = generateUlid();
|
|
7785
8759
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7786
8760
|
const targetDevice = input.targetDevice ?? "local";
|
|
8761
|
+
const sessionScope = input.sessionScope === void 0 ? resolveExeSession() : input.sessionScope;
|
|
7787
8762
|
await client.execute({
|
|
7788
|
-
sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, content, priority, status, created_at)
|
|
7789
|
-
VALUES (?, ?, 'local', ?, ?, ?, ?, ?, 'pending', ?)`,
|
|
8763
|
+
sql: `INSERT INTO messages (id, from_agent, from_device, target_agent, target_project, target_device, session_scope, content, priority, status, created_at)
|
|
8764
|
+
VALUES (?, ?, 'local', ?, ?, ?, ?, ?, ?, 'pending', ?)`,
|
|
7790
8765
|
args: [
|
|
7791
8766
|
id,
|
|
7792
8767
|
input.fromAgent,
|
|
7793
8768
|
input.targetAgent,
|
|
7794
8769
|
input.targetProject ?? null,
|
|
7795
8770
|
targetDevice,
|
|
8771
|
+
sessionScope,
|
|
7796
8772
|
input.content,
|
|
7797
8773
|
input.priority ?? "normal",
|
|
7798
8774
|
now
|
|
@@ -7806,9 +8782,10 @@ async function sendMessage(input) {
|
|
|
7806
8782
|
}
|
|
7807
8783
|
} catch {
|
|
7808
8784
|
}
|
|
8785
|
+
const sentScope = strictSessionScopeFilter(sessionScope);
|
|
7809
8786
|
const result = await client.execute({
|
|
7810
|
-
sql:
|
|
7811
|
-
args: [id]
|
|
8787
|
+
sql: `SELECT * FROM messages WHERE id = ?${sentScope.sql}`,
|
|
8788
|
+
args: [id, ...sentScope.args]
|
|
7812
8789
|
});
|
|
7813
8790
|
return rowToMessage(result.rows[0]);
|
|
7814
8791
|
}
|
|
@@ -7832,6 +8809,7 @@ async function deliverCrossMachineMessage(messageId, targetDevice) {
|
|
|
7832
8809
|
fromAgent: msg.fromAgent,
|
|
7833
8810
|
targetAgent: msg.targetAgent,
|
|
7834
8811
|
targetProject: msg.targetProject,
|
|
8812
|
+
sessionScope: msg.sessionScope,
|
|
7835
8813
|
content: msg.content,
|
|
7836
8814
|
priority: msg.priority,
|
|
7837
8815
|
createdAt: msg.createdAt
|
|
@@ -7875,7 +8853,7 @@ async function deliverLocalMessage(messageId) {
|
|
|
7875
8853
|
} catch {
|
|
7876
8854
|
const newRetryCount = msg.retryCount + 1;
|
|
7877
8855
|
if (newRetryCount >= MAX_RETRIES3) {
|
|
7878
|
-
await markFailed(messageId, "session unavailable after 10 retries");
|
|
8856
|
+
await markFailed(messageId, "session unavailable after 10 retries", msg.sessionScope);
|
|
7879
8857
|
} else {
|
|
7880
8858
|
await client.execute({
|
|
7881
8859
|
sql: "UPDATE messages SET retry_count = ? WHERE id = ?",
|
|
@@ -7885,85 +8863,101 @@ async function deliverLocalMessage(messageId) {
|
|
|
7885
8863
|
return false;
|
|
7886
8864
|
}
|
|
7887
8865
|
}
|
|
7888
|
-
async function getPendingMessages(targetAgent) {
|
|
8866
|
+
async function getPendingMessages(targetAgent, sessionScope) {
|
|
7889
8867
|
const client = getClient();
|
|
8868
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
7890
8869
|
const result = await client.execute({
|
|
7891
8870
|
sql: `SELECT * FROM messages
|
|
7892
|
-
WHERE target_agent = ? AND status IN ('pending', 'delivered')
|
|
8871
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered')${scope.sql}
|
|
7893
8872
|
ORDER BY id`,
|
|
7894
|
-
args: [targetAgent]
|
|
8873
|
+
args: [targetAgent, ...scope.args]
|
|
7895
8874
|
});
|
|
7896
8875
|
return result.rows.map((row) => rowToMessage(row));
|
|
7897
8876
|
}
|
|
7898
|
-
async function markRead(messageId) {
|
|
8877
|
+
async function markRead(messageId, sessionScope) {
|
|
7899
8878
|
const client = getClient();
|
|
8879
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
7900
8880
|
await client.execute({
|
|
7901
|
-
sql:
|
|
7902
|
-
|
|
8881
|
+
sql: `UPDATE messages SET status = 'read'
|
|
8882
|
+
WHERE id = ? AND status IN ('pending', 'delivered')${scope.sql}`,
|
|
8883
|
+
args: [messageId, ...scope.args]
|
|
7903
8884
|
});
|
|
7904
8885
|
}
|
|
7905
|
-
async function markAcknowledged(messageId) {
|
|
8886
|
+
async function markAcknowledged(messageId, sessionScope) {
|
|
7906
8887
|
const client = getClient();
|
|
8888
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
7907
8889
|
await client.execute({
|
|
7908
|
-
sql:
|
|
7909
|
-
|
|
8890
|
+
sql: `UPDATE messages SET status = 'acknowledged', processed_at = ?
|
|
8891
|
+
WHERE id = ? AND status = 'read'${scope.sql}`,
|
|
8892
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), messageId, ...scope.args]
|
|
7910
8893
|
});
|
|
7911
8894
|
}
|
|
7912
|
-
async function markProcessed(messageId) {
|
|
8895
|
+
async function markProcessed(messageId, sessionScope) {
|
|
7913
8896
|
const client = getClient();
|
|
8897
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
7914
8898
|
await client.execute({
|
|
7915
|
-
sql:
|
|
7916
|
-
|
|
8899
|
+
sql: `UPDATE messages SET status = 'processed', processed_at = ?
|
|
8900
|
+
WHERE id = ?${scope.sql}`,
|
|
8901
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), messageId, ...scope.args]
|
|
7917
8902
|
});
|
|
7918
8903
|
}
|
|
7919
|
-
async function getMessageStatus(messageId) {
|
|
8904
|
+
async function getMessageStatus(messageId, sessionScope) {
|
|
7920
8905
|
const client = getClient();
|
|
8906
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
7921
8907
|
const result = await client.execute({
|
|
7922
|
-
sql:
|
|
7923
|
-
args: [messageId]
|
|
8908
|
+
sql: `SELECT status FROM messages WHERE id = ?${scope.sql}`,
|
|
8909
|
+
args: [messageId, ...scope.args]
|
|
7924
8910
|
});
|
|
7925
8911
|
return result.rows[0]?.status ?? null;
|
|
7926
8912
|
}
|
|
7927
|
-
async function getUnacknowledgedMessages(targetAgent) {
|
|
8913
|
+
async function getUnacknowledgedMessages(targetAgent, sessionScope) {
|
|
7928
8914
|
const client = getClient();
|
|
8915
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
7929
8916
|
const result = await client.execute({
|
|
7930
8917
|
sql: `SELECT * FROM messages
|
|
7931
|
-
WHERE target_agent = ? AND status IN ('pending', 'delivered', 'read')
|
|
8918
|
+
WHERE target_agent = ? AND status IN ('pending', 'delivered', 'read')${scope.sql}
|
|
7932
8919
|
ORDER BY id`,
|
|
7933
|
-
args: [targetAgent]
|
|
8920
|
+
args: [targetAgent, ...scope.args]
|
|
7934
8921
|
});
|
|
7935
8922
|
return result.rows.map((row) => rowToMessage(row));
|
|
7936
8923
|
}
|
|
7937
|
-
async function getReadMessages(targetAgent) {
|
|
8924
|
+
async function getReadMessages(targetAgent, sessionScope) {
|
|
7938
8925
|
const client = getClient();
|
|
8926
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
7939
8927
|
const result = await client.execute({
|
|
7940
|
-
sql:
|
|
7941
|
-
|
|
8928
|
+
sql: `SELECT * FROM messages
|
|
8929
|
+
WHERE target_agent = ? AND status = 'read'${scope.sql}
|
|
8930
|
+
ORDER BY id`,
|
|
8931
|
+
args: [targetAgent, ...scope.args]
|
|
7942
8932
|
});
|
|
7943
8933
|
return result.rows.map((row) => rowToMessage(row));
|
|
7944
8934
|
}
|
|
7945
|
-
async function markFailed(messageId, reason) {
|
|
8935
|
+
async function markFailed(messageId, reason, sessionScope) {
|
|
7946
8936
|
const client = getClient();
|
|
8937
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
7947
8938
|
await client.execute({
|
|
7948
|
-
sql:
|
|
7949
|
-
|
|
8939
|
+
sql: `UPDATE messages SET status = 'failed', failed_at = ?, failure_reason = ?
|
|
8940
|
+
WHERE id = ?${scope.sql}`,
|
|
8941
|
+
args: [(/* @__PURE__ */ new Date()).toISOString(), reason, messageId, ...scope.args]
|
|
7950
8942
|
});
|
|
7951
8943
|
}
|
|
7952
|
-
async function getFailedMessages() {
|
|
8944
|
+
async function getFailedMessages(sessionScope) {
|
|
7953
8945
|
const client = getClient();
|
|
8946
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
7954
8947
|
const result = await client.execute({
|
|
7955
|
-
sql:
|
|
7956
|
-
args: []
|
|
8948
|
+
sql: `SELECT * FROM messages WHERE status = 'failed'${scope.sql} ORDER BY created_at DESC`,
|
|
8949
|
+
args: [...scope.args]
|
|
7957
8950
|
});
|
|
7958
8951
|
return result.rows.map((row) => rowToMessage(row));
|
|
7959
8952
|
}
|
|
7960
|
-
async function retryPendingMessages() {
|
|
8953
|
+
async function retryPendingMessages(sessionScope) {
|
|
7961
8954
|
const client = getClient();
|
|
8955
|
+
const scope = strictSessionScopeFilter(sessionScope);
|
|
7962
8956
|
const result = await client.execute({
|
|
7963
8957
|
sql: `SELECT * FROM messages
|
|
7964
|
-
WHERE status = 'pending' AND retry_count <
|
|
8958
|
+
WHERE status = 'pending' AND retry_count < ?${scope.sql}
|
|
7965
8959
|
ORDER BY id`,
|
|
7966
|
-
args: [MAX_RETRIES3]
|
|
8960
|
+
args: [MAX_RETRIES3, ...scope.args]
|
|
7967
8961
|
});
|
|
7968
8962
|
let delivered = 0;
|
|
7969
8963
|
for (const row of result.rows) {
|
|
@@ -7982,6 +8976,7 @@ var init_messaging = __esm({
|
|
|
7982
8976
|
"use strict";
|
|
7983
8977
|
init_database();
|
|
7984
8978
|
init_tmux_routing();
|
|
8979
|
+
init_task_scope();
|
|
7985
8980
|
MAX_RETRIES3 = 10;
|
|
7986
8981
|
_wsClientSend = null;
|
|
7987
8982
|
}
|
|
@@ -8179,11 +9174,11 @@ init_crm_bridge();
|
|
|
8179
9174
|
|
|
8180
9175
|
// src/lib/pipeline-router.ts
|
|
8181
9176
|
init_database();
|
|
8182
|
-
import
|
|
9177
|
+
import crypto3 from "crypto";
|
|
8183
9178
|
async function sinkConversationStore(msg, agentResponse, agentName) {
|
|
8184
9179
|
try {
|
|
8185
9180
|
const client = getClient();
|
|
8186
|
-
const id =
|
|
9181
|
+
const id = crypto3.randomUUID();
|
|
8187
9182
|
const mediaJson = msg.media ? JSON.stringify(msg.media) : null;
|
|
8188
9183
|
await client.execute({
|
|
8189
9184
|
sql: `INSERT INTO conversations
|
|
@@ -8233,7 +9228,7 @@ async function sinkMemory(msg, agentResponse, agentName) {
|
|
|
8233
9228
|
].filter(Boolean).join("\n");
|
|
8234
9229
|
const vector = await embed2(rawText);
|
|
8235
9230
|
await writeMemory2({
|
|
8236
|
-
id:
|
|
9231
|
+
id: crypto3.randomUUID(),
|
|
8237
9232
|
agent_id: agentName ?? "gateway",
|
|
8238
9233
|
agent_role: "gateway",
|
|
8239
9234
|
session_id: `gateway-${msg.platform}`,
|
|
@@ -9586,7 +10581,7 @@ var OllamaProvider = class {
|
|
|
9586
10581
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
9587
10582
|
import { homedir } from "os";
|
|
9588
10583
|
import { join } from "path";
|
|
9589
|
-
import { mkdirSync as
|
|
10584
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
9590
10585
|
var INITIAL_BACKOFF_MS = 1e3;
|
|
9591
10586
|
var MAX_BACKOFF_MS = 3e5;
|
|
9592
10587
|
var BACKOFF_MULTIPLIER = 2;
|
|
@@ -9612,7 +10607,7 @@ var WhatsAppAdapter = class {
|
|
|
9612
10607
|
disconnectedAt = 0;
|
|
9613
10608
|
async connect(config2) {
|
|
9614
10609
|
this.authDir = config2.credentials.authDir ?? AUTH_DIR;
|
|
9615
|
-
|
|
10610
|
+
mkdirSync3(this.authDir, { recursive: true });
|
|
9616
10611
|
const baileys = await import("@whiskeysockets/baileys");
|
|
9617
10612
|
const { makeWASocket, useMultiFileAuthState, fetchLatestBaileysVersion, DisconnectReason, makeCacheableSignalKeyStore } = baileys;
|
|
9618
10613
|
const { state, saveCreds } = await useMultiFileAuthState(this.authDir);
|
|
@@ -10914,12 +11909,12 @@ var SlackAdapter = class {
|
|
|
10914
11909
|
// src/gateway/adapters/imessage.ts
|
|
10915
11910
|
import { execFile } from "child_process";
|
|
10916
11911
|
import { promisify } from "util";
|
|
10917
|
-
import
|
|
10918
|
-
import
|
|
11912
|
+
import os6 from "os";
|
|
11913
|
+
import path9 from "path";
|
|
10919
11914
|
var execFileAsync = promisify(execFile);
|
|
10920
11915
|
var POLL_INTERVAL_MS = 5e3;
|
|
10921
|
-
var MESSAGES_DB_PATH =
|
|
10922
|
-
process.env.HOME ??
|
|
11916
|
+
var MESSAGES_DB_PATH = path9.join(
|
|
11917
|
+
process.env.HOME ?? os6.homedir(),
|
|
10923
11918
|
"Library/Messages/chat.db"
|
|
10924
11919
|
);
|
|
10925
11920
|
var IMessageAdapter = class {
|
|
@@ -11762,11 +12757,11 @@ async function ensureCRMContact(info) {
|
|
|
11762
12757
|
}
|
|
11763
12758
|
|
|
11764
12759
|
// src/automation/trigger-engine.ts
|
|
11765
|
-
import { readFileSync as
|
|
12760
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, existsSync as existsSync17, mkdirSync as mkdirSync9 } from "fs";
|
|
11766
12761
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
11767
|
-
import
|
|
11768
|
-
import
|
|
11769
|
-
var TRIGGERS_PATH =
|
|
12762
|
+
import path21 from "path";
|
|
12763
|
+
import os13 from "os";
|
|
12764
|
+
var TRIGGERS_PATH = path21.join(os13.homedir(), ".exe-os", "triggers.json");
|
|
11770
12765
|
var GRAPH_API_VERSION = "v21.0";
|
|
11771
12766
|
function substituteTemplate(template, record) {
|
|
11772
12767
|
return template.replace(
|
|
@@ -11820,9 +12815,9 @@ function evaluateConditions(conditions, record) {
|
|
|
11820
12815
|
return conditions.every((c) => evaluateCondition(c, record));
|
|
11821
12816
|
}
|
|
11822
12817
|
function loadTriggers(project) {
|
|
11823
|
-
if (!
|
|
12818
|
+
if (!existsSync17(TRIGGERS_PATH)) return [];
|
|
11824
12819
|
try {
|
|
11825
|
-
const raw =
|
|
12820
|
+
const raw = readFileSync14(TRIGGERS_PATH, "utf-8");
|
|
11826
12821
|
const all = JSON.parse(raw);
|
|
11827
12822
|
if (!Array.isArray(all)) return [];
|
|
11828
12823
|
if (project) {
|