mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-19 01:51:21 +08:00
fix(cli): emit run events once
This commit is contained in:
@@ -74,7 +74,7 @@ describe("opencode run (non-interactive subprocess)", () => {
|
||||
({ llm, opencode }) =>
|
||||
Effect.gen(function* () {
|
||||
yield* llm.text("structured output")
|
||||
const result = yield* opencode.run("say hi", { format: "json" })
|
||||
const result = yield* opencode.run("say hi", { format: "json", extraArgs: ["--auto"] })
|
||||
opencode.expectExit(result, 0)
|
||||
|
||||
const events = opencode.parseJsonEvents(result.stdout)
|
||||
@@ -83,9 +83,25 @@ describe("opencode run (non-interactive subprocess)", () => {
|
||||
expect(typeof evt.type).toBe("string")
|
||||
expect(typeof evt.sessionID).toBe("string")
|
||||
}
|
||||
// At least one `text` event should appear with the LLM's response.
|
||||
const text = events.find((e) => e.type === "text")
|
||||
expect(text).toBeDefined()
|
||||
expect(events.filter((event) => event.type === "step_start")).toHaveLength(1)
|
||||
expect(events.filter((event) => event.type === "text")).toHaveLength(1)
|
||||
expect(events.filter((event) => event.type === "step_finish")).toHaveLength(1)
|
||||
}),
|
||||
60_000,
|
||||
)
|
||||
|
||||
cliIt.live(
|
||||
"--format json emits each completed tool once",
|
||||
({ llm, opencode }) =>
|
||||
Effect.gen(function* () {
|
||||
yield* llm.tool("glob", { pattern: "package.json" })
|
||||
yield* llm.text("tool complete")
|
||||
const result = yield* opencode.run("find package.json", { format: "json", extraArgs: ["--auto"] })
|
||||
opencode.expectExit(result, 0)
|
||||
|
||||
const events = opencode.parseJsonEvents(result.stdout)
|
||||
expect(events.filter((event) => event.type === "tool_use")).toHaveLength(1)
|
||||
expect(events.filter((event) => event.type === "text")).toHaveLength(1)
|
||||
}),
|
||||
60_000,
|
||||
)
|
||||
|
||||
@@ -88,20 +88,14 @@ function retry(sessionID: string, attempt: number, message: string) {
|
||||
function assistant(id: string, sessionID = "session-1"): SdkEvent {
|
||||
return {
|
||||
id: `evt-${id}`,
|
||||
type: "sync",
|
||||
syncEvent: {
|
||||
type: "message.updated.1",
|
||||
id: `evt-${id}`,
|
||||
seq: 1,
|
||||
aggregateID: sessionID,
|
||||
data: {
|
||||
type: "message.updated",
|
||||
properties: {
|
||||
sessionID,
|
||||
info: assistantMessage({
|
||||
sessionID,
|
||||
info: assistantMessage({
|
||||
sessionID,
|
||||
id,
|
||||
parts: [],
|
||||
}).info,
|
||||
},
|
||||
id,
|
||||
parts: [],
|
||||
}).info,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -295,6 +289,18 @@ function textPart(id: string, messageID: string, text: string, sessionID = "sess
|
||||
}
|
||||
|
||||
function textUpdated(part: TextPart): SdkEvent {
|
||||
return {
|
||||
id: `evt-${part.id}-updated`,
|
||||
type: "message.part.updated",
|
||||
properties: {
|
||||
sessionID: part.sessionID,
|
||||
part,
|
||||
time: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function syncTextUpdated(part: TextPart): SdkEvent {
|
||||
return {
|
||||
id: `evt-${part.id}-updated`,
|
||||
type: "sync",
|
||||
@@ -338,17 +344,11 @@ function reasoningUpdated(part: ReasoningPart): SdkEvent {
|
||||
function toolUpdated(part: SessionToolPart): SdkEvent {
|
||||
return {
|
||||
id: `evt-${part.id}-updated`,
|
||||
type: "sync",
|
||||
syncEvent: {
|
||||
type: "message.part.updated.1",
|
||||
id: `evt-${part.id}-updated`,
|
||||
seq: 1,
|
||||
aggregateID: part.sessionID,
|
||||
data: {
|
||||
sessionID: part.sessionID,
|
||||
part,
|
||||
time: 1,
|
||||
},
|
||||
type: "message.part.updated",
|
||||
properties: {
|
||||
sessionID: part.sessionID,
|
||||
part,
|
||||
time: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -468,6 +468,34 @@ function sdk(
|
||||
}
|
||||
|
||||
describe("run stream transport", () => {
|
||||
test("ignores the sync copy of a native message event", async () => {
|
||||
const src = globalFeed()
|
||||
const ui = footer()
|
||||
const transport = await createSessionTransport({
|
||||
sdk: sdk({ globalStream: src.stream }),
|
||||
sessionID: "session-1",
|
||||
thinking: true,
|
||||
limits: () => ({}),
|
||||
footer: ui.api,
|
||||
})
|
||||
const part = {
|
||||
...textPart("text-1", "msg-1", "Hello"),
|
||||
time: { start: 1, end: 2 },
|
||||
}
|
||||
|
||||
try {
|
||||
src.push(globalEvent(assistant("msg-1")))
|
||||
src.push(globalEvent(textUpdated(part)))
|
||||
src.push(globalEvent(syncTextUpdated(part)))
|
||||
|
||||
await waitFor(() => ui.commits.find((item) => item.kind === "assistant" && item.text === "Hello"))
|
||||
expect(ui.commits.filter((item) => item.kind === "assistant" && item.text === "Hello")).toHaveLength(1)
|
||||
} finally {
|
||||
src.close()
|
||||
await transport.close()
|
||||
}
|
||||
})
|
||||
|
||||
test("does not replay persisted main-session history during bootstrap by default", async () => {
|
||||
const src = eventFeed()
|
||||
const ui = footer()
|
||||
|
||||
Reference in New Issue
Block a user