weacpx 0.4.3 → 0.4.5

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 CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  ## 这是什么
13
13
 
14
- `weacpx` 是一个可以通过微信、飞书或元宝直接控制 Codex / Claude Code / Gemini / OpenCode 的工具。它把聊天消息通过 `acpx` 连接到 Agent CLI 会话上,让你直接在手机里:
14
+ `weacpx` 是一个可以通过微信、飞书或元宝直接控制 Codex / Claude Code / Gemini / OpenCode 等 ACP Agent 的工具。它把聊天消息通过 `acpx` 连接到 Agent CLI 会话上,让你直接在手机里:
15
15
 
16
16
  - 新建和切换会话
17
17
  - 让 Agent 继续在指定项目目录里工作
@@ -34,7 +34,7 @@
34
34
  开始前,你至少需要:
35
35
 
36
36
  - Node.js 22+ 或 Bun
37
- - 已可用的 Codex / Claude Code / Gemini / OpenCode
37
+ - 已可用的 Codex / Claude Code / Gemini / OpenCode 等你要使用的 Agent CLI
38
38
  - 一台装了微信、飞书或元宝的手机
39
39
 
40
40
  > 微信频道基于 `weixin-agent-sdk` 工作,飞书频道使用飞书自建应用凭据,元宝频道使用 `appKey` / `appSecret`;底层 Agent 会话由 `acpx` 驱动。正常情况下,你不需要额外全局安装 `acpx`。
@@ -176,6 +176,9 @@ weacpx restart
176
176
  | `weacpx plugin add @ganglion/weacpx-channel-yuanbao && weacpx channel add yuanbao` | 安装并添加元宝频道,会提示输入元宝 appKey/appSecret |
177
177
  | `weacpx doctor` | 运行环境诊断 |
178
178
  | `weacpx version` | 查看当前版本 |
179
+ | `weacpx agent list` | 查看本机已注册的 agent |
180
+ | `weacpx agent add <name>` | 从内置模板添加 agent;已存在且配置不同的同名 agent 不会被覆盖 |
181
+ | `weacpx agent rm <name>` | 删除 agent |
179
182
  | `weacpx workspace list` | 查看本机已注册的 workspace |
180
183
  | `weacpx workspace add [name]` | 把当前目录注册成 workspace;不传 `name` 时使用当前目录名 |
181
184
  | `weacpx workspace rm <name>` | 删除 workspace |
@@ -223,6 +226,26 @@ weacpx ws rm frontend
223
226
 
224
227
  注意:`workspace add` 总是注册**当前终端所在目录**;如果不传名称,会用当前目录名作为 workspace 名称。
225
228
 
229
+ ### `agent` CLI 怎么用
230
+
231
+ `weacpx agent` 用来在电脑本机维护 `~/.weacpx/config.json` 里的 `agents` 配置;`agents` 是同等别名。
232
+
233
+ | 命令 | 说明 |
234
+ |------|------|
235
+ | `weacpx agent list` | 列出已注册的 agent |
236
+ | `weacpx agent templates` | 列出可添加的内置模板 |
237
+ | `weacpx agent add <name>` | 从内置模板添加 agent,例如 `kimi`、`opencode` |
238
+ | `weacpx agent rm <name>` | 删除指定 agent |
239
+
240
+ 常见用法:
241
+
242
+ ```bash
243
+ weacpx agent templates
244
+ weacpx agent add kimi
245
+ weacpx agents list
246
+ weacpx agent rm kimi
247
+ ```
248
+
226
249
  ### `doctor` 怎么用
227
250
 
228
251
  ```bash
@@ -245,9 +268,7 @@ weacpx doctor --smoke --agent codex --workspace backend
245
268
 
246
269
  ### Agent 管理
247
270
 
248
- 已内置常用的 CodexClaude Code;
249
-
250
- 可以使用 `/agent add opencode` 添加你所需要的 agents。
271
+ 默认配置通常已包含 `codex``claude`。如果你要使用其它 acpx 支持的 agent,可以先用 `/agent add <name>` 从内置模板添加。
251
272
 
252
273
  | 命令 | 说明 |
253
274
  |------|------|
@@ -256,6 +277,16 @@ weacpx doctor --smoke --agent codex --workspace backend
256
277
  | `/agent add opencode` | 添加 `OpenCode` Agent |
257
278
  | `/agent rm <name>` | 删除 agent |
258
279
 
280
+ 当前内置模板与 acpx 的 built-in agents 对齐:
281
+
282
+ ```text
283
+ codex, claude, pi, openclaw, gemini, cursor, copilot, droid,
284
+ factory-droid, factorydroid, iflow, kilocode, kimi, kiro,
285
+ opencode, qoder, qwen, trae
286
+ ```
287
+
288
+ 这些模板只写入 `driver`,实际启动命令交给 acpx 解析;例如 `/agent add kimi` 会保存 `{ "driver": "kimi" }`。完整命令说明见 [docs/commands.md](./docs/commands.md),配置字段见 [docs/config-reference.md](./docs/config-reference.md)。
289
+
259
290
  ### Workspace 管理
260
291
 
261
292
  | 命令 | 说明 |
@@ -356,6 +387,8 @@ README 里只保留用户视角的最常用命令。
356
387
 
357
388
  如果你想让 Codex、Claude Code 等外部 MCP host 直接使用 weacpx 的多 Agent 编排能力,可以把 `weacpx mcp-stdio` 配成一个 stdio MCP server。
358
389
 
390
+ `delegate_request` 支持 MCP Tasks:支持该能力的 host 可以让委派请求立即返回原生 task handle,之后通过 `tasks/get` / `tasks/result` / `tasks/cancel` 获取状态、结果或取消任务;worker 输出的 `[PROGRESS] ...` 会显示在 `tasks/get` / `tasks/list` 的 `statusMessage` 里;`input_required` 状态下的 `tasks/result` 会返回下一步操作提示并结束本次 result stream,而不是长时间阻塞;client 按提示调用 `task_get` / `task_approve` / `coordinator_answer_question` 等工具后,再继续 `tasks/get` / `tasks/result` 轮询。不支持 MCP Tasks 的 host 仍可使用兼容工具 `task_get` / `task_wait` / `task_cancel`。
391
+
359
392
  先启动 daemon:
360
393
 
361
394
  ```bash
