triflux 3.2.0-dev.9 → 3.3.0-dev.3

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/hub/tools.mjs CHANGED
@@ -8,16 +8,25 @@ import {
8
8
  teamTaskUpdate,
9
9
  teamSendMessage,
10
10
  } from './team/nativeProxy.mjs';
11
+ import {
12
+ ensurePipelineTable,
13
+ createPipeline,
14
+ } from './pipeline/index.mjs';
15
+ import {
16
+ readPipelineState,
17
+ initPipelineState,
18
+ listPipelineStates,
19
+ } from './pipeline/state.mjs';
11
20
 
12
21
  /**
13
22
  * MCP 도구 목록 생성
14
23
  * @param {object} store — createStore() 반환
15
24
  * @param {object} router — createRouter() 반환
16
- * @param {object} hitl — createHitlManager() 반환
17
- * @param {object} pipe — createPipeServer() 반환
18
- * @returns {Array<{name, description, inputSchema, handler}>}
19
- */
20
- export function createTools(store, router, hitl, pipe = null) {
25
+ * @param {object} hitl — createHitlManager() 반환
26
+ * @param {object} pipe — createPipeServer() 반환
27
+ * @returns {Array<{name, description, inputSchema, handler}>}
28
+ */
29
+ export function createTools(store, router, hitl, pipe = null) {
21
30
  /** 도구 핸들러 래퍼 — 에러 처리 + MCP content 형식 변환 */
22
31
  function wrap(code, fn) {
23
32
  return async (args) => {
@@ -49,10 +58,10 @@ export function createTools(store, router, hitl, pipe = null) {
49
58
  heartbeat_ttl_ms: { type: 'integer', minimum: 10000, maximum: 7200000 },
50
59
  },
51
60
  },
52
- handler: wrap('REGISTER_FAILED', (args) => {
53
- const data = router.registerAgent(args);
54
- return { ok: true, data };
55
- }),
61
+ handler: wrap('REGISTER_FAILED', (args) => {
62
+ const data = router.registerAgent(args);
63
+ return { ok: true, data };
64
+ }),
56
65
  },
57
66
 
58
67
  // ── 2. status ──
@@ -122,10 +131,10 @@ export function createTools(store, router, hitl, pipe = null) {
122
131
  }),
123
132
  },
124
133
 
125
- // ── 5. poll_messages ──
126
- {
127
- name: 'poll_messages',
128
- description: 'Deprecated. poll_messages 대신 Named Pipe subscribe/publish 채널을 사용합니다',
134
+ // ── 5. poll_messages ──
135
+ {
136
+ name: 'poll_messages',
137
+ description: 'Deprecated. poll_messages 대신 Named Pipe subscribe/publish 채널을 사용합니다',
129
138
  inputSchema: {
130
139
  type: 'object',
131
140
  required: ['agent_id'],
@@ -137,40 +146,40 @@ export function createTools(store, router, hitl, pipe = null) {
137
146
  ack_ids: { type: 'array', items: { type: 'string' }, maxItems: 100 },
138
147
  auto_ack: { type: 'boolean', default: false },
139
148
  },
140
- },
141
- handler: wrap('POLL_DEPRECATED', async (args) => {
142
- const replay = router.drainAgent(args.agent_id, {
143
- max_messages: args.max_messages,
144
- include_topics: args.include_topics,
145
- auto_ack: args.auto_ack,
146
- });
147
- if (args.ack_ids?.length) {
148
- router.ackMessages(args.ack_ids, args.agent_id);
149
- }
150
- return {
151
- ok: false,
152
- error: {
153
- code: 'POLL_DEPRECATED',
154
- message: 'poll_messages는 deprecated 되었습니다. pipe subscribe/publish 채널을 사용하세요.',
155
- },
156
- data: {
157
- pipe_path: pipe?.path || null,
158
- delivery_mode: 'pipe_push',
159
- protocol: 'ndjson',
160
- replay: {
161
- messages: replay,
162
- count: replay.length,
163
- },
164
- server_time_ms: Date.now(),
165
- },
166
- };
167
- }),
149
+ },
150
+ handler: wrap('POLL_DEPRECATED', async (args) => {
151
+ const replay = router.drainAgent(args.agent_id, {
152
+ max_messages: args.max_messages,
153
+ include_topics: args.include_topics,
154
+ auto_ack: args.auto_ack,
155
+ });
156
+ if (args.ack_ids?.length) {
157
+ router.ackMessages(args.ack_ids, args.agent_id);
158
+ }
159
+ return {
160
+ ok: false,
161
+ error: {
162
+ code: 'POLL_DEPRECATED',
163
+ message: 'poll_messages는 deprecated 되었습니다. pipe subscribe/publish 채널을 사용하세요.',
164
+ },
165
+ data: {
166
+ pipe_path: pipe?.path || null,
167
+ delivery_mode: 'pipe_push',
168
+ protocol: 'ndjson',
169
+ replay: {
170
+ messages: replay,
171
+ count: replay.length,
172
+ },
173
+ server_time_ms: Date.now(),
174
+ },
175
+ };
176
+ }),
168
177
  },
