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:
ismeth
2026-02-13 13:18:07 +01:00
committed by YeonGyu-Kim
parent 7db0f110ee
commit 84185490f9
22 changed files with 139 additions and 139 deletions
+1 -1
View File
@@ -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
View File
@@ -1 +0,0 @@
export { createSessionHandoffTool } from "./tools"
-4
View File
@@ -1,4 +0,0 @@
export interface SessionHandoffArgs {
agent: string
context: string
}
+1
View File
@@ -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.`
},
})
}
+4
View File
@@ -0,0 +1,4 @@
export interface SwitchAgentArgs {
agent: string
context: string
}