@@ -9,7 +9,13 @@
9
9
  "level": "info",
10
10
  "maxSizeBytes": 2097152,
11
11
  "maxFiles": 5,
12
- "retentionDays": 7
12
+ "retentionDays": 7,
13
+ "perf": {
14
+ "enabled": false,
15
+ "maxSizeBytes": 5242880,
16
+ "maxFiles": 3,
17
+ "retentionDays": 7
18
+ }
13
19
  },
14
20
  "channel": {
15
21
  "replyMode": "stream"
@@ -111,38 +111,24 @@ function extractPromptFailureMessage(result) {
111
111
  function extractPromptOutput(output) {
112
112
  const lines = output.split(`
113
113
  `).map((line) => line.trim()).filter((line) => line.length > 0);
114
- const messageSegments = [];
115
- let currentSegment = "";
114
+ let text = "";
116
115
  let hasAgentMessage = false;
117
116
  for (const line of lines) {
117
+ let event;
118
118
  try {
119
- const event = JSON.parse(line);
120
- const isMessageChunk = event.method === "session/update" && event.params?.update?.sessionUpdate === "agent_message_chunk" && event.params.update.content?.type === "text" && typeof event.params.update.content.text === "string";
121
- if (isMessageChunk) {
122
- hasAgentMessage = true;
123
- const chunk = event.params.update.content.text ?? "";
124
- if (chunk.length > 0) {
125
- currentSegment += chunk;
126
- }
127
- continue;
128
- }
129
- if (currentSegment.trim().length > 0) {
130
- messageSegments.push(currentSegment.trim());
131
- }
132
- currentSegment = "";
119
+ event = JSON.parse(line);
133
120
  } catch {
134
- if (currentSegment.trim().length > 0) {
135
- messageSegments.push(currentSegment.trim());
136
- currentSegment = "";
137
- }
121
+ continue;
122
+ }
123
+ const isMessageChunk = event.method === "session/update" && event.params?.update?.sessionUpdate === "agent_message_chunk" && event.params.update.content?.type === "text" && typeof event.params.update.content.text === "string";
124
+ if (isMessageChunk) {
125
+ hasAgentMessage = true;
126
+ text += event.params.update.content.text ?? "";
138
127
  }
139
128
  }
140
- if (currentSegment.trim().length > 0) {
141
- messageSegments.push(currentSegment.trim());
142
- }
143
- if (messageSegments.length > 0) {
129
+ if (hasAgentMessage && text.trim().length > 0) {
144
130
  return {
145
- text: messageSegments[messageSegments.length - 1],
131
+ text: text.trim(),
146
132
  hasAgentMessage
147
133
  };
148
134
  }
@@ -2,6 +2,7 @@ import type { Agent as ChatAgent } from "../weixin/agent/interface.js";
2
2
  import type { OrchestrationTaskRecord } from "../orchestration/orchestration-types.js";
3
3
  import type { AppLogger } from "../logging/app-logger.js";
4
4
  import type { PendingFinalChunk } from "../weixin/messaging/quota-manager.js";
5
+ import type { PerfTracer } from "../perf/perf-tracer.js";
5
6
  export type { ChatAgent };
6
7
  export interface OutboundQuota {
7
8
  onInbound(chatKey: string): void;
@@ -26,6 +27,7 @@ export interface ChannelStartInput {
26
27
  abortSignal: AbortSignal;
27
28
  quota: OutboundQuota;
28
29
  logger: AppLogger;
30
+ perfTracer?: PerfTracer;
29
31
  }
30
32
  export interface OrchestrationDeliveryCallbacks {
31
33
  markTaskNoticeDelivered: (taskId: string, accountId: string) => Promise<void>;