@tianshu-ai/tianshu 0.53.9 → 0.54.2

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 (27) hide show
  1. package/package.json +2 -1
  2. package/packages/server/dist/boot/routes-tts.d.ts +3 -0
  3. package/packages/server/dist/boot/routes-tts.d.ts.map +1 -0
  4. package/packages/server/dist/boot/routes-tts.js +297 -0
  5. package/packages/server/dist/boot/routes-tts.js.map +1 -0
  6. package/packages/server/dist/chat/handler.d.ts +5 -0
  7. package/packages/server/dist/chat/handler.d.ts.map +1 -1
  8. package/packages/server/dist/chat/handler.js +2 -1
  9. package/packages/server/dist/chat/handler.js.map +1 -1
  10. package/packages/server/dist/chat/system-prompt.d.ts +13 -1
  11. package/packages/server/dist/chat/system-prompt.d.ts.map +1 -1
  12. package/packages/server/dist/chat/system-prompt.js +82 -1
  13. package/packages/server/dist/chat/system-prompt.js.map +1 -1
  14. package/packages/server/dist/chat/ws-protocol.d.ts +6 -0
  15. package/packages/server/dist/chat/ws-protocol.d.ts.map +1 -1
  16. package/packages/server/dist/chat/ws-protocol.js.map +1 -1
  17. package/packages/server/dist/index.js +7 -0
  18. package/packages/server/dist/index.js.map +1 -1
  19. package/packages/server/package.json +1 -0
  20. package/packages/web/dist/assets/{DataSourceConfigForm-DhqzdM3I.js → DataSourceConfigForm-Bo-pSc6o.js} +1 -1
  21. package/packages/web/dist/assets/{bundle-web-D9reUxyI.js → bundle-web-B9gmlfYp.js} +1 -1
  22. package/packages/web/dist/assets/index-D8pqscXq.css +2 -0
  23. package/packages/web/dist/assets/index-GObK_V8a.js +273 -0
  24. package/packages/web/dist/assets/{react-force-graph-2d-BvcQUaLJ.js → react-force-graph-2d-DxhlCD96.js} +1 -1
  25. package/packages/web/dist/index.html +2 -2
  26. package/packages/web/dist/assets/index-6nQVP4ka.css +0 -2
  27. package/packages/web/dist/assets/index-BB2_lfN0.js +0 -269
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tianshu-ai/tianshu",
3
- "version": "0.53.9",
3
+ "version": "0.54.2",
4
4
  "description": "An open AI agent platform with a sidecar browser. Built in public.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/tianshu-ai/tianshu",
@@ -65,6 +65,7 @@
65
65
  "typescript": "^7.0.2"
66
66
  },
