Merge pull request #3754 from code-yeongyu/refactor/background-task-naming-cleanup
refactor(background-agent): normalize task ID field naming to camelCase
This commit is contained in:
+13
-15
@@ -68,12 +68,11 @@ export function createManagers(args: {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const backgroundManager = new deps.BackgroundManagerClass(
|
const backgroundManager = new deps.BackgroundManagerClass({
|
||||||
ctx,
|
pluginContext: ctx,
|
||||||
pluginConfig.background_task,
|
config: pluginConfig.background_task,
|
||||||
{
|
tmuxConfig,
|
||||||
tmuxConfig,
|
onSubagentSessionCreated: async (event: SubagentSessionCreatedEvent) => {
|
||||||
onSubagentSessionCreated: async (event: SubagentSessionCreatedEvent) => {
|
|
||||||
log("[create-managers] onSubagentSessionCreated callback received", {
|
log("[create-managers] onSubagentSessionCreated callback received", {
|
||||||
sessionID: event.sessionID,
|
sessionID: event.sessionID,
|
||||||
parentID: event.parentID,
|
parentID: event.parentID,
|
||||||
@@ -104,16 +103,15 @@ export function createManagers(args: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log("[create-managers] onSubagentSessionCreated callback completed")
|
log("[create-managers] onSubagentSessionCreated callback completed")
|
||||||
},
|
|
||||||
onShutdown: async () => {
|
|
||||||
await tmuxSessionManager.cleanup().catch((error) => {
|
|
||||||
log("[create-managers] tmux cleanup error during shutdown:", error)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
enableParentSessionNotifications: backgroundNotificationHookEnabled,
|
|
||||||
modelFallbackControllerAccessor,
|
|
||||||
},
|
},
|
||||||
)
|
onShutdown: async () => {
|
||||||
|
await tmuxSessionManager.cleanup().catch((error) => {
|
||||||
|
log("[create-managers] tmux cleanup error during shutdown:", error)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
enableParentSessionNotifications: backgroundNotificationHookEnabled,
|
||||||
|
modelFallbackControllerAccessor,
|
||||||
|
})
|
||||||
|
|
||||||
deps.initTaskToastManagerFn(ctx.client)
|
deps.initTaskToastManagerFn(ctx.client)
|
||||||
|
|
||||||
|
|||||||
@@ -3,28 +3,28 @@ import type { BackgroundTask, BackgroundTaskAttempt, BackgroundTaskStatus } from
|
|||||||
|
|
||||||
type TerminalAttemptStatus = Extract<BackgroundTaskStatus, "completed" | "error" | "cancelled" | "interrupt">
|
type TerminalAttemptStatus = Extract<BackgroundTaskStatus, "completed" | "error" | "cancelled" | "interrupt">
|
||||||
|
|
||||||
function toAttemptModel(model: DelegatedModelConfig | undefined): Pick<BackgroundTaskAttempt, "providerID" | "modelID" | "variant"> {
|
function toAttemptModel(model: DelegatedModelConfig | undefined): Pick<BackgroundTaskAttempt, "providerId" | "modelId" | "variant"> {
|
||||||
return {
|
return {
|
||||||
providerID: model?.providerID,
|
providerId: model?.providerID,
|
||||||
modelID: model?.modelID,
|
modelId: model?.modelID,
|
||||||
variant: model?.variant,
|
variant: model?.variant,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toTaskModel(attempt: BackgroundTaskAttempt): DelegatedModelConfig | undefined {
|
function toTaskModel(attempt: BackgroundTaskAttempt): DelegatedModelConfig | undefined {
|
||||||
if (!attempt.providerID || !attempt.modelID) {
|
if (!attempt.providerId || !attempt.modelId) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
providerID: attempt.providerID,
|
providerID: attempt.providerId,
|
||||||
modelID: attempt.modelID,
|
modelID: attempt.modelId,
|
||||||
...(attempt.variant ? { variant: attempt.variant } : {}),
|
...(attempt.variant ? { variant: attempt.variant } : {}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAttemptIndex(task: BackgroundTask, attemptID: string): number {
|
function getAttemptIndex(task: BackgroundTask, attemptID: string): number {
|
||||||
return task.attempts?.findIndex((attempt) => attempt.attemptID === attemptID) ?? -1
|
return task.attempts?.findIndex((attempt) => attempt.attemptId === attemptID) ?? -1
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAttempt(task: BackgroundTask, attemptID: string): BackgroundTaskAttempt | undefined {
|
function getAttempt(task: BackgroundTask, attemptID: string): BackgroundTaskAttempt | undefined {
|
||||||
@@ -54,9 +54,9 @@ export function ensureCurrentAttempt(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const attempt: BackgroundTaskAttempt = {
|
const attempt: BackgroundTaskAttempt = {
|
||||||
attemptID: `att_${crypto.randomUUID().slice(0, 8)}`,
|
attemptId: `att_${crypto.randomUUID().slice(0, 8)}`,
|
||||||
attemptNumber: (task.attempts?.length ?? 0) + 1,
|
attemptNumber: (task.attempts?.length ?? 0) + 1,
|
||||||
sessionID: task.sessionID,
|
sessionId: task.sessionId,
|
||||||
...toAttemptModel(model),
|
...toAttemptModel(model),
|
||||||
status: task.status,
|
status: task.status,
|
||||||
error: task.error,
|
error: task.error,
|
||||||
@@ -65,7 +65,7 @@ export function ensureCurrentAttempt(
|
|||||||
}
|
}
|
||||||
|
|
||||||
task.attempts = [...(task.attempts ?? []), attempt]
|
task.attempts = [...(task.attempts ?? []), attempt]
|
||||||
task.currentAttemptID = attempt.attemptID
|
task.currentAttemptID = attempt.attemptId
|
||||||
return attempt
|
return attempt
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ export function projectTaskFromCurrentAttempt(task: BackgroundTask): BackgroundT
|
|||||||
}
|
}
|
||||||
|
|
||||||
task.status = currentAttempt.status
|
task.status = currentAttempt.status
|
||||||
task.sessionID = currentAttempt.sessionID
|
task.sessionId = currentAttempt.sessionId
|
||||||
task.startedAt = currentAttempt.startedAt
|
task.startedAt = currentAttempt.startedAt
|
||||||
task.completedAt = currentAttempt.completedAt
|
task.completedAt = currentAttempt.completedAt
|
||||||
task.error = currentAttempt.error
|
task.error = currentAttempt.error
|
||||||
@@ -87,16 +87,16 @@ export function projectTaskFromCurrentAttempt(task: BackgroundTask): BackgroundT
|
|||||||
|
|
||||||
export function startAttempt(task: BackgroundTask, model: DelegatedModelConfig | undefined): BackgroundTaskAttempt {
|
export function startAttempt(task: BackgroundTask, model: DelegatedModelConfig | undefined): BackgroundTaskAttempt {
|
||||||
const attempt: BackgroundTaskAttempt = {
|
const attempt: BackgroundTaskAttempt = {
|
||||||
attemptID: `att_${crypto.randomUUID().slice(0, 8)}`,
|
attemptId: `att_${crypto.randomUUID().slice(0, 8)}`,
|
||||||
attemptNumber: (task.attempts?.length ?? 0) + 1,
|
attemptNumber: (task.attempts?.length ?? 0) + 1,
|
||||||
...toAttemptModel(model),
|
...toAttemptModel(model),
|
||||||
status: "pending",
|
status: "pending",
|
||||||
}
|
}
|
||||||
|
|
||||||
task.attempts = [...(task.attempts ?? []), attempt]
|
task.attempts = [...(task.attempts ?? []), attempt]
|
||||||
task.currentAttemptID = attempt.attemptID
|
task.currentAttemptID = attempt.attemptId
|
||||||
task.status = "pending"
|
task.status = "pending"
|
||||||
task.sessionID = undefined
|
task.sessionId = undefined
|
||||||
task.startedAt = undefined
|
task.startedAt = undefined
|
||||||
task.completedAt = undefined
|
task.completedAt = undefined
|
||||||
task.error = undefined
|
task.error = undefined
|
||||||
@@ -121,13 +121,13 @@ export function bindAttemptSession(
|
|||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
attempt.sessionID = sessionID
|
attempt.sessionId = sessionID
|
||||||
attempt.status = "running"
|
attempt.status = "running"
|
||||||
attempt.startedAt = new Date()
|
attempt.startedAt = new Date()
|
||||||
attempt.completedAt = undefined
|
attempt.completedAt = undefined
|
||||||
attempt.error = undefined
|
attempt.error = undefined
|
||||||
attempt.providerID = model?.providerID ?? attempt.providerID
|
attempt.providerId = model?.providerID ?? attempt.providerId
|
||||||
attempt.modelID = model?.modelID ?? attempt.modelID
|
attempt.modelId = model?.modelID ?? attempt.modelId
|
||||||
attempt.variant = model?.variant ?? attempt.variant
|
attempt.variant = model?.variant ?? attempt.variant
|
||||||
|
|
||||||
return getCurrentAttempt(projectTaskFromCurrentAttempt(task))
|
return getCurrentAttempt(projectTaskFromCurrentAttempt(task))
|
||||||
@@ -170,5 +170,5 @@ export function scheduleRetryAttempt(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function findAttemptBySession(task: BackgroundTask, sessionID: string): BackgroundTaskAttempt | undefined {
|
export function findAttemptBySession(task: BackgroundTask, sessionID: string): BackgroundTaskAttempt | undefined {
|
||||||
return task.attempts?.find((attempt) => attempt.sessionID === sessionID)
|
return task.attempts?.find((attempt) => attempt.sessionId === sessionID)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,20 +164,20 @@ Use \`background_output(task_id="<id>")\` to retrieve each result.
|
|||||||
status: "completed",
|
status: "completed",
|
||||||
attempts: [
|
attempts: [
|
||||||
{
|
{
|
||||||
attemptID: "att-1",
|
attemptId: "att-1",
|
||||||
attemptNumber: 1,
|
attemptNumber: 1,
|
||||||
sessionID: "ses-primary",
|
sessionId: "ses-primary",
|
||||||
providerID: "genai-proxy-openai",
|
providerId: "genai-proxy-openai",
|
||||||
modelID: "gpt-5.4-mini",
|
modelId: "gpt-5.4-mini",
|
||||||
status: "error",
|
status: "error",
|
||||||
error: "Forbidden: Selected provider is forbidden",
|
error: "Forbidden: Selected provider is forbidden",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
attemptID: "att-2",
|
attemptId: "att-2",
|
||||||
attemptNumber: 2,
|
attemptNumber: 2,
|
||||||
sessionID: "ses-fallback",
|
sessionId: "ses-fallback",
|
||||||
providerID: "anthropic",
|
providerId: "anthropic",
|
||||||
modelID: "claude-haiku-4.5",
|
modelId: "claude-haiku-4.5",
|
||||||
status: "completed",
|
status: "completed",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -193,20 +193,20 @@ Use \`background_output(task_id="<id>")\` to retrieve each result.
|
|||||||
status: "completed",
|
status: "completed",
|
||||||
attempts: [
|
attempts: [
|
||||||
{
|
{
|
||||||
attemptID: "att-1",
|
attemptId: "att-1",
|
||||||
attemptNumber: 1,
|
attemptNumber: 1,
|
||||||
sessionID: "ses-primary",
|
sessionId: "ses-primary",
|
||||||
providerID: "genai-proxy-openai",
|
providerId: "genai-proxy-openai",
|
||||||
modelID: "gpt-5.4-mini",
|
modelId: "gpt-5.4-mini",
|
||||||
status: "error",
|
status: "error",
|
||||||
error: "Forbidden: Selected provider is forbidden",
|
error: "Forbidden: Selected provider is forbidden",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
attemptID: "att-2",
|
attemptId: "att-2",
|
||||||
attemptNumber: 2,
|
attemptNumber: 2,
|
||||||
sessionID: "ses-fallback",
|
sessionId: "ses-fallback",
|
||||||
providerID: "anthropic",
|
providerId: "anthropic",
|
||||||
modelID: "claude-haiku-4.5",
|
modelId: "claude-haiku-4.5",
|
||||||
status: "completed",
|
status: "completed",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -11,16 +11,16 @@ export interface BackgroundTaskNotificationTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function formatAttemptModel(attempt: BackgroundTaskAttempt): string {
|
function formatAttemptModel(attempt: BackgroundTaskAttempt): string {
|
||||||
if (attempt.providerID && attempt.modelID) {
|
if (attempt.providerId && attempt.modelId) {
|
||||||
return `${attempt.providerID}/${attempt.modelID}`
|
return `${attempt.providerId}/${attempt.modelId}`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attempt.modelID) {
|
if (attempt.modelId) {
|
||||||
return attempt.modelID
|
return attempt.modelId
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attempt.providerID) {
|
if (attempt.providerId) {
|
||||||
return attempt.providerID
|
return attempt.providerId
|
||||||
}
|
}
|
||||||
|
|
||||||
return "unknown-model"
|
return "unknown-model"
|
||||||
@@ -34,7 +34,7 @@ function formatAttemptTimeline(task: BackgroundTaskNotificationTask): string {
|
|||||||
const lines = task.attempts
|
const lines = task.attempts
|
||||||
.map((attempt) => {
|
.map((attempt) => {
|
||||||
const attemptLines = [
|
const attemptLines = [
|
||||||
` - Attempt ${attempt.attemptNumber} — ${attempt.status.toUpperCase()} — ${formatAttemptModel(attempt)} — ${attempt.sessionID ?? "unknown"}`,
|
` - Attempt ${attempt.attemptNumber} — ${attempt.status.toUpperCase()} — ${formatAttemptModel(attempt)} — ${attempt.sessionId ?? "unknown"}`,
|
||||||
]
|
]
|
||||||
|
|
||||||
if (attempt.status !== "completed" && attempt.error) {
|
if (attempt.status !== "completed" && attempt.error) {
|
||||||
|
|||||||
@@ -22,24 +22,24 @@ function createBackgroundManager(config?: { defaultConcurrency?: number }): Back
|
|||||||
Reflect.set(client.session, "prompt", async () => ({ data: { info: {}, parts: [] } }))
|
Reflect.set(client.session, "prompt", async () => ({ data: { info: {}, parts: [] } }))
|
||||||
Reflect.set(client.session, "promptAsync", async () => ({ data: undefined }))
|
Reflect.set(client.session, "promptAsync", async () => ({ data: undefined }))
|
||||||
|
|
||||||
const manager = new BackgroundManager({
|
const manager = new BackgroundManager({ pluginContext: {
|
||||||
$: {} as PluginInput["$"],
|
$: {} as PluginInput["$"],
|
||||||
client,
|
client,
|
||||||
directory,
|
directory,
|
||||||
project: {} as PluginInput["project"],
|
project: {} as PluginInput["project"],
|
||||||
serverUrl: new URL("http://localhost"),
|
serverUrl: new URL("http://localhost"),
|
||||||
worktree: directory,
|
worktree: directory,
|
||||||
}, config)
|
}, config: config })
|
||||||
managersToShutdown.push(manager)
|
managersToShutdown.push(manager)
|
||||||
return manager
|
return manager
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMockTask(overrides: Partial<BackgroundTask> & { id: string; parentSessionID: string }): BackgroundTask {
|
function createMockTask(overrides: Partial<BackgroundTask> & { id: string; parentSessionId: string }): BackgroundTask {
|
||||||
return {
|
return {
|
||||||
id: overrides.id,
|
id: overrides.id,
|
||||||
sessionID: overrides.sessionID,
|
sessionId: overrides.sessionId,
|
||||||
parentSessionID: overrides.parentSessionID,
|
parentSessionId: overrides.parentSessionId,
|
||||||
parentMessageID: overrides.parentMessageID ?? "parent-message-id",
|
parentMessageId: overrides.parentMessageId ?? "parent-message-id",
|
||||||
description: overrides.description ?? "test task",
|
description: overrides.description ?? "test task",
|
||||||
prompt: overrides.prompt ?? "test prompt",
|
prompt: overrides.prompt ?? "test prompt",
|
||||||
agent: overrides.agent ?? "test-agent",
|
agent: overrides.agent ?? "test-agent",
|
||||||
@@ -90,12 +90,12 @@ describe("BackgroundManager.cancelTask cleanup", () => {
|
|||||||
const manager = createBackgroundManager()
|
const manager = createBackgroundManager()
|
||||||
const task = createMockTask({
|
const task = createMockTask({
|
||||||
id: "task-skip-notification-cleanup",
|
id: "task-skip-notification-cleanup",
|
||||||
parentSessionID: "parent-session-skip-notification-cleanup",
|
parentSessionId: "parent-session-skip-notification-cleanup",
|
||||||
sessionID: "session-skip-notification-cleanup",
|
sessionId: "session-skip-notification-cleanup",
|
||||||
})
|
})
|
||||||
|
|
||||||
getTaskMap(manager).set(task.id, task)
|
getTaskMap(manager).set(task.id, task)
|
||||||
getPendingByParent(manager).set(task.parentSessionID, new Set([task.id]))
|
getPendingByParent(manager).set(task.parentSessionId, new Set([task.id]))
|
||||||
|
|
||||||
// when
|
// when
|
||||||
const cancelled = await manager.cancelTask(task.id, {
|
const cancelled = await manager.cancelTask(task.id, {
|
||||||
@@ -105,7 +105,7 @@ describe("BackgroundManager.cancelTask cleanup", () => {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
expect(cancelled).toBe(true)
|
expect(cancelled).toBe(true)
|
||||||
expect(getPendingByParent(manager).get(task.parentSessionID)).toBeUndefined()
|
expect(getPendingByParent(manager).get(task.parentSessionId)).toBeUndefined()
|
||||||
runScheduledCleanup(manager, task.id)
|
runScheduledCleanup(manager, task.id)
|
||||||
expect(manager.getTask(task.id)).toBeUndefined()
|
expect(manager.getTask(task.id)).toBeUndefined()
|
||||||
})
|
})
|
||||||
@@ -115,12 +115,12 @@ describe("BackgroundManager.cancelTask cleanup", () => {
|
|||||||
const manager = createBackgroundManager()
|
const manager = createBackgroundManager()
|
||||||
const task = createMockTask({
|
const task = createMockTask({
|
||||||
id: "task-notify-cleanup",
|
id: "task-notify-cleanup",
|
||||||
parentSessionID: "parent-session-notify-cleanup",
|
parentSessionId: "parent-session-notify-cleanup",
|
||||||
sessionID: "session-notify-cleanup",
|
sessionId: "session-notify-cleanup",
|
||||||
})
|
})
|
||||||
|
|
||||||
getTaskMap(manager).set(task.id, task)
|
getTaskMap(manager).set(task.id, task)
|
||||||
getPendingByParent(manager).set(task.parentSessionID, new Set([task.id]))
|
getPendingByParent(manager).set(task.parentSessionId, new Set([task.id]))
|
||||||
|
|
||||||
// when
|
// when
|
||||||
const cancelled = await manager.cancelTask(task.id, {
|
const cancelled = await manager.cancelTask(task.id, {
|
||||||
@@ -143,13 +143,13 @@ describe("BackgroundManager.cancelTask cleanup", () => {
|
|||||||
|
|
||||||
const runningTask = createMockTask({
|
const runningTask = createMockTask({
|
||||||
id: "task-running-before-cancel",
|
id: "task-running-before-cancel",
|
||||||
parentSessionID: "parent-session-concurrency-cleanup",
|
parentSessionId: "parent-session-concurrency-cleanup",
|
||||||
sessionID: "session-running-before-cancel",
|
sessionId: "session-running-before-cancel",
|
||||||
concurrencyKey,
|
concurrencyKey,
|
||||||
})
|
})
|
||||||
const pendingTask = createMockTask({
|
const pendingTask = createMockTask({
|
||||||
id: "task-pending-after-cancel",
|
id: "task-pending-after-cancel",
|
||||||
parentSessionID: runningTask.parentSessionID,
|
parentSessionId: runningTask.parentSessionId,
|
||||||
status: "pending",
|
status: "pending",
|
||||||
startedAt: undefined,
|
startedAt: undefined,
|
||||||
queuedAt: new Date(),
|
queuedAt: new Date(),
|
||||||
@@ -159,20 +159,20 @@ describe("BackgroundManager.cancelTask cleanup", () => {
|
|||||||
agent: pendingTask.agent,
|
agent: pendingTask.agent,
|
||||||
description: pendingTask.description,
|
description: pendingTask.description,
|
||||||
model: pendingTask.model,
|
model: pendingTask.model,
|
||||||
parentMessageID: pendingTask.parentMessageID,
|
parentMessageId: pendingTask.parentMessageId,
|
||||||
parentSessionID: pendingTask.parentSessionID,
|
parentSessionId: pendingTask.parentSessionId,
|
||||||
prompt: pendingTask.prompt,
|
prompt: pendingTask.prompt,
|
||||||
}
|
}
|
||||||
|
|
||||||
getTaskMap(manager).set(runningTask.id, runningTask)
|
getTaskMap(manager).set(runningTask.id, runningTask)
|
||||||
getTaskMap(manager).set(pendingTask.id, pendingTask)
|
getTaskMap(manager).set(pendingTask.id, pendingTask)
|
||||||
getPendingByParent(manager).set(runningTask.parentSessionID, new Set([runningTask.id, pendingTask.id]))
|
getPendingByParent(manager).set(runningTask.parentSessionId, new Set([runningTask.id, pendingTask.id]))
|
||||||
getQueuesByKey(manager).set(concurrencyKey, [{ input: queuedInput, task: pendingTask }])
|
getQueuesByKey(manager).set(concurrencyKey, [{ input: queuedInput, task: pendingTask }])
|
||||||
|
|
||||||
Reflect.set(manager, "startTask", async ({ task }: { task: BackgroundTask; input: LaunchInput }) => {
|
Reflect.set(manager, "startTask", async ({ task }: { task: BackgroundTask; input: LaunchInput }) => {
|
||||||
task.status = "running"
|
task.status = "running"
|
||||||
task.startedAt = new Date()
|
task.startedAt = new Date()
|
||||||
task.sessionID = "session-started-after-cancel"
|
task.sessionId = "session-started-after-cancel"
|
||||||
task.concurrencyKey = concurrencyKey
|
task.concurrencyKey = concurrencyKey
|
||||||
task.concurrencyGroup = concurrencyKey
|
task.concurrencyGroup = concurrencyKey
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import type { BackgroundTask } from "./types"
|
|||||||
function createRunningTask(startedAt: Date): BackgroundTask {
|
function createRunningTask(startedAt: Date): BackgroundTask {
|
||||||
return {
|
return {
|
||||||
id: "task-1",
|
id: "task-1",
|
||||||
sessionID: "ses-1",
|
sessionId: "ses-1",
|
||||||
parentSessionID: "parent-ses-1",
|
parentSessionId: "parent-ses-1",
|
||||||
parentMessageID: "msg-1",
|
parentMessageId: "msg-1",
|
||||||
description: "test",
|
description: "test",
|
||||||
prompt: "test",
|
prompt: "test",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ function createMockTask(overrides: Partial<BackgroundTask> = {}): BackgroundTask
|
|||||||
prompt: "test prompt",
|
prompt: "test prompt",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
status: "error",
|
status: "error",
|
||||||
parentSessionID: "parent-session-1",
|
parentSessionId: "parent-session-1",
|
||||||
parentMessageID: "parent-message-1",
|
parentMessageId: "parent-message-1",
|
||||||
fallbackChain: [
|
fallbackChain: [
|
||||||
{ model: "fallback-model-1", providers: ["provider-a"], variant: undefined },
|
{ model: "fallback-model-1", providers: ["provider-a"], variant: undefined },
|
||||||
{ model: "fallback-model-2", providers: ["provider-b"], variant: undefined },
|
{ model: "fallback-model-2", providers: ["provider-b"], variant: undefined },
|
||||||
@@ -174,13 +174,13 @@ describe("tryFallbackRetry", () => {
|
|||||||
|
|
||||||
test("clears sessionID and startedAt", async () => {
|
test("clears sessionID and startedAt", async () => {
|
||||||
const args = createDefaultArgs({
|
const args = createDefaultArgs({
|
||||||
sessionID: "old-session",
|
sessionId: "old-session",
|
||||||
startedAt: new Date(),
|
startedAt: new Date(),
|
||||||
})
|
})
|
||||||
|
|
||||||
await tryFallbackRetry(args)
|
await tryFallbackRetry(args)
|
||||||
|
|
||||||
expect(args.task.sessionID).toBeUndefined()
|
expect(args.task.sessionId).toBeUndefined()
|
||||||
expect(args.task.startedAt).toBeUndefined()
|
expect(args.task.startedAt).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ describe("tryFallbackRetry", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test("aborts existing session", async () => {
|
test("aborts existing session", async () => {
|
||||||
const args = createDefaultArgs({ sessionID: "session-to-abort" })
|
const args = createDefaultArgs({ sessionId: "session-to-abort" })
|
||||||
|
|
||||||
await tryFallbackRetry(args)
|
await tryFallbackRetry(args)
|
||||||
|
|
||||||
@@ -227,7 +227,7 @@ describe("tryFallbackRetry", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test("waits for session abort before resolving", async () => {
|
test("waits for session abort before resolving", async () => {
|
||||||
const args = createDefaultArgs({ sessionID: "session-to-abort" })
|
const args = createDefaultArgs({ sessionId: "session-to-abort" })
|
||||||
const deferred = createDeferredPromise()
|
const deferred = createDeferredPromise()
|
||||||
args.abortMock.mockImplementationOnce(() => deferred.promise)
|
args.abortMock.mockImplementationOnce(() => deferred.promise)
|
||||||
|
|
||||||
@@ -263,15 +263,15 @@ describe("tryFallbackRetry", () => {
|
|||||||
test("finalizes the failed attempt, creates a new pending attempt, and enqueues its explicit attemptID", async () => {
|
test("finalizes the failed attempt, creates a new pending attempt, and enqueues its explicit attemptID", async () => {
|
||||||
const args = createDefaultArgs({
|
const args = createDefaultArgs({
|
||||||
status: "running",
|
status: "running",
|
||||||
sessionID: "session-attempt-1",
|
sessionId: "session-attempt-1",
|
||||||
startedAt: new Date("2026-04-27T00:00:00.000Z"),
|
startedAt: new Date("2026-04-27T00:00:00.000Z"),
|
||||||
attempts: [
|
attempts: [
|
||||||
{
|
{
|
||||||
attemptID: "attempt-1",
|
attemptId: "attempt-1",
|
||||||
attemptNumber: 1,
|
attemptNumber: 1,
|
||||||
sessionID: "session-attempt-1",
|
sessionId: "session-attempt-1",
|
||||||
providerID: "provider-a",
|
providerId: "provider-a",
|
||||||
modelID: "original-model",
|
modelId: "original-model",
|
||||||
status: "running",
|
status: "running",
|
||||||
startedAt: new Date("2026-04-27T00:00:00.000Z"),
|
startedAt: new Date("2026-04-27T00:00:00.000Z"),
|
||||||
},
|
},
|
||||||
@@ -283,8 +283,8 @@ describe("tryFallbackRetry", () => {
|
|||||||
|
|
||||||
expect(args.task.attempts).toHaveLength(2)
|
expect(args.task.attempts).toHaveLength(2)
|
||||||
expect(args.task.attempts?.[0]).toMatchObject({
|
expect(args.task.attempts?.[0]).toMatchObject({
|
||||||
attemptID: "attempt-1",
|
attemptId: "attempt-1",
|
||||||
sessionID: "session-attempt-1",
|
sessionId: "session-attempt-1",
|
||||||
status: "error",
|
status: "error",
|
||||||
error: "model overloaded",
|
error: "model overloaded",
|
||||||
})
|
})
|
||||||
@@ -293,11 +293,11 @@ describe("tryFallbackRetry", () => {
|
|||||||
const nextAttempt = args.task.attempts?.[1]
|
const nextAttempt = args.task.attempts?.[1]
|
||||||
expect(nextAttempt).toBeDefined()
|
expect(nextAttempt).toBeDefined()
|
||||||
expect(nextAttempt?.attemptNumber).toBe(2)
|
expect(nextAttempt?.attemptNumber).toBe(2)
|
||||||
expect(nextAttempt?.providerID).toBe("provider-a")
|
expect(nextAttempt?.providerId).toBe("provider-a")
|
||||||
expect(nextAttempt?.modelID).toBe("fallback-model-1")
|
expect(nextAttempt?.modelId).toBe("fallback-model-1")
|
||||||
expect(nextAttempt?.status).toBe("pending")
|
expect(nextAttempt?.status).toBe("pending")
|
||||||
|
|
||||||
expect(args.task.currentAttemptID).toBe(nextAttempt?.attemptID)
|
expect(args.task.currentAttemptID).toBe(nextAttempt?.attemptId)
|
||||||
expect(args.task.status).toBe("pending")
|
expect(args.task.status).toBe("pending")
|
||||||
expect(args.task.model).toEqual({
|
expect(args.task.model).toEqual({
|
||||||
providerID: "provider-a",
|
providerID: "provider-a",
|
||||||
@@ -308,7 +308,7 @@ describe("tryFallbackRetry", () => {
|
|||||||
const key = `${args.task.model!.providerID}/${args.task.model!.modelID}`
|
const key = `${args.task.model!.providerID}/${args.task.model!.modelID}`
|
||||||
const queue = args.queuesByKey.get(key)
|
const queue = args.queuesByKey.get(key)
|
||||||
expect(queue).toBeDefined()
|
expect(queue).toBeDefined()
|
||||||
expect((queue?.[0] as QueueItem & { attemptID?: string })?.attemptID).toBe(nextAttempt?.attemptID)
|
expect((queue?.[0] as QueueItem & { attemptID?: string })?.attemptID).toBe(nextAttempt?.attemptId)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -363,7 +363,7 @@ describe("tryFallbackRetry", () => {
|
|||||||
|
|
||||||
describe("#given task without session", () => {
|
describe("#given task without session", () => {
|
||||||
test("skips session abort", async () => {
|
test("skips session abort", async () => {
|
||||||
const args = createDefaultArgs({ sessionID: undefined })
|
const args = createDefaultArgs({ sessionId: undefined })
|
||||||
|
|
||||||
await tryFallbackRetry(args)
|
await tryFallbackRetry(args)
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ export async function tryFallbackRetry(args: {
|
|||||||
idleDeferralTimers.delete(task.id)
|
idleDeferralTimers.delete(task.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousSessionID = task.sessionID
|
const previousSessionID = task.sessionId
|
||||||
const previousModel = task.model
|
const previousModel = task.model
|
||||||
|
|
||||||
const transformedModelId = transformModelForProvider(providerID, nextFallback.model)
|
const transformedModelId = transformModelForProvider(providerID, nextFallback.model)
|
||||||
@@ -134,7 +134,7 @@ export async function tryFallbackRetry(args: {
|
|||||||
variant: nextFallback.variant,
|
variant: nextFallback.variant,
|
||||||
}
|
}
|
||||||
task.attemptCount = selectedAttemptCount
|
task.attemptCount = selectedAttemptCount
|
||||||
const failedAttemptID = ensureCurrentAttempt(task, previousModel).attemptID
|
const failedAttemptID = ensureCurrentAttempt(task, previousModel).attemptId
|
||||||
const nextAttempt = failedAttemptID
|
const nextAttempt = failedAttemptID
|
||||||
? scheduleRetryAttempt(task, failedAttemptID, nextModel, errorInfo.message)
|
? scheduleRetryAttempt(task, failedAttemptID, nextModel, errorInfo.message)
|
||||||
: undefined
|
: undefined
|
||||||
@@ -165,8 +165,8 @@ export async function tryFallbackRetry(args: {
|
|||||||
description: task.description,
|
description: task.description,
|
||||||
prompt: task.prompt,
|
prompt: task.prompt,
|
||||||
agent: task.agent,
|
agent: task.agent,
|
||||||
parentSessionID: task.parentSessionID,
|
parentSessionId: task.parentSessionId,
|
||||||
parentMessageID: task.parentMessageID,
|
parentMessageId: task.parentMessageId,
|
||||||
parentModel: task.parentModel,
|
parentModel: task.parentModel,
|
||||||
parentAgent: task.parentAgent,
|
parentAgent: task.parentAgent,
|
||||||
parentTools: task.parentTools,
|
parentTools: task.parentTools,
|
||||||
@@ -180,7 +180,7 @@ export async function tryFallbackRetry(args: {
|
|||||||
await abortWithTimeout(client, previousSessionID).catch(() => {})
|
await abortWithTimeout(client, previousSessionID).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.push({ task, input: retryInput, attemptID: nextAttempt.attemptID })
|
queue.push({ task, input: retryInput, attemptID: nextAttempt.attemptId })
|
||||||
queuesByKey.set(key, queue)
|
queuesByKey.set(key, queue)
|
||||||
processKey(key)
|
processKey(key)
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ function createManager(config?: BackgroundTaskConfig): BackgroundManager {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const manager = new BackgroundManager({ client, directory: tmpdir() } as unknown as PluginInput, config)
|
const manager = new BackgroundManager({ pluginContext: { client, directory: tmpdir() } as unknown as PluginInput, config: config })
|
||||||
const testManager = manager as unknown as {
|
const testManager = manager as unknown as {
|
||||||
enqueueNotificationForParent: (sessionID: string, fn: () => Promise<void>) => Promise<void>
|
enqueueNotificationForParent: (sessionId: string, fn: () => Promise<void>) => Promise<void>
|
||||||
notifyParentSession: (task: BackgroundTask) => Promise<void>
|
notifyParentSession: (task: BackgroundTask) => Promise<void>
|
||||||
tasks: Map<string, BackgroundTask>
|
tasks: Map<string, BackgroundTask>
|
||||||
}
|
}
|
||||||
|
|
||||||
testManager.enqueueNotificationForParent = async (_sessionID, fn) => {
|
testManager.enqueueNotificationForParent = async (_sessionId: sessionID, fn) => {
|
||||||
await fn()
|
await fn()
|
||||||
}
|
}
|
||||||
testManager.notifyParentSession = async () => {}
|
testManager.notifyParentSession = async () => {}
|
||||||
@@ -49,9 +49,9 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
})
|
})
|
||||||
const task: BackgroundTask = {
|
const task: BackgroundTask = {
|
||||||
id: "task-loop-1",
|
id: "task-loop-1",
|
||||||
sessionID: "session-loop-1",
|
sessionId: "session-loop-1",
|
||||||
parentSessionID: "parent-1",
|
parentSessionId: "parent-1",
|
||||||
parentMessageID: "msg-1",
|
parentMessageId: "msg-1",
|
||||||
description: "Looping task",
|
description: "Looping task",
|
||||||
prompt: "loop",
|
prompt: "loop",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -67,7 +67,7 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
for (let i = 0; i < 20; i++) {
|
for (let i = 0; i < 20; i++) {
|
||||||
manager.handleEvent({
|
manager.handleEvent({
|
||||||
type: "message.part.updated",
|
type: "message.part.updated",
|
||||||
properties: { sessionID: task.sessionID, type: "tool", tool: "read" },
|
properties: { sessionID: task.sessionId, type: "tool", tool: "read" },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,9 +87,9 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
})
|
})
|
||||||
const task: BackgroundTask = {
|
const task: BackgroundTask = {
|
||||||
id: "task-diverse-1",
|
id: "task-diverse-1",
|
||||||
sessionID: "session-diverse-1",
|
sessionId: "session-diverse-1",
|
||||||
parentSessionID: "parent-1",
|
parentSessionId: "parent-1",
|
||||||
parentMessageID: "msg-1",
|
parentMessageId: "msg-1",
|
||||||
description: "Healthy task",
|
description: "Healthy task",
|
||||||
prompt: "work",
|
prompt: "work",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -116,7 +116,7 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
]) {
|
]) {
|
||||||
manager.handleEvent({
|
manager.handleEvent({
|
||||||
type: "message.part.updated",
|
type: "message.part.updated",
|
||||||
properties: { sessionID: task.sessionID, type: "tool", tool: toolName },
|
properties: { sessionID: task.sessionId, type: "tool", tool: toolName },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,9 +137,9 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
})
|
})
|
||||||
const task: BackgroundTask = {
|
const task: BackgroundTask = {
|
||||||
id: "task-cap-1",
|
id: "task-cap-1",
|
||||||
sessionID: "session-cap-1",
|
sessionId: "session-cap-1",
|
||||||
parentSessionID: "parent-1",
|
parentSessionId: "parent-1",
|
||||||
parentMessageID: "msg-1",
|
parentMessageId: "msg-1",
|
||||||
description: "Backstop task",
|
description: "Backstop task",
|
||||||
prompt: "work",
|
prompt: "work",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -155,7 +155,7 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 3; i++) {
|
||||||
manager.handleEvent({
|
manager.handleEvent({
|
||||||
type: "message.part.updated",
|
type: "message.part.updated",
|
||||||
properties: { sessionID: task.sessionID, type: "tool", tool: "read" },
|
properties: { sessionID: task.sessionId, type: "tool", tool: "read" },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,9 +176,9 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
})
|
})
|
||||||
const task: BackgroundTask = {
|
const task: BackgroundTask = {
|
||||||
id: "task-dedupe-1",
|
id: "task-dedupe-1",
|
||||||
sessionID: "session-dedupe-1",
|
sessionId: "session-dedupe-1",
|
||||||
parentSessionID: "parent-1",
|
parentSessionId: "parent-1",
|
||||||
parentMessageID: "msg-1",
|
parentMessageId: "msg-1",
|
||||||
description: "Dedupe task",
|
description: "Dedupe task",
|
||||||
prompt: "work",
|
prompt: "work",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -197,7 +197,7 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
properties: {
|
properties: {
|
||||||
part: {
|
part: {
|
||||||
id: "tool-1",
|
id: "tool-1",
|
||||||
sessionID: task.sessionID,
|
sessionID: task.sessionId,
|
||||||
type: "tool",
|
type: "tool",
|
||||||
tool: "bash",
|
tool: "bash",
|
||||||
state: { status: "running" },
|
state: { status: "running" },
|
||||||
@@ -223,9 +223,9 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
})
|
})
|
||||||
const task: BackgroundTask = {
|
const task: BackgroundTask = {
|
||||||
id: "task-diff-files-1",
|
id: "task-diff-files-1",
|
||||||
sessionID: "session-diff-files-1",
|
sessionId: "session-diff-files-1",
|
||||||
parentSessionID: "parent-1",
|
parentSessionId: "parent-1",
|
||||||
parentMessageID: "msg-1",
|
parentMessageId: "msg-1",
|
||||||
description: "Reading different files",
|
description: "Reading different files",
|
||||||
prompt: "work",
|
prompt: "work",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -243,7 +243,7 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
type: "message.part.updated",
|
type: "message.part.updated",
|
||||||
properties: {
|
properties: {
|
||||||
part: {
|
part: {
|
||||||
sessionID: task.sessionID,
|
sessionID: task.sessionId,
|
||||||
type: "tool",
|
type: "tool",
|
||||||
tool: "read",
|
tool: "read",
|
||||||
state: { status: "running", input: { filePath: `/src/file-${i}.ts` } },
|
state: { status: "running", input: { filePath: `/src/file-${i}.ts` } },
|
||||||
@@ -268,9 +268,9 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
})
|
})
|
||||||
const task: BackgroundTask = {
|
const task: BackgroundTask = {
|
||||||
id: "task-same-file-1",
|
id: "task-same-file-1",
|
||||||
sessionID: "session-same-file-1",
|
sessionId: "session-same-file-1",
|
||||||
parentSessionID: "parent-1",
|
parentSessionId: "parent-1",
|
||||||
parentMessageID: "msg-1",
|
parentMessageId: "msg-1",
|
||||||
description: "Reading same file repeatedly",
|
description: "Reading same file repeatedly",
|
||||||
prompt: "work",
|
prompt: "work",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -288,7 +288,7 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
type: "message.part.updated",
|
type: "message.part.updated",
|
||||||
properties: {
|
properties: {
|
||||||
part: {
|
part: {
|
||||||
sessionID: task.sessionID,
|
sessionID: task.sessionId,
|
||||||
type: "tool",
|
type: "tool",
|
||||||
tool: "read",
|
tool: "read",
|
||||||
state: { status: "running", input: { filePath: "/src/same.ts" } },
|
state: { status: "running", input: { filePath: "/src/same.ts" } },
|
||||||
@@ -315,9 +315,9 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
})
|
})
|
||||||
const task: BackgroundTask = {
|
const task: BackgroundTask = {
|
||||||
id: "task-disabled-1",
|
id: "task-disabled-1",
|
||||||
sessionID: "session-disabled-1",
|
sessionId: "session-disabled-1",
|
||||||
parentSessionID: "parent-1",
|
parentSessionId: "parent-1",
|
||||||
parentMessageID: "msg-1",
|
parentMessageId: "msg-1",
|
||||||
description: "Disabled circuit breaker task",
|
description: "Disabled circuit breaker task",
|
||||||
prompt: "work",
|
prompt: "work",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -334,7 +334,7 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
manager.handleEvent({
|
manager.handleEvent({
|
||||||
type: "message.part.updated",
|
type: "message.part.updated",
|
||||||
properties: {
|
properties: {
|
||||||
sessionID: task.sessionID,
|
sessionID: task.sessionId,
|
||||||
type: "tool",
|
type: "tool",
|
||||||
tool: "read",
|
tool: "read",
|
||||||
},
|
},
|
||||||
@@ -358,9 +358,9 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
})
|
})
|
||||||
const task: BackgroundTask = {
|
const task: BackgroundTask = {
|
||||||
id: "task-cap-disabled-1",
|
id: "task-cap-disabled-1",
|
||||||
sessionID: "session-cap-disabled-1",
|
sessionId: "session-cap-disabled-1",
|
||||||
parentSessionID: "parent-1",
|
parentSessionId: "parent-1",
|
||||||
parentMessageID: "msg-1",
|
parentMessageId: "msg-1",
|
||||||
description: "Backstop task with disabled circuit breaker",
|
description: "Backstop task with disabled circuit breaker",
|
||||||
prompt: "work",
|
prompt: "work",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -376,7 +376,7 @@ describe("BackgroundManager circuit breaker", () => {
|
|||||||
for (const toolName of ["read", "grep", "edit"]) {
|
for (const toolName of ["read", "grep", "edit"]) {
|
||||||
manager.handleEvent({
|
manager.handleEvent({
|
||||||
type: "message.part.updated",
|
type: "message.part.updated",
|
||||||
properties: { sessionID: task.sessionID, type: "tool", tool: toolName },
|
properties: { sessionID: task.sessionId, type: "tool", tool: toolName },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,15 +21,15 @@ describe("BackgroundManager session permission", () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
const directory = tmpdir()
|
const directory = tmpdir()
|
||||||
const manager = new BackgroundManager({ client, directory } as unknown as PluginInput)
|
const manager = new BackgroundManager({ pluginContext: { client, directory } as unknown as PluginInput })
|
||||||
|
|
||||||
// when
|
// when
|
||||||
await manager.launch({
|
await manager.launch({
|
||||||
description: "Test task",
|
description: "Test task",
|
||||||
prompt: "Do something",
|
prompt: "Do something",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
parentSessionID: "ses_parent",
|
parentSessionId: "ses_parent",
|
||||||
parentMessageID: "msg_parent",
|
parentMessageId: "msg_parent",
|
||||||
})
|
})
|
||||||
await new Promise((resolve) => setTimeout(resolve, 50))
|
await new Promise((resolve) => setTimeout(resolve, 50))
|
||||||
manager.shutdown()
|
manager.shutdown()
|
||||||
@@ -62,15 +62,15 @@ describe("BackgroundManager session permission", () => {
|
|||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
const manager = new BackgroundManager({ client, directory: tmpdir() } as unknown as PluginInput)
|
const manager = new BackgroundManager({ pluginContext: { client, directory: tmpdir() } as unknown as PluginInput })
|
||||||
|
|
||||||
// when
|
// when
|
||||||
await manager.launch({
|
await manager.launch({
|
||||||
description: "Test task",
|
description: "Test task",
|
||||||
prompt: "Do something",
|
prompt: "Do something",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
parentSessionID: "ses_parent",
|
parentSessionId: "ses_parent",
|
||||||
parentMessageID: "msg_parent",
|
parentMessageId: "msg_parent",
|
||||||
sessionPermission: [
|
sessionPermission: [
|
||||||
{ permission: "question", action: "deny", pattern: "*" },
|
{ permission: "question", action: "deny", pattern: "*" },
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ function createDeferredPromise(): {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTask(overrides: Partial<BackgroundTask> & { id: string; sessionID: string }): BackgroundTask {
|
function createTask(overrides: Partial<BackgroundTask> & { id: string; sessionId: string }): BackgroundTask {
|
||||||
return {
|
return {
|
||||||
parentSessionID: "parent-session",
|
parentSessionId: "parent-session",
|
||||||
parentMessageID: "parent-message",
|
parentMessageId: "parent-message",
|
||||||
description: "test task",
|
description: "test task",
|
||||||
prompt: "test prompt",
|
prompt: "test prompt",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -34,7 +34,7 @@ function createTask(overrides: Partial<BackgroundTask> & { id: string; sessionID
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createBackgroundManager(): BackgroundManager {
|
function createBackgroundManager(): BackgroundManager {
|
||||||
return new BackgroundManager({
|
return new BackgroundManager({ pluginContext: {
|
||||||
client: {
|
client: {
|
||||||
session: {
|
session: {
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
@@ -47,7 +47,7 @@ function createBackgroundManager(): BackgroundManager {
|
|||||||
worktree: tmpdir(),
|
worktree: tmpdir(),
|
||||||
serverUrl: new URL("https://example.com"),
|
serverUrl: new URL("https://example.com"),
|
||||||
$: {} as never,
|
$: {} as never,
|
||||||
} as never)
|
} as never })
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("BackgroundManager shutdown global cleanup", () => {
|
describe("BackgroundManager shutdown global cleanup", () => {
|
||||||
@@ -74,14 +74,14 @@ describe("BackgroundManager shutdown global cleanup", () => {
|
|||||||
"task-running-shutdown-cleanup",
|
"task-running-shutdown-cleanup",
|
||||||
createTask({
|
createTask({
|
||||||
id: "task-running-shutdown-cleanup",
|
id: "task-running-shutdown-cleanup",
|
||||||
sessionID: runningSessionID,
|
sessionId: runningSessionID,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"task-completed-shutdown-cleanup",
|
"task-completed-shutdown-cleanup",
|
||||||
createTask({
|
createTask({
|
||||||
id: "task-completed-shutdown-cleanup",
|
id: "task-completed-shutdown-cleanup",
|
||||||
sessionID: completedSessionID,
|
sessionId: completedSessionID,
|
||||||
status: "completed",
|
status: "completed",
|
||||||
completedAt: new Date(),
|
completedAt: new Date(),
|
||||||
}),
|
}),
|
||||||
@@ -119,7 +119,7 @@ describe("BackgroundManager shutdown global cleanup", () => {
|
|||||||
"task-running-await-shutdown",
|
"task-running-await-shutdown",
|
||||||
createTask({
|
createTask({
|
||||||
id: "task-running-await-shutdown",
|
id: "task-running-await-shutdown",
|
||||||
sessionID: runningSessionID,
|
sessionId: runningSessionID,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ function createManagerWithStatus(statusImpl: () => Promise<{ data: Record<string
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return new BackgroundManager({ client, directory: tmpdir() } as unknown as PluginInput)
|
return new BackgroundManager({ pluginContext: { client, directory: tmpdir() } as unknown as PluginInput })
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("BackgroundManager polling overlap", () => {
|
describe("BackgroundManager polling overlap", () => {
|
||||||
@@ -56,12 +56,12 @@ describe("BackgroundManager polling overlap", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
function createRunningTask(sessionID: string): BackgroundTask {
|
function createRunningTask(sessionId: string): BackgroundTask {
|
||||||
return {
|
return {
|
||||||
id: `bg_test_${sessionID}`,
|
id: `bg_test_${sessionId}`,
|
||||||
sessionID,
|
sessionId,
|
||||||
parentSessionID: "parent-session",
|
parentSessionId: "parent-session",
|
||||||
parentMessageID: "parent-msg",
|
parentMessageId: "parent-msg",
|
||||||
description: "test task",
|
description: "test task",
|
||||||
prompt: "test",
|
prompt: "test",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -98,9 +98,7 @@ function createManagerWithClient(clientOverrides: Record<string, unknown> = {}):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
return new BackgroundManager(
|
return new BackgroundManager(
|
||||||
{ client, directory: tmpdir() } as unknown as PluginInput,
|
{ pluginContext: { client, directory: tmpdir() } as unknown as PluginInput, config: undefined, enableParentSessionNotifications: false },
|
||||||
undefined,
|
|
||||||
{ enableParentSessionNotifications: false },
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -129,12 +129,12 @@ interface Todo {
|
|||||||
id: string
|
id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatAttemptModelSummary(attempt: Pick<BackgroundTaskAttempt, "providerID" | "modelID"> | undefined): string | undefined {
|
function formatAttemptModelSummary(attempt: Pick<BackgroundTaskAttempt, "providerId" | "modelId"> | undefined): string | undefined {
|
||||||
if (!attempt?.providerID || !attempt.modelID) {
|
if (!attempt?.providerId || !attempt.modelId) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${attempt.providerID}/${attempt.modelID}`
|
return `${attempt.providerId}/${attempt.modelId}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPreviousAttempt(task: BackgroundTask, attemptID: string | undefined): BackgroundTaskAttempt | undefined {
|
function getPreviousAttempt(task: BackgroundTask, attemptID: string | undefined): BackgroundTaskAttempt | undefined {
|
||||||
@@ -142,7 +142,7 @@ function getPreviousAttempt(task: BackgroundTask, attemptID: string | undefined)
|
|||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const attemptIndex = task.attempts.findIndex((attempt) => attempt.attemptID === attemptID)
|
const attemptIndex = task.attempts.findIndex((attempt) => attempt.attemptId === attemptID)
|
||||||
if (attemptIndex <= 0) {
|
if (attemptIndex <= 0) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
@@ -173,6 +173,16 @@ export type OnSubagentSessionCreated = (event: SubagentSessionCreatedEvent) => P
|
|||||||
|
|
||||||
const MAX_TASK_REMOVAL_RESCHEDULES = 6
|
const MAX_TASK_REMOVAL_RESCHEDULES = 6
|
||||||
|
|
||||||
|
export interface BackgroundManagerConfig {
|
||||||
|
pluginContext: PluginInput
|
||||||
|
config?: BackgroundTaskConfig
|
||||||
|
tmuxConfig?: TmuxConfig
|
||||||
|
onSubagentSessionCreated?: OnSubagentSessionCreated
|
||||||
|
onShutdown?: () => void | Promise<void>
|
||||||
|
enableParentSessionNotifications?: boolean
|
||||||
|
modelFallbackControllerAccessor?: ModelFallbackControllerAccessor
|
||||||
|
}
|
||||||
|
|
||||||
export class BackgroundManager {
|
export class BackgroundManager {
|
||||||
|
|
||||||
|
|
||||||
@@ -207,26 +217,17 @@ export class BackgroundManager {
|
|||||||
readonly taskHistory = new TaskHistory()
|
readonly taskHistory = new TaskHistory()
|
||||||
private cachedCircuitBreakerSettings?: CircuitBreakerSettings
|
private cachedCircuitBreakerSettings?: CircuitBreakerSettings
|
||||||
|
|
||||||
constructor(
|
constructor(config: BackgroundManagerConfig) {
|
||||||
ctx: PluginInput,
|
const { pluginContext, ...options } = config
|
||||||
config?: BackgroundTaskConfig,
|
|
||||||
options?: {
|
|
||||||
tmuxConfig?: TmuxConfig
|
|
||||||
onSubagentSessionCreated?: OnSubagentSessionCreated
|
|
||||||
onShutdown?: () => void | Promise<void>
|
|
||||||
enableParentSessionNotifications?: boolean
|
|
||||||
modelFallbackControllerAccessor?: ModelFallbackControllerAccessor
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
this.tasks = new Map()
|
this.tasks = new Map()
|
||||||
this.tasksByParentSession = new Map()
|
this.tasksByParentSession = new Map()
|
||||||
this.notifications = new Map()
|
this.notifications = new Map()
|
||||||
this.pendingNotifications = new Map()
|
this.pendingNotifications = new Map()
|
||||||
this.pendingByParent = new Map()
|
this.pendingByParent = new Map()
|
||||||
this.client = ctx.client
|
this.client = pluginContext.client
|
||||||
this.directory = ctx.directory
|
this.directory = pluginContext.directory
|
||||||
this.concurrencyManager = new ConcurrencyManager(config)
|
this.concurrencyManager = new ConcurrencyManager(options.config)
|
||||||
this.config = config
|
this.config = options.config
|
||||||
this.tmuxEnabled = options?.tmuxConfig?.enabled ?? false
|
this.tmuxEnabled = options?.tmuxConfig?.enabled ?? false
|
||||||
this.onSubagentSessionCreated = options?.onSubagentSessionCreated
|
this.onSubagentSessionCreated = options?.onSubagentSessionCreated
|
||||||
this.onShutdown = options?.onShutdown
|
this.onShutdown = options?.onShutdown
|
||||||
@@ -317,36 +318,36 @@ export class BackgroundManager {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!task.rootSessionID) {
|
if (!task.rootSessionId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.unregisterRootDescendant(task.rootSessionID)
|
this.unregisterRootDescendant(task.rootSessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private addTask(task: BackgroundTask): void {
|
private addTask(task: BackgroundTask): void {
|
||||||
this.tasks.set(task.id, task)
|
this.tasks.set(task.id, task)
|
||||||
if (!task.parentSessionID) {
|
if (!task.parentSessionId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const taskIDs = this.tasksByParentSession.get(task.parentSessionID) ?? new Set<string>()
|
const taskIDs = this.tasksByParentSession.get(task.parentSessionId) ?? new Set<string>()
|
||||||
taskIDs.add(task.id)
|
taskIDs.add(task.id)
|
||||||
this.tasksByParentSession.set(task.parentSessionID, taskIDs)
|
this.tasksByParentSession.set(task.parentSessionId, taskIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeTask(task: BackgroundTask): void {
|
private removeTask(task: BackgroundTask): void {
|
||||||
this.tasks.delete(task.id)
|
this.tasks.delete(task.id)
|
||||||
this.removeTaskFromParentIndex(task.id, task.parentSessionID)
|
this.removeTaskFromParentIndex(task.id, task.parentSessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateTaskParent(task: BackgroundTask, parentSessionID: string): void {
|
private updateTaskParent(task: BackgroundTask, parentSessionID: string): void {
|
||||||
if (task.parentSessionID === parentSessionID) {
|
if (task.parentSessionId === parentSessionID) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.removeTaskFromParentIndex(task.id, task.parentSessionID)
|
this.removeTaskFromParentIndex(task.id, task.parentSessionId)
|
||||||
task.parentSessionID = parentSessionID
|
task.parentSessionId = parentSessionID
|
||||||
const taskIDs = this.tasksByParentSession.get(parentSessionID) ?? new Set<string>()
|
const taskIDs = this.tasksByParentSession.get(parentSessionID) ?? new Set<string>()
|
||||||
taskIDs.add(task.id)
|
taskIDs.add(task.id)
|
||||||
this.tasksByParentSession.set(parentSessionID, taskIDs)
|
this.tasksByParentSession.set(parentSessionID, taskIDs)
|
||||||
@@ -373,18 +374,18 @@ export class BackgroundManager {
|
|||||||
agent: input.agent,
|
agent: input.agent,
|
||||||
model: input.model,
|
model: input.model,
|
||||||
description: input.description,
|
description: input.description,
|
||||||
parentSessionID: input.parentSessionID,
|
parentSessionID: input.parentSessionId,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!input.agent || input.agent.trim() === "") {
|
if (!input.agent || input.agent.trim() === "") {
|
||||||
throw new Error("Agent parameter is required")
|
throw new Error("Agent parameter is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
const spawnReservation = await this.reserveSubagentSpawn(input.parentSessionID)
|
const spawnReservation = await this.reserveSubagentSpawn(input.parentSessionId)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log("[background-agent] spawn guard passed", {
|
log("[background-agent] spawn guard passed", {
|
||||||
parentSessionID: input.parentSessionID,
|
parentSessionID: input.parentSessionId,
|
||||||
rootSessionID: spawnReservation.spawnContext.rootSessionID,
|
rootSessionID: spawnReservation.spawnContext.rootSessionID,
|
||||||
childDepth: spawnReservation.spawnContext.childDepth,
|
childDepth: spawnReservation.spawnContext.childDepth,
|
||||||
descendantCount: spawnReservation.descendantCount,
|
descendantCount: spawnReservation.descendantCount,
|
||||||
@@ -395,15 +396,15 @@ export class BackgroundManager {
|
|||||||
id: `bg_${crypto.randomUUID().slice(0, 8)}`,
|
id: `bg_${crypto.randomUUID().slice(0, 8)}`,
|
||||||
status: "pending",
|
status: "pending",
|
||||||
queuedAt: new Date(),
|
queuedAt: new Date(),
|
||||||
rootSessionID: spawnReservation.spawnContext.rootSessionID,
|
rootSessionId: spawnReservation.spawnContext.rootSessionID,
|
||||||
// Do NOT set startedAt - will be set when running
|
// Do NOT set startedAt - will be set when running
|
||||||
// Do NOT set sessionID - will be set when running
|
// Do NOT set sessionID - will be set when running
|
||||||
description: input.description,
|
description: input.description,
|
||||||
prompt: input.prompt,
|
prompt: input.prompt,
|
||||||
agent: input.agent,
|
agent: input.agent,
|
||||||
spawnDepth: spawnReservation.spawnContext.childDepth,
|
spawnDepth: spawnReservation.spawnContext.childDepth,
|
||||||
parentSessionID: input.parentSessionID,
|
parentSessionId: input.parentSessionId,
|
||||||
parentMessageID: input.parentMessageID,
|
parentMessageId: input.parentMessageId,
|
||||||
parentModel: input.parentModel,
|
parentModel: input.parentModel,
|
||||||
parentAgent: input.parentAgent,
|
parentAgent: input.parentAgent,
|
||||||
parentTools: input.parentTools,
|
parentTools: input.parentTools,
|
||||||
@@ -415,19 +416,19 @@ export class BackgroundManager {
|
|||||||
const firstAttempt = startAttempt(task, input.model)
|
const firstAttempt = startAttempt(task, input.model)
|
||||||
|
|
||||||
this.addTask(task)
|
this.addTask(task)
|
||||||
this.taskHistory.record(input.parentSessionID, { id: task.id, agent: input.agent, description: input.description, status: "pending", category: input.category })
|
this.taskHistory.record(input.parentSessionId, { id: task.id, agent: input.agent, description: input.description, status: "pending", category: input.category })
|
||||||
|
|
||||||
// Track for batched notifications immediately (pending state)
|
// Track for batched notifications immediately (pending state)
|
||||||
if (input.parentSessionID) {
|
if (input.parentSessionId) {
|
||||||
const pending = this.pendingByParent.get(input.parentSessionID) ?? new Set()
|
const pending = this.pendingByParent.get(input.parentSessionId) ?? new Set()
|
||||||
pending.add(task.id)
|
pending.add(task.id)
|
||||||
this.pendingByParent.set(input.parentSessionID, pending)
|
this.pendingByParent.set(input.parentSessionId, pending)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to queue
|
// Add to queue
|
||||||
const key = this.getConcurrencyKeyFromInput(input)
|
const key = this.getConcurrencyKeyFromInput(input)
|
||||||
const queue = this.queuesByKey.get(key) ?? []
|
const queue = this.queuesByKey.get(key) ?? []
|
||||||
queue.push({ task, input, attemptID: firstAttempt.attemptID })
|
queue.push({ task, input, attemptID: firstAttempt.attemptId })
|
||||||
this.queuesByKey.set(key, queue)
|
this.queuesByKey.set(key, queue)
|
||||||
|
|
||||||
log("[background-agent] Task queued:", { taskId: task.id, key, queueLength: queue.length })
|
log("[background-agent] Task queued:", { taskId: task.id, key, queueLength: queue.length })
|
||||||
@@ -506,12 +507,12 @@ export class BackgroundManager {
|
|||||||
removeTaskToastTracking(item.task.id)
|
removeTaskToastTracking(item.task.id)
|
||||||
|
|
||||||
// Abort the orphaned session if one was created before the error
|
// Abort the orphaned session if one was created before the error
|
||||||
if (item.task.sessionID) {
|
if (item.task.sessionId) {
|
||||||
await this.abortSessionWithLogging(item.task.sessionID, "startTask error cleanup")
|
await this.abortSessionWithLogging(item.task.sessionId, "startTask error cleanup")
|
||||||
}
|
}
|
||||||
|
|
||||||
this.markForNotification(item.task)
|
this.markForNotification(item.task)
|
||||||
this.enqueueNotificationForParent(item.task.parentSessionID, () => this.notifyParentSession(item.task)).catch(err => {
|
this.enqueueNotificationForParent(item.task.parentSessionId, () => this.notifyParentSession(item.task)).catch(err => {
|
||||||
log("[background-agent] Failed to notify on startTask error:", err)
|
log("[background-agent] Failed to notify on startTask error:", err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -523,7 +524,7 @@ export class BackgroundManager {
|
|||||||
|
|
||||||
private async startTask(item: QueueItem): Promise<void> {
|
private async startTask(item: QueueItem): Promise<void> {
|
||||||
const { task, input } = item
|
const { task, input } = item
|
||||||
const attemptID = item.attemptID ?? ensureCurrentAttempt(task, input.model).attemptID
|
const attemptID = item.attemptID ?? ensureCurrentAttempt(task, input.model).attemptId
|
||||||
|
|
||||||
log("[background-agent] Starting task:", {
|
log("[background-agent] Starting task:", {
|
||||||
taskId: task.id,
|
taskId: task.id,
|
||||||
@@ -534,7 +535,7 @@ export class BackgroundManager {
|
|||||||
const concurrencyKey = this.getConcurrencyKeyFromInput(input)
|
const concurrencyKey = this.getConcurrencyKeyFromInput(input)
|
||||||
|
|
||||||
const parentSession = await this.client.session.get({
|
const parentSession = await this.client.session.get({
|
||||||
path: { id: input.parentSessionID },
|
path: { id: input.parentSessionId },
|
||||||
query: { directory: this.directory },
|
query: { directory: this.directory },
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
log(`[background-agent] Failed to get parent session: ${err}`)
|
log(`[background-agent] Failed to get parent session: ${err}`)
|
||||||
@@ -545,7 +546,7 @@ export class BackgroundManager {
|
|||||||
|
|
||||||
const createResult = await this.client.session.create({
|
const createResult = await this.client.session.create({
|
||||||
body: {
|
body: {
|
||||||
parentID: input.parentSessionID,
|
parentID: input.parentSessionId,
|
||||||
title: `${input.description} (@${input.agent} subagent)`,
|
title: `${input.description} (@${input.agent} subagent)`,
|
||||||
...(input.sessionPermission ? { permission: input.sessionPermission } : {}),
|
...(input.sessionPermission ? { permission: input.sessionPermission } : {}),
|
||||||
} as Record<string, unknown>,
|
} as Record<string, unknown>,
|
||||||
@@ -578,14 +579,14 @@ export class BackgroundManager {
|
|||||||
tmuxEnabled: this.tmuxEnabled,
|
tmuxEnabled: this.tmuxEnabled,
|
||||||
isInsideTmux: isInsideTmux(),
|
isInsideTmux: isInsideTmux(),
|
||||||
sessionID,
|
sessionID,
|
||||||
parentID: input.parentSessionID,
|
parentID: input.parentSessionId,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (this.onSubagentSessionCreated && this.tmuxEnabled && isInsideTmux()) {
|
if (this.onSubagentSessionCreated && this.tmuxEnabled && isInsideTmux()) {
|
||||||
log("[background-agent] Invoking tmux callback NOW", { sessionID })
|
log("[background-agent] Invoking tmux callback NOW", { sessionID })
|
||||||
await this.onSubagentSessionCreated({
|
await this.onSubagentSessionCreated({
|
||||||
sessionID,
|
sessionID,
|
||||||
parentID: input.parentSessionID,
|
parentID: input.parentSessionId,
|
||||||
title: input.description,
|
title: input.description,
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
log("[background-agent] Failed to spawn tmux pane:", err)
|
log("[background-agent] Failed to spawn tmux pane:", err)
|
||||||
@@ -599,8 +600,8 @@ export class BackgroundManager {
|
|||||||
if (this.tasks.get(task.id)?.status === "cancelled") {
|
if (this.tasks.get(task.id)?.status === "cancelled") {
|
||||||
await this.abortSessionWithLogging(sessionID, "cancelled during tmux setup")
|
await this.abortSessionWithLogging(sessionID, "cancelled during tmux setup")
|
||||||
subagentSessions.delete(sessionID)
|
subagentSessions.delete(sessionID)
|
||||||
if (task.rootSessionID) {
|
if (task.rootSessionId) {
|
||||||
this.unregisterRootDescendant(task.rootSessionID)
|
this.unregisterRootDescendant(task.rootSessionId)
|
||||||
}
|
}
|
||||||
this.concurrencyManager.release(concurrencyKey)
|
this.concurrencyManager.release(concurrencyKey)
|
||||||
return
|
return
|
||||||
@@ -610,8 +611,8 @@ export class BackgroundManager {
|
|||||||
if (!boundAttempt) {
|
if (!boundAttempt) {
|
||||||
await this.abortSessionWithLogging(sessionID, "stale attempt binding cleanup")
|
await this.abortSessionWithLogging(sessionID, "stale attempt binding cleanup")
|
||||||
subagentSessions.delete(sessionID)
|
subagentSessions.delete(sessionID)
|
||||||
if (task.rootSessionID) {
|
if (task.rootSessionId) {
|
||||||
this.unregisterRootDescendant(task.rootSessionID)
|
this.unregisterRootDescendant(task.rootSessionId)
|
||||||
}
|
}
|
||||||
this.concurrencyManager.release(concurrencyKey)
|
this.concurrencyManager.release(concurrencyKey)
|
||||||
return
|
return
|
||||||
@@ -627,8 +628,8 @@ export class BackgroundManager {
|
|||||||
if (task.retryNotification) {
|
if (task.retryNotification) {
|
||||||
const attemptNumber = boundAttempt.attemptNumber
|
const attemptNumber = boundAttempt.attemptNumber
|
||||||
const retrySessionUrl = buildLocalSessionUrl(parentDirectory, sessionID)
|
const retrySessionUrl = buildLocalSessionUrl(parentDirectory, sessionID)
|
||||||
const previousAttempt = getPreviousAttempt(task, boundAttempt.attemptID)
|
const previousAttempt = getPreviousAttempt(task, boundAttempt.attemptId)
|
||||||
const failedSessionID = previousAttempt?.sessionID ?? task.retryNotification.previousSessionID
|
const failedSessionID = previousAttempt?.sessionId ?? task.retryNotification.previousSessionID
|
||||||
const failedSessionLine = failedSessionID
|
const failedSessionLine = failedSessionID
|
||||||
? `\n- Failed session: \`${failedSessionID}\``
|
? `\n- Failed session: \`${failedSessionID}\``
|
||||||
: ""
|
: ""
|
||||||
@@ -642,7 +643,7 @@ export class BackgroundManager {
|
|||||||
: ""
|
: ""
|
||||||
const retryModel = formatAttemptModelSummary(boundAttempt) ?? task.retryNotification.nextModel
|
const retryModel = formatAttemptModelSummary(boundAttempt) ?? task.retryNotification.nextModel
|
||||||
this.queuePendingNotification(
|
this.queuePendingNotification(
|
||||||
task.parentSessionID,
|
task.parentSessionId,
|
||||||
`<system-reminder>
|
`<system-reminder>
|
||||||
[BACKGROUND TASK RETRY SESSION READY]
|
[BACKGROUND TASK RETRY SESSION READY]
|
||||||
**ID:** \`${task.id}\`
|
**ID:** \`${task.id}\`
|
||||||
@@ -657,7 +658,7 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
task.retryNotification = undefined
|
task.retryNotification = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
this.taskHistory.record(input.parentSessionID, { id: task.id, sessionID, agent: input.agent, description: input.description, status: "running", category: input.category, startedAt: task.startedAt })
|
this.taskHistory.record(input.parentSessionId, { id: task.id, sessionID, agent: input.agent, description: input.description, status: "running", category: input.category, startedAt: task.startedAt })
|
||||||
this.startPolling()
|
this.startPolling()
|
||||||
|
|
||||||
log("[background-agent] Launching task:", { taskId: task.id, sessionID, agent: input.agent })
|
log("[background-agent] Launching task:", { taskId: task.id, sessionID, agent: input.agent })
|
||||||
@@ -764,8 +765,8 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
existingTask.error = terminalError
|
existingTask.error = terminalError
|
||||||
existingTask.completedAt = new Date()
|
existingTask.completedAt = new Date()
|
||||||
}
|
}
|
||||||
if (existingTask.rootSessionID) {
|
if (existingTask.rootSessionId) {
|
||||||
this.unregisterRootDescendant(existingTask.rootSessionID)
|
this.unregisterRootDescendant(existingTask.rootSessionId)
|
||||||
}
|
}
|
||||||
if (existingTask.concurrencyKey) {
|
if (existingTask.concurrencyKey) {
|
||||||
this.concurrencyManager.release(existingTask.concurrencyKey)
|
this.concurrencyManager.release(existingTask.concurrencyKey)
|
||||||
@@ -779,7 +780,7 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
await this.abortSessionWithLogging(sessionID, "launch error cleanup")
|
await this.abortSessionWithLogging(sessionID, "launch error cleanup")
|
||||||
|
|
||||||
this.markForNotification(existingTask)
|
this.markForNotification(existingTask)
|
||||||
this.enqueueNotificationForParent(existingTask.parentSessionID, () => this.notifyParentSession(existingTask)).catch(err => {
|
this.enqueueNotificationForParent(existingTask.parentSessionId, () => this.notifyParentSession(existingTask)).catch(err => {
|
||||||
log("[background-agent] Failed to notify on error:", err)
|
log("[background-agent] Failed to notify on error:", err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -795,7 +796,7 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
if (!taskIDs) {
|
if (!taskIDs) {
|
||||||
const result: BackgroundTask[] = []
|
const result: BackgroundTask[] = []
|
||||||
for (const task of this.tasks.values()) {
|
for (const task of this.tasks.values()) {
|
||||||
if (task.parentSessionID === sessionID) {
|
if (task.parentSessionId === sessionID) {
|
||||||
result.push(task)
|
result.push(task)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -818,8 +819,8 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
|
|
||||||
for (const child of directChildren) {
|
for (const child of directChildren) {
|
||||||
result.push(child)
|
result.push(child)
|
||||||
if (child.sessionID) {
|
if (child.sessionId) {
|
||||||
const descendants = this.getAllDescendantTasks(child.sessionID)
|
const descendants = this.getAllDescendantTasks(child.sessionId)
|
||||||
result.push(...descendants)
|
result.push(...descendants)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -829,7 +830,7 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
|
|
||||||
findBySession(sessionID: string): BackgroundTask | undefined {
|
findBySession(sessionID: string): BackgroundTask | undefined {
|
||||||
for (const task of this.tasks.values()) {
|
for (const task of this.tasks.values()) {
|
||||||
if (task.sessionID === sessionID) {
|
if (task.sessionId === sessionID) {
|
||||||
return task
|
return task
|
||||||
}
|
}
|
||||||
if (findAttemptBySession(task, sessionID)) {
|
if (findAttemptBySession(task, sessionID)) {
|
||||||
@@ -850,14 +851,14 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
return {
|
return {
|
||||||
task,
|
task,
|
||||||
attemptID: undefined,
|
attemptID: undefined,
|
||||||
isCurrent: task.sessionID === sessionID,
|
isCurrent: task.sessionId === sessionID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
task,
|
task,
|
||||||
attemptID: attempt.attemptID,
|
attemptID: attempt.attemptId,
|
||||||
isCurrent: task.currentAttemptID === attempt.attemptID,
|
isCurrent: task.currentAttemptID === attempt.attemptId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -874,8 +875,8 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
*/
|
*/
|
||||||
async trackTask(input: {
|
async trackTask(input: {
|
||||||
taskId: string
|
taskId: string
|
||||||
sessionID: string
|
sessionId: string
|
||||||
parentSessionID: string
|
parentSessionId: string
|
||||||
description: string
|
description: string
|
||||||
agent?: string
|
agent?: string
|
||||||
parentAgent?: string
|
parentAgent?: string
|
||||||
@@ -885,10 +886,10 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
if (existingTask) {
|
if (existingTask) {
|
||||||
// P2 fix: Clean up old parent's pending set BEFORE changing parent
|
// P2 fix: Clean up old parent's pending set BEFORE changing parent
|
||||||
// Otherwise cleanupPendingByParent would use the new parent ID
|
// Otherwise cleanupPendingByParent would use the new parent ID
|
||||||
const parentChanged = input.parentSessionID !== existingTask.parentSessionID
|
const parentChanged = input.parentSessionId !== existingTask.parentSessionId
|
||||||
if (parentChanged) {
|
if (parentChanged) {
|
||||||
this.cleanupPendingByParent(existingTask) // Clean from OLD parent
|
this.cleanupPendingByParent(existingTask) // Clean from OLD parent
|
||||||
this.updateTaskParent(existingTask, input.parentSessionID)
|
this.updateTaskParent(existingTask, input.parentSessionId)
|
||||||
}
|
}
|
||||||
if (input.parentAgent !== undefined) {
|
if (input.parentAgent !== undefined) {
|
||||||
existingTask.parentAgent = input.parentAgent
|
existingTask.parentAgent = input.parentAgent
|
||||||
@@ -897,22 +898,22 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
existingTask.concurrencyGroup = input.concurrencyKey ?? existingTask.agent
|
existingTask.concurrencyGroup = input.concurrencyKey ?? existingTask.agent
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingTask.sessionID) {
|
if (existingTask.sessionId) {
|
||||||
subagentSessions.add(existingTask.sessionID)
|
subagentSessions.add(existingTask.sessionId)
|
||||||
}
|
}
|
||||||
this.startPolling()
|
this.startPolling()
|
||||||
|
|
||||||
// Track for batched notifications if task is pending or running
|
// Track for batched notifications if task is pending or running
|
||||||
if (existingTask.status === "pending" || existingTask.status === "running") {
|
if (existingTask.status === "pending" || existingTask.status === "running") {
|
||||||
const pending = this.pendingByParent.get(input.parentSessionID) ?? new Set()
|
const pending = this.pendingByParent.get(input.parentSessionId) ?? new Set()
|
||||||
pending.add(existingTask.id)
|
pending.add(existingTask.id)
|
||||||
this.pendingByParent.set(input.parentSessionID, pending)
|
this.pendingByParent.set(input.parentSessionId, pending)
|
||||||
} else if (!parentChanged) {
|
} else if (!parentChanged) {
|
||||||
// Only clean up if parent didn't change (already cleaned above if it did)
|
// Only clean up if parent didn't change (already cleaned above if it did)
|
||||||
this.cleanupPendingByParent(existingTask)
|
this.cleanupPendingByParent(existingTask)
|
||||||
}
|
}
|
||||||
|
|
||||||
log("[background-agent] External task already registered:", { taskId: existingTask.id, sessionID: existingTask.sessionID, status: existingTask.status })
|
log("[background-agent] External task already registered:", { taskId: existingTask.id, sessionID: existingTask.sessionId, status: existingTask.status })
|
||||||
|
|
||||||
return existingTask
|
return existingTask
|
||||||
}
|
}
|
||||||
@@ -926,9 +927,9 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
|
|
||||||
const task: BackgroundTask = {
|
const task: BackgroundTask = {
|
||||||
id: input.taskId,
|
id: input.taskId,
|
||||||
sessionID: input.sessionID,
|
sessionId: input.sessionId,
|
||||||
parentSessionID: input.parentSessionID,
|
parentSessionId: input.parentSessionId,
|
||||||
parentMessageID: "",
|
parentMessageId: "",
|
||||||
description: input.description,
|
description: input.description,
|
||||||
prompt: "",
|
prompt: "",
|
||||||
agent: input.agent || "task",
|
agent: input.agent || "task",
|
||||||
@@ -944,17 +945,17 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.addTask(task)
|
this.addTask(task)
|
||||||
subagentSessions.add(input.sessionID)
|
subagentSessions.add(input.sessionId)
|
||||||
this.startPolling()
|
this.startPolling()
|
||||||
this.taskHistory.record(input.parentSessionID, { id: task.id, sessionID: input.sessionID, agent: input.agent || "task", description: input.description, status: "running", startedAt: task.startedAt })
|
this.taskHistory.record(input.parentSessionId, { id: task.id, sessionID: input.sessionId, agent: input.agent || "task", description: input.description, status: "running", startedAt: task.startedAt })
|
||||||
|
|
||||||
if (input.parentSessionID) {
|
if (input.parentSessionId) {
|
||||||
const pending = this.pendingByParent.get(input.parentSessionID) ?? new Set()
|
const pending = this.pendingByParent.get(input.parentSessionId) ?? new Set()
|
||||||
pending.add(task.id)
|
pending.add(task.id)
|
||||||
this.pendingByParent.set(input.parentSessionID, pending)
|
this.pendingByParent.set(input.parentSessionId, pending)
|
||||||
}
|
}
|
||||||
|
|
||||||
log("[background-agent] Registered external task:", { taskId: task.id, sessionID: input.sessionID })
|
log("[background-agent] Registered external task:", { taskId: task.id, sessionID: input.sessionId })
|
||||||
|
|
||||||
return task
|
return task
|
||||||
}
|
}
|
||||||
@@ -965,14 +966,14 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
throw new Error(`Task not found for session: ${input.sessionId}`)
|
throw new Error(`Task not found for session: ${input.sessionId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!existingTask.sessionID) {
|
if (!existingTask.sessionId) {
|
||||||
throw new Error(`Task has no sessionID: ${existingTask.id}`)
|
throw new Error(`Task has no sessionID: ${existingTask.id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingTask.status === "running") {
|
if (existingTask.status === "running") {
|
||||||
log("[background-agent] Resume skipped - task already running:", {
|
log("[background-agent] Resume skipped - task already running:", {
|
||||||
taskId: existingTask.id,
|
taskId: existingTask.id,
|
||||||
sessionID: existingTask.sessionID,
|
sessionID: existingTask.sessionId,
|
||||||
})
|
})
|
||||||
return existingTask
|
return existingTask
|
||||||
}
|
}
|
||||||
@@ -993,8 +994,8 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
existingTask.status = "running"
|
existingTask.status = "running"
|
||||||
existingTask.completedAt = undefined
|
existingTask.completedAt = undefined
|
||||||
existingTask.error = undefined
|
existingTask.error = undefined
|
||||||
this.updateTaskParent(existingTask, input.parentSessionID)
|
this.updateTaskParent(existingTask, input.parentSessionId)
|
||||||
existingTask.parentMessageID = input.parentMessageID
|
existingTask.parentMessageId = input.parentMessageId
|
||||||
existingTask.parentModel = input.parentModel
|
existingTask.parentModel = input.parentModel
|
||||||
existingTask.parentAgent = input.parentAgent
|
existingTask.parentAgent = input.parentAgent
|
||||||
if (input.parentTools) {
|
if (input.parentTools) {
|
||||||
@@ -1012,14 +1013,14 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.startPolling()
|
this.startPolling()
|
||||||
if (existingTask.sessionID) {
|
if (existingTask.sessionId) {
|
||||||
subagentSessions.add(existingTask.sessionID)
|
subagentSessions.add(existingTask.sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.parentSessionID) {
|
if (input.parentSessionId) {
|
||||||
const pending = this.pendingByParent.get(input.parentSessionID) ?? new Set()
|
const pending = this.pendingByParent.get(input.parentSessionId) ?? new Set()
|
||||||
pending.add(existingTask.id)
|
pending.add(existingTask.id)
|
||||||
this.pendingByParent.set(input.parentSessionID, pending)
|
this.pendingByParent.set(input.parentSessionId, pending)
|
||||||
}
|
}
|
||||||
|
|
||||||
const toastManager = getTaskToastManager()
|
const toastManager = getTaskToastManager()
|
||||||
@@ -1032,10 +1033,10 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
log("[background-agent] Resuming task:", { taskId: existingTask.id, sessionID: existingTask.sessionID })
|
log("[background-agent] Resuming task:", { taskId: existingTask.id, sessionID: existingTask.sessionId })
|
||||||
|
|
||||||
log("[background-agent] Resuming task - calling prompt (fire-and-forget) with:", {
|
log("[background-agent] Resuming task - calling prompt (fire-and-forget) with:", {
|
||||||
sessionID: existingTask.sessionID,
|
sessionID: existingTask.sessionId,
|
||||||
agent: existingTask.agent,
|
agent: existingTask.agent,
|
||||||
model: existingTask.model,
|
model: existingTask.model,
|
||||||
promptLength: input.prompt.length,
|
promptLength: input.prompt.length,
|
||||||
@@ -1052,11 +1053,11 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
const resumeVariant = existingTask.model?.variant
|
const resumeVariant = existingTask.model?.variant
|
||||||
|
|
||||||
if (existingTask.model) {
|
if (existingTask.model) {
|
||||||
applySessionPromptParams(existingTask.sessionID!, existingTask.model)
|
applySessionPromptParams(existingTask.sessionId!, existingTask.model)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.client.session.promptAsync({
|
this.client.session.promptAsync({
|
||||||
path: { id: existingTask.sessionID },
|
path: { id: existingTask.sessionId },
|
||||||
body: {
|
body: {
|
||||||
agent: existingTask.agent,
|
agent: existingTask.agent,
|
||||||
...(resumeModel ? { model: resumeModel } : {}),
|
...(resumeModel ? { model: resumeModel } : {}),
|
||||||
@@ -1068,7 +1069,7 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
question: false,
|
question: false,
|
||||||
...getAgentToolRestrictions(existingTask.agent),
|
...getAgentToolRestrictions(existingTask.agent),
|
||||||
}
|
}
|
||||||
setSessionTools(existingTask.sessionID!, tools)
|
setSessionTools(existingTask.sessionId!, tools)
|
||||||
return tools
|
return tools
|
||||||
})(),
|
})(),
|
||||||
parts: [createInternalAgentTextPart(input.prompt)],
|
parts: [createInternalAgentTextPart(input.prompt)],
|
||||||
@@ -1087,8 +1088,8 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
const errorMessage = errorInfo.message ?? (error instanceof Error ? error.message : String(error))
|
const errorMessage = errorInfo.message ?? (error instanceof Error ? error.message : String(error))
|
||||||
existingTask.error = errorMessage
|
existingTask.error = errorMessage
|
||||||
existingTask.completedAt = new Date()
|
existingTask.completedAt = new Date()
|
||||||
if (existingTask.rootSessionID) {
|
if (existingTask.rootSessionId) {
|
||||||
this.unregisterRootDescendant(existingTask.rootSessionID)
|
this.unregisterRootDescendant(existingTask.rootSessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release concurrency on error to prevent slot leaks
|
// Release concurrency on error to prevent slot leaks
|
||||||
@@ -1101,12 +1102,12 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
|
|
||||||
// Abort the session to prevent infinite polling hang
|
// Abort the session to prevent infinite polling hang
|
||||||
// Awaited to prevent dangling promise during subagent teardown (Bun/WebKit SIGABRT)
|
// Awaited to prevent dangling promise during subagent teardown (Bun/WebKit SIGABRT)
|
||||||
if (existingTask.sessionID) {
|
if (existingTask.sessionId) {
|
||||||
await this.abortSessionWithLogging(existingTask.sessionID, "resume error cleanup")
|
await this.abortSessionWithLogging(existingTask.sessionId, "resume error cleanup")
|
||||||
}
|
}
|
||||||
|
|
||||||
this.markForNotification(existingTask)
|
this.markForNotification(existingTask)
|
||||||
this.enqueueNotificationForParent(existingTask.parentSessionID, () => this.notifyParentSession(existingTask)).catch(err => {
|
this.enqueueNotificationForParent(existingTask.parentSessionId, () => this.notifyParentSession(existingTask)).catch(err => {
|
||||||
log("[background-agent] Failed to notify on resume error:", err)
|
log("[background-agent] Failed to notify on resume error:", err)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -1386,25 +1387,25 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
|
|
||||||
const deletedSessionIDs = new Set<string>([sessionID])
|
const deletedSessionIDs = new Set<string>([sessionID])
|
||||||
for (const task of tasksToCancel.values()) {
|
for (const task of tasksToCancel.values()) {
|
||||||
if (task.sessionID) {
|
if (task.sessionId) {
|
||||||
deletedSessionIDs.add(task.sessionID)
|
deletedSessionIDs.add(task.sessionId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const task of tasksToCancel.values()) {
|
for (const task of tasksToCancel.values()) {
|
||||||
parentSessionsToClear.add(task.parentSessionID)
|
parentSessionsToClear.add(task.parentSessionId)
|
||||||
|
|
||||||
if (task.status === "running" || task.status === "pending") {
|
if (task.status === "running" || task.status === "pending") {
|
||||||
void this.cancelTask(task.id, {
|
void this.cancelTask(task.id, {
|
||||||
source: "session.deleted",
|
source: "session.deleted",
|
||||||
reason: "Session deleted",
|
reason: "Session deleted",
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
if (deletedSessionIDs.has(task.parentSessionID)) {
|
if (deletedSessionIDs.has(task.parentSessionId)) {
|
||||||
this.pendingNotifications.delete(task.parentSessionID)
|
this.pendingNotifications.delete(task.parentSessionId)
|
||||||
}
|
}
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
if (deletedSessionIDs.has(task.parentSessionID)) {
|
if (deletedSessionIDs.has(task.parentSessionId)) {
|
||||||
this.pendingNotifications.delete(task.parentSessionID)
|
this.pendingNotifications.delete(task.parentSessionId)
|
||||||
}
|
}
|
||||||
log("[background-agent] Failed to cancel task on session.deleted:", { taskId: task.id, error: err })
|
log("[background-agent] Failed to cancel task on session.deleted:", { taskId: task.id, error: err })
|
||||||
})
|
})
|
||||||
@@ -1449,8 +1450,8 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const { task, errorInfo, errorMessage, errorName } = args
|
const { task, errorInfo, errorMessage, errorName } = args
|
||||||
|
|
||||||
if (!task.fallbackChain && task.sessionID) {
|
if (!task.fallbackChain && task.sessionId) {
|
||||||
const sessionFallbackChain = this.modelFallbackControllerAccessor?.getSessionFallbackChain(task.sessionID)
|
const sessionFallbackChain = this.modelFallbackControllerAccessor?.getSessionFallbackChain(task.sessionId)
|
||||||
if (sessionFallbackChain?.length) {
|
if (sessionFallbackChain?.length) {
|
||||||
task.fallbackChain = sessionFallbackChain
|
task.fallbackChain = sessionFallbackChain
|
||||||
}
|
}
|
||||||
@@ -1490,10 +1491,10 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
task.error = errorMsg
|
task.error = errorMsg
|
||||||
task.completedAt = new Date()
|
task.completedAt = new Date()
|
||||||
}
|
}
|
||||||
if (task.rootSessionID) {
|
if (task.rootSessionId) {
|
||||||
this.unregisterRootDescendant(task.rootSessionID)
|
this.unregisterRootDescendant(task.rootSessionId)
|
||||||
}
|
}
|
||||||
this.taskHistory.record(task.parentSessionID, { id: task.id, sessionID: task.sessionID, agent: task.agent, description: task.description, status: "error", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt })
|
this.taskHistory.record(task.parentSessionId, { id: task.id, sessionID: task.sessionId, agent: task.agent, description: task.description, status: "error", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt })
|
||||||
|
|
||||||
if (task.concurrencyKey) {
|
if (task.concurrencyKey) {
|
||||||
this.concurrencyManager.release(task.concurrencyKey)
|
this.concurrencyManager.release(task.concurrencyKey)
|
||||||
@@ -1519,12 +1520,12 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
toastManager.removeTask(task.id)
|
toastManager.removeTask(task.id)
|
||||||
}
|
}
|
||||||
this.scheduleTaskRemoval(task.id)
|
this.scheduleTaskRemoval(task.id)
|
||||||
if (task.sessionID) {
|
if (task.sessionId) {
|
||||||
SessionCategoryRegistry.remove(task.sessionID)
|
SessionCategoryRegistry.remove(task.sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.markForNotification(task)
|
this.markForNotification(task)
|
||||||
this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task)).catch(err => {
|
this.enqueueNotificationForParent(task.parentSessionId, () => this.notifyParentSession(task)).catch(err => {
|
||||||
log("[background-agent] Error in notifyParentSession for errored task:", { taskId: task.id, error: err })
|
log("[background-agent] Error in notifyParentSession for errored task:", { taskId: task.id, error: err })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1534,7 +1535,7 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
errorInfo: { name?: string; message?: string },
|
errorInfo: { name?: string; message?: string },
|
||||||
source: string,
|
source: string,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const previousSessionID = task.sessionID
|
const previousSessionID = task.sessionId
|
||||||
const result = tryFallbackRetry({
|
const result = tryFallbackRetry({
|
||||||
task,
|
task,
|
||||||
errorInfo,
|
errorInfo,
|
||||||
@@ -1546,15 +1547,15 @@ The fallback retry session is now created and can be inspected directly.
|
|||||||
processKey: (key: string) => this.processKey(key),
|
processKey: (key: string) => this.processKey(key),
|
||||||
onRetrying: ({ task, source }) => {
|
onRetrying: ({ task, source }) => {
|
||||||
const currentAttempt = getCurrentAttempt(task)
|
const currentAttempt = getCurrentAttempt(task)
|
||||||
const previousAttempt = getPreviousAttempt(task, currentAttempt?.attemptID)
|
const previousAttempt = getPreviousAttempt(task, currentAttempt?.attemptId)
|
||||||
const sourceText = source ? ` via ${source}` : ""
|
const sourceText = source ? ` via ${source}` : ""
|
||||||
const failedSessionLine = previousAttempt?.sessionID ? `\n- Failed session: \`${previousAttempt.sessionID}\`` : ""
|
const failedSessionLine = previousAttempt?.sessionId ? `\n- Failed session: \`${previousAttempt.sessionId}\`` : ""
|
||||||
const failedModel = formatAttemptModelSummary(previousAttempt)
|
const failedModel = formatAttemptModelSummary(previousAttempt)
|
||||||
const failedModelLine = failedModel ? `\n- Failed model: \`${failedModel}\`` : ""
|
const failedModelLine = failedModel ? `\n- Failed model: \`${failedModel}\`` : ""
|
||||||
const failedErrorLine = previousAttempt?.error ? `\n- Error: ${previousAttempt.error}` : ""
|
const failedErrorLine = previousAttempt?.error ? `\n- Error: ${previousAttempt.error}` : ""
|
||||||
const nextModel = formatAttemptModelSummary(currentAttempt)
|
const nextModel = formatAttemptModelSummary(currentAttempt)
|
||||||
this.queuePendingNotification(
|
this.queuePendingNotification(
|
||||||
task.parentSessionID,
|
task.parentSessionId,
|
||||||
`<system-reminder>
|
`<system-reminder>
|
||||||
[BACKGROUND TASK RETRYING]
|
[BACKGROUND TASK RETRYING]
|
||||||
**ID:** \`${task.id}\`
|
**ID:** \`${task.id}\`
|
||||||
@@ -1576,9 +1577,9 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
}
|
}
|
||||||
|
|
||||||
markForNotification(task: BackgroundTask): void {
|
markForNotification(task: BackgroundTask): void {
|
||||||
const queue = this.notifications.get(task.parentSessionID) ?? []
|
const queue = this.notifications.get(task.parentSessionId) ?? []
|
||||||
queue.push(task)
|
queue.push(task)
|
||||||
this.notifications.set(task.parentSessionID, queue)
|
this.notifications.set(task.parentSessionId, queue)
|
||||||
}
|
}
|
||||||
|
|
||||||
getPendingNotifications(sessionID: string): BackgroundTask[] {
|
getPendingNotifications(sessionID: string): BackgroundTask[] {
|
||||||
@@ -1695,12 +1696,12 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
* Cleans up the parent entry if no pending tasks remain.
|
* Cleans up the parent entry if no pending tasks remain.
|
||||||
*/
|
*/
|
||||||
private cleanupPendingByParent(task: BackgroundTask): void {
|
private cleanupPendingByParent(task: BackgroundTask): void {
|
||||||
if (!task.parentSessionID) return
|
if (!task.parentSessionId) return
|
||||||
const pending = this.pendingByParent.get(task.parentSessionID)
|
const pending = this.pendingByParent.get(task.parentSessionId)
|
||||||
if (pending) {
|
if (pending) {
|
||||||
pending.delete(task.id)
|
pending.delete(task.id)
|
||||||
if (pending.size === 0) {
|
if (pending.size === 0) {
|
||||||
this.pendingByParent.delete(task.parentSessionID)
|
this.pendingByParent.delete(task.parentSessionId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1724,8 +1725,8 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
const task = this.tasks.get(taskId)
|
const task = this.tasks.get(taskId)
|
||||||
if (!task) return
|
if (!task) return
|
||||||
|
|
||||||
if (task.parentSessionID) {
|
if (task.parentSessionId) {
|
||||||
const siblings = this.getTasksByParentSession(task.parentSessionID)
|
const siblings = this.getTasksByParentSession(task.parentSessionId)
|
||||||
const runningOrPendingSiblings = siblings.filter(
|
const runningOrPendingSiblings = siblings.filter(
|
||||||
sibling => sibling.id !== taskId && (sibling.status === "running" || sibling.status === "pending"),
|
sibling => sibling.id !== taskId && (sibling.status === "running" || sibling.status === "pending"),
|
||||||
)
|
)
|
||||||
@@ -1739,10 +1740,10 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
|
|
||||||
this.clearNotificationsForTask(taskId)
|
this.clearNotificationsForTask(taskId)
|
||||||
this.removeTask(task)
|
this.removeTask(task)
|
||||||
this.clearTaskHistoryWhenParentTasksGone(task.parentSessionID)
|
this.clearTaskHistoryWhenParentTasksGone(task.parentSessionId)
|
||||||
if (task.sessionID) {
|
if (task.sessionId) {
|
||||||
subagentSessions.delete(task.sessionID)
|
subagentSessions.delete(task.sessionId)
|
||||||
SessionCategoryRegistry.remove(task.sessionID)
|
SessionCategoryRegistry.remove(task.sessionId)
|
||||||
}
|
}
|
||||||
log("[background-agent] Removed completed task from memory:", taskId)
|
log("[background-agent] Removed completed task from memory:", taskId)
|
||||||
}, TASK_CLEANUP_DELAY_MS)
|
}, TASK_CLEANUP_DELAY_MS)
|
||||||
@@ -1791,10 +1792,10 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
task.error = reason
|
task.error = reason
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (wasRunning && task.rootSessionID) {
|
if (wasRunning && task.rootSessionId) {
|
||||||
this.unregisterRootDescendant(task.rootSessionID)
|
this.unregisterRootDescendant(task.rootSessionId)
|
||||||
}
|
}
|
||||||
this.taskHistory.record(task.parentSessionID, { id: task.id, sessionID: task.sessionID, agent: task.agent, description: task.description, status: "cancelled", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt })
|
this.taskHistory.record(task.parentSessionId, { id: task.id, sessionID: task.sessionId, agent: task.agent, description: task.description, status: "cancelled", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt })
|
||||||
|
|
||||||
if (task.concurrencyKey) {
|
if (task.concurrencyKey) {
|
||||||
this.concurrencyManager.release(task.concurrencyKey)
|
this.concurrencyManager.release(task.concurrencyKey)
|
||||||
@@ -1813,11 +1814,11 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
this.idleDeferralTimers.delete(task.id)
|
this.idleDeferralTimers.delete(task.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abortSession && task.sessionID) {
|
if (abortSession && task.sessionId) {
|
||||||
// Awaited to prevent dangling promise during subagent teardown (Bun/WebKit SIGABRT)
|
// Awaited to prevent dangling promise during subagent teardown (Bun/WebKit SIGABRT)
|
||||||
await this.abortSessionWithLogging(task.sessionID, `task cancellation (${source})`)
|
await this.abortSessionWithLogging(task.sessionId, `task cancellation (${source})`)
|
||||||
|
|
||||||
SessionCategoryRegistry.remove(task.sessionID)
|
SessionCategoryRegistry.remove(task.sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeTaskToastTracking(task.id)
|
removeTaskToastTracking(task.id)
|
||||||
@@ -1832,7 +1833,7 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
this.markForNotification(task)
|
this.markForNotification(task)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task))
|
await this.enqueueNotificationForParent(task.parentSessionId, () => this.notifyParentSession(task))
|
||||||
log(`[background-agent] Task cancelled via ${source}:`, task.id)
|
log(`[background-agent] Task cancelled via ${source}:`, task.id)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log("[background-agent] Error in notifyParentSession for cancelled task:", { taskId: task.id, error: err })
|
log("[background-agent] Error in notifyParentSession for cancelled task:", { taskId: task.id, error: err })
|
||||||
@@ -1911,10 +1912,10 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
task.status = "completed"
|
task.status = "completed"
|
||||||
task.completedAt = new Date()
|
task.completedAt = new Date()
|
||||||
}
|
}
|
||||||
this.taskHistory.record(task.parentSessionID, { id: task.id, sessionID: task.sessionID, agent: task.agent, description: task.description, status: "completed", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt })
|
this.taskHistory.record(task.parentSessionId, { id: task.id, sessionID: task.sessionId, agent: task.agent, description: task.description, status: "completed", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt })
|
||||||
|
|
||||||
if (task.rootSessionID) {
|
if (task.rootSessionId) {
|
||||||
this.unregisterRootDescendant(task.rootSessionID)
|
this.unregisterRootDescendant(task.rootSessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeTaskToastTracking(task.id)
|
removeTaskToastTracking(task.id)
|
||||||
@@ -1933,15 +1934,15 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
this.idleDeferralTimers.delete(task.id)
|
this.idleDeferralTimers.delete(task.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task.sessionID) {
|
if (task.sessionId) {
|
||||||
// Awaited to prevent dangling promise during subagent teardown (Bun/WebKit SIGABRT)
|
// Awaited to prevent dangling promise during subagent teardown (Bun/WebKit SIGABRT)
|
||||||
await this.abortSessionWithLogging(task.sessionID, `task completion (${source})`)
|
await this.abortSessionWithLogging(task.sessionId, `task completion (${source})`)
|
||||||
|
|
||||||
SessionCategoryRegistry.remove(task.sessionID)
|
SessionCategoryRegistry.remove(task.sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task))
|
await this.enqueueNotificationForParent(task.parentSessionId, () => this.notifyParentSession(task))
|
||||||
log(`[background-agent] Task completed via ${source}:`, task.id)
|
log(`[background-agent] Task completed via ${source}:`, task.id)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log("[background-agent] Error in notifyParentSession:", { taskId: task.id, error: err })
|
log("[background-agent] Error in notifyParentSession:", { taskId: task.id, error: err })
|
||||||
@@ -1966,10 +1967,10 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.completedTaskSummaries.has(task.parentSessionID)) {
|
if (!this.completedTaskSummaries.has(task.parentSessionId)) {
|
||||||
this.completedTaskSummaries.set(task.parentSessionID, [])
|
this.completedTaskSummaries.set(task.parentSessionId, [])
|
||||||
}
|
}
|
||||||
this.completedTaskSummaries.get(task.parentSessionID)!.push({
|
this.completedTaskSummaries.get(task.parentSessionId)!.push({
|
||||||
id: task.id,
|
id: task.id,
|
||||||
description: task.description,
|
description: task.description,
|
||||||
status: task.status,
|
status: task.status,
|
||||||
@@ -1978,7 +1979,7 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Update pending tracking and check if all tasks complete
|
// Update pending tracking and check if all tasks complete
|
||||||
const pendingSet = this.pendingByParent.get(task.parentSessionID)
|
const pendingSet = this.pendingByParent.get(task.parentSessionId)
|
||||||
let allComplete = false
|
let allComplete = false
|
||||||
let remainingCount = 0
|
let remainingCount = 0
|
||||||
if (pendingSet) {
|
if (pendingSet) {
|
||||||
@@ -1986,21 +1987,21 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
remainingCount = pendingSet.size
|
remainingCount = pendingSet.size
|
||||||
allComplete = remainingCount === 0
|
allComplete = remainingCount === 0
|
||||||
if (allComplete) {
|
if (allComplete) {
|
||||||
this.pendingByParent.delete(task.parentSessionID)
|
this.pendingByParent.delete(task.parentSessionId)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
remainingCount = Array.from(this.tasks.values())
|
remainingCount = Array.from(this.tasks.values())
|
||||||
.filter(t => t.parentSessionID === task.parentSessionID && t.id !== task.id && (t.status === "running" || t.status === "pending"))
|
.filter(t => t.parentSessionId === task.parentSessionId && t.id !== task.id && (t.status === "running" || t.status === "pending"))
|
||||||
.length
|
.length
|
||||||
allComplete = remainingCount === 0
|
allComplete = remainingCount === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const completedTasks = allComplete
|
const completedTasks = allComplete
|
||||||
? (this.completedTaskSummaries.get(task.parentSessionID) ?? [{ id: task.id, description: task.description, status: task.status, error: task.error, attempts: cloneAttempts(task) }])
|
? (this.completedTaskSummaries.get(task.parentSessionId) ?? [{ id: task.id, description: task.description, status: task.status, error: task.error, attempts: cloneAttempts(task) }])
|
||||||
: []
|
: []
|
||||||
|
|
||||||
if (allComplete) {
|
if (allComplete) {
|
||||||
this.completedTaskSummaries.delete(task.parentSessionID)
|
this.completedTaskSummaries.delete(task.parentSessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusText = task.status === "completed"
|
const statusText = task.status === "completed"
|
||||||
@@ -2026,7 +2027,7 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
|
|
||||||
if (this.enableParentSessionNotifications) {
|
if (this.enableParentSessionNotifications) {
|
||||||
try {
|
try {
|
||||||
const messagesResp = await this.client.session.messages({ path: { id: task.parentSessionID } })
|
const messagesResp = await this.client.session.messages({ path: { id: task.parentSessionId } })
|
||||||
const messages = normalizeSDKResponse(messagesResp, [] as Array<{
|
const messages = normalizeSDKResponse(messagesResp, [] as Array<{
|
||||||
info?: {
|
info?: {
|
||||||
agent?: string
|
agent?: string
|
||||||
@@ -2038,7 +2039,7 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
}>)
|
}>)
|
||||||
promptContext = resolvePromptContextFromSessionMessages(
|
promptContext = resolvePromptContextFromSessionMessages(
|
||||||
messages,
|
messages,
|
||||||
task.parentSessionID,
|
task.parentSessionId,
|
||||||
)
|
)
|
||||||
const normalizedTools = isRecord(promptContext?.tools)
|
const normalizedTools = isRecord(promptContext?.tools)
|
||||||
? normalizePromptTools(promptContext.tools)
|
? normalizePromptTools(promptContext.tools)
|
||||||
@@ -2055,12 +2056,12 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
if (isAbortedSessionError(error)) {
|
if (isAbortedSessionError(error)) {
|
||||||
log("[background-agent] Parent session aborted while loading messages; using messageDir fallback:", {
|
log("[background-agent] Parent session aborted while loading messages; using messageDir fallback:", {
|
||||||
taskId: task.id,
|
taskId: task.id,
|
||||||
parentSessionID: task.parentSessionID,
|
parentSessionID: task.parentSessionId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const messageDir = join(MESSAGE_STORAGE, task.parentSessionID)
|
const messageDir = join(MESSAGE_STORAGE, task.parentSessionId)
|
||||||
const currentMessage = messageDir
|
const currentMessage = messageDir
|
||||||
? findNearestMessageExcludingCompaction(messageDir, task.parentSessionID)
|
? findNearestMessageExcludingCompaction(messageDir, task.parentSessionId)
|
||||||
: null
|
: null
|
||||||
agent = currentMessage?.agent ?? task.parentAgent
|
agent = currentMessage?.agent ?? task.parentAgent
|
||||||
model = currentMessage?.model?.providerID && currentMessage?.model?.modelID
|
model = currentMessage?.model?.providerID && currentMessage?.model?.modelID
|
||||||
@@ -2069,7 +2070,7 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
tools = normalizePromptTools(currentMessage?.tools) ?? tools
|
tools = normalizePromptTools(currentMessage?.tools) ?? tools
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolvedTools = resolveInheritedPromptTools(task.parentSessionID, tools)
|
const resolvedTools = resolveInheritedPromptTools(task.parentSessionId, tools)
|
||||||
|
|
||||||
log("[background-agent] notifyParentSession context:", {
|
log("[background-agent] notifyParentSession context:", {
|
||||||
taskId: task.id,
|
taskId: task.id,
|
||||||
@@ -2084,7 +2085,7 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.client.session.promptAsync({
|
await this.client.session.promptAsync({
|
||||||
path: { id: task.parentSessionID },
|
path: { id: task.parentSessionId },
|
||||||
body: {
|
body: {
|
||||||
noReply: !shouldReply,
|
noReply: !shouldReply,
|
||||||
...(agent !== undefined ? { agent } : {}),
|
...(agent !== undefined ? { agent } : {}),
|
||||||
@@ -2104,9 +2105,9 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
if (isAbortedSessionError(error)) {
|
if (isAbortedSessionError(error)) {
|
||||||
log("[background-agent] Parent session aborted while sending notification; continuing cleanup:", {
|
log("[background-agent] Parent session aborted while sending notification; continuing cleanup:", {
|
||||||
taskId: task.id,
|
taskId: task.id,
|
||||||
parentSessionID: task.parentSessionID,
|
parentSessionID: task.parentSessionId,
|
||||||
})
|
})
|
||||||
this.queuePendingNotification(task.parentSessionID, notification)
|
this.queuePendingNotification(task.parentSessionId, notification)
|
||||||
} else {
|
} else {
|
||||||
log("[background-agent] Failed to send notification:", error)
|
log("[background-agent] Failed to send notification:", error)
|
||||||
}
|
}
|
||||||
@@ -2114,7 +2115,7 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
} else {
|
} else {
|
||||||
log("[background-agent] Parent session notifications disabled, skipping prompt injection:", {
|
log("[background-agent] Parent session notifications disabled, skipping prompt injection:", {
|
||||||
taskId: task.id,
|
taskId: task.id,
|
||||||
parentSessionID: task.parentSessionID,
|
parentSessionID: task.parentSessionId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2141,10 +2142,10 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
task.status = "error"
|
task.status = "error"
|
||||||
task.error = errorMessage
|
task.error = errorMessage
|
||||||
task.completedAt = new Date()
|
task.completedAt = new Date()
|
||||||
if (!wasPending && task.rootSessionID) {
|
if (!wasPending && task.rootSessionId) {
|
||||||
this.unregisterRootDescendant(task.rootSessionID)
|
this.unregisterRootDescendant(task.rootSessionId)
|
||||||
}
|
}
|
||||||
this.taskHistory.record(task.parentSessionID, { id: task.id, sessionID: task.sessionID, agent: task.agent, description: task.description, status: "error", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt })
|
this.taskHistory.record(task.parentSessionId, { id: task.id, sessionID: task.sessionId, agent: task.agent, description: task.description, status: "error", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt })
|
||||||
if (task.concurrencyKey) {
|
if (task.concurrencyKey) {
|
||||||
this.concurrencyManager.release(task.concurrencyKey)
|
this.concurrencyManager.release(task.concurrencyKey)
|
||||||
task.concurrencyKey = undefined
|
task.concurrencyKey = undefined
|
||||||
@@ -2177,7 +2178,7 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
}
|
}
|
||||||
this.cleanupPendingByParent(task)
|
this.cleanupPendingByParent(task)
|
||||||
this.markForNotification(task)
|
this.markForNotification(task)
|
||||||
this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task)).catch(err => {
|
this.enqueueNotificationForParent(task.parentSessionId, () => this.notifyParentSession(task)).catch(err => {
|
||||||
log("[background-agent] Error in notifyParentSession for stale-pruned task:", { taskId: task.id, error: err })
|
log("[background-agent] Error in notifyParentSession for stale-pruned task:", { taskId: task.id, error: err })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -2193,7 +2194,7 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
directory: this.directory,
|
directory: this.directory,
|
||||||
config: this.config,
|
config: this.config,
|
||||||
concurrencyManager: this.concurrencyManager,
|
concurrencyManager: this.concurrencyManager,
|
||||||
notifyParentSession: (task) => this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task)),
|
notifyParentSession: (task) => this.enqueueNotificationForParent(task.parentSessionId, () => this.notifyParentSession(task)),
|
||||||
sessionStatuses: allStatuses,
|
sessionStatuses: allStatuses,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -2210,10 +2211,10 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
task.error = errorMessage
|
task.error = errorMessage
|
||||||
task.completedAt = new Date()
|
task.completedAt = new Date()
|
||||||
}
|
}
|
||||||
if (task.rootSessionID) {
|
if (task.rootSessionId) {
|
||||||
this.unregisterRootDescendant(task.rootSessionID)
|
this.unregisterRootDescendant(task.rootSessionId)
|
||||||
}
|
}
|
||||||
this.taskHistory.record(task.parentSessionID, { id: task.id, sessionID: task.sessionID, agent: task.agent, description: task.description, status: "error", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt })
|
this.taskHistory.record(task.parentSessionId, { id: task.id, sessionID: task.sessionId, agent: task.agent, description: task.description, status: "error", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt })
|
||||||
if (task.concurrencyKey) {
|
if (task.concurrencyKey) {
|
||||||
this.concurrencyManager.release(task.concurrencyKey)
|
this.concurrencyManager.release(task.concurrencyKey)
|
||||||
task.concurrencyKey = undefined
|
task.concurrencyKey = undefined
|
||||||
@@ -2234,12 +2235,12 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
this.clearNotificationsForTask(task.id)
|
this.clearNotificationsForTask(task.id)
|
||||||
removeTaskToastTracking(task.id)
|
removeTaskToastTracking(task.id)
|
||||||
this.scheduleTaskRemoval(task.id)
|
this.scheduleTaskRemoval(task.id)
|
||||||
if (task.sessionID) {
|
if (task.sessionId) {
|
||||||
SessionCategoryRegistry.remove(task.sessionID)
|
SessionCategoryRegistry.remove(task.sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.markForNotification(task)
|
this.markForNotification(task)
|
||||||
this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task)).catch(err => {
|
this.enqueueNotificationForParent(task.parentSessionId, () => this.notifyParentSession(task)).catch(err => {
|
||||||
log("[background-agent] Error in notifyParentSession for crashed task:", { taskId: task.id, error: err })
|
log("[background-agent] Error in notifyParentSession for crashed task:", { taskId: task.id, error: err })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -2258,7 +2259,7 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
for (const task of this.tasks.values()) {
|
for (const task of this.tasks.values()) {
|
||||||
if (task.status !== "running") continue
|
if (task.status !== "running") continue
|
||||||
|
|
||||||
const sessionID = task.sessionID
|
const sessionID = task.sessionId
|
||||||
if (!sessionID) continue
|
if (!sessionID) continue
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -2360,14 +2361,14 @@ The task was re-queued on a fallback model after a retryable failure.
|
|||||||
|
|
||||||
// Abort all running sessions to prevent zombie processes (#1240)
|
// Abort all running sessions to prevent zombie processes (#1240)
|
||||||
for (const task of this.tasks.values()) {
|
for (const task of this.tasks.values()) {
|
||||||
if (task.sessionID) {
|
if (task.sessionId) {
|
||||||
trackedSessionIDs.add(task.sessionID)
|
trackedSessionIDs.add(task.sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task.status === "running" && task.sessionID) {
|
if (task.status === "running" && task.sessionId) {
|
||||||
abortRequests.push({
|
abortRequests.push({
|
||||||
sessionID: task.sessionID,
|
sessionID: task.sessionId,
|
||||||
promise: abortWithTimeout(this.client, task.sessionID),
|
promise: abortWithTimeout(this.client, task.sessionId),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import { MIN_IDLE_TIME_MS } from "./constants"
|
|||||||
function createRunningTask(overrides: Partial<BackgroundTask> = {}): BackgroundTask {
|
function createRunningTask(overrides: Partial<BackgroundTask> = {}): BackgroundTask {
|
||||||
return {
|
return {
|
||||||
id: "task-1",
|
id: "task-1",
|
||||||
sessionID: "ses-idle-1",
|
sessionId: "ses-idle-1",
|
||||||
parentSessionID: "parent-ses-1",
|
parentSessionId: "parent-ses-1",
|
||||||
parentMessageID: "msg-1",
|
parentMessageId: "msg-1",
|
||||||
description: "test idle handler",
|
description: "test idle handler",
|
||||||
prompt: "test",
|
prompt: "test",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -91,7 +91,7 @@ describe("handleSessionIdleBackgroundEvent", () => {
|
|||||||
|
|
||||||
//#when
|
//#when
|
||||||
handleSessionIdleBackgroundEvent({
|
handleSessionIdleBackgroundEvent({
|
||||||
properties: { sessionID: task.sessionID! },
|
properties: { sessionID: task.sessionId! },
|
||||||
findBySession: () => task,
|
findBySession: () => task,
|
||||||
idleDeferralTimers: new Map(),
|
idleDeferralTimers: new Map(),
|
||||||
validateSessionHasOutput: () => Promise.resolve(true),
|
validateSessionHasOutput: () => Promise.resolve(true),
|
||||||
@@ -113,7 +113,7 @@ describe("handleSessionIdleBackgroundEvent", () => {
|
|||||||
|
|
||||||
//#when
|
//#when
|
||||||
handleSessionIdleBackgroundEvent({
|
handleSessionIdleBackgroundEvent({
|
||||||
properties: { sessionID: task.sessionID! },
|
properties: { sessionID: task.sessionId! },
|
||||||
findBySession: () => task,
|
findBySession: () => task,
|
||||||
idleDeferralTimers: new Map(),
|
idleDeferralTimers: new Map(),
|
||||||
validateSessionHasOutput: () => Promise.resolve(true),
|
validateSessionHasOutput: () => Promise.resolve(true),
|
||||||
@@ -141,7 +141,7 @@ describe("handleSessionIdleBackgroundEvent", () => {
|
|||||||
|
|
||||||
//#when
|
//#when
|
||||||
handleSessionIdleBackgroundEvent({
|
handleSessionIdleBackgroundEvent({
|
||||||
properties: { sessionID: task.sessionID! },
|
properties: { sessionID: task.sessionId! },
|
||||||
findBySession: () => task,
|
findBySession: () => task,
|
||||||
idleDeferralTimers,
|
idleDeferralTimers,
|
||||||
validateSessionHasOutput: () => Promise.resolve(true),
|
validateSessionHasOutput: () => Promise.resolve(true),
|
||||||
@@ -175,7 +175,7 @@ describe("handleSessionIdleBackgroundEvent", () => {
|
|||||||
|
|
||||||
//#when
|
//#when
|
||||||
handleSessionIdleBackgroundEvent({
|
handleSessionIdleBackgroundEvent({
|
||||||
properties: { sessionID: task.sessionID! },
|
properties: { sessionID: task.sessionId! },
|
||||||
findBySession: () => task,
|
findBySession: () => task,
|
||||||
idleDeferralTimers,
|
idleDeferralTimers,
|
||||||
validateSessionHasOutput: () => Promise.resolve(true),
|
validateSessionHasOutput: () => Promise.resolve(true),
|
||||||
@@ -206,7 +206,7 @@ describe("handleSessionIdleBackgroundEvent", () => {
|
|||||||
|
|
||||||
//#when
|
//#when
|
||||||
handleSessionIdleBackgroundEvent({
|
handleSessionIdleBackgroundEvent({
|
||||||
properties: { sessionID: task.sessionID! },
|
properties: { sessionID: task.sessionId! },
|
||||||
findBySession: () => task,
|
findBySession: () => task,
|
||||||
idleDeferralTimers,
|
idleDeferralTimers,
|
||||||
validateSessionHasOutput: () => Promise.resolve(true),
|
validateSessionHasOutput: () => Promise.resolve(true),
|
||||||
@@ -217,7 +217,7 @@ describe("handleSessionIdleBackgroundEvent", () => {
|
|||||||
|
|
||||||
//#then - wait for deferred timer
|
//#then - wait for deferred timer
|
||||||
await new Promise((resolve) => setTimeout(resolve, remainingMs + 50))
|
await new Promise((resolve) => setTimeout(resolve, remainingMs + 50))
|
||||||
expect(emitIdleEvent).toHaveBeenCalledWith(task.sessionID)
|
expect(emitIdleEvent).toHaveBeenCalledWith(task.sessionId)
|
||||||
expect(idleDeferralTimers.has(task.id)).toBe(false)
|
expect(idleDeferralTimers.has(task.id)).toBe(false)
|
||||||
} finally {
|
} finally {
|
||||||
Date.now = realDateNow
|
Date.now = realDateNow
|
||||||
@@ -233,7 +233,7 @@ describe("handleSessionIdleBackgroundEvent", () => {
|
|||||||
|
|
||||||
//#when
|
//#when
|
||||||
handleSessionIdleBackgroundEvent({
|
handleSessionIdleBackgroundEvent({
|
||||||
properties: { sessionID: task.sessionID! },
|
properties: { sessionID: task.sessionId! },
|
||||||
findBySession: () => task,
|
findBySession: () => task,
|
||||||
idleDeferralTimers: new Map(),
|
idleDeferralTimers: new Map(),
|
||||||
validateSessionHasOutput: () => Promise.resolve(true),
|
validateSessionHasOutput: () => Promise.resolve(true),
|
||||||
@@ -254,7 +254,7 @@ describe("handleSessionIdleBackgroundEvent", () => {
|
|||||||
|
|
||||||
//#when
|
//#when
|
||||||
handleSessionIdleBackgroundEvent({
|
handleSessionIdleBackgroundEvent({
|
||||||
properties: { sessionID: task.sessionID! },
|
properties: { sessionID: task.sessionId! },
|
||||||
findBySession: () => task,
|
findBySession: () => task,
|
||||||
idleDeferralTimers: new Map(),
|
idleDeferralTimers: new Map(),
|
||||||
validateSessionHasOutput: () => Promise.resolve(false),
|
validateSessionHasOutput: () => Promise.resolve(false),
|
||||||
@@ -275,7 +275,7 @@ describe("handleSessionIdleBackgroundEvent", () => {
|
|||||||
|
|
||||||
//#when
|
//#when
|
||||||
handleSessionIdleBackgroundEvent({
|
handleSessionIdleBackgroundEvent({
|
||||||
properties: { sessionID: task.sessionID! },
|
properties: { sessionID: task.sessionId! },
|
||||||
findBySession: () => task,
|
findBySession: () => task,
|
||||||
idleDeferralTimers: new Map(),
|
idleDeferralTimers: new Map(),
|
||||||
validateSessionHasOutput: () => Promise.resolve(true),
|
validateSessionHasOutput: () => Promise.resolve(true),
|
||||||
@@ -296,7 +296,7 @@ describe("handleSessionIdleBackgroundEvent", () => {
|
|||||||
|
|
||||||
//#when
|
//#when
|
||||||
handleSessionIdleBackgroundEvent({
|
handleSessionIdleBackgroundEvent({
|
||||||
properties: { sessionID: task.sessionID! },
|
properties: { sessionID: task.sessionId! },
|
||||||
findBySession: () => task,
|
findBySession: () => task,
|
||||||
idleDeferralTimers: new Map(),
|
idleDeferralTimers: new Map(),
|
||||||
validateSessionHasOutput: async () => {
|
validateSessionHasOutput: async () => {
|
||||||
@@ -320,7 +320,7 @@ describe("handleSessionIdleBackgroundEvent", () => {
|
|||||||
|
|
||||||
//#when
|
//#when
|
||||||
handleSessionIdleBackgroundEvent({
|
handleSessionIdleBackgroundEvent({
|
||||||
properties: { sessionID: task.sessionID! },
|
properties: { sessionID: task.sessionId! },
|
||||||
findBySession: () => task,
|
findBySession: () => task,
|
||||||
idleDeferralTimers: new Map(),
|
idleDeferralTimers: new Map(),
|
||||||
validateSessionHasOutput: () => Promise.resolve(true),
|
validateSessionHasOutput: () => Promise.resolve(true),
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ describe("background-agent spawner agent-not-found fallback", () => {
|
|||||||
description: "Implement feature",
|
description: "Implement feature",
|
||||||
prompt: "Please implement the break-even analysis",
|
prompt: "Please implement the break-even analysis",
|
||||||
agent: "Sisyphus-Junior",
|
agent: "Sisyphus-Junior",
|
||||||
parentSessionID: "ses_parent",
|
parentSessionId: "ses_parent",
|
||||||
parentMessageID: "msg_parent",
|
parentMessageId: "msg_parent",
|
||||||
})
|
})
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
@@ -47,8 +47,8 @@ describe("background-agent spawner agent-not-found fallback", () => {
|
|||||||
description: task.description,
|
description: task.description,
|
||||||
prompt: task.prompt,
|
prompt: task.prompt,
|
||||||
agent: task.agent,
|
agent: task.agent,
|
||||||
parentSessionID: task.parentSessionID,
|
parentSessionId: task.parentSessionId,
|
||||||
parentMessageID: task.parentMessageID,
|
parentMessageId: task.parentMessageId,
|
||||||
parentModel: task.parentModel,
|
parentModel: task.parentModel,
|
||||||
parentAgent: task.parentAgent,
|
parentAgent: task.parentAgent,
|
||||||
model: task.model,
|
model: task.model,
|
||||||
@@ -109,8 +109,8 @@ describe("background-agent spawner agent-not-found fallback", () => {
|
|||||||
description: "Implement feature",
|
description: "Implement feature",
|
||||||
prompt: "Do work",
|
prompt: "Do work",
|
||||||
agent: "Sisyphus-Junior",
|
agent: "Sisyphus-Junior",
|
||||||
parentSessionID: "ses_parent",
|
parentSessionId: "ses_parent",
|
||||||
parentMessageID: "msg_parent",
|
parentMessageId: "msg_parent",
|
||||||
})
|
})
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
@@ -119,8 +119,8 @@ describe("background-agent spawner agent-not-found fallback", () => {
|
|||||||
description: task.description,
|
description: task.description,
|
||||||
prompt: task.prompt,
|
prompt: task.prompt,
|
||||||
agent: task.agent,
|
agent: task.agent,
|
||||||
parentSessionID: task.parentSessionID,
|
parentSessionId: task.parentSessionId,
|
||||||
parentMessageID: task.parentMessageID,
|
parentMessageId: task.parentMessageId,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,8 +162,8 @@ describe("background-agent spawner agent-not-found fallback", () => {
|
|||||||
description: "Implement feature",
|
description: "Implement feature",
|
||||||
prompt: "Do work",
|
prompt: "Do work",
|
||||||
agent: "Sisyphus-Junior",
|
agent: "Sisyphus-Junior",
|
||||||
parentSessionID: "ses_parent",
|
parentSessionId: "ses_parent",
|
||||||
parentMessageID: "msg_parent",
|
parentMessageId: "msg_parent",
|
||||||
})
|
})
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
@@ -172,8 +172,8 @@ describe("background-agent spawner agent-not-found fallback", () => {
|
|||||||
description: task.description,
|
description: task.description,
|
||||||
prompt: task.prompt,
|
prompt: task.prompt,
|
||||||
agent: task.agent,
|
agent: task.agent,
|
||||||
parentSessionID: task.parentSessionID,
|
parentSessionId: task.parentSessionId,
|
||||||
parentMessageID: task.parentMessageID,
|
parentMessageId: task.parentMessageId,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,8 +221,8 @@ describe("background-agent spawner agent-not-found fallback", () => {
|
|||||||
description: "Test task",
|
description: "Test task",
|
||||||
prompt: "Do work",
|
prompt: "Do work",
|
||||||
agent: "Sisyphus-Junior",
|
agent: "Sisyphus-Junior",
|
||||||
parentSessionID: "ses_parent",
|
parentSessionId: "ses_parent",
|
||||||
parentMessageID: "msg_parent",
|
parentMessageId: "msg_parent",
|
||||||
})
|
})
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
@@ -231,8 +231,8 @@ describe("background-agent spawner agent-not-found fallback", () => {
|
|||||||
description: task.description,
|
description: task.description,
|
||||||
prompt: task.prompt,
|
prompt: task.prompt,
|
||||||
agent: task.agent,
|
agent: task.agent,
|
||||||
parentSessionID: task.parentSessionID,
|
parentSessionId: task.parentSessionId,
|
||||||
parentMessageID: task.parentMessageID,
|
parentMessageId: task.parentMessageId,
|
||||||
parentModel: task.parentModel,
|
parentModel: task.parentModel,
|
||||||
parentAgent: task.parentAgent,
|
parentAgent: task.parentAgent,
|
||||||
model: task.model,
|
model: task.model,
|
||||||
@@ -284,8 +284,8 @@ describe("background-agent spawner agent-not-found fallback", () => {
|
|||||||
description: "Test task",
|
description: "Test task",
|
||||||
prompt: "Do work",
|
prompt: "Do work",
|
||||||
agent: "Custom-Agent",
|
agent: "Custom-Agent",
|
||||||
parentSessionID: "ses_parent",
|
parentSessionId: "ses_parent",
|
||||||
parentMessageID: "msg_parent",
|
parentMessageId: "msg_parent",
|
||||||
})
|
})
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
@@ -294,8 +294,8 @@ describe("background-agent spawner agent-not-found fallback", () => {
|
|||||||
description: task.description,
|
description: task.description,
|
||||||
prompt: task.prompt,
|
prompt: task.prompt,
|
||||||
agent: task.agent,
|
agent: task.agent,
|
||||||
parentSessionID: task.parentSessionID,
|
parentSessionId: task.parentSessionId,
|
||||||
parentMessageID: task.parentMessageID,
|
parentMessageId: task.parentMessageId,
|
||||||
parentModel: task.parentModel,
|
parentModel: task.parentModel,
|
||||||
parentAgent: task.parentAgent,
|
parentAgent: task.parentAgent,
|
||||||
model: task.model,
|
model: task.model,
|
||||||
@@ -353,8 +353,8 @@ describe("background-agent spawner fallback model promotion", () => {
|
|||||||
description: "Test task",
|
description: "Test task",
|
||||||
prompt: "Do the thing",
|
prompt: "Do the thing",
|
||||||
agent: "oracle",
|
agent: "oracle",
|
||||||
parentSessionID: "parent-1",
|
parentSessionId: "parent-1",
|
||||||
parentMessageID: "message-1",
|
parentMessageId: "message-1",
|
||||||
model: {
|
model: {
|
||||||
providerID: "openai",
|
providerID: "openai",
|
||||||
modelID: "gpt-5.4",
|
modelID: "gpt-5.4",
|
||||||
@@ -371,8 +371,8 @@ describe("background-agent spawner fallback model promotion", () => {
|
|||||||
description: "Test task",
|
description: "Test task",
|
||||||
prompt: "Do the thing",
|
prompt: "Do the thing",
|
||||||
agent: "oracle",
|
agent: "oracle",
|
||||||
parentSessionID: "parent-1",
|
parentSessionId: "parent-1",
|
||||||
parentMessageID: "message-1",
|
parentMessageId: "message-1",
|
||||||
model: task.model,
|
model: task.model,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,8 +427,8 @@ describe("background-agent spawner fallback model promotion", () => {
|
|||||||
description: "Test task",
|
description: "Test task",
|
||||||
prompt: "Do work",
|
prompt: "Do work",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
parentSessionID: "ses_parent",
|
parentSessionId: "ses_parent",
|
||||||
parentMessageID: "msg_parent",
|
parentMessageId: "msg_parent",
|
||||||
model: { providerID: "openai", modelID: "gpt-5.4", variant: "medium" },
|
model: { providerID: "openai", modelID: "gpt-5.4", variant: "medium" },
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -438,8 +438,8 @@ describe("background-agent spawner fallback model promotion", () => {
|
|||||||
description: task.description,
|
description: task.description,
|
||||||
prompt: task.prompt,
|
prompt: task.prompt,
|
||||||
agent: task.agent,
|
agent: task.agent,
|
||||||
parentSessionID: task.parentSessionID,
|
parentSessionId: task.parentSessionId,
|
||||||
parentMessageID: task.parentMessageID,
|
parentMessageId: task.parentMessageId,
|
||||||
parentModel: task.parentModel,
|
parentModel: task.parentModel,
|
||||||
parentAgent: task.parentAgent,
|
parentAgent: task.parentAgent,
|
||||||
model: task.model,
|
model: task.model,
|
||||||
@@ -486,8 +486,8 @@ describe("background-agent spawner fallback model promotion", () => {
|
|||||||
description: "Test task",
|
description: "Test task",
|
||||||
prompt: "Do work",
|
prompt: "Do work",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
parentSessionID: "ses_parent",
|
parentSessionId: "ses_parent",
|
||||||
parentMessageID: "msg_parent",
|
parentMessageId: "msg_parent",
|
||||||
})
|
})
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
@@ -496,8 +496,8 @@ describe("background-agent spawner fallback model promotion", () => {
|
|||||||
description: task.description,
|
description: task.description,
|
||||||
prompt: task.prompt,
|
prompt: task.prompt,
|
||||||
agent: task.agent,
|
agent: task.agent,
|
||||||
parentSessionID: task.parentSessionID,
|
parentSessionId: task.parentSessionId,
|
||||||
parentMessageID: task.parentMessageID,
|
parentMessageId: task.parentMessageId,
|
||||||
parentModel: task.parentModel,
|
parentModel: task.parentModel,
|
||||||
parentAgent: task.parentAgent,
|
parentAgent: task.parentAgent,
|
||||||
model: task.model,
|
model: task.model,
|
||||||
@@ -542,8 +542,8 @@ describe("background-agent spawner fallback model promotion", () => {
|
|||||||
description: "Test task",
|
description: "Test task",
|
||||||
prompt: "Do work",
|
prompt: "Do work",
|
||||||
agent: "\u200Bsisyphus-junior",
|
agent: "\u200Bsisyphus-junior",
|
||||||
parentSessionID: "ses_parent",
|
parentSessionId: "ses_parent",
|
||||||
parentMessageID: "msg_parent",
|
parentMessageId: "msg_parent",
|
||||||
})
|
})
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
@@ -552,8 +552,8 @@ describe("background-agent spawner fallback model promotion", () => {
|
|||||||
description: task.description,
|
description: task.description,
|
||||||
prompt: task.prompt,
|
prompt: task.prompt,
|
||||||
agent: task.agent,
|
agent: task.agent,
|
||||||
parentSessionID: task.parentSessionID,
|
parentSessionId: task.parentSessionId,
|
||||||
parentMessageID: task.parentMessageID,
|
parentMessageId: task.parentMessageId,
|
||||||
parentModel: task.parentModel,
|
parentModel: task.parentModel,
|
||||||
parentAgent: task.parentAgent,
|
parentAgent: task.parentAgent,
|
||||||
model: task.model,
|
model: task.model,
|
||||||
@@ -596,8 +596,8 @@ describe("background-agent spawner fallback model promotion", () => {
|
|||||||
description: "Legacy ZWSP",
|
description: "Legacy ZWSP",
|
||||||
prompt: "Do work",
|
prompt: "Do work",
|
||||||
agent: "\u200B\u200BHephaestus - Deep Agent",
|
agent: "\u200B\u200BHephaestus - Deep Agent",
|
||||||
parentSessionID: "ses_parent",
|
parentSessionId: "ses_parent",
|
||||||
parentMessageID: "msg_parent",
|
parentMessageId: "msg_parent",
|
||||||
})
|
})
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
@@ -606,8 +606,8 @@ describe("background-agent spawner fallback model promotion", () => {
|
|||||||
description: task.description,
|
description: task.description,
|
||||||
prompt: task.prompt,
|
prompt: task.prompt,
|
||||||
agent: task.agent,
|
agent: task.agent,
|
||||||
parentSessionID: task.parentSessionID,
|
parentSessionId: task.parentSessionId,
|
||||||
parentMessageID: task.parentMessageID,
|
parentMessageId: task.parentMessageId,
|
||||||
parentModel: task.parentModel,
|
parentModel: task.parentModel,
|
||||||
parentAgent: task.parentAgent,
|
parentAgent: task.parentAgent,
|
||||||
model: task.model,
|
model: task.model,
|
||||||
@@ -665,8 +665,8 @@ describe("background-agent spawner tmux callback ordering", () => {
|
|||||||
description: "Blocking tmux test",
|
description: "Blocking tmux test",
|
||||||
prompt: "Do work",
|
prompt: "Do work",
|
||||||
agent: "general",
|
agent: "general",
|
||||||
parentSessionID: "ses_parent",
|
parentSessionId: "ses_parent",
|
||||||
parentMessageID: "msg_parent",
|
parentMessageId: "msg_parent",
|
||||||
})
|
})
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
@@ -675,8 +675,8 @@ describe("background-agent spawner tmux callback ordering", () => {
|
|||||||
description: task.description,
|
description: task.description,
|
||||||
prompt: task.prompt,
|
prompt: task.prompt,
|
||||||
agent: task.agent,
|
agent: task.agent,
|
||||||
parentSessionID: task.parentSessionID,
|
parentSessionId: task.parentSessionId,
|
||||||
parentMessageID: task.parentMessageID,
|
parentMessageId: task.parentMessageId,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ export function createTask(input: LaunchInput): BackgroundTask {
|
|||||||
description: input.description,
|
description: input.description,
|
||||||
prompt: input.prompt,
|
prompt: input.prompt,
|
||||||
agent: input.agent,
|
agent: input.agent,
|
||||||
parentSessionID: input.parentSessionID,
|
parentSessionId: input.parentSessionId,
|
||||||
parentMessageID: input.parentMessageID,
|
parentMessageId: input.parentMessageId,
|
||||||
parentModel: input.parentModel,
|
parentModel: input.parentModel,
|
||||||
parentAgent: input.parentAgent,
|
parentAgent: input.parentAgent,
|
||||||
model: input.model,
|
model: input.model,
|
||||||
@@ -84,7 +84,7 @@ export async function startTask(
|
|||||||
: input.agent
|
: input.agent
|
||||||
|
|
||||||
const parentSession = await client.session.get({
|
const parentSession = await client.session.get({
|
||||||
path: { id: input.parentSessionID },
|
path: { id: input.parentSessionId },
|
||||||
query: { directory },
|
query: { directory },
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
log(`[background-agent] Failed to get parent session: ${err}`)
|
log(`[background-agent] Failed to get parent session: ${err}`)
|
||||||
@@ -95,7 +95,7 @@ export async function startTask(
|
|||||||
|
|
||||||
const createResult = await client.session.create({
|
const createResult = await client.session.create({
|
||||||
body: {
|
body: {
|
||||||
parentID: input.parentSessionID,
|
parentID: input.parentSessionId,
|
||||||
...(input.sessionPermission ? { permission: input.sessionPermission } : {}),
|
...(input.sessionPermission ? { permission: input.sessionPermission } : {}),
|
||||||
} as Record<string, unknown>,
|
} as Record<string, unknown>,
|
||||||
query: {
|
query: {
|
||||||
@@ -116,7 +116,7 @@ export async function startTask(
|
|||||||
|
|
||||||
task.status = "running"
|
task.status = "running"
|
||||||
task.startedAt = new Date()
|
task.startedAt = new Date()
|
||||||
task.sessionID = sessionID
|
task.sessionId = sessionID
|
||||||
task.progress = {
|
task.progress = {
|
||||||
toolCalls: 0,
|
toolCalls: 0,
|
||||||
lastUpdate: new Date(),
|
lastUpdate: new Date(),
|
||||||
@@ -199,14 +199,14 @@ export async function startTask(
|
|||||||
tmuxEnabled,
|
tmuxEnabled,
|
||||||
isInsideTmux: isInsideTmux(),
|
isInsideTmux: isInsideTmux(),
|
||||||
sessionID,
|
sessionID,
|
||||||
parentID: input.parentSessionID,
|
parentID: input.parentSessionId,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (onSubagentSessionCreated && tmuxEnabled && isInsideTmux()) {
|
if (onSubagentSessionCreated && tmuxEnabled && isInsideTmux()) {
|
||||||
log("[background-agent] Invoking tmux callback (fire-and-forget)", { sessionID })
|
log("[background-agent] Invoking tmux callback (fire-and-forget)", { sessionID })
|
||||||
void onSubagentSessionCreated({
|
void onSubagentSessionCreated({
|
||||||
sessionID,
|
sessionID,
|
||||||
parentID: input.parentSessionID,
|
parentID: input.parentSessionId,
|
||||||
title: input.description,
|
title: input.description,
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
log("[background-agent] Failed to spawn tmux pane:", err)
|
log("[background-agent] Failed to spawn tmux pane:", err)
|
||||||
@@ -223,14 +223,14 @@ export async function resumeTask(
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { client, concurrencyManager, onTaskError } = ctx
|
const { client, concurrencyManager, onTaskError } = ctx
|
||||||
|
|
||||||
if (!task.sessionID) {
|
if (!task.sessionId) {
|
||||||
throw new Error(`Task has no sessionID: ${task.id}`)
|
throw new Error(`Task has no sessionID: ${task.id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task.status === "running") {
|
if (task.status === "running") {
|
||||||
log("[background-agent] Resume skipped - task already running:", {
|
log("[background-agent] Resume skipped - task already running:", {
|
||||||
taskId: task.id,
|
taskId: task.id,
|
||||||
sessionID: task.sessionID,
|
sessionID: task.sessionId,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -243,8 +243,8 @@ export async function resumeTask(
|
|||||||
task.status = "running"
|
task.status = "running"
|
||||||
task.completedAt = undefined
|
task.completedAt = undefined
|
||||||
task.error = undefined
|
task.error = undefined
|
||||||
task.parentSessionID = input.parentSessionID
|
task.parentSessionId = input.parentSessionId
|
||||||
task.parentMessageID = input.parentMessageID
|
task.parentMessageId = input.parentMessageId
|
||||||
task.parentModel = input.parentModel
|
task.parentModel = input.parentModel
|
||||||
task.parentAgent = input.parentAgent
|
task.parentAgent = input.parentAgent
|
||||||
task.startedAt = new Date()
|
task.startedAt = new Date()
|
||||||
@@ -254,7 +254,7 @@ export async function resumeTask(
|
|||||||
lastUpdate: new Date(),
|
lastUpdate: new Date(),
|
||||||
}
|
}
|
||||||
|
|
||||||
subagentSessions.add(task.sessionID)
|
subagentSessions.add(task.sessionId)
|
||||||
|
|
||||||
const toastManager = getTaskToastManager()
|
const toastManager = getTaskToastManager()
|
||||||
if (toastManager) {
|
if (toastManager) {
|
||||||
@@ -266,10 +266,10 @@ export async function resumeTask(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
log("[background-agent] Resuming task:", { taskId: task.id, sessionID: task.sessionID })
|
log("[background-agent] Resuming task:", { taskId: task.id, sessionID: task.sessionId })
|
||||||
|
|
||||||
log("[background-agent] Resuming task - calling prompt (fire-and-forget) with:", {
|
log("[background-agent] Resuming task - calling prompt (fire-and-forget) with:", {
|
||||||
sessionID: task.sessionID,
|
sessionID: task.sessionId,
|
||||||
agent: task.agent,
|
agent: task.agent,
|
||||||
model: task.model,
|
model: task.model,
|
||||||
promptLength: input.prompt.length,
|
promptLength: input.prompt.length,
|
||||||
@@ -283,7 +283,7 @@ export async function resumeTask(
|
|||||||
: undefined
|
: undefined
|
||||||
const resumeVariant = task.model?.variant
|
const resumeVariant = task.model?.variant
|
||||||
|
|
||||||
applySessionPromptParams(task.sessionID, task.model)
|
applySessionPromptParams(task.sessionId, task.model)
|
||||||
|
|
||||||
const resumeBody = {
|
const resumeBody = {
|
||||||
agent: task.agent,
|
agent: task.agent,
|
||||||
@@ -299,7 +299,7 @@ export async function resumeTask(
|
|||||||
}
|
}
|
||||||
|
|
||||||
client.session.promptAsync({
|
client.session.promptAsync({
|
||||||
path: { id: task.sessionID },
|
path: { id: task.sessionId },
|
||||||
body: resumeBody,
|
body: resumeBody,
|
||||||
}).catch(async (error) => {
|
}).catch(async (error) => {
|
||||||
if (isAgentNotFoundError(error) && task.agent !== FALLBACK_AGENT) {
|
if (isAgentNotFoundError(error) && task.agent !== FALLBACK_AGENT) {
|
||||||
@@ -310,7 +310,7 @@ export async function resumeTask(
|
|||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
await promptWithModelSuggestionRetry(client, {
|
await promptWithModelSuggestionRetry(client, {
|
||||||
path: { id: task.sessionID! },
|
path: { id: task.sessionId! },
|
||||||
body: buildFallbackBody(resumeBody, FALLBACK_AGENT),
|
body: buildFallbackBody(resumeBody, FALLBACK_AGENT),
|
||||||
})
|
})
|
||||||
task.agent = FALLBACK_AGENT
|
task.agent = FALLBACK_AGENT
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export class TaskStateManager {
|
|||||||
}
|
}
|
||||||
findBySession(sessionID: string): BackgroundTask | undefined {
|
findBySession(sessionID: string): BackgroundTask | undefined {
|
||||||
for (const task of this.tasks.values()) {
|
for (const task of this.tasks.values()) {
|
||||||
if (task.sessionID === sessionID) {
|
if (task.sessionId === sessionID) {
|
||||||
return task
|
return task
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ export class TaskStateManager {
|
|||||||
getTasksByParentSession(sessionID: string): BackgroundTask[] {
|
getTasksByParentSession(sessionID: string): BackgroundTask[] {
|
||||||
const result: BackgroundTask[] = []
|
const result: BackgroundTask[] = []
|
||||||
for (const task of this.tasks.values()) {
|
for (const task of this.tasks.values()) {
|
||||||
if (task.parentSessionID === sessionID) {
|
if (task.parentSessionId === sessionID) {
|
||||||
result.push(task)
|
result.push(task)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,8 +36,8 @@ export class TaskStateManager {
|
|||||||
|
|
||||||
for (const child of directChildren) {
|
for (const child of directChildren) {
|
||||||
result.push(child)
|
result.push(child)
|
||||||
if (child.sessionID) {
|
if (child.sessionId) {
|
||||||
const descendants = this.getAllDescendantTasks(child.sessionID)
|
const descendants = this.getAllDescendantTasks(child.sessionId)
|
||||||
result.push(...descendants)
|
result.push(...descendants)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,8 +79,8 @@ export class TaskStateManager {
|
|||||||
|
|
||||||
removeTask(taskId: string): void {
|
removeTask(taskId: string): void {
|
||||||
const task = this.tasks.get(taskId)
|
const task = this.tasks.get(taskId)
|
||||||
if (task?.sessionID) {
|
if (task?.sessionId) {
|
||||||
subagentSessions.delete(task.sessionID)
|
subagentSessions.delete(task.sessionId)
|
||||||
}
|
}
|
||||||
this.tasks.delete(taskId)
|
this.tasks.delete(taskId)
|
||||||
}
|
}
|
||||||
@@ -92,20 +92,20 @@ export class TaskStateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cleanupPendingByParent(task: BackgroundTask): void {
|
cleanupPendingByParent(task: BackgroundTask): void {
|
||||||
if (!task.parentSessionID) return
|
if (!task.parentSessionId) return
|
||||||
const pending = this.pendingByParent.get(task.parentSessionID)
|
const pending = this.pendingByParent.get(task.parentSessionId)
|
||||||
if (pending) {
|
if (pending) {
|
||||||
pending.delete(task.id)
|
pending.delete(task.id)
|
||||||
if (pending.size === 0) {
|
if (pending.size === 0) {
|
||||||
this.pendingByParent.delete(task.parentSessionID)
|
this.pendingByParent.delete(task.parentSessionId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
markForNotification(task: BackgroundTask): void {
|
markForNotification(task: BackgroundTask): void {
|
||||||
const queue = this.notifications.get(task.parentSessionID) ?? []
|
const queue = this.notifications.get(task.parentSessionId) ?? []
|
||||||
queue.push(task)
|
queue.push(task)
|
||||||
this.notifications.set(task.parentSessionID, queue)
|
this.notifications.set(task.parentSessionId, queue)
|
||||||
}
|
}
|
||||||
|
|
||||||
getPendingNotifications(sessionID: string): BackgroundTask[] {
|
getPendingNotifications(sessionID: string): BackgroundTask[] {
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ afterEach(() => {
|
|||||||
fakeTimers = undefined
|
fakeTimers = undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
function createTask(overrides: Partial<BackgroundTask> & { id: string; parentSessionID: string }): BackgroundTask {
|
function createTask(overrides: Partial<BackgroundTask> & { id: string; parentSessionId: string }): BackgroundTask {
|
||||||
const id = overrides.id
|
const id = overrides.id
|
||||||
const parentSessionID = overrides.parentSessionID
|
const parentSessionID = overrides.parentSessionId
|
||||||
const { id: _ignoredID, parentSessionID: _ignoredParentSessionID, ...rest } = overrides
|
const { id: _ignoredID, parentSessionId: _ignoredParentSessionID, ...rest } = overrides
|
||||||
|
|
||||||
return {
|
return {
|
||||||
parentMessageID: overrides.parentMessageID ?? "parent-message-id",
|
parentMessageId: overrides.parentMessageId ?? "parent-message-id",
|
||||||
description: overrides.description ?? overrides.id,
|
description: overrides.description ?? overrides.id,
|
||||||
prompt: overrides.prompt ?? `Prompt for ${overrides.id}`,
|
prompt: overrides.prompt ?? `Prompt for ${overrides.id}`,
|
||||||
agent: overrides.agent ?? "test-agent",
|
agent: overrides.agent ?? "test-agent",
|
||||||
@@ -43,7 +43,7 @@ function createTask(overrides: Partial<BackgroundTask> & { id: string; parentSes
|
|||||||
startedAt: overrides.startedAt ?? new Date("2026-03-11T00:00:00.000Z"),
|
startedAt: overrides.startedAt ?? new Date("2026-03-11T00:00:00.000Z"),
|
||||||
...rest,
|
...rest,
|
||||||
id,
|
id,
|
||||||
parentSessionID,
|
parentSessionId: parentSessionID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,9 +74,7 @@ function createManager(enableParentSessionNotifications: boolean): {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const manager = new BackgroundManager(
|
const manager = new BackgroundManager(
|
||||||
ctx,
|
{ pluginContext: ctx, config: undefined, enableParentSessionNotifications }
|
||||||
undefined,
|
|
||||||
{ enableParentSessionNotifications }
|
|
||||||
)
|
)
|
||||||
Reflect.set(manager, "client", client)
|
Reflect.set(manager, "client", client)
|
||||||
|
|
||||||
@@ -162,13 +160,13 @@ describe("BackgroundManager.notifyParentSession cleanup scheduling", () => {
|
|||||||
const { manager } = createManager(false)
|
const { manager } = createManager(false)
|
||||||
managerUnderTest = manager
|
managerUnderTest = manager
|
||||||
fakeTimers = installFakeTimers()
|
fakeTimers = installFakeTimers()
|
||||||
const taskA = createTask({ id: "task-a", parentSessionID: "parent-1", description: "task A", status: "completed", completedAt: new Date() })
|
const taskA = createTask({ id: "task-a", parentSessionId: "parent-1", description: "task A", status: "completed", completedAt: new Date() })
|
||||||
const taskB = createTask({ id: "task-b", parentSessionID: "parent-1", description: "task B", status: "running" })
|
const taskB = createTask({ id: "task-b", parentSessionId: "parent-1", description: "task B", status: "running" })
|
||||||
const taskC = createTask({ id: "task-c", parentSessionID: "parent-1", description: "task C", status: "pending" })
|
const taskC = createTask({ id: "task-c", parentSessionId: "parent-1", description: "task C", status: "pending" })
|
||||||
getTasks(manager).set(taskA.id, taskA)
|
getTasks(manager).set(taskA.id, taskA)
|
||||||
getTasks(manager).set(taskB.id, taskB)
|
getTasks(manager).set(taskB.id, taskB)
|
||||||
getTasks(manager).set(taskC.id, taskC)
|
getTasks(manager).set(taskC.id, taskC)
|
||||||
getPendingByParent(manager).set(taskA.parentSessionID, new Set([taskA.id, taskB.id, taskC.id]))
|
getPendingByParent(manager).set(taskA.parentSessionId, new Set([taskA.id, taskB.id, taskC.id]))
|
||||||
|
|
||||||
// when
|
// when
|
||||||
await notifyParentSessionForTest(manager, taskA)
|
await notifyParentSessionForTest(manager, taskA)
|
||||||
@@ -204,11 +202,11 @@ describe("BackgroundManager.notifyParentSession cleanup scheduling", () => {
|
|||||||
const { manager, promptAsyncCalls } = createManager(true)
|
const { manager, promptAsyncCalls } = createManager(true)
|
||||||
managerUnderTest = manager
|
managerUnderTest = manager
|
||||||
fakeTimers = installFakeTimers()
|
fakeTimers = installFakeTimers()
|
||||||
const taskA = createTask({ id: "task-a", parentSessionID: "parent-1", description: "task A", status: "completed", completedAt: new Date("2026-03-11T00:01:00.000Z") })
|
const taskA = createTask({ id: "task-a", parentSessionId: "parent-1", description: "task A", status: "completed", completedAt: new Date("2026-03-11T00:01:00.000Z") })
|
||||||
const taskB = createTask({ id: "task-b", parentSessionID: "parent-1", description: "task B", status: "running" })
|
const taskB = createTask({ id: "task-b", parentSessionId: "parent-1", description: "task B", status: "running" })
|
||||||
getTasks(manager).set(taskA.id, taskA)
|
getTasks(manager).set(taskA.id, taskA)
|
||||||
getTasks(manager).set(taskB.id, taskB)
|
getTasks(manager).set(taskB.id, taskB)
|
||||||
getPendingByParent(manager).set(taskA.parentSessionID, new Set([taskA.id, taskB.id]))
|
getPendingByParent(manager).set(taskA.parentSessionId, new Set([taskA.id, taskB.id]))
|
||||||
|
|
||||||
await notifyParentSessionForTest(manager, taskA)
|
await notifyParentSessionForTest(manager, taskA)
|
||||||
taskB.status = "completed"
|
taskB.status = "completed"
|
||||||
@@ -242,9 +240,9 @@ describe("BackgroundManager.notifyParentSession cleanup scheduling", () => {
|
|||||||
const { manager } = createManager(false)
|
const { manager } = createManager(false)
|
||||||
managerUnderTest = manager
|
managerUnderTest = manager
|
||||||
fakeTimers = installFakeTimers()
|
fakeTimers = installFakeTimers()
|
||||||
const task = createTask({ id: "task-a", parentSessionID: "parent-1", description: "task A", status: "completed", completedAt: new Date("2026-03-11T00:01:00.000Z") })
|
const task = createTask({ id: "task-a", parentSessionId: "parent-1", description: "task A", status: "completed", completedAt: new Date("2026-03-11T00:01:00.000Z") })
|
||||||
getTasks(manager).set(task.id, task)
|
getTasks(manager).set(task.id, task)
|
||||||
getPendingByParent(manager).set(task.parentSessionID, new Set([task.id]))
|
getPendingByParent(manager).set(task.parentSessionId, new Set([task.id]))
|
||||||
|
|
||||||
await notifyParentSessionForTest(manager, task)
|
await notifyParentSessionForTest(manager, task)
|
||||||
const cleanupTimer = getRequiredTimer(manager, task.id)
|
const cleanupTimer = getRequiredTimer(manager, task.id)
|
||||||
|
|||||||
@@ -29,20 +29,20 @@ function createManager(): BackgroundManager {
|
|||||||
$: {} as PluginInput["$"],
|
$: {} as PluginInput["$"],
|
||||||
}
|
}
|
||||||
|
|
||||||
const manager = new BackgroundManager(ctx)
|
const manager = new BackgroundManager({ pluginContext: ctx })
|
||||||
Reflect.set(manager, "client", client)
|
Reflect.set(manager, "client", client)
|
||||||
|
|
||||||
return manager
|
return manager
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTask(overrides: Partial<BackgroundTask> & { id: string; parentSessionID: string }): BackgroundTask {
|
function createTask(overrides: Partial<BackgroundTask> & { id: string; parentSessionId: string }): BackgroundTask {
|
||||||
const { id, parentSessionID, ...rest } = overrides
|
const { id, parentSessionID, ...rest } = overrides
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...rest,
|
...rest,
|
||||||
id,
|
id,
|
||||||
parentSessionID,
|
parentSessionID,
|
||||||
parentMessageID: rest.parentMessageID ?? "parent-message-id",
|
parentMessageId: rest.parentMessageId ?? "parent-message-id",
|
||||||
description: rest.description ?? id,
|
description: rest.description ?? id,
|
||||||
prompt: rest.prompt ?? `Prompt for ${id}`,
|
prompt: rest.prompt ?? `Prompt for ${id}`,
|
||||||
agent: rest.agent ?? "test-agent",
|
agent: rest.agent ?? "test-agent",
|
||||||
@@ -118,12 +118,12 @@ describe("task history cleanup", () => {
|
|||||||
managerUnderTest = manager
|
managerUnderTest = manager
|
||||||
const staleTask = createTask({
|
const staleTask = createTask({
|
||||||
id: "task-stale",
|
id: "task-stale",
|
||||||
parentSessionID: "parent-1",
|
parentSessionId: "parent-1",
|
||||||
startedAt: new Date(Date.now() - 31 * 60 * 1000),
|
startedAt: new Date(Date.now() - 31 * 60 * 1000),
|
||||||
})
|
})
|
||||||
const liveTask = createTask({
|
const liveTask = createTask({
|
||||||
id: "task-live",
|
id: "task-live",
|
||||||
parentSessionID: "parent-2",
|
parentSessionId: "parent-2",
|
||||||
startedAt: new Date(),
|
startedAt: new Date(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ describe("checkAndInterruptStaleTasks", () => {
|
|||||||
function createRunningTask(overrides: Partial<BackgroundTask> = {}): BackgroundTask {
|
function createRunningTask(overrides: Partial<BackgroundTask> = {}): BackgroundTask {
|
||||||
return {
|
return {
|
||||||
id: "task-1",
|
id: "task-1",
|
||||||
sessionID: "ses-1",
|
sessionId: "ses-1",
|
||||||
parentSessionID: "parent-ses-1",
|
parentSessionId: "parent-ses-1",
|
||||||
parentMessageID: "msg-1",
|
parentMessageId: "msg-1",
|
||||||
description: "test",
|
description: "test",
|
||||||
prompt: "test",
|
prompt: "test",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -745,8 +745,8 @@ describe("pruneStaleTasksAndNotifications", () => {
|
|||||||
function createTerminalTask(overrides: Partial<BackgroundTask> = {}): BackgroundTask {
|
function createTerminalTask(overrides: Partial<BackgroundTask> = {}): BackgroundTask {
|
||||||
return {
|
return {
|
||||||
id: "terminal-task",
|
id: "terminal-task",
|
||||||
parentSessionID: "parent",
|
parentSessionId: "parent",
|
||||||
parentMessageID: "msg",
|
parentMessageId: "msg",
|
||||||
description: "terminal",
|
description: "terminal",
|
||||||
prompt: "terminal",
|
prompt: "terminal",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -762,8 +762,8 @@ describe("pruneStaleTasksAndNotifications", () => {
|
|||||||
const tasks = new Map<string, BackgroundTask>()
|
const tasks = new Map<string, BackgroundTask>()
|
||||||
const oldTask: BackgroundTask = {
|
const oldTask: BackgroundTask = {
|
||||||
id: "old-task",
|
id: "old-task",
|
||||||
parentSessionID: "parent",
|
parentSessionId: "parent",
|
||||||
parentMessageID: "msg",
|
parentMessageId: "msg",
|
||||||
description: "old",
|
description: "old",
|
||||||
prompt: "old",
|
prompt: "old",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -791,8 +791,8 @@ describe("pruneStaleTasksAndNotifications", () => {
|
|||||||
const tasks = new Map<string, BackgroundTask>()
|
const tasks = new Map<string, BackgroundTask>()
|
||||||
const activeTask: BackgroundTask = {
|
const activeTask: BackgroundTask = {
|
||||||
id: "active-task",
|
id: "active-task",
|
||||||
parentSessionID: "parent",
|
parentSessionId: "parent",
|
||||||
parentMessageID: "msg",
|
parentMessageId: "msg",
|
||||||
description: "active",
|
description: "active",
|
||||||
prompt: "active",
|
prompt: "active",
|
||||||
agent: "oracle",
|
agent: "oracle",
|
||||||
@@ -824,8 +824,8 @@ describe("pruneStaleTasksAndNotifications", () => {
|
|||||||
const tasks = new Map<string, BackgroundTask>()
|
const tasks = new Map<string, BackgroundTask>()
|
||||||
const staleTask: BackgroundTask = {
|
const staleTask: BackgroundTask = {
|
||||||
id: "stale-task",
|
id: "stale-task",
|
||||||
parentSessionID: "parent",
|
parentSessionId: "parent",
|
||||||
parentMessageID: "msg",
|
parentMessageId: "msg",
|
||||||
description: "stale",
|
description: "stale",
|
||||||
prompt: "stale",
|
prompt: "stale",
|
||||||
agent: "oracle",
|
agent: "oracle",
|
||||||
@@ -857,8 +857,8 @@ describe("pruneStaleTasksAndNotifications", () => {
|
|||||||
const tasks = new Map<string, BackgroundTask>()
|
const tasks = new Map<string, BackgroundTask>()
|
||||||
const task: BackgroundTask = {
|
const task: BackgroundTask = {
|
||||||
id: "custom-ttl-task",
|
id: "custom-ttl-task",
|
||||||
parentSessionID: "parent",
|
parentSessionId: "parent",
|
||||||
parentMessageID: "msg",
|
parentMessageId: "msg",
|
||||||
description: "custom",
|
description: "custom",
|
||||||
prompt: "custom",
|
prompt: "custom",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -887,8 +887,8 @@ describe("pruneStaleTasksAndNotifications", () => {
|
|||||||
const tasks = new Map<string, BackgroundTask>()
|
const tasks = new Map<string, BackgroundTask>()
|
||||||
const task: BackgroundTask = {
|
const task: BackgroundTask = {
|
||||||
id: "within-ttl-task",
|
id: "within-ttl-task",
|
||||||
parentSessionID: "parent",
|
parentSessionId: "parent",
|
||||||
parentMessageID: "msg",
|
parentMessageId: "msg",
|
||||||
description: "within",
|
description: "within",
|
||||||
prompt: "within",
|
prompt: "within",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@@ -944,7 +944,7 @@ describe("pruneStaleTasksAndNotifications", () => {
|
|||||||
//#given
|
//#given
|
||||||
const task = createTerminalTask()
|
const task = createTerminalTask()
|
||||||
const tasks = new Map<string, BackgroundTask>([[task.id, task]])
|
const tasks = new Map<string, BackgroundTask>([[task.id, task]])
|
||||||
const notifications = new Map<string, BackgroundTask[]>([[task.parentSessionID, [task]]])
|
const notifications = new Map<string, BackgroundTask[]>([[task.parentSessionId, [task]]])
|
||||||
const pruned: string[] = []
|
const pruned: string[] = []
|
||||||
|
|
||||||
//#when
|
//#when
|
||||||
@@ -957,6 +957,6 @@ describe("pruneStaleTasksAndNotifications", () => {
|
|||||||
//#then
|
//#then
|
||||||
expect(pruned).toEqual([])
|
expect(pruned).toEqual([])
|
||||||
expect(tasks.has(task.id)).toBe(true)
|
expect(tasks.has(task.id)).toBe(true)
|
||||||
expect(notifications.has(task.parentSessionID)).toBe(false)
|
expect(notifications.has(task.parentSessionId)).toBe(false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ export async function checkAndInterruptStaleTasks(args: {
|
|||||||
if (task.status !== "running") continue
|
if (task.status !== "running") continue
|
||||||
|
|
||||||
const startedAt = task.startedAt
|
const startedAt = task.startedAt
|
||||||
const sessionID = task.sessionID
|
const sessionID = task.sessionId
|
||||||
if (!startedAt || !sessionID) continue
|
if (!startedAt || !sessionID) continue
|
||||||
|
|
||||||
const sessionStatus = sessionStatuses?.[sessionID]?.type
|
const sessionStatus = sessionStatuses?.[sessionID]?.type
|
||||||
|
|||||||
@@ -29,11 +29,11 @@ export interface TaskProgress {
|
|||||||
export type BackgroundTaskAttemptStatus = BackgroundTaskStatus
|
export type BackgroundTaskAttemptStatus = BackgroundTaskStatus
|
||||||
|
|
||||||
export interface BackgroundTaskAttempt {
|
export interface BackgroundTaskAttempt {
|
||||||
attemptID: string
|
attemptId: string
|
||||||
attemptNumber: number
|
attemptNumber: number
|
||||||
sessionID?: string
|
sessionId?: string
|
||||||
providerID?: string
|
providerId?: string
|
||||||
modelID?: string
|
modelId?: string
|
||||||
variant?: string
|
variant?: string
|
||||||
status: BackgroundTaskAttemptStatus
|
status: BackgroundTaskAttemptStatus
|
||||||
error?: string
|
error?: string
|
||||||
@@ -43,10 +43,10 @@ export interface BackgroundTaskAttempt {
|
|||||||
|
|
||||||
export interface BackgroundTask {
|
export interface BackgroundTask {
|
||||||
id: string
|
id: string
|
||||||
sessionID?: string
|
sessionId?: string
|
||||||
rootSessionID?: string
|
rootSessionId?: string
|
||||||
parentSessionID: string
|
parentSessionId: string
|
||||||
parentMessageID: string
|
parentMessageId: string
|
||||||
description: string
|
description: string
|
||||||
prompt: string
|
prompt: string
|
||||||
agent: string
|
agent: string
|
||||||
@@ -101,8 +101,8 @@ export interface LaunchInput {
|
|||||||
description: string
|
description: string
|
||||||
prompt: string
|
prompt: string
|
||||||
agent: string
|
agent: string
|
||||||
parentSessionID: string
|
parentSessionId: string
|
||||||
parentMessageID: string
|
parentMessageId: string
|
||||||
parentModel?: { providerID: string; modelID: string }
|
parentModel?: { providerID: string; modelID: string }
|
||||||
parentAgent?: string
|
parentAgent?: string
|
||||||
parentTools?: Record<string, boolean>
|
parentTools?: Record<string, boolean>
|
||||||
@@ -119,8 +119,8 @@ export interface LaunchInput {
|
|||||||
export interface ResumeInput {
|
export interface ResumeInput {
|
||||||
sessionId: string
|
sessionId: string
|
||||||
prompt: string
|
prompt: string
|
||||||
parentSessionID: string
|
parentSessionId: string
|
||||||
parentMessageID: string
|
parentMessageId: string
|
||||||
parentModel?: { providerID: string; modelID: string }
|
parentModel?: { providerID: string; modelID: string }
|
||||||
parentAgent?: string
|
parentAgent?: string
|
||||||
parentTools?: Record<string, boolean>
|
parentTools?: Record<string, boolean>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ function createManager(responses: TaskSnapshot[]) {
|
|||||||
describe("waitForTaskSessionID", () => {
|
describe("waitForTaskSessionID", () => {
|
||||||
test("#given task already has a session id #when waiting #then it returns immediately", async () => {
|
test("#given task already has a session id #when waiting #then it returns immediately", async () => {
|
||||||
// given
|
// given
|
||||||
const manager = createManager([{ sessionID: "ses_ready_123", status: "running" }])
|
const manager = createManager([{ sessionId: "ses_ready_123", status: "running" }])
|
||||||
|
|
||||||
// when
|
// when
|
||||||
const sessionID = await waitForTaskSessionID(manager, "bg_ready")
|
const sessionID = await waitForTaskSessionID(manager, "bg_ready")
|
||||||
@@ -37,7 +37,7 @@ describe("waitForTaskSessionID", () => {
|
|||||||
const manager = createManager([
|
const manager = createManager([
|
||||||
{ status: "running" },
|
{ status: "running" },
|
||||||
{ status: "running" },
|
{ status: "running" },
|
||||||
{ sessionID: "ses_late_123", status: "running" },
|
{ sessionId: "ses_late_123", status: "running" },
|
||||||
])
|
])
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ type SessionWaitTerminalStatus = Extract<BackgroundTaskStatus, "error" | "cancel
|
|||||||
type AbortSignalLike = { aborted: boolean }
|
type AbortSignalLike = { aborted: boolean }
|
||||||
|
|
||||||
interface TaskReader {
|
interface TaskReader {
|
||||||
getTask(taskID: string): { sessionID?: string; status?: BackgroundTaskStatus } | undefined
|
getTask(taskID: string): { sessionId?: string; status?: BackgroundTaskStatus } | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WaitForTaskSessionIDOptions {
|
export interface WaitForTaskSessionIDOptions {
|
||||||
@@ -39,8 +39,8 @@ export async function waitForTaskSessionID(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const initialTask = manager.getTask(taskID)
|
const initialTask = manager.getTask(taskID)
|
||||||
if (initialTask?.sessionID) {
|
if (initialTask?.sessionId) {
|
||||||
return initialTask.sessionID
|
return initialTask.sessionId
|
||||||
}
|
}
|
||||||
if (isTerminalStatus(initialTask?.status)) {
|
if (isTerminalStatus(initialTask?.status)) {
|
||||||
return undefined
|
return undefined
|
||||||
@@ -56,8 +56,8 @@ export async function waitForTaskSessionID(
|
|||||||
await waitForInterval(intervalMs)
|
await waitForInterval(intervalMs)
|
||||||
|
|
||||||
const task = manager.getTask(taskID)
|
const task = manager.getTask(taskID)
|
||||||
if (task?.sessionID) {
|
if (task?.sessionId) {
|
||||||
return task.sessionID
|
return task.sessionId
|
||||||
}
|
}
|
||||||
if (isTerminalStatus(task?.status)) {
|
if (isTerminalStatus(task?.status)) {
|
||||||
return undefined
|
return undefined
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ Task ID: ${task.id}
|
|||||||
Description: ${task.description}
|
Description: ${task.description}
|
||||||
Agent: ${task.agent}
|
Agent: ${task.agent}
|
||||||
Status: ${task.status}
|
Status: ${task.status}
|
||||||
Session ID: ${task.sessionID ?? "N/A"}
|
Session ID: ${task.sessionId ?? "N/A"}
|
||||||
|
|
||||||
Thinking summary (first ${THINKING_SUMMARY_MAX_CHARS} chars):
|
Thinking summary (first ${THINKING_SUMMARY_MAX_CHARS} chars):
|
||||||
${summaryText}
|
${summaryText}
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ export function createUnstableAgentBabysitterHook(ctx: BabysitterContext, option
|
|||||||
const lastReminderAt = reminderCooldowns.get(task.id)
|
const lastReminderAt = reminderCooldowns.get(task.id)
|
||||||
if (lastReminderAt && now - lastReminderAt < COOLDOWN_MS) continue
|
if (lastReminderAt && now - lastReminderAt < COOLDOWN_MS) continue
|
||||||
|
|
||||||
const summary = task.sessionID ? await getThinkingSummary(ctx, task.sessionID) : null
|
const summary = task.sessionId ? await getThinkingSummary(ctx, task.sessionId) : null
|
||||||
const reminder = buildReminder(task, summary, idleMs)
|
const reminder = buildReminder(task, summary, idleMs)
|
||||||
const { agent, model, tools } = await resolveMainSessionTarget(ctx, mainSessionID)
|
const { agent, model, tools } = await resolveMainSessionTarget(ctx, mainSessionID)
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export function createBackgroundCancel(manager: BackgroundManager, _client: Back
|
|||||||
id: task.id,
|
id: task.id,
|
||||||
description: task.description,
|
description: task.description,
|
||||||
status: originalStatus === "pending" ? "pending" : "running",
|
status: originalStatus === "pending" ? "pending" : "running",
|
||||||
sessionID: task.sessionID,
|
sessionID: task.sessionId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ Status: ${task.status}`
|
|||||||
|
|
||||||
Task ID: ${task.id}
|
Task ID: ${task.id}
|
||||||
Description: ${task.description}
|
Description: ${task.description}
|
||||||
Session ID: ${task.sessionID}
|
Session ID: ${task.sessionId}
|
||||||
Status: ${task.status}`
|
Status: ${task.status}`
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return `[ERROR] Error cancelling task: ${error instanceof Error ? error.message : String(error)}`
|
return `[ERROR] Error cancelling task: ${error instanceof Error ? error.message : String(error)}`
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ const mockContext = {
|
|||||||
function createTask(overrides: Partial<BackgroundTask> = {}): BackgroundTask {
|
function createTask(overrides: Partial<BackgroundTask> = {}): BackgroundTask {
|
||||||
return {
|
return {
|
||||||
id: "task-1",
|
id: "task-1",
|
||||||
sessionID: "ses-1",
|
sessionId: "ses-1",
|
||||||
parentSessionID: "main-1",
|
parentSessionId: "main-1",
|
||||||
parentMessageID: "msg-1",
|
parentMessageId: "msg-1",
|
||||||
description: "background task",
|
description: "background task",
|
||||||
prompt: "do work",
|
prompt: "do work",
|
||||||
agent: "test-agent",
|
agent: "test-agent",
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ describe("createBackgroundOutput metadata", () => {
|
|||||||
|
|
||||||
const task: BackgroundTask = {
|
const task: BackgroundTask = {
|
||||||
id: "task-1",
|
id: "task-1",
|
||||||
sessionID: undefined,
|
sessionId: undefined,
|
||||||
parentSessionID: "main-1",
|
parentSessionId: "main-1",
|
||||||
parentMessageID: "msg-1",
|
parentMessageId: "msg-1",
|
||||||
description: "background task",
|
description: "background task",
|
||||||
prompt: "do work",
|
prompt: "do work",
|
||||||
agent: "test-agent",
|
agent: "test-agent",
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export function createBackgroundOutput(manager: BackgroundOutputManager, client:
|
|||||||
agent: task.agent,
|
agent: task.agent,
|
||||||
category: task.category,
|
category: task.category,
|
||||||
description: task.description,
|
description: task.description,
|
||||||
...(task.sessionID ? { sessionId: task.sessionID, taskId: task.sessionID } : {}),
|
...(task.sessionId ? { sessionId: task.sessionId, taskId: task.sessionId } : {}),
|
||||||
} as Record<string, unknown>,
|
} as Record<string, unknown>,
|
||||||
}
|
}
|
||||||
await publishToolMetadata(ctx, meta)
|
await publishToolMetadata(ctx, meta)
|
||||||
@@ -129,7 +129,7 @@ export function createBackgroundOutput(manager: BackgroundOutputManager, client:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (resolvedTask.status === "completed") {
|
if (resolvedTask.status === "completed") {
|
||||||
recordBackgroundOutputConsumption(ctx.sessionID, ctx.messageID, resolvedTask.sessionID)
|
recordBackgroundOutputConsumption(ctx.sessionID, ctx.messageID, resolvedTask.sessionId)
|
||||||
return await formatTaskResult(resolvedTask, client)
|
return await formatTaskResult(resolvedTask, client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ const baseContext = {
|
|||||||
function createTask(overrides: Partial<BackgroundTask> = {}): BackgroundTask {
|
function createTask(overrides: Partial<BackgroundTask> = {}): BackgroundTask {
|
||||||
return {
|
return {
|
||||||
id: "task-1",
|
id: "task-1",
|
||||||
sessionID: taskSessionID,
|
sessionId: taskSessionID,
|
||||||
parentSessionID,
|
parentSessionId: parentSessionID,
|
||||||
parentMessageID: "msg-parent",
|
parentMessageId: "msg-parent",
|
||||||
description: "background task",
|
description: "background task",
|
||||||
prompt: "do work",
|
prompt: "do work",
|
||||||
agent: "test-agent",
|
agent: "test-agent",
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import { createBackgroundTask } from "./create-background-task"
|
|||||||
describe("createBackgroundTask", () => {
|
describe("createBackgroundTask", () => {
|
||||||
const launchMock = mock(async (): Promise<{
|
const launchMock = mock(async (): Promise<{
|
||||||
id: string
|
id: string
|
||||||
sessionID: string | null
|
sessionId: string | null
|
||||||
description: string
|
description: string
|
||||||
agent: string
|
agent: string
|
||||||
status: string
|
status: string
|
||||||
}> => ({
|
}> => ({
|
||||||
id: "test-task-id",
|
id: "test-task-id",
|
||||||
sessionID: null,
|
sessionId: null,
|
||||||
description: "Test task",
|
description: "Test task",
|
||||||
agent: "test-agent",
|
agent: "test-agent",
|
||||||
status: "pending",
|
status: "pending",
|
||||||
@@ -55,14 +55,14 @@ describe("createBackgroundTask", () => {
|
|||||||
//#given
|
//#given
|
||||||
launchMock.mockResolvedValueOnce({
|
launchMock.mockResolvedValueOnce({
|
||||||
id: "test-task-id",
|
id: "test-task-id",
|
||||||
sessionID: null,
|
sessionId: null,
|
||||||
description: "Test task",
|
description: "Test task",
|
||||||
agent: "test-agent",
|
agent: "test-agent",
|
||||||
status: "pending",
|
status: "pending",
|
||||||
})
|
})
|
||||||
getTaskMock.mockReturnValueOnce({
|
getTaskMock.mockReturnValueOnce({
|
||||||
id: "test-task-id",
|
id: "test-task-id",
|
||||||
sessionID: null,
|
sessionId: null,
|
||||||
description: "Test task",
|
description: "Test task",
|
||||||
agent: "test-agent",
|
agent: "test-agent",
|
||||||
status: "interrupt",
|
status: "interrupt",
|
||||||
@@ -81,7 +81,7 @@ describe("createBackgroundTask", () => {
|
|||||||
const abortController = new AbortController()
|
const abortController = new AbortController()
|
||||||
launchMock.mockResolvedValueOnce({
|
launchMock.mockResolvedValueOnce({
|
||||||
id: "test-task-id",
|
id: "test-task-id",
|
||||||
sessionID: null,
|
sessionId: null,
|
||||||
description: "Test task",
|
description: "Test task",
|
||||||
agent: "test-agent",
|
agent: "test-agent",
|
||||||
status: "pending",
|
status: "pending",
|
||||||
@@ -90,7 +90,7 @@ describe("createBackgroundTask", () => {
|
|||||||
abortController.abort()
|
abortController.abort()
|
||||||
return {
|
return {
|
||||||
id: "test-task-id",
|
id: "test-task-id",
|
||||||
sessionID: null,
|
sessionId: null,
|
||||||
description: "Test task",
|
description: "Test task",
|
||||||
agent: "test-agent",
|
agent: "test-agent",
|
||||||
status: "pending",
|
status: "pending",
|
||||||
@@ -114,15 +114,15 @@ describe("createBackgroundTask", () => {
|
|||||||
const firstAbortController = new AbortController()
|
const firstAbortController = new AbortController()
|
||||||
const secondAbortController = new AbortController()
|
const secondAbortController = new AbortController()
|
||||||
const states = new Map([
|
const states = new Map([
|
||||||
["task-1", { reads: 0, abortOnFirstRead: true, sessionID: "ses-1" }],
|
["task-1", { reads: 0, abortOnFirstRead: true, sessionId: "ses-1" }],
|
||||||
["task-2", { reads: 0, abortOnFirstRead: false, sessionID: "ses-2" }],
|
["task-2", { reads: 0, abortOnFirstRead: false, sessionId: "ses-2" }],
|
||||||
])
|
])
|
||||||
let launchCount = 0
|
let launchCount = 0
|
||||||
launchMock.mockImplementation(async () => {
|
launchMock.mockImplementation(async () => {
|
||||||
launchCount += 1
|
launchCount += 1
|
||||||
return launchCount === 1
|
return launchCount === 1
|
||||||
? { id: "task-1", sessionID: null, description: "Task 1", agent: "test-agent", status: "pending" }
|
? { id: "task-1", sessionId: null, description: "Task 1", agent: "test-agent", status: "pending" }
|
||||||
: { id: "task-2", sessionID: null, description: "Task 2", agent: "test-agent", status: "pending" }
|
: { id: "task-2", sessionId: null, description: "Task 2", agent: "test-agent", status: "pending" }
|
||||||
})
|
})
|
||||||
getTaskMock.mockImplementation((taskID: string) => {
|
getTaskMock.mockImplementation((taskID: string) => {
|
||||||
const state = states.get(taskID)
|
const state = states.get(taskID)
|
||||||
@@ -132,8 +132,8 @@ describe("createBackgroundTask", () => {
|
|||||||
firstAbortController.abort()
|
firstAbortController.abort()
|
||||||
}
|
}
|
||||||
return state.reads >= 2
|
return state.reads >= 2
|
||||||
? { id: taskID, sessionID: state.sessionID, description: "Task", agent: "test-agent", status: "pending" }
|
? { id: taskID, sessionId: state.sessionId, description: "Task", agent: "test-agent", status: "pending" }
|
||||||
: { id: taskID, sessionID: null, description: "Task", agent: "test-agent", status: "pending" }
|
: { id: taskID, sessionId: null, description: "Task", agent: "test-agent", status: "pending" }
|
||||||
})
|
})
|
||||||
|
|
||||||
//#when
|
//#when
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ export function createBackgroundTask(
|
|||||||
description: args.description,
|
description: args.description,
|
||||||
prompt: args.prompt,
|
prompt: args.prompt,
|
||||||
agent: args.agent.trim(),
|
agent: args.agent.trim(),
|
||||||
parentSessionID: ctx.sessionID,
|
parentSessionId: ctx.sessionID,
|
||||||
parentMessageID: ctx.messageID,
|
parentMessageId: ctx.messageID,
|
||||||
parentModel,
|
parentModel,
|
||||||
parentAgent,
|
parentAgent,
|
||||||
})
|
})
|
||||||
@@ -78,13 +78,13 @@ export function createBackgroundTask(
|
|||||||
const WAIT_FOR_SESSION_INTERVAL_MS = 50
|
const WAIT_FOR_SESSION_INTERVAL_MS = 50
|
||||||
const WAIT_FOR_SESSION_TIMEOUT_MS = 30000
|
const WAIT_FOR_SESSION_TIMEOUT_MS = 30000
|
||||||
const waitStart = Date.now()
|
const waitStart = Date.now()
|
||||||
let sessionId = task.sessionID
|
let sessionId = task.sessionId
|
||||||
while (!sessionId && Date.now() - waitStart < WAIT_FOR_SESSION_TIMEOUT_MS) {
|
while (!sessionId && Date.now() - waitStart < WAIT_FOR_SESSION_TIMEOUT_MS) {
|
||||||
const updated = manager.getTask(task.id)
|
const updated = manager.getTask(task.id)
|
||||||
if (updated?.status === "error" || updated?.status === "cancelled" || updated?.status === "interrupt") {
|
if (updated?.status === "error" || updated?.status === "cancelled" || updated?.status === "interrupt") {
|
||||||
return `Task ${`entered error state`}\.\n\nTask ID: ${task.id}`
|
return `Task ${`entered error state`}\.\n\nTask ID: ${task.id}`
|
||||||
}
|
}
|
||||||
sessionId = updated?.sessionID
|
sessionId = updated?.sessionId
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,12 +41,12 @@ export async function formatFullSession(
|
|||||||
thinkingMaxChars?: number
|
thinkingMaxChars?: number
|
||||||
}
|
}
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (!task.sessionID) {
|
if (!task.sessionId) {
|
||||||
return formatTaskStatus(task)
|
return formatTaskStatus(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
const messagesResult: BackgroundOutputMessagesResult = await client.session.messages({
|
const messagesResult: BackgroundOutputMessagesResult = await client.session.messages({
|
||||||
path: { id: task.sessionID },
|
path: { id: task.sessionId },
|
||||||
})
|
})
|
||||||
|
|
||||||
const errorMessage = getErrorMessage(messagesResult)
|
const errorMessage = getErrorMessage(messagesResult)
|
||||||
@@ -107,7 +107,7 @@ export async function formatFullSession(
|
|||||||
lines.push(`Task ID: ${task.id}`)
|
lines.push(`Task ID: ${task.id}`)
|
||||||
lines.push(`Description: ${task.description}`)
|
lines.push(`Description: ${task.description}`)
|
||||||
lines.push(`Status: ${task.status}`)
|
lines.push(`Status: ${task.status}`)
|
||||||
lines.push(`Session ID: ${task.sessionID}`)
|
lines.push(`Session ID: ${task.sessionId}`)
|
||||||
lines.push(`Total messages: ${normalizedMessages.length}`)
|
lines.push(`Total messages: ${normalizedMessages.length}`)
|
||||||
lines.push(`Returned: ${visibleMessages.length}`)
|
lines.push(`Returned: ${visibleMessages.length}`)
|
||||||
lines.push(`Has more: ${hasMore ? "true" : "false"}`)
|
lines.push(`Has more: ${hasMore ? "true" : "false"}`)
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import { formatTaskResult } from "./task-result-format"
|
|||||||
function createTask(overrides: Partial<BackgroundTask> = {}): BackgroundTask {
|
function createTask(overrides: Partial<BackgroundTask> = {}): BackgroundTask {
|
||||||
return {
|
return {
|
||||||
id: "task-1",
|
id: "task-1",
|
||||||
sessionID: "ses-1",
|
sessionId: "ses-1",
|
||||||
parentSessionID: "main-1",
|
parentSessionId: "main-1",
|
||||||
parentMessageID: "msg-1",
|
parentMessageId: "msg-1",
|
||||||
description: "background task",
|
description: "background task",
|
||||||
prompt: "do work",
|
prompt: "do work",
|
||||||
agent: "test-agent",
|
agent: "test-agent",
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ function getTimeString(value: unknown): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function formatTaskResult(task: BackgroundTask, client: BackgroundOutputClient): Promise<string> {
|
export async function formatTaskResult(task: BackgroundTask, client: BackgroundOutputClient): Promise<string> {
|
||||||
if (!task.sessionID) {
|
if (!task.sessionId) {
|
||||||
return `Error: Task has no sessionID`
|
return `Error: Task has no sessionID`
|
||||||
}
|
}
|
||||||
|
|
||||||
const messagesResult: BackgroundOutputMessagesResult = await client.session.messages({
|
const messagesResult: BackgroundOutputMessagesResult = await client.session.messages({
|
||||||
path: { id: task.sessionID },
|
path: { id: task.sessionId },
|
||||||
})
|
})
|
||||||
|
|
||||||
const errorMessage = getErrorMessage(messagesResult)
|
const errorMessage = getErrorMessage(messagesResult)
|
||||||
@@ -30,7 +30,7 @@ export async function formatTaskResult(task: BackgroundTask, client: BackgroundO
|
|||||||
Task ID: ${task.id}
|
Task ID: ${task.id}
|
||||||
Description: ${task.description}
|
Description: ${task.description}
|
||||||
Duration: ${formatDuration(task.startedAt ?? new Date(), task.completedAt)}
|
Duration: ${formatDuration(task.startedAt ?? new Date(), task.completedAt)}
|
||||||
Session ID: ${task.sessionID}
|
Session ID: ${task.sessionId}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ Session ID: ${task.sessionID}
|
|||||||
Task ID: ${task.id}
|
Task ID: ${task.id}
|
||||||
Description: ${task.description}
|
Description: ${task.description}
|
||||||
Duration: ${formatDuration(task.startedAt ?? new Date(), task.completedAt)}
|
Duration: ${formatDuration(task.startedAt ?? new Date(), task.completedAt)}
|
||||||
Session ID: ${task.sessionID}
|
Session ID: ${task.sessionId}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -67,14 +67,14 @@ Session ID: ${task.sessionID}
|
|||||||
Task ID: ${task.id}
|
Task ID: ${task.id}
|
||||||
Description: ${task.description}
|
Description: ${task.description}
|
||||||
Duration: ${formatDuration(task.startedAt ?? new Date(), task.completedAt)}
|
Duration: ${formatDuration(task.startedAt ?? new Date(), task.completedAt)}
|
||||||
Session ID: ${task.sessionID}
|
Session ID: ${task.sessionId}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Session error: ${sessionError}`
|
Session error: ${sessionError}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const newMessages = consumeNewMessages(task.sessionID, sortedMessages)
|
const newMessages = consumeNewMessages(task.sessionId, sortedMessages)
|
||||||
if (newMessages.length === 0) {
|
if (newMessages.length === 0) {
|
||||||
const duration = formatDuration(task.startedAt ?? new Date(), task.completedAt)
|
const duration = formatDuration(task.startedAt ?? new Date(), task.completedAt)
|
||||||
return `Task Result
|
return `Task Result
|
||||||
@@ -82,7 +82,7 @@ Session error: ${sessionError}`
|
|||||||
Task ID: ${task.id}
|
Task ID: ${task.id}
|
||||||
Description: ${task.description}
|
Description: ${task.description}
|
||||||
Duration: ${duration}
|
Duration: ${duration}
|
||||||
Session ID: ${task.sessionID}
|
Session ID: ${task.sessionId}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ Session ID: ${task.sessionID}
|
|||||||
Task ID: ${task.id}
|
Task ID: ${task.id}
|
||||||
Description: ${task.description}
|
Description: ${task.description}
|
||||||
Duration: ${duration}
|
Duration: ${duration}
|
||||||
Session ID: ${task.sessionID}
|
Session ID: ${task.sessionId}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ ${truncated}
|
|||||||
| Agent | ${task.agent} |
|
| Agent | ${task.agent} |
|
||||||
| Status | **${task.status}** |
|
| Status | **${task.status}** |
|
||||||
| ${durationLabel} | ${duration} |
|
| ${durationLabel} | ${duration} |
|
||||||
| Session ID | \`${task.sessionID}\` |${progressSection}
|
| Session ID | \`${task.sessionId}\` |${progressSection}
|
||||||
${statusNote}
|
${statusNote}
|
||||||
## Original Prompt
|
## Original Prompt
|
||||||
|
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ function createMockClient(messagesBySession: Record<string, BackgroundOutputMess
|
|||||||
function createTask(overrides: Partial<BackgroundTask> = {}): BackgroundTask {
|
function createTask(overrides: Partial<BackgroundTask> = {}): BackgroundTask {
|
||||||
return {
|
return {
|
||||||
id: "task-1",
|
id: "task-1",
|
||||||
sessionID: "ses-1",
|
sessionId: "ses-1",
|
||||||
parentSessionID: "main-1",
|
parentSessionId: "main-1",
|
||||||
parentMessageID: "msg-1",
|
parentMessageId: "msg-1",
|
||||||
description: "background task",
|
description: "background task",
|
||||||
prompt: "do work",
|
prompt: "do work",
|
||||||
agent: "test-agent",
|
agent: "test-agent",
|
||||||
@@ -345,7 +345,7 @@ describe("background_output blocking", () => {
|
|||||||
test("block=true keeps legacy task result output when full_session is not provided", async () => {
|
test("block=true keeps legacy task result output when full_session is not provided", async () => {
|
||||||
// #given a task that transitions running → completed after 2 polls
|
// #given a task that transitions running → completed after 2 polls
|
||||||
let pollCount = 0
|
let pollCount = 0
|
||||||
const task = createTask({ status: "running", sessionID: "ses-blocking-default" })
|
const task = createTask({ status: "running", sessionId: "ses-blocking-default" })
|
||||||
const manager: BackgroundOutputManager = {
|
const manager: BackgroundOutputManager = {
|
||||||
getTask: (id: string) => {
|
getTask: (id: string) => {
|
||||||
if (id !== task.id) return undefined
|
if (id !== task.id) return undefined
|
||||||
@@ -435,8 +435,8 @@ describe("background_cancel", () => {
|
|||||||
|
|
||||||
test("preserves original status in cancellation table", async () => {
|
test("preserves original status in cancellation table", async () => {
|
||||||
// #given
|
// #given
|
||||||
const taskA = createTask({ id: "task-a", status: "running", sessionID: "ses-a", description: "running task" })
|
const taskA = createTask({ id: "task-a", status: "running", sessionId: "ses-a", description: "running task" })
|
||||||
const taskB = createTask({ id: "task-b", status: "pending", sessionID: undefined, description: "pending task" })
|
const taskB = createTask({ id: "task-b", status: "pending", sessionId: undefined, description: "pending task" })
|
||||||
const manager = {
|
const manager = {
|
||||||
getTask: () => undefined,
|
getTask: () => undefined,
|
||||||
getAllDescendantTasks: () => [taskA, taskB],
|
getAllDescendantTasks: () => [taskA, taskB],
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ export async function executeBackgroundAgent(
|
|||||||
description: args.description,
|
description: args.description,
|
||||||
prompt: args.prompt,
|
prompt: args.prompt,
|
||||||
agent: args.subagent_type,
|
agent: args.subagent_type,
|
||||||
parentSessionID: toolContext.sessionID,
|
parentSessionId: toolContext.sessionID,
|
||||||
parentMessageID: toolContext.messageID,
|
parentMessageId: toolContext.messageID,
|
||||||
parentAgent,
|
parentAgent,
|
||||||
parentTools: getSessionTools(toolContext.sessionID),
|
parentTools: getSessionTools(toolContext.sessionID),
|
||||||
})
|
})
|
||||||
@@ -50,13 +50,13 @@ export async function executeBackgroundAgent(
|
|||||||
const waitTimeoutMs = 30_000
|
const waitTimeoutMs = 30_000
|
||||||
const waitIntervalMs = 50
|
const waitIntervalMs = 50
|
||||||
|
|
||||||
let sessionId = task.sessionID
|
let sessionId = task.sessionId
|
||||||
while (!sessionId && Date.now() - waitStart < waitTimeoutMs) {
|
while (!sessionId && Date.now() - waitStart < waitTimeoutMs) {
|
||||||
const updated = manager.getTask(task.id)
|
const updated = manager.getTask(task.id)
|
||||||
if (updated?.status === "error" || updated?.status === "cancelled" || updated?.status === "interrupt") {
|
if (updated?.status === "error" || updated?.status === "cancelled" || updated?.status === "interrupt") {
|
||||||
return `Task failed to start (status: ${updated.status}).\n\nTask ID: ${task.id}`
|
return `Task failed to start (status: ${updated.status}).\n\nTask ID: ${task.id}`
|
||||||
}
|
}
|
||||||
sessionId = updated?.sessionID
|
sessionId = updated?.sessionId
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ export async function executeBackground(
|
|||||||
description: args.description,
|
description: args.description,
|
||||||
prompt: args.prompt,
|
prompt: args.prompt,
|
||||||
agent: args.subagent_type,
|
agent: args.subagent_type,
|
||||||
parentSessionID: toolContext.sessionID,
|
parentSessionId: toolContext.sessionID,
|
||||||
parentMessageID: toolContext.messageID,
|
parentMessageId: toolContext.messageID,
|
||||||
parentAgent,
|
parentAgent,
|
||||||
parentTools: getSessionTools(toolContext.sessionID),
|
parentTools: getSessionTools(toolContext.sessionID),
|
||||||
model,
|
model,
|
||||||
@@ -59,13 +59,13 @@ export async function executeBackground(
|
|||||||
const WAIT_FOR_SESSION_INTERVAL_MS = 50
|
const WAIT_FOR_SESSION_INTERVAL_MS = 50
|
||||||
const WAIT_FOR_SESSION_TIMEOUT_MS = 30000
|
const WAIT_FOR_SESSION_TIMEOUT_MS = 30000
|
||||||
const waitStart = Date.now()
|
const waitStart = Date.now()
|
||||||
let sessionId = task.sessionID
|
let sessionId = task.sessionId
|
||||||
while (!sessionId && Date.now() - waitStart < WAIT_FOR_SESSION_TIMEOUT_MS) {
|
while (!sessionId && Date.now() - waitStart < WAIT_FOR_SESSION_TIMEOUT_MS) {
|
||||||
const updated = manager.getTask(task.id)
|
const updated = manager.getTask(task.id)
|
||||||
if (updated?.status === "error" || updated?.status === "cancelled" || updated?.status === "interrupt") {
|
if (updated?.status === "error" || updated?.status === "cancelled" || updated?.status === "interrupt") {
|
||||||
return `Task failed to start (status: ${updated.status}).\n\nTask ID: ${task.id}`
|
return `Task failed to start (status: ${updated.status}).\n\nTask ID: ${task.id}`
|
||||||
}
|
}
|
||||||
sessionId = updated?.sessionID
|
sessionId = updated?.sessionId
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ describe("executeBackgroundContinuation - subagent metadata", () => {
|
|||||||
description: "oracle consultation",
|
description: "oracle consultation",
|
||||||
agent: "oracle",
|
agent: "oracle",
|
||||||
status: "running",
|
status: "running",
|
||||||
sessionID: "ses_resumed_123",
|
sessionId: "ses_resumed_123",
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ describe("executeBackgroundContinuation - subagent metadata", () => {
|
|||||||
description: "unknown task",
|
description: "unknown task",
|
||||||
agent: undefined,
|
agent: undefined,
|
||||||
status: "running",
|
status: "running",
|
||||||
sessionID: "ses_resumed_456",
|
sessionId: "ses_resumed_456",
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ export async function executeBackgroundContinuation(
|
|||||||
const task = await manager.resume({
|
const task = await manager.resume({
|
||||||
sessionId: taskID,
|
sessionId: taskID,
|
||||||
prompt: effectivePrompt,
|
prompt: effectivePrompt,
|
||||||
parentSessionID: parentContext.sessionID,
|
parentSessionId: parentContext.sessionID,
|
||||||
parentMessageID: parentContext.messageID,
|
parentMessageId: parentContext.messageID,
|
||||||
parentModel: parentContext.model,
|
parentModel: parentContext.model,
|
||||||
parentAgent: parentContext.agent,
|
parentAgent: parentContext.agent,
|
||||||
parentTools: getSessionTools(parentContext.sessionID),
|
parentTools: getSessionTools(parentContext.sessionID),
|
||||||
})
|
})
|
||||||
const sessionId = task.sessionID
|
const sessionId = task.sessionId
|
||||||
const backgroundTaskId = task.id
|
const backgroundTaskId = task.id
|
||||||
const resolvedModel = resolveMetadataModel(task.model, parentContext.model)
|
const resolvedModel = resolveMetadataModel(task.model, parentContext.model)
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
|
|||||||
const manager = {
|
const manager = {
|
||||||
launch: async () => ({
|
launch: async () => ({
|
||||||
id: "bg_unresolved",
|
id: "bg_unresolved",
|
||||||
sessionID: undefined,
|
sessionId: undefined,
|
||||||
description: "Unresolved session",
|
description: "Unresolved session",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -72,12 +72,12 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
|
|||||||
const manager = {
|
const manager = {
|
||||||
launch: async () => ({
|
launch: async () => ({
|
||||||
id: "bg_resolved",
|
id: "bg_resolved",
|
||||||
sessionID: "ses_sub_123",
|
sessionId: "ses_sub_123",
|
||||||
description: "Resolved session",
|
description: "Resolved session",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
status: "running",
|
status: "running",
|
||||||
}),
|
}),
|
||||||
getTask: () => ({ sessionID: "ses_sub_123" }),
|
getTask: () => ({ sessionId: "ses_sub_123" }),
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await executeBackgroundTask(
|
const result = await executeBackgroundTask(
|
||||||
@@ -121,14 +121,14 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
|
|||||||
const manager = {
|
const manager = {
|
||||||
launch: async () => ({
|
launch: async () => ({
|
||||||
id: "bg_late",
|
id: "bg_late",
|
||||||
sessionID: undefined,
|
sessionId: undefined,
|
||||||
description: "Late session",
|
description: "Late session",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
status: "running",
|
status: "running",
|
||||||
}),
|
}),
|
||||||
getTask: () => {
|
getTask: () => {
|
||||||
reads += 1
|
reads += 1
|
||||||
return reads >= 2 ? { sessionID: "ses_late_123" } : undefined
|
return reads >= 2 ? { sessionId: "ses_late_123" } : undefined
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,13 +171,13 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
|
|||||||
launchCalls.push(input)
|
launchCalls.push(input)
|
||||||
return {
|
return {
|
||||||
id: "bg_permission",
|
id: "bg_permission",
|
||||||
sessionID: "ses_permission_123",
|
sessionId: "ses_permission_123",
|
||||||
description: "Permission session",
|
description: "Permission session",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
status: "running",
|
status: "running",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getTask: () => ({ sessionID: "ses_permission_123" }),
|
getTask: () => ({ sessionId: "ses_permission_123" }),
|
||||||
}
|
}
|
||||||
|
|
||||||
//#when
|
//#when
|
||||||
@@ -217,13 +217,13 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
|
|||||||
launchCalls.push(input)
|
launchCalls.push(input)
|
||||||
return {
|
return {
|
||||||
id: "bg_clean_agent",
|
id: "bg_clean_agent",
|
||||||
sessionID: "ses_clean_agent",
|
sessionId: "ses_clean_agent",
|
||||||
description: "Clean agent",
|
description: "Clean agent",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getTask: () => ({ sessionID: "ses_clean_agent" }),
|
getTask: () => ({ sessionId: "ses_clean_agent" }),
|
||||||
}
|
}
|
||||||
|
|
||||||
//#when
|
//#when
|
||||||
@@ -260,14 +260,14 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
|
|||||||
const manager = {
|
const manager = {
|
||||||
launch: async () => ({
|
launch: async () => ({
|
||||||
id: "bg_abort_after_launch",
|
id: "bg_abort_after_launch",
|
||||||
sessionID: undefined,
|
sessionId: undefined,
|
||||||
description: "Abort after launch",
|
description: "Abort after launch",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
status: "pending",
|
status: "pending",
|
||||||
}),
|
}),
|
||||||
getTask: () => {
|
getTask: () => {
|
||||||
abortController.abort()
|
abortController.abort()
|
||||||
return { sessionID: undefined, status: "pending" }
|
return { sessionId: undefined, status: "pending" }
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,7 +309,7 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
|
|||||||
const manager = {
|
const manager = {
|
||||||
launch: async () => ({
|
launch: async () => ({
|
||||||
id: "bg_abort_category",
|
id: "bg_abort_category",
|
||||||
sessionID: undefined,
|
sessionId: undefined,
|
||||||
description: "Abort category",
|
description: "Abort category",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
status: "pending",
|
status: "pending",
|
||||||
@@ -317,8 +317,8 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
|
|||||||
getTask: () => {
|
getTask: () => {
|
||||||
reads += 1
|
reads += 1
|
||||||
return reads >= 2
|
return reads >= 2
|
||||||
? { sessionID: "ses_abort_category", status: "running" }
|
? { sessionId: "ses_abort_category", status: "running" }
|
||||||
: { sessionID: undefined, status: "pending" }
|
: { sessionId: undefined, status: "pending" }
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,12 +359,12 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
|
|||||||
const manager = {
|
const manager = {
|
||||||
launch: async () => ({
|
launch: async () => ({
|
||||||
id: "bg_abort_terminal",
|
id: "bg_abort_terminal",
|
||||||
sessionID: undefined,
|
sessionId: undefined,
|
||||||
description: "Abort terminal",
|
description: "Abort terminal",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
status: "pending",
|
status: "pending",
|
||||||
}),
|
}),
|
||||||
getTask: () => ({ sessionID: undefined, status: "interrupt" }),
|
getTask: () => ({ sessionId: undefined, status: "interrupt" }),
|
||||||
}
|
}
|
||||||
|
|
||||||
//#when
|
//#when
|
||||||
@@ -401,7 +401,7 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
|
|||||||
const manager = {
|
const manager = {
|
||||||
launch: async () => ({
|
launch: async () => ({
|
||||||
id: "bg_crash_before_prompt",
|
id: "bg_crash_before_prompt",
|
||||||
sessionID: undefined,
|
sessionId: undefined,
|
||||||
description: "Crash before prompt",
|
description: "Crash before prompt",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
status: "pending",
|
status: "pending",
|
||||||
@@ -409,9 +409,9 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
|
|||||||
getTask: () => {
|
getTask: () => {
|
||||||
reads += 1
|
reads += 1
|
||||||
if (reads >= 2) {
|
if (reads >= 2) {
|
||||||
return { sessionID: "ses_orphan", status: "error", error: "crash between session creation and prompt send" }
|
return { sessionId: "ses_orphan", status: "error", error: "crash between session creation and prompt send" }
|
||||||
}
|
}
|
||||||
return { sessionID: undefined, status: "pending" }
|
return { sessionId: undefined, status: "pending" }
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,16 +447,16 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
|
|||||||
const firstAbortController = new AbortController()
|
const firstAbortController = new AbortController()
|
||||||
const secondAbortController = new AbortController()
|
const secondAbortController = new AbortController()
|
||||||
const states = new Map([
|
const states = new Map([
|
||||||
["bg_first", { reads: 0, abortOnFirstRead: true, sessionID: "ses_first" }],
|
["bg_first", { reads: 0, abortOnFirstRead: true, sessionId: "ses_first" }],
|
||||||
["bg_second", { reads: 0, abortOnFirstRead: false, sessionID: "ses_second" }],
|
["bg_second", { reads: 0, abortOnFirstRead: false, sessionId: "ses_second" }],
|
||||||
])
|
])
|
||||||
let launchCount = 0
|
let launchCount = 0
|
||||||
const manager = {
|
const manager = {
|
||||||
launch: async () => {
|
launch: async () => {
|
||||||
launchCount += 1
|
launchCount += 1
|
||||||
return launchCount === 1
|
return launchCount === 1
|
||||||
? { id: "bg_first", sessionID: undefined, description: "First", agent: "explore", status: "pending" }
|
? { id: "bg_first", sessionId: undefined, description: "First", agent: "explore", status: "pending" }
|
||||||
: { id: "bg_second", sessionID: undefined, description: "Second", agent: "explore", status: "pending" }
|
: { id: "bg_second", sessionId: undefined, description: "Second", agent: "explore", status: "pending" }
|
||||||
},
|
},
|
||||||
getTask: (taskID: string) => {
|
getTask: (taskID: string) => {
|
||||||
const state = states.get(taskID)
|
const state = states.get(taskID)
|
||||||
@@ -466,8 +466,8 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
|
|||||||
firstAbortController.abort()
|
firstAbortController.abort()
|
||||||
}
|
}
|
||||||
return state.reads >= 2
|
return state.reads >= 2
|
||||||
? { sessionID: state.sessionID, status: "running" }
|
? { sessionId: state.sessionId, status: "running" }
|
||||||
: { sessionID: undefined, status: "pending" }
|
: { sessionId: undefined, status: "pending" }
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,13 +531,13 @@ describeFn("executeBackgroundTask output/session metadata compatibility", () =>
|
|||||||
launchCalls.push(input)
|
launchCalls.push(input)
|
||||||
return {
|
return {
|
||||||
id: "bg_legacy_zwsp",
|
id: "bg_legacy_zwsp",
|
||||||
sessionID: "ses_legacy_zwsp",
|
sessionId: "ses_legacy_zwsp",
|
||||||
description: "Legacy ZWSP",
|
description: "Legacy ZWSP",
|
||||||
agent: "Hephaestus - Deep Agent",
|
agent: "Hephaestus - Deep Agent",
|
||||||
status: "running",
|
status: "running",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getTask: () => ({ sessionID: "ses_legacy_zwsp" }),
|
getTask: () => ({ sessionId: "ses_legacy_zwsp" }),
|
||||||
}
|
}
|
||||||
|
|
||||||
//#when
|
//#when
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ function continueSessionSetup(args: {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const sessionId = updated.sessionID
|
const sessionId = updated.sessionId
|
||||||
if (!sessionId) {
|
if (!sessionId) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ async function waitForBackgroundSessionStart(args: {
|
|||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionId = updated?.sessionID
|
sessionId = updated?.sessionId
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
return sessionId
|
return sessionId
|
||||||
}
|
}
|
||||||
@@ -117,8 +117,8 @@ export async function executeBackgroundTask(
|
|||||||
description: args.description,
|
description: args.description,
|
||||||
prompt: effectivePrompt,
|
prompt: effectivePrompt,
|
||||||
agent: normalizedAgent,
|
agent: normalizedAgent,
|
||||||
parentSessionID: parentContext.sessionID,
|
parentSessionId: parentContext.sessionID,
|
||||||
parentMessageID: parentContext.messageID,
|
parentMessageId: parentContext.messageID,
|
||||||
parentModel: parentContext.model,
|
parentModel: parentContext.model,
|
||||||
parentAgent: parentContext.agent,
|
parentAgent: parentContext.agent,
|
||||||
parentTools: getSessionTools(parentContext.sessionID),
|
parentTools: getSessionTools(parentContext.sessionID),
|
||||||
@@ -137,7 +137,7 @@ export async function executeBackgroundTask(
|
|||||||
const timing = getTimingConfig()
|
const timing = getTimingConfig()
|
||||||
let sessionId = await waitForBackgroundSessionStart({
|
let sessionId = await waitForBackgroundSessionStart({
|
||||||
taskId: task.id,
|
taskId: task.id,
|
||||||
initialSessionId: task.sessionID,
|
initialSessionId: task.sessionId,
|
||||||
manager,
|
manager,
|
||||||
timing,
|
timing,
|
||||||
abortSignal: ctx.abort,
|
abortSignal: ctx.abort,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ describe("task tool metadata awaiting", () => {
|
|||||||
prompt: "Do something",
|
prompt: "Do something",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
status: "pending",
|
status: "pending",
|
||||||
sessionID: "ses_child",
|
sessionId: "ses_child",
|
||||||
}),
|
}),
|
||||||
getTask: () => undefined,
|
getTask: () => undefined,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ describe("metadata model unification", () => {
|
|||||||
manager: {
|
manager: {
|
||||||
launch: async () => ({
|
launch: async () => ({
|
||||||
id: "bg_1", description: "test", agent: "explore",
|
id: "bg_1", description: "test", agent: "explore",
|
||||||
status: "pending", sessionID: "ses_bg", model: MODEL,
|
status: "pending", sessionId: "ses_bg", model: MODEL,
|
||||||
}),
|
}),
|
||||||
getTask: () => undefined,
|
getTask: () => undefined,
|
||||||
},
|
},
|
||||||
@@ -88,7 +88,7 @@ describe("metadata model unification", () => {
|
|||||||
|
|
||||||
const launchedTask = {
|
const launchedTask = {
|
||||||
id: "bg_unstable", description: "test", agent: "explore",
|
id: "bg_unstable", description: "test", agent: "explore",
|
||||||
status: "completed", sessionID: "ses_unstable", model: MODEL,
|
status: "completed", sessionId: "ses_unstable", model: MODEL,
|
||||||
}
|
}
|
||||||
await executeUnstableAgentTask(
|
await executeUnstableAgentTask(
|
||||||
args, ctx,
|
args, ctx,
|
||||||
@@ -130,7 +130,7 @@ describe("metadata model unification", () => {
|
|||||||
manager: {
|
manager: {
|
||||||
resume: async () => ({
|
resume: async () => ({
|
||||||
id: "bg_2", description: "continue", agent: "explore",
|
id: "bg_2", description: "continue", agent: "explore",
|
||||||
status: "running", sessionID: "ses_resumed", model: MODEL,
|
status: "running", sessionId: "ses_resumed", model: MODEL,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
} as any, parentContext)
|
} as any, parentContext)
|
||||||
@@ -210,7 +210,7 @@ describe("metadata model unification", () => {
|
|||||||
manager: {
|
manager: {
|
||||||
launch: async () => ({
|
launch: async () => ({
|
||||||
id: "bg_1", description: "test", agent: "explore",
|
id: "bg_1", description: "test", agent: "explore",
|
||||||
status: "pending", sessionID: "ses_bg",
|
status: "pending", sessionId: "ses_bg",
|
||||||
}),
|
}),
|
||||||
getTask: () => undefined,
|
getTask: () => undefined,
|
||||||
},
|
},
|
||||||
@@ -231,7 +231,7 @@ describe("metadata model unification", () => {
|
|||||||
|
|
||||||
const launchedTask = {
|
const launchedTask = {
|
||||||
id: "bg_unstable", description: "test", agent: "explore",
|
id: "bg_unstable", description: "test", agent: "explore",
|
||||||
status: "completed", sessionID: "ses_unstable",
|
status: "completed", sessionId: "ses_unstable",
|
||||||
}
|
}
|
||||||
|
|
||||||
await executeUnstableAgentTask(
|
await executeUnstableAgentTask(
|
||||||
@@ -274,7 +274,7 @@ describe("metadata model unification", () => {
|
|||||||
manager: {
|
manager: {
|
||||||
resume: async () => ({
|
resume: async () => ({
|
||||||
id: "bg_2", description: "continue", agent: "explore",
|
id: "bg_2", description: "continue", agent: "explore",
|
||||||
status: "running", sessionID: "ses_resumed",
|
status: "running", sessionId: "ses_resumed",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
} as any, parentContext)
|
} as any, parentContext)
|
||||||
@@ -385,7 +385,7 @@ describe("metadata model unification", () => {
|
|||||||
manager: {
|
manager: {
|
||||||
launch: async () => ({
|
launch: async () => ({
|
||||||
id: "bg_variant", description: "test", agent: "explore",
|
id: "bg_variant", description: "test", agent: "explore",
|
||||||
status: "pending", sessionID: "ses_bg_variant", model: MODEL_WITH_VARIANT,
|
status: "pending", sessionId: "ses_bg_variant", model: MODEL_WITH_VARIANT,
|
||||||
}),
|
}),
|
||||||
getTask: () => undefined,
|
getTask: () => undefined,
|
||||||
},
|
},
|
||||||
@@ -406,7 +406,7 @@ describe("metadata model unification", () => {
|
|||||||
|
|
||||||
const launchedTask = {
|
const launchedTask = {
|
||||||
id: "bg_unstable_variant", description: "test", agent: "explore",
|
id: "bg_unstable_variant", description: "test", agent: "explore",
|
||||||
status: "completed", sessionID: "ses_unstable_variant", model: MODEL_WITH_VARIANT,
|
status: "completed", sessionId: "ses_unstable_variant", model: MODEL_WITH_VARIANT,
|
||||||
}
|
}
|
||||||
|
|
||||||
await executeUnstableAgentTask(
|
await executeUnstableAgentTask(
|
||||||
@@ -449,7 +449,7 @@ describe("metadata model unification", () => {
|
|||||||
manager: {
|
manager: {
|
||||||
resume: async () => ({
|
resume: async () => ({
|
||||||
id: "bg_resume_variant", description: "continue", agent: "explore",
|
id: "bg_resume_variant", description: "continue", agent: "explore",
|
||||||
status: "running", sessionID: "ses_resumed_variant", model: MODEL_WITH_VARIANT,
|
status: "running", sessionId: "ses_resumed_variant", model: MODEL_WITH_VARIANT,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
} as any, parentContext)
|
} as any, parentContext)
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
|||||||
manager: {
|
manager: {
|
||||||
launch: async () => ({
|
launch: async () => ({
|
||||||
id: "bg_abc123", description: "test", agent: "explore",
|
id: "bg_abc123", description: "test", agent: "explore",
|
||||||
status: "pending", sessionID: "ses_xyz789",
|
status: "pending", sessionId: "ses_xyz789",
|
||||||
}),
|
}),
|
||||||
getTask: () => undefined,
|
getTask: () => undefined,
|
||||||
},
|
},
|
||||||
@@ -93,7 +93,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
|||||||
|
|
||||||
const launchedTask = {
|
const launchedTask = {
|
||||||
id: "bg_unstable_abc", description: "test", agent: "explore",
|
id: "bg_unstable_abc", description: "test", agent: "explore",
|
||||||
status: "completed", sessionID: "ses_unstable_xyz",
|
status: "completed", sessionId: "ses_unstable_xyz",
|
||||||
}
|
}
|
||||||
|
|
||||||
await executeUnstableAgentTask(
|
await executeUnstableAgentTask(
|
||||||
@@ -140,7 +140,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
|||||||
manager: {
|
manager: {
|
||||||
resume: async () => ({
|
resume: async () => ({
|
||||||
id: "bg_resumed_y", description: "continue", agent: "explore",
|
id: "bg_resumed_y", description: "continue", agent: "explore",
|
||||||
status: "running", sessionID: "ses_resumed_x", model: MODEL,
|
status: "running", sessionId: "ses_resumed_x", model: MODEL,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
} as any, parentContext)
|
} as any, parentContext)
|
||||||
@@ -164,7 +164,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
|||||||
manager: {
|
manager: {
|
||||||
resume: async () => ({
|
resume: async () => ({
|
||||||
id: "bg_resumed_y", description: "continue", agent: "explore",
|
id: "bg_resumed_y", description: "continue", agent: "explore",
|
||||||
status: "running", sessionID: "ses_resumed_x", model: MODEL, category: "deep",
|
status: "running", sessionId: "ses_resumed_x", model: MODEL, category: "deep",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
} as any, parentContext)
|
} as any, parentContext)
|
||||||
@@ -191,7 +191,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
|||||||
manager: {
|
manager: {
|
||||||
resume: async () => ({
|
resume: async () => ({
|
||||||
id: "bg_resumed_y", description: "continue", agent: "explore",
|
id: "bg_resumed_y", description: "continue", agent: "explore",
|
||||||
status: "running", sessionID: "ses_resumed_x", model: MODEL,
|
status: "running", sessionId: "ses_resumed_x", model: MODEL,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
} as any, parentContext)
|
} as any, parentContext)
|
||||||
@@ -372,7 +372,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
|||||||
manager: {
|
manager: {
|
||||||
launch: async () => ({
|
launch: async () => ({
|
||||||
id: "bg_abc123", description: "test", agent: "Sisyphus-Junior",
|
id: "bg_abc123", description: "test", agent: "Sisyphus-Junior",
|
||||||
status: "pending", sessionID: "ses_xyz789",
|
status: "pending", sessionId: "ses_xyz789",
|
||||||
}),
|
}),
|
||||||
getTask: () => undefined,
|
getTask: () => undefined,
|
||||||
},
|
},
|
||||||
@@ -397,7 +397,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
|||||||
|
|
||||||
const launchedTask = {
|
const launchedTask = {
|
||||||
id: "bg_unstable_abc", description: "test", agent: "Sisyphus-Junior",
|
id: "bg_unstable_abc", description: "test", agent: "Sisyphus-Junior",
|
||||||
status: "completed", sessionID: "ses_unstable_xyz",
|
status: "completed", sessionId: "ses_unstable_xyz",
|
||||||
}
|
}
|
||||||
|
|
||||||
await executeUnstableAgentTask(
|
await executeUnstableAgentTask(
|
||||||
@@ -436,7 +436,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
|||||||
const manager = {
|
const manager = {
|
||||||
getTask: (id: string) => ({
|
getTask: (id: string) => ({
|
||||||
id,
|
id,
|
||||||
sessionID: "ses_bg_session",
|
sessionId: "ses_bg_session",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
category: "deep",
|
category: "deep",
|
||||||
description: "test",
|
description: "test",
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ describe("delegate-task Oracle gap closure", () => {
|
|||||||
description: "existing",
|
description: "existing",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
status: "running",
|
status: "running",
|
||||||
sessionID: "ses_bg_category",
|
sessionId: "ses_bg_category",
|
||||||
category: "deep",
|
category: "deep",
|
||||||
model: MODEL,
|
model: MODEL,
|
||||||
}),
|
}),
|
||||||
@@ -163,7 +163,7 @@ describe("delegate-task Oracle gap closure", () => {
|
|||||||
description: "old desc",
|
description: "old desc",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
status: "running",
|
status: "running",
|
||||||
sessionID: "ses_bg_title",
|
sessionId: "ses_bg_title",
|
||||||
model: MODEL,
|
model: MODEL,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -221,7 +221,7 @@ describe("delegate-task Oracle gap closure", () => {
|
|||||||
description: "existing",
|
description: "existing",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
status: "running",
|
status: "running",
|
||||||
sessionID: "ses_bg_skills",
|
sessionId: "ses_bg_skills",
|
||||||
model: MODEL,
|
model: MODEL,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -161,8 +161,8 @@ describe("syncPollTimeoutMs threading", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mockManager = {
|
const mockManager = {
|
||||||
launch: async () => ({ id: "task_001", sessionID: "ses_unstable", status: "running" }),
|
launch: async () => ({ id: "task_001", sessionId: "ses_unstable", status: "running" }),
|
||||||
getTask: () => ({ id: "task_001", sessionID: "ses_unstable", status: "running" }),
|
getTask: () => ({ id: "task_001", sessionId: "ses_unstable", status: "running" }),
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await executeUnstableAgentTask(
|
const result = await executeUnstableAgentTask(
|
||||||
|
|||||||
@@ -578,7 +578,7 @@ describe("sisyphus-task", () => {
|
|||||||
// given a mock client with no model in config
|
// given a mock client with no model in config
|
||||||
const { createDelegateTask } = require("./tools")
|
const { createDelegateTask } = require("./tools")
|
||||||
|
|
||||||
const mockManager = { launch: async () => ({ id: "task-123", status: "pending", description: "Test task", agent: "sisyphus-junior", sessionID: "test-session" }) }
|
const mockManager = { launch: async () => ({ id: "task-123", status: "pending", description: "Test task", agent: "sisyphus-junior", sessionId: "test-session" }) }
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
app: { agents: async () => ({ data: [] }) },
|
app: { agents: async () => ({ data: [] }) },
|
||||||
config: { get: async () => ({}) }, // No model configured
|
config: { get: async () => ({}) }, // No model configured
|
||||||
@@ -687,7 +687,7 @@ describe("sisyphus-task", () => {
|
|||||||
const task = { id: "bg_1", status: "pending", description: "Test task", agent: "explore" }
|
const task = { id: "bg_1", status: "pending", description: "Test task", agent: "explore" }
|
||||||
tasks.set(task.id, task)
|
tasks.set(task.id, task)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
tasks.set(task.id, { ...task, status: "running", sessionID: "ses_child" })
|
tasks.set(task.id, { ...task, status: "running", sessionId: "ses_child" })
|
||||||
}, 20)
|
}, 20)
|
||||||
return task
|
return task
|
||||||
},
|
},
|
||||||
@@ -1363,7 +1363,7 @@ describe("sisyphus-task", () => {
|
|||||||
test("#given task_id without run_in_background #when executing #then throws required parameter error", async () => {
|
test("#given task_id without run_in_background #when executing #then throws required parameter error", async () => {
|
||||||
// given
|
// given
|
||||||
const { createDelegateTask } = require("./tools")
|
const { createDelegateTask } = require("./tools")
|
||||||
const mockManager = { resume: async () => ({ id: "task-1", sessionID: "ses_1", status: "running" }) }
|
const mockManager = { resume: async () => ({ id: "task-1", sessionId: "ses_1", status: "running" }) }
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
app: { agents: async () => ({ data: [] }) },
|
app: { agents: async () => ({ data: [] }) },
|
||||||
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
||||||
@@ -1596,7 +1596,7 @@ describe("sisyphus-task", () => {
|
|||||||
launchCalled = true
|
launchCalled = true
|
||||||
return {
|
return {
|
||||||
id: "bg_explicit_true",
|
id: "bg_explicit_true",
|
||||||
sessionID: "ses_bg_explicit_true",
|
sessionId: "ses_bg_explicit_true",
|
||||||
description: "Explicit true",
|
description: "Explicit true",
|
||||||
agent: "Sisyphus-Junior",
|
agent: "Sisyphus-Junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -1639,8 +1639,8 @@ describe("sisyphus-task", () => {
|
|||||||
const firstAbortController = new AbortController()
|
const firstAbortController = new AbortController()
|
||||||
const secondAbortController = new AbortController()
|
const secondAbortController = new AbortController()
|
||||||
const taskStates = new Map([
|
const taskStates = new Map([
|
||||||
["bg_tool_first", { reads: 0, abortOnFirstRead: true, sessionID: "ses_tool_first" }],
|
["bg_tool_first", { reads: 0, abortOnFirstRead: true, sessionId: "ses_tool_first" }],
|
||||||
["bg_tool_second", { reads: 0, abortOnFirstRead: false, sessionID: "ses_tool_second" }],
|
["bg_tool_second", { reads: 0, abortOnFirstRead: false, sessionId: "ses_tool_second" }],
|
||||||
])
|
])
|
||||||
let launchCount = 0
|
let launchCount = 0
|
||||||
const mockManager = {
|
const mockManager = {
|
||||||
@@ -1649,14 +1649,14 @@ describe("sisyphus-task", () => {
|
|||||||
return launchCount === 1
|
return launchCount === 1
|
||||||
? {
|
? {
|
||||||
id: "bg_tool_first",
|
id: "bg_tool_first",
|
||||||
sessionID: undefined,
|
sessionId: undefined,
|
||||||
description: "Tool first",
|
description: "Tool first",
|
||||||
agent: "Sisyphus-Junior",
|
agent: "Sisyphus-Junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
id: "bg_tool_second",
|
id: "bg_tool_second",
|
||||||
sessionID: undefined,
|
sessionId: undefined,
|
||||||
description: "Tool second",
|
description: "Tool second",
|
||||||
agent: "Sisyphus-Junior",
|
agent: "Sisyphus-Junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -1670,8 +1670,8 @@ describe("sisyphus-task", () => {
|
|||||||
firstAbortController.abort()
|
firstAbortController.abort()
|
||||||
}
|
}
|
||||||
return state.reads >= 2
|
return state.reads >= 2
|
||||||
? { sessionID: state.sessionID, status: "running" }
|
? { sessionId: state.sessionId, status: "running" }
|
||||||
: { sessionID: undefined, status: "pending" }
|
: { sessionId: undefined, status: "pending" }
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
@@ -1728,7 +1728,7 @@ describe("sisyphus-task", () => {
|
|||||||
|
|
||||||
const mockTask = {
|
const mockTask = {
|
||||||
id: "task-123",
|
id: "task-123",
|
||||||
sessionID: "ses_continue_test",
|
sessionId: "ses_continue_test",
|
||||||
description: "Continued task",
|
description: "Continued task",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -1890,7 +1890,7 @@ describe("sisyphus-task", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tool = createDelegateTask({
|
const tool = createDelegateTask({
|
||||||
manager: { resume: async () => ({ id: "task-var", sessionID: "ses_var_test", description: "Variant test", agent: "sisyphus-junior", status: "running" }) },
|
manager: { resume: async () => ({ id: "task-var", sessionId: "ses_var_test", description: "Variant test", agent: "sisyphus-junior", status: "running" }) },
|
||||||
client: mockClient,
|
client: mockClient,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1927,7 +1927,7 @@ describe("sisyphus-task", () => {
|
|||||||
|
|
||||||
const mockTask = {
|
const mockTask = {
|
||||||
id: "task-456",
|
id: "task-456",
|
||||||
sessionID: "ses_bg_continue",
|
sessionId: "ses_bg_continue",
|
||||||
description: "Background continued task",
|
description: "Background continued task",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -2223,7 +2223,7 @@ describe("sisyphus-task", () => {
|
|||||||
|
|
||||||
const launchedTask = {
|
const launchedTask = {
|
||||||
id: "task-unstable",
|
id: "task-unstable",
|
||||||
sessionID: "ses_unstable_gemini",
|
sessionId: "ses_unstable_gemini",
|
||||||
description: "Unstable gemini task",
|
description: "Unstable gemini task",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -2294,7 +2294,7 @@ describe("sisyphus-task", () => {
|
|||||||
launchCalled = true
|
launchCalled = true
|
||||||
return {
|
return {
|
||||||
id: "task-normal-bg",
|
id: "task-normal-bg",
|
||||||
sessionID: "ses_normal_bg",
|
sessionId: "ses_normal_bg",
|
||||||
description: "Normal background task",
|
description: "Normal background task",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -2350,7 +2350,7 @@ describe("sisyphus-task", () => {
|
|||||||
|
|
||||||
const launchedTask = {
|
const launchedTask = {
|
||||||
id: "task-unstable-minimax",
|
id: "task-unstable-minimax",
|
||||||
sessionID: "ses_unstable_minimax",
|
sessionId: "ses_unstable_minimax",
|
||||||
description: "Unstable minimax task",
|
description: "Unstable minimax task",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -2424,7 +2424,7 @@ describe("sisyphus-task", () => {
|
|||||||
const mockManager = {
|
const mockManager = {
|
||||||
launch: async () => {
|
launch: async () => {
|
||||||
launchCalled = true
|
launchCalled = true
|
||||||
return { id: "should-not-be-called", sessionID: "x", description: "x", agent: "x", status: "running" }
|
return { id: "should-not-be-called", sessionId: "x", description: "x", agent: "x", status: "running" }
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2486,7 +2486,7 @@ describe("sisyphus-task", () => {
|
|||||||
|
|
||||||
const launchedTask = {
|
const launchedTask = {
|
||||||
id: "task-artistry",
|
id: "task-artistry",
|
||||||
sessionID: "ses_artistry_gemini",
|
sessionId: "ses_artistry_gemini",
|
||||||
description: "Artistry gemini task",
|
description: "Artistry gemini task",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -2569,7 +2569,7 @@ describe("sisyphus-task", () => {
|
|||||||
const mockManager = {
|
const mockManager = {
|
||||||
launch: async () => {
|
launch: async () => {
|
||||||
launchCalled = true
|
launchCalled = true
|
||||||
return { id: "should-not-be-called", sessionID: "x", description: "x", agent: "x", status: "running" }
|
return { id: "should-not-be-called", sessionId: "x", description: "x", agent: "x", status: "running" }
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2630,7 +2630,7 @@ describe("sisyphus-task", () => {
|
|||||||
|
|
||||||
const launchedTask = {
|
const launchedTask = {
|
||||||
id: "task-custom-unstable",
|
id: "task-custom-unstable",
|
||||||
sessionID: "ses_custom_unstable",
|
sessionId: "ses_custom_unstable",
|
||||||
description: "Custom unstable task",
|
description: "Custom unstable task",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -2711,7 +2711,7 @@ describe("sisyphus-task", () => {
|
|||||||
launchInput = input
|
launchInput = input
|
||||||
return {
|
return {
|
||||||
id: "task-fallback",
|
id: "task-fallback",
|
||||||
sessionID: "ses_fallback_test",
|
sessionId: "ses_fallback_test",
|
||||||
description: "Fallback test task",
|
description: "Fallback test task",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -2775,7 +2775,7 @@ describe("sisyphus-task", () => {
|
|||||||
launchInput = input
|
launchInput = input
|
||||||
return {
|
return {
|
||||||
id: "task-ui-model",
|
id: "task-ui-model",
|
||||||
sessionID: "ses_ui_model_test",
|
sessionId: "ses_ui_model_test",
|
||||||
description: "UI model inheritance test",
|
description: "UI model inheritance test",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -2839,7 +2839,7 @@ describe("sisyphus-task", () => {
|
|||||||
launchInput = input
|
launchInput = input
|
||||||
return {
|
return {
|
||||||
id: "task-override",
|
id: "task-override",
|
||||||
sessionID: "ses_override_test",
|
sessionId: "ses_override_test",
|
||||||
description: "Override precedence test",
|
description: "Override precedence test",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -2900,7 +2900,7 @@ describe("sisyphus-task", () => {
|
|||||||
launchInput = input
|
launchInput = input
|
||||||
return {
|
return {
|
||||||
id: "task-category-precedence",
|
id: "task-category-precedence",
|
||||||
sessionID: "ses_category_precedence_test",
|
sessionId: "ses_category_precedence_test",
|
||||||
description: "Category precedence test",
|
description: "Category precedence test",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -2965,7 +2965,7 @@ describe("sisyphus-task", () => {
|
|||||||
launchInput = input
|
launchInput = input
|
||||||
return {
|
return {
|
||||||
id: "task-1295-quick",
|
id: "task-1295-quick",
|
||||||
sessionID: "ses_1295_quick",
|
sessionId: "ses_1295_quick",
|
||||||
description: "Issue 1295 regression",
|
description: "Issue 1295 regression",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -3027,7 +3027,7 @@ describe("sisyphus-task", () => {
|
|||||||
launchInput = input
|
launchInput = input
|
||||||
return {
|
return {
|
||||||
id: "task-1295-custom",
|
id: "task-1295-custom",
|
||||||
sessionID: "ses_1295_custom",
|
sessionId: "ses_1295_custom",
|
||||||
description: "Issue 1295 custom category",
|
description: "Issue 1295 custom category",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -3737,7 +3737,7 @@ describe("sisyphus-task", () => {
|
|||||||
launchInput = input
|
launchInput = input
|
||||||
return {
|
return {
|
||||||
id: "task-explore",
|
id: "task-explore",
|
||||||
sessionID: "ses_explore_model",
|
sessionId: "ses_explore_model",
|
||||||
description: "Explore task",
|
description: "Explore task",
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -4369,7 +4369,7 @@ describe("sisyphus-task", () => {
|
|||||||
const mockManager = {
|
const mockManager = {
|
||||||
launch: async () => ({
|
launch: async () => ({
|
||||||
id: "bg_meta_test",
|
id: "bg_meta_test",
|
||||||
sessionID: "ses_bg_metadata",
|
sessionId: "ses_bg_metadata",
|
||||||
description: "Background metadata test",
|
description: "Background metadata test",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ describe("executeUnstableAgentTask cleanup", () => {
|
|||||||
const cancelCalls: Array<{ taskId: string; options?: Record<string, unknown> }> = []
|
const cancelCalls: Array<{ taskId: string; options?: Record<string, unknown> }> = []
|
||||||
|
|
||||||
const mockManager = {
|
const mockManager = {
|
||||||
launch: async () => ({ id: "bg_abort_monitoring", sessionID: "ses_abort_monitoring", status: "running" }),
|
launch: async () => ({ id: "bg_abort_monitoring", sessionId: "ses_abort_monitoring", status: "running" }),
|
||||||
getTask: () => ({ id: "bg_abort_monitoring", sessionID: "ses_abort_monitoring", status: "running" }),
|
getTask: () => ({ id: "bg_abort_monitoring", sessionId: "ses_abort_monitoring", status: "running" }),
|
||||||
cancelTask: async (taskId: string, options?: Record<string, unknown>) => {
|
cancelTask: async (taskId: string, options?: Record<string, unknown>) => {
|
||||||
cancelCalls.push({ taskId, options })
|
cancelCalls.push({ taskId, options })
|
||||||
return true
|
return true
|
||||||
@@ -99,8 +99,8 @@ describe("executeUnstableAgentTask cleanup", () => {
|
|||||||
const cancelCalls: Array<{ taskId: string; options?: Record<string, unknown> }> = []
|
const cancelCalls: Array<{ taskId: string; options?: Record<string, unknown> }> = []
|
||||||
|
|
||||||
const mockManager = {
|
const mockManager = {
|
||||||
launch: async () => ({ id: "bg_timeout_cleanup", sessionID: "ses_timeout_cleanup", status: "running" }),
|
launch: async () => ({ id: "bg_timeout_cleanup", sessionId: "ses_timeout_cleanup", status: "running" }),
|
||||||
getTask: () => ({ id: "bg_timeout_cleanup", sessionID: "ses_timeout_cleanup", status: "running" }),
|
getTask: () => ({ id: "bg_timeout_cleanup", sessionId: "ses_timeout_cleanup", status: "running" }),
|
||||||
cancelTask: async (taskId: string, options?: Record<string, unknown>) => {
|
cancelTask: async (taskId: string, options?: Record<string, unknown>) => {
|
||||||
cancelCalls.push({ taskId, options })
|
cancelCalls.push({ taskId, options })
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ describe("executeUnstableAgentTask session permission", () => {
|
|||||||
launchCalls.push(input)
|
launchCalls.push(input)
|
||||||
return {
|
return {
|
||||||
id: "bg_unstable_permission",
|
id: "bg_unstable_permission",
|
||||||
sessionID: "ses_unstable_permission",
|
sessionId: "ses_unstable_permission",
|
||||||
description: "test task",
|
description: "test task",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
status: "running",
|
status: "running",
|
||||||
@@ -19,7 +19,7 @@ describe("executeUnstableAgentTask session permission", () => {
|
|||||||
},
|
},
|
||||||
getTask: () => ({
|
getTask: () => ({
|
||||||
id: "bg_unstable_permission",
|
id: "bg_unstable_permission",
|
||||||
sessionID: "ses_unstable_permission",
|
sessionId: "ses_unstable_permission",
|
||||||
status: "interrupt",
|
status: "interrupt",
|
||||||
description: "test task",
|
description: "test task",
|
||||||
agent: "sisyphus-junior",
|
agent: "sisyphus-junior",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ describe("executeUnstableAgentTask - interrupt detection", () => {
|
|||||||
//#given - a background task that gets interrupted on first poll check
|
//#given - a background task that gets interrupted on first poll check
|
||||||
const taskState = {
|
const taskState = {
|
||||||
id: "bg_test_interrupt",
|
id: "bg_test_interrupt",
|
||||||
sessionID: "ses_test_interrupt",
|
sessionId: "ses_test_interrupt",
|
||||||
status: "interrupt" as string,
|
status: "interrupt" as string,
|
||||||
description: "test interrupted task",
|
description: "test interrupted task",
|
||||||
prompt: "test prompt",
|
prompt: "test prompt",
|
||||||
@@ -42,7 +42,7 @@ describe("executeUnstableAgentTask - interrupt detection", () => {
|
|||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
session: {
|
session: {
|
||||||
status: async () => ({ data: { [taskState.sessionID!]: { type: "idle" } } }),
|
status: async () => ({ data: { [taskState.sessionId!]: { type: "idle" } } }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ describe("executeUnstableAgentTask - interrupt detection", () => {
|
|||||||
//#given - a background task that is already errored when poll checks
|
//#given - a background task that is already errored when poll checks
|
||||||
const taskState = {
|
const taskState = {
|
||||||
id: "bg_test_error",
|
id: "bg_test_error",
|
||||||
sessionID: "ses_test_error",
|
sessionId: "ses_test_error",
|
||||||
status: "error" as string,
|
status: "error" as string,
|
||||||
description: "test error task",
|
description: "test error task",
|
||||||
prompt: "test prompt",
|
prompt: "test prompt",
|
||||||
@@ -109,7 +109,7 @@ describe("executeUnstableAgentTask - interrupt detection", () => {
|
|||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
session: {
|
session: {
|
||||||
status: async () => ({ data: { [taskState.sessionID!]: { type: "idle" } } }),
|
status: async () => ({ data: { [taskState.sessionId!]: { type: "idle" } } }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -159,7 +159,7 @@ describe("executeUnstableAgentTask - interrupt detection", () => {
|
|||||||
//#given - a background task that is already cancelled when poll checks
|
//#given - a background task that is already cancelled when poll checks
|
||||||
const taskState = {
|
const taskState = {
|
||||||
id: "bg_test_cancel",
|
id: "bg_test_cancel",
|
||||||
sessionID: "ses_test_cancel",
|
sessionId: "ses_test_cancel",
|
||||||
status: "cancelled" as string,
|
status: "cancelled" as string,
|
||||||
description: "test cancelled task",
|
description: "test cancelled task",
|
||||||
prompt: "test prompt",
|
prompt: "test prompt",
|
||||||
@@ -176,7 +176,7 @@ describe("executeUnstableAgentTask - interrupt detection", () => {
|
|||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
session: {
|
session: {
|
||||||
status: async () => ({ data: { [taskState.sessionID!]: { type: "idle" } } }),
|
status: async () => ({ data: { [taskState.sessionId!]: { type: "idle" } } }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ export async function executeUnstableAgentTask(
|
|||||||
description: args.description,
|
description: args.description,
|
||||||
prompt: effectivePrompt,
|
prompt: effectivePrompt,
|
||||||
agent: agentToUse,
|
agent: agentToUse,
|
||||||
parentSessionID: parentContext.sessionID,
|
parentSessionId: parentContext.sessionID,
|
||||||
parentMessageID: parentContext.messageID,
|
parentMessageId: parentContext.messageID,
|
||||||
parentModel: parentContext.model,
|
parentModel: parentContext.model,
|
||||||
parentAgent: parentContext.agent,
|
parentAgent: parentContext.agent,
|
||||||
parentTools: getSessionTools(parentContext.sessionID),
|
parentTools: getSessionTools(parentContext.sessionID),
|
||||||
@@ -48,7 +48,7 @@ export async function executeUnstableAgentTask(
|
|||||||
|
|
||||||
const timing = getTimingConfig()
|
const timing = getTimingConfig()
|
||||||
const waitStart = Date.now()
|
const waitStart = Date.now()
|
||||||
let sessionID = task.sessionID
|
let sessionID = task.sessionId
|
||||||
while (!sessionID && Date.now() - waitStart < timing.WAIT_FOR_SESSION_TIMEOUT_MS) {
|
while (!sessionID && Date.now() - waitStart < timing.WAIT_FOR_SESSION_TIMEOUT_MS) {
|
||||||
if (ctx.abort?.aborted) {
|
if (ctx.abort?.aborted) {
|
||||||
cleanupReason = "Parent aborted while waiting for unstable task session start"
|
cleanupReason = "Parent aborted while waiting for unstable task session start"
|
||||||
@@ -56,7 +56,7 @@ export async function executeUnstableAgentTask(
|
|||||||
}
|
}
|
||||||
await new Promise(resolve => setTimeout(resolve, timing.WAIT_FOR_SESSION_INTERVAL_MS))
|
await new Promise(resolve => setTimeout(resolve, timing.WAIT_FOR_SESSION_INTERVAL_MS))
|
||||||
const updated = manager.getTask(task.id)
|
const updated = manager.getTask(task.id)
|
||||||
sessionID = updated?.sessionID
|
sessionID = updated?.sessionId
|
||||||
}
|
}
|
||||||
if (!sessionID) {
|
if (!sessionID) {
|
||||||
cleanupReason = "Unstable task session start timed out before session became available"
|
cleanupReason = "Unstable task session start timed out before session became available"
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ describe("executeUnstableAgentTask timeout handling", () => {
|
|||||||
const { executeUnstableAgentTask } = require("./unstable-agent-task")
|
const { executeUnstableAgentTask } = require("./unstable-agent-task")
|
||||||
|
|
||||||
const mockManager = {
|
const mockManager = {
|
||||||
launch: async () => ({ id: "task_001", sessionID: "ses_timeout", status: "running" }),
|
launch: async () => ({ id: "task_001", sessionId: "ses_timeout", status: "running" }),
|
||||||
getTask: () => ({ id: "task_001", sessionID: "ses_timeout", status: "running" }),
|
getTask: () => ({ id: "task_001", sessionId: "ses_timeout", status: "running" }),
|
||||||
}
|
}
|
||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
|
|||||||
Reference in New Issue
Block a user