Skip to content

feat: add Telegram remote control - #23

Open
sambitcreate wants to merge 15 commits into
mainfrom
feature/text-control
Open

feat: add Telegram remote control#23
sambitcreate wants to merge 15 commits into
mainfrom
feature/text-control

Conversation

@sambitcreate

@sambitcreate sambitcreate commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add owner-paired Telegram long-polling remote control with queued, Markdown-formatted replies.
  • Add Settings, IPC, encrypted token storage, provider/model selection, optional selected-folder workspace automation, and onboarding coverage.
  • Snapshot selected workspace authority when each prompt is accepted; strict /workspace name selection preserves internal whitespace and rejects case mismatches.

Verification

  • npm run type-check
  • npm run test:telegram — 67 passing
  • npm run lint
  • npm run build

Known repository check

  • npm test currently stops in pretest because the unrelated main/services/terminal.ts coverage gate reports 79.83% branches against an 80% threshold. This branch does not change that file.

Vendor pi-telegram's TypeScript Telegram adapter into Aiden as an
always-on main-process service, backed by the existing headless-turn
seam in schedule-execution.ts. Full-unattended single-owner model with
one persistent chat; MIT-sourced, ported with attribution. Adds the
plan doc and a row in the plans index.
Implement the Telegram Remote Control plan (Phases 0–5). A paired Telegram
owner can send messages to an Aiden bot and receive headless, full-authority
responses — the same trust boundary as scheduled tasks.

What ships:
- main/services/telegram/: Bot API client (long-polling transport), Markdown
  → HTML converter with 4096-char chunking, control/priority/default queue
  with dispatch gates, headless turn injection shim (mirrors
  schedule-execution.ts createBackgroundOwner + llmClient.start), config
  store (lastUpdateId via DataStore), and service core singleton with
  start/stop/stopAndSettle lifecycle.
- main/handlers/telegram.ts: telegram:get/setKey/setEnabled/connect/
  disconnect/resetPairing IPC handlers.
- main/index.ts: service started in whenReady, stopped in cleanupApplication
  and shutdownAndQuit.
- renderer/components/settings/telegram-settings.tsx: enable toggle, bot
  token input (safeStorage/Keychain), connection status, pairing controls,
  security notice.
- AppSettings: telegramEnabled, telegramAllowedUserId.
- UsageRequestSource: telegram.
- MIT attribution to pi-telegram / badlogic/pi-telegram.
- 50 tests (bot API 10, turn injection 9, queue 10, markdown 10, service
  core 11), all passing.

Phase 6 (onboarding bento tile) deferred pending a 1024×1024 PNG asset.

