jinn-cli 0.7.4 → 0.7.6

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 (62) hide show
  1. package/dist/src/connectors/slack/__tests__/format.test.d.ts +2 -0
  2. package/dist/src/connectors/slack/__tests__/format.test.d.ts.map +1 -0
  3. package/dist/src/connectors/slack/__tests__/format.test.js +115 -0
  4. package/dist/src/connectors/slack/__tests__/format.test.js.map +1 -0
  5. package/dist/src/connectors/slack/format.d.ts +7 -0
  6. package/dist/src/connectors/slack/format.d.ts.map +1 -1
  7. package/dist/src/connectors/slack/format.js +33 -3
  8. package/dist/src/connectors/slack/format.js.map +1 -1
  9. package/dist/src/connectors/whatsapp/__tests__/format.test.d.ts +2 -0
  10. package/dist/src/connectors/whatsapp/__tests__/format.test.d.ts.map +1 -0
  11. package/dist/src/connectors/whatsapp/__tests__/format.test.js +70 -0
  12. package/dist/src/connectors/whatsapp/__tests__/format.test.js.map +1 -0
  13. package/dist/src/connectors/whatsapp/format.d.ts +10 -0
  14. package/dist/src/connectors/whatsapp/format.d.ts.map +1 -1
  15. package/dist/src/connectors/whatsapp/format.js +34 -3
  16. package/dist/src/connectors/whatsapp/format.js.map +1 -1
  17. package/dist/src/gateway/api.d.ts.map +1 -1
  18. package/dist/src/gateway/api.js +35 -6
  19. package/dist/src/gateway/api.js.map +1 -1
  20. package/dist/src/gateway/files.d.ts.map +1 -1
  21. package/dist/src/gateway/files.js +9 -1
  22. package/dist/src/gateway/files.js.map +1 -1
  23. package/dist/web/404.html +1 -1
  24. package/dist/web/_next/static/chunks/155-5843f8840f40f6f8.js +1 -0
  25. package/dist/web/_next/static/chunks/192-f566317c5713c743.js +1 -0
  26. package/dist/web/_next/static/chunks/660-7b281827cf68988a.js +1 -0
  27. package/dist/web/_next/static/chunks/943.c30215b2dbec402b.js +1 -0
  28. package/dist/web/_next/static/chunks/app/chat/page-b111adcdff7746b0.js +1 -0
  29. package/dist/web/_next/static/chunks/app/org/page-b14614ed22c7a80d.js +1 -0
  30. package/dist/web/_next/static/chunks/app/settings/page-c2b014fb0706aa88.js +1 -0
  31. package/dist/web/_next/static/chunks/c967e59e-8414f3fe827ed369.js +1 -0
  32. package/dist/web/_next/static/chunks/{webpack-e7350efbcf65db9c.js → webpack-2e375360ad2078fe.js} +1 -1
  33. package/dist/web/_next/static/css/a2080d6b6696080a.css +1 -0
  34. package/dist/web/chat.html +1 -1
  35. package/dist/web/chat.txt +4 -4
  36. package/dist/web/cron.html +1 -1
  37. package/dist/web/cron.txt +4 -4
  38. package/dist/web/index.html +1 -1
  39. package/dist/web/index.txt +4 -4
  40. package/dist/web/kanban.html +1 -1
  41. package/dist/web/kanban.txt +4 -4
  42. package/dist/web/logs.html +2 -2
  43. package/dist/web/logs.txt +4 -4
  44. package/dist/web/org.html +1 -1
  45. package/dist/web/org.txt +4 -4
  46. package/dist/web/sessions.html +1 -1
  47. package/dist/web/sessions.txt +3 -3
  48. package/dist/web/settings.html +1 -1
  49. package/dist/web/settings.txt +4 -4
  50. package/dist/web/skills.html +1 -1
  51. package/dist/web/skills.txt +4 -4
  52. package/package.json +1 -1
  53. package/dist/web/_next/static/chunks/155-277bf3022d16e74f.js +0 -1
  54. package/dist/web/_next/static/chunks/192-28e0cb7a172d804d.js +0 -1
  55. package/dist/web/_next/static/chunks/865-b4eb9a132b937321.js +0 -1
  56. package/dist/web/_next/static/chunks/943.1c6d37432bcad8e8.js +0 -1
  57. package/dist/web/_next/static/chunks/app/chat/page-d9b12d7effe66c59.js +0 -1
  58. package/dist/web/_next/static/chunks/app/org/page-e51ef93389a23de8.js +0 -1
  59. package/dist/web/_next/static/chunks/app/settings/page-8d1048b944663524.js +0 -1
  60. package/dist/web/_next/static/css/92635e77ae2cd8fd.css +0 -1
  61. /package/dist/web/_next/static/{er4Sne2nYT8t4rNVzckgy → ec5FmQvpr0g4TGeSgU_z_}/_buildManifest.js +0 -0
  62. /package/dist/web/_next/static/{er4Sne2nYT8t4rNVzckgy → ec5FmQvpr0g4TGeSgU_z_}/_ssgManifest.js +0 -0
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=format.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.test.d.ts","sourceRoot":"","sources":["../../../../../src/connectors/slack/__tests__/format.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,115 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { markdownToSlackMrkdwn, formatResponse } from "../format.js";
3
+ describe("markdownToSlackMrkdwn", () => {
4
+ describe("headings", () => {
5
+ it("converts ## headings to bold on own line", () => {
6
+ expect(markdownToSlackMrkdwn("## My Heading")).toBe("*My Heading*");
7
+ });
8
+ it("converts ### headings to bold", () => {
9
+ expect(markdownToSlackMrkdwn("### Sub Heading")).toBe("*Sub Heading*");
10
+ });
11
+ it("converts # h1 to bold", () => {
12
+ expect(markdownToSlackMrkdwn("# Title")).toBe("*Title*");
13
+ });
14
+ it("converts headings with up to 6 levels", () => {
15
+ expect(markdownToSlackMrkdwn("###### Deep")).toBe("*Deep*");
16
+ });
17
+ it("only converts headings at start of line", () => {
18
+ expect(markdownToSlackMrkdwn("not a ## heading")).toBe("not a ## heading");
19
+ });
20
+ });
21
+ describe("bold", () => {
22
+ it("converts **bold** to *bold*", () => {
23
+ expect(markdownToSlackMrkdwn("this is **bold** text")).toBe("this is *bold* text");
24
+ });
25
+ it("converts __bold__ to *bold*", () => {
26
+ expect(markdownToSlackMrkdwn("this is __bold__ text")).toBe("this is *bold* text");
27
+ });
28
+ it("handles multiple bold segments", () => {
29
+ expect(markdownToSlackMrkdwn("**a** and **b**")).toBe("*a* and *b*");
30
+ });
31
+ });
32
+ describe("italic", () => {
33
+ it("converts _italic_ to _italic_ (passthrough)", () => {
34
+ expect(markdownToSlackMrkdwn("this is _italic_ text")).toBe("this is _italic_ text");
35
+ });
36
+ });
37
+ describe("strikethrough", () => {
38
+ it("converts ~~strike~~ to ~strike~", () => {
39
+ expect(markdownToSlackMrkdwn("this is ~~struck~~ out")).toBe("this is ~struck~ out");
40
+ });
41
+ });
42
+ describe("links", () => {
43
+ it("converts [text](url) to <url|text>", () => {
44
+ expect(markdownToSlackMrkdwn("click [here](https://example.com) now")).toBe("click <https://example.com|here> now");
45
+ });
46
+ it("converts multiple links", () => {
47
+ expect(markdownToSlackMrkdwn("[a](http://a.com) and [b](http://b.com)")).toBe("<http://a.com|a> and <http://b.com|b>");
48
+ });
49
+ it("handles bare URLs (no conversion needed)", () => {
50
+ expect(markdownToSlackMrkdwn("visit https://example.com")).toBe("visit https://example.com");
51
+ });
52
+ });
53
+ describe("bullet lists", () => {
54
+ it("converts - item to • item", () => {
55
+ expect(markdownToSlackMrkdwn("- first\n- second")).toBe("• first\n• second");
56
+ });
57
+ it("converts * item to • item", () => {
58
+ expect(markdownToSlackMrkdwn("* first\n* second")).toBe("• first\n• second");
59
+ });
60
+ it("preserves indented sub-items", () => {
61
+ expect(markdownToSlackMrkdwn("- top\n - nested")).toBe("• top\n • nested");
62
+ });
63
+ });
64
+ describe("code (preserved)", () => {
65
+ it("preserves inline code", () => {
66
+ expect(markdownToSlackMrkdwn("use `console.log`")).toBe("use `console.log`");
67
+ });
68
+ it("preserves code blocks", () => {
69
+ const input = "```\nconst x = 1;\n```";
70
+ expect(markdownToSlackMrkdwn(input)).toBe("```\nconst x = 1;\n```");
71
+ });
72
+ it("does not convert markdown inside code blocks", () => {
73
+ const input = "```\n## not a heading\n**not bold**\n```";
74
+ expect(markdownToSlackMrkdwn(input)).toBe("```\n## not a heading\n**not bold**\n```");
75
+ });
76
+ it("does not convert markdown inside inline code", () => {
77
+ expect(markdownToSlackMrkdwn("use `**not bold**`")).toBe("use `**not bold**`");
78
+ });
79
+ });
80
+ describe("blockquotes", () => {
81
+ it("preserves > blockquotes (Slack supports them)", () => {
82
+ expect(markdownToSlackMrkdwn("> quoted text")).toBe("> quoted text");
83
+ });
84
+ });
85
+ describe("numbered lists", () => {
86
+ it("preserves numbered lists as-is", () => {
87
+ expect(markdownToSlackMrkdwn("1. first\n2. second")).toBe("1. first\n2. second");
88
+ });
89
+ });
90
+ describe("complex mixed content", () => {
91
+ it("handles headings + bold + links together", () => {
92
+ const input = "## Summary\n\nThis is **important** and [see docs](https://docs.com).";
93
+ const expected = "*Summary*\n\nThis is *important* and <https://docs.com|see docs>.";
94
+ expect(markdownToSlackMrkdwn(input)).toBe(expected);
95
+ });
96
+ it("handles text between code blocks", () => {
97
+ const input = "Before\n```\ncode\n```\n**after**";
98
+ const expected = "Before\n```\ncode\n```\n*after*";
99
+ expect(markdownToSlackMrkdwn(input)).toBe(expected);
100
+ });
101
+ });
102
+ });
103
+ describe("formatResponse", () => {
104
+ it("applies markdown conversion before chunking", () => {
105
+ const result = formatResponse("## Hello\n\nThis is **bold**.");
106
+ expect(result).toEqual(["*Hello*\n\nThis is *bold*."]);
107
+ });
108
+ it("still chunks long messages after conversion", () => {
109
+ const longText = "## Title\n\n" + "word ".repeat(1000);
110
+ const result = formatResponse(longText);
111
+ expect(result.length).toBeGreaterThan(1);
112
+ expect(result[0].startsWith("*Title*")).toBe(true);
113
+ });
114
+ });
115
+ //# sourceMappingURL=format.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.test.js","sourceRoot":"","sources":["../../../../../src/connectors/slack/__tests__/format.test.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,12 @@
1
+ /**
2
+ * Convert standard markdown to Slack mrkdwn format.
3
+ * Handles headings, bold, strikethrough, links, and bullet lists.
4
+ * Preserves code blocks and inline code untouched.
5
+ */
6
+ export declare function markdownToSlackMrkdwn(text: string): string;
1
7
  /**
2
8
  * Split text into chunks that fit within Slack's message length limit.
9
+ * Converts markdown to Slack mrkdwn format before chunking.
3
10
  */
