@markusylisiurunen/tau 0.3.68 → 0.3.70

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 (145) hide show
  1. package/dist/core/history/cli.js +8 -1
  2. package/dist/core/history/cli.js.map +1 -1
  3. package/dist/core/history/migrations.js +0 -55
  4. package/dist/core/history/migrations.js.map +1 -1
  5. package/dist/core/history/remote_history_client.js +14 -2
  6. package/dist/core/history/remote_history_client.js.map +1 -1
  7. package/dist/core/history/replication.js +1 -1
  8. package/dist/core/history/replication.js.map +1 -1
  9. package/dist/core/history/setup.js +31 -4
  10. package/dist/core/history/setup.js.map +1 -1
  11. package/dist/core/personas.js +34 -3
  12. package/dist/core/personas.js.map +1 -1
  13. package/dist/core/static/code_mode/history/documentation.md +3 -1
  14. package/dist/core/static/tau_docs/history.md +28 -6
  15. package/dist/core/static/tau_docs/prompts-and-project-context.md +8 -0
  16. package/dist/core/static/tau_docs/telegram.md +6 -5
  17. package/dist/core/static/tau_docs/tui.md +2 -2
  18. package/dist/core/telegram/adapter.js +117 -3
  19. package/dist/core/telegram/adapter.js.map +1 -1
  20. package/dist/core/telegram/config.js +1 -1
  21. package/dist/core/telegram/session_manager.js +26 -0
  22. package/dist/core/telegram/session_manager.js.map +1 -1
  23. package/dist/core/tools/history.js +1 -0
  24. package/dist/core/tools/history.js.map +1 -1
  25. package/dist/core/utils/gemini_speech.js +1 -1
  26. package/dist/core/utils/gemini_transcription.js +1 -1
  27. package/dist/core/version.js +2 -2
  28. package/dist/diff_tool/app/dist/assets/{angular-html-D1LkfYUk.js → angular-html-TGVKIbXA.js} +1 -1
  29. package/dist/diff_tool/app/dist/assets/{angular-ts-GBkN0dgz.js → angular-ts-BR1Ck4xw.js} +1 -1
  30. package/dist/diff_tool/app/dist/assets/{apl-B9B6LoBR.js → apl-DX86sScC.js} +1 -1
  31. package/dist/diff_tool/app/dist/assets/{astro-Bq1oMHJ9.js → astro-CPBmHrtV.js} +1 -1
  32. package/dist/diff_tool/app/dist/assets/{blade-COJUdHCh.js → blade-CxhONptb.js} +1 -1
  33. package/dist/diff_tool/app/dist/assets/{c-B5mehGRz.js → c-DGLPRsse.js} +1 -1
  34. package/dist/diff_tool/app/dist/assets/{chapel-mTzW_3Rb.js → chapel-RThVDyb2.js} +1 -1
  35. package/dist/diff_tool/app/dist/assets/{cobol-CUZj6Dna.js → cobol-DdmfL9_4.js} +1 -1
  36. package/dist/diff_tool/app/dist/assets/{coffee-CNqmYL25.js → coffee-Cc3rH_kQ.js} +1 -1
  37. package/dist/diff_tool/app/dist/assets/{cpp-CRMagQcD.js → cpp-ByIB1X9N.js} +1 -1
  38. package/dist/diff_tool/app/dist/assets/{crystal-DCj_yeSi.js → crystal-D4oyx0-U.js} +1 -1
  39. package/dist/diff_tool/app/dist/assets/{css-DAwpXLWY.js → css-D8nErU8j.js} +1 -1
  40. package/dist/diff_tool/app/dist/assets/{edge-CtFTwS18.js → edge-CwM1llxP.js} +1 -1
  41. package/dist/diff_tool/app/dist/assets/{elixir-C1OrVr_M.js → elixir-CH1xxWSt.js} +1 -1
  42. package/dist/diff_tool/app/dist/assets/{elm-BdS37bq6.js → elm-DIHxLa6c.js} +1 -1
  43. package/dist/diff_tool/app/dist/assets/{erb-Ba0Xnkuj.js → erb-Cm1nGJ0q.js} +1 -1
  44. package/dist/diff_tool/app/dist/assets/{git-rebase-DRtTCAv_.js → git-rebase-XqH6cLrF.js} +1 -1
  45. package/dist/diff_tool/app/dist/assets/{glimmer-js-B4F-uWDL.js → glimmer-js-BqGQt95b.js} +1 -1
  46. package/dist/diff_tool/app/dist/assets/{glimmer-ts-JRBbzMuE.js → glimmer-ts-C9m18ZbT.js} +1 -1
  47. package/dist/diff_tool/app/dist/assets/{glsl-B0F7j1rZ.js → glsl-F8r7-2qc.js} +1 -1
  48. package/dist/diff_tool/app/dist/assets/{graphql-B2oXozjz.js → graphql-HRIQ08tp.js} +1 -1
  49. package/dist/diff_tool/app/dist/assets/{hack-BmdKh4Xg.js → hack-b2OBFgba.js} +1 -1
  50. package/dist/diff_tool/app/dist/assets/{haml-B5txh1mt.js → haml-BMiBYzBg.js} +1 -1
  51. package/dist/diff_tool/app/dist/assets/{handlebars-BrOeahQH.js → handlebars-DekHln92.js} +1 -1
  52. package/dist/diff_tool/app/dist/assets/{html-CwVI_Nmo.js → html-0sl00AVS.js} +1 -1
  53. package/dist/diff_tool/app/dist/assets/{html-derivative-Dmp-VtO0.js → html-derivative-olhhIOfr.js} +1 -1
  54. package/dist/diff_tool/app/dist/assets/{http-DZvA2jMi.js → http-DHDcde9F.js} +1 -1
  55. package/dist/diff_tool/app/dist/assets/{hurl-Czca3pRy.js → hurl-DDYRhecJ.js} +1 -1
  56. package/dist/diff_tool/app/dist/assets/index-C-CAr6eO.js +1559 -0
  57. package/dist/diff_tool/app/dist/assets/{java-C9B5avDT.js → java-HX7ZmFma.js} +1 -1
  58. package/dist/diff_tool/app/dist/assets/{javascript-DdUPgmCi.js → javascript-B46p6LUW.js} +1 -1
  59. package/dist/diff_tool/app/dist/assets/{jinja-C88anDVh.js → jinja-0oRZFwZT.js} +1 -1
  60. package/dist/diff_tool/app/dist/assets/{jison-CYn9Sl79.js → jison-Bsqd8yEw.js} +1 -1
  61. package/dist/diff_tool/app/dist/assets/{json-CYp3vrCJ.js → json-1uKHLwRn.js} +1 -1
  62. package/dist/diff_tool/app/dist/assets/{jsx-BuDHdbPM.js → jsx-D2laGlc4.js} +1 -1
  63. package/dist/diff_tool/app/dist/assets/{julia-CLyKC_SD.js → julia-B7CzN7W2.js} +1 -1
  64. package/dist/diff_tool/app/dist/assets/{just-BRw_BRkc.js → just-Duoo4Aa7.js} +1 -1
  65. package/dist/diff_tool/app/dist/assets/{latex-Bsyk0ZET.js → latex-yy-UOrbw.js} +1 -1
  66. package/dist/diff_tool/app/dist/assets/{liquid-B2KWKTUY.js → liquid-DGliSu2Y.js} +1 -1
  67. package/dist/diff_tool/app/dist/assets/{lua-BBCZNaoe.js → lua-MFb4H6uH.js} +1 -1
  68. package/dist/diff_tool/app/dist/assets/{marko-CzEoqxXx.js → marko-DO4tJoIQ.js} +1 -1
  69. package/dist/diff_tool/app/dist/assets/{mdc-BYYhPtQ2.js → mdc-B_h-aRcg.js} +1 -1
  70. package/dist/diff_tool/app/dist/assets/{nginx-B0ANyMYF.js → nginx-ZW7yrLH6.js} +1 -1
  71. package/dist/diff_tool/app/dist/assets/{nim-BnO-hPuv.js → nim-DTOMK7Zk.js} +1 -1
  72. package/dist/diff_tool/app/dist/assets/{org-DZ53bUbb.js → org-BRx006Oj.js} +1 -1
  73. package/dist/diff_tool/app/dist/assets/{perl-DCaXAjqZ.js → perl-CzcNKD-6.js} +1 -1
  74. package/dist/diff_tool/app/dist/assets/{php-DFJP7AYG.js → php-BiSrGF6V.js} +1 -1
  75. package/dist/diff_tool/app/dist/assets/pierre-dark-CpLgRqie.js +1 -0
  76. package/dist/diff_tool/app/dist/assets/pierre-dark-protanopia-deuteranopia-B35FxJx-.js +1 -0
  77. package/dist/diff_tool/app/dist/assets/pierre-dark-soft-kZQmAZld.js +1 -0
  78. package/dist/diff_tool/app/dist/assets/pierre-dark-tritanopia-CpjhbsIL.js +1 -0
  79. package/dist/diff_tool/app/dist/assets/pierre-dark-vibrant-CpQYzh95.js +1 -0
  80. package/dist/diff_tool/app/dist/assets/pierre-light-CoaEpmwp.js +1 -0
  81. package/dist/diff_tool/app/dist/assets/pierre-light-protanopia-deuteranopia-0fSaH845.js +1 -0
  82. package/dist/diff_tool/app/dist/assets/pierre-light-soft-lWLdNTOI.js +1 -0
  83. package/dist/diff_tool/app/dist/assets/pierre-light-tritanopia-CEbqgOJL.js +1 -0
  84. package/dist/diff_tool/app/dist/assets/pierre-light-vibrant-D80Fkn33.js +1 -0
  85. package/dist/diff_tool/app/dist/assets/{pug-CibApuhk.js → pug-CYv51GYY.js} +1 -1
  86. package/dist/diff_tool/app/dist/assets/{qml-DwVnDdTM.js → qml-0pTrG3lk.js} +1 -1
  87. package/dist/diff_tool/app/dist/assets/{r-C4Dchd1P.js → r-B0zy4_qi.js} +1 -1
  88. package/dist/diff_tool/app/dist/assets/{razor-CyJ-w1Ub.js → razor-BMuOnZCw.js} +1 -1
  89. package/dist/diff_tool/app/dist/assets/{regexp-CNghQ5eY.js → regexp-D4sf8V9F.js} +1 -1
  90. package/dist/diff_tool/app/dist/assets/{rst-CynWjePz.js → rst-B-dJoZuq.js} +1 -1
  91. package/dist/diff_tool/app/dist/assets/{ruby-CLYbVE1Z.js → ruby-B9dG1g9M.js} +1 -1
  92. package/dist/diff_tool/app/dist/assets/{sas-bJHxiY-x.js → sas-jS1VjXDD.js} +1 -1
  93. package/dist/diff_tool/app/dist/assets/{scss-CcgsuVdc.js → scss-DaEAH7JG.js} +1 -1
  94. package/dist/diff_tool/app/dist/assets/{shellscript-CFJeRn3g.js → shellscript-D26VaoMD.js} +1 -1
  95. package/dist/diff_tool/app/dist/assets/{shellsession-BzeG4DIz.js → shellsession-BrDdB3Ze.js} +1 -1
  96. package/dist/diff_tool/app/dist/assets/{soy-B1Ewncjv.js → soy-Dw-L8Pu_.js} +1 -1
  97. package/dist/diff_tool/app/dist/assets/{sql-C6gIz-i-.js → sql-C81_P4zv.js} +1 -1
  98. package/dist/diff_tool/app/dist/assets/{stata-DZ3n2NNy.js → stata-CvjhiEWy.js} +1 -1
  99. package/dist/diff_tool/app/dist/assets/{surrealql-CxjJ_MYO.js → surrealql-zH2wPEcX.js} +1 -1
  100. package/dist/diff_tool/app/dist/assets/{svelte-Dad0hAwn.js → svelte-DkxiJRsu.js} +1 -1
  101. package/dist/diff_tool/app/dist/assets/{templ-BuNk0dhh.js → templ-iHivH4zC.js} +1 -1
  102. package/dist/diff_tool/app/dist/assets/{tex-C61-WHOR.js → tex-tmkUHMpX.js} +1 -1
  103. package/dist/diff_tool/app/dist/assets/{ts-tags-BMWGwRt6.js → ts-tags-Co_nt4ji.js} +1 -1
  104. package/dist/diff_tool/app/dist/assets/{tsx-AKa5rdAm.js → tsx-Bq4h6cEE.js} +1 -1
  105. package/dist/diff_tool/app/dist/assets/{twig-CKXFwiec.js → twig-BrgfE6ee.js} +1 -1
  106. package/dist/diff_tool/app/dist/assets/{typescript-BzRr3JI0.js → typescript-DXeIJyMP.js} +1 -1
  107. package/dist/diff_tool/app/dist/assets/{typst-BXNdvFzz.js → typst-BDxuPNyd.js} +1 -1
  108. package/dist/diff_tool/app/dist/assets/{vue-Du8kVE1C.js → vue-DpegOn40.js} +1 -1
  109. package/dist/diff_tool/app/dist/assets/{vue-html-DMrkK9XH.js → vue-html-9jsGeHE2.js} +1 -1
  110. package/dist/diff_tool/app/dist/assets/{vue-vine-5f0lzpP6.js → vue-vine-Di2kBUqf.js} +1 -1
  111. package/dist/diff_tool/app/dist/assets/{xml-BUuAw89A.js → xml-DAh0wZod.js} +1 -1
  112. package/dist/diff_tool/app/dist/assets/{xsl-cGKOdY7Q.js → xsl-CAKtuHsO.js} +1 -1
  113. package/dist/diff_tool/app/dist/assets/{yaml-BxZ2CAV2.js → yaml-G84U_K-l.js} +1 -1
  114. package/dist/diff_tool/app/dist/index.html +1 -1
  115. package/dist/history/worker/index.js +369 -35
  116. package/dist/history/worker/index.js.map +1 -1
  117. package/dist/history/worker/migrations/0001_initial.sql +54 -0
  118. package/dist/history/worker/viewer.css +72 -0
  119. package/dist/history/worker/viewer.js +116 -0
  120. package/dist/host/local_session_host.js +0 -1
  121. package/dist/host/local_session_host.js.map +1 -1
  122. package/dist/host/session_protocol_handler.js +2 -1
  123. package/dist/host/session_protocol_handler.js.map +1 -1
  124. package/dist/protocol/session_protocol.js +46 -0
  125. package/dist/protocol/session_protocol.js.map +1 -1
  126. package/dist/tui/chat_view.js +4 -4
  127. package/dist/tui/chat_view.js.map +1 -1
  128. package/dist/tui/session_chat_controller.js +3 -1
  129. package/dist/tui/session_chat_controller.js.map +1 -1
  130. package/dist/tui/tool_ui_router.js +1 -1
  131. package/dist/tui/tool_ui_router.js.map +1 -1
  132. package/dist/tui/ui/chat_container.js +24 -19
  133. package/dist/tui/ui/chat_container.js.map +1 -1
  134. package/package.json +15 -10
  135. package/dist/diff_tool/app/dist/assets/index-B9k3vTfc.js +0 -1565
  136. package/dist/diff_tool/app/dist/assets/theming-B5HubD5i.js +0 -1
  137. package/dist/diff_tool/app/dist/assets/theming-BTK0wyKj.js +0 -1
  138. package/dist/diff_tool/app/dist/assets/theming-BTUlzJFw.js +0 -1
  139. package/dist/diff_tool/app/dist/assets/theming-BURFlYfV.js +0 -1
  140. package/dist/diff_tool/app/dist/assets/theming-BVZjihoZ.js +0 -1
  141. package/dist/diff_tool/app/dist/assets/theming-BWSkzMBn.js +0 -1
  142. package/dist/diff_tool/app/dist/assets/theming-BchIqPOb.js +0 -1
  143. package/dist/diff_tool/app/dist/assets/theming-CeLfRn2P.js +0 -1
  144. package/dist/diff_tool/app/dist/assets/theming-DMjwMSwU.js +0 -1
  145. package/dist/diff_tool/app/dist/assets/theming-QWGGdlwd.js +0 -1
