diff --git a/src/features/background-agent/manager.ts b/src/features/background-agent/manager.ts
index ab77ab46..aa492278 100644
--- a/src/features/background-agent/manager.ts
+++ b/src/features/background-agent/manager.ts
@@ -138,6 +138,7 @@ export class BackgroundManager {
parentModel: input.parentModel,
parentAgent: input.parentAgent,
model: input.model,
+ category: input.category,
}
this.tasks.set(task.id, task)
@@ -231,7 +232,7 @@ export class BackgroundManager {
const createResult = await this.client.session.create({
body: {
parentID: input.parentSessionID,
- title: `Background: ${input.description}`,
+ title: `${input.description} (@${input.agent} subagent)`,
permission: [
{ permission: "question", action: "deny" as const, pattern: "*" },
],
diff --git a/src/features/background-agent/types.ts b/src/features/background-agent/types.ts
index e9b29938..553f5bb5 100644
--- a/src/features/background-agent/types.ts
+++ b/src/features/background-agent/types.ts
@@ -38,6 +38,8 @@ export interface BackgroundTask {
parentAgent?: string
/** Marks if the task was launched from an unstable agent/category */
isUnstableAgent?: boolean
+ /** Category used for this task (e.g., 'quick', 'visual-engineering') */
+ category?: string
/** Last message count for stability detection */
lastMsgCount?: number
@@ -57,6 +59,7 @@ export interface LaunchInput {
isUnstableAgent?: boolean
skills?: string[]
skillContent?: string
+ category?: string
}
export interface ResumeInput {
diff --git a/src/tools/delegate-task/executor.ts b/src/tools/delegate-task/executor.ts
index c9a7712d..8d2a75fe 100644
--- a/src/tools/delegate-task/executor.ts
+++ b/src/tools/delegate-task/executor.ts
@@ -127,13 +127,16 @@ export async function executeBackgroundContinuation(
return `Background task continued.
Task ID: ${task.id}
-Session ID: ${task.sessionID}
Description: ${task.description}
Agent: ${task.agent}
Status: ${task.status}
Agent continues with full previous context preserved.
-Use \`background_output\` with task_id="${task.id}" to check progress.`
+Use \`background_output\` with task_id="${task.id}" to check progress.
+
+
+session_id: ${task.sessionID}
+`
} catch (error) {
return formatDetailedError(error, {
operation: "Continue background task",
@@ -277,14 +280,13 @@ export async function executeSyncContinuation(
return `Task continued and completed in ${duration}.
-Session ID: ${args.session_id}
-
---
${textContent || "(No text output)"}
----
-To continue this session: session_id="${args.session_id}"`
+
+session_id: ${args.session_id}
+`
}
export async function executeUnstableAgentTask(
@@ -311,6 +313,7 @@ export async function executeUnstableAgentTask(
model: categoryModel,
skills: args.load_skills.length > 0 ? args.load_skills : undefined,
skillContent: systemContent,
+ category: args.category,
})
const WAIT_FOR_SESSION_INTERVAL_MS = 100
@@ -408,7 +411,6 @@ Your run_in_background=false was automatically converted to background mode for
Duration: ${duration}
Agent: ${agentToUse}${args.category ? ` (category: ${args.category})` : ""}
-Session ID: ${sessionID}
MONITORING INSTRUCTIONS:
- The task was monitored and completed successfully
@@ -422,8 +424,9 @@ RESULT:
${textContent || "(No text output)"}
----
-To continue this session: session_id="${sessionID}"`
+
+session_id: ${sessionID}
+`
} catch (error) {
return formatDetailedError(error, {
operation: "Launch monitored background task",
@@ -457,6 +460,7 @@ export async function executeBackgroundTask(
model: categoryModel,
skills: args.load_skills.length > 0 ? args.load_skills : undefined,
skillContent: systemContent,
+ category: args.category,
})
ctx.metadata?.({
@@ -476,13 +480,15 @@ export async function executeBackgroundTask(
return `Background task launched.
Task ID: ${task.id}
-Session ID: ${task.sessionID}
Description: ${task.description}
Agent: ${task.agent}${args.category ? ` (category: ${args.category})` : ""}
Status: ${task.status}
System notifies on completion. Use \`background_output\` with task_id="${task.id}" to check.
-To continue this session: session_id="${task.sessionID}"`
+
+
+session_id: ${task.sessionID}
+`
} catch (error) {
return formatDetailedError(error, {
operation: "Launch background task",
@@ -517,7 +523,7 @@ export async function executeSyncTask(
const createResult = await client.session.create({
body: {
parentID: parentContext.sessionID,
- title: `Task: ${args.description}`,
+ title: `${args.description} (@${agentToUse} subagent)`,
permission: [
{ permission: "question", action: "deny" as const, pattern: "*" },
],
@@ -715,14 +721,14 @@ export async function executeSyncTask(
return `Task completed in ${duration}.
Agent: ${agentToUse}${args.category ? ` (category: ${args.category})` : ""}
-Session ID: ${sessionID}
---
${textContent || "(No text output)"}
----
-To continue this session: session_id="${sessionID}"`
+
+session_id: ${sessionID}
+`
} catch (error) {
if (toastManager && taskId !== undefined) {
toastManager.removeTask(taskId)
diff --git a/src/tools/delegate-task/tools.test.ts b/src/tools/delegate-task/tools.test.ts
index 64465ba9..d88d362b 100644
--- a/src/tools/delegate-task/tools.test.ts
+++ b/src/tools/delegate-task/tools.test.ts
@@ -2563,4 +2563,162 @@ describe("sisyphus-task", () => {
expect(promptBody.tools.delegate_task).toBe(false)
}, { timeout: 20000 })
})
+
+ describe("session title and metadata format (OpenCode compatibility)", () => {
+ test("sync session title follows OpenCode format: '{description} (@{agent} subagent)'", async () => {
+ // given
+ const { createDelegateTask } = require("./tools")
+ let createBody: any
+
+ const mockManager = { launch: async () => ({}) }
+ const mockClient = {
+ app: { agents: async () => ({ data: [] }) },
+ config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
+ model: { list: async () => [{ id: SYSTEM_DEFAULT_MODEL }] },
+ session: {
+ get: async () => ({ data: { directory: "/project" } }),
+ create: async (input: any) => {
+ createBody = input.body
+ return { data: { id: "ses_title_test" } }
+ },
+ prompt: async () => ({ data: {} }),
+ messages: async () => ({
+ data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "done" }] }]
+ }),
+ status: async () => ({ data: { "ses_title_test": { type: "idle" } } }),
+ },
+ }
+
+ const tool = createDelegateTask({
+ manager: mockManager,
+ client: mockClient,
+ })
+
+ const toolContext = {
+ sessionID: "parent-session",
+ messageID: "parent-message",
+ agent: "sisyphus",
+ abort: new AbortController().signal,
+ }
+
+ // when - sync task with category
+ await tool.execute(
+ {
+ description: "Implement feature X",
+ prompt: "Build the feature",
+ category: "quick",
+ run_in_background: false,
+ load_skills: [],
+ },
+ toolContext
+ )
+
+ // then - title should follow OpenCode format
+ expect(createBody.title).toBe("Implement feature X (@sisyphus-junior subagent)")
+ }, { timeout: 10000 })
+
+ test("sync task output includes block with session_id", async () => {
+ // given
+ const { createDelegateTask } = require("./tools")
+
+ const mockManager = { launch: async () => ({}) }
+ const mockClient = {
+ app: { agents: async () => ({ data: [] }) },
+ config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
+ model: { list: async () => [{ id: SYSTEM_DEFAULT_MODEL }] },
+ session: {
+ get: async () => ({ data: { directory: "/project" } }),
+ create: async () => ({ data: { id: "ses_metadata_test" } }),
+ prompt: async () => ({ data: {} }),
+ messages: async () => ({
+ data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Task completed" }] }]
+ }),
+ status: async () => ({ data: { "ses_metadata_test": { type: "idle" } } }),
+ },
+ }
+
+ const tool = createDelegateTask({
+ manager: mockManager,
+ client: mockClient,
+ })
+
+ const toolContext = {
+ sessionID: "parent-session",
+ messageID: "parent-message",
+ agent: "sisyphus",
+ abort: new AbortController().signal,
+ }
+
+ // when
+ const result = await tool.execute(
+ {
+ description: "Test metadata format",
+ prompt: "Do something",
+ category: "quick",
+ run_in_background: false,
+ load_skills: [],
+ },
+ toolContext
+ )
+
+ // then - output should contain block
+ expect(result).toContain("")
+ expect(result).toContain("session_id: ses_metadata_test")
+ expect(result).toContain("")
+ }, { timeout: 10000 })
+
+ test("background task output includes block with session_id", async () => {
+ // given
+ const { createDelegateTask } = require("./tools")
+
+ const mockManager = {
+ launch: async () => ({
+ id: "bg_meta_test",
+ sessionID: "ses_bg_metadata",
+ description: "Background metadata test",
+ agent: "sisyphus-junior",
+ status: "running",
+ }),
+ }
+ const mockClient = {
+ app: { agents: async () => ({ data: [] }) },
+ config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
+ model: { list: async () => [{ id: SYSTEM_DEFAULT_MODEL }] },
+ session: {
+ create: async () => ({ data: { id: "ses_bg_metadata" } }),
+ prompt: async () => ({ data: {} }),
+ messages: async () => ({ data: [] }),
+ },
+ }
+
+ const tool = createDelegateTask({
+ manager: mockManager,
+ client: mockClient,
+ })
+
+ const toolContext = {
+ sessionID: "parent-session",
+ messageID: "parent-message",
+ agent: "sisyphus",
+ abort: new AbortController().signal,
+ }
+
+ // when
+ const result = await tool.execute(
+ {
+ description: "Background metadata test",
+ prompt: "Do something",
+ category: "quick",
+ run_in_background: true,
+ load_skills: [],
+ },
+ toolContext
+ )
+
+ // then - output should contain block
+ expect(result).toContain("")
+ expect(result).toContain("session_id: ses_bg_metadata")
+ expect(result).toContain("")
+ }, { timeout: 10000 })
+ })
})