@markusylisiurunen/tau 0.3.82 → 0.3.84

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 (183) hide show
  1. package/README.md +1 -3
  2. package/dist/core/agent/agent_runtime.js +10 -6
  3. package/dist/core/agent/agent_runtime.js.map +1 -1
  4. package/dist/core/cli.js +2 -2
  5. package/dist/core/cli.js.map +1 -1
  6. package/dist/core/code_mode/models.js +61 -22
  7. package/dist/core/code_mode/models.js.map +1 -1
  8. package/dist/core/commands/auto_compact.js +16 -0
  9. package/dist/core/commands/auto_compact.js.map +1 -0
  10. package/dist/core/commands/registry.js +13 -0
  11. package/dist/core/commands/registry.js.map +1 -1
  12. package/dist/core/history/cli.js +48 -0
  13. package/dist/core/history/cli.js.map +1 -1
  14. package/dist/core/history/history_manager.js +9 -3
  15. package/dist/core/history/history_manager.js.map +1 -1
  16. package/dist/core/history/local_history_store.js +9 -2
  17. package/dist/core/history/local_history_store.js.map +1 -1
  18. package/dist/core/history/remote_history_client.js +18 -3
  19. package/dist/core/history/remote_history_client.js.map +1 -1
  20. package/dist/core/models/openrouter.js +17 -5
  21. package/dist/core/models/openrouter.js.map +1 -1
  22. package/dist/core/models/openrouter_decisions.js +16 -1
  23. package/dist/core/models/openrouter_decisions.js.map +1 -1
  24. package/dist/core/runtime/chat_runtime.js +5 -0
  25. package/dist/core/runtime/chat_runtime.js.map +1 -1
  26. package/dist/core/session/auto_compaction_threshold.js +4 -0
  27. package/dist/core/session/auto_compaction_threshold.js.map +1 -0
  28. package/dist/core/static/tau_docs/client-tools.md +53 -53
  29. package/dist/core/static/tau_docs/config-reference.md +46 -43
  30. package/dist/core/static/tau_docs/configuration.md +40 -36
  31. package/dist/core/static/tau_docs/credentials.md +32 -31
  32. package/dist/core/static/tau_docs/getting-started.md +28 -28
  33. package/dist/core/static/tau_docs/history.md +114 -79
  34. package/dist/core/static/tau_docs/image-generation.md +20 -18
  35. package/dist/core/static/tau_docs/index.md +30 -31
  36. package/dist/core/static/tau_docs/manifest.json +1 -0
  37. package/dist/core/static/tau_docs/models.md +17 -17
  38. package/dist/core/static/tau_docs/node-sdk.md +74 -69
  39. package/dist/core/static/tau_docs/nook.md +75 -69
  40. package/dist/core/static/tau_docs/openrouter.md +28 -13
  41. package/dist/core/static/tau_docs/ownership-and-scope.md +21 -23
  42. package/dist/core/static/tau_docs/pdf-unpacking.md +6 -6
  43. package/dist/core/static/tau_docs/personas.md +29 -27
  44. package/dist/core/static/tau_docs/prompts-and-project-context.md +41 -39
  45. package/dist/core/static/tau_docs/remote-sessions.md +38 -38
  46. package/dist/core/static/tau_docs/sdk-diff-review.md +16 -12
  47. package/dist/core/static/tau_docs/security.md +72 -72
  48. package/dist/core/static/tau_docs/session-protocol-methods.md +69 -45
  49. package/dist/core/static/tau_docs/session-protocol.md +59 -52
  50. package/dist/core/static/tau_docs/sessions.md +112 -88
  51. package/dist/core/static/tau_docs/skills.md +25 -20
  52. package/dist/core/static/tau_docs/speech-generation.md +5 -5
  53. package/dist/core/static/tau_docs/subagents.md +42 -44
  54. package/dist/core/static/tau_docs/telegram-projects.md +142 -0
  55. package/dist/core/static/tau_docs/telegram.md +67 -181
  56. package/dist/core/static/tau_docs/tools.md +72 -80
  57. package/dist/core/static/tau_docs/troubleshooting.md +94 -149
  58. package/dist/core/static/tau_docs/tui.md +127 -80
  59. package/dist/core/subagents/agent_supervisor.js +1 -0
  60. package/dist/core/subagents/agent_supervisor.js.map +1 -1
  61. package/dist/core/telegram/adapter.js +31 -1
  62. package/dist/core/telegram/adapter.js.map +1 -1
  63. package/dist/core/telegram/session_manager.js +11 -0
  64. package/dist/core/telegram/session_manager.js.map +1 -1
  65. package/dist/core/tool/image_generate.js +5 -17
  66. package/dist/core/tool/image_generate.js.map +1 -1
  67. package/dist/core/tool/openrouter.js +21 -2
  68. package/dist/core/tool/openrouter.js.map +1 -1
  69. package/dist/core/utils/gemini_transcription.js +43 -13
  70. package/dist/core/utils/gemini_transcription.js.map +1 -1
  71. package/dist/core/utils/model_stream.js +17 -1
  72. package/dist/core/utils/model_stream.js.map +1 -1
  73. package/dist/core/version.js +2 -2
  74. package/dist/diff_tool/app/dist/assets/{angular-html-D20byCHL.js → angular-html-Dr923UVo.js} +1 -1
  75. package/dist/diff_tool/app/dist/assets/{angular-ts-CvVQslm2.js → angular-ts-DOChuIbB.js} +1 -1
  76. package/dist/diff_tool/app/dist/assets/{apl-DNIxlYWe.js → apl-Bs_bcAcm.js} +1 -1
  77. package/dist/diff_tool/app/dist/assets/{astro-DDOw4EUG.js → astro-a4yvqyGV.js} +1 -1
  78. package/dist/diff_tool/app/dist/assets/{blade-DlhB-SLW.js → blade-BD5OxwVc.js} +1 -1
  79. package/dist/diff_tool/app/dist/assets/{c-C1f4vhDm.js → c-CPWQkPLc.js} +1 -1
  80. package/dist/diff_tool/app/dist/assets/{chapel-Ctt8xOX1.js → chapel-DaQatW92.js} +1 -1
  81. package/dist/diff_tool/app/dist/assets/{cobol-BXw71PAA.js → cobol-Dv7H5p9m.js} +1 -1
  82. package/dist/diff_tool/app/dist/assets/{coffee-CRh_5Nmr.js → coffee-BUGvGi57.js} +1 -1
  83. package/dist/diff_tool/app/dist/assets/{cpp-IlI-zWOZ.js → cpp-fo_wPsB8.js} +1 -1
  84. package/dist/diff_tool/app/dist/assets/{crystal-CXeL28i7.js → crystal--pI-R_dD.js} +1 -1
  85. package/dist/diff_tool/app/dist/assets/{css-BfST9hFI.js → css-BssyxPkY.js} +1 -1
  86. package/dist/diff_tool/app/dist/assets/{edge--Q22k_ty.js → edge-CZ8t3-Ec.js} +1 -1
  87. package/dist/diff_tool/app/dist/assets/{elixir-CIfC_rQx.js → elixir-UZt0J7RJ.js} +1 -1
  88. package/dist/diff_tool/app/dist/assets/{elm-OkZ29XRO.js → elm-DIbchse0.js} +1 -1
  89. package/dist/diff_tool/app/dist/assets/{erb-B8ZXgEqv.js → erb-BOBhlFtI.js} +1 -1
  90. package/dist/diff_tool/app/dist/assets/{git-rebase-CxkO_WGd.js → git-rebase-BAOt70YV.js} +1 -1
  91. package/dist/diff_tool/app/dist/assets/{glimmer-js-BVadyIrr.js → glimmer-js-CEvU-NQC.js} +1 -1
  92. package/dist/diff_tool/app/dist/assets/{glimmer-ts-D5J3Qb7n.js → glimmer-ts-BTMr7YQb.js} +1 -1
  93. package/dist/diff_tool/app/dist/assets/{glsl-B6iy73-Y.js → glsl-BdwQytbZ.js} +1 -1
  94. package/dist/diff_tool/app/dist/assets/{graphql-Dc3rUXf5.js → graphql-MVXf_Gip.js} +1 -1
  95. package/dist/diff_tool/app/dist/assets/{hack-q8Utf8Jp.js → hack-BKsyThYd.js} +1 -1
  96. package/dist/diff_tool/app/dist/assets/{haml-mjMIlj_2.js → haml-aYjo4kTQ.js} +1 -1
  97. package/dist/diff_tool/app/dist/assets/{handlebars-CA-BL8Ks.js → handlebars-CkBj5nNj.js} +1 -1
  98. package/dist/diff_tool/app/dist/assets/{html-BmPxhYCX.js → html-V_XLZjq3.js} +1 -1
  99. package/dist/diff_tool/app/dist/assets/{html-derivative-CvknIjuB.js → html-derivative-CLtkZVT2.js} +1 -1
  100. package/dist/diff_tool/app/dist/assets/{http-DNxLDRIi.js → http-nUN1ldGy.js} +1 -1
  101. package/dist/diff_tool/app/dist/assets/{hurl-kbd3Eq2V.js → hurl-BBwLfekr.js} +1 -1
  102. package/dist/diff_tool/app/dist/assets/index-Dt2v7POE.js +341 -0
  103. package/dist/diff_tool/app/dist/assets/{java-BuZshgf1.js → java-DLkxtNEL.js} +1 -1
  104. package/dist/diff_tool/app/dist/assets/{javascript-DfL2oeFU.js → javascript-CVr4Rx0e.js} +1 -1
  105. package/dist/diff_tool/app/dist/assets/{jinja-Bu0KQihU.js → jinja-BOSJTyD0.js} +1 -1
  106. package/dist/diff_tool/app/dist/assets/{jison-Bqn7Mf3Q.js → jison-Di3-hBGP.js} +1 -1
  107. package/dist/diff_tool/app/dist/assets/{json-BuX5LKaB.js → json-D9C8xfZt.js} +1 -1
  108. package/dist/diff_tool/app/dist/assets/{jsx-DdVJ-ZBp.js → jsx-CUDhGSfU.js} +1 -1
  109. package/dist/diff_tool/app/dist/assets/{julia-CQeijOoQ.js → julia-CRy8R7YY.js} +1 -1
  110. package/dist/diff_tool/app/dist/assets/{just-vzAktFGR.js → just-tUqjHI43.js} +1 -1
  111. package/dist/diff_tool/app/dist/assets/{latex-Dm60cg0k.js → latex-8_LCZmZN.js} +1 -1
  112. package/dist/diff_tool/app/dist/assets/{liquid-vxKzm2P8.js → liquid-CvGorueR.js} +1 -1
  113. package/dist/diff_tool/app/dist/assets/{lua-DIoqi4dd.js → lua-DR_7pVFC.js} +1 -1
  114. package/dist/diff_tool/app/dist/assets/{marko-BTHgFKby.js → marko-sTN8ZOd5.js} +1 -1
  115. package/dist/diff_tool/app/dist/assets/{mdc-CEfz8gZ9.js → mdc-xRsS5Ubl.js} +1 -1
  116. package/dist/diff_tool/app/dist/assets/{nginx-DNlFDEfJ.js → nginx-ypaVSshx.js} +1 -1
  117. package/dist/diff_tool/app/dist/assets/{nim-DnvT_0lZ.js → nim-DTc1FpAl.js} +1 -1
  118. package/dist/diff_tool/app/dist/assets/{org-BbD-ElVf.js → org-B7I2tEAw.js} +1 -1
  119. package/dist/diff_tool/app/dist/assets/{perl-DTsbnAmK.js → perl-CTNhHvKx.js} +1 -1
  120. package/dist/diff_tool/app/dist/assets/{php-CwEsJe-W.js → php-LzxyOsOP.js} +1 -1
  121. package/dist/diff_tool/app/dist/assets/{pug-BOttMVH-.js → pug-D5FIy7qc.js} +1 -1
  122. package/dist/diff_tool/app/dist/assets/{qml-C8KkSItN.js → qml-CfdV5Js7.js} +1 -1
  123. package/dist/diff_tool/app/dist/assets/{r-K4ksJj_R.js → r-CZ-EMZDs.js} +1 -1
  124. package/dist/diff_tool/app/dist/assets/{razor-yuWRrrG6.js → razor-BhyBjPmN.js} +1 -1
  125. package/dist/diff_tool/app/dist/assets/{regexp-CV-jhnUz.js → regexp-iPvMX04S.js} +1 -1
  126. package/dist/diff_tool/app/dist/assets/{rst-CLA3Attc.js → rst-Dpp9DE6Q.js} +1 -1
  127. package/dist/diff_tool/app/dist/assets/{ruby-DiHR2-DX.js → ruby-CVyShlOK.js} +1 -1
  128. package/dist/diff_tool/app/dist/assets/{sas-DxIjbYJ5.js → sas-DFmollAV.js} +1 -1
  129. package/dist/diff_tool/app/dist/assets/{scss-CShwEaV4.js → scss-Di6lsIQE.js} +1 -1
  130. package/dist/diff_tool/app/dist/assets/{shellscript-CeezOnrF.js → shellscript-CRHervLi.js} +1 -1
  131. package/dist/diff_tool/app/dist/assets/{shellsession-CjSS2r7j.js → shellsession-CHP7Q5Ym.js} +1 -1
  132. package/dist/diff_tool/app/dist/assets/{soy-CI_v23nJ.js → soy-CNXyGE_T.js} +1 -1
  133. package/dist/diff_tool/app/dist/assets/{sql-D9GaB00A.js → sql-DnPFF_uz.js} +1 -1
  134. package/dist/diff_tool/app/dist/assets/{stata-hK92TPE_.js → stata-CobN2W6n.js} +1 -1
  135. package/dist/diff_tool/app/dist/assets/{surrealql-UO8LtPJG.js → surrealql-DqvGXHs7.js} +1 -1
  136. package/dist/diff_tool/app/dist/assets/{svelte-kIlaw_oh.js → svelte-ChhfFBzQ.js} +1 -1
  137. package/dist/diff_tool/app/dist/assets/{templ-DFRz1JP6.js → templ-DKi2U_4-.js} +1 -1
  138. package/dist/diff_tool/app/dist/assets/{tex-CfxIXk_z.js → tex-Cjl-M5D5.js} +1 -1
  139. package/dist/diff_tool/app/dist/assets/{ts-tags-BtUHl0ff.js → ts-tags-CV2veQhe.js} +1 -1
  140. package/dist/diff_tool/app/dist/assets/{tsx-Cd4SAe2C.js → tsx-Dw0DYsvT.js} +1 -1
  141. package/dist/diff_tool/app/dist/assets/{twig-CWk4q5Y3.js → twig-r46-w1o_.js} +1 -1
  142. package/dist/diff_tool/app/dist/assets/{typescript-BqxGJiP0.js → typescript-QuYM-098.js} +1 -1
  143. package/dist/diff_tool/app/dist/assets/{typst-D9nAun2p.js → typst-B9jAU5LS.js} +1 -1
  144. package/dist/diff_tool/app/dist/assets/{vue-D6HYhg6_.js → vue-CIrFiYWU.js} +1 -1
  145. package/dist/diff_tool/app/dist/assets/{vue-html-1VRlaENS.js → vue-html-bfczklzO.js} +1 -1
  146. package/dist/diff_tool/app/dist/assets/{vue-vine-BaH0We9y.js → vue-vine-BmBCOwQI.js} +1 -1
  147. package/dist/diff_tool/app/dist/assets/{xml-C4RWnUuj.js → xml-DYPsS0xh.js} +1 -1
  148. package/dist/diff_tool/app/dist/assets/{xsl-D1iD38ga.js → xsl-DZFDwCkt.js} +1 -1
  149. package/dist/diff_tool/app/dist/assets/{yaml-yAb4W0G-.js → yaml-9CQ32JJD.js} +1 -1
  150. package/dist/diff_tool/app/dist/index.html +1 -1
  151. package/dist/history/worker/index.js +87 -83
  152. package/dist/history/worker/index.js.map +1 -1
  153. package/dist/host/hosted_ephemeral_agent_session.js +1 -0
  154. package/dist/host/hosted_ephemeral_agent_session.js.map +1 -1
  155. package/dist/host/local_session_host.js +30 -1
  156. package/dist/host/local_session_host.js.map +1 -1
  157. package/dist/host/session_protocol_handler.js +18 -0
  158. package/dist/host/session_protocol_handler.js.map +1 -1
  159. package/dist/main.js +6 -7
  160. package/dist/main.js.map +1 -1
  161. package/dist/protocol/index.d.ts +1 -1
  162. package/dist/protocol/index.js.map +1 -1
  163. package/dist/protocol/session_protocol.d.ts +13 -2
  164. package/dist/protocol/session_protocol.js +31 -1
  165. package/dist/protocol/session_protocol.js.map +1 -1
  166. package/dist/sdk/index.d.ts +1 -1
  167. package/dist/sdk/index.js.map +1 -1
  168. package/dist/sdk/session.js +11 -0
  169. package/dist/sdk/session.js.map +1 -1
  170. package/dist/sdk/types.d.ts +2 -0
  171. package/dist/store/session_snapshot_migrations.js +19 -1
  172. package/dist/store/session_snapshot_migrations.js.map +1 -1
  173. package/dist/tui/session_chat_controller.js +41 -4
  174. package/dist/tui/session_chat_controller.js.map +1 -1
  175. package/dist/tui/terminal.js +1 -0
  176. package/dist/tui/terminal.js.map +1 -1
  177. package/dist/tui/ui/chat_container.js +34 -17
  178. package/dist/tui/ui/chat_container.js.map +1 -1
  179. package/dist/tui/ui/footer.js +2 -4
  180. package/dist/tui/ui/footer.js.map +1 -1
  181. package/package.json +11 -10
  182. package/dist/diff_tool/app/dist/assets/index-D8ZI_2RC.js +0 -340
  183. package/dist/nook/README.md +0 -123