@@ -51,7 +51,7 @@ When Tau is idle, Enter and Ctrl+Enter both start a normal turn. While a turn is
51
51
 
52
52
  Pending input is session state shared by attached clients while the host remains alive. It is not durable across host restart or session recovery. A queued turn captures the persona, reasoning, tools, and model settings when that turn actually starts. Steering remains part of the active logical turn and keeps the settings captured when that turn began.
53
53
 
54
- Escape interrupts foreground client work or the main session’s active work. If a local diff review, recording, or speech playback task owns the foreground, Escape stops that task first; otherwise it requests main-session interruption from the host. It does not stop independently running supervised subagents; select one with Alt+Down and use Ctrl+G. Press Escape twice to clear the current editor text.
54
+ Escape interrupts foreground client work or the main session’s active work. If a local diff review, recording, or speech playback task owns the foreground, Escape stops that task first; otherwise it requests main-session interruption from the host, including cancellation of a running manual compaction. Once compaction has replaced model context, a late interruption does not undo it or turn successful persistence into a failure. It does not stop independently running supervised subagents; select one with Alt+Down and use Ctrl+G. Press Escape twice to clear the current editor text.
55
55
 
56
56
  Press Enter twice on an empty editor to retry from the current session history. Retry does not rewind or duplicate the last user message. Goal-controlled turns cannot be retried; resume a blocked goal instead.
