@triflux/core 10.51.0 → 10.53.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 (61) hide show
  1. package/hub/bridge.mjs +69 -1197
  2. package/hub/cli-adapter-base.mjs +4 -104
  3. package/hub/codex-adapter.mjs +7 -116
  4. package/hub/index.mjs +3 -20
  5. package/hub/lib/process-utils.mjs +1 -33
  6. package/hub/team/claude-daemon-control.mjs +1 -2
  7. package/hub/team/claude-session-projection.mjs +5 -73
  8. package/hub/team/claude-transcript.mjs +19 -0
  9. package/hub/team/nativeProxy.mjs +759 -0
  10. package/hub/team/session-context.mjs +1 -1
  11. package/hud/constants.mjs +9 -22
  12. package/hud/hud-qos-status.mjs +16 -19
  13. package/hud/providers/codex-probe.mjs +2 -31
  14. package/hud/providers/codex.mjs +16 -58
  15. package/hud/providers/gemini.mjs +20 -0
  16. package/hud/renderers.mjs +101 -77
  17. package/hud/utils.mjs +9 -13
  18. package/package.json +3 -6
  19. package/scripts/lib/agent-route-policy.mjs +0 -67
  20. package/scripts/lib/cli-claude.mjs +0 -12
  21. package/scripts/lib/env-probe.mjs +0 -70
  22. package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
  23. package/scripts/lib/mcp-guard-engine.mjs +42 -101
  24. package/scripts/lib/session-presence.mjs +1 -99
  25. package/hub/account-broker.mjs +0 -1221
  26. package/hub/adaptive-diagnostic.mjs +0 -350
  27. package/hub/adaptive-inject.mjs +0 -224
  28. package/hub/adaptive-memory.mjs +0 -277
  29. package/hub/adaptive.mjs +0 -172
  30. package/hub/assign-callbacks.mjs +0 -170
  31. package/hub/delegator/contracts.mjs +0 -38
  32. package/hub/delegator/index.mjs +0 -14
  33. package/hub/delegator/schema/delegator-tools.schema.json +0 -250
  34. package/hub/delegator/service.mjs +0 -336
  35. package/hub/delegator/tool-definitions.mjs +0 -35
  36. package/hub/hitl.mjs +0 -202
  37. package/hub/lib/known-errors.json +0 -72
  38. package/hub/lib/memory-store.mjs +0 -1001
  39. package/hub/lib/phase-manager.mjs +0 -493
  40. package/hub/lib/state-snapshot.mjs +0 -293
  41. package/hub/lib/trace-recorder.mjs +0 -153
  42. package/hub/lib/uuidv7.mjs +0 -45
  43. package/hub/memory-doctor.mjs +0 -640
  44. package/hub/middleware/quota-middleware.mjs +0 -70
  45. package/hub/middleware/request-logger.mjs +0 -191
  46. package/hub/paths.mjs +0 -24
  47. package/hub/pipeline/index.mjs +0 -163
  48. package/hub/pipeline/state.mjs +0 -191
  49. package/hub/pipeline/transitions.mjs +0 -131
  50. package/hub/reflexion.mjs +0 -290
  51. package/hub/role-contract.mjs +0 -312
  52. package/hub/router.mjs +0 -2026
  53. package/hub/session-fingerprint.mjs +0 -418
  54. package/hub/state.mjs +0 -300
  55. package/hub/team/synapse-http.mjs +0 -311
  56. package/hub/team-bridge.mjs +0 -27
  57. package/scripts/lib/claudemd-scanner.mjs +0 -26
  58. package/scripts/lib/context.mjs +0 -51
  59. package/scripts/lib/handoff.mjs +0 -187
  60. package/scripts/lib/logger.mjs +0 -122
  61. package/scripts/lib/stealth-fetch.mjs +0 -176
package/hub/bridge.mjs CHANGED
@@ -1,445 +1,47 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // hub/bridge.mjs — tfx-route.sh ↔ tfx-hub 브릿지 CLI
4
- //
5
- // Named Pipe/Unix Socket 제어 채널을 우선 사용하고,
6
- // 연결이 없을 때만 HTTP /bridge/* 엔드포인트로 내려간다.
3
+ // hub/bridge.mjs: tfx-route.sh 와 tfx-live 가 부르는 로컬 제어 CLI.
4
+ // 팀 하위 명령은 nativeProxy 를 직접 호출하고 Claude daemon, retry, intervene 은 로컬 모듈만 쓴다.
7
5
 
8
- import { randomUUID } from "node:crypto";
9
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
10
- import net from "node:net";
11
- import { homedir } from "node:os";
12
- import { join } from "node:path";
13
- import { fileURLToPath } from "node:url";
6
+ import { readFileSync } from "node:fs";
14
7
  import { parseArgs as nodeParseArgs } from "node:util";
15
8
 
16
- import { getPipelineStateDbPath } from "./pipeline/state.mjs";
17
9
  import {
18
10
  createRetryStateMachine,
19
11
  loadSnapshot,
20
12
  saveSnapshot,
21
13
  } from "./team/retry-state-machine.mjs";
22
14
 
