@markusylisiurunen/tau 0.3.97 → 0.3.99

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 (84) hide show
  1. package/dist/code_mode/runtime.js +1 -1
  2. package/dist/code_mode/runtime.js.map +1 -1
  3. package/dist/core/code_mode/agents.js +9 -1
  4. package/dist/core/code_mode/agents.js.map +1 -1
  5. package/dist/core/code_mode/models.js +69 -21
  6. package/dist/core/code_mode/models.js.map +1 -1
  7. package/dist/core/config/content_loader.js +27 -1
  8. package/dist/core/config/content_loader.js.map +1 -1
  9. package/dist/core/config/schema.js +9 -3
  10. package/dist/core/config/schema.js.map +1 -1
  11. package/dist/core/models/model_id.js +12 -0
  12. package/dist/core/models/model_id.js.map +1 -0
  13. package/dist/core/models/openrouter.js +63 -52
  14. package/dist/core/models/openrouter.js.map +1 -1
  15. package/dist/core/models/openrouter_budget.js +54 -0
  16. package/dist/core/models/openrouter_budget.js.map +1 -0
  17. package/dist/core/models/openrouter_media.js +20 -17
  18. package/dist/core/models/openrouter_media.js.map +1 -1
  19. package/dist/core/persona_selection.js +38 -0
  20. package/dist/core/persona_selection.js.map +1 -0
  21. package/dist/core/runtime/chat_runtime.js +20 -3
  22. package/dist/core/runtime/chat_runtime.js.map +1 -1
  23. package/dist/core/session/auto_compaction_threshold.js.map +1 -1
  24. package/dist/core/static/code_mode/history/documentation.md +1 -1
  25. package/dist/core/static/code_mode/sandbox_runner.mjs +1 -1
  26. package/dist/core/static/tau_docs/code-mode.md +2 -2
  27. package/dist/core/static/tau_docs/configuration.md +1 -1
  28. package/dist/core/static/tau_docs/history.md +6 -3
  29. package/dist/core/static/tau_docs/node-sdk.md +5 -5
  30. package/dist/core/static/tau_docs/openrouter.md +11 -10
  31. package/dist/core/static/tau_docs/ownership-and-scope.md +1 -1
  32. package/dist/core/static/tau_docs/personas.md +18 -3
  33. package/dist/core/static/tau_docs/remote-sessions.md +1 -1
  34. package/dist/core/static/tau_docs/session-protocol-methods.md +15 -6
  35. package/dist/core/static/tau_docs/session-protocol.md +2 -2
  36. package/dist/core/static/tau_docs/sessions.md +15 -4
  37. package/dist/core/static/tau_docs/subagents.md +10 -0
  38. package/dist/core/static/tau_docs/telegram-projects.md +4 -7
  39. package/dist/core/static/tau_docs/telegram.md +67 -21
  40. package/dist/core/static/tau_docs/troubleshooting.md +2 -0
  41. package/dist/core/static/tau_docs/tui.md +2 -2
  42. package/dist/core/subagents/launch_model.js +4 -7
  43. package/dist/core/subagents/launch_model.js.map +1 -1
  44. package/dist/core/subagents/native_subagent_runtime.js +1 -1
  45. package/dist/core/subagents/native_subagent_runtime.js.map +1 -1
  46. package/dist/core/subagents/registry.js.map +1 -1
  47. package/dist/core/telegram/adapter.js +70 -68
  48. package/dist/core/telegram/adapter.js.map +1 -1
  49. package/dist/core/telegram/config.js +43 -52
  50. package/dist/core/telegram/config.js.map +1 -1
  51. package/dist/core/telegram/local_session_client.js +5 -2
  52. package/dist/core/telegram/local_session_client.js.map +1 -1
  53. package/dist/core/telegram/runtime.js +14 -1
  54. package/dist/core/telegram/runtime.js.map +1 -1
  55. package/dist/core/telegram/session_manager.js +6 -7
  56. package/dist/core/telegram/session_manager.js.map +1 -1
  57. package/dist/core/tool/openrouter.js +1 -1
  58. package/dist/core/tool/openrouter.js.map +1 -1
  59. package/dist/core/tools/catalog.js +1 -0
  60. package/dist/core/tools/catalog.js.map +1 -1
  61. package/dist/core/tools/code_mode_worker.js +2 -2
  62. package/dist/core/tools/code_mode_worker.js.map +1 -1
  63. package/dist/core/utils/model_notices.js +0 -19
  64. package/dist/core/utils/model_notices.js.map +1 -1
  65. package/dist/core/version.js +1 -1
  66. package/dist/host/hosted_ephemeral_agent_session.js +1 -1
  67. package/dist/host/hosted_ephemeral_agent_session.js.map +1 -1
  68. package/dist/host/local_session_host.js +51 -15
  69. package/dist/host/local_session_host.js.map +1 -1
  70. package/dist/host/session_host.js.map +1 -1
  71. package/dist/host/session_protocol_handler.js +9 -1
  72. package/dist/host/session_protocol_handler.js.map +1 -1
  73. package/dist/protocol/session_protocol.d.ts +7 -2
  74. package/dist/protocol/session_protocol.js +21 -8
  75. package/dist/protocol/session_protocol.js.map +1 -1
  76. package/dist/sdk/local_client.d.ts +6 -0
  77. package/dist/sdk/local_client.js +14 -6
  78. package/dist/sdk/local_client.js.map +1 -1
  79. package/dist/sdk/session.js +4 -3
  80. package/dist/sdk/session.js.map +1 -1
  81. package/dist/sdk/types.d.ts +2 -2
  82. package/dist/tui/session_chat_controller.js +4 -1
  83. package/dist/tui/session_chat_controller.js.map +1 -1
  84. package/package.json +5 -5
@@ -166,7 +166,7 @@ For `tau serve`, make the changes on the host machine and restart the server. Re
166
166
 
167
167
  Some settings apply only when something new is created. `defaultPersona` and startup persona flags choose the persona of a new session. The execution environment's kind, identity, `cwd`, and home are fixed when the session is created or recovered.
168
168
 
169
- The Telegram runner loads its speech provider and its runner config at startup. Restart it after changing speech, projects, routing, or workspace preparation.
169
+ The Telegram runner loads its speech provider and its runner config at startup. Restart it after changing speech, bot startup settings, presets, projects, routing, or workspace preparation.
170
170
 
171
171
  - Workspace-preparation changes apply when a managed workspace is created or reconstructed. Restarting does not change a preserved workspace.
172
172
  - Command client tools are chosen when the runner creates the client for a session. Changes apply to new sessions, and also to existing sessions when a runner restart recovers them.
