@grinev/opencode-telegram-bot 0.21.0 → 0.21.2

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 (182) hide show
  1. package/.env.example +18 -2
  2. package/README.md +21 -8
  3. package/dist/app/{start-bot-app.js → bootstrap/start-bot-app.js} +16 -15
  4. package/dist/{bot/utils/assistant-run-footer.js → app/formatters/assistant-run-footer-formatter.js} +1 -1
  5. package/dist/{summary → app/formatters}/subagent-formatter.js +8 -3
  6. package/dist/{summary/formatter.js → app/formatters/summary-formatter.js} +4 -132
  7. package/dist/{summary → app/formatters}/tool-message-batcher.js +1 -1
  8. package/dist/{bot/assistant-run-state.js → app/managers/assistant-run-state-manager.js} +1 -1
  9. package/dist/{attach/manager.js → app/managers/attach-manager.js} +1 -1
  10. package/dist/{background-session/tracker.js → app/managers/background-session-manager.js} +2 -2
  11. package/dist/{scheduled-task/foreground-state.js → app/managers/foreground-session-state-manager.js} +1 -1
  12. package/dist/{interaction/manager.js → app/managers/interaction-manager.js} +31 -1
  13. package/dist/{permission/manager.js → app/managers/permission-manager.js} +1 -1
  14. package/dist/{question/manager.js → app/managers/question-manager.js} +1 -1
  15. package/dist/{rename/manager.js → app/managers/rename-manager.js} +1 -1
  16. package/dist/{scheduled-task/creation-manager.js → app/managers/scheduled-task-creation-manager.js} +2 -2
  17. package/dist/{summary/aggregator.js → app/managers/summary-aggregation-manager.js} +3 -3
  18. package/dist/{agent/manager.js → app/services/agent-selection-service.js} +4 -5
  19. package/dist/{attach/service.js → app/services/attach-service.js} +23 -44
  20. package/dist/{bot/utils/busy-reconciliation.js → app/services/busy-reconciliation-service.js} +12 -7
  21. package/dist/app/services/command-catalog-service.js +21 -0
  22. package/dist/{bot/utils/external-user-input.js → app/services/external-user-input-service.js} +4 -19
  23. package/dist/app/services/file-browser-service.js +265 -0
  24. package/dist/{bot/utils/file-download.js → app/services/file-download-service.js} +14 -28
  25. package/dist/app/services/mcp-catalog-service.js +79 -0
  26. package/dist/app/services/message-history-service.js +81 -0
  27. package/dist/{model/capabilities.js → app/services/model-capabilities-service.js} +2 -2
  28. package/dist/{model/context-limit.js → app/services/model-context-limit-service.js} +4 -4
  29. package/dist/{model/manager.js → app/services/model-selection-service.js} +4 -4
  30. package/dist/{project/manager.js → app/services/project-service.js} +3 -3
  31. package/dist/app/services/project-switch-service.js +39 -0
  32. package/dist/{bot/utils/busy-guard.js → app/services/run-control-service.js} +3 -14
  33. package/dist/{scheduled-task/executor.js → app/services/scheduled-task-executor-service.js} +5 -5
  34. package/dist/{scheduled-task/runtime.js → app/services/scheduled-task-runtime-service.js} +17 -56
  35. package/dist/{scheduled-task/schedule-parser.js → app/services/scheduled-task-schedule-parser-service.js} +3 -3
  36. package/dist/{scheduled-task/session-ignore.js → app/services/scheduled-task-session-ignore-service.js} +1 -1
  37. package/dist/{session/cache-manager.js → app/services/session-cache-service.js} +3 -3
  38. package/dist/{session/manager.js → app/services/session-service.js} +1 -1
  39. package/dist/app/services/skills-catalog-service.js +21 -0
  40. package/dist/{stt/client.js → app/services/stt-service.js} +2 -2
  41. package/dist/{tts/client.js → app/services/tts-service.js} +68 -5
  42. package/dist/{variant/manager.js → app/services/variant-selection-service.js} +3 -3
  43. package/dist/{scheduled-task/store.js → app/stores/scheduled-task-store.js} +3 -3
  44. package/dist/{settings/manager.js → app/stores/settings-store.js} +3 -3
  45. package/dist/app/types/worktree.js +1 -0
  46. package/dist/bot/{handlers/agent.js → callbacks/agent-selection-callback-handler.js} +8 -57
  47. package/dist/bot/callbacks/callback-router.js +110 -0
  48. package/dist/bot/{commands/commands.js → callbacks/command-catalog-callback-handler.js} +17 -197
  49. package/dist/bot/{handlers/context.js → callbacks/context-control-callback-handler.js} +3 -32
  50. package/dist/bot/callbacks/file-browser-callback-handler.js +219 -0
  51. package/dist/bot/callbacks/inline-menu-cancel-callback-handler.js +32 -0
  52. package/dist/bot/{commands/mcps.js → callbacks/mcp-catalog-callback-handler.js} +7 -172
  53. package/dist/bot/{commands/messages.js → callbacks/message-history-callback-handler.js} +17 -248
  54. package/dist/bot/{handlers/model.js → callbacks/model-selection-callback-handler.js} +10 -71
  55. package/dist/bot/callbacks/permission-callback-handler.js +114 -0
  56. package/dist/bot/callbacks/project-callback-handler.js +79 -0
  57. package/dist/bot/callbacks/question-callback-handler.js +159 -0
  58. package/dist/bot/{commands/rename.js → callbacks/rename-callback-handler.js} +6 -34
  59. package/dist/bot/{commands/tasklist.js → callbacks/scheduled-task-callback-handler.js} +83 -68
  60. package/dist/bot/{commands/sessions.js → callbacks/session-callback-handler.js} +19 -158
  61. package/dist/bot/{commands/skills.js → callbacks/skills-catalog-callback-handler.js} +8 -188
  62. package/dist/bot/{handlers/variant.js → callbacks/variant-selection-callback-handler.js} +7 -69
  63. package/dist/bot/callbacks/worktree-callback-handler.js +97 -0
  64. package/dist/bot/commands/{abort.js → abort-command.js} +6 -6
  65. package/dist/bot/commands/command-catalog-command.js +42 -0
  66. package/dist/bot/commands/{detach.js → detach-command.js} +8 -8
  67. package/dist/bot/commands/ls-command.js +48 -0
  68. package/dist/bot/commands/mcp-catalog-command.js +39 -0
  69. package/dist/bot/commands/messages-command.js +59 -0
  70. package/dist/bot/commands/{new.js → new-command.js} +12 -11
  71. package/dist/bot/commands/open-command.js +45 -0
  72. package/dist/bot/commands/{opencode-start.js → opencode-start-command.js} +1 -1
  73. package/dist/bot/commands/{opencode-stop.js → opencode-stop-command.js} +1 -1
  74. package/dist/bot/commands/projects-command.js +32 -0
  75. package/dist/bot/commands/rename-command.js +33 -0
  76. package/dist/bot/commands/sessions-command.js +42 -0
  77. package/dist/bot/commands/skills-catalog-command.js +42 -0
  78. package/dist/bot/commands/{start.js → start-command.js} +12 -12
  79. package/dist/bot/commands/{status.js → status-command.js} +9 -9
  80. package/dist/bot/commands/{task.js → task-command.js} +9 -81
  81. package/dist/bot/commands/tasklist-command.js +40 -0
  82. package/dist/bot/commands/{tts.js → tts-command.js} +2 -2
  83. package/dist/bot/commands/worktree-command.js +47 -0
  84. package/dist/bot/handlers/{document.js → document-handler.js} +3 -3
  85. package/dist/bot/handlers/{media-group.js → media-group-handler.js} +3 -3
  86. package/dist/bot/handlers/photo-handler.js +44 -0
  87. package/dist/bot/handlers/prompt.js +16 -16
  88. package/dist/bot/handlers/text-message-handler.js +61 -0
  89. package/dist/bot/handlers/tts-response-handler.js +29 -0
  90. package/dist/bot/handlers/{voice.js → voice-handler.js} +2 -2
  91. package/dist/bot/index.js +27 -1130
  92. package/dist/{keyboard/manager.js → bot/keyboards/keyboard-manager.js} +6 -6
  93. package/dist/bot/keyboards/keyboard-types.js +1 -0
  94. package/dist/bot/{utils/keyboard.js → keyboards/main-reply-keyboard.js} +2 -2
  95. package/dist/bot/menus/agent-selection-menu.js +54 -0
  96. package/dist/bot/menus/command-catalog-menu.js +101 -0
  97. package/dist/bot/menus/context-control-menu.js +33 -0
  98. package/dist/bot/menus/file-browser-menu.js +265 -0
  99. package/dist/bot/{handlers → menus}/inline-menu.js +4 -33
  100. package/dist/bot/menus/mcp-catalog-menu.js +97 -0
  101. package/dist/bot/menus/message-history-menu.js +107 -0
  102. package/dist/bot/menus/model-selection-menu.js +66 -0
  103. package/dist/bot/menus/permission-menu.js +116 -0
  104. package/dist/bot/{commands/projects.js → menus/project-selection-menu.js} +5 -105
  105. package/dist/bot/{handlers/question.js → menus/question-menu.js} +10 -166
  106. package/dist/bot/menus/rename-menu.js +6 -0
  107. package/dist/bot/menus/scheduled-task-menu.js +42 -0
  108. package/dist/bot/menus/session-selection-menu.js +115 -0
  109. package/dist/bot/menus/skills-catalog-menu.js +99 -0
  110. package/dist/bot/menus/variant-selection-menu.js +67 -0
  111. package/dist/bot/menus/worktree-selection-menu.js +76 -0
  112. package/dist/bot/{utils → messages}/assistant-rendering.js +2 -2
  113. package/dist/bot/messages/busy-blocked-renderer.js +11 -0
  114. package/dist/bot/messages/external-user-input-notification.js +22 -0
  115. package/dist/bot/messages/scheduled-task-delivery.js +63 -0
  116. package/dist/bot/{utils → messages}/send-downloaded-file.js +1 -1
  117. package/dist/bot/messages/summary-message-formatter.js +130 -0
  118. package/dist/bot/{utils → messages}/telegram-text.js +60 -0
  119. package/dist/{interaction/guard.js → bot/middleware/interaction-guard-decision.js} +11 -4
  120. package/dist/bot/middleware/interaction-guard.js +2 -2
  121. package/dist/bot/middleware/unknown-command.js +1 -1
  122. package/dist/{pinned/format.js → bot/pinned/pinned-message-format.js} +1 -2
  123. package/dist/{pinned/manager.js → bot/pinned/pinned-message-manager.js} +10 -10
  124. package/dist/bot/pinned/pinned-message-types.js +1 -0
  125. package/dist/{summary → bot/render}/markdown-to-telegram-v2.js +2 -2
  126. package/dist/bot/render/types.js +1 -0
  127. package/dist/bot/routers/command-router.js +73 -0
  128. package/dist/bot/routers/message-router.js +150 -0
  129. package/dist/bot/services/attach-presentation.js +40 -0
  130. package/dist/bot/services/event-subscription-service.js +842 -0
  131. package/dist/bot/services/project-switch-presentation.js +26 -0
  132. package/dist/bot/streaming/response-streamer.js +18 -2
  133. package/dist/cli.js +4 -4
  134. package/dist/config.js +21 -3
  135. package/dist/i18n/ar.js +491 -0
  136. package/dist/i18n/index.js +7 -0
  137. package/dist/index.js +1 -1
  138. package/dist/opencode/ready-refresh.js +2 -2
  139. package/dist/{service → runtime/service}/manager.js +2 -2
  140. package/dist/runtime/service/types.js +1 -0
  141. package/package.json +1 -1
  142. package/dist/bot/commands/ls.js +0 -445
  143. package/dist/bot/commands/open.js +0 -319
  144. package/dist/bot/commands/worktree.js +0 -201
  145. package/dist/bot/handlers/permission.js +0 -235
  146. package/dist/bot/utils/browser-roots.js +0 -140
  147. package/dist/bot/utils/file-tree.js +0 -92
  148. package/dist/bot/utils/send-tts-response.js +0 -37
  149. package/dist/bot/utils/switch-project.js +0 -45
  150. package/dist/bot/utils/telegram-file-url.js +0 -11
  151. package/dist/interaction/busy.js +0 -8
  152. package/dist/interaction/cleanup.js +0 -32
  153. /package/dist/{scheduled-task/display.js → app/formatters/scheduled-task-display-formatter.js} +0 -0
  154. /package/dist/{bot/utils/abort-error-suppression.js → app/managers/abort-suppression-manager.js} +0 -0
  155. /package/dist/{external-input/suppression.js → app/managers/external-input-suppression-manager.js} +0 -0
  156. /package/dist/{scheduled-task/next-run.js → app/services/scheduled-task-next-run-service.js} +0 -0
  157. /package/dist/{git/worktree.js → app/services/worktree-service.js} +0 -0
  158. /package/dist/{agent/types.js → app/types/agent.js} +0 -0
  159. /package/dist/{interaction/types.js → app/types/interaction.js} +0 -0
  160. /package/dist/{model/types.js → app/types/model.js} +0 -0
  161. /package/dist/{keyboard/types.js → app/types/permission.js} +0 -0
  162. /package/dist/{permission/types.js → app/types/project.js} +0 -0
  163. /package/dist/{pinned/types.js → app/types/question.js} +0 -0
  164. /package/dist/{scheduled-task/types.js → app/types/scheduled-task.js} +0 -0
  165. /package/dist/{question/types.js → app/types/session.js} +0 -0
  166. /package/dist/{service/types.js → app/types/settings.js} +0 -0
  167. /package/dist/{telegram/render/types.js → app/types/summary.js} +0 -0
  168. /package/dist/{variant/types.js → app/types/variant.js} +0 -0
  169. /package/dist/bot/commands/{help.js → help-command.js} +0 -0
  170. /package/dist/bot/{utils → messages}/send-with-markdown-fallback.js +0 -0
  171. /package/dist/bot/{utils → messages}/thinking-message.js +0 -0
  172. /package/dist/{telegram → bot}/render/block-fallback.js +0 -0
  173. /package/dist/{telegram → bot}/render/block-parser.js +0 -0
  174. /package/dist/{telegram → bot}/render/block-renderer.js +0 -0
  175. /package/dist/{telegram → bot}/render/chunker.js +0 -0
  176. /package/dist/{telegram → bot}/render/inline-renderer.js +0 -0
  177. /package/dist/{telegram → bot}/render/markdown-normalizer.js +0 -0
  178. /package/dist/{telegram → bot}/render/pipeline.js +0 -0
  179. /package/dist/{telegram → bot}/render/validator.js +0 -0
  180. /package/dist/bot/{utils/commands.js → routers/command-utils.js} +0 -0
  181. /package/dist/bot/{utils → streaming}/finalize-assistant-response.js +0 -0
  182. /package/dist/{service/runtime.js → runtime/service/env.js} +0 -0
