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