@@ -1,8 +1,8 @@
1
1
  # Telegram
2
2
 
3
- Tau's Telegram runner turns one or more bots into clients of local, in-process Tau sessions. It owns Telegram polling, chat routing, attachments, project selection, and workspace preparation on the runner machine. Each prepared workspace then becomes an ordinary local execution environment with normal Tau configuration, personas, tools, project context, session snapshots, and history.
3
+ Tau's Telegram runner connects one or more Telegram bots to Tau sessions that run in the same process. On the runner machine, it polls Telegram, routes chats, handles attachments, and prepares project workspaces. Each workspace is an ordinary local execution environment, with normal Tau configuration, personas, tools, project context, saved sessions, and history.
4
4
 
5
- Bot access can lead to model calls, filesystem changes, and repository mutations. Configure chat, user, and project boundaries before inviting a bot into a group.
5
+ Anyone who can use a bot can cause model calls, file changes, and repository changes. Set up chat, user, and project limits before adding a bot to a group.
6
6
 
7
7
  ## Start the runner
8
8
 
@@ -14,9 +14,9 @@ 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** a Tau `config.json` level. It controls runner concerns such as bot tokens, project definitions, workspace roots, and routing. Tau still loads normal configuration from the runner's startup working directory and from each prepared session 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, 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
- Run at most one Telegram runner for a given configuration and workspace root. Concurrent runners are unsupported and can race Telegram updates, persisted runner state, and workspace cleanup.
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
 