package/.env.example CHANGED
@@ -80,6 +80,15 @@ OPENCODE_MODEL_ID=big-pickle
80
80
  # If exceeded, the bot stops waiting for the result and marks the run as failed.
81
81
  # SCHEDULED_TASK_EXECUTION_TIMEOUT_MINUTES=120
82
82
 
83
+ # Send scheduled task result/error messages without Telegram push notifications (default: false)
84
+ # SCHEDULED_TASK_DISABLE_NOTIFICATION=false
85
+
86
+ # Response streaming mode: "edit" (default) or "draft" (default: edit)
87
+ # edit = uses editMessageText to update messages incrementally (may flicker)
88
+ # draft = uses sendMessageDraft (Bot API 9.5+) for smooth animated draft previews,
89
+ # then finalizes with sendMessage. Only works in private chats.
90
+ # RESPONSE_STREAMING_MODE=edit
91
+
83
92
  # Stream update throttle in milliseconds for assistant/tool message edits (default: 1000)
84
93
  # Higher value = fewer Telegram edit requests, lower value = more real-time updates
85
94
  # RESPONSE_STREAM_THROTTLE_MS=1000
@@ -89,7 +98,7 @@ OPENCODE_MODEL_ID=big-pickle
89
98
  # BASH_TOOL_DISPLAY_MAX_LENGTH=128