169
178
 
170
179
  // ── 6. handoff ──
171
- {
172
- name: 'handoff',
173
- description: '다른 에이전트에게 작업을 인계합니다. acceptance_criteria로 완료 기준 지정 가능',
180
+ {
181
+ name: 'handoff',
182
+ description: '다른 에이전트에게 작업을 인계합니다. acceptance_criteria로 완료 기준 지정 가능',
174
183
  inputSchema: {
175
184
  type: 'object',
176
185
  required: ['from', 'to', 'topic', 'task'],
@@ -187,15 +196,90 @@ export function createTools(store, router, hitl, pipe = null) {
187
196
  correlation_id: { type: 'string' },
188
197
  },
189
198
  },
190
- handler: wrap('HANDOFF_FAILED', (args) => {
191
- return router.handleHandoff(args);
192
- }),
193
- },
194
-
195
- // ── 7. request_human_input ──
196
- {
197
- name: 'request_human_input',
198
- description: '사용자에게 입력을 요청합니다 (CAPTCHA, 승인, 자격증명, 선택, 텍스트)',
199
+ handler: wrap('HANDOFF_FAILED', (args) => {
200
+ return router.handleHandoff(args);
201
+ }),
202
+ },
203
+
204
+ // ── 7. assign_async ──
205
+ {
206
+ name: 'assign_async',
207
+ description: 'AWS CAO 스타일 비차단 assign job을 생성하고 워커에게 실시간 전달합니다',
208
+ inputSchema: {
209
+ type: 'object',
210
+ required: ['supervisor_agent', 'worker_agent', 'task'],
211
+ properties: {
212
+ supervisor_agent: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
213
+ worker_agent: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
214
+ topic: { type: 'string', pattern: '^[a-zA-Z0-9._:-]+$', default: 'assign.job' },
215
+ task: { type: 'string', minLength: 1, maxLength: 20000 },
216
+ payload: { type: 'object' },
217
+ priority: { type: 'integer', minimum: 1, maximum: 9, default: 5 },
218
+ ttl_ms: { type: 'integer', minimum: 1000, maximum: 86400000, default: 600000 },
219
+ timeout_ms: { type: 'integer', minimum: 1000, maximum: 86400000, default: 600000 },
220
+ max_retries: { type: 'integer', minimum: 0, maximum: 20, default: 0 },
221
+ trace_id: { type: 'string' },
222
+ correlation_id: { type: 'string' },
223
+ },
224
+ },
225
+ handler: wrap('ASSIGN_ASYNC_FAILED', (args) => {
226
+ return router.assignAsync(args);
227
+ }),
228
+ },
229
+
230
+ // ── 8. assign_result ──
231
+ {
232
+ name: 'assign_result',
233
+ description: 'assign job의 진행/완료 결과를 보고합니다. completed + metadata.result 관례를 지원합니다',
234
+ inputSchema: {
235
+ type: 'object',
236
+ required: ['job_id', 'status'],
237
+ properties: {
238
+ job_id: { type: 'string', minLength: 1, maxLength: 128 },
239
+ worker_agent: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
240
+ status: { type: 'string', enum: ['queued', 'running', 'in_progress', 'completed', 'succeeded', 'success', 'failed', 'error', 'timed_out', 'timeout'] },
241
+ attempt: { type: 'integer', minimum: 1 },
242
+ result: {},
243
+ error: {},
244
+ metadata: { type: 'object' },
245
+ payload: { type: 'object' },
246
+ },
247
+ },
248
+ handler: wrap('ASSIGN_RESULT_FAILED', (args) => {
249
+ return router.reportAssignResult(args);
250
+ }),
251
+ },
252
+
253
+ // ── 9. assign_status ──
254
+ {
255
+ name: 'assign_status',
256
+ description: 'assign job 단건 상태 또는 supervisor/worker/status 기준 목록을 조회합니다',
257
+ inputSchema: {
258
+ type: 'object',
259
+ properties: {
260
+ job_id: { type: 'string', minLength: 1, maxLength: 128 },
261
+ supervisor_agent: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
262
+ worker_agent: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
263
+ status: { type: 'string', enum: ['queued', 'running', 'succeeded', 'failed', 'timed_out'] },
264
+ statuses: {
265
+ type: 'array',
266
+ items: { type: 'string', enum: ['queued', 'running', 'succeeded', 'failed', 'timed_out'] },
267
+ maxItems: 8,
268
+ },
269
+ trace_id: { type: 'string' },
270
+ correlation_id: { type: 'string' },
271
+ limit: { type: 'integer', minimum: 1, maximum: 100, default: 50 },
272
+ },
273
+ },
274
+ handler: wrap('ASSIGN_STATUS_FAILED', (args) => {
275
+ return router.getAssignStatus(args);
276
+ }),
277
+ },
278
+
279
+ // ── 10. request_human_input ──
280
+ {
281
+ name: 'request_human_input',
282
+ description: '사용자에게 입력을 요청합니다 (CAPTCHA, 승인, 자격증명, 선택, 텍스트)',
199
283
  inputSchema: {
200
284
  type: 'object',
201
285
  required: ['requester_agent', 'kind', 'prompt', 'requested_schema', 'deadline_ms', 'default_action'],
@@ -211,12 +295,12 @@ export function createTools(store, router, hitl, pipe = null) {
211
295
  correlation_id: { type: 'string' },
212
296
  },
213
297
  },
214
- handler: wrap('HITL_REQUEST_FAILED', (args) => {
215
- return hitl.requestHumanInput(args);
216
- }),
217
- },
218
-
219
- // ── 8. submit_human_input ──
298
+ handler: wrap('HITL_REQUEST_FAILED', (args) => {
299
+ return hitl.requestHumanInput(args);
300
+ }),
301
+ },
302
+
303
+ // ── 11. submit_human_input ──
220
304
  {
221
305
  name: 'submit_human_input',
222
306
  description: '사용자 입력 요청에 응답합니다 (accept, decline, cancel)',
@@ -235,7 +319,7 @@ export function createTools(store, router, hitl, pipe = null) {
235
319
  }),