57
57
 
@@ -199,7 +199,7 @@ The TUI also advertises diff review as a client tool unless `--no-client-tools`
199
199
 
200
200
  `/listen` and Ctrl+Y are currently macOS-only. Recording uses local `ffmpeg` with the AVFoundation audio input and stops when Ctrl+Y is pressed again, when Escape is pressed, or after 20 minutes. Startup fails if the microphone produces no audio within 15 seconds. Stopping manually or reaching the 20-minute limit finishes transcription and inserts the transcript at the cursor for review without submitting it.
201
201
 
202
- If transcription fails, Tau retains the local WAV and reports its path. Run `/listen retry` to transcribe the same audio again with the current provider and conversation context, or `/listen discard` to delete it. Retained recordings longer than 20 minutes are rejected rather than sent to the provider. A replacement recording deletes the retained file only after the new capture starts producing audio. Tau keeps at most one failed recording, and exiting leaves that file at the reported path for manual recovery. For live transcription, both providers extract spelling hints from recent conversation while recording begins, buffer audio until the transcription session is ready, and then stream subsequent audio. Gemini uses Gemini 3.7 Flash for hint extraction and `gemini-3.5-transcribe-live` in smart mode; OpenAI uses GPT-5.6 Luna and `gpt-live-transcribe`. Hint extraction failure does not fail transcription. On retry, Gemini uploads the retained recording for `gemini-3.5-transcribe` smart transcription and attempts to delete the remote file afterward; OpenAI uploads it to `gpt-transcribe` with recent context.
202
+ If transcription fails, Tau retains the local WAV and reports its path. Run `/listen retry` to transcribe the same audio again with the current provider and conversation context, or `/listen discard` to delete it. Retained recordings longer than 20 minutes are rejected rather than sent to the provider. A replacement recording deletes the retained file only after the new capture starts producing audio. Tau keeps at most one failed recording, and exiting leaves that file at the reported path for manual recovery. For live transcription, both providers extract spelling hints from recent conversation while recording begins, buffer audio until the transcription session is ready, and then stream subsequent audio. Gemini uses Gemini 3.8 Flash for hint extraction and `gemini-3.5-transcribe-live` in smart mode; OpenAI uses GPT-5.6 Luna and `gpt-live-transcribe`. Hint extraction failure does not fail transcription. On retry, Gemini uploads the retained recording for `gemini-3.5-transcribe` smart transcription and attempts to delete the remote file afterward; OpenAI uploads it to `gpt-transcribe` with recent context.
203
203
 
