Extract client SDK package (#1052)
* Extract client SDK package * Polish SDK client identity defaults * Build client before dependent CI jobs * Restore daemon client server export * Extract protocol and client SDK packages * Fix provider override schema validation * Fix app test daemon client imports * Simplify workspace build targets * Fix CLI test server build bootstrap * Run SDK package tests in CI * Fix rebase package split drift * Restore lockfile registry metadata * Update SDK config test for prompt default * Move terminal stream router test to client package * Fix rebase drift for protocol imports * Fix SDK agent capability fixture * Restore legacy server client exports * Fix server export compatibility test * Advertise custom mode icon client capability * Remove server daemon-client exports * Format rebased mode control import * Fix rebase drift for protocol imports Files added by upstream PRs (#893, #1147, #1154) referenced the pre-split shared/ paths that this branch moves into @getpaseo/protocol. Redirect those imports to the protocol package so typecheck stays green after the rebase.
This commit is contained in:
@@ -0,0 +1,280 @@
|
||||
import { z } from "zod";
|
||||
import type { AgentMode } from "./agent-types.js";
|
||||
|
||||
export type AgentModeColorTier = "safe" | "moderate" | "dangerous" | "planning" | `#${string}`;
|
||||
// Open string by design: the client looks icons up in a registry and falls back
|
||||
// to a default for unknown values. Daemon downgrades unknown icons for clients
|
||||
// that pre-date the open-string contract (see CLIENT_CAPS.customModeIcons).
|
||||
export type AgentModeIcon = string;
|
||||
|
||||
export interface AgentModeVisuals {
|
||||
icon: AgentModeIcon;
|
||||
colorTier: AgentModeColorTier;
|
||||
}
|
||||
|
||||
export type AgentProviderModeDefinition = Omit<AgentMode, "icon" | "colorTier"> &
|
||||
AgentModeVisuals & {
|
||||
// Marks the provider's most-permissioned no-prompt mode. Selecting it means tools run without approval; the runtime mechanism is provider-specific.
|
||||
isUnattended?: boolean;
|
||||
};
|
||||
|
||||
// TODO: `modes` should not be static. Providers (especially ACP) report their
|
||||
// own modes at runtime via session/new. We should fetch modes from the provider
|
||||
// as source of truth and enrich with UI metadata (icons, colorTier) on top.
|
||||
export interface AgentProviderDefinition {
|
||||
id: string;
|
||||
label: string;
|
||||
description: string;
|
||||
defaultModeId: string | null;
|
||||
modes: AgentProviderModeDefinition[];
|
||||
voice?: {
|
||||
enabled: boolean;
|
||||
defaultModeId: string;
|
||||
defaultModel?: string;
|
||||
};
|
||||
}
|
||||
|
||||
const CLAUDE_MODES: AgentProviderModeDefinition[] = [
|
||||
{
|
||||
id: "default",
|
||||
label: "Always Ask",
|
||||
description: "Prompts for permission the first time a tool is used",
|
||||
icon: "ShieldCheck",
|
||||
colorTier: "safe",
|
||||
},
|
||||
{
|
||||
id: "auto",
|
||||
label: "Auto mode",
|
||||
description: "Uses a model classifier to review permission prompts automatically",
|
||||
icon: "ShieldQuestionMark",
|
||||
colorTier: "moderate",
|
||||
},
|
||||
{
|
||||
id: "acceptEdits",
|
||||
label: "Accept File Edits",
|
||||
description: "Automatically approves edit-focused tools without prompting",
|
||||
icon: "ShieldAlert",
|
||||
colorTier: "moderate",
|
||||
},
|
||||
{
|
||||
id: "plan",
|
||||
label: "Plan Mode",
|
||||
description: "Analyze the codebase without executing tools or edits",
|
||||
icon: "ShieldCheck",
|
||||
colorTier: "planning",
|
||||
},
|
||||
{
|
||||
id: "bypassPermissions",
|
||||
label: "Bypass",
|
||||
description: "Skip all permission prompts (use with caution)",
|
||||
icon: "ShieldAlert",
|
||||
colorTier: "dangerous",
|
||||
isUnattended: true,
|
||||
},
|
||||
];
|
||||
|
||||
const CODEX_MODES: AgentProviderModeDefinition[] = [
|
||||
{
|
||||
id: "auto",
|
||||
label: "Default Permissions",
|
||||
description: "Edit files and run commands with Codex's default approval flow.",
|
||||
icon: "ShieldAlert",
|
||||
colorTier: "moderate",
|
||||
},
|
||||
{
|
||||
id: "auto-review",
|
||||
label: "Auto-review",
|
||||
description:
|
||||
"Same workspace-write permissions as Default, but eligible `on-request` approvals are routed through the auto-reviewer subagent.",
|
||||
icon: "ShieldQuestionMark",
|
||||
colorTier: "moderate",
|
||||
},
|
||||
{
|
||||
id: "full-access",
|
||||
label: "Full Access",
|
||||
description: "Edit files, run commands, and access the network without additional prompts.",
|
||||
icon: "ShieldAlert",
|
||||
colorTier: "dangerous",
|
||||
isUnattended: true,
|
||||
},
|
||||
];
|
||||
|
||||
const COPILOT_MODES: AgentProviderModeDefinition[] = [
|
||||
{
|
||||
id: "https://agentclientprotocol.com/protocol/session-modes#agent",
|
||||
label: "Agent",
|
||||
description: "Default agent mode for conversational interactions",
|
||||
icon: "ShieldAlert",
|
||||
colorTier: "moderate",
|
||||
},
|
||||
{
|
||||
id: "https://agentclientprotocol.com/protocol/session-modes#plan",
|
||||
label: "Plan",
|
||||
description: "Plan mode for creating and executing multi-step plans",
|
||||
icon: "ShieldCheck",
|
||||
colorTier: "planning",
|
||||
},
|
||||
{
|
||||
id: "allow-all",
|
||||
label: "Allow All",
|
||||
description: "Automatically approves all Copilot tool, path, and URL requests.",
|
||||
icon: "ShieldOff",
|
||||
colorTier: "dangerous",
|
||||
isUnattended: true,
|
||||
},
|
||||
];
|
||||
|
||||
const OPENCODE_MODES: AgentProviderModeDefinition[] = [
|
||||
{
|
||||
id: "build",
|
||||
label: "Build",
|
||||
description: "Allows edits and tool execution for implementation work",
|
||||
icon: "Bot",
|
||||
colorTier: "moderate",
|
||||
},
|
||||
{
|
||||
id: "plan",
|
||||
label: "Plan",
|
||||
description: "Read-only planning mode that avoids file edits",
|
||||
icon: "Bot",
|
||||
colorTier: "planning",
|
||||
},
|
||||
];
|
||||
|
||||
const MOCK_LOAD_TEST_MODES: AgentProviderModeDefinition[] = [
|
||||
{
|
||||
id: "load-test",
|
||||
label: "Load Test",
|
||||
description: "Streams repeated markdown, reasoning, and tool calls for app stress testing",
|
||||
icon: "ShieldOff",
|
||||
colorTier: "dangerous",
|
||||
},
|
||||
];
|
||||
|
||||
const MOCK_SLOW_MODES: AgentProviderModeDefinition[] = [
|
||||
{
|
||||
id: "default",
|
||||
label: "Default",
|
||||
description: "Dev-only mode for the mock slow provider",
|
||||
icon: "ShieldOff",
|
||||
colorTier: "dangerous",
|
||||
},
|
||||
];
|
||||
|
||||
export const AGENT_PROVIDER_DEFINITIONS: AgentProviderDefinition[] = [
|
||||
{
|
||||
id: "claude",
|
||||
label: "Claude",
|
||||
description: "Anthropic's multi-tool assistant with MCP support, streaming, and deep reasoning",
|
||||
defaultModeId: "default",
|
||||
modes: CLAUDE_MODES,
|
||||
voice: {
|
||||
enabled: true,
|
||||
defaultModeId: "default",
|
||||
defaultModel: "haiku",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "codex",
|
||||
label: "Codex",
|
||||
description: "OpenAI's Codex workspace agent with sandbox controls and optional network access",
|
||||
defaultModeId: "auto",
|
||||
modes: CODEX_MODES,
|
||||
voice: {
|
||||
enabled: true,
|
||||
defaultModeId: "auto",
|
||||
defaultModel: "gpt-5.4-mini",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "copilot",
|
||||
label: "Copilot",
|
||||
description: "GitHub Copilot via Agent Client Protocol with dynamic modes and session support",
|
||||
defaultModeId: "https://agentclientprotocol.com/protocol/session-modes#agent",
|
||||
modes: COPILOT_MODES,
|
||||
},
|
||||
{
|
||||
id: "opencode",
|
||||
label: "OpenCode",
|
||||
description: "Open-source coding assistant with multi-provider model support",
|
||||
defaultModeId: "build",
|
||||
modes: OPENCODE_MODES,
|
||||
voice: {
|
||||
enabled: true,
|
||||
defaultModeId: "build",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "pi",
|
||||
label: "Pi",
|
||||
description: "Minimal terminal-based coding agent with multi-provider LLM support",
|
||||
defaultModeId: null,
|
||||
modes: [],
|
||||
},
|
||||
];
|
||||
|
||||
export const DEV_AGENT_PROVIDER_DEFINITIONS: AgentProviderDefinition[] = [
|
||||
{
|
||||
id: "mock",
|
||||
label: "Mock Load Test",
|
||||
description:
|
||||
"Development-only provider that emits synthetic agent traffic for performance tests",
|
||||
defaultModeId: "load-test",
|
||||
modes: MOCK_LOAD_TEST_MODES,
|
||||
},
|
||||
{
|
||||
id: "mock-slow",
|
||||
label: "Mock Slow Provider",
|
||||
description: "Dev-only: hangs during model discovery to test loading and timeout UI",
|
||||
defaultModeId: "default",
|
||||
modes: MOCK_SLOW_MODES,
|
||||
},
|
||||
];
|
||||
|
||||
export function getAgentProviderDefinition(
|
||||
provider: string,
|
||||
definitions: AgentProviderDefinition[] = [
|
||||
...AGENT_PROVIDER_DEFINITIONS,
|
||||
...DEV_AGENT_PROVIDER_DEFINITIONS,
|
||||
],
|
||||
): AgentProviderDefinition {
|
||||
const definition = definitions.find((entry) => entry.id === provider);
|
||||
if (!definition) {
|
||||
throw new Error(`Unknown agent provider: ${provider}`);
|
||||
}
|
||||
return definition;
|
||||
}
|
||||
|
||||
export const BUILTIN_PROVIDER_IDS = AGENT_PROVIDER_DEFINITIONS.map((d) => d.id);
|
||||
export const AGENT_PROVIDER_IDS = BUILTIN_PROVIDER_IDS;
|
||||
|
||||
export const AgentProviderSchema = z.string();
|
||||
|
||||
export function isValidAgentProvider(
|
||||
value: string,
|
||||
validIds: Iterable<string> = BUILTIN_PROVIDER_IDS,
|
||||
): boolean {
|
||||
return Array.isArray(validIds) ? validIds.includes(value) : new Set(validIds).has(value);
|
||||
}
|
||||
|
||||
export function getUnattendedModeId(
|
||||
provider: string,
|
||||
definitions: AgentProviderDefinition[] = [
|
||||
...AGENT_PROVIDER_DEFINITIONS,
|
||||
...DEV_AGENT_PROVIDER_DEFINITIONS,
|
||||
],
|
||||
): string | undefined {
|
||||
const definition = definitions.find((entry) => entry.id === provider);
|
||||
return definition?.modes.find((mode) => mode.isUnattended)?.id;
|
||||
}
|
||||
|
||||
export function getModeVisuals(
|
||||
provider: string,
|
||||
modeId: string,
|
||||
definitions: AgentProviderDefinition[],
|
||||
): AgentModeVisuals | undefined {
|
||||
const definition = definitions.find((entry) => entry.id === provider);
|
||||
const mode = definition?.modes.find((m) => m.id === modeId);
|
||||
if (!mode) return undefined;
|
||||
return { icon: mode.icon, colorTier: mode.colorTier };
|
||||
}
|
||||
Reference in New Issue
Block a user