@minko-ai/minko 0.1.0 → 0.1.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.
Files changed (189) hide show
  1. package/README.md +577 -2
  2. package/assets/mascot-preview.png +0 -0
  3. package/assets/mascot-preview.txt +46 -0
  4. package/assets/mascot-source.png +0 -0
  5. package/assets/skills/README.md +21 -0
  6. package/assets/skills/brainstorming/LICENSE +21 -0
  7. package/assets/skills/brainstorming/SKILL.md +316 -0
  8. package/assets/skills/buddhist-method/LICENSE +21 -0
  9. package/assets/skills/buddhist-method/SKILL.md +137 -0
  10. package/assets/skills/buddhist-method/references/ariyasacca-debug.md +57 -0
  11. package/assets/skills/buddhist-method/references/extended-principles.md +100 -0
  12. package/assets/skills/code-humanizer/LICENSE +21 -0
  13. package/assets/skills/code-humanizer/SKILL.md +174 -0
  14. package/assets/skills/code-review/LICENSE +21 -0
  15. package/assets/skills/code-review/SKILL.md +232 -0
  16. package/assets/skills/code-review/reference/angular.md +768 -0
  17. package/assets/skills/code-review/reference/architecture-review-guide.md +472 -0
  18. package/assets/skills/code-review/reference/c.md +890 -0
  19. package/assets/skills/code-review/reference/code-quality-universal.md +488 -0
  20. package/assets/skills/code-review/reference/code-review-best-practices.md +136 -0
  21. package/assets/skills/code-review/reference/common-bugs-checklist.md +302 -0
  22. package/assets/skills/code-review/reference/cpp.md +893 -0
  23. package/assets/skills/code-review/reference/cross-cutting/async-concurrency-patterns.md +515 -0
  24. package/assets/skills/code-review/reference/cross-cutting/error-handling-principles.md +492 -0
  25. package/assets/skills/code-review/reference/cross-cutting/n-plus-one-queries.md +309 -0
  26. package/assets/skills/code-review/reference/cross-cutting/sql-injection-prevention.md +308 -0
  27. package/assets/skills/code-review/reference/cross-cutting/xss-prevention.md +264 -0
  28. package/assets/skills/code-review/reference/csharp.md +525 -0
  29. package/assets/skills/code-review/reference/css-less-sass.md +661 -0
  30. package/assets/skills/code-review/reference/dart.md +670 -0
  31. package/assets/skills/code-review/reference/django.md +985 -0
  32. package/assets/skills/code-review/reference/fastapi.md +580 -0
  33. package/assets/skills/code-review/reference/go.md +993 -0
  34. package/assets/skills/code-review/reference/java.md +409 -0
  35. package/assets/skills/code-review/reference/java8.md +586 -0
  36. package/assets/skills/code-review/reference/kotlin.md +1018 -0
  37. package/assets/skills/code-review/reference/nestjs.md +593 -0
  38. package/assets/skills/code-review/reference/performance-review-guide.md +816 -0
  39. package/assets/skills/code-review/reference/php.md +684 -0
  40. package/assets/skills/code-review/reference/python.md +1073 -0
  41. package/assets/skills/code-review/reference/qt.md +757 -0
  42. package/assets/skills/code-review/reference/react.md +871 -0
  43. package/assets/skills/code-review/reference/ruby.md +964 -0
  44. package/assets/skills/code-review/reference/rust.md +846 -0
  45. package/assets/skills/code-review/reference/security-review-guide.md +494 -0
  46. package/assets/skills/code-review/reference/svelte.md +1064 -0
  47. package/assets/skills/code-review/reference/swift.md +936 -0
  48. package/assets/skills/code-review/reference/typescript.md +1016 -0
  49. package/assets/skills/code-review/reference/vue.md +924 -0
  50. package/assets/skills/code-review/reference/zig.md +440 -0
  51. package/assets/skills/cyclomatic-complexity/LICENSE +201 -0
  52. package/assets/skills/cyclomatic-complexity/SKILL.md +69 -0
  53. package/assets/skills/five-whys/LICENSE +21 -0
  54. package/assets/skills/five-whys/SKILL.md +247 -0
  55. package/assets/skills/five-whys/examples/performance-regression.md +161 -0
  56. package/assets/skills/five-whys/examples/production-incident.md +134 -0
  57. package/assets/skills/five-whys/references/software-patterns.md +284 -0
  58. package/assets/skills/five-whys/references/toyota-origins.md +257 -0
  59. package/assets/skills/systematic-debugging/LICENSE +21 -0
  60. package/assets/skills/systematic-debugging/SKILL.md +283 -0
  61. package/assets/skills/test-driven-development/LICENSE +21 -0
  62. package/assets/skills/test-driven-development/SKILL.md +330 -0
  63. package/assets/skills/verification-before-completion/LICENSE +21 -0
  64. package/assets/skills/verification-before-completion/SKILL.md +120 -0
  65. package/assets/skills/vibe-sec/LICENSE +201 -0
  66. package/assets/skills/vibe-sec/SKILL.md +758 -0
  67. package/assets/skills/writing-plans/LICENSE +21 -0
  68. package/assets/skills/writing-plans/SKILL.md +204 -0
  69. package/bin/minko.js +2 -2
  70. package/dist/agent/approvalTokens.js +224 -0
  71. package/dist/agent/branchLock.js +315 -0
  72. package/dist/agent/compact.js +182 -0
  73. package/dist/agent/context.js +183 -0
  74. package/dist/agent/cron.js +326 -0
  75. package/dist/agent/hooks.js +211 -0
  76. package/dist/agent/lanes.js +511 -0
  77. package/dist/agent/loop.js +337 -0
  78. package/dist/agent/memory.js +159 -0
  79. package/dist/agent/permissions.js +71 -0
  80. package/dist/agent/plan.js +118 -0
  81. package/dist/agent/prompt.js +120 -0
  82. package/dist/agent/subagent.js +67 -0
  83. package/dist/agent/tasks.js +449 -0
  84. package/dist/agent/transcript.js +62 -0
  85. package/dist/agent/workerStore.js +172 -0
  86. package/dist/agent/workerTypes.js +1 -0
  87. package/dist/agent/workers.js +503 -0
  88. package/dist/cli/app.js +261 -0
  89. package/dist/cli/argv.js +206 -0
  90. package/dist/cli/commandsCommand.js +12 -0
  91. package/dist/cli/configCommand.js +170 -0
  92. package/dist/cli/doctorCommand.js +112 -0
  93. package/dist/cli/exportCommand.js +85 -0
  94. package/dist/cli/forkCommand.js +24 -0
  95. package/dist/cli/linkCommand.js +272 -0
  96. package/dist/cli/loginCommand.js +69 -0
  97. package/dist/cli/mcpLoginCommand.js +68 -0
  98. package/dist/cli/pluginCommand.js +113 -0
  99. package/dist/cli/powerCommand.js +174 -0
  100. package/dist/cli/promptFooter.js +74 -0
  101. package/dist/cli/relayCommand.js +38 -0
  102. package/dist/cli/sandboxCommand.js +31 -0
  103. package/dist/cli/serveCommand.js +104 -0
  104. package/dist/cli/statusCommand.js +120 -0
  105. package/dist/cli/tui.js +1104 -0
  106. package/dist/commands/registry.js +99 -0
  107. package/dist/config/paths.js +81 -0
  108. package/dist/config/store.js +605 -0
  109. package/dist/index.js +199 -0
  110. package/dist/link/protocol.js +159 -0
  111. package/dist/link/relay.js +184 -0
  112. package/dist/link/server.js +566 -0
  113. package/dist/link/tunnel.js +90 -0
  114. package/dist/lsp/client.js +340 -0
  115. package/dist/lsp/tool.js +177 -0
  116. package/dist/mcp/client.js +469 -0
  117. package/dist/mcp/httpClient.js +191 -0
  118. package/dist/mcp/server.js +206 -0
  119. package/dist/mcp/tools.js +173 -0
  120. package/dist/oauth/device.js +111 -0
  121. package/dist/oauth/store.js +56 -0
  122. package/dist/plugins/loader.js +291 -0
  123. package/dist/plugins/market.js +98 -0
  124. package/dist/providers/anthropic.js +279 -0
  125. package/dist/providers/factory.js +109 -0
  126. package/dist/providers/openai.js +182 -0
  127. package/dist/providers/retry.js +95 -0
  128. package/dist/providers/sse.js +67 -0
  129. package/dist/providers/types.js +10 -0
  130. package/dist/rag/embeddings.js +171 -0
  131. package/dist/rag/index.js +373 -0
  132. package/dist/rag/server.js +167 -0
  133. package/dist/rag/tool.js +148 -0
  134. package/dist/sandbox/index.js +55 -0
  135. package/dist/sdk/server.js +150 -0
  136. package/dist/sdk/session.js +104 -0
  137. package/dist/session/liveness.js +148 -0
  138. package/dist/session/recorder.js +49 -0
  139. package/dist/session/store.js +171 -0
  140. package/dist/skills/index.js +428 -0
  141. package/dist/theme.js +30 -0
  142. package/dist/tools/fs.js +195 -0
  143. package/dist/tools/notebook.js +109 -0
  144. package/dist/tools/registry.js +32 -0
  145. package/dist/tools/search.js +130 -0
  146. package/dist/tools/shell.js +200 -0
  147. package/dist/tools/structured.js +83 -0
  148. package/dist/tools/types.js +1 -0
  149. package/dist/ui/ansi.js +135 -0
  150. package/dist/ui/banner.js +103 -0
  151. package/dist/ui/input.js +49 -0
  152. package/dist/ui/inputModel.js +113 -0
  153. package/dist/ui/keyboard.js +142 -0
  154. package/dist/ui/mascot.js +34 -0
  155. package/dist/ui/mascotData.js +96 -0
  156. package/dist/ui/md.js +183 -0
  157. package/dist/ui/screen.js +49 -0
  158. package/dist/ui/spinner.js +52 -0
  159. package/dist/ui/transcript.js +287 -0
  160. package/dist/util/clipboard.js +53 -0
  161. package/dist/util/errors.js +50 -0
  162. package/dist/util/version.js +12 -0
  163. package/dist/web/fetch.js +168 -0
  164. package/dist/web/search.js +97 -0
  165. package/package.json +45 -36
  166. package/src/agent/loop.ts +0 -159
  167. package/src/agent/prompt.ts +0 -45
  168. package/src/cli/app.ts +0 -396
  169. package/src/cli/argv.ts +0 -139
  170. package/src/cli/configCommand.ts +0 -175
  171. package/src/config/paths.ts +0 -20
  172. package/src/config/store.ts +0 -149
  173. package/src/index.ts +0 -120
  174. package/src/providers/anthropic.ts +0 -215
  175. package/src/providers/openai.ts +0 -158
  176. package/src/providers/sse.ts +0 -57
  177. package/src/providers/types.ts +0 -59
  178. package/src/session/store.ts +0 -125
  179. package/src/theme.ts +0 -41
  180. package/src/tools/fs.ts +0 -143
  181. package/src/tools/registry.ts +0 -19
  182. package/src/tools/shell.ts +0 -120
  183. package/src/tools/types.ts +0 -26
  184. package/src/ui/ansi.ts +0 -110
  185. package/src/ui/banner.ts +0 -39
  186. package/src/ui/input.ts +0 -54
  187. package/src/ui/md.ts +0 -160
  188. package/src/ui/spinner.ts +0 -58
  189. package/src/util/errors.ts +0 -48
