@triflux/core 10.50.2 → 10.52.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 (84) hide show
  1. package/hooks/agy-session-hook.mjs +11 -56
  2. package/hooks/codex-session-hook.mjs +11 -101
  3. package/hub/bridge.mjs +65 -1250
  4. package/hub/cli-adapter-base.mjs +12 -90
  5. package/hub/codex-adapter.mjs +0 -1
  6. package/hub/codex-preflight.mjs +5 -28
  7. package/hub/index.mjs +3 -19
  8. package/hub/lib/process-utils.mjs +1 -156
  9. package/hub/lib/spawn-trace.mjs +3 -2
  10. package/hub/lib/timeout-defaults.mjs +0 -1
  11. package/hub/team/claude-daemon-control.mjs +1 -2
  12. package/hub/team/claude-session-projection.mjs +5 -73
  13. package/hub/team/nativeProxy.mjs +759 -0
  14. package/hub/team/session-context.mjs +1 -1
  15. package/hud/cli-policy.mjs +0 -22
  16. package/hud/constants.mjs +13 -26
  17. package/hud/hud-qos-status.mjs +42 -54
  18. package/hud/providers/gemini.mjs +125 -20
  19. package/hud/renderers.mjs +101 -122
  20. package/hud/utils.mjs +10 -6
  21. package/package.json +2 -6
  22. package/scripts/lib/agent-route-policy.mjs +143 -62
  23. package/scripts/lib/cli-claude.mjs +0 -14
  24. package/scripts/lib/env-probe.mjs +2 -131
  25. package/scripts/lib/gemini-profiles.mjs +2 -69
  26. package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
  27. package/scripts/lib/machine-profile.mjs +3 -17
  28. package/scripts/lib/mcp-filter.mjs +109 -53
  29. package/scripts/lib/mcp-guard-engine.mjs +40 -98
  30. package/scripts/lib/mcp-health.mjs +2 -2
  31. package/scripts/lib/mcp-server-catalog.mjs +2 -2
  32. package/scripts/lib/psmux-info.mjs +11 -59
  33. package/scripts/lib/session-presence.mjs +2 -100
  34. package/scripts/lib/skill-template.mjs +0 -245
  35. package/hub/adaptive-diagnostic.mjs +0 -350
  36. package/hub/adaptive-inject.mjs +0 -224
  37. package/hub/adaptive-memory.mjs +0 -277
  38. package/hub/adaptive.mjs +0 -172
  39. package/hub/assign-callbacks.mjs +0 -170
  40. package/hub/delegator/contracts.mjs +0 -38
  41. package/hub/delegator/index.mjs +0 -14
  42. package/hub/delegator/schema/delegator-tools.schema.json +0 -250
  43. package/hub/delegator/service.mjs +0 -336
  44. package/hub/delegator/tool-definitions.mjs +0 -35
  45. package/hub/dynamic-routing-engine.mjs +0 -511
  46. package/hub/hitl.mjs +0 -202
  47. package/hub/lib/known-errors.json +0 -72
  48. package/hub/lib/memory-store.mjs +0 -1001
  49. package/hub/lib/phase-manager.mjs +0 -493
  50. package/hub/lib/risk-tier.mjs +0 -53
  51. package/hub/lib/ssh-command.mjs +0 -231
  52. package/hub/lib/staged-file-detect.mjs +0 -43
  53. package/hub/lib/state-snapshot.mjs +0 -293
  54. package/hub/lib/tfx-route-args.mjs +0 -353
  55. package/hub/lib/trace-recorder.mjs +0 -153
  56. package/hub/lib/uuidv7.mjs +0 -45
  57. package/hub/memory-doctor.mjs +0 -640
  58. package/hub/middleware/quota-middleware.mjs +0 -70
  59. package/hub/middleware/request-logger.mjs +0 -191
  60. package/hub/paths.mjs +0 -24
  61. package/hub/pipeline/index.mjs +0 -163
  62. package/hub/pipeline/state.mjs +0 -191
  63. package/hub/pipeline/transitions.mjs +0 -131
  64. package/hub/reflexion.mjs +0 -290
  65. package/hub/role-contract.mjs +0 -314
  66. package/hub/router.mjs +0 -2026
  67. package/hub/routing-snapshot.mjs +0 -261
  68. package/hub/session-fingerprint.mjs +0 -418
  69. package/hub/state.mjs +0 -300
  70. package/hub/team/synapse-http.mjs +0 -311
  71. package/hub/team-bridge.mjs +0 -27
  72. package/mesh/index.mjs +0 -69
  73. package/mesh/mesh-budget.mjs +0 -137
  74. package/mesh/mesh-heartbeat.mjs +0 -100
  75. package/mesh/mesh-protocol.mjs +0 -96
  76. package/mesh/mesh-queue.mjs +0 -163
  77. package/mesh/mesh-registry.mjs +0 -78
  78. package/mesh/mesh-router.mjs +0 -77
  79. package/scripts/lib/claudemd-scanner.mjs +0 -223
  80. package/scripts/lib/context.mjs +0 -67
  81. package/scripts/lib/dynamic-route-cli.mjs +0 -107
  82. package/scripts/lib/handoff.mjs +0 -187
  83. package/scripts/lib/logger.mjs +0 -122
  84. package/scripts/lib/stealth-fetch.mjs +0 -176
