@geekbeer/minion 3.59.3 → 3.59.8

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.
@@ -17,7 +17,15 @@
17
17
  *
18
18
  * meeting_leave()
19
19
  * Records left_at for this minion. Returns { ok: true }; claude should
20
- * exit its participation loop after this call.
20
+ * exit its participation loop after this call. **Does NOT delete the
21
+ * meeting room** — only this minion's own departure is recorded.
22
+ *
23
+ * meeting_end()
24
+ * End and delete the meeting room (host minion only). Cascades to
25
+ * participants/messages and pushes end-notices to all other minions
26
+ * so their tmux sessions terminate. Returns { ok: true } or 403 if
27
+ * the calling minion is not the host. Use this when YOU created the
28
+ * meeting (via POST /api/minion/meetings) and the discussion is over.
21
29
  *
22
30
  * Environment (passed by meeting-runner):
23
31
  * MINION_MEETING_ID - meeting UUID
@@ -105,6 +113,22 @@ async function leave() {
105
113
  return { ok: true }
106
114
  }
107
115
 
116
+ async function endMeeting() {
117
+ const { resp, data } = await hqFetch(`/api/minion/meetings/${MEETING_ID}`, {
118
+ method: 'DELETE',
119
+ })
120
+ if (resp.status === 404 || data.meeting_ended) {
121
+ return { ok: true, meeting_ended: true }
122
+ }
123
+ if (resp.status === 403) {
124
+ throw new Error(data.error || 'Only the host minion can end this meeting')
125
+ }
126
+ if (!resp.ok) {
127
+ throw new Error(data.error || `end failed: ${resp.status}`)
128
+ }
129
+ return { ok: true }
130
+ }
131
+
108
132
  // --- Tool definitions --------------------------------------------------------
109
133
 
