refactor(athena): rename session_handoff to switch_agent to avoid confusion with /handoff command
Rename across all layers to eliminate naming ambiguity: - Tool: session_handoff → switch_agent - Hook: agent-handoff → agent-switch - Feature: agent-handoff/ → agent-switch/ - Types: SessionHandoffArgs → SwitchAgentArgs, PendingHandoff → PendingSwitch - Functions: setPendingHandoff → setPendingSwitch, consumePendingHandoff → consumePendingSwitch /handoff = inter-session context summary (existing command) switch_agent = intra-session active agent change (our new tool)
This commit is contained in:
+1
-1
@@ -38,7 +38,7 @@ export { createCallOmoAgent } from "./call-omo-agent"
|
||||
export { createAthenaCouncilTool } from "./athena-council"
|
||||
export { createLookAt } from "./look-at"
|
||||
export { createDelegateTask } from "./delegate-task"
|
||||
export { createSessionHandoffTool } from "./session-handoff"
|
||||
export { createSwitchAgentTool } from "./switch-agent"
|
||||
export {
|
||||
createTaskCreateTool,
|
||||
createTaskGetTool,
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { createSessionHandoffTool } from "./tools"
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface SessionHandoffArgs {
|
||||
agent: string
|
||||
context: string
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { createSwitchAgentTool } from "./tools"
|
||||
@@ -1,9 +1,9 @@
|
||||
import { describe, test, expect, beforeEach } from "bun:test"
|
||||
import { createSessionHandoffTool } from "./tools"
|
||||
import { consumePendingHandoff, _resetForTesting as resetHandoff } from "../../features/agent-handoff"
|
||||
import { createSwitchAgentTool } from "./tools"
|
||||
import { consumePendingSwitch, _resetForTesting as resetSwitch } from "../../features/agent-switch"
|
||||
import { getSessionAgent, _resetForTesting as resetSession } from "../../features/claude-code-session-state"
|
||||
|
||||
describe("session_handoff tool", () => {
|
||||
describe("switch_agent tool", () => {
|
||||
const sessionID = "test-session-123"
|
||||
const messageID = "msg-456"
|
||||
const agent = "athena"
|
||||
@@ -16,25 +16,25 @@ describe("session_handoff tool", () => {
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
resetHandoff()
|
||||
resetSwitch()
|
||||
resetSession()
|
||||
})
|
||||
|
||||
//#given valid atlas handoff args
|
||||
//#given valid atlas switch args
|
||||
//#when execute is called
|
||||
//#then it stores pending handoff and updates session agent
|
||||
test("should queue handoff to atlas", async () => {
|
||||
const tool = createSessionHandoffTool()
|
||||
//#then it stores pending switch and updates session agent
|
||||
test("should queue switch to atlas", async () => {
|
||||
const tool = createSwitchAgentTool()
|
||||
const result = await tool.execute(
|
||||
{ agent: "atlas", context: "Fix the auth bug based on council findings" },
|
||||
toolContext
|
||||
)
|
||||
|
||||
expect(result).toContain("atlas")
|
||||
expect(result).toContain("Handoff queued")
|
||||
expect(result).toContain("switch")
|
||||
|
||||
const handoff = consumePendingHandoff(sessionID)
|
||||
expect(handoff).toEqual({
|
||||
const entry = consumePendingSwitch(sessionID)
|
||||
expect(entry).toEqual({
|
||||
agent: "atlas",
|
||||
context: "Fix the auth bug based on council findings",
|
||||
})
|
||||
@@ -42,50 +42,50 @@ describe("session_handoff tool", () => {
|
||||
expect(getSessionAgent(sessionID)).toBe("atlas")
|
||||
})
|
||||
|
||||
//#given valid prometheus handoff args
|
||||
//#given valid prometheus switch args
|
||||
//#when execute is called
|
||||
//#then it stores pending handoff for prometheus
|
||||
test("should queue handoff to prometheus", async () => {
|
||||
const tool = createSessionHandoffTool()
|
||||
//#then it stores pending switch for prometheus
|
||||
test("should queue switch to prometheus", async () => {
|
||||
const tool = createSwitchAgentTool()
|
||||
const result = await tool.execute(
|
||||
{ agent: "Prometheus", context: "Create a plan for the refactoring" },
|
||||
toolContext
|
||||
)
|
||||
|
||||
expect(result).toContain("prometheus")
|
||||
expect(result).toContain("Handoff queued")
|
||||
expect(result).toContain("switch")
|
||||
|
||||
const handoff = consumePendingHandoff(sessionID)
|
||||
expect(handoff?.agent).toBe("prometheus")
|
||||
const entry = consumePendingSwitch(sessionID)
|
||||
expect(entry?.agent).toBe("prometheus")
|
||||
})
|
||||
|
||||
//#given an invalid agent name
|
||||
//#when execute is called
|
||||
//#then it returns an error
|
||||
test("should reject invalid agent names", async () => {
|
||||
const tool = createSessionHandoffTool()
|
||||
const tool = createSwitchAgentTool()
|
||||
const result = await tool.execute(
|
||||
{ agent: "librarian", context: "Some context" },
|
||||
toolContext
|
||||
)
|
||||
|
||||
expect(result).toContain("Invalid handoff target")
|
||||
expect(result).toContain("Invalid switch target")
|
||||
expect(result).toContain("librarian")
|
||||
expect(consumePendingHandoff(sessionID)).toBeUndefined()
|
||||
expect(consumePendingSwitch(sessionID)).toBeUndefined()
|
||||
})
|
||||
|
||||
//#given agent name with different casing
|
||||
//#when execute is called
|
||||
//#then it normalizes to lowercase
|
||||
test("should handle case-insensitive agent names", async () => {
|
||||
const tool = createSessionHandoffTool()
|
||||
const tool = createSwitchAgentTool()
|
||||
await tool.execute(
|
||||
{ agent: "ATLAS", context: "Fix things" },
|
||||
toolContext
|
||||
)
|
||||
|
||||
const handoff = consumePendingHandoff(sessionID)
|
||||
expect(handoff?.agent).toBe("atlas")
|
||||
const entry = consumePendingSwitch(sessionID)
|
||||
expect(entry?.agent).toBe("atlas")
|
||||
expect(getSessionAgent(sessionID)).toBe("atlas")
|
||||
})
|
||||
})
|
||||
@@ -1,37 +1,37 @@
|
||||
import { tool, type ToolDefinition } from "@opencode-ai/plugin"
|
||||
import { setPendingHandoff } from "../../features/agent-handoff"
|
||||
import { setPendingSwitch } from "../../features/agent-switch"
|
||||
import { updateSessionAgent } from "../../features/claude-code-session-state"
|
||||
import type { SessionHandoffArgs } from "./types"
|
||||
import type { SwitchAgentArgs } from "./types"
|
||||
|
||||
const DESCRIPTION =
|
||||
"Switch the active session agent. After calling this tool, the session will transition to the specified agent " +
|
||||
"with the provided context as its starting prompt. Use this to hand off work to another agent " +
|
||||
"(e.g., Atlas for fixes, Prometheus for planning). The handoff executes when the current agent's turn completes."
|
||||
"with the provided context as its starting prompt. Use this to route work to another agent " +
|
||||
"(e.g., Atlas for fixes, Prometheus for planning). The switch executes when the current agent's turn completes."
|
||||
|
||||
const ALLOWED_AGENTS = new Set(["atlas", "prometheus", "sisyphus", "hephaestus"])
|
||||
|
||||
export function createSessionHandoffTool(): ToolDefinition {
|
||||
export function createSwitchAgentTool(): ToolDefinition {
|
||||
return tool({
|
||||
description: DESCRIPTION,
|
||||
args: {
|
||||
agent: tool.schema
|
||||
.string()
|
||||
.describe("Target agent name to hand off to (e.g., 'atlas', 'prometheus')"),
|
||||
.describe("Target agent name to switch to (e.g., 'atlas', 'prometheus')"),
|
||||
context: tool.schema
|
||||
.string()
|
||||
.describe("Context message for the target agent — include confirmed findings, the original question, and what action to take"),
|
||||
},
|
||||
async execute(args: SessionHandoffArgs, toolContext) {
|
||||
async execute(args: SwitchAgentArgs, toolContext) {
|
||||
const agentName = args.agent.toLowerCase()
|
||||
|
||||
if (!ALLOWED_AGENTS.has(agentName)) {
|
||||
return `Invalid handoff target: "${args.agent}". Allowed agents: ${[...ALLOWED_AGENTS].join(", ")}`
|
||||
return `Invalid switch target: "${args.agent}". Allowed agents: ${[...ALLOWED_AGENTS].join(", ")}`
|
||||
}
|
||||
|
||||
updateSessionAgent(toolContext.sessionID, agentName)
|
||||
setPendingHandoff(toolContext.sessionID, agentName, args.context)
|
||||
setPendingSwitch(toolContext.sessionID, agentName, args.context)
|
||||
|
||||
return `Handoff queued. Session will switch to ${agentName} when your turn completes.`
|
||||
return `Agent switch queued. Session will switch to ${agentName} when your turn completes.`
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface SwitchAgentArgs {
|
||||
agent: string
|
||||
context: string
|
||||
}
|
||||
Reference in New Issue
Block a user