@circuitwall/jarela 1.2.0 → 1.4.0

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 (96) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +2 -2
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  5. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  6. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  15. package/.next/standalone/.next/server/app/_not-found.rsc +1 -1
  16. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  17. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  19. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  20. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  21. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  22. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/chats/route.js +3 -3
  23. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/chats/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/lookup/route.js +3 -3
  25. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/lookup/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/pair/route.js +3 -3
  27. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/pair/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/route.js +3 -3
  29. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/status/route.js +3 -3
  31. package/.next/standalone/.next/server/app/api/v1/bridges/[id]/status/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/v1/builtin-tools/route.js +218 -7
  33. package/.next/standalone/.next/server/app/api/v1/builtin-tools/route.js.map +1 -1
  34. package/.next/standalone/.next/server/app/api/v1/events/route.js +3 -3
  35. package/.next/standalone/.next/server/app/api/v1/events/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/v1/extension/agents/route.js +8 -1
  37. package/.next/standalone/.next/server/app/api/v1/extension/agents/route.js.map +1 -1
  38. package/.next/standalone/.next/server/app/api/v1/extension/fill/route.js +8 -1
  39. package/.next/standalone/.next/server/app/api/v1/extension/fill/route.js.map +1 -1
  40. package/.next/standalone/.next/server/app/api/v1/extension/refine/route.js +8 -1
  41. package/.next/standalone/.next/server/app/api/v1/extension/refine/route.js.map +1 -1
  42. package/.next/standalone/.next/server/app/api/v1/extension/turn/route.js +8 -1
  43. package/.next/standalone/.next/server/app/api/v1/extension/turn/route.js.map +1 -1
  44. package/.next/standalone/.next/server/app/api/v1/extensions/route.js +2 -2
  45. package/.next/standalone/.next/server/app/api/v1/extensions/tools/[name]/secrets/route.js +2 -2
  46. package/.next/standalone/.next/server/app/api/v1/page-capture/route.js +37 -3
  47. package/.next/standalone/.next/server/app/api/v1/page-capture/route.js.map +1 -1
  48. package/.next/standalone/.next/server/app/api/v1/tools/route.js +2 -2
  49. package/.next/standalone/.next/server/app/page.js +10 -18
  50. package/.next/standalone/.next/server/app/page.js.map +1 -1
  51. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  52. package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  53. package/.next/standalone/.next/server/chunks/210.js +1 -1
  54. package/.next/standalone/.next/server/chunks/239.js +5335 -5230
  55. package/.next/standalone/.next/server/chunks/239.js.map +1 -1
  56. package/.next/standalone/.next/server/chunks/{1683.js → 241.js} +210 -36
  57. package/.next/standalone/.next/server/chunks/241.js.map +1 -0
  58. package/.next/standalone/.next/server/chunks/{8135.js → 2539.js} +218 -36
  59. package/.next/standalone/.next/server/chunks/2539.js.map +1 -0
  60. package/.next/standalone/.next/server/chunks/4631.js +218 -7
  61. package/.next/standalone/.next/server/chunks/4631.js.map +1 -1
  62. package/.next/standalone/.next/server/chunks/8866.js +13389 -13073
  63. package/.next/standalone/.next/server/chunks/8866.js.map +1 -1
  64. package/.next/standalone/.next/server/chunks/9032.js +1 -1
  65. package/.next/standalone/.next/server/chunks/9032.js.map +1 -1
  66. package/.next/standalone/.next/server/middleware-build-manifest.js +2 -2
  67. package/.next/standalone/.next/server/pages/404.html +1 -1
  68. package/.next/standalone/.next/server/pages/500.html +1 -1
  69. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  70. package/.next/standalone/.next/static/chunks/app/{page-62e0d5f2404b403b.js → page-74846c864241b96d.js} +11 -19
  71. package/.next/standalone/.next/static/chunks/app/page-74846c864241b96d.js.map +1 -0
  72. package/.next/standalone/package.json +2 -1
  73. package/CHANGELOG.md +98 -0
  74. package/README.md +51 -26
  75. package/components/chat/InputBar.tsx +10 -1
  76. package/components/ui/BootScreen.tsx +0 -10
  77. package/lib/agents/agent-turn.ts +9 -0
  78. package/lib/agents/prepare/request.ts +9 -0
  79. package/lib/agents/run-thread.ts +9 -1
  80. package/lib/api/extension-turn.ts +7 -0
  81. package/lib/api/page-capture.test.ts +58 -0
  82. package/lib/api/page-capture.ts +31 -1
  83. package/lib/bridges/attachment-store.test.ts +440 -0
  84. package/lib/bridges/attachment-store.ts +184 -0
  85. package/lib/bridges/whatsapp.ts +50 -32
  86. package/lib/tools/async-results-tool.ts +114 -0
  87. package/lib/tools/async-results.test.ts +481 -0
  88. package/lib/tools/async-results.ts +165 -0
  89. package/lib/tools/builtins.ts +1 -0
  90. package/lib/tools/wallclock.ts +114 -8
  91. package/package.json +2 -1
  92. package/.next/standalone/.next/server/chunks/1683.js.map +0 -1
  93. package/.next/standalone/.next/server/chunks/8135.js.map +0 -1
  94. package/.next/standalone/.next/static/chunks/app/page-62e0d5f2404b403b.js.map +0 -1
  95. /package/.next/standalone/.next/static/{2xWP8843jbntFGKLnHK6R → AV5AO0yTRABo-NgwxhDe7}/_buildManifest.js +0 -0
  96. /package/.next/standalone/.next/static/{2xWP8843jbntFGKLnHK6R → AV5AO0yTRABo-NgwxhDe7}/_ssgManifest.js +0 -0
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
- exports.id = 1683;
3
- exports.ids = [1683];
2
+ exports.id = 241;
3
+ exports.ids = [241];
4
4
  exports.modules = {
5
5
 
6
- /***/ 71683:
6
+ /***/ 10241:
7
7
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
8
8
 
9
9
 
@@ -16,8 +16,10 @@ __webpack_require__.d(__webpack_exports__, {
16
16
 
17
17
  // EXTERNAL MODULE: external "node:crypto"
18
18
  var external_node_crypto_ = __webpack_require__(77598);
19
+ var external_node_crypto_default = /*#__PURE__*/__webpack_require__.n(external_node_crypto_);
19
20
  // EXTERNAL MODULE: external "node:path"
20
21
  var external_node_path_ = __webpack_require__(76760);
22
+ var external_node_path_default = /*#__PURE__*/__webpack_require__.n(external_node_path_);
21
23
  // EXTERNAL MODULE: external "node:fs"
22
24
  var external_node_fs_ = __webpack_require__(73024);
23
25
  // EXTERNAL MODULE: ./lib/db/index.ts + 7 modules
@@ -310,6 +312,160 @@ var agent_turn = __webpack_require__(88179);
310
312
  }
311
313
  }
312
314
 
315
+ ;// ./lib/bridges/attachment-store.ts
316
+ // Bridge attachment spill store.
317
+ //
318
+ // Inbound bridge messages (WhatsApp, future Telegram/Slack, etc.) can
319
+ // carry files that are too large or too opaque to inline straight into
320
+ // the LLM context: PDFs, spreadsheets, archives, multi-minute audio,
321
+ // short videos. We persist those bytes under the user's Jarela data
322
+ // dir and hand the agent a text pointer (`saved locally at <abs>`) so
323
+ // it can decide what to do — typically calling `file_read` on the path.
324
+ //
325
+ // Small media (e.g. ≤ 1 MB images) keep going inline so vision-capable
326
+ // models can still describe them in one round-trip without bouncing
327
+ // through disk.
328
+ //
329
+ // Layout: <dataDir>/bridge-attachments/<bridge_id>/<YYYY-MM-DD>/<id>-<safe-name>
330
+
331
+
332
+
333
+
334
+ const BRIDGE_ATTACHMENTS_DIRNAME = "bridge-attachments";
335
+ /** Default inline cap for media we still want the LLM to see directly. */ const DEFAULT_INLINE_LIMIT_BYTES = 1 * 1024 * 1024;
336
+ /** Media types kept inline when small. Everything else is always spilled. */ const INLINE_MIME_PREFIXES = [
337
+ "image/"
338
+ ];
339
+ /**
340
+ * Decide whether a buffer should be inlined as a `ContentPart` or
341
+ * spilled to disk. Keeps small images inline so vision models still
342
+ * work out-of-the-box; spills everything else.
343
+ */ function shouldInline(media_type, size, limit = DEFAULT_INLINE_LIMIT_BYTES) {
344
+ if (size > limit) return false;
345
+ return INLINE_MIME_PREFIXES.some((p)=>media_type.startsWith(p));
346
+ }
347
+ function baseDir() {
348
+ return external_node_path_default().join((0,data_dir/* getDataDir */.o)(), BRIDGE_ATTACHMENTS_DIRNAME);
349
+ }
350
+ function todayDir() {
351
+ // Local-time YYYY-MM-DD keeps directories human-scannable in the
352
+ // user's timezone. Cross-day boundary noise isn't worth UTC.
353
+ const d = new Date();
354
+ const yyyy = d.getFullYear();
355
+ const mm = String(d.getMonth() + 1).padStart(2, "0");
356
+ const dd = String(d.getDate()).padStart(2, "0");
357
+ return `${yyyy}-${mm}-${dd}`;
358
+ }
359
+ // Strip path separators, control chars, and anything that would
360
+ // surprise a Windows shell. Truncate so long captions can't blow
361
+ // MAX_PATH (260) on Win32.
362
+ function safeFilename(name, fallback) {
363
+ const raw = (name ?? "").trim() || fallback;
364
+ let s = raw.replace(/[\\/:*?"<>|\u0000-\u001f]/g, "_");
365
+ s = s.replace(/\s+/g, " ").trim();
366
+ if (s.length > 80) {
367
+ const ext = external_node_path_default().extname(s).slice(0, 12);
368
+ s = s.slice(0, 80 - ext.length) + ext;
369
+ }
370
+ return s || fallback;
371
+ }
372
+ function safeBridgeId(id) {
373
+ // bridge_id is internally generated but be defensive — refuse anything
374
+ // that could escape the attachments dir.
375
+ return id.replace(/[^A-Za-z0-9_-]/g, "_");
376
+ }
377
+ /**
378
+ * Persist a bridge attachment to disk and return its absolute path.
379
+ *
380
+ * Idempotent on (bridge_id, message_id, filename): re-saving the same
381
+ * inbound message overwrites the same file rather than fanning out
382
+ * duplicates on adapter restart.
383
+ */ async function saveBridgeAttachment(input) {
384
+ const dir = external_node_path_default().join(baseDir(), safeBridgeId(input.bridge_id), todayDir());
385
+ await external_node_fs_.promises.mkdir(dir, {
386
+ recursive: true
387
+ });
388
+ // Deterministic id when the adapter gave us a message id; fall back
389
+ // to a short random hex so concurrent unrelated messages can't collide.
390
+ const idPart = (input.message_id ?? "").replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 32) || external_node_crypto_default().randomBytes(6).toString("hex");
391
+ const fname = `${idPart}-${safeFilename(input.filename, "attachment")}`;
392
+ const abs = external_node_path_default().join(dir, fname);
393
+ await external_node_fs_.promises.writeFile(abs, input.buffer);
394
+ const sha256 = external_node_crypto_default().createHash("sha256").update(input.buffer).digest("hex");
395
+ return {
396
+ abs_path: abs,
397
+ size: input.buffer.length,
398
+ sha256
399
+ };
400
+ }
401
+ /**
402
+ * Delete bridge attachments older than `maxAgeMs`. Best-effort: a
403
+ * locked or vanished file is skipped silently. Empty per-day and
404
+ * per-bridge directories are pruned afterwards so the tree doesn't
405
+ * accumulate empty husks.
406
+ */ async function pruneBridgeAttachments(opts) {
407
+ const root = baseDir();
408
+ const cutoff = Date.now() - Math.max(0, opts.maxAgeMs);
409
+ const result = {
410
+ removed_files: 0,
411
+ removed_dirs: 0,
412
+ freed_bytes: 0
413
+ };
414
+ let bridges = [];
415
+ try {
416
+ bridges = await fs.readdir(root);
417
+ } catch {
418
+ return result;
419
+ }
420
+ for (const bridge of bridges){
421
+ const bridgeDir = path.join(root, bridge);
422
+ let days = [];
423
+ try {
424
+ days = await fs.readdir(bridgeDir);
425
+ } catch {
426
+ continue;
427
+ }
428
+ for (const day of days){
429
+ const dayDir = path.join(bridgeDir, day);
430
+ let files = [];
431
+ try {
432
+ files = await fs.readdir(dayDir);
433
+ } catch {
434
+ continue;
435
+ }
436
+ for (const f of files){
437
+ const fp = path.join(dayDir, f);
438
+ try {
439
+ const st = await fs.stat(fp);
440
+ if (!st.isFile()) continue;
441
+ if (st.mtimeMs >= cutoff) continue;
442
+ await fs.unlink(fp);
443
+ result.removed_files++;
444
+ result.freed_bytes += st.size;
445
+ } catch {}
446
+ }
447
+ try {
448
+ const remaining = await fs.readdir(dayDir);
449
+ if (remaining.length === 0) {
450
+ await fs.rmdir(dayDir);
451
+ result.removed_dirs++;
452
+ }
453
+ } catch {}
454
+ }
455
+ try {
456
+ const remaining = await fs.readdir(bridgeDir);
457
+ if (remaining.length === 0) {
458
+ await fs.rmdir(bridgeDir);
459
+ result.removed_dirs++;
460
+ }
461
+ } catch {}
462
+ }
463
+ return result;
464
+ }
465
+ /** Test helper: absolute path to the root attachments dir. */ function bridgeAttachmentsRoot() {
466
+ return baseDir();
467
+ }
468
+
313
469
  ;// ./lib/bridges/whatsapp.ts
314
470
  /**
315
471
  * Baileys WhatsApp adapter.
@@ -336,6 +492,7 @@ var agent_turn = __webpack_require__(88179);
336
492
  * fall into pairing mode again.
337
493
  */
338
494
 
495
+
339
496
  class whatsapp_WhatsAppBridgeAdapter {
340
497
  static{
341
498
  this.SENT_IDS_MAX = 500;
@@ -822,14 +979,40 @@ class whatsapp_WhatsAppBridgeAdapter {
822
979
  return null;
823
980
  }
824
981
  };
982
+ // Spill helper: persist any buffer we can't (or shouldn't) inline,
983
+ // and append a text pointer the agent can act on with file_read.
984
+ // Returns true if the buffer was spilled so callers can decide
985
+ // whether to also push an inline ContentPart.
986
+ const messageId = rawMessage?.key?.id ?? null;
987
+ const spill = async (buf, filename, mime, label)=>{
988
+ try {
989
+ const saved = await saveBridgeAttachment({
990
+ bridge_id: this.bridge_id,
991
+ filename,
992
+ media_type: mime,
993
+ message_id: messageId,
994
+ buffer: buf
995
+ });
996
+ const sizeKb = Math.max(1, Math.round(saved.size / 1024));
997
+ text = (text ? text + "\n" : "") + `[Attached ${label}: ${filename} (${mime}, ${sizeKb} KB) saved locally at ${saved.abs_path}. ` + `Use file_read on that path to inspect the contents.]`;
998
+ } catch (err) {
999
+ const m = err instanceof Error ? err.message : String(err);
1000
+ console.warn(`[bridge ${this.bridge_id}] failed to spill ${label} from ${remote_jid}: ${m}`);
1001
+ }
1002
+ };
825
1003
  if (inner.imageMessage) {
826
1004
  const buf = await download("image");
827
1005
  if (buf) {
828
- attachments.push({
829
- type: "image",
830
- media_type: sanitizeMediaType(inner.imageMessage.mimetype, "image", "image/jpeg"),
831
- data: buf.toString("base64")
832
- });
1006
+ const mime = sanitizeMediaType(inner.imageMessage.mimetype, "image", "image/jpeg");
1007
+ if (shouldInline(mime, buf.length)) {
1008
+ attachments.push({
1009
+ type: "image",
1010
+ media_type: mime,
1011
+ data: buf.toString("base64")
1012
+ });
1013
+ } else {
1014
+ await spill(buf, `image-${messageId ?? Date.now()}.${mime.split("/")[1] ?? "bin"}`, mime, "image");
1015
+ }
833
1016
  }
834
1017
  }
835
1018
  if (inner.stickerMessage) {
@@ -839,50 +1022,41 @@ class whatsapp_WhatsAppBridgeAdapter {
839
1022
  // models actually describe them. Animated stickers go through as
840
1023
  // their raw webp; providers that can't decode animated webp will
841
1024
  // typically render the first frame.
842
- attachments.push({
843
- type: "image",
844
- media_type: sanitizeMediaType(inner.stickerMessage.mimetype, "image", "image/webp"),
845
- data: buf.toString("base64")
846
- });
1025
+ const mime = sanitizeMediaType(inner.stickerMessage.mimetype, "image", "image/webp");
1026
+ if (shouldInline(mime, buf.length)) {
1027
+ attachments.push({
1028
+ type: "image",
1029
+ media_type: mime,
1030
+ data: buf.toString("base64")
1031
+ });
1032
+ } else {
1033
+ await spill(buf, `sticker-${messageId ?? Date.now()}.webp`, mime, "sticker");
1034
+ }
847
1035
  }
848
1036
  }
849
1037
  if (inner.audioMessage) {
850
1038
  const isVoice = !!inner.audioMessage.ptt;
851
1039
  const buf = await download(isVoice ? "voice" : "audio");
852
1040
  if (buf) {
853
- attachments.push({
854
- type: "file",
855
- name: isVoice ? "voice-note" : "audio",
856
- media_type: sanitizeMediaType(inner.audioMessage.mimetype, "audio", "audio/ogg"),
857
- data: buf.toString("base64")
858
- });
1041
+ const mime = sanitizeMediaType(inner.audioMessage.mimetype, "audio", "audio/ogg");
1042
+ const ext = mime.split("/")[1]?.replace(/^x-/, "") ?? "ogg";
1043
+ await spill(buf, `${isVoice ? "voice-note" : "audio"}-${messageId ?? Date.now()}.${ext}`, mime, isVoice ? "voice note" : "audio");
859
1044
  }
860
1045
  }
861
1046
  if (inner.videoMessage) {
862
1047
  const buf = await download("video");
863
1048
  if (buf) {
864
- attachments.push({
865
- type: "file",
866
- name: "video",
867
- media_type: sanitizeMediaType(inner.videoMessage.mimetype, "video", "video/mp4"),
868
- data: buf.toString("base64")
869
- });
1049
+ const mime = sanitizeMediaType(inner.videoMessage.mimetype, "video", "video/mp4");
1050
+ const ext = mime.split("/")[1] ?? "mp4";
1051
+ await spill(buf, `video-${messageId ?? Date.now()}.${ext}`, mime, "video");
870
1052
  }
871
1053
  }
872
1054
  if (inner.documentMessage) {
873
1055
  const buf = await download("document");
874
1056
  if (buf) {
875
1057
  const mime = sanitizeMediaType(inner.documentMessage.mimetype, "document", "application/octet-stream");
876
- // The LLM layer (lib/agents/llm.ts) inlines text/* and
877
- // application/json file parts directly as their `data` string. For
878
- // those we must store the decoded UTF-8 contents, not base64.
879
- const inlineAsText = mime.startsWith("text/") || mime === "application/json";
880
- attachments.push({
881
- type: "file",
882
- name: inner.documentMessage.fileName || inner.documentMessage.title || "document",
883
- media_type: mime,
884
- data: inlineAsText ? buf.toString("utf8") : buf.toString("base64")
885
- });
1058
+ const filename = inner.documentMessage.fileName || inner.documentMessage.title || `document-${messageId ?? Date.now()}`;
1059
+ await spill(buf, filename, mime, "document");
886
1060
  }
887
1061
  }
888
1062
  if (inner.locationMessage) {
@@ -1244,4 +1418,4 @@ async function stopAllBridges() {
1244
1418
 
1245
1419
  };
1246
1420
  ;
1247
- //# sourceMappingURL=1683.js.map
1421
+ //# sourceMappingURL=241.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"241.js","mappings":"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","sources":["webpack://@circuitwall/jarela/./lib/stores/bridges.ts","webpack://@circuitwall/jarela/./lib/bridges/router.ts","webpack://@circuitwall/jarela/./lib/bridges/dispatcher.ts","webpack://@circuitwall/jarela/./lib/bridges/attachment-store.ts","webpack://@circuitwall/jarela/./lib/bridges/whatsapp.ts","webpack://@circuitwall/jarela/./lib/bridges/runtime.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { join } from \"node:path\";\nimport { mkdirSync, rmSync } from \"node:fs\";\nimport { getDb } from \"@/lib/db\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type BridgeKind = \"whatsapp\";\nexport type BridgeStatus = \"disconnected\" | \"pairing\" | \"connected\" | \"error\";\n\nexport interface BridgeRow {\n id: string;\n kind: BridgeKind;\n name: string;\n status: BridgeStatus;\n qr: string | null;\n last_error: string | null;\n paired_id: string | null;\n enabled: number; // 0 | 1\n created_at: string;\n updated_at: string;\n}\n\nexport interface BridgeRouteRow {\n id: string;\n bridge_id: string;\n remote_jid: string; // Specific JID or '*' catch-all for unmatched chats on this bridge\n agent_id: string;\n label: string | null;\n // 1 = run the agent on inbound messages but suppress the outbound reply.\n // Per-route so the same agent can auto-reply in one chat and stay silent\n // in another (e.g. observer in a busy group, replier in a DM). Overrides\n // `respond_to` — when set, nothing goes out regardless of role match.\n silent_mode: number;\n // Which inbound sender role triggers an outbound reply on this route.\n // - 'counterpart': agent answers the user's chat partner / group members\n // (the typical \"auto-responder on my behalf\" case; default).\n // - 'user': agent only reacts to the user's own messages (expand /\n // translate-my-draft assistants).\n // The agent always RUNS on every inbound message (so it sees the full\n // conversation context) — this only gates whether the reply is sent.\n respond_to: \"user\" | \"counterpart\";\n created_at: string;\n updated_at: string;\n}\n\nconst now = () => new Date().toISOString();\n\n// ---------------------------------------------------------------------------\n// Auth dir on disk (Baileys multi-file auth state)\n// ---------------------------------------------------------------------------\n\n// Mirror lib/db/index.ts so we don't depend on importing it for a directory\n// path — keeps the store free of side-effects on import.\nimport { getDataDir } from \"@/lib/db/data-dir\";\n\nexport function bridgeAuthDir(bridgeId: string): string {\n return join(getDataDir(), \"baileys\", bridgeId);\n}\n\nexport function ensureBridgeAuthDir(bridgeId: string): string {\n const dir = bridgeAuthDir(bridgeId);\n mkdirSync(dir, { recursive: true });\n return dir;\n}\n\nexport function removeBridgeAuthDir(bridgeId: string): void {\n try { rmSync(bridgeAuthDir(bridgeId), { recursive: true, force: true }); }\n catch { /* dir didn't exist, fine */ }\n}\n\n// ---------------------------------------------------------------------------\n// Bridge CRUD\n// ---------------------------------------------------------------------------\n\nexport function listBridges(): BridgeRow[] {\n return getDb()\n .prepare(\"SELECT * FROM bridges ORDER BY created_at ASC\")\n .all() as unknown as BridgeRow[];\n}\n\nexport function getBridge(id: string): BridgeRow | null {\n return (getDb().prepare(\"SELECT * FROM bridges WHERE id=?\").get(id) as BridgeRow | undefined) ?? null;\n}\n\nexport function createBridge(input: { kind: BridgeKind; name: string }): BridgeRow {\n const id = randomUUID();\n const t = now();\n getDb()\n .prepare(\n `INSERT INTO bridges (id, kind, name, status, qr, last_error, paired_id, enabled, created_at, updated_at)\n VALUES (?, ?, ?, 'disconnected', NULL, NULL, NULL, 0, ?, ?)`,\n )\n .run(id, input.kind, input.name, t, t);\n return {\n id,\n kind: input.kind,\n name: input.name,\n status: \"disconnected\",\n qr: null,\n last_error: null,\n paired_id: null,\n enabled: 0,\n created_at: t,\n updated_at: t,\n };\n}\n\nexport function updateBridge(\n id: string,\n patch: Partial<Pick<BridgeRow, \"name\" | \"enabled\" | \"status\" | \"qr\" | \"last_error\" | \"paired_id\">>,\n): BridgeRow | null {\n const existing = getBridge(id);\n if (!existing) return null;\n const merged: BridgeRow = {\n ...existing,\n name: patch.name ?? existing.name,\n enabled: patch.enabled ?? existing.enabled,\n status: patch.status ?? existing.status,\n qr: patch.qr !== undefined ? patch.qr : existing.qr,\n last_error: patch.last_error !== undefined ? patch.last_error : existing.last_error,\n paired_id: patch.paired_id !== undefined ? patch.paired_id : existing.paired_id,\n updated_at: now(),\n };\n getDb()\n .prepare(\n `UPDATE bridges\n SET name=?, enabled=?, status=?, qr=?, last_error=?, paired_id=?, updated_at=?\n WHERE id=?`,\n )\n .run(merged.name, merged.enabled, merged.status, merged.qr, merged.last_error, merged.paired_id, merged.updated_at, id);\n return merged;\n}\n\nexport function deleteBridge(id: string): boolean {\n const db = getDb();\n db.prepare(\"DELETE FROM bridge_routes WHERE bridge_id=?\").run(id);\n const r = db.prepare(\"DELETE FROM bridges WHERE id=?\").run(id);\n return r.changes > 0;\n}\n\n// ---------------------------------------------------------------------------\n// Route CRUD\n// ---------------------------------------------------------------------------\n\nexport function listRoutes(bridgeId: string): BridgeRouteRow[] {\n return getDb()\n .prepare(\"SELECT * FROM bridge_routes WHERE bridge_id=? ORDER BY created_at ASC\")\n .all(bridgeId) as unknown as BridgeRouteRow[];\n}\n\nexport function listAllRoutes(): BridgeRouteRow[] {\n return getDb()\n .prepare(\"SELECT * FROM bridge_routes ORDER BY created_at ASC\")\n .all() as unknown as BridgeRouteRow[];\n}\n\nexport function getRoute(id: string): BridgeRouteRow | null {\n return (getDb().prepare(\"SELECT * FROM bridge_routes WHERE id=?\").get(id) as BridgeRouteRow | undefined) ?? null;\n}\n\nexport function findRoute(bridgeId: string, remoteJid: string): BridgeRouteRow | null {\n return (getDb()\n .prepare(\"SELECT * FROM bridge_routes WHERE bridge_id=? AND remote_jid=?\")\n .get(bridgeId, remoteJid) as BridgeRouteRow | undefined) ?? null;\n}\n\nexport function createRoute(input: {\n bridge_id: string;\n remote_jid: string; // Specific JID or '*' catch-all\n agent_id: string;\n label?: string | null;\n silent_mode?: boolean;\n respond_to?: \"user\" | \"counterpart\";\n}): BridgeRouteRow {\n const id = randomUUID();\n const t = now();\n const silent = input.silent_mode ? 1 : 0;\n const respondTo = input.respond_to ?? \"counterpart\";\n getDb()\n .prepare(\n `INSERT INTO bridge_routes (id, bridge_id, remote_jid, agent_id, label, silent_mode, respond_to, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n )\n .run(id, input.bridge_id, input.remote_jid, input.agent_id, input.label ?? null, silent, respondTo, t, t);\n return {\n id,\n bridge_id: input.bridge_id,\n remote_jid: input.remote_jid,\n agent_id: input.agent_id,\n label: input.label ?? null,\n silent_mode: silent,\n respond_to: respondTo,\n created_at: t,\n updated_at: t,\n };\n}\n\nexport function updateRoute(\n id: string,\n patch: {\n remote_jid?: string;\n agent_id?: string;\n label?: string | null;\n silent_mode?: boolean;\n respond_to?: \"user\" | \"counterpart\";\n },\n): BridgeRouteRow | null {\n const existing = getRoute(id);\n if (!existing) return null;\n const merged: BridgeRouteRow = {\n ...existing,\n remote_jid: patch.remote_jid ?? existing.remote_jid,\n agent_id: patch.agent_id ?? existing.agent_id,\n label: patch.label !== undefined ? patch.label : existing.label,\n silent_mode: patch.silent_mode !== undefined ? (patch.silent_mode ? 1 : 0) : existing.silent_mode,\n respond_to: patch.respond_to ?? existing.respond_to,\n updated_at: now(),\n };\n getDb()\n .prepare(\"UPDATE bridge_routes SET remote_jid=?, agent_id=?, label=?, silent_mode=?, respond_to=?, updated_at=? WHERE id=?\")\n .run(merged.remote_jid, merged.agent_id, merged.label, merged.silent_mode, merged.respond_to, merged.updated_at, id);\n return merged;\n}\n\nexport function deleteRoute(id: string): boolean {\n const r = getDb().prepare(\"DELETE FROM bridge_routes WHERE id=?\").run(id);\n return r.changes > 0;\n}\n","import { findRoute, type BridgeRouteRow } from \"@/lib/stores/bridges\";\n\n/**\n * Resolve which agent should handle an inbound message on (bridge, remote_jid).\n * 1) Exact route match on (bridge_id, remote_jid)\n * 2) Bridge-level catch-all route (`remote_jid='*'`) when present\n * 3) null (dispatcher drops the message silently)\n *\n * Catch-all lets one agent handle all otherwise-unrouted chats on a bridge.\n * This is especially useful for \"triage\" or \"observer\" agents.\n */\nexport function resolveRoute(bridge_id: string, remote_jid: string): BridgeRouteRow | null {\n return findRoute(bridge_id, remote_jid) ?? findRoute(bridge_id, \"*\");\n}\n\n/** Back-compat shim — prefer `resolveRoute` so callers can read silent_mode. */\nexport function resolveAgent(bridge_id: string, remote_jid: string): string | null {\n return resolveRoute(bridge_id, remote_jid)?.agent_id ?? null;\n}\n","import { getOrCreateAgentThread } from \"@/lib/stores/threads\";\nimport { getAgentConfig } from \"@/lib/stores/agent-configs\";\nimport { runAgentTurn } from \"@/lib/agents/agent-turn\";\nimport { publish as publishNotification } from \"@/lib/notifications/bus\";\nimport { resolveRoute } from \"./router\";\nimport { formatBridgePrompt } from \"./message-role\";\nimport type { BridgeAdapter, InboundMessage } from \"./types\";\n\n/**\n * Handle one inbound message from a bridge adapter:\n * 1. Resolve the chat → agent route. Unrouted → publish an advisory\n * event and drop. No thread created, no reply sent.\n * 2. Use `getOrCreateAgentThread(agent_id)` (one-thread-per-agent\n * invariant — `UNIQUE(agent_id)` on bridge_routes ensures no chat\n * interleaving).\n * 3. Drive the agent via the canonical `runAgentTurn` helper used by\n * every external context turn.\n * 4. Persist the assistant message and send the reply back through the\n * adapter on the originating channel.\n *\n * Top-level try/catch — adapter callbacks must never throw into the Baileys\n * socket event handler (would tear down the WS).\n */\nexport async function handleInboundMessage(\n adapter: BridgeAdapter,\n msg: InboundMessage,\n): Promise<void> {\n try {\n const route = resolveRoute(adapter.bridge_id, msg.remote_jid);\n if (!route) {\n // Unrouted chats are silently dropped. We intentionally do NOT publish\n // a notification here — the user already declared \"this chat isn't\n // monitored\" by not configuring a route, so popping a toast for every\n // inbound message in an active group would be noise. The console log\n // remains for debugging; the chat picker in BridgeEditor still shows\n // observed chats so a route can be added on demand.\n console.log(`[bridge ${adapter.bridge_id}] dropped: no route for ${msg.remote_jid} (${msg.push_name ?? \"?\"})`);\n return;\n }\n const agentId = route.agent_id;\n\n const agent = getAgentConfig(agentId);\n if (!agent) {\n console.warn(`[bridge ${adapter.bridge_id}] route points at missing agent ${agentId}, dropping`);\n return;\n }\n\n const thread = getOrCreateAgentThread(agentId);\n // Stamp bridge/chat provenance + sender role onto every inbound prompt.\n // Role framing (user / counterpart / agent) is shared across every\n // bridge adapter via `formatBridgePrompt` — see lib/bridges/message-role.ts.\n const chatName = msg.chat_name ?? msg.push_name ?? \"unknown\";\n const senderJid = msg.participant_jid ?? msg.remote_jid;\n const senderName = msg.sender_name ?? msg.push_name ?? senderJid;\n const silent = route.silent_mode === 1;\n const promptText = formatBridgePrompt({\n bridge_id: adapter.bridge_id,\n chat_id: msg.remote_jid,\n chat_name: chatName,\n is_group: msg.is_group,\n role: msg.role,\n sender_id: senderJid,\n sender_name: senderName,\n text: msg.text,\n silent,\n });\n // Keep typing outside the queue so users still see \"thinking\" while\n // waiting behind other queued turns for the same thread.\n const willReply = !silent && msg.role === route.respond_to;\n let typingActive = willReply;\n if (willReply) {\n void adapter.sendTyping(msg.remote_jid, true).catch(() => { /* best-effort */ });\n }\n const typingTimer = setInterval(() => {\n if (!typingActive) return;\n void adapter.sendTyping(msg.remote_jid, true).catch(() => { /* best-effort */ });\n }, 8_000);\n (typingTimer as unknown as { unref?: () => void }).unref?.();\n\n let reply = \"\";\n let suppressAssistant = false;\n try {\n const result = await runAgentTurn({\n thread_id: thread.thread_id,\n queue_source: \"bridge\",\n message: promptText,\n attachments: msg.attachments,\n user_category: \"bridge\",\n assistant_category: \"bridge\",\n silent,\n });\n reply = result.assistantContent.trim();\n suppressAssistant = result.skippedAssistant;\n } finally {\n typingActive = false;\n clearInterval(typingTimer);\n if (willReply) {\n void adapter.sendTyping(msg.remote_jid, false).catch(() => { /* best-effort */ });\n }\n }\n\n // Outbound reply gate: silent_mode (master switch) AND respond_to\n // (per-role trigger). Both must clear for a message to leave the\n // dispatcher. The WhatsApp adapter also re-checks `route.silent_mode`\n // inside its own sendText as a belt-and-suspenders guard, so even a\n // tool that called adapter.sendText directly would be dropped.\n if (reply.length > 0 && willReply) {\n try {\n await adapter.sendText(msg.remote_jid, reply);\n } catch (sendErr) {\n const m = sendErr instanceof Error ? sendErr.message : String(sendErr);\n console.error(`[bridge ${adapter.bridge_id}] sendText failed:`, m);\n }\n }\n\n if (!silent || !suppressAssistant) {\n publishNotification({\n type: \"bridge_message_received\",\n bridge_id: adapter.bridge_id,\n remote_jid: msg.remote_jid,\n push_name: msg.push_name,\n is_group: msg.is_group,\n thread_id: thread.thread_id,\n agent_id: agentId,\n preview: suppressAssistant ? \"\" : reply.replace(/\\s+/g, \" \").slice(0, 120),\n ts: Date.now(),\n });\n }\n } catch (err) {\n const m = err instanceof Error ? err.message : String(err);\n console.error(`[bridge ${adapter.bridge_id}] dispatcher error on ${msg.remote_jid}:`, m);\n }\n}\n","// Bridge attachment spill store.\n//\n// Inbound bridge messages (WhatsApp, future Telegram/Slack, etc.) can\n// carry files that are too large or too opaque to inline straight into\n// the LLM context: PDFs, spreadsheets, archives, multi-minute audio,\n// short videos. We persist those bytes under the user's Jarela data\n// dir and hand the agent a text pointer (`saved locally at <abs>`) so\n// it can decide what to do — typically calling `file_read` on the path.\n//\n// Small media (e.g. ≤ 1 MB images) keep going inline so vision-capable\n// models can still describe them in one round-trip without bouncing\n// through disk.\n//\n// Layout: <dataDir>/bridge-attachments/<bridge_id>/<YYYY-MM-DD>/<id>-<safe-name>\n\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\nimport { getDataDir } from \"@/lib/db/data-dir\";\n\nexport const BRIDGE_ATTACHMENTS_DIRNAME = \"bridge-attachments\";\n\n/** Default inline cap for media we still want the LLM to see directly. */\nexport const DEFAULT_INLINE_LIMIT_BYTES = 1 * 1024 * 1024;\n\n/** Media types kept inline when small. Everything else is always spilled. */\nexport const INLINE_MIME_PREFIXES: readonly string[] = [\"image/\"];\n\nexport interface SaveAttachmentInput {\n bridge_id: string;\n /** Best-effort source filename (may be missing — we'll synthesize one). */\n filename: string | null;\n media_type: string;\n /** Optional adapter-side message id used to make filenames deterministic. */\n message_id?: string | null;\n buffer: Buffer;\n}\n\nexport interface SavedAttachment {\n abs_path: string;\n size: number;\n sha256: string;\n}\n\n/**\n * Decide whether a buffer should be inlined as a `ContentPart` or\n * spilled to disk. Keeps small images inline so vision models still\n * work out-of-the-box; spills everything else.\n */\nexport function shouldInline(media_type: string, size: number, limit = DEFAULT_INLINE_LIMIT_BYTES): boolean {\n if (size > limit) return false;\n return INLINE_MIME_PREFIXES.some((p) => media_type.startsWith(p));\n}\n\nfunction baseDir(): string {\n return path.join(getDataDir(), BRIDGE_ATTACHMENTS_DIRNAME);\n}\n\nfunction todayDir(): string {\n // Local-time YYYY-MM-DD keeps directories human-scannable in the\n // user's timezone. Cross-day boundary noise isn't worth UTC.\n const d = new Date();\n const yyyy = d.getFullYear();\n const mm = String(d.getMonth() + 1).padStart(2, \"0\");\n const dd = String(d.getDate()).padStart(2, \"0\");\n return `${yyyy}-${mm}-${dd}`;\n}\n\n// Strip path separators, control chars, and anything that would\n// surprise a Windows shell. Truncate so long captions can't blow\n// MAX_PATH (260) on Win32.\nfunction safeFilename(name: string | null, fallback: string): string {\n const raw = (name ?? \"\").trim() || fallback;\n let s = raw.replace(/[\\\\/:*?\"<>|\\u0000-\\u001f]/g, \"_\");\n s = s.replace(/\\s+/g, \" \").trim();\n if (s.length > 80) {\n const ext = path.extname(s).slice(0, 12);\n s = s.slice(0, 80 - ext.length) + ext;\n }\n return s || fallback;\n}\n\nfunction safeBridgeId(id: string): string {\n // bridge_id is internally generated but be defensive — refuse anything\n // that could escape the attachments dir.\n return id.replace(/[^A-Za-z0-9_-]/g, \"_\");\n}\n\n/**\n * Persist a bridge attachment to disk and return its absolute path.\n *\n * Idempotent on (bridge_id, message_id, filename): re-saving the same\n * inbound message overwrites the same file rather than fanning out\n * duplicates on adapter restart.\n */\nexport async function saveBridgeAttachment(input: SaveAttachmentInput): Promise<SavedAttachment> {\n const dir = path.join(baseDir(), safeBridgeId(input.bridge_id), todayDir());\n await fs.mkdir(dir, { recursive: true });\n\n // Deterministic id when the adapter gave us a message id; fall back\n // to a short random hex so concurrent unrelated messages can't collide.\n const idPart = (input.message_id ?? \"\").replace(/[^A-Za-z0-9_-]/g, \"_\").slice(0, 32)\n || crypto.randomBytes(6).toString(\"hex\");\n const fname = `${idPart}-${safeFilename(input.filename, \"attachment\")}`;\n const abs = path.join(dir, fname);\n\n await fs.writeFile(abs, input.buffer);\n\n const sha256 = crypto.createHash(\"sha256\").update(input.buffer).digest(\"hex\");\n return { abs_path: abs, size: input.buffer.length, sha256 };\n}\n\nexport interface PruneOptions {\n /** Files older than this many ms are deleted. */\n maxAgeMs: number;\n}\n\nexport interface PruneResult {\n removed_files: number;\n removed_dirs: number;\n freed_bytes: number;\n}\n\n/**\n * Delete bridge attachments older than `maxAgeMs`. Best-effort: a\n * locked or vanished file is skipped silently. Empty per-day and\n * per-bridge directories are pruned afterwards so the tree doesn't\n * accumulate empty husks.\n */\nexport async function pruneBridgeAttachments(opts: PruneOptions): Promise<PruneResult> {\n const root = baseDir();\n const cutoff = Date.now() - Math.max(0, opts.maxAgeMs);\n const result: PruneResult = { removed_files: 0, removed_dirs: 0, freed_bytes: 0 };\n\n let bridges: string[] = [];\n try { bridges = await fs.readdir(root); } catch { return result; }\n\n for (const bridge of bridges) {\n const bridgeDir = path.join(root, bridge);\n let days: string[] = [];\n try { days = await fs.readdir(bridgeDir); } catch { continue; }\n\n for (const day of days) {\n const dayDir = path.join(bridgeDir, day);\n let files: string[] = [];\n try { files = await fs.readdir(dayDir); } catch { continue; }\n\n for (const f of files) {\n const fp = path.join(dayDir, f);\n try {\n const st = await fs.stat(fp);\n if (!st.isFile()) continue;\n if (st.mtimeMs >= cutoff) continue;\n await fs.unlink(fp);\n result.removed_files++;\n result.freed_bytes += st.size;\n } catch { /* skip */ }\n }\n\n try {\n const remaining = await fs.readdir(dayDir);\n if (remaining.length === 0) {\n await fs.rmdir(dayDir);\n result.removed_dirs++;\n }\n } catch { /* skip */ }\n }\n\n try {\n const remaining = await fs.readdir(bridgeDir);\n if (remaining.length === 0) {\n await fs.rmdir(bridgeDir);\n result.removed_dirs++;\n }\n } catch { /* skip */ }\n }\n\n return result;\n}\n\n/** Test helper: absolute path to the root attachments dir. */\nexport function bridgeAttachmentsRoot(): string {\n return baseDir();\n}\n","/**\n * Baileys WhatsApp adapter.\n *\n * Pure-Node WebSocket client speaking WhatsApp's Multi-Device protocol. No\n * Chromium, no public URL needed. Pairs via QR code shown in the UI.\n *\n * Lifecycle:\n * - `start()` boots the socket. Baileys auto-resumes from auth state on\n * disk if present (no re-pair); otherwise it emits a QR string, which\n * we convert to a data URL and surface via `onStatusChange`.\n * - `connection.update` events drive our status reporter\n * (`disconnected | pairing | connected | error`).\n * - `messages.upsert` events with `type='notify'` (real-time inbound) are\n * forwarded to the inbound handler. User-authored `fromMe` messages are\n * included so the agent sees full conversational context; bot-authored\n * echoes are suppressed via sent-message ID tracking. Text, captions, images\n * (vision), stickers (as webp images), voice notes / audio, video, and\n * documents are all extracted via `extractContent`; location and contact\n * payloads are flattened into the text body. Reactions, polls and other\n * protocol-only messages are silently dropped.\n * - `stop()` closes the WS without wiping auth.\n * - `resetAuth()` removes the auth dir on disk; the next `start()` will\n * fall into pairing mode again.\n */\n\nimport { createRequire } from \"node:module\";\nimport { ensureBridgeAuthDir, findRoute, removeBridgeAuthDir } from \"@/lib/stores/bridges\";\nimport type { BridgeAdapter, ChatInfo, InboundHandler, StatusHandler, InboundMessage, StatusUpdate } from \"./types\";\nimport type { ContentPart } from \"@/lib/tools/types\";\nimport { saveBridgeAttachment, shouldInline } from \"./attachment-store\";\n\n// Baileys + qrcode are dev-time-installed peer libs. We never import their\n// types directly — both modules are loaded via dynamic `import()` inside\n// start() and treated as opaque values shaped by `UnsafeBaileys` below.\n// That keeps `next build` healthy even on a fresh clone where the packages\n// haven't been installed yet, and protects us against minor API drift in\n// future Baileys releases without dragging the whole file into ts errors.\n\ntype WASocket = {\n ev: { on: (event: string, handler: (...args: unknown[]) => void) => void };\n user?: { id?: string };\n sendMessage: (\n jid: string,\n content: { text: string },\n options?: { getUrlInfo?: undefined },\n ) => Promise<unknown>;\n sendPresenceUpdate?: (presence: string, jid?: string) => Promise<unknown>;\n presenceSubscribe?: (jid: string) => Promise<unknown>;\n end?: (err: Error | undefined) => void;\n groupFetchAllParticipating?: () => Promise<Record<string, { id: string; subject?: string }>>;\n onWhatsApp?: (...jids: string[]) => Promise<Array<{ jid?: string; exists?: boolean; lid?: string }>>;\n updateMediaMessage?: (msg: unknown) => Promise<unknown>;\n};\n\ninterface UnsafeBaileys {\n default: (opts: Record<string, unknown>) => WASocket;\n useMultiFileAuthState: (dir: string) => Promise<{ state: unknown; saveCreds: () => Promise<void> }>;\n fetchLatestBaileysVersion: () => Promise<{ version: [number, number, number] }>;\n DisconnectReason: Record<string, number>;\n downloadMediaMessage: (\n message: unknown,\n type: \"buffer\" | \"stream\",\n options: Record<string, unknown>,\n ctx?: { logger?: unknown; reuploadRequest?: (msg: unknown) => Promise<unknown> },\n ) => Promise<Buffer>;\n Browsers: {\n ubuntu: (browser: string) => [string, string, string];\n macOS: (browser: string) => [string, string, string];\n windows: (browser: string) => [string, string, string];\n appropriate: (browser: string) => [string, string, string];\n };\n}\n\nexport class WhatsAppBridgeAdapter implements BridgeAdapter {\n readonly bridge_id: string;\n private sock: WASocket | null = null;\n private inboundHandler: InboundHandler | null = null;\n private statusHandler: StatusHandler | null = null;\n private stopping = false;\n private currentStatus: StatusUpdate[\"status\"] = \"disconnected\";\n // Chats observed since this adapter connected. Populated from\n // messaging-history.set (initial sync), chats.upsert/update, contacts\n // upsert (for display names), and observed messages. Cleared on disconnect.\n private chats = new Map<string, ChatInfo>();\n // The paired account's own JID (normalized, no device suffix). Set on\n // connection — used to recognize the self-chat so messages the user sends\n // to themselves can route to an agent.\n private selfJid: string | null = null;\n // IDs of messages we sent via sendText. WhatsApp echoes these back as\n // `fromMe` upserts; we suppress only those IDs so user-authored `fromMe`\n // messages still flow through for context. Bounded ring (most recent N).\n private sentIds: string[] = [];\n private sentIdsSet = new Set<string>();\n private static readonly SENT_IDS_MAX = 500;\n /**\n * Cap on per-attachment payload size we'll forward to the LLM. WhatsApp\n * compresses images to a few hundred KB; voice notes / short videos\n * usually sit well under this too. The cap mainly exists so a deliberately\n * crafted huge file (or a wallpaper-sized JPEG, or a 30-min video) doesn't\n * blow the agent's request budget. 8 MB raw ≈ ~11 MB base64 inline.\n */\n private static readonly MAX_MEDIA_BYTES = 8 * 1024 * 1024;\n // Runtime-only CommonJS resolver for optional legacy deps. Using\n // createRequire keeps webpack/tsc from trying to statically resolve\n // uninstalled optional packages at build time.\n private static readonly REQUIRE = createRequire(import.meta.url);\n\n constructor(bridge_id: string) {\n this.bridge_id = bridge_id;\n }\n\n onInboundMessage(handler: InboundHandler): void { this.inboundHandler = handler; }\n onStatusChange(handler: StatusHandler): void { this.statusHandler = handler; }\n\n async start(): Promise<void> {\n if (this.sock) return;\n this.stopping = false;\n\n // Dynamically import — see comment at top of file.\n let baileys: UnsafeBaileys;\n let qrcode: typeof import(\"qrcode\");\n try {\n try {\n baileys = (await import(\"@whiskeysockets/baileys\")) as unknown as UnsafeBaileys;\n } catch {\n // Backward compatibility for installs that still provide the legacy\n // unscoped package name.\n baileys = WhatsAppBridgeAdapter.REQUIRE(\"baileys\") as UnsafeBaileys;\n }\n qrcode = await import(\"qrcode\");\n } catch (err) {\n const m = err instanceof Error ? err.message : String(err);\n this.pushStatus({\n status: \"error\",\n error: `Baileys not installed. Install @whiskeysockets/baileys and qrcode. (${m})`,\n });\n throw err;\n }\n\n const authDir = ensureBridgeAuthDir(this.bridge_id);\n const { state, saveCreds } = await baileys.useMultiFileAuthState(authDir);\n\n let version: [number, number, number] | undefined;\n try {\n const v = await baileys.fetchLatestBaileysVersion();\n version = v.version;\n } catch {\n // Network blip during boot — Baileys will fall back to its bundled\n // version constant. Not fatal.\n }\n\n const sock = baileys.default({\n auth: state,\n version,\n // Quiet the embedded pino logger — we surface the meaningful events\n // (status, errors) via our own logging.\n logger: makeSilentLogger(),\n // IMPORTANT: WhatsApp validates the browser identifier during link-\n // device pairing and rejects unrecognized tuples with the misleading\n // \"Check your phone connection and try again\" error on the phone\n // (even though the WS connection itself is fine). The Baileys-shipped\n // `Browsers` helpers produce identifiers WhatsApp accepts. We tried\n // swapping the middle slot to \"Jarela\" once for branding in the\n // linked-devices list — WhatsApp rejected pairing. Keep \"Chrome\".\n browser: baileys.Browsers.ubuntu(\"Chrome\"),\n markOnlineOnConnect: false,\n syncFullHistory: false,\n });\n this.sock = sock;\n\n sock.ev.on(\"creds.update\", saveCreds);\n\n sock.ev.on(\"connection.update\", async (...args: unknown[]) => {\n const u = (args[0] ?? {}) as Record<string, unknown>;\n const conn = u.connection as string | undefined;\n const qr = u.qr as string | undefined;\n const lastDisconnect = u.lastDisconnect as\n | { error?: { output?: { statusCode?: number }; message?: string } }\n | undefined;\n\n if (qr) {\n try {\n const dataUrl = await qrcode.toDataURL(qr);\n this.pushStatus({ status: \"pairing\", qr_data_url: dataUrl, error: null });\n } catch {\n // QR encoding failed — fall back to raw string (UI can render it\n // via any QR component) by skipping data URL.\n this.pushStatus({ status: \"pairing\", qr_data_url: qr, error: null });\n }\n }\n\n if (conn === \"open\") {\n const me = (sock as unknown as { user?: { id?: string } }).user?.id ?? null;\n this.pushStatus({\n status: \"connected\",\n qr_data_url: null,\n error: null,\n paired_id: me,\n });\n // Pin the user's own number into the chat cache so \"Message yourself\"\n // is always pickable in the route editor — handy for testing a route\n // without needing a second WhatsApp account. The paired_id we get\n // from Baileys can include a device suffix (\":NN\") that's stripped\n // here to produce a routable @s.whatsapp.net JID.\n if (me) {\n const selfJid = normalizeUserJid(me);\n this.selfJid = selfJid;\n if (selfJid) this.observeChat(selfJid, \"Yourself\", null);\n }\n // Kick off a background fetch of group metadata so the picker has\n // names for joined groups even before any message arrives. Personal\n // chats trickle in via messaging-history.set + chats.upsert.\n void this.refreshChats().catch(() => { /* best-effort */ });\n } else if (conn === \"close\") {\n const code = lastDisconnect?.error?.output?.statusCode;\n const loggedOut = code === baileys.DisconnectReason?.loggedOut;\n\n if (this.stopping) {\n this.pushStatus({ status: \"disconnected\", qr_data_url: null, error: null });\n return;\n }\n if (loggedOut) {\n this.pushStatus({\n status: \"error\",\n qr_data_url: null,\n error: \"Logged out — auth invalidated by remote. Re-pair required.\",\n });\n // Don't auto-reconnect on logout; the user has to re-pair.\n this.sock = null;\n return;\n }\n // Otherwise: transient disconnect. Reconnect with simple backoff.\n const reason = lastDisconnect?.error?.message ?? `code=${code}`;\n this.pushStatus({ status: \"error\", qr_data_url: null, error: `Disconnected: ${reason}` });\n this.sock = null;\n setTimeout(() => {\n if (!this.stopping) {\n void this.start().catch((e) => {\n console.error(`[bridge ${this.bridge_id}] reconnect failed:`, e);\n });\n }\n }, 5_000).unref?.();\n }\n });\n\n sock.ev.on(\"messages.upsert\", async (...args: unknown[]) => {\n const payload = (args[0] ?? {}) as { messages?: unknown[]; type?: string };\n if (payload.type !== \"notify\") return; // ignore history/append\n for (const raw of payload.messages ?? []) {\n const m = raw as {\n key?: { remoteJid?: string; remoteJidAlt?: string; fromMe?: boolean; id?: string; participant?: string };\n pushName?: string;\n message?: WAMessageContent;\n messageTimestamp?: number | { low?: number };\n };\n if (!m.key?.remoteJid) continue;\n if (m.key.fromMe) {\n // Include user-authored `fromMe` traffic so the agent receives full\n // chat context (including the user's own replies), but suppress\n // bridge-authored echoes to prevent bot loopbacks.\n if (m.key.id && this.sentIdsSet.has(m.key.id)) continue;\n }\n // Baileys 7 / modern WhatsApp delivers many personal chats with a\n // `@lid` (\"Local IDentifier\") remoteJid instead of `@s.whatsapp.net`.\n // The chat picker filters those out (they aren't valid sendMessage\n // targets), so saved routes are keyed on the `@s.whatsapp.net` form.\n // To make routing actually match, prefer `remoteJidAlt` when it\n // points at a routable JID — that's the same chat in its\n // phone-number form.\n const remote_jid = pickRoutableJid(m.key.remoteJid, m.key.remoteJidAlt);\n const is_group = remote_jid.endsWith(\"@g.us\");\n // Update the chat cache regardless of payload shape — the chat is\n // \"real\" the moment we see any message from it, even one whose\n // body we end up unable to represent (e.g. reactions, polls).\n const ts = typeof m.messageTimestamp === \"number\"\n ? m.messageTimestamp * 1000\n : (m.messageTimestamp?.low ?? 0) * 1000 || Date.now();\n // In groups, pushName is typically the participant's display name,\n // not the group subject. Avoid poisoning the group chat label.\n this.observeChat(remote_jid, is_group ? null : (m.pushName ?? null), ts);\n\n // Unwrap protocol envelopes (view-once, ephemeral, etc.) so we can\n // see the underlying payload uniformly, then turn it into a text\n // body + ContentPart attachments the agent can consume.\n const inner = unwrapMessage(m.message);\n const { text, attachments } = await this.extractContent(inner, m, baileys, sock, remote_jid);\n\n // Drop messages we can't represent at all (e.g. protocol/system\n // notices, reactions, unsupported message types).\n if (!text && attachments.length === 0) continue;\n // In group chats `key.participant` is the actual sender's JID\n // (the chat-level remote_jid is the group, not the person). In 1:1\n // chats it's undefined — the sender == remote_jid. Normalize so the\n // dispatcher gets a routable user JID, not the legacy @c.us form.\n const participant_jid = is_group && m.key.participant\n ? normalizeUserJid(m.key.participant)\n : null;\n const chat_name = this.chats.get(remote_jid)?.name ?? m.pushName ?? null;\n const sender_name = participant_jid\n ? (this.chats.get(participant_jid)?.name ?? m.pushName ?? participant_jid)\n : (m.pushName ?? chat_name);\n const inbound: InboundMessage = {\n remote_jid,\n push_name: m.pushName ?? null,\n chat_name,\n sender_name,\n text,\n attachments: attachments.length ? attachments : undefined,\n message_id: m.key.id ?? null,\n is_group,\n participant_jid,\n // sentIdsSet filtering above means agent echoes never reach this\n // construction site, so \"user\" vs \"counterpart\" is the only live\n // distinction here.\n role: m.key.fromMe ? \"user\" : \"counterpart\",\n };\n if (this.inboundHandler) {\n try { await this.inboundHandler(inbound); }\n catch (err) {\n console.error(`[bridge ${this.bridge_id}] inbound handler threw:`, err);\n }\n }\n }\n });\n\n // ---- Chat cache: populated from history sync + live chat/contact events ----\n //\n // `messaging-history.set` carries the recent chats Baileys received\n // during the initial sync. `chats.upsert` / `chats.update` fire for\n // brand-new chats and metadata changes. `contacts.upsert` gives us\n // display names for personal contacts.\n sock.ev.on(\"messaging-history.set\", (...args: unknown[]) => {\n const payload = (args[0] ?? {}) as { chats?: unknown[]; contacts?: unknown[] };\n for (const raw of payload.chats ?? []) {\n const c = raw as { id?: string; name?: string; conversationTimestamp?: number };\n if (!c.id) continue;\n const ts = c.conversationTimestamp ? c.conversationTimestamp * 1000 : null;\n this.observeChat(c.id, c.name ?? null, ts);\n }\n for (const raw of payload.contacts ?? []) {\n const ct = raw as { id?: string; name?: string; notify?: string; verifiedName?: string };\n if (!ct.id) continue;\n const name = ct.name ?? ct.verifiedName ?? ct.notify ?? null;\n // Only register a chat if the contact id looks like a chat JID\n // (skip the user's own LID/PN noise). @s.whatsapp.net / @g.us only.\n if (ct.id.endsWith(\"@s.whatsapp.net\") || ct.id.endsWith(\"@g.us\")) {\n this.observeChat(ct.id, name, null);\n } else {\n // Still keep the display name handy in case we see this contact\n // from a different JID shape later — but don't surface non-chat\n // JIDs in the picker.\n }\n }\n });\n\n sock.ev.on(\"chats.upsert\", (...args: unknown[]) => {\n const list = (args[0] ?? []) as Array<{ id?: string; name?: string; conversationTimestamp?: number }>;\n for (const c of list) {\n if (!c.id) continue;\n const ts = c.conversationTimestamp ? c.conversationTimestamp * 1000 : null;\n this.observeChat(c.id, c.name ?? null, ts);\n }\n });\n\n sock.ev.on(\"chats.update\", (...args: unknown[]) => {\n const list = (args[0] ?? []) as Array<{ id?: string; name?: string; conversationTimestamp?: number }>;\n for (const c of list) {\n if (!c.id) continue;\n const ts = c.conversationTimestamp ? c.conversationTimestamp * 1000 : null;\n this.observeChat(c.id, c.name ?? null, ts);\n }\n });\n\n sock.ev.on(\"contacts.upsert\", (...args: unknown[]) => {\n const list = (args[0] ?? []) as Array<{ id?: string; name?: string; notify?: string; verifiedName?: string }>;\n for (const ct of list) {\n if (!ct.id) continue;\n if (!(ct.id.endsWith(\"@s.whatsapp.net\") || ct.id.endsWith(\"@g.us\"))) continue;\n this.observeChat(ct.id, ct.name ?? ct.verifiedName ?? ct.notify ?? null, null);\n }\n });\n }\n\n async stop(): Promise<void> {\n this.stopping = true;\n const sock = this.sock;\n this.sock = null;\n this.chats.clear();\n this.selfJid = null;\n this.sentIds = [];\n this.sentIdsSet.clear();\n if (!sock) return;\n try {\n // logout() also wipes server-side auth — we just want to close the\n // socket and leave creds on disk intact. `end()` is the right call.\n (sock as unknown as { end?: (err: Error | undefined) => void }).end?.(undefined);\n } catch { /* ignore */ }\n this.pushStatus({ status: \"disconnected\", qr_data_url: null, error: null });\n }\n\n async sendText(remote_jid: string, text: string): Promise<void> {\n // Hard guard: refuse to send to any chat whose route is in silent_mode.\n // The dispatcher already short-circuits before calling sendText, but\n // we re-check here so that *any* future code path that gets hold of an\n // adapter instance (agent tools, plugins, debug shells, scheduled jobs)\n // cannot bypass the user's silent-mode choice. The route table is the\n // single source of truth — one synchronous SQLite lookup per send is\n // cheap and avoids stale-cache bugs.\n const route = findRoute(this.bridge_id, remote_jid);\n if (route?.silent_mode === 1) {\n console.warn(\n `[bridge ${this.bridge_id}] sendText blocked: route ${route.id} (${remote_jid}) is in silent_mode`,\n );\n return;\n }\n const sock = this.sock;\n if (!sock) throw new Error(\"Bridge not connected\");\n const result = await (\n sock as unknown as {\n sendMessage: (\n jid: string,\n content: { text: string },\n options?: { getUrlInfo?: undefined },\n ) => Promise<unknown>;\n }\n ).sendMessage(\n remote_jid,\n { text },\n // Security hardening: prevent Baileys from invoking link-preview-js URL\n // fetches for outbound text messages (SSRF/loopback class risks).\n { getUrlInfo: undefined },\n );\n // Record the outgoing message ID so the matching `fromMe` echo from\n // messages.upsert doesn't re-enter the routing pipeline in the self-chat.\n const sentId = (result as { key?: { id?: string } } | null | undefined)?.key?.id;\n if (sentId) this.rememberSentId(sentId);\n }\n\n private rememberSentId(id: string): void {\n if (this.sentIdsSet.has(id)) return;\n this.sentIds.push(id);\n this.sentIdsSet.add(id);\n while (this.sentIds.length > WhatsAppBridgeAdapter.SENT_IDS_MAX) {\n const evict = this.sentIds.shift();\n if (evict) this.sentIdsSet.delete(evict);\n }\n }\n\n async sendTyping(remote_jid: string, typing: boolean): Promise<void> {\n // Hard guard: typing/composing is an outbound signal — suppress on\n // silent_mode routes for the same reason as sendText.\n if (typing) {\n const route = findRoute(this.bridge_id, remote_jid);\n if (route?.silent_mode === 1) return;\n }\n const sock = this.sock;\n if (!sock?.sendPresenceUpdate) return;\n // Subscribing makes WhatsApp deliver presence both ways — not strictly\n // required for sending our composing state, but cheap and keeps the\n // session consistent. Errors are best-effort.\n try {\n if (typing && sock.presenceSubscribe) {\n await sock.presenceSubscribe(remote_jid).catch(() => { /* best-effort */ });\n }\n await sock.sendPresenceUpdate(typing ? \"composing\" : \"paused\", remote_jid);\n } catch (err) {\n // Don't let presence hiccups break the reply path — just log.\n console.warn(`[bridge ${this.bridge_id}] sendPresenceUpdate failed:`, err);\n }\n }\n\n async resetAuth(): Promise<void> {\n await this.stop();\n removeBridgeAuthDir(this.bridge_id);\n }\n\n listChats(): ChatInfo[] {\n // Only chats whose JID is a routable chat (1:1 or group). Status JIDs\n // (broadcast lists, statuses, \"@broadcast\", \"@lid\", \"@s.whatsapp.net:0\")\n // are filtered out — none of them are valid sendMessage targets.\n const out: ChatInfo[] = [];\n for (const c of this.chats.values()) {\n if (c.remote_jid.endsWith(\"@s.whatsapp.net\") || c.remote_jid.endsWith(\"@g.us\")) {\n out.push(c);\n }\n }\n // Newest activity first; chats we've never seen a message from go last,\n // alphabetically by name so the picker is stable.\n out.sort((a, b) => {\n const ta = a.last_message_at ?? 0;\n const tb = b.last_message_at ?? 0;\n if (ta !== tb) return tb - ta;\n return (a.name ?? a.remote_jid).localeCompare(b.name ?? b.remote_jid);\n });\n return out;\n }\n\n async refreshChats(): Promise<void> {\n const sock = this.sock;\n if (!sock?.groupFetchAllParticipating) return;\n try {\n const groups = await sock.groupFetchAllParticipating();\n for (const g of Object.values(groups)) {\n if (!g?.id) continue;\n this.observeChat(g.id, g.subject ?? null, null);\n }\n } catch (err) {\n // Group enumeration is best-effort — a transient WS hiccup shouldn't\n // poison anything.\n console.warn(`[bridge ${this.bridge_id}] groupFetchAllParticipating failed:`, err);\n }\n }\n\n async lookupChat(input: string): Promise<ChatInfo | null> {\n const sock = this.sock;\n if (!sock?.onWhatsApp) return null;\n const digits = normalizePhoneDigits(input);\n if (digits.length < 6) return null; // refuse obviously-bogus short numbers\n try {\n const results = await sock.onWhatsApp(digits);\n const hit = results?.find((r) => r?.exists && r.jid);\n if (!hit?.jid) return null;\n // Add it to our chat cache so the UI's polling pass picks it up too.\n this.observeChat(hit.jid, null, null);\n return {\n remote_jid: hit.jid,\n name: null,\n is_group: hit.jid.endsWith(\"@g.us\"),\n last_message_at: null,\n };\n } catch (err) {\n console.warn(`[bridge ${this.bridge_id}] onWhatsApp lookup failed:`, err);\n return null;\n }\n }\n\n /**\n * Extract a routable text body + ContentPart attachments from one inbound\n * WhatsApp message. Handles every common payload type:\n * - imageMessage → image ContentPart (vision input)\n * - stickerMessage → image ContentPart (webp; vision input)\n * - audioMessage / PTT → file ContentPart with audio/* mime\n * - videoMessage → file ContentPart with video/* mime\n * - documentMessage → file ContentPart (utf-8 text for text/*+json,\n * base64 for everything else)\n * - location / liveLoc → inline text \"[location: lat,lng ...]\"\n * - contactMessage(s) → inline text with the vcard / display names\n *\n * Each download is independent — a failure on one attachment doesn't lose\n * the caption text or other attachments. Anything we can't represent\n * leaves both fields empty and the upsert loop drops the message.\n */\n private async extractContent(\n inner: WAMessageContent | undefined,\n rawMessage: unknown,\n baileys: UnsafeBaileys,\n sock: WASocket,\n remote_jid: string,\n ): Promise<{ text: string; attachments: ContentPart[] }> {\n const attachments: ContentPart[] = [];\n let text =\n inner?.conversation\n ?? inner?.extendedTextMessage?.text\n ?? inner?.imageMessage?.caption\n ?? inner?.videoMessage?.caption\n ?? inner?.documentMessage?.caption\n ?? \"\";\n if (!inner) return { text, attachments };\n\n // Baileys' downloadMediaMessage expects the *outer* upsert message\n // (with `.key` + `.message`), not the inner part. Wrap the per-kind\n // download here so the loop body stays declarative.\n const download = async (label: string): Promise<Buffer | null> => {\n try {\n const buf = await baileys.downloadMediaMessage(\n rawMessage,\n \"buffer\",\n {},\n {\n logger: makeSilentLogger(),\n reuploadRequest: sock.updateMediaMessage?.bind(sock),\n },\n );\n if (!buf || buf.length === 0) return null;\n if (buf.length > WhatsAppBridgeAdapter.MAX_MEDIA_BYTES) {\n console.warn(\n `[bridge ${this.bridge_id}] dropped oversize ${label} (${buf.length} bytes) from ${remote_jid}`,\n );\n return null;\n }\n return buf;\n } catch (err) {\n // Media decryption / network errors are non-fatal — fall through\n // so the caption (if any) and other parts still reach the agent.\n const m = err instanceof Error ? err.message : String(err);\n console.warn(`[bridge ${this.bridge_id}] ${label} download failed for ${remote_jid}: ${m}`);\n return null;\n }\n };\n\n // Spill helper: persist any buffer we can't (or shouldn't) inline,\n // and append a text pointer the agent can act on with file_read.\n // Returns true if the buffer was spilled so callers can decide\n // whether to also push an inline ContentPart.\n const messageId = (rawMessage as { key?: { id?: string } })?.key?.id ?? null;\n const spill = async (\n buf: Buffer,\n filename: string,\n mime: string,\n label: string,\n ): Promise<void> => {\n try {\n const saved = await saveBridgeAttachment({\n bridge_id: this.bridge_id,\n filename,\n media_type: mime,\n message_id: messageId,\n buffer: buf,\n });\n const sizeKb = Math.max(1, Math.round(saved.size / 1024));\n text = (text ? text + \"\\n\" : \"\")\n + `[Attached ${label}: ${filename} (${mime}, ${sizeKb} KB) saved locally at ${saved.abs_path}. `\n + `Use file_read on that path to inspect the contents.]`;\n } catch (err) {\n const m = err instanceof Error ? err.message : String(err);\n console.warn(`[bridge ${this.bridge_id}] failed to spill ${label} from ${remote_jid}: ${m}`);\n }\n };\n\n if (inner.imageMessage) {\n const buf = await download(\"image\");\n if (buf) {\n const mime = sanitizeMediaType(inner.imageMessage.mimetype, \"image\", \"image/jpeg\");\n if (shouldInline(mime, buf.length)) {\n attachments.push({ type: \"image\", media_type: mime, data: buf.toString(\"base64\") });\n } else {\n await spill(buf, `image-${messageId ?? Date.now()}.${mime.split(\"/\")[1] ?? \"bin\"}`, mime, \"image\");\n }\n }\n }\n\n if (inner.stickerMessage) {\n const buf = await download(\"sticker\");\n if (buf) {\n // Stickers are webp images — surfacing them as `image` lets vision\n // models actually describe them. Animated stickers go through as\n // their raw webp; providers that can't decode animated webp will\n // typically render the first frame.\n const mime = sanitizeMediaType(inner.stickerMessage.mimetype, \"image\", \"image/webp\");\n if (shouldInline(mime, buf.length)) {\n attachments.push({ type: \"image\", media_type: mime, data: buf.toString(\"base64\") });\n } else {\n await spill(buf, `sticker-${messageId ?? Date.now()}.webp`, mime, \"sticker\");\n }\n }\n }\n\n if (inner.audioMessage) {\n const isVoice = !!inner.audioMessage.ptt;\n const buf = await download(isVoice ? \"voice\" : \"audio\");\n if (buf) {\n const mime = sanitizeMediaType(inner.audioMessage.mimetype, \"audio\", \"audio/ogg\");\n const ext = mime.split(\"/\")[1]?.replace(/^x-/, \"\") ?? \"ogg\";\n await spill(buf, `${isVoice ? \"voice-note\" : \"audio\"}-${messageId ?? Date.now()}.${ext}`, mime, isVoice ? \"voice note\" : \"audio\");\n }\n }\n\n if (inner.videoMessage) {\n const buf = await download(\"video\");\n if (buf) {\n const mime = sanitizeMediaType(inner.videoMessage.mimetype, \"video\", \"video/mp4\");\n const ext = mime.split(\"/\")[1] ?? \"mp4\";\n await spill(buf, `video-${messageId ?? Date.now()}.${ext}`, mime, \"video\");\n }\n }\n\n if (inner.documentMessage) {\n const buf = await download(\"document\");\n if (buf) {\n const mime = sanitizeMediaType(\n inner.documentMessage.mimetype,\n \"document\",\n \"application/octet-stream\",\n );\n const filename = inner.documentMessage.fileName || inner.documentMessage.title || `document-${messageId ?? Date.now()}`;\n await spill(buf, filename, mime, \"document\");\n }\n }\n\n if (inner.locationMessage) {\n const { degreesLatitude, degreesLongitude, name, address } = inner.locationMessage;\n const parts = [`lat=${degreesLatitude ?? \"?\"}`, `lng=${degreesLongitude ?? \"?\"}`];\n if (name) parts.push(`name=\"${name}\"`);\n if (address) parts.push(`address=\"${address}\"`);\n text = (text ? text + \"\\n\" : \"\") + `[location: ${parts.join(\" \")}]`;\n }\n\n if (inner.liveLocationMessage) {\n const { degreesLatitude, degreesLongitude } = inner.liveLocationMessage;\n text = (text ? text + \"\\n\" : \"\") + `[live-location: lat=${degreesLatitude ?? \"?\"} lng=${degreesLongitude ?? \"?\"}]`;\n }\n\n if (inner.contactMessage) {\n const name = inner.contactMessage.displayName ?? \"unknown\";\n const vcard = inner.contactMessage.vcard ?? \"\";\n text = (text ? text + \"\\n\" : \"\") + `[contact: ${name}]\\n${vcard}`;\n }\n\n if (inner.contactsArrayMessage) {\n const names = (inner.contactsArrayMessage.contacts ?? [])\n .map((c) => c?.displayName ?? \"unknown\")\n .join(\", \");\n text = (text ? text + \"\\n\" : \"\") + `[contacts: ${names}]`;\n }\n\n return { text, attachments };\n }\n\n private observeChat(remote_jid: string, name: string | null, ts: number | null): void {\n if (!remote_jid) return;\n const existing = this.chats.get(remote_jid);\n // Prefer the most recently-observed name (group subjects rename; users\n // change push names). Don't overwrite a known name with null.\n const nextName = name ?? existing?.name ?? null;\n const nextTs = ts && (!existing?.last_message_at || ts > existing.last_message_at)\n ? ts\n : existing?.last_message_at ?? null;\n this.chats.set(remote_jid, {\n remote_jid,\n name: nextName,\n is_group: remote_jid.endsWith(\"@g.us\"),\n last_message_at: nextTs,\n });\n }\n\n private pushStatus(u: StatusUpdate): void {\n this.currentStatus = u.status;\n this.statusHandler?.(u);\n }\n\n get status(): StatusUpdate[\"status\"] { return this.currentStatus; }\n}\n\n// Minimal pino-compatible silent logger. Baileys uses `logger.child()`\n// chaining internally, so we return ourselves for every child call.\nfunction makeSilentLogger(): unknown {\n const noop = () => { /* no-op */ };\n const self: Record<string, unknown> = {\n level: \"silent\",\n trace: noop, debug: noop, info: noop, warn: noop, error: noop, fatal: noop,\n };\n self.child = () => self;\n return self;\n}\n\n// Defence against an attacker-controlled sender stuffing a hostile\n// mimetype (e.g. `text/html; charset=...<script>`) into an inbound\n// WhatsApp message and having it surface unsanitised in our chat UI's\n// data-URL or in a downstream LLM provider call. We pin each media kind\n// to a small allowlist and fall back to the canonical default whenever\n// the sender's claimed type is unknown or syntactically suspicious.\nconst MEDIA_TYPE_ALLOWLIST: Record<\"image\" | \"audio\" | \"video\" | \"document\", ReadonlySet<string>> = {\n image: new Set([\"image/jpeg\", \"image/png\", \"image/webp\", \"image/gif\", \"image/heic\", \"image/heif\"]),\n audio: new Set([\"audio/ogg\", \"audio/mpeg\", \"audio/mp4\", \"audio/aac\", \"audio/webm\", \"audio/wav\", \"audio/x-wav\", \"audio/3gpp\"]),\n video: new Set([\"video/mp4\", \"video/webm\", \"video/quicktime\", \"video/3gpp\"]),\n // Documents: we keep the generic catch-all plus a handful of common\n // office/text types so the UI can hint at the right viewer. Anything\n // unrecognised collapses to application/octet-stream.\n document: new Set([\n \"application/octet-stream\",\n \"application/pdf\",\n \"application/zip\",\n \"application/json\",\n \"application/xml\",\n \"application/msword\",\n \"application/vnd.ms-excel\",\n \"application/vnd.ms-powerpoint\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \"text/plain\",\n \"text/csv\",\n \"text/markdown\",\n \"text/html\",\n ]),\n};\n\nfunction sanitizeMediaType(\n raw: string | undefined,\n kind: \"image\" | \"audio\" | \"video\" | \"document\",\n fallback: string,\n): string {\n if (!raw || typeof raw !== \"string\") return fallback;\n // Strip parameters (`image/jpeg; charset=...`), lowercase, trim.\n const base = raw.split(\";\")[0].trim().toLowerCase();\n // Reject anything that doesn't look like a bare RFC 6838 type/subtype.\n if (!/^[a-z0-9!#$&^_.+-]+\\/[a-z0-9!#$&^_.+-]+$/.test(base)) return fallback;\n return MEDIA_TYPE_ALLOWLIST[kind].has(base) ? base : fallback;\n}\n\n/**\n * Subset of Baileys' WAMessageContent we look at. WhatsApp wraps payloads\n * in several envelope variants (view-once, ephemeral, V2 versions); see\n * `unwrapMessage` for how we collapse them.\n */\ntype WAMessageContent = {\n conversation?: string;\n extendedTextMessage?: { text?: string };\n stickerMessage?: { mimetype?: string; isAnimated?: boolean };\n audioMessage?: { mimetype?: string; ptt?: boolean; seconds?: number };\n videoMessage?: { caption?: string; mimetype?: string; seconds?: number };\n documentMessage?: { caption?: string; fileName?: string; title?: string; mimetype?: string };\n locationMessage?: { degreesLatitude?: number; degreesLongitude?: number; name?: string; address?: string };\n liveLocationMessage?: { degreesLatitude?: number; degreesLongitude?: number; caption?: string };\n contactMessage?: { displayName?: string; vcard?: string };\n contactsArrayMessage?: { contacts?: Array<{ displayName?: string; vcard?: string }> };\n imageMessage?: { caption?: string; mimetype?: string };\n viewOnceMessage?: { message?: WAMessageContent };\n viewOnceMessageV2?: { message?: WAMessageContent };\n viewOnceMessageV2Extension?: { message?: WAMessageContent };\n ephemeralMessage?: { message?: WAMessageContent };\n documentWithCaptionMessage?: { message?: WAMessageContent };\n};\n\n/**\n * Collapse the various WhatsApp envelope wrappers down to the inner content\n * with the actual `imageMessage` / `conversation` / `extendedTextMessage`\n * payload. Bounded recursion — these envelopes never nest deeper than 2\n * levels in practice, but we cap at 5 as defence in depth.\n */\nfunction unwrapMessage(msg: WAMessageContent | undefined, depth = 0): WAMessageContent | undefined {\n if (!msg || depth > 5) return msg;\n if (msg.ephemeralMessage?.message) return unwrapMessage(msg.ephemeralMessage.message, depth + 1);\n if (msg.viewOnceMessage?.message) return unwrapMessage(msg.viewOnceMessage.message, depth + 1);\n if (msg.viewOnceMessageV2?.message) return unwrapMessage(msg.viewOnceMessageV2.message, depth + 1);\n if (msg.viewOnceMessageV2Extension?.message) return unwrapMessage(msg.viewOnceMessageV2Extension.message, depth + 1);\n if (msg.documentWithCaptionMessage?.message) return unwrapMessage(msg.documentWithCaptionMessage.message, depth + 1);\n return msg;\n}\n\n/**\n * Strip everything that isn't a digit. WhatsApp identifies accounts by\n * country-code + number with no separators, no leading '+'. Accepts the\n * common human formats: \"+1 (555) 123-4567\", \"5511 99999-0000\", etc.\n */\nfunction normalizePhoneDigits(input: string): string {\n return input.replace(/\\D+/g, \"\");\n}\n\n/**\n * Prefer the routable JID form (`@s.whatsapp.net` or `@g.us`) over `@lid`.\n *\n * WhatsApp's privacy-preserving identifier rollout means inbound messages\n * frequently arrive with `key.remoteJid = \"<id>@lid\"` and the actual\n * phone-number JID in `key.remoteJidAlt`. The chat picker can only show\n * routable JIDs (you can't sendMessage to an `@lid`), so saved routes use\n * the `@s.whatsapp.net` form — without this normalization, every inbound\n * `@lid` message would silently fail to match its route.\n */\nfunction pickRoutableJid(primary: string, alt: string | undefined): string {\n const isRoutable = (j: string | undefined) =>\n !!j && (j.endsWith(\"@s.whatsapp.net\") || j.endsWith(\"@g.us\"));\n if (isRoutable(primary)) return primary;\n if (isRoutable(alt)) return alt!;\n return primary;\n}\n\n/**\n * Baileys' paired_id (sock.user.id) sometimes carries a device suffix\n * like \"5511999990000:23@s.whatsapp.net\". Strip the suffix so we get a\n * routable user JID — `sendMessage` won't deliver to a JID with `:NN`.\n * Returns null if the input is malformed.\n */\nfunction normalizeUserJid(id: string): string | null {\n // Strip any device suffix (\":NN\") before the '@'.\n const at = id.indexOf(\"@\");\n if (at < 0) return null;\n const user = id.slice(0, at).split(\":\")[0];\n const host = id.slice(at + 1);\n if (!user || !host) return null;\n // WhatsApp uses @s.whatsapp.net for user accounts; in some contexts\n // Baileys exposes @c.us as a legacy alias — normalize to @s.whatsapp.net.\n const normHost = host === \"c.us\" ? \"s.whatsapp.net\" : host;\n return `${user}@${normHost}`;\n}\n","/**\n * Bridge lifecycle manager.\n *\n * Owns a `Map<bridge_id, BridgeAdapter>` plus a `Map<bridge_id, StatusUpdate>`\n * of the latest known status per bridge (so the HTTP status endpoint can\n * return live QR / connection state without consulting the adapter directly).\n *\n * - `startAllBridges()` — called at app boot; scans `bridges` for\n * `enabled=1` rows and spins up their adapters.\n * - `startBridge(id)` / `stopBridge(id)` / `restartBridge(id)` — called from\n * the HTTP layer (toggle enabled, re-pair, delete).\n *\n * Pinned to globalThis so dev HMR doesn't double-start (same pattern as\n * lib/scheduler/index.ts).\n */\n\nimport { getBridge, listBridges, updateBridge } from \"@/lib/stores/bridges\";\nimport { publish as publishNotification } from \"@/lib/notifications/bus\";\nimport { getOrCreateGlobal } from \"@/lib/utils/global-state\";\nimport { handleInboundMessage } from \"./dispatcher\";\nimport { WhatsAppBridgeAdapter } from \"./whatsapp\";\nimport type { BridgeAdapter, StatusUpdate } from \"./types\";\n\ninterface RuntimeState {\n adapters: Map<string, BridgeAdapter>;\n status: Map<string, StatusUpdate>;\n started: boolean;\n}\nconst state = getOrCreateGlobal<RuntimeState>(\"__jarela_bridges\", () => ({\n adapters: new Map(),\n status: new Map(),\n started: false,\n}));\n\nfunction makeAdapter(bridge_id: string, kind: string): BridgeAdapter {\n switch (kind) {\n case \"whatsapp\":\n return new WhatsAppBridgeAdapter(bridge_id);\n default:\n throw new Error(`Unknown bridge kind: ${kind}`);\n }\n}\n\nfunction wireAdapter(adapter: BridgeAdapter): void {\n adapter.onInboundMessage((msg) => handleInboundMessage(adapter, msg));\n adapter.onStatusChange((update) => {\n state.status.set(adapter.bridge_id, update);\n // Persist the user-visible bits so they survive a process restart.\n const patch: Parameters<typeof updateBridge>[1] = {\n status: update.status,\n qr: update.qr_data_url ?? null,\n last_error: update.error ?? null,\n };\n if (update.paired_id !== undefined) patch.paired_id = update.paired_id;\n updateBridge(adapter.bridge_id, patch);\n publishNotification({\n type: \"bridge_status\",\n bridge_id: adapter.bridge_id,\n status: update.status,\n error: update.error ?? null,\n paired_id: update.paired_id ?? null,\n ts: Date.now(),\n });\n });\n}\n\nexport async function startBridge(bridge_id: string): Promise<void> {\n if (state.adapters.has(bridge_id)) return;\n const row = getBridge(bridge_id);\n if (!row) throw new Error(`Bridge ${bridge_id} not found`);\n const adapter = makeAdapter(bridge_id, row.kind);\n state.adapters.set(bridge_id, adapter);\n wireAdapter(adapter);\n try {\n await adapter.start();\n } catch (err) {\n state.adapters.delete(bridge_id);\n const m = err instanceof Error ? err.message : String(err);\n updateBridge(bridge_id, { status: \"error\", last_error: m });\n throw err;\n }\n}\n\nexport async function stopBridge(bridge_id: string): Promise<void> {\n const adapter = state.adapters.get(bridge_id);\n state.adapters.delete(bridge_id);\n state.status.delete(bridge_id);\n if (adapter) {\n try { await adapter.stop(); }\n catch (err) {\n console.error(`[bridge ${bridge_id}] stop failed:`, err);\n }\n }\n // Persist that the bridge is down (in case status callback didn't fire).\n updateBridge(bridge_id, { status: \"disconnected\", qr: null });\n}\n\nexport async function restartBridge(bridge_id: string): Promise<void> {\n await stopBridge(bridge_id);\n await startBridge(bridge_id);\n}\n\nexport async function repairBridge(bridge_id: string): Promise<void> {\n // Wipe creds on disk + restart. Adapter will produce a fresh QR.\n const adapter = state.adapters.get(bridge_id);\n if (adapter) {\n try { await adapter.resetAuth(); } catch { /* ignore */ }\n state.adapters.delete(bridge_id);\n state.status.delete(bridge_id);\n } else {\n // Not running — wipe the dir directly via a temporary adapter so callers\n // can re-pair an idle bridge without first enabling it.\n const tmp = makeAdapter(bridge_id, getBridge(bridge_id)?.kind ?? \"whatsapp\");\n await tmp.resetAuth();\n }\n updateBridge(bridge_id, { status: \"disconnected\", qr: null, last_error: null, paired_id: null });\n await startBridge(bridge_id);\n}\n\nexport function getBridgeRuntimeStatus(bridge_id: string): StatusUpdate | null {\n return state.status.get(bridge_id) ?? null;\n}\n\nexport function isBridgeRunning(bridge_id: string): boolean {\n return state.adapters.has(bridge_id);\n}\n\n/**\n * Snapshot of chats the adapter has observed since connecting. Returns []\n * when the bridge isn't running yet. Triggers a background refresh so that\n * subsequent calls see newly-fetched group metadata.\n */\nexport function listBridgeChats(bridge_id: string) {\n const adapter = state.adapters.get(bridge_id);\n if (!adapter) return [];\n // Fire-and-forget — refreshChats hits a WS round-trip; HTTP shouldn't\n // wait. The next poll will pick up any new entries.\n void adapter.refreshChats().catch(() => { /* logged inside */ });\n return adapter.listChats();\n}\n\n/**\n * Look up a freeform phone number (or other identifier) against the bridge\n * and return the resolved chat if it exists, else null. Returns null also\n * if the bridge isn't running.\n */\nexport async function lookupBridgeChat(bridge_id: string, input: string) {\n const adapter = state.adapters.get(bridge_id);\n if (!adapter) return null;\n return adapter.lookupChat(input);\n}\n\n/**\n * Boot hook. Idempotent — safe to call from a layout module that may be\n * evaluated multiple times in Next.js dev HMR.\n */\nexport async function startAllBridges(): Promise<void> {\n if (state.started) return;\n state.started = true;\n const rows = listBridges();\n for (const row of rows) {\n if (row.enabled !== 1) continue;\n try {\n await startBridge(row.id);\n } catch (err) {\n const m = err instanceof Error ? err.message : String(err);\n console.error(`[bridge ${row.id}] failed to start at boot:`, m);\n // updateBridge already happened inside startBridge's catch.\n }\n }\n}\n\n// Stop every running bridge. Called from the graceful-shutdown path so\n// WhatsApp WS sockets etc. close cleanly before the process exits. We\n// await in parallel — a single slow `adapter.stop()` shouldn't gate the\n// others — but the whole call is still bounded by the outer shutdown\n// timeout in `lib/lifecycle/shutdown.ts`.\nexport async function stopAllBridges(): Promise<void> {\n const ids = Array.from(state.adapters.keys());\n await Promise.allSettled(ids.map((id) => stopBridge(id)));\n state.started = false;\n}\n"],"names":["randomUUID","join","mkdirSync","rmSync","getDb","now","Date","toISOString","getDataDir","bridgeAuthDir","bridgeId","ensureBridgeAuthDir","dir","recursive","removeBridgeAuthDir","force","listBridges","prepare","all","getBridge","id","get","createBridge","input","t","run","kind","name","status","qr","last_error","paired_id","enabled","created_at","updated_at","updateBridge","patch","existing","merged","undefined","deleteBridge","db","r","changes","listRoutes","listAllRoutes","getRoute","findRoute","remoteJid","createRoute","silent","silent_mode","respondTo","respond_to","bridge_id","remote_jid","agent_id","label","updateRoute","deleteRoute","resolveRoute","resolveAgent","getOrCreateAgentThread","getAgentConfig","runAgentTurn","publish","publishNotification","formatBridgePrompt","handleInboundMessage","adapter","msg","route","console","log","push_name","agentId","agent","warn","thread","chatName","chat_name","senderJid","participant_jid","senderName","sender_name","promptText","chat_id","is_group","role","sender_id","text","willReply","typingActive","sendTyping","catch","typingTimer","setInterval","unref","reply","suppressAssistant","result","thread_id","queue_source","message","attachments","user_category","assistant_category","assistantContent","trim","skippedAssistant","clearInterval","length","sendText","sendErr","m","Error","String","error","type","preview","replace","slice","ts","err","promises","fs","path","crypto","BRIDGE_ATTACHMENTS_DIRNAME","DEFAULT_INLINE_LIMIT_BYTES","INLINE_MIME_PREFIXES","shouldInline","media_type","size","limit","some","p","startsWith","baseDir","todayDir","d","yyyy","getFullYear","mm","getMonth","padStart","dd","getDate","safeFilename","fallback","raw","s","ext","extname","safeBridgeId","saveBridgeAttachment","mkdir","idPart","message_id","randomBytes","toString","fname","filename","abs","writeFile","buffer","sha256","createHash","update","digest","abs_path","pruneBridgeAttachments","opts","root","cutoff","Math","max","maxAgeMs","removed_files","removed_dirs","freed_bytes","bridges","readdir","bridge","bridgeDir","days","day","dayDir","files","f","fp","st","stat","isFile","mtimeMs","unlink","remaining","rmdir","bridgeAttachmentsRoot","createRequire","WhatsAppBridgeAdapter","SENT_IDS_MAX","MAX_MEDIA_BYTES","REQUIRE","url","sock","inboundHandler","statusHandler","stopping","currentStatus","chats","Map","selfJid","sentIds","sentIdsSet","Set","onInboundMessage","handler","onStatusChange","start","baileys","qrcode","pushStatus","authDir","state","saveCreds","useMultiFileAuthState","version","v","fetchLatestBaileysVersion","default","auth","logger","makeSilentLogger","browser","Browsers","ubuntu","markOnlineOnConnect","syncFullHistory","ev","on","args","u","conn","connection","lastDisconnect","dataUrl","toDataURL","qr_data_url","me","user","normalizeUserJid","observeChat","refreshChats","code","output","statusCode","loggedOut","DisconnectReason","reason","setTimeout","e","payload","messages","key","fromMe","has","pickRoutableJid","remoteJidAlt","endsWith","messageTimestamp","low","pushName","inner","unwrapMessage","extractContent","participant","inbound","c","conversationTimestamp","contacts","ct","verifiedName","notify","list","stop","clear","end","sendMessage","getUrlInfo","sentId","rememberSentId","push","add","evict","shift","delete","typing","sendPresenceUpdate","presenceSubscribe","resetAuth","listChats","out","values","sort","a","b","ta","last_message_at","tb","localeCompare","groupFetchAllParticipating","groups","g","Object","subject","lookupChat","onWhatsApp","digits","normalizePhoneDigits","results","hit","find","exists","jid","rawMessage","conversation","extendedTextMessage","imageMessage","caption","videoMessage","documentMessage","download","buf","downloadMediaMessage","reuploadRequest","updateMediaMessage","bind","messageId","spill","mime","saved","sizeKb","round","sanitizeMediaType","mimetype","data","split","stickerMessage","audioMessage","isVoice","ptt","fileName","title","locationMessage","degreesLatitude","degreesLongitude","address","parts","liveLocationMessage","contactMessage","displayName","vcard","contactsArrayMessage","names","map","nextName","nextTs","set","noop","self","level","trace","debug","info","fatal","child","MEDIA_TYPE_ALLOWLIST","image","audio","video","document","base","toLowerCase","test","depth","ephemeralMessage","viewOnceMessage","viewOnceMessageV2","viewOnceMessageV2Extension","documentWithCaptionMessage","primary","alt","isRoutable","j","at","indexOf","host","normHost","getOrCreateGlobal","adapters","started","makeAdapter","wireAdapter","startBridge","row","stopBridge","restartBridge","repairBridge","tmp","getBridgeRuntimeStatus","isBridgeRunning","listBridgeChats","lookupBridgeChat","startAllBridges","rows","stopAllBridges","ids","Array","from","keys","Promise","allSettled"],"sourceRoot":"","ignoreList":[]}