@geekbeer/minion 3.17.0 → 3.23.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/core/db.js +24 -1
- package/core/lib/dag-step-poller.js +22 -13
- package/core/lib/llm-checker.js +4 -7
- package/core/lib/template-expander.js +21 -18
- package/core/llm-dispatch/mcp-server.js +185 -0
- package/core/llm-dispatch/session-pool.js +97 -0
- package/core/llm-plugins/claude/index.js +151 -0
- package/core/llm-plugins/claude/stream.js +166 -0
- package/core/llm-plugins/codex/index.js +161 -0
- package/core/llm-plugins/gemini/index.js +104 -0
- package/core/llm-plugins/lib/active.js +23 -0
- package/core/llm-plugins/lib/mcp-registration.js +132 -0
- package/core/llm-plugins/lib/skill-dirs.js +67 -0
- package/core/llm-plugins/lib/spawn-helper.js +88 -0
- package/core/llm-plugins/registry.js +168 -0
- package/core/llm-plugins/types.js +85 -0
- package/core/routes/llm.js +89 -0
- package/core/routes/skills.js +112 -57
- package/core/routes/todos.js +4 -3
- package/core/stores/todo-store.js +65 -5
- package/docs/api-reference.md +470 -0
- package/docs/task-guides.md +220 -0
- package/linux/bin/hq +168 -15
- package/linux/minion-cli.sh +14 -0
- package/linux/routes/chat.js +149 -2
- package/linux/routine-runner.js +22 -7
- package/linux/server.js +2 -0
- package/linux/workflow-runner.js +26 -8
- package/package.json +1 -1
- package/rules/core.md +25 -7
- package/win/bin/hq.ps1 +155 -27
- package/win/routes/chat.js +142 -2
- package/win/routine-runner.js +20 -6
- package/win/server.js +2 -0
- package/win/workflow-runner.js +20 -7
package/core/routes/todos.js
CHANGED
|
@@ -40,12 +40,13 @@ async function todoRoutes(fastify) {
|
|
|
40
40
|
return { success: false, error: 'Unauthorized' }
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const { status, priority, project_id, source_type, limit } = request.query || {}
|
|
43
|
+
const { status, priority, project_id, source_type, session_id, limit } = request.query || {}
|
|
44
44
|
const todos = todoStore.list({
|
|
45
45
|
status,
|
|
46
46
|
priority,
|
|
47
47
|
project_id,
|
|
48
48
|
source_type,
|
|
49
|
+
session_id,
|
|
49
50
|
limit: limit ? parseInt(limit, 10) : undefined,
|
|
50
51
|
})
|
|
51
52
|
|
|
@@ -75,14 +76,14 @@ async function todoRoutes(fastify) {
|
|
|
75
76
|
return { success: false, error: 'Unauthorized' }
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
const { title, description, priority, source_type, source_id, project_id, due_at, data } = request.body || {}
|
|
79
|
+
const { title, description, priority, source_type, source_id, project_id, due_at, data, session_id } = request.body || {}
|
|
79
80
|
if (!title) {
|
|
80
81
|
reply.code(400)
|
|
81
82
|
return { success: false, error: 'title is required' }
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
try {
|
|
85
|
-
const todo = todoStore.add({ title, description, priority, source_type, source_id, project_id, due_at, data })
|
|
86
|
+
const todo = todoStore.add({ title, description, priority, source_type, source_id, project_id, due_at, data, session_id })
|
|
86
87
|
console.log(`[Todos] Created: ${todo.id} "${todo.title}"`)
|
|
87
88
|
reply.code(201)
|
|
88
89
|
return { success: true, todo }
|
|
@@ -19,6 +19,11 @@ const VALID_SOURCE_TYPES = ['thread', 'workflow', 'directive', 'user', 'self']
|
|
|
19
19
|
// Auto-prune completed todos older than this
|
|
20
20
|
const PRUNE_DAYS = 30
|
|
21
21
|
|
|
22
|
+
// Max number of times an incomplete todo gets auto-injected into the chat
|
|
23
|
+
// prompt before we give up and stop injecting it (to avoid infinite loops
|
|
24
|
+
// when Claude cannot finish a task).
|
|
25
|
+
const MAX_INJECTION_COUNT = 5
|
|
26
|
+
|
|
22
27
|
/**
|
|
23
28
|
* Create a new TODO.
|
|
24
29
|
* @param {object} todo - { title, description?, priority?, source_type?, source_id?, project_id?, due_at?, data? }
|
|
@@ -50,12 +55,14 @@ function add(todo) {
|
|
|
50
55
|
created_at: now,
|
|
51
56
|
updated_at: now,
|
|
52
57
|
completed_at: null,
|
|
58
|
+
session_id: todo.session_id || null,
|
|
59
|
+
injection_count: 0,
|
|
53
60
|
...(todo.data ? { data: todo.data } : {}),
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
db.prepare(`
|
|
57
|
-
INSERT INTO todos (id, title, description, status, priority, source_type, source_id, project_id, due_at, created_at, updated_at, completed_at, data)
|
|
58
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
64
|
+
INSERT INTO todos (id, title, description, status, priority, source_type, source_id, project_id, due_at, created_at, updated_at, completed_at, data, session_id, injection_count)
|
|
65
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
59
66
|
`).run(
|
|
60
67
|
record.id,
|
|
61
68
|
record.title,
|
|
@@ -69,7 +76,9 @@ function add(todo) {
|
|
|
69
76
|
record.created_at,
|
|
70
77
|
record.updated_at,
|
|
71
78
|
record.completed_at,
|
|
72
|
-
record.data ? JSON.stringify(record.data) : null
|
|
79
|
+
record.data ? JSON.stringify(record.data) : null,
|
|
80
|
+
record.session_id,
|
|
81
|
+
record.injection_count
|
|
73
82
|
)
|
|
74
83
|
|
|
75
84
|
console.log(`[TodoStore] Added: "${record.title}" (${record.priority}, source=${record.source_type || 'none'})`)
|
|
@@ -115,7 +124,8 @@ function update(id, updates) {
|
|
|
115
124
|
db.prepare(`
|
|
116
125
|
UPDATE todos SET title = ?, description = ?, status = ?, priority = ?,
|
|
117
126
|
source_type = ?, source_id = ?, project_id = ?, due_at = ?,
|
|
118
|
-
updated_at = ?, completed_at = ?, data =
|
|
127
|
+
updated_at = ?, completed_at = ?, data = ?,
|
|
128
|
+
session_id = ?, injection_count = ?
|
|
119
129
|
WHERE id = ?
|
|
120
130
|
`).run(
|
|
121
131
|
merged.title,
|
|
@@ -129,6 +139,8 @@ function update(id, updates) {
|
|
|
129
139
|
merged.updated_at,
|
|
130
140
|
merged.completed_at,
|
|
131
141
|
merged.data ? JSON.stringify(merged.data) : null,
|
|
142
|
+
merged.session_id ?? null,
|
|
143
|
+
merged.injection_count ?? 0,
|
|
132
144
|
id
|
|
133
145
|
)
|
|
134
146
|
|
|
@@ -160,9 +172,50 @@ function getById(id) {
|
|
|
160
172
|
return row ? parseRow(row) : null
|
|
161
173
|
}
|
|
162
174
|
|
|
175
|
+
/**
|
|
176
|
+
* List incomplete (pending/in_progress) todos linked to a chat session that
|
|
177
|
+
* still have remaining injection budget. Used by the chat route to remind
|
|
178
|
+
* Claude of unfinished work even after context compaction.
|
|
179
|
+
* @param {string} sessionId
|
|
180
|
+
* @returns {Array}
|
|
181
|
+
*/
|
|
182
|
+
function listActiveForSession(sessionId) {
|
|
183
|
+
if (!sessionId) return []
|
|
184
|
+
const db = getDb()
|
|
185
|
+
const rows = db.prepare(`
|
|
186
|
+
SELECT * FROM todos
|
|
187
|
+
WHERE session_id = ?
|
|
188
|
+
AND status IN ('pending', 'in_progress')
|
|
189
|
+
AND injection_count < ?
|
|
190
|
+
ORDER BY
|
|
191
|
+
CASE priority WHEN 'urgent' THEN 0 WHEN 'high' THEN 1 WHEN 'normal' THEN 2 WHEN 'low' THEN 3 END,
|
|
192
|
+
created_at ASC
|
|
193
|
+
`).all(sessionId, MAX_INJECTION_COUNT)
|
|
194
|
+
return rows.map(parseRow)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Increment injection_count for the given todo IDs. Should be called once
|
|
199
|
+
* the reminder has been actually written into the outgoing prompt.
|
|
200
|
+
* @param {string[]} ids
|
|
201
|
+
*/
|
|
202
|
+
function markInjected(ids) {
|
|
203
|
+
if (!Array.isArray(ids) || ids.length === 0) return
|
|
204
|
+
const db = getDb()
|
|
205
|
+
const now = new Date().toISOString()
|
|
206
|
+
const stmt = db.prepare(`
|
|
207
|
+
UPDATE todos
|
|
208
|
+
SET injection_count = injection_count + 1, updated_at = ?
|
|
209
|
+
WHERE id = ?
|
|
210
|
+
`)
|
|
211
|
+
for (const id of ids) {
|
|
212
|
+
stmt.run(now, id)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
163
216
|
/**
|
|
164
217
|
* List TODOs with optional filters.
|
|
165
|
-
* @param {object} opts - { status?, priority?, project_id?, source_type?, limit? }
|
|
218
|
+
* @param {object} opts - { status?, priority?, project_id?, source_type?, session_id?, limit? }
|
|
166
219
|
* @returns {Array}
|
|
167
220
|
*/
|
|
168
221
|
function list(opts = {}) {
|
|
@@ -186,6 +239,10 @@ function list(opts = {}) {
|
|
|
186
239
|
conditions.push('source_type = ?')
|
|
187
240
|
params.push(opts.source_type)
|
|
188
241
|
}
|
|
242
|
+
if (opts.session_id) {
|
|
243
|
+
conditions.push('session_id = ?')
|
|
244
|
+
params.push(opts.session_id)
|
|
245
|
+
}
|
|
189
246
|
|
|
190
247
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''
|
|
191
248
|
const limit = opts.limit || 100
|
|
@@ -260,6 +317,9 @@ module.exports = {
|
|
|
260
317
|
remove,
|
|
261
318
|
getById,
|
|
262
319
|
list,
|
|
320
|
+
listActiveForSession,
|
|
321
|
+
markInjected,
|
|
263
322
|
getSummary,
|
|
264
323
|
pruneCompleted,
|
|
324
|
+
MAX_INJECTION_COUNT,
|
|
265
325
|
}
|
package/docs/api-reference.md
CHANGED
|
@@ -229,6 +229,38 @@ curl -X PUT /api/config/env \
|
|
|
229
229
|
The scheduler starts automatically on server boot if `REFLECTION_TIME` is configured.
|
|
230
230
|
Changes via the config API take effect immediately (no restart required).
|
|
231
231
|
|
|
232
|
+
### Todos
|
|
233
|
+
|
|
234
|
+
ミニオンローカルのToDoリスト。SQLiteに永続化され、HQにも同期される。
|
|
235
|
+
|
|
236
|
+
| Method | Endpoint | Description |
|
|
237
|
+
|--------|----------|-------------|
|
|
238
|
+
| GET | `/api/todos` | List todos. Query: `status`, `priority`, `project_id`, `source_type`, `session_id`, `limit` |
|
|
239
|
+
| GET | `/api/todos/summary` | Status counts |
|
|
240
|
+
| GET | `/api/todos/:id` | Get single todo |
|
|
241
|
+
| POST | `/api/todos` | Create. Body: `{title, description?, priority?, source_type?, source_id?, project_id?, due_at?, session_id?, data?}` |
|
|
242
|
+
| PUT | `/api/todos/:id` | Update any field including `status` |
|
|
243
|
+
| DELETE | `/api/todos/:id` | Delete |
|
|
244
|
+
|
|
245
|
+
**フィールド**:
|
|
246
|
+
- `status`: `pending` / `in_progress` / `done` / `cancelled`
|
|
247
|
+
- `priority`: `low` / `normal` / `high` / `urgent`
|
|
248
|
+
- `session_id`: チャットセッションID(任意)。**設定すると圧縮を跨いだ自動再掲の対象になる** — 次ターン以降のプロンプト冒頭で未完了Todoが自動表示される。
|
|
249
|
+
- `injection_count`: 自動再掲された回数(読み取り専用)。一定回数を超えたTodoは再掲が停止する。
|
|
250
|
+
|
|
251
|
+
**セッション紐づけ例**:
|
|
252
|
+
```bash
|
|
253
|
+
# 作成時にsession_idを指定すると、このセッションのチャットに自動で再掲される
|
|
254
|
+
curl -X POST -H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \
|
|
255
|
+
http://localhost:8080/api/todos \
|
|
256
|
+
-d '{"title": "レポートを保存", "session_id": "'$SESSION_ID'", "priority": "high"}'
|
|
257
|
+
|
|
258
|
+
# 完了マーク(即座に更新すること)
|
|
259
|
+
curl -X PUT -H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \
|
|
260
|
+
http://localhost:8080/api/todos/$TODO_ID \
|
|
261
|
+
-d '{"status": "done"}'
|
|
262
|
+
```
|
|
263
|
+
|
|
232
264
|
### Config
|
|
233
265
|
|
|
234
266
|
| Method | Endpoint | Description |
|
|
@@ -240,6 +272,42 @@ Changes via the config API take effect immediately (no restart required).
|
|
|
240
272
|
|
|
241
273
|
Allowed keys: `LLM_COMMAND`, `REFLECTION_TIME`
|
|
242
274
|
|
|
275
|
+
### LLM Plugins (opt-in)
|
|
276
|
+
|
|
277
|
+
プラグイン方式の LLM 設定。`primary` を設定すると有効化される。未設定の場合は従来の `LLM_COMMAND` 経路で動作する。設定は `~/minion/llm/config.json` にファイルとして保存される (env var に依存しないため quote 破損バグの影響を受けない)。
|
|
278
|
+
|
|
279
|
+
| Method | Endpoint | Description |
|
|
280
|
+
|--------|----------|-------------|
|
|
281
|
+
| GET | `/api/llm/plugins` | List available plugins with capabilities & auth status |
|
|
282
|
+
| GET | `/api/llm/config` | Get current primary/enabled selection |
|
|
283
|
+
| PUT | `/api/llm/config` | Update primary/enabled. Body: `{primary?, enabled?}` |
|
|
284
|
+
|
|
285
|
+
**GET /api/llm/plugins response**:
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"success": true,
|
|
289
|
+
"plugins": [
|
|
290
|
+
{
|
|
291
|
+
"name": "claude",
|
|
292
|
+
"displayName": "Claude Code",
|
|
293
|
+
"capabilities": {
|
|
294
|
+
"toolUse": true,
|
|
295
|
+
"streaming": true,
|
|
296
|
+
"vision": true,
|
|
297
|
+
"imageGeneration": false,
|
|
298
|
+
"sessionResume": true
|
|
299
|
+
},
|
|
300
|
+
"authenticated": true,
|
|
301
|
+
"builtin": true,
|
|
302
|
+
"enabled": true,
|
|
303
|
+
"primary": true
|
|
304
|
+
}
|
|
305
|
+
]
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**PUT /api/llm/config** body: `{ "primary": "claude", "enabled": ["claude", "gemini", "codex"] }`
|
|
310
|
+
|
|
243
311
|
### Permissions
|
|
244
312
|
|
|
245
313
|
CLIツール(Claude Code, Gemini CLI, Codex CLI)のパーミッション管理。
|
|
@@ -853,6 +921,408 @@ POST `/api/minion/execution` body:
|
|
|
853
921
|
}
|
|
854
922
|
```
|
|
855
923
|
|
|
924
|
+
### Pending Revisions (PM のみ)
|
|
925
|
+
|
|
926
|
+
レビューで `revision_requested` になったステップを検知する。ミニオンの `revision-watcher` デーモンが30秒ごとにポーリングし、LLM で差し戻し先ステップを判断した上で `/api/minion/revision-reset` を呼び出す。
|
|
927
|
+
|
|
928
|
+
| Method | Endpoint | Description |
|
|
929
|
+
|--------|----------|-------------|
|
|
930
|
+
| GET | `/api/minion/pending-revisions` | `revision_requested` 状態のステップ一覧(PMのプロジェクトのみ) |
|
|
931
|
+
| POST | `/api/minion/revision-reset` | 差し戻し対象ステップを `pending` に戻す |
|
|
932
|
+
|
|
933
|
+
GET Response:
|
|
934
|
+
```json
|
|
935
|
+
{
|
|
936
|
+
"revisions": [
|
|
937
|
+
{
|
|
938
|
+
"execution_id": "uuid",
|
|
939
|
+
"workflow_name": "daily-check",
|
|
940
|
+
"revision_step_index": 2,
|
|
941
|
+
"review_comment": "string (レビュアーのフィードバック)",
|
|
942
|
+
"pipeline": [
|
|
943
|
+
{
|
|
944
|
+
"step_index": 0,
|
|
945
|
+
"skill_version_id": "uuid",
|
|
946
|
+
"skill_name": "string|null",
|
|
947
|
+
"assigned_role": "pm|engineer|accountant"
|
|
948
|
+
}
|
|
949
|
+
]
|
|
950
|
+
}
|
|
951
|
+
]
|
|
952
|
+
}
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
POST `/api/minion/revision-reset` body:
|
|
956
|
+
```json
|
|
957
|
+
{
|
|
958
|
+
"execution_id": "uuid",
|
|
959
|
+
"target_step_index": 0,
|
|
960
|
+
"revision_step_index": 2,
|
|
961
|
+
"revision_feedback": "string (省略可、target step の review_comment に保存)"
|
|
962
|
+
}
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
- `target_step_index` は差し戻し先(やり直し起点)のインデックス
|
|
966
|
+
- `revision_step_index` は差し戻しを要求されたレビューステップのインデックス(`target_step_index <= revision_step_index`)
|
|
967
|
+
- target から revision までのステップが `pending` に戻り、`revision_feedback` が再実行時のコンテキストとして注入される
|
|
968
|
+
|
|
969
|
+
Response:
|
|
970
|
+
```json
|
|
971
|
+
{
|
|
972
|
+
"success": true,
|
|
973
|
+
"reset_count": 3,
|
|
974
|
+
"target_step_index": 0,
|
|
975
|
+
"revision_step_index": 2
|
|
976
|
+
}
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
---
|
|
980
|
+
|
|
981
|
+
## DAG Workflows (HQ, ノード/エッジ方式)
|
|
982
|
+
|
|
983
|
+
DAG ワークフローは従来の線形パイプラインを拡張した有向非巡回グラフ方式のワークフローです。HQダッシュボードの DAG エディタで作成・編集できるほか、ミニオンAPI経由でも JSON ベースで編集可能(PMロールのみ)。ミニオンはプルベースのポーリングで pending ノードを検出・実行します。
|
|
984
|
+
|
|
985
|
+
### 編集エンドポイント (PMロール限定)
|
|
986
|
+
|
|
987
|
+
| Method | Endpoint | Description |
|
|
988
|
+
|--------|----------|-------------|
|
|
989
|
+
| POST | `/api/minion/dag-workflows` | 新規 DAG ワークフローを作成(ドラフトとして保存) |
|
|
990
|
+
| PUT | `/api/minion/dag-workflows/:id` | ドラフト graph / メタデータを更新 |
|
|
991
|
+
| POST | `/api/minion/dag-workflows/:id/publish` | ドラフトをバリデーションし新バージョンとして公開 |
|
|
992
|
+
|
|
993
|
+
いずれも Bearer 認証 + プロジェクトメンバーシップが `role='pm'` のミニオンに限定。非PMは 403。PUT / POST はドラフト保存時にセマンティックバリデーションを行わない(`graph` が object で `nodes` / `edges` 配列を持つことの構造チェックのみ)。パブリッシュ時には `validateDagGraph` によるフル検証(ノードID重複・エッジの参照整合性・サイクル検出・必須フィールド等)が走る。
|
|
994
|
+
|
|
995
|
+
**推奨ワークフロー**: create → put で graph を整える → publish、または既存ワークフローに対して put → publish。
|
|
996
|
+
|
|
997
|
+
POST `/api/minion/dag-workflows` body:
|
|
998
|
+
```json
|
|
999
|
+
{
|
|
1000
|
+
"project_id": "uuid",
|
|
1001
|
+
"name": "my-dag",
|
|
1002
|
+
"graph": { "nodes": [], "edges": [] },
|
|
1003
|
+
"content": "Markdown description (optional)",
|
|
1004
|
+
"change_summary": "initial draft (optional)"
|
|
1005
|
+
}
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
- `name` は `/^[a-z0-9-]+$/`
|
|
1009
|
+
- `graph` は省略可。省略時はドラフト無しで作成される
|
|
1010
|
+
- レスポンスは作成された DAG ワークフロー全体(`current_version_id` は null のまま、ドラフトに保存)
|
|
1011
|
+
|
|
1012
|
+
PUT `/api/minion/dag-workflows/:id` body(全フィールド optional、省略したものは変更しない):
|
|
1013
|
+
```json
|
|
1014
|
+
{
|
|
1015
|
+
"graph": { "nodes": [...], "edges": [...] },
|
|
1016
|
+
"content": "...",
|
|
1017
|
+
"change_summary": "Updated fan-out branch",
|
|
1018
|
+
"name": "renamed-dag",
|
|
1019
|
+
"is_active": true,
|
|
1020
|
+
"maturity": "preview|stable|..."
|
|
1021
|
+
}
|
|
1022
|
+
```
|
|
1023
|
+
|
|
1024
|
+
- `graph` を渡すと draft_graph が上書きされる(構造チェックのみ、セマンティックチェック無し)
|
|
1025
|
+
- `content` / `change_summary` は `graph` と併せて draft スロットに保存される
|
|
1026
|
+
|
|
1027
|
+
POST `/api/minion/dag-workflows/:id/publish` (body なし):
|
|
1028
|
+
- 現在の draft_graph を `validateDagGraph` でフル検証
|
|
1029
|
+
- OK なら新バージョン行を `dag_workflow_versions` に追加し、`current_version_id` を更新、ドラフトをクリア
|
|
1030
|
+
- NG なら 400 で `{ error, details: [...] }` を返却
|
|
1031
|
+
|
|
1032
|
+
### ミニオン CLI ラッパー
|
|
1033
|
+
|
|
1034
|
+
以下の `hq` サブコマンドが上記エンドポイントを呼び出す。いずれもリクエスト送信前にローカルで JSON 構文検証を行う。
|
|
1035
|
+
|
|
1036
|
+
```bash
|
|
1037
|
+
hq create dag-workflow <body.json> # POST /api/minion/dag-workflows
|
|
1038
|
+
hq put dag-workflow <id> <body.json> # PUT /api/minion/dag-workflows/:id
|
|
1039
|
+
hq publish dag-workflow <id> # POST /api/minion/dag-workflows/:id/publish
|
|
1040
|
+
hq fetch dag-workflow <id> # GET /api/minion/dag-workflows/:id
|
|
1041
|
+
hq fetch dag-execution <id> # GET /api/minion/dag-executions/:id
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
### DAG Read Endpoints (チャットコンテキスト用)
|
|
1045
|
+
|
|
1046
|
+
ユーザーがHQダッシュボードでDAGエディタ/実行詳細を閲覧中にチャットした場合、チャットコンテキストに `context.type = 'dag-workflow'` または `'dag-execution'` が注入される。以下の `hq` CLI コマンドで詳細を取得できる(内部的に `/api/minion/dag-workflows/:id` / `/api/minion/dag-executions/:id` を叩く)。
|
|
1047
|
+
|
|
1048
|
+
| コマンド | 対応エンドポイント | 用途 |
|
|
1049
|
+
|---------|-------------------|------|
|
|
1050
|
+
| `hq fetch dag-workflow <id>` | GET `/api/minion/dag-workflows/:id` | DAG ワークフロー定義(published graph + draft)取得 |
|
|
1051
|
+
| `hq fetch dag-execution <id>` | GET `/api/minion/dag-executions/:id` | DAG 実行詳細(graph_snapshot + node_executions)取得 |
|
|
1052
|
+
|
|
1053
|
+
いずれもミニオンのプロジェクトメンバーシップでスコープされる。ミニオンが参加していないプロジェクトのDAGは 404。
|
|
1054
|
+
|
|
1055
|
+
GET `/api/minion/dag-workflows/:id` Response:
|
|
1056
|
+
```json
|
|
1057
|
+
{
|
|
1058
|
+
"id": "uuid",
|
|
1059
|
+
"project_id": "uuid",
|
|
1060
|
+
"name": "my-dag",
|
|
1061
|
+
"is_active": true,
|
|
1062
|
+
"maturity": "draft|stable|...",
|
|
1063
|
+
"current_version_id": "uuid",
|
|
1064
|
+
"draft_graph": { "nodes": [...], "edges": [...] } ,
|
|
1065
|
+
"draft_content": "markdown|null",
|
|
1066
|
+
"draft_change_summary": "string|null",
|
|
1067
|
+
"draft_updated_at": "ISO|null",
|
|
1068
|
+
"current_version": {
|
|
1069
|
+
"id": "uuid",
|
|
1070
|
+
"version": 3,
|
|
1071
|
+
"graph": { "nodes": [...], "edges": [...] },
|
|
1072
|
+
"content": "markdown",
|
|
1073
|
+
"change_summary": "string",
|
|
1074
|
+
"created_at": "ISO"
|
|
1075
|
+
},
|
|
1076
|
+
"my_role": "pm|engineer|accountant|null"
|
|
1077
|
+
}
|
|
1078
|
+
```
|
|
1079
|
+
|
|
1080
|
+
GET `/api/minion/dag-executions/:id` Response:
|
|
1081
|
+
```json
|
|
1082
|
+
{
|
|
1083
|
+
"id": "uuid",
|
|
1084
|
+
"dag_workflow_id": "uuid",
|
|
1085
|
+
"dag_workflow_name": "my-dag",
|
|
1086
|
+
"dag_workflow_version_id": "uuid",
|
|
1087
|
+
"dag_workflow_version": 3,
|
|
1088
|
+
"project_id": "uuid",
|
|
1089
|
+
"status": "pending|running|completed|failed",
|
|
1090
|
+
"graph_snapshot": { "nodes": [...], "edges": [...] },
|
|
1091
|
+
"started_at": "ISO|null",
|
|
1092
|
+
"completed_at": "ISO|null",
|
|
1093
|
+
"created_at": "ISO",
|
|
1094
|
+
"node_executions": [
|
|
1095
|
+
{
|
|
1096
|
+
"id": "uuid",
|
|
1097
|
+
"node_id": "n2",
|
|
1098
|
+
"scope_path": "",
|
|
1099
|
+
"status": "pending|waiting|running|completed|failed|skipped",
|
|
1100
|
+
"outcome": "success|failure|null",
|
|
1101
|
+
"input_data": {},
|
|
1102
|
+
"output_data": {},
|
|
1103
|
+
"output_summary": "string|null",
|
|
1104
|
+
"requires_review": false,
|
|
1105
|
+
"review_status": "review_pending|approved|rejected|revision_requested|null",
|
|
1106
|
+
"revision_count": 0,
|
|
1107
|
+
"started_at": "ISO|null",
|
|
1108
|
+
"completed_at": "ISO|null"
|
|
1109
|
+
}
|
|
1110
|
+
]
|
|
1111
|
+
}
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
### DAG Runtime Endpoints (ミニオン向け)
|
|
1115
|
+
|
|
1116
|
+
| Method | Endpoint | Description |
|
|
1117
|
+
|--------|----------|-------------|
|
|
1118
|
+
| GET | `/api/dag/minion/pending-nodes` | 自分が実行すべき pending ノード一覧(ロール一致・依存解決済み) |
|
|
1119
|
+
| POST | `/api/dag/minion/claim-node` | ノードを楽観ロックで取得(排他実行) |
|
|
1120
|
+
| POST | `/api/dag/minion/node-complete` | ノード完了を報告し、下流ノードへカスケード |
|
|
1121
|
+
|
|
1122
|
+
ミニオンの `dag-step-poller` デーモンが30秒ごとに `pending-nodes` を叩き、最大2並列で claim → skill/transform 実行 → node-complete の流れを回す。`skill`・`transform` 以外のノード(start/end/review/fan_out/join/conditional)はHQ内部のカスケードエンジンが処理するため、ミニオンには返されない。
|
|
1123
|
+
|
|
1124
|
+
#### GET /api/dag/minion/pending-nodes
|
|
1125
|
+
|
|
1126
|
+
Response:
|
|
1127
|
+
```json
|
|
1128
|
+
{
|
|
1129
|
+
"nodes": [
|
|
1130
|
+
{
|
|
1131
|
+
"node_execution_id": "uuid",
|
|
1132
|
+
"execution_id": "uuid",
|
|
1133
|
+
"dag_workflow_name": "my-dag",
|
|
1134
|
+
"node_id": "node-abc",
|
|
1135
|
+
"scope_path": "",
|
|
1136
|
+
"node_type": "skill",
|
|
1137
|
+
"skill_version_id": "uuid|null",
|
|
1138
|
+
"skill_name": "skill-1|null",
|
|
1139
|
+
"assigned_role": "pm|engineer|accountant",
|
|
1140
|
+
"input_data": { "...": "..." },
|
|
1141
|
+
"revision_feedback": "string|null",
|
|
1142
|
+
"transform_instruction": "string|null"
|
|
1143
|
+
}
|
|
1144
|
+
]
|
|
1145
|
+
}
|
|
1146
|
+
```
|
|
1147
|
+
|
|
1148
|
+
返却条件:
|
|
1149
|
+
- `assigned_role` がミニオンのプロジェクトロールと一致
|
|
1150
|
+
- ノードの `status` が `pending`
|
|
1151
|
+
- `node_type` が `skill` または `transform`
|
|
1152
|
+
- スコープ内の全依存ノードが `completed`(`requires_review` の場合は `approved` も必要)
|
|
1153
|
+
|
|
1154
|
+
`scope_path` は fan-out 内のインスタンスを示す(ルートは空文字列、1段のfan-outでは `fan_out_A:0`、ネストでは `fan_out_A:2/fan_out_B:1`)。依存解決は同じ scope_path 内で閉じる。
|
|
1155
|
+
|
|
1156
|
+
#### POST /api/dag/minion/claim-node
|
|
1157
|
+
|
|
1158
|
+
Body:
|
|
1159
|
+
```json
|
|
1160
|
+
{ "node_execution_id": "uuid" }
|
|
1161
|
+
```
|
|
1162
|
+
|
|
1163
|
+
Response (成功 / 201):
|
|
1164
|
+
```json
|
|
1165
|
+
{ "success": true, "node_execution_id": "uuid" }
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
Response (競合 / 409): 他ミニオンが既に claim 済み、または状態が pending でない場合。
|
|
1169
|
+
```json
|
|
1170
|
+
{ "error": "Node already claimed or not pending" }
|
|
1171
|
+
```
|
|
1172
|
+
|
|
1173
|
+
409 を受け取った場合は `pending-nodes` を取り直して次のノードに進むこと。
|
|
1174
|
+
|
|
1175
|
+
#### POST /api/dag/minion/node-complete
|
|
1176
|
+
|
|
1177
|
+
Body:
|
|
1178
|
+
```json
|
|
1179
|
+
{
|
|
1180
|
+
"node_execution_id": "uuid",
|
|
1181
|
+
"status": "completed|failed",
|
|
1182
|
+
"output_data": { "...": "..." },
|
|
1183
|
+
"output_summary": "string (省略可、レポート/サマリー)"
|
|
1184
|
+
}
|
|
1185
|
+
```
|
|
1186
|
+
|
|
1187
|
+
- `status: completed` で `requires_review` なノードはサーバ側で `review_status=review_pending` になりカスケードは停止(レビュー承認まで下流は生成されない)
|
|
1188
|
+
- `status: failed` でもカスケードは走る(fan-out join が `on_failure=ignore|collect` で集約できるため)
|
|
1189
|
+
- `output_data` は下流ノードの `input_data` に伝播する。**スキル実行時はスキル本文の「## Output Data」セクションの JSON コードブロックを抽出して `output_data` に載せる規約**(ミニオンの `dag-node-executor` がこの抽出を行う)
|
|
1190
|
+
|
|
1191
|
+
Response:
|
|
1192
|
+
```json
|
|
1193
|
+
{ "success": true }
|
|
1194
|
+
```
|
|
1195
|
+
|
|
1196
|
+
レビュー待ち状態の場合:
|
|
1197
|
+
```json
|
|
1198
|
+
{ "success": true, "review_pending": true }
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
### DAG Graph Structure
|
|
1202
|
+
|
|
1203
|
+
DAG ワークフローの graph は以下の構造で保存される(`dag_workflow_versions.graph` / `dag_executions.graph_snapshot`):
|
|
1204
|
+
|
|
1205
|
+
```json
|
|
1206
|
+
{
|
|
1207
|
+
"nodes": [
|
|
1208
|
+
{ "id": "n1", "type": "start", "label": "Start", "position": { "x": 0, "y": 0 } },
|
|
1209
|
+
{
|
|
1210
|
+
"id": "n2",
|
|
1211
|
+
"type": "skill",
|
|
1212
|
+
"label": "Fetch data",
|
|
1213
|
+
"skill_version_id": "uuid",
|
|
1214
|
+
"assigned_role": "engineer"
|
|
1215
|
+
},
|
|
1216
|
+
{
|
|
1217
|
+
"id": "n3",
|
|
1218
|
+
"type": "fan_out",
|
|
1219
|
+
"label": "Per item",
|
|
1220
|
+
"fan_out_source": ".items",
|
|
1221
|
+
"template": { "nodes": [ ... ], "edges": [ ... ] },
|
|
1222
|
+
"join_mode": "all",
|
|
1223
|
+
"on_failure": "collect",
|
|
1224
|
+
"max_concurrency": 3
|
|
1225
|
+
},
|
|
1226
|
+
{
|
|
1227
|
+
"id": "n4",
|
|
1228
|
+
"type": "join",
|
|
1229
|
+
"label": "Collect",
|
|
1230
|
+
"join_mode": "all",
|
|
1231
|
+
"aggregation": "array"
|
|
1232
|
+
},
|
|
1233
|
+
{ "id": "n5", "type": "end", "label": "End" }
|
|
1234
|
+
],
|
|
1235
|
+
"edges": [
|
|
1236
|
+
{ "id": "e1", "source": "n1", "target": "n2", "kind": "normal" },
|
|
1237
|
+
{ "id": "e2", "source": "n2", "target": "n3" },
|
|
1238
|
+
{ "id": "e3", "source": "n3", "target": "n4" },
|
|
1239
|
+
{ "id": "e4", "source": "n4", "target": "n5" }
|
|
1240
|
+
]
|
|
1241
|
+
}
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
#### Node Types
|
|
1245
|
+
|
|
1246
|
+
| `type` | 役割 | ミニオン実行 |
|
|
1247
|
+
|--------|------|---------------|
|
|
1248
|
+
| `start` | エントリポイント | ❌ (内部) |
|
|
1249
|
+
| `end` | 終端 | ❌ (内部) |
|
|
1250
|
+
| `skill` | スキル実行。`skill_version_id` と `assigned_role` が必須 | ✅ |
|
|
1251
|
+
| `transform` | LLM によるデータ変換。`transform_instruction` が必須 | ✅ |
|
|
1252
|
+
| `review` | レビューゲート。`approved` / `revision_requested` で分岐 | ❌ (内部) |
|
|
1253
|
+
| `fan_out` | 配列入力をテンプレートsub-graphに展開して並列実行。子が全て settle すると自ノードが completed に遷移 | ❌ (内部) |
|
|
1254
|
+
| `join` | N本の上流エッジを待ち合わせる汎用バリア。fan_out とは独立 | ❌ (内部) |
|
|
1255
|
+
| `conditional` | 条件分岐(`llm` / `regex` / `jq`) | ❌ (内部) |
|
|
1256
|
+
|
|
1257
|
+
#### DagNode 主要フィールド
|
|
1258
|
+
|
|
1259
|
+
| Field | Type | 説明 |
|
|
1260
|
+
|-------|------|------|
|
|
1261
|
+
| `id` | string | グラフ内ユニークなノードID |
|
|
1262
|
+
| `type` | DagNodeType | 上記ノード種別 |
|
|
1263
|
+
| `label` | string | 表示名 |
|
|
1264
|
+
| `skill_version_id` | string? | skill ノードで使用するスキルバージョンUUID |
|
|
1265
|
+
| `assigned_role` | `pm`\|`engineer`\|`accountant` | 実行ロール |
|
|
1266
|
+
| `fan_out_source` | string? | fan_out: 入力から配列を取り出すドット記法(例 `.items`) |
|
|
1267
|
+
| `template` | DagGraph? | fan_out: 各要素ごとに展開するsub-graph |
|
|
1268
|
+
| `max_concurrency` | number? | fan_out: 並列インスタンス上限 |
|
|
1269
|
+
| `join_mode` | `all`\|`any`\|`majority` | fan_out / join: 完了判定 |
|
|
1270
|
+
| `on_failure` | `fail_all`\|`ignore`\|`collect` | fan_out / join: 失敗時の挙動 |
|
|
1271
|
+
| `aggregation` | `array`\|`merge` | join: 上流出力の束ね方(デフォルト `array`) |
|
|
1272
|
+
| `condition_type` | `llm`\|`regex`\|`jq` | conditional: 条件評価方式 |
|
|
1273
|
+
| `condition_expression` | string | conditional: 条件式 |
|
|
1274
|
+
| `branches` | Record<string,string> | conditional: 条件出力→遷移先ノードID |
|
|
1275
|
+
| `default_branch` | string? | conditional: マッチしなかった場合の遷移先 |
|
|
1276
|
+
| `transform_instruction` | string | transform: LLMへの変換指示 |
|
|
1277
|
+
| `review` | object | review: レビュー設定 |
|
|
1278
|
+
|
|
1279
|
+
#### DagEdge
|
|
1280
|
+
|
|
1281
|
+
| Field | Type | 説明 |
|
|
1282
|
+
|-------|------|------|
|
|
1283
|
+
| `id` | string | エッジID |
|
|
1284
|
+
| `source` | string | 起点ノードID |
|
|
1285
|
+
| `target` | string | 終点ノードID |
|
|
1286
|
+
| `kind` | `normal`\|`approved`\|`revision` | `normal`=通常、`approved`=review承認時、`revision`=review差し戻し時(サイクル検出では無視される) |
|
|
1287
|
+
| `condition_label` | string? | 表示ラベル |
|
|
1288
|
+
|
|
1289
|
+
### DAG Execution API (読み取り)
|
|
1290
|
+
|
|
1291
|
+
| Method | Endpoint | Auth | 用途 |
|
|
1292
|
+
|--------|----------|------|------|
|
|
1293
|
+
| GET | `/api/dag/executions/:execId` | セッション | 実行詳細 (graph_snapshot + node_executions) 取得。UI用、ミニオンは通常使わない |
|
|
1294
|
+
|
|
1295
|
+
Response:
|
|
1296
|
+
```json
|
|
1297
|
+
{
|
|
1298
|
+
"id": "uuid",
|
|
1299
|
+
"dag_workflow_id": "uuid",
|
|
1300
|
+
"dag_workflow_name": "my-dag",
|
|
1301
|
+
"dag_workflow_version_id": "uuid",
|
|
1302
|
+
"status": "pending|running|completed|failed",
|
|
1303
|
+
"graph_snapshot": { "nodes": [...], "edges": [...] },
|
|
1304
|
+
"started_at": "ISO",
|
|
1305
|
+
"completed_at": "ISO|null",
|
|
1306
|
+
"node_executions": [
|
|
1307
|
+
{
|
|
1308
|
+
"id": "uuid",
|
|
1309
|
+
"node_id": "n2",
|
|
1310
|
+
"scope_path": "",
|
|
1311
|
+
"status": "pending|waiting|running|completed|failed|skipped",
|
|
1312
|
+
"outcome": "success|failure|null",
|
|
1313
|
+
"input_data": {},
|
|
1314
|
+
"output_data": {},
|
|
1315
|
+
"output_summary": "string|null",
|
|
1316
|
+
"requires_review": false,
|
|
1317
|
+
"review_status": "review_pending|approved|rejected|revision_requested|null",
|
|
1318
|
+
"revision_count": 0,
|
|
1319
|
+
"started_at": "ISO|null",
|
|
1320
|
+
"completed_at": "ISO|null"
|
|
1321
|
+
}
|
|
1322
|
+
]
|
|
1323
|
+
}
|
|
1324
|
+
```
|
|
1325
|
+
|
|
856
1326
|
### Issue Reporting (GitHub Issue 起票)
|
|
857
1327
|
|
|
858
1328
|
| Method | Endpoint | Description |
|