weacpx 0.4.5 → 0.4.6

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
@@ -169,7 +169,7 @@ weacpx restart
169
169
  | `weacpx status` | 查看后台状态、PID、配置路径、日志路径 |
170
170
  | `weacpx stop` | 停止后台实例 |
171
171
  | `weacpx restart` | 重启后台实例,让频道配置变更生效 |
172
- | `weacpx update [--all|<name>]` | 检查并更新 weacpx 与已安装插件;安装了插件时会交互式选择更新项 |
172
+ | `weacpx update [--all\|<name>]` | 检查并更新 weacpx 与已安装插件;安装了插件时会交互式选择更新项 |
173
173
  | `weacpx channel list` | 查看已配置的消息频道 |
174
174
  | `weacpx plugin known` | 查看官方插件清单(飞书/元宝包名) |
175
175
  | `weacpx plugin add @ganglion/weacpx-channel-feishu && weacpx channel add feishu` | 安装并添加飞书频道,会提示输入飞书应用凭据 |
@@ -351,22 +351,15 @@ README 里只保留用户视角的最常用命令。
351
351
  | 命令 | 说明 |
352
352
  |------|------|
353
353
  | `/dg <agent> <task>` | 快速委派一个子任务 |
354
- | `/group new <title>` | 创建任务组 |
355
- | `/group add <groupId> <agent> <task>` | 往任务组里加子任务 |
356
- | `/groups` | 查看任务组列表 |
357
- | `/group <id>` | 查看单个任务组 |
358
- | `/group cancel <groupId>` | 取消组内未结束任务 |
359
354
  | `/tasks` | 查看当前主线下的任务 |
360
355
  | `/task <id>` | 查看单个任务详情 |
361
356
  | `/task approve <id>` | 批准 `needs_confirmation` 任务 |
362
- | `/task cancel <id>` | 取消任务 |
357
+ | `/task cancel <id>` | 取消任务;取消一个尚未批准的任务等同于拒绝 |
363
358
 
364
359
  最常见例子:
365
360
 
366
361
  ```text
367
362
  /dg claude 审查当前方案的 3 个高风险点
368
- /group new review
369
- /group add review claude 审查接口设计
370
363
  /tasks
371
364
  /task approve task_123
372
365
  ```
@@ -376,9 +369,9 @@ README 里只保留用户视角的最常用命令。
376
369
  - 当前会话就是主控会话
377
370
  - 被委派出去的是独立子任务会话
378
371
  - agent 发起的委派请求默认需要人工确认
379
- - group 适合把 2~3 个独立子任务并行派出去,再把结果汇总回主线
372
+ - 如果你在用外部 MCP host(Codex / Claude Code),用 `delegate_batch` 一次派发多个并行子任务:传一个 `tasks` 数组,底层自动建组,全部结果一次性回注,无需手动维护 groupId
380
373
 
381
- 如果你想先理解什么时候该用 delegate、什么时候该开 group,请看:
374
+ 如果你想先理解什么时候该用 delegate、什么时候应该并行派出多个子任务,请看:
382
375
 
383
376
  - [docs/weacpx-group-usage-guide.md](./docs/weacpx-group-usage-guide.md)
384
377
 
@@ -387,7 +380,7 @@ README 里只保留用户视角的最常用命令。
387
380
 
388
381
  如果你想让 Codex、Claude Code 等外部 MCP host 直接使用 weacpx 的多 Agent 编排能力,可以把 `weacpx mcp-stdio` 配成一个 stdio MCP server。
