mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
fix(kiro): report status on Kiro's V3 engine, including the built-in default agent (#33660)
Kiro's V3 engine (default from Kiro CLI 3.0) now reports status in Orca: Orca installs a global hooks file under KIRO_HOME/hooks (covering the built-in default agent, local and remote, never overwriting user files) and recognizes V3's PascalCase hook events. Fixes #31577 Supersedes #24937 Co-authored-by: Dongho Lee <126236161+LDH1103@users.noreply.github.com>
This commit is contained in:
@@ -84,7 +84,11 @@ export function isNewTurnEvent(source: AgentHookSource, eventName: unknown): boo
|
||||
return eventName === 'turn_start'
|
||||
case 'kiro':
|
||||
// Why: agentSpawn opens a fresh session in the pane, so stale tool/prompt caches go too.
|
||||
return eventName === 'agentSpawn' || eventName === 'userPromptSubmit'
|
||||
return (
|
||||
eventName === 'agentSpawn' ||
|
||||
eventName === 'userPromptSubmit' ||
|
||||
eventName === 'UserPromptSubmit'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
import { createHookListenerState, type HookListenerState } from '../listener-state'
|
||||
import { normalizeAndAccept } from '../../agent-hook-listener-test-harness'
|
||||
import { isNewTurnEvent } from '../provider-event-routing'
|
||||
|
||||
// Payload shapes copied from a live kiro-cli 2.28 TUI run with hooks on a custom agent.
|
||||
const base = { cwd: '/tmp/ws', session_id: 'f6fe70d7-a1b2-4c3d-9e8f-001122334455' }
|
||||
@@ -66,7 +67,81 @@ describe('normalizeKiroEvent', () => {
|
||||
expect(post?.payload.state).toBe('working')
|
||||
})
|
||||
|
||||
it('ignores event names Kiro does not document', () => {
|
||||
expect(normalizeAndAccept(state, 'kiro', event('Stop'))).toBeNull()
|
||||
it("ignores names neither engine fires, and V3's lazy SessionStart", () => {
|
||||
// Why: Kiro's docs spell V3 triggers PromptSubmit/AgentStop, but 2.27 never fires them.
|
||||
for (const name of ['PromptSubmit', 'AgentStop', 'SessionStart', 'SomethingElse']) {
|
||||
expect(normalizeAndAccept(state, 'kiro', event(name))).toBeNull()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Shapes captured from kiro-cli 2.27.0 (`chat --tui --v3`) running Orca's standalone
|
||||
// `~/.kiro/hooks/*.json` file: Claude's PascalCase names, a `sess_` id, and no final reply.
|
||||
const V3_SESSION_ID = 'sess_966a9c85-10a0-4a38-8fd0-91dbeffb032b'
|
||||
|
||||
function v3Event(name: string, extra: Record<string, unknown> = {}): Record<string, unknown> {
|
||||
return {
|
||||
session_id: V3_SESSION_ID,
|
||||
hook_event_name: name,
|
||||
cwd: 'C:\\Users\\dev\\project',
|
||||
...extra
|
||||
}
|
||||
}
|
||||
|
||||
describe('normalizeKiroEvent with the V3 engine', () => {
|
||||
let state: HookListenerState
|
||||
|
||||
beforeEach(() => {
|
||||
state = createHookListenerState()
|
||||
})
|
||||
|
||||
it('opens a turn on UserPromptSubmit, not on the SessionStart just before it', () => {
|
||||
expect(isNewTurnEvent('kiro', 'UserPromptSubmit')).toBe(true)
|
||||
expect(isNewTurnEvent('kiro', 'SessionStart')).toBe(false)
|
||||
expect(normalizeAndAccept(state, 'kiro', v3Event('SessionStart'))).toBeNull()
|
||||
|
||||
const submitted = normalizeAndAccept(
|
||||
state,
|
||||
'kiro',
|
||||
v3Event('UserPromptSubmit', { prompt: 'Run the shell command: echo probe-ok' })
|
||||
)
|
||||
expect(submitted?.payload).toMatchObject({
|
||||
state: 'working',
|
||||
agentType: 'kiro',
|
||||
prompt: 'Run the shell command: echo probe-ok'
|
||||
})
|
||||
})
|
||||
|
||||
it('names the running tool and keeps a string tool_response as tool output', () => {
|
||||
normalizeAndAccept(state, 'kiro', v3Event('UserPromptSubmit', { prompt: 'echo it' }))
|
||||
const pre = normalizeAndAccept(
|
||||
state,
|
||||
'kiro',
|
||||
v3Event('PreToolUse', { tool_name: 'execute_pwsh', tool_input: { command: 'echo probe-ok' } })
|
||||
)
|
||||
expect(pre?.payload).toMatchObject({ state: 'working', toolName: 'execute_pwsh' })
|
||||
|
||||
const post = normalizeAndAccept(
|
||||
state,
|
||||
'kiro',
|
||||
v3Event('PostToolUse', {
|
||||
tool_name: 'execute_pwsh',
|
||||
tool_input: { command: 'echo probe-ok' },
|
||||
tool_response: 'probe-ok'
|
||||
})
|
||||
)
|
||||
expect(post?.payload).toMatchObject({
|
||||
state: 'working',
|
||||
lastAssistantMessage: 'probe-ok',
|
||||
lastAssistantMessageIsToolOutput: true
|
||||
})
|
||||
})
|
||||
|
||||
it('reports done on Stop and SessionEnd, which carry no final reply', () => {
|
||||
normalizeAndAccept(state, 'kiro', v3Event('UserPromptSubmit', { prompt: 'finish' }))
|
||||
for (const name of ['Stop', 'SessionEnd']) {
|
||||
const event = normalizeAndAccept(state, 'kiro', v3Event(name))
|
||||
expect(event?.payload).toMatchObject({ state: 'done', agentType: 'kiro', prompt: 'finish' })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -19,12 +19,18 @@ export function normalizeKiroEvent(
|
||||
case 'userPromptSubmit':
|
||||
case 'preToolUse':
|
||||
case 'postToolUse':
|
||||
case 'UserPromptSubmit':
|
||||
case 'PreToolUse':
|
||||
case 'PostToolUse':
|
||||
stateName = 'working'
|
||||
break
|
||||
case 'agentSpawn':
|
||||
case 'stop':
|
||||
case 'Stop':
|
||||
case 'SessionEnd':
|
||||
stateName = 'done'
|
||||
break
|
||||
// V3 fires SessionStart lazily, just before the first prompt; done would flash idle mid-turn.
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -2,13 +2,17 @@ import type { ToolSnapshot } from '../listener-event'
|
||||
import { readString } from '../tool-input-preview'
|
||||
import { extractClaudeToolFields } from './claude-tool-fields'
|
||||
|
||||
/** Kiro's camelCase hook names mapped onto the Claude names its payload fields mirror. */
|
||||
/** Kiro V2 camelCase hook names mapped onto Claude's; V3 already sends Claude's names. */
|
||||
export const KIRO_CLAUDE_EVENT_NAMES: Readonly<Record<string, string>> = Object.freeze({
|
||||
agentSpawn: 'SessionStart',
|
||||
userPromptSubmit: 'UserPromptSubmit',
|
||||
preToolUse: 'PreToolUse',
|
||||
postToolUse: 'PostToolUse',
|
||||
stop: 'Stop'
|
||||
stop: 'Stop',
|
||||
UserPromptSubmit: 'UserPromptSubmit',
|
||||
PreToolUse: 'PreToolUse',
|
||||
PostToolUse: 'PostToolUse',
|
||||
Stop: 'Stop'
|
||||
})
|
||||
|
||||
/** Kiro keeps Claude tool fields, maps camelCase events and sends final text in assistant_response. */
|
||||
|
||||
Reference in New Issue
Block a user