@markusylisiurunen/tau 0.3.50 → 0.3.52

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 (105) hide show
  1. package/dist/code_mode/command.js +26 -14
  2. package/dist/code_mode/command.js.map +1 -1
  3. package/dist/code_mode/index.d.ts +2 -1
  4. package/dist/code_mode/index.js +1 -0
  5. package/dist/code_mode/index.js.map +1 -1
  6. package/dist/core/cli.js +0 -11
  7. package/dist/core/cli.js.map +1 -1
  8. package/dist/core/client_tools/command_client_tools.js +236 -151
  9. package/dist/core/client_tools/command_client_tools.js.map +1 -1
  10. package/dist/core/config/schema.js +8 -2
  11. package/dist/core/config/schema.js.map +1 -1
  12. package/dist/core/history/history_manager.js +73 -7
  13. package/dist/core/history/history_manager.js.map +1 -1
  14. package/dist/core/history/local_history_store.js +73 -2
  15. package/dist/core/history/local_history_store.js.map +1 -1
  16. package/dist/core/history/remote_history_client.js +36 -6
  17. package/dist/core/history/remote_history_client.js.map +1 -1
  18. package/dist/core/modes/index.js +0 -1
  19. package/dist/core/modes/index.js.map +1 -1
  20. package/dist/core/static/tau_docs/client-tools.md +212 -12
  21. package/dist/core/static/tau_docs/config-reference.md +1 -1
  22. package/dist/core/static/tau_docs/configuration.md +1 -1
  23. package/dist/core/static/tau_docs/credentials.md +4 -4
  24. package/dist/core/static/tau_docs/history.md +6 -4
  25. package/dist/core/static/tau_docs/index.md +1 -1
  26. package/dist/core/static/tau_docs/node-sdk.md +33 -26
  27. package/dist/core/static/tau_docs/ownership-and-scope.md +3 -4
  28. package/dist/core/static/tau_docs/prompts-and-project-context.md +2 -2
  29. package/dist/core/static/tau_docs/remote-sessions.md +4 -47
  30. package/dist/core/static/tau_docs/security.md +2 -6
  31. package/dist/core/static/tau_docs/session-protocol-methods.md +48 -7
  32. package/dist/core/static/tau_docs/session-protocol.md +18 -18
  33. package/dist/core/static/tau_docs/sessions.md +6 -5
  34. package/dist/core/static/tau_docs/telegram.md +8 -4
  35. package/dist/core/static/tau_docs/tools.md +1 -1
  36. package/dist/core/static/tau_docs/troubleshooting.md +3 -5
  37. package/dist/core/static/tau_docs/tui.md +4 -6
  38. package/dist/core/telegram/adapter.js +226 -18
  39. package/dist/core/telegram/adapter.js.map +1 -1
  40. package/dist/core/telegram/config.js +1 -1
  41. package/dist/core/telegram/config.js.map +1 -1
  42. package/dist/core/telegram/project_preferences.js +42 -12
  43. package/dist/core/telegram/project_preferences.js.map +1 -1
  44. package/dist/core/telegram/runtime.js +6 -1
  45. package/dist/core/telegram/runtime.js.map +1 -1
  46. package/dist/core/telegram/session_manager.js +50 -4
  47. package/dist/core/telegram/session_manager.js.map +1 -1
  48. package/dist/core/telegram/tts.js +137 -0
  49. package/dist/core/telegram/tts.js.map +1 -0
  50. package/dist/core/tools/execution_backend.js +13 -3
  51. package/dist/core/tools/execution_backend.js.map +1 -1
  52. package/dist/core/tools/presentation.js +55 -17
  53. package/dist/core/tools/presentation.js.map +1 -1
  54. package/dist/core/utils/gemini_speech.js +123 -42
  55. package/dist/core/utils/gemini_speech.js.map +1 -1
  56. package/dist/core/utils/gemini_transcription.js +1 -1
  57. package/dist/core/version.js +1 -1
  58. package/dist/execution/cloudflare_sandbox_execution_environment.js +2 -2
  59. package/dist/execution/cloudflare_sandbox_execution_environment.js.map +1 -1
  60. package/dist/execution/fly_sprite_execution_environment.js +2 -2
  61. package/dist/execution/fly_sprite_execution_environment.js.map +1 -1
  62. package/dist/host/client_tool_broker.js +71 -17
  63. package/dist/host/client_tool_broker.js.map +1 -1
  64. package/dist/host/local_session_host.js +2 -2
  65. package/dist/host/local_session_host.js.map +1 -1
  66. package/dist/host/session_host.js.map +1 -1
  67. package/dist/host/session_protocol_handler.js +17 -6
  68. package/dist/host/session_protocol_handler.js.map +1 -1
  69. package/dist/main.js +18 -92
  70. package/dist/main.js.map +1 -1
  71. package/dist/protocol/session_protocol.d.ts +22 -2
  72. package/dist/protocol/session_protocol.js +47 -3
  73. package/dist/protocol/session_protocol.js.map +1 -1
  74. package/dist/sdk/client_tool_command.d.ts +41 -13
  75. package/dist/sdk/client_tool_command.js +157 -39
  76. package/dist/sdk/client_tool_command.js.map +1 -1
  77. package/dist/sdk/client_tool_presentation.d.ts +17 -0
  78. package/dist/sdk/client_tool_presentation.js +9 -0
  79. package/dist/sdk/client_tool_presentation.js.map +1 -0
  80. package/dist/sdk/code_mode.js +10 -1
  81. package/dist/sdk/code_mode.js.map +1 -1
  82. package/dist/sdk/index.d.ts +5 -4
  83. package/dist/sdk/index.js +2 -1
  84. package/dist/sdk/index.js.map +1 -1
  85. package/dist/sdk/session.js +28 -9
  86. package/dist/sdk/session.js.map +1 -1
  87. package/dist/sdk/types.d.ts +11 -1
  88. package/dist/transport/errors.d.ts +0 -11
  89. package/dist/transport/errors.js +0 -12
  90. package/dist/transport/errors.js.map +1 -1
  91. package/dist/transport/index.d.ts +1 -3
  92. package/dist/transport/index.js +1 -2
  93. package/dist/transport/index.js.map +1 -1
  94. package/dist/tui/session_chat_app.js +28 -15
  95. package/dist/tui/session_chat_app.js.map +1 -1
  96. package/dist/tui/session_chat_controller.js +0 -51
  97. package/dist/tui/session_chat_controller.js.map +1 -1
  98. package/dist/tui/speech_playback.js +3 -3
  99. package/dist/tui/speech_playback.js.map +1 -1
  100. package/package.json +2 -2
  101. package/dist/core/modes/rpc_server.js +0 -105
  102. package/dist/core/modes/rpc_server.js.map +0 -1
  103. package/dist/transport/stdio_session_transport.d.ts +0 -48
  104. package/dist/transport/stdio_session_transport.js +0 -338
  105. package/dist/transport/stdio_session_transport.js.map +0 -1