236
320
  },
237
321
 
238
- // ── 9. team_info ──
322
+ // ── 12. team_info ──
239
323
  {
240
324
  name: 'team_info',
241
325
  description: 'Claude Native Teams 메타/멤버/경로 정보를 조회합니다',
@@ -253,7 +337,7 @@ export function createTools(store, router, hitl, pipe = null) {
253
337
  }),
254
338
  },
255
339
 
256
- // ── 10. team_task_list ──
340
+ // ── 13. team_task_list ──
257
341
  {
258
342
  name: 'team_task_list',
259
343
  description: 'Claude Native Teams task 목록을 owner/status 조건으로 조회합니다',
@@ -277,7 +361,7 @@ export function createTools(store, router, hitl, pipe = null) {
277
361
  }),
278
362
  },
279
363
 
280
- // ── 11. team_task_update ──
364
+ // ── 14. team_task_update ──
281
365
  {
282
366
  name: 'team_task_update',
283
367
  description: 'Claude Native Teams task를 claim/update 합니다',
@@ -305,7 +389,7 @@ export function createTools(store, router, hitl, pipe = null) {
305
389
  }),
306
390
  },
307
391
 
308
- // ── 12. team_send_message ──
392
+ // ── 15. team_send_message ──
309
393
  {
310
394
  name: 'team_send_message',
311
395
  description: 'Claude Native Teams inbox에 메시지를 append 합니다',
@@ -325,5 +409,81 @@ export function createTools(store, router, hitl, pipe = null) {
325
409
  return teamSendMessage(args);
326
410
  }),
