@askexenow/exe-os 0.9.13 → 0.9.15

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.
Files changed (70) hide show
  1. package/dist/bin/backfill-conversations.js +31 -4
  2. package/dist/bin/backfill-responses.js +31 -4
  3. package/dist/bin/backfill-vectors.js +25 -4
  4. package/dist/bin/cleanup-stale-review-tasks.js +1 -1
  5. package/dist/bin/cli.js +55 -8
  6. package/dist/bin/exe-assign.js +31 -4
  7. package/dist/bin/exe-boot.js +25 -4
  8. package/dist/bin/exe-dispatch.js +1 -1
  9. package/dist/bin/exe-doctor.js +1 -1
  10. package/dist/bin/exe-export-behaviors.js +1 -1
  11. package/dist/bin/exe-forget.js +1 -1
  12. package/dist/bin/exe-gateway.js +447 -21
  13. package/dist/bin/exe-heartbeat.js +1 -1
  14. package/dist/bin/exe-kill.js +1 -1
  15. package/dist/bin/exe-launch-agent.js +1 -1
  16. package/dist/bin/exe-link.js +31 -4
  17. package/dist/bin/exe-pending-messages.js +1 -1
  18. package/dist/bin/exe-pending-notifications.js +1 -1
  19. package/dist/bin/exe-pending-reviews.js +1 -1
  20. package/dist/bin/exe-rename.js +31 -4
  21. package/dist/bin/exe-review.js +1 -1
  22. package/dist/bin/exe-search.js +31 -4
  23. package/dist/bin/exe-session-cleanup.js +25 -4
  24. package/dist/bin/exe-start-codex.js +1 -1
  25. package/dist/bin/exe-start-opencode.js +1 -1
  26. package/dist/bin/exe-status.js +1 -1
  27. package/dist/bin/exe-team.js +1 -1
  28. package/dist/bin/git-sweep.js +25 -4
  29. package/dist/bin/graph-backfill.js +1 -1
  30. package/dist/bin/graph-export.js +1 -1
  31. package/dist/bin/scan-tasks.js +25 -4
  32. package/dist/bin/setup.js +46 -8
  33. package/dist/bin/shard-migrate.js +1 -1
  34. package/dist/bin/wiki-sync.js +1 -1
  35. package/dist/gateway/index.js +128 -125
  36. package/dist/hooks/bug-report-worker.js +1 -1
  37. package/dist/hooks/codex-stop-task-finalizer.js +1 -1
  38. package/dist/hooks/commit-complete.js +25 -4
  39. package/dist/hooks/error-recall.js +31 -4
  40. package/dist/hooks/ingest-worker.js +25 -4
  41. package/dist/hooks/ingest.js +1 -1
  42. package/dist/hooks/instructions-loaded.js +31 -4
  43. package/dist/hooks/notification.js +31 -4
  44. package/dist/hooks/post-compact.js +31 -4
  45. package/dist/hooks/pre-compact.js +25 -4
  46. package/dist/hooks/pre-tool-use.js +31 -4
  47. package/dist/hooks/prompt-ingest-worker.js +25 -4
  48. package/dist/hooks/prompt-submit.js +25 -4
  49. package/dist/hooks/response-ingest-worker.js +25 -4
  50. package/dist/hooks/session-end.js +25 -4
  51. package/dist/hooks/session-start.js +31 -4
  52. package/dist/hooks/stop.js +25 -4
  53. package/dist/hooks/subagent-stop.js +31 -4
  54. package/dist/hooks/summary-worker.js +25 -4
  55. package/dist/index.js +128 -125
  56. package/dist/lib/cloud-sync.js +31 -4
  57. package/dist/lib/database.js +31 -4
  58. package/dist/lib/db-daemon-client.js +31 -3
  59. package/dist/lib/db.js +31 -4
  60. package/dist/lib/device-registry.js +31 -4
  61. package/dist/lib/embedder.js +30 -3
  62. package/dist/lib/exe-daemon-client.js +31 -3
  63. package/dist/lib/exe-daemon.js +1969 -156
  64. package/dist/lib/hybrid-search.js +31 -4
  65. package/dist/lib/schedules.js +1 -1
  66. package/dist/lib/store.js +1 -1
  67. package/dist/mcp/server.js +25 -4
  68. package/dist/runtime/index.js +25 -4
  69. package/dist/tui/App.js +34 -4
  70. package/package.json +1 -1
@@ -400,7 +400,7 @@ var init_daemon_protocol = __esm({
400
400
  });
401
401
 
402
402
  // src/lib/daemon-auth.ts
403
- import crypto from "crypto";
403
+ import crypto2 from "crypto";
404
404
  import path2 from "path";
405
405
  import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync } from "fs";
