@mcptoolshop/voice-soundboard-core 0.1.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 (158) hide show
  1. package/LICENSE +21 -0
  2. package/dist/ambient/emitter.d.ts +34 -0
  3. package/dist/ambient/emitter.d.ts.map +1 -0
  4. package/dist/ambient/emitter.js +125 -0
  5. package/dist/ambient/emitter.js.map +1 -0
  6. package/dist/ambient/index.d.ts +5 -0
  7. package/dist/ambient/index.d.ts.map +1 -0
  8. package/dist/ambient/index.js +5 -0
  9. package/dist/ambient/index.js.map +1 -0
  10. package/dist/ambient/redact.d.ts +11 -0
  11. package/dist/ambient/redact.d.ts.map +1 -0
  12. package/dist/ambient/redact.js +40 -0
  13. package/dist/ambient/redact.js.map +1 -0
  14. package/dist/ambient/types.d.ts +38 -0
  15. package/dist/ambient/types.d.ts.map +1 -0
  16. package/dist/ambient/types.js +8 -0
  17. package/dist/ambient/types.js.map +1 -0
  18. package/dist/artifact.d.ts +37 -0
  19. package/dist/artifact.d.ts.map +1 -0
  20. package/dist/artifact.js +61 -0
  21. package/dist/artifact.js.map +1 -0
  22. package/dist/chunking/chunker.d.ts +15 -0
  23. package/dist/chunking/chunker.d.ts.map +1 -0
  24. package/dist/chunking/chunker.js +156 -0
  25. package/dist/chunking/chunker.js.map +1 -0
  26. package/dist/chunking/index.d.ts +5 -0
  27. package/dist/chunking/index.d.ts.map +1 -0
  28. package/dist/chunking/index.js +4 -0
  29. package/dist/chunking/index.js.map +1 -0
  30. package/dist/chunking/limits.d.ts +12 -0
  31. package/dist/chunking/limits.d.ts.map +1 -0
  32. package/dist/chunking/limits.js +12 -0
  33. package/dist/chunking/limits.js.map +1 -0
  34. package/dist/chunking/types.d.ts +26 -0
  35. package/dist/chunking/types.d.ts.map +1 -0
  36. package/dist/chunking/types.js +3 -0
  37. package/dist/chunking/types.js.map +1 -0
  38. package/dist/dialogue/index.d.ts +4 -0
  39. package/dist/dialogue/index.d.ts.map +1 -0
  40. package/dist/dialogue/index.js +3 -0
  41. package/dist/dialogue/index.js.map +1 -0
  42. package/dist/dialogue/parser.d.ts +33 -0
  43. package/dist/dialogue/parser.d.ts.map +1 -0
  44. package/dist/dialogue/parser.js +182 -0
  45. package/dist/dialogue/parser.js.map +1 -0
  46. package/dist/dialogue/types.d.ts +29 -0
  47. package/dist/dialogue/types.d.ts.map +1 -0
  48. package/dist/dialogue/types.js +3 -0
  49. package/dist/dialogue/types.js.map +1 -0
  50. package/dist/emotion/index.d.ts +5 -0
  51. package/dist/emotion/index.d.ts.map +1 -0
  52. package/dist/emotion/index.js +5 -0
  53. package/dist/emotion/index.js.map +1 -0
  54. package/dist/emotion/map.d.ts +17 -0
  55. package/dist/emotion/map.d.ts.map +1 -0
  56. package/dist/emotion/map.js +27 -0
  57. package/dist/emotion/map.js.map +1 -0
  58. package/dist/emotion/parser.d.ts +14 -0
  59. package/dist/emotion/parser.d.ts.map +1 -0
  60. package/dist/emotion/parser.js +96 -0
  61. package/dist/emotion/parser.js.map +1 -0
  62. package/dist/emotion/types.d.ts +26 -0
  63. package/dist/emotion/types.d.ts.map +1 -0
  64. package/dist/emotion/types.js +12 -0
  65. package/dist/emotion/types.js.map +1 -0
  66. package/dist/index.d.ts +16 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +17 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/limits.d.ts +44 -0
  71. package/dist/limits.d.ts.map +1 -0
  72. package/dist/limits.js +65 -0
  73. package/dist/limits.js.map +1 -0
  74. package/dist/orchestrator/concat.d.ts +20 -0
  75. package/dist/orchestrator/concat.d.ts.map +1 -0
  76. package/dist/orchestrator/concat.js +139 -0
  77. package/dist/orchestrator/concat.js.map +1 -0
  78. package/dist/orchestrator/emotionRunner.d.ts +38 -0
  79. package/dist/orchestrator/emotionRunner.d.ts.map +1 -0
  80. package/dist/orchestrator/emotionRunner.js +115 -0
  81. package/dist/orchestrator/emotionRunner.js.map +1 -0
  82. package/dist/orchestrator/index.d.ts +6 -0
  83. package/dist/orchestrator/index.d.ts.map +1 -0
  84. package/dist/orchestrator/index.js +5 -0
  85. package/dist/orchestrator/index.js.map +1 -0
  86. package/dist/orchestrator/runner.d.ts +31 -0
  87. package/dist/orchestrator/runner.d.ts.map +1 -0
  88. package/dist/orchestrator/runner.js +106 -0
  89. package/dist/orchestrator/runner.js.map +1 -0
  90. package/dist/orchestrator/types.d.ts +47 -0
  91. package/dist/orchestrator/types.d.ts.map +1 -0
  92. package/dist/orchestrator/types.js +3 -0
  93. package/dist/orchestrator/types.js.map +1 -0
  94. package/dist/presets.d.ts +20 -0
  95. package/dist/presets.d.ts.map +1 -0
  96. package/dist/presets.js +22 -0
  97. package/dist/presets.js.map +1 -0
  98. package/dist/request.d.ts +26 -0
  99. package/dist/request.d.ts.map +1 -0
  100. package/dist/request.js +33 -0
  101. package/dist/request.js.map +1 -0
  102. package/dist/sandbox.d.ts +15 -0
  103. package/dist/sandbox.d.ts.map +1 -0
  104. package/dist/sandbox.js +27 -0
  105. package/dist/sandbox.js.map +1 -0
  106. package/dist/schemas.d.ts +49 -0
  107. package/dist/schemas.d.ts.map +1 -0
  108. package/dist/schemas.js +3 -0
  109. package/dist/schemas.js.map +1 -0
  110. package/dist/sfx/generator.d.ts +15 -0
  111. package/dist/sfx/generator.d.ts.map +1 -0
  112. package/dist/sfx/generator.js +114 -0
  113. package/dist/sfx/generator.js.map +1 -0
  114. package/dist/sfx/index.d.ts +7 -0
  115. package/dist/sfx/index.d.ts.map +1 -0
  116. package/dist/sfx/index.js +7 -0
  117. package/dist/sfx/index.js.map +1 -0
  118. package/dist/sfx/parser.d.ts +16 -0
  119. package/dist/sfx/parser.d.ts.map +1 -0
  120. package/dist/sfx/parser.js +100 -0
  121. package/dist/sfx/parser.js.map +1 -0
  122. package/dist/sfx/registry.d.ts +17 -0
  123. package/dist/sfx/registry.d.ts.map +1 -0
  124. package/dist/sfx/registry.js +13 -0
  125. package/dist/sfx/registry.js.map +1 -0
  126. package/dist/sfx/stitcher.d.ts +29 -0
  127. package/dist/sfx/stitcher.d.ts.map +1 -0
  128. package/dist/sfx/stitcher.js +44 -0
  129. package/dist/sfx/stitcher.js.map +1 -0
  130. package/dist/sfx/types.d.ts +22 -0
  131. package/dist/sfx/types.d.ts.map +1 -0
  132. package/dist/sfx/types.js +10 -0
  133. package/dist/sfx/types.js.map +1 -0
  134. package/dist/ssml/index.d.ts +5 -0
  135. package/dist/ssml/index.d.ts.map +1 -0
  136. package/dist/ssml/index.js +4 -0
  137. package/dist/ssml/index.js.map +1 -0
  138. package/dist/ssml/limits.d.ts +14 -0
  139. package/dist/ssml/limits.d.ts.map +1 -0
  140. package/dist/ssml/limits.js +14 -0
  141. package/dist/ssml/limits.js.map +1 -0
  142. package/dist/ssml/parser.d.ts +28 -0
  143. package/dist/ssml/parser.d.ts.map +1 -0
  144. package/dist/ssml/parser.js +348 -0
  145. package/dist/ssml/parser.js.map +1 -0
  146. package/dist/ssml/types.d.ts +46 -0
  147. package/dist/ssml/types.d.ts.map +1 -0
  148. package/dist/ssml/types.js +3 -0
  149. package/dist/ssml/types.js.map +1 -0
  150. package/dist/validate.d.ts +29 -0
  151. package/dist/validate.d.ts.map +1 -0
  152. package/dist/validate.js +60 -0
  153. package/dist/validate.js.map +1 -0
  154. package/dist/voices.d.ts +22 -0
  155. package/dist/voices.d.ts.map +1 -0
  156. package/dist/voices.js +31 -0
  157. package/dist/voices.js.map +1 -0
  158. package/package.json +46 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mcp-tool-shop
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,34 @@
1
+ /**
2
+ * AmbientEmitter — manages ephemeral inner-monologue entries.
3
+ *
4
+ * Features:
5
+ * - Global rate limiting (min interval between emissions)
6
+ * - Per-category cooldowns
7
+ * - FIFO buffer with max size
8
+ * - TTL-based expiry
9
+ * - Sensitive content redaction
10
+ */
11
+ import type { AmbientCategory, AmbientConfig, AmbientEntry, AmbientResult } from "./types.js";
12
+ export declare class AmbientEmitter {
13
+ private readonly config;
14
+ private readonly buffer;
15
+ private lastEmitTime;
16
+ private readonly categoryCooldowns;
17
+ constructor(config?: Partial<AmbientConfig>);
18
+ /** Whether the ambient system is enabled. */
19
+ get enabled(): boolean;
20
+ /** Current buffer contents (expired entries are pruned). */
21
+ get entries(): readonly AmbientEntry[];
22
+ /** Number of entries in the buffer. */
23
+ get size(): number;
24
+ /**
25
+ * Submit a thought for emission.
26
+ * Returns accepted entry or rejection reason.
27
+ */
28
+ emitThought(text: string, category?: AmbientCategory): AmbientResult;
29
+ /** Remove expired entries from the buffer. */
30
+ private pruneExpired;
31
+ /** Clear all entries and reset cooldowns. */
32
+ clear(): void;
33
+ }
34
+ //# sourceMappingURL=emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../src/ambient/emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EACV,eAAe,EACf,aAAa,EACb,YAAY,EACZ,aAAa,EACd,MAAM,YAAY,CAAC;AAYpB,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAsC;gBAE5D,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC;IAI3C,6CAA6C;IAC7C,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,4DAA4D;IAC5D,IAAI,OAAO,IAAI,SAAS,YAAY,EAAE,CAGrC;IAED,uCAAuC;IACvC,IAAI,IAAI,IAAI,MAAM,CAGjB;IAED;;;OAGG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,eAA2B,GAAG,aAAa;IAoE/E,8CAA8C;IAC9C,OAAO,CAAC,YAAY;IAYpB,6CAA6C;IAC7C,KAAK,IAAI,IAAI;CAKd"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * AmbientEmitter — manages ephemeral inner-monologue entries.
3
+ *
4
+ * Features:
5
+ * - Global rate limiting (min interval between emissions)
6
+ * - Per-category cooldowns
7
+ * - FIFO buffer with max size
8
+ * - TTL-based expiry
9
+ * - Sensitive content redaction
10
+ */
11
+ import { randomUUID } from "node:crypto";
12
+ import { redactSensitive } from "./redact.js";
13
+ const DEFAULT_CONFIG = {
14
+ enabled: false,
15
+ globalCooldownMs: 10_000,
16
+ categoryCooldownMs: 15_000,
17
+ maxBufferSize: 5,
18
+ ttlMs: 60_000,
19
+ maxTextLength: 500,
20
+ };
21
+ export class AmbientEmitter {
22
+ config;
23
+ buffer = [];
24
+ lastEmitTime = 0;
25
+ categoryCooldowns = new Map();
26
+ constructor(config) {
27
+ this.config = { ...DEFAULT_CONFIG, ...config };
28
+ }
29
+ /** Whether the ambient system is enabled. */
30
+ get enabled() {
31
+ return this.config.enabled;
32
+ }
33
+ /** Current buffer contents (expired entries are pruned). */
34
+ get entries() {
35
+ this.pruneExpired();
36
+ return [...this.buffer];
37
+ }
38
+ /** Number of entries in the buffer. */
39
+ get size() {
40
+ this.pruneExpired();
41
+ return this.buffer.length;
42
+ }
43
+ /**
44
+ * Submit a thought for emission.
45
+ * Returns accepted entry or rejection reason.
46
+ */
47
+ emitThought(text, category = "general") {
48
+ if (!this.config.enabled) {
49
+ return { accepted: false, reason: "Ambient system is disabled", code: "AMBIENT_DISABLED" };
50
+ }
51
+ if (!text || !text.trim()) {
52
+ return { accepted: false, reason: "Empty text", code: "AMBIENT_EMPTY" };
53
+ }
54
+ // Enforce text length limit
55
+ const trimmed = text.trim();
56
+ if (trimmed.length > this.config.maxTextLength) {
57
+ return {
58
+ accepted: false,
59
+ reason: `Text exceeds max length (${this.config.maxTextLength} chars)`,
60
+ code: "AMBIENT_TOO_LONG",
61
+ };
62
+ }
63
+ // Check global rate limit
64
+ const now = Date.now();
65
+ if (now - this.lastEmitTime < this.config.globalCooldownMs) {
66
+ const waitMs = this.config.globalCooldownMs - (now - this.lastEmitTime);
67
+ return {
68
+ accepted: false,
69
+ reason: `Rate limited (global cooldown, ${Math.ceil(waitMs / 1000)}s remaining)`,
70
+ code: "AMBIENT_RATE_LIMITED",
71
+ };
72
+ }
73
+ // Check per-category cooldown
74
+ const lastCategorySend = this.categoryCooldowns.get(category) ?? 0;
75
+ if (now - lastCategorySend < this.config.categoryCooldownMs) {
76
+ const waitMs = this.config.categoryCooldownMs - (now - lastCategorySend);
77
+ return {
78
+ accepted: false,
79
+ reason: `Rate limited (${category} cooldown, ${Math.ceil(waitMs / 1000)}s remaining)`,
80
+ code: "AMBIENT_CATEGORY_COOLDOWN",
81
+ };
82
+ }
83
+ // Redact sensitive content
84
+ const { text: redactedText, redacted } = redactSensitive(trimmed);
85
+ // Create entry
86
+ const entry = {
87
+ id: randomUUID().slice(0, 8),
88
+ text: redactedText,
89
+ category,
90
+ createdAt: now,
91
+ expiresAt: now + this.config.ttlMs,
92
+ redacted,
93
+ };
94
+ // Add to buffer (FIFO eviction if full)
95
+ this.pruneExpired();
96
+ while (this.buffer.length >= this.config.maxBufferSize) {
97
+ this.buffer.shift();
98
+ }
99
+ this.buffer.push(entry);
100
+ // Update cooldowns
101
+ this.lastEmitTime = now;
102
+ this.categoryCooldowns.set(category, now);
103
+ return { accepted: true, entry };
104
+ }
105
+ /** Remove expired entries from the buffer. */
106
+ pruneExpired() {
107
+ const now = Date.now();
108
+ let i = 0;
109
+ while (i < this.buffer.length) {
110
+ if (this.buffer[i].expiresAt <= now) {
111
+ this.buffer.splice(i, 1);
112
+ }
113
+ else {
114
+ i++;
115
+ }
116
+ }
117
+ }
118
+ /** Clear all entries and reset cooldowns. */
119
+ clear() {
120
+ this.buffer.length = 0;
121
+ this.lastEmitTime = 0;
122
+ this.categoryCooldowns.clear();
123
+ }
124
+ }
125
+ //# sourceMappingURL=emitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../src/ambient/emitter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** Ambient module — barrel exports. */
2
+ export { AMBIENT_CATEGORIES, type AmbientCategory, type AmbientEntry, type AmbientConfig, type AmbientWarning, type AmbientResult, } from "./types.js";
3
+ export { type RedactResult, redactSensitive, } from "./redact.js";
4
+ export { AmbientEmitter } from "./emitter.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ambient/index.ts"],"names":[],"mappings":"AAAA,uCAAuC;AAEvC,OAAO,EACL,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,KAAK,YAAY,EACjB,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,5 @@
1
+ /** Ambient module — barrel exports. */
2
+ export { AMBIENT_CATEGORIES, } from "./types.js";
3
+ export { redactSensitive, } from "./redact.js";
4
+ export { AmbientEmitter } from "./emitter.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ambient/index.ts"],"names":[],"mappings":"AAAA,uCAAuC;AAEvC,OAAO,EACL,kBAAkB,GAMnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAEL,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,11 @@
1
+ /** Ambient redaction — strip sensitive patterns from monologue text. */
2
+ export interface RedactResult {
3
+ readonly text: string;
4
+ readonly redacted: boolean;
5
+ }
6
+ /**
7
+ * Redact sensitive patterns from text.
8
+ * Returns the redacted text and whether any redaction occurred.
9
+ */
10
+ export declare function redactSensitive(text: string): RedactResult;
11
+ //# sourceMappingURL=redact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/ambient/redact.ts"],"names":[],"mappings":"AAAA,wEAAwE;AAExE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAuBD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAe1D"}
@@ -0,0 +1,40 @@
1
+ /** Ambient redaction — strip sensitive patterns from monologue text. */
2
+ /**
3
+ * Patterns that indicate sensitive content.
4
+ * Each regex is applied globally; matched content is replaced with [REDACTED].
5
+ */
6
+ const SENSITIVE_PATTERNS = [
7
+ // API keys and tokens (common formats: sk_live_xxx, pk_test_xxx, api_key_xxx, etc.)
8
+ /\b(?:sk|pk|api|key|token|secret|bearer)[_-][a-zA-Z0-9_-]{16,}\b/gi,
9
+ // AWS-style keys
10
+ /\bAKIA[A-Z0-9]{16}\b/g,
11
+ // JWT tokens
12
+ /\beyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}\b/g,
13
+ // Passwords in common formats
14
+ /(?:password|passwd|pwd)\s*[:=]\s*\S+/gi,
15
+ // File paths with sensitive names
16
+ /(?:\/|\\)(?:\.env|credentials|secrets?|private[_-]?key|id_rsa)[^\s]*/gi,
17
+ // Email addresses (potential PII)
18
+ /\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b/g,
19
+ // IP addresses
20
+ /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g,
21
+ ];
22
+ /**
23
+ * Redact sensitive patterns from text.
24
+ * Returns the redacted text and whether any redaction occurred.
25
+ */
26
+ export function redactSensitive(text) {
27
+ let result = text;
28
+ let redacted = false;
29
+ for (const pattern of SENSITIVE_PATTERNS) {
30
+ // Reset lastIndex for global regexps
31
+ pattern.lastIndex = 0;
32
+ if (pattern.test(result)) {
33
+ redacted = true;
34
+ pattern.lastIndex = 0;
35
+ result = result.replace(pattern, "[REDACTED]");
36
+ }
37
+ }
38
+ return { text: result, redacted };
39
+ }
40
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/ambient/redact.ts"],"names":[],"mappings":"AAAA,wEAAwE;AAOxE;;;GAGG;AACH,MAAM,kBAAkB,GAAsB;IAC5C,oFAAoF;IACpF,mEAAmE;IACnE,iBAAiB;IACjB,uBAAuB;IACvB,aAAa;IACb,oEAAoE;IACpE,8BAA8B;IAC9B,wCAAwC;IACxC,kCAAkC;IAClC,wEAAwE;IACxE,kCAAkC;IAClC,qDAAqD;IACrD,eAAe;IACf,yCAAyC;CAC1C,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;QACzC,qCAAqC;QACrC,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;QACtB,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACzB,QAAQ,GAAG,IAAI,CAAC;YAChB,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;YACtB,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AACpC,CAAC"}
@@ -0,0 +1,38 @@
1
+ /** Ambient types — inner monologue and ephemeral events. */
2
+ export declare const AMBIENT_CATEGORIES: readonly ["general", "thinking", "observation", "debug"];
3
+ export type AmbientCategory = (typeof AMBIENT_CATEGORIES)[number];
4
+ export interface AmbientEntry {
5
+ readonly id: string;
6
+ readonly text: string;
7
+ readonly category: AmbientCategory;
8
+ readonly createdAt: number;
9
+ readonly expiresAt: number;
10
+ readonly redacted: boolean;
11
+ }
12
+ export interface AmbientConfig {
13
+ /** Whether ambient system is enabled. Default: false. */
14
+ readonly enabled: boolean;
15
+ /** Global rate limit: minimum ms between emissions. Default: 10000 (10s). */
16
+ readonly globalCooldownMs: number;
17
+ /** Per-category cooldown in ms. Default: 15000 (15s). */
18
+ readonly categoryCooldownMs: number;
19
+ /** Max entries in buffer. Default: 5. */
20
+ readonly maxBufferSize: number;
21
+ /** Entry TTL in ms. Default: 60000 (60s). */
22
+ readonly ttlMs: number;
23
+ /** Max text length per entry. Default: 500. */
24
+ readonly maxTextLength: number;
25
+ }
26
+ export interface AmbientWarning {
27
+ readonly code: string;
28
+ readonly message: string;
29
+ }
30
+ export type AmbientResult = {
31
+ readonly accepted: true;
32
+ readonly entry: AmbientEntry;
33
+ } | {
34
+ readonly accepted: false;
35
+ readonly reason: string;
36
+ readonly code: string;
37
+ };
38
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/ambient/types.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAE5D,eAAO,MAAM,kBAAkB,0DAKrB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,yDAAyD;IACzD,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,yCAAyC;IACzC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,6CAA6C;IAC7C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,+CAA+C;IAC/C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;CAAE,GACzD;IAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC"}
@@ -0,0 +1,8 @@
1
+ /** Ambient types — inner monologue and ephemeral events. */
2
+ export const AMBIENT_CATEGORIES = [
3
+ "general",
4
+ "thinking",
5
+ "observation",
6
+ "debug",
7
+ ];
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/ambient/types.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAE5D,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,SAAS;IACT,UAAU;IACV,aAAa;IACb,OAAO;CACC,CAAC"}
@@ -0,0 +1,37 @@
1
+ /** Artifact mode — how synthesized audio is delivered. */
2
+ import type { ArtifactMode } from "./schemas.js";
3
+ export { type ArtifactMode };
4
+ /** Default artifact mode: write to disk, return path. */
5
+ export declare const DEFAULT_ARTIFACT_MODE: ArtifactMode;
6
+ /** Supported output audio formats. */
7
+ export type OutputFormat = "wav" | "mp3" | "ogg" | "raw";
8
+ export declare const DEFAULT_OUTPUT_FORMAT: OutputFormat;
9
+ export interface ArtifactConfig {
10
+ /** How to deliver audio: "path" writes to disk, "base64" returns inline. */
11
+ readonly mode: ArtifactMode;
12
+ /** Output directory for "path" mode (resolved and validated). */
13
+ readonly outputDir?: string;
14
+ /** Audio format. Defaults to "wav". */
15
+ readonly format: OutputFormat;
16
+ }
17
+ /** Build an artifact config with defaults applied. */
18
+ export declare function buildArtifactConfig(opts?: {
19
+ mode?: ArtifactMode;
20
+ outputDir?: string;
21
+ format?: OutputFormat;
22
+ }): ArtifactConfig;
23
+ export declare class OutputDirError extends Error {
24
+ readonly code: string;
25
+ constructor(message: string, code?: string);
26
+ }
27
+ /** Default sandboxed output root: <tmpdir>/voice-soundboard/ */
28
+ export declare function defaultOutputRoot(): string;
29
+ /**
30
+ * Resolve and validate an output directory within the sandbox root.
31
+ *
32
+ * - If outputDir is undefined, returns `<root>/` (the sandbox root).
33
+ * - If outputDir is provided, it must resolve inside the root (no traversal).
34
+ * - Ensures the directory exists and is writable.
35
+ */
36
+ export declare function resolveOutputDir(outputDir: string | undefined, root: string): Promise<string>;
37
+ //# sourceMappingURL=artifact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact.d.ts","sourceRoot":"","sources":["../src/artifact.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAK1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAGjD,OAAO,EAAE,KAAK,YAAY,EAAE,CAAC;AAE7B,yDAAyD;AACzD,eAAO,MAAM,qBAAqB,EAAE,YAAqB,CAAC;AAE1D,sCAAsC;AACtC,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AACzD,eAAO,MAAM,qBAAqB,EAAE,YAAoB,CAAC;AAEzD,MAAM,WAAW,cAAc;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;CAC/B;AAED,sDAAsD;AACtD,wBAAgB,mBAAmB,CAAC,IAAI,CAAC,EAAE;IACzC,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB,GAAG,cAAc,CAMjB;AAID,qBAAa,cAAe,SAAQ,KAAK;aAGrB,IAAI,EAAE,MAAM;gBAD5B,OAAO,EAAE,MAAM,EACC,IAAI,GAAE,MAA6B;CAKtD;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CAiCjB"}
@@ -0,0 +1,61 @@
1
+ /** Artifact mode — how synthesized audio is delivered. */
2
+ import { tmpdir } from "node:os";
3
+ import { resolve, relative, isAbsolute } from "node:path";
4
+ import { mkdir, access, constants } from "node:fs/promises";
5
+ import { isSymlink } from "./sandbox.js";
6
+ /** Default artifact mode: write to disk, return path. */
7
+ export const DEFAULT_ARTIFACT_MODE = "path";
8
+ export const DEFAULT_OUTPUT_FORMAT = "wav";
9
+ /** Build an artifact config with defaults applied. */
10
+ export function buildArtifactConfig(opts) {
11
+ return {
12
+ mode: opts?.mode ?? DEFAULT_ARTIFACT_MODE,
13
+ outputDir: opts?.outputDir,
14
+ format: opts?.format ?? DEFAULT_OUTPUT_FORMAT,
15
+ };
16
+ }
17
+ // ── Output dir sandboxing ──
18
+ export class OutputDirError extends Error {
19
+ code;
20
+ constructor(message, code = "OUTPUT_DIR_INVALID") {
21
+ super(message);
22
+ this.code = code;
23
+ this.name = "OutputDirError";
24
+ }
25
+ }
26
+ /** Default sandboxed output root: <tmpdir>/voice-soundboard/ */
27
+ export function defaultOutputRoot() {
28
+ return resolve(tmpdir(), "voice-soundboard");
29
+ }
30
+ /**
31
+ * Resolve and validate an output directory within the sandbox root.
32
+ *
33
+ * - If outputDir is undefined, returns `<root>/` (the sandbox root).
34
+ * - If outputDir is provided, it must resolve inside the root (no traversal).
35
+ * - Ensures the directory exists and is writable.
36
+ */
37
+ export async function resolveOutputDir(outputDir, root) {
38
+ const resolved = outputDir
39
+ ? resolve(root, outputDir)
40
+ : root;
41
+ // Traversal check: resolved path must be inside root
42
+ const rel = relative(root, resolved);
43
+ if (rel.startsWith("..") || isAbsolute(rel)) {
44
+ throw new OutputDirError(`Output directory "${outputDir}" escapes sandbox root "${root}"`);
45
+ }
46
+ // Symlink check: reject if target is a symlink (prevents escape via symlink)
47
+ if (await isSymlink(resolved)) {
48
+ throw new OutputDirError(`Output directory "${resolved}" is a symlink — not allowed`);
49
+ }
50
+ // Ensure it exists
51
+ await mkdir(resolved, { recursive: true });
52
+ // Ensure it's writable
53
+ try {
54
+ await access(resolved, constants.W_OK);
55
+ }
56
+ catch {
57
+ throw new OutputDirError(`Output directory "${resolved}" is not writable`);
58
+ }
59
+ return resolved;
60
+ }
61
+ //# sourceMappingURL=artifact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact.js","sourceRoot":"","sources":["../src/artifact.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAE1D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE5D,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzC,yDAAyD;AACzD,MAAM,CAAC,MAAM,qBAAqB,GAAiB,MAAM,CAAC;AAI1D,MAAM,CAAC,MAAM,qBAAqB,GAAiB,KAAK,CAAC;AAWzD,sDAAsD;AACtD,MAAM,UAAU,mBAAmB,CAAC,IAInC;IACC,OAAO;QACL,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,qBAAqB;QACzC,SAAS,EAAE,IAAI,EAAE,SAAS;QAC1B,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,qBAAqB;KAC9C,CAAC;AACJ,CAAC;AAED,8BAA8B;AAE9B,MAAM,OAAO,cAAe,SAAQ,KAAK;IAGrB;IAFlB,YACE,OAAe,EACC,OAAe,oBAAoB;QAEnD,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,SAAI,GAAJ,IAAI,CAA+B;QAGnD,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,gEAAgE;AAChE,MAAM,UAAU,iBAAiB;IAC/B,OAAO,OAAO,CAAC,MAAM,EAAE,EAAE,kBAAkB,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,SAA6B,EAC7B,IAAY;IAEZ,MAAM,QAAQ,GAAG,SAAS;QACxB,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC;QAC1B,CAAC,CAAC,IAAI,CAAC;IAET,qDAAqD;IACrD,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACrC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,cAAc,CACtB,qBAAqB,SAAS,2BAA2B,IAAI,GAAG,CACjE,CAAC;IACJ,CAAC;IAED,6EAA6E;IAC7E,IAAI,MAAM,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,cAAc,CACtB,qBAAqB,QAAQ,8BAA8B,CAC5D,CAAC;IACJ,CAAC;IAED,mBAAmB;IACnB,MAAM,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3C,uBAAuB;IACvB,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,cAAc,CACtB,qBAAqB,QAAQ,mBAAmB,CACjD,CAAC;IACJ,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Smart text chunker — splits long text for sequential synthesis.
3
+ *
4
+ * Split cascade:
5
+ * 1. Paragraphs (double newline)
6
+ * 2. Sentences (period/exclamation/question followed by space or EOL)
7
+ * 3. Punctuation (comma, semicolon, colon, dash followed by space)
8
+ * 4. Hard split at maxChunkChars boundary (last resort)
9
+ *
10
+ * Tiny chunks (< minChunkChars) are merged with the previous chunk.
11
+ */
12
+ import type { ChunkResult, ChunkingOptions } from "./types.js";
13
+ /** Split text into synthesis-friendly chunks. */
14
+ export declare function chunkText(text: string, options?: ChunkingOptions): ChunkResult;
15
+ //# sourceMappingURL=chunker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunker.d.ts","sourceRoot":"","sources":["../../src/chunking/chunker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAgB,eAAe,EAAE,MAAM,YAAY,CAAC;AAG7E,iDAAiD;AACjD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,WAAW,CA6D9E"}
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Smart text chunker — splits long text for sequential synthesis.
3
+ *
4
+ * Split cascade:
5
+ * 1. Paragraphs (double newline)
6
+ * 2. Sentences (period/exclamation/question followed by space or EOL)
7
+ * 3. Punctuation (comma, semicolon, colon, dash followed by space)
8
+ * 4. Hard split at maxChunkChars boundary (last resort)
9
+ *
10
+ * Tiny chunks (< minChunkChars) are merged with the previous chunk.
11
+ */
12
+ import { CHUNK_LIMITS } from "./limits.js";
13
+ /** Split text into synthesis-friendly chunks. */
14
+ export function chunkText(text, options) {
15
+ const maxChunkChars = options?.maxChunkChars ?? CHUNK_LIMITS.maxChunkChars;
16
+ const minChunkChars = options?.minChunkChars ?? CHUNK_LIMITS.minChunkChars;
17
+ const maxTotalChars = options?.maxTotalChars ?? CHUNK_LIMITS.maxTotalChars;
18
+ const maxChunks = options?.maxChunks ?? CHUNK_LIMITS.maxChunks;
19
+ const warnings = [];
20
+ let input = text.trim();
21
+ if (!input) {
22
+ return { chunks: [], wasChunked: false, wasTruncated: false, warnings };
23
+ }
24
+ // Enforce total chars limit
25
+ let wasTruncated = false;
26
+ if (input.length > maxTotalChars) {
27
+ input = truncateAtBoundary(input, maxTotalChars);
28
+ wasTruncated = true;
29
+ warnings.push({
30
+ code: "TRUNCATED",
31
+ message: `Text truncated from ${text.trim().length} to ${input.length} characters (limit: ${maxTotalChars})`,
32
+ });
33
+ }
34
+ // If short enough, return as single chunk
35
+ if (input.length <= maxChunkChars) {
36
+ return { chunks: [input], wasChunked: false, wasTruncated, warnings };
37
+ }
38
+ // Split cascade
39
+ let rawChunks = splitByParagraphs(input);
40
+ rawChunks = refineSplits(rawChunks, maxChunkChars, splitBySentences);
41
+ rawChunks = refineSplits(rawChunks, maxChunkChars, splitByPunctuation);
42
+ rawChunks = refineSplits(rawChunks, maxChunkChars, hardSplit);
43
+ // Merge tiny chunks
44
+ rawChunks = mergeTiny(rawChunks, minChunkChars, maxChunkChars);
45
+ // Enforce max chunks
46
+ if (rawChunks.length > maxChunks) {
47
+ rawChunks = rawChunks.slice(0, maxChunks);
48
+ wasTruncated = true;
49
+ warnings.push({
50
+ code: "TRUNCATED",
51
+ message: `Chunk count reduced to ${maxChunks} (limit)`,
52
+ });
53
+ }
54
+ if (rawChunks.length > 1) {
55
+ warnings.push({
56
+ code: "CHUNKED_TEXT",
57
+ message: `Text split into ${rawChunks.length} chunks`,
58
+ });
59
+ }
60
+ return {
61
+ chunks: rawChunks,
62
+ wasChunked: rawChunks.length > 1,
63
+ wasTruncated,
64
+ warnings,
65
+ };
66
+ }
67
+ // ── Split strategies ──
68
+ /** Split on paragraph boundaries (double newline). */
69
+ function splitByParagraphs(text) {
70
+ return text
71
+ .split(/\n\s*\n/)
72
+ .map((s) => s.trim())
73
+ .filter(Boolean);
74
+ }
75
+ /** Split on sentence boundaries. */
76
+ function splitBySentences(text) {
77
+ // Split after sentence-ending punctuation followed by space or EOL
78
+ const parts = text.split(/(?<=[.!?])\s+/);
79
+ return parts.map((s) => s.trim()).filter(Boolean);
80
+ }
81
+ /** Split on clause-level punctuation. */
82
+ function splitByPunctuation(text) {
83
+ const parts = text.split(/(?<=[,;:\u2014])\s+/);
84
+ return parts.map((s) => s.trim()).filter(Boolean);
85
+ }
86
+ /** Hard split at character boundary (last resort). */
87
+ function hardSplit(text, maxChars) {
88
+ const limit = maxChars ?? CHUNK_LIMITS.maxChunkChars;
89
+ const result = [];
90
+ let remaining = text;
91
+ while (remaining.length > limit) {
92
+ // Try to split at a word boundary
93
+ let splitIdx = remaining.lastIndexOf(" ", limit);
94
+ if (splitIdx <= 0) {
95
+ splitIdx = limit;
96
+ }
97
+ result.push(remaining.slice(0, splitIdx).trim());
98
+ remaining = remaining.slice(splitIdx).trim();
99
+ }
100
+ if (remaining) {
101
+ result.push(remaining);
102
+ }
103
+ return result;
104
+ }
105
+ /**
106
+ * For any chunk still over maxChunkChars, apply a finer split strategy.
107
+ */
108
+ function refineSplits(chunks, maxChunkChars, splitter) {
109
+ const result = [];
110
+ for (const chunk of chunks) {
111
+ if (chunk.length <= maxChunkChars) {
112
+ result.push(chunk);
113
+ }
114
+ else {
115
+ result.push(...splitter(chunk, maxChunkChars));
116
+ }
117
+ }
118
+ return result;
119
+ }
120
+ /** Merge tiny chunks with their neighbor. */
121
+ function mergeTiny(chunks, minChars, maxChars) {
122
+ if (chunks.length <= 1)
123
+ return chunks;
124
+ const result = [chunks[0]];
125
+ for (let i = 1; i < chunks.length; i++) {
126
+ const current = chunks[i];
127
+ const prev = result[result.length - 1];
128
+ if (current.length < minChars && (prev.length + current.length + 1) <= maxChars) {
129
+ // Merge with previous
130
+ result[result.length - 1] = prev + " " + current;
131
+ }
132
+ else {
133
+ result.push(current);
134
+ }
135
+ }
136
+ return result;
137
+ }
138
+ /** Truncate text at a natural boundary near maxChars. */
139
+ function truncateAtBoundary(text, maxChars) {
140
+ if (text.length <= maxChars)
141
+ return text;
142
+ // Try sentence boundary
143
+ const sub = text.slice(0, maxChars);
144
+ const sentEnd = Math.max(sub.lastIndexOf(". "), sub.lastIndexOf("! "), sub.lastIndexOf("? "));
145
+ if (sentEnd > maxChars * 0.5) {
146
+ return text.slice(0, sentEnd + 1).trim();
147
+ }
148
+ // Try word boundary
149
+ const wordEnd = sub.lastIndexOf(" ");
150
+ if (wordEnd > maxChars * 0.5) {
151
+ return text.slice(0, wordEnd).trim();
152
+ }
153
+ // Hard cut
154
+ return sub.trim();
155
+ }
156
+ //# sourceMappingURL=chunker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunker.js","sourceRoot":"","sources":["../../src/chunking/chunker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** Chunking module — split long text for sequential synthesis. */
2
+ export { type ChunkingOptions, type ChunkResult, type ChunkWarning, } from "./types.js";
3
+ export { CHUNK_LIMITS } from "./limits.js";
4
+ export { chunkText } from "./chunker.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/chunking/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAElE,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,4 @@
1
+ /** Chunking module — split long text for sequential synthesis. */
2
+ export { CHUNK_LIMITS } from "./limits.js";
3
+ export { chunkText } from "./chunker.js";
4
+ //# sourceMappingURL=index.js.map