Skip to content

feat: remote permission msg#1404

Merged
zerob13 merged 3 commits intodevfrom
feat/remote-permission-msg
Mar 29, 2026
Merged

feat: remote permission msg#1404
zerob13 merged 3 commits intodevfrom
feat/remote-permission-msg

Conversation

@zerob13
Copy link
Copy Markdown
Collaborator

@zerob13 zerob13 commented Mar 29, 2026

Summary by CodeRabbit

  • New Features

    • Interactive pending tool interactions for Telegram and Feishu: inline Allow/Deny and option buttons, Feishu cards with plain-text fallbacks, automatic prompt refresh when callbacks expire, and immediate resolved-state edits on selection.
    • Added /pending to re-send the current prompt; /status shows waiting state; unrelated session commands are blocked while waiting.
  • Tests

    • Added coverage for pending-interaction routing, callbacks, refresh/resend, and runtime fallback behavior.
  • Chores

    • Bumped better-sqlite3-multiple-ciphers and electron.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 29, 2026

📝 Walkthrough

Walkthrough

Implements structured pending tool interactions for Telegram and Feishu: adds spec/docs, new pending-interaction types and parsing, runner APIs to expose/respond to pending interactions, platform prompt builders (cards/buttons and fallbacks), router/poller/binding-store changes for callback tokens and gating, and tests covering the flows.

Changes

