@markusylisiurunen/tau 0.3.50 → 0.3.51

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 (88) 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/modes/index.js +0 -1
  11. package/dist/core/modes/index.js.map +1 -1
  12. package/dist/core/static/tau_docs/client-tools.md +212 -12
  13. package/dist/core/static/tau_docs/configuration.md +1 -1
  14. package/dist/core/static/tau_docs/credentials.md +4 -4
  15. package/dist/core/static/tau_docs/history.md +1 -1
  16. package/dist/core/static/tau_docs/index.md +1 -1
  17. package/dist/core/static/tau_docs/node-sdk.md +33 -26
  18. package/dist/core/static/tau_docs/ownership-and-scope.md +3 -4
  19. package/dist/core/static/tau_docs/prompts-and-project-context.md +2 -2
  20. package/dist/core/static/tau_docs/remote-sessions.md +4 -47
  21. package/dist/core/static/tau_docs/security.md +2 -6
  22. package/dist/core/static/tau_docs/session-protocol-methods.md +48 -7
  23. package/dist/core/static/tau_docs/session-protocol.md +18 -18
  24. package/dist/core/static/tau_docs/sessions.md +6 -5
  25. package/dist/core/static/tau_docs/telegram.md +8 -4
  26. package/dist/core/static/tau_docs/troubleshooting.md +3 -5
  27. package/dist/core/static/tau_docs/tui.md +4 -6
  28. package/dist/core/telegram/adapter.js +226 -18
  29. package/dist/core/telegram/adapter.js.map +1 -1
  30. package/dist/core/telegram/config.js +1 -1
  31. package/dist/core/telegram/config.js.map +1 -1
  32. package/dist/core/telegram/project_preferences.js +42 -12
  33. package/dist/core/telegram/project_preferences.js.map +1 -1
  34. package/dist/core/telegram/runtime.js +6 -1
  35. package/dist/core/telegram/runtime.js.map +1 -1
  36. package/dist/core/telegram/session_manager.js +50 -4
  37. package/dist/core/telegram/session_manager.js.map +1 -1
  38. package/dist/core/telegram/tts.js +137 -0
  39. package/dist/core/telegram/tts.js.map +1 -0
  40. package/dist/core/tools/presentation.js +55 -17
  41. package/dist/core/tools/presentation.js.map +1 -1
  42. package/dist/core/utils/gemini_speech.js +122 -41
  43. package/dist/core/utils/gemini_speech.js.map +1 -1
  44. package/dist/core/version.js +1 -1
  45. package/dist/host/client_tool_broker.js +71 -17
  46. package/dist/host/client_tool_broker.js.map +1 -1
  47. package/dist/host/local_session_host.js +2 -2
  48. package/dist/host/local_session_host.js.map +1 -1
  49. package/dist/host/session_host.js.map +1 -1
  50. package/dist/host/session_protocol_handler.js +17 -6
  51. package/dist/host/session_protocol_handler.js.map +1 -1
  52. package/dist/main.js +18 -92
  53. package/dist/main.js.map +1 -1
  54. package/dist/protocol/session_protocol.d.ts +22 -2
  55. package/dist/protocol/session_protocol.js +47 -3
  56. package/dist/protocol/session_protocol.js.map +1 -1
  57. package/dist/sdk/client_tool_command.d.ts +41 -13
  58. package/dist/sdk/client_tool_command.js +157 -39
  59. package/dist/sdk/client_tool_command.js.map +1 -1
  60. package/dist/sdk/client_tool_presentation.d.ts +17 -0
  61. package/dist/sdk/client_tool_presentation.js +9 -0
  62. package/dist/sdk/client_tool_presentation.js.map +1 -0
  63. package/dist/sdk/code_mode.js +10 -1
  64. package/dist/sdk/code_mode.js.map +1 -1
  65. package/dist/sdk/index.d.ts +5 -4
  66. package/dist/sdk/index.js +2 -1
  67. package/dist/sdk/index.js.map +1 -1
  68. package/dist/sdk/session.js +28 -9
  69. package/dist/sdk/session.js.map +1 -1
  70. package/dist/sdk/types.d.ts +11 -1
  71. package/dist/transport/errors.d.ts +0 -11
  72. package/dist/transport/errors.js +0 -12
  73. package/dist/transport/errors.js.map +1 -1
  74. package/dist/transport/index.d.ts +1 -3
  75. package/dist/transport/index.js +1 -2
  76. package/dist/transport/index.js.map +1 -1
  77. package/dist/tui/session_chat_app.js +28 -15
  78. package/dist/tui/session_chat_app.js.map +1 -1
  79. package/dist/tui/session_chat_controller.js +0 -51
  80. package/dist/tui/session_chat_controller.js.map +1 -1
  81. package/dist/tui/speech_playback.js +3 -3
  82. package/dist/tui/speech_playback.js.map +1 -1
  83. package/package.json +2 -2
  84. package/dist/core/modes/rpc_server.js +0 -105
  85. package/dist/core/modes/rpc_server.js.map +0 -1
  86. package/dist/transport/stdio_session_transport.d.ts +0 -48
  87. package/dist/transport/stdio_session_transport.js +0 -338
  88. 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
 