21
21
  ## Minimal configuration
22
22
 
@@ -44,24 +44,24 @@ A useful configuration defines at least one bot and one project:
44
44
  }
45
45
  ```
46
46
 
47
- The bot token is a required literal string; Telegram config has no token environment indirection. Protect the file, never commit it, and rotate an exposed token through BotFather. Do not print the config into a session or shared log.
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 object fields are stripped, so a misspelled field can have no effect without making the JSON invalid.
49
+ Unknown fields are removed, so a misspelled field silently has no effect.
50
50
 
51
- ## Top-level contract
51
+ ## Top-level fields
52
52
 
53
53
  | Field | Required | Behavior |
54
54
  | --- | --- | --- |
55
- | `bots` | Yes | Non-empty object keyed by operator-chosen bot ID. |
55
+ | `bots` | Yes | Non-empty object keyed by a bot ID you choose. |
56
56
  | `projects` | Yes | Object keyed by project ID. Bots select from these definitions. |
57
57
  | `workspaceRoot` | No | Base for managed workspaces; defaults to `.tau/telegram-workspaces` beside the Telegram config file. |
58
58
  | `maxSessions` | No | Positive integer cap on active sessions across the whole runner. |
59
59
 
60
- A relative top-level `workspaceRoot` resolves from the Telegram config file's directory. Tau persists runner session records at `<workspaceRoot>-sessions.json` and per-chat preferences at `<workspaceRoot>-project-preferences.json`. These are runner-owned state files, not operator editing surfaces. Session snapshots remain in the normal host store under the runner user's Tau home.
60
+ A relative `workspaceRoot` resolves from the config file's directory. Tau saves runner session records in `<workspaceRoot>-sessions.json` and per-chat preferences in `<workspaceRoot>-project-preferences.json`. These are the runner's own state files; do not edit them. Sessions are saved in the usual place in the runner user's Tau home.
61
61
 
62
- `maxSessions` counts queued, preparing, running, and waiting sessions across all configured bots. Failed records do not consume the active cap, but they remain visible to their owning chat until replaced or closed.
62
+ `maxSessions` counts queued, preparing, running, and waiting sessions across all bots. Failed sessions do not count, but stay visible to their chat until replaced or closed.
63
63
 
64
- ## Bot contract and access control
64
+ ## Bots and access control
65
65
 
66
66
  Each `bots.<id>` object supports:
67
67
 
@@ -76,152 +76,37 @@ Each `bots.<id>` object supports:
76
76
 
77
77
  The runner retries failed polling after 1,000 ms and uses a 30-second Telegram long-poll timeout.
78
78
 
79
- An absent or empty `allowedUserIds` means no user restriction. An absent or empty `allowedChatIds` allows DMs but allows no groups. When `allowedChatIds` is non-empty, it restricts DMs to listed chat IDs and enables only listed groups. Group IDs are usually negative integers.
79
+ A missing or empty `allowedUserIds` allows every user. A missing or empty `allowedChatIds` allows all DMs and no groups. A non-empty `allowedChatIds` allows only the listed DMs and groups. Group IDs are usually negative.
80
80
 
81
- Use both lists for a private bot. `allowedUserIds` controls who can trigger work, but messages from other users in an allowed group can still enter the sender-attributed pending group context. Only add the bot to groups whose conversation is suitable for model input.
81
+ Use both lists for a private bot. `allowedUserIds` decides who can start work, but in an allowed group, messages from other users can still become context, labeled with their sender. Add the bot only to groups whose conversation is fine to send to the model.
82
82
 
83
- A bot sees only `allowedProjectIds`. If that field is omitted, it sees every configured project. A sole allowed project is selected automatically; otherwise a chat needs `defaultProjectId` or an explicit `/use_<project>` preference before `/new`.
83
+ A bot sees only its `allowedProjectIds`, or every project if the field is missing. If only one project is allowed, it is selected automatically. Otherwise a chat needs `defaultProjectId` or a `/use_<project>` choice before `/new`.
84
84
 
85
- Telegram’s 100-command limit permits eleven built-ins and up to 89 `/use_<projectId>` commands.
85
+ Telegram allows 100 commands per bot: twelve built-ins and up to 88 `/use_<projectId>` commands.
86
86
 
87
- ## Project IDs and common fields
87
+ ## Projects
88
88
 
89
- Project IDs become Telegram command suffixes. They must contain only lowercase letters, digits, and underscores, and may be at most 28 characters:
89
+ Each project tells the runner where a session works. There are three kinds:
90
90
 
91
- ```text
92
- ledger
93
- platform_api
94
- release2026
95
- ```
96
-
97
- 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.
98
-
99
- Each project must define exactly one workspace source: `repo`, `directory`, or `projectIds`.
100
-
101
- ## Repository projects
102
-
103
- A repository project clones one GitHub repository into a session-specific managed workspace:
104
-
105
- ```json
106
- {
107
- "projects": {
108
- "ledger": {
109
- "repo": "acme/ledger",
110
- "ref": "main",
111
- "workingDirectory": "packages/api",
112
- "persona": "gpt-6.1-sol-coder"
113
- }
114
- }
115
- }
116
- ```
91
+ - A **repository project** clones one GitHub repository into a new workspace for each session.
92
+ - A **persistent-directory project** reuses one existing directory for all of its sessions.
93
+ - A **composite project** combines several repository projects in one workspace.
117
94
 
118
- `repo` must use GitHub `owner/repo` syntax. Arbitrary Git URLs are not accepted. The runner needs `gh` and `git` on its login-shell `PATH`, and `gh` must already be authenticated for the repository.
95
+ Project IDs become `/use_<projectId>` commands, so they may contain only lowercase letters, digits, and underscores, and be at most 28 characters. Every project can have a `description`, and must define exactly one of `repo`, `directory`, or `projectIds`. [Telegram projects and workspaces](telegram-projects.md) describes each kind, where workspaces live, provision hooks, and what happens to workspaces on restart.
119
96
 
120
- All managed projects use the top-level `workspaceRoot`. A session workspace is:
121
-
122
- ```text
123
- <effective-workspace-root>/<project-id>/<telegram-session-id>
124
- ```
125
-
126
- `workingDirectory` is optional and must be a relative directory inside the clone. Tau validates that it exists and does not escape the repository, then uses it as the session `cwd`. Without it, the repository root is the `cwd`.
127
-
128
- `ref` is optional and is passed to `git checkout` after cloning. Without it, the clone's default branch remains checked out. Configure a ref when sessions must begin from a predictable branch or commit.
129
-
130
- ### Repository caches
131
-
132
- Tau keeps a persistent bare cache at:
133
-
134
- ```text
135
- <effective-workspace-root>-repo-cache/<project-id>.git
136
- ```
137
-
138
- The first preparation uses `gh repo clone <owner/repo> <cache> -- --bare`. Later preparations fetch and prune the cache, then create the session workspace with a shared local clone. If the repository configured for the same project ID changes, Tau discards and recreates that cache.
139
-
140
- Caches are shared preparation inputs, not session workspaces.
141
-
142
- ## Persistent-directory projects
143
-
144
- A persistent-directory project reuses one existing directory instead of creating a managed clone:
145
-
146
- ```json
147
- {
148
- "projects": {
149
- "notes": {
150
- "directory": "/srv/tau/notes",
151
- "persona": "gpt-6.1-sol-coder"
152
- }
153
- }
154
- }
155
- ```
156
-
157
- Relative `directory` paths resolve from the Telegram config file's directory. JSON does not expand `~`, so use an absolute path when referring to a home directory.
158
-
159
- The directory must already exist. Tau never creates, replaces, provisions, or removes it. `/new`, session close, runner shutdown, and startup cleanup all preserve it.
160
-
161
- Every session for this project uses the same directory as its execution-environment `cwd`, including sessions owned by different chats or bots. Tau does not serialize their filesystem work. Use `maxSessions`, bot project scoping, and access allowlists to prevent unsafe concurrent edits when the directory is not designed for them.
162
-
163
- Persistent-directory sessions omit the conventional history `repository` attribute. On recovery, Tau requires the configured directory to match the directory stored in the Tau session snapshot. Changing `directory` does not migrate existing sessions and causes those recoveries to fail.
164
-
165
- ## Composite projects
166
-
167
- A composite project creates a root containing multiple repositories:
168
-
169
- ```json
170
- {
171
- "projects": {
172
- "web": { "repo": "acme/web", "ref": "main" },
173
- "api": { "repo": "acme/api", "workingDirectory": "services/http" },
174
- "platform": {
175
- "projectIds": ["web", "api"],
176
- "persona": "gpt-6.1-sol-coder:high",
177
- "instructions": "Keep shared contracts synchronized.",
178
- "subagents": {
179
- "launchModels": ["openai/gpt-6.1-sol:high"]
180
- }
181
- }
182
- }
183
- }
184
- ```
185
-
186
- `projectIds` requires at least two unique repository projects; directories and composites are invalid members. Order controls workspace context and history `repository`.
187
-
188
- Members live at `<composite-root>/<member-project-id>` and use each repository's cache, ref, and working directory. The root is the session `cwd`. At creation, Tau writes a root `AGENTS.md` listing the members and optional `instructions`, plus root `.tau/config.json` containing `subagents` or `{}`. These are workspace files, not synchronized mirrors of Telegram configuration, and are not regenerated when a preserved session reconnects.
189
-
190
- `subagents.launchModels` sets the subagent launch override allowlist. Runtime config resolves and enforces entries. See [subagents](subagents.md) for model syntax and inheritance.
191
-
192
- The composite owns the parent persona, subagents, model catalog, config, settings, and tools. Child `.tau/config.json` files are not merged. A subagent in a member directory rebuilds only target context: environment and repository metadata, applicable `AGENTS.md`, and all discovered skills.
193
-
194
- Composite preparation is all-or-nothing. If one member cannot be prepared, Tau removes the generated composite workspace. Composite workspaces and member repository caches use the top-level `workspaceRoot`.
195
-
196
- ## Managed workspace lifecycle
197
-
198
- Repository and composite projects follow the same lifecycle. Tau prepares one workspace per session, then the session continues working there. A normal runner restart reconnects without fetching or checking out repositories, rerunning provision hooks, or rewriting generated files. Workspace-preparation changes in Telegram configuration affect later preparations, not a preserved workspace.
199
-
200
- If the expected workspace is missing, recovery reconstructs it from the caches and current project configuration. `/new` and session close remove a managed workspace; normal shutdown preserves it. Tau does not commit, push, or save uncommitted changes elsewhere.
201
-
202
- ## Managed workspace safety
203
-
204
- **Important:** startup removes entries under managed workspace roots that no persisted session references. Dedicate these roots to Tau; never use a home directory, repository collection, or unrelated tree.
205
-
206
- Tau preserves referenced managed workspaces, configured persistent directories, and repository caches. `/new` closes the active session before replacing it. Closing a repository or composite session interrupts work and removes its workspace; persistent directories remain untouched.
207
-
208
- ## Provision hooks
209
-
210
- A repository may provide `.tau/scripts/provision` at its root. It must be a regular executable file, not a symlink, and start with a shebang. Tau runs it through `session.exec` from the configured working directory after the session becomes available, without blocking chat input.
211
-
212
- New and reconstructed workspaces run their hooks; preserved workspaces and persistent-directory projects do not. Composite sessions run member hooks in order and continue after failures. A failure is reported to linked chats but leaves the session usable. Keep hooks repeatable and non-interactive.
97
+ **Important:** at startup, the runner deletes everything under its managed workspace roots that no saved session uses. Give Tau a dedicated `workspaceRoot`; never use a home directory, a collection of repositories, or any directory with other content.
213
98
 
214
99
  ## Normal Tau configuration inside workspaces
215
100
 
216
- After preparation, Tau creates an ordinary local session at the workspace `cwd`. Runtime discovery reads normal `~/.config/tau` and ancestor `.tau` content visible from that path: personas, prompts, skills, project context, host tools, and other session settings work as they do in the TUI.
101
+ 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.
217
102
 
218
- A project `persona` overrides the normal default for that session. Sessions include applicable `AGENTS.md` instructions. Composite root context is generated deliberately and uses its required persona.
103
+ A project's `persona` replaces the default for its sessions. Composite sessions use the generated root files and their required persona.
219
104
 
220
- Every Telegram turn starts with hidden `<system>` guidance identifying Telegram as the source and reply destination. It favors direct simple answers and brief acknowledgements or meaningful progress updates for tool-driven or multi-step work, since users cannot see tool activity.
105
+ 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.
221
106
 
222
- A per-bot `systemMessage` appears in a second hidden block after the default. Audio transcripts receive another warning that they may contain noise or errors. Unlike project context, hidden guidance is persisted with user turns and can appear in history, so never put credentials in configured messages.
107
+ 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.
223
108
 
224
- The runner's speech credentials and `speech.voiceId` are loaded at startup from its environment and normal Tau config, based on the runner process's startup `cwd`. Restart the runner after changing those settings.
109
+ 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.
225
110
 
226
111
  ## Chat commands
227
112
 
@@ -236,14 +121,17 @@ The runner's speech credentials and `speech.voiceId` are loaded at startup from
236
121
  | `/effort_high` | Selects high reasoning for future turns. |
237
122
  | `/effort_xhigh` | Selects xhigh reasoning for future turns. |
238
123
  | `/prompt` | [Records a saved prompt](prompts-and-project-context.md#telegram-prompt-picker) while idle, without starting a turn. |
124
+ | `/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. |
239
125
  | `/compact` | Runs summary-only compaction while idle. |
