@geekbeer/minion 3.50.0 → 3.51.2

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.
@@ -0,0 +1,101 @@
1
+ /**
2
+ * DAG Cron Poller
3
+ *
4
+ * Polling daemon that asks HQ to fire any due cron-scheduled DAG workflows
5
+ * for projects where this minion is the PM. The actual claim, trigger, and
6
+ * next_run_at advancement are all done in HQ — the poller is just a clock
7
+ * source. Multiple PMs in the same project are race-safe via a conditional
8
+ * UPDATE on dag_workflows.next_run_at inside the HQ handler.
9
+ *
10
+ * The minimum cron interval (5 min) makes a 60s polling cadence more than
11
+ * precise enough; cron precision is bounded by max(poll_interval, 60s).
12
+ */
13
+
14
+ const { config, isHqConfigured } = require('../config')
15
+
16
+ const POLL_INTERVAL_MS = 60_000
17
+
18
+ let polling = false
19
+ let pollTimer = null
20
+ let lastPollAt = null
21
+ let lastFiredCount = 0
22
+
23
+ async function pollOnce() {
24
+ if (!isHqConfigured()) return
25
+ if (polling) return
26
+
27
+ polling = true
28
+ try {
29
+ const url = `${config.HQ_URL}/api/dag/minion/dag-cron-tick`
30
+ const resp = await fetch(url, {
31
+ method: 'POST',
32
+ headers: {
33
+ 'Content-Type': 'application/json',
34
+ 'Authorization': `Bearer ${config.API_TOKEN}`,
35
+ },
36
+ })
37
+
38
+ if (!resp.ok) {
39
+ throw new Error(`dag-cron-tick failed: ${resp.status}`)
40
+ }
41
+
42
+ const data = await resp.json()
43
+ const fired = Array.isArray(data.fired) ? data.fired : []
44
+ const skipped = Array.isArray(data.skipped) ? data.skipped : []
45
+ const errors = Array.isArray(data.errors) ? data.errors : []
46
+
47
+ lastFiredCount = fired.length
48
+
49
+ if (fired.length > 0 || errors.length > 0) {
50
+ console.log(
51
+ `[DagCronPoller] Fired ${fired.length}, skipped ${skipped.length}, errors ${errors.length}`
52
+ )
53
+ for (const f of fired) {
54
+ console.log(`[DagCronPoller] fired: ${f.name} (workflow ${f.workflow_id} → execution ${f.execution_id})`)
55
+ }
56
+ for (const e of errors) {
57
+ console.error(`[DagCronPoller] error on ${e.workflow_id}: ${e.error}`)
58
+ }
59
+ }
60
+ } catch (err) {
61
+ if (err.message?.includes('fetch failed') || err.message?.includes('ECONNREFUSED')) {
62
+ console.log('[DagCronPoller] HQ unreachable, will retry next cycle')
63
+ } else {
64
+ console.error(`[DagCronPoller] Poll error: ${err.message}`)
65
+ }
66
+ } finally {
67
+ polling = false
68
+ lastPollAt = new Date().toISOString()
69
+ }
70
+ }
71
+
72
+ function start() {
73
+ if (!isHqConfigured()) {
74
+ console.log('[DagCronPoller] HQ not configured, cron poller disabled')
75
+ return
76
+ }
77
+
78
+ // Stagger initial poll so the three pollers (step / dag-step / cron) do
79
+ // not all hit HQ in the same second on minion startup.
80
+ setTimeout(() => pollOnce(), 12_000)
81
+ pollTimer = setInterval(() => pollOnce(), POLL_INTERVAL_MS)
82
+ console.log(`[DagCronPoller] Started (polling every ${POLL_INTERVAL_MS / 1000}s)`)
83
+ }
84
+
85
+ function stop() {
86
+ if (pollTimer) {
87
+ clearInterval(pollTimer)
88
+ pollTimer = null
89
+ console.log('[DagCronPoller] Stopped')
90
+ }
91
+ }
92
+
93
+ function getStatus() {
94
+ return {
95
+ running: pollTimer !== null,
96
+ last_poll_at: lastPollAt,
97
+ last_fired_count: lastFiredCount,
98
+ }
99
+ }
100
+
101
+ module.exports = { start, stop, pollOnce, getStatus }
@@ -1275,8 +1275,7 @@ PUT `/api/minion/dag-workflows/:id` body(全フィールド optional、省略
1275
1275
  "content": "...",
1276
1276
  "change_summary": "Updated fan-out branch",
1277
1277
  "name": "renamed-dag",
1278
- "is_active": true,
1279
- "maturity": "preview|stable|..."
1278
+ "is_active": true
1280
1279
  }