@@ -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
 
@@ -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.
@@ -1,6 +1,6 @@
1
1
  # Remote sessions
2
2
 
3
- Remote sessions let the terminal stay close to the user while the session host and execution environment run elsewhere. Tau does not turn remote work into a second product mode: `tau attach` uses the same TUI over a WebSocket or stdio transport. The important choice is which process should stay alive, who owns credentials and persistence, and where agent-visible commands run.
3
+ Remote sessions let the terminal stay close to the user while the session host and execution environment run elsewhere. Tau does not turn remote work into a second product mode: `tau attach` uses the same TUI over WebSocket. The important boundaries are who owns credentials and persistence and where agent-visible commands run.
4
4
 
5
5
  ## Choose the connection shape
6
6
 
@@ -9,12 +9,10 @@ Use the smallest shape that matches the lifecycle you need.
9
9
  | Shape | Best for | Lifetime and ownership |
10
10
  | --- | --- | --- |
11
11
  | `tau serve` plus WebSocket attach | Long-running hosts, reconnects, and multiple observers | The server owns the host independently of any one TUI. |
12
- | `tau attach -- ssh … tau rpc` | Ad hoc access through SSH without exposing a listener | The attachment owns one remote RPC process. Closing it shuts that host down. |
13
- | `tau rpc` directly | Editors, automation, and custom protocol clients over NDJSON | The parent process owns the RPC host and its stdin/stdout. |
14
12
  | Node SDK with its default client | Applications that want an in-process host | The SDK client owns the host and closes it with the client. |
15
13
  | Node SDK over WebSocket | Applications sharing a long-running `tau serve` host | The server owns sessions; the SDK observes them remotely. |
16
14
 
17
- Use `tau serve` when a turn should continue after a laptop disconnects. Use stdio/SSH when SSH is already the desired security and process boundary and it is acceptable for closing the TUI to stop remote work. The [session protocol](session-protocol.md) and [Node SDK](node-sdk.md) cover developer APIs and wire details; this page stays at the operational level.
15
+ Use `tau serve` for remote TUI and SDK connections. Keep it bound to loopback behind an SSH tunnel when SSH is the desired security boundary. The [session protocol](session-protocol.md) and [Node SDK](node-sdk.md) cover developer APIs and wire details; this page stays at the operational level.
18
16
 
19
17
  ## Host sessions over WebSocket
20
18
 
@@ -58,24 +56,6 @@ Then attach locally:
58
56
  tau attach ws://127.0.0.1:8787
59
57
  ```
60
58
 
61
- ## Attach through stdio and SSH
62
-
63
- Anything after `--` is launched as a local child process and must speak Tau’s session protocol over stdin and stdout. SSH makes that child a remote `tau rpc` process:
64
-
65
- ```sh
66
- tau attach -- ssh dev@buildbox.example tau rpc
67
- ```
68
-
69
- A login shell or project-specific host configuration can be selected in the remote command:
70
-
71
- ```sh
72
- tau attach -- ssh dev@buildbox.example 'cd /srv/tau-host && tau rpc'
73
- ```
74
-
75
- The remote process’s stdin and stdout are protocol traffic. Shell startup files and wrapper scripts must not print banners to stdout. Put diagnostics on stderr.
76
-
77
- This is a one-shot host. When the TUI exits or the transport fails, the local child is terminated, `tau rpc` shuts down its host, active work is interrupted, and durable state is recovered on the next process. Do not use this shape when work must continue through client disconnects; run `tau serve` instead.
78
-
79
59
  ## List, select, create, or attach
80
60
 
81
61
  Without `--session` or `--new`, `tau attach` asks the host for its session list and opens an interactive selector:
@@ -94,14 +74,6 @@ tau attach \
94
74
  ws://127.0.0.1:8787
95
75
  ```
96
76
 
