@triflux/core 10.51.0 → 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 (55) hide show
  1. package/hub/bridge.mjs +65 -1172
  2. package/hub/index.mjs +2 -18
  3. package/hub/lib/process-utils.mjs +1 -33
  4. package/hub/team/claude-daemon-control.mjs +1 -2
  5. package/hub/team/claude-session-projection.mjs +5 -73
  6. package/hub/team/nativeProxy.mjs +759 -0
  7. package/hub/team/session-context.mjs +1 -1
  8. package/hud/constants.mjs +2 -0
  9. package/hud/hud-qos-status.mjs +21 -5
  10. package/hud/providers/gemini.mjs +20 -0
  11. package/hud/renderers.mjs +79 -46
  12. package/hud/utils.mjs +9 -5
  13. package/package.json +2 -4
  14. package/scripts/lib/agent-route-policy.mjs +0 -67
  15. package/scripts/lib/cli-claude.mjs +0 -12
  16. package/scripts/lib/env-probe.mjs +0 -70
  17. package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
  18. package/scripts/lib/mcp-guard-engine.mjs +40 -98
  19. package/scripts/lib/session-presence.mjs +1 -99
  20. package/hub/adaptive-diagnostic.mjs +0 -350
  21. package/hub/adaptive-inject.mjs +0 -224
  22. package/hub/adaptive-memory.mjs +0 -277
  23. package/hub/adaptive.mjs +0 -172
  24. package/hub/assign-callbacks.mjs +0 -170
  25. package/hub/delegator/contracts.mjs +0 -38
  26. package/hub/delegator/index.mjs +0 -14
  27. package/hub/delegator/schema/delegator-tools.schema.json +0 -250
  28. package/hub/delegator/service.mjs +0 -336
  29. package/hub/delegator/tool-definitions.mjs +0 -35
  30. package/hub/hitl.mjs +0 -202
  31. package/hub/lib/known-errors.json +0 -72
  32. package/hub/lib/memory-store.mjs +0 -1001
  33. package/hub/lib/phase-manager.mjs +0 -493
  34. package/hub/lib/state-snapshot.mjs +0 -293
  35. package/hub/lib/trace-recorder.mjs +0 -153
  36. package/hub/lib/uuidv7.mjs +0 -45
  37. package/hub/memory-doctor.mjs +0 -640
  38. package/hub/middleware/quota-middleware.mjs +0 -70
  39. package/hub/middleware/request-logger.mjs +0 -191
  40. package/hub/paths.mjs +0 -24
  41. package/hub/pipeline/index.mjs +0 -163
  42. package/hub/pipeline/state.mjs +0 -191
  43. package/hub/pipeline/transitions.mjs +0 -131
  44. package/hub/reflexion.mjs +0 -290
  45. package/hub/role-contract.mjs +0 -312
  46. package/hub/router.mjs +0 -2026
  47. package/hub/session-fingerprint.mjs +0 -418
  48. package/hub/state.mjs +0 -300
  49. package/hub/team/synapse-http.mjs +0 -311
  50. package/hub/team-bridge.mjs +0 -27
  51. package/scripts/lib/claudemd-scanner.mjs +0 -26
  52. package/scripts/lib/context.mjs +0 -51
  53. package/scripts/lib/handoff.mjs +0 -187
  54. package/scripts/lib/logger.mjs +0 -122
  55. package/scripts/lib/stealth-fetch.mjs +0 -176
