feat(telegram-bot): introducing telegram bot - #292
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
skill-check — worker0 verified, 24 skipped (no docs/).
Four for four. Nicely done. |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (29)
📝 WalkthroughWalkthroughAdds a complete Changestelegram-bot Rust Worker
CI, Permissions, and Renaming
Sequence Diagram(s)sequenceDiagram
rect rgba(100, 149, 237, 0.5)
Note over Telegram,ingress: Polling or Webhook Ingress
end
participant Telegram
participant ingress
participant webhook_handle
participant process_update
participant kv
participant harness_client
participant stream_render
participant Telegram2 as Telegram
Telegram->>ingress: getUpdates (polling) / POST webhook
ingress->>webhook_handle: process_update_with_tracing(update)
webhook_handle->>webhook_handle: validate secret / extract chat_id
webhook_handle->>kv: chat_session(chat_id)
webhook_handle->>harness_client: harness::send(HarnessSendRequest)
harness_client-->>webhook_handle: HarnessSendResponse(session_id, turn_id)
webhook_handle->>kv: set_chat_session(chat_id, session_id)
rect rgba(60, 179, 113, 0.5)
Note over harness_client,Telegram2: Async render loop (turn in progress)
end
harness_client-->>stream_render: on-message-added binding
stream_render->>kv: record_order_key / set_entry_message_id
stream_render->>Telegram2: send_message_draft / edit_message_text
harness_client-->>stream_render: on-message-updated (revision++)
stream_render->>Telegram2: edit_message_text (throttled)
harness_client-->>stream_render: on-turn-completed
stream_render->>stream_render: finalize_session
stream_render->>Telegram2: send_rich_message (final)
stream_render->>kv: set_entry_finalized
Estimated code review effort🎯 5 (Critical) | ⏱️ ~150 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 20
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (2)
telegram-bot/src/clients/state.rs-57-61 (1)
57-61:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUse checked conversion for
u64 -> i64inget_i64(Line 60).
n as i64can wrap large unsigned values into negative IDs. Usei64::try_fromso out-of-range data becomesNoneinstead of corrupted values.Proposed fix
- Ok(v) if v.is_u64() => v.as_u64().map(|n| n as i64), + Ok(v) if v.is_u64() => v.as_u64().and_then(|n| i64::try_from(n).ok()),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@telegram-bot/src/clients/state.rs` around lines 57 - 61, In the get_i64 function, the unsafe cast from u64 to i64 on line 60 using `n as i64` can cause large values to wrap into negative numbers. Replace the unchecked cast `n as i64` with a checked conversion using `i64::try_from(n).ok()` to safely handle out-of-range values by returning None instead of corrupted negative IDs.telegram-bot/src/clients/telegram.rs-197-204 (1)
197-204:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd
/settingsto the registered Telegram command menu.The PR objectives list
/settingsas a supported command, butsetMyCommandsomits it, so Telegram clients will not show it in the command picker.🧩 Proposed fix
{ "command": "thinking", "description": "Set reasoning depth" }, { "command": "verbosity", "description": "Set transcript verbosity" }, + { "command": "settings", "description": "Show current bot settings" }, ]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@telegram-bot/src/clients/telegram.rs` around lines 197 - 204, The `commands` JSON array does not include the `/settings` command even though it is listed in the PR objectives and should be available to users through the Telegram command menu. Add a new command entry to the json! array with command set to "settings" and an appropriate description explaining what the settings command does, placing it among the other command definitions to ensure it gets registered with Telegram's setMyCommands method.
🧹 Nitpick comments (1)
telegram-bot/tests/integration.rs (1)
84-90: ⚡ Quick winAvoid passing the test when registration is missing after successful boot.
Once engine and worker have started, this path should fail, not skip; otherwise registration regressions become false-green CI results.
Suggested fix
if !registered { - eprintln!( - "skipping: telegram-bot did not register (configuration worker or bot_token required)" - ); client.shutdown_async().await; - return; + panic!("telegram-bot::webhook was not registered on engine"); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@telegram-bot/tests/integration.rs` around lines 84 - 90, The test currently skips when the telegram-bot fails to register after successful boot, which allows registration regressions to pass silently. In the conditional block checking `if !registered`, replace the eprintln call and early return with an assertion that fails the test (such as using assert! or panic!) to ensure registration failures are caught as actual test failures rather than skipped tests, preventing false-green CI results.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@telegram-bot/config.yaml`:
- Around line 2-3: In the config.yaml file, the reminder worker entry (lines
2-3) uses a hardcoded absolute machine-specific path that is not portable.
Replace the current reminder worker name and worker_path with a
telegram-bot-compatible configuration that uses relative paths or proper
environment-agnostic references instead of the absolute user-specific path,
ensuring the configuration will work consistently across different developer
machines and CI environments.
In `@telegram-bot/src/clients/harness.rs`:
- Around line 118-130: The status_active function currently returns false when
the trigger request fails, which masks temporary unavailability as an inactive
session. Change the return type from bool to Result to propagate errors instead
of silently converting them to false in the Err(_) branch, then update the
webhook callsite that invokes status_active to explicitly handle the error case
rather than treating it as an inactive session.
In `@telegram-bot/src/clients/telegram.rs`:
- Around line 288-297: The HTTP error at the map_err call in the POST request
handler is leaking the bot token because reqwest::Error's Display implementation
includes the full URL. Call without_url() on the error object before formatting
it into the IIIError message to remove the URL and prevent token leakage in logs
while preserving the actual error details.
In `@telegram-bot/src/config.rs`:
- Around line 186-188: The environment variable placeholder documentation in the
bot_token field doc comment uses the format ${TELEGRAM_BOT_TOKEN:} but the
parser treats everything inside ${...} as the literal variable name, so it looks
up TELEGRAM_BOT_TOKEN: (including the colon) rather than just
TELEGRAM_BOT_TOKEN. Update the doc comment for bot_token to use the correct
format ${TELEGRAM_BOT_TOKEN} without the trailing colon, and apply the same
correction to all similar doc comments mentioned in lines 411-421 to ensure
documentation consistently matches what the parser actually resolves.
- Around line 344-349: The deserialize_optional_u64 function uses a redundant
Ok(...?) pattern that triggers clippy::needless_question_mark. Remove the Ok()
wrapper and the ? operator, and directly return the result from
Option::<u64>::deserialize(deserializer) since it already returns the correct
Result type that matches the function's return type.
In `@telegram-bot/src/deps.rs`:
- Around line 218-223: The chat reset logic clears session state from multiple
maps (stream_sessions, pending_entries, finalized_entries, entry_locks,
typing_tasks) but fails to clear the revisions map which is also keyed by
session ID. Add a retain or remove call on self.revisions to clear entries for
the old_session_id (similar to the pattern used for the other maps), ensuring
that the revisions map is cleaned up along with the other session state to
prevent unbounded memory growth on repeated resets.
In `@telegram-bot/src/functions/bindings/pending_created.rs`:
- Around line 80-85: The truncate function performs unsafe string slicing at
position `max` without checking if it's a valid UTF-8 character boundary,
causing panics when truncating user-provided strings that contain multibyte
UTF-8 characters like emoji or non-ASCII. Modify the else branch of the truncate
function to check if the position at `max` is a valid char boundary using Rust's
`is_char_boundary()` method. If it's not at a boundary, iterate backwards from
`max` to find the nearest valid char boundary before slicing, ensuring the
function safely truncates any UTF-8 string without panicking.
In `@telegram-bot/src/functions/bindings/turn_completed.rs`:
- Around line 82-112: The dequeue operation in the fifo_queues handling (where
q.remove(0) is called) happens before validating that the model lookup and
message send will succeed. This causes messages to be lost if either the
kv::chat_model call returns None or the webhook::send_user_message call fails.
Defer the removal of the item from the queue by first checking that the model
exists and the message sends successfully, and only then remove it from the
queue. Reorganize the logic to peek at the queue item first, validate all
prerequisites including model lookup and message sending, and only remove the
item from the fifo_queues after confirming success.
In `@telegram-bot/src/functions/webhook.rs`:
- Around line 623-635: The `header_matches` function currently checks only two
hardcoded case variations of the header name, but HTTP header names are
case-insensitive. Instead of checking specific casings in a loop, iterate
through all keys in the map and compare them case-insensitively by converting
both the map key and the target header name to lowercase, then check if the
corresponding value matches the secret. This ensures the function will accept
the header regardless of how proxies or runtimes normalize the header name
casing.
- Around line 37-42: The webhook validation logic in the UpdatesAdapter::Webhook
block currently permits requests to pass through when no secret is configured,
creating a security vulnerability by accepting unauthenticated requests. Modify
the logic to require that a webhook secret must be present for authentication;
if the secret is None (when webhook.secret.as_deref() would return None), return
an IIIError immediately instead of allowing the request to proceed. This
implements fail-closed behavior where webhook requests are rejected unless a
valid secret is configured and matches the provided header.
In `@telegram-bot/src/ingress.rs`:
- Around line 107-131: The success case for the telegram::get_updates call does
not check for cancellation while awaiting the result, which can block graceful
shutdown for up to 50 seconds. Refactor the match statement that calls
telegram::get_updates to use tokio::select! to simultaneously await both the
get_updates future and check cancel.cancelled(), similar to the pattern already
implemented in the error case with the sleep call. This will allow immediate
cancellation detection even while get_updates is in flight rather than only
checking at the start of each loop iteration.
In `@telegram-bot/src/kv.rs`:
- Around line 182-214: The functions clear_chat_session and clear_chat_model are
ignoring errors returned by state::delete calls using the let _ = pattern, which
causes these functions to report success even when the deletion operations fail
and stale KV values remain. Replace the let _ = state::delete(...).await;
statements with proper error handling by using the ? operator to propagate any
errors returned by state::delete, ensuring that if deletion fails the function
returns an error instead of Ok.
- Around line 15-33: The set_chat_session function performs two separate
state::set calls to create bidirectional mappings between chat and session. If
the second state::set call fails, the first mapping persists while the second is
missing, creating inconsistent state. Add error handling to implement
compensating rollback: if the second state::set call (the one with
"session:{session_id}:chat" key) fails, delete the first key that was
successfully written (the "chat:{chat_id}:session" key) before returning the
error, ensuring both keys are either present together or both absent.
In `@telegram-bot/src/render/stream.rs`:
- Around line 1278-1288: The DraftFinalizeStep enum and DRAFT_FINALIZE_STEPS
constant are stale test helpers that do not reflect the actual implementation
order. The implementation posts the answer before clearing the draft, but the
current enum variants and constant still encode "clear before answer" first.
Either remove these test helpers entirely if they are no longer used, or update
the DraftFinalizeStep variant names and reorder the DRAFT_FINALIZE_STEPS
constant to match the actual implementation flow (clear draft, post answer,
clear draft after). Apply the same fix to the similar code referenced at lines
1681-1692.
- Around line 704-735: The code currently only edits the first chunk message and
resends all subsequent chunks as new messages on each update, causing duplicate
continuation bubbles. In the block where message_id exists, the for loop that
sends continuation chunks via send_message_formatted needs to track the
resulting message IDs per entry. Either persist these continuation chunk message
IDs (similar to how the first chunk's id is tracked) so they can be edited in
subsequent updates, or defer sending continuation chunks until finalization to
ensure each chunk is sent exactly once instead of being re-sent on every
revision pass.
- Around line 1076-1082: The `clear_pending_materialization` function is only
invoked when `result.is_ok()` is true, meaning failed Telegram sends leave the
entry stuck in `chat_pending_materialization` and block subsequent operations.
Move the `clear_pending_materialization(&deps.runtime, chat_id, entry_id)` call
outside the success condition block so it executes on both success and failure,
preventing the pending materialization from blocking later entries with higher
order keys. Ensure the function is called unconditionally after the
`make().await` operation regardless of the result status.
- Around line 1030-1039: The send_chat_message_in_order function exceeds the
Clippy limit for function parameters with 8 arguments. To fix this, create a
struct to group the related message parameters (such as chunk_idx, text,
entry_id, and reply_markup) into a single MessageData or similar struct, then
pass this struct as a parameter to reduce the argument count below the
threshold. Alternatively, if the public API signature is intentionally required
as-is, add the #[allow(clippy::too_many_arguments)] attribute above the function
signature with a comment explaining why the current shape is necessary.
In `@telegram-bot/src/render/typing.rs`:
- Around line 115-159: The issue is that when a typing task's cancellation token
is triggered, the cleanup code unconditionally removes the session_id from
typing_tasks, which can delete a newer typing task's cancellation token if one
was inserted while the old task was still running. To fix this, modify all the
cleanup operations (in the handle_typing_task_async function) to only remove the
session_id from typing_tasks if the stored token matches the current
cancellation token being cleaned up. This prevents stale tasks from deleting
newer tokens that should remain active for subsequent stop_typing calls.
- Around line 220-224: The code is triggering a Clippy lint violation by
reassigning the `streaming` field after creating `WorkerConfig::default()`.
Instead of creating the default `WorkerConfig` and then modifying its
`streaming` field, construct the `WorkerConfig` with the `streaming` field
already set during initialization using struct update syntax. Create the
`StreamingConfig` with the `transport` value inline and pass it directly when
building the `WorkerConfig` to avoid the post-default field reassignment.
In `@telegram-bot/src/render/verbosity.rs`:
- Around line 132-138: The truncate_json function uses byte slicing with
&s[..max] which can panic when max lands in the middle of a multibyte UTF-8
character. Replace the byte slicing approach with a UTF-8 safe method that finds
the nearest valid character boundary at or before the max position. Consider
using Rust's built-in char methods or string iteration to safely truncate at a
valid UTF-8 boundary before appending the ellipsis.
---
Minor comments:
In `@telegram-bot/src/clients/state.rs`:
- Around line 57-61: In the get_i64 function, the unsafe cast from u64 to i64 on
line 60 using `n as i64` can cause large values to wrap into negative numbers.
Replace the unchecked cast `n as i64` with a checked conversion using
`i64::try_from(n).ok()` to safely handle out-of-range values by returning None
instead of corrupted negative IDs.
In `@telegram-bot/src/clients/telegram.rs`:
- Around line 197-204: The `commands` JSON array does not include the
`/settings` command even though it is listed in the PR objectives and should be
available to users through the Telegram command menu. Add a new command entry to
the json! array with command set to "settings" and an appropriate description
explaining what the settings command does, placing it among the other command
definitions to ensure it gets registered with Telegram's setMyCommands method.
---
Nitpick comments:
In `@telegram-bot/tests/integration.rs`:
- Around line 84-90: The test currently skips when the telegram-bot fails to
register after successful boot, which allows registration regressions to pass
silently. In the conditional block checking `if !registered`, replace the
eprintln call and early return with an assertion that fails the test (such as
using assert! or panic!) to ensure registration failures are caught as actual
test failures rather than skipped tests, preventing false-green CI results.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0e89b4ea-60a2-451a-8af7-a05a2b6023e5
⛔ Files ignored due to path filters (2)
telegram-bot/.iii-worker.lockis excluded by!**/*.locktelegram-bot/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (65)
.github/workflows/create-tag.yml.github/workflows/release.ymlREADME.mdharness/architecture/integration.mdharness/skills/SKILL.mdiii-permissions.yamltech-specs/2026-06-agentic/README.mdtech-specs/2026-06-agentic/presentation/src/components/diagrams/SystemMap.tsxtech-specs/2026-06-agentic/presentation/src/content/workers.tstech-specs/2026-06-agentic/presentation/src/pages/TelegramPage.tsxtelegram-bot/Cargo.tomltelegram-bot/README.mdtelegram-bot/build.rstelegram-bot/config.yamltelegram-bot/iii.worker.yamltelegram-bot/skills/SKILL.mdtelegram-bot/src/clients/approval.rstelegram-bot/src/clients/harness.rstelegram-bot/src/clients/mod.rstelegram-bot/src/clients/router.rstelegram-bot/src/clients/state.rstelegram-bot/src/clients/telegram.rstelegram-bot/src/config.rstelegram-bot/src/configuration.rstelegram-bot/src/deps.rstelegram-bot/src/functions/bindings/message_added.rstelegram-bot/src/functions/bindings/message_updated.rstelegram-bot/src/functions/bindings/mod.rstelegram-bot/src/functions/bindings/pending_created.rstelegram-bot/src/functions/bindings/pending_resolved.rstelegram-bot/src/functions/bindings/status_changed.rstelegram-bot/src/functions/bindings/turn_completed.rstelegram-bot/src/functions/mod.rstelegram-bot/src/functions/set_webhook.rstelegram-bot/src/functions/webhook.rstelegram-bot/src/ingress.rstelegram-bot/src/kv.rstelegram-bot/src/lib.rstelegram-bot/src/main.rstelegram-bot/src/manifest.rstelegram-bot/src/preferences.rstelegram-bot/src/render/chunk.rstelegram-bot/src/render/format.rstelegram-bot/src/render/mod.rstelegram-bot/src/render/stream.rstelegram-bot/src/render/throttle.rstelegram-bot/src/render/typing.rstelegram-bot/src/render/verbosity.rstelegram-bot/src/surface.rstelegram-bot/src/telemetry.rstelegram-bot/src/types.rstelegram-bot/tests/fixtures/config.yamltelegram-bot/tests/golden/schemas/telegram-bot.on-config-change.jsontelegram-bot/tests/golden/schemas/telegram-bot.on-message-added.jsontelegram-bot/tests/golden/schemas/telegram-bot.on-message-updated.jsontelegram-bot/tests/golden/schemas/telegram-bot.on-pending-created.jsontelegram-bot/tests/golden/schemas/telegram-bot.on-pending-resolved.jsontelegram-bot/tests/golden/schemas/telegram-bot.on-status-changed.jsontelegram-bot/tests/golden/schemas/telegram-bot.on-turn-completed.jsontelegram-bot/tests/golden/schemas/telegram-bot.set-webhook.jsontelegram-bot/tests/golden/schemas/telegram-bot.webhook.jsontelegram-bot/tests/integration.rstelegram-bot/tests/manifest.rstelegram-bot/tests/schemas.rstelegram-bot/tests/support/mod.rs
💤 Files with no reviewable changes (1)
- harness/architecture/integration.md
Summary
Adds telegram-bot, a Telegram surface for the harness agent stack. Inbound chat messages become
harness::sendturns; assistant output streams back as live Telegram edits, with slash commands, model pickers, and inline tool-call approvals.The worker owns Telegram UX only — turn execution, streaming, and durability stay in
harness,session-manager, andapproval-gate.What it does
/start,/stop,/model,/help,/thinking,/verbosity,/settingseditMessageTextfallbackapproval-gatetelegram-botconfiguration (bot token, model, verbosity,functions_allow, etc.)Prerequisites
Test plan
/startclears session and shows model pickerSummary by CodeRabbit
New Features
Documentation