feat(opencode): map question.asked to waiting state (#1406)
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
9b3b2a5486
commit
85b125f6bf
|
|
@ -660,6 +660,22 @@ describe('OpenCode hook normalization', () => {
|
|||
expect(result?.payload.state).toBe('waiting')
|
||||
})
|
||||
|
||||
it('AskUserQuestion maps to waiting', () => {
|
||||
// Why: OpenCode emits `question.asked` when the agent uses an ask-the-user
|
||||
// tool (distinct from `permission.asked`, which blocks on tool approval).
|
||||
// Both leave the agent idle-but-waiting on a human, so both must render
|
||||
// the same red "needs attention" indicator. Without this mapping the pane
|
||||
// silently stays in `working` and the user has no visual cue that the
|
||||
// agent is waiting on them.
|
||||
const result = _internals.normalizeHookPayload(
|
||||
'opencode',
|
||||
buildBody({ hook_event_name: 'AskUserQuestion' }),
|
||||
'production'
|
||||
)
|
||||
expect(result?.payload.state).toBe('waiting')
|
||||
expect(result?.payload.agentType).toBe('opencode')
|
||||
})
|
||||
|
||||
it('unknown event name returns null', () => {
|
||||
const result = _internals.normalizeHookPayload(
|
||||
'opencode',
|
||||
|
|
|
|||
|
|
@ -844,11 +844,13 @@ function normalizeCodexEvent(
|
|||
|
||||
// Why: OpenCode has no declarative hook surface — it exposes in-process plugin
|
||||
// events (session.status busy/idle, session.idle, permission.asked,
|
||||
// message.updated, message.part.updated). The bundled plugin (see
|
||||
// opencode/hook-service) pre-maps those to our stable hook_event_name
|
||||
// question.asked, message.updated, message.part.updated). The bundled plugin
|
||||
// (see opencode/hook-service) pre-maps those to our stable hook_event_name
|
||||
// vocabulary before POSTing so this normalizer can share the same switch
|
||||
// shape as Claude/Codex/Gemini. SessionBusy = turn started, SessionIdle =
|
||||
// turn finished, PermissionRequest = blocked on user approval, MessagePart =
|
||||
// turn finished, PermissionRequest = blocked on user approval, AskUserQuestion =
|
||||
// blocked on user reply to an ask-the-user tool (both map to `waiting` so the
|
||||
// sidebar renders the red "needs attention" indicator), MessagePart =
|
||||
// incremental text from user prompt or assistant reply (stays in `working`
|
||||
// because streaming chunks must not flip the row to done mid-turn).
|
||||
function normalizeOpenCodeEvent(
|
||||
|
|
@ -862,7 +864,7 @@ function normalizeOpenCodeEvent(
|
|||
? 'working'
|
||||
: eventName === 'SessionIdle'
|
||||
? 'done'
|
||||
: eventName === 'PermissionRequest'
|
||||
: eventName === 'PermissionRequest' || eventName === 'AskUserQuestion'
|
||||
? 'waiting'
|
||||
: null
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,21 @@ describe('OpenCode hook plugin source', () => {
|
|||
expect(source).toContain('cachedEndpointValues = null;')
|
||||
})
|
||||
|
||||
it('forwards question.asked as AskUserQuestion so the pane flips to waiting', () => {
|
||||
// Why: OpenCode exposes two separate plugin events for human-in-the-loop
|
||||
// moments — `permission.asked` (blocks on tool approval) and
|
||||
// `question.asked` (the agent called an ask-the-user tool). The plugin
|
||||
// must forward both so the server-side normalizer can map each to
|
||||
// `waiting` and render the red indicator. Dropping `question.asked`
|
||||
// leaves the pane stuck in `working` while the agent is actually idle,
|
||||
// waiting on a human reply — exactly the bug three other OpenCode
|
||||
// integrations (cmux, t3code, open-vibe-island) all handle.
|
||||
const source = _internals.getOpenCodePluginSource()
|
||||
|
||||
expect(source).toContain('if (event.type === "question.asked")')
|
||||
expect(source).toContain('await post("AskUserQuestion", event.properties || {});')
|
||||
})
|
||||
|
||||
it('guards endpoint-file parse warnings with a process-lifetime latch', () => {
|
||||
// Why: ENOENT is the normal pre-install case and must stay silent, but a
|
||||
// malformed/unreadable file (EACCES, EIO, parse error) would otherwise
|
||||
|
|
|
|||
|
|
@ -259,6 +259,18 @@ function getOpenCodePluginSource(): string {
|
|||
' return;',
|
||||
' }',
|
||||
'',
|
||||
' if (event.type === "question.asked") {',
|
||||
' // Why: question.asked fires when OpenCode uses an ask-the-user tool',
|
||||
' // (distinct from permission.asked, which blocks on tool approval).',
|
||||
' // The agent is idle-but-waiting on a human reply, not running, so we',
|
||||
' // must flip the pane to the same red "needs attention" state used for',
|
||||
' // permission requests. Like permission.asked, do not touch lastStatus',
|
||||
' // so the next SessionBusy/SessionIdle after the user answers still',
|
||||
' // fires and restores the normal working/done flow.',
|
||||
' await post("AskUserQuestion", event.properties || {});',
|
||||
' return;',
|
||||
' }',
|
||||
'',
|
||||
' if (event.type === "message.updated") {',
|
||||
' // Why: role is already cached above the isChildSession await so the',
|
||||
' // back-to-back message.part.updated for the same messageID is not',
|
||||
|
|
|
|||
Loading…
Reference in New Issue