@triflux/core 10.50.2 → 10.51.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 (46) 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 +1 -79
  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 +1 -1
  8. package/hub/lib/known-errors.json +2 -2
  9. package/hub/lib/process-utils.mjs +0 -123
  10. package/hub/lib/spawn-trace.mjs +3 -2
  11. package/hub/lib/timeout-defaults.mjs +0 -1
  12. package/hub/role-contract.mjs +0 -2
  13. package/hud/cli-policy.mjs +0 -22
  14. package/hud/constants.mjs +11 -26
  15. package/hud/hud-qos-status.mjs +23 -51
  16. package/hud/providers/gemini.mjs +105 -20
  17. package/hud/renderers.mjs +50 -104
  18. package/hud/utils.mjs +2 -2
  19. package/package.json +1 -3
  20. package/scripts/lib/agent-route-policy.mjs +162 -14
  21. package/scripts/lib/claudemd-scanner.mjs +2 -199
  22. package/scripts/lib/cli-claude.mjs +0 -2
  23. package/scripts/lib/context.mjs +3 -19
  24. package/scripts/lib/env-probe.mjs +5 -64
  25. package/scripts/lib/gemini-profiles.mjs +2 -69
  26. package/scripts/lib/machine-profile.mjs +3 -17
  27. package/scripts/lib/mcp-filter.mjs +109 -53
  28. package/scripts/lib/mcp-health.mjs +2 -2
  29. package/scripts/lib/mcp-server-catalog.mjs +2 -2
  30. package/scripts/lib/psmux-info.mjs +11 -59
  31. package/scripts/lib/session-presence.mjs +1 -1
  32. package/scripts/lib/skill-template.mjs +0 -245
  33. package/hub/dynamic-routing-engine.mjs +0 -511
  34. package/hub/lib/risk-tier.mjs +0 -53
  35. package/hub/lib/ssh-command.mjs +0 -231
  36. package/hub/lib/staged-file-detect.mjs +0 -43
  37. package/hub/lib/tfx-route-args.mjs +0 -353
  38. package/hub/routing-snapshot.mjs +0 -261
  39. package/mesh/index.mjs +0 -69
  40. package/mesh/mesh-budget.mjs +0 -137
  41. package/mesh/mesh-heartbeat.mjs +0 -100
  42. package/mesh/mesh-protocol.mjs +0 -96
  43. package/mesh/mesh-queue.mjs +0 -163
  44. package/mesh/mesh-registry.mjs +0 -78
  45. package/mesh/mesh-router.mjs +0 -77
  46. package/scripts/lib/dynamic-route-cli.mjs +0 -107