@@ -1,353 +0,0 @@
1
- // hub/lib/tfx-route-args.mjs
2
- // Phase 3 Step B — tfx-auto / tfx-route 플래그 파서.
3
- // 설계 문서: .triflux/plans/phase3-lead-codex-ralph-escalate.md
4
- //
5
- // 입력: ARGUMENTS 문자열 또는 토큰 배열.
6
- // 출력: {cli, mode, parallel, retry, isolation, remote, lead, noClaudeNative,
7
- // maxIterations, task, warnings}.
8
- //
9
- // 기존 플래그 (Phase 2 v10.9.33+):
10
- // --cli {auto|codex|antigravity|claude}
11
- // --mode {quick|deep|consensus|live}
12
- // --parallel {1|N|swarm}
13
- // --retry {0|1|ralph|auto-escalate} (Phase 3 에서 ralph/auto-escalate 신규)
14
- // --isolation {none|worktree}
15
- // --remote {none|<host>}
16
- // --rounds <N> (--mode live 전용, 기본 4)
17
- //
18
- // Phase 3 신규:
19
- // --lead {claude|codex} (tfx-auto-codex 의미 흡수)
20
- // --no-claude-native (Claude native sub-agent 경로 disable)
21
- // --max-iterations <N> (ralph / auto-escalate 상한, 0=unlimited)
22
-
23
- import { hasCodeChange } from "./staged-file-detect.mjs";
24
-
25
- export const DEFAULT_OPTIONS = Object.freeze({
26
- cli: "auto",
27
- mode: "quick",
28
- parallel: "1",
29
- retry: "1",
30
- isolation: "none",
31
- remote: "none",
32
- lead: "claude",
33
- noClaudeNative: false,
34
- maxIterations: 0,
35
- rounds: 4,
36
- });
37
-
38
- const VALID_VALUES = Object.freeze({
39
- cli: ["auto", "codex", "antigravity", "claude"],
40
- mode: ["quick", "deep", "consensus", "live"],
41
- retry: ["0", "1", "ralph", "auto-escalate"],
42
- isolation: ["none", "worktree"],
43
- lead: ["claude", "codex"],
44
- });
45
-
46
- const VALUE_FLAGS = new Set([
47
- "--cli",
48
- "--mode",
49
- "--parallel",
50
- "--retry",
51
- "--isolation",
52
- "--remote",
53
- "--lead",
54
- "--max-iterations",
55
- "--rounds",
56
- ]);
57
-
58
- const BOOL_FLAGS = new Set(["--no-claude-native"]);
59
-
60
- export function parseArgs(input) {
61
- const tokens = Array.isArray(input)
62
- ? input.slice()
63
- : tokenize(String(input || ""));
64
- const opts = { ...DEFAULT_OPTIONS };
65
- const warnings = [];
66
- const taskTokens = [];
67
- const providedFlags = new Set();
68
-
69
- let i = 0;
70
- while (i < tokens.length) {
71
- const raw = tokens[i];
72
-
73
- if (BOOL_FLAGS.has(raw)) {
74
- providedFlags.add(raw);
75
- applyBool(opts, raw);
76
- i += 1;
77
- continue;
78
- }
79
-
80
- // --flag=value 지원
81
- const eqIdx = raw.indexOf("=");
82
- let flag = raw;
83
- let value = null;
84
- if (eqIdx > 0 && raw.startsWith("--")) {
85
- flag = raw.slice(0, eqIdx);
86
- value = raw.slice(eqIdx + 1);
87
- }
88
-
89
- if (VALUE_FLAGS.has(flag)) {
90
- providedFlags.add(flag);
91
- if (value === null) {
92
- const next = tokens[i + 1];
93
- if (next === undefined || next.startsWith("--")) {
94
- warnings.push(`${flag} needs a value`);
95
- i += 1;
96
- continue;
97
- }
98
- value = next;
99
- i += 2;
100
- } else {
101
- i += 1;
102
- }
103
- applyValue(opts, flag, value, warnings);
104
- continue;
105
- }
106
-
107
- if (raw.startsWith("--")) {
108
- warnings.push(`unknown flag: ${raw}`);
109
- i += 1;
110
- continue;
111
- }
112
-
113
- taskTokens.push(raw);
114
- i += 1;
115
- }
116
-
117
- validate(opts, warnings, providedFlags);
118
-
119
- return {
120
- ...opts,
121
- task: taskTokens.join(" ").trim(),
122
- warnings,
123
- };
124
- }
125
-
126
- function tokenize(str) {
127
- const tokens = [];
128
- let cur = "";
129
- let quote = null;
130
- for (const ch of str) {
131
- if (quote) {
132
- if (ch === quote) {
133
- quote = null;
134
- } else {
135
- cur += ch;
136
- }
137
- continue;
138
- }
139
- if (ch === '"' || ch === "'") {
140
- quote = ch;
141
- continue;
142
- }
143
- if (/\s/.test(ch)) {
144
- if (cur) {
145
- tokens.push(cur);
146
- cur = "";
147
- }
148
- continue;
149
- }
150
- cur += ch;
151
- }
152
- if (cur) tokens.push(cur);
153
- return tokens;
154
- }
155
-
156
- function applyBool(opts, flag) {
157
- switch (flag) {
158
- case "--no-claude-native":
159
- opts.noClaudeNative = true;
160
- break;
161
- }
162
- }
163
-
164
- function applyValue(opts, flag, value, warnings) {
165
- switch (flag) {
166
- case "--cli":
167
- if (value === "gemini") {
168
- opts.cli = "antigravity";
169
- warnings.push(
170
- "--cli gemini is deprecated; using --cli antigravity instead",
171
- );
172
- } else {
173
- opts.cli = value;
174
- }
175
- break;
176
- case "--mode":
177
- opts.mode = value;
178
- break;
179
- case "--parallel":
180
- opts.parallel = value;
181
- break;
182
- case "--retry":
183
- opts.retry = value;
184
- break;
185
- case "--isolation":
186
- opts.isolation = value;
187
- break;
188
- case "--remote":
189
- opts.remote = value;
190
- break;
191
- case "--lead":
192
- opts.lead = value;
193
- break;
194
- case "--max-iterations": {
195
- const n = Number.parseInt(value, 10);
196
- if (Number.isNaN(n) || n < 0) {
197
- warnings.push(
198
- `invalid --max-iterations=${value}, expected non-negative integer (0=unlimited)`,
199
- );
200
- } else {
201
- opts.maxIterations = n;
202
- }
203
- break;
204
- }
205
- case "--rounds": {
206
- const n = Number(value);
207
- if (!Number.isInteger(n) || n <= 0) {
208
- warnings.push(`invalid --rounds=${value}, expected positive integer`);
209
- } else {
210
- opts.rounds = n;
211
- }
212
- break;
213
- }
214
- }
215
- }
216
-
217
- function validate(opts, warnings, providedFlags = new Set()) {
218
- for (const key of Object.keys(VALID_VALUES)) {
219
- if (!VALID_VALUES[key].includes(opts[key])) {
220
- warnings.push(
221
- `invalid --${key}=${opts[key]}, expected one of ${VALID_VALUES[key].join("|")}`,
222
- );
223
- }
224
- }
225
- if (
226
- opts.parallel !== "1" &&
227
- opts.parallel !== "swarm" &&
228
- !/^\d+$/.test(opts.parallel)
229
- ) {
230
- warnings.push(`invalid --parallel=${opts.parallel}, expected 1|N|swarm`);
231
- }
232
-
233
- if (opts.mode === "live") {
234
- const ignored = [
235
- ["--parallel", "parallel", DEFAULT_OPTIONS.parallel],
236
- ["--isolation", "isolation", DEFAULT_OPTIONS.isolation],
237
- ["--remote", "remote", DEFAULT_OPTIONS.remote],
238
- ];
239
- for (const [flag, key, fallback] of ignored) {
240
- if (providedFlags.has(flag)) {
241
- warnings.push(`${flag} ignored with --mode live`);
242
- opts[key] = fallback;
243
- }
244
- }
245
- if (providedFlags.has("--retry") && opts.retry === "ralph") {
246
- warnings.push("--retry ralph ignored with --mode live");
247
- opts.retry = DEFAULT_OPTIONS.retry;
248
- }
249
- } else if (providedFlags.has("--rounds")) {
250
- warnings.push("--rounds ignored unless --mode live");
251
- opts.rounds = DEFAULT_OPTIONS.rounds;
252
- }
253
-
254
- const parallelOne = opts.parallel === "1" || opts.parallel === 1;
255
- if (parallelOne && opts.isolation === "worktree") {
256
- warnings.push(
257
- "--isolation worktree requires --parallel >=2 or swarm; forcing isolation=none",
258
- );
259
- opts.isolation = "none";
260
- }
261
- if (opts.remote !== "none" && opts.parallel !== "swarm") {
262
- warnings.push(
263
- `--remote ${opts.remote} ignored (requires --parallel swarm)`,
264
- );
265
- }
266
- }
267
-
268
- export { VALID_VALUES };
269
-
270
- /**
271
- * Decide dispatch mode based on parsed args and current git state.
272
- *
273
- * Issue #281: auto router code changes should use isolated swarm dispatch for
274
- * multi-task work unless the user explicitly selected local parallel mode.
275
- *
276
- * @param {object} args
277
- * @param {object} [opts]
278
- * @param {string} [opts.cwd]
279
- * @param {Function} [opts.detector]
280
- * @returns {{
281
- * mode: "single" | "multi" | "swarm",
282
- * escalated: boolean,
283
- * warning: string | null,
284
- * reason: string,
285
- * }}
286
- */
287
- export function decideDispatchMode(
288
- args = {},
289
- { cwd = process.cwd(), detector = hasCodeChange } = {},
290
- ) {
291
- const tasksCount = countTasks(args);
292
- const parallel = normalizeParallel(args.parallel);
293
- const hasUserParallel = parallel !== null && parallel !== "1";
294
- const hasUserSwarm = parallel === "swarm" || args.isolation === "worktree";
295
- const codeChanged = Boolean(detector({ cwd }));
296
-
297
- if (hasUserSwarm) {
298
- return {
299
- mode: "swarm",
300
- escalated: false,
301
- warning: null,
302
- reason: "user-explicit-swarm",
303
- };
304
- }
305
-
306
- if (hasUserParallel && codeChanged) {
307
- return {
308
- mode: "multi",
309
- escalated: false,
310
- warning: `[tfx-auto] WARNING: 코드 변경 감지 (cwd race 위험). --parallel swarm --isolation worktree 권장. 사용자 명시 --parallel=${args.parallel} 존중하여 multi 로 진행.`,
311
- reason: "user-explicit-multi-with-code-change",
312
- };
313
- }
314
-
315
- if (tasksCount >= 2 && codeChanged) {
316
- return {
317
- mode: "swarm",
318
- escalated: true,
319
- warning: `[tfx-auto] 코드 변경 ${tasksCount}건 태스크 + dirty cwd 감지. swarm dispatch 자동 escalate (--parallel swarm --isolation worktree). Issue #281.`,
320
- reason: "auto-escalate-code-change",
321
- };
322
- }
323
-
324
- if (tasksCount >= 2 || hasUserParallel) {
325
- return {
326
- mode: "multi",
327
- escalated: false,
328
- warning: null,
329
- reason: "multi-no-code-change",
330
- };
331
- }
332
-
333
- return {
334
- mode: "single",
335
- escalated: false,
336
- warning: null,
337
- reason: "single-task",
338
- };
339
- }
340
-
341
- function countTasks(args) {
342
- if (Array.isArray(args.tasks)) return args.tasks.length;
343
- if (typeof args.tasks === "number") return args.tasks;
344
- if (typeof args.tasks === "string") return args.tasks.trim() ? 1 : 0;
345
- if (typeof args.task === "string") return args.task.trim() ? 1 : 0;
346
- return args.tasks ?? 0;
347
- }
348
-
349
- function normalizeParallel(value) {
350
- if (value == null) return null;
351
- if (value === "swarm") return value;
352
- return String(value);
353
- }
@@ -1,153 +0,0 @@
1
- const VALID_CLIENTS = new Set(["codex", "claude", "gemini", "unknown"]);
2
- const VALID_PHASES = new Set(["ingress", "handler", "store", "event-loop"]);
3
- const SAMPLE_LIMIT = 1000;
4
-
5
- const categories = new Map();
6
- const workerDurations = [];
7
- const workerCounts = {
8
- spawned: 0,
9
- exited: 0,
10
- };
11
-
12
- function debugInvalid(kind, details) {
13
- if (process.env.TFX_TRACE_DEBUG !== "1") return;
14
- console.debug(`[trace-recorder] ignored invalid ${kind}`, details);
15
- }
16
-
17
- function normalizeClient(client) {
18
- const value = String(client || "unknown").toLowerCase();
19
- return VALID_CLIENTS.has(value) ? value : "unknown";
20
- }
21
-
22
- function normalizePhase(phase) {
23
- const value = String(phase || "handler");
24
- return VALID_PHASES.has(value) ? value : "handler";
25
- }
26
-
27
- function validDuration(durMs) {
28
- return Number.isFinite(durMs) && durMs >= 0;
29
- }
30
-
31
- function emptyStats() {
32
- return {
33
- count: 0,
34
- total: 0,
35
- min: null,
36
- max: null,
37
- samples: [],
38
- phases: {},
39
- };
40
- }
41
-
42
- function addDuration(stats, durMs, phase) {
43
- stats.count += 1;
44
- stats.total += durMs;
45
- stats.min = stats.min == null ? durMs : Math.min(stats.min, durMs);
46
- stats.max = stats.max == null ? durMs : Math.max(stats.max, durMs);
47
- stats.samples.push(durMs);
48
- if (stats.samples.length > SAMPLE_LIMIT) {
49
- stats.samples.splice(0, stats.samples.length - SAMPLE_LIMIT);
50
- }
51
-
52
- const phaseStats = stats.phases[phase] || emptyPhaseStats();
53
- phaseStats.count += 1;
54
- phaseStats.total += durMs;
55
- phaseStats.min =
56
- phaseStats.min == null ? durMs : Math.min(phaseStats.min, durMs);
57
- phaseStats.max =
58
- phaseStats.max == null ? durMs : Math.max(phaseStats.max, durMs);
59
- stats.phases[phase] = phaseStats;
60
- }
61
-
62
- function emptyPhaseStats() {
63
- return { count: 0, total: 0, min: null, max: null };
64
- }
65
-
66
- function percentile(samples, percentileValue) {
67
- if (!samples.length) return null;
68
- const sorted = [...samples].sort((a, b) => a - b);
69
- const index = Math.ceil((percentileValue / 100) * sorted.length) - 1;
70
- return sorted[Math.max(0, Math.min(sorted.length - 1, index))];
71
- }
72
-
73
- function publicStats(stats) {
74
- return {
75
- count: stats.count,
76
- total: stats.total,
77
- min: stats.min,
78
- max: stats.max,
79
- p50: percentile(stats.samples, 50),
80
- p95: percentile(stats.samples, 95),
81
- p99: percentile(stats.samples, 99),
82
- phases: stats.phases,
83
- };
84
- }
85
-
86
- export function recordRequest(
87
- category,
88
- durMs,
89
- client = "unknown",
90
- phase = "handler",
91
- ) {
92
- if (!category || typeof category !== "string") return;
93
- if (!validDuration(durMs)) {
94
- debugInvalid("request duration", { category, durMs, client, phase });
95
- return;
96
- }
97
-
98
- const clientKey = normalizeClient(client);
99
- const phaseKey = normalizePhase(phase);
100
- const bucket = categories.get(category) || new Map();
101
- const stats = bucket.get(clientKey) || emptyStats();
102
- addDuration(stats, durMs, phaseKey);
103
- bucket.set(clientKey, stats);
104
- categories.set(category, bucket);
105
- }
106
-
107
- export function recordWorker(workerId, kind, command, durMs) {
108
- if (!workerId || typeof workerId !== "string") return;
109
- if (kind === "spawned") {
110
- workerCounts.spawned += 1;
111
- return;
112
- }
113
- if (kind !== "exited") return;
114
-
115
- workerCounts.exited += 1;
116
- if (durMs == null) return;
117
- if (!validDuration(durMs)) {
118
- debugInvalid("worker duration", { workerId, kind, command, durMs });
119
- return;
120
- }
121
- workerDurations.push(durMs);
122
- if (workerDurations.length > SAMPLE_LIMIT) {
123
- workerDurations.splice(0, workerDurations.length - SAMPLE_LIMIT);
124
- }
125
- }
126
-
127
- export function snapshot() {
128
- const categorySnapshot = {};
129
- for (const [category, bucket] of categories.entries()) {
130
- categorySnapshot[category] = {};
131
- for (const [client, stats] of bucket.entries()) {
132
- categorySnapshot[category][client] = publicStats(stats);
133
- }
134
- }
135
-
136
- return {
137
- categories: categorySnapshot,
138
- workers: {
139
- spawned: workerCounts.spawned,
140
- exited: workerCounts.exited,
141
- p50: percentile(workerDurations, 50),
142
- p95: percentile(workerDurations, 95),
143
- p99: percentile(workerDurations, 99),
144
- },
145
- };
146
- }
147
-
148
- export function reset() {
149
- categories.clear();
150
- workerDurations.length = 0;
151
- workerCounts.spawned = 0;
152
- workerCounts.exited = 0;
153
- }
@@ -1,45 +0,0 @@
1
- // hub/lib/uuidv7.mjs — UUIDv7 generator (RFC 9562, monotonic)
2
- import { randomBytes } from "node:crypto";
3
-
4
- let _rndPool = Buffer.alloc(0),
5
- _rndOff = 0;
6
-
7
- function pooledRandom(n) {
8
- if (_rndOff + n > _rndPool.length) {
9
- _rndPool = randomBytes(256);
10
- _rndOff = 0;
11
- }
12
- const out = Buffer.from(_rndPool.subarray(_rndOff, _rndOff + n));
13
- _rndOff += n;
14
- return out;
15
- }
16
-
17
- let _lastMs = 0n;
18
- let _seq = 0;
19
-
20
- /** UUIDv7 생성 (RFC 9562, 단조 증가 보장) */
21
- export function uuidv7() {
22
- let now = BigInt(Date.now());
23
- if (now <= _lastMs) {
24
- _seq++;
25
- if (_seq > 0xfff) {
26
- now = _lastMs + 1n;
27
- _seq = 0;
28
- }
29
- } else {
30
- _seq = 0;
31
- }
32
- _lastMs = now;
33
- const buf = pooledRandom(16);
34
- buf[0] = Number((now >> 40n) & 0xffn);
35
- buf[1] = Number((now >> 32n) & 0xffn);
36
- buf[2] = Number((now >> 24n) & 0xffn);
37
- buf[3] = Number((now >> 16n) & 0xffn);
38
- buf[4] = Number((now >> 8n) & 0xffn);
39
- buf[5] = Number(now & 0xffn);
40
- buf[6] = ((_seq >> 8) & 0x0f) | 0x70;
41
- buf[7] = _seq & 0xff;
42
- buf[8] = (buf[8] & 0x3f) | 0x80;
43
- const h = buf.toString("hex");
44
- return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
45
- }