240
126
  | `/interrupt` | Interrupts the active Tau turn. |
241
- | `/tts_on` | Enables a Gemini-generated voice note after each final assistant response. |
127
+ | `/tts_on` | Sends a voice note after each final assistant response. |
242
128
  | `/tts_off` | Disables voice responses. |
243
129
 
244
- [Persona](personas.md) selection is session-local; `/new` uses the project default.
130
+ 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.
245
131
 
246
- Preferences persist per bot and chat across restarts, projects, and sessions. Project changes apply to `/new`, not the active session.
132
+ A [persona](personas.md) choice applies to the current session only; `/new` uses the project's default.
133
+
134
+ 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.
247
135
 
248
136
  In groups, commands must explicitly mention the bot. Accepted forms include:
249
137
 
@@ -257,81 +145,79 @@ Commands addressed to other bots are ignored.
257
145
 
258
146
  ## DMs, groups, and active work
259
147
 
260
- In a DM, ordinary text goes to the active session. If no session is selected but the chat owns exactly one session, Tau selects it automatically. Otherwise the bot asks for `/new`.
148
+ In a DM, ordinary text goes to the active session. If none is selected but the chat has exactly one session, Tau selects it. Otherwise the bot asks for `/new`.
261
149
 
262
- In an allowed group, ordinary messages trigger Tau only when they explicitly mention the bot username. Non-triggering text and captions are buffered as sender-attributed background context. On the next valid mention, Tau includes up to the most recent 50 buffered messages since the previous bot-triggering turn, then clears that buffer after successful submission.
150
+ In an allowed group, a message starts a turn only when it mentions the bot's username. Other text and captions are kept as background context, labeled with their sender. At the next mention, Tau includes up to the 50 most recent kept messages since the last turn, and clears them once the turn is submitted.
263
151
 