327
411
  },
412
+
413
+ // ── 16. pipeline_state ──
414
+ {
415
+ name: 'pipeline_state',
416
+ description: '파이프라인 상태를 조회합니다 (--thorough 모드)',
417
+ inputSchema: {
418
+ type: 'object',
419
+ required: ['team_name'],
420
+ properties: {
421
+ team_name: { type: 'string', pattern: '^[a-z0-9][a-z0-9-]*$' },
422
+ },
423
+ },
424
+ handler: wrap('PIPELINE_STATE_FAILED', (args) => {
425
+ ensurePipelineTable(store.db);
426
+ const state = readPipelineState(store.db, args.team_name);
427
+ return state
428
+ ? { ok: true, data: state }
429
+ : { ok: false, error: { code: 'PIPELINE_NOT_FOUND', message: `파이프라인 없음: ${args.team_name}` } };
430
+ }),
431
+ },
432
+
433
+ // ── 17. pipeline_advance ──
434
+ {
435
+ name: 'pipeline_advance',
436
+ description: '파이프라인을 다음 단계로 전이합니다 (전이 규칙 + fix loop 바운딩 적용)',
437
+ inputSchema: {
438
+ type: 'object',
439
+ required: ['team_name', 'phase'],
440
+ properties: {
441
+ team_name: { type: 'string', pattern: '^[a-z0-9][a-z0-9-]*$' },
442
+ phase: { type: 'string', enum: ['plan', 'prd', 'exec', 'verify', 'fix', 'complete', 'failed'] },
443
+ },
444
+ },
445
+ handler: wrap('PIPELINE_ADVANCE_FAILED', (args) => {
446
+ ensurePipelineTable(store.db);
447
+ const pipeline = createPipeline(store.db, args.team_name);
448
+ return pipeline.advance(args.phase);
449
+ }),
450
+ },
451
+
452
+ // ── 18. pipeline_init ──
453
+ {
454
+ name: 'pipeline_init',
455
+ description: '새 파이프라인을 초기화합니다 (기존 상태 덮어쓰기)',
456
+ inputSchema: {
457
+ type: 'object',
458
+ required: ['team_name'],
459
+ properties: {
460
+ team_name: { type: 'string', pattern: '^[a-z0-9][a-z0-9-]*$' },
461
+ fix_max: { type: 'integer', minimum: 1, maximum: 20, default: 3 },
462
+ ralph_max: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
463
+ },
464
+ },
465
+ handler: wrap('PIPELINE_INIT_FAILED', (args) => {
466
+ ensurePipelineTable(store.db);
467
+ const state = initPipelineState(store.db, args.team_name, {
468
+ fix_max: args.fix_max,
469
+ ralph_max: args.ralph_max,
470
+ });
471
+ return { ok: true, data: state };
472
+ }),
473
+ },
474
+
475
+ // ── 19. pipeline_list ──
476
+ {
477
+ name: 'pipeline_list',
478
+ description: '활성 파이프라인 목록을 조회합니다',
479
+ inputSchema: {
480
+ type: 'object',
481
+ properties: {},
482
+ },
483
+ handler: wrap('PIPELINE_LIST_FAILED', () => {
484
+ ensurePipelineTable(store.db);
485
+ return { ok: true, data: listPipelineStates(store.db) };
486
+ }),
487
+ },
328
488
  ];
329
489
  }