feat(v2/approvals): per-card titles and structured options

Approval cards now carry a required title (Add MCP Request, Install
Packages Request, Rebuild Request, Credentials Request) and structured
options with distinct pre-click label, post-click selectedLabel (e.g.
" Approved" / " Rejected"), and value used for click routing. The
title and normalized options are persisted in pending_questions so the
post-click card edit can render the correct per-type title and selected
label on both chat-sdk channels and Discord interactions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Koshkoshinsk
2026-04-14 14:49:01 +00:00
parent 8d60af71d3
commit d92d75e173
15 changed files with 211 additions and 51 deletions

View File

@@ -75,16 +75,29 @@ export function deleteSession(id: string): void {
export function createPendingQuestion(pq: PendingQuestion): void {
getDb()
.prepare(
`INSERT INTO pending_questions (question_id, session_id, message_out_id, platform_id, channel_type, thread_id, created_at)
VALUES (@question_id, @session_id, @message_out_id, @platform_id, @channel_type, @thread_id, @created_at)`,
`INSERT INTO pending_questions (question_id, session_id, message_out_id, platform_id, channel_type, thread_id, title, options_json, created_at)
VALUES (@question_id, @session_id, @message_out_id, @platform_id, @channel_type, @thread_id, @title, @options_json, @created_at)`,
)
.run(pq);
.run({
question_id: pq.question_id,
session_id: pq.session_id,
message_out_id: pq.message_out_id,
platform_id: pq.platform_id,
channel_type: pq.channel_type,
thread_id: pq.thread_id,
title: pq.title,
options_json: JSON.stringify(pq.options),
created_at: pq.created_at,
});
}
export function getPendingQuestion(questionId: string): PendingQuestion | undefined {
return getDb().prepare('SELECT * FROM pending_questions WHERE question_id = ?').get(questionId) as
| PendingQuestion
| undefined;
const row = getDb()
.prepare('SELECT * FROM pending_questions WHERE question_id = ?')
.get(questionId) as (Omit<PendingQuestion, 'options'> & { options_json: string }) | undefined;
if (!row) return undefined;
const { options_json, ...rest } = row;
return { ...rest, options: JSON.parse(options_json) };
}
export function deletePendingQuestion(questionId: string): void {