@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/lib/cloud-sync.js
CHANGED
|
@@ -400,6 +400,18 @@ function logError(msg) {
|
|
|
400
400
|
}
|
|
401
401
|
}
|
|
402
402
|
var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
403
|
+
var FETCH_TIMEOUT_MS = 3e4;
|
|
404
|
+
async function fetchWithRetry(url, init) {
|
|
405
|
+
const attempt = async () => {
|
|
406
|
+
const signal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
|
|
407
|
+
return fetch(url, { ...init, signal });
|
|
408
|
+
};
|
|
409
|
+
const resp = await attempt();
|
|
410
|
+
if (resp.status >= 500) {
|
|
411
|
+
return attempt();
|
|
412
|
+
}
|
|
413
|
+
return resp;
|
|
414
|
+
}
|
|
403
415
|
function assertSecureEndpoint(endpoint) {
|
|
404
416
|
if (endpoint.startsWith("https://")) return;
|
|
405
417
|
if (endpoint.startsWith("http://")) {
|
|
@@ -421,7 +433,7 @@ async function cloudPush(records, maxVersion, config) {
|
|
|
421
433
|
const json = JSON.stringify(records);
|
|
422
434
|
const compressed = compress(Buffer.from(json, "utf8"));
|
|
423
435
|
const blob = encryptSyncBlob(compressed);
|
|
424
|
-
const resp = await
|
|
436
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/push`, {
|
|
425
437
|
method: "POST",
|
|
426
438
|
headers: {
|
|
427
439
|
Authorization: `Bearer ${config.apiKey}`,
|
|
@@ -439,7 +451,7 @@ async function cloudPush(records, maxVersion, config) {
|
|
|
439
451
|
async function cloudPull(sinceVersion, config) {
|
|
440
452
|
assertSecureEndpoint(config.endpoint);
|
|
441
453
|
try {
|
|
442
|
-
const response = await
|
|
454
|
+
const response = await fetchWithRetry(`${config.endpoint}/sync/pull`, {
|
|
443
455
|
method: "POST",
|
|
444
456
|
headers: {
|
|
445
457
|
Authorization: `Bearer ${config.apiKey}`,
|
|
@@ -555,20 +567,87 @@ async function cloudSync(config) {
|
|
|
555
567
|
try {
|
|
556
568
|
await cloudPushRoster(config);
|
|
557
569
|
} catch (err) {
|
|
558
|
-
|
|
559
|
-
`);
|
|
570
|
+
logError(`[cloud-sync] Roster push: ${err instanceof Error ? err.message : String(err)}`);
|
|
560
571
|
}
|
|
561
572
|
try {
|
|
562
573
|
await cloudPullRoster(config);
|
|
563
574
|
} catch (err) {
|
|
564
|
-
|
|
565
|
-
|
|
575
|
+
logError(`[cloud-sync] Roster pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
576
|
+
}
|
|
577
|
+
let behaviorsResult = { pushed: false, pulled: 0 };
|
|
578
|
+
try {
|
|
579
|
+
behaviorsResult.pushed = await cloudPushBehaviors(config);
|
|
580
|
+
} catch (err) {
|
|
581
|
+
logError(`[cloud-sync] Behaviors push: ${err instanceof Error ? err.message : String(err)}`);
|
|
582
|
+
}
|
|
583
|
+
try {
|
|
584
|
+
const pullResult2 = await cloudPullBehaviors(config);
|
|
585
|
+
behaviorsResult.pulled = pullResult2.pulled;
|
|
586
|
+
} catch (err) {
|
|
587
|
+
logError(`[cloud-sync] Behaviors pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
588
|
+
}
|
|
589
|
+
let graphragResult = { pushed: false, pulled: 0 };
|
|
590
|
+
try {
|
|
591
|
+
graphragResult.pushed = await cloudPushGraphRAG(config);
|
|
592
|
+
} catch (err) {
|
|
593
|
+
logError(`[cloud-sync] GraphRAG push: ${err instanceof Error ? err.message : String(err)}`);
|
|
594
|
+
}
|
|
595
|
+
try {
|
|
596
|
+
const pullResult2 = await cloudPullGraphRAG(config);
|
|
597
|
+
graphragResult.pulled = pullResult2.pulled;
|
|
598
|
+
} catch (err) {
|
|
599
|
+
logError(`[cloud-sync] GraphRAG pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
600
|
+
}
|
|
601
|
+
let tasksResult = { pushed: false, pulled: 0 };
|
|
602
|
+
try {
|
|
603
|
+
tasksResult.pushed = await cloudPushTasks(config);
|
|
604
|
+
} catch (err) {
|
|
605
|
+
logError(`[cloud-sync] Tasks push: ${err instanceof Error ? err.message : String(err)}`);
|
|
566
606
|
}
|
|
567
|
-
|
|
607
|
+
try {
|
|
608
|
+
const pullResult2 = await cloudPullTasks(config);
|
|
609
|
+
tasksResult.pulled = pullResult2.pulled;
|
|
610
|
+
} catch (err) {
|
|
611
|
+
logError(`[cloud-sync] Tasks pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
612
|
+
}
|
|
613
|
+
let conversationsResult = { pushed: false, pulled: 0 };
|
|
614
|
+
try {
|
|
615
|
+
conversationsResult.pushed = await cloudPushConversations(config);
|
|
616
|
+
} catch (err) {
|
|
617
|
+
logError(`[cloud-sync] Conversations push: ${err instanceof Error ? err.message : String(err)}`);
|
|
618
|
+
}
|
|
619
|
+
try {
|
|
620
|
+
const pullResult2 = await cloudPullConversations(config);
|
|
621
|
+
conversationsResult.pulled = pullResult2.pulled;
|
|
622
|
+
} catch (err) {
|
|
623
|
+
logError(`[cloud-sync] Conversations pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
624
|
+
}
|
|
625
|
+
let documentsResult = { pushed: false, pulled: 0 };
|
|
626
|
+
try {
|
|
627
|
+
documentsResult.pushed = await cloudPushDocuments(config);
|
|
628
|
+
} catch (err) {
|
|
629
|
+
logError(`[cloud-sync] Documents push: ${err instanceof Error ? err.message : String(err)}`);
|
|
630
|
+
}
|
|
631
|
+
try {
|
|
632
|
+
const pullResult2 = await cloudPullDocuments(config);
|
|
633
|
+
documentsResult.pulled = pullResult2.pulled;
|
|
634
|
+
} catch (err) {
|
|
635
|
+
logError(`[cloud-sync] Documents pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
636
|
+
}
|
|
637
|
+
return {
|
|
638
|
+
pushed,
|
|
639
|
+
pulled,
|
|
640
|
+
behaviors: behaviorsResult,
|
|
641
|
+
graphrag: graphragResult,
|
|
642
|
+
tasks: tasksResult,
|
|
643
|
+
conversations: conversationsResult,
|
|
644
|
+
documents: documentsResult
|
|
645
|
+
};
|
|
568
646
|
}
|
|
569
647
|
function buildRosterBlob(paths) {
|
|
570
648
|
const rosterPath = paths?.rosterPath ?? path5.join(EXE_AI_DIR, "exe-employees.json");
|
|
571
649
|
const identityDir = paths?.identityDir ?? path5.join(EXE_AI_DIR, "identity");
|
|
650
|
+
const configPath = paths?.configPath ?? path5.join(EXE_AI_DIR, "config.json");
|
|
572
651
|
let roster = [];
|
|
573
652
|
if (existsSync5(rosterPath)) {
|
|
574
653
|
try {
|
|
@@ -585,9 +664,16 @@ function buildRosterBlob(paths) {
|
|
|
585
664
|
}
|
|
586
665
|
}
|
|
587
666
|
}
|
|
588
|
-
|
|
667
|
+
let config;
|
|
668
|
+
if (existsSync5(configPath)) {
|
|
669
|
+
try {
|
|
670
|
+
config = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
671
|
+
} catch {
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
const content = JSON.stringify({ roster, identities, config });
|
|
589
675
|
const hash = Buffer.from(content).length;
|
|
590
|
-
return { roster, identities, version: hash };
|
|
676
|
+
return { roster, identities, config, version: hash };
|
|
591
677
|
}
|
|
592
678
|
async function cloudPushRoster(config) {
|
|
593
679
|
assertSecureEndpoint(config.endpoint);
|
|
@@ -606,7 +692,7 @@ async function cloudPushRoster(config) {
|
|
|
606
692
|
const json = JSON.stringify(blob);
|
|
607
693
|
const compressed = compress(Buffer.from(json, "utf8"));
|
|
608
694
|
const encrypted = encryptSyncBlob(compressed);
|
|
609
|
-
const resp = await
|
|
695
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/push-roster`, {
|
|
610
696
|
method: "POST",
|
|
611
697
|
headers: {
|
|
612
698
|
Authorization: `Bearer ${config.apiKey}`,
|
|
@@ -635,7 +721,7 @@ async function cloudPushRoster(config) {
|
|
|
635
721
|
async function cloudPullRoster(config) {
|
|
636
722
|
assertSecureEndpoint(config.endpoint);
|
|
637
723
|
try {
|
|
638
|
-
const resp = await
|
|
724
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/pull-roster`, {
|
|
639
725
|
method: "GET",
|
|
640
726
|
headers: {
|
|
641
727
|
Authorization: `Bearer ${config.apiKey}`,
|
|
@@ -655,6 +741,20 @@ async function cloudPullRoster(config) {
|
|
|
655
741
|
return { added: 0 };
|
|
656
742
|
}
|
|
657
743
|
}
|
|
744
|
+
function mergeConfig(remoteConfig, configPath) {
|
|
745
|
+
const cfgPath = configPath ?? path5.join(EXE_AI_DIR, "config.json");
|
|
746
|
+
let local = {};
|
|
747
|
+
if (existsSync5(cfgPath)) {
|
|
748
|
+
try {
|
|
749
|
+
local = JSON.parse(readFileSync5(cfgPath, "utf-8"));
|
|
750
|
+
} catch {
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
const merged = { ...remoteConfig, ...local };
|
|
754
|
+
const dir = path5.dirname(cfgPath);
|
|
755
|
+
if (!existsSync5(dir)) mkdirSync2(dir, { recursive: true });
|
|
756
|
+
writeFileSync2(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
757
|
+
}
|
|
658
758
|
async function mergeRosterFromRemote(remote, paths) {
|
|
659
759
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
660
760
|
const identityDir = paths?.identityDir ?? path5.join(EXE_AI_DIR, "identity");
|
|
@@ -681,14 +781,405 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
681
781
|
if (added > 0) {
|
|
682
782
|
await saveEmployees(localEmployees, rosterPath);
|
|
683
783
|
}
|
|
784
|
+
if (remote.config && Object.keys(remote.config).length > 0) {
|
|
785
|
+
try {
|
|
786
|
+
mergeConfig(remote.config, paths?.configPath);
|
|
787
|
+
} catch {
|
|
788
|
+
}
|
|
789
|
+
}
|
|
684
790
|
return { added };
|
|
685
791
|
}
|
|
792
|
+
async function cloudPushBlob(route, data, metaKey, config) {
|
|
793
|
+
if (data.length === 0) return { ok: true };
|
|
794
|
+
assertSecureEndpoint(config.endpoint);
|
|
795
|
+
const json = JSON.stringify(data);
|
|
796
|
+
const version = Buffer.from(json).length;
|
|
797
|
+
try {
|
|
798
|
+
const client = getClient();
|
|
799
|
+
const meta = await client.execute({
|
|
800
|
+
sql: "SELECT value FROM sync_meta WHERE key = ?",
|
|
801
|
+
args: [metaKey]
|
|
802
|
+
});
|
|
803
|
+
const lastVersion = meta.rows.length > 0 ? Number(meta.rows[0].value) : 0;
|
|
804
|
+
if (version === lastVersion) return { ok: true };
|
|
805
|
+
} catch {
|
|
806
|
+
}
|
|
807
|
+
try {
|
|
808
|
+
const compressed = compress(Buffer.from(json, "utf8"));
|
|
809
|
+
const encrypted = encryptSyncBlob(compressed);
|
|
810
|
+
const resp = await fetchWithRetry(`${config.endpoint}${route}`, {
|
|
811
|
+
method: "POST",
|
|
812
|
+
headers: {
|
|
813
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
814
|
+
"Content-Type": "application/json",
|
|
815
|
+
"X-Device-Id": loadDeviceId()
|
|
816
|
+
},
|
|
817
|
+
body: JSON.stringify({ blob: encrypted })
|
|
818
|
+
});
|
|
819
|
+
if (resp.ok) {
|
|
820
|
+
try {
|
|
821
|
+
const client = getClient();
|
|
822
|
+
await client.execute({
|
|
823
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES (?, ?)",
|
|
824
|
+
args: [metaKey, String(version)]
|
|
825
|
+
});
|
|
826
|
+
} catch {
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return { ok: resp.ok };
|
|
830
|
+
} catch (err) {
|
|
831
|
+
logError(`[cloud-sync] PUSH ${route}: ${err instanceof Error ? err.message : String(err)}`);
|
|
832
|
+
return { ok: false };
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
async function cloudPullBlob(route, config) {
|
|
836
|
+
assertSecureEndpoint(config.endpoint);
|
|
837
|
+
try {
|
|
838
|
+
const resp = await fetchWithRetry(`${config.endpoint}${route}`, {
|
|
839
|
+
method: "GET",
|
|
840
|
+
headers: {
|
|
841
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
842
|
+
"X-Device-Id": loadDeviceId()
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
if (!resp.ok) return null;
|
|
846
|
+
const data = await resp.json();
|
|
847
|
+
if (!data.blob) return null;
|
|
848
|
+
const compressed = decryptSyncBlob(data.blob);
|
|
849
|
+
const json = decompress(compressed).toString("utf8");
|
|
850
|
+
return JSON.parse(json);
|
|
851
|
+
} catch (err) {
|
|
852
|
+
logError(`[cloud-sync] PULL ${route}: ${err instanceof Error ? err.message : String(err)}`);
|
|
853
|
+
return null;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
async function cloudPushBehaviors(config) {
|
|
857
|
+
const client = getClient();
|
|
858
|
+
const result = await client.execute("SELECT * FROM behaviors");
|
|
859
|
+
const rows = result.rows;
|
|
860
|
+
const { ok } = await cloudPushBlob(
|
|
861
|
+
"/sync/push-behaviors",
|
|
862
|
+
rows,
|
|
863
|
+
"last_behaviors_push_version",
|
|
864
|
+
config
|
|
865
|
+
);
|
|
866
|
+
return ok;
|
|
867
|
+
}
|
|
868
|
+
async function cloudPullBehaviors(config) {
|
|
869
|
+
const remoteBehaviors = await cloudPullBlob(
|
|
870
|
+
"/sync/pull-behaviors",
|
|
871
|
+
config
|
|
872
|
+
);
|
|
873
|
+
if (!remoteBehaviors || remoteBehaviors.length === 0) return { pulled: 0 };
|
|
874
|
+
const client = getClient();
|
|
875
|
+
let pulled = 0;
|
|
876
|
+
for (const behavior of remoteBehaviors) {
|
|
877
|
+
const existing = await client.execute({
|
|
878
|
+
sql: `SELECT COUNT(*) as cnt FROM behaviors
|
|
879
|
+
WHERE agent_id = ? AND content = ?`,
|
|
880
|
+
args: [behavior.agent_id, behavior.content]
|
|
881
|
+
});
|
|
882
|
+
if (Number(existing.rows[0]?.cnt) > 0) continue;
|
|
883
|
+
await client.execute({
|
|
884
|
+
sql: `INSERT OR IGNORE INTO behaviors
|
|
885
|
+
(id, agent_id, project_name, domain, content, active, priority, created_at, updated_at)
|
|
886
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
887
|
+
args: [
|
|
888
|
+
behavior.id,
|
|
889
|
+
behavior.agent_id,
|
|
890
|
+
behavior.project_name ?? null,
|
|
891
|
+
behavior.domain ?? null,
|
|
892
|
+
behavior.content,
|
|
893
|
+
behavior.active,
|
|
894
|
+
behavior.priority ?? "p1",
|
|
895
|
+
behavior.created_at,
|
|
896
|
+
behavior.updated_at
|
|
897
|
+
]
|
|
898
|
+
});
|
|
899
|
+
pulled++;
|
|
900
|
+
}
|
|
901
|
+
return { pulled };
|
|
902
|
+
}
|
|
903
|
+
async function cloudPushGraphRAG(config) {
|
|
904
|
+
const client = getClient();
|
|
905
|
+
const [entities, relationships, aliases, entityMems, relMems, hyperedges, hyperedgeNodes] = await Promise.all([
|
|
906
|
+
client.execute("SELECT * FROM entities"),
|
|
907
|
+
client.execute("SELECT * FROM relationships"),
|
|
908
|
+
client.execute("SELECT * FROM entity_aliases"),
|
|
909
|
+
client.execute("SELECT * FROM entity_memories"),
|
|
910
|
+
client.execute("SELECT * FROM relationship_memories"),
|
|
911
|
+
client.execute("SELECT * FROM hyperedges"),
|
|
912
|
+
client.execute("SELECT * FROM hyperedge_nodes")
|
|
913
|
+
]);
|
|
914
|
+
const blob = {
|
|
915
|
+
entities: entities.rows,
|
|
916
|
+
relationships: relationships.rows,
|
|
917
|
+
entity_aliases: aliases.rows,
|
|
918
|
+
entity_memories: entityMems.rows,
|
|
919
|
+
relationship_memories: relMems.rows,
|
|
920
|
+
hyperedges: hyperedges.rows,
|
|
921
|
+
hyperedge_nodes: hyperedgeNodes.rows
|
|
922
|
+
};
|
|
923
|
+
const { ok } = await cloudPushBlob(
|
|
924
|
+
"/sync/push-graphrag",
|
|
925
|
+
[blob],
|
|
926
|
+
"last_graphrag_push_version",
|
|
927
|
+
config
|
|
928
|
+
);
|
|
929
|
+
return ok;
|
|
930
|
+
}
|
|
931
|
+
async function cloudPullGraphRAG(config) {
|
|
932
|
+
const data = await cloudPullBlob(
|
|
933
|
+
"/sync/pull-graphrag",
|
|
934
|
+
config
|
|
935
|
+
);
|
|
936
|
+
if (!data || data.length === 0) return { pulled: 0 };
|
|
937
|
+
const blob = data[0];
|
|
938
|
+
const client = getClient();
|
|
939
|
+
let pulled = 0;
|
|
940
|
+
if (blob.entities.length > 0) {
|
|
941
|
+
const stmts = blob.entities.map((e) => ({
|
|
942
|
+
sql: `INSERT OR IGNORE INTO entities (id, name, type, first_seen, last_seen, properties)
|
|
943
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
944
|
+
args: [e.id, e.name, e.type, e.first_seen, e.last_seen, e.properties ?? "{}"]
|
|
945
|
+
}));
|
|
946
|
+
await client.batch(stmts, "write");
|
|
947
|
+
pulled += stmts.length;
|
|
948
|
+
}
|
|
949
|
+
if (blob.relationships.length > 0) {
|
|
950
|
+
const stmts = blob.relationships.map((r) => ({
|
|
951
|
+
sql: `INSERT OR IGNORE INTO relationships
|
|
952
|
+
(id, source_entity_id, target_entity_id, type, weight, timestamp, properties, confidence, confidence_label)
|
|
953
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
954
|
+
args: [
|
|
955
|
+
r.id,
|
|
956
|
+
r.source_entity_id,
|
|
957
|
+
r.target_entity_id,
|
|
958
|
+
r.type,
|
|
959
|
+
r.weight ?? 1,
|
|
960
|
+
r.timestamp,
|
|
961
|
+
r.properties ?? "{}",
|
|
962
|
+
r.confidence ?? 1,
|
|
963
|
+
r.confidence_label ?? "extracted"
|
|
964
|
+
]
|
|
965
|
+
}));
|
|
966
|
+
await client.batch(stmts, "write");
|
|
967
|
+
pulled += stmts.length;
|
|
968
|
+
}
|
|
969
|
+
if (blob.entity_aliases.length > 0) {
|
|
970
|
+
const stmts = blob.entity_aliases.map((a) => ({
|
|
971
|
+
sql: `INSERT OR IGNORE INTO entity_aliases (alias, canonical_entity_id) VALUES (?, ?)`,
|
|
972
|
+
args: [a.alias, a.canonical_entity_id]
|
|
973
|
+
}));
|
|
974
|
+
await client.batch(stmts, "write");
|
|
975
|
+
pulled += stmts.length;
|
|
976
|
+
}
|
|
977
|
+
if (blob.entity_memories.length > 0) {
|
|
978
|
+
const stmts = blob.entity_memories.map((em) => ({
|
|
979
|
+
sql: `INSERT OR IGNORE INTO entity_memories (entity_id, memory_id) VALUES (?, ?)`,
|
|
980
|
+
args: [em.entity_id, em.memory_id]
|
|
981
|
+
}));
|
|
982
|
+
await client.batch(stmts, "write");
|
|
983
|
+
pulled += stmts.length;
|
|
984
|
+
}
|
|
985
|
+
if (blob.relationship_memories.length > 0) {
|
|
986
|
+
const stmts = blob.relationship_memories.map((rm) => ({
|
|
987
|
+
sql: `INSERT OR IGNORE INTO relationship_memories (relationship_id, memory_id) VALUES (?, ?)`,
|
|
988
|
+
args: [rm.relationship_id, rm.memory_id]
|
|
989
|
+
}));
|
|
990
|
+
await client.batch(stmts, "write");
|
|
991
|
+
pulled += stmts.length;
|
|
992
|
+
}
|
|
993
|
+
if (blob.hyperedges.length > 0) {
|
|
994
|
+
const stmts = blob.hyperedges.map((h) => ({
|
|
995
|
+
sql: `INSERT OR IGNORE INTO hyperedges (id, label, relation, confidence, timestamp)
|
|
996
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
997
|
+
args: [h.id, h.label, h.relation, h.confidence ?? 1, h.timestamp]
|
|
998
|
+
}));
|
|
999
|
+
await client.batch(stmts, "write");
|
|
1000
|
+
pulled += stmts.length;
|
|
1001
|
+
}
|
|
1002
|
+
if (blob.hyperedge_nodes.length > 0) {
|
|
1003
|
+
const stmts = blob.hyperedge_nodes.map((hn) => ({
|
|
1004
|
+
sql: `INSERT OR IGNORE INTO hyperedge_nodes (hyperedge_id, entity_id) VALUES (?, ?)`,
|
|
1005
|
+
args: [hn.hyperedge_id, hn.entity_id]
|
|
1006
|
+
}));
|
|
1007
|
+
await client.batch(stmts, "write");
|
|
1008
|
+
pulled += stmts.length;
|
|
1009
|
+
}
|
|
1010
|
+
return { pulled };
|
|
1011
|
+
}
|
|
1012
|
+
async function cloudPushTasks(config) {
|
|
1013
|
+
const client = getClient();
|
|
1014
|
+
const result = await client.execute("SELECT * FROM tasks");
|
|
1015
|
+
const rows = result.rows;
|
|
1016
|
+
const { ok } = await cloudPushBlob(
|
|
1017
|
+
"/sync/push-tasks",
|
|
1018
|
+
rows,
|
|
1019
|
+
"last_tasks_push_version",
|
|
1020
|
+
config
|
|
1021
|
+
);
|
|
1022
|
+
return ok;
|
|
1023
|
+
}
|
|
1024
|
+
async function cloudPullTasks(config) {
|
|
1025
|
+
const remoteTasks = await cloudPullBlob(
|
|
1026
|
+
"/sync/pull-tasks",
|
|
1027
|
+
config
|
|
1028
|
+
);
|
|
1029
|
+
if (!remoteTasks || remoteTasks.length === 0) return { pulled: 0 };
|
|
1030
|
+
const client = getClient();
|
|
1031
|
+
const stmts = remoteTasks.map((t) => ({
|
|
1032
|
+
sql: `INSERT OR IGNORE INTO tasks
|
|
1033
|
+
(id, title, assigned_to, assigned_by, project_name, priority, status, task_file, created_at, updated_at,
|
|
1034
|
+
blocked_by, parent_task_id, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at)
|
|
1035
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1036
|
+
args: [
|
|
1037
|
+
t.id,
|
|
1038
|
+
t.title,
|
|
1039
|
+
t.assigned_to,
|
|
1040
|
+
t.assigned_by,
|
|
1041
|
+
t.project_name,
|
|
1042
|
+
t.priority ?? "p1",
|
|
1043
|
+
t.status ?? "open",
|
|
1044
|
+
t.task_file ?? null,
|
|
1045
|
+
t.created_at,
|
|
1046
|
+
t.updated_at,
|
|
1047
|
+
t.blocked_by ?? null,
|
|
1048
|
+
t.parent_task_id ?? null,
|
|
1049
|
+
t.budget_tokens ?? null,
|
|
1050
|
+
t.budget_fallback_model ?? null,
|
|
1051
|
+
t.tokens_used ?? 0,
|
|
1052
|
+
t.tokens_warned_at ?? null
|
|
1053
|
+
]
|
|
1054
|
+
}));
|
|
1055
|
+
await client.batch(stmts, "write");
|
|
1056
|
+
return { pulled: remoteTasks.length };
|
|
1057
|
+
}
|
|
1058
|
+
async function cloudPushConversations(config) {
|
|
1059
|
+
const client = getClient();
|
|
1060
|
+
const result = await client.execute("SELECT * FROM conversations");
|
|
1061
|
+
const rows = result.rows;
|
|
1062
|
+
const { ok } = await cloudPushBlob(
|
|
1063
|
+
"/sync/push-conversations",
|
|
1064
|
+
rows,
|
|
1065
|
+
"last_conversations_push_version",
|
|
1066
|
+
config
|
|
1067
|
+
);
|
|
1068
|
+
return ok;
|
|
1069
|
+
}
|
|
1070
|
+
async function cloudPullConversations(config) {
|
|
1071
|
+
const remoteConvos = await cloudPullBlob(
|
|
1072
|
+
"/sync/pull-conversations",
|
|
1073
|
+
config
|
|
1074
|
+
);
|
|
1075
|
+
if (!remoteConvos || remoteConvos.length === 0) return { pulled: 0 };
|
|
1076
|
+
const client = getClient();
|
|
1077
|
+
const stmts = remoteConvos.map((c) => ({
|
|
1078
|
+
sql: `INSERT OR IGNORE INTO conversations
|
|
1079
|
+
(id, platform, external_id, sender_id, sender_name, sender_phone, sender_email,
|
|
1080
|
+
recipient_id, channel_id, thread_id, reply_to_id, content_text, content_media,
|
|
1081
|
+
content_metadata, agent_response, agent_name, timestamp, ingested_at)
|
|
1082
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1083
|
+
args: [
|
|
1084
|
+
c.id,
|
|
1085
|
+
c.platform,
|
|
1086
|
+
c.external_id ?? null,
|
|
1087
|
+
c.sender_id,
|
|
1088
|
+
c.sender_name ?? null,
|
|
1089
|
+
c.sender_phone ?? null,
|
|
1090
|
+
c.sender_email ?? null,
|
|
1091
|
+
c.recipient_id ?? null,
|
|
1092
|
+
c.channel_id,
|
|
1093
|
+
c.thread_id ?? null,
|
|
1094
|
+
c.reply_to_id ?? null,
|
|
1095
|
+
c.content_text ?? null,
|
|
1096
|
+
c.content_media ?? null,
|
|
1097
|
+
c.content_metadata ?? null,
|
|
1098
|
+
c.agent_response ?? null,
|
|
1099
|
+
c.agent_name ?? null,
|
|
1100
|
+
c.timestamp,
|
|
1101
|
+
c.ingested_at
|
|
1102
|
+
]
|
|
1103
|
+
}));
|
|
1104
|
+
await client.batch(stmts, "write");
|
|
1105
|
+
return { pulled: remoteConvos.length };
|
|
1106
|
+
}
|
|
1107
|
+
async function cloudPushDocuments(config) {
|
|
1108
|
+
const client = getClient();
|
|
1109
|
+
const [workspaces, documents] = await Promise.all([
|
|
1110
|
+
client.execute("SELECT * FROM workspaces"),
|
|
1111
|
+
client.execute("SELECT * FROM documents")
|
|
1112
|
+
]);
|
|
1113
|
+
const blob = {
|
|
1114
|
+
workspaces: workspaces.rows,
|
|
1115
|
+
documents: documents.rows
|
|
1116
|
+
};
|
|
1117
|
+
const { ok } = await cloudPushBlob(
|
|
1118
|
+
"/sync/push-documents",
|
|
1119
|
+
[blob],
|
|
1120
|
+
"last_documents_push_version",
|
|
1121
|
+
config
|
|
1122
|
+
);
|
|
1123
|
+
return ok;
|
|
1124
|
+
}
|
|
1125
|
+
async function cloudPullDocuments(config) {
|
|
1126
|
+
const data = await cloudPullBlob(
|
|
1127
|
+
"/sync/pull-documents",
|
|
1128
|
+
config
|
|
1129
|
+
);
|
|
1130
|
+
if (!data || data.length === 0) return { pulled: 0 };
|
|
1131
|
+
const blob = data[0];
|
|
1132
|
+
const client = getClient();
|
|
1133
|
+
let pulled = 0;
|
|
1134
|
+
if (blob.workspaces.length > 0) {
|
|
1135
|
+
const stmts = blob.workspaces.map((w) => ({
|
|
1136
|
+
sql: `INSERT OR IGNORE INTO workspaces (id, slug, name, owner_agent_id, created_at, metadata)
|
|
1137
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
1138
|
+
args: [w.id, w.slug, w.name, w.owner_agent_id ?? null, w.created_at, w.metadata ?? null]
|
|
1139
|
+
}));
|
|
1140
|
+
await client.batch(stmts, "write");
|
|
1141
|
+
pulled += stmts.length;
|
|
1142
|
+
}
|
|
1143
|
+
if (blob.documents.length > 0) {
|
|
1144
|
+
const stmts = blob.documents.map((d) => ({
|
|
1145
|
+
sql: `INSERT OR IGNORE INTO documents
|
|
1146
|
+
(id, workspace_id, filename, mime, source_type, user_id, uploaded_at, metadata)
|
|
1147
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1148
|
+
args: [
|
|
1149
|
+
d.id,
|
|
1150
|
+
d.workspace_id,
|
|
1151
|
+
d.filename,
|
|
1152
|
+
d.mime ?? null,
|
|
1153
|
+
d.source_type ?? null,
|
|
1154
|
+
d.user_id ?? null,
|
|
1155
|
+
d.uploaded_at,
|
|
1156
|
+
d.metadata ?? null
|
|
1157
|
+
]
|
|
1158
|
+
}));
|
|
1159
|
+
await client.batch(stmts, "write");
|
|
1160
|
+
pulled += stmts.length;
|
|
1161
|
+
}
|
|
1162
|
+
return { pulled };
|
|
1163
|
+
}
|
|
686
1164
|
export {
|
|
687
1165
|
buildRosterBlob,
|
|
688
1166
|
cloudPull,
|
|
1167
|
+
cloudPullBehaviors,
|
|
1168
|
+
cloudPullBlob,
|
|
1169
|
+
cloudPullConversations,
|
|
1170
|
+
cloudPullDocuments,
|
|
1171
|
+
cloudPullGraphRAG,
|
|
689
1172
|
cloudPullRoster,
|
|
1173
|
+
cloudPullTasks,
|
|
690
1174
|
cloudPush,
|
|
1175
|
+
cloudPushBehaviors,
|
|
1176
|
+
cloudPushBlob,
|
|
1177
|
+
cloudPushConversations,
|
|
1178
|
+
cloudPushDocuments,
|
|
1179
|
+
cloudPushGraphRAG,
|
|
691
1180
|
cloudPushRoster,
|
|
1181
|
+
cloudPushTasks,
|
|
692
1182
|
cloudSync,
|
|
1183
|
+
mergeConfig,
|
|
693
1184
|
mergeRosterFromRemote
|
|
694
1185
|
};
|