4
11
  export declare function formatResponse(text: string): string[];
5
12
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../../src/connectors/slack/format.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CA8BrD;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAqBjB"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../../src/connectors/slack/format.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAyB1D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAgCrD;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAqBjB"}
@@ -2,15 +2,45 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { randomUUID } from "node:crypto";
4
4
  const SLACK_MAX_LENGTH = 3000;
5
+ /**
6
+ * Convert standard markdown to Slack mrkdwn format.
7
+ * Handles headings, bold, strikethrough, links, and bullet lists.
8
+ * Preserves code blocks and inline code untouched.
9
+ */
10
+ export function markdownToSlackMrkdwn(text) {
11
+ // Split text into code and non-code segments to protect code from conversion
12
+ const segments = text.split(/(```[\s\S]*?```|`[^`]+`)/g);
13
+ return segments
14
+ .map((segment, i) => {
15
+ // Odd indices are code matches — leave them untouched
16
+ if (i % 2 === 1)
17
+ return segment;
18
+ return (segment
19
+ // Headings: ## text → *text* (must be at start of line)
20
+ .replace(/^(#{1,6})\s+(.+)$/gm, (_match, _hashes, content) => `*${content}*`)
21
+ // Bold: **text** or __text__ → *text*
22
+ .replace(/\*\*(.+?)\*\*/g, "*$1*")
23
+ .replace(/__(.+?)__/g, "*$1*")
24
+ // Strikethrough: ~~text~~ → ~text~
25
+ .replace(/~~(.+?)~~/g, "~$1~")
26
+ // Links: [text](url) → <url|text>
27
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<$2|$1>")
28
+ // Bullet lists: - item or * item → • item (with optional indentation)
29
+ .replace(/^(\s*)[-*]\s+/gm, "$1• "));
30
+ })
31
+ .join("");
32
+ }
5
33
  /**
6
34
  * Split text into chunks that fit within Slack's message length limit.
35
+ * Converts markdown to Slack mrkdwn format before chunking.
7
36
  */
8
37
  export function formatResponse(text) {
9
- if (text.length <= SLACK_MAX_LENGTH) {
10
- return [text];
38
+ const converted = markdownToSlackMrkdwn(text);
39
+ if (converted.length <= SLACK_MAX_LENGTH) {
40
+ return [converted];
11
41
  }
12
42
  const chunks = [];
13
- let remaining = text;
43
+ let remaining = converted;
14
44
  while (remaining.length > 0) {
15
45
  if (remaining.length <= SLACK_MAX_LENGTH) {
16
46
  chunks.push(remaining);
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","sourceRoot":"","sources":["../../../../src/connectors/slack/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,IAAI,CAAC,MAAM,IAAI,gBAAgB,EAAE,CAAC;QACpC,OAAO,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAED,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,SAAS,GAAG,IAAI,CAAC;IAErB,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,IAAI,SAAS,CAAC,MAAM,IAAI,gBAAgB,EAAE,CAAC;YACzC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACvB,MAAM;QACR,CAAC;QAED,sDAAsD;QACtD,IAAI,UAAU,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;QAC/D,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;YACpB,oCAAoC;YACpC,UAAU,GAAG,SAAS,CAAC,WAAW,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;QAC5D,CAAC;QACD,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;YACpB,uCAAuC;YACvC,UAAU,GAAG,gBAAgB,CAAC;QAChC,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;QAC5C,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,SAAS,EAAE,CAAC;IACtD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,GAAW,EACX,KAAa,EACb,OAAe;IAEf,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;KAC9C,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,kCAAkC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAC9F,CAAC;IAED,qEAAqE;IACrE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;IACtC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACxC,MAAM,QAAQ,GAAG,GAAG,UAAU,EAAE,GAAG,GAAG,EAAE,CAAC;IACzC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAE/C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IACzD,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAEpC,OAAO,SAAS,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../../../src/connectors/slack/format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=format.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.test.d.ts","sourceRoot":"","sources":["../../../../../src/connectors/whatsapp/__tests__/format.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,70 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { markdownToWhatsApp, formatResponse } from "../format.js";
3
+ describe("markdownToWhatsApp", () => {
4
+ describe("headings", () => {
5
+ it("converts ## headings to *bold* on own line", () => {
6
+ expect(markdownToWhatsApp("## My Heading")).toBe("*My Heading*");
7
+ });
8
+ it("converts # h1 to bold", () => {
9
+ expect(markdownToWhatsApp("# Title")).toBe("*Title*");
10
+ });
11
+ });
12
+ describe("bold", () => {
13
+ it("converts **bold** to *bold*", () => {
14
+ expect(markdownToWhatsApp("this is **bold** text")).toBe("this is *bold* text");
15
+ });
16
+ it("converts __bold__ to *bold*", () => {
17
+ expect(markdownToWhatsApp("this is __bold__ text")).toBe("this is *bold* text");
18
+ });
19
+ });
20
+ describe("italic", () => {
21
+ it("preserves _italic_ (WhatsApp uses same syntax)", () => {
22
+ expect(markdownToWhatsApp("this is _italic_ text")).toBe("this is _italic_ text");
23
+ });
24
+ });
25
+ describe("strikethrough", () => {
26
+ it("converts ~~strike~~ to ~strike~", () => {
27
+ expect(markdownToWhatsApp("this is ~~struck~~ out")).toBe("this is ~struck~ out");
28
+ });
29
+ });
30
+ describe("links", () => {
31
+ it("converts [text](url) to text (url) since WA auto-links", () => {
32
+ expect(markdownToWhatsApp("click [here](https://example.com)")).toBe("click here (https://example.com)");
33
+ });
34
+ });
35
+ describe("bullet lists", () => {
36
+ it("converts - item to • item", () => {
37
+ expect(markdownToWhatsApp("- first\n- second")).toBe("• first\n• second");
38
+ });
39
+ it("converts * item to • item (not bold)", () => {
40
+ expect(markdownToWhatsApp("* first\n* second")).toBe("• first\n• second");
41
+ });
42
+ });
43
+ describe("code", () => {
44
+ it("preserves inline code", () => {
45
+ expect(markdownToWhatsApp("use `code`")).toBe("use `code`");
46
+ });
47
+ it("preserves code blocks", () => {
48
+ const input = "```\ncode here\n```";
49
+ expect(markdownToWhatsApp(input)).toBe("```\ncode here\n```");
50
+ });
51
+ it("does not convert markdown inside code blocks", () => {
52
+ const input = "```\n## not a heading\n```";
53
+ expect(markdownToWhatsApp(input)).toBe("```\n## not a heading\n```");
54
+ });
55
+ });
56
+ describe("mixed content", () => {
57
+ it("handles headings + bold + links", () => {
58
+ const input = "## Summary\n\nThis is **important** and [docs](https://docs.com).";
59
+ const expected = "*Summary*\n\nThis is *important* and docs (https://docs.com).";
60
+ expect(markdownToWhatsApp(input)).toBe(expected);
61
+ });
62
+ });
63
+ });
64
+ describe("formatResponse", () => {
65
+ it("applies markdown conversion before chunking", () => {
66
+ const result = formatResponse("## Hello\n\n**bold** text");
67
+ expect(result).toEqual(["*Hello*\n\n*bold* text"]);
68
+ });
69
+ });
70
+ //# sourceMappingURL=format.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.test.js","sourceRoot":"","sources":["../../../../../src/connectors/whatsapp/__tests__/format.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAElE,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;QACxB,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;YACpD,MAAM,CAAC,kBAAkB,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;YAC/B,MAAM,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;YACrC,MAAM,CAAC,kBAAkB,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;YACrC,MAAM,CAAC,kBAAkB,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;QACtB,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;YACxD,MAAM,CAAC,kBAAkB,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACpF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;YACzC,MAAM,CAAC,kBAAkB,CAAC,wBAAwB,CAAC,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QACpF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;QACrB,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;YAChE,MAAM,CAAC,kBAAkB,CAAC,mCAAmC,CAAC,CAAC,CAAC,IAAI,CAClE,kCAAkC,CACnC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;YACnC,MAAM,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;YAC9C,MAAM,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;YAC/B,MAAM,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;YAC/B,MAAM,KAAK,GAAG,qBAAqB,CAAC;YACpC,MAAM,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QAChE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,KAAK,GAAG,4BAA4B,CAAC;YAC3C,MAAM,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;QACvE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;YACzC,MAAM,KAAK,GAAG,mEAAmE,CAAC;YAClF,MAAM,QAAQ,GAAG,+DAA+D,CAAC;YACjF,MAAM,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,MAAM,GAAG,cAAc,CAAC,2BAA2B,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,2 +1,12 @@
1
+ /**
2
+ * Convert standard markdown to WhatsApp formatting.
3
+ * WhatsApp supports *bold*, _italic_, ~strikethrough~, ```code```, `code`.
4
+ * It does NOT support headings or hyperlinks, so we convert those.
5
+ */
6
+ export declare function markdownToWhatsApp(text: string): string;
7
+ /**
8
+ * Split text into chunks that fit within WhatsApp's message length limit.
9
+ * Converts markdown to WhatsApp formatting before chunking.
10
+ */
1
11
  export declare function formatResponse(text: string): string[];
