fix(subagent): remove permission.question=deny override that caused zombie sessions
Child session creation was injecting permission: { question: 'deny' } which
conflicted with OpenCode's child session permission handling, causing subagent
sessions to hang with 0 messages after creation (zombie state).
Remove the permission override from all session creators (BackgroundManager,
sync-session-creator, call-omo-agent) and rely on prompt-level tool restrictions
(tools.question=false) to maintain the intended policy.
Closes #1711
This commit is contained in:
parent
fd99a29d6e
commit
b0c570e054
@ -1520,7 +1520,7 @@ describe("BackgroundManager - Non-blocking Queue Integration", () => {
|
||||
})
|
||||
|
||||
describe("task transitions pending→running when slot available", () => {
|
||||
test("should inherit parent session permission rules (and force deny question)", async () => {
|
||||
test("does not override parent session permission when creating child session", async () => {
|
||||
// given
|
||||
const createCalls: any[] = []
|
||||
const parentPermission = [
|
||||
@ -1562,11 +1562,7 @@ describe("BackgroundManager - Non-blocking Queue Integration", () => {
|
||||
|
||||
// then
|
||||
expect(createCalls).toHaveLength(1)
|
||||
const permission = createCalls[0]?.body?.permission
|
||||
expect(permission).toEqual([
|
||||
{ permission: "plan_enter", action: "deny", pattern: "*" },
|
||||
{ permission: "question", action: "deny", pattern: "*" },
|
||||
])
|
||||
expect(createCalls[0]?.body?.permission).toBeUndefined()
|
||||
})
|
||||
|
||||
test("should transition first task to running immediately", async () => {
|
||||
|
||||
@ -236,17 +236,10 @@ export class BackgroundManager {
|
||||
const parentDirectory = parentSession?.data?.directory ?? this.directory
|
||||
log(`[background-agent] Parent dir: ${parentSession?.data?.directory}, using: ${parentDirectory}`)
|
||||
|
||||
const inheritedPermission = (parentSession as any)?.data?.permission
|
||||
const permissionRules = Array.isArray(inheritedPermission)
|
||||
? inheritedPermission.filter((r: any) => r?.permission !== "question")
|
||||
: []
|
||||
permissionRules.push({ permission: "question", action: "deny" as const, pattern: "*" })
|
||||
|
||||
const createResult = await this.client.session.create({
|
||||
body: {
|
||||
parentID: input.parentSessionID,
|
||||
title: `${input.description} (@${input.agent} subagent)`,
|
||||
permission: permissionRules,
|
||||
} as any,
|
||||
query: {
|
||||
directory: parentDirectory,
|
||||
|
||||
@ -3,7 +3,7 @@ import { describe, test, expect } from "bun:test"
|
||||
import { createTask, startTask } from "./spawner"
|
||||
|
||||
describe("background-agent spawner.startTask", () => {
|
||||
test("should inherit parent session permission rules (and force deny question)", async () => {
|
||||
test("does not override parent session permission rules when creating child session", async () => {
|
||||
//#given
|
||||
const createCalls: any[] = []
|
||||
const parentPermission = [
|
||||
@ -57,9 +57,6 @@ describe("background-agent spawner.startTask", () => {
|
||||
|
||||
//#then
|
||||
expect(createCalls).toHaveLength(1)
|
||||
expect(createCalls[0]?.body?.permission).toEqual([
|
||||
{ permission: "plan_enter", action: "deny", pattern: "*" },
|
||||
{ permission: "question", action: "deny", pattern: "*" },
|
||||
])
|
||||
expect(createCalls[0]?.body?.permission).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
@ -58,17 +58,10 @@ export async function startTask(
|
||||
const parentDirectory = parentSession?.data?.directory ?? directory
|
||||
log(`[background-agent] Parent dir: ${parentSession?.data?.directory}, using: ${parentDirectory}`)
|
||||
|
||||
const inheritedPermission = (parentSession as any)?.data?.permission
|
||||
const permissionRules = Array.isArray(inheritedPermission)
|
||||
? inheritedPermission.filter((r: any) => r?.permission !== "question")
|
||||
: []
|
||||
permissionRules.push({ permission: "question", action: "deny" as const, pattern: "*" })
|
||||
|
||||
const createResult = await client.session.create({
|
||||
body: {
|
||||
parentID: input.parentSessionID,
|
||||
title: `Background: ${input.description}`,
|
||||
permission: permissionRules,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any,
|
||||
query: {
|
||||
|
||||
@ -15,7 +15,6 @@ export async function createBackgroundSession(options: {
|
||||
const body = {
|
||||
parentID: input.parentSessionID,
|
||||
title: `Background: ${input.description}`,
|
||||
permission: [{ permission: "question", action: "deny" as const, pattern: "*" }],
|
||||
}
|
||||
|
||||
const createResult = await client.session
|
||||
|
||||
@ -69,7 +69,6 @@ export async function startQueuedTask(args: {
|
||||
body: {
|
||||
parentID: input.parentSessionID,
|
||||
title: `${input.description} (@${input.agent} subagent)`,
|
||||
permission: [{ permission: "question", action: "deny" as const, pattern: "*" }],
|
||||
} as any,
|
||||
query: {
|
||||
directory: parentDirectory,
|
||||
|
||||
50
src/tools/call-omo-agent/session-creator.test.ts
Normal file
50
src/tools/call-omo-agent/session-creator.test.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
|
||||
import { createOrGetSession } from "./session-creator"
|
||||
import { _resetForTesting, subagentSessions } from "../../features/claude-code-session-state"
|
||||
|
||||
describe("call-omo-agent createOrGetSession", () => {
|
||||
test("creates child session without overriding permission and tracks it as subagent session", async () => {
|
||||
// given
|
||||
_resetForTesting()
|
||||
|
||||
const createCalls: Array<unknown> = []
|
||||
const ctx = {
|
||||
directory: "/project",
|
||||
client: {
|
||||
session: {
|
||||
get: async () => ({ data: { directory: "/parent" } }),
|
||||
create: async (args: unknown) => {
|
||||
createCalls.push(args)
|
||||
return { data: { id: "ses_child" } }
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const toolContext = {
|
||||
sessionID: "ses_parent",
|
||||
messageID: "msg_parent",
|
||||
agent: "sisyphus",
|
||||
abort: new AbortController().signal,
|
||||
}
|
||||
|
||||
const args = {
|
||||
description: "test",
|
||||
prompt: "hello",
|
||||
subagent_type: "explore",
|
||||
run_in_background: true,
|
||||
}
|
||||
|
||||
// when
|
||||
const result = await createOrGetSession(args as any, toolContext as any, ctx as any)
|
||||
|
||||
// then
|
||||
expect(result).toEqual({ sessionID: "ses_child", isNew: true })
|
||||
expect(createCalls).toHaveLength(1)
|
||||
const createBody = (createCalls[0] as any)?.body
|
||||
expect(createBody?.parentID).toBe("ses_parent")
|
||||
expect(createBody?.permission).toBeUndefined()
|
||||
expect(subagentSessions.has("ses_child")).toBe(true)
|
||||
})
|
||||
})
|
||||
@ -1,5 +1,6 @@
|
||||
import type { CallOmoAgentArgs } from "./types"
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { subagentSessions } from "../../features/claude-code-session-state"
|
||||
import { log } from "../../shared"
|
||||
|
||||
export async function createOrGetSession(
|
||||
@ -38,9 +39,6 @@ export async function createOrGetSession(
|
||||
body: {
|
||||
parentID: toolContext.sessionID,
|
||||
title: `${args.description} (@${args.subagent_type} subagent)`,
|
||||
permission: [
|
||||
{ permission: "question", action: "deny" as const, pattern: "*" },
|
||||
],
|
||||
} as any,
|
||||
query: {
|
||||
directory: parentDirectory,
|
||||
@ -65,6 +63,7 @@ Original error: ${createResult.error}`)
|
||||
|
||||
const sessionID = createResult.data.id
|
||||
log(`[call_omo_agent] Created session: ${sessionID}`)
|
||||
subagentSessions.add(sessionID)
|
||||
return { sessionID, isNew: true }
|
||||
}
|
||||
}
|
||||
|
||||
47
src/tools/call-omo-agent/subagent-session-creator.test.ts
Normal file
47
src/tools/call-omo-agent/subagent-session-creator.test.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
|
||||
import { resolveOrCreateSessionId } from "./subagent-session-creator"
|
||||
import { _resetForTesting, subagentSessions } from "../../features/claude-code-session-state"
|
||||
|
||||
describe("call-omo-agent resolveOrCreateSessionId", () => {
|
||||
test("tracks newly created child session as subagent session", async () => {
|
||||
// given
|
||||
_resetForTesting()
|
||||
|
||||
const createCalls: Array<unknown> = []
|
||||
const ctx = {
|
||||
directory: "/project",
|
||||
client: {
|
||||
session: {
|
||||
get: async () => ({ data: { directory: "/parent" } }),
|
||||
create: async (args: unknown) => {
|
||||
createCalls.push(args)
|
||||
return { data: { id: "ses_child_sync" } }
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const args = {
|
||||
description: "sync test",
|
||||
prompt: "hello",
|
||||
subagent_type: "explore",
|
||||
run_in_background: false,
|
||||
}
|
||||
|
||||
const toolContext = {
|
||||
sessionID: "ses_parent",
|
||||
messageID: "msg_parent",
|
||||
agent: "sisyphus",
|
||||
abort: new AbortController().signal,
|
||||
}
|
||||
|
||||
// when
|
||||
const result = await resolveOrCreateSessionId(ctx as any, args as any, toolContext as any)
|
||||
|
||||
// then
|
||||
expect(result).toEqual({ ok: true, sessionID: "ses_child_sync" })
|
||||
expect(createCalls).toHaveLength(1)
|
||||
expect(subagentSessions.has("ses_child_sync")).toBe(true)
|
||||
})
|
||||
})
|
||||
@ -1,5 +1,6 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { log } from "../../shared"
|
||||
import { subagentSessions } from "../../features/claude-code-session-state"
|
||||
import type { CallOmoAgentArgs } from "./types"
|
||||
import type { ToolContextWithMetadata } from "./tool-context-with-metadata"
|
||||
|
||||
@ -63,5 +64,6 @@ Original error: ${createResult.error}`,
|
||||
|
||||
const sessionID = createResult.data.id
|
||||
log(`[call_omo_agent] Created session: ${sessionID}`)
|
||||
subagentSessions.add(sessionID)
|
||||
return { ok: true, sessionID }
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ export async function promptSubagentSession(
|
||||
tools: {
|
||||
...getAgentToolRestrictions(options.agent),
|
||||
task: false,
|
||||
question: false,
|
||||
},
|
||||
parts: [{ type: "text", text: options.prompt }],
|
||||
},
|
||||
|
||||
@ -13,9 +13,6 @@ export async function createSyncSession(
|
||||
body: {
|
||||
parentID: input.parentSessionID,
|
||||
title: `${input.description} (@${input.agentToUse} subagent)`,
|
||||
permission: [
|
||||
{ permission: "question", action: "deny" as const, pattern: "*" },
|
||||
],
|
||||
} as any,
|
||||
query: {
|
||||
directory: parentDirectory,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user