67
67
  "dependencies": {
68
+ "@andresaya/edge-tts": "^1.8.0",
68
69
  "@clack/prompts": "^1.5.1",
69
70
  "@earendil-works/pi-agent-core": "0.82.1",
70
71
  "@earendil-works/pi-ai": "0.82.1",
@@ -0,0 +1,3 @@
1
+ import type { Express } from "express";
2
+ export declare function mountTtsRoutes(app: Express): void;
3
+ //# sourceMappingURL=routes-tts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes-tts.d.ts","sourceRoot":"","sources":["../../src/boot/routes-tts.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAkQ1D,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,QA8E1C"}
@@ -0,0 +1,297 @@
1
+ // POST /api/tts: text -> audio proxy.
2
+ //
3
+ // Yu, 2026-09-19: supports two TTS backends selected by env
4
+ // TTS_PROVIDER:
5
+ //
6
+ // "edge" (default) — Microsoft Edge's online TTS via the
7
+ // @andresaya/edge-tts npm package. Zero local setup, no API key,
8
+ // free unlimited use. Chinese voices (Xiaoxiao/Yunxi/Yunyang)
9
+ // are near-production quality. Trade-off: needs internet;
10
+ // Microsoft EULA forbids commercial use (fine for a personal
11
+ // side project like tianshu, revisit before shipping).
12
+ //
13
+ // "cosyvoice" — Local CosyVoice 2 FastAPI server (Apache 2.0,
14
+ // ~150 ms first-packet latency, native Apple Silicon MPS).
15
+ // Yu runs the CosyVoice server separately; tianshu forwards
16
+ // via HTTP. See scripts/COSYVOICE_SETUP.md for setup.
17
+ //
18
+ // Same public contract in both cases:
19
+ // POST /api/tts body: { text, voice? }
20
+ // → response body: audio/* (mp3 for edge, wav for cosyvoice)
21
+ //
22
+ // Browser <audio> plays either directly; useTts hook doesn't care.
23
+ import { EdgeTTS } from "@andresaya/edge-tts";
24
+ // ─── Common config ───────────────────────────────────────────
25
+ /** Which backend to use. Default "edge" because it works out of
26
+ * the box with no local deps; user can switch to "cosyvoice"
27
+ * once that's running locally. */
28
+ const TTS_PROVIDER = (process.env.TTS_PROVIDER || "edge").toLowerCase();
29
+ /** Absolute cap on request text — we never let a browser paste a
30
+ * novel-length string; the upstream will time out anyway. */
31
+ const MAX_TEXT_CHARS = 4000;
32
+ // ─── Edge TTS config ─────────────────────────────────────────
33
+ /** Default Chinese voice when the client doesn't specify one.
34
+ * Xiaoxiao is Microsoft's flagship zh-CN voice — natural
35
+ * prosody, works for both formal and conversational text.
36
+ * Alternatives: zh-CN-YunxiNeural (male), zh-CN-YunyangNeural
37
+ * (male, newscaster), en-US-AriaNeural (English female). */
38
+ const DEFAULT_EDGE_VOICE = "zh-CN-XiaoxiaoNeural";
39
+ /** Edge output format — 24 kHz mono MP3 at 96 kbps. Browsers
40
+ * play mp3 natively via <audio>, no header building needed. */
41
+ const EDGE_OUTPUT_FORMAT = "audio-24khz-96kbitrate-mono-mp3";
42
+ // ─── CosyVoice config ────────────────────────────────────────
43
+ /** CosyVoice FastAPI server URL (default matches server.py's
44
+ * `--port 50000`). */
45
+ const DEFAULT_COSY_URL = "http://localhost:50000";
46
+ /** CosyVoice2-0.5B outputs 24 kHz mono int16 PCM. Hardcoded to
47
+ * match the model; expose as env if we run a different one. */
48
+ const COSY_SAMPLE_RATE = 24000;
49
+ const COSY_CHANNELS = 1;
50
+ const COSY_BITS_PER_SAMPLE = 16;
51
+ /** Default CosyVoice speaker id. */
52
+ const DEFAULT_COSY_VOICE = "中文女";
53
+ /**
54
+ * Build a 44-byte RIFF wav header for mono int16 PCM.
55
+ * Used by the CosyVoice branch because CosyVoice yields raw PCM.
56
+ */
57
+ function buildWavHeader(pcmByteLen) {
58
+ const header = Buffer.alloc(44);
59
+ const byteRate = COSY_SAMPLE_RATE * COSY_CHANNELS * (COSY_BITS_PER_SAMPLE / 8);
60
+ const blockAlign = COSY_CHANNELS * (COSY_BITS_PER_SAMPLE / 8);
61
+ header.write("RIFF", 0);
62
+ header.writeUInt32LE(36 + pcmByteLen, 4);
63
+ header.write("WAVE", 8);
64
+ header.write("fmt ", 12);
65
+ header.writeUInt32LE(16, 16);
66
+ header.writeUInt16LE(1, 20);
67
+ header.writeUInt16LE(COSY_CHANNELS, 22);
68
+ header.writeUInt32LE(COSY_SAMPLE_RATE, 24);
69
+ header.writeUInt32LE(byteRate, 28);
70
+ header.writeUInt16LE(blockAlign, 32);
71
+ header.writeUInt16LE(COSY_BITS_PER_SAMPLE, 34);
72
+ header.write("data", 36);
73
+ header.writeUInt32LE(pcmByteLen, 40);
74
+ return header;
75
+ }
76
+ /**
77
+ * Edge TTS handler — streams mp3 chunks via @andresaya/edge-tts's
78
+ * synthesizeStream() async iterator.
79
+ *
80
+ * Yu, 2026-09-19 21:27: switched from batch synthesize() +
81
+ * toBase64() to streaming. Reason: edge-tts's cloud endpoint has
82
+ * ~1.8s first-byte latency; the batch path also waited for the
83
+ * FULL synthesis before returning (~2.6s), so users heard silence
84
+ * for ~2s after the reply text finished streaming. Streaming
85
+ * halves the perceived delay because express flushes each chunk
86
+ * to the browser and MediaSource on the client starts playback
87
+ * as soon as the first chunk arrives.
88
+ *
89
+ * Wire-level (probed 2026-09-19 against @andresaya/edge-tts@1.8.0):
90
+ * synthesizeStream(text, voice, { outputFormat })
91
+ * → AsyncIterable<Uint8Array>
92
+ * Each chunk is ~1.4 KB of raw MP3 frames aligned on frame
93
+ * boundaries. Content-Type is audio/mpeg. No trailing footer or
94
+ * completion marker — iterator just ends.
95
+ *
96
+ * Response uses chunked transfer encoding implicitly (express
97
+ * writes without Content-Length). Client MediaSource + fetch
98
+ * ReadableStream + SourceBuffer.appendBuffer plays as chunks land.
99
+ */
100
+ async function handleEdge(res, text, voice) {
101
+ const useVoice = voice?.trim() || DEFAULT_EDGE_VOICE;
102
+ const tts = new EdgeTTS();
103
+ console.log(`[tts] edge streaming synthesise: len=${text.length} voice=${useVoice}`);
104
+ // Set headers up-front; don't set Content-Length — that would
105
+ // require buffering the whole thing.
106
+ res.setHeader("Content-Type", "audio/mpeg");
107
+ res.setHeader("Cache-Control", "no-store");
108
+ // Explicitly disable Nagle-style buffering on the response so
109
+ // the first chunk gets flushed to the client immediately.
110
+ res.setHeader("X-Accel-Buffering", "no");
111
+ const start = Date.now();
112
+ let chunkCount = 0;
113
+ let byteCount = 0;
114
+ let firstChunkMs = null;
115
+ try {
116
+ const iter = tts.synthesizeStream(text, useVoice, { outputFormat: EDGE_OUTPUT_FORMAT });
117
+ for await (const chunk of iter) {
118
+ if (firstChunkMs == null)
119
+ firstChunkMs = Date.now() - start;
120
+ chunkCount++;
121
+ byteCount += chunk.length;
122
+ res.write(Buffer.from(chunk));
123
+ }
124
+ }
125
+ catch (err) {
126
+ console.warn("[tts] edge stream failed mid-flight:", err);
127
+ // Headers already sent; can't return a JSON error body. Best
128
+ // effort: end the response so the browser sees an empty stream.
129
+ try {
130
+ res.end();
131
+ }
132
+ catch {
133
+ // ignore
134
+ }
135
+ return;
136
+ }
137
+ res.end();
138
+ console.log(`[tts] edge streamed mp3: ${byteCount}B in ${chunkCount} chunks ` +
139
+ `(first at ${firstChunkMs}ms, total ${Date.now() - start}ms)`);
140
+ }
141
+ /**
142
+ * CosyVoice handler — forwards to the local FastAPI server, buffers
143
+ * PCM, prepends a wav header, streams back.
144
+ */
145
+ async function handleCosyvoice(res, text, voice) {
146
+ const useVoice = voice?.trim() || DEFAULT_COSY_VOICE;
147
+ const upstream = process.env.TTS_URL || DEFAULT_COSY_URL;
148
+ const url = `${upstream}/inference_sft`;
149
+ // CosyVoice's FastAPI expects multipart/form-data (server.py:
150
+ // `tts_text: str = Form(), spk_id: str = Form()`).
151
+ const form = new FormData();
152
+ form.set("tts_text", text);
153
+ form.set("spk_id", useVoice);
154
+ console.log(`[tts] cosyvoice forwarding: len=${text.length} voice=${useVoice} url=${url}`);
155
+ let upstreamRes;
156
+ try {
157
+ upstreamRes = await fetch(url, { method: "POST", body: form });
158
+ }
159
+ catch (err) {
160
+ console.warn(`[tts] cosyvoice upstream unreachable: ${upstream}`, err);
161
+ res.status(503).json({
162
+ error: "tts upstream unreachable",
163
+ detail: err instanceof Error ? err.message : String(err),
164
+ hint: `Is CosyVoice running at ${upstream}? See scripts/COSYVOICE_SETUP.md.`,
165
+ });
166
+ return;
167
+ }
168
+ if (!upstreamRes.ok || !upstreamRes.body) {
169
+ console.warn(`[tts] cosyvoice upstream error: ${upstreamRes.status} ${upstreamRes.statusText}`);
170
+ res.status(upstreamRes.status).json({
171
+ error: "tts upstream returned error",
172
+ status: upstreamRes.status,
173
+ statusText: upstreamRes.statusText,
174
+ });
175
+ return;
176
+ }
177
+ // Buffer full PCM then wrap with RIFF wav header (needs total
178
+ // byte length up front). ~24 KB/s at 24 kHz mono int16 — a 10 s
179
+ // reply is ~240 KB, safe in memory.
180
+ const chunks = [];
181
+ let totalBytes = 0;
182
+ const reader = upstreamRes.body.getReader();
183
+ try {
184
+ while (true) {
185
+ const { value, done } = await reader.read();
186
+ if (done)
187
+ break;
188
+ const buf = Buffer.from(value);
189
+ chunks.push(buf);
190
+ totalBytes += buf.length;
191
+ }
192
+ }
193
+ catch (err) {
194
+ console.warn("[tts] cosyvoice stream aborted:", err);
195
+ res.status(502).json({
196
+ error: "tts upstream stream aborted",
197
+ detail: err instanceof Error ? err.message : String(err),
198
+ });
199
+ return;
200
+ }
201
+ if (totalBytes === 0) {
202
+ console.warn("[tts] cosyvoice returned empty PCM");
203
+ res.status(502).json({ error: "tts upstream returned empty audio" });
204
+ return;
205
+ }
206
+ const header = buildWavHeader(totalBytes);
207
+ const wavLen = header.length + totalBytes;
208
+ res.setHeader("Content-Type", "audio/wav");
209
+ res.setHeader("Content-Length", String(wavLen));
210
+ res.setHeader("Cache-Control", "no-store");
211
+ res.write(header);
212
+ for (const chunk of chunks)
213
+ res.write(chunk);
214
+ res.end();
215
+ console.log(`[tts] cosyvoice delivered wav: pcm=${totalBytes}B total=${wavLen}B ` +
216
+ `(${(totalBytes /
217
+ COSY_SAMPLE_RATE /
218
+ (COSY_BITS_PER_SAMPLE / 8) /
219
+ COSY_CHANNELS).toFixed(1)}s)`);
220
+ }
221
+ export function mountTtsRoutes(app) {
222
+ app.post("/api/tts", async (req, res) => {
223
+ const body = (req.body ?? {});
224
+ const text = typeof body.text === "string" ? body.text.trim() : "";
225
+ const voice = typeof body.voice === "string" && body.voice.trim().length
226
+ ? body.voice.trim()
227
+ : undefined;
228
+ // Client override wins over env default. Yu, 2026-09-19: added
229
+ // so the Settings page can flip between edge/cosyvoice without
230
+ // restarting the server.
231
+ const requestedProvider = typeof body.provider === "string" &&
232
+ body.provider.trim().length
233
+ ? body.provider.trim().toLowerCase()
234
+ : TTS_PROVIDER;
235
+ if (!text) {
236
+ res.status(400).json({ error: "text is required" });
237
+ return;
238
+ }
239
+ if (text.length > MAX_TEXT_CHARS) {
240
+ res.status(400).json({
241
+ error: `text too long: ${text.length} chars > max ${MAX_TEXT_CHARS}`,
242
+ });
243
+ return;
244
+ }
245
+ if (requestedProvider === "edge") {
246
+ await handleEdge(res, text, voice);
247
+ }
248
+ else if (requestedProvider === "cosyvoice") {
249
+ await handleCosyvoice(res, text, voice);
250
+ }
251
+ else {
252
+ console.warn(`[tts] unknown provider: ${requestedProvider}`);
253
+ res.status(400).json({
254
+ error: `unknown provider: ${requestedProvider}`,
255
+ hint: "Use provider=edge or provider=cosyvoice",
256
+ });
257
+ }
258
+ });
259
+ // GET /api/tts/status
260
+ //
261
+ // Powers the Settings > Text-to-Speech page. Reports:
262
+ // - providerDefault: server's TTS_PROVIDER env (client uses
263
+ // this as a fallback when the user has no preference yet)
264
+ // - cosyvoiceUrl: env TTS_URL (or default) so the user can see
265
+ // what external service the server is pointed at
266
+ // - cosyvoiceReachable: null (didn't probe) | true | false
267
+ // — probes only when the caller asked for it via ?probe=1,
268
+ // because Yu is happy to launch CosyVoice separately and
269
+ // doesn't want a health check on every settings page load.
270
+ app.get("/api/tts/status", async (req, res) => {
271
+ const cosyvoiceUrl = process.env.TTS_URL || DEFAULT_COSY_URL;
272
+ let cosyvoiceReachable = null;
273
+ const shouldProbe = String(req.query.probe ?? "1") === "1";
274
+ if (shouldProbe) {
275
+ try {
276
+ // We don't have a dedicated health endpoint on CosyVoice's
277
+ // FastAPI server, so probe /docs (auto-generated by FastAPI
278
+ // — cheap, always present, no side effects).
279
+ const probe = await fetch(`${cosyvoiceUrl}/docs`, {
280
+ method: "GET",
281
+ signal: AbortSignal.timeout(1500),
282
+ });
283
+ cosyvoiceReachable = probe.ok;
284
+ }
285
+ catch {
286
+ cosyvoiceReachable = false;
287
+ }
288
+ }
289
+ res.json({
290
+ providerDefault: TTS_PROVIDER,
291
+ cosyvoiceUrl,
292
+ cosyvoiceReachable,
293
+ });
294
+ });
295
+ console.log(`[tts] mounted /api/tts + /api/tts/status provider=${TTS_PROVIDER}`);
296
+ }
297
+ //# sourceMappingURL=routes-tts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes-tts.js","sourceRoot":"","sources":["../../src/boot/routes-tts.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,4DAA4D;AAC5D,gBAAgB;AAChB,EAAE;AACF,2DAA2D;AAC3D,qEAAqE;AACrE,kEAAkE;AAClE,8DAA8D;AAC9D,iEAAiE;AACjE,2DAA2D;AAC3D,EAAE;AACF,gEAAgE;AAChE,+DAA+D;AAC/D,gEAAgE;AAChE,0DAA0D;AAC1D,EAAE;AACF,sCAAsC;AACtC,2CAA2C;AAC3C,+DAA+D;AAC/D,EAAE;AACF,mEAAmE;AAGnE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE9C,gEAAgE;AAEhE;;mCAEmC;AACnC,MAAM,YAAY,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;AAExE;8DAC8D;AAC9D,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B,gEAAgE;AAEhE;;;;6DAI6D;AAC7D,MAAM,kBAAkB,GAAG,sBAAsB,CAAC;AAElD;gEACgE;AAChE,MAAM,kBAAkB,GAAG,iCAAiC,CAAC;AAE7D,gEAAgE;AAEhE;uBACuB;AACvB,MAAM,gBAAgB,GAAG,wBAAwB,CAAC;AAElD;gEACgE;AAChE,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAC/B,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEhC,oCAAoC;AACpC,MAAM,kBAAkB,GAAG,KAAK,CAAC;AAWjC;;;GAGG;AACH,SAAS,cAAc,CAAC,UAAkB;IACxC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAChC,MAAM,QAAQ,GACZ,gBAAgB,GAAG,aAAa,GAAG,CAAC,oBAAoB,GAAG,CAAC,CAAC,CAAC;IAChE,MAAM,UAAU,GAAG,aAAa,GAAG,CAAC,oBAAoB,GAAG,CAAC,CAAC,CAAC;IAE9D,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxB,MAAM,CAAC,aAAa,CAAC,EAAE,GAAG,UAAU,EAAE,CAAC,CAAC,CAAC;IACzC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxB,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACzB,MAAM,CAAC,aAAa,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAC7B,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC5B,MAAM,CAAC,aAAa,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IACxC,MAAM,CAAC,aAAa,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;IAC3C,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACnC,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACrC,MAAM,CAAC,aAAa,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;IAC/C,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACzB,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAErC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,KAAK,UAAU,UAAU,CACvB,GAAa,EACb,IAAY,EACZ,KAAyB;IAEzB,MAAM,QAAQ,GAAG,KAAK,EAAE,IAAI,EAAE,IAAI,kBAAkB,CAAC;IACrD,MAAM,GAAG,GAAG,IAAI,OAAO,EAAE,CAAC;IAE1B,OAAO,CAAC,GAAG,CACT,wCAAwC,IAAI,CAAC,MAAM,UAAU,QAAQ,EAAE,CACxE,CAAC;IAEF,8DAA8D;IAC9D,qCAAqC;IACrC,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;IAC5C,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IAC3C,8DAA8D;IAC9D,0DAA0D;IAC1D,GAAG,CAAC,SAAS,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC;IAEzC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,YAAY,GAAkB,IAAI,CAAC;IAEvC,IAAI,CAAC;QACH,MAAM,IAAI,GAAI,GAMZ,CAAC,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,YAAY,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC1E,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;YAC/B,IAAI,YAAY,IAAI,IAAI;gBAAE,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YAC5D,UAAU,EAAE,CAAC;YACb,SAAS,IAAI,KAAK,CAAC,MAAM,CAAC;YAC1B,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,sCAAsC,EAAE,GAAG,CAAC,CAAC;QAC1D,6DAA6D;QAC7D,gEAAgE;QAChE,IAAI,CAAC;YACH,GAAG,CAAC,GAAG,EAAE,CAAC;QACZ,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,OAAO;IACT,CAAC;IAED,GAAG,CAAC,GAAG,EAAE,CAAC;IACV,OAAO,CAAC,GAAG,CACT,4BAA4B,SAAS,QAAQ,UAAU,UAAU;QAC/D,aAAa,YAAY,aAAa,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,KAAK,CAChE,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,eAAe,CAC5B,GAAa,EACb,IAAY,EACZ,KAAyB;IAEzB,MAAM,QAAQ,GAAG,KAAK,EAAE,IAAI,EAAE,IAAI,kBAAkB,CAAC;IACrD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,gBAAgB,CAAC;IACzD,MAAM,GAAG,GAAG,GAAG,QAAQ,gBAAgB,CAAC;IAExC,8DAA8D;IAC9D,mDAAmD;IACnD,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;IAC5B,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAC3B,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAE7B,OAAO,CAAC,GAAG,CACT,mCAAmC,IAAI,CAAC,MAAM,UAAU,QAAQ,QAAQ,GAAG,EAAE,CAC9E,CAAC;IAEF,IAAI,WAAgC,CAAC;IACrC,IAAI,CAAC;QACH,WAAW,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,yCAAyC,QAAQ,EAAE,EAAE,GAAG,CAAC,CAAC;QACvE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,KAAK,EAAE,0BAA0B;YACjC,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;YACxD,IAAI,EAAE,2BAA2B,QAAQ,mCAAmC;SAC7E,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,IAAI,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;QACzC,OAAO,CAAC,IAAI,CACV,mCAAmC,WAAW,CAAC,MAAM,IAAI,WAAW,CAAC,UAAU,EAAE,CAClF,CAAC;QACF,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;YAClC,KAAK,EAAE,6BAA6B;YACpC,MAAM,EAAE,WAAW,CAAC,MAAM;YAC1B,UAAU,EAAE,WAAW,CAAC,UAAU;SACnC,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,8DAA8D;IAC9D,gEAAgE;IAChE,oCAAoC;IACpC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;IAC5C,IAAI,CAAC;QACH,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YAC5C,IAAI,IAAI;gBAAE,MAAM;YAChB,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjB,UAAU,IAAI,GAAG,CAAC,MAAM,CAAC;QAC3B,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAC;QACrD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,KAAK,EAAE,6BAA6B;YACpC,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACzD,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;QACnD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mCAAmC,EAAE,CAAC,CAAC;QACrE,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC;IAE1C,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;IAC3C,GAAG,CAAC,SAAS,CAAC,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAChD,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IAC3C,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAClB,KAAK,MAAM,KAAK,IAAI,MAAM;QAAE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC7C,GAAG,CAAC,GAAG,EAAE,CAAC;IAEV,OAAO,CAAC,GAAG,CACT,sCAAsC,UAAU,WAAW,MAAM,IAAI;QACnE,IAAI,CACF,UAAU;YACV,gBAAgB;YAChB,CAAC,oBAAoB,GAAG,CAAC,CAAC;YAC1B,aAAa,CACd,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CACnB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAY;IACzC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACzD,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAmB,CAAC;QAChD,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,MAAM,KAAK,GACT,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM;YACxD,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;YACnB,CAAC,CAAC,SAAS,CAAC;QAChB,+DAA+D;QAC/D,+DAA+D;QAC/D,yBAAyB;QACzB,MAAM,iBAAiB,GACrB,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ;YACjC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM;YACzB,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;YACpC,CAAC,CAAC,YAAY,CAAC;QAEnB,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;YACjC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,KAAK,EAAE,kBAAkB,IAAI,CAAC,MAAM,gBAAgB,cAAc,EAAE;aACrE,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,IAAI,iBAAiB,KAAK,MAAM,EAAE,CAAC;YACjC,MAAM,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QACrC,CAAC;aAAM,IAAI,iBAAiB,KAAK,WAAW,EAAE,CAAC;YAC7C,MAAM,eAAe,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,2BAA2B,iBAAiB,EAAE,CAAC,CAAC;YAC7D,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,KAAK,EAAE,qBAAqB,iBAAiB,EAAE;gBAC/C,IAAI,EAAE,yCAAyC;aAChD,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,sBAAsB;IACtB,EAAE;IACF,sDAAsD;IACtD,8DAA8D;IAC9D,8DAA8D;IAC9D,iEAAiE;IACjE,qDAAqD;IACrD,6DAA6D;IAC7D,+DAA+D;IAC/D,6DAA6D;IAC7D,+DAA+D;IAC/D,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QAC/D,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,gBAAgB,CAAC;QAC7D,IAAI,kBAAkB,GAAmB,IAAI,CAAC;QAC9C,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC;QAC3D,IAAI,WAAW,EAAE,CAAC;YAChB,IAAI,CAAC;gBACH,2DAA2D;gBAC3D,4DAA4D;gBAC5D,6CAA6C;gBAC7C,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,GAAG,YAAY,OAAO,EAAE;oBAChD,MAAM,EAAE,KAAK;oBACb,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC;iBAClC,CAAC,CAAC;gBACH,kBAAkB,GAAG,KAAK,CAAC,EAAE,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,kBAAkB,GAAG,KAAK,CAAC;YAC7B,CAAC;QACH,CAAC;QACD,GAAG,CAAC,IAAI,CAAC;YACP,eAAe,EAAE,YAAY;YAC7B,YAAY;YACZ,kBAAkB;SACnB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,qDAAqD,YAAY,EAAE,CAAC,CAAC;AACnF,CAAC"}
@@ -31,6 +31,11 @@ interface RunPromptArgs {
31
31
  signal: AbortSignal;
32
32
  pluginRegistry?: import("../core/plugins/registry.js").PluginRegistry;
33
33
  homeDir?: string;
34
+ /** Yu, 2026-09-19: client had voice mode on when it sent the
35
+ * prompt. When true, defaultSystemPrompt gets a per-turn hint to
36
+ * inject the voice-summary fragment. Text-mode turns leave the
37
+ * prompt unchanged. */
38
+ voiceMode?: boolean;
34
39
  /**
35
40
  * Optional explicit session. When provided, runPrompt skips the
36
41
  * `ensureActiveSession(userId)` lookup and uses this session
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/chat/handler.ts"],"names":[],"mappings":"AAgDA,OAAO,EACL,mBAAmB,EACnB,cAAc,EAEd,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC9B,MAAM,uBAAuB,CAAC;AAI/B,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,GAC9B,CAAC;AAGF,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,wBAAwB,EACxB,2BAA2B,EAC3B,wBAAwB,EACxB,2BAA2B,EAC3B,yBAAyB,EACzB,6BAA6B,EAC7B,2BAA2B,EAC3B,4BAA4B,EAC5B,KAAK,oBAAoB,GAC1B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,EAOL,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAC;AA4B1B,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAOL,KAAK,WAAW,EAEjB,MAAM,eAAe,CAAC;AAWvB,OAAO,EAGL,KAAK,SAAS,EAEd,KAAK,cAAc,EAEpB,MAAM,kBAAkB,CAAC;AA6B1B,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,aAAa,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,CAAC;IAClB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,6BAA6B,EAAE,cAAc,CAAC;IACtE,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAyP7D;AAED,UAAU,aAAa;IACrB,GAAG,EAAE,aAAa,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;IAC/B,MAAM,EAAE,WAAW,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,6BAA6B,EAAE,cAAc,CAAC;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,OAAO,eAAe,EAAE,WAAW,CAAC;IAC9C;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA6jClE;AAmVD,wBAAgB,uBAAuB,CACrC,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EAC3D,2BAA2B,CAAC,EAAE,MAAM,GACnC,MAAM,CAuCR;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,oBAAoB,EAAE,MAAM,GAAG,MAAM,EAAE,CAgB3E;AAsCD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,aAAa,EAClB,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CA0Ef;AA4QD,wBAAgB,cAAc,IAAI,WAAW,EAAE,CAkC9C;AAGD,YAAY,EAAE,WAAW,EAAE,CAAC"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/chat/handler.ts"],"names":[],"mappings":"AAgDA,OAAO,EACL,mBAAmB,EACnB,cAAc,EAEd,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC9B,MAAM,uBAAuB,CAAC;AAI/B,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,GAC9B,CAAC;AAGF,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,wBAAwB,EACxB,2BAA2B,EAC3B,wBAAwB,EACxB,2BAA2B,EAC3B,yBAAyB,EACzB,6BAA6B,EAC7B,2BAA2B,EAC3B,4BAA4B,EAC5B,KAAK,oBAAoB,GAC1B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,EAOL,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAC;AA4B1B,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAOL,KAAK,WAAW,EAEjB,MAAM,eAAe,CAAC;AAWvB,OAAO,EAGL,KAAK,SAAS,EAEd,KAAK,cAAc,EAEpB,MAAM,kBAAkB,CAAC;AA6B1B,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,aAAa,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,CAAC;IAClB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,6BAA6B,EAAE,cAAc,CAAC;IACtE,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CA0P7D;AAED,UAAU,aAAa;IACrB,GAAG,EAAE,aAAa,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;IAC/B,MAAM,EAAE,WAAW,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,6BAA6B,EAAE,cAAc,CAAC;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;4BAGwB;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,OAAO,eAAe,EAAE,WAAW,CAAC;IAC9C;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA8jClE;AAmVD,wBAAgB,uBAAuB,CACrC,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EAC3D,2BAA2B,CAAC,EAAE,MAAM,GACnC,MAAM,CAuCR;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,oBAAoB,EAAE,MAAM,GAAG,MAAM,EAAE,CAgB3E;AAsCD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,aAAa,EAClB,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CA0Ef;AA4QD,wBAAgB,cAAc,IAAI,WAAW,EAAE,CAkC9C;AAGD,YAAY,EAAE,WAAW,EAAE,CAAC"}
@@ -213,6 +213,7 @@ export function attachChatHandler(opts) {
213
213
  pluginRegistry,
214
214
  homeDir,
215
215
  session: explicitSession,
216
+ voiceMode: parsed.voiceMode === true,
216
217
  }).catch((err) => {
217
218
  send({
218
219
  type: "stream_error",
@@ -598,7 +599,7 @@ export async function runPrompt(args) {
598
599
  replyStyle: mainConfig.overrides.replyStyle,
599
600
  userOnboarding: mainConfig.overrides.userOnboarding,
600
601
  customFragments: mainConfig.customFragments,
601
- });
602
+ }, { voiceMode: args.voiceMode === true });
602
603
  dumpSystemPrompt({ ctx, role: "main", userId, systemPrompt });
603
604
  // pi 0.80: the harness owns a `Models` instance and resolves auth
604
605
  // through it, replacing 0.79's `getApiKeyAndHeaders` callback. We