@@ -45,18 +45,21 @@ These rules differ from how the session itself behaves. Use [sessions](sessions.
45
45
 
46
46
  ## Filter by attributes
47
47
 
48
- Session creation attributes are string pairs set by the client that created the session, and they never change. History stores them and can filter on exact values or on case-sensitive substrings. They describe where a session came from; they are not instructions.
48
+ Session creation attributes are string pairs recorded when a session is created, and they never change. Tau sets `machine` and `cwd`, and the client that created the session sets the others. History stores them and can filter on exact values or on case-sensitive substrings. They describe where a session came from; they are not instructions.
49
49
 
50
- Two common attributes are:
50
+ Common attributes are:
51
51
 
52
52
  | Attribute | Meaning |
53
53
  | --- | --- |
54
54
  | `source` | The creating client, usually `tui`, `telegram`, or a value an SDK caller chose |
55
55
  | `repository` | Normalized `host/owner/repository`; composite workspaces join repositories with commas |
56
+ | `machine` | Hostname of the execution environment |
57
+ | `cwd` | The session's working directory in the execution environment |
58
+ | `telegram_bot` | ID of the Telegram bot that owns the session, as configured under `bots` |
56
59
 
57
60
  A local TUI usually fills in `repository` from the current Git repository or repositories directly below it. Telegram fills it in for repository projects and leaves it out for persistent-directory projects. Sessions created with `tau attach` may not have it, and SDK or protocol clients set only the attributes they choose.
58
61
 
59
- Because composite values are comma-separated, a substring filter on `repository` finds both single-repository and composite sessions. Attributes may be missing, out of date, or set deliberately by a client, so confirm important facts from the transcript or the current workspace.
62
+ Because composite values are comma-separated, a substring filter on `repository` finds both single-repository and composite sessions. Attributes may be missing, out of date, or set deliberately by a client, so confirm important facts from the transcript or the current workspace. For example, older saved sessions may lack `machine` and `cwd`, because recovery never changes attributes.
60
63
 
61
64
  ## Agent access is explicit and read-only
62
65
 
@@ -147,11 +147,11 @@ try {
147
147
  }
148
148
  ```
149
149
 
150
- `client.sessions.create(input)` sends `session.create`, then observes the new session before returning. `client.sessions.observe(sessionId)` observes an existing session. `client.sessions.list()` returns `{ sessionId, lifecycle }` for each session.
150
+ `client.sessions.create(input)` sends `session.create`. `client.sessions.list()` returns `{ sessionId, lifecycle }` for each session.
151
151
 
152
- Creating a session requires the full execution environment and the creation attributes, which never change afterward. The `cwd` must be absolute and is a path in the chosen environment, which may not be where the SDK runs. A Fly Sprite session refers to an existing Sprite that the host is configured to reach. See [sessions](sessions.md) for creation attributes and [ownership and scope](ownership-and-scope.md) for which machine owns which paths.
152
+ Creating a session requires the full execution environment and the creation attributes, which never change afterward. Tau sets the `machine` and `cwd` attributes itself and replaces any values you send for them. The `cwd` of the execution environment must be absolute and is a path in the chosen environment, which may not be where the SDK runs. A Fly Sprite session refers to an existing Sprite that the host is configured to reach. See [sessions](sessions.md) for creation attributes and [ownership and scope](ownership-and-scope.md) for which machine owns which paths.
153
153
 
154
- To set the main conversation's automatic-compaction threshold at creation, pass `autoCompactThresholdTokens` as a safe integer of at least 50,000. Omit it or pass `null` to use the model's context window minus 16,384 tokens. The effective threshold cannot exceed this model-based default.
154
+ To set the main conversation's automatic-compaction threshold at creation, pass `autoCompactThresholdTokens` as a safe integer of at least 50,000. Omit it or pass `null` to use the persona default, falling back to the model's context window minus 16,384 tokens. Applying a persona resets the threshold to `null`, even when reapplying the same persona, unless the call supplies a new threshold. The effective threshold cannot exceed this model-based limit.
155
155
 
156
156
  Use `session.setAutoCompactThreshold(50000)` to change the threshold from the next logical turn, or `session.setAutoCompactThreshold(null)` to restore the default. Snapshot `settings` contain both `autoCompactThresholdTokens` (the configured value) and `effectiveAutoCompactThresholdTokens` (the trigger for the selected model). See [automatic compaction](sessions.md#automatic-compaction) for timing, limits, and costs.
157
157
 
@@ -191,7 +191,7 @@ Each subscription returns an unsubscribe function.
191
191
  | `snapshot()` | Read the complete current snapshot. |
192
192
  | `setAutoCompactThreshold(thresholdTokens)` | Set or reset automatic compaction, starting with the next logical turn. |
193
193
  | `setReasoning(reasoning)` | Set reasoning, starting with the next turn. |
194
- | `setPersona(personaId)` | Change persona and return the updated snapshot. |
194
+ | `setPersona(personaId, overrides?)` | Atomically apply a persona, with optional `{ reasoning, autoCompactThresholdTokens }` overrides, and return the updated snapshot. Pass `null` without overrides to apply the workspace default. |
195
195
  | `resolvePrompt(promptId)` | Load a current prompt body from the execution environment. |
196
196
  | `autocompletePaths({ query, limit })` | Get a limited list of path suggestions from the execution environment. |
197
197
  | `reload()` | Reload the session's configuration and content. |
@@ -393,7 +393,7 @@ The SDK also exports `executeTauCodeMode` for standalone runs. The separate `@ma
393
393
 
394
394
  Most turn and session-changing methods do not accept an `AbortSignal`. Call `session.interrupt()` to stop running work on the host. `session.exec()` is the exception: its optional signal cancels only that command.
395
395
 
396
- `session.unobserve()` ends one session object. `client.close()` ends the connection: it rejects open requests, aborts client tool handlers, waits for them to finish, and closes the transport. For the default in-process client, it also saves sessions and shuts down the host. Over WebSocket, the remote host and its sessions keep running.
396
+ `client.close()` ends the connection: it rejects open requests, aborts client tool handlers, waits for them to finish, and closes the transport. For the default in-process client, it also saves sessions and shuts down the host. Over WebSocket, the remote host and its sessions keep running.
397
397
 
398
398
  Always close clients in `finally`. Never use a session object after `unobserve()`, or a client after `close()`.
399
399
 
@@ -87,7 +87,7 @@ Instructions and criterion guidance accept strings, JSON objects, or arrays; cho
87
87
 
88
88
  Luna Decisions supports at most 200 questions, 255 categories per choice question, and 2–10 rubric levels per score question. Tau checks these limits before sending the request. Jev also accepts a single-level score rubric. Both models use the same JSON input format.
89
89
 
90
- Luna also accepts up to 16 ordered local PNG/JPEG/WebP images using repeatable `--image <path>` flags:
90
+ Luna also accepts up to 64 ordered local PNG/JPEG/WebP images using repeatable `--image <path>` flags:
91
91
 
92
92
  ```sh
93
93
  tau tool openrouter decisions \
@@ -136,7 +136,7 @@ Flags belong after `chat`. No positional arguments are accepted.
136
136
  | `--system <text>` or `--system-file <path>` | Optional, mutually exclusive, nonblank explicit system instructions |
137
137
  | `--image <path>` | Repeatable local PNG, JPEG, or WebP image |
138
138
  | `--audio <path>` | Repeatable local WAV or MP3 audio; Gemini only |
139
- | `--video <path>` | Local MP4 video; Gemini only |
139
+ | `--video <path>` | Repeatable local MP4 video; Gemini only |
140
140
  | `--reasoning <effort>` | Optional model-supported effort from the table |
141
141
 
142
142
  All file paths resolve relative to the invoking working directory. Prompt/system files name regular files; `-` has special stdin meaning only for decisions. Omitting system instructions sends no system message. Omitting reasoning leaves that parameter to the provider, not necessarily disabled. Effort does not request a written reasoning trace.
@@ -164,19 +164,20 @@ These are successful exchanges with distinguishable outcomes; scripts must inspe
164
164
 
165
165
  ## Outputs and recovery
166
166
 
167
- These are cautious safety limits set by Tau, not the providers' maximum capacities. Byte limits use decimal units.
167
+ Tau sets these limits to reject abnormal input. They are not the providers' maximum capacities, and a request within them can still exceed the selected model's context window or a provider limit. 1 MiB is 1,048,576 bytes.
168
168
 
169
169
  | Resource | Limit |
170
170
  | --- | --- |
171
- | Decisions JSON, each prompt or system input | 1,000,000 UTF-8 bytes |
172
- | Attachments per request | 16 total, including at most 4 audio files and 1 video |
173
- | Each image | 5,000,000 bytes, 8,000 pixels per edge, 32 megapixels; one frame |
174
- | Each audio or video | 12,000,000 bytes |
171
+ | Decisions JSON | 20 MiB of UTF-8 |
172
+ | Chat prompt, system instructions, and schema | 20 MiB of UTF-8 combined |
173
+ | Attachments per request | 64 of any mix of types; 16 for Jev |
174
+ | Each image | 32 MiB, 32,768 pixels per edge, 100 megapixels; one frame |
175
+ | Each audio or video | 256 MiB |
175
176
  | Audio formats | Single-stream PCM 16-bit little-endian WAV or MP3 |
176
177
  | Video format | H.264 MP4 up to 3840×2160 with optional AAC audio |
177
- | Audio/video duration | Known positive duration; 600 seconds combined |
178
- | Complete encoded JSON request | 20,000,000 bytes, including base64 expansion and text |
179
- | Response | 8,000,000 bytes |
178
+ | Audio/video duration | Known positive duration; 7,200 seconds (2 hours) combined |
179
+ | Complete encoded JSON request | 512 MiB, including base64 expansion and text |
180
+ | Response | 32 MiB |
180
181
  | Local media probe | 15 seconds per file |
181
182
  | HTTP request and response | 10 minutes |
182
183
 
@@ -72,7 +72,7 @@ Start from the behavior that consumes the setting:
72
72
  1. If it changes what the agent sees or can do in the project, edit configuration or content in the execution environment's discovery path.
73
73
  2. If it changes the TUI's theme, speech, or command client tools, edit the TUI client's configuration and restart that client.
74
74
  3. If it changes credentials, session storage, remote history, or hosted execution environment connections, set it for the host process and restart the host when required.
75
- 4. If it changes Telegram routing or workspace preparation, edit the runner's `--config-file` on the runner machine.
75
+ 4. If it changes Telegram routing, bot startup settings, presets, or workspace preparation, edit the runner's `--config-file` on the runner machine.
76
76
 
77
77
  For a local `tau` session these locations are often the same home and repository. Following the ownership rule anyway means the configuration still works when the session becomes remote.
78
78
 
@@ -92,6 +92,20 @@ Each custom persona is complete on its own. It does not inherit a provider, mode
92
92
  | `serviceTier` | `priority` or `flex`. Used only by `openai` and `openai-codex`. |
93
93
  | `allowedReasoningLevels` | Array of reasoning efforts offered by the TUI selector. |
94
94
  | `tools` | List of persona-selectable tools. |
95
+ | `autoCompactThresholdTokens` | Default automatic-compaction threshold: a safe integer of at least 50,000 tokens. |
96
+ | `subagentAutoCompactThresholdTokens` | Map from `<provider>/<model>` IDs (without reasoning effort) to safe integer thresholds of at least 50,000 tokens for native Tau subagents launched with that model. |
97
+
98
+ For example, these optional frontmatter fields give the main conversation a 200,000-token default and explicit launches of one subagent model an 80,000-token default:
99
+
100
+ ```yaml
101
+ autoCompactThresholdTokens: 200000
102
+ subagentAutoCompactThresholdTokens:
103
+ openai/gpt-6.1-sol: 80000
104
+ ```
105
+
106
+ In `subagentAutoCompactThresholdTokens` keys, the provider is lowercased and the model ID is kept exactly as written, so `OpenAI/gpt-6.1-sol` and `openai/gpt-6.1-sol` name the same model. The provider must be known. A model ID missing from the catalog is allowed, as for `model`. Claude Code models are not allowed because Claude Code manages its own compaction.
107
+
108
+ Thresholds are capped at the selected model's context window minus 16,384 tokens. A session's `/auto-compact` override takes precedence over the persona default, and so does a Telegram bot's default threshold. See [sessions](sessions.md) and [subagents](subagents.md) for inheritance and reset behavior.
95
109
 
96
110
  Every persona can use all discovered skills. If a custom persona omits `tools`, Tau enables the standard tools and `agents`. A `tools` list without `agents` prevents the agent from starting or managing subagents. See [skills](skills.md) and [subagents](subagents.md) for those contracts.
97
111
 
@@ -113,7 +127,7 @@ A `tools` list replaces the defaults. Names are lowercased and duplicates remove
113
127
  }
114
128
  ```
115
129
 
116
- The most specific `defaultPersona` wins. The ID must match exactly, including case. An unknown default produces a warning, and Tau uses the first available persona.
130
+ The most specific `defaultPersona` wins. The ID must match exactly, including case. An unknown default produces a warning, and Tau uses the first available persona. Telegram [workspace startup and presets](telegram.md#startup-and-presets) select the default the same way.
117
131
 
118
132
  Override the default for one TUI launch:
119
133
 
@@ -135,7 +149,7 @@ This command matches the ID case-insensitively. `Ctrl+P` cycles through the avai
135
149
 
136
150
  `reasoning` is the persona's default effort. A startup suffix, the session reasoning command, or `Shift+Tab` can override it. You can change reasoning while a turn is running, but the running turn keeps the model and tool settings it started with. The new effort applies from the next submitted or queued turn.
137
151
 
138
- `allowedReasoningLevels` sets which values the TUI cycles through for the persona. It limits only the TUI selector; protocol clients can still set other levels. When it is omitted, the TUI offers all standard levels for a model that supports reasoning. For a model whose catalog entry says `reasoning: false`, the selector shows only `none`.
152
+ `allowedReasoningLevels` sets which values the TUI cycles through for the persona. It also limits an effort passed to `setPersona`, including a Telegram preset's `reasoning`. TUI startup suffixes, `defaultPersona` suffixes, and `session.setReasoning` can still set other levels. When it is omitted, the TUI offers all standard levels for a model that supports reasoning. For a model whose catalog entry says `reasoning: false`, the selector shows only `none`.
139
153
 
140
154
  An empty `allowedReasoningLevels` does not create an empty selector. The TUI uses its normal model-aware choices.
141
155
 
@@ -145,7 +159,7 @@ An empty `allowedReasoningLevels` does not create an empty selector. The TUI use
145
159
 
146
160
  After editing personas, skills, or project context, run `/reload` while the session is idle. Reload finds personas again and rebuilds the session's prompt and tools.
147
161
 
148
- If the current persona ID still exists, Tau applies its newly loaded definition. This can reset settings such as reasoning or service tier to the definition's values. If the ID is gone, Tau selects the first available persona. Existing messages stay as they are; a persona change never rewrites earlier history.
162
+ If the current persona ID still exists, Tau applies its newly loaded definition and keeps the session's current settings, including reasoning and service tier. Selecting the persona again applies its declared defaults. If the ID is gone, Tau selects the first available persona. Existing messages stay as they are; a persona change never rewrites earlier history.
149
163
 
150
164
  The TUI refuses to reload while a turn is running. Subagents that are already running keep the setup they started with. Subagents started after the reload use the reloaded persona. See [subagents](subagents.md).
151
165
 
@@ -158,6 +172,7 @@ An invalid persona is skipped and reported as a configuration warning. Other val
158
172
  - an `id` that does not exactly match the filename;
159
173
  - an unknown provider or an unresolved model;
160
174
  - an invalid reasoning effort or service tier;
175
+ - an automatic-compaction threshold below 50,000, or a `subagentAutoCompactThresholdTokens` key with an unknown provider or the `claude-code` provider;
161
176
  - an unknown persona tool.
162
177
 
163
178
  `/reload` shows warnings, with file paths, in the transcript. For a new local session, `tau --debug --persona <id>` shows the selected model, settings, skills, subagents, tools, and the complete prompt. If an edit seems to have no effect in a remote session, confirm the session's execution environment and `cwd` before editing another copy of the file. [Troubleshooting](troubleshooting.md) covers that check in more detail.
@@ -85,7 +85,7 @@ tau attach \
85
85
 
86
86
  With the default `local` execution kind, this path is on the host machine, not the attaching machine. The directory must already exist and contain the repository or workspace you want. Tau creates only the session, not the directory or repository.
87
87
 
88
- A remote `--new` sets the creation attribute `source: "tui"`. It does not look at the remote directory to fill in repository information. Clients that need it should create sessions through the SDK or protocol and send complete attributes. See [sessions](sessions.md).
88
+ A remote `--new` sets the creation attribute `source: "tui"`. It does not look at the remote directory to fill in repository information. Clients that need it should create sessions through the SDK or protocol and send `repository` themselves. The host still records `machine` and `cwd` for the session. See [sessions](sessions.md).
89
89
 
90
90
  `tau attach` does not accept the persona flags of local startup. Set the server's default when starting the host, for example `tau serve --persona opus-5.5-coder`, or switch the new session with `/persona:<id>` after attaching.
91
91
 
@@ -79,7 +79,9 @@ result: { sessionId: string }
79
79
 
80
80
  `cwd` must be an absolute path inside the chosen execution environment. A Fly Sprite must already exist, and the host must have a `flySprites` connection configured. Tau does not create the target or the repository.
81
81
 
82
- `attributes` is required, even when empty. It accepts at most 32 pairs, which never change after creation. Keys are 1 to 64 characters and values at most 1,024 characters. Tau stores them as given and does not fill in missing ones. Conventional attributes and their use are covered in [sessions](sessions.md) and [history](history.md).
82
+ `attributes` is required, even when empty. Tau adds `machine` and `cwd` from the execution environment and replaces any values the caller sends for those two keys. It stores other attributes as given and does not fill in missing ones. Attributes never change after creation. Conventional attributes and their use are covered in [sessions](sessions.md) and [history](history.md).
83
+
84
+ The final set can have at most 32 pairs, so a caller can send at most 30 attributes other than `machine` and `cwd`. Keys are 1 to 64 characters and values at most 1,024 characters. `executionEnvironment.cwd` has the same 1,024-character limit because it becomes an attribute value. A request that breaks these limits returns `invalid_params` before Tau starts the execution environment. Creation can still fail after that if Tau cannot read the hostname from the execution environment, or if the resulting `machine` or `cwd` value is too long.
83
85
 
84
86
  A local `env` sets environment variables for the execution environment. Names must be valid variable names, values cannot contain NUL, and `HOME` is not allowed because the execution environment sets it. These variables are saved with the session, so do not put secrets there unless the session store is protected accordingly.
85
87
 
@@ -332,13 +334,13 @@ result: {
332
334
  }
333
335
  ```
334
336
 
335
- `thresholdTokens` must be a safe integer of at least 50,000, or `null` to restore the model-based default. Numeric strings are rejected. Session creation accepts the same values in the optional `autoCompactThresholdTokens` field, which defaults to `null`.
337
+ `thresholdTokens` must be a safe integer of at least 50,000, or `null` to remove the session override and use the default threshold. Numeric strings are rejected. Session creation accepts the same values in the optional `autoCompactThresholdTokens` field, which defaults to `null`.
336
338
 
337
- The default threshold is the model's context window minus 16,384 tokens. With an override, the effective threshold is the smaller of the configured value and that default. A model with a small context window can therefore have an effective threshold below 50,000.
339
+ The effective threshold uses the session override, then any host default (such as a [Telegram bot's threshold](telegram.md#compaction-thresholds)), then the persona default, then the model's context window minus 16,384 tokens. It cannot exceed that model-based limit. A model with a small context window can therefore have an effective threshold below 50,000.
338
340
 
339
341
  The host saves the setting and publishes a `settings.set` delta to observers. Changing it does not start compaction. A running turn and its steering continuations keep their original threshold.
340
342
 
341
- Reloads, persona changes, and recovery preserve the configured value. The effective threshold is recalculated for the selected model. Subagents and ephemeral threads use their model-based defaults. See [automatic compaction](sessions.md#automatic-compaction) for request-size limits and costs.
343
+ Reloads, reasoning changes, and recovery preserve the configured value. `session.setPersona` sets it to the supplied threshold, or to `null` if none is supplied, even when reapplying the current persona. The effective threshold is recalculated for the selected persona and model. Native Tau subagents launched without a model override inherit the parent's launching-turn threshold. Native launches with an explicit model use the persona's subagent threshold for that model, or the model's default. Claude Code subagents manage their own compaction. Ephemeral threads use the persona default without the main session override. See [automatic compaction](sessions.md#automatic-compaction) for request-size limits and costs.
342
344
 
343
345
  ### `session.setReasoning`
344
346
 
@@ -359,16 +361,23 @@ The change waits its turn behind other changes but does not interrupt a running
359
361
 
360
362
  ### `session.setPersona`
361
363
 
362
- Changes the persona to an id in the session catalog.
364
+ Applies a persona and optional settings atomically. A string `personaId` must exactly match an ID in the session catalog. `personaId: null` selects the current workspace default, including its effort suffix, and accepts no overrides. If that default names an unavailable persona, the first persona in the catalog is selected.
363
365
 
364
366
  ```ts
365
367
  params: {
366
368
  sessionId: string;
367
- personaId: string;
369
+ personaId: string | null;
370
+ reasoning?: Reasoning;
371
+ autoCompactThresholdTokens?: number;
368
372
  }
369
373
  result: SessionProtocolSnapshot;
370
374
  ```
371
375
 
376
+ - `reasoning` overrides the persona's default effort. Without it, the persona's default applies.
377
+ - `autoCompactThresholdTokens` sets the session's compaction override, as a safe integer of at least 50,000. Without it, the override is cleared.
378
+
379
+ The `reasoning` override must be in the persona's `allowedReasoningLevels` when that list is non-empty, and a model without reasoning accepts only `none`. These limits do not apply to a workspace default's effort suffix. An unavailable persona or unsupported override fails with `invalid_request` before any setting changes.
380
+
372
381
  This changes the context and waits its turn behind other changes. It interrupts a running turn and rejects pending input, then returns the new snapshot.
373
382
 
374
383
  ### `session.resolvePrompt`
@@ -110,8 +110,8 @@ Observing controls which updates you receive; it does not make you the owner. `s
110
110
 
111
111
  Both auto-compaction fields in `settings` are required:
112
112
 
113
- - `autoCompactThresholdTokens` is the configured value: a safe integer of at least 50,000, or `null` for the model-based default.
114
- - `effectiveAutoCompactThresholdTokens` is the integer threshold clients should display. It is the model's context window minus 16,384 tokens, or the configured value if that is smaller.
113
+ - `autoCompactThresholdTokens` is the configured value: a safe integer of at least 50,000, or `null` to use the default threshold.
114
+ - `effectiveAutoCompactThresholdTokens` is the integer threshold clients should display. It resolves the session override, then any host default (such as a Telegram bot's threshold), then the persona default, then the model's context window minus 16,384 tokens. It is capped at that model-based limit.
115
115
 
116
116
  `bootstrap.model.contextWindow` is the full model window, regardless of the compaction threshold.
117
117
 
@@ -10,8 +10,12 @@ A session also gets creation attributes, which never change. They are short stri
10
10
 
11
11
  - `source`: the client that created the session, such as `tui` or `sdk`.
12
12
  - `repository`: a normalized `host/owner/repository` value. Composite workspaces use a comma-separated list.
13
+ - `machine`: the hostname of the execution environment, which may differ from the machine that runs the client or host.
14
+ - `cwd`: the session's working directory in that execution environment.
13
15
 
14
- A local TUI can fill in `repository` before creating the session, because it manages the local execution environment directly. A remote host never inspects a path to work out attributes. The remote TUI sends `source: "tui"`. SDK and protocol clients should send complete attributes themselves.
16
+ Tau sets `machine` and `cwd` on every new session and replaces any values the client sends for those two keys. Recovering a saved session keeps its attributes unchanged, so older saved sessions may lack these two attributes.
17
+
18
+ Clients set all other attributes. A local TUI can fill in `repository` before creating the session, because it manages the local execution environment directly. A remote host never inspects a path to work out `repository`. The remote TUI sends `source: "tui"`. SDK and protocol clients should send the other attributes they need themselves.
15
19
 
16
20
  A session's execution environment and working directory are fixed when it is created. `/new` creates another session in the same environment, with the current persona and reasoning settings. The existing session is kept, unchanged.
17
21
 
@@ -90,16 +94,23 @@ The summary aims both to let work continue right away and to keep earlier work r
90
94
 
91
95
  ### Automatic compaction
92
96
 
93
- Automatic compaction runs before a model call when the provider's latest token count, plus an estimate for content added since, exceeds the threshold. By default, the threshold is the model's context window minus a fixed reserve of 16,384 tokens. Writing the checkpoint uses the normal model settings, with no special output limit.
97
+ Automatic compaction runs before a model call when the provider's latest token count, plus an estimate for content added since, exceeds the threshold. The threshold comes from the first of these that is set:
98
+
99
+ 1. the session override;
100
+ 2. in Telegram sessions, the bot's `autoCompactThresholdTokens` (see [Telegram](telegram.md#compaction-thresholds));
101
+ 3. the persona's `autoCompactThresholdTokens`;
102
+ 4. the model's context window minus a fixed reserve of 16,384 tokens.
103
+
104
+ Every threshold is capped at that model-based limit. Writing the checkpoint uses the normal model settings, with no special output limit.
94
105
 
95
106
  To compact earlier, set a token threshold for the session. The effective threshold is the smaller of this setting and the model's context window minus 16,384 tokens.
96
107
 
97
108
  - In the TUI, use `/auto-compact 50000` or `/auto-compact 50k`.
98
109
  - In Telegram, use `/auto_compact 50000` or `/auto_compact 50k`.
99
- - With either command, use `default` to restore the model-based threshold, or omit the argument to show the configured and effective thresholds.
110
+ - With either command, use `default` to remove the session override and use the configured default again. Omit the argument to show the configured and effective thresholds.
100
111
  - SDK and protocol inputs accept a safe integer of at least 50,000, or `null` for the default. They do not accept strings such as `50k`.
101
112
 
102
- The minimum configured threshold is 50,000 tokens. The setting applies to the main conversation and survives recovery, reloads, and persona changes. Subagents and ephemeral threads use their model-based defaults.
113
+ The minimum configured threshold is 50,000 tokens. The setting applies to the main conversation. It survives recovery, reloads, and reasoning changes. Applying a persona clears it, even when reapplying the same persona, unless the request supplies a new threshold. Native Tau subagents launched without a model override inherit the parent launching turn's effective threshold, including its session override. Native launches with an explicit model use the persona's model-keyed subagent threshold or that model's default (see [subagents](subagents.md)). Claude Code subagents manage their own compaction. Ephemeral threads use the persona default, without the main session override; forks retain their source thread's threshold.
103
114
 
104
115
  Changing the setting does not start compaction. The new threshold applies from the next logical turn. A running turn and its steering continuations keep their original threshold.
105
116
 
@@ -51,6 +51,16 @@ The configured list of launch models is an allowlist. It does not choose a defau
51
51
 
52
52
  A model ID may be missing from the catalog if its provider is known (see [models](models.md)). An entry with an invalid provider, model, effort, or format makes the configuration field invalid. Duplicate entries are removed.
53
53
 
54
+ ### Automatic compaction
55
+
56
+ These rules apply to native Tau subagents. [Claude Code subagents](#claude-code-subagents) manage their own compaction.
57
+
58
+ When `model` is omitted, a subagent inherits the effective threshold of the parent's launching turn, including any session `/auto-compact` override. A setting change during that turn affects only later turns and their launches.
59
+
60
+ When `model` is specified, the threshold comes from the persona's `subagentAutoCompactThresholdTokens` entry for that `<provider>/<model>`, without reasoning effort. The provider matches in any case, and the model ID must match exactly. If there is no entry, it uses the selected model's context window minus 16,384 tokens. This applies even when the explicit model matches the parent or only changes reasoning effort. The parent's threshold is not inherited in this case.
61
+
62
+ All thresholds are capped at the selected model's context window minus 16,384 tokens. Each child measures its own usage and keeps its launch threshold for follow-ups. These optional settings do not change the launch allowlist. See [personas](personas.md) for a configuration example.
63
+
54
64
  ### Allowing model overrides
55
65
 
56
66
  Configure the allowlist with `subagents.launchModels` in `config.json`:
@@ -12,7 +12,7 @@ platform_api
12
12
  release2026
13
13
  ```
14
14
 
15
- Every project can have an optional non-empty `description`. Repository and persistent-directory projects can select `persona` as `<id>` or `<id>:<reasoning>`. Composite projects require their own persona. Sessions load applicable `AGENTS.md` instructions.
15
+ Every project can have an optional non-empty `description`. Projects do not select a persona. The bot does that through [startup and presets](telegram.md#startup-and-presets). Sessions load applicable `AGENTS.md` instructions.
16
16
 
17
17
  Each project must define exactly one workspace source: `repo`, `directory`, or `projectIds`.
18
18
 
@@ -26,8 +26,7 @@ A repository project clones one GitHub repository into a session-specific manage
26
26
  "ledger": {
27
27
  "repo": "acme/ledger",
28
28
  "ref": "main",
29
- "workingDirectory": "packages/api",
30
- "persona": "gpt-6.1-sol-coder"
29
+ "workingDirectory": "packages/api"
31
30
  }
32
31
  }
33
32
  }
@@ -65,8 +64,7 @@ A persistent-directory project reuses one existing directory instead of creating
65
64
  {
66
65
  "projects": {
67
66
  "notes": {
68
- "directory": "/srv/tau/notes",
69
- "persona": "gpt-6.1-sol-coder"
67
+ "directory": "/srv/tau/notes"
70
68
  }
71
69
  }
72
70
  }
@@ -91,7 +89,6 @@ A composite project creates a root containing multiple repositories:
91
89
  "api": { "repo": "acme/api", "workingDirectory": "services/http" },
92
90
  "platform": {
93
91
  "projectIds": ["web", "api"],
94
- "persona": "gpt-6.1-sol-coder:high",
95
92
  "instructions": "Keep shared contracts synchronized.",
96
93
  "subagents": {
97
94
  "launchModels": ["openai/gpt-6.1-sol:high"]
@@ -107,7 +104,7 @@ Members are placed at `<composite-root>/<member-project-id>` and use each reposi
107
104
 
108
105
  `subagents.launchModels` sets which models subagents may be started with. See [subagents](subagents.md) for the syntax and inheritance.
109
106
 
110
- The composite's settings apply to the whole session: persona, subagents, model catalog, configuration, and tools. Members' `.tau/config.json` files are not merged. A subagent started in a member directory rebuilds only the context that depends on the directory: environment and repository details, the applicable `AGENTS.md`, and the skills found there.
107
+ Configuration resolved at the composite root applies to the whole session, including the workspace's `defaultPersona`, subagents, model catalog, and tools. Members' `.tau/config.json` files are not merged. A subagent started in a member directory rebuilds only the context that depends on the directory: environment and repository details, the applicable `AGENTS.md`, and the skills found there.
111
108
 
112
109
  If any member cannot be prepared, Tau removes the whole composite workspace. Composite workspaces and member caches use the top-level `workspaceRoot`.
113
110
 
@@ -14,7 +14,7 @@ tau telegram --config-file /etc/tau/telegram.json
14
14
 
15
15
  The process stays in the foreground until `SIGINT` or `SIGTERM`. Relative `--config-file` paths resolve from the process working directory. Paths inside that file resolve as described below, usually from the config file's directory.
16
16
 
17
- The Telegram file is **not** one of Tau's `config.json` levels. It configures the runner: bot tokens, projects, workspace roots, and routing. Tau still loads normal configuration from the runner's startup directory and from each session's workspace. See [configuration](configuration.md) and [ownership and scope](ownership-and-scope.md).
17
+ The Telegram file is **not** one of Tau's `config.json` levels. It configures the runner: bot tokens, startup settings, presets, projects, workspace roots, and routing. Tau still loads normal configuration from the runner's startup directory and from each session's workspace. See [configuration](configuration.md) and [ownership and scope](ownership-and-scope.md).
18
18
 
19
19
  Run at most one runner per configuration and workspace root. Two runners would compete for Telegram updates, runner state, and workspace cleanup.
20
20
 
@@ -28,6 +28,7 @@ A useful configuration defines at least one bot and one project:
28
28
  "bots": {
29
29
  "engineering": {
30
30
  "botToken": "<telegram-bot-token>",
31
+ "startup": { "source": "workspace" },
31
32
  "allowedProjectIds": ["ledger"],
32
33
  "allowedUserIds": [18422031],
33
34
  "allowedChatIds": [18422031],
@@ -37,8 +38,7 @@ A useful configuration defines at least one bot and one project:
37
38
  "projects": {
38
39
  "ledger": {
39
40
  "repo": "acme/ledger",
40
- "ref": "main",
41
- "persona": "gpt-6.1-sol-coder:high"
41
+ "ref": "main"
42
42
  }
43
43
  }
44
44
  }
@@ -46,7 +46,7 @@ A useful configuration defines at least one bot and one project:
46
46
 
47
47
  The bot token must be written in the file; it cannot come from an environment variable. Protect the file, never commit it, and rotate an exposed token through BotFather. Never print the config into a session or shared log.
48
48
 
49
- Unknown fields are removed, so a misspelled field silently has no effect. The exception is a `personaMenu` entry, where an unknown field makes the config invalid.
49
+ The runner refuses to start if the file contains an unknown field, and the error names that field.
50
50
 
51
51
  ## Top-level fields
52
52
 
@@ -69,7 +69,9 @@ Each `bots.<id>` object supports:
69
69
  | --- | --- | --- |
70
70
  | `botToken` | Yes | Telegram Bot API token. |
71
71
  | `allowedProjectIds` | No | Non-empty unique subset of configured project IDs; omission exposes all projects. |
72
- | `personaMenu` | No | Ordered list of personas offered by `/persona`; omission offers every persona. See [persona menu](#persona-menu). |
72
+ | `autoCompactThresholdTokens` | No | Default automatic-compaction threshold for this bot's sessions. See [compaction thresholds](#compaction-thresholds). |
73
+ | `startup` | Yes | `{ "source": "workspace" }` or `{ "source": "preset", "preset": "<key>" }`. See [startup and presets](#startup-and-presets). |
74
+ | `presets` | No | Named choices that `/persona` offers. Omission offers all workspace personas, and `{}` offers none. |
73
75
  | `allowedUserIds` | No | Integer user IDs allowed to trigger turns, commands, and callbacks. |
74
76
  | `allowedChatIds` | No | Integer chat IDs allowed to interact with the bot; also opts groups in. |
75
77
  | `defaultProjectId` | No | Initial project preference, and must be allowed for this bot. |
@@ -83,7 +85,7 @@ Use both lists for a private bot. `allowedUserIds` decides who can start work, b
83
85
 
84
86
  A bot sees only its `allowedProjectIds`, or every project if the field is missing. If only one project is allowed, it is selected automatically and the command palette omits `/use_<project>` commands. The command still works when typed directly. Otherwise the palette includes project-switch commands, and a chat needs `defaultProjectId` or a `/use_<project>` choice before `/new`. Each startup replaces the palette, removing switch commands if the bot now has only one available project.
85
87
 
86
- Telegram allows 100 commands per bot: twelve built-ins and up to 88 `/use_<projectId>` commands.
88
+ Telegram allows 100 commands per bot: thirteen built-ins and up to 87 `/use_<projectId>` commands. With `presets: {}`, `/persona` is hidden, which leaves room for one more project command.
87
89
 
88
90
  ## Projects
89
91
 
@@ -101,10 +103,18 @@ Project IDs become `/use_<projectId>` commands, so they may contain only lowerca
101
103
 
102
104
  After preparation, Tau creates an ordinary local session in the workspace. It reads `~/.config/tau` and the `.tau` directories above the workspace as usual, so personas, prompts, skills, project context, host tools, and other settings work as in the TUI.
103
105
 
104
- A project's `persona` replaces the default for its sessions. Composite sessions use the generated root files and their required persona.
106
+ The bot's `startup` selects a new session's settings, as described in [startup and presets](#startup-and-presets).
105
107
 
106
108
  Every Telegram turn starts with a hidden `<system>` block saying the message comes from Telegram and the reply goes there. It asks for direct answers to simple questions, and for short acknowledgements or useful progress updates during longer work, because users cannot see tool activity.
107
109
 
110
+ Each message also carries its sender, as reported by Telegram. A DM turn gets a hidden block with the sender. In a group, the trigger message and each context message are labeled with their sender. Each queued attachment keeps the sender of the message that carried it. The sender is a JSON object with the numeric Telegram user ID and, when available, the display name and the username without the `@`, or `null` when Telegram does not identify one:
111
+
112
+ ```json
113
+ { "user-id": 123456789, "name": "Ada Lovelace", "username": "ada" }
114
+ ```
115
+
116
+ The agent is told that names and usernames are chosen by the sender and do not prove identity or grant permission. Only `allowedChatIds` and `allowedUserIds` control who can use the bot.
117
+
108
118
  A per-bot `systemMessage` goes in a second hidden block. Audio transcripts get another block warning that they may contain noise or errors. Hidden blocks are saved with the user's message and can appear in history, so never put credentials in configured messages.
109
119
 
110
120
  The runner loads its speech credentials and `speech.voiceId` at startup, from its environment and from the Tau config for its startup directory. Restart the runner after changing them.
@@ -116,21 +126,21 @@ The runner loads its speech credentials and `speech.voiceId` at startup, from it
116
126
  | `/use_<projectId>` | Selects the project for future `/new` sessions. |
117
127
  | `/new` | Replaces the active session using the preferred project. |
118
128
  | `/status` | Reports session state, project, model, reasoning, context usage, and cost. |
119
- | `/persona` | Switches persona while idle without losing history or starting a turn. |
129
+ | `/persona` | Applies a persona or preset from a menu while idle, without losing history or starting a turn. |
120
130
  | `/effort_low` | Selects low reasoning for future turns. |
121
131
  | `/effort_medium` | Selects medium reasoning for future turns. |
122
132
  | `/effort_high` | Selects high reasoning for future turns. |
123
133
  | `/effort_xhigh` | Selects xhigh reasoning for future turns. |
124
134
  | `/prompt` | [Records a saved prompt](prompts-and-project-context.md#telegram-prompt-picker) while idle, without starting a turn. |
125
- | `/auto_compact [tokens\|default]` | Shows the automatic-compaction threshold. Pass `50k` or `50000` to set it (minimum 50,000 tokens), or `default` to restore the model-based threshold. |
135
+ | `/auto_compact [tokens\|default]` | Shows the automatic-compaction threshold. Pass `50k` or `50000` to set it (minimum 50,000 tokens), or `default` to remove the session override and use the default. |
126
136
  | `/compact` | Runs summary-only compaction while idle. |
127
137
  | `/interrupt` | Interrupts the active Tau turn. |
128
138
  | `/tts_on` | Sends a voice note after each final assistant response. |
129
139
  | `/tts_off` | Disables voice responses. |
130
140
 
131
- Changes to the auto-compaction threshold apply from the next turn and survive session recovery. `/status` shows context usage as a percentage of the full model window. When an override is set, it also shows the effective threshold in parentheses. See [automatic compaction](sessions.md#automatic-compaction) for limits and costs.
141
+ Changes to the auto-compaction threshold apply from the next turn and survive session recovery. `/status` shows context usage as a percentage of the full model window. When a session override is set, or the default threshold is below the model-based threshold, it also shows the effective threshold in parentheses. See [compaction thresholds](#compaction-thresholds) for bot defaults and [automatic compaction](sessions.md#automatic-compaction) for limits and costs.
132
142
 
133
- A [persona](personas.md) choice applies to the current session only; `/new` uses the project's default.
143
+ A [persona](personas.md) or preset choice applies to the current session only. `/new` starts from the bot's `startup`.
134
144
 
135
145
  Preferences are saved per bot and chat, and survive restarts and new sessions. A project change applies at the next `/new`, not to the current session.
136
146
 
@@ -144,22 +154,58 @@ In groups, commands must explicitly mention the bot. Accepted forms include:
144
154
 
145
155
  Commands addressed to other bots are ignored.
146
156
 
147
- ### Persona menu
157
+ ## Startup and presets
148
158
 
149
- By default, `/persona` offers every persona in the session. A bot's `personaMenu` replaces that list with the personas you name, in the order you list them, and can give each one a shorter label:
159
+ `startup` decides how a bot's new sessions start, and `presets` decides what `/persona` offers. `startup` does not add a menu entry.
160
+
161
+ `startup` is required. `{ "source": "workspace" }` uses the workspace's `defaultPersona`, found through the same configuration layers as the TUI, including any effort suffix. To start with a named preset instead:
150
162
 
151
163
  ```json
152
- "personaMenu": [
153
- { "id": "gpt-6.1-sol-coder", "label": "sol" },
154
- { "id": "sonnet-5.5-coder" }
155
- ]
164
+ "startup": { "source": "preset", "preset": "astra" },
165
+ "presets": {
166
+ "workspace": { "source": "workspace", "label": "Workspace default" },
167
+ "astra": {
168
+ "source": "persona",
169
+ "label": "Astra",
170
+ "persona": "gpt-6-astra-chat",
171
+ "reasoning": "low",
172
+ "autoCompactThresholdTokens": 120000
173
+ }
174
+ }
156
175
  ```
157
176
 
158
- Each entry has a required `id`, the exact persona ID, and an optional `label`. Both must be non-empty after surrounding whitespace is trimmed, IDs must be unique, and no other fields are allowed. Without a label, the picker shows the persona's own label, or its ID if it has none. The label appears in the picker and in the switch confirmation. It changes nothing else, so the persona keeps its ID, prompt, model, tools, and reasoning settings, and the TUI still shows its normal name.
177
+ `startup.preset` must name a key in `presets`. There are two kinds of preset:
178
+
179
+ - A `workspace` preset resolves the workspace's `defaultPersona` each time it is applied.
180
+ - A `persona` preset names a persona ID without an effort suffix. Its optional `reasoning` is one of `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max`, and defaults to the persona's own setting. Its optional `autoCompactThresholdTokens` is [described below](#compaction-thresholds).
181
+
182
+ Every preset needs a non-empty `label`. Keys must be non-empty, without surrounding whitespace.
183
+
184
+ The `/persona` menu depends on `presets`:
185
+
186
+ - Omitted: all personas available in the session's workspace, with their own labels and default settings.
187
+ - `{}`: nothing, and `/persona` is hidden from the command palette. Typing it replies that no presets are configured.
188
+ - With entries: exactly those entries, in the object's order. Integer-like keys sort first, so use non-numeric keys to keep the order you wrote.
189
+
190
+ Buttons show only labels, because a session does not record which preset was applied. `/status` reports the session's actual settings.
191
+
192
+ Applying a preset changes the prompt, model, tools, reasoning, and other persona settings together, and keeps the conversation. It applies them once, so effort commands then change only reasoning, and applying the preset again restores its settings.
193
+
194
+ An unavailable persona stays in the menu. Applying it, or a `reasoning` value the persona does not support, reports an error and changes no settings. The value must be in the persona's `allowedReasoningLevels` when that list is non-empty, and a model without reasoning accepts only `none`. A failing preset at `startup` fails the new session.
195
+
196
+ A workspace selection works as in the TUI: an unavailable `defaultPersona` falls back to the first available persona, and its effort suffix skips these checks.
197
+
198
+ Restart the runner after editing `startup` or `presets`. Recovery keeps a session's saved settings and does not reapply them. Reload keeps the session's settings if its persona still exists.
199
+
200
+ ## Compaction thresholds
201
+
202
+ A bot's optional `autoCompactThresholdTokens` sets a default for its sessions. A persona preset's optional field of the same name sets a saved session override when the preset is applied, including at startup. Both take a safe integer of at least 50,000 tokens.
203
+
204
+ A session uses its saved override first, then the bot default, the persona default, and finally the model's context window minus 16,384 tokens. The effective threshold is capped at that model-based limit. `/auto_compact` changes the saved override. Applying a persona or preset clears the previous override unless the preset sets a threshold, even when the persona stays the same.
159
205
 
160
- The menu is matched against each session's personas when `/persona` runs, because different projects can define different personas. Listed personas that the session lacks are named in the picker message, and the rest of the menu still works. An empty list, `[]`, offers no personas.
206
+ The runner reads the bot default at startup and does not save it with the session. Recovered sessions use the current bot default and keep their saved override.
161
207
 
162
- The menu only changes what the picker offers. It does not restrict which personas a session may use, and it does not change defaults. A project's `persona` still applies to new sessions even if the menu omits it. When the session's current persona is not in the menu, the picker message names it, but it is not added as a choice.
208
+ Native subagents launched without a model use the session's effective threshold, as described in [subagents](subagents.md#automatic-compaction). Ephemeral threads use the persona's own default and ignore both the bot default and the session override.
163
209
 
164
210
  ## DMs, groups, and active work
165
211
 
@@ -220,7 +266,7 @@ Do not verify bot tokens, provider keys, Access secrets, transcript contents, or
220
266
 
221
267
  ## Troubleshooting
222
268
 
223
- **The runner rejects its config.** Startup errors name every invalid bot or project field. Check the required `bots` and `projects` objects, exact project IDs, positive numbers, allowed project IDs, persona suffixes, `personaMenu` entries, and that each project defines exactly one of `repo`, `directory`, or `projectIds`. Check the JSON syntax without printing the file, which contains bot tokens, and restart the runner after fixing it. Relative paths in the file resolve from its directory.
269
+ **The runner rejects its config.** Startup errors name every invalid bot or project field. Check the required `bots` and `projects` objects, exact project IDs, positive numbers, allowed project IDs, startup sources, preset references, and that each project defines exactly one of `repo`, `directory`, or `projectIds`. Check the JSON syntax without printing the file, which contains bot tokens, and restart the runner after fixing it. Relative paths in the file resolve from its directory.
224
270
 
225
271
  **The bot ignores a DM.** If `allowedChatIds` is set, the DM's chat ID must be listed. If `allowedUserIds` is set, the sender's user ID must be listed too.
226
272
 
@@ -200,6 +200,8 @@ If attachment reports an unsupported protocol version or an invalid message, upg
200
200
 
201
201
  For `tau attach --new`, `--cwd` must be an absolute path inside the chosen execution environment. With the default local kind, it is a path on the host, not on the attaching machine. Tau does not create the directory, clone a repository, or detect repository attributes.
202
202
 
203
+ Creating a session also fails if Tau cannot read the hostname from the execution environment, for example because `node` is missing there or the target does not respond in time. The error gives the reason and any output from the failed command. Over the session protocol, these details are in the error's `data.cause`.
204
+
203
205
  For a Fly Sprite session, check on the host that:
204
206
 
205
207
  - `flySprites` is set in the host's startup configuration;