2
12
  //# sourceMappingURL=format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../../src/connectors/whatsapp/format.ts"],"names":[],"mappings":"AAEA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAarD"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../../src/connectors/whatsapp/format.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAuBvD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAerD"}
@@ -1,9 +1,40 @@
1
1
  const WA_MAX_LENGTH = 4000;
2
+ /**
3
+ * Convert standard markdown to WhatsApp formatting.
4
+ * WhatsApp supports *bold*, _italic_, ~strikethrough~, ```code```, `code`.
5
+ * It does NOT support headings or hyperlinks, so we convert those.
6
+ */
7
+ export function markdownToWhatsApp(text) {
8
+ const segments = text.split(/(```[\s\S]*?```|`[^`]+`)/g);
9
+ return segments
10
+ .map((segment, i) => {
11
+ if (i % 2 === 1)
12
+ return segment;
13
+ return (segment
14
+ // Headings: ## text → *text* (bold on own line)
15
+ .replace(/^(#{1,6})\s+(.+)$/gm, (_match, _hashes, content) => `*${content}*`)
16
+ // Bold: **text** or __text__ → *text*
17
+ .replace(/\*\*(.+?)\*\*/g, "*$1*")
18
+ .replace(/__(.+?)__/g, "*$1*")
19
+ // Strikethrough: ~~text~~ → ~text~
20
+ .replace(/~~(.+?)~~/g, "~$1~")
21
+ // Links: [text](url) → text (url) — WhatsApp auto-links URLs
22
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)")
23
+ // Bullet lists: - item or * item → • item
24
+ .replace(/^(\s*)[-*]\s+/gm, "$1• "));
25
+ })
26
+ .join("");
27
+ }
28
+ /**
29
+ * Split text into chunks that fit within WhatsApp's message length limit.
30
+ * Converts markdown to WhatsApp formatting before chunking.
31
+ */
2
32
  export function formatResponse(text) {
3
- if (text.length <= WA_MAX_LENGTH)
4
- return [text];
33
+ const converted = markdownToWhatsApp(text);
34
+ if (converted.length <= WA_MAX_LENGTH)
35
+ return [converted];
5
36
  const chunks = [];
6
- let remaining = text;
37
+ let remaining = converted;
7
38
  while (remaining.length > 0) {
8
39
  if (remaining.length <= WA_MAX_LENGTH) {
9
40
  chunks.push(remaining);
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","sourceRoot":"","sources":["../../../../src/connectors/whatsapp/format.ts"],"names":[],"mappings":"AAAA,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,IAAI,CAAC,MAAM,IAAI,aAAa;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,SAAS,GAAG,IAAI,CAAC;IACrB,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,IAAI,SAAS,CAAC,MAAM,IAAI,aAAa,EAAE,CAAC;YAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAAC,MAAM;QAAC,CAAC;QACzE,IAAI,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QACvD,IAAI,KAAK,IAAI,CAAC;YAAE,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAClE,IAAI,KAAK,IAAI,CAAC;YAAE,KAAK,GAAG,aAAa,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;QACvC,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,CAAC;IACjD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../../../src/connectors/whatsapp/format.ts"],"names":[],"mappings":"AAAA,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAEzD,OAAO,QAAQ;SACZ,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE;QAClB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,OAAO,OAAO,CAAC;QAEhC,OAAO,CACL,OAAO;YACL,gDAAgD;aAC/C,OAAO,CAAC,qBAAqB,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,OAAO,GAAG,CAAC;YAC7E,sCAAsC;aACrC,OAAO,CAAC,gBAAgB,EAAE,MAAM,CAAC;aACjC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC;YAC9B,mCAAmC;aAClC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC;YAC9B,6DAA6D;aAC5D,OAAO,CAAC,0BAA0B,EAAE,SAAS,CAAC;YAC/C,0CAA0C;aACzC,OAAO,CAAC,iBAAiB,EAAE,MAAM,CAAC,CACtC,CAAC;IACJ,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,SAAS,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAE3C,IAAI,SAAS,CAAC,MAAM,IAAI,aAAa;QAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,SAAS,GAAG,SAAS,CAAC;IAC1B,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,IAAI,SAAS,CAAC,MAAM,IAAI,aAAa,EAAE,CAAC;YAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAAC,MAAM;QAAC,CAAC;QACzE,IAAI,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QACvD,IAAI,KAAK,IAAI,CAAC;YAAE,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAClE,IAAI,KAAK,IAAI,CAAC;YAAE,KAAK,GAAG,aAAa,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;QACvC,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,CAAC;IACjD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/gateway/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,IAAI,WAAW,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMhF,OAAO,KAAK,EAAoC,UAAU,EAAmB,MAAM,oBAAoB,CAAC;AAExG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AA0C7D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,cAAc,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,UAAU,CAAC;IAC5B,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,oBAAoB,EAAE,SAAS,CAAC,CAAC;CACjE;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,CAgCpE;AAyLD,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CAoxCf"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/gateway/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,IAAI,WAAW,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMhF,OAAO,KAAK,EAAoC,UAAU,EAAmB,MAAM,oBAAoB,CAAC;AAExG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AA4C7D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,cAAc,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,UAAU,CAAC;IAC5B,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,oBAAoB,EAAE,SAAS,CAAC,CAAC;CACjE;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,CAgCpE;AAgND,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CAwxCf"}
@@ -5,8 +5,8 @@ import path from "node:path";
5
5
  import yaml from "js-yaml";
6
6
  import { isInterruptibleEngine } from "../shared/types.js";
7
7
  import { buildContext } from "../sessions/context.js";
8
- import { initDb, listSessions, getSession, createSession, updateSession, deleteSession, deleteSessions, insertMessage, getMessages, enqueueQueueItem, cancelQueueItem, getQueueItems, cancelAllPendingQueueItems, listAllPendingQueueItems, } from "../sessions/registry.js";
9
- import { CONFIG_PATH, CRON_RUNS, ORG_DIR, SKILLS_DIR, LOGS_DIR, TMP_DIR, } from "../shared/paths.js";
8
+ import { initDb, listSessions, getSession, createSession, updateSession, deleteSession, deleteSessions, insertMessage, getMessages, enqueueQueueItem, cancelQueueItem, getQueueItems, cancelAllPendingQueueItems, listAllPendingQueueItems, getFile, } from "../sessions/registry.js";
9
+ import { CONFIG_PATH, CRON_RUNS, ORG_DIR, SKILLS_DIR, LOGS_DIR, TMP_DIR, FILES_DIR, } from "../shared/paths.js";
10
10
  import { logger } from "../shared/logger.js";
11
11
  import { getSttStatus, downloadModel, transcribe as sttTranscribe, resolveLanguages, WHISPER_LANGUAGES } from "../stt/stt.js";
12
12
  import { JINN_HOME } from "../shared/paths.js";
@@ -94,7 +94,7 @@ function dispatchWebSessionRun(session, prompt, engine, config, context, opts) {
94
94
  const run = async () => {
95
95
  await context.sessionManager.getQueue().enqueue(session.sessionKey || session.sourceRef, async () => {
96
96
  context.emit("session:started", { sessionId: session.id });
97
- await runWebSession(session, prompt, engine, config, context);
97
+ await runWebSession(session, prompt, engine, config, context, opts?.attachments);
98
98
  }, opts?.queueItemId);
99
99
  };
100
100
  const launch = () => {
@@ -146,6 +146,32 @@ async function readJsonBody(req, res) {
146
146
  return { ok: false };
147
147
  }
148
148
  }
149
+ /** Resolve an array of file IDs to local filesystem paths for engine consumption. */
150
+ function resolveAttachmentPaths(fileIds) {
151
+ if (!Array.isArray(fileIds))
152
+ return [];
153
+ const paths = [];
154
+ for (const id of fileIds) {
155
+ if (typeof id !== "string" || !id.trim())
156
+ continue;
157
+ const meta = getFile(id);
158
+ if (!meta) {
159
+ logger.warn(`Attachment file not found: ${id}`);
160
+ continue;
161
+ }
162
+ const filePath = path.join(FILES_DIR, meta.id, meta.filename);
163
+ if (fs.existsSync(filePath)) {
164
+ paths.push(filePath);
165
+ }
166
+ else if (meta.path && fs.existsSync(meta.path)) {
167
+ paths.push(meta.path);
168
+ }
169
+ else {
170
+ logger.warn(`Attachment file missing on disk: ${id} (${meta.filename})`);
171
+ }
172
+ }
173
+ return paths;
174
+ }
149
175
  function json(res, data, status = 200) {
150
176
  res.writeHead(status, { "Content-Type": "application/json" });
151
177
  res.end(JSON.stringify(data));
@@ -514,10 +540,11 @@ export async function handleApiRequest(req, res, context) {
514
540
  lastActivity: new Date().toISOString(),
515
541
  });
516
542
  session.status = "running";
543
+ const attachmentPaths = resolveAttachmentPaths(body.attachments);
517
544
  const queueSessionKey = session.sessionKey || session.sourceRef || session.id;
518
545
  const queueItemId = enqueueQueueItem(session.id, queueSessionKey, prompt);
519
546
  context.emit("queue:updated", { sessionId: session.id, sessionKey: queueSessionKey });
520
- dispatchWebSessionRun(session, prompt, engine, config, context, { queueItemId });
547
+ dispatchWebSessionRun(session, prompt, engine, config, context, { queueItemId, attachments: attachmentPaths.length > 0 ? attachmentPaths : undefined });
521
548
  return json(res, serializeSession(session, context), 201);
522
549
  }
523
550
  // POST /api/sessions/:id/message
@@ -585,10 +612,11 @@ export async function handleApiRequest(req, res, context) {
585
612
  }
586
613
  // Clear any pending cancellation so the new message runs normally.
587
614
  context.sessionManager.getQueue().clearCancelled(session.sessionKey || session.sourceRef || session.id);
615
+ const attachmentPaths = resolveAttachmentPaths(body.attachments);
588
616
  const sessionKey = session.sessionKey || session.sourceRef || session.id;
589
617
  const queueItemId = enqueueQueueItem(session.id, sessionKey, prompt);
590
618
  context.emit("queue:updated", { sessionId: session.id, sessionKey });
591
- dispatchWebSessionRun(session, prompt, engine, config, context, { queueItemId });
619
+ dispatchWebSessionRun(session, prompt, engine, config, context, { queueItemId, attachments: attachmentPaths.length > 0 ? attachmentPaths : undefined });
592
620
  return json(res, { status: "queued", sessionId: session.id });
593
621
  }
594
622
  // GET /api/cron
@@ -1622,7 +1650,7 @@ function loadTranscriptMessages(engineSessionId) {
1622
1650
  }
1623
1651
  return [];
1624
1652
  }
1625
- async function runWebSession(session, prompt, engine, config, context) {
1653
+ async function runWebSession(session, prompt, engine, config, context, attachments) {
1626
1654
  const currentSession = getSession(session.id);
1627
1655
  if (!currentSession) {
1628
1656
  logger.info(`Skipping deleted web session ${session.id} before run start`);
@@ -1687,6 +1715,7 @@ async function runWebSession(session, prompt, engine, config, context) {
1687
1715
  model: currentSession.model ?? engineConfig.model,
1688
1716
  effortLevel,
1689
1717
  cliFlags: employee?.cliFlags,
1718
+ attachments: attachments?.length ? attachments : undefined,
1690
1719
  sessionId: currentSession.id,
1691
1720
  onStream: (delta) => {
1692
1721
  const now = Date.now();