@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.
- package/.env.example +18 -2
- package/README.md +21 -8
- package/dist/app/{start-bot-app.js → bootstrap/start-bot-app.js} +16 -15
- package/dist/{bot/utils/assistant-run-footer.js → app/formatters/assistant-run-footer-formatter.js} +1 -1
- package/dist/{summary → app/formatters}/subagent-formatter.js +8 -3
- package/dist/{summary/formatter.js → app/formatters/summary-formatter.js} +4 -132
- package/dist/{summary → app/formatters}/tool-message-batcher.js +1 -1
- package/dist/{bot/assistant-run-state.js → app/managers/assistant-run-state-manager.js} +1 -1
- package/dist/{attach/manager.js → app/managers/attach-manager.js} +1 -1
- package/dist/{background-session/tracker.js → app/managers/background-session-manager.js} +2 -2
- package/dist/{scheduled-task/foreground-state.js → app/managers/foreground-session-state-manager.js} +1 -1
- package/dist/{interaction/manager.js → app/managers/interaction-manager.js} +31 -1
- package/dist/{permission/manager.js → app/managers/permission-manager.js} +1 -1
- package/dist/{question/manager.js → app/managers/question-manager.js} +1 -1
- package/dist/{rename/manager.js → app/managers/rename-manager.js} +1 -1
- package/dist/{scheduled-task/creation-manager.js → app/managers/scheduled-task-creation-manager.js} +2 -2
- package/dist/{summary/aggregator.js → app/managers/summary-aggregation-manager.js} +3 -3
- package/dist/{agent/manager.js → app/services/agent-selection-service.js} +4 -5
- package/dist/{attach/service.js → app/services/attach-service.js} +23 -44
- package/dist/{bot/utils/busy-reconciliation.js → app/services/busy-reconciliation-service.js} +12 -7
- package/dist/app/services/command-catalog-service.js +21 -0
- package/dist/{bot/utils/external-user-input.js → app/services/external-user-input-service.js} +4 -19
- package/dist/app/services/file-browser-service.js +265 -0
- package/dist/{bot/utils/file-download.js → app/services/file-download-service.js} +14 -28
- package/dist/app/services/mcp-catalog-service.js +79 -0
- package/dist/app/services/message-history-service.js +81 -0
- package/dist/{model/capabilities.js → app/services/model-capabilities-service.js} +2 -2
- package/dist/{model/context-limit.js → app/services/model-context-limit-service.js} +4 -4
- package/dist/{model/manager.js → app/services/model-selection-service.js} +4 -4
- package/dist/{project/manager.js → app/services/project-service.js} +3 -3
- package/dist/app/services/project-switch-service.js +39 -0
- package/dist/{bot/utils/busy-guard.js → app/services/run-control-service.js} +3 -14
- package/dist/{scheduled-task/executor.js → app/services/scheduled-task-executor-service.js} +5 -5
- package/dist/{scheduled-task/runtime.js → app/services/scheduled-task-runtime-service.js} +17 -56
- package/dist/{scheduled-task/schedule-parser.js → app/services/scheduled-task-schedule-parser-service.js} +3 -3
- package/dist/{scheduled-task/session-ignore.js → app/services/scheduled-task-session-ignore-service.js} +1 -1
- package/dist/{session/cache-manager.js → app/services/session-cache-service.js} +3 -3
- package/dist/{session/manager.js → app/services/session-service.js} +1 -1
- package/dist/app/services/skills-catalog-service.js +21 -0
- package/dist/{stt/client.js → app/services/stt-service.js} +2 -2
- package/dist/{tts/client.js → app/services/tts-service.js} +68 -5
- package/dist/{variant/manager.js → app/services/variant-selection-service.js} +3 -3
- package/dist/{scheduled-task/store.js → app/stores/scheduled-task-store.js} +3 -3
- package/dist/{settings/manager.js → app/stores/settings-store.js} +3 -3
- package/dist/app/types/worktree.js +1 -0
- package/dist/bot/{handlers/agent.js → callbacks/agent-selection-callback-handler.js} +8 -57
- package/dist/bot/callbacks/callback-router.js +110 -0
- package/dist/bot/{commands/commands.js → callbacks/command-catalog-callback-handler.js} +17 -197
- package/dist/bot/{handlers/context.js → callbacks/context-control-callback-handler.js} +3 -32
- package/dist/bot/callbacks/file-browser-callback-handler.js +219 -0
- package/dist/bot/callbacks/inline-menu-cancel-callback-handler.js +32 -0
- package/dist/bot/{commands/mcps.js → callbacks/mcp-catalog-callback-handler.js} +7 -172
- package/dist/bot/{commands/messages.js → callbacks/message-history-callback-handler.js} +17 -248
- package/dist/bot/{handlers/model.js → callbacks/model-selection-callback-handler.js} +10 -71
- package/dist/bot/callbacks/permission-callback-handler.js +114 -0
- package/dist/bot/callbacks/project-callback-handler.js +79 -0
- package/dist/bot/callbacks/question-callback-handler.js +159 -0
- package/dist/bot/{commands/rename.js → callbacks/rename-callback-handler.js} +6 -34
- package/dist/bot/{commands/tasklist.js → callbacks/scheduled-task-callback-handler.js} +83 -68
- package/dist/bot/{commands/sessions.js → callbacks/session-callback-handler.js} +19 -158
- package/dist/bot/{commands/skills.js → callbacks/skills-catalog-callback-handler.js} +8 -188
- package/dist/bot/{handlers/variant.js → callbacks/variant-selection-callback-handler.js} +7 -69
- package/dist/bot/callbacks/worktree-callback-handler.js +97 -0
- package/dist/bot/commands/{abort.js → abort-command.js} +6 -6
- package/dist/bot/commands/command-catalog-command.js +42 -0
- package/dist/bot/commands/{detach.js → detach-command.js} +8 -8
- package/dist/bot/commands/ls-command.js +48 -0
- package/dist/bot/commands/mcp-catalog-command.js +39 -0
- package/dist/bot/commands/messages-command.js +59 -0
- package/dist/bot/commands/{new.js → new-command.js} +12 -11
- package/dist/bot/commands/open-command.js +45 -0
- package/dist/bot/commands/{opencode-start.js → opencode-start-command.js} +1 -1
- package/dist/bot/commands/{opencode-stop.js → opencode-stop-command.js} +1 -1
- package/dist/bot/commands/projects-command.js +32 -0
- package/dist/bot/commands/rename-command.js +33 -0
- package/dist/bot/commands/sessions-command.js +42 -0
- package/dist/bot/commands/skills-catalog-command.js +42 -0
- package/dist/bot/commands/{start.js → start-command.js} +12 -12
- package/dist/bot/commands/{status.js → status-command.js} +9 -9
- package/dist/bot/commands/{task.js → task-command.js} +9 -81
- package/dist/bot/commands/tasklist-command.js +40 -0
- package/dist/bot/commands/{tts.js → tts-command.js} +2 -2
- package/dist/bot/commands/worktree-command.js +47 -0
- package/dist/bot/handlers/{document.js → document-handler.js} +3 -3
- package/dist/bot/handlers/{media-group.js → media-group-handler.js} +3 -3
- package/dist/bot/handlers/photo-handler.js +44 -0
- package/dist/bot/handlers/prompt.js +16 -16
- package/dist/bot/handlers/text-message-handler.js +61 -0
- package/dist/bot/handlers/tts-response-handler.js +29 -0
- package/dist/bot/handlers/{voice.js → voice-handler.js} +2 -2
- package/dist/bot/index.js +27 -1130
- package/dist/{keyboard/manager.js → bot/keyboards/keyboard-manager.js} +6 -6
- package/dist/bot/keyboards/keyboard-types.js +1 -0
- package/dist/bot/{utils/keyboard.js → keyboards/main-reply-keyboard.js} +2 -2
- package/dist/bot/menus/agent-selection-menu.js +54 -0
- package/dist/bot/menus/command-catalog-menu.js +101 -0
- package/dist/bot/menus/context-control-menu.js +33 -0
- package/dist/bot/menus/file-browser-menu.js +265 -0
- package/dist/bot/{handlers → menus}/inline-menu.js +4 -33
- package/dist/bot/menus/mcp-catalog-menu.js +97 -0
- package/dist/bot/menus/message-history-menu.js +107 -0
- package/dist/bot/menus/model-selection-menu.js +66 -0
- package/dist/bot/menus/permission-menu.js +116 -0
- package/dist/bot/{commands/projects.js → menus/project-selection-menu.js} +5 -105
- package/dist/bot/{handlers/question.js → menus/question-menu.js} +10 -166
- package/dist/bot/menus/rename-menu.js +6 -0
- package/dist/bot/menus/scheduled-task-menu.js +42 -0
- package/dist/bot/menus/session-selection-menu.js +115 -0
- package/dist/bot/menus/skills-catalog-menu.js +99 -0
- package/dist/bot/menus/variant-selection-menu.js +67 -0
- package/dist/bot/menus/worktree-selection-menu.js +76 -0
- package/dist/bot/{utils → messages}/assistant-rendering.js +2 -2
- package/dist/bot/messages/busy-blocked-renderer.js +11 -0
- package/dist/bot/messages/external-user-input-notification.js +22 -0
- package/dist/bot/messages/scheduled-task-delivery.js +63 -0
- package/dist/bot/{utils → messages}/send-downloaded-file.js +1 -1
- package/dist/bot/messages/summary-message-formatter.js +130 -0
- package/dist/bot/{utils → messages}/telegram-text.js +60 -0
- package/dist/{interaction/guard.js → bot/middleware/interaction-guard-decision.js} +11 -4
- package/dist/bot/middleware/interaction-guard.js +2 -2
- package/dist/bot/middleware/unknown-command.js +1 -1
- package/dist/{pinned/format.js → bot/pinned/pinned-message-format.js} +1 -2
- package/dist/{pinned/manager.js → bot/pinned/pinned-message-manager.js} +10 -10
- package/dist/bot/pinned/pinned-message-types.js +1 -0
- package/dist/{summary → bot/render}/markdown-to-telegram-v2.js +2 -2
- package/dist/bot/render/types.js +1 -0
- package/dist/bot/routers/command-router.js +73 -0
- package/dist/bot/routers/message-router.js +150 -0
- package/dist/bot/services/attach-presentation.js +40 -0
- package/dist/bot/services/event-subscription-service.js +842 -0
- package/dist/bot/services/project-switch-presentation.js +26 -0
- package/dist/bot/streaming/response-streamer.js +18 -2
- package/dist/cli.js +4 -4
- package/dist/config.js +21 -3
- package/dist/i18n/ar.js +491 -0
- package/dist/i18n/index.js +7 -0
- package/dist/index.js +1 -1
- package/dist/opencode/ready-refresh.js +2 -2
- package/dist/{service → runtime/service}/manager.js +2 -2
- package/dist/runtime/service/types.js +1 -0
- package/package.json +1 -1
- package/dist/bot/commands/ls.js +0 -445
- package/dist/bot/commands/open.js +0 -319
- package/dist/bot/commands/worktree.js +0 -201
- package/dist/bot/handlers/permission.js +0 -235
- package/dist/bot/utils/browser-roots.js +0 -140
- package/dist/bot/utils/file-tree.js +0 -92
- package/dist/bot/utils/send-tts-response.js +0 -37
- package/dist/bot/utils/switch-project.js +0 -45
- package/dist/bot/utils/telegram-file-url.js +0 -11
- package/dist/interaction/busy.js +0 -8
- package/dist/interaction/cleanup.js +0 -32
- /package/dist/{scheduled-task/display.js → app/formatters/scheduled-task-display-formatter.js} +0 -0
- /package/dist/{bot/utils/abort-error-suppression.js → app/managers/abort-suppression-manager.js} +0 -0
- /package/dist/{external-input/suppression.js → app/managers/external-input-suppression-manager.js} +0 -0
- /package/dist/{scheduled-task/next-run.js → app/services/scheduled-task-next-run-service.js} +0 -0
- /package/dist/{git/worktree.js → app/services/worktree-service.js} +0 -0
- /package/dist/{agent/types.js → app/types/agent.js} +0 -0
- /package/dist/{interaction/types.js → app/types/interaction.js} +0 -0
- /package/dist/{model/types.js → app/types/model.js} +0 -0
- /package/dist/{keyboard/types.js → app/types/permission.js} +0 -0
- /package/dist/{permission/types.js → app/types/project.js} +0 -0
- /package/dist/{pinned/types.js → app/types/question.js} +0 -0
- /package/dist/{scheduled-task/types.js → app/types/scheduled-task.js} +0 -0
- /package/dist/{question/types.js → app/types/session.js} +0 -0
- /package/dist/{service/types.js → app/types/settings.js} +0 -0
- /package/dist/{telegram/render/types.js → app/types/summary.js} +0 -0
- /package/dist/{variant/types.js → app/types/variant.js} +0 -0
- /package/dist/bot/commands/{help.js → help-command.js} +0 -0
- /package/dist/bot/{utils → messages}/send-with-markdown-fallback.js +0 -0
- /package/dist/bot/{utils → messages}/thinking-message.js +0 -0
- /package/dist/{telegram → bot}/render/block-fallback.js +0 -0
- /package/dist/{telegram → bot}/render/block-parser.js +0 -0
- /package/dist/{telegram → bot}/render/block-renderer.js +0 -0
- /package/dist/{telegram → bot}/render/chunker.js +0 -0
- /package/dist/{telegram → bot}/render/inline-renderer.js +0 -0
- /package/dist/{telegram → bot}/render/markdown-normalizer.js +0 -0
- /package/dist/{telegram → bot}/render/pipeline.js +0 -0
- /package/dist/{telegram → bot}/render/validator.js +0 -0
- /package/dist/bot/{utils/commands.js → routers/command-utils.js} +0 -0
- /package/dist/bot/{utils → streaming}/finalize-assistant-response.js +0 -0
- /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`)
|
|
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
|
|
243
|
-
| `TTS_API_URL` |
|
|
244
|
-
| `TTS_API_KEY` | OpenAI-compatible
|
|
245
|
-
| `TTS_MODEL` |
|
|
246
|
-
| `TTS_VOICE` | TTS voice name. Defaults to `alloy`
|
|
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 "
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
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("
|
|
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 () => {
|
package/dist/{bot/utils/assistant-run-footer.js → app/formatters/assistant-run-footer-formatter.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getAgentDisplayName } from "
|
|
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 {
|
|
2
|
-
import {
|
|
3
|
-
|
|
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 "
|
|
3
|
-
import { logger } from "
|
|
4
|
-
import { t } from "
|
|
5
|
-
import { getCurrentProject } from "../settings
|
|
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,5 +1,5 @@
|
|
|
1
|
-
import { isScheduledTaskSessionIgnored } from "../scheduled-task
|
|
2
|
-
import { logger } from "
|
|
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,8 @@
|
|
|
1
|
-
import {
|
|
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
|
+
}
|
package/dist/{scheduled-task/creation-manager.js → app/managers/scheduled-task-creation-manager.js}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { cloneParsedTaskSchedule, cloneScheduledTaskModel } from "
|
|
2
|
-
import { logger } from "
|
|
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 "
|
|
2
|
-
import { logger } from "
|
|
3
|
-
import { getCurrentProject } from "../settings
|
|
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 "
|
|
2
|
-
import { getCurrentProject } from "../settings
|
|
3
|
-
import { getCurrentSession } from "
|
|
4
|
-
import {
|
|
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 "
|
|
2
|
-
import { isOpencodeServerHealthy } from "
|
|
3
|
-
import { summaryAggregator } from "../summary
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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 (!
|
|
23
|
+
if (!attachPresentation) {
|
|
48
24
|
return;
|
|
49
25
|
}
|
|
50
26
|
const attached = attachManager.getSnapshot();
|
|
51
|
-
await
|
|
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
|
|
75
|
+
await attachPresentation?.ensurePinnedSession({
|
|
97
76
|
api: bot.api,
|
|
98
77
|
chatId,
|
|
99
78
|
session,
|
package/dist/{bot/utils/busy-reconciliation.js → app/services/busy-reconciliation-service.js}
RENAMED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { opencodeClient } from "../../opencode/client.js";
|
|
2
|
-
import { foregroundSessionState, } from "
|
|
3
|
-
import { scheduledTaskRuntime } from "
|
|
4
|
-
import { attachManager } from "
|
|
5
|
-
import { markAttachedSessionBusy, markAttachedSessionIdle } from "
|
|
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
|
-
|
|
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
|
}
|