Files
Kilo-Org_kilocode/packages/opencode/test/tool/task_status.test.ts
T
2026-06-09 10:17:21 +02:00

125 lines
4.0 KiB
TypeScript

import { afterEach, describe, expect } from "bun:test"
import { Effect, Layer } from "effect"
import { Agent } from "@/agent/agent"
import { BackgroundJob } from "@/background/job"
import { Bus } from "@/bus"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { Session } from "@/session/session"
import { MessageID } from "@/session/schema"
import { SessionStatus } from "@/session/status"
import { TaskStatusTool } from "@/tool/task_status"
import { Truncate } from "@/tool/truncate"
import { RuntimeFlags } from "@/effect/runtime-flags"
import { disposeAllInstances } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
afterEach(async () => {
await disposeAllInstances()
})
const layer = (flags: Partial<RuntimeFlags.Info> = {}) =>
Layer.mergeAll(
Agent.defaultLayer,
BackgroundJob.defaultLayer,
Bus.defaultLayer,
CrossSpawnSpawner.defaultLayer,
Session.defaultLayer,
SessionStatus.defaultLayer,
Truncate.defaultLayer,
RuntimeFlags.layer(flags),
)
const it = testEffect(layer({ experimentalBackgroundSubagents: true }))
describe("tool.task_status", () => {
it.instance("returns completed background job output", () =>
Effect.gen(function* () {
const jobs = yield* BackgroundJob.Service
const sessions = yield* Session.Service
const tool = yield* TaskStatusTool
const def = yield* tool.init()
const chat = yield* sessions.create({})
const task = yield* sessions.create({ parentID: chat.id })
yield* jobs.start({ id: task.id, type: "task", run: Effect.succeed("all done") })
const result = yield* def.execute(
{ task_id: task.id, wait: true, timeout_ms: 1_000 },
{
sessionID: chat.id,
messageID: MessageID.ascending(),
agent: "build",
abort: new AbortController().signal,
messages: [],
metadata: () => Effect.void,
ask: () => Effect.void,
},
)
expect(result.output).toContain("state: completed")
expect(result.output).toContain("all done")
expect(result.metadata.timed_out).toBe(false)
}),
)
it.instance("wait=true times out while the background job is running", () =>
Effect.gen(function* () {
const jobs = yield* BackgroundJob.Service
const sessions = yield* Session.Service
const tool = yield* TaskStatusTool
const def = yield* tool.init()
const chat = yield* sessions.create({})
const task = yield* sessions.create({ parentID: chat.id })
yield* jobs.start({ id: task.id, type: "task", run: Effect.never })
const result = yield* def.execute(
{ task_id: task.id, wait: true, timeout_ms: 50 },
{
sessionID: chat.id,
messageID: MessageID.ascending(),
agent: "build",
abort: new AbortController().signal,
messages: [],
metadata: () => Effect.void,
ask: () => Effect.void,
},
)
expect(result.output).toContain("state: running")
expect(result.output).toContain("Timed out after 50ms")
expect(result.metadata.timed_out).toBe(true)
}),
)
// kilocode_change start - task status cannot inspect another session's child
it.instance("rejects cross-session task inspection", () =>
Effect.gen(function* () {
const sessions = yield* Session.Service
const tool = yield* TaskStatusTool
const def = yield* tool.init()
const owner = yield* sessions.create({})
const other = yield* sessions.create({})
const task = yield* sessions.create({ parentID: owner.id })
const exit = yield* def
.execute(
{ task_id: task.id },
{
sessionID: other.id,
messageID: MessageID.ascending(),
agent: "build",
abort: new AbortController().signal,
messages: [],
metadata: () => Effect.void,
ask: () => Effect.void,
},
)
.pipe(Effect.exit)
expect(exit._tag).toBe("Failure")
}),
)
// kilocode_change end
})