Skip to content

Handle OpenCode question prompts in Discord flow - #66

Open
fox3000foxy wants to merge 6 commits into
bevibing:mainfrom
fox3000foxy:fix-question-asked-discord-flow
Open

Handle OpenCode question prompts in Discord flow#66
fox3000foxy wants to merge 6 commits into
bevibing:mainfrom
fox3000foxy:fix-question-asked-discord-flow

Conversation

@fox3000foxy

Copy link
Copy Markdown

Summary

  • handle OpenCode question.asked SSE events instead of leaving Discord runs stuck in Running
  • render pending questions in Discord with option buttons plus a reject button
  • answer/reject questions through OpenCode's /question/{requestID}/reply and /reject endpoints
  • add regression coverage for SSE question events, question API helpers, and Discord button parsing for que_... IDs

Fixes #46.

Testing

  • npm test -- --run
  • npm run build
  • locally installed this branch and verified a real plan-approval flow in Discord no longer hangs

Copy of #60

@RoundTable02 RoundTable02 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for taking over #60 and preserving the original commit. I independently reproduced #46: main has no question.asked callback path, while this PR does deliver the event to the execution layer. However, the current implementation still fails several valid OpenCode question flows, so I do not think it is safe to merge yet.

Verification performed:

  • main: 171/171 existing tests passed; the added issue regression test failed because onQuestionAsked does not exist.
  • PR #60: 178/178 existing tests and build passed, but an adversarial multi-question test showed that it immediately sends only [["A"]], dropping the second question.
  • PR #66: 184/184 existing tests and build passed, and the basic issue regression test passed. Four adversarial tests failed: it submitted [["A"], []], accepted a request owned by another session, serialized a message select with min_values: 0, and threw InteractionNotReplied on an OpenCode failure before the interaction was acknowledged.

The inline comments below cover the blocking findings. The OpenCode 1.3.9 contract also enables custom answers by default, so the Discord flow needs a modal or a mapped next-message reply path rather than options-only handling.

Security note: I found no new dependency, secret, SSRF, or unauthenticated authorization issue in this patch. The session/request ownership issue below is an integrity boundary problem because /question lists pending requests across all sessions on the same OpenCode server.

Recommendation: keep #60 superseded, address these findings in #66, and add regression tests that validate serialized Discord component payloads and error/timeout paths.

Comment thread src/handlers/buttonHandler.ts Outdated
Comment thread src/handlers/buttonHandler.ts
Comment thread src/handlers/buttonHandler.ts Outdated
Comment thread src/handlers/buttonHandler.ts Outdated
Comment thread src/handlers/buttonHandler.ts
@fox3000foxy

Copy link
Copy Markdown
Author

I will make changes later in the weekend, thanks for the review

@RoundTable02 RoundTable02 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for addressing the previous review. I re-ran the full suite and the adversarial regression tests against 8d3ac28.

Confirmed fixed:

  • Discord message selects now serialize with min_values: 1.
  • Incomplete manual submission is rejected instead of posting an empty answer array.
  • Answer/select/modal/submit paths acknowledge interactions before calling OpenCode.
  • Answer paths now bind the request to the current OpenCode session.

Verification:

  • Existing suite: 188/188 passed.
  • TypeScript build: passed.
  • Follow-up adversarial suite: 3 passed, 4 failed.

Four blocking issues remain. The inline comments cover the exact cases: OpenCode's omitted custom field is interpreted incorrectly, explicit custom input can create a six-button Discord row, requests with more than five questions lose controls, and reject still bypasses the request/session ownership check.

No new dependency or secret changes were introduced by the follow-up commit. I recommend keeping this PR in CHANGES_REQUESTED until these cases are covered by regression tests and fixed.

Comment thread src/handlers/buttonHandler.ts Outdated
Comment thread src/handlers/buttonHandler.ts Outdated
Comment thread src/handlers/buttonHandler.ts
Comment thread src/handlers/buttonHandler.ts
- custom detection: treat custom as enabled unless explicitly disabled
  (question.custom !== false instead of === true)
- 6-button row: when 5 options + custom would exceed limit, use select menu
  instead; add custom button in its own row for select menu questions
- pagination: support 5+ questions by paginating maxRows-1 questions per page
  with Prev/Next nav in the bottom row
- reject ownership: add findRequestForSession check before rejecting
@fox3000foxy
fox3000foxy requested a review from RoundTable02 July 27, 2026 06:42

@RoundTable02 RoundTable02 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for addressing the four findings from the previous review. I re-ran the full suite and fresh adversarial regressions against 745d68a.

Confirmed fixed:

  • Omitted custom is now treated as enabled.
  • Five options plus Custom no longer creates a six-button row.
  • Six one-row questions are reachable through pagination.
  • Reject now enforces request/session ownership.

Verification:

  • Existing suite: 188/188 passed.
  • TypeScript build: passed.
  • Fresh adversarial suite: 4 passed, 2 failed.
  • The Reject unit test fails when run in isolation, despite passing as part of the full suite.

Two blocking hang regressions remain. Pagination is based on a fixed question count even though some questions consume two component rows, which makes questions disappear between pages. Separately, the message text is not paginated and can exceed Discord's 2,000-character content limit; the resulting fallback has no answer controls. The inline comments include exact reproductions and fixes.

I also found two correctness/coverage gaps: OpenCode permits more than 25 options but this implementation silently drops the overflow, and the Reject test passes only because mock state leaks from the previous test.

Security update: the cross-session Reject integrity issue is fixed. No dependency or lockfile changes, new secrets, or new authentication bypasses were introduced by this follow-up. The current production audit findings are pre-existing and not caused by this PR.

Recommendation: keep this PR in CHANGES_REQUESTED until the variable-row pagination, complete-message length budget, option overflow behavior, and independent regression tests are addressed.

Comment thread src/handlers/buttonHandler.ts Outdated
Comment thread src/handlers/buttonHandler.ts Outdated
Comment thread src/handlers/buttonHandler.ts
Comment thread src/__tests__/buttonHandler.test.ts
…ation

- Pagination now counts by actual row consumption (select+custom=2 rows)
- buildQuestionText only renders the current page's questions
- Options >25 force enable custom input so overflow is reachable
- Reject test now explicitly mocks listQuestions (no state leak)
@fox3000foxy
fox3000foxy requested a review from RoundTable02 July 27, 2026 12:00
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.

[Bug] Session hangs indefinitely when OpenCode fires a question.asked event

2 participants