@humanclaw/humanclaw 1.1.5 → 1.2.0
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 +48 -41
- package/README_EN.md +47 -40
- package/dist/index.js +124 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
**碳基节点编排框架 —— 将人类抽象为分布式 Worker 节点**
|
|
6
6
|
|
|
7
|
-
[](https://github.com/humanclaw/humanclaw/actions/workflows/ci.yml)
|
|
8
7
|
[](https://www.npmjs.com/package/@humanclaw/humanclaw)
|
|
9
8
|
[](https://opensource.org/licenses/MIT)
|
|
10
9
|
|
|
@@ -18,7 +17,9 @@
|
|
|
18
17
|
|
|
19
18
|
## 概述
|
|
20
19
|
|
|
21
|
-
HumanClaw 是一个碳基节点编排框架。系统将真实人类抽象为 Agent(碳基节点),将现实中的任务派发与结果收集抽象为进程的**挂起(Suspend)**与**恢复(Resume
|
|
20
|
+
HumanClaw 是一个碳基节点编排框架。系统将真实人类抽象为 Agent(碳基节点),将现实中的任务派发与结果收集抽象为进程的**挂起(Suspend)**与**恢复(Resume)**。
|
|
21
|
+
|
|
22
|
+
核心流程:输入自然语言需求 → 选人 → AI 自动规划(拆任务 + 生成话术 + 设 DDL)→ 确认分发 → 收交付物 → AI 聚合审查。
|
|
22
23
|
|
|
23
24
|
## 核心架构
|
|
24
25
|
|
|
@@ -30,17 +31,17 @@ HumanClaw 是一个碳基节点编排框架。系统将真实人类抽象为 Age
|
|
|
30
31
|
│ │ Resume + Result │ │
|
|
31
32
|
└─────┬───────┘ └──────────────┘
|
|
32
33
|
│
|
|
33
|
-
│
|
|
34
|
+
│ AI Review
|
|
34
35
|
▼
|
|
35
36
|
┌─────────────┐
|
|
36
|
-
│
|
|
37
|
-
│
|
|
37
|
+
│ LLM 审查 │
|
|
38
|
+
│ (Claude/GPT)│
|
|
38
39
|
└─────────────┘
|
|
39
40
|
```
|
|
40
41
|
|
|
41
|
-
- **Master
|
|
42
|
+
- **Master 节点**:输入需求,AI 自动拆解为独立子任务,分发给碳基节点
|
|
42
43
|
- **Worker 节点 (HumanAgent)**:接收带 `trace_id` 的独立任务,在碳基世界异步执行
|
|
43
|
-
-
|
|
44
|
+
- **AI 审查**:所有任务完成后,LLM 自动审查交付质量并生成报告
|
|
44
45
|
|
|
45
46
|
## 快速开始
|
|
46
47
|
|
|
@@ -65,16 +66,17 @@ humanclaw agent add
|
|
|
65
66
|
# 交互式录入:节点名称、技能标签
|
|
66
67
|
```
|
|
67
68
|
|
|
68
|
-
###
|
|
69
|
+
### AI 规划任务
|
|
69
70
|
|
|
70
71
|
```bash
|
|
71
|
-
humanclaw
|
|
72
|
+
humanclaw plan "完成首页重构,包括导航栏和页脚的响应式改版"
|
|
73
|
+
# AI 自动拆解任务、匹配碳基节点、生成话术和 DDL
|
|
72
74
|
```
|
|
73
75
|
|
|
74
|
-
###
|
|
76
|
+
### 查看算力池
|
|
75
77
|
|
|
76
78
|
```bash
|
|
77
|
-
humanclaw
|
|
79
|
+
humanclaw agent list
|
|
78
80
|
```
|
|
79
81
|
|
|
80
82
|
## API 接口
|
|
@@ -84,28 +86,21 @@ humanclaw status
|
|
|
84
86
|
| `GET` | `/api/v1/nodes/status` | 碳基算力池状态 |
|
|
85
87
|
| `POST` | `/api/v1/nodes` | 注册碳基节点 |
|
|
86
88
|
| `PATCH` | `/api/v1/nodes/:id/status` | 更新节点状态 |
|
|
89
|
+
| `POST` | `/api/v1/jobs/plan` | AI 智能规划(不分发) |
|
|
87
90
|
| `POST` | `/api/v1/jobs/create` | 创建并分发任务 |
|
|
88
91
|
| `GET` | `/api/v1/jobs/active` | 获取看板数据 |
|
|
89
92
|
| `POST` | `/api/v1/tasks/resume` | 提交交付物,触发恢复 |
|
|
90
93
|
| `POST` | `/api/v1/tasks/reject` | 打回重做 |
|
|
91
|
-
| `POST` | `/api/v1/jobs/:id/
|
|
94
|
+
| `POST` | `/api/v1/jobs/:id/review` | AI 聚合审查交付质量 |
|
|
95
|
+
| `GET` | `/api/v1/config` | 获取 LLM 配置 |
|
|
96
|
+
| `PUT` | `/api/v1/config` | 更新 LLM 配置 |
|
|
92
97
|
|
|
93
|
-
###
|
|
98
|
+
### AI 规划示例
|
|
94
99
|
|
|
95
100
|
```bash
|
|
96
|
-
curl -X POST http://localhost:2026/api/v1/jobs/
|
|
101
|
+
curl -X POST http://localhost:2026/api/v1/jobs/plan \
|
|
97
102
|
-H "Content-Type: application/json" \
|
|
98
|
-
-d '{
|
|
99
|
-
"original_prompt": "完成首页重构",
|
|
100
|
-
"openclaw_callback": "",
|
|
101
|
-
"tasks": [
|
|
102
|
-
{
|
|
103
|
-
"assignee_id": "emp_xxxxxxxx",
|
|
104
|
-
"todo_description": "实现响应式导航栏",
|
|
105
|
-
"deadline": "2026-03-28T18:00:00Z"
|
|
106
|
-
}
|
|
107
|
-
]
|
|
108
|
-
}'
|
|
103
|
+
-d '{ "prompt": "完成首页重构" }'
|
|
109
104
|
```
|
|
110
105
|
|
|
111
106
|
### 提交交付物
|
|
@@ -115,7 +110,7 @@ curl -X POST http://localhost:2026/api/v1/tasks/resume \
|
|
|
115
110
|
-H "Content-Type: application/json" \
|
|
116
111
|
-d '{
|
|
117
112
|
"trace_id": "TK-9527",
|
|
118
|
-
"result_data": { "text": "
|
|
113
|
+
"result_data": { "text": "https://github.com/org/repo/pull/42" }
|
|
119
114
|
}'
|
|
120
115
|
```
|
|
121
116
|
|
|
@@ -123,17 +118,34 @@ curl -X POST http://localhost:2026/api/v1/tasks/resume \
|
|
|
123
118
|
|
|
124
119
|
Web 看板包含三个核心视图:
|
|
125
120
|
|
|
126
|
-
-
|
|
127
|
-
- **碳基编排大盘** — 任务 Kanban
|
|
121
|
+
- **碳基算力池** — 实时查看碳基节点状态(🟢空闲 🟡忙碌 🔴离线 🟣崩溃),一键添加/删除节点
|
|
122
|
+
- **碳基编排大盘** — AI 智能规划 + 任务 Kanban + 可交互任务卡片(点击直接提交交付/打回)+ AI 聚合审查
|
|
128
123
|
- **I/O 交付终端** — 输入 trace_id 和交付载荷,触发系统恢复
|
|
129
124
|
|
|
125
|
+
### AI 功能
|
|
126
|
+
|
|
127
|
+
- **智能规划** — 输入需求,AI 自动拆任务、匹配碳基节点、生成布置话术、设 DDL(可调)
|
|
128
|
+
- **聚合审查** — 全部交付后,AI 审查每个交付物质量(支持 GitHub PR/Commit/Issue URL),生成评分报告
|
|
129
|
+
- **可配置 LLM** — 支持 Claude / OpenAI,可自定义 Base URL 接入私有模型服务(vLLM / Ollama / Azure)
|
|
130
|
+
|
|
130
131
|
## 核心工作流
|
|
131
132
|
|
|
132
|
-
1. **镜像封装** —
|
|
133
|
-
2.
|
|
134
|
-
3.
|
|
135
|
-
4. **异步恢复** —
|
|
136
|
-
5.
|
|
133
|
+
1. **镜像封装** — 录入碳基成员信息,构建碳基算力池
|
|
134
|
+
2. **AI 规划** — 输入需求,AI 拆解任务、匹配节点、生成话术和 DDL
|
|
135
|
+
3. **确认分发** — 预览规划结果,调整 DDL,确认后一键分发
|
|
136
|
+
4. **异步恢复** — 碳基节点提交交付物(支持 GitHub URL),系统唤醒 Job
|
|
137
|
+
5. **AI 审查** — 所有子任务完成后,LLM 审查交付质量并生成报告
|
|
138
|
+
|
|
139
|
+
## 环境变量
|
|
140
|
+
|
|
141
|
+
| 变量 | 默认值 | 说明 |
|
|
142
|
+
|------|--------|------|
|
|
143
|
+
| `HUMANCLAW_LLM_PROVIDER` | `claude` | LLM 提供商:`claude` 或 `openai` |
|
|
144
|
+
| `HUMANCLAW_LLM_API_KEY` | - | LLM API Key(使用 AI 功能时必填) |
|
|
145
|
+
| `HUMANCLAW_LLM_MODEL` | 按 provider | 可选覆盖模型名 |
|
|
146
|
+
| `HUMANCLAW_LLM_BASE_URL` | 官方地址 | 自定义 API 地址(私有部署) |
|
|
147
|
+
|
|
148
|
+
> Dashboard 设置面板也可以配置以上参数,优先级高于环境变量。
|
|
137
149
|
|
|
138
150
|
## 数据模型
|
|
139
151
|
|
|
@@ -154,12 +166,6 @@ interface HumanTask {
|
|
|
154
166
|
status: TaskStatus; // PENDING | DISPATCHED | RESOLVED | OVERDUE
|
|
155
167
|
result_data: unknown;
|
|
156
168
|
}
|
|
157
|
-
|
|
158
|
-
interface OrchestrationJob {
|
|
159
|
-
job_id: string;
|
|
160
|
-
original_prompt: string;
|
|
161
|
-
openclaw_callback: string;
|
|
162
|
-
}
|
|
163
169
|
```
|
|
164
170
|
|
|
165
171
|
## 开发
|
|
@@ -178,9 +184,10 @@ npm run lint # 类型检查
|
|
|
178
184
|
- **Runtime**: Node.js 22+, TypeScript (ESM, strict)
|
|
179
185
|
- **API**: Express v5
|
|
180
186
|
- **Storage**: SQLite (better-sqlite3, WAL mode)
|
|
187
|
+
- **LLM**: Claude / OpenAI(原生 fetch,零依赖)
|
|
181
188
|
- **CLI**: Commander.js + @clack/prompts
|
|
182
|
-
- **Dashboard**:
|
|
183
|
-
- **Testing**: Vitest (
|
|
189
|
+
- **Dashboard**: 内联 HTML(无需构建)
|
|
190
|
+
- **Testing**: Vitest (40 tests)
|
|
184
191
|
|
|
185
192
|
## License
|
|
186
193
|
|
package/README_EN.md
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
**Carbon-Based Node Orchestration Framework — Humans as Distributed Worker Nodes**
|
|
6
6
|
|
|
7
|
-
[](https://github.com/humanclaw/humanclaw/actions/workflows/ci.yml)
|
|
8
7
|
[](https://www.npmjs.com/package/@humanclaw/humanclaw)
|
|
9
8
|
[](https://opensource.org/licenses/MIT)
|
|
10
9
|
|
|
@@ -18,7 +17,9 @@
|
|
|
18
17
|
|
|
19
18
|
## Overview
|
|
20
19
|
|
|
21
|
-
HumanClaw is a carbon-based node orchestration framework. The system abstracts real humans as Agents (carbon-based nodes), models task dispatch and result collection as process **Suspend** and **Resume
|
|
20
|
+
HumanClaw is a carbon-based node orchestration framework. The system abstracts real humans as Agents (carbon-based nodes), models task dispatch and result collection as process **Suspend** and **Resume**.
|
|
21
|
+
|
|
22
|
+
Core flow: natural language input → select people → AI auto-plans (breaks down tasks + generates briefings + sets deadlines) → confirm dispatch → collect deliverables → AI aggregated review.
|
|
22
23
|
|
|
23
24
|
## Core Architecture
|
|
24
25
|
|
|
@@ -30,17 +31,17 @@ HumanClaw is a carbon-based node orchestration framework. The system abstracts r
|
|
|
30
31
|
│ │ Resume + Result │ │
|
|
31
32
|
└─────┬───────┘ └──────────────┘
|
|
32
33
|
│
|
|
33
|
-
│
|
|
34
|
+
│ AI Review
|
|
34
35
|
▼
|
|
35
36
|
┌─────────────┐
|
|
36
|
-
│
|
|
37
|
-
│ (
|
|
37
|
+
│ LLM Review │
|
|
38
|
+
│ (Claude/GPT)│
|
|
38
39
|
└─────────────┘
|
|
39
40
|
```
|
|
40
41
|
|
|
41
|
-
- **Master Node**:
|
|
42
|
+
- **Master Node**: Input requirements, AI auto-breaks them into independent sub-tasks, dispatches to carbon-based nodes
|
|
42
43
|
- **Worker Node (HumanAgent)**: Receives independent tasks with a `trace_id`, executes asynchronously in the carbon-based world
|
|
43
|
-
- **
|
|
44
|
+
- **AI Review**: After all tasks complete, LLM reviews deliverable quality and generates a report
|
|
44
45
|
|
|
45
46
|
## Quick Start
|
|
46
47
|
|
|
@@ -65,16 +66,17 @@ humanclaw agent add
|
|
|
65
66
|
# Interactive prompts for: node name, capability tags
|
|
66
67
|
```
|
|
67
68
|
|
|
68
|
-
###
|
|
69
|
+
### AI Task Planning
|
|
69
70
|
|
|
70
71
|
```bash
|
|
71
|
-
humanclaw
|
|
72
|
+
humanclaw plan "Rebuild the homepage with responsive navbar and footer"
|
|
73
|
+
# AI auto-breaks tasks, matches nodes, generates briefings and deadlines
|
|
72
74
|
```
|
|
73
75
|
|
|
74
|
-
###
|
|
76
|
+
### View Compute Pool
|
|
75
77
|
|
|
76
78
|
```bash
|
|
77
|
-
humanclaw
|
|
79
|
+
humanclaw agent list
|
|
78
80
|
```
|
|
79
81
|
|
|
80
82
|
## API Endpoints
|
|
@@ -84,28 +86,21 @@ humanclaw status
|
|
|
84
86
|
| `GET` | `/api/v1/nodes/status` | Carbon compute pool status |
|
|
85
87
|
| `POST` | `/api/v1/nodes` | Register carbon-based node |
|
|
86
88
|
| `PATCH` | `/api/v1/nodes/:id/status` | Update node status |
|
|
89
|
+
| `POST` | `/api/v1/jobs/plan` | AI task planning (does not dispatch) |
|
|
87
90
|
| `POST` | `/api/v1/jobs/create` | Create and dispatch job |
|
|
88
91
|
| `GET` | `/api/v1/jobs/active` | Get kanban data |
|
|
89
92
|
| `POST` | `/api/v1/tasks/resume` | Submit deliverable, trigger resume |
|
|
90
93
|
| `POST` | `/api/v1/tasks/reject` | Reject and retry |
|
|
91
|
-
| `POST` | `/api/v1/jobs/:id/
|
|
94
|
+
| `POST` | `/api/v1/jobs/:id/review` | AI aggregated review of deliverables |
|
|
95
|
+
| `GET` | `/api/v1/config` | Get LLM configuration |
|
|
96
|
+
| `PUT` | `/api/v1/config` | Update LLM configuration |
|
|
92
97
|
|
|
93
|
-
###
|
|
98
|
+
### AI Planning Example
|
|
94
99
|
|
|
95
100
|
```bash
|
|
96
|
-
curl -X POST http://localhost:2026/api/v1/jobs/
|
|
101
|
+
curl -X POST http://localhost:2026/api/v1/jobs/plan \
|
|
97
102
|
-H "Content-Type: application/json" \
|
|
98
|
-
-d '{
|
|
99
|
-
"original_prompt": "Rebuild the homepage",
|
|
100
|
-
"openclaw_callback": "",
|
|
101
|
-
"tasks": [
|
|
102
|
-
{
|
|
103
|
-
"assignee_id": "emp_xxxxxxxx",
|
|
104
|
-
"todo_description": "Implement responsive navbar",
|
|
105
|
-
"deadline": "2026-03-28T18:00:00Z"
|
|
106
|
-
}
|
|
107
|
-
]
|
|
108
|
-
}'
|
|
103
|
+
-d '{ "prompt": "Rebuild the homepage" }'
|
|
109
104
|
```
|
|
110
105
|
|
|
111
106
|
### Submit Deliverable
|
|
@@ -115,7 +110,7 @@ curl -X POST http://localhost:2026/api/v1/tasks/resume \
|
|
|
115
110
|
-H "Content-Type: application/json" \
|
|
116
111
|
-d '{
|
|
117
112
|
"trace_id": "TK-9527",
|
|
118
|
-
"result_data": { "text": "
|
|
113
|
+
"result_data": { "text": "https://github.com/org/repo/pull/42" }
|
|
119
114
|
}'
|
|
120
115
|
```
|
|
121
116
|
|
|
@@ -123,17 +118,34 @@ curl -X POST http://localhost:2026/api/v1/tasks/resume \
|
|
|
123
118
|
|
|
124
119
|
The web dashboard includes three core views:
|
|
125
120
|
|
|
126
|
-
- **Carbon Compute Pool
|
|
127
|
-
- **Carbon Orchestration Pipeline** — Task Kanban (
|
|
121
|
+
- **Carbon Compute Pool** — Real-time carbon-based node status (🟢Idle 🟡Busy 🔴Offline 🟣OOM), add/remove nodes
|
|
122
|
+
- **Carbon Orchestration Pipeline** — AI planning + Task Kanban + interactive task cards (click to submit/reject) + AI review
|
|
128
123
|
- **I/O Resolution Terminal** — Input trace_id and payload to trigger system resume
|
|
129
124
|
|
|
125
|
+
### AI Features
|
|
126
|
+
|
|
127
|
+
- **Smart Planning** — Input requirements, AI auto-breaks tasks, matches nodes, generates briefings, sets adjustable deadlines
|
|
128
|
+
- **Aggregated Review** — After all deliveries, AI reviews each deliverable (supports GitHub PR/Commit/Issue URLs), generates quality report
|
|
129
|
+
- **Configurable LLM** — Supports Claude / OpenAI, custom Base URL for private deployments (vLLM / Ollama / Azure)
|
|
130
|
+
|
|
130
131
|
## Core Workflow
|
|
131
132
|
|
|
132
133
|
1. **Agent Encapsulation** — Register human members, build the carbon compute pool
|
|
133
|
-
2. **
|
|
134
|
-
3. **
|
|
135
|
-
4. **Async Resume** — Carbon-based nodes submit deliverables, system wakes up the Job
|
|
136
|
-
5. **
|
|
134
|
+
2. **AI Planning** — Input requirements, AI breaks tasks, matches nodes, generates briefings and deadlines
|
|
135
|
+
3. **Confirm Dispatch** — Preview plan, adjust deadlines, one-click dispatch
|
|
136
|
+
4. **Async Resume** — Carbon-based nodes submit deliverables (supports GitHub URLs), system wakes up the Job
|
|
137
|
+
5. **AI Review** — When all sub-tasks complete, LLM reviews deliverable quality and generates a report
|
|
138
|
+
|
|
139
|
+
## Environment Variables
|
|
140
|
+
|
|
141
|
+
| Variable | Default | Description |
|
|
142
|
+
|----------|---------|-------------|
|
|
143
|
+
| `HUMANCLAW_LLM_PROVIDER` | `claude` | LLM provider: `claude` or `openai` |
|
|
144
|
+
| `HUMANCLAW_LLM_API_KEY` | - | LLM API Key (required for AI features) |
|
|
145
|
+
| `HUMANCLAW_LLM_MODEL` | per provider | Optional model override |
|
|
146
|
+
| `HUMANCLAW_LLM_BASE_URL` | official | Custom API URL (private deployments) |
|
|
147
|
+
|
|
148
|
+
> Dashboard settings panel can also configure these, with higher priority than env vars.
|
|
137
149
|
|
|
138
150
|
## Data Models
|
|
139
151
|
|
|
@@ -154,12 +166,6 @@ interface HumanTask {
|
|
|
154
166
|
status: TaskStatus; // PENDING | DISPATCHED | RESOLVED | OVERDUE
|
|
155
167
|
result_data: unknown;
|
|
156
168
|
}
|
|
157
|
-
|
|
158
|
-
interface OrchestrationJob {
|
|
159
|
-
job_id: string;
|
|
160
|
-
original_prompt: string;
|
|
161
|
-
openclaw_callback: string;
|
|
162
|
-
}
|
|
163
169
|
```
|
|
164
170
|
|
|
165
171
|
## Development
|
|
@@ -178,9 +184,10 @@ npm run lint # Type check
|
|
|
178
184
|
- **Runtime**: Node.js 22+, TypeScript (ESM, strict)
|
|
179
185
|
- **API**: Express v5
|
|
180
186
|
- **Storage**: SQLite (better-sqlite3, WAL mode)
|
|
187
|
+
- **LLM**: Claude / OpenAI (native fetch, zero dependencies)
|
|
181
188
|
- **CLI**: Commander.js + @clack/prompts
|
|
182
|
-
- **Dashboard**:
|
|
183
|
-
- **Testing**: Vitest (
|
|
189
|
+
- **Dashboard**: Inline HTML (no build step)
|
|
190
|
+
- **Testing**: Vitest (40 tests)
|
|
184
191
|
|
|
185
192
|
## License
|
|
186
193
|
|
package/dist/index.js
CHANGED
|
@@ -35,12 +35,13 @@ function getDb(dbPath) {
|
|
|
35
35
|
function initSchema(db2) {
|
|
36
36
|
db2.exec(`
|
|
37
37
|
CREATE TABLE IF NOT EXISTS agents (
|
|
38
|
-
agent_id
|
|
39
|
-
name
|
|
38
|
+
agent_id TEXT PRIMARY KEY,
|
|
39
|
+
name TEXT NOT NULL,
|
|
40
40
|
capabilities TEXT NOT NULL DEFAULT '[]',
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
relationship TEXT NOT NULL DEFAULT '',
|
|
42
|
+
status TEXT NOT NULL DEFAULT 'IDLE'
|
|
43
|
+
CHECK (status IN ('IDLE', 'BUSY', 'OFFLINE', 'OOM')),
|
|
44
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
44
45
|
);
|
|
45
46
|
|
|
46
47
|
CREATE TABLE IF NOT EXISTS jobs (
|
|
@@ -73,6 +74,10 @@ function initSchema(db2) {
|
|
|
73
74
|
value TEXT NOT NULL
|
|
74
75
|
);
|
|
75
76
|
`);
|
|
77
|
+
const cols = db2.prepare("PRAGMA table_info(agents)").all();
|
|
78
|
+
if (!cols.some((c) => c.name === "relationship")) {
|
|
79
|
+
db2.exec(`ALTER TABLE agents ADD COLUMN relationship TEXT NOT NULL DEFAULT ''`);
|
|
80
|
+
}
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
// src/routes/nodes.ts
|
|
@@ -89,12 +94,13 @@ function createAgent(agent, db2) {
|
|
|
89
94
|
const conn = db2 ?? getDb();
|
|
90
95
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
91
96
|
conn.prepare(
|
|
92
|
-
`INSERT INTO agents (agent_id, name, capabilities, status, created_at)
|
|
93
|
-
VALUES (?, ?, ?, ?, ?)`
|
|
97
|
+
`INSERT INTO agents (agent_id, name, capabilities, relationship, status, created_at)
|
|
98
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
94
99
|
).run(
|
|
95
100
|
agent.agent_id,
|
|
96
101
|
agent.name,
|
|
97
102
|
JSON.stringify(agent.capabilities),
|
|
103
|
+
agent.relationship || "",
|
|
98
104
|
agent.status,
|
|
99
105
|
now
|
|
100
106
|
);
|
|
@@ -167,7 +173,7 @@ router.get("/status", (_req, res) => {
|
|
|
167
173
|
});
|
|
168
174
|
});
|
|
169
175
|
router.post("/", (req, res) => {
|
|
170
|
-
const { name, capabilities, status } = req.body;
|
|
176
|
+
const { name, capabilities, relationship, status } = req.body;
|
|
171
177
|
if (!name || !Array.isArray(capabilities)) {
|
|
172
178
|
res.status(400).json({ error: "name and capabilities[] are required" });
|
|
173
179
|
return;
|
|
@@ -176,6 +182,7 @@ router.post("/", (req, res) => {
|
|
|
176
182
|
agent_id: generateId("emp"),
|
|
177
183
|
name,
|
|
178
184
|
capabilities,
|
|
185
|
+
relationship: relationship || "",
|
|
179
186
|
status: status ?? "IDLE"
|
|
180
187
|
});
|
|
181
188
|
res.status(201).json(agent);
|
|
@@ -536,7 +543,8 @@ function buildUserPrompt(prompt, agents) {
|
|
|
536
543
|
const agentList = agents.map((a) => {
|
|
537
544
|
const load = a.active_task_count > 0 ? `\u5F53\u524D\u6709 ${a.active_task_count} \u4E2A\u8FDB\u884C\u4E2D\u7684\u4EFB\u52A1` : "\u5F53\u524D\u7A7A\u95F2";
|
|
538
545
|
const speed = a.avg_delivery_hours !== null ? `\u5E73\u5747\u4EA4\u4ED8\u65F6\u95F4 ${a.avg_delivery_hours}h` : "\u6682\u65E0\u5386\u53F2\u6570\u636E";
|
|
539
|
-
|
|
546
|
+
const rel = a.relationship ? ` \u5173\u7CFB: ${a.relationship}` : "";
|
|
547
|
+
return `- ${a.name} (ID: ${a.agent_id}) \u6280\u80FD: [${a.capabilities.join(", ")}]${rel} ${load} ${speed}`;
|
|
540
548
|
}).join("\n");
|
|
541
549
|
return `\u5F53\u524D\u65F6\u95F4: ${now.toISOString()}
|
|
542
550
|
|
|
@@ -738,6 +746,61 @@ function rejectTask(traceId, newDeadline, db2) {
|
|
|
738
746
|
return getTask(traceId, conn);
|
|
739
747
|
}
|
|
740
748
|
|
|
749
|
+
// src/services/simulator.ts
|
|
750
|
+
function buildSimulatePrompt(agentName, relationship, capabilities, taskDescription, deadline) {
|
|
751
|
+
return `\u4F60\u73B0\u5728\u626E\u6F14\u4E00\u4E2A\u540D\u53EB\u300C${agentName}\u300D\u7684\u4EBA\uFF0C\u4F60\u7684\u6280\u80FD\u6807\u7B7E\u662F [${capabilities.join(", ")}]\u3002
|
|
752
|
+
${relationship ? `\u4F60\u548C\u5E03\u7F6E\u4EFB\u52A1\u7684\u4EBA\u7684\u5173\u7CFB\u662F\uFF1A${relationship}\u3002` : ""}
|
|
753
|
+
|
|
754
|
+
\u4F60\u6536\u5230\u4E86\u4E00\u4E2A\u4EFB\u52A1\uFF1A
|
|
755
|
+
${taskDescription}
|
|
756
|
+
|
|
757
|
+
\u622A\u6B62\u65F6\u95F4\uFF1A${new Date(deadline).toLocaleString("zh-CN")}
|
|
758
|
+
|
|
759
|
+
\u8BF7\u7AD9\u5728\u300C${agentName}\u300D\u7684\u89C6\u89D2\uFF0C\u7528\u8FD9\u4E2A\u4EBA\u7269\u81EA\u7136\u7684\u8BED\u6C14\u548C\u53E3\u543B\uFF0C\u5199\u4E00\u6BB5\u4EFB\u52A1\u4EA4\u4ED8\u6C47\u62A5\u3002
|
|
760
|
+
\u8981\u6C42\uFF1A
|
|
761
|
+
1. \u5185\u5BB9\u8981\u8D34\u5408\u4EFB\u52A1\u63CF\u8FF0\uFF0C\u4F53\u73B0\u4E13\u4E1A\u80FD\u529B
|
|
762
|
+
2. \u8BED\u6C14\u7B26\u5408\u4EBA\u7269\u8EAB\u4EFD${relationship ? "\u548C\u4E0E\u4E0A\u7EA7\u7684\u5173\u7CFB" : ""}
|
|
763
|
+
3. \u6C47\u62A5\u5185\u5BB9\u5177\u4F53\u3001\u6709\u7EC6\u8282\uFF0C\u5305\u542B\u505A\u4E86\u4EC0\u4E48\u3001\u9047\u5230\u4E86\u4EC0\u4E48\u95EE\u9898\u3001\u6700\u7EC8\u7ED3\u679C\u5982\u4F55
|
|
764
|
+
4. \u5B57\u6570 200-400 \u5B57
|
|
765
|
+
5. \u76F4\u63A5\u8F93\u51FA\u6C47\u62A5\u5185\u5BB9\uFF0C\u4E0D\u8981\u52A0\u4EFB\u4F55\u683C\u5F0F\u524D\u7F00\u6216\u8BF4\u660E`;
|
|
766
|
+
}
|
|
767
|
+
async function simulateDelivery(traceId, provider, db2) {
|
|
768
|
+
const conn = db2 ?? getDb();
|
|
769
|
+
const task = getTask(traceId, conn);
|
|
770
|
+
if (!task) {
|
|
771
|
+
throw new Error(`Task not found: ${traceId}`);
|
|
772
|
+
}
|
|
773
|
+
const agent = getAgent(task.assignee_id, conn);
|
|
774
|
+
if (!agent) {
|
|
775
|
+
throw new Error(`Agent not found: ${task.assignee_id}`);
|
|
776
|
+
}
|
|
777
|
+
const llm = provider ?? createLlmProvider();
|
|
778
|
+
const response = await llm.complete({
|
|
779
|
+
messages: [
|
|
780
|
+
{
|
|
781
|
+
role: "system",
|
|
782
|
+
content: "\u4F60\u662F\u4E00\u4E2A\u89D2\u8272\u626E\u6F14\u4E13\u5BB6\uFF0C\u64C5\u957F\u6A21\u62DF\u4E0D\u540C\u4EBA\u7269\u7684\u8BED\u6C14\u548C\u6C47\u62A5\u98CE\u683C\u3002"
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
role: "user",
|
|
786
|
+
content: buildSimulatePrompt(
|
|
787
|
+
agent.name,
|
|
788
|
+
agent.relationship,
|
|
789
|
+
agent.capabilities,
|
|
790
|
+
task.todo_description,
|
|
791
|
+
task.deadline
|
|
792
|
+
)
|
|
793
|
+
}
|
|
794
|
+
],
|
|
795
|
+
temperature: 0.7,
|
|
796
|
+
max_tokens: 1024
|
|
797
|
+
});
|
|
798
|
+
return {
|
|
799
|
+
trace_id: traceId,
|
|
800
|
+
simulated_delivery: response.content
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
|
|
741
804
|
// src/routes/tasks.ts
|
|
742
805
|
var router3 = Router3();
|
|
743
806
|
router3.post("/resume", (req, res) => {
|
|
@@ -776,6 +839,21 @@ router3.post("/reject", (req, res) => {
|
|
|
776
839
|
res.status(400).json({ error: message });
|
|
777
840
|
}
|
|
778
841
|
});
|
|
842
|
+
router3.post("/simulate", async (req, res) => {
|
|
843
|
+
const { trace_id } = req.body;
|
|
844
|
+
if (!trace_id) {
|
|
845
|
+
res.status(400).json({ error: "trace_id is required" });
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
try {
|
|
849
|
+
const result = await simulateDelivery(trace_id);
|
|
850
|
+
res.json(result);
|
|
851
|
+
} catch (error) {
|
|
852
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
853
|
+
const status = message.includes("API Key") || message.includes("API key") ? 503 : 400;
|
|
854
|
+
res.status(status).json({ error: message });
|
|
855
|
+
}
|
|
856
|
+
});
|
|
779
857
|
var tasks_default = router3;
|
|
780
858
|
|
|
781
859
|
// src/routes/sync.ts
|
|
@@ -803,11 +881,13 @@ function buildReviewUserPrompt(originalPrompt, tasks) {
|
|
|
803
881
|
const t = tasks[i];
|
|
804
882
|
let resultText = "";
|
|
805
883
|
if (t.result_data) {
|
|
806
|
-
|
|
807
|
-
const rd = JSON.parse(t.result_data);
|
|
808
|
-
resultText = rd.text || JSON.stringify(rd, null, 2);
|
|
809
|
-
} catch {
|
|
884
|
+
if (typeof t.result_data === "string") {
|
|
810
885
|
resultText = t.result_data;
|
|
886
|
+
} else if (typeof t.result_data === "object") {
|
|
887
|
+
const rd = t.result_data;
|
|
888
|
+
resultText = rd.text || JSON.stringify(rd, null, 2);
|
|
889
|
+
} else {
|
|
890
|
+
resultText = String(t.result_data);
|
|
811
891
|
}
|
|
812
892
|
}
|
|
813
893
|
prompt += `--- \u5B50\u4EFB\u52A1 ${i + 1} ---
|
|
@@ -1131,6 +1211,7 @@ async function loadFleet(el){
|
|
|
1131
1211
|
for(const a of d.agents){
|
|
1132
1212
|
h+='<div class="card"><div class="agent-header"><span class="dot '+a.status+'"></span><span class="agent-name">'+esc(a.name)+'</span></div>';
|
|
1133
1213
|
h+='<div class="agent-id">'+a.agent_id+'</div>';
|
|
1214
|
+
if(a.relationship)h+='<div style="font-size:11px;color:var(--purple);margin-bottom:4px">👥 '+esc(a.relationship)+'</div>';
|
|
1134
1215
|
h+='<div class="caps">';for(const c of a.capabilities)h+='<span class="cap">'+esc(c)+'</span>';h+='</div>';
|
|
1135
1216
|
h+='<div class="agent-meta"><span>\u4EFB\u52A1: '+a.active_task_count+'</span>';
|
|
1136
1217
|
if(a.avg_delivery_hours!==null)h+='<span>\u5E73\u5747\u4EA4\u4ED8: '+a.avg_delivery_hours+'h</span>';
|
|
@@ -1152,6 +1233,7 @@ window.showAddAgent=function(){
|
|
|
1152
1233
|
ov.innerHTML='<div class="form-card"><h3>+ \u6DFB\u52A0\u78B3\u57FA\u8282\u70B9</h3>'
|
|
1153
1234
|
+'<div class="fg"><label>\u8282\u70B9\u540D\u79F0</label><input id="aa-name" placeholder="\u4F8B: \u524D\u7AEF\u8001\u674E"/></div>'
|
|
1154
1235
|
+'<div class="fg"><label>\u6280\u80FD\u6807\u7B7E</label><input id="aa-caps" placeholder="\u4F8B: UI/UX, \u524D\u7AEF\u5F00\u53D1, \u6297\u538B\u80FD\u529B\u5F3A"/><div class="hint">\u591A\u4E2A\u6807\u7B7E\u7528\u9017\u53F7\u5206\u9694</div></div>'
|
|
1236
|
+
+'<div class="fg"><label>\u4E0E\u4F60\u7684\u5173\u7CFB <span style="color:var(--text-dim);font-weight:400">(\u53EF\u9009)</span></label><input id="aa-rel" placeholder="\u4F8B: \u76F4\u5C5E\u4E0B\u5C5E / \u5B9E\u4E60\u751F / \u5916\u5305\u540C\u4E8B / \u4E49\u5F1F"/><div class="hint">\u63CF\u8FF0\u6B64\u4EBA\u4E0E\u4F60\u7684\u5173\u7CFB\uFF0CAI \u89C4\u5212\u548C\u6A21\u62DF\u4EA4\u4ED8\u65F6\u4F1A\u53C2\u8003</div></div>'
|
|
1155
1237
|
+'<div class="btn-group"><button class="btn btn-primary" onclick="submitAgent()">\u6CE8\u518C\u8282\u70B9</button><button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u53D6\u6D88</button></div></div>';
|
|
1156
1238
|
document.body.appendChild(ov);
|
|
1157
1239
|
document.getElementById('aa-name').focus();
|
|
@@ -1159,10 +1241,11 @@ window.showAddAgent=function(){
|
|
|
1159
1241
|
window.submitAgent=async function(){
|
|
1160
1242
|
const name=document.getElementById('aa-name').value.trim();
|
|
1161
1243
|
const caps=document.getElementById('aa-caps').value.trim();
|
|
1244
|
+
const rel=document.getElementById('aa-rel').value.trim();
|
|
1162
1245
|
if(!name){toast('\u8BF7\u8F93\u5165\u8282\u70B9\u540D\u79F0',false);return}
|
|
1163
1246
|
if(!caps){toast('\u8BF7\u8F93\u5165\u81F3\u5C11\u4E00\u4E2A\u6280\u80FD\u6807\u7B7E',false);return}
|
|
1164
1247
|
try{
|
|
1165
|
-
const r=await fetch(API+'/nodes',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name,capabilities:caps.split(',').map(s=>s.trim()).filter(Boolean)})});
|
|
1248
|
+
const r=await fetch(API+'/nodes',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name,capabilities:caps.split(',').map(s=>s.trim()).filter(Boolean),relationship:rel})});
|
|
1166
1249
|
const d=await r.json();
|
|
1167
1250
|
if(!r.ok){toast(d.error||'\u6CE8\u518C\u5931\u8D25',false);return}
|
|
1168
1251
|
toast('\u8282\u70B9 '+d.agent_id+' \u6CE8\u518C\u6210\u529F\uFF01',true);
|
|
@@ -1439,13 +1522,14 @@ window.showTaskDetail=function(traceId){
|
|
|
1439
1522
|
if(t.status==='RESOLVED'&&t.result_data){
|
|
1440
1523
|
// Show result
|
|
1441
1524
|
let resultText='';
|
|
1442
|
-
|
|
1525
|
+
if(typeof t.result_data==='object'&&t.result_data){resultText=t.result_data.text||JSON.stringify(t.result_data,null,2)}else{resultText=String(t.result_data||'')}
|
|
1443
1526
|
h+='<div class="detail-row"><div class="detail-label">\u4EA4\u4ED8\u7ED3\u679C</div><div class="result-display">'+esc(resultText)+'</div></div>';
|
|
1444
1527
|
h+='<div class="btn-group"><button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u5173\u95ED</button></div>';
|
|
1445
1528
|
}else{
|
|
1446
1529
|
// Input for resume/reject
|
|
1447
1530
|
h+='<div class="fg" style="margin-top:16px"><label>\u63D0\u4EA4 Human \u4EA4\u4ED8\u7269</label><textarea id="td-payload" rows="4" placeholder="\u7C98\u8D34\u4EA4\u4ED8\u7269\u5185\u5BB9\u3001GitHub PR/Commit URL\u3001\u5DE5\u4F5C\u6C47\u62A5\u7B49..."></textarea><div class="hint">\u652F\u6301\u8D34 GitHub URL\uFF08PR\u3001Commit\u3001Issue\uFF09\uFF0CAI \u5BA1\u67E5\u65F6\u4F1A\u5206\u6790</div></div>';
|
|
1448
1531
|
h+='<div class="btn-group">';
|
|
1532
|
+
h+='<button class="btn btn-primary btn-sm" onclick="simulateDelivery(\\''+t.trace_id+'\\')">🤖 \u6A21\u62DF\u4EA4\u4ED8</button>';
|
|
1449
1533
|
h+='<button class="btn btn-green" onclick="taskResume(\\''+t.trace_id+'\\')">\u63D0\u4EA4\u4EA4\u4ED8 (Resume)</button>';
|
|
1450
1534
|
h+='<button class="btn btn-danger" onclick="taskReject(\\''+t.trace_id+'\\')">\u6253\u56DE\u91CD\u505A (Reject)</button>';
|
|
1451
1535
|
h+='<button class="btn btn-ghost" onclick="document.getElementById(\\'overlay\\').remove()">\u53D6\u6D88</button>';
|
|
@@ -1481,6 +1565,21 @@ window.taskReject=async function(traceId){
|
|
|
1481
1565
|
}catch{toast('\u7F51\u7EDC\u9519\u8BEF',false)}
|
|
1482
1566
|
};
|
|
1483
1567
|
|
|
1568
|
+
window.simulateDelivery=async function(traceId){
|
|
1569
|
+
const btn=event.target;
|
|
1570
|
+
const origText=btn.innerHTML;
|
|
1571
|
+
btn.disabled=true;btn.innerHTML='⌛ AI \u751F\u6210\u4E2D...';
|
|
1572
|
+
try{
|
|
1573
|
+
const r=await fetch(API+'/tasks/simulate',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({trace_id:traceId})});
|
|
1574
|
+
const d=await r.json();
|
|
1575
|
+
if(!r.ok){toast(d.error||'\u6A21\u62DF\u5931\u8D25',false);btn.disabled=false;btn.innerHTML=origText;return}
|
|
1576
|
+
const ta=document.getElementById('td-payload');
|
|
1577
|
+
if(ta)ta.value=d.simulated_delivery;
|
|
1578
|
+
toast('\u5DF2\u751F\u6210\u6A21\u62DF\u4EA4\u4ED8\u5185\u5BB9',true);
|
|
1579
|
+
btn.disabled=false;btn.innerHTML=origText;
|
|
1580
|
+
}catch(e){toast('\u7F51\u7EDC\u9519\u8BEF: '+e.message,false);btn.disabled=false;btn.innerHTML=origText}
|
|
1581
|
+
};
|
|
1582
|
+
|
|
1484
1583
|
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
1485
1584
|
// TERMINAL
|
|
1486
1585
|
// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
@@ -1660,10 +1759,20 @@ agentCmd.command("add").description("Register a new carbon-based node").action(a
|
|
|
1660
1759
|
process.exit(0);
|
|
1661
1760
|
}
|
|
1662
1761
|
const capabilities = capInput.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1762
|
+
const relInput = await p.text({
|
|
1763
|
+
message: "Relationship with you (optional):",
|
|
1764
|
+
placeholder: "e.g. direct report / intern / contractor",
|
|
1765
|
+
defaultValue: ""
|
|
1766
|
+
});
|
|
1767
|
+
if (p.isCancel(relInput)) {
|
|
1768
|
+
p.cancel("Cancelled.");
|
|
1769
|
+
process.exit(0);
|
|
1770
|
+
}
|
|
1663
1771
|
const agent = createAgent({
|
|
1664
1772
|
agent_id: generateId("emp"),
|
|
1665
1773
|
name,
|
|
1666
1774
|
capabilities,
|
|
1775
|
+
relationship: relInput || "",
|
|
1667
1776
|
status: "IDLE"
|
|
1668
1777
|
});
|
|
1669
1778
|
p.outro(
|