264
- Pending group context can include attachment paths, audio transcripts, and processing errors. It is untrusted model input. `allowedUserIds` prevents an unlisted sender from triggering work, but their message can still become context in an allowed group.
152
+ Group context can include attachment paths, audio transcripts, and processing errors. The model receives it as untrusted input. `allowedUserIds` stops an unlisted sender from starting work, but their messages can still become context in an allowed group.
265
153
 
266
- Telegram text and transcribed audio use automatic submit-or-steer behavior. When the session is idle, the input starts a normal turn. When Tau is already working, it becomes steering input that stops the active turn at its next safe boundary and continues with the new message. Additional steering follows Tau's normal batching behavior. Telegram does not expose a separate queued-turn command. Use `/interrupt` when the desired action is to stop rather than steer.
154
+ Text and transcribed audio either start or steer a turn. When the session is idle, the message starts a normal turn. When Tau is working, the message steers the running turn: the turn stops at its next safe point and continues with the new message. Further steering is batched as usual. Telegram has no command to queue a separate turn. Use `/interrupt` to stop instead of steering.
267
155
 
268
- Tau keeps tool and lifecycle chatter quiet. It sends committed assistant text, including multiple messages from one run, and refreshes the typing indicator during work. Oversized replies are split into bounded chunks. Durable failed, blocked, and confirmed-unaccepted turn notifications remain pending until Telegram acknowledges delivery.
156
+ Tau does not report tool activity or lifecycle events. It sends assistant text, including several messages from one run, and keeps the typing indicator on during work. Long replies are split into parts. Notifications about failed, blocked, and unaccepted turns are kept until Telegram confirms delivery.
269
157
 
