@agent-native/toolkit 0.5.1 → 0.6.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 (177) hide show
  1. package/dist/composer/AgentComposerFrame.d.ts +19 -0
  2. package/dist/composer/AgentComposerFrame.d.ts.map +1 -0
  3. package/dist/composer/AgentComposerFrame.js +14 -0
  4. package/dist/composer/AgentComposerFrame.js.map +1 -0
  5. package/dist/composer/ComposerPlusMenu.d.ts +17 -0
  6. package/dist/composer/ComposerPlusMenu.d.ts.map +1 -0
  7. package/dist/composer/ComposerPlusMenu.js +501 -0
  8. package/dist/composer/ComposerPlusMenu.js.map +1 -0
  9. package/dist/composer/MentionPopover.d.ts +29 -0
  10. package/dist/composer/MentionPopover.d.ts.map +1 -0
  11. package/dist/composer/MentionPopover.js +180 -0
  12. package/dist/composer/MentionPopover.js.map +1 -0
  13. package/dist/composer/PastedTextChip.d.ts +9 -0
  14. package/dist/composer/PastedTextChip.d.ts.map +1 -0
  15. package/dist/composer/PastedTextChip.js +47 -0
  16. package/dist/composer/PastedTextChip.js.map +1 -0
  17. package/dist/composer/PromptComposer.d.ts +114 -0
  18. package/dist/composer/PromptComposer.d.ts.map +1 -0
  19. package/dist/composer/PromptComposer.js +302 -0
  20. package/dist/composer/PromptComposer.js.map +1 -0
  21. package/dist/composer/PromptComposer.spec.d.ts +2 -0
  22. package/dist/composer/PromptComposer.spec.d.ts.map +1 -0
  23. package/dist/composer/PromptComposer.spec.js +64 -0
  24. package/dist/composer/PromptComposer.spec.js.map +1 -0
  25. package/dist/composer/RealtimeVoiceMode.d.ts +104 -0
  26. package/dist/composer/RealtimeVoiceMode.d.ts.map +1 -0
  27. package/dist/composer/RealtimeVoiceMode.js +245 -0
  28. package/dist/composer/RealtimeVoiceMode.js.map +1 -0
  29. package/dist/composer/RealtimeVoiceMode.spec.d.ts +2 -0
  30. package/dist/composer/RealtimeVoiceMode.spec.d.ts.map +1 -0
  31. package/dist/composer/RealtimeVoiceMode.spec.js +439 -0
  32. package/dist/composer/RealtimeVoiceMode.spec.js.map +1 -0
  33. package/dist/composer/TiptapComposer.d.ts +221 -0
  34. package/dist/composer/TiptapComposer.d.ts.map +1 -0
  35. package/dist/composer/TiptapComposer.js +1838 -0
  36. package/dist/composer/TiptapComposer.js.map +1 -0
  37. package/dist/composer/TiptapComposer.spec.d.ts +2 -0
  38. package/dist/composer/TiptapComposer.spec.d.ts.map +1 -0
  39. package/dist/composer/TiptapComposer.spec.js +277 -0
  40. package/dist/composer/TiptapComposer.spec.js.map +1 -0
  41. package/dist/composer/VoiceButton.d.ts +25 -0
  42. package/dist/composer/VoiceButton.d.ts.map +1 -0
  43. package/dist/composer/VoiceButton.js +100 -0
  44. package/dist/composer/VoiceButton.js.map +1 -0
  45. package/dist/composer/VoiceButton.spec.d.ts +2 -0
  46. package/dist/composer/VoiceButton.spec.d.ts.map +1 -0
  47. package/dist/composer/VoiceButton.spec.js +19 -0
  48. package/dist/composer/VoiceButton.spec.js.map +1 -0
  49. package/dist/composer/asset-picker-url.d.ts +16 -0
  50. package/dist/composer/asset-picker-url.d.ts.map +1 -0
  51. package/dist/composer/asset-picker-url.js +64 -0
  52. package/dist/composer/asset-picker-url.js.map +1 -0
  53. package/dist/composer/asset-picker-url.spec.d.ts +2 -0
  54. package/dist/composer/asset-picker-url.spec.d.ts.map +1 -0
  55. package/dist/composer/asset-picker-url.spec.js +23 -0
  56. package/dist/composer/asset-picker-url.spec.js.map +1 -0
  57. package/dist/composer/attachment-accept.d.ts +9 -0
  58. package/dist/composer/attachment-accept.d.ts.map +1 -0
  59. package/dist/composer/attachment-accept.js +65 -0
  60. package/dist/composer/attachment-accept.js.map +1 -0
  61. package/dist/composer/attachment-accept.spec.d.ts +2 -0
  62. package/dist/composer/attachment-accept.spec.d.ts.map +1 -0
  63. package/dist/composer/attachment-accept.spec.js +15 -0
  64. package/dist/composer/attachment-accept.spec.js.map +1 -0
  65. package/dist/composer/draft-key.d.ts +2 -0
  66. package/dist/composer/draft-key.d.ts.map +1 -0
  67. package/dist/composer/draft-key.js +8 -0
  68. package/dist/composer/draft-key.js.map +1 -0
  69. package/dist/composer/draft-key.spec.d.ts +2 -0
  70. package/dist/composer/draft-key.spec.d.ts.map +1 -0
  71. package/dist/composer/draft-key.spec.js +13 -0
  72. package/dist/composer/draft-key.spec.js.map +1 -0
  73. package/dist/composer/extensions/FileReference.d.ts +3 -0
  74. package/dist/composer/extensions/FileReference.d.ts.map +1 -0
  75. package/dist/composer/extensions/FileReference.js +36 -0
  76. package/dist/composer/extensions/FileReference.js.map +1 -0
  77. package/dist/composer/extensions/MentionReference.d.ts +3 -0
  78. package/dist/composer/extensions/MentionReference.d.ts.map +1 -0
  79. package/dist/composer/extensions/MentionReference.js +66 -0
  80. package/dist/composer/extensions/MentionReference.js.map +1 -0
  81. package/dist/composer/extensions/SkillReference.d.ts +3 -0
  82. package/dist/composer/extensions/SkillReference.d.ts.map +1 -0
  83. package/dist/composer/extensions/SkillReference.js +40 -0
  84. package/dist/composer/extensions/SkillReference.js.map +1 -0
  85. package/dist/composer/index.d.ts +14 -0
  86. package/dist/composer/index.d.ts.map +1 -0
  87. package/dist/composer/index.js +13 -0
  88. package/dist/composer/index.js.map +1 -0
  89. package/dist/composer/pasted-text.d.ts +31 -0
  90. package/dist/composer/pasted-text.d.ts.map +1 -0
  91. package/dist/composer/pasted-text.js +130 -0
  92. package/dist/composer/pasted-text.js.map +1 -0
  93. package/dist/composer/pasted-text.spec.d.ts +2 -0
  94. package/dist/composer/pasted-text.spec.d.ts.map +1 -0
  95. package/dist/composer/pasted-text.spec.js +107 -0
  96. package/dist/composer/pasted-text.spec.js.map +1 -0
  97. package/dist/composer/prompt-attachments.d.ts +18 -0
  98. package/dist/composer/prompt-attachments.d.ts.map +1 -0
  99. package/dist/composer/prompt-attachments.js +65 -0
  100. package/dist/composer/prompt-attachments.js.map +1 -0
  101. package/dist/composer/prompt-attachments.spec.d.ts +2 -0
  102. package/dist/composer/prompt-attachments.spec.d.ts.map +1 -0
  103. package/dist/composer/prompt-attachments.spec.js +63 -0
  104. package/dist/composer/prompt-attachments.spec.js.map +1 -0
  105. package/dist/composer/realtime-voice-audio-level.d.ts +14 -0
  106. package/dist/composer/realtime-voice-audio-level.d.ts.map +1 -0
  107. package/dist/composer/realtime-voice-audio-level.js +47 -0
  108. package/dist/composer/realtime-voice-audio-level.js.map +1 -0
  109. package/dist/composer/realtime-voice-audio-level.spec.d.ts +2 -0
  110. package/dist/composer/realtime-voice-audio-level.spec.d.ts.map +1 -0
  111. package/dist/composer/realtime-voice-audio-level.spec.js +25 -0
  112. package/dist/composer/realtime-voice-audio-level.spec.js.map +1 -0
  113. package/dist/composer/realtime-voice-transcript.d.ts +26 -0
  114. package/dist/composer/realtime-voice-transcript.d.ts.map +1 -0
  115. package/dist/composer/realtime-voice-transcript.js +103 -0
  116. package/dist/composer/realtime-voice-transcript.js.map +1 -0
  117. package/dist/composer/realtime-voice-transcript.spec.d.ts +2 -0
  118. package/dist/composer/realtime-voice-transcript.spec.d.ts.map +1 -0
  119. package/dist/composer/realtime-voice-transcript.spec.js +76 -0
  120. package/dist/composer/realtime-voice-transcript.spec.js.map +1 -0
  121. package/dist/composer/runtime-adapters.d.ts +163 -0
  122. package/dist/composer/runtime-adapters.d.ts.map +1 -0
  123. package/dist/composer/runtime-adapters.js +130 -0
  124. package/dist/composer/runtime-adapters.js.map +1 -0
  125. package/dist/composer/types.d.ts +60 -0
  126. package/dist/composer/types.d.ts.map +1 -0
  127. package/dist/composer/types.js +2 -0
  128. package/dist/composer/types.js.map +1 -0
  129. package/dist/composer/use-file-search.d.ts +6 -0
  130. package/dist/composer/use-file-search.d.ts.map +1 -0
  131. package/dist/composer/use-file-search.js +53 -0
  132. package/dist/composer/use-file-search.js.map +1 -0
  133. package/dist/composer/use-mention-search.d.ts +6 -0
  134. package/dist/composer/use-mention-search.d.ts.map +1 -0
  135. package/dist/composer/use-mention-search.js +75 -0
  136. package/dist/composer/use-mention-search.js.map +1 -0
  137. package/dist/composer/use-skills.d.ts +7 -0
  138. package/dist/composer/use-skills.d.ts.map +1 -0
  139. package/dist/composer/use-skills.js +40 -0
  140. package/dist/composer/use-skills.js.map +1 -0
  141. package/dist/composer/useRealtimeVoiceMode.d.ts +179 -0
  142. package/dist/composer/useRealtimeVoiceMode.d.ts.map +1 -0
  143. package/dist/composer/useRealtimeVoiceMode.js +1657 -0
  144. package/dist/composer/useRealtimeVoiceMode.js.map +1 -0
  145. package/dist/composer/useRealtimeVoiceMode.spec.d.ts +2 -0
  146. package/dist/composer/useRealtimeVoiceMode.spec.d.ts.map +1 -0
  147. package/dist/composer/useRealtimeVoiceMode.spec.js +618 -0
  148. package/dist/composer/useRealtimeVoiceMode.spec.js.map +1 -0
  149. package/dist/composer/useVoiceDictation.d.ts +57 -0
  150. package/dist/composer/useVoiceDictation.d.ts.map +1 -0
  151. package/dist/composer/useVoiceDictation.js +1004 -0
  152. package/dist/composer/useVoiceDictation.js.map +1 -0
  153. package/dist/composer/useVoiceDictation.spec.d.ts +2 -0
  154. package/dist/composer/useVoiceDictation.spec.d.ts.map +1 -0
  155. package/dist/composer/useVoiceDictation.spec.js +32 -0
  156. package/dist/composer/useVoiceDictation.spec.js.map +1 -0
  157. package/dist/editor/RegistryBlockContext.d.ts +68 -0
  158. package/dist/editor/RegistryBlockContext.d.ts.map +1 -0
  159. package/dist/editor/RegistryBlockContext.js +10 -0
  160. package/dist/editor/RegistryBlockContext.js.map +1 -0
  161. package/dist/editor/RegistryBlockNode.d.ts +66 -0
  162. package/dist/editor/RegistryBlockNode.d.ts.map +1 -0
  163. package/dist/editor/RegistryBlockNode.js +406 -0
  164. package/dist/editor/RegistryBlockNode.js.map +1 -0
  165. package/dist/editor/RegistryBlockNode.spec.d.ts +2 -0
  166. package/dist/editor/RegistryBlockNode.spec.d.ts.map +1 -0
  167. package/dist/editor/RegistryBlockNode.spec.js +215 -0
  168. package/dist/editor/RegistryBlockNode.spec.js.map +1 -0
  169. package/dist/editor/index.d.ts +2 -0
  170. package/dist/editor/index.d.ts.map +1 -1
  171. package/dist/editor/index.js +2 -0
  172. package/dist/editor/index.js.map +1 -1
  173. package/dist/editor/registrySlashCommands.d.ts +68 -0
  174. package/dist/editor/registrySlashCommands.d.ts.map +1 -0
  175. package/dist/editor/registrySlashCommands.js +51 -0
  176. package/dist/editor/registrySlashCommands.js.map +1 -0
  177. package/package.json +16 -1