90
99
 
91
100
  # Bot locale: supported locale code (default: en)
92
- # Supported locales: en, de, es, fr, ru, zh
101
+ # Supported locales: en, ar, de, es, fr, ru, zh
93
102
  # BOT_LOCALE=en
94
103
 
95
104
  # Hide thinking indicator messages (default: false)
@@ -135,7 +144,7 @@ OPENCODE_MODEL_ID=big-pickle
135
144
 
136
145
  # Text-to-Speech credentials (optional)
137
146
  # TTS reply behavior is controlled globally with /tts and persisted in settings.json.
138
- # Provider: "openai" (default) or "google".
147
+ # Provider: "openai" (default), "elevenlabs", or "google".
139
148
  #
140
149
  # --- OpenAI-compatible (default) ---
141
150
  # Set TTS_API_URL and TTS_API_KEY to any OpenAI-compatible TTS endpoint.
@@ -144,6 +153,13 @@ OPENCODE_MODEL_ID=big-pickle
144
153
  # TTS_MODEL=gpt-4o-mini-tts
145
154
  # TTS_VOICE=alloy
146
155
  #
156
+ # --- ElevenLabs ---
157
+ # TTS_PROVIDER=elevenlabs
158
+ # TTS_API_URL=https://api.elevenlabs.io/v1
159
+ # TTS_API_KEY=
160
+ # TTS_MODEL=eleven_flash_v2_5
161
+ # TTS_VOICE=21m00Tcm4TlvDq8ikWAM
162
+ #
147
163
  # --- Google Cloud TTS ---
148
164
  # 1. Create a project at https://console.cloud.google.com
149
165
  # 2. Enable "Cloud Text-to-Speech API"
package/README.md CHANGED
@@ -17,12 +17,14 @@ Scheduled tasks support. Turns the bot into a lightweight OpenClaw alternative f
17
17
 
18
18
  Platforms: macOS, Windows, Linux
19
19
 
20
- Languages: English (`en`), Deutsch (`de`), Español (`es`), Français (`fr`), Русский (`ru`), 简体中文 (`zh`)
20
+ Languages: English (`en`), العربية (`ar`), Deutsch (`de`), Español (`es`), Français (`fr`), Русский (`ru`), 简体中文 (`zh`)
21
21
 
22
22
  <p align="center">
23
23
  <img src="assets/screencast.gif" width="45%" alt="OpenCode Telegram Bot screencast" />
24
24
  </p>
25
25
 
