chore: initialize qiming workspace repository

This commit is contained in:
Codex
2026-05-29 14:22:48 +08:00
commit bfd67a0f2c
10750 changed files with 1885711 additions and 0 deletions

View File

@@ -0,0 +1,174 @@
# ACP (Agent Client Protocol) Implementation
This directory contains a clean, protocol-compliant implementation of the [Agent Client Protocol](https://agentclientprotocol.com/) for opencode.
## Architecture
The implementation follows a clean separation of concerns:
### Core Components
- **`agent.ts`** - Implements the `Agent` interface from `@agentclientprotocol/sdk`
- Handles initialization and capability negotiation
- Manages session lifecycle (`session/new`, `session/load`)
- Processes prompts and returns responses
- Properly implements ACP protocol v1
- **`client.ts`** - Implements the `Client` interface for client-side capabilities
- File operations (`readTextFile`, `writeTextFile`)
- Permission requests (auto-approves for now)
- Terminal support (stub implementation)
- **`session.ts`** - Session state management
- Creates and tracks ACP sessions
- Maps ACP sessions to internal opencode sessions
- Maintains working directory context
- Handles MCP server configurations
- **`server.ts`** - ACP server startup and lifecycle
- Sets up JSON-RPC over stdio using the official library
- Manages graceful shutdown on SIGTERM/SIGINT
- Provides Instance context for the agent
- **`types.ts`** - Type definitions for internal use
## Usage
### Command Line
```bash
# Start the ACP server in the current directory
opencode acp
# Start in a specific directory
opencode acp --cwd /path/to/project
```
### Question Tool Opt-In
ACP excludes `QuestionTool` by default.
```bash
OPENCODE_ENABLE_QUESTION_TOOL=1 opencode acp
```
Enable this only for ACP clients that support interactive question prompts.
### Programmatic
```typescript
import { ACPServer } from "./acp/server"
await ACPServer.start()
```
### Integration with Zed
Add to your Zed configuration (`~/.config/zed/settings.json`):
```json
{
"agent_servers": {
"OpenCode": {
"command": "opencode",
"args": ["acp"]
}
}
}
```
## Protocol Compliance
This implementation follows the ACP specification v1:
**Initialization**
- Proper `initialize` request/response with protocol version negotiation
- Capability advertisement (`agentCapabilities`)
- Authentication support (stub)
**Session Management**
- `session/new` - Create new conversation sessions
- `session/load` - Resume existing sessions (basic support)
- Working directory context (`cwd`)
- MCP server configuration support
**Prompting**
- `session/prompt` - Process user messages
- Content block handling (text, resources)
- Response with stop reasons
**Client Capabilities**
- File read/write operations
- Permission requests
- Terminal support (stub for future)
## Current Limitations
### Not Yet Implemented
1. **Streaming Responses** - Currently returns complete responses instead of streaming via `session/update` notifications
2. **Tool Call Reporting** - Doesn't report tool execution progress
3. **Session Modes** - No mode switching support yet
4. **Authentication** - No actual auth implementation
5. **Terminal Support** - Placeholder only
6. **Session Persistence** - `session/load` doesn't restore actual conversation history
### Future Enhancements
- **Real-time Streaming**: Implement `session/update` notifications for progressive responses
- **Tool Call Visibility**: Report tool executions as they happen
- **Session Persistence**: Save and restore full conversation history
- **Mode Support**: Implement different operational modes (ask, code, etc.)
- **Enhanced Permissions**: More sophisticated permission handling
- **Terminal Integration**: Full terminal support via opencode's bash tool
## Testing
```bash
# Run ACP tests
bun test test/acp.test.ts
# Test manually with stdio
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1}}' | opencode acp
```
## Design Decisions
### Why the Official Library?
We use `@agentclientprotocol/sdk` instead of implementing JSON-RPC ourselves because:
- Ensures protocol compliance
- Handles edge cases and future protocol versions
- Reduces maintenance burden
- Works with other ACP clients automatically
### Clean Architecture
Each component has a single responsibility:
- **Agent** = Protocol interface
- **Client** = Client-side operations
- **Session** = State management
- **Server** = Lifecycle and I/O
This makes the codebase maintainable and testable.
### Mapping to OpenCode
ACP sessions map cleanly to opencode's internal session model:
- ACP `session/new` → creates internal Session
- ACP `session/prompt` → uses SessionPrompt.prompt()
- Working directory context preserved per-session
- Tool execution uses existing ToolRegistry
## References
- [ACP Specification](https://agentclientprotocol.com/)
- [TypeScript Library](https://github.com/agentclientprotocol/typescript-sdk)
- [Protocol Examples](https://github.com/agentclientprotocol/typescript-sdk/tree/main/src/examples)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,129 @@
import { RequestError, type McpServer } from "@agentclientprotocol/sdk"
import type { ACPSessionState } from "./types"
import { Log } from "@/util"
import type { OpencodeClient } from "@opencode-ai/sdk/v2"
const log = Log.create({ service: "acp-session-manager" })
export class ACPSessionManager {
private sessions = new Map<string, ACPSessionState>()
private sdk: OpencodeClient
constructor(sdk: OpencodeClient) {
this.sdk = sdk
}
tryGet(sessionId: string): ACPSessionState | undefined {
return this.sessions.get(sessionId)
}
/**
* 创建 ACP 侧会话镜像,并可选保存客户端在 `session/new` 里通过 `_meta.systemPrompt` 传入的自定义系统提示。
* 该字段不会写入 OpenCode session 实体,仅在 ACP 桥接层保留,供后续 `sdk.session.prompt` 注入 `system`。
*/
async create(
cwd: string,
mcpServers: McpServer[],
model?: ACPSessionState["model"],
systemPrompt?: ACPSessionState["systemPrompt"],
): Promise<ACPSessionState> {
const session = await this.sdk.session
.create(
{
directory: cwd,
},
{ throwOnError: true },
)
.then((x) => x.data!)
const sessionId = session.id
const resolvedModel = model
const state: ACPSessionState = {
id: sessionId,
cwd,
mcpServers,
createdAt: new Date(),
model: resolvedModel,
systemPrompt,
}
log.info("creating_session", {
sessionId,
hasSystemPrompt: systemPrompt !== undefined,
})
this.sessions.set(sessionId, state)
return state
}
async load(
sessionId: string,
cwd: string,
mcpServers: McpServer[],
model?: ACPSessionState["model"],
): Promise<ACPSessionState> {
const session = await this.sdk.session
.get(
{
sessionID: sessionId,
directory: cwd,
},
{ throwOnError: true },
)
.then((x) => x.data!)
const resolvedModel = model
const state: ACPSessionState = {
id: sessionId,
cwd,
mcpServers,
createdAt: new Date(session.time.created),
model: resolvedModel,
}
log.info("loading_session", { state })
this.sessions.set(sessionId, state)
return state
}
get(sessionId: string): ACPSessionState {
const session = this.sessions.get(sessionId)
if (!session) {
log.error("session not found", { sessionId })
throw RequestError.invalidParams(JSON.stringify({ error: `Session not found: ${sessionId}` }))
}
return session
}
getModel(sessionId: string) {
const session = this.get(sessionId)
return session.model
}
setModel(sessionId: string, model: ACPSessionState["model"]) {
const session = this.get(sessionId)
session.model = model
this.sessions.set(sessionId, session)
return session
}
getVariant(sessionId: string) {
const session = this.get(sessionId)
return session.variant
}
setVariant(sessionId: string, variant?: string) {
const session = this.get(sessionId)
session.variant = variant
this.sessions.set(sessionId, session)
return session
}
setMode(sessionId: string, modeId: string) {
const session = this.get(sessionId)
session.modeId = modeId
this.sessions.set(sessionId, session)
return session
}
}

View File

@@ -0,0 +1,33 @@
import type { McpServer } from "@agentclientprotocol/sdk"
import type { OpencodeClient } from "@opencode-ai/sdk/v2"
import type { ProviderID, ModelID } from "../provider/schema"
/**
* ACP `session/new` 在 `params._meta.systemPrompt` 里下发的系统提示(与 claude-code-acp 等客户端约定一致)。
* - 字符串:作为本条会话每次 `prompt` 传给后端的 `system` 全文;
* - `{ append }`:仅追加片段,由 HTTP `session.prompt` 的 `system` 字段传入,在 LLM 层与 agent / provider 提示合并。
*/
export type ACPSystemPromptMeta = string | { append: string }
export interface ACPSessionState {
id: string
cwd: string
mcpServers: McpServer[]
createdAt: Date
model?: {
providerID: ProviderID
modelID: ModelID
}
variant?: string
modeId?: string
/** 来自 `newSession` 的 `_meta.systemPrompt`,在每次非 slash-command 的 `prompt` 中映射为 SDK 的 `system` */
systemPrompt?: ACPSystemPromptMeta
}
export interface ACPConfig {
sdk: OpencodeClient
defaultModel?: {
providerID: ProviderID
modelID: ModelID
}
}