@triflux/core 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/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/role-contract.mjs +314 -0
- package/hub/role-control-events.mjs +113 -0
- package/hub/router.mjs +135 -30
- package/hub/team/claude-daemon-control.mjs +28 -15
- package/hub/team/intervention.mjs +530 -0
- package/hub/team/retry-state-machine.mjs +6 -2
- 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/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/hub/lib/memory-store.mjs
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// hub/lib/memory-store.mjs — In-memory store (SQLite-free fallback for @triflux/core)
|
|
2
2
|
|
|
3
3
|
import { recalcConfidence } from "../reflexion.mjs";
|
|
4
|
+
import {
|
|
5
|
+
clampDurationMs,
|
|
6
|
+
DEFAULT_ASSIGN_TIMEOUT_MS,
|
|
7
|
+
} from "./timeout-defaults.mjs";
|
|
4
8
|
import { uuidv7 } from "./uuidv7.mjs";
|
|
5
9
|
|
|
6
10
|
export function clone(value) {
|
|
@@ -19,12 +23,6 @@ function clampPriority(value, fallback = 5) {
|
|
|
19
23
|
return Math.max(1, Math.min(Math.trunc(num), 9));
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
function clampDuration(value, fallback = 600000, min = 1000, max = 86400000) {
|
|
23
|
-
const num = Number(value);
|
|
24
|
-
if (!Number.isFinite(num)) return fallback;
|
|
25
|
-
return Math.max(min, Math.min(Math.trunc(num), max));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
26
|
export function clampConfidence(value, fallback = 0.5) {
|
|
29
27
|
const num = Number(value);
|
|
30
28
|
if (!Number.isFinite(num)) return fallback;
|
|
@@ -177,6 +175,15 @@ export function createMemoryStore() {
|
|
|
177
175
|
lease_id: uuidv7(),
|
|
178
176
|
lease_expires_ms: next.lease_expires_ms,
|
|
179
177
|
server_time_ms: now,
|
|
178
|
+
effective: {
|
|
179
|
+
updated: true,
|
|
180
|
+
online: next.status === "online",
|
|
181
|
+
status: next.status,
|
|
182
|
+
last_seen_ms: next.last_seen_ms,
|
|
183
|
+
lease_expires_ms: next.lease_expires_ms,
|
|
184
|
+
store_type: "memory",
|
|
185
|
+
db_path: null,
|
|
186
|
+
},
|
|
180
187
|
};
|
|
181
188
|
},
|
|
182
189
|
|
|
@@ -191,6 +198,7 @@ export function createMemoryStore() {
|
|
|
191
198
|
agent_id: agentId,
|
|
192
199
|
lease_expires_ms: Date.now() + ttlMs,
|
|
193
200
|
server_time_ms: Date.now(),
|
|
201
|
+
effective: { updated: false, store_type: "memory", db_path: null },
|
|
194
202
|
};
|
|
195
203
|
const now = Date.now();
|
|
196
204
|
current.last_seen_ms = now;
|
|
@@ -200,6 +208,7 @@ export function createMemoryStore() {
|
|
|
200
208
|
agent_id: agentId,
|
|
201
209
|
lease_expires_ms: current.lease_expires_ms,
|
|
202
210
|
server_time_ms: now,
|
|
211
|
+
effective: { updated: true, store_type: "memory", db_path: null },
|
|
203
212
|
};
|
|
204
213
|
},
|
|
205
214
|
|
|
@@ -455,9 +464,10 @@ export function createMemoryStore() {
|
|
|
455
464
|
retry_count = 0,
|
|
456
465
|
max_retries = 0,
|
|
457
466
|
priority = 5,
|
|
458
|
-
ttl_ms =
|
|
459
|
-
timeout_ms =
|
|
467
|
+
ttl_ms = DEFAULT_ASSIGN_TIMEOUT_MS,
|
|
468
|
+
timeout_ms = DEFAULT_ASSIGN_TIMEOUT_MS,
|
|
460
469
|
deadline_ms,
|
|
470
|
+
dispatched_at_ms = null,
|
|
461
471
|
trace_id,
|
|
462
472
|
correlation_id,
|
|
463
473
|
last_message_id = null,
|
|
@@ -465,7 +475,10 @@ export function createMemoryStore() {
|
|
|
465
475
|
error = null,
|
|
466
476
|
}) {
|
|
467
477
|
const now = Date.now();
|
|
468
|
-
const normalizedTimeout =
|
|
478
|
+
const normalizedTimeout = clampDurationMs(
|
|
479
|
+
timeout_ms,
|
|
480
|
+
DEFAULT_ASSIGN_TIMEOUT_MS,
|
|
481
|
+
);
|
|
469
482
|
const row = {
|
|
470
483
|
job_id: job_id || uuidv7(),
|
|
471
484
|
supervisor_agent,
|
|
@@ -478,7 +491,7 @@ export function createMemoryStore() {
|
|
|
478
491
|
retry_count: Math.max(0, Number(retry_count) || 0),
|
|
479
492
|
max_retries: Math.max(0, Number(max_retries) || 0),
|
|
480
493
|
priority: clampPriority(priority, 5),
|
|
481
|
-
ttl_ms:
|
|
494
|
+
ttl_ms: clampDurationMs(ttl_ms, normalizedTimeout),
|
|
482
495
|
timeout_ms: normalizedTimeout,
|
|
483
496
|
deadline_ms: Number.isFinite(Number(deadline_ms))
|
|
484
497
|
? Math.trunc(Number(deadline_ms))
|
|
@@ -491,6 +504,10 @@ export function createMemoryStore() {
|
|
|
491
504
|
created_at_ms: now,
|
|
492
505
|
updated_at_ms: now,
|
|
493
506
|
started_at_ms: status === "running" ? now : null,
|
|
507
|
+
dispatched_at_ms:
|
|
508
|
+
dispatched_at_ms == null || !Number.isFinite(Number(dispatched_at_ms))
|
|
509
|
+
? null
|
|
510
|
+
: Math.trunc(Number(dispatched_at_ms)),
|
|
494
511
|
completed_at_ms: ["succeeded", "failed", "timed_out"].includes(status)
|
|
495
512
|
? now
|
|
496
513
|
: null,
|
|
@@ -515,7 +532,7 @@ export function createMemoryStore() {
|
|
|
515
532
|
const isTerminal = ["succeeded", "failed", "timed_out"].includes(
|
|
516
533
|
nextStatus,
|
|
517
534
|
);
|
|
518
|
-
const nextTimeout =
|
|
535
|
+
const nextTimeout = clampDurationMs(
|
|
519
536
|
patch.timeout_ms ?? current.timeout_ms,
|
|
520
537
|
current.timeout_ms,
|
|
521
538
|
);
|
|
@@ -543,7 +560,7 @@ export function createMemoryStore() {
|
|
|
543
560
|
patch.priority ?? current.priority,
|
|
544
561
|
current.priority || 5,
|
|
545
562
|
),
|
|
546
|
-
ttl_ms:
|
|
563
|
+
ttl_ms: clampDurationMs(
|
|
547
564
|
patch.ttl_ms ?? current.ttl_ms,
|
|
548
565
|
current.ttl_ms || nextTimeout,
|
|
549
566
|
),
|
|
@@ -580,6 +597,9 @@ export function createMemoryStore() {
|
|
|
580
597
|
: nextStatus === "running"
|
|
581
598
|
? current.started_at_ms || now
|
|
582
599
|
: current.started_at_ms,
|
|
600
|
+
dispatched_at_ms: Object.hasOwn(patch, "dispatched_at_ms")
|
|
601
|
+
? patch.dispatched_at_ms
|
|
602
|
+
: current.dispatched_at_ms,
|
|
583
603
|
completed_at_ms: Object.hasOwn(patch, "completed_at_ms")
|
|
584
604
|
? patch.completed_at_ms
|
|
585
605
|
: isTerminal
|
|
@@ -645,7 +665,7 @@ export function createMemoryStore() {
|
|
|
645
665
|
current.attempt + 1,
|
|
646
666
|
Number(patch.attempt ?? current.attempt + 1) || 1,
|
|
647
667
|
);
|
|
648
|
-
const nextTimeout =
|
|
668
|
+
const nextTimeout = clampDurationMs(
|
|
649
669
|
patch.timeout_ms ?? current.timeout_ms,
|
|
650
670
|
current.timeout_ms,
|
|
651
671
|
);
|
|
@@ -657,6 +677,7 @@ export function createMemoryStore() {
|
|
|
657
677
|
deadline_ms: Date.now() + nextTimeout,
|
|
658
678
|
completed_at_ms: null,
|
|
659
679
|
started_at_ms: null,
|
|
680
|
+
dispatched_at_ms: null,
|
|
660
681
|
last_retry_at_ms: Date.now(),
|
|
661
682
|
result: patch.result ?? null,
|
|
662
683
|
error: Object.hasOwn(patch, "error") ? patch.error : current.error,
|
|
@@ -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
|
+
}
|