@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
|
@@ -2385,10 +2385,23 @@ var cloud_sync_exports = {};
|
|
|
2385
2385
|
__export(cloud_sync_exports, {
|
|
2386
2386
|
buildRosterBlob: () => buildRosterBlob,
|
|
2387
2387
|
cloudPull: () => cloudPull,
|
|
2388
|
+
cloudPullBehaviors: () => cloudPullBehaviors,
|
|
2389
|
+
cloudPullBlob: () => cloudPullBlob,
|
|
2390
|
+
cloudPullConversations: () => cloudPullConversations,
|
|
2391
|
+
cloudPullDocuments: () => cloudPullDocuments,
|
|
2392
|
+
cloudPullGraphRAG: () => cloudPullGraphRAG,
|
|
2388
2393
|
cloudPullRoster: () => cloudPullRoster,
|
|
2394
|
+
cloudPullTasks: () => cloudPullTasks,
|
|
2389
2395
|
cloudPush: () => cloudPush,
|
|
2396
|
+
cloudPushBehaviors: () => cloudPushBehaviors,
|
|
2397
|
+
cloudPushBlob: () => cloudPushBlob,
|
|
2398
|
+
cloudPushConversations: () => cloudPushConversations,
|
|
2399
|
+
cloudPushDocuments: () => cloudPushDocuments,
|
|
2400
|
+
cloudPushGraphRAG: () => cloudPushGraphRAG,
|
|
2390
2401
|
cloudPushRoster: () => cloudPushRoster,
|
|
2402
|
+
cloudPushTasks: () => cloudPushTasks,
|
|
2391
2403
|
cloudSync: () => cloudSync,
|
|
2404
|
+
mergeConfig: () => mergeConfig,
|
|
2392
2405
|
mergeRosterFromRemote: () => mergeRosterFromRemote
|
|
2393
2406
|
});
|
|
2394
2407
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync9, readdirSync as readdirSync2, mkdirSync as mkdirSync3, appendFileSync } from "fs";
|
|
@@ -2402,6 +2415,17 @@ function logError(msg) {
|
|
|
2402
2415
|
} catch {
|
|
2403
2416
|
}
|
|
2404
2417
|
}
|
|
2418
|
+
async function fetchWithRetry(url, init) {
|
|
2419
|
+
const attempt = async () => {
|
|
2420
|
+
const signal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
|
|
2421
|
+
return fetch(url, { ...init, signal });
|
|
2422
|
+
};
|
|
2423
|
+
const resp = await attempt();
|
|
2424
|
+
if (resp.status >= 500) {
|
|
2425
|
+
return attempt();
|
|
2426
|
+
}
|
|
2427
|
+
return resp;
|
|
2428
|
+
}
|
|
2405
2429
|
function assertSecureEndpoint(endpoint) {
|
|
2406
2430
|
if (endpoint.startsWith("https://")) return;
|
|
2407
2431
|
if (endpoint.startsWith("http://")) {
|
|
@@ -2423,7 +2447,7 @@ async function cloudPush(records, maxVersion, config) {
|
|
|
2423
2447
|
const json = JSON.stringify(records);
|
|
2424
2448
|
const compressed = compress(Buffer.from(json, "utf8"));
|
|
2425
2449
|
const blob = encryptSyncBlob(compressed);
|
|
2426
|
-
const resp = await
|
|
2450
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/push`, {
|
|
2427
2451
|
method: "POST",
|
|
2428
2452
|
headers: {
|
|
2429
2453
|
Authorization: `Bearer ${config.apiKey}`,
|
|
@@ -2441,7 +2465,7 @@ async function cloudPush(records, maxVersion, config) {
|
|
|
2441
2465
|
async function cloudPull(sinceVersion, config) {
|
|
2442
2466
|
assertSecureEndpoint(config.endpoint);
|
|
2443
2467
|
try {
|
|
2444
|
-
const response = await
|
|
2468
|
+
const response = await fetchWithRetry(`${config.endpoint}/sync/pull`, {
|
|
2445
2469
|
method: "POST",
|
|
2446
2470
|
headers: {
|
|
2447
2471
|
Authorization: `Bearer ${config.apiKey}`,
|
|
@@ -2557,20 +2581,87 @@ async function cloudSync(config) {
|
|
|
2557
2581
|
try {
|
|
2558
2582
|
await cloudPushRoster(config);
|
|
2559
2583
|
} catch (err) {
|
|
2560
|
-
|
|
2561
|
-
`);
|
|
2584
|
+
logError(`[cloud-sync] Roster push: ${err instanceof Error ? err.message : String(err)}`);
|
|
2562
2585
|
}
|
|
2563
2586
|
try {
|
|
2564
2587
|
await cloudPullRoster(config);
|
|
2565
2588
|
} catch (err) {
|
|
2566
|
-
|
|
2567
|
-
|
|
2589
|
+
logError(`[cloud-sync] Roster pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
2590
|
+
}
|
|
2591
|
+
let behaviorsResult = { pushed: false, pulled: 0 };
|
|
2592
|
+
try {
|
|
2593
|
+
behaviorsResult.pushed = await cloudPushBehaviors(config);
|
|
2594
|
+
} catch (err) {
|
|
2595
|
+
logError(`[cloud-sync] Behaviors push: ${err instanceof Error ? err.message : String(err)}`);
|
|
2596
|
+
}
|
|
2597
|
+
try {
|
|
2598
|
+
const pullResult2 = await cloudPullBehaviors(config);
|
|
2599
|
+
behaviorsResult.pulled = pullResult2.pulled;
|
|
2600
|
+
} catch (err) {
|
|
2601
|
+
logError(`[cloud-sync] Behaviors pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
2602
|
+
}
|
|
2603
|
+
let graphragResult = { pushed: false, pulled: 0 };
|
|
2604
|
+
try {
|
|
2605
|
+
graphragResult.pushed = await cloudPushGraphRAG(config);
|
|
2606
|
+
} catch (err) {
|
|
2607
|
+
logError(`[cloud-sync] GraphRAG push: ${err instanceof Error ? err.message : String(err)}`);
|
|
2608
|
+
}
|
|
2609
|
+
try {
|
|
2610
|
+
const pullResult2 = await cloudPullGraphRAG(config);
|
|
2611
|
+
graphragResult.pulled = pullResult2.pulled;
|
|
2612
|
+
} catch (err) {
|
|
2613
|
+
logError(`[cloud-sync] GraphRAG pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
2614
|
+
}
|
|
2615
|
+
let tasksResult = { pushed: false, pulled: 0 };
|
|
2616
|
+
try {
|
|
2617
|
+
tasksResult.pushed = await cloudPushTasks(config);
|
|
2618
|
+
} catch (err) {
|
|
2619
|
+
logError(`[cloud-sync] Tasks push: ${err instanceof Error ? err.message : String(err)}`);
|
|
2620
|
+
}
|
|
2621
|
+
try {
|
|
2622
|
+
const pullResult2 = await cloudPullTasks(config);
|
|
2623
|
+
tasksResult.pulled = pullResult2.pulled;
|
|
2624
|
+
} catch (err) {
|
|
2625
|
+
logError(`[cloud-sync] Tasks pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
2626
|
+
}
|
|
2627
|
+
let conversationsResult = { pushed: false, pulled: 0 };
|
|
2628
|
+
try {
|
|
2629
|
+
conversationsResult.pushed = await cloudPushConversations(config);
|
|
2630
|
+
} catch (err) {
|
|
2631
|
+
logError(`[cloud-sync] Conversations push: ${err instanceof Error ? err.message : String(err)}`);
|
|
2632
|
+
}
|
|
2633
|
+
try {
|
|
2634
|
+
const pullResult2 = await cloudPullConversations(config);
|
|
2635
|
+
conversationsResult.pulled = pullResult2.pulled;
|
|
2636
|
+
} catch (err) {
|
|
2637
|
+
logError(`[cloud-sync] Conversations pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
2638
|
+
}
|
|
2639
|
+
let documentsResult = { pushed: false, pulled: 0 };
|
|
2640
|
+
try {
|
|
2641
|
+
documentsResult.pushed = await cloudPushDocuments(config);
|
|
2642
|
+
} catch (err) {
|
|
2643
|
+
logError(`[cloud-sync] Documents push: ${err instanceof Error ? err.message : String(err)}`);
|
|
2568
2644
|
}
|
|
2569
|
-
|
|
2645
|
+
try {
|
|
2646
|
+
const pullResult2 = await cloudPullDocuments(config);
|
|
2647
|
+
documentsResult.pulled = pullResult2.pulled;
|
|
2648
|
+
} catch (err) {
|
|
2649
|
+
logError(`[cloud-sync] Documents pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
2650
|
+
}
|
|
2651
|
+
return {
|
|
2652
|
+
pushed,
|
|
2653
|
+
pulled,
|
|
2654
|
+
behaviors: behaviorsResult,
|
|
2655
|
+
graphrag: graphragResult,
|
|
2656
|
+
tasks: tasksResult,
|
|
2657
|
+
conversations: conversationsResult,
|
|
2658
|
+
documents: documentsResult
|
|
2659
|
+
};
|
|
2570
2660
|
}
|
|
2571
2661
|
function buildRosterBlob(paths) {
|
|
2572
2662
|
const rosterPath = paths?.rosterPath ?? path9.join(EXE_AI_DIR, "exe-employees.json");
|
|
2573
2663
|
const identityDir = paths?.identityDir ?? path9.join(EXE_AI_DIR, "identity");
|
|
2664
|
+
const configPath = paths?.configPath ?? path9.join(EXE_AI_DIR, "config.json");
|
|
2574
2665
|
let roster = [];
|
|
2575
2666
|
if (existsSync9(rosterPath)) {
|
|
2576
2667
|
try {
|
|
@@ -2587,9 +2678,16 @@ function buildRosterBlob(paths) {
|
|
|
2587
2678
|
}
|
|
2588
2679
|
}
|
|
2589
2680
|
}
|
|
2590
|
-
|
|
2681
|
+
let config;
|
|
2682
|
+
if (existsSync9(configPath)) {
|
|
2683
|
+
try {
|
|
2684
|
+
config = JSON.parse(readFileSync7(configPath, "utf-8"));
|
|
2685
|
+
} catch {
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
const content = JSON.stringify({ roster, identities, config });
|
|
2591
2689
|
const hash = Buffer.from(content).length;
|
|
2592
|
-
return { roster, identities, version: hash };
|
|
2690
|
+
return { roster, identities, config, version: hash };
|
|
2593
2691
|
}
|
|
2594
2692
|
async function cloudPushRoster(config) {
|
|
2595
2693
|
assertSecureEndpoint(config.endpoint);
|
|
@@ -2608,7 +2706,7 @@ async function cloudPushRoster(config) {
|
|
|
2608
2706
|
const json = JSON.stringify(blob);
|
|
2609
2707
|
const compressed = compress(Buffer.from(json, "utf8"));
|
|
2610
2708
|
const encrypted = encryptSyncBlob(compressed);
|
|
2611
|
-
const resp = await
|
|
2709
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/push-roster`, {
|
|
2612
2710
|
method: "POST",
|
|
2613
2711
|
headers: {
|
|
2614
2712
|
Authorization: `Bearer ${config.apiKey}`,
|
|
@@ -2637,7 +2735,7 @@ async function cloudPushRoster(config) {
|
|
|
2637
2735
|
async function cloudPullRoster(config) {
|
|
2638
2736
|
assertSecureEndpoint(config.endpoint);
|
|
2639
2737
|
try {
|
|
2640
|
-
const resp = await
|
|
2738
|
+
const resp = await fetchWithRetry(`${config.endpoint}/sync/pull-roster`, {
|
|
2641
2739
|
method: "GET",
|
|
2642
2740
|
headers: {
|
|
2643
2741
|
Authorization: `Bearer ${config.apiKey}`,
|
|
@@ -2657,6 +2755,20 @@ async function cloudPullRoster(config) {
|
|
|
2657
2755
|
return { added: 0 };
|
|
2658
2756
|
}
|
|
2659
2757
|
}
|
|
2758
|
+
function mergeConfig(remoteConfig, configPath) {
|
|
2759
|
+
const cfgPath = configPath ?? path9.join(EXE_AI_DIR, "config.json");
|
|
2760
|
+
let local = {};
|
|
2761
|
+
if (existsSync9(cfgPath)) {
|
|
2762
|
+
try {
|
|
2763
|
+
local = JSON.parse(readFileSync7(cfgPath, "utf-8"));
|
|
2764
|
+
} catch {
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
const merged = { ...remoteConfig, ...local };
|
|
2768
|
+
const dir = path9.dirname(cfgPath);
|
|
2769
|
+
if (!existsSync9(dir)) mkdirSync3(dir, { recursive: true });
|
|
2770
|
+
writeFileSync2(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
2771
|
+
}
|
|
2660
2772
|
async function mergeRosterFromRemote(remote, paths) {
|
|
2661
2773
|
const rosterPath = paths?.rosterPath ?? void 0;
|
|
2662
2774
|
const identityDir = paths?.identityDir ?? path9.join(EXE_AI_DIR, "identity");
|
|
@@ -2683,9 +2795,387 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
2683
2795
|
if (added > 0) {
|
|
2684
2796
|
await saveEmployees(localEmployees, rosterPath);
|
|
2685
2797
|
}
|
|
2798
|
+
if (remote.config && Object.keys(remote.config).length > 0) {
|
|
2799
|
+
try {
|
|
2800
|
+
mergeConfig(remote.config, paths?.configPath);
|
|
2801
|
+
} catch {
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2686
2804
|
return { added };
|
|
2687
2805
|
}
|
|
2688
|
-
|
|
2806
|
+
async function cloudPushBlob(route, data, metaKey, config) {
|
|
2807
|
+
if (data.length === 0) return { ok: true };
|
|
2808
|
+
assertSecureEndpoint(config.endpoint);
|
|
2809
|
+
const json = JSON.stringify(data);
|
|
2810
|
+
const version = Buffer.from(json).length;
|
|
2811
|
+
try {
|
|
2812
|
+
const client = getClient();
|
|
2813
|
+
const meta = await client.execute({
|
|
2814
|
+
sql: "SELECT value FROM sync_meta WHERE key = ?",
|
|
2815
|
+
args: [metaKey]
|
|
2816
|
+
});
|
|
2817
|
+
const lastVersion = meta.rows.length > 0 ? Number(meta.rows[0].value) : 0;
|
|
2818
|
+
if (version === lastVersion) return { ok: true };
|
|
2819
|
+
} catch {
|
|
2820
|
+
}
|
|
2821
|
+
try {
|
|
2822
|
+
const compressed = compress(Buffer.from(json, "utf8"));
|
|
2823
|
+
const encrypted = encryptSyncBlob(compressed);
|
|
2824
|
+
const resp = await fetchWithRetry(`${config.endpoint}${route}`, {
|
|
2825
|
+
method: "POST",
|
|
2826
|
+
headers: {
|
|
2827
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
2828
|
+
"Content-Type": "application/json",
|
|
2829
|
+
"X-Device-Id": loadDeviceId()
|
|
2830
|
+
},
|
|
2831
|
+
body: JSON.stringify({ blob: encrypted })
|
|
2832
|
+
});
|
|
2833
|
+
if (resp.ok) {
|
|
2834
|
+
try {
|
|
2835
|
+
const client = getClient();
|
|
2836
|
+
await client.execute({
|
|
2837
|
+
sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES (?, ?)",
|
|
2838
|
+
args: [metaKey, String(version)]
|
|
2839
|
+
});
|
|
2840
|
+
} catch {
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
return { ok: resp.ok };
|
|
2844
|
+
} catch (err) {
|
|
2845
|
+
logError(`[cloud-sync] PUSH ${route}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2846
|
+
return { ok: false };
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
async function cloudPullBlob(route, config) {
|
|
2850
|
+
assertSecureEndpoint(config.endpoint);
|
|
2851
|
+
try {
|
|
2852
|
+
const resp = await fetchWithRetry(`${config.endpoint}${route}`, {
|
|
2853
|
+
method: "GET",
|
|
2854
|
+
headers: {
|
|
2855
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
2856
|
+
"X-Device-Id": loadDeviceId()
|
|
2857
|
+
}
|
|
2858
|
+
});
|
|
2859
|
+
if (!resp.ok) return null;
|
|
2860
|
+
const data = await resp.json();
|
|
2861
|
+
if (!data.blob) return null;
|
|
2862
|
+
const compressed = decryptSyncBlob(data.blob);
|
|
2863
|
+
const json = decompress(compressed).toString("utf8");
|
|
2864
|
+
return JSON.parse(json);
|
|
2865
|
+
} catch (err) {
|
|
2866
|
+
logError(`[cloud-sync] PULL ${route}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2867
|
+
return null;
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
async function cloudPushBehaviors(config) {
|
|
2871
|
+
const client = getClient();
|
|
2872
|
+
const result = await client.execute("SELECT * FROM behaviors");
|
|
2873
|
+
const rows = result.rows;
|
|
2874
|
+
const { ok } = await cloudPushBlob(
|
|
2875
|
+
"/sync/push-behaviors",
|
|
2876
|
+
rows,
|
|
2877
|
+
"last_behaviors_push_version",
|
|
2878
|
+
config
|
|
2879
|
+
);
|
|
2880
|
+
return ok;
|
|
2881
|
+
}
|
|
2882
|
+
async function cloudPullBehaviors(config) {
|
|
2883
|
+
const remoteBehaviors = await cloudPullBlob(
|
|
2884
|
+
"/sync/pull-behaviors",
|
|
2885
|
+
config
|
|
2886
|
+
);
|
|
2887
|
+
if (!remoteBehaviors || remoteBehaviors.length === 0) return { pulled: 0 };
|
|
2888
|
+
const client = getClient();
|
|
2889
|
+
let pulled = 0;
|
|
2890
|
+
for (const behavior of remoteBehaviors) {
|
|
2891
|
+
const existing = await client.execute({
|
|
2892
|
+
sql: `SELECT COUNT(*) as cnt FROM behaviors
|
|
2893
|
+
WHERE agent_id = ? AND content = ?`,
|
|
2894
|
+
args: [behavior.agent_id, behavior.content]
|
|
2895
|
+
});
|
|
2896
|
+
if (Number(existing.rows[0]?.cnt) > 0) continue;
|
|
2897
|
+
await client.execute({
|
|
2898
|
+
sql: `INSERT OR IGNORE INTO behaviors
|
|
2899
|
+
(id, agent_id, project_name, domain, content, active, priority, created_at, updated_at)
|
|
2900
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2901
|
+
args: [
|
|
2902
|
+
behavior.id,
|
|
2903
|
+
behavior.agent_id,
|
|
2904
|
+
behavior.project_name ?? null,
|
|
2905
|
+
behavior.domain ?? null,
|
|
2906
|
+
behavior.content,
|
|
2907
|
+
behavior.active,
|
|
2908
|
+
behavior.priority ?? "p1",
|
|
2909
|
+
behavior.created_at,
|
|
2910
|
+
behavior.updated_at
|
|
2911
|
+
]
|
|
2912
|
+
});
|
|
2913
|
+
pulled++;
|
|
2914
|
+
}
|
|
2915
|
+
return { pulled };
|
|
2916
|
+
}
|
|
2917
|
+
async function cloudPushGraphRAG(config) {
|
|
2918
|
+
const client = getClient();
|
|
2919
|
+
const [entities, relationships, aliases, entityMems, relMems, hyperedges, hyperedgeNodes] = await Promise.all([
|
|
2920
|
+
client.execute("SELECT * FROM entities"),
|
|
2921
|
+
client.execute("SELECT * FROM relationships"),
|
|
2922
|
+
client.execute("SELECT * FROM entity_aliases"),
|
|
2923
|
+
client.execute("SELECT * FROM entity_memories"),
|
|
2924
|
+
client.execute("SELECT * FROM relationship_memories"),
|
|
2925
|
+
client.execute("SELECT * FROM hyperedges"),
|
|
2926
|
+
client.execute("SELECT * FROM hyperedge_nodes")
|
|
2927
|
+
]);
|
|
2928
|
+
const blob = {
|
|
2929
|
+
entities: entities.rows,
|
|
2930
|
+
relationships: relationships.rows,
|
|
2931
|
+
entity_aliases: aliases.rows,
|
|
2932
|
+
entity_memories: entityMems.rows,
|
|
2933
|
+
relationship_memories: relMems.rows,
|
|
2934
|
+
hyperedges: hyperedges.rows,
|
|
2935
|
+
hyperedge_nodes: hyperedgeNodes.rows
|
|
2936
|
+
};
|
|
2937
|
+
const { ok } = await cloudPushBlob(
|
|
2938
|
+
"/sync/push-graphrag",
|
|
2939
|
+
[blob],
|
|
2940
|
+
"last_graphrag_push_version",
|
|
2941
|
+
config
|
|
2942
|
+
);
|
|
2943
|
+
return ok;
|
|
2944
|
+
}
|
|
2945
|
+
async function cloudPullGraphRAG(config) {
|
|
2946
|
+
const data = await cloudPullBlob(
|
|
2947
|
+
"/sync/pull-graphrag",
|
|
2948
|
+
config
|
|
2949
|
+
);
|
|
2950
|
+
if (!data || data.length === 0) return { pulled: 0 };
|
|
2951
|
+
const blob = data[0];
|
|
2952
|
+
const client = getClient();
|
|
2953
|
+
let pulled = 0;
|
|
2954
|
+
if (blob.entities.length > 0) {
|
|
2955
|
+
const stmts = blob.entities.map((e) => ({
|
|
2956
|
+
sql: `INSERT OR IGNORE INTO entities (id, name, type, first_seen, last_seen, properties)
|
|
2957
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
2958
|
+
args: [e.id, e.name, e.type, e.first_seen, e.last_seen, e.properties ?? "{}"]
|
|
2959
|
+
}));
|
|
2960
|
+
await client.batch(stmts, "write");
|
|
2961
|
+
pulled += stmts.length;
|
|
2962
|
+
}
|
|
2963
|
+
if (blob.relationships.length > 0) {
|
|
2964
|
+
const stmts = blob.relationships.map((r) => ({
|
|
2965
|
+
sql: `INSERT OR IGNORE INTO relationships
|
|
2966
|
+
(id, source_entity_id, target_entity_id, type, weight, timestamp, properties, confidence, confidence_label)
|
|
2967
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2968
|
+
args: [
|
|
2969
|
+
r.id,
|
|
2970
|
+
r.source_entity_id,
|
|
2971
|
+
r.target_entity_id,
|
|
2972
|
+
r.type,
|
|
2973
|
+
r.weight ?? 1,
|
|
2974
|
+
r.timestamp,
|
|
2975
|
+
r.properties ?? "{}",
|
|
2976
|
+
r.confidence ?? 1,
|
|
2977
|
+
r.confidence_label ?? "extracted"
|
|
2978
|
+
]
|
|
2979
|
+
}));
|
|
2980
|
+
await client.batch(stmts, "write");
|
|
2981
|
+
pulled += stmts.length;
|
|
2982
|
+
}
|
|
2983
|
+
if (blob.entity_aliases.length > 0) {
|
|
2984
|
+
const stmts = blob.entity_aliases.map((a) => ({
|
|
2985
|
+
sql: `INSERT OR IGNORE INTO entity_aliases (alias, canonical_entity_id) VALUES (?, ?)`,
|
|
2986
|
+
args: [a.alias, a.canonical_entity_id]
|
|
2987
|
+
}));
|
|
2988
|
+
await client.batch(stmts, "write");
|
|
2989
|
+
pulled += stmts.length;
|
|
2990
|
+
}
|
|
2991
|
+
if (blob.entity_memories.length > 0) {
|
|
2992
|
+
const stmts = blob.entity_memories.map((em) => ({
|
|
2993
|
+
sql: `INSERT OR IGNORE INTO entity_memories (entity_id, memory_id) VALUES (?, ?)`,
|
|
2994
|
+
args: [em.entity_id, em.memory_id]
|
|
2995
|
+
}));
|
|
2996
|
+
await client.batch(stmts, "write");
|
|
2997
|
+
pulled += stmts.length;
|
|
2998
|
+
}
|
|
2999
|
+
if (blob.relationship_memories.length > 0) {
|
|
3000
|
+
const stmts = blob.relationship_memories.map((rm) => ({
|
|
3001
|
+
sql: `INSERT OR IGNORE INTO relationship_memories (relationship_id, memory_id) VALUES (?, ?)`,
|
|
3002
|
+
args: [rm.relationship_id, rm.memory_id]
|
|
3003
|
+
}));
|
|
3004
|
+
await client.batch(stmts, "write");
|
|
3005
|
+
pulled += stmts.length;
|
|
3006
|
+
}
|
|
3007
|
+
if (blob.hyperedges.length > 0) {
|
|
3008
|
+
const stmts = blob.hyperedges.map((h) => ({
|
|
3009
|
+
sql: `INSERT OR IGNORE INTO hyperedges (id, label, relation, confidence, timestamp)
|
|
3010
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
3011
|
+
args: [h.id, h.label, h.relation, h.confidence ?? 1, h.timestamp]
|
|
3012
|
+
}));
|
|
3013
|
+
await client.batch(stmts, "write");
|
|
3014
|
+
pulled += stmts.length;
|
|
3015
|
+
}
|
|
3016
|
+
if (blob.hyperedge_nodes.length > 0) {
|
|
3017
|
+
const stmts = blob.hyperedge_nodes.map((hn) => ({
|
|
3018
|
+
sql: `INSERT OR IGNORE INTO hyperedge_nodes (hyperedge_id, entity_id) VALUES (?, ?)`,
|
|
3019
|
+
args: [hn.hyperedge_id, hn.entity_id]
|
|
3020
|
+
}));
|
|
3021
|
+
await client.batch(stmts, "write");
|
|
3022
|
+
pulled += stmts.length;
|
|
3023
|
+
}
|
|
3024
|
+
return { pulled };
|
|
3025
|
+
}
|
|
3026
|
+
async function cloudPushTasks(config) {
|
|
3027
|
+
const client = getClient();
|
|
3028
|
+
const result = await client.execute("SELECT * FROM tasks");
|
|
3029
|
+
const rows = result.rows;
|
|
3030
|
+
const { ok } = await cloudPushBlob(
|
|
3031
|
+
"/sync/push-tasks",
|
|
3032
|
+
rows,
|
|
3033
|
+
"last_tasks_push_version",
|
|
3034
|
+
config
|
|
3035
|
+
);
|
|
3036
|
+
return ok;
|
|
3037
|
+
}
|
|
3038
|
+
async function cloudPullTasks(config) {
|
|
3039
|
+
const remoteTasks = await cloudPullBlob(
|
|
3040
|
+
"/sync/pull-tasks",
|
|
3041
|
+
config
|
|
3042
|
+
);
|
|
3043
|
+
if (!remoteTasks || remoteTasks.length === 0) return { pulled: 0 };
|
|
3044
|
+
const client = getClient();
|
|
3045
|
+
const stmts = remoteTasks.map((t) => ({
|
|
3046
|
+
sql: `INSERT OR IGNORE INTO tasks
|
|
3047
|
+
(id, title, assigned_to, assigned_by, project_name, priority, status, task_file, created_at, updated_at,
|
|
3048
|
+
blocked_by, parent_task_id, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at)
|
|
3049
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3050
|
+
args: [
|
|
3051
|
+
t.id,
|
|
3052
|
+
t.title,
|
|
3053
|
+
t.assigned_to,
|
|
3054
|
+
t.assigned_by,
|
|
3055
|
+
t.project_name,
|
|
3056
|
+
t.priority ?? "p1",
|
|
3057
|
+
t.status ?? "open",
|
|
3058
|
+
t.task_file ?? null,
|
|
3059
|
+
t.created_at,
|
|
3060
|
+
t.updated_at,
|
|
3061
|
+
t.blocked_by ?? null,
|
|
3062
|
+
t.parent_task_id ?? null,
|
|
3063
|
+
t.budget_tokens ?? null,
|
|
3064
|
+
t.budget_fallback_model ?? null,
|
|
3065
|
+
t.tokens_used ?? 0,
|
|
3066
|
+
t.tokens_warned_at ?? null
|
|
3067
|
+
]
|
|
3068
|
+
}));
|
|
3069
|
+
await client.batch(stmts, "write");
|
|
3070
|
+
return { pulled: remoteTasks.length };
|
|
3071
|
+
}
|
|
3072
|
+
async function cloudPushConversations(config) {
|
|
3073
|
+
const client = getClient();
|
|
3074
|
+
const result = await client.execute("SELECT * FROM conversations");
|
|
3075
|
+
const rows = result.rows;
|
|
3076
|
+
const { ok } = await cloudPushBlob(
|
|
3077
|
+
"/sync/push-conversations",
|
|
3078
|
+
rows,
|
|
3079
|
+
"last_conversations_push_version",
|
|
3080
|
+
config
|
|
3081
|
+
);
|
|
3082
|
+
return ok;
|
|
3083
|
+
}
|
|
3084
|
+
async function cloudPullConversations(config) {
|
|
3085
|
+
const remoteConvos = await cloudPullBlob(
|
|
3086
|
+
"/sync/pull-conversations",
|
|
3087
|
+
config
|
|
3088
|
+
);
|
|
3089
|
+
if (!remoteConvos || remoteConvos.length === 0) return { pulled: 0 };
|
|
3090
|
+
const client = getClient();
|
|
3091
|
+
const stmts = remoteConvos.map((c) => ({
|
|
3092
|
+
sql: `INSERT OR IGNORE INTO conversations
|
|
3093
|
+
(id, platform, external_id, sender_id, sender_name, sender_phone, sender_email,
|
|
3094
|
+
recipient_id, channel_id, thread_id, reply_to_id, content_text, content_media,
|
|
3095
|
+
content_metadata, agent_response, agent_name, timestamp, ingested_at)
|
|
3096
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3097
|
+
args: [
|
|
3098
|
+
c.id,
|
|
3099
|
+
c.platform,
|
|
3100
|
+
c.external_id ?? null,
|
|
3101
|
+
c.sender_id,
|
|
3102
|
+
c.sender_name ?? null,
|
|
3103
|
+
c.sender_phone ?? null,
|
|
3104
|
+
c.sender_email ?? null,
|
|
3105
|
+
c.recipient_id ?? null,
|
|
3106
|
+
c.channel_id,
|
|
3107
|
+
c.thread_id ?? null,
|
|
3108
|
+
c.reply_to_id ?? null,
|
|
3109
|
+
c.content_text ?? null,
|
|
3110
|
+
c.content_media ?? null,
|
|
3111
|
+
c.content_metadata ?? null,
|
|
3112
|
+
c.agent_response ?? null,
|
|
3113
|
+
c.agent_name ?? null,
|
|
3114
|
+
c.timestamp,
|
|
3115
|
+
c.ingested_at
|
|
3116
|
+
]
|
|
3117
|
+
}));
|
|
3118
|
+
await client.batch(stmts, "write");
|
|
3119
|
+
return { pulled: remoteConvos.length };
|
|
3120
|
+
}
|
|
3121
|
+
async function cloudPushDocuments(config) {
|
|
3122
|
+
const client = getClient();
|
|
3123
|
+
const [workspaces, documents] = await Promise.all([
|
|
3124
|
+
client.execute("SELECT * FROM workspaces"),
|
|
3125
|
+
client.execute("SELECT * FROM documents")
|
|
3126
|
+
]);
|
|
3127
|
+
const blob = {
|
|
3128
|
+
workspaces: workspaces.rows,
|
|
3129
|
+
documents: documents.rows
|
|
3130
|
+
};
|
|
3131
|
+
const { ok } = await cloudPushBlob(
|
|
3132
|
+
"/sync/push-documents",
|
|
3133
|
+
[blob],
|
|
3134
|
+
"last_documents_push_version",
|
|
3135
|
+
config
|
|
3136
|
+
);
|
|
3137
|
+
return ok;
|
|
3138
|
+
}
|
|
3139
|
+
async function cloudPullDocuments(config) {
|
|
3140
|
+
const data = await cloudPullBlob(
|
|
3141
|
+
"/sync/pull-documents",
|
|
3142
|
+
config
|
|
3143
|
+
);
|
|
3144
|
+
if (!data || data.length === 0) return { pulled: 0 };
|
|
3145
|
+
const blob = data[0];
|
|
3146
|
+
const client = getClient();
|
|
3147
|
+
let pulled = 0;
|
|
3148
|
+
if (blob.workspaces.length > 0) {
|
|
3149
|
+
const stmts = blob.workspaces.map((w) => ({
|
|
3150
|
+
sql: `INSERT OR IGNORE INTO workspaces (id, slug, name, owner_agent_id, created_at, metadata)
|
|
3151
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
3152
|
+
args: [w.id, w.slug, w.name, w.owner_agent_id ?? null, w.created_at, w.metadata ?? null]
|
|
3153
|
+
}));
|
|
3154
|
+
await client.batch(stmts, "write");
|
|
3155
|
+
pulled += stmts.length;
|
|
3156
|
+
}
|
|
3157
|
+
if (blob.documents.length > 0) {
|
|
3158
|
+
const stmts = blob.documents.map((d) => ({
|
|
3159
|
+
sql: `INSERT OR IGNORE INTO documents
|
|
3160
|
+
(id, workspace_id, filename, mime, source_type, user_id, uploaded_at, metadata)
|
|
3161
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3162
|
+
args: [
|
|
3163
|
+
d.id,
|
|
3164
|
+
d.workspace_id,
|
|
3165
|
+
d.filename,
|
|
3166
|
+
d.mime ?? null,
|
|
3167
|
+
d.source_type ?? null,
|
|
3168
|
+
d.user_id ?? null,
|
|
3169
|
+
d.uploaded_at,
|
|
3170
|
+
d.metadata ?? null
|
|
3171
|
+
]
|
|
3172
|
+
}));
|
|
3173
|
+
await client.batch(stmts, "write");
|
|
3174
|
+
pulled += stmts.length;
|
|
3175
|
+
}
|
|
3176
|
+
return { pulled };
|
|
3177
|
+
}
|
|
3178
|
+
var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS;
|
|
2689
3179
|
var init_cloud_sync = __esm({
|
|
2690
3180
|
"src/lib/cloud-sync.ts"() {
|
|
2691
3181
|
"use strict";
|
|
@@ -2697,6 +3187,7 @@ var init_cloud_sync = __esm({
|
|
|
2697
3187
|
init_config();
|
|
2698
3188
|
init_employees();
|
|
2699
3189
|
LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
|
|
3190
|
+
FETCH_TIMEOUT_MS = 3e4;
|
|
2700
3191
|
}
|
|
2701
3192
|
});
|
|
2702
3193
|
|
|
@@ -2771,7 +3262,8 @@ async function writeMemory(record) {
|
|
|
2771
3262
|
has_error: record.has_error ? 1 : 0,
|
|
2772
3263
|
raw_text: record.raw_text,
|
|
2773
3264
|
vector: record.vector,
|
|
2774
|
-
version:
|
|
3265
|
+
version: 0,
|
|
3266
|
+
// Placeholder — assigned atomically at flush time
|
|
2775
3267
|
task_id: record.task_id ?? null,
|
|
2776
3268
|
importance: record.importance ?? 5,
|
|
2777
3269
|
status: record.status ?? "active",
|
|
@@ -2805,6 +3297,13 @@ async function flushBatch() {
|
|
|
2805
3297
|
_flushing = true;
|
|
2806
3298
|
try {
|
|
2807
3299
|
const batch = _pendingRecords.slice(0);
|
|
3300
|
+
const client = getClient();
|
|
3301
|
+
const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
|
|
3302
|
+
let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
3303
|
+
for (const row of batch) {
|
|
3304
|
+
row.version = baseVersion++;
|
|
3305
|
+
}
|
|
3306
|
+
_nextVersion = baseVersion;
|
|
2808
3307
|
const buildStmt = (row) => {
|
|
2809
3308
|
const hasVector = row.vector !== null;
|
|
2810
3309
|
const taskId = row.task_id ?? null;
|