23
- const HUB_PID_FILE = join(homedir(), ".claude", "cache", "tfx-hub", "hub.pid");
24
- const HUB_TOKEN_FILE = join(homedir(), ".claude", ".tfx-hub-token");
25
- const PROJECT_ROOT = fileURLToPath(new URL("..", import.meta.url));
26
- const HUB_DEFAULT_PORT = 27888;
27
- const LOOPBACK_HOSTS = new Set(["127.0.0.1", "localhost", "::1"]);
28
-
29
- function formatHostForUrl(host) {
30
- return host.includes(":") ? `[${host}]` : host;
31
- }
32
-
33
- function normalizeLoopbackHost(host) {
34
- if (typeof host !== "string") return "127.0.0.1";
35
- const candidate = host.trim();
36
- return LOOPBACK_HOSTS.has(candidate) ? candidate : "127.0.0.1";
37
- }
38
-
39
- function normalizeToken(raw) {
40
- if (raw == null) return null;
41
- const token = String(raw).trim();
42
- return token || null;
43
- }
44
-
45
- // Hub 인증 토큰 읽기 (파일 없으면 null → 하위 호환)
46
- function readHubToken() {
47
- const envToken = normalizeToken(process.env.TFX_HUB_TOKEN);
48
- if (envToken) return envToken;
49
- try {
50
- return normalizeToken(readFileSync(HUB_TOKEN_FILE, "utf8"));
51
- } catch {
52
- return null;
53
- }
54
- }
55
-
56
- export function getHubUrl() {
57
- if (process.env.TFX_HUB_URL)
58
- return process.env.TFX_HUB_URL.replace(/\/mcp$/, "");
59
-
60
- const envPort = Number.parseInt(String(process.env.TFX_HUB_PORT ?? ""), 10);
61
- const port =
62
- Number.isFinite(envPort) && envPort > 0 ? envPort : HUB_DEFAULT_PORT;
63
- let host = "127.0.0.1";
64
-
65
- if (existsSync(HUB_PID_FILE)) {
66
- try {
67
- const info = JSON.parse(readFileSync(HUB_PID_FILE, "utf8"));
68
- host = normalizeLoopbackHost(info?.host);
69
- } catch {
70
- // 무시
71
- }
72
- }
73
-
74
- return `http://${formatHostForUrl(host)}:${port}`;
75
- }
76
-
77
- export function getHubPipePath() {
78
- if (process.env.TFX_HUB_PIPE) return process.env.TFX_HUB_PIPE;
79
-
80
- if (!existsSync(HUB_PID_FILE)) return null;
81
- try {
82
- const info = JSON.parse(readFileSync(HUB_PID_FILE, "utf8"));
83
- return info.pipe_path || info.pipePath || null;
84
- } catch {
85
- return null;
86
- }
87
- }
88
-
89
- const HUB_OPERATIONS = Object.freeze({
90
- register: {
91
- transport: "command",
92
- action: "register",
93
- httpPath: "/bridge/register",
94
- },
95
- heartbeat: {
96
- transport: "command",
97
- action: "heartbeat",
98
- httpPath: "/bridge/heartbeat",
99
- },
100
- result: {
101
- transport: "command",
102
- action: "result",
103
- httpPath: "/bridge/result",
104
- },
105
- control: {
106
- transport: "command",
107
- action: "control",
108
- httpPath: "/bridge/control",
109
- },
110
- handoff: {
111
- transport: "command",
112
- action: "handoff",
113
- httpPath: "/bridge/handoff",
114
- },
115
- publish: {
116
- transport: "command",
117
- action: "publish",
118
- httpPath: "/bridge/publish",
119
- },
120
- takeoverRole: {
121
- transport: "command",
122
- action: "takeover_role",
123
- httpPath: "/bridge/takeover-role",
124
- },
125
- sendInput: {
126
- transport: "command",
127
- action: "send_input",
128
- httpPath: "/bridge/send-input",
129
- },
130
- context: { transport: "query", action: "drain", httpPath: "/bridge/context" },
131
- deregister: {
132
- transport: "command",
133
- action: "deregister",
134
- httpPath: "/bridge/deregister",
135
- },
136
- assignAsync: {
137
- transport: "command",
138
- action: "assign",
139
- httpPath: "/bridge/assign/async",
140
- },
141
- assignResult: {
142
- transport: "command",
143
- action: "assign_result",
144
- httpPath: "/bridge/assign/result",
145
- },
146
- assignStatus: {
147
- transport: "query",
148
- action: "assign_status",
149
- httpPath: "/bridge/assign/status",
150
- },
151
- assignRetry: {
152
- transport: "command",
153
- action: "assign_retry",
154
- httpPath: "/bridge/assign/retry",
155
- },
156
- teamInfo: {
157
- transport: "query",
158
- action: "team_info",
159
- httpPath: "/bridge/team/info",
160
- },
161
- teamTaskList: {
162
- transport: "query",
163
- action: "team_task_list",
164
- httpPath: "/bridge/team/task-list",
165
- },
166
- teamTaskUpdate: {
167
- transport: "command",
168
- action: "team_task_update",
169
- httpPath: "/bridge/team/task-update",
170
- },
171
- teamSendMessage: {
172
- transport: "command",
173
- action: "team_send_message",
174
- httpPath: "/bridge/team/send-message",
175
- },
176
- pipelineState: {
177
- transport: "query",
178
- action: "pipeline_state",
179
- httpPath: "/bridge/pipeline/state",
180
- },
181
- pipelineAdvance: {
182
- transport: "command",
183
- action: "pipeline_advance",
184
- httpPath: "/bridge/pipeline/advance",
185
- },
186
- pipelineInit: {
187
- transport: "command",
188
- action: "pipeline_init",
189
- httpPath: "/bridge/pipeline/init",
190
- },
191
- pipelineList: {
192
- transport: "query",
193
- action: "pipeline_list",
194
- httpPath: "/bridge/pipeline/list",
195
- },
196
- hubStatus: {
197
- transport: "query",
198
- action: "status",
199
- httpPath: "/status",
200
- httpMethod: "GET",
201
- },
202
- delegatorDelegate: {
203
- transport: "command",
204
- action: "delegator_delegate",
205
- httpPath: "/bridge/delegator/delegate",
206
- },
207
- delegatorReply: {
208
- transport: "command",
209
- action: "delegator_reply",
210
- httpPath: "/bridge/delegator/reply",
211
- },
212
- delegatorStatus: {
213
- transport: "query",
214
- action: "delegator_status",
215
- httpPath: "/bridge/delegator/status",
216
- },
217
- "hitl-request": {
218
- transport: "command",
219
- action: "hitl_request",
220
- httpPath: "/bridge/hitl/request",
221
- },
222
- "hitl-submit": {
223
- transport: "command",
224
- action: "hitl_submit",
225
- httpPath: "/bridge/hitl/submit",
226
- },
227
- "hitl-pending": {
228
- transport: "query",
229
- action: "hitl_pending",
230
- httpPath: "/bridge/hitl/pending",
231
- },
232
- });
233
-
234
- export async function requestJson(
235
- path,
236
- { method = "POST", body, timeoutMs = 5000 } = {},
237
- ) {
238
- const controller = new AbortController();
239
- const timer = setTimeout(() => controller.abort(), timeoutMs);
240
-
241
- try {
242
- const headers = {};
243
- const token = readHubToken();
244
- if (token) {
245
- headers.Authorization = `Bearer ${token}`;
246
- }
247
- if (body !== undefined) {
248
- headers["Content-Type"] = "application/json";
249
- }
250
-
251
- const res = await fetch(`${getHubUrl()}${path}`, {
252
- method,
253
- headers,
254
- body: body === undefined ? undefined : JSON.stringify(body),
255
- signal: controller.signal,
256
- });
257
- clearTimeout(timer);
258
- return await res.json();
259
- } catch {
260
- clearTimeout(timer);
261
- return null;
262
- }
263
- }
264
-
265
- export async function post(path, body, timeoutMs = 5000) {
266
- return await requestJson(path, { method: "POST", body, timeoutMs });
267
- }
268
-
269
- export async function connectPipe(timeoutMs = 1200) {
270
- const pipePath = getHubPipePath();
271
- if (!pipePath) return null;
272
-
273
- return await new Promise((resolve) => {
274
- const socket = net.createConnection(pipePath);
275
- const timer = setTimeout(() => {
276
- try {
277
- socket.destroy();
278
- } catch {}
279
- resolve(null);
280
- }, timeoutMs);
281
-
282
- socket.once("connect", () => {
283
- clearTimeout(timer);
284
- socket.setEncoding("utf8");
285
- resolve(socket);
286
- });
287
-
288
- socket.once("error", () => {
289
- clearTimeout(timer);
290
- try {
291
- socket.destroy();
292
- } catch {}
293
- resolve(null);
294
- });
295
- });
296
- }
297
-
298
- async function pipeRequest(type, action, payload, timeoutMs = 3000) {
299
- const socket = await connectPipe(Math.min(timeoutMs, 1500));
300
- if (!socket) return null;
301
-
302
- return await new Promise((resolve) => {
303
- const requestId = randomUUID();
304
- let buffer = "";
305
- let settled = false;
306
- const timer = setTimeout(() => {
307
- finish(null);
308
- }, timeoutMs);
309
-
310
- const finish = (result) => {
311
- if (settled) return;
312
- settled = true;
313
- clearTimeout(timer);
314
- try {
315
- socket.end();
316
- } catch {}
317
- resolve(result);
318
- };
319
-
320
- socket.on("data", (chunk) => {
321
- buffer += chunk;
322
- let newlineIndex = buffer.indexOf("\n");
323
- while (newlineIndex >= 0) {
324
- const line = buffer.slice(0, newlineIndex).trim();
325
- buffer = buffer.slice(newlineIndex + 1);
326
- newlineIndex = buffer.indexOf("\n");
327
- if (!line) continue;
328
-
329
- let frame;
330
- try {
331
- frame = JSON.parse(line);
332
- } catch {
333
- continue;
334
- }
335
-
336
- if (frame?.type !== "response" || frame.request_id !== requestId)
337
- continue;
338
- finish({
339
- ok: frame.ok,
340
- error: frame.error,
341
- data: frame.data,
342
- });
343
- return;
344
- }
345
- });
346
-
347
- socket.on("error", () => finish(null));
348
- socket.write(
349
- JSON.stringify({
350
- type,
351
- request_id: requestId,
352
- payload: { action, ...payload },
353
- }) + "\n",
354
- );
355
- });
356
- }
357
-
358
- async function pipeCommand(action, payload, timeoutMs = 3000) {
359
- return await pipeRequest("command", action, payload, timeoutMs);
360
- }
361
-
362
- async function pipeQuery(action, payload, timeoutMs = 3000) {
363
- return await pipeRequest("query", action, payload, timeoutMs);
364
- }
365
-
366
15
  export function parseArgs(argv) {
367
16
  const { values, positionals } = nodeParseArgs({
368
17
  args: argv,
369
18
  options: {
370
- agent: { type: "string" },
371
- "agent-id": { type: "string" },
372
- cli: { type: "string" },
373
- timeout: { type: "string" },
374
- topics: { type: "string" },
375
- capabilities: { type: "string" },
376
- file: { type: "string" },
377
- payload: { type: "string" },
378
- "payload-file": { type: "string" },
379
- topic: { type: "string" },
380
- trace: { type: "string" },
381
- correlation: { type: "string" },
382
- "exit-code": { type: "string" },
383
- max: { type: "string" },
384
- out: { type: "string" },
385
19
  team: { type: "string" },
386
20
  "task-id": { type: "string" },
387
- "job-id": { type: "string" },
388
21
  owner: { type: "string" },
389
22
  status: { type: "string" },
390
23
  statuses: { type: "string" },
391
24
  claim: { type: "boolean" },
392
25
  actor: { type: "string" },
393
- command: { type: "string" },
394
- role: { type: "string" },
395
- "session-id": { type: "string" },
396
- cwd: { type: "string" },
397
- "worktree-path": { type: "string" },
398
- branch: { type: "string" },
399
- host: { type: "string" },
400
- "session-kind": { type: "string" },
401
- "is-remote": { type: "string" },
402
- "tmux-session": { type: "string" },
403
- "tmux-pane": { type: "string" },
404
- "omx-session-id": { type: "string" },
405
- "codex-session-id": { type: "string" },
406
- reason: { type: "string" },
407
- type: { type: "string" },
408
26
  from: { type: "string" },
409
27
  to: { type: "string" },
410
28
  text: { type: "string" },
411
- task: { type: "string" },
412
- "supervisor-agent": { type: "string" },
413
- "worker-agent": { type: "string" },
414
- priority: { type: "string" },
415
- "ttl-ms": { type: "string" },
416
- "timeout-ms": { type: "string" },
417
- "max-retries": { type: "string" },
418
- attempt: { type: "string" },
419
- result: { type: "string" },
420
- error: { type: "string" },
421
- metadata: { type: "string" },
422
- "requested-by": { type: "string" },
423
29
  summary: { type: "string" },
424
30
  color: { type: "string" },
425
31
  limit: { type: "string" },
426
32
  "include-internal": { type: "boolean" },
427
33
  subject: { type: "string" },
428
34
  description: { type: "string" },
429
- "fix-max": { type: "string" },
430
- "ralph-max": { type: "string" },
431
35
  "active-form": { type: "string" },
432
36
  "add-blocks": { type: "string" },
433
37
  "add-blocked-by": { type: "string" },
434
38
  "metadata-patch": { type: "string" },
435
39
  "if-match-mtime-ms": { type: "string" },
436
- provider: { type: "string" },
40
+ payload: { type: "string" },
41
+ "payload-file": { type: "string" },
42
+ "session-id": { type: "string" },
43
+ reason: { type: "string" },
437
44
  mode: { type: "string" },
438
- prompt: { type: "string" },
439
- reply: { type: "string" },
440
- done: { type: "boolean" },
441
- "mcp-profile": { type: "string" },
442
- "session-key": { type: "string" },
443
45
  snapshot: { type: "string" },
444
46
  "snapshot-file": { type: "string" },
445
47
  event: { type: "string" },
@@ -479,130 +81,6 @@ export function readBridgePayload(args = {}, stdin) {
479
81
  return {};
480
82
  }
481
83
 
482
- function normalizeBridgePayload(payload) {
483
- if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
484
- return {};
485
- }
486
- return payload;
487
- }
488
-
489
- function buildHandoffBody(from, to, payload) {
490
- const normalizedPayload = normalizeBridgePayload(payload);
491
- return {
492
- ...normalizedPayload,
493
- from,
494
- to,
495
- payload: normalizedPayload,
496
- };
497
- }
498
-
499
- function buildPublishBody(from, to, type, payload) {
500
- const normalizedPayload = normalizeBridgePayload(payload);
501
- return {
502
- ...normalizedPayload,
503
- from,
504
- to,
505
- type,
506
- message_type: type,
507
- payload: normalizedPayload,
508
- };
509
- }
510
-
511
- async function requestHub(operation, body, timeoutMs = 3000, fallback = null) {
512
- const viaPipe =
513
- operation.transport === "command"
514
- ? await pipeCommand(operation.action, body, timeoutMs)
515
- : await pipeQuery(operation.action, body, timeoutMs);
516
- if (viaPipe) {
517
- return { transport: "pipe", result: viaPipe };
518
- }
519
-
520
- const viaHttp = operation.httpPath
521
- ? await requestJson(operation.httpPath, {
522
- method: operation.httpMethod || "POST",
523
- body: operation.httpMethod === "GET" ? undefined : body,
524
- timeoutMs: Math.max(timeoutMs, 5000),
525
- })
526
- : null;
527
- if (viaHttp) {
528
- return { transport: "http", result: viaHttp };
529
- }
530
-
531
- if (!fallback) return null;
532
- const viaFallback = await fallback();
533
- if (!viaFallback) return null;
534
- return { transport: "fallback", result: viaFallback };
535
- }
536
-
537
- function unavailableResult() {
538
- return { ok: false, reason: "hub_unavailable" };
539
- }
540
-
541
- function nonEmptyString(value, fallback = "") {
542
- const normalized = String(value ?? "").trim();
543
- return normalized || fallback;
544
- }
545
-
546
- function readBoolean(value) {
547
- return ["1", "true", "yes"].includes(nonEmptyString(value).toLowerCase());
548
- }
549
-
550
- function sessionAgentId(sessionId) {
551
- return `codex-session-${sessionId.replace(/[^A-Za-z0-9._-]/g, "_")}`;
552
- }
553
-
554
- export function buildInteractiveSessionRegistrationPayload(args = {}) {
555
- const sessionId = nonEmptyString(args["session-id"]);
556
- if (!sessionId) return null;
557
-
558
- const cwd = nonEmptyString(args.cwd, process.cwd());
559
- const worktreePath = nonEmptyString(args["worktree-path"], cwd);
560
- const host = nonEmptyString(args.host, "local");
561
- const sessionKind = nonEmptyString(args["session-kind"], "interactive");
562
- const agentId = nonEmptyString(args.agent, sessionAgentId(sessionId));
563
- const metadata = {
564
- pid: process.ppid,
565
- registered_at: Date.now(),
566
- cwd,
567
- worktree_path: worktreePath,
568
- branch: nonEmptyString(args.branch),
569
- host,
570
- session_kind: sessionKind,
571
- is_remote: readBoolean(args["is-remote"]),
572
- ...(nonEmptyString(args["tmux-session"])
573
- ? { tmux_session: nonEmptyString(args["tmux-session"]) }
574
- : {}),
575
- ...(nonEmptyString(args["tmux-pane"])
576
- ? { tmux_pane: nonEmptyString(args["tmux-pane"]) }
577
- : {}),
578
- ...(nonEmptyString(args["omx-session-id"])
579
- ? { omx_session_id: nonEmptyString(args["omx-session-id"]) }
580
- : {}),
581
- ...(nonEmptyString(args["codex-session-id"])
582
- ? { codex_session_id: nonEmptyString(args["codex-session-id"]) }
583
- : {}),
584
- };
585
-
586
- return {
587
- sessionId,
588
- cwd,
589
- worktreePath,
590
- branch: nonEmptyString(args.branch),
591
- host,
592
- sessionKind,
593
- isRemote: readBoolean(args["is-remote"]),
594
- agent_id: agentId,
595
- cli: nonEmptyString(args.cli, "codex"),
596
- capabilities: args.capabilities
597
- ? String(args.capabilities).split(",")
598
- : ["code"],
599
- topics: args.topics ? String(args.topics).split(",") : [],
600
- heartbeat_ttl_ms: 300000,
601
- session_id: sessionId,
602
- metadata,
603
- };
604
- }
605
-
606
84
  function emitJson(payload) {
607
85
  if (payload !== undefined) {
608
86
  console.log(JSON.stringify(payload));
@@ -610,613 +88,79 @@ function emitJson(payload) {
610
88
  return payload?.ok !== false;
611
89
  }
612
90
 
613
- const HUB_STORE_CLI_VALUES = new Set(["codex", "gemini", "claude", "other"]);
614
- function normalizeCliForStore(cli) {
615
- return HUB_STORE_CLI_VALUES.has(cli) ? cli : "other";
616
- }
617
-
618
- async function cmdRegister(args) {
619
- const interactiveSession = buildInteractiveSessionRegistrationPayload(args);
620
- if (interactiveSession) {
621
- const result = await requestJson("/synapse/register", {
622
- body: interactiveSession,
623
- timeoutMs: 3000,
624
- });
625
- return emitJson(result || unavailableResult());
626
- }
627
-
628
- const agentId = args.agent;
629
- const timeoutSec = parseInt(args.timeout || "600", 10);
630
- const rawCli = args.cli || "other";
631
- const outcome = await requestHub(HUB_OPERATIONS.register, {
632
- agent_id: agentId,
633
- cli: normalizeCliForStore(rawCli),
634
- timeout_sec: timeoutSec,
635
- heartbeat_ttl_ms: (timeoutSec + 120) * 1000,
636
- topics: args.topics ? args.topics.split(",") : [],
637
- capabilities: args.capabilities ? args.capabilities.split(",") : ["code"],
638
- metadata: {
639
- cli: rawCli,
640
- pid: process.ppid,
641
- registered_at: Date.now(),
642
- },
643
- });
644
- const result = outcome?.result;
645
-
646
- if (result?.ok) {
647
- return emitJson({
648
- ok: true,
649
- agent_id: agentId,
650
- lease_id: result.data?.lease_id,
651
- presence_generation: result.data?.presence_generation,
652
- lease_expires_ms: result.data?.lease_expires_ms,
653
- pipe_path: result.data?.pipe_path || getHubPipePath(),
654
- hub_instance_id: result.data?.hub_instance_id,
655
- store_fingerprint: result.data?.store_fingerprint,
656
- });
657
- }
658
-
659
- return emitJson(result || unavailableResult());
660
- }
661
-
662
- async function cmdHeartbeat(args) {
663
- const outcome = await requestHub(HUB_OPERATIONS.heartbeat, {
664
- agent_id: args.agent,
665
- ttl_ms: args["ttl-ms"] != null ? Number(args["ttl-ms"]) : undefined,
666
- presence_generation:
667
- args.generation != null ? Number(args.generation) : undefined,
668
- hub_instance_id: args["hub-instance-id"],
669
- store_fingerprint: args["store-fingerprint"],
670
- });
671
- return emitJson(outcome?.result || unavailableResult());
672
- }
673
-
674
- async function cmdResult(args) {
675
- let output = "";
676
- if (args.file && existsSync(args.file)) {
677
- output = readFileSync(args.file, "utf8").slice(0, 49152);
678
- }
679
-
680
- const defaultPayload = {
681
- agent_id: args.agent,
682
- exit_code: parseInt(args["exit-code"] || "0", 10),
683
- output_length: output.length,
684
- output_preview: output.slice(0, 4096),
685
- output_file: args.file || null,
686
- completed_at: Date.now(),
687
- };
688
-
689
- const outcome = await requestHub(HUB_OPERATIONS.result, {
690
- agent_id: args.agent,
691
- topic: args.topic || "task.result",
692
- payload: args.payload
693
- ? parseJsonSafe(args.payload, defaultPayload)
694
- : defaultPayload,
695
- trace_id: args.trace || undefined,
696
- correlation_id: args.correlation || undefined,
697
- });
698
- const result = outcome?.result;
699
-
700
- if (result?.ok) {
701
- return emitJson({ ok: true, message_id: result.data?.message_id });
702
- }
703
-
704
- return emitJson(result || unavailableResult());
705
- }
706
-
707
- async function cmdControl(args) {
708
- const outcome = await requestHub(HUB_OPERATIONS.control, {
709
- from_agent: args.from || "lead",
710
- to_agent: args.to,
711
- command: args.command,
712
- reason: args.reason || "",
713
- payload: args.payload ? parseJsonSafe(args.payload, {}) : {},
714
- trace_id: args.trace || undefined,
715
- correlation_id: args.correlation || undefined,
716
- ttl_ms: args["ttl-ms"] != null ? Number(args["ttl-ms"]) : undefined,
717
- });
718
- const result = outcome?.result;
719
- return emitJson(result || unavailableResult());
720
- }
721
-
722
- async function cmdHandoff(args) {
723
- const from = args.from || args[1];
724
- const to = args.to || args[2];
725
- const payload = parseJsonSafe(args.payload || args[3] || "{}", {});
726
- const outcome = await requestHub(
727
- HUB_OPERATIONS.handoff,
728
- buildHandoffBody(from, to, payload),
729
- );
730
- const result = outcome?.result;
731
- return emitJson(result || unavailableResult());
732
- }
733
-
734
- async function cmdPublish(args) {
735
- const from = args.from || args[1];
736
- const to = args.to || args[2];
737
- const type = args.type || args[3] || "event";
738
- const payload = parseJsonSafe(args.payload || args[4] || "{}", {});
739
- const outcome = await requestHub(
740
- HUB_OPERATIONS.publish,
741
- buildPublishBody(from, to, type, payload),
742
- );
743
- const result = outcome?.result;
744
- return emitJson(result || unavailableResult());
745
- }
746
-
747
- async function cmdTakeoverRole(args) {
748
- const role = args.role || args[1] || "cto";
749
- const agentId = args.agent || args["agent-id"] || args[2];
750
- const outcome = await requestHub(HUB_OPERATIONS.takeoverRole, {
751
- role,
752
- agent_id: agentId,
753
- reason: args.reason || "manual",
754
- requested_by: args["requested-by"] || "bridge",
755
- hub_instance_id: args["hub-instance-id"],
756
- store_fingerprint: args["store-fingerprint"],
757
- });
758
- const result = outcome?.result;
759
- return emitJson(result || unavailableResult());
760
- }
761
-
762
- async function cmdSendInput(args) {
763
- const outcome = await requestHub(HUB_OPERATIONS.sendInput, {
764
- session_id: args["session-id"],
765
- text: args.text,
766
- });
767
- const result = outcome?.result;
768
- return emitJson(result || unavailableResult());
769
- }
770
-
771
- async function cmdContext(args) {
772
- const outcome = await requestHub(HUB_OPERATIONS.context, {
773
- agent_id: args.agent,
774
- presence_generation:
775
- args.generation != null ? Number(args.generation) : undefined,
776
- topics: args.topics ? args.topics.split(",") : undefined,
777
- max_messages: parseInt(args.max || "10", 10),
778
- auto_ack: true,
779
- });
780
- const result = outcome?.result;
781
-
782
- if (result?.ok && result.data?.messages?.length) {
783
- const parts = result.data.messages.map((message, index) => {
784
- const payload =
785
- typeof message.payload === "string"
786
- ? message.payload
787
- : JSON.stringify(message.payload, null, 2);
788
- return `=== Context ${index + 1}: ${message.from_agent || "unknown"} (${message.topic || "unknown"}) ===\n${payload}`;
789
- });
790
- const combined = parts.join("\n\n");
791
-
792
- if (args.out) {
793
- writeFileSync(args.out, combined, "utf8");
794
- return emitJson({
795
- ok: true,
796
- count: result.data.messages.length,
797
- file: args.out,
798
- });
799
- } else {
800
- return emitJson({
801
- ok: true,
802
- count: result.data.messages.length,
803
- context: combined,
804
- });
805
- }
806
- }
807
-
808
- if (result?.ok) {
809
- if (args.out) {
810
- return emitJson({ ok: true, count: 0 });
811
- }
812
- return emitJson({ ok: true, count: 0, context: "" });
813
- }
814
-
815
- return emitJson(result || unavailableResult());
816
- }
817
-
818
- async function cmdDeregister(args) {
819
- const outcome = await requestHub(HUB_OPERATIONS.deregister, {
820
- agent_id: args.agent,
821
- presence_generation:
822
- args.generation != null ? Number(args.generation) : undefined,
823
- hub_instance_id: args["hub-instance-id"],
824
- store_fingerprint: args["store-fingerprint"],
825
- });
826
- const result = outcome?.result;
827
-
828
- return emitJson(result || unavailableResult());
829
- }
830
-
831
- async function cmdAssignAsync(args) {
832
- const outcome = await requestHub(HUB_OPERATIONS.assignAsync, {
833
- supervisor_agent: args["supervisor-agent"],
834
- worker_agent: args["worker-agent"],
835
- task: args.task,
836
- topic: args.topic || "assign.job",
837
- payload: args.payload ? parseJsonSafe(args.payload, {}) : {},
838
- priority: args.priority != null ? Number(args.priority) : undefined,
839
- ttl_ms: args["ttl-ms"] != null ? Number(args["ttl-ms"]) : undefined,
840
- timeout_ms:
841
- args["timeout-ms"] != null ? Number(args["timeout-ms"]) : undefined,
842
- max_retries:
843
- args["max-retries"] != null ? Number(args["max-retries"]) : undefined,
844
- trace_id: args.trace || undefined,
845
- correlation_id: args.correlation || undefined,
846
- });
847
- const result = outcome?.result;
848
- return emitJson(result || unavailableResult());
849
- }
850
-
851
- async function cmdAssignResult(args) {
852
- const outcome = await requestHub(HUB_OPERATIONS.assignResult, {
853
- job_id: args["job-id"],
854
- worker_agent: args["worker-agent"],
855
- status: args.status,
856
- attempt: args.attempt != null ? Number(args.attempt) : undefined,
857
- result: args.result ? parseJsonSafe(args.result, null) : undefined,
858
- error: args.error ? parseJsonSafe(args.error, null) : undefined,
859
- payload: args.payload ? parseJsonSafe(args.payload, {}) : {},
860
- metadata: args.metadata ? parseJsonSafe(args.metadata, {}) : {},
861
- });
862
- const result = outcome?.result;
863
- return emitJson(result || unavailableResult());
864
- }
865
-
866
- async function cmdAssignStatus(args) {
867
- const outcome = await requestHub(HUB_OPERATIONS.assignStatus, {
868
- job_id: args["job-id"],
869
- });
870
- const result = outcome?.result;
871
- return emitJson(result || unavailableResult());
872
- }
873
-
874
- async function cmdAssignRetry(args) {
875
- const outcome = await requestHub(HUB_OPERATIONS.assignRetry, {
876
- job_id: args["job-id"],
877
- reason: args.reason,
878
- requested_by: args["requested-by"],
879
- });
880
- const result = outcome?.result;
881
- return emitJson(result || unavailableResult());
91
+ function splitList(value) {
92
+ return value
93
+ .split(",")
94
+ .map((item) => item.trim())
95
+ .filter(Boolean);
882
96
  }
883
97
 
884
98
  async function cmdTeamInfo(args) {
885
- const body = {
886
- team_name: args.team,
887
- include_members: true,
888
- include_paths: true,
889
- };
890
- const outcome = await requestHub(
891
- HUB_OPERATIONS.teamInfo,
892
- body,
893
- 3000,
894
- async () => {
895
- const { teamInfo } = await import("./team/nativeProxy.mjs");
896
- return await teamInfo(body);
897
- },
99
+ const { teamInfo } = await import("./team/nativeProxy.mjs");
100
+ return emitJson(
101
+ await teamInfo({
102
+ team_name: args.team,
103
+ include_members: true,
104
+ include_paths: true,
105
+ }),
898
106
  );
899
- const result = outcome?.result;
900
- return emitJson(result || unavailableResult());
901
107
  }
902
108
 
903
109
  async function cmdTeamTaskList(args) {
904
- const body = {
905
- team_name: args.team,
906
- owner: args.owner,
907
- statuses: args.statuses
908
- ? args.statuses
909
- .split(",")
910
- .map((status) => status.trim())
911
- .filter(Boolean)
912
- : [],
913
- include_internal: !!args["include-internal"],
914
- limit: parseInt(args.limit || "200", 10),
915
- };
916
- const outcome = await requestHub(
917
- HUB_OPERATIONS.teamTaskList,
918
- body,
919
- 3000,
920
- async () => {
921
- const { teamTaskList } = await import("./team/nativeProxy.mjs");
922
- return await teamTaskList(body);
923
- },
110
+ const { teamTaskList } = await import("./team/nativeProxy.mjs");
111
+ return emitJson(
112
+ await teamTaskList({
113
+ team_name: args.team,
114
+ owner: args.owner,
115
+ statuses: args.statuses ? splitList(args.statuses) : [],
116
+ include_internal: !!args["include-internal"],
117
+ limit: parseInt(args.limit || "200", 10),
118
+ }),
924
119
  );
925
- const result = outcome?.result;
926
- return emitJson(result || unavailableResult());
927
120
  }
928
121
 
929
122
  async function cmdTeamTaskUpdate(args) {
930
- const body = {
931
- team_name: args.team,
932
- task_id: args["task-id"],
933
- claim: !!args.claim,
934
- owner: args.owner,
935
- status: args.status,
936
- subject: args.subject,
937
- description: args.description,
938
- activeForm: args["active-form"],
939
- add_blocks: args["add-blocks"]
940
- ? args["add-blocks"]
941
- .split(",")
942
- .map((value) => value.trim())
943
- .filter(Boolean)
944
- : undefined,
945
- add_blocked_by: args["add-blocked-by"]
946
- ? args["add-blocked-by"]
947
- .split(",")
948
- .map((value) => value.trim())
949
- .filter(Boolean)
950
- : undefined,
951
- metadata_patch: args["metadata-patch"]
952
- ? parseJsonSafe(args["metadata-patch"], null)
953
- : undefined,
954
- if_match_mtime_ms:
955
- args["if-match-mtime-ms"] != null
956
- ? Number(args["if-match-mtime-ms"])
123
+ const { teamTaskUpdate } = await import("./team/nativeProxy.mjs");
124
+ return emitJson(
125
+ await teamTaskUpdate({
126
+ team_name: args.team,
127
+ task_id: args["task-id"],
128
+ claim: !!args.claim,
129
+ owner: args.owner,
130
+ status: args.status,
131
+ subject: args.subject,
132
+ description: args.description,
133
+ activeForm: args["active-form"],
134
+ add_blocks: args["add-blocks"]
135
+ ? splitList(args["add-blocks"])
957
136
  : undefined,
958
- actor: args.actor,
959
- };
960
- const outcome = await requestHub(
961
- HUB_OPERATIONS.teamTaskUpdate,
962
- body,
963
- 3000,
964
- async () => {
965
- const { teamTaskUpdate } = await import("./team/nativeProxy.mjs");
966
- return await teamTaskUpdate(body);
967
- },
137
+ add_blocked_by: args["add-blocked-by"]
138
+ ? splitList(args["add-blocked-by"])
139
+ : undefined,
140
+ metadata_patch: args["metadata-patch"]
141
+ ? parseJsonSafe(args["metadata-patch"], null)
142
+ : undefined,
143
+ if_match_mtime_ms:
144
+ args["if-match-mtime-ms"] != null
145
+ ? Number(args["if-match-mtime-ms"])
146
+ : undefined,
147
+ actor: args.actor,
148
+ }),
968
149
  );
969
- const result = outcome?.result;
970
- return emitJson(result || unavailableResult());
971
150
  }
972
151
 
973
152
  async function cmdTeamSendMessage(args) {
974
- const body = {
975
- team_name: args.team,
976
- from: args.from,
977
- to: args.to || "team-lead",
978
- text: args.text,
979
- summary: args.summary,
980
- color: args.color || "blue",
981
- };
982
- const outcome = await requestHub(
983
- HUB_OPERATIONS.teamSendMessage,
984
- body,
985
- 3000,
986
- async () => {
987
- const { teamSendMessage } = await import("./team/nativeProxy.mjs");
988
- return await teamSendMessage(body);
989
- },
990
- );
991
- const result = outcome?.result;
992
- return emitJson(result || unavailableResult());
993
- }
994
-
995
- function getHubDbPath() {
996
- return getPipelineStateDbPath(PROJECT_ROOT);
997
- }
998
-
999
- async function cmdPipelineState(args) {
1000
- const outcome = await requestHub(
1001
- HUB_OPERATIONS.pipelineState,
1002
- { team_name: args.team },
1003
- 3000,
1004
- async () => {
1005
- try {
1006
- const { default: Database } = await import("better-sqlite3");
1007
- const { ensurePipelineTable, readPipelineState } = await import(
1008
- "./pipeline/state.mjs"
1009
- );
1010
- const dbPath = getHubDbPath();
1011
- if (!existsSync(dbPath)) {
1012
- return { ok: false, error: "hub_db_not_found" };
1013
- }
1014
- const db = new Database(dbPath, { readonly: true });
1015
- ensurePipelineTable(db);
1016
- const state = readPipelineState(db, args.team);
1017
- db.close();
1018
- return state
1019
- ? { ok: true, data: state }
1020
- : { ok: false, error: "pipeline_not_found" };
1021
- } catch (e) {
1022
- return { ok: false, error: e.message };
1023
- }
1024
- },
1025
- );
1026
- const result = outcome?.result;
1027
- return emitJson(result || unavailableResult());
1028
- }
1029
-
1030
- // Best-effort phase-manager bridge (silent on failure — pipeline 동작 영향 X)
1031
- async function writePhaseSafely(runId, phase, status) {
1032
- if (!runId) return;
1033
- try {
1034
- const mod = await import("./lib/phase-manager.mjs");
1035
- if (typeof mod?.writePhase === "function") {
1036
- await mod.writePhase(runId, phase, status);
1037
- }
1038
- if (typeof mod?.syncToGstack === "function") {
1039
- const slug = process.cwd().split(/[\\/]/).pop();
1040
- await mod.syncToGstack(runId, slug);
1041
- }
1042
- } catch {
1043
- /* silent — phase tracking is best-effort */
1044
- }
1045
- }
1046
-
1047
- async function writePhaseSafelyFromLegacy(runId, legacyStatus) {
1048
- if (!runId || !legacyStatus) return;
1049
- try {
1050
- const mod = await import("./lib/phase-manager.mjs");
1051
- if (typeof mod?.coerceLegacyPhase !== "function") return;
1052
- const coerced = mod.coerceLegacyPhase(legacyStatus);
1053
- if (!coerced) return;
1054
- if (coerced === "complete") {
1055
- const current =
1056
- typeof mod.readPhase === "function" ? await mod.readPhase(runId) : null;
1057
- const lastPhase = current?.phase || current?.lastPhase;
1058
- if (lastPhase) await mod.writePhase(runId, lastPhase, "complete");
1059
- return;
1060
- }
1061
- await mod.writePhase(runId, coerced, "active");
1062
- if (typeof mod?.syncToGstack === "function") {
1063
- const slug = process.cwd().split(/[\\/]/).pop();
1064
- await mod.syncToGstack(runId, slug);
1065
- }
1066
- } catch {
1067
- /* silent */
1068
- }
1069
- }
1070
-
1071
- async function cmdPipelineAdvance(args) {
1072
- const body = {
1073
- team_name: args.team,
1074
- phase: args.status, // --status를 phase로 재활용
1075
- };
1076
- const outcome = await requestHub(
1077
- HUB_OPERATIONS.pipelineAdvance,
1078
- body,
1079
- 3000,
1080
- async () => {
1081
- try {
1082
- const { default: Database } = await import("better-sqlite3");
1083
- const { createPipeline } = await import("./pipeline/index.mjs");
1084
- const dbPath = getHubDbPath();
1085
- if (!existsSync(dbPath)) {
1086
- return { ok: false, error: "hub_db_not_found" };
1087
- }
1088
- const db = new Database(dbPath);
1089
- const pipeline = createPipeline(db, args.team);
1090
- const advanceResult = pipeline.advance(args.status);
1091
- db.close();
1092
- return advanceResult;
1093
- } catch (e) {
1094
- return { ok: false, error: e.message };
1095
- }
1096
- },
1097
- );
1098
- const result = outcome?.result;
1099
- if (args.team && args.status && result?.ok !== false) {
1100
- void writePhaseSafelyFromLegacy(args.team, args.status);
1101
- }
1102
- return emitJson(result || unavailableResult());
1103
- }
1104
-
1105
- async function cmdPipelineInit(args) {
1106
- const outcome = await requestHub(HUB_OPERATIONS.pipelineInit, {
1107
- team_name: args.team,
1108
- fix_max: args["fix-max"] != null ? Number(args["fix-max"]) : undefined,
1109
- ralph_max:
1110
- args["ralph-max"] != null ? Number(args["ralph-max"]) : undefined,
1111
- });
1112
- const result = outcome?.result;
1113
- if (args.team && result?.ok !== false) {
1114
- void writePhaseSafely(args.team, "Strategy", "active");
1115
- }
1116
- return emitJson(result || unavailableResult());
1117
- }
1118
-
1119
- async function cmdPipelineList() {
1120
- const outcome = await requestHub(HUB_OPERATIONS.pipelineList, {});
1121
- const result = outcome?.result;
1122
- return emitJson(result || unavailableResult());
1123
- }
1124
-
1125
- async function cmdPing() {
1126
- const outcome = await requestHub(
1127
- HUB_OPERATIONS.hubStatus,
1128
- { scope: "hub" },
1129
- 2000,
1130
- );
1131
-
1132
- if (outcome?.transport === "pipe" && outcome.result?.ok) {
1133
- return emitJson({
1134
- ok: true,
1135
- hub: outcome.result.data?.hub?.state || "healthy",
1136
- pipe_path: getHubPipePath(),
1137
- transport: "pipe",
1138
- });
1139
- }
1140
-
1141
- if (outcome?.transport === "http" && outcome.result) {
1142
- const data = outcome.result;
1143
- return emitJson({
1144
- ok: true,
1145
- hub: data.hub?.state,
1146
- sessions: data.sessions,
1147
- pipe_path: data.pipe?.path || data.pipe_path || null,
1148
- transport: "http",
1149
- });
1150
- }
1151
-
1152
- return emitJson(unavailableResult());
1153
- }
1154
-
1155
- async function cmdDelegatorDelegate(args) {
1156
- const body = {
1157
- prompt: args.text || args.prompt,
1158
- provider: args.provider || "auto",
1159
- mode: args.mode || "sync",
1160
- agent_type: args.agent || "executor",
1161
- mcp_profile: args["mcp-profile"] || "auto",
1162
- session_key: args["session-key"] || undefined,
1163
- timeout_ms:
1164
- args["timeout-ms"] != null ? Number(args["timeout-ms"]) : undefined,
1165
- };
1166
- const timeoutMs = body.mode === "async" ? 10000 : 120000;
1167
- const outcome = await requestHub(
1168
- HUB_OPERATIONS.delegatorDelegate,
1169
- body,
1170
- timeoutMs,
1171
- );
1172
- return emitJson(outcome?.result || unavailableResult());
1173
- }
1174
-
1175
- async function cmdDelegatorReply(args) {
1176
- const body = {
1177
- job_id: args["job-id"],
1178
- reply: args.text || args.reply,
1179
- done: !!args.done,
1180
- };
1181
- const outcome = await requestHub(HUB_OPERATIONS.delegatorReply, body, 120000);
1182
- return emitJson(outcome?.result || unavailableResult());
1183
- }
1184
-
1185
- async function cmdDelegatorStatus(args) {
1186
- const body = {
1187
- job_id: args["job-id"],
1188
- };
1189
- const outcome = await requestHub(HUB_OPERATIONS.delegatorStatus, body, 5000);
1190
- return emitJson(outcome?.result || unavailableResult());
1191
- }
1192
-
1193
- async function cmdHitlRequest(args) {
1194
- const body = {
1195
- kind: args[1],
1196
- prompt: args[2],
1197
- requester_agent: args[3] || "cli",
1198
- };
1199
- const outcome = await requestHub(
1200
- HUB_OPERATIONS["hitl-request"],
1201
- body,
1202
- 120000,
153
+ const { teamSendMessage } = await import("./team/nativeProxy.mjs");
154
+ return emitJson(
155
+ await teamSendMessage({
156
+ team_name: args.team,
157
+ from: args.from,
158
+ to: args.to || "team-lead",
159
+ text: args.text,
160
+ summary: args.summary,
161
+ color: args.color || "blue",
162
+ }),
1203
163
  );
1204
- return emitJson(outcome?.result || unavailableResult());
1205
- }
1206
-
1207
- async function cmdHitlSubmit(args) {
1208
- const body = {
1209
- request_id: args[1],
1210
- action: args[2] || "accept",
1211
- content: args[3],
1212
- };
1213
- const outcome = await requestHub(HUB_OPERATIONS["hitl-submit"], body, 120000);
1214
- return emitJson(outcome?.result || unavailableResult());
1215
- }
1216
-
1217
- async function cmdHitlPending() {
1218
- const outcome = await requestHub(HUB_OPERATIONS["hitl-pending"], {}, 5000);
1219
- return emitJson(outcome?.result || unavailableResult());
1220
164
  }
1221
165
 
1222
166
  async function loadDaemonControl() {
@@ -1521,9 +465,8 @@ async function cmdDaemonWait(args) {
1521
465
  if (!payload.short && !payload.sessionId)
1522
466
  throw new Error("short or sessionId is required");
1523
467
  const { probeClaudeDaemonCandidates } = await loadDaemonControl();
1524
- const { findClaudeTranscript, readClaudeTranscript } = await import(
1525
- "./team/claude-transcript.mjs"
1526
- );
468
+ const { claudeWaitVerdict, findClaudeTranscript, readClaudeTranscript } =
469
+ await import("./team/claude-transcript.mjs");
1527
470
  const timeoutMs = numericOption(payload.timeoutMs, 30_000);
1528
471
  const pollIntervalMs = Math.max(
1529
472
  10,
@@ -1591,28 +534,8 @@ async function cmdDaemonWait(args) {
1591
534
  target: probe.target,
1592
535
  ...daemonProbeMetadata(probe),
1593
536
  };
1594
- if (transcript?.error) {
1595
- return emitJson({
1596
- ...last,
1597
- ok: false,
1598
- status: "failed",
1599
- error: transcript.error,
1600
- });
1601
- }
1602
- if (
1603
- (idle || transcript?.sectionClosed) &&
1604
- transcript?.userSeen &&
1605
- transcript.turnEnded
1606
- ) {
1607
- if (!transcript.response.trim())
1608
- return emitJson({
1609
- ...last,
1610
- ok: false,
1611
- status: "failed",
1612
- error: "turn ended without assistant text",
1613
- });
1614
- return emitJson({ ...last, status: "completed", done: true });
1615
- }
537
+ const verdict = claudeWaitVerdict(transcript, idle);
538
+ if (verdict) return emitJson({ ...last, ...verdict });
1616
539
  if (Date.now() >= deadline) break;
1617
540
  await new Promise((resolve) =>
1618
541
  setTimeout(resolve, Math.min(pollIntervalMs, deadline - Date.now())),
@@ -1837,8 +760,7 @@ async function cmdRetryStatus(args) {
1837
760
  return true;
1838
761
  }
1839
762
 
1840
- // intervene-run은 hub transport를 거치지 않는다. heartbeat가 hub 장애 중에도
1841
- // process/daemon/pane 사다리를 실행할 수 있도록 순수 로컬 모듈만 호출한다.
763
+ // intervene-run은 순수 로컬 모듈만 호출한다.
1842
764
  async function cmdInterveneRun(args) {
1843
765
  try {
1844
766
  const payload = readBridgePayload(args);
@@ -1904,34 +826,6 @@ export async function main(argv = process.argv.slice(2)) {
1904
826
  const args = parseArgs(argv.slice(1));
1905
827
 
1906
828
  switch (cmd) {
1907
- case "register":
1908
- return await cmdRegister(args);
1909
- case "heartbeat":
1910
- return await cmdHeartbeat(args);
1911
- case "result":
1912
- return await cmdResult(args);
1913
- case "control":
1914
- return await cmdControl(args);
1915
- case "handoff":
1916
- return await cmdHandoff(args);
1917
- case "publish":
1918
- return await cmdPublish(args);
1919
- case "takeover-role":
1920
- return await cmdTakeoverRole(args);
1921
- case "send-input":
1922
- return await cmdSendInput(args);
1923
- case "context":
1924
- return await cmdContext(args);
1925
- case "deregister":
1926
- return await cmdDeregister(args);
1927
- case "assign-async":
1928
- return await cmdAssignAsync(args);
1929
- case "assign-result":
1930
- return await cmdAssignResult(args);
1931
- case "assign-status":
1932
- return await cmdAssignStatus(args);
1933
- case "assign-retry":
1934
- return await cmdAssignRetry(args);
1935
829
  case "team-info":
1936
830
  return await cmdTeamInfo(args);
1937
831
  case "team-task-list":
@@ -1940,28 +834,6 @@ export async function main(argv = process.argv.slice(2)) {
1940
834
  return await cmdTeamTaskUpdate(args);
1941
835
  case "team-send-message":
1942
836
  return await cmdTeamSendMessage(args);
1943
- case "pipeline-state":
1944
- return await cmdPipelineState(args);
1945
- case "pipeline-advance":
1946
- return await cmdPipelineAdvance(args);
1947
- case "pipeline-init":
1948
- return await cmdPipelineInit(args);
1949
- case "pipeline-list":
1950
- return await cmdPipelineList(args);
1951
- case "ping":
1952
- return await cmdPing(args);
1953
- case "delegator-delegate":
1954
- return await cmdDelegatorDelegate(args);
1955
- case "delegator-reply":
1956
- return await cmdDelegatorReply(args);
1957
- case "delegator-status":
1958
- return await cmdDelegatorStatus(args);
1959
- case "hitl-request":
1960
- return await cmdHitlRequest(args);
1961
- case "hitl-submit":
1962
- return await cmdHitlSubmit(args);
1963
- case "hitl-pending":
1964
- return await cmdHitlPending(args);
1965
837
  case "daemon-probe":
1966
838
  return await cmdDaemonProbe(args);
1967
839
  case "daemon-attach":
@@ -1978,7 +850,7 @@ export async function main(argv = process.argv.slice(2)) {
1978
850
  return await cmdInterveneRun(args);
1979
851
  default:
1980
852
  console.error(
1981
- "사용법: bridge.mjs <register|heartbeat|result|control|handoff|publish|takeover-role|send-input|context|deregister|assign-async|assign-result|assign-status|assign-retry|team-info|team-task-list|team-task-update|team-send-message|pipeline-state|pipeline-advance|pipeline-init|pipeline-list|ping|delegator-delegate|delegator-reply|delegator-status|hitl-request|hitl-submit|hitl-pending|daemon-probe|daemon-attach|daemon-wait|daemon-interrupt|retry-run|retry-status|intervene-run> [--옵션]",
853
+ "사용법: bridge.mjs <team-info|team-task-list|team-task-update|team-send-message|daemon-probe|daemon-attach|daemon-wait|daemon-interrupt|retry-run|retry-status|intervene-run> [--옵션]",
1982
854
  );
1983
855
  process.exit(1);
1984
856
  }