@@ -117,23 +117,222 @@ Diff-tool launcher settings are separate from command client-tool definitions. T
117
117
 
118
118
  ## Implement a command tool with Tau's helper
119
119
 
120
- Command client tools use Tau's version 3 bidirectional NDJSON protocol over stdin and stdout. Use the exported helper instead of implementing framing manually:
120
+ Command client tools use Tau's version 4 bidirectional NDJSON protocol over stdin and stdout. Use the exported helper instead of implementing framing manually:
121
121
 
122
122
  ```ts
123
- import { runTauClientToolCommand } from "@markusylisiurunen/tau/code-mode";
123
+ import {
124
+ runTauClientToolCommand,
125
+ truncateTauClientToolText,
126
+ } from "@markusylisiurunen/tau/code-mode";
127
+
128
+ await runTauClientToolCommand({
129
+ name: "notify_desktop",
130
+ describe(args) {
131
+ const input = args as { title: string; message: string };
132
+ return {
133
+ subject: truncateTauClientToolText(input.title),
134
+ };
135
+ },
136
+ async execute(args, context) {
137
+ const input = args as { title: string; message: string };
138
+ context.signal.throwIfAborted();
139
+
140
+ await showNotification(input.title, input.message, context.signal);
141
+ return {
142
+ content: "Notification displayed.",
143
+ presentation: {
144
+ subject: truncateTauClientToolText(input.title),
145
+ },
146
+ };
147
+ },
148
+ });
149
+ ```
150
+
151
+ `describe` is optional. When present, it runs before acknowledgement and may return a partial running presentation containing `subject`, `subjectWrap`, `details`, or `metadata`. The execution result may independently include the same partial shape for the terminal card. Tau supplies every omitted field, owns lifecycle actions and the operation derived from the registered tool name, and renders a complete fallback when execution ends without a result.
152
+
153
+ Tau preserves every explicit presentation field up to the protocol safety limits; it does not apply display truncation or normalize client text. `truncateTauClientToolText` provides optional caller-controlled `maxLines`, `maxLineChars`, and `head` or `middle` truncation. Its defaults match Tau's concise subject policy, but callers may select larger or smaller positive limits.
154
+
155
+ For a subject, use the returned string directly. For a block of detail text, split the returned string on `\n` and map each line to one `details` entry:
156
+
157
+ ```ts
158
+ const details = truncateTauClientToolText(output, {
159
+ maxLines: 7,
160
+ maxLineChars: 512,
161
+ strategy: "middle",
162
+ })
163
+ .split("\n")
164
+ .map((text) => ({ text }));
165
+ ```
166
+
167
+ Each detail or metadata entry is a single protocol line, so use `maxLines: 1` when assigning the helper's result directly to one entry. The helper shapes text for presentation; the protocol byte and collection limits still apply. Empty detail or metadata arrays explicitly suppress that phase's defaults.
168
+
169
+ `runTauClientToolCommand` reads the preparation, writes readiness and any running presentation, waits until the host accepts the call, then provides the standard execution context and writes the final result. It handles execution-environment request and cancellation framing and reacts to `SIGINT`, `SIGTERM`, and protocol input closure by aborting the handler.
170
+
171
+ Reserve stdout for the helper's protocol. Write diagnostics to stderr. Return a string or `{ content, presentation? }` for success. Return `{ ok: false, error, presentation? }` for a structured tool failure. Successful content and failure text become model-visible tool results.
172
+
173
+ ### Version 4 frame reference
174
+
175
+ The shapes below use TypeScript notation. Serialize each frame as one exact JSON object followed by a newline; unknown fields are invalid.
124
176
 
