@markusylisiurunen/tau 0.3.49 → 0.3.50

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 (57) hide show
  1. package/README.md +22 -908
  2. package/dist/core/commands/registry.js +4 -4
  3. package/dist/core/commands/registry.js.map +1 -1
  4. package/dist/core/personas.js +19 -10
  5. package/dist/core/personas.js.map +1 -1
  6. package/dist/core/runtime/runtime_bootstrap.js +14 -9
  7. package/dist/core/runtime/runtime_bootstrap.js.map +1 -1
  8. package/dist/core/static/tau_docs/client-tools.md +228 -0
  9. package/dist/core/static/tau_docs/config-reference.md +422 -0
  10. package/dist/core/static/tau_docs/configuration.md +210 -0
  11. package/dist/core/static/tau_docs/credentials.md +200 -0
  12. package/dist/core/static/tau_docs/getting-started.md +140 -0
  13. package/dist/core/static/tau_docs/history.md +163 -0
  14. package/dist/core/static/tau_docs/index.md +40 -0
  15. package/dist/core/static/tau_docs/manifest.json +28 -0
  16. package/dist/core/static/tau_docs/models.md +198 -0
  17. package/dist/core/static/tau_docs/node-sdk.md +399 -0
  18. package/dist/core/static/tau_docs/nook.md +264 -0
  19. package/dist/core/static/tau_docs/ownership-and-scope.md +104 -0
  20. package/dist/core/static/tau_docs/personas.md +199 -0
  21. package/dist/core/static/tau_docs/prompts-and-project-context.md +181 -0
  22. package/dist/core/static/tau_docs/remote-sessions.md +274 -0
  23. package/dist/core/static/tau_docs/security.md +188 -0
  24. package/dist/core/static/tau_docs/session-protocol-methods.md +577 -0
  25. package/dist/core/static/tau_docs/session-protocol.md +265 -0
  26. package/dist/core/static/tau_docs/sessions.md +223 -0
  27. package/dist/core/static/tau_docs/skills.md +176 -0
  28. package/dist/core/static/tau_docs/subagents.md +203 -0
  29. package/dist/core/static/tau_docs/telegram.md +342 -0
  30. package/dist/core/static/tau_docs/tools.md +203 -0
  31. package/dist/core/static/tau_docs/troubleshooting.md +292 -0
  32. package/dist/core/static/tau_docs/tui.md +224 -0
  33. package/dist/core/telegram/session_manager.js +4 -3
  34. package/dist/core/telegram/session_manager.js.map +1 -1
  35. package/dist/core/tools/catalog.js +3 -1
  36. package/dist/core/tools/catalog.js.map +1 -1
  37. package/dist/core/tools/presentation.js +12 -1
  38. package/dist/core/tools/presentation.js.map +1 -1
  39. package/dist/core/tools/tau_docs.js +115 -0
  40. package/dist/core/tools/tau_docs.js.map +1 -0
  41. package/dist/core/tools/tool_names.js +8 -0
  42. package/dist/core/tools/tool_names.js.map +1 -1
  43. package/dist/core/utils/repository.js +19 -0
  44. package/dist/core/utils/repository.js.map +1 -1
  45. package/dist/core/version.js +1 -1
  46. package/dist/host/client_tool_broker.js +3 -18
  47. package/dist/host/client_tool_broker.js.map +1 -1
  48. package/dist/protocol/session_protocol.d.ts +1 -0
  49. package/dist/protocol/session_protocol.js +2 -1
  50. package/dist/protocol/session_protocol.js.map +1 -1
  51. package/dist/tui/session_chat_app.js +1 -0
  52. package/dist/tui/session_chat_app.js.map +1 -1
  53. package/dist/tui/session_chat_controller.js +13 -13
  54. package/dist/tui/session_chat_controller.js.map +1 -1
  55. package/dist/tui/session_creation_attributes.js +3 -3
  56. package/dist/tui/session_creation_attributes.js.map +1 -1
  57. package/package.json +2 -2
