triflux 10.41.1 → 10.43.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.
- package/CLAUDE.md +233 -0
- package/bin/tfx-live.mjs +3 -1
- package/bin/triflux.mjs +48 -1
- package/config/routing-policy.json +1 -1
- package/cto/hygiene-actions.mjs +355 -0
- package/cto/hygiene.mjs +79 -9
- package/cto/index.mjs +14 -2
- package/cto/steward.mjs +362 -0
- package/hooks/keyword-rules.json +39 -5
- package/hooks/pipeline-stop.mjs +1 -1
- package/hub/bridge.mjs +80 -1
- package/hub/cli-adapter-base.mjs +100 -19
- package/hub/codex-adapter.mjs +5 -1
- package/hub/dynamic-routing-engine.mjs +1 -1
- package/hub/gemini-adapter.mjs +3 -1
- package/hub/intent.mjs +13 -9
- package/hub/lib/cto-env.mjs +94 -0
- package/hub/lib/memory-store.mjs +34 -13
- package/hub/lib/timeout-defaults.mjs +45 -0
- package/hub/lib/worker-lifecycle.mjs +101 -0
- package/hub/log-retention.mjs +726 -0
- package/hub/middleware/request-logger.mjs +4 -1
- package/hub/pipe.mjs +11 -1
- package/hub/public/tray.html +50 -53
- package/hub/role-contract.mjs +314 -0
- package/hub/role-control-events.mjs +113 -0
- package/hub/router.mjs +135 -30
- package/hub/schema.sql +1 -0
- package/hub/server.mjs +89 -44
- package/hub/store.mjs +50 -19
- package/hub/team/claude-daemon-control.mjs +28 -15
- package/hub/team/cli/commands/start/parse-args.mjs +2 -2
- package/hub/team/headless.mjs +295 -24
- package/hub/team/intervention.mjs +530 -0
- package/hub/team/retry-state-machine.mjs +6 -2
- package/hub/team/swarm-hypervisor.mjs +20 -2
- package/hub/team/swarm-locks.mjs +17 -1
- package/hub/team/uds-orchestrator.mjs +62 -9
- package/hub/tools.mjs +9 -7
- package/hub/workers/codex-app-server-worker.mjs +54 -5
- package/hub/workers/codex-mcp.mjs +1 -1
- package/hub/workers/delegator-mcp.mjs +18 -18
- package/hud/constants.mjs +21 -0
- package/hud/context-monitor.mjs +18 -21
- package/hud/hud-qos-status.mjs +1 -1
- package/hud/providers/codex-probe.mjs +216 -0
- package/hud/providers/codex.mjs +206 -29
- package/hud/renderers.mjs +6 -9
- package/package.json +1 -1
- package/scripts/__tests__/tfx-route-bash-node-parity.test.mjs +1 -1
- package/scripts/__tests__/tfx-route-node-entry.test.mjs +6 -6
- package/scripts/headless-guard.mjs +70 -23
- package/scripts/lib/cli-agy.mjs +1 -0
- package/scripts/lib/cli-claude.mjs +1 -0
- package/scripts/lib/cli-codex.mjs +22 -21
- package/scripts/lib/codex-profile-config.mjs +2 -2
- package/scripts/pack.mjs +1 -0
- package/scripts/setup.mjs +15 -11
- package/scripts/tfx-gate-activate.mjs +1 -1
- package/scripts/tfx-route-worker.mjs +5 -0
- package/scripts/tfx-route.sh +136 -84
- package/skills/tfx-analysis/SKILL.md +3 -1
- package/skills/tfx-harness/SKILL.md +89 -0
- package/skills/tfx-hub/SKILL.md +1 -1
- package/skills/tfx-plan/SKILL.md +3 -1
- package/skills/tfx-profile/SKILL.md +9 -9
- package/skills/tfx-prune/SKILL.md +4 -2
- package/skills/tfx-qa/SKILL.md +6 -2
- package/skills/tfx-research/SKILL.md +3 -1
- package/skills/tfx-review/SKILL.md +5 -3
- package/skills/tfx-setup/SKILL.md +1 -1
- package/tui/codex-profile.mjs +3 -1
- package/tui/setup.mjs +4 -4
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// hub 조율 평면 타임아웃/TTL의 단일 기준값. 환경은 호출 시점에 읽는다.
|
|
2
|
+
|
|
3
|
+
export const MIN_DURATION_MS = 1_000;
|
|
4
|
+
export const MAX_DURATION_MS = 86_400_000;
|
|
5
|
+
export const DEFAULT_ASSIGN_TIMEOUT_MS = 600_000;
|
|
6
|
+
export const DEFAULT_ASSIGN_TTL_MS = 600_000;
|
|
7
|
+
export const DEFAULT_HANDOFF_TTL_MS = 600_000;
|
|
8
|
+
export const DEFAULT_SWARM_LOCK_TTL_MS = 600_000;
|
|
9
|
+
export const DEFAULT_REGISTER_TIMEOUT_SEC = 600;
|
|
10
|
+
export const REGISTER_HEARTBEAT_GRACE_MS = 120_000;
|
|
11
|
+
export const DEFAULT_STALL_INTERVENTION_SEC = 1_200;
|
|
12
|
+
export const DEFAULT_HARD_CEILING_SEC = 21_600;
|
|
13
|
+
|
|
14
|
+
function readEnvSeconds(env, name, fallbackSec) {
|
|
15
|
+
const value = Number(env?.[name]);
|
|
16
|
+
return Number.isFinite(value) && value > 0 ? Math.trunc(value) : fallbackSec;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function resolveStallInterventionMs(env = process.env) {
|
|
20
|
+
return (
|
|
21
|
+
readEnvSeconds(env, "TFX_STALL_THRESHOLD", DEFAULT_STALL_INTERVENTION_SEC) *
|
|
22
|
+
1000
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function resolveHardCeilingMs(env = process.env) {
|
|
27
|
+
return (
|
|
28
|
+
readEnvSeconds(env, "TFX_HARD_CEILING_SEC", DEFAULT_HARD_CEILING_SEC) * 1000
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function resolveWorkerLeaseTtlMs(env = process.env) {
|
|
33
|
+
return resolveStallInterventionMs(env) + REGISTER_HEARTBEAT_GRACE_MS;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function clampDurationMs(
|
|
37
|
+
value,
|
|
38
|
+
fallback = DEFAULT_ASSIGN_TIMEOUT_MS,
|
|
39
|
+
min = MIN_DURATION_MS,
|
|
40
|
+
max = MAX_DURATION_MS,
|
|
41
|
+
) {
|
|
42
|
+
const numeric = Number(value);
|
|
43
|
+
if (!Number.isFinite(numeric)) return fallback;
|
|
44
|
+
return Math.max(min, Math.min(Math.trunc(numeric), max));
|
|
45
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// 활동 기반 워커 lifecycle. 시간 기본값은 timeout-defaults SSOT에서만 가져온다.
|
|
2
|
+
import {
|
|
3
|
+
resolveHardCeilingMs,
|
|
4
|
+
resolveStallInterventionMs,
|
|
5
|
+
} from "./timeout-defaults.mjs";
|
|
6
|
+
|
|
7
|
+
export { resolveHardCeilingMs, resolveStallInterventionMs };
|
|
8
|
+
|
|
9
|
+
export function isActivityLifecycleEnabled(env = process.env) {
|
|
10
|
+
return env.TFX_ACTIVITY_LIFECYCLE !== "0";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 활동 신호를 기준으로 무활동 개입과 절대 상한을 판정한다.
|
|
15
|
+
* 호출자는 observe()에 자기 채널의 기존 활동 서명을 전달하고, check()에
|
|
16
|
+
* 개입 대상 문맥을 제공한다. 이 모듈은 신호를 생성하거나 개입을 실행하지 않는다.
|
|
17
|
+
*/
|
|
18
|
+
export function createActivityLifecycle({
|
|
19
|
+
enabled = isActivityLifecycleEnabled(),
|
|
20
|
+
interventionMs = resolveStallInterventionMs(),
|
|
21
|
+
hardCeilingMs = resolveHardCeilingMs(),
|
|
22
|
+
maxInterventions = 1,
|
|
23
|
+
onIntervene,
|
|
24
|
+
now = Date.now,
|
|
25
|
+
} = {}) {
|
|
26
|
+
const startedAt = now();
|
|
27
|
+
let lastActivityAt = startedAt;
|
|
28
|
+
let lastSignature;
|
|
29
|
+
let hasSignature = false;
|
|
30
|
+
let interventions = 0;
|
|
31
|
+
let timeoutReason = "";
|
|
32
|
+
let pendingIntervention = null;
|
|
33
|
+
|
|
34
|
+
const markActivity = () => {
|
|
35
|
+
lastActivityAt = now();
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
async function intervene(context = {}) {
|
|
39
|
+
if (!enabled || typeof onIntervene !== "function") return false;
|
|
40
|
+
if (interventions >= maxInterventions || pendingIntervention) return false;
|
|
41
|
+
|
|
42
|
+
interventions += 1;
|
|
43
|
+
const inactiveMs = Math.max(0, now() - lastActivityAt);
|
|
44
|
+
pendingIntervention = Promise.resolve(
|
|
45
|
+
onIntervene({ ...context, inactiveMs, interventions }),
|
|
46
|
+
)
|
|
47
|
+
.then((handled) => handled === true)
|
|
48
|
+
.catch(() => false);
|
|
49
|
+
try {
|
|
50
|
+
const handled = await pendingIntervention;
|
|
51
|
+
if (handled) markActivity();
|
|
52
|
+
return handled;
|
|
53
|
+
} finally {
|
|
54
|
+
pendingIntervention = null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return Object.freeze({
|
|
59
|
+
observe(...args) {
|
|
60
|
+
if (!enabled) return false;
|
|
61
|
+
const [signature] = args;
|
|
62
|
+
const changed =
|
|
63
|
+
args.length === 0 || !hasSignature || signature !== lastSignature;
|
|
64
|
+
if (changed) {
|
|
65
|
+
lastSignature = signature;
|
|
66
|
+
hasSignature = true;
|
|
67
|
+
markActivity();
|
|
68
|
+
}
|
|
69
|
+
return changed;
|
|
70
|
+
},
|
|
71
|
+
async check(context = {}) {
|
|
72
|
+
if (!enabled || timeoutReason) return timeoutReason;
|
|
73
|
+
const current = now();
|
|
74
|
+
if (current - startedAt >= hardCeilingMs) {
|
|
75
|
+
timeoutReason = "hard_ceiling";
|
|
76
|
+
return timeoutReason;
|
|
77
|
+
}
|
|
78
|
+
if (current - lastActivityAt < interventionMs || pendingIntervention)
|
|
79
|
+
return "";
|
|
80
|
+
if (await intervene(context)) return "";
|
|
81
|
+
timeoutReason = "inactivity";
|
|
82
|
+
return timeoutReason;
|
|
83
|
+
},
|
|
84
|
+
intervene,
|
|
85
|
+
get enabled() {
|
|
86
|
+
return enabled;
|
|
87
|
+
},
|
|
88
|
+
get hardCeilingMs() {
|
|
89
|
+
return hardCeilingMs;
|
|
90
|
+
},
|
|
91
|
+
get interventionMs() {
|
|
92
|
+
return interventionMs;
|
|
93
|
+
},
|
|
94
|
+
get interventions() {
|
|
95
|
+
return interventions;
|
|
96
|
+
},
|
|
97
|
+
get timeoutReason() {
|
|
98
|
+
return timeoutReason;
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|