1281
1280
  ```
1282
1281
 
@@ -1338,7 +1337,7 @@ POST `/api/minion/dag-workflows/:id/publish` (body なし):
1338
1337
  |------|---------------|------|
1339
1338
  | `start` / `end` | なし | |
1340
1339
  | `skill` | `skill_id`, `skill_version_id`, `assigned_role` | |
1341
- | `review` | `review: { reviewer_type, criteria, revision_target }` | `revision` エッジと `approved` エッジが**自動生成**される |
1340
+ | `review` | `assigned_role`, `review: { criteria, revision_target }` | `assigned_role: 'human'` でダッシュボードからの人間レビュー、`'pm'`/`'engineer'`/`'accountant'` で将来の minion レビュー枠(現在は未実装、`waiting` で停止)。`revision` / `approved` エッジは自動生成 |
1342
1341
  | `fan_out` | `fan_out_source`, `template` | `template` は sub-graph `{ nodes, edges }` |
1343
1342
  | `join` | `join_mode`, `aggregation` | |
1344
1343
  | `conditional` | `condition_type`, `branches` or `default_branch` | |
@@ -1351,13 +1350,24 @@ POST `/api/minion/dag-workflows/:id/publish` (body なし):
1351
1350
  "label": "Quality Check",
1352
1351
  "after": "skill_1",
1353
1352
  "before": "end",
1353
+ "assigned_role": "human",
1354
1354
  "review": {
1355
- "reviewer_type": "human",
1356
1355
  "criteria": "成果物の品質を確認",
1357
- "revision_target": "skill_1"
1356
+ "revision_target": "skill_1",
1357
+ "preferred_reviewer_id": "<user UUID(任意・指定なしの場合は全プロジェクトメンバーに通知)>"
1358
1358
  }
1359
1359
  }
1360
1360
  ```
1361
+
1362
+ `assigned_role` の値:
1363
+ - `human` — ダッシュボードから人間が承認 (`workflow.review_requested` 通知が送信される)
1364
+ - `pm` / `engineer` / `accountant` — 将来の minion レビュー枠 (現在は未実装、`waiting` で停止)
1365
+
1366
+ `preferred_reviewer_id` (任意):
1367
+ - `assigned_role === 'human'` の場合: `profiles.user_id` を指定すると通知をその人に絞る。脱退済みなら全メンバーにフォールバック
1368
+ - `assigned_role !== 'human'` の場合: `minions.id` を指定(現在は未実装)
1369
+
1370
+ > 互換性: 旧形式 `review: { reviewer_type, reviewer_id }` も読み取り可能ですが、新規作成時は `assigned_role` + `preferred_reviewer_id` を使ってください。
1361
1371
  → `skill_1 → end` のエッジが削除され、以下が自動生成:
1362
1372
  - `skill_1 → review_1` (normal)
1363
1373
  - `review_1 → end` (approved)
@@ -1634,7 +1644,6 @@ GET `/api/minion/dag-workflows/:id` Response:
1634
1644
  "project_id": "uuid",
1635
1645
  "name": "my-dag",
1636
1646
  "is_active": true,
1637
- "maturity": "draft|stable|...",
1638
1647
  "current_version_id": "uuid",
1639
1648
  "draft_graph": { "nodes": [...], "edges": [...] } ,
1640
1649
  "draft_content": "markdown|null",
@@ -1693,9 +1702,12 @@ GET `/api/minion/dag-executions/:id` Response:
1693
1702
  | GET | `/api/dag/minion/pending-nodes` | 自分が実行すべき pending ノード一覧(ロール一致・依存解決済み) |
1694
1703
  | POST | `/api/dag/minion/claim-node` | ノードを楽観ロックで取得(排他実行) |
1695
1704
  | POST | `/api/dag/minion/node-complete` | ノード完了を報告し、下流ノードへカスケード |
1705
+ | POST | `/api/dag/minion/dag-cron-tick` | PMロールのミニオン専用。所属プロジェクトのcron-enabled DAGワークフローを発火 |
1696
1706
 
1697
1707
  ミニオンの `dag-step-poller` デーモンが30秒ごとに `pending-nodes` を叩き、最大2並列で claim → skill/transform 実行 → node-complete の流れを回す。`skill`・`transform` 以外のノード(start/end/review/fan_out/join/conditional)はHQ内部のカスケードエンジンが処理するため、ミニオンには返されない。
1698
1708
 
1709
+ `dag-cron-poller` デーモン(v3.51.0〜)は60秒ごとに `dag-cron-tick` を叩く。HQ側で「呼び出しミニオンがPMロールであるプロジェクトのDAGワークフロー」のうち `cron_enabled AND next_run_at <= now()` を atomic claim → triggerDagExecution。Push型ではないので、PMミニオンが offline ならcron発火が遅延する。
1710
+
1699
1711
  #### GET /api/dag/minion/pending-nodes
