import { describe, expect, it } from "vitest"; import { parseLine, makeParser } from "../argv"; describe("parseLine opencode", () => { it("extracts text from nested part payload", () => { const line = JSON.stringify({ type: "text", sessionID: "ses_test", part: { type: "text", text: "ok", }, }); expect(parseLine("opencode", line)).toContainEqual({ kind: "delta", text: "ok", }); }); it("emits one delta when top-level and nested text are both present", () => { const line = JSON.stringify({ type: "text", text: "ok", part: { type: "text", text: "ok", }, }); expect(parseLine("opencode", line)).toEqual([ { kind: "delta", text: "ok", }, ]); }); it("falls back to top-level text when nested text is empty", () => { const line = JSON.stringify({ type: "text", content: "ok", part: { type: "text", text: "", }, }); expect(parseLine("opencode", line)).toEqual([ { kind: "delta", text: "ok", }, ]); }); it("extracts session only from step start payload", () => { expect( parseLine( "opencode", JSON.stringify({ type: "step_start", sessionID: "ses_test", part: { type: "step-start", }, }), ), ).toContainEqual({ kind: "meta", key: "session", value: "ses_test", }); expect( parseLine( "opencode", JSON.stringify({ type: "text", sessionID: "ses_test", part: { type: "text", text: "ok", }, }), ), ).not.toContainEqual({ kind: "meta", key: "session", value: "ses_test", }); }); it("extracts usage from step finish payload and accumulates successive steps", () => { const line1 = JSON.stringify({ type: "step_finish", part: { type: "step-finish", tokens: { input: 10, output: 2, cache: { read: 3, write: 4, }, }, cost: 0.01, }, }); const line2 = JSON.stringify({ type: "step_finish", part: { type: "step-finish", tokens: { input: 5, output: 1, cache: { read: 1, write: 1, }, }, cost: 0.005, }, }); const parser = makeParser("opencode"); expect(parser(line1)).toEqual([ { kind: "meta", key: "usage", value: { input_tokens: 10, output_tokens: 2, cache_read_input_tokens: 3, cache_creation_input_tokens: 4, }, }, { kind: "meta", key: "cost_usd", value: 0.01, }, ]); expect(parser(line2)).toEqual([ { kind: "meta", key: "usage", value: { input_tokens: 15, output_tokens: 3, cache_read_input_tokens: 4, cache_creation_input_tokens: 5, }, }, { kind: "meta", key: "cost_usd", value: 0.015, }, ]); }); }); describe("parseLine bob", () => { it("extracts text from stream-json output", () => { const line = JSON.stringify({ text: "Hello", }); expect(parseLine("bob", line)).toEqual([ { kind: "delta", text: "Hello", }, ]); }); it("extracts content field when present", () => { const line = JSON.stringify({ content: "World", }); expect(parseLine("bob", line)).toEqual([ { kind: "delta", text: "World", }, ]); }); it("extracts message field when present", () => { const line = JSON.stringify({ message: "Test", }); expect(parseLine("bob", line)).toEqual([ { kind: "delta", text: "Test", }, ]); }); it("handles final answer after thinking when --hide-intermediary-output is used", () => { // When --hide-intermediary-output is enabled, Bob only emits the final answer. // This test verifies that the parser correctly handles the final completion. const finalAnswer = JSON.stringify({ text: "Final result", }); expect(parseLine("bob", finalAnswer)).toEqual([ { kind: "delta", text: "Final result", }, ]); }); });