fix task_status stale terminal reads
parent
e6222529e7
commit
1a705cbca5
|
|
@ -44,35 +44,58 @@ async function inspect(taskID: string) {
|
|||
}
|
||||
}
|
||||
|
||||
let latestUser: MessageV2.User | undefined
|
||||
let latestAssistant:
|
||||
| {
|
||||
info: MessageV2.Assistant
|
||||
parts: MessageV2.Part[]
|
||||
}
|
||||
| undefined
|
||||
for await (const item of MessageV2.stream(taskID)) {
|
||||
if (item.info.role !== "assistant") continue
|
||||
|
||||
const text = item.parts.findLast((part) => part.type === "text")?.text ?? ""
|
||||
if (item.info.error) {
|
||||
const summary = errorText(item.info.error)
|
||||
return {
|
||||
state: "error" as const,
|
||||
text: text || summary,
|
||||
}
|
||||
}
|
||||
|
||||
const done = item.info.finish && !["tool-calls", "unknown"].includes(item.info.finish)
|
||||
if (done) {
|
||||
return {
|
||||
state: "completed" as const,
|
||||
text,
|
||||
if (!latestUser && item.info.role === "user") latestUser = item.info
|
||||
if (!latestAssistant && item.info.role === "assistant") {
|
||||
latestAssistant = {
|
||||
info: item.info,
|
||||
parts: item.parts,
|
||||
}
|
||||
}
|
||||
if (latestUser && latestAssistant) break
|
||||
}
|
||||
|
||||
if (!latestAssistant) {
|
||||
return {
|
||||
state: "running" as const,
|
||||
text: text || "Task is still running.",
|
||||
text: "Task has started but has not produced output yet.",
|
||||
}
|
||||
}
|
||||
|
||||
if (latestUser && latestUser.id > latestAssistant.info.id) {
|
||||
return {
|
||||
state: "running" as const,
|
||||
text: "Task is starting.",
|
||||
}
|
||||
}
|
||||
|
||||
const text = latestAssistant.parts.findLast((part) => part.type === "text")?.text ?? ""
|
||||
if (latestAssistant.info.error) {
|
||||
const summary = errorText(latestAssistant.info.error)
|
||||
return {
|
||||
state: "error" as const,
|
||||
text: text || summary,
|
||||
}
|
||||
}
|
||||
|
||||
const done = latestAssistant.info.finish && !["tool-calls", "unknown"].includes(latestAssistant.info.finish)
|
||||
if (done) {
|
||||
return {
|
||||
state: "completed" as const,
|
||||
text,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
state: "running" as const,
|
||||
text: "Task has started but has not produced output yet.",
|
||||
text: text || "Task is still running.",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,22 @@ const ctx = {
|
|||
ask: async () => {},
|
||||
}
|
||||
|
||||
async function user(sessionID: string) {
|
||||
await Session.updateMessage({
|
||||
id: Identifier.ascending("message"),
|
||||
sessionID,
|
||||
role: "user",
|
||||
time: {
|
||||
created: Date.now(),
|
||||
},
|
||||
agent: "build",
|
||||
model: {
|
||||
providerID: "test-provider",
|
||||
modelID: "test-model",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function assistant(input: { sessionID: string; text: string; error?: string }) {
|
||||
const msg = await Session.updateMessage({
|
||||
id: Identifier.ascending("message"),
|
||||
|
|
@ -116,13 +132,13 @@ describe("tool.task_status", () => {
|
|||
const tool = await TaskStatusTool.init()
|
||||
|
||||
SessionStatus.set(session.id, { type: "busy" })
|
||||
setTimeout(async () => {
|
||||
const transition = Bun.sleep(150).then(async () => {
|
||||
SessionStatus.set(session.id, { type: "idle" })
|
||||
await assistant({
|
||||
sessionID: session.id,
|
||||
text: "finished later",
|
||||
})
|
||||
}, 150)
|
||||
})
|
||||
|
||||
const result = await tool.execute(
|
||||
{
|
||||
|
|
@ -133,9 +149,83 @@ describe("tool.task_status", () => {
|
|||
ctx,
|
||||
)
|
||||
|
||||
await transition
|
||||
expect(result.output).toContain("state: completed")
|
||||
expect(result.output).toContain("finished later")
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("returns error when child run fails", async () => {
|
||||
await using tmp = await tmpdir({ git: true })
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const session = await Session.create({})
|
||||
const tool = await TaskStatusTool.init()
|
||||
|
||||
await assistant({
|
||||
sessionID: session.id,
|
||||
text: "",
|
||||
error: "child failed",
|
||||
})
|
||||
|
||||
const result = await tool.execute({ task_id: session.id }, ctx)
|
||||
expect(result.output).toContain("state: error")
|
||||
expect(result.output).toContain("child failed")
|
||||
expect(result.metadata.state).toBe("error")
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("wait=true times out with timed_out metadata", async () => {
|
||||
await using tmp = await tmpdir({ git: true })
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const session = await Session.create({})
|
||||
const tool = await TaskStatusTool.init()
|
||||
|
||||
SessionStatus.set(session.id, { type: "busy" })
|
||||
const result = await tool.execute(
|
||||
{
|
||||
task_id: session.id,
|
||||
wait: true,
|
||||
timeout_ms: 80,
|
||||
},
|
||||
ctx,
|
||||
)
|
||||
|
||||
expect(result.output).toContain("Timed out after 80ms")
|
||||
expect(result.metadata.timed_out).toBe(true)
|
||||
expect(result.metadata.state).toBe("running")
|
||||
SessionStatus.set(session.id, { type: "idle" })
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("returns running for resumed task with a newer user turn", async () => {
|
||||
await using tmp = await tmpdir({ git: true })
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const session = await Session.create({})
|
||||
const tool = await TaskStatusTool.init()
|
||||
|
||||
await user(session.id)
|
||||
await assistant({
|
||||
sessionID: session.id,
|
||||
text: "old done",
|
||||
})
|
||||
await user(session.id)
|
||||
|
||||
const result = await tool.execute({ task_id: session.id }, ctx)
|
||||
expect(result.output).toContain("state: running")
|
||||
expect(result.output).toContain("Task is starting.")
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue