mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-13 23:52:06 +00:00
test(task): cover background agents
This commit is contained in:
@@ -319,6 +319,10 @@ exports[`tool parameters JSON Schema (wire shape) task 1`] = `
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"properties": {
|
||||
"background": {
|
||||
"description": "When true, launch the subagent in the background and return immediately",
|
||||
"type": "boolean",
|
||||
},
|
||||
"command": {
|
||||
"description": "The command that triggered this task",
|
||||
"type": "string",
|
||||
|
||||
@@ -235,6 +235,10 @@ describe("tool parameters", () => {
|
||||
const parsed = parse(Task, { description: "d", prompt: "p", subagent_type: "general" })
|
||||
expect(parsed.subagent_type).toBe("general")
|
||||
})
|
||||
test("accepts optional background flag", () => {
|
||||
const parsed = parse(Task, { description: "d", prompt: "p", subagent_type: "general", background: true })
|
||||
expect(parsed.background).toBe(true)
|
||||
})
|
||||
test("rejects missing prompt", () => {
|
||||
expect(accepts(Task, { description: "d", subagent_type: "general" })).toBe(false)
|
||||
})
|
||||
|
||||
@@ -15,7 +15,9 @@ import { Question } from "@/question"
|
||||
import { Todo } from "@/session/todo"
|
||||
import { Skill } from "@/skill"
|
||||
import { Agent } from "@/agent/agent"
|
||||
import { BackgroundJob } from "@/background/job"
|
||||
import { Session } from "@/session/session"
|
||||
import { SessionStatus } from "@/session/status"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import { Git } from "@/git"
|
||||
import { LSP } from "@/lsp/lsp"
|
||||
@@ -32,6 +34,7 @@ import { ToolJsonSchema } from "@/tool/json-schema"
|
||||
|
||||
const node = CrossSpawnSpawner.defaultLayer
|
||||
const originalExperimentalScout = Flag.OPENCODE_EXPERIMENTAL_SCOUT
|
||||
const originalBackgroundAgents = Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS
|
||||
const configLayer = TestConfig.layer({
|
||||
directories: () => InstanceState.directory.pipe(Effect.map((dir) => [path.join(dir, ".opencode")])),
|
||||
})
|
||||
@@ -44,6 +47,7 @@ const registryLayer = ToolRegistry.layer.pipe(
|
||||
Layer.provide(Skill.defaultLayer),
|
||||
Layer.provide(Agent.defaultLayer),
|
||||
Layer.provide(Session.defaultLayer),
|
||||
Layer.provide(Layer.mergeAll(SessionStatus.defaultLayer, BackgroundJob.defaultLayer)),
|
||||
Layer.provide(Provider.defaultLayer),
|
||||
Layer.provide(Git.defaultLayer),
|
||||
Layer.provide(Reference.defaultLayer),
|
||||
@@ -62,6 +66,7 @@ const it = testEffect(Layer.mergeAll(registryLayer, node, Agent.defaultLayer))
|
||||
|
||||
afterEach(async () => {
|
||||
Flag.OPENCODE_EXPERIMENTAL_SCOUT = originalExperimentalScout
|
||||
Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS = originalBackgroundAgents
|
||||
await disposeAllInstances()
|
||||
})
|
||||
|
||||
@@ -88,6 +93,41 @@ describe("tool.registry", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("hides task_status unless experimental background agents are enabled", () =>
|
||||
Effect.gen(function* () {
|
||||
Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS = false
|
||||
const registry = yield* ToolRegistry.Service
|
||||
const ids = yield* registry.ids()
|
||||
|
||||
expect(ids).not.toContain("task_status")
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("hides task background parameter unless experimental background agents are enabled", () =>
|
||||
Effect.gen(function* () {
|
||||
Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS = false
|
||||
const registry = yield* ToolRegistry.Service
|
||||
const agent = yield* Agent.Service
|
||||
const build = yield* agent.get("build")
|
||||
const task = (yield* registry.tools({ providerID: ProviderID.opencode, modelID: ModelID.make("test"), agent: build })).find(
|
||||
(tool) => tool.id === "task",
|
||||
)
|
||||
|
||||
expect(task?.jsonSchema).toBeDefined()
|
||||
expect((task?.jsonSchema?.properties as Record<string, unknown> | undefined)?.background).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("shows task_status when experimental background agents are enabled", () =>
|
||||
Effect.gen(function* () {
|
||||
Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS = true
|
||||
const registry = yield* ToolRegistry.Service
|
||||
const ids = yield* registry.ids()
|
||||
|
||||
expect(ids).toContain("task_status")
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("loads tools from .opencode/tool (singular)", () =>
|
||||
Effect.gen(function* () {
|
||||
const test = yield* TestInstance
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
import { afterEach, describe, expect } from "bun:test"
|
||||
import { Effect, Exit, Fiber, Layer } from "effect"
|
||||
import { Agent } from "../../src/agent/agent"
|
||||
import { BackgroundJob } from "@/background/job"
|
||||
import { Bus } from "@/bus"
|
||||
import { Config } from "@/config/config"
|
||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||
import { Session } from "@/session/session"
|
||||
import { MessageV2 } from "../../src/session/message-v2"
|
||||
import type { SessionPrompt } from "../../src/session/prompt"
|
||||
import { MessageID, PartID, SessionID } from "../../src/session/schema"
|
||||
import { SessionRunState } from "@/session/run-state"
|
||||
import { SessionStatus } from "@/session/status"
|
||||
import { ModelID, ProviderID } from "../../src/provider/schema"
|
||||
import { TaskTool, type TaskPromptOps } from "../../src/tool/task"
|
||||
import { Truncate } from "@/tool/truncate"
|
||||
import { ToolRegistry } from "@/tool/registry"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { disposeAllInstances } from "../fixture/fixture"
|
||||
import { testEffect } from "../lib/effect"
|
||||
|
||||
const originalBackgroundAgents = Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS
|
||||
|
||||
afterEach(async () => {
|
||||
Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS = originalBackgroundAgents
|
||||
await disposeAllInstances()
|
||||
})
|
||||
|
||||
@@ -26,9 +34,13 @@ const ref = {
|
||||
const it = testEffect(
|
||||
Layer.mergeAll(
|
||||
Agent.defaultLayer,
|
||||
BackgroundJob.defaultLayer,
|
||||
Bus.defaultLayer,
|
||||
Config.defaultLayer,
|
||||
CrossSpawnSpawner.defaultLayer,
|
||||
Session.defaultLayer,
|
||||
SessionRunState.defaultLayer,
|
||||
SessionStatus.defaultLayer,
|
||||
Truncate.defaultLayer,
|
||||
ToolRegistry.defaultLayer,
|
||||
),
|
||||
@@ -80,6 +92,7 @@ function stubOps(opts?: { onPrompt?: (input: SessionPrompt.PromptInput) => void;
|
||||
opts?.onPrompt?.(input)
|
||||
return reply(input, opts?.text ?? "done")
|
||||
}),
|
||||
loop: (input) => Effect.succeed(reply({ sessionID: input.sessionID, parts: [] }, opts?.text ?? "done")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,6 +307,7 @@ describe("tool.task", () => {
|
||||
ready.resolve(input)
|
||||
return cancelled.promise
|
||||
}).pipe(Effect.as(reply(input, "cancelled"))),
|
||||
loop: (input) => Effect.succeed(reply({ sessionID: input.sessionID, parts: [] }, "done")),
|
||||
}
|
||||
|
||||
const fiber = yield* def
|
||||
@@ -432,4 +446,318 @@ describe("tool.task", () => {
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
it.instance("rejects background execution when the experiment is disabled", () =>
|
||||
Effect.gen(function* () {
|
||||
Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS = false
|
||||
const { chat, assistant } = yield* seed()
|
||||
const tool = yield* TaskTool
|
||||
const def = yield* tool.init()
|
||||
|
||||
const exit = yield* def
|
||||
.execute(
|
||||
{
|
||||
description: "inspect bug",
|
||||
prompt: "look into the cache key path",
|
||||
subagent_type: "general",
|
||||
background: true,
|
||||
},
|
||||
{
|
||||
sessionID: chat.id,
|
||||
messageID: assistant.id,
|
||||
agent: "build",
|
||||
abort: new AbortController().signal,
|
||||
extra: { promptOps: stubOps() },
|
||||
messages: [],
|
||||
metadata: () => Effect.void,
|
||||
ask: () => Effect.void,
|
||||
},
|
||||
)
|
||||
.pipe(Effect.exit)
|
||||
|
||||
expect(Exit.isFailure(exit)).toBe(true)
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("execute launches background tasks without waiting for completion", () =>
|
||||
Effect.gen(function* () {
|
||||
Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS = true
|
||||
const jobs = yield* BackgroundJob.Service
|
||||
const { chat, assistant } = yield* seed()
|
||||
const tool = yield* TaskTool
|
||||
const def = yield* tool.init()
|
||||
|
||||
const result = yield* def.execute(
|
||||
{
|
||||
description: "inspect bug",
|
||||
prompt: "look into the cache key path",
|
||||
subagent_type: "general",
|
||||
background: true,
|
||||
},
|
||||
{
|
||||
sessionID: chat.id,
|
||||
messageID: assistant.id,
|
||||
agent: "build",
|
||||
abort: new AbortController().signal,
|
||||
extra: {
|
||||
promptOps: {
|
||||
...stubOps(),
|
||||
prompt: () => Effect.never,
|
||||
} satisfies TaskPromptOps,
|
||||
},
|
||||
messages: [],
|
||||
metadata: () => Effect.void,
|
||||
ask: () => Effect.void,
|
||||
},
|
||||
)
|
||||
|
||||
const job = yield* jobs.get(result.metadata.sessionId)
|
||||
expect(result.metadata.background).toBe(true)
|
||||
expect(result.output).toContain("state: running")
|
||||
expect(job?.status).toBe("running")
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("background tasks complete through the background job service", () =>
|
||||
Effect.gen(function* () {
|
||||
Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS = true
|
||||
const jobs = yield* BackgroundJob.Service
|
||||
const { chat, assistant } = yield* seed()
|
||||
const tool = yield* TaskTool
|
||||
const def = yield* tool.init()
|
||||
|
||||
const result = yield* def.execute(
|
||||
{
|
||||
description: "inspect bug",
|
||||
prompt: "look into the cache key path",
|
||||
subagent_type: "general",
|
||||
background: true,
|
||||
},
|
||||
{
|
||||
sessionID: chat.id,
|
||||
messageID: assistant.id,
|
||||
agent: "build",
|
||||
abort: new AbortController().signal,
|
||||
extra: { promptOps: stubOps({ text: "background done" }) },
|
||||
messages: [],
|
||||
metadata: () => Effect.void,
|
||||
ask: () => Effect.void,
|
||||
},
|
||||
)
|
||||
|
||||
const waited = yield* jobs.wait({ id: result.metadata.sessionId, timeout: 1_000 })
|
||||
expect(waited.timedOut).toBe(false)
|
||||
expect(waited.info?.status).toBe("completed")
|
||||
expect(waited.info?.output).toBe("background done")
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("background task completion does not wait for the parent resume loop", () =>
|
||||
Effect.gen(function* () {
|
||||
Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS = true
|
||||
const jobs = yield* BackgroundJob.Service
|
||||
const sessions = yield* Session.Service
|
||||
const { chat, assistant } = yield* seed()
|
||||
const tool = yield* TaskTool
|
||||
const def = yield* tool.init()
|
||||
|
||||
const result = yield* def.execute(
|
||||
{
|
||||
description: "inspect bug",
|
||||
prompt: "look into the cache key path",
|
||||
subagent_type: "general",
|
||||
background: true,
|
||||
},
|
||||
{
|
||||
sessionID: chat.id,
|
||||
messageID: assistant.id,
|
||||
agent: "build",
|
||||
abort: new AbortController().signal,
|
||||
extra: {
|
||||
promptOps: {
|
||||
...stubOps({ text: "background done" }),
|
||||
prompt: (input) =>
|
||||
input.noReply
|
||||
? Effect.gen(function* () {
|
||||
const user = yield* sessions.updateMessage({
|
||||
id: input.messageID ?? MessageID.ascending(),
|
||||
role: "user",
|
||||
sessionID: input.sessionID,
|
||||
agent: input.agent ?? "build",
|
||||
model: input.model ?? ref,
|
||||
time: { created: Date.now() },
|
||||
})
|
||||
const parts = input.parts.map((part) => ({
|
||||
...part,
|
||||
id: part.id ?? PartID.ascending(),
|
||||
messageID: user.id,
|
||||
sessionID: input.sessionID,
|
||||
}))
|
||||
yield* Effect.forEach(parts, (part) => sessions.updatePart(part), { discard: true })
|
||||
return { info: user, parts }
|
||||
})
|
||||
: Effect.succeed(reply(input, "background done")),
|
||||
loop: () => Effect.never,
|
||||
} satisfies TaskPromptOps,
|
||||
},
|
||||
messages: [],
|
||||
metadata: () => Effect.void,
|
||||
ask: () => Effect.void,
|
||||
},
|
||||
)
|
||||
|
||||
const waited = yield* jobs.wait({ id: result.metadata.sessionId, timeout: 1_000 })
|
||||
expect(waited.timedOut).toBe(false)
|
||||
expect(waited.info?.status).toBe("completed")
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("removing the parent session cancels running background tasks", () =>
|
||||
Effect.gen(function* () {
|
||||
Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS = true
|
||||
const jobs = yield* BackgroundJob.Service
|
||||
const sessions = yield* Session.Service
|
||||
const { chat, assistant } = yield* seed()
|
||||
const tool = yield* TaskTool
|
||||
const def = yield* tool.init()
|
||||
|
||||
const result = yield* def.execute(
|
||||
{
|
||||
description: "inspect bug",
|
||||
prompt: "look into the cache key path",
|
||||
subagent_type: "general",
|
||||
background: true,
|
||||
},
|
||||
{
|
||||
sessionID: chat.id,
|
||||
messageID: assistant.id,
|
||||
agent: "build",
|
||||
abort: new AbortController().signal,
|
||||
extra: {
|
||||
promptOps: {
|
||||
...stubOps(),
|
||||
prompt: () => Effect.never,
|
||||
} satisfies TaskPromptOps,
|
||||
},
|
||||
messages: [],
|
||||
metadata: () => Effect.void,
|
||||
ask: () => Effect.void,
|
||||
},
|
||||
)
|
||||
|
||||
yield* sessions.remove(chat.id)
|
||||
const waited = yield* jobs.wait({ id: result.metadata.sessionId, timeout: 1_000 })
|
||||
expect(waited.timedOut).toBe(false)
|
||||
expect(waited.info?.status).toBe("cancelled")
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("removing the child task session cancels its running background task", () =>
|
||||
Effect.gen(function* () {
|
||||
Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS = true
|
||||
const jobs = yield* BackgroundJob.Service
|
||||
const sessions = yield* Session.Service
|
||||
const { chat, assistant } = yield* seed()
|
||||
const tool = yield* TaskTool
|
||||
const def = yield* tool.init()
|
||||
|
||||
const result = yield* def.execute(
|
||||
{
|
||||
description: "inspect bug",
|
||||
prompt: "look into the cache key path",
|
||||
subagent_type: "general",
|
||||
background: true,
|
||||
},
|
||||
{
|
||||
sessionID: chat.id,
|
||||
messageID: assistant.id,
|
||||
agent: "build",
|
||||
abort: new AbortController().signal,
|
||||
extra: {
|
||||
promptOps: {
|
||||
...stubOps(),
|
||||
prompt: () => Effect.never,
|
||||
} satisfies TaskPromptOps,
|
||||
},
|
||||
messages: [],
|
||||
metadata: () => Effect.void,
|
||||
ask: () => Effect.void,
|
||||
},
|
||||
)
|
||||
|
||||
yield* sessions.remove(result.metadata.sessionId)
|
||||
const waited = yield* jobs.wait({ id: result.metadata.sessionId, timeout: 1_000 })
|
||||
expect(waited.timedOut).toBe(false)
|
||||
expect(waited.info?.status).toBe("cancelled")
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("cancelling the parent run cancels running background tasks", () =>
|
||||
Effect.gen(function* () {
|
||||
Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS = true
|
||||
const jobs = yield* BackgroundJob.Service
|
||||
const runState = yield* SessionRunState.Service
|
||||
const { chat, assistant } = yield* seed()
|
||||
const tool = yield* TaskTool
|
||||
const def = yield* tool.init()
|
||||
|
||||
const result = yield* def.execute(
|
||||
{
|
||||
description: "inspect bug",
|
||||
prompt: "look into the cache key path",
|
||||
subagent_type: "general",
|
||||
background: true,
|
||||
},
|
||||
{
|
||||
sessionID: chat.id,
|
||||
messageID: assistant.id,
|
||||
agent: "build",
|
||||
abort: new AbortController().signal,
|
||||
extra: {
|
||||
promptOps: {
|
||||
...stubOps(),
|
||||
prompt: () => Effect.never,
|
||||
} satisfies TaskPromptOps,
|
||||
},
|
||||
messages: [],
|
||||
metadata: () => Effect.void,
|
||||
ask: () => Effect.void,
|
||||
},
|
||||
)
|
||||
|
||||
yield* runState.cancel(chat.id)
|
||||
const waited = yield* jobs.wait({ id: result.metadata.sessionId, timeout: 1_000 })
|
||||
expect(waited.timedOut).toBe(false)
|
||||
expect(waited.info?.status).toBe("cancelled")
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance("cancelling a parent run recursively cancels descendant background tasks", () =>
|
||||
Effect.gen(function* () {
|
||||
const jobs = yield* BackgroundJob.Service
|
||||
const runState = yield* SessionRunState.Service
|
||||
const sessions = yield* Session.Service
|
||||
const { chat } = yield* seed()
|
||||
const child = yield* sessions.create({ parentID: chat.id, title: "child" })
|
||||
const grandchild = yield* sessions.create({ parentID: child.id, title: "grandchild" })
|
||||
|
||||
yield* jobs.start({
|
||||
id: child.id,
|
||||
type: "task",
|
||||
metadata: { parentSessionId: chat.id, sessionId: child.id },
|
||||
run: Effect.never,
|
||||
})
|
||||
yield* jobs.start({
|
||||
id: grandchild.id,
|
||||
type: "task",
|
||||
metadata: { parentSessionId: child.id, sessionId: grandchild.id },
|
||||
run: Effect.never,
|
||||
})
|
||||
|
||||
yield* runState.cancel(chat.id)
|
||||
|
||||
expect((yield* jobs.get(child.id))?.status).toBe("cancelled")
|
||||
expect((yield* jobs.get(grandchild.id))?.status).toBe("cancelled")
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
95
packages/opencode/test/tool/task_status.test.ts
Normal file
95
packages/opencode/test/tool/task_status.test.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
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 { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { disposeAllInstances } from "../fixture/fixture"
|
||||
import { testEffect } from "../lib/effect"
|
||||
|
||||
const originalBackgroundAgents = Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS
|
||||
|
||||
afterEach(async () => {
|
||||
Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS = originalBackgroundAgents
|
||||
await disposeAllInstances()
|
||||
})
|
||||
|
||||
const it = testEffect(
|
||||
Layer.mergeAll(
|
||||
Agent.defaultLayer,
|
||||
BackgroundJob.defaultLayer,
|
||||
Bus.defaultLayer,
|
||||
CrossSpawnSpawner.defaultLayer,
|
||||
Session.defaultLayer,
|
||||
SessionStatus.defaultLayer,
|
||||
Truncate.defaultLayer,
|
||||
),
|
||||
)
|
||||
|
||||
describe("tool.task_status", () => {
|
||||
it.instance("returns completed background job output", () =>
|
||||
Effect.gen(function* () {
|
||||
Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS = true
|
||||
const jobs = yield* BackgroundJob.Service
|
||||
const sessions = yield* Session.Service
|
||||
const tool = yield* TaskStatusTool
|
||||
const def = yield* tool.init()
|
||||
const chat = yield* sessions.create({})
|
||||
|
||||
yield* jobs.start({ id: chat.id, type: "task", run: Effect.succeed("all done") })
|
||||
|
||||
const result = yield* def.execute(
|
||||
{ task_id: chat.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* () {
|
||||
Flag.OPENCODE_EXPERIMENTAL_BACKGROUND_AGENTS = true
|
||||
const jobs = yield* BackgroundJob.Service
|
||||
const sessions = yield* Session.Service
|
||||
const tool = yield* TaskStatusTool
|
||||
const def = yield* tool.init()
|
||||
const chat = yield* sessions.create({})
|
||||
|
||||
yield* jobs.start({ id: chat.id, type: "task", run: Effect.never })
|
||||
|
||||
const result = yield* def.execute(
|
||||
{ task_id: chat.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)
|
||||
}),
|
||||
)
|
||||
})
|
||||
Reference in New Issue
Block a user