refactor: implement proper ACP session modes and fix type safety

Replace client-side permission mode hack with proper ACP session modes:
- Add typed Client interface implementation to ACPClient
- Import proper types from ACP SDK (SessionNotification, RequestPermissionRequest, etc.)
- Fix critical sessionUpdate bug where params.update was undefined
- Replace PermissionMode with SessionMode from ACP spec
- Add getCurrentMode(), getAvailableModes(), setSessionMode() to AgentManager
- Update MCP tools: replace set_agent_permission_mode with set_agent_mode
- Add session_state message type to send live agents/commands to client
- Update UI to display session mode badges (ask/code/architect)
- Remove all permissionsMode references from tests

Agents now have ONE mode (session mode) that controls their behavior,
system prompts, and tool availability - not separate permission/session modes.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
Mohamed Boudra
2025-10-21 15:49:55 +02:00
co-authored by Claude Happy
parent 4f2c2fc14a
commit cf977968c6
9 changed files with 393 additions and 120 deletions
@@ -123,6 +123,12 @@ export const AgentCreatedMessageSchema = z.object({
agentId: z.string(),
status: z.string(),
type: z.literal("claude"),
currentModeId: z.string().optional(),
availableModes: z.array(z.object({
id: z.string(),
name: z.string(),
description: z.string().nullable().optional(),
})).optional(),
}),
});
@@ -151,6 +157,38 @@ export const AgentStatusMessageSchema = z.object({
}),
});
export const SessionStateMessageSchema = z.object({
type: z.literal("session_state"),
payload: z.object({
agents: z.array(
z.object({
id: z.string(),
status: z.string(),
createdAt: z.date(),
type: z.literal("claude"),
sessionId: z.string().optional(),
error: z.string().optional(),
currentModeId: z.string().optional(),
availableModes: z.array(z.object({
id: z.string(),
name: z.string(),
description: z.string().nullable().optional(),
})).optional(),
})
),
commands: z.array(
z.object({
id: z.string(),
name: z.string(),
workingDirectory: z.string(),
currentCommand: z.string(),
isDead: z.boolean(),
exitCode: z.number().nullable(),
})
),
}),
});
export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
ActivityLogMessageSchema,
AssistantChunkMessageSchema,
@@ -162,6 +200,7 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
AgentCreatedMessageSchema,
AgentUpdateMessageSchema,
AgentStatusMessageSchema,
SessionStateMessageSchema,
]);
export type SessionOutboundMessage = z.infer<
@@ -179,6 +218,7 @@ export type ConversationLoadedMessage = z.infer<typeof ConversationLoadedMessage
export type AgentCreatedMessage = z.infer<typeof AgentCreatedMessageSchema>;
export type AgentUpdateMessage = z.infer<typeof AgentUpdateMessageSchema>;
export type AgentStatusMessage = z.infer<typeof AgentStatusMessageSchema>;
export type SessionStateMessage = z.infer<typeof SessionStateMessageSchema>;
// Type exports for payload types
export type ActivityLogPayload = z.infer<typeof ActivityLogPayloadSchema>;