da251c9b30
Rename BackgroundTask and attempt ID fields to camelCase across background-agent consumers while moving BackgroundManager construction to a single config object. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
225 lines
6.5 KiB
TypeScript
225 lines
6.5 KiB
TypeScript
const { describe, test, expect, beforeEach, afterEach, mock } = require("bun:test")
|
|
|
|
describe("executeUnstableAgentTask - interrupt detection", () => {
|
|
beforeEach(() => {
|
|
//#given - configure fast timing for all tests
|
|
const { __setTimingConfig } = require("./timing")
|
|
__setTimingConfig({
|
|
POLL_INTERVAL_MS: 10,
|
|
MIN_STABILITY_TIME_MS: 0,
|
|
STABILITY_POLLS_REQUIRED: 1,
|
|
MAX_POLL_TIME_MS: 500,
|
|
WAIT_FOR_SESSION_TIMEOUT_MS: 100,
|
|
WAIT_FOR_SESSION_INTERVAL_MS: 10,
|
|
})
|
|
})
|
|
|
|
afterEach(() => {
|
|
//#given - reset timing after each test
|
|
const { __resetTimingConfig } = require("./timing")
|
|
__resetTimingConfig()
|
|
mock.restore()
|
|
})
|
|
|
|
test("should return error immediately when background task becomes interrupted during polling", async () => {
|
|
//#given - a background task that gets interrupted on first poll check
|
|
const taskState = {
|
|
id: "bg_test_interrupt",
|
|
sessionId: "ses_test_interrupt",
|
|
status: "interrupt" as string,
|
|
description: "test interrupted task",
|
|
prompt: "test prompt",
|
|
agent: "sisyphus-junior",
|
|
error: "Agent not found" as string | undefined,
|
|
}
|
|
|
|
const launchState = { ...taskState, status: "running" as string, error: undefined as string | undefined }
|
|
|
|
const mockManager = {
|
|
launch: async () => launchState,
|
|
getTask: () => taskState,
|
|
}
|
|
|
|
const mockClient = {
|
|
session: {
|
|
status: async () => ({ data: { [taskState.sessionId!]: { type: "idle" } } }),
|
|
messages: async () => ({ data: [] }),
|
|
},
|
|
}
|
|
|
|
const { executeUnstableAgentTask } = require("./unstable-agent-task")
|
|
|
|
const args = {
|
|
prompt: "test prompt",
|
|
description: "test task",
|
|
category: "test",
|
|
load_skills: [],
|
|
run_in_background: false,
|
|
}
|
|
|
|
const mockCtx = {
|
|
sessionID: "parent-session",
|
|
callID: "call-123",
|
|
metadata: () => {},
|
|
}
|
|
|
|
const mockExecutorCtx = {
|
|
manager: mockManager,
|
|
client: mockClient,
|
|
directory: "/tmp",
|
|
}
|
|
|
|
const parentContext = {
|
|
sessionID: "parent-session",
|
|
messageID: "msg-123",
|
|
}
|
|
|
|
//#when - executeUnstableAgentTask encounters an interrupted task
|
|
const startTime = Date.now()
|
|
const result = await executeUnstableAgentTask(
|
|
args, mockCtx, mockExecutorCtx, parentContext,
|
|
"test-agent", undefined, undefined, "test-model"
|
|
)
|
|
const elapsed = Date.now() - startTime
|
|
|
|
//#then - should return quickly with interrupt error, not hang until MAX_POLL_TIME_MS
|
|
expect(result).toContain("interrupt")
|
|
expect(result.toLowerCase()).toContain("agent not found")
|
|
expect(elapsed).toBeLessThan(400)
|
|
})
|
|
|
|
test("should return error immediately when background task becomes errored during polling", async () => {
|
|
//#given - a background task that is already errored when poll checks
|
|
const taskState = {
|
|
id: "bg_test_error",
|
|
sessionId: "ses_test_error",
|
|
status: "error" as string,
|
|
description: "test error task",
|
|
prompt: "test prompt",
|
|
agent: "sisyphus-junior",
|
|
error: "Rate limit exceeded" as string | undefined,
|
|
}
|
|
|
|
const launchState = { ...taskState, status: "running" as string, error: undefined as string | undefined }
|
|
|
|
const mockManager = {
|
|
launch: async () => launchState,
|
|
getTask: () => taskState,
|
|
}
|
|
|
|
const mockClient = {
|
|
session: {
|
|
status: async () => ({ data: { [taskState.sessionId!]: { type: "idle" } } }),
|
|
messages: async () => ({ data: [] }),
|
|
},
|
|
}
|
|
|
|
const { executeUnstableAgentTask } = require("./unstable-agent-task")
|
|
|
|
const args = {
|
|
prompt: "test prompt",
|
|
description: "test task",
|
|
category: "test",
|
|
load_skills: [],
|
|
run_in_background: false,
|
|
}
|
|
|
|
const mockCtx = {
|
|
sessionID: "parent-session",
|
|
callID: "call-123",
|
|
metadata: () => {},
|
|
}
|
|
|
|
const mockExecutorCtx = {
|
|
manager: mockManager,
|
|
client: mockClient,
|
|
directory: "/tmp",
|
|
}
|
|
|
|
const parentContext = {
|
|
sessionID: "parent-session",
|
|
messageID: "msg-123",
|
|
}
|
|
|
|
//#when - executeUnstableAgentTask encounters an errored task
|
|
const startTime = Date.now()
|
|
const result = await executeUnstableAgentTask(
|
|
args, mockCtx, mockExecutorCtx, parentContext,
|
|
"test-agent", undefined, undefined, "test-model"
|
|
)
|
|
const elapsed = Date.now() - startTime
|
|
|
|
//#then - should return quickly with error, not hang until MAX_POLL_TIME_MS
|
|
expect(result).toContain("error")
|
|
expect(result.toLowerCase()).toContain("rate limit exceeded")
|
|
expect(elapsed).toBeLessThan(400)
|
|
})
|
|
|
|
test("should return error immediately when background task becomes cancelled during polling", async () => {
|
|
//#given - a background task that is already cancelled when poll checks
|
|
const taskState = {
|
|
id: "bg_test_cancel",
|
|
sessionId: "ses_test_cancel",
|
|
status: "cancelled" as string,
|
|
description: "test cancelled task",
|
|
prompt: "test prompt",
|
|
agent: "sisyphus-junior",
|
|
error: "Stale timeout" as string | undefined,
|
|
}
|
|
|
|
const launchState = { ...taskState, status: "running" as string, error: undefined as string | undefined }
|
|
|
|
const mockManager = {
|
|
launch: async () => launchState,
|
|
getTask: () => taskState,
|
|
}
|
|
|
|
const mockClient = {
|
|
session: {
|
|
status: async () => ({ data: { [taskState.sessionId!]: { type: "idle" } } }),
|
|
messages: async () => ({ data: [] }),
|
|
},
|
|
}
|
|
|
|
const { executeUnstableAgentTask } = require("./unstable-agent-task")
|
|
|
|
const args = {
|
|
prompt: "test prompt",
|
|
description: "test task",
|
|
category: "test",
|
|
load_skills: [],
|
|
run_in_background: false,
|
|
}
|
|
|
|
const mockCtx = {
|
|
sessionID: "parent-session",
|
|
callID: "call-123",
|
|
metadata: () => {},
|
|
}
|
|
|
|
const mockExecutorCtx = {
|
|
manager: mockManager,
|
|
client: mockClient,
|
|
directory: "/tmp",
|
|
}
|
|
|
|
const parentContext = {
|
|
sessionID: "parent-session",
|
|
messageID: "msg-123",
|
|
}
|
|
|
|
//#when - executeUnstableAgentTask encounters a cancelled task
|
|
const startTime = Date.now()
|
|
const result = await executeUnstableAgentTask(
|
|
args, mockCtx, mockExecutorCtx, parentContext,
|
|
"test-agent", undefined, undefined, "test-model"
|
|
)
|
|
const elapsed = Date.now() - startTime
|
|
|
|
//#then - should return quickly with cancel info, not hang until MAX_POLL_TIME_MS
|
|
expect(result).toContain("cancel")
|
|
expect(result.toLowerCase()).toContain("stale timeout")
|
|
expect(elapsed).toBeLessThan(400)
|
|
})
|
|
})
|