@@ -0,0 +1,503 @@
1
+ import { runAgent } from "./loop.js";
2
+ import { ALL_TOOLS } from "../tools/registry.js";
3
+ import { WorkerStore } from "./workerStore.js";
4
+ const WORKER_SYSTEM = [
5
+ '你是 Minko 的后台只读 worker。',
6
+ '只做被委派的调查,使用 read_file / list_dir / search_files / retrieve_context 等只读工具。',
7
+ '不要修改文件、不要执行命令、不要向用户提问。',
8
+ '完成后给出简明结论;不确定的地方如实说明。',
9
+ ].join('\n');
10
+ /**
11
+ * In-process background workers. Each one is an independent, read-only agent
12
+ * with its own context, bounded concurrency and a timeout.
13
+ */
14
+ export class WorkerRegistry {
15
+ options;
16
+ workers = new Map();
17
+ queue = [];
18
+ running = 0;
19
+ runningWritable = false;
20
+ sequence = 0;
21
+ store;
22
+ constructor(options) {
23
+ this.options = {
24
+ getProvider: options.getProvider,
25
+ getConfig: options.getConfig,
26
+ cwd: options.cwd,
27
+ maxConcurrent: options.maxConcurrent ?? 2,
28
+ timeoutMs: options.timeoutMs ?? 300_000,
29
+ maxOutputChars: options.maxOutputChars ?? 8_000,
30
+ maxWorkers: options.maxWorkers ?? 50,
31
+ approve: options.approve,
32
+ approvalStore: options.approvalStore,
33
+ };
34
+ this.store = options.store ?? new WorkerStore(options.storeDir);
35
+ this.store.markStale();
36
+ }
37
+ getStore() {
38
+ return this.store;
39
+ }
40
+ create(input) {
41
+ const prompt = input.prompt.trim();
42
+ if (prompt.length === 0) {
43
+ throw new Error('缺少 prompt 参数');
44
+ }
45
+ this.sequence += 1;
46
+ const id = 'w-' + Date.now().toString(36) + '-' + this.sequence.toString(36);
47
+ let resolveDone = () => { };
48
+ const done = new Promise((resolve) => { resolveDone = resolve; });
49
+ const cwd = input.cwd?.trim();
50
+ const worker = {
51
+ id,
52
+ name: input.name?.trim() || 'worker-' + this.sequence,
53
+ prompt,
54
+ team: input.team?.trim() || undefined,
55
+ cwd: cwd && cwd.length > 0 ? cwd : this.options.cwd,
56
+ allowWrite: input.allowWrite === true,
57
+ state: 'queued',
58
+ createdAt: new Date().toISOString(),
59
+ controller: new AbortController(),
60
+ done,
61
+ resolveDone,
62
+ };
63
+ this.workers.set(id, worker);
64
+ this.queue.push(worker);
65
+ this.store.save(infoOf(worker));
66
+ this.trim();
67
+ this.pump();
68
+ return infoOf(worker);
69
+ }
70
+ list() {
71
+ return [...this.workers.values()]
72
+ .sort((a, b) => b.createdAt.localeCompare(a.createdAt))
73
+ .map(infoOf);
74
+ }
75
+ get(id) {
76
+ const worker = this.workers.get(id);
77
+ return worker ? infoOf(worker) : null;
78
+ }
79
+ async wait(id, timeoutMs = this.options.timeoutMs) {
80
+ const worker = this.workers.get(id);
81
+ if (!worker) {
82
+ throw new Error('找不到 worker:' + id);
83
+ }
84
+ const finished = await raceTimeout(worker.done, timeoutMs);
85
+ if (!finished) {
86
+ throw new Error('worker 等待超时:' + id);
87
+ }
88
+ return infoOf(worker);
89
+ }
90
+ stop(id) {
91
+ const worker = this.workers.get(id);
92
+ if (!worker) {
93
+ return false;
94
+ }
95
+ if (worker.state === 'queued') {
96
+ this.queue = this.queue.filter((candidate) => candidate !== worker);
97
+ worker.state = 'stopped';
98
+ worker.endedAt = new Date().toISOString();
99
+ this.store.save(infoOf(worker));
100
+ worker.resolveDone();
101
+ return true;
102
+ }
103
+ worker.controller.abort();
104
+ return true;
105
+ }
106
+ stopAll() {
107
+ for (const worker of this.workers.values()) {
108
+ if (worker.state === 'running' || worker.state === 'queued') {
109
+ worker.controller.abort();
110
+ }
111
+ }
112
+ }
113
+ team(name) {
114
+ const wanted = name.trim();
115
+ return [...this.workers.values()]
116
+ .filter((worker) => worker.team === wanted)
117
+ .map(infoOf);
118
+ }
119
+ async waitTeam(name, timeoutMs = this.options.timeoutMs) {
120
+ const members = this.team(name);
121
+ if (members.length === 0) {
122
+ throw new Error('找不到团队:' + name);
123
+ }
124
+ await Promise.all(members.map((member) => this.wait(member.id, timeoutMs)));
125
+ return this.team(name);
126
+ }
127
+ pump() {
128
+ let index = 0;
129
+ while (this.running < this.options.maxConcurrent && index < this.queue.length) {
130
+ const worker = this.queue[index];
131
+ if (worker.allowWrite && this.runningWritable) {
132
+ index += 1;
133
+ continue;
134
+ }
135
+ this.queue.splice(index, 1);
136
+ if (worker.controller.signal.aborted) {
137
+ worker.state = 'stopped';
138
+ worker.endedAt = new Date().toISOString();
139
+ this.store.save(infoOf(worker));
140
+ worker.resolveDone();
141
+ continue;
142
+ }
143
+ if (worker.allowWrite) {
144
+ this.runningWritable = true;
145
+ }
146
+ this.start(worker);
147
+ }
148
+ }
149
+ start(worker) {
150
+ this.running += 1;
151
+ worker.state = 'running';
152
+ worker.startedAt = new Date().toISOString();
153
+ this.store.save(infoOf(worker));
154
+ const timer = setTimeout(() => worker.controller.abort(), this.options.timeoutMs);
155
+ timer.unref?.();
156
+ void (async () => {
157
+ try {
158
+ const allowWrite = worker.allowWrite;
159
+ const tools = ALL_TOOLS.filter((tool) => tool.readOnly
160
+ || (allowWrite && (tool.spec.name === 'write_file' || tool.spec.name === 'edit_file')));
161
+ const approve = allowWrite
162
+ ? this.options.approve ?? (async () => false)
163
+ : async () => false;
164
+ const hooks = {
165
+ onText: () => { },
166
+ approve,
167
+ onAssistantMessage: (message) => {
168
+ this.store.appendMessage(worker.id, message);
169
+ this.store.save(infoOf(worker));
170
+ },
171
+ };
172
+ this.store.appendMessage(worker.id, { role: 'system', content: WORKER_SYSTEM });
173
+ this.store.appendMessage(worker.id, { role: 'user', content: worker.prompt });
174
+ const outcome = await runAgent({
175
+ provider: this.options.getProvider(),
176
+ tools,
177
+ messages: [
178
+ { role: 'system', content: WORKER_SYSTEM },
179
+ { role: 'user', content: worker.prompt },
180
+ ],
181
+ cwd: worker.cwd ?? this.options.cwd,
182
+ signal: worker.controller.signal,
183
+ approval: allowWrite ? 'ask' : 'auto',
184
+ power: allowWrite ? 'restricted' : 'workspace',
185
+ permissions: allowWrite
186
+ ? { deny: [], ask: ['write_file', 'edit_file'], allow: [] }
187
+ : undefined,
188
+ maxRounds: allowWrite ? 30 : 20,
189
+ config: this.options.getConfig?.(),
190
+ approvalStore: this.options.approvalStore,
191
+ hooks,
192
+ });
193
+ worker.result = cap(lastAssistantText(outcome.messages), this.options.maxOutputChars);
194
+ worker.state = worker.controller.signal.aborted ? 'stopped' : 'done';
195
+ }
196
+ catch (error) {
197
+ if (worker.controller.signal.aborted) {
198
+ worker.state = 'stopped';
199
+ }
200
+ else {
201
+ worker.state = 'failed';
202
+ worker.error = error instanceof Error ? error.message : String(error);
203
+ }
204
+ }
205
+ finally {
206
+ clearTimeout(timer);
207
+ if (worker.allowWrite) {
208
+ this.runningWritable = false;
209
+ }
210
+ worker.endedAt = new Date().toISOString();
211
+ this.store.save(infoOf(worker));
212
+ this.running -= 1;
213
+ worker.resolveDone();
214
+ this.trim();
215
+ this.pump();
216
+ }
217
+ })();
218
+ }
219
+ trim() {
220
+ if (this.workers.size <= this.options.maxWorkers) {
221
+ return;
222
+ }
223
+ const completed = [...this.workers.values()]
224
+ .filter((worker) => worker.state === 'done' || worker.state === 'failed' || worker.state === 'stopped')
225
+ .sort((a, b) => (a.endedAt ?? '').localeCompare(b.endedAt ?? ''));
226
+ while (this.workers.size > this.options.maxWorkers && completed.length > 0) {
227
+ const worker = completed.shift();
228
+ if (worker) {
229
+ this.workers.delete(worker.id);
230
+ }
231
+ }
232
+ }
233
+ }
234
+ /** Tools exposed to the main agent for background work and teams. */
235
+ export function createWorkerTools(registry) {
236
+ const create = {
237
+ readOnly: true,
238
+ spec: {
239
+ name: 'worker_create',
240
+ description: '启动一个后台调查 worker,立即返回 id;allow_write 为 true 时可修改工作区文件(每次写入需确认),写入 worker 串行运行。',
241
+ parameters: {
242
+ type: 'object',
243
+ properties: {
244
+ prompt: { type: 'string', description: '交给 worker 的完整任务' },
245
+ name: { type: 'string', description: '便于识别的短名称' },
246
+ allow_write: { type: 'boolean', description: '允许 write_file / edit_file;每次写入需要确认' },
247
+ },
248
+ required: ['prompt'],
249
+ additionalProperties: false,
250
+ },
251
+ },
252
+ async run(args, ctx) {
253
+ const prompt = typeof args.prompt === 'string' ? args.prompt : '';
254
+ const name = typeof args.name === 'string' ? args.name : undefined;
255
+ const allowWrite = args.allow_write === true;
256
+ if (allowWrite && ctx.planMode) {
257
+ throw new Error('计划模式不能启动写入 worker;先提交计划或退出计划模式');
258
+ }
259
+ const worker = registry.create({ prompt, name, cwd: ctx.cwd, allowWrite });
260
+ return '已创建 worker ' + worker.id + '(' + worker.name + ',' + worker.state
261
+ + (allowWrite ? ',可写入' : ',只读') + ')。用 worker_wait 等待结果。';
262
+ },
263
+ };
264
+ const list = {
265
+ readOnly: true,
266
+ spec: {
267
+ name: 'worker_list',
268
+ description: '列出当前进程里的后台 worker 及其状态。',
269
+ parameters: { type: 'object', properties: {}, additionalProperties: false },
270
+ },
271
+ async run() {
272
+ const workers = registry.list();
273
+ if (workers.length === 0) {
274
+ return '还没有 worker。';
275
+ }
276
+ return workers.map((worker) => worker.id + ' [' + worker.state + '] ' + worker.name
277
+ + (worker.team ? ' · team ' + worker.team : '')).join('\n');
278
+ },
279
+ };
280
+ const get = {
281
+ readOnly: true,
282
+ spec: {
283
+ name: 'worker_get',
284
+ description: '查看一个 worker 的状态、结果或错误。',
285
+ parameters: {
286
+ type: 'object',
287
+ properties: { id: { type: 'string' } },
288
+ required: ['id'],
289
+ additionalProperties: false,
290
+ },
291
+ },
292
+ async run(args) {
293
+ const id = typeof args.id === 'string' ? args.id : '';
294
+ const worker = registry.get(id);
295
+ if (!worker) {
296
+ throw new Error('找不到 worker:' + id);
297
+ }
298
+ return formatWorker(worker);
299
+ },
300
+ };
301
+ const wait = {
302
+ readOnly: true,
303
+ spec: {
304
+ name: 'worker_wait',
305
+ description: '等待一个 worker 完成并返回结果;超时会报错。',
306
+ parameters: {
307
+ type: 'object',
308
+ properties: {
309
+ id: { type: 'string' },
310
+ timeout_ms: { type: 'number' },
311
+ },
312
+ required: ['id'],
313
+ additionalProperties: false,
314
+ },
315
+ },
316
+ async run(args) {
317
+ const id = typeof args.id === 'string' ? args.id : '';
318
+ const timeout = typeof args.timeout_ms === 'number' && args.timeout_ms > 0
319
+ ? Math.floor(args.timeout_ms)
320
+ : 120_000;
321
+ const worker = await registry.wait(id, timeout);
322
+ return formatWorker(worker);
323
+ },
324
+ };
325
+ const stop = {
326
+ readOnly: true,
327
+ spec: {
328
+ name: 'worker_stop',
329
+ description: '停止一个后台 worker。',
330
+ parameters: {
331
+ type: 'object',
332
+ properties: { id: { type: 'string' } },
333
+ required: ['id'],
334
+ additionalProperties: false,
335
+ },
336
+ },
337
+ async run(args) {
338
+ const id = typeof args.id === 'string' ? args.id : '';
339
+ if (!registry.stop(id)) {
340
+ throw new Error('找不到 worker:' + id);
341
+ }
342
+ return '已停止 worker ' + id;
343
+ },
344
+ };
345
+ const teamCreate = {
346
+ readOnly: true,
347
+ spec: {
348
+ name: 'team_create',
349
+ description: '创建一个 worker 团队,并行调查多个子任务;任务可设 allow_write 修改文件(写入任务串行)。用 team_wait 收集结果。',
350
+ parameters: {
351
+ type: 'object',
352
+ properties: {
353
+ name: { type: 'string', description: '团队名称' },
354
+ tasks: {
355
+ type: 'array',
356
+ items: {
357
+ type: 'object',
358
+ properties: {
359
+ prompt: { type: 'string' },
360
+ description: { type: 'string' },
361
+ allow_write: { type: 'boolean' },
362
+ },
363
+ required: ['prompt'],
364
+ additionalProperties: false,
365
+ },
366
+ },
367
+ },
368
+ required: ['name', 'tasks'],
369
+ additionalProperties: false,
370
+ },
371
+ },
372
+ async run(args, ctx) {
373
+ const name = typeof args.name === 'string' ? args.name.trim() : '';
374
+ if (name.length === 0) {
375
+ throw new Error('缺少 name 参数');
376
+ }
377
+ if (!Array.isArray(args.tasks) || args.tasks.length === 0) {
378
+ throw new Error('tasks 必须是非空数组');
379
+ }
380
+ if (args.tasks.length > 8) {
381
+ throw new Error('一个团队最多 8 个任务');
382
+ }
383
+ const created = [];
384
+ for (const raw of args.tasks) {
385
+ if (raw === null || typeof raw !== 'object') {
386
+ continue;
387
+ }
388
+ const task = raw;
389
+ if (typeof task.prompt !== 'string' || task.prompt.trim().length === 0) {
390
+ throw new Error('每个任务都需要 prompt');
391
+ }
392
+ const description = typeof task.description === 'string'
393
+ ? task.description.trim()
394
+ : '';
395
+ const allowWrite = task.allow_write === true;
396
+ if (allowWrite && ctx.planMode) {
397
+ throw new Error('计划模式不能启动写入 worker;先提交计划或退出计划模式');
398
+ }
399
+ created.push(registry.create({
400
+ prompt: task.prompt,
401
+ name: description.length > 0 ? description : undefined,
402
+ team: name,
403
+ cwd: ctx.cwd,
404
+ allowWrite,
405
+ }));
406
+ }
407
+ return '团队 ' + name + ' 已创建(' + created.length + ' 个 worker):\n'
408
+ + created.map((worker) => '- ' + worker.id + ' ' + worker.name).join('\n')
409
+ + '\n用 team_wait 等待全部结果。';
410
+ },
411
+ };
412
+ const teamWait = {
413
+ readOnly: true,
414
+ spec: {
415
+ name: 'team_wait',
416
+ description: '等待某个团队的全部 worker 完成,返回每个任务的状态和结果。',
417
+ parameters: {
418
+ type: 'object',
419
+ properties: {
420
+ name: { type: 'string' },
421
+ timeout_ms: { type: 'number' },
422
+ },
423
+ required: ['name'],
424
+ additionalProperties: false,
425
+ },
426
+ },
427
+ async run(args) {
428
+ const name = typeof args.name === 'string' ? args.name.trim() : '';
429
+ if (name.length === 0) {
430
+ throw new Error('缺少 name 参数');
431
+ }
432
+ const timeout = typeof args.timeout_ms === 'number' && args.timeout_ms > 0
433
+ ? Math.floor(args.timeout_ms)
434
+ : 120_000;
435
+ const workers = await registry.waitTeam(name, timeout);
436
+ return '团队 ' + name + '(' + workers.length + ' 个)\n\n'
437
+ + workers.map((worker, index) => (index + 1) + '. ' + formatWorker(worker)).join('\n\n');
438
+ },
439
+ };
440
+ return [create, list, get, wait, stop, teamCreate, teamWait];
441
+ }
442
+ function infoOf(worker) {
443
+ return {
444
+ id: worker.id,
445
+ name: worker.name,
446
+ prompt: worker.prompt,
447
+ team: worker.team,
448
+ cwd: worker.cwd,
449
+ allowWrite: worker.allowWrite,
450
+ state: worker.state,
451
+ result: worker.result,
452
+ error: worker.error,
453
+ createdAt: worker.createdAt,
454
+ startedAt: worker.startedAt,
455
+ endedAt: worker.endedAt,
456
+ };
457
+ }
458
+ function formatWorker(worker) {
459
+ const lines = [worker.id + ' [' + worker.state + '] ' + worker.name
460
+ + (worker.team ? ' · team ' + worker.team : '')];
461
+ if (worker.result) {
462
+ lines.push(worker.result);
463
+ }
464
+ if (worker.error) {
465
+ lines.push('错误:' + worker.error);
466
+ }
467
+ return lines.join('\n');
468
+ }
469
+ function lastAssistantText(messages) {
470
+ const message = [...messages]
471
+ .reverse()
472
+ .find((candidate) => candidate.role === 'assistant' && candidate.content.length > 0);
473
+ return message && message.role === 'assistant'
474
+ ? message.content
475
+ : '(worker 没有返回文本)';
476
+ }
477
+ function cap(text, max) {
478
+ if (text.length <= max) {
479
+ return text;
480
+ }
481
+ const suffix = '\n…(worker 输出过长,已截断)';
482
+ return text.slice(0, Math.max(0, max - suffix.length)) + suffix;
483
+ }
484
+ function raceTimeout(promise, timeoutMs) {
485
+ return new Promise((resolve) => {
486
+ let done = false;
487
+ const timer = setTimeout(() => {
488
+ if (!done) {
489
+ done = true;
490
+ resolve(false);
491
+ }
492
+ }, timeoutMs);
493
+ timer.unref?.();
494
+ void promise.then(() => {
495
+ if (done) {
496
+ return;
497
+ }
498
+ done = true;
499
+ clearTimeout(timer);
500
+ resolve(true);
501
+ });
502
+ });
503
+ }