@markusylisiurunen/tau 0.3.75 → 0.3.77
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.
- package/dist/core/cli.js +0 -3
- package/dist/core/cli.js.map +1 -1
- package/dist/core/config/builtin_themes.js +0 -30
- package/dist/core/config/builtin_themes.js.map +1 -1
- package/dist/core/config/content_loader.js +10 -115
- package/dist/core/config/content_loader.js.map +1 -1
- package/dist/core/config/index.js +1 -1
- package/dist/core/config/index.js.map +1 -1
- package/dist/core/config/paths.js +0 -1
- package/dist/core/config/paths.js.map +1 -1
- package/dist/core/config/runtime.js +1 -1
- package/dist/core/config/runtime.js.map +1 -1
- package/dist/core/config/runtime_config_snapshot.js +0 -1
- package/dist/core/config/runtime_config_snapshot.js.map +1 -1
- package/dist/core/config/schema.js +8 -84
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/config/skills_loader.js +0 -8
- package/dist/core/config/skills_loader.js.map +1 -1
- package/dist/core/config/theme_variants.js.map +1 -1
- package/dist/core/config/virtual_bundle.js +1 -4
- package/dist/core/config/virtual_bundle.js.map +1 -1
- package/dist/core/debug.js +0 -2
- package/dist/core/debug.js.map +1 -1
- package/dist/core/index.js +0 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/models/catalog.js +2 -3
- package/dist/core/models/catalog.js.map +1 -1
- package/dist/core/personas.js +3 -81
- package/dist/core/personas.js.map +1 -1
- package/dist/core/static/index.js +0 -3
- package/dist/core/static/index.js.map +1 -1
- package/dist/core/static/tau_docs/config-reference.md +17 -59
- package/dist/core/static/tau_docs/configuration.md +5 -10
- package/dist/core/static/tau_docs/credentials.md +9 -28
- package/dist/core/static/tau_docs/image-generation.md +1 -1
- package/dist/core/static/tau_docs/node-sdk.md +1 -1
- package/dist/core/static/tau_docs/ownership-and-scope.md +3 -3
- package/dist/core/static/tau_docs/pdf-unpacking.md +1 -1
- package/dist/core/static/tau_docs/personas.md +3 -1
- package/dist/core/static/tau_docs/prompts-and-project-context.md +0 -10
- package/dist/core/static/tau_docs/remote-sessions.md +5 -21
- package/dist/core/static/tau_docs/session-protocol-methods.md +1 -7
- package/dist/core/static/tau_docs/skills.md +0 -22
- package/dist/core/static/tau_docs/speech-generation.md +1 -1
- package/dist/core/static/tau_docs/telegram.md +15 -17
- package/dist/core/static/tau_docs/troubleshooting.md +15 -15
- package/dist/core/static/tau_docs/tui.md +9 -24
- package/dist/core/telegram/adapter.js +120 -20
- package/dist/core/telegram/adapter.js.map +1 -1
- package/dist/core/telegram/cli.js +4 -5
- package/dist/core/telegram/cli.js.map +1 -1
- package/dist/core/telegram/runtime.js +3 -2
- package/dist/core/telegram/runtime.js.map +1 -1
- package/dist/core/telegram/session_manager.js +14 -0
- package/dist/core/telegram/session_manager.js.map +1 -1
- package/dist/core/telegram/tts.js +11 -9
- package/dist/core/telegram/tts.js.map +1 -1
- package/dist/core/tool/image_generate.js +37 -14
- package/dist/core/tool/image_generate.js.map +1 -1
- package/dist/core/tool/media.js +84 -4
- package/dist/core/tool/media.js.map +1 -1
- package/dist/core/tool/pdf_unpack.js +17 -10
- package/dist/core/tool/pdf_unpack.js.map +1 -1
- package/dist/core/tool/speech_generate.js +30 -13
- package/dist/core/tool/speech_generate.js.map +1 -1
- package/dist/core/utils/gemini_transcription.js +5 -95
- package/dist/core/utils/gemini_transcription.js.map +1 -1
- package/dist/core/utils/{gemini_speech.js → speech.js} +167 -428
- package/dist/core/utils/speech.js.map +1 -0
- package/dist/core/utils/speech_to_text.js +24 -52
- package/dist/core/utils/speech_to_text.js.map +1 -1
- package/dist/core/utils/speech_to_text_keywords.js +65 -0
- package/dist/core/utils/speech_to_text_keywords.js.map +1 -1
- package/dist/core/version.js +1 -1
- package/dist/execution/fly_sprite_execution_environment.js +4 -2
- package/dist/execution/fly_sprite_execution_environment.js.map +1 -1
- package/dist/main.js +6 -78
- package/dist/main.js.map +1 -1
- package/dist/protocol/index.d.ts +1 -1
- package/dist/protocol/index.js.map +1 -1
- package/dist/protocol/session_protocol.d.ts +2 -15
- package/dist/protocol/session_protocol.js +1 -20
- package/dist/protocol/session_protocol.js.map +1 -1
- package/dist/sdk/local_client.js +0 -6
- package/dist/sdk/local_client.js.map +1 -1
- package/dist/tui/chat_view.js +1 -3
- package/dist/tui/chat_view.js.map +1 -1
- package/dist/tui/listen_capture.js +7 -26
- package/dist/tui/listen_capture.js.map +1 -1
- package/dist/tui/session_chat_app.js +3 -19
- package/dist/tui/session_chat_app.js.map +1 -1
- package/dist/tui/session_chat_controller.js +58 -29
- package/dist/tui/session_chat_controller.js.map +1 -1
- package/dist/tui/speech_playback.js +10 -8
- package/dist/tui/speech_playback.js.map +1 -1
- package/dist/tui/ui/pending_messages.js +2 -1
- package/dist/tui/ui/pending_messages.js.map +1 -1
- package/package.json +3 -3
- package/dist/core/install/cli.js +0 -260
- package/dist/core/install/cli.js.map +0 -1
- package/dist/core/models/tau_model_overrides.js +0 -4
- package/dist/core/models/tau_model_overrides.js.map +0 -1
- package/dist/core/utils/gemini_speech.js.map +0 -1
- package/dist/core/utils/openai_transcription.js +0 -594
- package/dist/core/utils/openai_transcription.js.map +0 -1
- package/dist/execution/cloudflare_sandbox_execution_environment.js +0 -563
- package/dist/execution/cloudflare_sandbox_execution_environment.js.map +0 -1
- package/dist/execution/sandbox_tool_helpers.js +0 -30
- package/dist/execution/sandbox_tool_helpers.js.map +0 -1
- package/dist/starter/prompts/approval-review-branch.md +0 -7
- package/dist/starter/prompts/approval-review-current-changes.md +0 -7
- package/dist/starter/prompts/approval-review-last-commit.md +0 -7
- package/dist/starter/prompts/commit-all.md +0 -9
- package/dist/starter/prompts/commit-staged.md +0 -9
- package/dist/starter/prompts/guided-review-branch.md +0 -7
- package/dist/starter/prompts/guided-review-current-changes.md +0 -7
- package/dist/starter/prompts/guided-review-last-commit.md +0 -7
- package/dist/starter/prompts/plan.md +0 -7
- package/dist/starter/prompts/review-branch.md +0 -7
- package/dist/starter/prompts/review-current-changes.md +0 -7
- package/dist/starter/prompts/review-last-commit.md +0 -7
- package/dist/starter/prompts/rewrite-prompt.md +0 -89
- package/dist/starter/skills/approval-review/SKILL.md +0 -219
- package/dist/starter/skills/code-review/SKILL.md +0 -102
- package/dist/starter/skills/guided-review/SKILL.md +0 -152
- package/dist/starter/skills/plan/SKILL.md +0 -53
|
@@ -64,16 +64,6 @@ After recording succeeds, the picker becomes non-actionable and shows the prompt
|
|
|
64
64
|
|
|
65
65
|
Only the newest picker in a chat is active. Pickers are bound to the session that opened them and expire after selection, session replacement, or runner restart. Duplicate taps do not record the same picker twice. Prompt selection does not consume pending attachments or group context.
|
|
66
66
|
|
|
67
|
-
### Install starter prompts
|
|
68
|
-
|
|
69
|
-
Use `tau install` to copy Tau's starter content:
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
tau install --prompt commit-staged
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
The default target is `.tau/prompts/` under the current directory. Add `--global` for `~/.config/tau/prompts/`. Existing prompt files are skipped unless `--force` is supplied. Running `tau install` without `--prompt` or `--skill` installs all starter prompts and skills.
|
|
76
|
-
|
|
77
67
|
## `AGENTS.md` provides standing context
|
|
78
68
|
|
|
79
69
|
`AGENTS.md` is project context injected into the effective system prompt. It is suitable for repository conventions, architectural boundaries, verification commands, and instructions that should apply to every relevant request.
|
|
@@ -91,7 +91,7 @@ Remote `--new` supplies the conventional creation attribute `source: "tui"`. It
|
|
|
91
91
|
|
|
92
92
|
## Select an execution environment
|
|
93
93
|
|
|
94
|
-
A session can use a local host directory
|
|
94
|
+
A session can use a local host directory or an already-provisioned Fly Sprite. In all cases, the execution cwd is an absolute path inside that environment.
|
|
95
95
|
|
|
96
96
|
### Host-local directory
|
|
97
97
|
|
|
@@ -103,22 +103,6 @@ tau attach --new --cwd /srv/workspaces/tau ws://host.example:8787
|
|
|
103
103
|
|
|
104
104
|
The host process executes agent-visible filesystem and command operations on its own machine. Tau does not clone, pull, or provision a repository.
|
|
105
105
|
|
|
106
|
-
### Cloudflare Sandbox
|
|
107
|
-
|
|
108
|
-
The host must already have a named bridge in `cloudflareSandbox.bridges`, and the sandbox must already exist:
|
|
109
|
-
|
|
110
|
-
```sh
|
|
111
|
-
tau attach \
|
|
112
|
-
--new \
|
|
113
|
-
--execution-kind cloudflare-sandbox \
|
|
114
|
-
--cloudflare-bridge production \
|
|
115
|
-
--cloudflare-sandbox sandbox-42 \
|
|
116
|
-
--cwd /workspace/tau \
|
|
117
|
-
wss://tau.example.com
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
`--cloudflare-bridge` identifies host configuration. `--cloudflare-sandbox` identifies an existing sandbox reachable through that bridge. Tau does not create the sandbox or copy a repository into it.
|
|
121
|
-
|
|
122
106
|
### Fly Sprite
|
|
123
107
|
|
|
124
108
|
The host must already have a named API target in `flySprites.apis`, and the Sprite must already exist:
|
|
@@ -166,7 +150,7 @@ An attached session spans three logical machines even when two happen to share o
|
|
|
166
150
|
- command execution, platform, PATH, and runtime dependencies
|
|
167
151
|
- automatic-compaction archives and other target-side temporary files
|
|
168
152
|
|
|
169
|
-
This is why changing a host theme does not affect a remote TUI, why `!git status` runs against the execution environment, and why a custom diff application opens on the attaching machine. [Ownership and scope](ownership-and-scope.md) applies the same model across Tau.
|
|
153
|
+
This is why changing a host’s theme selection does not affect a remote TUI, why `!git status` runs against the execution environment, and why a custom diff application opens on the attaching machine. [Ownership and scope](ownership-and-scope.md) applies the same model across Tau.
|
|
170
154
|
|
|
171
155
|
## Reload or restart the correct process
|
|
172
156
|
|
|
@@ -178,7 +162,7 @@ Different changes have different owners:
|
|
|
178
162
|
| Effective model `apiKeys` in execution-environment or session configuration | Wait for idle, then run `/reload`; new sessions also resolve the current values. |
|
|
179
163
|
| Managed Codex auth changed with `tau auth` | No host restart; auth storage is read again on later credential resolutions. |
|
|
180
164
|
| Attaching themes, diff launcher, speech config, or configured client tools | Restart `tau attach`. |
|
|
181
|
-
| Host process environment variables, history target, WebSocket listener,
|
|
165
|
+
| Host process environment variables, history target, WebSocket listener, Fly API target, or host startup flags | Restart `tau serve`. |
|
|
182
166
|
| Host Tau package, built-in tools, protocol, session recovery code, or built-in documentation | Upgrade and restart the host. |
|
|
183
167
|
| TUI package, keybindings, rendering, local speech, or client-tool implementation | Upgrade and restart the attaching client. |
|
|
184
168
|
|
|
@@ -215,9 +199,9 @@ Avoid downgrading a host that has already written sessions with a newer storage
|
|
|
215
199
|
The host lists only stored sessions whose execution-environment kind it can currently restore. Confirm that:
|
|
216
200
|
|
|
217
201
|
- the connection uses the same host user and home directory as the original process
|
|
218
|
-
- the
|
|
202
|
+
- the Fly API id still exists in host configuration
|
|
219
203
|
- host credentials are available to the restarted process
|
|
220
|
-
- the target
|
|
204
|
+
- the target Sprite or local directory still exists
|
|
221
205
|
- the session was created on this host rather than another machine with a different store
|
|
222
206
|
|
|
223
207
|
Do not edit the session JSON to change environment identity. Restore the owning configuration or target instead.
|
|
@@ -63,12 +63,6 @@ Creates a session in one explicitly selected execution environment.
|
|
|
63
63
|
params: {
|
|
64
64
|
executionEnvironment:
|
|
65
65
|
| { kind: "local"; cwd: string; env?: Record<string, string> }
|
|
66
|
-
| {
|
|
67
|
-
kind: "cloudflare-sandbox";
|
|
68
|
-
bridgeId: string;
|
|
69
|
-
sandboxId: string;
|
|
70
|
-
cwd: string;
|
|
71
|
-
}
|
|
72
66
|
| {
|
|
73
67
|
kind: "fly-sprite";
|
|
74
68
|
apiId: string;
|
|
@@ -83,7 +77,7 @@ params: {
|
|
|
83
77
|
result: { sessionId: string }
|
|
84
78
|
```
|
|
85
79
|
|
|
86
|
-
`cwd` must be absolute inside the selected execution environment.
|
|
80
|
+
`cwd` must be absolute inside the selected execution environment. Fly Sprites must already exist and be reachable through a host-configured resolver. Tau does not provision a target or repository.
|
|
87
81
|
|
|
88
82
|
`attributes` is required, including when empty. It accepts at most 32 immutable pairs; keys are 1 to 64 characters and values at most 1,024 characters. Tau stores the supplied strings without inferring missing provenance. Conventional attributes and their use are covered in [sessions](sessions.md) and [history](history.md).
|
|
89
83
|
|
|
@@ -133,28 +133,6 @@ Skill references compose. If `release-check` instructs the agent to use `@@skill
|
|
|
133
133
|
|
|
134
134
|
After activation, the agent reads `SKILL.md` from the path in the discovered index. It should load only relevant files from `references/` or `assets/`, and prefer provided scripts when they implement the required workflow. Skills are treated as read-only unless the user explicitly asks to edit them.
|
|
135
135
|
|
|
136
|
-
## Installing starter skills
|
|
137
|
-
|
|
138
|
-
Tau ships starter prompts and skills that can be copied into a project:
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
tau install
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
By default this installs all starter prompts and skills under the current directory's `.tau/`. To install one skill:
|
|
145
|
-
|
|
146
|
-
```bash
|
|
147
|
-
tau install --skill code-review
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
To install under `~/.config/tau/` instead:
|
|
151
|
-
|
|
152
|
-
```bash
|
|
153
|
-
tau install --global --skill commit
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
Existing skill directories are skipped. `--force` replaces the entire same-named target directory, including files that are not present in the starter copy, so use it only when replacement is intended. `--prompt` and `--skill` are mutually exclusive. See [prompts and project context](prompts-and-project-context.md) for the prompt side of `tau install`.
|
|
157
|
-
|
|
158
136
|
## Applying changes and checking discovery
|
|
159
137
|
|
|
160
138
|
A running TUI session keeps its current content catalog and prompt context until it reloads. Run:
|
|
@@ -152,7 +152,7 @@ The output parent directory must exist. Neither the requested WAV nor `<output>.
|
|
|
152
152
|
|
|
153
153
|
Stdout on success is one JSON object with absolute `output`, `artifacts`, and the number of `batches`. The manifest contains the script text; keep artifacts private for sensitive scripts.
|
|
154
154
|
|
|
155
|
-
Failures exit nonzero and report the retained directory. Requests are not automatically retried because a failed request may already have incurred a charge.
|
|
155
|
+
Failures exit nonzero and identify invalid script fields, chunks, or turns before generation, or the failed batch and processing stage afterward. Existing output or artifact paths require a fresh `--output` path; preserve recovery artifacts. Errors after output preparation report the retained directory, which may be incomplete. If final publication fails, copy the completed `assembled.wav` to a fresh path instead of generating again. Requests are not automatically retried because a failed request may already have incurred a charge; another generation request may incur another charge.
|
|
156
156
|
|
|
157
157
|
To recover after a later batch fails, inspect the manifest's `completed` flags and chunk mapping. Generate only the unfinished chunks to a fresh output path, then assemble the completed PCM files from both runs in script order. Request stitching does not carry over between invocations.
|
|
158
158
|
|
|
@@ -224,25 +224,28 @@ Every Telegram turn starts with hidden `<system>` guidance identifying Telegram
|
|
|
224
224
|
|
|
225
225
|
Top-level and per-bot `systemMessage` values share a second hidden block after the default, in that order. Audio transcripts receive another warning that they may contain noise or errors. Unlike project context, hidden guidance is persisted with user turns and can appear in history, so never put credentials in configured messages.
|
|
226
226
|
|
|
227
|
-
The runner's speech
|
|
227
|
+
The runner's speech credentials and `speech.voiceId` are loaded at startup from its environment and normal Tau config, based on the runner process's startup `cwd`. Restart the runner after changing those settings.
|
|
228
228
|
|
|
229
229
|
## Chat commands
|
|
230
230
|
|
|
231
231
|
| Command | Behavior |
|
|
232
232
|
| --- | --- |
|
|
233
|
-
| `/use_<projectId>` |
|
|
234
|
-
| `/new` |
|
|
235
|
-
| `/status` | Reports session state, project, model, reasoning, context usage, cost, and goal
|
|
236
|
-
| `/
|
|
237
|
-
| `/
|
|
238
|
-
| `/
|
|
239
|
-
| `/
|
|
233
|
+
| `/use_<projectId>` | Selects the project for future `/new` sessions. |
|
|
234
|
+
| `/new` | Replaces the active session using the preferred project. |
|
|
235
|
+
| `/status` | Reports session state, project, model, reasoning, context usage, cost, and goal. |
|
|
236
|
+
| `/persona` | Switches persona while idle without losing history or starting a turn. |
|
|
237
|
+
| `/effort_low` | Selects low reasoning for future turns. |
|
|
238
|
+
| `/effort_medium` | Selects medium reasoning for future turns. |
|
|
239
|
+
| `/effort_high` | Selects high reasoning for future turns. |
|
|
240
|
+
| `/effort_xhigh` | Selects xhigh reasoning for future turns. |
|
|
240
241
|
| `/prompt` | [Records a saved prompt](prompts-and-project-context.md#telegram-prompt-picker) while idle, without starting a turn. |
|
|
241
|
-
| `/compact` | Runs summary-only
|
|
242
|
+
| `/compact` | Runs summary-only compaction while idle. |
|
|
242
243
|
| `/interrupt` | Interrupts the active Tau turn. |
|
|
243
244
|
| `/tts_on` | Enables a Gemini-generated voice note after each final assistant response. |
|
|
244
245
|
| `/tts_off` | Disables voice responses. |
|
|
245
246
|
|
|
247
|
+
[Persona](personas.md) selection is session-local; `/new` uses the project default.
|
|
248
|
+
|
|
246
249
|
Preferences persist per bot and chat across restarts, projects, and sessions. Project changes apply to `/new`, not the active session.
|
|
247
250
|
|
|
248
251
|
In groups, commands must explicitly mention the bot. Accepted forms include:
|
|
@@ -277,14 +280,9 @@ Uploads only queue files; captions are metadata. The next text or voice transcri
|
|
|
277
280
|
|
|
278
281
|
Files use native temporary directories in the execution environment; OS cleanup can invalidate their paths. Pending queues and group context clear on shutdown. File contents and metadata are untrusted.
|
|
279
282
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
- Gemini: `GEMINI_API_KEY`, then `apiKeys.google`
|
|
283
|
-
- OpenAI: `OPENAI_API_KEY`, then `apiKeys.openai`
|
|
284
|
-
|
|
285
|
-
Gemini extracts spelling hints with Gemini 3.8 Flash and transcribes uploaded audio verbatim with `gemini-3.5-transcribe`, using English (`en-US`) and Finnish (`fi-FI`) hints. It attempts remote file deletion afterward. OpenAI normalizes downloaded audio with runner-side `ffmpeg` and uploads it to `gpt-transcribe`. Missing keys prevent transcription, not file delivery. See [credentials](credentials.md).
|
|
283
|
+
Voice transcription uses Gemini with `GEMINI_API_KEY`, then `apiKeys.google`. Optional hints use GPT-6 Luna (reasoning disabled) and OpenAI credentials. Gemini transcribes audio verbatim with `gemini-3.5-transcribe`, using English (`en-US`) and Finnish (`fi-FI`) hints. It attempts remote file deletion. File delivery needs no Google key. See [credentials](credentials.md).
|
|
286
284
|
|
|
287
|
-
`/tts_on`
|
|
285
|
+
`/tts_on` rewrites text with `gpt-6-luna` (reasoning disabled) and generates speech with Eleven v4 Turbo. It requires both OpenAI and ElevenLabs credentials and runner `ffmpeg` with Opus. Voice notes use brisk delivery and 1.15× speed. See [`speech` configuration](config-reference.md#speech) for default and custom voice selection. Source and rewritten text each allow 10,000 Unicode characters; audio allows 32 MiB. Rewrite and job timeouts are one and five minutes. Jobs are ephemeral. Failure sends `voice response failed. please try again.` without affecting text; details stay in logs.
|
|
288
286
|
|
|
289
287
|
## Command client tools
|
|
290
288
|
|
|
@@ -335,7 +333,7 @@ Do not verify bot tokens, provider keys, Access secrets, transcript contents, or
|
|
|
335
333
|
|
|
336
334
|
**A persistent-directory session fails recovery.** Restore the configured directory and its original path, or return the project config to the snapshot's durable `cwd`. Tau does not migrate an existing session to a new persistent directory.
|
|
337
335
|
|
|
338
|
-
**Audio reports a missing key.** Set the credential for the
|
|
336
|
+
**Audio reports a missing key.** Set the credential for the Gemini in the runner process and restart it. A workspace-only environment change does not update the runner's startup speech configuration.
|
|
339
337
|
|
|
340
338
|
**A message sent during active work changes direction.** Telegram uses steering, not ordinary queueing, while a turn is running. Wait for completion before sending an independent next task, or use `/interrupt` to stop the current run first.
|
|
341
339
|
|
|
@@ -108,7 +108,7 @@ Providers must be known and model IDs are exact and case-sensitive. Check warnin
|
|
|
108
108
|
|
|
109
109
|
### Theme
|
|
110
110
|
|
|
111
|
-
Check the
|
|
111
|
+
Check the exact built-in theme ID and the client’s `defaultTheme`. Select `/theme:<id>` for the current run, or restart the TUI to apply a new startup default. A remote host’s theme selection does not affect an attached client.
|
|
112
112
|
|
|
113
113
|
## A tool or subagent is unavailable
|
|
114
114
|
|
|
@@ -148,13 +148,13 @@ A detached owning client makes its tools unavailable and cancels active calls. R
|
|
|
148
148
|
|
|
149
149
|
Find the process making the request:
|
|
150
150
|
|
|
151
|
-
| Operation
|
|
152
|
-
|
|
|
153
|
-
| Model, host `web`, `history`, or `nook`
|
|
154
|
-
| `/listen`, `/speak`, or TUI client tool
|
|
155
|
-
| Telegram bot or transcription
|
|
156
|
-
| `tau nook`, `tau history`, or PDF unpack
|
|
157
|
-
|
|
|
151
|
+
| Operation | Owner |
|
|
152
|
+
| ---------------------------------------- | ------------ |
|
|
153
|
+
| Model, host `web`, `history`, or `nook` | Host |
|
|
154
|
+
| `/listen`, `/speak`, or TUI client tool | Client |
|
|
155
|
+
| Telegram bot or transcription | Runner |
|
|
156
|
+
| `tau nook`, `tau history`, or PDF unpack | Invoking CLI |
|
|
157
|
+
| Fly Sprite resolution | Host startup |
|
|
158
158
|
|
|
159
159
|
A laptop variable does not update a remote host. Environment changes require an owner restart. Runtime `apiKeys` can reload, but only from an eligible execution-environment level and subject to feature precedence.
|
|
160
160
|
|
|
@@ -205,11 +205,11 @@ If attachment reports an unsupported protocol version or invalid peer message, u
|
|
|
205
205
|
|
|
206
206
|
For `tau attach --new`, `--cwd` must be an absolute path inside the selected execution environment. With the default local execution kind it is a host path, not a path on the attaching machine. Tau does not create the directory, clone a repository, or infer remote repository attributes.
|
|
207
207
|
|
|
208
|
-
For
|
|
208
|
+
For Fly Sprite creation, verify on the host that:
|
|
209
209
|
|
|
210
|
-
- the named
|
|
210
|
+
- the named API id exists in startup configuration;
|
|
211
211
|
- its credential is available to the host process;
|
|
212
|
-
- the named
|
|
212
|
+
- the named Sprite already exists;
|
|
213
213
|
- the absolute target `cwd` exists there; and
|
|
214
214
|
- the configured execution home matches the intended target account.
|
|
215
215
|
|
|
@@ -219,7 +219,7 @@ Without `--session` or `--new`, attach needs a TTY for its selector. In automati
|
|
|
219
219
|
|
|
220
220
|
## A session is missing, interrupted, or will not recover
|
|
221
221
|
|
|
222
|
-
A selector shows only sessions this host can load and restore. Confirm its machine, OS user, home, Tau version, resolver configuration, and target still match the creator. Another user's `~/.config/tau/sessions` is a different store. Restore missing
|
|
222
|
+
A selector shows only sessions this host can load and restore. Confirm its machine, OS user, home, Tau version, resolver configuration, and target still match the creator. Another user's `~/.config/tau/sessions` is a different store. Restore missing API definitions, credentials, local directory, or Sprite. Never edit session JSON to substitute a target or `cwd`.
|
|
223
223
|
|
|
224
224
|
Recovery returns idle, aborts unfinished turns, cancels running maintenance, removes live subagents, and blocks an active goal. Review the last assistant and tools, then safely check `!!pwd` and `!!git status --short`. Resume a goal only after understanding the stop. Retry continues current history without rerunning completed tools automatically.
|
|
225
225
|
|
|
@@ -273,13 +273,13 @@ Recovery needs the same `workspaceRoot`, project definitions, host home, and Tau
|
|
|
273
273
|
|
|
274
274
|
## Telegram audio or attachment processing fails
|
|
275
275
|
|
|
276
|
-
Telegram audio transcription uses
|
|
276
|
+
Telegram audio transcription uses Gemini and needs `GEMINI_API_KEY` or `apiKeys.google`. Set the credential for the runner process and restart it after changing the environment or configured key.
|
|
277
277
|
|
|
278
|
-
Distinguish download, materialization, format, and transcription errors. The reply or runner log states which stage failed. Confirm Telegram can deliver the file to the bot, the attachment type is supported, the runner can write its temporary directory, and
|
|
278
|
+
Distinguish download, materialization, format, and transcription errors. The reply or runner log states which stage failed. Confirm Telegram can deliver the file to the bot, the attachment type is supported, the runner can write its temporary directory, and Gemini accepts the media type. Do not log media bytes or transcripts merely to prove they exist.
|
|
279
279
|
|
|
280
280
|
Successful audio turns echo `transcribed: …` before submission. In groups, non-triggering audio can be buffered for later mentioned context. If that is inappropriate for the chat, narrow `allowedChatIds` or avoid enabling the group.
|
|
281
281
|
|
|
282
|
-
Outgoing `/tts_on` voice responses
|
|
282
|
+
Outgoing `/tts_on` voice responses need both `OPENAI_API_KEY` or `apiKeys.openai` for rewriting and `ELEVENLABS_API_KEY` or `apiKeys.elevenlabs` for synthesis. Check [`speech.voiceId`](config-reference.md#speech) if a configured voice is unavailable. They also require runner-side `ffmpeg` with Opus support. A generation or delivery failure sends `voice response failed. please try again.` while detailed diagnostics remain in runner logs. The original text response remains delivered.
|
|
283
283
|
|
|
284
284
|
## Telegram replies or notifications are delayed or missing
|
|
285
285
|
|
|
@@ -47,7 +47,7 @@ When Tau is idle, Enter and Ctrl+Enter both start a normal turn. While a turn is
|
|
|
47
47
|
|
|
48
48
|
- Enter queues the text as a new turn to run when the session becomes idle.
|
|
49
49
|
- Ctrl+Enter steers the active turn. Tau applies steering at a safe continuation boundary rather than injecting it into a model response or tool execution in progress.
|
|
50
|
-
- Alt+Up cancels all pending queued messages and steering that has not yet been applied, then restores their text to the editor. Multiple messages are separated with `---`.
|
|
50
|
+
- Alt+Up cancels all pending queued messages and steering that has not yet been applied, then restores their text to the editor. Multiple messages are separated with `---`. Hidden guidance, such as transcription warnings, stays hidden in the pending list and editor. Restoring messages preserves their guidance in order after any guidance already attached to the draft. Submission places all guidance at the beginning of the combined message. Editing a non-empty draft preserves it; clearing the draft clears it.
|
|
51
51
|
|
|
52
52
|
Pending input is session state shared by attached clients while the host remains alive. It is not durable across host restart or session recovery. A queued turn captures the persona, reasoning, tools, and model settings when that turn actually starts. Steering remains part of the active logical turn and keeps the settings captured when that turn began.
|
|
53
53
|
|
|
@@ -143,22 +143,7 @@ Direct commands require the session to be idle. In an attached TUI, `!pwd` repor
|
|
|
143
143
|
|
|
144
144
|
## Use themes
|
|
145
145
|
|
|
146
|
-
Themes belong to the TUI process and never become session state. Tau
|
|
147
|
-
|
|
148
|
-
- `~/.config/tau/themes/<id>.json` for user themes, when the TUI cwd is under the user’s home.
|
|
149
|
-
- `.tau/themes/<id>.json` at discovered project configuration levels, with the nearest project definition winning by id.
|
|
150
|
-
|
|
151
|
-
The filename is the theme id. A custom theme is a flat JSON object from semantic palette token names to colors:
|
|
152
|
-
|
|
153
|
-
```json
|
|
154
|
-
{
|
|
155
|
-
"brandAccent": "#8fb3ff",
|
|
156
|
-
"textMuted": "rgb(145, 151, 166)",
|
|
157
|
-
"feedbackError": "hsl(354, 70%, 72%)"
|
|
158
|
-
}
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
Colors accept `#rgb`, `#rrggbb`, `rgb(r, g, b)`, and `hsl(h, s%, l%)`. Unknown tokens, non-string values, and invalid colors are ignored. Missing tokens render without a custom color. Custom themes are single-variant; built-in themes adapt to detected terminal appearance.
|
|
146
|
+
Themes belong to the TUI process and never become session state. Tau’s built-in themes adapt to detected terminal appearance.
|
|
162
147
|
|
|
163
148
|
Set the startup theme with `defaultTheme` in the attaching client’s [configuration](configuration.md), or switch for the current run:
|
|
164
149
|
|
|
@@ -166,7 +151,7 @@ Set the startup theme with `defaultTheme` in the attaching client’s [configura
|
|
|
166
151
|
/theme:gold
|
|
167
152
|
```
|
|
168
153
|
|
|
169
|
-
Theme ids are exact and case-sensitive. `/theme` does not persist the selection.
|
|
154
|
+
Theme ids are exact and case-sensitive. `/theme` does not persist the selection. Restart the TUI to apply a new `defaultTheme`. A remote host’s theme selection does not affect an attached client.
|
|
170
155
|
|
|
171
156
|
## Review a diff
|
|
172
157
|
|
|
@@ -197,21 +182,21 @@ The TUI also advertises diff review as a client tool unless `--no-client-tools`
|
|
|
197
182
|
|
|
198
183
|
## Use speech
|
|
199
184
|
|
|
200
|
-
`/listen` and Ctrl+Y are currently macOS-only. Recording uses local `ffmpeg` with the AVFoundation audio input and stops when Ctrl+Y is pressed again, when Escape is pressed, or at the
|
|
185
|
+
`/listen` and Ctrl+Y are currently macOS-only. Recording uses local `ffmpeg` with the AVFoundation audio input and stops when Ctrl+Y is pressed again, when Escape is pressed, or at the 9-minute recording limit. The Gemini limit leaves time for setup and finalization within its 10-minute live session. Startup fails if the microphone produces no audio within 15 seconds. Stopping manually or reaching the recording limit finishes transcription and inserts the transcript at the cursor for review without submitting it.
|
|
201
186
|
|
|
202
|
-
Gemini displays a replaceable live preview in muted italic text at the original cursor position. Finalized dictation uses normal text once transcription settles. Its final transcript replaces the preview without duplication. Editing stays disabled from recording startup through finalization, and failure restores the original draft.
|
|
187
|
+
Gemini displays a replaceable live preview in muted italic text at the original cursor position. Finalized dictation uses normal text once transcription settles. Its final transcript replaces the preview without duplication. Submitting a draft containing finalized dictation includes hidden guidance that some or all of the message may have been transcribed from speech and may contain transcription errors. The guidance remains after edits and clears when the draft is emptied. Editing stays disabled from recording startup through finalization, and failure restores the original draft. Recording and retries cannot start while another editor update is pending.
|
|
203
188
|
|
|
204
|
-
If transcription fails, Tau retains the local WAV and reports its path. Run `/listen retry` to transcribe the same audio again with the current
|
|
189
|
+
If transcription fails, Tau retains the local WAV and reports its path. Run `/listen retry` to transcribe the same audio again with Gemini and the current conversation context, or `/listen discard` to delete it. Retained recordings longer than 20 minutes are rejected rather than sent to the provider. A replacement recording deletes the retained file only after the new capture starts producing audio. Tau keeps at most one failed recording, and exiting leaves that file at the reported path for manual recovery. For live transcription, GPT-6 Luna extracts spelling hints from recent conversation while recording begins, with reasoning disabled. Tau buffers audio until the transcription session is ready and then streams subsequent audio. Gemini uses `gemini-3.5-transcribe-live` in verbatim mode with English (`en-US`) and Finnish (`fi-FI`) language hints. Missing OpenAI credentials or hint extraction failure does not fail transcription. On retry, Gemini uploads the retained recording for `gemini-3.5-transcribe` verbatim transcription with the same language hints and attempts to delete the remote file afterward.
|
|
205
190
|
|
|
206
|
-
Install `ffmpeg
|
|
191
|
+
Install `ffmpeg`:
|
|
207
192
|
|
|
208
193
|
```sh
|
|
209
194
|
brew install ffmpeg
|
|
210
195
|
```
|
|
211
196
|
|
|
212
|
-
|
|
197
|
+
Speech transcription uses Gemini and requires `GEMINI_API_KEY` or `apiKeys.google`. Credentials are read by the TUI process, including during remote attachment.
|
|
213
198
|
|
|
214
|
-
`/speak` is also macOS-only. It rewrites the last assistant response
|
|
199
|
+
`/speak` is also macOS-only. It rewrites the last assistant response with GPT-6 Luna (reasoning disabled), streams Eleven v4 Turbo audio, and plays it at 1.15× speed through the local `ffplay` command included with `ffmpeg`. It requires both `OPENAI_API_KEY` or `apiKeys.openai` for rewriting and `ELEVENLABS_API_KEY` or `apiKeys.elevenlabs` for synthesis, runs only while the session is idle, and can be stopped with Escape. Longer responses are divided into balanced segments targeting at most two minutes of generated speech each. Speech source and rewritten text are limited to 10,000 Unicode characters, and generation stops after 32 MiB of raw audio. Voice lookups and synthesis requests have a two-minute deadline. See [`speech` configuration](config-reference.md#speech) for the default Maisie/Caleb lookup and custom voice selection.
|
|
215
200
|
|
|
216
201
|
## Reload the right component
|
|
217
202
|
|
|
@@ -777,9 +777,10 @@ class TelegramAdapterImpl {
|
|
|
777
777
|
botUsername;
|
|
778
778
|
pollIntervalMs;
|
|
779
779
|
requestTimeoutSeconds;
|
|
780
|
-
speechToTextProvider;
|
|
781
780
|
geminiApiKey;
|
|
782
|
-
|
|
781
|
+
openAIApiKey;
|
|
782
|
+
elevenLabsApiKey;
|
|
783
|
+
speechVoiceId;
|
|
783
784
|
speechToTextDeps;
|
|
784
785
|
sessionManager;
|
|
785
786
|
projectPreferences;
|
|
@@ -794,6 +795,7 @@ class TelegramAdapterImpl {
|
|
|
794
795
|
commandHandlers;
|
|
795
796
|
callbackActionHandlers;
|
|
796
797
|
abortController = new AbortController();
|
|
798
|
+
personaPickers = new Map();
|
|
797
799
|
promptPickers = new Map();
|
|
798
800
|
activeSessionsByChat = new Map();
|
|
799
801
|
sessionsByChat = new Map();
|
|
@@ -832,9 +834,10 @@ class TelegramAdapterImpl {
|
|
|
832
834
|
this.botUsername = options.botUsername;
|
|
833
835
|
this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
834
836
|
this.requestTimeoutSeconds = options.requestTimeoutSeconds ?? DEFAULT_REQUEST_TIMEOUT_SECONDS;
|
|
835
|
-
this.speechToTextProvider = options.speechToTextProvider ?? "openai";
|
|
836
837
|
this.geminiApiKey = options.geminiApiKey?.trim() || undefined;
|
|
837
|
-
this.
|
|
838
|
+
this.openAIApiKey = options.openAIApiKey?.trim() || undefined;
|
|
839
|
+
this.elevenLabsApiKey = options.elevenLabsApiKey?.trim() || undefined;
|
|
840
|
+
this.speechVoiceId = options.speechVoiceId;
|
|
838
841
|
this.speechToTextDeps = options.speechToTextDeps;
|
|
839
842
|
this.sessionManager = options.sessionManager;
|
|
840
843
|
this.projectPreferences = options.projectPreferences;
|
|
@@ -912,6 +915,11 @@ class TelegramAdapterImpl {
|
|
|
912
915
|
callbackAction: "status",
|
|
913
916
|
handler: async (chatId) => this.handleStatus(chatId),
|
|
914
917
|
},
|
|
918
|
+
{
|
|
919
|
+
command: "/persona",
|
|
920
|
+
description: "switch the active session persona",
|
|
921
|
+
handler: async (chatId, args) => this.handlePersona(chatId, args),
|
|
922
|
+
},
|
|
915
923
|
{
|
|
916
924
|
command: "/prompt",
|
|
917
925
|
description: "add a saved prompt without starting a turn",
|
|
@@ -1458,6 +1466,10 @@ class TelegramAdapterImpl {
|
|
|
1458
1466
|
await handler(chatId, args, sourceMessageId);
|
|
1459
1467
|
}
|
|
1460
1468
|
async handleCallback(chatId, callbackData, messageId) {
|
|
1469
|
+
if (callbackData.startsWith("persona:")) {
|
|
1470
|
+
await this.handlePersonaCallback(chatId, callbackData, messageId);
|
|
1471
|
+
return true;
|
|
1472
|
+
}
|
|
1461
1473
|
if (callbackData.startsWith("prompt:")) {
|
|
1462
1474
|
await this.handlePromptCallback(chatId, callbackData, messageId);
|
|
1463
1475
|
return true;
|
|
@@ -1528,6 +1540,99 @@ class TelegramAdapterImpl {
|
|
|
1528
1540
|
await this.reply(chatId, `failed to save voice response preference: ${error instanceof Error ? error.message : String(error)}`);
|
|
1529
1541
|
}
|
|
1530
1542
|
}
|
|
1543
|
+
async handlePersona(chatId, args) {
|
|
1544
|
+
if (args.length > 0) {
|
|
1545
|
+
await this.reply(chatId, "use /persona to choose a persona.");
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
const session = await this.requireActiveSession(chatId);
|
|
1549
|
+
if (!session)
|
|
1550
|
+
return;
|
|
1551
|
+
try {
|
|
1552
|
+
const snapshot = await this.getSessionManagerForChat(chatId).getSessionSnapshot(session.id);
|
|
1553
|
+
if (!snapshot) {
|
|
1554
|
+
await this.reply(chatId, "the session is still preparing.");
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
const personas = snapshot.catalog.personas;
|
|
1558
|
+
if (personas.length === 0) {
|
|
1559
|
+
await this.reply(chatId, "this session has no personas.");
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
this.personaPickers.set(chatId, {
|
|
1563
|
+
token: randomUUID(),
|
|
1564
|
+
sessionId: session.id,
|
|
1565
|
+
personas,
|
|
1566
|
+
currentPersonaId: snapshot.settings.personaId,
|
|
1567
|
+
});
|
|
1568
|
+
await this.showPersonaPage(chatId, 0);
|
|
1569
|
+
}
|
|
1570
|
+
catch (error) {
|
|
1571
|
+
await this.reply(chatId, this.formatManagerError(error));
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
async showPersonaPage(chatId, page, messageId) {
|
|
1575
|
+
const picker = this.personaPickers.get(chatId);
|
|
1576
|
+
if (!picker)
|
|
1577
|
+
return;
|
|
1578
|
+
const start = page * 20;
|
|
1579
|
+
const keyboard = picker.personas.slice(start, start + 20).map((persona, index) => [
|
|
1580
|
+
{
|
|
1581
|
+
text: `${persona.label ?? persona.id}${persona.id === picker.currentPersonaId ? " (current)" : ""}`,
|
|
1582
|
+
callback_data: `persona:${picker.token}:select:${start + index}`,
|
|
1583
|
+
},
|
|
1584
|
+
]);
|
|
1585
|
+
const navigation = [];
|
|
1586
|
+
if (page > 0)
|
|
1587
|
+
navigation.push({
|
|
1588
|
+
text: "previous",
|
|
1589
|
+
callback_data: `persona:${picker.token}:page:${page - 1}`,
|
|
1590
|
+
});
|
|
1591
|
+
if (start + 20 < picker.personas.length)
|
|
1592
|
+
navigation.push({ text: "next", callback_data: `persona:${picker.token}:page:${page + 1}` });
|
|
1593
|
+
if (navigation.length > 0)
|
|
1594
|
+
keyboard.push(navigation);
|
|
1595
|
+
const text = "choose a persona for this session. switching is available only while Tau is idle.";
|
|
1596
|
+
const replyMarkup = { inline_keyboard: keyboard };
|
|
1597
|
+
if (messageId !== undefined) {
|
|
1598
|
+
await this.sendWithRetry((signal) => this.api.editMessage(chatId, messageId, text, { replyMarkup, signal }));
|
|
1599
|
+
}
|
|
1600
|
+
else {
|
|
1601
|
+
await this.reply(chatId, text, { replyMarkup });
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
async handlePersonaCallback(chatId, data, messageId) {
|
|
1605
|
+
const match = /^persona:([^:]+):(select|page):(0|[1-9][0-9]*)$/.exec(data);
|
|
1606
|
+
const picker = this.personaPickers.get(chatId);
|
|
1607
|
+
if (!match ||
|
|
1608
|
+
!picker ||
|
|
1609
|
+
picker.token !== match[1] ||
|
|
1610
|
+
this.getActiveSession(chatId)?.id !== picker.sessionId ||
|
|
1611
|
+
messageId === undefined) {
|
|
1612
|
+
await this.reply(chatId, "this persona picker has expired. use /persona to open a new one.");
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
const index = Number(match[3]);
|
|
1616
|
+
if (match[2] === "page") {
|
|
1617
|
+
if (index * 20 < picker.personas.length)
|
|
1618
|
+
await this.showPersonaPage(chatId, index, messageId);
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
const persona = picker.personas[index];
|
|
1622
|
+
if (!persona)
|
|
1623
|
+
return;
|
|
1624
|
+
let snapshot;
|
|
1625
|
+
try {
|
|
1626
|
+
snapshot = await this.getSessionManagerForChat(chatId).setPersona(picker.sessionId, persona.id);
|
|
1627
|
+
}
|
|
1628
|
+
catch (error) {
|
|
1629
|
+
await this.reply(chatId, this.formatManagerError(error));
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
this.personaPickers.delete(chatId);
|
|
1633
|
+
const text = `switched to ${persona.label ?? persona.id}. this session is using ${snapshot.bootstrap.model.name || snapshot.bootstrap.model.id} with ${snapshot.settings.reasoning ?? "none"} reasoning.`;
|
|
1634
|
+
await this.sendWithRetry((signal) => this.api.editMessage(chatId, messageId, text, { signal }));
|
|
1635
|
+
}
|
|
1531
1636
|
async handlePrompt(chatId, args) {
|
|
1532
1637
|
if (args.length > 0) {
|
|
1533
1638
|
await this.reply(chatId, "use /prompt to choose a saved prompt.");
|
|
@@ -1867,20 +1972,10 @@ class TelegramAdapterImpl {
|
|
|
1867
1972
|
}
|
|
1868
1973
|
}
|
|
1869
1974
|
getSpeechToTextApiKey() {
|
|
1870
|
-
|
|
1871
|
-
case "gemini":
|
|
1872
|
-
return this.geminiApiKey;
|
|
1873
|
-
case "openai":
|
|
1874
|
-
return this.openaiApiKey;
|
|
1875
|
-
}
|
|
1975
|
+
return this.geminiApiKey;
|
|
1876
1976
|
}
|
|
1877
1977
|
getSpeechToTextApiKeyErrorMessage(action) {
|
|
1878
|
-
|
|
1879
|
-
case "gemini":
|
|
1880
|
-
return `set GEMINI_API_KEY or apiKeys.google to ${action}`;
|
|
1881
|
-
case "openai":
|
|
1882
|
-
return `set OPENAI_API_KEY or apiKeys.openai to ${action}`;
|
|
1883
|
-
}
|
|
1978
|
+
return `set GEMINI_API_KEY or apiKeys.google to ${action}`;
|
|
1884
1979
|
}
|
|
1885
1980
|
async transcribeTelegramVoice(chatId, message, options = {}) {
|
|
1886
1981
|
if (message.durationMs > SPEECH_TO_TEXT_CLIENT_MAX_DURATION_MS) {
|
|
@@ -1901,9 +1996,9 @@ class TelegramAdapterImpl {
|
|
|
1901
1996
|
let transcript;
|
|
1902
1997
|
try {
|
|
1903
1998
|
const transcription = createSpeechToTextTranscription({
|
|
1904
|
-
provider: this.speechToTextProvider,
|
|
1905
1999
|
mode: "file",
|
|
1906
2000
|
apiKey,
|
|
2001
|
+
openAIApiKey: this.openAIApiKey,
|
|
1907
2002
|
context: await this.resolveSpeechToTextContext(chatId),
|
|
1908
2003
|
deps: {
|
|
1909
2004
|
...this.speechToTextDeps,
|
|
@@ -2234,11 +2329,16 @@ class TelegramAdapterImpl {
|
|
|
2234
2329
|
}, TELEGRAM_TTS_JOB_TIMEOUT_MS);
|
|
2235
2330
|
timeout.unref?.();
|
|
2236
2331
|
try {
|
|
2237
|
-
if (!this.
|
|
2238
|
-
throw new Error("missing
|
|
2332
|
+
if (!this.openAIApiKey) {
|
|
2333
|
+
throw new Error("missing OpenAI credential for Telegram voice responses");
|
|
2334
|
+
}
|
|
2335
|
+
if (!this.elevenLabsApiKey) {
|
|
2336
|
+
throw new Error("missing ElevenLabs credential for Telegram voice responses");
|
|
2239
2337
|
}
|
|
2240
2338
|
const voice = await this.generateVoice({
|
|
2241
|
-
|
|
2339
|
+
openAIApiKey: this.openAIApiKey,
|
|
2340
|
+
elevenLabsApiKey: this.elevenLabsApiKey,
|
|
2341
|
+
voiceId: this.speechVoiceId,
|
|
2242
2342
|
sourceText,
|
|
2243
2343
|
fetchImpl: this.fetchImpl,
|
|
2244
2344
|
signal: AbortSignal.any([this.abortController.signal, timeoutController.signal]),
|