270
158
  ## Attachments and audio
271
159
 
272
- Telegram accepts arbitrary documents, photos, videos, animations, video notes, audio files, and voice notes. The agent receives execution-environment paths, sanitized filenames, MIME types, byte sizes, and captions. Receipt never executes files.
160
+ The bot accepts any documents, photos, videos, animations, video notes, audio files, and voice notes. The agent receives the file paths in the execution environment, cleaned-up filenames, MIME types, sizes, and captions. Files are never run on receipt.
273
161
 
274
- Limits are 32 attachments per turn, 20 MiB per file, and 100 MiB per turn. Downloads are bounded even without size metadata. Oversized or failed transfers are skipped with a warning.
162
+ Limits are 32 attachments per turn, 20 MiB per file, and 100 MiB per turn. Downloads are limited even when Telegram does not report a size. Files that are too large or fail to download are skipped with a warning.
275
163
 
276
- Uploads only queue files; captions are metadata. The next text or voice transcript submits or steers with one hidden `<system>` block per attachment. Only voice notes are automatically transcribed, up to 20 minutes. Audio-player files and audio documents arrive without transcription. Voice originals remain available even if transcription fails. DM and bot-triggering group transcripts are echoed before submission.
164
+ Sending a file only queues it, and its caption is kept as metadata. The next text or voice message starts or steers a turn, with one hidden `<system>` block per attachment. Only voice notes are transcribed, up to 20 minutes long. Audio files and audio documents arrive without a transcript. The original voice file stays available even if transcription fails. Transcripts of DMs, and of group messages that mention the bot, are echoed before submission.
277
165
 
