修复审批请求在聊天中无提示且无法响应 (#467)
* fix: support run approval prompts in chat * fix(chat): render approval prompts * fix(chat): dedupe approval pattern labels * chore: sync approval flow with current main - update Hermes Agent approval support guidance to PR #21899 - initialize Hermes table schemas in session-sync tests
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { io, type Socket } from 'socket.io-client'
|
||||
import type { ApprovalChoice } from '../../utils/approval-commands'
|
||||
import { request, getBaseUrlValue, getApiKey } from '../client'
|
||||
|
||||
export type ContentBlock =
|
||||
@@ -39,6 +40,14 @@ export interface RunEvent {
|
||||
/** Final response text on `run.completed`. May be empty/null if the agent
|
||||
* silently swallowed an upstream error — see chat store for fallback. */
|
||||
output?: string | null
|
||||
command?: string
|
||||
description?: string
|
||||
pattern_key?: string
|
||||
pattern_keys?: string[]
|
||||
choices?: ApprovalChoice[]
|
||||
resolved?: number
|
||||
choice?: ApprovalChoice
|
||||
all?: boolean
|
||||
usage?: {
|
||||
input_tokens: number
|
||||
output_tokens: number
|
||||
@@ -75,6 +84,8 @@ const sessionEventHandlers = new Map<string, {
|
||||
onCompressionCompleted: (event: RunEvent) => void
|
||||
onAbortStarted: (event: RunEvent) => void
|
||||
onAbortCompleted: (event: RunEvent) => void
|
||||
onApprovalRequest: (event: RunEvent) => void
|
||||
onApprovalResponded: (event: RunEvent) => void
|
||||
onUsageUpdated: (event: RunEvent) => void
|
||||
onRunQueued?: (event: RunEvent) => void
|
||||
}>()
|
||||
@@ -275,6 +286,32 @@ function globalAbortCompletedHandler(event: RunEvent): void {
|
||||
sessionEventHandlers.delete(sid)
|
||||
}
|
||||
|
||||
/**
|
||||
* Global approval.request event handler
|
||||
*/
|
||||
function globalApprovalRequestHandler(event: RunEvent): void {
|
||||
const sid = event.session_id
|
||||
if (!sid) return
|
||||
|
||||
const handlers = sessionEventHandlers.get(sid)
|
||||
if (handlers?.onApprovalRequest) {
|
||||
handlers.onApprovalRequest(event)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Global approval.responded event handler
|
||||
*/
|
||||
function globalApprovalRespondedHandler(event: RunEvent): void {
|
||||
const sid = event.session_id
|
||||
if (!sid) return
|
||||
|
||||
const handlers = sessionEventHandlers.get(sid)
|
||||
if (handlers?.onApprovalResponded) {
|
||||
handlers.onApprovalResponded(event)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Global usage.updated event handler
|
||||
*/
|
||||
@@ -310,6 +347,8 @@ export function registerSessionHandlers(
|
||||
onCompressionCompleted: (event: RunEvent) => void
|
||||
onAbortStarted: (event: RunEvent) => void
|
||||
onAbortCompleted: (event: RunEvent) => void
|
||||
onApprovalRequest: (event: RunEvent) => void
|
||||
onApprovalResponded: (event: RunEvent) => void
|
||||
onUsageUpdated: (event: RunEvent) => void
|
||||
onRunQueued?: (event: RunEvent) => void
|
||||
}
|
||||
@@ -392,6 +431,11 @@ export function connectChatRun(): Socket {
|
||||
chatRunSocket.on('abort.started', globalAbortStartedHandler)
|
||||
chatRunSocket.on('abort.completed', globalAbortCompletedHandler)
|
||||
|
||||
// Approval events
|
||||
chatRunSocket.on('approval.requested', globalApprovalRequestHandler)
|
||||
chatRunSocket.on('approval.request', globalApprovalRequestHandler)
|
||||
chatRunSocket.on('approval.responded', globalApprovalRespondedHandler)
|
||||
|
||||
// Usage events
|
||||
chatRunSocket.on('usage.updated', globalUsageUpdatedHandler)
|
||||
|
||||
@@ -519,6 +563,14 @@ export function startRunViaSocket(
|
||||
closed = true
|
||||
onDone()
|
||||
},
|
||||
onApprovalRequest: (evt: RunEvent) => {
|
||||
if (closed) return
|
||||
onEvent(evt)
|
||||
},
|
||||
onApprovalResponded: (evt: RunEvent) => {
|
||||
if (closed) return
|
||||
onEvent(evt)
|
||||
},
|
||||
onUsageUpdated: (evt: RunEvent) => {
|
||||
if (closed) return
|
||||
onEvent(evt)
|
||||
@@ -544,6 +596,16 @@ export function startRunViaSocket(
|
||||
}
|
||||
}
|
||||
|
||||
export function submitApprovalViaSocket(
|
||||
sessionId: string,
|
||||
choice: ApprovalChoice,
|
||||
all = false,
|
||||
): Socket {
|
||||
const socket = connectChatRun()
|
||||
socket.emit('approval.respond', { session_id: sessionId, choice, all })
|
||||
return socket
|
||||
}
|
||||
|
||||
export async function fetchModels(): Promise<{ data: Array<{ id: string }> }> {
|
||||
return request('/api/hermes/v1/models')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user