import { useQuery } from '@tanstack/react-query'
import type { UseQueryResult } from '@tanstack/react-query'
import { apiFetch } from '../../api/client'
import { adaptSession } from '../../api/adapters'
import type { Session, SessionDTO } from '../../api/adapters'
export interface TurnDTO {
turn_id: string
seq: number
role: 'user' | 'assistant' | 'tool' | 'system'
timestamp: number
content: string
model?: string
tokens_in?: number
tokens_out?: number
cost_usd?: number
tool_calls?: unknown
metadata?: unknown
}
export interface Turn {
i: number
role: 'user' | 'assistant' | 'tool'
body: string
model?: string
tokensIn?: number
tokensOut?: number
toolName?: string
toolKind?: 'call' | 'result'
toolOutput?: string
}
export interface SessionWithTurnsDTO extends SessionDTO {
turns: TurnDTO[]
}
export interface SessionWithTurns extends Omit<Session, 'turns'> {
turns: Turn[]
}
function extractToolCall(
raw: unknown,
): { toolName?: string; toolKind?: 'call' | 'result'; toolOutput?: string } {
if (raw == null || typeof raw !== 'object') {
return {}
}
const obj = raw as Record<string, unknown>
// opencode format: {tool, status, output}
const opencodeTool = typeof obj['tool'] === 'string' ? obj['tool'] : undefined
const opencodeStatus = typeof obj['status'] === 'string' ? obj['status'] : undefined
if (opencodeTool != null) {
const kind = opencodeStatus === 'completed' ? 'result' : 'call'
const output = typeof obj['output'] === 'string' ? obj['output'] : undefined
return { toolName: opencodeTool, toolKind: kind as 'call' | 'result', toolOutput: output }
}
// claude-code format: {name, type: "tool_use"|"tool_result"}
const name = typeof obj['name'] === 'string' ? obj['name'] : undefined
const type = typeof obj['type'] === 'string' ? obj['type'] : undefined
const kind =
type === 'tool_use' ? 'call' as const
: type === 'tool_result' ? 'result' as const
: undefined
if (name != null && kind != null) {
const output = typeof obj['content'] === 'string' ? obj['content'] : undefined
return { toolName: name, toolKind: kind, toolOutput: output }
}
return {}
}
function extractFirstToolCall(
raw: unknown,
): { toolName?: string; toolKind?: 'call' | 'result'; toolOutput?: string } {
if (Array.isArray(raw) && raw.length > 0) {
return extractToolCall(raw[0])
}
return extractToolCall(raw)
}
export function adaptTurn(d: TurnDTO): Turn {
const role: 'user' | 'assistant' | 'tool' =
d.role === 'system' ? 'assistant' : d.role
const { toolName, toolKind, toolOutput } = extractFirstToolCall(d.tool_calls)
return {
i: d.seq,
role,
body: d.content,
model: d.model,
tokensIn: d.tokens_in,
tokensOut: d.tokens_out,
toolName,
toolKind,
toolOutput,
}
}
export function useSession(
tool: string,
host: string,
id: string,
): UseQueryResult<SessionWithTurns> {
return useQuery({
queryKey: ['session', tool, host, id],
queryFn: async () => {
const dto = await apiFetch<SessionWithTurnsDTO>(
`/api/v1/sessions/${tool}/${host}/${id}`,
)
const session = adaptSession(dto)
return { ...session, turns: dto.turns.map(adaptTurn) }
},
})
}