1700
1712
 
1701
1713
  Response:
@@ -1774,6 +1786,32 @@ Response:
1774
1786
  { "success": true, "review_pending": true }
1775
1787
  ```
1776
1788
 
1789
+ #### POST /api/dag/minion/dag-cron-tick
1790
+
1791
+ PMロールのミニオン専用。所属プロジェクトのcron-enabled DAGワークフローを発火させる。`dag-cron-poller` が60秒間隔で叩くため、AI側から直接呼ぶ必要はない。
1792
+
1793
+ Body: なし
1794
+
1795
+ Response:
1796
+ ```json
1797
+ {
1798
+ "fired": [
1799
+ { "workflow_id": "uuid", "execution_id": "uuid", "name": "Daily Report" }
1800
+ ],
1801
+ "skipped": [
1802
+ { "workflow_id": "uuid", "reason": "claimed_by_other|already_running" }
1803
+ ],
1804
+ "errors": [
1805
+ { "workflow_id": "uuid", "error": "..." }
1806
+ ]
1807
+ }
1808
+ ```
1809
+
1810
+ - 呼び出しミニオンがPMロールでないプロジェクトのワークフローは対象外(HQ側で `project_members.role='pm'` でフィルタ)
1811
+ - 同一プロジェクトに複数のPMミニオンが居る場合、条件付き UPDATE で先勝ちclaim。負けた側は `skipped: claimed_by_other` で返る
1812
+ - `cron_skip_if_running=true` のワークフローで前回実行が `pending`/`running` のままなら `skipped: already_running` で返る
1813
+ - 発火後の経路は手動トリガーと同じ。`dag_executions` 行作成 → start auto-complete → cascade → 各ミニオンの `dag-step-poller` が pending node を pull
1814
+
1777
1815
  ### DAG Graph Structure
1778
1816
 
1779
1817
  DAG ワークフローの graph は以下の構造で保存される(`dag_workflow_versions.graph` / `dag_executions.graph_snapshot`):
@@ -154,6 +154,8 @@ DAG ワークフローは有向非巡回グラフでスキル間の依存関係
154
154
 
155
155
  **HQダッシュボードの DAG エディタ(プロジェクト画面 → 「DAG (beta)」タブ)で GUI 編集**できるほか、**ミニオンからは JSON ベースで編集可能**(PMロールのみ)。ミニオンは実行ランタイムも担当し、`dag-step-poller` デーモンが pending ノードを自動で処理します。
156
156
 
157
+ **cronスケジュール (v3.51.0〜):** DAGワークフローは cron 式による定期実行をサポート。設定はHQダッシュボードのDAGビューにある SchedulePanel から行う(最小実行間隔 5分)。発火主体はそのプロジェクトのPMロールのミニオンで、`dag-cron-poller` デーモンが60秒間隔で `/api/dag/minion/dag-cron-tick` を叩いて発火させる。PM不在のプロジェクトでは cron は発火しない。
158
+
157
159
  ### DAG ワークフロー一覧の取得
158
160
 
159
161
  ```bash
@@ -164,7 +166,7 @@ hq list dag-workflows
164
166
  hq list dag-workflows <project-uuid>