125
- await runTauClientToolCommand(async (args, context) => {
126
- const input = args as { title: string; message: string };
127
- context.signal.throwIfAborted();
177
+ Tau starts the exchange by writing:
128
178
 
129
- await showNotification(input.title, input.message, context.signal);
130
- return { content: "Notification displayed." };
179
+ - `{ version: 4, type: "prepare", sessionId, agentId, callId, toolName, arguments }` exactly once. The identity fields are non-empty strings and `arguments` is the validated model input.
180
+ - `{ version: 4, type: "execute" }` after accepting the command's ready frame. This authorizes execution.
181
+
182
+ The command writes:
183
+
184
+ - `{ version: 4, type: "ready", presentation?: PresentationOverride }` exactly once after preparation.
185
+ - `{ version: 4, type: "result", ok: true, content, presentation?: PresentationOverride }` or `{ version: 4, type: "result", ok: false, error, presentation?: PresentationOverride }` exactly once after authorization. `content` and `error` are strings.
186
+
187
+ During authorized execution, the command may write `{ version: 4, type: "exec", requestId, command, options }`. The non-empty `command` string runs in the session execution environment. `options` is required and may contain `args: string[]`, `env: Record<string, string>`, base64-encoded string `stdinBase64`, string `cwd`, positive integer `timeoutMs`, and positive integer `maxCaptureBytes`.
188
+
189
+ Tau answers with the same `requestId` and either:
190
+
191
+ - `{ version: 4, type: "exec.result", requestId, ok: true, result: ExecResult }`
192
+ - `{ version: 4, type: "exec.result", requestId, ok: false, error: string }`
193
+
194
+ `ExecResult` contains string `output`, `stdout`, and `stderr`; nullable `exitCode` and `closeSignal`; and boolean `truncated`, `timedOut`, and `aborted`. A command may cancel one unresolved request with `{ version: 4, type: "exec.cancel", requestId }`. Request IDs must be non-empty strings and cannot be reused within a call.
195
+
196
+ ### Implement the protocol directly in JavaScript
197
+
198
+ A JavaScript command can implement the version 4 handshake using only Node.js built-ins, without importing Tau or the code-mode package:
199
+
200
+ ```js
201
+ #!/usr/bin/env node
202
+
203
+ import { arch, platform, release } from "node:os";
204
+ import { createInterface } from "node:readline";
205
+
206
+ const lines = createInterface({
207
+ input: process.stdin,
208
+ crlfDelay: Number.POSITIVE_INFINITY,
131
209
  });
210
+ const input = lines[Symbol.asyncIterator]();
211
+
212
+ async function readFrame() {
213
+ const next = await input.next();
214
+ if (next.done) throw new Error("Tau closed the command protocol");
215
+ return JSON.parse(next.value);
216
+ }
217
+
218
+ function writeFrame(frame) {
219
+ process.stdout.write(`${JSON.stringify(frame)}\n`);
220
+ }
221
+
222
+ const prepare = await readFrame();
223
+ if (
224
+ prepare.version !== 4 ||
225
+ prepare.type !== "prepare" ||
226
+ prepare.toolName !== "system_info"
227
+ ) {
228
+ throw new Error("Invalid system_info preparation");
229
+ }
230
+
231
+ writeFrame({
232
+ version: 4,
233
+ type: "ready",
234
+ presentation: {
235
+ subject: "local system",
236
+ },
237
+ });
238
+
239
+ const execute = await readFrame();
240
+ if (execute.version !== 4 || execute.type !== "execute") {
241
+ throw new Error("Invalid system_info authorization");
242
+ }
243
+
244
+ writeFrame({
245
+ version: 4,
246
+ type: "exec",
247
+ requestId: "git-status",
248
+ command: "git status --short",
249
+ options: {
250
+ maxCaptureBytes: 256 * 1024,
251
+ },
252
+ });
253
+
254
+ const response = await readFrame();
255
+ if (
256
+ response.version !== 4 ||
257
+ response.type !== "exec.result" ||
258
+ response.requestId !== "git-status"
259
+ ) {
260
+ throw new Error("Invalid execution-environment response");
261
+ }
262
+ if (!response.ok) throw new Error(response.error);
263
+
264
+ const localSystem = `${platform()} ${release()} ${arch()}`;
265
+ const workspaceStatus =
266
+ response.result.output.trim() || "Working tree is clean.";
267
+ writeFrame({
268
+ version: 4,
269
+ type: "result",
270
+ ok: true,
271
+ content: `${localSystem}\n\n${workspaceStatus}`,
272
+ presentation: {
273
+ subject: "local system",
274
+ },
275
+ });
276
+
277
+ lines.close();
132
278
  ```
133
279
 
134
- `runTauClientToolCommand` reads the invocation, provides a standard context, handles execution-environment request and cancellation framing, and writes the final result. It also reacts to `SIGINT`, `SIGTERM`, and protocol input closure by aborting the handler.
280
+ The `exec` frame asks Tau to run `git status --short` in the session execution environment, which may be a different machine from the JavaScript process. Tau returns the matching `exec.result` on stdin. Request IDs are single-use, and the command must check both the ID and `ok` before consuming the result.
281
+
282
+ Make the file executable with `chmod +x`. Diagnostics and uncaught errors go to stderr; stdout remains reserved for protocol frames.
283
+
284
+ ### Implement a simple command tool in Bash
285
+
286
+ A small command that needs only client-machine authority can also implement the handshake in Bash. This example depends on `jq` for safe JSON parsing and encoding:
287
+
288
+ ```bash
289
+ #!/usr/bin/env bash
290
+ set -euo pipefail
291
+
292
+ IFS= read -r prepare
293
+ jq -e '
294
+ .version == 4 and
295
+ .type == "prepare" and
296
+ .toolName == "system_info"
297
+ ' >/dev/null <<<"$prepare"
298
+
299
+ jq -cn '{
300
+ version: 4,
301
+ type: "ready",
302
+ presentation: {
303
+ subject: "local system"
304
+ }
305
+ }'
306
+
307
+ IFS= read -r execute
308
+ jq -e '.version == 4 and .type == "execute"' >/dev/null <<<"$execute"
309
+
310
+ content=$(uname -a)
311
+ jq -cn --arg content "$content" '{
312
+ version: 4,
313
+ type: "result",
314
+ ok: true,
315
+ content: $content
316
+ }'
317
+ ```
318
+
319
+ Configure either executable as an argument-free command tool:
320
+
321
+ ```json
322
+ {
323
+ "name": "system_info",
324
+ "defaultEnabled": true,
325
+ "description": "Report operating-system information from the client machine.",
326
+ "parameters": {
327
+ "type": "object",
328
+ "properties": {},
329
+ "additionalProperties": false
330
+ },
331
+ "command": "./bin/tau-system-info"
332
+ }
333
+ ```
135
334
 
136
- Reserve stdout for the helper's protocol. Write diagnostics to stderr. Return either a string or `{ content: string }`; that text becomes the model-visible tool result.
335
+ Both `ready.presentation` and `result.presentation` are optional partial objects with `subject`, `subjectWrap`, `details`, and `metadata`. The ready value applies while the call runs; the result value applies only to its terminal state. Omit either object, or any field within it, to use Tau's default for that phase. Empty `details` or `metadata` arrays suppress the corresponding default field. The script must emit `ready` before reading the authorization-bearing `execute` frame. Direct implementations can emit the same `exec` frames shown in the JavaScript example, but the TypeScript helper is preferable when the tool needs multiple target-environment requests, cancellation forwarding, or more involved protocol handling.
137
336
 
138
337
  The handler receives:
139
338
 
@@ -181,7 +380,7 @@ Tau exports two higher-level helpers for client tools that expose a bounded Java
181
380
 
182
381
  For an executable configured through `clientTools`, keep the exact parameters schema to one required `code` string with no additional properties, then call `runTauCodeModeCommand` in the executable. For SDK clients, pass the returned tool from `createTauCodeModeClientTool` in the client's `clientTools` array.
183
382
 
184
- Both helpers supply the invocation identities, cancellation signal, execution-environment facade, progressive `docs` value, bounded API bridge, and agent-scoped scratch files. The model-facing description remains explicit caller input. Use Tau's optional shared description builder only when its progressive-disclosure wording fits the tool; Tau does not silently rewrite a configured description.
383
+ Both helpers supply the invocation identities, cancellation signal, execution-environment facade, progressive `docs` value, bounded API bridge, and agent-scoped scratch files. They use the submitted code, concisely truncated and character-wrapped, as the running and terminal tool-card subject. The model-facing description remains explicit caller input. Use Tau's optional shared description builder only when its progressive-disclosure wording fits the tool; Tau does not silently rewrite a configured description.
185
384
 
186
385
  The generic code-mode runtime is documented through its exported types and generated tool documentation. Do not layer another unbounded process or network channel behind it without making that authority clear in the tool description.
187
386
 
@@ -195,12 +394,13 @@ The command protocol is intentionally bounded:
195
394
  - Captured stderr is limited to 1 MiB. Exceeding it terminates the command and fails the tool.
196
395
  - Execution-environment stdin is limited to 16 MiB decoded, and capture can be requested up to 24 MiB per execution.
197
396
  - At most eight execution requests may be unresolved concurrently.
397
+ - Each client-tool presentation override is limited to 1 MiB in total. Its subject is limited to 256 KiB; metadata values to 16 KiB each; detail values to 256 KiB each; and detail and metadata collections to 1,024 entries each. These are safety limits, not recommended UI sizes. Tau preserves explicit values within those limits. Clients may use the exported helper when they want a concise preview.
198
398
 
199
- The configured `executionTimeoutMs` covers the whole command invocation and defaults to 60 seconds. The host also requires the owning client to acknowledge a dispatched call promptly. Standard Tau SDK clients handle acknowledgement before invoking the tool handler.
399
+ The configured `executionTimeoutMs` covers preparation and execution and defaults to 60 seconds. The host also requires the owning client to prepare and acknowledge a dispatched call promptly. Command executables emit readiness and any bounded running presentation before acknowledgement, then wait for Tau to authorize execution.
200
400
 
201
401
  Tau starts each configured command in a detached process group. Cancellation sends termination to the group and escalates to `SIGKILL` after a short grace period, even if the original group leader exits first. The helper aborts pending execution-environment requests and stops accepting work when stdin closes.
202
402
 
203
- A successful command must exit with status zero after producing one final version 3 result. Missing results, malformed framing, data after the result, reused execution request IDs, nonzero exit, timeout, cancellation, excessive output, and protocol-limit violations fail the call. Stderr is included in failure diagnostics but is not a successful result channel.
403
+ A successful protocol exchange must emit one version 4 ready frame, wait for the execute frame, emit one final version 4 result with `ok: true` or `ok: false`, and exit with status zero. An `ok: false` frame is a communicated tool failure; process and framing failures still use stderr and a nonzero exit. Missing or duplicate readiness, execution data before authorization, missing results, malformed framing, data after the result, reused execution request IDs, timeout, cancellation, excessive output, and protocol-limit violations fail the call.
204
404
 
205
405
  ## Disconnects, reconnects, and durability
206
406
 
@@ -222,7 +222,7 @@ A global-only remote history target:
222
222
 
223
223
  | Nested field | Type | Required | Contract |
224
224
  | --- | --- | --- | --- |
225
- | `endpoint` | Non-empty string | Yes | HTTP(S) URL with no query or fragment |
225
+ | `endpoint` | Non-empty string | Yes | HTTP(S) URL with no credentials, query, or fragment |
226
226
  | `apiKey` | Non-empty string | No | Inline service API key |
227
227
  | `apiKeyEnv` | Non-empty string | No | Host environment variable containing the key |
228
228
 
@@ -185,7 +185,7 @@ Restart the host process to apply settings used to construct host-wide services,
185
185
  - `history`
186
186
  - host environment variables and Codex account forcing
187
187
 
188
- For `tau serve` or `tau rpc`, make the changes on the host machine and restart that server. Restarting only an attached TUI does not rebuild the host.
188
+ For `tau serve`, make the changes on the host machine and restart the server. Restarting only an attached TUI does not rebuild the host.
189
189
 
190
190
  ### New session or runner
191
191
 
@@ -19,10 +19,10 @@ Common cases are:
19
19
  | Nook host tool | Session host |
20
20
  | Cloudflare Sandbox bridge and Fly Sprite API | Session host startup |
21
21
  | `/listen` and `/speak` | TUI client |
22
- | Telegram transcription | Telegram runner |
22
+ | Telegram transcription and voice responses | Telegram runner |
23
23
  | `tau tool pdf-unpack` | The process running that command |
24
24
 
25
- With local `tau`, these roles normally share one machine. With `tau attach`, setting a key only in the attached client's shell does not authenticate the remote host. Run `tau auth` on the host machine and set host-owned environment variables where `tau serve`, `tau rpc`, or the SDK host actually runs. See [ownership and scope](ownership-and-scope.md) for the full boundary.
25
+ With local `tau`, these roles normally share one machine. With `tau attach`, setting a key only in the attached client's shell does not authenticate the remote host. Run `tau auth` on the host machine and set host-owned environment variables where `tau serve` or the SDK host actually runs. See [ownership and scope](ownership-and-scope.md) for the full boundary.
26
26
 
27
27
  ## Provider API keys
28
28
 
@@ -61,12 +61,12 @@ Several Tau features share provider credentials but intentionally prefer a fixed
61
61
  | Feature | Resolution order |
62
62
  | --- | --- |
63
63
  | Exa web search and fetch | `EXA_API_KEY`, then `apiKeys.exa` |
64
- | Google speech, Gemini speech-to-text, and Telegram Gemini transcription | `GEMINI_API_KEY`, then `apiKeys.google` |
64
+ | Google speech, Gemini speech-to-text, Telegram Gemini transcription, and Telegram voice responses | `GEMINI_API_KEY`, then `apiKeys.google` |
65
65
  | Mistral speech-to-text, Telegram Mistral transcription, and PDF OCR | `MISTRAL_API_KEY`, then `apiKeys.mistral` |
66
66
 
67
67
  The Google and Mistral rows describe feature-specific helpers. Model calls follow the general model-authentication order instead, where the configured provider key wins over ambient environment authentication.
68
68
 
69
- `web.discover` does not require Exa. `web.search` and `web.fetch` do. `/speak` uses Google. `/listen` and Telegram audio use the configured `speechToText.provider`, which is `mistral` unless configuration selects `gemini`. PDF OCR through `tau tool pdf-unpack` uses Mistral.
69
+ `web.discover` does not require Exa. `web.search` and `web.fetch` do. `/speak` and Telegram `/tts_on` voice responses use Google. `/listen` and incoming Telegram audio use the configured `speechToText.provider`, which is `mistral` unless configuration selects `gemini`. PDF OCR through `tau tool pdf-unpack` uses Mistral.
70
70
 
71
71
  Set these variables on the process that owns the feature. For example, a remote TUI's `/speak` reads the attached client's `GEMINI_API_KEY`, while a Google model selected by the session reads credentials at the host.
72
72
 
@@ -12,7 +12,7 @@ Every Tau host opens a machine-local SQLite database at:
12
12
  ~/.config/tau/history.sqlite
13
13
  ```
14
14
 
15
- The path belongs to the **host home**. An attached TUI and a remote execution environment do not get separate history stores merely because they participate in the session. Local `tau`, `tau serve`, `tau rpc`, and the default SDK host all use the host machine's database.
15
+ The path belongs to the **host home**. An attached TUI and a remote execution environment do not get separate history stores merely because they participate in the session. Local `tau`, `tau serve`, and the default SDK host all use the host machine's database.
16
16
 
17
17
  For each session, history stores its immutable creation attributes and an ordered active transcript containing:
18
18
 
@@ -99,7 +99,7 @@ Remote history is accepted only in the host's eligible global Tau config, normal
99
99
  }
100
100
  ```
101
101
 
102
- `endpoint` must be an HTTP or HTTPS URL without a query or hash. Tau removes trailing slashes. The API key resolves on the host in this order:
102
+ `endpoint` must be an HTTP or HTTPS URL without credentials, a query, or a hash. Tau removes trailing slashes. The API key resolves on the host in this order:
103
103
 
104
104
  1. `TAU_HISTORY_API_KEY`
105
105
  2. the host environment variable named by `history.apiKeyEnv`
@@ -118,7 +118,9 @@ Remote replication is local-first:
118
118
  3. The host sends pending operations to the configured endpoint asynchronously.
119
119
  4. Successful acknowledgements remove those operations from the outbox.
120
120
 
121
- A service outage does not block session execution or local transcript capture. Pending operations remain durable and are retried when replication is scheduled again, including after host restart or later history activity. The remote service applies operations idempotently and in order.
121
+ A service outage does not block session execution or local transcript capture. Pending operations remain durable and are retried when replication is scheduled again, including after host restart or later history activity. The remote service applies operations idempotently. Tau preserves operation order within each session while processing separate session lanes independently.
122
+
123
+ A permanent operation-domain rejection, such as conflicting immutable session metadata, quarantines only that session's replication lane. Its pending operations and diagnostic remain in local SQLite, later operations for that session stay blocked, and unrelated sessions continue replicating. Transient transport, authentication, rate-limit, and service failures do not quarantine a lane; they leave endpoint replication pending for a later retry. Both kinds of failure produce a structured `history_replication_failed` host log without exposing the API key.
122
124
 
123
125
  Local entries retain their complete captured payloads. For remote replication, an entry larger than 1 MiB keeps its identity and metadata but middle-truncates oversized content, arguments, or results with an explicit marker. Remote history is therefore useful for retrieval, but the host's local entry can contain details that the shared copy intentionally omits.
124
126
 
@@ -144,7 +146,7 @@ Cloudflare operational failures are visible through normal Worker logs, Cron Eve
144
146
 
145
147
  **The history tool returns a service error while the session still works.** Remote queries and asynchronous replication can fail independently of session execution. Check endpoint reachability and Worker logs without printing the bearer key. Local capture should continue unless the session also contains a `history unavailable` warning.
146
148
 
147
- **A session does not appear in remote search.** Confirm that the host was restarted with the global remote config, that the session was opened while that target was active, and that later history activity has had a chance to flush the durable outbox. Do not assume remote digests are immediate.
149
+ **A session does not appear in remote search.** Confirm that the host was restarted with the global remote config, that the session was opened while that target was active, and that later history activity has had a chance to flush the durable outbox. Check host logs for `history_replication_failed`; a quarantined failure affects only the named session, while an unquarantined endpoint failure remains retryable. Do not assume remote digests are immediate.
148
150
 
149
151
  **Local history became unavailable.** Check host-side filesystem access, free space, and ownership for `~/.config/tau`. Restarting is required to reopen a manager disabled by an earlier local failure.
150
152
 
@@ -34,7 +34,7 @@ The intrinsic `tau_docs` tool reads one exact Markdown path at a time. It does n
34
34
 
35
35
  - [Tools](tools.md) explains built-in tool availability, execution, cancellation, and code-mode tools.
36
36
  - [Sessions](sessions.md) covers creation, turns, queueing, compaction, rewind, goals, recovery, and persistence.
37
- - [Remote sessions](remote-sessions.md) explains `serve`, `rpc`, `attach`, remote paths, and transport authentication.
37
+ - [Remote sessions](remote-sessions.md) explains `serve`, `attach`, remote paths, and transport authentication.
38
38
  - [History](history.md) covers local transcript history, optional remote replication, and the history tool.
39
39
  - [Nook](nook.md) explains configuration and operation of the optional static mini-app platform.
40
40
  - [Telegram](telegram.md) covers runner configuration, projects, workspaces, routing, and recovery.
@@ -1,6 +1,6 @@
1
1
  # Node SDK
2
2
 
3
- Tau's Node SDK provides a typed client for creating, observing, and controlling sessions without implementing the wire protocol directly. Use the in-process client when your application should own the host, the WebSocket client for a long-running `tau serve` host, or the transport adapter when another process owns the connection.
3
+ Tau's Node SDK provides a typed client for creating, observing, and controlling sessions without implementing the wire protocol directly. Use the in-process client when your application should own the host, the WebSocket client for a long-running `tau serve` host, or the transport adapter with a custom connection implementation.
4
4
 
5
5
  The SDK uses the same public [session protocol](session-protocol.md) as the TUI. Session behavior is therefore consistent across local applications, remote integrations, and terminal clients.
6
6
 
@@ -74,23 +74,7 @@ WebSocket authentication grants full session access. Deployment and TLS guidance
74
74
 
75
75
  ### Supply a protocol transport
76
76
 
77
- `createTauSdkClientFromTransport(transport, options?)` builds the same client facade over any `SessionProtocolTransport`. Tau exports `StdioSessionProtocolTransport` for a spawned `tau rpc` process:
78
-
79
- ```ts
80
- import { spawn } from "node:child_process";
81
- import {
82
- StdioSessionProtocolTransport,
83
- createTauSdkClientFromTransport,
84
- } from "@markusylisiurunen/tau/sdk";
85
-
86
- const child = spawn("tau", ["rpc"], {
87
- stdio: ["pipe", "pipe", "pipe"],
88
- });
89
- const transport = new StdioSessionProtocolTransport(child);
90
- const client = await createTauSdkClientFromTransport(transport);
91
- ```
92
-
93
- The stdio transport owns the supplied process connection and terminates it on close. Stdout must contain only protocol NDJSON; stderr is retained for bounded process diagnostics.
77
+ `createTauSdkClientFromTransport(transport, options?)` builds the same client facade over any `SessionProtocolTransport`. This keeps the SDK facade independent of WebSocket and allows applications to supply another transport without changing session semantics.
94
78
 
95
79
  A custom transport implements:
96
80
 
@@ -295,6 +279,11 @@ If rendering `timeline.item`, accept only the active epoch and merge by its allo
295
279
  Pass `TauSdkClientTool` entries in `clientTools` when model-facing work must run in the integration process:
296
280
 
297
281
  ```ts
282
+ import {
283
+ createTauSdkClient,
284
+ truncateTauClientToolText,
285
+ } from "@markusylisiurunen/tau/sdk";
286
+
298
287
  const client = await createTauSdkClient({
299
288
  clientTools: [
300
289
  {
@@ -303,12 +292,22 @@ const client = await createTauSdkClient({
303
292
  description: "Choose one item from the user's local workspace.",
304
293
  parameters: {
305
294
  type: "object",
306
- properties: {},
295
+ properties: {
296
+ choice: { type: "string" },
297
+ },
298
+ required: ["choice"],
307
299
  additionalProperties: false,
308
300
  },
309
301
  executionTimeoutMs: 60_000,
310
302
  },
311
- execute: async (_args, context) => {
303
+ describe: (args) => {
304
+ const input = args as { choice: string };
305
+ return {
306
+ subject: truncateTauClientToolText(input.choice),
307
+ };
308
+ },
309
+ execute: async (args, context) => {
310
+ const input = args as { choice: string };
312
311
  context.signal.throwIfAborted();
313
312
  const status = await context.executionEnvironment.exec(
314
313
  "git status --short",
@@ -316,7 +315,12 @@ const client = await createTauSdkClient({
316
315
  signal: context.signal,
317
316
  },
318
317
  );
319
- return status.output || "Working tree is clean.";
318
+ return {
319
+ content: status.output || "Working tree is clean.",
320
+ presentation: {
321
+ subject: truncateTauClientToolText(input.choice),
322
+ },
323
+ };
320
324
  },
321
325
  },
322
326
  ],
@@ -325,7 +329,11 @@ const client = await createTauSdkClient({
325
329
 
326
330
  The handler receives `sessionId`, owning `agentId`, `callId`, an `AbortSignal`, and an execution-environment facade. The handler itself runs on the client machine. `context.executionEnvironment.exec()` crosses the session boundary and runs in the session execution environment.
327
331
 
328
- The SDK acknowledges delegated calls, converts a returned string or `{ content }` to the wire result, reports thrown errors, and aborts handlers on host cancellation, client close, or terminal transport failure. `client.close()` waits for active handlers to settle.
332
+ `describe` is optional. When present, the SDK calls it with the arguments and a reduced context containing `sessionId`, `agentId`, `callId`, and `signal` before acknowledgement. This context deliberately has no execution-environment facade. It may return a partial running presentation containing `subject`, `subjectWrap`, `details`, or `metadata`. Tau owns action and operation, fills every omitted field, and acknowledges the resolved presentation before calling `execute`.
333
+
334
+ The execution result may independently include the same partial shape for the terminal card. Return a string or `{ content, presentation? }` for success, or `{ ok: false, error, presentation? }` for a structured failure. Omitted presentation fields use Tau's terminal defaults; empty detail or metadata arrays suppress those defaults. If `describe` throws, the SDK reports a preparation error without calling `execute`. If the client never returns a result because of cancellation, timeout, detach, or another failure, the host produces a complete fallback presentation.
335
+
336
+ Tau preserves every explicit presentation field up to the protocol safety limits; it does not apply display truncation or normalize client text. `truncateTauClientToolText` provides optional caller-controlled line, character, and head or middle truncation. Use its result directly for a subject. For a block of detail text, split the result on `\n` and map each line to one `details` entry; use `maxLines: 1` when assigning it directly to one detail or metadata entry. The helper shapes text but does not replace protocol validation. The SDK aborts handlers on host cancellation, client close, or terminal transport failure. `client.close()` waits for active handlers to settle.
329
337
 
330
338
  Tool definitions are frozen for each assistant turn and remain independent of persona tool allowlists. Names cannot collide with host tools or another observing client's tools. See [client tools](client-tools.md) for authority, limits, command-backed tools, and disconnect behavior.
331
339
 
@@ -353,7 +361,7 @@ const tickets = createTauCodeModeClientTool({
353
361
  });
354
362
  ```
355
363
 
356
- Pass `tickets` in `clientTools`. Generated code receives the declared API namespace, progressively disclosed `docs`, console output, live `Date` and `Math`, and agent-scoped scratch files when invoked as a client tool. API calls cross a bounded JSON bridge. The tool description remains explicit caller input; the builder is optional.
364
+ Pass `tickets` in `clientTools`. Generated code receives the declared API namespace, progressively disclosed `docs`, console output, live `Date` and `Math`, and agent-scoped scratch files when invoked as a client tool. API calls cross a bounded JSON bridge. The submitted code appears, concisely truncated and character-wrapped, as the running and terminal tool-card subject. The tool description remains explicit caller input; the builder is optional.
357
365
 
358
366
  The SDK also exports `executeTauCodeMode` for standalone execution. The separate `@markusylisiurunen/tau/code-mode` entry point additionally exports file-capability types, `runTauClientToolCommand`, and `runTauCodeModeCommand` for command-backed tools. Use the helpers instead of implementing their framing manually.
359
367
 
@@ -361,7 +369,7 @@ The SDK also exports `executeTauCodeMode` for standalone execution. The separate
361
369
 
362
370
  Most turn and mutation methods do not accept an `AbortSignal`. Call `session.interrupt()` to cancel active host work. `session.exec()` is the exception: its optional signal targets only that execution.
363
371
 
364
- `session.unobserve()` retires one facade. `client.close()` retires the connection, rejects pending transport requests, aborts client-tool handlers, waits for them to settle, and then closes the transport. For the default in-process client it also persists sessions and shuts down the owned host. For WebSocket it leaves the remote host and sessions running. For stdio it closes the owned process connection.
372
+ `session.unobserve()` retires one facade. `client.close()` retires the connection, rejects pending transport requests, aborts client-tool handlers, waits for them to settle, and then closes the transport. For the default in-process client it also persists sessions and shuts down the owned host. For WebSocket it leaves the remote host and sessions running.
365
373
 
366
374
  Always close clients in `finally`. Do not continue using a session facade after unobserve or any client after close.
367
375
 
@@ -371,7 +379,6 @@ All exported SDK and transport errors extend `TauSessionClientError`:
371
379
 
372
380
  - `TauSessionProtocolResponseError` means the host returned a protocol error. It exposes `code`, `message`, `requestId`, and optional `data`.
373
381
  - `TauTransportError` means connection setup, framing, version validation, timeout, closure, or another terminal transport operation failed.
374
- - `TauProcessError` extends `TauTransportError` for a stdio subprocess failure and includes `exitCode`, `signal`, and bounded `stderr`.
375
382
 
376
383
  Branch on a protocol error's `code`, not its message. A successful request can still return a failed, aborted, or blocked turn outcome, so inspect `result.turn.status` separately.
377
384
 
@@ -384,7 +391,7 @@ The SDK entry point exports the types needed at integration boundaries rather th
384
391
  - `TauSdkClient`, `TauSdkSession`, client option types, session summaries, and request and result aliases;
385
392
  - `TauSdkDelta`, `SessionProtocolSnapshot`, pending-message types, subagent-activity types, and ephemeral event types;
386
393
  - `TauSdkClientTool`, its execution context and environment facade, and code-mode definition and result types;
387
- - `SessionProtocolTransport`, listener types, WebSocket options, stdio process type, and transport errors.
394
+ - `SessionProtocolTransport`, listener types, WebSocket options, and transport errors.
388
395
 
389
396
  It also exports `applySessionProtocolDelta`, `applySessionProtocolSubagentActivitiesMessage`, the turn-ledger helpers, and user-text projection helpers:
390
397
 
@@ -12,7 +12,7 @@ Client tools execute on the client machine, not wherever the agent's Bash tool r
12
12
 
13
13
  ### Host
14
14
 
15
- The **host** creates, observes, persists, and recovers sessions. It owns model calls, credentials, session orchestration, tool binding, history storage, and execution-environment lifecycle. Local `tau` creates an in-process host. `tau serve` and `tau rpc` are standalone host entry points.
15
+ The **host** creates, observes, persists, and recovers sessions. It owns model calls, credentials, session orchestration, tool binding, history storage, and execution-environment lifecycle. Local `tau` creates an in-process host. `tau serve` is the standalone host entry point.
16
16
 
17
17
  The host's home owns data such as session snapshots, authentication storage, usage logs, and the local history database. Use Tau commands and session operations to manage these stores rather than editing their files directly.
18
18
 
@@ -38,10 +38,9 @@ For repository and composite projects it prepares managed workspaces. A configur
38
38
  | --- | --- | --- | --- |
39
39
  | `tau` | Local TUI process | In-process on the same machine | Local `cwd` where Tau started |
40
40
  | `tau attach … ws://…` | Machine running `tau attach` | Machine running `tau serve` | Environment selected or restored by that host |
41
- | `tau attach … -- <command>` | Machine running `tau attach` | Machine running the protocol command, often reached through SSH | Environment selected or restored by that host |
42
- | `tau rpc` or `tau serve` | A separate protocol client | The server process | Local or configured hosted environment chosen by the client |
41
+ | `tau serve` | A separate protocol client | The server process | Local or configured hosted environment chosen by the client |
43
42
  | Default Node SDK client | SDK caller | In-process with the SDK caller | Usually a local environment supplied at session creation |
44
- | SDK over WebSocket or stdio | SDK caller | Remote server or command process | Environment selected or restored by that host |
43
+ | SDK over WebSocket | SDK caller | Remote server | Environment selected or restored by that host |
45
44
  | `tau telegram` | Telegram runner | In-process on the runner machine | Prepared project workspace or persistent directory |
46
45
 
47
46
  A Cloudflare Sandbox or Fly Sprite can place the execution environment on another target while the host stays on its own machine. The host keeps provider credentials and orchestration authority; the target owns its paths and commands.
@@ -139,7 +139,7 @@ tau --no-agent-context-files
139
139
 
140
140
  This disables ancestor `AGENTS.md` injection, configured `agentContextFiles`, and the descendant paths-only scan. It does not disable personas, prompt templates, or [skills](skills.md).
141
141
 
142
- The option applies to local TUI sessions and to hosts started with `tau rpc` or `tau serve`. For the Node SDK, `noAgentContextFiles: true` provides the same host-level behavior. An attached client cannot retroactively change how an existing remote host session was created.
142
+ The option applies to local TUI sessions and to hosts started with `tau serve`. For the Node SDK, `noAgentContextFiles: true` provides the same host-level behavior. An attached client cannot retroactively change how an existing remote host session was created.
143
143
 
144
144
  ## Alternate subagent working directories
145
145
 
@@ -176,6 +176,6 @@ For a new local TUI session, debug mode prints discovered content, loaded contex
176
176
  tau --debug --persona release-coder
177
177
  ```
178
178
 
179
- Add `--no-agent-context-files` to verify the context-free variant. Debug mode is TUI startup functionality and is not available with `tau rpc` or `tau serve`.
179
+ Add `--no-agent-context-files` to verify the context-free variant. Debug mode is TUI startup functionality and is not available with `tau serve`.
180
180
 
181
181
  If expected context is missing, verify the execution-environment `cwd` and `home`, not just the attached client's current directory. Then check exact filenames, canonical path eligibility, configuration-level path bases, scan exclusions, and reload warnings. See [troubleshooting](troubleshooting.md) for broader diagnostics.