97
- The equivalent SSH form is:
98
-
99
- ```sh
100
- tau attach \
101
- --session 0195d6e4-4cf9-7f44-a2d8-f8f7f49ee9d3 \
102
- -- ssh dev@buildbox.example tau rpc
103
- ```
104
-
105
77
  Create a new session with an absolute cwd in the selected execution environment:
106
78
 
107
79
  ```sh
@@ -111,15 +83,6 @@ tau attach \
111
83
  ws://127.0.0.1:8787
112
84
  ```
113
85
 
114
- For stdio/SSH:
115
-
116
- ```sh
117
- tau attach \
118
- --new \
119
- --cwd /srv/workspaces/tau \
120
- -- ssh dev@buildbox.example tau rpc
121
- ```
122
-
123
86
  For the default `local` execution kind, this path is on the host machine, not the attaching machine. It must already be a usable directory with the desired repository or workspace. Tau creates the session, not the directory or repository.
124
87
 
125
88
  Remote `--new` supplies the conventional creation attribute `source: "tui"`. It does not infer repository metadata by inspecting the remote cwd. Clients that need repository provenance should create through the SDK or protocol and provide complete immutable attributes. See [sessions](sessions.md).
@@ -215,7 +178,7 @@ Different changes have different owners:
215
178
  | Effective model `apiKeys` in execution-environment or session configuration | Wait for idle, then run `/reload`; new sessions also resolve the current values. |
216
179
  | Managed Codex auth changed with `tau auth` | No host restart; auth storage is read again on later credential resolutions. |
217
180
  | Attaching themes, diff launcher, speech config, or configured client tools | Restart `tau attach`. |
218
- | Host process environment variables, history target, WebSocket listener, Cloudflare bridge, Fly API target, or host startup flags | Restart `tau serve` or the `tau rpc` process. |
181
+ | Host process environment variables, history target, WebSocket listener, Cloudflare bridge, Fly API target, or host startup flags | Restart `tau serve`. |
219
182
  | Host Tau package, built-in tools, protocol, session recovery code, or built-in documentation | Upgrade and restart the host. |
220
183
  | TUI package, keybindings, rendering, local speech, or client-tool implementation | Upgrade and restart the attaching client. |
221
184
 
@@ -227,8 +190,6 @@ A WebSocket connection observes a hosted session; it does not own or delete it.
227
190
 
228
191
  A clean `tau serve` shutdown interrupts active work, persists live sessions, and closes clients. On restart, the host lists sessions whose execution environments it can restore. Recovery returns sessions idle, drops pending queued and steering messages, discards live subagents, and changes an active persistent goal to blocked. Use `/goal resume` only after checking why the host stopped.
229
192
 
230
- A stdio/SSH connection is different because its RPC process is the host. Closing the connection ends that process. The session remains stored under the remote host user’s `~/.config/tau/sessions` and can be observed by a later `tau rpc` process using the same home and compatible resolver configuration.
231
-
232
193
  ## Use multiple observers carefully
233
194
 
234
195
  Multiple WebSocket clients can observe the same live session and receive the same committed updates and pending-message state. They can also submit, queue, steer, interrupt, or mutate that session, so coordinate human or automation ownership rather than treating observers as read-only.
@@ -265,10 +226,6 @@ Do not edit the session JSON to change environment identity. Restore the owning
265
226
 
266
227
  Verify that server and client use the same token and that a reverse proxy preserves the WebSocket request path and query string. `TAU_WS_AUTH_TOKEN` can silently supply either side, so inspect the environment as well as command-line flags. Do not print the token in shared logs.
267
228
 
268
- ### SSH attachment fails before the TUI opens
269
-
270
- Run the remote command directly and confirm that `tau rpc` is installed and can start. Protocol stdout must contain only NDJSON. Login banners, shell startup output, or wrapper diagnostics on stdout corrupt the transport; redirect them to stderr or remove them.
271
-
272
229
  ### A reconnect shows an interrupted turn
273
230
 
274
- A client disconnect alone does not stop a WebSocket-hosted turn, but server shutdown does. Stdio/SSH attachment shutdown also ends its host. Review the last assistant and tool states, verify the execution environment with `!!pwd` and `!!git status --short`, then retry or resume a blocked goal intentionally. [Sessions](sessions.md) explains recovery and safe verification.
231
+ A client disconnect alone does not stop a WebSocket-hosted turn, but server shutdown does. Review the last assistant and tool states, verify the execution environment with `!!pwd` and `!!git status --short`, then retry or resume a blocked goal intentionally. [Sessions](sessions.md) explains recovery and safe verification.