@geekbeer/minion 2.68.6 → 2.70.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.
- package/core/api.js +28 -29
- package/core/lib/thread-watcher.js +26 -14
- package/core/routes/{help-threads.js → threads.js} +30 -30
- package/docs/api-reference.md +28 -29
- package/linux/server.js +2 -2
- package/package.json +1 -1
- package/win/server.js +2 -2
package/core/api.js
CHANGED
|
@@ -88,75 +88,75 @@ async function sendHeartbeat(data) {
|
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
90
|
* Create a project thread (help or discussion) on HQ.
|
|
91
|
-
* @param {object} data - { project_id, title,
|
|
91
|
+
* @param {object} data - { project_id, title, content, thread_type?, mentions?, context? }
|
|
92
92
|
* @returns {Promise<{ thread: object }>}
|
|
93
93
|
*/
|
|
94
|
-
async function
|
|
95
|
-
return request('/
|
|
94
|
+
async function createThread(data) {
|
|
95
|
+
return request('/threads', {
|
|
96
96
|
method: 'POST',
|
|
97
97
|
body: JSON.stringify(data),
|
|
98
98
|
})
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/**
|
|
102
|
-
* Get open
|
|
102
|
+
* Get open threads in this minion's projects.
|
|
103
103
|
* @returns {Promise<{ threads: object[] }>}
|
|
104
104
|
*/
|
|
105
|
-
async function
|
|
106
|
-
return request('/
|
|
105
|
+
async function getOpenThreads() {
|
|
106
|
+
return request('/threads/open')
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
|
-
* Get a
|
|
110
|
+
* Get a thread by ID with messages.
|
|
111
111
|
* @param {string} threadId
|
|
112
112
|
* @returns {Promise<{ thread: object, messages: object[] }>}
|
|
113
113
|
*/
|
|
114
|
-
async function
|
|
115
|
-
return request(`/
|
|
114
|
+
async function getThread(threadId) {
|
|
115
|
+
return request(`/threads/${threadId}`)
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
|
-
* Post a message to a
|
|
119
|
+
* Post a message to a thread.
|
|
120
120
|
* @param {string} threadId
|
|
121
121
|
* @param {object} data - { content, attachments?, mentions? }
|
|
122
122
|
*/
|
|
123
|
-
async function
|
|
124
|
-
return request(`/
|
|
123
|
+
async function postThreadMessage(threadId, data) {
|
|
124
|
+
return request(`/threads/${threadId}/messages`, {
|
|
125
125
|
method: 'POST',
|
|
126
126
|
body: JSON.stringify(data),
|
|
127
127
|
})
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
/**
|
|
131
|
-
* Resolve a
|
|
131
|
+
* Resolve a thread.
|
|
132
132
|
* @param {string} threadId
|
|
133
133
|
* @param {string} resolution
|
|
134
134
|
*/
|
|
135
|
-
async function
|
|
136
|
-
return request(`/
|
|
135
|
+
async function resolveThread(threadId, resolution) {
|
|
136
|
+
return request(`/threads/${threadId}/resolve`, {
|
|
137
137
|
method: 'PATCH',
|
|
138
138
|
body: JSON.stringify({ resolution }),
|
|
139
139
|
})
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
/**
|
|
143
|
-
* Cancel a
|
|
143
|
+
* Cancel a thread.
|
|
144
144
|
* @param {string} threadId
|
|
145
145
|
* @param {string} [reason]
|
|
146
146
|
*/
|
|
147
|
-
async function
|
|
148
|
-
return request(`/
|
|
147
|
+
async function cancelThread(threadId, reason) {
|
|
148
|
+
return request(`/threads/${threadId}/cancel`, {
|
|
149
149
|
method: 'PATCH',
|
|
150
150
|
body: JSON.stringify({ reason: reason || null }),
|
|
151
151
|
})
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
/**
|
|
155
|
-
* Permanently delete a
|
|
155
|
+
* Permanently delete a thread (PM only).
|
|
156
156
|
* @param {string} threadId
|
|
157
157
|
*/
|
|
158
|
-
async function
|
|
159
|
-
return request(`/
|
|
158
|
+
async function deleteThread(threadId) {
|
|
159
|
+
return request(`/threads/${threadId}`, {
|
|
160
160
|
method: 'DELETE',
|
|
161
161
|
})
|
|
162
162
|
}
|
|
@@ -192,14 +192,13 @@ module.exports = {
|
|
|
192
192
|
reportStepComplete,
|
|
193
193
|
reportIssue,
|
|
194
194
|
sendHeartbeat,
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
deleteHelpThread,
|
|
195
|
+
createThread,
|
|
196
|
+
getOpenThreads,
|
|
197
|
+
getThread,
|
|
198
|
+
postThreadMessage,
|
|
199
|
+
resolveThread,
|
|
200
|
+
cancelThread,
|
|
201
|
+
deleteThread,
|
|
203
202
|
createProjectMemory,
|
|
204
203
|
searchProjectMemories,
|
|
205
204
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* 3. Rate limiting: max 1 LLM evaluation per thread per 5 minutes
|
|
11
11
|
*
|
|
12
12
|
* Flow per poll cycle:
|
|
13
|
-
* 1. GET /api/minion/
|
|
13
|
+
* 1. GET /api/minion/threads/open → list of open threads
|
|
14
14
|
* 2. For each thread with new activity:
|
|
15
15
|
* a. Check mentions → if mentioned, must evaluate
|
|
16
16
|
* b. Check read state → skip if no new messages
|
|
@@ -98,7 +98,7 @@ async function pollOnce() {
|
|
|
98
98
|
|
|
99
99
|
polling = true
|
|
100
100
|
try {
|
|
101
|
-
const data = await api.request('/
|
|
101
|
+
const data = await api.request('/threads/open')
|
|
102
102
|
|
|
103
103
|
if (!data.threads || data.threads.length === 0) {
|
|
104
104
|
return
|
|
@@ -139,7 +139,7 @@ async function processThread(thread, myProjects, now) {
|
|
|
139
139
|
// Fetch thread detail to get message count and check for new messages
|
|
140
140
|
let detail
|
|
141
141
|
try {
|
|
142
|
-
detail = await api.request(`/
|
|
142
|
+
detail = await api.request(`/threads/${thread.id}`)
|
|
143
143
|
} catch {
|
|
144
144
|
return
|
|
145
145
|
}
|
|
@@ -155,6 +155,13 @@ async function processThread(thread, myProjects, now) {
|
|
|
155
155
|
// New messages detected
|
|
156
156
|
const newMessages = messages.slice(state.lastMessageCount)
|
|
157
157
|
|
|
158
|
+
// Skip if all new messages are from self (avoid re-triggering on own posts)
|
|
159
|
+
const newFromOthers = newMessages.filter(m => m.sender_minion_id !== config.MINION_ID)
|
|
160
|
+
if (newFromOthers.length === 0) {
|
|
161
|
+
readState.set(thread.id, { ...state, lastMessageCount: messageCount })
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
|
|
158
165
|
// Update read state (message count only — eval timestamp updated after LLM call)
|
|
159
166
|
readState.set(thread.id, { ...state, lastMessageCount: messageCount })
|
|
160
167
|
|
|
@@ -194,9 +201,9 @@ async function evaluateWithLlm(threadSummary, threadDetail, allMessages, newMess
|
|
|
194
201
|
.join('\n')
|
|
195
202
|
|
|
196
203
|
const threadType = threadDetail.thread_type || 'help'
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
204
|
+
const isRequester = threadDetail.requester_minion_id === config.MINION_ID
|
|
205
|
+
const mentions = threadDetail.mentions || []
|
|
206
|
+
const mentionsSummary = mentions.length > 0 ? mentions.join(', ') : 'なし(全員に向けた投稿)'
|
|
200
207
|
|
|
201
208
|
// Extract optional context metadata
|
|
202
209
|
const ctx = threadDetail.context || {}
|
|
@@ -205,13 +212,14 @@ async function evaluateWithLlm(threadSummary, threadDetail, allMessages, newMess
|
|
|
205
212
|
ctx.attempted_resolution ? `試行済み: ${ctx.attempted_resolution}` : '',
|
|
206
213
|
].filter(Boolean).join('\n')
|
|
207
214
|
|
|
208
|
-
const prompt = `あなたはプロジェクト「${myProject.name}」のチームメンバー(ロール: ${myProject.role})です。
|
|
215
|
+
const prompt = `あなたはプロジェクト「${myProject.name}」のチームメンバー(ロール: ${myProject.role}、ID: ${config.MINION_ID})です。
|
|
209
216
|
以下のスレッドに対して、あなたが返信すべきかどうかを判断し、返信する場合はその内容を生成してください。
|
|
210
217
|
|
|
211
218
|
スレッドタイプ: ${threadType}
|
|
212
219
|
タイトル: ${threadDetail.title}
|
|
213
|
-
|
|
214
|
-
${
|
|
220
|
+
起票者: ${isRequester ? 'あなた自身' : `他のミニオン(${threadDetail.requester_minion_id?.slice(0, 8)})`}
|
|
221
|
+
メンション対象: ${mentionsSummary}
|
|
222
|
+
${contextInfo}
|
|
215
223
|
|
|
216
224
|
--- メッセージ履歴 ---
|
|
217
225
|
${messageHistory || '(メッセージなし)'}
|
|
@@ -225,11 +233,15 @@ ${messageHistory || '(メッセージなし)'}
|
|
|
225
233
|
}
|
|
226
234
|
|
|
227
235
|
判断基準:
|
|
236
|
+
- 自分が起票したスレッドの場合、他のメンバーの回答を待つべき(追加情報がある場合を除く)
|
|
237
|
+
- メンション対象が特定のロールやミニオンに限定されている場合、自分が対象でなければ静観する
|
|
228
238
|
- 自分のロール(${myProject.role})に関連する話題か
|
|
229
239
|
- 自分が貢献できる知見や意見があるか
|
|
230
240
|
- 既に十分な回答がある場合は重複を避ける
|
|
231
|
-
-
|
|
232
|
-
|
|
241
|
+
- 人間に聞くべき場合は @user メンションを含めて返信する
|
|
242
|
+
|
|
243
|
+
フォーマットルール:
|
|
244
|
+
- 他のミニオンに言及する場合は必ず Minion(IDの先頭8文字) の形式を使うこと(例: メッセージ履歴中の Minion(abc12345) をそのまま使用)`
|
|
233
245
|
|
|
234
246
|
try {
|
|
235
247
|
const result = await llmCallFn(prompt)
|
|
@@ -251,7 +263,7 @@ ${messageHistory || '(メッセージなし)'}
|
|
|
251
263
|
mentions.push('user')
|
|
252
264
|
}
|
|
253
265
|
|
|
254
|
-
await api.request(`/
|
|
266
|
+
await api.request(`/threads/${threadSummary.id}/messages`, {
|
|
255
267
|
method: 'POST',
|
|
256
268
|
body: JSON.stringify({
|
|
257
269
|
content: parsed.response,
|
|
@@ -281,7 +293,7 @@ async function fallbackMemoryMatch(thread) {
|
|
|
281
293
|
|
|
282
294
|
if (!memData.memories || memData.memories.length === 0) return
|
|
283
295
|
|
|
284
|
-
const keywords =
|
|
296
|
+
const keywords = thread.title
|
|
285
297
|
.toLowerCase()
|
|
286
298
|
.split(/[\s,.\-_/]+/)
|
|
287
299
|
.filter(w => w.length >= 3)
|
|
@@ -301,7 +313,7 @@ async function fallbackMemoryMatch(thread) {
|
|
|
301
313
|
.map(m => `[${m.title}] ${m.content}`)
|
|
302
314
|
.join('\n\n')
|
|
303
315
|
|
|
304
|
-
await api.request(`/
|
|
316
|
+
await api.request(`/threads/${thread.id}/messages`, {
|
|
305
317
|
method: 'POST',
|
|
306
318
|
body: JSON.stringify({
|
|
307
319
|
content: `関連するプロジェクトメモリーが見つかりました:\n\n${knowledgeSummary}`,
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
* Project thread routes (local API on minion)
|
|
3
3
|
*
|
|
4
4
|
* Endpoints:
|
|
5
|
-
* - GET /api/
|
|
6
|
-
* - POST /api/
|
|
7
|
-
* - GET /api/
|
|
8
|
-
* - POST /api/
|
|
9
|
-
* - POST /api/
|
|
10
|
-
* - POST /api/
|
|
11
|
-
* - DELETE /api/
|
|
5
|
+
* - GET /api/threads - List open threads in minion's projects
|
|
6
|
+
* - POST /api/threads - Create a new thread (proxied to HQ)
|
|
7
|
+
* - GET /api/threads/:id - Get thread detail with messages
|
|
8
|
+
* - POST /api/threads/:id/messages - Post a message to a thread
|
|
9
|
+
* - POST /api/threads/:id/resolve - Resolve a thread
|
|
10
|
+
* - POST /api/threads/:id/cancel - Cancel a thread
|
|
11
|
+
* - DELETE /api/threads/:id - Permanently delete a thread (PM only)
|
|
12
12
|
* - GET /api/project-memories - Search project memories
|
|
13
13
|
* - POST /api/project-memories - Create a project memory
|
|
14
14
|
*/
|
|
@@ -19,77 +19,77 @@ const api = require('../api')
|
|
|
19
19
|
/**
|
|
20
20
|
* @param {import('fastify').FastifyInstance} fastify
|
|
21
21
|
*/
|
|
22
|
-
async function
|
|
22
|
+
async function threadRoutes(fastify) {
|
|
23
23
|
// List open threads
|
|
24
|
-
fastify.get('/api/
|
|
24
|
+
fastify.get('/api/threads', async (request, reply) => {
|
|
25
25
|
if (!verifyToken(request)) {
|
|
26
26
|
reply.code(401)
|
|
27
27
|
return { success: false, error: 'Unauthorized' }
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
try {
|
|
31
|
-
const data = await api.
|
|
31
|
+
const data = await api.getOpenThreads()
|
|
32
32
|
return { success: true, threads: data.threads || [] }
|
|
33
33
|
} catch (error) {
|
|
34
|
-
console.error(`[
|
|
34
|
+
console.error(`[Threads] List error: ${error.message}`)
|
|
35
35
|
reply.code(500)
|
|
36
36
|
return { success: false, error: error.message }
|
|
37
37
|
}
|
|
38
38
|
})
|
|
39
39
|
|
|
40
40
|
// Create thread
|
|
41
|
-
fastify.post('/api/
|
|
41
|
+
fastify.post('/api/threads', async (request, reply) => {
|
|
42
42
|
if (!verifyToken(request)) {
|
|
43
43
|
reply.code(401)
|
|
44
44
|
return { success: false, error: 'Unauthorized' }
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
try {
|
|
48
|
-
const data = await api.
|
|
48
|
+
const data = await api.createThread(request.body)
|
|
49
49
|
return { success: true, thread: data.thread }
|
|
50
50
|
} catch (error) {
|
|
51
|
-
console.error(`[
|
|
51
|
+
console.error(`[Threads] Create error: ${error.message}`)
|
|
52
52
|
reply.code(error.statusCode || 500)
|
|
53
53
|
return { success: false, error: error.message }
|
|
54
54
|
}
|
|
55
55
|
})
|
|
56
56
|
|
|
57
57
|
// Get thread detail
|
|
58
|
-
fastify.get('/api/
|
|
58
|
+
fastify.get('/api/threads/:id', async (request, reply) => {
|
|
59
59
|
if (!verifyToken(request)) {
|
|
60
60
|
reply.code(401)
|
|
61
61
|
return { success: false, error: 'Unauthorized' }
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
try {
|
|
65
|
-
const data = await api.
|
|
65
|
+
const data = await api.getThread(request.params.id)
|
|
66
66
|
return { success: true, thread: data.thread, messages: data.messages }
|
|
67
67
|
} catch (error) {
|
|
68
|
-
console.error(`[
|
|
68
|
+
console.error(`[Threads] Get error: ${error.message}`)
|
|
69
69
|
reply.code(error.statusCode || 500)
|
|
70
70
|
return { success: false, error: error.message }
|
|
71
71
|
}
|
|
72
72
|
})
|
|
73
73
|
|
|
74
74
|
// Post message
|
|
75
|
-
fastify.post('/api/
|
|
75
|
+
fastify.post('/api/threads/:id/messages', async (request, reply) => {
|
|
76
76
|
if (!verifyToken(request)) {
|
|
77
77
|
reply.code(401)
|
|
78
78
|
return { success: false, error: 'Unauthorized' }
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
try {
|
|
82
|
-
const data = await api.
|
|
82
|
+
const data = await api.postThreadMessage(request.params.id, request.body)
|
|
83
83
|
return { success: true, message: data.message }
|
|
84
84
|
} catch (error) {
|
|
85
|
-
console.error(`[
|
|
85
|
+
console.error(`[Threads] Message error: ${error.message}`)
|
|
86
86
|
reply.code(error.statusCode || 500)
|
|
87
87
|
return { success: false, error: error.message }
|
|
88
88
|
}
|
|
89
89
|
})
|
|
90
90
|
|
|
91
91
|
// Resolve thread
|
|
92
|
-
fastify.post('/api/
|
|
92
|
+
fastify.post('/api/threads/:id/resolve', async (request, reply) => {
|
|
93
93
|
if (!verifyToken(request)) {
|
|
94
94
|
reply.code(401)
|
|
95
95
|
return { success: false, error: 'Unauthorized' }
|
|
@@ -97,34 +97,34 @@ async function helpThreadRoutes(fastify) {
|
|
|
97
97
|
|
|
98
98
|
try {
|
|
99
99
|
const { resolution } = request.body || {}
|
|
100
|
-
const data = await api.
|
|
100
|
+
const data = await api.resolveThread(request.params.id, resolution)
|
|
101
101
|
return { success: true, thread: data.thread }
|
|
102
102
|
} catch (error) {
|
|
103
|
-
console.error(`[
|
|
103
|
+
console.error(`[Threads] Resolve error: ${error.message}`)
|
|
104
104
|
reply.code(error.statusCode || 500)
|
|
105
105
|
return { success: false, error: error.message }
|
|
106
106
|
}
|
|
107
107
|
})
|
|
108
108
|
|
|
109
109
|
// Permanently delete thread (PM only)
|
|
110
|
-
fastify.delete('/api/
|
|
110
|
+
fastify.delete('/api/threads/:id', async (request, reply) => {
|
|
111
111
|
if (!verifyToken(request)) {
|
|
112
112
|
reply.code(401)
|
|
113
113
|
return { success: false, error: 'Unauthorized' }
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
try {
|
|
117
|
-
const data = await api.
|
|
117
|
+
const data = await api.deleteThread(request.params.id)
|
|
118
118
|
return { success: true, deleted: data.deleted }
|
|
119
119
|
} catch (error) {
|
|
120
|
-
console.error(`[
|
|
120
|
+
console.error(`[Threads] Delete error: ${error.message}`)
|
|
121
121
|
reply.code(error.statusCode || 500)
|
|
122
122
|
return { success: false, error: error.message }
|
|
123
123
|
}
|
|
124
124
|
})
|
|
125
125
|
|
|
126
126
|
// Cancel thread
|
|
127
|
-
fastify.post('/api/
|
|
127
|
+
fastify.post('/api/threads/:id/cancel', async (request, reply) => {
|
|
128
128
|
if (!verifyToken(request)) {
|
|
129
129
|
reply.code(401)
|
|
130
130
|
return { success: false, error: 'Unauthorized' }
|
|
@@ -132,10 +132,10 @@ async function helpThreadRoutes(fastify) {
|
|
|
132
132
|
|
|
133
133
|
try {
|
|
134
134
|
const { reason } = request.body || {}
|
|
135
|
-
const data = await api.
|
|
135
|
+
const data = await api.cancelThread(request.params.id, reason)
|
|
136
136
|
return { success: true, thread: data.thread }
|
|
137
137
|
} catch (error) {
|
|
138
|
-
console.error(`[
|
|
138
|
+
console.error(`[Threads] Cancel error: ${error.message}`)
|
|
139
139
|
reply.code(error.statusCode || 500)
|
|
140
140
|
return { success: false, error: error.message }
|
|
141
141
|
}
|
|
@@ -186,4 +186,4 @@ async function helpThreadRoutes(fastify) {
|
|
|
186
186
|
})
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
module.exports = {
|
|
189
|
+
module.exports = { threadRoutes }
|
package/docs/api-reference.md
CHANGED
|
@@ -281,21 +281,21 @@ GET `/api/daemons/status` response:
|
|
|
281
281
|
|
|
282
282
|
| Method | Endpoint | Description |
|
|
283
283
|
|--------|----------|-------------|
|
|
284
|
-
| GET | `/api/
|
|
285
|
-
| POST | `/api/
|
|
286
|
-
| GET | `/api/
|
|
287
|
-
| POST | `/api/
|
|
288
|
-
| POST | `/api/
|
|
289
|
-
| POST | `/api/
|
|
290
|
-
| DELETE | `/api/
|
|
291
|
-
|
|
292
|
-
POST `/api/
|
|
284
|
+
| GET | `/api/threads` | 参加プロジェクトのオープンスレッド一覧 |
|
|
285
|
+
| POST | `/api/threads` | スレッドを起票(初回メッセージを同時作成) |
|
|
286
|
+
| GET | `/api/threads/:id` | スレッド詳細 + メッセージ一覧 |
|
|
287
|
+
| POST | `/api/threads/:id/messages` | スレッドにメッセージを投稿 |
|
|
288
|
+
| POST | `/api/threads/:id/resolve` | スレッドを解決済みにする |
|
|
289
|
+
| POST | `/api/threads/:id/cancel` | スレッドをキャンセル |
|
|
290
|
+
| DELETE | `/api/threads/:id` | スレッドを完全削除(PMのみ) |
|
|
291
|
+
|
|
292
|
+
POST `/api/threads` body (ヘルプスレッド起票):
|
|
293
293
|
```json
|
|
294
294
|
{
|
|
295
295
|
"project_id": "uuid",
|
|
296
296
|
"thread_type": "help",
|
|
297
297
|
"title": "ランサーズの2FA認証コードが必要",
|
|
298
|
-
"
|
|
298
|
+
"content": "ログイン時に2段階認証を要求された。メールで届く6桁コードの入力が必要。",
|
|
299
299
|
"mentions": ["role:pm"],
|
|
300
300
|
"context": {
|
|
301
301
|
"category": "auth",
|
|
@@ -304,13 +304,13 @@ POST `/api/help-threads` body (ヘルプスレッド起票):
|
|
|
304
304
|
}
|
|
305
305
|
```
|
|
306
306
|
|
|
307
|
-
POST `/api/
|
|
307
|
+
POST `/api/threads` body (ディスカッションスレッド起票):
|
|
308
308
|
```json
|
|
309
309
|
{
|
|
310
310
|
"project_id": "uuid",
|
|
311
311
|
"thread_type": "discussion",
|
|
312
312
|
"title": "デプロイ手順の確認",
|
|
313
|
-
"
|
|
313
|
+
"content": "本番デプロイ前にステージングで確認するフローに変えたい。意見ある?",
|
|
314
314
|
"mentions": ["role:engineer"]
|
|
315
315
|
}
|
|
316
316
|
```
|
|
@@ -320,7 +320,7 @@ POST `/api/help-threads` body (ディスカッションスレッド起票):
|
|
|
320
320
|
| `project_id` | string | Yes | プロジェクト UUID |
|
|
321
321
|
| `thread_type` | string | No | `help`(デフォルト)or `discussion` |
|
|
322
322
|
| `title` | string | Yes | スレッドの要約 |
|
|
323
|
-
| `
|
|
323
|
+
| `content` | string | Yes | スレッド本文(thread_messagesの最初のメッセージとして保存) |
|
|
324
324
|
| `mentions` | string[] | No | メンション対象。形式: `role:engineer`, `role:pm`, `minion:<minion_id>`, `user` |
|
|
325
325
|
| `context` | object | No | 任意のメタデータ(category, urgency, workflow_execution_id等) |
|
|
326
326
|
|
|
@@ -334,7 +334,7 @@ POST `/api/help-threads` body (ディスカッションスレッド起票):
|
|
|
334
334
|
- トークン消費を抑えるため、当事者が明確な場合はメンションを推奨
|
|
335
335
|
- ミニオンが自力で解決できない場合、`@user` メンション付きで返信して人間に助けを求める
|
|
336
336
|
|
|
337
|
-
POST `/api/
|
|
337
|
+
POST `/api/threads/:id/messages` body:
|
|
338
338
|
```json
|
|
339
339
|
{
|
|
340
340
|
"content": "関連するプロジェクトメモリーが見つかりました: ...",
|
|
@@ -348,14 +348,14 @@ POST `/api/help-threads/:id/messages` body:
|
|
|
348
348
|
| `attachments` | object | No | 添付ファイル情報(JSON) |
|
|
349
349
|
| `mentions` | string[] | No | メンション対象 |
|
|
350
350
|
|
|
351
|
-
POST `/api/
|
|
351
|
+
POST `/api/threads/:id/resolve` body:
|
|
352
352
|
```json
|
|
353
353
|
{
|
|
354
354
|
"resolution": "ユーザーから2FAコード(123456)を受け取りログイン成功"
|
|
355
355
|
}
|
|
356
356
|
```
|
|
357
357
|
|
|
358
|
-
POST `/api/
|
|
358
|
+
POST `/api/threads/:id/cancel` body (optional):
|
|
359
359
|
```json
|
|
360
360
|
{
|
|
361
361
|
"reason": "誤って起票したため取り消し"
|
|
@@ -409,7 +409,7 @@ POST `/api/project-memories` body:
|
|
|
409
409
|
**推奨ワークフロー:**
|
|
410
410
|
1. ブロッカー発生 → `GET /api/project-memories?project_id=...&category=auth&search=2fa` で既知の知見を検索
|
|
411
411
|
2. 該当あり → 知見に基づいて自己解決 or 即エスカレーション
|
|
412
|
-
3. 該当なし → `POST /api/
|
|
412
|
+
3. 該当なし → `POST /api/threads` でブロッカー起票
|
|
413
413
|
4. 解決後 → `POST /api/project-memories` で知見を蓄積
|
|
414
414
|
|
|
415
415
|
### Commands
|
|
@@ -770,21 +770,20 @@ Response:
|
|
|
770
770
|
|
|
771
771
|
スキルはバージョン管理される。push ごとに新バージョンが作成され、ファイルは Supabase Storage に保存される。
|
|
772
772
|
|
|
773
|
-
###
|
|
773
|
+
### Project Threads (HQ)
|
|
774
774
|
|
|
775
775
|
| Method | Endpoint | Description |
|
|
776
776
|
|--------|----------|-------------|
|
|
777
|
-
| POST | `/api/minion/
|
|
778
|
-
| GET | `/api/minion/
|
|
779
|
-
| GET | `/api/minion/
|
|
780
|
-
| POST | `/api/minion/
|
|
781
|
-
| PATCH | `/api/minion/
|
|
782
|
-
| PATCH | `/api/minion/
|
|
783
|
-
| DELETE | `/api/minion/
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
詳細なリクエスト/レスポンス仕様はローカル API セクションの「Help Threads」を参照。
|
|
777
|
+
| POST | `/api/minion/threads` | スレッドを起票(初回メッセージを同時作成) |
|
|
778
|
+
| GET | `/api/minion/threads/open` | 参加プロジェクトの未解決スレッド一覧 |
|
|
779
|
+
| GET | `/api/minion/threads/:id` | スレッド詳細 + メッセージ一覧 |
|
|
780
|
+
| POST | `/api/minion/threads/:id/messages` | スレッドにメッセージを投稿 |
|
|
781
|
+
| PATCH | `/api/minion/threads/:id/resolve` | スレッドを解決済みにする。Body: `{resolution}` |
|
|
782
|
+
| PATCH | `/api/minion/threads/:id/cancel` | スレッドをキャンセル。Body: `{reason?}` |
|
|
783
|
+
| DELETE | `/api/minion/threads/:id` | スレッドを完全削除(PMのみ)。メッセージもCASCADE削除 |
|
|
784
|
+
|
|
785
|
+
ローカルエージェントの `/api/threads` は上記 HQ API へのプロキシ。
|
|
786
|
+
詳細なリクエスト/レスポンス仕様はローカル API セクションの「Project Threads」を参照。
|
|
788
787
|
|
|
789
788
|
### Project Memories (HQ)
|
|
790
789
|
|
package/linux/server.js
CHANGED
|
@@ -73,7 +73,7 @@ const { memoryRoutes } = require('../core/routes/memory')
|
|
|
73
73
|
const { dailyLogRoutes } = require('../core/routes/daily-logs')
|
|
74
74
|
const { sudoersRoutes } = require('../core/routes/sudoers')
|
|
75
75
|
const { permissionRoutes } = require('../core/routes/permissions')
|
|
76
|
-
const {
|
|
76
|
+
const { threadRoutes } = require('../core/routes/threads')
|
|
77
77
|
const { daemonRoutes } = require('../core/routes/daemons')
|
|
78
78
|
|
|
79
79
|
// Linux-specific routes
|
|
@@ -278,7 +278,7 @@ async function registerAllRoutes(app) {
|
|
|
278
278
|
await app.register(dailyLogRoutes)
|
|
279
279
|
await app.register(sudoersRoutes)
|
|
280
280
|
await app.register(permissionRoutes)
|
|
281
|
-
await app.register(
|
|
281
|
+
await app.register(threadRoutes)
|
|
282
282
|
await app.register(daemonRoutes, { heartbeatStatus: () => ({ running: !!heartbeatTimer, last_beat_at: lastBeatAt }) })
|
|
283
283
|
|
|
284
284
|
// Linux-specific routes
|
package/package.json
CHANGED
package/win/server.js
CHANGED
|
@@ -57,7 +57,7 @@ const { variableRoutes } = require('../core/routes/variables')
|
|
|
57
57
|
const { memoryRoutes } = require('../core/routes/memory')
|
|
58
58
|
const { dailyLogRoutes } = require('../core/routes/daily-logs')
|
|
59
59
|
const { permissionRoutes } = require('../core/routes/permissions')
|
|
60
|
-
const {
|
|
60
|
+
const { threadRoutes } = require('../core/routes/threads')
|
|
61
61
|
const { daemonRoutes } = require('../core/routes/daemons')
|
|
62
62
|
|
|
63
63
|
// Validate configuration
|
|
@@ -212,7 +212,7 @@ async function registerRoutes(app) {
|
|
|
212
212
|
await app.register(memoryRoutes)
|
|
213
213
|
await app.register(dailyLogRoutes)
|
|
214
214
|
await app.register(permissionRoutes)
|
|
215
|
-
await app.register(
|
|
215
|
+
await app.register(threadRoutes)
|
|
216
216
|
await app.register(daemonRoutes, { heartbeatStatus: () => ({ running: !!heartbeatTimer, last_beat_at: lastBeatAt }) })
|
|
217
217
|
|
|
218
218
|
// Shutdown endpoint — allows detached restart/update scripts to trigger
|