389
382
 
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`。
383
+ `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_list` / `task_watch` / `task_cancel`。
391
384
 
392
385
  先启动 daemon:
393
386
 
@@ -162,8 +162,12 @@ function extractJsonRpcErrorMessages(output) {
162
162
  `).map((line) => line.trim()).filter((line) => line.length > 0).flatMap((line) => {
163
163
  try {
164
164
  const payload = JSON.parse(line);
165
- if (typeof payload.error?.message === "string" && payload.error.message.length > 0) {
166
- return [payload.error.message];
165
+ const err = payload.error;
166
+ const dataMsg = typeof err?.data?.message === "string" && err.data.message.length > 0 ? err.data.message : undefined;
167
+ const baseMsg = typeof err?.message === "string" && err.message.length > 0 ? err.message : undefined;
168
+ const chosen = dataMsg && dataMsg !== baseMsg ? dataMsg : baseMsg;
169
+ if (chosen) {
170
+ return [chosen];
167
171
  }
168
172
  } catch {
169
173
  return [];
package/dist/cli.js CHANGED
@@ -9617,11 +9617,9 @@ function readVersion(moduleUrl = import.meta.url) {
9617
9617
  var PACKAGE_NAME = "weacpx";
9618
9618
  var init_version = () => {};
9619
9619
 
9620
- // src/orchestration/task-wait-timeouts.ts
9621
- var DEFAULT_TASK_WAIT_TIMEOUT_MS, MAX_TASK_WAIT_TIMEOUT_MS, DEFAULT_TASK_WAIT_POLL_INTERVAL_MS = 1000, MAX_TASK_WAIT_POLL_INTERVAL_MS = 1e4, TASK_WAIT_RPC_TIMEOUT_PADDING_MS = 5000, DEFAULT_TASK_WATCH_TIMEOUT_MS = 60000, MAX_TASK_WATCH_TIMEOUT_MS, DEFAULT_TASK_WATCH_POLL_INTERVAL_MS = 1000, MAX_TASK_WATCH_POLL_INTERVAL_MS = 1e4, TASK_WATCH_RPC_TIMEOUT_PADDING_MS = 5000;
9622
- var init_task_wait_timeouts = __esm(() => {
9623
- DEFAULT_TASK_WAIT_TIMEOUT_MS = 5 * 60000;
9624
- MAX_TASK_WAIT_TIMEOUT_MS = 20 * 60000;
9620
+ // src/orchestration/task-watch-timeouts.ts
9621
+ var DEFAULT_TASK_WATCH_TIMEOUT_MS = 60000, MAX_TASK_WATCH_TIMEOUT_MS, DEFAULT_TASK_WATCH_POLL_INTERVAL_MS = 1000, MAX_TASK_WATCH_POLL_INTERVAL_MS = 1e4, TASK_WATCH_RPC_TIMEOUT_PADDING_MS = 5000;
9622
+ var init_task_watch_timeouts = __esm(() => {
9625
9623
  MAX_TASK_WATCH_TIMEOUT_MS = 20 * 60000;
9626
9624
  });
9627
9625
 
@@ -14019,7 +14017,7 @@ ${buildFinalHeadsUp({
14019
14017
  sendWeixinErrorNotice({
14020
14018
  to,
14021
14019
  contextToken,
14022
- message: `⚠️ 过程失败:${err instanceof Error ? err.message : JSON.stringify(err)}`,
14020
+ message: `⚠️ 执行出错:${err instanceof Error ? err.message : JSON.stringify(err)}`,
14023
14021
  baseUrl: deps.baseUrl,
14024
14022
  token: deps.token,
14025
14023
  errLog: deps.errLog
@@ -15553,8 +15551,12 @@ function extractJsonRpcErrorMessages(output) {
15553
15551
  `).map((line) => line.trim()).filter((line) => line.length > 0).flatMap((line) => {
15554
15552
  try {
15555
15553
  const payload = JSON.parse(line);
15556
- if (typeof payload.error?.message === "string" && payload.error.message.length > 0) {
15557
- return [payload.error.message];
15554
+ const err = payload.error;
15555
+ const dataMsg = typeof err?.data?.message === "string" && err.data.message.length > 0 ? err.data.message : undefined;
15556
+ const baseMsg = typeof err?.message === "string" && err.message.length > 0 ? err.message : undefined;
15557
+ const chosen = dataMsg && dataMsg !== baseMsg ? dataMsg : baseMsg;
15558
+ if (chosen) {
15559
+ return [chosen];
15558
15560
  }
15559
15561
  } catch {
15560
15562
  return [];
@@ -17243,18 +17245,18 @@ function renderDelegateSuccess2(taskId, workerSession) {
17243
17245
  return [`已创建委派任务「${taskId}」`, `worker 会话:${workerSession}`].join(`
17244
17246
  `);
17245
17247
  }
17246
- function renderGroupCreated2(group) {
17248
+ function renderGroupCreated(group) {
17247
17249
  return [`已创建任务组「${group.groupId}」`, `- 标题:${group.title}`].join(`
17248
17250
  `);
17249
17251
  }
17250
- function renderGroupList2(groups) {
17252
+ function renderGroupList(groups) {
17251
17253
  if (groups.length === 0) {
17252
17254
  return "当前协调会话下还没有任务组。";
17253
17255
  }
17254
- return ["当前协调会话的任务组:", ...groups.map((group) => renderGroupListItem2(group))].join(`
17256
+ return ["当前协调会话的任务组:", ...groups.map((group) => renderGroupListItem(group))].join(`
17255
17257
  `);
17256
17258
  }
17257
- function renderGroupSummary2(summary) {
17259
+ function renderGroupSummary(summary) {
17258
17260
  const { group, tasks } = summary;
17259
17261
  const lines = [
17260
17262
  `任务组「${group.groupId}」`,
@@ -17286,7 +17288,7 @@ function renderGroupSummary2(summary) {
17286
17288
  return lines.join(`
17287
17289
  `);
17288
17290
  }
17289
- function renderGroupCancelSuccess2(input) {
17291
+ function renderGroupCancelSuccess(input) {
17290
17292
  return [
17291
17293
  `任务组「${input.summary.group.groupId}」已发起取消`,
17292
17294
  `- 已请求取消:${input.cancelledTaskIds.length}`,
@@ -17369,7 +17371,7 @@ function renderTaskApprovalSuccess2(task) {
17369
17371
  return [`已批准任务「${task.taskId}」。`, `- 当前状态:${task.status}`].join(`
17370
17372
  `);
17371
17373
  }
17372
- function renderTaskRejectionSuccess2(task) {
17374
+ function renderTaskRejectSuccess(task) {
17373
17375
  return [`已拒绝任务「${task.taskId}」。`, `- 当前状态:${task.status}`].join(`
17374
17376
  `);
17375
17377
  }
@@ -17403,7 +17405,7 @@ function renderTaskListItem2(task) {
17403
17405
  ].filter(Boolean).map((item) => `;${item}`).join("");
17404
17406
  return `- ${task.taskId} [${task.status}] ${task.targetAgent}${role} -> ${task.workerSession ?? "未分配"}${group}${source}${summary}${reliability}`;
17405
17407
  }
17406
- function renderGroupListItem2(group) {
17408
+ function renderGroupListItem(group) {
17407
17409
  const reliability = [
17408
17410
  group.group.injectionPending ? "注入待重试" : ""
17409
17411
  ].filter(Boolean).map((item) => `;${item}`).join("");
@@ -17464,7 +17466,7 @@ async function handleGroupCreate(context, chatKey, title) {
17464
17466
  coordinatorSession: session.transportSession,
17465
17467
  title
17466
17468
  });
17467
- return { text: renderGroupCreated2(group) };
17469
+ return { text: renderGroupCreated(group) };
17468
17470
  }
17469
17471
  async function handleGroupList(context, chatKey, filter) {
17470
17472
  const session = await getCurrentSession(context, chatKey);
@@ -17479,7 +17481,7 @@ async function handleGroupList(context, chatKey, filter) {
17479
17481
  coordinatorSession: session.transportSession,
17480
17482
  ...filter ?? {}
17481
17483
  });
17482
- return { text: renderGroupList2(groups) };
17484
+ return { text: renderGroupList(groups) };
17483
17485
  }
17484
17486
  async function handleGroupGet(context, chatKey, groupId) {
17485
17487
  const session = await getCurrentSession(context, chatKey);
@@ -17497,7 +17499,7 @@ async function handleGroupGet(context, chatKey, groupId) {
17497
17499
  if (!group) {
17498
17500
  return { text: GROUP_NOT_FOUND_TEXT };
17499
17501
  }
17500
- return { text: renderGroupSummary2(group) };
17502
+ return { text: renderGroupSummary(group) };
17501
17503
  }
17502
17504
  async function handleGroupCancel(context, chatKey, groupId) {
17503
17505
  const session = await getCurrentSession(context, chatKey);
@@ -17519,7 +17521,7 @@ async function handleGroupCancel(context, chatKey, groupId) {
17519
17521
  groupId,
17520
17522
  coordinatorSession: session.transportSession
17521
17523
  });
17522
- return { text: renderGroupCancelSuccess2(cancelled) };
17524
+ return { text: renderGroupCancelSuccess(cancelled) };
17523
17525
  }
17524
17526
  async function handleGroupDelegate(context, chatKey, groupId, targetAgent, task, role, replyContextToken, accountId) {
17525
17527
  const session = await getCurrentSession(context, chatKey);
@@ -17607,11 +17609,11 @@ async function handleTaskReject(context, chatKey, taskId) {
17607
17609
  if (task.status !== "needs_confirmation") {
17608
17610
  return { text: renderTaskConfirmationUnavailable(task) };
17609
17611
  }
17610
- const rejected = await orchestration.rejectTask({
17612
+ const rejected = await orchestration.cancelTask({
17611
17613
  taskId,
17612
17614
  coordinatorSession: session.transportSession
17613
17615
  });
17614
- return { text: renderTaskRejectionSuccess2(rejected) };
17616
+ return { text: renderTaskRejectSuccess(rejected) };
17615
17617
  }
17616
17618
  async function handleTaskCancel(context, chatKey, taskId) {
17617
17619
  const session = await getCurrentSession(context, chatKey);
@@ -19309,8 +19311,6 @@ class OrchestrationServer {
19309
19311
  return await this.dispatchTaskGet(params);
19310
19312
  case "task.list":
19311
19313
  return await this.handlers.listTasks(this.parseTaskListFilter(params));
19312
- case "task.wait":
19313
- return await this.handlers.waitTask(this.parseWaitTaskInput(params));
19314
19314
  case "task.watch":
19315
19315
  return await this.handlers.watchTask(this.parseWatchTaskInput(params));
19316
19316
  case "task.approve":
@@ -19319,12 +19319,6 @@ class OrchestrationServer {
19319
19319
  taskId: requireString(params, "taskId"),
19320
19320
  coordinatorSession: requireString(params, "coordinatorSession")
19321
19321
  });
19322
- case "task.reject":
19323
- requireOnlyKeys(params, ["taskId", "coordinatorSession"], "params");
19324
- return await this.handlers.rejectTask({
19325
- taskId: requireString(params, "taskId"),
19326
- coordinatorSession: requireString(params, "coordinatorSession")
19327
- });
19328
19322
  case "task.cancel":
19329
19323
  return await this.handlers.cancelTask(this.parseCancelTaskInput(params));
19330
19324
  case "worker.reply":
@@ -19357,15 +19351,6 @@ class OrchestrationServer {
19357
19351
  ...expectedActivePackageId !== undefined ? { expectedActivePackageId } : {}
19358
19352
  });
19359
19353
  }
19360
- case "coordinator.follow_up_human_package":
19361
- requireOnlyKeys(params, ["coordinatorSession", "packageId", "priorMessageId", "taskQuestions", "promptText"], "params");
19362
- return await this.handlers.coordinatorFollowUpHumanPackage({
19363
- coordinatorSession: requireString(params, "coordinatorSession"),
19364
- packageId: requireString(params, "packageId"),
19365
- priorMessageId: requireString(params, "priorMessageId"),
19366
- taskQuestions: requireTaskQuestions(params, "taskQuestions"),
19367
- promptText: requireString(params, "promptText")
19368
- });
19369
19354
  case "coordinator.review_contested_result":
19370
19355
  requireOnlyKeys(params, ["coordinatorSession", "taskId", "reviewId", "decision"], "params");
19371
19356
  return await this.handlers.coordinatorReviewContestedResult({
@@ -19380,20 +19365,6 @@ class OrchestrationServer {
19380
19365
  coordinatorSession: requireString(params, "coordinatorSession"),
19381
19366
  title: requireString(params, "title")
19382
19367
  });
19383
- case "group.get":
19384
- requireOnlyKeys(params, ["coordinatorSession", "groupId"], "params");
19385
- return await this.handlers.getGroupSummary({
19386
- coordinatorSession: requireString(params, "coordinatorSession"),
19387
- groupId: requireString(params, "groupId")
19388
- });
19389
- case "group.list":
19390
- return await this.handlers.listGroupSummaries(this.parseGroupListFilter(params));
19391
- case "group.cancel":
19392
- requireOnlyKeys(params, ["coordinatorSession", "groupId"], "params");
19393
- return await this.handlers.cancelGroup({
19394
- coordinatorSession: requireString(params, "coordinatorSession"),
19395
- groupId: requireString(params, "groupId")
19396
- });
19397
19368
  default:
19398
19369
  throw new OrchestrationInvalidRequestError(`unsupported orchestration method: ${method}`);
19399
19370
  }
@@ -19488,17 +19459,6 @@ class OrchestrationServer {
19488
19459
  ...resultText !== undefined ? { resultText } : {}
19489
19460
  };
19490
19461
  }
19491
- parseWaitTaskInput(params) {
19492
- requireOnlyKeys(params, ["coordinatorSession", "taskId", "timeoutMs", "pollIntervalMs"], "params");
19493
- const timeoutMs = requireOptionalIntegerInRange(params, "timeoutMs", 0, MAX_TASK_WAIT_TIMEOUT_MS);
19494
- const pollIntervalMs = requireOptionalIntegerInRange(params, "pollIntervalMs", 1, MAX_TASK_WAIT_POLL_INTERVAL_MS);
19495
- return {
19496
- coordinatorSession: requireString(params, "coordinatorSession"),
19497
- taskId: requireString(params, "taskId"),
19498
- ...timeoutMs !== undefined ? { timeoutMs } : {},
19499
- ...pollIntervalMs !== undefined ? { pollIntervalMs } : {}
19500
- };
19501
- }
19502
19462
  parseWatchTaskInput(params) {
19503
19463
  requireOnlyKeys(params, ["coordinatorSession", "taskId", "afterSeq", "mode", "includeProgress", "timeoutMs", "pollIntervalMs"], "params");
19504
19464
  const afterSeq = requireOptionalIntegerInRange(params, "afterSeq", 0, Number.MAX_SAFE_INTEGER);
@@ -19526,20 +19486,6 @@ class OrchestrationServer {
19526
19486
  whatIsNeeded: requireString(params, "whatIsNeeded")
19527
19487
  };
19528
19488
  }
19529
- parseGroupListFilter(params) {
19530
- requireOnlyKeys(params, ["coordinatorSession", "status", "stuck", "sort", "order"], "params");
19531
- const status = requireOptionalEnum(params, "status", ["pending", "running", "terminal"]);
19532
- const stuck = requireOptionalBoolean(params, "stuck");
19533
- const sort = requireOptionalEnum(params, "sort", ["updatedAt", "createdAt"]);
19534
- const order = requireOptionalEnum(params, "order", ["asc", "desc"]);
19535
- return {
19536
- coordinatorSession: requireString(params, "coordinatorSession"),
19537
- ...status !== undefined ? { status } : {},
19538
- ...stuck !== undefined ? { stuck } : {},
19539
- ...sort !== undefined ? { sort } : {},
19540
- ...order !== undefined ? { order } : {}
19541
- };
19542
- }
19543
19489
  async cleanupEndpoint() {
19544
19490
  if (this.endpoint.kind !== "unix") {
19545
19491
  return;
@@ -19750,7 +19696,7 @@ function isServerNotRunningError(error2) {
19750
19696
  var OrchestrationInvalidRequestError, ORCHESTRATION_RPC_METHODS;
19751
19697
  var init_orchestration_server = __esm(() => {
19752
19698
  init_orchestration_ipc();
19753
- init_task_wait_timeouts();
19699
+ init_task_watch_timeouts();
19754
19700
  OrchestrationInvalidRequestError = class OrchestrationInvalidRequestError extends Error {
19755
19701
  };
19756
19702
  ORCHESTRATION_RPC_METHODS = new Set([
@@ -19758,22 +19704,16 @@ var init_orchestration_server = __esm(() => {
19758
19704
  "delegate.request",
19759
19705
  "task.get",
19760
19706
  "task.list",
19761
- "task.wait",
19762
19707
  "task.watch",
19763
19708
  "task.approve",
19764
- "task.reject",
19765
19709
  "task.cancel",
19766
19710
  "worker.reply",
19767
19711
  "worker.raise_question",
19768
19712
  "coordinator.answer_question",
19769
19713
  "coordinator.retract_answer",
19770
19714
  "coordinator.request_human_input",
19771
- "coordinator.follow_up_human_package",
19772
19715
  "coordinator.review_contested_result",
19773
- "group.new",
19774
- "group.get",
19775
- "group.list",
19776
- "group.cancel"
19716
+ "group.new"
19777
19717
  ]);
19778
19718
  });
19779
19719
 
@@ -19950,15 +19890,6 @@ class OrchestrationService {
19950
19890
  this.logEvent("orchestration.group.created", "group created", this.groupContext(group));
19951
19891
  return group;
19952
19892
  }
19953
- async getGroup(groupId) {
19954
- const state = await this.deps.loadState();
19955
- const group = this.ensureGroups(state)[groupId];
19956
- return group ? { ...group } : null;
19957
- }
19958
- async listGroups(coordinatorSession) {
19959
- const state = await this.deps.loadState();
19960
- return Object.values(this.ensureGroups(state)).filter((group) => coordinatorSession === undefined || group.coordinatorSession === coordinatorSession).sort((left, right) => left.createdAt.localeCompare(right.createdAt)).map((group) => ({ ...group }));
19961
- }
19962
19893
  async getGroupSummary(input) {
19963
19894
  const state = await this.deps.loadState();
19964
19895
  const group = this.ensureGroups(state)[input.groupId];
@@ -20597,30 +20528,6 @@ class OrchestrationService {
20597
20528
  const task = state.orchestration.tasks[taskId];
20598
20529
  return task ? { ...task } : null;
20599
20530
  }
20600
- async waitTask(input) {
20601
- const timeoutMs = clampWaitTimeout(input.timeoutMs);
20602
- const pollIntervalMs = clampPollInterval(input.pollIntervalMs);
20603
- const deadline = Date.now() + timeoutMs;
20604
- while (true) {
20605
- const state = await this.deps.loadState();
20606
- const task = state.orchestration.tasks[input.taskId];
20607
- if (!task || task.coordinatorSession !== input.coordinatorSession) {
20608
- return { status: "not_found", task: null };
20609
- }
20610
- const snapshot = { ...task };
20611
- if (isTerminalTaskStatus2(task.status) && task.reviewPending === undefined) {
20612
- return { status: "terminal", task: snapshot };
20613
- }
20614
- if (isAttentionRequiredTask(task)) {
20615
- return { status: "attention_required", task: snapshot };
20616
- }
20617
- const remainingMs = deadline - Date.now();
20618
- if (remainingMs <= 0) {
20619
- return { status: "timeout", task: snapshot };
20620
- }
20621
- await sleep2(Math.min(pollIntervalMs, remainingMs));
20622
- }
20623
- }
20624
20531
  async watchTask(input) {
20625
20532
  const timeoutMs = clampWatchTimeout(input.timeoutMs);
20626
20533
  const pollIntervalMs = clampWatchPollInterval(input.pollIntervalMs);
@@ -21039,88 +20946,6 @@ class OrchestrationService {
21039
20946
  queuedTaskIds: prepared.queuedTaskIds
21040
20947
  };
21041
20948
  }
21042
- async coordinatorFollowUpHumanPackage(input) {
21043
- const promptText = input.promptText.trim();
21044
- if (promptText.length === 0) {
21045
- throw new Error("promptText must be a non-empty string");
21046
- }
21047
- if (input.taskQuestions.length === 0) {
21048
- throw new Error("taskQuestions must contain at least one question");
21049
- }
21050
- const prepared = await this.mutate(async () => {
21051
- const state = await this.deps.loadState();
21052
- if (this.isExternalCoordinatorSession(state, input.coordinatorSession)) {
21053
- throw new Error("human input routing is not configured for external coordinator");
21054
- }
21055
- const coordinatorState = this.ensureCoordinatorQuestionState(state, input.coordinatorSession);
21056
- if (coordinatorState.activePackageId !== input.packageId) {
21057
- throw new Error(`package "${input.packageId}" is not the active package for coordinator "${input.coordinatorSession}"`);
21058
- }
21059
- const packageRecord = this.ensureHumanQuestionPackages(state)[input.packageId];
21060
- if (!packageRecord || packageRecord.status !== "active") {
21061
- throw new Error(`package "${input.packageId}" is not active`);
21062
- }
21063
- const latestMessage = packageRecord.messages.at(-1);
21064
- if (!latestMessage || latestMessage.messageId !== input.priorMessageId) {
21065
- throw new Error(`package "${input.packageId}" latest message is "${latestMessage?.messageId ?? ""}", not "${input.priorMessageId}"`);
21066
- }
21067
- if (!latestMessage.deliveredAt) {
21068
- throw new Error(`package "${input.packageId}" latest message "${latestMessage.messageId}" is not delivered yet`);
21069
- }
21070
- const tasks = input.taskQuestions.map(({ taskId, questionId }) => {
21071
- const task = state.orchestration.tasks[taskId];
21072
- if (!task) {
21073
- throw new Error(`task "${taskId}" does not exist`);
21074
- }
21075
- this.assertCoordinatorOwnership(task, input.coordinatorSession);
21076
- if (!packageRecord.openTaskIds.includes(taskId)) {
21077
- throw new Error(`task "${taskId}" does not belong to active package "${input.packageId}"`);
21078
- }
21079
- this.assertCoordinatorQuestionMatch(task, questionId);
21080
- return task;
21081
- });
21082
- const now = this.deps.now().toISOString();
21083
- const route = this.resolveFrozenPackageMessageRoute(latestMessage);
21084
- const messageId = this.deps.createId();
21085
- const message = {
21086
- messageId,
21087
- kind: "follow_up",
21088
- promptText,
21089
- createdAt: now,
21090
- taskQuestions: input.taskQuestions.map((entry) => ({ ...entry })),
21091
- ...route ? this.serializeFrozenDeliveryRoute(route) : {}
21092
- };
21093
- packageRecord.messages.push(message);
21094
- packageRecord.awaitingReplyMessageId = undefined;
21095
- packageRecord.updatedAt = now;
21096
- for (const task of tasks) {
21097
- task.status = "waiting_for_human";
21098
- task.openQuestion = {
21099
- ...task.openQuestion,
21100
- packageId: input.packageId
21101
- };
21102
- task.updatedAt = now;
21103
- this.appendTaskEvent(task, now, "attention_required", {
21104
- status: "waiting_for_human",
21105
- message: task.openQuestion.question
21106
- });
21107
- this.bumpGroupUpdated(state, task.groupId, now);
21108
- }
21109
- await this.deps.saveState(state);
21110
- return {
21111
- coordinatorSession: input.coordinatorSession,
21112
- packageId: input.packageId,
21113
- messageId,
21114
- promptText,
21115
- route
21116
- };
21117
- });
21118
- await this.deliverHumanQuestionPackageMessage(prepared);
21119
- return {
21120
- packageId: prepared.packageId,
21121
- messageId: prepared.messageId
21122
- };
21123
- }
21124
20949
  async retryHumanQuestionPackageDelivery(input) {
21125
20950
  const prepared = await this.mutate(async () => {
21126
20951
  const state = await this.deps.loadState();
@@ -21760,7 +21585,11 @@ class OrchestrationService {
21760
21585
  return { task: { ...task }, shouldPropagate, closedPackageId: undefined };
21761
21586
  }
21762
21587
  const closedPackageId = this.detachTaskFromQuestionFlows(state, task, now);
21588
+ const wasNeedsConfirmation = task.status === "needs_confirmation";
21763
21589
  task.status = "cancelled";
21590
+ if (wasNeedsConfirmation && task.summary.trim().length === 0) {
21591
+ task.summary = "rejected";
21592
+ }
21764
21593
  task.openQuestion = undefined;
21765
21594
  task.cancelRequestedAt = task.cancelRequestedAt ?? now;
21766
21595
  task.cancelCompletedAt = now;
@@ -21980,29 +21809,6 @@ class OrchestrationService {
21980
21809
  this.logEvent("orchestration.task.approved", "task approved", this.taskContext(prepared.task));
21981
21810
  return prepared.task;
21982
21811
  }
21983
- async rejectTask(input) {
21984
- const task = await this.mutate(async () => {
21985
- const state = await this.deps.loadState();
21986
- const task2 = state.orchestration.tasks[input.taskId];
21987
- if (!task2) {
21988
- throw new Error(`task "${input.taskId}" does not exist`);
21989
- }
21990
- this.assertCoordinatorOwnership(task2, input.coordinatorSession);
21991
- this.assertNeedsConfirmation(task2);
21992
- task2.status = "cancelled";
21993
- task2.summary = "rejected";
21994
- task2.updatedAt = this.deps.now().toISOString();
21995
- this.appendTaskEvent(task2, task2.updatedAt, "status_changed", {
21996
- status: "cancelled",
21997
- summary: "rejected",
21998
- message: "Task rejected"
21999
- });
22000
- await this.deps.saveState(state);
22001
- return { ...task2 };
22002
- });
22003
- this.logEvent("orchestration.task.rejected", "task rejected", this.taskContext(task));
22004
- return task;
22005
- }
22006
21812
  async resolveWorkerSession(input) {
22007
21813
  const role = this.normalizeRole(input.role);
22008
21814
  const reusable = await this.deps.findReusableWorkerSession?.({
@@ -22946,24 +22752,6 @@ function isTerminalTaskStatus2(status) {
22946
22752
  function isAttentionRequiredTask(task) {
22947
22753
  return task.reviewPending !== undefined || task.status === "needs_confirmation" || task.status === "blocked" || task.status === "waiting_for_human";
22948
22754
  }
22949
- function clampWaitTimeout(timeoutMs) {
22950
- if (timeoutMs === undefined) {
22951
- return DEFAULT_TASK_WAIT_TIMEOUT_MS;
22952
- }
22953
- if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
22954
- return 0;
22955
- }
22956
- return Math.min(Math.floor(timeoutMs), MAX_TASK_WAIT_TIMEOUT_MS);
22957
- }
22958
- function clampPollInterval(pollIntervalMs) {
22959
- if (pollIntervalMs === undefined) {
22960
- return DEFAULT_TASK_WAIT_POLL_INTERVAL_MS;
22961
- }
22962
- if (!Number.isFinite(pollIntervalMs) || pollIntervalMs <= 0) {
22963
- return 1;
22964
- }
22965
- return Math.min(Math.floor(pollIntervalMs), MAX_TASK_WAIT_POLL_INTERVAL_MS);
22966
- }
22967
22755
  async function sleep2(ms) {
22968
22756
  await new Promise((resolve3) => setTimeout(resolve3, ms));
22969
22757
  }
@@ -22973,7 +22761,7 @@ function isRequestDelegateInput(input) {
22973
22761
  var MAX_TASK_EVENTS_PER_TASK = 200;
22974
22762
  var init_orchestration_service = __esm(() => {
22975
22763
  init_quota_errors();
22976
- init_task_wait_timeouts();
22764
+ init_task_watch_timeouts();
22977
22765
  });
22978
22766
 
22979
22767
  // src/orchestration/worker-prompts.ts
@@ -39387,9 +39175,8 @@ function requireHome(env) {
39387
39175
  }
39388
39176
 
39389
39177
  // src/mcp/weacpx-mcp-tools.ts
39390
- init_task_wait_timeouts();
39178
+ init_task_watch_timeouts();
39391
39179
  init_quota_errors();
39392
- var groupStatusSchema = exports_external.enum(["pending", "running", "terminal"]);
39393
39180
  var taskStatusSchema = exports_external.enum([
39394
39181
  "needs_confirmation",
39395
39182
  "running",
@@ -39412,7 +39199,7 @@ function buildWeacpxMcpToolRegistry(input) {
39412
39199
  const tools = [
39413
39200
  {
39414
39201
  name: "delegate_request",
39415
- description: `Delegate a subtask to another agent under the current coordinator. Pass an absolute workingDirectory for the worker. Supports MCP Tasks when the client requests task execution: the tool can return a native task handle immediately, then clients can use tasks/get, tasks/result, tasks/list, and tasks/cancel. For legacy clients, after this returns status=running, keep the returned taskId and use task_get/task_list for non-blocking progress snapshots; call task_wait only when the user explicitly wants you to wait/block until completion or attention is required. If status=needs_confirmation, wait for the user to approve (task_approve / task_reject) and do not call task_wait yet.${availableAgents && availableAgents.length > 0 ? ` Available agents: ${availableAgents.join(", ")}.` : ""}`,
39202
+ description: `Delegate a subtask to another agent under the current coordinator. Pass an absolute workingDirectory for the worker. Supports MCP Tasks when the client requests task execution: the tool can return a native task handle immediately, then clients can use tasks/get, tasks/result, tasks/list, and tasks/cancel.${availableAgents && availableAgents.length > 0 ? ` Available agents: ${availableAgents.join(", ")}.` : ""}`,
39416
39203
  execution: { taskSupport: "optional" },
39417
39204
  inputSchema: exports_external.object({
39418
39205
  targetAgent: exports_external.string().min(1),
@@ -39432,66 +39219,41 @@ function buildWeacpxMcpToolRegistry(input) {
39432
39219
  })
39433
39220
  },
39434
39221
  {
39435
- name: "group_new",
39436
- description: "Create a new task group under the current coordinator. Use to batch multiple delegate_request calls together; pass the resulting groupId on each delegate so they share lifecycle and cancellation.",
39437
- inputSchema: exports_external.object({
39438
- title: exports_external.string().min(1)
39439
- }).strict(),
39440
- handler: async (args) => await asToolResult(async () => {
39441
- const group = await transport.createGroup({
39442
- coordinatorSession,
39443
- title: args.title
39444
- });
39445
- return createSuccessResult(renderGroupCreated(group), group);
39446
- })
39447
- },
39448
- {
39449
- name: "group_get",
39450
- description: "Fetch a single task-group summary under the current coordinator. Use to check aggregate progress when waiting on a batch of delegations.",
39222
+ name: "delegate_batch",
39223
+ description: `Delegate several subtasks at once. Pass a "tasks" array; when it holds 2+ tasks they are bound to one auto-created group, so their results are reported back to you together when the whole batch finishes — one handoff instead of one interruption per task. Use this whenever you have multiple parallel delegations. Returns one result per task in input order; a task that fails to start carries an "error" field and does not abort the rest. Legacy-style only: it does not support MCP task execution — use delegate_request for a single native task handle.`,
39451
39224
  inputSchema: exports_external.object({
39452
- groupId: exports_external.string().min(1)
39225
+ title: exports_external.string().min(1).optional(),
39226
+ tasks: exports_external.array(exports_external.object({
39227
+ targetAgent: exports_external.string().min(1),
39228
+ task: exports_external.string().min(1),
39229
+ workingDirectory: exports_external.string().min(1).optional(),
39230
+ role: exports_external.string().min(1).optional()
39231
+ }).strict()).min(1)
39453
39232
  }).strict(),
39454
39233
  handler: async (args) => await asToolResult(async () => {
39455
- const summary = await transport.getGroup({
39234
+ const { title, tasks } = args;
39235
+ const groupId = tasks.length >= 2 ? (await transport.createGroup({
39456
39236
  coordinatorSession,
39457
- groupId: args.groupId
39458
- });
39459
- return createSuccessResult(summary ? renderGroupSummary(summary) : "Group not found.", { group: summary });
39460
- })
39461
- },
39462
- {
39463
- name: "group_list",
39464
- description: "List task groups under the current coordinator. Use to recover groupIds for an earlier batch.",
39465
- inputSchema: exports_external.object({
39466
- status: groupStatusSchema.optional(),
39467
- stuck: exports_external.boolean().optional(),
39468
- sort: sortSchema.optional(),
39469
- order: orderSchema.optional()
39470
- }).strict(),
39471
- handler: async (args) => await asToolResult(async () => {
39472
- const { status, stuck, sort, order } = args;
39473
- const summaries = await transport.listGroups({
39474
- coordinatorSession,
39475
- ...status !== undefined ? { status } : {},
39476
- ...stuck !== undefined ? { stuck } : {},
39477
- ...sort !== undefined ? { sort } : {},
39478
- ...order !== undefined ? { order } : {}
39479
- });
39480
- return createSuccessResult(renderGroupList(summaries), { groups: summaries });
39481
- })
39482
- },
39483
- {
39484
- name: "group_cancel",
39485
- description: "Cancel all unfinished tasks in a task group under the current coordinator. Use to abort a batch started via group_new + delegate_request.",
39486
- inputSchema: exports_external.object({
39487
- groupId: exports_external.string().min(1)
39488
- }).strict(),
39489
- handler: async (args) => await asToolResult(async () => {
39490
- const result = await transport.cancelGroup({
39491
- coordinatorSession,
39492
- groupId: args.groupId
39493
- });
39494
- return createSuccessResult(renderGroupCancelSuccess(result), result);
39237
+ title: title ?? `Batch delegation (${tasks.length} tasks)`
39238
+ })).groupId : undefined;
39239
+ const results = [];
39240
+ for (const [index, entry] of tasks.entries()) {
39241
+ try {
39242
+ const result = await transport.delegateRequest({
39243
+ coordinatorSession,
39244
+ ...sourceHandle ? { sourceHandle } : {},
39245
+ targetAgent: entry.targetAgent,
39246
+ task: entry.task,
39247
+ ...entry.workingDirectory ? { workingDirectory: entry.workingDirectory } : {},
39248
+ ...entry.role ? { role: entry.role } : {},
39249
+ ...groupId ? { groupId } : {}
39250
+ });
39251
+ results.push({ index, taskId: result.taskId, status: result.status });
39252
+ } catch (error2) {
39253
+ results.push({ index, error: formatToolError(error2) });
39254
+ }
39255
+ }
39256
+ return createSuccessResult(renderDelegateBatchSuccess(groupId, results), { ...groupId ? { groupId } : {}, tasks: results });
39495
39257
  })
39496
39258
  },
39497
39259
  {
@@ -39531,7 +39293,7 @@ function buildWeacpxMcpToolRegistry(input) {
39531
39293
  },
39532
39294
  {
39533
39295
  name: "task_approve",
39534
- description: "Approve a pending task under the current coordinator. Use when delegate_request returned status=needs_confirmation and the user has authorized it; after approval, use task_get/task_list for snapshots or task_wait only if intentionally blocking.",
39296
+ description: "Approve a task that delegate_request returned as needs_confirmation, once the user has authorized it. The task then starts running.",
39535
39297
  inputSchema: exports_external.object({
39536
39298
  taskId: exports_external.string().min(1)
39537
39299
  }).strict(),
@@ -39543,23 +39305,9 @@ function buildWeacpxMcpToolRegistry(input) {
39543
39305
  return createSuccessResult(renderTaskApprovalSuccess(task), task);
39544
39306
  })
39545
39307
  },
39546
- {
39547
- name: "task_reject",
39548
- description: "Reject a pending task under the current coordinator. Use when delegate_request returned status=needs_confirmation and the user declined; no task_wait is needed afterwards.",
39549
- inputSchema: exports_external.object({
39550
- taskId: exports_external.string().min(1)
39551
- }).strict(),
39552
- handler: async (args) => await asToolResult(async () => {
39553
- const task = await transport.rejectTask({
39554
- coordinatorSession,
39555
- taskId: args.taskId
39556
- });
39557
- return createSuccessResult(renderTaskRejectionSuccess(task), task);
39558
- })
39559
- },
39560
39308
  {
39561
39309
  name: "task_cancel",
39562
- description: "Request cancellation for a task under the current coordinator. Use to abort a running delegation; the task transitions to a terminal state shortly after.",
39310
+ description: "Cancel a task under the current coordinator. Works in any non-terminal state: a running delegation is aborted, and a task still waiting for approval (needs_confirmation) is rejected. The task transitions to a terminal state shortly after.",
39563
39311
  inputSchema: exports_external.object({
39564
39312
  taskId: exports_external.string().min(1)
39565
39313
  }).strict(),
@@ -39571,25 +39319,9 @@ function buildWeacpxMcpToolRegistry(input) {
39571
39319
  return createSuccessResult(renderTaskCancelRequest(task), task);
39572
39320
  })
39573
39321
  },
39574
- {
39575
- name: "task_wait",
39576
- description: `Wait for a task to finish or require attention. This is a blocking legacy compatibility tool; do not call it automatically after delegate_request when the user only asked to start/delegate work. Use task_get/task_list for non-blocking progress snapshots; call task_wait when the user explicitly asks to wait, or when your next step truly depends on completion. Returns status=terminal (done; call task_get for the result), status=attention_required (call task_get first to read the task's current status, then branch: needs_confirmation -> task_approve or task_reject; blocked or waiting_for_human -> coordinator_answer_question; reviewPending set -> coordinator_review_contested_result; after resolving, use task_get/task_list snapshots or task_wait only if intentionally blocking), or status=timeout (still running; use task_get for a snapshot, or task_wait only if intentionally blocking). Defaults: timeout ${DEFAULT_TASK_WAIT_TIMEOUT_MS} ms, poll interval ${DEFAULT_TASK_WAIT_POLL_INTERVAL_MS} ms. Maximums: timeout ${MAX_TASK_WAIT_TIMEOUT_MS} ms, poll interval ${MAX_TASK_WAIT_POLL_INTERVAL_MS} ms.`,
39577
- inputSchema: exports_external.object({
39578
- taskId: exports_external.string().min(1),
39579
- timeoutMs: exports_external.number().int().min(0).max(MAX_TASK_WAIT_TIMEOUT_MS).optional(),
39580
- pollIntervalMs: exports_external.number().int().min(1).max(MAX_TASK_WAIT_POLL_INTERVAL_MS).optional()
39581
- }).strict(),
39582
- handler: async (args) => await asToolResult(async () => {
39583
- const result = await transport.waitTask({
39584
- coordinatorSession,
39585
- ...args
39586
- });
39587
- return createSuccessResult(renderTaskWaitResult(result), result);
39588
- })
39589
- },
39590
39322
  {
39591
39323
  name: "task_watch",
39592
- description: `Long-poll a task for the next event, attention-required state, or terminal state. This is the recommended legacy way to watch a delegated task without repeatedly calling task_wait. For MCP-task-capable clients, request task execution for this tool to create a background watcher: the call returns a native task handle immediately, and tasks/result returns when the watch condition is met. The native watcher is single-shot: it runs one watch cycle then terminates, so to keep watching start another task_watch with afterSeq set to the returned nextAfterSeq. Defaults: timeout ${DEFAULT_TASK_WATCH_TIMEOUT_MS} ms, poll interval ${DEFAULT_TASK_WATCH_POLL_INTERVAL_MS} ms. Maximums: timeout ${MAX_TASK_WATCH_TIMEOUT_MS} ms, poll interval ${MAX_TASK_WATCH_POLL_INTERVAL_MS} ms.`,
39324
+ description: `Long-poll a task for the next event, attention-required state, or terminal state. For MCP-task-capable clients, request task execution for this tool to create a background watcher: the call returns a native task handle immediately, and tasks/result returns when the watch condition is met. The native watcher is single-shot: it runs one watch cycle then terminates, so to keep watching start another task_watch with afterSeq set to the returned nextAfterSeq. Defaults: timeout ${DEFAULT_TASK_WATCH_TIMEOUT_MS} ms, poll interval ${DEFAULT_TASK_WATCH_POLL_INTERVAL_MS} ms. Maximums: timeout ${MAX_TASK_WATCH_TIMEOUT_MS} ms, poll interval ${MAX_TASK_WATCH_POLL_INTERVAL_MS} ms.`,
39593
39325
  execution: { taskSupport: "optional" },
39594
39326
  inputSchema: exports_external.object({
39595
39327
  taskId: exports_external.string().min(1),
@@ -39609,7 +39341,7 @@ function buildWeacpxMcpToolRegistry(input) {
39609
39341
  },
39610
39342
  {
39611
39343
  name: "worker_raise_question",
39612
- description: "Raise a blocker question for the current bound worker session. Worker-side only: call this from inside a delegated task when you are blocked and need the coordinator's input. Coordinators waiting on a delegation should not call this; use task_get/task_list for snapshots, or task_wait only if intentionally blocking.",
39344
+ description: "Raise a blocker question for the current bound worker session. Worker-side only: call this from inside a delegated task when you are blocked and need the coordinator's input.",
39613
39345
  inputSchema: exports_external.object({
39614
39346
  taskId: exports_external.string().min(1),
39615
39347
  question: exports_external.string().min(1),
@@ -39629,7 +39361,7 @@ function buildWeacpxMcpToolRegistry(input) {
39629
39361
  },
39630
39362
  {
39631
39363
  name: "coordinator_answer_question",
39632
- description: "Answer a blocked worker question under the current coordinator. Use when task_get shows a pending question; after answering, use task_get/task_list for snapshots or task_wait only if intentionally blocking for the worker to finish.",
39364
+ description: "Answer a blocked worker question under the current coordinator. Use when task_get shows a pending question.",
39633
39365
  inputSchema: exports_external.object({
39634
39366
  taskId: exports_external.string().min(1),
39635
39367
  questionId: exports_external.string().min(1),
@@ -39659,23 +39391,6 @@ function buildWeacpxMcpToolRegistry(input) {
39659
39391
  return createSuccessResult(renderCoordinatorRequestHumanInputSuccess(result), result);
39660
39392
  })
39661
39393
  },
39662
- {
39663
- name: "coordinator_follow_up_human_package",
39664
- description: "Append a follow-up message to the active human question package under the current coordinator. Use to clarify or add context to an in-flight package created via coordinator_request_human_input.",
39665
- inputSchema: exports_external.object({
39666
- packageId: exports_external.string().min(1),
39667
- priorMessageId: exports_external.string().min(1),
39668
- taskQuestions: exports_external.array(taskQuestionSchema).min(1),
39669
- promptText: exports_external.string().min(1)
39670
- }).strict(),
39671
- handler: async (args) => await asToolResult(async () => {
39672
- const result = await transport.coordinatorFollowUpHumanPackage({
39673
- coordinatorSession,
39674
- ...args
39675
- });
39676
- return createSuccessResult(renderCoordinatorFollowUpHumanPackageSuccess(result), result);
39677
- })
39678
- },
39679
39394
  {
39680
39395
  name: "coordinator_review_contested_result",
39681
39396
  description: "Review a contested result under the current coordinator. Use when a worker's result has been challenged and the coordinator must decide accept or discard.",
@@ -39697,8 +39412,7 @@ function buildWeacpxMcpToolRegistry(input) {
39697
39412
  ];
39698
39413
  if (isExternalCoordinator) {
39699
39414
  const externalCoordinatorIncompatibleTools = new Set([
39700
- "coordinator_request_human_input",
39701
- "coordinator_follow_up_human_package"
39415
+ "coordinator_request_human_input"
39702
39416
  ]);
39703
39417
  return tools.filter((tool) => !externalCoordinatorIncompatibleTools.has(tool.name));
39704
39418
  }
@@ -39723,37 +39437,6 @@ async function asToolResult(action) {
39723
39437
  return createErrorResult(formatToolError(error2));
39724
39438
  }
39725
39439
  }
39726
- function renderTaskWaitResult(result) {
39727
- if (result.status === "not_found") {
39728
- return "Task not found.";
39729
- }
39730
- if (!result.task) {
39731
- return `Task wait ${result.status.replace("_", " ")}; current state is unavailable.`;
39732
- }
39733
- if (result.status === "timeout") {
39734
- return [
39735
- `Task ${result.task.taskId} wait timed out; current state is ${result.task.status}.`,
39736
- `Next: call task_get for a snapshot, or call task_wait again only if you intentionally want to keep blocking.`
39737
- ].join(`
39738
- `);
39739
- }
39740
- if (result.status === "attention_required") {
39741
- return [
39742
- `Task ${result.task.taskId} requires attention; current state is ${result.task.status}.`,
39743
- `Next: call task_get to read the task's current status and any reviewPending / openQuestion fields, then branch by what you see:`,
39744
- ` - status=needs_confirmation -> task_approve or task_reject`,
39745
- ` - status=blocked or waiting_for_human -> coordinator_answer_question`,
39746
- ` - reviewPending set -> coordinator_review_contested_result`,
39747
- `After resolving, use task_get/task_list for snapshots, or call task_wait again only if you intentionally want to keep blocking.`
39748
- ].join(`
39749
- `);
39750
- }
39751
- return [
39752
- `Task ${result.task.taskId} reached terminal state ${result.task.status}.`,
39753
- `Next: call task_get to read the worker's final result before reporting back to the user.`
39754
- ].join(`
39755
- `);
39756
- }
39757
39440
  function renderTaskWatchResult(result) {
39758
39441
  if (result.status === "not_found" || !result.task) {
39759
39442
  return "Task not found.";
@@ -39787,72 +39470,16 @@ function createErrorResult(message) {
39787
39470
  };
39788
39471
  }
39789
39472
  function renderDelegateSuccess(result) {
39790
- const next = result.status === "needs_confirmation" ? `Next: this delegation requires user approval; do not call task_wait yet. Tell the user, then call task_approve or task_reject based on their response.` : `Next: task "${result.taskId}" is running. Return this taskId to the user, or call task_get/task_list for non-blocking progress snapshots (or task_watch to long-poll for the next event). Call task_wait only if the user explicitly asks you to wait/block until completion.`;
39473
+ const next = result.status === "needs_confirmation" ? `Next: this delegation requires user approval. Tell the user, then call task_approve or task_cancel based on their response.` : `Next: task "${result.taskId}" is running. Return this taskId to the user, call task_get/task_list for non-blocking progress snapshots, or task_watch to long-poll for the next event or terminal state.`;
39791
39474
  return [`Delegation task "${result.taskId}" created.`, `- Status: ${result.status}`, next].join(`
39792
39475
  `);
39793
39476
  }
39794
- function renderGroupCreated(group) {
39795
- return [`Task group "${group.groupId}" created.`, `- Title: ${group.title}`].join(`
39796
- `);
39797
- }
39798
- function renderGroupSummary(summary) {
39799
- const { group, tasks } = summary;
39800
- const lines = [
39801
- `Task group "${group.groupId}"`,
39802
- `- Title: ${group.title}`,
39803
- `- Coordinator session: ${group.coordinatorSession}`,
39804
- `- Total tasks: ${summary.totalTasks}`,
39805
- `- Pending approval: ${summary.pendingApprovalTasks}`,
39806
- `- Running: ${summary.runningTasks}`,
39807
- `- Completed: ${summary.completedTasks}`,
39808
- `- Failed: ${summary.failedTasks}`,
39809
- `- Cancelled: ${summary.cancelledTasks}`,
39810
- `- Terminal: ${summary.terminal ? "yes" : "no"}`
39811
- ];
39812
- if (group.injectionPending !== undefined) {
39813
- lines.push(`- Injection pending: ${group.injectionPending ? "yes" : "no"}`);
39814
- }
39815
- if (group.injectionAppliedAt) {
39816
- lines.push(`- Injection completed at: ${group.injectionAppliedAt}`);
39817
- }
39818
- if (group.lastInjectionError) {
39819
- lines.push(`- Last injection error: ${group.lastInjectionError}`);
39820
- }
39821
- if (tasks.length > 0) {
39822
- lines.push("- Members:");
39823
- for (const task of tasks) {
39824
- lines.push(` - ${task.taskId} [${task.status}] ${task.targetAgent}`);
39825
- }
39826
- }
39827
- return lines.join(`
39828
- `);
39829
- }
39830
- function renderGroupList(groups) {
39831
- if (groups.length === 0) {
39832
- return "There are no task groups under the current coordinator.";
39833
- }
39834
- return ["Task groups for the current coordinator:", ...groups.map((group) => renderGroupListItem(group))].join(`
39835
- `);
39836
- }
39837
- function renderGroupListItem(group) {
39838
- const reliability = group.group.injectionPending ? " | injection pending" : "";
39839
- return [
39840
- `- ${group.group.groupId}`,
39841
- group.group.title,
39842
- `total ${group.totalTasks}`,
39843
- `pending ${group.pendingApprovalTasks}`,
39844
- `running ${group.runningTasks}`,
39845
- `completed ${group.completedTasks}`,
39846
- `failed ${group.failedTasks}`,
39847
- `cancelled ${group.cancelledTasks}${reliability}`
39848
- ].join(" | ");
39849
- }
39850
- function renderGroupCancelSuccess(input) {
39851
- return [
39852
- `Task group "${input.summary.group.groupId}" cancellation requested.`,
39853
- `- Cancel requested: ${input.cancelledTaskIds.length}`,
39854
- `- Skipped terminal tasks: ${input.skippedTaskIds.length}`
39855
- ].join(`
39477
+ function renderDelegateBatchSuccess(groupId, results) {
39478
+ const lines = results.map((entry) => entry.error ? ` - #${entry.index}: failed to start — ${entry.error}` : ` - #${entry.index}: task "${entry.taskId}" (${entry.status})`);
39479
+ const started = results.filter((entry) => entry.taskId).length;
39480
+ const header = groupId ? `Batch delegation created group "${groupId}" with ${started}/${results.length} tasks started.` : `Delegation: ${started}/${results.length} task started.`;
39481
+ const next = started > 0 ? "Next: track the started tasks with task_get/task_list, or task_watch to long-poll. The group reports all results back together once every task is terminal." : "Next: every task failed to start — fix the errors above and retry.";
39482
+ return [header, "- Tasks:", ...lines, next].join(`
39856
39483
  `);
39857
39484
  }
39858
39485
  function renderTaskList(tasks) {
@@ -39923,7 +39550,9 @@ function renderTaskSummary(task) {
39923
39550
  events.push({ at: task.updatedAt, event: "injection failed", detail: task.lastInjectionError });
39924
39551
  events.sort((a, b) => a.at.localeCompare(b.at));
39925
39552
  const timeline = events.length > 0 ? ["- Timeline:", ...events.map((e) => ` - [${e.at}] ${e.event}${e.detail ? `: ${e.detail}` : ""}`)] : [];
39926
- return [...header, ...timeline].join(`
39553
+ const isTerminal2 = task.status === "completed" || task.status === "failed" || task.status === "cancelled";
39554
+ const next = isTerminal2 ? ["Next: summarize this result for the user."] : [];
39555
+ return [...header, ...timeline, ...next].join(`
39927
39556
  `);
39928
39557
  }
39929
39558
  function renderTaskCancelRequest(task) {
@@ -39938,14 +39567,10 @@ function renderTaskApprovalSuccess(task) {
39938
39567
  return [
39939
39568
  `Task "${task.taskId}" approved.`,
39940
39569
  `- Current status: ${task.status}`,
39941
- `Next: use task_get/task_list for non-blocking progress snapshots, or call task_wait only if you intentionally want to block until the worker finishes; then task_get to read the final result.`
39570
+ `Next: use task_get/task_list for non-blocking progress snapshots, or task_watch to long-poll until the worker finishes; then task_get to read the final result.`
39942
39571
  ].join(`
39943
39572
  `);
39944
39573
  }
39945
- function renderTaskRejectionSuccess(task) {
39946
- return [`Task "${task.taskId}" rejected.`, `- Current status: ${task.status}`].join(`
39947
- `);
39948
- }
39949
39574
  function renderWorkerRaiseQuestionSuccess(task) {
39950
39575
  return [`Blocker question submitted for task "${task.taskId}".`, `- questionId: ${task.questionId}`].join(`
39951
39576
  `);
@@ -39959,10 +39584,6 @@ function renderCoordinatorRequestHumanInputSuccess(result) {
39959
39584
  `) : [`Queued the question in the current human question queue.`, `- Queued tasks: ${result.queuedTaskIds.length}`].join(`
39960
39585
  `);
39961
39586
  }
39962
- function renderCoordinatorFollowUpHumanPackageSuccess(result) {
39963
- return [`Appended follow-up to human package "${result.packageId}".`, `- messageId: ${result.messageId}`].join(`
39964
- `);
39965
- }
39966
39587
  function renderCoordinatorReviewContestedResultSuccess(task, decision) {
39967
39588
  const actionText = decision === "accept" ? "Accepted" : "Discarded";
39968
39589
  return [`${actionText} contested result for task "${task.taskId}".`, `- Current status: ${task.status}`].join(`
@@ -39980,7 +39601,7 @@ function formatToolError(error2) {
39980
39601
 
39981
39602
  // src/orchestration/orchestration-client.ts
39982
39603
  init_orchestration_ipc();
39983
- init_task_wait_timeouts();
39604
+ init_task_watch_timeouts();
39984
39605
  import { randomUUID } from "node:crypto";
39985
39606
  import { createConnection } from "node:net";
39986
39607
 
@@ -40005,18 +39626,12 @@ class OrchestrationClient {
40005
39626
  async listTasks(filter) {
40006
39627
  return await this.request("task.list", { filter });
40007
39628
  }
40008
- async waitTask(input) {
40009
- return await this.request("task.wait", input, getWaitRequestTimeoutMs(input.timeoutMs, this.timeoutMs));
40010
- }
40011
39629
  async watchTask(input) {
40012
39630
  return await this.request("task.watch", input, getWatchRequestTimeoutMs(input.timeoutMs, this.timeoutMs));
40013
39631
  }
40014
39632
  async approveTask(input) {
40015
39633
  return await this.request("task.approve", input);
40016
39634
  }
40017
- async rejectTask(input) {
40018
- return await this.request("task.reject", input);
40019
- }
40020
39635
  async cancelTask(input) {
40021
39636
  return await this.request("task.cancel", input);
40022
39637
  }
@@ -40038,24 +39653,12 @@ class OrchestrationClient {
40038
39653
  async coordinatorRequestHumanInput(input) {
40039
39654
  return await this.request("coordinator.request_human_input", input);
40040
39655
  }
40041
- async coordinatorFollowUpHumanPackage(input) {
40042
- return await this.request("coordinator.follow_up_human_package", input);
40043
- }
40044
39656
  async coordinatorReviewContestedResult(input) {
40045
39657
  return await this.request("coordinator.review_contested_result", input);
40046
39658
  }
40047
39659
  async createGroup(input) {
40048
39660
  return await this.request("group.new", input);
40049
39661
  }
40050
- async getGroup(input) {
40051
- return await this.request("group.get", input);
40052
- }
40053
- async listGroups(input) {
40054
- return await this.request("group.list", input);
40055
- }
40056
- async cancelGroup(input) {
40057
- return await this.request("group.cancel", input);
40058
- }
40059
39662
  async request(method, params, timeoutMs = this.timeoutMs) {
40060
39663
  const id = this.createId();
40061
39664
  return await new Promise((resolve, reject) => {
@@ -40122,11 +39725,6 @@ class OrchestrationClient {
40122
39725
  });
40123
39726
  }
40124
39727
  }
40125
- function getWaitRequestTimeoutMs(waitTimeoutMs, defaultTimeoutMs) {
40126
- const requestedWaitTimeoutMs = waitTimeoutMs === undefined ? undefined : Number.isFinite(waitTimeoutMs) ? waitTimeoutMs : 0;
40127
- const boundedWaitTimeoutMs = Math.min(Math.max(Math.floor(requestedWaitTimeoutMs ?? DEFAULT_TASK_WAIT_TIMEOUT_MS), 0), MAX_TASK_WAIT_TIMEOUT_MS);
40128
- return Math.max(defaultTimeoutMs, boundedWaitTimeoutMs + TASK_WAIT_RPC_TIMEOUT_PADDING_MS);
40129
- }
40130
39728
  function getWatchRequestTimeoutMs(watchTimeoutMs, defaultTimeoutMs) {
40131
39729
  const requestedWatchTimeoutMs = watchTimeoutMs === undefined ? undefined : Number.isFinite(watchTimeoutMs) ? watchTimeoutMs : 0;
40132
39730
  const boundedWatchTimeoutMs = Math.min(Math.max(Math.floor(requestedWatchTimeoutMs ?? DEFAULT_TASK_WATCH_TIMEOUT_MS), 0), MAX_TASK_WATCH_TIMEOUT_MS);
@@ -40146,9 +39744,6 @@ function createOrchestrationTransport(endpoint, deps = {}) {
40146
39744
  ...input.groupId !== undefined ? { groupId: input.groupId } : {}
40147
39745
  }),
40148
39746
  createGroup: async (input) => await client.createGroup(input),
40149
- getGroup: async (input) => await client.getGroup(input),
40150
- listGroups: async (input) => await client.listGroups(input),
40151
- cancelGroup: async (input) => await client.cancelGroup(input),
40152
39747
  getTask: async (input) => await client.getTaskForCoordinator(input),
40153
39748
  listTasks: async (input) => await client.listTasks({
40154
39749
  coordinatorSession: input.coordinatorSession,
@@ -40158,9 +39753,7 @@ function createOrchestrationTransport(endpoint, deps = {}) {
40158
39753
  ...input.order !== undefined ? { order: input.order } : {}
40159
39754
  }),
40160
39755
  approveTask: async (input) => await client.approveTask(input),
40161
- rejectTask: async (input) => await client.rejectTask(input),
40162
39756
  cancelTask: async (input) => await client.cancelTaskForCoordinator(input),
40163
- waitTask: async (input) => await client.waitTask(input),
40164
39757
  watchTask: async (input) => await client.watchTask(input),
40165
39758
  workerRaiseQuestion: async (input) => {
40166
39759
  const sourceHandle = input.sourceHandle.trim();
@@ -40177,7 +39770,6 @@ function createOrchestrationTransport(endpoint, deps = {}) {
40177
39770
  },
40178
39771
  coordinatorAnswerQuestion: async (input) => await client.coordinatorAnswerQuestion(input),
40179
39772
  coordinatorRequestHumanInput: async (input) => await client.coordinatorRequestHumanInput(input),
40180
- coordinatorFollowUpHumanPackage: async (input) => await client.coordinatorFollowUpHumanPackage(input),
40181
39773
  coordinatorReviewContestedResult: async (input) => await client.coordinatorReviewContestedResult(input)
40182
39774
  };
40183
39775
  }
@@ -40189,32 +39781,12 @@ var WATCH_TASKS_CACHE_LIMIT = 256;
40189
39781
  var WEACPX_MCP_SERVER_INSTRUCTIONS = [
40190
39782
  "Use these tools to orchestrate work across other agents under your coordinator session.",
40191
39783
  "",
40192
- "Typical lifecycle for a single delegation:",
40193
- "Preferred MCP Tasks lifecycle (for clients that support task-augmented tools/call):",
40194
- "1. Call delegate_request with task execution requested. It returns a native MCP task handle immediately.",
40195
- "2. Use task_watch with MCP task execution to start a background watcher, or use tasks/get / tasks/list to poll status. Use tasks/result after terminal status, or on input_required to receive an actionable next-step package; use tasks/cancel to cancel.",
40196
- " - When tasks/result returns input_required, that result stream is complete. Call the recommended tool, then resume polling with tasks/get / tasks/result.",
40197
- "3. Status mapping: working = running, input_required = needs_confirmation / blocked / waiting_for_human / contested review, completed / failed / cancelled are terminal.",
40198
- "",
40199
- "Legacy tool lifecycle for clients without MCP Tasks support:",
40200
- "1. delegate_request → returns { taskId, status }.",
40201
- " - status=running: the worker has started. Return the taskId to the user, use task_get / task_list for non-blocking snapshots, or task_watch to long-poll for the next event; only go to step 2 when you intentionally want to block waiting.",
40202
- " - status=needs_confirmation: tell the user, then call task_approve or task_reject based on their response. After task_approve, use task_get/task_list for snapshots or step 2 only if intentionally blocking. Do not call task_wait before approval.",
40203
- "2. Optional blocking wait: task_wait(taskId) → blocks until the task is done, needs attention, or times out. Do not call it automatically when the user asked to delegate and continue.",
40204
- " - status=terminal: go to step 3.",
40205
- " - status=attention_required: the task is in needs_confirmation / blocked / waiting_for_human, or has reviewPending set. Call task_get(taskId) to read the actual status and any openQuestion / reviewPending fields, then branch:",
40206
- " * needs_confirmation -> task_approve or task_reject (after approval, use snapshots or optional blocking wait only if needed)",
40207
- " * blocked or waiting_for_human -> coordinator_answer_question (the answer can come from you or be relayed from a human you consulted)",
40208
- " * reviewPending set -> coordinator_review_contested_result with accept or discard",
40209
- " After resolving, use task_get / task_list for snapshots, or step 2 only if intentionally blocking.",
40210
- " - status=timeout: the task is still running. Use task_get for a snapshot, or call task_wait again only if you still intentionally want to block.",
40211
- "3. The task is terminal. Call task_get(taskId) to read the worker's final result, then summarize it for the user. Do not invent results that did not come from task_get.",
39784
+ "Delegate with delegate_request (one task) or delegate_batch (several at once). Each returns a taskId and a status.",
39785
+ "Then follow the task: clients that support MCP Tasks should request task execution on delegate_request / task_watch and poll with tasks/get / tasks/list / tasks/result; other clients use task_get / task_list for snapshots or task_watch to long-poll.",
40212
39786
  "",
40213
- "Batching: use group_new before a wave of delegate_request calls and pass groupId on each, then group_get / group_list / group_cancel to manage the batch.",
40214
- "Cancellation: task_cancel aborts a single running task; group_cancel aborts the whole batch.",
40215
- "Discovery: task_list / group_list recover taskIds and groupIds from earlier in the session.",
39787
+ "Most tool results end with a 'Next:' line telling you the concrete next step follow it when present. In short: status=needs_confirmation needs task_approve or task_cancel; a task that needs attention (blocked / waiting_for_human / a contested review) is resolved with coordinator_answer_question or coordinator_review_contested_result; a terminal task is read with task_get. Never report a result you did not read from task_get.",
40216
39788
  "",
40217
- "worker_raise_question is worker-side only — call it from inside a delegated task when you are blocked, not from the coordinator that is waiting on a delegation."
39789
+ "worker_raise_question is worker-side only — call it from inside a delegated task when you are blocked, not from the coordinator waiting on a delegation."
40218
39790
  ].join(`
40219
39791
  `);
40220
39792
  function createWeacpxMcpServer(options) {
@@ -40627,7 +40199,7 @@ function renderInputRequiredTaskResult(task) {
40627
40199
  function inputRequiredActions(task) {
40628
40200
  const actions = [];
40629
40201
  if (task.status === "needs_confirmation") {
40630
- actions.push("task_approve", "task_reject");
40202
+ actions.push("task_approve", "task_cancel");
40631
40203
  }
40632
40204
  if (task.status === "blocked" || task.status === "waiting_for_human" || task.openQuestion) {
40633
40205
  actions.push("coordinator_answer_question");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "weacpx",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "description": "使用微信 ClawBot 随时随地通过 `acpx` 控制 Claude Code、Codex 等 Agents。",
5
5
  "keywords": [
6
6
  "acpx",