@@ -1,163 +0,0 @@
1
- const DEFAULT_MAX_QUEUE_SIZE = 100;
2
- const DEFAULT_TTL_MS = 0; // 0 = no expiry
3
-
4
- /**
5
- * Creates a per-agent message queue with TTL and size limits.
6
- *
7
- * @param {object} [opts]
8
- * @param {number} [opts.maxQueueSize=100] - Max messages per agent queue
9
- * @param {number} [opts.ttlMs=0] - Message TTL in ms (0 = no expiry)
10
- * @returns {object} Queue API
11
- */
12
- export function createMessageQueue(opts = {}) {
13
- const { maxQueueSize = DEFAULT_MAX_QUEUE_SIZE, ttlMs = DEFAULT_TTL_MS } =
14
- opts;
15
-
16
- /** @type {Map<string, Array<{ message: object, enqueuedAt: number }>>} */
17
- const queues = new Map();
18
-
19
- /**
20
- * Returns the queue array for an agent (creates if absent).
21
- * @param {string} agentId
22
- * @returns {Array}
23
- */
24
- function getQueue(agentId) {
25
- let q = queues.get(agentId);
26
- if (!q) {
27
- q = [];
28
- queues.set(agentId, q);
29
- }
30
- return q;
31
- }
32
-
33
- /**
34
- * Removes expired messages from the front of a queue.
35
- * @param {Array} q
36
- * @param {number} now
37
- */
38
- function purgeExpired(q, now) {
39
- if (ttlMs <= 0) return;
40
- while (q.length > 0 && now - q[0].enqueuedAt > ttlMs) {
41
- q.shift();
42
- }
43
- }
44
-
45
- /**
46
- * Adds a message to the target agent's queue.
47
- * If queue exceeds maxQueueSize, the oldest message is dropped.
48
- *
49
- * @param {string} agentId - Target agent
50
- * @param {object} message - Mesh message
51
- * @returns {{ queued: boolean, dropped: boolean }}
52
- */
53
- function enqueue(agentId, message) {
54
- if (!agentId || typeof agentId !== "string") {
55
- throw new TypeError("agentId must be a non-empty string");
56
- }
57
- const q = getQueue(agentId);
58
- const now = Date.now();
59
-
60
- purgeExpired(q, now);
61
-
62
- let dropped = false;
63
- if (q.length >= maxQueueSize) {
64
- q.shift();
65
- dropped = true;
66
- }
67
-
68
- q.push({ message, enqueuedAt: now });
69
- return { queued: true, dropped };
70
- }
71
-
72
- /**
73
- * Removes and returns the next message for an agent.
74
- *
75
- * @param {string} agentId
76
- * @returns {object | null} The message, or null if queue is empty
77
- */
78
- function dequeue(agentId) {
79
- const q = queues.get(agentId);
80
- if (!q || q.length === 0) return null;
81
-
82
- const now = Date.now();
83
- purgeExpired(q, now);
84
-
85
- if (q.length === 0) return null;
86
- return q.shift().message;
87
- }
88
-
89
- /**
90
- * Returns the next message without removing it.
91
- *
92
- * @param {string} agentId
93
- * @returns {object | null}
94
- */
95
- function peek(agentId) {
96
- const q = queues.get(agentId);
97
- if (!q || q.length === 0) return null;
98
-
99
- const now = Date.now();
100
- purgeExpired(q, now);
101
-
102
- if (q.length === 0) return null;
103
- return q[0].message;
104
- }
105
-
106
- /**
107
- * Returns the number of (non-expired) messages in an agent's queue.
108
- *
109
- * @param {string} agentId
110
- * @returns {number}
111
- */
112
- function size(agentId) {
113
- const q = queues.get(agentId);
114
- if (!q) return 0;
115
-
116
- const now = Date.now();
117
- purgeExpired(q, now);
118
-
119
- return q.length;
120
- }
121
-
122
- /**
123
- * Drains all messages for an agent.
124
- *
125
- * @param {string} agentId
126
- * @returns {object[]} Array of messages
127
- */
128
- function drain(agentId) {
129
- const q = queues.get(agentId);
130
- if (!q || q.length === 0) return [];
131
-
132
- const now = Date.now();
133
- purgeExpired(q, now);
134
-
135
- const messages = q.map((entry) => entry.message);
136
- q.length = 0;
137
- return messages;
138
- }
139
-
140
- /**
141
- * Removes an agent's queue entirely.
142
- * @param {string} agentId
143
- */
144
- function clear(agentId) {
145
- queues.delete(agentId);
146
- }
147
-
148
- /**
149
- * Returns total message count across all agent queues.
150
- * @returns {number}
151
- */
152
- function totalSize() {
153
- let total = 0;
154
- const now = Date.now();
155
- for (const [, q] of queues) {
156
- purgeExpired(q, now);
157
- total += q.length;
158
- }
159
- return total;
160
- }
161
-
162
- return { enqueue, dequeue, peek, size, drain, clear, totalSize };
163
- }
@@ -1,78 +0,0 @@
1
- /**
2
- * Creates an agent registry for the mesh network.
3
- * Agents register with capabilities; registry enables discovery.
4
- * @returns {object} Registry API
5
- */
6
- export function createRegistry() {
7
- // Map<agentId, AgentInfo>
8
- const agents = new Map();
9
-
10
- /**
11
- * Registers an agent with the registry.
12
- * @param {string} agentId
13
- * @param {string[]} capabilities
14
- */
15
- function register(agentId, capabilities = []) {
16
- if (!agentId || typeof agentId !== "string") {
17
- throw new TypeError("agentId must be a non-empty string");
18
- }
19
- if (!Array.isArray(capabilities)) {
20
- throw new TypeError("capabilities must be an array");
21
- }
22
- const info = Object.freeze({
23
- agentId,
24
- capabilities: Object.freeze([...capabilities]),
25
- registeredAt: new Date().toISOString(),
26
- });
27
- agents.set(agentId, info);
28
- }
29
-
30
- /**
31
- * Unregisters an agent from the registry.
32
- * @param {string} agentId
33
- */
34
- function unregister(agentId) {
35
- agents.delete(agentId);
36
- }
37
-
38
- /**
39
- * Discovers agents that have a specific capability.
40
- * @param {string} capability
41
- * @returns {string[]} Array of agentIds
42
- */
43
- function discover(capability) {
44
- const result = [];
45
- for (const [agentId, info] of agents) {
46
- if (info.capabilities.includes(capability)) {
47
- result.push(agentId);
48
- }
49
- }
50
- return result;
51
- }
52
-
53
- /**
54
- * Gets agent info by ID.
55
- * @param {string} agentId
56
- * @returns {object | null}
57
- */
58
- function getAgent(agentId) {
59
- return agents.get(agentId) ?? null;
60
- }
61
-
62
- /**
63
- * Lists all registered agents.
64
- * @returns {object[]}
65
- */
66
- function listAll() {
67
- return [...agents.values()];
68
- }
69
-
70
- /**
71
- * Clears all registered agents.
72
- */
73
- function clear() {
74
- agents.clear();
75
- }
76
-
77
- return { register, unregister, discover, getAgent, listAll, clear };
78
- }
@@ -1,77 +0,0 @@
1
- import { validate } from "./mesh-protocol.mjs";
2
-
3
- /**
4
- * Routes a mesh message to target agent(s) based on the `to` field.
5
- *
6
- * Addressing modes:
7
- * - "agent-id" → direct delivery (registry lookup)
8
- * - "*" → broadcast to all registered agents
9
- * - "capability:X" → discover agents with capability X
10
- *
11
- * @param {object} message - A mesh-protocol message
12
- * @param {object} registry - A mesh-registry instance
13
- * @returns {{ routed: boolean, targets?: string[], reason?: string }}
14
- */
15
- export function routeMessage(message, registry) {
16
- const { valid, errors } = validate(message);
17
- if (!valid) {
18
- return { routed: false, reason: `invalid message: ${errors.join(", ")}` };
19
- }
20
-
21
- const { to, from } = message;
22
-
23
- // Broadcast
24
- if (to === "*") {
25
- const all = registry.listAll();
26
- const targets = all.map((a) => a.agentId).filter((id) => id !== from);
27
- if (targets.length === 0) {
28
- return { routed: false, reason: "broadcast: no agents registered" };
29
- }
30
- return { routed: true, targets };
31
- }
32
-
33
- // Capability-based routing
34
- if (to.startsWith("capability:")) {
35
- const capability = to.slice("capability:".length);
36
- if (!capability) {
37
- return { routed: false, reason: "capability: empty capability name" };
38
- }
39
- const targets = registry.discover(capability);
40
- if (targets.length === 0) {
41
- return {
42
- routed: false,
43
- reason: `capability: no agents with "${capability}"`,
44
- };
45
- }
46
- return { routed: true, targets };
47
- }
48
-
49
- // Direct addressing
50
- const agent = registry.getAgent(to);
51
- if (!agent) {
52
- return { routed: false, reason: `agent not found: "${to}"` };
53
- }
54
- return { routed: true, targets: [to] };
55
- }
56
-
57
- /**
58
- * Routes a message and collects dead-letter info when delivery fails.
59
- *
60
- * @param {object} message
61
- * @param {object} registry
62
- * @returns {{ routed: boolean, targets?: string[], deadLetter?: object }}
63
- */
64
- export function routeOrDeadLetter(message, registry) {
65
- const result = routeMessage(message, registry);
66
- if (!result.routed) {
67
- return {
68
- ...result,
69
- deadLetter: {
70
- originalMessage: message,
71
- reason: result.reason,
72
- timestamp: new Date().toISOString(),
73
- },
74
- };
75
- }
76
- return result;
77
- }
@@ -1,107 +0,0 @@
1
- #!/usr/bin/env node
2
- // scripts/lib/dynamic-route-cli.mjs
3
- //
4
- // Phase 1 Wire-up 3 helper — tfx-route.sh 등 sh 경로에서 dynamic routing
5
- // decision 을 받아 단일 CLI 문자열 (또는 JSON) 으로 출력한다.
6
- //
7
- // 사용:
8
- // node scripts/lib/dynamic-route-cli.mjs --agent-hint codex
9
- // → "codex"
10
- // node scripts/lib/dynamic-route-cli.mjs --agent-hint codex --json
11
- // → {"scenario":"fallback","mode":"codex-default","shards":[{"cli":"codex",...}]}
12
- // node scripts/lib/dynamic-route-cli.mjs --agent-hint codex --field scenario
13
- // → "fallback"
14
- //
15
- // 정책:
16
- // - opt-in env TRIFLUX_DYNAMIC_ROUTING=1|true 미설정 시 즉시 silent exit 0
17
- // (override 없음 = stdout empty, sh caller 가 graceful no-op).
18
- // - error 발생 시 stderr 에 한 줄 + exit 0 (sh caller 가 fallback path 진행).
19
-
20
- import { createDynamicRouter } from "../../hub/dynamic-routing-engine.mjs";
21
-
22
- function parseArgs(argv) {
23
- const args = {
24
- taskId: `tfx-route-${process.pid}-${Date.now()}`,
25
- agentHint: "codex",
26
- teamSize: 1,
27
- json: false,
28
- field: null,
29
- };
30
- for (let i = 2; i < argv.length; i++) {
31
- const v = argv[i];
32
- switch (v) {
33
- case "--task-id":
34
- args.taskId = argv[++i] || args.taskId;
35
- break;
36
- case "--agent-hint":
37
- args.agentHint = argv[++i] || args.agentHint;
38
- break;
39
- case "--team-size":
40
- args.teamSize = Number(argv[++i]) || args.teamSize;
41
- break;
42
- case "--json":
43
- args.json = true;
44
- break;
45
- case "--field":
46
- args.field = argv[++i] || null;
47
- break;
48
- case "--help":
49
- case "-h":
50
- process.stdout.write(
51
- "Usage: dynamic-route-cli.mjs [--task-id ID] [--agent-hint CLI] [--team-size N] [--json|--field FIELD]\n",
52
- );
53
- process.exit(0);
54
- break;
55
- }
56
- }
57
- return args;
58
- }
59
-
60
- async function main() {
61
- const args = parseArgs(process.argv);
62
-
63
- // opt-in env 미설정 시 silent no-op (sh caller graceful path)
64
- const flag = process.env.TRIFLUX_DYNAMIC_ROUTING;
65
- if (flag !== "1" && flag !== "true") return;
66
-
67
- let decision;
68
- try {
69
- const router = createDynamicRouter();
70
- decision = await router.routeRequest({
71
- task_id: args.taskId,
72
- team_size: args.teamSize,
73
- agent_hint: args.agentHint,
74
- });
75
- } catch (err) {
76
- process.stderr.write(
77
- `[dynamic-route-cli] error: ${err?.message || String(err)}\n`,
78
- );
79
- return;
80
- }
81
-
82
- if (args.json) {
83
- process.stdout.write(JSON.stringify(decision));
84
- return;
85
- }
86
-
87
- if (args.field) {
88
- const value = decision?.[args.field];
89
- if (value !== undefined && value !== null) {
90
- process.stdout.write(String(value));
91
- }
92
- return;
93
- }
94
-
95
- // 기본 — shards[0].cli 만 stdout 으로 (sh caller 가 즉시 비교)
96
- const cli = decision?.shards?.[0]?.cli;
97
- if (cli) {
98
- process.stdout.write(String(cli));
99
- }
100
- }
101
-
102
- main().catch((err) => {
103
- process.stderr.write(
104
- `[dynamic-route-cli] fatal: ${err?.message || String(err)}\n`,
105
- );
106
- process.exit(0); // sh caller 가 정상 fallback 하도록 always 0
107
- });