triflux 10.2.0 → 10.3.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/hub/tools.mjs CHANGED
@@ -2,22 +2,20 @@
2
2
  // register/status/publish/ask/poll/handoff/HITL + team proxy
3
3
  // 모든 도구 응답: { ok: boolean, error?: { code, message }, data?: ... }
4
4
 
5
- import { getTeamBridge } from './team-bridge.mjs';
5
+ import { createPipeline, ensurePipelineTable } from "./pipeline/index.mjs";
6
6
  import {
7
- ensurePipelineTable,
8
- createPipeline,
9
- } from './pipeline/index.mjs';
10
- import {
11
- readPipelineState,
12
7
  initPipelineState,
13
8
  listPipelineStates,
14
- } from './pipeline/state.mjs';
9
+ readPipelineState,
10
+ } from "./pipeline/state.mjs";
11
+ import { sendInputToConductorSession } from "./team/conductor-registry.mjs";
12
+ import { getTeamBridge } from "./team-bridge.mjs";
15
13
 
16
- const TEAM_BRIDGE_NOT_REGISTERED = 'bridge_not_registered';
14
+ const TEAM_BRIDGE_NOT_REGISTERED = "bridge_not_registered";
17
15
 
18
16
  function getTeamBridgeMethod(methodName) {
19
17
  const method = getTeamBridge()?.[methodName];
20
- return typeof method === 'function' ? method : null;
18
+ return typeof method === "function" ? method : null;
21
19
  }
22
20
 