@@ -0,0 +1,198 @@
1
+ # Models
2
+
3
+ A model definition tells Tau how to call a provider model and how to reason about its capabilities, limits, and cost. A persona selects one provider and model, while credentials authorize the resulting request. Keeping those layers separate makes it possible to update model metadata without rewriting persona behavior.
4
+
5
+ Tau ships a versioned catalog and lets the execution environment overlay it with `models.json`. The effective catalog for a session therefore depends on both the installed Tau version and the session working directory.
6
+
7
+ ## Providers, models, and personas
8
+
9
+ A **provider** owns authentication and one or more request APIs. A **model** is addressed by a provider ID and an exact model ID, such as `openai/gpt-5.6-sol`. A [persona](personas.md) binds that pair to a system prompt, settings, tools, skills, and subagents.
10
+
11
+ The bundled catalog comes from Tau's model runtime and Tau-owned extensions. It supplies known provider IDs, bundled model IDs, request API names, endpoints, capability flags, token limits, and pricing. Bundled does not mean currently usable: a provider may still lack credentials, an account may not expose a model, or a configured endpoint may reject it. See [credentials](credentials.md).
12
+
13
+ Tau applies `models.json` overlays before resolving built-in or custom personas. A bundled persona therefore uses overridden metadata when its provider and model are patched.
14
+
15
+ ## Where `models.json` is loaded
16
+
17
+ Tau uses the same level discovery as runtime configuration:
18
+
19
+ - `~/.config/tau/models.json` is the global overlay when the session `cwd` is inside the execution environment's home.
20
+ - Every ancestor `.tau/models.json` is a project overlay.
21
+ - When `cwd` is inside home, project discovery stops at home. Otherwise it continues to the filesystem root.
22
+
23
+ Merge order is bundled catalog, global overlay, then project overlays from the farthest parent to the nearest. The nearest value wins for the same field.
24
+
25
+ These paths belong to the execution environment. In an attached session, edit `models.json` on the session target, not on the TUI machine. The general path rules and reload boundary are covered in [configuration](configuration.md).
26
+
27
+ ## File shape
28
+
29
+ A `models.json` file has one required `providers` object. Provider keys are normalized to lowercase and must already be known to the bundled catalog.
30
+
31
+ ```json
32
+ {
33
+ "providers": {
34
+ "openai": {
35
+ "baseUrl": "https://models.internal.example/v1",
36
+ "headers": {
37
+ "x-tenant": "payments"
38
+ },
39
+ "models": [
40
+ {
41
+ "id": "gpt-5.6-sol",
42
+ "contextWindow": 300000,
43
+ "maxTokens": 48000
44
+ },
45
+ {
46
+ "id": "gpt-5.7-preview",
47
+ "name": "GPT-5.7 preview",
48
+ "reasoning": true,
49
+ "input": ["text", "image"],
50
+ "contextWindow": 200000,
51
+ "maxTokens": 32000,
52
+ "cost": {
53
+ "input": 3,
54
+ "output": 18,
55
+ "cacheRead": 0.3,
56
+ "cacheWrite": 3.75
57
+ }
58
+ }
59
+ ]
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ Unknown object fields are discarded. A malformed JSON document or invalid schema rejects that whole file and produces a configuration warning. An unknown provider produces a warning for that provider entry; Tau can still process valid entries from the same file.
66
+
67
+ ## Provider fields
68
+
69
+ A provider entry accepts these optional defaults:
70
+
71
+ | Field | Contract |
72
+ | --- | --- |
73
+ | `api` | Non-empty request API name. It must correspond to an API implementation available in the installed model runtime. |
74
+ | `baseUrl` | Non-empty endpoint URL string used for provider models unless a model overrides it. |
75
+ | `headers` | String-to-string map merged into model request headers. |
76
+ | `compat` | Provider-specific compatibility value. Objects shallow-merge; another JSON value replaces the prior value. |
77
+ | `models` | Array of model patches or definitions. |
78
+
79
+ Provider defaults apply to every effective model for that provider. Across levels, `headers` merge by key and `compat` objects merge one level deep. Other provider fields use the nearest defined value.
80
+
81
+ Headers are literal strings, not environment references. Prefer the mechanisms in [credentials](credentials.md) for secrets. If a proxy requires a routing header, keep the containing file private and remember that a project overlay may be committed.
82
+
83
+ ## Model fields
84
+
85
+ Each model entry requires `id`. Its value is trimmed but otherwise exact and case-sensitive. All remaining fields are optional patches:
86
+
87
+ | Field | Contract |
88
+ | --- | --- |
89
+ | `id` | Required non-empty model ID. It forms the key with the normalized provider ID. |
90
+ | `name` | Non-empty display name. |
91
+ | `api` | Non-empty request API name, overriding the provider or bundled value. |
92
+ | `baseUrl` | Non-empty endpoint string, overriding the provider or bundled value. |
93
+ | `headers` | String-to-string map. Model headers merge over provider headers by key. |
94
+ | `reasoning` | Boolean indicating whether the model supports reasoning controls. |
95
+ | `input` | Array containing `text`, `image`, or both. The array replaces the prior value. |
96
+ | `contextWindow` | Positive integer context-window size in tokens. Tau uses it for context pressure and compaction decisions. |
97
+ | `maxTokens` | Positive integer maximum output-token setting. |
98
+ | `cost` | Partial rates object described below. Fields merge into prior cost metadata. |
99
+ | `compat` | Provider-adapter compatibility value. Objects shallow-merge; another JSON value replaces the prior value. |
100
+
101
+ A model-level value always overrides the effective provider-level value, even when the model patch came from a broader configuration level. Among model patches for the same provider and ID, the nearest level wins field by field. `headers`, object `compat`, and individual `cost` fields merge; `input` and `cost.tiers` replace their previous arrays.
102
+
103
+ `compat` is intentionally adapter-specific and is not structurally validated by Tau. Use only keys understood by the selected API implementation. An accepted but unsupported `api` or `compat` value can still fail when a request is made.
104
+
105
+ ## Cost and tiered pricing
106
+
107
+ Base cost rates are US dollars per million tokens:
108
+
109
+ ```json
110
+ {
111
+ "cost": {
112
+ "input": 2,
113
+ "output": 12,
114
+ "cacheRead": 0.2,
115
+ "cacheWrite": 2.5
116
+ }
117
+ }
118
+ ```
119
+
120
+ Each base field is optional in a patch because omitted values inherit. For a newly synthesized model, inherited provider defaults remain unless replaced.
121
+
122
+ `cost.tiers` is an array of complete request-wide rates. Every tier requires all five fields:
123
+
124
+ ```json
125
+ {
126
+ "cost": {
127
+ "tiers": [
128
+ {
129
+ "inputTokensAbove": 272000,
130
+ "input": 4,
131
+ "output": 18,
132
+ "cacheRead": 0.4,
133
+ "cacheWrite": 5
134
+ }
135
+ ]
136
+ }
137
+ }
138
+ ```
139
+
140
+ Tau totals input, cache-read, and cache-write usage for the request. When that total is greater than a tier's `inputTokensAbove`, the entire request uses that tier's rates. If several thresholds match, the highest threshold wins. The threshold comparison is strict, so a total exactly equal to the threshold stays on the lower rate.
141
+
142
+ Keep pricing metadata accurate. Tau uses it for displayed and recorded cost, not only documentation.
143
+
144
+ ## Unbundled model IDs
145
+
146
+ Custom personas and subagent launch allowlists may name an unbundled model ID when the provider is known. Tau handles it in one of two ways:
147
+
148
+ 1. If merged `models.json` contains the provider and ID, Tau derives a model from that provider's bundled template and applies provider and model patches.
149
+ 2. Otherwise, general model resolution synthesizes the requested ID from that provider's bundled template and applies provider-level patches.
150
+
151
+ This supports newly released IDs before Tau bundles them. It does not discover capabilities or prices from the provider. The synthesized definition inherits the first bundled template for that provider, so explicitly define any differing `api`, endpoint, inputs, reasoning support, token limits, compatibility settings, and cost.
152
+
153
+ A completely new provider cannot be introduced through `models.json`; provider keys must be known to the installed runtime. Also note that `modelSystemNotices` requires a **configured** model. An unbundled ID used only through synthesis must first be listed in `models.json` before a notice can target it.
154
+
155
+ ## Model system notices
156
+
157
+ `modelSystemNotices` belongs in `config.json`, but its keys are validated against the merged configured model catalog, so it is closely tied to model configuration:
158
+
159
+ ```json
160
+ {
161
+ "modelSystemNotices": {
162
+ "openai/gpt-5.7-preview": "Use the preview endpoint only for non-production analysis."
163
+ }
164
+ }
165
+ ```
166
+
167
+ Keys use exact `<provider>/<model>` form. Provider IDs must be known. Model IDs are case-sensitive and must be bundled or listed in layered `models.json`. Values must be non-empty strings. The map merges by key across configuration levels, with the nearest notice winning.
168
+
169
+ When Tau commits input for a main agent or subagent using that model, it prepends the notice as a hidden model-facing system block. The block is persisted with the user message and later compaction sees it as source history. Tau does not add a fresh current notice to maintenance prompts or synthetic compaction messages. Ephemeral agents do not receive model system notices.
170
+
171
+ Use notices for model-specific operational guidance, not credentials or transient secrets. The notice becomes durable session content.
172
+
173
+ ## Applying and verifying changes
174
+
175
+ Run `/reload` in an idle TUI session after changing `models.json` or `modelSystemNotices`. Reload resolves runtime content again, reapplies the active persona when its ID still exists, and reports model or configuration warnings. The TUI refuses to reload during an active turn.
176
+
177
+ For a new local session, this command verifies that the intended persona resolves to the expected provider and model ID without starting the TUI:
178
+
179
+ ```sh
180
+ tau --debug --persona release-coder
181
+ ```
182
+
183
+ Debug output shows effective persona IDs and selected model IDs, not every model metadata field. It also prints the full effective system prompt and project context, so treat its output accordingly. A small real request is the final check for endpoint, API compatibility, model availability, and credentials.
184
+
185
+ Common warnings and failures include:
186
+
187
+ - invalid JSON or a missing `providers` object;
188
+ - an unknown provider;
189
+ - an empty model ID, API, endpoint, or name;
190
+ - non-string headers;
191
+ - unsupported values in `input`;
192
+ - non-positive or non-integer token limits;
193
+ - negative cost rates or an incomplete tier;
194
+ - a persona that references a provider with no bundled template;
195
+ - a notice targeting a model that was never bundled or configured; and
196
+ - a syntactically accepted API or compatibility setting that the runtime adapter cannot use.
197
+
198
+ Warnings identify the exact `models.json` or `config.json` path. In a remote session, that path is in the execution environment. See [troubleshooting](troubleshooting.md) when the edited file and the warning path do not match.
@@ -0,0 +1,399 @@
1
+ # Node SDK
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.
4
+
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
+
7
+ ## Install and import
8
+
9
+ Tau requires Node.js 24 or later.
10
+
11
+ ```sh
12
+ npm install @markusylisiurunen/tau
13
+ ```
14
+
15
+ Import the SDK from its package entry point:
16
+
17
+ ```ts
18
+ import { createTauSdkClient } from "@markusylisiurunen/tau/sdk";
19
+ ```
20
+
21
+ Tau is an ES module package.
22
+
23
+ ## Choose a client
24
+
25
+ ### Own an in-process host
26
+
27
+ `createTauSdkClient()` creates a local host and connects through an in-process transport:
28
+
29
+ ```ts
30
+ const client = await createTauSdkClient({
31
+ persona: "gpt-5.6-sol-coder:high",
32
+ });
33
+ ```
34
+
35
+ The host uses the current host user's Tau session store and history database. `cwd` in the client options controls startup configuration for host-wide services and defaults to `process.cwd()`. Each session separately resolves runtime configuration and content from the execution environment and `cwd` passed to `client.sessions.create()`.
36
+
37
+ The in-process options are:
38
+
39
+ ```ts
40
+ type TauSdkClientOptions = {
41
+ cwd?: string;
42
+ persona?: string;
43
+ reasoning?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
44
+ noAgentContextFiles?: boolean;
45
+ connectTimeoutMs?: number;
46
+ initialize?: { client: { name: string; version: string } };
47
+ clientTools?: TauSdkClientTool[];
48
+ };
49
+ ```
50
+
51
+ `persona`, `reasoning`, and `noAgentContextFiles` configure sessions created by this owned host. `connectTimeoutMs` defaults to 5,000 ms. Default initialization metadata is `{ client: { name: "tau-sdk", version: "1" } }`.
52
+
53
+ Closing this client also shuts down its host after persisting live sessions.
54
+
55
+ ### Connect to `tau serve`
56
+
57
+ `createTauSdkWebSocketClient()` connects to a long-running WebSocket host:
58
+
59
+ ```ts
60
+ import { createTauSdkWebSocketClient } from "@markusylisiurunen/tau/sdk";
61
+
62
+ const client = await createTauSdkWebSocketClient({
63
+ url: "wss://tau.example.com",
64
+ authToken: process.env.TAU_WS_AUTH_TOKEN,
65
+ initialize: {
66
+ client: { name: "acme-automation", version: "1.0.0" },
67
+ },
68
+ });
69
+ ```
70
+
71
+ Options include `url`, optional `authToken`, `connectTimeoutMs`, `initialize`, `clientTools`, and an optional `webSocketFactory` for runtimes that need a custom WebSocket implementation. Closing the SDK client closes only its connection. Sessions remain with the server.
72
+
73
+ WebSocket authentication grants full session access. Deployment and TLS guidance belongs to [remote sessions](remote-sessions.md), and the trust model belongs to [security](security.md).
74
+
75
+ ### Supply a protocol transport
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.
94
+
95
+ A custom transport implements:
96
+
97
+ ```ts
98
+ type SessionProtocolTransport = {
99
+ readonly ready: SessionProtocolReadyMessage;
100
+ connect(initializeParams, timeoutMs): Promise<void>;
101
+ request(method, params): Promise<unknown>;
102
+ onDelta(listener): () => void;
103
+ onEphemeral(listener): () => void;
104
+ onPendingUserMessages(listener): () => void;
105
+ onSubagentActivities(listener): () => void;
106
+ onClientTool(listener): () => void;
107
+ onFailure(listener): () => void;
108
+ close(): Promise<void>;
109
+ };
110
+ ```
111
+
112
+ A terminal transport failure must call `onFailure` listeners so the SDK can abort delegated client tools and reject outstanding work.
113
+
114
+ ## Create and run a session
115
+
116
+ A typical integration creates and automatically observes a session, listens for state, submits work, and closes cleanly:
117
+
118
+ ```ts
119
+ import {
120
+ TauSessionProtocolResponseError,
121
+ TauTransportError,
122
+ createTauSdkClient,
123
+ } from "@markusylisiurunen/tau/sdk";
124
+
125
+ const client = await createTauSdkClient();
126
+ const session = await client.sessions.create({
127
+ executionEnvironment: {
128
+ kind: "local",
129
+ cwd: process.cwd(),
130
+ },
131
+ attributes: {
132
+ source: "sdk",
133
+ repository: "github.com/example/atlas",
134
+ },
135
+ });
136
+
137
+ const unsubscribe = session.onDelta((delta) => {
138
+ console.log(delta.sessionId, delta.fromRevision, delta.toRevision);
139
+ });
140
+
141
+ try {
142
+ const result = await session.submit("Summarize the current changes.");
143
+ const snapshot = await session.snapshot();
144
+ console.log(result.userHistoryEntryId, result.turn.status, snapshot.revision);
145
+ } catch (error) {
146
+ if (error instanceof TauSessionProtocolResponseError) {
147
+ console.error(error.code, error.message);
148
+ } else if (error instanceof TauTransportError) {
149
+ console.error(error.message);
150
+ } else {
151
+ throw error;
152
+ }
153
+ } finally {
154
+ unsubscribe();
155
+ await session.unobserve();
156
+ await client.close();
157
+ }
158
+ ```
159
+
160
+ `client.sessions.create(input)` sends `session.create`, then observes the new session before resolving. `client.sessions.observe(sessionId)` does the same observation handshake for an existing session. `client.sessions.list()` returns `{ sessionId, lifecycle }` summaries.
161
+
162
+ Creation requires complete execution-environment input and immutable attributes. The execution `cwd` must be absolute and belongs to the selected environment, not necessarily the SDK process. Cloudflare Sandbox and Fly Sprite sessions refer to already-provisioned targets configured on the host. See [sessions](sessions.md) for creation attributes and [ownership and scope](ownership-and-scope.md) for path ownership.
163
+
164
+ `session.unobserve()` stops observation and makes that `TauSdkSession` facade terminal. It does not delete the hosted session. `client.close()` is idempotent and closes the whole client.
165
+
166
+ The connected `TauSdkClient` exposes:
167
+
168
+ | Member | Purpose |
169
+ | --- | --- |
170
+ | `ready` | The validated server `ready` message and advertised methods. |
171
+ | `sessions.create(input)` | Create, observe, and return a session facade. |
172
+ | `sessions.list()` | List hosted session summaries. |
173
+ | `sessions.observe(sessionId)` | Observe an existing session and return a facade. |
174
+ | `subscribe(listener)` | Receive deltas for every observed session on this connection. |
175
+ | `subscribePendingUserMessages(listener)` | Receive pending-state replacements across observed sessions. |
176
+ | `subscribeSubagentActivities(listener)` | Receive subagent-activity changes across observed sessions. |
177
+ | `subscribeEphemeral(listener)` | Receive best-effort ephemeral events across observed sessions. |
178
+ | `close()` | Close the client and its owned resources. |
179
+
180
+ Each subscription returns an unsubscribe function.
181
+
182
+ ## Use the session facade
183
+
184
+ `TauSdkSession.id` is the bound session id. Its methods map directly to protocol operations; the [method reference](session-protocol-methods.md) defines exact results, turn outcomes, busy rules, and mutation behavior.
185
+
186
+ | SDK method | Purpose |
187
+ | --- | --- |
188
+ | `record(text, options?)` | Append user text without running a turn. |
189
+ | `submit(text, options?)` | Append user text and run an idle session turn. |
190
+ | `queue(text, options?)` | Run now or wait behind active session work. |
191
+ | `steer(text)` | Redirect active model work at a safe boundary. |
192
+ | `cancelPendingMessages()` | Cancel all queued and unapplied steering input. |
193
+ | `retry()` | Run from current history without appending user text. |
194
+ | `exec(command, options?)` | Run an independent login-Bash command in the execution environment. |
195
+ | `sample({ context, options })` | Run isolated inference without mutating the session. |
196
+ | `interrupt()` | Request cancellation of active session work. |
197
+ | `snapshot()` | Read the complete authoritative snapshot. |
198
+ | `startGoal(objective)` | Create and run a persistent autonomous goal. |
199
+ | `resumeGoal()` | Continue a blocked goal. |
200
+ | `clearGoal()` | Clear the current goal and return the updated snapshot. |
201
+ | `setReasoning(reasoning)` | Set reasoning for the next independently started turn. |
202
+ | `setPersona(personaId)` | Change persona and return the updated snapshot. |
203
+ | `resolvePrompt(promptId)` | Load a current prompt body from the execution environment. |
204
+ | `autocompletePaths({ query, limit })` | Request bounded execution-environment path suggestions. |
205
+ | `reload()` | Reload session-owned configuration and content. |
206
+ | `compact(mode, options?)` | Manually compact model context. |
207
+ | `rewindToHistoryEntryId(id)` | Rewind from one user history entry while idle. |
208
+ | `interruptSubagent(subagentId)` | Interrupt one live subagent run. |
209
+ | `createEphemeralContext(options)` | Create a non-persisted host-owned agent context. |
210
+ | `submitEphemeralThread(options)` | Run or continue one ephemeral thread. |
211
+ | `closeEphemeralContext(contextId)` | Close an ephemeral context and its threads. |
212
+ | `unobserve()` | Stop observation and retire this facade. |
213
+
214
+ `submit`, `queue`, and `record` accept `{ historyEntryId?: string }`. When omitted, Tau generates the user history id. Use `getTauSdkSessionTurnRecord(snapshot, id)` to distinguish unknown, running, and settled accepted turns, or `getTauSdkSessionTurnOutcome(snapshot, id)` when only a settled outcome matters.
215
+
216
+ ### Execute a command
217
+
218
+ `exec` generates a unique wire-level execution id and supports exact positional arguments, environment overrides other than `HOME`, binary stdin, a command `cwd`, timeout, capture limit, and cancellation signal:
219
+
220
+ ```ts
221
+ const controller = new AbortController();
222
+ const result = await session.exec('exec "$0" "$@"', {
223
+ args: ["git", "status", "--short"],
224
+ cwd: "/srv/workspaces/atlas",
225
+ timeoutMs: 10_000,
226
+ maxCaptureBytes: 256 * 1024,
227
+ signal: controller.signal,
228
+ });
229
+
230
+ console.log(result.exitCode, result.output);
231
+ ```
232
+
233
+ Aborting the signal sends targeted `session.cancelExec` and rejects the SDK call. It does not interrupt turns or other executions. The operation does not enter conversation history.
234
+
235
+ ### Sample a model
236
+
237
+ `sample` uses the session's active resolved model target and credentials but only the supplied provider-neutral context:
238
+
239
+ ```ts
240
+ const sampled = await session.sample({
241
+ context: {
242
+ systemPrompt: "Classify the request in one word.",
243
+ messages: [
244
+ {
245
+ role: "user",
246
+ content: [{ type: "text", text: "I cannot log in." }],
247
+ timestamp: Date.now(),
248
+ },
249
+ ],
250
+ },
251
+ options: { reasoning: "low", maxTokens: 100 },
252
+ });
253
+
254
+ console.log(sampled.message.content);
255
+ ```
256
+
257
+ The returned complete assistant message can be reused in a later sampling context. Optional tool schemas can produce tool-call content, but Tau does not execute those calls. Sampling does not emit deltas, change snapshot revision or cost, or write conversation history.
258
+
259
+ ## Consume streamed state
260
+
261
+ The SDK exposes connection-wide subscriptions on `TauSdkClient` and session-filtered subscriptions on `TauSdkSession`. Prefer the session facade unless one component deliberately multiplexes several sessions.
262
+
263
+ ### Snapshot deltas
264
+
265
+ `session.onDelta(listener)` receives only that session's `TauSdkDelta` messages. The facade buffers deltas received before its first local delta listener and replays them when that listener is attached. Use `applySessionProtocolDelta` against an installed snapshot. On any revision gap or invalid transition, refresh with `session.snapshot()`.
266
+
267
+ A method that returns an authoritative snapshot, such as reload, compact, rewind, persona change, or goal clearing, lets the facade discard buffered deltas through that revision. Do not also replay stale presentation transitions from those discarded deltas.
268
+
269
+ ### Pending input
270
+
271
+ `session.pendingUserMessages()` returns a clone of the current full pending-message state. `session.onPendingUserMessages(listener)` immediately emits the current baseline, then newer full replacements. Its revision is independent of the snapshot.
272
+
273
+ ```ts
274
+ const stopPending = session.onPendingUserMessages(({ state }) => {
275
+ console.log(state.messages.map((message) => [message.mode, message.text]));
276
+ });
277
+ ```
278
+
279
+ Pending input is shared among clients observing the live hosted session and starts empty after recovery.
280
+
281
+ ### Subagent activity
282
+
283
+ `session.subagentActivities()` returns the current transient activity state. `session.onSubagentActivities(listener)` immediately emits the current agents as `agent.set` changes, then later per-agent replacements and removals.
284
+
285
+ For a custom accumulator, use `applySessionProtocolSubagentActivitiesMessage`. This channel has its own revision and does not replace durable `snapshot.agents`.
286
+
287
+ ### Ephemeral events
288
+
289
+ `session.onEphemeral(listener)` receives best-effort footer notices, ephemeral thread progress, and non-recoverable timeline notices. These events are not replayed or included in `session.snapshot()`.
290
+
291
+ If rendering `timeline.item`, accept only the active epoch and merge by its allocated sequence. Compaction and rewind causes determine which client-local ephemeral items remain valid. The [session protocol](session-protocol.md) gives the application rules.
292
+
293
+ ## Provide client tools
294
+
295
+ Pass `TauSdkClientTool` entries in `clientTools` when model-facing work must run in the integration process:
296
+
297
+ ```ts
298
+ const client = await createTauSdkClient({
299
+ clientTools: [
300
+ {
301
+ schema: {
302
+ name: "local_picker",
303
+ description: "Choose one item from the user's local workspace.",
304
+ parameters: {
305
+ type: "object",
306
+ properties: {},
307
+ additionalProperties: false,
308
+ },
309
+ executionTimeoutMs: 60_000,
310
+ },
311
+ execute: async (_args, context) => {
312
+ context.signal.throwIfAborted();
313
+ const status = await context.executionEnvironment.exec(
314
+ "git status --short",
315
+ {
316
+ signal: context.signal,
317
+ },
318
+ );
319
+ return status.output || "Working tree is clean.";
320
+ },
321
+ },
322
+ ],
323
+ });
324
+ ```
325
+
326
+ 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
+
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.
329
+
330
+ 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
+
332
+ ### Build a code-mode tool
333
+
334
+ `createTauCodeModeClientTool()` wraps a bounded one-shot JavaScript API as an SDK client tool:
335
+
336
+ ```ts
337
+ import {
338
+ buildTauCodeModeToolDescription,
339
+ createTauCodeModeClientTool,
340
+ } from "@markusylisiurunen/tau/sdk";
341
+
342
+ const name = "tickets";
343
+ const tickets = createTauCodeModeClientTool({
344
+ name,
345
+ description: buildTauCodeModeToolDescription({
346
+ name,
347
+ description: "Read support tickets.",
348
+ }),
349
+ documentation: "# Tickets API\n\nUse `tickets.get(id)` to read one ticket.",
350
+ api: {
351
+ get: async ([id], { signal }) => ticketClient.get(String(id), { signal }),
352
+ },
353
+ });
354
+ ```
355
+
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.
357
+
358
+ 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
+
360
+ ## Cancel and close deliberately
361
+
362
+ 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
+
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.
365
+
366
+ Always close clients in `finally`. Do not continue using a session facade after unobserve or any client after close.
367
+
368
+ ## Handle errors
369
+
370
+ All exported SDK and transport errors extend `TauSessionClientError`:
371
+
372
+ - `TauSessionProtocolResponseError` means the host returned a protocol error. It exposes `code`, `message`, `requestId`, and optional `data`.
373
+ - `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
+
376
+ 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
+
378
+ Listener exceptions are isolated from SDK event delivery. Handle failures inside listeners and keep state application deterministic.
379
+
380
+ ## Use the public types and helpers
381
+
382
+ The SDK entry point exports the types needed at integration boundaries rather than requiring imports from internal modules:
383
+
384
+ - `TauSdkClient`, `TauSdkSession`, client option types, session summaries, and request and result aliases;
385
+ - `TauSdkDelta`, `SessionProtocolSnapshot`, pending-message types, subagent-activity types, and ephemeral event types;
386
+ - `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.
388
+
389
+ It also exports `applySessionProtocolDelta`, `applySessionProtocolSubagentActivitiesMessage`, the turn-ledger helpers, and user-text projection helpers:
390
+
391
+ ```ts
392
+ import {
393
+ getTauUserDisplayText,
394
+ getTauUserModelText,
395
+ projectTauUserText,
396
+ } from "@markusylisiurunen/tau/sdk";
397
+ ```
398
+
399
+ Use `getTauUserDisplayText` before rendering raw snapshot user text. It removes Tau metadata and leading exact hidden system blocks. `getTauUserModelText` removes Tau metadata while preserving model-facing hidden instructions. `projectTauUserText` returns both views together. These helpers apply only to user messages.