Cohort / File(s) Summary
Documentation
docs/specs/remote-tool-interactions/plan.md, docs/specs/remote-tool-interactions/spec.md, docs/specs/remote-tool-interactions/tasks.md
New spec, plan, and task documents defining the pending-interaction loop, platform rendering/parsing rules, callback/token lifecycle, router gating, and test strategy.
Types & Constants
src/main/presenter/remoteControlPresenter/types.ts
Added pending-interaction types (permission/question), Telegram callback builders/parsers, Feishu card types/outbound actions, /pending command entries, and TELEGRAM_INTERACTION_CALLBACK_TTL_MS.
Parsing & Model
src/main/presenter/remoteControlPresenter/services/remoteInteraction.ts
New parser module that extracts/normalizes tool_call_permission and question_request blocks into RemotePendingInteraction objects and helpers for options/permission payloads.
Runner APIs & Snapshot
src/main/presenter/remoteControlPresenter/services/remoteConversationRunner.ts
Snapshots/status now include pendingInteraction; added getPendingInteraction() and respondToPendingInteraction(); detection/streaming logic updated and execution creation refactored.
Binding Store (callbacks)
src/main/presenter/remoteControlPresenter/services/remoteBindingStore.ts
In-memory pending-interaction token store with TTL eviction; added createPendingInteractionState, getPendingInteractionState, clearPendingInteractionState; cleared on rebinding/cleanup.
Telegram prompts & outbound
src/main/presenter/remoteControlPresenter/telegram/telegramInteractionPrompt.ts, src/main/presenter/remoteControlPresenter/telegram/telegramOutbound.ts
New prompt builder for permissions/questions (inline Allow/Deny, option buttons, Other); removed desktop-confirmation notice from outbound text.
Telegram poller & routing
src/main/presenter/remoteControlPresenter/telegram/telegramPoller.ts, src/main/presenter/remoteControlPresenter/services/remoteCommandRouter.ts
Poller supports deferred continuations, run-scoped cancellation, sends pending prompts; router parses pending callbacks, blocks non-allowed commands while pending, implements /pending, refreshes expired callbacks, and supports deferred continuations.
Feishu card & runtime
src/main/presenter/remoteControlPresenter/feishu/feishuInteractionPrompt.ts, src/main/presenter/remoteControlPresenter/feishu/feishuClient.ts, src/main/presenter/remoteControlPresenter/feishu/feishuRuntime.ts
Feishu card builder + fallback text; FeishuClient.sendCard() added; runtime dispatches outboundActions, sends card when pendingInteraction exists, falls back to text on card send failure.
Routers / Services (Feishu)
src/main/presenter/remoteControlPresenter/services/feishuCommandRouter.ts
Detects pending interactions, parses text responses into permission/question responses, blocks unsupported commands, supports /pending returning outbound card actions.
Tests
test/main/presenter/remoteControlPresenter/*.test.ts
Added/updated tests and helpers for pending flows: binding-store token lifecycle, runner pending extraction/respond, Telegram poller deferred/refresh behavior, Feishu card send/fallback, and command-router pending parsing.
Dependencies
package.json
Bumped better-sqlite3-multiple-ciphers to 12.8.0 and electron to ^39.8.5.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Router
    participant Runner
    participant BindingStore
    participant Client as Telegram/Feishu Client

    Note over Router,Runner: New pending-interaction flow
    User->>Router: Message or /pending
    Router->>Runner: getPendingInteraction(endpointKey)
    Runner-->>Router: RemotePendingInteraction or null

    alt Pending exists
        Router->>BindingStore: createPendingInteractionState(endpoint, {messageId, toolCallId})
        Router->>Client: send pending prompt (card/buttons + fallback)
        Client->>User: Display prompt

        User->>Client: Button callback or text reply
        Client->>Router: Callback or message with token/text
        Router->>BindingStore: getPendingInteractionState(token)
        BindingStore-->>Router: state or null

        alt Token valid
            Router->>Runner: respondToPendingInteraction(endpointKey, parsedResponse)
            Runner->>Runner: respondToolInteraction()/resume execution
            Runner-->>Router: continuation execution (maybe deferred)
            Router->>Client: edit/send resolved text and subsequent content
            Client->>User: Show resolution and continue
        else Token expired or missing
            Router->>Runner: (no response) request refresh -> re-send prompt
            Router->>Client: re-send refreshed prompt
        end
    else No pending
        Router->>Runner: handle message/command normally
        Runner-->>Router: execution
        Router->>Client: send replies
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 A little rabbit hops in code so bright,
Pending prompts flash buttons in the night,
Cards and callbacks bridge the chat and mind,
Allow, deny, or choose — the flow’s aligned,
Hoppity onward, responses now rewind.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'feat: remote permission msg' is vague and does not clearly convey the primary scope of the changeset, which implements comprehensive remote tool interactions (pending interactions, question handling, Telegram/Feishu integration, command routing, and callback management). Consider using a more descriptive title such as 'feat: implement remote pending tool interactions for Telegram and Feishu' to better reflect the full scope of the feature being introduced.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/remote-permission-msg

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/main/presenter/remoteControlPresenter/feishu/feishuClient.ts (1)

163-189: Tighten the sendCard payload contract.

sendCard() accepts FeishuInteractiveCardPayload, but that type declares all fields optional in src/main/presenter/remoteControlPresenter/types.ts:364-370, allowing malformed cards to pass the compiler. The createCardPayload() helper (line 21) only stringifies without validation. The real builder buildFeishuPendingInteractionCard() always emits a complete card with config, header, and elements, yet the test fixture demonstrates the type accepts a header-only partial. Narrow the type to match what the builder actually produces, or add runtime validation at the boundary.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/presenter/remoteControlPresenter/feishu/feishuClient.ts` around
lines 163 - 189, The sendCard boundary currently accepts
FeishuInteractiveCardPayload (which has all-optional fields) and blindly
stringifies via createCardPayload; tighten this by either (A) updating the
FeishuInteractiveCardPayload type to require the fields the builder emits
(config, header, elements) so callers like buildFeishuPendingInteractionCard
match the compile-time contract, or (B) add a runtime guard inside sendCard that
validates the payload contains non-empty config, header and elements (and throws
or returns a clear error) before calling createCardPayload; update sendCard,
FeishuInteractiveCardPayload, createCardPayload, and ensure tests/use sites that
rely on buildFeishuPendingInteractionCard remain consistent with the new
required shape.
src/main/presenter/remoteControlPresenter/services/remoteBindingStore.ts (1)

450-464: Weak token generation should use cryptographic randomness for improved defense-in-depth.

Token generation via createTelegramCallbackToken() combines Date.now().toString(36) with only 6 random characters from Math.random().toString(36), creating ~2.1 billion possible suffixes. While the practical exploit risk is limited by multi-factor validation (tokens must match endpoint key, message ID, and tool call ID), short TTL (10 minutes), and in-memory storage, using crypto.randomUUID() would provide stronger entropy.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/presenter/remoteControlPresenter/services/remoteBindingStore.ts`
around lines 450 - 464, createPendingInteractionState uses a weak
createTelegramCallbackToken() relying on Math.random; replace the token with a
cryptographically strong value (e.g., crypto.randomUUID() or hex from
crypto.randomBytes) to improve entropy. Update the implementation that currently
produces tokens (or stop calling createTelegramCallbackToken() from
createPendingInteractionState) so the stored token is generated via Node's
crypto API, and ensure any callers/validators (lookup code that compares tokens)
continue to accept the new format (UUID/hex) in RemoteBindingStore methods such
as createPendingInteractionState and any token validation/lookup logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/specs/remote-tool-interactions/spec.md`:
- Around line 1-47: Create two companion artifacts alongside spec.md: a plan.md
and tasks.md under the same docs/specs/remote-tool-interactions/ directory. In
plan.md outline goals, rollout steps, dependencies, migration/compatibility
notes referencing RemoteConversationSnapshot.pendingInteraction, Telegram and
Feishu rendering behaviors, expired callback token refresh, and the blocked
commands list (/new, /use, /model) plus allowed commands (/help, /status, /open,
/pending). In tasks.md enumerate actionable tickets: design API changes,
Electron main integration, Telegram button/callback and text-reply handling,
Feishu card fallback, token refresh logic, /pending re-send, tests, and docs,
each with owner, estimate, and acceptance criteria tied to the spec’s acceptance
criteria. Ensure both files resolve any [NEEDS CLARIFICATION] items before
marking ready.

In `@src/main/presenter/remoteControlPresenter/services/feishuCommandRouter.ts`:
- Around line 339-349: The current selection logic in the handler (uses
Number.parseInt(normalized, 10) and variable optionIndex) accepts inputs like "2
please" or "2." as valid—change it to require an exact numeric reply: after
computing optionIndex from normalized, verify that normalized trimmed exactly
matches optionIndex.toString() (or use a /^\d+$/ check) before returning the {
kind: 'question_option', optionLabel: question.options[optionIndex - 1].label }
result; otherwise fall through to the non-numeric/custom input handling. Ensure
this check is applied only when question.multiple is false.
- Around line 299-309: The current buildPendingPromptResult returns a card with
fallbackText set to formatPendingTextReplyHint(interaction) which omits the full
pending prompt; change fallbackText to use the full plain-text builder
buildFeishuPendingInteractionText(interaction) so card failures preserve the
user's permission/question details, and add the corresponding import for
buildFeishuPendingInteractionText alongside buildFeishuPendingInteractionCard;
update references in buildPendingPromptResult (FeishuCommandRouteResult,
RemotePendingInteraction) accordingly.

In `@src/main/presenter/remoteControlPresenter/telegram/telegramPoller.ts`:
- Around line 226-229: The poll loop is awaiting routed.deferred inside
handleRawUpdate(), which blocks later updates and can hang stop(); instead, do
not await routed.deferred on-loop—dispatch the continuation off the loop by
launching dispatchRouteResult(target, deferred) as a detached background task
(e.g., spawn via Promise.resolve().then(...) or setImmediate) and ensure errors
are caught inside that task; additionally, if graceful shutdown is needed, race
those detached tasks against loopPromise/stop() using a tracked task list or a
cancellable/race wrapper so stop() can settle without waiting indefinitely for
routed.deferred.

---

Nitpick comments:
In `@src/main/presenter/remoteControlPresenter/feishu/feishuClient.ts`:
- Around line 163-189: The sendCard boundary currently accepts
FeishuInteractiveCardPayload (which has all-optional fields) and blindly
stringifies via createCardPayload; tighten this by either (A) updating the
FeishuInteractiveCardPayload type to require the fields the builder emits
(config, header, elements) so callers like buildFeishuPendingInteractionCard
match the compile-time contract, or (B) add a runtime guard inside sendCard that
validates the payload contains non-empty config, header and elements (and throws
or returns a clear error) before calling createCardPayload; update sendCard,
FeishuInteractiveCardPayload, createCardPayload, and ensure tests/use sites that
rely on buildFeishuPendingInteractionCard remain consistent with the new
required shape.

In `@src/main/presenter/remoteControlPresenter/services/remoteBindingStore.ts`:
- Around line 450-464: createPendingInteractionState uses a weak
createTelegramCallbackToken() relying on Math.random; replace the token with a
cryptographically strong value (e.g., crypto.randomUUID() or hex from
crypto.randomBytes) to improve entropy. Update the implementation that currently
produces tokens (or stop calling createTelegramCallbackToken() from
createPendingInteractionState) so the stored token is generated via Node's
crypto API, and ensure any callers/validators (lookup code that compares tokens)
continue to accept the new format (UUID/hex) in RemoteBindingStore methods such
as createPendingInteractionState and any token validation/lookup logic.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7391023f-d011-4663-8e55-04727cf43a92

📥 Commits

Reviewing files that changed from the base of the PR and between a05c3f4 and d7a6957.

📒 Files selected for processing (22)
  • docs/specs/remote-tool-interactions/plan.md
  • docs/specs/remote-tool-interactions/spec.md
  • package.json
  • src/main/presenter/remoteControlPresenter/feishu/feishuClient.ts
  • src/main/presenter/remoteControlPresenter/feishu/feishuInteractionPrompt.ts
  • src/main/presenter/remoteControlPresenter/feishu/feishuRuntime.ts
  • src/main/presenter/remoteControlPresenter/services/feishuCommandRouter.ts
  • src/main/presenter/remoteControlPresenter/services/remoteBindingStore.ts
  • src/main/presenter/remoteControlPresenter/services/remoteCommandRouter.ts
  • src/main/presenter/remoteControlPresenter/services/remoteConversationRunner.ts
  • src/main/presenter/remoteControlPresenter/services/remoteInteraction.ts
  • src/main/presenter/remoteControlPresenter/telegram/telegramInteractionPrompt.ts
  • src/main/presenter/remoteControlPresenter/telegram/telegramOutbound.ts
  • src/main/presenter/remoteControlPresenter/telegram/telegramPoller.ts
  • src/main/presenter/remoteControlPresenter/types.ts
  • test/main/presenter/remoteControlPresenter/feishuCommandRouter.test.ts
  • test/main/presenter/remoteControlPresenter/feishuRuntime.test.ts
  • test/main/presenter/remoteControlPresenter/remoteBindingStore.test.ts
  • test/main/presenter/remoteControlPresenter/remoteCommandRouter.test.ts
  • test/main/presenter/remoteControlPresenter/remoteConversationRunner.test.ts
  • test/main/presenter/remoteControlPresenter/telegramOutbound.test.ts
  • test/main/presenter/remoteControlPresenter/telegramPoller.test.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/main/presenter/remoteControlPresenter/services/feishuCommandRouter.ts (1)

365-370: Minor: Consider simplifying the boolean check.

Given that question.custom is typed as boolean, question.custom !== false is equivalent to question.custom. However, the current form is defensive and not incorrect.

-    if (question.multiple || question.custom !== false) {
+    if (question.multiple || question.custom) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/presenter/remoteControlPresenter/services/feishuCommandRouter.ts`
around lines 365 - 370, The boolean check can be simplified: in the branch where
you currently test "if (question.multiple || question.custom !== false)" replace
the defensive "question.custom !== false" with "question.custom" since
question.custom is typed as boolean; update the condition in the function
containing this block (the branch that returns { kind: 'question_custom',
answerText: normalized }) to use "question.multiple || question.custom" so the
logic and returned object remain the same but the expression is clearer.
docs/specs/remote-tool-interactions/plan.md (1)

68-68: Minor: Add hyphen for compound adjective.

"interactive-card style" should be "interactive-card-style" when used as a compound adjective modifying "outbound messages".

-  - Pending interactions render as interactive-card style outbound messages when the card API succeeds.
+  - Pending interactions render as interactive-card-style outbound messages when the card API succeeds.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/specs/remote-tool-interactions/plan.md` at line 68, The phrase
"interactive-card style" is missing a hyphen for the compound adjective; update
the sentence that currently reads "Pending interactions render as
interactive-card style outbound messages when the card API succeeds." to use
"interactive-card-style" so it reads "Pending interactions render as
interactive-card-style outbound messages when the card API succeeds."
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docs/specs/remote-tool-interactions/plan.md`:
- Line 68: The phrase "interactive-card style" is missing a hyphen for the
compound adjective; update the sentence that currently reads "Pending
interactions render as interactive-card style outbound messages when the card
API succeeds." to use "interactive-card-style" so it reads "Pending interactions
render as interactive-card-style outbound messages when the card API succeeds."

In `@src/main/presenter/remoteControlPresenter/services/feishuCommandRouter.ts`:
- Around line 365-370: The boolean check can be simplified: in the branch where
you currently test "if (question.multiple || question.custom !== false)" replace
the defensive "question.custom !== false" with "question.custom" since
question.custom is typed as boolean; update the condition in the function
containing this block (the branch that returns { kind: 'question_custom',
answerText: normalized }) to use "question.multiple || question.custom" so the
logic and returned object remain the same but the expression is clearer.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 498653a3-6d12-4758-9c78-b895fc1eb3f6

📥 Commits

Reviewing files that changed from the base of the PR and between d7a6957 and 8fa5d93.

📒 Files selected for processing (6)
  • docs/specs/remote-tool-interactions/plan.md
  • docs/specs/remote-tool-interactions/tasks.md
  • src/main/presenter/remoteControlPresenter/services/feishuCommandRouter.ts
  • src/main/presenter/remoteControlPresenter/telegram/telegramPoller.ts
  • test/main/presenter/remoteControlPresenter/feishuCommandRouter.test.ts
  • test/main/presenter/remoteControlPresenter/telegramPoller.test.ts
✅ Files skipped from review due to trivial changes (2)
  • docs/specs/remote-tool-interactions/tasks.md
  • test/main/presenter/remoteControlPresenter/feishuCommandRouter.test.ts

@zerob13 zerob13 merged commit f9af684 into dev Mar 29, 2026
3 checks passed
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