@@ -0,0 +1,130 @@
1
+ // Page-sized pastes turn into a `Pasted text` attachment chip instead of being
2
+ // dumped into the editor. Short paragraphs and everyday lists should stay
3
+ // inline so the composer still feels like a normal text field.
4
+ const PASTED_TEXT_MIN_CHARS = 3200;
5
+ const PASTED_TEXT_MIN_LINES = 24;
6
+ const PASTED_TEXT_FILENAME_PREFIX = "pasted-text-";
7
+ // A copied HTML document/source is recognizable from its markup: a closing tag
8
+ // (`</div>`), a doctype, or a common structural/element tag. We key off the
9
+ // *content* the user actually pasted, not the clipboard's `text/html` flavor —
10
+ // editors (VS Code) and rich-text apps (Google Docs) populate `text/html` with
11
+ // syntax-highlight spans or formatting wrappers even when the real content is
12
+ // plain code/prose, so trusting `text/html` blindly would mangle those pastes.
13
+ const HTML_SOURCE_SIGNAL = /<!doctype\s+html|<html[\s>]|<\/[a-z][a-z0-9-]*\s*>|<(?:body|head|div|span|section|main|header|footer|nav|article|aside|ul|ol|li|table|thead|tbody|tr|td|th|h[1-6]|p|a|img|button|input|textarea|select|form|label|script|style|link|meta|svg|canvas|template)\b/i;
14
+ // A real HTML *document* announces itself with a doctype / html / head / body.
15
+ // When one of these is present we keep the HTML classification even if an inline
16
+ // <script> contains JS keywords — it's a genuine page.
17
+ const HTML_DOCUMENT_SIGNAL = /<!doctype\s+html|<html[\s>]|<head[\s>]|<body[\s>]/i;
18
+ // JSX/TSX source contains the same `</div>`/`<span>` tags as an HTML document,
19
+ // so the HTML tag signal alone misfiles a pasted React/TS component (even a bare
20
+ // function component) as an `.html` artifact — the agent then mishandles it as a
21
+ // hostable document instead of source. These markers appear in JS/TS/JSX source
22
+ // but not in a plain HTML *fragment*: `className=` (JSX uses it; HTML uses
23
+ // `class=`), ES module import/export, arrow functions, TS type/React annotations,
24
+ // React hooks, and the basic JS declaration/return keywords that make up a
25
+ // component body.
26
+ const CODE_SOURCE_SIGNAL = /\bclassName=|\bimport\b|\bexport\b|=>|:\s*React\.|\buse[A-Z]\w*\(|\b(?:function|const|let|var|return|class|interface|type|enum)\b/;
27
+ function looksLikeHtml(value) {
28
+ if (!value || !HTML_SOURCE_SIGNAL.test(value))
29
+ return false;
30
+ // A self-announcing HTML document stays HTML even if it embeds a <script>.
31
+ if (HTML_DOCUMENT_SIGNAL.test(value))
32
+ return true;
33
+ // Otherwise it's a bare fragment — if it carries JS/TS/JSX code signals,
34
+ // treat it as source code, not an HTML attachment.
35
+ if (CODE_SOURCE_SIGNAL.test(value))
36
+ return false;
37
+ return true;
38
+ }
39
+ /** Read the relevant clipboard flavors from a paste/drop DataTransfer. */
40
+ export function readClipboardPaste(data) {
41
+ const text = data?.getData("text/plain") ?? "";
42
+ const html = data?.getData("text/html") ?? "";
43
+ return { text, html: html.trim() ? html : undefined };
44
+ }
45
+ // Decide what to actually store for a paste. Preserving HTML markup means a
46
+ // pasted HTML document behaves exactly like uploading that .html file: the agent
47
+ // recognizes it as a hostable artifact and reads it verbatim via
48
+ // `contentFromAttachment` instead of retyping it inline (which cuts off
49
+ // mid-stream on large files and triggers a continuation loop / "spin").
50
+ function selectPasteBody(paste) {
51
+ const plain = paste.text ?? "";
52
+ const html = paste.html ?? "";
53
+ // 1) The pasted text is itself HTML source (copied from an editor, a file, or
54
+ // view-source). The plain text *is* the markup, so keep it as .html.
55
+ if (plain.trim() && looksLikeHtml(plain)) {
56
+ return { body: plain, ext: "html", type: "text/html" };
57
+ }
58
+ // 2) No usable plain text, but a real HTML flavor exists (some apps only
59
+ // expose text/html). Fall back to the markup so nothing is dropped.
60
+ if (!plain.trim() && looksLikeHtml(html)) {
61
+ return { body: html, ext: "html", type: "text/html" };
62
+ }
63
+ // 3) Default: keep the clean plain text. Avoids the syntax-highlight /
64
+ // rich-text noise that lives in text/html when the plain text is already
65
+ // the real content (code from an editor, prose from a doc).
66
+ return { body: plain, ext: "txt", type: "text/plain" };
67
+ }
68
+ export function shouldConvertPasteToAttachment(text) {
69
+ if (!text)
70
+ return false;
71
+ if (text.length >= PASTED_TEXT_MIN_CHARS)
72
+ return true;
73
+ let lines = 1;
74
+ for (let i = 0; i < text.length; i++) {
75
+ if (text.charCodeAt(i) === 10) {
76
+ lines++;
77
+ if (lines >= PASTED_TEXT_MIN_LINES)
78
+ return true;
79
+ }
80
+ }
81
+ return false;
82
+ }
83
+ /**
84
+ * Whether a clipboard paste is large enough to become a `Pasted text`
85
+ * attachment chip. Mirrors `shouldConvertPasteToAttachment` but evaluates the
86
+ * representation we'd actually store, so an HTML-only paste (empty text/plain)
87
+ * still converts on the strength of its markup.
88
+ */
89
+ export function shouldConvertClipboardToAttachment(paste) {
90
+ return shouldConvertPasteToAttachment(selectPasteBody(paste).body);
91
+ }
92
+ function pastedAttachmentName(ext) {
93
+ return `${PASTED_TEXT_FILENAME_PREFIX}${Date.now()}-${Math.random()
94
+ .toString(36)
95
+ .slice(2, 8)}.${ext}`;
96
+ }
97
+ /**
98
+ * Build the attachment File for a page-sized paste, preserving HTML markup when
99
+ * the pasted content is an HTML document so it travels the same rail as an
100
+ * uploaded .html file.
101
+ */
102
+ export function createPastedAttachmentFile(paste) {
103
+ const { body, ext, type } = selectPasteBody(paste);
104
+ return new File([body], pastedAttachmentName(ext), { type });
105
+ }
106
+ /** Back-compat helper for callers that only have plain text. */
107
+ export function createPastedTextFile(text) {
108
+ return createPastedAttachmentFile({ text });
109
+ }
110
+ export function isPastedTextAttachmentName(name) {
111
+ return !!name && name.startsWith(PASTED_TEXT_FILENAME_PREFIX);
112
+ }
113
+ // Strips the `<attachment name=...>\n` / `\n</attachment>` envelope that
114
+ // SimpleTextAttachmentAdapter wraps the file body in when sending. Returns the
115
+ // raw body for previewing.
116
+ export function unwrapAttachmentEnvelope(text) {
117
+ const match = text.match(/^<attachment\b[^>]*>\n([\s\S]*)\n<\/attachment>$/);
118
+ return match ? match[1] : text;
119
+ }
120
+ export function countLines(text) {
121
+ if (!text)
122
+ return 0;
123
+ let lines = 1;
124
+ for (let i = 0; i < text.length; i++) {
125
+ if (text.charCodeAt(i) === 10)
126
+ lines++;
127
+ }
128
+ return lines;
129
+ }
130
+ //# sourceMappingURL=pasted-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pasted-text.js","sourceRoot":"","sources":["../../src/composer/pasted-text.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pasted-text.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pasted-text.spec.d.ts","sourceRoot":"","sources":["../../src/composer/pasted-text.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,107 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createPastedAttachmentFile, isPastedTextAttachmentName, readClipboardPaste, shouldConvertClipboardToAttachment, shouldConvertPasteToAttachment, unwrapAttachmentEnvelope, } from "./pasted-text.js";
3
+ function clipboard(flavors) {
4
+ return { getData: (type) => flavors[type] ?? "" };
5
+ }
6
+ describe("shouldConvertPasteToAttachment", () => {
7
+ it("keeps a paragraph or two inline", () => {
8
+ const twoParagraphs = [
9
+ "This is a normal paragraph pasted into the composer. ".repeat(16),
10
+ "This is a second paragraph with enough words to feel real, but it is nowhere near a page of text. ".repeat(10),
11
+ ].join("\n\n");
12
+ expect(shouldConvertPasteToAttachment(twoParagraphs)).toBe(false);
13
+ });
14
+ it("keeps a basic bulleted list inline", () => {
15
+ const list = [
16
+ "- Confirm project scope",
17
+ "- Review current designs",
18
+ "- Check the edge cases",
19
+ "- Share implementation notes",
20
+ "- Follow up with QA",
21
+ "- Update release notes",
22
+ "- Coordinate rollout",
23
+ "- Watch for support reports",
24
+ ].join("\n");
25
+ expect(shouldConvertPasteToAttachment(list)).toBe(false);
26
+ });
27
+ it("converts roughly page-sized prose to an attachment", () => {
28
+ const pageOfText = "This is source material for the agent to review. ".repeat(72);
29
+ expect(shouldConvertPasteToAttachment(pageOfText)).toBe(true);
30
+ });
31
+ it("converts page-length line-oriented text to an attachment", () => {
32
+ const pageOfLines = Array.from({ length: 24 }, (_, index) => `${index + 1}. A full-page outline item with supporting detail.`).join("\n");
33
+ expect(shouldConvertPasteToAttachment(pageOfLines)).toBe(true);
34
+ });
35
+ it("unwraps assistant-ui text attachment envelopes with attributes", () => {
36
+ expect(unwrapAttachmentEnvelope('<attachment name="notes.txt" contentType="text/plain">\nLine one\nLine two\n</attachment>')).toBe("Line one\nLine two");
37
+ });
38
+ });
39
+ describe("readClipboardPaste", () => {
40
+ it("returns both flavors when present", () => {
41
+ const paste = readClipboardPaste(clipboard({ "text/plain": "hello", "text/html": "<p>hello</p>" }));
42
+ expect(paste.text).toBe("hello");
43
+ expect(paste.html).toBe("<p>hello</p>");
44
+ });
45
+ it("omits an empty html flavor", () => {
46
+ const paste = readClipboardPaste(clipboard({ "text/plain": "hello", "text/html": " " }));
47
+ expect(paste.html).toBeUndefined();
48
+ });
49
+ it("tolerates a missing DataTransfer", () => {
50
+ const paste = readClipboardPaste(null);
51
+ expect(paste.text).toBe("");
52
+ expect(paste.html).toBeUndefined();
53
+ });
54
+ });
55
+ const pageOfHtml = `<!doctype html>\n<html><body>${"<div>Product narrative section.</div>\n".repeat(80)}</body></html>`;
56
+ describe("createPastedAttachmentFile", () => {
57
+ it("stores pasted HTML source as a real .html attachment", () => {
58
+ const paste = { text: pageOfHtml };
59
+ const file = createPastedAttachmentFile(paste);
60
+ expect(file.name).toMatch(/^pasted-text-.*\.html$/);
61
+ expect(file.type).toBe("text/html");
62
+ expect(isPastedTextAttachmentName(file.name)).toBe(true);
63
+ });
64
+ it("keeps plain prose as a .txt attachment (no html flavor)", () => {
65
+ const file = createPastedAttachmentFile({
66
+ text: "This is just a long block of plain prose. ".repeat(80),
67
+ });
68
+ expect(file.name).toMatch(/^pasted-text-.*\.txt$/);
69
+ expect(file.type).toBe("text/plain");
70
+ });
71
+ it("does NOT promote code to html just because text/html carries highlight markup", () => {
72
+ // VS Code / editors put syntax-highlight <span> markup in text/html even
73
+ // though the real content (text/plain) is plain code. Keep it as .txt.
74
+ const code = "const x: number = 1;\n".repeat(200);
75
+ const highlightHtml = `<div style="color:#abb2bf">${"<span>const</span> <span>x</span>\n".repeat(200)}</div>`;
76
+ const file = createPastedAttachmentFile({
77
+ text: code,
78
+ html: highlightHtml,
79
+ });
80
+ expect(file.name).toMatch(/\.txt$/);
81
+ expect(file.type).toBe("text/plain");
82
+ });
83
+ it("falls back to the html flavor when there is no usable plain text", () => {
84
+ const file = createPastedAttachmentFile({ text: " ", html: pageOfHtml });
85
+ expect(file.name).toMatch(/\.html$/);
86
+ expect(file.type).toBe("text/html");
87
+ });
88
+ it("preserves the markup verbatim in the file body", async () => {
89
+ const file = createPastedAttachmentFile({ text: pageOfHtml });
90
+ expect(await file.text()).toBe(pageOfHtml);
91
+ });
92
+ });
93
+ describe("shouldConvertClipboardToAttachment", () => {
94
+ it("converts a page-sized HTML source paste", () => {
95
+ expect(shouldConvertClipboardToAttachment({ text: pageOfHtml })).toBe(true);
96
+ });
97
+ it("converts an html-only paste with an empty plain flavor", () => {
98
+ expect(shouldConvertClipboardToAttachment({ text: "", html: pageOfHtml })).toBe(true);
99
+ });
100
+ it("keeps a short paste inline even if it carries rich html", () => {
101
+ expect(shouldConvertClipboardToAttachment({
102
+ text: "two words",
103
+ html: "<b>two</b> <i>words</i>",
104
+ })).toBe(false);
105
+ });
106
+ });
107
+ //# sourceMappingURL=pasted-text.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pasted-text.spec.js","sourceRoot":"","sources":["../../src/composer/pasted-text.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface AgentPromptAttachment {
2
+ name: string;
3
+ type?: string;
4
+ size?: number;
5
+ text?: string;
6
+ dataUrl?: string;
7
+ }
8
+ export declare function escapePromptAttachmentAttribute(value: string): string;
9
+ export declare function formatPromptWithAttachments(prompt: string, attachments: readonly AgentPromptAttachment[]): string;
10
+ export declare const AGENT_PROMPT_MAX_INLINE_TEXT_CHARS = 60000;
11
+ export declare const AGENT_PROMPT_MAX_INLINE_IMAGE_BYTES: number;
12
+ export interface ReadAgentPromptAttachmentOptions {
13
+ maxInlineTextChars?: number;
14
+ maxInlineImageBytes?: number;
15
+ }
16
+ export declare function readAgentPromptAttachment(file: File, options?: ReadAgentPromptAttachmentOptions): Promise<AgentPromptAttachment>;
17
+ export declare function isInlineableAgentPromptFile(file: File): boolean;
18
+ //# sourceMappingURL=prompt-attachments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-attachments.d.ts","sourceRoot":"","sources":["../../src/composer/prompt-attachments.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,SAAS,qBAAqB,EAAE,GAC5C,MAAM,CAkBR;AAED,eAAO,MAAM,kCAAkC,QAAS,CAAC;AACzD,eAAO,MAAM,mCAAmC,QAAkB,CAAC;AAEnE,MAAM,WAAW,gCAAgC;IAC/C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,gCAAqC,GAC7C,OAAO,CAAC,qBAAqB,CAAC,CA6BhC;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAK/D"}
@@ -0,0 +1,65 @@
1
+ export function escapePromptAttachmentAttribute(value) {
2
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
3
+ }
4
+ export function formatPromptWithAttachments(prompt, attachments) {
5
+ if (attachments.length === 0)
6
+ return prompt;
7
+ const attachmentText = attachments
8
+ .map((attachment) => {
9
+ const size = attachment.size ? ` size="${attachment.size}"` : "";
10
+ const type = attachment.type
11
+ ? ` type="${escapePromptAttachmentAttribute(attachment.type)}"`
12
+ : "";
13
+ if (attachment.dataUrl) {
14
+ return `<attached-image name="${escapePromptAttachmentAttribute(attachment.name)}"${type}${size}>\n${attachment.dataUrl}\n</attached-image>`;
15
+ }
16
+ const body = attachment.text?.trim() ||
17
+ "Selected in the UI. If this file is needed, inspect it from the workspace or ask for a readable copy.";
18
+ return `<attached-file name="${escapePromptAttachmentAttribute(attachment.name)}"${type}${size}>\n${body}\n</attached-file>`;
19
+ })
20
+ .join("\n\n");
21
+ return `${prompt.trimEnd()}\n\nAttached context:\n${attachmentText}`;
22
+ }
23
+ export const AGENT_PROMPT_MAX_INLINE_TEXT_CHARS = 60_000;
24
+ export const AGENT_PROMPT_MAX_INLINE_IMAGE_BYTES = 2 * 1024 * 1024;
25
+ export async function readAgentPromptAttachment(file, options = {}) {
26
+ const maxInlineTextChars = options.maxInlineTextChars ?? AGENT_PROMPT_MAX_INLINE_TEXT_CHARS;
27
+ const maxInlineImageBytes = options.maxInlineImageBytes ?? AGENT_PROMPT_MAX_INLINE_IMAGE_BYTES;
28
+ const attachment = {
29
+ name: file.name,
30
+ type: file.type || undefined,
31
+ size: file.size,
32
+ };
33
+ if (isInlineableAgentPromptFile(file) && file.size <= maxInlineTextChars) {
34
+ try {
35
+ attachment.text = await file.text();
36
+ }
37
+ catch {
38
+ // Keep the filename-only attachment if the browser cannot read it.
39
+ }
40
+ }
41
+ else if (file.type.startsWith("image/") &&
42
+ file.size <= maxInlineImageBytes) {
43
+ try {
44
+ attachment.dataUrl = await readFileAsDataUrl(file);
45
+ }
46
+ catch {
47
+ // Keep the filename-only attachment if the browser cannot read it.
48
+ }
49
+ }
50
+ return attachment;
51
+ }
52
+ export function isInlineableAgentPromptFile(file) {
53
+ if (file.type.startsWith("text/"))
54
+ return true;
55
+ return /\.(cjs|css|csv|html|js|json|jsx|md|mdx|mjs|sql|tsx?|txt|xml|yaml|yml)$/i.test(file.name);
56
+ }
57
+ function readFileAsDataUrl(file) {
58
+ return new Promise((resolve, reject) => {
59
+ const reader = new FileReader();
60
+ reader.onload = () => resolve(String(reader.result ?? ""));
61
+ reader.onerror = () => reject(reader.error ?? new Error("Could not read file"));
62
+ reader.readAsDataURL(file);
63
+ });
64
+ }
65
+ //# sourceMappingURL=prompt-attachments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-attachments.js","sourceRoot":"","sources":["../../src/composer/prompt-attachments.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=prompt-attachments.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-attachments.spec.d.ts","sourceRoot":"","sources":["../../src/composer/prompt-attachments.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,63 @@
1
+ // @vitest-environment happy-dom
2
+ import { describe, expect, it } from "vitest";
3
+ import { escapePromptAttachmentAttribute, formatPromptWithAttachments, isInlineableAgentPromptFile, readAgentPromptAttachment, } from "./prompt-attachments.js";
4
+ describe("prompt attachment helpers", () => {
5
+ it("inlines readable text files up to the configured limit", async () => {
6
+ const file = new File(["hello"], "notes.md", {
7
+ type: "text/markdown",
8
+ });
9
+ const attachment = await readAgentPromptAttachment(file);
10
+ expect(isInlineableAgentPromptFile(file)).toBe(true);
11
+ expect(attachment).toEqual({
12
+ name: "notes.md",
13
+ type: "text/markdown",
14
+ size: 5,
15
+ text: "hello",
16
+ });
17
+ });
18
+ it("falls back to filename metadata for oversized text files", async () => {
19
+ const file = new File(["hello"], "notes.md", {
20
+ type: "text/markdown",
21
+ });
22
+ const attachment = await readAgentPromptAttachment(file, {
23
+ maxInlineTextChars: 2,
24
+ });
25
+ expect(attachment).toEqual({
26
+ name: "notes.md",
27
+ type: "text/markdown",
28
+ size: 5,
29
+ });
30
+ });
31
+ it("inlines small image files as data URLs", async () => {
32
+ const file = new File(["fake image"], "screenshot.png", {
33
+ type: "image/png",
34
+ });
35
+ const attachment = await readAgentPromptAttachment(file);
36
+ expect(attachment.name).toBe("screenshot.png");
37
+ expect(attachment.type).toBe("image/png");
38
+ expect(attachment.dataUrl).toContain("data:image/png;base64,");
39
+ });
40
+ it("formats attachments with escaped XML attributes", () => {
41
+ const formatted = formatPromptWithAttachments("Review this", [
42
+ {
43
+ name: 'bad"name&.ts',
44
+ type: "text/plain",
45
+ size: 12,
46
+ text: "const x = 1;",
47
+ },
48
+ {
49
+ name: "shot.png",
50
+ type: "image/png",
51
+ size: 3,
52
+ dataUrl: "data:image/png;base64,abc",
53
+ },
54
+ ]);
55
+ expect(escapePromptAttachmentAttribute('a&"b')).toBe("a&amp;&quot;b");
56
+ expect(formatted).toContain("Attached context:");
57
+ expect(formatted).toContain('name="bad&quot;name&amp;.ts"');
58
+ expect(formatted).toContain("<attached-file");
59
+ expect(formatted).toContain("<attached-image");
60
+ expect(formatted).toContain("data:image/png;base64,abc");
61
+ });
62
+ });
63
+ //# sourceMappingURL=prompt-attachments.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-attachments.spec.js","sourceRoot":"","sources":["../../src/composer/prompt-attachments.spec.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAEhC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EACL,+BAA+B,EAC/B,2BAA2B,EAC3B,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,yBAAyB,CAAC;AAEjC,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,UAAU,EAAE;YAC3C,IAAI,EAAE,eAAe;SACtB,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,MAAM,yBAAyB,CAAC,IAAI,CAAC,CAAC;QAEzD,MAAM,CAAC,2BAA2B,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrD,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC;YACzB,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,CAAC;YACP,IAAI,EAAE,OAAO;SACd,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACxE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,UAAU,EAAE;YAC3C,IAAI,EAAE,eAAe;SACtB,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,MAAM,yBAAyB,CAAC,IAAI,EAAE;YACvD,kBAAkB,EAAE,CAAC;SACtB,CAAC,CAAC;QAEH,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC;YACzB,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,CAAC;SACR,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,YAAY,CAAC,EAAE,gBAAgB,EAAE;YACtD,IAAI,EAAE,WAAW;SAClB,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,MAAM,yBAAyB,CAAC,IAAI,CAAC,CAAC;QAEzD,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC/C,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC1C,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,SAAS,GAAG,2BAA2B,CAAC,aAAa,EAAE;YAC3D;gBACE,IAAI,EAAE,cAAc;gBACpB,IAAI,EAAE,YAAY;gBAClB,IAAI,EAAE,EAAE;gBACR,IAAI,EAAE,cAAc;aACrB;YACD;gBACE,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,WAAW;gBACjB,IAAI,EAAE,CAAC;gBACP,OAAO,EAAE,2BAA2B;aACrC;SACF,CAAC,CAAC;QAEH,MAAM,CAAC,+BAA+B,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACtE,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QACjD,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;QAC5D,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC9C,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC/C,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface RealtimeVoiceAudioLevels {
2
+ input: number;
3
+ output: number;
4
+ }
5
+ export interface RealtimeVoiceAudioLevelStore {
6
+ getSnapshot: () => RealtimeVoiceAudioLevels;
7
+ subscribe: (listener: () => void) => () => void;
8
+ set: (levels: RealtimeVoiceAudioLevels) => void;
9
+ reset: () => void;
10
+ }
11
+ export declare function normalizeRealtimeVoiceRms(samples: Uint8Array): number;
12
+ export declare function smoothRealtimeVoiceLevel(previous: number, next: number): number;
13
+ export declare function createRealtimeVoiceAudioLevelStore(): RealtimeVoiceAudioLevelStore;
14
+ //# sourceMappingURL=realtime-voice-audio-level.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-voice-audio-level.d.ts","sourceRoot":"","sources":["../../src/composer/realtime-voice-audio-level.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,MAAM,wBAAwB,CAAC;IAC5C,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;IAChD,GAAG,EAAE,CAAC,MAAM,EAAE,wBAAwB,KAAK,IAAI,CAAC;IAChD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AASD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CASrE;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,MAAM,CAIR;AAED,wBAAgB,kCAAkC,IAAI,4BAA4B,CAwBjF"}
@@ -0,0 +1,47 @@
1
+ const SILENT_LEVELS = { input: 0, output: 0 };
2
+ function clampLevel(value) {
3
+ if (!Number.isFinite(value))
4
+ return 0;
5
+ return Math.max(0, Math.min(1, value));
6
+ }
7
+ export function normalizeRealtimeVoiceRms(samples) {
8
+ if (samples.length === 0)
9
+ return 0;
10
+ let sumSquares = 0;
11
+ for (const sample of samples) {
12
+ const normalized = (sample - 128) / 128;
13
+ sumSquares += normalized * normalized;
14
+ }
15
+ const rms = Math.sqrt(sumSquares / samples.length);
16
+ return clampLevel(Math.max(0, rms - 0.012) * 3.2);
17
+ }
18
+ export function smoothRealtimeVoiceLevel(previous, next) {
19
+ const target = clampLevel(next);
20
+ const factor = target > previous ? 0.55 : 0.2;
21
+ return clampLevel(previous + (target - previous) * factor);
22
+ }
23
+ export function createRealtimeVoiceAudioLevelStore() {
24
+ let snapshot = SILENT_LEVELS;
25
+ const listeners = new Set();
26
+ const set = (levels) => {
27
+ const next = {
28
+ input: clampLevel(levels.input),
29
+ output: clampLevel(levels.output),
30
+ };
31
+ if (next.input === snapshot.input && next.output === snapshot.output)
32
+ return;
33
+ snapshot = next;
34
+ for (const listener of listeners)
35
+ listener();
36
+ };
37
+ return {
38
+ getSnapshot: () => snapshot,
39
+ subscribe(listener) {
40
+ listeners.add(listener);
41
+ return () => listeners.delete(listener);
42
+ },
43
+ set,
44
+ reset: () => set(SILENT_LEVELS),
45
+ };
46
+ }
47
+ //# sourceMappingURL=realtime-voice-audio-level.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-voice-audio-level.js","sourceRoot":"","sources":["../../src/composer/realtime-voice-audio-level.ts"],"names":[],"mappings":"AAYA,MAAM,aAAa,GAA6B,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;AAExE,SAAS,UAAU,CAAC,KAAa;IAC/B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACtC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,OAAmB;IAC3D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACnC,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,UAAU,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;QACxC,UAAU,IAAI,UAAU,GAAG,UAAU,CAAC;IACxC,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnD,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,QAAgB,EAChB,IAAY;IAEZ,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;IAC9C,OAAO,UAAU,CAAC,QAAQ,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC,GAAG,MAAM,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,kCAAkC;IAChD,IAAI,QAAQ,GAAG,aAAa,CAAC;IAC7B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;IAExC,MAAM,GAAG,GAAG,CAAC,MAAgC,EAAE,EAAE;QAC/C,MAAM,IAAI,GAAG;YACX,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC;YAC/B,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC;SAClC,CAAC;QACF,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;YAClE,OAAO;QACT,QAAQ,GAAG,IAAI,CAAC;QAChB,KAAK,MAAM,QAAQ,IAAI,SAAS;YAAE,QAAQ,EAAE,CAAC;IAC/C,CAAC,CAAC;IAEF,OAAO;QACL,WAAW,EAAE,GAAG,EAAE,CAAC,QAAQ;QAC3B,SAAS,CAAC,QAAQ;YAChB,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACxB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QACD,GAAG;QACH,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,aAAa,CAAC;KAChC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=realtime-voice-audio-level.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-voice-audio-level.spec.d.ts","sourceRoot":"","sources":["../../src/composer/realtime-voice-audio-level.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,25 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { createRealtimeVoiceAudioLevelStore, normalizeRealtimeVoiceRms, smoothRealtimeVoiceLevel, } from "./realtime-voice-audio-level.js";
3
+ describe("realtime voice audio levels", () => {
4
+ it("normalizes silence and clamps loud audio", () => {
5
+ expect(normalizeRealtimeVoiceRms(new Uint8Array(32).fill(128))).toBe(0);
6
+ expect(normalizeRealtimeVoiceRms(new Uint8Array(32).fill(255))).toBe(1);
7
+ });
8
+ it("attacks faster than it decays", () => {
9
+ expect(smoothRealtimeVoiceLevel(0, 1)).toBe(0.55);
10
+ expect(smoothRealtimeVoiceLevel(1, 0)).toBe(0.8);
11
+ });
12
+ it("notifies only on changes and resets both channels", () => {
13
+ const store = createRealtimeVoiceAudioLevelStore();
14
+ const listener = vi.fn();
15
+ store.subscribe(listener);
16
+ store.set({ input: 0.5, output: 0.25 });
17
+ store.set({ input: 0.5, output: 0.25 });
18
+ expect(listener).toHaveBeenCalledOnce();
19
+ expect(store.getSnapshot()).toEqual({ input: 0.5, output: 0.25 });
20
+ store.reset();
21
+ expect(store.getSnapshot()).toEqual({ input: 0, output: 0 });
22
+ expect(listener).toHaveBeenCalledTimes(2);
23
+ });
24
+ });
25
+ //# sourceMappingURL=realtime-voice-audio-level.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-voice-audio-level.spec.js","sourceRoot":"","sources":["../../src/composer/realtime-voice-audio-level.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAElD,OAAO,EACL,kCAAkC,EAClC,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,iCAAiC,CAAC;AAEzC,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC3C,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,yBAAyB,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxE,MAAM,CAAC,yBAAyB,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,wBAAwB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,CAAC,wBAAwB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,KAAK,GAAG,kCAAkC,EAAE,CAAC;QACnD,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACzB,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC1B,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,EAAE,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7D,MAAM,CAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,26 @@
1
+ export type RealtimeVoiceTranscriptRole = "user" | "assistant";
2
+ export interface RealtimeVoiceTranscriptMessage {
3
+ id: string;
4
+ threadId: string;
5
+ role: RealtimeVoiceTranscriptRole;
6
+ text: string;
7
+ createdAt: string;
8
+ }
9
+ export interface RealtimeVoiceTranscriptSink {
10
+ threadId: string;
11
+ active: boolean;
12
+ append: (message: RealtimeVoiceTranscriptMessage) => boolean;
13
+ }
14
+ export interface RealtimeVoiceTranscriptRegistry {
15
+ activeThreadId: () => string | undefined;
16
+ publish: (message: RealtimeVoiceTranscriptMessage) => void;
17
+ register: (sink: RealtimeVoiceTranscriptSink) => () => void;
18
+ pendingCount: () => number;
19
+ }
20
+ export declare function appendRealtimeVoiceTranscriptToRepository(value: unknown, transcript: RealtimeVoiceTranscriptMessage): {
21
+ appended: boolean;
22
+ repository: Record<string, unknown>;
23
+ };
24
+ export declare function createRealtimeVoiceTranscriptRegistry(): RealtimeVoiceTranscriptRegistry;
25
+ export declare const realtimeVoiceTranscriptRegistry: RealtimeVoiceTranscriptRegistry;
26
+ //# sourceMappingURL=realtime-voice-transcript.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-voice-transcript.d.ts","sourceRoot":"","sources":["../../src/composer/realtime-voice-transcript.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,2BAA2B,GAAG,MAAM,GAAG,WAAW,CAAC;AAE/D,MAAM,WAAW,8BAA8B;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,2BAA2B,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,CAAC,OAAO,EAAE,8BAA8B,KAAK,OAAO,CAAC;CAC9D;AAED,MAAM,WAAW,+BAA+B;IAC9C,cAAc,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACzC,OAAO,EAAE,CAAC,OAAO,EAAE,8BAA8B,KAAK,IAAI,CAAC;IAC3D,QAAQ,EAAE,CAAC,IAAI,EAAE,2BAA2B,KAAK,MAAM,IAAI,CAAC;IAC5D,YAAY,EAAE,MAAM,MAAM,CAAC;CAC5B;AAED,wBAAgB,yCAAyC,CACvD,KAAK,EAAE,OAAO,EACd,UAAU,EAAE,8BAA8B,GACzC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CA6C5D;AAED,wBAAgB,qCAAqC,IAAI,+BAA+B,CAyDvF;AAED,eAAO,MAAM,+BAA+B,iCACH,CAAC"}