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:
co-authored by
Claude
Happy
parent
4f2c2fc14a
commit
cf977968c6
@@ -1,9 +1,23 @@
|
||||
import { Terminal, Bot } from "lucide-react";
|
||||
import { Terminal, Bot, Code } from "lucide-react";
|
||||
import type { AgentStatus } from "../../server/acp/types.js";
|
||||
|
||||
export interface ActiveProcessesProps {
|
||||
terminals: Array<{ id: string; name: string }>;
|
||||
agents: Array<{ id: string; status: AgentStatus; type: "claude" }>;
|
||||
agents: Array<{
|
||||
id: string;
|
||||
status: AgentStatus;
|
||||
type: "claude";
|
||||
currentModeId?: string;
|
||||
availableModes?: Array<{ id: string; name: string; description?: string | null }>;
|
||||
}>;
|
||||
commands: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
workingDirectory: string;
|
||||
currentCommand: string;
|
||||
isDead: boolean;
|
||||
exitCode: number | null;
|
||||
}>;
|
||||
activeProcessId: string | null;
|
||||
activeProcessType: "terminal" | "agent" | null;
|
||||
onSelectProcess: (id: string, type: "terminal" | "agent") => void;
|
||||
@@ -33,9 +47,27 @@ function getAgentStatusLabel(status: AgentStatus): string {
|
||||
return status;
|
||||
}
|
||||
|
||||
function getModeName(modeId?: string, availableModes?: Array<{ id: string; name: string }>): string {
|
||||
if (!modeId) return "unknown";
|
||||
const mode = availableModes?.find(m => m.id === modeId);
|
||||
return mode?.name || modeId;
|
||||
}
|
||||
|
||||
function getModeColor(modeId?: string): string {
|
||||
if (!modeId) return "#9ca3af"; // gray
|
||||
|
||||
// Color based on common mode types
|
||||
if (modeId.includes("ask")) return "#f59e0b"; // orange - asks permission
|
||||
if (modeId.includes("code")) return "#22c55e"; // green - writes code
|
||||
if (modeId.includes("architect") || modeId.includes("plan")) return "#3b82f6"; // blue - plans
|
||||
|
||||
return "#9ca3af"; // gray - unknown
|
||||
}
|
||||
|
||||
export function ActiveProcesses({
|
||||
terminals,
|
||||
agents,
|
||||
commands,
|
||||
activeProcessId,
|
||||
activeProcessType,
|
||||
onSelectProcess,
|
||||
@@ -73,28 +105,70 @@ export function ActiveProcesses({
|
||||
</button>
|
||||
))}
|
||||
|
||||
{agents.map((agent) => (
|
||||
<button
|
||||
key={`agent-${agent.id}`}
|
||||
className={`process-pill agent ${
|
||||
activeProcessType === "agent" && activeProcessId === agent.id
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => onSelectProcess(agent.id, "agent")}
|
||||
type="button"
|
||||
data-status={agent.status}
|
||||
{agents.map((agent) => {
|
||||
const modeName = getModeName(agent.currentModeId, agent.availableModes);
|
||||
const modesList = agent.availableModes?.map(m => m.name).join(", ") || "unknown";
|
||||
|
||||
return (
|
||||
<button
|
||||
key={`agent-${agent.id}`}
|
||||
className={`process-pill agent ${
|
||||
activeProcessType === "agent" && activeProcessId === agent.id
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => onSelectProcess(agent.id, "agent")}
|
||||
type="button"
|
||||
data-status={agent.status}
|
||||
title={`Status: ${getAgentStatusLabel(agent.status)} | Mode: ${modeName} | Available: ${modesList}`}
|
||||
>
|
||||
<Bot size={14} />
|
||||
<span className="process-name">
|
||||
{agent.id.substring(0, 8)}
|
||||
</span>
|
||||
<span
|
||||
className={`process-status-indicator ${agent.status}`}
|
||||
style={{ backgroundColor: getAgentStatusColor(agent.status) }}
|
||||
title={getAgentStatusLabel(agent.status)}
|
||||
/>
|
||||
<span
|
||||
className="session-mode-badge"
|
||||
style={{
|
||||
backgroundColor: getModeColor(agent.currentModeId),
|
||||
opacity: 0.3,
|
||||
width: "6px",
|
||||
height: "6px",
|
||||
borderRadius: "50%",
|
||||
marginLeft: "4px",
|
||||
}}
|
||||
title={`Mode: ${modeName}`}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
{commands.map((command) => (
|
||||
<div
|
||||
key={`command-${command.id}`}
|
||||
className="process-pill command"
|
||||
title={`${command.currentCommand} | ${command.workingDirectory}${command.isDead ? ` | Exit code: ${command.exitCode}` : ""}`}
|
||||
>
|
||||
<Bot size={14} />
|
||||
<Code size={14} />
|
||||
<span className="process-name">
|
||||
{agent.id.substring(0, 8)}
|
||||
{command.name || command.currentCommand.substring(0, 20)}
|
||||
</span>
|
||||
<span
|
||||
className={`process-status-indicator ${agent.status}`}
|
||||
style={{ backgroundColor: getAgentStatusColor(agent.status) }}
|
||||
title={getAgentStatusLabel(agent.status)}
|
||||
className={`process-status-indicator ${command.isDead ? "dead" : "running"}`}
|
||||
style={{
|
||||
backgroundColor: command.isDead
|
||||
? command.exitCode === 0
|
||||
? "#22c55e"
|
||||
: "#ef4444"
|
||||
: "#3b82f6",
|
||||
}}
|
||||
title={command.isDead ? `Exited: ${command.exitCode}` : "Running"}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user