Design reference: pi-telegram (https://github.com/llblab/pi-telegram, MIT).
…r, link escape

Post-review corrections from GPT-5.6 correctness audit:

MUST-FIX:
- Fix persisted polling offset off-by-one: persist update_id+1 (the resume
  offset) instead of raw update_id. Prevents duplicate processing on restart.
- Fix /stop corrupting dispatch state: stop() no longer resets activeTurn
  mid-turn (the dispatchTurn finally block owns it). /stop now clears the
  queue and honestly reports that the in-flight turn continues.

SHOULD-FIX:
- Fix typing indicator never firing: set activeTurn=true BEFORE calling
  sendTypingIndicator so the loop condition evaluates correctly.
- Fix settleAsyncWork not in a finally: wrap appendMessage in try/finally
  matching the proven schedule-execution.ts pattern.
- Fix offset persisted on handleUpdate failure: only persist after success.
- Fix link URL attribute injection: escape quotes in href values.
- Fix chunker splitting inside <pre> tags: balancePreTags closes/reopens
  <pre> at chunk boundaries.
- Fix getStatus() hardcoded values: remove dead enabled/hasToken/allowedUserId
  fields; derive status from actual started/lastError state.
- Fix polling indicator lying on error: status reflects actual running state.

NIT:
- Remove dead '&gt; ' blockquote branch (escaping happens later).
- Fix fence-close heuristic: closing fence must be >= opening fence length.
- Add private-chat guard: ignore group/supergroup messages.
- Move answerCallbackQuery after the authorization gate.

Test updates: mock sleep yields to event loop (setImmediate) to prevent
typing-loop microtask starvation; stop() test asserts activeTurn stays true
while a turn is in flight.
Separate the Telegram chat ID (for API calls) from the owner's user ID
(for the persistent Aiden chat key). For private chats they're equal, but
the explicit fields prevent a subtle bug if group support is ever added.
Settings UI now shows a provider/model Select populated from usable
providers. The choice persists to AppSettings (telegramProviderId /
telegramModel) and is preferred by resolveProvider() over the global
lastProviderId/lastModel fallback.

Fixes the 'configure in Aiden' turn failure: the provider fingerprint
assertion (assertScheduledProviderFingerprint) rejected Telegram turns
because no providerFingerprint was passed. sendTelegramTurn now computes
it on the fly via scheduledProviderFingerprint(provider) and passes it
through to llmClient.start options.

resolveProvider() now returns the full StoredProvider so the fingerprint
can be computed. Settings UI surfaces a clear empty state guiding users
to Settings → Providers when none are configured.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

The registerSubagentHandlers import and function call were replaced by Telegram's equivalents in main/handlers/index.ts. This completely removes subagent IPC handler registration — all subagent functionality will break at runtime.

Reviewed changes — Telegram remote control: Bot API client, owner pairing, lane-based queue with dispatch gates, Markdown-to-HTML delivery, headless turn injection (matching the schedule-execution.ts pattern), encrypted token storage, provider/model picker, optional folder-workspace authority, onboarding bento tile, and 67 passing tests across 7 test files.

  • Telegram bot API — injectable transport (TelegramBotApi with getUpdates, sendMessage, sendChatAction), production fetch transport, abort-signal support.
  • Queue and dispatch — control / priority / default lanes; control bypasses gates; priority + default require idle + no pending dispatch.
  • Turn injectionsendTelegramTurn follows the exact beginChatTurn → appendMessage → start → terminal → release pattern from schedule-execution.ts, with permission: "full" and allowComputerUse: false.
  • Workspace authoritytelegramWorkspaceId snapshotted at enqueue; project turns use assistant-automation with workspace-isolated chat; stale selections fail before generation; /workspace preserves internal whitespace and rejects case mismatches.
  • Settings UI — Token input (password field, safeStorage-backed), enable toggle, provider/model picker, workspace selector (with unavailable selection handling), connect/disconnect, security notice.
  • Onboarding — bento tile in the feature gallery, 1024×1024 transparent PNG, asset contract test updated.

⚠️ Unwired test script

test:telegram is defined in package.json but not referenced in pretest or pretest:coverage. Per AGENTS.md and the project's learnings, new test scripts must be wired into the standard test flow. The 67 Telegram tests are not exercised by npm test or npm run test:coverage.

Technical details
# Wire test:telegram into pretest

## Affected sites
- `package.json:75``test:telegram` is defined but never invoked by `pretest` or `pretest:coverage`.

## Required outcome
- Add `&& npm run test:telegram` to both `pretest` and `pretest:coverage` so `npm test` and CI exercise all Telegram tests.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Pro𝕏

Comment thread main/handlers/index.ts
Comment thread main/handlers/index.ts

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

The critical issue from the prior review remains unaddressed: registerSubagentHandlers is still removed from main/handlers/index.ts, which will break all subagent functionality at runtime.

Reviewed changes — New commits since prior pullfrog review (322511a7d90d2d):

  • Added null-safety terminal testflushHistory() is now tested as a no-op before a history store is installed.
  • Added Telegram AppSettings fieldstelegramEnabled, telegramAllowedUserId, telegramProviderId, telegramModel, telegramWorkspaceId added to AppSettings.
  • Added "telegram" usage source — New entry in UsageRequestSource union type and REQUEST_SOURCES set.
  • Wired test:telegram into npm test — Telegram tests now execute via npm test (via the test script, not pretest or pretest:coverage).

Pullfrog  | Fix it ➔View workflow run | Using DeepSeek Pro𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found. Prior critical issue (missing registerSubagentHandlers) is now fixed with a regression test.

Reviewed changes — one commit since prior pullfrog review (7d90d2d):

  • Restored registerSubagentHandlers — import and call re-added to main/handlers/index.ts, preserving both Telegram and subagent handler registrations.
  • Added IPC bootstrap regression testmain/handlers/ipc-contract.test.ts now verifies that both dedicated handler registration surfaces are imported and invoked from the bootstrap, preventing accidental removal in future refactors.

Pullfrog  | View workflow run | Using DeepSeek Pro𝕏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant