mu-agent-bridge 1.0.71 → 1.0.73

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.
Files changed (120) hide show
  1. package/README.md +9 -6
  2. package/dist/src-ts/agent.js +35 -0
  3. package/dist/src-ts/channel-sessions.js +3 -2
  4. package/dist/src-ts/control-server.js +5 -2
  5. package/dist/src-ts/dsh-agent.js +25 -12
  6. package/dist/src-ts/plugin-catalog.js +11 -2
  7. package/dist/src-ts/plugin-manager.js +183 -9
  8. package/dist/src-ts/plugins/abweb/index.js +1 -1
  9. package/dist/src-ts/plugins/abweb/public/app.js +68 -2
  10. package/dist/src-ts/plugins/abweb/public/index.html +15 -0
  11. package/dist/src-ts/plugins/abweb/public/style.css +4 -0
  12. package/dist/src-ts/plugins/aun/index.js +66 -10
  13. package/dist/src-ts/plugins/aun/messages.js +3 -0
  14. package/dist/src-ts/plugins/chatgpt/cdp-client.js +26 -12
  15. package/dist/src-ts/plugins/chatgpt/index.js +77 -17
  16. package/dist/src-ts/plugins/claude-cli/index.js +19 -2
  17. package/dist/src-ts/plugins/codex-cli/index.js +19 -2
  18. package/dist/src-ts/plugins/doubao/cdp-client.js +5 -2
  19. package/dist/src-ts/plugins/doubao/index.js +36 -4
  20. package/dist/src-ts/plugins/dsh/index.js +78 -6
  21. package/dist/src-ts/plugins/local-cli/agent.js +74 -9
  22. package/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
  23. package/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
  24. package/dist/src-ts/plugins/personal-assistant/index.js +242 -0
  25. package/dist/src-ts/plugins/personal-assistant/model-client.js +167 -0
  26. package/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
  27. package/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
  28. package/dist/src-ts/plugins/personal-assistant/tools.js +166 -0
  29. package/dist/src-ts/plugins/qwenwork/cdp-client.js +47 -11
  30. package/dist/src-ts/plugins/qwenwork/index.js +89 -8
  31. package/dist/src-ts/plugins/workbuddy/cdp-client.js +9 -4
  32. package/dist/src-ts/plugins/workbuddy/index.js +104 -33
  33. package/dist/src-ts/session-management.js +105 -6
  34. package/dist/tests-ts/abweb-cli.test.js +10 -2
  35. package/dist/tests-ts/abweb-model-config.test.js +70 -0
  36. package/dist/tests-ts/agent.test.js +27 -0
  37. package/dist/tests-ts/aun-messages.test.js +6 -0
  38. package/dist/tests-ts/aun-plugin.test.js +85 -0
  39. package/dist/tests-ts/bundled-package.test.js +12 -2
  40. package/dist/tests-ts/channel-sessions.test.js +27 -0
  41. package/dist/tests-ts/chatgpt-plugin.test.js +99 -0
  42. package/dist/tests-ts/control-server.test.js +36 -0
  43. package/dist/tests-ts/doubao-plugin.test.js +92 -0
  44. package/dist/tests-ts/dsh-agent.test.js +77 -0
  45. package/dist/tests-ts/feishu-plugin.test.js +1 -1
  46. package/dist/tests-ts/installer-assets.test.js +1 -1
  47. package/dist/tests-ts/local-cli.test.js +119 -1
  48. package/dist/tests-ts/model-provider-cancellation.test.js +150 -0
  49. package/dist/tests-ts/personal-assistant-coordination.test.js +76 -0
  50. package/dist/tests-ts/personal-assistant-extensions.test.js +29 -0
  51. package/dist/tests-ts/personal-assistant-owner-context.test.js +107 -0
  52. package/dist/tests-ts/personal-assistant-remote-ability.test.js +123 -0
  53. package/dist/tests-ts/personal-assistant.test.js +389 -0
  54. package/dist/tests-ts/platform-integration.test.js +124 -1
  55. package/dist/tests-ts/plugin-catalog.test.js +12 -2
  56. package/dist/tests-ts/plugin-manager.test.js +280 -0
  57. package/dist/tests-ts/publish-packages.test.js +42 -0
  58. package/dist/tests-ts/qwenwork-plugin.test.js +105 -1
  59. package/dist/tests-ts/session-management.test.js +155 -0
  60. package/dist/tests-ts/workbuddy-plugin.test.js +154 -2
  61. package/package.json +1 -1
  62. package/plugins/abweb/dist/src-ts/agent.js +35 -0
  63. package/plugins/abweb/dist/src-ts/plugins/abweb/index.js +1 -1
  64. package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.js +68 -2
  65. package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.ts +53 -3
  66. package/plugins/abweb/dist/src-ts/plugins/abweb/public/index.html +15 -0
  67. package/plugins/abweb/dist/src-ts/plugins/abweb/public/style.css +4 -0
  68. package/plugins/abweb/package.json +1 -1
  69. package/plugins/abweb/plugin.json +1 -1
  70. package/plugins/aun/dist/src-ts/agent.js +35 -0
  71. package/plugins/aun/dist/src-ts/plugins/aun/index.js +66 -10
  72. package/plugins/aun/dist/src-ts/plugins/aun/messages.js +3 -0
  73. package/plugins/chatgpt/dist/src-ts/agent.js +35 -0
  74. package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/cdp-client.js +26 -12
  75. package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +77 -17
  76. package/plugins/chatgpt/package.json +1 -1
  77. package/plugins/chatgpt/plugin.json +1 -1
  78. package/plugins/claude-cli/dist/src-ts/agent.js +35 -0
  79. package/plugins/claude-cli/dist/src-ts/plugins/claude-cli/index.js +19 -2
  80. package/plugins/claude-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
  81. package/plugins/claude-cli/package.json +1 -1
  82. package/plugins/claude-cli/plugin.json +3 -2
  83. package/plugins/codex-cli/dist/src-ts/agent.js +35 -0
  84. package/plugins/codex-cli/dist/src-ts/plugins/codex-cli/index.js +19 -2
  85. package/plugins/codex-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
  86. package/plugins/codex-cli/package.json +1 -1
  87. package/plugins/codex-cli/plugin.json +3 -2
  88. package/plugins/doubao/dist/src-ts/agent.js +35 -0
  89. package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +5 -2
  90. package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +36 -4
  91. package/plugins/doubao/package.json +1 -1
  92. package/plugins/doubao/plugin.json +3 -2
  93. package/plugins/dsh/dist/src-ts/agent.js +35 -0
  94. package/plugins/dsh/dist/src-ts/dsh-agent.js +25 -12
  95. package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +78 -6
  96. package/plugins/dsh/package.json +1 -1
  97. package/plugins/dsh/plugin.json +3 -2
  98. package/plugins/feishu/dist/src-ts/agent.js +35 -0
  99. package/plugins/personal-assistant/config.schema.json +22 -0
  100. package/plugins/personal-assistant/dist/src-ts/agent.js +69 -0
  101. package/plugins/personal-assistant/dist/src-ts/plugin.js +13 -0
  102. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
  103. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
  104. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/index.js +242 -0
  105. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/model-client.js +167 -0
  106. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
  107. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
  108. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/tools.js +166 -0
  109. package/plugins/personal-assistant/package.json +39 -0
  110. package/plugins/personal-assistant/plugin.json +15 -0
  111. package/plugins/qwenwork/dist/src-ts/agent.js +35 -0
  112. package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/cdp-client.js +47 -11
  113. package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +89 -8
  114. package/plugins/qwenwork/package.json +1 -1
  115. package/plugins/qwenwork/plugin.json +3 -2
  116. package/plugins/workbuddy/dist/src-ts/agent.js +35 -0
  117. package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +9 -4
  118. package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +104 -33
  119. package/plugins/workbuddy/package.json +1 -1
  120. package/plugins/workbuddy/plugin.json +3 -2
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DoubaoPlugin = exports.doubaoManifest = void 0;
4
4
  const promises_1 = require("node:fs/promises");
5
5
  const node_path_1 = require("node:path");
6
+ const agent_1 = require("../../agent");
6
7
  const cdp_client_1 = require("./cdp-client");
7
8
  const session_history_1 = require("./session-history");
8
9
  const plugin_1 = require("../../plugin");
@@ -10,17 +11,22 @@ const desktop_launch_1 = require("../desktop-launch");
10
11
  const node_os_1 = require("node:os");
11
12
  exports.doubaoManifest = {
12
13
  id: "doubao",
13
- version: "1.0.23",
14
+ version: "1.0.24",
14
15
  displayName: "豆包",
15
16
  entry: "dist/index.js",
16
17
  apiVersion: "1",
17
18
  process: "in-process",
18
- capabilities: ["chat", "health"],
19
+ capabilities: ["chat", "health", "model-provider"],
19
20
  configSchema: "config.schema.json",
20
21
  };
21
22
  class DoubaoPlugin {
22
23
  injectedClient;
23
24
  manifest = exports.doubaoManifest;
25
+ get modelProvider() {
26
+ if (!this.client?.complete || !this.client.createSession)
27
+ return undefined;
28
+ return { complete: (request) => this.completeModelProvider(request) };
29
+ }
24
30
  attachmentInput = true;
25
31
  sessionOperations = [];
26
32
  sessionHistory;
@@ -145,9 +151,9 @@ class DoubaoPlugin {
145
151
  if (request.attachments?.length && !client.complete)
146
152
  throw new Error("当前豆包客户端不支持附件输入");
147
153
  if (!client.complete)
148
- return { text: await client.direct(request.task) };
154
+ return { text: await client.direct(request.task, request.signal) };
149
155
  const conversationId = request.sessionId ? this.bindings.get(request.sessionId) ?? "" : undefined;
150
- const response = await client.complete(request.task, undefined, request.attachments, conversationId);
156
+ const response = await client.complete(request.task, request.signal, request.attachments, conversationId);
151
157
  if (!request.sessionId)
152
158
  return response;
153
159
  if (!response.sessionId || !/^\d+$/.test(response.sessionId))
@@ -156,6 +162,32 @@ class DoubaoPlugin {
156
162
  return { ...response, sessionId: request.sessionId };
157
163
  });
158
164
  }
165
+ async completeModelProvider(request) {
166
+ let stage = "豆包模型排队(queue)";
167
+ try {
168
+ request.signal.throwIfAborted();
169
+ const run = this.enqueue(async () => {
170
+ request.signal.throwIfAborted();
171
+ stage = "连接豆包模型(connect)";
172
+ const client = this.client;
173
+ if (this.state !== "ready" || !client?.connected() || !client.complete)
174
+ throw new Error("豆包 CDP 未连接,模型请求不会启动或重启桌面");
175
+ stage = "等待豆包模型生成(generate)";
176
+ // 空服务端标识会原子创建独立会话,无需切换用户桌面页面。
177
+ const response = await client.complete(request.prompt, request.signal, undefined, this.bindings.get(request.sessionId) ?? "");
178
+ if (!response.sessionId || !/^\d+$/.test(response.sessionId))
179
+ throw new Error("豆包未返回有效的服务端会话标识");
180
+ await this.remember(request.sessionId, response.sessionId);
181
+ return { text: response.text };
182
+ });
183
+ return await (0, agent_1.abortable)(run, request.signal);
184
+ }
185
+ catch (error) {
186
+ if (error instanceof agent_1.AgentOperationError)
187
+ throw error;
188
+ throw new agent_1.AgentOperationError("DOUBAO_MODEL_FAILED", stage, error);
189
+ }
190
+ }
159
191
  enqueue(task) {
160
192
  this.pending++;
161
193
  const run = this.queue.then(task);
@@ -1,23 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DshPlugin = exports.dshManifest = void 0;
4
+ const promises_1 = require("node:fs/promises");
4
5
  const node_path_1 = require("node:path");
6
+ const agent_1 = require("../../agent");
5
7
  const dsh_agent_1 = require("../../dsh-agent");
6
8
  const dsh_paths_1 = require("../../dsh-paths");
7
9
  const openai_adapter_1 = require("../qwenwork/openai-adapter");
8
10
  const session_history_1 = require("./session-history");
9
11
  exports.dshManifest = {
10
12
  id: "dsh",
11
- version: "1.0.11",
13
+ version: "1.0.12",
12
14
  displayName: "DeepSeek Harness",
13
15
  entry: "dist/index.js",
14
16
  apiVersion: "1",
15
17
  process: "in-process",
16
- capabilities: ["chat", "health"],
18
+ capabilities: ["chat", "health", "model-provider"],
17
19
  configSchema: "config.schema.json",
18
20
  };
19
21
  class DshPlugin {
20
22
  manifest = exports.dshManifest;
23
+ modelProvider = { complete: (request) => this.completeModelProvider(request) };
21
24
  attachmentInput = true;
22
25
  sessionOperations = ["new"];
23
26
  sessionHistory;
@@ -26,8 +29,27 @@ class DshPlugin {
26
29
  message = "插件尚未启动";
27
30
  adapter;
28
31
  activeRequests = 0;
32
+ queue = Promise.resolve();
33
+ modelSessions = new Map();
34
+ modelSessionsPath;
29
35
  unregister = [];
30
36
  async activate(context) {
37
+ this.modelSessions.clear();
38
+ this.modelSessionsPath = context.dataDir ? (0, node_path_1.join)(context.dataDir, "model-provider-sessions.json") : undefined;
39
+ if (this.modelSessionsPath) {
40
+ try {
41
+ const saved = JSON.parse(await (0, promises_1.readFile)(this.modelSessionsPath, "utf8"));
42
+ const valid = (value) => typeof value === "string" && value.trim() && value.length <= 512 && !/[\u0000-\u001f]/.test(value);
43
+ if (!saved || typeof saved !== "object" || Array.isArray(saved) || Object.entries(saved).some(([key, value]) => !valid(key) || !valid(value)))
44
+ throw new Error("DSH 个人助理模型会话记录无效");
45
+ for (const [key, value] of Object.entries(saved))
46
+ this.modelSessions.set(key, value);
47
+ }
48
+ catch (error) {
49
+ if (error.code !== "ENOENT")
50
+ throw error;
51
+ }
52
+ }
31
53
  const configured = context.config.patchPath;
32
54
  const patchPath = typeof configured === "string" && configured.trim()
33
55
  ? configured
@@ -52,6 +74,7 @@ class DshPlugin {
52
74
  context.logger?.info?.("dsh plugin activated");
53
75
  }
54
76
  async deactivate() {
77
+ await this.queue;
55
78
  for (const dispose of this.unregister.splice(0))
56
79
  dispose();
57
80
  await this.adapter?.close();
@@ -67,12 +90,61 @@ class DshPlugin {
67
90
  async handle(request) {
68
91
  if (!this.agent || this.status !== "ready")
69
92
  throw new Error("dsh 插件未就绪");
70
- this.activeRequests++;
93
+ const run = this.queue.then(async () => {
94
+ this.activeRequests++;
95
+ try {
96
+ return await this.agent.send(request);
97
+ }
98
+ finally {
99
+ this.activeRequests--;
100
+ }
101
+ });
102
+ this.queue = run.then(() => undefined, () => undefined);
103
+ return run;
104
+ }
105
+ async completeModelProvider(request) {
106
+ let stage = "DSH 模型排队(queue)";
71
107
  try {
72
- return await this.agent.send(request);
108
+ request.signal.throwIfAborted();
109
+ const run = this.queue.then(async () => {
110
+ request.signal.throwIfAborted();
111
+ stage = "连接 DSH 模型(connect)";
112
+ if (!this.agent || this.status !== "ready")
113
+ throw new Error("DSH 插件未就绪");
114
+ this.activeRequests++;
115
+ try {
116
+ const sessionId = this.modelSessions.get(request.sessionId);
117
+ stage = "等待 DSH 模型生成(generate)";
118
+ const response = await this.agent.send({
119
+ task: request.prompt,
120
+ timeoutMs: request.timeoutMs,
121
+ signal: request.signal,
122
+ ...(sessionId ? { sessionId } : { newSession: true }),
123
+ }, true);
124
+ if (!sessionId) {
125
+ if (!response.sessionId)
126
+ throw new Error("DSH 未返回个人助理模型会话标识");
127
+ const next = new Map(this.modelSessions).set(request.sessionId, response.sessionId);
128
+ if (this.modelSessionsPath) {
129
+ await (0, promises_1.mkdir)((0, node_path_1.resolve)(this.modelSessionsPath, ".."), { recursive: true });
130
+ await (0, promises_1.writeFile)(`${this.modelSessionsPath}.tmp`, JSON.stringify(Object.fromEntries(next)), { mode: 0o600 });
131
+ await (0, promises_1.rename)(`${this.modelSessionsPath}.tmp`, this.modelSessionsPath);
132
+ }
133
+ this.modelSessions.set(request.sessionId, response.sessionId);
134
+ }
135
+ return { text: response.text };
136
+ }
137
+ finally {
138
+ this.activeRequests--;
139
+ }
140
+ });
141
+ this.queue = run.then(() => undefined, () => undefined);
142
+ return await (0, agent_1.abortable)(run, request.signal);
73
143
  }
74
- finally {
75
- this.activeRequests--;
144
+ catch (error) {
145
+ if (error instanceof agent_1.AgentOperationError)
146
+ throw error;
147
+ throw new agent_1.AgentOperationError("DSH_MODEL_FAILED", stage, error);
76
148
  }
77
149
  }
78
150
  }
@@ -6,6 +6,7 @@ const node_child_process_1 = require("node:child_process");
6
6
  const node_fs_1 = require("node:fs");
7
7
  const promises_1 = require("node:fs/promises");
8
8
  const node_path_1 = require("node:path");
9
+ const node_os_1 = require("node:os");
9
10
  const node_crypto_1 = require("node:crypto");
10
11
  const nativeId = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;
11
12
  const maximumOutput = 4 * 1024 * 1024;
@@ -22,6 +23,21 @@ function diagnostic(value) {
22
23
  } })
23
24
  .slice(0, 2000);
24
25
  }
26
+ function modelDiagnostic(error) {
27
+ const message = String(error);
28
+ const code = /CLI 退出码 (?:\d+|[A-Z][A-Z0-9_]+)/.exec(message)?.[0];
29
+ if (code)
30
+ return `${code}:请检查 CLI 登录、权限和网络`;
31
+ if (message.includes("CLI 调用超时"))
32
+ return "CLI 模型调用超时,请检查登录、网络或权限审批";
33
+ if (message.includes("CLI 请求已取消"))
34
+ return "CLI 模型请求已取消";
35
+ if (message.includes("插件已停止"))
36
+ return "插件已停止,当前 CLI 模型任务已终止";
37
+ if (message.includes("CLI 输出超过"))
38
+ return "CLI 模型输出超过 4MB 限制";
39
+ return "CLI 模型调用或结构化输出失败,请检查 CLI 状态";
40
+ }
25
41
  async function resolveWindowsCliShim(file, provider) {
26
42
  const modules = (0, node_path_1.basename)((0, node_path_1.dirname)(file)) === ".bin" ? (0, node_path_1.dirname)((0, node_path_1.dirname)(file)) : (0, node_path_1.join)((0, node_path_1.dirname)(file), "node_modules");
27
43
  const root = (0, node_path_1.join)(modules, ...(provider === "codex" ? ["@openai", "codex"] : ["@anthropic-ai", "claude-code"]));
@@ -76,6 +92,8 @@ class LocalCliAgent {
76
92
  ready = false;
77
93
  message = "插件尚未启动";
78
94
  version = "";
95
+ modelModeSupported = false;
96
+ get modelInput() { return this.ready && this.modelModeSupported; }
79
97
  sessions = new Map();
80
98
  writes = Promise.resolve();
81
99
  pending = new Map();
@@ -108,6 +126,9 @@ class LocalCliAgent {
108
126
  const help = (await this.run(this.provider === "codex" ? ["exec", "--help"] : ["--help"], "", 10000)).stdout;
109
127
  if (this.provider === "codex" ? !help.includes("--json") || !help.includes("resume") : !help.includes("--output-format") || !help.includes("--resume"))
110
128
  throw new Error("CLI 版本不支持所需的结构化输出和会话恢复,请升级 CLI");
129
+ this.modelModeSupported = this.provider === "claude"
130
+ ? ["--json-schema", "--tools", "--strict-mcp-config", "--mcp-config", "--no-session-persistence", "--settings", "--setting-sources"].every(flag => help.includes(flag))
131
+ : ["--output-schema", "--ephemeral", "--sandbox", "--disable", "--config", "--ignore-rules"].every(flag => help.includes(flag));
111
132
  this.sessions = new Map();
112
133
  try {
113
134
  const data = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(context.dataDir, "cli-sessions.json"), "utf8"));
@@ -128,7 +149,9 @@ class LocalCliAgent {
128
149
  throw new Error(this.message);
129
150
  }
130
151
  }
131
- run(args, input, timeout) {
152
+ run(args, input, timeout, signal) {
153
+ if (signal?.aborted)
154
+ return Promise.reject(new Error("CLI 请求已取消"));
132
155
  const command = this.command;
133
156
  let child;
134
157
  let stop;
@@ -153,8 +176,10 @@ class LocalCliAgent {
153
176
  }
154
177
  };
155
178
  stop = () => terminate("插件已停止,当前 CLI 任务已终止");
179
+ const abort = () => terminate("CLI 请求已取消");
180
+ signal?.addEventListener("abort", abort, { once: true });
156
181
  const timer = setTimeout(() => terminate(`CLI 调用超时(${timeout}ms),请检查登录、网络或权限审批`), timeout);
157
- const cleanup = () => { clearTimeout(timer); this.children.delete(child); };
182
+ const cleanup = () => { clearTimeout(timer); signal?.removeEventListener("abort", abort); this.children.delete(child); };
158
183
  child.stdout.setEncoding("utf8");
159
184
  child.stderr.setEncoding("utf8");
160
185
  const collect = (value, error) => {
@@ -227,35 +252,70 @@ class LocalCliAgent {
227
252
  throw new Error(`${this.manifest.displayName} 本期不支持附件输入`);
228
253
  if (!request.task?.trim())
229
254
  throw new Error("CLI 任务文本不能为空");
255
+ return this.executeCli(request, request.modelResponseSchema);
256
+ }
257
+ /**
258
+ * 模型来源的独立入口。调用方只提供最小模型契约,不能通过普通 Agent handle 进入管理路径。
259
+ */
260
+ async completeStructuredModel(request, responseSchema) {
261
+ if (!request.prompt?.trim())
262
+ throw new Error("模型请求文本不能为空");
263
+ if (!request.sessionId?.trim())
264
+ throw new Error("模型请求会话标识不能为空");
265
+ const response = await this.executeCli({ task: request.prompt, sessionId: request.sessionId, timeoutMs: request.timeoutMs, signal: request.signal }, responseSchema);
266
+ return { text: response.text };
267
+ }
268
+ async executeCli(request, modelSchema) {
269
+ const modelMode = modelSchema !== undefined;
270
+ if (modelMode && !this.modelModeSupported)
271
+ throw new Error("当前 CLI 不支持无工具结构化模型请求,请升级 CLI");
272
+ const schema = modelMode ? JSON.stringify(modelSchema) : undefined;
273
+ if (modelMode && (!modelSchema || typeof modelSchema !== "object" || Array.isArray(modelSchema) || !schema || Buffer.byteLength(schema) > 64_000))
274
+ throw new Error("模型输出 Schema 无效或过大");
230
275
  const logical = request.sessionId || (0, node_crypto_1.randomUUID)();
231
276
  return this.serial(logical, async () => {
232
277
  this.requireReady();
233
- const id = request.newSession ? undefined : this.sessions.get(logical);
278
+ const id = modelMode || request.newSession ? undefined : this.sessions.get(logical);
279
+ let schemaFile;
280
+ let schemaDirectory;
234
281
  const args = this.provider === "codex"
235
282
  ? ["exec", ...(id ? ["resume", id] : []), "--json", "-"]
236
283
  : ["--print", "--output-format", "json", ...(id ? ["--resume", id] : [])];
284
+ if (modelMode && this.provider === "claude")
285
+ args.push("--json-schema", schema, "--tools", "", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--settings", '{"disableAllHooks":true}', "--setting-sources", "user", "--no-session-persistence");
237
286
  const timeout = request.timeoutMs ?? this.timeoutMs;
238
287
  if (!Number.isInteger(timeout) || timeout <= 0 || timeout > 2147483647)
239
288
  throw new Error("任务超时必须是有效的正整数毫秒数");
240
289
  const started = Date.now();
241
290
  this.context.logger.info("本地 CLI 任务开始", { plugin: this.manifest.id, sessionId: logical, resume: Boolean(id) });
242
291
  try {
243
- const output = await this.run(args, request.task, timeout);
244
- const response = this.parse(output.stdout);
292
+ if (modelMode && this.provider === "codex") {
293
+ schemaDirectory = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "mab-codex-model-"));
294
+ schemaFile = (0, node_path_1.join)(schemaDirectory, "response-schema.json");
295
+ await (0, promises_1.writeFile)(schemaFile, schema, { flag: "wx", mode: 0o600 });
296
+ args.splice(-1, 0, "--ephemeral", "--sandbox", "read-only", "--output-schema", schemaFile, "--disable", "shell_tool", "--disable", "apps", "--disable", "plugins", "--disable", "hooks", "--ignore-rules", "--config", "mcp_servers={}");
297
+ }
298
+ const output = await this.run(args, request.task, timeout, request.signal);
299
+ const response = this.parse(output.stdout, modelMode);
245
300
  if (id && response.id !== id)
246
301
  throw new Error("CLI 恢复返回的会话 ID 与指定会话不一致,保留原会话绑定");
247
- await this.save(logical, response.id);
302
+ if (!modelMode)
303
+ await this.save(logical, response.id);
248
304
  this.context.logger.info("本地 CLI 任务完成", { plugin: this.manifest.id, sessionId: logical, durationMs: Date.now() - started, exitCode: 0 });
249
- return { text: response.text, sessionId: logical };
305
+ return { text: response.text, ...(!modelMode ? { sessionId: logical } : {}) };
250
306
  }
251
307
  catch (error) {
252
- const detail = diagnostic(String(error));
308
+ const detail = modelMode ? modelDiagnostic(error) : diagnostic(String(error));
253
309
  this.context.logger.error("本地 CLI 任务失败", { plugin: this.manifest.id, sessionId: logical, durationMs: Date.now() - started, error: detail });
254
310
  throw new Error(`${this.manifest.displayName}:${detail}`);
255
311
  }
312
+ finally {
313
+ if (schemaFile && schemaDirectory)
314
+ await (0, promises_1.rm)(schemaDirectory, { recursive: true, force: true });
315
+ }
256
316
  });
257
317
  }
258
- parse(stdout) {
318
+ parse(stdout, modelMode = false) {
259
319
  let id = "", text = "";
260
320
  try {
261
321
  if (this.provider === "codex") {
@@ -287,6 +347,11 @@ class LocalCliAgent {
287
347
  throw new Error(JSON.stringify(event.errors ?? event));
288
348
  id = event.session_id;
289
349
  text = event.result;
350
+ if (modelMode) {
351
+ if (!event.structured_output || typeof event.structured_output !== "object" || Array.isArray(event.structured_output))
352
+ throw new Error("缺少有效结构化模型输出");
353
+ text = JSON.stringify(event.structured_output);
354
+ }
290
355
  }
291
356
  if (typeof id !== "string" || !nativeId.test(id))
292
357
  throw new Error("缺少有效原生会话 ID");
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.responseSchema = void 0;
4
+ exports.claudeCliCompletion = claudeCliCompletion;
5
+ const tools_1 = require("./tools");
6
+ exports.responseSchema = {
7
+ type: "object", additionalProperties: false, required: ["role", "content", "tool_calls"],
8
+ properties: {
9
+ role: { type: "string", enum: ["assistant"] },
10
+ content: { type: ["string", "null"], maxLength: 32_000 },
11
+ tool_calls: { type: "array", maxItems: 8, items: {
12
+ type: "object", additionalProperties: false, required: ["id", "type", "function"],
13
+ properties: {
14
+ id: { type: "string", minLength: 1, maxLength: 128 }, type: { type: "string", enum: ["function"] },
15
+ function: { type: "object", additionalProperties: false, required: ["name", "arguments"], properties: {
16
+ name: { type: "string", enum: [...tools_1.TOOL_NAMES] }, arguments: { type: "string", maxLength: 64_000 },
17
+ } },
18
+ },
19
+ } },
20
+ },
21
+ };
22
+ function claudeCliCompletion(callPlugin, timeoutMs) {
23
+ return async (messages, signal) => {
24
+ const response = await callPlugin("claude-cli", {
25
+ task: JSON.stringify({ instructions: "你是个人助理的模型推理器。按照 messages 中的系统要求处理最后一条用户消息;tools 仅描述宿主提供的管理工具。需要查询或执行任务时返回 tool_calls,待工具结果返回后再回答;不要编造结果。每次调用使用不同 id。最终回答时 tool_calls 为空数组。仅返回 Schema 规定的 JSON。", messages, tools: (0, tools_1.toolDefinitions)() }),
26
+ modelResponseSchema: exports.responseSchema, timeoutMs, signal,
27
+ });
28
+ return response.text;
29
+ };
30
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCoordinator = createCoordinator;
4
+ const node_child_process_1 = require("node:child_process");
5
+ const node_util_1 = require("node:util");
6
+ const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
7
+ const isAid = (value) => typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{2,255}$/.test(value);
8
+ const isAgentId = (value) => typeof value === "string" && /^[a-z0-9][a-z0-9-]{0,63}$/.test(value);
9
+ const isThreadId = (value) => typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/.test(value);
10
+ function createCoordinator(options) {
11
+ if (!isAid(options.selfAid))
12
+ throw new Error("selfAid 无效");
13
+ const allowed = new Map();
14
+ for (const agent of options.allowedAgents) {
15
+ if (!isAgentId(agent.id) || !Array.isArray(agent.aids) || agent.aids.some((aid) => !isAid(aid)))
16
+ throw new Error("Agent 白名单无效");
17
+ allowed.set(agent.id, new Set(agent.aids));
18
+ }
19
+ const run = options.execFile ?? (async (file, args) => execFileAsync(file, args, { timeout: 30_000, maxBuffer: 1024 * 1024, encoding: "utf8", windowsHide: true }));
20
+ const command = async (args) => {
21
+ const { stdout } = await run(options.ecPath ?? "ec", args);
22
+ let value;
23
+ try {
24
+ value = JSON.parse(stdout);
25
+ }
26
+ catch {
27
+ throw new Error("EvolCore 返回了无效 JSON");
28
+ }
29
+ if (!value || typeof value !== "object" || Array.isArray(value) || value.ok === false)
30
+ throw new Error("EvolCore 命令失败");
31
+ return value;
32
+ };
33
+ return {
34
+ async dispatch({ agentId, aid, task }) {
35
+ if (!isAgentId(agentId) || !isAid(aid) || !allowed.get(agentId)?.has(aid))
36
+ throw new Error("目标 Agent/AID 不在白名单中");
37
+ if (typeof task !== "string" || !task.trim() || task.length > 20_000)
38
+ throw new Error("任务长度无效");
39
+ const thread = await command(["thread", "create", aid, "--name", `个人助理派活-${agentId}`, "--format", "json"]);
40
+ const threadId = thread.thread_id ?? thread.threadId ?? thread.id;
41
+ if (!isThreadId(threadId))
42
+ throw new Error("EvolCore 未返回有效 threadId");
43
+ await command(["msg", "send", options.selfAid, aid, "--text", task, "--thread", threadId, "--return", "required", "--format", "json"]);
44
+ return { status: "pending", agentId, aid, threadId };
45
+ },
46
+ async getStatus({ aid, threadId }) {
47
+ if (!isAid(aid))
48
+ throw new Error("AID 无效");
49
+ if (!isThreadId(threadId))
50
+ throw new Error("threadId 无效");
51
+ const history = await command(["thread", "history", threadId, "--format", "json"]);
52
+ const state = history.status ?? history.state;
53
+ const status = state === "completed" || state === "failed" || state === "cancelled" ? state : "pending";
54
+ return { status, aid, threadId, history };
55
+ },
56
+ };
57
+ }