26
+ > I use [boardown](https://github.com/grinev/boardown), my open-source Markdown-based task board, to plan and track this project. It stores tasks in plain `.md` files and can be used as a VS Code extension or a desktop app.
27
+
26
28
  ## Features
27
29
 
28
30
  - **Remote coding** — send prompts to OpenCode from anywhere, receive complete results with code sent as files
@@ -190,7 +192,7 @@ For this to work, the console OpenCode instance must be started on the same port
190
192
 
191
193
  ### Localization
192
194
 
193
- - Supported locales: `en`, `de`, `es`, `fr`, `ru`, `zh`
195
+ - Supported locales: `en`, `ar`, `de`, `es`, `fr`, `ru`, `zh`
194
196
  - The setup wizard asks for language first
195
197
  - You can change locale later with `BOT_LOCALE`
196
198
 
@@ -217,7 +219,7 @@ When installed via npm, the configuration wizard handles the initial setup. The
217
219
  | `OPENCODE_SERVER_PASSWORD` | Server auth password | No | — |
218
220
  | `OPENCODE_MODEL_PROVIDER` | Default model provider | Yes | `opencode` |
219
221
  | `OPENCODE_MODEL_ID` | Default model ID | Yes | `big-pickle` |
220
- | `BOT_LOCALE` | Bot UI language (supported locale code, e.g. `en`, `de`, `es`, `fr`, `ru`, `zh`) | No | `en` |
222
+ | `BOT_LOCALE` | Bot UI language (supported locale code, e.g. `en`, `ar`, `de`, `es`, `fr`, `ru`, `zh`) | No | `en` |
221
223
  | `SESSIONS_LIST_LIMIT` | Sessions per page in `/sessions` | No | `10` |
222
224
  | `MESSAGES_LIST_LIMIT` | User messages per page in `/messages` | No | `10` |
223
225
  | `PROJECTS_LIST_LIMIT` | Projects per page in `/projects` | No | `10` |
@@ -225,6 +227,7 @@ When installed via npm, the configuration wizard handles the initial setup. The
225
227
  | `COMMANDS_LIST_LIMIT` | Items per page in `/commands` and `/skills` | No | `10` |
226
228
  | `TASK_LIMIT` | Maximum number of scheduled tasks that can exist at once | No | `10` |
227
229
  | `SCHEDULED_TASK_EXECUTION_TIMEOUT_MINUTES` | Maximum time the bot waits for one scheduled task run before marking it failed | No | `120` |
230
+ | `SCHEDULED_TASK_DISABLE_NOTIFICATION` | Send scheduled task result/error messages without Telegram push notifications | No | `false` |
228
231
  | `BASH_TOOL_DISPLAY_MAX_LENGTH` | Maximum displayed length for `bash` tool commands in Telegram summaries; longer commands are truncated | No | `128` |
229
232
  | `SERVICE_MESSAGES_INTERVAL_SEC` | Service messages interval (thinking + tool calls); keep `>=2` to avoid Telegram rate limits, `0` = immediate | No | `5` |
230
233
  | `HIDE_THINKING_MESSAGES` | Hide `💭 Thinking...` service messages | No | `false` |
@@ -239,11 +242,11 @@ When installed via npm, the configuration wizard handles the initial setup. The
239
242
  | `STT_MODEL` | STT model name passed to `/audio/transcriptions` | No | `whisper-large-v3-turbo` |
240
243
  | `STT_LANGUAGE` | Optional language hint (empty = provider auto-detect) | No | — |
241
244
  | `STT_NOTE_PROMPT` | Optional note prepended to the LLM prompt as `[Note: ...]` for voice transcriptions; empty / `false` / `0` disable it | No | — |
242
- | `TTS_PROVIDER` | TTS provider: `openai` for OpenAI-compatible APIs or `google` for Google Cloud TTS | No | `openai` |
243
- | `TTS_API_URL` | OpenAI-compatible TTS API base URL | No | — |
244
- | `TTS_API_KEY` | OpenAI-compatible TTS API key | No | — |
245
- | `TTS_MODEL` | OpenAI-compatible TTS model name passed to `/audio/speech` | No | `gpt-4o-mini-tts` |
246
- | `TTS_VOICE` | TTS voice name. Defaults to `alloy` for OpenAI-compatible APIs and `en-US-Studio-O` for Google Cloud TTS | No | provider-specific |
245
+ | `TTS_PROVIDER` | TTS provider: `openai` for OpenAI-compatible APIs, `elevenlabs` for ElevenLabs, or `google` for Google Cloud TTS | No | `openai` |
246
+ | `TTS_API_URL` | TTS API base URL for OpenAI-compatible APIs or ElevenLabs | No | — |
247
+ | `TTS_API_KEY` | TTS API key for OpenAI-compatible APIs or ElevenLabs | No | — |
248
+ | `TTS_MODEL` | TTS model name. Passed as `model` for OpenAI-compatible APIs and `model_id` for ElevenLabs | No | `gpt-4o-mini-tts` |
249
+ | `TTS_VOICE` | TTS voice name or ElevenLabs voice ID. Defaults to `alloy`, `21m00Tcm4TlvDq8ikWAM`, or `en-US-Studio-O` by provider | No | provider-specific |
247
250
  | `GOOGLE_APPLICATION_CREDENTIALS` | Path to a Google Cloud service account JSON key file for `TTS_PROVIDER=google` | No | — |
248
251
  | `LOG_LEVEL` | Log level (`debug`, `info`, `warn`, `error`) | No | `info` |
249
252
  | `LOG_RETENTION` | Number of log files to keep: launch files in `sources`, daily files in `installed` | No | `10` |
@@ -323,6 +326,16 @@ TTS_MODEL=gpt-4o-mini-tts
323
326
  TTS_VOICE=alloy
324
327
  ```
325
328
 
329
+ ElevenLabs TTS configuration example:
330
+
331
+ ```env
332
+ TTS_PROVIDER=elevenlabs
333
+ TTS_API_URL=https://api.elevenlabs.io/v1
334
+ TTS_API_KEY=your-elevenlabs-api-key
335
+ TTS_MODEL=eleven_flash_v2_5
336
+ TTS_VOICE=21m00Tcm4TlvDq8ikWAM
337
+ ```
338
+
326
339
  Google Cloud TTS configuration example:
327
340
 
328
341
  ```env
@@ -1,22 +1,23 @@
1
1
  import fs from "node:fs/promises";
2
2
  import { readFile } from "node:fs/promises";
3
- import { cleanupBotRuntime, createBot } from "../bot/index.js";
4
- import { config } from "../config.js";
5
- import { opencodeAutoRestartService } from "../opencode/auto-restart.js";
6
- import { notifyOpencodeReadyIfHealthy, registerOpenCodeReadyRefreshHandler, } from "../opencode/ready-refresh.js";
7
- import { loadSettings } from "../settings/manager.js";
8
- import { scheduledTaskRuntime } from "../scheduled-task/runtime.js";
9
- import { reconcileStoredModelSelection } from "../model/manager.js";
10
- import { getRuntimeMode } from "../runtime/mode.js";
11
- import { getRuntimePaths } from "../runtime/paths.js";
12
- import { clearServiceStateFile } from "../service/manager.js";
13
- import { getServiceStateFilePathFromEnv, isServiceChildProcess } from "../service/runtime.js";
14
- import { getLogFilePath, initializeLogger, logger } from "../utils/logger.js";
15
- import { safeBackgroundTask } from "../utils/safe-background-task.js";
3
+ import { cleanupBotRuntime, createBot } from "../../bot/index.js";
4
+ import { createScheduledTaskDeliverySender } from "../../bot/messages/scheduled-task-delivery.js";
5
+ import { config } from "../../config.js";
6
+ import { opencodeAutoRestartService } from "../../opencode/auto-restart.js";
7
+ import { notifyOpencodeReadyIfHealthy, registerOpenCodeReadyRefreshHandler, } from "../../opencode/ready-refresh.js";
8
+ import { loadSettings } from "../stores/settings-store.js";
9
+ import { scheduledTaskRuntime } from "../services/scheduled-task-runtime-service.js";
10
+ import { reconcileStoredModelSelection } from "../services/model-selection-service.js";
11
+ import { getRuntimeMode } from "../../runtime/mode.js";
12
+ import { getRuntimePaths } from "../../runtime/paths.js";
13
+ import { clearServiceStateFile } from "../../runtime/service/manager.js";
14
+ import { getServiceStateFilePathFromEnv, isServiceChildProcess } from "../../runtime/service/env.js";
15
+ import { getLogFilePath, initializeLogger, logger } from "../../utils/logger.js";
16
+ import { safeBackgroundTask } from "../../utils/safe-background-task.js";
16
17
  const SHUTDOWN_TIMEOUT_MS = 5000;
17
18
  async function getBotVersion() {
18
19
  try {
19
- const packageJsonPath = new URL("../../package.json", import.meta.url);
20
+ const packageJsonPath = new URL("../../../package.json", import.meta.url);
20
21
  const packageJsonContent = await readFile(packageJsonPath, "utf-8");
21
22
  const packageJson = JSON.parse(packageJsonContent);
22
23
  return packageJson.version ?? "unknown";
@@ -43,7 +44,7 @@ export async function startBotApp() {
43
44
  await reconcileStoredModelSelection();
44
45
  registerOpenCodeReadyRefreshHandler();
45
46
  const bot = createBot();
46
- await scheduledTaskRuntime.initialize(bot);
47
+ await scheduledTaskRuntime.initialize(bot, createScheduledTaskDeliverySender(bot.api, config.telegram.allowedUserId));
47
48
  safeBackgroundTask({
48
49
  taskName: "app.opencodeStartup",
49
50
  task: async () => {
@@ -1,4 +1,4 @@
1
- import { getAgentDisplayName } from "../../agent/types.js";
1
+ import { getAgentDisplayName } from "../types/agent.js";
2
2
  function formatDuration(elapsedMs) {
3
3
  const safeElapsedMs = Math.max(0, Math.round(elapsedMs));
4
4
  const totalSeconds = Math.floor(safeElapsedMs / 1000);
@@ -1,6 +1,11 @@
1
- import { formatModelDisplayName } from "../pinned/format.js";
2
- import { t } from "../i18n/index.js";
3
- import { formatCompactToolInfo } from "./formatter.js";
1
+ import { t } from "../../i18n/index.js";
2
+ import { formatCompactToolInfo } from "./summary-formatter.js";
3
+ function formatModelDisplayName(providerID, modelID) {
4
+ if (providerID && modelID) {
5
+ return `${providerID}/${modelID}`;
6
+ }
7
+ return t("pinned.unknown");
8
+ }
4
9
  function shouldPreferInputDetails(tool, input) {
5
10
  if (!input) {
6
11
  return false;
@@ -1,12 +1,8 @@
1
1
  import * as path from "path";
2
- import { config } from "../config.js";
3
- import { logger } from "../utils/logger.js";
4
- import { t } from "../i18n/index.js";
5
- import { getCurrentProject } from "../settings/manager.js";
6
- import { convertToTelegramMarkdownV2 } from "./markdown-to-telegram-v2.js";
7
- import { normalizeMarkdownForTelegramRendering } from "../telegram/render/markdown-normalizer.js";
8
- const TELEGRAM_MESSAGE_LIMIT = 4096;
9
- const MARKDOWN_V2_RESERVED_CHARS = /([_\*\[\]\(\)~`>#+\-=|{}.!\\])/g;
2
+ import { config } from "../../config.js";
3
+ import { logger } from "../../utils/logger.js";
4
+ import { t } from "../../i18n/index.js";
5
+ import { getCurrentProject } from "../stores/settings-store.js";
10
6
  function truncateWithEllipsis(text, maxLength) {
11
7
  if (text.length <= maxLength) {
12
8
  return text;
@@ -16,50 +12,6 @@ function truncateWithEllipsis(text, maxLength) {
16
12
  }
17
13
  return `${text.slice(0, maxLength - 3).trimEnd()}...`;
18
14
  }
19
- function endsWithOddTrailingBackslashes(text, start, end) {
20
- let backslashCount = 0;
21
- for (let index = end - 1; index >= start; index--) {
22
- if (text[index] !== "\\") {
23
- break;
24
- }
25
- backslashCount += 1;
26
- }
27
- return backslashCount % 2 === 1;
28
- }
29
- function resolveSplitEndIndex(text, currentIndex, maxLength, options) {
30
- const hardLimit = Math.min(text.length, currentIndex + maxLength);
31
- if (hardLimit >= text.length) {
32
- return text.length;
33
- }
34
- let endIndex = hardLimit;
35
- const breakPoint = text.lastIndexOf("\n", endIndex);
36
- if (breakPoint > currentIndex) {
37
- endIndex = breakPoint + 1;
38
- }
39
- if (!options?.avoidTrailingMarkdownEscape) {
40
- return endIndex;
41
- }
42
- while (endIndex > currentIndex && endsWithOddTrailingBackslashes(text, currentIndex, endIndex)) {
43
- endIndex -= 1;
44
- }
45
- return endIndex > currentIndex ? endIndex : hardLimit;
46
- }
47
- function splitText(text, maxLength, options) {
48
- const parts = [];
49
- let currentIndex = 0;
50
- while (currentIndex < text.length) {
51
- const endIndex = resolveSplitEndIndex(text, currentIndex, maxLength, options);
52
- if (endIndex <= currentIndex) {
53
- const fallbackEnd = Math.min(text.length, currentIndex + 1);
54
- parts.push(text.slice(currentIndex, fallbackEnd));
55
- currentIndex = fallbackEnd;
56
- continue;
57
- }
58
- parts.push(text.slice(currentIndex, endIndex));
59
- currentIndex = endIndex;
60
- }
61
- return parts;
62
- }
63
15
  export function normalizePathForDisplay(filePath) {
64
16
  const normalizedPath = filePath.replace(/\\/g, "/");
65
17
  const project = getCurrentProject();
@@ -81,86 +33,6 @@ export function normalizePathForDisplay(filePath) {
81
33
  }
82
34
  return normalizedPath;
83
35
  }
84
- export function formatSummary(text) {
85
- return formatSummaryWithMode(text, config.bot.messageFormatMode);
86
- }
87
- export function escapePlainTextForTelegramMarkdownV2(text) {
88
- return text.replace(MARKDOWN_V2_RESERVED_CHARS, "\\$1");
89
- }
90
- function formatMarkdownForTelegram(text) {
91
- try {
92
- const preprocessed = normalizeMarkdownForTelegramRendering(text);
93
- return escapeMarkdownV2PipesOutsideCode(convertToTelegramMarkdownV2(preprocessed));
94
- }
95
- catch (error) {
96
- logger.warn("[Formatter] Failed to convert markdown summary, falling back to raw text", error);
97
- return text;
98
- }
99
- }
100
- function escapeMarkdownV2PipesOutsideCode(text) {
101
- let result = "";
102
- let index = 0;
103
- let inInlineCode = false;
104
- let inCodeFence = false;
105
- while (index < text.length) {
106
- if (text.startsWith("```", index)) {
107
- result += "```";
108
- index += 3;
109
- inCodeFence = !inCodeFence;
110
- continue;
111
- }
112
- const char = text[index];
113
- if (!inCodeFence && char === "`") {
114
- inInlineCode = !inInlineCode;
115
- result += char;
116
- index += 1;
117
- continue;
118
- }
119
- if (!inCodeFence && !inInlineCode && char === "|" && text[index - 1] !== "\\") {
120
- result += "\\|";
121
- index += 1;
122
- continue;
123
- }
124
- result += char;
125
- index += 1;
126
- }
127
- return result;
128
- }
129
- export function formatSummaryWithMode(text, mode, maxLength = TELEGRAM_MESSAGE_LIMIT) {
130
- if (!text || text.trim().length === 0) {
131
- return [];
132
- }
133
- const normalizedMaxLength = Math.max(1, Math.floor(maxLength));
134
- const rawTextLimit = mode === "raw" ? Math.max(1, normalizedMaxLength - "```\n\n```".length) : normalizedMaxLength;
135
- const parts = splitText(text, rawTextLimit);
136
- const formattedParts = [];
137
- for (const part of parts) {
138
- const trimmed = part.trim();
139
- if (!trimmed) {
140
- continue;
141
- }
142
- if (mode === "markdown") {
143
- const converted = formatMarkdownForTelegram(trimmed);
144
- const convertedParts = splitText(converted, normalizedMaxLength, {
145
- avoidTrailingMarkdownEscape: true,
146
- });
147
- for (const convertedPart of convertedParts) {
148
- const normalizedPart = convertedPart.trim();
149
- if (normalizedPart) {
150
- formattedParts.push(normalizedPart);
151
- }
152
- }
153
- continue;
154
- }
155
- if (parts.length > 1) {
156
- formattedParts.push(`\`\`\`\n${trimmed}\n\`\`\``);
157
- }
158
- else {
159
- formattedParts.push(trimmed);
160
- }
161
- }
162
- return formattedParts;
163
- }
164
36
  function getToolDetails(tool, input) {
165
37
  if (!input) {
166
38
  return "";
@@ -1,4 +1,4 @@
1
- import { logger } from "../utils/logger.js";
1
+ import { logger } from "../../utils/logger.js";
2
2
  export class ToolMessageBatcher {
3
3
  sendText;
4
4
  sendFile;
@@ -1,4 +1,4 @@
1
- import { logger } from "../utils/logger.js";
1
+ import { logger } from "../../utils/logger.js";
2
2
  class AssistantRunState {
3
3
  runs = new Map();
4
4
  startRun(sessionId, info) {
@@ -1,4 +1,4 @@
1
- import { logger } from "../utils/logger.js";
1
+ import { logger } from "../../utils/logger.js";
2
2
  class AttachManager {
3
3
  state = null;
4
4
  attach(sessionId, directory) {
@@ -1,5 +1,5 @@
1
- import { isScheduledTaskSessionIgnored } from "../scheduled-task/session-ignore.js";
2
- import { logger } from "../utils/logger.js";
1
+ import { isScheduledTaskSessionIgnored } from "../services/scheduled-task-session-ignore-service.js";
2
+ import { logger } from "../../utils/logger.js";
3
3
  class BackgroundSessionTracker {
4
4
  directory = null;
5
5
  onNotification = null;
@@ -1,4 +1,4 @@
1
- import { logger } from "../utils/logger.js";
1
+ import { logger } from "../../utils/logger.js";
2
2
  class ForegroundSessionState {
3
3
  activeSessions = new Map();
4
4
  markBusy(sessionId, directory) {
@@ -1,4 +1,8 @@
1
- import { logger } from "../utils/logger.js";
1
+ import { permissionManager } from "./permission-manager.js";
2
+ import { questionManager } from "./question-manager.js";
3
+ import { renameManager } from "./rename-manager.js";
4
+ import { taskCreationManager } from "./scheduled-task-creation-manager.js";
5
+ import { logger } from "../../utils/logger.js";
2
6
  export const DEFAULT_ALLOWED_INTERACTION_COMMANDS = ["/help", "/status", "/abort", "/detach"];
3
7
  function normalizeCommand(command) {
4
8
  const trimmed = command.trim().toLowerCase();
@@ -104,3 +108,29 @@ class InteractionManager {
104
108
  }
105
109
  }
106
110
  export const interactionManager = new InteractionManager();
111
+ export function clearAllInteractionState(reason) {
112
+ const questionActive = questionManager.isActive();
113
+ const permissionActive = permissionManager.isActive();
114
+ const renameActive = renameManager.isWaitingForName();
115
+ const taskCreationActive = taskCreationManager.isActive();
116
+ const interactionSnapshot = interactionManager.getSnapshot();
117
+ questionManager.clear();
118
+ permissionManager.clear();
119
+ renameManager.clear();
120
+ taskCreationManager.clear();
121
+ interactionManager.clear(reason);
122
+ const hasAnyActiveState = questionActive ||
123
+ permissionActive ||
124
+ renameActive ||
125
+ taskCreationActive ||
126
+ interactionSnapshot !== null;
127
+ const message = `[InteractionCleanup] Cleared state: reason=${reason}, ` +
128
+ `questionActive=${questionActive}, permissionActive=${permissionActive}, ` +
129
+ `renameActive=${renameActive}, taskCreationActive=${taskCreationActive}, ` +
130
+ `interactionKind=${interactionSnapshot?.kind || "none"}`;
131
+ if (hasAnyActiveState) {
132
+ logger.info(message);
133
+ return;
134
+ }
135
+ logger.debug(message);
136
+ }
@@ -1,4 +1,4 @@
1
- import { logger } from "../utils/logger.js";
1
+ import { logger } from "../../utils/logger.js";
2
2
  class PermissionManager {
3
3
  state = {
4
4
  requestsByMessageId: new Map(),
@@ -1,4 +1,4 @@
1
- import { logger } from "../utils/logger.js";
1
+ import { logger } from "../../utils/logger.js";
2
2
  class QuestionManager {
3
3
  state = {
4
4
  questions: [],
@@ -1,4 +1,4 @@
1
- import { logger } from "../utils/logger.js";
1
+ import { logger } from "../../utils/logger.js";
2
2
  class RenameManager {
3
3
  state = {
4
4
  isWaiting: false,
@@ -1,5 +1,5 @@
1
- import { cloneParsedTaskSchedule, cloneScheduledTaskModel } from "./types.js";
2
- import { logger } from "../utils/logger.js";
1
+ import { cloneParsedTaskSchedule, cloneScheduledTaskModel } from "../types/scheduled-task.js";
2
+ import { logger } from "../../utils/logger.js";
3
3
  function cloneState(state) {
4
4
  return {
5
5
  ...state,
@@ -1,6 +1,6 @@
1
- import { normalizePathForDisplay, prepareCodeFile } from "./formatter.js";
2
- import { logger } from "../utils/logger.js";
3
- import { getCurrentProject } from "../settings/manager.js";
1
+ import { normalizePathForDisplay, prepareCodeFile } from "../formatters/summary-formatter.js";
2
+ import { logger } from "../../utils/logger.js";
3
+ import { getCurrentProject } from "../stores/settings-store.js";
4
4
  function extractFirstUpdatedFileFromTitle(title) {
5
5
  for (const rawLine of title.split("\n")) {
6
6
  const line = rawLine.trim();
@@ -1,8 +1,7 @@
1
- import { opencodeClient } from "../opencode/client.js";
2
- import { getCurrentProject } from "../settings/manager.js";
3
- import { getCurrentSession } from "../session/manager.js";
4
- import { getCurrentAgent, setCurrentAgent } from "../settings/manager.js";
5
- import { logger } from "../utils/logger.js";
1
+ import { opencodeClient } from "../../opencode/client.js";
2
+ import { getCurrentAgent, getCurrentProject, setCurrentAgent } from "../stores/settings-store.js";
3
+ import { getCurrentSession } from "./session-service.js";
4
+ import { logger } from "../../utils/logger.js";
6
5
  /**
7
6
  * Get list of available agents from OpenCode API
8
7
  * @returns Array of available agents (filtered by mode and hidden flag)
@@ -1,17 +1,17 @@
1
- import { opencodeClient } from "../opencode/client.js";
2
- import { isOpencodeServerHealthy } from "../opencode/ready-refresh.js";
3
- import { summaryAggregator } from "../summary/aggregator.js";
4
- import { pinnedMessageManager } from "../pinned/manager.js";
5
- import { keyboardManager } from "../keyboard/manager.js";
6
- import { questionManager } from "../question/manager.js";
7
- import { permissionManager } from "../permission/manager.js";
8
- import { showCurrentQuestion } from "../bot/handlers/question.js";
9
- import { showPermissionRequest } from "../bot/handlers/permission.js";
10
- import { getCurrentSession } from "../session/manager.js";
11
- import { getCurrentProject } from "../settings/manager.js";
12
- import { attachManager } from "./manager.js";
13
- import { logger } from "../utils/logger.js";
14
- import { isExpectedOpencodeUnavailableError } from "../utils/opencode-error.js";
1
+ import { opencodeClient } from "../../opencode/client.js";
2
+ import { isOpencodeServerHealthy } from "../../opencode/ready-refresh.js";
3
+ import { summaryAggregator } from "../managers/summary-aggregation-manager.js";
4
+ import { questionManager } from "../managers/question-manager.js";
5
+ import { permissionManager } from "../managers/permission-manager.js";
6
+ import { getCurrentSession } from "./session-service.js";
7
+ import { getCurrentProject } from "../stores/settings-store.js";
8
+ import { attachManager } from "../managers/attach-manager.js";
9
+ import { logger } from "../../utils/logger.js";
10
+ import { isExpectedOpencodeUnavailableError } from "../../utils/opencode-error.js";
11
+ let attachPresentation = null;
12
+ export function configureAttachPresentation(deps) {
13
+ attachPresentation = deps;
14
+ }
15
15
  function getAttachBusyStatus(sessionId, statuses) {
16
16
  if (!statuses || typeof statuses !== "object") {
17
17
  return false;
@@ -19,36 +19,12 @@ function getAttachBusyStatus(sessionId, statuses) {
19
19
  const sessionStatus = statuses[sessionId];
20
20
  return sessionStatus?.type === "busy";
21
21
  }
22
- async function ensureAttachPinnedSession({ api, chatId, session, forceFullRestore = false, }) {
23
- if (!pinnedMessageManager.isInitialized()) {
24
- pinnedMessageManager.initialize(api, chatId);
25
- }
26
- keyboardManager.initialize(api, chatId);
27
- const pinnedState = pinnedMessageManager.getState();
28
- if (pinnedState.sessionId === session.id && pinnedState.messageId) {
29
- if (forceFullRestore) {
30
- await pinnedMessageManager.loadContextFromHistory(session.id, session.directory);
31
- }
32
- return;
33
- }
34
- if (pinnedState.messageId && pinnedState.sessionId === null) {
35
- await pinnedMessageManager.restoreExistingSession(session.id, session.title);
36
- }
37
- else {
38
- await pinnedMessageManager.onSessionChange(session.id, session.title);
39
- }
40
- await pinnedMessageManager.loadContextFromHistory(session.id, session.directory);
41
- const contextInfo = pinnedMessageManager.getContextInfo();
42
- if (contextInfo) {
43
- keyboardManager.updateContext(contextInfo.tokensUsed, contextInfo.tokensLimit);
44
- }
45
- }
46
22
  async function syncPinnedAttachState() {
47
- if (!pinnedMessageManager.isInitialized()) {
23
+ if (!attachPresentation) {
48
24
  return;
49
25
  }
50
26
  const attached = attachManager.getSnapshot();
51
- await pinnedMessageManager.setAttachState(attached !== null, attached?.busy ?? false);
27
+ await attachPresentation.syncAttachState(attached !== null, attached?.busy ?? false);
52
28
  }
53
29
  async function restorePendingQuestion(bot, chatId, sessionId, directory) {
54
30
  const { data, error } = await opencodeClient.question.list({
@@ -64,11 +40,11 @@ async function restorePendingQuestion(bot, chatId, sessionId, directory) {
64
40
  return false;
65
41
  }
66
42
  const pendingQuestion = data.find((request) => request.sessionID === sessionId);
67
- if (!pendingQuestion) {
43
+ if (!pendingQuestion || !attachPresentation) {
68
44
  return false;
69
45
  }
70
46
  questionManager.startQuestions(pendingQuestion.questions, pendingQuestion.id);
71
- await showCurrentQuestion(bot.api, chatId);
47
+ await attachPresentation.showCurrentQuestion(bot.api, chatId);
72
48
  return true;
73
49
  }
74
50
  async function restorePendingPermissions(bot, chatId, sessionId, directory) {
@@ -85,15 +61,18 @@ async function restorePendingPermissions(bot, chatId, sessionId, directory) {
85
61
  return 0;
86
62
  }
87
63
  const pendingPermissions = data.filter((request) => request.sessionID === sessionId);
64
+ if (!attachPresentation) {
65
+ return 0;
66
+ }
88
67
  for (const request of pendingPermissions) {
89
- await showPermissionRequest(bot.api, chatId, request);
68
+ await attachPresentation.showPermissionRequest(bot.api, chatId, request);
90
69
  }
91
70
  return pendingPermissions.length;
92
71
  }
93
72
  export async function attachToSession(deps) {
94
73
  const { bot, chatId, session, ensureEventSubscription, forceFullRestore = false } = deps;
95
74
  const alreadyAttached = attachManager.isAttachedSession(session.id, session.directory);
96
- await ensureAttachPinnedSession({
75
+ await attachPresentation?.ensurePinnedSession({
97
76
  api: bot.api,
98
77
  chatId,
99
78
  session,
@@ -1,19 +1,22 @@
1
1
  import { opencodeClient } from "../../opencode/client.js";
2
- import { foregroundSessionState, } from "../../scheduled-task/foreground-state.js";
3
- import { scheduledTaskRuntime } from "../../scheduled-task/runtime.js";
4
- import { attachManager } from "../../attach/manager.js";
5
- import { markAttachedSessionBusy, markAttachedSessionIdle } from "../../attach/service.js";
6
- import { assistantRunState } from "../assistant-run-state.js";
7
- import { clearPromptResponseMode } from "../handlers/prompt.js";
2
+ import { foregroundSessionState, } from "../managers/foreground-session-state-manager.js";
3
+ import { scheduledTaskRuntime } from "./scheduled-task-runtime-service.js";
4
+ import { attachManager } from "../managers/attach-manager.js";
5
+ import { markAttachedSessionBusy, markAttachedSessionIdle } from "./attach-service.js";
6
+ import { assistantRunState } from "../managers/assistant-run-state-manager.js";
8
7
  import { logger } from "../../utils/logger.js";
9
8
  const RECONCILE_MIN_INTERVAL_MS = 10_000;
10
9
  const FOREGROUND_BUSY_RECONCILE_GRACE_MS = 2_000;
11
10
  const inFlightDirectories = new Set();
12
11
  const lastReconcileAtByDirectory = new Map();
13
12
  let responseStreamerInstance = null;
13
+ let clearPromptResponseModeForReconciliation = null;
14
14
  export function setResponseStreamerForReconciliation(streamer) {
15
15
  responseStreamerInstance = streamer;
16
16
  }
17
+ export function setPromptResponseModeClearerForReconciliation(clearer) {
18
+ clearPromptResponseModeForReconciliation = clearer;
19
+ }
17
20
  function getReconciliationTargets(directory) {
18
21
  const foregroundBusySessions = foregroundSessionState
19
22
  .getBusySessions()
@@ -38,7 +41,7 @@ function isWithinForegroundBusyGracePeriod(session, now) {
38
41
  async function clearForegroundBusySession(sessionId, reason) {
39
42
  foregroundSessionState.markIdle(sessionId);
40
43
  assistantRunState.clearRun(sessionId, reason);
41
- clearPromptResponseMode(sessionId);
44
+ clearPromptResponseModeForReconciliation?.(sessionId);
42
45
  }
43
46
  export async function reconcileBusyStateNow(directory, now = Date.now()) {
44
47
  if (!directory) {
@@ -118,4 +121,6 @@ export async function reconcileBusyState(directory, now = Date.now()) {
118
121
  export function __resetBusyReconciliationForTests() {
119
122
  inFlightDirectories.clear();
120
123
  lastReconcileAtByDirectory.clear();
124
+ responseStreamerInstance = null;
125
+ clearPromptResponseModeForReconciliation = null;
121
126
  }