feat: ACP base provider, Copilot integration, eliminate hardcoded provider unions (#170)
* feat(server): add ACP base provider with Claude ACP integration Implement a generic Agent Client Protocol (ACP) base provider that any ACP-compatible agent can extend with minimal code. Includes a concrete Claude ACP implementation via @agentclientprotocol/claude-agent-acp with full parity to the existing Claude Code provider. The base handles subprocess lifecycle, streaming translation, permission bridging, terminal/fs callbacks, listModels, loadSession/resume, and mode/model management. The concrete class only specifies the command, modes, and availability check. * fix: update lockfile signatures and Nix hash * feat: add Copilot ACP provider, eliminate hardcoded provider unions, fix ACP streaming bugs Add Copilot as an ACP provider (copilot --acp), with real modes and models discovered from the ACP server. Fix two ACP base bugs: duplicate assistant text (emit deltas not cumulative) and idle→running→stuck (fire-and-return startTurn). Replace all hardcoded provider string unions with string/manifest- derived values so adding a provider only requires: impl class, manifest entry, registry factory, icon, and E2E config. Add provider docs and Copilot icon. * fix: update lockfile signatures and Nix hash * feat(app): add OpenCode provider icon --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
github-actions[bot] <github-actions[bot]@users.noreply.github.com>
parent
5d89f9444a
commit
a854096c35
@@ -412,7 +412,7 @@ export async function runRunCommand(
|
||||
const callStructuredTurn = async (structuredPrompt: string): Promise<string> => {
|
||||
if (!structuredAgent) {
|
||||
structuredAgent = await client.createAgent({
|
||||
provider: resolvedProviderModel.provider as "claude" | "codex" | "opencode",
|
||||
provider: resolvedProviderModel.provider,
|
||||
cwd,
|
||||
title: options.name,
|
||||
modeId: options.mode,
|
||||
@@ -511,7 +511,7 @@ export async function runRunCommand(
|
||||
|
||||
// Create the agent
|
||||
const agent = await client.createAgent({
|
||||
provider: resolvedProviderModel.provider as "claude" | "codex" | "opencode",
|
||||
provider: resolvedProviderModel.provider,
|
||||
cwd,
|
||||
title: options.name,
|
||||
modeId: options.mode,
|
||||
|
||||
@@ -18,9 +18,9 @@ export interface LoopRunRow {
|
||||
}
|
||||
|
||||
export interface LoopRunOptions extends CommandOptions {
|
||||
provider?: "claude" | "codex" | "opencode";
|
||||
provider?: string;
|
||||
model?: string;
|
||||
verifyProvider?: "claude" | "codex" | "opencode";
|
||||
verifyProvider?: string;
|
||||
verifyModel?: string;
|
||||
verify?: string;
|
||||
verifyCheck?: string[];
|
||||
|
||||
@@ -45,11 +45,11 @@ export interface LoopRecord {
|
||||
name: string | null;
|
||||
prompt: string;
|
||||
cwd: string;
|
||||
provider: "claude" | "codex" | "opencode";
|
||||
provider: string;
|
||||
model: string | null;
|
||||
workerProvider: "claude" | "codex" | "opencode" | null;
|
||||
workerProvider: string | null;
|
||||
workerModel: string | null;
|
||||
verifierProvider: "claude" | "codex" | "opencode" | null;
|
||||
verifierProvider: string | null;
|
||||
verifierModel: string | null;
|
||||
verifyPrompt: string | null;
|
||||
verifyChecks: string[];
|
||||
@@ -122,11 +122,11 @@ export interface LoopStopPayload {
|
||||
export interface LoopRunInput {
|
||||
prompt: string;
|
||||
cwd: string;
|
||||
provider?: "claude" | "codex" | "opencode";
|
||||
provider?: string;
|
||||
model?: string;
|
||||
workerProvider?: "claude" | "codex" | "opencode";
|
||||
workerProvider?: string;
|
||||
workerModel?: string;
|
||||
verifierProvider?: "claude" | "codex" | "opencode";
|
||||
verifierProvider?: string;
|
||||
verifierModel?: string;
|
||||
verifyPrompt?: string;
|
||||
verifyChecks?: string[];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Command } from "commander";
|
||||
import type { CommandOptions, ListResult, OutputSchema } from "../../output/index.js";
|
||||
import { AGENT_PROVIDER_DEFINITIONS } from "@getpaseo/server";
|
||||
|
||||
/** Provider list item for display */
|
||||
export interface ProviderListItem {
|
||||
@@ -9,27 +10,13 @@ export interface ProviderListItem {
|
||||
modes: string;
|
||||
}
|
||||
|
||||
/** Static provider data - providers are built-in and don't require daemon */
|
||||
const PROVIDERS: ProviderListItem[] = [
|
||||
{
|
||||
provider: "claude",
|
||||
status: "available",
|
||||
defaultMode: "default",
|
||||
modes: "plan, default, bypass",
|
||||
},
|
||||
{
|
||||
provider: "codex",
|
||||
status: "available",
|
||||
defaultMode: "auto",
|
||||
modes: "read-only, auto, full-access",
|
||||
},
|
||||
{
|
||||
provider: "opencode",
|
||||
status: "available",
|
||||
defaultMode: "default",
|
||||
modes: "plan, default, bypass",
|
||||
},
|
||||
];
|
||||
/** Derive provider list from the manifest — single source of truth */
|
||||
const PROVIDERS: ProviderListItem[] = AGENT_PROVIDER_DEFINITIONS.map((def) => ({
|
||||
provider: def.id,
|
||||
status: "available",
|
||||
defaultMode: def.defaultModeId ?? "default",
|
||||
modes: def.modes.map((m) => m.label).join(", "),
|
||||
}));
|
||||
|
||||
/** Schema for provider ls output */
|
||||
export const providerLsSchema: OutputSchema<ProviderListItem> = {
|
||||
|
||||
@@ -22,7 +22,7 @@ export type ScheduleTarget =
|
||||
| {
|
||||
type: "new-agent";
|
||||
config: {
|
||||
provider: "claude" | "codex" | "opencode";
|
||||
provider: string;
|
||||
cwd: string;
|
||||
modeId?: string;
|
||||
model?: string;
|
||||
|
||||
Reference in New Issue
Block a user