204
204
  Install `ffmpeg` and configure a speech-to-text provider:
205
205
 
@@ -1,3 +1,4 @@
1
+ import { randomUUID } from "node:crypto";
1
2
  import { mkdtemp, readdir, rm, writeFile } from "node:fs/promises";
2
3
  import { tmpdir } from "node:os";
3
4
  import { basename, extname, join } from "node:path";
@@ -543,7 +544,7 @@ const TelegramCallbackQuerySchema = telegramPartialObject({
543
544
  id: z.string(),
544
545
  from: TelegramUserSchema,
545
546
  data: z.string(),
546
- message: telegramPartialObject({ chat: TelegramChatSchema }),
547
+ message: telegramPartialObject({ message_id: z.number(), chat: TelegramChatSchema }),
547
548
  });
548
549
  const TelegramUpdateSchema = telegramPartialObject({
549
550
  update_id: z.number(),
@@ -721,6 +722,14 @@ function createTelegramApi(botToken) {
721
722
  ...(options?.replyMarkup ? { reply_markup: options.replyMarkup } : {}),
722
723
  }, z.unknown(), options.signal);
723
724
  },
725
+ async editMessage(chatId, messageId, text, options) {
726
+ await callTelegramMethod("editMessageText", {
727
+ chat_id: chatId,
728
+ message_id: messageId,
729
+ text,
730
+ reply_markup: options.replyMarkup ?? { inline_keyboard: [] },
731
+ }, z.unknown(), options.signal);
732
+ },
724
733
  async sendRichMessage(chatId, markdown, options) {
725
734
  await callTelegramMethod("sendRichMessage", {
726
735
  chat_id: chatId,
@@ -806,6 +815,7 @@ class TelegramAdapterImpl {
806
815
  commandHandlers;
807
816
  callbackActionHandlers;
808
817
  abortController = new AbortController();
818
+ promptPickers = new Map();
809
819
  activeSessionsByChat = new Map();
810
820
  sessionsByChat = new Map();
811
821
  chatsBySession = new Map();
@@ -929,6 +939,11 @@ class TelegramAdapterImpl {
929
939
  callbackAction: "status",
930
940
  handler: async (chatId) => this.handleStatus(chatId),
931
941
  },
942
+ {
943
+ command: "/prompt",
944
+ description: "add a saved prompt without starting a turn",
945
+ handler: async (chatId, args) => this.handlePrompt(chatId, args),
946
+ },
932
947
  {
933
948
  command: "/compact",
934
949
  description: "compact session context",
@@ -1203,7 +1218,7 @@ class TelegramAdapterImpl {
1203
1218
  await this.answerCallbackQuery(callbackQuery.id);
1204
1219
  return;
1205
1220
  }
1206
- const callbackHandled = await this.handleCallback(chat.id, callbackData);
1221
+ const callbackHandled = await this.handleCallback(chat.id, callbackData, callbackQuery.message?.message_id);
1207
1222
  await this.answerCallbackQuery(callbackQuery.id, callbackHandled ? "done" : undefined);
1208
1223
  }
1209
1224
  parseAudioMessage(message) {
@@ -1583,7 +1598,11 @@ class TelegramAdapterImpl {
1583
1598
  }
1584
1599
  await handler(chatId, args, sourceMessageId);
1585
1600
  }
1586
- async handleCallback(chatId, callbackData) {
1601
+ async handleCallback(chatId, callbackData, messageId) {
1602
+ if (callbackData.startsWith("prompt:")) {
1603
+ await this.handlePromptCallback(chatId, callbackData, messageId);
1604
+ return true;
1605
+ }
1587
1606
  if (!callbackData.startsWith(CALLBACK_ACTION_PREFIX)) {
1588
1607
  return false;
1589
1608
  }
@@ -1650,6 +1669,101 @@ class TelegramAdapterImpl {
1650
1669
  await this.reply(chatId, `failed to save voice response preference: ${error instanceof Error ? error.message : String(error)}`);
1651
1670
  }
1652
1671
  }
1672
+ async handlePrompt(chatId, args) {
1673
+ if (args.length > 0) {
1674
+ await this.reply(chatId, "use /prompt to choose a saved prompt.");
1675
+ return;
1676
+ }
1677
+ const session = await this.requireActiveSession(chatId);
1678
+ if (!session)
1679
+ return;
1680
+ try {
1681
+ const snapshot = await this.getSessionManagerForChat(chatId).getSessionSnapshot(session.id);
1682
+ if (!snapshot) {
1683
+ await this.reply(chatId, "the session is still preparing.");
1684
+ return;
1685
+ }
1686
+ const prompts = snapshot.catalog.prompts;
1687
+ if (prompts.length === 0) {
1688
+ await this.reply(chatId, "this session has no saved prompts.");
1689
+ return;
1690
+ }
1691
+ this.promptPickers.set(chatId, { token: randomUUID(), sessionId: session.id, prompts });
1692
+ await this.showPromptPage(chatId, 0);
1693
+ }
1694
+ catch (error) {
1695
+ await this.reply(chatId, this.formatManagerError(error));
1696
+ }
1697
+ }
1698
+ async showPromptPage(chatId, page, messageId) {
1699
+ const picker = this.promptPickers.get(chatId);
1700
+ if (!picker)
1701
+ return;
1702
+ const start = page * 20;
1703
+ const keyboard = picker.prompts.slice(start, start + 20).map((prompt, index) => [
1704
+ {
1705
+ text: prompt.label ?? prompt.id,
1706
+ callback_data: `prompt:${picker.token}:select:${start + index}`,
1707
+ },
1708
+ ]);
1709
+ const navigation = [];
1710
+ if (page > 0)
1711
+ navigation.push({
1712
+ text: "previous",
1713
+ callback_data: `prompt:${picker.token}:page:${page - 1}`,
1714
+ });
1715
+ if (start + 20 < picker.prompts.length)
1716
+ navigation.push({ text: "next", callback_data: `prompt:${picker.token}:page:${page + 1}` });
1717
+ if (navigation.length > 0)
1718
+ keyboard.push(navigation);
1719
+ const text = "choose a saved prompt to add to the conversation without starting a turn. selection is available only while Tau is idle.";
1720
+ const replyMarkup = { inline_keyboard: keyboard };
1721
+ if (messageId !== undefined) {
1722
+ await this.sendWithRetry((signal) => this.api.editMessage(chatId, messageId, text, { replyMarkup, signal }));
1723
+ }
1724
+ else {
1725
+ await this.reply(chatId, text, { replyMarkup });
1726
+ }
1727
+ }
1728
+ async handlePromptCallback(chatId, data, messageId) {
1729
+ const match = /^prompt:([^:]+):(select|page):(0|[1-9][0-9]*)$/.exec(data);
1730
+ const picker = this.promptPickers.get(chatId);
1731
+ if (!match ||
1732
+ !picker ||
1733
+ picker.token !== match[1] ||
1734
+ this.getActiveSession(chatId)?.id !== picker.sessionId ||
1735
+ messageId === undefined) {
1736
+ await this.reply(chatId, "this prompt picker has expired. use /prompt to open a new one.");
1737
+ return;
1738
+ }
1739
+ const index = Number(match[3]);
1740
+ if (match[2] === "page") {
1741
+ if (index * 20 < picker.prompts.length)
1742
+ await this.showPromptPage(chatId, index, messageId);
1743
+ return;
1744
+ }
1745
+ const prompt = picker.prompts[index];
1746
+ if (!prompt)
1747
+ return;
1748
+ let text;
1749
+ try {
1750
+ text = await this.getSessionManagerForChat(chatId).recordPrompt(picker.sessionId, prompt.id);
1751
+ }
1752
+ catch (error) {
1753
+ await this.reply(chatId, this.formatManagerError(error));
1754
+ return;
1755
+ }
1756
+ this.promptPickers.delete(chatId);
1757
+ const chunks = splitTelegramMessage(`added this prompt to the conversation. send a message when you’re ready for Tau to respond.\n\n${text}`);
1758
+ for (const [index, chunk] of chunks.entries()) {
1759
+ if (index === 0) {
1760
+ await this.sendWithRetry((signal) => this.api.editMessage(chatId, messageId, chunk, { signal }));
1761
+ }
1762
+ else {
1763
+ await this.reply(chatId, chunk);
1764
+ }
1765
+ }
1766
+ }
1653
1767
  async handleSetReasoning(chatId, reasoning, args) {
1654
1768
  if (args.length > 0) {
1655
1769
  await this.reply(chatId, `usage: /effort_${reasoning}`);