/**
 * Agent-harness TraceAdapter (D0088 slice 3).
 *
 * Agent-harness runs promptfoo's `anthropic:claude-agent-sdk` provider, which
 * (post the promptfoo `toolCalls` bump) returns the agent's full tool
 * transcript as `response.metadata.toolCalls`. Each entry has the shape
 * `{ id, name, input, output, is_error, parentToolUseId }` — close to MCP's
 * `ToolCallEntry`, so this adapter near-clones `mcp-trace-adapter.ts` and maps
 * onto the collector's `RawToolCall` with `provider-metadata` provenance,
 * tagged `mode: "agent-harness"`.
 *
 * The one shape difference from MCP is the error channel: MCP hands us a string
 * `error`; `claude-agent-sdk` hands us a boolean `is_error` plus the error text
 * folded into `output` (it is the failed `tool_result` content). We normalize
 * `is_error` → `error` by deriving an error string from `output` so a failed
 * call carries the same truthy `error` signal the collector and downstream UI
 * expect. The redactor scrubs both `output` and `error` before persistence.
 *
 * v1 captures the call sequence (name/input/output/error) only. The SDK's
 * `id` / `parentToolUseId` (sub-agent nesting) and `skillCalls` have no slot in
 * the mode-agnostic `RawToolCall` / `ToolCallRecord` model and no consumer yet,
 * so they are intentionally dropped here — a later slice can widen the model if
 * sub-agent nesting becomes useful.
 *
 * @see packages/eval/src/pipeline/compiler/mode-handlers/agent-harness/compiler.ts
 * @see docs/design-docs/agent-harness-traces-and-fixtures.md
 */
function isRecord(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
function numberArrayOrUndefined(value) {
    return Array.isArray(value) && value.every((n) => typeof n === "number")
        ? value
        : undefined;
}
/**
 * Render the failed call's `output` into the collector's string `error`
 * channel. `claude-agent-sdk` keeps the error text in `output`, so we reuse it;
 * a missing output still yields a non-empty marker so the failure is visible.
 */
function toErrorText(output) {
    if (typeof output === "string" && output.length > 0)
        return output;
    if (output === undefined)
        return "tool call reported an error";
    try {
        return JSON.stringify(output);
    }
    catch {
        return String(output);
    }
}
/** Normalize one `claude-agent-sdk` tool call into the collector's `RawToolCall`. */
function toRawToolCall(entry) {
    return {
        name: entry.name,
        input: isRecord(entry.input) ? entry.input : {},
        output: entry.output,
        ...(entry.is_error ? { error: toErrorText(entry.output) } : {}),
    };
}
/**
 * Read the provider metadata bag. `claude-agent-sdk` attaches it at
 * `response.metadata`; some promptfoo paths lift it to the top-level
 * `metadata`. Prefer the response envelope, fall back to top-level.
 */
function metadataOf(native) {
    return native.response?.metadata ?? native.metadata ?? {};
}
export const agentHarnessTraceAdapter = {
    mode: "agent-harness",
    toRawTrace(native) {
        const meta = metadataOf(native);
        const entries = Array.isArray(meta.toolCalls)
            ? meta.toolCalls
            : [];
        const llmTimings = numberArrayOrUndefined(meta.llmTimings);
        return {
            ...(native.response?.tokenUsage
                ? { tokenUsage: native.response.tokenUsage }
                : {}),
            ...(native.latencyMs !== undefined
                ? { latencyMs: native.latencyMs }
                : {}),
            metadata: {
                toolCalls: entries.map(toRawToolCall),
                ...(llmTimings ? { llmTimings } : {}),
            },
        };
    },
};