110
134
  function toolsList() {
@@ -143,8 +167,19 @@ function toolsList() {
143
167
  {
144
168
  name: 'meeting_leave',
145
169
  description:
146
- 'Leave the meeting. Call this when the meeting is over or you have been ' +
147
- 'explicitly excused. After calling this, exit your participation loop.',
170
+ 'Leave the meeting (depart, do NOT delete the room). Records your left_at. ' +
171
+ 'Use this when you have been excused but the meeting continues without you. ' +
172
+ 'If you are the host and the discussion is over, call meeting_end instead.',
173
+ inputSchema: { type: 'object', properties: {} },
174
+ },
175
+ {
176
+ name: 'meeting_end',
177
+ description:
178
+ 'End and delete the meeting room (host minion only). Cascades to participants ' +
179
+ 'and messages and shuts down every other minion in the room. Call this when ' +
180
+ 'YOU created the meeting and the discussion is concluded — meeting_leave only ' +
181
+ 'records your own departure and leaves the room hanging. Fails with 403 if you ' +
182
+ 'are not the host.',
148
183
  inputSchema: { type: 'object', properties: {} },
149
184
  },
150
185
  ]
@@ -160,6 +195,8 @@ async function handleToolCall(name, args) {
160
195
  return getState()
161
196
  case 'meeting_leave':
162
197
  return leave()
198
+ case 'meeting_end':
199
+ return endMeeting()
163
200
  default:
164
201
  throw new Error(`Unknown tool: ${name}`)
165
202
  }
@@ -34,6 +34,14 @@ function buildMeetingKickoffPrompt({ meetingId, title, purpose, host, selfName,
34
34
  '5. `meeting_wait_for_next_message` の戻り値が `{ meeting_ended: true }` になったら',
35
35
  ' `meeting_leave` を呼んでループを終了する',
36
36
  '',
37
+ '## ミーティングの終了 (leave と end の使い分け)',
38
+ '',
39
+ '- **`meeting_leave`**: 自分だけ退席する。ルームは残り、他参加者は続行可能。',
40
+ ' 「あなただけ退席してOK」と明示された時のみ使う。',
41
+ '- **`meeting_end`**: ルームを削除して全員を解散する。**ホストのみ呼び出せる** (403になる)。',
42
+ ' あなたが招集したミーティングで議論が終わったら `meeting_end` を呼ぶこと。',
43
+ ' `meeting_leave` だけだとルームが孤児として残り続けるので注意。',
44
+ '',
37
45
  '## 発言ガイドライン',
38
46
  '- 1回の発言は **2-3 文以内** を基本とする (会議でだらだら話さない)',
39
47
  '- 議論を前進させる発言を優先する (賛否・確認・代替案・質問)',
@@ -1989,13 +1989,17 @@ hq note search <project_id> "キーワード"
1989
1989
 
1990
1990
  ### Meeting Rooms 🧪 (HQ, experimental, v3.59.0〜)
1991
1991
 
1992
- ミーティング機能はミニオン上の `meeting` MCP サーバーが叩く HQ エンドポイント。通常 AI 側から直接コールすることはなく、招待 (`POST /api/meetings/invitations` をミニオンが受信) で起動された claude セッションが MCP ツール経由で利用する。
1992
+ ミーティング機能はミニオン上の `meeting` MCP サーバーが叩く HQ エンドポイント。**参加中**の操作は MCP ツール経由で自動的に叩かれるが、**自分で会議を招集する**場合 (例: PMミニオンが朝会を主催) は `POST /api/minion/meetings` をルーティン/スキルから直接叩く。
1993
1993
 
1994
1994
  | Method | Endpoint | Description |
1995
1995
  |--------|----------|-------------|
1996
+ | POST | `/api/minion/meetings` | **会議室を作成して参加者を招待する。** Body: `{project_id, title, purpose?, invitees?: [{type: 'user'\|'minion', id}]}`。呼び出し元ミニオンは自動でホスト+参加者になる。プロジェクトメンバーかつ `experimental_meetings` 有効ワークスペースで動作 |
1996
1997
  | GET | `/api/minion/meetings/:id` | ミーティングのメタデータ + 参加者一覧。`meeting_get_state` ツールから呼ばれる |
1997
1998
  | GET | `/api/minion/meetings/:id/messages?after=<iso>&wait=true` | long-poll で次の発言を待機 (最大 ~25秒)。404 = 会議終了 |
1998
1999
  | POST | `/api/minion/meetings/:id/messages` | 発言。Body: `{text}` |
1999
- | POST | `/api/minion/meetings/:id` | アクション。Body: `{action: 'leave' \| 'heartbeat'}` |
2000
+ | POST | `/api/minion/meetings/:id` | アクション。Body: `{action: 'leave' \| 'heartbeat'}`。`leave` は自分の `left_at` を記録するだけでルームは残る |
2001
+ | DELETE | `/api/minion/meetings/:id` | **ルームを削除して会議を終了する** (ホストミニオン限定、403 if not host)。CASCADE で participants/messages 削除 + 他参加ミニオンに終了通知 push |
2000
2002
 
2001
- これらは MCP ツール (`meeting_wait_for_next_message`, `meeting_speak`, `meeting_leave`) として claude に公開される。直接叩く必要はない。
2003
+ 参加中の操作 (GET state / long-poll messages / speak / leave / end / heartbeat) は MCP ツール (`meeting_get_state`, `meeting_wait_for_next_message`, `meeting_speak`, `meeting_leave`, `meeting_end`) として claude に公開されるため、直接叩く必要はない。会議の**作成**だけはルーティン/スキルから明示的に叩く必要がある (作成後は HQ がプッシュした招待で各ミニオンに mt-* tmux セッションが立ち上がる)。
2004
+
2005
+ **leave と end の違い**: `meeting_leave` は自分だけ退席するアクション (ルームは残る)、`meeting_end` (DELETE) はルームごと削除して全員解散させるアクション (ホストミニオン限定)。自分が招集した会議が終わったら必ず `meeting_end` を呼ばないとルームが孤児として残る。
@@ -821,6 +821,38 @@ curl -H "Authorization: Bearer $API_TOKEN" http://localhost:8080/api/meetings/ac
821
821
 
822
822
  セッション復帰機構は未実装。tmux/claude が落ちた場合は再起動されず、新しい招待を待つ。
823
823
 
824
+ ### ミーティングを自分で招集する (v3.59.6〜)
825
+
826
+ PMロール等で「自分が会議を主催したい」場合は HQ の `POST /api/minion/meetings` を直接叩く。呼び出し元ミニオンは自動でホスト + 参加者になり、`invitees` で指定した他ミニオン・人間が `meeting_participants` に挿入される。他ミニオンへの招待は HQ がプッシュするので、こちら側で追加処理は不要。
827
+
828
+ ```bash
829
+ curl -X POST "$HQ_URL/api/minion/meetings" \
830
+ -H "Authorization: Bearer $API_TOKEN" \
831
+ -H "Content-Type: application/json" \
832
+ -d '{
833
+ "project_id": "<project-uuid>",
834
+ "title": "朝会 2026-05-15",
835
+ "purpose": "前日進捗の共有とブロッカーの洗い出し",
836
+ "invitees": [
837
+ {"type": "minion", "id": "<other-minion-uuid>"},
838
+ {"type": "user", "id": "<user-uuid>"}
839
+ ]
840
+ }'
841
+ ```
842
+
843
+ **前提条件:**
844
+ - 自分がそのプロジェクトの `project_members` に登録されていること (403 が返るならPMに依頼)
845
+ - プロジェクトが所属する workspace の `feature_flags.experimental_meetings = true` (人間管理者しか有効化できない)
846
+ - 作成成功後、自分自身のミニオンにも招待が届き `mt-{id8}` tmux セッションが起動する
847
+
848
+ **会議の終了 (ホストの責務):**
849
+ 自分が招集した会議で議論が終わったら、MCP ツール `meeting_end` を呼んでルームを削除する。`meeting_leave` は自分が退席するだけでルームは残るため、ホストが leave で抜けるとルームが孤児になり HQ ダッシュボードのアクティブ会議室一覧に居座り続ける。`meeting_end` は ホストミニオン限定 (それ以外は 403)。
850
+
851
+ ```
852
+ meeting_end() # DELETE /api/minion/meetings/:id を裏で叩く
853
+ # → 全参加ミニオンに終了通知が push され、各 mt-* tmux が終了する
854
+ ```
855
+
824
856
  ## ツール・MCPサーバーのインストール
825
857
 
826
858
  スキルが `requires` で宣言している MCP サーバーや CLI ツールが不足している場合は、`~/.minion/docs/environment-setup.md` の手順に従ってインストールする。
@@ -165,6 +165,7 @@ async function runMeeting({ meetingId, title, purpose, host, hqUrl, selfName, ro
165
165
  'mcp__meeting__meeting_speak',
166
166
  'mcp__meeting__meeting_get_state',
167
167
  'mcp__meeting__meeting_leave',
168
+ 'mcp__meeting__meeting_end',
168
169
  ].join(',')
169
170
  const llmCommand = `${claudeBin} -p --mcp-config "${mcpConfigFile}" --allowedTools "${allowedTools}" < "${promptFile}"`
170
171
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekbeer/minion",
3
- "version": "3.59.3",
3
+ "version": "3.59.8",
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
@@ -279,7 +279,7 @@ Routine 実行中は以下もtmuxセッション環境で利用可能:
279
279
 
280
280
  ## Meeting Participation (ミーティング参加, experimental, v3.59.0〜)
281
281
 
282
- HQ からミーティング招待 (`POST /api/meetings/invitations`) を受け取ると、専用 tmux セッション `mt-{meetingId8}` で claude が起動し、`meeting` MCP サーバー経由で会議に参加する。
282
+ HQ からミーティング招待 (`POST /api/meetings/invitations`) を受け取ると、専用 tmux セッション `mt-{meetingId8}` で claude が起動し、`meeting` MCP サーバー経由で会議に参加する。**自分から会議を招集する** (`POST $HQ_URL/api/minion/meetings`) ことも可能 (v3.59.6〜、PMロール推奨)。詳細は `~/.minion/docs/task-guides.md` の「ミーティングを自分で招集する」を参照。
283
283
 
284
284
  **基本ループ:**
285
285
  1. `meeting_get_state` で参加者・目的・タイトルを把握
@@ -291,6 +291,11 @@ HQ からミーティング招待 (`POST /api/meetings/invitations`) を受け
291
291
  4. 応答すべきなら `meeting_speak` で **1-3 文程度** の発言を送信
292
292
  5. 戻り値が `{ meeting_ended: true }` になったら `meeting_leave` を呼んで終了
293
293
 
294
+ **`meeting_leave` と `meeting_end` の使い分け** (v3.59.7〜):
295
+ - `meeting_leave` = 自分だけ退席。ルームは残り議論は続行
296
+ - `meeting_end` = ルームごと削除して全員解散 (**ホストミニオンのみ**、403になる)
297
+ - 自分が招集した会議で議論が終わったら **必ず `meeting_end`** を呼ぶこと。`meeting_leave` だけだとルームが孤児として残る
298
+
294
299
  **振る舞いのルール:**
295
300
  - 発言は短く保つ (会議でだらだら話さない)
296
301
  - 他のミニオンの発言と重複しない (既に同じ趣旨を誰かが言っていたら追従しない)
@@ -130,6 +130,7 @@ async function runMeeting({ meetingId, title, purpose, host, hqUrl, selfName, ro
130
130
  'mcp__meeting__meeting_speak',
131
131
  'mcp__meeting__meeting_get_state',
132
132
  'mcp__meeting__meeting_leave',
133
+ 'mcp__meeting__meeting_end',
133
134
  ].join(',')
134
135
 
135
136
  await fs.writeFile(
@@ -48,10 +48,15 @@ $needsAdmin = $Command -in $adminRequired
48
48
  # `stop --force` rewrites NSSM AppExit config and may need to kill processes
49
49
  # owned by the service account → requires Administrator.
50
50
  if ($Command -eq 'stop' -and $Force) { $needsAdmin = $true }
51
+ # `restart --all` triggers the MinionVNC schtask which is registered with
52
+ # /RL HIGHEST, so schtasks /Run requires Administrator.
53
+ if ($Command -eq 'restart' -and $All) { $needsAdmin = $true }
51
54
  if ($needsAdmin) {
52
55
  $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
53
56
  if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
54
- $label = if ($Command -eq 'stop' -and $Force) { 'stop --force' } else { $Command }
57
+ $label = if ($Command -eq 'stop' -and $Force) { 'stop --force' }
58
+ elseif ($Command -eq 'restart' -and $All) { 'restart --all' }
59
+ else { $Command }
55
60
  Write-Host "ERROR: '$label' requires Administrator privileges." -ForegroundColor Red
56
61
  Write-Host " Right-click PowerShell and select 'Run as administrator'." -ForegroundColor Yellow
57
62
  exit 1
@@ -585,7 +590,7 @@ function Restart-AllMinionServices {
585
590
  foreach ($svc in $services) {
586
591
  $state = Get-ServiceState $svc
587
592
  if (-not $state) {
588
- Write-Warn "$svc: not installed, skipping"
593
+ Write-Warn "${svc}: not installed, skipping"
589
594
  continue
590
595
  }
591
596
  if ($state -ne 'STOPPED') {
@@ -614,9 +619,17 @@ function Restart-AllMinionServices {
614
619
  }
615
620
 
616
621
  # Step 5: Re-trigger user-session logon tasks. schtasks /Run is a no-op if
617
- # the task is already running, so this is safe regardless of current state.
618
- schtasks /Run /TN MinionVNC 2>$null | Out-Null
619
- schtasks /Run /TN MinionWSL 2>$null | Out-Null
622
+ # the task is already running. MinionVNC is /RL HIGHEST so we require
623
+ # Administrator at the entry point (see needsAdmin check above), which
624
+ # is what allows this /Run to succeed.
625
+ foreach ($task in @('MinionVNC', 'MinionWSL')) {
626
+ $output = & schtasks /Run /TN $task 2>&1
627
+ if ($LASTEXITCODE -eq 0) {
628
+ Write-Host "Triggered logon task: $task"
629
+ } else {
630
+ Write-Warn "Logon task '$task' could not be triggered: $output"
631
+ }
632
+ }
620
633
 
621
634
  Write-Host "All minion services restarted"
622
635
  }
@@ -1874,7 +1887,7 @@ switch ($Command) {
1874
1887
  Write-Host " stop Stop the minion-agent service (graceful)"
1875
1888
  Write-Host " stop --force Force-stop all minion services & processes (admin required)"
1876
1889
  Write-Host " restart Restart the minion-agent service"
1877
- Write-Host " restart --all Restart all minion services (agent + websockify + cloudflared)"
1890
+ Write-Host " restart --all Restart all minion services + logon tasks (admin required)"
1878
1891
  Write-Host " status Show agent service status"
1879
1892
  Write-Host " health Check agent health endpoint"
1880
1893
  Write-Host " daemons Show all daemon service status"
@@ -1902,5 +1915,6 @@ switch ($Command) {
1902
1915
  Write-Host " minion-cloudflared, then re-trigger MinionVNC/MinionWSL"
1903
1916
  Write-Host " logon tasks. Use after Windows Update or when only"
1904
1917
  Write-Host " some services came back online."
1918
+ Write-Host " Requires Administrator (MinionVNC schtask is /RL HIGHEST)."
1905
1919
  }
1906
1920
  }