278
- Files use native temporary directories in the execution environment; OS cleanup can invalidate their paths. Pending queues and group context clear on shutdown. File contents and metadata are untrusted.
166
+ Files are stored in the execution environment's temporary directory, so system cleanup can remove them. Queued files and group context are cleared on shutdown. Treat file contents and metadata as untrusted.
279
167
 
280
- Voice transcription uses Gemini with `GEMINI_API_KEY`, then `apiKeys.google`. Optional hints use GPT-6 Luna (reasoning disabled) and OpenAI credentials. Gemini transcribes audio verbatim with `gemini-3.5-transcribe`, using English (`en-US`) and Finnish (`fi-FI`) hints. It attempts remote file deletion. File delivery needs no Google key. See [credentials](credentials.md).
168
+ Voice transcription uses Gemini with `GEMINI_API_KEY`, or else `apiKeys.google`. Optional spelling hints use GPT-6 Luna (reasoning disabled) with OpenAI credentials. Gemini transcribes verbatim with `gemini-3.5-transcribe`, using English (`en-US`) and Finnish (`fi-FI`) hints, and Tau tries to delete the uploaded file afterward. Receiving files needs no Google key. See [credentials](credentials.md).
281
169
 
282
- `/tts_on` rewrites text with `gpt-6-luna` (reasoning disabled) and generates speech with Eleven v4 Turbo. It requires both OpenAI and ElevenLabs credentials and runner `ffmpeg` with Opus. Voice notes use brisk delivery and 1.15× speed. See [`speech` configuration](config-reference.md#speech) for default and custom voice selection. Source and rewritten text each allow 10,000 Unicode characters; audio allows 32 MiB. Rewrite and job timeouts are one and five minutes. Jobs are ephemeral. Failure sends `voice response failed. please try again.` without affecting text; details stay in logs.
170
+ `/tts_on` rewrites text with `gpt-6-luna` (reasoning disabled) and generates speech with Eleven v4 Turbo. It requires both OpenAI and ElevenLabs credentials and runner `ffmpeg` with Opus. Voice notes use brisk delivery and 1.15× speed. See [`speech` configuration](config-reference.md#speech) for default and custom voice selection. Source and rewritten text are limited to 10,000 Unicode characters each, and audio to 32 MiB. Rewriting times out after one minute and the whole job after five. Jobs are not saved. On failure, the bot sends `voice response failed. please try again.`; the text reply is unaffected and details go to the logs.
283
171
 
284
172
  ## Command client tools
285
173
 
286
- Telegram provides [photo, video, audio, and document delivery tools](tools.md#sending-files-to-telegram), plus workspace-selected command tools. Global `clientTools` definitions provide executables; the workspace's nearest `enabledClientTools` selects an exact subset. An empty list disables configured tools, not built-in delivery tools.
174
+ Telegram offers [photo, video, audio, and document delivery tools](tools.md#sending-files-to-telegram), plus the command tools selected by the workspace. Global `clientTools` define the commands, and the workspace's nearest `enabledClientTools` selects which ones are offered. An empty list turns off configured tools, but not the delivery tools.
287
175
 
288
- These command processes run on the Telegram runner machine with the runner process environment. They can reach the session workspace only through their explicit execution-environment facade, despite physical co-location. Telegram does not advertise TUI-only `diff_review` or `prefill_input` tools.
176
+ Command processes run on the runner machine with the runner's environment. Even though the workspace is on the same machine, they reach it only through the execution-environment API. Telegram does not offer the TUI's `diff_review` or `prefill_input`.
289
177
 
290
- Tool selection occurs when the session client is created or reconnected. `/reload` does not rebuild the Telegram client advertisement. Restart the runner or create a new session client after changing command client-tool selection. See [client tools](client-tools.md).
178
+ Tools are selected when the runner creates or reconnects a session's client. `/reload` does not change them. Restart the runner, or start a new session, after changing which command tools are selected. See [client tools](client-tools.md).
291
179
 
292
180
  ## Persistence and restart behavior
293
181
 
294
- Normal shutdown interrupts live work, waits for it to settle, and disconnects sessions. Finished sessions reconnect with their conversation on restart. A session interrupted during workspace or Tau session creation starts preparation again. A persistent-directory workspace is never reconstructed and must remain at its original path.
182
+ Normal shutdown interrupts running work, waits for it to finish, and disconnects sessions. On restart, sessions reconnect with their conversation. A session interrupted while its workspace or Tau session was being created starts preparation again. A persistent-directory workspace is never reconstructed and must stay at its original path.
295
183
 
296
- If the connection is lost after Telegram submits a message, startup checks whether Tau accepted and completed it. Running work remains interruptible until it settles. A confirmed unaccepted message prompts the user to resend it; failed or blocked outcomes remain queued until delivery succeeds.
184
+ If the connection drops after a message was sent to Tau, startup checks whether Tau accepted and finished it. Work still running can be interrupted until it finishes. If Tau never accepted the message, the user is asked to resend it. Failed or blocked results stay queued until they are delivered.
297
185
 
298
- Restart does not replay responses. It clears voice jobs and short-lived retries, but pending notifications remain.
186
+ Restart does not resend earlier replies. It clears voice jobs and pending retries, but keeps pending notifications.
299
187
 
300
- If a failed session still has unresolved submitted work, Tau can reconnect to determine the outcome. Other failed sessions remain visible with their original diagnostic until the owning chat replaces or closes them. Do not edit runner state files to force recovery.
188
+ If a failed session still has submitted work with an unknown result, Tau can reconnect to find out. Other failed sessions stay visible with their original error until their chat replaces or closes them. Do not edit runner state files to force recovery.
301
189
 
302
190
  ## Verify a runner safely
303
191
 
304
- 1. Validate that the Telegram file is parseable JSON without displaying it in a shared terminal transcript.
192
+ 1. Check that the Telegram file is valid JSON, without showing it in a shared terminal.
305
193
  2. Confirm the runner user can execute `tau`, `git`, and `gh`, and that `gh` can access each repository.
306
194
  3. Start the runner and watch for config, polling, command-sync, cache, checkout, and recovery errors. Successful startup prints `tau telegram running`.
307
195
  4. In an allowed DM, run `/status`, select a project if needed, then run `/new`.
308
- 5. Submit a small nonsensitive prompt and confirm an assistant response.
309
- 6. If groups are enabled, verify that an unmentioned message stays quiet and an explicitly mentioned command works.
310
- 7. If a provision hook exists, confirm its completion or reported failure before relying on its dependencies.
311
- 8. Restart the runner normally and use `/status` to confirm the session recovered without replaying old replies.
196
+ 5. Send a small, non-sensitive prompt and confirm the reply.
197
+ 6. If groups are enabled, check that a message without a mention gets no reply and a command with a mention works.
198
+ 7. If there is a provision hook, wait for it to finish or report a failure before relying on what it installs.
199
+ 8. Restart the runner normally and use `/status` to confirm the session recovered without resending old replies.
312
200
 
313
201
  Do not verify bot tokens, provider keys, Access secrets, transcript contents, or runner state by printing them.
314
202
 
315
- ## Common failures
316
-
317
- **The runner rejects its config.** Check required `bots` and `projects` objects, exact project IDs, positive numeric fields, known allowed projects, and that each project defines exactly one of `repo`, `directory`, or `projectIds`.
203
+ ## Troubleshooting
318
204
 
319
- **The bot ignores a DM.** If `allowedChatIds` is set, the DM chat ID must be listed. If `allowedUserIds` is set, the sender's user ID must also be listed.
205
+ **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, 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.
320
206
 
321
- **The bot ignores a group.** Groups require an explicit `allowedChatIds` entry and a direct `@botusername` mention. Commands also need the mention in one of the accepted command forms.
207
+ **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.
322
208
 
323
- **`/new` asks for a project.** Configure `defaultProjectId`, expose only one project, or run the matching `/use_<projectId>` command first.
209
+ **The bot ignores a group.** The group must be in `allowedChatIds`, the message must mention the bot (commands in one of the accepted forms), and the sender must be in `allowedUserIds` when that list is set. Messages that do not mention the bot can still become context for the next turn, so make sure that is acceptable before allowing more groups.
324
210
 
325
- **Repository preparation fails.** Verify runner-side `gh` authentication, `git` availability, repository access, configured `ref`, and that `workingDirectory` exists in the checked-out tree. A changed repository under an existing project ID causes cache reinitialization.
211
+ **`/new` asks for a project, or uses the wrong one.** Set `defaultProjectId`, allow only one project, or run `/use_<projectId>` first. `/use_<projectId>` affects only future `/new` sessions; `/status` shows the current session's project and the saved choice separately.
326
212
 
327
- **Startup removed unexpected files.** The configured workspace root was not dedicated to Tau. Stop the runner and move `workspaceRoot` to an isolated directory before restarting. Recovery data does not make unrelated deleted files restorable.
213
+ **A message sent during work changes direction.** While a turn is running, a new message steers it instead of waiting in a queue. Wait for the turn to finish before sending an unrelated task, or use `/interrupt` first.
328
214
 
329
- **Provisioning fails but chat still works.** This is expected isolation. Fix the executable bit, shebang, script behavior, or dependencies, then create a new managed workspace if the hook needs to run again.
215
+ **Audio or an attachment fails.** The reply or runner log names the step that failed: download, saving, format, or transcription. Check that Telegram can deliver the file to the bot, the type is supported, the runner can write to its temporary directory, and Gemini accepts the media type. Transcription needs `GEMINI_API_KEY` or `apiKeys.google` for the runner process; restart the runner after setting it. Changing a workspace's environment does not affect the runner. Do not log media or transcripts to debug this.
330
216
 
331
- **A persistent-directory session fails recovery.** Restore the configured directory and its original path, or return the project config to the snapshot's durable `cwd`. Tau does not migrate an existing session to a new persistent directory.
217
+ **Voice replies fail.** `/tts_on` needs OpenAI and ElevenLabs credentials and `ffmpeg` with Opus on the runner. Check [`speech.voiceId`](config-reference.md#speech) if a configured voice is unavailable. The bot replies `voice response failed. please try again.` and logs the details; the text reply is still delivered.
332
218
 
333
- **Audio reports a missing key.** Set the credential for the Gemini in the runner process and restart it. A workspace-only environment change does not update the runner's startup speech configuration.
219
+ **Replies or notifications arrive late or not at all.** Each outgoing message part has a deadline and is retried twice on retryable errors. Tau honors Telegram's `retry_after`, and later notifications in a chat wait for earlier ones. Long replies are split, so only a later part may have failed. The runner log shows the method, status, retry class, attempt, chat, session, and message. Fix network, rate-limit, token, or chat permission problems on the runner. Before resending by hand, check which parts arrived. A delivery failure is not a failed turn, and notifications about failed or blocked turns are sent again after a restart. Never fix delivery by editing runner state.
334
220
 
335
- **A message sent during active work changes direction.** Telegram uses steering, not ordinary queueing, while a turn is running. Wait for completion before sending an independent next task, or use `/interrupt` to stop the current run first.
221
+ **A command client tool is missing.** Check global `clientTools`, the workspace's `enabledClientTools`, and whether the session's client was created after the change. Telegram never offers the TUI's tools.
336
222
 
337
- **A command client tool is absent.** Check global `clientTools`, the prepared workspace's `enabledClientTools`, and whether the session client was created after the change. Telegram never provides TUI-only tools.
223
+ For workspace preparation, provisioning, and recovery problems, see [Telegram projects and workspaces](telegram-projects.md#troubleshooting).