@harmonyos-arkts/opencode-acp 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # Harmony-ACP
2
+
3
+ Enhanced ACP (Agent Client Protocol) server for [OpenCode](https://github.com/sst/opencode). Runs as an independent process and provides **subagent visibility** and **full traffic logging** that OpenCode's built-in ACP lacks.
4
+
5
+ ## Why
6
+
7
+ OpenCode's built-in ACP has two key limitations:
8
+
9
+ 1. **Invisible subagents**: When an agent spawns subagents (via the Task tool), those child sessions' events — tool calls, text output, permission requests — are invisible to ACP clients like Zed or VSCode. The built-in `SessionManager` only registers top-level sessions, silently dropping all events from child sessions.
10
+
11
+ 2. **No traffic visibility**: There's no way to inspect what's flowing between the ACP client and OpenCode — making debugging difficult.
12
+
13
+ Harmony-ACP solves both without modifying any OpenCode source code.
14
+
15
+ ## How It Works
16
+
17
+ ```
18
+ ┌─────────────────────────┐
19
+ │ Editor (Zed / VSCode) │
20
+ └────────────┬────────────┘
21
+ │ stdio (JSON-RPC)
22
+ ┌────────────▼────────────┐
23
+ │ Harmony-ACP │ ← Independent process
24
+ │ - Auto-discovers │
25
+ │ child sessions │
26
+ │ - Routes child events │
27
+ │ with own sessionId │
28
+ │ - Logs all traffic │
29
+ │ - Assembles message │
30
+ │ deltas │
31
+ └────────────┬────────────┘
32
+ │ HTTP / SSE
33
+ ┌────────────▼────────────┐
34
+ │ opencode serve │ ← Unmodified OpenCode
35
+ └─────────────────────────┘
36
+ ```
37
+
38
+ ### Subagent Visibility
39
+
40
+ Each child session is announced as an independent virtual session with its own sessionId:
41
+
42
+ ```typescript
43
+ // Harmony-ACP: child sessions get their own identity
44
+ await sendToClient({
45
+ sessionId: childSessionId,
46
+ update: {
47
+ sessionUpdate: "session_info_update",
48
+ title: "Explore project structure (@explore subagent)",
49
+ _meta: { parentSessionId, isSubagent: true },
50
+ },
51
+ })
52
+ ```
53
+
54
+ The client can distinguish parent vs child messages by sessionId, and use `_meta.parentSessionId` to display the hierarchy.
55
+
56
+ ### Message Assembly
57
+
58
+ OpenCode sends text/reasoning content as many small delta chunks. Harmony-ACP accumulates these in memory and flushes complete messages at boundaries:
59
+
60
+ ```
61
+ [delta] "H" → [delta] "el" → [delta] "lo" → [boundary: tool_call]
62
+
63
+ [log] agent_message_complete: "Hello"
64
+ ```
65
+
66
+ Boundaries: tool_call start, new message ID, session change, or process stop.
67
+
68
+ ## Quick Start
69
+
70
+ ### Prerequisites
71
+
72
+ - Node.js >= 18
73
+ - OpenCode installed (`npm i -g opencode`)
74
+
75
+ ### Install
76
+
77
+ ```bash
78
+ git clone <repo-url> harmony-acp
79
+ cd harmony-acp
80
+ npm install
81
+ npm run build
82
+ ```
83
+
84
+ ### Run
85
+
86
+ ```bash
87
+ # 1. Start OpenCode server
88
+ opencode serve --port 4096
89
+
90
+ # 2. Start Harmony-ACP
91
+ node dist/index.cjs --server http://localhost:4096
92
+ ```
93
+
94
+ ### Editor Configuration
95
+
96
+ **Zed** (`~/.config/zed/settings.json`):
97
+
98
+ ```json
99
+ {
100
+ "agent_servers": {
101
+ "Harmony-ACP": {
102
+ "command": "node",
103
+ "args": ["/path/to/harmony-acp/dist/index.cjs", "--server", "http://localhost:4096"]
104
+ }
105
+ }
106
+ }
107
+ ```
108
+
109
+ **VSCode** (with ACP extension):
110
+
111
+ Configure the agent server command to point to `dist/index.cjs` with the `--server` flag.
112
+
113
+ ## CLI Options
114
+
115
+ ```
116
+ Usage: harmony-acp [options]
117
+
118
+ Options:
119
+ --server, -s <url> OpenCode server URL (default: http://localhost:4096)
120
+ --cwd, -c <dir> Working directory (default: current directory)
121
+ --help, -h Show this help message
122
+ ```
123
+
124
+ ## Traffic Logging
125
+
126
+ All communication is logged to `~/.harmony-acp/logs/` as JSONL files (auto-rotated, max 30 files).
127
+
128
+ ### Log Categories
129
+
130
+ | Category | Direction | Description |
131
+ |----------|-----------|-------------|
132
+ | `acp.in` | Editor → Harmony-ACP | Incoming ACP requests (initialize, prompt, newSession, etc.) |
133
+ | `acp.out` | Harmony-ACP → Editor | Outgoing ACP responses and session updates |
134
+ | `oc.call` | Harmony-ACP → OpenCode | Calls to OpenCode SDK (session.create, session.prompt, etc.) |
135
+ | `oc.event` | OpenCode → Harmony-ACP | SSE events (session.created, permission.asked, etc.) |
136
+ | `system` | Internal | Lifecycle events (start, stop, errors) |
137
+
138
+ ### Log Viewer
139
+
140
+ ```bash
141
+ # View latest log (colorized, summarized)
142
+ npm run logs
143
+
144
+ # Show assembled message content (full text, not truncated JSON)
145
+ node scripts/view-log.mjs --content
146
+
147
+ # Filter by category or action
148
+ node scripts/view-log.mjs --filter acp.out
149
+ node scripts/view-log.mjs --filter prompt
150
+
151
+ # Raw JSONL output
152
+ node scripts/view-log.mjs --raw
153
+
154
+ # List available log files
155
+ node scripts/view-log.mjs --list
156
+ ```
157
+
158
+ ### Assembled Message Logs
159
+
160
+ When using `--content`, assembled messages are displayed with full text:
161
+
162
+ ```
163
+ 14:23:01.456 acp.out MSG[142chars] session=a1b2c3d4e5f6
164
+ The project uses TypeScript with Effect framework.
165
+ Key files are located in src/ directory...
166
+ ```
167
+
168
+ ## Architecture
169
+
170
+ | File | Purpose |
171
+ |------|---------|
172
+ | `src/index.ts` | CLI entry: argument parsing, stdio setup, server connection |
173
+ | `src/agent.ts` | ACP Agent: implements `Agent` interface, session management, prompting |
174
+ | `src/session-manager.ts` | Enhanced session tracking with parent-child relationship index |
175
+ | `src/event-handler.ts` | SSE event subscription, routing, and message delta assembly |
176
+ | `src/logger.ts` | JSONL structured logging with message assembly support |
177
+ | `src/types.ts` | Shared type definitions |
178
+ | `src/utils.ts` | Tool kind mapping utilities |
179
+ | `scripts/view-log.mjs` | Colorized log viewer |
180
+ | `scripts/test-integration.mjs` | Full pipeline integration test |
181
+
182
+ ### SessionManager Enhancement
183
+
184
+ Unlike OpenCode's built-in `ACPSessionManager` which only knows about explicitly created sessions, Harmony-ACP's `SessionManager`:
185
+
186
+ - **Auto-discovers** child sessions via `session.created` SSE events
187
+ - **Maintains a parent-child index** (`children: Map<parentID, Set<childID>>`)
188
+ - **Resolves root sessions** via `findRootSession()` to route child events to the correct ACP client
189
+
190
+ ### EventHandler Enhancement
191
+
192
+ The `EventHandler` subscribes to OpenCode's global SSE event stream and handles:
193
+
194
+ | Event | Behavior |
195
+ |-------|----------|
196
+ | `session.created` | Auto-registers child sessions, announces as virtual session to client |
197
+ | `permission.asked` | Forwards permission requests including those from child sessions |
198
+ | `message.part.updated` | Routes tool execution updates from child sessions |
199
+ | `message.part.delta` | Accumulates text/reasoning deltas, streams to client, assembles for logging |
200
+
201
+ ## Development
202
+
203
+ ```bash
204
+ npm run build # esbuild → dist/index.cjs
205
+ npm run typecheck # TypeScript type checking
206
+ npm run test # SSE event test (requires running OpenCode server)
207
+ npm run test:integration # Full pipeline test (requires running OpenCode server)
208
+ ```
209
+
210
+ ## Comparison with Built-in ACP
211
+
212
+ | Feature | OpenCode Built-in ACP | Harmony-ACP |
213
+ |---------|----------------------|-------------|
214
+ | Session management | Top-level only | Top-level + child sessions |
215
+ | Subagent tool calls visible | No | Yes |
216
+ | Subagent text output visible | No | Yes |
217
+ | Subagent permissions forwarded | No | Yes |
218
+ | Traffic logging | No | Yes (JSONL, categorized) |
219
+ | Message assembly | N/A | Yes (delta → complete) |
220
+ | Modifies OpenCode source | N/A | No |
221
+ | Runs as independent process | No (integrated) | Yes |
222
+
223
+ ## License
224
+
225
+ MIT
package/README.zh.md ADDED
@@ -0,0 +1,223 @@
1
+ # Harmony-ACP
2
+
3
+ [OpenCode](https://github.com/sst/opencode) 的增强版 ACP(Agent Client Protocol)服务器。以独立进程运行,提供 OpenCode 内置 ACP 所缺少的 **子代理可见性** 和 **全量流量日志**。
4
+
5
+ ## 为什么需要它
6
+
7
+ OpenCode 内置的 ACP 有两个关键限制:
8
+
9
+ 1. **子代理不可见**:当代理通过 Task 工具派生子代理时,子 session 的事件——工具调用、文本输出、权限请求——对 ACP 客户端(如 Zed、VSCode)完全不可见。原因是内置的 `SessionManager` 只注册顶层 session,来自子 session 的事件会被静默丢弃。
10
+
11
+ 2. **无流量可观测性**:无法检查 ACP 客户端与 OpenCode 之间流动的数据,调试困难。
12
+
13
+ Harmony-ACP 在不修改任何 OpenCode 源码的前提下解决了这两个问题。
14
+
15
+ ## 工作原理
16
+
17
+ ```
18
+ ┌─────────────────────────┐
19
+ │ 编辑器 (Zed / VSCode) │
20
+ └────────────┬────────────┘
21
+ │ stdio (JSON-RPC)
22
+ ┌────────────▼────────────┐
23
+ │ Harmony-ACP │ ← 独立进程
24
+ │ - 自动发现子 session │
25
+ │ - 子 session 拥有独立 │
26
+ │ sessionId │
27
+ │ - 记录全量流量日志 │
28
+ │ - 自动拼接消息碎片 │
29
+ └────────────┬────────────┘
30
+ │ HTTP / SSE
31
+ ┌────────────▼────────────┐
32
+ │ opencode serve │ ← 未修改的 OpenCode
33
+ └─────────────────────────┘
34
+ ```
35
+
36
+ ### 子代理可见性
37
+
38
+ 每个子 session 作为独立虚拟 session 公告,拥有自己的 sessionId:
39
+
40
+ ```typescript
41
+ // Harmony-ACP:子 session 获得独立身份
42
+ await sendToClient({
43
+ sessionId: childSessionId,
44
+ update: {
45
+ sessionUpdate: "session_info_update",
46
+ title: "Explore project structure (@explore subagent)",
47
+ _meta: { parentSessionId, isSubagent: true },
48
+ },
49
+ })
50
+ ```
51
+
52
+ 客户端通过 sessionId 区分父/子消息,通过 `_meta.parentSessionId` 展示层级关系。
53
+
54
+ ### 消息拼装
55
+
56
+ OpenCode 以许多小 delta 碎片发送文本/推理内容。Harmony-ACP 在内存中累积这些碎片,遇到边界事件时刷新为完整消息:
57
+
58
+ ```
59
+ [delta] "你" → [delta] "好" → [delta] "!" → [边界: tool_call]
60
+
61
+ [日志] agent_message_complete: "你好!"
62
+ ```
63
+
64
+ 边界触发条件:tool_call 开始、新 messageID、session 切换、进程停止。
65
+
66
+ ## 快速开始
67
+
68
+ ### 前置条件
69
+
70
+ - Node.js >= 18
71
+ - 已安装 OpenCode(`npm i -g opencode`)
72
+
73
+ ### 安装
74
+
75
+ ```bash
76
+ git clone <repo-url> harmony-acp
77
+ cd harmony-acp
78
+ npm install
79
+ npm run build
80
+ ```
81
+
82
+ ### 运行
83
+
84
+ ```bash
85
+ # 1. 启动 OpenCode 服务
86
+ opencode serve --port 4096
87
+
88
+ # 2. 启动 Harmony-ACP
89
+ node dist/index.cjs --server http://localhost:4096
90
+ ```
91
+
92
+ ### 编辑器配置
93
+
94
+ **Zed**(`~/.config/zed/settings.json`):
95
+
96
+ ```json
97
+ {
98
+ "agent_servers": {
99
+ "Harmony-ACP": {
100
+ "command": "node",
101
+ "args": ["/path/to/harmony-acp/dist/index.cjs", "--server", "http://localhost:4096"]
102
+ }
103
+ }
104
+ }
105
+ ```
106
+
107
+ **VSCode**(配合 ACP 插件):
108
+
109
+ 将代理服务器命令配置为指向 `dist/index.cjs`,并传入 `--server` 参数。
110
+
111
+ ## 命令行选项
112
+
113
+ ```
114
+ 用法:harmony-acp [选项]
115
+
116
+ 选项:
117
+ --server, -s <url> OpenCode 服务器地址(默认:http://localhost:4096)
118
+ --cwd, -c <目录> 工作目录(默认:当前目录)
119
+ --help, -h 显示帮助信息
120
+ ```
121
+
122
+ ## 流量日志
123
+
124
+ 所有通信记录到 `~/.harmony-acp/logs/` 目录下的 JSONL 文件(自动轮转,最多保留 30 个文件)。
125
+
126
+ ### 日志分类
127
+
128
+ | 分类 | 方向 | 说明 |
129
+ |------|------|------|
130
+ | `acp.in` | 编辑器 → Harmony-ACP | 收到的 ACP 请求(initialize、prompt、newSession 等) |
131
+ | `acp.out` | Harmony-ACP → 编辑器 | 发出的 ACP 响应和 session 更新 |
132
+ | `oc.call` | Harmony-ACP → OpenCode | 对 OpenCode SDK 的调用(session.create、session.prompt 等) |
133
+ | `oc.event` | OpenCode → Harmony-ACP | SSE 事件(session.created、permission.asked 等) |
134
+ | `system` | 内部 | 生命周期事件(启动、停止、错误) |
135
+
136
+ ### 日志查看器
137
+
138
+ ```bash
139
+ # 查看最新日志(彩色、摘要化)
140
+ npm run logs
141
+
142
+ # 展示拼装后的完整消息文本
143
+ node scripts/view-log.mjs --content
144
+
145
+ # 按分类或动作过滤
146
+ node scripts/view-log.mjs --filter acp.out
147
+ node scripts/view-log.mjs --filter prompt
148
+
149
+ # 原始 JSONL 输出
150
+ node scripts/view-log.mjs --raw
151
+
152
+ # 列出可用日志文件
153
+ node scripts/view-log.mjs --list
154
+ ```
155
+
156
+ ### 拼装消息日志
157
+
158
+ 使用 `--content` 参数时,拼装消息以完整文本展示:
159
+
160
+ ```
161
+ 14:23:01.456 acp.out MSG[142chars] session=a1b2c3d4e5f6
162
+ 项目使用 TypeScript 和 Effect 框架。
163
+ 关键文件位于 src/ 目录...
164
+ ```
165
+
166
+ ## 架构
167
+
168
+ | 文件 | 职责 |
169
+ |------|------|
170
+ | `src/index.ts` | 入口:参数解析、stdio 设置、服务器连接 |
171
+ | `src/agent.ts` | ACP 代理:实现 `Agent` 接口、session 管理、prompt 转发、日志包装 |
172
+ | `src/session-manager.ts` | 增强版 session 管理,维护父子关系索引 |
173
+ | `src/event-handler.ts` | SSE 事件订阅、路由和消息 delta 拼装 |
174
+ | `src/logger.ts` | JSONL 结构化日志,支持消息拼装 |
175
+ | `src/types.ts` | 共享类型定义 |
176
+ | `src/utils.ts` | 工具函数(toolKind 映射等) |
177
+ | `scripts/view-log.mjs` | 彩色日志查看器 |
178
+ | `scripts/test-integration.mjs` | 全链路集成测试 |
179
+
180
+ ### SessionManager 增强
181
+
182
+ 与 OpenCode 内置的 `ACPSessionManager` 只感知显式创建的 session 不同,Harmony-ACP 的 `SessionManager`:
183
+
184
+ - **自动发现**子 session——通过 `session.created` SSE 事件
185
+ - **维护父子关系索引**——`children: Map<parentID, Set<childID>>`
186
+ - **解析根 session**——通过 `findRootSession()` 将子 session 事件路由到正确的 ACP 客户端
187
+
188
+ ### EventHandler 增强
189
+
190
+ `EventHandler` 订阅 OpenCode 的全局 SSE 事件流并处理:
191
+
192
+ | 事件 | 行为 |
193
+ |------|------|
194
+ | `session.created` | 自动注册子 session,作为虚拟 session 公告给客户端 |
195
+ | `permission.asked` | 转发权限请求,包括来自子 session 的请求 |
196
+ | `message.part.updated` | 路由子 session 的工具执行状态更新 |
197
+ | `message.part.delta` | 累积文本/推理 delta,流式转发给客户端,拼装后写入日志 |
198
+
199
+ ## 开发
200
+
201
+ ```bash
202
+ npm run build # esbuild → dist/index.cjs
203
+ npm run typecheck # TypeScript 类型检查
204
+ npm run test # SSE 事件测试(需运行 OpenCode 服务器)
205
+ npm run test:integration # 全链路集成测试(需运行 OpenCode 服务器)
206
+ ```
207
+
208
+ ## 与内置 ACP 对比
209
+
210
+ | 特性 | OpenCode 内置 ACP | Harmony-ACP |
211
+ |------|-------------------|-------------|
212
+ | Session 管理 | 仅顶层 session | 顶层 + 子 session |
213
+ | 子代理工具调用可见 | 否 | 是 |
214
+ | 子代理文本输出可见 | 否 | 是 |
215
+ | 子代理权限请求转发 | 否 | 是 |
216
+ | 流量日志 | 无 | 有(JSONL,分类记录) |
217
+ | 消息碎片拼装 | N/A | 有(delta → 完整消息) |
218
+ | 是否修改 OpenCode 源码 | — | 否 |
219
+ | 运行方式 | 集成在 OpenCode 进程内 | 独立进程 |
220
+
221
+ ## 许可证
222
+
223
+ MIT