team-anya 0.2.3 → 0.2.4

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.
@@ -253,7 +253,11 @@
253
253
  | project.mode | string | `project` / `adhoc` |
254
254
  | project.repos | array | 仓库列表(project 模式,单仓库也是数组) |
255
255
 
256
- **约束**: Brief 只写做什么和标准,不写怎么做。派工三步走:`task.dispatch`(MCP) → bash 准备工作区 → `yor.spawn`(MCP),三步缺一不可。
256
+ **约束**: Brief 只写做什么和标准,不写怎么做。
257
+
258
+ **派工一步到位**:dispatch 成功后自动启动 Yor,无需再手动调用 `yor.spawn`。
259
+ 返回值中 `instance_id` 为 Yor 实例 ID,`spawn_error`(如有)表示自动启动失败。
260
+ dispatch 返回 status="blocked" 时不会启动 Yor,通知人类即可。
257
261
 
258
262
  ### task.lookup
259
263
 
@@ -103,31 +103,32 @@ memory.search 没有结果?30 秒内组装调研 Brief → 派工 Yor → 回
103
103
 
104
104
  ## 派工流程
105
105
 
106
+ **一步派工**:调用 `task.dispatch(...)` 即可,Yor 会自动启动。
107
+
106
108
  ```
107
- 1. task.dispatch(MCP) 创建任务 + 写 brief + 自动准备隔离工作区 → 返回 working_dir
108
- 2. yor.spawn(MCP) → 启动 Yor,传入 working_dir 和 brief_path
109
+ result = task.dispatch({ title, brief, project_id, ... })
110
+ // Yor 已自动启动,result.instance_id Yor 实例 ID
111
+ channel.send({ message: "已派工,稍等" })
109
112
  ```
110
113
 
111
- ### Step 1: 创建任务 + 准备工作区
114
+ ### 返回值
112
115
 
113
- 调用 `task.dispatch(...)`,返回:
114
116
  - `task_id`:任务 ID
115
117
  - `brief_path`:brief 文件绝对路径
116
118
  - `working_dir`:隔离工作区绝对路径
117
119
  - `status`:`dispatched`(成功)或 `blocked`(工作区准备失败)
118
- - `error`:失败原因(仅 status=blocked)
120
+ - `instance_id`:Yor 实例 ID(成功时)
121
+ - `spawn_error`:Yor 启动失败原因(任务已创建但 Yor 未启动,可手动调 `yor.spawn` 重试)
122
+ - `error`:工作区准备失败原因(仅 status=blocked)
119
123
 
120
124
  工作区自动准备:
121
125
  - **project 模式**:自动 clone --local 到 `{workspaceDir}/{repo_name}`
122
126
  - **adhoc 模式**:自动 mkdir 到 `{taskDir}/adhoc`
123
127
 
124
- ### Step 2: 启动 Yor
125
-
126
- 调用 `yor.spawn(...)`,**必须原样传入 dispatch 返回的 `working_dir` 和 `brief_path`**。
127
-
128
- > **⚠️ 严禁修改 working_dir!** 不要传 repo 子目录(如 `WS-xxx/test-repo`),必须传工作区根目录(`WS-xxx/`)。工作区根目录包含 Yor 的角色文件(CLAUDE.md、PROFILE.md 等),Yor 启动后会自动在子目录中操作代码。
128
+ ### 异常处理
129
129
 
130
- dispatch 返回 status=blocked 时,不执行 yor.spawn——通知人类工作区准备失败的原因。
130
+ - `status=blocked` 通知人类工作区准备失败的原因
131
+ - `spawn_error` → 任务已创建(READY),可手动调用 `yor.spawn({ task_id, working_dir, brief_path })` 重试
131
132
 
132
133
  ## 审核闭环
133
134
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "team-anya",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "type": "module",
5
5
  "description": "Team Anya - AI 数字员工系统",
6
6
  "bin": {
@@ -460,8 +460,10 @@ Brief 只写做什么和标准,不写怎么做。
460
460
  同时从当前对话上下文中提取 created_by(sender_name 或 sender_id)和 source_chat_id(chat_id),确保任务可追溯。
461
461
 
462
462
  返回值包含 task_id、brief_path 和 working_dir(已准备好的隔离工作区绝对路径)。
463
- 派工两步走:task.dispatch(MCP) → yor.spawn(MCP,传入返回的 working_dir 和 brief_path)。
464
- 如果返回 status="blocked",说明工作区准备失败,error 字段包含原因,此时不要调用 yor.spawn,应通知人类。`,
463
+
464
+ 派工成功(status="dispatched")后会自动启动 Yor,无需再调用 yor.spawn。返回值中 instance_id 为 Yor 实例 ID。
465
+ 如果返回 status="blocked",说明工作区准备失败,error 字段包含原因,应通知人类。
466
+ 如果返回 spawn_error,说明 Yor 启动失败但任务已创建(READY),可手动调用 yor.spawn 重试。`,
465
467
  inputSchema: TaskDispatchInputSchema,
466
468
  layer: 2,
467
469
  roles: ['loid'],
@@ -795,13 +797,33 @@ export function createToolRouter(role, deps) {
795
797
  if (!deps.workspaceManager)
796
798
  throw new Error('workspaceManager 未注入');
797
799
  const { taskDispatch } = await import('./layer2/loid/task-dispatch.js');
798
- return taskDispatch({
800
+ const dispatchResult = await taskDispatch({
799
801
  db: deps.db,
800
802
  workspacePath: deps.workspacePath,
801
803
  logger: deps.logger,
802
804
  getProjectConfig: deps.getProjectConfig,
803
805
  workspaceManager: deps.workspaceManager,
804
806
  }, args);
807
+ // 派工成功 → 自动 spawn Yor(固定流程,不需要 LLM 判断)
808
+ if (dispatchResult.status === 'dispatched' && dispatchResult.working_dir) {
809
+ if (!deps.yorOrchestrator) {
810
+ deps.logger?.error('[anya:pipeline] [dispatch] auto-spawn 跳过: yorOrchestrator 未注入');
811
+ }
812
+ else {
813
+ try {
814
+ const { yorSpawn } = await import('./layer2/loid/yor-spawn.js');
815
+ const spawnResult = await yorSpawn({ yorOrchestrator: deps.yorOrchestrator, logger: deps.logger }, { task_id: dispatchResult.task_id, working_dir: dispatchResult.working_dir, brief_path: dispatchResult.brief_path });
816
+ dispatchResult.instance_id = spawnResult.instance_id;
817
+ deps.logger?.info(`[anya:pipeline] [dispatch] auto-spawn 完成: ${spawnResult.instance_id}`);
818
+ }
819
+ catch (err) {
820
+ const msg = err instanceof Error ? err.message : String(err);
821
+ deps.logger?.error(`[anya:pipeline] [dispatch] auto-spawn 失败: ${msg}`);
822
+ dispatchResult.spawn_error = msg;
823
+ }
824
+ }
825
+ }
826
+ return dispatchResult;
805
827
  }
806
828
  case 'task.lookup': {
807
829
  const { taskLookup } = await import('./layer2/loid/task-lookup.js');