@askexenow/exe-os 0.8.36 → 0.8.37
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 +9 -1
- package/dist/bin/backfill-responses.js +9 -1
- package/dist/bin/cli.js +255 -31
- package/dist/bin/exe-assign.js +9 -1
- package/dist/bin/exe-boot.js +554 -23
- package/dist/bin/exe-dispatch.js +43 -3
- package/dist/bin/exe-export-behaviors.js +7 -0
- package/dist/bin/exe-gateway.js +57 -9
- package/dist/bin/exe-heartbeat.js +2 -1
- package/dist/bin/exe-kill.js +7 -0
- package/dist/bin/exe-launch-agent.js +8 -1
- package/dist/bin/exe-link.js +503 -12
- package/dist/bin/exe-pending-messages.js +2 -1
- package/dist/bin/exe-pending-reviews.js +2 -1
- package/dist/bin/exe-review.js +9 -1
- package/dist/bin/exe-search.js +9 -1
- package/dist/bin/exe-session-cleanup.js +11 -2
- package/dist/bin/git-sweep.js +9 -1
- package/dist/bin/graph-backfill.js +7 -0
- package/dist/bin/graph-export.js +7 -0
- package/dist/bin/install.js +35 -5
- package/dist/bin/scan-tasks.js +9 -1
- package/dist/bin/shard-migrate.js +7 -0
- package/dist/bin/wiki-sync.js +7 -0
- package/dist/gateway/index.js +57 -9
- package/dist/hooks/bug-report-worker.js +45 -5
- package/dist/hooks/commit-complete.js +9 -1
- package/dist/hooks/error-recall.js +10 -2
- package/dist/hooks/exe-heartbeat-hook.js +1 -1
- package/dist/hooks/ingest-worker.js +56 -8
- package/dist/hooks/ingest.js +1 -1
- package/dist/hooks/instructions-loaded.js +10 -2
- package/dist/hooks/notification.js +10 -2
- package/dist/hooks/post-compact.js +10 -2
- package/dist/hooks/pre-compact.js +10 -2
- package/dist/hooks/pre-tool-use.js +10 -2
- package/dist/hooks/prompt-ingest-worker.js +9 -1
- package/dist/hooks/prompt-submit.js +56 -8
- package/dist/hooks/response-ingest-worker.js +9 -1
- package/dist/hooks/session-end.js +10 -2
- package/dist/hooks/session-start.js +10 -2
- package/dist/hooks/stop.js +10 -2
- package/dist/hooks/subagent-stop.js +10 -2
- package/dist/hooks/summary-worker.js +512 -13
- package/dist/index.js +65 -15
- package/dist/lib/cloud-sync.js +502 -11
- package/dist/lib/exe-daemon.js +73 -23
- package/dist/lib/hybrid-search.js +9 -1
- package/dist/lib/messaging.js +43 -3
- package/dist/lib/store.js +9 -1
- package/dist/lib/tasks.js +47 -7
- package/dist/lib/tmux-routing.js +45 -3
- package/dist/mcp/server.js +73 -16
- package/dist/mcp/tools/create-task.js +48 -8
- package/dist/mcp/tools/deactivate-behavior.js +1 -1
- package/dist/mcp/tools/list-tasks.js +2 -1
- package/dist/mcp/tools/send-message.js +46 -6
- package/dist/mcp/tools/update-task.js +3 -2
- package/dist/runtime/index.js +54 -4
- package/dist/tui/App.js +54 -4
- package/package.json +2 -2
- package/src/commands/exe/afk.md +116 -0
package/dist/bin/exe-link.js
CHANGED
|
@@ -623,10 +623,23 @@ var cloud_sync_exports = {};
|
|
|
623
623
|
__export(cloud_sync_exports, {
|
|
624
624
|
buildRosterBlob: () => buildRosterBlob,
|
|
625
625
|
cloudPull: () => cloudPull,
|
|
626
|
+
cloudPullBehaviors: () => cloudPullBehaviors,
|
|
627
|
+
cloudPullBlob: () => cloudPullBlob,
|
|
628
|
+
cloudPullConversations: () => cloudPullConversations,
|
|
629
|
+
cloudPullDocuments: () => cloudPullDocuments,
|
|
630
|
+
cloudPullGraphRAG: () => cloudPullGraphRAG,
|
|
626
631
|
cloudPullRoster: () => cloudPullRoster,
|
|
632
|
+
cloudPullTasks: () => cloudPullTasks,
|
|
627
633
|
cloudPush: () => cloudPush,
|
|
634
|
+
cloudPushBehaviors: () => cloudPushBehaviors,
|
|
635
|
+
cloudPushBlob: () => cloudPushBlob,
|
|
636
|
+
cloudPushConversations: () => cloudPushConversations,
|
|
637
|
+
cloudPushDocuments: () => cloudPushDocuments,
|
|
638
|
+
cloudPushGraphRAG: () => cloudPushGraphRAG,
|
|
628
639
|
cloudPushRoster: () => cloudPushRoster,
|
|
640
|
+
cloudPushTasks: () => cloudPushTasks,
|
|
629
641
|
cloudSync: () => cloudSync,
|
|
642
|
+
mergeConfig: () => mergeConfig,
|
|
630
643
|
mergeRosterFromRemote: () => mergeRosterFromRemote
|
|
631
644
|
});
|
|
632
645
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync, mkdirSync as mkdirSync2, appendFileSync } from "fs";
|
|
@@ -640,6 +653,17 @@ function logError(msg) {
|
|
|
640
653
|
} catch {
|
|
641
654
|
}
|
|
642
655
|
}
|
|
656
|
+
async function fetchWithRetry(url, init) {
|
|
657
|
+
const attempt = async () => {
|
|
658
|
+
const signal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
|
|
659
|
+
return fetch(url, { ...init, signal });
|
|
660
|
+
};
|
|
661
|
+
const resp = await attempt();
|
|
662
|
+
if (resp.status >= 500) {
|
|
663
|
+
return attempt();
|
|
664
|
+
}
|
|
665
|
+
return resp;
|
|
666
|
+
}
|
|
643
667
|
function assertSecureEndpoint(endpoint) {
|
|
644
668
|
if (endpoint.startsWith("https://")) return;
|
|
645
669
|
if (endpoint.startsWith("http://")) {
|
|
@@ -661,7 +685,7 @@ async function cloudPush(records, maxVersion, config) {
|
|
|
661
685
|
const json = JSON.stringify(records);
|
|
662
686
|
const compressed = compress(Buffer.from(json, "utf8"));
|
|
663
687
|
const blob = encryptSyncBlob(compressed);
|
|
664
|
-
const resp = await
|
|
688
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/push`, {
|
|
665
689
|
method: "POST",
|
|
666
690
|
headers: {
|
|
667
691
|
Authorization: `Bearer ${config.apiKey}`,
|
|
@@ -679,7 +703,7 @@ async function cloudPush(records, maxVersion, config) {
|
|
|
679
703
|
async function cloudPull(sinceVersion, config) {
|
|
680
704
|
assertSecureEndpoint(config.endpoint);
|
|
681
705
|
try {
|
|
682
|
-
const response = await
|
|
706
|
+
const response = await fetchWithRetry(`${config.endpoint}/sync/pull`, {
|
|
683
707
|
method: "POST",
|
|
684
708
|
headers: {
|
|
685
709
|
Authorization: `Bearer ${config.apiKey}`,
|
|
@@ -795,20 +819,87 @@ async function cloudSync(config) {
|
|
|
795
819
|
try {
|
|
796
820
|
await cloudPushRoster(config);
|
|
797
821
|
} catch (err) {
|
|
798
|
-
|
|
799
|
-
`);
|
|
822
|
+
logError(`[cloud-sync] Roster push: ${err instanceof Error ? err.message : String(err)}`);
|
|
800
823
|
}
|
|
801
824
|
try {
|
|
802
825
|
await cloudPullRoster(config);
|
|
803
826
|
} catch (err) {
|
|
804
|
-
|
|
805
|
-
|
|
827
|
+
logError(`[cloud-sync] Roster pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
828
|
+
}
|
|
829
|
+
let behaviorsResult = { pushed: false, pulled: 0 };
|
|
830
|
+
try {
|
|
831
|
+
behaviorsResult.pushed = await cloudPushBehaviors(config);
|
|
832
|
+
} catch (err) {
|
|
833
|
+
logError(`[cloud-sync] Behaviors push: ${err instanceof Error ? err.message : String(err)}`);
|
|
834
|
+
}
|
|
835
|
+
try {
|
|
836
|
+
const pullResult2 = await cloudPullBehaviors(config);
|
|
837
|
+
behaviorsResult.pulled = pullResult2.pulled;
|
|
838
|
+
} catch (err) {
|
|
839
|
+
logError(`[cloud-sync] Behaviors pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
840
|
+
}
|
|
841
|
+
let graphragResult = { pushed: false, pulled: 0 };
|
|
842
|
+
try {
|
|
843
|
+
graphragResult.pushed = await cloudPushGraphRAG(config);
|
|
844
|
+
} catch (err) {
|
|
845
|
+
logError(`[cloud-sync] GraphRAG push: ${err instanceof Error ? err.message : String(err)}`);
|
|
846
|
+
}
|
|
847
|
+
try {
|
|
848
|
+
const pullResult2 = await cloudPullGraphRAG(config);
|
|
849
|
+
graphragResult.pulled = pullResult2.pulled;
|
|
850
|
+
} catch (err) {
|
|
851
|
+
logError(`[cloud-sync] GraphRAG pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
852
|
+
}
|
|
853
|
+
let tasksResult = { pushed: false, pulled: 0 };
|
|
854
|
+
try {
|
|
855
|
+
tasksResult.pushed = await cloudPushTasks(config);
|
|
856
|
+
} catch (err) {
|
|
857
|
+
logError(`[cloud-sync] Tasks push: ${err instanceof Error ? err.message : String(err)}`);
|
|
806
858
|
}
|
|
807
|
-
|
|
859
|
+
try {
|
|
860
|
+
const pullResult2 = await cloudPullTasks(config);
|
|
861
|
+
tasksResult.pulled = pullResult2.pulled;
|
|
862
|
+
} catch (err) {
|
|
863
|
+
logError(`[cloud-sync] Tasks pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
864
|
+
}
|
|
865
|
+
let conversationsResult = { pushed: false, pulled: 0 };
|
|
866
|
+
try {
|
|
867
|
+
conversationsResult.pushed = await cloudPushConversations(config);
|
|
868
|
+
} catch (err) {
|
|
869
|
+
logError(`[cloud-sync] Conversations push: ${err instanceof Error ? err.message : String(err)}`);
|
|
870
|
+
}
|
|
871
|
+
try {
|
|
872
|
+
const pullResult2 = await cloudPullConversations(config);
|
|
873
|
+
conversationsResult.pulled = pullResult2.pulled;
|
|
874
|
+
} catch (err) {
|
|
875
|
+
logError(`[cloud-sync] Conversations pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
876
|
+
}
|
|
877
|
+
let documentsResult = { pushed: false, pulled: 0 };
|
|
878
|
+
try {
|
|
879
|
+
documentsResult.pushed = await cloudPushDocuments(config);
|
|
880
|
+
} catch (err) {
|
|
881
|
+
logError(`[cloud-sync] Documents push: ${err instanceof Error ? err.message : String(err)}`);
|
|
882
|
+
}
|
|
883
|
+
try {
|
|
884
|
+
const pullResult2 = await cloudPullDocuments(config);
|
|
885
|
+
documentsResult.pulled = pullResult2.pulled;
|
|
886
|
+
} catch (err) {
|
|
887
|
+
logError(`[cloud-sync] Documents pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
888
|
+
}
|
|
889
|
+
return {
|
|
890
|
+
pushed,
|
|
891
|
+
pulled,
|
|
892
|
+
behaviors: behaviorsResult,
|
|
893
|
+
graphrag: graphragResult,
|
|
894
|
+
tasks: tasksResult,
|
|
895
|
+
conversations: conversationsResult,
|
|
896
|
+
documents: documentsResult
|
|
897
|
+
};
|
|
808
898
|
}
|
|
809
899
|
function buildRosterBlob(paths) {
|
|
810
900
|
const rosterPath = paths?.rosterPath ?? path6.join(EXE_AI_DIR, "exe-employees.json");
|
|
811
901
|
const identityDir = paths?.identityDir ?? path6.join(EXE_AI_DIR, "identity");
|
|
902
|
+
const configPath = paths?.configPath ?? path6.join(EXE_AI_DIR, "config.json");
|
|
812
903
|
let roster = [];
|
|
813
904
|
if (existsSync6(rosterPath)) {
|
|
814
905
|
try {
|
|
@@ -825,9 +916,16 @@ function buildRosterBlob(paths) {
|
|
|
825
916
|
}
|
|
826
917
|
}
|
|
827
918
|
}
|
|
828
|
-
|
|
919
|
+
let config;
|
|
920
|
+
if (existsSync6(configPath)) {
|
|
921
|
+
try {
|
|
922
|
+
config = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
923
|
+
} catch {
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
const content = JSON.stringify({ roster, identities, config });
|
|
829
927
|
const hash = Buffer.from(content).length;
|
|
830
|
-
return { roster, identities, version: hash };
|
|
928
|
+
return { roster, identities, config, version: hash };
|
|
831
929
|
}
|
|
832
930
|
async function cloudPushRoster(config) {
|
|
833
931
|
assertSecureEndpoint(config.endpoint);
|
|
@@ -846,7 +944,7 @@ async function cloudPushRoster(config) {
|
|
|
846
944
|
const json = JSON.stringify(blob);
|
|
847
945
|
const compressed = compress(Buffer.from(json, "utf8"));
|
|
848
946
|
const encrypted = encryptSyncBlob(compressed);
|
|
849
|
-
const resp = await
|
|
947
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/push-roster`, {
|
|
850
948
|
method: "POST",
|
|
851
949
|
headers: {
|
|
852
950
|
Authorization: `Bearer ${config.apiKey}`,
|
|
@@ -875,7 +973,7 @@ async function cloudPushRoster(config) {
|
|
|
875
973
|
async function cloudPullRoster(config) {
|
|
876
974
|
assertSecureEndpoint(config.endpoint);
|
|
877
975
|
try {
|
|
878
|
-
const resp = await
|
|
976
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/pull-roster`, {
|
|
879
977
|
method: "GET",
|
|
880
978
|
headers: {
|
|
881
979
|
Authorization: `Bearer ${config.apiKey}`,
|
|
@@ -895,6 +993,20 @@ async function cloudPullRoster(config) {
|
|
|
895
993
|
return { added: 0 };
|
|
896
994
|
}
|
|
897
995
|
}
|
|
996
|
+
function mergeConfig(remoteConfig, configPath) {
|
|
997
|
+
const cfgPath = configPath ?? path6.join(EXE_AI_DIR, "config.json");
|
|
998
|
+
let local = {};
|
|
999
|
+
if (existsSync6(cfgPath)) {
|
|
1000
|
+
try {
|
|
1001
|
+
local = JSON.parse(readFileSync5(cfgPath, "utf-8"));
|
|
1002
|
+
} catch {
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
const merged = { ...remoteConfig, ...local };
|
|
1006
|
+
const dir = path6.dirname(cfgPath);
|
|
1007
|
+
if (!existsSync6(dir)) mkdirSync2(dir, { recursive: true });
|
|
1008
|
+
writeFileSync2(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
1009
|
+
}
|
|
898
1010
|
async function mergeRosterFromRemote(remote, paths) {
|
|
899
1011
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
900
1012
|
const identityDir = paths?.identityDir ?? path6.join(EXE_AI_DIR, "identity");
|
|
@@ -921,9 +1033,387 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
921
1033
|
if (added > 0) {
|
|
922
1034
|
await saveEmployees(localEmployees, rosterPath);
|
|
923
1035
|
}
|
|
1036
|
+
if (remote.config && Object.keys(remote.config).length > 0) {
|
|
1037
|
+
try {
|
|
1038
|
+
mergeConfig(remote.config, paths?.configPath);
|
|
1039
|
+
} catch {
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
924
1042
|
return { added };
|
|
925
1043
|
}
|
|
926
|
-
|
|
1044
|
+
async function cloudPushBlob(route, data, metaKey, config) {
|
|
1045
|
+
if (data.length === 0) return { ok: true };
|
|
1046
|
+
assertSecureEndpoint(config.endpoint);
|
|
1047
|
+
const json = JSON.stringify(data);
|
|
1048
|
+
const version = Buffer.from(json).length;
|
|
1049
|
+
try {
|
|
1050
|
+
const client = getClient();
|
|
1051
|
+
const meta = await client.execute({
|
|
1052
|
+
sql: "SELECT value FROM sync_meta WHERE key = ?",
|
|
1053
|
+
args: [metaKey]
|
|
1054
|
+
});
|
|
1055
|
+
const lastVersion = meta.rows.length > 0 ? Number(meta.rows[0].value) : 0;
|
|
1056
|
+
if (version === lastVersion) return { ok: true };
|
|
1057
|
+
} catch {
|
|
1058
|
+
}
|
|
1059
|
+
try {
|
|
1060
|
+
const compressed = compress(Buffer.from(json, "utf8"));
|
|
1061
|
+
const encrypted = encryptSyncBlob(compressed);
|
|
1062
|
+
const resp = await fetchWithRetry(`${config.endpoint}${route}`, {
|
|
1063
|
+
method: "POST",
|
|
1064
|
+
headers: {
|
|
1065
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
1066
|
+
"Content-Type": "application/json",
|
|
1067
|
+
"X-Device-Id": loadDeviceId()
|
|
1068
|
+
},
|
|
1069
|
+
body: JSON.stringify({ blob: encrypted })
|
|
1070
|
+
});
|
|
1071
|
+
if (resp.ok) {
|
|
1072
|
+
try {
|
|
1073
|
+
const client = getClient();
|
|
1074
|
+
await client.execute({
|
|
1075
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES (?, ?)",
|
|
1076
|
+
args: [metaKey, String(version)]
|
|
1077
|
+
});
|
|
1078
|
+
} catch {
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
return { ok: resp.ok };
|
|
1082
|
+
} catch (err) {
|
|
1083
|
+
logError(`[cloud-sync] PUSH ${route}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1084
|
+
return { ok: false };
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
async function cloudPullBlob(route, config) {
|
|
1088
|
+
assertSecureEndpoint(config.endpoint);
|
|
1089
|
+
try {
|
|
1090
|
+
const resp = await fetchWithRetry(`${config.endpoint}${route}`, {
|
|
1091
|
+
method: "GET",
|
|
1092
|
+
headers: {
|
|
1093
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
1094
|
+
"X-Device-Id": loadDeviceId()
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
1097
|
+
if (!resp.ok) return null;
|
|
1098
|
+
const data = await resp.json();
|
|
1099
|
+
if (!data.blob) return null;
|
|
1100
|
+
const compressed = decryptSyncBlob(data.blob);
|
|
1101
|
+
const json = decompress(compressed).toString("utf8");
|
|
1102
|
+
return JSON.parse(json);
|
|
1103
|
+
} catch (err) {
|
|
1104
|
+
logError(`[cloud-sync] PULL ${route}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1105
|
+
return null;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
async function cloudPushBehaviors(config) {
|
|
1109
|
+
const client = getClient();
|
|
1110
|
+
const result = await client.execute("SELECT * FROM behaviors");
|
|
1111
|
+
const rows = result.rows;
|
|
1112
|
+
const { ok } = await cloudPushBlob(
|
|
1113
|
+
"/sync/push-behaviors",
|
|
1114
|
+
rows,
|
|
1115
|
+
"last_behaviors_push_version",
|
|
1116
|
+
config
|
|
1117
|
+
);
|
|
1118
|
+
return ok;
|
|
1119
|
+
}
|
|
1120
|
+
async function cloudPullBehaviors(config) {
|
|
1121
|
+
const remoteBehaviors = await cloudPullBlob(
|
|
1122
|
+
"/sync/pull-behaviors",
|
|
1123
|
+
config
|
|
1124
|
+
);
|
|
1125
|
+
if (!remoteBehaviors || remoteBehaviors.length === 0) return { pulled: 0 };
|
|
1126
|
+
const client = getClient();
|
|
1127
|
+
let pulled = 0;
|
|
1128
|
+
for (const behavior of remoteBehaviors) {
|
|
1129
|
+
const existing = await client.execute({
|
|
1130
|
+
sql: `SELECT COUNT(*) as cnt FROM behaviors
|
|
1131
|
+
WHERE agent_id = ? AND content = ?`,
|
|
1132
|
+
args: [behavior.agent_id, behavior.content]
|
|
1133
|
+
});
|
|
1134
|
+
if (Number(existing.rows[0]?.cnt) > 0) continue;
|
|
1135
|
+
await client.execute({
|
|
1136
|
+
sql: `INSERT OR IGNORE INTO behaviors
|
|
1137
|
+
(id, agent_id, project_name, domain, content, active, priority, created_at, updated_at)
|
|
1138
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1139
|
+
args: [
|
|
1140
|
+
behavior.id,
|
|
1141
|
+
behavior.agent_id,
|
|
1142
|
+
behavior.project_name ?? null,
|
|
1143
|
+
behavior.domain ?? null,
|
|
1144
|
+
behavior.content,
|
|
1145
|
+
behavior.active,
|
|
1146
|
+
behavior.priority ?? "p1",
|
|
1147
|
+
behavior.created_at,
|
|
1148
|
+
behavior.updated_at
|
|
1149
|
+
]
|
|
1150
|
+
});
|
|
1151
|
+
pulled++;
|
|
1152
|
+
}
|
|
1153
|
+
return { pulled };
|
|
1154
|
+
}
|
|
1155
|
+
async function cloudPushGraphRAG(config) {
|
|
1156
|
+
const client = getClient();
|
|
1157
|
+
const [entities, relationships, aliases, entityMems, relMems, hyperedges, hyperedgeNodes] = await Promise.all([
|
|
1158
|
+
client.execute("SELECT * FROM entities"),
|
|
1159
|
+
client.execute("SELECT * FROM relationships"),
|
|
1160
|
+
client.execute("SELECT * FROM entity_aliases"),
|
|
1161
|
+
client.execute("SELECT * FROM entity_memories"),
|
|
1162
|
+
client.execute("SELECT * FROM relationship_memories"),
|
|
1163
|
+
client.execute("SELECT * FROM hyperedges"),
|
|
1164
|
+
client.execute("SELECT * FROM hyperedge_nodes")
|
|
1165
|
+
]);
|
|
1166
|
+
const blob = {
|
|
1167
|
+
entities: entities.rows,
|
|
1168
|
+
relationships: relationships.rows,
|
|
1169
|
+
entity_aliases: aliases.rows,
|
|
1170
|
+
entity_memories: entityMems.rows,
|
|
1171
|
+
relationship_memories: relMems.rows,
|
|
1172
|
+
hyperedges: hyperedges.rows,
|
|
1173
|
+
hyperedge_nodes: hyperedgeNodes.rows
|
|
1174
|
+
};
|
|
1175
|
+
const { ok } = await cloudPushBlob(
|
|
1176
|
+
"/sync/push-graphrag",
|
|
1177
|
+
[blob],
|
|
1178
|
+
"last_graphrag_push_version",
|
|
1179
|
+
config
|
|
1180
|
+
);
|
|
1181
|
+
return ok;
|
|
1182
|
+
}
|
|
1183
|
+
async function cloudPullGraphRAG(config) {
|
|
1184
|
+
const data = await cloudPullBlob(
|
|
1185
|
+
"/sync/pull-graphrag",
|
|
1186
|
+
config
|
|
1187
|
+
);
|
|
1188
|
+
if (!data || data.length === 0) return { pulled: 0 };
|
|
1189
|
+
const blob = data[0];
|
|
1190
|
+
const client = getClient();
|
|
1191
|
+
let pulled = 0;
|
|
1192
|
+
if (blob.entities.length > 0) {
|
|
1193
|
+
const stmts = blob.entities.map((e) => ({
|
|
1194
|
+
sql: `INSERT OR IGNORE INTO entities (id, name, type, first_seen, last_seen, properties)
|
|
1195
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
1196
|
+
args: [e.id, e.name, e.type, e.first_seen, e.last_seen, e.properties ?? "{}"]
|
|
1197
|
+
}));
|
|
1198
|
+
await client.batch(stmts, "write");
|
|
1199
|
+
pulled += stmts.length;
|
|
1200
|
+
}
|
|
1201
|
+
if (blob.relationships.length > 0) {
|
|
1202
|
+
const stmts = blob.relationships.map((r) => ({
|
|
1203
|
+
sql: `INSERT OR IGNORE INTO relationships
|
|
1204
|
+
(id, source_entity_id, target_entity_id, type, weight, timestamp, properties, confidence, confidence_label)
|
|
1205
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1206
|
+
args: [
|
|
1207
|
+
r.id,
|
|
1208
|
+
r.source_entity_id,
|
|
1209
|
+
r.target_entity_id,
|
|
1210
|
+
r.type,
|
|
1211
|
+
r.weight ?? 1,
|
|
1212
|
+
r.timestamp,
|
|
1213
|
+
r.properties ?? "{}",
|
|
1214
|
+
r.confidence ?? 1,
|
|
1215
|
+
r.confidence_label ?? "extracted"
|
|
1216
|
+
]
|
|
1217
|
+
}));
|
|
1218
|
+
await client.batch(stmts, "write");
|
|
1219
|
+
pulled += stmts.length;
|
|
1220
|
+
}
|
|
1221
|
+
if (blob.entity_aliases.length > 0) {
|
|
1222
|
+
const stmts = blob.entity_aliases.map((a) => ({
|
|
1223
|
+
sql: `INSERT OR IGNORE INTO entity_aliases (alias, canonical_entity_id) VALUES (?, ?)`,
|
|
1224
|
+
args: [a.alias, a.canonical_entity_id]
|
|
1225
|
+
}));
|
|
1226
|
+
await client.batch(stmts, "write");
|
|
1227
|
+
pulled += stmts.length;
|
|
1228
|
+
}
|
|
1229
|
+
if (blob.entity_memories.length > 0) {
|
|
1230
|
+
const stmts = blob.entity_memories.map((em) => ({
|
|
1231
|
+
sql: `INSERT OR IGNORE INTO entity_memories (entity_id, memory_id) VALUES (?, ?)`,
|
|
1232
|
+
args: [em.entity_id, em.memory_id]
|
|
1233
|
+
}));
|
|
1234
|
+
await client.batch(stmts, "write");
|
|
1235
|
+
pulled += stmts.length;
|
|
1236
|
+
}
|
|
1237
|
+
if (blob.relationship_memories.length > 0) {
|
|
1238
|
+
const stmts = blob.relationship_memories.map((rm) => ({
|
|
1239
|
+
sql: `INSERT OR IGNORE INTO relationship_memories (relationship_id, memory_id) VALUES (?, ?)`,
|
|
1240
|
+
args: [rm.relationship_id, rm.memory_id]
|
|
1241
|
+
}));
|
|
1242
|
+
await client.batch(stmts, "write");
|
|
1243
|
+
pulled += stmts.length;
|
|
1244
|
+
}
|
|
1245
|
+
if (blob.hyperedges.length > 0) {
|
|
1246
|
+
const stmts = blob.hyperedges.map((h) => ({
|
|
1247
|
+
sql: `INSERT OR IGNORE INTO hyperedges (id, label, relation, confidence, timestamp)
|
|
1248
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
1249
|
+
args: [h.id, h.label, h.relation, h.confidence ?? 1, h.timestamp]
|
|
1250
|
+
}));
|
|
1251
|
+
await client.batch(stmts, "write");
|
|
1252
|
+
pulled += stmts.length;
|
|
1253
|
+
}
|
|
1254
|
+
if (blob.hyperedge_nodes.length > 0) {
|
|
1255
|
+
const stmts = blob.hyperedge_nodes.map((hn) => ({
|
|
1256
|
+
sql: `INSERT OR IGNORE INTO hyperedge_nodes (hyperedge_id, entity_id) VALUES (?, ?)`,
|
|
1257
|
+
args: [hn.hyperedge_id, hn.entity_id]
|
|
1258
|
+
}));
|
|
1259
|
+
await client.batch(stmts, "write");
|
|
1260
|
+
pulled += stmts.length;
|
|
1261
|
+
}
|
|
1262
|
+
return { pulled };
|
|
1263
|
+
}
|
|
1264
|
+
async function cloudPushTasks(config) {
|
|
1265
|
+
const client = getClient();
|
|
1266
|
+
const result = await client.execute("SELECT * FROM tasks");
|
|
1267
|
+
const rows = result.rows;
|
|
1268
|
+
const { ok } = await cloudPushBlob(
|
|
1269
|
+
"/sync/push-tasks",
|
|
1270
|
+
rows,
|
|
1271
|
+
"last_tasks_push_version",
|
|
1272
|
+
config
|
|
1273
|
+
);
|
|
1274
|
+
return ok;
|
|
1275
|
+
}
|
|
1276
|
+
async function cloudPullTasks(config) {
|
|
1277
|
+
const remoteTasks = await cloudPullBlob(
|
|
1278
|
+
"/sync/pull-tasks",
|
|
1279
|
+
config
|
|
1280
|
+
);
|
|
1281
|
+
if (!remoteTasks || remoteTasks.length === 0) return { pulled: 0 };
|
|
1282
|
+
const client = getClient();
|
|
1283
|
+
const stmts = remoteTasks.map((t) => ({
|
|
1284
|
+
sql: `INSERT OR IGNORE INTO tasks
|
|
1285
|
+
(id, title, assigned_to, assigned_by, project_name, priority, status, task_file, created_at, updated_at,
|
|
1286
|
+
blocked_by, parent_task_id, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at)
|
|
1287
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1288
|
+
args: [
|
|
1289
|
+
t.id,
|
|
1290
|
+
t.title,
|
|
1291
|
+
t.assigned_to,
|
|
1292
|
+
t.assigned_by,
|
|
1293
|
+
t.project_name,
|
|
1294
|
+
t.priority ?? "p1",
|
|
1295
|
+
t.status ?? "open",
|
|
1296
|
+
t.task_file ?? null,
|
|
1297
|
+
t.created_at,
|
|
1298
|
+
t.updated_at,
|
|
1299
|
+
t.blocked_by ?? null,
|
|
1300
|
+
t.parent_task_id ?? null,
|
|
1301
|
+
t.budget_tokens ?? null,
|
|
1302
|
+
t.budget_fallback_model ?? null,
|
|
1303
|
+
t.tokens_used ?? 0,
|
|
1304
|
+
t.tokens_warned_at ?? null
|
|
1305
|
+
]
|
|
1306
|
+
}));
|
|
1307
|
+
await client.batch(stmts, "write");
|
|
1308
|
+
return { pulled: remoteTasks.length };
|
|
1309
|
+
}
|
|
1310
|
+
async function cloudPushConversations(config) {
|
|
1311
|
+
const client = getClient();
|
|
1312
|
+
const result = await client.execute("SELECT * FROM conversations");
|
|
1313
|
+
const rows = result.rows;
|
|
1314
|
+
const { ok } = await cloudPushBlob(
|
|
1315
|
+
"/sync/push-conversations",
|
|
1316
|
+
rows,
|
|
1317
|
+
"last_conversations_push_version",
|
|
1318
|
+
config
|
|
1319
|
+
);
|
|
1320
|
+
return ok;
|
|
1321
|
+
}
|
|
1322
|
+
async function cloudPullConversations(config) {
|
|
1323
|
+
const remoteConvos = await cloudPullBlob(
|
|
1324
|
+
"/sync/pull-conversations",
|
|
1325
|
+
config
|
|
1326
|
+
);
|
|
1327
|
+
if (!remoteConvos || remoteConvos.length === 0) return { pulled: 0 };
|
|
1328
|
+
const client = getClient();
|
|
1329
|
+
const stmts = remoteConvos.map((c) => ({
|
|
1330
|
+
sql: `INSERT OR IGNORE INTO conversations
|
|
1331
|
+
(id, platform, external_id, sender_id, sender_name, sender_phone, sender_email,
|
|
1332
|
+
recipient_id, channel_id, thread_id, reply_to_id, content_text, content_media,
|
|
1333
|
+
content_metadata, agent_response, agent_name, timestamp, ingested_at)
|
|
1334
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1335
|
+
args: [
|
|
1336
|
+
c.id,
|
|
1337
|
+
c.platform,
|
|
1338
|
+
c.external_id ?? null,
|
|
1339
|
+
c.sender_id,
|
|
1340
|
+
c.sender_name ?? null,
|
|
1341
|
+
c.sender_phone ?? null,
|
|
1342
|
+
c.sender_email ?? null,
|
|
1343
|
+
c.recipient_id ?? null,
|
|
1344
|
+
c.channel_id,
|
|
1345
|
+
c.thread_id ?? null,
|
|
1346
|
+
c.reply_to_id ?? null,
|
|
1347
|
+
c.content_text ?? null,
|
|
1348
|
+
c.content_media ?? null,
|
|
1349
|
+
c.content_metadata ?? null,
|
|
1350
|
+
c.agent_response ?? null,
|
|
1351
|
+
c.agent_name ?? null,
|
|
1352
|
+
c.timestamp,
|
|
1353
|
+
c.ingested_at
|
|
1354
|
+
]
|
|
1355
|
+
}));
|
|
1356
|
+
await client.batch(stmts, "write");
|
|
1357
|
+
return { pulled: remoteConvos.length };
|
|
1358
|
+
}
|
|
1359
|
+
async function cloudPushDocuments(config) {
|
|
1360
|
+
const client = getClient();
|
|
1361
|
+
const [workspaces, documents] = await Promise.all([
|
|
1362
|
+
client.execute("SELECT * FROM workspaces"),
|
|
1363
|
+
client.execute("SELECT * FROM documents")
|
|
1364
|
+
]);
|
|
1365
|
+
const blob = {
|
|
1366
|
+
workspaces: workspaces.rows,
|
|
1367
|
+
documents: documents.rows
|
|
1368
|
+
};
|
|
1369
|
+
const { ok } = await cloudPushBlob(
|
|
1370
|
+
"/sync/push-documents",
|
|
1371
|
+
[blob],
|
|
1372
|
+
"last_documents_push_version",
|
|
1373
|
+
config
|
|
1374
|
+
);
|
|
1375
|
+
return ok;
|
|
1376
|
+
}
|
|
1377
|
+
async function cloudPullDocuments(config) {
|
|
1378
|
+
const data = await cloudPullBlob(
|
|
1379
|
+
"/sync/pull-documents",
|
|
1380
|
+
config
|
|
1381
|
+
);
|
|
1382
|
+
if (!data || data.length === 0) return { pulled: 0 };
|
|
1383
|
+
const blob = data[0];
|
|
1384
|
+
const client = getClient();
|
|
1385
|
+
let pulled = 0;
|
|
1386
|
+
if (blob.workspaces.length > 0) {
|
|
1387
|
+
const stmts = blob.workspaces.map((w) => ({
|
|
1388
|
+
sql: `INSERT OR IGNORE INTO workspaces (id, slug, name, owner_agent_id, created_at, metadata)
|
|
1389
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
1390
|
+
args: [w.id, w.slug, w.name, w.owner_agent_id ?? null, w.created_at, w.metadata ?? null]
|
|
1391
|
+
}));
|
|
1392
|
+
await client.batch(stmts, "write");
|
|
1393
|
+
pulled += stmts.length;
|
|
1394
|
+
}
|
|
1395
|
+
if (blob.documents.length > 0) {
|
|
1396
|
+
const stmts = blob.documents.map((d) => ({
|
|
1397
|
+
sql: `INSERT OR IGNORE INTO documents
|
|
1398
|
+
(id, workspace_id, filename, mime, source_type, user_id, uploaded_at, metadata)
|
|
1399
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1400
|
+
args: [
|
|
1401
|
+
d.id,
|
|
1402
|
+
d.workspace_id,
|
|
1403
|
+
d.filename,
|
|
1404
|
+
d.mime ?? null,
|
|
1405
|
+
d.source_type ?? null,
|
|
1406
|
+
d.user_id ?? null,
|
|
1407
|
+
d.uploaded_at,
|
|
1408
|
+
d.metadata ?? null
|
|
1409
|
+
]
|
|
1410
|
+
}));
|
|
1411
|
+
await client.batch(stmts, "write");
|
|
1412
|
+
pulled += stmts.length;
|
|
1413
|
+
}
|
|
1414
|
+
return { pulled };
|
|
1415
|
+
}
|
|
1416
|
+
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS;
|
|
927
1417
|
var init_cloud_sync = __esm({
|
|
928
1418
|
"src/lib/cloud-sync.ts"() {
|
|
929
1419
|
"use strict";
|
|
@@ -935,6 +1425,7 @@ var init_cloud_sync = __esm({
|
|
|
935
1425
|
init_config();
|
|
936
1426
|
init_employees();
|
|
937
1427
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
1428
|
+
FETCH_TIMEOUT_MS = 3e4;
|
|
938
1429
|
}
|
|
939
1430
|
});
|
|
940
1431
|
|
|
@@ -1450,7 +1450,7 @@ var init_plan_limits = __esm({
|
|
|
1450
1450
|
import path9 from "path";
|
|
1451
1451
|
import os4 from "os";
|
|
1452
1452
|
import { fileURLToPath } from "url";
|
|
1453
|
-
var SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
1453
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
1454
1454
|
var init_tmux_routing = __esm({
|
|
1455
1455
|
"src/lib/tmux-routing.ts"() {
|
|
1456
1456
|
"use strict";
|
|
@@ -1462,6 +1462,7 @@ var init_tmux_routing = __esm({
|
|
|
1462
1462
|
init_provider_table();
|
|
1463
1463
|
init_intercom_queue();
|
|
1464
1464
|
init_plan_limits();
|
|
1465
|
+
SPAWN_LOCK_DIR = path9.join(os4.homedir(), ".exe-os", "spawn-locks");
|
|
1465
1466
|
SESSION_CACHE = path9.join(os4.homedir(), ".exe-os", "session-cache");
|
|
1466
1467
|
INTERCOM_LOG2 = path9.join(os4.homedir(), ".exe-os", "intercom.log");
|
|
1467
1468
|
DEBOUNCE_FILE = path9.join(SESSION_CACHE, "intercom-debounce.json");
|
|
@@ -1482,7 +1482,7 @@ var init_plan_limits = __esm({
|
|
|
1482
1482
|
import path11 from "path";
|
|
1483
1483
|
import os5 from "os";
|
|
1484
1484
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1485
|
-
var SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
1485
|
+
var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
|
|
1486
1486
|
var init_tmux_routing = __esm({
|
|
1487
1487
|
"src/lib/tmux-routing.ts"() {
|
|
1488
1488
|
"use strict";
|
|
@@ -1494,6 +1494,7 @@ var init_tmux_routing = __esm({
|
|
|
1494
1494
|
init_provider_table();
|
|
1495
1495
|
init_intercom_queue();
|
|
1496
1496
|
init_plan_limits();
|
|
1497
|
+
SPAWN_LOCK_DIR = path11.join(os5.homedir(), ".exe-os", "spawn-locks");
|
|
1497
1498
|
SESSION_CACHE = path11.join(os5.homedir(), ".exe-os", "session-cache");
|
|
1498
1499
|
INTERCOM_LOG2 = path11.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
1499
1500
|
DEBOUNCE_FILE = path11.join(SESSION_CACHE, "intercom-debounce.json");
|
package/dist/bin/exe-review.js
CHANGED
|
@@ -1461,7 +1461,8 @@ async function writeMemory(record) {
|
|
|
1461
1461
|
has_error: record.has_error ? 1 : 0,
|
|
1462
1462
|
raw_text: record.raw_text,
|
|
1463
1463
|
vector: record.vector,
|
|
1464
|
-
version:
|
|
1464
|
+
version: 0,
|
|
1465
|
+
// Placeholder — assigned atomically at flush time
|
|
1465
1466
|
task_id: record.task_id ?? null,
|
|
1466
1467
|
importance: record.importance ?? 5,
|
|
1467
1468
|
status: record.status ?? "active",
|
|
@@ -1495,6 +1496,13 @@ async function flushBatch() {
|
|
|
1495
1496
|
_flushing = true;
|
|
1496
1497
|
try {
|
|
1497
1498
|
const batch = _pendingRecords.slice(0);
|
|
1499
|
+
const client = getClient();
|
|
1500
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
1501
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1502
|
+
for (const row of batch) {
|
|
1503
|
+
row.version = baseVersion++;
|
|
1504
|
+
}
|
|
1505
|
+
_nextVersion = baseVersion;
|
|
1498
1506
|
const buildStmt = (row) => {
|
|
1499
1507
|
const hasVector = row.vector !== null;
|
|
1500
1508
|
const taskId = row.task_id ?? null;
|
package/dist/bin/exe-search.js
CHANGED
|
@@ -1530,7 +1530,8 @@ async function writeMemory(record) {
|
|
|
1530
1530
|
has_error: record.has_error ? 1 : 0,
|
|
1531
1531
|
raw_text: record.raw_text,
|
|
1532
1532
|
vector: record.vector,
|
|
1533
|
-
version:
|
|
1533
|
+
version: 0,
|
|
1534
|
+
// Placeholder — assigned atomically at flush time
|
|
1534
1535
|
task_id: record.task_id ?? null,
|
|
1535
1536
|
importance: record.importance ?? 5,
|
|
1536
1537
|
status: record.status ?? "active",
|
|
@@ -1564,6 +1565,13 @@ async function flushBatch() {
|
|
|
1564
1565
|
_flushing = true;
|
|
1565
1566
|
try {
|
|
1566
1567
|
const batch = _pendingRecords.slice(0);
|
|
1568
|
+
const client = getClient();
|
|
1569
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
1570
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
1571
|
+
for (const row of batch) {
|
|
1572
|
+
row.version = baseVersion++;
|
|
1573
|
+
}
|
|
1574
|
+
_nextVersion = baseVersion;
|
|
1567
1575
|
const buildStmt = (row) => {
|
|
1568
1576
|
const hasVector = row.vector !== null;
|
|
1569
1577
|
const taskId = row.task_id ?? null;
|