Merge pull request #1653 from code-yeongyu/fix/plan-prometheus-decoupling
fix(delegation): decouple plan from prometheus and fix sync task responses
This commit is contained in:
commit
24a013b867
@ -600,6 +600,187 @@ describe("Prometheus direct override priority over category", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("Plan agent model inheritance from prometheus", () => {
|
||||||
|
test("plan agent inherits all model-related settings from resolved prometheus config", async () => {
|
||||||
|
//#given - prometheus resolves to claude-opus-4-6 with model settings
|
||||||
|
spyOn(shared, "resolveModelPipeline" as any).mockReturnValue({
|
||||||
|
model: "anthropic/claude-opus-4-6",
|
||||||
|
provenance: "provider-fallback",
|
||||||
|
variant: "max",
|
||||||
|
})
|
||||||
|
const pluginConfig: OhMyOpenCodeConfig = {
|
||||||
|
sisyphus_agent: {
|
||||||
|
planner_enabled: true,
|
||||||
|
replace_plan: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const config: Record<string, unknown> = {
|
||||||
|
model: "anthropic/claude-opus-4-6",
|
||||||
|
agent: {
|
||||||
|
plan: {
|
||||||
|
name: "plan",
|
||||||
|
mode: "primary",
|
||||||
|
prompt: "original plan prompt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const handler = createConfigHandler({
|
||||||
|
ctx: { directory: "/tmp" },
|
||||||
|
pluginConfig,
|
||||||
|
modelCacheState: {
|
||||||
|
anthropicContext1MEnabled: false,
|
||||||
|
modelContextLimitsCache: new Map(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
//#when
|
||||||
|
await handler(config)
|
||||||
|
|
||||||
|
//#then - plan inherits model and variant from prometheus, but NOT prompt
|
||||||
|
const agents = config.agent as Record<string, { mode?: string; model?: string; variant?: string; prompt?: string }>
|
||||||
|
expect(agents.plan).toBeDefined()
|
||||||
|
expect(agents.plan.mode).toBe("subagent")
|
||||||
|
expect(agents.plan.model).toBe("anthropic/claude-opus-4-6")
|
||||||
|
expect(agents.plan.variant).toBe("max")
|
||||||
|
expect(agents.plan.prompt).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("plan agent inherits temperature, reasoningEffort, and other model settings from prometheus", async () => {
|
||||||
|
//#given - prometheus configured with category that has temperature and reasoningEffort
|
||||||
|
spyOn(shared, "resolveModelPipeline" as any).mockReturnValue({
|
||||||
|
model: "openai/gpt-5.2",
|
||||||
|
provenance: "override",
|
||||||
|
variant: "high",
|
||||||
|
})
|
||||||
|
const pluginConfig: OhMyOpenCodeConfig = {
|
||||||
|
sisyphus_agent: {
|
||||||
|
planner_enabled: true,
|
||||||
|
replace_plan: true,
|
||||||
|
},
|
||||||
|
agents: {
|
||||||
|
prometheus: {
|
||||||
|
model: "openai/gpt-5.2",
|
||||||
|
variant: "high",
|
||||||
|
temperature: 0.3,
|
||||||
|
top_p: 0.9,
|
||||||
|
maxTokens: 16000,
|
||||||
|
reasoningEffort: "high",
|
||||||
|
textVerbosity: "medium",
|
||||||
|
thinking: { type: "enabled", budgetTokens: 8000 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const config: Record<string, unknown> = {
|
||||||
|
model: "anthropic/claude-opus-4-6",
|
||||||
|
agent: {},
|
||||||
|
}
|
||||||
|
const handler = createConfigHandler({
|
||||||
|
ctx: { directory: "/tmp" },
|
||||||
|
pluginConfig,
|
||||||
|
modelCacheState: {
|
||||||
|
anthropicContext1MEnabled: false,
|
||||||
|
modelContextLimitsCache: new Map(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
//#when
|
||||||
|
await handler(config)
|
||||||
|
|
||||||
|
//#then - plan inherits ALL model-related settings from resolved prometheus
|
||||||
|
const agents = config.agent as Record<string, Record<string, unknown>>
|
||||||
|
expect(agents.plan).toBeDefined()
|
||||||
|
expect(agents.plan.mode).toBe("subagent")
|
||||||
|
expect(agents.plan.model).toBe("openai/gpt-5.2")
|
||||||
|
expect(agents.plan.variant).toBe("high")
|
||||||
|
expect(agents.plan.temperature).toBe(0.3)
|
||||||
|
expect(agents.plan.top_p).toBe(0.9)
|
||||||
|
expect(agents.plan.maxTokens).toBe(16000)
|
||||||
|
expect(agents.plan.reasoningEffort).toBe("high")
|
||||||
|
expect(agents.plan.textVerbosity).toBe("medium")
|
||||||
|
expect(agents.plan.thinking).toEqual({ type: "enabled", budgetTokens: 8000 })
|
||||||
|
})
|
||||||
|
|
||||||
|
test("plan agent user override takes priority over prometheus inherited settings", async () => {
|
||||||
|
//#given - prometheus resolves to opus, but user has plan override for gpt-5.2
|
||||||
|
spyOn(shared, "resolveModelPipeline" as any).mockReturnValue({
|
||||||
|
model: "anthropic/claude-opus-4-6",
|
||||||
|
provenance: "provider-fallback",
|
||||||
|
variant: "max",
|
||||||
|
})
|
||||||
|
const pluginConfig: OhMyOpenCodeConfig = {
|
||||||
|
sisyphus_agent: {
|
||||||
|
planner_enabled: true,
|
||||||
|
replace_plan: true,
|
||||||
|
},
|
||||||
|
agents: {
|
||||||
|
plan: {
|
||||||
|
model: "openai/gpt-5.2",
|
||||||
|
variant: "high",
|
||||||
|
temperature: 0.5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const config: Record<string, unknown> = {
|
||||||
|
model: "anthropic/claude-opus-4-6",
|
||||||
|
agent: {},
|
||||||
|
}
|
||||||
|
const handler = createConfigHandler({
|
||||||
|
ctx: { directory: "/tmp" },
|
||||||
|
pluginConfig,
|
||||||
|
modelCacheState: {
|
||||||
|
anthropicContext1MEnabled: false,
|
||||||
|
modelContextLimitsCache: new Map(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
//#when
|
||||||
|
await handler(config)
|
||||||
|
|
||||||
|
//#then - plan uses its own override, not prometheus settings
|
||||||
|
const agents = config.agent as Record<string, Record<string, unknown>>
|
||||||
|
expect(agents.plan.model).toBe("openai/gpt-5.2")
|
||||||
|
expect(agents.plan.variant).toBe("high")
|
||||||
|
expect(agents.plan.temperature).toBe(0.5)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("plan agent does NOT inherit prompt, description, or color from prometheus", async () => {
|
||||||
|
//#given
|
||||||
|
spyOn(shared, "resolveModelPipeline" as any).mockReturnValue({
|
||||||
|
model: "anthropic/claude-opus-4-6",
|
||||||
|
provenance: "provider-fallback",
|
||||||
|
variant: "max",
|
||||||
|
})
|
||||||
|
const pluginConfig: OhMyOpenCodeConfig = {
|
||||||
|
sisyphus_agent: {
|
||||||
|
planner_enabled: true,
|
||||||
|
replace_plan: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const config: Record<string, unknown> = {
|
||||||
|
model: "anthropic/claude-opus-4-6",
|
||||||
|
agent: {},
|
||||||
|
}
|
||||||
|
const handler = createConfigHandler({
|
||||||
|
ctx: { directory: "/tmp" },
|
||||||
|
pluginConfig,
|
||||||
|
modelCacheState: {
|
||||||
|
anthropicContext1MEnabled: false,
|
||||||
|
modelContextLimitsCache: new Map(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
//#when
|
||||||
|
await handler(config)
|
||||||
|
|
||||||
|
//#then - plan has model settings but NOT prompt/description/color
|
||||||
|
const agents = config.agent as Record<string, Record<string, unknown>>
|
||||||
|
expect(agents.plan.model).toBe("anthropic/claude-opus-4-6")
|
||||||
|
expect(agents.plan.prompt).toBeUndefined()
|
||||||
|
expect(agents.plan.description).toBeUndefined()
|
||||||
|
expect(agents.plan.color).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("Deadlock prevention - fetchAvailableModels must not receive client", () => {
|
describe("Deadlock prevention - fetchAvailableModels must not receive client", () => {
|
||||||
test("fetchAvailableModels should be called with undefined client to prevent deadlock during plugin init", async () => {
|
test("fetchAvailableModels should be called with undefined client to prevent deadlock during plugin init", async () => {
|
||||||
// given - This test ensures we don't regress on issue #1301
|
// given - This test ensures we don't regress on issue #1301
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import { AGENT_NAME_MAP } from "../shared/migration";
|
|||||||
import { AGENT_MODEL_REQUIREMENTS } from "../shared/model-requirements";
|
import { AGENT_MODEL_REQUIREMENTS } from "../shared/model-requirements";
|
||||||
import { PROMETHEUS_SYSTEM_PROMPT, PROMETHEUS_PERMISSION } from "../agents/prometheus";
|
import { PROMETHEUS_SYSTEM_PROMPT, PROMETHEUS_PERMISSION } from "../agents/prometheus";
|
||||||
import { DEFAULT_CATEGORIES } from "../tools/delegate-task/constants";
|
import { DEFAULT_CATEGORIES } from "../tools/delegate-task/constants";
|
||||||
|
import { buildPlanDemoteConfig } from "./plan-model-inheritance";
|
||||||
import type { ModelCacheState } from "../plugin-state";
|
import type { ModelCacheState } from "../plugin-state";
|
||||||
import type { CategoryConfig } from "../config/schema";
|
import type { CategoryConfig } from "../config/schema";
|
||||||
|
|
||||||
@ -385,8 +386,10 @@ export function createConfigHandler(deps: ConfigHandlerDeps) {
|
|||||||
: {};
|
: {};
|
||||||
|
|
||||||
const planDemoteConfig = shouldDemotePlan
|
const planDemoteConfig = shouldDemotePlan
|
||||||
? { mode: "subagent" as const
|
? buildPlanDemoteConfig(
|
||||||
}
|
agentConfig["prometheus"] as Record<string, unknown> | undefined,
|
||||||
|
pluginConfig.agents?.plan as Record<string, unknown> | undefined,
|
||||||
|
)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
config.agent = {
|
config.agent = {
|
||||||
|
|||||||
118
src/plugin-handlers/plan-model-inheritance.test.ts
Normal file
118
src/plugin-handlers/plan-model-inheritance.test.ts
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import { describe, test, expect } from "bun:test"
|
||||||
|
import { buildPlanDemoteConfig } from "./plan-model-inheritance"
|
||||||
|
|
||||||
|
describe("buildPlanDemoteConfig", () => {
|
||||||
|
test("returns only mode when prometheus and plan override are both undefined", () => {
|
||||||
|
//#given
|
||||||
|
const prometheusConfig = undefined
|
||||||
|
const planOverride = undefined
|
||||||
|
|
||||||
|
//#when
|
||||||
|
const result = buildPlanDemoteConfig(prometheusConfig, planOverride)
|
||||||
|
|
||||||
|
//#then
|
||||||
|
expect(result).toEqual({ mode: "subagent" })
|
||||||
|
})
|
||||||
|
|
||||||
|
test("extracts all model settings from prometheus config", () => {
|
||||||
|
//#given
|
||||||
|
const prometheusConfig = {
|
||||||
|
name: "prometheus",
|
||||||
|
model: "anthropic/claude-opus-4-6",
|
||||||
|
variant: "max",
|
||||||
|
mode: "all",
|
||||||
|
prompt: "You are Prometheus...",
|
||||||
|
permission: { edit: "allow" },
|
||||||
|
description: "Plan agent (Prometheus)",
|
||||||
|
color: "#FF5722",
|
||||||
|
temperature: 0.1,
|
||||||
|
top_p: 0.95,
|
||||||
|
maxTokens: 32000,
|
||||||
|
thinking: { type: "enabled", budgetTokens: 10000 },
|
||||||
|
reasoningEffort: "high",
|
||||||
|
textVerbosity: "medium",
|
||||||
|
providerOptions: { key: "value" },
|
||||||
|
}
|
||||||
|
|
||||||
|
//#when
|
||||||
|
const result = buildPlanDemoteConfig(prometheusConfig, undefined)
|
||||||
|
|
||||||
|
//#then - picks model settings, NOT prompt/permission/description/color/name/mode
|
||||||
|
expect(result.mode).toBe("subagent")
|
||||||
|
expect(result.model).toBe("anthropic/claude-opus-4-6")
|
||||||
|
expect(result.variant).toBe("max")
|
||||||
|
expect(result.temperature).toBe(0.1)
|
||||||
|
expect(result.top_p).toBe(0.95)
|
||||||
|
expect(result.maxTokens).toBe(32000)
|
||||||
|
expect(result.thinking).toEqual({ type: "enabled", budgetTokens: 10000 })
|
||||||
|
expect(result.reasoningEffort).toBe("high")
|
||||||
|
expect(result.textVerbosity).toBe("medium")
|
||||||
|
expect(result.providerOptions).toEqual({ key: "value" })
|
||||||
|
expect(result.prompt).toBeUndefined()
|
||||||
|
expect(result.permission).toBeUndefined()
|
||||||
|
expect(result.description).toBeUndefined()
|
||||||
|
expect(result.color).toBeUndefined()
|
||||||
|
expect(result.name).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("plan override takes priority over prometheus for all model settings", () => {
|
||||||
|
//#given
|
||||||
|
const prometheusConfig = {
|
||||||
|
model: "anthropic/claude-opus-4-6",
|
||||||
|
variant: "max",
|
||||||
|
temperature: 0.1,
|
||||||
|
reasoningEffort: "high",
|
||||||
|
}
|
||||||
|
const planOverride = {
|
||||||
|
model: "openai/gpt-5.2",
|
||||||
|
variant: "high",
|
||||||
|
temperature: 0.5,
|
||||||
|
reasoningEffort: "low",
|
||||||
|
}
|
||||||
|
|
||||||
|
//#when
|
||||||
|
const result = buildPlanDemoteConfig(prometheusConfig, planOverride)
|
||||||
|
|
||||||
|
//#then
|
||||||
|
expect(result.model).toBe("openai/gpt-5.2")
|
||||||
|
expect(result.variant).toBe("high")
|
||||||
|
expect(result.temperature).toBe(0.5)
|
||||||
|
expect(result.reasoningEffort).toBe("low")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("falls back to prometheus when plan override has partial settings", () => {
|
||||||
|
//#given
|
||||||
|
const prometheusConfig = {
|
||||||
|
model: "anthropic/claude-opus-4-6",
|
||||||
|
variant: "max",
|
||||||
|
temperature: 0.1,
|
||||||
|
reasoningEffort: "high",
|
||||||
|
}
|
||||||
|
const planOverride = {
|
||||||
|
model: "openai/gpt-5.2",
|
||||||
|
}
|
||||||
|
|
||||||
|
//#when
|
||||||
|
const result = buildPlanDemoteConfig(prometheusConfig, planOverride)
|
||||||
|
|
||||||
|
//#then - plan model wins, rest inherits from prometheus
|
||||||
|
expect(result.model).toBe("openai/gpt-5.2")
|
||||||
|
expect(result.variant).toBe("max")
|
||||||
|
expect(result.temperature).toBe(0.1)
|
||||||
|
expect(result.reasoningEffort).toBe("high")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("skips undefined values from both sources", () => {
|
||||||
|
//#given
|
||||||
|
const prometheusConfig = {
|
||||||
|
model: "anthropic/claude-opus-4-6",
|
||||||
|
}
|
||||||
|
|
||||||
|
//#when
|
||||||
|
const result = buildPlanDemoteConfig(prometheusConfig, undefined)
|
||||||
|
|
||||||
|
//#then
|
||||||
|
expect(result).toEqual({ mode: "subagent", model: "anthropic/claude-opus-4-6" })
|
||||||
|
expect(Object.keys(result)).toEqual(["mode", "model"])
|
||||||
|
})
|
||||||
|
})
|
||||||
27
src/plugin-handlers/plan-model-inheritance.ts
Normal file
27
src/plugin-handlers/plan-model-inheritance.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
const MODEL_SETTINGS_KEYS = [
|
||||||
|
"model",
|
||||||
|
"variant",
|
||||||
|
"temperature",
|
||||||
|
"top_p",
|
||||||
|
"maxTokens",
|
||||||
|
"thinking",
|
||||||
|
"reasoningEffort",
|
||||||
|
"textVerbosity",
|
||||||
|
"providerOptions",
|
||||||
|
] as const
|
||||||
|
|
||||||
|
export function buildPlanDemoteConfig(
|
||||||
|
prometheusConfig: Record<string, unknown> | undefined,
|
||||||
|
planOverride: Record<string, unknown> | undefined,
|
||||||
|
): Record<string, unknown> {
|
||||||
|
const modelSettings: Record<string, unknown> = {}
|
||||||
|
|
||||||
|
for (const key of MODEL_SETTINGS_KEYS) {
|
||||||
|
const value = planOverride?.[key] ?? prometheusConfig?.[key]
|
||||||
|
if (value !== undefined) {
|
||||||
|
modelSettings[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { mode: "subagent" as const, ...modelSettings }
|
||||||
|
}
|
||||||
@ -538,7 +538,7 @@ export function buildPlanAgentSystemPrepend(
|
|||||||
* List of agent names that should be treated as plan agents.
|
* List of agent names that should be treated as plan agents.
|
||||||
* Case-insensitive matching is used.
|
* Case-insensitive matching is used.
|
||||||
*/
|
*/
|
||||||
export const PLAN_AGENT_NAMES = ["plan", "prometheus", "planner"]
|
export const PLAN_AGENT_NAMES = ["plan", "planner"]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the given agent name is a plan agent.
|
* Check if the given agent name is a plan agent.
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import { resolveMultipleSkillsAsync } from "../../features/opencode-skill-loader
|
|||||||
import { discoverSkills } from "../../features/opencode-skill-loader"
|
import { discoverSkills } from "../../features/opencode-skill-loader"
|
||||||
import { getTaskToastManager } from "../../features/task-toast-manager"
|
import { getTaskToastManager } from "../../features/task-toast-manager"
|
||||||
import { subagentSessions, getSessionAgent } from "../../features/claude-code-session-state"
|
import { subagentSessions, getSessionAgent } from "../../features/claude-code-session-state"
|
||||||
import { log, getAgentToolRestrictions, resolveModelPipeline, promptWithModelSuggestionRetry } from "../../shared"
|
import { log, getAgentToolRestrictions, resolveModelPipeline, promptWithModelSuggestionRetry, promptSyncWithModelSuggestionRetry } from "../../shared"
|
||||||
import { fetchAvailableModels, isModelAvailable } from "../../shared/model-availability"
|
import { fetchAvailableModels, isModelAvailable } from "../../shared/model-availability"
|
||||||
import { readConnectedProvidersCache } from "../../shared/connected-providers-cache"
|
import { readConnectedProvidersCache } from "../../shared/connected-providers-cache"
|
||||||
import { CATEGORY_MODEL_REQUIREMENTS } from "../../shared/model-requirements"
|
import { CATEGORY_MODEL_REQUIREMENTS } from "../../shared/model-requirements"
|
||||||
@ -211,7 +211,7 @@ export async function executeSyncContinuation(
|
|||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
await (client.session as any).promptAsync({
|
await promptSyncWithModelSuggestionRetry(client, {
|
||||||
path: { id: args.session_id! },
|
path: { id: args.session_id! },
|
||||||
body: {
|
body: {
|
||||||
...(resumeAgent !== undefined ? { agent: resumeAgent } : {}),
|
...(resumeAgent !== undefined ? { agent: resumeAgent } : {}),
|
||||||
@ -233,30 +233,6 @@ export async function executeSyncContinuation(
|
|||||||
return `Failed to send continuation prompt: ${errorMessage}\n\nSession ID: ${args.session_id}`
|
return `Failed to send continuation prompt: ${errorMessage}\n\nSession ID: ${args.session_id}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const timing = getTimingConfig()
|
|
||||||
const pollStart = Date.now()
|
|
||||||
let lastMsgCount = 0
|
|
||||||
let stablePolls = 0
|
|
||||||
|
|
||||||
while (Date.now() - pollStart < 60000) {
|
|
||||||
await new Promise(resolve => setTimeout(resolve, timing.POLL_INTERVAL_MS))
|
|
||||||
|
|
||||||
const elapsed = Date.now() - pollStart
|
|
||||||
if (elapsed < timing.SESSION_CONTINUATION_STABILITY_MS) continue
|
|
||||||
|
|
||||||
const messagesCheck = await client.session.messages({ path: { id: args.session_id! } })
|
|
||||||
const msgs = ((messagesCheck as { data?: unknown }).data ?? messagesCheck) as Array<unknown>
|
|
||||||
const currentMsgCount = msgs.length
|
|
||||||
|
|
||||||
if (currentMsgCount > 0 && currentMsgCount === lastMsgCount) {
|
|
||||||
stablePolls++
|
|
||||||
if (stablePolls >= timing.STABILITY_POLLS_REQUIRED) break
|
|
||||||
} else {
|
|
||||||
stablePolls = 0
|
|
||||||
lastMsgCount = currentMsgCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const messagesResult = await client.session.messages({
|
const messagesResult = await client.session.messages({
|
||||||
path: { id: args.session_id! },
|
path: { id: args.session_id! },
|
||||||
})
|
})
|
||||||
@ -621,7 +597,7 @@ export async function executeSyncTask(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const allowTask = isPlanAgent(agentToUse)
|
const allowTask = isPlanAgent(agentToUse)
|
||||||
await promptWithModelSuggestionRetry(client, {
|
await promptSyncWithModelSuggestionRetry(client, {
|
||||||
path: { id: sessionID },
|
path: { id: sessionID },
|
||||||
body: {
|
body: {
|
||||||
agent: agentToUse,
|
agent: agentToUse,
|
||||||
@ -659,70 +635,6 @@ export async function executeSyncTask(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const syncTiming = getTimingConfig()
|
|
||||||
const pollStart = Date.now()
|
|
||||||
let lastMsgCount = 0
|
|
||||||
let stablePolls = 0
|
|
||||||
let pollCount = 0
|
|
||||||
|
|
||||||
log("[task] Starting poll loop", { sessionID, agentToUse })
|
|
||||||
|
|
||||||
while (Date.now() - pollStart < syncTiming.MAX_POLL_TIME_MS) {
|
|
||||||
if (ctx.abort?.aborted) {
|
|
||||||
log("[task] Aborted by user", { sessionID })
|
|
||||||
if (toastManager && taskId) toastManager.removeTask(taskId)
|
|
||||||
return `Task aborted.\n\nSession ID: ${sessionID}`
|
|
||||||
}
|
|
||||||
|
|
||||||
await new Promise(resolve => setTimeout(resolve, syncTiming.POLL_INTERVAL_MS))
|
|
||||||
pollCount++
|
|
||||||
|
|
||||||
const statusResult = await client.session.status()
|
|
||||||
const allStatuses = (statusResult.data ?? {}) as Record<string, { type: string }>
|
|
||||||
const sessionStatus = allStatuses[sessionID]
|
|
||||||
|
|
||||||
if (pollCount % 10 === 0) {
|
|
||||||
log("[task] Poll status", {
|
|
||||||
sessionID,
|
|
||||||
pollCount,
|
|
||||||
elapsed: Math.floor((Date.now() - pollStart) / 1000) + "s",
|
|
||||||
sessionStatus: sessionStatus?.type ?? "not_in_status",
|
|
||||||
stablePolls,
|
|
||||||
lastMsgCount,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sessionStatus && sessionStatus.type !== "idle") {
|
|
||||||
stablePolls = 0
|
|
||||||
lastMsgCount = 0
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const elapsed = Date.now() - pollStart
|
|
||||||
if (elapsed < syncTiming.MIN_STABILITY_TIME_MS) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const messagesCheck = await client.session.messages({ path: { id: sessionID } })
|
|
||||||
const msgs = ((messagesCheck as { data?: unknown }).data ?? messagesCheck) as Array<unknown>
|
|
||||||
const currentMsgCount = msgs.length
|
|
||||||
|
|
||||||
if (currentMsgCount === lastMsgCount) {
|
|
||||||
stablePolls++
|
|
||||||
if (stablePolls >= syncTiming.STABILITY_POLLS_REQUIRED) {
|
|
||||||
log("[task] Poll complete - messages stable", { sessionID, pollCount, currentMsgCount })
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stablePolls = 0
|
|
||||||
lastMsgCount = currentMsgCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Date.now() - pollStart >= syncTiming.MAX_POLL_TIME_MS) {
|
|
||||||
log("[task] Poll timeout reached", { sessionID, pollCount, lastMsgCount, stablePolls })
|
|
||||||
}
|
|
||||||
|
|
||||||
const messagesResult = await client.session.messages({
|
const messagesResult = await client.session.messages({
|
||||||
path: { id: sessionID },
|
path: { id: sessionID },
|
||||||
})
|
})
|
||||||
@ -963,7 +875,7 @@ Sisyphus-Junior is spawned automatically when you specify a category. Pick the a
|
|||||||
return {
|
return {
|
||||||
agentToUse: "",
|
agentToUse: "",
|
||||||
categoryModel: undefined,
|
categoryModel: undefined,
|
||||||
error: `You are prometheus. You cannot delegate to prometheus via task.
|
error: `You are the plan agent. You cannot delegate to plan via task.
|
||||||
|
|
||||||
Create the work plan directly - that's your job as the planning agent.`,
|
Create the work plan directly - that's your job as the planning agent.`,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -135,12 +135,12 @@ describe("sisyphus-task", () => {
|
|||||||
expect(result).toBe(true)
|
expect(result).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("returns true for 'prometheus'", () => {
|
test("returns false for 'prometheus' (decoupled from plan)", () => {
|
||||||
// given / #when
|
//#given / #when
|
||||||
const result = isPlanAgent("prometheus")
|
const result = isPlanAgent("prometheus")
|
||||||
|
|
||||||
// then
|
//#then - prometheus is NOT a plan agent
|
||||||
expect(result).toBe(true)
|
expect(result).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("returns true for 'planner'", () => {
|
test("returns true for 'planner'", () => {
|
||||||
@ -159,12 +159,12 @@ describe("sisyphus-task", () => {
|
|||||||
expect(result).toBe(true)
|
expect(result).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("returns true for case-insensitive match 'Prometheus'", () => {
|
test("returns false for case-insensitive match 'Prometheus' (decoupled from plan)", () => {
|
||||||
// given / #when
|
//#given / #when
|
||||||
const result = isPlanAgent("Prometheus")
|
const result = isPlanAgent("Prometheus")
|
||||||
|
|
||||||
// then
|
//#then - Prometheus is NOT a plan agent
|
||||||
expect(result).toBe(true)
|
expect(result).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("returns false for 'oracle'", () => {
|
test("returns false for 'oracle'", () => {
|
||||||
@ -199,11 +199,11 @@ describe("sisyphus-task", () => {
|
|||||||
expect(result).toBe(false)
|
expect(result).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("PLAN_AGENT_NAMES contains expected values", () => {
|
test("PLAN_AGENT_NAMES contains only plan and planner (not prometheus)", () => {
|
||||||
// given / #when / #then
|
//#given / #when / #then
|
||||||
expect(PLAN_AGENT_NAMES).toContain("plan")
|
expect(PLAN_AGENT_NAMES).toContain("plan")
|
||||||
expect(PLAN_AGENT_NAMES).toContain("prometheus")
|
|
||||||
expect(PLAN_AGENT_NAMES).toContain("planner")
|
expect(PLAN_AGENT_NAMES).toContain("planner")
|
||||||
|
expect(PLAN_AGENT_NAMES).not.toContain("prometheus")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -2258,68 +2258,36 @@ describe("sisyphus-task", () => {
|
|||||||
expect(result).toBe(buildPlanAgentSystemPrepend(availableCategories, availableSkills))
|
expect(result).toBe(buildPlanAgentSystemPrepend(availableCategories, availableSkills))
|
||||||
})
|
})
|
||||||
|
|
||||||
test("prepends plan agent system prompt when agentName is 'prometheus'", () => {
|
test("does not prepend plan agent prompt for prometheus agent", () => {
|
||||||
// given
|
//#given - prometheus is NOT a plan agent (decoupled)
|
||||||
const { buildSystemContent } = require("./tools")
|
const { buildSystemContent } = require("./tools")
|
||||||
const { buildPlanAgentSystemPrepend } = require("./constants")
|
const skillContent = "You are a strategic planner"
|
||||||
|
|
||||||
const availableCategories = [
|
//#when
|
||||||
{
|
|
||||||
name: "ultrabrain",
|
|
||||||
description: "Complex architecture, deep logical reasoning",
|
|
||||||
model: "openai/gpt-5.3-codex",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
const availableSkills = [
|
|
||||||
{
|
|
||||||
name: "git-master",
|
|
||||||
description: "Atomic commits, git operations.",
|
|
||||||
location: "plugin",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
// when
|
|
||||||
const result = buildSystemContent({
|
const result = buildSystemContent({
|
||||||
|
skillContent,
|
||||||
agentName: "prometheus",
|
agentName: "prometheus",
|
||||||
availableCategories,
|
|
||||||
availableSkills,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// then
|
//#then - prometheus should NOT get plan agent system prepend
|
||||||
expect(result).toContain("<system>")
|
expect(result).toBe(skillContent)
|
||||||
expect(result).toBe(buildPlanAgentSystemPrepend(availableCategories, availableSkills))
|
expect(result).not.toContain("MANDATORY CONTEXT GATHERING PROTOCOL")
|
||||||
})
|
})
|
||||||
|
|
||||||
test("prepends plan agent system prompt when agentName is 'Prometheus' (case insensitive)", () => {
|
test("does not prepend plan agent prompt for Prometheus (case insensitive)", () => {
|
||||||
// given
|
//#given - Prometheus (capitalized) is NOT a plan agent
|
||||||
const { buildSystemContent } = require("./tools")
|
const { buildSystemContent } = require("./tools")
|
||||||
const { buildPlanAgentSystemPrepend } = require("./constants")
|
const skillContent = "You are a strategic planner"
|
||||||
|
|
||||||
const availableCategories = [
|
//#when
|
||||||
{
|
|
||||||
name: "quick",
|
|
||||||
description: "Trivial tasks",
|
|
||||||
model: "anthropic/claude-haiku-4-5",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
const availableSkills = [
|
|
||||||
{
|
|
||||||
name: "dev-browser",
|
|
||||||
description: "Persistent browser state automation.",
|
|
||||||
location: "plugin",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
// when
|
|
||||||
const result = buildSystemContent({
|
const result = buildSystemContent({
|
||||||
|
skillContent,
|
||||||
agentName: "Prometheus",
|
agentName: "Prometheus",
|
||||||
availableCategories,
|
|
||||||
availableSkills,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// then
|
//#then
|
||||||
expect(result).toContain("<system>")
|
expect(result).toBe(skillContent)
|
||||||
expect(result).toBe(buildPlanAgentSystemPrepend(availableCategories, availableSkills))
|
expect(result).not.toContain("MANDATORY CONTEXT GATHERING PROTOCOL")
|
||||||
})
|
})
|
||||||
|
|
||||||
test("combines plan agent prepend with skill content", () => {
|
test("combines plan agent prepend with skill content", () => {
|
||||||
@ -2565,14 +2533,14 @@ describe("sisyphus-task", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("prometheus self-delegation block", () => {
|
describe("plan agent self-delegation block", () => {
|
||||||
test("prometheus cannot delegate to prometheus - returns error with guidance", async () => {
|
test("plan agent cannot delegate to plan - returns error with guidance", async () => {
|
||||||
// given - current agent is prometheus
|
//#given - current agent is plan
|
||||||
const { createDelegateTask } = require("./tools")
|
const { createDelegateTask } = require("./tools")
|
||||||
|
|
||||||
const mockManager = { launch: async () => ({}) }
|
const mockManager = { launch: async () => ({}) }
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
app: { agents: async () => ({ data: [{ name: "prometheus", mode: "subagent" }] }) },
|
app: { agents: async () => ({ data: [{ name: "plan", mode: "subagent" }] }) },
|
||||||
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
@ -2592,44 +2560,44 @@ describe("sisyphus-task", () => {
|
|||||||
const toolContext = {
|
const toolContext = {
|
||||||
sessionID: "parent-session",
|
sessionID: "parent-session",
|
||||||
messageID: "parent-message",
|
messageID: "parent-message",
|
||||||
agent: "prometheus",
|
agent: "plan",
|
||||||
abort: new AbortController().signal,
|
abort: new AbortController().signal,
|
||||||
}
|
}
|
||||||
|
|
||||||
// when - prometheus tries to delegate to prometheus
|
//#when - plan agent tries to delegate to plan
|
||||||
const result = await tool.execute(
|
const result = await tool.execute(
|
||||||
{
|
{
|
||||||
description: "Test self-delegation block",
|
description: "Test self-delegation block",
|
||||||
prompt: "Create a plan",
|
prompt: "Create a plan",
|
||||||
subagent_type: "prometheus",
|
subagent_type: "plan",
|
||||||
run_in_background: false,
|
run_in_background: false,
|
||||||
load_skills: [],
|
load_skills: [],
|
||||||
},
|
},
|
||||||
toolContext
|
toolContext
|
||||||
)
|
)
|
||||||
|
|
||||||
// then - should return error telling prometheus to create plan directly
|
//#then - should return error telling plan agent to create plan directly
|
||||||
expect(result).toContain("prometheus")
|
expect(result).toContain("plan agent")
|
||||||
expect(result).toContain("directly")
|
expect(result).toContain("directly")
|
||||||
})
|
})
|
||||||
|
|
||||||
test("non-prometheus agent CAN delegate to prometheus - proceeds normally", async () => {
|
test("prometheus is NOT a plan agent - can delegate to plan normally", async () => {
|
||||||
// given - current agent is sisyphus
|
//#given - current agent is prometheus (no longer treated as plan agent)
|
||||||
const { createDelegateTask } = require("./tools")
|
const { createDelegateTask } = require("./tools")
|
||||||
|
|
||||||
const mockManager = { launch: async () => ({}) }
|
const mockManager = { launch: async () => ({}) }
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
app: { agents: async () => ({ data: [{ name: "prometheus", mode: "subagent" }] }) },
|
app: { agents: async () => ({ data: [{ name: "plan", mode: "subagent" }] }) },
|
||||||
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_prometheus_allowed" } }),
|
create: async () => ({ data: { id: "ses_plan_from_prometheus" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
promptAsync: async () => ({ data: {} }),
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Plan created successfully" }] }]
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Plan created successfully" }] }]
|
||||||
}),
|
}),
|
||||||
status: async () => ({ data: { "ses_prometheus_allowed": { type: "idle" } } }),
|
status: async () => ({ data: { "ses_plan_from_prometheus": { type: "idle" } } }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2641,34 +2609,34 @@ describe("sisyphus-task", () => {
|
|||||||
const toolContext = {
|
const toolContext = {
|
||||||
sessionID: "parent-session",
|
sessionID: "parent-session",
|
||||||
messageID: "parent-message",
|
messageID: "parent-message",
|
||||||
agent: "sisyphus",
|
agent: "prometheus",
|
||||||
abort: new AbortController().signal,
|
abort: new AbortController().signal,
|
||||||
}
|
}
|
||||||
|
|
||||||
// when - sisyphus delegates to prometheus
|
//#when - prometheus delegates to plan (should work now)
|
||||||
const result = await tool.execute(
|
const result = await tool.execute(
|
||||||
{
|
{
|
||||||
description: "Test prometheus delegation from non-prometheus agent",
|
description: "Test plan delegation from prometheus",
|
||||||
prompt: "Create a plan",
|
prompt: "Create a plan",
|
||||||
subagent_type: "prometheus",
|
subagent_type: "plan",
|
||||||
run_in_background: false,
|
run_in_background: false,
|
||||||
load_skills: [],
|
load_skills: [],
|
||||||
},
|
},
|
||||||
toolContext
|
toolContext
|
||||||
)
|
)
|
||||||
|
|
||||||
// then - should proceed normally
|
//#then - should proceed normally (prometheus is not plan agent)
|
||||||
expect(result).not.toContain("Cannot delegate")
|
expect(result).not.toContain("Cannot delegate")
|
||||||
expect(result).toContain("Plan created successfully")
|
expect(result).toContain("Plan created successfully")
|
||||||
}, { timeout: 20000 })
|
}, { timeout: 20000 })
|
||||||
|
|
||||||
test("case-insensitive: Prometheus (capitalized) cannot delegate to prometheus", async () => {
|
test("planner agent self-delegation is also blocked", async () => {
|
||||||
// given - current agent is Prometheus (capitalized)
|
//#given - current agent is planner
|
||||||
const { createDelegateTask } = require("./tools")
|
const { createDelegateTask } = require("./tools")
|
||||||
|
|
||||||
const mockManager = { launch: async () => ({}) }
|
const mockManager = { launch: async () => ({}) }
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
app: { agents: async () => ({ data: [{ name: "prometheus", mode: "subagent" }] }) },
|
app: { agents: async () => ({ data: [{ name: "planner", mode: "subagent" }] }) },
|
||||||
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
@ -2688,24 +2656,24 @@ describe("sisyphus-task", () => {
|
|||||||
const toolContext = {
|
const toolContext = {
|
||||||
sessionID: "parent-session",
|
sessionID: "parent-session",
|
||||||
messageID: "parent-message",
|
messageID: "parent-message",
|
||||||
agent: "Prometheus",
|
agent: "planner",
|
||||||
abort: new AbortController().signal,
|
abort: new AbortController().signal,
|
||||||
}
|
}
|
||||||
|
|
||||||
// when - Prometheus tries to delegate to prometheus
|
//#when - planner tries to delegate to plan
|
||||||
const result = await tool.execute(
|
const result = await tool.execute(
|
||||||
{
|
{
|
||||||
description: "Test case-insensitive block",
|
description: "Test planner self-delegation block",
|
||||||
prompt: "Create a plan",
|
prompt: "Create a plan",
|
||||||
subagent_type: "prometheus",
|
subagent_type: "plan",
|
||||||
run_in_background: false,
|
run_in_background: false,
|
||||||
load_skills: [],
|
load_skills: [],
|
||||||
},
|
},
|
||||||
toolContext
|
toolContext
|
||||||
)
|
)
|
||||||
|
|
||||||
// then - should still return error
|
//#then - should return error (planner is a plan agent alias)
|
||||||
expect(result).toContain("prometheus")
|
expect(result).toContain("plan agent")
|
||||||
expect(result).toContain("directly")
|
expect(result).toContain("directly")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -2903,9 +2871,9 @@ describe("sisyphus-task", () => {
|
|||||||
}, { timeout: 20000 })
|
}, { timeout: 20000 })
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("prometheus subagent task permission", () => {
|
describe("subagent task permission", () => {
|
||||||
test("prometheus subagent should have task permission enabled", async () => {
|
test("plan subagent should have task permission enabled", async () => {
|
||||||
// given - sisyphus delegates to prometheus
|
//#given - sisyphus delegates to plan agent
|
||||||
const { createDelegateTask } = require("./tools")
|
const { createDelegateTask } = require("./tools")
|
||||||
let promptBody: any
|
let promptBody: any
|
||||||
|
|
||||||
@ -2917,17 +2885,17 @@ describe("sisyphus-task", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
app: { agents: async () => ({ data: [{ name: "prometheus", mode: "subagent" }] }) },
|
app: { agents: async () => ({ data: [{ name: "plan", mode: "subagent" }] }) },
|
||||||
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_prometheus_delegate" } }),
|
create: async () => ({ data: { id: "ses_plan_delegate" } }),
|
||||||
prompt: promptMock,
|
prompt: promptMock,
|
||||||
promptAsync: promptMock,
|
promptAsync: promptMock,
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Plan created" }] }]
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Plan created" }] }]
|
||||||
}),
|
}),
|
||||||
status: async () => ({ data: { "ses_prometheus_delegate": { type: "idle" } } }),
|
status: async () => ({ data: { "ses_plan_delegate": { type: "idle" } } }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2943,10 +2911,65 @@ describe("sisyphus-task", () => {
|
|||||||
abort: new AbortController().signal,
|
abort: new AbortController().signal,
|
||||||
}
|
}
|
||||||
|
|
||||||
// when - sisyphus delegates to prometheus
|
//#when - sisyphus delegates to plan
|
||||||
await tool.execute(
|
await tool.execute(
|
||||||
{
|
{
|
||||||
description: "Test prometheus task permission",
|
description: "Test plan task permission",
|
||||||
|
prompt: "Create a plan",
|
||||||
|
subagent_type: "plan",
|
||||||
|
run_in_background: false,
|
||||||
|
load_skills: [],
|
||||||
|
},
|
||||||
|
toolContext
|
||||||
|
)
|
||||||
|
|
||||||
|
//#then - plan agent should have task permission
|
||||||
|
expect(promptBody.tools.task).toBe(true)
|
||||||
|
}, { timeout: 20000 })
|
||||||
|
|
||||||
|
test("prometheus subagent should NOT have task permission (decoupled from plan)", async () => {
|
||||||
|
//#given - sisyphus delegates to prometheus (no longer a plan agent)
|
||||||
|
const { createDelegateTask } = require("./tools")
|
||||||
|
let promptBody: any
|
||||||
|
|
||||||
|
const mockManager = { launch: async () => ({}) }
|
||||||
|
|
||||||
|
const promptMock = async (input: any) => {
|
||||||
|
promptBody = input.body
|
||||||
|
return { data: {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockClient = {
|
||||||
|
app: { agents: async () => ({ data: [{ name: "prometheus", mode: "subagent" }] }) },
|
||||||
|
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
||||||
|
session: {
|
||||||
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
|
create: async () => ({ data: { id: "ses_prometheus_no_task" } }),
|
||||||
|
prompt: promptMock,
|
||||||
|
promptAsync: promptMock,
|
||||||
|
messages: async () => ({
|
||||||
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Plan created" }] }]
|
||||||
|
}),
|
||||||
|
status: async () => ({ data: { "ses_prometheus_no_task": { type: "idle" } } }),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const tool = createDelegateTask({
|
||||||
|
manager: mockManager,
|
||||||
|
client: mockClient,
|
||||||
|
})
|
||||||
|
|
||||||
|
const toolContext = {
|
||||||
|
sessionID: "parent-session",
|
||||||
|
messageID: "parent-message",
|
||||||
|
agent: "sisyphus",
|
||||||
|
abort: new AbortController().signal,
|
||||||
|
}
|
||||||
|
|
||||||
|
//#when - sisyphus delegates to prometheus
|
||||||
|
await tool.execute(
|
||||||
|
{
|
||||||
|
description: "Test prometheus no task permission",
|
||||||
prompt: "Create a plan",
|
prompt: "Create a plan",
|
||||||
subagent_type: "prometheus",
|
subagent_type: "prometheus",
|
||||||
run_in_background: false,
|
run_in_background: false,
|
||||||
@ -2955,12 +2978,12 @@ describe("sisyphus-task", () => {
|
|||||||
toolContext
|
toolContext
|
||||||
)
|
)
|
||||||
|
|
||||||
// then - prometheus should have task permission
|
//#then - prometheus should NOT have task permission (it's not a plan agent)
|
||||||
expect(promptBody.tools.task).toBe(true)
|
expect(promptBody.tools.task).toBe(false)
|
||||||
}, { timeout: 20000 })
|
}, { timeout: 20000 })
|
||||||
|
|
||||||
test("non-prometheus subagent should NOT have task permission", async () => {
|
test("non-plan subagent should NOT have task permission", async () => {
|
||||||
// given - sisyphus delegates to oracle (non-prometheus)
|
//#given - sisyphus delegates to oracle (non-plan)
|
||||||
const { createDelegateTask } = require("./tools")
|
const { createDelegateTask } = require("./tools")
|
||||||
let promptBody: any
|
let promptBody: any
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user