165
167
  ```
166
168
 
167
- レスポンスには各ワークフローの `id`, `name`, `is_active`, `maturity`, `my_role` 等が含まれる。個別の graph 詳細は `hq fetch dag-workflow <id>` で取得する。
169
+ レスポンスには各ワークフローの `id`, `name`, `is_active`, `my_role` 等が含まれる。個別の graph 詳細は `hq fetch dag-workflow <id>` で取得する。
168
170
 
169
171
  ### ミニオンによる DAG ワークフロー編集 (PM のみ)
170
172
 
@@ -307,8 +309,8 @@ cat > /tmp/n.json <<'EOF'
307
309
  "label": "Quality Check",
308
310
  "after": "skill_1",
309
311
  "before": "end_1",
312
+ "assigned_role": "human",
310
313
  "review": {
311
- "reviewer_type": "human",
312
314
  "criteria": "成果物の品質を確認",
313
315
  "revision_target": "skill_1"
314
316
  }
package/linux/bin/hq CHANGED
@@ -275,7 +275,7 @@ case "${1:-}" in
275
275
  body_file="${4:-}"
276
276
  if [ -z "$id" ] || [ -z "$body_file" ]; then
277
277
  echo "Usage: hq put dag-workflow <id> <body.json>" >&2
278
- echo " body.json may contain { graph, content, change_summary, name, is_active, maturity }" >&2
278
+ echo " body.json may contain { graph, content, change_summary, name, is_active }" >&2
279
279
  exit 1
280
280
  fi
281
281
  validate_json_file "$body_file"
package/linux/server.js CHANGED
@@ -60,6 +60,7 @@ const { getConfigWarnings } = require('../core/lib/config-warnings')
60
60
  // Pull-model daemons (from core/)
61
61
  const stepPoller = require('../core/lib/step-poller')
62
62
  const dagStepPoller = require('../core/lib/dag-step-poller')
63
+ const dagCronPoller = require('../core/lib/dag-cron-poller')
63
64
  const boardTaskPoller = require('../core/lib/board-task-poller')
64
65
  const revisionWatcher = require('../core/lib/revision-watcher')
65
66
  const reflectionScheduler = require('../core/lib/reflection-scheduler')
@@ -407,6 +408,7 @@ async function start() {
407
408
  // Start Pull-model daemons
408
409
  stepPoller.start()
409
410
  dagStepPoller.start()
411
+ dagCronPoller.start()
410
412
  boardTaskPoller.setRunner(boardTaskRunner)
411
413
  boardTaskPoller.start()
412
414
  revisionWatcher.start()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekbeer/minion",
3
- "version": "3.50.0",
3
+ "version": "3.51.2",
4
4
  "description": "AI Agent runtime for Minion - manages status and skill deployment on VPS",
5
5
  "main": "linux/server.js",
6
6
  "bin": {
package/rules/core.md CHANGED
@@ -176,7 +176,9 @@ Note: Codex CLI の `.codex/` ディレクトリはLLMからの直接編集が
176
176
 
177
177
  主なカテゴリ: Projects, Context, Workflows, DAG Workflows, Skills, Executions, Routines, Reports
178
178
 
179
- DAG ワークフローのランタイム API は `$HQ_URL/api/dag/minion/*`(pending-nodes / claim-node / node-complete)。`dag-step-poller` デーモンが自動でポーリングするため、通常ミニオンのAI側から直接叩くことは無い。
179
+ DAG ワークフローのランタイム API は `$HQ_URL/api/dag/minion/*`(pending-nodes / claim-node / node-complete / dag-cron-tick)。`dag-step-poller` と `dag-cron-poller` デーモンが自動でポーリングするため、通常ミニオンのAI側から直接叩くことは無い。
180
+
181
+ **DAG Cron Trigger (v3.51.0〜):** `dag_workflows.cron_enabled = true` が設定されたDAGワークフローは、所属プロジェクトのPMミニオンの `dag-cron-poller` が60秒間隔で `POST /api/dag/minion/dag-cron-tick` を叩いて発火する。発火主体はPMロールのミニオンに限定(HQ側で `project_members.role='pm'` でフィルタ)。最小実行間隔は5分(HQ側 `validateCronExpression` で enforce)。発火後の経路は手動トリガーと同じで、`dag_executions` 行が作成され、通常通り各ミニオンの `dag-step-poller` がpendingノードをclaimする。
180
182
 
181
183
  ## Environment Variables
182
184
 
package/win/bin/hq.ps1 CHANGED
@@ -237,7 +237,7 @@ switch ($Command) {
237
237
  $BodyFile = $Arg3
238
238
  if (-not $Id -or -not $BodyFile) {
239
239
  Write-Error "Usage: hq put dag-workflow <id> <body.json>"
240
- Write-Error " body.json may contain { graph, content, change_summary, name, is_active, maturity }"
240
+ Write-Error " body.json may contain { graph, content, change_summary, name, is_active }"
241
241
  exit 1
242
242
  }
243
243
  Assert-ValidJsonFile -Path $BodyFile
package/win/server.js CHANGED
@@ -36,6 +36,7 @@ const { getConfigWarnings } = require('../core/lib/config-warnings')
36
36
  // Pull-model daemons (from core/)
37
37
  const stepPoller = require('../core/lib/step-poller')
38
38
  const dagStepPoller = require('../core/lib/dag-step-poller')
39
+ const dagCronPoller = require('../core/lib/dag-cron-poller')
39
40
  const boardTaskPoller = require('../core/lib/board-task-poller')
40
41
  const revisionWatcher = require('../core/lib/revision-watcher')
41
42
  const reflectionScheduler = require('../core/lib/reflection-scheduler')
@@ -374,6 +375,7 @@ async function start() {
374
375
  // Start Pull-model daemons
375
376
  stepPoller.start()
376
377
  dagStepPoller.start()
378
+ dagCronPoller.start()
377
379
  boardTaskPoller.setRunner(boardTaskRunner)
378
380
  boardTaskPoller.start()
379
381
  revisionWatcher.start()