23
21
  function teamInfoFallback(args = {}) {
@@ -86,7 +84,7 @@ function teamSendMessageFallback(args = {}) {
86
84
  ok: true,
87
85
  data: {
88
86
  message_id: null,
89
- recipient: args.to ?? 'team-lead',
87
+ recipient: args.to ?? "team-lead",
90
88
  inbox_file: null,
91
89
  queued_at: null,
92
90
  unread_count: 0,
@@ -110,10 +108,13 @@ export function createTools(store, router, hitl, pipe = null) {
110
108
  return async (args) => {
111
109
  try {
112
110
  const result = await fn(args);
113
- return { content: [{ type: 'text', text: JSON.stringify(result) }] };
111
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
114
112
  } catch (e) {
115
113
  const err = { ok: false, error: { code, message: e.message } };
116
- return { content: [{ type: 'text', text: JSON.stringify(err) }], isError: true };
114
+ return {
115
+ content: [{ type: "text", text: JSON.stringify(err) }],
116
+ isError: true,
117
+ };
117
118
  }
118
119
  };
119
120
  }
@@ -121,22 +122,37 @@ export function createTools(store, router, hitl, pipe = null) {
121
122
  return [
122
123
  // ── 1. register ──
123
124
  {
124
- name: 'register',
125
- description: '에이전트를 허브에 등록하고 lease를 발급받습니다',
125
+ name: "register",
126
+ description: "에이전트를 허브에 등록하고 lease를 발급받습니다",
126
127
  inputSchema: {
127
- type: 'object',
128
- required: ['agent_id', 'cli', 'capabilities', 'topics', 'heartbeat_ttl_ms'],
128
+ type: "object",
129
+ required: [
130
+ "agent_id",
131
+ "cli",
132
+ "capabilities",
133
+ "topics",
134
+ "heartbeat_ttl_ms",
135
+ ],
129
136
  properties: {
130
- agent_id: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
131
- cli: { type: 'string', enum: ['codex', 'gemini', 'claude', 'other'] },
132
- pid: { type: 'integer', minimum: 1 },
133
- capabilities: { type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 64 },
134
- topics: { type: 'array', items: { type: 'string' }, maxItems: 64 },
135
- metadata: { type: 'object' },
136
- heartbeat_ttl_ms: { type: 'integer', minimum: 10000, maximum: 7200000 },
137
+ agent_id: { type: "string", pattern: "^[a-zA-Z0-9._:-]{3,64}$" },
138
+ cli: { type: "string", enum: ["codex", "gemini", "claude", "other"] },
139
+ pid: { type: "integer", minimum: 1 },
140
+ capabilities: {
141
+ type: "array",
142
+ items: { type: "string" },
143
+ minItems: 1,
144
+ maxItems: 64,
145
+ },
146
+ topics: { type: "array", items: { type: "string" }, maxItems: 64 },
147
+ metadata: { type: "object" },
148
+ heartbeat_ttl_ms: {
149
+ type: "integer",
150
+ minimum: 10000,
151
+ maximum: 7200000,
152
+ },
137
153
  },
138
154
  },
139
- handler: wrap('REGISTER_FAILED', (args) => {
155
+ handler: wrap("REGISTER_FAILED", (args) => {
140
156
  const data = router.registerAgent(args);
141
157
  return { ok: true, data };
142
158
  }),
@@ -144,88 +160,128 @@ export function createTools(store, router, hitl, pipe = null) {
144
160
 
145
161
  // ── 2. status ──
146
162
  {
147
- name: 'status',
148
- description: '허브, 에이전트, 큐, 트레이스 상태를 조회합니다',
163
+ name: "status",
164
+ description: "허브, 에이전트, 큐, 트레이스 상태를 조회합니다",
149
165
  inputSchema: {
150
- type: 'object',
166
+ type: "object",
151
167
  properties: {
152
- scope: { type: 'string', enum: ['hub', 'agent', 'queue', 'trace'], default: 'hub' },
153
- agent_id: { type: 'string' },
154
- trace_id: { type: 'string' },
155
- include_metrics: { type: 'boolean', default: true },
168
+ scope: {
169
+ type: "string",
170
+ enum: ["hub", "agent", "queue", "trace"],
171
+ default: "hub",
172
+ },
173
+ agent_id: { type: "string" },
174
+ trace_id: { type: "string" },
175
+ include_metrics: { type: "boolean", default: true },
156
176
  },
157
177
  },
158
- handler: wrap('STATUS_FAILED', (args) => {
159
- return router.getStatus(args.scope || 'hub', args);
178
+ handler: wrap("STATUS_FAILED", (args) => {
179
+ return router.getStatus(args.scope || "hub", args);
160
180
  }),
161
181
  },
162
182
 
163
183
  // ── 3. publish ──
164
184
  {
165
- name: 'publish',
166
- description: '이벤트 또는 응답 메시지를 발행합니다. to에 "topic:XXX" 지정 시 구독자 전체 fanout',
185
+ name: "publish",
186
+ description:
187
+ '이벤트 또는 응답 메시지를 발행합니다. to에 "topic:XXX" 지정 시 구독자 전체 fanout',
167
188
  inputSchema: {
168
- type: 'object',
169
- required: ['from', 'to', 'topic', 'payload'],
189
+ type: "object",
190
+ required: ["from", "to", "topic", "payload"],
170
191
  properties: {
171
- from: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
172
- to: { type: 'string' },
173
- topic: { type: 'string', pattern: '^[a-zA-Z0-9._:-]+$' },
174
- priority: { type: 'integer', minimum: 1, maximum: 9, default: 5 },
175
- ttl_ms: { type: 'integer', minimum: 1000, maximum: 86400000, default: 300000 },
176
- payload: { type: 'object' },
177
- trace_id: { type: 'string' },
178
- correlation_id: { type: 'string' },
192
+ from: { type: "string", pattern: "^[a-zA-Z0-9._:-]{3,64}$" },
193
+ to: { type: "string" },
194
+ topic: { type: "string", pattern: "^[a-zA-Z0-9._:-]+$" },
195
+ priority: { type: "integer", minimum: 1, maximum: 9, default: 5 },
196
+ ttl_ms: {
197
+ type: "integer",
198
+ minimum: 1000,
199
+ maximum: 86400000,
200
+ default: 300000,
201
+ },
202
+ payload: { type: "object" },
203
+ trace_id: { type: "string" },
204
+ correlation_id: { type: "string" },
179
205
  },
180
206
  },
181
- handler: wrap('PUBLISH_FAILED', (args) => {
207
+ handler: wrap("PUBLISH_FAILED", (args) => {
182
208
  return router.handlePublish(args);
183
209
  }),
184
210
  },
185
211
 
186
212
  // ── 4. ask ──
187
213
  {
188
- name: 'ask',
189
- description: '다른 에이전트에게 질문합니다. await_response_ms > 0이면 짧은 폴링으로 응답 대기',
214
+ name: "ask",
215
+ description:
216
+ "다른 에이전트에게 질문합니다. await_response_ms > 0이면 짧은 폴링으로 응답 대기",
190
217
  inputSchema: {
191
- type: 'object',
192
- required: ['from', 'to', 'topic', 'question'],
218
+ type: "object",
219
+ required: ["from", "to", "topic", "question"],
193
220
  properties: {
194
- from: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
195
- to: { type: 'string' },
196
- topic: { type: 'string', pattern: '^[a-zA-Z0-9._:-]+$' },
197
- question: { type: 'string', minLength: 1, maxLength: 20000 },
198
- context_refs: { type: 'array', items: { type: 'string' }, maxItems: 32 },
199
- payload: { type: 'object' },
200
- priority: { type: 'integer', minimum: 1, maximum: 9, default: 5 },
201
- ttl_ms: { type: 'integer', minimum: 1000, maximum: 86400000, default: 300000 },
202
- await_response_ms: { type: 'integer', minimum: 0, maximum: 30000, default: 0 },
203
- trace_id: { type: 'string' },
204
- correlation_id: { type: 'string' },
221
+ from: { type: "string", pattern: "^[a-zA-Z0-9._:-]{3,64}$" },
222
+ to: { type: "string" },
223
+ topic: { type: "string", pattern: "^[a-zA-Z0-9._:-]+$" },
224
+ question: { type: "string", minLength: 1, maxLength: 20000 },
225
+ context_refs: {
226
+ type: "array",
227
+ items: { type: "string" },
228
+ maxItems: 32,
229
+ },
230
+ payload: { type: "object" },
231
+ priority: { type: "integer", minimum: 1, maximum: 9, default: 5 },
232
+ ttl_ms: {
233
+ type: "integer",
234
+ minimum: 1000,
235
+ maximum: 86400000,
236
+ default: 300000,
237
+ },
238
+ await_response_ms: {
239
+ type: "integer",
240
+ minimum: 0,
241
+ maximum: 30000,
242
+ default: 0,
243
+ },
244
+ trace_id: { type: "string" },
245
+ correlation_id: { type: "string" },
205
246
  },
206
247
  },
207
- handler: wrap('ASK_FAILED', async (args) => {
248
+ handler: wrap("ASK_FAILED", async (args) => {
208
249
  return await router.handleAsk(args);
209
250
  }),
210
251
  },
211
252
 
212
253
  // ── 5. poll_messages ──
213
254
  {
214
- name: 'poll_messages',
215
- description: 'Deprecated. poll_messages 대신 Named Pipe subscribe/publish 채널을 사용합니다',
255
+ name: "poll_messages",
256
+ description:
257
+ "Deprecated. poll_messages 대신 Named Pipe subscribe/publish 채널을 사용합니다",
216
258
  inputSchema: {
217
- type: 'object',
218
- required: ['agent_id'],
259
+ type: "object",
260
+ required: ["agent_id"],
219
261
  properties: {
220
- agent_id: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
221
- wait_ms: { type: 'integer', minimum: 0, maximum: 30000, default: 1000 },
222
- max_messages: { type: 'integer', minimum: 1, maximum: 100, default: 20 },
223
- include_topics: { type: 'array', items: { type: 'string' }, maxItems: 64 },
224
- ack_ids: { type: 'array', items: { type: 'string' }, maxItems: 100 },
225
- auto_ack: { type: 'boolean', default: false },
262
+ agent_id: { type: "string", pattern: "^[a-zA-Z0-9._:-]{3,64}$" },
263
+ wait_ms: {
264
+ type: "integer",
265
+ minimum: 0,
266
+ maximum: 30000,
267
+ default: 1000,
268
+ },
269
+ max_messages: {
270
+ type: "integer",
271
+ minimum: 1,
272
+ maximum: 100,
273
+ default: 20,
274
+ },
275
+ include_topics: {
276
+ type: "array",
277
+ items: { type: "string" },
278
+ maxItems: 64,
279
+ },
280
+ ack_ids: { type: "array", items: { type: "string" }, maxItems: 100 },
281
+ auto_ack: { type: "boolean", default: false },
226
282
  },
227
283
  },
228
- handler: wrap('POLL_DEPRECATED', async (args) => {
284
+ handler: wrap("POLL_DEPRECATED", async (args) => {
229
285
  const replay = router.drainAgent(args.agent_id, {
230
286
  max_messages: args.max_messages,
231
287
  include_topics: args.include_topics,
@@ -237,13 +293,14 @@ export function createTools(store, router, hitl, pipe = null) {
237
293
  return {
238
294
  ok: false,
239
295
  error: {
240
- code: 'POLL_DEPRECATED',
241
- message: 'poll_messages는 deprecated 되었습니다. pipe subscribe/publish 채널을 사용하세요.',
296
+ code: "POLL_DEPRECATED",
297
+ message:
298
+ "poll_messages는 deprecated 되었습니다. pipe subscribe/publish 채널을 사용하세요.",
242
299
  },
243
300
  data: {
244
301
  pipe_path: pipe?.path || null,
245
- delivery_mode: 'pipe_push',
246
- protocol: 'ndjson',
302
+ delivery_mode: "pipe_push",
303
+ protocol: "ndjson",
247
304
  replay: {
248
305
  messages: replay,
249
306
  count: replay.length,
@@ -256,275 +313,394 @@ export function createTools(store, router, hitl, pipe = null) {
256
313
 
257
314
  // ── 6. handoff ──
258
315
  {
259
- name: 'handoff',
260
- description: '다른 에이전트에게 작업을 인계합니다. acceptance_criteria로 완료 기준 지정 가능',
316
+ name: "handoff",
317
+ description:
318
+ "다른 에이전트에게 작업을 인계합니다. acceptance_criteria로 완료 기준 지정 가능",
261
319
  inputSchema: {
262
- type: 'object',
263
- required: ['from', 'to', 'topic', 'task'],
320
+ type: "object",
321
+ required: ["from", "to", "topic", "task"],
264
322
  properties: {
265
- from: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
266
- to: { type: 'string' },
267
- topic: { type: 'string', pattern: '^[a-zA-Z0-9._:-]+$' },
268
- task: { type: 'string', minLength: 1, maxLength: 20000 },
269
- acceptance_criteria: { type: 'array', items: { type: 'string' }, maxItems: 32 },
270
- context_refs: { type: 'array', items: { type: 'string' }, maxItems: 32 },
271
- priority: { type: 'integer', minimum: 1, maximum: 9, default: 5 },
272
- ttl_ms: { type: 'integer', minimum: 1000, maximum: 86400000, default: 600000 },
273
- trace_id: { type: 'string' },
274
- correlation_id: { type: 'string' },
323
+ from: { type: "string", pattern: "^[a-zA-Z0-9._:-]{3,64}$" },
324
+ to: { type: "string" },
325
+ topic: { type: "string", pattern: "^[a-zA-Z0-9._:-]+$" },
326
+ task: { type: "string", minLength: 1, maxLength: 20000 },
327
+ acceptance_criteria: {
328
+ type: "array",
329
+ items: { type: "string" },
330
+ maxItems: 32,
331
+ },
332
+ context_refs: {
333
+ type: "array",
334
+ items: { type: "string" },
335
+ maxItems: 32,
336
+ },
337
+ priority: { type: "integer", minimum: 1, maximum: 9, default: 5 },
338
+ ttl_ms: {
339
+ type: "integer",
340
+ minimum: 1000,
341
+ maximum: 86400000,
342
+ default: 600000,
343
+ },
344
+ trace_id: { type: "string" },
345
+ correlation_id: { type: "string" },
275
346
  },
276
347
  },
277
- handler: wrap('HANDOFF_FAILED', (args) => {
348
+ handler: wrap("HANDOFF_FAILED", (args) => {
278
349
  return router.handleHandoff(args);
279
350
  }),
280
351
  },
281
352
 
282
353
  // ── 7. assign_async ──
283
354
  {
284
- name: 'assign_async',
285
- description: 'AWS CAO 스타일 비차단 assign job을 생성하고 워커에게 실시간 전달합니다',
355
+ name: "assign_async",
356
+ description:
357
+ "AWS CAO 스타일 비차단 assign job을 생성하고 워커에게 실시간 전달합니다",
286
358
  inputSchema: {
287
- type: 'object',
288
- required: ['supervisor_agent', 'worker_agent', 'task'],
359
+ type: "object",
360
+ required: ["supervisor_agent", "worker_agent", "task"],
289
361
  properties: {
290
- supervisor_agent: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
291
- worker_agent: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
292
- topic: { type: 'string', pattern: '^[a-zA-Z0-9._:-]+$', default: 'assign.job' },
293
- task: { type: 'string', minLength: 1, maxLength: 20000 },
294
- payload: { type: 'object' },
295
- priority: { type: 'integer', minimum: 1, maximum: 9, default: 5 },
296
- ttl_ms: { type: 'integer', minimum: 1000, maximum: 86400000, default: 600000 },
297
- timeout_ms: { type: 'integer', minimum: 1000, maximum: 86400000, default: 600000 },
298
- max_retries: { type: 'integer', minimum: 0, maximum: 20, default: 0 },
299
- trace_id: { type: 'string' },
300
- correlation_id: { type: 'string' },
362
+ supervisor_agent: {
363
+ type: "string",
364
+ pattern: "^[a-zA-Z0-9._:-]{3,64}$",
365
+ },
366
+ worker_agent: { type: "string", pattern: "^[a-zA-Z0-9._:-]{3,64}$" },
367
+ topic: {
368
+ type: "string",
369
+ pattern: "^[a-zA-Z0-9._:-]+$",
370
+ default: "assign.job",
371
+ },
372
+ task: { type: "string", minLength: 1, maxLength: 20000 },
373
+ payload: { type: "object" },
374
+ priority: { type: "integer", minimum: 1, maximum: 9, default: 5 },
375
+ ttl_ms: {
376
+ type: "integer",
377
+ minimum: 1000,
378
+ maximum: 86400000,
379
+ default: 600000,
380
+ },
381
+ timeout_ms: {
382
+ type: "integer",
383
+ minimum: 1000,
384
+ maximum: 86400000,
385
+ default: 600000,
386
+ },
387
+ max_retries: { type: "integer", minimum: 0, maximum: 20, default: 0 },
388
+ trace_id: { type: "string" },
389
+ correlation_id: { type: "string" },
301
390
  },
302
391
  },
303
- handler: wrap('ASSIGN_ASYNC_FAILED', (args) => {
392
+ handler: wrap("ASSIGN_ASYNC_FAILED", (args) => {
304
393
  return router.assignAsync(args);
305
394
  }),
306
395
  },
307
396
 
308
397
  // ── 8. assign_result ──
309
398
  {
310
- name: 'assign_result',
311
- description: 'assign job의 진행/완료 결과를 보고합니다. completed + metadata.result 관례를 지원합니다',
399
+ name: "assign_result",
400
+ description:
401
+ "assign job의 진행/완료 결과를 보고합니다. completed + metadata.result 관례를 지원합니다",
312
402
  inputSchema: {
313
- type: 'object',
314
- required: ['job_id', 'status'],
403
+ type: "object",
404
+ required: ["job_id", "status"],
315
405
  properties: {
316
- job_id: { type: 'string', minLength: 1, maxLength: 128 },
317
- worker_agent: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
318
- status: { type: 'string', enum: ['queued', 'running', 'in_progress', 'completed', 'succeeded', 'success', 'failed', 'error', 'timed_out', 'timeout'] },
319
- attempt: { type: 'integer', minimum: 1 },
406
+ job_id: { type: "string", minLength: 1, maxLength: 128 },
407
+ worker_agent: { type: "string", pattern: "^[a-zA-Z0-9._:-]{3,64}$" },
408
+ status: {
409
+ type: "string",
410
+ enum: [
411
+ "queued",
412
+ "running",
413
+ "in_progress",
414
+ "completed",
415
+ "succeeded",
416
+ "success",
417
+ "failed",
418
+ "error",
419
+ "timed_out",
420
+ "timeout",
421
+ ],
422
+ },
423
+ attempt: { type: "integer", minimum: 1 },
320
424
  result: {},
321
425
  error: {},
322
- metadata: { type: 'object' },
323
- payload: { type: 'object' },
426
+ metadata: { type: "object" },
427
+ payload: { type: "object" },
324
428
  },
325
429
  },
326
- handler: wrap('ASSIGN_RESULT_FAILED', (args) => {
430
+ handler: wrap("ASSIGN_RESULT_FAILED", (args) => {
327
431
  return router.reportAssignResult(args);
328
432
  }),
329
433
  },
330
434
 
331
435
  // ── 9. assign_status ──
332
436
  {
333
- name: 'assign_status',
334
- description: 'assign job 단건 상태 또는 supervisor/worker/status 기준 목록을 조회합니다',
437
+ name: "assign_status",
438
+ description:
439
+ "assign job 단건 상태 또는 supervisor/worker/status 기준 목록을 조회합니다",
335
440
  inputSchema: {
336
- type: 'object',
441
+ type: "object",
337
442
  properties: {
338
- job_id: { type: 'string', minLength: 1, maxLength: 128 },
339
- supervisor_agent: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
340
- worker_agent: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
341
- status: { type: 'string', enum: ['queued', 'running', 'succeeded', 'failed', 'timed_out'] },
443
+ job_id: { type: "string", minLength: 1, maxLength: 128 },
444
+ supervisor_agent: {
445
+ type: "string",
446
+ pattern: "^[a-zA-Z0-9._:-]{3,64}$",
447
+ },
448
+ worker_agent: { type: "string", pattern: "^[a-zA-Z0-9._:-]{3,64}$" },
449
+ status: {
450
+ type: "string",
451
+ enum: ["queued", "running", "succeeded", "failed", "timed_out"],
452
+ },
342
453
  statuses: {
343
- type: 'array',
344
- items: { type: 'string', enum: ['queued', 'running', 'succeeded', 'failed', 'timed_out'] },
454
+ type: "array",
455
+ items: {
456
+ type: "string",
457
+ enum: ["queued", "running", "succeeded", "failed", "timed_out"],
458
+ },
345
459
  maxItems: 8,
346
460
  },
347
- trace_id: { type: 'string' },
348
- correlation_id: { type: 'string' },
349
- limit: { type: 'integer', minimum: 1, maximum: 100, default: 50 },
461
+ trace_id: { type: "string" },
462
+ correlation_id: { type: "string" },
463
+ limit: { type: "integer", minimum: 1, maximum: 100, default: 50 },
350
464
  },
351
465
  },
352
- handler: wrap('ASSIGN_STATUS_FAILED', (args) => {
466
+ handler: wrap("ASSIGN_STATUS_FAILED", (args) => {
353
467
  return router.getAssignStatus(args);
354
468
  }),
355
469
  },
356
470
 
357
471
  // ── 10. request_human_input ──
358
472
  {
359
- name: 'request_human_input',
360
- description: '사용자에게 입력을 요청합니다 (CAPTCHA, 승인, 자격증명, 선택, 텍스트)',
473
+ name: "request_human_input",
474
+ description:
475
+ "사용자에게 입력을 요청합니다 (CAPTCHA, 승인, 자격증명, 선택, 텍스트)",
361
476
  inputSchema: {
362
- type: 'object',
363
- required: ['requester_agent', 'kind', 'prompt', 'requested_schema', 'deadline_ms', 'default_action'],
477
+ type: "object",
478
+ required: [
479
+ "requester_agent",
480
+ "kind",
481
+ "prompt",
482
+ "requested_schema",
483
+ "deadline_ms",
484
+ "default_action",
485
+ ],
364
486
  properties: {
365
- requester_agent: { type: 'string', pattern: '^[a-zA-Z0-9._:-]{3,64}$' },
366
- kind: { type: 'string', enum: ['captcha', 'approval', 'credential', 'choice', 'text'] },
367
- prompt: { type: 'string', minLength: 1, maxLength: 20000 },
368
- requested_schema: { type: 'object' },
369
- deadline_ms: { type: 'integer', minimum: 1000 },
370
- default_action: { type: 'string', enum: ['decline', 'cancel', 'timeout_continue'] },
371
- channel_preference: { type: 'string', enum: ['terminal', 'pipe', 'file_polling'], default: 'terminal' },
372
- trace_id: { type: 'string' },
373
- correlation_id: { type: 'string' },
487
+ requester_agent: {
488
+ type: "string",
489
+ pattern: "^[a-zA-Z0-9._:-]{3,64}$",
490
+ },
491
+ kind: {
492
+ type: "string",
493
+ enum: ["captcha", "approval", "credential", "choice", "text"],
494
+ },
495
+ prompt: { type: "string", minLength: 1, maxLength: 20000 },
496
+ requested_schema: { type: "object" },
497
+ deadline_ms: { type: "integer", minimum: 1000 },
498
+ default_action: {
499
+ type: "string",
500
+ enum: ["decline", "cancel", "timeout_continue"],
501
+ },
502
+ channel_preference: {
503
+ type: "string",
504
+ enum: ["terminal", "pipe", "file_polling"],
505
+ default: "terminal",
506
+ },
507
+ trace_id: { type: "string" },
508
+ correlation_id: { type: "string" },
374
509
  },
375
510
  },
376
- handler: wrap('HITL_REQUEST_FAILED', (args) => {
511
+ handler: wrap("HITL_REQUEST_FAILED", (args) => {
377
512
  return hitl.requestHumanInput(args);
378
513
  }),
379
514
  },
380
515
 
381
516
  // ── 11. submit_human_input ──
382
517
  {
383
- name: 'submit_human_input',
384
- description: '사용자 입력 요청에 응답합니다 (accept, decline, cancel)',
518
+ name: "submit_human_input",
519
+ description: "사용자 입력 요청에 응답합니다 (accept, decline, cancel)",
385
520
  inputSchema: {
386
- type: 'object',
387
- required: ['request_id', 'action'],
521
+ type: "object",
522
+ required: ["request_id", "action"],
388
523
  properties: {
389
- request_id: { type: 'string' },
390
- action: { type: 'string', enum: ['accept', 'decline', 'cancel'] },
391
- content: { type: 'object' },
392
- submitted_by: { type: 'string', default: 'human' },
524
+ request_id: { type: "string" },
525
+ action: { type: "string", enum: ["accept", "decline", "cancel"] },
526
+ content: { type: "object" },
527
+ submitted_by: { type: "string", default: "human" },
393
528
  },
394
529
  },
395
- handler: wrap('HITL_SUBMIT_FAILED', (args) => {
530
+ handler: wrap("HITL_SUBMIT_FAILED", (args) => {
396
531
  return hitl.submitHumanInput(args);
397
532
  }),
398
533
  },
399
534
 
400
535
  // ── 12. team_info ──
401
536
  {
402
- name: 'team_info',
403
- description: 'Claude Native Teams 메타/멤버/경로 정보를 조회합니다',
537
+ name: "team_info",
538
+ description: "Claude Native Teams 메타/멤버/경로 정보를 조회합니다",
404
539
  inputSchema: {
405
- type: 'object',
406
- required: ['team_name'],
540
+ type: "object",
541
+ required: ["team_name"],
407
542
  properties: {
408
- team_name: { type: 'string', minLength: 1, maxLength: 128, pattern: '^[a-z0-9][a-z0-9-]*$' },
409
- include_members: { type: 'boolean', default: true },
410
- include_paths: { type: 'boolean', default: true },
543
+ team_name: {
544
+ type: "string",
545
+ minLength: 1,
546
+ maxLength: 128,
547
+ pattern: "^[a-z0-9][a-z0-9-]*$",
548
+ },
549
+ include_members: { type: "boolean", default: true },
550
+ include_paths: { type: "boolean", default: true },
411
551
  },
412
552
  },
413
- handler: wrap('TEAM_INFO_FAILED', (args) => {
414
- const teamInfo = getTeamBridgeMethod('teamInfo');
553
+ handler: wrap("TEAM_INFO_FAILED", (args) => {
554
+ const teamInfo = getTeamBridgeMethod("teamInfo");
415
555
  return teamInfo ? teamInfo(args) : teamInfoFallback(args);
416
556
  }),
417
557
  },
418
558
 
419
559
  // ── 13. team_task_list ──
420
560
  {
421
- name: 'team_task_list',
422
- description: 'Claude Native Teams task 목록을 owner/status 조건으로 조회합니다. 실패 판정은 completed + metadata.result도 함께 확인해야 합니다',
561
+ name: "team_task_list",
562
+ description:
563
+ "Claude Native Teams task 목록을 owner/status 조건으로 조회합니다. 실패 판정은 completed + metadata.result도 함께 확인해야 합니다",
423
564
  inputSchema: {
424
- type: 'object',
425
- required: ['team_name'],
565
+ type: "object",
566
+ required: ["team_name"],
426
567
  properties: {
427
- team_name: { type: 'string', pattern: '^[a-z0-9][a-z0-9-]*$' },
428
- owner: { type: 'string' },
568
+ team_name: { type: "string", pattern: "^[a-z0-9][a-z0-9-]*$" },
569
+ owner: { type: "string" },
429
570
  statuses: {
430
- type: 'array',
431
- items: { type: 'string', enum: ['pending', 'in_progress', 'completed', 'failed', 'deleted'] },
571
+ type: "array",
572
+ items: {
573
+ type: "string",
574
+ enum: [
575
+ "pending",
576
+ "in_progress",
577
+ "completed",
578
+ "failed",
579
+ "deleted",
580
+ ],
581
+ },
432
582
  maxItems: 8,
433
583
  },
434
- include_internal: { type: 'boolean', default: false },
435
- limit: { type: 'integer', minimum: 1, maximum: 1000, default: 200 },
584
+ include_internal: { type: "boolean", default: false },
585
+ limit: { type: "integer", minimum: 1, maximum: 1000, default: 200 },
436
586
  },
437
587
  },
438
- handler: wrap('TEAM_TASK_LIST_FAILED', (args) => {
439
- const teamTaskList = getTeamBridgeMethod('teamTaskList');
588
+ handler: wrap("TEAM_TASK_LIST_FAILED", (args) => {
589
+ const teamTaskList = getTeamBridgeMethod("teamTaskList");
440
590
  return teamTaskList ? teamTaskList(args) : teamTaskListFallback(args);
441
591
  }),
442
592
  },
443
593
 
444
594
  // ── 14. team_task_update ──
445
595
  {
446
- name: 'team_task_update',
447
- description: 'Claude Native Teams task를 claim/update 합니다. status: "failed" 입력은 completed + metadata.result="failed"로 정규화됩니다',
596
+ name: "team_task_update",
597
+ description:
598
+ 'Claude Native Teams task를 claim/update 합니다. status: "failed" 입력은 completed + metadata.result="failed"로 정규화됩니다',
448
599
  inputSchema: {
449
- type: 'object',
450
- required: ['team_name', 'task_id'],
600
+ type: "object",
601
+ required: ["team_name", "task_id"],
451
602
  properties: {
452
- team_name: { type: 'string', pattern: '^[a-z0-9][a-z0-9-]*$' },
453
- task_id: { type: 'string', minLength: 1, maxLength: 64 },
454
- claim: { type: 'boolean', default: false },
455
- owner: { type: 'string' },
456
- status: { type: 'string', enum: ['pending', 'in_progress', 'completed', 'failed', 'deleted'] },
457
- subject: { type: 'string' },
458
- description: { type: 'string' },
459
- activeForm: { type: 'string' },
460
- add_blocks: { type: 'array', items: { type: 'string' } },
461
- add_blocked_by: { type: 'array', items: { type: 'string' } },
462
- metadata_patch: { type: 'object' },
463
- if_match_mtime_ms: { type: 'number' },
464
- actor: { type: 'string' },
603
+ team_name: { type: "string", pattern: "^[a-z0-9][a-z0-9-]*$" },
604
+ task_id: { type: "string", minLength: 1, maxLength: 64 },
605
+ claim: { type: "boolean", default: false },
606
+ owner: { type: "string" },
607
+ status: {
608
+ type: "string",
609
+ enum: ["pending", "in_progress", "completed", "failed", "deleted"],
610
+ },
611
+ subject: { type: "string" },
612
+ description: { type: "string" },
613
+ activeForm: { type: "string" },
614
+ add_blocks: { type: "array", items: { type: "string" } },
615
+ add_blocked_by: { type: "array", items: { type: "string" } },
616
+ metadata_patch: { type: "object" },
617
+ if_match_mtime_ms: { type: "number" },
618
+ actor: { type: "string" },
465
619
  },
466
620
  },
467
- handler: wrap('TEAM_TASK_UPDATE_FAILED', (args) => {
468
- const teamTaskUpdate = getTeamBridgeMethod('teamTaskUpdate');
469
- return teamTaskUpdate ? teamTaskUpdate(args) : teamTaskUpdateFallback(args);
621
+ handler: wrap("TEAM_TASK_UPDATE_FAILED", (args) => {
622
+ const teamTaskUpdate = getTeamBridgeMethod("teamTaskUpdate");
623
+ return teamTaskUpdate
624
+ ? teamTaskUpdate(args)
625
+ : teamTaskUpdateFallback(args);
470
626
  }),
471
627
  },
472
628
 
473
629
  // ── 15. team_send_message ──
474
630
  {
475
- name: 'team_send_message',
476
- description: 'Claude Native Teams inbox에 메시지를 append 합니다',
631
+ name: "team_send_message",
632
+ description: "Claude Native Teams inbox에 메시지를 append 합니다",
477
633
  inputSchema: {
478
- type: 'object',
479
- required: ['team_name', 'from', 'text'],
634
+ type: "object",
635
+ required: ["team_name", "from", "text"],
480
636
  properties: {
481
- team_name: { type: 'string', pattern: '^[a-z0-9][a-z0-9-]*$' },
482
- from: { type: 'string', minLength: 1, maxLength: 128 },
483
- to: { type: 'string', default: 'team-lead' },
484
- text: { type: 'string', minLength: 1, maxLength: 200000 },
485
- summary: { type: 'string', maxLength: 1000 },
486
- color: { type: 'string', default: 'blue' },
637
+ team_name: { type: "string", pattern: "^[a-z0-9][a-z0-9-]*$" },
638
+ from: { type: "string", minLength: 1, maxLength: 128 },
639
+ to: { type: "string", default: "team-lead" },
640
+ text: { type: "string", minLength: 1, maxLength: 200000 },
641
+ summary: { type: "string", maxLength: 1000 },
642
+ color: { type: "string", default: "blue" },
487
643
  },
488
644
  },
489
- handler: wrap('TEAM_SEND_MESSAGE_FAILED', (args) => {
490
- const teamSendMessage = getTeamBridgeMethod('teamSendMessage');
491
- return teamSendMessage ? teamSendMessage(args) : teamSendMessageFallback(args);
645
+ handler: wrap("TEAM_SEND_MESSAGE_FAILED", (args) => {
646
+ const teamSendMessage = getTeamBridgeMethod("teamSendMessage");
647
+ return teamSendMessage
648
+ ? teamSendMessage(args)
649
+ : teamSendMessageFallback(args);
492
650
  }),
493
651
  },
494
652
 
495
653
  // ── 16. pipeline_state ──
496
654
  {
497
- name: 'pipeline_state',
498
- description: '파이프라인 상태를 조회합니다 (--thorough 모드)',
655
+ name: "pipeline_state",
656
+ description: "파이프라인 상태를 조회합니다 (--thorough 모드)",
499
657
  inputSchema: {
500
- type: 'object',
501
- required: ['team_name'],
658
+ type: "object",
659
+ required: ["team_name"],
502
660
  properties: {
503
- team_name: { type: 'string', pattern: '^[a-z0-9][a-z0-9-]*$' },
661
+ team_name: { type: "string", pattern: "^[a-z0-9][a-z0-9-]*$" },
504
662
  },
505
663
  },
506
- handler: wrap('PIPELINE_STATE_FAILED', (args) => {
664
+ handler: wrap("PIPELINE_STATE_FAILED", (args) => {
507
665
  ensurePipelineTable(store.db);
508
666
  const state = readPipelineState(store.db, args.team_name);
509
667
  return state
510
668
  ? { ok: true, data: state }
511
- : { ok: false, error: { code: 'PIPELINE_NOT_FOUND', message: `파이프라인 없음: ${args.team_name}` } };
669
+ : {
670
+ ok: false,
671
+ error: {
672
+ code: "PIPELINE_NOT_FOUND",
673
+ message: `파이프라인 없음: ${args.team_name}`,
674
+ },
675
+ };
512
676
  }),
513
677
  },
514
678
 
515
679
  // ── 17. pipeline_advance ──
516
680
  {
517
- name: 'pipeline_advance',
518
- description: '파이프라인을 다음 단계로 전이합니다 (전이 규칙 + fix loop 바운딩 적용)',
681
+ name: "pipeline_advance",
682
+ description:
683
+ "파이프라인을 다음 단계로 전이합니다 (전이 규칙 + fix loop 바운딩 적용)",
519
684
  inputSchema: {
520
- type: 'object',
521
- required: ['team_name', 'phase'],
685
+ type: "object",
686
+ required: ["team_name", "phase"],
522
687
  properties: {
523
- team_name: { type: 'string', pattern: '^[a-z0-9][a-z0-9-]*$' },
524
- phase: { type: 'string', enum: ['plan', 'prd', 'exec', 'verify', 'fix', 'complete', 'failed'] },
688
+ team_name: { type: "string", pattern: "^[a-z0-9][a-z0-9-]*$" },
689
+ phase: {
690
+ type: "string",
691
+ enum: [
692
+ "plan",
693
+ "prd",
694
+ "exec",
695
+ "verify",
696
+ "fix",
697
+ "complete",
698
+ "failed",
699
+ ],
700
+ },
525
701
  },
526
702
  },
527
- handler: wrap('PIPELINE_ADVANCE_FAILED', (args) => {
703
+ handler: wrap("PIPELINE_ADVANCE_FAILED", (args) => {
528
704
  ensurePipelineTable(store.db);
529
705
  const pipeline = createPipeline(store.db, args.team_name);
530
706
  return pipeline.advance(args.phase);
@@ -533,18 +709,18 @@ export function createTools(store, router, hitl, pipe = null) {
533
709
 
534
710
  // ── 18. pipeline_init ──
535
711
  {
536
- name: 'pipeline_init',
537
- description: '새 파이프라인을 초기화합니다 (기존 상태 덮어쓰기)',
712
+ name: "pipeline_init",
713
+ description: "새 파이프라인을 초기화합니다 (기존 상태 덮어쓰기)",
538
714
  inputSchema: {
539
- type: 'object',
540
- required: ['team_name'],
715
+ type: "object",
716
+ required: ["team_name"],
541
717
  properties: {
542
- team_name: { type: 'string', pattern: '^[a-z0-9][a-z0-9-]*$' },
543
- fix_max: { type: 'integer', minimum: 1, maximum: 20, default: 3 },
544
- ralph_max: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
718
+ team_name: { type: "string", pattern: "^[a-z0-9][a-z0-9-]*$" },
719
+ fix_max: { type: "integer", minimum: 1, maximum: 20, default: 3 },
720
+ ralph_max: { type: "integer", minimum: 1, maximum: 100, default: 10 },
545
721
  },
546
722
  },
547
- handler: wrap('PIPELINE_INIT_FAILED', (args) => {
723
+ handler: wrap("PIPELINE_INIT_FAILED", (args) => {
548
724
  ensurePipelineTable(store.db);
549
725
  const state = initPipelineState(store.db, args.team_name, {
550
726
  fix_max: args.fix_max,
@@ -556,21 +732,48 @@ export function createTools(store, router, hitl, pipe = null) {
556
732
 
557
733
  // ── 19. pipeline_advance_gated (HITL 승인 게이트) ──
558
734
  {
559
- name: 'pipeline_advance_gated',
560
- description: 'HITL 승인 게이트가 포함된 파이프라인 전이. 지정 단계로의 전이 전 사용자 승인을 요청하고, 승인 후 전이를 실행합니다. deadline 초과 시 default_action에 따라 자동 처리됩니다.',
735
+ name: "pipeline_advance_gated",
736
+ description:
737
+ "HITL 승인 게이트가 포함된 파이프라인 전이. 지정 단계로의 전이 전 사용자 승인을 요청하고, 승인 후 전이를 실행합니다. deadline 초과 시 default_action에 따라 자동 처리됩니다.",
561
738
  inputSchema: {
562
- type: 'object',
563
- required: ['team_name', 'phase'],
739
+ type: "object",
740
+ required: ["team_name", "phase"],
564
741
  properties: {
565
- team_name: { type: 'string', pattern: '^[a-z0-9][a-z0-9-]*$' },
566
- phase: { type: 'string', enum: ['plan', 'prd', 'exec', 'verify', 'fix', 'complete', 'failed'] },
567
- prompt: { type: 'string', description: '사용자에게 표시할 승인 요청 메시지' },
568
- deadline_ms: { type: 'integer', minimum: 5000, maximum: 600000, default: 120000 },
569
- default_action: { type: 'string', enum: ['timeout_continue', 'timeout_abort'], default: 'timeout_continue' },
570
- requester_agent: { type: 'string', description: '요청자 에이전트 이름' },
742
+ team_name: { type: "string", pattern: "^[a-z0-9][a-z0-9-]*$" },
743
+ phase: {
744
+ type: "string",
745
+ enum: [
746
+ "plan",
747
+ "prd",
748
+ "exec",
749
+ "verify",
750
+ "fix",
751
+ "complete",
752
+ "failed",
753
+ ],
754
+ },
755
+ prompt: {
756
+ type: "string",
757
+ description: "사용자에게 표시할 승인 요청 메시지",
758
+ },
759
+ deadline_ms: {
760
+ type: "integer",
761
+ minimum: 5000,
762
+ maximum: 600000,
763
+ default: 120000,
764
+ },
765
+ default_action: {
766
+ type: "string",
767
+ enum: ["timeout_continue", "timeout_abort"],
768
+ default: "timeout_continue",
769
+ },
770
+ requester_agent: {
771
+ type: "string",
772
+ description: "요청자 에이전트 이름",
773
+ },
571
774
  },
572
775
  },
573
- handler: wrap('PIPELINE_ADVANCE_GATED_FAILED', (args) => {
776
+ handler: wrap("PIPELINE_ADVANCE_GATED_FAILED", (args) => {
574
777
  ensurePipelineTable(store.db);
575
778
  const pipeline = createPipeline(store.db, args.team_name);
576
779
 
@@ -580,23 +783,25 @@ export function createTools(store, router, hitl, pipe = null) {
580
783
  return {
581
784
  ok: false,
582
785
  error: {
583
- code: 'TRANSITION_BLOCKED',
786
+ code: "TRANSITION_BLOCKED",
584
787
  message: `전이 불가: ${current.phase} → ${args.phase}`,
585
788
  },
586
789
  };
587
790
  }
588
791
 
589
792
  // HITL 승인 요청 생성
590
- const approvalPrompt = args.prompt || `파이프라인 ${args.team_name}: ${pipeline.getState().phase} → ${args.phase} 전이를 승인하시겠습니까?`;
793
+ const approvalPrompt =
794
+ args.prompt ||
795
+ `파이프라인 ${args.team_name}: ${pipeline.getState().phase} → ${args.phase} 전이를 승인하시겠습니까?`;
591
796
  const deadlineMs = args.deadline_ms || 120000;
592
797
  const now = Date.now();
593
798
 
594
799
  const hitlResult = hitl.requestHumanInput({
595
800
  requester_agent: args.requester_agent || `pipeline:${args.team_name}`,
596
- kind: 'approval',
801
+ kind: "approval",
597
802
  prompt: approvalPrompt,
598
803
  deadline_ms: now + deadlineMs,
599
- default_action: args.default_action || 'timeout_continue',
804
+ default_action: args.default_action || "timeout_continue",
600
805
  });
601
806
 
602
807
  if (!hitlResult.ok) {
@@ -612,8 +817,8 @@ export function createTools(store, router, hitl, pipe = null) {
612
817
  target_phase: args.phase,
613
818
  current_phase: pipeline.getState().phase,
614
819
  deadline_ms: now + deadlineMs,
615
- default_action: args.default_action || 'timeout_continue',
616
- message: `승인 대기 중. ID: ${hitlResult.data.request_id}. ${Math.round(deadlineMs / 1000)}초 후 ${args.default_action || 'timeout_continue'} 자동 실행.`,
820
+ default_action: args.default_action || "timeout_continue",
821
+ message: `승인 대기 중. ID: ${hitlResult.data.request_id}. ${Math.round(deadlineMs / 1000)}초 후 ${args.default_action || "timeout_continue"} 자동 실행.`,
617
822
  },
618
823
  };
619
824
  }),
@@ -621,16 +826,33 @@ export function createTools(store, router, hitl, pipe = null) {
621
826
 
622
827
  // ── 20. pipeline_list ──
623
828
  {
624
- name: 'pipeline_list',
625
- description: '활성 파이프라인 목록을 조회합니다',
829
+ name: "pipeline_list",
830
+ description: "활성 파이프라인 목록을 조회합니다",
626
831
  inputSchema: {
627
- type: 'object',
832
+ type: "object",
628
833
  properties: {},
629
834
  },
630
- handler: wrap('PIPELINE_LIST_FAILED', () => {
835
+ handler: wrap("PIPELINE_LIST_FAILED", () => {
631
836
  ensurePipelineTable(store.db);
632
837
  return { ok: true, data: listPipelineStates(store.db) };
633
838
  }),
634
839
  },
840
+
841
+ // ── 21. send_input ──
842
+ {
843
+ name: "send_input",
844
+ description: "Send input text to a worker in INPUT_WAIT state",
845
+ inputSchema: {
846
+ type: "object",
847
+ properties: {
848
+ session_id: { type: "string", description: "Conductor session ID" },
849
+ text: { type: "string", description: "Input text to send" },
850
+ },
851
+ required: ["session_id", "text"],
852
+ },
853
+ handler: wrap("SEND_INPUT_FAILED", (args) => {
854
+ return sendInputToConductorSession(args.session_id, args.text);
855
+ }),
856
+ },
635
857
  ];
636
858
  }