@triflux/core 10.50.1 → 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.
- package/hooks/agy-session-hook.mjs +11 -56
- package/hooks/codex-session-hook.mjs +11 -101
- package/hub/bridge.mjs +1 -79
- package/hub/cli-adapter-base.mjs +12 -90
- package/hub/codex-adapter.mjs +0 -1
- package/hub/codex-preflight.mjs +5 -28
- package/hub/index.mjs +3 -9
- package/hub/lib/cto-env.mjs +0 -23
- package/hub/lib/known-errors.json +2 -2
- package/hub/lib/process-utils.mjs +0 -123
- package/hub/lib/spawn-trace.mjs +3 -2
- package/hub/lib/timeout-defaults.mjs +0 -1
- package/hub/paths.mjs +0 -6
- package/hub/pipeline/index.mjs +0 -190
- package/hub/pipeline/transitions.mjs +0 -10
- package/hub/reflexion.mjs +1 -84
- package/hub/role-contract.mjs +0 -2
- package/hud/cli-policy.mjs +0 -26
- package/hud/colors.mjs +0 -13
- package/hud/constants.mjs +7 -153
- package/hud/context-monitor.mjs +3 -64
- package/hud/hud-qos-status.mjs +54 -264
- package/hud/providers/claude.mjs +4 -16
- package/hud/providers/gemini.mjs +83 -689
- package/hud/renderers.mjs +59 -367
- package/hud/terminal.mjs +1 -63
- package/hud/utils.mjs +2 -26
- package/package.json +1 -4
- package/scripts/lib/agent-route-policy.mjs +162 -14
- package/scripts/lib/claudemd-scanner.mjs +2 -199
- package/scripts/lib/cli-claude.mjs +0 -2
- package/scripts/lib/context.mjs +3 -19
- package/scripts/lib/env-probe.mjs +5 -64
- package/scripts/lib/gemini-profiles.mjs +2 -69
- package/scripts/lib/legacy-mcp-cleanup.mjs +14 -3
- package/scripts/lib/machine-profile.mjs +3 -17
- package/scripts/lib/mcp-filter.mjs +109 -53
- package/scripts/lib/mcp-health.mjs +2 -2
- package/scripts/lib/mcp-server-catalog.mjs +2 -2
- package/scripts/lib/psmux-info.mjs +25 -104
- package/scripts/lib/session-presence.mjs +1 -1
- package/scripts/lib/skill-template.mjs +0 -271
- package/hub/codex-compat.mjs +0 -12
- package/hub/dynamic-routing-engine.mjs +0 -511
- package/hub/fullcycle.mjs +0 -113
- package/hub/gemini-adapter.mjs +0 -174
- package/hub/intent.mjs +0 -321
- package/hub/lib/mcp-response-cache.mjs +0 -239
- package/hub/lib/path-utils.mjs +0 -165
- package/hub/lib/risk-tier.mjs +0 -53
- package/hub/lib/ssh-command.mjs +0 -231
- package/hub/lib/staged-file-detect.mjs +0 -43
- package/hub/lib/tfx-route-args.mjs +0 -353
- package/hub/lib/timeline-adapter.mjs +0 -190
- package/hub/pipeline/gates/confidence.mjs +0 -75
- package/hub/pipeline/gates/consensus.mjs +0 -106
- package/hub/pipeline/gates/index.mjs +0 -9
- package/hub/pipeline/gates/selfcheck.mjs +0 -120
- package/hub/quality/deslop.mjs +0 -274
- package/hub/research.mjs +0 -155
- package/hub/role-control-events.mjs +0 -113
- package/hub/routing/complexity.mjs +0 -269
- package/hub/routing/index.mjs +0 -122
- package/hub/routing/q-learning.mjs +0 -387
- package/hub/routing-snapshot.mjs +0 -261
- package/hub/token-mode.mjs +0 -228
- package/hud/mission-board.mjs +0 -56
- package/hud/providers/cto.mjs +0 -76
- package/mesh/index.mjs +0 -69
- package/mesh/mesh-budget.mjs +0 -137
- package/mesh/mesh-heartbeat.mjs +0 -100
- package/mesh/mesh-protocol.mjs +0 -96
- package/mesh/mesh-queue.mjs +0 -163
- package/mesh/mesh-registry.mjs +0 -78
- package/mesh/mesh-router.mjs +0 -77
- package/scripts/lib/agent-json.mjs +0 -27
- package/scripts/lib/async.mjs +0 -174
- package/scripts/lib/cli-agy.mjs +0 -85
- package/scripts/lib/cli-codex.mjs +0 -62
- package/scripts/lib/cli-gemini.mjs +0 -37
- package/scripts/lib/dynamic-route-cli.mjs +0 -107
- package/scripts/lib/env.mjs +0 -14
- package/scripts/lib/hub.mjs +0 -48
- package/scripts/lib/pid.mjs +0 -63
- package/scripts/lib/quota.mjs +0 -47
- package/scripts/lib/skill-state.mjs +0 -220
- package/scripts/lib/team.mjs +0 -75
- package/scripts/lib/timeout.mjs +0 -66
- package/scripts/lib/tmp.mjs +0 -26
- package/scripts/lib/toml.mjs +0 -132
|
@@ -488,68 +488,6 @@ function hasExactGbrainServe(commandLine) {
|
|
|
488
488
|
);
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
-
function hasGbrainServeCommand(commandLine) {
|
|
492
|
-
return /(^|\s)gbrain\s+serve(\s|$)/i.test(commandLine.trim());
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
function hasFsmonitorDaemonCommand(commandLine) {
|
|
496
|
-
return FSMONITOR_DAEMON_PATTERN.test(commandLine);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
function categoryForShardProcess(proc, context) {
|
|
500
|
-
const name = normalizeName(proc.name);
|
|
501
|
-
const commandLine = normalizeCommandLine(proc.commandLine);
|
|
502
|
-
const worktreePath = normalizeCommandLine(context.worktreePath || "");
|
|
503
|
-
const shardMarker = context.shardName
|
|
504
|
-
? `.codex-swarm\\wt-${context.shardName}`
|
|
505
|
-
: "";
|
|
506
|
-
const hasWorktree = worktreePath && commandLine.includes(worktreePath);
|
|
507
|
-
const hasShardMarker = shardMarker && commandLine.includes(shardMarker);
|
|
508
|
-
|
|
509
|
-
if (name === "node.exe" || name === "node") {
|
|
510
|
-
if (hasWorktree || hasShardMarker) return "node";
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
if (name === "bash.exe" || name === "bash" || name === "sh") {
|
|
514
|
-
if (hasWorktree) return "bash";
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
if (name === "conhost.exe" || name === "conhost") {
|
|
518
|
-
if (
|
|
519
|
-
context.sessionIds?.length > 0 &&
|
|
520
|
-
context.sessionIds.some((id) => id && commandLine.includes(id))
|
|
521
|
-
) {
|
|
522
|
-
return "conhost";
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
if (name === "bun.exe" || name === "bun") {
|
|
527
|
-
if ((hasWorktree || hasShardMarker) && hasGbrainServeCommand(commandLine)) {
|
|
528
|
-
return "bun";
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
if (name === "git.exe" || name === "git") {
|
|
533
|
-
if (
|
|
534
|
-
(hasWorktree || hasShardMarker) &&
|
|
535
|
-
hasFsmonitorDaemonCommand(commandLine)
|
|
536
|
-
) {
|
|
537
|
-
return "git";
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
return null;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
function killPid(
|
|
545
|
-
pid,
|
|
546
|
-
{ killFn = process.kill, protectedPids = new Set() } = {},
|
|
547
|
-
) {
|
|
548
|
-
if (protectedPids.has(pid)) return false;
|
|
549
|
-
killFn(pid, "SIGKILL");
|
|
550
|
-
return true;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
491
|
/**
|
|
554
492
|
* Return a process tree rooted at `rootPid`.
|
|
555
493
|
*
|
|
@@ -730,67 +668,6 @@ export function findProcessesByCommandLine(
|
|
|
730
668
|
});
|
|
731
669
|
}
|
|
732
670
|
|
|
733
|
-
/**
|
|
734
|
-
* Cleanup processes associated with a swarm shard.
|
|
735
|
-
*
|
|
736
|
-
* Sequence: kill known top PID snapshots, scan scoped command lines, skip
|
|
737
|
-
* protected PIDs, and kill only narrowly gated runtime categories.
|
|
738
|
-
*
|
|
739
|
-
* @param {{worktreePath?: string, sessionIds?: string[], topPids?: Array<number | {pid: number}>, runId?: string, shardName?: string, dryRun?: boolean, isWindows?: boolean, spawnSyncFn?: typeof spawnSync, killFn?: typeof process.kill, protectedPids?: Set<number>}} opts
|
|
740
|
-
* @returns {{killed: number, scanned: number, skipped: number, byCategory: {node: number, bash: number, conhost: number, bun: number, git: number}}}
|
|
741
|
-
*/
|
|
742
|
-
export function cleanupShardProcesses({
|
|
743
|
-
worktreePath,
|
|
744
|
-
sessionIds = [],
|
|
745
|
-
topPids = [],
|
|
746
|
-
runId,
|
|
747
|
-
shardName,
|
|
748
|
-
dryRun = false,
|
|
749
|
-
isWindows = IS_WINDOWS,
|
|
750
|
-
spawnSyncFn = spawnSync,
|
|
751
|
-
killFn = process.kill,
|
|
752
|
-
protectedPids,
|
|
753
|
-
} = {}) {
|
|
754
|
-
const protectedSet = getProtectedPids({
|
|
755
|
-
protectedPids,
|
|
756
|
-
isWindows,
|
|
757
|
-
spawnSyncFn,
|
|
758
|
-
includeAncestorScan: true,
|
|
759
|
-
});
|
|
760
|
-
const byCategory = { node: 0, bash: 0, conhost: 0, bun: 0, git: 0 };
|
|
761
|
-
let killed = 0;
|
|
762
|
-
let skipped = 0;
|
|
763
|
-
|
|
764
|
-
if (!dryRun && topPids.length > 0) {
|
|
765
|
-
killed += killProcessTreeSnapshot(topPids, {
|
|
766
|
-
killFn,
|
|
767
|
-
protectedPids: protectedSet,
|
|
768
|
-
}).killed;
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
const scanned = getProcessSnapshot({ isWindows, spawnSyncFn });
|
|
772
|
-
|
|
773
|
-
for (const proc of scanned) {
|
|
774
|
-
const category = categoryForShardProcess(proc, {
|
|
775
|
-
worktreePath,
|
|
776
|
-
sessionIds,
|
|
777
|
-
shardName,
|
|
778
|
-
});
|
|
779
|
-
if (!category) continue;
|
|
780
|
-
byCategory[category]++;
|
|
781
|
-
if (protectedSet.has(proc.pid)) {
|
|
782
|
-
skipped++;
|
|
783
|
-
continue;
|
|
784
|
-
}
|
|
785
|
-
if (dryRun) continue;
|
|
786
|
-
try {
|
|
787
|
-
if (killPid(proc.pid, { killFn, protectedPids: protectedSet })) killed++;
|
|
788
|
-
} catch {}
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
return { killed, scanned: scanned.length, skipped, byCategory };
|
|
792
|
-
}
|
|
793
|
-
|
|
794
671
|
/**
|
|
795
672
|
* Cleanup narrowly gated orphan runtime processes.
|
|
796
673
|
*
|
package/hub/lib/spawn-trace.mjs
CHANGED
|
@@ -7,13 +7,14 @@ import { join } from "node:path";
|
|
|
7
7
|
const LOG_DIR = join(homedir(), ".triflux", "logs");
|
|
8
8
|
const DEDUPE_WINDOW_MS = 5_000;
|
|
9
9
|
const RATE_WINDOW_MS = 1_000;
|
|
10
|
+
const DEFAULT_MAX_SPAWN_PER_SEC = 100;
|
|
10
11
|
// multi-worker headless dispatch 가 1 초 안에 ~25-30+ psmux/tmux 명령을 호출한다.
|
|
11
12
|
// 2-worker dispatchBatch 가 default 30 limit 을 초과해 `rate_limit` throw 로 mac
|
|
12
13
|
// 호환성 회귀 (smoke test 발견, 2026-05-15). 100 으로 상향 — 폭주 안전망은
|
|
13
14
|
// 여전히 (default 100/sec) 유지, env override 로 보수 설정 가능.
|
|
14
15
|
export let MAX_SPAWN_PER_SEC = resolvePositiveInteger(
|
|
15
16
|
process.env.TRIFLUX_MAX_SPAWN_RATE,
|
|
16
|
-
|
|
17
|
+
DEFAULT_MAX_SPAWN_PER_SEC,
|
|
17
18
|
);
|
|
18
19
|
export const MAX_TOTAL_DESCENDANTS = resolvePositiveInteger(
|
|
19
20
|
process.env.TRIFLUX_MAX_DESCENDANTS,
|
|
@@ -160,7 +161,7 @@ export function getMaxSpawnPerSec() {
|
|
|
160
161
|
export function reload() {
|
|
161
162
|
MAX_SPAWN_PER_SEC = resolvePositiveInteger(
|
|
162
163
|
process.env.TRIFLUX_MAX_SPAWN_RATE,
|
|
163
|
-
|
|
164
|
+
DEFAULT_MAX_SPAWN_PER_SEC,
|
|
164
165
|
);
|
|
165
166
|
return getMaxSpawnPerSec();
|
|
166
167
|
}
|
|
@@ -5,7 +5,6 @@ export const MAX_DURATION_MS = 86_400_000;
|
|
|
5
5
|
export const DEFAULT_ASSIGN_TIMEOUT_MS = 600_000;
|
|
6
6
|
export const DEFAULT_ASSIGN_TTL_MS = 600_000;
|
|
7
7
|
export const DEFAULT_HANDOFF_TTL_MS = 600_000;
|
|
8
|
-
export const DEFAULT_SWARM_LOCK_TTL_MS = 600_000;
|
|
9
8
|
export const DEFAULT_REGISTER_TIMEOUT_SEC = 600;
|
|
10
9
|
export const REGISTER_HEARTBEAT_GRACE_MS = 120_000;
|
|
11
10
|
export const DEFAULT_STALL_INTERVENTION_SEC = 1_200;
|
package/hub/paths.mjs
CHANGED
|
@@ -6,10 +6,7 @@ import { join } from "node:path";
|
|
|
6
6
|
export const TFX_WORK_DIR = ".tfx";
|
|
7
7
|
export const TFX_PLANS_DIR = join(TFX_WORK_DIR, "plans");
|
|
8
8
|
export const TFX_REPORTS_DIR = join(TFX_WORK_DIR, "reports");
|
|
9
|
-
export const TFX_HANDOFFS_DIR = join(TFX_WORK_DIR, "handoffs");
|
|
10
|
-
export const TFX_LOGS_DIR = join(TFX_WORK_DIR, "logs");
|
|
11
9
|
export const TFX_STATE_DIR = join(TFX_WORK_DIR, "state");
|
|
12
|
-
export const TFX_FULLCYCLE_DIR = join(TFX_WORK_DIR, "fullcycle");
|
|
13
10
|
|
|
14
11
|
/**
|
|
15
12
|
* triflux 워킹 디렉토리 구조를 보장한다.
|
|
@@ -20,10 +17,7 @@ export function ensureTfxDirs(baseDir) {
|
|
|
20
17
|
TFX_WORK_DIR,
|
|
21
18
|
TFX_PLANS_DIR,
|
|
22
19
|
TFX_REPORTS_DIR,
|
|
23
|
-
TFX_HANDOFFS_DIR,
|
|
24
|
-
TFX_LOGS_DIR,
|
|
25
20
|
TFX_STATE_DIR,
|
|
26
|
-
TFX_FULLCYCLE_DIR,
|
|
27
21
|
]) {
|
|
28
22
|
mkdirSync(join(baseDir, relativeDir), { recursive: true });
|
|
29
23
|
}
|
package/hub/pipeline/index.mjs
CHANGED
|
@@ -3,11 +3,7 @@
|
|
|
3
3
|
// 상태(state.mjs) + 전이(transitions.mjs) 통합 인터페이스
|
|
4
4
|
|
|
5
5
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
6
|
-
import { homedir } from "node:os";
|
|
7
6
|
import { join, resolve } from "node:path";
|
|
8
|
-
import { classifyIntent as _classifyIntent } from "../intent.mjs";
|
|
9
|
-
import { runConfidenceCheck } from "./gates/confidence.mjs";
|
|
10
|
-
import { runSelfCheck } from "./gates/selfcheck.mjs";
|
|
11
7
|
import {
|
|
12
8
|
ensurePipelineTable,
|
|
13
9
|
initPipelineState,
|
|
@@ -21,7 +17,6 @@ import {
|
|
|
21
17
|
TERMINAL,
|
|
22
18
|
transitionPhase,
|
|
23
19
|
} from "./transitions.mjs";
|
|
24
|
-
// deslop gate: 호출자가 scanDirectory/detectSlop 결과를 전달
|
|
25
20
|
|
|
26
21
|
/**
|
|
27
22
|
* 파이프라인 매니저 생성
|
|
@@ -162,192 +157,7 @@ export function createPipeline(db, teamName, opts = {}) {
|
|
|
162
157
|
remove() {
|
|
163
158
|
return removePipelineState(db, teamName);
|
|
164
159
|
},
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Confidence Gate 실행 + 자동 전이
|
|
168
|
-
* prd → confidence → exec/failed
|
|
169
|
-
* @param {string|object} planArtifact
|
|
170
|
-
* @param {object} context - { checks?, codebaseFiles?, existingTests? }
|
|
171
|
-
* @returns {{ ok: boolean, gate: object, state?: object, error?: string }}
|
|
172
|
-
*/
|
|
173
|
-
runConfidenceGate(planArtifact, context = {}) {
|
|
174
|
-
const current = readPipelineState(db, teamName);
|
|
175
|
-
if (!current) return { ok: false, error: `파이프라인 없음: ${teamName}` };
|
|
176
|
-
|
|
177
|
-
if (current.phase !== "confidence") {
|
|
178
|
-
return {
|
|
179
|
-
ok: false,
|
|
180
|
-
error: `confidence gate는 confidence 단계에서만 실행 가능 (현재: ${current.phase})`,
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const gate = runConfidenceCheck(planArtifact, context);
|
|
185
|
-
this.setArtifact("confidence_result", gate);
|
|
186
|
-
|
|
187
|
-
if (gate.decision === "abort") {
|
|
188
|
-
const result = this.advance("failed");
|
|
189
|
-
return { ok: true, gate, state: result.state };
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// proceed 또는 alternative → exec로 전이
|
|
193
|
-
const result = this.advance("exec");
|
|
194
|
-
return { ok: result.ok, gate, state: result.state, error: result.error };
|
|
195
|
-
},
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Deslop Gate 실행 + 자동 전이
|
|
199
|
-
* exec → deslop → verify
|
|
200
|
-
* 호출자가 미리 deslop 결과를 생성하여 전달.
|
|
201
|
-
* @param {object} [deslopResult] - scanDirectory() 또는 detectSlop() 결과
|
|
202
|
-
* @returns {{ ok: boolean, gate: object, state?: object, error?: string }}
|
|
203
|
-
*/
|
|
204
|
-
runDeslopGate(deslopResult = null) {
|
|
205
|
-
const current = readPipelineState(db, teamName);
|
|
206
|
-
if (!current) return { ok: false, error: `파이프라인 없음: ${teamName}` };
|
|
207
|
-
|
|
208
|
-
if (current.phase !== "deslop") {
|
|
209
|
-
return {
|
|
210
|
-
ok: false,
|
|
211
|
-
error: `deslop gate는 deslop 단계에서만 실행 가능 (현재: ${current.phase})`,
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const gate = deslopResult || {
|
|
216
|
-
files: [],
|
|
217
|
-
summary: { total: 0, clean: 0 },
|
|
218
|
-
};
|
|
219
|
-
this.setArtifact("deslop_result", gate);
|
|
220
|
-
|
|
221
|
-
// deslop은 항상 verify로 전이 (정보 제공 게이트, 차단 없음)
|
|
222
|
-
const result = this.advance("verify");
|
|
223
|
-
return { ok: result.ok, gate, state: result.state, error: result.error };
|
|
224
|
-
},
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Self-Check Gate 실행 + 자동 전이
|
|
228
|
-
* verify → selfcheck → complete/fix
|
|
229
|
-
* @param {string|object} execResult
|
|
230
|
-
* @param {string|object} verifyResult
|
|
231
|
-
* @param {object} requirements - { hasDiff?, evidence? }
|
|
232
|
-
* @returns {{ ok: boolean, gate: object, state?: object, error?: string }}
|
|
233
|
-
*/
|
|
234
|
-
runSelfCheckGate(execResult, verifyResult, requirements = {}) {
|
|
235
|
-
const current = readPipelineState(db, teamName);
|
|
236
|
-
if (!current) return { ok: false, error: `파이프라인 없음: ${teamName}` };
|
|
237
|
-
|
|
238
|
-
if (current.phase !== "selfcheck") {
|
|
239
|
-
return {
|
|
240
|
-
ok: false,
|
|
241
|
-
error: `selfcheck gate는 selfcheck 단계에서만 실행 가능 (현재: ${current.phase})`,
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const gate = runSelfCheck(execResult, verifyResult, requirements);
|
|
246
|
-
this.setArtifact("selfcheck_result", gate);
|
|
247
|
-
|
|
248
|
-
if (gate.passed) {
|
|
249
|
-
const result = this.advance("complete");
|
|
250
|
-
return {
|
|
251
|
-
ok: result.ok,
|
|
252
|
-
gate,
|
|
253
|
-
state: result.state,
|
|
254
|
-
error: result.error,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Red Flag 탐지 또는 필수 질문 실패 → fix
|
|
259
|
-
const result = this.advance("fix");
|
|
260
|
-
return { ok: result.ok, gate, state: result.state, error: result.error };
|
|
261
|
-
},
|
|
262
160
|
};
|
|
263
161
|
}
|
|
264
|
-
|
|
265
|
-
// ── 토큰 벤치마크 훅 ──
|
|
266
|
-
|
|
267
|
-
let _tokenSnapshotMod = null;
|
|
268
|
-
|
|
269
|
-
async function loadTokenSnapshot() {
|
|
270
|
-
if (_tokenSnapshotMod) return _tokenSnapshotMod;
|
|
271
|
-
try {
|
|
272
|
-
_tokenSnapshotMod = await import("../../scripts/token-snapshot.mjs");
|
|
273
|
-
} catch {
|
|
274
|
-
_tokenSnapshotMod = null;
|
|
275
|
-
}
|
|
276
|
-
return _tokenSnapshotMod;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* 파이프라인 시작 시 토큰 스냅샷 캡처
|
|
281
|
-
* @param {string} label - 스냅샷 라벨 (e.g. teamName + timestamp)
|
|
282
|
-
* @returns {Promise<{label: string, snapshot: object}|null>}
|
|
283
|
-
*/
|
|
284
|
-
export async function benchmarkStart(label) {
|
|
285
|
-
const mod = await loadTokenSnapshot();
|
|
286
|
-
if (!mod?.takeSnapshot) return null;
|
|
287
|
-
try {
|
|
288
|
-
const snapshot = mod.takeSnapshot(label);
|
|
289
|
-
return { label, snapshot };
|
|
290
|
-
} catch {
|
|
291
|
-
return null;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* 파이프라인 종료 시 diff 계산 + 결과 저장
|
|
297
|
-
* @param {string} preLabel - 시작 스냅샷 라벨
|
|
298
|
-
* @param {string} postLabel - 종료 스냅샷 라벨
|
|
299
|
-
* @param {object} options - { agent?, cli?, id? }
|
|
300
|
-
* @returns {Promise<object|null>} diff 결과
|
|
301
|
-
*/
|
|
302
|
-
export async function benchmarkEnd(preLabel, postLabel, options = {}) {
|
|
303
|
-
const mod = await loadTokenSnapshot();
|
|
304
|
-
if (!mod?.takeSnapshot || !mod?.computeDiff) return null;
|
|
305
|
-
try {
|
|
306
|
-
// 종료 스냅샷 캡처
|
|
307
|
-
mod.takeSnapshot(postLabel);
|
|
308
|
-
// diff 계산 (결과는 DIFFS_DIR에 자동 저장됨)
|
|
309
|
-
const diff = mod.computeDiff(preLabel, postLabel, options);
|
|
310
|
-
|
|
311
|
-
// 추가로 타임스탬프 기반 사본 저장
|
|
312
|
-
const diffsDir = join(
|
|
313
|
-
homedir(),
|
|
314
|
-
".omc",
|
|
315
|
-
"state",
|
|
316
|
-
"cx-auto-tokens",
|
|
317
|
-
"diffs",
|
|
318
|
-
);
|
|
319
|
-
mkdirSync(diffsDir, { recursive: true });
|
|
320
|
-
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
321
|
-
const outPath = join(diffsDir, `${ts}.json`);
|
|
322
|
-
writeFileSync(outPath, JSON.stringify(diff, null, 2));
|
|
323
|
-
|
|
324
|
-
return diff;
|
|
325
|
-
} catch {
|
|
326
|
-
return null;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* 트리아지 통합: quickClassify 고신뢰 시 Codex 분류 스킵 판정
|
|
332
|
-
* @param {string} prompt
|
|
333
|
-
* @param {number} [threshold=0.8]
|
|
334
|
-
* @returns {{ skip: boolean, routing: object|null, classification: object }}
|
|
335
|
-
*/
|
|
336
|
-
export function triageWithIntent(prompt, threshold = 0.8) {
|
|
337
|
-
const classification = _classifyIntent(prompt);
|
|
338
|
-
if (classification.confidence >= threshold) {
|
|
339
|
-
return { skip: true, routing: classification.routing, classification };
|
|
340
|
-
}
|
|
341
|
-
return { skip: false, routing: null, classification };
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
export {
|
|
345
|
-
classifyIntent,
|
|
346
|
-
INTENT_CATEGORIES,
|
|
347
|
-
quickClassify,
|
|
348
|
-
} from "../intent.mjs";
|
|
349
|
-
export { autoFixSlop, detectSlop, scanDirectory } from "../quality/deslop.mjs";
|
|
350
|
-
export { CRITERIA, runConfidenceCheck } from "./gates/confidence.mjs";
|
|
351
|
-
export { QUESTIONS, RED_FLAGS, runSelfCheck } from "./gates/selfcheck.mjs";
|
|
352
162
|
export { ensurePipelineTable } from "./state.mjs";
|
|
353
163
|
export { ALLOWED, canTransition, PHASES, TERMINAL } from "./transitions.mjs";
|
|
@@ -73,19 +73,9 @@ export function transitionPhase(state, nextPhase) {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
// fix → exec 재진입 시 (fix 후 재실행)
|
|
77
|
-
if (current === "fix" && nextPhase === "exec") {
|
|
78
|
-
// fix_attempt 유지 (이미 fix 진입 시 증가됨)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
76
|
// verify → fix → ... → verify 반복 후 fix_max 초과 시 ralph loop
|
|
82
77
|
if (nextPhase === "failed" && current === "fix") {
|
|
83
|
-
// ralph loop 반복 증가
|
|
84
78
|
next.ralph_iteration = (state.ralph_iteration || 0) + 1;
|
|
85
|
-
if (next.ralph_iteration > (state.ralph_max || 10)) {
|
|
86
|
-
// 최종 실패 — ralph loop도 초과
|
|
87
|
-
next.phase = "failed";
|
|
88
|
-
}
|
|
89
79
|
}
|
|
90
80
|
|
|
91
81
|
// phase_history 기록
|
package/hub/reflexion.mjs
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
//
|
|
2
|
-
// 에러를 구조화 저장 → 다음 세션에서 유사 에러 패턴 매칭 → 자동 솔루션 적용
|
|
1
|
+
// 오류 패턴 정규화 및 adaptive rule 관리
|
|
3
2
|
|
|
4
|
-
const DEFAULT_REFLEXION_TYPE = "reflexion";
|
|
5
3
|
export const ADAPTIVE_RULE_TYPE = "adaptive";
|
|
6
4
|
const DEFAULT_CONFIDENCE = 0.5;
|
|
7
|
-
const ACTIVE_RULE_CONFIDENCE = 0.5;
|
|
8
5
|
const ADAPTIVE_PROMOTION_STEP = 0.1;
|
|
9
6
|
const ADAPTIVE_DECAY_STEP = 0.1;
|
|
10
7
|
const ADAPTIVE_DELETE_THRESHOLD = 0.3;
|
|
@@ -100,12 +97,6 @@ function buildAdaptiveSolution(errorContext = {}, errorText = "") {
|
|
|
100
97
|
return `${toolName} 재시도 전 실패 원인을 검증하세요: ${summary}`;
|
|
101
98
|
}
|
|
102
99
|
|
|
103
|
-
function filterEntriesByType(entries, type) {
|
|
104
|
-
return entries.filter(
|
|
105
|
-
(entry) => (entry.type || DEFAULT_REFLEXION_TYPE) === type,
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
100
|
function parseAdaptiveRuleId(ruleId, projectSlug = "") {
|
|
110
101
|
const value = pickString(ruleId);
|
|
111
102
|
if (!value) return null;
|
|
@@ -187,68 +178,6 @@ export function normalizeError(errorMessage) {
|
|
|
187
178
|
return p.toLowerCase().replace(/\s+/g, " ").trim();
|
|
188
179
|
}
|
|
189
180
|
|
|
190
|
-
/**
|
|
191
|
-
* 에러에 대한 기존 솔루션 검색
|
|
192
|
-
* @deprecated reflexion_entries 기반. adaptive_rules로 통합 예정. 현재 런타임 호출 없음.
|
|
193
|
-
* @param {object} store - createStore() 반환 객체
|
|
194
|
-
* @param {string} errorMessage - 원본 에러 메시지
|
|
195
|
-
* @param {object} [context={}] - { file, function, cli, agent }
|
|
196
|
-
* @returns {{ found: boolean, entries: Array, bestMatch: object|null }}
|
|
197
|
-
*/
|
|
198
|
-
export function lookupSolution(store, errorMessage, context = {}) {
|
|
199
|
-
const pattern = normalizeError(errorMessage);
|
|
200
|
-
if (!pattern) return { found: false, entries: [], bestMatch: null };
|
|
201
|
-
const entries = store.findReflexion(pattern, context);
|
|
202
|
-
if (!entries.length) return { found: false, entries: [], bestMatch: null };
|
|
203
|
-
return { found: true, entries, bestMatch: entries[0] };
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* 에러 해결 후 학습 저장
|
|
208
|
-
* @deprecated reflexion_entries 기반. adaptive_rules로 통합 예정. 현재 런타임 호출 없음.
|
|
209
|
-
* 동일 패턴이 존재하면 hit 업데이트, 없으면 새로 생성
|
|
210
|
-
* @param {object} store
|
|
211
|
-
* @param {{ error: string, solution: string, context?: object, success?: boolean }} opts
|
|
212
|
-
* @returns {object|null}
|
|
213
|
-
*/
|
|
214
|
-
export function learnFromError(
|
|
215
|
-
store,
|
|
216
|
-
{ error, solution, context = {}, success = false },
|
|
217
|
-
) {
|
|
218
|
-
const pattern = normalizeError(error);
|
|
219
|
-
if (!pattern || !solution) return null;
|
|
220
|
-
const existing = filterEntriesByType(
|
|
221
|
-
store.findReflexion(pattern, context),
|
|
222
|
-
DEFAULT_REFLEXION_TYPE,
|
|
223
|
-
);
|
|
224
|
-
if (existing.length && existing[0].error_pattern === pattern) {
|
|
225
|
-
return store.updateReflexionHit(existing[0].id, success);
|
|
226
|
-
}
|
|
227
|
-
const newEntry = store.addReflexion({
|
|
228
|
-
type: DEFAULT_REFLEXION_TYPE,
|
|
229
|
-
error_pattern: pattern,
|
|
230
|
-
error_message: error,
|
|
231
|
-
context,
|
|
232
|
-
solution,
|
|
233
|
-
solution_code: null,
|
|
234
|
-
});
|
|
235
|
-
return success && newEntry
|
|
236
|
-
? store.updateReflexionHit(newEntry.id, true)
|
|
237
|
-
: newEntry;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* 솔루션 적용 결과 피드백
|
|
242
|
-
* @deprecated reflexion_entries 기반. adaptive_rules로 통합 예정. 현재 런타임 호출 없음.
|
|
243
|
-
* @param {object} store
|
|
244
|
-
* @param {string} entryId
|
|
245
|
-
* @param {boolean} success
|
|
246
|
-
* @returns {object|null}
|
|
247
|
-
*/
|
|
248
|
-
export function reportOutcome(store, entryId, success) {
|
|
249
|
-
return store.updateReflexionHit(entryId, success);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
181
|
/**
|
|
253
182
|
* PostToolUseFailure 컨텍스트에서 adaptive rule payload 생성
|
|
254
183
|
* @param {object} errorContext
|
|
@@ -347,18 +276,6 @@ export function decayRules(store, _sessionCount, projectSlug) {
|
|
|
347
276
|
return result;
|
|
348
277
|
}
|
|
349
278
|
|
|
350
|
-
/**
|
|
351
|
-
* 현재 활성화된 adaptive rules 조회
|
|
352
|
-
* @param {object} store — store-adapter 인스턴스 (listAdaptiveRules 필요)
|
|
353
|
-
* @param {string} projectSlug
|
|
354
|
-
* @returns {Array}
|
|
355
|
-
*/
|
|
356
|
-
export function getActiveAdaptiveRules(store, projectSlug) {
|
|
357
|
-
return listAdaptiveRulesFromStore(store, projectSlug).filter(
|
|
358
|
-
(rule) => rule.confidence >= ACTIVE_RULE_CONFIDENCE,
|
|
359
|
-
);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
279
|
/**
|
|
363
280
|
* 신뢰도 자동 조정 (success_count / hit_count, 샘플 크기 기반 감쇠)
|
|
364
281
|
* hit_count가 작으면 0.5(기본값)쪽으로 보수적으로 수렴
|
package/hub/role-contract.mjs
CHANGED
|
@@ -216,8 +216,6 @@ export function reduceRoleReachabilityTransition(state, trigger) {
|
|
|
216
216
|
: { state: transition.to, action: transition.action };
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
export const reduceRoleReachability = reduceRoleReachabilityTransition;
|
|
220
|
-
|
|
221
219
|
function snapshotLegacyDetails(snapshot) {
|
|
222
220
|
const legacy = isRecord(snapshot.legacy) ? snapshot.legacy : {};
|
|
223
221
|
return {
|
package/hud/cli-policy.mjs
CHANGED
|
@@ -41,29 +41,3 @@ export function resolveHudCliVisibility(env = process.env, opts = {}) {
|
|
|
41
41
|
return { showCodex: true, antigravityAllowed: true, policy: null };
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* antigravity / gemini 슬롯 행을 그릴지 판정한다.
|
|
47
|
-
*
|
|
48
|
-
* antigravity 가 허용이면 기존 동작 그대로 항상 그린다. 정책으로 꺼졌을 때만
|
|
49
|
-
* gemini 폴백 정보가 하나라도 있는지 보고, 전부 비어 있으면 행을 생략한다.
|
|
50
|
-
*
|
|
51
|
-
* @param {{
|
|
52
|
-
* antigravityAllowed?: boolean,
|
|
53
|
-
* antigravityReady?: boolean,
|
|
54
|
-
* geminiEmail?: unknown,
|
|
55
|
-
* geminiBucket?: unknown,
|
|
56
|
-
* geminiSession?: unknown,
|
|
57
|
-
* }} [args]
|
|
58
|
-
* @returns {boolean}
|
|
59
|
-
*/
|
|
60
|
-
export function shouldRenderGeminiFallbackRow({
|
|
61
|
-
antigravityAllowed = true,
|
|
62
|
-
antigravityReady = false,
|
|
63
|
-
geminiEmail = null,
|
|
64
|
-
geminiBucket = null,
|
|
65
|
-
geminiSession = null,
|
|
66
|
-
} = {}) {
|
|
67
|
-
if (antigravityAllowed || antigravityReady) return true;
|
|
68
|
-
return Boolean(geminiEmail || geminiBucket || geminiSession);
|
|
69
|
-
}
|
package/hud/colors.mjs
CHANGED
|
@@ -47,20 +47,7 @@ export function colorByPercent(value, text) {
|
|
|
47
47
|
return green(text);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
export function colorCooldown(seconds, text) {
|
|
51
|
-
if (seconds > 120) return red(text);
|
|
52
|
-
if (seconds > 0) return yellow(text);
|
|
53
|
-
return dim(text);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function colorParallel(current, cap) {
|
|
57
|
-
if (current >= cap) return green(`${current}/${cap}`);
|
|
58
|
-
if (current > 1) return yellow(`${current}/${cap}`);
|
|
59
|
-
return red(`${current}/${cap}`);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
50
|
export const GAUGE_WIDTH = 5;
|
|
63
|
-
export const GAUGE_BLOCKS = ["░", "▒", "▓", "█"]; // 밝기 0~3
|
|
64
51
|
|
|
65
52
|
export function coloredBar(percent, width = GAUGE_WIDTH, baseColor = null) {
|
|
66
53
|
const safePercent = Math.min(100, Math.max(0, percent));
|