merge(dev): resolve latest manager and runtime-fallback conflicts
Sync the PR branch with the newest dev branch and resolve the new import-level conflicts in background-agent manager and runtime-fallback tests. Preserve both the delegated bootstrap coverage from this branch and the newer upstream test utilities and runtime wiring changes, then re-verify the affected delegated fallback suites and typecheck. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -1,7 +1,25 @@
|
||||
import type { OpencodeClient } from "./types"
|
||||
import { log } from "../../shared/logger"
|
||||
import { isRecord } from "../../shared/record-type-guard"
|
||||
import { readConnectedProvidersCache, readProviderModelsCache } from "../../shared/connected-providers-cache"
|
||||
|
||||
type ModelListClient = OpencodeClient & {
|
||||
model: { list: () => Promise<unknown> }
|
||||
}
|
||||
|
||||
function hasModelList(client: OpencodeClient): client is ModelListClient {
|
||||
return "model" in client && isRecord(client.model) && typeof client.model.list === "function"
|
||||
}
|
||||
|
||||
function isModelRow(value: unknown): value is { provider: string; id: string } {
|
||||
return isRecord(value) && typeof value.provider === "string" && typeof value.id === "string"
|
||||
}
|
||||
|
||||
function extractModelRows(result: unknown): Array<{ provider: string; id: string }> {
|
||||
const rows = Array.isArray(result) ? result : isRecord(result) && Array.isArray(result.data) ? result.data : []
|
||||
return rows.filter(isModelRow)
|
||||
}
|
||||
|
||||
function addFromProviderModels(
|
||||
out: Set<string>,
|
||||
providerID: string,
|
||||
@@ -35,24 +53,17 @@ export async function getAvailableModelsForDelegateTask(client: OpencodeClient):
|
||||
return new Set()
|
||||
}
|
||||
|
||||
const modelList = (client as unknown as { model?: { list?: () => Promise<unknown> } })
|
||||
?.model
|
||||
?.list
|
||||
|
||||
if (!modelList) {
|
||||
if (!hasModelList(client)) {
|
||||
return new Set()
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await modelList()
|
||||
const rows = Array.isArray(result)
|
||||
? result
|
||||
: ((result as { data?: unknown }).data as Array<{ provider?: string; id?: string }> | undefined) ?? []
|
||||
const result = await client.model.list()
|
||||
const rows = extractModelRows(result)
|
||||
|
||||
const connected = new Set(connectedProviders)
|
||||
const out = new Set<string>()
|
||||
for (const row of rows) {
|
||||
if (!row?.provider || !row?.id) continue
|
||||
if (!connected.has(row.provider)) continue
|
||||
out.add(`${row.provider}/${row.id}`)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ const { describe, test, expect, beforeEach, afterEach, spyOn, mock } = require("
|
||||
import { resolveCategoryExecution } from "./category-resolver"
|
||||
import type { ExecutorContext } from "./executor-types"
|
||||
import * as connectedProvidersCache from "../../shared/connected-providers-cache"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("resolveCategoryExecution", () => {
|
||||
let connectedProvidersSpy: ReturnType<typeof spyOn> | undefined
|
||||
@@ -26,8 +27,8 @@ describe("resolveCategoryExecution", () => {
|
||||
})
|
||||
|
||||
const createMockExecutorContext = (): ExecutorContext => ({
|
||||
client: {} as any,
|
||||
manager: {} as any,
|
||||
client: unsafeTestValue({}),
|
||||
manager: unsafeTestValue({}),
|
||||
directory: "/tmp/test",
|
||||
userCategories: {},
|
||||
sisyphusJuniorModel: undefined,
|
||||
|
||||
@@ -2,6 +2,7 @@ const { describe, test, expect } = require("bun:test")
|
||||
|
||||
import { executeBackgroundTask } from "./executor"
|
||||
import type { DelegateTaskArgs, ToolContextWithMetadata } from "./types"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("task tool metadata awaiting", () => {
|
||||
test("executeBackgroundTask awaits ctx.metadata before returning", async () => {
|
||||
@@ -28,7 +29,7 @@ describe("task tool metadata awaiting", () => {
|
||||
subagent_type: "explore",
|
||||
}
|
||||
|
||||
const executorCtx = {
|
||||
const executorCtx = unsafeTestValue({
|
||||
manager: {
|
||||
launch: async () => ({
|
||||
id: "task_1",
|
||||
@@ -40,7 +41,7 @@ describe("task tool metadata awaiting", () => {
|
||||
}),
|
||||
getTask: () => undefined,
|
||||
},
|
||||
} as any
|
||||
})
|
||||
|
||||
const parentContext = {
|
||||
sessionID: "ses_parent",
|
||||
|
||||
@@ -2,6 +2,7 @@ const { describe, test, expect } = require("bun:test")
|
||||
|
||||
import type { DelegateTaskArgs, ToolContextWithMetadata } from "./types"
|
||||
import type { ParentContext } from "./executor-types"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
const MODEL = { providerID: "anthropic", modelID: "claude-sonnet-4-6" }
|
||||
const MODEL_WITH_VARIANT = { providerID: "google", modelID: "gemini-3.1-pro", variant: "high" }
|
||||
@@ -63,7 +64,7 @@ describe("metadata model unification", () => {
|
||||
load_skills: [], run_in_background: true, subagent_type: "explore",
|
||||
}
|
||||
|
||||
await executeBackgroundTask(args, ctx, {
|
||||
await executeBackgroundTask(args, ctx, unsafeTestValue({
|
||||
manager: {
|
||||
launch: async () => ({
|
||||
id: "bg_1", description: "test", agent: "explore",
|
||||
@@ -71,7 +72,7 @@ describe("metadata model unification", () => {
|
||||
}),
|
||||
getTask: () => undefined,
|
||||
},
|
||||
} as any, parentContext, "explore", MODEL, undefined)
|
||||
}), parentContext, "explore", MODEL, undefined)
|
||||
|
||||
const meta = ctx.captured.find((m: any) => m.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -92,7 +93,7 @@ describe("metadata model unification", () => {
|
||||
}
|
||||
await executeUnstableAgentTask(
|
||||
args, ctx,
|
||||
{
|
||||
unsafeTestValue({
|
||||
manager: {
|
||||
launch: async () => launchedTask,
|
||||
getTask: () => launchedTask,
|
||||
@@ -109,7 +110,7 @@ describe("metadata model unification", () => {
|
||||
},
|
||||
},
|
||||
syncPollTimeoutMs: 100,
|
||||
} as any,
|
||||
}),
|
||||
parentContext, "explore", MODEL, undefined, "anthropic/claude-sonnet-4-6",
|
||||
)
|
||||
|
||||
@@ -126,14 +127,14 @@ describe("metadata model unification", () => {
|
||||
load_skills: [], run_in_background: true, task_id: "ses_resumed",
|
||||
}
|
||||
|
||||
await executeBackgroundContinuation(args, ctx, {
|
||||
await executeBackgroundContinuation(args, ctx, unsafeTestValue({
|
||||
manager: {
|
||||
resume: async () => ({
|
||||
id: "bg_2", description: "continue", agent: "explore",
|
||||
status: "running", sessionId: "ses_resumed", model: MODEL,
|
||||
}),
|
||||
},
|
||||
} as any, parentContext)
|
||||
}), parentContext)
|
||||
|
||||
const meta = ctx.captured.find((m: any) => m.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -153,7 +154,7 @@ describe("metadata model unification", () => {
|
||||
fetchSyncResult: async () => ({ ok: true as const, textContent: "done" }),
|
||||
}
|
||||
|
||||
await executeSyncContinuation(args, ctx, {
|
||||
await executeSyncContinuation(args, ctx, unsafeTestValue({
|
||||
client: {
|
||||
session: {
|
||||
messages: async () => ({
|
||||
@@ -162,7 +163,7 @@ describe("metadata model unification", () => {
|
||||
prompt: async () => ({}),
|
||||
},
|
||||
},
|
||||
} as any, parentContext, deps)
|
||||
}), parentContext, deps)
|
||||
|
||||
const meta = ctx.captured.find((m: any) => m.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -206,7 +207,7 @@ describe("metadata model unification", () => {
|
||||
load_skills: [], run_in_background: true, subagent_type: "explore",
|
||||
}
|
||||
|
||||
await executeBackgroundTask(args, ctx, {
|
||||
await executeBackgroundTask(args, ctx, unsafeTestValue({
|
||||
manager: {
|
||||
launch: async () => ({
|
||||
id: "bg_1", description: "test", agent: "explore",
|
||||
@@ -214,7 +215,7 @@ describe("metadata model unification", () => {
|
||||
}),
|
||||
getTask: () => undefined,
|
||||
},
|
||||
} as any, parentContext, "explore", undefined, undefined)
|
||||
}), parentContext, "explore", undefined, undefined)
|
||||
|
||||
const meta = ctx.captured.find((m: any) => m.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -236,7 +237,7 @@ describe("metadata model unification", () => {
|
||||
|
||||
await executeUnstableAgentTask(
|
||||
args, ctx,
|
||||
{
|
||||
unsafeTestValue({
|
||||
manager: {
|
||||
launch: async () => launchedTask,
|
||||
getTask: () => launchedTask,
|
||||
@@ -253,7 +254,7 @@ describe("metadata model unification", () => {
|
||||
},
|
||||
},
|
||||
syncPollTimeoutMs: 100,
|
||||
} as any,
|
||||
}),
|
||||
parentContext, "explore", undefined, undefined, "anthropic/claude-sonnet-4-6",
|
||||
)
|
||||
|
||||
@@ -270,14 +271,14 @@ describe("metadata model unification", () => {
|
||||
load_skills: [], run_in_background: true, task_id: "ses_resumed",
|
||||
}
|
||||
|
||||
await executeBackgroundContinuation(args, ctx, {
|
||||
await executeBackgroundContinuation(args, ctx, unsafeTestValue({
|
||||
manager: {
|
||||
resume: async () => ({
|
||||
id: "bg_2", description: "continue", agent: "explore",
|
||||
status: "running", sessionId: "ses_resumed",
|
||||
}),
|
||||
},
|
||||
} as any, parentContext)
|
||||
}), parentContext)
|
||||
|
||||
const meta = ctx.captured.find((m: any) => m.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -297,14 +298,14 @@ describe("metadata model unification", () => {
|
||||
fetchSyncResult: async () => ({ ok: true as const, textContent: "done" }),
|
||||
}
|
||||
|
||||
await executeSyncContinuation(args, ctx, {
|
||||
await executeSyncContinuation(args, ctx, unsafeTestValue({
|
||||
client: {
|
||||
session: {
|
||||
messages: async () => ({ data: [] }),
|
||||
prompt: async () => ({}),
|
||||
},
|
||||
},
|
||||
} as any, parentContext, deps)
|
||||
}), parentContext, deps)
|
||||
|
||||
const meta = ctx.captured.find((m: any) => m.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -381,7 +382,7 @@ describe("metadata model unification", () => {
|
||||
category: "visual-engineering", load_skills: [], run_in_background: true, subagent_type: "explore",
|
||||
}
|
||||
|
||||
await executeBackgroundTask(args, ctx, {
|
||||
await executeBackgroundTask(args, ctx, unsafeTestValue({
|
||||
manager: {
|
||||
launch: async () => ({
|
||||
id: "bg_variant", description: "test", agent: "explore",
|
||||
@@ -389,7 +390,7 @@ describe("metadata model unification", () => {
|
||||
}),
|
||||
getTask: () => undefined,
|
||||
},
|
||||
} as any, parentContext, "explore", MODEL_WITH_VARIANT, undefined)
|
||||
}), parentContext, "explore", MODEL_WITH_VARIANT, undefined)
|
||||
|
||||
const meta = ctx.captured.find((metadataEvent: any) => metadataEvent.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -411,7 +412,7 @@ describe("metadata model unification", () => {
|
||||
|
||||
await executeUnstableAgentTask(
|
||||
args, ctx,
|
||||
{
|
||||
unsafeTestValue({
|
||||
manager: {
|
||||
launch: async () => launchedTask,
|
||||
getTask: () => launchedTask,
|
||||
@@ -428,7 +429,7 @@ describe("metadata model unification", () => {
|
||||
},
|
||||
},
|
||||
syncPollTimeoutMs: 100,
|
||||
} as any,
|
||||
}),
|
||||
parentContext, "explore", MODEL_WITH_VARIANT, undefined, "google/gemini-3.1-pro high",
|
||||
)
|
||||
|
||||
@@ -445,14 +446,14 @@ describe("metadata model unification", () => {
|
||||
load_skills: [], run_in_background: true, task_id: "ses_resumed_variant",
|
||||
}
|
||||
|
||||
await executeBackgroundContinuation(args, ctx, {
|
||||
await executeBackgroundContinuation(args, ctx, unsafeTestValue({
|
||||
manager: {
|
||||
resume: async () => ({
|
||||
id: "bg_resume_variant", description: "continue", agent: "explore",
|
||||
status: "running", sessionId: "ses_resumed_variant", model: MODEL_WITH_VARIANT,
|
||||
}),
|
||||
},
|
||||
} as any, parentContext)
|
||||
}), parentContext)
|
||||
|
||||
const meta = ctx.captured.find((metadataEvent: any) => metadataEvent.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -472,7 +473,7 @@ describe("metadata model unification", () => {
|
||||
fetchSyncResult: async () => ({ ok: true as const, textContent: "done" }),
|
||||
}
|
||||
|
||||
await executeSyncContinuation(args, ctx, {
|
||||
await executeSyncContinuation(args, ctx, unsafeTestValue({
|
||||
client: {
|
||||
session: {
|
||||
messages: async () => ({
|
||||
@@ -481,7 +482,7 @@ describe("metadata model unification", () => {
|
||||
prompt: async () => ({}),
|
||||
},
|
||||
},
|
||||
} as any, parentContext, deps)
|
||||
}), parentContext, deps)
|
||||
|
||||
const meta = ctx.captured.find((metadataEvent: any) => metadataEvent.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
|
||||
@@ -2,6 +2,7 @@ const { describe, test, expect } = require("bun:test")
|
||||
|
||||
import type { DelegateTaskArgs, ToolContextWithMetadata } from "./types"
|
||||
import type { ParentContext } from "./executor-types"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
const MODEL = { providerID: "anthropic", modelID: "claude-sonnet-4-6" }
|
||||
|
||||
@@ -64,7 +65,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
load_skills: [], run_in_background: true, subagent_type: "explore",
|
||||
}
|
||||
|
||||
await executeBackgroundTask(args, ctx, {
|
||||
await executeBackgroundTask(args, ctx, unsafeTestValue({
|
||||
manager: {
|
||||
launch: async () => ({
|
||||
id: "bg_abc123", description: "test", agent: "explore",
|
||||
@@ -72,7 +73,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
}),
|
||||
getTask: () => undefined,
|
||||
},
|
||||
} as any, parentContext, "explore", MODEL, undefined)
|
||||
}), parentContext, "explore", MODEL, undefined)
|
||||
|
||||
const meta = ctx.captured.find((m: any) => m.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -98,7 +99,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
|
||||
await executeUnstableAgentTask(
|
||||
args, ctx,
|
||||
{
|
||||
unsafeTestValue({
|
||||
manager: {
|
||||
launch: async () => launchedTask,
|
||||
getTask: () => launchedTask,
|
||||
@@ -115,7 +116,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
},
|
||||
},
|
||||
syncPollTimeoutMs: 100,
|
||||
} as any,
|
||||
}),
|
||||
parentContext, "explore", MODEL, undefined, "anthropic/claude-sonnet-4-6",
|
||||
)
|
||||
|
||||
@@ -136,14 +137,14 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
load_skills: [], run_in_background: true, task_id: "ses_resumed_x",
|
||||
}
|
||||
|
||||
await executeBackgroundContinuation(args, ctx, {
|
||||
await executeBackgroundContinuation(args, ctx, unsafeTestValue({
|
||||
manager: {
|
||||
resume: async () => ({
|
||||
id: "bg_resumed_y", description: "continue", agent: "explore",
|
||||
status: "running", sessionId: "ses_resumed_x", model: MODEL,
|
||||
}),
|
||||
},
|
||||
} as any, parentContext)
|
||||
}), parentContext)
|
||||
|
||||
const meta = ctx.captured.find((m: any) => m.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -160,14 +161,14 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
load_skills: [], run_in_background: true, task_id: "ses_resumed_x",
|
||||
}
|
||||
|
||||
await executeBackgroundContinuation(args, ctx, {
|
||||
await executeBackgroundContinuation(args, ctx, unsafeTestValue({
|
||||
manager: {
|
||||
resume: async () => ({
|
||||
id: "bg_resumed_y", description: "continue", agent: "explore",
|
||||
status: "running", sessionId: "ses_resumed_x", model: MODEL, category: "deep",
|
||||
}),
|
||||
},
|
||||
} as any, parentContext)
|
||||
}), parentContext)
|
||||
|
||||
const meta = ctx.captured.find((item: any) => item.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -187,14 +188,14 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
task_id: "ses_resumed_x",
|
||||
}
|
||||
|
||||
await executeBackgroundContinuation(args, ctx, {
|
||||
await executeBackgroundContinuation(args, ctx, unsafeTestValue({
|
||||
manager: {
|
||||
resume: async () => ({
|
||||
id: "bg_resumed_y", description: "continue", agent: "explore",
|
||||
status: "running", sessionId: "ses_resumed_x", model: MODEL,
|
||||
}),
|
||||
},
|
||||
} as any, parentContext)
|
||||
}), parentContext)
|
||||
|
||||
const meta = ctx.captured.find((item: any) => item.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -216,7 +217,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
fetchSyncResult: async () => ({ ok: true as const, textContent: "done" }),
|
||||
}
|
||||
|
||||
await executeSyncContinuation(args, ctx, {
|
||||
await executeSyncContinuation(args, ctx, unsafeTestValue({
|
||||
client: {
|
||||
session: {
|
||||
messages: async () => ({
|
||||
@@ -225,7 +226,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
prompt: async () => ({}),
|
||||
},
|
||||
},
|
||||
} as any, parentContext, deps)
|
||||
}), parentContext, deps)
|
||||
|
||||
const meta = ctx.captured.find((m: any) => m.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -246,7 +247,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
fetchSyncResult: async () => ({ ok: true as const, textContent: "done" }),
|
||||
}
|
||||
|
||||
await executeSyncContinuation(args, ctx, {
|
||||
await executeSyncContinuation(args, ctx, unsafeTestValue({
|
||||
client: {
|
||||
session: {
|
||||
messages: async () => ({
|
||||
@@ -255,7 +256,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
prompt: async () => ({}),
|
||||
},
|
||||
},
|
||||
} as any, parentContext, deps)
|
||||
}), parentContext, deps)
|
||||
|
||||
const meta = ctx.captured.find((item: any) => item.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -275,7 +276,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
fetchSyncResult: async () => ({ ok: true as const, textContent: "done" }),
|
||||
}
|
||||
|
||||
await executeSyncContinuation(args, ctx, {
|
||||
await executeSyncContinuation(args, ctx, unsafeTestValue({
|
||||
client: {
|
||||
session: {
|
||||
messages: async () => ({
|
||||
@@ -284,7 +285,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
prompt: async () => ({}),
|
||||
},
|
||||
},
|
||||
} as any, parentContext, deps)
|
||||
}), parentContext, deps)
|
||||
|
||||
const meta = ctx.captured.find((item: any) => item.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -309,7 +310,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
fetchSyncResult: async () => ({ ok: true as const, textContent: "done" }),
|
||||
}
|
||||
|
||||
await executeSyncContinuation(args, ctx, {
|
||||
await executeSyncContinuation(args, ctx, unsafeTestValue({
|
||||
client: {
|
||||
session: {
|
||||
messages: async () => ({
|
||||
@@ -318,7 +319,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
prompt: async () => ({}),
|
||||
},
|
||||
},
|
||||
} as any, parentContext, deps)
|
||||
}), parentContext, deps)
|
||||
|
||||
const meta = ctx.captured.find((item: any) => item.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -368,7 +369,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
run_in_background: true,
|
||||
}
|
||||
|
||||
await executeBackgroundTask(args, ctx, {
|
||||
await executeBackgroundTask(args, ctx, unsafeTestValue({
|
||||
manager: {
|
||||
launch: async () => ({
|
||||
id: "bg_abc123", description: "test", agent: "Sisyphus-Junior",
|
||||
@@ -376,7 +377,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
}),
|
||||
getTask: () => undefined,
|
||||
},
|
||||
} as any, parentContext, "Sisyphus-Junior", MODEL, undefined)
|
||||
}), parentContext, "Sisyphus-Junior", MODEL, undefined)
|
||||
|
||||
const meta = ctx.captured.find((item: any) => item.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -402,7 +403,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
|
||||
await executeUnstableAgentTask(
|
||||
args, ctx,
|
||||
{
|
||||
unsafeTestValue({
|
||||
manager: {
|
||||
launch: async () => launchedTask,
|
||||
getTask: () => launchedTask,
|
||||
@@ -419,7 +420,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
},
|
||||
},
|
||||
syncPollTimeoutMs: 100,
|
||||
} as any,
|
||||
}),
|
||||
parentContext, "Sisyphus-Junior", MODEL, undefined, "anthropic/claude-sonnet-4-6",
|
||||
)
|
||||
|
||||
@@ -438,14 +439,14 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
load_skills: [], run_in_background: true, task_id: "ses_resume_title",
|
||||
}
|
||||
|
||||
await executeBackgroundContinuation(args, ctx, {
|
||||
await executeBackgroundContinuation(args, ctx, unsafeTestValue({
|
||||
manager: {
|
||||
resume: async () => ({
|
||||
id: "bg_resume_title", description: "continue work", agent: "explore",
|
||||
status: "running", sessionId: "ses_resume_title", model: MODEL,
|
||||
}),
|
||||
},
|
||||
} as any, parentContext)
|
||||
}), parentContext)
|
||||
|
||||
const meta = ctx.captured.find((item: any) => item.metadata?.sessionId)
|
||||
expect(meta).toBeDefined()
|
||||
@@ -460,7 +461,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
load_skills: [], run_in_background: false, task_id: "ses_sync_title",
|
||||
}
|
||||
|
||||
await executeSyncContinuation(args, ctx, {
|
||||
await executeSyncContinuation(args, ctx, unsafeTestValue({
|
||||
client: {
|
||||
session: {
|
||||
messages: async () => ({
|
||||
@@ -469,7 +470,7 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
prompt: async () => ({}),
|
||||
},
|
||||
},
|
||||
} as any, parentContext, {
|
||||
}), parentContext, {
|
||||
pollSyncSession: async () => null,
|
||||
fetchSyncResult: async () => ({ ok: true as const, textContent: "done" }),
|
||||
})
|
||||
@@ -500,8 +501,8 @@ describe("taskId and backgroundTaskId metadata consistency", () => {
|
||||
},
|
||||
}
|
||||
|
||||
const bgOutput = createBackgroundOutput(manager as any, client as any)
|
||||
await bgOutput.execute({ task_id: "bg_output_xyz" } as any, ctx as any)
|
||||
const bgOutput = createBackgroundOutput(unsafeTestValue(manager), unsafeTestValue(client))
|
||||
await bgOutput.execute(unsafeTestValue({ task_id: "bg_output_xyz" }), unsafeTestValue(ctx))
|
||||
|
||||
const meta = ctx.captured.find((m: any) => m.metadata?.backgroundTaskId)
|
||||
expect(meta).toBeDefined()
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import { describe, expect, mock, test } from "bun:test"
|
||||
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
import type { OpencodeClient } from "./types"
|
||||
import { sendSyncPrompt } from "./sync-prompt-sender"
|
||||
import {
|
||||
promptSyncWithModelSuggestionRetry,
|
||||
promptWithModelSuggestionRetry,
|
||||
} from "../../shared/model-suggestion-retry"
|
||||
|
||||
type PromptRetryClient = Parameters<typeof promptWithModelSuggestionRetry>[0]
|
||||
type PromptRetryArgs = Parameters<typeof promptWithModelSuggestionRetry>[1]
|
||||
type PromptSyncRetryClient = Parameters<typeof promptSyncWithModelSuggestionRetry>[0]
|
||||
type PromptSyncRetryArgs = Parameters<typeof promptSyncWithModelSuggestionRetry>[1]
|
||||
|
||||
describe("sendSyncPrompt session routing", () => {
|
||||
test("#given a sync child session directory #when sending the prompt #then promptAsync uses that OpenCode directory route", async () => {
|
||||
// given
|
||||
const promptCalls: PromptRetryArgs[] = []
|
||||
const promptWithRetry = mock(async (_client: PromptRetryClient, input: PromptRetryArgs) => {
|
||||
promptCalls.push(input)
|
||||
})
|
||||
|
||||
// when
|
||||
await sendSyncPrompt(
|
||||
unsafeTestValue<OpencodeClient>({ session: {} }),
|
||||
{
|
||||
sessionID: "ses_child",
|
||||
agentToUse: "sisyphus-junior",
|
||||
args: {
|
||||
description: "test task",
|
||||
prompt: "test prompt",
|
||||
run_in_background: false,
|
||||
load_skills: [],
|
||||
},
|
||||
systemContent: undefined,
|
||||
categoryModel: undefined,
|
||||
directory: "/parent/project",
|
||||
toastManager: null,
|
||||
taskId: undefined,
|
||||
},
|
||||
{
|
||||
promptWithModelSuggestionRetry: promptWithRetry,
|
||||
promptSyncWithModelSuggestionRetry: mock(async () => {}),
|
||||
},
|
||||
)
|
||||
|
||||
// then
|
||||
expect(promptCalls).toHaveLength(1)
|
||||
expect(promptCalls[0]?.query).toEqual({ directory: "/parent/project" })
|
||||
})
|
||||
|
||||
test("#given oracle falls back to promptSync #when async prompt returns unexpected EOF #then the sync retry keeps the same directory route", async () => {
|
||||
// given
|
||||
const promptSyncCalls: PromptSyncRetryArgs[] = []
|
||||
const promptWithRetry = mock(async () => {
|
||||
throw new Error("JSON Parse error: Unexpected EOF")
|
||||
})
|
||||
const promptSyncWithRetry = mock(async (_client: PromptSyncRetryClient, input: PromptSyncRetryArgs) => {
|
||||
promptSyncCalls.push(input)
|
||||
})
|
||||
|
||||
// when
|
||||
const result = await sendSyncPrompt(
|
||||
unsafeTestValue<OpencodeClient>({ session: {} }),
|
||||
{
|
||||
sessionID: "ses_child",
|
||||
agentToUse: "oracle",
|
||||
args: {
|
||||
description: "test task",
|
||||
prompt: "test prompt",
|
||||
run_in_background: false,
|
||||
load_skills: [],
|
||||
},
|
||||
systemContent: undefined,
|
||||
categoryModel: undefined,
|
||||
directory: "/parent/project",
|
||||
toastManager: null,
|
||||
taskId: undefined,
|
||||
},
|
||||
{
|
||||
promptWithModelSuggestionRetry: promptWithRetry,
|
||||
promptSyncWithModelSuggestionRetry: promptSyncWithRetry,
|
||||
},
|
||||
)
|
||||
|
||||
// then
|
||||
expect(result).toBeNull()
|
||||
expect(promptSyncCalls).toHaveLength(1)
|
||||
expect(promptSyncCalls[0]?.query).toEqual({ directory: "/parent/project" })
|
||||
})
|
||||
})
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
promptSyncWithModelSuggestionRetry,
|
||||
promptWithModelSuggestionRetry,
|
||||
} from "../../shared/model-suggestion-retry"
|
||||
import { routePromptRetry, routePromptSyncRetry } from "../../shared/session-route"
|
||||
import { formatDetailedError } from "./error-formatting"
|
||||
import { getAgentToolRestrictions } from "../../shared/agent-tool-restrictions"
|
||||
import { stripInvisibleAgentCharacters } from "../../shared/agent-display-names"
|
||||
@@ -60,6 +61,7 @@ export async function sendSyncPrompt(
|
||||
promptText?: string
|
||||
systemContent: string | undefined
|
||||
categoryModel: DelegatedModelConfig | undefined
|
||||
directory: string
|
||||
toastManager: { removeTask: (id: string) => void } | null | undefined
|
||||
taskId: string | undefined
|
||||
sisyphusAgentConfig?: SisyphusAgentConfig
|
||||
@@ -100,11 +102,12 @@ export async function sendSyncPrompt(
|
||||
}
|
||||
|
||||
try {
|
||||
await deps.promptWithModelSuggestionRetry(client, promptArgs)
|
||||
const routedPromptArgs = routePromptRetry(promptArgs, input.directory)
|
||||
await deps.promptWithModelSuggestionRetry(client, routedPromptArgs)
|
||||
} catch (promptError) {
|
||||
if (isOracleAgent(input.agentToUse) && isUnexpectedEofError(promptError)) {
|
||||
try {
|
||||
await deps.promptSyncWithModelSuggestionRetry(client, promptArgs)
|
||||
await deps.promptSyncWithModelSuggestionRetry(client, routePromptSyncRetry(promptArgs, input.directory))
|
||||
return null
|
||||
} catch (oracleRetryError) {
|
||||
promptError = oracleRetryError
|
||||
|
||||
@@ -188,6 +188,7 @@ export async function executeSyncTask(
|
||||
args,
|
||||
promptText: delegatedPromptText,
|
||||
systemContent,
|
||||
directory: createSessionResult.parentDirectory,
|
||||
toastManager,
|
||||
taskId,
|
||||
sisyphusAgentConfig: executorCtx.sisyphusAgentConfig,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
const { describe, expect, test } = require("bun:test")
|
||||
|
||||
function requireFresh<T>(modulePath: string): T {
|
||||
@@ -18,14 +19,14 @@ function createDelegateTask(...args: Parameters<typeof import("./tools").createD
|
||||
const toolDefinition = createDelegateTask({ manager: {} as never, client: {} as never, directory: "/tmp/test" })
|
||||
|
||||
//#when
|
||||
const categorySchema = toolDefinition.args.category as unknown as {
|
||||
const categorySchema = unsafeTestValue<{
|
||||
def: {
|
||||
type: string
|
||||
innerType: {
|
||||
def: { type: string }
|
||||
}
|
||||
}
|
||||
}
|
||||
}>(toolDefinition.args.category)
|
||||
|
||||
//#then
|
||||
expect(categorySchema.def.type).toBe("optional")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
|
||||
import { executeUnstableAgentTask } from "./unstable-agent-task"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("executeUnstableAgentTask session permission", () => {
|
||||
test("passes question-deny session permission into background launch", async () => {
|
||||
@@ -33,7 +34,7 @@ describe("executeUnstableAgentTask session permission", () => {
|
||||
metadata: () => {},
|
||||
abort: new AbortController().signal,
|
||||
} satisfies Parameters<typeof executeUnstableAgentTask>[1]
|
||||
const executorContext = {
|
||||
const executorContext = unsafeTestValue<Parameters<typeof executeUnstableAgentTask>[2]>({
|
||||
manager: mockManager,
|
||||
client: {
|
||||
session: {
|
||||
@@ -41,7 +42,7 @@ describe("executeUnstableAgentTask session permission", () => {
|
||||
messages: async () => ({ data: [] }),
|
||||
},
|
||||
},
|
||||
} as unknown as Parameters<typeof executeUnstableAgentTask>[2]
|
||||
})
|
||||
const parentContext = {
|
||||
sessionID: "parent-session",
|
||||
messageID: "msg_parent",
|
||||
|
||||
Reference in New Issue
Block a user