refactor: migrate channel approval system from runs-store to pending-interactions#8428
Conversation
…interactions Co-Authored-By: Claude <noreply@anthropic.com>
…elMessageWithApprovals) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
vellum-assistant/assistant/src/runtime/routes/channel-inbound-routes.ts
Lines 753 to 755 in 722ec10
The old approval-aware path forced forceStrictSideEffects=true for non-guardian/unverified_channel actors, but this new call into processMessage no longer carries any equivalent override. In the current runtime, tool prompting enforcement is driven by memoryPolicy.strictSideEffects (default false for standard threads), so these actors can now bypass the previous forced-confirmation behavior and execute side-effect tools without guaranteed guardian approval prompts.
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| // The onEvent callback in processMessage registers pending interactions, and | ||
| // approval interception (above) handles decisions via the pending-interactions tracker. | ||
| processChannelMessageInBackground({ | ||
| processMessage, |
There was a problem hiding this comment.
Restore approval-event registration for channel ingress
This refactor routes webhook messages through processChannelMessageInBackground, but that path calls processMessage without any event hook; daemon/server.ts runs session.runAgentLoop(..., () => {}), so confirmation_request events are never registered in pending-interactions (registration currently happens in conversation-routes.ts via makeHubPublisher). When a channel message triggers a tool confirmation, handleApprovalInterception cannot find any pending request, so button/text approvals are treated as stale and the run can remain blocked waiting on a confirmation that users cannot complete.
Useful? React with 👍 / 👎.
… channel files) Main refactored channel-inbound-routes.ts into inbound-conversation.ts and inbound-message-handler.ts, and channel-guardian-routes.ts into three sub-files (guardian-approval-interception.ts, guardian-approval-prompt.ts, guardian-expiry-sweep.ts). PR3's runId->requestId migration and RunOrchestrator removal has been applied to all the new split files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
# Conflicts: # assistant/src/__tests__/channel-approval-routes.test.ts # assistant/src/__tests__/channel-approvals.test.ts # assistant/src/runtime/channel-approvals.ts # assistant/src/runtime/routes/guardian-approval-interception.ts # assistant/src/runtime/routes/guardian-expiry-sweep.ts # assistant/src/runtime/routes/inbound-message-handler.ts
| export const channelGuardianApprovalRequests = sqliteTable('channel_guardian_approval_requests', { | ||
| id: text('id').primaryKey(), | ||
| runId: text('run_id').notNull(), | ||
| requestId: text('request_id'), |
There was a problem hiding this comment.
🔴 Missing DB migration to add request_id column to channel_guardian_approval_requests table
The Drizzle schema definition adds requestId: text('request_id') to channelGuardianApprovalRequests, and the store layer (channel-guardian-store.ts) reads/writes this column, but no migration DDL adds it to the actual SQLite database.
Root Cause and Impact
The table is created by the migration at assistant/src/memory/migrations/110-channel-guardian.ts:49-67 with raw SQL CREATE TABLE IF NOT EXISTS channel_guardian_approval_requests (...) — this SQL does not include a request_id column. Other new columns (like assistant_id at line 74) are added via idempotent ALTER TABLE ... ADD COLUMN statements with try/catch, but there is no corresponding ALTER TABLE channel_guardian_approval_requests ADD COLUMN request_id TEXT anywhere in the migrations.
This means:
- Existing databases never get the column added — any query or insert involving
request_idwill fail with an SQL error. - New databases also don't get the column, since the
CREATE TABLE IF NOT EXISTSin the migration defines the actual DDL, not the Drizzle schema.
All new functions that query by this column will fail at runtime:
getPendingApprovalForRequest()atchannel-guardian-store.ts:706getUnresolvedApprovalForRequest()atchannel-guardian-store.ts:748getPendingApprovalByRequestAndGuardianChat()atchannel-guardian-store.ts:844createApprovalRequest()whenrequestIdis passed atchannel-guardian-store.ts:661
Impact: The guardian approval flow will crash with SQL errors whenever these code paths are exercised, breaking Telegram/SMS guardian approval decisions.
Prompt for agents
Add a migration to the channel guardian migration file at assistant/src/memory/migrations/110-channel-guardian.ts to add the request_id column to the channel_guardian_approval_requests table. Follow the existing pattern used for assistant_id at line 74 of that file. Add this line after the existing ALTER TABLE statements (around line 74):
try { database.run(/*sql*/ `ALTER TABLE channel_guardian_approval_requests ADD COLUMN request_id TEXT`); } catch { /* already exists */ }
This ensures existing databases get the column added, and new databases (where CREATE TABLE IF NOT EXISTS already ran) silently skip it.
Was this helpful? React with 👍 or 👎 to provide feedback.
…MessageWithApprovals Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…id migration - Channel inbound messages now register confirmation_request/secret_request events in pending-interactions via makePendingInteractionRegistrar, matching the pattern used in conversation-routes.ts via makeHubPublisher - Add ALTER TABLE migration for request_id column on channel_guardian_approval_requests table Addresses Codex P0 and Devin P0 review feedback on PR #8428. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ration PR #8428 removed runId from ApprovalPayload and switched callback_data to use requestId, but the tests were not updated. This removes the obsolete runId validation test and updates all approval test payloads to match the new schema. Co-Authored-By: Claude <noreply@anthropic.com>
Summary
PR 3/6 in the remove-runs-centralize-messages plan.
Migrates the channel approval system (Telegram, SMS) from runId + runs-store + RunOrchestrator to requestId + pending-interactions tracker + session.handleConfirmationResponse().
Plan: .private/plans/REMOVE_RUNS_CENTRALIZE_MESSAGES.md