@@ -1,293 +0,0 @@
1
- import { execFile } from "node:child_process";
2
- import {
3
- copyFile,
4
- mkdir,
5
- readdir,
6
- rename,
7
- rm,
8
- stat,
9
- writeFile,
10
- } from "node:fs/promises";
11
- import { tmpdir } from "node:os";
12
- import { dirname, isAbsolute, join, relative, resolve } from "node:path";
13
- import { promisify } from "node:util";
14
-
15
- const execFileAsync = promisify(execFile);
16
- const DEFAULT_MAX_SNAPSHOTS = 10;
17
- const DAY_MS = 24 * 60 * 60 * 1000;
18
-
19
- export const CODEX_STATE_INCLUDES = Object.freeze([
20
- "config.toml",
21
- "AGENTS.md",
22
- "skills",
23
- "agents",
24
- "prompts",
25
- "plugins",
26
- ]);
27
-
28
- export const CODEX_STATE_EXCLUDES = Object.freeze([
29
- "*.sqlite*",
30
- ".sandbox*",
31
- ".tmp",
32
- "_archived_skills",
33
- "memories",
34
- "cache",
35
- "log",
36
- "logs",
37
- "sessions",
38
- "auth.json",
39
- ".credentials.json",
40
- "*.bak*",
41
- "*.tmp-*",
42
- "cap_sid",
43
- "installation_id",
44
- "history.jsonl",
45
- "models_cache.json",
46
- ]);
47
-
48
- export const GEMINI_STATE_INCLUDES = Object.freeze([
49
- "settings.json",
50
- "settings.local.json",
51
- "GEMINI.md",
52
- "commands",
53
- "extensions",
54
- "plugins",
55
- "skills",
56
- "agents",
57
- ]);
58
-
59
- export const GEMINI_STATE_EXCLUDES = Object.freeze([
60
- "*.sqlite*",
61
- "cache",
62
- "log",
63
- "logs",
64
- "sessions",
65
- "auth.json",
66
- ".credentials.json",
67
- "*.bak*",
68
- ]);
69
-
70
- export const STATE_SNAPSHOT_THRESHOLD_MS = DAY_MS;
71
- export const STATE_SNAPSHOT_MAX_SNAPSHOTS = DEFAULT_MAX_SNAPSHOTS;
72
-
73
- function normalizePath(path) {
74
- return String(path || "")
75
- .replace(/\\/gu, "/")
76
- .replace(/^\/+/u, "");
77
- }
78
-
79
- function globToRegExp(pattern) {
80
- const escaped = String(pattern).replace(/[.+^${}()|[\]\\]/gu, "\\$&");
81
- return new RegExp(`^${escaped.replace(/\*/gu, ".*")}$`, "u");
82
- }
83
-
84
- function compileExclude(pattern) {
85
- const text = normalizePath(pattern).replace(/\/+$/u, "");
86
- if (text.includes("*")) {
87
- const regex = globToRegExp(text);
88
- return (relativePath) => {
89
- const normalized = normalizePath(relativePath);
90
- return normalized.split("/").some((part) => regex.test(part));
91
- };
92
- }
93
-
94
- return (relativePath) => {
95
- const normalized = normalizePath(relativePath);
96
- return normalized.split("/").includes(text);
97
- };
98
- }
99
-
100
- function isSubpath(parent, child) {
101
- const rel = relative(parent, child);
102
- return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
103
- }
104
-
105
- async function pathStats(path) {
106
- try {
107
- return await stat(path);
108
- } catch (error) {
109
- if (error?.code === "ENOENT") return null;
110
- throw error;
111
- }
112
- }
113
-
114
- async function collectFiles({ sourceDir, includes, excludeMatchers }) {
115
- const sourceRoot = resolve(sourceDir);
116
- const files = [];
117
-
118
- async function visit(absPath, relativePath) {
119
- if (excludeMatchers.some((matcher) => matcher(relativePath))) return;
120
-
121
- const info = await pathStats(absPath);
122
- if (!info) return;
123
- if (info.isDirectory()) {
124
- const children = await readdir(absPath, { withFileTypes: true });
125
- for (const child of children) {
126
- await visit(join(absPath, child.name), join(relativePath, child.name));
127
- }
128
- return;
129
- }
130
- if (info.isFile()) {
131
- files.push({
132
- absPath,
133
- relativePath: normalizePath(relativePath),
134
- size: info.size,
135
- });
136
- }
137
- }
138
-
139
- for (const include of includes || []) {
140
- const relativeInclude = normalizePath(include);
141
- if (!relativeInclude || relativeInclude.startsWith("../")) continue;
142
- const absPath = resolve(sourceRoot, relativeInclude);
143
- if (!isSubpath(sourceRoot, absPath)) continue;
144
- await visit(absPath, relativeInclude);
145
- }
146
-
147
- files.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
148
- return files;
149
- }
150
-
151
- async function listSnapshots(destDir) {
152
- const names = await readdir(destDir).catch((error) => {
153
- if (error?.code === "ENOENT") return [];
154
- throw error;
155
- });
156
- const snapshots = [];
157
- for (const name of names) {
158
- if (!name.endsWith(".tar.gz")) continue;
159
- const path = join(destDir, name);
160
- const info = await pathStats(path);
161
- if (info?.isFile()) snapshots.push({ name, path, mtimeMs: info.mtimeMs });
162
- }
163
- snapshots.sort(
164
- (a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name),
165
- );
166
- return snapshots;
167
- }
168
-
169
- async function copyToStaging(files, stagingDir) {
170
- for (const file of files) {
171
- const targetPath = join(stagingDir, ...file.relativePath.split("/"));
172
- await mkdir(dirname(targetPath), { recursive: true });
173
- await copyFile(file.absPath, targetPath);
174
- }
175
- }
176
-
177
- async function createArchive({ stagingDir, archivePath, files }) {
178
- const listPath = join(stagingDir, ".snapshot-files");
179
- const archiveName = normalizePath(relative(stagingDir, archivePath));
180
- await writeFile(
181
- listPath,
182
- `${files.map((file) => file.relativePath).join("\n")}\n`,
183
- "utf8",
184
- );
185
- await execFileAsync(
186
- "tar",
187
- ["-czf", archiveName, "-C", ".", "-T", ".snapshot-files"],
188
- {
189
- cwd: stagingDir,
190
- windowsHide: true,
191
- },
192
- );
193
- await rm(listPath, { force: true });
194
- }
195
-
196
- async function pruneSnapshots(destDir, maxSnapshots) {
197
- const snapshots = await listSnapshots(destDir);
198
- const keep = Math.max(1, Number(maxSnapshots) || DEFAULT_MAX_SNAPSHOTS);
199
- for (const snapshot of snapshots.slice(keep)) {
200
- await rm(snapshot.path, { force: true });
201
- }
202
- }
203
-
204
- function formatStamp(date) {
205
- return date
206
- .toISOString()
207
- .replace(/[-:]/gu, "")
208
- .replace(/\.\d{3}Z$/u, "Z");
209
- }
210
-
211
- function uniqueSuffix() {
212
- return `${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
213
- }
214
-
215
- /**
216
- * Snapshot selected user state into a rolling tar.gz archive.
217
- *
218
- * @param {object} options
219
- * @param {string} options.sourceDir
220
- * @param {string} options.destDir
221
- * @param {string[]} options.includes
222
- * @param {string[]} options.excludes
223
- * @param {number} options.thresholdMs
224
- * @param {number} [options.maxSnapshots=10]
225
- * @returns {Promise<{skipped: boolean, reason?: string, path?: string, sizeBytes?: number, fileCount?: number}>}
226
- */
227
- export async function snapshotState({
228
- sourceDir,
229
- destDir,
230
- includes,
231
- excludes = [],
232
- thresholdMs = 0,
233
- maxSnapshots = DEFAULT_MAX_SNAPSHOTS,
234
- }) {
235
- const sourceRoot = resolve(sourceDir || "");
236
- const destRoot = resolve(destDir || "");
237
- const sourceInfo = await pathStats(sourceRoot);
238
- if (!sourceInfo?.isDirectory()) {
239
- return { skipped: true, reason: "source-missing" };
240
- }
241
-
242
- await mkdir(destRoot, { recursive: true });
243
- const snapshots = await listSnapshots(destRoot);
244
- const newest = snapshots[0];
245
- if (
246
- newest &&
247
- Number(thresholdMs) > 0 &&
248
- Date.now() - newest.mtimeMs < Number(thresholdMs)
249
- ) {
250
- return { skipped: true, reason: "threshold", path: newest.path };
251
- }
252
-
253
- const excludeMatchers = excludes.map((pattern) => compileExclude(pattern));
254
- const files = await collectFiles({
255
- sourceDir: sourceRoot,
256
- includes,
257
- excludeMatchers,
258
- });
259
- if (files.length === 0) {
260
- return { skipped: true, reason: "empty" };
261
- }
262
-
263
- const suffix = uniqueSuffix();
264
- const stagingDir = join(tmpdir(), `tfx-state-snapshot-${suffix}`);
265
- const tempArchivePath = join(stagingDir, `.state-${suffix}.tar.gz.tmp`);
266
- const finalArchivePath = join(
267
- destRoot,
268
- `state-${formatStamp(new Date())}-${suffix}.tar.gz`,
269
- );
270
-
271
- try {
272
- await mkdir(stagingDir, { recursive: true });
273
- await copyToStaging(files, stagingDir);
274
- await createArchive({
275
- stagingDir,
276
- archivePath: tempArchivePath,
277
- files,
278
- });
279
- await rename(tempArchivePath, finalArchivePath);
280
- await pruneSnapshots(destRoot, maxSnapshots);
281
- const archiveInfo = await stat(finalArchivePath);
282
- const sizeBytes = files.reduce((sum, file) => sum + file.size, 0);
283
- return {
284
- skipped: false,
285
- path: finalArchivePath,
286
- sizeBytes: archiveInfo.size || sizeBytes,
287
- fileCount: files.length,
288
- };
289
- } finally {
290
- await rm(stagingDir, { recursive: true, force: true });
291
- await rm(tempArchivePath, { force: true });
292
- }
293
- }
@@ -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
- }