406
406
  function normalizeToken(token) {
@@ -419,7 +419,7 @@ function readDaemonToken() {
419
419
  function ensureDaemonToken(seed) {
420
420
  const existing = readDaemonToken();
421
421
  if (existing) return existing;
422
- const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
422
+ const token = normalizeToken(seed) ?? crypto2.randomBytes(32).toString("hex");
423
423
  ensurePrivateDirSync(EXE_AI_DIR);
424
424
  writeFileSync(DAEMON_TOKEN_PATH, `${token}
425
425
  `, "utf8");
@@ -976,8 +976,8 @@ function logQueue(msg) {
976
976
  process.stderr.write(`[intercom-queue] ${msg}
977
977
  `);
978
978
  try {
979
- const { appendFileSync: appendFileSync2 } = __require("fs");
980
- appendFileSync2(INTERCOM_LOG, line);
979
+ const { appendFileSync: appendFileSync3 } = __require("fs");
980
+ appendFileSync3(INTERCOM_LOG, line);
981
981
  } catch {
982
982
  }
983
983
  }
@@ -1635,7 +1635,7 @@ async function ensureCompatibilityViews(prisma) {
1635
1635
  for (const mapping of VIEW_MAPPINGS) {
1636
1636
  const relation = mapping.source.replace(/"/g, "");
1637
1637
  const rows = await prisma.$queryRawUnsafe(
1638
- "SELECT to_regclass($1) AS regclass",
1638
+ "SELECT to_regclass($1)::text AS regclass",
1639
1639
  relation
1640
1640
  );
1641
1641
  if (!rows[0]?.regclass) {
@@ -1952,8 +1952,29 @@ function findPackageRoot() {
1952
1952
  }
1953
1953
  return null;
1954
1954
  }
1955
+ function getAvailableMemoryGB() {
1956
+ if (process.platform === "darwin") {
1957
+ try {
1958
+ const { execSync: execSync12 } = __require("child_process");
1959
+ const vmstat = execSync12("vm_stat", { encoding: "utf8" });
1960
+ const pageSize = 16384;
1961
+ const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1962
+ const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1963
+ const free = vmstat.match(/Pages free:\s+(\d+)/);
1964
+ const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1965
+ const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1966
+ const freePages = free ? parseInt(free[1], 10) : 0;
1967
+ const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1968
+ const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1969
+ return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1970
+ } catch {
1971
+ return os6.freemem() / (1024 * 1024 * 1024);
1972
+ }
1973
+ }
1974
+ return os6.freemem() / (1024 * 1024 * 1024);
1975
+ }
1955
1976
  function spawnDaemon() {
1956
- const freeGB = os6.freemem() / (1024 * 1024 * 1024);
1977
+ const freeGB = getAvailableMemoryGB();
1957
1978
  const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
1958
1979
  if (totalGB <= 8) {
1959
1980
  process.stderr.write(
@@ -1962,9 +1983,9 @@ function spawnDaemon() {
1962
1983
  );
1963
1984
  return;
1964
1985
  }
1965
- if (totalGB <= 16 && freeGB < 4) {
1986
+ if (totalGB <= 16 && freeGB < 2) {
1966
1987
  process.stderr.write(
1967
- `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB free / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1988
+ `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1968
1989
  `
1969
1990
  );
1970
1991
  return;
@@ -3546,6 +3567,27 @@ import { pathToFileURL as pathToFileURL2 } from "url";
3546
3567
  import os7 from "os";
3547
3568
  import path9 from "path";
3548
3569
  import { jwtVerify, importSPKI } from "jose";
3570
+ function loadDeviceId() {
3571
+ const deviceJsonPath = path9.join(EXE_AI_DIR, "device.json");
3572
+ try {
3573
+ if (existsSync9(deviceJsonPath)) {
3574
+ const data = JSON.parse(readFileSync8(deviceJsonPath, "utf8"));
3575
+ if (data.deviceId) return data.deviceId;
3576
+ }
3577
+ } catch {
3578
+ }
3579
+ try {
3580
+ if (existsSync9(DEVICE_ID_PATH)) {
3581
+ const id2 = readFileSync8(DEVICE_ID_PATH, "utf8").trim();
3582
+ if (id2) return id2;
3583
+ }
3584
+ } catch {
3585
+ }
3586
+ const id = randomUUID2();
3587
+ mkdirSync4(EXE_AI_DIR, { recursive: true });
3588
+ writeFileSync6(DEVICE_ID_PATH, id, "utf8");
3589
+ return id;
3590
+ }
3549
3591
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
3550
3592
  var init_license = __esm({
3551
3593
  "src/lib/license.ts"() {
@@ -3676,7 +3718,7 @@ var init_task_scope = __esm({
3676
3718
  });
3677
3719
 
3678
3720
  // src/lib/notifications.ts
3679
- import crypto2 from "crypto";
3721
+ import crypto3 from "crypto";
3680
3722
  import path11 from "path";
3681
3723
  import os8 from "os";
3682
3724
  import {
@@ -3689,7 +3731,7 @@ import {
3689
3731
  async function writeNotification(notification) {
3690
3732
  try {
3691
3733
  const client = getClient();
3692
- const id = crypto2.randomUUID();
3734
+ const id = crypto3.randomUUID();
3693
3735
  const now = (/* @__PURE__ */ new Date()).toISOString();
3694
3736
  const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
3695
3737
  await client.execute({
@@ -3745,7 +3787,7 @@ __export(session_kill_telemetry_exports, {
3745
3787
  recordSessionKill: () => recordSessionKill,
3746
3788
  sumTokensSavedSince: () => sumTokensSavedSince
3747
3789
  });
3748
- import crypto3 from "crypto";
3790
+ import crypto4 from "crypto";
3749
3791
  async function recordSessionKill(input) {
3750
3792
  try {
3751
3793
  const client = getClient();
@@ -3755,7 +3797,7 @@ async function recordSessionKill(input) {
3755
3797
  ticks_idle, estimated_tokens_saved)
3756
3798
  VALUES (?, ?, ?, ?, ?, ?, ?)`,
3757
3799
  args: [
3758
- crypto3.randomUUID(),
3800
+ crypto4.randomUUID(),
3759
3801
  input.sessionName,
3760
3802
  input.agentId,
3761
3803
  (/* @__PURE__ */ new Date()).toISOString(),
@@ -3996,7 +4038,7 @@ var init_session_scope = __esm({
3996
4038
  });
3997
4039
 
3998
4040
  // src/lib/tasks-crud.ts
3999
- import crypto4 from "crypto";
4041
+ import crypto5 from "crypto";
4000
4042
  import path13 from "path";
4001
4043
  import os9 from "os";
4002
4044
  import { execSync as execSync6 } from "child_process";
@@ -4117,7 +4159,7 @@ async function resolveTask(client, identifier, scopeSession) {
4117
4159
  }
4118
4160
  async function createTaskCore(input) {
4119
4161
  const client = getClient();
4120
- const id = crypto4.randomUUID();
4162
+ const id = crypto5.randomUUID();
4121
4163
  const now = (/* @__PURE__ */ new Date()).toISOString();
4122
4164
  const slug = slugify(input.title);
4123
4165
  let earlySessionScope = null;
@@ -5063,10 +5105,10 @@ var init_tasks_notify = __esm({
5063
5105
  });
5064
5106
 
5065
5107
  // src/lib/behaviors.ts
5066
- import crypto5 from "crypto";
5108
+ import crypto6 from "crypto";
5067
5109
  async function storeBehavior(opts) {
5068
5110
  const client = getClient();
5069
- const id = crypto5.randomUUID();
5111
+ const id = crypto6.randomUUID();
5070
5112
  const now = (/* @__PURE__ */ new Date()).toISOString();
5071
5113
  await client.execute({
5072
5114
  sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
@@ -5095,7 +5137,7 @@ __export(skill_learning_exports, {
5095
5137
  storeTrajectory: () => storeTrajectory,
5096
5138
  sweepTrajectories: () => sweepTrajectories
5097
5139
  });
5098
- import crypto6 from "crypto";
5140
+ import crypto7 from "crypto";
5099
5141
  async function extractTrajectory(taskId, agentId) {
5100
5142
  const client = getClient();
5101
5143
  const result = await client.execute({
@@ -5124,11 +5166,11 @@ async function extractTrajectory(taskId, agentId) {
5124
5166
  return signature;
5125
5167
  }
5126
5168
  function hashSignature(signature) {
5127
- return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
5169
+ return crypto7.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
5128
5170
  }
5129
5171
  async function storeTrajectory(opts) {
5130
5172
  const client = getClient();
5131
- const id = crypto6.randomUUID();
5173
+ const id = crypto7.randomUUID();
5132
5174
  const now = (/* @__PURE__ */ new Date()).toISOString();
5133
5175
  const signatureHash = hashSignature(opts.signature);
5134
5176
  await client.execute({
@@ -7633,6 +7675,255 @@ var init_daemon_orchestration = __esm({
7633
7675
  }
7634
7676
  });
7635
7677
 
7678
+ // src/lib/projection-worker.ts
7679
+ var projection_worker_exports = {};
7680
+ __export(projection_worker_exports, {
7681
+ processProjectionBatch: () => processProjectionBatch,
7682
+ projectionHandlersForTests: () => projectionHandlersForTests,
7683
+ resetProjectionWorkerForTests: () => resetProjectionWorkerForTests,
7684
+ setProjectionWorkerPrismaClientForTests: () => setProjectionWorkerPrismaClientForTests,
7685
+ startProjectionWorker: () => startProjectionWorker,
7686
+ stopProjectionWorker: () => stopProjectionWorker
7687
+ });
7688
+ import os12 from "os";
7689
+ import path19 from "path";
7690
+ import { existsSync as existsSync17 } from "fs";
7691
+ import { createRequire as createRequire3 } from "module";
7692
+ import { pathToFileURL as pathToFileURL3 } from "url";
7693
+ function loadPrisma() {
7694
+ if (!prismaPromise) {
7695
+ prismaPromise = (async () => {
7696
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
7697
+ if (explicitPath) {
7698
+ const mod2 = await import(pathToFileURL3(explicitPath).href);
7699
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
7700
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
7701
+ return new Ctor2();
7702
+ }
7703
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path19.join(os12.homedir(), "exe-db");
7704
+ const req = createRequire3(path19.join(exeDbRoot, "package.json"));
7705
+ const entry = req.resolve("@prisma/client");
7706
+ const mod = await import(pathToFileURL3(entry).href);
7707
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
7708
+ if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
7709
+ return new Ctor();
7710
+ })();
7711
+ }
7712
+ return prismaPromise;
7713
+ }
7714
+ function setProjectionWorkerPrismaClientForTests(client) {
7715
+ prismaPromise = client ? Promise.resolve(client) : null;
7716
+ }
7717
+ function resetProjectionWorkerForTests() {
7718
+ running = false;
7719
+ if (pollTimer) {
7720
+ clearTimeout(pollTimer);
7721
+ pollTimer = null;
7722
+ }
7723
+ prismaPromise = null;
7724
+ }
7725
+ async function processBatch() {
7726
+ const prisma = await loadPrisma();
7727
+ const events = await prisma.$queryRawUnsafe(
7728
+ `SELECT "id", "source", "source_id", "event_type", "payload", "metadata", "timestamp"
7729
+ FROM "raw"."raw_events"
7730
+ WHERE "processed_at" IS NULL
7731
+ ORDER BY "timestamp" ASC
7732
+ LIMIT $1`,
7733
+ BATCH_SIZE
7734
+ );
7735
+ if (events.length === 0) return 0;
7736
+ let processed = 0;
7737
+ for (const event of events) {
7738
+ try {
7739
+ const handler = projectionHandlers[event.source] ?? defaultHandler;
7740
+ const result = await handler(event, prisma);
7741
+ await prisma.$executeRawUnsafe(
7742
+ `UPDATE "raw"."raw_events"
7743
+ SET "processed_at" = NOW(), "projections" = $1::jsonb
7744
+ WHERE "id" = $2`,
7745
+ JSON.stringify({ targets: result.targets, skipped: result.skipped }),
7746
+ event.id
7747
+ );
7748
+ processed++;
7749
+ } catch (err) {
7750
+ const message = err instanceof Error ? err.message : String(err);
7751
+ process.stderr.write(
7752
+ `[projection-worker] Error processing event ${event.id}: ${message}
7753
+ `
7754
+ );
7755
+ await prisma.$executeRawUnsafe(
7756
+ `UPDATE "raw"."raw_events"
7757
+ SET "processed_at" = NOW(), "projections" = $1::jsonb
7758
+ WHERE "id" = $2`,
7759
+ JSON.stringify({ error: message }),
7760
+ event.id
7761
+ );
7762
+ }
7763
+ }
7764
+ return processed;
7765
+ }
7766
+ function startProjectionWorker() {
7767
+ if (running) return;
7768
+ if (!process.env.DATABASE_URL) {
7769
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path19.join(os12.homedir(), "exe-db");
7770
+ if (!existsSync17(path19.join(exeDbRoot, "package.json"))) {
7771
+ process.stderr.write("[projection-worker] Skipped \u2014 no exe-db found. Set DATABASE_URL or EXE_DB_ROOT to enable.\n");
7772
+ return;
7773
+ }
7774
+ }
7775
+ running = true;
7776
+ process.stderr.write("[projection-worker] Starting...\n");
7777
+ const tick = async () => {
7778
+ if (!running) return;
7779
+ try {
7780
+ const count = await processBatch();
7781
+ if (count > 0) {
7782
+ process.stderr.write(`[projection-worker] Processed ${count} events.
7783
+ `);
7784
+ }
7785
+ } catch (err) {
7786
+ process.stderr.write(
7787
+ `[projection-worker] Poll error: ${err instanceof Error ? err.message : String(err)}
7788
+ `
7789
+ );
7790
+ }
7791
+ if (running) {
7792
+ pollTimer = setTimeout(tick, POLL_INTERVAL_MS);
7793
+ }
7794
+ };
7795
+ void tick();
7796
+ }
7797
+ function stopProjectionWorker() {
7798
+ running = false;
7799
+ if (pollTimer) {
7800
+ clearTimeout(pollTimer);
7801
+ pollTimer = null;
7802
+ }
7803
+ process.stderr.write("[projection-worker] Stopped.\n");
7804
+ }
7805
+ async function processProjectionBatch() {
7806
+ return processBatch();
7807
+ }
7808
+ var prismaPromise, projectionHandlers, projectionHandlersForTests, defaultHandler, BATCH_SIZE, POLL_INTERVAL_MS, running, pollTimer;
7809
+ var init_projection_worker = __esm({
7810
+ "src/lib/projection-worker.ts"() {
7811
+ "use strict";
7812
+ prismaPromise = null;
7813
+ projectionHandlers = {
7814
+ async whatsapp(event, prisma) {
7815
+ const targets = [];
7816
+ const payload = event.payload;
7817
+ await prisma.$executeRawUnsafe(
7818
+ `INSERT INTO "memory"."memory_records" ("id", "agent_id", "agent_role", "session_id", "tool_name", "project_name", "raw_text", "memory_type", "source_type", "intent")
7819
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
7820
+ ON CONFLICT ("id") DO NOTHING`,
7821
+ crypto.randomUUID(),
7822
+ "system",
7823
+ "ingest",
7824
+ "projection-worker",
7825
+ "ingest_raw",
7826
+ "exe-os",
7827
+ JSON.stringify(payload),
7828
+ "raw",
7829
+ "whatsapp",
7830
+ event.event_type
7831
+ );
7832
+ targets.push("memory");
7833
+ return { targets };
7834
+ },
7835
+ async shopify(event, prisma) {
7836
+ const targets = [];
7837
+ const payload = event.payload;
7838
+ await prisma.$executeRawUnsafe(
7839
+ `INSERT INTO "memory"."memory_records" ("id", "agent_id", "agent_role", "session_id", "tool_name", "project_name", "raw_text", "memory_type", "source_type", "intent")
7840
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
7841
+ ON CONFLICT ("id") DO NOTHING`,
7842
+ crypto.randomUUID(),
7843
+ "system",
7844
+ "ingest",
7845
+ "projection-worker",
7846
+ "ingest_raw",
7847
+ "exe-os",
7848
+ JSON.stringify(payload),
7849
+ "raw",
7850
+ "shopify",
7851
+ event.event_type
7852
+ );
7853
+ targets.push("memory");
7854
+ return { targets };
7855
+ },
7856
+ async cloud_sync(event, prisma) {
7857
+ const targets = [];
7858
+ const payload = event.payload;
7859
+ await prisma.$executeRawUnsafe(
7860
+ `INSERT INTO "memory"."memory_records" ("id", "agent_id", "agent_role", "session_id", "tool_name", "project_name", "raw_text", "memory_type", "source_type", "intent", "domain")
7861
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
7862
+ ON CONFLICT ("id") DO NOTHING`,
7863
+ payload.id ?? crypto.randomUUID(),
7864
+ payload.agent_id ?? "system",
7865
+ payload.agent_role ?? "ingest",
7866
+ payload.session_id ?? "projection-worker",
7867
+ payload.tool_name ?? "cloud_sync",
7868
+ payload.project_name ?? "exe-os",
7869
+ payload.raw_text ?? JSON.stringify(payload),
7870
+ payload.memory_type ?? "raw",
7871
+ "cloud_sync",
7872
+ event.event_type,
7873
+ payload.domain ?? null
7874
+ );
7875
+ targets.push("memory");
7876
+ return { targets };
7877
+ },
7878
+ async external_agent(event, prisma) {
7879
+ const targets = [];
7880
+ const payload = event.payload;
7881
+ await prisma.$executeRawUnsafe(
7882
+ `INSERT INTO "memory"."memory_records" ("id", "agent_id", "agent_role", "session_id", "tool_name", "project_name", "raw_text", "memory_type", "source_type", "intent", "domain")
7883
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
7884
+ ON CONFLICT ("id") DO NOTHING`,
7885
+ crypto.randomUUID(),
7886
+ "system",
7887
+ "ingest",
7888
+ "projection-worker",
7889
+ "ingest_raw",
7890
+ "exe-os",
7891
+ JSON.stringify(payload),
7892
+ "raw",
7893
+ "external_agent",
7894
+ event.event_type,
7895
+ payload.domain ?? null
7896
+ );
7897
+ targets.push("memory");
7898
+ return { targets };
7899
+ }
7900
+ };
7901
+ projectionHandlersForTests = projectionHandlers;
7902
+ defaultHandler = async (event, prisma) => {
7903
+ await prisma.$executeRawUnsafe(
7904
+ `INSERT INTO "memory"."memory_records" ("id", "agent_id", "agent_role", "session_id", "tool_name", "project_name", "raw_text", "memory_type", "source_type", "intent")
7905
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
7906
+ ON CONFLICT ("id") DO NOTHING`,
7907
+ crypto.randomUUID(),
7908
+ "system",
7909
+ "ingest",
7910
+ "projection-worker",
7911
+ "ingest_raw",
7912
+ "exe-os",
7913
+ JSON.stringify(event.payload),
7914
+ "raw",
7915
+ event.source,
7916
+ event.event_type
7917
+ );
7918
+ return { targets: ["memory"] };
7919
+ };
7920
+ BATCH_SIZE = 50;
7921
+ POLL_INTERVAL_MS = 1e4;
7922
+ running = false;
7923
+ pollTimer = null;
7924
+ }
7925
+ });
7926
+
7636
7927
  // src/lib/shard-manager.ts
7637
7928
  var shard_manager_exports = {};
7638
7929
  __export(shard_manager_exports, {
@@ -7647,12 +7938,12 @@ __export(shard_manager_exports, {
7647
7938
  listShards: () => listShards,
7648
7939
  shardExists: () => shardExists
7649
7940
  });
7650
- import path19 from "path";
7651
- import { existsSync as existsSync17, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
7941
+ import path20 from "path";
7942
+ import { existsSync as existsSync18, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
7652
7943
  import { createClient as createClient2 } from "@libsql/client";
7653
7944
  function initShardManager(encryptionKey) {
7654
7945
  _encryptionKey = encryptionKey;
7655
- if (!existsSync17(SHARDS_DIR)) {
7946
+ if (!existsSync18(SHARDS_DIR)) {
7656
7947
  mkdirSync7(SHARDS_DIR, { recursive: true });
7657
7948
  }
7658
7949
  _shardingEnabled = true;
@@ -7682,7 +7973,7 @@ function getShardClient(projectName) {
7682
7973
  while (_shards.size >= MAX_OPEN_SHARDS) {
7683
7974
  evictLRU();
7684
7975
  }
7685
- const dbPath = path19.join(SHARDS_DIR, `${safeName}.db`);
7976
+ const dbPath = path20.join(SHARDS_DIR, `${safeName}.db`);
7686
7977
  const client = createClient2({
7687
7978
  url: `file:${dbPath}`,
7688
7979
  encryptionKey: _encryptionKey
@@ -7693,10 +7984,10 @@ function getShardClient(projectName) {
7693
7984
  }
7694
7985
  function shardExists(projectName) {
7695
7986
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
7696
- return existsSync17(path19.join(SHARDS_DIR, `${safeName}.db`));
7987
+ return existsSync18(path20.join(SHARDS_DIR, `${safeName}.db`));
7697
7988
  }
7698
7989
  function listShards() {
7699
- if (!existsSync17(SHARDS_DIR)) return [];
7990
+ if (!existsSync18(SHARDS_DIR)) return [];
7700
7991
  return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
7701
7992
  }
7702
7993
  async function ensureShardSchema(client) {
@@ -7943,7 +8234,7 @@ var init_shard_manager = __esm({
7943
8234
  "src/lib/shard-manager.ts"() {
7944
8235
  "use strict";
7945
8236
  init_config();
7946
- SHARDS_DIR = path19.join(EXE_AI_DIR, "shards");
8237
+ SHARDS_DIR = path20.join(EXE_AI_DIR, "shards");
7947
8238
  SHARD_IDLE_MS = 5 * 60 * 1e3;
7948
8239
  MAX_OPEN_SHARDS = 10;
7949
8240
  EVICTION_INTERVAL_MS = 60 * 1e3;
@@ -7965,14 +8256,14 @@ __export(keychain_exports, {
7965
8256
  setMasterKey: () => setMasterKey
7966
8257
  });
7967
8258
  import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
7968
- import { existsSync as existsSync18 } from "fs";
7969
- import path20 from "path";
7970
- import os12 from "os";
8259
+ import { existsSync as existsSync19 } from "fs";
8260
+ import path21 from "path";
8261
+ import os13 from "os";
7971
8262
  function getKeyDir() {
7972
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path20.join(os12.homedir(), ".exe-os");
8263
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path21.join(os13.homedir(), ".exe-os");
7973
8264
  }
7974
8265
  function getKeyPath() {
7975
- return path20.join(getKeyDir(), "master.key");
8266
+ return path21.join(getKeyDir(), "master.key");
7976
8267
  }
7977
8268
  async function tryKeytar() {
7978
8269
  try {
@@ -7993,9 +8284,9 @@ async function getMasterKey() {
7993
8284
  }
7994
8285
  }
7995
8286
  const keyPath = getKeyPath();
7996
- if (!existsSync18(keyPath)) {
8287
+ if (!existsSync19(keyPath)) {
7997
8288
  process.stderr.write(
7998
- `[keychain] Key not found at ${keyPath} (HOME=${os12.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
8289
+ `[keychain] Key not found at ${keyPath} (HOME=${os13.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
7999
8290
  `
8000
8291
  );
8001
8292
  return null;
@@ -8036,7 +8327,7 @@ async function deleteMasterKey() {
8036
8327
  }
8037
8328
  }
8038
8329
  const keyPath = getKeyPath();
8039
- if (existsSync18(keyPath)) {
8330
+ if (existsSync19(keyPath)) {
8040
8331
  await unlink(keyPath);
8041
8332
  }
8042
8333
  }
@@ -9215,16 +9506,16 @@ async function pushToWiki(consolidation, config) {
9215
9506
  const contentLines = consolidation.rawText.split("\n").filter((l) => l.trim() && !l.startsWith("CONSOLIDATION") && !l.match(/^[A-Z\s]+:$/)).join(" ");
9216
9507
  const keywords = contentLines.toLowerCase().replace(/[^a-z0-9\s]/g, "").split(/\s+/).filter((w) => w.length > 3);
9217
9508
  let bestMatch = null;
9218
- for (const doc of docs) {
9219
- if (!doc.id || !doc.title) continue;
9220
- const titleWords = doc.title.toLowerCase().replace(/[^a-z0-9\s]/g, "").split(/\s+/).filter((w) => w.length > 3);
9509
+ for (const doc2 of docs) {
9510
+ if (!doc2.id || !doc2.title) continue;
9511
+ const titleWords = doc2.title.toLowerCase().replace(/[^a-z0-9\s]/g, "").split(/\s+/).filter((w) => w.length > 3);
9221
9512
  if (titleWords.length === 0) continue;
9222
9513
  const matchCount = titleWords.filter(
9223
9514
  (tw) => keywords.some((k) => k.includes(tw) || tw.includes(k))
9224
9515
  ).length;
9225
9516
  const score = matchCount / titleWords.length;
9226
9517
  if (score > (bestMatch?.score ?? 0)) {
9227
- bestMatch = { id: doc.id, title: doc.title, score };
9518
+ bestMatch = { id: doc2.id, title: doc2.title, score };
9228
9519
  }
9229
9520
  }
9230
9521
  if (bestMatch && bestMatch.score >= config.wikiAutoUpdateThreshold) {
@@ -9453,10 +9744,10 @@ async function disposeEmbedder() {
9453
9744
  async function embedDirect(text) {
9454
9745
  const llamaCpp = await import("node-llama-cpp");
9455
9746
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
9456
- const { existsSync: existsSync21 } = await import("fs");
9457
- const path25 = await import("path");
9458
- const modelPath = path25.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
9459
- if (!existsSync21(modelPath)) {
9747
+ const { existsSync: existsSync24 } = await import("fs");
9748
+ const path28 = await import("path");
9749
+ const modelPath = path28.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
9750
+ if (!existsSync24(modelPath)) {
9460
9751
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
9461
9752
  }
9462
9753
  const llama = await llamaCpp.getLlama();
@@ -9484,66 +9775,1551 @@ var init_embedder = __esm({
9484
9775
  }
9485
9776
  });
9486
9777
 
9487
- // src/lib/code-chunker.ts
9488
- import ts from "typescript";
9489
- function chunkSourceFile(source, fileName = "file.ts") {
9490
- const sourceFile = ts.createSourceFile(
9491
- fileName,
9492
- source,
9493
- ts.ScriptTarget.Latest,
9494
- true,
9495
- // setParentNodes
9496
- fileName.endsWith(".tsx") || fileName.endsWith(".jsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS
9778
+ // src/lib/crypto.ts
9779
+ import crypto8 from "crypto";
9780
+ function initSyncCrypto(masterKey) {
9781
+ if (masterKey.length !== 32) {
9782
+ throw new Error(`Master key must be 32 bytes, got ${masterKey.length}`);
9783
+ }
9784
+ _syncKey = Buffer.from(
9785
+ crypto8.hkdfSync("sha256", masterKey, "", SYNC_HKDF_INFO, 32)
9497
9786
  );
9498
- const chunks = [];
9499
- const lines = source.split("\n");
9500
- const importLines = [];
9501
- function getLineNumber(pos) {
9502
- return sourceFile.getLineAndCharacterOfPosition(pos).line + 1;
9787
+ }
9788
+ function isSyncCryptoInitialized() {
9789
+ return _syncKey !== null;
9790
+ }
9791
+ function requireSyncKey() {
9792
+ if (!_syncKey) {
9793
+ throw new Error("Sync crypto not initialized. Call initSyncCrypto(masterKey) first.");
9794
+ }
9795
+ return _syncKey;
9796
+ }
9797
+ function encryptSyncBlob(data) {
9798
+ const key = requireSyncKey();
9799
+ const iv = crypto8.randomBytes(IV_LENGTH);
9800
+ const cipher = crypto8.createCipheriv(ALGORITHM, key, iv);
9801
+ const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
9802
+ const tag = cipher.getAuthTag();
9803
+ return Buffer.concat([iv, encrypted, tag]).toString("base64");
9804
+ }
9805
+ function decryptSyncBlob(ciphertext) {
9806
+ const key = requireSyncKey();
9807
+ const combined = Buffer.from(ciphertext, "base64");
9808
+ if (combined.length < IV_LENGTH + TAG_LENGTH) {
9809
+ throw new Error("Sync blob too short to contain IV + tag");
9810
+ }
9811
+ const iv = combined.subarray(0, IV_LENGTH);
9812
+ const tag = combined.subarray(combined.length - TAG_LENGTH);
9813
+ const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
9814
+ const decipher = crypto8.createDecipheriv(ALGORITHM, key, iv);
9815
+ decipher.setAuthTag(tag);
9816
+ return Buffer.concat([decipher.update(encrypted), decipher.final()]);
9817
+ }
9818
+ var ALGORITHM, IV_LENGTH, TAG_LENGTH, SYNC_HKDF_INFO, _syncKey;
9819
+ var init_crypto = __esm({
9820
+ "src/lib/crypto.ts"() {
9821
+ "use strict";
9822
+ ALGORITHM = "aes-256-gcm";
9823
+ IV_LENGTH = 12;
9824
+ TAG_LENGTH = 16;
9825
+ SYNC_HKDF_INFO = "exe-mem-sync-v2";
9826
+ _syncKey = null;
9503
9827
  }
9504
- function getLeadingComment(node) {
9505
- const fullText = sourceFile.getFullText();
9506
- const ranges = ts.getLeadingCommentRanges(fullText, node.getFullStart());
9507
- if (!ranges || ranges.length === 0) return void 0;
9508
- return ranges.map((r) => fullText.slice(r.pos, r.end)).join("\n");
9828
+ });
9829
+
9830
+ // src/lib/compress.ts
9831
+ import { brotliCompressSync, brotliDecompressSync, constants } from "zlib";
9832
+ function compress(input) {
9833
+ if (input.length === 0) return Buffer.alloc(0);
9834
+ return brotliCompressSync(input, {
9835
+ params: {
9836
+ [constants.BROTLI_PARAM_QUALITY]: 4
9837
+ }
9838
+ });
9839
+ }
9840
+ function decompress(input) {
9841
+ if (input.length === 0) return Buffer.alloc(0);
9842
+ return brotliDecompressSync(input);
9843
+ }
9844
+ var init_compress = __esm({
9845
+ "src/lib/compress.ts"() {
9846
+ "use strict";
9509
9847
  }
9510
- function getNodeText(node) {
9511
- return node.getText(sourceFile);
9848
+ });
9849
+
9850
+ // src/lib/crdt-sync.ts
9851
+ var crdt_sync_exports = {};
9852
+ __export(crdt_sync_exports, {
9853
+ _setStatePath: () => _setStatePath,
9854
+ applyRemoteUpdate: () => applyRemoteUpdate,
9855
+ destroyCrdtDoc: () => destroyCrdtDoc,
9856
+ getDiffUpdate: () => getDiffUpdate,
9857
+ getFullState: () => getFullState,
9858
+ getStateVector: () => getStateVector,
9859
+ importExistingBehaviors: () => importExistingBehaviors,
9860
+ importExistingMemories: () => importExistingMemories,
9861
+ initCrdtDoc: () => initCrdtDoc,
9862
+ isCrdtSyncEnabled: () => isCrdtSyncEnabled,
9863
+ onUpdate: () => onUpdate,
9864
+ readAllBehaviors: () => readAllBehaviors,
9865
+ readAllMemories: () => readAllMemories,
9866
+ rebuildFromDb: () => rebuildFromDb
9867
+ });
9868
+ import * as Y from "yjs";
9869
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync10, existsSync as existsSync20, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
9870
+ import path22 from "path";
9871
+ import { homedir as homedir2 } from "os";
9872
+ function getStatePath() {
9873
+ return _statePathOverride ?? DEFAULT_STATE_PATH;
9874
+ }
9875
+ function _setStatePath(p) {
9876
+ _statePathOverride = p;
9877
+ }
9878
+ function initCrdtDoc() {
9879
+ if (doc) return doc;
9880
+ doc = new Y.Doc();
9881
+ const sp = getStatePath();
9882
+ if (existsSync20(sp)) {
9883
+ try {
9884
+ const state = readFileSync15(sp);
9885
+ Y.applyUpdate(doc, new Uint8Array(state));
9886
+ } catch {
9887
+ console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
9888
+ try {
9889
+ unlinkSync7(sp);
9890
+ } catch {
9891
+ }
9892
+ rebuildFromDb().catch((err) => {
9893
+ console.warn("[crdt-sync] rebuild from DB failed:", err);
9894
+ });
9895
+ }
9512
9896
  }
9513
- function getName(node) {
9514
- if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
9515
- return node.name?.getText(sourceFile) ?? "(anonymous)";
9897
+ doc.on("update", () => {
9898
+ persistState();
9899
+ });
9900
+ return doc;
9901
+ }
9902
+ function getMemoriesMap() {
9903
+ const d = initCrdtDoc();
9904
+ return d.getMap("memories");
9905
+ }
9906
+ function getBehaviorsMap() {
9907
+ const d = initCrdtDoc();
9908
+ return d.getMap("behaviors");
9909
+ }
9910
+ function applyRemoteUpdate(update) {
9911
+ const d = initCrdtDoc();
9912
+ Y.applyUpdate(d, update);
9913
+ }
9914
+ function getFullState() {
9915
+ const d = initCrdtDoc();
9916
+ return Y.encodeStateAsUpdate(d);
9917
+ }
9918
+ function getDiffUpdate(remoteStateVector) {
9919
+ const d = initCrdtDoc();
9920
+ return Y.encodeStateAsUpdate(d, remoteStateVector);
9921
+ }
9922
+ function getStateVector() {
9923
+ const d = initCrdtDoc();
9924
+ return Y.encodeStateVector(d);
9925
+ }
9926
+ function importExistingMemories(memories) {
9927
+ const map = getMemoriesMap();
9928
+ const d = initCrdtDoc();
9929
+ let imported = 0;
9930
+ d.transact(() => {
9931
+ for (const mem of memories) {
9932
+ if (!mem.id) continue;
9933
+ if (map.has(mem.id)) continue;
9934
+ const entry = new Y.Map();
9935
+ entry.set("id", mem.id);
9936
+ entry.set("agent_id", mem.agent_id ?? null);
9937
+ entry.set("agent_role", mem.agent_role ?? null);
9938
+ entry.set("session_id", mem.session_id ?? null);
9939
+ entry.set("timestamp", mem.timestamp ?? null);
9940
+ entry.set("tool_name", mem.tool_name ?? null);
9941
+ entry.set("project_name", mem.project_name ?? null);
9942
+ entry.set("has_error", mem.has_error ?? 0);
9943
+ entry.set("raw_text", mem.raw_text ?? "");
9944
+ entry.set("version", mem.version ?? 0);
9945
+ entry.set("author_device_id", mem.author_device_id ?? null);
9946
+ entry.set("scope", mem.scope ?? "business");
9947
+ map.set(mem.id, entry);
9948
+ imported++;
9516
9949
  }
9517
- if (ts.isClassDeclaration(node)) {
9518
- return node.name?.getText(sourceFile) ?? "(anonymous class)";
9950
+ });
9951
+ return imported;
9952
+ }
9953
+ function importExistingBehaviors(behaviors) {
9954
+ const map = getBehaviorsMap();
9955
+ const d = initCrdtDoc();
9956
+ let imported = 0;
9957
+ d.transact(() => {
9958
+ for (const beh of behaviors) {
9959
+ if (!beh.id) continue;
9960
+ if (map.has(beh.id)) continue;
9961
+ const entry = new Y.Map();
9962
+ entry.set("id", beh.id);
9963
+ entry.set("agent_id", beh.agent_id ?? null);
9964
+ entry.set("project_name", beh.project_name ?? null);
9965
+ entry.set("domain", beh.domain ?? null);
9966
+ entry.set("content", beh.content ?? null);
9967
+ entry.set("active", beh.active ?? 1);
9968
+ entry.set("priority", beh.priority ?? "p1");
9969
+ entry.set("created_at", beh.created_at ?? null);
9970
+ entry.set("updated_at", beh.updated_at ?? null);
9971
+ map.set(beh.id, entry);
9972
+ imported++;
9519
9973
  }
9520
- if (ts.isInterfaceDeclaration(node)) {
9521
- return node.name.getText(sourceFile);
9974
+ });
9975
+ return imported;
9976
+ }
9977
+ function readAllMemories() {
9978
+ const map = getMemoriesMap();
9979
+ const records = [];
9980
+ map.forEach((entry, id) => {
9981
+ records.push({
9982
+ id,
9983
+ agent_id: entry.get("agent_id"),
9984
+ agent_role: entry.get("agent_role"),
9985
+ session_id: entry.get("session_id"),
9986
+ timestamp: entry.get("timestamp"),
9987
+ tool_name: entry.get("tool_name"),
9988
+ project_name: entry.get("project_name"),
9989
+ has_error: entry.get("has_error"),
9990
+ raw_text: entry.get("raw_text"),
9991
+ version: entry.get("version"),
9992
+ author_device_id: entry.get("author_device_id"),
9993
+ scope: entry.get("scope")
9994
+ });
9995
+ });
9996
+ return records;
9997
+ }
9998
+ function readAllBehaviors() {
9999
+ const map = getBehaviorsMap();
10000
+ const records = [];
10001
+ map.forEach((entry, id) => {
10002
+ records.push({
10003
+ id,
10004
+ agent_id: entry.get("agent_id"),
10005
+ project_name: entry.get("project_name"),
10006
+ domain: entry.get("domain"),
10007
+ content: entry.get("content"),
10008
+ active: entry.get("active"),
10009
+ priority: entry.get("priority"),
10010
+ created_at: entry.get("created_at"),
10011
+ updated_at: entry.get("updated_at")
10012
+ });
10013
+ });
10014
+ return records;
10015
+ }
10016
+ function onUpdate(callback) {
10017
+ const d = initCrdtDoc();
10018
+ const handler = (update) => callback(update);
10019
+ d.on("update", handler);
10020
+ return () => d.off("update", handler);
10021
+ }
10022
+ function persistState() {
10023
+ if (!doc) return;
10024
+ try {
10025
+ const sp = getStatePath();
10026
+ const dir = path22.dirname(sp);
10027
+ if (!existsSync20(dir)) mkdirSync8(dir, { recursive: true });
10028
+ const state = Y.encodeStateAsUpdate(doc);
10029
+ writeFileSync10(sp, Buffer.from(state));
10030
+ } catch {
10031
+ }
10032
+ }
10033
+ function isCrdtSyncEnabled() {
10034
+ return process.env.EXE_CRDT_SYNC !== "0";
10035
+ }
10036
+ async function rebuildFromDb() {
10037
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
10038
+ const client = getClient2();
10039
+ const result = await client.execute(
10040
+ "SELECT id, agent_id, agent_role, session_id, timestamp, tool_name, project_name, has_error, raw_text, version, author_device_id, scope FROM memories"
10041
+ );
10042
+ const memories = result.rows.map((row) => ({
10043
+ id: String(row.id),
10044
+ agent_id: row.agent_id,
10045
+ agent_role: row.agent_role,
10046
+ session_id: row.session_id,
10047
+ timestamp: row.timestamp,
10048
+ tool_name: row.tool_name,
10049
+ project_name: row.project_name,
10050
+ has_error: row.has_error,
10051
+ raw_text: row.raw_text,
10052
+ version: row.version,
10053
+ author_device_id: row.author_device_id,
10054
+ scope: row.scope
10055
+ }));
10056
+ const count = importExistingMemories(memories);
10057
+ persistState();
10058
+ return count;
10059
+ }
10060
+ function destroyCrdtDoc() {
10061
+ if (doc) {
10062
+ doc.destroy();
10063
+ doc = null;
10064
+ }
10065
+ }
10066
+ var DEFAULT_STATE_PATH, _statePathOverride, doc;
10067
+ var init_crdt_sync = __esm({
10068
+ "src/lib/crdt-sync.ts"() {
10069
+ "use strict";
10070
+ DEFAULT_STATE_PATH = path22.join(homedir2(), ".exe-os", "crdt-state.bin");
10071
+ _statePathOverride = null;
10072
+ doc = null;
10073
+ }
10074
+ });
10075
+
10076
+ // src/lib/cloud-sync.ts
10077
+ var cloud_sync_exports = {};
10078
+ __export(cloud_sync_exports, {
10079
+ assertSecureEndpoint: () => assertSecureEndpoint,
10080
+ buildRosterBlob: () => buildRosterBlob,
10081
+ cloudPull: () => cloudPull,
10082
+ cloudPullBehaviors: () => cloudPullBehaviors,
10083
+ cloudPullBlob: () => cloudPullBlob,
10084
+ cloudPullConversations: () => cloudPullConversations,
10085
+ cloudPullDocuments: () => cloudPullDocuments,
10086
+ cloudPullGlobalProcedures: () => cloudPullGlobalProcedures,
10087
+ cloudPullGraphRAG: () => cloudPullGraphRAG,
10088
+ cloudPullRoster: () => cloudPullRoster,
10089
+ cloudPullTasks: () => cloudPullTasks,
10090
+ cloudPush: () => cloudPush,
10091
+ cloudPushBehaviors: () => cloudPushBehaviors,
10092
+ cloudPushBlob: () => cloudPushBlob,
10093
+ cloudPushConversations: () => cloudPushConversations,
10094
+ cloudPushDocuments: () => cloudPushDocuments,
10095
+ cloudPushGlobalProcedures: () => cloudPushGlobalProcedures,
10096
+ cloudPushGraphRAG: () => cloudPushGraphRAG,
10097
+ cloudPushRoster: () => cloudPushRoster,
10098
+ cloudPushTasks: () => cloudPushTasks,
10099
+ cloudSync: () => cloudSync,
10100
+ mergeConfig: () => mergeConfig,
10101
+ mergeRosterFromRemote: () => mergeRosterFromRemote,
10102
+ pushToPostgres: () => pushToPostgres,
10103
+ recordRosterDeletion: () => recordRosterDeletion
10104
+ });
10105
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync11, existsSync as existsSync21, readdirSync as readdirSync5, mkdirSync as mkdirSync9, appendFileSync as appendFileSync2, unlinkSync as unlinkSync8, openSync as openSync2, closeSync as closeSync2 } from "fs";
10106
+ import crypto9 from "crypto";
10107
+ import path23 from "path";
10108
+ import { homedir as homedir3 } from "os";
10109
+ function sqlSafe(v) {
10110
+ return v === void 0 ? null : v;
10111
+ }
10112
+ function logError(msg) {
10113
+ try {
10114
+ const logPath = path23.join(homedir3(), ".exe-os", "workers.log");
10115
+ appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
10116
+ `);
10117
+ } catch {
10118
+ }
10119
+ }
10120
+ function loadPgClient() {
10121
+ if (_pgFailed) return null;
10122
+ const postgresUrl = process.env.DATABASE_URL;
10123
+ const configPath = path23.join(EXE_AI_DIR, "config.json");
10124
+ let cloudPostgresUrl;
10125
+ try {
10126
+ if (existsSync21(configPath)) {
10127
+ const cfg = JSON.parse(readFileSync16(configPath, "utf8"));
10128
+ cloudPostgresUrl = cfg.cloud?.postgresUrl;
10129
+ if (cfg.cloud?.syncToPostgres === false) {
10130
+ _pgFailed = true;
10131
+ return null;
10132
+ }
9522
10133
  }
9523
- if (ts.isTypeAliasDeclaration(node)) {
9524
- return node.name.getText(sourceFile);
10134
+ } catch {
10135
+ }
10136
+ const url = postgresUrl || cloudPostgresUrl;
10137
+ if (!url) {
10138
+ _pgFailed = true;
10139
+ return null;
10140
+ }
10141
+ if (!_pgPromise) {
10142
+ _pgPromise = (async () => {
10143
+ const { createRequire: createRequire4 } = await import("module");
10144
+ const { pathToFileURL: pathToFileURL4 } = await import("url");
10145
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path23.join(homedir3(), "exe-db");
10146
+ const req = createRequire4(path23.join(exeDbRoot, "package.json"));
10147
+ const entry = req.resolve("@prisma/client");
10148
+ const mod = await import(pathToFileURL4(entry).href);
10149
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
10150
+ if (!Ctor) throw new Error("No PrismaClient");
10151
+ return new Ctor();
10152
+ })().catch(() => {
10153
+ _pgFailed = true;
10154
+ _pgPromise = null;
10155
+ throw new Error("pg_unavailable");
10156
+ });
10157
+ }
10158
+ return _pgPromise;
10159
+ }
10160
+ async function pushToPostgres(records) {
10161
+ const loader = loadPgClient();
10162
+ if (!loader) return 0;
10163
+ let prisma;
10164
+ try {
10165
+ prisma = await loader;
10166
+ } catch {
10167
+ return 0;
10168
+ }
10169
+ let inserted = 0;
10170
+ for (const rec of records) {
10171
+ try {
10172
+ await prisma.$executeRawUnsafe(
10173
+ `INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
10174
+ VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
10175
+ ON CONFLICT (source, source_id, event_type) DO NOTHING`,
10176
+ String(rec.id ?? ""),
10177
+ JSON.stringify(rec),
10178
+ JSON.stringify({ agent_id: rec.agent_id, project_name: rec.project_name, tool_name: rec.tool_name }),
10179
+ rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
10180
+ );
10181
+ inserted++;
10182
+ } catch {
9525
10183
  }
9526
- if (ts.isEnumDeclaration(node)) {
9527
- return node.name.getText(sourceFile);
10184
+ }
10185
+ return inserted;
10186
+ }
10187
+ async function withRosterLock(fn) {
10188
+ try {
10189
+ const fd = openSync2(ROSTER_LOCK_PATH, "wx");
10190
+ closeSync2(fd);
10191
+ writeFileSync11(ROSTER_LOCK_PATH, String(Date.now()));
10192
+ } catch (err) {
10193
+ if (err.code === "EEXIST") {
10194
+ try {
10195
+ const ts2 = parseInt(readFileSync16(ROSTER_LOCK_PATH, "utf-8"), 10);
10196
+ if (Date.now() - ts2 < LOCK_STALE_MS) {
10197
+ throw new Error("Roster merge already in progress \u2014 another sync is running");
10198
+ }
10199
+ unlinkSync8(ROSTER_LOCK_PATH);
10200
+ const fd = openSync2(ROSTER_LOCK_PATH, "wx");
10201
+ closeSync2(fd);
10202
+ writeFileSync11(ROSTER_LOCK_PATH, String(Date.now()));
10203
+ } catch (retryErr) {
10204
+ if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
10205
+ throw new Error("Roster merge already in progress \u2014 another sync is running");
10206
+ }
10207
+ } else {
10208
+ throw err;
9528
10209
  }
9529
- if (ts.isVariableStatement(node)) {
9530
- const decls = node.declarationList.declarations;
9531
- return decls.map((d) => d.name.getText(sourceFile)).join(", ");
10210
+ }
10211
+ try {
10212
+ return await fn();
10213
+ } finally {
10214
+ try {
10215
+ unlinkSync8(ROSTER_LOCK_PATH);
10216
+ } catch {
9532
10217
  }
9533
- if (ts.isExportAssignment(node)) {
9534
- return "default export";
10218
+ }
10219
+ }
10220
+ async function fetchWithRetry(url, init) {
10221
+ const MAX_RETRIES4 = 3;
10222
+ const BASE_DELAY_MS2 = 200;
10223
+ let lastError;
10224
+ for (let attempt = 0; attempt <= MAX_RETRIES4; attempt++) {
10225
+ try {
10226
+ const signal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
10227
+ const resp = await fetch(url, { ...init, signal });
10228
+ if (resp && resp.status >= 500 && attempt < MAX_RETRIES4) {
10229
+ await new Promise((r) => setTimeout(r, BASE_DELAY_MS2 * Math.pow(2, attempt)));
10230
+ continue;
10231
+ }
10232
+ return resp;
10233
+ } catch (err) {
10234
+ lastError = err;
10235
+ if (attempt === MAX_RETRIES4) throw err;
10236
+ await new Promise((r) => setTimeout(r, BASE_DELAY_MS2 * Math.pow(2, attempt)));
9535
10237
  }
9536
- return "(unknown)";
9537
10238
  }
9538
- function visitTopLevel(node) {
9539
- if (ts.isImportDeclaration(node)) {
9540
- importLines.push({
9541
- start: getLineNumber(node.getStart(sourceFile)),
9542
- end: getLineNumber(node.getEnd())
9543
- });
10239
+ throw lastError;
10240
+ }
10241
+ function assertSecureEndpoint(endpoint) {
10242
+ if (endpoint.startsWith("https://")) return;
10243
+ if (endpoint.startsWith("http://")) {
10244
+ try {
10245
+ const parsed = new URL(endpoint);
10246
+ if (LOCALHOST_PATTERNS.test(parsed.hostname)) return;
10247
+ } catch {
9544
10248
  return;
9545
10249
  }
9546
- if (ts.isFunctionDeclaration(node)) {
10250
+ throw new Error(
10251
+ `Insecure cloud endpoint rejected: "${endpoint}". Use https:// for remote hosts. Plain http:// is only allowed for localhost.`
10252
+ );
10253
+ }
10254
+ }
10255
+ async function cloudPush(records, maxVersion, config) {
10256
+ if (records.length === 0) return true;
10257
+ assertSecureEndpoint(config.endpoint);
10258
+ try {
10259
+ const json = JSON.stringify(records);
10260
+ const compressed = compress(Buffer.from(json, "utf8"));
10261
+ const blob = encryptSyncBlob(compressed);
10262
+ const resp = await fetchWithRetry(`${config.endpoint}/sync/push`, {
10263
+ method: "POST",
10264
+ headers: {
10265
+ Authorization: `Bearer ${config.apiKey}`,
10266
+ "Content-Type": "application/json",
10267
+ "X-Device-Id": loadDeviceId(),
10268
+ "X-Expected-Version": String(maxVersion)
10269
+ },
10270
+ body: JSON.stringify({ version: maxVersion, blob })
10271
+ });
10272
+ if (resp == null) {
10273
+ logError("[cloud-sync] PUSH FAILED: no response from server");
10274
+ return false;
10275
+ }
10276
+ if (resp.status === 409) {
10277
+ logError("[cloud-sync] PUSH VERSION CONFLICT \u2014 re-pull required before next push");
10278
+ return false;
10279
+ }
10280
+ return resp.ok;
10281
+ } catch (err) {
10282
+ logError(`[cloud-sync] PUSH FAILED: ${err instanceof Error ? err.message : String(err)}`);
10283
+ return false;
10284
+ }
10285
+ }
10286
+ async function cloudPull(sinceVersion, config) {
10287
+ assertSecureEndpoint(config.endpoint);
10288
+ try {
10289
+ const response = await fetchWithRetry(`${config.endpoint}/sync/pull`, {
10290
+ method: "POST",
10291
+ headers: {
10292
+ Authorization: `Bearer ${config.apiKey}`,
10293
+ "Content-Type": "application/json",
10294
+ "X-Device-Id": loadDeviceId()
10295
+ },
10296
+ body: JSON.stringify({ since_version: sinceVersion })
10297
+ });
10298
+ if (response == null) {
10299
+ logError("[cloud-sync] PULL FAILED: no response from server");
10300
+ return { records: [], maxVersion: sinceVersion };
10301
+ }
10302
+ if (!response.ok) return { records: [], maxVersion: sinceVersion };
10303
+ const data = await response.json();
10304
+ const allRecords = [];
10305
+ for (const { blob } of data.blobs ?? []) {
10306
+ try {
10307
+ const compressed = decryptSyncBlob(blob);
10308
+ const json = decompress(compressed).toString("utf8");
10309
+ const records = JSON.parse(json);
10310
+ allRecords.push(...records);
10311
+ } catch {
10312
+ continue;
10313
+ }
10314
+ }
10315
+ return { records: allRecords, maxVersion: data.max_version ?? sinceVersion };
10316
+ } catch (err) {
10317
+ logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
10318
+ return { records: [], maxVersion: sinceVersion };
10319
+ }
10320
+ }
10321
+ async function cloudSync(config) {
10322
+ if (!isSyncCryptoInitialized()) {
10323
+ try {
10324
+ const { getMasterKey: getMasterKey2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
10325
+ const masterKey = await getMasterKey2();
10326
+ if (masterKey) {
10327
+ initSyncCrypto(masterKey);
10328
+ } else {
10329
+ throw new Error("No master key found");
10330
+ }
10331
+ } catch (err) {
10332
+ throw new Error(`[cloud-sync] Cannot initialize encryption: ${err instanceof Error ? err.message : String(err)}`);
10333
+ }
10334
+ }
10335
+ let client;
10336
+ try {
10337
+ client = getClient();
10338
+ } catch {
10339
+ throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
10340
+ }
10341
+ try {
10342
+ const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
10343
+ await getRawClient2().execute("PRAGMA wal_checkpoint(PASSIVE)");
10344
+ } catch {
10345
+ }
10346
+ try {
10347
+ await client.execute(
10348
+ "CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)"
10349
+ );
10350
+ } catch (e) {
10351
+ logError(`[cloud-sync] sync_meta CREATE failed: ${e instanceof Error ? e.message : String(e)}`);
10352
+ }
10353
+ const pullMeta = await client.execute(
10354
+ "SELECT value FROM sync_meta WHERE key = 'last_cloud_pull_version'"
10355
+ );
10356
+ const lastPullVersion = pullMeta.rows.length > 0 ? Number(pullMeta.rows[0].value) : 0;
10357
+ const pullResult = await cloudPull(lastPullVersion, config);
10358
+ let pulled = 0;
10359
+ if (pullResult.records.length > 0) {
10360
+ if (isCrdtSyncEnabled()) {
10361
+ const { initCrdtDoc: initCrdtDoc2, importExistingMemories: importExistingMemories2, readAllMemories: readAllMemories2 } = await Promise.resolve().then(() => (init_crdt_sync(), crdt_sync_exports));
10362
+ initCrdtDoc2();
10363
+ importExistingMemories2(
10364
+ pullResult.records.map((rec) => ({
10365
+ id: String(rec.id ?? ""),
10366
+ agent_id: rec.agent_id,
10367
+ agent_role: rec.agent_role,
10368
+ session_id: rec.session_id,
10369
+ timestamp: rec.timestamp,
10370
+ tool_name: rec.tool_name,
10371
+ project_name: rec.project_name,
10372
+ has_error: rec.has_error ?? 0,
10373
+ raw_text: rec.raw_text ?? "",
10374
+ version: rec.version ?? 0,
10375
+ author_device_id: rec.author_device_id,
10376
+ scope: rec.scope ?? "business"
10377
+ }))
10378
+ );
10379
+ const pulledIds = new Set(pullResult.records.map((r) => String(r.id ?? "")));
10380
+ const merged = readAllMemories2().filter((rec) => pulledIds.has(rec.id));
10381
+ const stmts = merged.map((rec) => ({
10382
+ sql: `INSERT OR REPLACE INTO memories
10383
+ (id, agent_id, agent_role, session_id, timestamp,
10384
+ tool_name, project_name, has_error, raw_text, version,
10385
+ author_device_id, scope)
10386
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
10387
+ args: [
10388
+ sqlSafe(rec.id),
10389
+ sqlSafe(rec.agent_id),
10390
+ sqlSafe(rec.agent_role),
10391
+ sqlSafe(rec.session_id),
10392
+ sqlSafe(rec.timestamp),
10393
+ sqlSafe(rec.tool_name),
10394
+ sqlSafe(rec.project_name),
10395
+ sqlSafe(rec.has_error ?? 0),
10396
+ sqlSafe(rec.raw_text ?? ""),
10397
+ sqlSafe(rec.version ?? 0),
10398
+ sqlSafe(rec.author_device_id),
10399
+ sqlSafe(rec.scope ?? "business")
10400
+ ]
10401
+ }));
10402
+ if (stmts.length > 0) await client.batch(stmts, "write");
10403
+ pulled = pullResult.records.length;
10404
+ } else {
10405
+ const stmts = pullResult.records.map((rec) => ({
10406
+ sql: `INSERT OR REPLACE INTO memories
10407
+ (id, agent_id, agent_role, session_id, timestamp,
10408
+ tool_name, project_name, has_error, raw_text, version,
10409
+ author_device_id, scope)
10410
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
10411
+ args: [
10412
+ sqlSafe(rec.id),
10413
+ sqlSafe(rec.agent_id),
10414
+ sqlSafe(rec.agent_role),
10415
+ sqlSafe(rec.session_id),
10416
+ sqlSafe(rec.timestamp),
10417
+ sqlSafe(rec.tool_name),
10418
+ sqlSafe(rec.project_name),
10419
+ sqlSafe(rec.has_error ?? 0),
10420
+ sqlSafe(rec.raw_text ?? ""),
10421
+ sqlSafe(rec.version ?? 0),
10422
+ sqlSafe(rec.author_device_id),
10423
+ sqlSafe(rec.scope ?? "business")
10424
+ ]
10425
+ }));
10426
+ await client.batch(stmts, "write");
10427
+ pulled = pullResult.records.length;
10428
+ }
10429
+ }
10430
+ if (pulled > 0) {
10431
+ try {
10432
+ await pushToPostgres(pullResult.records);
10433
+ } catch {
10434
+ }
10435
+ }
10436
+ if (pullResult.maxVersion > lastPullVersion) {
10437
+ await client.execute({
10438
+ sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_pull_version', ?)",
10439
+ args: [String(pullResult.maxVersion)]
10440
+ });
10441
+ }
10442
+ const pushMeta = await client.execute(
10443
+ "SELECT value FROM sync_meta WHERE key = 'last_cloud_push_version'"
10444
+ );
10445
+ const lastPushVersion = pushMeta.rows.length > 0 ? Number(pushMeta.rows[0].value) : 0;
10446
+ let pushed = 0;
10447
+ let batchCursor = lastPushVersion;
10448
+ while (true) {
10449
+ const recordsResult = await client.execute({
10450
+ sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
10451
+ tool_name, project_name, has_error, raw_text, version,
10452
+ author_device_id, scope
10453
+ FROM memories
10454
+ WHERE version > ?
10455
+ AND (scope IS NULL OR scope != 'personal')
10456
+ ORDER BY version ASC
10457
+ LIMIT ?`,
10458
+ args: [batchCursor, PUSH_BATCH_SIZE]
10459
+ });
10460
+ if (recordsResult.rows.length === 0) break;
10461
+ const records = recordsResult.rows.map((row) => ({
10462
+ id: row.id,
10463
+ agent_id: row.agent_id,
10464
+ agent_role: row.agent_role,
10465
+ session_id: row.session_id,
10466
+ timestamp: row.timestamp,
10467
+ tool_name: row.tool_name,
10468
+ project_name: row.project_name,
10469
+ has_error: row.has_error,
10470
+ raw_text: row.raw_text,
10471
+ version: row.version,
10472
+ author_device_id: row.author_device_id,
10473
+ scope: row.scope
10474
+ }));
10475
+ const maxVersion = Number(records[records.length - 1].version);
10476
+ const pushOk = await cloudPush(records, maxVersion, config);
10477
+ if (!pushOk) break;
10478
+ try {
10479
+ await pushToPostgres(records);
10480
+ } catch {
10481
+ }
10482
+ await client.execute({
10483
+ sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
10484
+ args: [String(maxVersion)]
10485
+ });
10486
+ pushed += records.length;
10487
+ batchCursor = maxVersion;
10488
+ if (recordsResult.rows.length < PUSH_BATCH_SIZE) break;
10489
+ }
10490
+ try {
10491
+ await cloudPushRoster(config);
10492
+ } catch (err) {
10493
+ logError(`[cloud-sync] Roster push: ${err instanceof Error ? err.message : String(err)}`);
10494
+ }
10495
+ try {
10496
+ await cloudPullRoster(config);
10497
+ } catch (err) {
10498
+ logError(`[cloud-sync] Roster pull: ${err instanceof Error ? err.message : String(err)}`);
10499
+ }
10500
+ try {
10501
+ await cloudPushGlobalProcedures(config);
10502
+ } catch (err) {
10503
+ logError(`[cloud-sync] Global procedures push: ${err instanceof Error ? err.message : String(err)}`);
10504
+ }
10505
+ try {
10506
+ await cloudPullGlobalProcedures(config);
10507
+ } catch (err) {
10508
+ logError(`[cloud-sync] Global procedures pull: ${err instanceof Error ? err.message : String(err)}`);
10509
+ }
10510
+ const countRows = async (sql) => {
10511
+ try {
10512
+ return Number((await client.execute(sql)).rows[0]?.cnt ?? 0);
10513
+ } catch {
10514
+ return 0;
10515
+ }
10516
+ };
10517
+ let behaviorsResult = { pushed: 0, pulled: 0 };
10518
+ try {
10519
+ await cloudPushBehaviors(config);
10520
+ behaviorsResult.pushed = await countRows("SELECT COUNT(*) as cnt FROM behaviors WHERE active = 1");
10521
+ } catch (err) {
10522
+ logError(`[cloud-sync] Behaviors push: ${err instanceof Error ? err.message : String(err)}`);
10523
+ }
10524
+ try {
10525
+ const pullResult2 = await cloudPullBehaviors(config);
10526
+ behaviorsResult.pulled = pullResult2.pulled;
10527
+ } catch (err) {
10528
+ logError(`[cloud-sync] Behaviors pull: ${err instanceof Error ? err.message : String(err)}`);
10529
+ }
10530
+ let graphragResult = { pushed: 0, pulled: 0 };
10531
+ try {
10532
+ await cloudPushGraphRAG(config);
10533
+ graphragResult.pushed = await countRows("SELECT COUNT(*) as cnt FROM entities");
10534
+ } catch (err) {
10535
+ logError(`[cloud-sync] GraphRAG push: ${err instanceof Error ? err.message : String(err)}`);
10536
+ }
10537
+ try {
10538
+ const pullResult2 = await cloudPullGraphRAG(config);
10539
+ graphragResult.pulled = pullResult2.pulled;
10540
+ } catch (err) {
10541
+ logError(`[cloud-sync] GraphRAG pull: ${err instanceof Error ? err.message : String(err)}`);
10542
+ }
10543
+ let tasksResult = { pushed: 0, pulled: 0 };
10544
+ try {
10545
+ await cloudPushTasks(config);
10546
+ tasksResult.pushed = await countRows("SELECT COUNT(*) as cnt FROM tasks");
10547
+ } catch (err) {
10548
+ logError(`[cloud-sync] Tasks push: ${err instanceof Error ? err.message : String(err)}`);
10549
+ }
10550
+ try {
10551
+ const pullResult2 = await cloudPullTasks(config);
10552
+ tasksResult.pulled = pullResult2.pulled;
10553
+ } catch (err) {
10554
+ logError(`[cloud-sync] Tasks pull: ${err instanceof Error ? err.message : String(err)}`);
10555
+ }
10556
+ let conversationsResult = { pushed: 0, pulled: 0 };
10557
+ try {
10558
+ await cloudPushConversations(config);
10559
+ conversationsResult.pushed = await countRows("SELECT COUNT(*) as cnt FROM conversations");
10560
+ } catch (err) {
10561
+ logError(`[cloud-sync] Conversations push: ${err instanceof Error ? err.message : String(err)}`);
10562
+ }
10563
+ try {
10564
+ const pullResult2 = await cloudPullConversations(config);
10565
+ conversationsResult.pulled = pullResult2.pulled;
10566
+ } catch (err) {
10567
+ logError(`[cloud-sync] Conversations pull: ${err instanceof Error ? err.message : String(err)}`);
10568
+ }
10569
+ let documentsResult = { pushed: 0, pulled: 0 };
10570
+ try {
10571
+ await cloudPushDocuments(config);
10572
+ documentsResult.pushed = await countRows("SELECT COUNT(*) as cnt FROM documents");
10573
+ } catch (err) {
10574
+ logError(`[cloud-sync] Documents push: ${err instanceof Error ? err.message : String(err)}`);
10575
+ }
10576
+ try {
10577
+ const pullResult2 = await cloudPullDocuments(config);
10578
+ documentsResult.pulled = pullResult2.pulled;
10579
+ } catch (err) {
10580
+ logError(`[cloud-sync] Documents pull: ${err instanceof Error ? err.message : String(err)}`);
10581
+ }
10582
+ let rosterResult = { employees: 0, identities: 0 };
10583
+ try {
10584
+ const employees = await loadEmployees();
10585
+ rosterResult.employees = employees.length;
10586
+ const idDir = path23.join(EXE_AI_DIR, "identity");
10587
+ if (existsSync21(idDir)) {
10588
+ rosterResult.identities = readdirSync5(idDir).filter((f) => f.endsWith(".md")).length;
10589
+ }
10590
+ } catch {
10591
+ }
10592
+ const totalMemories = await countRows("SELECT COUNT(*) as cnt FROM memories WHERE status = 'active' OR status IS NULL");
10593
+ return {
10594
+ pushed,
10595
+ pulled,
10596
+ totalMemories,
10597
+ behaviors: behaviorsResult,
10598
+ graphrag: graphragResult,
10599
+ tasks: tasksResult,
10600
+ conversations: conversationsResult,
10601
+ documents: documentsResult,
10602
+ roster: rosterResult
10603
+ };
10604
+ }
10605
+ function recordRosterDeletion(name) {
10606
+ let deletions = [];
10607
+ try {
10608
+ if (existsSync21(ROSTER_DELETIONS_PATH)) {
10609
+ deletions = JSON.parse(readFileSync16(ROSTER_DELETIONS_PATH, "utf-8"));
10610
+ }
10611
+ } catch {
10612
+ }
10613
+ if (!deletions.includes(name)) deletions.push(name);
10614
+ writeFileSync11(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
10615
+ }
10616
+ function consumeRosterDeletions() {
10617
+ try {
10618
+ if (!existsSync21(ROSTER_DELETIONS_PATH)) return [];
10619
+ const deletions = JSON.parse(readFileSync16(ROSTER_DELETIONS_PATH, "utf-8"));
10620
+ writeFileSync11(ROSTER_DELETIONS_PATH, "[]");
10621
+ return deletions;
10622
+ } catch {
10623
+ return [];
10624
+ }
10625
+ }
10626
+ function buildRosterBlob(paths) {
10627
+ const rosterPath = paths?.rosterPath ?? path23.join(EXE_AI_DIR, "exe-employees.json");
10628
+ const identityDir = paths?.identityDir ?? path23.join(EXE_AI_DIR, "identity");
10629
+ const configPath = paths?.configPath ?? path23.join(EXE_AI_DIR, "config.json");
10630
+ let roster = [];
10631
+ if (existsSync21(rosterPath)) {
10632
+ try {
10633
+ roster = JSON.parse(readFileSync16(rosterPath, "utf-8"));
10634
+ } catch {
10635
+ }
10636
+ }
10637
+ const identities = {};
10638
+ if (existsSync21(identityDir)) {
10639
+ for (const file of readdirSync5(identityDir).filter((f) => f.endsWith(".md"))) {
10640
+ try {
10641
+ identities[file] = readFileSync16(path23.join(identityDir, file), "utf-8");
10642
+ } catch {
10643
+ }
10644
+ }
10645
+ }
10646
+ let config;
10647
+ if (existsSync21(configPath)) {
10648
+ try {
10649
+ config = JSON.parse(readFileSync16(configPath, "utf-8"));
10650
+ } catch {
10651
+ }
10652
+ }
10653
+ let agentConfig;
10654
+ const agentConfigPath = path23.join(EXE_AI_DIR, "agent-config.json");
10655
+ if (existsSync21(agentConfigPath)) {
10656
+ try {
10657
+ agentConfig = JSON.parse(readFileSync16(agentConfigPath, "utf-8"));
10658
+ } catch {
10659
+ }
10660
+ }
10661
+ const deletedNames = consumeRosterDeletions();
10662
+ const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
10663
+ const hash = crypto9.createHash("sha256").update(content).digest("hex").slice(0, 16);
10664
+ return { roster, identities, config, agentConfig, deletedNames, version: hash };
10665
+ }
10666
+ async function cloudPushRoster(config) {
10667
+ assertSecureEndpoint(config.endpoint);
10668
+ const blob = buildRosterBlob();
10669
+ if (blob.roster.length === 0) return true;
10670
+ try {
10671
+ const client = getClient();
10672
+ const meta = await client.execute(
10673
+ "SELECT value FROM sync_meta WHERE key = 'last_roster_push_version'"
10674
+ );
10675
+ const lastVersion = meta.rows.length > 0 ? Number(meta.rows[0].value) : 0;
10676
+ if (blob.version === lastVersion) return true;
10677
+ } catch {
10678
+ }
10679
+ try {
10680
+ const json = JSON.stringify(blob);
10681
+ const compressed = compress(Buffer.from(json, "utf8"));
10682
+ const encrypted = encryptSyncBlob(compressed);
10683
+ const resp = await fetchWithRetry(`${config.endpoint}/sync/push-roster`, {
10684
+ method: "POST",
10685
+ headers: {
10686
+ Authorization: `Bearer ${config.apiKey}`,
10687
+ "Content-Type": "application/json",
10688
+ "X-Device-Id": loadDeviceId()
10689
+ },
10690
+ body: JSON.stringify({ blob: encrypted })
10691
+ });
10692
+ if (resp.ok) {
10693
+ try {
10694
+ const client = getClient();
10695
+ await client.execute({
10696
+ sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_roster_push_version', ?)",
10697
+ args: [String(blob.version)]
10698
+ });
10699
+ } catch {
10700
+ }
10701
+ }
10702
+ return resp.ok;
10703
+ } catch (err) {
10704
+ process.stderr.write(`[cloud-sync] ROSTER PUSH FAILED: ${err instanceof Error ? err.message : String(err)}
10705
+ `);
10706
+ return false;
10707
+ }
10708
+ }
10709
+ async function cloudPullRoster(config) {
10710
+ assertSecureEndpoint(config.endpoint);
10711
+ try {
10712
+ const resp = await fetchWithRetry(`${config.endpoint}/sync/pull-roster`, {
10713
+ method: "GET",
10714
+ headers: {
10715
+ Authorization: `Bearer ${config.apiKey}`,
10716
+ "X-Device-Id": loadDeviceId()
10717
+ }
10718
+ });
10719
+ if (!resp.ok) return { added: 0 };
10720
+ const data = await resp.json();
10721
+ if (!data.blob) return { added: 0 };
10722
+ const compressed = decryptSyncBlob(data.blob);
10723
+ const json = decompress(compressed).toString("utf8");
10724
+ const remote = JSON.parse(json);
10725
+ return mergeRosterFromRemote(remote);
10726
+ } catch (err) {
10727
+ process.stderr.write(`[cloud-sync] ROSTER PULL FAILED: ${err instanceof Error ? err.message : String(err)}
10728
+ `);
10729
+ return { added: 0 };
10730
+ }
10731
+ }
10732
+ function mergeConfig(remoteConfig, configPath) {
10733
+ const cfgPath = configPath ?? path23.join(EXE_AI_DIR, "config.json");
10734
+ let local = {};
10735
+ if (existsSync21(cfgPath)) {
10736
+ try {
10737
+ local = JSON.parse(readFileSync16(cfgPath, "utf-8"));
10738
+ } catch {
10739
+ }
10740
+ }
10741
+ const merged = { ...remoteConfig, ...local };
10742
+ const dir = path23.dirname(cfgPath);
10743
+ ensurePrivateDirSync(dir);
10744
+ writeFileSync11(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
10745
+ enforcePrivateFileSync(cfgPath);
10746
+ }
10747
+ async function mergeRosterFromRemote(remote, paths) {
10748
+ return withRosterLock(async () => {
10749
+ const rosterPath = paths?.rosterPath ?? void 0;
10750
+ const identityDir = paths?.identityDir ?? path23.join(EXE_AI_DIR, "identity");
10751
+ const localEmployees = await loadEmployees(rosterPath);
10752
+ const localNames = new Set(localEmployees.map((e) => e.name));
10753
+ let added = 0;
10754
+ let identitiesUpdated = 0;
10755
+ for (const remoteEmp of remote.roster) {
10756
+ if (!localNames.has(remoteEmp.name)) {
10757
+ localEmployees.push(remoteEmp);
10758
+ localNames.add(remoteEmp.name);
10759
+ added++;
10760
+ try {
10761
+ registerBinSymlinks(remoteEmp.name);
10762
+ } catch {
10763
+ }
10764
+ }
10765
+ const lookupKey = `${remoteEmp.name}.md`;
10766
+ const matchedKey = Object.keys(remote.identities).find(
10767
+ (k) => k.toLowerCase() === lookupKey.toLowerCase()
10768
+ ) ?? lookupKey;
10769
+ const remoteIdentity = remote.identities[matchedKey];
10770
+ if (remoteIdentity) {
10771
+ if (!existsSync21(identityDir)) mkdirSync9(identityDir, { recursive: true });
10772
+ const idPath = path23.join(identityDir, `${remoteEmp.name}.md`);
10773
+ let localIdentity = null;
10774
+ try {
10775
+ localIdentity = existsSync21(idPath) ? readFileSync16(idPath, "utf-8") : null;
10776
+ } catch {
10777
+ }
10778
+ if (localIdentity !== remoteIdentity) {
10779
+ writeFileSync11(idPath, remoteIdentity, "utf-8");
10780
+ identitiesUpdated++;
10781
+ }
10782
+ }
10783
+ }
10784
+ let removed = 0;
10785
+ if (remote.deletedNames && remote.deletedNames.length > 0) {
10786
+ const toRemove = new Set(remote.deletedNames);
10787
+ const filtered = localEmployees.filter((e) => !toRemove.has(e.name));
10788
+ removed = localEmployees.length - filtered.length;
10789
+ if (removed > 0) {
10790
+ localEmployees.length = 0;
10791
+ localEmployees.push(...filtered);
10792
+ }
10793
+ }
10794
+ if (added > 0 || removed > 0) {
10795
+ await saveEmployees(localEmployees, rosterPath);
10796
+ }
10797
+ if (remote.config && Object.keys(remote.config).length > 0) {
10798
+ try {
10799
+ mergeConfig(remote.config, paths?.configPath);
10800
+ } catch {
10801
+ }
10802
+ }
10803
+ if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
10804
+ try {
10805
+ const agentConfigPath = path23.join(EXE_AI_DIR, "agent-config.json");
10806
+ let local = {};
10807
+ if (existsSync21(agentConfigPath)) {
10808
+ try {
10809
+ local = JSON.parse(readFileSync16(agentConfigPath, "utf-8"));
10810
+ } catch {
10811
+ }
10812
+ }
10813
+ const merged = { ...remote.agentConfig, ...local };
10814
+ ensurePrivateDirSync(path23.dirname(agentConfigPath));
10815
+ writeFileSync11(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
10816
+ enforcePrivateFileSync(agentConfigPath);
10817
+ } catch {
10818
+ }
10819
+ }
10820
+ return { added, identitiesUpdated };
10821
+ });
10822
+ }
10823
+ async function cloudPushBlob(route, data, metaKey, config) {
10824
+ if (data.length === 0) return { ok: true };
10825
+ assertSecureEndpoint(config.endpoint);
10826
+ const json = JSON.stringify(data);
10827
+ const version = Buffer.from(json).length;
10828
+ try {
10829
+ const client = getClient();
10830
+ const meta = await client.execute({
10831
+ sql: "SELECT value FROM sync_meta WHERE key = ?",
10832
+ args: [metaKey]
10833
+ });
10834
+ const lastVersion = meta.rows.length > 0 ? Number(meta.rows[0].value) : 0;
10835
+ if (version === lastVersion) return { ok: true };
10836
+ } catch {
10837
+ }
10838
+ try {
10839
+ const compressed = compress(Buffer.from(json, "utf8"));
10840
+ const encrypted = encryptSyncBlob(compressed);
10841
+ const resp = await fetchWithRetry(`${config.endpoint}${route}`, {
10842
+ method: "POST",
10843
+ headers: {
10844
+ Authorization: `Bearer ${config.apiKey}`,
10845
+ "Content-Type": "application/json",
10846
+ "X-Device-Id": loadDeviceId()
10847
+ },
10848
+ body: JSON.stringify({ blob: encrypted })
10849
+ });
10850
+ if (resp.ok) {
10851
+ try {
10852
+ const client = getClient();
10853
+ await client.execute({
10854
+ sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES (?, ?)",
10855
+ args: [metaKey, String(version)]
10856
+ });
10857
+ } catch {
10858
+ }
10859
+ }
10860
+ return { ok: resp.ok };
10861
+ } catch (err) {
10862
+ logError(`[cloud-sync] PUSH ${route}: ${err instanceof Error ? err.message : String(err)}`);
10863
+ return { ok: false };
10864
+ }
10865
+ }
10866
+ async function cloudPullBlob(route, config) {
10867
+ assertSecureEndpoint(config.endpoint);
10868
+ try {
10869
+ const resp = await fetchWithRetry(`${config.endpoint}${route}`, {
10870
+ method: "GET",
10871
+ headers: {
10872
+ Authorization: `Bearer ${config.apiKey}`,
10873
+ "X-Device-Id": loadDeviceId()
10874
+ }
10875
+ });
10876
+ if (!resp.ok) return null;
10877
+ const data = await resp.json();
10878
+ if (!data.blob) return null;
10879
+ const compressed = decryptSyncBlob(data.blob);
10880
+ const json = decompress(compressed).toString("utf8");
10881
+ return JSON.parse(json);
10882
+ } catch (err) {
10883
+ logError(`[cloud-sync] PULL ${route}: ${err instanceof Error ? err.message : String(err)}`);
10884
+ return null;
10885
+ }
10886
+ }
10887
+ async function cloudPushGlobalProcedures(config) {
10888
+ const client = getClient();
10889
+ const result = await client.execute("SELECT * FROM global_procedures LIMIT 1000");
10890
+ const rows = result.rows;
10891
+ const { ok } = await cloudPushBlob(
10892
+ "/sync/push-global-procedures",
10893
+ rows,
10894
+ "last_global_procedures_push_version",
10895
+ config
10896
+ );
10897
+ return ok;
10898
+ }
10899
+ async function cloudPullGlobalProcedures(config) {
10900
+ const remoteProcs = await cloudPullBlob(
10901
+ "/sync/pull-global-procedures",
10902
+ config
10903
+ );
10904
+ if (!remoteProcs || remoteProcs.length === 0) return { pulled: 0 };
10905
+ const client = getClient();
10906
+ const stmts = remoteProcs.map((p) => ({
10907
+ sql: `INSERT INTO global_procedures
10908
+ (id, title, content, priority, domain, active, created_at, updated_at)
10909
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
10910
+ ON CONFLICT(id) DO UPDATE SET
10911
+ title = excluded.title,
10912
+ content = excluded.content,
10913
+ priority = excluded.priority,
10914
+ domain = excluded.domain,
10915
+ active = excluded.active,
10916
+ updated_at = excluded.updated_at
10917
+ WHERE excluded.updated_at > global_procedures.updated_at`,
10918
+ args: [
10919
+ sqlSafe(p.id),
10920
+ sqlSafe(p.title),
10921
+ sqlSafe(p.content),
10922
+ sqlSafe(p.priority ?? "p0"),
10923
+ sqlSafe(p.domain),
10924
+ sqlSafe(p.active ?? 1),
10925
+ sqlSafe(p.created_at),
10926
+ sqlSafe(p.updated_at)
10927
+ ]
10928
+ }));
10929
+ await client.batch(stmts, "write");
10930
+ return { pulled: remoteProcs.length };
10931
+ }
10932
+ async function cloudPushBehaviors(config) {
10933
+ const client = getClient();
10934
+ const result = await client.execute("SELECT * FROM behaviors LIMIT 10000");
10935
+ const rows = result.rows;
10936
+ const { ok } = await cloudPushBlob(
10937
+ "/sync/push-behaviors",
10938
+ rows,
10939
+ "last_behaviors_push_version",
10940
+ config
10941
+ );
10942
+ return ok;
10943
+ }
10944
+ async function cloudPullBehaviors(config) {
10945
+ const remoteBehaviors = await cloudPullBlob(
10946
+ "/sync/pull-behaviors",
10947
+ config
10948
+ );
10949
+ if (!remoteBehaviors || remoteBehaviors.length === 0) return { pulled: 0 };
10950
+ const client = getClient();
10951
+ let pulled = 0;
10952
+ for (const behavior of remoteBehaviors) {
10953
+ const existing = await client.execute({
10954
+ sql: `SELECT COUNT(*) as cnt FROM behaviors
10955
+ WHERE agent_id = ? AND content = ?`,
10956
+ args: [sqlSafe(behavior.agent_id), sqlSafe(behavior.content)]
10957
+ });
10958
+ if (Number(existing.rows[0]?.cnt) > 0) continue;
10959
+ await client.execute({
10960
+ sql: `INSERT OR IGNORE INTO behaviors
10961
+ (id, agent_id, project_name, domain, content, active, priority, created_at, updated_at)
10962
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
10963
+ args: [
10964
+ sqlSafe(behavior.id),
10965
+ sqlSafe(behavior.agent_id),
10966
+ sqlSafe(behavior.project_name),
10967
+ sqlSafe(behavior.domain),
10968
+ sqlSafe(behavior.content),
10969
+ sqlSafe(behavior.active ?? 1),
10970
+ sqlSafe(behavior.priority ?? "p1"),
10971
+ sqlSafe(behavior.created_at),
10972
+ sqlSafe(behavior.updated_at)
10973
+ ]
10974
+ });
10975
+ pulled++;
10976
+ }
10977
+ return { pulled };
10978
+ }
10979
+ async function cloudPushGraphRAG(config) {
10980
+ const client = getClient();
10981
+ const [entities, relationships, aliases, entityMems, relMems, hyperedges, hyperedgeNodes] = await Promise.all([
10982
+ client.execute("SELECT * FROM entities LIMIT 50000"),
10983
+ client.execute("SELECT * FROM relationships LIMIT 50000"),
10984
+ client.execute("SELECT * FROM entity_aliases LIMIT 50000"),
10985
+ client.execute("SELECT * FROM entity_memories LIMIT 50000"),
10986
+ client.execute("SELECT * FROM relationship_memories LIMIT 50000"),
10987
+ client.execute("SELECT * FROM hyperedges LIMIT 50000"),
10988
+ client.execute("SELECT * FROM hyperedge_nodes LIMIT 50000")
10989
+ ]);
10990
+ const blob = {
10991
+ entities: entities.rows,
10992
+ relationships: relationships.rows,
10993
+ entity_aliases: aliases.rows,
10994
+ entity_memories: entityMems.rows,
10995
+ relationship_memories: relMems.rows,
10996
+ hyperedges: hyperedges.rows,
10997
+ hyperedge_nodes: hyperedgeNodes.rows
10998
+ };
10999
+ const { ok } = await cloudPushBlob(
11000
+ "/sync/push-graphrag",
11001
+ [blob],
11002
+ "last_graphrag_push_version",
11003
+ config
11004
+ );
11005
+ return ok;
11006
+ }
11007
+ async function cloudPullGraphRAG(config) {
11008
+ const data = await cloudPullBlob(
11009
+ "/sync/pull-graphrag",
11010
+ config
11011
+ );
11012
+ if (!data || data.length === 0) return { pulled: 0 };
11013
+ const blob = data[0];
11014
+ const client = getClient();
11015
+ let pulled = 0;
11016
+ if (blob.entities.length > 0) {
11017
+ const stmts = blob.entities.map((e) => ({
11018
+ sql: `INSERT OR IGNORE INTO entities (id, name, type, first_seen, last_seen, properties)
11019
+ VALUES (?, ?, ?, ?, ?, ?)`,
11020
+ args: [sqlSafe(e.id), sqlSafe(e.name), sqlSafe(e.type), sqlSafe(e.first_seen), sqlSafe(e.last_seen), sqlSafe(e.properties ?? "{}")]
11021
+ }));
11022
+ await client.batch(stmts, "write");
11023
+ pulled += stmts.length;
11024
+ }
11025
+ if (blob.relationships.length > 0) {
11026
+ const stmts = blob.relationships.map((r) => ({
11027
+ sql: `INSERT OR IGNORE INTO relationships
11028
+ (id, source_entity_id, target_entity_id, type, weight, timestamp, properties, confidence, confidence_label)
11029
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
11030
+ args: [
11031
+ sqlSafe(r.id),
11032
+ sqlSafe(r.source_entity_id),
11033
+ sqlSafe(r.target_entity_id),
11034
+ sqlSafe(r.type),
11035
+ sqlSafe(r.weight ?? 1),
11036
+ sqlSafe(r.timestamp),
11037
+ sqlSafe(r.properties ?? "{}"),
11038
+ sqlSafe(r.confidence ?? 1),
11039
+ sqlSafe(r.confidence_label ?? "extracted")
11040
+ ]
11041
+ }));
11042
+ await client.batch(stmts, "write");
11043
+ pulled += stmts.length;
11044
+ }
11045
+ if (blob.entity_aliases.length > 0) {
11046
+ const stmts = blob.entity_aliases.map((a) => ({
11047
+ sql: `INSERT OR IGNORE INTO entity_aliases (alias, canonical_entity_id) VALUES (?, ?)`,
11048
+ args: [sqlSafe(a.alias), sqlSafe(a.canonical_entity_id)]
11049
+ }));
11050
+ await client.batch(stmts, "write");
11051
+ pulled += stmts.length;
11052
+ }
11053
+ if (blob.entity_memories.length > 0) {
11054
+ const stmts = blob.entity_memories.map((em) => ({
11055
+ sql: `INSERT OR IGNORE INTO entity_memories (entity_id, memory_id) VALUES (?, ?)`,
11056
+ args: [sqlSafe(em.entity_id), sqlSafe(em.memory_id)]
11057
+ }));
11058
+ await client.batch(stmts, "write");
11059
+ pulled += stmts.length;
11060
+ }
11061
+ if (blob.relationship_memories.length > 0) {
11062
+ const stmts = blob.relationship_memories.map((rm) => ({
11063
+ sql: `INSERT OR IGNORE INTO relationship_memories (relationship_id, memory_id) VALUES (?, ?)`,
11064
+ args: [sqlSafe(rm.relationship_id), sqlSafe(rm.memory_id)]
11065
+ }));
11066
+ await client.batch(stmts, "write");
11067
+ pulled += stmts.length;
11068
+ }
11069
+ if (blob.hyperedges.length > 0) {
11070
+ const stmts = blob.hyperedges.map((h) => ({
11071
+ sql: `INSERT OR IGNORE INTO hyperedges (id, label, relation, confidence, timestamp)
11072
+ VALUES (?, ?, ?, ?, ?)`,
11073
+ args: [sqlSafe(h.id), sqlSafe(h.label), sqlSafe(h.relation), sqlSafe(h.confidence ?? 1), sqlSafe(h.timestamp)]
11074
+ }));
11075
+ await client.batch(stmts, "write");
11076
+ pulled += stmts.length;
11077
+ }
11078
+ if (blob.hyperedge_nodes.length > 0) {
11079
+ const stmts = blob.hyperedge_nodes.map((hn) => ({
11080
+ sql: `INSERT OR IGNORE INTO hyperedge_nodes (hyperedge_id, entity_id) VALUES (?, ?)`,
11081
+ args: [sqlSafe(hn.hyperedge_id), sqlSafe(hn.entity_id)]
11082
+ }));
11083
+ await client.batch(stmts, "write");
11084
+ pulled += stmts.length;
11085
+ }
11086
+ return { pulled };
11087
+ }
11088
+ async function cloudPushTasks(config) {
11089
+ const client = getClient();
11090
+ const result = await client.execute("SELECT * FROM tasks LIMIT 10000");
11091
+ const rows = result.rows;
11092
+ const { ok } = await cloudPushBlob(
11093
+ "/sync/push-tasks",
11094
+ rows,
11095
+ "last_tasks_push_version",
11096
+ config
11097
+ );
11098
+ return ok;
11099
+ }
11100
+ async function cloudPullTasks(config) {
11101
+ const remoteTasks = await cloudPullBlob(
11102
+ "/sync/pull-tasks",
11103
+ config
11104
+ );
11105
+ if (!remoteTasks || remoteTasks.length === 0) return { pulled: 0 };
11106
+ const client = getClient();
11107
+ const stmts = remoteTasks.map((t) => ({
11108
+ sql: `INSERT OR IGNORE INTO tasks
11109
+ (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, created_at, updated_at,
11110
+ blocked_by, parent_task_id, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at)
11111
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
11112
+ args: [
11113
+ sqlSafe(t.id),
11114
+ sqlSafe(t.title),
11115
+ sqlSafe(t.assigned_to),
11116
+ sqlSafe(t.assigned_by),
11117
+ sqlSafe(t.project_name),
11118
+ sqlSafe(t.priority ?? "p1"),
11119
+ sqlSafe(t.status ?? "open"),
11120
+ sqlSafe(t.task_file),
11121
+ sqlSafe(t.created_at),
11122
+ sqlSafe(t.updated_at),
11123
+ sqlSafe(t.blocked_by),
11124
+ sqlSafe(t.parent_task_id),
11125
+ sqlSafe(t.budget_tokens),
11126
+ sqlSafe(t.budget_fallback_model),
11127
+ sqlSafe(t.tokens_used ?? 0),
11128
+ sqlSafe(t.tokens_warned_at)
11129
+ ]
11130
+ }));
11131
+ await client.batch(stmts, "write");
11132
+ return { pulled: remoteTasks.length };
11133
+ }
11134
+ async function cloudPushConversations(config) {
11135
+ const client = getClient();
11136
+ const result = await client.execute("SELECT * FROM conversations LIMIT 50000");
11137
+ const rows = result.rows;
11138
+ const { ok } = await cloudPushBlob(
11139
+ "/sync/push-conversations",
11140
+ rows,
11141
+ "last_conversations_push_version",
11142
+ config
11143
+ );
11144
+ return ok;
11145
+ }
11146
+ async function cloudPullConversations(config) {
11147
+ const remoteConvos = await cloudPullBlob(
11148
+ "/sync/pull-conversations",
11149
+ config
11150
+ );
11151
+ if (!remoteConvos || remoteConvos.length === 0) return { pulled: 0 };
11152
+ const client = getClient();
11153
+ const stmts = remoteConvos.map((c) => ({
11154
+ sql: `INSERT OR IGNORE INTO conversations
11155
+ (id, platform, external_id, sender_id, sender_name, sender_phone, sender_email,
11156
+ recipient_id, channel_id, thread_id, reply_to_id, content_text, content_media,
11157
+ content_metadata, agent_response, agent_name, timestamp, ingested_at)
11158
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
11159
+ args: [
11160
+ sqlSafe(c.id),
11161
+ sqlSafe(c.platform),
11162
+ sqlSafe(c.external_id),
11163
+ sqlSafe(c.sender_id),
11164
+ sqlSafe(c.sender_name),
11165
+ sqlSafe(c.sender_phone),
11166
+ sqlSafe(c.sender_email),
11167
+ sqlSafe(c.recipient_id),
11168
+ sqlSafe(c.channel_id),
11169
+ sqlSafe(c.thread_id),
11170
+ sqlSafe(c.reply_to_id),
11171
+ sqlSafe(c.content_text),
11172
+ sqlSafe(c.content_media),
11173
+ sqlSafe(c.content_metadata),
11174
+ sqlSafe(c.agent_response),
11175
+ sqlSafe(c.agent_name),
11176
+ sqlSafe(c.timestamp),
11177
+ sqlSafe(c.ingested_at)
11178
+ ]
11179
+ }));
11180
+ await client.batch(stmts, "write");
11181
+ return { pulled: remoteConvos.length };
11182
+ }
11183
+ async function cloudPushDocuments(config) {
11184
+ const client = getClient();
11185
+ const [workspaces, documents] = await Promise.all([
11186
+ client.execute("SELECT * FROM workspaces LIMIT 1000"),
11187
+ client.execute("SELECT * FROM documents LIMIT 10000")
11188
+ ]);
11189
+ const blob = {
11190
+ workspaces: workspaces.rows,
11191
+ documents: documents.rows
11192
+ };
11193
+ const { ok } = await cloudPushBlob(
11194
+ "/sync/push-documents",
11195
+ [blob],
11196
+ "last_documents_push_version",
11197
+ config
11198
+ );
11199
+ return ok;
11200
+ }
11201
+ async function cloudPullDocuments(config) {
11202
+ const data = await cloudPullBlob(
11203
+ "/sync/pull-documents",
11204
+ config
11205
+ );
11206
+ if (!data || data.length === 0) return { pulled: 0 };
11207
+ const blob = data[0];
11208
+ const client = getClient();
11209
+ let pulled = 0;
11210
+ if (blob.workspaces.length > 0) {
11211
+ const stmts = blob.workspaces.map((w) => ({
11212
+ sql: `INSERT OR IGNORE INTO workspaces (id, slug, name, owner_agent_id, created_at, metadata)
11213
+ VALUES (?, ?, ?, ?, ?, ?)`,
11214
+ args: [sqlSafe(w.id), sqlSafe(w.slug), sqlSafe(w.name), sqlSafe(w.owner_agent_id), sqlSafe(w.created_at), sqlSafe(w.metadata)]
11215
+ }));
11216
+ await client.batch(stmts, "write");
11217
+ pulled += stmts.length;
11218
+ }
11219
+ if (blob.documents.length > 0) {
11220
+ const stmts = blob.documents.map((d) => ({
11221
+ sql: `INSERT OR IGNORE INTO documents
11222
+ (id, workspace_id, filename, mime, source_type, user_id, uploaded_at, metadata)
11223
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
11224
+ args: [
11225
+ sqlSafe(d.id),
11226
+ sqlSafe(d.workspace_id),
11227
+ sqlSafe(d.filename),
11228
+ sqlSafe(d.mime),
11229
+ sqlSafe(d.source_type),
11230
+ sqlSafe(d.user_id),
11231
+ sqlSafe(d.uploaded_at),
11232
+ sqlSafe(d.metadata)
11233
+ ]
11234
+ }));
11235
+ await client.batch(stmts, "write");
11236
+ pulled += stmts.length;
11237
+ }
11238
+ return { pulled };
11239
+ }
11240
+ var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, _pgPromise, _pgFailed, ROSTER_DELETIONS_PATH;
11241
+ var init_cloud_sync = __esm({
11242
+ "src/lib/cloud-sync.ts"() {
11243
+ "use strict";
11244
+ init_database();
11245
+ init_crypto();
11246
+ init_compress();
11247
+ init_license();
11248
+ init_config();
11249
+ init_crdt_sync();
11250
+ init_employees();
11251
+ init_secure_files();
11252
+ LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
11253
+ FETCH_TIMEOUT_MS = 3e4;
11254
+ PUSH_BATCH_SIZE = 5e3;
11255
+ ROSTER_LOCK_PATH = path23.join(EXE_AI_DIR, "roster-merge.lock");
11256
+ LOCK_STALE_MS = 3e4;
11257
+ _pgPromise = null;
11258
+ _pgFailed = false;
11259
+ ROSTER_DELETIONS_PATH = path23.join(EXE_AI_DIR, "roster-deletions.json");
11260
+ }
11261
+ });
11262
+
11263
+ // src/lib/code-chunker.ts
11264
+ import ts from "typescript";
11265
+ function chunkSourceFile(source, fileName = "file.ts") {
11266
+ const sourceFile = ts.createSourceFile(
11267
+ fileName,
11268
+ source,
11269
+ ts.ScriptTarget.Latest,
11270
+ true,
11271
+ // setParentNodes
11272
+ fileName.endsWith(".tsx") || fileName.endsWith(".jsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS
11273
+ );
11274
+ const chunks = [];
11275
+ const lines = source.split("\n");
11276
+ const importLines = [];
11277
+ function getLineNumber(pos) {
11278
+ return sourceFile.getLineAndCharacterOfPosition(pos).line + 1;
11279
+ }
11280
+ function getLeadingComment(node) {
11281
+ const fullText = sourceFile.getFullText();
11282
+ const ranges = ts.getLeadingCommentRanges(fullText, node.getFullStart());
11283
+ if (!ranges || ranges.length === 0) return void 0;
11284
+ return ranges.map((r) => fullText.slice(r.pos, r.end)).join("\n");
11285
+ }
11286
+ function getNodeText(node) {
11287
+ return node.getText(sourceFile);
11288
+ }
11289
+ function getName(node) {
11290
+ if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
11291
+ return node.name?.getText(sourceFile) ?? "(anonymous)";
11292
+ }
11293
+ if (ts.isClassDeclaration(node)) {
11294
+ return node.name?.getText(sourceFile) ?? "(anonymous class)";
11295
+ }
11296
+ if (ts.isInterfaceDeclaration(node)) {
11297
+ return node.name.getText(sourceFile);
11298
+ }
11299
+ if (ts.isTypeAliasDeclaration(node)) {
11300
+ return node.name.getText(sourceFile);
11301
+ }
11302
+ if (ts.isEnumDeclaration(node)) {
11303
+ return node.name.getText(sourceFile);
11304
+ }
11305
+ if (ts.isVariableStatement(node)) {
11306
+ const decls = node.declarationList.declarations;
11307
+ return decls.map((d) => d.name.getText(sourceFile)).join(", ");
11308
+ }
11309
+ if (ts.isExportAssignment(node)) {
11310
+ return "default export";
11311
+ }
11312
+ return "(unknown)";
11313
+ }
11314
+ function visitTopLevel(node) {
11315
+ if (ts.isImportDeclaration(node)) {
11316
+ importLines.push({
11317
+ start: getLineNumber(node.getStart(sourceFile)),
11318
+ end: getLineNumber(node.getEnd())
11319
+ });
11320
+ return;
11321
+ }
11322
+ if (ts.isFunctionDeclaration(node)) {
9547
11323
  chunks.push({
9548
11324
  kind: "function",
9549
11325
  name: getName(node),
@@ -9678,13 +11454,13 @@ __export(graph_rag_exports, {
9678
11454
  resolveAlias: () => resolveAlias,
9679
11455
  storeExtraction: () => storeExtraction
9680
11456
  });
9681
- import crypto7 from "crypto";
11457
+ import crypto10 from "crypto";
9682
11458
  function normalizeEntityName(name) {
9683
11459
  return name.replace(/\s*\([^)]*\)\s*/g, "").trim().toLowerCase();
9684
11460
  }
9685
11461
  function entityId(name, type) {
9686
11462
  const normalized = normalizeEntityName(name);
9687
- return crypto7.createHash("sha256").update(`${normalized}::${type.toLowerCase()}`).digest("hex").slice(0, 16);
11463
+ return crypto10.createHash("sha256").update(`${normalized}::${type.toLowerCase()}`).digest("hex").slice(0, 16);
9688
11464
  }
9689
11465
  async function resolveAlias(client, name) {
9690
11466
  const normalized = normalizeEntityName(name);
@@ -9934,7 +11710,7 @@ async function storeExtraction(client, extraction, memoryId, timestamp) {
9934
11710
  const targetAlias = await resolveAlias(client, r.target);
9935
11711
  const sourceId = sourceAlias ?? entityId(r.source, r.sourceType);
9936
11712
  const targetId = targetAlias ?? entityId(r.target, r.targetType);
9937
- const relId = crypto7.randomUUID().slice(0, 16);
11713
+ const relId = crypto10.randomUUID().slice(0, 16);
9938
11714
  try {
9939
11715
  await client.execute({
9940
11716
  sql: `INSERT OR IGNORE INTO entities (id, name, type, first_seen, last_seen)
@@ -9997,7 +11773,7 @@ async function storeExtraction(client, extraction, memoryId, timestamp) {
9997
11773
  }
9998
11774
  }
9999
11775
  for (const h of extraction.hyperedges) {
10000
- const hId = crypto7.randomUUID().slice(0, 16);
11776
+ const hId = crypto10.randomUUID().slice(0, 16);
10001
11777
  try {
10002
11778
  await client.execute({
10003
11779
  sql: `INSERT OR IGNORE INTO hyperedges (id, label, relation, confidence, timestamp)
@@ -10061,7 +11837,7 @@ async function extractBatch(client, batchSize = 50, model = "claude-haiku-4-5-20
10061
11837
  totalEntities += stored.entitiesStored;
10062
11838
  totalRelationships += stored.relationshipsStored;
10063
11839
  }
10064
- const contentHash = crypto7.createHash("sha256").update(rawContent).digest("hex").slice(0, 32);
11840
+ const contentHash = crypto10.createHash("sha256").update(rawContent).digest("hex").slice(0, 32);
10065
11841
  await client.execute({
10066
11842
  sql: "UPDATE memories SET graph_extracted = 1, content_hash = ?, graph_extracted_hash = ? WHERE id = ?",
10067
11843
  args: [contentHash, contentHash, memoryId]
@@ -10178,8 +11954,8 @@ __export(wiki_sync_exports, {
10178
11954
  listWorkspaces: () => listWorkspaces,
10179
11955
  syncMemories: () => syncMemories
10180
11956
  });
10181
- async function wikiRequest(config, path25, method = "GET", body) {
10182
- const url = `${config.wikiUrl}/api/v1${path25}`;
11957
+ async function wikiRequest(config, path28, method = "GET", body) {
11958
+ const url = `${config.wikiUrl}/api/v1${path28}`;
10183
11959
  const headers = {
10184
11960
  "Authorization": `Bearer ${config.wikiApiKey}`,
10185
11961
  "Content-Type": "application/json"
@@ -10191,7 +11967,7 @@ async function wikiRequest(config, path25, method = "GET", body) {
10191
11967
  signal: AbortSignal.timeout(3e4)
10192
11968
  });
10193
11969
  if (!response.ok) {
10194
- throw new Error(`Wiki API ${method} ${path25}: ${response.status} ${response.statusText}`);
11970
+ throw new Error(`Wiki API ${method} ${path28}: ${response.status} ${response.statusText}`);
10195
11971
  }
10196
11972
  return response.json();
10197
11973
  }
@@ -10303,8 +12079,8 @@ __export(token_spend_exports, {
10303
12079
  import { readdir } from "fs/promises";
10304
12080
  import { createReadStream } from "fs";
10305
12081
  import { createInterface } from "readline";
10306
- import path21 from "path";
10307
- import os13 from "os";
12082
+ import path24 from "path";
12083
+ import os14 from "os";
10308
12084
  function getPricing(model) {
10309
12085
  if (MODEL_PRICING[model]) return MODEL_PRICING[model];
10310
12086
  const stripped = model.replace(/-\d{8}$/, "");
@@ -10331,18 +12107,18 @@ async function getAgentSpend(period = "7d") {
10331
12107
  for (const row of dbResult.rows) {
10332
12108
  sessionAgent.set(row.session_uuid, row.agent_id);
10333
12109
  }
10334
- const claudeDir = path21.join(os13.homedir(), ".claude", "projects");
12110
+ const claudeDir = path24.join(os14.homedir(), ".claude", "projects");
10335
12111
  let projectDirs = [];
10336
12112
  try {
10337
12113
  const entries = await readdir(claudeDir);
10338
- projectDirs = entries.map((e) => path21.join(claudeDir, e));
12114
+ projectDirs = entries.map((e) => path24.join(claudeDir, e));
10339
12115
  } catch {
10340
12116
  return [];
10341
12117
  }
10342
12118
  const agentTotals = /* @__PURE__ */ new Map();
10343
12119
  for (const [sessionUuid, agentId] of sessionAgent) {
10344
12120
  for (const dir of projectDirs) {
10345
- const jsonlPath = path21.join(dir, `${sessionUuid}.jsonl`);
12121
+ const jsonlPath = path24.join(dir, `${sessionUuid}.jsonl`);
10346
12122
  try {
10347
12123
  const usage = await extractSessionUsage(jsonlPath);
10348
12124
  if (usage.input === 0 && usage.output === 0) continue;
@@ -10467,11 +12243,11 @@ __export(update_check_exports, {
10467
12243
  getRemoteVersion: () => getRemoteVersion
10468
12244
  });
10469
12245
  import { execSync as execSync11 } from "child_process";
10470
- import { readFileSync as readFileSync15 } from "fs";
10471
- import path22 from "path";
12246
+ import { readFileSync as readFileSync17 } from "fs";
12247
+ import path25 from "path";
10472
12248
  function getLocalVersion(packageRoot) {
10473
- const pkgPath = path22.join(packageRoot, "package.json");
10474
- const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
12249
+ const pkgPath = path25.join(packageRoot, "package.json");
12250
+ const pkg = JSON.parse(readFileSync17(pkgPath, "utf-8"));
10475
12251
  return pkg.version;
10476
12252
  }
10477
12253
  function getRemoteVersion() {
@@ -10514,16 +12290,16 @@ __export(ws_auth_exports, {
10514
12290
  deriveWsAuthToken: () => deriveWsAuthToken,
10515
12291
  hashAuthToken: () => hashAuthToken
10516
12292
  });
10517
- import crypto8 from "crypto";
12293
+ import crypto11 from "crypto";
10518
12294
  function deriveWsAuthToken(masterKey) {
10519
- return Buffer.from(crypto8.hkdfSync("sha256", masterKey, "", WS_AUTH_HKDF_INFO, 32));
12295
+ return Buffer.from(crypto11.hkdfSync("sha256", masterKey, "", WS_AUTH_HKDF_INFO, 32));
10520
12296
  }
10521
12297
  function deriveOrgId(masterKey) {
10522
- const raw = Buffer.from(crypto8.hkdfSync("sha256", masterKey, "", ORG_ID_HKDF_INFO, 32));
10523
- return crypto8.createHash("sha256").update(raw).digest("hex").slice(0, 32);
12298
+ const raw = Buffer.from(crypto11.hkdfSync("sha256", masterKey, "", ORG_ID_HKDF_INFO, 32));
12299
+ return crypto11.createHash("sha256").update(raw).digest("hex").slice(0, 32);
10524
12300
  }
10525
12301
  function hashAuthToken(token) {
10526
- return crypto8.createHash("sha256").update(token).digest("hex");
12302
+ return crypto11.createHash("sha256").update(token).digest("hex");
10527
12303
  }
10528
12304
  var WS_AUTH_HKDF_INFO, ORG_ID_HKDF_INFO;
10529
12305
  var init_ws_auth = __esm({
@@ -10541,14 +12317,14 @@ __export(device_registry_exports, {
10541
12317
  resolveTargetDevice: () => resolveTargetDevice,
10542
12318
  setFriendlyName: () => setFriendlyName
10543
12319
  });
10544
- import crypto9 from "crypto";
10545
- import os14 from "os";
10546
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync8, existsSync as existsSync19 } from "fs";
10547
- import path23 from "path";
12320
+ import crypto12 from "crypto";
12321
+ import os15 from "os";
12322
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, mkdirSync as mkdirSync10, existsSync as existsSync22 } from "fs";
12323
+ import path26 from "path";
10548
12324
  function getDeviceInfo() {
10549
- if (existsSync19(DEVICE_JSON_PATH)) {
12325
+ if (existsSync22(DEVICE_JSON_PATH)) {
10550
12326
  try {
10551
- const raw = readFileSync16(DEVICE_JSON_PATH, "utf8");
12327
+ const raw = readFileSync18(DEVICE_JSON_PATH, "utf8");
10552
12328
  const data = JSON.parse(raw);
10553
12329
  if (data.deviceId && data.friendlyName && data.hostname) {
10554
12330
  return data;
@@ -10556,20 +12332,20 @@ function getDeviceInfo() {
10556
12332
  } catch {
10557
12333
  }
10558
12334
  }
10559
- const hostname = os14.hostname();
12335
+ const hostname = os15.hostname();
10560
12336
  const info = {
10561
- deviceId: crypto9.randomUUID(),
12337
+ deviceId: crypto12.randomUUID(),
10562
12338
  friendlyName: hostname.replace(/\./g, "-").toLowerCase(),
10563
12339
  hostname
10564
12340
  };
10565
- mkdirSync8(path23.dirname(DEVICE_JSON_PATH), { recursive: true });
10566
- writeFileSync10(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
12341
+ mkdirSync10(path26.dirname(DEVICE_JSON_PATH), { recursive: true });
12342
+ writeFileSync12(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
10567
12343
  return info;
10568
12344
  }
10569
12345
  function setFriendlyName(name) {
10570
12346
  const info = getDeviceInfo();
10571
12347
  info.friendlyName = name;
10572
- writeFileSync10(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
12348
+ writeFileSync12(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
10573
12349
  }
10574
12350
  async function resolveTargetDevice(targetAgent, targetProject) {
10575
12351
  const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
@@ -10603,7 +12379,7 @@ var init_device_registry = __esm({
10603
12379
  "src/lib/device-registry.ts"() {
10604
12380
  "use strict";
10605
12381
  init_config();
10606
- DEVICE_JSON_PATH = path23.join(EXE_AI_DIR, "device.json");
12382
+ DEVICE_JSON_PATH = path26.join(EXE_AI_DIR, "device.json");
10607
12383
  }
10608
12384
  });
10609
12385
 
@@ -10628,18 +12404,18 @@ function assertSecureWsUrl(url) {
10628
12404
  `Malformed WebSocket URL rejected: "${url}".`
10629
12405
  );
10630
12406
  }
10631
- if (LOCALHOST_PATTERNS.test(parsed.hostname)) return;
12407
+ if (LOCALHOST_PATTERNS2.test(parsed.hostname)) return;
10632
12408
  throw new Error(
10633
12409
  `Insecure WebSocket URL rejected: "${url}". Use wss:// for remote hosts. Plain ws:// is only allowed for localhost.`
10634
12410
  );
10635
12411
  }
10636
12412
  }
10637
- var LOCALHOST_PATTERNS;
12413
+ var LOCALHOST_PATTERNS2;
10638
12414
  var init_gateway_client = __esm({
10639
12415
  "src/lib/gateway-client.ts"() {
10640
12416
  "use strict";
10641
12417
  init_message_queue();
10642
- LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
12418
+ LOCALHOST_PATTERNS2 = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
10643
12419
  }
10644
12420
  });
10645
12421
 
@@ -10827,10 +12603,10 @@ __export(messaging_exports, {
10827
12603
  sendMessage: () => sendMessage,
10828
12604
  setWsClientSend: () => setWsClientSend
10829
12605
  });
10830
- import crypto10 from "crypto";
12606
+ import crypto13 from "crypto";
10831
12607
  function generateUlid() {
10832
12608
  const timestamp = Date.now().toString(36).padStart(10, "0");
10833
- const random = crypto10.randomBytes(10).toString("hex").slice(0, 16);
12609
+ const random = crypto13.randomBytes(10).toString("hex").slice(0, 16);
10834
12610
  return (timestamp + random).toUpperCase();
10835
12611
  }
10836
12612
  function rowToMessage(row) {
@@ -11089,13 +12865,13 @@ init_memory();
11089
12865
  init_daemon_protocol();
11090
12866
  init_daemon_auth();
11091
12867
  init_daemon_orchestration();
11092
- import os15 from "os";
12868
+ import os16 from "os";
11093
12869
  import net2 from "net";
11094
- import { writeFileSync as writeFileSync11, unlinkSync as unlinkSync7, mkdirSync as mkdirSync9, existsSync as existsSync20, readFileSync as readFileSync17, chmodSync as chmodSync2 } from "fs";
11095
- import path24 from "path";
12870
+ import { writeFileSync as writeFileSync13, unlinkSync as unlinkSync9, mkdirSync as mkdirSync11, existsSync as existsSync23, readFileSync as readFileSync19, chmodSync as chmodSync2 } from "fs";
12871
+ import path27 from "path";
11096
12872
  import { getLlama } from "node-llama-cpp";
11097
- var SOCKET_PATH2 = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path24.join(EXE_AI_DIR, "exed.sock");
11098
- var PID_PATH2 = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path24.join(EXE_AI_DIR, "exed.pid");
12873
+ var SOCKET_PATH2 = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path27.join(EXE_AI_DIR, "exed.sock");
12874
+ var PID_PATH2 = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path27.join(EXE_AI_DIR, "exed.pid");
11099
12875
  var MODEL_FILE = "jina-embeddings-v5-small-q4_k_m.gguf";
11100
12876
  var IDLE_TIMEOUT_MS = 15 * 60 * 1e3;
11101
12877
  var REVIEW_POLL_INTERVAL_MS = 60 * 1e3;
@@ -11123,8 +12899,8 @@ function enqueue(queue, entry) {
11123
12899
  queue.push(entry);
11124
12900
  }
11125
12901
  async function loadModel() {
11126
- const modelPath = path24.join(MODELS_DIR, MODEL_FILE);
11127
- if (!existsSync20(modelPath)) {
12902
+ const modelPath = path27.join(MODELS_DIR, MODEL_FILE);
12903
+ if (!existsSync23(modelPath)) {
11128
12904
  process.stderr.write(`[exed] FATAL: model not found at ${modelPath}
11129
12905
  `);
11130
12906
  process.exit(1);
@@ -11194,6 +12970,11 @@ function checkIdle() {
11194
12970
  }
11195
12971
  async function shutdown() {
11196
12972
  resetIdleTimer();
12973
+ try {
12974
+ const { stopProjectionWorker: stopProjectionWorker2 } = await Promise.resolve().then(() => (init_projection_worker(), projection_worker_exports));
12975
+ stopProjectionWorker2();
12976
+ } catch {
12977
+ }
11197
12978
  try {
11198
12979
  const { disposeShards: disposeShards2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
11199
12980
  disposeShards2();
@@ -11215,11 +12996,11 @@ async function shutdown() {
11215
12996
  }
11216
12997
  _llama = null;
11217
12998
  try {
11218
- unlinkSync7(SOCKET_PATH2);
12999
+ unlinkSync9(SOCKET_PATH2);
11219
13000
  } catch {
11220
13001
  }
11221
13002
  try {
11222
- unlinkSync7(PID_PATH2);
13003
+ unlinkSync9(PID_PATH2);
11223
13004
  } catch {
11224
13005
  }
11225
13006
  process.stderr.write("[exed] Shutdown complete.\n");
@@ -11355,28 +13136,28 @@ async function handleIngest(req) {
11355
13136
  }
11356
13137
  }
11357
13138
  function startServer() {
11358
- mkdirSync9(path24.dirname(SOCKET_PATH2), { recursive: true });
13139
+ mkdirSync11(path27.dirname(SOCKET_PATH2), { recursive: true });
11359
13140
  try {
11360
- chmodSync2(path24.dirname(SOCKET_PATH2), 448);
13141
+ chmodSync2(path27.dirname(SOCKET_PATH2), 448);
11361
13142
  } catch {
11362
13143
  }
11363
13144
  _daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV2] ?? null);
11364
13145
  for (const oldFile of ["embed.sock", "embed.pid"]) {
11365
- const oldPath = path24.join(path24.dirname(SOCKET_PATH2), oldFile);
13146
+ const oldPath = path27.join(path27.dirname(SOCKET_PATH2), oldFile);
11366
13147
  try {
11367
13148
  if (oldFile.endsWith(".pid")) {
11368
- const pid = parseInt(readFileSync17(oldPath, "utf8").trim(), 10);
13149
+ const pid = parseInt(readFileSync19(oldPath, "utf8").trim(), 10);
11369
13150
  if (pid > 0) try {
11370
13151
  process.kill(pid, "SIGKILL");
11371
13152
  } catch {
11372
13153
  }
11373
13154
  }
11374
- unlinkSync7(oldPath);
13155
+ unlinkSync9(oldPath);
11375
13156
  } catch {
11376
13157
  }
11377
13158
  }
11378
13159
  try {
11379
- unlinkSync7(SOCKET_PATH2);
13160
+ unlinkSync9(SOCKET_PATH2);
11380
13161
  } catch {
11381
13162
  }
11382
13163
  const server = net2.createServer((socket) => {
@@ -11466,7 +13247,7 @@ function startServer() {
11466
13247
  chmodSync2(SOCKET_PATH2, 384);
11467
13248
  } catch {
11468
13249
  }
11469
- writeFileSync11(PID_PATH2, String(process.pid));
13250
+ writeFileSync13(PID_PATH2, String(process.pid));
11470
13251
  try {
11471
13252
  chmodSync2(PID_PATH2, 384);
11472
13253
  } catch {
@@ -11666,6 +13447,35 @@ function startConsolidation() {
11666
13447
  `);
11667
13448
  });
11668
13449
  }
13450
+ var CLOUD_SYNC_INTERVAL_MS = Number(process.env.EXE_CLOUD_SYNC_INTERVAL_MS) || 5 * 60 * 1e3;
13451
+ function startCloudSyncTimer() {
13452
+ const tick = async () => {
13453
+ try {
13454
+ const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
13455
+ const config = await loadConfig2();
13456
+ if (!config.cloud?.apiKey || !config.cloud?.endpoint) return;
13457
+ const { cloudSync: cloudSync2 } = await Promise.resolve().then(() => (init_cloud_sync(), cloud_sync_exports));
13458
+ const result = await cloudSync2({
13459
+ apiKey: config.cloud.apiKey,
13460
+ endpoint: config.cloud.endpoint
13461
+ });
13462
+ if (result.pushed > 0 || result.pulled > 0) {
13463
+ process.stderr.write(
13464
+ `[exed] Cloud sync: pushed=${result.pushed} pulled=${result.pulled} total=${result.totalMemories}
13465
+ `
13466
+ );
13467
+ }
13468
+ } catch (err) {
13469
+ process.stderr.write(`[exed] Cloud sync error (non-fatal): ${err instanceof Error ? err.message : String(err)}
13470
+ `);
13471
+ }
13472
+ };
13473
+ const timer = setInterval(() => void tick(), CLOUD_SYNC_INTERVAL_MS);
13474
+ timer.unref();
13475
+ setTimeout(() => void tick(), 3e4);
13476
+ process.stderr.write(`[exed] Cloud sync timer started (every ${CLOUD_SYNC_INTERVAL_MS / 6e4}m)
13477
+ `);
13478
+ }
11669
13479
  var SKILL_SWEEP_INTERVAL_MS = 6 * 60 * 60 * 1e3;
11670
13480
  function startSkillSweep() {
11671
13481
  const tick = async () => {
@@ -11763,7 +13573,7 @@ function startWikiSync() {
11763
13573
  });
11764
13574
  }
11765
13575
  var AGENT_STATS_INTERVAL_MS = 60 * 1e3;
11766
- var AGENT_STATS_PATH = path24.join(EXE_AI_DIR, "agent-stats.json");
13576
+ var AGENT_STATS_PATH = path27.join(EXE_AI_DIR, "agent-stats.json");
11767
13577
  async function writeAgentStats() {
11768
13578
  if (!await ensureStoreForPolling()) return;
11769
13579
  try {
@@ -11821,7 +13631,7 @@ async function writeAgentStats() {
11821
13631
  pid: process.pid
11822
13632
  }
11823
13633
  };
11824
- writeFileSync11(AGENT_STATS_PATH, JSON.stringify(stats, null, 2), "utf8");
13634
+ writeFileSync13(AGENT_STATS_PATH, JSON.stringify(stats, null, 2), "utf8");
11825
13635
  } catch (err) {
11826
13636
  process.stderr.write(`[exed] Agent stats error: ${err instanceof Error ? err.message : String(err)}
11827
13637
  `);
@@ -11893,12 +13703,12 @@ function startIntercomQueueDrain() {
11893
13703
  const hasInProgressTask = (session) => {
11894
13704
  try {
11895
13705
  const { baseAgentName: ban } = (init_employees(), __toCommonJS(employees_exports));
11896
- const path25 = __require("path");
11897
- const { existsSync: existsSync21 } = __require("fs");
11898
- const os16 = __require("os");
13706
+ const path28 = __require("path");
13707
+ const { existsSync: existsSync24 } = __require("fs");
13708
+ const os17 = __require("os");
11899
13709
  const agent = ban(session.split("-")[0] ?? session);
11900
- const markerPath = path25.join(os16.homedir(), ".exe-os", "session-cache", `current-task-${agent}.json`);
11901
- return existsSync21(markerPath);
13710
+ const markerPath = path28.join(os17.homedir(), ".exe-os", "session-cache", `current-task-${agent}.json`);
13711
+ return existsSync24(markerPath);
11902
13712
  } catch {
11903
13713
  return false;
11904
13714
  }
@@ -11987,7 +13797,7 @@ function startAutoWake() {
11987
13797
  process.stderr.write(`[exed] Auto-wake started (every ${AUTO_WAKE_INTERVAL_MS / 1e3}s)
11988
13798
  `);
11989
13799
  }
11990
- var TOTAL_MEM_GB = os15.totalmem() / 1024 ** 3;
13800
+ var TOTAL_MEM_GB = os16.totalmem() / 1024 ** 3;
11991
13801
  var RSS_WARN_BYTES = Number(process.env.EXE_RSS_WARN_MB) * 1024 * 1024 || (TOTAL_MEM_GB >= 64 ? 6 * 1024 ** 3 : TOTAL_MEM_GB >= 32 ? 4 * 1024 ** 3 : 1024 ** 3);
11992
13802
  var RSS_RESTART_BYTES = Number(process.env.EXE_RSS_RESTART_MB) * 1024 * 1024 || (TOTAL_MEM_GB >= 64 ? 8 * 1024 ** 3 : TOTAL_MEM_GB >= 32 ? 6 * 1024 ** 3 : 2 * 1024 ** 3);
11993
13803
  var RSS_CHECK_INTERVAL_MS = 30 * 1e3;
@@ -12023,8 +13833,8 @@ process.on("SIGINT", () => void shutdown());
12023
13833
  process.on("SIGTERM", () => void shutdown());
12024
13834
  function checkExistingDaemon() {
12025
13835
  try {
12026
- if (!existsSync20(PID_PATH2)) return false;
12027
- const pid = parseInt(readFileSync17(PID_PATH2, "utf8").trim(), 10);
13836
+ if (!existsSync23(PID_PATH2)) return false;
13837
+ const pid = parseInt(readFileSync19(PID_PATH2, "utf8").trim(), 10);
12028
13838
  if (!pid || isNaN(pid)) return false;
12029
13839
  process.kill(pid, 0);
12030
13840
  process.stderr.write(`[exed] Another daemon is already running (PID ${pid}). Exiting.
@@ -12037,7 +13847,7 @@ function checkExistingDaemon() {
12037
13847
  return true;
12038
13848
  }
12039
13849
  try {
12040
- unlinkSync7(PID_PATH2);
13850
+ unlinkSync9(PID_PATH2);
12041
13851
  } catch {
12042
13852
  }
12043
13853
  return false;
@@ -12103,6 +13913,7 @@ try {
12103
13913
  startIdleKill();
12104
13914
  startConsolidation();
12105
13915
  startSkillSweep();
13916
+ startCloudSyncTimer();
12106
13917
  startOrphanReaper();
12107
13918
  startAgentStats();
12108
13919
  startCapacityMonitoring();
@@ -12113,6 +13924,8 @@ try {
12113
13924
  startConfidenceDecay();
12114
13925
  startAutoUpdateCheck();
12115
13926
  startRssWatchdog();
13927
+ const { startProjectionWorker: startProjectionWorker2 } = await Promise.resolve().then(() => (init_projection_worker(), projection_worker_exports));
13928
+ startProjectionWorker2();
12116
13929
  try {
12117
13930
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
12118
13931
  const config = await loadConfig2();
@@ -12206,11 +14019,11 @@ try {
12206
14019
  process.stderr.write(`[exed] FATAL: ${err instanceof Error ? err.message : String(err)}
12207
14020
  `);
12208
14021
  try {
12209
- unlinkSync7(SOCKET_PATH2);
14022
+ unlinkSync9(SOCKET_PATH2);
12210
14023
  } catch {
12211
14024
  }
12212
14025
  try {
12213
- unlinkSync7(PID_PATH2);
14026
+ unlinkSync9(PID_PATH2);
12214
14027
  } catch {
12215
14028
  }
12216
14029
  process.exit(1);